ripthrow 2.1.0 → 2.1.1
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/package.json +1 -1
- package/src/result-builder-async.ts +74 -28
- package/src/utils/index.ts +1 -0
- package/src/utils/pipe.ts +25 -0
package/package.json
CHANGED
|
@@ -14,11 +14,36 @@ import { tapErr } from "./operators/tap-err";
|
|
|
14
14
|
import type { Report } from "./report";
|
|
15
15
|
import type { AsyncResult, Result } from "./types/result";
|
|
16
16
|
|
|
17
|
+
type Op = (
|
|
18
|
+
r: Result<unknown, unknown>,
|
|
19
|
+
) => Result<unknown, unknown> | Promise<Result<unknown, unknown>>;
|
|
20
|
+
|
|
17
21
|
export class AsyncResultBuilder<T, E> {
|
|
18
22
|
private readonly _promise: AsyncResult<T, E>;
|
|
23
|
+
private readonly _ops: Op[];
|
|
24
|
+
private _executed: AsyncResult<T, E> | null = null;
|
|
19
25
|
|
|
20
|
-
constructor(promise: AsyncResult<T, E
|
|
26
|
+
constructor(promise: AsyncResult<T, E>, ops?: Op[]) {
|
|
21
27
|
this._promise = promise;
|
|
28
|
+
this._ops = ops ?? [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private execute(): AsyncResult<T, E> {
|
|
32
|
+
if (this._executed) {
|
|
33
|
+
return this._executed;
|
|
34
|
+
}
|
|
35
|
+
if (this._ops.length === 0) {
|
|
36
|
+
this._executed = this._promise;
|
|
37
|
+
} else {
|
|
38
|
+
this._executed = this._promise.then(async (r) => {
|
|
39
|
+
let current: Result<unknown, unknown> = r;
|
|
40
|
+
for (const op of this._ops) {
|
|
41
|
+
current = await op(current);
|
|
42
|
+
}
|
|
43
|
+
return current as Result<T, E>;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return this._executed;
|
|
22
47
|
}
|
|
23
48
|
|
|
24
49
|
static ok<U = void, F = unknown>(value?: U): AsyncResultBuilder<U, F> {
|
|
@@ -70,53 +95,69 @@ export class AsyncResultBuilder<T, E> {
|
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
get result(): AsyncResult<T, E> {
|
|
73
|
-
return this.
|
|
98
|
+
return this.execute();
|
|
74
99
|
}
|
|
75
100
|
|
|
76
101
|
get isOk(): Promise<boolean> {
|
|
77
|
-
return this.
|
|
102
|
+
return this.execute().then(isOk);
|
|
78
103
|
}
|
|
79
104
|
|
|
80
105
|
get isErr(): Promise<boolean> {
|
|
81
|
-
return this.
|
|
106
|
+
return this.execute().then(isErr);
|
|
82
107
|
}
|
|
83
108
|
|
|
84
109
|
map<R>(fn: (value: T) => R): AsyncResultBuilder<R, E> {
|
|
85
|
-
|
|
110
|
+
const op: Op = (r: Result<unknown, unknown>) => map(r as Result<T, E>, fn);
|
|
111
|
+
return new AsyncResultBuilder<R, E>(this._promise as unknown as AsyncResult<R, E>, [
|
|
112
|
+
...this._ops,
|
|
113
|
+
op,
|
|
114
|
+
]);
|
|
86
115
|
}
|
|
87
116
|
|
|
88
117
|
mapErr<F>(fn: (error: E) => F): AsyncResultBuilder<T, F> {
|
|
89
|
-
|
|
118
|
+
const op: Op = (r: Result<unknown, unknown>) => mapErr(r as Result<T, E>, fn);
|
|
119
|
+
return new AsyncResultBuilder<T, F>(this._promise as unknown as AsyncResult<T, F>, [
|
|
120
|
+
...this._ops,
|
|
121
|
+
op,
|
|
122
|
+
]);
|
|
90
123
|
}
|
|
91
124
|
|
|
92
125
|
andThen<R>(fn: (value: T) => Result<R, E> | AsyncResult<R, E>): AsyncResultBuilder<R, E> {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
126
|
+
const op: Op = (r: Result<unknown, unknown>) => {
|
|
127
|
+
const res = r as Result<T, E>;
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
return res;
|
|
130
|
+
}
|
|
131
|
+
return fn(res.value);
|
|
132
|
+
};
|
|
133
|
+
return new AsyncResultBuilder<R, E>(this._promise as unknown as AsyncResult<R, E>, [
|
|
134
|
+
...this._ops,
|
|
135
|
+
op,
|
|
136
|
+
]);
|
|
101
137
|
}
|
|
102
138
|
|
|
103
139
|
orElse<F>(fn: (error: E) => Result<T, F> | AsyncResult<T, F>): AsyncResultBuilder<T, F> {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
140
|
+
const op: Op = (r: Result<unknown, unknown>) => {
|
|
141
|
+
const res = r as Result<T, E>;
|
|
142
|
+
if (res.ok) {
|
|
143
|
+
return res;
|
|
144
|
+
}
|
|
145
|
+
return fn(res.error);
|
|
146
|
+
};
|
|
147
|
+
return new AsyncResultBuilder<T, F>(this._promise as unknown as AsyncResult<T, F>, [
|
|
148
|
+
...this._ops,
|
|
149
|
+
op,
|
|
150
|
+
]);
|
|
112
151
|
}
|
|
113
152
|
|
|
114
153
|
tap(fn: (value: T) => void): AsyncResultBuilder<T, E> {
|
|
115
|
-
|
|
154
|
+
const op: Op = (r: Result<unknown, unknown>) => tap(r as Result<T, E>, fn);
|
|
155
|
+
return new AsyncResultBuilder<T, E>(this._promise, [...this._ops, op]);
|
|
116
156
|
}
|
|
117
157
|
|
|
118
158
|
tapErr(fn: (error: E) => void): AsyncResultBuilder<T, E> {
|
|
119
|
-
|
|
159
|
+
const op: Op = (r: Result<unknown, unknown>) => tapErr(r as Result<T, E>, fn);
|
|
160
|
+
return new AsyncResultBuilder<T, E>(this._promise, [...this._ops, op]);
|
|
120
161
|
}
|
|
121
162
|
|
|
122
163
|
context(
|
|
@@ -124,19 +165,24 @@ export class AsyncResultBuilder<T, E> {
|
|
|
124
165
|
help?: string,
|
|
125
166
|
meta?: Record<string, unknown>,
|
|
126
167
|
): AsyncResultBuilder<T, Report> {
|
|
127
|
-
|
|
168
|
+
const op: Op = (r: Result<unknown, unknown>) =>
|
|
169
|
+
contextOp(r as Result<T, E>, message, help, meta);
|
|
170
|
+
return new AsyncResultBuilder<T, Report>(this._promise as unknown as AsyncResult<T, Report>, [
|
|
171
|
+
...this._ops,
|
|
172
|
+
op,
|
|
173
|
+
]);
|
|
128
174
|
}
|
|
129
175
|
|
|
130
176
|
match<R>(handlers: { ok: (value: T) => R; err: (error: E) => R }): Promise<R> {
|
|
131
|
-
return this.
|
|
177
|
+
return this.execute().then((r) => match(r, handlers));
|
|
132
178
|
}
|
|
133
179
|
|
|
134
180
|
unwrapOr(defaultValue: T): Promise<T> {
|
|
135
|
-
return this.
|
|
181
|
+
return this.execute().then((r) => unwrapOr(r, defaultValue));
|
|
136
182
|
}
|
|
137
183
|
|
|
138
184
|
unwrap(): Promise<T> {
|
|
139
|
-
return this.
|
|
185
|
+
return this.execute().then((r) => unwrap(r));
|
|
140
186
|
}
|
|
141
187
|
}
|
|
142
188
|
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipes a value through a series of transform functions.
|
|
3
|
+
* Accepts both sync and Promise values — awaits the input if needed.
|
|
4
|
+
*
|
|
5
|
+
* @category Utilities
|
|
6
|
+
* @example
|
|
7
|
+
* const result = await pipe(
|
|
8
|
+
* safe(() => JSON.parse(input)),
|
|
9
|
+
* (r) => map(r, (data: any) => data.a),
|
|
10
|
+
* (r) => unwrapOr(r, 0),
|
|
11
|
+
* );
|
|
12
|
+
*/
|
|
13
|
+
// biome-ignore lint/suspicious/noExplicitAny: variadic pipe needs flexibility
|
|
14
|
+
export async function pipe<T, Fns extends Array<(arg: any) => any>>(
|
|
15
|
+
value: T | Promise<T>,
|
|
16
|
+
...fns: Fns
|
|
17
|
+
// biome-ignore lint/suspicious/noExplicitAny: conditional type inference
|
|
18
|
+
): Promise<Fns extends [...any[], infer Last] ? (Last extends (arg: any) => infer R ? R : T) : T> {
|
|
19
|
+
let acc: unknown = await value;
|
|
20
|
+
for (const fn of fns) {
|
|
21
|
+
// biome-ignore lint/suspicious/noExplicitAny: variadic pipe
|
|
22
|
+
acc = (fn as any)(acc);
|
|
23
|
+
}
|
|
24
|
+
return acc as never;
|
|
25
|
+
}
|