@nlozgachev/pipelined 0.65.0 → 0.66.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -52
- package/dist/{Core-BDXRLmMa.cjs → Core-6SkC-fh5.cjs} +286 -206
- package/dist/{Core-BHN87zoB.mjs → Core-D7_9dZpV.mjs} +281 -201
- package/dist/core.cjs +2 -2
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.mjs +2 -2
- package/dist/data.cjs +60 -19
- package/dist/data.d.cts +23 -7
- package/dist/data.d.ts +23 -7
- package/dist/data.mjs +60 -19
- package/dist/{index-DCK_VPog.d.cts → index-8A9NqJTq.d.cts} +189 -152
- package/dist/{index-B07Wr815.d.ts → index-CY4ZLbjM.d.ts} +189 -152
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
|
@@ -284,6 +284,189 @@ const Equality = {
|
|
|
284
284
|
}
|
|
285
285
|
};
|
|
286
286
|
//#endregion
|
|
287
|
+
//#region src/Core/EventBus.ts
|
|
288
|
+
const makeEventBus = (options) => ({
|
|
289
|
+
options,
|
|
290
|
+
_listeners: /* @__PURE__ */ new Set(),
|
|
291
|
+
_listenerArray: null,
|
|
292
|
+
_queue: [],
|
|
293
|
+
_isEmitting: false
|
|
294
|
+
});
|
|
295
|
+
const emitEventBus = (target, message) => {
|
|
296
|
+
const targets = Array.isArray(target) ? target : [target];
|
|
297
|
+
const msg = message;
|
|
298
|
+
for (const bus of targets) {
|
|
299
|
+
bus._queue.push(msg);
|
|
300
|
+
if (!bus._isEmitting) {
|
|
301
|
+
bus._isEmitting = true;
|
|
302
|
+
try {
|
|
303
|
+
while (bus._queue.length > 0) {
|
|
304
|
+
const nextMsg = bus._queue.shift();
|
|
305
|
+
if (bus._listenerArray === null) bus._listenerArray = Array.from(bus._listeners);
|
|
306
|
+
const listeners = bus._listenerArray;
|
|
307
|
+
for (const listener of listeners) try {
|
|
308
|
+
listener(nextMsg);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
if (bus.options?.onError) bus.options.onError(err);
|
|
311
|
+
else throw err;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
} finally {
|
|
315
|
+
bus._isEmitting = false;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
const forwardEventBus = (options) => {
|
|
321
|
+
const targets = Array.isArray(options.to) ? options.to : [options.to];
|
|
322
|
+
const filterSet = options.only ? new Set(options.only) : null;
|
|
323
|
+
const handler = (msg) => {
|
|
324
|
+
if (filterSet !== null && !filterSet.has(msg.kind)) return;
|
|
325
|
+
for (const target of targets) emitEventBus(target, msg);
|
|
326
|
+
};
|
|
327
|
+
options.from._listeners.add(handler);
|
|
328
|
+
options.from._listenerArray = null;
|
|
329
|
+
return () => {
|
|
330
|
+
options.from._listeners.delete(handler);
|
|
331
|
+
options.from._listenerArray = null;
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
const listenEventBus = (bus, events, options) => {
|
|
335
|
+
const eventList = Array.isArray(events) ? events : [events];
|
|
336
|
+
const isOrdered = options?.ordered ?? false;
|
|
337
|
+
const isStrict = options?.strict ?? false;
|
|
338
|
+
const isOnce = options?.once ?? false;
|
|
339
|
+
const resetKinds = options?.reset ? new Set(Array.isArray(options.reset) ? options.reset : [options.reset]) : null;
|
|
340
|
+
const optionalKinds = options?.optional ? new Set(Array.isArray(options.optional) ? options.optional : [options.optional]) : null;
|
|
341
|
+
const createMatcher = (onMatch) => {
|
|
342
|
+
let sequenceIndex = 0;
|
|
343
|
+
return (msg) => {
|
|
344
|
+
if (resetKinds !== null && resetKinds.has(msg.kind)) {
|
|
345
|
+
sequenceIndex = 0;
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!isOrdered) {
|
|
349
|
+
if (eventList.includes(msg.kind)) onMatch(msg);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
let expectedKind = eventList[sequenceIndex];
|
|
353
|
+
if (expectedKind !== msg.kind && optionalKinds !== null) {
|
|
354
|
+
let lookaheadIndex = sequenceIndex;
|
|
355
|
+
while (lookaheadIndex < eventList.length && optionalKinds.has(eventList[lookaheadIndex]) && eventList[lookaheadIndex] !== msg.kind) lookaheadIndex++;
|
|
356
|
+
if (lookaheadIndex < eventList.length && eventList[lookaheadIndex] === msg.kind) {
|
|
357
|
+
sequenceIndex = lookaheadIndex;
|
|
358
|
+
expectedKind = eventList[sequenceIndex];
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (msg.kind === expectedKind) {
|
|
362
|
+
sequenceIndex++;
|
|
363
|
+
if (sequenceIndex === eventList.length) {
|
|
364
|
+
sequenceIndex = 0;
|
|
365
|
+
onMatch(msg);
|
|
366
|
+
}
|
|
367
|
+
} else if (isStrict) sequenceIndex = msg.kind === eventList[0] ? 1 : 0;
|
|
368
|
+
else if (eventList.includes(msg.kind)) sequenceIndex = msg.kind === eventList[0] ? 1 : 0;
|
|
369
|
+
};
|
|
370
|
+
};
|
|
371
|
+
return {
|
|
372
|
+
reduce: (reducer, initialState) => {
|
|
373
|
+
let currentState = initialState;
|
|
374
|
+
const listenerFn = createMatcher((msg) => {
|
|
375
|
+
currentState = reducer(msg, currentState);
|
|
376
|
+
if (isOnce) {
|
|
377
|
+
bus._listeners.delete(listenerFn);
|
|
378
|
+
bus._listenerArray = null;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
const unsubscribe = () => {
|
|
382
|
+
bus._listeners.delete(listenerFn);
|
|
383
|
+
bus._listenerArray = null;
|
|
384
|
+
};
|
|
385
|
+
bus._listeners.add(listenerFn);
|
|
386
|
+
bus._listenerArray = null;
|
|
387
|
+
return {
|
|
388
|
+
unsubscribe,
|
|
389
|
+
getState: () => currentState
|
|
390
|
+
};
|
|
391
|
+
},
|
|
392
|
+
tap: (effect) => {
|
|
393
|
+
const listenerFn = createMatcher((msg) => {
|
|
394
|
+
effect(msg);
|
|
395
|
+
if (isOnce) {
|
|
396
|
+
bus._listeners.delete(listenerFn);
|
|
397
|
+
bus._listenerArray = null;
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
const unsubscribe = () => {
|
|
401
|
+
bus._listeners.delete(listenerFn);
|
|
402
|
+
bus._listenerArray = null;
|
|
403
|
+
};
|
|
404
|
+
bus._listeners.add(listenerFn);
|
|
405
|
+
bus._listenerArray = null;
|
|
406
|
+
return unsubscribe;
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
const EventBus = {
|
|
411
|
+
/**
|
|
412
|
+
* Constructs a new `EventBus` instance.
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```ts
|
|
416
|
+
* const bus = EventBus.make<AppMessages>({ name: "app" });
|
|
417
|
+
* ```
|
|
418
|
+
*/
|
|
419
|
+
make: makeEventBus,
|
|
420
|
+
/**
|
|
421
|
+
* Emits a message payload to one or more target event buses.
|
|
422
|
+
*
|
|
423
|
+
* Uses a synchronous breadth-first trampoline queue to handle re-entrant emissions deterministically.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```ts
|
|
427
|
+
* EventBus.emit(busA, {
|
|
428
|
+
* kind: "userLoggedIn",
|
|
429
|
+
* value: { userId: "user-1" },
|
|
430
|
+
* });
|
|
431
|
+
*
|
|
432
|
+
* EventBus.emit([busA, busB], {
|
|
433
|
+
* kind: "userLoggedIn",
|
|
434
|
+
* value: { userId: "user-1" },
|
|
435
|
+
* });
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
emit: emitEventBus,
|
|
439
|
+
/**
|
|
440
|
+
* Forwards messages from one event bus to another (or multiple).
|
|
441
|
+
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```ts
|
|
444
|
+
* const stop = EventBus.forward({
|
|
445
|
+
* from: authBus,
|
|
446
|
+
* to: analyticsBus,
|
|
447
|
+
* only: ["userLoggedIn"],
|
|
448
|
+
* });
|
|
449
|
+
* ```
|
|
450
|
+
*/
|
|
451
|
+
forward: forwardEventBus,
|
|
452
|
+
/**
|
|
453
|
+
* Initiates listener registration on an event bus for specific event kind(s) or sequence.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```ts
|
|
457
|
+
* const sub = EventBus.listen(
|
|
458
|
+
* appBus,
|
|
459
|
+
* ["userLoggedIn", "checkoutStarted"],
|
|
460
|
+
* { ordered: true }
|
|
461
|
+
* ).reduce(
|
|
462
|
+
* (msg, state) => ({ count: state.count + 1 }),
|
|
463
|
+
* { count: 0 }
|
|
464
|
+
* );
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
listen: listenEventBus
|
|
468
|
+
};
|
|
469
|
+
//#endregion
|
|
287
470
|
//#region src/Core/Lazy.ts
|
|
288
471
|
const fromFn = (f) => {
|
|
289
472
|
let done = false;
|
|
@@ -763,6 +946,24 @@ const Maybe = {
|
|
|
763
946
|
Result: (data) => Result.is.ok(data) ? makeSome$1(data.value) : makeNone$1()
|
|
764
947
|
},
|
|
765
948
|
/**
|
|
949
|
+
* Wraps a synchronous operation that may throw, returning a `Maybe<A>`.
|
|
950
|
+
* Returns `Some(value)` if successful, or `None` if an exception is thrown.
|
|
951
|
+
*
|
|
952
|
+
* @example
|
|
953
|
+
* ```ts
|
|
954
|
+
* const safeParse = (s: string) => Maybe.tryCatch(() => JSON.parse(s));
|
|
955
|
+
* safeParse('{"a": 1}'); // Some({ a: 1 })
|
|
956
|
+
* safeParse('invalid'); // None
|
|
957
|
+
* ```
|
|
958
|
+
*/
|
|
959
|
+
tryCatch: (f) => {
|
|
960
|
+
try {
|
|
961
|
+
return makeSome$1(f());
|
|
962
|
+
} catch {
|
|
963
|
+
return makeNone$1();
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
/**
|
|
766
967
|
* Transforms the value inside a Maybe if it exists.
|
|
767
968
|
*
|
|
768
969
|
* @example
|
|
@@ -849,16 +1050,32 @@ const Maybe = {
|
|
|
849
1050
|
return data;
|
|
850
1051
|
},
|
|
851
1052
|
/**
|
|
852
|
-
*
|
|
1053
|
+
* Executes a side effect when the Maybe is None, without changing the Maybe.
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```ts
|
|
1057
|
+
* pipe(
|
|
1058
|
+
* Maybe.make.none(),
|
|
1059
|
+
* Maybe.tapNone(() => console.log("Value missing")),
|
|
1060
|
+
* );
|
|
1061
|
+
* ```
|
|
1062
|
+
*/
|
|
1063
|
+
tapNone: (f) => (data) => {
|
|
1064
|
+
if (isNone(data)) f();
|
|
1065
|
+
return data;
|
|
1066
|
+
},
|
|
1067
|
+
/**
|
|
1068
|
+
* Filters a Maybe based on a predicate or type guard.
|
|
853
1069
|
* Returns None if the predicate returns false or if the Maybe is already None.
|
|
854
1070
|
*
|
|
855
1071
|
* @example
|
|
856
1072
|
* ```ts
|
|
857
1073
|
* pipe(Maybe.make.some(5), Maybe.filter(n => n > 3)); // Some(5)
|
|
858
1074
|
* pipe(Maybe.make.some(2), Maybe.filter(n => n > 3)); // None
|
|
1075
|
+
* pipe(Maybe.make.some("hi"), Maybe.filter((x): x is string => typeof x === "string")); // Some("hi")
|
|
859
1076
|
* ```
|
|
860
1077
|
*/
|
|
861
|
-
filter: (predicate) => (data) => isSome(data) ? predicate(data.value) ? data : makeNone$1() : data,
|
|
1078
|
+
filter: ((predicate) => (data) => isSome(data) ? predicate(data.value) ? data : makeNone$1() : data),
|
|
862
1079
|
/**
|
|
863
1080
|
* Recovers from a None by providing a fallback Maybe.
|
|
864
1081
|
* The fallback can produce a different type, widening the result to `Maybe<A | B>`.
|
|
@@ -3837,189 +4054,6 @@ const State = {
|
|
|
3837
4054
|
}
|
|
3838
4055
|
};
|
|
3839
4056
|
//#endregion
|
|
3840
|
-
//#region src/Core/Stream.ts
|
|
3841
|
-
const makeStream = (options) => ({
|
|
3842
|
-
options,
|
|
3843
|
-
_listeners: /* @__PURE__ */ new Set(),
|
|
3844
|
-
_listenerArray: null,
|
|
3845
|
-
_queue: [],
|
|
3846
|
-
_isEmitting: false
|
|
3847
|
-
});
|
|
3848
|
-
const emitStream = (target, message) => {
|
|
3849
|
-
const targets = Array.isArray(target) ? target : [target];
|
|
3850
|
-
const msg = message;
|
|
3851
|
-
for (const stream of targets) {
|
|
3852
|
-
stream._queue.push(msg);
|
|
3853
|
-
if (!stream._isEmitting) {
|
|
3854
|
-
stream._isEmitting = true;
|
|
3855
|
-
try {
|
|
3856
|
-
while (stream._queue.length > 0) {
|
|
3857
|
-
const nextMsg = stream._queue.shift();
|
|
3858
|
-
if (stream._listenerArray === null) stream._listenerArray = Array.from(stream._listeners);
|
|
3859
|
-
const listeners = stream._listenerArray;
|
|
3860
|
-
for (const listener of listeners) try {
|
|
3861
|
-
listener(nextMsg);
|
|
3862
|
-
} catch (err) {
|
|
3863
|
-
if (stream.options?.onError) stream.options.onError(err);
|
|
3864
|
-
else throw err;
|
|
3865
|
-
}
|
|
3866
|
-
}
|
|
3867
|
-
} finally {
|
|
3868
|
-
stream._isEmitting = false;
|
|
3869
|
-
}
|
|
3870
|
-
}
|
|
3871
|
-
}
|
|
3872
|
-
};
|
|
3873
|
-
const forwardStream = (options) => {
|
|
3874
|
-
const targets = Array.isArray(options.to) ? options.to : [options.to];
|
|
3875
|
-
const filterSet = options.only ? new Set(options.only) : null;
|
|
3876
|
-
const handler = (msg) => {
|
|
3877
|
-
if (filterSet !== null && !filterSet.has(msg.kind)) return;
|
|
3878
|
-
for (const target of targets) emitStream(target, msg);
|
|
3879
|
-
};
|
|
3880
|
-
options.from._listeners.add(handler);
|
|
3881
|
-
options.from._listenerArray = null;
|
|
3882
|
-
return () => {
|
|
3883
|
-
options.from._listeners.delete(handler);
|
|
3884
|
-
options.from._listenerArray = null;
|
|
3885
|
-
};
|
|
3886
|
-
};
|
|
3887
|
-
const listenStream = (stream, events, options) => {
|
|
3888
|
-
const eventList = Array.isArray(events) ? events : [events];
|
|
3889
|
-
const isOrdered = options?.ordered ?? false;
|
|
3890
|
-
const isStrict = options?.strict ?? false;
|
|
3891
|
-
const isOnce = options?.once ?? false;
|
|
3892
|
-
const resetKinds = options?.reset ? new Set(Array.isArray(options.reset) ? options.reset : [options.reset]) : null;
|
|
3893
|
-
const optionalKinds = options?.optional ? new Set(Array.isArray(options.optional) ? options.optional : [options.optional]) : null;
|
|
3894
|
-
const createMatcher = (onMatch) => {
|
|
3895
|
-
let sequenceIndex = 0;
|
|
3896
|
-
return (msg) => {
|
|
3897
|
-
if (resetKinds !== null && resetKinds.has(msg.kind)) {
|
|
3898
|
-
sequenceIndex = 0;
|
|
3899
|
-
return;
|
|
3900
|
-
}
|
|
3901
|
-
if (!isOrdered) {
|
|
3902
|
-
if (eventList.includes(msg.kind)) onMatch(msg);
|
|
3903
|
-
return;
|
|
3904
|
-
}
|
|
3905
|
-
let expectedKind = eventList[sequenceIndex];
|
|
3906
|
-
if (expectedKind !== msg.kind && optionalKinds !== null) {
|
|
3907
|
-
let lookaheadIndex = sequenceIndex;
|
|
3908
|
-
while (lookaheadIndex < eventList.length && optionalKinds.has(eventList[lookaheadIndex]) && eventList[lookaheadIndex] !== msg.kind) lookaheadIndex++;
|
|
3909
|
-
if (lookaheadIndex < eventList.length && eventList[lookaheadIndex] === msg.kind) {
|
|
3910
|
-
sequenceIndex = lookaheadIndex;
|
|
3911
|
-
expectedKind = eventList[sequenceIndex];
|
|
3912
|
-
}
|
|
3913
|
-
}
|
|
3914
|
-
if (msg.kind === expectedKind) {
|
|
3915
|
-
sequenceIndex++;
|
|
3916
|
-
if (sequenceIndex === eventList.length) {
|
|
3917
|
-
sequenceIndex = 0;
|
|
3918
|
-
onMatch(msg);
|
|
3919
|
-
}
|
|
3920
|
-
} else if (isStrict) sequenceIndex = msg.kind === eventList[0] ? 1 : 0;
|
|
3921
|
-
else if (eventList.includes(msg.kind)) sequenceIndex = msg.kind === eventList[0] ? 1 : 0;
|
|
3922
|
-
};
|
|
3923
|
-
};
|
|
3924
|
-
return {
|
|
3925
|
-
reduce: (reducer, initialState) => {
|
|
3926
|
-
let currentState = initialState;
|
|
3927
|
-
const listenerFn = createMatcher((msg) => {
|
|
3928
|
-
currentState = reducer(msg, currentState);
|
|
3929
|
-
if (isOnce) {
|
|
3930
|
-
stream._listeners.delete(listenerFn);
|
|
3931
|
-
stream._listenerArray = null;
|
|
3932
|
-
}
|
|
3933
|
-
});
|
|
3934
|
-
const unsubscribe = () => {
|
|
3935
|
-
stream._listeners.delete(listenerFn);
|
|
3936
|
-
stream._listenerArray = null;
|
|
3937
|
-
};
|
|
3938
|
-
stream._listeners.add(listenerFn);
|
|
3939
|
-
stream._listenerArray = null;
|
|
3940
|
-
return {
|
|
3941
|
-
unsubscribe,
|
|
3942
|
-
getState: () => currentState
|
|
3943
|
-
};
|
|
3944
|
-
},
|
|
3945
|
-
tap: (effect) => {
|
|
3946
|
-
const listenerFn = createMatcher((msg) => {
|
|
3947
|
-
effect(msg);
|
|
3948
|
-
if (isOnce) {
|
|
3949
|
-
stream._listeners.delete(listenerFn);
|
|
3950
|
-
stream._listenerArray = null;
|
|
3951
|
-
}
|
|
3952
|
-
});
|
|
3953
|
-
const unsubscribe = () => {
|
|
3954
|
-
stream._listeners.delete(listenerFn);
|
|
3955
|
-
stream._listenerArray = null;
|
|
3956
|
-
};
|
|
3957
|
-
stream._listeners.add(listenerFn);
|
|
3958
|
-
stream._listenerArray = null;
|
|
3959
|
-
return unsubscribe;
|
|
3960
|
-
}
|
|
3961
|
-
};
|
|
3962
|
-
};
|
|
3963
|
-
const Stream = {
|
|
3964
|
-
/**
|
|
3965
|
-
* Constructs a new `Stream` instance.
|
|
3966
|
-
*
|
|
3967
|
-
* @example
|
|
3968
|
-
* ```ts
|
|
3969
|
-
* const stream = Stream.make<AppMessages>({ name: "app" });
|
|
3970
|
-
* ```
|
|
3971
|
-
*/
|
|
3972
|
-
make: makeStream,
|
|
3973
|
-
/**
|
|
3974
|
-
* Emits a message payload to one or more target streams.
|
|
3975
|
-
*
|
|
3976
|
-
* Uses a synchronous breadth-first trampoline queue to handle re-entrant emissions deterministically.
|
|
3977
|
-
*
|
|
3978
|
-
* @example
|
|
3979
|
-
* ```ts
|
|
3980
|
-
* Stream.emit(streamA, {
|
|
3981
|
-
* kind: "userLoggedIn",
|
|
3982
|
-
* value: { userId: "user-1" },
|
|
3983
|
-
* });
|
|
3984
|
-
*
|
|
3985
|
-
* Stream.emit([streamA, streamB], {
|
|
3986
|
-
* kind: "userLoggedIn",
|
|
3987
|
-
* value: { userId: "user-1" },
|
|
3988
|
-
* });
|
|
3989
|
-
* ```
|
|
3990
|
-
*/
|
|
3991
|
-
emit: emitStream,
|
|
3992
|
-
/**
|
|
3993
|
-
* Forwards messages from one stream to another (or multiple).
|
|
3994
|
-
*
|
|
3995
|
-
* @example
|
|
3996
|
-
* ```ts
|
|
3997
|
-
* const stop = Stream.forward({
|
|
3998
|
-
* from: authStream,
|
|
3999
|
-
* to: analyticsStream,
|
|
4000
|
-
* only: ["userLoggedIn"],
|
|
4001
|
-
* });
|
|
4002
|
-
* ```
|
|
4003
|
-
*/
|
|
4004
|
-
forward: forwardStream,
|
|
4005
|
-
/**
|
|
4006
|
-
* Initiates listener registration on a stream for specific event kind(s) or sequence.
|
|
4007
|
-
*
|
|
4008
|
-
* @example
|
|
4009
|
-
* ```ts
|
|
4010
|
-
* const sub = Stream.listen(
|
|
4011
|
-
* appStream,
|
|
4012
|
-
* ["userLoggedIn", "checkoutStarted"],
|
|
4013
|
-
* { ordered: true }
|
|
4014
|
-
* ).reduce(
|
|
4015
|
-
* (msg, state) => ({ count: state.count + 1 }),
|
|
4016
|
-
* { count: 0 }
|
|
4017
|
-
* );
|
|
4018
|
-
* ```
|
|
4019
|
-
*/
|
|
4020
|
-
listen: listenStream
|
|
4021
|
-
};
|
|
4022
|
-
//#endregion
|
|
4023
4057
|
//#region src/Core/TaskMaybe.ts
|
|
4024
4058
|
const makeSome = (value) => Task.resolve(Maybe.make.some(value));
|
|
4025
4059
|
const makeNone = () => Task.resolve(Maybe.make.none());
|
|
@@ -4481,14 +4515,15 @@ Maybe: (data) => Task.map(Result.to.Maybe)(data) },
|
|
|
4481
4515
|
* Retries a fallible Task.Result according to a RetryPolicy.
|
|
4482
4516
|
* If the task succeeds, returns Ok immediately.
|
|
4483
4517
|
* If the task fails, retries up to policy.attempts times with delays generated by policy.
|
|
4518
|
+
* An optional `when` predicate selectively retries only specific errors (e.g. transient network errors).
|
|
4484
4519
|
*
|
|
4485
4520
|
* @example
|
|
4486
4521
|
* ```ts
|
|
4487
4522
|
* const policy = RetryPolicy.exponential({ attempts: 3, initial: Duration.milliseconds(100) });
|
|
4488
|
-
* const retryableFetch = pipe(fetchData, Task.Result.retry(policy));
|
|
4523
|
+
* const retryableFetch = pipe(fetchData, Task.Result.retry(policy, { when: (err) => isTransient(err) }));
|
|
4489
4524
|
* ```
|
|
4490
4525
|
*/
|
|
4491
|
-
retry: (policy) => (task) => (signal) => Deferred.from.Promise((() => {
|
|
4526
|
+
retry: (policy, options) => (task) => (signal) => Deferred.from.Promise((() => {
|
|
4492
4527
|
const { attempts } = policy;
|
|
4493
4528
|
const wait = (duration) => new Promise((res) => {
|
|
4494
4529
|
let timerId;
|
|
@@ -4496,10 +4531,7 @@ Maybe: (data) => Task.map(Result.to.Maybe)(data) },
|
|
|
4496
4531
|
clearTimeout(timerId);
|
|
4497
4532
|
res();
|
|
4498
4533
|
};
|
|
4499
|
-
if (signal) {
|
|
4500
|
-
if (signal.aborted) return res();
|
|
4501
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
4502
|
-
}
|
|
4534
|
+
if (signal) signal.addEventListener("abort", onAbort, { once: true });
|
|
4503
4535
|
timerId = setTimeout(() => {
|
|
4504
4536
|
signal?.removeEventListener("abort", onAbort);
|
|
4505
4537
|
res();
|
|
@@ -4507,6 +4539,7 @@ Maybe: (data) => Task.map(Result.to.Maybe)(data) },
|
|
|
4507
4539
|
});
|
|
4508
4540
|
const executeAttempt = (attempt) => Deferred.to.Promise(task(signal)).then((res) => {
|
|
4509
4541
|
if (Result.is.ok(res) || attempt >= attempts || signal?.aborted) return res;
|
|
4542
|
+
if (options?.when && !options.when(res.error)) return res;
|
|
4510
4543
|
const delay = policy.getDelay(attempt);
|
|
4511
4544
|
return wait(delay).then(() => executeAttempt(attempt + 1));
|
|
4512
4545
|
});
|
|
@@ -4564,7 +4597,35 @@ Maybe: (data) => Task.map(Result.to.Maybe)(data) },
|
|
|
4564
4597
|
* // [Ok(val1), Err(err2), Ok(val3)]
|
|
4565
4598
|
* ```
|
|
4566
4599
|
*/
|
|
4567
|
-
allSettled: (tasks) => (signal) => Deferred.from.Promise(Promise.all(tasks.map((task) => Deferred.to.Promise(task(signal)))))
|
|
4600
|
+
allSettled: (tasks) => (signal) => Deferred.from.Promise(Promise.all(tasks.map((task) => Deferred.to.Promise(task(signal))))),
|
|
4601
|
+
/**
|
|
4602
|
+
* Narrows the Ok value with a predicate. If the predicate returns false,
|
|
4603
|
+
* converts the value to an Err using onFail.
|
|
4604
|
+
*
|
|
4605
|
+
* @example
|
|
4606
|
+
* ```ts
|
|
4607
|
+
* pipe(
|
|
4608
|
+
* Task.Result.make.ok(42),
|
|
4609
|
+
* Task.Result.ensure(n => n > 50, () => "too small")
|
|
4610
|
+
* ); // Task.Result resolving to Err("too small")
|
|
4611
|
+
* ```
|
|
4612
|
+
*/
|
|
4613
|
+
ensure: (predicate, onFail) => (task) => (signal) => Deferred.from.Promise(Deferred.to.Promise(task(signal)).then(Result.ensure(predicate, onFail))),
|
|
4614
|
+
/**
|
|
4615
|
+
* Maps both the Err and Ok channels simultaneously.
|
|
4616
|
+
*
|
|
4617
|
+
* @example
|
|
4618
|
+
* ```ts
|
|
4619
|
+
* pipe(
|
|
4620
|
+
* Task.Result.make.ok(42),
|
|
4621
|
+
* Task.Result.bimap(
|
|
4622
|
+
* (err: string) => new Error(err),
|
|
4623
|
+
* (n: number) => n * 2
|
|
4624
|
+
* )
|
|
4625
|
+
* ); // Task.Result resolving to Ok(84)
|
|
4626
|
+
* ```
|
|
4627
|
+
*/
|
|
4628
|
+
bimap: (onErr, onOk) => (task) => (signal) => Deferred.from.Promise(Deferred.to.Promise(task(signal)).then(Result.bimap(onErr, onOk)))
|
|
4568
4629
|
};
|
|
4569
4630
|
//#endregion
|
|
4570
4631
|
//#region src/internal/InternalTypes.ts
|
|
@@ -5360,14 +5421,33 @@ sync: syncTask },
|
|
|
5360
5421
|
})),
|
|
5361
5422
|
/**
|
|
5362
5423
|
* Runs multiple Tasks in parallel and collects their results.
|
|
5424
|
+
* An optional `concurrency` option limits the number of tasks executing at any given time.
|
|
5363
5425
|
*
|
|
5364
5426
|
* @example
|
|
5365
5427
|
* ```ts
|
|
5366
5428
|
* Task.all([loadConfig, detectLocale, loadTheme])();
|
|
5367
5429
|
* // Deferred<[Config, string, Theme]>
|
|
5430
|
+
*
|
|
5431
|
+
* Task.all([loadConfig, detectLocale, loadTheme], { concurrency: 2 })();
|
|
5368
5432
|
* ```
|
|
5369
5433
|
*/
|
|
5370
|
-
all: (tasks) => fromPromise((signal) =>
|
|
5434
|
+
all: (tasks, options) => fromPromise((signal) => {
|
|
5435
|
+
const concurrency = options?.concurrency;
|
|
5436
|
+
if (concurrency === void 0 || concurrency <= 0 || tasks.length <= concurrency) return Promise.all(tasks.map((t) => toPromise(t, signal)));
|
|
5437
|
+
const len = tasks.length;
|
|
5438
|
+
const results = new Array(len);
|
|
5439
|
+
let nextIndex = 0;
|
|
5440
|
+
const worker = async () => {
|
|
5441
|
+
while (nextIndex < len) {
|
|
5442
|
+
const currentIndex = nextIndex++;
|
|
5443
|
+
results[currentIndex] = await toPromise(tasks[currentIndex], signal);
|
|
5444
|
+
}
|
|
5445
|
+
};
|
|
5446
|
+
const workerCount = Math.min(concurrency, len);
|
|
5447
|
+
const workers = [];
|
|
5448
|
+
for (let i = 0; i < workerCount; i++) workers.push(worker());
|
|
5449
|
+
return Promise.all(workers).then(() => results);
|
|
5450
|
+
}),
|
|
5371
5451
|
/**
|
|
5372
5452
|
* Delays the execution of a Task by the specified duration.
|
|
5373
5453
|
* Useful for debouncing or rate limiting.
|
|
@@ -5434,21 +5514,21 @@ sync: syncTask },
|
|
|
5434
5514
|
return run(times);
|
|
5435
5515
|
}),
|
|
5436
5516
|
/**
|
|
5437
|
-
*
|
|
5438
|
-
* An optional delay duration can be inserted between runs.
|
|
5439
|
-
* An optional `
|
|
5517
|
+
* Polls a Task repeatedly until the result satisfies a predicate, returning that result.
|
|
5518
|
+
* An optional delay duration can be inserted between polling runs.
|
|
5519
|
+
* An optional `attempts` cap stops the loop after N calls — the last value is returned
|
|
5440
5520
|
* regardless of whether the predicate was satisfied.
|
|
5441
5521
|
*
|
|
5442
5522
|
* @example
|
|
5443
5523
|
* ```ts
|
|
5444
5524
|
* pipe(
|
|
5445
5525
|
* checkStatus,
|
|
5446
|
-
* Task.
|
|
5526
|
+
* Task.poll({ until: (s) => s === "ready", delay: Duration.milliseconds(500) })
|
|
5447
5527
|
* )(); // polls every 500ms until status is "ready"
|
|
5448
5528
|
* ```
|
|
5449
5529
|
*/
|
|
5450
|
-
|
|
5451
|
-
const {
|
|
5530
|
+
poll: (options) => (task) => fromPromise((signal) => {
|
|
5531
|
+
const { until: predicate, delay: delayDuration, attempts } = options;
|
|
5452
5532
|
const wait = () => new Promise((r) => {
|
|
5453
5533
|
let timerId;
|
|
5454
5534
|
const onAbort = () => {
|
|
@@ -5465,7 +5545,7 @@ sync: syncTask },
|
|
|
5465
5545
|
if (signal?.aborted && lastValue !== void 0) return Promise.resolve(lastValue);
|
|
5466
5546
|
return toPromise(task, signal).then((a) => {
|
|
5467
5547
|
if (predicate(a)) return a;
|
|
5468
|
-
if (
|
|
5548
|
+
if (attempts !== void 0 && attempt >= attempts) return a;
|
|
5469
5549
|
if (signal?.aborted) return a;
|
|
5470
5550
|
return wait().then(() => run(attempt + 1, a));
|
|
5471
5551
|
});
|
|
@@ -6025,6 +6105,12 @@ Object.defineProperty(exports, "Equality", {
|
|
|
6025
6105
|
return Equality;
|
|
6026
6106
|
}
|
|
6027
6107
|
});
|
|
6108
|
+
Object.defineProperty(exports, "EventBus", {
|
|
6109
|
+
enumerable: true,
|
|
6110
|
+
get: function() {
|
|
6111
|
+
return EventBus;
|
|
6112
|
+
}
|
|
6113
|
+
});
|
|
6028
6114
|
Object.defineProperty(exports, "Lazy", {
|
|
6029
6115
|
enumerable: true,
|
|
6030
6116
|
get: function() {
|
|
@@ -6115,12 +6201,6 @@ Object.defineProperty(exports, "State", {
|
|
|
6115
6201
|
return State;
|
|
6116
6202
|
}
|
|
6117
6203
|
});
|
|
6118
|
-
Object.defineProperty(exports, "Stream", {
|
|
6119
|
-
enumerable: true,
|
|
6120
|
-
get: function() {
|
|
6121
|
-
return Stream;
|
|
6122
|
-
}
|
|
6123
|
-
});
|
|
6124
6204
|
Object.defineProperty(exports, "Task", {
|
|
6125
6205
|
enumerable: true,
|
|
6126
6206
|
get: function() {
|