awaitly 2.0.0 → 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.
Files changed (88) hide show
  1. package/dist/di-Biw_plBn.d.cts +15 -0
  2. package/dist/di-DxyH2N3i.d.ts +15 -0
  3. package/dist/durable.cjs +4670 -0
  4. package/dist/durable.cjs.map +1 -0
  5. package/dist/durable.d.cts +9 -0
  6. package/dist/durable.d.ts +9 -0
  7. package/dist/durable.js +4610 -0
  8. package/dist/durable.js.map +1 -0
  9. package/dist/{di-BbFFfO8y.d.ts → duration-BjVn3QpB.d.cts} +1 -15
  10. package/dist/{di-BDlT7InM.d.cts → duration-BjVn3QpB.d.ts} +1 -15
  11. package/dist/engine.cjs +4501 -0
  12. package/dist/engine.cjs.map +1 -0
  13. package/dist/engine.d.cts +113 -0
  14. package/dist/engine.d.ts +113 -0
  15. package/dist/engine.js +4474 -0
  16. package/dist/engine.js.map +1 -0
  17. package/dist/{errors-DtXvrCiO.d.cts → errors-sDSeaZBO.d.cts} +1 -1
  18. package/dist/{errors-DtXvrCiO.d.ts → errors-sDSeaZBO.d.ts} +1 -1
  19. package/dist/guards-CwQZro1F.d.ts +72 -0
  20. package/dist/guards-DhhOJZda.d.cts +72 -0
  21. package/dist/hitl-BnnnLzh2.d.cts +468 -0
  22. package/dist/hitl-pYfkQMv_.d.ts +468 -0
  23. package/dist/hitl.cjs +646 -0
  24. package/dist/hitl.cjs.map +1 -0
  25. package/dist/hitl.d.cts +440 -0
  26. package/dist/hitl.d.ts +440 -0
  27. package/dist/hitl.js +605 -0
  28. package/dist/hitl.js.map +1 -0
  29. package/dist/index-Bew12SZ8.d.ts +417 -0
  30. package/dist/index-Bwt-iz50.d.cts +417 -0
  31. package/dist/{types-B8NfNRGX.d.ts → index-Cv4K_3sZ.d.ts} +2 -1162
  32. package/dist/{types-BZ2f4MRR.d.cts → index-ICmfQi08.d.cts} +2 -1162
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +11 -1527
  35. package/dist/index.d.ts +11 -1527
  36. package/dist/persistence.cjs +552 -0
  37. package/dist/persistence.cjs.map +1 -0
  38. package/dist/persistence.d.cts +254 -0
  39. package/dist/persistence.d.ts +254 -0
  40. package/dist/persistence.js +499 -0
  41. package/dist/persistence.js.map +1 -0
  42. package/dist/reliability.cjs +1651 -0
  43. package/dist/reliability.cjs.map +1 -0
  44. package/dist/reliability.d.cts +1442 -0
  45. package/dist/reliability.d.ts +1442 -0
  46. package/dist/reliability.js +1589 -0
  47. package/dist/reliability.js.map +1 -0
  48. package/dist/result.d.cts +1 -1
  49. package/dist/result.d.ts +1 -1
  50. package/dist/run.cjs +2283 -0
  51. package/dist/run.cjs.map +1 -0
  52. package/dist/run.d.cts +90 -0
  53. package/dist/run.d.ts +90 -0
  54. package/dist/run.js +2247 -0
  55. package/dist/run.js.map +1 -0
  56. package/dist/saga.cjs +3699 -0
  57. package/dist/saga.cjs.map +1 -0
  58. package/dist/saga.d.cts +162 -0
  59. package/dist/saga.d.ts +162 -0
  60. package/dist/saga.js +3670 -0
  61. package/dist/saga.js.map +1 -0
  62. package/dist/store-contract-BI98VYmX.d.ts +66 -0
  63. package/dist/store-contract-C2HyR_o6.d.cts +66 -0
  64. package/dist/streaming.cjs +895 -0
  65. package/dist/streaming.cjs.map +1 -0
  66. package/dist/streaming.d.cts +594 -0
  67. package/dist/streaming.d.ts +594 -0
  68. package/dist/streaming.js +832 -0
  69. package/dist/streaming.js.map +1 -0
  70. package/dist/testing.d.cts +4 -2
  71. package/dist/testing.d.ts +4 -2
  72. package/dist/types-BDTxgKKc.d.ts +845 -0
  73. package/dist/types-C9Y71dua.d.cts +845 -0
  74. package/dist/types-d8q8iQlk.d.cts +323 -0
  75. package/dist/types-sObbY4mX.d.ts +323 -0
  76. package/dist/webhook.cjs +461 -0
  77. package/dist/webhook.cjs.map +1 -0
  78. package/dist/webhook.d.cts +497 -0
  79. package/dist/webhook.d.ts +497 -0
  80. package/dist/webhook.js +422 -0
  81. package/dist/webhook.js.map +1 -0
  82. package/dist/workflow.cjs +2 -2614
  83. package/dist/workflow.cjs.map +1 -1
  84. package/dist/workflow.d.cts +12 -3023
  85. package/dist/workflow.d.ts +12 -3023
  86. package/dist/workflow.js +2 -2535
  87. package/dist/workflow.js.map +1 -1
  88. package/package.json +46 -1
@@ -0,0 +1,1589 @@
1
+ // src/slugs.ts
2
+ var AWAITLY_SLUGS = {
3
+ // --- step-* ---
4
+ "step-require-id": "step-require-id",
5
+ "step-no-immediate-execution": "step-no-immediate-execution",
6
+ "step-require-thunk-for-key": "step-require-thunk-for-key",
7
+ "step-no-bare-await": "step-no-bare-await",
8
+ "step-no-try-catch-wrap": "step-no-try-catch-wrap",
9
+ "step-stable-cache-keys": "step-stable-cache-keys",
10
+ // --- workflow-* ---
11
+ "workflow-no-floating": "workflow-no-floating",
12
+ "workflow-options-position": "workflow-options-position",
13
+ "workflow-callback-shape": "workflow-callback-shape",
14
+ "workflow-no-callable-form": "workflow-no-callable-form",
15
+ "workflow-no-dynamic-import": "workflow-no-dynamic-import",
16
+ "workflow-prefer-step-if": "workflow-prefer-step-if",
17
+ "workflow-prefer-step-foreach": "workflow-prefer-step-foreach",
18
+ // --- result-* ---
19
+ "result-no-floating": "result-no-floating",
20
+ "result-require-handling": "result-require-handling",
21
+ "result-no-double-wrap": "result-no-double-wrap",
22
+ "result-no-manual-propagation": "result-no-manual-propagation",
23
+ "result-no-direct-ok-err": "result-no-direct-ok-err",
24
+ // --- error-* ---
25
+ "error-check-unexpected-first": "error-check-unexpected-first",
26
+ "error-access-cause": "error-access-cause",
27
+ "error-normalize": "error-normalize",
28
+ "error-no-throw-in-deps": "error-no-throw-in-deps",
29
+ // --- concurrency-* ---
30
+ "concurrency-no-promise-all": "concurrency-no-promise-all",
31
+ "concurrency-no-promise-race": "concurrency-no-promise-race",
32
+ "concurrency-no-promise-allsettled": "concurrency-no-promise-allsettled",
33
+ // --- runtime-* ---
34
+ "runtime-step-timeout": "runtime-step-timeout",
35
+ "runtime-step-aborted": "runtime-step-aborted",
36
+ "runtime-retry-exhausted": "runtime-retry-exhausted",
37
+ "runtime-rate-limit": "runtime-rate-limit",
38
+ "runtime-circuit-open": "runtime-circuit-open",
39
+ "runtime-unexpected": "runtime-unexpected",
40
+ "runtime-resolver-not-found": "runtime-resolver-not-found",
41
+ "runtime-saga-compensation": "runtime-saga-compensation"
42
+ };
43
+ function slugDocsUrl(slug) {
44
+ return `https://jagreehal.github.io/awaitly/rules/#${slug}`;
45
+ }
46
+ var ALL_SLUGS = Object.keys(
47
+ AWAITLY_SLUGS
48
+ );
49
+
50
+ // src/tagged-error.ts
51
+ var InternalTaggedErrorBase = class extends Error {
52
+ type;
53
+ /** @deprecated Use `type`. */
54
+ _tag;
55
+ };
56
+ function TaggedError(tag, options) {
57
+ return class extends InternalTaggedErrorBase {
58
+ type = tag;
59
+ /** @deprecated Use `type`. */
60
+ _tag = tag;
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ constructor(props, errorOptions) {
63
+ const message = options?.message ? options.message(props ?? {}) : tag;
64
+ super(message);
65
+ this.name = tag;
66
+ if (options?.slug !== void 0) {
67
+ if (!options.hint) {
68
+ throw new TypeError(
69
+ `TaggedError: 'hint' is required when 'slug' is set (slug: "${options.slug}")`
70
+ );
71
+ }
72
+ Object.defineProperty(this, "code", {
73
+ value: options.slug,
74
+ enumerable: true,
75
+ writable: false,
76
+ configurable: false
77
+ });
78
+ Object.defineProperty(this, "hint", {
79
+ value: options.hint,
80
+ enumerable: true,
81
+ writable: false,
82
+ configurable: false
83
+ });
84
+ Object.defineProperty(this, "docsUrl", {
85
+ value: slugDocsUrl(options.slug),
86
+ enumerable: true,
87
+ writable: false,
88
+ configurable: false
89
+ });
90
+ }
91
+ Object.setPrototypeOf(this, new.target.prototype);
92
+ if (props && typeof props === "object") {
93
+ let safeProps;
94
+ if (options?.slug !== void 0) {
95
+ const {
96
+ _tag: _,
97
+ type: _t,
98
+ name: _n,
99
+ message: _m,
100
+ stack: _s,
101
+ code: _c,
102
+ hint: _h,
103
+ docsUrl: _d,
104
+ ...rest
105
+ } = props;
106
+ safeProps = rest;
107
+ } else {
108
+ const {
109
+ _tag: _,
110
+ type: _t,
111
+ name: _n,
112
+ message: _m,
113
+ stack: _s,
114
+ ...rest
115
+ } = props;
116
+ safeProps = rest;
117
+ }
118
+ const hasUserCause = Object.prototype.hasOwnProperty.call(
119
+ safeProps,
120
+ "cause"
121
+ );
122
+ const userCause = hasUserCause ? safeProps.cause : void 0;
123
+ if (hasUserCause) {
124
+ delete safeProps.cause;
125
+ }
126
+ const hasOptionsCause = errorOptions?.cause !== void 0;
127
+ if (hasUserCause && hasOptionsCause) {
128
+ throw new TypeError(
129
+ "TaggedError: cannot provide 'cause' in props when also setting ErrorOptions.cause"
130
+ );
131
+ }
132
+ Object.assign(this, safeProps);
133
+ if (hasUserCause) {
134
+ this.cause = userCause;
135
+ }
136
+ if (hasOptionsCause) {
137
+ this.cause = errorOptions?.cause;
138
+ }
139
+ } else if (errorOptions?.cause !== void 0) {
140
+ this.cause = errorOptions.cause;
141
+ }
142
+ }
143
+ };
144
+ }
145
+ Object.defineProperty(TaggedError, Symbol.hasInstance, {
146
+ value: (instance) => instance instanceof InternalTaggedErrorBase
147
+ });
148
+ ((TaggedError2) => {
149
+ function isError(value) {
150
+ return value instanceof Error;
151
+ }
152
+ TaggedError2.isError = isError;
153
+ function isTaggedError(value) {
154
+ return value instanceof InternalTaggedErrorBase;
155
+ }
156
+ TaggedError2.isTaggedError = isTaggedError;
157
+ function match(error, handlers) {
158
+ const tag = error._tag;
159
+ const handler = handlers[tag];
160
+ return handler(
161
+ error
162
+ );
163
+ }
164
+ TaggedError2.match = match;
165
+ function matchPartial(error, handlers, otherwise) {
166
+ const tag = error._tag;
167
+ const handler = handlers[tag];
168
+ if (handler) {
169
+ return handler(
170
+ error
171
+ );
172
+ }
173
+ return otherwise(error);
174
+ }
175
+ TaggedError2.matchPartial = matchPartial;
176
+ })(TaggedError || (TaggedError = {}));
177
+
178
+ // src/errors.ts
179
+ var TimeoutError = class extends (/* @__PURE__ */ TaggedError("TimeoutError", {
180
+ slug: "runtime-step-timeout",
181
+ hint: "Increase the step's timeout option, or check why the upstream operation is slow.",
182
+ message: (p) => p.operation ? `TimeoutError: ${p.operation} timed out after ${p.ms}ms` : `TimeoutError: Operation timed out after ${p.ms}ms`
183
+ })) {
184
+ };
185
+ var UnexpectedError = class extends (/* @__PURE__ */ TaggedError("UnexpectedError", {
186
+ slug: "runtime-unexpected",
187
+ hint: "An unexpected exception escaped a step. Inspect cause; consider returning a typed Result instead of throwing.",
188
+ message: (p) => `UnexpectedError: ${p.cause instanceof Error ? p.cause.message : String(p.cause ?? "unknown")}`
189
+ })) {
190
+ };
191
+
192
+ // src/result/index.ts
193
+ function ok(value) {
194
+ return { ok: true, value };
195
+ }
196
+ function err(error, options) {
197
+ const cause = options?.cause;
198
+ return { ok: false, error, ...cause !== void 0 ? { cause } : {} };
199
+ }
200
+
201
+ // src/duration.ts
202
+ function millis(ms) {
203
+ return { _tag: "Duration", millis: ms };
204
+ }
205
+ function seconds(s) {
206
+ return { _tag: "Duration", millis: s * 1e3 };
207
+ }
208
+ function minutes(m) {
209
+ return { _tag: "Duration", millis: m * 60 * 1e3 };
210
+ }
211
+ function hours(h) {
212
+ return { _tag: "Duration", millis: h * 60 * 60 * 1e3 };
213
+ }
214
+ function days(d) {
215
+ return { _tag: "Duration", millis: d * 24 * 60 * 60 * 1e3 };
216
+ }
217
+ function toMillis(duration) {
218
+ return duration.millis;
219
+ }
220
+ function parse(input) {
221
+ const match = input.trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/i);
222
+ if (!match) return void 0;
223
+ const value = parseFloat(match[1]);
224
+ const unit = match[2].toLowerCase();
225
+ switch (unit) {
226
+ case "ms":
227
+ return millis(value);
228
+ case "s":
229
+ return seconds(value);
230
+ case "m":
231
+ return minutes(value);
232
+ case "h":
233
+ return hours(value);
234
+ case "d":
235
+ return days(value);
236
+ default:
237
+ return void 0;
238
+ }
239
+ }
240
+
241
+ // src/core/bound-steps.ts
242
+ var isDepResultShaped = (value) => typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean" && (value.ok ? "value" in value : "error" in value);
243
+
244
+ // src/core/policies.ts
245
+ var toMs = (d) => typeof d === "number" ? d : toMillis(d);
246
+ var attemptCall = async (fn, args) => {
247
+ try {
248
+ const value = await fn(...args);
249
+ if (isDepResultShaped(value)) {
250
+ return value.ok ? { kind: "ok", value: value.value } : { kind: "err", error: value.error, result: value };
251
+ }
252
+ return { kind: "ok", value };
253
+ } catch (thrown) {
254
+ return { kind: "threw", thrown };
255
+ }
256
+ };
257
+ var named = (wrapped, source) => {
258
+ const name = source.name;
259
+ if (name) {
260
+ Object.defineProperty(wrapped, "name", { value: name, configurable: true });
261
+ }
262
+ return wrapped;
263
+ };
264
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
265
+ function retry(fn, options) {
266
+ const attempts = Math.max(1, Math.trunc(options.attempts));
267
+ const baseDelay = options.delay === void 0 ? 0 : toMs(options.delay);
268
+ const maxDelay = options.maxDelay === void 0 ? Infinity : toMs(options.maxDelay);
269
+ const backoff = options.backoff ?? "fixed";
270
+ const delayFor = (attempt) => {
271
+ const raw = backoff === "exponential" ? baseDelay * 2 ** (attempt - 1) : backoff === "linear" ? baseDelay * attempt : baseDelay;
272
+ return Math.min(raw, maxDelay);
273
+ };
274
+ const wrapped = async (...args) => {
275
+ let last = { kind: "threw", thrown: void 0 };
276
+ for (let attempt = 1; attempt <= attempts; attempt++) {
277
+ last = await attemptCall(fn, args);
278
+ if (last.kind === "ok") return ok(last.value);
279
+ const failure = last.kind === "err" ? last.error : last.thrown;
280
+ if (options.retryIf && !options.retryIf(failure)) break;
281
+ if (attempt < attempts) {
282
+ options.onRetry?.({ attempt, failure });
283
+ const ms = delayFor(attempt);
284
+ if (ms > 0) await sleep(ms);
285
+ }
286
+ }
287
+ if (last.kind === "err") return last.result;
288
+ throw last.thrown;
289
+ };
290
+ return named(wrapped, fn);
291
+ }
292
+ function timeout(fn, after) {
293
+ const ms = toMs(after);
294
+ const TIMED_OUT = /* @__PURE__ */ Symbol("timed-out");
295
+ const wrapped = async (...args) => {
296
+ let timer;
297
+ try {
298
+ const outcome = await Promise.race([
299
+ attemptCall(fn, args),
300
+ new Promise((resolve) => {
301
+ timer = setTimeout(() => resolve(TIMED_OUT), ms);
302
+ })
303
+ ]);
304
+ if (outcome === TIMED_OUT) {
305
+ return err(new TimeoutError({ operation: fn.name || void 0, ms }));
306
+ }
307
+ if (outcome.kind === "ok") return ok(outcome.value);
308
+ if (outcome.kind === "err") return outcome.result;
309
+ throw outcome.thrown;
310
+ } finally {
311
+ if (timer !== void 0) clearTimeout(timer);
312
+ }
313
+ };
314
+ return named(wrapped, fn);
315
+ }
316
+ function fallback(fn, onFailure) {
317
+ const wrapped = async (...args) => {
318
+ const outcome = await attemptCall(fn, args);
319
+ if (outcome.kind === "ok") return ok(outcome.value);
320
+ const failure = outcome.kind === "err" ? outcome.error : new UnexpectedError({ cause: outcome.thrown });
321
+ const recovered = await attemptCall(onFailure, [failure, ...args]);
322
+ if (recovered.kind === "ok") return ok(recovered.value);
323
+ if (recovered.kind === "err") return recovered.result;
324
+ throw recovered.thrown;
325
+ };
326
+ return named(wrapped, fn);
327
+ }
328
+
329
+ // src/core/index.ts
330
+ function err2(error, options) {
331
+ const cause = options?.cause;
332
+ return { ok: false, error, ...cause !== void 0 ? { cause } : {} };
333
+ }
334
+
335
+ // src/circuit-breaker.ts
336
+ var CircuitOpenError = class extends Error {
337
+ type = "CIRCUIT_OPEN";
338
+ circuitName;
339
+ state;
340
+ retryAfterMs;
341
+ constructor(options) {
342
+ super(
343
+ options.message ?? `Circuit breaker "${options.circuitName}" is ${options.state}. Retry after ${Math.ceil(options.retryAfterMs / 1e3)}s`
344
+ );
345
+ this.name = "CircuitOpenError";
346
+ this.circuitName = options.circuitName;
347
+ this.state = options.state;
348
+ this.retryAfterMs = options.retryAfterMs;
349
+ }
350
+ };
351
+ function isCircuitOpenError(error) {
352
+ return typeof error === "object" && error !== null && error.type === "CIRCUIT_OPEN";
353
+ }
354
+ var DEFAULT_CONFIG = {
355
+ failureThreshold: 5,
356
+ resetTimeout: 3e4,
357
+ windowSize: 6e4,
358
+ halfOpenMax: 3
359
+ };
360
+ function createCircuitBreaker(name, config) {
361
+ const effectiveConfig = {
362
+ ...DEFAULT_CONFIG,
363
+ ...config
364
+ };
365
+ let state = "CLOSED";
366
+ let failures = [];
367
+ let lastFailureTime = null;
368
+ let lastSuccessTime = null;
369
+ let successCount = 0;
370
+ let halfOpenSuccesses = 0;
371
+ function cleanupFailures() {
372
+ const now = Date.now();
373
+ failures = failures.filter(
374
+ (f) => now - f.timestamp < effectiveConfig.windowSize
375
+ );
376
+ }
377
+ function transitionTo(newState) {
378
+ if (state !== newState) {
379
+ const oldState = state;
380
+ state = newState;
381
+ if (newState === "HALF_OPEN") {
382
+ halfOpenSuccesses = 0;
383
+ }
384
+ effectiveConfig.onStateChange?.(oldState, newState, name);
385
+ }
386
+ }
387
+ function checkOpenToHalfOpen() {
388
+ if (state !== "OPEN" || lastFailureTime === null) {
389
+ return false;
390
+ }
391
+ const now = Date.now();
392
+ if (now - lastFailureTime >= effectiveConfig.resetTimeout) {
393
+ transitionTo("HALF_OPEN");
394
+ return true;
395
+ }
396
+ return false;
397
+ }
398
+ function handleSuccess() {
399
+ lastSuccessTime = Date.now();
400
+ successCount++;
401
+ if (state === "HALF_OPEN") {
402
+ halfOpenSuccesses++;
403
+ if (halfOpenSuccesses >= effectiveConfig.halfOpenMax) {
404
+ transitionTo("CLOSED");
405
+ failures = [];
406
+ }
407
+ }
408
+ }
409
+ function handleFailure(error) {
410
+ const now = Date.now();
411
+ lastFailureTime = now;
412
+ cleanupFailures();
413
+ failures.push({ timestamp: now, error });
414
+ if (state === "HALF_OPEN") {
415
+ transitionTo("OPEN");
416
+ } else if (state === "CLOSED") {
417
+ if (failures.length >= effectiveConfig.failureThreshold) {
418
+ transitionTo("OPEN");
419
+ }
420
+ }
421
+ }
422
+ function canExecute() {
423
+ if (state === "CLOSED") {
424
+ return 0;
425
+ }
426
+ if (state === "OPEN") {
427
+ if (checkOpenToHalfOpen()) {
428
+ return 0;
429
+ }
430
+ const now = Date.now();
431
+ const elapsed = lastFailureTime ? now - lastFailureTime : 0;
432
+ return Math.max(0, effectiveConfig.resetTimeout - elapsed);
433
+ }
434
+ return 0;
435
+ }
436
+ return {
437
+ async execute(operation, _options) {
438
+ const waitTime = canExecute();
439
+ if (waitTime > 0) {
440
+ throw new CircuitOpenError({
441
+ circuitName: name,
442
+ state,
443
+ retryAfterMs: waitTime
444
+ });
445
+ }
446
+ try {
447
+ const result = await operation();
448
+ handleSuccess();
449
+ return result;
450
+ } catch (error) {
451
+ handleFailure(error);
452
+ throw error;
453
+ }
454
+ },
455
+ async executeResult(operation, _options) {
456
+ const waitTime = canExecute();
457
+ if (waitTime > 0) {
458
+ return err2(
459
+ new CircuitOpenError({
460
+ circuitName: name,
461
+ state,
462
+ retryAfterMs: waitTime
463
+ })
464
+ );
465
+ }
466
+ try {
467
+ const result = await operation();
468
+ if (result.ok) {
469
+ handleSuccess();
470
+ } else {
471
+ handleFailure(result.error);
472
+ }
473
+ return result;
474
+ } catch (error) {
475
+ handleFailure(error);
476
+ throw error;
477
+ }
478
+ },
479
+ getState() {
480
+ if (state === "OPEN") {
481
+ checkOpenToHalfOpen();
482
+ }
483
+ return state;
484
+ },
485
+ getStats() {
486
+ cleanupFailures();
487
+ return {
488
+ state: this.getState(),
489
+ failureCount: failures.length,
490
+ successCount,
491
+ lastFailureTime,
492
+ lastSuccessTime,
493
+ halfOpenSuccesses
494
+ };
495
+ },
496
+ reset() {
497
+ transitionTo("CLOSED");
498
+ failures = [];
499
+ halfOpenSuccesses = 0;
500
+ },
501
+ forceOpen() {
502
+ lastFailureTime = Date.now();
503
+ transitionTo("OPEN");
504
+ },
505
+ recordSuccess() {
506
+ handleSuccess();
507
+ },
508
+ recordFailure(error) {
509
+ handleFailure(error ?? new Error("Manual failure"));
510
+ }
511
+ };
512
+ }
513
+ var circuitBreakerPresets = {
514
+ /**
515
+ * Aggressive circuit breaker for critical paths.
516
+ * Opens quickly (3 failures) and recovers slowly (60s).
517
+ */
518
+ critical: {
519
+ failureThreshold: 3,
520
+ resetTimeout: 6e4,
521
+ windowSize: 3e4,
522
+ halfOpenMax: 1
523
+ },
524
+ /**
525
+ * Standard circuit breaker for typical API calls.
526
+ * Balanced between stability and availability.
527
+ */
528
+ standard: {
529
+ failureThreshold: 5,
530
+ resetTimeout: 3e4,
531
+ windowSize: 6e4,
532
+ halfOpenMax: 3
533
+ },
534
+ /**
535
+ * Lenient circuit breaker for non-critical operations.
536
+ * Opens slowly (10 failures) and recovers quickly (15s).
537
+ */
538
+ lenient: {
539
+ failureThreshold: 10,
540
+ resetTimeout: 15e3,
541
+ windowSize: 12e4,
542
+ halfOpenMax: 5
543
+ }
544
+ };
545
+
546
+ // src/rate-limiter/index.ts
547
+ function isRateLimitExceededError(error) {
548
+ return typeof error === "object" && error !== null && error.type === "RATE_LIMIT_EXCEEDED";
549
+ }
550
+ function isQueueFullError(error) {
551
+ return typeof error === "object" && error !== null && error.type === "QUEUE_FULL";
552
+ }
553
+ function createRateLimiter(name, config) {
554
+ const { maxPerSecond, strategy = "wait" } = config;
555
+ const maxTokens = config.burstCapacity ?? maxPerSecond * 2;
556
+ let tokens = maxTokens;
557
+ let lastRefill = Date.now();
558
+ const refillRate = maxPerSecond / 1e3;
559
+ const waitQueue = [];
560
+ function refill() {
561
+ const now = Date.now();
562
+ const elapsed = now - lastRefill;
563
+ const tokensToAdd = elapsed * refillRate;
564
+ tokens = Math.min(maxTokens, tokens + tokensToAdd);
565
+ lastRefill = now;
566
+ }
567
+ function tryConsume() {
568
+ refill();
569
+ if (tokens >= 1) {
570
+ tokens -= 1;
571
+ return 0;
572
+ }
573
+ const tokensNeeded = 1 - tokens;
574
+ return Math.ceil(tokensNeeded / refillRate);
575
+ }
576
+ async function waitForToken() {
577
+ return new Promise((resolve) => {
578
+ const check = () => {
579
+ const waitTime = tryConsume();
580
+ if (waitTime === 0) {
581
+ resolve();
582
+ } else {
583
+ waitQueue.push(check);
584
+ setTimeout(() => {
585
+ const idx = waitQueue.indexOf(check);
586
+ if (idx !== -1) {
587
+ waitQueue.splice(idx, 1);
588
+ check();
589
+ }
590
+ }, waitTime);
591
+ }
592
+ };
593
+ check();
594
+ });
595
+ }
596
+ return {
597
+ async execute(operation) {
598
+ const waitTime = tryConsume();
599
+ if (waitTime > 0) {
600
+ if (strategy === "reject") {
601
+ throw {
602
+ type: "RATE_LIMIT_EXCEEDED",
603
+ limiterName: name,
604
+ retryAfterMs: waitTime
605
+ };
606
+ }
607
+ await waitForToken();
608
+ }
609
+ return operation();
610
+ },
611
+ async executeResult(operation) {
612
+ const waitTime = tryConsume();
613
+ if (waitTime > 0) {
614
+ if (strategy === "reject") {
615
+ return err2({
616
+ type: "RATE_LIMIT_EXCEEDED",
617
+ limiterName: name,
618
+ retryAfterMs: waitTime
619
+ });
620
+ }
621
+ await waitForToken();
622
+ }
623
+ return operation();
624
+ },
625
+ getStats() {
626
+ refill();
627
+ return {
628
+ availableTokens: Math.floor(tokens),
629
+ maxTokens,
630
+ tokensPerSecond: maxPerSecond,
631
+ waitingCount: waitQueue.length
632
+ };
633
+ },
634
+ reset() {
635
+ tokens = maxTokens;
636
+ lastRefill = Date.now();
637
+ waitQueue.length = 0;
638
+ }
639
+ };
640
+ }
641
+ function createConcurrencyLimiter(name, config) {
642
+ const { maxConcurrent, strategy = "queue", maxQueueSize = Infinity } = config;
643
+ let activeCount = 0;
644
+ const queue = [];
645
+ async function acquire() {
646
+ if (activeCount < maxConcurrent) {
647
+ activeCount++;
648
+ return;
649
+ }
650
+ if (strategy === "reject") {
651
+ throw {
652
+ type: "QUEUE_FULL",
653
+ limiterName: name,
654
+ queueSize: queue.length,
655
+ maxQueueSize
656
+ };
657
+ }
658
+ if (queue.length >= maxQueueSize) {
659
+ throw {
660
+ type: "QUEUE_FULL",
661
+ limiterName: name,
662
+ queueSize: queue.length,
663
+ maxQueueSize
664
+ };
665
+ }
666
+ return new Promise((resolve, reject) => {
667
+ queue.push({ resolve, reject });
668
+ });
669
+ }
670
+ function release() {
671
+ activeCount--;
672
+ if (queue.length > 0 && activeCount < maxConcurrent) {
673
+ activeCount++;
674
+ const next = queue.shift();
675
+ next?.resolve();
676
+ }
677
+ }
678
+ return {
679
+ async execute(operation) {
680
+ await acquire();
681
+ try {
682
+ return await operation();
683
+ } finally {
684
+ release();
685
+ }
686
+ },
687
+ async executeAll(operations) {
688
+ const results = new Array(operations.length);
689
+ const executing = [];
690
+ for (let i = 0; i < operations.length; i++) {
691
+ const index = i;
692
+ const promise = this.execute(operations[index]).then((result) => {
693
+ results[index] = result;
694
+ });
695
+ executing.push(promise);
696
+ }
697
+ await Promise.all(executing);
698
+ return results;
699
+ },
700
+ async executeResult(operation) {
701
+ try {
702
+ await acquire();
703
+ } catch (error) {
704
+ if (isQueueFullError(error)) {
705
+ return err2(error);
706
+ }
707
+ throw error;
708
+ }
709
+ try {
710
+ return await operation();
711
+ } finally {
712
+ release();
713
+ }
714
+ },
715
+ getStats() {
716
+ return {
717
+ activeCount,
718
+ maxConcurrent,
719
+ queueSize: queue.length,
720
+ maxQueueSize
721
+ };
722
+ },
723
+ reset() {
724
+ activeCount = 0;
725
+ while (queue.length > 0) {
726
+ const item = queue.shift();
727
+ item?.reject(new Error("Limiter reset"));
728
+ }
729
+ }
730
+ };
731
+ }
732
+ function createCombinedLimiter(name, config) {
733
+ const rate = config.rate ? createRateLimiter(`${name}-rate`, config.rate) : void 0;
734
+ const concurrency = config.concurrency ? createConcurrencyLimiter(`${name}-concurrency`, config.concurrency) : void 0;
735
+ return {
736
+ rate,
737
+ concurrency,
738
+ async execute(operation) {
739
+ let op = operation;
740
+ if (rate) {
741
+ const originalOp = op;
742
+ op = () => rate.execute(originalOp);
743
+ }
744
+ if (concurrency) {
745
+ return concurrency.execute(op);
746
+ }
747
+ return op();
748
+ }
749
+ };
750
+ }
751
+ function createFixedWindowLimiter(name, config) {
752
+ const { limit, windowMs = 1e3, strategy = "wait" } = config;
753
+ let windowStart = Date.now();
754
+ let requestCount = 0;
755
+ const waitQueue = [];
756
+ function checkWindow() {
757
+ const now = Date.now();
758
+ const elapsed = now - windowStart;
759
+ if (elapsed >= windowMs) {
760
+ windowStart = now;
761
+ requestCount = 0;
762
+ return 0;
763
+ }
764
+ return windowMs - elapsed;
765
+ }
766
+ function tryConsume(cost) {
767
+ const remainingMs = checkWindow();
768
+ if (requestCount + cost <= limit) {
769
+ requestCount += cost;
770
+ return 0;
771
+ }
772
+ return remainingMs;
773
+ }
774
+ async function waitForWindow(cost) {
775
+ return new Promise((resolve) => {
776
+ const check = () => {
777
+ const waitTime = tryConsume(cost);
778
+ if (waitTime === 0) {
779
+ resolve();
780
+ } else {
781
+ waitQueue.push({ resolve: check, cost });
782
+ setTimeout(() => {
783
+ const idx = waitQueue.findIndex((w) => w.resolve === check);
784
+ if (idx !== -1) {
785
+ waitQueue.splice(idx, 1);
786
+ check();
787
+ }
788
+ }, waitTime);
789
+ }
790
+ };
791
+ check();
792
+ });
793
+ }
794
+ return {
795
+ async execute(operation, cost = 1) {
796
+ if (cost > limit) {
797
+ throw {
798
+ type: "RATE_LIMIT_EXCEEDED",
799
+ limiterName: name,
800
+ retryAfterMs: windowMs
801
+ };
802
+ }
803
+ const waitTime = tryConsume(cost);
804
+ if (waitTime > 0) {
805
+ if (strategy === "reject") {
806
+ throw {
807
+ type: "RATE_LIMIT_EXCEEDED",
808
+ limiterName: name,
809
+ retryAfterMs: waitTime
810
+ };
811
+ }
812
+ await waitForWindow(cost);
813
+ }
814
+ return operation();
815
+ },
816
+ async executeResult(operation, cost = 1) {
817
+ if (cost > limit) {
818
+ return err2({
819
+ type: "RATE_LIMIT_EXCEEDED",
820
+ limiterName: name,
821
+ retryAfterMs: windowMs
822
+ });
823
+ }
824
+ const waitTime = tryConsume(cost);
825
+ if (waitTime > 0) {
826
+ if (strategy === "reject") {
827
+ return err2({
828
+ type: "RATE_LIMIT_EXCEEDED",
829
+ limiterName: name,
830
+ retryAfterMs: waitTime
831
+ });
832
+ }
833
+ await waitForWindow(cost);
834
+ }
835
+ return operation();
836
+ },
837
+ getStats() {
838
+ const remainingMs = checkWindow();
839
+ return {
840
+ requestCount,
841
+ limit,
842
+ windowMs,
843
+ remainingMs,
844
+ waitingCount: waitQueue.length
845
+ };
846
+ },
847
+ reset() {
848
+ windowStart = Date.now();
849
+ requestCount = 0;
850
+ waitQueue.length = 0;
851
+ }
852
+ };
853
+ }
854
+ function createCostBasedRateLimiter(name, config) {
855
+ const { tokensPerSecond, strategy = "wait" } = config;
856
+ const maxTokens = config.maxTokens ?? tokensPerSecond * 2;
857
+ let tokens = maxTokens;
858
+ let lastRefill = Date.now();
859
+ const refillRate = tokensPerSecond / 1e3;
860
+ const waitQueue = [];
861
+ function refill() {
862
+ const now = Date.now();
863
+ const elapsed = now - lastRefill;
864
+ const tokensToAdd = elapsed * refillRate;
865
+ tokens = Math.min(maxTokens, tokens + tokensToAdd);
866
+ lastRefill = now;
867
+ }
868
+ function tryConsume(cost) {
869
+ refill();
870
+ if (tokens >= cost) {
871
+ tokens -= cost;
872
+ return 0;
873
+ }
874
+ const tokensNeeded = cost - tokens;
875
+ return Math.ceil(tokensNeeded / refillRate);
876
+ }
877
+ async function waitForTokens(cost) {
878
+ return new Promise((resolve) => {
879
+ const check = () => {
880
+ const waitTime = tryConsume(cost);
881
+ if (waitTime === 0) {
882
+ resolve();
883
+ } else {
884
+ waitQueue.push({ check, cost });
885
+ setTimeout(() => {
886
+ const idx = waitQueue.findIndex((w) => w.check === check);
887
+ if (idx !== -1) {
888
+ waitQueue.splice(idx, 1);
889
+ check();
890
+ }
891
+ }, waitTime);
892
+ }
893
+ };
894
+ check();
895
+ });
896
+ }
897
+ return {
898
+ async execute(operation, cost = 1) {
899
+ if (cost > maxTokens) {
900
+ throw {
901
+ type: "RATE_LIMIT_EXCEEDED",
902
+ limiterName: name,
903
+ retryAfterMs: Math.ceil(cost / refillRate)
904
+ };
905
+ }
906
+ const waitTime = tryConsume(cost);
907
+ if (waitTime > 0) {
908
+ if (strategy === "reject") {
909
+ throw {
910
+ type: "RATE_LIMIT_EXCEEDED",
911
+ limiterName: name,
912
+ retryAfterMs: waitTime
913
+ };
914
+ }
915
+ await waitForTokens(cost);
916
+ }
917
+ return operation();
918
+ },
919
+ async executeResult(operation, cost = 1) {
920
+ if (cost > maxTokens) {
921
+ return err2({
922
+ type: "RATE_LIMIT_EXCEEDED",
923
+ limiterName: name,
924
+ retryAfterMs: Math.ceil(cost / refillRate)
925
+ });
926
+ }
927
+ const waitTime = tryConsume(cost);
928
+ if (waitTime > 0) {
929
+ if (strategy === "reject") {
930
+ return err2({
931
+ type: "RATE_LIMIT_EXCEEDED",
932
+ limiterName: name,
933
+ retryAfterMs: waitTime
934
+ });
935
+ }
936
+ await waitForTokens(cost);
937
+ }
938
+ return operation();
939
+ },
940
+ getStats() {
941
+ refill();
942
+ return {
943
+ availableTokens: tokens,
944
+ maxTokens,
945
+ tokensPerSecond,
946
+ waitingCount: waitQueue.length
947
+ };
948
+ },
949
+ reset() {
950
+ tokens = maxTokens;
951
+ lastRefill = Date.now();
952
+ waitQueue.length = 0;
953
+ }
954
+ };
955
+ }
956
+ var rateLimiterPresets = {
957
+ /**
958
+ * Typical API rate limit (10 req/s).
959
+ */
960
+ api: {
961
+ maxPerSecond: 10,
962
+ burstCapacity: 20,
963
+ strategy: "wait"
964
+ },
965
+ /**
966
+ * Database pool limit (concurrent connections).
967
+ */
968
+ database: {
969
+ maxConcurrent: 10,
970
+ strategy: "queue",
971
+ maxQueueSize: 100
972
+ },
973
+ /**
974
+ * Aggressive rate limit for external APIs (5 req/s).
975
+ */
976
+ external: {
977
+ maxPerSecond: 5,
978
+ burstCapacity: 10,
979
+ strategy: "wait"
980
+ }
981
+ };
982
+
983
+ // src/cache.ts
984
+ function toMs2(duration) {
985
+ if (typeof duration === "string") {
986
+ const parsed = parse(duration);
987
+ if (!parsed) {
988
+ throw new Error(`Invalid duration string: ${duration}`);
989
+ }
990
+ return parsed.millis;
991
+ }
992
+ return duration.millis;
993
+ }
994
+ function cached(fn) {
995
+ let state = { status: "empty" };
996
+ return async () => {
997
+ if (state.status === "filled") {
998
+ return state.value;
999
+ }
1000
+ if (state.status === "pending") {
1001
+ return state.promise;
1002
+ }
1003
+ const promise = Promise.resolve(fn()).then((value) => {
1004
+ state = { status: "filled", value };
1005
+ return value;
1006
+ });
1007
+ state = { status: "pending", promise };
1008
+ return promise;
1009
+ };
1010
+ }
1011
+ function cachedWithTTL(fn, options) {
1012
+ const ttlMs = toMs2(options.ttl);
1013
+ let state = { status: "empty" };
1014
+ return async () => {
1015
+ const now = Date.now();
1016
+ if (state.status === "filled" && now < state.expiresAt) {
1017
+ return state.value;
1018
+ }
1019
+ if (state.status === "pending") {
1020
+ return state.promise;
1021
+ }
1022
+ const promise = Promise.resolve(fn()).then((value) => {
1023
+ state = {
1024
+ status: "filled",
1025
+ value,
1026
+ expiresAt: Date.now() + ttlMs
1027
+ };
1028
+ return value;
1029
+ });
1030
+ state = { status: "pending", promise };
1031
+ return promise;
1032
+ };
1033
+ }
1034
+ function cachedFunction(fn, options = {}) {
1035
+ const {
1036
+ keyFn = (...args) => JSON.stringify(args),
1037
+ maxSize = Infinity
1038
+ } = options;
1039
+ const ttlMs = options.ttl ? toMs2(options.ttl) : void 0;
1040
+ const cache = /* @__PURE__ */ new Map();
1041
+ const pending = /* @__PURE__ */ new Map();
1042
+ let hits = 0;
1043
+ let misses = 0;
1044
+ function evictOldest() {
1045
+ if (cache.size <= maxSize) return;
1046
+ let oldestKey = null;
1047
+ let oldestTime = Infinity;
1048
+ for (const [key, entry] of cache.entries()) {
1049
+ if (entry.timestamp < oldestTime) {
1050
+ oldestTime = entry.timestamp;
1051
+ oldestKey = key;
1052
+ }
1053
+ }
1054
+ if (oldestKey) {
1055
+ cache.delete(oldestKey);
1056
+ }
1057
+ }
1058
+ const memoized = async (...args) => {
1059
+ const key = keyFn(...args);
1060
+ const now = Date.now();
1061
+ const cached2 = cache.get(key);
1062
+ if (cached2) {
1063
+ if (cached2.expiresAt && now >= cached2.expiresAt) {
1064
+ cache.delete(key);
1065
+ } else {
1066
+ hits++;
1067
+ return cached2.value;
1068
+ }
1069
+ }
1070
+ const pendingPromise = pending.get(key);
1071
+ if (pendingPromise) {
1072
+ return pendingPromise;
1073
+ }
1074
+ misses++;
1075
+ const promise = Promise.resolve(fn(...args)).then((value) => {
1076
+ const entry = {
1077
+ value,
1078
+ timestamp: Date.now(),
1079
+ expiresAt: ttlMs ? Date.now() + ttlMs : void 0
1080
+ };
1081
+ cache.set(key, entry);
1082
+ pending.delete(key);
1083
+ evictOldest();
1084
+ return value;
1085
+ });
1086
+ pending.set(key, promise);
1087
+ try {
1088
+ return await promise;
1089
+ } catch (error) {
1090
+ pending.delete(key);
1091
+ throw error;
1092
+ }
1093
+ };
1094
+ memoized.clear = () => {
1095
+ cache.clear();
1096
+ pending.clear();
1097
+ };
1098
+ memoized.delete = (...args) => {
1099
+ const key = keyFn(...args);
1100
+ return cache.delete(key);
1101
+ };
1102
+ memoized.has = (...args) => {
1103
+ const key = keyFn(...args);
1104
+ const entry = cache.get(key);
1105
+ if (!entry) return false;
1106
+ if (entry.expiresAt && Date.now() >= entry.expiresAt) {
1107
+ cache.delete(key);
1108
+ return false;
1109
+ }
1110
+ return true;
1111
+ };
1112
+ memoized.getStats = () => ({
1113
+ hits,
1114
+ misses,
1115
+ size: cache.size
1116
+ });
1117
+ return memoized;
1118
+ }
1119
+ function once(fn) {
1120
+ let state = { status: "idle" };
1121
+ const onceFn = async () => {
1122
+ if (state.status === "done") {
1123
+ return state.value;
1124
+ }
1125
+ if (state.status === "failed") {
1126
+ throw state.error;
1127
+ }
1128
+ if (state.status === "running") {
1129
+ return state.promise;
1130
+ }
1131
+ const promise = Promise.resolve(fn()).then((value) => {
1132
+ state = { status: "done", value };
1133
+ return value;
1134
+ }).catch((error) => {
1135
+ state = { status: "failed", error };
1136
+ throw error;
1137
+ });
1138
+ state = { status: "running", promise };
1139
+ return promise;
1140
+ };
1141
+ Object.defineProperty(onceFn, "called", {
1142
+ get: () => state.status !== "idle"
1143
+ });
1144
+ Object.defineProperty(onceFn, "completed", {
1145
+ get: () => state.status === "done"
1146
+ });
1147
+ Object.defineProperty(onceFn, "failed", {
1148
+ get: () => state.status === "failed"
1149
+ });
1150
+ onceFn.reset = () => {
1151
+ state = { status: "idle" };
1152
+ };
1153
+ return onceFn;
1154
+ }
1155
+ function createCache(config = {}) {
1156
+ const { maxSize = Infinity } = config;
1157
+ const defaultTTLMs = config.defaultTTL ? toMs2(config.defaultTTL) : void 0;
1158
+ const store = /* @__PURE__ */ new Map();
1159
+ let hits = 0;
1160
+ let misses = 0;
1161
+ function isExpired(entry) {
1162
+ return entry.expiresAt !== void 0 && Date.now() >= entry.expiresAt;
1163
+ }
1164
+ function evictOldest() {
1165
+ if (store.size <= maxSize) return;
1166
+ let oldestKey = null;
1167
+ let oldestTime = Infinity;
1168
+ for (const [key, entry] of store.entries()) {
1169
+ if (entry.timestamp < oldestTime) {
1170
+ oldestTime = entry.timestamp;
1171
+ oldestKey = key;
1172
+ }
1173
+ }
1174
+ if (oldestKey !== null) {
1175
+ store.delete(oldestKey);
1176
+ }
1177
+ }
1178
+ return {
1179
+ get(key) {
1180
+ const entry = store.get(key);
1181
+ if (!entry) {
1182
+ misses++;
1183
+ return void 0;
1184
+ }
1185
+ if (isExpired(entry)) {
1186
+ store.delete(key);
1187
+ misses++;
1188
+ return void 0;
1189
+ }
1190
+ hits++;
1191
+ return entry.value;
1192
+ },
1193
+ set(key, value, options) {
1194
+ const ttlMs = options?.ttl ? toMs2(options.ttl) : defaultTTLMs;
1195
+ const now = Date.now();
1196
+ store.set(key, {
1197
+ value,
1198
+ timestamp: now,
1199
+ expiresAt: ttlMs ? now + ttlMs : void 0
1200
+ });
1201
+ evictOldest();
1202
+ },
1203
+ has(key) {
1204
+ const entry = store.get(key);
1205
+ if (!entry) return false;
1206
+ if (isExpired(entry)) {
1207
+ store.delete(key);
1208
+ return false;
1209
+ }
1210
+ return true;
1211
+ },
1212
+ delete(key) {
1213
+ return store.delete(key);
1214
+ },
1215
+ clear() {
1216
+ store.clear();
1217
+ },
1218
+ get size() {
1219
+ return store.size;
1220
+ },
1221
+ getStats() {
1222
+ return { hits, misses, size: store.size };
1223
+ }
1224
+ };
1225
+ }
1226
+
1227
+ // src/singleflight.ts
1228
+ function singleflight(operation, options) {
1229
+ const inflight = /* @__PURE__ */ new Map();
1230
+ const cache = options.ttl ? /* @__PURE__ */ new Map() : null;
1231
+ return async (...args) => {
1232
+ const key = options.key(...args);
1233
+ if (cache) {
1234
+ const cached2 = cache.get(key);
1235
+ if (cached2 && cached2.expiresAt > Date.now()) {
1236
+ return cached2.result;
1237
+ }
1238
+ if (cached2) {
1239
+ cache.delete(key);
1240
+ }
1241
+ }
1242
+ const existing = inflight.get(key);
1243
+ if (existing) {
1244
+ return existing;
1245
+ }
1246
+ const promise = operation(...args).then((result) => {
1247
+ if (cache && options.ttl && result.ok) {
1248
+ cache.set(key, {
1249
+ result,
1250
+ expiresAt: Date.now() + options.ttl
1251
+ });
1252
+ }
1253
+ return result;
1254
+ }).finally(() => {
1255
+ inflight.delete(key);
1256
+ });
1257
+ inflight.set(key, promise);
1258
+ return promise;
1259
+ };
1260
+ }
1261
+ function createSingleflightGroup() {
1262
+ const inflight = /* @__PURE__ */ new Map();
1263
+ return {
1264
+ execute: async (key, operation) => {
1265
+ const existing = inflight.get(key);
1266
+ if (existing) {
1267
+ return existing;
1268
+ }
1269
+ const promise = operation().then((result) => result).finally(() => {
1270
+ inflight.delete(key);
1271
+ });
1272
+ inflight.set(key, promise);
1273
+ return promise;
1274
+ },
1275
+ isInflight: (key) => inflight.has(key),
1276
+ size: () => inflight.size,
1277
+ clear: () => inflight.clear()
1278
+ };
1279
+ }
1280
+
1281
+ // src/policies.ts
1282
+ function mergePolicies(...policies) {
1283
+ const result = {};
1284
+ for (const policy of policies) {
1285
+ if (policy.key !== void 0) result.key = policy.key;
1286
+ if (policy.retry !== void 0) {
1287
+ result.retry = result.retry ? { ...result.retry, ...policy.retry } : { ...policy.retry };
1288
+ }
1289
+ if (policy.timeout !== void 0) {
1290
+ result.timeout = result.timeout ? { ...result.timeout, ...policy.timeout } : { ...policy.timeout };
1291
+ }
1292
+ }
1293
+ return result;
1294
+ }
1295
+ function createPolicyApplier(...basePolicies) {
1296
+ const basePolicy = mergePolicies(...basePolicies);
1297
+ return (stepOptions2) => {
1298
+ const opts = stepOptions2 ?? {};
1299
+ return mergePolicies(basePolicy, opts);
1300
+ };
1301
+ }
1302
+ function createPolicyBundle(name, ...policies) {
1303
+ return {
1304
+ name,
1305
+ policy: mergePolicies(...policies)
1306
+ };
1307
+ }
1308
+ function retryPolicy(options) {
1309
+ return { retry: options };
1310
+ }
1311
+ var retryPolicies = {
1312
+ /**
1313
+ * No retry - fail immediately on error.
1314
+ */
1315
+ none: retryPolicy({ attempts: 1 }),
1316
+ /**
1317
+ * Quick retry for transient errors (3 attempts, fast backoff).
1318
+ */
1319
+ transient: retryPolicy({
1320
+ attempts: 3,
1321
+ backoff: "exponential",
1322
+ initialDelay: 100,
1323
+ maxDelay: 1e3,
1324
+ jitter: true
1325
+ }),
1326
+ /**
1327
+ * Standard retry for API calls (3 attempts, moderate backoff).
1328
+ */
1329
+ standard: retryPolicy({
1330
+ attempts: 3,
1331
+ backoff: "exponential",
1332
+ initialDelay: 200,
1333
+ maxDelay: 5e3,
1334
+ jitter: true
1335
+ }),
1336
+ /**
1337
+ * Aggressive retry for critical operations (5 attempts, longer backoff).
1338
+ */
1339
+ aggressive: retryPolicy({
1340
+ attempts: 5,
1341
+ backoff: "exponential",
1342
+ initialDelay: 500,
1343
+ maxDelay: 3e4,
1344
+ jitter: true
1345
+ }),
1346
+ /**
1347
+ * Fixed interval retry (useful for polling).
1348
+ */
1349
+ fixed: (attempts, delayMs) => retryPolicy({
1350
+ attempts,
1351
+ backoff: "fixed",
1352
+ initialDelay: delayMs,
1353
+ jitter: false
1354
+ }),
1355
+ /**
1356
+ * Linear backoff retry.
1357
+ */
1358
+ linear: (attempts, initialDelay) => retryPolicy({
1359
+ attempts,
1360
+ backoff: "linear",
1361
+ initialDelay,
1362
+ jitter: true
1363
+ }),
1364
+ /**
1365
+ * Custom retry policy builder.
1366
+ */
1367
+ custom: (options) => retryPolicy({
1368
+ backoff: "exponential",
1369
+ initialDelay: 100,
1370
+ maxDelay: 3e4,
1371
+ jitter: true,
1372
+ ...options
1373
+ })
1374
+ };
1375
+ function timeoutPolicy(options) {
1376
+ return { timeout: options };
1377
+ }
1378
+ var timeoutPolicies = {
1379
+ /**
1380
+ * No timeout.
1381
+ */
1382
+ none: {},
1383
+ /**
1384
+ * Fast timeout for quick operations (1 second).
1385
+ */
1386
+ fast: timeoutPolicy({ ms: 1e3 }),
1387
+ /**
1388
+ * Standard API timeout (5 seconds).
1389
+ */
1390
+ api: timeoutPolicy({ ms: 5e3 }),
1391
+ /**
1392
+ * Extended timeout for slower operations (30 seconds).
1393
+ */
1394
+ extended: timeoutPolicy({ ms: 3e4 }),
1395
+ /**
1396
+ * Long timeout for batch operations (2 minutes).
1397
+ */
1398
+ long: timeoutPolicy({ ms: 12e4 }),
1399
+ /**
1400
+ * Custom timeout in milliseconds.
1401
+ */
1402
+ ms: (ms) => timeoutPolicy({ ms }),
1403
+ /**
1404
+ * Custom timeout in seconds.
1405
+ */
1406
+ seconds: (seconds2) => timeoutPolicy({ ms: seconds2 * 1e3 }),
1407
+ /**
1408
+ * Timeout with custom error.
1409
+ */
1410
+ withError: (ms, error) => timeoutPolicy({ ms, error }),
1411
+ /**
1412
+ * Timeout with AbortSignal support.
1413
+ */
1414
+ withSignal: (ms) => timeoutPolicy({ ms, signal: true })
1415
+ };
1416
+ var servicePolicies = {
1417
+ /**
1418
+ * Policy for external HTTP APIs.
1419
+ * - 5 second timeout
1420
+ * - 3 retries with exponential backoff
1421
+ */
1422
+ httpApi: mergePolicies(
1423
+ timeoutPolicies.api,
1424
+ retryPolicies.standard
1425
+ ),
1426
+ /**
1427
+ * Policy for database operations.
1428
+ * - 30 second timeout
1429
+ * - 2 retries for transient errors
1430
+ */
1431
+ database: mergePolicies(
1432
+ timeoutPolicies.extended,
1433
+ retryPolicy({
1434
+ attempts: 2,
1435
+ backoff: "exponential",
1436
+ initialDelay: 100,
1437
+ maxDelay: 2e3,
1438
+ jitter: true
1439
+ })
1440
+ ),
1441
+ /**
1442
+ * Policy for cache operations.
1443
+ * - 1 second timeout
1444
+ * - No retry (cache misses are not errors)
1445
+ */
1446
+ cache: mergePolicies(
1447
+ timeoutPolicies.fast,
1448
+ retryPolicies.none
1449
+ ),
1450
+ /**
1451
+ * Policy for message queue operations.
1452
+ * - 30 second timeout
1453
+ * - 5 retries with longer backoff
1454
+ */
1455
+ messageQueue: mergePolicies(
1456
+ timeoutPolicies.extended,
1457
+ retryPolicies.aggressive
1458
+ ),
1459
+ /**
1460
+ * Policy for file operations.
1461
+ * - 2 minute timeout
1462
+ * - 3 retries
1463
+ */
1464
+ fileSystem: mergePolicies(
1465
+ timeoutPolicies.long,
1466
+ retryPolicies.standard
1467
+ ),
1468
+ /**
1469
+ * Policy for third-party services with rate limits.
1470
+ * - 10 second timeout
1471
+ * - 5 retries with linear backoff
1472
+ */
1473
+ rateLimited: mergePolicies(
1474
+ timeoutPolicy({ ms: 1e4 }),
1475
+ retryPolicy({
1476
+ attempts: 5,
1477
+ backoff: "linear",
1478
+ initialDelay: 1e3,
1479
+ maxDelay: 1e4,
1480
+ jitter: true
1481
+ })
1482
+ )
1483
+ };
1484
+ function withPolicy(policy, stepOptions2) {
1485
+ const opts = stepOptions2 ?? {};
1486
+ return mergePolicies(policy, opts);
1487
+ }
1488
+ function withPolicies(policies, stepOptions2) {
1489
+ const opts = stepOptions2 ?? {};
1490
+ return mergePolicies(...policies, opts);
1491
+ }
1492
+ function conditionalPolicy(condition, policy, elsePolicy = {}) {
1493
+ return condition ? policy : elsePolicy;
1494
+ }
1495
+ function envPolicy(envPolicies, currentEnv = process.env.NODE_ENV ?? "development", defaultPolicy = {}) {
1496
+ return envPolicies[currentEnv] ?? defaultPolicy;
1497
+ }
1498
+ function createPolicyRegistry() {
1499
+ const policies = /* @__PURE__ */ new Map();
1500
+ return {
1501
+ register(name, policy) {
1502
+ policies.set(name, policy);
1503
+ },
1504
+ get(name) {
1505
+ return policies.get(name);
1506
+ },
1507
+ has(name) {
1508
+ return policies.has(name);
1509
+ },
1510
+ names() {
1511
+ return Array.from(policies.keys());
1512
+ },
1513
+ apply(policyName, stepOptions2) {
1514
+ const policy = policies.get(policyName);
1515
+ if (!policy) {
1516
+ throw new Error(`Policy not found: ${policyName}`);
1517
+ }
1518
+ return withPolicy(policy, stepOptions2);
1519
+ }
1520
+ };
1521
+ }
1522
+ function stepOptions() {
1523
+ const policies = [];
1524
+ const builder = {
1525
+ key(key) {
1526
+ policies.push({ key });
1527
+ return builder;
1528
+ },
1529
+ policy(policy) {
1530
+ policies.push(policy);
1531
+ return builder;
1532
+ },
1533
+ timeout(ms) {
1534
+ policies.push(timeoutPolicy({ ms }));
1535
+ return builder;
1536
+ },
1537
+ retry(options) {
1538
+ policies.push(retryPolicy(options));
1539
+ return builder;
1540
+ },
1541
+ retries(attempts) {
1542
+ policies.push(retryPolicies.custom({ attempts }));
1543
+ return builder;
1544
+ },
1545
+ build() {
1546
+ return mergePolicies(...policies);
1547
+ }
1548
+ };
1549
+ return builder;
1550
+ }
1551
+ export {
1552
+ CircuitOpenError,
1553
+ cached,
1554
+ cachedFunction,
1555
+ cachedWithTTL,
1556
+ circuitBreakerPresets,
1557
+ conditionalPolicy,
1558
+ createCache,
1559
+ createCircuitBreaker,
1560
+ createCombinedLimiter,
1561
+ createConcurrencyLimiter,
1562
+ createCostBasedRateLimiter,
1563
+ createFixedWindowLimiter,
1564
+ createPolicyApplier,
1565
+ createPolicyBundle,
1566
+ createPolicyRegistry,
1567
+ createRateLimiter,
1568
+ createSingleflightGroup,
1569
+ envPolicy,
1570
+ fallback,
1571
+ isCircuitOpenError,
1572
+ isQueueFullError,
1573
+ isRateLimitExceededError,
1574
+ mergePolicies,
1575
+ once,
1576
+ rateLimiterPresets,
1577
+ retry,
1578
+ retryPolicies,
1579
+ retryPolicy,
1580
+ servicePolicies,
1581
+ singleflight,
1582
+ stepOptions,
1583
+ timeout,
1584
+ timeoutPolicies,
1585
+ timeoutPolicy,
1586
+ withPolicies,
1587
+ withPolicy
1588
+ };
1589
+ //# sourceMappingURL=reliability.js.map