@emmvish/stable-request 1.6.2 → 1.6.4

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 CHANGED
@@ -1,12 +1,16 @@
1
1
  # @emmvish/stable-request
2
2
 
3
- A powerful HTTP Workflow Execution Engine for Node.js that transforms unreliable API calls into robust, production-ready workflows with advanced retry mechanisms, circuit breakers, and sophisticated execution patterns.
3
+ A production-grade HTTP Workflow Execution Engine for Node.js that transforms unreliable API calls into resilient, observable, and sophisticated multi-phase workflows with intelligent retry strategies, circuit breakers, and advanced execution patterns.
4
4
 
5
5
  ## Navigation
6
6
 
7
7
  - [Overview](#overview)
8
+ - [Why stable-request?](#why-stable-request)
8
9
  - [Installation](#installation)
9
10
  - [Quick Start](#quick-start)
11
+ - [Single Request with Retry](#single-request-with-retry)
12
+ - [Batch Requests (API Gateway)](#batch-requests-api-gateway)
13
+ - [Multi-Phase Workflow](#multi-phase-workflow)
10
14
  - [Core Features](#core-features)
11
15
  - [Intelligent Retry Strategies](#intelligent-retry-strategies)
12
16
  - [Circuit Breaker Pattern](#circuit-breaker-pattern)
@@ -19,19 +23,35 @@ A powerful HTTP Workflow Execution Engine for Node.js that transforms unreliable
19
23
  - [Branched Workflows](#branched-workflows)
20
24
  - [Advanced Capabilities](#advanced-capabilities)
21
25
  - [Config Cascading](#config-cascading)
26
+ - [Request Grouping](#request-grouping)
22
27
  - [Shared Buffer and Pre-Execution Hooks](#shared-buffer-and-pre-execution-hooks)
23
28
  - [Comprehensive Observability](#comprehensive-observability)
24
- - [API Surface](#api-surface)
29
+ - [Trial Mode](#trial-mode)
30
+ - [Common Use Cases](#common-use-cases)
25
31
  - [License](#license)
26
32
 
27
33
  ## Overview
28
34
 
29
- `@emmvish/stable-request` is built for applications that need to orchestrate complex, multi-step API interactions with guarantees around reliability, observability, and fault tolerance. Unlike simple HTTP clients, it provides:
35
+ `@emmvish/stable-request` is engineered for applications requiring robust orchestration of complex, multi-step API interactions with enterprise-grade reliability, observability, and fault tolerance. It goes far beyond simple HTTP clients by providing:
30
36
 
31
- - **Workflow-First Design**: Organize API calls into phases, branches, and decision trees
32
- - **Enterprise Resilience**: Built-in circuit breakers, retry strategies, and failure handling
33
- - **Execution Flexibility**: Sequential, concurrent, mixed, and non-linear execution patterns
34
- - **Production-Ready Observability**: Detailed hooks for monitoring, logging, and error analysis
37
+ - **Workflow-First Architecture**: Organize API calls into phases, branches, and decision trees with full control over execution order
38
+ - **Enterprise Resilience**: Built-in circuit breakers, configurable retry strategies, and sophisticated failure handling
39
+ - **Execution Flexibility**: Sequential, concurrent, mixed, and non-linear execution patterns to match your business logic
40
+ - **Production-Ready Observability**: Comprehensive hooks for monitoring, logging, error analysis, and execution history tracking
41
+ - **Performance Optimization**: Response caching, rate limiting, and concurrency control to maximize efficiency
42
+ - **Type Safety**: Full TypeScript support with 40+ exported types
43
+
44
+ ## Why stable-request?
45
+
46
+ Modern applications often need to:
47
+ - **Orchestrate complex API workflows** with dependencies between steps
48
+ - **Handle unreliable APIs** with intelligent retry and fallback mechanisms
49
+ - **Prevent cascade failures** when downstream services fail
50
+ - **Optimize performance** by caching responses and controlling request rates
51
+ - **Monitor and debug** complex request flows in production
52
+ - **Implement conditional logic** based on API responses (branching, looping)
53
+
54
+ `@emmvish/stable-request` solves all these challenges with a unified, type-safe API that scales from simple requests to sophisticated multi-phase workflows.
35
55
 
36
56
  ## Installation
37
57
 
@@ -39,223 +59,616 @@ A powerful HTTP Workflow Execution Engine for Node.js that transforms unreliable
39
59
  npm install @emmvish/stable-request
40
60
  ```
41
61
 
62
+ **Requirements**: Node.js 14+ (ES Modules)
63
+
42
64
  ## Quick Start
43
65
 
44
66
  ### Single Request with Retry
45
67
 
68
+ Execute a single HTTP request with automatic retry on failure:
69
+
46
70
  ```typescript
47
71
  import { stableRequest, RETRY_STRATEGIES } from '@emmvish/stable-request';
48
72
 
49
- const data = await stableRequest({
73
+ const userData = await stableRequest({
50
74
  reqData: {
51
75
  hostname: 'api.example.com',
52
- path: '/users',
53
- method: 'GET'
76
+ path: '/users/123',
77
+ headers: { 'Authorization': 'Bearer token' }
78
+ }, // 'GET' is default HTTP method, if not specified
79
+ resReq: true, // Return response data
80
+ attempts: 3, // Retry up to 3 times
81
+ wait: 1000, // 1 second between retries
82
+ retryStrategy: RETRY_STRATEGIES.EXPONENTIAL,
83
+ logAllErrors: true // Log all failed attempts
84
+ });
85
+
86
+ console.log(userData); // { id: 123, name: 'John' }
87
+ ```
88
+
89
+ ### Batch Requests (API Gateway)
90
+
91
+ Execute multiple requests concurrently or sequentially:
92
+
93
+ ```typescript
94
+ import { stableApiGateway } from '@emmvish/stable-request';
95
+
96
+ const requests = [
97
+ {
98
+ id: 'users',
99
+ requestOptions: {
100
+ reqData: { path: '/users' },
101
+ resReq: true
102
+ }
54
103
  },
55
- resReq: true,
56
- attempts: 3,
57
- wait: 1000,
58
- retryStrategy: RETRY_STRATEGIES.EXPONENTIAL
104
+ {
105
+ id: 'orders',
106
+ requestOptions: {
107
+ reqData: { path: '/orders' },
108
+ resReq: true
109
+ }
110
+ },
111
+ {
112
+ id: 'products',
113
+ requestOptions: {
114
+ reqData: { path: '/products' },
115
+ resReq: true
116
+ }
117
+ }
118
+ ];
119
+
120
+ const results = await stableApiGateway(requests, {
121
+ concurrentExecution: true, // Execute in parallel
122
+ commonRequestData: {
123
+ hostname: 'api.example.com',
124
+ headers: { 'X-API-Key': 'secret' }
125
+ },
126
+ commonAttempts: 2, // Retry each request twice
127
+ commonWait: 500
128
+ });
129
+
130
+ results.forEach(result => {
131
+ console.log(`${result.id}:`, result.data);
59
132
  });
60
133
  ```
61
134
 
62
135
  ### Multi-Phase Workflow
63
136
 
137
+ Orchestrate complex workflows with multiple phases:
138
+
64
139
  ```typescript
65
- import { stableWorkflow } from '@emmvish/stable-request';
140
+ import { stableWorkflow, PHASE_DECISION_ACTIONS, REQUEST_METHODS } from '@emmvish/stable-request';
66
141
 
67
- const result = await stableWorkflow([
142
+ const phases = [
68
143
  {
69
- id: 'auth',
144
+ id: 'authentication',
70
145
  requests: [
71
- { id: 'login', requestOptions: { reqData: { path: '/auth/login' }, resReq: true } }
146
+ {
147
+ id: 'login',
148
+ requestOptions: {
149
+ reqData: {
150
+ path: '/auth/login',
151
+ method: REQUEST_METHODS.POST,
152
+ body: { username: 'user', password: 'pass' }
153
+ },
154
+ resReq: true
155
+ }
156
+ }
72
157
  ]
73
158
  },
74
159
  {
75
160
  id: 'fetch-data',
76
- concurrentExecution: true,
161
+ concurrentExecution: true, // Execute requests in parallel
77
162
  requests: [
78
- { id: 'users', requestOptions: { reqData: { path: '/users' }, resReq: true } },
79
- { id: 'orders', requestOptions: { reqData: { path: '/orders' }, resReq: true } }
163
+ { id: 'user-profile', requestOptions: { reqData: { path: '/profile' }, resReq: true } },
164
+ { id: 'user-orders', requestOptions: { reqData: { path: '/orders' }, resReq: true } },
165
+ { id: 'user-settings', requestOptions: { reqData: { path: '/settings' }, resReq: true } }
166
+ ]
167
+ },
168
+ {
169
+ id: 'process-data',
170
+ requests: [
171
+ {
172
+ id: 'update-analytics',
173
+ requestOptions: {
174
+ reqData: { path: '/analytics', method: REQUEST_METHODS.POST },
175
+ resReq: false
176
+ }
177
+ }
80
178
  ]
81
179
  }
82
- ], {
180
+ ];
181
+
182
+ const result = await stableWorkflow(phases, {
83
183
  workflowId: 'user-data-sync',
84
184
  commonRequestData: { hostname: 'api.example.com' },
85
- stopOnFirstPhaseError: true
185
+ commonAttempts: 3,
186
+ stopOnFirstPhaseError: true, // Stop if any phase fails
187
+ logPhaseResults: true // Log each phase completion
86
188
  });
189
+
190
+ console.log(`Workflow completed: ${result.success}`);
191
+ console.log(`Total requests: ${result.totalRequests}`);
192
+ console.log(`Successful: ${result.successfulRequests}`);
193
+ console.log(`Failed: ${result.failedRequests}`);
194
+ console.log(`Execution time: ${result.executionTime}ms`);
87
195
  ```
88
196
 
89
197
  ## Core Features
90
198
 
91
199
  ### Intelligent Retry Strategies
92
200
 
93
- Automatically retry failed requests with configurable strategies:
201
+ Automatically retry failed requests with sophisticated backoff strategies:
202
+
203
+ ```typescript
204
+ import { stableRequest, RETRY_STRATEGIES } from '@emmvish/stable-request';
205
+
206
+ // Fixed delay: constant wait time
207
+ await stableRequest({
208
+ reqData: { hostname: 'api.example.com', path: '/data' },
209
+ attempts: 5,
210
+ wait: 1000, // 1 second between each retry
211
+ retryStrategy: RETRY_STRATEGIES.FIXED
212
+ });
213
+
214
+ // Linear backoff: incrementally increasing delays
215
+ await stableRequest({
216
+ reqData: { hostname: 'api.example.com', path: '/data' },
217
+ attempts: 5,
218
+ wait: 1000, // 1s, 2s, 3s, 4s, 5s
219
+ retryStrategy: RETRY_STRATEGIES.LINEAR
220
+ });
94
221
 
95
- - **Fixed Delay**: Constant wait time between retries
96
- - **Linear Backoff**: Incrementally increasing delays
97
- - **Exponential Backoff**: Exponentially growing delays with optional jitter
98
- - **Fibonacci Backoff**: Delays based on Fibonacci sequence
222
+ // Exponential backoff: exponentially growing delays
223
+ await stableRequest({
224
+ reqData: { hostname: 'api.example.com', path: '/data' },
225
+ attempts: 5,
226
+ wait: 1000, // 1s, 2s, 4s, 8s, 16s
227
+ retryStrategy: RETRY_STRATEGIES.EXPONENTIAL
228
+ });
229
+ ```
99
230
 
100
- Each request can have individual retry configurations, or inherit from workflow-level defaults.
231
+ **Features**:
232
+ - Automatic retry on 5xx errors and network failures
233
+ - No retry on 4xx client errors (configurable)
234
+ - Maximum allowed wait time to prevent excessive delays
235
+ - Per-request or workflow-level configuration
236
+
237
+ **Custom Response Validation**:
238
+ ```typescript
239
+ await stableRequest({
240
+ reqData: { hostname: 'api.example.com', path: '/job/status' },
241
+ resReq: true,
242
+ attempts: 10,
243
+ wait: 2000,
244
+ responseAnalyzer: async ({ data }) => {
245
+ // Retry until job is complete
246
+ return data.status === 'completed';
247
+ }
248
+ });
249
+ ```
101
250
 
102
251
  ### Circuit Breaker Pattern
103
252
 
104
253
  Prevent cascade failures and system overload with built-in circuit breakers:
105
254
 
106
- - **Automatic State Management**: Transitions between Closed → Open → Half-Open states
107
- - **Configurable Thresholds**: Set failure rates and time windows
108
- - **Request/Attempt Level Tracking**: Monitor at granular or aggregate levels
109
- - **Graceful Degradation**: Fail fast when services are down
255
+ ```typescript
256
+ import { stableRequest, CircuitBreakerState } from '@emmvish/stable-request';
257
+
258
+ await stableRequest({
259
+ reqData: { hostname: 'unreliable-api.example.com', path: '/data' },
260
+ attempts: 3,
261
+ circuitBreaker: {
262
+ failureThreshold: 5, // Open after 5 failures
263
+ successThreshold: 2, // Close after 2 successes in half-open
264
+ timeout: 60000, // Wait 60s before trying again (half-open)
265
+ trackIndividualAttempts: false // Track at request level (not attempt level)
266
+ }
267
+ });
268
+ ```
269
+
270
+ **Circuit Breaker States**:
271
+ - **CLOSED**: Normal operation, requests flow through
272
+ - **OPEN**: Too many failures, requests blocked immediately
273
+ - **HALF_OPEN**: Testing if service recovered, limited requests allowed
274
+
275
+ **Workflow-Level Circuit Breakers**:
276
+ ```typescript
277
+ import { CircuitBreaker } from '@emmvish/stable-request';
278
+
279
+ const sharedBreaker = new CircuitBreaker({
280
+ failureThreshold: 10,
281
+ successThreshold: 3,
282
+ timeout: 120000
283
+ });
284
+
285
+ await stableWorkflow(phases, {
286
+ circuitBreaker: sharedBreaker, // Shared across all requests
287
+ commonRequestData: { hostname: 'api.example.com' }
288
+ });
289
+
290
+ // Check circuit breaker state
291
+ console.log(sharedBreaker.getState());
292
+ // { state: 'CLOSED', failures: 0, successes: 0, ... }
293
+ ```
110
294
 
111
295
  ### Response Caching
112
296
 
113
- Reduce redundant API calls with intelligent caching:
297
+ Reduce redundant API calls and improve performance with intelligent caching:
298
+
299
+ ```typescript
300
+ await stableRequest({
301
+ reqData: { hostname: 'api.example.com', path: '/static-data' },
302
+ resReq: true,
303
+ cache: {
304
+ enabled: true,
305
+ ttl: 300000, // Cache for 5 minutes
306
+ key: 'custom-cache-key' // Optional: custom cache key
307
+ }
308
+ });
309
+
310
+ // Subsequent identical requests within 5 minutes will use cached response
311
+ ```
312
+
313
+ **Global Cache Management**:
314
+ ```typescript
315
+ import { getGlobalCacheManager, resetGlobalCacheManager } from '@emmvish/stable-request';
316
+
317
+ const cacheManager = getGlobalCacheManager();
318
+
319
+ // Inspect cache statistics
320
+ const stats = cacheManager.getStats();
321
+ console.log(stats);
322
+ // { size: 42, validEntries: 38, expiredEntries: 4 }
323
+
324
+ // Clear all cached responses
325
+ cacheManager.clearAll();
326
+
327
+ // Or reset the global cache instance
328
+ resetGlobalCacheManager();
329
+ ```
114
330
 
115
- - **TTL-Based Expiration**: Configure cache lifetime per request
116
- - **Request Fingerprinting**: Automatic deduplication based on request signature
117
- - **Workflow-Wide Sharing**: Cache responses across phases and branches
118
- - **Manual Cache Management**: Programmatic cache inspection and clearing
331
+ **Cache Features**:
332
+ - Automatic request fingerprinting (method, URL, headers, body)
333
+ - TTL-based expiration
334
+ - Workflow-wide sharing across phases and branches
335
+ - Manual cache inspection and clearing
336
+ - Per-request cache configuration
119
337
 
120
338
  ### Rate Limiting and Concurrency Control
121
339
 
122
340
  Respect API rate limits and control system load:
123
341
 
124
- - **Token Bucket Rate Limiting**: Smooth out request bursts
125
- - **Concurrency Limiters**: Cap maximum parallel requests
126
- - **Per-Phase Configuration**: Different limits for different workflow stages
127
- - **Automatic Queueing**: Requests wait their turn without failing
342
+ ```typescript
343
+ await stableWorkflow(phases, {
344
+ commonRequestData: { hostname: 'api.example.com' },
345
+
346
+ // Rate limiting (token bucket algorithm)
347
+ rateLimit: {
348
+ maxRequests: 100, // 100 requests
349
+ timeWindow: 60000 // per 60 seconds
350
+ },
351
+
352
+ // Concurrency limiting
353
+ maxConcurrentRequests: 5 // Max 5 parallel requests
354
+ });
355
+ ```
356
+
357
+ **Per-Phase Configuration**:
358
+ ```typescript
359
+ const phases = [
360
+ {
361
+ id: 'bulk-import',
362
+ maxConcurrentRequests: 10, // Override workflow limit
363
+ rateLimit: {
364
+ maxRequests: 50,
365
+ timeWindow: 10000
366
+ },
367
+ requests: [...]
368
+ }
369
+ ];
370
+ ```
371
+
372
+ **Standalone Rate Limiter**:
373
+ ```typescript
374
+ import { RateLimiter } from '@emmvish/stable-request';
375
+
376
+ const limiter = new RateLimiter({
377
+ maxRequests: 1000,
378
+ timeWindow: 3600000 // 1000 requests per hour
379
+ });
380
+
381
+ await limiter.acquire(); // Waits if limit exceeded
382
+ // Make request
383
+ ```
128
384
 
129
385
  ## Workflow Execution Patterns
130
386
 
131
387
  ### Sequential and Concurrent Phases
132
388
 
133
- Control execution order at the phase level:
389
+ Control execution order at the phase and request level:
134
390
 
135
- - **Sequential Phases**: Execute phases one after another (default)
136
- - **Concurrent Phases**: Run all phases in parallel
137
- - **Per-Phase Control**: Each phase can define whether its requests run concurrently or sequentially
391
+ **Sequential Phases (Default)**:
392
+ ```typescript
393
+ const phases = [
394
+ { id: 'step-1', requests: [...] }, // Executes first
395
+ { id: 'step-2', requests: [...] }, // Then this
396
+ { id: 'step-3', requests: [...] } // Finally this
397
+ ];
138
398
 
399
+ await stableWorkflow(phases, {
400
+ commonRequestData: { hostname: 'api.example.com' }
401
+ });
402
+ ```
403
+
404
+ **Concurrent Phases**:
139
405
  ```typescript
140
406
  const phases = [
141
- { id: 'init', requests: [...] }, // Sequential phase
142
- {
143
- id: 'parallel-fetch',
144
- concurrentExecution: true, // Concurrent requests within phase
407
+ { id: 'init', requests: [...] },
408
+ { id: 'parallel-1', requests: [...] },
409
+ { id: 'parallel-2', requests: [...] }
410
+ ];
411
+
412
+ await stableWorkflow(phases, {
413
+ concurrentPhaseExecution: true, // All phases run in parallel
414
+ commonRequestData: { hostname: 'api.example.com' }
415
+ });
416
+ ```
417
+
418
+ **Concurrent Requests Within Phase**:
419
+ ```typescript
420
+ const phases = [
421
+ {
422
+ id: 'data-fetch',
423
+ concurrentExecution: true, // Requests run in parallel
424
+ requests: [
425
+ { id: 'users', requestOptions: { reqData: { path: '/users' }, resReq: true } },
426
+ { id: 'products', requestOptions: { reqData: { path: '/products' }, resReq: true } },
427
+ { id: 'orders', requestOptions: { reqData: { path: '/orders' }, resReq: true } }
428
+ ]
429
+ }
430
+ ];
431
+ ```
432
+
433
+ **Stop on First Error**:
434
+ ```typescript
435
+ const phases = [
436
+ {
437
+ id: 'critical-phase',
438
+ stopOnFirstError: true, // Stop phase if any request fails
145
439
  requests: [...]
146
440
  }
147
441
  ];
148
442
 
149
- await stableWorkflow(phases, {
150
- concurrentPhaseExecution: true // Run phases in parallel
443
+ await stableWorkflow(phases, {
444
+ stopOnFirstPhaseError: true, // Stop workflow if any phase fails
445
+ commonRequestData: { hostname: 'api.example.com' }
151
446
  });
152
447
  ```
153
448
 
154
449
  ### Mixed Execution Mode
155
450
 
156
- Combine sequential and concurrent phases in a single workflow:
157
-
158
- - Mark specific phases as concurrent while others remain sequential
159
- - Fine-grained control over execution topology
160
- - Useful for scenarios like: "authenticate first, then fetch data in parallel, then process sequentially"
451
+ Combine sequential and concurrent phases for fine-grained control:
161
452
 
162
453
  ```typescript
163
454
  const phases = [
164
- { id: 'auth', requests: [...] }, // Sequential
165
455
  {
166
- id: 'fetch',
167
- markConcurrentPhase: true, // Runs concurrently with next phase
168
- requests: [...]
456
+ id: 'authenticate',
457
+ requests: [{ id: 'login', requestOptions: {...} }]
458
+ },
459
+ {
460
+ id: 'fetch-user-data',
461
+ markConcurrentPhase: true, // This phase runs concurrently...
462
+ requests: [{ id: 'profile', requestOptions: {...} }]
169
463
  },
170
464
  {
171
- id: 'more-fetch',
172
- markConcurrentPhase: true, // Runs concurrently with previous
173
- requests: [...]
465
+ id: 'fetch-orders',
466
+ markConcurrentPhase: true, // ...with this phase
467
+ requests: [{ id: 'orders', requestOptions: {...} }]
174
468
  },
175
- { id: 'process', requests: [...] } // Sequential, waits for above
469
+ {
470
+ id: 'process-results', // This waits for above to complete
471
+ requests: [{ id: 'analytics', requestOptions: {...} }]
472
+ }
176
473
  ];
177
474
 
178
- await stableWorkflow(phases, {
179
- enableMixedExecution: true
475
+ await stableWorkflow(phases, {
476
+ enableMixedExecution: true, // Enable mixed execution mode
477
+ commonRequestData: { hostname: 'api.example.com' }
180
478
  });
181
479
  ```
182
480
 
183
- ### Non-Linear Workflows
481
+ **Use Case**: Authenticate first (sequential), then fetch multiple data sources in parallel (concurrent), then process results (sequential).
184
482
 
185
- Build dynamic workflows with conditional branching and looping:
483
+ ### Non-Linear Workflows
186
484
 
187
- - **JUMP**: Skip to a specific phase based on runtime conditions
188
- - **SKIP**: Skip upcoming phases and jump to a target
189
- - **REPLAY**: Re-execute the current phase (with limits)
190
- - **TERMINATE**: Stop the entire workflow early
191
- - **CONTINUE**: Proceed to the next phase (default)
485
+ Build dynamic workflows with conditional branching, looping, and early termination:
192
486
 
193
487
  ```typescript
488
+ import { PHASE_DECISION_ACTIONS } from '@emmvish/stable-request';
489
+
194
490
  const phases = [
195
491
  {
196
- id: 'validate',
197
- requests: [...],
198
- phaseDecisionHook: async ({ phaseResult }) => {
199
- if (phaseResult.responses[0].data.isValid) {
200
- return { action: PHASE_DECISION_ACTIONS.JUMP, targetPhaseId: 'success' };
492
+ id: 'validate-user',
493
+ requests: [
494
+ { id: 'check', requestOptions: { reqData: { path: '/validate' }, resReq: true } }
495
+ ],
496
+ phaseDecisionHook: async ({ phaseResult, sharedBuffer }) => {
497
+ const isValid = phaseResult.responses[0]?.data?.isValid;
498
+
499
+ if (isValid) {
500
+ // Jump directly to success phase
501
+ return {
502
+ action: PHASE_DECISION_ACTIONS.JUMP,
503
+ targetPhaseId: 'success-flow'
504
+ };
505
+ } else {
506
+ // Continue to retry logic
507
+ return { action: PHASE_DECISION_ACTIONS.CONTINUE };
201
508
  }
202
- return { action: PHASE_DECISION_ACTIONS.CONTINUE };
203
509
  }
204
510
  },
205
- { id: 'retry-logic', requests: [...] },
206
- { id: 'success', requests: [...] }
511
+ {
512
+ id: 'retry-validation',
513
+ allowReplay: true,
514
+ maxReplayCount: 3,
515
+ requests: [
516
+ { id: 'retry', requestOptions: { reqData: { path: '/retry-validate' }, resReq: true } }
517
+ ],
518
+ phaseDecisionHook: async ({ phaseResult, executionHistory }) => {
519
+ const replayCount = executionHistory.filter(
520
+ h => h.phaseId === 'retry-validation'
521
+ ).length;
522
+
523
+ const success = phaseResult.responses[0]?.data?.success;
524
+
525
+ if (success) {
526
+ return { action: PHASE_DECISION_ACTIONS.JUMP, targetPhaseId: 'success-flow' };
527
+ } else if (replayCount < 3) {
528
+ return { action: PHASE_DECISION_ACTIONS.REPLAY };
529
+ } else {
530
+ return { action: PHASE_DECISION_ACTIONS.TERMINATE, metadata: { reason: 'Max retries exceeded' } };
531
+ }
532
+ }
533
+ },
534
+ {
535
+ id: 'success-flow',
536
+ requests: [
537
+ { id: 'finalize', requestOptions: { reqData: { path: '/finalize' }, resReq: true } }
538
+ ]
539
+ }
207
540
  ];
208
541
 
209
- await stableWorkflow(phases, {
210
- enableNonLinearExecution: true
542
+ const result = await stableWorkflow(phases, {
543
+ enableNonLinearExecution: true, // Enable non-linear execution
544
+ workflowId: 'adaptive-validation',
545
+ commonRequestData: { hostname: 'api.example.com' }
211
546
  });
547
+
548
+ console.log(result.executionHistory);
549
+ // Array of execution records showing which phases ran and why
212
550
  ```
213
551
 
552
+ **Phase Decision Actions**:
553
+ - **CONTINUE**: Proceed to next sequential phase (default)
554
+ - **JUMP**: Skip to a specific phase by ID
555
+ - **SKIP**: Skip upcoming phases until a target phase (or end)
556
+ - **REPLAY**: Re-execute the current phase (requires `allowReplay: true`)
557
+ - **TERMINATE**: Stop the entire workflow immediately
558
+
214
559
  **Decision Hook Context**:
215
- - Access to current phase results
216
- - Execution history (replay count, previous phases)
217
- - Shared buffer for cross-phase state
218
- - Concurrent phase results (in mixed execution)
560
+ ```typescript
561
+ phaseDecisionHook: async ({
562
+ phaseResult, // Current phase execution result
563
+ executionHistory, // Array of all executed phases
564
+ sharedBuffer, // Cross-phase shared state
565
+ concurrentResults // Results from concurrent phases (mixed execution)
566
+ }) => {
567
+ // Your decision logic
568
+ return { action: PHASE_DECISION_ACTIONS.CONTINUE };
569
+ }
570
+ ```
571
+
572
+ **Replay Limits**:
573
+ ```typescript
574
+ {
575
+ id: 'polling-phase',
576
+ allowReplay: true,
577
+ maxReplayCount: 10, // Maximum 10 replays
578
+ requests: [...],
579
+ phaseDecisionHook: async ({ phaseResult }) => {
580
+ if (phaseResult.responses[0]?.data?.ready) {
581
+ return { action: PHASE_DECISION_ACTIONS.CONTINUE };
582
+ }
583
+ return { action: PHASE_DECISION_ACTIONS.REPLAY };
584
+ }
585
+ }
586
+ ```
219
587
 
220
588
  ### Branched Workflows
221
589
 
222
590
  Execute multiple independent workflow paths in parallel or sequentially:
223
591
 
224
- - **Parallel Branches**: Run branches concurrently (mark with `markConcurrentBranch: true`)
225
- - **Sequential Branches**: Execute branches one after another
226
- - **Branch-Level Decisions**: Control workflow from branch hooks
227
- - **Branch Replay/Termination**: Branches support non-linear execution too
228
-
229
592
  ```typescript
230
593
  const branches = [
231
594
  {
232
595
  id: 'user-flow',
233
- markConcurrentBranch: true, // Parallel
234
- phases: [...]
596
+ markConcurrentBranch: true, // Execute in parallel
597
+ phases: [
598
+ { id: 'fetch-user', requests: [...] },
599
+ { id: 'update-user', requests: [...] }
600
+ ]
235
601
  },
236
602
  {
237
603
  id: 'analytics-flow',
238
- markConcurrentBranch: true, // Parallel
239
- phases: [...]
604
+ markConcurrentBranch: true, // Execute in parallel
605
+ phases: [
606
+ { id: 'log-event', requests: [...] },
607
+ { id: 'update-metrics', requests: [...] }
608
+ ]
240
609
  },
241
610
  {
242
- id: 'cleanup-flow', // Sequential (default)
243
- phases: [...]
611
+ id: 'cleanup-flow', // Sequential (waits for above)
612
+ phases: [
613
+ { id: 'clear-cache', requests: [...] },
614
+ { id: 'notify', requests: [...] }
615
+ ]
244
616
  }
245
617
  ];
246
618
 
247
- await stableWorkflow([], {
619
+ const result = await stableWorkflow([], { // Empty phases array
248
620
  enableBranchExecution: true,
249
- branches
621
+ branches,
622
+ workflowId: 'multi-branch-workflow',
623
+ commonRequestData: { hostname: 'api.example.com' }
250
624
  });
625
+
626
+ console.log(result.branches); // Branch execution results
627
+ console.log(result.branchExecutionHistory); // Branch-level execution history
628
+ ```
629
+
630
+ **Branch-Level Configuration**:
631
+ ```typescript
632
+ const branches = [
633
+ {
634
+ id: 'high-priority-branch',
635
+ markConcurrentBranch: false,
636
+ commonConfig: { // Branch-level config overrides
637
+ commonAttempts: 5,
638
+ commonWait: 2000,
639
+ commonCache: { enabled: true, ttl: 120000 }
640
+ },
641
+ phases: [...]
642
+ }
643
+ ];
251
644
  ```
252
645
 
253
646
  **Branch Features**:
254
- - Each branch has its own phase execution
647
+ - Each branch has independent phase execution
255
648
  - Branches share the workflow's `sharedBuffer`
256
649
  - Branch decision hooks can terminate the entire workflow
257
650
  - Supports all execution patterns (mixed, non-linear) within branches
258
651
 
652
+ **Branch Decision Hooks**:
653
+ ```typescript
654
+ const branches = [
655
+ {
656
+ id: 'conditional-branch',
657
+ branchDecisionHook: async ({ branchResult, sharedBuffer }) => {
658
+ if (branchResult.failedRequests > 0) {
659
+ return {
660
+ action: PHASE_DECISION_ACTIONS.TERMINATE,
661
+ terminateWorkflow: true, // Terminate entire workflow
662
+ metadata: { reason: 'Critical branch failed' }
663
+ };
664
+ }
665
+ return { action: PHASE_DECISION_ACTIONS.CONTINUE };
666
+ },
667
+ phases: [...]
668
+ }
669
+ ];
670
+ ```
671
+
259
672
  ## Advanced Capabilities
260
673
 
261
674
  ### Config Cascading
@@ -267,7 +680,12 @@ await stableWorkflow(phases, {
267
680
  // Workflow-level config (lowest priority)
268
681
  commonAttempts: 3,
269
682
  commonWait: 1000,
683
+ commonRetryStrategy: RETRY_STRATEGIES.EXPONENTIAL,
270
684
  commonCache: { enabled: true, ttl: 60000 },
685
+ commonRequestData: {
686
+ hostname: 'api.example.com',
687
+ headers: { 'X-API-Version': 'v2' }
688
+ },
271
689
 
272
690
  branches: [{
273
691
  id: 'my-branch',
@@ -280,13 +698,17 @@ await stableWorkflow(phases, {
280
698
  id: 'my-phase',
281
699
  commonConfig: {
282
700
  // Phase-level config (overrides branch and workflow)
283
- commonAttempts: 1
701
+ commonAttempts: 1,
702
+ commonCache: { enabled: false }
284
703
  },
285
704
  requests: [{
705
+ id: 'my-request',
286
706
  requestOptions: {
287
707
  // Request-level config (highest priority)
708
+ reqData: { path: '/critical' },
288
709
  attempts: 10,
289
- cache: { enabled: false }
710
+ wait: 100,
711
+ cache: { enabled: true, ttl: 300000 }
290
712
  }
291
713
  }]
292
714
  }]
@@ -294,86 +716,451 @@ await stableWorkflow(phases, {
294
716
  });
295
717
  ```
296
718
 
719
+ **Priority**: Request > Phase > Branch > Workflow
720
+
721
+ ### Request Grouping
722
+
723
+ Define reusable configurations for groups of related requests:
724
+
725
+ ```typescript
726
+ const requests = [
727
+ {
728
+ id: 'critical-1',
729
+ groupId: 'critical',
730
+ requestOptions: { reqData: { path: '/critical/1' }, resReq: true }
731
+ },
732
+ {
733
+ id: 'critical-2',
734
+ groupId: 'critical',
735
+ requestOptions: { reqData: { path: '/critical/2' }, resReq: true }
736
+ },
737
+ {
738
+ id: 'optional-1',
739
+ groupId: 'optional',
740
+ requestOptions: { reqData: { path: '/optional/1' }, resReq: false }
741
+ }
742
+ ];
743
+
744
+ await stableApiGateway(requests, {
745
+ commonRequestData: { hostname: 'api.example.com' },
746
+ commonAttempts: 1, // Default: 1 attempt
747
+
748
+ requestGroups: [
749
+ {
750
+ groupId: 'critical',
751
+ commonAttempts: 5, // Critical requests: 5 attempts
752
+ commonWait: 2000,
753
+ commonRetryStrategy: RETRY_STRATEGIES.EXPONENTIAL,
754
+ commonFinalErrorAnalyzer: async () => false // Never suppress errors
755
+ },
756
+ {
757
+ groupId: 'optional',
758
+ commonAttempts: 2, // Optional requests: 2 attempts
759
+ commonWait: 500,
760
+ commonFinalErrorAnalyzer: async () => true // Suppress errors (return false)
761
+ }
762
+ ]
763
+ });
764
+ ```
765
+
766
+ **Use Cases**:
767
+ - Different retry strategies for critical vs. optional requests
768
+ - Separate error handling for different request types
769
+ - Grouped logging and monitoring
770
+
297
771
  ### Shared Buffer and Pre-Execution Hooks
298
772
 
299
- Share state and transform requests dynamically:
773
+ Share state across phases/branches and dynamically transform requests:
300
774
 
301
- **Shared Buffer**: Cross-phase/branch communication
775
+ **Shared Buffer**:
302
776
  ```typescript
303
- const sharedBuffer = { authToken: null };
777
+ const sharedBuffer = {
778
+ authToken: null,
779
+ userId: null,
780
+ metrics: []
781
+ };
782
+
783
+ const phases = [
784
+ {
785
+ id: 'auth',
786
+ requests: [{
787
+ id: 'login',
788
+ requestOptions: {
789
+ reqData: { path: '/login', method: REQUEST_METHODS.POST },
790
+ resReq: true,
791
+ preExecution: {
792
+ preExecutionHook: ({ commonBuffer }) => {
793
+ // Write to buffer after response
794
+ return {};
795
+ },
796
+ preExecutionHookParams: {},
797
+ applyPreExecutionConfigOverride: false,
798
+ continueOnPreExecutionHookFailure: false
799
+ }
800
+ }
801
+ }]
802
+ },
803
+ {
804
+ id: 'fetch-data',
805
+ requests: [{
806
+ id: 'profile',
807
+ requestOptions: {
808
+ reqData: { path: '/profile' },
809
+ resReq: true,
810
+ preExecution: {
811
+ preExecutionHook: ({ commonBuffer }) => {
812
+ // Use token from buffer
813
+ return {
814
+ reqData: {
815
+ headers: {
816
+ 'Authorization': `Bearer ${commonBuffer.authToken}`
817
+ }
818
+ }
819
+ };
820
+ },
821
+ applyPreExecutionConfigOverride: true // Apply returned config
822
+ }
823
+ }
824
+ }]
825
+ }
826
+ ];
304
827
 
305
828
  await stableWorkflow(phases, {
306
829
  sharedBuffer,
307
- // Phases can read/write to sharedBuffer via preExecution hooks
830
+ commonRequestData: { hostname: 'api.example.com' }
308
831
  });
832
+
833
+ console.log(sharedBuffer); // Updated with data from workflow
309
834
  ```
310
835
 
311
- **Pre-Execution Hooks**: Modify requests before execution
836
+ **Pre-Execution Hook Use Cases**:
837
+ - Dynamic header injection (auth tokens, correlation IDs)
838
+ - Request payload transformation based on previous responses
839
+ - Conditional request configuration (skip, modify, enhance)
840
+ - Cross-phase state management
841
+
842
+ **Hook Failure Handling**:
312
843
  ```typescript
313
844
  {
314
- requestOptions: {
315
- preExecution: {
316
- preExecutionHook: ({ commonBuffer, inputParams }) => {
317
- // Access buffer, compute values, return config overrides
318
- return {
319
- reqData: {
320
- headers: { 'Authorization': `Bearer ${commonBuffer.authToken}` }
321
- }
322
- };
323
- },
324
- applyPreExecutionConfigOverride: true
325
- }
845
+ preExecution: {
846
+ preExecutionHook: async ({ commonBuffer, inputParams }) => {
847
+ // May throw error
848
+ const token = await fetchTokenFromExternalSource();
849
+ return { reqData: { headers: { 'Authorization': token } } };
850
+ },
851
+ continueOnPreExecutionHookFailure: true // Continue even if hook fails
326
852
  }
327
853
  }
328
854
  ```
329
855
 
330
856
  ### Comprehensive Observability
331
857
 
332
- Built-in hooks for monitoring, logging, and analysis:
858
+ Built-in hooks for monitoring, logging, and analysis at every level:
333
859
 
334
860
  **Request-Level Hooks**:
335
- - `responseAnalyzer`: Validate responses, trigger retries based on business logic
336
- - `handleErrors`: Custom error handling and logging
337
- - `handleSuccessfulAttemptData`: Log successful attempts
338
- - `finalErrorAnalyzer`: Analyze final failure after all retries
861
+ ```typescript
862
+ await stableRequest({
863
+ reqData: { hostname: 'api.example.com', path: '/data' },
864
+ resReq: true,
865
+ attempts: 3,
866
+
867
+ // Validate response content
868
+ responseAnalyzer: async ({ data, reqData, params }) => {
869
+ console.log('Analyzing response:', data);
870
+ return data.status === 'success'; // false = retry
871
+ },
872
+
873
+ // Custom error handling
874
+ handleErrors: async ({ errorLog, reqData, commonBuffer }) => {
875
+ console.error('Request failed:', errorLog);
876
+ await sendToMonitoring(errorLog);
877
+ },
878
+
879
+ // Log successful attempts
880
+ handleSuccessfulAttemptData: async ({ successfulAttemptData, reqData }) => {
881
+ console.log('Request succeeded:', successfulAttemptData);
882
+ },
883
+
884
+ // Analyze final error after all retries
885
+ finalErrorAnalyzer: async ({ error, reqData }) => {
886
+ console.error('All retries exhausted:', error);
887
+ return error.message.includes('404'); // true = return false instead of throw
888
+ },
889
+
890
+ // Pass custom parameters to hooks
891
+ hookParams: {
892
+ responseAnalyzerParams: { expectedFormat: 'json' },
893
+ handleErrorsParams: { alertChannel: 'slack' }
894
+ },
895
+
896
+ logAllErrors: true,
897
+ logAllSuccessfulAttempts: true
898
+ });
899
+ ```
339
900
 
340
901
  **Workflow-Level Hooks**:
341
- - `handlePhaseCompletion`: React to phase completion
342
- - `handlePhaseError`: Handle phase-level failures
343
- - `handlePhaseDecision`: Monitor non-linear execution decisions
344
- - `handleBranchCompletion`: Track branch execution
345
- - `handleBranchDecision`: Monitor branch-level decisions
902
+ ```typescript
903
+ await stableWorkflow(phases, {
904
+ workflowId: 'monitored-workflow',
905
+
906
+ // Called after each phase completes
907
+ handlePhaseCompletion: async ({ workflowId, phaseResult, params }) => {
908
+ console.log(`Phase ${phaseResult.phaseId} completed`);
909
+ console.log(`Requests: ${phaseResult.totalRequests}`);
910
+ console.log(`Success: ${phaseResult.successfulRequests}`);
911
+ console.log(`Failed: ${phaseResult.failedRequests}`);
912
+ await sendMetrics(phaseResult);
913
+ },
914
+
915
+ // Called when a phase fails
916
+ handlePhaseError: async ({ workflowId, error, phaseResult }) => {
917
+ console.error(`Phase ${phaseResult.phaseId} failed:`, error);
918
+ await alertOnCall(error);
919
+ },
920
+
921
+ // Monitor non-linear execution decisions
922
+ handlePhaseDecision: async ({ workflowId, decision, phaseResult }) => {
923
+ console.log(`Phase decision: ${decision.action}`);
924
+ if (decision.targetPhaseId) {
925
+ console.log(`Target: ${decision.targetPhaseId}`);
926
+ }
927
+ },
928
+
929
+ // Monitor branch completion
930
+ handleBranchCompletion: async ({ workflowId, branchResult }) => {
931
+ console.log(`Branch ${branchResult.branchId} completed`);
932
+ },
933
+
934
+ // Monitor branch decisions
935
+ handleBranchDecision: async ({ workflowId, decision, branchResult }) => {
936
+ console.log(`Branch decision: ${decision.action}`);
937
+ },
938
+
939
+ // Pass parameters to workflow hooks
940
+ workflowHookParams: {
941
+ handlePhaseCompletionParams: { environment: 'production' },
942
+ handlePhaseErrorParams: { severity: 'high' }
943
+ },
944
+
945
+ logPhaseResults: true,
946
+ commonRequestData: { hostname: 'api.example.com' }
947
+ });
948
+ ```
346
949
 
347
950
  **Execution History**:
348
- Every workflow result includes detailed execution history with timestamps, decisions, and metadata.
951
+ ```typescript
952
+ const result = await stableWorkflow(phases, {
953
+ enableNonLinearExecution: true,
954
+ workflowId: 'tracked-workflow',
955
+ commonRequestData: { hostname: 'api.example.com' }
956
+ });
957
+
958
+ // Detailed execution history
959
+ result.executionHistory.forEach(record => {
960
+ console.log({
961
+ phaseId: record.phaseId,
962
+ executionNumber: record.executionNumber,
963
+ decision: record.decision,
964
+ timestamp: record.timestamp,
965
+ metadata: record.metadata
966
+ });
967
+ });
968
+
969
+ // Branch execution history
970
+ result.branchExecutionHistory?.forEach(record => {
971
+ console.log({
972
+ branchId: record.branchId,
973
+ action: record.action,
974
+ timestamp: record.timestamp
975
+ });
976
+ });
977
+ ```
978
+
979
+ ### Trial Mode
980
+
981
+ Test and debug workflows without making real API calls:
982
+
983
+ ```typescript
984
+ await stableRequest({
985
+ reqData: { hostname: 'api.example.com', path: '/data' },
986
+ resReq: true,
987
+ attempts: 3,
988
+ trialMode: {
989
+ enabled: true,
990
+ successProbability: 0.5, // 50% chance of success
991
+ retryableProbability: 0.8, // 80% of failures are retryable
992
+ latencyRange: { min: 100, max: 500 } // Simulated latency: 100-500ms
993
+ }
994
+ });
995
+ ```
996
+
997
+ **Use Cases**:
998
+ - Test retry logic without hitting APIs
999
+ - Simulate failure scenarios
1000
+ - Load testing with controlled failure rates
1001
+ - Development without backend dependencies
1002
+
1003
+ ## Common Use Cases
1004
+
1005
+ ### Multi-Step Data Synchronization
1006
+
1007
+ ```typescript
1008
+ const syncPhases = [
1009
+ {
1010
+ id: 'fetch-source-data',
1011
+ concurrentExecution: true,
1012
+ requests: [
1013
+ { id: 'users', requestOptions: { reqData: { path: '/source/users' }, resReq: true } },
1014
+ { id: 'orders', requestOptions: { reqData: { path: '/source/orders' }, resReq: true } }
1015
+ ]
1016
+ },
1017
+ {
1018
+ id: 'transform-data',
1019
+ requests: [
1020
+ {
1021
+ id: 'transform',
1022
+ requestOptions: {
1023
+ reqData: { path: '/transform', method: REQUEST_METHODS.POST },
1024
+ resReq: true
1025
+ }
1026
+ }
1027
+ ]
1028
+ },
1029
+ {
1030
+ id: 'upload-to-destination',
1031
+ concurrentExecution: true,
1032
+ requests: [
1033
+ { id: 'upload-users', requestOptions: { reqData: { path: '/dest/users', method: REQUEST_METHODS.POST }, resReq: false } },
1034
+ { id: 'upload-orders', requestOptions: { reqData: { path: '/dest/orders', method: REQUEST_METHODS.POST }, resReq: false } }
1035
+ ]
1036
+ }
1037
+ ];
349
1038
 
350
- ## API Surface
1039
+ await stableWorkflow(syncPhases, {
1040
+ workflowId: 'data-sync',
1041
+ commonRequestData: { hostname: 'api.example.com' },
1042
+ commonAttempts: 3,
1043
+ stopOnFirstPhaseError: true,
1044
+ logPhaseResults: true
1045
+ });
1046
+ ```
351
1047
 
352
- ### Core Functions
1048
+ ### API Gateway with Fallbacks
353
1049
 
354
- - **`stableRequest`**: Single HTTP request with retry logic
355
- - **`stableApiGateway`**: Execute multiple requests (concurrent or sequential)
356
- - **`stableWorkflow`**: Orchestrate multi-phase workflows with advanced patterns
1050
+ ```typescript
1051
+ const requests = [
1052
+ {
1053
+ id: 'primary-service',
1054
+ groupId: 'critical',
1055
+ requestOptions: {
1056
+ reqData: { hostname: 'primary.api.com', path: '/data' },
1057
+ resReq: true,
1058
+ finalErrorAnalyzer: async ({ error }) => {
1059
+ // If primary fails, mark as handled (don't throw)
1060
+ return true;
1061
+ }
1062
+ }
1063
+ },
1064
+ {
1065
+ id: 'fallback-service',
1066
+ groupId: 'fallback',
1067
+ requestOptions: {
1068
+ reqData: { hostname: 'backup.api.com', path: '/data' },
1069
+ resReq: true
1070
+ }
1071
+ }
1072
+ ];
357
1073
 
358
- ### Utility Exports
1074
+ const results = await stableApiGateway(requests, {
1075
+ concurrentExecution: false, // Sequential: try fallback only if primary fails
1076
+ requestGroups: [
1077
+ { groupId: 'critical', commonAttempts: 3 },
1078
+ { groupId: 'fallback', commonAttempts: 1 }
1079
+ ]
1080
+ });
1081
+ ```
359
1082
 
360
- - **Circuit Breaker**: `CircuitBreaker`, `CircuitBreakerOpenError`
361
- - **Rate Limiting**: `RateLimiter`
362
- - **Concurrency**: `ConcurrencyLimiter`
363
- - **Caching**: `CacheManager`, `getGlobalCacheManager`, `resetGlobalCacheManager`
364
- - **Execution Utilities**: `executeNonLinearWorkflow`, `executeBranchWorkflow`, `executePhase`
1083
+ ### Polling with Conditional Termination
365
1084
 
366
- ### Enums
1085
+ ```typescript
1086
+ const pollingPhases = [
1087
+ {
1088
+ id: 'poll-job-status',
1089
+ allowReplay: true,
1090
+ maxReplayCount: 20,
1091
+ requests: [
1092
+ {
1093
+ id: 'status-check',
1094
+ requestOptions: {
1095
+ reqData: { path: '/job/status' },
1096
+ resReq: true
1097
+ }
1098
+ }
1099
+ ],
1100
+ phaseDecisionHook: async ({ phaseResult, executionHistory }) => {
1101
+ const status = phaseResult.responses[0]?.data?.status;
1102
+ const attempts = executionHistory.filter(h => h.phaseId === 'poll-job-status').length;
1103
+
1104
+ if (status === 'completed') {
1105
+ return { action: PHASE_DECISION_ACTIONS.CONTINUE };
1106
+ } else if (status === 'failed') {
1107
+ return { action: PHASE_DECISION_ACTIONS.TERMINATE, metadata: { reason: 'Job failed' } };
1108
+ } else if (attempts < 20) {
1109
+ return { action: PHASE_DECISION_ACTIONS.REPLAY };
1110
+ } else {
1111
+ return { action: PHASE_DECISION_ACTIONS.TERMINATE, metadata: { reason: 'Timeout' } };
1112
+ }
1113
+ }
1114
+ },
1115
+ {
1116
+ id: 'process-results',
1117
+ requests: [
1118
+ { id: 'fetch-results', requestOptions: { reqData: { path: '/job/results' }, resReq: true } }
1119
+ ]
1120
+ }
1121
+ ];
1122
+
1123
+ await stableWorkflow(pollingPhases, {
1124
+ enableNonLinearExecution: true,
1125
+ commonRequestData: { hostname: 'api.example.com' },
1126
+ commonWait: 5000 // 5 second wait between polls
1127
+ });
1128
+ ```
367
1129
 
368
- - `RETRY_STRATEGIES`: Fixed, Linear, Exponential, Fibonacci
369
- - `REQUEST_METHODS`: GET, POST, PUT, PATCH, DELETE, etc.
370
- - `PHASE_DECISION_ACTIONS`: CONTINUE, JUMP, SKIP, REPLAY, TERMINATE
371
- - `VALID_REQUEST_PROTOCOLS`: HTTP, HTTPS
372
- - `CircuitBreakerState`: CLOSED, OPEN, HALF_OPEN
1130
+ ### Webhook Retry with Circuit Breaker
373
1131
 
374
- ### TypeScript Types
1132
+ ```typescript
1133
+ import { CircuitBreaker, REQUEST_METHODS } from '@emmvish/stable-request';
1134
+
1135
+ const webhookBreaker = new CircuitBreaker({
1136
+ failureThreshold: 3,
1137
+ successThreshold: 2,
1138
+ timeout: 30000
1139
+ });
375
1140
 
376
- Full TypeScript support with 40+ exported types for complete type safety across workflows, requests, configurations, and hooks.
1141
+ async function sendWebhook(eventData: any) {
1142
+ try {
1143
+ await stableRequest({
1144
+ reqData: {
1145
+ hostname: 'webhook.example.com',
1146
+ path: '/events',
1147
+ method: REQUEST_METHODS.POST,
1148
+ body: eventData
1149
+ },
1150
+ attempts: 5,
1151
+ wait: 1000,
1152
+ retryStrategy: RETRY_STRATEGIES.EXPONENTIAL,
1153
+ circuitBreaker: webhookBreaker,
1154
+ handleErrors: async ({ errorLog }) => {
1155
+ console.error('Webhook delivery failed:', errorLog);
1156
+ await queueForRetry(eventData);
1157
+ }
1158
+ });
1159
+ } catch (error) {
1160
+ console.error('Webhook permanently failed:', error);
1161
+ }
1162
+ }
1163
+ ```
377
1164
 
378
1165
  ## License
379
1166