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 +15 -0
- package/README.md +104 -59
- package/dist/index.d.mts +96 -35
- package/dist/index.d.ts +96 -35
- package/dist/index.js +49 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
[](https://www.npmjs.com/package/safe-await-lib)
|
|
5
5
|
[](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
|
|
14
|
+
Traditional `async / await` requires repetitive `try/catch` blocks, which:
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
- clutter business logic
|
|
17
|
+
- encourage inconsistent error handling
|
|
18
|
+
- hide error intent
|
|
17
19
|
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
-
|
|
29
|
-
-
|
|
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
|
-
-
|
|
32
|
-
- Tree-shakable
|
|
33
|
-
-
|
|
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
|
|
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); //
|
|
72
|
+
console.log(data); // "Hello World"
|
|
69
73
|
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Using a direct promise
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
const [
|
|
78
|
+
```ts
|
|
79
|
+
const [err, result] = await safe(Promise.resolve(42));
|
|
73
80
|
```
|
|
74
81
|
|
|
75
82
|
---
|
|
76
83
|
|
|
77
|
-
## 🛠️ API
|
|
84
|
+
## 🛠️ API
|
|
85
|
+
|
|
86
|
+
### `safe(input)`
|
|
78
87
|
|
|
79
|
-
|
|
88
|
+
```ts
|
|
89
|
+
safe<T>(input: Promise<T> | (() => T | Promise<T>))
|
|
90
|
+
→ Promise<[SafeError | null, T | null]>
|
|
91
|
+
```
|
|
80
92
|
|
|
81
|
-
|
|
82
|
-
- **returns**: `[SafeError | null, T | null]`
|
|
93
|
+
---
|
|
83
94
|
|
|
84
|
-
### `SafeError`
|
|
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
|
|
101
|
+
cause?: unknown; // Original error or context
|
|
91
102
|
}
|
|
92
103
|
```
|
|
93
104
|
|
|
94
105
|
---
|
|
95
106
|
|
|
96
|
-
## 🌱 Advanced Usage
|
|
107
|
+
## 🌱 Advanced Usage (v0.2.0)
|
|
97
108
|
|
|
98
|
-
###
|
|
109
|
+
### ⏱️ Timeout handling — `safe.withTimeout`
|
|
99
110
|
|
|
100
111
|
```ts
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### 🔁 Retry logic — `safe.retry`
|
|
106
122
|
|
|
107
123
|
```ts
|
|
108
|
-
const [err, data] = await safe.retry(
|
|
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
|
-
##
|
|
142
|
+
## ✅ Available Today (v0.2.0)
|
|
143
|
+
|
|
144
|
+
SAFE-AWAIT-LIB currently provides:
|
|
114
145
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
157
|
+
# Install dependencies
|
|
132
158
|
npm install
|
|
133
159
|
|
|
134
|
-
#
|
|
160
|
+
# Development build (watch mode)
|
|
135
161
|
npm run dev
|
|
136
162
|
|
|
137
|
-
# Type
|
|
163
|
+
# Type checking
|
|
138
164
|
npm run typecheck
|
|
139
165
|
|
|
140
166
|
# Run tests
|
|
141
167
|
npm test
|
|
142
168
|
|
|
143
|
-
# Build for
|
|
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
|
-
|
|
192
|
+
Contributions are welcome.
|
|
152
193
|
|
|
153
194
|
1. Fork the repository
|
|
154
|
-
2. Create a feature branch
|
|
155
|
-
3.
|
|
156
|
-
4.
|
|
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
|
|
200
|
+
Please respect the existing **TypeScript typings** and **error model**.
|
|
159
201
|
|
|
160
202
|
---
|
|
161
203
|
|
|
162
|
-
## ❓ FAQ
|
|
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
|
-
-
|
|
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
|
-
|
|
168
|
-
Yes. SAFE-AWAIT-LIB is fully compatible.
|
|
214
|
+
### Is this production-ready?
|
|
169
215
|
|
|
170
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
43
|
-
*
|
|
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
|
|
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
|
-
*
|
|
165
|
+
* Executes a synchronous or asynchronous operation safely.
|
|
95
166
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
167
|
+
* `safe` wraps any function, promise, or value and always returns a predictable tuple
|
|
168
|
+
* instead of throwing errors.
|
|
98
169
|
*
|
|
99
|
-
* ##
|
|
170
|
+
* ## Usage
|
|
100
171
|
* ```ts
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* const [
|
|
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.
|
|
107
|
-
* } else {
|
|
108
|
-
* console.log(data);
|
|
177
|
+
* console.error(err.code, err.message);
|
|
109
178
|
* }
|
|
110
179
|
* ```
|
|
111
180
|
*
|
|
112
|
-
* ##
|
|
113
|
-
* -
|
|
114
|
-
* -
|
|
115
|
-
* -
|
|
116
|
-
*
|
|
117
|
-
* ##
|
|
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
|
-
*
|
|
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
|
-
*
|
|
43
|
-
*
|
|
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
|
|
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
|
-
*
|
|
165
|
+
* Executes a synchronous or asynchronous operation safely.
|
|
95
166
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
167
|
+
* `safe` wraps any function, promise, or value and always returns a predictable tuple
|
|
168
|
+
* instead of throwing errors.
|
|
98
169
|
*
|
|
99
|
-
* ##
|
|
170
|
+
* ## Usage
|
|
100
171
|
* ```ts
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* const [
|
|
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.
|
|
107
|
-
* } else {
|
|
108
|
-
* console.log(data);
|
|
177
|
+
* console.error(err.code, err.message);
|
|
109
178
|
* }
|
|
110
179
|
* ```
|
|
111
180
|
*
|
|
112
|
-
* ##
|
|
113
|
-
* -
|
|
114
|
-
* -
|
|
115
|
-
* -
|
|
116
|
-
*
|
|
117
|
-
* ##
|
|
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
|
|
73
|
-
return [null,
|
|
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
|
-
|
|
82
|
-
|
|
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
|
|
46
|
-
return [null,
|
|
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
|
-
|
|
55
|
-
|
|
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
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -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":[]}
|