@typed/fx 2.0.0-beta.0 → 2.0.0-beta.2
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 +24 -1
- package/dist/Fx/combinators/additive.d.ts +94 -0
- package/dist/Fx/combinators/additive.d.ts.map +1 -0
- package/dist/Fx/combinators/additive.js +92 -0
- package/dist/Fx/combinators/catch.d.ts +61 -0
- package/dist/Fx/combinators/catch.d.ts.map +1 -1
- package/dist/Fx/combinators/catch.js +54 -0
- package/dist/Fx/combinators/changesWithEffect.d.ts +20 -0
- package/dist/Fx/combinators/changesWithEffect.d.ts.map +1 -0
- package/dist/Fx/combinators/changesWithEffect.js +28 -0
- package/dist/Fx/combinators/dropUntil.d.ts +29 -0
- package/dist/Fx/combinators/dropUntil.d.ts.map +1 -0
- package/dist/Fx/combinators/dropUntil.js +23 -0
- package/dist/Fx/combinators/flatMapConcurrently.d.ts.map +1 -1
- package/dist/Fx/combinators/flatMapConcurrently.js +3 -2
- package/dist/Fx/combinators/index.d.ts +10 -0
- package/dist/Fx/combinators/index.d.ts.map +1 -1
- package/dist/Fx/combinators/index.js +10 -0
- package/dist/Fx/combinators/keyed.d.ts +1 -1
- package/dist/Fx/combinators/keyed.d.ts.map +1 -1
- package/dist/Fx/combinators/mapBoth.d.ts +21 -0
- package/dist/Fx/combinators/mapBoth.d.ts.map +1 -0
- package/dist/Fx/combinators/mapBoth.js +14 -0
- package/dist/Fx/combinators/mapError.d.ts +17 -0
- package/dist/Fx/combinators/mapError.d.ts.map +1 -0
- package/dist/Fx/combinators/mapError.js +16 -0
- package/dist/Fx/combinators/provide.d.ts +34 -1
- package/dist/Fx/combinators/provide.d.ts.map +1 -1
- package/dist/Fx/combinators/provide.js +27 -0
- package/dist/Fx/combinators/result.d.ts +23 -0
- package/dist/Fx/combinators/result.d.ts.map +1 -0
- package/dist/Fx/combinators/result.js +32 -0
- package/dist/Fx/combinators/scan.d.ts +33 -0
- package/dist/Fx/combinators/scan.d.ts.map +1 -0
- package/dist/Fx/combinators/scan.js +38 -0
- package/dist/Fx/combinators/skip.d.ts +13 -0
- package/dist/Fx/combinators/skip.d.ts.map +1 -1
- package/dist/Fx/combinators/skip.js +11 -0
- package/dist/Fx/combinators/skipWhile.d.ts +49 -0
- package/dist/Fx/combinators/skipWhile.d.ts.map +1 -0
- package/dist/Fx/combinators/skipWhile.js +66 -0
- package/dist/Fx/combinators/slice.d.ts +13 -0
- package/dist/Fx/combinators/slice.d.ts.map +1 -1
- package/dist/Fx/combinators/slice.js +11 -0
- package/dist/Fx/combinators/take.d.ts +13 -0
- package/dist/Fx/combinators/take.d.ts.map +1 -1
- package/dist/Fx/combinators/take.js +11 -0
- package/dist/Fx/combinators/takeUntil.d.ts +14 -0
- package/dist/Fx/combinators/takeUntil.d.ts.map +1 -1
- package/dist/Fx/combinators/takeUntil.js +14 -0
- package/dist/Fx/combinators/takeWhile.d.ts +29 -0
- package/dist/Fx/combinators/takeWhile.d.ts.map +1 -0
- package/dist/Fx/combinators/takeWhile.js +23 -0
- package/dist/Fx/combinators/zip.d.ts +75 -0
- package/dist/Fx/combinators/zip.d.ts.map +1 -0
- package/dist/Fx/combinators/zip.js +100 -0
- package/dist/Fx/constructors/at.d.ts +2 -2
- package/dist/Fx/constructors/at.d.ts.map +1 -1
- package/dist/Fx/constructors/periodic.d.ts +1 -1
- package/dist/Fx/constructors/periodic.d.ts.map +1 -1
- package/dist/Push/Push.d.ts +64 -1
- package/dist/Push/Push.d.ts.map +1 -1
- package/dist/Push/Push.js +57 -0
- package/dist/RefSubject/RefArray.d.ts.map +1 -1
- package/dist/RefSubject/RefArray.js +2 -1
- package/dist/RefSubject/RefChunk.d.ts.map +1 -1
- package/dist/RefSubject/RefChunk.js +2 -1
- package/dist/RefSubject/RefDateTime.d.ts +4 -4
- package/dist/RefSubject/RefDateTime.d.ts.map +1 -1
- package/dist/RefSubject/RefHashMap.d.ts.map +1 -1
- package/dist/RefSubject/RefHashMap.js +5 -1
- package/dist/RefSubject/RefIterable.d.ts +1 -1
- package/dist/RefSubject/RefIterable.d.ts.map +1 -1
- package/dist/RefSubject/RefIterable.js +6 -1
- package/dist/RefSubject/RefRecord.d.ts.map +1 -1
- package/dist/RefSubject/RefRecord.js +3 -2
- package/dist/RefSubject/RefSubject.d.ts +48 -1
- package/dist/RefSubject/RefSubject.d.ts.map +1 -1
- package/dist/RefSubject/RefSubject.js +80 -1
- package/dist/RefSubject/RefTrie.d.ts +7 -7
- package/dist/RefSubject/RefTrie.d.ts.map +1 -1
- package/dist/RefSubject/RefTrie.js +8 -3
- package/dist/Sink/combinators.d.ts +57 -0
- package/dist/Sink/combinators.d.ts.map +1 -1
- package/dist/Sink/combinators.js +104 -1
- package/dist/Versioned/Versioned.d.ts +30 -0
- package/dist/Versioned/Versioned.d.ts.map +1 -1
- package/dist/Versioned/Versioned.js +18 -0
- package/package.json +10 -6
- package/src/Fx/combinators/additive.ts +142 -0
- package/src/Fx/combinators/catch.ts +256 -0
- package/src/Fx/combinators/changesWithEffect.ts +66 -0
- package/src/Fx/combinators/dropUntil.ts +47 -0
- package/src/Fx/combinators/flatMapConcurrently.ts +5 -2
- package/src/Fx/combinators/index.ts +10 -0
- package/src/Fx/combinators/keyed.ts +2 -2
- package/src/Fx/combinators/mapBoth.ts +40 -0
- package/src/Fx/combinators/mapError.ts +28 -0
- package/src/Fx/combinators/provide.ts +63 -1
- package/src/Fx/combinators/result.ts +39 -0
- package/src/Fx/combinators/scan.ts +82 -0
- package/src/Fx/combinators/skip.ts +21 -0
- package/src/Fx/combinators/skipWhile.ts +100 -0
- package/src/Fx/combinators/slice.ts +23 -0
- package/src/Fx/combinators/take.ts +21 -0
- package/src/Fx/combinators/takeUntil.ts +38 -0
- package/src/Fx/combinators/takeWhile.ts +47 -0
- package/src/Fx/combinators/zip.ts +175 -0
- package/src/Fx/constructors/at.ts +3 -3
- package/src/Fx/constructors/periodic.ts +1 -1
- package/src/Fx.additive-combinators.test.ts +126 -0
- package/src/Fx.catch-additive.test.ts +206 -0
- package/src/Fx.catch.test.ts +1 -2
- package/src/Fx.dropUntil.test.ts +61 -0
- package/src/Fx.lifecycle.test.ts +1 -2
- package/src/Fx.mapError-mapBoth.test.ts +101 -0
- package/src/Fx.provide-combinators.test.ts +94 -0
- package/src/Fx.result-changesWithEffect.test.ts +112 -0
- package/src/Fx.scan.test.ts +73 -0
- package/src/Fx.takeWhile-skipWhile.test.ts +84 -0
- package/src/Fx.zip-merge-additive.test.ts +171 -0
- package/src/Fx.zip.test.ts +133 -0
- package/src/Push/Push.ts +170 -1
- package/src/Push.additive.test.ts +256 -0
- package/src/RefSubject/RefArray.ts +4 -1
- package/src/RefSubject/RefChunk.ts +2 -1
- package/src/RefSubject/RefDateTime.ts +6 -6
- package/src/RefSubject/RefHashMap.ts +10 -1
- package/src/RefSubject/RefIterable.ts +11 -2
- package/src/RefSubject/RefRecord.ts +9 -2
- package/src/RefSubject/RefSubject.ts +108 -9
- package/src/RefSubject/RefTrie.ts +19 -10
- package/src/RefSubject.additive-parity.test.ts +101 -0
- package/src/Sink/combinators.ts +123 -1
- package/src/Sink.combinators.test.ts +88 -0
- package/src/Sink.reduce-collect-head-last.test.ts +107 -0
- package/src/Versioned/Versioned.ts +76 -0
- package/src/Versioned.filterMap.test.ts +91 -0
- package/tsconfig.json +0 -6
package/src/Push/Push.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type * as Cause from "effect/Cause";
|
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
9
|
import { dual, identity } from "effect/Function";
|
|
10
10
|
import * as Layer from "effect/Layer";
|
|
11
|
-
import
|
|
11
|
+
import * as Option from "effect/Option";
|
|
12
12
|
import { pipeArguments } from "effect/Pipeable";
|
|
13
13
|
import type * as Scope from "effect/Scope";
|
|
14
14
|
import * as ServiceMap from "effect/ServiceMap";
|
|
@@ -432,6 +432,68 @@ export const mapEffect: {
|
|
|
432
432
|
return make(push, Fx.mapEffect(push, f));
|
|
433
433
|
});
|
|
434
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Transforms the output (Fx) error channel of a `Push` using the provided function.
|
|
437
|
+
*
|
|
438
|
+
* Failures (Cause) are mapped via `Cause.map`, so only the typed failure (`Fail`)
|
|
439
|
+
* is transformed; defects and interrupts are preserved unchanged.
|
|
440
|
+
*
|
|
441
|
+
* Mirrors `Effect.mapError` on the Fx side.
|
|
442
|
+
*
|
|
443
|
+
* @since 1.0.0
|
|
444
|
+
* @category combinators
|
|
445
|
+
*/
|
|
446
|
+
export const mapError: {
|
|
447
|
+
<E2, E3>(
|
|
448
|
+
f: (e: E2) => E3,
|
|
449
|
+
): <A, E, R, B, R2>(push: Push<A, E, R, B, E2, R2>) => Push<A, E, R, B, E3, R2>;
|
|
450
|
+
<A, E, R, B, E2, R2, E3>(
|
|
451
|
+
push: Push<A, E, R, B, E2, R2>,
|
|
452
|
+
f: (e: E2) => E3,
|
|
453
|
+
): Push<A, E, R, B, E3, R2>;
|
|
454
|
+
} = dual(2, function mapError<
|
|
455
|
+
A,
|
|
456
|
+
E,
|
|
457
|
+
R,
|
|
458
|
+
B,
|
|
459
|
+
E2,
|
|
460
|
+
R2,
|
|
461
|
+
E3,
|
|
462
|
+
>(push: Push<A, E, R, B, E2, R2>, f: (e: E2) => E3): Push<A, E, R, B, E3, R2> {
|
|
463
|
+
return make(push, Fx.mapError(push, f));
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Transforms both the output (Fx) success and error channels of a `Push` using the provided options.
|
|
468
|
+
*
|
|
469
|
+
* Mirrors `Effect.mapBoth` on the Fx side: `onSuccess` maps emitted values,
|
|
470
|
+
* `onFailure` maps the typed failure (via `Cause.map`); defects and interrupts are preserved.
|
|
471
|
+
*
|
|
472
|
+
* @since 1.0.0
|
|
473
|
+
* @category combinators
|
|
474
|
+
*/
|
|
475
|
+
export const mapBoth: {
|
|
476
|
+
<B, C, E2, E3>(options: {
|
|
477
|
+
readonly onFailure: (e: E2) => E3;
|
|
478
|
+
readonly onSuccess: (b: B) => C;
|
|
479
|
+
}): <A, E, R, R2>(push: Push<A, E, R, B, E2, R2>) => Push<A, E, R, C, E3, R2>;
|
|
480
|
+
<A, E, R, B, E2, R2, C, E3>(
|
|
481
|
+
push: Push<A, E, R, B, E2, R2>,
|
|
482
|
+
options: { readonly onFailure: (e: E2) => E3; readonly onSuccess: (b: B) => C },
|
|
483
|
+
): Push<A, E, R, C, E3, R2>;
|
|
484
|
+
} = dual(
|
|
485
|
+
2,
|
|
486
|
+
function mapBoth<A, E, R, B, E2, R2, C, E3>(
|
|
487
|
+
push: Push<A, E, R, B, E2, R2>,
|
|
488
|
+
options: {
|
|
489
|
+
readonly onFailure: (e: E2) => E3;
|
|
490
|
+
readonly onSuccess: (b: B) => C;
|
|
491
|
+
},
|
|
492
|
+
): Push<A, E, R, C, E3, R2> {
|
|
493
|
+
return make(push, Fx.mapBoth(push, options));
|
|
494
|
+
},
|
|
495
|
+
);
|
|
496
|
+
|
|
435
497
|
export const filter: {
|
|
436
498
|
<B>(
|
|
437
499
|
f: (b: B) => boolean,
|
|
@@ -844,6 +906,113 @@ export const exhaustLatestMapEffect: {
|
|
|
844
906
|
return make(push, Fx.exhaustLatestMapEffect(push, f));
|
|
845
907
|
});
|
|
846
908
|
|
|
909
|
+
/**
|
|
910
|
+
* Maps over the output (Fx) side of a `Push` with an accumulator: for each emitted value `b`,
|
|
911
|
+
* applies `f(state, b)` to get `[nextState, emitted]` and emits the second element.
|
|
912
|
+
* The first element is the initial state; subsequent states are updated by each step.
|
|
913
|
+
*
|
|
914
|
+
* @param initial - Initial accumulator state.
|
|
915
|
+
* @param f - Reducer `(state, value) => [nextState, emitted]`.
|
|
916
|
+
* @returns A `Push` whose Fx side emits the accumulated/mapped values.
|
|
917
|
+
* @since 1.0.0
|
|
918
|
+
* @category combinators
|
|
919
|
+
*/
|
|
920
|
+
export const mapAccum: {
|
|
921
|
+
<S, B, C>(
|
|
922
|
+
initial: S,
|
|
923
|
+
f: (s: S, b: B) => readonly [S, C],
|
|
924
|
+
): <A, E, R, E2, R2>(push: Push<A, E, R, B, E2, R2>) => Push<A, E, R, C, E2, R2>;
|
|
925
|
+
<A, E, R, B, E2, R2, S, C>(
|
|
926
|
+
push: Push<A, E, R, B, E2, R2>,
|
|
927
|
+
initial: S,
|
|
928
|
+
f: (s: S, b: B) => readonly [S, C],
|
|
929
|
+
): Push<A, E, R, C, E2, R2>;
|
|
930
|
+
} = dual(3, function mapAccum<
|
|
931
|
+
A,
|
|
932
|
+
E,
|
|
933
|
+
R,
|
|
934
|
+
B,
|
|
935
|
+
E2,
|
|
936
|
+
R2,
|
|
937
|
+
S,
|
|
938
|
+
C,
|
|
939
|
+
>(push: Push<A, E, R, B, E2, R2>, initial: S, f: (s: S, b: B) => readonly [S, C]): Push<
|
|
940
|
+
A,
|
|
941
|
+
E,
|
|
942
|
+
R,
|
|
943
|
+
C,
|
|
944
|
+
E2,
|
|
945
|
+
R2
|
|
946
|
+
> {
|
|
947
|
+
return make(
|
|
948
|
+
push,
|
|
949
|
+
Fx.make((sink) =>
|
|
950
|
+
push.run(
|
|
951
|
+
Sink.loop(sink, initial, (s, b) => {
|
|
952
|
+
const [sNext, c] = f(s, b);
|
|
953
|
+
return [c, sNext];
|
|
954
|
+
}),
|
|
955
|
+
),
|
|
956
|
+
),
|
|
957
|
+
);
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Maps over the output (Fx) side of a `Push` with an effectful accumulator: for each emitted value `b`,
|
|
962
|
+
* runs `f(state, b)` to get `[nextState, emitted]` and emits the second element.
|
|
963
|
+
*
|
|
964
|
+
* @param initial - Initial accumulator state.
|
|
965
|
+
* @param f - Effectful reducer `(state, value) => Effect<[nextState, emitted]>`.
|
|
966
|
+
* @returns A `Push` whose Fx side emits the accumulated/mapped values.
|
|
967
|
+
* @since 1.0.0
|
|
968
|
+
* @category combinators
|
|
969
|
+
*/
|
|
970
|
+
export const mapAccumEffect: {
|
|
971
|
+
<S, B, C, E3, R3>(
|
|
972
|
+
initial: S,
|
|
973
|
+
f: (s: S, b: B) => Effect.Effect<readonly [S, C], E3, R3>,
|
|
974
|
+
): <A, E, R, E2, R2>(push: Push<A, E, R, B, E2, R2>) => Push<A, E, R, C, E2 | E3, R2 | R3>;
|
|
975
|
+
<A, E, R, B, E2, R2, S, C, E3, R3>(
|
|
976
|
+
push: Push<A, E, R, B, E2, R2>,
|
|
977
|
+
initial: S,
|
|
978
|
+
f: (s: S, b: B) => Effect.Effect<readonly [S, C], E3, R3>,
|
|
979
|
+
): Push<A, E, R, C, E2 | E3, R2 | R3>;
|
|
980
|
+
} = dual(3, function mapAccumEffect<
|
|
981
|
+
A,
|
|
982
|
+
E,
|
|
983
|
+
R,
|
|
984
|
+
B,
|
|
985
|
+
E2,
|
|
986
|
+
R2,
|
|
987
|
+
S,
|
|
988
|
+
C,
|
|
989
|
+
E3,
|
|
990
|
+
R3,
|
|
991
|
+
>(push: Push<A, E, R, B, E2, R2>, initial: S, f: (s: S, b: B) => Effect.Effect<readonly [S, C], E3, R3>): Push<
|
|
992
|
+
A,
|
|
993
|
+
E,
|
|
994
|
+
R,
|
|
995
|
+
C,
|
|
996
|
+
E2 | E3,
|
|
997
|
+
R2 | R3
|
|
998
|
+
> {
|
|
999
|
+
return make(
|
|
1000
|
+
push,
|
|
1001
|
+
Fx.make(<RSink>(sink: Sink.Sink<C, E2 | E3, RSink>) =>
|
|
1002
|
+
push.run(
|
|
1003
|
+
Sink.filterMapLoopEffect(sink, initial, (s, b) =>
|
|
1004
|
+
f(s, b).pipe(
|
|
1005
|
+
Effect.map(([sNext, c]) => [Option.some(c), sNext] as const),
|
|
1006
|
+
Effect.catchCause((cause) =>
|
|
1007
|
+
sink.onFailure(cause).pipe(Effect.as([Option.none(), s] as const)),
|
|
1008
|
+
),
|
|
1009
|
+
),
|
|
1010
|
+
),
|
|
1011
|
+
),
|
|
1012
|
+
),
|
|
1013
|
+
);
|
|
1014
|
+
});
|
|
1015
|
+
|
|
847
1016
|
export function Service<Self, A, E = never, B = never, E2 = never>() {
|
|
848
1017
|
return <const Id extends string>(id: Id): Push.Class<Self, Id, A, E, B, E2> => {
|
|
849
1018
|
const service = ServiceMap.Service<Self, Push<A, E, never, B, E2, never>>(id);
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { assert, describe, it } from "vitest";
|
|
2
|
+
import * as Cause from "effect/Cause";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Exit from "effect/Exit";
|
|
5
|
+
import { Fx, Push, Sink } from "./index.js";
|
|
6
|
+
|
|
7
|
+
describe("Push.mapError", () => {
|
|
8
|
+
it("maps Fx output error channel to new type", () =>
|
|
9
|
+
Effect.gen(function* () {
|
|
10
|
+
const push = Push.make(
|
|
11
|
+
Sink.make(
|
|
12
|
+
() => Effect.void,
|
|
13
|
+
() => Effect.void,
|
|
14
|
+
),
|
|
15
|
+
Fx.fail("err"),
|
|
16
|
+
);
|
|
17
|
+
const mapped = Push.mapError(push, (s) => ({ msg: s }));
|
|
18
|
+
const exit = yield* Effect.exit(Fx.collectAll(mapped));
|
|
19
|
+
assert(Exit.isFailure(exit));
|
|
20
|
+
const result = Cause.findFail(exit.cause);
|
|
21
|
+
assert(result._tag === "Success");
|
|
22
|
+
assert.deepStrictEqual(result.success.error, { msg: "err" });
|
|
23
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
24
|
+
|
|
25
|
+
it("dual call style: data-last", () =>
|
|
26
|
+
Effect.gen(function* () {
|
|
27
|
+
const push = Push.make(
|
|
28
|
+
Sink.make(
|
|
29
|
+
() => Effect.void,
|
|
30
|
+
() => Effect.void,
|
|
31
|
+
),
|
|
32
|
+
Fx.fail(42),
|
|
33
|
+
);
|
|
34
|
+
const mapped = Push.mapError(push, (n) => `n=${n}`);
|
|
35
|
+
const exit = yield* Effect.exit(Fx.collectAll(mapped));
|
|
36
|
+
assert(Exit.isFailure(exit));
|
|
37
|
+
const result = Cause.findFail(exit.cause);
|
|
38
|
+
assert(result._tag === "Success");
|
|
39
|
+
assert.strictEqual(result.success.error, "n=42");
|
|
40
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
41
|
+
|
|
42
|
+
it("preserves success values on Fx side", () =>
|
|
43
|
+
Effect.gen(function* () {
|
|
44
|
+
const push = Push.make(
|
|
45
|
+
Sink.make(
|
|
46
|
+
() => Effect.void,
|
|
47
|
+
() => Effect.void,
|
|
48
|
+
),
|
|
49
|
+
Fx.fromIterable([1, 2, 3]),
|
|
50
|
+
);
|
|
51
|
+
const mapped = push.pipe(Push.mapError((e: string) => e));
|
|
52
|
+
const result = yield* Fx.collectAll(mapped);
|
|
53
|
+
assert.deepStrictEqual(result, [1, 2, 3]);
|
|
54
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
55
|
+
|
|
56
|
+
it("does not map defects (Cause.map only maps Fail)", () =>
|
|
57
|
+
Effect.gen(function* () {
|
|
58
|
+
const push = Push.make(
|
|
59
|
+
Sink.make(
|
|
60
|
+
() => Effect.void,
|
|
61
|
+
() => Effect.void,
|
|
62
|
+
),
|
|
63
|
+
Fx.die("defect"),
|
|
64
|
+
);
|
|
65
|
+
const mapped = Push.mapError(push, (_e: string) => "mapped");
|
|
66
|
+
const exit = yield* Effect.exit(Fx.collectAll(mapped));
|
|
67
|
+
assert(Exit.isFailure(exit));
|
|
68
|
+
assert(Cause.hasDies(exit.cause));
|
|
69
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("Push.mapBoth", () => {
|
|
73
|
+
it("maps both Fx success and error channels", () =>
|
|
74
|
+
Effect.gen(function* () {
|
|
75
|
+
const push = Push.make(
|
|
76
|
+
Sink.make(
|
|
77
|
+
() => Effect.void,
|
|
78
|
+
() => Effect.void,
|
|
79
|
+
),
|
|
80
|
+
Fx.fromIterable([1, 2, 3]),
|
|
81
|
+
);
|
|
82
|
+
const mapped = Push.mapBoth(push, {
|
|
83
|
+
onSuccess: (n) => n * 10,
|
|
84
|
+
onFailure: (e: string) => e.length,
|
|
85
|
+
});
|
|
86
|
+
const result = yield* Fx.collectAll(mapped);
|
|
87
|
+
assert.deepStrictEqual(result, [10, 20, 30]);
|
|
88
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
89
|
+
|
|
90
|
+
it("maps Fx failure when stream fails", () =>
|
|
91
|
+
Effect.gen(function* () {
|
|
92
|
+
const push = Push.make(
|
|
93
|
+
Sink.make(
|
|
94
|
+
() => Effect.void,
|
|
95
|
+
() => Effect.void,
|
|
96
|
+
),
|
|
97
|
+
Fx.fail("boom"),
|
|
98
|
+
);
|
|
99
|
+
const mapped = Push.mapBoth(push, {
|
|
100
|
+
onSuccess: (a: number) => a,
|
|
101
|
+
onFailure: (s: string) => ({ message: s }),
|
|
102
|
+
});
|
|
103
|
+
const exit = yield* Effect.exit(Fx.collectAll(mapped));
|
|
104
|
+
assert(Exit.isFailure(exit));
|
|
105
|
+
const result = Cause.findFail(exit.cause);
|
|
106
|
+
assert(result._tag === "Success");
|
|
107
|
+
assert.deepStrictEqual(result.success.error, { message: "boom" });
|
|
108
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
109
|
+
|
|
110
|
+
it("dual call style: data-last", () =>
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
const push = Push.make(
|
|
113
|
+
Sink.make(
|
|
114
|
+
() => Effect.void,
|
|
115
|
+
() => Effect.void,
|
|
116
|
+
),
|
|
117
|
+
Fx.fromIterable(["a", "b"]),
|
|
118
|
+
);
|
|
119
|
+
const mapped = Push.mapBoth(push, {
|
|
120
|
+
onSuccess: (s) => s.toUpperCase(),
|
|
121
|
+
onFailure: (e: number) => String(e),
|
|
122
|
+
});
|
|
123
|
+
const result = yield* Fx.collectAll(mapped);
|
|
124
|
+
assert.deepStrictEqual(result, ["A", "B"]);
|
|
125
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
126
|
+
|
|
127
|
+
it("preserves defects (onFailure only maps Fail)", () =>
|
|
128
|
+
Effect.gen(function* () {
|
|
129
|
+
const push = Push.make(
|
|
130
|
+
Sink.make(
|
|
131
|
+
() => Effect.void,
|
|
132
|
+
() => Effect.void,
|
|
133
|
+
),
|
|
134
|
+
Fx.die("oops"),
|
|
135
|
+
);
|
|
136
|
+
const mapped = Push.mapBoth(push, {
|
|
137
|
+
onSuccess: (a: number) => a,
|
|
138
|
+
onFailure: (_e: string) => "mapped",
|
|
139
|
+
});
|
|
140
|
+
const exit = yield* Effect.exit(Fx.collectAll(mapped));
|
|
141
|
+
assert(Exit.isFailure(exit));
|
|
142
|
+
assert(Cause.hasDies(exit.cause));
|
|
143
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("Push.mapAccum", () => {
|
|
147
|
+
it("emits accumulated values from (state, value) => [nextState, emitted]", () =>
|
|
148
|
+
Effect.gen(function* () {
|
|
149
|
+
const push = Push.make(
|
|
150
|
+
Sink.make(
|
|
151
|
+
() => Effect.void,
|
|
152
|
+
() => Effect.void,
|
|
153
|
+
),
|
|
154
|
+
Fx.fromIterable([1, 2, 3, 4]),
|
|
155
|
+
);
|
|
156
|
+
const accumulated = Push.mapAccum(push, 0, (sum, n) => [sum + n, sum + n] as const);
|
|
157
|
+
const result = yield* Fx.collectAll(accumulated);
|
|
158
|
+
assert.deepStrictEqual(result, [1, 3, 6, 10]);
|
|
159
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
160
|
+
|
|
161
|
+
it("data-last form: pipe(push, mapAccum(initial, f))", () =>
|
|
162
|
+
Effect.gen(function* () {
|
|
163
|
+
const push = Push.make(
|
|
164
|
+
Sink.make(
|
|
165
|
+
() => Effect.void,
|
|
166
|
+
() => Effect.void,
|
|
167
|
+
),
|
|
168
|
+
Fx.fromIterable(["a", "b", "c"]),
|
|
169
|
+
);
|
|
170
|
+
const accumulated = push.pipe(
|
|
171
|
+
Push.mapAccum("", (acc, s) => [`${acc}${s}`, `${acc}${s}`] as const),
|
|
172
|
+
);
|
|
173
|
+
const result = yield* Fx.collectAll(accumulated);
|
|
174
|
+
assert.deepStrictEqual(result, ["a", "ab", "abc"]);
|
|
175
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
176
|
+
|
|
177
|
+
it("data-first form: mapAccum(push, initial, f)", () =>
|
|
178
|
+
Effect.gen(function* () {
|
|
179
|
+
const push = Push.make(
|
|
180
|
+
Sink.make(
|
|
181
|
+
() => Effect.void,
|
|
182
|
+
() => Effect.void,
|
|
183
|
+
),
|
|
184
|
+
Fx.fromIterable([2, 3, 4]),
|
|
185
|
+
);
|
|
186
|
+
const accumulated = Push.mapAccum(push, 1, (prod, n) => [prod * n, prod * n] as const);
|
|
187
|
+
const result = yield* Fx.collectAll(accumulated);
|
|
188
|
+
assert.deepStrictEqual(result, [2, 6, 24]);
|
|
189
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
190
|
+
|
|
191
|
+
it("single value emits one accumulated result", () =>
|
|
192
|
+
Effect.gen(function* () {
|
|
193
|
+
const push = Push.make(
|
|
194
|
+
Sink.make(
|
|
195
|
+
() => Effect.void,
|
|
196
|
+
() => Effect.void,
|
|
197
|
+
),
|
|
198
|
+
Fx.succeed(42),
|
|
199
|
+
);
|
|
200
|
+
const accumulated = Push.mapAccum(push, 0, (s, n) => [s + n, n] as const);
|
|
201
|
+
const result = yield* Fx.collectAll(accumulated);
|
|
202
|
+
assert.deepStrictEqual(result, [42]);
|
|
203
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe("Push.mapAccumEffect", () => {
|
|
207
|
+
it("emits accumulated values from effectful reducer", () =>
|
|
208
|
+
Effect.gen(function* () {
|
|
209
|
+
const push = Push.make(
|
|
210
|
+
Sink.make(
|
|
211
|
+
() => Effect.void,
|
|
212
|
+
() => Effect.void,
|
|
213
|
+
),
|
|
214
|
+
Fx.fromIterable([1, 2, 3]),
|
|
215
|
+
);
|
|
216
|
+
const accumulated = Push.mapAccumEffect(push, 0, (sum, n) =>
|
|
217
|
+
Effect.succeed([sum + n, sum + n] as const),
|
|
218
|
+
);
|
|
219
|
+
const result = yield* Fx.collectAll(accumulated);
|
|
220
|
+
assert.deepStrictEqual(result, [1, 3, 6]);
|
|
221
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
222
|
+
|
|
223
|
+
it("data-first form: mapAccumEffect(push, initial, f)", () =>
|
|
224
|
+
Effect.gen(function* () {
|
|
225
|
+
const push = Push.make(
|
|
226
|
+
Sink.make(
|
|
227
|
+
() => Effect.void,
|
|
228
|
+
() => Effect.void,
|
|
229
|
+
),
|
|
230
|
+
Fx.fromIterable([10, 20]),
|
|
231
|
+
);
|
|
232
|
+
const accumulated = Push.mapAccumEffect(push, 1, (acc, n) =>
|
|
233
|
+
Effect.succeed([acc * n, acc * n] as const),
|
|
234
|
+
);
|
|
235
|
+
const result = yield* Fx.collectAll(accumulated);
|
|
236
|
+
assert.deepStrictEqual(result, [10, 200]);
|
|
237
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
238
|
+
|
|
239
|
+
it("propagates effect failure", () =>
|
|
240
|
+
Effect.gen(function* () {
|
|
241
|
+
const push = Push.make(
|
|
242
|
+
Sink.make(
|
|
243
|
+
() => Effect.void,
|
|
244
|
+
() => Effect.void,
|
|
245
|
+
),
|
|
246
|
+
Fx.fromIterable([1, 2, 3]),
|
|
247
|
+
);
|
|
248
|
+
const accumulated = Push.mapAccumEffect(push, 0, (sum, n) =>
|
|
249
|
+
n === 2 ? Effect.fail("stop" as const) : Effect.succeed([sum + n, sum + n] as const),
|
|
250
|
+
);
|
|
251
|
+
const exit = yield* Effect.exit(Fx.collectAll(accumulated));
|
|
252
|
+
assert(Exit.isFailure(exit));
|
|
253
|
+
const fail = Cause.findFail(exit.cause);
|
|
254
|
+
assert(fail._tag === "Success" && fail.success.error === "stop");
|
|
255
|
+
}).pipe(Effect.scoped, Effect.runPromise));
|
|
256
|
+
});
|
|
@@ -14,6 +14,7 @@ import type * as Order from "effect/Order";
|
|
|
14
14
|
import type * as Scope from "effect/Scope";
|
|
15
15
|
import type * as Fx from "../Fx/index.js";
|
|
16
16
|
import * as RefSubject from "./RefSubject.js";
|
|
17
|
+
import { Filter, Result } from "effect";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* A RefArray is a RefSubject that is specialized over an array of values.
|
|
@@ -310,7 +311,9 @@ export const partition: {
|
|
|
310
311
|
predicate: (a: A) => boolean,
|
|
311
312
|
): RefSubject.Computed<never, E, readonly [ReadonlyArray<A>, ReadonlyArray<A>]>;
|
|
312
313
|
} = dual(2, function partition<A, E, R>(ref: RefArray<A, E, R>, predicate: (a: A) => boolean) {
|
|
313
|
-
return RefSubject.map(ref,
|
|
314
|
+
return RefSubject.map(ref, (array) =>
|
|
315
|
+
ReadonlyArray.partition(array, Result.liftPredicate(predicate, Result.fail)),
|
|
316
|
+
);
|
|
314
317
|
});
|
|
315
318
|
|
|
316
319
|
/**
|
|
@@ -12,6 +12,7 @@ import type * as Order from "effect/Order";
|
|
|
12
12
|
import type * as Scope from "effect/Scope";
|
|
13
13
|
import type * as Fx from "../Fx/index.js";
|
|
14
14
|
import * as RefSubject from "./RefSubject.js";
|
|
15
|
+
import { Result } from "effect";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* A RefChunk is a RefSubject specialized over a Chunk of values.
|
|
@@ -356,7 +357,7 @@ export const partition: {
|
|
|
356
357
|
predicate: (a: A) => boolean,
|
|
357
358
|
): RefSubject.Computed<[Chunk.Chunk<A>, Chunk.Chunk<A>], E, R>;
|
|
358
359
|
} = dual(2, function partition<A, E, R>(ref: RefChunk<A, E, R>, predicate: (a: A) => boolean) {
|
|
359
|
-
return RefSubject.map(ref, Chunk.partition(predicate));
|
|
360
|
+
return RefSubject.map(ref, Chunk.partition(Result.liftPredicate(predicate, Result.fail)));
|
|
360
361
|
});
|
|
361
362
|
|
|
362
363
|
/**
|
|
@@ -110,13 +110,13 @@ export const add: {
|
|
|
110
110
|
*/
|
|
111
111
|
export const addDuration: {
|
|
112
112
|
(
|
|
113
|
-
duration: Duration.
|
|
113
|
+
duration: Duration.Input,
|
|
114
114
|
): <E, R>(ref: RefDateTime<E, R>) => RefSubject.Computed<DateTime.DateTime, E, R>;
|
|
115
115
|
<E, R>(
|
|
116
116
|
ref: RefDateTime<E, R>,
|
|
117
|
-
duration: Duration.
|
|
117
|
+
duration: Duration.Input,
|
|
118
118
|
): RefSubject.Computed<DateTime.DateTime, E, R>;
|
|
119
|
-
} = dual(2, function addDuration<E, R>(ref: RefDateTime<E, R>, duration: Duration.
|
|
119
|
+
} = dual(2, function addDuration<E, R>(ref: RefDateTime<E, R>, duration: Duration.Input) {
|
|
120
120
|
return RefSubject.map(ref, (self) => DateTime.addDuration(self, duration));
|
|
121
121
|
});
|
|
122
122
|
|
|
@@ -177,16 +177,16 @@ export const subtract: {
|
|
|
177
177
|
*/
|
|
178
178
|
export const subtractDuration: {
|
|
179
179
|
(
|
|
180
|
-
duration: Duration.
|
|
180
|
+
duration: Duration.Input,
|
|
181
181
|
): <E, R>(ref: RefDateTime<E, R>) => RefSubject.Computed<DateTime.DateTime, E, R>;
|
|
182
182
|
<E, R>(
|
|
183
183
|
ref: RefDateTime<E, R>,
|
|
184
|
-
duration: Duration.
|
|
184
|
+
duration: Duration.Input,
|
|
185
185
|
): RefSubject.Computed<DateTime.DateTime, E, R>;
|
|
186
186
|
} = dual(2, function subtractDuration<
|
|
187
187
|
E,
|
|
188
188
|
R,
|
|
189
|
-
>(ref: RefDateTime<E, R>, duration: Duration.
|
|
189
|
+
>(ref: RefDateTime<E, R>, duration: Duration.Input) {
|
|
190
190
|
return RefSubject.map(ref, (self) => DateTime.subtractDuration(self, duration));
|
|
191
191
|
});
|
|
192
192
|
|
|
@@ -11,6 +11,7 @@ import * as Option from "effect/Option";
|
|
|
11
11
|
import type * as Scope from "effect/Scope";
|
|
12
12
|
import type * as Fx from "../Fx/index.js";
|
|
13
13
|
import * as RefSubject from "./RefSubject.js";
|
|
14
|
+
import { Result } from "effect";
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* A RefHashMap is a RefSubject specialized over a HashMap.
|
|
@@ -354,7 +355,15 @@ export const filterMapValues: {
|
|
|
354
355
|
R,
|
|
355
356
|
B,
|
|
356
357
|
>(ref: RefHashMap<K, V, E, R>, f: (value: V, key: K) => Option.Option<B>) {
|
|
357
|
-
return RefSubject.map(
|
|
358
|
+
return RefSubject.map(
|
|
359
|
+
ref,
|
|
360
|
+
HashMap.filterMap((value, key) =>
|
|
361
|
+
Option.match(f(value, key), {
|
|
362
|
+
onNone: () => Result.failVoid,
|
|
363
|
+
onSome: (b) => Result.succeed(b),
|
|
364
|
+
}),
|
|
365
|
+
),
|
|
366
|
+
);
|
|
358
367
|
});
|
|
359
368
|
|
|
360
369
|
/**
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* @since 1.18.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { Result } from "effect";
|
|
6
7
|
import type * as Effect from "effect/Effect";
|
|
7
8
|
import { equals } from "effect/Equal";
|
|
8
9
|
import { dual } from "effect/Function";
|
|
9
10
|
import * as Iterable from "effect/Iterable";
|
|
10
|
-
import
|
|
11
|
+
import * as Option from "effect/Option";
|
|
11
12
|
import type * as Scope from "effect/Scope";
|
|
12
13
|
import type * as Fx from "../Fx/index.js";
|
|
13
14
|
import * as RefSubject from "./RefSubject.js";
|
|
@@ -233,7 +234,15 @@ export const filterMap: {
|
|
|
233
234
|
E,
|
|
234
235
|
R,
|
|
235
236
|
>(ref: RefIterable<A, E, R>, f: (a: A, index: number) => Option.Option<A>) {
|
|
236
|
-
return RefSubject.update(
|
|
237
|
+
return RefSubject.update(
|
|
238
|
+
ref,
|
|
239
|
+
Iterable.filterMap((a, index) =>
|
|
240
|
+
Option.match(f(a, index), {
|
|
241
|
+
onNone: () => Result.failVoid,
|
|
242
|
+
onSome: (b) => Result.succeed(b),
|
|
243
|
+
}),
|
|
244
|
+
),
|
|
245
|
+
);
|
|
237
246
|
});
|
|
238
247
|
|
|
239
248
|
/**
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @since 1.18.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { Result } from "effect";
|
|
6
7
|
import type * as Effect from "effect/Effect";
|
|
7
8
|
import { equals } from "effect/Equal";
|
|
8
9
|
import { dual } from "effect/Function";
|
|
@@ -450,7 +451,11 @@ export const filterMapValues: {
|
|
|
450
451
|
R,
|
|
451
452
|
B,
|
|
452
453
|
>(ref: RefRecord<K, V, E, R>, f: (value: V, key: K) => Option.Option<B>) {
|
|
453
|
-
return RefSubject.map(ref, (r) =>
|
|
454
|
+
return RefSubject.map(ref, (r) =>
|
|
455
|
+
Record.filterMap(r, (value, key) =>
|
|
456
|
+
f(value, key) ? Result.succeed(value) : Result.fail(value),
|
|
457
|
+
),
|
|
458
|
+
);
|
|
454
459
|
});
|
|
455
460
|
|
|
456
461
|
/**
|
|
@@ -477,7 +482,9 @@ export const partition: {
|
|
|
477
482
|
return RefSubject.map(
|
|
478
483
|
ref,
|
|
479
484
|
(r) =>
|
|
480
|
-
Record.partition(r,
|
|
485
|
+
Record.partition(r, (value, key) =>
|
|
486
|
+
predicate(value, key) ? Result.succeed(value) : Result.fail(value),
|
|
487
|
+
) as [Record.ReadonlyRecord<K, V>, Record.ReadonlyRecord<K, V>],
|
|
481
488
|
);
|
|
482
489
|
});
|
|
483
490
|
|