@unthrown/effect 0.1.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/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/index.cjs +145 -0
- package/dist/index.d.cts +104 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +104 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +141 -0
- package/dist/index.mjs.map +1 -0
- package/docs/index.md +270 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benoit Travers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @unthrown/effect
|
|
2
|
+
|
|
3
|
+
> [Effect](https://effect.website) interop for
|
|
4
|
+
> [unthrown](https://github.com/btravstack/unthrown)'s `Result`.
|
|
5
|
+
|
|
6
|
+
📖 **[Documentation](https://btravstack.github.io/unthrown/guide/interop)** ·
|
|
7
|
+
[API Reference](https://btravstack.github.io/unthrown/api/effect/)
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @unthrown/effect effect
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Effect is the one neighbour that shares unthrown's three-channel shape: an
|
|
14
|
+
`Exit<A, E>` is a success or a `Cause`, and a `Cause` distinguishes a modeled
|
|
15
|
+
failure (`Cause.fail` ↔ `Err`) from an unexpected one (`Cause.die` ↔ `Defect`).
|
|
16
|
+
So `Result ↔ Exit` is a genuine **bijection**.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { ok, err } from "unthrown";
|
|
20
|
+
import { toExit, fromEffect, toEither } from "@unthrown/effect";
|
|
21
|
+
import { Effect } from "effect";
|
|
22
|
+
|
|
23
|
+
toExit(ok(1)); // Exit.succeed(1)
|
|
24
|
+
toExit(err("e")); // Exit.fail("e") — a modeled Cause.fail
|
|
25
|
+
|
|
26
|
+
// Run an Effect and collect its outcome (die/interrupt become a Defect):
|
|
27
|
+
await fromEffect(Effect.succeed(1)).match({ ok, err, defect: String });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
- `toExit` / `fromExit` — the bijection: `Ok↔succeed`, `Err↔Cause.fail`,
|
|
31
|
+
`Defect↔Cause.die`. On the way back a die/interruption becomes a `Defect`, and
|
|
32
|
+
a `Defect` **dominates** a modeled failure in a composite cause (same rule as
|
|
33
|
+
`all`).
|
|
34
|
+
- `toEither` / `fromEither` — `Either` has no defect channel, so `toEither(r,
|
|
35
|
+
onDefect)` **forces** you to triage the defect into `E` (Thesis #3). `fromEither`
|
|
36
|
+
never yields a `Defect`.
|
|
37
|
+
- `toEffect` / `fromEffect` — `toEffect` lifts a `Result` **or** `AsyncResult`
|
|
38
|
+
into an `Effect<T, E>` (`Defect → Effect.die`); `fromEffect` runs an
|
|
39
|
+
environment-free `Effect<T, E>` to an `AsyncResult<T, E>`.
|
|
40
|
+
|
|
41
|
+
`effect` is a peer dependency.
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
[MIT](../../LICENSE) © Benoit TRAVERS
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let effect = require("effect");
|
|
3
|
+
let unthrown = require("unthrown");
|
|
4
|
+
//#region src/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* Convert a `Result` into an Effect `Exit` — a **bijection**, since both
|
|
7
|
+
* carry three channels.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* `Ok → Exit.succeed`, `Err → Exit.fail` (a modeled `Cause.fail`), and
|
|
11
|
+
* `Defect → Exit.die` (an unexpected `Cause.die`). Round-trips with
|
|
12
|
+
* {@link fromExit}.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam T - the success value type.
|
|
15
|
+
* @typeParam E - the modeled error type.
|
|
16
|
+
* @param result - the result to convert.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { ok } from "unthrown";
|
|
21
|
+
* import { toExit } from "@unthrown/effect";
|
|
22
|
+
* toExit(ok(1)); // Exit.succeed(1)
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function toExit(result) {
|
|
26
|
+
return result.match({
|
|
27
|
+
ok: (value) => effect.Exit.succeed(value),
|
|
28
|
+
err: (error) => effect.Exit.fail(error),
|
|
29
|
+
defect: (cause) => effect.Exit.die(cause)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert an Effect `Exit` into a `Result` — the inverse of
|
|
34
|
+
* {@link toExit}.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* `Exit.Success → Ok`. For a failure, the enclosing `Cause` is reduced:
|
|
38
|
+
*
|
|
39
|
+
* - a `Cause.die` becomes a `Defect`,
|
|
40
|
+
* - otherwise a `Cause.fail` becomes the modeled `Err`,
|
|
41
|
+
* - a pure interruption (or empty cause) becomes a `Defect`.
|
|
42
|
+
*
|
|
43
|
+
* A `Defect` **dominates** a modeled failure in a composite cause — the same
|
|
44
|
+
* rule unthrown's `all` uses, on the principle that an unexpected failure is the
|
|
45
|
+
* more severe signal.
|
|
46
|
+
*
|
|
47
|
+
* @typeParam T - the success value type.
|
|
48
|
+
* @typeParam E - the modeled error type.
|
|
49
|
+
* @param exit - the exit to convert.
|
|
50
|
+
*/
|
|
51
|
+
function fromExit(exit) {
|
|
52
|
+
return effect.Exit.match(exit, {
|
|
53
|
+
onSuccess: (value) => (0, unthrown.ok)(value),
|
|
54
|
+
onFailure: (cause) => {
|
|
55
|
+
const die = effect.Cause.dieOption(cause);
|
|
56
|
+
if (effect.Option.isSome(die)) return dieToResult(die.value);
|
|
57
|
+
const failure = effect.Cause.failureOption(cause);
|
|
58
|
+
if (effect.Option.isSome(failure)) return (0, unthrown.err)(failure.value);
|
|
59
|
+
return dieToResult(effect.Cause.squash(cause));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Convert a `Result` into an Effect `Either`, triaging any defect.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* `Either` has no defect channel, so a `Defect` cannot pass through silently —
|
|
68
|
+
* `onDefect` **must** fold its cause into a modeled error `E` (a `Left`). This
|
|
69
|
+
* is the boundary-qualification rule (Thesis #3) applied on the way out:
|
|
70
|
+
* `Ok → Right`, `Err → Left`, `Defect → Left(onDefect(cause))`.
|
|
71
|
+
*
|
|
72
|
+
* @typeParam T - the success value type.
|
|
73
|
+
* @typeParam E - the modeled error type.
|
|
74
|
+
* @param result - the result to convert.
|
|
75
|
+
* @param onDefect - folds a defect's unknown cause into a modeled `E`.
|
|
76
|
+
*/
|
|
77
|
+
function toEither(result, onDefect) {
|
|
78
|
+
return result.match({
|
|
79
|
+
ok: (value) => effect.Either.right(value),
|
|
80
|
+
err: (error) => effect.Either.left(error),
|
|
81
|
+
defect: (cause) => effect.Either.left(onDefect(cause))
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Convert an Effect `Either` into a `Result`.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* `Right → Ok`, `Left → Err`. An `Either` carries no defect, so the result is
|
|
89
|
+
* never a `Defect`.
|
|
90
|
+
*
|
|
91
|
+
* @typeParam T - the success value type.
|
|
92
|
+
* @typeParam E - the modeled error type.
|
|
93
|
+
* @param either - the either to convert.
|
|
94
|
+
*/
|
|
95
|
+
function fromEither(either) {
|
|
96
|
+
return effect.Either.match(either, {
|
|
97
|
+
onLeft: (error) => (0, unthrown.err)(error),
|
|
98
|
+
onRight: (value) => (0, unthrown.ok)(value)
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function toEffect(source) {
|
|
102
|
+
if (isAsyncResult(source)) return effect.Effect.flatMap(effect.Effect.promise(() => settle(source)), (result) => resultToEffect(result));
|
|
103
|
+
return resultToEffect(source);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Run an `Effect` and collect its outcome as an `AsyncResult`.
|
|
107
|
+
*
|
|
108
|
+
* @remarks
|
|
109
|
+
* The effect must need no environment (`R = never`). It is run to an `Exit`
|
|
110
|
+
* (which never rejects), then folded with {@link fromExit}: success → `Ok`, a
|
|
111
|
+
* modeled failure → `Err`, a die/interruption → `Defect`. The returned
|
|
112
|
+
* `AsyncResult` never throws when awaited.
|
|
113
|
+
*
|
|
114
|
+
* @typeParam T - the success value type.
|
|
115
|
+
* @typeParam E - the modeled error type.
|
|
116
|
+
* @param effect - the effect to run.
|
|
117
|
+
*/
|
|
118
|
+
function fromEffect(effect$1) {
|
|
119
|
+
return (0, unthrown.fromSafePromise)(effect.Effect.runPromiseExit(effect$1)).flatMap((exit) => fromExit(exit));
|
|
120
|
+
}
|
|
121
|
+
function resultToEffect(result) {
|
|
122
|
+
return result.match({
|
|
123
|
+
ok: (value) => effect.Effect.succeed(value),
|
|
124
|
+
err: (error) => effect.Effect.fail(error),
|
|
125
|
+
defect: (cause) => effect.Effect.die(cause)
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function dieToResult(cause) {
|
|
129
|
+
return (0, unthrown.fromThrowable)(() => {
|
|
130
|
+
throw cause;
|
|
131
|
+
}, unthrown.defect)();
|
|
132
|
+
}
|
|
133
|
+
function settle(asyncResult) {
|
|
134
|
+
return (async () => await asyncResult)();
|
|
135
|
+
}
|
|
136
|
+
function isAsyncResult(source) {
|
|
137
|
+
return typeof source.then === "function";
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
exports.fromEffect = fromEffect;
|
|
141
|
+
exports.fromEither = fromEither;
|
|
142
|
+
exports.fromExit = fromExit;
|
|
143
|
+
exports.toEffect = toEffect;
|
|
144
|
+
exports.toEither = toEither;
|
|
145
|
+
exports.toExit = toExit;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Effect, Either, Exit } from "effect";
|
|
2
|
+
import { AsyncResult, Result } from "unthrown";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Convert a `Result` into an Effect `Exit` — a **bijection**, since both
|
|
7
|
+
* carry three channels.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* `Ok → Exit.succeed`, `Err → Exit.fail` (a modeled `Cause.fail`), and
|
|
11
|
+
* `Defect → Exit.die` (an unexpected `Cause.die`). Round-trips with
|
|
12
|
+
* {@link fromExit}.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam T - the success value type.
|
|
15
|
+
* @typeParam E - the modeled error type.
|
|
16
|
+
* @param result - the result to convert.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { ok } from "unthrown";
|
|
21
|
+
* import { toExit } from "@unthrown/effect";
|
|
22
|
+
* toExit(ok(1)); // Exit.succeed(1)
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function toExit<T, E>(result: Result<T, E>): Exit.Exit<T, E>;
|
|
26
|
+
/**
|
|
27
|
+
* Convert an Effect `Exit` into a `Result` — the inverse of
|
|
28
|
+
* {@link toExit}.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* `Exit.Success → Ok`. For a failure, the enclosing `Cause` is reduced:
|
|
32
|
+
*
|
|
33
|
+
* - a `Cause.die` becomes a `Defect`,
|
|
34
|
+
* - otherwise a `Cause.fail` becomes the modeled `Err`,
|
|
35
|
+
* - a pure interruption (or empty cause) becomes a `Defect`.
|
|
36
|
+
*
|
|
37
|
+
* A `Defect` **dominates** a modeled failure in a composite cause — the same
|
|
38
|
+
* rule unthrown's `all` uses, on the principle that an unexpected failure is the
|
|
39
|
+
* more severe signal.
|
|
40
|
+
*
|
|
41
|
+
* @typeParam T - the success value type.
|
|
42
|
+
* @typeParam E - the modeled error type.
|
|
43
|
+
* @param exit - the exit to convert.
|
|
44
|
+
*/
|
|
45
|
+
declare function fromExit<T, E>(exit: Exit.Exit<T, E>): Result<T, E>;
|
|
46
|
+
/**
|
|
47
|
+
* Convert a `Result` into an Effect `Either`, triaging any defect.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* `Either` has no defect channel, so a `Defect` cannot pass through silently —
|
|
51
|
+
* `onDefect` **must** fold its cause into a modeled error `E` (a `Left`). This
|
|
52
|
+
* is the boundary-qualification rule (Thesis #3) applied on the way out:
|
|
53
|
+
* `Ok → Right`, `Err → Left`, `Defect → Left(onDefect(cause))`.
|
|
54
|
+
*
|
|
55
|
+
* @typeParam T - the success value type.
|
|
56
|
+
* @typeParam E - the modeled error type.
|
|
57
|
+
* @param result - the result to convert.
|
|
58
|
+
* @param onDefect - folds a defect's unknown cause into a modeled `E`.
|
|
59
|
+
*/
|
|
60
|
+
declare function toEither<T, E>(result: Result<T, E>, onDefect: (cause: unknown) => E): Either.Either<T, E>;
|
|
61
|
+
/**
|
|
62
|
+
* Convert an Effect `Either` into a `Result`.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* `Right → Ok`, `Left → Err`. An `Either` carries no defect, so the result is
|
|
66
|
+
* never a `Defect`.
|
|
67
|
+
*
|
|
68
|
+
* @typeParam T - the success value type.
|
|
69
|
+
* @typeParam E - the modeled error type.
|
|
70
|
+
* @param either - the either to convert.
|
|
71
|
+
*/
|
|
72
|
+
declare function fromEither<T, E>(either: Either.Either<T, E>): Result<T, E>;
|
|
73
|
+
/**
|
|
74
|
+
* Lift a `Result` or `AsyncResult` into an `Effect`.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* `Ok → Effect.succeed`, `Err → Effect.fail`, `Defect → Effect.die`. The
|
|
78
|
+
* resulting `Effect` needs no environment (`R = never`). An `AsyncResult` is
|
|
79
|
+
* awaited inside the effect (it never rejects), so this is the `AsyncResult →
|
|
80
|
+
* Effect` direction too.
|
|
81
|
+
*
|
|
82
|
+
* @typeParam T - the success value type.
|
|
83
|
+
* @typeParam E - the modeled error type.
|
|
84
|
+
* @param source - the result, or async result, to lift.
|
|
85
|
+
*/
|
|
86
|
+
declare function toEffect<T, E>(source: Result<T, E>): Effect.Effect<T, E>;
|
|
87
|
+
declare function toEffect<T, E>(source: AsyncResult<T, E>): Effect.Effect<T, E>;
|
|
88
|
+
/**
|
|
89
|
+
* Run an `Effect` and collect its outcome as an `AsyncResult`.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* The effect must need no environment (`R = never`). It is run to an `Exit`
|
|
93
|
+
* (which never rejects), then folded with {@link fromExit}: success → `Ok`, a
|
|
94
|
+
* modeled failure → `Err`, a die/interruption → `Defect`. The returned
|
|
95
|
+
* `AsyncResult` never throws when awaited.
|
|
96
|
+
*
|
|
97
|
+
* @typeParam T - the success value type.
|
|
98
|
+
* @typeParam E - the modeled error type.
|
|
99
|
+
* @param effect - the effect to run.
|
|
100
|
+
*/
|
|
101
|
+
declare function fromEffect<T, E>(effect: Effect.Effect<T, E>): AsyncResult<T, E>;
|
|
102
|
+
//#endregion
|
|
103
|
+
export { fromEffect, fromEither, fromExit, toEffect, toEither, toExit };
|
|
104
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;AA0CA;;;;;;;;;;;;;;;;;;iBAAgB,MAAA,OAAa,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,CAAA;;;;AAAC;AA2BlE;;;;;;;;;;;;;;;iBAAgB,QAAA,OAAe,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;AAAC;AA4BlE;;;;;;;iBAAgB,QAAA,OACd,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,GAClB,QAAA,GAAW,KAAA,cAAmB,CAAA,GAC7B,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;;;;;;iBAmBJ,UAAA,OAAiB,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;AAnBpD;AAmBrB;;;;;;iBAoBgB,QAAA,OAAe,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA;AAAA,iBACvD,QAAA,OAAe,MAAA,EAAQ,WAAA,CAAY,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;;;;;;;;iBAwB5D,UAAA,OAAiB,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,WAAA,CAAY,CAAA,EAAG,CAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Effect, Either, Exit } from "effect";
|
|
2
|
+
import { AsyncResult, Result } from "unthrown";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Convert a `Result` into an Effect `Exit` — a **bijection**, since both
|
|
7
|
+
* carry three channels.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* `Ok → Exit.succeed`, `Err → Exit.fail` (a modeled `Cause.fail`), and
|
|
11
|
+
* `Defect → Exit.die` (an unexpected `Cause.die`). Round-trips with
|
|
12
|
+
* {@link fromExit}.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam T - the success value type.
|
|
15
|
+
* @typeParam E - the modeled error type.
|
|
16
|
+
* @param result - the result to convert.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { ok } from "unthrown";
|
|
21
|
+
* import { toExit } from "@unthrown/effect";
|
|
22
|
+
* toExit(ok(1)); // Exit.succeed(1)
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function toExit<T, E>(result: Result<T, E>): Exit.Exit<T, E>;
|
|
26
|
+
/**
|
|
27
|
+
* Convert an Effect `Exit` into a `Result` — the inverse of
|
|
28
|
+
* {@link toExit}.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* `Exit.Success → Ok`. For a failure, the enclosing `Cause` is reduced:
|
|
32
|
+
*
|
|
33
|
+
* - a `Cause.die` becomes a `Defect`,
|
|
34
|
+
* - otherwise a `Cause.fail` becomes the modeled `Err`,
|
|
35
|
+
* - a pure interruption (or empty cause) becomes a `Defect`.
|
|
36
|
+
*
|
|
37
|
+
* A `Defect` **dominates** a modeled failure in a composite cause — the same
|
|
38
|
+
* rule unthrown's `all` uses, on the principle that an unexpected failure is the
|
|
39
|
+
* more severe signal.
|
|
40
|
+
*
|
|
41
|
+
* @typeParam T - the success value type.
|
|
42
|
+
* @typeParam E - the modeled error type.
|
|
43
|
+
* @param exit - the exit to convert.
|
|
44
|
+
*/
|
|
45
|
+
declare function fromExit<T, E>(exit: Exit.Exit<T, E>): Result<T, E>;
|
|
46
|
+
/**
|
|
47
|
+
* Convert a `Result` into an Effect `Either`, triaging any defect.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* `Either` has no defect channel, so a `Defect` cannot pass through silently —
|
|
51
|
+
* `onDefect` **must** fold its cause into a modeled error `E` (a `Left`). This
|
|
52
|
+
* is the boundary-qualification rule (Thesis #3) applied on the way out:
|
|
53
|
+
* `Ok → Right`, `Err → Left`, `Defect → Left(onDefect(cause))`.
|
|
54
|
+
*
|
|
55
|
+
* @typeParam T - the success value type.
|
|
56
|
+
* @typeParam E - the modeled error type.
|
|
57
|
+
* @param result - the result to convert.
|
|
58
|
+
* @param onDefect - folds a defect's unknown cause into a modeled `E`.
|
|
59
|
+
*/
|
|
60
|
+
declare function toEither<T, E>(result: Result<T, E>, onDefect: (cause: unknown) => E): Either.Either<T, E>;
|
|
61
|
+
/**
|
|
62
|
+
* Convert an Effect `Either` into a `Result`.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* `Right → Ok`, `Left → Err`. An `Either` carries no defect, so the result is
|
|
66
|
+
* never a `Defect`.
|
|
67
|
+
*
|
|
68
|
+
* @typeParam T - the success value type.
|
|
69
|
+
* @typeParam E - the modeled error type.
|
|
70
|
+
* @param either - the either to convert.
|
|
71
|
+
*/
|
|
72
|
+
declare function fromEither<T, E>(either: Either.Either<T, E>): Result<T, E>;
|
|
73
|
+
/**
|
|
74
|
+
* Lift a `Result` or `AsyncResult` into an `Effect`.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* `Ok → Effect.succeed`, `Err → Effect.fail`, `Defect → Effect.die`. The
|
|
78
|
+
* resulting `Effect` needs no environment (`R = never`). An `AsyncResult` is
|
|
79
|
+
* awaited inside the effect (it never rejects), so this is the `AsyncResult →
|
|
80
|
+
* Effect` direction too.
|
|
81
|
+
*
|
|
82
|
+
* @typeParam T - the success value type.
|
|
83
|
+
* @typeParam E - the modeled error type.
|
|
84
|
+
* @param source - the result, or async result, to lift.
|
|
85
|
+
*/
|
|
86
|
+
declare function toEffect<T, E>(source: Result<T, E>): Effect.Effect<T, E>;
|
|
87
|
+
declare function toEffect<T, E>(source: AsyncResult<T, E>): Effect.Effect<T, E>;
|
|
88
|
+
/**
|
|
89
|
+
* Run an `Effect` and collect its outcome as an `AsyncResult`.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* The effect must need no environment (`R = never`). It is run to an `Exit`
|
|
93
|
+
* (which never rejects), then folded with {@link fromExit}: success → `Ok`, a
|
|
94
|
+
* modeled failure → `Err`, a die/interruption → `Defect`. The returned
|
|
95
|
+
* `AsyncResult` never throws when awaited.
|
|
96
|
+
*
|
|
97
|
+
* @typeParam T - the success value type.
|
|
98
|
+
* @typeParam E - the modeled error type.
|
|
99
|
+
* @param effect - the effect to run.
|
|
100
|
+
*/
|
|
101
|
+
declare function fromEffect<T, E>(effect: Effect.Effect<T, E>): AsyncResult<T, E>;
|
|
102
|
+
//#endregion
|
|
103
|
+
export { fromEffect, fromEither, fromExit, toEffect, toEither, toExit };
|
|
104
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;AA0CA;;;;;;;;;;;;;;;;;;iBAAgB,MAAA,OAAa,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,CAAA;;;;AAAC;AA2BlE;;;;;;;;;;;;;;;iBAAgB,QAAA,OAAe,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;AAAC;AA4BlE;;;;;;;iBAAgB,QAAA,OACd,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,GAClB,QAAA,GAAW,KAAA,cAAmB,CAAA,GAC7B,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;;;;;;iBAmBJ,UAAA,OAAiB,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;AAnBpD;AAmBrB;;;;;;iBAoBgB,QAAA,OAAe,MAAA,EAAQ,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA;AAAA,iBACvD,QAAA,OAAe,MAAA,EAAQ,WAAA,CAAY,CAAA,EAAG,CAAA,IAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA;;;;;;;;;;;;;;iBAwB5D,UAAA,OAAiB,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,IAAK,WAAA,CAAY,CAAA,EAAG,CAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Cause, Effect, Either, Exit, Option } from "effect";
|
|
2
|
+
import { defect, err, fromSafePromise, fromThrowable, ok } from "unthrown";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* Convert a `Result` into an Effect `Exit` — a **bijection**, since both
|
|
6
|
+
* carry three channels.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* `Ok → Exit.succeed`, `Err → Exit.fail` (a modeled `Cause.fail`), and
|
|
10
|
+
* `Defect → Exit.die` (an unexpected `Cause.die`). Round-trips with
|
|
11
|
+
* {@link fromExit}.
|
|
12
|
+
*
|
|
13
|
+
* @typeParam T - the success value type.
|
|
14
|
+
* @typeParam E - the modeled error type.
|
|
15
|
+
* @param result - the result to convert.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { ok } from "unthrown";
|
|
20
|
+
* import { toExit } from "@unthrown/effect";
|
|
21
|
+
* toExit(ok(1)); // Exit.succeed(1)
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function toExit(result) {
|
|
25
|
+
return result.match({
|
|
26
|
+
ok: (value) => Exit.succeed(value),
|
|
27
|
+
err: (error) => Exit.fail(error),
|
|
28
|
+
defect: (cause) => Exit.die(cause)
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Convert an Effect `Exit` into a `Result` — the inverse of
|
|
33
|
+
* {@link toExit}.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* `Exit.Success → Ok`. For a failure, the enclosing `Cause` is reduced:
|
|
37
|
+
*
|
|
38
|
+
* - a `Cause.die` becomes a `Defect`,
|
|
39
|
+
* - otherwise a `Cause.fail` becomes the modeled `Err`,
|
|
40
|
+
* - a pure interruption (or empty cause) becomes a `Defect`.
|
|
41
|
+
*
|
|
42
|
+
* A `Defect` **dominates** a modeled failure in a composite cause — the same
|
|
43
|
+
* rule unthrown's `all` uses, on the principle that an unexpected failure is the
|
|
44
|
+
* more severe signal.
|
|
45
|
+
*
|
|
46
|
+
* @typeParam T - the success value type.
|
|
47
|
+
* @typeParam E - the modeled error type.
|
|
48
|
+
* @param exit - the exit to convert.
|
|
49
|
+
*/
|
|
50
|
+
function fromExit(exit) {
|
|
51
|
+
return Exit.match(exit, {
|
|
52
|
+
onSuccess: (value) => ok(value),
|
|
53
|
+
onFailure: (cause) => {
|
|
54
|
+
const die = Cause.dieOption(cause);
|
|
55
|
+
if (Option.isSome(die)) return dieToResult(die.value);
|
|
56
|
+
const failure = Cause.failureOption(cause);
|
|
57
|
+
if (Option.isSome(failure)) return err(failure.value);
|
|
58
|
+
return dieToResult(Cause.squash(cause));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Convert a `Result` into an Effect `Either`, triaging any defect.
|
|
64
|
+
*
|
|
65
|
+
* @remarks
|
|
66
|
+
* `Either` has no defect channel, so a `Defect` cannot pass through silently —
|
|
67
|
+
* `onDefect` **must** fold its cause into a modeled error `E` (a `Left`). This
|
|
68
|
+
* is the boundary-qualification rule (Thesis #3) applied on the way out:
|
|
69
|
+
* `Ok → Right`, `Err → Left`, `Defect → Left(onDefect(cause))`.
|
|
70
|
+
*
|
|
71
|
+
* @typeParam T - the success value type.
|
|
72
|
+
* @typeParam E - the modeled error type.
|
|
73
|
+
* @param result - the result to convert.
|
|
74
|
+
* @param onDefect - folds a defect's unknown cause into a modeled `E`.
|
|
75
|
+
*/
|
|
76
|
+
function toEither(result, onDefect) {
|
|
77
|
+
return result.match({
|
|
78
|
+
ok: (value) => Either.right(value),
|
|
79
|
+
err: (error) => Either.left(error),
|
|
80
|
+
defect: (cause) => Either.left(onDefect(cause))
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Convert an Effect `Either` into a `Result`.
|
|
85
|
+
*
|
|
86
|
+
* @remarks
|
|
87
|
+
* `Right → Ok`, `Left → Err`. An `Either` carries no defect, so the result is
|
|
88
|
+
* never a `Defect`.
|
|
89
|
+
*
|
|
90
|
+
* @typeParam T - the success value type.
|
|
91
|
+
* @typeParam E - the modeled error type.
|
|
92
|
+
* @param either - the either to convert.
|
|
93
|
+
*/
|
|
94
|
+
function fromEither(either) {
|
|
95
|
+
return Either.match(either, {
|
|
96
|
+
onLeft: (error) => err(error),
|
|
97
|
+
onRight: (value) => ok(value)
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function toEffect(source) {
|
|
101
|
+
if (isAsyncResult(source)) return Effect.flatMap(Effect.promise(() => settle(source)), (result) => resultToEffect(result));
|
|
102
|
+
return resultToEffect(source);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Run an `Effect` and collect its outcome as an `AsyncResult`.
|
|
106
|
+
*
|
|
107
|
+
* @remarks
|
|
108
|
+
* The effect must need no environment (`R = never`). It is run to an `Exit`
|
|
109
|
+
* (which never rejects), then folded with {@link fromExit}: success → `Ok`, a
|
|
110
|
+
* modeled failure → `Err`, a die/interruption → `Defect`. The returned
|
|
111
|
+
* `AsyncResult` never throws when awaited.
|
|
112
|
+
*
|
|
113
|
+
* @typeParam T - the success value type.
|
|
114
|
+
* @typeParam E - the modeled error type.
|
|
115
|
+
* @param effect - the effect to run.
|
|
116
|
+
*/
|
|
117
|
+
function fromEffect(effect) {
|
|
118
|
+
return fromSafePromise(Effect.runPromiseExit(effect)).flatMap((exit) => fromExit(exit));
|
|
119
|
+
}
|
|
120
|
+
function resultToEffect(result) {
|
|
121
|
+
return result.match({
|
|
122
|
+
ok: (value) => Effect.succeed(value),
|
|
123
|
+
err: (error) => Effect.fail(error),
|
|
124
|
+
defect: (cause) => Effect.die(cause)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function dieToResult(cause) {
|
|
128
|
+
return fromThrowable(() => {
|
|
129
|
+
throw cause;
|
|
130
|
+
}, defect)();
|
|
131
|
+
}
|
|
132
|
+
function settle(asyncResult) {
|
|
133
|
+
return (async () => await asyncResult)();
|
|
134
|
+
}
|
|
135
|
+
function isAsyncResult(source) {
|
|
136
|
+
return typeof source.then === "function";
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
export { fromEffect, fromEither, fromExit, toEffect, toEither, toExit };
|
|
140
|
+
|
|
141
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["// @unthrown/effect — interop between unthrown's `Result`/`AsyncResult` and\n// Effect's `Exit`, `Either`, and `Effect`.\n//\n// Effect is the one neighbour that shares unthrown's three-channel shape: an\n// `Exit<A, E>` is `Success` | `Failure(Cause)`, and a `Cause` distinguishes a\n// modeled failure (`Cause.fail`, ↔ `Err`) from an unexpected one (`Cause.die`,\n// ↔ `Defect`). So `Result ↔ Exit` is a genuine bijection — the showcase here.\n//\n// import { ok } from \"unthrown\";\n// import { toExit, fromEffect } from \"@unthrown/effect\";\n//\n// toExit(ok(1)); // Exit.succeed(1)\n// await fromEffect(Effect.succeed(1)).match({ ok, err, defect });\n//\n// `Either` has only two channels, so converting a `Result` *into* an `Either`\n// forces you to triage the defect with `onDefect` (Thesis #3): there is no\n// silent path that drops it.\n\nimport { Cause, Effect, Either, Exit, Option } from \"effect\";\nimport { defect, err, fromSafePromise, fromThrowable, ok } from \"unthrown\";\nimport type { AsyncResult, Result } from \"unthrown\";\n\n/**\n * Convert a `Result` into an Effect `Exit` — a **bijection**, since both\n * carry three channels.\n *\n * @remarks\n * `Ok → Exit.succeed`, `Err → Exit.fail` (a modeled `Cause.fail`), and\n * `Defect → Exit.die` (an unexpected `Cause.die`). Round-trips with\n * {@link fromExit}.\n *\n * @typeParam T - the success value type.\n * @typeParam E - the modeled error type.\n * @param result - the result to convert.\n *\n * @example\n * ```ts\n * import { ok } from \"unthrown\";\n * import { toExit } from \"@unthrown/effect\";\n * toExit(ok(1)); // Exit.succeed(1)\n * ```\n */\nexport function toExit<T, E>(result: Result<T, E>): Exit.Exit<T, E> {\n return result.match<Exit.Exit<T, E>>({\n ok: (value) => Exit.succeed(value),\n err: (error) => Exit.fail(error),\n defect: (cause) => Exit.die(cause),\n });\n}\n\n/**\n * Convert an Effect `Exit` into a `Result` — the inverse of\n * {@link toExit}.\n *\n * @remarks\n * `Exit.Success → Ok`. For a failure, the enclosing `Cause` is reduced:\n *\n * - a `Cause.die` becomes a `Defect`,\n * - otherwise a `Cause.fail` becomes the modeled `Err`,\n * - a pure interruption (or empty cause) becomes a `Defect`.\n *\n * A `Defect` **dominates** a modeled failure in a composite cause — the same\n * rule unthrown's `all` uses, on the principle that an unexpected failure is the\n * more severe signal.\n *\n * @typeParam T - the success value type.\n * @typeParam E - the modeled error type.\n * @param exit - the exit to convert.\n */\nexport function fromExit<T, E>(exit: Exit.Exit<T, E>): Result<T, E> {\n return Exit.match(exit, {\n onSuccess: (value) => ok(value),\n onFailure: (cause) => {\n const die = Cause.dieOption(cause);\n if (Option.isSome(die)) return dieToResult<T, E>(die.value);\n const failure = Cause.failureOption(cause);\n if (Option.isSome(failure)) return err(failure.value);\n // No modeled failure and no die: a pure interruption (or empty cause).\n return dieToResult<T, E>(Cause.squash(cause));\n },\n });\n}\n\n/**\n * Convert a `Result` into an Effect `Either`, triaging any defect.\n *\n * @remarks\n * `Either` has no defect channel, so a `Defect` cannot pass through silently —\n * `onDefect` **must** fold its cause into a modeled error `E` (a `Left`). This\n * is the boundary-qualification rule (Thesis #3) applied on the way out:\n * `Ok → Right`, `Err → Left`, `Defect → Left(onDefect(cause))`.\n *\n * @typeParam T - the success value type.\n * @typeParam E - the modeled error type.\n * @param result - the result to convert.\n * @param onDefect - folds a defect's unknown cause into a modeled `E`.\n */\nexport function toEither<T, E>(\n result: Result<T, E>,\n onDefect: (cause: unknown) => E,\n): Either.Either<T, E> {\n return result.match<Either.Either<T, E>>({\n ok: (value) => Either.right(value),\n err: (error) => Either.left(error),\n defect: (cause) => Either.left(onDefect(cause)),\n });\n}\n\n/**\n * Convert an Effect `Either` into a `Result`.\n *\n * @remarks\n * `Right → Ok`, `Left → Err`. An `Either` carries no defect, so the result is\n * never a `Defect`.\n *\n * @typeParam T - the success value type.\n * @typeParam E - the modeled error type.\n * @param either - the either to convert.\n */\nexport function fromEither<T, E>(either: Either.Either<T, E>): Result<T, E> {\n return Either.match(either, {\n onLeft: (error) => err(error),\n onRight: (value) => ok(value),\n });\n}\n\n/**\n * Lift a `Result` or `AsyncResult` into an `Effect`.\n *\n * @remarks\n * `Ok → Effect.succeed`, `Err → Effect.fail`, `Defect → Effect.die`. The\n * resulting `Effect` needs no environment (`R = never`). An `AsyncResult` is\n * awaited inside the effect (it never rejects), so this is the `AsyncResult →\n * Effect` direction too.\n *\n * @typeParam T - the success value type.\n * @typeParam E - the modeled error type.\n * @param source - the result, or async result, to lift.\n */\nexport function toEffect<T, E>(source: Result<T, E>): Effect.Effect<T, E>;\nexport function toEffect<T, E>(source: AsyncResult<T, E>): Effect.Effect<T, E>;\nexport function toEffect<T, E>(source: Result<T, E> | AsyncResult<T, E>): Effect.Effect<T, E> {\n if (isAsyncResult(source)) {\n return Effect.flatMap(\n Effect.promise(() => settle(source)),\n (result) => resultToEffect(result),\n );\n }\n return resultToEffect(source);\n}\n\n/**\n * Run an `Effect` and collect its outcome as an `AsyncResult`.\n *\n * @remarks\n * The effect must need no environment (`R = never`). It is run to an `Exit`\n * (which never rejects), then folded with {@link fromExit}: success → `Ok`, a\n * modeled failure → `Err`, a die/interruption → `Defect`. The returned\n * `AsyncResult` never throws when awaited.\n *\n * @typeParam T - the success value type.\n * @typeParam E - the modeled error type.\n * @param effect - the effect to run.\n */\nexport function fromEffect<T, E>(effect: Effect.Effect<T, E>): AsyncResult<T, E> {\n return fromSafePromise(Effect.runPromiseExit(effect)).flatMap((exit) => fromExit(exit));\n}\n\nfunction resultToEffect<T, E>(result: Result<T, E>): Effect.Effect<T, E> {\n return result.match<Effect.Effect<T, E>>({\n ok: (value) => Effect.succeed(value),\n err: (error) => Effect.fail(error),\n defect: (cause) => Effect.die(cause),\n });\n}\n\n// Effect's `die`/interruption channel is an un-triaged failure crossing into\n// unthrown; replaying it through the throwable boundary lands it in the `Defect`\n// state — the sanctioned (boundary-only) way to mint a defect `Result`.\nfunction dieToResult<T, E>(cause: unknown): Result<T, E> {\n return fromThrowable<[], never, never>((): never => {\n throw cause;\n }, defect)();\n}\n\nfunction settle<T, E>(asyncResult: AsyncResult<T, E>): Promise<Result<T, E>> {\n return (async () => await asyncResult)();\n}\n\nfunction isAsyncResult<T, E>(\n source: Result<T, E> | AsyncResult<T, E>,\n): source is AsyncResult<T, E> {\n return typeof (source as { then?: unknown }).then === \"function\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,OAAa,QAAuC;CAClE,OAAO,OAAO,MAAuB;EACnC,KAAK,UAAU,KAAK,QAAQ,KAAK;EACjC,MAAM,UAAU,KAAK,KAAK,KAAK;EAC/B,SAAS,UAAU,KAAK,IAAI,KAAK;CACnC,CAAC;AACH;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,SAAe,MAAqC;CAClE,OAAO,KAAK,MAAM,MAAM;EACtB,YAAY,UAAU,GAAG,KAAK;EAC9B,YAAY,UAAU;GACpB,MAAM,MAAM,MAAM,UAAU,KAAK;GACjC,IAAI,OAAO,OAAO,GAAG,GAAG,OAAO,YAAkB,IAAI,KAAK;GAC1D,MAAM,UAAU,MAAM,cAAc,KAAK;GACzC,IAAI,OAAO,OAAO,OAAO,GAAG,OAAO,IAAI,QAAQ,KAAK;GAEpD,OAAO,YAAkB,MAAM,OAAO,KAAK,CAAC;EAC9C;CACF,CAAC;AACH;;;;;;;;;;;;;;;AAgBA,SAAgB,SACd,QACA,UACqB;CACrB,OAAO,OAAO,MAA2B;EACvC,KAAK,UAAU,OAAO,MAAM,KAAK;EACjC,MAAM,UAAU,OAAO,KAAK,KAAK;EACjC,SAAS,UAAU,OAAO,KAAK,SAAS,KAAK,CAAC;CAChD,CAAC;AACH;;;;;;;;;;;;AAaA,SAAgB,WAAiB,QAA2C;CAC1E,OAAO,OAAO,MAAM,QAAQ;EAC1B,SAAS,UAAU,IAAI,KAAK;EAC5B,UAAU,UAAU,GAAG,KAAK;CAC9B,CAAC;AACH;AAiBA,SAAgB,SAAe,QAA+D;CAC5F,IAAI,cAAc,MAAM,GACtB,OAAO,OAAO,QACZ,OAAO,cAAc,OAAO,MAAM,CAAC,IAClC,WAAW,eAAe,MAAM,CACnC;CAEF,OAAO,eAAe,MAAM;AAC9B;;;;;;;;;;;;;;AAeA,SAAgB,WAAiB,QAAgD;CAC/E,OAAO,gBAAgB,OAAO,eAAe,MAAM,CAAC,CAAC,CAAC,SAAS,SAAS,SAAS,IAAI,CAAC;AACxF;AAEA,SAAS,eAAqB,QAA2C;CACvE,OAAO,OAAO,MAA2B;EACvC,KAAK,UAAU,OAAO,QAAQ,KAAK;EACnC,MAAM,UAAU,OAAO,KAAK,KAAK;EACjC,SAAS,UAAU,OAAO,IAAI,KAAK;CACrC,CAAC;AACH;AAKA,SAAS,YAAkB,OAA8B;CACvD,OAAO,oBAA6C;EAClD,MAAM;CACR,GAAG,MAAM,CAAC,CAAC;AACb;AAEA,SAAS,OAAa,aAAuD;CAC3E,QAAQ,YAAY,MAAM,YAAA,CAAa;AACzC;AAEA,SAAS,cACP,QAC6B;CAC7B,OAAO,OAAQ,OAA8B,SAAS;AACxD"}
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
**@unthrown/effect**
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
# @unthrown/effect
|
|
6
|
+
|
|
7
|
+
## Functions
|
|
8
|
+
|
|
9
|
+
### fromEffect()
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
function fromEffect<T, E>(effect): AsyncResult<T, E>;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Defined in: index.ts:165
|
|
16
|
+
|
|
17
|
+
Run an `Effect` and collect its outcome as an `AsyncResult`.
|
|
18
|
+
|
|
19
|
+
#### Type Parameters
|
|
20
|
+
|
|
21
|
+
| Type Parameter | Description |
|
|
22
|
+
| ------ | ------ |
|
|
23
|
+
| `T` | the success value type. |
|
|
24
|
+
| `E` | the modeled error type. |
|
|
25
|
+
|
|
26
|
+
#### Parameters
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Description |
|
|
29
|
+
| ------ | ------ | ------ |
|
|
30
|
+
| `effect` | `Effect`<`T`, `E`> | the effect to run. |
|
|
31
|
+
|
|
32
|
+
#### Returns
|
|
33
|
+
|
|
34
|
+
`AsyncResult`<`T`, `E`>
|
|
35
|
+
|
|
36
|
+
#### Remarks
|
|
37
|
+
|
|
38
|
+
The effect must need no environment (`R = never`). It is run to an `Exit`
|
|
39
|
+
(which never rejects), then folded with [fromExit](#fromexit): success → `Ok`, a
|
|
40
|
+
modeled failure → `Err`, a die/interruption → `Defect`. The returned
|
|
41
|
+
`AsyncResult` never throws when awaited.
|
|
42
|
+
|
|
43
|
+
***
|
|
44
|
+
|
|
45
|
+
### fromEither()
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
function fromEither<T, E>(either): Result<T, E>;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Defined in: index.ts:120
|
|
52
|
+
|
|
53
|
+
Convert an Effect `Either` into a `Result`.
|
|
54
|
+
|
|
55
|
+
#### Type Parameters
|
|
56
|
+
|
|
57
|
+
| Type Parameter | Description |
|
|
58
|
+
| ------ | ------ |
|
|
59
|
+
| `T` | the success value type. |
|
|
60
|
+
| `E` | the modeled error type. |
|
|
61
|
+
|
|
62
|
+
#### Parameters
|
|
63
|
+
|
|
64
|
+
| Parameter | Type | Description |
|
|
65
|
+
| ------ | ------ | ------ |
|
|
66
|
+
| `either` | `Either`<`T`, `E`> | the either to convert. |
|
|
67
|
+
|
|
68
|
+
#### Returns
|
|
69
|
+
|
|
70
|
+
`Result`<`T`, `E`>
|
|
71
|
+
|
|
72
|
+
#### Remarks
|
|
73
|
+
|
|
74
|
+
`Right → Ok`, `Left → Err`. An `Either` carries no defect, so the result is
|
|
75
|
+
never a `Defect`.
|
|
76
|
+
|
|
77
|
+
***
|
|
78
|
+
|
|
79
|
+
### fromExit()
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
function fromExit<T, E>(exit): Result<T, E>;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Defined in: index.ts:70
|
|
86
|
+
|
|
87
|
+
Convert an Effect `Exit` into a `Result` — the inverse of
|
|
88
|
+
[toExit](#toexit).
|
|
89
|
+
|
|
90
|
+
#### Type Parameters
|
|
91
|
+
|
|
92
|
+
| Type Parameter | Description |
|
|
93
|
+
| ------ | ------ |
|
|
94
|
+
| `T` | the success value type. |
|
|
95
|
+
| `E` | the modeled error type. |
|
|
96
|
+
|
|
97
|
+
#### Parameters
|
|
98
|
+
|
|
99
|
+
| Parameter | Type | Description |
|
|
100
|
+
| ------ | ------ | ------ |
|
|
101
|
+
| `exit` | `Exit`<`T`, `E`> | the exit to convert. |
|
|
102
|
+
|
|
103
|
+
#### Returns
|
|
104
|
+
|
|
105
|
+
`Result`<`T`, `E`>
|
|
106
|
+
|
|
107
|
+
#### Remarks
|
|
108
|
+
|
|
109
|
+
`Exit.Success → Ok`. For a failure, the enclosing `Cause` is reduced:
|
|
110
|
+
|
|
111
|
+
- a `Cause.die` becomes a `Defect`,
|
|
112
|
+
- otherwise a `Cause.fail` becomes the modeled `Err`,
|
|
113
|
+
- a pure interruption (or empty cause) becomes a `Defect`.
|
|
114
|
+
|
|
115
|
+
A `Defect` **dominates** a modeled failure in a composite cause — the same
|
|
116
|
+
rule unthrown's `all` uses, on the principle that an unexpected failure is the
|
|
117
|
+
more severe signal.
|
|
118
|
+
|
|
119
|
+
***
|
|
120
|
+
|
|
121
|
+
### toEffect()
|
|
122
|
+
|
|
123
|
+
#### Call Signature
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
function toEffect<T, E>(source): Effect<T, E>;
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Defined in: index.ts:140
|
|
130
|
+
|
|
131
|
+
Lift a `Result` or `AsyncResult` into an `Effect`.
|
|
132
|
+
|
|
133
|
+
##### Type Parameters
|
|
134
|
+
|
|
135
|
+
| Type Parameter | Description |
|
|
136
|
+
| ------ | ------ |
|
|
137
|
+
| `T` | the success value type. |
|
|
138
|
+
| `E` | the modeled error type. |
|
|
139
|
+
|
|
140
|
+
##### Parameters
|
|
141
|
+
|
|
142
|
+
| Parameter | Type | Description |
|
|
143
|
+
| ------ | ------ | ------ |
|
|
144
|
+
| `source` | `Result`<`T`, `E`> | the result, or async result, to lift. |
|
|
145
|
+
|
|
146
|
+
##### Returns
|
|
147
|
+
|
|
148
|
+
`Effect`<`T`, `E`>
|
|
149
|
+
|
|
150
|
+
##### Remarks
|
|
151
|
+
|
|
152
|
+
`Ok → Effect.succeed`, `Err → Effect.fail`, `Defect → Effect.die`. The
|
|
153
|
+
resulting `Effect` needs no environment (`R = never`). An `AsyncResult` is
|
|
154
|
+
awaited inside the effect (it never rejects), so this is the `AsyncResult →
|
|
155
|
+
Effect` direction too.
|
|
156
|
+
|
|
157
|
+
#### Call Signature
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
function toEffect<T, E>(source): Effect<T, E>;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Defined in: index.ts:141
|
|
164
|
+
|
|
165
|
+
Lift a `Result` or `AsyncResult` into an `Effect`.
|
|
166
|
+
|
|
167
|
+
##### Type Parameters
|
|
168
|
+
|
|
169
|
+
| Type Parameter | Description |
|
|
170
|
+
| ------ | ------ |
|
|
171
|
+
| `T` | the success value type. |
|
|
172
|
+
| `E` | the modeled error type. |
|
|
173
|
+
|
|
174
|
+
##### Parameters
|
|
175
|
+
|
|
176
|
+
| Parameter | Type | Description |
|
|
177
|
+
| ------ | ------ | ------ |
|
|
178
|
+
| `source` | `AsyncResult`<`T`, `E`> | the result, or async result, to lift. |
|
|
179
|
+
|
|
180
|
+
##### Returns
|
|
181
|
+
|
|
182
|
+
`Effect`<`T`, `E`>
|
|
183
|
+
|
|
184
|
+
##### Remarks
|
|
185
|
+
|
|
186
|
+
`Ok → Effect.succeed`, `Err → Effect.fail`, `Defect → Effect.die`. The
|
|
187
|
+
resulting `Effect` needs no environment (`R = never`). An `AsyncResult` is
|
|
188
|
+
awaited inside the effect (it never rejects), so this is the `AsyncResult →
|
|
189
|
+
Effect` direction too.
|
|
190
|
+
|
|
191
|
+
***
|
|
192
|
+
|
|
193
|
+
### toEither()
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
function toEither<T, E>(result, onDefect): Either<T, E>;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Defined in: index.ts:98
|
|
200
|
+
|
|
201
|
+
Convert a `Result` into an Effect `Either`, triaging any defect.
|
|
202
|
+
|
|
203
|
+
#### Type Parameters
|
|
204
|
+
|
|
205
|
+
| Type Parameter | Description |
|
|
206
|
+
| ------ | ------ |
|
|
207
|
+
| `T` | the success value type. |
|
|
208
|
+
| `E` | the modeled error type. |
|
|
209
|
+
|
|
210
|
+
#### Parameters
|
|
211
|
+
|
|
212
|
+
| Parameter | Type | Description |
|
|
213
|
+
| ------ | ------ | ------ |
|
|
214
|
+
| `result` | `Result`<`T`, `E`> | the result to convert. |
|
|
215
|
+
| `onDefect` | (`cause`) => `E` | folds a defect's unknown cause into a modeled `E`. |
|
|
216
|
+
|
|
217
|
+
#### Returns
|
|
218
|
+
|
|
219
|
+
`Either`<`T`, `E`>
|
|
220
|
+
|
|
221
|
+
#### Remarks
|
|
222
|
+
|
|
223
|
+
`Either` has no defect channel, so a `Defect` cannot pass through silently —
|
|
224
|
+
`onDefect` **must** fold its cause into a modeled error `E` (a `Left`). This
|
|
225
|
+
is the boundary-qualification rule (Thesis #3) applied on the way out:
|
|
226
|
+
`Ok → Right`, `Err → Left`, `Defect → Left(onDefect(cause))`.
|
|
227
|
+
|
|
228
|
+
***
|
|
229
|
+
|
|
230
|
+
### toExit()
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
function toExit<T, E>(result): Exit<T, E>;
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Defined in: index.ts:43
|
|
237
|
+
|
|
238
|
+
Convert a `Result` into an Effect `Exit` — a **bijection**, since both
|
|
239
|
+
carry three channels.
|
|
240
|
+
|
|
241
|
+
#### Type Parameters
|
|
242
|
+
|
|
243
|
+
| Type Parameter | Description |
|
|
244
|
+
| ------ | ------ |
|
|
245
|
+
| `T` | the success value type. |
|
|
246
|
+
| `E` | the modeled error type. |
|
|
247
|
+
|
|
248
|
+
#### Parameters
|
|
249
|
+
|
|
250
|
+
| Parameter | Type | Description |
|
|
251
|
+
| ------ | ------ | ------ |
|
|
252
|
+
| `result` | `Result`<`T`, `E`> | the result to convert. |
|
|
253
|
+
|
|
254
|
+
#### Returns
|
|
255
|
+
|
|
256
|
+
`Exit`<`T`, `E`>
|
|
257
|
+
|
|
258
|
+
#### Remarks
|
|
259
|
+
|
|
260
|
+
`Ok → Exit.succeed`, `Err → Exit.fail` (a modeled `Cause.fail`), and
|
|
261
|
+
`Defect → Exit.die` (an unexpected `Cause.die`). Round-trips with
|
|
262
|
+
[fromExit](#fromexit).
|
|
263
|
+
|
|
264
|
+
#### Example
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
import { ok } from "unthrown";
|
|
268
|
+
import { toExit } from "@unthrown/effect";
|
|
269
|
+
toExit(ok(1)); // Exit.succeed(1)
|
|
270
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unthrown/effect",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Effect interop for unthrown",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"effect",
|
|
7
|
+
"either",
|
|
8
|
+
"errors-as-values",
|
|
9
|
+
"exit",
|
|
10
|
+
"result",
|
|
11
|
+
"typescript",
|
|
12
|
+
"unthrown"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/btravstack/unthrown#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/btravstack/unthrown/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Benoit TRAVERS <benoit.travers.fr@gmail.com>",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/btravstack/unthrown.git",
|
|
23
|
+
"directory": "packages/effect"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"docs"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"module": "./dist/index.mjs",
|
|
32
|
+
"types": "./dist/index.d.mts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"import": {
|
|
36
|
+
"types": "./dist/index.d.mts",
|
|
37
|
+
"default": "./dist/index.mjs"
|
|
38
|
+
},
|
|
39
|
+
"require": {
|
|
40
|
+
"types": "./dist/index.d.cts",
|
|
41
|
+
"default": "./dist/index.cjs"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"unthrown": "0.1.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "24.13.2",
|
|
51
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
52
|
+
"effect": "3.21.4",
|
|
53
|
+
"tsdown": "0.22.2",
|
|
54
|
+
"typedoc": "0.28.19",
|
|
55
|
+
"typedoc-plugin-markdown": "4.12.0",
|
|
56
|
+
"typescript": "6.0.3",
|
|
57
|
+
"vitest": "4.1.8",
|
|
58
|
+
"@unthrown/typedoc": "0.1.0",
|
|
59
|
+
"@unthrown/tsconfig": "0.1.0"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"effect": "^3"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=22.19"
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"build": "tsdown src/index.ts --format cjs,esm --dts --clean",
|
|
69
|
+
"build:docs": "typedoc",
|
|
70
|
+
"dev": "tsdown src/index.ts --format cjs,esm --dts --watch",
|
|
71
|
+
"test": "vitest run",
|
|
72
|
+
"typecheck": "tsc --noEmit"
|
|
73
|
+
}
|
|
74
|
+
}
|