safe-await-lib 0.1.4 → 0.2.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/LICENCE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2025 Chelohub Inc.
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -4,33 +4,39 @@
4
4
  [![npm version](https://img.shields.io/npm/v/safe-await-lib.svg)](https://www.npmjs.com/package/safe-await-lib)
5
5
  [![License](https://img.shields.io/npm/l/safe-await-lib.svg)](https://github.com/chelohubinc/safe-await-lib/blob/main/LICENSE)
6
6
 
7
- Safe async/await utility for handling promises without try/catch.
7
+ Safe async/await utility for handling promises **without try/catch**.
8
8
  Designed for **Node.js**, **TypeScript**, **React**, **React Native**, and **Expo**.
9
9
 
10
10
  ---
11
11
 
12
12
  ## 📌 Why SAFE-AWAIT-LIB?
13
13
 
14
- Traditional async/await patterns require `try/catch` blocks, which can clutter code and make error handling inconsistent.
14
+ Traditional `async / await` requires repetitive `try/catch` blocks, which:
15
15
 
16
- **SAFE-AWAIT-LIB solves this problem by:**
16
+ - clutter business logic
17
+ - encourage inconsistent error handling
18
+ - hide error intent
17
19
 
18
- - Returning a consistent `[SafeError | null, T | null]` tuple for every async operation
19
- - Normalizing errors into a predictable format
20
- - Eliminating boilerplate `try/catch` blocks
21
- - Making code cleaner, safer, and easier to maintain
20
+ **SAFE-AWAIT-LIB** solves this by enforcing a predictable, explicit error-handling pattern.
21
+
22
+ ### Core idea
23
+
24
+ > **Async code should never throw — it should return.**
22
25
 
23
26
  ---
24
27
 
25
- ## 🚀 Features
28
+ ## 🚀 Features (v0.2.0)
26
29
 
27
30
  - Safe execution of async functions and promises
28
- - Standardized error handling with `SafeError` objects
29
- - Fully TypeScript-typed for modern development
31
+ - Always returns a predictable tuple: `[SafeError | null, T | null]`
32
+ - Standardized error normalization
33
+ - Fully typed with TypeScript
30
34
  - Compatible with Node.js, React, React Native, and Expo
31
- - Supports dual module output (ESM + CJS)
32
- - Tree-shakable for modern bundlers
33
- - Future-ready with planned modules: `withTimeout`, `retry`, `all`, `allSettled`, `once`, `strict`, `map`, `unwrap`, `mockSuccess`, `mockError`, `debug`
35
+ - Dual output: **ESM + CJS**
36
+ - Tree-shakable
37
+ - Built-in utilities:
38
+ - `safe.withTimeout` — timeout control for async operations
39
+ - `safe.retry` — retry logic for unstable operations
34
40
 
35
41
  ---
36
42
 
@@ -54,121 +60,160 @@ pnpm add safe-await-lib
54
60
  ```ts
55
61
  import safe from 'safe-await-lib';
56
62
 
57
- // Async function
58
63
  async function fetchData() {
59
- return Promise.resolve('Hello World!');
64
+ return 'Hello World';
60
65
  }
61
66
 
62
- // Using safe
63
67
  const [err, data] = await safe(fetchData);
64
68
 
65
69
  if (err) {
66
- console.error(err.message);
70
+ console.error(err.message, err.code);
67
71
  } else {
68
- console.log(data); // 'Hello World!'
72
+ console.log(data); // "Hello World"
69
73
  }
74
+ ```
75
+
76
+ ### Using a direct promise
70
77
 
71
- // Direct promise usage
72
- const [err2, result] = await safe(Promise.resolve(42));
78
+ ```ts
79
+ const [err, result] = await safe(Promise.resolve(42));
73
80
  ```
74
81
 
75
82
  ---
76
83
 
77
- ## 🛠️ API Overview
84
+ ## 🛠️ API
85
+
86
+ ### `safe(input)`
78
87
 
79
- ### `safe(input: SafeInput<T>): SafeResult<T>`
88
+ ```ts
89
+ safe<T>(input: Promise<T> | (() => T | Promise<T>))
90
+ → Promise<[SafeError | null, T | null]>
91
+ ```
80
92
 
81
- - **input**: `Promise<T>` or `() => T | Promise<T>`
82
- - **returns**: `[SafeError | null, T | null]`
93
+ ---
83
94
 
84
- ### `SafeError` Structure
95
+ ### `SafeError` structure
85
96
 
86
97
  ```ts
87
98
  interface SafeError {
88
99
  message: string; // Human-readable message
89
100
  code: string; // Standardized error code
90
- cause?: unknown; // Original error or additional context
101
+ cause?: unknown; // Original error or context
91
102
  }
92
103
  ```
93
104
 
94
105
  ---
95
106
 
96
- ## 🌱 Advanced Usage Examples
107
+ ## 🌱 Advanced Usage (v0.2.0)
97
108
 
98
- ### Handling multiple async operations
109
+ ### ⏱️ Timeout handling — `safe.withTimeout`
99
110
 
100
111
  ```ts
101
- // Future module: safe.all
102
- const [err, results] = await safe.all([fetchData(), Promise.resolve(10)]);
112
+ const [err, data] = await safe.withTimeout(fetchData(), 1000);
113
+
114
+ if (err?.code === 'TIMEOUT_ERROR') {
115
+ console.error('Operation timed out');
116
+ }
103
117
  ```
104
118
 
105
- ### Using retry (planned in v0.2.0)
119
+ ---
120
+
121
+ ### 🔁 Retry logic — `safe.retry`
106
122
 
107
123
  ```ts
108
- const [err, data] = await safe.retry(() => fetchData(), { attempts: 3, delay: 500 });
124
+ const [err, data] = await safe.retry(
125
+ () => fetchData(),
126
+ {
127
+ retries: 3,
128
+ delayMs: 500,
129
+ onRetry: (error, attempt) => {
130
+ console.log(`Retry ${attempt} failed`, error);
131
+ }
132
+ }
133
+ );
134
+
135
+ if (err) {
136
+ console.error(err.code); // RETRY_FAILED
137
+ }
109
138
  ```
110
139
 
111
140
  ---
112
141
 
113
- ## 📊 Roadmap
142
+ ## Available Today (v0.2.0)
143
+
144
+ SAFE-AWAIT-LIB currently provides:
114
145
 
115
- | Version | Features Planned |
116
- | ------- | ---------------------- |
117
- | v0.2.0 | withTimeout, retry |
118
- | v0.3.0 | all, allSettled |
119
- | v0.4.0 | withContext |
120
- | v0.5.0 | once |
121
- | v0.6.0 | strict |
122
- | v0.7.0 | map, unwrap |
123
- | v0.8.0 | mockSuccess, mockError |
124
- | v0.9.0 | debug |
146
+ - `safe()` core safe async execution
147
+ - `safe.withTimeout()` timeout control
148
+ - `safe.retry()` retry mechanism
149
+
150
+ More utilities will be added incrementally.
125
151
 
126
152
  ---
127
153
 
128
154
  ## 🧪 Development
129
155
 
130
156
  ```bash
131
- # Install dev dependencies
157
+ # Install dependencies
132
158
  npm install
133
159
 
134
- # Run development build with watch
160
+ # Development build (watch mode)
135
161
  npm run dev
136
162
 
137
- # Type check
163
+ # Type checking
138
164
  npm run typecheck
139
165
 
140
166
  # Run tests
141
167
  npm test
142
168
 
143
- # Build for distribution
169
+ # Build for production
144
170
  npm run build
145
171
  ```
146
172
 
147
173
  ---
148
174
 
175
+ ## 📊 Roadmap
176
+
177
+ | Version | Features |
178
+ | ------: | ---------------------- |
179
+ | v0.2.0 | withTimeout, retry ✅ |
180
+ | v0.3.0 | all, allSettled |
181
+ | v0.4.0 | withContext |
182
+ | v0.5.0 | once |
183
+ | v0.6.0 | strict |
184
+ | v0.7.0 | map, unwrap |
185
+ | v0.8.0 | mockSuccess, mockError |
186
+ | v0.9.0 | debug |
187
+
188
+ ---
189
+
149
190
  ## 📝 Contributing
150
191
 
151
- SAFE-AWAIT-LIB welcomes contributions!
192
+ Contributions are welcome.
152
193
 
153
194
  1. Fork the repository
154
- 2. Create a feature branch (`git checkout -b feature/your-feature`)
155
- 3. Run tests locally (`npm test`)
156
- 4. Submit a pull request
195
+ 2. Create a feature branch
196
+ 3. Add tests for your changes
197
+ 4. Ensure `npm test` passes
198
+ 5. Submit a pull request
157
199
 
158
- Please adhere to the existing **TypeScript typings** and **code style**.
200
+ Please respect the existing **TypeScript typings** and **error model**.
159
201
 
160
202
  ---
161
203
 
162
- ## ❓ FAQ / Tips
204
+ ## ❓ FAQ
205
+
206
+ ### Why return a tuple instead of throwing?
207
+
208
+ It enforces explicit error handling and removes runtime surprises.
209
+
210
+ ### Can I use this in React Native or Expo?
163
211
 
164
- - **Why use `[SafeError | null, T | null]` instead of try/catch?**
165
- It ensures consistent error handling and makes async code more readable.
212
+ Yes. SAFE-AWAIT-LIB is runtime-agnostic and fully compatible.
166
213
 
167
- - **Can I use this in React Native / Expo?**
168
- Yes. SAFE-AWAIT-LIB is fully compatible.
214
+ ### Is this production-ready?
169
215
 
170
- - **How do I handle retries or timeouts?**
171
- Future modules like `retry` and `withTimeout` will handle these cases cleanly.
216
+ Yes. The core API is stable and versioned.
172
217
 
173
218
  ---
174
219
 
package/dist/index.d.mts CHANGED
@@ -36,18 +36,89 @@ type SafeInput<T> = Promise<T> | (() => T | Promise<T>);
36
36
  /**
37
37
  * Internal execution engine for SAFE-AWAIT-LIB.
38
38
  *
39
- * This function executes a promise or a function safely and converts
40
- * any thrown or rejected value into a standardized `SafeResult` tuple.
39
+ * Safely executes a promise, synchronous value, or a function returning a value or promise,
40
+ * converting any thrown or rejected value into a standardized `SafeResult` tuple.
41
41
  *
42
- * It is intentionally minimal and side-effect free, serving as the
43
- * foundation for all higher-level modules (retry, timeout, etc.).
42
+ * ## Behavior
43
+ * - Resolves with `[null, result]` if the operation succeeds.
44
+ * - Resolves with `[SafeError, null]` if the operation throws or rejects.
45
+ * - Never throws — always returns `[SafeError | null, T | null]`.
44
46
  *
45
- * @param input - A promise or a function returning a value or a promise
47
+ * @param input - A promise, a synchronous value, or a function returning a value or promise
46
48
  *
47
49
  * @returns A Promise resolving to a `[SafeError | null, T | null]` tuple
48
50
  */
49
51
  declare function coreSafe<T>(input: SafeInput<T>): SafeResult<T>;
50
52
 
53
+ /**
54
+ * Configuration options for the `retry` function.
55
+ */
56
+ interface RetryOptions {
57
+ /**
58
+ * Number of retry attempts (default: 3)
59
+ */
60
+ retries?: number;
61
+ /**
62
+ * Delay in milliseconds between attempts (default: 0)
63
+ */
64
+ delayMs?: number;
65
+ /**
66
+ * Callback invoked after each failed attempt
67
+ */
68
+ onRetry?: (error: unknown, attempt: number) => void;
69
+ }
70
+ /**
71
+ * Retries a failing operation multiple times before giving up.
72
+ *
73
+ * Useful for unstable or flaky operations such as network requests.
74
+ *
75
+ * ## Usage
76
+ * ```ts
77
+ * const [err, data] = await safe.retry(
78
+ * () => fetchData(),
79
+ * { retries: 3, delayMs: 500 }
80
+ * );
81
+ * ```
82
+ *
83
+ * ## Behavior
84
+ * - Retries the operation up to `retries` times
85
+ * - Optional `delayMs` between attempts.
86
+ * - Invokes `onRetry` after each failed attempt.
87
+ * - Never throws — always returns `[SafeError | null, T | null]`.
88
+ * - Returns a `SafeError` with code `RETRY_FAILED` if all attempts fail
89
+ * - Use `err?.code === ERROR_CODES.RETRY_FAILED` to detect a complete retry failure.
90
+ *
91
+ * @param input A function or promise to retry
92
+ * @param options Retry configuration
93
+ */
94
+ declare function retry<T>(input: SafeInput<T>, options?: RetryOptions): SafeResult<T>;
95
+
96
+ /**
97
+ * Executes an operation with a time limit.
98
+ *
99
+ * If the operation does not resolve within the given duration,
100
+ * it fails with a `TIMEOUT_ERROR`.
101
+ *
102
+ * ## Usage
103
+ * ```ts
104
+ * const [err, data] = await safe.withTimeout(fetchData(), 1000);
105
+ *
106
+ * if (err?.code === ERROR_CODES.TIMEOUT) {
107
+ * console.error("Operation timed out");
108
+ * }
109
+ * ```
110
+ *
111
+ * ## Behavior
112
+ * - Resolves with `[null, result]` if the operation completes within the timeout.
113
+ * - Resolves with `[SafeError, null]` if the operation throws or exceeds the timeout.
114
+ * - Never throws — always returns `[SafeError | null, T | null]`.
115
+ * - Use `err?.code === ERROR_CODES.TIMEOUT` to detect timeout errors specifically.
116
+ *
117
+ * @param input A promise or a function returning a value or a promise
118
+ * @param input A promise, a synchronous value, or a function returning a value or a promise
119
+ */
120
+ declare function withTimeout<T>(input: SafeInput<T>, ms: number): SafeResult<T>;
121
+
51
122
  /**
52
123
  * Standardized error codes used across the SAFE-AWAIT-LIB package.
53
124
  *
@@ -91,44 +162,34 @@ interface ErrorWithCause extends Error {
91
162
  }
92
163
 
93
164
  /**
94
- * `safe` is the core function of the SAFE-AWAIT-LIB package.
165
+ * Executes a synchronous or asynchronous operation safely.
95
166
  *
96
- * It safely executes any synchronous or asynchronous operation and
97
- * always returns a predictable tuple instead of throwing errors.
167
+ * `safe` wraps any function, promise, or value and always returns a predictable tuple
168
+ * instead of throwing errors.
98
169
  *
99
- * ## Basic usage
170
+ * ## Usage
100
171
  * ```ts
101
- * import safe from "safe-await-lib";
102
- *
103
- * const [err, data] = await safe(async () => fetchData());
172
+ * const [err, data] = await safe(() => doSomething());
173
+ * const [err2, data2] = await safe(fetchData()); // promise directly
174
+ * const [err3, value] = await safe(42); // synchronous value
104
175
  *
105
176
  * if (err) {
106
- * console.error(err.message, err.code);
107
- * } else {
108
- * console.log(data);
177
+ * console.error(err.code, err.message);
109
178
  * }
110
179
  * ```
111
180
  *
112
- * ## Philosophy
113
- * - No try/catch pollution
114
- * - No unhandled promise rejections
115
- * - Explicit error handling
116
- *
117
- * ## Planned extensions
118
- * - v0.2.0: withTimeout, retry
119
- * - v0.3.0: all, allSettled
120
- * - v0.4.0: withContext
121
- * - v0.5.0: once
122
- * - v0.6.0: strict
123
- * - v0.7.0: map, unwrap
124
- * - v0.8.0: mockSuccess, mockError
125
- * - v0.9.0: debug
126
- *
127
- * ## Return value
128
- * Always returns a tuple:
181
+ * ## Behavior
182
+ * - Never throws — always returns `[SafeError | null, T | null]`
183
+ * - Normalizes any thrown or rejected value into a `SafeError`
184
+ * - Supports promises, synchronous values, or functions returning a value or promise
185
+ *
186
+ * ## Return
129
187
  * - `[null, result]` on success
130
- * - `[SafeError, null]` on failure
188
+ * - `[SafeError, null]` on failure — use `err.code` to identify the error type
131
189
  */
132
- declare const safe: typeof coreSafe;
190
+ declare const safe: typeof coreSafe & {
191
+ withTimeout: typeof withTimeout;
192
+ retry: typeof retry;
193
+ };
133
194
 
134
- export { ERROR_CODES, type ErrorCode, type ErrorWithCause, type SafeError, type SafeInput, type SafeResult, safe as default, safe };
195
+ export { ERROR_CODES, type ErrorCode, type ErrorWithCause, type RetryOptions, type SafeError, type SafeInput, type SafeResult, safe as default, safe };
package/dist/index.d.ts CHANGED
@@ -36,18 +36,89 @@ type SafeInput<T> = Promise<T> | (() => T | Promise<T>);
36
36
  /**
37
37
  * Internal execution engine for SAFE-AWAIT-LIB.
38
38
  *
39
- * This function executes a promise or a function safely and converts
40
- * any thrown or rejected value into a standardized `SafeResult` tuple.
39
+ * Safely executes a promise, synchronous value, or a function returning a value or promise,
40
+ * converting any thrown or rejected value into a standardized `SafeResult` tuple.
41
41
  *
42
- * It is intentionally minimal and side-effect free, serving as the
43
- * foundation for all higher-level modules (retry, timeout, etc.).
42
+ * ## Behavior
43
+ * - Resolves with `[null, result]` if the operation succeeds.
44
+ * - Resolves with `[SafeError, null]` if the operation throws or rejects.
45
+ * - Never throws — always returns `[SafeError | null, T | null]`.
44
46
  *
45
- * @param input - A promise or a function returning a value or a promise
47
+ * @param input - A promise, a synchronous value, or a function returning a value or promise
46
48
  *
47
49
  * @returns A Promise resolving to a `[SafeError | null, T | null]` tuple
48
50
  */
49
51
  declare function coreSafe<T>(input: SafeInput<T>): SafeResult<T>;
50
52
 
53
+ /**
54
+ * Configuration options for the `retry` function.
55
+ */
56
+ interface RetryOptions {
57
+ /**
58
+ * Number of retry attempts (default: 3)
59
+ */
60
+ retries?: number;
61
+ /**
62
+ * Delay in milliseconds between attempts (default: 0)
63
+ */
64
+ delayMs?: number;
65
+ /**
66
+ * Callback invoked after each failed attempt
67
+ */
68
+ onRetry?: (error: unknown, attempt: number) => void;
69
+ }
70
+ /**
71
+ * Retries a failing operation multiple times before giving up.
72
+ *
73
+ * Useful for unstable or flaky operations such as network requests.
74
+ *
75
+ * ## Usage
76
+ * ```ts
77
+ * const [err, data] = await safe.retry(
78
+ * () => fetchData(),
79
+ * { retries: 3, delayMs: 500 }
80
+ * );
81
+ * ```
82
+ *
83
+ * ## Behavior
84
+ * - Retries the operation up to `retries` times
85
+ * - Optional `delayMs` between attempts.
86
+ * - Invokes `onRetry` after each failed attempt.
87
+ * - Never throws — always returns `[SafeError | null, T | null]`.
88
+ * - Returns a `SafeError` with code `RETRY_FAILED` if all attempts fail
89
+ * - Use `err?.code === ERROR_CODES.RETRY_FAILED` to detect a complete retry failure.
90
+ *
91
+ * @param input A function or promise to retry
92
+ * @param options Retry configuration
93
+ */
94
+ declare function retry<T>(input: SafeInput<T>, options?: RetryOptions): SafeResult<T>;
95
+
96
+ /**
97
+ * Executes an operation with a time limit.
98
+ *
99
+ * If the operation does not resolve within the given duration,
100
+ * it fails with a `TIMEOUT_ERROR`.
101
+ *
102
+ * ## Usage
103
+ * ```ts
104
+ * const [err, data] = await safe.withTimeout(fetchData(), 1000);
105
+ *
106
+ * if (err?.code === ERROR_CODES.TIMEOUT) {
107
+ * console.error("Operation timed out");
108
+ * }
109
+ * ```
110
+ *
111
+ * ## Behavior
112
+ * - Resolves with `[null, result]` if the operation completes within the timeout.
113
+ * - Resolves with `[SafeError, null]` if the operation throws or exceeds the timeout.
114
+ * - Never throws — always returns `[SafeError | null, T | null]`.
115
+ * - Use `err?.code === ERROR_CODES.TIMEOUT` to detect timeout errors specifically.
116
+ *
117
+ * @param input A promise or a function returning a value or a promise
118
+ * @param input A promise, a synchronous value, or a function returning a value or a promise
119
+ */
120
+ declare function withTimeout<T>(input: SafeInput<T>, ms: number): SafeResult<T>;
121
+
51
122
  /**
52
123
  * Standardized error codes used across the SAFE-AWAIT-LIB package.
53
124
  *
@@ -91,44 +162,34 @@ interface ErrorWithCause extends Error {
91
162
  }
92
163
 
93
164
  /**
94
- * `safe` is the core function of the SAFE-AWAIT-LIB package.
165
+ * Executes a synchronous or asynchronous operation safely.
95
166
  *
96
- * It safely executes any synchronous or asynchronous operation and
97
- * always returns a predictable tuple instead of throwing errors.
167
+ * `safe` wraps any function, promise, or value and always returns a predictable tuple
168
+ * instead of throwing errors.
98
169
  *
99
- * ## Basic usage
170
+ * ## Usage
100
171
  * ```ts
101
- * import safe from "safe-await-lib";
102
- *
103
- * const [err, data] = await safe(async () => fetchData());
172
+ * const [err, data] = await safe(() => doSomething());
173
+ * const [err2, data2] = await safe(fetchData()); // promise directly
174
+ * const [err3, value] = await safe(42); // synchronous value
104
175
  *
105
176
  * if (err) {
106
- * console.error(err.message, err.code);
107
- * } else {
108
- * console.log(data);
177
+ * console.error(err.code, err.message);
109
178
  * }
110
179
  * ```
111
180
  *
112
- * ## Philosophy
113
- * - No try/catch pollution
114
- * - No unhandled promise rejections
115
- * - Explicit error handling
116
- *
117
- * ## Planned extensions
118
- * - v0.2.0: withTimeout, retry
119
- * - v0.3.0: all, allSettled
120
- * - v0.4.0: withContext
121
- * - v0.5.0: once
122
- * - v0.6.0: strict
123
- * - v0.7.0: map, unwrap
124
- * - v0.8.0: mockSuccess, mockError
125
- * - v0.9.0: debug
126
- *
127
- * ## Return value
128
- * Always returns a tuple:
181
+ * ## Behavior
182
+ * - Never throws — always returns `[SafeError | null, T | null]`
183
+ * - Normalizes any thrown or rejected value into a `SafeError`
184
+ * - Supports promises, synchronous values, or functions returning a value or promise
185
+ *
186
+ * ## Return
129
187
  * - `[null, result]` on success
130
- * - `[SafeError, null]` on failure
188
+ * - `[SafeError, null]` on failure — use `err.code` to identify the error type
131
189
  */
132
- declare const safe: typeof coreSafe;
190
+ declare const safe: typeof coreSafe & {
191
+ withTimeout: typeof withTimeout;
192
+ retry: typeof retry;
193
+ };
133
194
 
134
- export { ERROR_CODES, type ErrorCode, type ErrorWithCause, type SafeError, type SafeInput, type SafeResult, safe as default, safe };
195
+ export { ERROR_CODES, type ErrorCode, type ErrorWithCause, type RetryOptions, type SafeError, type SafeInput, type SafeResult, safe as default, safe };
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ERROR_CODES: () => ERROR_CODES,
23
24
  default: () => index_default,
24
25
  safe: () => safe
25
26
  });
@@ -69,18 +70,60 @@ function formatError(err, defaultCode = ERROR_CODES.UNKNOWN) {
69
70
  async function coreSafe(input) {
70
71
  try {
71
72
  const promise = typeof input === "function" ? input() : input;
72
- const data = await promise;
73
- return [null, data];
73
+ const result = await promise;
74
+ return [null, result];
74
75
  } catch (error) {
75
76
  return [formatError(error), null];
76
77
  }
77
78
  }
78
79
 
80
+ // src/modules/retry.ts
81
+ function sleep(ms) {
82
+ return new Promise((resolve) => setTimeout(resolve, ms));
83
+ }
84
+ async function retry(input, options = {}) {
85
+ const {
86
+ retries = 3,
87
+ delayMs = 0,
88
+ onRetry
89
+ } = options;
90
+ let lastError;
91
+ for (let attempt = 1; attempt <= retries; attempt++) {
92
+ try {
93
+ const result = typeof input === "function" ? await input() : await input;
94
+ return [null, result];
95
+ } catch (err) {
96
+ lastError = err;
97
+ onRetry == null ? void 0 : onRetry(err, attempt);
98
+ if (attempt < retries && delayMs > 0) {
99
+ await sleep(delayMs);
100
+ }
101
+ }
102
+ }
103
+ return [formatError(lastError, ERROR_CODES.RETRY_FAILED), null];
104
+ }
105
+
106
+ // src/modules/timeout.ts
107
+ async function withTimeout(input, ms) {
108
+ let timer;
109
+ try {
110
+ const promise = typeof input === "function" ? input() : input;
111
+ const timeoutPromise = new Promise((_, reject) => {
112
+ timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`), ERROR_CODES.TIMEOUT)), ms);
113
+ });
114
+ const result = await Promise.race([promise, timeoutPromise]);
115
+ return [null, result];
116
+ } catch (error) {
117
+ return [error, null];
118
+ } finally {
119
+ clearTimeout(timer);
120
+ }
121
+ }
122
+
79
123
  // src/index.ts
80
124
  var safe = Object.assign(coreSafe, {
81
- // v0.2.0
82
- // withTimeout,
83
- // retry,
125
+ withTimeout,
126
+ retry
84
127
  // v0.3.0
85
128
  // all,
86
129
  // allSettled,
@@ -102,6 +145,7 @@ var safe = Object.assign(coreSafe, {
102
145
  var index_default = safe;
103
146
  // Annotate the CommonJS export names for ESM import in node:
104
147
  0 && (module.exports = {
148
+ ERROR_CODES,
105
149
  safe
106
150
  });
107
151
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors/codes.ts","../src/errors/formatter.ts","../src/core/safe.ts"],"sourcesContent":["import { coreSafe } from \"./core/safe\";\n\n/**\n * `safe` is the core function of the SAFE-AWAIT-LIB package.\n *\n * It safely executes any synchronous or asynchronous operation and\n * always returns a predictable tuple instead of throwing errors.\n *\n * ## Basic usage\n * ```ts\n * import safe from \"safe-await-lib\";\n *\n * const [err, data] = await safe(async () => fetchData());\n *\n * if (err) {\n * console.error(err.message, err.code);\n * } else {\n * console.log(data);\n * }\n * ```\n *\n * ## Philosophy\n * - No try/catch pollution\n * - No unhandled promise rejections\n * - Explicit error handling\n *\n * ## Planned extensions\n * - v0.2.0: withTimeout, retry\n * - v0.3.0: all, allSettled\n * - v0.4.0: withContext\n * - v0.5.0: once\n * - v0.6.0: strict\n * - v0.7.0: map, unwrap\n * - v0.8.0: mockSuccess, mockError\n * - v0.9.0: debug\n *\n * ## Return value\n * Always returns a tuple:\n * - `[null, result]` on success\n * - `[SafeError, null]` on failure\n */\nexport const safe = Object.assign(coreSafe, {\n // v0.2.0\n // withTimeout,\n // retry,\n\n // v0.3.0\n // all,\n // allSettled,\n\n // v0.4.0\n // withContext,\n\n // v0.5.0\n // once,\n\n // v0.6.0\n // strict,\n\n // v0.7.0\n // map,\n // unwrap,\n\n // v0.8.0\n // mockSuccess,\n // mockError,\n\n // v0.9.0\n // debug,\n});\n\nexport type { SafeError, SafeResult, SafeInput } from \"./core/type\";\n\nexport type { ERROR_CODES, ErrorCode, ErrorWithCause } from \"./errors/codes\";\n\nexport default safe;","/**\n * Standardized error codes used across the SAFE-AWAIT-LIB package.\n *\n * These codes allow consumers to reliably identify the nature\n * of an error without relying on string comparison of messages.\n *\n * Each module of the package should use one of these codes\n * when returning or normalizing an error.\n */\nexport const ERROR_CODES = {\n /** Fallback error for unknown or unhandled failures */\n UNKNOWN: 'UNKNOWN_ERROR',\n\n /** Thrown when an operation exceeds a configured timeout */\n TIMEOUT: 'TIMEOUT_ERROR',\n\n /** Used when all retry attempts have failed */\n RETRY_FAILED: 'RETRY_FAILED',\n\n /** Used when an operation is explicitly aborted or cancelled */\n ABORTED: 'ABORT_ERROR',\n\n /** Used when input validation fails */\n VALIDATION: 'VALIDATION_ERROR',\n\n /** Used when a function guarded by `once()` is called more than once */\n EXECUTION_ONCE: 'ALREADY_EXECUTED'\n} as const;\n\n/**\n * Union type of all supported error codes.\n *\n * This type ensures strong typing and prevents the use\n * of unsupported or custom error codes across the package.\n */\nexport type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];\n\n/**\n * Internal helper interface to support the `cause` property\n * on Error objects in environments where it is not\n * yet fully supported or typed.\n *\n * This allows SAFE-AWAIT-LIB to preserve the original error\n * while still returning a normalized SafeError object.\n */\nexport interface ErrorWithCause extends Error {\n cause?: unknown;\n}\n","import { SafeError } from '../core/type';\nimport { ERROR_CODES, ErrorCode, ErrorWithCause } from './codes';\n\n/**\n * Normalizes any thrown value into a `SafeError`.\n *\n * This function is the foundation of SAFE-AWAIT-LIB's error-handling strategy.\n * It guarantees that all errors returned by the library follow the same\n * predictable structure, regardless of what was originally thrown.\n *\n * Supported inputs:\n * - `Error` instances (native or custom)\n * - string errors\n * - unknown or non-error values\n *\n * ## Normalization rules\n * - Preserves the original error message when possible\n * - Uses a standardized error code\n * - Keeps the original error in the `cause` field when available\n *\n * @param err - Any value thrown or rejected by an operation\n * @param defaultCode - Fallback error code when none is provided\n *\n * @returns A normalized `SafeError` object\n */\nexport function formatError(\n err: unknown,\n defaultCode: ErrorCode = ERROR_CODES.UNKNOWN\n): SafeError {\n if (err instanceof Error) {\n const errorWithCause = err as ErrorWithCause;\n\n return {\n message: err.message,\n code: (err as any).code || defaultCode,\n cause: errorWithCause.cause ?? err\n };\n }\n\n if (typeof err === 'string') {\n return {\n message: err,\n code: defaultCode,\n };\n }\n\n return {\n message: 'An unexpected error occurred',\n code: defaultCode,\n cause: err\n };\n}\n","import { formatError } from \"../errors/formatter\";\nimport { SafeInput, SafeResult } from \"./type\";\n\n/**\n * Internal execution engine for SAFE-AWAIT-LIB.\n *\n * This function executes a promise or a function safely and converts\n * any thrown or rejected value into a standardized `SafeResult` tuple.\n *\n * It is intentionally minimal and side-effect free, serving as the\n * foundation for all higher-level modules (retry, timeout, etc.).\n *\n * @param input - A promise or a function returning a value or a promise\n *\n * @returns A Promise resolving to a `[SafeError | null, T | null]` tuple\n */\nexport async function coreSafe<T>(input: SafeInput<T>): SafeResult<T> {\n try {\n const promise = typeof input === 'function' ? input() : input;\n const data = await promise;\n\n return [null, data];\n } catch (error) {\n return [formatError(error), null];\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSO,IAAM,cAAc;AAAA;AAAA,EAEvB,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA;AAAA,EAGT,cAAc;AAAA;AAAA,EAGd,SAAS;AAAA;AAAA,EAGT,YAAY;AAAA;AAAA,EAGZ,gBAAgB;AACpB;;;ACFO,SAAS,YACZ,KACA,cAAyB,YAAY,SAC5B;AA5Bb;AA6BI,MAAI,eAAe,OAAO;AACtB,UAAM,iBAAiB;AAEvB,WAAO;AAAA,MACH,SAAS,IAAI;AAAA,MACb,MAAO,IAAY,QAAQ;AAAA,MAC3B,QAAO,oBAAe,UAAf,YAAwB;AAAA,IACnC;AAAA,EACJ;AAEA,MAAI,OAAO,QAAQ,UAAU;AACzB,WAAO;AAAA,MACH,SAAS;AAAA,MACT,MAAM;AAAA,IACV;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,EACX;AACJ;;;ACnCA,eAAsB,SAAY,OAAoC;AAClE,MAAI;AACA,UAAM,UAAU,OAAO,UAAU,aAAa,MAAM,IAAI;AACxD,UAAM,OAAO,MAAM;AAEnB,WAAO,CAAC,MAAM,IAAI;AAAA,EACtB,SAAS,OAAO;AACZ,WAAO,CAAC,YAAY,KAAK,GAAG,IAAI;AAAA,EACpC;AACJ;;;AHgBO,IAAM,OAAO,OAAO,OAAO,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4B5C,CAAC;AAMD,IAAO,gBAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors/codes.ts","../src/errors/formatter.ts","../src/core/safe.ts","../src/modules/retry.ts","../src/modules/timeout.ts"],"sourcesContent":["import { coreSafe } from \"./core/safe\";\nimport { retry } from \"./modules/retry\";\nimport { withTimeout } from \"./modules/timeout\";\n\n/**\n * Executes a synchronous or asynchronous operation safely.\n *\n * `safe` wraps any function, promise, or value and always returns a predictable tuple\n * instead of throwing errors.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe(() => doSomething());\n * const [err2, data2] = await safe(fetchData()); // promise directly\n * const [err3, value] = await safe(42); // synchronous value\n *\n * if (err) {\n * console.error(err.code, err.message);\n * }\n * ```\n *\n * ## Behavior\n * - Never throws — always returns `[SafeError | null, T | null]`\n * - Normalizes any thrown or rejected value into a `SafeError`\n * - Supports promises, synchronous values, or functions returning a value or promise\n *\n * ## Return\n * - `[null, result]` on success\n * - `[SafeError, null]` on failure — use `err.code` to identify the error type\n */\nexport const safe = Object.assign(coreSafe, {\n withTimeout,\n retry,\n\n // v0.3.0\n // all,\n // allSettled,\n\n // v0.4.0\n // withContext,\n\n // v0.5.0\n // once,\n\n // v0.6.0\n // strict,\n\n // v0.7.0\n // map,\n // unwrap,\n\n // v0.8.0\n // mockSuccess,\n // mockError,\n\n // v0.9.0\n // debug,\n});\n\nexport type {\n SafeError,\n SafeResult,\n SafeInput\n} from \"./core/type\";\n\nexport type {\n ErrorCode,\n ErrorWithCause\n} from \"./errors/codes\";\n\nexport type {\n RetryOptions\n} from \"./modules/retry\";\n\nexport { ERROR_CODES } from \"./errors/codes\";\n\nexport default safe;","/**\n * Standardized error codes used across the SAFE-AWAIT-LIB package.\n *\n * These codes allow consumers to reliably identify the nature\n * of an error without relying on string comparison of messages.\n *\n * Each module of the package should use one of these codes\n * when returning or normalizing an error.\n */\nexport const ERROR_CODES = {\n /** Fallback error for unknown or unhandled failures */\n UNKNOWN: 'UNKNOWN_ERROR',\n\n /** Thrown when an operation exceeds a configured timeout */\n TIMEOUT: 'TIMEOUT_ERROR',\n\n /** Used when all retry attempts have failed */\n RETRY_FAILED: 'RETRY_FAILED',\n\n /** Used when an operation is explicitly aborted or cancelled */\n ABORTED: 'ABORT_ERROR',\n\n /** Used when input validation fails */\n VALIDATION: 'VALIDATION_ERROR',\n\n /** Used when a function guarded by `once()` is called more than once */\n EXECUTION_ONCE: 'ALREADY_EXECUTED'\n} as const;\n\n/**\n * Union type of all supported error codes.\n *\n * This type ensures strong typing and prevents the use\n * of unsupported or custom error codes across the package.\n */\nexport type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];\n\n/**\n * Internal helper interface to support the `cause` property\n * on Error objects in environments where it is not\n * yet fully supported or typed.\n *\n * This allows SAFE-AWAIT-LIB to preserve the original error\n * while still returning a normalized SafeError object.\n */\nexport interface ErrorWithCause extends Error {\n cause?: unknown;\n}\n","import { SafeError } from '../core/type';\nimport { ERROR_CODES, ErrorCode, ErrorWithCause } from './codes';\n\n/**\n * Normalizes any thrown value into a `SafeError`.\n *\n * This function is the foundation of SAFE-AWAIT-LIB's error-handling strategy.\n * It guarantees that all errors returned by the library follow the same\n * predictable structure, regardless of what was originally thrown.\n *\n * Supported inputs:\n * - `Error` instances (native or custom)\n * - string errors\n * - unknown or non-error values\n *\n * ## Normalization rules\n * - Preserves the original error message when possible\n * - Uses a standardized error code\n * - Keeps the original error in the `cause` field when available\n *\n * @param err - Any value thrown or rejected by an operation\n * @param defaultCode - Fallback error code when none is provided\n *\n * @returns A normalized `SafeError` object\n */\nexport function formatError(\n err: unknown,\n defaultCode: ErrorCode = ERROR_CODES.UNKNOWN\n): SafeError {\n if (err instanceof Error) {\n const errorWithCause = err as ErrorWithCause;\n\n return {\n message: err.message,\n code: (err as any).code || defaultCode,\n cause: errorWithCause.cause ?? err\n };\n }\n\n if (typeof err === 'string') {\n return {\n message: err,\n code: defaultCode,\n };\n }\n\n return {\n message: 'An unexpected error occurred',\n code: defaultCode,\n cause: err\n };\n}\n","import { formatError } from \"../errors/formatter\";\nimport { SafeInput, SafeResult } from \"./type\";\n\n/**\n * Internal execution engine for SAFE-AWAIT-LIB.\n *\n * Safely executes a promise, synchronous value, or a function returning a value or promise,\n * converting any thrown or rejected value into a standardized `SafeResult` tuple.\n *\n * ## Behavior\n * - Resolves with `[null, result]` if the operation succeeds.\n * - Resolves with `[SafeError, null]` if the operation throws or rejects.\n * - Never throws — always returns `[SafeError | null, T | null]`.\n *\n * @param input - A promise, a synchronous value, or a function returning a value or promise\n *\n * @returns A Promise resolving to a `[SafeError | null, T | null]` tuple\n */\nexport async function coreSafe<T>(input: SafeInput<T>): SafeResult<T> {\n try {\n const promise = typeof input === 'function' ? input() : input;\n const result = await promise;\n return [null, result];\n } catch (error) {\n return [formatError(error), null];\n }\n}\n","import { SafeInput, SafeResult } from \"../core/type\";\nimport { ERROR_CODES } from \"../errors/codes\";\nimport { formatError } from \"../errors/formatter\";\n\n/**\n * Configuration options for the `retry` function.\n */\nexport interface RetryOptions {\n /**\n * Number of retry attempts (default: 3)\n */\n retries?: number;\n\n /**\n * Delay in milliseconds between attempts (default: 0)\n */\n delayMs?: number;\n\n /**\n * Callback invoked after each failed attempt\n */\n onRetry?: (error: unknown, attempt: number) => void;\n}\n\n\nfunction sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Retries a failing operation multiple times before giving up.\n *\n * Useful for unstable or flaky operations such as network requests.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe.retry(\n * () => fetchData(),\n * { retries: 3, delayMs: 500 }\n * );\n * ```\n *\n * ## Behavior\n * - Retries the operation up to `retries` times\n * - Optional `delayMs` between attempts.\n * - Invokes `onRetry` after each failed attempt.\n * - Never throws — always returns `[SafeError | null, T | null]`.\n * - Returns a `SafeError` with code `RETRY_FAILED` if all attempts fail\n * - Use `err?.code === ERROR_CODES.RETRY_FAILED` to detect a complete retry failure.\n *\n * @param input A function or promise to retry\n * @param options Retry configuration\n */\nexport async function retry<T>(input: SafeInput<T>, options: RetryOptions = {}): SafeResult<T> {\n const {\n retries = 3,\n delayMs = 0,\n onRetry\n } = options;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= retries; attempt++) {\n try {\n const result = typeof input === 'function' ? await input() : await input;\n return [null, result];\n } catch (err) {\n lastError = err;\n onRetry?.(err, attempt);\n if (attempt < retries && delayMs > 0) {\n await sleep(delayMs);\n }\n }\n }\n\n return [formatError(lastError, ERROR_CODES.RETRY_FAILED), null];\n}","import { ERROR_CODES } from '../errors/codes';\nimport { formatError } from '../errors/formatter';\nimport { SafeInput, SafeResult } from './../core/type';\n\n/**\n * Executes an operation with a time limit.\n *\n * If the operation does not resolve within the given duration,\n * it fails with a `TIMEOUT_ERROR`.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe.withTimeout(fetchData(), 1000);\n *\n * if (err?.code === ERROR_CODES.TIMEOUT) {\n * console.error(\"Operation timed out\");\n * }\n * ```\n *\n * ## Behavior\n * - Resolves with `[null, result]` if the operation completes within the timeout.\n * - Resolves with `[SafeError, null]` if the operation throws or exceeds the timeout.\n * - Never throws — always returns `[SafeError | null, T | null]`.\n * - Use `err?.code === ERROR_CODES.TIMEOUT` to detect timeout errors specifically.\n *\n * @param input A promise or a function returning a value or a promise\n * @param input A promise, a synchronous value, or a function returning a value or a promise\n */\nexport async function withTimeout<T>(input: SafeInput<T>, ms: number): SafeResult<T> {\n let timer: ReturnType<typeof setTimeout>;\n\n try {\n const promise = typeof input === 'function' ? input() : input;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`), ERROR_CODES.TIMEOUT)), ms);\n });\n\n const result = await Promise.race([promise, timeoutPromise]);\n return [null, result];\n } catch (error: any) {\n return [error, null];\n } finally {\n clearTimeout(timer!);\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSO,IAAM,cAAc;AAAA;AAAA,EAEvB,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA;AAAA,EAGT,cAAc;AAAA;AAAA,EAGd,SAAS;AAAA;AAAA,EAGT,YAAY;AAAA;AAAA,EAGZ,gBAAgB;AACpB;;;ACFO,SAAS,YACZ,KACA,cAAyB,YAAY,SAC5B;AA5Bb;AA6BI,MAAI,eAAe,OAAO;AACtB,UAAM,iBAAiB;AAEvB,WAAO;AAAA,MACH,SAAS,IAAI;AAAA,MACb,MAAO,IAAY,QAAQ;AAAA,MAC3B,QAAO,oBAAe,UAAf,YAAwB;AAAA,IACnC;AAAA,EACJ;AAEA,MAAI,OAAO,QAAQ,UAAU;AACzB,WAAO;AAAA,MACH,SAAS;AAAA,MACT,MAAM;AAAA,IACV;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,EACX;AACJ;;;ACjCA,eAAsB,SAAY,OAAoC;AAClE,MAAI;AACA,UAAM,UAAU,OAAO,UAAU,aAAa,MAAM,IAAI;AACxD,UAAM,SAAS,MAAM;AACrB,WAAO,CAAC,MAAM,MAAM;AAAA,EACxB,SAAS,OAAO;AACZ,WAAO,CAAC,YAAY,KAAK,GAAG,IAAI;AAAA,EACpC;AACJ;;;ACDA,SAAS,MAAM,IAAY;AACvB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACzD;AA0BA,eAAsB,MAAS,OAAqB,UAAwB,CAAC,GAAkB;AAC3F,QAAM;AAAA,IACF,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,EACJ,IAAI;AAEJ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,SAAS,WAAW;AACjD,QAAI;AACA,YAAM,SAAS,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI,MAAM;AACnE,aAAO,CAAC,MAAM,MAAM;AAAA,IACxB,SAAS,KAAK;AACV,kBAAY;AACZ,yCAAU,KAAK;AACf,UAAI,UAAU,WAAW,UAAU,GAAG;AAClC,cAAM,MAAM,OAAO;AAAA,MACvB;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO,CAAC,YAAY,WAAW,YAAY,YAAY,GAAG,IAAI;AAClE;;;AChDA,eAAsB,YAAe,OAAqB,IAA2B;AACjF,MAAI;AAEJ,MAAI;AACA,UAAM,UAAU,OAAO,UAAU,aAAa,MAAM,IAAI;AAExD,UAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACrD,cAAQ,WAAW,MAAM,OAAO,YAAY,IAAI,MAAM,iBAAiB,EAAE,IAAI,GAAG,YAAY,OAAO,CAAC,GAAG,EAAE;AAAA,IAC7G,CAAC;AAED,UAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAC3D,WAAO,CAAC,MAAM,MAAM;AAAA,EACxB,SAAS,OAAY;AACjB,WAAO,CAAC,OAAO,IAAI;AAAA,EACvB,UAAE;AACE,iBAAa,KAAM;AAAA,EACvB;AACJ;;;ALfO,IAAM,OAAO,OAAO,OAAO,UAAU;AAAA,EACxC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBJ,CAAC;AAmBD,IAAO,gBAAQ;","names":[]}
package/dist/index.mjs CHANGED
@@ -42,18 +42,60 @@ function formatError(err, defaultCode = ERROR_CODES.UNKNOWN) {
42
42
  async function coreSafe(input) {
43
43
  try {
44
44
  const promise = typeof input === "function" ? input() : input;
45
- const data = await promise;
46
- return [null, data];
45
+ const result = await promise;
46
+ return [null, result];
47
47
  } catch (error) {
48
48
  return [formatError(error), null];
49
49
  }
50
50
  }
51
51
 
52
+ // src/modules/retry.ts
53
+ function sleep(ms) {
54
+ return new Promise((resolve) => setTimeout(resolve, ms));
55
+ }
56
+ async function retry(input, options = {}) {
57
+ const {
58
+ retries = 3,
59
+ delayMs = 0,
60
+ onRetry
61
+ } = options;
62
+ let lastError;
63
+ for (let attempt = 1; attempt <= retries; attempt++) {
64
+ try {
65
+ const result = typeof input === "function" ? await input() : await input;
66
+ return [null, result];
67
+ } catch (err) {
68
+ lastError = err;
69
+ onRetry == null ? void 0 : onRetry(err, attempt);
70
+ if (attempt < retries && delayMs > 0) {
71
+ await sleep(delayMs);
72
+ }
73
+ }
74
+ }
75
+ return [formatError(lastError, ERROR_CODES.RETRY_FAILED), null];
76
+ }
77
+
78
+ // src/modules/timeout.ts
79
+ async function withTimeout(input, ms) {
80
+ let timer;
81
+ try {
82
+ const promise = typeof input === "function" ? input() : input;
83
+ const timeoutPromise = new Promise((_, reject) => {
84
+ timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`), ERROR_CODES.TIMEOUT)), ms);
85
+ });
86
+ const result = await Promise.race([promise, timeoutPromise]);
87
+ return [null, result];
88
+ } catch (error) {
89
+ return [error, null];
90
+ } finally {
91
+ clearTimeout(timer);
92
+ }
93
+ }
94
+
52
95
  // src/index.ts
53
96
  var safe = Object.assign(coreSafe, {
54
- // v0.2.0
55
- // withTimeout,
56
- // retry,
97
+ withTimeout,
98
+ retry
57
99
  // v0.3.0
58
100
  // all,
59
101
  // allSettled,
@@ -74,6 +116,7 @@ var safe = Object.assign(coreSafe, {
74
116
  });
75
117
  var index_default = safe;
76
118
  export {
119
+ ERROR_CODES,
77
120
  index_default as default,
78
121
  safe
79
122
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/codes.ts","../src/errors/formatter.ts","../src/core/safe.ts","../src/index.ts"],"sourcesContent":["/**\n * Standardized error codes used across the SAFE-AWAIT-LIB package.\n *\n * These codes allow consumers to reliably identify the nature\n * of an error without relying on string comparison of messages.\n *\n * Each module of the package should use one of these codes\n * when returning or normalizing an error.\n */\nexport const ERROR_CODES = {\n /** Fallback error for unknown or unhandled failures */\n UNKNOWN: 'UNKNOWN_ERROR',\n\n /** Thrown when an operation exceeds a configured timeout */\n TIMEOUT: 'TIMEOUT_ERROR',\n\n /** Used when all retry attempts have failed */\n RETRY_FAILED: 'RETRY_FAILED',\n\n /** Used when an operation is explicitly aborted or cancelled */\n ABORTED: 'ABORT_ERROR',\n\n /** Used when input validation fails */\n VALIDATION: 'VALIDATION_ERROR',\n\n /** Used when a function guarded by `once()` is called more than once */\n EXECUTION_ONCE: 'ALREADY_EXECUTED'\n} as const;\n\n/**\n * Union type of all supported error codes.\n *\n * This type ensures strong typing and prevents the use\n * of unsupported or custom error codes across the package.\n */\nexport type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];\n\n/**\n * Internal helper interface to support the `cause` property\n * on Error objects in environments where it is not\n * yet fully supported or typed.\n *\n * This allows SAFE-AWAIT-LIB to preserve the original error\n * while still returning a normalized SafeError object.\n */\nexport interface ErrorWithCause extends Error {\n cause?: unknown;\n}\n","import { SafeError } from '../core/type';\nimport { ERROR_CODES, ErrorCode, ErrorWithCause } from './codes';\n\n/**\n * Normalizes any thrown value into a `SafeError`.\n *\n * This function is the foundation of SAFE-AWAIT-LIB's error-handling strategy.\n * It guarantees that all errors returned by the library follow the same\n * predictable structure, regardless of what was originally thrown.\n *\n * Supported inputs:\n * - `Error` instances (native or custom)\n * - string errors\n * - unknown or non-error values\n *\n * ## Normalization rules\n * - Preserves the original error message when possible\n * - Uses a standardized error code\n * - Keeps the original error in the `cause` field when available\n *\n * @param err - Any value thrown or rejected by an operation\n * @param defaultCode - Fallback error code when none is provided\n *\n * @returns A normalized `SafeError` object\n */\nexport function formatError(\n err: unknown,\n defaultCode: ErrorCode = ERROR_CODES.UNKNOWN\n): SafeError {\n if (err instanceof Error) {\n const errorWithCause = err as ErrorWithCause;\n\n return {\n message: err.message,\n code: (err as any).code || defaultCode,\n cause: errorWithCause.cause ?? err\n };\n }\n\n if (typeof err === 'string') {\n return {\n message: err,\n code: defaultCode,\n };\n }\n\n return {\n message: 'An unexpected error occurred',\n code: defaultCode,\n cause: err\n };\n}\n","import { formatError } from \"../errors/formatter\";\nimport { SafeInput, SafeResult } from \"./type\";\n\n/**\n * Internal execution engine for SAFE-AWAIT-LIB.\n *\n * This function executes a promise or a function safely and converts\n * any thrown or rejected value into a standardized `SafeResult` tuple.\n *\n * It is intentionally minimal and side-effect free, serving as the\n * foundation for all higher-level modules (retry, timeout, etc.).\n *\n * @param input - A promise or a function returning a value or a promise\n *\n * @returns A Promise resolving to a `[SafeError | null, T | null]` tuple\n */\nexport async function coreSafe<T>(input: SafeInput<T>): SafeResult<T> {\n try {\n const promise = typeof input === 'function' ? input() : input;\n const data = await promise;\n\n return [null, data];\n } catch (error) {\n return [formatError(error), null];\n }\n}\n","import { coreSafe } from \"./core/safe\";\n\n/**\n * `safe` is the core function of the SAFE-AWAIT-LIB package.\n *\n * It safely executes any synchronous or asynchronous operation and\n * always returns a predictable tuple instead of throwing errors.\n *\n * ## Basic usage\n * ```ts\n * import safe from \"safe-await-lib\";\n *\n * const [err, data] = await safe(async () => fetchData());\n *\n * if (err) {\n * console.error(err.message, err.code);\n * } else {\n * console.log(data);\n * }\n * ```\n *\n * ## Philosophy\n * - No try/catch pollution\n * - No unhandled promise rejections\n * - Explicit error handling\n *\n * ## Planned extensions\n * - v0.2.0: withTimeout, retry\n * - v0.3.0: all, allSettled\n * - v0.4.0: withContext\n * - v0.5.0: once\n * - v0.6.0: strict\n * - v0.7.0: map, unwrap\n * - v0.8.0: mockSuccess, mockError\n * - v0.9.0: debug\n *\n * ## Return value\n * Always returns a tuple:\n * - `[null, result]` on success\n * - `[SafeError, null]` on failure\n */\nexport const safe = Object.assign(coreSafe, {\n // v0.2.0\n // withTimeout,\n // retry,\n\n // v0.3.0\n // all,\n // allSettled,\n\n // v0.4.0\n // withContext,\n\n // v0.5.0\n // once,\n\n // v0.6.0\n // strict,\n\n // v0.7.0\n // map,\n // unwrap,\n\n // v0.8.0\n // mockSuccess,\n // mockError,\n\n // v0.9.0\n // debug,\n});\n\nexport type { SafeError, SafeResult, SafeInput } from \"./core/type\";\n\nexport type { ERROR_CODES, ErrorCode, ErrorWithCause } from \"./errors/codes\";\n\nexport default safe;"],"mappings":";AASO,IAAM,cAAc;AAAA;AAAA,EAEvB,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA;AAAA,EAGT,cAAc;AAAA;AAAA,EAGd,SAAS;AAAA;AAAA,EAGT,YAAY;AAAA;AAAA,EAGZ,gBAAgB;AACpB;;;ACFO,SAAS,YACZ,KACA,cAAyB,YAAY,SAC5B;AA5Bb;AA6BI,MAAI,eAAe,OAAO;AACtB,UAAM,iBAAiB;AAEvB,WAAO;AAAA,MACH,SAAS,IAAI;AAAA,MACb,MAAO,IAAY,QAAQ;AAAA,MAC3B,QAAO,oBAAe,UAAf,YAAwB;AAAA,IACnC;AAAA,EACJ;AAEA,MAAI,OAAO,QAAQ,UAAU;AACzB,WAAO;AAAA,MACH,SAAS;AAAA,MACT,MAAM;AAAA,IACV;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,EACX;AACJ;;;ACnCA,eAAsB,SAAY,OAAoC;AAClE,MAAI;AACA,UAAM,UAAU,OAAO,UAAU,aAAa,MAAM,IAAI;AACxD,UAAM,OAAO,MAAM;AAEnB,WAAO,CAAC,MAAM,IAAI;AAAA,EACtB,SAAS,OAAO;AACZ,WAAO,CAAC,YAAY,KAAK,GAAG,IAAI;AAAA,EACpC;AACJ;;;ACgBO,IAAM,OAAO,OAAO,OAAO,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4B5C,CAAC;AAMD,IAAO,gBAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/errors/codes.ts","../src/errors/formatter.ts","../src/core/safe.ts","../src/modules/retry.ts","../src/modules/timeout.ts","../src/index.ts"],"sourcesContent":["/**\n * Standardized error codes used across the SAFE-AWAIT-LIB package.\n *\n * These codes allow consumers to reliably identify the nature\n * of an error without relying on string comparison of messages.\n *\n * Each module of the package should use one of these codes\n * when returning or normalizing an error.\n */\nexport const ERROR_CODES = {\n /** Fallback error for unknown or unhandled failures */\n UNKNOWN: 'UNKNOWN_ERROR',\n\n /** Thrown when an operation exceeds a configured timeout */\n TIMEOUT: 'TIMEOUT_ERROR',\n\n /** Used when all retry attempts have failed */\n RETRY_FAILED: 'RETRY_FAILED',\n\n /** Used when an operation is explicitly aborted or cancelled */\n ABORTED: 'ABORT_ERROR',\n\n /** Used when input validation fails */\n VALIDATION: 'VALIDATION_ERROR',\n\n /** Used when a function guarded by `once()` is called more than once */\n EXECUTION_ONCE: 'ALREADY_EXECUTED'\n} as const;\n\n/**\n * Union type of all supported error codes.\n *\n * This type ensures strong typing and prevents the use\n * of unsupported or custom error codes across the package.\n */\nexport type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES];\n\n/**\n * Internal helper interface to support the `cause` property\n * on Error objects in environments where it is not\n * yet fully supported or typed.\n *\n * This allows SAFE-AWAIT-LIB to preserve the original error\n * while still returning a normalized SafeError object.\n */\nexport interface ErrorWithCause extends Error {\n cause?: unknown;\n}\n","import { SafeError } from '../core/type';\nimport { ERROR_CODES, ErrorCode, ErrorWithCause } from './codes';\n\n/**\n * Normalizes any thrown value into a `SafeError`.\n *\n * This function is the foundation of SAFE-AWAIT-LIB's error-handling strategy.\n * It guarantees that all errors returned by the library follow the same\n * predictable structure, regardless of what was originally thrown.\n *\n * Supported inputs:\n * - `Error` instances (native or custom)\n * - string errors\n * - unknown or non-error values\n *\n * ## Normalization rules\n * - Preserves the original error message when possible\n * - Uses a standardized error code\n * - Keeps the original error in the `cause` field when available\n *\n * @param err - Any value thrown or rejected by an operation\n * @param defaultCode - Fallback error code when none is provided\n *\n * @returns A normalized `SafeError` object\n */\nexport function formatError(\n err: unknown,\n defaultCode: ErrorCode = ERROR_CODES.UNKNOWN\n): SafeError {\n if (err instanceof Error) {\n const errorWithCause = err as ErrorWithCause;\n\n return {\n message: err.message,\n code: (err as any).code || defaultCode,\n cause: errorWithCause.cause ?? err\n };\n }\n\n if (typeof err === 'string') {\n return {\n message: err,\n code: defaultCode,\n };\n }\n\n return {\n message: 'An unexpected error occurred',\n code: defaultCode,\n cause: err\n };\n}\n","import { formatError } from \"../errors/formatter\";\nimport { SafeInput, SafeResult } from \"./type\";\n\n/**\n * Internal execution engine for SAFE-AWAIT-LIB.\n *\n * Safely executes a promise, synchronous value, or a function returning a value or promise,\n * converting any thrown or rejected value into a standardized `SafeResult` tuple.\n *\n * ## Behavior\n * - Resolves with `[null, result]` if the operation succeeds.\n * - Resolves with `[SafeError, null]` if the operation throws or rejects.\n * - Never throws — always returns `[SafeError | null, T | null]`.\n *\n * @param input - A promise, a synchronous value, or a function returning a value or promise\n *\n * @returns A Promise resolving to a `[SafeError | null, T | null]` tuple\n */\nexport async function coreSafe<T>(input: SafeInput<T>): SafeResult<T> {\n try {\n const promise = typeof input === 'function' ? input() : input;\n const result = await promise;\n return [null, result];\n } catch (error) {\n return [formatError(error), null];\n }\n}\n","import { SafeInput, SafeResult } from \"../core/type\";\nimport { ERROR_CODES } from \"../errors/codes\";\nimport { formatError } from \"../errors/formatter\";\n\n/**\n * Configuration options for the `retry` function.\n */\nexport interface RetryOptions {\n /**\n * Number of retry attempts (default: 3)\n */\n retries?: number;\n\n /**\n * Delay in milliseconds between attempts (default: 0)\n */\n delayMs?: number;\n\n /**\n * Callback invoked after each failed attempt\n */\n onRetry?: (error: unknown, attempt: number) => void;\n}\n\n\nfunction sleep(ms: number) {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n/**\n * Retries a failing operation multiple times before giving up.\n *\n * Useful for unstable or flaky operations such as network requests.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe.retry(\n * () => fetchData(),\n * { retries: 3, delayMs: 500 }\n * );\n * ```\n *\n * ## Behavior\n * - Retries the operation up to `retries` times\n * - Optional `delayMs` between attempts.\n * - Invokes `onRetry` after each failed attempt.\n * - Never throws — always returns `[SafeError | null, T | null]`.\n * - Returns a `SafeError` with code `RETRY_FAILED` if all attempts fail\n * - Use `err?.code === ERROR_CODES.RETRY_FAILED` to detect a complete retry failure.\n *\n * @param input A function or promise to retry\n * @param options Retry configuration\n */\nexport async function retry<T>(input: SafeInput<T>, options: RetryOptions = {}): SafeResult<T> {\n const {\n retries = 3,\n delayMs = 0,\n onRetry\n } = options;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= retries; attempt++) {\n try {\n const result = typeof input === 'function' ? await input() : await input;\n return [null, result];\n } catch (err) {\n lastError = err;\n onRetry?.(err, attempt);\n if (attempt < retries && delayMs > 0) {\n await sleep(delayMs);\n }\n }\n }\n\n return [formatError(lastError, ERROR_CODES.RETRY_FAILED), null];\n}","import { ERROR_CODES } from '../errors/codes';\nimport { formatError } from '../errors/formatter';\nimport { SafeInput, SafeResult } from './../core/type';\n\n/**\n * Executes an operation with a time limit.\n *\n * If the operation does not resolve within the given duration,\n * it fails with a `TIMEOUT_ERROR`.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe.withTimeout(fetchData(), 1000);\n *\n * if (err?.code === ERROR_CODES.TIMEOUT) {\n * console.error(\"Operation timed out\");\n * }\n * ```\n *\n * ## Behavior\n * - Resolves with `[null, result]` if the operation completes within the timeout.\n * - Resolves with `[SafeError, null]` if the operation throws or exceeds the timeout.\n * - Never throws — always returns `[SafeError | null, T | null]`.\n * - Use `err?.code === ERROR_CODES.TIMEOUT` to detect timeout errors specifically.\n *\n * @param input A promise or a function returning a value or a promise\n * @param input A promise, a synchronous value, or a function returning a value or a promise\n */\nexport async function withTimeout<T>(input: SafeInput<T>, ms: number): SafeResult<T> {\n let timer: ReturnType<typeof setTimeout>;\n\n try {\n const promise = typeof input === 'function' ? input() : input;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`), ERROR_CODES.TIMEOUT)), ms);\n });\n\n const result = await Promise.race([promise, timeoutPromise]);\n return [null, result];\n } catch (error: any) {\n return [error, null];\n } finally {\n clearTimeout(timer!);\n }\n}","import { coreSafe } from \"./core/safe\";\nimport { retry } from \"./modules/retry\";\nimport { withTimeout } from \"./modules/timeout\";\n\n/**\n * Executes a synchronous or asynchronous operation safely.\n *\n * `safe` wraps any function, promise, or value and always returns a predictable tuple\n * instead of throwing errors.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe(() => doSomething());\n * const [err2, data2] = await safe(fetchData()); // promise directly\n * const [err3, value] = await safe(42); // synchronous value\n *\n * if (err) {\n * console.error(err.code, err.message);\n * }\n * ```\n *\n * ## Behavior\n * - Never throws — always returns `[SafeError | null, T | null]`\n * - Normalizes any thrown or rejected value into a `SafeError`\n * - Supports promises, synchronous values, or functions returning a value or promise\n *\n * ## Return\n * - `[null, result]` on success\n * - `[SafeError, null]` on failure — use `err.code` to identify the error type\n */\nexport const safe = Object.assign(coreSafe, {\n withTimeout,\n retry,\n\n // v0.3.0\n // all,\n // allSettled,\n\n // v0.4.0\n // withContext,\n\n // v0.5.0\n // once,\n\n // v0.6.0\n // strict,\n\n // v0.7.0\n // map,\n // unwrap,\n\n // v0.8.0\n // mockSuccess,\n // mockError,\n\n // v0.9.0\n // debug,\n});\n\nexport type {\n SafeError,\n SafeResult,\n SafeInput\n} from \"./core/type\";\n\nexport type {\n ErrorCode,\n ErrorWithCause\n} from \"./errors/codes\";\n\nexport type {\n RetryOptions\n} from \"./modules/retry\";\n\nexport { ERROR_CODES } from \"./errors/codes\";\n\nexport default safe;"],"mappings":";AASO,IAAM,cAAc;AAAA;AAAA,EAEvB,SAAS;AAAA;AAAA,EAGT,SAAS;AAAA;AAAA,EAGT,cAAc;AAAA;AAAA,EAGd,SAAS;AAAA;AAAA,EAGT,YAAY;AAAA;AAAA,EAGZ,gBAAgB;AACpB;;;ACFO,SAAS,YACZ,KACA,cAAyB,YAAY,SAC5B;AA5Bb;AA6BI,MAAI,eAAe,OAAO;AACtB,UAAM,iBAAiB;AAEvB,WAAO;AAAA,MACH,SAAS,IAAI;AAAA,MACb,MAAO,IAAY,QAAQ;AAAA,MAC3B,QAAO,oBAAe,UAAf,YAAwB;AAAA,IACnC;AAAA,EACJ;AAEA,MAAI,OAAO,QAAQ,UAAU;AACzB,WAAO;AAAA,MACH,SAAS;AAAA,MACT,MAAM;AAAA,IACV;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,SAAS;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,EACX;AACJ;;;ACjCA,eAAsB,SAAY,OAAoC;AAClE,MAAI;AACA,UAAM,UAAU,OAAO,UAAU,aAAa,MAAM,IAAI;AACxD,UAAM,SAAS,MAAM;AACrB,WAAO,CAAC,MAAM,MAAM;AAAA,EACxB,SAAS,OAAO;AACZ,WAAO,CAAC,YAAY,KAAK,GAAG,IAAI;AAAA,EACpC;AACJ;;;ACDA,SAAS,MAAM,IAAY;AACvB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACzD;AA0BA,eAAsB,MAAS,OAAqB,UAAwB,CAAC,GAAkB;AAC3F,QAAM;AAAA,IACF,UAAU;AAAA,IACV,UAAU;AAAA,IACV;AAAA,EACJ,IAAI;AAEJ,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,SAAS,WAAW;AACjD,QAAI;AACA,YAAM,SAAS,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI,MAAM;AACnE,aAAO,CAAC,MAAM,MAAM;AAAA,IACxB,SAAS,KAAK;AACV,kBAAY;AACZ,yCAAU,KAAK;AACf,UAAI,UAAU,WAAW,UAAU,GAAG;AAClC,cAAM,MAAM,OAAO;AAAA,MACvB;AAAA,IACJ;AAAA,EACJ;AAEA,SAAO,CAAC,YAAY,WAAW,YAAY,YAAY,GAAG,IAAI;AAClE;;;AChDA,eAAsB,YAAe,OAAqB,IAA2B;AACjF,MAAI;AAEJ,MAAI;AACA,UAAM,UAAU,OAAO,UAAU,aAAa,MAAM,IAAI;AAExD,UAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACrD,cAAQ,WAAW,MAAM,OAAO,YAAY,IAAI,MAAM,iBAAiB,EAAE,IAAI,GAAG,YAAY,OAAO,CAAC,GAAG,EAAE;AAAA,IAC7G,CAAC;AAED,UAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC;AAC3D,WAAO,CAAC,MAAM,MAAM;AAAA,EACxB,SAAS,OAAY;AACjB,WAAO,CAAC,OAAO,IAAI;AAAA,EACvB,UAAE;AACE,iBAAa,KAAM;AAAA,EACvB;AACJ;;;ACfO,IAAM,OAAO,OAAO,OAAO,UAAU;AAAA,EACxC;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBJ,CAAC;AAmBD,IAAO,gBAAQ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safe-await-lib",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "Safe async/await utility for handling promises without try/catch.",
5
5
  "author": "Chelohub Inc. <npm@borislukrece.com>",
6
6
  "scripts": {