safe-await-lib 0.2.0 → 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/dist/index.d.mts +26 -19
- package/dist/index.d.ts +26 -19
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -36,13 +36,15 @@ 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
|
*/
|
|
@@ -80,9 +82,11 @@ interface RetryOptions {
|
|
|
80
82
|
*
|
|
81
83
|
* ## Behavior
|
|
82
84
|
* - Retries the operation up to `retries` times
|
|
83
|
-
* - Optional
|
|
84
|
-
* -
|
|
85
|
-
* -
|
|
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.
|
|
86
90
|
*
|
|
87
91
|
* @param input A function or promise to retry
|
|
88
92
|
* @param options Retry configuration
|
|
@@ -105,14 +109,15 @@ declare function retry<T>(input: SafeInput<T>, options?: RetryOptions): SafeResu
|
|
|
105
109
|
* ```
|
|
106
110
|
*
|
|
107
111
|
* ## Behavior
|
|
108
|
-
* - Resolves with `[null, result]` if
|
|
109
|
-
* - Resolves with `[SafeError, null]`
|
|
110
|
-
* - Never throws
|
|
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.
|
|
111
116
|
*
|
|
112
|
-
* @param input A promise or
|
|
113
|
-
* @param
|
|
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
|
|
114
119
|
*/
|
|
115
|
-
declare function withTimeout<T>(input:
|
|
120
|
+
declare function withTimeout<T>(input: SafeInput<T>, ms: number): SafeResult<T>;
|
|
116
121
|
|
|
117
122
|
/**
|
|
118
123
|
* Standardized error codes used across the SAFE-AWAIT-LIB package.
|
|
@@ -159,12 +164,14 @@ interface ErrorWithCause extends Error {
|
|
|
159
164
|
/**
|
|
160
165
|
* Executes a synchronous or asynchronous operation safely.
|
|
161
166
|
*
|
|
162
|
-
* `safe` wraps any function or
|
|
167
|
+
* `safe` wraps any function, promise, or value and always returns a predictable tuple
|
|
163
168
|
* instead of throwing errors.
|
|
164
169
|
*
|
|
165
170
|
* ## Usage
|
|
166
171
|
* ```ts
|
|
167
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
|
|
168
175
|
*
|
|
169
176
|
* if (err) {
|
|
170
177
|
* console.error(err.code, err.message);
|
|
@@ -172,13 +179,13 @@ interface ErrorWithCause extends Error {
|
|
|
172
179
|
* ```
|
|
173
180
|
*
|
|
174
181
|
* ## Behavior
|
|
175
|
-
* - Never throws
|
|
176
|
-
* -
|
|
177
|
-
* -
|
|
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
|
|
178
185
|
*
|
|
179
186
|
* ## Return
|
|
180
187
|
* - `[null, result]` on success
|
|
181
|
-
* - `[SafeError, null]` on failure
|
|
188
|
+
* - `[SafeError, null]` on failure — use `err.code` to identify the error type
|
|
182
189
|
*/
|
|
183
190
|
declare const safe: typeof coreSafe & {
|
|
184
191
|
withTimeout: typeof withTimeout;
|
package/dist/index.d.ts
CHANGED
|
@@ -36,13 +36,15 @@ 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
|
*/
|
|
@@ -80,9 +82,11 @@ interface RetryOptions {
|
|
|
80
82
|
*
|
|
81
83
|
* ## Behavior
|
|
82
84
|
* - Retries the operation up to `retries` times
|
|
83
|
-
* - Optional
|
|
84
|
-
* -
|
|
85
|
-
* -
|
|
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.
|
|
86
90
|
*
|
|
87
91
|
* @param input A function or promise to retry
|
|
88
92
|
* @param options Retry configuration
|
|
@@ -105,14 +109,15 @@ declare function retry<T>(input: SafeInput<T>, options?: RetryOptions): SafeResu
|
|
|
105
109
|
* ```
|
|
106
110
|
*
|
|
107
111
|
* ## Behavior
|
|
108
|
-
* - Resolves with `[null, result]` if
|
|
109
|
-
* - Resolves with `[SafeError, null]`
|
|
110
|
-
* - Never throws
|
|
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.
|
|
111
116
|
*
|
|
112
|
-
* @param input A promise or
|
|
113
|
-
* @param
|
|
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
|
|
114
119
|
*/
|
|
115
|
-
declare function withTimeout<T>(input:
|
|
120
|
+
declare function withTimeout<T>(input: SafeInput<T>, ms: number): SafeResult<T>;
|
|
116
121
|
|
|
117
122
|
/**
|
|
118
123
|
* Standardized error codes used across the SAFE-AWAIT-LIB package.
|
|
@@ -159,12 +164,14 @@ interface ErrorWithCause extends Error {
|
|
|
159
164
|
/**
|
|
160
165
|
* Executes a synchronous or asynchronous operation safely.
|
|
161
166
|
*
|
|
162
|
-
* `safe` wraps any function or
|
|
167
|
+
* `safe` wraps any function, promise, or value and always returns a predictable tuple
|
|
163
168
|
* instead of throwing errors.
|
|
164
169
|
*
|
|
165
170
|
* ## Usage
|
|
166
171
|
* ```ts
|
|
167
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
|
|
168
175
|
*
|
|
169
176
|
* if (err) {
|
|
170
177
|
* console.error(err.code, err.message);
|
|
@@ -172,13 +179,13 @@ interface ErrorWithCause extends Error {
|
|
|
172
179
|
* ```
|
|
173
180
|
*
|
|
174
181
|
* ## Behavior
|
|
175
|
-
* - Never throws
|
|
176
|
-
* -
|
|
177
|
-
* -
|
|
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
|
|
178
185
|
*
|
|
179
186
|
* ## Return
|
|
180
187
|
* - `[null, result]` on success
|
|
181
|
-
* - `[SafeError, null]` on failure
|
|
188
|
+
* - `[SafeError, null]` on failure — use `err.code` to identify the error type
|
|
182
189
|
*/
|
|
183
190
|
declare const safe: typeof coreSafe & {
|
|
184
191
|
withTimeout: typeof withTimeout;
|
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,8 +70,8 @@ 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
|
}
|
|
@@ -89,8 +90,8 @@ async function retry(input, options = {}) {
|
|
|
89
90
|
let lastError;
|
|
90
91
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
91
92
|
try {
|
|
92
|
-
const
|
|
93
|
-
return [null,
|
|
93
|
+
const result = typeof input === "function" ? await input() : await input;
|
|
94
|
+
return [null, result];
|
|
94
95
|
} catch (err) {
|
|
95
96
|
lastError = err;
|
|
96
97
|
onRetry == null ? void 0 : onRetry(err, attempt);
|
|
@@ -99,7 +100,7 @@ async function retry(input, options = {}) {
|
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
|
-
return [formatError(lastError,
|
|
103
|
+
return [formatError(lastError, ERROR_CODES.RETRY_FAILED), null];
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// src/modules/timeout.ts
|
|
@@ -108,7 +109,7 @@ async function withTimeout(input, ms) {
|
|
|
108
109
|
try {
|
|
109
110
|
const promise = typeof input === "function" ? input() : input;
|
|
110
111
|
const timeoutPromise = new Promise((_, reject) => {
|
|
111
|
-
timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`),
|
|
112
|
+
timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`), ERROR_CODES.TIMEOUT)), ms);
|
|
112
113
|
});
|
|
113
114
|
const result = await Promise.race([promise, timeoutPromise]);
|
|
114
115
|
return [null, result];
|
|
@@ -144,6 +145,7 @@ var safe = Object.assign(coreSafe, {
|
|
|
144
145
|
var index_default = safe;
|
|
145
146
|
// Annotate the CommonJS export names for ESM import in node:
|
|
146
147
|
0 && (module.exports = {
|
|
148
|
+
ERROR_CODES,
|
|
147
149
|
safe
|
|
148
150
|
});
|
|
149
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","../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 or promise and always returns a predictable tuple\n * instead of throwing errors.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe(() => doSomething());\n *\n * if (err) {\n * console.error(err.code, err.message);\n * }\n * ```\n *\n * ## Behavior\n * - Never throws\n * - Always resolves\n * - Normalizes all errors into `SafeError`\n *\n * ## Return\n * - `[null, result]` on success\n * - `[SafeError, null]` on failure\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 ERROR_CODES,\n ErrorCode,\n ErrorWithCause\n} from \"./errors/codes\";\n\nexport type {\n RetryOptions\n} from \"./modules/retry\";\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 return [null, data];\n } catch (error) {\n return [formatError(error), null];\n }\n}\n","import { SafeInput, SafeResult } from \"../core/type\";\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 delay between attempts\n * - Calls `onRetry` after each failure\n * - Returns `RETRY_FAILED` if all attempts fail\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 data = typeof input === 'function' ? (await input()) : (await input);\n return [null, data];\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, 'RETRY_FAILED'), null];\n}","import { ERROR_CODES } from '../errors/codes';\nimport { formatError } from '../errors/formatter';\nimport { 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 completed in time\n * - Resolves with `[SafeError, null]` on timeout or failure\n * - Never throws\n *\n * @param input A promise or synchronous value\n * @param ms Timeout duration in milliseconds\n */\nexport async function withTimeout<T>(input: T | Promise<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`), 'TIMEOUT_ERROR')), 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;;;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;AACnB,WAAO,CAAC,MAAM,IAAI;AAAA,EACtB,SAAS,OAAO;AACZ,WAAO,CAAC,YAAY,KAAK,GAAG,IAAI;AAAA,EACpC;AACJ;;;ACAA,SAAS,MAAM,IAAY;AACvB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACzD;AAwBA,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,OAAO,OAAO,UAAU,aAAc,MAAM,MAAM,IAAM,MAAM;AACpE,aAAO,CAAC,MAAM,IAAI;AAAA,IACtB,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,cAAc,GAAG,IAAI;AACxD;;;AC9CA,eAAsB,YAAe,OAAuB,IAA2B;AACnF,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,eAAe,CAAC,GAAG,EAAE;AAAA,IACzG,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;;;ALhBO,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;AAkBD,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,8 +42,8 @@ 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
|
}
|
|
@@ -62,8 +62,8 @@ async function retry(input, options = {}) {
|
|
|
62
62
|
let lastError;
|
|
63
63
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
64
64
|
try {
|
|
65
|
-
const
|
|
66
|
-
return [null,
|
|
65
|
+
const result = typeof input === "function" ? await input() : await input;
|
|
66
|
+
return [null, result];
|
|
67
67
|
} catch (err) {
|
|
68
68
|
lastError = err;
|
|
69
69
|
onRetry == null ? void 0 : onRetry(err, attempt);
|
|
@@ -72,7 +72,7 @@ async function retry(input, options = {}) {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
return [formatError(lastError,
|
|
75
|
+
return [formatError(lastError, ERROR_CODES.RETRY_FAILED), null];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// src/modules/timeout.ts
|
|
@@ -81,7 +81,7 @@ async function withTimeout(input, ms) {
|
|
|
81
81
|
try {
|
|
82
82
|
const promise = typeof input === "function" ? input() : input;
|
|
83
83
|
const timeoutPromise = new Promise((_, reject) => {
|
|
84
|
-
timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`),
|
|
84
|
+
timer = setTimeout(() => reject(formatError(new Error(`Timeout after ${ms}ms`), ERROR_CODES.TIMEOUT)), ms);
|
|
85
85
|
});
|
|
86
86
|
const result = await Promise.race([promise, timeoutPromise]);
|
|
87
87
|
return [null, result];
|
|
@@ -116,6 +116,7 @@ var safe = Object.assign(coreSafe, {
|
|
|
116
116
|
});
|
|
117
117
|
var index_default = safe;
|
|
118
118
|
export {
|
|
119
|
+
ERROR_CODES,
|
|
119
120
|
index_default as default,
|
|
120
121
|
safe
|
|
121
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/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 * 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 return [null, data];\n } catch (error) {\n return [formatError(error), null];\n }\n}\n","import { SafeInput, SafeResult } from \"../core/type\";\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 delay between attempts\n * - Calls `onRetry` after each failure\n * - Returns `RETRY_FAILED` if all attempts fail\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 data = typeof input === 'function' ? (await input()) : (await input);\n return [null, data];\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, 'RETRY_FAILED'), null];\n}","import { ERROR_CODES } from '../errors/codes';\nimport { formatError } from '../errors/formatter';\nimport { 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 completed in time\n * - Resolves with `[SafeError, null]` on timeout or failure\n * - Never throws\n *\n * @param input A promise or synchronous value\n * @param ms Timeout duration in milliseconds\n */\nexport async function withTimeout<T>(input: T | Promise<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`), 'TIMEOUT_ERROR')), 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 or promise and always returns a predictable tuple\n * instead of throwing errors.\n *\n * ## Usage\n * ```ts\n * const [err, data] = await safe(() => doSomething());\n *\n * if (err) {\n * console.error(err.code, err.message);\n * }\n * ```\n *\n * ## Behavior\n * - Never throws\n * - Always resolves\n * - Normalizes all errors into `SafeError`\n *\n * ## Return\n * - `[null, result]` on success\n * - `[SafeError, null]` on failure\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 ERROR_CODES,\n ErrorCode,\n ErrorWithCause\n} from \"./errors/codes\";\n\nexport type {\n RetryOptions\n} from \"./modules/retry\";\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;AACnB,WAAO,CAAC,MAAM,IAAI;AAAA,EACtB,SAAS,OAAO;AACZ,WAAO,CAAC,YAAY,KAAK,GAAG,IAAI;AAAA,EACpC;AACJ;;;ACAA,SAAS,MAAM,IAAY;AACvB,SAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AACzD;AAwBA,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,OAAO,OAAO,UAAU,aAAc,MAAM,MAAM,IAAM,MAAM;AACpE,aAAO,CAAC,MAAM,IAAI;AAAA,IACtB,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,cAAc,GAAG,IAAI;AACxD;;;AC9CA,eAAsB,YAAe,OAAuB,IAA2B;AACnF,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,eAAe,CAAC,GAAG,EAAE;AAAA,IACzG,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;;;AChBO,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;AAkBD,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":[]}
|