vinh-async-utils 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/async-retry.d.ts +58 -0
- package/dist/lib/async-retry.d.ts.map +1 -0
- package/dist/lib/async-retry.js +77 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @org/async
|
|
2
|
+
|
|
3
|
+
Async utility functions with retry logic and error handling for TypeScript applications.
|
|
4
|
+
|
|
5
|
+
## ๐ฆ Package Information
|
|
6
|
+
|
|
7
|
+
- **Version**: 0.0.1
|
|
8
|
+
- **Publishable**: โ
Yes
|
|
9
|
+
- **Tag**: `scope:async`
|
|
10
|
+
- **Module Boundaries**: Can only import from `scope:shared` packages
|
|
11
|
+
|
|
12
|
+
## โ ๏ธ CI Demo Note
|
|
13
|
+
|
|
14
|
+
**This package includes an intentionally failing test to demonstrate Nx's self-healing CI feature.** When you run tests, you'll see one failure. This is by design to showcase how `nx fix-ci` works in the CI pipeline.
|
|
15
|
+
|
|
16
|
+
## ๐ Features
|
|
17
|
+
|
|
18
|
+
This package provides powerful async utilities:
|
|
19
|
+
|
|
20
|
+
- **retry** - Retry failed async operations with configurable options
|
|
21
|
+
- **createRetry** - Create reusable retry functions with default options
|
|
22
|
+
- **withRetry** - Wrap functions to automatically retry on failure
|
|
23
|
+
- **retryAll** - Retry multiple operations and wait for all
|
|
24
|
+
- **retryRace** - Race multiple operations with retry
|
|
25
|
+
- **retryAllSettled** - Get all results regardless of success/failure
|
|
26
|
+
- **TimeoutError** - Custom error class for timeout scenarios
|
|
27
|
+
|
|
28
|
+
## ๐ Usage Examples
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { retry, createRetry, withRetry, TimeoutError } from '@org/async';
|
|
32
|
+
|
|
33
|
+
// Basic retry with default options
|
|
34
|
+
const result = await retry(async () => {
|
|
35
|
+
const response = await fetch('/api/data');
|
|
36
|
+
return response.json();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Retry with custom options
|
|
40
|
+
const data = await retry(
|
|
41
|
+
async (attempt) => {
|
|
42
|
+
console.log(`Attempt ${attempt + 1}`);
|
|
43
|
+
return await riskyOperation();
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
retries: 3, // Max 3 retries
|
|
47
|
+
delay: 1000, // 1 second delay
|
|
48
|
+
backoff: 2 // Exponential backoff factor
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Create a reusable retry function
|
|
53
|
+
const retryWithDefaults = createRetry({
|
|
54
|
+
retries: 2,
|
|
55
|
+
delay: 500
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const result1 = await retryWithDefaults(operation1);
|
|
59
|
+
const result2 = await retryWithDefaults(operation2);
|
|
60
|
+
|
|
61
|
+
// Wrap a function to automatically retry
|
|
62
|
+
const safeFunction = withRetry(riskyFunction, { retries: 3 });
|
|
63
|
+
const result = await safeFunction(arg1, arg2);
|
|
64
|
+
|
|
65
|
+
// Retry multiple operations
|
|
66
|
+
const results = await retryAll([
|
|
67
|
+
operation1,
|
|
68
|
+
operation2,
|
|
69
|
+
operation3
|
|
70
|
+
], { retries: 2 });
|
|
71
|
+
|
|
72
|
+
// Race operations with retry
|
|
73
|
+
const fastest = await retryRace([
|
|
74
|
+
slowOperation,
|
|
75
|
+
fastOperation
|
|
76
|
+
]);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## ๐งช Testing
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Run tests for this package (includes 1 intentional failure for CI demo)
|
|
83
|
+
nx test async
|
|
84
|
+
|
|
85
|
+
# Run tests in watch mode
|
|
86
|
+
nx test async --watch
|
|
87
|
+
|
|
88
|
+
# Run specific test file
|
|
89
|
+
nx test async --testFile=async-retry.spec.ts
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## ๐๏ธ Building
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Build the package
|
|
96
|
+
nx build async
|
|
97
|
+
|
|
98
|
+
# The build output will be in dist/packages/async
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## ๐ Available Commands
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
nx build async # Build the package
|
|
105
|
+
nx test async # Run tests (includes intentional failure)
|
|
106
|
+
nx lint async # Lint the package
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## ๐ Module Boundaries
|
|
110
|
+
|
|
111
|
+
This package has the tag `scope:async` and can only import from:
|
|
112
|
+
- `@org/utils` (tagged with `scope:shared`)
|
|
113
|
+
|
|
114
|
+
Attempting to import from `@org/colors` or `@org/strings` will result in a linting error due to module boundary constraints.
|
|
115
|
+
|
|
116
|
+
## ๐ง Self-Healing CI Demo
|
|
117
|
+
|
|
118
|
+
This package demonstrates Nx's self-healing CI capabilities. The intentionally failing test helps showcase:
|
|
119
|
+
|
|
120
|
+
1. How CI detects failures
|
|
121
|
+
2. How `nx fix-ci` provides automated suggestions
|
|
122
|
+
3. How the system can self-correct common issues
|
|
123
|
+
|
|
124
|
+
To see this in action in CI, the workflow runs:
|
|
125
|
+
```bash
|
|
126
|
+
npx nx fix-ci
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
This command analyzes failures and provides actionable fixes.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib/async-retry.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for retry function
|
|
3
|
+
*/
|
|
4
|
+
export interface RetryOptions {
|
|
5
|
+
/** Maximum number of retries (default: 3) */
|
|
6
|
+
retries?: number;
|
|
7
|
+
/** Initial delay in ms (default: 1000) */
|
|
8
|
+
delay?: number;
|
|
9
|
+
/** Maximum delay in ms (default: 30000) */
|
|
10
|
+
maxDelay?: number;
|
|
11
|
+
/** Exponential backoff factor (default: 2) */
|
|
12
|
+
factor?: number;
|
|
13
|
+
/** Callback on each retry */
|
|
14
|
+
onRetry?: (error: Error, attempt: number, nextDelay: number) => void;
|
|
15
|
+
/** Function to determine if should retry */
|
|
16
|
+
shouldRetry?: (error: Error, attempt: number) => boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Options for retry with timeout
|
|
20
|
+
*/
|
|
21
|
+
export interface RetryWithTimeoutOptions extends RetryOptions {
|
|
22
|
+
/** Timeout in milliseconds for each attempt */
|
|
23
|
+
timeout?: number;
|
|
24
|
+
/** Custom timeout error message */
|
|
25
|
+
timeoutMessage?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Timeout error class
|
|
29
|
+
*/
|
|
30
|
+
export declare class TimeoutError extends Error {
|
|
31
|
+
code: string;
|
|
32
|
+
constructor(message?: string);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Retry failed async functions with exponential backoff
|
|
36
|
+
*/
|
|
37
|
+
export declare function retry<T>(fn: (attempt: number) => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
38
|
+
/**
|
|
39
|
+
* Create a reusable retry wrapper with preset options
|
|
40
|
+
*/
|
|
41
|
+
export declare function createRetry(defaultOptions?: RetryOptions): <T>(fn: (attempt: number) => Promise<T>, overrides?: RetryOptions) => Promise<T>;
|
|
42
|
+
/**
|
|
43
|
+
* Wrap a function to always retry on failure
|
|
44
|
+
*/
|
|
45
|
+
export declare function withRetry<T extends (...args: any[]) => Promise<any>>(fn: T, options?: RetryOptions): T;
|
|
46
|
+
/**
|
|
47
|
+
* Execute multiple async operations with individual retry logic
|
|
48
|
+
*/
|
|
49
|
+
export declare function retryAll<T>(fns: Array<(attempt: number) => Promise<T>>, options?: RetryOptions): Promise<T[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Execute multiple async operations, returning first successful result
|
|
52
|
+
*/
|
|
53
|
+
export declare function retryRace<T>(fns: Array<(attempt: number) => Promise<T>>, options?: RetryOptions): Promise<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Execute multiple async operations, returning all results (including errors)
|
|
56
|
+
*/
|
|
57
|
+
export declare function retryAllSettled<T>(fns: Array<(attempt: number) => Promise<T>>, options?: RetryOptions): Promise<PromiseSettledResult<T>[]>;
|
|
58
|
+
//# sourceMappingURL=async-retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-retry.d.ts","sourceRoot":"","sources":["../../src/lib/async-retry.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,4CAA4C;IAC5C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1D;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,IAAI,SAAa;gBAEL,OAAO,SAAwB;CAI5C;AAQD;;GAEG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,CAAC,CAsCZ;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,cAAc,GAAE,YAAiB,IACnD,CAAC,EACP,IAAI,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,YAAW,YAAiB,KAC3B,OAAO,CAAC,CAAC,CAAC,CAGd;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAClE,EAAE,EAAE,CAAC,EACL,OAAO,GAAE,YAAiB,GACzB,CAAC,CAIH;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAC3C,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,EAAE,CAAC,CAEd;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAC3C,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACrC,GAAG,EAAE,KAAK,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,EAC3C,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC,CAEpC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout error class
|
|
3
|
+
*/
|
|
4
|
+
export class TimeoutError extends Error {
|
|
5
|
+
code = 'TIMEOUT';
|
|
6
|
+
constructor(message = 'Operation timed out') {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'TimeoutError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Sleep for specified milliseconds
|
|
13
|
+
*/
|
|
14
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
15
|
+
/**
|
|
16
|
+
* Retry failed async functions with exponential backoff
|
|
17
|
+
*/
|
|
18
|
+
export async function retry(fn, options = {}) {
|
|
19
|
+
const { retries = 3, delay = 1000, maxDelay = 30000, factor = 2, onRetry = () => {
|
|
20
|
+
return;
|
|
21
|
+
}, shouldRetry = () => true, } = options;
|
|
22
|
+
let lastError;
|
|
23
|
+
let currentDelay = delay;
|
|
24
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
return await fn(attempt);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
lastError = error;
|
|
30
|
+
// Check if we should retry
|
|
31
|
+
if (attempt === retries || !shouldRetry(lastError, attempt)) {
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
// Call onRetry callback
|
|
35
|
+
onRetry(lastError, attempt, currentDelay);
|
|
36
|
+
// Wait before retrying
|
|
37
|
+
await sleep(currentDelay);
|
|
38
|
+
// Calculate next delay with exponential backoff
|
|
39
|
+
currentDelay = Math.min(currentDelay * factor, maxDelay);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw lastError;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a reusable retry wrapper with preset options
|
|
46
|
+
*/
|
|
47
|
+
export function createRetry(defaultOptions = {}) {
|
|
48
|
+
return (fn, overrides = {}) => {
|
|
49
|
+
return retry(fn, { ...defaultOptions, ...overrides });
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Wrap a function to always retry on failure
|
|
54
|
+
*/
|
|
55
|
+
export function withRetry(fn, options = {}) {
|
|
56
|
+
return (async (...args) => {
|
|
57
|
+
return retry(() => fn(...args), options);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Execute multiple async operations with individual retry logic
|
|
62
|
+
*/
|
|
63
|
+
export async function retryAll(fns, options = {}) {
|
|
64
|
+
return Promise.all(fns.map((fn) => retry(fn, options)));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Execute multiple async operations, returning first successful result
|
|
68
|
+
*/
|
|
69
|
+
export async function retryRace(fns, options = {}) {
|
|
70
|
+
return Promise.race(fns.map((fn) => retry(fn, options)));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Execute multiple async operations, returning all results (including errors)
|
|
74
|
+
*/
|
|
75
|
+
export async function retryAllSettled(fns, options = {}) {
|
|
76
|
+
return Promise.allSettled(fns.map((fn) => retry(fn, options)));
|
|
77
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vinh-async-utils",
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
"./package.json": "./package.json",
|
|
10
|
+
".": {
|
|
11
|
+
"@org/source": "./src/index.ts",
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"!**/*.tsbuildinfo"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"tslib": "^2.3.0"
|
|
23
|
+
},
|
|
24
|
+
"nx": {
|
|
25
|
+
"tags": [
|
|
26
|
+
"scope:async"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|