@zelgadis87/utils-core 5.5.0-beta.2 → 6.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.rollup/index.cjs +75 -35
- package/.rollup/index.cjs.map +1 -1
- package/.rollup/index.d.ts +75 -30
- package/.rollup/index.mjs +75 -34
- package/.rollup/index.mjs.map +1 -1
- package/.rollup/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +29 -0
- package/package.json +1 -1
- package/src/Optional.ts +32 -36
- package/src/async/CancelableDeferred.ts.off +1 -1
- package/src/lazy/Lazy.ts +7 -3
- package/src/sorting/ComparisonChain.ts.off +1 -1
- package/src/sorting/Sorter.ts +4 -4
- package/src/time/TimeInstant.ts +1 -1
- package/src/upgrade/DataUpgrader.ts +45 -2
- package/src/upgrade/DataUpgraderBuilder.ts +7 -0
- package/src/utils/arrays.ts +1 -1
- package/src/utils/operations.ts +2 -2
- package/src/utils/random.ts +1 -1
- package/src/utils/records/entries.ts +0 -3
package/.rollup/index.cjs
CHANGED
|
@@ -213,7 +213,7 @@ function throwIfNullOrUndefined(source, errorProducer = () => new Error(`Unexpec
|
|
|
213
213
|
return ifNullOrUndefined(source, () => { throw errorProducer(); });
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
class
|
|
216
|
+
class OptionalClz {
|
|
217
217
|
_present;
|
|
218
218
|
_value;
|
|
219
219
|
constructor(t) {
|
|
@@ -225,9 +225,9 @@ class Optional {
|
|
|
225
225
|
return this._value;
|
|
226
226
|
}
|
|
227
227
|
get() {
|
|
228
|
-
return this.getOrElseThrow(
|
|
228
|
+
return this.getOrElseThrow();
|
|
229
229
|
}
|
|
230
|
-
getOrElseThrow(errorProducer) {
|
|
230
|
+
getOrElseThrow(errorProducer = () => new ErrorGetEmptyOptional()) {
|
|
231
231
|
if (this.isEmpty())
|
|
232
232
|
throw errorProducer();
|
|
233
233
|
return this._value;
|
|
@@ -262,12 +262,6 @@ class Optional {
|
|
|
262
262
|
if (this.isPresent())
|
|
263
263
|
return callback(this.get());
|
|
264
264
|
}
|
|
265
|
-
ifPresentThenClear(callback) {
|
|
266
|
-
if (this.isPresent()) {
|
|
267
|
-
callback(this.get());
|
|
268
|
-
this.clear();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
265
|
apply(callbackIfPresent, callbackIfEmpty) {
|
|
272
266
|
if (this.isEmpty()) {
|
|
273
267
|
return callbackIfEmpty();
|
|
@@ -284,7 +278,6 @@ class Optional {
|
|
|
284
278
|
return newValue;
|
|
285
279
|
}
|
|
286
280
|
}
|
|
287
|
-
orElse = this.orElseReturn.bind(this);
|
|
288
281
|
orElseReturnNull() {
|
|
289
282
|
return this.isPresent() ? this.get() : null;
|
|
290
283
|
}
|
|
@@ -299,7 +292,6 @@ class Optional {
|
|
|
299
292
|
return newValueProducer();
|
|
300
293
|
}
|
|
301
294
|
}
|
|
302
|
-
orElseGet = this.orElseProduce.bind(this);
|
|
303
295
|
orElseReturnAndApply(newValue) {
|
|
304
296
|
if (this.isPresent()) {
|
|
305
297
|
return this.get();
|
|
@@ -340,18 +332,16 @@ class Optional {
|
|
|
340
332
|
}
|
|
341
333
|
orElseReturnNullable(newValue) {
|
|
342
334
|
if (this.isEmpty())
|
|
343
|
-
return
|
|
335
|
+
return OptionalClz.ofNullable(newValue);
|
|
344
336
|
return this;
|
|
345
337
|
}
|
|
346
|
-
orElseNullable = this.orElseReturnNullable.bind(this);
|
|
347
338
|
orElseProduceNullable(newValueProducer) {
|
|
348
339
|
if (this.isEmpty()) {
|
|
349
340
|
const newValue = newValueProducer();
|
|
350
|
-
return
|
|
341
|
+
return OptionalClz.ofNullable(newValue);
|
|
351
342
|
}
|
|
352
343
|
return this;
|
|
353
344
|
}
|
|
354
|
-
orElseGetNullable = this.orElseProduceNullable.bind(this);
|
|
355
345
|
orElseThrow(errorProducer) {
|
|
356
346
|
if (this.isEmpty())
|
|
357
347
|
throw errorProducer();
|
|
@@ -363,35 +353,35 @@ class Optional {
|
|
|
363
353
|
throw errorProducer(this.get());
|
|
364
354
|
}
|
|
365
355
|
mapTo(mapper) {
|
|
366
|
-
return this.apply(() =>
|
|
356
|
+
return this.apply(() => OptionalClz.ofNullable(mapper(this.get())), () => OptionalClz.empty());
|
|
367
357
|
}
|
|
368
358
|
flatMapTo(mapper) {
|
|
369
|
-
return this.apply(() => mapper(this.get()), () =>
|
|
359
|
+
return this.apply(() => mapper(this.get()), () => OptionalClz.empty());
|
|
370
360
|
}
|
|
371
361
|
filter(predicate) {
|
|
372
362
|
if (this.isEmpty())
|
|
373
363
|
return this;
|
|
374
364
|
if (predicate(this.get()))
|
|
375
365
|
return this;
|
|
376
|
-
return
|
|
366
|
+
return OptionalClz.empty();
|
|
377
367
|
}
|
|
378
368
|
static empty() {
|
|
379
|
-
return new
|
|
369
|
+
return new OptionalClz(undefined);
|
|
380
370
|
}
|
|
381
371
|
static of(t) {
|
|
382
372
|
if (isNullOrUndefined(t))
|
|
383
373
|
throw new ErrorCannotInstantiatePresentOptionalWithEmptyValue();
|
|
384
|
-
return new
|
|
374
|
+
return new OptionalClz(t);
|
|
385
375
|
}
|
|
386
376
|
static ofNullable(t) {
|
|
387
|
-
return new
|
|
377
|
+
return new OptionalClz(t);
|
|
388
378
|
}
|
|
389
379
|
static findInArray(arr, predicate) {
|
|
390
|
-
return
|
|
380
|
+
return OptionalClz.ofNullable(arr.find(predicate));
|
|
391
381
|
}
|
|
392
382
|
static findIndexInArray(arr, predicate) {
|
|
393
383
|
const idx = arr.findIndex(predicate);
|
|
394
|
-
return idx === -1 ?
|
|
384
|
+
return idx === -1 ? OptionalClz.empty() : OptionalClz.of(idx);
|
|
395
385
|
}
|
|
396
386
|
}
|
|
397
387
|
class ErrorGetEmptyOptional extends Error {
|
|
@@ -840,11 +830,11 @@ function shallowArrayEquals(a, b) {
|
|
|
840
830
|
}
|
|
841
831
|
/** @deprecated[2026.03.01]: Use {@link Optional.findInArray} instead. */
|
|
842
832
|
function findInArray(arr, predicate) {
|
|
843
|
-
return
|
|
833
|
+
return OptionalClz.findInArray(arr, predicate);
|
|
844
834
|
}
|
|
845
835
|
/** @deprecated[2026.03.01]: Use {@link Optional.findIndexInArray} instead. */
|
|
846
836
|
function findIndexInArray(arr, predicate) {
|
|
847
|
-
return
|
|
837
|
+
return OptionalClz.findIndexInArray(arr, predicate);
|
|
848
838
|
}
|
|
849
839
|
function zip(ts, rs) {
|
|
850
840
|
if (ts.length !== rs.length)
|
|
@@ -865,8 +855,8 @@ function unzip(arr) {
|
|
|
865
855
|
*/
|
|
866
856
|
function arrayGet(arr, index) {
|
|
867
857
|
if (index < 0 || index >= arr.length)
|
|
868
|
-
return
|
|
869
|
-
return
|
|
858
|
+
return OptionalClz.empty();
|
|
859
|
+
return OptionalClz.of(arr[index]);
|
|
870
860
|
}
|
|
871
861
|
|
|
872
862
|
function isTrue(x) {
|
|
@@ -1445,7 +1435,7 @@ const NEVER = new Promise(_resolve => { });
|
|
|
1445
1435
|
|
|
1446
1436
|
/**
|
|
1447
1437
|
* Returns a random integer in the range [min, max].
|
|
1448
|
-
* @deprecated Use randomIntegerInInterval or randomDecimalInInterval instead
|
|
1438
|
+
* @deprecated[2026.03.17]: Use randomIntegerInInterval or randomDecimalInInterval instead.
|
|
1449
1439
|
*/
|
|
1450
1440
|
function randomNumberInInterval(min, max) {
|
|
1451
1441
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
@@ -1500,8 +1490,6 @@ function entriesToDict(entries) {
|
|
|
1500
1490
|
function entriesToEntries(dict, mapper) {
|
|
1501
1491
|
return entriesToDict(dictToEntries(dict).map((entry) => mapper(entry)));
|
|
1502
1492
|
}
|
|
1503
|
-
/** @deprecated[2025.08.01]: Compatibility layer. */
|
|
1504
|
-
const mapEntries = entriesToEntries;
|
|
1505
1493
|
function entriesToList(dict, mapper) {
|
|
1506
1494
|
return dictToEntries(dict).map((entry) => mapper(entry));
|
|
1507
1495
|
}
|
|
@@ -1587,6 +1575,9 @@ class Lazy {
|
|
|
1587
1575
|
fnEmpty();
|
|
1588
1576
|
}
|
|
1589
1577
|
}
|
|
1578
|
+
mapOrElse(onPresent, onEmpty) {
|
|
1579
|
+
return this._initialized ? onPresent(this._value) : onEmpty();
|
|
1580
|
+
}
|
|
1590
1581
|
empty() {
|
|
1591
1582
|
this._initialized = false;
|
|
1592
1583
|
}
|
|
@@ -2504,7 +2495,7 @@ class TimeInstant extends TimeBase {
|
|
|
2504
2495
|
});
|
|
2505
2496
|
}
|
|
2506
2497
|
/**
|
|
2507
|
-
* @deprecated
|
|
2498
|
+
* @deprecated[2025.10.19]: Use fromIso8601 instead.
|
|
2508
2499
|
*/
|
|
2509
2500
|
static tryFromIso8601 = this.fromIso8601;
|
|
2510
2501
|
static now() {
|
|
@@ -3799,6 +3790,49 @@ const VERSION_FIELD = "$version";
|
|
|
3799
3790
|
* finding the shortest upgrade path and applying transformations. This is particularly useful for
|
|
3800
3791
|
* handling persisted data that may be in any historical format.
|
|
3801
3792
|
*
|
|
3793
|
+
* ## Architectural Conventions
|
|
3794
|
+
*
|
|
3795
|
+
* ### Type Aliasing
|
|
3796
|
+
* ```typescript
|
|
3797
|
+
* // Xstar = union of ALL known versions (upgrader input type)
|
|
3798
|
+
* type TMyData_Vstar = TMyData_V1 | TMyData_V2 | TMyData_V3;
|
|
3799
|
+
*
|
|
3800
|
+
* // No suffix = alias for the CURRENT latest version (upgrader output type)
|
|
3801
|
+
* type TMyData = TMyData_V3;
|
|
3802
|
+
* // When adding V4 later, update Vstar and change the alias:
|
|
3803
|
+
* // type TMyData_Vstar = TMyData_V1 | TMyData_V2 | TMyData_V3 | TMyData_V4;
|
|
3804
|
+
* // type TMyData = TMyData_V4;
|
|
3805
|
+
* ```
|
|
3806
|
+
*
|
|
3807
|
+
* ### Always Run the Upgrader
|
|
3808
|
+
*
|
|
3809
|
+
* **Every piece of serialized data must be treated as potentially any old version.**
|
|
3810
|
+
*
|
|
3811
|
+
* Never inspect `$version` manually and skip the upgrader. Always feed raw parsed data
|
|
3812
|
+
* directly into `upgrade()`. The upgrader is **idempotent** — calling it on data already
|
|
3813
|
+
* at the latest version is a no-op that returns immediately. Bypassing it creates fragile
|
|
3814
|
+
* fast paths that break when new versions are introduced.
|
|
3815
|
+
*
|
|
3816
|
+
* ```typescript
|
|
3817
|
+
* // ✅ CORRECT — always delegate to the upgrader
|
|
3818
|
+
* const raw = JSON.parse(json) as TMyData_Vstar;
|
|
3819
|
+
* const current = await upgrader.upgrade(raw);
|
|
3820
|
+
*
|
|
3821
|
+
* // ❌ WRONG — manual version check bypasses the upgrader
|
|
3822
|
+
* const raw = JSON.parse(json);
|
|
3823
|
+
* if (raw.$version === 3) {
|
|
3824
|
+
* // This works today, breaks silently when V4 is added
|
|
3825
|
+
* }
|
|
3826
|
+
* ```
|
|
3827
|
+
*
|
|
3828
|
+
* ### Adding a New Version
|
|
3829
|
+
*
|
|
3830
|
+
* 1. Define the new version type with `$version: N`
|
|
3831
|
+
* 2. Add it to the `Vstar` union
|
|
3832
|
+
* 3. Change the no-suffix alias to point to the new version
|
|
3833
|
+
* 4. Add a transition from the previous latest to the new version
|
|
3834
|
+
* 5. Existing code that calls `upgrade()` requires zero changes
|
|
3835
|
+
*
|
|
3802
3836
|
* **Key Use Case:** Reading serialized data of unknown version and migrating it transparently.
|
|
3803
3837
|
*
|
|
3804
3838
|
* @example
|
|
@@ -3808,9 +3842,9 @@ const VERSION_FIELD = "$version";
|
|
|
3808
3842
|
* type TSerializedData_V2 = { name: string; age: number; $version: 2 };
|
|
3809
3843
|
* type TSerializedData_V3 = { name: string; age: number; email: string; $version: 3 };
|
|
3810
3844
|
*
|
|
3811
|
-
* // Step 2: Create union
|
|
3845
|
+
* // Step 2: Create Vstar union (all versions) and current-version alias (latest only)
|
|
3812
3846
|
* type TSerializedData_Vstar = TSerializedData_V1 | TSerializedData_V2 | TSerializedData_V3;
|
|
3813
|
-
* type TSerializedData = TSerializedData_V3;
|
|
3847
|
+
* type TSerializedData = TSerializedData_V3; // Update this when adding V4
|
|
3814
3848
|
*
|
|
3815
3849
|
* // Step 3: Create upgrader with transition functions
|
|
3816
3850
|
* const upgrader = DataUpgrader.create<TSerializedData_Vstar, TSerializedData>(3)
|
|
@@ -3950,6 +3984,13 @@ function isUpgradable(obj) {
|
|
|
3950
3984
|
* - Duplicate transition definitions
|
|
3951
3985
|
* - Backward transitions (e.g., version 2 → 1)
|
|
3952
3986
|
*
|
|
3987
|
+
* ## Architectural Convention
|
|
3988
|
+
*
|
|
3989
|
+
* The built DataUpgrader is the **single point of versioning** for your data. Never bypass it
|
|
3990
|
+
* with manual `$version` checks — `upgrade()` is idempotent (no-op on latest version) and
|
|
3991
|
+
* accepts the full `Vstar` union, returning the current latest version. See {@link DataUpgrader}
|
|
3992
|
+
* for the full type aliasing conventions (`Vstar` union, no-suffix alias, adding new versions).
|
|
3993
|
+
*
|
|
3953
3994
|
* @example
|
|
3954
3995
|
* ```typescript
|
|
3955
3996
|
* // Step 1: Define all historical versions with $version discriminator
|
|
@@ -4074,7 +4115,7 @@ exports.NEVER = NEVER;
|
|
|
4074
4115
|
exports.NonExhaustiveSwitchError = NonExhaustiveSwitchError;
|
|
4075
4116
|
exports.Operation = Operation;
|
|
4076
4117
|
exports.OperationAggregateError = OperationAggregateError;
|
|
4077
|
-
exports.Optional =
|
|
4118
|
+
exports.Optional = OptionalClz;
|
|
4078
4119
|
exports.PredicateBuilder = PredicateBuilder;
|
|
4079
4120
|
exports.RandomTimeDuration = RandomTimeDuration;
|
|
4080
4121
|
exports.RateThrottler = RateThrottler;
|
|
@@ -4187,7 +4228,6 @@ exports.jsonCloneDeep = jsonCloneDeep;
|
|
|
4187
4228
|
exports.last = last$1;
|
|
4188
4229
|
exports.listToDict = listToDict;
|
|
4189
4230
|
exports.mapDefined = mapDefined;
|
|
4190
|
-
exports.mapEntries = mapEntries;
|
|
4191
4231
|
exports.mapFirstTruthy = mapFirstTruthy;
|
|
4192
4232
|
exports.mapTruthys = mapTruthys;
|
|
4193
4233
|
exports.max = max;
|