@zokugun/xtry 0.1.0 → 0.3.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/README.md CHANGED
@@ -7,75 +7,159 @@
7
7
  [![Donation](https://img.shields.io/badge/donate-liberapay-green)](https://liberapay.com/daiyam/donate)
8
8
  [![Donation](https://img.shields.io/badge/donate-paypal-green)](https://paypal.me/daiyam99)
9
9
 
10
- Simple `try/catch` wrapper returning Result
10
+ Simple `try/catch` wrappers that always return a `Result` discriminated union, plus ready-made helpers (`ok`, `err`) for predictable control flow.
11
11
 
12
- Getting Started
13
- ---------------
12
+ Why xtry?
13
+ ---------
14
14
 
15
- With [node](http://nodejs.org) previously installed:
15
+ - Turn any sync or async function into an explicit `Result` object with zero dependencies.
16
+ - Strong TypeScript types guide your control flow (`fails` and tagged errors).
17
+ - Optional failure handlers let you log, meter, or mutate state exactly where the error occurs.
16
18
 
17
- npm install @zokugun/xtry
19
+ Installation
20
+ ------------
18
21
 
19
- Basic Example
20
- -------------
22
+ ```bash
23
+ npm install @zokugun/xtry
24
+ ```
25
+
26
+ Quick Start
27
+ -----------
21
28
 
22
29
  ```typescript
23
30
  import { xatry } from '@zokugun/xtry'
24
31
 
25
- const { fails, value, error } = xatry(somePromise);
32
+ const userResult = await xatry(fetchUserFromApi());
33
+
34
+ if(userResult.fails) {
35
+ console.error(userResult.error);
36
+ return;
37
+ }
38
+
39
+ console.log('User loaded:', userResult.value);
26
40
  ```
27
41
 
28
42
  Advanced Example
29
43
  ----------------
30
44
 
31
45
  ```typescript
32
- import { err, ok, type Result, xatry, xtry } from '@zokugun/xtry'
46
+ import { err, type Result, xatry, xtry } from '@zokugun/xtry'
33
47
 
34
48
  export type FoobarError = { type: 'FOOBAR'; message: string };
35
49
 
36
- function foobar(): Result<number, FoobarError> {
37
- const { fails, value, error } = xatry(somePromise);
50
+ async function foobar(): Result<number, FoobarError> {
51
+ const result = await xatry(fetchUserFromApi());
38
52
 
39
- if (fails) {
53
+ if(fails) {
40
54
  return err({ type: 'FOOBAR', message: 'The promise has failed...' });
41
55
  }
42
56
 
43
- return ok(value);
57
+ return try(() => calculateAge(result.value));
44
58
  }
45
59
 
46
- function main() {
47
- const result = foobar();
60
+ async function main() {
61
+ const result = await foobar();
48
62
 
49
- if (result.fails) {
63
+ if(result.fails) {
50
64
  console.error(result.error.message);
51
- exit(1);
65
+
66
+ return;
67
+ }
68
+
69
+ console.log(result.value);
70
+ }
71
+ ```
72
+
73
+ Partial Example
74
+ ---------------
75
+
76
+ `YResult` extends the base `Result` union with a `success` flag so you can distinguish "valid failure" states from true errors.
77
+
78
+ ```typescript
79
+ import { err, ok, yerr, yok, type YResult } from '@zokugun/xtry'
80
+
81
+ function toNumber(input: string): YResult<number, MyError, 'empty-string'> {
82
+ if(input.length > 0) {
83
+ return yerr('empty-string');
84
+ }
85
+
86
+ const floatValue = Number.parseFloat(input);
87
+
88
+ if(Number.isNaN(floatValue)) {
89
+ return err({ type: '#VALUE!' });
90
+ }
91
+
92
+ return yok(floatValue);
93
+ }
94
+
95
+ function add(_x: string, _y: number): Result<number, MyError> {
96
+ const x = toNumber(_x);
97
+ if(x.fails) {
98
+ return x;
99
+ }
100
+ if(!x.success) {
101
+ return ok(0);
102
+ }
103
+
104
+ const y = toNumber(_y);
105
+ if(y.fails) {
106
+ return y;
107
+ }
108
+ if(!y.success) {
109
+ return ok(0);
52
110
  }
53
111
 
54
- console.log(value);
112
+ return x.value + y.value;
55
113
  }
56
114
  ```
57
115
 
58
- API
59
- ---
116
+ API reference
117
+ -------------
118
+
119
+ ### Result helpers
60
120
 
61
121
  ```typescript
62
- type Success<T> = {
63
- fails: false;
64
- value: T;
65
- error: null;
66
- };
67
- type Failure<E> = {
68
- fails: true;
69
- value: null;
70
- error: E;
71
- };
122
+ export type Success<T> = { fails: false; value: T; error: null };
123
+ export type Failure<E> = { fails: true; value: null; error: E };
72
124
  export type Result<T, E> = Success<T> | Failure<E>;
73
- export function xtry<T, E>(func: () => Exclude<T, Promise<unknown>>, handler?: ((error: E) => void)): Result<T, E>;
74
- export async function xatry<T, E>(func: (() => Exclude<T, Promise<unknown>>) | Promise<Exclude<T, Promise<unknown>>>, handler?: ((error: E) => void)): Promise<Result<T, E>>;
125
+
75
126
  export function ok<T>(value: T): Success<T>;
76
127
  export function err<E>(error: E): Failure<E>;
77
128
  ```
78
129
 
130
+ ### Try helpers
131
+
132
+ ```typescript
133
+ export function xtry<T, E>(func: () => Exclude<T, Promise<unknown>>, handler?: (error: unknown) => void | E): Result<T, E>;
134
+ export function xatry<T, E>(func: (() => Exclude<T, Promise<unknown>>) | Promise<Exclude<T, Promise<unknown>>>, handler?: (error: unknown) => void | E): Promise<Result<T, E>>;
135
+
136
+ export function stringifyError(error: unknown): string;
137
+ ```
138
+
139
+ Both helpers:
140
+
141
+ - execute the supplied function and capture thrown values;
142
+ - call the optional `handler` before turning that value into `err(error)`;
143
+ - never throw, making the return signature a reliable discriminated union.
144
+
145
+ ### Partial helpers
146
+
147
+ ```typescript
148
+ export type YSuccess<T> = Success<T> & { success: true };
149
+ export type YFailure<S> = { fails: false; success: false; type: S; value: null; error: null };
150
+ export type YResult<T, E, S> = Failure<E> | YSuccess<T> | YFailure<S>;
151
+
152
+ export function yok<T>(value: T): YSuccess<T>;
153
+ export function yerr<S>(type: S): YFailure<S>;
154
+ ```
155
+
156
+ These helpers are useful when you need to separate soft rejections (`success: false`) from hard failures (`fails: true`).
157
+
158
+ Tips
159
+ ----
160
+
161
+ - Narrow on `fails` first, then use other flags (`success`, custom `type` or `value`) for the happy-path branching.
162
+
79
163
  Donations
80
164
  ---------
81
165
 
package/lib/index.d.ts CHANGED
@@ -1,16 +1,4 @@
1
- type Success<T> = {
2
- fails: false;
3
- value: T;
4
- error: null;
5
- };
6
- type Failure<E> = {
7
- fails: true;
8
- value: null;
9
- error: E;
10
- };
11
- export type Result<T, E> = Success<T> | Failure<E>;
12
- export declare function xtry<T, E>(func: () => Exclude<T, Promise<unknown>>, handler?: ((error: E) => void)): Result<T, E>;
13
- export declare function xatry<T, E>(func: (() => Exclude<T, Promise<unknown>>) | Promise<Exclude<T, Promise<unknown>>>, handler?: ((error: E) => void)): Promise<Result<T, E>>;
14
- export declare function ok<T>(value: T): Success<T>;
15
- export declare function err<E>(error: E): Failure<E>;
16
- export {};
1
+ export * from './partial.js';
2
+ export * from './result.js';
3
+ export * from './stringify-error.js';
4
+ export * from './try.js';
package/lib/index.js CHANGED
@@ -1,38 +1,4 @@
1
- export function xtry(func, handler) {
2
- try {
3
- const value = func();
4
- return ok(value);
5
- }
6
- catch (error) {
7
- if (handler) {
8
- handler(error);
9
- }
10
- return err(error);
11
- }
12
- }
13
- export async function xatry(func, handler) {
14
- try {
15
- const value = await (func instanceof Promise ? func : Promise.resolve().then(func));
16
- return ok(value);
17
- }
18
- catch (error) {
19
- if (handler) {
20
- handler(error);
21
- }
22
- return err(error);
23
- }
24
- }
25
- export function ok(value) {
26
- return {
27
- fails: false,
28
- value,
29
- error: null,
30
- };
31
- }
32
- export function err(error) {
33
- return {
34
- fails: true,
35
- value: null,
36
- error,
37
- };
38
- }
1
+ export * from './partial.js';
2
+ export * from './result.js';
3
+ export * from './stringify-error.js';
4
+ export * from './try.js';
@@ -0,0 +1,14 @@
1
+ import { type Failure, type Success } from './result.js';
2
+ export type YResult<T, E, S> = Failure<E> | YSuccess<T> | YFailure<S>;
3
+ export type YSuccess<T> = Success<T> & {
4
+ success: true;
5
+ };
6
+ export type YFailure<S> = {
7
+ fails: false;
8
+ success: false;
9
+ type: S;
10
+ value: null;
11
+ error: null;
12
+ };
13
+ export declare function yok<T>(value: T): YSuccess<T>;
14
+ export declare function yerr<S>(type: S): YFailure<S>;
package/lib/partial.js ADDED
@@ -0,0 +1,17 @@
1
+ export function yok(value) {
2
+ return {
3
+ fails: false,
4
+ success: true,
5
+ value,
6
+ error: null,
7
+ };
8
+ }
9
+ export function yerr(type) {
10
+ return {
11
+ fails: false,
12
+ success: false,
13
+ type,
14
+ value: null,
15
+ error: null,
16
+ };
17
+ }
@@ -0,0 +1,13 @@
1
+ export type Success<T> = {
2
+ fails: false;
3
+ value: T;
4
+ error: null;
5
+ };
6
+ export type Failure<E> = {
7
+ fails: true;
8
+ value: null;
9
+ error: E;
10
+ };
11
+ export type Result<T, E> = Success<T> | Failure<E>;
12
+ export declare function ok<T>(value: T): Success<T>;
13
+ export declare function err<E>(error: E): Failure<E>;
package/lib/result.js ADDED
@@ -0,0 +1,14 @@
1
+ export function ok(value) {
2
+ return {
3
+ fails: false,
4
+ value,
5
+ error: null,
6
+ };
7
+ }
8
+ export function err(error) {
9
+ return {
10
+ fails: true,
11
+ value: null,
12
+ error,
13
+ };
14
+ }
@@ -0,0 +1 @@
1
+ export declare function stringifyError(error: unknown): string;
@@ -0,0 +1,18 @@
1
+ export function stringifyError(error) {
2
+ if (typeof error === 'string') {
3
+ return error;
4
+ }
5
+ if (error instanceof Error) {
6
+ return error.message ?? String(error);
7
+ }
8
+ try {
9
+ const json = JSON.stringify(error);
10
+ if (typeof json === 'string') {
11
+ return json;
12
+ }
13
+ }
14
+ catch {
15
+ // fallthrough
16
+ }
17
+ return String(error);
18
+ }
package/lib/try.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { type Result } from './result.js';
2
+ export declare function xtry<T, E = unknown>(func: () => Exclude<T, Promise<unknown>>, handler?: (error: unknown) => E | undefined): E extends void ? Result<T, unknown> : Result<T, E>;
3
+ export declare function xatry<T, E = unknown>(func: (() => Exclude<T, Promise<unknown>>) | Promise<Exclude<T, Promise<unknown>>>, handler?: (error: unknown) => E | undefined): Promise<E extends void ? Result<T, unknown> : Result<T, E>>;
package/lib/try.js ADDED
@@ -0,0 +1,37 @@
1
+ import { err, ok } from './result.js';
2
+ export function xtry(func, handler) {
3
+ try {
4
+ const value = func();
5
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
6
+ return ok(value);
7
+ }
8
+ catch (error) {
9
+ if (handler) {
10
+ const newError = handler(error);
11
+ if (newError !== undefined) {
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
13
+ return err(newError);
14
+ }
15
+ }
16
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
17
+ return err(error);
18
+ }
19
+ }
20
+ export async function xatry(func, handler) {
21
+ try {
22
+ const value = await (func instanceof Promise ? func : Promise.resolve().then(func));
23
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
24
+ return ok(value);
25
+ }
26
+ catch (error) {
27
+ if (handler) {
28
+ const newError = handler(error);
29
+ if (newError !== undefined) {
30
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
31
+ return err(newError);
32
+ }
33
+ }
34
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
35
+ return err(error);
36
+ }
37
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zokugun/xtry",
3
3
  "description": "simple try/catch wrapper returning Result",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "author": {
6
6
  "name": "Baptiste Augrain",
7
7
  "email": "daiyam@zokugun.org"
package/lib/test.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export type FoobarError = {
2
- type: 'FOOBAR';
3
- message: string;
4
- };
package/lib/test.js DELETED
@@ -1,21 +0,0 @@
1
- import { err, ok, xatry } from './index.js';
2
- import { exit } from 'node:process';
3
- import { readFile } from "fs/promises";
4
- async function readConfigFile() {
5
- return readFile("./config.json", "utf-8").then((data) => JSON.parse(data).PORT);
6
- }
7
- async function foobar() {
8
- const { fails, value } = await xatry(readConfigFile());
9
- if (fails) {
10
- return err({ type: 'FOOBAR', message: 'The promise has failed...' });
11
- }
12
- return ok(value);
13
- }
14
- async function main() {
15
- const { fails, value } = await xatry(foobar);
16
- if (fails) {
17
- exit(1);
18
- }
19
- console.log(value);
20
- }
21
- main();