@emmvish/stable-request 1.6.2 → 1.6.3

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