@yildizpay/http-adapter 3.7.0 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/README.tr.md +72 -0
- package/dist/builders/http-adapter.builder.d.ts +3 -0
- package/dist/builders/http-adapter.builder.js +5 -1
- package/dist/builders/http-adapter.builder.js.map +1 -1
- package/dist/core/default-http-client.js +3 -1
- package/dist/core/default-http-client.js.map +1 -1
- package/dist/core/http.adapter.d.ts +3 -1
- package/dist/core/http.adapter.js +19 -7
- package/dist/core/http.adapter.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/observability/circuit-breaker-observer.d.ts +7 -0
- package/dist/observability/circuit-breaker-observer.js +3 -0
- package/dist/observability/circuit-breaker-observer.js.map +1 -0
- package/dist/observability/http-adapter-observer.d.ts +9 -0
- package/dist/observability/http-adapter-observer.js +3 -0
- package/dist/observability/http-adapter-observer.js.map +1 -0
- package/dist/resilience/circuit-breaker/circuit-breaker.d.ts +3 -0
- package/dist/resilience/circuit-breaker/circuit-breaker.js +11 -1
- package/dist/resilience/circuit-breaker/circuit-breaker.js.map +1 -1
- package/dist/resilience/retry-executor.d.ts +3 -1
- package/dist/resilience/retry-executor.js +6 -1
- package/dist/resilience/retry-executor.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -415,6 +415,78 @@ Node.js runs on a single-threaded event loop, but `async/await` introduces coope
|
|
|
415
415
|
|
|
416
416
|
To prevent this, the circuit breaker uses a **probe flag**: only the first caller gets the probe slot; all subsequent concurrent callers receive `CircuitBreakerOpenException` until the probe resolves. This is a deliberate trade-off — a few requests are rejected in exchange for a controlled, safe recovery test.
|
|
417
417
|
|
|
418
|
+
## Observability
|
|
419
|
+
|
|
420
|
+
The adapter ships with a two-tier observability system. **Observers are read-only** — they cannot modify requests or responses. Use them for metrics, structured logging, and distributed tracing. Use interceptors when you need to mutate the pipeline.
|
|
421
|
+
|
|
422
|
+
### `HttpAdapterObserver`
|
|
423
|
+
|
|
424
|
+
Attach a single observer to the adapter via `.withObserver()` on the builder.
|
|
425
|
+
|
|
426
|
+
| Hook | When it fires |
|
|
427
|
+
|---|---|
|
|
428
|
+
| `onRequestStart(request)` | After all request interceptors, immediately before the HTTP call |
|
|
429
|
+
| `onRequestSuccess(response, durationMs)` | After a successful response (includes retry time if retries occurred) |
|
|
430
|
+
| `onRequestFailure(error, durationMs)` | When the final error is propagated to the caller |
|
|
431
|
+
| `onRetry(attempt, error, delayMs)` | Each time a retry is scheduled, before the backoff delay |
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { HttpAdapterObserver, HttpAdapter, RetryPolicies } from '@yildizpay/http-adapter';
|
|
435
|
+
|
|
436
|
+
class MetricsObserver implements HttpAdapterObserver {
|
|
437
|
+
onRequestSuccess(_response: Response, durationMs: number): void {
|
|
438
|
+
metrics.histogram('http.request.duration', durationMs);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
onRequestFailure(error: BaseAdapterException, durationMs: number): void {
|
|
442
|
+
metrics.increment('http.request.error', { type: error.name });
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
onRetry(attempt: number, _error: BaseAdapterException, delayMs: number): void {
|
|
446
|
+
logger.warn(`Retry attempt ${attempt} in ${delayMs}ms`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const adapter = HttpAdapter.builder()
|
|
451
|
+
.withRetryPolicy(RetryPolicies.exponential(3))
|
|
452
|
+
.withObserver(new MetricsObserver())
|
|
453
|
+
.build();
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### `CircuitBreakerObserver`
|
|
457
|
+
|
|
458
|
+
Attach an observer to a `CircuitBreaker` instance via the fluent `.observe()` method.
|
|
459
|
+
|
|
460
|
+
| Hook | When it fires |
|
|
461
|
+
|---|---|
|
|
462
|
+
| `onStateChange(from, to)` | On every state transition (CLOSED↔OPEN↔HALF_OPEN) |
|
|
463
|
+
| `onSuccess()` | After every successful execution |
|
|
464
|
+
| `onFailure(error)` | When a failure is counted (i.e. `isFailure` predicate returned `true`) |
|
|
465
|
+
| `onProbeRejected()` | When a concurrent caller is turned away in HALF_OPEN |
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { CircuitBreaker, CircuitBreakerObserver, CircuitState } from '@yildizpay/http-adapter';
|
|
469
|
+
|
|
470
|
+
class CircuitMetricsObserver implements CircuitBreakerObserver {
|
|
471
|
+
onStateChange(from: CircuitState, to: CircuitState): void {
|
|
472
|
+
logger.warn(`Circuit breaker: ${from} → ${to}`);
|
|
473
|
+
metrics.increment('circuit_breaker.state_change', { from, to });
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
onProbeRejected(): void {
|
|
477
|
+
metrics.increment('circuit_breaker.probe_rejected');
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const adapter = HttpAdapter.builder()
|
|
482
|
+
.withCircuitBreaker(
|
|
483
|
+
new CircuitBreaker({ failureThreshold: 5 })
|
|
484
|
+
.observe(new CircuitMetricsObserver()),
|
|
485
|
+
)
|
|
486
|
+
.withObserver(new MetricsObserver())
|
|
487
|
+
.build();
|
|
488
|
+
```
|
|
489
|
+
|
|
418
490
|
## Interceptors
|
|
419
491
|
|
|
420
492
|
Thanks to the **Interface Segregation Principle (ISP)**, you aren't forced to implement massive interfaces. You can hook into the exact lifecycle event you need by implementing `HttpRequestInterceptor`, `HttpResponseInterceptor`, or `HttpErrorInterceptor`.
|
package/README.tr.md
CHANGED
|
@@ -415,6 +415,78 @@ Node.js, tek iş parçacıklı (single-threaded) bir event loop üzerinde çalı
|
|
|
415
415
|
|
|
416
416
|
Bunu önlemek için circuit breaker bir **probe flag** kullanır: yalnızca ilk çağıran probe slotunu alır; probe tamamlanana kadar diğer tüm eş zamanlı çağrılar `CircuitBreakerOpenException` ile reddedilir. Bu bilinçli bir tasarım kararıdır — birkaç isteği feda ederek servisin gerçekten sağlıklı olup olmadığı güvenli biçimde doğrulanır.
|
|
417
417
|
|
|
418
|
+
## Observability
|
|
419
|
+
|
|
420
|
+
Adaptör, iki katmanlı bir observability sistemiyle birlikte gelir. **Observer'lar salt okunurdur** — request veya response'u değiştiremezler. Metrics, yapılandırılmış loglama ve dağıtık izleme için observer'ları, pipeline'ı değiştirmeniz gerektiğinde ise interceptor'ları kullanın.
|
|
421
|
+
|
|
422
|
+
### `HttpAdapterObserver`
|
|
423
|
+
|
|
424
|
+
Builder üzerindeki `.withObserver()` metoduyla adaptöre tek bir observer bağlanır.
|
|
425
|
+
|
|
426
|
+
| Hook | Ne zaman tetiklenir |
|
|
427
|
+
|---|---|
|
|
428
|
+
| `onRequestStart(request)` | Tüm request interceptor'larından sonra, HTTP çağrısından hemen önce |
|
|
429
|
+
| `onRequestSuccess(response, durationMs)` | Başarılı yanıt sonrasında (retry süresi dahil) |
|
|
430
|
+
| `onRequestFailure(error, durationMs)` | Son hata çağırana iletildiğinde |
|
|
431
|
+
| `onRetry(attempt, error, delayMs)` | Her retry planlandığında, backoff gecikmesinden önce |
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { HttpAdapterObserver, HttpAdapter, RetryPolicies } from '@yildizpay/http-adapter';
|
|
435
|
+
|
|
436
|
+
class MetricsObserver implements HttpAdapterObserver {
|
|
437
|
+
onRequestSuccess(_response: Response, durationMs: number): void {
|
|
438
|
+
metrics.histogram('http.request.duration', durationMs);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
onRequestFailure(error: BaseAdapterException, durationMs: number): void {
|
|
442
|
+
metrics.increment('http.request.error', { type: error.name });
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
onRetry(attempt: number, _error: BaseAdapterException, delayMs: number): void {
|
|
446
|
+
logger.warn(`Retry denemesi ${attempt}, ${delayMs}ms sonra`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const adapter = HttpAdapter.builder()
|
|
451
|
+
.withRetryPolicy(RetryPolicies.exponential(3))
|
|
452
|
+
.withObserver(new MetricsObserver())
|
|
453
|
+
.build();
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### `CircuitBreakerObserver`
|
|
457
|
+
|
|
458
|
+
Fluent `.observe()` metodu aracılığıyla bir `CircuitBreaker` instance'ına observer bağlanır.
|
|
459
|
+
|
|
460
|
+
| Hook | Ne zaman tetiklenir |
|
|
461
|
+
|---|---|
|
|
462
|
+
| `onStateChange(from, to)` | Her state geçişinde (CLOSED↔OPEN↔HALF_OPEN) |
|
|
463
|
+
| `onSuccess()` | Her başarılı çalıştırma sonrasında |
|
|
464
|
+
| `onFailure(error)` | Bir hata sayıldığında (`isFailure` predicate `true` döndürdüğünde) |
|
|
465
|
+
| `onProbeRejected()` | HALF_OPEN'da eş zamanlı bir çağıran reddedildiğinde |
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import { CircuitBreaker, CircuitBreakerObserver, CircuitState } from '@yildizpay/http-adapter';
|
|
469
|
+
|
|
470
|
+
class CircuitMetricsObserver implements CircuitBreakerObserver {
|
|
471
|
+
onStateChange(from: CircuitState, to: CircuitState): void {
|
|
472
|
+
logger.warn(`Circuit breaker: ${from} → ${to}`);
|
|
473
|
+
metrics.increment('circuit_breaker.state_change', { from, to });
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
onProbeRejected(): void {
|
|
477
|
+
metrics.increment('circuit_breaker.probe_rejected');
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const adapter = HttpAdapter.builder()
|
|
482
|
+
.withCircuitBreaker(
|
|
483
|
+
new CircuitBreaker({ failureThreshold: 5 })
|
|
484
|
+
.observe(new CircuitMetricsObserver()),
|
|
485
|
+
)
|
|
486
|
+
.withObserver(new MetricsObserver())
|
|
487
|
+
.build();
|
|
488
|
+
```
|
|
489
|
+
|
|
418
490
|
## Interceptors
|
|
419
491
|
|
|
420
492
|
**Interface Segregation Principle (ISP)** sayesinde gereksiz metodları implement etmek zorunda kalmazsınız. Yalnızca ihtiyaç duyduğunuz lifecycle event'e göre `HttpRequestInterceptor`, `HttpResponseInterceptor` veya `HttpErrorInterceptor` interface'ini implement edebilirsiniz.
|
|
@@ -4,16 +4,19 @@ import { RetryPolicy } from '../contracts/retry-policy.contract';
|
|
|
4
4
|
import { HttpClientContract } from '../contracts/http-client.contract';
|
|
5
5
|
import { CircuitBreaker } from '../resilience/circuit-breaker/circuit-breaker';
|
|
6
6
|
import { CircuitBreakerOptions } from '../resilience/circuit-breaker/circuit-breaker-options';
|
|
7
|
+
import { HttpAdapterObserver } from '../observability/http-adapter-observer';
|
|
7
8
|
export declare class HttpAdapterBuilder {
|
|
8
9
|
private readonly interceptors;
|
|
9
10
|
private retryPolicy;
|
|
10
11
|
private httpClient;
|
|
11
12
|
private circuitBreaker;
|
|
12
13
|
private correlationIdConfig;
|
|
14
|
+
private observer;
|
|
13
15
|
withInterceptor(...interceptors: HttpInterceptor[]): this;
|
|
14
16
|
withRetryPolicy(policy: RetryPolicy): this;
|
|
15
17
|
withCircuitBreaker(breaker: CircuitBreaker | CircuitBreakerOptions): this;
|
|
16
18
|
withHttpClient(client: HttpClientContract): this;
|
|
17
19
|
withCorrelationId(header?: string): this;
|
|
20
|
+
withObserver(observer: HttpAdapterObserver): this;
|
|
18
21
|
build(): HttpAdapter;
|
|
19
22
|
}
|
|
@@ -28,8 +28,12 @@ class HttpAdapterBuilder {
|
|
|
28
28
|
this.correlationIdConfig = { enabled: true, header: header ?? correlation_id_config_1.DEFAULT_CORRELATION_ID_HEADER };
|
|
29
29
|
return this;
|
|
30
30
|
}
|
|
31
|
+
withObserver(observer) {
|
|
32
|
+
this.observer = observer;
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
31
35
|
build() {
|
|
32
|
-
return http_adapter_1.HttpAdapter.create(this.interceptors, this.retryPolicy, this.httpClient, this.circuitBreaker, this.correlationIdConfig);
|
|
36
|
+
return http_adapter_1.HttpAdapter.create(this.interceptors, this.retryPolicy, this.httpClient, this.circuitBreaker, this.correlationIdConfig, this.observer);
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
exports.HttpAdapterBuilder = HttpAdapterBuilder;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-adapter.builder.js","sourceRoot":"","sources":["../../src/builders/http-adapter.builder.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAInD,mFAA+E;AAE/E,2EAGyC;
|
|
1
|
+
{"version":3,"file":"http-adapter.builder.js","sourceRoot":"","sources":["../../src/builders/http-adapter.builder.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAInD,mFAA+E;AAE/E,2EAGyC;AAoBzC,MAAa,kBAAkB;IAA/B;QACmB,iBAAY,GAAsB,EAAE,CAAC;IA+GxD,CAAC;IAlGQ,eAAe,CAAC,GAAG,YAA+B;QACvD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAOM,eAAe,CAAC,MAAmB;QACxC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAWM,kBAAkB,CAAC,OAA+C;QACvE,IAAI,CAAC,cAAc,GAAG,OAAO,YAAY,gCAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,gCAAc,CAAC,OAAO,CAAC,CAAC;QAChG,OAAO,IAAI,CAAC;IACd,CAAC;IASM,cAAc,CAAC,MAA0B;QAC9C,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAoBM,iBAAiB,CAAC,MAAe;QACtC,IAAI,CAAC,mBAAmB,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,IAAI,qDAA6B,EAAE,CAAC;QAC9F,OAAO,IAAI,CAAC;IACd,CAAC;IAgBM,YAAY,CAAC,QAA6B;QAC/C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAOM,KAAK;QACV,OAAO,0BAAW,CAAC,MAAM,CACvB,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,UAAU,EACf,IAAI,CAAC,cAAc,EACnB,IAAI,CAAC,mBAAmB,EACxB,IAAI,CAAC,QAAQ,CACd,CAAC;IACJ,CAAC;CACF;AAhHD,gDAgHC"}
|
|
@@ -18,7 +18,9 @@ class FetchHttpClient {
|
|
|
18
18
|
'Content-Type': 'application/json',
|
|
19
19
|
...config.headers,
|
|
20
20
|
},
|
|
21
|
-
body: config.
|
|
21
|
+
body: config.method !== 'GET' && config.method !== 'HEAD' && config.data
|
|
22
|
+
? JSON.stringify(config.data)
|
|
23
|
+
: undefined,
|
|
22
24
|
signal: controller.signal,
|
|
23
25
|
});
|
|
24
26
|
let responseData = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-http-client.js","sourceRoot":"","sources":["../../src/core/default-http-client.ts"],"names":[],"mappings":";;;AAKA,uFAAkF;AAClF,iFAA4E;AAC5E,iFAA4E;AAQ5E,MAAa,eAAe;IACnB,KAAK,CAAC,OAAO,CAClB,MAA+B;QAE/B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,IAAI,SAAqC,CAAC;QAE1C,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACzC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;gBACvC,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,GAAG,MAAM,CAAC,OAAO;iBAClB;gBACD,IAAI,
|
|
1
|
+
{"version":3,"file":"default-http-client.js","sourceRoot":"","sources":["../../src/core/default-http-client.ts"],"names":[],"mappings":";;;AAKA,uFAAkF;AAClF,iFAA4E;AAC5E,iFAA4E;AAQ5E,MAAa,eAAe;IACnB,KAAK,CAAC,OAAO,CAClB,MAA+B;QAE/B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,IAAI,SAAqC,CAAC;QAE1C,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;YACzC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;gBACvC,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,GAAG,MAAM,CAAC,OAAO;iBAClB;gBACD,IAAI,EACF,MAAM,CAAC,MAAM,KAAK,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI;oBAChE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC;oBAC7B,CAAC,CAAC,SAAS;gBACf,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YAEH,IAAI,YAAY,GAAY,IAAI,CAAC;YACjC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAEzD,IAAI,WAAW,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC9C,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACzD,CAAC;iBAAM,CAAC;gBACN,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACzD,CAAC;YAED,MAAM,eAAe,GAA2B,EAAE,CAAC;YACnD,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACtC,eAAe,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,6CAAoB,CAAC,kBAAkB,CAC3C,QAAQ,CAAC,MAAM,EACf,YAAiB,EACjB,eAAe,EACf,8BAA8B,QAAQ,CAAC,MAAM,EAAE,CAChD,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,IAAI,EAAE,YAAiB;gBACvB,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,OAAO,EAAE,eAAe;aACzB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,6CAAoB;gBAAE,MAAM,KAAK,CAAC;YACvD,MAAM,mDAAuB,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7D,CAAC;gBAAS,CAAC;YACT,IAAI,SAAS,EAAE,CAAC;gBACd,YAAY,CAAC,SAAS,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;CACF;AA9DD,0CA8DC;AAEY,QAAA,iBAAiB,GAAG,IAAI,eAAe,EAAE,CAAC"}
|
|
@@ -6,17 +6,19 @@ import { CircuitBreaker } from '../resilience/circuit-breaker/circuit-breaker';
|
|
|
6
6
|
import { HttpClientContract } from '../contracts/http-client.contract';
|
|
7
7
|
import { HttpAdapterBuilder } from '../builders/http-adapter.builder';
|
|
8
8
|
import { CorrelationIdConfig } from '../models/correlation-id-config';
|
|
9
|
+
import { HttpAdapterObserver } from '../observability/http-adapter-observer';
|
|
9
10
|
export declare class HttpAdapter {
|
|
10
11
|
private readonly httpClient;
|
|
11
12
|
private readonly retryPolicy?;
|
|
12
13
|
private readonly circuitBreaker?;
|
|
13
14
|
private readonly correlationIdConfig?;
|
|
15
|
+
private readonly observer?;
|
|
14
16
|
private readonly requestInterceptors;
|
|
15
17
|
private readonly responseInterceptors;
|
|
16
18
|
private readonly validatedResponseInterceptors;
|
|
17
19
|
private readonly errorInterceptors;
|
|
18
20
|
private constructor();
|
|
19
|
-
static create(interceptors: HttpInterceptor[], retryPolicy?: RetryPolicy, httpClient?: HttpClientContract, circuitBreaker?: CircuitBreaker, correlationIdConfig?: CorrelationIdConfig): HttpAdapter;
|
|
21
|
+
static create(interceptors: HttpInterceptor[], retryPolicy?: RetryPolicy, httpClient?: HttpClientContract, circuitBreaker?: CircuitBreaker, correlationIdConfig?: CorrelationIdConfig, observer?: HttpAdapterObserver): HttpAdapter;
|
|
20
22
|
static builder(): HttpAdapterBuilder;
|
|
21
23
|
send<T = unknown>(request: Request): Promise<Response<T>>;
|
|
22
24
|
private dispatch;
|
|
@@ -10,34 +10,45 @@ const validation_exception_1 = require("../exceptions/validation.exception");
|
|
|
10
10
|
const http_adapter_builder_1 = require("../builders/http-adapter.builder");
|
|
11
11
|
const correlation_id_config_1 = require("../models/correlation-id-config");
|
|
12
12
|
class HttpAdapter {
|
|
13
|
-
constructor(interceptors, httpClient, retryPolicy, circuitBreaker, correlationIdConfig) {
|
|
13
|
+
constructor(interceptors, httpClient, retryPolicy, circuitBreaker, correlationIdConfig, observer) {
|
|
14
14
|
this.httpClient = httpClient;
|
|
15
15
|
this.retryPolicy = retryPolicy;
|
|
16
16
|
this.circuitBreaker = circuitBreaker;
|
|
17
17
|
this.correlationIdConfig = correlationIdConfig;
|
|
18
|
+
this.observer = observer;
|
|
18
19
|
this.requestInterceptors = interceptors.filter((i) => typeof i.onRequest === 'function');
|
|
19
20
|
this.responseInterceptors = interceptors.filter((i) => typeof i.onResponse === 'function');
|
|
20
21
|
this.validatedResponseInterceptors = interceptors.filter((i) => typeof i.onResponseValidated === 'function');
|
|
21
22
|
this.errorInterceptors = interceptors.filter((i) => typeof i.onError === 'function');
|
|
22
23
|
}
|
|
23
|
-
static create(interceptors, retryPolicy, httpClient, circuitBreaker, correlationIdConfig) {
|
|
24
|
-
return new HttpAdapter(interceptors, httpClient ?? default_http_client_1.defaultHttpClient, retryPolicy, circuitBreaker, correlationIdConfig);
|
|
24
|
+
static create(interceptors, retryPolicy, httpClient, circuitBreaker, correlationIdConfig, observer) {
|
|
25
|
+
return new HttpAdapter(interceptors, httpClient ?? default_http_client_1.defaultHttpClient, retryPolicy, circuitBreaker, correlationIdConfig, observer);
|
|
25
26
|
}
|
|
26
27
|
static builder() {
|
|
27
28
|
return new http_adapter_builder_1.HttpAdapterBuilder();
|
|
28
29
|
}
|
|
29
30
|
async send(request) {
|
|
31
|
+
const startTime = Date.now();
|
|
30
32
|
const executePipeline = () => {
|
|
31
33
|
if (!this.retryPolicy) {
|
|
32
34
|
return this.dispatch(request);
|
|
33
35
|
}
|
|
34
|
-
const executor = new retry_executor_1.RetryExecutor(this.retryPolicy);
|
|
36
|
+
const executor = new retry_executor_1.RetryExecutor(this.retryPolicy, this.observer);
|
|
35
37
|
return executor.execute(() => this.dispatch(request));
|
|
36
38
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
try {
|
|
40
|
+
const response = await (this.circuitBreaker
|
|
41
|
+
? this.circuitBreaker.execute(executePipeline)
|
|
42
|
+
: executePipeline());
|
|
43
|
+
this.observer?.onRequestSuccess?.(response, Date.now() - startTime);
|
|
44
|
+
return response;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof base_adapter_exception_1.BaseAdapterException) {
|
|
48
|
+
this.observer?.onRequestFailure?.(error, Date.now() - startTime);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
39
51
|
}
|
|
40
|
-
return this.circuitBreaker.execute(executePipeline);
|
|
41
52
|
}
|
|
42
53
|
async dispatch(request) {
|
|
43
54
|
const processedRequest = await this.runRequestInterceptors(request);
|
|
@@ -51,6 +62,7 @@ class HttpAdapter {
|
|
|
51
62
|
};
|
|
52
63
|
processedRequest.setTimestamp(new Date());
|
|
53
64
|
this.applyCorrelationIdHeader(processedRequest);
|
|
65
|
+
this.observer?.onRequestStart?.(processedRequest);
|
|
54
66
|
const response = await this.executeHttpCall(processedRequest, url, requestContext);
|
|
55
67
|
const interceptedResponse = await this.runResponseInterceptors(response);
|
|
56
68
|
await this.runValidators(interceptedResponse, request);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.adapter.js","sourceRoot":"","sources":["../../src/core/http.adapter.ts"],"names":[],"mappings":";;;AAAA,+DAA0D;AAE1D,iDAA8C;AAU9C,iEAA6D;AAG7D,uDAAmD;AACnD,iFAA4E;AAC5E,6EAAyE;AACzE,2EAAsE;AACtE,2EAGyC;
|
|
1
|
+
{"version":3,"file":"http.adapter.js","sourceRoot":"","sources":["../../src/core/http.adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export * from './resilience/policies/decorrelated-jitter.retry-policy';
|
|
|
20
20
|
export * from './resilience/circuit-breaker/circuit-state.enum';
|
|
21
21
|
export * from './resilience/circuit-breaker/circuit-breaker-options';
|
|
22
22
|
export * from './resilience/circuit-breaker/circuit-breaker';
|
|
23
|
+
export * from './observability/http-adapter-observer';
|
|
24
|
+
export * from './observability/circuit-breaker-observer';
|
|
23
25
|
export * from './auth/token-provider';
|
|
24
26
|
export * from './auth/bearer-auth.interceptor';
|
|
25
27
|
export * from './auth/basic-auth.interceptor';
|
package/dist/index.js
CHANGED
|
@@ -36,6 +36,8 @@ __exportStar(require("./resilience/policies/decorrelated-jitter.retry-policy"),
|
|
|
36
36
|
__exportStar(require("./resilience/circuit-breaker/circuit-state.enum"), exports);
|
|
37
37
|
__exportStar(require("./resilience/circuit-breaker/circuit-breaker-options"), exports);
|
|
38
38
|
__exportStar(require("./resilience/circuit-breaker/circuit-breaker"), exports);
|
|
39
|
+
__exportStar(require("./observability/http-adapter-observer"), exports);
|
|
40
|
+
__exportStar(require("./observability/circuit-breaker-observer"), exports);
|
|
39
41
|
__exportStar(require("./auth/token-provider"), exports);
|
|
40
42
|
__exportStar(require("./auth/bearer-auth.interceptor"), exports);
|
|
41
43
|
__exportStar(require("./auth/basic-auth.interceptor"), exports);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,sDAAoC;AACpC,6DAA2C;AAG3C,6DAA2C;AAC3C,kEAAgD;AAGhD,mDAAiC;AACjC,oDAAkC;AAClC,2DAAyC;AACzC,2DAAyC;AACzC,iEAA+C;AAG/C,wEAAsD;AACtD,oEAAkD;AAClD,uEAAqD;AACrD,0EAAwD;AAGxD,8DAA4C;AAC5C,yFAAuE;AACvE,iFAA+D;AAC/D,oFAAkE;AAClE,iFAA+D;AAC/D,yFAAuE;AACvE,kFAAgE;AAChE,uFAAqE;AACrE,+EAA6D;AAG7D,wDAAsC;AACtC,iEAA+C;AAC/C,gEAA8C;AAC9C,6DAA2C;AAG3C,kEAAgD;AAChD,4DAA0C;AAG1C,8EAA4D;AAC5D,sEAAoD;AACpD,sEAAoD;AACpD,sEAAoD;AACpD,kEAAgD;AAChD,yEAAuD;AACvD,iEAA+C;AAC/C,oEAAkD;AAClD,gEAA8C;AAC9C,yDAAuC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AACA,sDAAoC;AACpC,6DAA2C;AAG3C,6DAA2C;AAC3C,kEAAgD;AAGhD,mDAAiC;AACjC,oDAAkC;AAClC,2DAAyC;AACzC,2DAAyC;AACzC,iEAA+C;AAG/C,wEAAsD;AACtD,oEAAkD;AAClD,uEAAqD;AACrD,0EAAwD;AAGxD,8DAA4C;AAC5C,yFAAuE;AACvE,iFAA+D;AAC/D,oFAAkE;AAClE,iFAA+D;AAC/D,yFAAuE;AACvE,kFAAgE;AAChE,uFAAqE;AACrE,+EAA6D;AAG7D,wEAAsD;AACtD,2EAAyD;AAGzD,wDAAsC;AACtC,iEAA+C;AAC/C,gEAA8C;AAC9C,6DAA2C;AAG3C,kEAAgD;AAChD,4DAA0C;AAG1C,8EAA4D;AAC5D,sEAAoD;AACpD,sEAAoD;AACpD,sEAAoD;AACpD,kEAAgD;AAChD,yEAAuD;AACvD,iEAA+C;AAC/C,oEAAkD;AAClD,gEAA8C;AAC9C,yDAAuC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CircuitState } from '../resilience/circuit-breaker/circuit-state.enum';
|
|
2
|
+
export interface CircuitBreakerObserver {
|
|
3
|
+
onStateChange?(from: CircuitState, to: CircuitState): void;
|
|
4
|
+
onSuccess?(): void;
|
|
5
|
+
onFailure?(error: unknown): void;
|
|
6
|
+
onProbeRejected?(): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker-observer.js","sourceRoot":"","sources":["../../src/observability/circuit-breaker-observer.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Request } from '../models/request';
|
|
2
|
+
import { Response } from '../models/response';
|
|
3
|
+
import { BaseAdapterException } from '../exceptions/base-adapter.exception';
|
|
4
|
+
export interface HttpAdapterObserver {
|
|
5
|
+
onRequestStart?(request: Request): void;
|
|
6
|
+
onRequestSuccess?(response: Response, durationMs: number): void;
|
|
7
|
+
onRequestFailure?(error: BaseAdapterException, durationMs: number): void;
|
|
8
|
+
onRetry?(attempt: number, error: BaseAdapterException, delayMs: number): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-adapter-observer.js","sourceRoot":"","sources":["../../src/observability/http-adapter-observer.ts"],"names":[],"mappings":""}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { CircuitState } from './circuit-state.enum';
|
|
2
2
|
import { CircuitBreakerOptions } from './circuit-breaker-options';
|
|
3
|
+
import { CircuitBreakerObserver } from '../../observability/circuit-breaker-observer';
|
|
3
4
|
export declare class CircuitBreaker {
|
|
4
5
|
private state;
|
|
5
6
|
private failureCount;
|
|
6
7
|
private successCount;
|
|
7
8
|
private nextAttemptAt;
|
|
8
9
|
private halfOpenProbeInFlight;
|
|
10
|
+
private observer;
|
|
9
11
|
private readonly failureThreshold;
|
|
10
12
|
private readonly resetTimeoutMs;
|
|
11
13
|
private readonly successThreshold;
|
|
12
14
|
private readonly isFailurePredicate;
|
|
13
15
|
constructor(options?: CircuitBreakerOptions);
|
|
16
|
+
observe(observer: CircuitBreakerObserver): this;
|
|
14
17
|
getState(): CircuitState;
|
|
15
18
|
execute<T>(operation: () => Promise<T>): Promise<T>;
|
|
16
19
|
private recordSuccess;
|
|
@@ -15,6 +15,10 @@ class CircuitBreaker {
|
|
|
15
15
|
this.successThreshold = options?.successThreshold ?? 1;
|
|
16
16
|
this.isFailurePredicate = options?.isFailure ?? (() => true);
|
|
17
17
|
}
|
|
18
|
+
observe(observer) {
|
|
19
|
+
this.observer = observer;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
18
22
|
getState() {
|
|
19
23
|
if (this.state === circuit_state_enum_1.CircuitState.OPEN && Date.now() >= this.nextAttemptAt) {
|
|
20
24
|
this.transitionTo(circuit_state_enum_1.CircuitState.HALF_OPEN);
|
|
@@ -27,18 +31,22 @@ class CircuitBreaker {
|
|
|
27
31
|
throw new circuit_breaker_open_exception_1.CircuitBreakerOpenException(this.nextAttemptAt);
|
|
28
32
|
}
|
|
29
33
|
if (currentState === circuit_state_enum_1.CircuitState.HALF_OPEN) {
|
|
30
|
-
if (this.halfOpenProbeInFlight)
|
|
34
|
+
if (this.halfOpenProbeInFlight) {
|
|
35
|
+
this.observer?.onProbeRejected?.();
|
|
31
36
|
throw new circuit_breaker_open_exception_1.CircuitBreakerOpenException(0, 'Circuit Breaker is HALF_OPEN. A probe request is already in flight.');
|
|
37
|
+
}
|
|
32
38
|
this.halfOpenProbeInFlight = true;
|
|
33
39
|
}
|
|
34
40
|
try {
|
|
35
41
|
const result = await operation();
|
|
36
42
|
this.recordSuccess();
|
|
43
|
+
this.observer?.onSuccess?.();
|
|
37
44
|
return result;
|
|
38
45
|
}
|
|
39
46
|
catch (error) {
|
|
40
47
|
if (this.isFailurePredicate(error)) {
|
|
41
48
|
this.recordFailure();
|
|
49
|
+
this.observer?.onFailure?.(error);
|
|
42
50
|
}
|
|
43
51
|
throw error;
|
|
44
52
|
}
|
|
@@ -71,11 +79,13 @@ class CircuitBreaker {
|
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
transitionTo(newState) {
|
|
82
|
+
const previousState = this.state;
|
|
74
83
|
this.state = newState;
|
|
75
84
|
this.failureCount = 0;
|
|
76
85
|
this.successCount = 0;
|
|
77
86
|
this.halfOpenProbeInFlight = false;
|
|
78
87
|
this.nextAttemptAt = newState === circuit_state_enum_1.CircuitState.OPEN ? Date.now() + this.resetTimeoutMs : 0;
|
|
88
|
+
this.observer?.onStateChange?.(previousState, newState);
|
|
79
89
|
}
|
|
80
90
|
}
|
|
81
91
|
exports.CircuitBreaker = CircuitBreaker;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/resilience/circuit-breaker/circuit-breaker.ts"],"names":[],"mappings":";;;AAAA,6DAAoD;AAEpD,oGAA8F;
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/resilience/circuit-breaker/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { RetryPolicy } from '../contracts/retry-policy.contract';
|
|
2
|
+
import { HttpAdapterObserver } from '../observability/http-adapter-observer';
|
|
2
3
|
export declare class RetryExecutor {
|
|
3
4
|
private readonly policy;
|
|
4
|
-
|
|
5
|
+
private readonly observer?;
|
|
6
|
+
constructor(policy: RetryPolicy, observer?: HttpAdapterObserver | undefined);
|
|
5
7
|
execute<T>(operation: () => Promise<T>): Promise<T>;
|
|
6
8
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RetryExecutor = void 0;
|
|
4
|
+
const base_adapter_exception_1 = require("../exceptions/base-adapter.exception");
|
|
4
5
|
class RetryExecutor {
|
|
5
|
-
constructor(policy) {
|
|
6
|
+
constructor(policy, observer) {
|
|
6
7
|
this.policy = policy;
|
|
8
|
+
this.observer = observer;
|
|
7
9
|
}
|
|
8
10
|
async execute(operation) {
|
|
9
11
|
let attempt = 1;
|
|
@@ -17,6 +19,9 @@ class RetryExecutor {
|
|
|
17
19
|
if (attempt >= this.policy.maxAttempts)
|
|
18
20
|
throw err;
|
|
19
21
|
const delay = this.policy.backoffMs(attempt);
|
|
22
|
+
if (this.observer?.onRetry && err instanceof base_adapter_exception_1.BaseAdapterException) {
|
|
23
|
+
this.observer.onRetry(attempt, err, delay);
|
|
24
|
+
}
|
|
20
25
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
21
26
|
attempt++;
|
|
22
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-executor.js","sourceRoot":"","sources":["../../src/resilience/retry-executor.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"retry-executor.js","sourceRoot":"","sources":["../../src/resilience/retry-executor.ts"],"names":[],"mappings":";;;AACA,iFAA4E;AAa5E,MAAa,aAAa;IAOxB,YACmB,MAAmB,EACnB,QAA8B;QAD9B,WAAM,GAAN,MAAM,CAAa;QACnB,aAAQ,GAAR,QAAQ,CAAsB;IAC9C,CAAC;IAYJ,KAAK,CAAC,OAAO,CAAI,SAA2B;QAC1C,IAAI,OAAO,GAAG,CAAC,CAAC;QAEhB,OAAO,IAAI,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,OAAO,MAAM,SAAS,EAAE,CAAC;YAC3B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;oBAAE,MAAM,GAAG,CAAC;gBAEzC,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW;oBAAE,MAAM,GAAG,CAAC;gBAElD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBAE7C,IAAI,IAAI,CAAC,QAAQ,EAAE,OAAO,IAAI,GAAG,YAAY,6CAAoB,EAAE,CAAC;oBAClE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC7C,CAAC;gBAED,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;gBAE3D,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;CACF;AA7CD,sCA6CC"}
|