@fabricorg/platform 1.7.0 → 1.9.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 (69) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +250 -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 +37 -9
  8. package/dist/adapters/index.d.cts +421 -1
  9. package/dist/adapters/index.d.ts +421 -1
  10. package/dist/adapters/index.js +1 -1
  11. package/dist/chunk-6HY35TKB.cjs +491 -0
  12. package/dist/chunk-6HY35TKB.cjs.map +1 -0
  13. package/dist/chunk-6UGEIDLC.cjs +26 -0
  14. package/dist/chunk-6UGEIDLC.cjs.map +1 -0
  15. package/dist/{chunk-GU3LZWJD.js → chunk-AFXFAEWA.js} +16 -3
  16. package/dist/chunk-AFXFAEWA.js.map +1 -0
  17. package/dist/chunk-AFXKSIVI.cjs +540 -0
  18. package/dist/chunk-AFXKSIVI.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-HEYMMVDP.js +480 -0
  24. package/dist/chunk-HEYMMVDP.js.map +1 -0
  25. package/dist/{chunk-USDLP4E7.cjs → chunk-NNXQZPB3.cjs} +4 -4
  26. package/dist/{chunk-USDLP4E7.cjs.map → chunk-NNXQZPB3.cjs.map} +1 -1
  27. package/dist/{chunk-6QX4I45I.js → chunk-PIDZQAEL.js} +2 -2
  28. package/dist/{chunk-6QX4I45I.js.map → chunk-PIDZQAEL.js.map} +1 -1
  29. package/dist/chunk-Q4Q4JPR3.js +524 -0
  30. package/dist/chunk-Q4Q4JPR3.js.map +1 -0
  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/{chunk-45OKKLNY.cjs → chunk-YGIPN3TU.cjs} +17 -2
  36. package/dist/chunk-YGIPN3TU.cjs.map +1 -0
  37. package/dist/{chunk-DCC3UIUJ.js → chunk-ZMQTV5OJ.js} +3 -3
  38. package/dist/{chunk-DCC3UIUJ.js.map → chunk-ZMQTV5OJ.js.map} +1 -1
  39. package/dist/evidence/index.cjs +45 -1
  40. package/dist/evidence/index.d.cts +187 -1
  41. package/dist/evidence/index.d.ts +187 -1
  42. package/dist/evidence/index.js +3 -1
  43. package/dist/governance/index.d.cts +1 -0
  44. package/dist/governance/index.d.ts +1 -0
  45. package/dist/index.cjs +143 -66
  46. package/dist/index.d.cts +3 -3
  47. package/dist/index.d.ts +3 -3
  48. package/dist/index.js +8 -7
  49. package/dist/policies/index.cjs +17 -9
  50. package/dist/policies/index.d.cts +15 -1
  51. package/dist/policies/index.d.ts +15 -1
  52. package/dist/policies/index.js +1 -1
  53. package/dist/privacy/index.cjs +19 -18
  54. package/dist/privacy/index.js +2 -1
  55. package/dist/testing/index.cjs +5 -5
  56. package/dist/testing/index.js +2 -2
  57. package/package.json +1 -1
  58. package/dist/chunk-45OKKLNY.cjs.map +0 -1
  59. package/dist/chunk-ATWWZTRC.js.map +0 -1
  60. package/dist/chunk-FAG2RTPM.js +0 -120
  61. package/dist/chunk-FAG2RTPM.js.map +0 -1
  62. package/dist/chunk-GU3LZWJD.js.map +0 -1
  63. package/dist/chunk-OZLZHISS.js +0 -3
  64. package/dist/chunk-OZLZHISS.js.map +0 -1
  65. package/dist/chunk-PG5LYTUX.cjs +0 -4
  66. package/dist/chunk-PG5LYTUX.cjs.map +0 -1
  67. package/dist/chunk-X3GHYMAJ.cjs.map +0 -1
  68. package/dist/chunk-XZQZQ5SW.cjs +0 -129
  69. package/dist/chunk-XZQZQ5SW.cjs.map +0 -1
@@ -1,3 +1,366 @@
1
+ /**
2
+ * State of an adapter circuit breaker.
3
+ *
4
+ * - `closed`: calls flow and outcomes are counted.
5
+ * - `open`: the dependency is presumed unhealthy. In `enforce` mode calls are
6
+ * rejected without being attempted until `cooldownMs` has elapsed.
7
+ * - `half_open`: the cooldown elapsed. One probe call is admitted; its outcome
8
+ * closes the breaker (success) or reopens it (failure).
9
+ */
10
+ type AdapterCircuitState = "closed" | "open" | "half_open";
11
+ /**
12
+ * How a single adapter outcome counts toward the breaker.
13
+ *
14
+ * - `success`: the dependency answered and the call succeeded.
15
+ * - `failure`: evidence the dependency is unhealthy.
16
+ * - `ignored`: the dependency answered but rejected the request on its merits
17
+ * (a validation or business rejection). It is neither a success nor a
18
+ * dependency failure and does not enter the rolling window.
19
+ */
20
+ type AdapterCircuitOutcome = "success" | "failure" | "ignored";
21
+ /**
22
+ * Map an {@link AdapterOutcomeKind} to how it counts toward a circuit breaker.
23
+ *
24
+ * | Outcome kind | Counts as |
25
+ * |---------------------|-----------|
26
+ * | `success` | `success` |
27
+ * | `transient_failure` | `failure` |
28
+ * | `timeout` | `failure` |
29
+ * | `ambiguous` | `failure` |
30
+ * | `permanent_failure` | `ignored` |
31
+ *
32
+ * `ambiguous` counts as a failure: the dependency did not give a definite
33
+ * answer, which is a health signal even though the Host routes the invocation
34
+ * to reconciliation. `permanent_failure` (validation or rejection) is the
35
+ * dependency working correctly and saying no, so it does not trip the breaker.
36
+ * `executeWithAdapterRetry` classifies a thrown error with the caller's
37
+ * `classifyThrownError` (default `transient_failure`) and maps that kind here,
38
+ * so an unclassified thrown error counts as a failure.
39
+ */
40
+ declare function classifyCircuitBreakerOutcome(kind: AdapterOutcomeKind): AdapterCircuitOutcome;
41
+ /** The answer a breaker gives before a call. */
42
+ interface AdapterCircuitAdmission {
43
+ /**
44
+ * Whether the call may proceed. Always `true` in `monitor` mode; `false`
45
+ * only in `enforce` mode while the breaker is open, forced open, or
46
+ * half-open with its probe already in flight.
47
+ */
48
+ allowed: boolean;
49
+ /** State at the time of the decision. */
50
+ state: AdapterCircuitState;
51
+ /** True when this admission is the half-open probe. Pass it back to `record`. */
52
+ probe: boolean;
53
+ /** Monitor mode only: true when `enforce` mode would have rejected this call. */
54
+ wouldReject?: boolean;
55
+ /**
56
+ * Milliseconds until the breaker will admit a probe, when known. Absent while
57
+ * a manual `force_open` override is set or a half-open probe is in flight.
58
+ */
59
+ retryAfterMs?: number;
60
+ /** Set when a manual override decided the admission. */
61
+ manualOverride?: AdapterCircuitBreakerManualOverride;
62
+ /**
63
+ * The breaker epoch the admission was granted in; opaque to callers.
64
+ * {@link InMemoryAdapterCircuitBreaker} lets an outcome change state only
65
+ * when this still matches its current epoch. An admission without it is
66
+ * treated as current.
67
+ */
68
+ generation?: number;
69
+ }
70
+ /**
71
+ * Contract between a circuit breaker and `executeWithAdapterRetry`.
72
+ * Implement it to plug in a shared or distributed breaker.
73
+ */
74
+ interface AdapterCircuitBreaker {
75
+ /** Registry key this breaker guards; defaults to `adapterType:operation`. */
76
+ readonly key: string;
77
+ readonly mode: "monitor" | "enforce";
78
+ /** Current state, applying any elapsed cooldown or manual override. */
79
+ getState(): AdapterCircuitState;
80
+ /**
81
+ * Preview whether a call would be admitted now. Side-effect free: it does
82
+ * not commit an elapsed cooldown, take the half-open probe or emit
83
+ * telemetry.
84
+ */
85
+ check(): AdapterCircuitAdmission;
86
+ /**
87
+ * Decide whether a call may start now. Call it immediately before the
88
+ * external effect and pass the returned admission to {@link record}.
89
+ */
90
+ acquire(): AdapterCircuitAdmission;
91
+ /**
92
+ * Record the outcome of a call that was admitted by {@link acquire}. Pass
93
+ * that admission, not a {@link check} preview, so the breaker can tell a
94
+ * stale outcome from a current one.
95
+ */
96
+ record(outcome: AdapterCircuitOutcome, admission?: AdapterCircuitAdmission): void;
97
+ }
98
+ type AdapterCircuitBreakerEventReason = "failure_threshold" | "cooldown_elapsed" | "probe_succeeded" | "probe_failed" | "manual_override" | "manual_override_cleared";
99
+ interface AdapterCircuitBreakerEventBase {
100
+ key: string;
101
+ mode: "monitor" | "enforce";
102
+ /** Epoch milliseconds from the breaker's clock. */
103
+ at: number;
104
+ }
105
+ /** Telemetry reported by {@link InMemoryAdapterCircuitBreaker}. */
106
+ type AdapterCircuitBreakerEvent = (AdapterCircuitBreakerEventBase & {
107
+ type: "state_change";
108
+ from: AdapterCircuitState;
109
+ to: AdapterCircuitState;
110
+ reason: AdapterCircuitBreakerEventReason;
111
+ manualOverride?: AdapterCircuitBreakerManualOverride;
112
+ }) | (AdapterCircuitBreakerEventBase & {
113
+ /** Enforce mode refused a call; it was not attempted. */
114
+ type: "rejected";
115
+ state: AdapterCircuitState;
116
+ retryAfterMs?: number;
117
+ manualOverride?: AdapterCircuitBreakerManualOverride;
118
+ }) | (AdapterCircuitBreakerEventBase & {
119
+ /** Monitor mode let a call through that enforce mode would have refused. */
120
+ type: "would_reject";
121
+ state: AdapterCircuitState;
122
+ retryAfterMs?: number;
123
+ manualOverride?: AdapterCircuitBreakerManualOverride;
124
+ });
125
+ /**
126
+ * Telemetry callback. Errors it throws and promises it rejects are caught
127
+ * and discarded so telemetry cannot change an adapter call's outcome.
128
+ *
129
+ * {@link InMemoryAdapterCircuitBreaker} calls it only after the operation that
130
+ * produced the event has committed, never part-way through a decision or a
131
+ * record. A callback may call back into the breaker; it sees committed state,
132
+ * and events its own calls produce are delivered after it returns, in order.
133
+ */
134
+ type AdapterCircuitBreakerEventListener = (event: AdapterCircuitBreakerEvent) => void | Promise<void>;
135
+ interface InMemoryAdapterCircuitBreakerOptions {
136
+ /** Usually `adapterType:operation`. */
137
+ key: string;
138
+ config: AdapterCircuitBreakerConfig;
139
+ /** Epoch-millisecond clock. Defaults to `Date.now`. */
140
+ now?: () => number;
141
+ onEvent?: AdapterCircuitBreakerEventListener;
142
+ }
143
+ /**
144
+ * Validate a circuit-breaker config. Throws a `RangeError` naming the first
145
+ * invalid field: `mode` must be `monitor` or `enforce`; `failureThreshold` and
146
+ * `minimumSamples` positive integers; `windowMs` and `probeLeaseMs` positive
147
+ * finite numbers; `cooldownMs` a non-negative finite number; `manualOverride`
148
+ * `force_open` or `force_closed` when present.
149
+ */
150
+ declare function assertAdapterCircuitBreakerConfig(config: AdapterCircuitBreakerConfig): void;
151
+ /**
152
+ * Dependency-free, in-process circuit breaker for one adapter operation.
153
+ *
154
+ * State lives in this instance only: it is not shared between processes,
155
+ * hosts, or replicas, and it is lost on restart. Each replica trips on the
156
+ * failures it observes itself.
157
+ *
158
+ * Tripping rule (parity with the .NET `InMemoryAdapterCircuitBreaker`): after
159
+ * a failure is recorded while `closed`, let `N` be the outcomes recorded within
160
+ * the last `windowMs` and `M = minimumSamples ?? failureThreshold`. The breaker
161
+ * opens when `N >= M` and either
162
+ * 1. the most recent `failureThreshold` outcomes in the window are all
163
+ * failures, or
164
+ * 2. the failure ratio in the window is at least `failureThreshold / M`.
165
+ *
166
+ * With `minimumSamples` omitted this reads "`failureThreshold` consecutive
167
+ * failures". With `minimumSamples: 20, failureThreshold: 10` it also opens at
168
+ * a 50% failure rate over at least 20 calls.
169
+ *
170
+ * After `cooldownMs` the breaker turns `half_open` and admits one probe. A
171
+ * probe success closes it and clears the window; a probe failure reopens it
172
+ * and restarts the cooldown. At most one probe is outstanding at a time: it
173
+ * holds a lease of `probeLeaseMs` (default `max(cooldownMs, windowMs)`, never
174
+ * derived from the cooldown alone, so `cooldownMs: 0` still admits one probe).
175
+ * A probe that has not recorded when its lease expires is considered lost and
176
+ * the next call becomes the probe.
177
+ *
178
+ * Epochs: the breaker keeps a generation counter that advances on every state
179
+ * change (including one committed by an elapsed cooldown), every manual
180
+ * override change, and every probe grant. Each admission carries the
181
+ * generation it was granted in. `record` first commits any elapsed cooldown,
182
+ * then compares generations. An outcome from an earlier epoch is stale: it is
183
+ * dropped entirely. It changes no state, does not resolve or free the probe
184
+ * slot, and is not added to the rolling window, so it cannot contribute to a
185
+ * later trip either. Because a probe grant starts a new epoch, the outstanding
186
+ * probe is the only `probe: true` admission of the current epoch, which is how
187
+ * a replaced probe's late outcome is told apart. An outcome recorded without an
188
+ * admission, or with one that has no `generation`, is treated as current: it is
189
+ * sampled, can trip a closed breaker and resolves an outstanding probe.
190
+ *
191
+ * Telemetry is buffered and delivered after each operation commits. An
192
+ * elapsed cooldown is committed, and its `cooldown_elapsed` event delivered,
193
+ * as a step of its own before `acquire`, `record`, `getState` or
194
+ * `setManualOverride` continues, so a callback reacting to it (for example by
195
+ * forcing the breaker open) governs the call that observed it. Every other
196
+ * event is delivered once the method's decision or record is final, and a
197
+ * callback never revokes an admission that is already committed. Two cases
198
+ * follow from that: a breaker call made from inside a callback queues its own
199
+ * `cooldown_elapsed` behind the current delivery and decides first; and with
200
+ * `cooldownMs: 0`, a cooldown that falls due again during the decision (because
201
+ * a callback reopened the breaker) is committed and admitted before its event
202
+ * is delivered.
203
+ *
204
+ * `monitor` mode runs the same state machine and reports the same telemetry
205
+ * but admits every call; calls enforce mode would have refused are reported
206
+ * as `would_reject`.
207
+ *
208
+ * A manual override pins the reported state (`force_open` → `open`,
209
+ * `force_closed` → `closed`) and suspends automatic transitions while set;
210
+ * outcomes of calls admitted under the override are still recorded. Like an
211
+ * automatic open, `force_open` blocks only in `enforce` mode. Clearing the
212
+ * override returns the breaker to `closed` with its window intact.
213
+ */
214
+ declare class InMemoryAdapterCircuitBreaker implements AdapterCircuitBreaker {
215
+ readonly key: string;
216
+ readonly mode: "monitor" | "enforce";
217
+ private readonly config;
218
+ private readonly now;
219
+ private readonly onEvent;
220
+ private readonly probeLeaseMs;
221
+ private samples;
222
+ private state;
223
+ private openedAt;
224
+ private probeIssuedAt;
225
+ /** Advances on every state change, manual override change and probe grant. */
226
+ private generation;
227
+ private manualOverride;
228
+ /** Telemetry waiting for the current operation to commit. */
229
+ private readonly outbox;
230
+ private operationDepth;
231
+ private flushing;
232
+ constructor(options: InMemoryAdapterCircuitBreakerOptions);
233
+ getState(): AdapterCircuitState;
234
+ getManualOverride(): AdapterCircuitBreakerManualOverride | undefined;
235
+ /**
236
+ * Set (`force_open` / `force_closed`) or clear (`undefined`) the manual
237
+ * override. Clearing returns the breaker to `closed` with its window intact.
238
+ */
239
+ setManualOverride(override: AdapterCircuitBreakerManualOverride | undefined): void;
240
+ check(): AdapterCircuitAdmission;
241
+ acquire(): AdapterCircuitAdmission;
242
+ record(outcome: AdapterCircuitOutcome, admission?: AdapterCircuitAdmission): void;
243
+ private applyOutcome;
244
+ /**
245
+ * `take` is false for {@link check}: a pure preview that sees an elapsed
246
+ * cooldown without committing it, takes no probe and emits nothing.
247
+ */
248
+ private decide;
249
+ private refuse;
250
+ private releaseProbe;
251
+ /** Apply the time-based open → half_open transition. */
252
+ private advance;
253
+ private shouldTrip;
254
+ private prune;
255
+ private transition;
256
+ private reportedState;
257
+ /**
258
+ * Commit an elapsed cooldown as an operation of its own, delivering its
259
+ * event before the caller's operation reads state.
260
+ */
261
+ private settle;
262
+ /**
263
+ * Run `step` to completion, then deliver the telemetry it produced. The
264
+ * return value is fixed before any listener runs.
265
+ */
266
+ private operation;
267
+ private emit;
268
+ /**
269
+ * Deliver buffered events in order. A listener that calls back into the
270
+ * breaker queues its own events; this loop delivers them after it returns
271
+ * instead of recursing.
272
+ */
273
+ private flush;
274
+ private deliver;
275
+ }
276
+ /** Trusted invocation context available when selecting an adapter breaker. */
277
+ interface AdapterCircuitBreakerScope {
278
+ tenantId: string;
279
+ spaceId?: string;
280
+ }
281
+ /**
282
+ * Key by tenant, optional space, vendor, and adapter operation. Components are
283
+ * URI-encoded to distinguish embedded delimiters; ordinary adapter identifiers
284
+ * remain readable as the trailing `adapterType:operation`.
285
+ *
286
+ * Missing scope uses a separate `unscoped` namespace (still split by vendor)
287
+ * rather than rejecting the call. Calls without scope share that
288
+ * namespace; callers must supply scope for tenant isolation. Missing space or
289
+ * vendor is distinct from an empty string. `vendor` is required by the public
290
+ * adapter type, but its absence is tolerated for JavaScript callers.
291
+ * Keys appear in errors and telemetry: use non-secret identifiers.
292
+ */
293
+ declare function scopedAdapterCircuitBreakerKey(adapter: AdapterImplementation, scope: AdapterCircuitBreakerScope | undefined): string;
294
+ /** Supplies the breaker, if any, that guards an adapter in invocation scope. */
295
+ interface AdapterCircuitBreakerProvider {
296
+ forAdapter(adapter: AdapterImplementation, scope?: AdapterCircuitBreakerScope): AdapterCircuitBreaker | undefined;
297
+ }
298
+ interface AdapterCircuitBreakerRegistryOptions {
299
+ /**
300
+ * Defaults to `adapterType:operation`, shared across tenants and vendors in
301
+ * this registry. Use {@link scopedAdapterCircuitBreakerKey} for tenant,
302
+ * space, and vendor isolation. The first config seen for a key wins;
303
+ * later conflicting configs reuse that breaker without throwing. Breakers
304
+ * are never evicted, and scoped keys are high-cardinality: avoid them as
305
+ * metric labels.
306
+ */
307
+ key?: (adapter: AdapterImplementation, scope: AdapterCircuitBreakerScope | undefined) => string;
308
+ /** Epoch-millisecond clock handed to every breaker. Defaults to `Date.now`. */
309
+ now?: () => number;
310
+ onEvent?: AdapterCircuitBreakerEventListener;
311
+ }
312
+ /**
313
+ * Lazily creates one {@link InMemoryAdapterCircuitBreaker} per
314
+ * key, from that adapter's own `circuitBreaker` config. The default key is
315
+ * `adapterType:operation`, shared across tenants and vendors; override it with
316
+ * {@link AdapterCircuitBreakerRegistryOptions.key} to isolate invocation scopes.
317
+ * Adapters without a config get no breaker. The first config seen for a key
318
+ * wins; later conflicting configs reuse the existing breaker without throwing.
319
+ * Share a key only between adapters intended to use the same configuration.
320
+ */
321
+ declare class AdapterCircuitBreakerRegistry implements AdapterCircuitBreakerProvider {
322
+ private readonly options;
323
+ private readonly breakers;
324
+ constructor(options?: AdapterCircuitBreakerRegistryOptions);
325
+ forAdapter(adapter: AdapterImplementation, scope?: AdapterCircuitBreakerScope): InMemoryAdapterCircuitBreaker | undefined;
326
+ /** Looks up the default `adapterType:operation` key without creating a breaker. */
327
+ get(adapterType: string, operation: string): InMemoryAdapterCircuitBreaker | undefined;
328
+ /** Looks up an exact key, including a custom key, without creating a breaker. */
329
+ getByKey(key: string): InMemoryAdapterCircuitBreaker | undefined;
330
+ }
331
+ /**
332
+ * Thrown by `executeWithAdapterRetry` when an `enforce`-mode breaker refuses
333
+ * an attempt. The refused attempt never reached `execute`, so no external
334
+ * effect happened for it.
335
+ */
336
+ declare class AdapterCircuitOpenError extends Error {
337
+ readonly name = "AdapterCircuitOpenError";
338
+ readonly code: "ADAPTER_CIRCUIT_OPEN";
339
+ readonly reason: "circuit_open";
340
+ readonly adapterKey: string;
341
+ readonly state: AdapterCircuitState;
342
+ /** The attempt number that was refused (1 = refused before any call). */
343
+ readonly attempt: number;
344
+ readonly retryAfterMs?: number;
345
+ readonly manualOverride?: AdapterCircuitBreakerManualOverride;
346
+ /** Error of the previous attempt, when the breaker opened mid-retry. */
347
+ readonly lastError?: string;
348
+ constructor(input: {
349
+ adapterKey: string;
350
+ state: AdapterCircuitState;
351
+ attempt: number;
352
+ retryAfterMs?: number;
353
+ manualOverride?: AdapterCircuitBreakerManualOverride;
354
+ lastError?: string;
355
+ });
356
+ }
357
+ /**
358
+ * Recognise an {@link AdapterCircuitOpenError}, including one constructed by a
359
+ * second copy of this package (for example an ESM and a CommonJS build loaded
360
+ * side by side), where `instanceof` fails.
361
+ */
362
+ declare function isAdapterCircuitOpenError(error: unknown): error is AdapterCircuitOpenError;
363
+
1
364
  interface AdapterExecutionContext {
2
365
  tenantId: string;
3
366
  spaceId: string;
@@ -89,12 +452,50 @@ interface ResolvedAdapterRetryPolicy {
89
452
  backoffMultiplier: number;
90
453
  maxDelayMs: number;
91
454
  }
455
+ /** Manual circuit-breaker override: pin the breaker open or closed. */
456
+ type AdapterCircuitBreakerManualOverride = "force_open" | "force_closed";
457
+ /**
458
+ * Per-adapter circuit-breaker configuration, read by
459
+ * {@link AdapterCircuitBreakerRegistry} (and so by Platform Host) to create one
460
+ * in-process {@link InMemoryAdapterCircuitBreaker} per registry key (default
461
+ * `adapterType:operation`; configurable through the registry `key` option).
462
+ * Validated by {@link assertAdapterCircuitBreakerConfig}.
463
+ */
92
464
  interface AdapterCircuitBreakerConfig {
465
+ /**
466
+ * `monitor` records outcomes, runs the state machine and reports what it
467
+ * would have refused, but never blocks a call. `enforce` refuses calls
468
+ * while the breaker is open, without attempting them.
469
+ */
93
470
  mode: "monitor" | "enforce";
471
+ /**
472
+ * A failure count. Let `M = minimumSamples ?? failureThreshold`. Once at
473
+ * least `M` outcomes fall within `windowMs`, the breaker opens when the most
474
+ * recent `failureThreshold` of them are all failures, or when the failure
475
+ * ratio in the window reaches `failureThreshold / M`. Without
476
+ * `minimumSamples` this is "`failureThreshold` consecutive failures".
477
+ * Matches the .NET port's `InMemoryAdapterCircuitBreaker`. Positive integer.
478
+ */
94
479
  failureThreshold: number;
480
+ /** Rolling window, in milliseconds, of outcomes the thresholds consider. */
95
481
  windowMs: number;
482
+ /** Milliseconds an open breaker waits before admitting one half-open probe. */
96
483
  cooldownMs: number;
484
+ /** Outcomes required in the window before the breaker can open. Defaults to `failureThreshold`. */
97
485
  minimumSamples?: number;
486
+ /**
487
+ * How long, in milliseconds, the single half-open probe holds its slot
488
+ * before it is presumed lost and another call may probe. Defaults to
489
+ * `max(cooldownMs, windowMs)`; it is independent of `cooldownMs`, so a zero
490
+ * or short cooldown never admits concurrent probes. Positive.
491
+ */
492
+ probeLeaseMs?: number;
493
+ /**
494
+ * Optional initial manual override. `force_open` reports `open` (and blocks
495
+ * only in `enforce` mode); `force_closed` reports `closed` and never blocks.
496
+ * Change it at runtime with `InMemoryAdapterCircuitBreaker.setManualOverride`.
497
+ */
498
+ manualOverride?: AdapterCircuitBreakerManualOverride;
98
499
  }
99
500
  interface AdapterImplementation {
100
501
  adapterType: string;
@@ -136,6 +537,25 @@ interface ExecuteWithRetryOptions<T> {
136
537
  classifyThrownError?: (error: unknown) => AdapterOutcomeKind;
137
538
  sleep?: (delayMs: number) => Promise<void>;
138
539
  onAttempt?: (attempt: AdapterAttempt<T>) => Promise<void> | void;
540
+ /**
541
+ * Optional per-attempt preparation, awaited before the circuit breaker is
542
+ * consulted and before `execute` (for example, persisting the attempt
543
+ * number). Put every await a caller needs before the call here, so that
544
+ * nothing awaits between breaker admission and the external call. If it
545
+ * throws, no admission is taken and no breaker outcome is recorded; the
546
+ * error is classified with `classifyThrownError` and retried or rethrown
547
+ * exactly like an error thrown by `execute`.
548
+ */
549
+ prepareAttempt?: (attempt: number, maxAttempts: number) => Promise<void> | void;
550
+ /**
551
+ * Optional circuit breaker. When present, `acquire()` is called
552
+ * immediately before every `execute` call, with no await in between, and every attempt's outcome is
553
+ * recorded through {@link classifyCircuitBreakerOutcome}. In `enforce` mode
554
+ * a refused attempt is not executed and {@link AdapterCircuitOpenError} is
555
+ * thrown; if the breaker opens between attempts, retrying stops and the
556
+ * same error is thrown. When omitted, behaviour is unchanged.
557
+ */
558
+ circuitBreaker?: AdapterCircuitBreaker;
139
559
  }
140
560
  declare class MissingAdapterRegistrationError extends Error {
141
561
  constructor(adapterType: string, operation: string);
@@ -171,4 +591,4 @@ declare function shouldRetryAdapterOutcome(kind: AdapterOutcomeKind, idempotent:
171
591
  declare function assertAdapterOutcomeConsistency(result: AdapterExecutionResult): void;
172
592
  declare function executeWithAdapterRetry<T>(options: ExecuteWithRetryOptions<T>): Promise<T>;
173
593
 
174
- export { type AcceptedExternalOperation, type AdapterAttempt, type AdapterCircuitBreakerConfig, type AdapterExecutionContext, type AdapterExecutionResult, type AdapterExecutor, type AdapterImplementation, type AdapterOutcomeEvidence, type AdapterOutcomeKind, AdapterRegistry, type AdapterRetryPolicy, type ExecuteWithRetryOptions, MissingAdapterRegistrationError, type ResolvedAdapterRetryPolicy, assertAdapterOutcomeConsistency, classifyAdapterOutcome, executeWithAdapterRetry, getRetryDelayMs, resolveAdapterRetryPolicy, shouldRetryAdapterOutcome };
594
+ export { type AcceptedExternalOperation, type AdapterAttempt, type AdapterCircuitAdmission, type AdapterCircuitBreaker, type AdapterCircuitBreakerConfig, type AdapterCircuitBreakerEvent, type AdapterCircuitBreakerEventListener, type AdapterCircuitBreakerEventReason, type AdapterCircuitBreakerManualOverride, type AdapterCircuitBreakerProvider, AdapterCircuitBreakerRegistry, type AdapterCircuitBreakerRegistryOptions, type AdapterCircuitBreakerScope, AdapterCircuitOpenError, type AdapterCircuitOutcome, type AdapterCircuitState, type AdapterExecutionContext, type AdapterExecutionResult, type AdapterExecutor, type AdapterImplementation, type AdapterOutcomeEvidence, type AdapterOutcomeKind, AdapterRegistry, type AdapterRetryPolicy, type ExecuteWithRetryOptions, InMemoryAdapterCircuitBreaker, type InMemoryAdapterCircuitBreakerOptions, MissingAdapterRegistrationError, type ResolvedAdapterRetryPolicy, assertAdapterCircuitBreakerConfig, assertAdapterOutcomeConsistency, classifyAdapterOutcome, classifyCircuitBreakerOutcome, executeWithAdapterRetry, getRetryDelayMs, isAdapterCircuitOpenError, resolveAdapterRetryPolicy, scopedAdapterCircuitBreakerKey, shouldRetryAdapterOutcome };