@praha/byethrow 0.9.0 → 0.10.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.
@@ -1,7 +1,6 @@
1
1
  import type { Result, ResultAsync } from '../result.js';
2
2
  /**
3
- * Wraps a function execution (sync or async) or a Promise in a {@link Result} or {@link ResultAsync} type,
4
- * capturing errors and returning them in a structured way.
3
+ * Executes a function that may throw and wraps the result in a {@link Result} or {@link ResultAsync}.
5
4
  *
6
5
  * You can use either a custom `catch` handler or rely on the `safe: true` option
7
6
  * to assume the function cannot throw.
@@ -9,28 +8,13 @@ import type { Result, ResultAsync } from '../result.js';
9
8
  * @function
10
9
  * @typeParam T - The function type to execute (sync or async) or a Promise type.
11
10
  * @typeParam E - The error type to return if `catch` is used.
11
+ * @returns A {@link Result} or {@link ResultAsync} wrapping the function's return value or the caught error.
12
12
  *
13
13
  * @example Sync try-catch
14
14
  * ```ts
15
15
  * import { Result } from '@praha/byethrow';
16
16
  *
17
- * const fn = Result.try({
18
- * try: (x: number) => {
19
- * if (x < 0) throw new Error('Negative!');
20
- * return x * 2;
21
- * },
22
- * catch: (error) => new Error('Oops!', { cause: error }),
23
- * });
24
- *
25
- * const result = fn(5); // Result.Result<number, Error>
26
- * ```
27
- *
28
- * @example Sync try-catch with immediate execution
29
- * ```ts
30
- * import { Result } from '@praha/byethrow';
31
- *
32
17
  * const result = Result.try({
33
- * immediate: true,
34
18
  * try: () => {
35
19
  * const x = Math.random() * 10 - 5;
36
20
  * if (x < 0) throw new Error('Negative!');
@@ -46,21 +30,8 @@ import type { Result, ResultAsync } from '../result.js';
46
30
  * ```ts
47
31
  * import { Result } from '@praha/byethrow';
48
32
  *
49
- * const fn = Result.try({
50
- * safe: true,
51
- * try: (x: number) => x + 1,
52
- * });
53
- *
54
- * const result = fn(1); // Result.Result<number, never>
55
- * ```
56
- *
57
- * @example Sync safe with immediate execution
58
- * ```ts
59
- * import { Result } from '@praha/byethrow';
60
- *
61
33
  * const result = Result.try({
62
34
  * safe: true,
63
- * immediate: true,
64
35
  * try: () => Math.random() + 1,
65
36
  * });
66
37
  *
@@ -71,20 +42,7 @@ import type { Result, ResultAsync } from '../result.js';
71
42
  * ```ts
72
43
  * import { Result } from '@praha/byethrow';
73
44
  *
74
- * const fn = Result.try({
75
- * try: async (id: string) => await fetch(`/api/data/${id}`),
76
- * catch: (error) => new Error('Oops!', { cause: error }),
77
- * });
78
- *
79
- * const result = await fn('abc'); // Result.ResultAsync<Response, Error>
80
- * ```
81
- *
82
- * @example Async try-catch with immediate execution
83
- * ```ts
84
- * import { Result } from '@praha/byethrow';
85
- *
86
45
  * const result = Result.try({
87
- * immediate: true,
88
46
  * try: () => fetch('/api/data'),
89
47
  * catch: (error) => new Error('Fetch failed', { cause: error }),
90
48
  * });
@@ -96,21 +54,8 @@ import type { Result, ResultAsync } from '../result.js';
96
54
  * ```ts
97
55
  * import { Result } from '@praha/byethrow';
98
56
  *
99
- * const fn = Result.try({
100
- * safe: true,
101
- * try: async () => await Promise.resolve('ok'),
102
- * });
103
- *
104
- * const result = await fn(); // Result.ResultAsync<string, never>
105
- * ```
106
- *
107
- * @example Async safe with immediate execution
108
- * ```ts
109
- * import { Result } from '@praha/byethrow';
110
- *
111
57
  * const result = Result.try({
112
58
  * safe: true,
113
- * immediate: true,
114
59
  * try: () => Promise.resolve('ok'),
115
60
  * });
116
61
  *
@@ -120,40 +65,20 @@ import type { Result, ResultAsync } from '../result.js';
120
65
  * @category Creators
121
66
  */
122
67
  declare const try_: {
123
- <T extends (...args: readonly any[]) => Promise<any>, E>(options: {
124
- try: T;
125
- catch: (error: unknown) => E;
126
- }): (...args: Parameters<T>) => ResultAsync<Awaited<ReturnType<T>>, E>;
127
68
  <T extends () => Promise<any>, E>(options: {
128
- immediate: true;
129
69
  try: T;
130
70
  catch: (error: unknown) => E;
131
71
  }): ResultAsync<Awaited<ReturnType<T>>, E>;
132
- <T extends (...args: readonly any[]) => Promise<any>>(options: {
133
- safe: true;
134
- try: T;
135
- }): (...args: Parameters<T>) => ResultAsync<Awaited<ReturnType<T>>, never>;
136
72
  <T extends () => Promise<any>>(options: {
137
73
  safe: true;
138
- immediate: true;
139
74
  try: T;
140
75
  }): ResultAsync<Awaited<ReturnType<T>>, never>;
141
- <T extends (...args: readonly any[]) => any, E>(options: {
142
- try: T;
143
- catch: (error: unknown) => E;
144
- }): (...args: Parameters<T>) => Result<ReturnType<T>, E>;
145
76
  <T extends () => any, E>(options: {
146
- immediate: true;
147
77
  try: T;
148
78
  catch: (error: unknown) => E;
149
79
  }): Result<ReturnType<T>, E>;
150
- <T extends (...args: readonly any[]) => any>(options: {
151
- safe: true;
152
- try: T;
153
- }): (...args: Parameters<T>) => Result<ReturnType<T>, never>;
154
80
  <T extends () => any>(options: {
155
81
  safe: true;
156
- immediate: true;
157
82
  try: T;
158
83
  }): Result<ReturnType<T>, never>;
159
84
  };
@@ -15,7 +15,7 @@
15
15
  * return Result.succeed();
16
16
  * };
17
17
  *
18
- * const findUser = Result.try({
18
+ * const findUser = Result.fn({
19
19
  * try: (id: string) => {
20
20
  * return { id, name: 'John Doe' };
21
21
  * },
@@ -44,7 +44,7 @@
44
44
  * return R.succeed();
45
45
  * };
46
46
  *
47
- * const findUser = R.try({
47
+ * const findUser = R.fn({
48
48
  * try: (id: string) => {
49
49
  * return { id, name: 'John Doe' };
50
50
  * },
@@ -7,6 +7,7 @@ export * from './functions/bind.js';
7
7
  export * from './functions/collect.js';
8
8
  export * from './functions/do.js';
9
9
  export * from './functions/fail.js';
10
+ export * from './functions/fn.js';
10
11
  export * from './functions/inspect.js';
11
12
  export * from './functions/inspect-error.js';
12
13
  export * from './functions/is-failure.js';
@@ -15,6 +16,7 @@ export * from './functions/is-success.js';
15
16
  export * from './functions/map.js';
16
17
  export * from './functions/map-error.js';
17
18
  export * from './functions/or-else.js';
19
+ export * from './functions/or-through.js';
18
20
  export * from './functions/parse.js';
19
21
  export * from './functions/pipe.js';
20
22
  export * from './functions/sequence.js';
@@ -7,6 +7,7 @@ export * from "./functions/bind.js";
7
7
  export * from "./functions/collect.js";
8
8
  export * from "./functions/do.js";
9
9
  export * from "./functions/fail.js";
10
+ export * from "./functions/fn.js";
10
11
  export * from "./functions/inspect.js";
11
12
  export * from "./functions/inspect-error.js";
12
13
  export * from "./functions/is-failure.js";
@@ -15,6 +16,7 @@ export * from "./functions/is-success.js";
15
16
  export * from "./functions/map.js";
16
17
  export * from "./functions/map-error.js";
17
18
  export * from "./functions/or-else.js";
19
+ export * from "./functions/or-through.js";
18
20
  export * from "./functions/parse.js";
19
21
  export * from "./functions/pipe.js";
20
22
  export * from "./functions/sequence.js";
@@ -1,13 +1,17 @@
1
- import type { Failure, InferFailure, Result, ResultAsync } from '../result.js';
1
+ import type { HasPromise } from '../internals/types/has-promise.js';
2
+ import type { Failure, InferFailure, ResultMaybeAsync } from '../result.js';
2
3
  /**
3
4
  * Asserts that a {@link Result} or {@link ResultAsync} is a {@link Failure} and returns it.
4
- * If the result is a {@link Success}, throws an error.
5
+ * This function requires that the result's success type is `never`, meaning the result is
6
+ * guaranteed to be a {@link Failure} at the type level.
7
+ * If the result is a {@link Success} at runtime, throws an error.
5
8
  *
6
9
  * @function
7
- * @typeParam E - The type of the error value.
10
+ * @typeParam R - The result type that extends {@link ResultMaybeAsync} with `never` as the success type.
8
11
  * @param result - The {@link Result} or {@link ResultAsync} to assert as a {@link Failure}.
9
- * @returns The {@link Failure} result or a Promise of {@link Success} result.
10
- * @throws {Error} If the result is a {@link Success}.
12
+ * The success type must be `never`.
13
+ * @returns The {@link Failure} result or a Promise of {@link Failure} result.
14
+ * @throws {Error} If the result is a {@link Success} at runtime.
11
15
  *
12
16
  * @example
13
17
  * ```ts
@@ -18,24 +22,15 @@ import type { Failure, InferFailure, Result, ResultAsync } from '../result.js';
18
22
  * // failure: { type: 'Failure', error: 'error' }
19
23
  * ```
20
24
  *
21
- * @example Throws on Success
22
- * ```ts
23
- * // @errors: 2769
24
- * import { Result } from '@praha/byethrow';
25
- *
26
- * const result = Result.succeed(42);
27
- * Result.assertFailure(result); // throws Error
28
- * ```
29
- *
30
- * @example Safe unwrap with assertFailure
25
+ * @example Type-safe usage after narrowing success type
31
26
  * ```ts
32
27
  * import { Result } from '@praha/byethrow';
33
28
  *
34
29
  * const getResult = (): Result.Result<number, string> => Result.fail('error');
35
30
  * const value = Result.pipe(
36
31
  * getResult(),
37
- * Result.andThen(() => Result.fail('die')),
38
- * Result.assertFailure,
32
+ * Result.andThen(() => Result.fail('die')), // Success type becomes never
33
+ * Result.assertFailure, // Type-safe: success type is now never
39
34
  * Result.unwrapError(), // Safe unwrap after assertion
40
35
  * );
41
36
  * ```
@@ -44,7 +39,4 @@ import type { Failure, InferFailure, Result, ResultAsync } from '../result.js';
44
39
  *
45
40
  * @category Assertions
46
41
  */
47
- export declare const assertFailure: {
48
- <R extends ResultAsync<never, any>>(result: R): Promise<Failure<InferFailure<R>>>;
49
- <R extends Result<never, any>>(result: R): Failure<InferFailure<R>>;
50
- };
42
+ export declare const assertFailure: <R extends ResultMaybeAsync<never, any>>(result: R) => true extends HasPromise<R> ? Promise<Failure<InferFailure<R>>> : Failure<InferFailure<R>>;
@@ -1,13 +1,17 @@
1
- import type { InferSuccess, Result, ResultAsync, Success } from '../result.js';
1
+ import type { HasPromise } from '../internals/types/has-promise.js';
2
+ import type { InferSuccess, ResultMaybeAsync, Success } from '../result.js';
2
3
  /**
3
4
  * Asserts that a {@link Result} or {@link ResultAsync} is a {@link Success} and returns it.
4
- * If the result is a {@link Failure}, throws an error.
5
+ * This function requires that the result's error type is `never`, meaning the result is
6
+ * guaranteed to be a {@link Success} at the type level.
7
+ * If the result is a {@link Failure} at runtime, throws an error.
5
8
  *
6
9
  * @function
7
- * @typeParam T - The type of the success value.
10
+ * @typeParam R - The result type that extends {@link ResultMaybeAsync} with `never` as the error type.
8
11
  * @param result - The {@link Result} or {@link ResultAsync} to assert as a {@link Success}.
12
+ * The error type must be `never`.
9
13
  * @returns The {@link Success} result or a Promise of {@link Success} result.
10
- * @throws {Error} If the result is a {@link Failure}.
14
+ * @throws {Error} If the result is a {@link Failure} at runtime.
11
15
  *
12
16
  * @example
13
17
  * ```ts
@@ -18,24 +22,15 @@ import type { InferSuccess, Result, ResultAsync, Success } from '../result.js';
18
22
  * // success: { type: 'Success', value: 42 }
19
23
  * ```
20
24
  *
21
- * @example Throws on Failure
22
- * ```ts
23
- * // @errors: 2769
24
- * import { Result } from '@praha/byethrow';
25
- *
26
- * const result = Result.fail('error');
27
- * Result.assertSuccess(result); // throws Error
28
- * ```
29
- *
30
- * @example Safe unwrap with assertSuccess
25
+ * @example Type-safe usage after narrowing error type
31
26
  * ```ts
32
27
  * import { Result } from '@praha/byethrow';
33
28
  *
34
29
  * const getResult = (): Result.Result<number, string> => Result.succeed(42);
35
30
  * const value = Result.pipe(
36
31
  * getResult(),
37
- * Result.orElse(() => Result.succeed('fallback')),
38
- * Result.assertSuccess,
32
+ * Result.orElse(() => Result.succeed(0)), // Error type becomes never
33
+ * Result.assertSuccess, // Type-safe: error type is now never
39
34
  * Result.unwrap(), // Safe unwrap after assertion
40
35
  * );
41
36
  * ```
@@ -44,7 +39,4 @@ import type { InferSuccess, Result, ResultAsync, Success } from '../result.js';
44
39
  *
45
40
  * @category Assertions
46
41
  */
47
- export declare const assertSuccess: {
48
- <R extends ResultAsync<any, never>>(result: R): Promise<Success<InferSuccess<R>>>;
49
- <R extends Result<any, never>>(result: R): Success<InferSuccess<R>>;
50
- };
42
+ export declare const assertSuccess: <R extends ResultMaybeAsync<any, never>>(result: R) => true extends HasPromise<R> ? Promise<Success<InferSuccess<R>>> : Success<InferSuccess<R>>;
@@ -0,0 +1,84 @@
1
+ import type { Result, ResultAsync } from '../result.js';
2
+ /**
3
+ * Wraps a function that may throw and returns a new function that returns a {@link Result} or {@link ResultAsync}.
4
+ *
5
+ * You can use either a custom `catch` handler or rely on the `safe: true` option
6
+ * to assume the function cannot throw.
7
+ *
8
+ * @function
9
+ * @typeParam T - The function type to execute (sync or async) or a Promise type.
10
+ * @typeParam E - The error type to return if `catch` is used.
11
+ * @returns A new function that returns a {@link Result} or {@link ResultAsync} wrapping the original function's return value or the caught error.
12
+ *
13
+ * @example Sync try-catch
14
+ * ```ts
15
+ * import { Result } from '@praha/byethrow';
16
+ *
17
+ * const fn = Result.fn({
18
+ * try: (x: number) => {
19
+ * if (x < 0) throw new Error('Negative!');
20
+ * return x * 2;
21
+ * },
22
+ * catch: (error) => new Error('Oops!', { cause: error }),
23
+ * });
24
+ *
25
+ * const result = fn(5); // Result.Result<number, Error>
26
+ * ```
27
+ *
28
+ * @example Sync safe
29
+ * ```ts
30
+ * import { Result } from '@praha/byethrow';
31
+ *
32
+ * const fn = Result.fn({
33
+ * safe: true,
34
+ * try: (x: number) => x + 1,
35
+ * });
36
+ *
37
+ * const result = fn(1); // Result.Result<number, never>
38
+ * ```
39
+ *
40
+ * @example Async try-catch
41
+ * ```ts
42
+ * import { Result } from '@praha/byethrow';
43
+ *
44
+ * const fn = Result.fn({
45
+ * try: async (id: string) => await fetch(`/api/data/${id}`),
46
+ * catch: (error) => new Error('Oops!', { cause: error }),
47
+ * });
48
+ *
49
+ * const result = await fn('abc'); // Result.ResultAsync<Response, Error>
50
+ * ```
51
+ *
52
+ * @example Async safe
53
+ * ```ts
54
+ * import { Result } from '@praha/byethrow';
55
+ *
56
+ * const fn = Result.fn({
57
+ * safe: true,
58
+ * try: async () => await Promise.resolve('ok'),
59
+ * });
60
+ *
61
+ * const result = await fn(); // Result.ResultAsync<string, never>
62
+ * ```
63
+ *
64
+ * @category Creators
65
+ */
66
+ declare const fn: {
67
+ <T extends (...args: readonly any[]) => Promise<any>, E>(options: {
68
+ try: T;
69
+ catch: (error: unknown) => E;
70
+ }): (...args: Parameters<T>) => ResultAsync<Awaited<ReturnType<T>>, E>;
71
+ <T extends (...args: readonly any[]) => Promise<any>>(options: {
72
+ safe: true;
73
+ try: T;
74
+ }): (...args: Parameters<T>) => ResultAsync<Awaited<ReturnType<T>>, never>;
75
+ <T extends (...args: readonly any[]) => any, E>(options: {
76
+ try: T;
77
+ catch: (error: unknown) => E;
78
+ }): (...args: Parameters<T>) => Result<ReturnType<T>, E>;
79
+ <T extends (...args: readonly any[]) => any>(options: {
80
+ safe: true;
81
+ try: T;
82
+ }): (...args: Parameters<T>) => Result<ReturnType<T>, never>;
83
+ };
84
+ export { fn };
@@ -0,0 +1,21 @@
1
+ import { fail } from "./fail.js";
2
+ import { succeed } from "./succeed.js";
3
+ import { isPromise } from "../internals/helpers/is-promise.js";
4
+ const fn_fn = (options)=>{
5
+ const fn = (...args)=>{
6
+ try {
7
+ const output = options.try(...args);
8
+ if (isPromise(output)) {
9
+ const promise = succeed(output);
10
+ if ('safe' in options && options.safe) return promise;
11
+ return promise.catch((error)=>fail(options.catch(error)));
12
+ }
13
+ return succeed(output);
14
+ } catch (error) {
15
+ if ('safe' in options && options.safe) throw error;
16
+ return fail(options.catch(error));
17
+ }
18
+ };
19
+ return fn;
20
+ };
21
+ export { fn_fn as fn };
@@ -1,9 +1,9 @@
1
- import type { Failure, Result } from '../result.js';
1
+ import type { Result } from '../result.js';
2
2
  /**
3
3
  * Type guard to check if a {@link Result} is a {@link Failure}.
4
4
  *
5
5
  * @function
6
- * @typeParam E - The type of the error value.
6
+ * @typeParam R - The type of the result to check.
7
7
  * @param result - The {@link Result} to check.
8
8
  * @returns `true` if the result is a {@link Failure}, otherwise `false`.
9
9
  *
@@ -19,4 +19,6 @@ import type { Failure, Result } from '../result.js';
19
19
  *
20
20
  * @category Type Guards
21
21
  */
22
- export declare const isFailure: <E>(result: Result<unknown, E>) => result is Failure<E>;
22
+ export declare const isFailure: <R extends Result<unknown, unknown>>(result: R) => result is Extract<R, {
23
+ readonly type: "Failure";
24
+ }>;
@@ -1,9 +1,9 @@
1
- import type { Result, Success } from '../result.js';
1
+ import type { Result } from '../result.js';
2
2
  /**
3
3
  * Type guard to check if a {@link Result} is a {@link Success}.
4
4
  *
5
5
  * @function
6
- * @typeParam T - The type of the success value.
6
+ * @typeParam R - The type of the result to check.
7
7
  * @param result - The {@link Result} to check.
8
8
  * @returns `true` if the result is a {@link Success}, otherwise `false`.
9
9
  *
@@ -19,4 +19,6 @@ import type { Result, Success } from '../result.js';
19
19
  *
20
20
  * @category Type Guards
21
21
  */
22
- export declare const isSuccess: <T>(result: Result<T, unknown>) => result is Success<T>;
22
+ export declare const isSuccess: <R extends Result<unknown, unknown>>(result: R) => result is Extract<R, {
23
+ readonly type: "Success";
24
+ }>;
@@ -0,0 +1,64 @@
1
+ import type { InferFailure, InferSuccess, ResultFor, ResultMaybeAsync } from '../result.js';
2
+ /**
3
+ * Runs an additional computation using the error value of a {@link Result} or {@link ResultAsync},
4
+ * but **returns the original failure** if the additional computation is successful.
5
+ *
6
+ * If the original result is a {@link Success}, it is returned immediately without running the function.
7
+ * If the original result is a {@link Failure}, the function is executed with the error value.
8
+ * If the function returns a {@link Success}, the original failure is returned.
9
+ * If the function returns a {@link Failure}, that new failure is returned.
10
+ *
11
+ * Useful for running error recovery or fallback logic while preserving the original error on success.
12
+ *
13
+ * @function
14
+ * @typeParam R1 - The input {@link Result} or {@link ResultAsync}.
15
+ * @typeParam R2 - The result type returned by `fn`.
16
+ *
17
+ * @example Success Case
18
+ * ```ts
19
+ * import { Result } from '@praha/byethrow';
20
+ *
21
+ * const result = Result.pipe(
22
+ * Result.succeed(5),
23
+ * Result.orThrough((error) => {
24
+ * return Result.succeed(null);
25
+ * }),
26
+ * );
27
+ * // { type: 'Success', value: 5 }
28
+ * ```
29
+ *
30
+ * @example Failure Case (function returns a Success)
31
+ * ```ts
32
+ * import { Result } from '@praha/byethrow';
33
+ *
34
+ * const result = Result.pipe(
35
+ * Result.fail('error'),
36
+ * Result.orThrough((error) => {
37
+ * console.log('Logging error:', error);
38
+ * return Result.succeed(null);
39
+ * }),
40
+ * );
41
+ * // { type: 'Failure', error: 'error' }
42
+ * ```
43
+ *
44
+ * @example Failure Case (function returns a Failure)
45
+ * ```ts
46
+ * import { Result } from '@praha/byethrow';
47
+ *
48
+ * const result = Result.pipe(
49
+ * Result.fail('original error'),
50
+ * Result.orThrough((error) => {
51
+ * return Result.fail('new error');
52
+ * }),
53
+ * );
54
+ * // { type: 'Failure', error: 'new error' }
55
+ * ```
56
+ *
57
+ * @see {@link pipe} - It is recommended to use this function with the {@link pipe} function for better readability and composability.
58
+ *
59
+ * @category Combinators
60
+ */
61
+ export declare const orThrough: {
62
+ <R1 extends ResultMaybeAsync<any, any>, R2 extends ResultMaybeAsync<any, any>>(fn: (a: InferFailure<R1>) => R2): (result: R1) => ResultFor<R1 | R2, InferSuccess<R1>, InferFailure<R1> | InferFailure<R2>>;
63
+ <F extends (a: any) => ResultMaybeAsync<any, any>>(fn: F): <R1 extends ResultMaybeAsync<any, Parameters<F>[0]>>(result: R1) => ResultFor<R1 | ReturnType<F>, InferSuccess<R1>, InferFailure<R1> | InferFailure<F>>;
64
+ };
@@ -0,0 +1,16 @@
1
+ import { isSuccess } from "./is-success.js";
2
+ import { isPromise } from "../internals/helpers/is-promise.js";
3
+ const orThrough = (fn)=>(result)=>{
4
+ const apply = (r)=>{
5
+ if (isSuccess(r)) return r;
6
+ const next = fn(r.error);
7
+ if (isPromise(next)) return next.then((n)=>{
8
+ if (isSuccess(n)) return r;
9
+ return n;
10
+ });
11
+ if (isSuccess(next)) return r;
12
+ return next;
13
+ };
14
+ return isPromise(result) ? result.then(apply) : apply(result);
15
+ };
16
+ export { orThrough };