@fabricorg/platform 1.7.0 → 1.8.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 (59) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +214 -3
  3. package/dist/actions/index.cjs +6 -6
  4. package/dist/actions/index.d.cts +9 -2
  5. package/dist/actions/index.d.ts +9 -2
  6. package/dist/actions/index.js +1 -1
  7. package/dist/adapters/index.cjs +33 -9
  8. package/dist/adapters/index.d.cts +388 -1
  9. package/dist/adapters/index.d.ts +388 -1
  10. package/dist/adapters/index.js +1 -1
  11. package/dist/chunk-5FOCPRTR.js +511 -0
  12. package/dist/chunk-5FOCPRTR.js.map +1 -0
  13. package/dist/{chunk-USDLP4E7.cjs → chunk-5IVFPWK6.cjs} +4 -4
  14. package/dist/{chunk-USDLP4E7.cjs.map → chunk-5IVFPWK6.cjs.map} +1 -1
  15. package/dist/chunk-6HY35TKB.cjs +491 -0
  16. package/dist/chunk-6HY35TKB.cjs.map +1 -0
  17. package/dist/chunk-6UGEIDLC.cjs +26 -0
  18. package/dist/chunk-6UGEIDLC.cjs.map +1 -0
  19. package/dist/chunk-BJGZE3GE.js +20 -0
  20. package/dist/chunk-BJGZE3GE.js.map +1 -0
  21. package/dist/{chunk-X3GHYMAJ.cjs → chunk-CRZXTZ36.cjs} +2 -22
  22. package/dist/chunk-CRZXTZ36.cjs.map +1 -0
  23. package/dist/{chunk-DCC3UIUJ.js → chunk-GFU7BGEJ.js} +3 -3
  24. package/dist/{chunk-DCC3UIUJ.js.map → chunk-GFU7BGEJ.js.map} +1 -1
  25. package/dist/chunk-HEYMMVDP.js +480 -0
  26. package/dist/chunk-HEYMMVDP.js.map +1 -0
  27. package/dist/chunk-IF6NJORI.cjs +526 -0
  28. package/dist/chunk-IF6NJORI.cjs.map +1 -0
  29. package/dist/{chunk-6QX4I45I.js → chunk-PIDZQAEL.js} +2 -2
  30. package/dist/{chunk-6QX4I45I.js.map → chunk-PIDZQAEL.js.map} +1 -1
  31. package/dist/{chunk-ATWWZTRC.js → chunk-TYNIFC7J.js} +3 -18
  32. package/dist/chunk-TYNIFC7J.js.map +1 -0
  33. package/dist/{chunk-XCAOO6KG.cjs → chunk-UGWGHTNC.cjs} +2 -2
  34. package/dist/{chunk-XCAOO6KG.cjs.map → chunk-UGWGHTNC.cjs.map} +1 -1
  35. package/dist/evidence/index.cjs +45 -1
  36. package/dist/evidence/index.d.cts +187 -1
  37. package/dist/evidence/index.d.ts +187 -1
  38. package/dist/evidence/index.js +3 -1
  39. package/dist/governance/index.d.cts +1 -0
  40. package/dist/governance/index.d.ts +1 -0
  41. package/dist/index.cjs +124 -59
  42. package/dist/index.d.cts +2 -2
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.js +7 -6
  45. package/dist/privacy/index.cjs +19 -18
  46. package/dist/privacy/index.js +2 -1
  47. package/dist/testing/index.cjs +5 -5
  48. package/dist/testing/index.js +2 -2
  49. package/package.json +1 -1
  50. package/dist/chunk-ATWWZTRC.js.map +0 -1
  51. package/dist/chunk-FAG2RTPM.js +0 -120
  52. package/dist/chunk-FAG2RTPM.js.map +0 -1
  53. package/dist/chunk-OZLZHISS.js +0 -3
  54. package/dist/chunk-OZLZHISS.js.map +0 -1
  55. package/dist/chunk-PG5LYTUX.cjs +0 -4
  56. package/dist/chunk-PG5LYTUX.cjs.map +0 -1
  57. package/dist/chunk-X3GHYMAJ.cjs.map +0 -1
  58. package/dist/chunk-XZQZQ5SW.cjs +0 -129
  59. package/dist/chunk-XZQZQ5SW.cjs.map +0 -1
@@ -0,0 +1,526 @@
1
+ 'use strict';
2
+
3
+ // adapters/circuit-breaker.ts
4
+ function classifyCircuitBreakerOutcome(kind) {
5
+ switch (kind) {
6
+ case "success":
7
+ return "success";
8
+ case "permanent_failure":
9
+ return "ignored";
10
+ case "transient_failure":
11
+ case "timeout":
12
+ case "ambiguous":
13
+ return "failure";
14
+ default:
15
+ return "failure";
16
+ }
17
+ }
18
+ var MANUAL_OVERRIDES = ["force_open", "force_closed"];
19
+ function assertAdapterCircuitBreakerConfig(config) {
20
+ if (!config || typeof config !== "object") {
21
+ throw new RangeError("Adapter circuit breaker config must be an object.");
22
+ }
23
+ if (config.mode !== "monitor" && config.mode !== "enforce") {
24
+ throw new RangeError(`Adapter circuit breaker mode must be "monitor" or "enforce"; got ${JSON.stringify(config.mode)}.`);
25
+ }
26
+ if (!Number.isInteger(config.failureThreshold) || config.failureThreshold < 1) {
27
+ throw new RangeError(`Adapter circuit breaker failureThreshold must be a positive integer; got ${String(config.failureThreshold)}.`);
28
+ }
29
+ if (typeof config.windowMs !== "number" || !Number.isFinite(config.windowMs) || config.windowMs <= 0) {
30
+ throw new RangeError(`Adapter circuit breaker windowMs must be a positive number of milliseconds; got ${String(config.windowMs)}.`);
31
+ }
32
+ if (typeof config.cooldownMs !== "number" || !Number.isFinite(config.cooldownMs) || config.cooldownMs < 0) {
33
+ throw new RangeError(`Adapter circuit breaker cooldownMs must be a non-negative number of milliseconds; got ${String(config.cooldownMs)}.`);
34
+ }
35
+ if (config.minimumSamples !== void 0 && (!Number.isInteger(config.minimumSamples) || config.minimumSamples < 1)) {
36
+ throw new RangeError(`Adapter circuit breaker minimumSamples must be a positive integer; got ${String(config.minimumSamples)}.`);
37
+ }
38
+ if (config.probeLeaseMs !== void 0 && (typeof config.probeLeaseMs !== "number" || !Number.isFinite(config.probeLeaseMs) || config.probeLeaseMs <= 0)) {
39
+ throw new RangeError(`Adapter circuit breaker probeLeaseMs must be a positive number of milliseconds; got ${String(config.probeLeaseMs)}.`);
40
+ }
41
+ if (config.manualOverride !== void 0 && !MANUAL_OVERRIDES.includes(config.manualOverride)) {
42
+ throw new RangeError(`Adapter circuit breaker manualOverride must be "force_open" or "force_closed"; got ${JSON.stringify(config.manualOverride)}.`);
43
+ }
44
+ }
45
+ var InMemoryAdapterCircuitBreaker = class {
46
+ key;
47
+ mode;
48
+ config;
49
+ now;
50
+ onEvent;
51
+ probeLeaseMs;
52
+ samples = [];
53
+ state = "closed";
54
+ openedAt = 0;
55
+ probeIssuedAt;
56
+ /** Advances on every state change, manual override change and probe grant. */
57
+ generation = 0;
58
+ manualOverride;
59
+ /** Telemetry waiting for the current operation to commit. */
60
+ outbox = [];
61
+ operationDepth = 0;
62
+ flushing = false;
63
+ constructor(options) {
64
+ assertAdapterCircuitBreakerConfig(options.config);
65
+ this.key = options.key;
66
+ this.config = { ...options.config };
67
+ this.mode = options.config.mode;
68
+ this.now = options.now ?? Date.now;
69
+ this.onEvent = options.onEvent;
70
+ this.manualOverride = options.config.manualOverride;
71
+ this.probeLeaseMs = options.config.probeLeaseMs ?? Math.max(options.config.cooldownMs, options.config.windowMs);
72
+ }
73
+ getState() {
74
+ this.settle(this.now());
75
+ return this.reportedState();
76
+ }
77
+ getManualOverride() {
78
+ return this.manualOverride;
79
+ }
80
+ /**
81
+ * Set (`force_open` / `force_closed`) or clear (`undefined`) the manual
82
+ * override. Clearing returns the breaker to `closed` with its window intact.
83
+ */
84
+ setManualOverride(override) {
85
+ if (override !== void 0 && !MANUAL_OVERRIDES.includes(override)) {
86
+ throw new RangeError(`Adapter circuit breaker manualOverride must be "force_open" or "force_closed"; got ${JSON.stringify(override)}.`);
87
+ }
88
+ const now = this.now();
89
+ this.settle(now);
90
+ if (override === this.manualOverride) return;
91
+ this.operation(() => {
92
+ const from = this.reportedState();
93
+ this.manualOverride = override;
94
+ this.generation++;
95
+ if (override === void 0) {
96
+ this.state = "closed";
97
+ this.releaseProbe();
98
+ }
99
+ this.emit({
100
+ type: "state_change",
101
+ key: this.key,
102
+ mode: this.mode,
103
+ at: now,
104
+ from,
105
+ to: this.reportedState(),
106
+ reason: override === void 0 ? "manual_override_cleared" : "manual_override",
107
+ ...override ? { manualOverride: override } : {}
108
+ });
109
+ });
110
+ }
111
+ check() {
112
+ return this.decide(false, this.now());
113
+ }
114
+ acquire() {
115
+ const now = this.now();
116
+ this.settle(now);
117
+ return this.operation(() => this.decide(true, now));
118
+ }
119
+ record(outcome, admission) {
120
+ const now = this.now();
121
+ this.settle(now);
122
+ this.operation(() => this.applyOutcome(outcome, admission, now));
123
+ }
124
+ applyOutcome(outcome, admission, now) {
125
+ const generation = admission?.generation;
126
+ if (generation !== void 0 && generation !== this.generation) return;
127
+ const resolvesProbe = this.state === "half_open" && this.probeIssuedAt !== void 0 && (generation === void 0 || admission?.probe === true);
128
+ if (outcome === "ignored") {
129
+ if (resolvesProbe) this.releaseProbe();
130
+ return;
131
+ }
132
+ this.prune(now);
133
+ this.samples.push({ at: now, failure: outcome === "failure" });
134
+ if (this.manualOverride) return;
135
+ if (resolvesProbe) {
136
+ this.releaseProbe();
137
+ if (outcome === "success") {
138
+ this.samples = [];
139
+ this.transition("closed", "probe_succeeded", now);
140
+ } else {
141
+ this.openedAt = now;
142
+ this.transition("open", "probe_failed", now);
143
+ }
144
+ return;
145
+ }
146
+ if (this.state === "closed" && outcome === "failure" && this.shouldTrip()) {
147
+ this.openedAt = now;
148
+ this.transition("open", "failure_threshold", now);
149
+ }
150
+ }
151
+ /**
152
+ * `take` is false for {@link check}: a pure preview that sees an elapsed
153
+ * cooldown without committing it, takes no probe and emits nothing.
154
+ */
155
+ decide(take, now) {
156
+ if (this.manualOverride === "force_closed") {
157
+ return { allowed: true, state: "closed", probe: false, manualOverride: "force_closed", generation: this.generation };
158
+ }
159
+ if (this.manualOverride === "force_open") {
160
+ return this.refuse(take, { state: "open", manualOverride: "force_open" });
161
+ }
162
+ const cooldownElapsed = this.state === "open" && now - this.openedAt >= this.config.cooldownMs;
163
+ if (take) this.advance(now);
164
+ const state = !take && cooldownElapsed ? "half_open" : this.state;
165
+ if (state === "closed") return { allowed: true, state: "closed", probe: false, generation: this.generation };
166
+ if (state === "open") {
167
+ return this.refuse(take, {
168
+ state: "open",
169
+ retryAfterMs: Math.max(0, this.openedAt + this.config.cooldownMs - now)
170
+ });
171
+ }
172
+ const probeFree = !take && cooldownElapsed || this.probeIssuedAt === void 0 || now - this.probeIssuedAt >= this.probeLeaseMs;
173
+ if (!probeFree) return this.refuse(take, { state: "half_open" });
174
+ if (!take) return { allowed: true, state: "half_open", probe: true, generation: this.generation };
175
+ this.probeIssuedAt = now;
176
+ this.generation++;
177
+ return { allowed: true, state: "half_open", probe: true, generation: this.generation };
178
+ }
179
+ refuse(take, detail) {
180
+ const enforce = this.mode === "enforce";
181
+ if (take) {
182
+ this.emit({
183
+ type: enforce ? "rejected" : "would_reject",
184
+ key: this.key,
185
+ mode: this.mode,
186
+ at: this.now(),
187
+ ...detail
188
+ });
189
+ }
190
+ const generation = this.generation;
191
+ return enforce ? { allowed: false, probe: false, ...detail, generation } : { allowed: true, probe: false, wouldReject: true, ...detail, generation };
192
+ }
193
+ releaseProbe() {
194
+ this.probeIssuedAt = void 0;
195
+ }
196
+ /** Apply the time-based open → half_open transition. */
197
+ advance(now) {
198
+ if (this.state === "open" && now - this.openedAt >= this.config.cooldownMs) {
199
+ this.releaseProbe();
200
+ this.transition("half_open", "cooldown_elapsed", now);
201
+ }
202
+ }
203
+ shouldTrip() {
204
+ const minimumSamples = this.config.minimumSamples ?? this.config.failureThreshold;
205
+ const total = this.samples.length;
206
+ if (total < minimumSamples) return false;
207
+ let consecutive = 0;
208
+ for (let index = total - 1; index >= 0 && this.samples[index].failure; index--) consecutive++;
209
+ if (consecutive >= this.config.failureThreshold) return true;
210
+ const failures = this.samples.reduce((count, sample) => count + (sample.failure ? 1 : 0), 0);
211
+ return failures / total >= this.config.failureThreshold / minimumSamples;
212
+ }
213
+ prune(now) {
214
+ const cutoff = now - this.config.windowMs;
215
+ let drop = 0;
216
+ while (drop < this.samples.length && this.samples[drop].at < cutoff) drop++;
217
+ if (drop > 0) this.samples.splice(0, drop);
218
+ }
219
+ transition(to, reason, at) {
220
+ const from = this.state;
221
+ if (from === to) return;
222
+ this.state = to;
223
+ this.generation++;
224
+ this.emit({ type: "state_change", key: this.key, mode: this.mode, at, from, to, reason });
225
+ }
226
+ reportedState() {
227
+ if (this.manualOverride) return this.manualOverride === "force_open" ? "open" : "closed";
228
+ return this.state;
229
+ }
230
+ /**
231
+ * Commit an elapsed cooldown as an operation of its own, delivering its
232
+ * event before the caller's operation reads state.
233
+ */
234
+ settle(now) {
235
+ if (this.manualOverride) return;
236
+ this.operation(() => this.advance(now));
237
+ }
238
+ /**
239
+ * Run `step` to completion, then deliver the telemetry it produced. The
240
+ * return value is fixed before any listener runs.
241
+ */
242
+ operation(step) {
243
+ this.operationDepth++;
244
+ try {
245
+ return step();
246
+ } finally {
247
+ this.operationDepth--;
248
+ if (this.operationDepth === 0) this.flush();
249
+ }
250
+ }
251
+ emit(event) {
252
+ if (this.onEvent) this.outbox.push(event);
253
+ }
254
+ /**
255
+ * Deliver buffered events in order. A listener that calls back into the
256
+ * breaker queues its own events; this loop delivers them after it returns
257
+ * instead of recursing.
258
+ */
259
+ flush() {
260
+ if (this.flushing) return;
261
+ this.flushing = true;
262
+ try {
263
+ for (let event = this.outbox.shift(); event; event = this.outbox.shift()) this.deliver(event);
264
+ } finally {
265
+ this.flushing = false;
266
+ }
267
+ }
268
+ deliver(event) {
269
+ const listener = this.onEvent;
270
+ if (!listener) return;
271
+ try {
272
+ const result = listener(event);
273
+ if (result && typeof result.then === "function") {
274
+ result.then(void 0, () => void 0);
275
+ }
276
+ } catch {
277
+ }
278
+ }
279
+ };
280
+ var AdapterCircuitBreakerRegistry = class {
281
+ constructor(options = {}) {
282
+ this.options = options;
283
+ }
284
+ options;
285
+ breakers = /* @__PURE__ */ new Map();
286
+ forAdapter(adapter) {
287
+ if (!adapter.circuitBreaker) return void 0;
288
+ const key = `${adapter.adapterType}:${adapter.operation}`;
289
+ let breaker = this.breakers.get(key);
290
+ if (!breaker) {
291
+ breaker = new InMemoryAdapterCircuitBreaker({
292
+ key,
293
+ config: adapter.circuitBreaker,
294
+ ...this.options.now ? { now: this.options.now } : {},
295
+ ...this.options.onEvent ? { onEvent: this.options.onEvent } : {}
296
+ });
297
+ this.breakers.set(key, breaker);
298
+ }
299
+ return breaker;
300
+ }
301
+ /** The breaker already created for `adapterType:operation`, if any. */
302
+ get(adapterType, operation) {
303
+ return this.breakers.get(`${adapterType}:${operation}`);
304
+ }
305
+ };
306
+ var AdapterCircuitOpenError = class extends Error {
307
+ name = "AdapterCircuitOpenError";
308
+ code = "ADAPTER_CIRCUIT_OPEN";
309
+ reason = "circuit_open";
310
+ adapterKey;
311
+ state;
312
+ /** The attempt number that was refused (1 = refused before any call). */
313
+ attempt;
314
+ retryAfterMs;
315
+ manualOverride;
316
+ /** Error of the previous attempt, when the breaker opened mid-retry. */
317
+ lastError;
318
+ constructor(input) {
319
+ const after = input.retryAfterMs !== void 0 ? `; retry after ${input.retryAfterMs} ms` : "";
320
+ super(`Adapter circuit is ${input.state === "half_open" ? "half-open with its probe in flight" : "open"} for ${input.adapterKey}; the call was not attempted${after}.`);
321
+ this.adapterKey = input.adapterKey;
322
+ this.state = input.state;
323
+ this.attempt = input.attempt;
324
+ if (input.retryAfterMs !== void 0) this.retryAfterMs = input.retryAfterMs;
325
+ if (input.manualOverride !== void 0) this.manualOverride = input.manualOverride;
326
+ if (input.lastError !== void 0) this.lastError = input.lastError;
327
+ }
328
+ };
329
+ function isAdapterCircuitOpenError(error) {
330
+ if (error instanceof AdapterCircuitOpenError) return true;
331
+ return typeof error === "object" && error !== null && error.name === "AdapterCircuitOpenError" && error.code === "ADAPTER_CIRCUIT_OPEN";
332
+ }
333
+
334
+ // adapters/index.ts
335
+ function adapterKey(adapterType, operation) {
336
+ return `${adapterType}:${operation}`;
337
+ }
338
+ var MissingAdapterRegistrationError = class extends Error {
339
+ constructor(adapterType, operation) {
340
+ super(`No adapter registered for adapterType "${adapterType}" and operation "${operation}"`);
341
+ this.name = "MissingAdapterRegistrationError";
342
+ }
343
+ };
344
+ var AdapterRegistry = class {
345
+ adapters = /* @__PURE__ */ new Map();
346
+ register(adapter) {
347
+ const key = adapterKey(adapter.adapterType, adapter.operation);
348
+ if (this.adapters.has(key)) {
349
+ throw new Error(
350
+ `Adapter already registered for adapterType "${adapter.adapterType}" and operation "${adapter.operation}"`
351
+ );
352
+ }
353
+ this.adapters.set(key, adapter);
354
+ }
355
+ resolve(adapterType, operation) {
356
+ return this.adapters.get(adapterKey(adapterType, operation));
357
+ }
358
+ require(adapterType, operation) {
359
+ const adapter = this.resolve(adapterType, operation);
360
+ if (!adapter) {
361
+ throw new MissingAdapterRegistrationError(adapterType, operation);
362
+ }
363
+ return adapter;
364
+ }
365
+ };
366
+ var DEFAULT_RETRY_POLICY = {
367
+ maxAttempts: 3,
368
+ initialDelayMs: 100,
369
+ backoffMultiplier: 2,
370
+ maxDelayMs: 1e3
371
+ };
372
+ var NO_RETRY_POLICY = {
373
+ idempotent: false,
374
+ maxAttempts: 1,
375
+ initialDelayMs: 0,
376
+ backoffMultiplier: 1,
377
+ maxDelayMs: 0
378
+ };
379
+ function resolveAdapterRetryPolicy(policy, defaultIdempotent) {
380
+ const idempotent = policy?.idempotent ?? defaultIdempotent;
381
+ if (!idempotent) {
382
+ return NO_RETRY_POLICY;
383
+ }
384
+ return {
385
+ idempotent: true,
386
+ maxAttempts: Math.max(1, Math.min(policy?.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts, 5)),
387
+ initialDelayMs: Math.max(0, policy?.initialDelayMs ?? DEFAULT_RETRY_POLICY.initialDelayMs),
388
+ backoffMultiplier: Math.max(
389
+ 1,
390
+ policy?.backoffMultiplier ?? DEFAULT_RETRY_POLICY.backoffMultiplier
391
+ ),
392
+ maxDelayMs: Math.max(0, policy?.maxDelayMs ?? DEFAULT_RETRY_POLICY.maxDelayMs)
393
+ };
394
+ }
395
+ function getRetryDelayMs(attempt, policy) {
396
+ const exponentialDelay = policy.initialDelayMs * policy.backoffMultiplier ** Math.max(0, attempt - 1);
397
+ return Math.min(exponentialDelay, policy.maxDelayMs);
398
+ }
399
+ function classifyAdapterOutcome(result) {
400
+ if (result.outcome) {
401
+ assertAdapterOutcomeConsistency(result);
402
+ return result.outcome;
403
+ }
404
+ return result.success ? "success" : "transient_failure";
405
+ }
406
+ function shouldRetryAdapterOutcome(kind, idempotent) {
407
+ return kind === "transient_failure" && idempotent;
408
+ }
409
+ function assertAdapterOutcomeConsistency(result) {
410
+ if (result.success && result.outcome && result.outcome !== "success") {
411
+ throw new Error(
412
+ `Adapter returned success=true with contradictory outcome "${result.outcome}"; a successful result must not declare a non-success outcome.`
413
+ );
414
+ }
415
+ }
416
+ function recordCircuitOutcome(breaker, outcome, admission) {
417
+ if (!breaker) return;
418
+ try {
419
+ breaker.record(outcome, admission);
420
+ } catch {
421
+ }
422
+ }
423
+ function circuitOpenError(breaker, admission, attempt, lastError) {
424
+ return new AdapterCircuitOpenError({
425
+ adapterKey: breaker.key,
426
+ state: admission.state,
427
+ attempt,
428
+ ...admission.retryAfterMs !== void 0 ? { retryAfterMs: admission.retryAfterMs } : {},
429
+ ...admission.manualOverride !== void 0 ? { manualOverride: admission.manualOverride } : {},
430
+ ...lastError !== void 0 ? { lastError } : {}
431
+ });
432
+ }
433
+ async function executeWithAdapterRetry(options) {
434
+ const policy = resolveAdapterRetryPolicy(options.policy, options.defaultIdempotent);
435
+ const sleep = options.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
436
+ const breaker = options.circuitBreaker;
437
+ let lastError;
438
+ for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
439
+ let admission;
440
+ let executed = false;
441
+ let recorded = false;
442
+ const record = (outcome) => {
443
+ if (!executed || recorded) return;
444
+ recorded = true;
445
+ recordCircuitOutcome(breaker, outcome, admission);
446
+ };
447
+ let refusal;
448
+ async function handleThrown(error) {
449
+ lastError = error instanceof Error ? error.message : String(error);
450
+ const thrownOutcome = options.classifyThrownError ? options.classifyThrownError(error) : "transient_failure";
451
+ record(classifyCircuitBreakerOutcome(thrownOutcome));
452
+ let willRetry = shouldRetryAdapterOutcome(thrownOutcome, policy.idempotent) && attempt < policy.maxAttempts;
453
+ const blocked = willRetry ? refusedRetry(breaker) : void 0;
454
+ if (blocked) willRetry = false;
455
+ const nextDelayMs = willRetry ? getRetryDelayMs(attempt, policy) : void 0;
456
+ await options.onAttempt?.({ attempt, error: lastError, willRetry, nextDelayMs });
457
+ if (blocked) return blocked;
458
+ if (!willRetry) return "rethrow";
459
+ await sleep(nextDelayMs ?? 0);
460
+ return "retry";
461
+ }
462
+ try {
463
+ if (options.prepareAttempt) await options.prepareAttempt(attempt, policy.maxAttempts);
464
+ } catch (error) {
465
+ const decision = await handleThrown(error);
466
+ if (decision === "rethrow") throw error;
467
+ if (decision === "retry") continue;
468
+ refusal = decision;
469
+ }
470
+ if (!refusal) {
471
+ if (breaker) {
472
+ admission = breaker.acquire();
473
+ if (!admission.allowed) throw circuitOpenError(breaker, admission, attempt, lastError);
474
+ }
475
+ try {
476
+ executed = true;
477
+ const result = await options.execute(attempt, policy.maxAttempts);
478
+ if (options.isSuccessful(result)) {
479
+ record("success");
480
+ await options.onAttempt?.({ attempt, result, willRetry: false });
481
+ return result;
482
+ }
483
+ lastError = options.getError(result) ?? "Adapter returned unsuccessful result";
484
+ const outcome = options.classifyOutcome ? options.classifyOutcome(result) : "transient_failure";
485
+ record(classifyCircuitBreakerOutcome(outcome));
486
+ let willRetry = shouldRetryAdapterOutcome(outcome, policy.idempotent) && attempt < policy.maxAttempts;
487
+ refusal = willRetry ? refusedRetry(breaker) : void 0;
488
+ if (refusal) willRetry = false;
489
+ const nextDelayMs = willRetry ? getRetryDelayMs(attempt, policy) : void 0;
490
+ await options.onAttempt?.({ attempt, result, error: lastError, willRetry, nextDelayMs });
491
+ if (!willRetry && !refusal) {
492
+ return result;
493
+ }
494
+ if (!refusal) await sleep(nextDelayMs ?? 0);
495
+ } catch (error) {
496
+ const decision = await handleThrown(error);
497
+ if (decision === "rethrow") throw error;
498
+ refusal = decision === "retry" ? void 0 : decision;
499
+ }
500
+ }
501
+ if (refusal && breaker) throw circuitOpenError(breaker, refusal, attempt + 1, lastError);
502
+ }
503
+ throw new Error(lastError ?? "Adapter retry exhausted");
504
+ }
505
+ function refusedRetry(breaker) {
506
+ if (!breaker) return void 0;
507
+ const preview = breaker.check();
508
+ return preview.allowed ? void 0 : preview;
509
+ }
510
+
511
+ exports.AdapterCircuitBreakerRegistry = AdapterCircuitBreakerRegistry;
512
+ exports.AdapterCircuitOpenError = AdapterCircuitOpenError;
513
+ exports.AdapterRegistry = AdapterRegistry;
514
+ exports.InMemoryAdapterCircuitBreaker = InMemoryAdapterCircuitBreaker;
515
+ exports.MissingAdapterRegistrationError = MissingAdapterRegistrationError;
516
+ exports.assertAdapterCircuitBreakerConfig = assertAdapterCircuitBreakerConfig;
517
+ exports.assertAdapterOutcomeConsistency = assertAdapterOutcomeConsistency;
518
+ exports.classifyAdapterOutcome = classifyAdapterOutcome;
519
+ exports.classifyCircuitBreakerOutcome = classifyCircuitBreakerOutcome;
520
+ exports.executeWithAdapterRetry = executeWithAdapterRetry;
521
+ exports.getRetryDelayMs = getRetryDelayMs;
522
+ exports.isAdapterCircuitOpenError = isAdapterCircuitOpenError;
523
+ exports.resolveAdapterRetryPolicy = resolveAdapterRetryPolicy;
524
+ exports.shouldRetryAdapterOutcome = shouldRetryAdapterOutcome;
525
+ //# sourceMappingURL=chunk-IF6NJORI.cjs.map
526
+ //# sourceMappingURL=chunk-IF6NJORI.cjs.map