zresult 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # 1.0.0 (2026-04-13)
2
+
3
+
4
+ ### Features
5
+
6
+ * first working version ([fa0de11](https://github.com/nfroidure/zresult/commit/fa0de110c7281aa56b5388d0b1b6cbd883b36606))
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+ Copyright © 2017 Nicolas Froidure
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the “Software”), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in
12
+ all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,218 @@
1
+ [//]: # ( )
2
+ [//]: # (This file is automatically generated by a `metapak`)
3
+ [//]: # (module. Do not change it except between the)
4
+ [//]: # (`content:start/end` flags, your changes would)
5
+ [//]: # (be overridden.)
6
+ [//]: # ( )
7
+ # zresult
8
+ > It helps to know the error you'll get.
9
+
10
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nfroidure/zresult/blob/main/LICENSE)
11
+
12
+
13
+ [//]: # (::contents:start)
14
+
15
+ `zresult` is an ultra-lightweight TypeScript wrapper designed to transform error
16
+ handling into a predictable, strictly typed, and elegant data flow. Inspired by
17
+ the `Result` pattern from Rust and Go, it integrates natively with `yerror` to
18
+ provide absolute **Type Safety** for your error codes.
19
+
20
+ ## Key Features
21
+
22
+ - ✅ **No more noisy `try/catch`**: Handle errors as return values.
23
+ - ✅ **Exhaustive Type Safety**: Your error codes and debug data are validated
24
+ via the [yerror](https://www.npmjs.com/package/yerror)'s `YErrorRegistry`
25
+ type.
26
+ - ✅ **Explicit Flow**: Clear distinction between synchronous and asynchronous
27
+ operations.
28
+ - ✅ **Selective Catching**: Capture only the errors you know how to handle, let
29
+ the others bubble up (e.g., unexpected bugs or `ReferenceError`).
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ npm install zresult yerror
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ### 1. Creating Success or Failure
40
+
41
+ ```ts
42
+ import { ok, fail } from 'zresult';
43
+
44
+ const success = ok(42);
45
+ // returns { ok: true, value: 42 }
46
+
47
+ const failure = fail('E_USER_NOT_FOUND', ['123']);
48
+ // returns { ok: false, error: YError }
49
+ ```
50
+
51
+ ### 2. Executing a Function (Attempt)
52
+
53
+ Use attempt for asynchronous functions and attemptSync for the synchronous
54
+ world.
55
+
56
+ ```ts
57
+ import { attempt, attemptSync } from 'zresult';
58
+
59
+ // Asynchronous
60
+ const result = await attempt(fetchUser, userId);
61
+
62
+ if (!result.ok) {
63
+ console.error(result.error.code); // Strictly typed!
64
+ return;
65
+ }
66
+ console.log(result.value.name);
67
+
68
+ // Synchronous
69
+ const config = attemptSync(parseConfig, rawData);
70
+ ```
71
+
72
+ ### 3. Selective Capture (The "From" Pattern)
73
+
74
+ Only handle expected errors and let unexpected bugs crash the process cleanly.
75
+
76
+ ```ts
77
+ import { attemptFrom } from 'zresult';
78
+
79
+ // We only capture 'E_NETWORK' errors
80
+ const result = await attemptFrom(['E_NETWORK'], sendMessage, msg);
81
+
82
+ if (!result.ok) {
83
+ // result.error is strictly typed as YError<'E_NETWORK'> here
84
+ return retry(msg);
85
+ }
86
+ ```
87
+
88
+ ### 4. Stabilizing an existing Promise (Settle)
89
+
90
+ Useful for transforming a Promise from a third-party library into a Result.
91
+
92
+ ```ts
93
+ import { settle } from 'zresult';
94
+
95
+ const result = await settle(externalApi.call());
96
+ ```
97
+
98
+ ### Integration with YError
99
+
100
+ `zresult` shines when used with a declared `YErrorRegistry`. TypeScript will
101
+ prevent you from using non-existent error codes or invalid debug arguments.
102
+
103
+ ```ts
104
+ declare module 'yerror' {
105
+ interface YErrorRegistry {
106
+ E_ACCESS_DENIED: [userId: string];
107
+ }
108
+ }
109
+
110
+ // TypeScript validates both 'E_ACCESS_DENIED' and the debug array contents
111
+ return fail('E_ACCESS_DENIED', ['user_123']);
112
+ ```
113
+
114
+ [//]: # (::contents:end)
115
+
116
+ # API
117
+ ## Functions
118
+
119
+ <dl>
120
+ <dt><a href="#ok">ok()</a></dt>
121
+ <dd><p>Create a successful result</p>
122
+ </dd>
123
+ <dt><a href="#fail">fail()</a></dt>
124
+ <dd><p>Create a failure result using a YError code and debug values</p>
125
+ </dd>
126
+ <dt><a href="#failWith">failWith()</a></dt>
127
+ <dd><p>Wrap an existing error into a failure result</p>
128
+ </dd>
129
+ <dt><a href="#settle">settle()</a></dt>
130
+ <dd><p>Transform a Promise into a Result catching any error</p>
131
+ </dd>
132
+ <dt><a href="#settleFrom">settleFrom()</a></dt>
133
+ <dd><p>Transform a Promise into a Result catching only known
134
+ errors and letting unknown pass through</p>
135
+ </dd>
136
+ <dt><a href="#attempt">attempt()</a></dt>
137
+ <dd><p>Runs an asynchronous function and provides a Result catching
138
+ any error</p>
139
+ </dd>
140
+ <dt><a href="#attemptFrom">attemptFrom()</a></dt>
141
+ <dd><p>Runs an asynchronous function and provides a Result catching
142
+ only known errors and letting unknown pass through</p>
143
+ </dd>
144
+ <dt><a href="#attemptSync">attemptSync()</a></dt>
145
+ <dd><p>Runs a synchronous function and provides a Result catching
146
+ any error</p>
147
+ </dd>
148
+ <dt><a href="#attemptSyncFrom">attemptSyncFrom()</a></dt>
149
+ <dd><p>Runs a synchronous function and provides a Result catching
150
+ only known errors and letting unknown pass through</p>
151
+ </dd>
152
+ </dl>
153
+
154
+ <a name="ok"></a>
155
+
156
+ ## ok()
157
+ Create a successful result
158
+
159
+ **Kind**: global function
160
+ <a name="fail"></a>
161
+
162
+ ## fail()
163
+ Create a failure result using a YError code and debug values
164
+
165
+ **Kind**: global function
166
+ <a name="failWith"></a>
167
+
168
+ ## failWith()
169
+ Wrap an existing error into a failure result
170
+
171
+ **Kind**: global function
172
+ <a name="settle"></a>
173
+
174
+ ## settle()
175
+ Transform a Promise into a Result catching any error
176
+
177
+ **Kind**: global function
178
+ <a name="settleFrom"></a>
179
+
180
+ ## settleFrom()
181
+ Transform a Promise into a Result catching only known
182
+ errors and letting unknown pass through
183
+
184
+ **Kind**: global function
185
+ <a name="attempt"></a>
186
+
187
+ ## attempt()
188
+ Runs an asynchronous function and provides a Result catching
189
+ any error
190
+
191
+ **Kind**: global function
192
+ <a name="attemptFrom"></a>
193
+
194
+ ## attemptFrom()
195
+ Runs an asynchronous function and provides a Result catching
196
+ only known errors and letting unknown pass through
197
+
198
+ **Kind**: global function
199
+ <a name="attemptSync"></a>
200
+
201
+ ## attemptSync()
202
+ Runs a synchronous function and provides a Result catching
203
+ any error
204
+
205
+ **Kind**: global function
206
+ <a name="attemptSyncFrom"></a>
207
+
208
+ ## attemptSyncFrom()
209
+ Runs a synchronous function and provides a Result catching
210
+ only known errors and letting unknown pass through
211
+
212
+ **Kind**: global function
213
+
214
+ # Authors
215
+ - [Nicolas Froidure](http://insertafter.com/en/index.html)
216
+
217
+ # License
218
+ [MIT](https://github.com/nfroidure/zresult/blob/main/LICENSE)
@@ -0,0 +1,33 @@
1
+ import { type YErrorDebug, type YErrorRegistry, YError } from 'yerror';
2
+ /** A type to encapsulate a result that can either be
3
+ * failure or a success */
4
+ export type Result<T, E extends Error | YError> = {
5
+ readonly ok: true;
6
+ readonly value: T;
7
+ } | {
8
+ readonly ok: false;
9
+ readonly error: E;
10
+ };
11
+ /** Create a successful result */
12
+ export declare function ok<T>(value: T): Result<T, never>;
13
+ /** Create a failure result using a YError code and debug values */
14
+ export declare function fail<C extends keyof YErrorRegistry>(code: C, debug: YErrorDebug<C>, error?: Error): Result<never, YError<C>>;
15
+ /** Wrap an existing error into a failure result */
16
+ export declare function failWith<E extends Error | YError>(error: E): Result<never, E extends YError<infer C> ? YError<C> : YError<'E_UNEXPECTED'>>;
17
+ /** Transform a Promise into a Result catching any error */
18
+ export declare function settle<T>(promise: Promise<T>): Promise<Result<T, YError>>;
19
+ /** Transform a Promise into a Result catching only known
20
+ * errors and letting unknown pass through */
21
+ export declare function settleFrom<C extends keyof YErrorRegistry, T>(codes: C[], promise: Promise<T>): Promise<Result<T, YError<C>>>;
22
+ /** Runs an asynchronous function and provides a Result catching
23
+ * any error */
24
+ export declare function attempt<T, A extends any[]>(fn: (...args: A) => Promise<T>, ...args: A): Promise<Result<T, YError>>;
25
+ /** Runs an asynchronous function and provides a Result catching
26
+ * only known errors and letting unknown pass through */
27
+ export declare function attemptFrom<C extends keyof YErrorRegistry, T, A extends any[]>(codes: C[], fn: (...args: A) => Promise<T>, ...args: A): Promise<Result<T, YError<C>>>;
28
+ /** Runs a synchronous function and provides a Result catching
29
+ * any error */
30
+ export declare function attemptSync<T, A extends any[]>(fn: (...args: A) => T, ...args: A): Result<T, YError>;
31
+ /** Runs a synchronous function and provides a Result catching
32
+ * only known errors and letting unknown pass through */
33
+ export declare function attemptSyncFrom<C extends keyof YErrorRegistry, T, A extends any[]>(codes: C[], fn: (...args: A) => T, ...args: A): Result<T, YError<C>>;
package/dist/index.js ADDED
@@ -0,0 +1,101 @@
1
+ import { looksLikeAYError, YError, hasYErrorCode, } from 'yerror';
2
+ /** Create a successful result */
3
+ export function ok(value) {
4
+ return {
5
+ ok: true,
6
+ value,
7
+ };
8
+ }
9
+ /** Create a failure result using a YError code and debug values */
10
+ export function fail(code, debug, error) {
11
+ return {
12
+ ok: false,
13
+ error: new YError(code, debug, { cause: error }),
14
+ };
15
+ }
16
+ /** Wrap an existing error into a failure result */
17
+ export function failWith(error) {
18
+ return {
19
+ ok: false,
20
+ error: (looksLikeAYError(error)
21
+ ? error
22
+ : new YError('E_UNEXPECTED', [], { cause: error })),
23
+ };
24
+ }
25
+ /** Transform a Promise into a Result catching any error */
26
+ export async function settle(promise) {
27
+ try {
28
+ const value = await promise;
29
+ return ok(value);
30
+ }
31
+ catch (err) {
32
+ return failWith(YError.cast(err));
33
+ }
34
+ }
35
+ /** Transform a Promise into a Result catching only known
36
+ * errors and letting unknown pass through */
37
+ export async function settleFrom(codes, promise) {
38
+ try {
39
+ const value = await promise;
40
+ return ok(value);
41
+ }
42
+ catch (err) {
43
+ for (const code of codes) {
44
+ if (hasYErrorCode(err, code)) {
45
+ return failWith(err);
46
+ }
47
+ }
48
+ throw err;
49
+ }
50
+ }
51
+ /** Runs an asynchronous function and provides a Result catching
52
+ * any error */
53
+ export async function attempt(fn, ...args) {
54
+ try {
55
+ return ok(await fn(...args));
56
+ }
57
+ catch (err) {
58
+ return failWith(YError.cast(err));
59
+ }
60
+ }
61
+ /** Runs an asynchronous function and provides a Result catching
62
+ * only known errors and letting unknown pass through */
63
+ export async function attemptFrom(codes, fn, ...args) {
64
+ try {
65
+ return ok(await fn(...args));
66
+ }
67
+ catch (err) {
68
+ for (const code of codes) {
69
+ if (hasYErrorCode(err, code)) {
70
+ return failWith(err);
71
+ }
72
+ }
73
+ throw err;
74
+ }
75
+ }
76
+ /** Runs a synchronous function and provides a Result catching
77
+ * any error */
78
+ export function attemptSync(fn, ...args) {
79
+ try {
80
+ return ok(fn(...args));
81
+ }
82
+ catch (err) {
83
+ return failWith(YError.cast(err));
84
+ }
85
+ }
86
+ /** Runs a synchronous function and provides a Result catching
87
+ * only known errors and letting unknown pass through */
88
+ export function attemptSyncFrom(codes, fn, ...args) {
89
+ try {
90
+ return ok(fn(...args));
91
+ }
92
+ catch (err) {
93
+ for (const code of codes) {
94
+ if (hasYErrorCode(err, code)) {
95
+ return failWith(err);
96
+ }
97
+ }
98
+ throw err;
99
+ }
100
+ }
101
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ declare module 'yerror' {
2
+ interface YErrorRegistry {
3
+ E_ERROR: `arg${number}`[];
4
+ E_ERROR_1: [string, string];
5
+ E_ERROR_2: [`arg2.1`, `arg2.2`];
6
+ E_ERROR_3: [`arg3.1`, `arg3.2`];
7
+ }
8
+ }
9
+ export {};
@@ -0,0 +1,271 @@
1
+ import { describe, test, expect } from '@jest/globals';
2
+ import { ok, fail, failWith, settle, settleFrom, attempt, attemptFrom, attemptSync, attemptSyncFrom, } from './index.js';
3
+ import { YError } from 'yerror';
4
+ describe('zeerror', () => {
5
+ describe('ok()', () => {
6
+ test('should produce a result with a value', () => {
7
+ const result = ok({ my: 'result' });
8
+ if (result.ok) {
9
+ const value = result.value.my;
10
+ expect(value).toEqual('result');
11
+ // @ts-expect-error : expect error if type is casted
12
+ expect(result.value.not).toBeUndefined();
13
+ }
14
+ else {
15
+ // @ts-expect-error : expect error since no value
16
+ expect(result.value).toBeUndefined();
17
+ }
18
+ });
19
+ });
20
+ describe('fail()', () => {
21
+ test('should produce a result with an error', () => {
22
+ const error = new Error('E_AOUCH');
23
+ const result = fail('E_ERROR_2', ['arg2.1', 'arg2.2'], error);
24
+ if (result.ok) {
25
+ // @ts-expect-error : expect error since no error
26
+ expect(result.error).toBeUndefined();
27
+ }
28
+ else {
29
+ // @ts-expect-error : expect error since nothing at this index
30
+ expect(result.error.debug[2]).toBeUndefined();
31
+ expect(result.error.debug).toEqual([`arg2.1`, `arg2.2`]);
32
+ expect(result.error.cause).toEqual(error);
33
+ }
34
+ });
35
+ });
36
+ describe('failWith()', () => {
37
+ test('should produce a result with an error', () => {
38
+ const error = new Error('E_AOUCH');
39
+ const result = failWith(error);
40
+ if (result.ok) {
41
+ // @ts-expect-error : expect error since no error
42
+ expect(result.error).toBeUndefined();
43
+ }
44
+ else {
45
+ expect(result.error.code).toEqual('E_UNEXPECTED');
46
+ expect(result.error.cause).toEqual(error);
47
+ }
48
+ });
49
+ test('should produce a result with a yerror', () => {
50
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
51
+ const result = failWith(error);
52
+ if (result.ok) {
53
+ // @ts-expect-error : expect error since no error
54
+ expect(result.error).toBeUndefined();
55
+ }
56
+ else {
57
+ expect(result.error.debug[1]).toEqual('arg2.2');
58
+ // @ts-expect-error : expect error since nothing at this index
59
+ expect(result.error.debug[2]).toBeUndefined();
60
+ expect(result.error).toEqual(error);
61
+ }
62
+ });
63
+ test('should handle non-error throws', () => {
64
+ const result = failWith('Something went wrong');
65
+ expect(result.ok).toBe(false);
66
+ if (!result.ok) {
67
+ expect(result.error.code).toEqual('E_UNEXPECTED');
68
+ }
69
+ });
70
+ });
71
+ describe('settle()', () => {
72
+ test('should produce a result with a promise of a value', async () => {
73
+ const resultPromise = settle(Promise.resolve({ my: 'result' }));
74
+ const result = await resultPromise;
75
+ if (result.ok) {
76
+ const value = result.value.my;
77
+ expect(value).toEqual('result');
78
+ // @ts-expect-error : expect error if type is casted
79
+ expect(result.value.not).toBeUndefined();
80
+ }
81
+ else {
82
+ // @ts-expect-error : expect error since no value
83
+ expect(result.value).toBeUndefined();
84
+ }
85
+ });
86
+ test('should produce a result with a promise of an error', async () => {
87
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
88
+ const resultPromise = settle(Promise.reject(error));
89
+ const result = await resultPromise;
90
+ if (result.ok) {
91
+ // @ts-expect-error : expect error since no error
92
+ expect(result.error).toBeUndefined();
93
+ }
94
+ else {
95
+ expect(result.error.debug[1]).toEqual('arg2.2');
96
+ expect(result.error.debug[2]).toBeUndefined();
97
+ expect(result.error).toEqual(error);
98
+ }
99
+ });
100
+ });
101
+ describe('settleFrom()', () => {
102
+ test('should produce a result with a promise of a value', async () => {
103
+ const resultPromise = settleFrom(['E_ERROR'], Promise.resolve({ my: 'result' }));
104
+ const result = await resultPromise;
105
+ if (result.ok) {
106
+ const value = result.value.my;
107
+ expect(value).toEqual('result');
108
+ // @ts-expect-error : expect error if type is casted
109
+ expect(result.value.not).toBeUndefined();
110
+ }
111
+ else {
112
+ // @ts-expect-error : expect error since no value
113
+ expect(result.value).toBeUndefined();
114
+ }
115
+ });
116
+ test('should produce a result with a promise of an error', async () => {
117
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
118
+ const resultPromise = settleFrom(['E_ERROR_2'], Promise.reject(error));
119
+ const result = await resultPromise;
120
+ if (result.ok) {
121
+ // @ts-expect-error : expect error since no error
122
+ expect(result.error).toBeUndefined();
123
+ }
124
+ else {
125
+ expect(result.error.debug[1]).toEqual('arg2.2');
126
+ // @ts-expect-error : expect error since nothing at this index
127
+ expect(result.error.debug[2]).toBeUndefined();
128
+ expect(result.error).toEqual(error);
129
+ }
130
+ });
131
+ test('should let unknown errors pass through', async () => {
132
+ const unknownError = new YError('E_ERROR_3', ['arg3.1', 'arg3.2']);
133
+ await expect(settleFrom(['E_ERROR_2'], Promise.reject(unknownError))).rejects.toThrow(unknownError);
134
+ });
135
+ });
136
+ describe('attempt()', () => {
137
+ test('should produce a result with a promise of a value', async () => {
138
+ const resultPromise = attempt(async (test) => ({ my: test }), 'result');
139
+ const result = await resultPromise;
140
+ if (result.ok) {
141
+ const value = result.value.my;
142
+ expect(value).toEqual('result');
143
+ // @ts-expect-error : expect error if type is casted
144
+ expect(result.value.not).toBeUndefined();
145
+ }
146
+ else {
147
+ // @ts-expect-error : expect error since no value
148
+ expect(result.value).toBeUndefined();
149
+ }
150
+ });
151
+ test('should produce a result with a promise of an error', async () => {
152
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
153
+ const resultPromise = attempt(() => Promise.reject(error));
154
+ const result = await resultPromise;
155
+ if (result.ok) {
156
+ // @ts-expect-error : expect error since no error
157
+ expect(result.error).toBeUndefined();
158
+ }
159
+ else {
160
+ expect(result.error.debug[1]).toEqual('arg2.2');
161
+ expect(result.error.debug[2]).toBeUndefined();
162
+ expect(result.error).toEqual(error);
163
+ }
164
+ });
165
+ test('should catch synchronous throws', async () => {
166
+ const error = new Error('Sync crash');
167
+ const result = await attempt(async () => {
168
+ throw error;
169
+ });
170
+ if (!result.ok) {
171
+ expect(result.error.cause).toEqual(error);
172
+ }
173
+ else {
174
+ throw new YError('E_UNEXPECTED_SUCCESS');
175
+ }
176
+ });
177
+ });
178
+ describe('attemptFrom()', () => {
179
+ test('should produce a result with a promise of a value', async () => {
180
+ const resultPromise = attemptFrom(['E_ERROR'], async (test) => ({ my: test }), 'result');
181
+ const result = await resultPromise;
182
+ if (result.ok) {
183
+ const value = result.value.my;
184
+ expect(value).toEqual('result');
185
+ // @ts-expect-error : expect error if type is casted
186
+ expect(result.value.not).toBeUndefined();
187
+ }
188
+ else {
189
+ // @ts-expect-error : expect error since no value
190
+ expect(result.value).toBeUndefined();
191
+ }
192
+ });
193
+ test('should produce a result with a promise of an error', async () => {
194
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
195
+ const resultPromise = attemptFrom(['E_ERROR_2'], () => Promise.reject(error));
196
+ const result = await resultPromise;
197
+ if (result.ok) {
198
+ // @ts-expect-error : expect error since no error
199
+ expect(result.error).toBeUndefined();
200
+ }
201
+ else {
202
+ expect(result.error.debug[1]).toEqual('arg2.2');
203
+ // @ts-expect-error : expect error since nothing at this index
204
+ expect(result.error.debug[2]).toBeUndefined();
205
+ expect(result.error).toEqual(error);
206
+ }
207
+ });
208
+ });
209
+ describe('attemptSync()', () => {
210
+ test('should produce a result with a value', () => {
211
+ const result = attemptSync((test) => ({ my: test }), 'result');
212
+ if (result.ok) {
213
+ const value = result.value.my;
214
+ expect(value).toEqual('result');
215
+ // @ts-expect-error : expect error if type is casted
216
+ expect(result.value.not).toBeUndefined();
217
+ }
218
+ else {
219
+ // @ts-expect-error : expect error since no value
220
+ expect(result.value).toBeUndefined();
221
+ }
222
+ });
223
+ test('should produce a result with an error', async () => {
224
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
225
+ const result = attemptSync(() => {
226
+ throw error;
227
+ });
228
+ if (result.ok) {
229
+ // @ts-expect-error : expect error since no error
230
+ expect(result.error).toBeUndefined();
231
+ }
232
+ else {
233
+ expect(result.error.debug[1]).toEqual('arg2.2');
234
+ expect(result.error.debug[2]).toBeUndefined();
235
+ expect(result.error).toEqual(error);
236
+ }
237
+ });
238
+ });
239
+ describe('attemptSyncFrom()', () => {
240
+ test('should produce a result with a value', () => {
241
+ const result = attemptSyncFrom(['E_ERROR'], (test) => ({ my: test }), 'result');
242
+ if (result.ok) {
243
+ const value = result.value.my;
244
+ expect(value).toEqual('result');
245
+ // @ts-expect-error : expect error if type is casted
246
+ expect(result.value.not).toBeUndefined();
247
+ }
248
+ else {
249
+ // @ts-expect-error : expect error since no value
250
+ expect(result.value).toBeUndefined();
251
+ }
252
+ });
253
+ test('should produce a result with an error', () => {
254
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
255
+ const result = attemptSyncFrom(['E_ERROR_2'], () => {
256
+ throw error;
257
+ });
258
+ if (result.ok) {
259
+ // @ts-expect-error : expect error since no error
260
+ expect(result.error).toBeUndefined();
261
+ }
262
+ else {
263
+ expect(result.error.debug[1]).toEqual('arg2.2');
264
+ // @ts-expect-error : expect error since nothing at this index
265
+ expect(result.error.debug[2]).toBeUndefined();
266
+ expect(result.error).toEqual(error);
267
+ }
268
+ });
269
+ });
270
+ });
271
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,149 @@
1
+ {
2
+ "metapak": {
3
+ "configs": [
4
+ "main",
5
+ "readme",
6
+ "tsesm",
7
+ "eslint",
8
+ "jest",
9
+ "codeclimate",
10
+ "ghactions",
11
+ "jsdocs"
12
+ ],
13
+ "data": {
14
+ "files": "'src/**/*.ts'",
15
+ "testsFiles": "'src/**/*.test.ts'",
16
+ "distFiles": "'dist/**/*.js'",
17
+ "ignore": [
18
+ "dist"
19
+ ],
20
+ "bundleFiles": [
21
+ "dist",
22
+ "src"
23
+ ]
24
+ }
25
+ },
26
+ "name": "zresult",
27
+ "version": "1.0.0",
28
+ "description": "Wrap errors into results with that simple library.",
29
+ "scripts": {
30
+ "build": "rimraf 'dist' && tsc --outDir dist",
31
+ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
32
+ "cli": "env NODE_ENV=${NODE_ENV:-cli}",
33
+ "commitlint": "commitlint",
34
+ "cover": "node --run jest -- --coverage",
35
+ "doc": "echo \"# API\" > API.md; jsdoc2md 'dist/**/*.js' >> API.md && git add API.md",
36
+ "format": "node --run prettier",
37
+ "jest": "NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest",
38
+ "lint": "eslint 'src/**/*.ts'",
39
+ "metapak": "metapak",
40
+ "precommit": "node --run test && node --run lint && node --run build && node --run doc && node --run metapak -- -s",
41
+ "prettier": "prettier --write 'src/**/*.ts'",
42
+ "preversion": "node --run test && node --run lint && node --run build && node --run doc && node --run metapak -- -s",
43
+ "rebuild": "swc ./src -s -d dist -C jsc.target=es2022",
44
+ "test": "node --run jest",
45
+ "version": "node --run changelog"
46
+ },
47
+ "engines": {
48
+ "node": ">=24.14.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git://github.com/nfroidure/zresult.git"
53
+ },
54
+ "keywords": [
55
+ "error",
56
+ "why",
57
+ "better"
58
+ ],
59
+ "author": {
60
+ "name": "Nicolas Froidure",
61
+ "email": "nicolas.froidure@insertafter.com",
62
+ "url": "http://insertafter.com/en/index.html"
63
+ },
64
+ "license": "MIT",
65
+ "bugs": {
66
+ "url": "https://github.com/nfroidure/zresult/issues"
67
+ },
68
+ "dependencies": {
69
+ "yerror": "^11.0.0"
70
+ },
71
+ "devDependencies": {
72
+ "@commitlint/cli": "^20.5.0",
73
+ "@commitlint/config-conventional": "^20.5.0",
74
+ "@eslint/js": "^10.0.1",
75
+ "@swc/cli": "^0.8.1",
76
+ "@swc/core": "^1.15.24",
77
+ "@swc/jest": "^0.2.39",
78
+ "conventional-changelog": "^7.2.0",
79
+ "eslint": "^10.2.0",
80
+ "eslint-config-prettier": "^10.1.8",
81
+ "eslint-plugin-jest": "^29.15.1",
82
+ "eslint-plugin-prettier": "^5.5.5",
83
+ "jest": "^30.3.0",
84
+ "jsdoc-to-markdown": "^9.1.3",
85
+ "metapak": "^7.1.0",
86
+ "metapak-nfroidure": "22.1.1",
87
+ "prettier": "^3.8.1",
88
+ "rimraf": "^6.1.3",
89
+ "typescript": "^6.0.2",
90
+ "typescript-eslint": "^8.58.0"
91
+ },
92
+ "contributors": [],
93
+ "files": [
94
+ "dist",
95
+ "src",
96
+ "LICENSE",
97
+ "README.md",
98
+ "CHANGELOG.md"
99
+ ],
100
+ "prettier": {
101
+ "semi": true,
102
+ "printWidth": 80,
103
+ "singleQuote": true,
104
+ "trailingComma": "all",
105
+ "proseWrap": "always"
106
+ },
107
+ "funding": {
108
+ "type": "individual",
109
+ "url": "https://github.com/sponsors/nfroidure"
110
+ },
111
+ "overrides": {
112
+ "typescript-eslint": {
113
+ "typescript": "^6"
114
+ },
115
+ "@typescript-eslint/eslint-plugin": {
116
+ "typescript": "^6"
117
+ },
118
+ "@typescript-eslint/parser": {
119
+ "typescript": "^6"
120
+ }
121
+ },
122
+ "type": "module",
123
+ "main": "dist/index.js",
124
+ "types": "dist/index.d.ts",
125
+ "jest": {
126
+ "coverageReporters": [
127
+ "lcov"
128
+ ],
129
+ "testPathIgnorePatterns": [
130
+ "/node_modules/"
131
+ ],
132
+ "roots": [
133
+ "<rootDir>/src"
134
+ ],
135
+ "transform": {
136
+ "^.+\\.tsx?$": [
137
+ "@swc/jest",
138
+ {}
139
+ ]
140
+ },
141
+ "testEnvironment": "node",
142
+ "moduleNameMapper": {
143
+ "(.+)\\.js": "$1"
144
+ },
145
+ "extensionsToTreatAsEsm": [
146
+ ".ts"
147
+ ]
148
+ }
149
+ }
@@ -0,0 +1,336 @@
1
+ import { describe, test, expect } from '@jest/globals';
2
+ import {
3
+ ok,
4
+ fail,
5
+ failWith,
6
+ settle,
7
+ settleFrom,
8
+ attempt,
9
+ attemptFrom,
10
+ attemptSync,
11
+ attemptSyncFrom,
12
+ } from './index.js';
13
+ import { YError } from 'yerror';
14
+
15
+ declare module 'yerror' {
16
+ interface YErrorRegistry {
17
+ E_ERROR: `arg${number}`[];
18
+ E_ERROR_1: [string, string];
19
+ E_ERROR_2: [`arg2.1`, `arg2.2`];
20
+ E_ERROR_3: [`arg3.1`, `arg3.2`];
21
+ }
22
+ }
23
+
24
+ describe('zresult', () => {
25
+ describe('ok()', () => {
26
+ test('should produce a result with a value', () => {
27
+ const result = ok({ my: 'result' } as const);
28
+
29
+ if (result.ok) {
30
+ const value: string = result.value.my;
31
+
32
+ expect(value).toEqual('result');
33
+ // @ts-expect-error : expect error if type is casted
34
+ expect(result.value.not).toBeUndefined();
35
+ } else {
36
+ // @ts-expect-error : expect error since no value
37
+ expect(result.value).toBeUndefined();
38
+ }
39
+ });
40
+ });
41
+
42
+ describe('fail()', () => {
43
+ test('should produce a result with an error', () => {
44
+ const error = new Error('E_AOUCH');
45
+ const result = fail('E_ERROR_2', ['arg2.1', 'arg2.2'], error);
46
+
47
+ if (result.ok) {
48
+ // @ts-expect-error : expect error since no error
49
+ expect(result.error).toBeUndefined();
50
+ } else {
51
+ // @ts-expect-error : expect error since nothing at this index
52
+ expect(result.error.debug[2]).toBeUndefined();
53
+ expect(result.error.debug).toEqual([`arg2.1`, `arg2.2`]);
54
+ expect(result.error.cause).toEqual(error);
55
+ }
56
+ });
57
+ });
58
+
59
+ describe('failWith()', () => {
60
+ test('should produce a result with an error', () => {
61
+ const error = new Error('E_AOUCH');
62
+ const result = failWith(error);
63
+
64
+ if (result.ok) {
65
+ // @ts-expect-error : expect error since no error
66
+ expect(result.error).toBeUndefined();
67
+ } else {
68
+ expect(result.error.code).toEqual('E_UNEXPECTED');
69
+ expect(result.error.cause).toEqual(error);
70
+ }
71
+ });
72
+
73
+ test('should produce a result with a yerror', () => {
74
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
75
+ const result = failWith(error);
76
+
77
+ if (result.ok) {
78
+ // @ts-expect-error : expect error since no error
79
+ expect(result.error).toBeUndefined();
80
+ } else {
81
+ expect(result.error.debug[1]).toEqual('arg2.2');
82
+ // @ts-expect-error : expect error since nothing at this index
83
+ expect(result.error.debug[2]).toBeUndefined();
84
+ expect(result.error).toEqual(error);
85
+ }
86
+ });
87
+
88
+ test('should handle non-error throws', () => {
89
+ const result = failWith('Something went wrong' as unknown as Error);
90
+
91
+ expect(result.ok).toBe(false);
92
+ if (!result.ok) {
93
+ expect(result.error.code).toEqual('E_UNEXPECTED');
94
+ }
95
+ });
96
+ });
97
+
98
+ describe('settle()', () => {
99
+ test('should produce a result with a promise of a value', async () => {
100
+ const resultPromise = settle(Promise.resolve({ my: 'result' } as const));
101
+ const result = await resultPromise;
102
+
103
+ if (result.ok) {
104
+ const value: string = result.value.my;
105
+
106
+ expect(value).toEqual('result');
107
+ // @ts-expect-error : expect error if type is casted
108
+ expect(result.value.not).toBeUndefined();
109
+ } else {
110
+ // @ts-expect-error : expect error since no value
111
+ expect(result.value).toBeUndefined();
112
+ }
113
+ });
114
+
115
+ test('should produce a result with a promise of an error', async () => {
116
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
117
+ const resultPromise = settle(Promise.reject(error));
118
+ const result = await resultPromise;
119
+
120
+ if (result.ok) {
121
+ // @ts-expect-error : expect error since no error
122
+ expect(result.error).toBeUndefined();
123
+ } else {
124
+ expect(result.error.debug[1]).toEqual('arg2.2');
125
+ expect(result.error.debug[2]).toBeUndefined();
126
+ expect(result.error).toEqual(error);
127
+ }
128
+ });
129
+ });
130
+
131
+ describe('settleFrom()', () => {
132
+ test('should produce a result with a promise of a value', async () => {
133
+ const resultPromise = settleFrom(
134
+ ['E_ERROR'],
135
+ Promise.resolve({ my: 'result' } as const),
136
+ );
137
+ const result = await resultPromise;
138
+
139
+ if (result.ok) {
140
+ const value: string = result.value.my;
141
+
142
+ expect(value).toEqual('result');
143
+ // @ts-expect-error : expect error if type is casted
144
+ expect(result.value.not).toBeUndefined();
145
+ } else {
146
+ // @ts-expect-error : expect error since no value
147
+ expect(result.value).toBeUndefined();
148
+ }
149
+ });
150
+
151
+ test('should produce a result with a promise of an error', async () => {
152
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
153
+ const resultPromise = settleFrom(['E_ERROR_2'], Promise.reject(error));
154
+ const result = await resultPromise;
155
+
156
+ if (result.ok) {
157
+ // @ts-expect-error : expect error since no error
158
+ expect(result.error).toBeUndefined();
159
+ } else {
160
+ expect(result.error.debug[1]).toEqual('arg2.2');
161
+ // @ts-expect-error : expect error since nothing at this index
162
+ expect(result.error.debug[2]).toBeUndefined();
163
+ expect(result.error).toEqual(error);
164
+ }
165
+ });
166
+
167
+ test('should let unknown errors pass through', async () => {
168
+ const unknownError = new YError('E_ERROR_3', ['arg3.1', 'arg3.2']);
169
+
170
+ await expect(
171
+ settleFrom(['E_ERROR_2'], Promise.reject(unknownError)),
172
+ ).rejects.toThrow(unknownError);
173
+ });
174
+ });
175
+
176
+ describe('attempt()', () => {
177
+ test('should produce a result with a promise of a value', async () => {
178
+ const resultPromise = attempt(
179
+ async (test: string) => ({ my: test }),
180
+ 'result',
181
+ );
182
+ const result = await resultPromise;
183
+
184
+ if (result.ok) {
185
+ const value: string = result.value.my;
186
+
187
+ expect(value).toEqual('result');
188
+ // @ts-expect-error : expect error if type is casted
189
+ expect(result.value.not).toBeUndefined();
190
+ } else {
191
+ // @ts-expect-error : expect error since no value
192
+ expect(result.value).toBeUndefined();
193
+ }
194
+ });
195
+
196
+ test('should produce a result with a promise of an error', async () => {
197
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
198
+ const resultPromise = attempt(() => Promise.reject(error));
199
+ const result = await resultPromise;
200
+
201
+ if (result.ok) {
202
+ // @ts-expect-error : expect error since no error
203
+ expect(result.error).toBeUndefined();
204
+ } else {
205
+ expect(result.error.debug[1]).toEqual('arg2.2');
206
+ expect(result.error.debug[2]).toBeUndefined();
207
+ expect(result.error).toEqual(error);
208
+ }
209
+ });
210
+
211
+ test('should catch synchronous throws', async () => {
212
+ const error = new Error('Sync crash');
213
+ const result = await attempt(async (): Promise<undefined> => {
214
+ throw error;
215
+ });
216
+
217
+ if (!result.ok) {
218
+ expect(result.error.cause).toEqual(error);
219
+ } else {
220
+ throw new YError('E_UNEXPECTED_SUCCESS');
221
+ }
222
+ });
223
+ });
224
+
225
+ describe('attemptFrom()', () => {
226
+ test('should produce a result with a promise of a value', async () => {
227
+ const resultPromise = attemptFrom(
228
+ ['E_ERROR'],
229
+ async (test: string) => ({ my: test }),
230
+ 'result',
231
+ );
232
+ const result = await resultPromise;
233
+
234
+ if (result.ok) {
235
+ const value: string = result.value.my;
236
+
237
+ expect(value).toEqual('result');
238
+ // @ts-expect-error : expect error if type is casted
239
+ expect(result.value.not).toBeUndefined();
240
+ } else {
241
+ // @ts-expect-error : expect error since no value
242
+ expect(result.value).toBeUndefined();
243
+ }
244
+ });
245
+
246
+ test('should produce a result with a promise of an error', async () => {
247
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
248
+ const resultPromise = attemptFrom(
249
+ ['E_ERROR_2'],
250
+ (): Promise<undefined> => Promise.reject(error),
251
+ );
252
+ const result = await resultPromise;
253
+
254
+ if (result.ok) {
255
+ // @ts-expect-error : expect error since no error
256
+ expect(result.error).toBeUndefined();
257
+ } else {
258
+ expect(result.error.debug[1]).toEqual('arg2.2');
259
+ // @ts-expect-error : expect error since nothing at this index
260
+ expect(result.error.debug[2]).toBeUndefined();
261
+ expect(result.error).toEqual(error);
262
+ }
263
+ });
264
+ });
265
+
266
+ describe('attemptSync()', () => {
267
+ test('should produce a result with a value', () => {
268
+ const result = attemptSync((test: string) => ({ my: test }), 'result');
269
+
270
+ if (result.ok) {
271
+ const value: string = result.value.my;
272
+
273
+ expect(value).toEqual('result');
274
+ // @ts-expect-error : expect error if type is casted
275
+ expect(result.value.not).toBeUndefined();
276
+ } else {
277
+ // @ts-expect-error : expect error since no value
278
+ expect(result.value).toBeUndefined();
279
+ }
280
+ });
281
+
282
+ test('should produce a result with an error', async () => {
283
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
284
+ const result = attemptSync(() => {
285
+ throw error;
286
+ });
287
+
288
+ if (result.ok) {
289
+ // @ts-expect-error : expect error since no error
290
+ expect(result.error).toBeUndefined();
291
+ } else {
292
+ expect(result.error.debug[1]).toEqual('arg2.2');
293
+ expect(result.error.debug[2]).toBeUndefined();
294
+ expect(result.error).toEqual(error);
295
+ }
296
+ });
297
+ });
298
+
299
+ describe('attemptSyncFrom()', () => {
300
+ test('should produce a result with a value', () => {
301
+ const result = attemptSyncFrom(
302
+ ['E_ERROR'],
303
+ (test: string) => ({ my: test }),
304
+ 'result',
305
+ );
306
+
307
+ if (result.ok) {
308
+ const value: string = result.value.my;
309
+
310
+ expect(value).toEqual('result');
311
+ // @ts-expect-error : expect error if type is casted
312
+ expect(result.value.not).toBeUndefined();
313
+ } else {
314
+ // @ts-expect-error : expect error since no value
315
+ expect(result.value).toBeUndefined();
316
+ }
317
+ });
318
+
319
+ test('should produce a result with an error', () => {
320
+ const error = new YError('E_ERROR_2', ['arg2.1', 'arg2.2']);
321
+ const result = attemptSyncFrom(['E_ERROR_2'], (): undefined => {
322
+ throw error;
323
+ });
324
+
325
+ if (result.ok) {
326
+ // @ts-expect-error : expect error since no error
327
+ expect(result.error).toBeUndefined();
328
+ } else {
329
+ expect(result.error.debug[1]).toEqual('arg2.2');
330
+ // @ts-expect-error : expect error since nothing at this index
331
+ expect(result.error.debug[2]).toBeUndefined();
332
+ expect(result.error).toEqual(error);
333
+ }
334
+ });
335
+ });
336
+ });
package/src/index.ts ADDED
@@ -0,0 +1,157 @@
1
+ import {
2
+ type YErrorDebug,
3
+ type YErrorRegistry,
4
+ looksLikeAYError,
5
+ YError,
6
+ hasYErrorCode,
7
+ } from 'yerror';
8
+
9
+ /** A type to encapsulate a result that can either be
10
+ * failure or a success */
11
+ export type Result<T, E extends Error | YError> =
12
+ | { readonly ok: true; readonly value: T }
13
+ | { readonly ok: false; readonly error: E };
14
+
15
+ /** Create a successful result */
16
+ export function ok<T>(value: T): Result<T, never> {
17
+ return {
18
+ ok: true,
19
+ value,
20
+ };
21
+ }
22
+
23
+ /** Create a failure result using a YError code and debug values */
24
+ export function fail<C extends keyof YErrorRegistry>(
25
+ code: C,
26
+ debug: YErrorDebug<C>,
27
+ error?: Error,
28
+ ): Result<never, YError<C>> {
29
+ return {
30
+ ok: false,
31
+ error: new YError(code, debug, { cause: error }),
32
+ };
33
+ }
34
+
35
+ /** Wrap an existing error into a failure result */
36
+ export function failWith<E extends Error | YError>(
37
+ error: E,
38
+ ): Result<
39
+ never,
40
+ E extends YError<infer C> ? YError<C> : YError<'E_UNEXPECTED'>
41
+ > {
42
+ return {
43
+ ok: false,
44
+ error: (looksLikeAYError(error)
45
+ ? error
46
+ : new YError('E_UNEXPECTED', [], { cause: error })) as E extends YError<
47
+ infer C
48
+ >
49
+ ? YError<C>
50
+ : YError<'E_UNEXPECTED'>,
51
+ };
52
+ }
53
+
54
+ /** Transform a Promise into a Result catching any error */
55
+ export async function settle<T>(
56
+ promise: Promise<T>,
57
+ ): Promise<Result<T, YError>> {
58
+ try {
59
+ const value = await promise;
60
+
61
+ return ok(value);
62
+ } catch (err) {
63
+ return failWith(YError.cast(err as Error));
64
+ }
65
+ }
66
+
67
+ /** Transform a Promise into a Result catching only known
68
+ * errors and letting unknown pass through */
69
+ export async function settleFrom<C extends keyof YErrorRegistry, T>(
70
+ codes: C[],
71
+ promise: Promise<T>,
72
+ ): Promise<Result<T, YError<C>>> {
73
+ try {
74
+ const value = await promise;
75
+
76
+ return ok(value);
77
+ } catch (err) {
78
+ for (const code of codes) {
79
+ if (hasYErrorCode(err, code)) {
80
+ return failWith(err);
81
+ }
82
+ }
83
+ throw err;
84
+ }
85
+ }
86
+
87
+ /** Runs an asynchronous function and provides a Result catching
88
+ * any error */
89
+ export async function attempt<
90
+ T,
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
+ A extends any[],
93
+ >(fn: (...args: A) => Promise<T>, ...args: A): Promise<Result<T, YError>> {
94
+ try {
95
+ return ok(await fn(...args));
96
+ } catch (err) {
97
+ return failWith(YError.cast(err as Error));
98
+ }
99
+ }
100
+
101
+ /** Runs an asynchronous function and provides a Result catching
102
+ * only known errors and letting unknown pass through */
103
+ export async function attemptFrom<
104
+ C extends keyof YErrorRegistry,
105
+ T,
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ A extends any[],
108
+ >(
109
+ codes: C[],
110
+ fn: (...args: A) => Promise<T>,
111
+ ...args: A
112
+ ): Promise<Result<T, YError<C>>> {
113
+ try {
114
+ return ok(await fn(...args));
115
+ } catch (err) {
116
+ for (const code of codes) {
117
+ if (hasYErrorCode(err, code)) {
118
+ return failWith(err);
119
+ }
120
+ }
121
+ throw err;
122
+ }
123
+ }
124
+
125
+ /** Runs a synchronous function and provides a Result catching
126
+ * any error */
127
+ export function attemptSync<
128
+ T,
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ A extends any[],
131
+ >(fn: (...args: A) => T, ...args: A): Result<T, YError> {
132
+ try {
133
+ return ok(fn(...args));
134
+ } catch (err) {
135
+ return failWith(YError.cast(err as Error));
136
+ }
137
+ }
138
+
139
+ /** Runs a synchronous function and provides a Result catching
140
+ * only known errors and letting unknown pass through */
141
+ export function attemptSyncFrom<
142
+ C extends keyof YErrorRegistry,
143
+ T,
144
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
145
+ A extends any[],
146
+ >(codes: C[], fn: (...args: A) => T, ...args: A): Result<T, YError<C>> {
147
+ try {
148
+ return ok(fn(...args));
149
+ } catch (err) {
150
+ for (const code of codes) {
151
+ if (hasYErrorCode(err, code)) {
152
+ return failWith(err);
153
+ }
154
+ }
155
+ throw err;
156
+ }
157
+ }