@shirudo/ddd-kit 2.2.0 → 3.0.0-rc.11

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.
@@ -0,0 +1,1260 @@
1
+ import { StructuredError } from "@shirudo/base-error";
2
+ //#region src/errors/kit-errors.d.ts
3
+ /**
4
+ * **The kit's error identity model (since v3).** Every kit error is a
5
+ * structured error carrying exactly ONE identifier: `code`, a stable
6
+ * SCREAMING_SNAKE string, and `error.name === error.code` by design, so
7
+ * there is no name/code drift and nothing to keep in sync. `category`
8
+ * follows the class hierarchy mechanically (`"DOMAIN"`,
9
+ * `"INFRASTRUCTURE"`, or `"WIRING"` for the crash-loud family) and
10
+ * `retryable` is a plain boolean field.
11
+ *
12
+ * **No base-error adoption required.** Consumers branch with a plain
13
+ * `switch (error.code)`, catch via `instanceof DomainError` /
14
+ * `instanceof InfrastructureError` (exported from this kit), and read
15
+ * `retryable` / `cause` as ordinary properties. base-error's toolbox
16
+ * (`matchError` exhaustive dispatch, `isStructuredError`, the
17
+ * public-error catalog and `toProblem`) works on every kit error as an
18
+ * OPT-IN benefit on top, never as a prerequisite.
19
+ */
20
+ /**
21
+ * Options for consumer subclasses of {@link DomainError} and
22
+ * {@link InfrastructureError}: the `code` (which also becomes
23
+ * `error.name`) and the technical `message` are the only obligations;
24
+ * `retryable` defaults to `false` and the category is fixed by the base.
25
+ */
26
+ interface KitErrorOptions<TCode extends string> {
27
+ /** Stable SCREAMING_SNAKE identifier; also becomes `error.name`. */
28
+ code: TCode;
29
+ /** Technical message for logs and debugging, never for clients. */
30
+ message: string;
31
+ /** Optional underlying error preserved in the cause chain. */
32
+ cause?: unknown;
33
+ /** Whether retrying the failed operation can succeed. Default `false`. */
34
+ retryable?: boolean;
35
+ }
36
+ /**
37
+ * Abstract base for **domain-invariant violations**. Domain methods
38
+ * (aggregates, entity validation hooks, value-object constructors)
39
+ * throw `DomainError`-derived exceptions when a business rule is
40
+ * violated. Consumers derive their own concrete errors (e.g.
41
+ * `class OrderAlreadyShippedError extends DomainError<"ORDER_ALREADY_SHIPPED">`)
42
+ * for `instanceof`-style catching at the App-Service boundary, where
43
+ * they typically map to HTTP 400 / business-rule responses.
44
+ *
45
+ * The library itself ships no business-rule `DomainError` subclass: the
46
+ * kit can't know your invariants. (The domain-state-machine module's
47
+ * transition errors are the structural exception.)
48
+ *
49
+ * The `category` is fixed to `"DOMAIN"` and `retryable` defaults to
50
+ * `false`, so a subclass supplies only its `code` and `message`:
51
+ *
52
+ * ```ts
53
+ * class OrderAlreadyShippedError extends DomainError<"ORDER_ALREADY_SHIPPED"> {
54
+ * constructor(orderId: string) {
55
+ * super({
56
+ * code: "ORDER_ALREADY_SHIPPED",
57
+ * message: `Order ${orderId} has already been shipped`,
58
+ * });
59
+ * }
60
+ * }
61
+ * ```
62
+ */
63
+ declare abstract class DomainError<TCode extends string = string> extends StructuredError<TCode, "DOMAIN"> {
64
+ protected constructor(options: KitErrorOptions<TCode>);
65
+ /** Carries the fields the concrete error declares into the log object. */
66
+ protected buildLogObject(): Record<string, unknown>;
67
+ }
68
+ /**
69
+ * Internal base for the kit's crash-loud **WIRING** family: deterministic
70
+ * programming/configuration bugs that must fail the operation loudly and
71
+ * never be absorbed by generic domain or infrastructure handlers. One
72
+ * implementation of the `{ category: "WIRING", retryable: false }` shape
73
+ * so the family cannot drift. Exported for the kit's own modules only;
74
+ * not part of the package entries.
75
+ */
76
+ declare abstract class KitWiringError<TCode extends string> extends StructuredError<TCode, "WIRING"> {
77
+ protected constructor(code: TCode, message: string, cause?: unknown);
78
+ /** Carries the fields the concrete error declares into the log object. */
79
+ protected buildLogObject(): Record<string, unknown>;
80
+ }
81
+ /**
82
+ * Abstract base for **infrastructure / persistence failures** that the
83
+ * App-Service can recover from: typically by retrying, by returning
84
+ * HTTP 404 / 409, or by surfacing a "please try again" UX. These are
85
+ * not domain-invariant violations (the business rules were not
86
+ * broken); they describe race conditions and missing rows at the
87
+ * storage boundary.
88
+ *
89
+ * The `category` is fixed to `"INFRASTRUCTURE"`; `retryable` defaults
90
+ * to `false` (opt in per subclass, see {@link ConcurrencyConflictError}).
91
+ *
92
+ * Library-internal concrete subclasses: {@link AggregateNotFoundError},
93
+ * {@link ConcurrencyConflictError}, {@link DuplicateAggregateError},
94
+ * plus the unit-of-work lifecycle wrappers `CommitError` and
95
+ * `RollbackError` (in `src/application/unit-of-work/errors.ts`).
96
+ */
97
+ declare abstract class InfrastructureError<TCode extends string = string> extends StructuredError<TCode, "INFRASTRUCTURE"> {
98
+ protected constructor(options: KitErrorOptions<TCode>);
99
+ /** Carries the fields the concrete error declares into the log object. */
100
+ protected buildLogObject(): Record<string, unknown>;
101
+ }
102
+ /**
103
+ * Copy-safe membership check for the kit's domain-error family.
104
+ *
105
+ * `instanceof` is false for an error constructed by another loaded copy of
106
+ * the kit (a separately installed adapter package, a CJS/ESM dual load), so
107
+ * kit boundaries that route by error family fall back to the structural
108
+ * `category` field, the stable cross-copy contract.
109
+ */
110
+ declare function isDomainErrorLike(value: unknown): value is DomainError;
111
+ /**
112
+ * Copy-safe membership check for the kit's infrastructure-error family.
113
+ * Same rationale as {@link isDomainErrorLike}.
114
+ */
115
+ declare function isInfrastructureErrorLike(value: unknown): value is InfrastructureError;
116
+ /**
117
+ * Copy-safe membership check for the kit's wiring-error family.
118
+ * Same rationale as {@link isDomainErrorLike}.
119
+ *
120
+ * A wiring error states a deterministic programming or configuration defect.
121
+ * A kit boundary that translates failures uses this check to pass such an
122
+ * error through untouched, instead of relabelling it as a store failure.
123
+ *
124
+ * The check narrows to the structural shape, not to a class: the family's
125
+ * base stays kit-internal, and an error from another kit copy carries the
126
+ * shape without being an instance of this copy's class.
127
+ */
128
+ declare function isWiringErrorLike(value: unknown): value is Error & {
129
+ readonly code: string;
130
+ readonly category: "WIRING";
131
+ readonly retryable: false;
132
+ };
133
+ /** Options bag for {@link InMemoryCapacityExceededError}. */
134
+ interface InMemoryCapacityExceededErrorOptions {
135
+ /** Concrete reference adapter whose configured capacity was exhausted. */
136
+ readonly store: string;
137
+ /** Bounded collection or logical resource, such as `events` or `sources`. */
138
+ readonly resource: string;
139
+ /** Configured maximum number of retained records. */
140
+ readonly limit: number;
141
+ /** Records retained before the rejected operation. */
142
+ readonly current: number;
143
+ /** New records the rejected operation would have retained. */
144
+ readonly attempted: number;
145
+ }
146
+ /**
147
+ * A finite-capacity in-memory reference adapter rejected new state before
148
+ * mutation. Existing records remain usable; callers must release explicit
149
+ * lifecycle state, increase the configured limit, or switch to a durable
150
+ * adapter. The error is not retryable without one of those external changes.
151
+ */
152
+ declare class InMemoryCapacityExceededError extends InfrastructureError<"IN_MEMORY_CAPACITY_EXCEEDED"> {
153
+ readonly store: string;
154
+ readonly resource: string;
155
+ readonly limit: number;
156
+ readonly current: number;
157
+ readonly attempted: number;
158
+ constructor(options: InMemoryCapacityExceededErrorOptions);
159
+ }
160
+ /**
161
+ * Thrown when a projection built with `projectionFromHandlers` receives an
162
+ * event type with no own handler entry: the declared event union and the
163
+ * handler map disagree at runtime, which is a programming / configuration
164
+ * bug rather than a domain or infrastructure failure.
165
+ *
166
+ * Deliberately **not** on `DomainError` or `InfrastructureError`:
167
+ * a generic `catch (e instanceof DomainError)` handler at the App
168
+ * layer must not mask a forgotten handler; this should crash loud and
169
+ * fail the calling Use Case so the bug surfaces in development.
170
+ *
171
+ * Use `isBaseError(e)` from `@shirudo/base-error` to detect
172
+ * "any structured error from the kit or any other BaseError-using
173
+ * library" at the App boundary.
174
+ */
175
+ declare class MissingHandlerError extends KitWiringError<"MISSING_HANDLER"> {
176
+ readonly eventType: string;
177
+ constructor(eventType: string, cause?: unknown);
178
+ }
179
+ /**
180
+ * Thrown by an event-sourced aggregate when `apply()` or replay reaches an
181
+ * event type with no own entry in the `folds` map: the declared event union
182
+ * and the map disagree at runtime. Same posture as
183
+ * {@link MissingHandlerError}: a deterministic bug, never a domain
184
+ * rejection, so it propagates through `replayHistory` instead of riding its
185
+ * `Result`.
186
+ */
187
+ declare class MissingFoldError extends KitWiringError<"MISSING_FOLD"> {
188
+ readonly eventType: string;
189
+ constructor(eventType: string, cause?: unknown);
190
+ }
191
+ /**
192
+ * Thrown by an event-sourced aggregate when a fold returns `undefined`
193
+ * for an event, which is almost always a fold without a `return` statement.
194
+ * Storing that result would set the aggregate state to `undefined`, record
195
+ * the fact anyway on the apply path, and leave every later fold working on
196
+ * nothing. Same posture as {@link MissingFoldError}: a deterministic bug
197
+ * in the folds map, never a domain rejection, so it propagates through
198
+ * `replayHistory` instead of riding its `Result`.
199
+ */
200
+ declare class FoldReturnedNoStateError extends KitWiringError<"FOLD_RETURNED_NO_STATE"> {
201
+ readonly eventType: string;
202
+ constructor(eventType: string);
203
+ }
204
+ /** Constructor options for {@link DirectStateMutationError}. */
205
+ interface DirectStateMutationErrorOptions {
206
+ /** The aggregate the error names. */
207
+ readonly identity: {
208
+ readonly aggregateType: string;
209
+ readonly aggregateId: string;
210
+ };
211
+ }
212
+ /**
213
+ * Thrown by `EventSourcedAggregate.setState`: on an event-sourced aggregate
214
+ * the state changes only through `apply()`, where the fact is recorded and
215
+ * the version advances with it. A direct state write would leave the
216
+ * instance ahead of its stream with nothing to replay. A wiring error: a
217
+ * deterministic bug in the aggregate's own code, the remedy is an event
218
+ * and a handler.
219
+ */
220
+ declare class DirectStateMutationError extends KitWiringError<"DIRECT_STATE_MUTATION"> {
221
+ readonly identity: DirectStateMutationErrorOptions["identity"];
222
+ constructor(options: DirectStateMutationErrorOptions);
223
+ }
224
+ /**
225
+ * Thrown by `Projector.project` when an event cannot be projected
226
+ * safely because its cursor is missing or malformed, or its aggregate
227
+ * identity is absent. Applying such an event would break idempotency, so
228
+ * the batch fails. Events written by `withCommit` carry the complete
229
+ * cursor automatically; other sources compose a gap-proof committed-event
230
+ * envelope. A well-formed cursor that does not continue the stored chain
231
+ * instead throws {@link ProjectionGapError}.
232
+ *
233
+ * A wiring error, not a `DomainError`: see {@link MissingHandlerError}
234
+ * for the rationale of crashing loud at the App layer.
235
+ */
236
+ declare class UnprojectableEventError extends KitWiringError<"UNPROJECTABLE_EVENT"> {
237
+ readonly projection: string;
238
+ readonly eventId: string;
239
+ constructor(projection: string, eventId: string, reason: string, cause?: unknown);
240
+ }
241
+ /**
242
+ * Thrown when a valid projection cursor does not continue the stored
243
+ * per-aggregate chain. This is an infrastructure/delivery failure: an
244
+ * event or commit is missing, commonly because a partition reordered or
245
+ * dead-lettered it. The projector does not apply the later event and the
246
+ * checkpoint stays put until the missing history is replayed or the
247
+ * projection is rebuilt.
248
+ */
249
+ declare class ProjectionGapError extends InfrastructureError<"PROJECTION_GAP"> {
250
+ readonly projection: string;
251
+ readonly eventId: string;
252
+ readonly previousPosition: string;
253
+ readonly receivedPosition: string;
254
+ constructor(projection: string, eventId: string, previousPosition: string, receivedPosition: string);
255
+ }
256
+ /**
257
+ * Thrown when one batch delivers previously unseen positions of the same
258
+ * aggregate in descending order. Unlike {@link ProjectionGapError}, this is
259
+ * direct proof that the feed violated its per-aggregate ordering contract;
260
+ * no missing-history inference is needed. Positions already covered by the
261
+ * checkpoint at batch start and exact receipts repeated inside the batch
262
+ * remain valid redeliveries and do not trip this diagnostic guard.
263
+ */
264
+ declare class ProjectionOrderViolationError extends InfrastructureError<"PROJECTION_ORDER_VIOLATION"> {
265
+ readonly projection: string;
266
+ readonly eventId: string;
267
+ readonly previousReceivedPosition: string;
268
+ readonly receivedPosition: string;
269
+ constructor(projection: string, eventId: string, previousReceivedPosition: string, receivedPosition: string);
270
+ }
271
+ /**
272
+ * Thrown when a source maps different event identities to one position, either
273
+ * inside the current batch or at the position stored as the projection's
274
+ * watermark. The checkpoint retains the identity of that one last-applied
275
+ * event, so the durable collision is provable without keeping an unbounded
276
+ * processed-event ledger. Positions behind the watermark remain governed by
277
+ * the source's one-logical-event-per-position contract.
278
+ */
279
+ declare class ProjectionIdentityViolationError extends InfrastructureError<"PROJECTION_IDENTITY_VIOLATION"> {
280
+ readonly projection: string;
281
+ readonly eventId: string;
282
+ readonly recordedEventId: string;
283
+ readonly position: string;
284
+ constructor(projection: string, eventId: string, recordedEventId: string, position: string);
285
+ }
286
+ /**
287
+ * Thrown when one logical projection position keeps its event identity but its
288
+ * commit-boundary receipt changes. `commitSize` and
289
+ * `previousEventfulAggregateVersion` are part of the continuity proof, so a
290
+ * source must keep them immutable just like the eventId. Accepting a
291
+ * contradictory redelivery could hide an incomplete commit or predecessor.
292
+ */
293
+ declare class ProjectionReceiptViolationError extends InfrastructureError<"PROJECTION_RECEIPT_VIOLATION"> {
294
+ readonly projection: string;
295
+ readonly eventId: string;
296
+ readonly recordedReceipt: string;
297
+ readonly receivedReceipt: string;
298
+ constructor(projection: string, eventId: string, recordedReceipt: string, receivedReceipt: string);
299
+ }
300
+ /** A malformed or non-JSON-safe message at an integration boundary. */
301
+ declare class InvalidIntegrationMessageError extends InfrastructureError<"INVALID_INTEGRATION_MESSAGE"> {
302
+ readonly path: string;
303
+ readonly reason: string;
304
+ constructor(path: string, reason: string, cause?: unknown);
305
+ }
306
+ /** A malformed or non-JSON-safe command selected for durable delivery. */
307
+ declare class InvalidCommandMessageError extends InfrastructureError<"INVALID_COMMAND_MESSAGE"> {
308
+ readonly path: string;
309
+ readonly reason: string;
310
+ constructor(path: string, reason: string, cause?: unknown);
311
+ }
312
+ /**
313
+ * Thrown by `Entity` (constructor and `setState`), by the event-sourced
314
+ * fold (`apply` and replay), by the event constructors for the payload,
315
+ * and by the event metadata helpers (`createDomainEvent`'s
316
+ * `options.metadata`, `mergeMetadata`, `copyMetadata`) when the value
317
+ * carries an own `"__proto__"` data key:
318
+ * the shape `JSON.parse` produces for hostile DB rows or request bodies
319
+ * handed to reconstitute factories. Such a key can never be legitimate
320
+ * domain state; accepting it would hand a prototype-pollution payload to
321
+ * every downstream consumer that copies the state through `[[Set]]`
322
+ * (`Object.assign`, for-in assignment loops), and dropping it would be
323
+ * silent data mutation. The check looks at the root object only; nested
324
+ * objects are not walked, and a class instance is an ownership transfer
325
+ * that passes.
326
+ *
327
+ * Deliberately **not** a `DomainError` or `InfrastructureError` (same
328
+ * posture as {@link MissingHandlerError}): untrusted input reaching the
329
+ * domain layer unvalidated is a boundary bug, and a generic
330
+ * business-rule handler must not absorb it. Validate and strip untrusted
331
+ * input at the application edge; model genuinely arbitrary keys with a
332
+ * `Map`, not a plain object.
333
+ */
334
+ declare class HostileStateKeyError extends KitWiringError<"HOSTILE_STATE_KEY"> {
335
+ readonly key: string;
336
+ constructor(key: string, subject?: string);
337
+ }
338
+ /**
339
+ * Thrown by the `Entity` constructor when the id is not a non-blank
340
+ * string. That covers `null`, `undefined`, a blank string, and a
341
+ * non-string value that reached the constructor through a cast. An
342
+ * entity without a usable identity cannot be tracked, compared, or
343
+ * persisted, so the construction fails before any state is stored. A
344
+ * wiring error: a deterministic bug at the call site, never a domain
345
+ * rejection.
346
+ */
347
+ declare class MissingEntityIdError extends KitWiringError<"MISSING_ENTITY_ID"> {
348
+ constructor(
349
+ /** The rejected value, for the message only; never a usable id. */
350
+ received: unknown);
351
+ }
352
+ /**
353
+ * Thrown when a number that is not a valid aggregate version reaches the
354
+ * kit: `toVersion`, `markReconstituted`, `setVersion`, and the post-commit
355
+ * acknowledgement all reject it. A version is a safe integer of at least
356
+ * zero, and a restore never moves below the current version. A wiring
357
+ * error: an adapter passed a corrupt row value or a wrong number, and
358
+ * the optimistic-concurrency cursor must not carry it. Not retryable.
359
+ */
360
+ declare class InvalidVersionError extends KitWiringError<"INVALID_VERSION"> {
361
+ readonly value: unknown;
362
+ /** Why the value was rejected, for example "is not a safe integer". */
363
+ readonly reason: string;
364
+ constructor(value: unknown,
365
+ /** Why the value was rejected, for example "is not a safe integer". */
366
+ reason: string);
367
+ }
368
+ /** Constructor options for {@link UnreplayableAggregateError}. */
369
+ interface UnreplayableAggregateErrorOptions {
370
+ /** The aggregate the error names. */
371
+ readonly identity: {
372
+ readonly aggregateType: string;
373
+ readonly aggregateId: string;
374
+ };
375
+ /** Why the aggregate cannot take a replay, with the safe remedy. */
376
+ readonly reason: string;
377
+ }
378
+ /**
379
+ * Thrown by `EventSourcedAggregate.replayHistory` when the replay target
380
+ * carries unflushed `pendingEvents`. Replaying persisted facts onto that
381
+ * instance would advance the version underneath decisions made against an
382
+ * older state and could later claim history the stream does not carry.
383
+ *
384
+ * Deliberately **not** a `DomainError` or `InfrastructureError` (same
385
+ * posture as {@link MissingHandlerError}): a deterministic programming
386
+ * bug in how the aggregate was constructed before the restore. It
387
+ * propagates as a throw instead of riding the replay methods' `Result`
388
+ * channel, so a generic corrupted-stream handler cannot absorb it.
389
+ * Reconstitution belongs on a bare instance: construct the aggregate
390
+ * without factory-recorded events or prior mutations, then restore.
391
+ *
392
+ * Each throw site carries the safe remedy in its `reason`. Persistence
393
+ * lifecycle state is intentionally not mutable through the aggregate API:
394
+ * commit an actually saved instance through application orchestration, or
395
+ * discard a dirty instance and replay into a fresh one.
396
+ */
397
+ declare class UnreplayableAggregateError extends KitWiringError<"UNREPLAYABLE_AGGREGATE"> {
398
+ readonly identity: UnreplayableAggregateErrorOptions["identity"];
399
+ constructor(options: UnreplayableAggregateErrorOptions);
400
+ }
401
+ /**
402
+ * Constructor options for {@link MisattributedEventError} and
403
+ * {@link ForeignEventError}: the identity of the aggregate that received the
404
+ * event, and the aggregate identity fields the event carries. A missing
405
+ * field on the event matches by default, so `actual` names only what the
406
+ * event states.
407
+ */
408
+ interface AggregateIdentityMismatchOptions {
409
+ readonly expected: {
410
+ readonly aggregateType: string;
411
+ readonly aggregateId: string;
412
+ };
413
+ readonly actual: {
414
+ readonly aggregateType?: string;
415
+ readonly aggregateId?: string;
416
+ };
417
+ readonly eventType: string;
418
+ }
419
+ /**
420
+ * Thrown by `EventSourcedAggregate.apply()` when a NEW event carries an
421
+ * `aggregateId` or `aggregateType` naming a different aggregate: a
422
+ * deterministic programming bug at the call site (a hand-built or
423
+ * copied event that belongs elsewhere), caught before the event can be
424
+ * recorded and poison the own stream. Events with MISSING aggregate
425
+ * identity fields do not trip this: `apply()` stamps them from the aggregate,
426
+ * the same guarantee `createEvent` gives. A wiring error, distinct
427
+ * from {@link ForeignEventError} on purpose: a wrong new event is a
428
+ * bug in today's code, a wrong PERSISTED row is corrupted or miswired
429
+ * infrastructure, and handlers for one must not absorb the other.
430
+ */
431
+ declare class MisattributedEventError extends KitWiringError<"MISATTRIBUTED_EVENT"> {
432
+ /** Identity of the aggregate that received the event. */
433
+ readonly expected: AggregateIdentityMismatchOptions["expected"];
434
+ /** Aggregate identity fields the event carries. */
435
+ readonly actual: AggregateIdentityMismatchOptions["actual"];
436
+ readonly eventType: string;
437
+ constructor(options: AggregateIdentityMismatchOptions);
438
+ }
439
+ /** Constructor options for {@link SnapshotVersionNotRestoredError}. */
440
+ interface SnapshotVersionNotRestoredErrorOptions {
441
+ /** The aggregate the error names. */
442
+ readonly identity: {
443
+ readonly aggregateType: string;
444
+ readonly aggregateId: string;
445
+ };
446
+ /** The version the snapshot carries. */
447
+ readonly snapshotVersion: number;
448
+ /** The version the factory's aggregate reports. */
449
+ readonly restoredVersion: number;
450
+ }
451
+ /**
452
+ * Thrown by `reconstituteAggregateFromSnapshot` when the `reconstitute`
453
+ * factory returns an aggregate at a version other than the snapshot
454
+ * version. The factory ignored the version parameter, usually a forgotten
455
+ * `markReconstituted(version)`. A wiring error in the snapshot model,
456
+ * never snapshot corruption: routing it into the discard-and-refold
457
+ * channel would mask it as perpetual silent refolding.
458
+ */
459
+ declare class SnapshotVersionNotRestoredError extends KitWiringError<"SNAPSHOT_VERSION_NOT_RESTORED"> {
460
+ readonly identity: SnapshotVersionNotRestoredErrorOptions["identity"];
461
+ readonly snapshotVersion: number;
462
+ readonly restoredVersion: number;
463
+ constructor(options: SnapshotVersionNotRestoredErrorOptions);
464
+ }
465
+ /**
466
+ * The structural-integrity rejection for a stored snapshot. A consumer's
467
+ * adapter-owned `SnapshotModel` may throw it from migration or reconstitution
468
+ * when the blob could not have been produced by any version of the model
469
+ * (missing fields, impossible types, truncated data). An
470
+ * `InfrastructureError`, because corrupted persistence is a storage
471
+ * problem, never a business rejection; it is nevertheless RECOVERABLE
472
+ * by design: the repository catches it, discards the derived snapshot, and
473
+ * refolds from the authoritative event stream.
474
+ */
475
+ declare class SnapshotCorruptedError extends InfrastructureError<"SNAPSHOT_CORRUPTED"> {
476
+ constructor(message: string, cause?: unknown);
477
+ }
478
+ /**
479
+ * Thrown when an event reaches the aggregate's recording paths
480
+ * (`apply`, `setState`, `addDomainEvent`) without having been minted by
481
+ * the kit's constructors: `createDomainEvent`,
482
+ * `createDomainEventFromFacts`, `createUncommittedDomainEvent`, or the
483
+ * aggregate `createEvent` helper. Those constructors deep-freeze the
484
+ * event, defensively copy payload and metadata, and mark the result as
485
+ * minted. The mark has two tiers: a
486
+ * module-private one for events of this loaded copy of the kit, and a
487
+ * cooperative `Symbol.for` brand that a second loaded copy stamps and
488
+ * recognizes. Anything else (a hand-rolled literal, a shallow-frozen
489
+ * copy with mutable nested data) is rejected: a mutable event recorded
490
+ * next to a state change can silently diverge from it afterwards. A
491
+ * wiring error: deterministic bug at the call site, the remedy is
492
+ * minting through the constructors. The gate catches accidents, not
493
+ * adversaries: code in the same process can fake the brand.
494
+ */
495
+ declare class UnmintedEventError extends KitWiringError<"UNMINTED_EVENT"> {
496
+ constructor(eventType: string);
497
+ }
498
+ /** Constructor options for {@link ReentrantEventRecordingError}. */
499
+ interface ReentrantEventRecordingErrorOptions {
500
+ /** The aggregate the error names. */
501
+ readonly identity: {
502
+ readonly aggregateType: string;
503
+ readonly aggregateId: string;
504
+ };
505
+ }
506
+ /**
507
+ * Thrown by `recordPendingEvents` when the aggregate's pending-event list
508
+ * changes while its events are being stamped: a stamp provider that
509
+ * directly or transitively triggers a new decision on the same aggregate
510
+ * would otherwise have that decision silently discarded when recording
511
+ * replaces the pending list. Recording is atomic: when this guard fires,
512
+ * every decision (including the re-entrant one) remains unrecorded. A
513
+ * wiring error: deterministic bug at the call site, the remedy is keeping
514
+ * stamp providers free of domain decisions.
515
+ */
516
+ declare class ReentrantEventRecordingError extends KitWiringError<"REENTRANT_EVENT_RECORDING"> {
517
+ readonly identity: ReentrantEventRecordingErrorOptions["identity"];
518
+ constructor(options: ReentrantEventRecordingErrorOptions);
519
+ }
520
+ /** Constructor options for {@link DuplicateEventIdError}. */
521
+ interface DuplicateEventIdErrorOptions {
522
+ /** The aggregate the error names. */
523
+ readonly identity: {
524
+ readonly aggregateType: string;
525
+ readonly aggregateId: string;
526
+ };
527
+ /** The event id two pending events would have shared. */
528
+ readonly eventId: string;
529
+ }
530
+ /**
531
+ * Thrown when two facts of one aggregate would carry the same `eventId`.
532
+ * Two causes, two sites: the aggregate rejects a recorded event that is
533
+ * already pending at the append, before the state moves; and
534
+ * `recordPendingEvents` rejects a stamp provider that returns one reused
535
+ * stamp (or repeats an explicit id). Either would mint two distinct facts
536
+ * sharing one identity, and downstream idempotent consumers keyed on
537
+ * `eventId` silently drop one of them. A wiring error: deterministic bug at
538
+ * the append site or in the stamp provider, the remedy is one fresh
539
+ * identity per fact.
540
+ */
541
+ declare class DuplicateEventIdError extends KitWiringError<"DUPLICATE_EVENT_ID"> {
542
+ readonly identity: DuplicateEventIdErrorOptions["identity"];
543
+ /** The event id two pending events would have shared. */
544
+ readonly eventId: string;
545
+ constructor(options: DuplicateEventIdErrorOptions);
546
+ }
547
+ /** Constructor options for {@link PendingEventLimitExceededError}. */
548
+ interface PendingEventLimitExceededErrorOptions {
549
+ /** The aggregate the error names. */
550
+ readonly identity: {
551
+ readonly aggregateType: string;
552
+ readonly aggregateId: string;
553
+ };
554
+ /** The configured `maxPendingEvents`. */
555
+ readonly limit: number;
556
+ /** Events pending before the rejected recording. */
557
+ readonly pending: number;
558
+ /** Events the rejected recording would have added. */
559
+ readonly added: number;
560
+ }
561
+ /**
562
+ * Thrown when a recording would grow the pending list of an aggregate past
563
+ * `AggregateConfig.maxPendingEvents`. The check runs before the state
564
+ * moves, so the rejected decision records nothing and moves nothing. The
565
+ * limit is a modelling signal, not a runtime budget: a decision that emits
566
+ * hundreds of facts points at a missing aggregate boundary, and a retry
567
+ * repeats it. A wiring error: split the aggregate, or emit fewer facts
568
+ * per decision.
569
+ */
570
+ declare class PendingEventLimitExceededError extends KitWiringError<"PENDING_EVENT_LIMIT_EXCEEDED"> {
571
+ readonly identity: PendingEventLimitExceededErrorOptions["identity"];
572
+ readonly limit: number;
573
+ readonly pending: number;
574
+ readonly added: number;
575
+ constructor(options: PendingEventLimitExceededErrorOptions);
576
+ }
577
+ /** Constructor options for {@link PendingEventBatchMismatchError}. */
578
+ interface PendingEventBatchMismatchErrorOptions {
579
+ /** The aggregate the error names. */
580
+ readonly identity: {
581
+ readonly aggregateType: string;
582
+ readonly aggregateId: string;
583
+ };
584
+ /** Events in the committed batch. */
585
+ readonly batchLength: number;
586
+ /** Events pending on the aggregate. */
587
+ readonly pendingLength: number;
588
+ }
589
+ /**
590
+ * Thrown by the post-commit acknowledgement of an aggregate when the
591
+ * committed batch is not the prefix of its pending events any more. The
592
+ * batch is longer than the pending list, or an event in it is not the
593
+ * pending event at the same position. Acknowledging such a batch would
594
+ * drop decisions the commit never persisted or keep events it did. The
595
+ * pending list stays untouched. A wiring error in application commit
596
+ * orchestration: acknowledge exactly the batch that was enrolled, once.
597
+ */
598
+ declare class PendingEventBatchMismatchError extends KitWiringError<"PENDING_EVENT_BATCH_MISMATCH"> {
599
+ readonly identity: PendingEventBatchMismatchErrorOptions["identity"];
600
+ readonly batchLength: number;
601
+ readonly pendingLength: number;
602
+ constructor(options: PendingEventBatchMismatchErrorOptions);
603
+ }
604
+ /**
605
+ * Thrown by persisted-event consumers (including `replayHistory` and
606
+ * `Projector`) when an event carries an
607
+ * `aggregateId` or `aggregateType` that names a different aggregate:
608
+ * the persisted row belongs to someone else (a miswired stream read,
609
+ * ids colliding across aggregate types, a corrupted store). An
610
+ * `InfrastructureError`, NOT a `DomainError` (same posture as
611
+ * {@link SnapshotSchemaMismatchError}): a wrong aggregate identity is data
612
+ * corruption or wiring, never an expected business rejection, so it
613
+ * must not be absorbed by generic domain error handling or presented
614
+ * as a 4xx. It therefore PROPAGATES as a throw through the replay
615
+ * methods' `Result` contract (which reserves `Err` for `DomainError`),
616
+ * after the usual all-or-nothing rollback. History events without the
617
+ * optional aggregate identity fields pass unchecked (the fields are optional on
618
+ * the event shape); new events are covered by
619
+ * {@link MisattributedEventError}.
620
+ */
621
+ declare class ForeignEventError extends InfrastructureError<"FOREIGN_EVENT"> {
622
+ /** Identity of the aggregate that received the event. */
623
+ readonly expected: AggregateIdentityMismatchOptions["expected"];
624
+ /** Aggregate identity fields the event carries. */
625
+ readonly actual: AggregateIdentityMismatchOptions["actual"];
626
+ readonly eventType: string;
627
+ constructor(options: AggregateIdentityMismatchOptions);
628
+ }
629
+ /** How a page of a stream read breaks the `readStream` contract. */
630
+ type EventStreamPageReason = "empty_page" | "stream_vanished" | "page_past_target" | "page_over_limit" | "head_regressed" | "invalid_head";
631
+ /** Constructor options for {@link InvalidEventStreamPageError}. */
632
+ interface InvalidEventStreamPageErrorOptions {
633
+ /** The aggregate the error names. */
634
+ readonly identity: {
635
+ readonly aggregateType: string;
636
+ readonly aggregateId: string;
637
+ };
638
+ /** How the page breaks the contract; see {@link EventStreamPageReason}. */
639
+ readonly reason: EventStreamPageReason;
640
+ /** The exclusive cursor the page followed. */
641
+ readonly fromVersion: number;
642
+ /**
643
+ * The pinned inclusive target version. Absent for `invalid_head` on the
644
+ * first page: that page pins no target.
645
+ */
646
+ readonly targetVersion?: number;
647
+ /**
648
+ * The stream head the page reported, as the adapter returned it. Present
649
+ * for `head_regressed` and `invalid_head`.
650
+ */
651
+ readonly lastVersion?: unknown;
652
+ /** The stream head the first page reported. Present for `head_regressed`. */
653
+ readonly firstPageLastVersion?: number;
654
+ /**
655
+ * The number of events on the page. Present for `page_past_target` and
656
+ * `page_over_limit`.
657
+ */
658
+ readonly eventCount?: number;
659
+ /** The page limit the read asked for. Present for `page_over_limit`. */
660
+ readonly limit?: number;
661
+ }
662
+ /**
663
+ * Thrown when a page of a stream read breaks the `readStream` contract.
664
+ * `readStreamPages` checks every page it reads, and
665
+ * `reconstituteAggregateFromStreamPages` checks every page an adapter that
666
+ * pages on its own hands over. Both throw this error for the same defect.
667
+ *
668
+ * The `reason` names the defect. `empty_page`: the page holds no event,
669
+ * but events remain before the target version. `stream_vanished`: a
670
+ * continuation page reports the stream absent. `page_past_target`: the
671
+ * page holds more events than its window has left. `page_over_limit`: the
672
+ * page holds more events than the `limit` of the read. `head_regressed`: a
673
+ * continuation page reports a head below the head of the first page.
674
+ * `invalid_head`: a page of an existing stream reports a head that is not
675
+ * a safe integer of at least 1, for example `0` for a stream without
676
+ * events, or a string from a driver that returns big integers as text.
677
+ *
678
+ * No case is retryable. Fix the adapter: run `createEventStoreContractTests`
679
+ * against an EventStore adapter, and `createReplayableStreamPagesContractTests`
680
+ * against an adapter that pages on its own. Two causes lie outside the
681
+ * adapter. A stream is append-only, and only a physical removal deletes it
682
+ * (the repository guide, "Domain deletion versus physical removal"), so a
683
+ * removal that runs during the read gives `stream_vanished`. An upcaster
684
+ * that splits one stored event into several gives `page_past_target` at
685
+ * the replay, because the replay counts one version per event.
686
+ */
687
+ declare class InvalidEventStreamPageError extends InfrastructureError<"INVALID_EVENT_STREAM_PAGE"> {
688
+ readonly identity: InvalidEventStreamPageErrorOptions["identity"];
689
+ readonly reason: EventStreamPageReason;
690
+ readonly fromVersion: number;
691
+ readonly targetVersion: number | undefined;
692
+ readonly lastVersion: unknown;
693
+ readonly firstPageLastVersion: number | undefined;
694
+ readonly eventCount: number | undefined;
695
+ readonly limit: number | undefined;
696
+ constructor(options: InvalidEventStreamPageErrorOptions);
697
+ }
698
+ /** The check a {@link ReplayTargetMismatchError} reports. */
699
+ type ReplayTargetMismatchReason = "target_not_at_cursor" | "pages_short_of_target";
700
+ /** Constructor options for {@link ReplayTargetMismatchError}. */
701
+ interface ReplayTargetMismatchErrorOptions {
702
+ /** The aggregate the error names. */
703
+ readonly identity: {
704
+ readonly aggregateType: string;
705
+ readonly aggregateId: string;
706
+ };
707
+ /** The check that failed; see {@link ReplayTargetMismatchReason}. */
708
+ readonly reason: ReplayTargetMismatchReason;
709
+ /** The cursor the read started at; the replay target must start here. */
710
+ readonly fromVersion: number;
711
+ /**
712
+ * Pinned inclusive target the replay had to reach: the stream head, or
713
+ * `toVersion` on a point-in-time read.
714
+ */
715
+ readonly targetVersion: number;
716
+ /** Version the aggregate held when the check ran. */
717
+ readonly actualVersion: number;
718
+ }
719
+ /**
720
+ * Thrown by `reconstituteAggregateFromStreamPages`, or by a load recipe
721
+ * that replays on its own, when the replay target does not line up with the
722
+ * read. Events carry no stream position, so the aggregate cannot detect a
723
+ * tail that overlaps or misses the version it was reconstituted at. Only
724
+ * the caller, which pinned the target version, can compare. A single page
725
+ * that breaks its window is an {@link InvalidEventStreamPageError} instead.
726
+ *
727
+ * The `reason` names the check that failed. `target_not_at_cursor`: the
728
+ * replay target stands at a version other than the `fromVersion` the read
729
+ * used, found before any page is replayed; a reconstitution factory reports
730
+ * the wrong version. `pages_short_of_target`: the pages ended before the
731
+ * target version. The read stopped early, the stream has a gap, or an
732
+ * upcaster merged several stored events into one. For an EventStore
733
+ * adapter, run `createEventStoreContractTests` and
734
+ * `createEsRepositoryContractTests` against it and fix its windowing. None
735
+ * of the cases is retryable. A snapshot beyond its stream does not reach
736
+ * the replay: `readStreamPages` reports that window as unreachable first,
737
+ * and an adapter that pages on its own must do the same.
738
+ */
739
+ declare class ReplayTargetMismatchError extends InfrastructureError<"REPLAY_TARGET_MISMATCH"> {
740
+ readonly identity: ReplayTargetMismatchErrorOptions["identity"];
741
+ readonly reason: ReplayTargetMismatchReason;
742
+ readonly fromVersion: number;
743
+ readonly targetVersion: number;
744
+ readonly actualVersion: number;
745
+ constructor(options: ReplayTargetMismatchErrorOptions);
746
+ }
747
+ /** Constructor options for {@link ReplayRejectedError}. */
748
+ interface ReplayRejectedErrorOptions {
749
+ /** The aggregate the error names. */
750
+ readonly identity: {
751
+ readonly aggregateType: string;
752
+ readonly aggregateId: string;
753
+ };
754
+ /**
755
+ * The version the aggregate held before the rejected page. With
756
+ * `toVersion`, it bounds the window `(fromVersion, toVersion]` of the
757
+ * rejected page, not the window of the read.
758
+ */
759
+ readonly fromVersion: number;
760
+ /** The last stream position of the rejected page (inclusive). */
761
+ readonly toVersion: number;
762
+ /** The error the aggregate raised for a stored event of the page. */
763
+ readonly cause: DomainError;
764
+ }
765
+ /**
766
+ * The `Err` of `reconstituteAggregateFromStreamPages`: the aggregate
767
+ * rejected a stored event while it replayed a page of the stream.
768
+ *
769
+ * The window `(fromVersion, toVersion]` locates the rejected page, and
770
+ * `cause` holds the `DomainError` of the aggregate. A stored stream that
771
+ * the domain cannot replay is a defect of the stored data, not a request
772
+ * the caller can correct. So this is an `InfrastructureError`, and it is
773
+ * not retryable. An empty window means that the replay target rejected an
774
+ * empty history before the first page.
775
+ */
776
+ declare class ReplayRejectedError extends InfrastructureError<"REPLAY_REJECTED"> {
777
+ readonly identity: ReplayRejectedErrorOptions["identity"];
778
+ readonly fromVersion: number;
779
+ readonly toVersion: number;
780
+ readonly cause: DomainError;
781
+ constructor(options: ReplayRejectedErrorOptions);
782
+ }
783
+ /**
784
+ * Thrown when an event harvested from an aggregate cannot be safely composed
785
+ * into a commit envelope, or when an outbox can prove that accepting a
786
+ * candidate would violate its event identity/source chain. Harvest failures
787
+ * include missing `aggregateId` / `aggregateType` (downstream routing would
788
+ * break), or an
789
+ * eventful persisted aggregate did not advance its version (two commits
790
+ * would receive the same source position). These programming bugs are
791
+ * deterministic and fail identically on every retry.
792
+ *
793
+ * Deliberately **not** an {@link InfrastructureError} (same reasoning as
794
+ * {@link MissingHandlerError}): this is a deterministic programming error,
795
+ * not a transient storage failure. A `catch (e instanceof InfrastructureError)`
796
+ * retry handler, or a retrying `TransactionScope`, must NOT mask it or loop on
797
+ * it forever; it should crash loud so the caller misuse surfaces in
798
+ * development. This is why `withCommit` throws it directly and
799
+ * `UnitOfWork.run` passes it through unchanged instead of wrapping it in
800
+ * `CommitError`.
801
+ */
802
+ declare class EventHarvestError extends KitWiringError<"EVENT_HARVEST_FAILED"> {
803
+ /** The `type` of the offending event, for programmatic routing. */
804
+ readonly eventType?: string | undefined;
805
+ constructor(message: string,
806
+ /** The `type` of the offending event, for programmatic routing. */
807
+ eventType?: string | undefined);
808
+ }
809
+ /**
810
+ * Thrown at bootstrap when the global key of a kit capability registry
811
+ * already holds a value that is not a registry: another module claimed
812
+ * the key. The kit neither shares that value nor overwrites it, because a
813
+ * silent replacement would break whichever module owned the key first. A
814
+ * wiring error in the host process; the remedy is one owner per key.
815
+ */
816
+ declare class CapabilityRegistryConflictError extends KitWiringError<"CAPABILITY_REGISTRY_CONFLICT"> {
817
+ readonly key: symbol;
818
+ constructor(key: symbol);
819
+ }
820
+ /**
821
+ * Thrown when a kit operation receives an instance that this package did
822
+ * not construct: a structural lookalike, a repository DTO, or an instance
823
+ * from an incompatible copy of the package. Such an instance carries none
824
+ * of the kit-managed capabilities the operation needs. A wiring error:
825
+ * extend the kit's base classes and run one compatible package copy.
826
+ */
827
+ declare class UnmanagedInstanceError extends KitWiringError<"UNMANAGED_INSTANCE"> {
828
+ /** The kit operation that rejected the instance. */
829
+ readonly operation: string;
830
+ /** What was rejected: "aggregate", "entity", "the persistence baseline". */
831
+ readonly subject: string;
832
+ /** The rejected instance's id, when it has one. */
833
+ readonly instanceId?: unknown | undefined;
834
+ constructor(
835
+ /** The kit operation that rejected the instance. */
836
+ operation: string,
837
+ /** What was rejected: "aggregate", "entity", "the persistence baseline". */
838
+ subject: string,
839
+ /** The rejected instance's id, when it has one. */
840
+ instanceId?: unknown | undefined,
841
+ /** One extra sentence about the registry state, when it explains the rejection. */
842
+ detail?: string);
843
+ }
844
+ /** Constructor options for {@link UnregisteredHandlerError}. */
845
+ interface UnregisteredHandlerErrorOptions {
846
+ /** Which bus rejected the dispatch. */
847
+ readonly busKind: "command" | "query";
848
+ /** The message type no handler was registered for. */
849
+ readonly messageType: string;
850
+ }
851
+ /**
852
+ * Produced by the in-memory `CommandBus` / `QueryBus` when a message is
853
+ * dispatched for a type no handler was registered under: a wiring bug
854
+ * (typo in the type string, missing `register` call at bootstrap), not
855
+ * a domain or infrastructure failure.
856
+ *
857
+ * Carries the `WIRING` category (same crash-loud family as
858
+ * {@link MissingHandlerError}), and since v3 it is THROWN by `execute`
859
+ * and `executeUnsafe` alike, never delivered through the error channel:
860
+ * the channel carries expected failures a registered handler produced,
861
+ * and a generic err-branch must not absorb a mis-wired bus. Catch it
862
+ * only at a boundary that turns bugs into 500s.
863
+ */
864
+ declare class UnregisteredHandlerError extends KitWiringError<"UNREGISTERED_HANDLER"> {
865
+ readonly busKind: "command" | "query";
866
+ readonly messageType: string;
867
+ constructor(options: UnregisteredHandlerErrorOptions);
868
+ }
869
+ /** Constructor options for {@link DuplicateHandlerRegistrationError}. */
870
+ interface DuplicateHandlerRegistrationErrorOptions {
871
+ /** Which bus rejected the registration. */
872
+ readonly busKind: "command" | "query";
873
+ /** The message type a handler was already registered for. */
874
+ readonly messageType: string;
875
+ }
876
+ /**
877
+ * Produced by `CommandBus.register` / `QueryBus.register` when a handler
878
+ * is registered for a type that already has one: silent replacement would
879
+ * turn the first handler into dead code with no signal, so the wiring bug
880
+ * surfaces at registration time. Same crash-loud family as
881
+ * {@link UnregisteredHandlerError}; catch it only at a boundary that
882
+ * turns bugs into 500s.
883
+ */
884
+ declare class DuplicateHandlerRegistrationError extends KitWiringError<"DUPLICATE_HANDLER_REGISTRATION"> {
885
+ readonly busKind: "command" | "query";
886
+ readonly messageType: string;
887
+ constructor(options: DuplicateHandlerRegistrationErrorOptions);
888
+ }
889
+ /** Constructor options for {@link ErrorMapperFailedError}. */
890
+ interface ErrorMapperFailedErrorOptions {
891
+ /** Which bus was mapping the failure. */
892
+ readonly busKind: "command" | "query";
893
+ /** The registered handler's ORIGINAL failure (also set as `cause`). */
894
+ readonly handlerError: unknown;
895
+ /** The mapper failure or invalid-decision diagnostic. */
896
+ readonly mapperError: unknown;
897
+ }
898
+ /**
899
+ * Produced by the in-memory `CommandBus` / `QueryBus` when the configured
900
+ * `mapExpectedError` policy fails while classifying a registered handler's
901
+ * failure, either by throwing or by returning an invalid decision. A broken
902
+ * mapper is a wiring bug: letting its failure propagate bare would
903
+ * replace the handler's original failure entirely, and the rest of the
904
+ * kit is fastidious about never letting a secondary failure mask the
905
+ * primary one (`RollbackError.rollbackCause`, the neutralized observers).
906
+ *
907
+ * The handler's original failure is preserved as `cause` (so cause-chain
908
+ * walks, retryability checks, and error-type mapping keep working) and
909
+ * the mapper's own failure rides along as {@link mapperCause}.
910
+ *
911
+ * Carries the `WIRING` category (same crash-loud family as
912
+ * {@link MissingHandlerError} and {@link UnregisteredHandlerError}): it is
913
+ * thrown, never delivered through the error channel.
914
+ */
915
+ declare class ErrorMapperFailedError extends KitWiringError<"ERROR_MAPPER_FAILED"> {
916
+ readonly busKind: "command" | "query";
917
+ /** The mapper failure or invalid-decision diagnostic. */
918
+ readonly mapperCause: unknown;
919
+ constructor(options: ErrorMapperFailedErrorOptions);
920
+ }
921
+ /** Constructor options for {@link UnenrolledChangesError}. */
922
+ interface UnenrolledChangesErrorOptions {
923
+ /** The aggregate the error names. */
924
+ readonly identity: {
925
+ readonly aggregateType: string;
926
+ readonly aggregateId: string;
927
+ };
928
+ /** Whether the repository of the aggregate is append-only. */
929
+ readonly appendOnly?: boolean;
930
+ }
931
+ /**
932
+ * Thrown at the end of a `UnitOfWork.run` when an aggregate that was
933
+ * loaded into the identity map changed but no `update` intent was registered.
934
+ * Without this guard the changed state or pending events would be silently
935
+ * dropped.
936
+ *
937
+ * Deliberately **not** an `InfrastructureError` (same posture as
938
+ * {@link MissingHandlerError}): a programming bug that must crash loud,
939
+ * not be absorbed by a generic infrastructure-error handler. The throw
940
+ * happens inside the transaction, so the unit of work rolls back and
941
+ * leaves no partial state.
942
+ *
943
+ * **Scope of the guard.** A best-effort runtime safety net, not a proof.
944
+ * It sees aggregates that repository adapters register through
945
+ * `tracking.trackLoaded` and detects ordinary state changes through the version
946
+ * captured at load. The pending-event count remains a second guard for an
947
+ * invalid event-only mutation that did not advance the version. A freshly
948
+ * created aggregate that is never passed to `add` is invisible to the kit.
949
+ *
950
+ * An append-only repository installs no `update`, so for its aggregate the
951
+ * message names the rule instead: a loaded append-only aggregate must not
952
+ * change.
953
+ */
954
+ declare class UnenrolledChangesError extends KitWiringError<"UNENROLLED_CHANGES"> {
955
+ readonly identity: UnenrolledChangesErrorOptions["identity"];
956
+ constructor(options: UnenrolledChangesErrorOptions);
957
+ }
958
+ /** Constructor options for {@link AggregateDeletedError}. */
959
+ interface AggregateDeletedErrorOptions {
960
+ /** The aggregate the error names. */
961
+ readonly identity: {
962
+ readonly aggregateType: string;
963
+ readonly aggregateId: string;
964
+ };
965
+ }
966
+ /**
967
+ * Thrown when an aggregate removed within the current unit of work is added,
968
+ * updated, or tracked again in the same operation. Removal is final within an
969
+ * operation; writing afterwards would resurrect the row, which is always a
970
+ * use-case bug.
971
+ *
972
+ * Carries the `WIRING` category (same reasoning as
973
+ * {@link MissingHandlerError}): a programming bug that should crash
974
+ * loud, not be absorbed by a generic infrastructure-error handler.
975
+ */
976
+ declare class AggregateDeletedError extends KitWiringError<"AGGREGATE_DELETED"> {
977
+ readonly identity: AggregateDeletedErrorOptions["identity"];
978
+ constructor(options: AggregateDeletedErrorOptions);
979
+ }
980
+ /**
981
+ * Thrown by `AggregatePersistence.getById()` when an aggregate with the
982
+ * given id does not exist. `InfrastructureError` because the storage
983
+ * boundary, not a business rule, decided the row is absent. Use the
984
+ * nullable variant `findById()` if "not found" is a valid outcome.
985
+ *
986
+ * Accepts an optional `cause` so a repository adapter can wrap a lower-level
987
+ * "row not found" or driver-level error without
988
+ * losing context. Cause-chain helpers (`getRootCause`,
989
+ * `findInCauseChain`) from `@shirudo/base-error` traverse the chain.
990
+ *
991
+ * Not retryable: retrying won't make the row appear.
992
+ */
993
+ interface AggregateNotFoundErrorOptions {
994
+ /** The aggregate the error names. */
995
+ readonly identity: {
996
+ readonly aggregateType: string;
997
+ readonly aggregateId: string;
998
+ };
999
+ /** Optional lower-level error to preserve in the cause chain. */
1000
+ readonly cause?: unknown;
1001
+ }
1002
+ declare class AggregateNotFoundError extends InfrastructureError<"AGGREGATE_NOT_FOUND"> {
1003
+ readonly identity: AggregateNotFoundErrorOptions["identity"];
1004
+ constructor(options: AggregateNotFoundErrorOptions);
1005
+ }
1006
+ /**
1007
+ * Thrown by a repository's `add()` flush when a row with the
1008
+ * aggregate's id already exists (unique-constraint violation): two
1009
+ * concurrent creators raced on the same business-derived id, or the
1010
+ * id generator collided. Same delegation model as
1011
+ * {@link ConcurrencyConflictError}: the kit ships the class, the
1012
+ * consumer repository maps its driver's unique-violation signal to it
1013
+ * instead of letting a raw driver error escape -
1014
+ *
1015
+ * - Postgres: SQLSTATE `23505` (`unique_violation`)
1016
+ * - MySQL/MariaDB: errno `1062` (`ER_DUP_ENTRY`)
1017
+ * - SQLite: `SQLITE_CONSTRAINT_UNIQUE` (extended code 2067)
1018
+ *
1019
+ * `InfrastructureError` because the storage boundary detects the
1020
+ * collision. NOT retryable: re-running the same INSERT cannot succeed.
1021
+ * The right reactions are domain decisions - map to HTTP 409, or for
1022
+ * idempotency-key flows load the existing aggregate and treat the
1023
+ * request as already-applied.
1024
+ */
1025
+ interface DuplicateAggregateErrorOptions {
1026
+ /** The aggregate the error names. */
1027
+ readonly identity: {
1028
+ readonly aggregateType: string;
1029
+ readonly aggregateId: string;
1030
+ };
1031
+ /** Optional driver-level error to preserve in the cause chain. */
1032
+ readonly cause?: unknown;
1033
+ }
1034
+ declare class DuplicateAggregateError extends InfrastructureError<"DUPLICATE_AGGREGATE"> {
1035
+ readonly identity: DuplicateAggregateErrorOptions["identity"];
1036
+ constructor(options: DuplicateAggregateErrorOptions);
1037
+ }
1038
+ /**
1039
+ * Thrown by `reconstituteAggregateFromSnapshot` when the stored snapshot
1040
+ * carries a different schema version than its adapter-owned `SnapshotModel`
1041
+ * and the model declares no `migrate` function. Without the check, a snapshot
1042
+ * written against an older DTO shape would surface as an undefined-field crash on
1043
+ * the first method call after a much later restore.
1044
+ *
1045
+ * `InfrastructureError` because the storage boundary served outdated
1046
+ * data; the schema evolving past stored snapshots is an expected
1047
+ * lifecycle event, not a programming bug. NOT retryable: the recovery
1048
+ * is a code path, not a repeat. Add `migrate` to the snapshot model (upgrade
1049
+ * old DTOs in place), or catch this error in the repository, discard the
1050
+ * snapshot, and refold from the full event stream / reload from the source of
1051
+ * truth.
1052
+ */
1053
+ interface SnapshotSchemaMismatchErrorOptions {
1054
+ /** The aggregate the error names. */
1055
+ readonly identity: {
1056
+ readonly aggregateType: string;
1057
+ readonly aggregateId: string;
1058
+ };
1059
+ readonly expectedSchemaVersion: number;
1060
+ readonly actualSchemaVersion: number;
1061
+ }
1062
+ declare class SnapshotSchemaMismatchError extends InfrastructureError<"SNAPSHOT_SCHEMA_MISMATCH"> {
1063
+ readonly identity: SnapshotSchemaMismatchErrorOptions["identity"];
1064
+ readonly expectedSchemaVersion: number;
1065
+ readonly actualSchemaVersion: number;
1066
+ constructor(options: SnapshotSchemaMismatchErrorOptions);
1067
+ }
1068
+ /**
1069
+ * Why the version check failed, and whether a stored version exists to name.
1070
+ *
1071
+ * The reason is diagnostic. Callers branch on the code and on `retryable`,
1072
+ * never on the reason.
1073
+ */
1074
+ type ConcurrencyConflictReason =
1075
+ /** The aggregate is stored at another version. `actualVersion` carries it. */
1076
+ "stale_version" |
1077
+ /**
1078
+ * The write matched nothing although the stored version equals the
1079
+ * expected one. Either the write statement carries a condition beyond the
1080
+ * version, for example a tenant id. Or its version read answered from a
1081
+ * transaction snapshot instead of the current row. Both are defects of the
1082
+ * adapter, so this reason is the one that is not retryable: a predicate
1083
+ * defect fires on every write, and retrying it multiplies the load of a
1084
+ * broken deployment instead of surfacing it.
1085
+ *
1086
+ * One occurrence does not tell the two causes apart; their rates do. A
1087
+ * predicate defect fires at a flat rate whatever the load, a snapshot read
1088
+ * only when writes race. An adapter whose version read is a snapshot read
1089
+ * can opt this reason back into retrying through the `isRetryable` of its
1090
+ * retry policy.
1091
+ */
1092
+ "version_unchanged" |
1093
+ /**
1094
+ * The aggregate no longer exists. Only a store that can lose a persisted
1095
+ * record reports this. An append-only event stream cannot: a stream that
1096
+ * was never created is at version 0, which is `stale_version`.
1097
+ */
1098
+ "aggregate_absent" |
1099
+ /** The version read failed. The failure travels as the cause. */
1100
+ "version_unknown";
1101
+ type ConcurrencyConflictErrorOptions = {
1102
+ /** The aggregate the error names. */
1103
+ readonly identity: {
1104
+ readonly aggregateType: string;
1105
+ readonly aggregateId: string;
1106
+ };
1107
+ /** The write that failed, or `null` when the raising code does not know it. */
1108
+ readonly intent?: "add" | "update" | "remove" | null;
1109
+ readonly expectedVersion: number;
1110
+ /** Optional driver-level error to preserve in the cause chain. */
1111
+ readonly cause?: unknown;
1112
+ } & ({
1113
+ readonly reason: "stale_version" | "version_unchanged";
1114
+ /** The version the store holds. */
1115
+ readonly actualVersion: number;
1116
+ } | {
1117
+ readonly reason: "aggregate_absent" | "version_unknown";
1118
+ /** No stored version exists to name. */
1119
+ readonly actualVersion?: null;
1120
+ });
1121
+ /**
1122
+ * Surfaced by a Unit-of-Work flush when the aggregate's expected version does
1123
+ * not match the version currently persisted: i.e. another writer
1124
+ * updated the aggregate concurrently. The canonical optimistic-
1125
+ * concurrency signal; the App-Service typically reloads, re-applies
1126
+ * the use case, and retries, or surfaces HTTP 409 to the caller.
1127
+ *
1128
+ * **Retry means a FRESH unit of work** (a new `UnitOfWork.run()` /
1129
+ * `withCommit` invocation): reload, re-apply, and register `update` again. Do NOT catch this
1130
+ * inside the same `run()` callback and continue: the failed aggregate
1131
+ * is already enrolled (its events would be committed for a write that
1132
+ * never happened) and the identity map still serves the same stale
1133
+ * instance to any in-place "reload".
1134
+ *
1135
+ * `InfrastructureError` because the persistence layer (not a domain
1136
+ * rule) detects the race. Its `retryable` follows the reason, so the
1137
+ * `isRetryable` predicate from `@shirudo/base-error` picks up every reason
1138
+ * but `version_unchanged`, which names a defect of the adapter.
1139
+ */
1140
+ declare class ConcurrencyConflictError extends InfrastructureError<"CONCURRENCY_CONFLICT"> {
1141
+ readonly identity: ConcurrencyConflictErrorOptions["identity"];
1142
+ /** The write that failed, or `null` when the raising code does not know it. */
1143
+ readonly intent: NonNullable<ConcurrencyConflictErrorOptions["intent"]> | null;
1144
+ readonly expectedVersion: number;
1145
+ /** The stored version, or `null` when none exists to name. */
1146
+ readonly actualVersion: number | null;
1147
+ readonly reason: ConcurrencyConflictReason;
1148
+ constructor(options: ConcurrencyConflictErrorOptions);
1149
+ }
1150
+ /**
1151
+ * Options bag for {@link IdempotencyKeyReuseError}.
1152
+ */
1153
+ interface IdempotencyKeyReuseErrorOptions {
1154
+ readonly key: string;
1155
+ readonly storedFingerprint: string;
1156
+ readonly receivedFingerprint: string;
1157
+ /** Optional driver-level error to preserve in the cause chain. */
1158
+ readonly cause?: unknown;
1159
+ }
1160
+ /**
1161
+ * Thrown by `IdempotencyStore.claim()` when the same idempotency key
1162
+ * arrives with a DIFFERENT command fingerprint than the one it was
1163
+ * first claimed with: the caller is reusing a key for a different
1164
+ * command. Replaying the stored outcome would answer a question that
1165
+ * was never asked; rejecting is the only safe reaction.
1166
+ *
1167
+ * `InfrastructureError` because the store detects the collision, same
1168
+ * delegation model as {@link DuplicateAggregateError}. NOT retryable:
1169
+ * re-sending the same mismatched pair cannot succeed. Map it to an
1170
+ * unprocessable/conflict application outcome.
1171
+ */
1172
+ declare class IdempotencyKeyReuseError extends InfrastructureError<"IDEMPOTENCY_KEY_REUSE"> {
1173
+ readonly key: string;
1174
+ readonly storedFingerprint: string;
1175
+ readonly receivedFingerprint: string;
1176
+ constructor(options: IdempotencyKeyReuseErrorOptions);
1177
+ }
1178
+ /** Options bag for {@link IdempotencyClaimLostError}. */
1179
+ interface IdempotencyClaimLostErrorOptions {
1180
+ readonly key: string;
1181
+ readonly token: string;
1182
+ /** Optional driver-level error to preserve in the cause chain. */
1183
+ readonly cause?: unknown;
1184
+ }
1185
+ /**
1186
+ * Thrown when a leased idempotency owner tries to renew, complete, or
1187
+ * reconcile through a claim token that no longer owns the key. The usual
1188
+ * cause is lease expiry followed by a successful takeover. The stale
1189
+ * execution must abort before its transaction commits; retrying starts from
1190
+ * a fresh claim or replays the winner.
1191
+ */
1192
+ declare class IdempotencyClaimLostError extends InfrastructureError<"IDEMPOTENCY_CLAIM_LOST"> {
1193
+ readonly key: string;
1194
+ readonly token: string;
1195
+ constructor(options: IdempotencyClaimLostErrorOptions);
1196
+ }
1197
+ /**
1198
+ * Options bag for {@link IdempotencyInFlightError}.
1199
+ */
1200
+ interface IdempotencyInFlightErrorOptions {
1201
+ readonly key: string;
1202
+ /** Optional driver-level error to preserve in the cause chain. */
1203
+ readonly cause?: unknown;
1204
+ }
1205
+ /**
1206
+ * Thrown by `IdempotencyStore.claim()` when the key is already claimed
1207
+ * by an execution that has not completed yet: the first delivery of the
1208
+ * command is still running (or crashed mid-flight on a
1209
+ * non-transactional store). Retryable by design: a later retry either
1210
+ * finds the completed outcome and replays it, or finds the claim
1211
+ * released (rolled back) and executes fresh. `RetryingTransactionScope`
1212
+ * picks this up through the `retryable` flag without extra wiring.
1213
+ */
1214
+ declare class IdempotencyInFlightError extends InfrastructureError<"IDEMPOTENCY_IN_FLIGHT"> {
1215
+ readonly key: string;
1216
+ constructor(options: IdempotencyInFlightErrorOptions);
1217
+ }
1218
+ /** Options bag for {@link IdempotencyReconciliationRequiredError}. */
1219
+ interface IdempotencyReconciliationRequiredErrorOptions {
1220
+ readonly key: string;
1221
+ readonly fingerprint: string;
1222
+ readonly token: string;
1223
+ readonly expiredAt: string;
1224
+ }
1225
+ /**
1226
+ * An expired staged outcome cannot be replayed or discarded until the
1227
+ * application checks the authoritative write model. Immediate retry without
1228
+ * that evidence cannot make progress, so this error is deliberately not
1229
+ * marked retryable.
1230
+ */
1231
+ declare class IdempotencyReconciliationRequiredError extends InfrastructureError<"IDEMPOTENCY_RECONCILIATION_REQUIRED"> {
1232
+ readonly key: string;
1233
+ readonly fingerprint: string;
1234
+ readonly token: string;
1235
+ readonly expiredAt: string;
1236
+ constructor(options: IdempotencyReconciliationRequiredErrorOptions);
1237
+ }
1238
+ /**
1239
+ * Thrown by `IdempotencyStore.complete()` when no pending claim exists
1240
+ * for the key: `complete` ran without a preceding successful `claim`
1241
+ * in the same execution, or against a key whose claim was already
1242
+ * completed or abandoned. Always a wiring bug in hand-rolled
1243
+ * orchestration (`withIdempotentCommit` cannot produce it), hence the
1244
+ * crash-loud category.
1245
+ */
1246
+ declare class IdempotencyCompletionWithoutClaimError extends KitWiringError<"IDEMPOTENCY_COMPLETED_WITHOUT_CLAIM"> {
1247
+ readonly key: string;
1248
+ constructor(key: string);
1249
+ }
1250
+ /**
1251
+ * The closed union of every error code the kit itself can produce
1252
+ * (consumer subclasses of {@link DomainError} / {@link InfrastructureError}
1253
+ * add their own on top). Useful for building `switch` tables or
1254
+ * base-error `matchError` cases that cover kit and consumer codes
1255
+ * together, without importing anything from base-error.
1256
+ */
1257
+ type KitErrorCode = "AGGREGATE_DELETED" | "AGGREGATE_NOT_FOUND" | "AGGREGATE_TRACKING" | "CAPABILITY_REGISTRY_CONFLICT" | "COMMIT_FAILED" | "CONCURRENCY_CONFLICT" | "DIRECT_STATE_MUTATION" | "DOMAIN_TRANSITION_GUARD_REJECTED" | "DUPLICATE_AGGREGATE" | "DUPLICATE_EVENT_ID" | "DUPLICATE_HANDLER_REGISTRATION" | "ERROR_MAPPER_FAILED" | "EVENT_AGGREGATE_IDENTITY_INVALID" | "EVENT_BUS_CLOSED" | "EVENT_HARVEST_FAILED" | "EVENT_ID_INVALID" | "EVENT_ID_REQUIRED" | "EVENT_OCCURRED_AT_INVALID" | "EVENT_OCCURRED_AT_REQUIRED" | "EVENT_SCHEMA_VERSION_INVALID" | "EVENT_TYPE_INVALID" | "FOLD_RETURNED_NO_STATE" | "FOREIGN_EVENT" | "HOSTILE_STATE_KEY" | "IDEMPOTENCY_CLAIM_LOST" | "IDEMPOTENCY_COMPLETED_WITHOUT_CLAIM" | "IDEMPOTENCY_IN_FLIGHT" | "IDEMPOTENCY_KEY_REUSE" | "IDEMPOTENCY_RECONCILIATION_REQUIRED" | "IN_MEMORY_CAPACITY_EXCEEDED" | "INVALID_DOMAIN_MACHINE_CONTEXT" | "INVALID_DOMAIN_MACHINE_DEFINITION" | "INVALID_DOMAIN_MACHINE_INPUT" | "INVALID_DOMAIN_MACHINE_SNAPSHOT" | "INVALID_DOMAIN_TRANSITION" | "INVALID_DOMAIN_TRANSITION_GUARD_RESULT" | "INVALID_DOMAIN_TRANSITION_RESULT" | "INVALID_COMMAND_MESSAGE" | "INVALID_EVENT_STREAM_PAGE" | "INVALID_FLUSH_STATEMENT" | "INVALID_INTEGRATION_MESSAGE" | "INVALID_MONEY" | "INVALID_REPOSITORY_ADAPTER" | "INVALID_REPOSITORY_DEFINITION" | "INVALID_VERSION" | "MISATTRIBUTED_EVENT" | "MISSING_ENTITY_ID" | "MISSING_FOLD" | "MISSING_HANDLER" | "MONEY_CURRENCY_MISMATCH" | "MONEY_PRECISION_LOSS" | "MONEY_SCALE_MISMATCH" | "NESTED_UNIT_OF_WORK" | "PENDING_EVENT_BATCH_MISMATCH" | "PENDING_EVENT_LIMIT_EXCEEDED" | "PROJECTION_GAP" | "PROJECTION_IDENTITY_VIOLATION" | "PROJECTION_ORDER_VIOLATION" | "PROJECTION_RECEIPT_VIOLATION" | "PUBLISH_DEPTH_EXCEEDED" | "REENTRANT_DOMAIN_STATE_MACHINE_EVALUATION" | "REENTRANT_EVENT_RECORDING" | "REPLAY_REJECTED" | "REPLAY_TARGET_MISMATCH" | "REPOSITORY_ERROR_MAPPING_FAILED" | "ROLLBACK_FAILED" | "SNAPSHOT_CORRUPTED" | "SNAPSHOT_SCHEMA_MISMATCH" | "SNAPSHOT_TIME_INVALID" | "SNAPSHOT_VERSION_NOT_RESTORED" | "TRANSACTION_CLOSED" | "UNENROLLED_CHANGES" | "UNKNOWN_CURRENCY" | "UNMANAGED_INSTANCE" | "UNMINTED_EVENT" | "UNPROJECTABLE_EVENT" | "UNREGISTERED_HANDLER" | "UNREPLAYABLE_AGGREGATE";
1258
+ //#endregion
1259
+ export { PendingEventLimitExceededErrorOptions as $, IdempotencyInFlightErrorOptions as A, InvalidEventStreamPageErrorOptions as B, FoldReturnedNoStateError as C, UnreplayableAggregateError as Ct, IdempotencyClaimLostErrorOptions as D, isWiringErrorLike as Dt, IdempotencyClaimLostError as E, isInfrastructureErrorLike as Et, InMemoryCapacityExceededError as F, KitWiringError as G, InvalidVersionError as H, InMemoryCapacityExceededErrorOptions as I, MissingFoldError as J, MisattributedEventError as K, InfrastructureError as L, IdempotencyKeyReuseErrorOptions as M, IdempotencyReconciliationRequiredError as N, IdempotencyCompletionWithoutClaimError as O, IdempotencyReconciliationRequiredErrorOptions as P, PendingEventLimitExceededError as Q, InvalidCommandMessageError as R, EventStreamPageReason as S, UnregisteredHandlerErrorOptions as St, HostileStateKeyError as T, isDomainErrorLike as Tt, KitErrorCode as U, InvalidIntegrationMessageError as V, KitErrorOptions as W, PendingEventBatchMismatchError as X, MissingHandlerError as Y, PendingEventBatchMismatchErrorOptions as Z, DuplicateHandlerRegistrationError as _, UnenrolledChangesErrorOptions as _t, AggregateNotFoundErrorOptions as a, ReentrantEventRecordingErrorOptions as at, ErrorMapperFailedErrorOptions as b, UnprojectableEventError as bt, ConcurrencyConflictErrorOptions as c, ReplayTargetMismatchError as ct, DirectStateMutationErrorOptions as d, SnapshotCorruptedError as dt, ProjectionGapError as et, DomainError as f, SnapshotSchemaMismatchError as ft, DuplicateEventIdErrorOptions as g, UnenrolledChangesError as gt, DuplicateEventIdError as h, SnapshotVersionNotRestoredErrorOptions as ht, AggregateNotFoundError as i, ReentrantEventRecordingError as it, IdempotencyKeyReuseError as j, IdempotencyInFlightError as k, ConcurrencyConflictReason as l, ReplayTargetMismatchErrorOptions as lt, DuplicateAggregateErrorOptions as m, SnapshotVersionNotRestoredError as mt, AggregateDeletedErrorOptions as n, ProjectionOrderViolationError as nt, CapabilityRegistryConflictError as o, ReplayRejectedError as ot, DuplicateAggregateError as p, SnapshotSchemaMismatchErrorOptions as pt, MissingEntityIdError as q, AggregateIdentityMismatchOptions as r, ProjectionReceiptViolationError as rt, ConcurrencyConflictError as s, ReplayRejectedErrorOptions as st, AggregateDeletedError as t, ProjectionIdentityViolationError as tt, DirectStateMutationError as u, ReplayTargetMismatchReason as ut, DuplicateHandlerRegistrationErrorOptions as v, UnmanagedInstanceError as vt, ForeignEventError as w, UnreplayableAggregateErrorOptions as wt, EventHarvestError as x, UnregisteredHandlerError as xt, ErrorMapperFailedError as y, UnmintedEventError as yt, InvalidEventStreamPageError as z };
1260
+ //# sourceMappingURL=kit-errors.d.ts.map