flowshield 1.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.
Files changed (76) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +272 -0
  4. package/dist/cjs/compose.js +53 -0
  5. package/dist/cjs/compose.js.map +1 -0
  6. package/dist/cjs/index.js +32 -0
  7. package/dist/cjs/index.js.map +1 -0
  8. package/dist/cjs/policies/bulkhead.js +75 -0
  9. package/dist/cjs/policies/bulkhead.js.map +1 -0
  10. package/dist/cjs/policies/cache.js +99 -0
  11. package/dist/cjs/policies/cache.js.map +1 -0
  12. package/dist/cjs/policies/circuit-breaker.js +110 -0
  13. package/dist/cjs/policies/circuit-breaker.js.map +1 -0
  14. package/dist/cjs/policies/fallback.js +33 -0
  15. package/dist/cjs/policies/fallback.js.map +1 -0
  16. package/dist/cjs/policies/hedge.js +74 -0
  17. package/dist/cjs/policies/hedge.js.map +1 -0
  18. package/dist/cjs/policies/rate-limiter.js +92 -0
  19. package/dist/cjs/policies/rate-limiter.js.map +1 -0
  20. package/dist/cjs/policies/retry.js +61 -0
  21. package/dist/cjs/policies/retry.js.map +1 -0
  22. package/dist/cjs/policies/timeout.js +45 -0
  23. package/dist/cjs/policies/timeout.js.map +1 -0
  24. package/dist/cjs/types.js +55 -0
  25. package/dist/cjs/types.js.map +1 -0
  26. package/dist/cjs/utils.js +84 -0
  27. package/dist/cjs/utils.js.map +1 -0
  28. package/dist/esm/compose.js +49 -0
  29. package/dist/esm/compose.js.map +1 -0
  30. package/dist/esm/index.js +13 -0
  31. package/dist/esm/index.js.map +1 -0
  32. package/dist/esm/policies/bulkhead.js +72 -0
  33. package/dist/esm/policies/bulkhead.js.map +1 -0
  34. package/dist/esm/policies/cache.js +96 -0
  35. package/dist/esm/policies/cache.js.map +1 -0
  36. package/dist/esm/policies/circuit-breaker.js +107 -0
  37. package/dist/esm/policies/circuit-breaker.js.map +1 -0
  38. package/dist/esm/policies/fallback.js +30 -0
  39. package/dist/esm/policies/fallback.js.map +1 -0
  40. package/dist/esm/policies/hedge.js +71 -0
  41. package/dist/esm/policies/hedge.js.map +1 -0
  42. package/dist/esm/policies/rate-limiter.js +89 -0
  43. package/dist/esm/policies/rate-limiter.js.map +1 -0
  44. package/dist/esm/policies/retry.js +58 -0
  45. package/dist/esm/policies/retry.js.map +1 -0
  46. package/dist/esm/policies/timeout.js +42 -0
  47. package/dist/esm/policies/timeout.js.map +1 -0
  48. package/dist/esm/types.js +46 -0
  49. package/dist/esm/types.js.map +1 -0
  50. package/dist/esm/utils.js +77 -0
  51. package/dist/esm/utils.js.map +1 -0
  52. package/dist/types/compose.d.ts +32 -0
  53. package/dist/types/compose.d.ts.map +1 -0
  54. package/dist/types/index.d.ts +12 -0
  55. package/dist/types/index.d.ts.map +1 -0
  56. package/dist/types/policies/bulkhead.d.ts +20 -0
  57. package/dist/types/policies/bulkhead.d.ts.map +1 -0
  58. package/dist/types/policies/cache.d.ts +19 -0
  59. package/dist/types/policies/cache.d.ts.map +1 -0
  60. package/dist/types/policies/circuit-breaker.d.ts +19 -0
  61. package/dist/types/policies/circuit-breaker.d.ts.map +1 -0
  62. package/dist/types/policies/fallback.d.ts +14 -0
  63. package/dist/types/policies/fallback.d.ts.map +1 -0
  64. package/dist/types/policies/hedge.d.ts +15 -0
  65. package/dist/types/policies/hedge.d.ts.map +1 -0
  66. package/dist/types/policies/rate-limiter.d.ts +19 -0
  67. package/dist/types/policies/rate-limiter.d.ts.map +1 -0
  68. package/dist/types/policies/retry.d.ts +14 -0
  69. package/dist/types/policies/retry.d.ts.map +1 -0
  70. package/dist/types/policies/timeout.d.ts +15 -0
  71. package/dist/types/policies/timeout.d.ts.map +1 -0
  72. package/dist/types/types.d.ts +133 -0
  73. package/dist/types/types.d.ts.map +1 -0
  74. package/dist/types/utils.d.ts +21 -0
  75. package/dist/types/utils.d.ts.map +1 -0
  76. package/package.json +80 -0
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.circuitBreaker = circuitBreaker;
4
+ const types_js_1 = require("../types.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ const DEFAULTS = {
7
+ failureThreshold: 5,
8
+ successThreshold: 1,
9
+ resetTimeout: 30_000,
10
+ };
11
+ /**
12
+ * Creates a circuit breaker policy that prevents calls to a failing service.
13
+ *
14
+ * Returns an object with `execute` (the policy function) and `handle`
15
+ * (an interface to inspect / reset the breaker state).
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const cb = circuitBreaker({ failureThreshold: 3, resetTimeout: 10_000 });
20
+ * const result = await cb.execute(() => fetch('/api/health'));
21
+ * console.log(cb.handle.state); // 'closed'
22
+ * ```
23
+ */
24
+ function circuitBreaker(options = {}) {
25
+ const failureThreshold = options.failureThreshold ?? DEFAULTS.failureThreshold;
26
+ const successThreshold = options.successThreshold ?? DEFAULTS.successThreshold;
27
+ const resetTimeout = options.resetTimeout ?? DEFAULTS.resetTimeout;
28
+ (0, utils_js_1.assertPositiveInteger)(failureThreshold, 'failureThreshold');
29
+ (0, utils_js_1.assertPositiveInteger)(successThreshold, 'successThreshold');
30
+ (0, utils_js_1.assertPositive)(resetTimeout, 'resetTimeout');
31
+ let state = 'closed';
32
+ let failureCount = 0;
33
+ let successCount = 0;
34
+ let nextAttemptTime = 0;
35
+ function trip() {
36
+ state = 'open';
37
+ nextAttemptTime = Date.now() + resetTimeout;
38
+ options.onOpen?.();
39
+ }
40
+ function resetState() {
41
+ state = 'closed';
42
+ failureCount = 0;
43
+ successCount = 0;
44
+ options.onClose?.();
45
+ }
46
+ function tryTransitionToHalfOpen() {
47
+ if (state === 'open' && Date.now() >= nextAttemptTime) {
48
+ state = 'half-open';
49
+ successCount = 0;
50
+ options.onHalfOpen?.();
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ const handle = {
56
+ get state() {
57
+ // Auto-transition if timer has elapsed
58
+ if (state === 'open' && Date.now() >= nextAttemptTime) {
59
+ state = 'half-open';
60
+ successCount = 0;
61
+ options.onHalfOpen?.();
62
+ }
63
+ return state;
64
+ },
65
+ get failureCount() {
66
+ return failureCount;
67
+ },
68
+ get successCount() {
69
+ return successCount;
70
+ },
71
+ reset() {
72
+ resetState();
73
+ },
74
+ };
75
+ async function execute(fn) {
76
+ if (state === 'open') {
77
+ if (!tryTransitionToHalfOpen()) {
78
+ throw new types_js_1.CircuitOpenError();
79
+ }
80
+ }
81
+ try {
82
+ const result = await fn();
83
+ // Success handling
84
+ if (state === 'half-open') {
85
+ successCount++;
86
+ if (successCount >= successThreshold) {
87
+ resetState();
88
+ }
89
+ }
90
+ else {
91
+ // In closed state, reset failure count on success
92
+ failureCount = 0;
93
+ }
94
+ return result;
95
+ }
96
+ catch (error) {
97
+ failureCount++;
98
+ if (state === 'half-open') {
99
+ // Any failure in half-open trips back to open
100
+ trip();
101
+ }
102
+ else if (failureCount >= failureThreshold) {
103
+ trip();
104
+ }
105
+ throw error;
106
+ }
107
+ }
108
+ return { execute, handle };
109
+ }
110
+ //# sourceMappingURL=circuit-breaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/policies/circuit-breaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fallback = fallback;
4
+ /**
5
+ * Creates a fallback policy that returns a fallback value (or the result
6
+ * of a fallback function) when the wrapped operation fails.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const result = await fallback({ fallback: [] })(
11
+ * () => fetch('/api/items').then(r => r.json()),
12
+ * );
13
+ * ```
14
+ */
15
+ function fallback(options) {
16
+ const shouldFallback = options.shouldFallback ?? (() => true);
17
+ return async (fn) => {
18
+ try {
19
+ return await fn();
20
+ }
21
+ catch (error) {
22
+ if (!shouldFallback(error)) {
23
+ throw error;
24
+ }
25
+ const fb = options.fallback;
26
+ if (typeof fb === 'function') {
27
+ return await fb(error);
28
+ }
29
+ return fb;
30
+ }
31
+ };
32
+ }
33
+ //# sourceMappingURL=fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../../src/policies/fallback.ts"],"names":[],"mappings":";;AAaA,4BAkBC;AA7BD;;;;;;;;;;GAUG;AACH,SAAgB,QAAQ,CAAI,OAA2B;IACrD,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAE9D,OAAO,KAAK,EAAK,EAAoB,EAAkB,EAAE;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,KAAK,CAAC;YACd,CAAC;YAED,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;YAC5B,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;gBAC7B,OAAO,MAAO,EAAyC,CAAC,KAAK,CAAC,CAAC;YACjE,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hedge = hedge;
4
+ const utils_js_1 = require("../utils.js");
5
+ const DEFAULTS = {
6
+ hedgeDelay: 2000,
7
+ };
8
+ /**
9
+ * Creates a hedge policy that sends a parallel (hedged) request if the
10
+ * primary request doesn't resolve within the configured delay.
11
+ * The first request to resolve wins; the other's result is discarded.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const result = await hedge({ hedgeDelay: 1000 })(
16
+ * () => fetch('/api/data').then(r => r.json()),
17
+ * );
18
+ * ```
19
+ */
20
+ function hedge(options = {}) {
21
+ const hedgeDelay = options.hedgeDelay ?? DEFAULTS.hedgeDelay;
22
+ (0, utils_js_1.assertPositive)(hedgeDelay, 'hedgeDelay');
23
+ return async (fn) => {
24
+ return new Promise((resolve, reject) => {
25
+ let settled = false;
26
+ let primaryDone = false;
27
+ let hedgeDone = false;
28
+ let primaryError;
29
+ let hedgeLaunched = false;
30
+ const trySettle = (value) => {
31
+ if (!settled) {
32
+ settled = true;
33
+ clearTimeout(hedgeTimer);
34
+ resolve(value);
35
+ }
36
+ };
37
+ const tryRejectBoth = () => {
38
+ if (primaryDone && hedgeDone && !settled) {
39
+ settled = true;
40
+ reject(primaryError);
41
+ }
42
+ };
43
+ const doLaunchHedge = () => {
44
+ hedgeLaunched = true;
45
+ const hedgeRequest = fn();
46
+ hedgeRequest.then(trySettle, (_hedgeErr) => {
47
+ hedgeDone = true;
48
+ if (!primaryDone && !settled) {
49
+ return;
50
+ }
51
+ tryRejectBoth();
52
+ });
53
+ };
54
+ // Primary request
55
+ const primary = fn();
56
+ primary.then(trySettle, (err) => {
57
+ primaryDone = true;
58
+ primaryError = err;
59
+ if (!hedgeLaunched) {
60
+ clearTimeout(hedgeTimer);
61
+ doLaunchHedge();
62
+ }
63
+ tryRejectBoth();
64
+ });
65
+ // Schedule hedged request
66
+ const hedgeTimer = setTimeout(() => {
67
+ if (!settled) {
68
+ doLaunchHedge();
69
+ }
70
+ }, hedgeDelay);
71
+ });
72
+ };
73
+ }
74
+ //# sourceMappingURL=hedge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hedge.js","sourceRoot":"","sources":["../../../src/policies/hedge.ts"],"names":[],"mappings":";;AAmBA,sBA8DC;AAhFD,0CAA6C;AAE7C,MAAM,QAAQ,GAAG;IACf,UAAU,EAAE,IAAI;CACjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,SAAgB,KAAK,CAAC,UAAwB,EAAE;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC;IAE7D,IAAA,yBAAc,EAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAEzC,OAAO,KAAK,EAAK,EAAoB,EAAc,EAAE;QACnD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,IAAI,OAAO,GAAG,KAAK,CAAC;YACpB,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,YAAqB,CAAC;YAC1B,IAAI,aAAa,GAAG,KAAK,CAAC;YAE1B,MAAM,SAAS,GAAG,CAAC,KAAQ,EAAE,EAAE;gBAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,YAAY,CAAC,UAAU,CAAC,CAAC;oBACzB,OAAO,CAAC,KAAK,CAAC,CAAC;gBACjB,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,aAAa,GAAG,GAAG,EAAE;gBACzB,IAAI,WAAW,IAAI,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;oBACzC,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM,CAAC,YAAY,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC,CAAC;YAEF,MAAM,aAAa,GAAG,GAAG,EAAE;gBACzB,aAAa,GAAG,IAAI,CAAC;gBACrB,MAAM,YAAY,GAAG,EAAE,EAAE,CAAC;gBAC1B,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,SAAkB,EAAE,EAAE;oBAClD,SAAS,GAAG,IAAI,CAAC;oBACjB,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC;wBAC7B,OAAO;oBACT,CAAC;oBACD,aAAa,EAAE,CAAC;gBAClB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC;YAEF,kBAAkB;YAClB,MAAM,OAAO,GAAG,EAAE,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,GAAY,EAAE,EAAE;gBACvC,WAAW,GAAG,IAAI,CAAC;gBACnB,YAAY,GAAG,GAAG,CAAC;gBAEnB,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,YAAY,CAAC,UAAU,CAAC,CAAC;oBACzB,aAAa,EAAE,CAAC;gBAClB,CAAC;gBAED,aAAa,EAAE,CAAC;YAClB,CAAC,CAAC,CAAC;YAEH,0BAA0B;YAC1B,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE;gBACjC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,aAAa,EAAE,CAAC;gBAClB,CAAC;YACH,CAAC,EAAE,UAAU,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rateLimiter = rateLimiter;
4
+ const types_js_1 = require("../types.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ const DEFAULTS = {
7
+ tokensPerInterval: 10,
8
+ interval: 1000,
9
+ rejectOnLimit: false,
10
+ };
11
+ /**
12
+ * Creates a token-bucket rate limiter policy that limits how many times
13
+ * the wrapped operation can be executed within a time interval.
14
+ *
15
+ * Returns an object with `execute` (the policy function) and `handle`
16
+ * (an interface to inspect / reset the bucket).
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const rl = rateLimiter({ tokensPerInterval: 5, interval: 1000 });
21
+ * const result = await rl.execute(() => fetch('/api/data'));
22
+ * ```
23
+ */
24
+ function rateLimiter(options = {}) {
25
+ const tokensPerInterval = options.tokensPerInterval ?? DEFAULTS.tokensPerInterval;
26
+ const interval = options.interval ?? DEFAULTS.interval;
27
+ const rejectOnLimit = options.rejectOnLimit ?? DEFAULTS.rejectOnLimit;
28
+ (0, utils_js_1.assertPositiveInteger)(tokensPerInterval, 'tokensPerInterval');
29
+ (0, utils_js_1.assertPositive)(interval, 'interval');
30
+ let tokens = tokensPerInterval;
31
+ let lastRefill = Date.now();
32
+ const waiters = [];
33
+ function refill() {
34
+ const now = Date.now();
35
+ const elapsed = now - lastRefill;
36
+ const newTokens = Math.floor((elapsed / interval) * tokensPerInterval);
37
+ if (newTokens > 0) {
38
+ tokens = Math.min(tokensPerInterval, tokens + newTokens);
39
+ lastRefill = now;
40
+ }
41
+ }
42
+ function releaseWaiters() {
43
+ while (tokens > 0 && waiters.length > 0) {
44
+ tokens--;
45
+ const waiter = waiters.shift();
46
+ waiter.resolve();
47
+ }
48
+ }
49
+ // Refill timer — only active when there are waiters
50
+ let refillTimer = null;
51
+ function startRefillTimer() {
52
+ if (refillTimer !== null)
53
+ return;
54
+ refillTimer = setInterval(() => {
55
+ refill();
56
+ releaseWaiters();
57
+ if (waiters.length === 0 && refillTimer !== null) {
58
+ clearInterval(refillTimer);
59
+ refillTimer = null;
60
+ }
61
+ }, Math.min(interval, 100));
62
+ }
63
+ const handle = {
64
+ get availableTokens() {
65
+ refill();
66
+ return tokens;
67
+ },
68
+ reset() {
69
+ tokens = tokensPerInterval;
70
+ lastRefill = Date.now();
71
+ releaseWaiters();
72
+ },
73
+ };
74
+ async function execute(fn) {
75
+ refill();
76
+ if (tokens > 0) {
77
+ tokens--;
78
+ return fn();
79
+ }
80
+ if (rejectOnLimit) {
81
+ throw new types_js_1.RateLimitExceededError();
82
+ }
83
+ // Queue and wait for a token
84
+ await new Promise((resolve) => {
85
+ waiters.push({ resolve });
86
+ startRefillTimer();
87
+ });
88
+ return fn();
89
+ }
90
+ return { execute, handle };
91
+ }
92
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/policies/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.retry = retry;
4
+ const types_js_1 = require("../types.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ const DEFAULTS = {
7
+ maxAttempts: 3,
8
+ delay: 200,
9
+ backoff: 'exponential',
10
+ maxDelay: 30_000,
11
+ };
12
+ /**
13
+ * Creates a retry policy that re-executes the wrapped operation on failure
14
+ * using the configured backoff strategy.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * const result = await retry({ maxAttempts: 5, backoff: 'exponential' })(
19
+ * () => fetch('/api/data'),
20
+ * );
21
+ * ```
22
+ */
23
+ function retry(options = {}) {
24
+ const maxAttempts = options.maxAttempts ?? DEFAULTS.maxAttempts;
25
+ const baseDelay = options.delay ?? DEFAULTS.delay;
26
+ const backoff = options.backoff ?? DEFAULTS.backoff;
27
+ const maxDelay = options.maxDelay ?? DEFAULTS.maxDelay;
28
+ const shouldRetry = options.shouldRetry ?? (() => true);
29
+ const onRetry = options.onRetry;
30
+ const signal = options.signal;
31
+ (0, utils_js_1.assertPositiveInteger)(maxAttempts, 'maxAttempts');
32
+ (0, utils_js_1.assertPositive)(baseDelay, 'delay');
33
+ (0, utils_js_1.assertPositive)(maxDelay, 'maxDelay');
34
+ return async (fn) => {
35
+ let lastError;
36
+ let previousDelay = baseDelay;
37
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
38
+ if (signal?.aborted) {
39
+ throw signal.reason;
40
+ }
41
+ try {
42
+ return await fn();
43
+ }
44
+ catch (error) {
45
+ lastError = error;
46
+ if (attempt === maxAttempts) {
47
+ break;
48
+ }
49
+ if (!shouldRetry(error, attempt)) {
50
+ break;
51
+ }
52
+ const delayMs = (0, utils_js_1.calculateDelay)(attempt, baseDelay, backoff, maxDelay, previousDelay);
53
+ previousDelay = delayMs;
54
+ onRetry?.(error, attempt, delayMs);
55
+ await (0, utils_js_1.sleep)(delayMs, signal);
56
+ }
57
+ }
58
+ throw new types_js_1.RetryExhaustedError(maxAttempts, lastError);
59
+ };
60
+ }
61
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/policies/retry.ts"],"names":[],"mappings":";;AAsBA,sBA8CC;AAnED,0CAAkD;AAClD,0CAA2F;AAE3F,MAAM,QAAQ,GAAG;IACf,WAAW,EAAE,CAAC;IACd,KAAK,EAAE,GAAG;IACV,OAAO,EAAE,aAAsB;IAC/B,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,SAAgB,KAAK,CAAC,UAAwB,EAAE;IAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC;IAChE,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC;IAClD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC;IACpD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC;IACvD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE9B,IAAA,gCAAqB,EAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAClD,IAAA,yBAAc,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACnC,IAAA,yBAAc,EAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAErC,OAAO,KAAK,EAAK,EAAoB,EAAc,EAAE;QACnD,IAAI,SAAkB,CAAC;QACvB,IAAI,aAAa,GAAG,SAAS,CAAC;QAE9B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACxD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,MAAM,CAAC,MAAM,CAAC;YACtB,CAAC;YAED,IAAI,CAAC;gBACH,OAAO,MAAM,EAAE,EAAE,CAAC;YACpB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,GAAG,KAAK,CAAC;gBAElB,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;oBAC5B,MAAM;gBACR,CAAC;gBAED,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;oBACjC,MAAM;gBACR,CAAC;gBAED,MAAM,OAAO,GAAG,IAAA,yBAAc,EAAC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;gBACrF,aAAa,GAAG,OAAO,CAAC;gBAExB,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAEnC,MAAM,IAAA,gBAAK,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,MAAM,IAAI,8BAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.timeout = timeout;
4
+ const types_js_1 = require("../types.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ /**
7
+ * Creates a timeout policy that rejects if the wrapped operation
8
+ * does not resolve within the specified deadline.
9
+ *
10
+ * Uses AbortController for cooperative cancellation when the
11
+ * wrapped function supports it.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const result = await timeout({ ms: 5000 })(() => fetch('/api/data'));
16
+ * ```
17
+ */
18
+ function timeout(options) {
19
+ (0, utils_js_1.assertPositive)(options.ms, 'ms');
20
+ return async (fn) => {
21
+ return new Promise((resolve, reject) => {
22
+ let settled = false;
23
+ const timer = setTimeout(() => {
24
+ if (!settled) {
25
+ settled = true;
26
+ reject(new types_js_1.TimeoutError(options.ms));
27
+ }
28
+ }, options.ms);
29
+ fn().then((value) => {
30
+ if (!settled) {
31
+ settled = true;
32
+ clearTimeout(timer);
33
+ resolve(value);
34
+ }
35
+ }, (error) => {
36
+ if (!settled) {
37
+ settled = true;
38
+ clearTimeout(timer);
39
+ reject(error);
40
+ }
41
+ });
42
+ });
43
+ };
44
+ }
45
+ //# sourceMappingURL=timeout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../../src/policies/timeout.ts"],"names":[],"mappings":";;AAgBA,0BAgCC;AA/CD,0CAA2C;AAC3C,0CAA6C;AAE7C;;;;;;;;;;;GAWG;AACH,SAAgB,OAAO,CAAC,OAAuB;IAC7C,IAAA,yBAAc,EAAC,OAAO,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAEjC,OAAO,KAAK,EAAK,EAAoB,EAAc,EAAE;QACnD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,IAAI,OAAO,GAAG,KAAK,CAAC;YAEpB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,MAAM,CAAC,IAAI,uBAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;YAEf,EAAE,EAAE,CAAC,IAAI,CACP,CAAC,KAAK,EAAE,EAAE;gBACR,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,KAAK,CAAC,CAAC;gBACjB,CAAC;YACH,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;gBACR,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,GAAG,IAAI,CAAC;oBACf,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,MAAM,CAAC,KAAK,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ // ─── Core Types ─────────────────────────────────────────────────────────────
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.RetryExhaustedError = exports.RateLimitExceededError = exports.BulkheadRejectedError = exports.CircuitOpenError = exports.TimeoutError = exports.FlowShieldError = void 0;
5
+ // ─── Errors ─────────────────────────────────────────────────────────────────
6
+ class FlowShieldError extends Error {
7
+ constructor(message, cause) {
8
+ super(message);
9
+ this.name = 'FlowShieldError';
10
+ // Fix prototype chain for instanceof checks
11
+ Object.setPrototypeOf(this, new.target.prototype);
12
+ if (cause !== undefined) {
13
+ this.cause = cause;
14
+ }
15
+ }
16
+ }
17
+ exports.FlowShieldError = FlowShieldError;
18
+ class TimeoutError extends FlowShieldError {
19
+ constructor(ms) {
20
+ super(`Operation timed out after ${ms}ms`);
21
+ this.name = 'TimeoutError';
22
+ }
23
+ }
24
+ exports.TimeoutError = TimeoutError;
25
+ class CircuitOpenError extends FlowShieldError {
26
+ constructor() {
27
+ super('Circuit breaker is open — request rejected');
28
+ this.name = 'CircuitOpenError';
29
+ }
30
+ }
31
+ exports.CircuitOpenError = CircuitOpenError;
32
+ class BulkheadRejectedError extends FlowShieldError {
33
+ constructor() {
34
+ super('Bulkhead capacity exceeded — request rejected');
35
+ this.name = 'BulkheadRejectedError';
36
+ }
37
+ }
38
+ exports.BulkheadRejectedError = BulkheadRejectedError;
39
+ class RateLimitExceededError extends FlowShieldError {
40
+ constructor() {
41
+ super('Rate limit exceeded — request rejected');
42
+ this.name = 'RateLimitExceededError';
43
+ }
44
+ }
45
+ exports.RateLimitExceededError = RateLimitExceededError;
46
+ class RetryExhaustedError extends FlowShieldError {
47
+ attempts;
48
+ constructor(attempts, lastError) {
49
+ super(`All ${attempts} retry attempts exhausted`, lastError);
50
+ this.name = 'RetryExhaustedError';
51
+ this.attempts = attempts;
52
+ }
53
+ }
54
+ exports.RetryExhaustedError = RetryExhaustedError;
55
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";AAAA,+EAA+E;;;AAkJ/E,+EAA+E;AAE/E,MAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,4CAA4C;QAC5C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACvB,IAA4B,CAAC,KAAK,GAAG,KAAK,CAAC;QAC9C,CAAC;IACH,CAAC;CACF;AAVD,0CAUC;AAED,MAAa,YAAa,SAAQ,eAAe;IAC/C,YAAY,EAAU;QACpB,KAAK,CAAC,6BAA6B,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AALD,oCAKC;AAED,MAAa,gBAAiB,SAAQ,eAAe;IACnD;QACE,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC;AAED,MAAa,qBAAsB,SAAQ,eAAe;IACxD;QACE,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AALD,sDAKC;AAED,MAAa,sBAAuB,SAAQ,eAAe;IACzD;QACE,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AALD,wDAKC;AAED,MAAa,mBAAoB,SAAQ,eAAe;IACtC,QAAQ,CAAS;IACjC,YAAY,QAAgB,EAAE,SAAkB;QAC9C,KAAK,CAAC,OAAO,QAAQ,2BAA2B,EAAE,SAAS,CAAC,CAAC;QAC7D,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAPD,kDAOC"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sleep = sleep;
4
+ exports.calculateDelay = calculateDelay;
5
+ exports.assertPositiveInteger = assertPositiveInteger;
6
+ exports.assertPositive = assertPositive;
7
+ exports.assertNonNegative = assertNonNegative;
8
+ /**
9
+ * Sleep for `ms` milliseconds. Supports cooperative cancellation via AbortSignal.
10
+ */
11
+ function sleep(ms, signal) {
12
+ return new Promise((resolve, reject) => {
13
+ if (signal?.aborted) {
14
+ reject(signal.reason);
15
+ return;
16
+ }
17
+ let done = false;
18
+ const timer = setTimeout(() => {
19
+ done = true;
20
+ if (signal) {
21
+ signal.removeEventListener('abort', onAbort);
22
+ }
23
+ resolve();
24
+ }, ms);
25
+ const onAbort = () => {
26
+ if (!done) {
27
+ done = true;
28
+ clearTimeout(timer);
29
+ reject(signal.reason);
30
+ }
31
+ };
32
+ if (signal) {
33
+ signal.addEventListener('abort', onAbort, { once: true });
34
+ }
35
+ });
36
+ }
37
+ /**
38
+ * Calculate the delay for a given attempt using the specified backoff strategy.
39
+ */
40
+ function calculateDelay(attempt, baseDelay, strategy, maxDelay, previousDelay) {
41
+ let delay;
42
+ switch (strategy) {
43
+ case 'constant':
44
+ delay = baseDelay;
45
+ break;
46
+ case 'linear':
47
+ delay = baseDelay * attempt;
48
+ break;
49
+ case 'exponential':
50
+ delay = baseDelay * Math.pow(2, attempt - 1);
51
+ break;
52
+ case 'decorrelatedJitter': {
53
+ const prev = previousDelay ?? baseDelay;
54
+ delay = Math.random() * (prev * 3 - baseDelay) + baseDelay;
55
+ break;
56
+ }
57
+ }
58
+ return Math.min(delay, maxDelay);
59
+ }
60
+ /**
61
+ * Validate that a number is a positive integer.
62
+ */
63
+ function assertPositiveInteger(value, name) {
64
+ if (!Number.isFinite(value) || value < 1 || Math.floor(value) !== value) {
65
+ throw new RangeError(`${name} must be a positive integer, got ${value}`);
66
+ }
67
+ }
68
+ /**
69
+ * Validate that a number is a positive finite number.
70
+ */
71
+ function assertPositive(value, name) {
72
+ if (!Number.isFinite(value) || value <= 0) {
73
+ throw new RangeError(`${name} must be a positive number, got ${value}`);
74
+ }
75
+ }
76
+ /**
77
+ * Validate that a number is a non-negative finite number.
78
+ */
79
+ function assertNonNegative(value, name) {
80
+ if (!Number.isFinite(value) || value < 0) {
81
+ throw new RangeError(`${name} must be a non-negative number, got ${value}`);
82
+ }
83
+ }
84
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Composes multiple policies into a single policy.
3
+ * Policies are applied **left-to-right** (outermost first).
4
+ *
5
+ * `pipe(a, b, c)(fn)` is equivalent to `a(()=> b(()=> c(fn)))`.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const resilient = pipe(
10
+ * timeout({ ms: 5000 }),
11
+ * retry({ maxAttempts: 3 }),
12
+ * );
13
+ * const result = await resilient(() => fetch('/api'));
14
+ * ```
15
+ */
16
+ export function pipe(...policies) {
17
+ if (policies.length === 0) {
18
+ return (fn) => fn();
19
+ }
20
+ return (fn) => {
21
+ // Build from right to left: the innermost policy wraps fn directly
22
+ let composed = fn;
23
+ for (let i = policies.length - 1; i >= 0; i--) {
24
+ const policy = policies[i];
25
+ const next = composed;
26
+ composed = () => policy(next);
27
+ }
28
+ return composed();
29
+ };
30
+ }
31
+ /**
32
+ * Wraps an inner policy with an outer policy.
33
+ * `wrap(outer, inner)(fn)` is equivalent to `outer(() => inner(fn))`.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const policy = wrap(
38
+ * timeout({ ms: 5000 }),
39
+ * retry({ maxAttempts: 3 }),
40
+ * );
41
+ * const result = await policy(() => fetch('/api'));
42
+ * ```
43
+ */
44
+ export function wrap(outer, inner) {
45
+ return (fn) => {
46
+ return outer(() => inner(fn));
47
+ };
48
+ }
49
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/compose.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,IAAI,CAAC,GAAG,QAAkB;IACxC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAI,EAAoB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;IAC3C,CAAC;IAED,OAAO,CAAI,EAAoB,EAAc,EAAE;QAC7C,mEAAmE;QACnE,IAAI,QAAQ,GAAqB,EAAE,CAAC;QAEpC,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC;YACtB,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,IAAI,CAAC,KAAa,EAAE,KAAa;IAC/C,OAAO,CAAI,EAAoB,EAAc,EAAE;QAC7C,OAAO,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC;AACJ,CAAC"}