@goodie-ts/resilience 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -9
- package/dist/beans.json +99 -0
- package/dist/circuit-breaker-interceptor.d.ts.map +1 -1
- package/dist/circuit-breaker-interceptor.js +143 -92
- package/dist/circuit-breaker-interceptor.js.map +1 -1
- package/dist/decorators/circuit-breaker.d.ts +2 -4
- package/dist/decorators/circuit-breaker.d.ts.map +1 -1
- package/dist/decorators/circuit-breaker.js +3 -6
- package/dist/decorators/circuit-breaker.js.map +1 -1
- package/dist/decorators/retryable.d.ts +2 -4
- package/dist/decorators/retryable.d.ts.map +1 -1
- package/dist/decorators/retryable.js +3 -6
- package/dist/decorators/retryable.js.map +1 -1
- package/dist/decorators/timeout.d.ts +2 -4
- package/dist/decorators/timeout.d.ts.map +1 -1
- package/dist/decorators/timeout.js +3 -6
- package/dist/decorators/timeout.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/retry-interceptor.d.ts.map +1 -1
- package/dist/retry-interceptor.js +93 -42
- package/dist/retry-interceptor.js.map +1 -1
- package/dist/timeout-interceptor.d.ts.map +1 -1
- package/dist/timeout-interceptor.js +68 -17
- package/dist/timeout-interceptor.js.map +1 -1
- package/package.json +9 -7
- package/dist/resilience-transformer-plugin.d.ts +0 -9
- package/dist/resilience-transformer-plugin.d.ts.map +0 -1
- package/dist/resilience-transformer-plugin.js +0 -211
- package/dist/resilience-transformer-plugin.js.map +0 -1
package/README.md
CHANGED
|
@@ -42,16 +42,9 @@ class PaymentService {
|
|
|
42
42
|
- `TimeoutError` — thrown when `@Timeout` duration is exceeded
|
|
43
43
|
- `CircuitOpenError` — thrown when the circuit breaker is OPEN and rejecting calls
|
|
44
44
|
|
|
45
|
-
##
|
|
45
|
+
## Setup
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
import { diPlugin } from '@goodie-ts/vite-plugin';
|
|
49
|
-
import { createResiliencePlugin } from '@goodie-ts/resilience';
|
|
50
|
-
|
|
51
|
-
export default defineConfig({
|
|
52
|
-
plugins: [diPlugin({ plugins: [createResiliencePlugin()] })],
|
|
53
|
-
});
|
|
54
|
-
```
|
|
47
|
+
No plugin configuration needed — `@goodie-ts/resilience` ships pre-scanned beans and AOP config in `beans.json`. The transformer auto-discovers them at build time.
|
|
55
48
|
|
|
56
49
|
## License
|
|
57
50
|
|
package/dist/beans.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"package": "@goodie-ts/resilience",
|
|
4
|
+
"beans": [
|
|
5
|
+
{
|
|
6
|
+
"tokenRef": {
|
|
7
|
+
"kind": "class",
|
|
8
|
+
"className": "CircuitBreakerInterceptor",
|
|
9
|
+
"importPath": "@goodie-ts/resilience"
|
|
10
|
+
},
|
|
11
|
+
"scope": "singleton",
|
|
12
|
+
"eager": false,
|
|
13
|
+
"name": null,
|
|
14
|
+
"constructorDeps": [],
|
|
15
|
+
"fieldDeps": [],
|
|
16
|
+
"factoryKind": "constructor",
|
|
17
|
+
"providesSource": null,
|
|
18
|
+
"baseTokenRefs": null,
|
|
19
|
+
"metadata": {},
|
|
20
|
+
"sourceLocation": {
|
|
21
|
+
"filePath": "@goodie-ts/resilience",
|
|
22
|
+
"line": 39,
|
|
23
|
+
"column": 1
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"tokenRef": {
|
|
28
|
+
"kind": "class",
|
|
29
|
+
"className": "RetryInterceptor",
|
|
30
|
+
"importPath": "@goodie-ts/resilience"
|
|
31
|
+
},
|
|
32
|
+
"scope": "singleton",
|
|
33
|
+
"eager": false,
|
|
34
|
+
"name": null,
|
|
35
|
+
"constructorDeps": [],
|
|
36
|
+
"fieldDeps": [],
|
|
37
|
+
"factoryKind": "constructor",
|
|
38
|
+
"providesSource": null,
|
|
39
|
+
"baseTokenRefs": null,
|
|
40
|
+
"metadata": {},
|
|
41
|
+
"sourceLocation": {
|
|
42
|
+
"filePath": "@goodie-ts/resilience",
|
|
43
|
+
"line": 31,
|
|
44
|
+
"column": 1
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"tokenRef": {
|
|
49
|
+
"kind": "class",
|
|
50
|
+
"className": "TimeoutInterceptor",
|
|
51
|
+
"importPath": "@goodie-ts/resilience"
|
|
52
|
+
},
|
|
53
|
+
"scope": "singleton",
|
|
54
|
+
"eager": false,
|
|
55
|
+
"name": null,
|
|
56
|
+
"constructorDeps": [],
|
|
57
|
+
"fieldDeps": [],
|
|
58
|
+
"factoryKind": "constructor",
|
|
59
|
+
"providesSource": null,
|
|
60
|
+
"baseTokenRefs": null,
|
|
61
|
+
"metadata": {},
|
|
62
|
+
"sourceLocation": {
|
|
63
|
+
"filePath": "@goodie-ts/resilience",
|
|
64
|
+
"line": 24,
|
|
65
|
+
"column": 1
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"aop": {
|
|
70
|
+
"CircuitBreaker": {
|
|
71
|
+
"interceptor": "CircuitBreakerInterceptor",
|
|
72
|
+
"order": -20,
|
|
73
|
+
"defaults": {
|
|
74
|
+
"failureThreshold": 5,
|
|
75
|
+
"resetTimeout": 30000,
|
|
76
|
+
"halfOpenAttempts": 1
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"Retryable": {
|
|
80
|
+
"interceptor": "RetryInterceptor",
|
|
81
|
+
"order": -10,
|
|
82
|
+
"defaults": {
|
|
83
|
+
"maxAttempts": 3,
|
|
84
|
+
"delay": 1000,
|
|
85
|
+
"multiplier": 1
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"Timeout": {
|
|
89
|
+
"interceptor": "TimeoutInterceptor",
|
|
90
|
+
"order": -30,
|
|
91
|
+
"argMapping": [
|
|
92
|
+
"duration"
|
|
93
|
+
],
|
|
94
|
+
"defaults": {
|
|
95
|
+
"duration": 5000
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circuit-breaker-interceptor.d.ts","sourceRoot":"","sources":["../src/circuit-breaker-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"circuit-breaker-interceptor.d.ts","sourceRoot":"","sources":["../src/circuit-breaker-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAG3E,KAAK,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAoBpD,yEAAyE;AACzE,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,SAAS,EAAE,MAAM;CAI9B;AAED;;;;;;GAMG;AACH,qBACa,yBAA0B,YAAW,iBAAiB;IACjE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAE5D,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO;IAyD1C,gEAAgE;IAChE,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY;IAKpE,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,SAAS;CAclB"}
|
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { Singleton } from '@goodie-ts/decorators';
|
|
1
36
|
/** Error thrown when the circuit breaker is open and rejecting calls. */
|
|
2
37
|
export class CircuitOpenError extends Error {
|
|
3
38
|
constructor(methodKey) {
|
|
@@ -12,107 +47,123 @@ export class CircuitOpenError extends Error {
|
|
|
12
47
|
*
|
|
13
48
|
* Each decorated method gets its own circuit, keyed by `className:methodName`.
|
|
14
49
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
50
|
+
let CircuitBreakerInterceptor = (() => {
|
|
51
|
+
let _classDecorators = [Singleton()];
|
|
52
|
+
let _classDescriptor;
|
|
53
|
+
let _classExtraInitializers = [];
|
|
54
|
+
let _classThis;
|
|
55
|
+
var CircuitBreakerInterceptor = class {
|
|
56
|
+
static { _classThis = this; }
|
|
57
|
+
static {
|
|
58
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
59
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
60
|
+
CircuitBreakerInterceptor = _classThis = _classDescriptor.value;
|
|
61
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
62
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
63
|
+
}
|
|
64
|
+
circuits = new Map();
|
|
65
|
+
intercept(ctx) {
|
|
66
|
+
const meta = ctx.metadata;
|
|
67
|
+
if (!meta)
|
|
68
|
+
return ctx.proceed();
|
|
69
|
+
const key = `${ctx.className}:${ctx.methodName}`;
|
|
70
|
+
const circuit = this.getOrCreateCircuit(key, meta);
|
|
71
|
+
if (circuit.state === 'OPEN') {
|
|
72
|
+
const elapsed = Date.now() - circuit.lastFailureTime;
|
|
73
|
+
if (elapsed >= circuit.resetTimeout) {
|
|
74
|
+
circuit.state = 'HALF_OPEN';
|
|
75
|
+
circuit.successCount = 0;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
throw new CircuitOpenError(key);
|
|
79
|
+
}
|
|
28
80
|
}
|
|
29
|
-
|
|
81
|
+
// Only allow one probe at a time during HALF_OPEN. Concurrent calls are
|
|
82
|
+
// rejected immediately so a burst of requests doesn't overwhelm a
|
|
83
|
+
// recovering backend.
|
|
84
|
+
if (circuit.state === 'HALF_OPEN' && circuit.halfOpenProbeInFlight) {
|
|
30
85
|
throw new CircuitOpenError(key);
|
|
31
86
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
const isProbe = circuit.state === 'HALF_OPEN';
|
|
88
|
+
if (isProbe) {
|
|
89
|
+
circuit.halfOpenProbeInFlight = true;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const result = ctx.proceed();
|
|
93
|
+
if (result instanceof Promise) {
|
|
94
|
+
return result.then((value) => {
|
|
95
|
+
if (isProbe)
|
|
96
|
+
circuit.halfOpenProbeInFlight = false;
|
|
97
|
+
this.onSuccess(circuit);
|
|
98
|
+
return value;
|
|
99
|
+
}, (error) => {
|
|
100
|
+
if (isProbe)
|
|
101
|
+
circuit.halfOpenProbeInFlight = false;
|
|
102
|
+
this.onFailure(circuit);
|
|
103
|
+
throw error;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (isProbe)
|
|
107
|
+
circuit.halfOpenProbeInFlight = false;
|
|
108
|
+
this.onSuccess(circuit);
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (isProbe)
|
|
113
|
+
circuit.halfOpenProbeInFlight = false;
|
|
114
|
+
this.onFailure(circuit);
|
|
115
|
+
throw error;
|
|
57
116
|
}
|
|
58
|
-
if (isProbe)
|
|
59
|
-
circuit.halfOpenProbeInFlight = false;
|
|
60
|
-
this.onSuccess(circuit);
|
|
61
|
-
return result;
|
|
62
117
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
throw error;
|
|
118
|
+
/** Visible for testing — get the current state of a circuit. */
|
|
119
|
+
getCircuitState(className, methodName) {
|
|
120
|
+
const key = `${className}:${methodName}`;
|
|
121
|
+
return this.circuits.get(key)?.state ?? 'CLOSED';
|
|
68
122
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
halfOpenAttempts: meta.halfOpenAttempts,
|
|
86
|
-
halfOpenProbeInFlight: false,
|
|
87
|
-
};
|
|
88
|
-
this.circuits.set(key, circuit);
|
|
123
|
+
getOrCreateCircuit(key, meta) {
|
|
124
|
+
let circuit = this.circuits.get(key);
|
|
125
|
+
if (!circuit) {
|
|
126
|
+
circuit = {
|
|
127
|
+
state: 'CLOSED',
|
|
128
|
+
failureCount: 0,
|
|
129
|
+
successCount: 0,
|
|
130
|
+
lastFailureTime: 0,
|
|
131
|
+
failureThreshold: meta.failureThreshold,
|
|
132
|
+
resetTimeout: meta.resetTimeout,
|
|
133
|
+
halfOpenAttempts: meta.halfOpenAttempts,
|
|
134
|
+
halfOpenProbeInFlight: false,
|
|
135
|
+
};
|
|
136
|
+
this.circuits.set(key, circuit);
|
|
137
|
+
}
|
|
138
|
+
return circuit;
|
|
89
139
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
140
|
+
onSuccess(circuit) {
|
|
141
|
+
if (circuit.state === 'HALF_OPEN') {
|
|
142
|
+
circuit.successCount++;
|
|
143
|
+
if (circuit.successCount >= circuit.halfOpenAttempts) {
|
|
144
|
+
circuit.state = 'CLOSED';
|
|
145
|
+
circuit.failureCount = 0;
|
|
146
|
+
circuit.successCount = 0;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (circuit.state === 'CLOSED') {
|
|
97
150
|
circuit.failureCount = 0;
|
|
98
|
-
circuit.successCount = 0;
|
|
99
151
|
}
|
|
100
152
|
}
|
|
101
|
-
|
|
102
|
-
circuit.failureCount
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
else if (circuit.state === 'CLOSED' &&
|
|
113
|
-
circuit.failureCount >= circuit.failureThreshold) {
|
|
114
|
-
circuit.state = 'OPEN';
|
|
153
|
+
onFailure(circuit) {
|
|
154
|
+
circuit.failureCount++;
|
|
155
|
+
circuit.lastFailureTime = Date.now();
|
|
156
|
+
if (circuit.state === 'HALF_OPEN') {
|
|
157
|
+
circuit.state = 'OPEN';
|
|
158
|
+
circuit.successCount = 0;
|
|
159
|
+
}
|
|
160
|
+
else if (circuit.state === 'CLOSED' &&
|
|
161
|
+
circuit.failureCount >= circuit.failureThreshold) {
|
|
162
|
+
circuit.state = 'OPEN';
|
|
163
|
+
}
|
|
115
164
|
}
|
|
116
|
-
}
|
|
117
|
-
|
|
165
|
+
};
|
|
166
|
+
return CircuitBreakerInterceptor = _classThis;
|
|
167
|
+
})();
|
|
168
|
+
export { CircuitBreakerInterceptor };
|
|
118
169
|
//# sourceMappingURL=circuit-breaker-interceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circuit-breaker-interceptor.js","sourceRoot":"","sources":["../src/circuit-breaker-interceptor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"circuit-breaker-interceptor.js","sourceRoot":"","sources":["../src/circuit-breaker-interceptor.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,13 +6,11 @@ export interface CircuitBreakerOptions {
|
|
|
6
6
|
/** Number of successes in HALF_OPEN needed to close the circuit (default: 1). */
|
|
7
7
|
halfOpenAttempts?: number;
|
|
8
8
|
}
|
|
9
|
-
type MethodDecorator_Stage3 = (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
10
9
|
/**
|
|
11
10
|
* Mark a method for circuit breaker protection.
|
|
12
11
|
*
|
|
13
|
-
* At compile time, the
|
|
12
|
+
* At compile time, the AOP scanner reads the type parameter
|
|
14
13
|
* and wires the `CircuitBreakerInterceptor` via AOP.
|
|
15
14
|
*/
|
|
16
|
-
export declare
|
|
17
|
-
export {};
|
|
15
|
+
export declare const CircuitBreaker: (opts?: CircuitBreakerOptions | undefined) => (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
18
16
|
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/decorators/circuit-breaker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/decorators/circuit-breaker.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,0DAMV,GAAG,sEADhB,CAAC"}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
+
import { createAopDecorator } from '@goodie-ts/aop';
|
|
1
2
|
/**
|
|
2
3
|
* Mark a method for circuit breaker protection.
|
|
3
4
|
*
|
|
4
|
-
* At compile time, the
|
|
5
|
+
* At compile time, the AOP scanner reads the type parameter
|
|
5
6
|
* and wires the `CircuitBreakerInterceptor` via AOP.
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
return (_target, _context) => {
|
|
9
|
-
// No-op: read at compile time by the resilience transformer plugin
|
|
10
|
-
};
|
|
11
|
-
}
|
|
8
|
+
export const CircuitBreaker = createAopDecorator();
|
|
12
9
|
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/decorators/circuit-breaker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/decorators/circuit-breaker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAYpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,kBAAkB,EAK5C,CAAC"}
|
|
@@ -6,13 +6,11 @@ export interface RetryOptions {
|
|
|
6
6
|
/** Multiplier for exponential backoff (default: 1 — no backoff). */
|
|
7
7
|
multiplier?: number;
|
|
8
8
|
}
|
|
9
|
-
type MethodDecorator_Stage3 = (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
10
9
|
/**
|
|
11
10
|
* Mark a method for automatic retry on failure.
|
|
12
11
|
*
|
|
13
|
-
* At compile time, the
|
|
12
|
+
* At compile time, the AOP scanner reads the type parameter
|
|
14
13
|
* and wires the `RetryInterceptor` via AOP. No runtime metadata is stored.
|
|
15
14
|
*/
|
|
16
|
-
export declare
|
|
17
|
-
export {};
|
|
15
|
+
export declare const Retryable: (opts?: RetryOptions | undefined) => (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
18
16
|
//# sourceMappingURL=retryable.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retryable.d.ts","sourceRoot":"","sources":["../../src/decorators/retryable.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retryable.d.ts","sourceRoot":"","sources":["../../src/decorators/retryable.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,SAAS,iDAMkG,GAAG,sEADvH,CAAC"}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
+
import { createAopDecorator } from '@goodie-ts/aop';
|
|
1
2
|
/**
|
|
2
3
|
* Mark a method for automatic retry on failure.
|
|
3
4
|
*
|
|
4
|
-
* At compile time, the
|
|
5
|
+
* At compile time, the AOP scanner reads the type parameter
|
|
5
6
|
* and wires the `RetryInterceptor` via AOP. No runtime metadata is stored.
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
return (_target, _context) => {
|
|
9
|
-
// No-op: read at compile time by the resilience transformer plugin
|
|
10
|
-
};
|
|
11
|
-
}
|
|
8
|
+
export const Retryable = createAopDecorator();
|
|
12
9
|
//# sourceMappingURL=retryable.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retryable.js","sourceRoot":"","sources":["../../src/decorators/retryable.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retryable.js","sourceRoot":"","sources":["../../src/decorators/retryable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAYpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,kBAAkB,EAKvC,CAAC"}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
type MethodDecorator_Stage3 = (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
2
1
|
/**
|
|
3
2
|
* Mark a method for automatic timeout.
|
|
4
3
|
*
|
|
5
|
-
* At compile time, the
|
|
4
|
+
* At compile time, the AOP scanner reads the type parameter
|
|
6
5
|
* and wires the `TimeoutInterceptor` via AOP.
|
|
7
6
|
*
|
|
8
7
|
* @param duration - Timeout duration in milliseconds.
|
|
9
8
|
*/
|
|
10
|
-
export declare
|
|
11
|
-
export {};
|
|
9
|
+
export declare const Timeout: (duration: number) => (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
12
10
|
//# sourceMappingURL=timeout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../src/decorators/timeout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../src/decorators/timeout.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,kCAOkX,GAAG,sEADrY,CAAC"}
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
+
import { createAopDecorator } from '@goodie-ts/aop';
|
|
1
2
|
/**
|
|
2
3
|
* Mark a method for automatic timeout.
|
|
3
4
|
*
|
|
4
|
-
* At compile time, the
|
|
5
|
+
* At compile time, the AOP scanner reads the type parameter
|
|
5
6
|
* and wires the `TimeoutInterceptor` via AOP.
|
|
6
7
|
*
|
|
7
8
|
* @param duration - Timeout duration in milliseconds.
|
|
8
9
|
*/
|
|
9
|
-
export
|
|
10
|
-
return (_target, _context) => {
|
|
11
|
-
// No-op: read at compile time by the resilience transformer plugin
|
|
12
|
-
};
|
|
13
|
-
}
|
|
10
|
+
export const Timeout = createAopDecorator();
|
|
14
11
|
//# sourceMappingURL=timeout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/decorators/timeout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../src/decorators/timeout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,kBAAkB,EAMrC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ export { CircuitBreakerInterceptor, CircuitOpenError, } from './circuit-breaker-
|
|
|
2
2
|
export { CircuitBreaker } from './decorators/circuit-breaker.js';
|
|
3
3
|
export { Retryable } from './decorators/retryable.js';
|
|
4
4
|
export { Timeout } from './decorators/timeout.js';
|
|
5
|
-
export { createResiliencePlugin } from './resilience-transformer-plugin.js';
|
|
6
5
|
export { RetryInterceptor } from './retry-interceptor.js';
|
|
7
6
|
export { TimeoutError, TimeoutInterceptor } from './timeout-interceptor.js';
|
|
8
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export { CircuitBreakerInterceptor, CircuitOpenError, } from './circuit-breaker-
|
|
|
2
2
|
export { CircuitBreaker } from './decorators/circuit-breaker.js';
|
|
3
3
|
export { Retryable } from './decorators/retryable.js';
|
|
4
4
|
export { Timeout } from './decorators/timeout.js';
|
|
5
|
-
export { createResiliencePlugin } from './resilience-transformer-plugin.js';
|
|
6
5
|
export { RetryInterceptor } from './retry-interceptor.js';
|
|
7
6
|
export { TimeoutError, TimeoutInterceptor } from './timeout-interceptor.js';
|
|
8
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-interceptor.d.ts","sourceRoot":"","sources":["../src/retry-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"retry-interceptor.d.ts","sourceRoot":"","sources":["../src/retry-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAU3E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBACa,gBAAiB,YAAW,iBAAiB;IACxD,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO;IAQ1C,OAAO,CAAC,OAAO;IAoBf,OAAO,CAAC,WAAW;CA+BpB"}
|
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { Singleton } from '@goodie-ts/decorators';
|
|
1
36
|
/**
|
|
2
37
|
* AOP interceptor that retries failed method calls with configurable
|
|
3
38
|
* backoff strategy (exponential backoff with random jitter).
|
|
@@ -18,51 +53,67 @@
|
|
|
18
53
|
* failure. Callers should always `await` the return value of `@Retryable`
|
|
19
54
|
* methods.
|
|
20
55
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
return result;
|
|
56
|
+
let RetryInterceptor = (() => {
|
|
57
|
+
let _classDecorators = [Singleton()];
|
|
58
|
+
let _classDescriptor;
|
|
59
|
+
let _classExtraInitializers = [];
|
|
60
|
+
let _classThis;
|
|
61
|
+
var RetryInterceptor = class {
|
|
62
|
+
static { _classThis = this; }
|
|
63
|
+
static {
|
|
64
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
65
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
66
|
+
RetryInterceptor = _classThis = _classDescriptor.value;
|
|
67
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
68
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
36
69
|
}
|
|
37
|
-
|
|
38
|
-
|
|
70
|
+
intercept(ctx) {
|
|
71
|
+
const meta = ctx.metadata;
|
|
72
|
+
if (!meta)
|
|
73
|
+
return ctx.proceed();
|
|
74
|
+
const result = this.tryCall(ctx, meta, 1);
|
|
75
|
+
return result;
|
|
39
76
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
77
|
+
tryCall(ctx, meta, attempt) {
|
|
78
|
+
try {
|
|
79
|
+
const result = ctx.proceed();
|
|
80
|
+
if (result instanceof Promise) {
|
|
81
|
+
return result.catch((error) => this.handleError(ctx, meta, attempt, error));
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return this.handleError(ctx, meta, attempt, error);
|
|
87
|
+
}
|
|
44
88
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
89
|
+
handleError(ctx, meta, attempt, error) {
|
|
90
|
+
if (attempt >= meta.maxAttempts) {
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
// Exponential backoff with random jitter (50–100% of computed delay)
|
|
94
|
+
// to prevent thundering herd when many callers retry simultaneously.
|
|
95
|
+
const baseDelay = meta.delay * meta.multiplier ** (attempt - 1);
|
|
96
|
+
const delayMs = baseDelay * (0.5 + Math.random() * 0.5);
|
|
97
|
+
// For async methods, use setTimeout-based delay
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
try {
|
|
101
|
+
const result = this.tryCall(ctx, meta, attempt + 1);
|
|
102
|
+
if (result instanceof Promise) {
|
|
103
|
+
result.then(resolve, reject);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
resolve(result);
|
|
107
|
+
}
|
|
56
108
|
}
|
|
57
|
-
|
|
58
|
-
|
|
109
|
+
catch (retryError) {
|
|
110
|
+
reject(retryError);
|
|
59
111
|
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
112
|
+
}, delayMs);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
return RetryInterceptor = _classThis;
|
|
117
|
+
})();
|
|
118
|
+
export { RetryInterceptor };
|
|
68
119
|
//# sourceMappingURL=retry-interceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-interceptor.js","sourceRoot":"","sources":["../src/retry-interceptor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retry-interceptor.js","sourceRoot":"","sources":["../src/retry-interceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AASlD;;;;;;;;;;;;;;;;;;;GAmBG;IAEU,gBAAgB;4BAD5B,SAAS,EAAE;;;;;;;;YACZ,6KA4DC;;;YA5DY,uDAAgB;;QAC3B,SAAS,CAAC,GAAsB;YAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAqC,CAAC;YACvD,IAAI,CAAC,IAAI;gBAAE,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC;YAEhC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC1C,OAAO,MAAM,CAAC;QAChB,CAAC;QAEO,OAAO,CACb,GAAsB,EACtB,IAAmB,EACnB,OAAe;YAEf,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;gBAE7B,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;oBAC9B,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAC5B,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAC5C,CAAC;gBACJ,CAAC;gBAED,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAEO,WAAW,CACjB,GAAsB,EACtB,IAAmB,EACnB,OAAe,EACf,KAAc;YAEd,IAAI,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAChC,MAAM,KAAK,CAAC;YACd,CAAC;YAED,qEAAqE;YACrE,qEAAqE;YACrE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;YAChE,MAAM,OAAO,GAAG,SAAS,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;YAExD,gDAAgD;YAChD,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC9C,UAAU,CAAC,GAAG,EAAE;oBACd,IAAI,CAAC;wBACH,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;wBACpD,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;4BAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;wBAC/B,CAAC;6BAAM,CAAC;4BACN,OAAO,CAAC,MAAM,CAAC,CAAC;wBAClB,CAAC;oBACH,CAAC;oBAAC,OAAO,UAAU,EAAE,CAAC;wBACpB,MAAM,CAAC,UAAU,CAAC,CAAC;oBACrB,CAAC;gBACH,CAAC,EAAE,OAAO,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC;;;;SA3DU,gBAAgB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout-interceptor.d.ts","sourceRoot":"","sources":["../src/timeout-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"timeout-interceptor.d.ts","sourceRoot":"","sources":["../src/timeout-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAQ3E,2DAA2D;AAC3D,qBAAa,YAAa,SAAQ,KAAK;gBACzB,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAIhD;AAED;;;;;;GAMG;AACH,qBACa,kBAAmB,YAAW,iBAAiB;IAC1D,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO;CAuB3C"}
|
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
4
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
5
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
6
|
+
var _, done = false;
|
|
7
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
8
|
+
var context = {};
|
|
9
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
10
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
11
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
12
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
13
|
+
if (kind === "accessor") {
|
|
14
|
+
if (result === void 0) continue;
|
|
15
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
16
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
17
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
18
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
19
|
+
}
|
|
20
|
+
else if (_ = accept(result)) {
|
|
21
|
+
if (kind === "field") initializers.unshift(_);
|
|
22
|
+
else descriptor[key] = _;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
|
+
done = true;
|
|
27
|
+
};
|
|
28
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
+
var useValue = arguments.length > 2;
|
|
30
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
+
}
|
|
33
|
+
return useValue ? value : void 0;
|
|
34
|
+
};
|
|
35
|
+
import { Singleton } from '@goodie-ts/decorators';
|
|
1
36
|
/** Error thrown when a method call exceeds its timeout. */
|
|
2
37
|
export class TimeoutError extends Error {
|
|
3
38
|
constructor(methodKey, duration) {
|
|
@@ -12,22 +47,38 @@ export class TimeoutError extends Error {
|
|
|
12
47
|
* For sync methods, the timeout cannot be enforced (sync code blocks the
|
|
13
48
|
* event loop), so the result is returned as-is.
|
|
14
49
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return Promise.race([result, timeoutPromise]).finally(() => clearTimeout(timeoutId));
|
|
50
|
+
let TimeoutInterceptor = (() => {
|
|
51
|
+
let _classDecorators = [Singleton()];
|
|
52
|
+
let _classDescriptor;
|
|
53
|
+
let _classExtraInitializers = [];
|
|
54
|
+
let _classThis;
|
|
55
|
+
var TimeoutInterceptor = class {
|
|
56
|
+
static { _classThis = this; }
|
|
57
|
+
static {
|
|
58
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
59
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
60
|
+
TimeoutInterceptor = _classThis = _classDescriptor.value;
|
|
61
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
62
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
29
63
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
64
|
+
intercept(ctx) {
|
|
65
|
+
const meta = ctx.metadata;
|
|
66
|
+
if (!meta)
|
|
67
|
+
return ctx.proceed();
|
|
68
|
+
const key = `${ctx.className}:${ctx.methodName}`;
|
|
69
|
+
const result = ctx.proceed();
|
|
70
|
+
// Timeout only applies to async methods (sync methods can't be interrupted)
|
|
71
|
+
if (result instanceof Promise) {
|
|
72
|
+
let timeoutId;
|
|
73
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
74
|
+
timeoutId = setTimeout(() => reject(new TimeoutError(key, meta.duration)), meta.duration);
|
|
75
|
+
});
|
|
76
|
+
return Promise.race([result, timeoutPromise]).finally(() => clearTimeout(timeoutId));
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
return TimeoutInterceptor = _classThis;
|
|
82
|
+
})();
|
|
83
|
+
export { TimeoutInterceptor };
|
|
33
84
|
//# sourceMappingURL=timeout-interceptor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout-interceptor.js","sourceRoot":"","sources":["../src/timeout-interceptor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"timeout-interceptor.js","sourceRoot":"","sources":["../src/timeout-interceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAOlD,2DAA2D;AAC3D,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,SAAiB,EAAE,QAAgB;QAC7C,KAAK,CAAC,UAAU,SAAS,oBAAoB,QAAQ,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;;;;;GAMG;IAEU,kBAAkB;4BAD9B,SAAS,EAAE;;;;;;;;YACZ,6KAwBC;;;YAxBY,uDAAkB;;QAC7B,SAAS,CAAC,GAAsB;YAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAuC,CAAC;YACzD,IAAI,CAAC,IAAI;gBAAE,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC;YAEhC,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,SAAS,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACjD,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;YAE7B,4EAA4E;YAC5E,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;gBAC9B,IAAI,SAAwC,CAAC;gBAC7C,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;oBACtD,SAAS,GAAG,UAAU,CACpB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,EAClD,IAAI,CAAC,QAAQ,CACd,CAAC;gBACJ,CAAC,CAAC,CAAC;gBACH,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CACzD,YAAY,CAAC,SAAS,CAAC,CACxB,CAAC;YACJ,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;;;;SAvBU,kBAAkB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodie-ts/resilience",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Resilience patterns for goodie-ts — @Retryable, @CircuitBreaker, @Timeout decorators",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
|
+
"goodie": {
|
|
16
|
+
"beans": "dist/beans.json"
|
|
17
|
+
},
|
|
15
18
|
"main": "dist/index.js",
|
|
16
19
|
"types": "dist/index.d.ts",
|
|
17
20
|
"exports": {
|
|
@@ -21,21 +24,20 @@
|
|
|
21
24
|
}
|
|
22
25
|
},
|
|
23
26
|
"dependencies": {
|
|
24
|
-
"@goodie-ts/aop": "0.
|
|
25
|
-
|
|
26
|
-
"peerDependencies": {
|
|
27
|
-
"@goodie-ts/transformer": "0.5.1"
|
|
27
|
+
"@goodie-ts/aop": "^0.6.0",
|
|
28
|
+
"@goodie-ts/decorators": "^0.5.2"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
30
31
|
"@types/node": "^22.0.0",
|
|
31
32
|
"ts-morph": "^24.0.0",
|
|
32
|
-
"@goodie-ts/
|
|
33
|
+
"@goodie-ts/cli": "0.6.0",
|
|
34
|
+
"@goodie-ts/transformer": "0.6.0"
|
|
33
35
|
},
|
|
34
36
|
"files": [
|
|
35
37
|
"dist"
|
|
36
38
|
],
|
|
37
39
|
"scripts": {
|
|
38
|
-
"build": "tsc",
|
|
40
|
+
"build": "tsc && goodie generate --mode library",
|
|
39
41
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { TransformerPlugin } from '@goodie-ts/transformer';
|
|
2
|
-
/**
|
|
3
|
-
* Create the resilience transformer plugin.
|
|
4
|
-
*
|
|
5
|
-
* Scans @Retryable, @CircuitBreaker, and @Timeout decorators on methods
|
|
6
|
-
* and wires the appropriate interceptors as AOP dependencies at compile time.
|
|
7
|
-
*/
|
|
8
|
-
export declare function createResiliencePlugin(): TransformerPlugin;
|
|
9
|
-
//# sourceMappingURL=resilience-transformer-plugin.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resilience-transformer-plugin.d.ts","sourceRoot":"","sources":["../src/resilience-transformer-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AA4BhC;;;;;GAKG;AACH,wBAAgB,sBAAsB,IAAI,iBAAiB,CAiJ1D"}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
const DECORATOR_MAP = {
|
|
2
|
-
Retryable: 'retry',
|
|
3
|
-
CircuitBreaker: 'circuitBreaker',
|
|
4
|
-
Timeout: 'timeout',
|
|
5
|
-
};
|
|
6
|
-
/** Order values — outermost (lowest) runs first: Timeout → CircuitBreaker → Retry */
|
|
7
|
-
const ORDER_MAP = {
|
|
8
|
-
timeout: -30, // Outermost — enforces deadline
|
|
9
|
-
circuitBreaker: -20, // Middle — rejects if circuit open
|
|
10
|
-
retry: -10, // Innermost — retries close to the method
|
|
11
|
-
};
|
|
12
|
-
const INTERCEPTOR_CLASS_MAP = {
|
|
13
|
-
retry: 'RetryInterceptor',
|
|
14
|
-
circuitBreaker: 'CircuitBreakerInterceptor',
|
|
15
|
-
timeout: 'TimeoutInterceptor',
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* Create the resilience transformer plugin.
|
|
19
|
-
*
|
|
20
|
-
* Scans @Retryable, @CircuitBreaker, and @Timeout decorators on methods
|
|
21
|
-
* and wires the appropriate interceptors as AOP dependencies at compile time.
|
|
22
|
-
*/
|
|
23
|
-
export function createResiliencePlugin() {
|
|
24
|
-
const classResilienceInfo = new Map();
|
|
25
|
-
return {
|
|
26
|
-
name: 'resilience',
|
|
27
|
-
beforeScan() {
|
|
28
|
-
classResilienceInfo.clear();
|
|
29
|
-
},
|
|
30
|
-
visitMethod(ctx) {
|
|
31
|
-
const decorators = ctx.methodDeclaration.getDecorators();
|
|
32
|
-
for (const decorator of decorators) {
|
|
33
|
-
const decoratorName = decorator.getName();
|
|
34
|
-
const kind = DECORATOR_MAP[decoratorName];
|
|
35
|
-
if (!kind)
|
|
36
|
-
continue;
|
|
37
|
-
const args = decorator.getArguments();
|
|
38
|
-
const metadata = parseMetadata(kind, args);
|
|
39
|
-
const key = `${ctx.filePath}:${ctx.className}`;
|
|
40
|
-
const existing = classResilienceInfo.get(key) ?? [];
|
|
41
|
-
existing.push({
|
|
42
|
-
methodName: ctx.methodName,
|
|
43
|
-
kind,
|
|
44
|
-
metadata,
|
|
45
|
-
});
|
|
46
|
-
classResilienceInfo.set(key, existing);
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
afterResolve(beans) {
|
|
50
|
-
const usedInterceptors = new Set();
|
|
51
|
-
for (const bean of beans) {
|
|
52
|
-
const className = bean.tokenRef.kind === 'class' ? bean.tokenRef.className : undefined;
|
|
53
|
-
if (!className)
|
|
54
|
-
continue;
|
|
55
|
-
const key = `${bean.tokenRef.importPath}:${className}`;
|
|
56
|
-
const infos = classResilienceInfo.get(key);
|
|
57
|
-
if (!infos || infos.length === 0)
|
|
58
|
-
continue;
|
|
59
|
-
const existing = (bean.metadata.interceptedMethods ?? []);
|
|
60
|
-
for (const info of infos) {
|
|
61
|
-
const interceptorClassName = INTERCEPTOR_CLASS_MAP[info.kind];
|
|
62
|
-
usedInterceptors.add(interceptorClassName);
|
|
63
|
-
const methodEntry = existing.find((m) => m.methodName === info.methodName);
|
|
64
|
-
const interceptorRef = {
|
|
65
|
-
className: interceptorClassName,
|
|
66
|
-
importPath: '@goodie-ts/resilience',
|
|
67
|
-
adviceType: 'around',
|
|
68
|
-
order: ORDER_MAP[info.kind],
|
|
69
|
-
metadata: info.metadata,
|
|
70
|
-
};
|
|
71
|
-
if (methodEntry) {
|
|
72
|
-
methodEntry.interceptors.push(interceptorRef);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
existing.push({
|
|
76
|
-
methodName: info.methodName,
|
|
77
|
-
interceptors: [interceptorRef],
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
bean.metadata.interceptedMethods = existing;
|
|
82
|
-
}
|
|
83
|
-
if (usedInterceptors.size === 0)
|
|
84
|
-
return beans;
|
|
85
|
-
// Add synthetic beans for each used interceptor
|
|
86
|
-
const syntheticBeans = [];
|
|
87
|
-
for (const interceptorClassName of usedInterceptors) {
|
|
88
|
-
syntheticBeans.push({
|
|
89
|
-
tokenRef: {
|
|
90
|
-
kind: 'class',
|
|
91
|
-
className: interceptorClassName,
|
|
92
|
-
importPath: '@goodie-ts/resilience',
|
|
93
|
-
},
|
|
94
|
-
scope: 'singleton',
|
|
95
|
-
eager: false,
|
|
96
|
-
name: undefined,
|
|
97
|
-
constructorDeps: [],
|
|
98
|
-
fieldDeps: [],
|
|
99
|
-
factoryKind: 'constructor',
|
|
100
|
-
providesSource: undefined,
|
|
101
|
-
metadata: {},
|
|
102
|
-
sourceLocation: {
|
|
103
|
-
filePath: '@goodie-ts/resilience',
|
|
104
|
-
line: 0,
|
|
105
|
-
column: 0,
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
return [...beans, ...syntheticBeans];
|
|
110
|
-
},
|
|
111
|
-
codegen(beans) {
|
|
112
|
-
const usedClasses = new Set();
|
|
113
|
-
for (const bean of beans) {
|
|
114
|
-
const methods = bean.metadata.interceptedMethods;
|
|
115
|
-
if (!methods)
|
|
116
|
-
continue;
|
|
117
|
-
for (const m of methods) {
|
|
118
|
-
for (const i of m.interceptors) {
|
|
119
|
-
if (i.importPath === '@goodie-ts/resilience') {
|
|
120
|
-
usedClasses.add(i.className);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (usedClasses.size === 0)
|
|
126
|
-
return {};
|
|
127
|
-
const classNames = [...usedClasses].sort().join(', ');
|
|
128
|
-
return {
|
|
129
|
-
imports: [
|
|
130
|
-
`import { ${classNames} } from '@goodie-ts/resilience'`,
|
|
131
|
-
"import { buildInterceptorChain } from '@goodie-ts/aop'",
|
|
132
|
-
],
|
|
133
|
-
};
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
/** Strip TypeScript numeric separators: 1_000 → 1000 */
|
|
138
|
-
function stripSeparators(s) {
|
|
139
|
-
return s.replace(/_/g, '');
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Parse decorator arguments into metadata.
|
|
143
|
-
*
|
|
144
|
-
* **Limitation:** Only literal numeric values are supported. Const references
|
|
145
|
-
* or computed expressions (e.g. `maxAttempts: MAX_RETRIES`) will fall back to
|
|
146
|
-
* defaults silently. This matches the compile-time AST text parsing approach
|
|
147
|
-
* used by all goodie-ts transformer plugins.
|
|
148
|
-
*/
|
|
149
|
-
function parseMetadata(kind, args) {
|
|
150
|
-
switch (kind) {
|
|
151
|
-
case 'retry': {
|
|
152
|
-
const defaults = { maxAttempts: 3, delay: 1000, multiplier: 1 };
|
|
153
|
-
if (args.length === 0)
|
|
154
|
-
return defaults;
|
|
155
|
-
const text = args[0].getText();
|
|
156
|
-
const maxMatch = text.match(/maxAttempts\s*:\s*(\d[\d_]*)/);
|
|
157
|
-
const delayMatch = text.match(/delay\s*:\s*(\d[\d_]*)/);
|
|
158
|
-
const multMatch = text.match(/multiplier\s*:\s*([\d_]+(?:\.[\d_]+)?)/);
|
|
159
|
-
return {
|
|
160
|
-
maxAttempts: maxMatch
|
|
161
|
-
? Number.parseInt(stripSeparators(maxMatch[1]), 10)
|
|
162
|
-
: defaults.maxAttempts,
|
|
163
|
-
delay: delayMatch
|
|
164
|
-
? Number.parseInt(stripSeparators(delayMatch[1]), 10)
|
|
165
|
-
: defaults.delay,
|
|
166
|
-
multiplier: multMatch
|
|
167
|
-
? Number.parseFloat(stripSeparators(multMatch[1]))
|
|
168
|
-
: defaults.multiplier,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
case 'circuitBreaker': {
|
|
172
|
-
const defaults = {
|
|
173
|
-
failureThreshold: 5,
|
|
174
|
-
resetTimeout: 30000,
|
|
175
|
-
halfOpenAttempts: 1,
|
|
176
|
-
};
|
|
177
|
-
if (args.length === 0)
|
|
178
|
-
return defaults;
|
|
179
|
-
const text = args[0].getText();
|
|
180
|
-
const threshMatch = text.match(/failureThreshold\s*:\s*(\d[\d_]*)/);
|
|
181
|
-
const resetMatch = text.match(/resetTimeout\s*:\s*(\d[\d_]*)/);
|
|
182
|
-
const halfMatch = text.match(/halfOpenAttempts\s*:\s*(\d[\d_]*)/);
|
|
183
|
-
return {
|
|
184
|
-
failureThreshold: threshMatch
|
|
185
|
-
? Number.parseInt(stripSeparators(threshMatch[1]), 10)
|
|
186
|
-
: defaults.failureThreshold,
|
|
187
|
-
resetTimeout: resetMatch
|
|
188
|
-
? Number.parseInt(stripSeparators(resetMatch[1]), 10)
|
|
189
|
-
: defaults.resetTimeout,
|
|
190
|
-
halfOpenAttempts: halfMatch
|
|
191
|
-
? Number.parseInt(stripSeparators(halfMatch[1]), 10)
|
|
192
|
-
: defaults.halfOpenAttempts,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
case 'timeout': {
|
|
196
|
-
if (args.length === 0)
|
|
197
|
-
return { duration: 5000 };
|
|
198
|
-
const text = stripSeparators(args[0].getText());
|
|
199
|
-
const num = Number.parseInt(text, 10);
|
|
200
|
-
if (Number.isNaN(num)) {
|
|
201
|
-
if (text.includes('{')) {
|
|
202
|
-
console.warn(`[resilience] @Timeout received an object literal argument (${text}). ` +
|
|
203
|
-
'Only numeric durations are supported — falling back to 5000ms.');
|
|
204
|
-
}
|
|
205
|
-
return { duration: 5000 };
|
|
206
|
-
}
|
|
207
|
-
return { duration: num };
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
//# sourceMappingURL=resilience-transformer-plugin.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resilience-transformer-plugin.js","sourceRoot":"","sources":["../src/resilience-transformer-plugin.ts"],"names":[],"mappings":"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"}
|