safe-await-lib 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -0
- package/dist/index.d.mts +92 -0
- package/dist/index.d.ts +92 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +80 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# SAFE-AWAIT
|
|
2
|
+
|
|
3
|
+
[](https://github.com/chelohubinc/safe-await/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/safe-await)
|
|
5
|
+
[](https://github.com/chelohubinc/safe-await/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
Safe async/await utility for handling promises without try/catch.
|
|
8
|
+
Designed for **Node.js**, **TypeScript**, **React**, **React Native**, and **Expo**.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ๐ Why SAFE-AWAIT?
|
|
13
|
+
|
|
14
|
+
Traditional async/await patterns require `try/catch` blocks, which can clutter code and make error handling inconsistent.
|
|
15
|
+
|
|
16
|
+
**SAFE-AWAIT solves this problem by:**
|
|
17
|
+
|
|
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
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## ๐ Features
|
|
26
|
+
|
|
27
|
+
- Safe execution of async functions and promises
|
|
28
|
+
- Standardized error handling with `SafeError` objects
|
|
29
|
+
- Fully TypeScript-typed for modern development
|
|
30
|
+
- 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`
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## ๐ฆ Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# npm
|
|
41
|
+
npm install safe-await
|
|
42
|
+
|
|
43
|
+
# yarn
|
|
44
|
+
yarn add safe-await
|
|
45
|
+
|
|
46
|
+
# pnpm
|
|
47
|
+
pnpm add safe-await
|
|
48
|
+
````
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## โก Basic Usage
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import safe from 'safe-await';
|
|
56
|
+
|
|
57
|
+
// Async function
|
|
58
|
+
async function fetchData() {
|
|
59
|
+
return Promise.resolve('Hello World!');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Using safe
|
|
63
|
+
const [err, data] = await safe(fetchData);
|
|
64
|
+
|
|
65
|
+
if (err) {
|
|
66
|
+
console.error(err.message);
|
|
67
|
+
} else {
|
|
68
|
+
console.log(data); // 'Hello World!'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Direct promise usage
|
|
72
|
+
const [err2, result] = await safe(Promise.resolve(42));
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## ๐ ๏ธ API Overview
|
|
78
|
+
|
|
79
|
+
### `safe(input: SafeInput<T>): SafeResult<T>`
|
|
80
|
+
|
|
81
|
+
- **input**: `Promise<T>` or `() => T | Promise<T>`
|
|
82
|
+
- **returns**: `[SafeError | null, T | null]`
|
|
83
|
+
|
|
84
|
+
### `SafeError` Structure
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
interface SafeError {
|
|
88
|
+
message: string; // Human-readable message
|
|
89
|
+
code: string; // Standardized error code
|
|
90
|
+
cause?: unknown; // Original error or additional context
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## ๐ฑ Advanced Usage Examples
|
|
97
|
+
|
|
98
|
+
### Handling multiple async operations
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
// Future module: safe.all
|
|
102
|
+
const [err, results] = await safe.all([fetchData(), Promise.resolve(10)]);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Using retry (planned in v0.2.0)
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const [err, data] = await safe.retry(() => fetchData(), { attempts: 3, delay: 500 });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## ๐ Roadmap
|
|
114
|
+
|
|
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 |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## ๐งช Development
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Install dev dependencies
|
|
132
|
+
npm install
|
|
133
|
+
|
|
134
|
+
# Run development build with watch
|
|
135
|
+
npm run dev
|
|
136
|
+
|
|
137
|
+
# Type check
|
|
138
|
+
npm run typecheck
|
|
139
|
+
|
|
140
|
+
# Run tests
|
|
141
|
+
npm test
|
|
142
|
+
|
|
143
|
+
# Build for distribution
|
|
144
|
+
npm run build
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## ๐ Contributing
|
|
150
|
+
|
|
151
|
+
SAFE-AWAIT welcomes contributions!
|
|
152
|
+
|
|
153
|
+
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
|
|
157
|
+
|
|
158
|
+
Please adhere to the existing **TypeScript typings** and **code style**.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## โ FAQ / Tips
|
|
163
|
+
|
|
164
|
+
- **Why use `[SafeError | null, T | null]` instead of try/catch?**
|
|
165
|
+
It ensures consistent error handling and makes async code more readable.
|
|
166
|
+
|
|
167
|
+
- **Can I use this in React Native / Expo?**
|
|
168
|
+
Yes. SAFE-AWAIT is fully compatible.
|
|
169
|
+
|
|
170
|
+
- **How do I handle retries or timeouts?**
|
|
171
|
+
Future modules like `retry` and `withTimeout` will handle these cases cleanly.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## ๐ License
|
|
176
|
+
|
|
177
|
+
ISC License ยฉ Chelohub Inc.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## ๐ Links
|
|
182
|
+
|
|
183
|
+
- **GitHub:** [https://github.com/chelohubinc/safe-await](https://github.com/chelohubinc/safe-await)
|
|
184
|
+
- **NPM:** [https://www.npmjs.com/package/safe-await](https://www.npmjs.com/package/safe-await)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized error object used throughout the SAFE-AWAIT package.
|
|
3
|
+
*
|
|
4
|
+
* Every error returned by `safe()` or its extensions follows this shape,
|
|
5
|
+
* ensuring consistent and predictable error handling.
|
|
6
|
+
*/
|
|
7
|
+
interface SafeError {
|
|
8
|
+
/** Human-readable error message */
|
|
9
|
+
message: string;
|
|
10
|
+
/** Machine-readable error code */
|
|
11
|
+
code: string;
|
|
12
|
+
/** Original error or additional context, if available */
|
|
13
|
+
cause?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Standard return type for all SAFE-AWAIT operations.
|
|
17
|
+
*
|
|
18
|
+
* The result is always a tuple:
|
|
19
|
+
* - `[null, T]` when the operation succeeds
|
|
20
|
+
* - `[SafeError, null]` when the operation fails
|
|
21
|
+
*
|
|
22
|
+
* This pattern eliminates the need for try/catch blocks.
|
|
23
|
+
*/
|
|
24
|
+
type SafeResult<T> = Promise<[SafeError | null, T | null]>;
|
|
25
|
+
/**
|
|
26
|
+
* Accepted input type for the `safe()` function.
|
|
27
|
+
*
|
|
28
|
+
* Allows passing either:
|
|
29
|
+
* - a Promise
|
|
30
|
+
* - a function returning a value or a Promise
|
|
31
|
+
*
|
|
32
|
+
* This enables lazy execution and consistent error handling.
|
|
33
|
+
*/
|
|
34
|
+
type SafeInput<T> = Promise<T> | (() => T | Promise<T>);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Internal execution engine for SAFE-AWAIT.
|
|
38
|
+
*
|
|
39
|
+
* This function executes a promise or a function safely and converts
|
|
40
|
+
* any thrown or rejected value into a standardized `SafeResult` tuple.
|
|
41
|
+
*
|
|
42
|
+
* It is intentionally minimal and side-effect free, serving as the
|
|
43
|
+
* foundation for all higher-level modules (retry, timeout, etc.).
|
|
44
|
+
*
|
|
45
|
+
* @param input - A promise or a function returning a value or a promise
|
|
46
|
+
*
|
|
47
|
+
* @returns A Promise resolving to a `[SafeError | null, T | null]` tuple
|
|
48
|
+
*/
|
|
49
|
+
declare function coreSafe<T>(input: SafeInput<T>): SafeResult<T>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* `safe` is the core function of the SAFE-AWAIT package.
|
|
53
|
+
*
|
|
54
|
+
* It safely executes any synchronous or asynchronous operation and
|
|
55
|
+
* always returns a predictable tuple instead of throwing errors.
|
|
56
|
+
*
|
|
57
|
+
* ## Basic usage
|
|
58
|
+
* ```ts
|
|
59
|
+
* import safe from "safe-await";
|
|
60
|
+
*
|
|
61
|
+
* const [err, data] = await safe(async () => fetchData());
|
|
62
|
+
*
|
|
63
|
+
* if (err) {
|
|
64
|
+
* console.error(err.message, err.code);
|
|
65
|
+
* } else {
|
|
66
|
+
* console.log(data);
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* ## Philosophy
|
|
71
|
+
* - No try/catch pollution
|
|
72
|
+
* - No unhandled promise rejections
|
|
73
|
+
* - Explicit error handling
|
|
74
|
+
*
|
|
75
|
+
* ## Planned extensions
|
|
76
|
+
* - v0.2.0: withTimeout, retry
|
|
77
|
+
* - v0.3.0: all, allSettled
|
|
78
|
+
* - v0.4.0: withContext
|
|
79
|
+
* - v0.5.0: once
|
|
80
|
+
* - v0.6.0: strict
|
|
81
|
+
* - v0.7.0: map, unwrap
|
|
82
|
+
* - v0.8.0: mockSuccess, mockError
|
|
83
|
+
* - v0.9.0: debug
|
|
84
|
+
*
|
|
85
|
+
* ## Return value
|
|
86
|
+
* Always returns a tuple:
|
|
87
|
+
* - `[null, result]` on success
|
|
88
|
+
* - `[SafeError, null]` on failure
|
|
89
|
+
*/
|
|
90
|
+
declare const safe: typeof coreSafe;
|
|
91
|
+
|
|
92
|
+
export { safe as default, safe };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized error object used throughout the SAFE-AWAIT package.
|
|
3
|
+
*
|
|
4
|
+
* Every error returned by `safe()` or its extensions follows this shape,
|
|
5
|
+
* ensuring consistent and predictable error handling.
|
|
6
|
+
*/
|
|
7
|
+
interface SafeError {
|
|
8
|
+
/** Human-readable error message */
|
|
9
|
+
message: string;
|
|
10
|
+
/** Machine-readable error code */
|
|
11
|
+
code: string;
|
|
12
|
+
/** Original error or additional context, if available */
|
|
13
|
+
cause?: unknown;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Standard return type for all SAFE-AWAIT operations.
|
|
17
|
+
*
|
|
18
|
+
* The result is always a tuple:
|
|
19
|
+
* - `[null, T]` when the operation succeeds
|
|
20
|
+
* - `[SafeError, null]` when the operation fails
|
|
21
|
+
*
|
|
22
|
+
* This pattern eliminates the need for try/catch blocks.
|
|
23
|
+
*/
|
|
24
|
+
type SafeResult<T> = Promise<[SafeError | null, T | null]>;
|
|
25
|
+
/**
|
|
26
|
+
* Accepted input type for the `safe()` function.
|
|
27
|
+
*
|
|
28
|
+
* Allows passing either:
|
|
29
|
+
* - a Promise
|
|
30
|
+
* - a function returning a value or a Promise
|
|
31
|
+
*
|
|
32
|
+
* This enables lazy execution and consistent error handling.
|
|
33
|
+
*/
|
|
34
|
+
type SafeInput<T> = Promise<T> | (() => T | Promise<T>);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Internal execution engine for SAFE-AWAIT.
|
|
38
|
+
*
|
|
39
|
+
* This function executes a promise or a function safely and converts
|
|
40
|
+
* any thrown or rejected value into a standardized `SafeResult` tuple.
|
|
41
|
+
*
|
|
42
|
+
* It is intentionally minimal and side-effect free, serving as the
|
|
43
|
+
* foundation for all higher-level modules (retry, timeout, etc.).
|
|
44
|
+
*
|
|
45
|
+
* @param input - A promise or a function returning a value or a promise
|
|
46
|
+
*
|
|
47
|
+
* @returns A Promise resolving to a `[SafeError | null, T | null]` tuple
|
|
48
|
+
*/
|
|
49
|
+
declare function coreSafe<T>(input: SafeInput<T>): SafeResult<T>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* `safe` is the core function of the SAFE-AWAIT package.
|
|
53
|
+
*
|
|
54
|
+
* It safely executes any synchronous or asynchronous operation and
|
|
55
|
+
* always returns a predictable tuple instead of throwing errors.
|
|
56
|
+
*
|
|
57
|
+
* ## Basic usage
|
|
58
|
+
* ```ts
|
|
59
|
+
* import safe from "safe-await";
|
|
60
|
+
*
|
|
61
|
+
* const [err, data] = await safe(async () => fetchData());
|
|
62
|
+
*
|
|
63
|
+
* if (err) {
|
|
64
|
+
* console.error(err.message, err.code);
|
|
65
|
+
* } else {
|
|
66
|
+
* console.log(data);
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* ## Philosophy
|
|
71
|
+
* - No try/catch pollution
|
|
72
|
+
* - No unhandled promise rejections
|
|
73
|
+
* - Explicit error handling
|
|
74
|
+
*
|
|
75
|
+
* ## Planned extensions
|
|
76
|
+
* - v0.2.0: withTimeout, retry
|
|
77
|
+
* - v0.3.0: all, allSettled
|
|
78
|
+
* - v0.4.0: withContext
|
|
79
|
+
* - v0.5.0: once
|
|
80
|
+
* - v0.6.0: strict
|
|
81
|
+
* - v0.7.0: map, unwrap
|
|
82
|
+
* - v0.8.0: mockSuccess, mockError
|
|
83
|
+
* - v0.9.0: debug
|
|
84
|
+
*
|
|
85
|
+
* ## Return value
|
|
86
|
+
* Always returns a tuple:
|
|
87
|
+
* - `[null, result]` on success
|
|
88
|
+
* - `[SafeError, null]` on failure
|
|
89
|
+
*/
|
|
90
|
+
declare const safe: typeof coreSafe;
|
|
91
|
+
|
|
92
|
+
export { safe as default, safe };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
default: () => index_default,
|
|
24
|
+
safe: () => safe
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/errors/codes.ts
|
|
29
|
+
var ERROR_CODES = {
|
|
30
|
+
/** Fallback error for unknown or unhandled failures */
|
|
31
|
+
UNKNOWN: "UNKNOWN_ERROR",
|
|
32
|
+
/** Thrown when an operation exceeds a configured timeout */
|
|
33
|
+
TIMEOUT: "TIMEOUT_ERROR",
|
|
34
|
+
/** Used when all retry attempts have failed */
|
|
35
|
+
RETRY_FAILED: "RETRY_FAILED",
|
|
36
|
+
/** Used when an operation is explicitly aborted or cancelled */
|
|
37
|
+
ABORTED: "ABORT_ERROR",
|
|
38
|
+
/** Used when input validation fails */
|
|
39
|
+
VALIDATION: "VALIDATION_ERROR",
|
|
40
|
+
/** Used when a function guarded by `once()` is called more than once */
|
|
41
|
+
EXECUTION_ONCE: "ALREADY_EXECUTED"
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/errors/formatter.ts
|
|
45
|
+
function formatError(err, defaultCode = ERROR_CODES.UNKNOWN) {
|
|
46
|
+
var _a;
|
|
47
|
+
if (err instanceof Error) {
|
|
48
|
+
const errorWithCause = err;
|
|
49
|
+
return {
|
|
50
|
+
message: err.message,
|
|
51
|
+
code: err.code || defaultCode,
|
|
52
|
+
cause: (_a = errorWithCause.cause) != null ? _a : err
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (typeof err === "string") {
|
|
56
|
+
return {
|
|
57
|
+
message: err,
|
|
58
|
+
code: defaultCode
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
message: "An unexpected error occurred",
|
|
63
|
+
code: defaultCode,
|
|
64
|
+
cause: err
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/core/safe.ts
|
|
69
|
+
async function coreSafe(input) {
|
|
70
|
+
try {
|
|
71
|
+
const promise = typeof input === "function" ? input() : input;
|
|
72
|
+
const data = await promise;
|
|
73
|
+
return [null, data];
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return [formatError(error), null];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/index.ts
|
|
80
|
+
var safe = Object.assign(coreSafe, {
|
|
81
|
+
// v0.2.0
|
|
82
|
+
// withTimeout,
|
|
83
|
+
// retry,
|
|
84
|
+
// v0.3.0
|
|
85
|
+
// all,
|
|
86
|
+
// allSettled,
|
|
87
|
+
// v0.4.0
|
|
88
|
+
// withContext,
|
|
89
|
+
// v0.5.0
|
|
90
|
+
// once,
|
|
91
|
+
// v0.6.0
|
|
92
|
+
// strict,
|
|
93
|
+
// v0.7.0
|
|
94
|
+
// map,
|
|
95
|
+
// unwrap,
|
|
96
|
+
// v0.8.0
|
|
97
|
+
// mockSuccess,
|
|
98
|
+
// mockError,
|
|
99
|
+
// v0.9.0
|
|
100
|
+
// debug,
|
|
101
|
+
});
|
|
102
|
+
var index_default = safe;
|
|
103
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
104
|
+
0 && (module.exports = {
|
|
105
|
+
safe
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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 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\";\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 default safe;","/**\n * Standardized error codes used across the SAFE-AWAIT 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 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'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.\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;AAED,IAAO,gBAAQ;","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/errors/codes.ts
|
|
2
|
+
var ERROR_CODES = {
|
|
3
|
+
/** Fallback error for unknown or unhandled failures */
|
|
4
|
+
UNKNOWN: "UNKNOWN_ERROR",
|
|
5
|
+
/** Thrown when an operation exceeds a configured timeout */
|
|
6
|
+
TIMEOUT: "TIMEOUT_ERROR",
|
|
7
|
+
/** Used when all retry attempts have failed */
|
|
8
|
+
RETRY_FAILED: "RETRY_FAILED",
|
|
9
|
+
/** Used when an operation is explicitly aborted or cancelled */
|
|
10
|
+
ABORTED: "ABORT_ERROR",
|
|
11
|
+
/** Used when input validation fails */
|
|
12
|
+
VALIDATION: "VALIDATION_ERROR",
|
|
13
|
+
/** Used when a function guarded by `once()` is called more than once */
|
|
14
|
+
EXECUTION_ONCE: "ALREADY_EXECUTED"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/errors/formatter.ts
|
|
18
|
+
function formatError(err, defaultCode = ERROR_CODES.UNKNOWN) {
|
|
19
|
+
var _a;
|
|
20
|
+
if (err instanceof Error) {
|
|
21
|
+
const errorWithCause = err;
|
|
22
|
+
return {
|
|
23
|
+
message: err.message,
|
|
24
|
+
code: err.code || defaultCode,
|
|
25
|
+
cause: (_a = errorWithCause.cause) != null ? _a : err
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (typeof err === "string") {
|
|
29
|
+
return {
|
|
30
|
+
message: err,
|
|
31
|
+
code: defaultCode
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
message: "An unexpected error occurred",
|
|
36
|
+
code: defaultCode,
|
|
37
|
+
cause: err
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/core/safe.ts
|
|
42
|
+
async function coreSafe(input) {
|
|
43
|
+
try {
|
|
44
|
+
const promise = typeof input === "function" ? input() : input;
|
|
45
|
+
const data = await promise;
|
|
46
|
+
return [null, data];
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return [formatError(error), null];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/index.ts
|
|
53
|
+
var safe = Object.assign(coreSafe, {
|
|
54
|
+
// v0.2.0
|
|
55
|
+
// withTimeout,
|
|
56
|
+
// retry,
|
|
57
|
+
// v0.3.0
|
|
58
|
+
// all,
|
|
59
|
+
// allSettled,
|
|
60
|
+
// v0.4.0
|
|
61
|
+
// withContext,
|
|
62
|
+
// v0.5.0
|
|
63
|
+
// once,
|
|
64
|
+
// v0.6.0
|
|
65
|
+
// strict,
|
|
66
|
+
// v0.7.0
|
|
67
|
+
// map,
|
|
68
|
+
// unwrap,
|
|
69
|
+
// v0.8.0
|
|
70
|
+
// mockSuccess,
|
|
71
|
+
// mockError,
|
|
72
|
+
// v0.9.0
|
|
73
|
+
// debug,
|
|
74
|
+
});
|
|
75
|
+
var index_default = safe;
|
|
76
|
+
export {
|
|
77
|
+
index_default as default,
|
|
78
|
+
safe
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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 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 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'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.\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 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\";\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 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;AAED,IAAO,gBAAQ;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "safe-await-lib",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Safe async/await utility for handling promises without try/catch.",
|
|
5
|
+
"author": "Chelohub Inc. <npm@borislukrece.com>",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsup",
|
|
8
|
+
"dev": "tsup --watch",
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"release:patch": "npm version patch && git push origin HEAD --follow-tags",
|
|
11
|
+
"release:minor": "npm version minor && git push origin HEAD --follow-tags",
|
|
12
|
+
"release:major": "npm version major && git push origin HEAD --follow-tags",
|
|
13
|
+
"test": "vitest"
|
|
14
|
+
},
|
|
15
|
+
"directories": {
|
|
16
|
+
"test": "tests"
|
|
17
|
+
},
|
|
18
|
+
"main": "./dist/index.cjs",
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"require": "./dist/index.cjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"tsup": "^8.5.1",
|
|
29
|
+
"typescript": "^5.9.3",
|
|
30
|
+
"vitest": "^4.0.16"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/chelohubinc/safe-await"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"async",
|
|
41
|
+
"await",
|
|
42
|
+
"promise",
|
|
43
|
+
"error-handling",
|
|
44
|
+
"try-catch",
|
|
45
|
+
"result",
|
|
46
|
+
"typescript",
|
|
47
|
+
"react-native",
|
|
48
|
+
"expo"
|
|
49
|
+
],
|
|
50
|
+
"license": "ISC",
|
|
51
|
+
"sideEffects": false,
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18"
|
|
54
|
+
}
|
|
55
|
+
}
|