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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ripthrow",
3
3
  "module": "src/index.ts",
4
- "version": "2.1.0",
4
+ "version": "2.1.1",
5
5
  "description": "Zero-overhead, type-safe error handling for TypeScript.",
6
6
  "keywords": [
7
7
  "result",
@@ -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._promise;
98
+ return this.execute();
74
99
  }
75
100
 
76
101
  get isOk(): Promise<boolean> {
77
- return this._promise.then(isOk);
102
+ return this.execute().then(isOk);
78
103
  }
79
104
 
80
105
  get isErr(): Promise<boolean> {
81
- return this._promise.then(isErr);
106
+ return this.execute().then(isErr);
82
107
  }
83
108
 
84
109
  map<R>(fn: (value: T) => R): AsyncResultBuilder<R, E> {
85
- return new AsyncResultBuilder(this._promise.then((r) => map(r, fn)));
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
- return new AsyncResultBuilder(this._promise.then((r) => mapErr(r, fn)));
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
- return new AsyncResultBuilder(
94
- this._promise.then((r) => {
95
- if (!r.ok) {
96
- return r;
97
- }
98
- return fn(r.value);
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
- return new AsyncResultBuilder(
105
- this._promise.then((r) => {
106
- if (r.ok) {
107
- return r;
108
- }
109
- return fn(r.error);
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
- return new AsyncResultBuilder(this._promise.then((r) => tap(r, fn)));
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
- return new AsyncResultBuilder(this._promise.then((r) => tapErr(r, fn)));
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
- return new AsyncResultBuilder(this._promise.then((r) => contextOp(r, message, help, meta)));
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._promise.then((r) => match(r, handlers));
177
+ return this.execute().then((r) => match(r, handlers));
132
178
  }
133
179
 
134
180
  unwrapOr(defaultValue: T): Promise<T> {
135
- return this._promise.then((r) => unwrapOr(r, defaultValue));
181
+ return this.execute().then((r) => unwrapOr(r, defaultValue));
136
182
  }
137
183
 
138
184
  unwrap(): Promise<T> {
139
- return this._promise.then((r) => unwrap(r));
185
+ return this.execute().then((r) => unwrap(r));
140
186
  }
141
187
  }
142
188
 
@@ -1,3 +1,4 @@
1
1
  export { all } from "./all";
2
2
  export { any } from "./any";
3
3
  export { kindOf } from "./kind-of";
4
+ export { pipe } from "./pipe";
@@ -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
+ }