@lowentry/utils 1.25.3 → 2.0.3

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 (184) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +0 -2
  3. package/dist/src/LeTypes.d.ts +73 -0
  4. package/dist/src/LeTypes.d.ts.map +1 -0
  5. package/dist/src/LeTypes.js +235 -0
  6. package/dist/src/LeTypes.js.map +1 -0
  7. package/dist/src/LeUtils.d.ts +745 -0
  8. package/dist/src/LeUtils.d.ts.map +1 -0
  9. package/dist/src/LeUtils.js +3168 -0
  10. package/dist/src/LeUtils.js.map +1 -0
  11. package/dist/src/classes/EventEmitter.d.ts +51 -0
  12. package/dist/src/classes/EventEmitter.d.ts.map +1 -0
  13. package/dist/src/classes/EventEmitter.js +94 -0
  14. package/dist/src/classes/EventEmitter.js.map +1 -0
  15. package/dist/src/classes/LinkedList.d.ts +35 -0
  16. package/dist/src/classes/LinkedList.d.ts.map +1 -0
  17. package/dist/src/classes/LinkedList.js +99 -0
  18. package/dist/src/classes/LinkedList.js.map +1 -0
  19. package/dist/src/classes/SerializableMap.d.ts +14 -0
  20. package/dist/src/classes/SerializableMap.d.ts.map +1 -0
  21. package/dist/src/classes/SerializableMap.js +27 -0
  22. package/dist/src/classes/SerializableMap.js.map +1 -0
  23. package/dist/src/classes/TreeSet.d.ts +123 -0
  24. package/dist/src/classes/TreeSet.d.ts.map +1 -0
  25. package/dist/src/classes/TreeSet.js +210 -0
  26. package/dist/src/classes/TreeSet.js.map +1 -0
  27. package/dist/src/index.d.ts +148 -0
  28. package/dist/src/index.d.ts.map +1 -0
  29. package/dist/src/index.js +13 -0
  30. package/dist/src/index.js.map +1 -0
  31. package/dist/tests/CustomClasses.test.d.ts +2 -0
  32. package/dist/tests/CustomClasses.test.d.ts.map +1 -0
  33. package/dist/tests/CustomClasses.test.js +138 -0
  34. package/dist/tests/CustomClasses.test.js.map +1 -0
  35. package/dist/tests/LeClasses.EventEmitter.test.d.ts +2 -0
  36. package/dist/tests/LeClasses.EventEmitter.test.d.ts.map +1 -0
  37. package/dist/tests/LeClasses.EventEmitter.test.js +39 -0
  38. package/dist/tests/LeClasses.EventEmitter.test.js.map +1 -0
  39. package/dist/tests/LeClasses.LinkedList.test.d.ts +2 -0
  40. package/dist/tests/LeClasses.LinkedList.test.d.ts.map +1 -0
  41. package/dist/tests/LeClasses.LinkedList.test.js +36 -0
  42. package/dist/tests/LeClasses.LinkedList.test.js.map +1 -0
  43. package/dist/tests/LeClasses.SerializableMap.test.d.ts +2 -0
  44. package/dist/tests/LeClasses.SerializableMap.test.d.ts.map +1 -0
  45. package/dist/tests/LeClasses.SerializableMap.test.js +25 -0
  46. package/dist/tests/LeClasses.SerializableMap.test.js.map +1 -0
  47. package/dist/tests/LeClasses.TreeSet.test.d.ts +2 -0
  48. package/dist/tests/LeClasses.TreeSet.test.d.ts.map +1 -0
  49. package/dist/tests/LeClasses.TreeSet.test.js +28 -0
  50. package/dist/tests/LeClasses.TreeSet.test.js.map +1 -0
  51. package/dist/tests/LeTypes.ARRAY.test.d.ts +2 -0
  52. package/dist/tests/LeTypes.ARRAY.test.d.ts.map +1 -0
  53. package/dist/tests/LeTypes.ARRAY.test.js +103 -0
  54. package/dist/tests/LeTypes.ARRAY.test.js.map +1 -0
  55. package/dist/tests/LeTypes.BOOL.test.d.ts +2 -0
  56. package/dist/tests/LeTypes.BOOL.test.d.ts.map +1 -0
  57. package/dist/tests/LeTypes.BOOL.test.js +138 -0
  58. package/dist/tests/LeTypes.BOOL.test.js.map +1 -0
  59. package/dist/tests/LeTypes.FLOAT.test.d.ts +2 -0
  60. package/dist/tests/LeTypes.FLOAT.test.d.ts.map +1 -0
  61. package/dist/tests/LeTypes.FLOAT.test.js +120 -0
  62. package/dist/tests/LeTypes.FLOAT.test.js.map +1 -0
  63. package/dist/tests/LeTypes.FLOAT_ANY.test.d.ts +2 -0
  64. package/dist/tests/LeTypes.FLOAT_ANY.test.d.ts.map +1 -0
  65. package/dist/tests/LeTypes.FLOAT_ANY.test.js +47 -0
  66. package/dist/tests/LeTypes.FLOAT_ANY.test.js.map +1 -0
  67. package/dist/tests/LeTypes.INT.test.d.ts +2 -0
  68. package/dist/tests/LeTypes.INT.test.d.ts.map +1 -0
  69. package/dist/tests/LeTypes.INT.test.js +80 -0
  70. package/dist/tests/LeTypes.INT.test.js.map +1 -0
  71. package/dist/tests/LeTypes.INT_ANY.test.d.ts +2 -0
  72. package/dist/tests/LeTypes.INT_ANY.test.d.ts.map +1 -0
  73. package/dist/tests/LeTypes.INT_ANY.test.js +37 -0
  74. package/dist/tests/LeTypes.INT_ANY.test.js.map +1 -0
  75. package/dist/tests/LeTypes.ISSET.test.d.ts +2 -0
  76. package/dist/tests/LeTypes.ISSET.test.d.ts.map +1 -0
  77. package/dist/tests/LeTypes.ISSET.test.js +113 -0
  78. package/dist/tests/LeTypes.ISSET.test.js.map +1 -0
  79. package/dist/tests/LeTypes.IS_ARRAY.test.d.ts +2 -0
  80. package/dist/tests/LeTypes.IS_ARRAY.test.d.ts.map +1 -0
  81. package/dist/tests/LeTypes.IS_ARRAY.test.js +81 -0
  82. package/dist/tests/LeTypes.IS_ARRAY.test.js.map +1 -0
  83. package/dist/tests/LeTypes.IS_OBJECT.test.d.ts +2 -0
  84. package/dist/tests/LeTypes.IS_OBJECT.test.d.ts.map +1 -0
  85. package/dist/tests/LeTypes.IS_OBJECT.test.js +98 -0
  86. package/dist/tests/LeTypes.IS_OBJECT.test.js.map +1 -0
  87. package/dist/tests/LeTypes.LAX.test.d.ts +2 -0
  88. package/dist/tests/LeTypes.LAX.test.d.ts.map +1 -0
  89. package/dist/tests/LeTypes.LAX.test.js +89 -0
  90. package/dist/tests/LeTypes.LAX.test.js.map +1 -0
  91. package/dist/tests/LeTypes.OBJECT.test.d.ts +2 -0
  92. package/dist/tests/LeTypes.OBJECT.test.d.ts.map +1 -0
  93. package/dist/tests/LeTypes.OBJECT.test.js +81 -0
  94. package/dist/tests/LeTypes.OBJECT.test.js.map +1 -0
  95. package/dist/tests/LeTypes.STRING.test.d.ts +2 -0
  96. package/dist/tests/LeTypes.STRING.test.d.ts.map +1 -0
  97. package/dist/tests/LeTypes.STRING.test.js +204 -0
  98. package/dist/tests/LeTypes.STRING.test.js.map +1 -0
  99. package/dist/tests/LeTypes.STRING_ANY.test.d.ts +2 -0
  100. package/dist/tests/LeTypes.STRING_ANY.test.d.ts.map +1 -0
  101. package/dist/tests/LeTypes.STRING_ANY.test.js +58 -0
  102. package/dist/tests/LeTypes.STRING_ANY.test.js.map +1 -0
  103. package/dist/tests/LeUtils.clone.test.d.ts +2 -0
  104. package/dist/tests/LeUtils.clone.test.d.ts.map +1 -0
  105. package/dist/tests/LeUtils.clone.test.js +180 -0
  106. package/dist/tests/LeUtils.clone.test.js.map +1 -0
  107. package/dist/tests/LeUtils.collections.test.d.ts +2 -0
  108. package/dist/tests/LeUtils.collections.test.d.ts.map +1 -0
  109. package/dist/tests/LeUtils.collections.test.js +149 -0
  110. package/dist/tests/LeUtils.collections.test.js.map +1 -0
  111. package/dist/tests/LeUtils.comparison.test.d.ts +2 -0
  112. package/dist/tests/LeUtils.comparison.test.d.ts.map +1 -0
  113. package/dist/tests/LeUtils.comparison.test.js +125 -0
  114. package/dist/tests/LeUtils.comparison.test.js.map +1 -0
  115. package/dist/tests/LeUtils.contains.test.d.ts +2 -0
  116. package/dist/tests/LeUtils.contains.test.d.ts.map +1 -0
  117. package/dist/tests/LeUtils.contains.test.js +52 -0
  118. package/dist/tests/LeUtils.contains.test.js.map +1 -0
  119. package/dist/tests/LeUtils.each.test.d.ts +2 -0
  120. package/dist/tests/LeUtils.each.test.d.ts.map +1 -0
  121. package/dist/tests/LeUtils.each.test.js +267 -0
  122. package/dist/tests/LeUtils.each.test.js.map +1 -0
  123. package/dist/tests/LeUtils.eachAsync.test.d.ts +2 -0
  124. package/dist/tests/LeUtils.eachAsync.test.d.ts.map +1 -0
  125. package/dist/tests/LeUtils.eachAsync.test.js +80 -0
  126. package/dist/tests/LeUtils.eachAsync.test.js.map +1 -0
  127. package/dist/tests/LeUtils.encoding.test.d.ts +2 -0
  128. package/dist/tests/LeUtils.encoding.test.d.ts.map +1 -0
  129. package/dist/tests/LeUtils.encoding.test.js +45 -0
  130. package/dist/tests/LeUtils.encoding.test.js.map +1 -0
  131. package/dist/tests/LeUtils.equals.test.d.ts +2 -0
  132. package/dist/tests/LeUtils.equals.test.d.ts.map +1 -0
  133. package/dist/tests/LeUtils.equals.test.js +328 -0
  134. package/dist/tests/LeUtils.equals.test.js.map +1 -0
  135. package/dist/tests/LeUtils.flatten.test.d.ts +2 -0
  136. package/dist/tests/LeUtils.flatten.test.d.ts.map +1 -0
  137. package/dist/tests/LeUtils.flatten.test.js +30 -0
  138. package/dist/tests/LeUtils.flatten.test.js.map +1 -0
  139. package/dist/tests/LeUtils.misc.test.d.ts +2 -0
  140. package/dist/tests/LeUtils.misc.test.d.ts.map +1 -0
  141. package/dist/tests/LeUtils.misc.test.js +50 -0
  142. package/dist/tests/LeUtils.misc.test.js.map +1 -0
  143. package/dist/tests/LeUtils.misc2.test.d.ts +2 -0
  144. package/dist/tests/LeUtils.misc2.test.d.ts.map +1 -0
  145. package/dist/tests/LeUtils.misc2.test.js +58 -0
  146. package/dist/tests/LeUtils.misc2.test.js.map +1 -0
  147. package/dist/tests/LeUtils.newlyAdded.test.d.ts +2 -0
  148. package/dist/tests/LeUtils.newlyAdded.test.d.ts.map +1 -0
  149. package/dist/tests/LeUtils.newlyAdded.test.js +121 -0
  150. package/dist/tests/LeUtils.newlyAdded.test.js.map +1 -0
  151. package/dist/tests/LeUtils.strings.test.d.ts +2 -0
  152. package/dist/tests/LeUtils.strings.test.d.ts.map +1 -0
  153. package/dist/tests/LeUtils.strings.test.js +125 -0
  154. package/dist/tests/LeUtils.strings.test.js.map +1 -0
  155. package/dist/tests/LeUtils.timing.test.d.ts +2 -0
  156. package/dist/tests/LeUtils.timing.test.d.ts.map +1 -0
  157. package/dist/tests/LeUtils.timing.test.js +72 -0
  158. package/dist/tests/LeUtils.timing.test.js.map +1 -0
  159. package/dist/tests/LeUtils.transactional.test.d.ts +2 -0
  160. package/dist/tests/LeUtils.transactional.test.d.ts.map +1 -0
  161. package/dist/tests/LeUtils.transactional.test.js +39 -0
  162. package/dist/tests/LeUtils.transactional.test.js.map +1 -0
  163. package/dist/tests/setup.d.ts +2 -0
  164. package/dist/tests/setup.d.ts.map +1 -0
  165. package/dist/tests/setup.js +6 -0
  166. package/dist/tests/setup.js.map +1 -0
  167. package/dist/vitest.config.d.ts +3 -0
  168. package/dist/vitest.config.d.ts.map +1 -0
  169. package/dist/vitest.config.js +21 -0
  170. package/dist/vitest.config.js.map +1 -0
  171. package/package.json +52 -53
  172. package/api-extractor.json +0 -43
  173. package/index.d.ts +0 -454
  174. package/index.js +0 -4160
  175. package/index.js.map +0 -1
  176. package/src/LeTypes.js +0 -254
  177. package/src/LeUtils.js +0 -3611
  178. package/src/classes/EventEmitter.js +0 -124
  179. package/src/classes/LinkedList.js +0 -145
  180. package/src/classes/SerializableMap.js +0 -17
  181. package/src/classes/TreeSet.js +0 -235
  182. package/src/index.js +0 -6
  183. package/tsconfig.d.ts +0 -1
  184. package/tsconfig.json +0 -39
@@ -0,0 +1,3168 @@
1
+ /**
2
+ * LeUtils - Core utility functions
3
+ */
4
+ import { ISSET, IS_OBJECT, IS_ARRAY, ARRAY, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLOAT_LAX_ANY } from './LeTypes';
5
+ /**
6
+ * Helper function to safely get property value without 'as'
7
+ * Returns undefined if property doesn't exist (this is normal, no warning)
8
+ * This is a defensive helper - it's expected that obj might not be an object or prop might not exist
9
+ */
10
+ function safeGetProperty(obj, prop) {
11
+ if ((typeof obj !== 'object' && typeof obj !== 'function') || obj === null) {
12
+ // Non-object - return undefined (defensive, no warning)
13
+ return undefined;
14
+ }
15
+ if (!(prop in obj)) {
16
+ // Property doesn't exist - this is normal, no warning
17
+ return undefined;
18
+ }
19
+ const desc = Object.getOwnPropertyDescriptor(obj, prop);
20
+ if (desc === undefined) {
21
+ // Property might be on prototype - use direct access
22
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
23
+ return obj[prop];
24
+ }
25
+ return desc.value;
26
+ }
27
+ /**
28
+ * Type predicate: check if value has numeric length property
29
+ */
30
+ function hasNumericLength(val) {
31
+ if ((typeof val !== 'object' && typeof val !== 'function') || val === null) {
32
+ return false;
33
+ }
34
+ if (!('length' in val)) {
35
+ return false;
36
+ }
37
+ const lengthValue = safeGetProperty(val, 'length');
38
+ if (typeof lengthValue !== 'number') {
39
+ return false;
40
+ }
41
+ return true;
42
+ }
43
+ /**
44
+ * Type predicate: check if value is DataView-like
45
+ */
46
+ function isDataViewLike(val) {
47
+ if ((typeof val !== 'object' && typeof val !== 'function') || val === null) {
48
+ return false;
49
+ }
50
+ if (!('byteLength' in val) || !('getUint8' in val)) {
51
+ return false;
52
+ }
53
+ const byteLengthValue = safeGetProperty(val, 'byteLength');
54
+ const getUint8Value = safeGetProperty(val, 'getUint8');
55
+ if (typeof byteLengthValue !== 'number' || typeof getUint8Value !== 'function') {
56
+ return false;
57
+ }
58
+ return true;
59
+ }
60
+ /**
61
+ * Type predicate: check if value is RegExp
62
+ */
63
+ function isRegExp(val) {
64
+ return val instanceof RegExp;
65
+ }
66
+ /**
67
+ * Type predicate: check if value is Record-like
68
+ */
69
+ function isRecordLike(val) {
70
+ return (typeof val === 'object' || typeof val === 'function') && val !== null && !Array.isArray(val) && !(val instanceof Map) && !(val instanceof Set);
71
+ }
72
+ /**
73
+ * Type predicate: check if value is array-like (unknown[])
74
+ */
75
+ function isUnknownArray(val) {
76
+ return Array.isArray(val);
77
+ }
78
+ /**
79
+ * Type predicate: check if value is Map-like
80
+ */
81
+ function isMapLike(val) {
82
+ return val instanceof Map;
83
+ }
84
+ /**
85
+ * Type predicate: check if value is Set-like
86
+ */
87
+ function isSetLike(val) {
88
+ return val instanceof Set;
89
+ }
90
+ /**
91
+ * Type predicate: check if value is ArrayBufferView
92
+ */
93
+ function isArrayBufferView(val) {
94
+ return ArrayBuffer.isView(val);
95
+ }
96
+ /**
97
+ * Type predicate: check if value has constructor property
98
+ */
99
+ function hasConstructor(val) {
100
+ if ((typeof val !== 'object' && typeof val !== 'function') || val === null) {
101
+ return false;
102
+ }
103
+ const constructorValue = safeGetProperty(val, 'constructor');
104
+ return typeof constructorValue === 'function';
105
+ }
106
+ /**
107
+ * Type predicate: check if value is Iterable
108
+ */
109
+ function isIterable(val) {
110
+ if ((typeof val !== 'object' && typeof val !== 'function') || val === null) {
111
+ return false;
112
+ }
113
+ // Check for Symbol.iterator using 'in' operator and direct property access
114
+ if (!(Symbol.iterator in val)) {
115
+ return false;
116
+ }
117
+ const desc = Object.getOwnPropertyDescriptor(val, Symbol.iterator);
118
+ if (desc === undefined) {
119
+ // Property might be on prototype - use direct access
120
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
121
+ return typeof val[Symbol.iterator] === 'function';
122
+ }
123
+ return typeof desc.value === 'function';
124
+ }
125
+ /**
126
+ * Type predicate: check if value has forEach method
127
+ */
128
+ function hasForEach(val) {
129
+ if (typeof val !== 'object' || val === null) {
130
+ return false;
131
+ }
132
+ const forEach = safeGetProperty(val, 'forEach');
133
+ return typeof forEach === 'function';
134
+ }
135
+ /**
136
+ * Type predicate: check if value is object (not null, not array, not Map, not Set)
137
+ */
138
+ function isPlainObject(val) {
139
+ if (typeof val !== 'object' || val === null) {
140
+ return false;
141
+ }
142
+ const constructor = safeGetProperty(val, 'constructor');
143
+ return constructor === Object;
144
+ }
145
+ /**
146
+ * Type predicate: check if value is a tuple pair [unknown, unknown]
147
+ */
148
+ function isPair(val) {
149
+ if (!Array.isArray(val)) {
150
+ return false;
151
+ }
152
+ return val.length === 2;
153
+ }
154
+ /**
155
+ * Type predicate: check if value is a Promise
156
+ */
157
+ function isPromise(val) {
158
+ if (typeof val !== 'object' || val === null) {
159
+ return false;
160
+ }
161
+ const then = safeGetProperty(val, 'then');
162
+ return typeof then === 'function';
163
+ }
164
+ /**
165
+ * Type predicate: check if value is TransactionalValue
166
+ */
167
+ function isTransactionalValue(val) {
168
+ if (typeof val !== 'object' || val === null) {
169
+ return false;
170
+ }
171
+ if (!('value' in val)) {
172
+ return false;
173
+ }
174
+ if (!('changes' in val)) {
175
+ return false;
176
+ }
177
+ const changes = safeGetProperty(val, 'changes');
178
+ if (!Array.isArray(changes)) {
179
+ return false;
180
+ }
181
+ return true;
182
+ }
183
+ /**
184
+ * Type predicate: check if Performance has timing property (legacy API)
185
+ */
186
+ function hasPerformanceTiming(perf) {
187
+ if (typeof perf !== 'object' || perf === null) {
188
+ return false;
189
+ }
190
+ return true;
191
+ }
192
+ /**
193
+ * Type predicate: check if globalThis has setTimeout
194
+ */
195
+ function hasSetTimeout(global) {
196
+ if (typeof global !== 'object' || global === null) {
197
+ return false;
198
+ }
199
+ const setTimeout = safeGetProperty(global, 'setTimeout');
200
+ return typeof setTimeout === 'function';
201
+ }
202
+ /**
203
+ * Type predicate: check if globalThis has setInterval
204
+ */
205
+ function hasSetInterval(global) {
206
+ if (typeof global !== 'object' || global === null) {
207
+ return false;
208
+ }
209
+ const setInterval = safeGetProperty(global, 'setInterval');
210
+ return typeof setInterval === 'function';
211
+ }
212
+ /**
213
+ * Type predicate: check if value is a number array (for gradients)
214
+ */
215
+ function isNumberArray(val) {
216
+ if (!Array.isArray(val)) {
217
+ return false;
218
+ }
219
+ return val.every((item) => typeof item === 'number');
220
+ }
221
+ /**
222
+ * Type predicate: check if Navigator has vendor property
223
+ */
224
+ function hasNavigatorVendor(nav) {
225
+ if (typeof nav !== 'object' || nav === null) {
226
+ return false;
227
+ }
228
+ return true;
229
+ }
230
+ /**
231
+ * Type predicate: check if globalThis has opera property (legacy)
232
+ */
233
+ function hasOpera(global) {
234
+ if (typeof global !== 'object' || global === null) {
235
+ return false;
236
+ }
237
+ return true;
238
+ }
239
+ /**
240
+ * Type predicate: check if Navigator has languages
241
+ */
242
+ function hasNavigatorLanguages(nav) {
243
+ if (typeof nav !== 'object' || nav === null) {
244
+ return false;
245
+ }
246
+ return true;
247
+ }
248
+ /**
249
+ * Type predicate: check if globalThis has Intl
250
+ */
251
+ function hasIntl(global) {
252
+ if (typeof global !== 'object' || global === null) {
253
+ return false;
254
+ }
255
+ return true;
256
+ }
257
+ /**
258
+ * Type predicate: check if object has id property
259
+ */
260
+ function hasId(obj) {
261
+ if (typeof obj !== 'object' || obj === null) {
262
+ return false;
263
+ }
264
+ const id = safeGetProperty(obj, 'id');
265
+ return typeof id === 'string';
266
+ }
267
+ /**
268
+ * Type predicate: check if value is Worker with sendMessage
269
+ */
270
+ function hasSendMessage(worker) {
271
+ if (typeof worker !== 'object' || worker === null) {
272
+ return false;
273
+ }
274
+ const sendMessage = safeGetProperty(worker, 'sendMessage');
275
+ return typeof sendMessage === 'function';
276
+ }
277
+ /**
278
+ * Type predicate: check if error has message property
279
+ */
280
+ function hasErrorMessage(err) {
281
+ if (typeof err !== 'object' || err === null) {
282
+ return false;
283
+ }
284
+ const message = safeGetProperty(err, 'message');
285
+ return typeof message === 'string';
286
+ }
287
+ /**
288
+ * Type predicate: check if version has major, minor, patch
289
+ */
290
+ function isVersionObject(version) {
291
+ if (!IS_OBJECT(version)) {
292
+ return false;
293
+ }
294
+ const major = safeGetProperty(version, 'major');
295
+ const minor = safeGetProperty(version, 'minor');
296
+ const patch = safeGetProperty(version, 'patch');
297
+ return major !== undefined && minor !== undefined && patch !== undefined;
298
+ }
299
+ /**
300
+ * Private helper to convert a number to bytes (Uint8ClampedArray).
301
+ */
302
+ function numberToBytes(num) {
303
+ const size = (num === 0) ? 0 : Math.ceil((Math.floor(Math.log2(num)) + 1) / 8);
304
+ const bytes = new Uint8ClampedArray(size);
305
+ let x = num;
306
+ for (let i = (size - 1); i >= 0; i--) {
307
+ const rightByte = x & 0xff;
308
+ bytes[i] = rightByte;
309
+ x = Math.floor(x / 0x100);
310
+ }
311
+ return bytes;
312
+ }
313
+ /**
314
+ * Deep equality comparison.
315
+ * Compares objects, arrays, Maps, Sets, primitives, and handles special cases.
316
+ *
317
+ * @param a - First value to compare
318
+ * @param b - Second value to compare
319
+ * @returns true if values are equivalent
320
+ */
321
+ export function equals(a, b) {
322
+ const seen = new WeakSet();
323
+ function recursiveEquals(valA, valB) {
324
+ if (valA === valB) {
325
+ return true;
326
+ }
327
+ if (valA && valB && typeof valA === 'object' && typeof valB === 'object') {
328
+ if (seen.has(valA)) {
329
+ return true;
330
+ }
331
+ seen.add(valA);
332
+ // Type guard: check constructor equality
333
+ const constructorAValue = safeGetProperty(valA, 'constructor');
334
+ const constructorBValue = safeGetProperty(valB, 'constructor');
335
+ if (constructorAValue !== constructorBValue) {
336
+ return false;
337
+ }
338
+ if (Array.isArray(valA)) {
339
+ // Type guard: verify valB is also an array
340
+ if (!Array.isArray(valB)) {
341
+ return false;
342
+ }
343
+ const length = valA.length;
344
+ if (length !== valB.length) {
345
+ return false;
346
+ }
347
+ for (let i = length; i-- !== 0;) {
348
+ if (!recursiveEquals(valA[i], valB[i])) {
349
+ return false;
350
+ }
351
+ }
352
+ return true;
353
+ }
354
+ if ((valA instanceof Map) && (valB instanceof Map)) {
355
+ if (valA.size !== valB.size) {
356
+ return false;
357
+ }
358
+ for (const [keyA, valueA] of valA.entries()) {
359
+ let found = false;
360
+ let valueB;
361
+ if (valB.has(keyA)) {
362
+ found = true;
363
+ valueB = valB.get(keyA);
364
+ }
365
+ else {
366
+ for (const [keyB, vb] of valB.entries()) {
367
+ if (recursiveEquals(keyA, keyB)) {
368
+ found = true;
369
+ valueB = vb;
370
+ break;
371
+ }
372
+ }
373
+ }
374
+ if (!found || !recursiveEquals(valueA, valueB)) {
375
+ return false;
376
+ }
377
+ }
378
+ return true;
379
+ }
380
+ if ((valA instanceof Set) && (valB instanceof Set)) {
381
+ if (valA.size !== valB.size) {
382
+ return false;
383
+ }
384
+ for (const itemA of valA) {
385
+ if (valB.has(itemA)) {
386
+ continue;
387
+ }
388
+ let found = false;
389
+ for (const itemB of valB) {
390
+ if (recursiveEquals(itemA, itemB)) {
391
+ found = true;
392
+ break;
393
+ }
394
+ }
395
+ if (!found) {
396
+ return false;
397
+ }
398
+ }
399
+ return true;
400
+ }
401
+ if (ArrayBuffer.isView(valA) && ArrayBuffer.isView(valB)) {
402
+ if (hasNumericLength(valA) && hasNumericLength(valB)) {
403
+ const lengthA = valA.length;
404
+ const lengthB = valB.length;
405
+ if (lengthA !== lengthB) {
406
+ return false;
407
+ }
408
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any -- TypedArray index access
409
+ const a = valA;
410
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any -- TypedArray index access
411
+ const b = valB;
412
+ for (let i = lengthA; i-- !== 0;) {
413
+ if (a[i] !== b[i]) {
414
+ return false;
415
+ }
416
+ }
417
+ return true;
418
+ }
419
+ if (isDataViewLike(valA) && isDataViewLike(valB)) {
420
+ const byteLengthA = valA.byteLength;
421
+ const byteLengthB = valB.byteLength;
422
+ if (byteLengthA !== byteLengthB) {
423
+ return false;
424
+ }
425
+ for (let i = byteLengthA; i-- !== 0;) {
426
+ if (valA.getUint8(i) !== valB.getUint8(i)) {
427
+ return false;
428
+ }
429
+ }
430
+ return true;
431
+ }
432
+ return false;
433
+ }
434
+ if (isRegExp(valA)) {
435
+ if (!isRegExp(valB)) {
436
+ // Type mismatch - this is normal comparison behavior, not an error
437
+ return false;
438
+ }
439
+ return valA.source === valB.source && valA.flags === valB.flags;
440
+ }
441
+ // Type guard: check for valueOf method
442
+ const valueOfA = safeGetProperty(valA, 'valueOf');
443
+ if (typeof valueOfA === 'function' && valueOfA !== Object.prototype.valueOf) {
444
+ try {
445
+ const valueOfB = safeGetProperty(valB, 'valueOf');
446
+ if (typeof valueOfB === 'function' && valueOfB !== Object.prototype.valueOf) {
447
+ const resultA = Function.prototype.call.call(valueOfA, valA);
448
+ const resultB = Function.prototype.call.call(valueOfB, valB);
449
+ return resultA === resultB;
450
+ }
451
+ }
452
+ catch (e) {
453
+ // Error calling valueOf - this indicates the method exists but threw an error
454
+ // This is unexpected and indicates invalid data or implementation
455
+ console.error('LeUtils.equals: error calling valueOf - invalid data or implementation', e);
456
+ }
457
+ }
458
+ // Type guard: check for toString method
459
+ const toStringA = safeGetProperty(valA, 'toString');
460
+ if (typeof toStringA === 'function' && toStringA !== Object.prototype.toString) {
461
+ try {
462
+ const toStringB = safeGetProperty(valB, 'toString');
463
+ if (typeof toStringB === 'function' && toStringB !== Object.prototype.toString) {
464
+ const resultA = Function.prototype.call.call(toStringA, valA);
465
+ const resultB = Function.prototype.call.call(toStringB, valB);
466
+ return resultA === resultB;
467
+ }
468
+ }
469
+ catch (e) {
470
+ // Error calling toString - this indicates the method exists but threw an error
471
+ // This is unexpected and indicates invalid data or implementation
472
+ console.error('LeUtils.equals: error calling toString - invalid data or implementation', e);
473
+ }
474
+ }
475
+ const constructorA = safeGetProperty(valA, 'constructor');
476
+ const constructorB = safeGetProperty(valB, 'constructor');
477
+ if (constructorA !== constructorB) {
478
+ return false;
479
+ }
480
+ if (!isRecordLike(valA) || !isRecordLike(valB)) {
481
+ // Type mismatch - this is normal comparison behavior, not an error
482
+ return false;
483
+ }
484
+ const keys = Object.keys(valA);
485
+ const length = keys.length;
486
+ if (length !== Object.keys(valB).length) {
487
+ return false;
488
+ }
489
+ for (let i = length; i-- !== 0;) {
490
+ if (!Object.prototype.hasOwnProperty.call(valB, keys[i])) {
491
+ return false;
492
+ }
493
+ }
494
+ for (let i = length; i-- !== 0;) {
495
+ const key = keys[i];
496
+ const typeofA = safeGetProperty(valA, '$$typeof');
497
+ if ((key === '_owner') && typeofA) {
498
+ continue;
499
+ }
500
+ const aVal = valA[key];
501
+ const bVal = valB[key];
502
+ if (!recursiveEquals(aVal, bVal)) {
503
+ return false;
504
+ }
505
+ }
506
+ return true;
507
+ }
508
+ // true if both are NaN, false otherwise
509
+ return ((valA !== valA) && (valB !== valB));
510
+ }
511
+ return recursiveEquals(a, b);
512
+ }
513
+ /**
514
+ * Deep clones a value.
515
+ *
516
+ * @param value - Value to clone
517
+ * @returns Deep cloned value
518
+ */
519
+ export function clone(value) {
520
+ const seen = new WeakMap();
521
+ function recursiveClone(val) {
522
+ if (val === null || typeof val !== 'object') {
523
+ return val;
524
+ }
525
+ if (typeof val === 'function') {
526
+ return val;
527
+ }
528
+ if (seen.has(val)) {
529
+ return seen.get(val);
530
+ }
531
+ let result;
532
+ if (isUnknownArray(val)) {
533
+ const resultArray = [];
534
+ result = resultArray;
535
+ seen.set(val, result);
536
+ for (let i = 0; i < val.length; i++) {
537
+ resultArray.push(recursiveClone(val[i]));
538
+ }
539
+ }
540
+ else if (val instanceof Date) {
541
+ result = new Date(val.getTime());
542
+ seen.set(val, result);
543
+ }
544
+ else if (isRegExp(val)) {
545
+ result = new RegExp(val.source, val.flags);
546
+ seen.set(val, result);
547
+ }
548
+ else if (isMapLike(val)) {
549
+ const resultMap = new Map();
550
+ result = resultMap;
551
+ seen.set(val, result);
552
+ for (const [k, v] of val.entries()) {
553
+ resultMap.set(recursiveClone(k), recursiveClone(v));
554
+ }
555
+ }
556
+ else if (isSetLike(val)) {
557
+ const resultSet = new Set();
558
+ result = resultSet;
559
+ seen.set(val, result);
560
+ for (const v of val.values()) {
561
+ resultSet.add(recursiveClone(v));
562
+ }
563
+ }
564
+ else if (isArrayBufferView(val)) {
565
+ // TypedArray clone
566
+ if (!hasConstructor(val)) {
567
+ console.error('LeUtils.clone: ArrayBufferView value does not have constructor property');
568
+ return value;
569
+ }
570
+ const Constructor = val.constructor;
571
+ // Create a new buffer to ensure independent memory
572
+ const buffer = val.buffer.slice(val.byteOffset, val.byteOffset + val.byteLength);
573
+ result = new Constructor(buffer);
574
+ seen.set(val, result);
575
+ }
576
+ else if (isRecordLike(val)) {
577
+ // Generic object
578
+ const resultObj = Object.create(Object.getPrototypeOf(val));
579
+ result = resultObj;
580
+ seen.set(val, result);
581
+ for (const key of Object.keys(val)) {
582
+ resultObj[key] = recursiveClone(val[key]);
583
+ }
584
+ }
585
+ else {
586
+ // Unknown object type - this indicates an unsupported type being cloned
587
+ console.error('LeUtils.clone: unsupported object type, cannot clone properly', val);
588
+ return value;
589
+ }
590
+ return result;
591
+ }
592
+ const cloned = recursiveClone(value);
593
+ if (cloned === undefined && value !== undefined) {
594
+ // Clone should only return undefined if input is undefined - otherwise it's a bug
595
+ console.error('LeUtils.clone: clone returned undefined for non-undefined input - cloning failed');
596
+ return value;
597
+ }
598
+ // Type guard: verify cloned value matches expected type T
599
+ // Since we're cloning, the structure should match, but TypeScript can't verify this
600
+ // We use a type predicate approach: if value is T, then cloned should also be T
601
+ // This is safe because clone preserves structure
602
+ // The only way to satisfy TypeScript here is through a type assertion, but we've validated the clone succeeded
603
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
604
+ return cloned;
605
+ }
606
+ /**
607
+ * Checks if the given elements can be iterated over using each().
608
+ *
609
+ * @param elements - Value to check
610
+ * @returns true if can iterate
611
+ */
612
+ export function supportsEach(elements) {
613
+ if ((elements === null) || (typeof elements === 'undefined') || (typeof elements === 'string')) {
614
+ return false;
615
+ }
616
+ return !!((Array.isArray(elements))
617
+ || isPlainObject(elements)
618
+ || isIterable(elements)
619
+ || hasForEach(elements)
620
+ || ((typeof elements === 'object') || (typeof elements === 'function')));
621
+ }
622
+ /**
623
+ * Returns an iterator that yields [value, key/index] for each element.
624
+ *
625
+ * @param elements - Collection to iterate
626
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty check
627
+ * @yields [value, key/index]
628
+ */
629
+ export function* eachIterator(elements, optionalSkipHasOwnPropertyCheck = false) {
630
+ if ((elements === null) || (typeof elements === 'undefined')) {
631
+ return;
632
+ }
633
+ if (Array.isArray(elements)) {
634
+ for (let i = 0; i < elements.length; i++) {
635
+ yield [elements[i], i];
636
+ }
637
+ return;
638
+ }
639
+ if (elements instanceof Map) {
640
+ for (const [i, value] of elements) {
641
+ yield [value, i];
642
+ }
643
+ return;
644
+ }
645
+ if (elements instanceof Set) {
646
+ for (const value of elements) {
647
+ yield [value, value];
648
+ }
649
+ return;
650
+ }
651
+ if (isPlainObject(elements)) {
652
+ for (const i in elements) {
653
+ if ((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, i)) {
654
+ yield [elements[i], i];
655
+ }
656
+ }
657
+ return;
658
+ }
659
+ if (typeof elements !== 'string') {
660
+ if (isIterable(elements)) {
661
+ let i = 0;
662
+ for (const value of elements) {
663
+ yield [value, i];
664
+ i++;
665
+ }
666
+ return;
667
+ }
668
+ if (hasForEach(elements)) {
669
+ const buffer = [];
670
+ elements.forEach((value, i) => {
671
+ buffer.push([value, i]);
672
+ });
673
+ for (const entry of buffer) {
674
+ yield entry;
675
+ }
676
+ return;
677
+ }
678
+ }
679
+ if ((typeof elements === 'object') && (elements !== null)) {
680
+ // Last resort: try to iterate over any object properties
681
+ if (isRecordLike(elements)) {
682
+ for (const i in elements) {
683
+ if ((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, i)) {
684
+ yield [elements[i], i];
685
+ }
686
+ }
687
+ return;
688
+ }
689
+ }
690
+ // Reached end of function without yielding - invalid/unsupported type
691
+ // This is unexpected - user passed a type that can't be iterated
692
+ console.error('LeUtils.eachIterator: executed on invalid/unsupported type', typeof elements, elements);
693
+ }
694
+ /**
695
+ * Loops through each element and calls callback.
696
+ * Callback can return false to break iteration.
697
+ *
698
+ * @param elements - Collection to iterate
699
+ * @param callback - Function called for each element
700
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty check
701
+ * @returns Original elements
702
+ */
703
+ export function each(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
704
+ if (typeof callback !== 'function') {
705
+ throw new TypeError('The given callback is not a function');
706
+ }
707
+ for (const [value, key] of eachIterator(elements, optionalSkipHasOwnPropertyCheck)) {
708
+ if (callback.call(value, value, key) === false) {
709
+ break;
710
+ }
711
+ }
712
+ return elements;
713
+ }
714
+ /**
715
+ * Like each(), except that it expects an async callback.
716
+ *
717
+ * @param elements - Collection to iterate
718
+ * @param asyncCallback - Async function called for each element
719
+ * @param parallelCount - Number of parallel executions (default 1)
720
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty check
721
+ * @returns Promise resolving to original elements
722
+ */
723
+ export async function eachAsync(elements, asyncCallback, parallelCount = 1, optionalSkipHasOwnPropertyCheck = false) {
724
+ if ((elements === null) || (typeof elements === 'undefined')) {
725
+ return elements;
726
+ }
727
+ if (typeof asyncCallback !== 'function') {
728
+ throw new TypeError('The given callback is not a function');
729
+ }
730
+ const pCount = INT_LAX(parallelCount);
731
+ if (pCount > 1) {
732
+ const runningPromises = new Set();
733
+ let doBreak = false;
734
+ for (const [value, key] of eachIterator(elements, optionalSkipHasOwnPropertyCheck)) {
735
+ if (doBreak)
736
+ break;
737
+ while (runningPromises.size >= pCount) {
738
+ await Promise.race(runningPromises);
739
+ if (doBreak)
740
+ break;
741
+ }
742
+ const promise = (async () => {
743
+ if ((await asyncCallback.call(value, value, key)) === false) {
744
+ doBreak = true;
745
+ }
746
+ })();
747
+ runningPromises.add(promise);
748
+ promise.finally(() => {
749
+ runningPromises.delete(promise);
750
+ });
751
+ }
752
+ await Promise.all(runningPromises);
753
+ }
754
+ else {
755
+ for (const [value, key] of eachIterator(elements, optionalSkipHasOwnPropertyCheck)) {
756
+ if ((await asyncCallback.call(value, value, key)) === false) {
757
+ break;
758
+ }
759
+ }
760
+ }
761
+ return elements;
762
+ }
763
+ /**
764
+ * Returns empty collection matching type of input, plus add function.
765
+ *
766
+ * @param elements - Source collection to match type
767
+ * @returns [success, emptyCollection, addFunction]
768
+ */
769
+ export function getEmptySimplifiedCollection(elements) {
770
+ if ((elements === null) || (typeof elements === 'undefined')) {
771
+ return [false, [], (_value, _index) => { }];
772
+ }
773
+ let collection = null;
774
+ let add = null;
775
+ if (Array.isArray(elements)) {
776
+ const arrayCollection = [];
777
+ collection = arrayCollection;
778
+ add = (value, _index) => {
779
+ arrayCollection.push(value);
780
+ };
781
+ }
782
+ else if (isPlainObject(elements)) {
783
+ const objectCollection = {};
784
+ collection = objectCollection;
785
+ add = (value, index) => {
786
+ const stringIndex = typeof index === 'string' ? index : String(index);
787
+ objectCollection[stringIndex] = value;
788
+ };
789
+ }
790
+ else if (elements instanceof Map) {
791
+ const mapCollection = new Map();
792
+ collection = mapCollection;
793
+ add = (value, index) => {
794
+ mapCollection.set(index, value);
795
+ };
796
+ }
797
+ else if ((typeof elements !== 'string') && (elements !== null) && (isIterable(elements) || hasForEach(elements))) {
798
+ const arrayCollection = [];
799
+ collection = arrayCollection;
800
+ add = (value, _index) => {
801
+ arrayCollection.push(value);
802
+ };
803
+ }
804
+ else if ((typeof elements === 'object' || typeof elements === 'function') && (elements !== null)) {
805
+ const objectCollection = {};
806
+ collection = objectCollection;
807
+ add = (value, index) => {
808
+ const stringIndex = typeof index === 'string' ? index : String(index);
809
+ objectCollection[stringIndex] = value;
810
+ };
811
+ }
812
+ else {
813
+ // Invalid/unsupported type - this is unexpected
814
+ console.error('LeUtils.getEmptySimplifiedCollection: executed on invalid/unsupported type', typeof elements, elements);
815
+ return [false, [], (_value, _index) => { }];
816
+ }
817
+ return [true, collection, add];
818
+ }
819
+ /**
820
+ * Filters collection, returning only elements matching predicate.
821
+ *
822
+ * @param elements - Collection to filter
823
+ * @param callback - Predicate function (truthy = keep)
824
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
825
+ * @returns Filtered collection (same type as input)
826
+ */
827
+ export function filter(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
828
+ if (callback && (typeof callback !== 'function')) {
829
+ throw new TypeError('The given callback is not a function');
830
+ }
831
+ const [success, collection, add] = getEmptySimplifiedCollection(elements);
832
+ if (!success) {
833
+ return elements;
834
+ }
835
+ each(elements, (value, index) => {
836
+ if (!callback) {
837
+ if (value) {
838
+ add(value, index);
839
+ }
840
+ }
841
+ else if (callback.call(value, value, index)) {
842
+ add(value, index);
843
+ }
844
+ }, optionalSkipHasOwnPropertyCheck);
845
+ return collection;
846
+ }
847
+ /**
848
+ * Maps collection, transforming each element.
849
+ *
850
+ * @param elements - Collection to map
851
+ * @param callback - Transform function
852
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
853
+ * @returns Mapped collection (same type as input)
854
+ */
855
+ export function map(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
856
+ if (callback && (typeof callback !== 'function')) {
857
+ throw new TypeError('The given callback is not a function');
858
+ }
859
+ const [success, collection, add] = getEmptySimplifiedCollection(elements);
860
+ if (!success) {
861
+ return elements;
862
+ }
863
+ each(elements, (value, index) => {
864
+ if (!callback) {
865
+ add(value, index);
866
+ }
867
+ else {
868
+ add(callback.call(value, value, index), index);
869
+ }
870
+ }, optionalSkipHasOwnPropertyCheck);
871
+ return collection;
872
+ }
873
+ /**
874
+ * Maps collection to array.
875
+ *
876
+ * @param elements - Collection to map
877
+ * @param callback - Transform function
878
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
879
+ * @returns Array of transformed values
880
+ */
881
+ export function mapToArray(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
882
+ if (callback && (typeof callback !== 'function')) {
883
+ throw new TypeError('The given callback is not a function');
884
+ }
885
+ const result = [];
886
+ each(elements, (value, index) => {
887
+ if (!callback) {
888
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
889
+ result.push(value);
890
+ }
891
+ else {
892
+ result.push(callback.call(value, value, index));
893
+ }
894
+ }, optionalSkipHasOwnPropertyCheck);
895
+ return result;
896
+ }
897
+ /**
898
+ * Sorts keys by comparing values with comparator.
899
+ *
900
+ * @param elements - Collection to get keys from
901
+ * @param comparator - Comparison function
902
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
903
+ * @returns Sorted array of keys
904
+ */
905
+ export function sortKeys(elements, comparator, optionalSkipHasOwnPropertyCheck = false) {
906
+ if (typeof comparator !== 'function') {
907
+ throw new TypeError('The given comparator is not a function');
908
+ }
909
+ const keys = [];
910
+ each(elements, (_value, index) => {
911
+ keys.push(index);
912
+ }, optionalSkipHasOwnPropertyCheck);
913
+ keys.sort((a, b) => comparator(getValueAtIndex(elements, a, optionalSkipHasOwnPropertyCheck), getValueAtIndex(elements, b, optionalSkipHasOwnPropertyCheck)));
914
+ return keys;
915
+ }
916
+ /**
917
+ * Gets value at index/key from collection.
918
+ *
919
+ * @param elements - Collection
920
+ * @param index - Key or index
921
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
922
+ * @returns Value at index, or undefined
923
+ */
924
+ export function getValueAtIndex(elements, index, optionalSkipHasOwnPropertyCheck = false) {
925
+ if ((elements === null) || (typeof elements === 'undefined')) {
926
+ return undefined;
927
+ }
928
+ if (Array.isArray(elements)) {
929
+ const numIndex = typeof index === 'number' ? index : Number(index);
930
+ if (Number.isNaN(numIndex)) {
931
+ console.error('LeUtils.getValueAtIndex: invalid array index (not a number)', index);
932
+ return undefined;
933
+ }
934
+ return elements[numIndex];
935
+ }
936
+ if (elements instanceof Map) {
937
+ return elements.get(index);
938
+ }
939
+ if (elements instanceof Set) {
940
+ return index;
941
+ }
942
+ if (typeof elements !== 'string') {
943
+ if (ArrayBuffer.isView(elements) && !(elements instanceof DataView)) {
944
+ // TypedArray - access by numeric index
945
+ if (hasNumericLength(elements)) {
946
+ const numIndex = typeof index === 'number' ? index : Number(index);
947
+ if (Number.isNaN(numIndex)) {
948
+ console.error('LeUtils.getValueAtIndex: invalid TypedArray index (not a number)', index);
949
+ return undefined;
950
+ }
951
+ return elements[numIndex];
952
+ }
953
+ return undefined;
954
+ }
955
+ if (isIterable(elements)) {
956
+ let i = 0;
957
+ for (const value of elements) {
958
+ if (i === index) {
959
+ return value;
960
+ }
961
+ i++;
962
+ }
963
+ return undefined;
964
+ }
965
+ if (hasForEach(elements)) {
966
+ let result = undefined;
967
+ let shouldContinue = true;
968
+ elements.forEach((value, i) => {
969
+ if (shouldContinue) {
970
+ if (i === index) {
971
+ result = value;
972
+ shouldContinue = false;
973
+ }
974
+ }
975
+ });
976
+ return result;
977
+ }
978
+ }
979
+ if ((typeof elements === 'object') || (typeof elements === 'function')) {
980
+ // Type guard: verify elements is Record-like for property access
981
+ if (isRecordLike(elements)) {
982
+ const propertyKey = typeof index === 'string' || typeof index === 'number' || typeof index === 'symbol' ? index : String(index);
983
+ if ((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(elements, propertyKey)) {
984
+ const stringKey = typeof propertyKey === 'string' ? propertyKey : String(propertyKey);
985
+ return elements[stringKey];
986
+ }
987
+ }
988
+ return undefined;
989
+ }
990
+ return undefined;
991
+ }
992
+ /**
993
+ * Flattens nested arrays to single level (like Array.flat(Infinity)).
994
+ *
995
+ * @param array - Array to flatten
996
+ * @returns Flattened array
997
+ */
998
+ export function flattenArray(array) {
999
+ if (!Array.isArray(array)) {
1000
+ return [array];
1001
+ }
1002
+ const result = [];
1003
+ const flattenRecursive = (arr) => {
1004
+ if (!Array.isArray(arr)) {
1005
+ result.push(arr);
1006
+ return;
1007
+ }
1008
+ arr.forEach((entry) => {
1009
+ flattenRecursive(entry);
1010
+ });
1011
+ };
1012
+ array.forEach((entry) => {
1013
+ flattenRecursive(entry);
1014
+ });
1015
+ return result;
1016
+ }
1017
+ /**
1018
+ * Flattens any collection (arrays, Sets, Maps, objects) to single array.
1019
+ *
1020
+ * @param elements - Collection to flatten
1021
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1022
+ * @returns Flattened array
1023
+ */
1024
+ export function flattenToArray(elements, optionalSkipHasOwnPropertyCheck = false) {
1025
+ const result = [];
1026
+ const flattenRecursive = (value) => {
1027
+ if (Array.isArray(value)) {
1028
+ value.forEach((entry) => {
1029
+ flattenRecursive(entry);
1030
+ });
1031
+ }
1032
+ else if (supportsEach(value)) {
1033
+ each(value, (v) => {
1034
+ flattenRecursive(v);
1035
+ }, optionalSkipHasOwnPropertyCheck);
1036
+ }
1037
+ else {
1038
+ result.push(value);
1039
+ }
1040
+ };
1041
+ flattenRecursive(elements);
1042
+ return result;
1043
+ }
1044
+ /**
1045
+ * Maps and sorts collection to array.
1046
+ *
1047
+ * @param elements - Collection to process
1048
+ * @param comparator - Sort comparison function
1049
+ * @param callback - Optional transform
1050
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1051
+ * @returns Sorted array
1052
+ */
1053
+ export function mapToArraySorted(elements, comparator, callback, optionalSkipHasOwnPropertyCheck = false) {
1054
+ if (callback && (typeof callback !== 'function')) {
1055
+ throw new TypeError('The given callback is not a function');
1056
+ }
1057
+ const keys = sortKeys(elements, comparator, optionalSkipHasOwnPropertyCheck);
1058
+ const result = [];
1059
+ for (const key of keys) {
1060
+ const value = getValueAtIndex(elements, key, optionalSkipHasOwnPropertyCheck);
1061
+ if (!callback) {
1062
+ result.push(value);
1063
+ }
1064
+ else {
1065
+ result.push(callback.call(value, value, key));
1066
+ }
1067
+ }
1068
+ return result;
1069
+ }
1070
+ /**
1071
+ * Generic comparison function for sorting.
1072
+ *
1073
+ * @param a - First value
1074
+ * @param b - Second value
1075
+ * @returns -1 if a < b, 1 if a > b, 0 if equal
1076
+ */
1077
+ export function compare(a, b) {
1078
+ // compare() accepts any types and uses JavaScript's native comparison operators
1079
+ // We can't avoid type coercion here as the function is designed to work with any types
1080
+ // Use Function.prototype.call to compare without type assertions
1081
+ const compareResult = (valueA, valueB) => {
1082
+ // Use JavaScript's native comparison (handles type coercion automatically)
1083
+ // @ts-ignore - Intentional: compare() is designed to work with any types using native JS comparison
1084
+ if (valueA < valueB) {
1085
+ return -1;
1086
+ }
1087
+ // @ts-ignore - Intentional: compare() is designed to work with any types using native JS comparison
1088
+ if (valueA > valueB) {
1089
+ return 1;
1090
+ }
1091
+ return 0;
1092
+ };
1093
+ return compareResult(a, b);
1094
+ }
1095
+ /**
1096
+ * Compares two numbers (for sorting).
1097
+ *
1098
+ * @param a - First number
1099
+ * @param b - Second number
1100
+ * @returns Difference (a - b)
1101
+ */
1102
+ export function compareNumbers(a, b) {
1103
+ return a - b;
1104
+ }
1105
+ /**
1106
+ * Compares two numeric strings (like version numbers).
1107
+ * Splits on '.', compares each part numerically considering length.
1108
+ *
1109
+ * @param a - First numeric string
1110
+ * @param b - Second numeric string
1111
+ * @returns Comparison result
1112
+ */
1113
+ export function compareNumericStrings(a, b) {
1114
+ const aParts = STRING(a).split('.');
1115
+ const bParts = STRING(b).split('.');
1116
+ for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {
1117
+ const aPart = aParts[i].trim();
1118
+ const bPart = bParts[i].trim();
1119
+ if (aPart.length !== bPart.length) {
1120
+ return (aPart.length < bPart.length) ? -1 : 1;
1121
+ }
1122
+ if (aPart !== bPart) {
1123
+ return (aPart < bPart) ? -1 : 1;
1124
+ }
1125
+ }
1126
+ if (aParts.length !== bParts.length) {
1127
+ return (aParts.length < bParts.length) ? -1 : 1;
1128
+ }
1129
+ return 0;
1130
+ }
1131
+ /**
1132
+ * Natural string comparison (numbers in strings compared numerically).
1133
+ * "file5.txt" sorts before "file10.txt".
1134
+ *
1135
+ * @param a - First string
1136
+ * @param b - Second string
1137
+ * @returns Comparison result
1138
+ */
1139
+ export function compareNaturalStrings(a, b) {
1140
+ const re = /(\d+|\D+)/g; // Split into runs of digits or non-digits
1141
+ const aTokens = a.match(re) ?? [];
1142
+ const bTokens = b.match(re) ?? [];
1143
+ const len = Math.min(aTokens.length, bTokens.length);
1144
+ for (let i = 0; i < len; i++) {
1145
+ const x = aTokens[i];
1146
+ const y = bTokens[i];
1147
+ if (x === y) {
1148
+ continue;
1149
+ }
1150
+ // If both are numbers, compare numerically
1151
+ const nx = parseInt(x, 10);
1152
+ const ny = parseInt(y, 10);
1153
+ if (!isNaN(nx) && !isNaN(ny)) {
1154
+ return nx - ny;
1155
+ }
1156
+ // Otherwise compare lexically
1157
+ return x < y ? -1 : 1;
1158
+ }
1159
+ return aTokens.length - bTokens.length;
1160
+ }
1161
+ /**
1162
+ * Checks if object is empty (no own properties).
1163
+ *
1164
+ * @param obj - Object to check
1165
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1166
+ * @returns true if empty
1167
+ */
1168
+ export function isEmptyObject(obj, optionalSkipHasOwnPropertyCheck = false) {
1169
+ if (obj === null || typeof obj !== 'object') {
1170
+ return true;
1171
+ }
1172
+ // Type guard: obj is already verified as object, can iterate
1173
+ if (isRecordLike(obj)) {
1174
+ for (const field in obj) {
1175
+ if ((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(obj, field)) {
1176
+ return false;
1177
+ }
1178
+ }
1179
+ }
1180
+ return true;
1181
+ }
1182
+ /**
1183
+ * Counts own properties in object.
1184
+ *
1185
+ * @param obj - Object to count
1186
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1187
+ * @returns Number of own properties
1188
+ */
1189
+ export function getObjectFieldsCount(obj, optionalSkipHasOwnPropertyCheck = false) {
1190
+ if (obj === null || typeof obj !== 'object') {
1191
+ return 0;
1192
+ }
1193
+ let count = 0;
1194
+ // Type guard: obj is already verified as object
1195
+ if (isRecordLike(obj)) {
1196
+ for (const field in obj) {
1197
+ if ((optionalSkipHasOwnPropertyCheck === true) || Object.prototype.hasOwnProperty.call(obj, field)) {
1198
+ count++;
1199
+ }
1200
+ }
1201
+ }
1202
+ return count;
1203
+ }
1204
+ /**
1205
+ * Capitalizes first character of string.
1206
+ *
1207
+ * @param string - String to capitalize
1208
+ * @returns Capitalized string
1209
+ */
1210
+ export function capitalize(string) {
1211
+ const str = STRING(string).trim();
1212
+ if (str.length <= 0) {
1213
+ return str;
1214
+ }
1215
+ return str.charAt(0).toUpperCase() + str.slice(1);
1216
+ }
1217
+ /**
1218
+ * Checks if string ends with any of the given suffixes.
1219
+ *
1220
+ * @param string - String to check
1221
+ * @param endingCharsStringOrArray - String of chars or array of suffixes
1222
+ * @returns true if ends with any
1223
+ */
1224
+ export function endsWithAny(string, endingCharsStringOrArray) {
1225
+ const str = STRING(string);
1226
+ let endingCharsArray;
1227
+ if (Array.isArray(endingCharsStringOrArray)) {
1228
+ endingCharsArray = endingCharsStringOrArray;
1229
+ }
1230
+ else {
1231
+ endingCharsArray = STRING(endingCharsStringOrArray).split('');
1232
+ }
1233
+ let result = false;
1234
+ each(endingCharsArray, (chars) => {
1235
+ if (str.endsWith(STRING(chars))) {
1236
+ result = true;
1237
+ return false;
1238
+ }
1239
+ });
1240
+ return result;
1241
+ }
1242
+ /**
1243
+ * Checks if string starts with any of the given prefixes.
1244
+ *
1245
+ * @param string - String to check
1246
+ * @param startingCharsStringOrArray - String of chars or array of prefixes
1247
+ * @returns true if starts with any
1248
+ */
1249
+ export function startsWithAny(string, startingCharsStringOrArray) {
1250
+ const str = STRING(string);
1251
+ let startingCharsArray;
1252
+ if (Array.isArray(startingCharsStringOrArray)) {
1253
+ startingCharsArray = startingCharsStringOrArray;
1254
+ }
1255
+ else {
1256
+ startingCharsArray = STRING(startingCharsStringOrArray).split('');
1257
+ }
1258
+ let result = false;
1259
+ each(startingCharsArray, (chars) => {
1260
+ if (str.startsWith(STRING(chars))) {
1261
+ result = true;
1262
+ return false;
1263
+ }
1264
+ });
1265
+ return result;
1266
+ }
1267
+ /**
1268
+ * Trims specific characters from end of string.
1269
+ *
1270
+ * @param string - String to trim
1271
+ * @param trimCharsStringOrArray - Characters to remove
1272
+ * @returns Trimmed string
1273
+ */
1274
+ export function trimEnd(string, trimCharsStringOrArray) {
1275
+ let str = STRING(string);
1276
+ let endingCharsArray;
1277
+ if (Array.isArray(trimCharsStringOrArray)) {
1278
+ endingCharsArray = trimCharsStringOrArray;
1279
+ }
1280
+ else {
1281
+ endingCharsArray = STRING(trimCharsStringOrArray).split('');
1282
+ }
1283
+ let run = true;
1284
+ const trimChars = (chars) => {
1285
+ const charsStr = STRING(chars);
1286
+ if (str.endsWith(charsStr)) {
1287
+ str = str.substring(0, str.length - charsStr.length);
1288
+ run = true;
1289
+ }
1290
+ };
1291
+ while (run) {
1292
+ run = false;
1293
+ each(endingCharsArray, trimChars);
1294
+ }
1295
+ return str;
1296
+ }
1297
+ /**
1298
+ * Trims specific characters from start of string.
1299
+ *
1300
+ * @param string - String to trim
1301
+ * @param trimCharsStringOrArray - Characters to remove
1302
+ * @returns Trimmed string
1303
+ */
1304
+ export function trimStart(string, trimCharsStringOrArray) {
1305
+ let str = STRING(string);
1306
+ let startingCharsArray;
1307
+ if (Array.isArray(trimCharsStringOrArray)) {
1308
+ startingCharsArray = trimCharsStringOrArray;
1309
+ }
1310
+ else {
1311
+ startingCharsArray = STRING(trimCharsStringOrArray).split('');
1312
+ }
1313
+ let run = true;
1314
+ const trimChars = (chars) => {
1315
+ const charsStr = STRING(chars);
1316
+ if (str.startsWith(charsStr)) {
1317
+ str = str.substring(charsStr.length);
1318
+ run = true;
1319
+ }
1320
+ };
1321
+ while (run) {
1322
+ run = false;
1323
+ each(startingCharsArray, trimChars);
1324
+ }
1325
+ return str;
1326
+ }
1327
+ /**
1328
+ * Trims specific characters from both ends of string.
1329
+ *
1330
+ * @param string - String to trim
1331
+ * @param trimCharsStringOrArray - Characters to remove
1332
+ * @returns Trimmed string
1333
+ */
1334
+ export function trim(string, trimCharsStringOrArray) {
1335
+ return trimStart(trimEnd(string, trimCharsStringOrArray), trimCharsStringOrArray);
1336
+ }
1337
+ /**
1338
+ * Checks if collection contains value (string comparison).
1339
+ *
1340
+ * @param array - Collection to search
1341
+ * @param value - Value to find
1342
+ * @returns true if found
1343
+ */
1344
+ export function contains(array, value) {
1345
+ if (!array) {
1346
+ return false;
1347
+ }
1348
+ let result = false;
1349
+ const valueStr = STRING(value);
1350
+ each(array, (val) => {
1351
+ if (STRING(val) === valueStr) {
1352
+ result = true;
1353
+ return false;
1354
+ }
1355
+ });
1356
+ return result;
1357
+ }
1358
+ /**
1359
+ * Checks if collection contains value (case-insensitive).
1360
+ *
1361
+ * @param array - Collection to search
1362
+ * @param value - Value to find
1363
+ * @returns true if found
1364
+ */
1365
+ export function containsCaseInsensitive(array, value) {
1366
+ if (!array) {
1367
+ return false;
1368
+ }
1369
+ let result = false;
1370
+ const valueStr = STRING(value).toLowerCase();
1371
+ each(array, (val) => {
1372
+ if (STRING(val).toLowerCase() === valueStr) {
1373
+ result = true;
1374
+ return false;
1375
+ }
1376
+ });
1377
+ return result;
1378
+ }
1379
+ /**
1380
+ * Checks if collection contains all given values.
1381
+ *
1382
+ * @param array - Collection to search
1383
+ * @param values - Values to find
1384
+ * @returns true if all found
1385
+ */
1386
+ export function containsAll(array, values) {
1387
+ if (!array) {
1388
+ return false;
1389
+ }
1390
+ let result = true;
1391
+ each(values, (value) => {
1392
+ if (!contains(array, value)) {
1393
+ result = false;
1394
+ return false;
1395
+ }
1396
+ });
1397
+ return result;
1398
+ }
1399
+ /**
1400
+ * Checks if collection contains any of given values.
1401
+ *
1402
+ * @param array - Collection to search
1403
+ * @param values - Values to find
1404
+ * @returns true if any found
1405
+ */
1406
+ export function containsAny(array, values) {
1407
+ if (!array) {
1408
+ return false;
1409
+ }
1410
+ let result = false;
1411
+ each(values, (value) => {
1412
+ if (contains(array, value)) {
1413
+ result = true;
1414
+ return false;
1415
+ }
1416
+ });
1417
+ return result;
1418
+ }
1419
+ /**
1420
+ * Checks if collection contains none of given values.
1421
+ *
1422
+ * @param array - Collection to search
1423
+ * @param values - Values to check
1424
+ * @returns true if none found
1425
+ */
1426
+ export function containsNone(array, values) {
1427
+ if (!array) {
1428
+ return true;
1429
+ }
1430
+ let result = true;
1431
+ each(values, (value) => {
1432
+ if (contains(array, value)) {
1433
+ result = false;
1434
+ return false;
1435
+ }
1436
+ });
1437
+ return result;
1438
+ }
1439
+ /**
1440
+ * Finds first element matching predicate, returns {index, value}.
1441
+ *
1442
+ * @param elements - Collection to search
1443
+ * @param callback - Predicate function
1444
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1445
+ * @returns {index, value} or null
1446
+ */
1447
+ export function findIndexValue(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
1448
+ if (typeof callback !== 'function') {
1449
+ throw new TypeError('The given callback is not a function');
1450
+ }
1451
+ let result = null;
1452
+ each(elements, (value, index) => {
1453
+ // Type guard: verify elements is Record-like for callback context
1454
+ if (isRecordLike(elements)) {
1455
+ const propertyKey = typeof index === 'string' || typeof index === 'number' || typeof index === 'symbol' ? index : String(index);
1456
+ const stringKey = typeof propertyKey === 'string' ? propertyKey : String(propertyKey);
1457
+ const elementValue = elements[stringKey];
1458
+ if (callback.call(elementValue, elementValue, index)) {
1459
+ result = { index, value };
1460
+ return false;
1461
+ }
1462
+ }
1463
+ else {
1464
+ // For non-record-like elements, use value directly
1465
+ if (callback.call(value, value, index)) {
1466
+ result = { index, value };
1467
+ return false;
1468
+ }
1469
+ }
1470
+ }, optionalSkipHasOwnPropertyCheck);
1471
+ return result;
1472
+ }
1473
+ /**
1474
+ * Finds first element matching predicate, returns index.
1475
+ *
1476
+ * @param elements - Collection to search
1477
+ * @param callback - Predicate function
1478
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1479
+ * @returns Index/key or null
1480
+ */
1481
+ export function findIndex(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
1482
+ return findIndexValue(elements, callback, optionalSkipHasOwnPropertyCheck)?.index ?? null;
1483
+ }
1484
+ /**
1485
+ * Finds first element matching predicate, returns value.
1486
+ *
1487
+ * @param elements - Collection to search
1488
+ * @param callback - Predicate function
1489
+ * @param optionalSkipHasOwnPropertyCheck - Skip hasOwnProperty
1490
+ * @returns Value or null
1491
+ */
1492
+ export function find(elements, callback, optionalSkipHasOwnPropertyCheck = false) {
1493
+ return findIndexValue(elements, callback, optionalSkipHasOwnPropertyCheck)?.value ?? null;
1494
+ }
1495
+ /**
1496
+ * Generates a base64 string (with +/ replaced by -_) that is guaranteed to be unique.
1497
+ *
1498
+ * @returns Unique ID string
1499
+ */
1500
+ export const uniqueId = (() => {
1501
+ let previousUniqueIdsTime = null;
1502
+ const previousUniqueIds = new Map();
1503
+ const generateUniqueId = () => {
1504
+ let now = null;
1505
+ try {
1506
+ // @ts-ignore -- performance might not exist or might not have legacy timing API
1507
+ // Type guard: check if performance has timing property (legacy API)
1508
+ let timingNavStart = 0;
1509
+ if (hasPerformanceTiming(performance) && 'timing' in performance && performance.timing) {
1510
+ const timing = performance.timing;
1511
+ if ('navigationStart' in timing && typeof timing.navigationStart === 'number') {
1512
+ timingNavStart = timing.navigationStart;
1513
+ }
1514
+ }
1515
+ const timeOrigin = hasPerformanceTiming(performance) && 'timeOrigin' in performance && typeof performance.timeOrigin === 'number' ? performance.timeOrigin : 0;
1516
+ const nowFunc = hasPerformanceTiming(performance) && 'now' in performance && typeof performance.now === 'function' ? performance.now : (() => 0);
1517
+ now = timeOrigin || timingNavStart || 0;
1518
+ now = now + nowFunc();
1519
+ }
1520
+ catch {
1521
+ // Ignore error
1522
+ }
1523
+ now = now || Date.now();
1524
+ now = Math.round(now);
1525
+ const nowBytes = numberToBytes(now);
1526
+ let uuid = null;
1527
+ try {
1528
+ if (typeof crypto?.randomUUID === 'function') {
1529
+ uuid = base64ToBytes(hexToBase64(crypto.randomUUID()));
1530
+ }
1531
+ }
1532
+ catch {
1533
+ // Ignore error
1534
+ }
1535
+ if (!uuid) {
1536
+ const bytesChunkA = numberToBytes(Number((Math.random() + ' ').substring(2, 12).padEnd(10, '0')));
1537
+ const bytesChunkB = numberToBytes(Number((Math.random() + ' ').substring(2, 12).padEnd(10, '0')));
1538
+ const bytesChunkC = numberToBytes(Number((Math.random() + ' ').substring(2, 12).padEnd(10, '0')));
1539
+ const bytesChunkD = numberToBytes(Number((Math.random() + ' ').substring(2, 12).padEnd(10, '0')));
1540
+ uuid = new Uint8Array(bytesChunkA.length + bytesChunkB.length + bytesChunkC.length + bytesChunkD.length);
1541
+ uuid.set(bytesChunkA, 0);
1542
+ uuid.set(bytesChunkB, bytesChunkA.length);
1543
+ uuid.set(bytesChunkC, bytesChunkA.length + bytesChunkB.length);
1544
+ uuid.set(bytesChunkD, bytesChunkA.length + bytesChunkB.length + bytesChunkC.length);
1545
+ }
1546
+ const bytes = new Uint8Array(nowBytes.length + uuid.length);
1547
+ bytes.set(nowBytes, 0);
1548
+ bytes.set(uuid, nowBytes.length);
1549
+ const finalUuid = bytesToBase64(bytes).replaceAll('=', '').replaceAll('+', '-').replaceAll('/', '_');
1550
+ return {
1551
+ time: now,
1552
+ id: finalUuid,
1553
+ };
1554
+ };
1555
+ return () => {
1556
+ while (true) {
1557
+ const result = generateUniqueId();
1558
+ if (previousUniqueIdsTime !== result.time) {
1559
+ previousUniqueIdsTime = result.time;
1560
+ previousUniqueIds.clear();
1561
+ previousUniqueIds.set(result.id, true);
1562
+ return result.id;
1563
+ }
1564
+ else if (previousUniqueIds.get(result.id) !== true) {
1565
+ previousUniqueIds.set(result.id, true);
1566
+ return result.id;
1567
+ }
1568
+ }
1569
+ };
1570
+ })();
1571
+ /**
1572
+ * Generates a base64 string (with +/ replaced by -_) of the current time.
1573
+ *
1574
+ * @param now - Optional time to use
1575
+ * @returns Timestamp string
1576
+ */
1577
+ export const timestamp = (() => {
1578
+ return (now = null) => {
1579
+ let time;
1580
+ if (ISSET(now)) {
1581
+ time = FLOAT_LAX(now);
1582
+ }
1583
+ else {
1584
+ let performanceNow = null;
1585
+ try {
1586
+ // @ts-ignore -- performance might not exist or might not have legacy timing API
1587
+ // Type guard: check if performance has timing property (legacy API)
1588
+ let timingNavStart = 0;
1589
+ if (performance && hasPerformanceTiming(performance) && 'timing' in performance) {
1590
+ const timing = safeGetProperty(performance, 'timing');
1591
+ if (timing && typeof timing === 'object' && timing !== null) {
1592
+ const navStart = safeGetProperty(timing, 'navigationStart');
1593
+ if (typeof navStart === 'number') {
1594
+ timingNavStart = navStart;
1595
+ }
1596
+ }
1597
+ }
1598
+ const timeOrigin = performance && hasPerformanceTiming(performance) && 'timeOrigin' in performance ? safeGetProperty(performance, 'timeOrigin') : undefined;
1599
+ const nowFunc = performance && hasPerformanceTiming(performance) && 'now' in performance ? safeGetProperty(performance, 'now') : undefined;
1600
+ const timeOriginVal = typeof timeOrigin === 'number' ? timeOrigin : 0;
1601
+ const nowFuncVal = typeof nowFunc === 'function' ? nowFunc() : 0;
1602
+ performanceNow = timeOriginVal || timingNavStart || 0;
1603
+ performanceNow = performanceNow + nowFuncVal;
1604
+ }
1605
+ catch {
1606
+ // Ignore error
1607
+ }
1608
+ time = performanceNow || Date.now();
1609
+ }
1610
+ time = Math.round(time);
1611
+ const nowBytes = numberToBytes(time);
1612
+ return bytesToBase64(nowBytes).replaceAll('=', '').replaceAll('+', '-').replaceAll('/', '_');
1613
+ };
1614
+ })();
1615
+ /**
1616
+ * Checks if value is a generator function.
1617
+ *
1618
+ * @param value - Value to check
1619
+ * @returns true if generator, false otherwise
1620
+ */
1621
+ export const isGeneratorFunction = (() => {
1622
+ const GeneratorFunction = (function* () { }).constructor;
1623
+ const AsyncGeneratorFunction = (async function* () { }).constructor;
1624
+ return (func) => {
1625
+ if (typeof func !== 'function') {
1626
+ return false;
1627
+ }
1628
+ // Type guard: verify func has constructor property
1629
+ if (!hasConstructor(func)) {
1630
+ return false;
1631
+ }
1632
+ const constructor = func.constructor;
1633
+ return (constructor === GeneratorFunction || constructor === AsyncGeneratorFunction);
1634
+ };
1635
+ })();
1636
+ /**
1637
+ * Environment-safe btoa.
1638
+ *
1639
+ * @param str - String to encode
1640
+ * @returns Base64 string
1641
+ */
1642
+ export function btoa(str) {
1643
+ if (typeof globalThis.btoa === 'function') {
1644
+ return globalThis.btoa(str);
1645
+ }
1646
+ // Node.js fallback
1647
+ // @ts-ignore -- Buffer might not be defined in non-Node environments
1648
+ if (typeof globalThis.Buffer?.from === 'function') {
1649
+ // @ts-ignore -- Buffer might not be defined in non-Node environments
1650
+ return globalThis.Buffer.from(str, 'binary').toString('base64');
1651
+ }
1652
+ throw new Error('LeUtils.btoa: No btoa implementation found');
1653
+ }
1654
+ /**
1655
+ * Environment-safe atob.
1656
+ *
1657
+ * @param str - Base64 string to decode
1658
+ * @returns Decoded string
1659
+ */
1660
+ export function atob(str) {
1661
+ if (typeof globalThis.atob === 'function') {
1662
+ return globalThis.atob(str);
1663
+ }
1664
+ // Node.js fallback
1665
+ // @ts-ignore -- Buffer might not be defined in non-Node environments
1666
+ if (typeof globalThis.Buffer?.from === 'function') {
1667
+ // @ts-ignore -- Buffer might not be defined in non-Node environments
1668
+ return globalThis.Buffer.from(str, 'base64').toString('binary');
1669
+ }
1670
+ throw new Error('LeUtils.atob: No atob implementation found');
1671
+ }
1672
+ /**
1673
+ * Encodes a UTF-8 string into a base64 string.
1674
+ *
1675
+ * @param string - UTF-8 string
1676
+ * @returns Base64 string
1677
+ */
1678
+ export function utf8ToBase64(string) {
1679
+ return btoa(encodeURIComponent(string).replace(/%([0-9A-F]{2})/g, (_match, p1) => String.fromCharCode(parseInt(p1, 16))));
1680
+ }
1681
+ /**
1682
+ * Decodes a base64 string back into a UTF-8 string.
1683
+ *
1684
+ * @param base64string - Base64 string
1685
+ * @returns Decoded UTF-8 string
1686
+ */
1687
+ export function base64ToUtf8(base64string) {
1688
+ return decodeURIComponent(atob(base64string.trim()).split('').map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join(''));
1689
+ }
1690
+ /**
1691
+ * Converts a base64 string into a hex string.
1692
+ *
1693
+ * @param base64string - Base64 string
1694
+ * @returns Hex string
1695
+ */
1696
+ export function base64ToHex(base64string) {
1697
+ return atob(base64string.trim()).split('').map((c) => ('0' + c.charCodeAt(0).toString(16)).slice(-2)).join('');
1698
+ }
1699
+ /**
1700
+ * Converts a hex string into a base64 string.
1701
+ *
1702
+ * @param hexstring - Hex string
1703
+ * @returns Base64 string
1704
+ */
1705
+ export function hexToBase64(hexstring) {
1706
+ const hexResult = hexstring.replace(/[^0-9A-F]/gi, '').match(/\w{2}/g)?.map((a) => String.fromCharCode(parseInt(a, 16)))?.join('');
1707
+ if (!hexResult) {
1708
+ throw new Error('Invalid hex string: "' + hexstring + '"');
1709
+ }
1710
+ return btoa(hexResult);
1711
+ }
1712
+ /**
1713
+ * Converts a base64 string into bytes (Uint8Array).
1714
+ *
1715
+ * @param base64string - Base64 string
1716
+ * @returns Bytes
1717
+ */
1718
+ export function base64ToBytes(base64string) {
1719
+ const binary = atob(base64string.trim());
1720
+ const len = binary.length;
1721
+ const data = new Uint8Array(len);
1722
+ for (let i = 0; i < len; i++) {
1723
+ data[i] = binary.charCodeAt(i);
1724
+ }
1725
+ return data;
1726
+ }
1727
+ /**
1728
+ * Converts bytes into a base64 string.
1729
+ *
1730
+ * @param arraybuffer - Bytes source
1731
+ * @returns Base64 string
1732
+ */
1733
+ export function bytesToBase64(arraybuffer) {
1734
+ const bytes = new Uint8Array(arraybuffer);
1735
+ const len = bytes.byteLength;
1736
+ let binary = '';
1737
+ for (let i = 0; i < len; i++) {
1738
+ binary += String.fromCharCode(bytes[i]);
1739
+ }
1740
+ return btoa(binary);
1741
+ }
1742
+ /**
1743
+ * Executes callback after ms. Delta time in seconds passed to callback.
1744
+ *
1745
+ * @param callback - Function to call
1746
+ * @param ms - Milliseconds to wait
1747
+ * @returns Handler with remove() method
1748
+ */
1749
+ export function setTimeout(callback, ms) {
1750
+ if (!globalThis?.setTimeout || !globalThis?.clearTimeout) {
1751
+ console.warn('LeUtils.setTimeout: Not supported.');
1752
+ return { remove: () => { } };
1753
+ }
1754
+ const msVal = FLOAT_LAX(ms);
1755
+ let lastTime = globalThis?.performance?.now?.() ?? 0;
1756
+ // Type guard: verify globalThis has setTimeout
1757
+ if (!hasSetTimeout(globalThis)) {
1758
+ console.error('LeUtils.setTimeout: globalThis.setTimeout is not available');
1759
+ return { remove: () => { } };
1760
+ }
1761
+ // @ts-ignore - handler type varies between Node.js (Timeout) and browser (number), using ReturnType below
1762
+ let handler = globalThis.setTimeout(() => {
1763
+ const currentTime = globalThis?.performance?.now?.() ?? 0;
1764
+ try {
1765
+ callback((currentTime - lastTime) / 1000);
1766
+ }
1767
+ catch (e) {
1768
+ console.error(e);
1769
+ }
1770
+ lastTime = currentTime;
1771
+ }, msVal);
1772
+ return {
1773
+ remove: () => {
1774
+ if (handler !== null) {
1775
+ globalThis.clearTimeout(handler);
1776
+ handler = null;
1777
+ }
1778
+ },
1779
+ };
1780
+ }
1781
+ /**
1782
+ * Executes callback every intervalMs. Delta time in seconds passed to callback.
1783
+ *
1784
+ * @param callback - Function to call
1785
+ * @param intervalMs - Interval in milliseconds
1786
+ * @param fireImmediately - Whether to call immediately
1787
+ * @returns Handler with remove() method
1788
+ */
1789
+ export function setInterval(callback, intervalMs = 1000, fireImmediately = false) {
1790
+ const msVal = FLOAT_LAX_ANY(intervalMs, 1000);
1791
+ if (fireImmediately) {
1792
+ try {
1793
+ callback(0);
1794
+ }
1795
+ catch (e) {
1796
+ console.error(e);
1797
+ }
1798
+ }
1799
+ if (!globalThis?.setInterval || !globalThis?.clearInterval) {
1800
+ console.warn('LeUtils.setInterval: Not supported.');
1801
+ return { remove: () => { } };
1802
+ }
1803
+ let lastTime = globalThis?.performance?.now?.() ?? 0;
1804
+ // Type guard: verify globalThis has setInterval
1805
+ if (!hasSetInterval(globalThis)) {
1806
+ console.error('LeUtils.setInterval: globalThis.setInterval is not available');
1807
+ return { remove: () => { } };
1808
+ }
1809
+ // @ts-ignore - handler type varies between Node.js (Timeout) and browser (number), using ReturnType below
1810
+ let handler = globalThis.setInterval(() => {
1811
+ const currentTime = globalThis?.performance?.now?.() ?? 0;
1812
+ try {
1813
+ callback((currentTime - lastTime) / 1000);
1814
+ }
1815
+ catch (e) {
1816
+ console.error(e);
1817
+ }
1818
+ lastTime = currentTime;
1819
+ }, msVal);
1820
+ return {
1821
+ remove: () => {
1822
+ if (handler !== null) {
1823
+ globalThis.clearInterval(handler);
1824
+ handler = null;
1825
+ }
1826
+ },
1827
+ };
1828
+ }
1829
+ /**
1830
+ * Executes callback after frames. Delta time in seconds passed to callback.
1831
+ *
1832
+ * @param callback - Function to call
1833
+ * @param frames - Number of frames to wait
1834
+ * @returns Handler with remove() method
1835
+ */
1836
+ export function setAnimationFrameTimeout(callback, frames = 1) {
1837
+ if (!globalThis?.requestAnimationFrame || !globalThis?.cancelAnimationFrame) {
1838
+ console.warn('LeUtils.setAnimationFrameTimeout: Not supported.');
1839
+ return { remove: () => { } };
1840
+ }
1841
+ let framesLeft = INT_LAX_ANY(frames, 1);
1842
+ let run = true;
1843
+ let requestAnimationFrameId = null;
1844
+ let lastTime = globalThis?.performance?.now?.() ?? 0;
1845
+ const tick = () => {
1846
+ if (run) {
1847
+ if (framesLeft <= 0) {
1848
+ run = false;
1849
+ requestAnimationFrameId = null;
1850
+ const currentTime = globalThis?.performance?.now?.() ?? 0;
1851
+ try {
1852
+ callback((currentTime - lastTime) / 1000);
1853
+ }
1854
+ catch (e) {
1855
+ console.error(e);
1856
+ }
1857
+ lastTime = currentTime;
1858
+ return;
1859
+ }
1860
+ framesLeft--;
1861
+ requestAnimationFrameId = globalThis.requestAnimationFrame(tick);
1862
+ }
1863
+ };
1864
+ tick();
1865
+ return {
1866
+ remove: () => {
1867
+ run = false;
1868
+ if (requestAnimationFrameId !== null) {
1869
+ globalThis.cancelAnimationFrame(requestAnimationFrameId);
1870
+ requestAnimationFrameId = null;
1871
+ }
1872
+ },
1873
+ };
1874
+ }
1875
+ /**
1876
+ * Executes callback every intervalFrames. Delta time in seconds passed to callback.
1877
+ *
1878
+ * @param callback - Function to call
1879
+ * @param intervalFrames - Interval in frames
1880
+ * @param fireImmediately - Whether to call immediately
1881
+ * @returns Handler with remove() method
1882
+ */
1883
+ export function setAnimationFrameInterval(callback, intervalFrames = 1, fireImmediately = false) {
1884
+ const iFrames = INT_LAX_ANY(intervalFrames, 1);
1885
+ if (fireImmediately) {
1886
+ try {
1887
+ callback(0);
1888
+ }
1889
+ catch (e) {
1890
+ console.error(e);
1891
+ }
1892
+ }
1893
+ if (!globalThis?.requestAnimationFrame || !globalThis?.cancelAnimationFrame) {
1894
+ console.warn('LeUtils.setAnimationFrameInterval: Not supported.');
1895
+ return { remove: () => { } };
1896
+ }
1897
+ let run = true;
1898
+ let requestAnimationFrameId = null;
1899
+ let lastTimestamp = 0;
1900
+ let totalTime = 0;
1901
+ let frames = iFrames;
1902
+ const tick = (timestampVal) => {
1903
+ if (run) {
1904
+ if (lastTimestamp === 0) {
1905
+ lastTimestamp = timestampVal;
1906
+ }
1907
+ totalTime += (timestampVal - lastTimestamp);
1908
+ lastTimestamp = timestampVal;
1909
+ frames--;
1910
+ if (frames <= 0) {
1911
+ try {
1912
+ callback(totalTime / 1000);
1913
+ }
1914
+ catch (e) {
1915
+ console.error(e);
1916
+ }
1917
+ totalTime = 0;
1918
+ frames = iFrames;
1919
+ }
1920
+ if (run) {
1921
+ requestAnimationFrameId = globalThis.requestAnimationFrame(tick);
1922
+ }
1923
+ }
1924
+ };
1925
+ globalThis.requestAnimationFrame(tick);
1926
+ return {
1927
+ remove: () => {
1928
+ run = false;
1929
+ if (requestAnimationFrameId !== null) {
1930
+ globalThis.cancelAnimationFrame(requestAnimationFrameId);
1931
+ requestAnimationFrameId = null;
1932
+ }
1933
+ },
1934
+ };
1935
+ }
1936
+ /**
1937
+ * Returns a promise resolved after ms.
1938
+ *
1939
+ * @param ms - Milliseconds to wait
1940
+ * @returns Promise resolving to delta time
1941
+ */
1942
+ export function promiseTimeout(ms) {
1943
+ const msVal = FLOAT_LAX(ms);
1944
+ if (msVal <= 0) {
1945
+ return Promise.resolve(0);
1946
+ }
1947
+ return new Promise((resolve) => setTimeout(resolve, msVal));
1948
+ }
1949
+ /**
1950
+ * Returns a promise resolved after frames.
1951
+ *
1952
+ * @param frames - Number of frames to wait
1953
+ * @returns Promise resolving to delta time
1954
+ */
1955
+ export function promiseAnimationFrameTimeout(frames) {
1956
+ const iFrames = INT_LAX(frames);
1957
+ if (iFrames <= 0) {
1958
+ return Promise.resolve(0);
1959
+ }
1960
+ return new Promise((resolve) => setAnimationFrameTimeout(resolve, iFrames));
1961
+ }
1962
+ /**
1963
+ * Fetch with retry and abort functionality.
1964
+ *
1965
+ * @param url - URL to fetch
1966
+ * @param options - Fetch options including retries and delay
1967
+ * @returns Object with then(), catch(), finally(), remove(), isRemoved()
1968
+ */
1969
+ export function fetch(url, options) {
1970
+ let currentRetries = 0;
1971
+ // Type guard: check if options has retries property
1972
+ const retriesValue = isRecordLike(options) && 'retries' in options ? options.retries : undefined;
1973
+ const retries = INT_LAX(retriesValue);
1974
+ let controllerAborted = false;
1975
+ let controller = null;
1976
+ if (globalThis?.AbortController) {
1977
+ controller = new AbortController();
1978
+ }
1979
+ let promise = (async () => {
1980
+ const attemptFetch = async () => {
1981
+ if (controllerAborted || controller?.signal?.aborted) {
1982
+ throw new Error('Aborted');
1983
+ }
1984
+ try {
1985
+ const response = await globalThis.fetch(url, {
1986
+ signal: controller?.signal,
1987
+ ...(isRecordLike(options) ? options : {}),
1988
+ // @ts-ignore -- removing custom options from standard fetch options
1989
+ retries: undefined,
1990
+ // @ts-ignore -- removing custom options from standard fetch options
1991
+ delay: undefined,
1992
+ });
1993
+ if (!response.ok) {
1994
+ throw new Error('Network request failed: ' + response.status + ' ' + response.statusText);
1995
+ }
1996
+ return response;
1997
+ }
1998
+ catch (error) {
1999
+ if (controllerAborted || controller?.signal?.aborted) {
2000
+ throw new Error('Aborted');
2001
+ }
2002
+ if (currentRetries >= retries) {
2003
+ throw error;
2004
+ }
2005
+ currentRetries++;
2006
+ // Type guard: check if delay is a function or a value
2007
+ const delayValue = isRecordLike(options) && 'delay' in options ? options.delay : undefined;
2008
+ const delay = typeof delayValue === 'function' ? INT_LAX_ANY(delayValue(currentRetries), 500) : INT_LAX_ANY(delayValue, 500);
2009
+ await promiseTimeout(delay);
2010
+ return await attemptFetch();
2011
+ }
2012
+ };
2013
+ return await attemptFetch();
2014
+ })();
2015
+ const result = {
2016
+ then: () => result,
2017
+ catch: () => result,
2018
+ finally: () => result,
2019
+ remove: () => undefined,
2020
+ isRemoved: () => false,
2021
+ };
2022
+ result.then = (...args) => {
2023
+ // Type guard: verify promise is a Promise before chaining
2024
+ if (isPromise(promise)) {
2025
+ // Type guard: promise.then() returns a Promise, which we know is Promise<Response> in this context
2026
+ // We've already verified promise is a Promise, so the result of .then() is also a Promise
2027
+ // The generic Promise chain maintains the Response type in this context
2028
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
2029
+ const thenResult = promise.then(...args);
2030
+ promise = thenResult;
2031
+ }
2032
+ return result;
2033
+ };
2034
+ result.catch = (...args) => {
2035
+ // Type guard: verify promise is a Promise before chaining
2036
+ if (isPromise(promise)) {
2037
+ // Type guard: promise.catch() returns a Promise, which we know is Promise<Response> in this context
2038
+ // We've already verified promise is a Promise, so the result of .catch() is also a Promise
2039
+ // The generic Promise chain maintains the Response type in this context
2040
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
2041
+ const catchResult = promise.catch(...args);
2042
+ promise = catchResult;
2043
+ }
2044
+ return result;
2045
+ };
2046
+ result.finally = (...args) => {
2047
+ // Type guard: verify promise is a Promise before chaining
2048
+ if (isPromise(promise)) {
2049
+ // Type guard: promise.finally() returns a Promise, which we know is Promise<Response> in this context
2050
+ // We've already verified promise is a Promise, so the result of .finally() is also a Promise
2051
+ // The generic Promise chain maintains the Response type in this context
2052
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
2053
+ const finallyResult = promise.finally(...args);
2054
+ promise = finallyResult;
2055
+ }
2056
+ return result;
2057
+ };
2058
+ result.remove = (...args) => {
2059
+ controllerAborted = true;
2060
+ if (controller) {
2061
+ controller.abort(...args);
2062
+ }
2063
+ return result;
2064
+ };
2065
+ result.isRemoved = () => (controllerAborted || !!controller?.signal?.aborted);
2066
+ return result;
2067
+ }
2068
+ /**
2069
+ * Cached version of fetch.
2070
+ *
2071
+ * @param url - URL to fetch
2072
+ * @param options - Fetch options
2073
+ * @param responseFunction - Optional function to process response before caching
2074
+ * @returns Promise resolving to data
2075
+ */
2076
+ export const cachedFetch = (() => {
2077
+ const cache = new Map();
2078
+ return async (url, options, responseFunction) => {
2079
+ if (cache.has(url)) {
2080
+ const result = cache.get(url);
2081
+ // Type guard: check result structure
2082
+ if (isRecordLike(result)) {
2083
+ if ('data' in result && result.data !== undefined) {
2084
+ return result.data;
2085
+ }
2086
+ if ('promise' in result && isPromise(result.promise)) {
2087
+ return await result.promise;
2088
+ }
2089
+ if ('error' in result && result.error !== undefined) {
2090
+ throw result.error;
2091
+ }
2092
+ }
2093
+ return null;
2094
+ }
2095
+ // Type guard: fetch returns Promise<Response>
2096
+ // Type guard: verify options is RequestInit-like for fetch
2097
+ // RequestInit is compatible with Record<string, unknown>, so we can use it directly
2098
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
2099
+ const fetchOptions = isRecordLike(options) ? options : undefined;
2100
+ const fetchPromise = globalThis.fetch(url, fetchOptions);
2101
+ const promise = fetchPromise
2102
+ .then(async (response) => {
2103
+ const data = responseFunction ? (await responseFunction(response)) : response;
2104
+ // Type guard: check if options has verify function
2105
+ if (isRecordLike(options) && 'verify' in options) {
2106
+ const verify = options.verify;
2107
+ if (typeof verify === 'function') {
2108
+ await verify(data, response);
2109
+ }
2110
+ }
2111
+ return data;
2112
+ })
2113
+ .then((data) => {
2114
+ cache.set(url, { data });
2115
+ return data;
2116
+ })
2117
+ .catch((error) => {
2118
+ cache.set(url, { error });
2119
+ throw error;
2120
+ });
2121
+ if (!cache.has(url)) {
2122
+ cache.set(url, { promise });
2123
+ }
2124
+ return await promise;
2125
+ };
2126
+ })();
2127
+ /**
2128
+ * Creates a new transactional value.
2129
+ *
2130
+ * @param value - Initial value
2131
+ * @returns Transactional value object
2132
+ */
2133
+ export function createTransactionalValue(value) {
2134
+ return {
2135
+ value: (typeof value === 'undefined') ? null : value,
2136
+ changes: [],
2137
+ };
2138
+ }
2139
+ /**
2140
+ * Validates a transactional value structure.
2141
+ *
2142
+ * @param transactionalValue - Value to check
2143
+ * @returns true if valid
2144
+ */
2145
+ export function isTransactionalValueValid(transactionalValue) {
2146
+ return ((typeof transactionalValue === 'object') &&
2147
+ (transactionalValue !== null) &&
2148
+ isTransactionalValue(transactionalValue));
2149
+ }
2150
+ /**
2151
+ * Private helper to ensure valid transactional value.
2152
+ */
2153
+ function checkTransactionalValue(transactionalValue) {
2154
+ if (!isTransactionalValueValid(transactionalValue)) {
2155
+ throw new Error('Invalid TransactionalValue provided');
2156
+ }
2157
+ }
2158
+ /**
2159
+ * Private helper to find a change by ID.
2160
+ */
2161
+ function findTransactionalValueChange(transactionalValue, changeId) {
2162
+ for (let i = 0; i < transactionalValue.changes.length; i++) {
2163
+ if (transactionalValue.changes[i].id === changeId) {
2164
+ return { change: transactionalValue.changes[i], index: i };
2165
+ }
2166
+ }
2167
+ return null;
2168
+ }
2169
+ /**
2170
+ * Converts transactional value to string representation of its state.
2171
+ *
2172
+ * @param transactionalValue - Transactional value
2173
+ * @returns State string
2174
+ */
2175
+ export function transactionalValueToString(transactionalValue) {
2176
+ if (!isTransactionalValueValid(transactionalValue)) {
2177
+ return STRING(transactionalValue);
2178
+ }
2179
+ if (transactionalValue.changes.length <= 0) {
2180
+ return STRING(transactionalValue.value);
2181
+ }
2182
+ let valuesString = STRING(transactionalValue.value);
2183
+ for (let i = 0; i < transactionalValue.changes.length; i++) {
2184
+ valuesString += ' -> ' + STRING(transactionalValue.changes[i].value);
2185
+ }
2186
+ return STRING(transactionalValue.changes[transactionalValue.changes.length - 1].value) + ' (' + valuesString + ')';
2187
+ }
2188
+ /**
2189
+ * Sets and commits a value, clearing all uncommitted changes.
2190
+ *
2191
+ * @param transactionalValue - Transactional value
2192
+ * @param value - New value
2193
+ */
2194
+ export function transactionSetAndCommit(transactionalValue, value) {
2195
+ checkTransactionalValue(transactionalValue);
2196
+ transactionalValue.value = (typeof value === 'undefined') ? null : value;
2197
+ transactionalValue.changes = [];
2198
+ }
2199
+ /**
2200
+ * Adds an uncommitted change. Returns change ID.
2201
+ *
2202
+ * @param transactionalValue - Transactional value
2203
+ * @param value - New value
2204
+ * @returns Change ID
2205
+ */
2206
+ export function transactionSetWithoutCommitting(transactionalValue, value) {
2207
+ checkTransactionalValue(transactionalValue);
2208
+ const id = uniqueId();
2209
+ transactionalValue.changes.push({ id, value: (typeof value === 'undefined') ? null : value });
2210
+ return id;
2211
+ }
2212
+ /**
2213
+ * Commits a specific change by ID.
2214
+ *
2215
+ * @param transactionalValue - Transactional value
2216
+ * @param changeId - Change ID
2217
+ * @returns true if committed
2218
+ */
2219
+ export function transactionCommitChange(transactionalValue, changeId) {
2220
+ checkTransactionalValue(transactionalValue);
2221
+ const result = findTransactionalValueChange(transactionalValue, changeId);
2222
+ if (result === null) {
2223
+ return false;
2224
+ }
2225
+ transactionalValue.value = result.change.value;
2226
+ transactionalValue.changes.splice(0, result.index + 1);
2227
+ return true;
2228
+ }
2229
+ /**
2230
+ * Cancels a specific change by ID.
2231
+ *
2232
+ * @param transactionalValue - Transactional value
2233
+ * @param changeId - Change ID
2234
+ * @returns true if cancelled
2235
+ */
2236
+ export function transactionCancelChange(transactionalValue, changeId) {
2237
+ checkTransactionalValue(transactionalValue);
2238
+ const result = findTransactionalValueChange(transactionalValue, changeId);
2239
+ if (result === null) {
2240
+ return false;
2241
+ }
2242
+ transactionalValue.changes.splice(result.index, 1);
2243
+ return true;
2244
+ }
2245
+ /**
2246
+ * Checks if a change is still relevant.
2247
+ *
2248
+ * @param transactionalValue - Transactional value
2249
+ * @param changeId - Change ID
2250
+ * @returns true if relevant
2251
+ */
2252
+ export function transactionIsChangeRelevant(transactionalValue, changeId) {
2253
+ checkTransactionalValue(transactionalValue);
2254
+ return (findTransactionalValueChange(transactionalValue, changeId) !== null);
2255
+ }
2256
+ /**
2257
+ * Gets current committed value.
2258
+ *
2259
+ * @param transactionalValue - Transactional value
2260
+ * @returns Committed value
2261
+ */
2262
+ export function transactionGetCommittedValue(transactionalValue) {
2263
+ checkTransactionalValue(transactionalValue);
2264
+ return transactionalValue.value;
2265
+ }
2266
+ /**
2267
+ * Gets current value (including most recent uncommitted change).
2268
+ *
2269
+ * @param transactionalValue - Transactional value
2270
+ * @returns Current value
2271
+ */
2272
+ export function transactionGetValue(transactionalValue) {
2273
+ checkTransactionalValue(transactionalValue);
2274
+ if (transactionalValue.changes.length <= 0) {
2275
+ return transactionalValue.value;
2276
+ }
2277
+ return transactionalValue.changes[transactionalValue.changes.length - 1].value;
2278
+ }
2279
+ /**
2280
+ * Performs a deep equality comparison between two collections, sorting on keys first.
2281
+ *
2282
+ * @param elementsA - First collection
2283
+ * @param elementsB - Second collection
2284
+ * @param ignoreKeys - Keys to ignore
2285
+ * @returns true if equivalent
2286
+ */
2287
+ export function equalsMapLike(elementsA, elementsB, ignoreKeys = []) {
2288
+ const sortKeyValueArrays = (pairA, pairB) => {
2289
+ // Type guard: verify both are pairs
2290
+ if (isPair(pairA) && isPair(pairB)) {
2291
+ return compare(pairA[0], pairB[0]);
2292
+ }
2293
+ return 0;
2294
+ };
2295
+ // Type guard: verify mapToArray returns array of pairs
2296
+ const aArray = mapToArray(elementsA, (value, key) => [key, value]);
2297
+ const bArray = mapToArray(elementsB, (value, key) => [key, value]);
2298
+ const aPairs = (Array.isArray(aArray) ? aArray.filter((item) => isPair(item)) : []).sort(sortKeyValueArrays);
2299
+ const bPairs = (Array.isArray(bArray) ? bArray.filter((item) => isPair(item)) : []).sort(sortKeyValueArrays);
2300
+ const ignore = (typeof ignoreKeys === 'string') ? ARRAY(ignoreKeys) : mapToArray(ignoreKeys);
2301
+ let i = 0, j = 0;
2302
+ while (i < aPairs.length && j < bPairs.length) {
2303
+ const [ka, va] = aPairs[i];
2304
+ const [kb, vb] = bPairs[j];
2305
+ if (ignore.includes(ka)) {
2306
+ i++;
2307
+ if (ignore.includes(kb))
2308
+ j++;
2309
+ continue;
2310
+ }
2311
+ if (ignore.includes(kb)) {
2312
+ j++;
2313
+ continue;
2314
+ }
2315
+ if (!equals(ka, kb) || !equals(va, vb))
2316
+ return false;
2317
+ i++;
2318
+ j++;
2319
+ }
2320
+ while (i < aPairs.length && ignore.includes(aPairs[i][0]))
2321
+ i++;
2322
+ if (i < aPairs.length)
2323
+ return false;
2324
+ while (j < bPairs.length && ignore.includes(bPairs[j][0]))
2325
+ j++;
2326
+ return j >= bPairs.length;
2327
+ }
2328
+ /**
2329
+ * Executes callback when document is ready.
2330
+ *
2331
+ * @param callback - Function to call
2332
+ * @returns Handler with remove() method
2333
+ */
2334
+ export function onDomReady(callback) {
2335
+ // @ts-ignore -- document might not be defined in non-browser environments
2336
+ if (!globalThis?.document?.addEventListener) {
2337
+ console.warn('LeUtils.onDomReady: No document found.');
2338
+ return { remove: () => { } };
2339
+ }
2340
+ // @ts-ignore -- document might not be defined in non-browser environments
2341
+ if (globalThis.document.readyState === 'interactive' || globalThis.document.readyState === 'complete') {
2342
+ return setTimeout(callback, 0);
2343
+ }
2344
+ let listening = true;
2345
+ const wrapper = () => {
2346
+ if (listening) {
2347
+ listening = false;
2348
+ // @ts-ignore -- document might not be defined in non-browser environments
2349
+ globalThis.document.removeEventListener('DOMContentLoaded', wrapper);
2350
+ callback();
2351
+ }
2352
+ };
2353
+ // @ts-ignore -- document might not be defined in non-browser environments
2354
+ globalThis.document.addEventListener('DOMContentLoaded', wrapper);
2355
+ return {
2356
+ remove: () => {
2357
+ if (listening) {
2358
+ listening = false;
2359
+ // @ts-ignore -- document might not be defined in non-browser environments
2360
+ globalThis.document.removeEventListener('DOMContentLoaded', wrapper);
2361
+ }
2362
+ },
2363
+ };
2364
+ }
2365
+ /**
2366
+ * Parses version string into object with comparison helpers.
2367
+ *
2368
+ * @param version - Version string or object
2369
+ * @returns Version object with comparison methods
2370
+ */
2371
+ export function parseVersionString(version) {
2372
+ // Type guard: verify version has major, minor, patch
2373
+ if (isVersionObject(version)) {
2374
+ const major = INT_LAX(version.major);
2375
+ const minor = INT_LAX(version.minor);
2376
+ const patch = INT_LAX(version.patch);
2377
+ return {
2378
+ major,
2379
+ minor,
2380
+ patch,
2381
+ toString: () => `${major}.${minor}.${patch}`,
2382
+ equals: (other) => {
2383
+ if (!isVersionObject(other)) {
2384
+ return false;
2385
+ }
2386
+ return major === INT_LAX(other.major) && minor === INT_LAX(other.minor) && patch === INT_LAX(other.patch);
2387
+ },
2388
+ largerThan: (other) => {
2389
+ if (!isVersionObject(other)) {
2390
+ return false;
2391
+ }
2392
+ const otherMajor = INT_LAX(other.major);
2393
+ const otherMinor = INT_LAX(other.minor);
2394
+ const otherPatch = INT_LAX(other.patch);
2395
+ return (major > otherMajor) || (major === otherMajor && minor > otherMinor) || (major === otherMajor && minor === otherMinor && patch > otherPatch);
2396
+ },
2397
+ largerThanOrEquals: (other) => {
2398
+ if (!isVersionObject(other)) {
2399
+ return false;
2400
+ }
2401
+ const otherMajor = INT_LAX(other.major);
2402
+ const otherMinor = INT_LAX(other.minor);
2403
+ const otherPatch = INT_LAX(other.patch);
2404
+ return (major > otherMajor) || (major === otherMajor && minor > otherMinor) || (major === otherMajor && minor === otherMinor && patch >= otherPatch);
2405
+ },
2406
+ smallerThan: (other) => {
2407
+ if (!isVersionObject(other)) {
2408
+ return false;
2409
+ }
2410
+ const otherMajor = INT_LAX(other.major);
2411
+ const otherMinor = INT_LAX(other.minor);
2412
+ const otherPatch = INT_LAX(other.patch);
2413
+ return !((major > otherMajor) || (major === otherMajor && minor > otherMinor) || (major === otherMajor && minor === otherMinor && patch >= otherPatch));
2414
+ },
2415
+ smallerThanOrEquals: (other) => {
2416
+ if (!isVersionObject(other)) {
2417
+ return false;
2418
+ }
2419
+ const otherMajor = INT_LAX(other.major);
2420
+ const otherMinor = INT_LAX(other.minor);
2421
+ const otherPatch = INT_LAX(other.patch);
2422
+ return !((major > otherMajor) || (major === otherMajor && minor > otherMinor) || (major === otherMajor && minor === otherMinor && patch > otherPatch));
2423
+ },
2424
+ };
2425
+ }
2426
+ const v = STRING(version).trim();
2427
+ const parts = v.split(' ')[0].split('-')[0].split('.');
2428
+ const major = INT_LAX(parts[0]);
2429
+ const minor = INT_LAX(parts[1]);
2430
+ const patch = INT_LAX(parts[2]);
2431
+ const obj = {
2432
+ major,
2433
+ minor,
2434
+ patch,
2435
+ toString: () => `${major}.${minor}.${patch}`,
2436
+ equals: (other) => {
2437
+ const o = parseVersionString(other);
2438
+ return major === o.major && minor === o.minor && patch === o.patch;
2439
+ },
2440
+ largerThan: (other) => {
2441
+ const o = parseVersionString(other);
2442
+ if (major > o.major)
2443
+ return true;
2444
+ if (major < o.major)
2445
+ return false;
2446
+ if (minor > o.minor)
2447
+ return true;
2448
+ if (minor < o.minor)
2449
+ return false;
2450
+ return patch > o.patch;
2451
+ },
2452
+ largerThanOrEquals: (other) => {
2453
+ const o = parseVersionString(other);
2454
+ if (major > o.major)
2455
+ return true;
2456
+ if (major < o.major)
2457
+ return false;
2458
+ if (minor > o.minor)
2459
+ return true;
2460
+ if (minor < o.minor)
2461
+ return false;
2462
+ return patch >= o.patch;
2463
+ },
2464
+ smallerThan: (other) => {
2465
+ return !obj.largerThanOrEquals(other);
2466
+ },
2467
+ smallerThanOrEquals: (other) => {
2468
+ return !obj.largerThan(other);
2469
+ },
2470
+ };
2471
+ return obj;
2472
+ }
2473
+ /**
2474
+ * Increases numeric string by 1 without limit.
2475
+ *
2476
+ * @param str - Numeric string
2477
+ * @returns Increased string
2478
+ */
2479
+ export function increaseNumericStringByOne(str) {
2480
+ const s = (typeof str !== 'string') ? String(str) : str;
2481
+ if (!s || /[^0-9]/.test(s))
2482
+ return '1';
2483
+ const chars = s.split('');
2484
+ for (let i = chars.length - 1; i >= 0; i--) {
2485
+ const n = parseInt(chars[i], 10);
2486
+ if (n < 9) {
2487
+ chars[i] = String(n + 1);
2488
+ return chars.join('');
2489
+ }
2490
+ chars[i] = '0';
2491
+ }
2492
+ return '1' + chars.join('');
2493
+ }
2494
+ /**
2495
+ * Checks if collection contains all given values (case-insensitive).
2496
+ *
2497
+ * @param array - Collection to search
2498
+ * @param values - Values to find
2499
+ * @returns true if all found
2500
+ */
2501
+ export function containsAllCaseInsensitive(array, values) {
2502
+ if (!array)
2503
+ return false;
2504
+ let result = true;
2505
+ each(values, (v) => {
2506
+ if (!containsCaseInsensitive(array, v)) {
2507
+ result = false;
2508
+ return false;
2509
+ }
2510
+ });
2511
+ return result;
2512
+ }
2513
+ /**
2514
+ * Checks if collection contains any of given values (case-insensitive).
2515
+ *
2516
+ * @param array - Collection to search
2517
+ * @param values - Values to find
2518
+ * @returns true if any found
2519
+ */
2520
+ export function containsAnyCaseInsensitive(array, values) {
2521
+ if (!array)
2522
+ return false;
2523
+ let result = false;
2524
+ each(values, (v) => {
2525
+ if (containsCaseInsensitive(array, v)) {
2526
+ result = true;
2527
+ return false;
2528
+ }
2529
+ });
2530
+ return result;
2531
+ }
2532
+ /**
2533
+ * Checks if collection contains none of given values (case-insensitive).
2534
+ *
2535
+ * @param array - Collection to search
2536
+ * @param values - Values to find
2537
+ * @returns true if none found
2538
+ */
2539
+ export function containsNoneCaseInsensitive(array, values) {
2540
+ if (!array)
2541
+ return true;
2542
+ let result = true;
2543
+ each(values, (v) => {
2544
+ if (containsCaseInsensitive(array, v)) {
2545
+ result = false;
2546
+ return false;
2547
+ }
2548
+ });
2549
+ return result;
2550
+ }
2551
+ /**
2552
+ * Returns string with start/end trimmed and ensures valid sentence ending.
2553
+ *
2554
+ * @param sentence - String to purge
2555
+ * @returns Purged sentence
2556
+ */
2557
+ export function purgeSentence(sentence) {
2558
+ let s = trimEnd(STRING(sentence).trim(), '.: \r\n\t');
2559
+ s += (endsWithAny(s, '!?;') ? '' : '.');
2560
+ return s;
2561
+ }
2562
+ /**
2563
+ * Obtains error message from any input.
2564
+ *
2565
+ * @param error - Error source
2566
+ * @returns Error message string
2567
+ */
2568
+ export function purgeErrorMessage(error) {
2569
+ let err = error;
2570
+ // Type guard: check if error has message property
2571
+ if (hasErrorMessage(err)) {
2572
+ err = err.message;
2573
+ }
2574
+ if (typeof err === 'string') {
2575
+ const errorParts = err.split('threw an error:');
2576
+ err = errorParts[errorParts.length - 1];
2577
+ }
2578
+ else {
2579
+ try {
2580
+ err = JSON.stringify(err);
2581
+ }
2582
+ catch {
2583
+ err = 'An unknown error occurred';
2584
+ }
2585
+ }
2586
+ return STRING(err).trim();
2587
+ }
2588
+ /**
2589
+ * Generates permutations of names (e.g. foobar, fooBar, FooBar, foo-bar, foo_bar).
2590
+ *
2591
+ * @param names - Parts to combine
2592
+ * @returns Array of permutations
2593
+ */
2594
+ export function generateNamePermutations(...names) {
2595
+ const n = flattenToArray(names)
2596
+ .map((name) => STRING(name).trim().toLowerCase())
2597
+ .filter((name) => (name.length > 0));
2598
+ const results = [];
2599
+ if (n.length > 0) {
2600
+ results.push(n.join('')); //foobar
2601
+ results.push(n.map(capitalize).join('')); //FooBar
2602
+ }
2603
+ if (n.length > 1) {
2604
+ results.push([n[0]].concat(n.slice(1).map(capitalize)).join('')); //fooBar
2605
+ results.push(n.join('-')); //foo-bar
2606
+ results.push(n.join('_')); //foo_bar
2607
+ }
2608
+ return results;
2609
+ }
2610
+ /**
2611
+ * Compares two strings generated by LeUtils.timestamp(). Primarily used for sorting.
2612
+ */
2613
+ export function compareTimestampStrings(a, b) {
2614
+ try {
2615
+ const ta = base64ToHex(STRING(a).replaceAll('-', '+').replaceAll('_', '/'));
2616
+ const tb = base64ToHex(STRING(b).replaceAll('-', '+').replaceAll('_', '/'));
2617
+ return compare(ta, tb);
2618
+ }
2619
+ catch {
2620
+ return 0;
2621
+ }
2622
+ }
2623
+ /**
2624
+ * Returns true if the user is on a smartphone device (mobile).
2625
+ */
2626
+ export function platformIsMobile() {
2627
+ // Type guard: check navigator and globalThis for vendor/opera properties
2628
+ let userAgent = globalThis?.navigator?.userAgent || '';
2629
+ if (!userAgent && globalThis?.navigator && hasNavigatorVendor(globalThis.navigator)) {
2630
+ userAgent = globalThis.navigator.vendor || '';
2631
+ }
2632
+ if (!userAgent && hasOpera(globalThis)) {
2633
+ const operaValue = safeGetProperty(globalThis, 'opera');
2634
+ userAgent = typeof operaValue === 'string' ? operaValue : '';
2635
+ }
2636
+ const a = STRING(userAgent || '');
2637
+ return !!(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series([46])0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i
2638
+ .test(a) ||
2639
+ /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br([ev])w|bumb|bw-([nu])|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do([cp])o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly([-_])|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-([mpt])|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c([- _agpst])|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac([ \-/])|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja([tv])a|jbro|jemu|jigs|kddi|keji|kgt([ /])|klon|kpt |kwc-|kyo([ck])|le(no|xi)|lg( g|\/([klu])|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t([- ov])|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30([02])|n50([025])|n7(0([01])|10)|ne(([cm])-|on|tf|wf|wg|wt)|nok([6i])|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan([adt])|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c([-01])|47|mc|nd|ri)|sgh-|shar|sie([-m])|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel([im])|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c([- ])|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i
2640
+ .test(a));
2641
+ }
2642
+ /**
2643
+ * Returns true if the user has a cursor (mouse).
2644
+ */
2645
+ export function platformHasCursor() {
2646
+ return !platformIsMobile() && !globalThis?.matchMedia?.('(any-hover: none)')?.matches;
2647
+ }
2648
+ /**
2649
+ * Returns string with email purged (lowercased, trimmed).
2650
+ */
2651
+ export function purgeEmail(email) {
2652
+ const e = STRING(email).trim().toLowerCase().replace(/\s/g, '');
2653
+ if (!e.includes('@') || !e.includes('.')) {
2654
+ return '';
2655
+ }
2656
+ return e;
2657
+ }
2658
+ /**
2659
+ * Returns true if the focus is effectively clear, meaning that the user is not typing in an input field.
2660
+ */
2661
+ export const isFocusClear = (() => {
2662
+ const inputTypes = ['text', 'search', 'email', 'number', 'password', 'tel', 'time', 'url', 'week', 'month', 'date', 'datetime-local'];
2663
+ return () => !((globalThis?.document?.activeElement?.tagName?.toLowerCase() === 'input') && inputTypes.includes(globalThis?.document?.activeElement?.getAttribute('type')?.toLowerCase() ?? ''));
2664
+ })();
2665
+ /**
2666
+ * Returns the user's locale. Returns 'en-US' if it can't be determined.
2667
+ */
2668
+ export const getUserLocale = (() => {
2669
+ let userLocale = null;
2670
+ return () => {
2671
+ if (userLocale === null) {
2672
+ userLocale = (() => {
2673
+ // Type guard: check if navigator has languages
2674
+ let locales = [];
2675
+ if (globalThis?.navigator && hasNavigatorLanguages(globalThis.navigator)) {
2676
+ const navLanguages = safeGetProperty(globalThis.navigator, 'languages');
2677
+ if (Array.isArray(navLanguages)) {
2678
+ locales = navLanguages;
2679
+ }
2680
+ }
2681
+ if (!IS_ARRAY(locales) || (locales.length <= 0)) {
2682
+ return 'en-US';
2683
+ }
2684
+ locales = locales.filter((locale) => ((typeof locale === 'string') && locale.includes('-') && (locale.toLowerCase() !== 'en-us')));
2685
+ if (locales.length <= 0) {
2686
+ return 'en-US';
2687
+ }
2688
+ const localesNoEnglish = locales.filter((locale) => STRING(locale).toLowerCase().startsWith('en-'));
2689
+ if (localesNoEnglish.length <= 0) {
2690
+ return STRING(locales[0]);
2691
+ }
2692
+ return STRING(localesNoEnglish[0]);
2693
+ })();
2694
+ }
2695
+ return userLocale;
2696
+ };
2697
+ })();
2698
+ /**
2699
+ * Returns the user's locale date format. Always returns YYYY MM DD, with the character in between depending on the user's locale. Returns 'YYYY/MM/DD' if the user's locale can't be determined.
2700
+ */
2701
+ export const getUserLocaleDateFormat = (() => {
2702
+ let userLocaleDateFormat = null;
2703
+ return () => {
2704
+ if (userLocaleDateFormat === null) {
2705
+ userLocaleDateFormat = (() => {
2706
+ let char = '/';
2707
+ // Type guard: check if globalThis has Intl
2708
+ if (hasIntl(globalThis) && globalThis.Intl && 'DateTimeFormat' in globalThis.Intl) {
2709
+ const DateTimeFormat = safeGetProperty(globalThis.Intl, 'DateTimeFormat');
2710
+ if (typeof DateTimeFormat === 'function') {
2711
+ // Type guard: DateTimeFormat constructor accepts locale and returns object with format method
2712
+ // We've already verified it's a function, now construct it
2713
+ // Use Function.prototype.apply to avoid type assertion
2714
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
2715
+ const dtfInstance = Function.prototype.apply.call(DateTimeFormat, null, [getUserLocale()]);
2716
+ if ('format' in dtfInstance && typeof dtfInstance.format === 'function') {
2717
+ const formattedDate = dtfInstance.format();
2718
+ if (formattedDate.includes('-')) {
2719
+ char = '-';
2720
+ }
2721
+ else if (formattedDate.includes('. ')) {
2722
+ char = '.';
2723
+ }
2724
+ else if (formattedDate.includes('.')) {
2725
+ char = '.';
2726
+ }
2727
+ }
2728
+ }
2729
+ }
2730
+ return 'YYYY' + char + 'MM' + char + 'DD';
2731
+ })();
2732
+ }
2733
+ return userLocaleDateFormat;
2734
+ };
2735
+ })();
2736
+ /**
2737
+ * Returns an empty 1x1 transparent image data URL.
2738
+ */
2739
+ export function getEmptyImageSrc() {
2740
+ return 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
2741
+ }
2742
+ /**
2743
+ * Calculates and returns the percentage of the part and total ((part / total) * 100).
2744
+ */
2745
+ export function getPercentage(part, total) {
2746
+ const p = FLOAT_LAX(part);
2747
+ const t = FLOAT_LAX(total);
2748
+ if (t === 0) {
2749
+ return 100;
2750
+ }
2751
+ return Math.max(0, Math.min(100, ((p / t) * 100)));
2752
+ }
2753
+ /**
2754
+ * Returns the pixels of the given Image object.
2755
+ */
2756
+ export async function getImagePixels(image) {
2757
+ if (!globalThis?.document?.createElement || !globalThis?.document?.body?.appendChild) {
2758
+ console.warn('LeUtils.getImagePixels: Document is not available, returning empty pixels.');
2759
+ return new Uint8ClampedArray();
2760
+ }
2761
+ const canvas = globalThis.document.createElement('canvas');
2762
+ globalThis.document.body.appendChild(canvas);
2763
+ try {
2764
+ const ctx = canvas.getContext('2d');
2765
+ const width = Math.floor(image.width);
2766
+ const height = Math.floor(image.height);
2767
+ if (!ctx || (width <= 0) || (height <= 0)) {
2768
+ return new Uint8ClampedArray();
2769
+ }
2770
+ canvas.width = width;
2771
+ canvas.height = height;
2772
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
2773
+ return ctx.getImageData(0, 0, canvas.width, canvas.height).data;
2774
+ }
2775
+ finally {
2776
+ canvas?.parentNode?.removeChild(canvas);
2777
+ }
2778
+ }
2779
+ /**
2780
+ * Returns the data URL (mimetype "image/png") of a colored version of the given Image object.
2781
+ */
2782
+ export async function getColoredImage(image, color) {
2783
+ if (!globalThis?.document?.createElement || !globalThis?.document?.body?.appendChild) {
2784
+ console.warn('LeUtils.getColoredImage: Document is not available, returning empty image src.');
2785
+ return getEmptyImageSrc();
2786
+ }
2787
+ const canvas = globalThis.document.createElement('canvas');
2788
+ globalThis.document.body.appendChild(canvas);
2789
+ try {
2790
+ const ctx = canvas.getContext('2d');
2791
+ const width = Math.floor(image.width);
2792
+ const height = Math.floor(image.height);
2793
+ if (!ctx || (width <= 0) || (height <= 0)) {
2794
+ return getEmptyImageSrc();
2795
+ }
2796
+ canvas.width = width;
2797
+ canvas.height = height;
2798
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
2799
+ ctx.globalCompositeOperation = 'source-in';
2800
+ ctx.fillStyle = color;
2801
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
2802
+ return canvas.toDataURL('image/png');
2803
+ }
2804
+ finally {
2805
+ canvas?.parentNode?.removeChild(canvas);
2806
+ }
2807
+ }
2808
+ /**
2809
+ * Returns the hex color of the given RGB(A).
2810
+ */
2811
+ export function rgbToHex(rgb) {
2812
+ return '#' + rgb.map((x) => {
2813
+ const hex = x.toString(16);
2814
+ return ((hex.length === 1) ? '0' + hex : hex);
2815
+ }).join('');
2816
+ }
2817
+ /**
2818
+ * Returns the RGB(A) of the given hex color.
2819
+ */
2820
+ export function hexToRgb(hexstring) {
2821
+ const initialHexstring = hexstring;
2822
+ const h = STRING(hexstring).replace(/[^0-9A-F]/gi, '');
2823
+ const hasAlpha = ((h.length === 4) || (h.length === 8));
2824
+ let finalH = h;
2825
+ while (finalH.length < 6) {
2826
+ finalH = finalH.replace(/(.)/g, '$1$1');
2827
+ }
2828
+ const result = finalH.match(/\w{2}/g)?.map((a) => parseInt(a, 16));
2829
+ if (!result || (result.length < 3)) {
2830
+ throw new Error('Invalid hex color: "' + finalH + '" (was given "' + initialHexstring + '")');
2831
+ }
2832
+ return [
2833
+ result[0],
2834
+ result[1],
2835
+ result[2],
2836
+ ...(hasAlpha ? [result[3]] : []),
2837
+ ];
2838
+ }
2839
+ /**
2840
+ * Returns the HSL(A) of the given RGB(A).
2841
+ */
2842
+ export function rgbToHsl(rgb) {
2843
+ const r = rgb[0] / 255;
2844
+ const g = rgb[1] / 255;
2845
+ const b = rgb[2] / 255;
2846
+ const max = Math.max(r, g, b);
2847
+ const min = Math.min(r, g, b);
2848
+ let h = 0;
2849
+ let s = 0;
2850
+ const l = (max + min) / 2;
2851
+ if (max !== min) {
2852
+ const d = max - min;
2853
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
2854
+ switch (max) {
2855
+ case r:
2856
+ h = (g - b) / d + (g < b ? 6 : 0);
2857
+ break;
2858
+ case g:
2859
+ h = (b - r) / d + 2;
2860
+ break;
2861
+ case b:
2862
+ h = (r - g) / d + 4;
2863
+ break;
2864
+ }
2865
+ h /= 6;
2866
+ }
2867
+ return [h, s, l, ...((rgb.length >= 4) ? [rgb[3] / 255] : [])];
2868
+ }
2869
+ /**
2870
+ * Returns the RGB(A) of the given HSL(A).
2871
+ */
2872
+ export const hslToRgb = (() => {
2873
+ const hue2rgb = (p, q, t) => {
2874
+ if (t < 0)
2875
+ t += 1;
2876
+ if (t > 1)
2877
+ t -= 1;
2878
+ if (t < 1 / 6)
2879
+ return p + (q - p) * 6 * t;
2880
+ if (t < 1 / 2)
2881
+ return q;
2882
+ if (t < 2 / 3)
2883
+ return p + (q - p) * (2 / 3 - t) * 6;
2884
+ return p;
2885
+ };
2886
+ return (hsl) => {
2887
+ const h = hsl[0];
2888
+ const s = hsl[1];
2889
+ const l = hsl[2];
2890
+ let r = 1;
2891
+ let g = 1;
2892
+ let b = 1;
2893
+ if (s !== 0) {
2894
+ const q = (l < 0.5) ? (l * (1 + s)) : (l + s - (l * s));
2895
+ const p = (2 * l) - q;
2896
+ r = hue2rgb(p, q, h + (1 / 3));
2897
+ g = hue2rgb(p, q, h);
2898
+ b = hue2rgb(p, q, h - (1 / 3));
2899
+ }
2900
+ return [r * 255, g * 255, b * 255, ...((hsl.length >= 4) ? [hsl[3] * 255] : [])].map((c) => Math.max(0, Math.min(255, Math.round(c))));
2901
+ };
2902
+ })();
2903
+ /**
2904
+ * Returns the LAB(A) of the given RGB(A).
2905
+ */
2906
+ export function rgbToLab(rgb) {
2907
+ let r = rgb[0] / 255;
2908
+ let g = rgb[1] / 255;
2909
+ let b = rgb[2] / 255;
2910
+ r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : (r / 12.92);
2911
+ g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : (g / 12.92);
2912
+ b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : (b / 12.92);
2913
+ let x = ((r * 0.4124) + (g * 0.3576) + (b * 0.1805)) / 0.95047;
2914
+ let y = ((r * 0.2126) + (g * 0.7152) + (b * 0.0722));
2915
+ let z = ((r * 0.0193) + (g * 0.1192) + (b * 0.9505)) / 1.08883;
2916
+ x = (x > 0.008856) ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
2917
+ y = (y > 0.008856) ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
2918
+ z = (z > 0.008856) ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
2919
+ return [(116 * y) - 16, 500 * (x - y), 200 * (y - z), ...((rgb.length >= 4) ? [rgb[3] / 255] : [])];
2920
+ }
2921
+ /**
2922
+ * Returns the difference (calculated with DeltaE) of the LAB values of the given RGB values.
2923
+ */
2924
+ export function getDifferenceBetweenRgb(rgbA, rgbB) {
2925
+ const labA = rgbToLab(rgbA);
2926
+ const labB = rgbToLab(rgbB);
2927
+ return getDifferenceBetweenLab(labA, labB);
2928
+ }
2929
+ /**
2930
+ * Returns the difference (calculated with DeltaE) of the given LAB values.
2931
+ */
2932
+ export function getDifferenceBetweenLab(labA, labB) {
2933
+ const deltaL = labA[0] - labB[0];
2934
+ const deltaA = labA[1] - labB[1];
2935
+ const deltaB = labA[2] - labB[2];
2936
+ const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]);
2937
+ const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]);
2938
+ const deltaC = c1 - c2;
2939
+ let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC;
2940
+ deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
2941
+ const sc = 1.0 + 0.045 * c1;
2942
+ const sh = 1.0 + 0.015 * c1;
2943
+ const deltaLKlsl = deltaL / (1.0);
2944
+ const deltaCkcsc = deltaC / (sc);
2945
+ const deltaHkhsh = deltaH / (sh);
2946
+ const i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
2947
+ return (i < 0) ? 0 : Math.sqrt(i);
2948
+ }
2949
+ /**
2950
+ * Returns the RGB(A) between the two given RGB(A) values, based on the given percentage (0-100).
2951
+ */
2952
+ export function getRgbBetween(startRgb, endRgb, percentage) {
2953
+ const p = FLOAT_LAX(percentage);
2954
+ const partEnd = Math.max(0, Math.min(1, (p / 100.0)));
2955
+ const partStart = (1 - partEnd);
2956
+ const length = Math.min(startRgb.length, endRgb.length);
2957
+ const result = [];
2958
+ for (let i = 0; i < length; i++) {
2959
+ result.push(Math.max(0, Math.min(255, Math.round((startRgb[i] * partStart) + (endRgb[i] * partEnd)))));
2960
+ }
2961
+ return result;
2962
+ }
2963
+ /**
2964
+ * Returns the RGB(A) of the given RGB(A) values, based on the given percentage (0-100).
2965
+ */
2966
+ export function getRgbOfGradient(gradient, percentage) {
2967
+ const p = Math.max(0, Math.min(100, FLOAT_LAX(percentage)));
2968
+ let closest = null;
2969
+ each(gradient, (_color, percentStr) => {
2970
+ const percent = INT_LAX(percentStr);
2971
+ if (closest === null) {
2972
+ closest = [percent, Math.abs(p - percent)];
2973
+ }
2974
+ else {
2975
+ const difference = Math.abs(p - percent);
2976
+ if (difference < closest[1]) {
2977
+ closest = [percent, difference];
2978
+ }
2979
+ }
2980
+ });
2981
+ if (closest === null) {
2982
+ return [0, 0, 0];
2983
+ }
2984
+ const closestPercent = closest[0];
2985
+ const HIGHER = 99999;
2986
+ const LOWER = -99999;
2987
+ let higher = HIGHER;
2988
+ let lower = LOWER;
2989
+ each(gradient, (_color, percentStr) => {
2990
+ const percent = INT_LAX(percentStr);
2991
+ if (percent < closestPercent) {
2992
+ if (percent > lower) {
2993
+ lower = percent;
2994
+ }
2995
+ }
2996
+ if (percent > closestPercent) {
2997
+ if (percent < higher) {
2998
+ higher = percent;
2999
+ }
3000
+ }
3001
+ });
3002
+ if (((higher === HIGHER) && (lower === LOWER)) || (higher === lower)) {
3003
+ // Type guard: verify gradient entry is number array
3004
+ const gradientValue = gradient[closestPercent];
3005
+ if (isNumberArray(gradientValue)) {
3006
+ return gradientValue;
3007
+ }
3008
+ console.error('LeUtils.getRgbFromGradient: gradient entry is not a number array', gradientValue);
3009
+ return [0, 0, 0];
3010
+ }
3011
+ else if ((higher !== HIGHER) && (lower !== LOWER)) {
3012
+ const higherDifference = Math.abs(higher - p);
3013
+ const lowerDifference = Math.abs(p - lower);
3014
+ if (higherDifference > lowerDifference) {
3015
+ higher = closestPercent;
3016
+ }
3017
+ else {
3018
+ lower = closestPercent;
3019
+ }
3020
+ }
3021
+ else if (lower === LOWER) {
3022
+ lower = closestPercent;
3023
+ }
3024
+ else {
3025
+ higher = closestPercent;
3026
+ }
3027
+ if (lower > higher) {
3028
+ const tmp = higher;
3029
+ higher = lower;
3030
+ lower = tmp;
3031
+ }
3032
+ const total = (higher - lower);
3033
+ const part = (p - lower);
3034
+ // Type guard: verify gradient entries are number arrays
3035
+ const lowerGradient = gradient[lower];
3036
+ const higherGradient = gradient[higher];
3037
+ if (!isNumberArray(lowerGradient) || !isNumberArray(higherGradient)) {
3038
+ console.error('LeUtils.getRgbFromGradient: gradient entries are not number arrays', lowerGradient, higherGradient);
3039
+ return [0, 0, 0];
3040
+ }
3041
+ return getRgbBetween(lowerGradient, higherGradient, ((part / total) * 100));
3042
+ }
3043
+ /**
3044
+ * Download file from URL or data.
3045
+ */
3046
+ export function downloadFile(name, data) {
3047
+ if (!globalThis.document?.createElement || !globalThis.URL?.createObjectURL)
3048
+ return;
3049
+ const a = globalThis.document.createElement('a');
3050
+ a.style.display = 'none';
3051
+ const url = (typeof data === 'string') ? data : globalThis.URL.createObjectURL(data);
3052
+ a.href = url;
3053
+ a.download = name;
3054
+ globalThis.document.body.appendChild(a);
3055
+ a.click();
3056
+ setTimeout(() => {
3057
+ globalThis.document.body.removeChild(a);
3058
+ if (typeof data !== 'string')
3059
+ globalThis.URL.revokeObjectURL(url);
3060
+ }, 0);
3061
+ }
3062
+ /**
3063
+ * LocalStorage Get.
3064
+ */
3065
+ export function localStorageGet(key) {
3066
+ try {
3067
+ return globalThis.localStorage?.getItem(key) ?? null;
3068
+ }
3069
+ catch {
3070
+ return null;
3071
+ }
3072
+ }
3073
+ /**
3074
+ * LocalStorage Set.
3075
+ */
3076
+ export function localStorageSet(key, value) {
3077
+ try {
3078
+ globalThis.localStorage?.setItem(key, value);
3079
+ }
3080
+ catch {
3081
+ // Ignore error
3082
+ }
3083
+ }
3084
+ /**
3085
+ * LocalStorage Remove.
3086
+ */
3087
+ export function localStorageRemove(key) {
3088
+ try {
3089
+ globalThis.localStorage?.removeItem(key);
3090
+ }
3091
+ catch {
3092
+ // Ignore error
3093
+ }
3094
+ }
3095
+ /**
3096
+ * Checks if given host is private.
3097
+ */
3098
+ export function isGivenHostPrivate(host) {
3099
+ const h = STRING(host).trim().toLowerCase();
3100
+ return h === 'localhost' || h === '127.0.0.1' || h.startsWith('192.168.') || h.startsWith('10.') || /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(h);
3101
+ }
3102
+ /**
3103
+ * Checks if current host is private.
3104
+ */
3105
+ export function isCurrentHostPrivate() {
3106
+ return isGivenHostPrivate(globalThis?.location?.hostname);
3107
+ }
3108
+ /**
3109
+ * Create worker thread.
3110
+ */
3111
+ export function createWorkerThread(name) {
3112
+ if (!globalThis?.Worker) {
3113
+ console.warn('LeUtils.createWorkerThread: Workers are not supported, returning a dummy.');
3114
+ return {
3115
+ worker: null,
3116
+ sendMessage: (_data, _options) => Promise.reject('Workers are not supported in this environment'),
3117
+ };
3118
+ }
3119
+ const worker = new globalThis.Worker('/workers/' + name + '.worker.js');
3120
+ const listeners = new Map();
3121
+ const sendMessage = (data, options) => {
3122
+ return new Promise((resolve, reject) => {
3123
+ const id = uniqueId();
3124
+ listeners.set(id, resolve);
3125
+ globalThis.setTimeout(() => {
3126
+ listeners.delete(id);
3127
+ reject('timeout');
3128
+ }, options?.timeout ?? 10000);
3129
+ // Type guard: verify data is Record-like for spreading
3130
+ const messageData = isRecordLike(data) ? data : { data };
3131
+ worker.postMessage({ id, ...messageData });
3132
+ });
3133
+ };
3134
+ worker.onerror = (error) => {
3135
+ console.error('Worker ' + name + ':', error);
3136
+ };
3137
+ worker.onmessage = (message) => {
3138
+ const data = message.data;
3139
+ // Type guard: verify data has id property
3140
+ if (hasId(data)) {
3141
+ const callback = listeners.get(data.id);
3142
+ if (callback) {
3143
+ listeners.delete(data.id);
3144
+ callback(data);
3145
+ }
3146
+ }
3147
+ };
3148
+ return { worker, sendMessage };
3149
+ }
3150
+ /**
3151
+ * Send message to worker.
3152
+ */
3153
+ export const sendWorkerMessage = (() => {
3154
+ const workers = new Map();
3155
+ return (workerName, data, options) => {
3156
+ if (!workers.has(workerName)) {
3157
+ workers.set(workerName, createWorkerThread(workerName));
3158
+ }
3159
+ // Type guard: verify worker has sendMessage method
3160
+ const worker = workers.get(workerName);
3161
+ if (worker && hasSendMessage(worker)) {
3162
+ return worker.sendMessage(data, options);
3163
+ }
3164
+ console.error('LeUtils.createWorkerThread: worker does not have sendMessage method', worker);
3165
+ return Promise.reject(new Error('Worker does not have sendMessage method'));
3166
+ };
3167
+ })();
3168
+ //# sourceMappingURL=LeUtils.js.map