@private.me/xbind 1.3.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES.md +212 -0
- package/README.md +388 -6
- package/dist-standalone/_deps/mldsa-wasm/dist/mldsa.js +1 -1920
- package/dist-standalone/_deps/shared/cjs/errors.js +1 -639
- package/dist-standalone/_deps/shared/cjs/index.js +1 -496
- package/dist-standalone/_deps/shared/cjs/types.js +1 -317
- package/dist-standalone/_deps/shared/errors.js +1 -255
- package/dist-standalone/_deps/shared/index.js +1 -74
- package/dist-standalone/_deps/shared/types.js +1 -90
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js +1 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js +1 -1
- package/dist-standalone/_deps/ux-helpers/errors.js +1 -1
- package/dist-standalone/_deps/ux-helpers/index.js +1 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js +1 -1
- package/dist-standalone/_deps/ux-helpers/progress.js +1 -1
- package/dist-standalone/_deps/ux-helpers/search.js +1 -1
- package/dist-standalone/_deps/xchange/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/auto-accept.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/index.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/cjs/xchange.js +1 -1
- package/dist-standalone/_deps/xchange/errors.js +1 -1
- package/dist-standalone/_deps/xchange/index.js +1 -1
- package/dist-standalone/_deps/xchange/invite-client.js +1 -1
- package/dist-standalone/_deps/xchange/lazy-init.js +1 -1
- package/dist-standalone/_deps/xchange/trust-integration.js +1 -1
- package/dist-standalone/_deps/xchange/xchange.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/index.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/cjs/types.js +1 -1
- package/dist-standalone/_deps/xregistry/discovery.js +1 -1
- package/dist-standalone/_deps/xregistry/errors.js +1 -1
- package/dist-standalone/_deps/xregistry/index.js +1 -1
- package/dist-standalone/_deps/xregistry/registry.js +1 -1
- package/dist-standalone/_deps/xregistry/schema.js +1 -1
- package/dist-standalone/_deps/xregistry/types.js +1 -1
- package/dist-standalone/agent-call.js +1 -642
- package/dist-standalone/agent-sdk.js +1 -328
- package/dist-standalone/agent.d.ts +95 -5
- package/dist-standalone/agent.js +1 -1545
- package/dist-standalone/approval.js +1 -193
- package/dist-standalone/async-iterators.d.ts +275 -0
- package/dist-standalone/async-iterators.js +1 -0
- package/dist-standalone/auth.js +1 -219
- package/dist-standalone/auto-accept.js +1 -229
- package/dist-standalone/backup-config.js +1 -201
- package/dist-standalone/backup.d.ts +114 -0
- package/dist-standalone/backup.js +1 -0
- package/dist-standalone/batch-operations.d.ts +297 -0
- package/dist-standalone/batch-operations.js +1 -0
- package/dist-standalone/cancellation.d.ts +301 -0
- package/dist-standalone/cancellation.js +1 -0
- package/dist-standalone/checkpoint.js +1 -186
- package/dist-standalone/circuit-breaker.d.ts +351 -0
- package/dist-standalone/circuit-breaker.js +1 -0
- package/dist-standalone/cjs/agent-call.js +1 -651
- package/dist-standalone/cjs/agent-sdk.js +1 -332
- package/dist-standalone/cjs/agent.js +1 -1582
- package/dist-standalone/cjs/approval.js +1 -199
- package/dist-standalone/cjs/async-iterators.js +1 -0
- package/dist-standalone/cjs/auth.js +1 -225
- package/dist-standalone/cjs/auto-accept.js +1 -233
- package/dist-standalone/cjs/backup-config.js +1 -207
- package/dist-standalone/cjs/backup.js +1 -0
- package/dist-standalone/cjs/batch-operations.js +1 -0
- package/dist-standalone/cjs/cancellation.js +1 -0
- package/dist-standalone/cjs/checkpoint.js +1 -193
- package/dist-standalone/cjs/circuit-breaker.js +1 -0
- package/dist-standalone/cjs/cli/init.js +1 -486
- package/dist-standalone/cjs/config-validation.js +1 -0
- package/dist-standalone/cjs/connect.js +1 -312
- package/dist-standalone/cjs/connection-pool.js +1 -0
- package/dist-standalone/cjs/correlation-id.js +1 -339
- package/dist-standalone/cjs/crypto-utils.js +1 -0
- package/dist-standalone/cjs/debug-mode.js +1 -0
- package/dist-standalone/cjs/did-document.js +1 -101
- package/dist-standalone/cjs/did-privateme.js +1 -130
- package/dist-standalone/cjs/did-web.js +1 -201
- package/dist-standalone/cjs/discovery.js +1 -462
- package/dist-standalone/cjs/dual-mode.js +1 -251
- package/dist-standalone/cjs/email-templates.js +1 -313
- package/dist-standalone/cjs/email-transport.js +1 -239
- package/dist-standalone/cjs/envelope.js +1 -510
- package/dist-standalone/cjs/errors.js +1 -826
- package/dist-standalone/cjs/event-emitter.js +1 -0
- package/dist-standalone/cjs/gateway-state.js +1 -55
- package/dist-standalone/cjs/gateway-transport.js +1 -120
- package/dist-standalone/cjs/graceful-degradation.js +1 -0
- package/dist-standalone/cjs/guardrails.js +1 -223
- package/dist-standalone/cjs/health-check.js +1 -0
- package/dist-standalone/cjs/http-compat.js +1 -272
- package/dist-standalone/cjs/http-status-map.js +1 -571
- package/dist-standalone/cjs/identity.js +1 -540
- package/dist-standalone/cjs/index.js +1 -237
- package/dist-standalone/cjs/invitation.js +1 -421
- package/dist-standalone/cjs/invite.js +1 -328
- package/dist-standalone/cjs/key-agreement.js +1 -246
- package/dist-standalone/cjs/lazy-init.js +1 -300
- package/dist-standalone/cjs/logger.js +1 -0
- package/dist-standalone/cjs/mdns-discovery.js +1 -202
- package/dist-standalone/cjs/nonce-store.js +1 -66
- package/dist-standalone/cjs/pairing-manager.js +1 -223
- package/dist-standalone/cjs/plugin-system.js +1 -0
- package/dist-standalone/cjs/plugins/logging.js +1 -0
- package/dist-standalone/cjs/plugins/metrics.js +1 -0
- package/dist-standalone/cjs/plugins/validation.js +1 -0
- package/dist-standalone/cjs/policy.js +1 -320
- package/dist-standalone/cjs/progress-callbacks.js +1 -0
- package/dist-standalone/cjs/redis-nonce-store.js +1 -76
- package/dist-standalone/cjs/registry-middleware.js +1 -50
- package/dist-standalone/cjs/retry-strategies.js +1 -0
- package/dist-standalone/cjs/retry-transport.js +1 -102
- package/dist-standalone/cjs/runtime/browser.js +1 -0
- package/dist-standalone/cjs/runtime/edge.js +1 -0
- package/dist-standalone/cjs/runtime/react-native.js +1 -0
- package/dist-standalone/cjs/security-policy.js +1 -245
- package/dist-standalone/cjs/serialization.js +1 -0
- package/dist-standalone/cjs/split-channel.js +1 -177
- package/dist-standalone/cjs/subscription-proof.js +1 -230
- package/dist-standalone/cjs/succession.js +1 -148
- package/dist-standalone/cjs/timeouts.js +1 -0
- package/dist-standalone/cjs/trace-context.js +1 -0
- package/dist-standalone/cjs/trace-spans.js +1 -0
- package/dist-standalone/cjs/transport.js +1 -63
- package/dist-standalone/cjs/trust-registry.js +1 -742
- package/dist-standalone/cjs/types/error-response.js +1 -56
- package/dist-standalone/cjs/vault-auth.js +1 -0
- package/dist-standalone/cjs/vault-store-loader.js +1 -0
- package/dist-standalone/cjs/verify.js +1 -25
- package/dist-standalone/cjs/version-info.js +1 -0
- package/dist-standalone/cjs/xfetch.js +1 -252
- package/dist-standalone/cli/init.js +1 -449
- package/dist-standalone/cli/setup.js +1 -514
- package/dist-standalone/cli/types.js +1 -27
- package/dist-standalone/cli/xbind.js +1 -148
- package/dist-standalone/config-validation.d.ts +185 -0
- package/dist-standalone/config-validation.js +1 -0
- package/dist-standalone/connect.js +1 -274
- package/dist-standalone/connection-pool.d.ts +251 -0
- package/dist-standalone/connection-pool.js +1 -0
- package/dist-standalone/correlation-id.js +1 -326
- package/dist-standalone/crypto-utils.d.ts +60 -0
- package/dist-standalone/crypto-utils.js +1 -0
- package/dist-standalone/debug-mode.d.ts +286 -0
- package/dist-standalone/debug-mode.js +1 -0
- package/dist-standalone/did-document.js +1 -96
- package/dist-standalone/did-privateme.js +1 -121
- package/dist-standalone/did-web.js +1 -196
- package/dist-standalone/discovery.js +1 -458
- package/dist-standalone/dual-mode.js +1 -247
- package/dist-standalone/email-templates.js +1 -309
- package/dist-standalone/email-transport.js +1 -232
- package/dist-standalone/envelope.d.ts +29 -1
- package/dist-standalone/envelope.js +1 -497
- package/dist-standalone/errors.d.ts +10 -0
- package/dist-standalone/errors.js +1 -811
- package/dist-standalone/event-emitter.d.ts +395 -0
- package/dist-standalone/event-emitter.js +1 -0
- package/dist-standalone/gateway-state.js +1 -51
- package/dist-standalone/gateway-transport.js +1 -116
- package/dist-standalone/graceful-degradation.d.ts +246 -0
- package/dist-standalone/graceful-degradation.js +1 -0
- package/dist-standalone/guardrails.js +1 -216
- package/dist-standalone/health-check.d.ts +150 -0
- package/dist-standalone/health-check.js +1 -0
- package/dist-standalone/http-compat.js +1 -267
- package/dist-standalone/http-status-map.js +1 -561
- package/dist-standalone/identity.d.ts +64 -1
- package/dist-standalone/identity.js +1 -515
- package/dist-standalone/index.d.ts +45 -3
- package/dist-standalone/index.js +1 -52
- package/dist-standalone/invitation.js +1 -415
- package/dist-standalone/invite.js +1 -324
- package/dist-standalone/key-agreement.d.ts +61 -13
- package/dist-standalone/key-agreement.js +1 -236
- package/dist-standalone/lazy-init.js +1 -295
- package/dist-standalone/logger.d.ts +77 -0
- package/dist-standalone/logger.js +1 -0
- package/dist-standalone/mdns-discovery.js +1 -195
- package/dist-standalone/nonce-store.d.ts +16 -3
- package/dist-standalone/nonce-store.js +1 -62
- package/dist-standalone/package.json +0 -1
- package/dist-standalone/pairing-manager.js +1 -219
- package/dist-standalone/plugin-system.d.ts +145 -0
- package/dist-standalone/plugin-system.js +1 -0
- package/dist-standalone/policy.js +1 -315
- package/dist-standalone/progress-callbacks.d.ts +394 -0
- package/dist-standalone/progress-callbacks.js +1 -0
- package/dist-standalone/redis-nonce-store.js +1 -72
- package/dist-standalone/registry-middleware.js +1 -47
- package/dist-standalone/retry-strategies.d.ts +382 -0
- package/dist-standalone/retry-strategies.js +1 -0
- package/dist-standalone/retry-transport.js +1 -98
- package/dist-standalone/security-policy.js +1 -239
- package/dist-standalone/serialization.d.ts +244 -0
- package/dist-standalone/serialization.js +1 -0
- package/dist-standalone/split-channel.d.ts +49 -1
- package/dist-standalone/split-channel.js +1 -171
- package/dist-standalone/subscription-proof.js +1 -224
- package/dist-standalone/succession.js +1 -142
- package/dist-standalone/timeouts.d.ts +275 -0
- package/dist-standalone/timeouts.js +1 -0
- package/dist-standalone/trace-context.d.ts +252 -0
- package/dist-standalone/trace-context.js +1 -0
- package/dist-standalone/trace-spans.d.ts +360 -0
- package/dist-standalone/trace-spans.js +1 -0
- package/dist-standalone/transport.js +1 -59
- package/dist-standalone/trust-registry.d.ts +106 -5
- package/dist-standalone/trust-registry.js +1 -702
- package/dist-standalone/vault-auth.d.ts +91 -0
- package/dist-standalone/vault-auth.js +1 -0
- package/dist-standalone/vault-store-loader.d.ts +110 -0
- package/dist-standalone/vault-store-loader.js +1 -0
- package/dist-standalone/verify.js +1 -16
- package/dist-standalone/version-info.d.ts +259 -0
- package/dist-standalone/version-info.js +1 -0
- package/dist-standalone/xfetch.js +1 -247
- package/llms.txt +1 -0
- package/package.json +65 -5
- package/share1.dat +0 -0
- package/dist-standalone/_deps/crypto/base64.d.ts +0 -29
- package/dist-standalone/_deps/crypto/base64.js +0 -222
- package/dist-standalone/_deps/crypto/cjs/base64.js +0 -665
- package/dist-standalone/_deps/crypto/cjs/errors.js +0 -675
- package/dist-standalone/_deps/crypto/cjs/hmac.js +0 -473
- package/dist-standalone/_deps/crypto/cjs/index.js +0 -852
- package/dist-standalone/_deps/crypto/cjs/package.json +0 -1
- package/dist-standalone/_deps/crypto/cjs/padding.js +0 -511
- package/dist-standalone/_deps/crypto/cjs/share-header.js +0 -372
- package/dist-standalone/_deps/crypto/cjs/shares.js +0 -874
- package/dist-standalone/_deps/crypto/cjs/tlv.js +0 -1021
- package/dist-standalone/_deps/crypto/cjs/uuid.js +0 -443
- package/dist-standalone/_deps/crypto/cjs/verify.js +0 -414
- package/dist-standalone/_deps/crypto/cjs/xorida.js +0 -923
- package/dist-standalone/_deps/crypto/errors.d.ts +0 -51
- package/dist-standalone/_deps/crypto/errors.js +0 -199
- package/dist-standalone/_deps/crypto/hmac.d.ts +0 -39
- package/dist-standalone/_deps/crypto/hmac.js +0 -134
- package/dist-standalone/_deps/crypto/index.d.ts +0 -20
- package/dist-standalone/_deps/crypto/index.js +0 -145
- package/dist-standalone/_deps/crypto/padding.d.ts +0 -19
- package/dist-standalone/_deps/crypto/padding.js +0 -159
- package/dist-standalone/_deps/crypto/share-header.d.ts +0 -44
- package/dist-standalone/_deps/crypto/share-header.js +0 -92
- package/dist-standalone/_deps/crypto/shares.d.ts +0 -27
- package/dist-standalone/_deps/crypto/shares.js +0 -295
- package/dist-standalone/_deps/crypto/tlv.d.ts +0 -26
- package/dist-standalone/_deps/crypto/tlv.js +0 -364
- package/dist-standalone/_deps/crypto/uuid.d.ts +0 -22
- package/dist-standalone/_deps/crypto/uuid.js +0 -136
- package/dist-standalone/_deps/crypto/verify.d.ts +0 -15
- package/dist-standalone/_deps/crypto/verify.js +0 -71
- package/dist-standalone/_deps/crypto/xorida.d.ts +0 -44
- package/dist-standalone/_deps/crypto/xorida.js +0 -366
- package/dist-standalone/_deps/shared/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/errors.js.map +0 -1
- package/dist-standalone/_deps/shared/index.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/index.js.map +0 -1
- package/dist-standalone/_deps/shared/types.d.ts.map +0 -1
- package/dist-standalone/_deps/shared/types.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/errors.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/index.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/pagination.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/progress.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/search.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/cjs/types.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/errors.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/index.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/index.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/pagination.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/pagination.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/progress.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/progress.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/search.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/search.js.map +0 -1
- package/dist-standalone/_deps/ux-helpers/types.d.ts.map +0 -1
- package/dist-standalone/_deps/ux-helpers/types.js.map +0 -1
- package/dist-standalone/_deps/xregistry/discovery.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/discovery.js.map +0 -1
- package/dist-standalone/_deps/xregistry/errors.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/errors.js.map +0 -1
- package/dist-standalone/_deps/xregistry/index.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/index.js.map +0 -1
- package/dist-standalone/_deps/xregistry/registry.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/registry.js.map +0 -1
- package/dist-standalone/_deps/xregistry/schema.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/schema.js.map +0 -1
- package/dist-standalone/_deps/xregistry/types.d.ts.map +0 -1
- package/dist-standalone/_deps/xregistry/types.js.map +0 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module retry-strategies
|
|
3
|
+
* Configurable retry strategies for enhanced reliability.
|
|
4
|
+
*
|
|
5
|
+
* Provides flexible retry policies with exponential backoff, jitter, circuit breakers,
|
|
6
|
+
* and error-specific retry logic. Designed for production-grade fault tolerance.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { RetryStrategy, CircuitBreaker } from '@private.me/xbind/retry-strategies';
|
|
11
|
+
*
|
|
12
|
+
* // Exponential backoff with circuit breaker
|
|
13
|
+
* const strategy = RetryStrategy.exponentialBackoff({
|
|
14
|
+
* maxAttempts: 5,
|
|
15
|
+
* initialDelayMs: 1000,
|
|
16
|
+
* maxDelayMs: 30000,
|
|
17
|
+
* multiplier: 2,
|
|
18
|
+
* jitter: true
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* const circuitBreaker = new CircuitBreaker({
|
|
22
|
+
* failureThreshold: 5,
|
|
23
|
+
* resetTimeoutMs: 60000
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { Result } from '@private.me/shared';
|
|
28
|
+
import type { TransportError } from './transport.js';
|
|
29
|
+
/**
|
|
30
|
+
* Retry context provides information about the current retry attempt.
|
|
31
|
+
*/
|
|
32
|
+
export interface RetryContext {
|
|
33
|
+
/** Current attempt number (0-indexed) */
|
|
34
|
+
readonly attempt: number;
|
|
35
|
+
/** Error from the previous attempt */
|
|
36
|
+
readonly error: TransportError;
|
|
37
|
+
/** Timestamp of the first attempt */
|
|
38
|
+
readonly startTime: number;
|
|
39
|
+
/** Total elapsed time since first attempt (ms) */
|
|
40
|
+
readonly elapsedMs: number;
|
|
41
|
+
/** Additional metadata */
|
|
42
|
+
readonly metadata?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Result of a retry decision.
|
|
46
|
+
*/
|
|
47
|
+
export interface RetryDecision {
|
|
48
|
+
/** Whether to retry the operation */
|
|
49
|
+
readonly shouldRetry: boolean;
|
|
50
|
+
/** Delay before next retry (ms), if shouldRetry is true */
|
|
51
|
+
readonly delayMs?: number;
|
|
52
|
+
/** Reason for the decision (for logging/debugging) */
|
|
53
|
+
readonly reason?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Configuration for exponential backoff retry strategy.
|
|
57
|
+
*/
|
|
58
|
+
export interface ExponentialBackoffConfig {
|
|
59
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
60
|
+
maxAttempts?: number;
|
|
61
|
+
/** Initial delay in milliseconds (default: 1000) */
|
|
62
|
+
initialDelayMs?: number;
|
|
63
|
+
/** Maximum delay cap in milliseconds (default: 30000) */
|
|
64
|
+
maxDelayMs?: number;
|
|
65
|
+
/** Backoff multiplier (default: 2) */
|
|
66
|
+
multiplier?: number;
|
|
67
|
+
/** Enable jitter randomization (default: true) */
|
|
68
|
+
jitter?: boolean;
|
|
69
|
+
/** Maximum jitter percentage (0-1, default: 0.2 = ±20%) */
|
|
70
|
+
jitterFactor?: number;
|
|
71
|
+
/** Only retry on specific errors (default: all errors) */
|
|
72
|
+
retryableErrors?: ReadonlyArray<TransportError>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Configuration for linear backoff retry strategy.
|
|
76
|
+
*/
|
|
77
|
+
export interface LinearBackoffConfig {
|
|
78
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
79
|
+
maxAttempts?: number;
|
|
80
|
+
/** Fixed delay increment in milliseconds (default: 1000) */
|
|
81
|
+
delayIncrementMs?: number;
|
|
82
|
+
/** Initial delay in milliseconds (default: 1000) */
|
|
83
|
+
initialDelayMs?: number;
|
|
84
|
+
/** Maximum delay cap in milliseconds (default: 30000) */
|
|
85
|
+
maxDelayMs?: number;
|
|
86
|
+
/** Enable jitter randomization (default: true) */
|
|
87
|
+
jitter?: boolean;
|
|
88
|
+
/** Maximum jitter percentage (0-1, default: 0.1 = ±10%) */
|
|
89
|
+
jitterFactor?: number;
|
|
90
|
+
/** Only retry on specific errors (default: all errors) */
|
|
91
|
+
retryableErrors?: ReadonlyArray<TransportError>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Configuration for fixed delay retry strategy.
|
|
95
|
+
*/
|
|
96
|
+
export interface FixedDelayConfig {
|
|
97
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
98
|
+
maxAttempts?: number;
|
|
99
|
+
/** Fixed delay in milliseconds (default: 1000) */
|
|
100
|
+
delayMs?: number;
|
|
101
|
+
/** Enable jitter randomization (default: false) */
|
|
102
|
+
jitter?: boolean;
|
|
103
|
+
/** Maximum jitter percentage (0-1, default: 0.1 = ±10%) */
|
|
104
|
+
jitterFactor?: number;
|
|
105
|
+
/** Only retry on specific errors (default: all errors) */
|
|
106
|
+
retryableErrors?: ReadonlyArray<TransportError>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Configuration for circuit breaker.
|
|
110
|
+
*/
|
|
111
|
+
export interface CircuitBreakerConfig {
|
|
112
|
+
/** Number of consecutive failures before opening circuit (default: 5) */
|
|
113
|
+
failureThreshold?: number;
|
|
114
|
+
/** Time to wait before attempting to close circuit (ms, default: 60000) */
|
|
115
|
+
resetTimeoutMs?: number;
|
|
116
|
+
/** Number of successful requests needed to close circuit (default: 2) */
|
|
117
|
+
successThreshold?: number;
|
|
118
|
+
/** Time window for counting failures (ms, default: 60000) */
|
|
119
|
+
windowMs?: number;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Circuit breaker states.
|
|
123
|
+
*/
|
|
124
|
+
export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
125
|
+
/**
|
|
126
|
+
* Statistics for circuit breaker monitoring.
|
|
127
|
+
*/
|
|
128
|
+
export interface CircuitBreakerStats {
|
|
129
|
+
/** Current circuit state */
|
|
130
|
+
readonly state: CircuitState;
|
|
131
|
+
/** Number of consecutive failures */
|
|
132
|
+
readonly consecutiveFailures: number;
|
|
133
|
+
/** Number of consecutive successes in HALF_OPEN state */
|
|
134
|
+
readonly consecutiveSuccesses: number;
|
|
135
|
+
/** Timestamp when circuit was opened (if open) */
|
|
136
|
+
readonly openedAt?: number;
|
|
137
|
+
/** Total requests attempted */
|
|
138
|
+
readonly totalRequests: number;
|
|
139
|
+
/** Total failures */
|
|
140
|
+
readonly totalFailures: number;
|
|
141
|
+
/** Total successes */
|
|
142
|
+
readonly totalSuccesses: number;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Core retry strategy interface.
|
|
146
|
+
*
|
|
147
|
+
* Implementations determine retry behavior based on context.
|
|
148
|
+
*/
|
|
149
|
+
export interface IRetryStrategy {
|
|
150
|
+
/**
|
|
151
|
+
* Decide whether to retry the operation.
|
|
152
|
+
*
|
|
153
|
+
* @param context - Current retry context
|
|
154
|
+
* @returns Retry decision with delay
|
|
155
|
+
*/
|
|
156
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
157
|
+
/**
|
|
158
|
+
* Get strategy name for logging.
|
|
159
|
+
*/
|
|
160
|
+
readonly name: string;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Exponential backoff retry strategy.
|
|
164
|
+
*
|
|
165
|
+
* Delay formula: min(initialDelay * multiplier^attempt, maxDelay) ± jitter
|
|
166
|
+
*
|
|
167
|
+
* Best for: Network requests, API calls, database operations.
|
|
168
|
+
* Avoids overwhelming failing services with exponentially increasing delays.
|
|
169
|
+
*/
|
|
170
|
+
export declare class ExponentialBackoffStrategy implements IRetryStrategy {
|
|
171
|
+
readonly name = "ExponentialBackoff";
|
|
172
|
+
private readonly maxAttempts;
|
|
173
|
+
private readonly initialDelayMs;
|
|
174
|
+
private readonly maxDelayMs;
|
|
175
|
+
private readonly multiplier;
|
|
176
|
+
private readonly jitter;
|
|
177
|
+
private readonly jitterFactor;
|
|
178
|
+
private readonly retryableErrors;
|
|
179
|
+
constructor(config?: ExponentialBackoffConfig);
|
|
180
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
181
|
+
private applyJitter;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Linear backoff retry strategy.
|
|
185
|
+
*
|
|
186
|
+
* Delay formula: min(initialDelay + attempt * increment, maxDelay) ± jitter
|
|
187
|
+
*
|
|
188
|
+
* Best for: Rate-limited APIs, gradual load recovery.
|
|
189
|
+
* More predictable than exponential, suitable when service recovery is linear.
|
|
190
|
+
*/
|
|
191
|
+
export declare class LinearBackoffStrategy implements IRetryStrategy {
|
|
192
|
+
readonly name = "LinearBackoff";
|
|
193
|
+
private readonly maxAttempts;
|
|
194
|
+
private readonly delayIncrementMs;
|
|
195
|
+
private readonly initialDelayMs;
|
|
196
|
+
private readonly maxDelayMs;
|
|
197
|
+
private readonly jitter;
|
|
198
|
+
private readonly jitterFactor;
|
|
199
|
+
private readonly retryableErrors;
|
|
200
|
+
constructor(config?: LinearBackoffConfig);
|
|
201
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
202
|
+
private applyJitter;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Fixed delay retry strategy.
|
|
206
|
+
*
|
|
207
|
+
* Delay formula: delayMs ± jitter (constant for all attempts)
|
|
208
|
+
*
|
|
209
|
+
* Best for: Simple retry scenarios, testing.
|
|
210
|
+
* Predictable timing, no backoff complexity.
|
|
211
|
+
*/
|
|
212
|
+
export declare class FixedDelayStrategy implements IRetryStrategy {
|
|
213
|
+
readonly name = "FixedDelay";
|
|
214
|
+
private readonly maxAttempts;
|
|
215
|
+
private readonly delayMs;
|
|
216
|
+
private readonly jitter;
|
|
217
|
+
private readonly jitterFactor;
|
|
218
|
+
private readonly retryableErrors;
|
|
219
|
+
constructor(config?: FixedDelayConfig);
|
|
220
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
221
|
+
private applyJitter;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* No retry strategy (fail immediately).
|
|
225
|
+
*
|
|
226
|
+
* Best for: Operations that must succeed on first try, testing.
|
|
227
|
+
*/
|
|
228
|
+
export declare class NoRetryStrategy implements IRetryStrategy {
|
|
229
|
+
readonly name = "NoRetry";
|
|
230
|
+
shouldRetry(_context: RetryContext): RetryDecision;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Circuit breaker for preventing cascading failures.
|
|
234
|
+
*
|
|
235
|
+
* States:
|
|
236
|
+
* - CLOSED: Normal operation, requests pass through
|
|
237
|
+
* - OPEN: Circuit tripped, requests fail immediately
|
|
238
|
+
* - HALF_OPEN: Testing recovery, limited requests allowed
|
|
239
|
+
*
|
|
240
|
+
* Best practices:
|
|
241
|
+
* - Use with retry strategies for comprehensive fault tolerance
|
|
242
|
+
* - Monitor circuit state changes for alerting
|
|
243
|
+
* - Tune thresholds based on service characteristics
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const breaker = new CircuitBreaker({
|
|
248
|
+
* failureThreshold: 5,
|
|
249
|
+
* resetTimeoutMs: 60000,
|
|
250
|
+
* successThreshold: 2
|
|
251
|
+
* });
|
|
252
|
+
*
|
|
253
|
+
* // Check before operation
|
|
254
|
+
* if (breaker.allowRequest()) {
|
|
255
|
+
* const result = await operation();
|
|
256
|
+
* if (result.ok) {
|
|
257
|
+
* breaker.recordSuccess();
|
|
258
|
+
* } else {
|
|
259
|
+
* breaker.recordFailure();
|
|
260
|
+
* }
|
|
261
|
+
* }
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
export declare class CircuitBreaker {
|
|
265
|
+
private state;
|
|
266
|
+
private consecutiveFailures;
|
|
267
|
+
private consecutiveSuccesses;
|
|
268
|
+
private openedAt?;
|
|
269
|
+
private totalRequests;
|
|
270
|
+
private totalFailures;
|
|
271
|
+
private totalSuccesses;
|
|
272
|
+
private failureTimestamps;
|
|
273
|
+
private readonly failureThreshold;
|
|
274
|
+
private readonly resetTimeoutMs;
|
|
275
|
+
private readonly successThreshold;
|
|
276
|
+
private readonly windowMs;
|
|
277
|
+
constructor(config?: CircuitBreakerConfig);
|
|
278
|
+
/**
|
|
279
|
+
* Check if request should be allowed through circuit.
|
|
280
|
+
*
|
|
281
|
+
* @returns True if request should proceed, false if circuit is open
|
|
282
|
+
*/
|
|
283
|
+
allowRequest(): boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Record a successful operation.
|
|
286
|
+
*/
|
|
287
|
+
recordSuccess(): void;
|
|
288
|
+
/**
|
|
289
|
+
* Record a failed operation.
|
|
290
|
+
*/
|
|
291
|
+
recordFailure(): void;
|
|
292
|
+
/**
|
|
293
|
+
* Get current circuit breaker statistics.
|
|
294
|
+
*/
|
|
295
|
+
getStats(): CircuitBreakerStats;
|
|
296
|
+
/**
|
|
297
|
+
* Reset circuit breaker to initial state.
|
|
298
|
+
*/
|
|
299
|
+
reset(): void;
|
|
300
|
+
/**
|
|
301
|
+
* Force circuit to open (for testing or manual intervention).
|
|
302
|
+
*/
|
|
303
|
+
forceOpen(): void;
|
|
304
|
+
/**
|
|
305
|
+
* Force circuit to close (for testing or manual recovery).
|
|
306
|
+
*/
|
|
307
|
+
forceClose(): void;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Factory for creating retry strategies with common configurations.
|
|
311
|
+
*/
|
|
312
|
+
export declare class RetryStrategy {
|
|
313
|
+
/**
|
|
314
|
+
* Create exponential backoff strategy.
|
|
315
|
+
*
|
|
316
|
+
* @param config - Configuration options
|
|
317
|
+
* @returns Exponential backoff strategy
|
|
318
|
+
*/
|
|
319
|
+
static exponentialBackoff(config?: ExponentialBackoffConfig): IRetryStrategy;
|
|
320
|
+
/**
|
|
321
|
+
* Create linear backoff strategy.
|
|
322
|
+
*
|
|
323
|
+
* @param config - Configuration options
|
|
324
|
+
* @returns Linear backoff strategy
|
|
325
|
+
*/
|
|
326
|
+
static linearBackoff(config?: LinearBackoffConfig): IRetryStrategy;
|
|
327
|
+
/**
|
|
328
|
+
* Create fixed delay strategy.
|
|
329
|
+
*
|
|
330
|
+
* @param config - Configuration options
|
|
331
|
+
* @returns Fixed delay strategy
|
|
332
|
+
*/
|
|
333
|
+
static fixedDelay(config?: FixedDelayConfig): IRetryStrategy;
|
|
334
|
+
/**
|
|
335
|
+
* Create no retry strategy (fail immediately).
|
|
336
|
+
*
|
|
337
|
+
* @returns No retry strategy
|
|
338
|
+
*/
|
|
339
|
+
static noRetry(): IRetryStrategy;
|
|
340
|
+
/**
|
|
341
|
+
* Create aggressive retry strategy for critical operations.
|
|
342
|
+
*
|
|
343
|
+
* Config: 5 attempts, 500ms initial, max 10s, exponential backoff.
|
|
344
|
+
*
|
|
345
|
+
* @returns Aggressive retry strategy
|
|
346
|
+
*/
|
|
347
|
+
static aggressive(): IRetryStrategy;
|
|
348
|
+
/**
|
|
349
|
+
* Create conservative retry strategy for non-critical operations.
|
|
350
|
+
*
|
|
351
|
+
* Config: 2 attempts, 2s initial, max 5s, linear backoff.
|
|
352
|
+
*
|
|
353
|
+
* @returns Conservative retry strategy
|
|
354
|
+
*/
|
|
355
|
+
static conservative(): IRetryStrategy;
|
|
356
|
+
/**
|
|
357
|
+
* Create default retry strategy (balanced).
|
|
358
|
+
*
|
|
359
|
+
* Config: 3 attempts, 1s initial, max 30s, exponential backoff.
|
|
360
|
+
*
|
|
361
|
+
* @returns Default retry strategy
|
|
362
|
+
*/
|
|
363
|
+
static default(): IRetryStrategy;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Execute an operation with retry strategy and optional circuit breaker.
|
|
367
|
+
*
|
|
368
|
+
* @param operation - Async operation to execute
|
|
369
|
+
* @param strategy - Retry strategy to use
|
|
370
|
+
* @param circuitBreaker - Optional circuit breaker
|
|
371
|
+
* @returns Result of the operation
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```typescript
|
|
375
|
+
* const result = await executeWithRetry(
|
|
376
|
+
* async () => transport.send(envelope, did),
|
|
377
|
+
* RetryStrategy.exponentialBackoff(),
|
|
378
|
+
* circuitBreaker
|
|
379
|
+
* );
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
export declare function executeWithRetry<T>(operation: () => Promise<Result<T, TransportError>>, strategy: IRetryStrategy, circuitBreaker?: CircuitBreaker): Promise<Result<T, TransportError>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{err}from"./_deps/shared/index.js";export class ExponentialBackoffStrategy{name="ExponentialBackoff";maxAttempts;initialDelayMs;maxDelayMs;multiplier;jitter;jitterFactor;retryableErrors;constructor(t={}){this.maxAttempts=t.maxAttempts??3,this.initialDelayMs=t.initialDelayMs??1e3,this.maxDelayMs=t.maxDelayMs??3e4,this.multiplier=t.multiplier??2,this.jitter=t.jitter??!0,this.jitterFactor=t.jitterFactor??.2,this.retryableErrors=t.retryableErrors?new Set(t.retryableErrors):new Set(["SEND_FAILED","NETWORK_ERROR","RECIPIENT_UNREACHABLE","TIMEOUT"])}shouldRetry(t){if(t.attempt>=this.maxAttempts)return{shouldRetry:!1,reason:`Maximum attempts (${this.maxAttempts}) exceeded`};if(!this.retryableErrors.has(t.error))return{shouldRetry:!1,reason:`Error type '${t.error}' is not retryable`};const e=this.initialDelayMs*Math.pow(this.multiplier,t.attempt),s=Math.min(e,this.maxDelayMs),r=this.jitter?this.applyJitter(s):s;return{shouldRetry:!0,delayMs:Math.round(r),reason:`Attempt ${t.attempt+1}/${this.maxAttempts}`}}applyJitter(t){const e=new Uint32Array(1);crypto.getRandomValues(e);return t+(2*(e[0]/4294967295)-1)*(t*this.jitterFactor)}}export class LinearBackoffStrategy{name="LinearBackoff";maxAttempts;delayIncrementMs;initialDelayMs;maxDelayMs;jitter;jitterFactor;retryableErrors;constructor(t={}){this.maxAttempts=t.maxAttempts??3,this.delayIncrementMs=t.delayIncrementMs??1e3,this.initialDelayMs=t.initialDelayMs??1e3,this.maxDelayMs=t.maxDelayMs??3e4,this.jitter=t.jitter??!0,this.jitterFactor=t.jitterFactor??.1,this.retryableErrors=t.retryableErrors?new Set(t.retryableErrors):new Set(["SEND_FAILED","NETWORK_ERROR","RECIPIENT_UNREACHABLE","TIMEOUT"])}shouldRetry(t){if(t.attempt>=this.maxAttempts)return{shouldRetry:!1,reason:`Maximum attempts (${this.maxAttempts}) exceeded`};if(!this.retryableErrors.has(t.error))return{shouldRetry:!1,reason:`Error type '${t.error}' is not retryable`};const e=this.initialDelayMs+t.attempt*this.delayIncrementMs,s=Math.min(e,this.maxDelayMs),r=this.jitter?this.applyJitter(s):s;return{shouldRetry:!0,delayMs:Math.round(r),reason:`Attempt ${t.attempt+1}/${this.maxAttempts}`}}applyJitter(t){const e=new Uint32Array(1);crypto.getRandomValues(e);return t+(2*(e[0]/4294967295)-1)*(t*this.jitterFactor)}}export class FixedDelayStrategy{name="FixedDelay";maxAttempts;delayMs;jitter;jitterFactor;retryableErrors;constructor(t={}){this.maxAttempts=t.maxAttempts??3,this.delayMs=t.delayMs??1e3,this.jitter=t.jitter??!1,this.jitterFactor=t.jitterFactor??.1,this.retryableErrors=t.retryableErrors?new Set(t.retryableErrors):new Set(["SEND_FAILED","NETWORK_ERROR","RECIPIENT_UNREACHABLE","TIMEOUT"])}shouldRetry(t){if(t.attempt>=this.maxAttempts)return{shouldRetry:!1,reason:`Maximum attempts (${this.maxAttempts}) exceeded`};if(!this.retryableErrors.has(t.error))return{shouldRetry:!1,reason:`Error type '${t.error}' is not retryable`};const e=this.jitter?this.applyJitter(this.delayMs):this.delayMs;return{shouldRetry:!0,delayMs:Math.round(e),reason:`Attempt ${t.attempt+1}/${this.maxAttempts}`}}applyJitter(t){const e=new Uint32Array(1);crypto.getRandomValues(e);return t+(2*(e[0]/4294967295)-1)*(t*this.jitterFactor)}}export class NoRetryStrategy{name="NoRetry";shouldRetry(t){return{shouldRetry:!1,reason:"No retry policy - fail immediately"}}}export class CircuitBreaker{state="CLOSED";consecutiveFailures=0;consecutiveSuccesses=0;openedAt;totalRequests=0;totalFailures=0;totalSuccesses=0;failureTimestamps=[];failureThreshold;resetTimeoutMs;successThreshold;windowMs;constructor(t={}){this.failureThreshold=t.failureThreshold??5,this.resetTimeoutMs=t.resetTimeoutMs??6e4,this.successThreshold=t.successThreshold??2,this.windowMs=t.windowMs??6e4}allowRequest(){const t=Date.now();return this.failureTimestamps=this.failureTimestamps.filter(e=>t-e<this.windowMs),"CLOSED"===this.state||("OPEN"!==this.state||!!(this.openedAt&&t-this.openedAt>=this.resetTimeoutMs)&&(this.state="HALF_OPEN",this.consecutiveSuccesses=0,!0))}recordSuccess(){this.totalRequests++,this.totalSuccesses++,this.consecutiveFailures=0,"HALF_OPEN"===this.state&&(this.consecutiveSuccesses++,this.consecutiveSuccesses>=this.successThreshold&&(this.state="CLOSED",this.consecutiveSuccesses=0,this.openedAt=void 0))}recordFailure(){this.totalRequests++,this.totalFailures++,this.consecutiveFailures++,this.consecutiveSuccesses=0;const t=Date.now();if(this.failureTimestamps.push(t),"CLOSED"===this.state||"HALF_OPEN"===this.state){this.failureTimestamps.filter(e=>t-e<this.windowMs).length>=this.failureThreshold&&(this.state="OPEN",this.openedAt=t)}}getStats(){return{state:this.state,consecutiveFailures:this.consecutiveFailures,consecutiveSuccesses:this.consecutiveSuccesses,openedAt:this.openedAt,totalRequests:this.totalRequests,totalFailures:this.totalFailures,totalSuccesses:this.totalSuccesses}}reset(){this.state="CLOSED",this.consecutiveFailures=0,this.consecutiveSuccesses=0,this.openedAt=void 0,this.totalRequests=0,this.totalFailures=0,this.totalSuccesses=0,this.failureTimestamps=[]}forceOpen(){this.state="OPEN",this.openedAt=Date.now()}forceClose(){this.state="CLOSED",this.consecutiveFailures=0,this.consecutiveSuccesses=0,this.openedAt=void 0}}export class RetryStrategy{static exponentialBackoff(t){return new ExponentialBackoffStrategy(t)}static linearBackoff(t){return new LinearBackoffStrategy(t)}static fixedDelay(t){return new FixedDelayStrategy(t)}static noRetry(){return new NoRetryStrategy}static aggressive(){return new ExponentialBackoffStrategy({maxAttempts:5,initialDelayMs:500,maxDelayMs:1e4,multiplier:2,jitter:!0})}static conservative(){return new LinearBackoffStrategy({maxAttempts:2,initialDelayMs:2e3,delayIncrementMs:1e3,maxDelayMs:5e3,jitter:!0})}static default(){return new ExponentialBackoffStrategy({maxAttempts:3,initialDelayMs:1e3,maxDelayMs:3e4,multiplier:2,jitter:!0})}}export async function executeWithRetry(t,e,s){const r=Date.now();let i=0,a="NETWORK_ERROR";for(;;){if(s&&!s.allowRequest())return err("SEND_FAILED");const o=await t();if(o.ok)return s?.recordSuccess(),o;a=o.error,s?.recordFailure();const l={attempt:i,error:a,startTime:r,elapsedMs:Date.now()-r},n=e.shouldRetry(l);if(!n.shouldRetry)return err(a);n.delayMs&&n.delayMs>0&&await sleep(n.delayMs),i++}}function sleep(t){return new Promise(e=>setTimeout(e,t))}
|
|
@@ -1,98 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Decorator that adds exponential backoff retry logic to any transport adapter.
|
|
4
|
-
*
|
|
5
|
-
* Retry delays follow exponential backoff with jitter:
|
|
6
|
-
* - Formula: 2^attempt * baseDelay + jitter
|
|
7
|
-
* - Jitter: Math.random() * maxJitter * 2 - maxJitter
|
|
8
|
-
* - Default delays: 1s, 2s, 4s (with ±200ms jitter)
|
|
9
|
-
*
|
|
10
|
-
* Use case: Push notification delivery failures requiring automatic retry.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const transport = new RetryTransportAdapter(baseTransport, {
|
|
15
|
-
* maxRetries: 3,
|
|
16
|
-
* baseDelayMs: 1000,
|
|
17
|
-
* maxJitterMs: 200
|
|
18
|
-
* });
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export class RetryTransportAdapter {
|
|
22
|
-
inner;
|
|
23
|
-
maxRetries;
|
|
24
|
-
baseDelayMs;
|
|
25
|
-
maxJitterMs;
|
|
26
|
-
/**
|
|
27
|
-
* Create a new RetryTransportAdapter wrapping an existing transport.
|
|
28
|
-
*
|
|
29
|
-
* @param inner - The transport adapter to wrap with retry logic
|
|
30
|
-
* @param options - Retry configuration options
|
|
31
|
-
*/
|
|
32
|
-
constructor(inner, options = {}) {
|
|
33
|
-
this.inner = inner;
|
|
34
|
-
this.maxRetries = options.maxRetries ?? 3;
|
|
35
|
-
this.baseDelayMs = options.baseDelayMs ?? 1000;
|
|
36
|
-
this.maxJitterMs = options.maxJitterMs ?? 200;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Send an envelope with exponential backoff retry logic.
|
|
40
|
-
*
|
|
41
|
-
* Retries on all error types (SEND_FAILED, NETWORK_ERROR, RECIPIENT_UNREACHABLE, TIMEOUT).
|
|
42
|
-
* Throws error after all retries are exhausted.
|
|
43
|
-
*
|
|
44
|
-
* @param envelope - The envelope to send
|
|
45
|
-
* @param recipientDid - The recipient's DID
|
|
46
|
-
* @returns Result with void on success, or TransportError on failure
|
|
47
|
-
* @throws Error if all retry attempts are exhausted
|
|
48
|
-
*/
|
|
49
|
-
async send(envelope, recipientDid) {
|
|
50
|
-
let lastError;
|
|
51
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
52
|
-
const result = await this.inner.send(envelope, recipientDid);
|
|
53
|
-
// Success - return immediately
|
|
54
|
-
if (result.ok) {
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
// Store error for final throw
|
|
58
|
-
lastError = result.error;
|
|
59
|
-
// Don't delay after final attempt
|
|
60
|
-
if (attempt < this.maxRetries) {
|
|
61
|
-
// Exponential backoff: 2^attempt * baseDelay + jitter
|
|
62
|
-
const delay = Math.pow(2, attempt) * this.baseDelayMs;
|
|
63
|
-
// SAFETY: Using crypto.getRandomValues for OWASP-compliant secure random jitter
|
|
64
|
-
const jitterArray = new Uint32Array(1);
|
|
65
|
-
crypto.getRandomValues(jitterArray);
|
|
66
|
-
const jitter = (jitterArray[0] / 0xffffffff) * this.maxJitterMs * 2 -
|
|
67
|
-
this.maxJitterMs;
|
|
68
|
-
await this.sleep(delay + jitter);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// All retries exhausted - throw error with clear message
|
|
72
|
-
throw new Error(`Failed after ${this.maxRetries} retries: ${lastError ?? 'unknown error'}`);
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Register a handler for incoming envelopes.
|
|
76
|
-
* Delegates directly to the inner transport.
|
|
77
|
-
*
|
|
78
|
-
* @param handler - The envelope handler function
|
|
79
|
-
*/
|
|
80
|
-
onReceive(handler) {
|
|
81
|
-
this.inner.onReceive(handler);
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Shut down the transport.
|
|
85
|
-
* Delegates directly to the inner transport.
|
|
86
|
-
*/
|
|
87
|
-
dispose() {
|
|
88
|
-
this.inner.dispose();
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Sleep for a specified duration.
|
|
92
|
-
*
|
|
93
|
-
* @param ms - Duration in milliseconds
|
|
94
|
-
*/
|
|
95
|
-
sleep(ms) {
|
|
96
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
1
|
+
export class RetryTransportAdapter{inner;maxRetries;baseDelayMs;maxJitterMs;constructor(e,t={}){this.inner=e,this.maxRetries=t.maxRetries??3,this.baseDelayMs=t.baseDelayMs??1e3,this.maxJitterMs=t.maxJitterMs??200}async send(e,t){let s;for(let r=0;r<=this.maxRetries;r++){const i=await this.inner.send(e,t);if(i.ok)return i;if(s=i.error,r<this.maxRetries){const e=Math.pow(2,r)*this.baseDelayMs,t=new Uint32Array(1);crypto.getRandomValues(t);const s=t[0]/4294967295*this.maxJitterMs*2-this.maxJitterMs;await this.sleep(e+s)}}throw new Error(`Failed after ${this.maxRetries} retries: ${s??"unknown error"}`)}onReceive(e){this.inner.onReceive(e)}dispose(){this.inner.dispose()}sleep(e){return new Promise(t=>setTimeout(t,e))}}
|