@shirudo/ddd-kit 3.0.0-rc.13 → 3.0.0-rc.15

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.
package/dist/index.d.ts CHANGED
@@ -1,33 +1,18 @@
1
1
  import { $ as PendingEventLimitExceededErrorOptions, A as IdempotencyInFlightErrorOptions, B as InvalidEventStreamPageErrorOptions, C as FoldReturnedNoStateError, Ct as UnreplayableAggregateError, D as IdempotencyClaimLostErrorOptions, Dt as isWiringErrorLike, E as IdempotencyClaimLostError, Et as isInfrastructureErrorLike, F as InMemoryCapacityExceededError, G as KitWiringError, H as InvalidVersionError, I as InMemoryCapacityExceededErrorOptions, J as MissingFoldError, K as MisattributedEventError, L as InfrastructureError, M as IdempotencyKeyReuseErrorOptions, N as IdempotencyReconciliationRequiredError, O as IdempotencyCompletionWithoutClaimError, P as IdempotencyReconciliationRequiredErrorOptions, Q as PendingEventLimitExceededError, R as InvalidCommandMessageError, S as EventStreamPageReason, St as UnregisteredHandlerErrorOptions, T as HandBuiltEventError, Tt as isDomainErrorLike, U as KitErrorCode, V as InvalidIntegrationMessageError, W as KitErrorOptions, X as PendingEventBatchMismatchError, Y as MissingHandlerError, Z as PendingEventBatchMismatchErrorOptions, _ as DuplicateHandlerRegistrationError, _t as UnmanagedInstanceError, a as AggregateNotFoundErrorOptions, at as ReentrantEventRecordingError, b as ErrorMapperFailedErrorOptions, bt as UnregisteredChangesErrorOptions, c as ConcurrencyConflictErrorOptions, ct as ReplayRejectedErrorOptions, d as DirectStateMutationErrorOptions, dt as ReplayTargetMismatchReason, et as ProjectionGapError, f as DomainError, ft as SnapshotCorruptedError, g as DuplicateEventIdErrorOptions, gt as SnapshotVersionNotRestoredErrorOptions, h as DuplicateEventIdError, ht as SnapshotVersionNotRestoredError, i as AggregateNotFoundError, it as PrototypePollutionError, j as IdempotencyKeyReuseError, k as IdempotencyInFlightError, l as ConcurrencyConflictReason, lt as ReplayTargetMismatchError, m as DuplicateAggregateErrorOptions, mt as SnapshotSchemaMismatchErrorOptions, n as AggregateDeletedErrorOptions, nt as ProjectionOrderViolationError, o as CapabilityRegistryConflictError, ot as ReentrantEventRecordingErrorOptions, p as DuplicateAggregateError, pt as SnapshotSchemaMismatchError, q as MissingEntityIdError, r as AggregateIdentityMismatchOptions, rt as ProjectionReceiptViolationError, s as ConcurrencyConflictError, st as ReplayRejectedError, t as AggregateDeletedError, tt as ProjectionIdentityViolationError, u as DirectStateMutationError, ut as ReplayTargetMismatchErrorOptions, v as DuplicateHandlerRegistrationErrorOptions, vt as UnprojectableEventError, w as ForeignEventError, wt as UnreplayableAggregateErrorOptions, x as EventCollectionError, xt as UnregisteredHandlerError, y as ErrorMapperFailedError, yt as UnregisteredChangesError, z as InvalidEventStreamPageError } from "./chunks/kit-errors.js";
2
- import { $ as Version, A as IdempotentCommitRequest, At as DomainEventFactoryOptions, B as CommitRegistrationOptions, Bt as createStampedDomainEvent, C as IdempotencyClaim, Ct as AnyUnstampedDomainEvent, D as IdempotencyReconciliation, Dt as CreateUnstampedDomainEventOptions, E as IdempotencyOperationErrorContext, Et as CreateStampedDomainEventOptions, F as DeadLetterDeadline, Ft as UnstampedDomainEvent, G as TransactionalOptions, Gt as Command, H as WithCommitWorkResult, Ht as mergeMetadata, I as DeadlineStore, It as UnstampedDomainEventOf, J as OnceOptions, Jt as JsonObject, K as EventBus, Kt as CommandHandler, L as DueDeadline, Lt as copyMetadata, M as IdempotentExecution, Mt as EventIdFactory, N as WithIdempotentCommitDeps, Nt as EventMetadata, O as IdempotencyReconciliationDecision, Ot as DomainEvent, P as withIdempotentCommit, Pt as PendingDomainEvent, Q as ReplayableAggregate, R as AggregateCommitToken, Rt as createDomainEvent, S as isPositionAfter, St as AnyDomainEvent, T as IdempotencyLease, Tt as CreateDomainEventStampOptions, U as withCommit, Ut as ClockFactory, V as WithCommitDeps, Vt as defaultDomainEventFactory, W as TransactionScope, Wt as AggregateIdentity, X as Aggregate, Xt as JsonValue, Y as PublishOptions, Yt as JsonPrimitive, Z as AggregateSnapshot, _ as IdentityMap, _t as ExecutionContext, a as EventStoreAppendOptions, at as CommandMessageContent, b as ProjectionCheckpointStore, bt as EventCommitCandidate, c as StreamReadResult, ct as CommandOutboxMapper, d as RepositoryTracking, dt as routeEventsToCommandOutbox, et as sameVersion, f as UnitOfWorkIdentityMap, ft as DeadLetterRecord, g as AggregateClass, gt as OutboxWriter, h as PersistenceModel, ht as OutboxRecord, i as EventStore, it as CommandCommitOriginCandidate, j as IdempotentCommitResult, jt as DomainEventStamp, k as IdempotencyStore, kt as DomainEventFactory, l as AggregatePersistenceWrite, lt as CommandOutboxWriter, m as PersistenceLifecycle, mt as Outbox, n as ReplayableStreamPages, nt as Id, o as EventStreamReader, ot as CommandMessageRelationships, p as PersistenceChanges, pt as DispatchTrackingOutbox, q as EventHandler, qt as PublishedCommand, r as reconstituteAggregateFromStreamPages, rt as IdGenerator, s as ReadStreamOptions, st as CommandOutboxCommitCandidate, t as SnapshotStore, tt as toVersion, u as AggregateWriteIntent, ut as DurableCommandMessage, v as Projection, vt as CommitPosition, w as IdempotencyClaimHandle, wt as CreateDomainEventOptions, x as ProjectionPosition, xt as EventCommitCandidatePosition, y as ProjectionCheckpoint, yt as CommittedDomainEvent, z as CommitRegistration, zt as createDomainEventFactory } from "./chunks/snapshot-store.js";
2
+ import { $ as PublishOptions, $t as JsonPrimitive, A as IdempotentCommitRequest, At as CreateEventOptions, B as CommitRegistrationOptions, Bt as UnstampedDomainEventOf, C as IdempotencyClaim, Ct as CommittedDomainEvent, D as IdempotencyReconciliation, Dt as AnyUnstampedDomainEvent, E as IdempotencyOperationErrorContext, Et as AnyDomainEvent, F as DeadLetterDeadline, Ft as DomainEventStamp, G as DomainEventStampProvider, Gt as defaultDomainEventFactory, H as WithCommitWorkResult, Ht as createDomainEvent, I as DeadlineStore, It as EventIdFactory, J as TransactionScope, Jt as AggregateIdentity, K as SharedDomainEventStampOptions, Kt as mergeMetadata, L as DueDeadline, Lt as EventMetadata, M as IdempotentExecution, Mt as DomainEvent, N as WithIdempotentCommitDeps, Nt as DomainEventFactory, O as IdempotencyReconciliationDecision, Ot as CreateDomainEventOptions, P as withIdempotentCommit, Pt as DomainEventFactoryOptions, Q as OnceOptions, Qt as JsonObject, R as AggregateCommitToken, Rt as PendingDomainEvent, S as isPositionAfter, St as CommitPosition, T as IdempotencyLease, Tt as EventCommitCandidatePosition, U as withCommit, Ut as createDomainEventFactory, V as WithCommitDeps, Vt as copyMetadata, W as DomainEventStampFactory, Wt as createStampedDomainEvent, X as EventBus, Xt as CommandHandler, Y as TransactionalOptions, Yt as Command, Z as EventHandler, Zt as PublishedCommand, _ as IdentityMap, _t as DispatchTrackingOutbox, a as EventStoreAppendOptions, at as toVersion, b as ProjectionCheckpointStore, bt as OutboxWriter, c as StreamReadResult, ct as CommandCommitOriginCandidate, d as RepositoryTracking, dt as CommandOutboxCommitCandidate, en as JsonValue, et as Aggregate, f as UnitOfWorkIdentityMap, ft as CommandOutboxMapper, g as AggregateClass, gt as DeadLetterRecord, h as PersistenceModel, ht as routeEventsToCommandOutbox, i as EventStore, it as sameVersion, j as IdempotentCommitResult, jt as CreateStampedDomainEventOptions, k as IdempotencyStore, kt as CreateDomainEventStampOptions, l as AggregatePersistenceWrite, lt as CommandMessageContent, m as PersistenceLifecycle, mt as DurableCommandMessage, n as ReplayableStreamPages, nt as ReplayableAggregate, o as EventStreamReader, ot as Id, p as PersistenceChanges, pt as CommandOutboxWriter, q as stampPendingEvents, qt as ClockFactory, r as reconstituteAggregateFromStreamPages, rt as Version, s as ReadStreamOptions, st as IdGenerator, t as SnapshotStore, tt as AggregateSnapshot, u as AggregateWriteIntent, ut as CommandMessageRelationships, v as Projection, vt as Outbox, w as IdempotencyClaimHandle, wt as EventCommitCandidate, x as ProjectionPosition, xt as ExecutionContext, y as ProjectionCheckpoint, yt as OutboxRecord, z as CommitRegistration, zt as UnstampedDomainEvent } from "./chunks/snapshot-store.js";
3
3
  import { Result } from "@shirudo/result";
4
4
  import { ValidationError } from "@shirudo/base-error";
5
5
  //#region src/application/cqrs/internal/bus-internals.d.ts
6
6
  /**
7
7
  * Keeps manual result typing available only for the deliberately untyped
8
8
  * default map. Concrete maps, typed or refined index maps, and `any` keep their
9
- * mapped result contract authoritative.
9
+ * mapped result contract authoritative. Under the `object` bound, only `any`
10
+ * satisfies `unknown extends TMap`; the usual `0 extends 1 & TMap` probe
11
+ * reduces to `never` there and misses `any`.
10
12
  */
11
- type UntypedMapDispatch<TMap extends Record<string, unknown>, TMessage> = 0 extends 1 & TMap ? never : string extends keyof TMap ? Record<string, unknown> extends TMap ? TMessage : never : never;
13
+ type UntypedMapDispatch<TMap extends object, TMessage> = unknown extends TMap ? never : string extends keyof TMap ? Record<string, unknown> extends TMap ? TMessage : never : never;
12
14
  //#endregion
13
15
  //#region src/application/cqrs/command/command-bus.d.ts
14
- /**
15
- * Type map for command types to their return types.
16
- * Used to improve type inference in CommandBus.
17
- *
18
- * @example
19
- * ```typescript
20
- * type MyCommandMap = {
21
- * CreateOrder: OrderId;
22
- * CancelOrder: void;
23
- * };
24
- *
25
- * const bus = new CommandBus<MyCommandMap>();
26
- * const result = await bus.execute({ type: "CreateOrder", ... });
27
- * // result: Result<OrderId, string> ← automatically inferred
28
- * ```
29
- */
30
- type CommandTypeMap = Record<string, unknown>;
31
16
  /**
32
17
  * Construction options for {@link CommandBus}.
33
18
  *
@@ -71,7 +56,7 @@ interface CommandBusOptions<E = string> {
71
56
  * // result: Result<unknown, string>
72
57
  * ```
73
58
  */
74
- interface ICommandBus<TMap extends CommandTypeMap = CommandTypeMap, E = string> {
59
+ interface ICommandBus<TMap extends object = Record<string, unknown>, E = string> {
75
60
  /**
76
61
  * Executes a command by dispatching it to the registered handler.
77
62
  * When a type map is provided, the return type is inferred from the command type.
@@ -143,7 +128,7 @@ interface ICommandBus<TMap extends CommandTypeMap = CommandTypeMap, E = string>
143
128
  * const result = await bus.execute({ type: "CreateOrder", ... });
144
129
  * ```
145
130
  */
146
- declare class CommandBus<TMap extends CommandTypeMap = CommandTypeMap, E = string> implements ICommandBus<TMap, E> {
131
+ declare class CommandBus<TMap extends object = Record<string, unknown>, E = string> implements ICommandBus<TMap, E> {
147
132
  private readonly handlers;
148
133
  private readonly mapExpectedError;
149
134
  constructor(options?: CommandBusOptions<E>);
@@ -248,23 +233,6 @@ interface Query {
248
233
  type QueryHandler<Q extends Query, R> = (query: Q) => Promise<R>;
249
234
  //#endregion
250
235
  //#region src/application/cqrs/query/query-bus.d.ts
251
- /**
252
- * Type map for query types to their return types.
253
- * Used to improve type inference in QueryBus.
254
- *
255
- * @example
256
- * ```typescript
257
- * type MyQueryMap = {
258
- * GetOrder: Order | null;
259
- * ListOrders: Order[];
260
- * };
261
- *
262
- * const bus = new QueryBus<MyQueryMap>();
263
- * const result = await bus.execute({ type: "GetOrder", orderId: "123" });
264
- * // result: Result<Order | null, string> ← automatically inferred
265
- * ```
266
- */
267
- type QueryTypeMap = Record<string, unknown>;
268
236
  /**
269
237
  * Construction options for {@link QueryBus}.
270
238
  *
@@ -307,7 +275,7 @@ interface QueryBusOptions<E = string> {
307
275
  * // result: Result<unknown, string>
308
276
  * ```
309
277
  */
310
- interface IQueryBus<TMap extends QueryTypeMap = QueryTypeMap, E = string> {
278
+ interface IQueryBus<TMap extends object = Record<string, unknown>, E = string> {
311
279
  /**
312
280
  * Executes a query by dispatching it to the registered handler.
313
281
  * When a type map is provided, the return type is inferred from the query type.
@@ -391,7 +359,7 @@ interface IQueryBus<TMap extends QueryTypeMap = QueryTypeMap, E = string> {
391
359
  * const result = await bus.execute({ type: "GetOrder", orderId: "123" });
392
360
  * ```
393
361
  */
394
- declare class QueryBus<TMap extends QueryTypeMap = QueryTypeMap, E = string> implements IQueryBus<TMap, E> {
362
+ declare class QueryBus<TMap extends object = Record<string, unknown>, E = string> implements IQueryBus<TMap, E> {
395
363
  private readonly handlers;
396
364
  private readonly mapExpectedError;
397
365
  constructor(options?: QueryBusOptions<E>);
@@ -737,8 +705,8 @@ declare class DeadlineProcessor<TPayload = unknown> extends PollLoop {
737
705
  private readonly classifyFailure?;
738
706
  constructor(options: DeadlineProcessorOptions<TPayload>);
739
707
  /**
740
- * One full delivery pass (the `run`/`drainOnce` shell lives on
741
- * {@link PollLoop}): delivers due deadlines batch by batch until
708
+ * One full delivery pass (the `run`/`drainOnce` shell lives on the
709
+ * shared poll loop): delivers due deadlines batch by batch until
742
710
  * nothing is due or a cycle contained a failure.
743
711
  */
744
712
  protected pass(signal?: AbortSignal): Promise<"drained" | "stopped">;
@@ -1212,8 +1180,8 @@ declare class OutboxDispatcher<Evt extends AnyDomainEvent> extends PollLoop {
1212
1180
  private readonly trackingOutbox?;
1213
1181
  constructor(options: OutboxDispatcherOptions<Evt>);
1214
1182
  /**
1215
- * One full dispatch pass (the `run`/`drainOnce` shell lives on
1216
- * {@link PollLoop}): dispatches pending records batch by batch until
1183
+ * One full dispatch pass (the `run`/`drainOnce` shell lives on the
1184
+ * shared poll loop): dispatches pending records batch by batch until
1217
1185
  * the backlog is empty or a failure stops progress. A `"stopped"`
1218
1186
  * pass leaves the failed record pending (or dead-lettered by a
1219
1187
  * tracking outbox); the next cycle retries it.
@@ -1563,37 +1531,6 @@ declare class RollbackError extends InfrastructureError<"ROLLBACK_FAILED"> {
1563
1531
  constructor(cause: unknown, rollbackCause: unknown);
1564
1532
  }
1565
1533
  //#endregion
1566
- //#region src/application/unit-of-work/stamp-pending-events.d.ts
1567
- /** Minimal shell role accepted by {@link stampPendingEvents}. */
1568
- type DomainEventStampFactory = Pick<DomainEventFactory, "createStamp">;
1569
- /** Per-decision stamp provider for metadata that depends on the event. */
1570
- type DomainEventStampProvider<TEvent extends AnyDomainEvent> = (event: UnstampedDomainEventOf<TEvent>, index: number) => DomainEventStamp;
1571
- /**
1572
- * Stamp options that every decision of one stamping shares: a stamp time
1573
- * and metadata such as the correlation id of the request. An `eventId` is per
1574
- * fact, so the factory creates it for each decision.
1575
- */
1576
- type SharedDomainEventStampOptions = Omit<CreateDomainEventStampOptions, "eventId">;
1577
- /**
1578
- * Records every still-unstamped event accepted by an aggregate.
1579
- *
1580
- * The function is a command that also returns the recorded batch, a
1581
- * deliberate exception to command-query separation: the caller hands the
1582
- * batch on to persistence and needs no second read of `pendingEvents`.
1583
- *
1584
- * Recording is atomic with respect to the aggregate's pending list: if stamp
1585
- * creation or validation fails, every decision remains unstamped. A
1586
- * successful second call returns the same event objects and does not read the
1587
- * factory again, which keeps event identity stable across transaction retries.
1588
- *
1589
- * Pass a `DomainEventFactory` (only its `createStamp` role is required) for one
1590
- * uniform stamping policy. Add `stampOptions` for metadata that every
1591
- * decision of the batch shares, such as the correlation id of the request.
1592
- * Pass a callback instead when metadata depends on the concrete decision.
1593
- */
1594
- declare function stampPendingEvents<TId extends Id<string>, TEvent extends AnyDomainEvent>(aggregate: Aggregate<TId, TEvent>, factory: DomainEventStampFactory, stampOptions?: SharedDomainEventStampOptions): ReadonlyArray<TEvent>;
1595
- declare function stampPendingEvents<TId extends Id<string>, TEvent extends AnyDomainEvent>(aggregate: Aggregate<TId, TEvent>, createStamp: DomainEventStampProvider<TEvent>): ReadonlyArray<TEvent>;
1596
- //#endregion
1597
1534
  //#region src/application/unit-of-work/unit-of-work.d.ts
1598
1535
  /**
1599
1536
  * What the application work callback receives: repositories already bound to
@@ -1624,6 +1561,11 @@ interface RunOptions {
1624
1561
  * `TransactionScope`. Use `AbortSignal.timeout(ms)` for a deadline.
1625
1562
  */
1626
1563
  readonly signal?: AbortSignal;
1564
+ /**
1565
+ * Stamp options that every event stamped in this run shares, for example
1566
+ * the correlation id of the request.
1567
+ */
1568
+ readonly stampOptions?: SharedDomainEventStampOptions;
1627
1569
  }
1628
1570
  declare const repositoryDefinitionBrand: unique symbol;
1629
1571
  /** Adapter wiring accepted by {@link defineRepository}. */
@@ -1832,6 +1774,12 @@ interface UnitOfWorkDeps<Evt extends AnyDomainEvent, TCtx, TDefinitions extends
1832
1774
  * application phase. Default `30000`ms.
1833
1775
  */
1834
1776
  postCommitTimeoutMs?: number;
1777
+ /**
1778
+ * See `withCommit`: stamps every pending event that is still unstamped
1779
+ * when `add`, `update`, or `remove` registers its aggregate. Defaults to
1780
+ * `createDomainEventFactory()`.
1781
+ */
1782
+ eventStampFactory?: DomainEventStampFactory;
1835
1783
  repositories: CompatibleRepositoryDefinitions<Evt, TCtx, TDefinitions>;
1836
1784
  }
1837
1785
  /**
@@ -2388,8 +2336,8 @@ interface AggregateConfig<TState = unknown> extends EntityConfig<TState> {
2388
2336
  * tracking, the kit-internal post-commit acknowledgement capability,
2389
2337
  * the `markReconstituted` post-load marker, and the `createEvent` helper
2390
2338
  * that auto-injects `aggregateId` + `aggregateType` on every event the
2391
- * aggregate emits. The application shell stamps the pending decisions
2392
- * with `stampPendingEvents` before persistence.
2339
+ * aggregate emits. The commit stamps the pending decisions when it
2340
+ * registers the aggregate, before persistence.
2393
2341
  *
2394
2342
  * Consumers do NOT extend this class directly; extend
2395
2343
  * `StateStoredAggregate` for state-stored aggregates or
@@ -2599,15 +2547,22 @@ declare abstract class BaseAggregate<TState, TId extends Id<string>, TEvent exte
2599
2547
  * Creates the immutable business fact accepted by this aggregate without
2600
2548
  * reading a clock, generating an id, or attaching tracing metadata.
2601
2549
  *
2602
- * The application shell stamps pending events after the domain operation
2603
- * and before persistence. Payload schema version stays here, next to the
2550
+ * The commit stamps pending events when it registers the aggregate,
2551
+ * before persistence. Payload schema version stays here, next to the
2604
2552
  * concrete event producer, rather than in shell-owned stamp data.
2605
2553
  */
2606
- protected createEvent<E extends TEvent>(type: E["type"], payload: E["payload"], options?: Omit<CreateUnstampedDomainEventOptions, "aggregateId" | "aggregateType">): UnstampedDomainEventOf<E>;
2554
+ protected createEvent<E extends TEvent>(type: E["type"], payload: E["payload"], options?: CreateEventOptions): UnstampedDomainEventOf<E>;
2607
2555
  }
2608
2556
  //#endregion
2609
2557
  //#region src/domain/aggregate/event-sourced-aggregate.d.ts
2610
- type Fold<TState, TEvent> = (state: TState, event: TEvent) => TState;
2558
+ /**
2559
+ * Folds one event of type `TEvent` into the state of an event-sourced
2560
+ * aggregate and returns the next state. Each entry of
2561
+ * `EventSourcedAggregate.folds` is a fold. The fold receives the event
2562
+ * without its stamp: a live `apply` folds the event before the commit stamps
2563
+ * it.
2564
+ */
2565
+ type Fold<TState, TEvent extends AnyDomainEvent> = (state: TState, event: UnstampedDomainEventOf<TEvent>) => TState;
2611
2566
  /**
2612
2567
  * Base class for Event-Sourced Aggregate Roots (Vernon, IDDD Chapter 8).
2613
2568
  *
@@ -2797,9 +2752,9 @@ declare abstract class EventSourcedAggregate<TState, TId extends Id<string>, TEv
2797
2752
  * producing silently divergent state. When a time or identity changes a
2798
2753
  * business decision, pass it in the payload.
2799
2754
  */
2800
- protected abstract readonly folds: { [K in TEvent["type"]]: Fold<TState, UnstampedDomainEventOf<Extract<TEvent, {
2755
+ protected abstract readonly folds: { [K in TEvent["type"]]: Fold<TState, Extract<TEvent, {
2801
2756
  type: K;
2802
- }>>>; };
2757
+ }>>; };
2803
2758
  }
2804
2759
  /**
2805
2760
  * Reconstitutes an event-sourced aggregate from one page of history and
@@ -3004,7 +2959,7 @@ interface DeepOmitOptions {
3004
2959
  * path, so memoising the first path's result would be wrong. This is
3005
2960
  * inherently exponential for diamond-shaped sharing (a node reachable
3006
2961
  * via 2^n paths is cloned 2^n times); the walk aborts with a descriptive
3007
- * error after {@link PATH_SENSITIVE_VISIT_BUDGET} node visits instead of
2962
+ * error after a fixed budget of node visits instead of
3008
2963
  * hanging the process.
3009
2964
  *
3010
2965
  * **Prototype-pollution safety.** `__proto__` and `constructor` keys
@@ -3258,7 +3213,8 @@ declare abstract class ValueObject<T extends object> implements IValueObject<T>
3258
3213
  //#region src/domain/value-object/value-object-or-issues.d.ts
3259
3214
  /**
3260
3215
  * Builds an immutable value object while collecting **all** validation
3261
- * violations into a single {@link ValidationError}, instead of failing on the
3216
+ * violations into a single `ValidationError` of `@shirudo/base-error`,
3217
+ * instead of failing on the
3262
3218
  * first one. This is the Result-first, multi-error counterpart to
3263
3219
  * `valueObjectOrError` (which returns a single string message).
3264
3220
  *
@@ -4210,7 +4166,7 @@ interface RetryPolicy {
4210
4166
  maxDelayMs?: number;
4211
4167
  /**
4212
4168
  * Classifier deciding whether an error is worth retrying. Default
4213
- * {@link someChainRetryable} (walks the cause chain for the loose
4169
+ * `someChainRetryable` of `@shirudo/base-error` (walks the cause chain for the loose
4214
4170
  * `retryable === true` marker, so a `ConcurrencyConflictError` matches
4215
4171
  * even when an adapter wraps it, unless its reason is
4216
4172
  * `version_unchanged`, which names a defect a retry repeats). Override
@@ -4333,6 +4289,10 @@ declare class InMemorySnapshotStore<TState = unknown> implements SnapshotStore<T
4333
4289
  }
4334
4290
  //#endregion
4335
4291
  //#region src/persistence/snapshot-store/snapshot-model.d.ts
4292
+ /**
4293
+ * The fields of an aggregate that a snapshot model reads.
4294
+ * @inline
4295
+ */
4336
4296
  interface SnapshotAggregate {
4337
4297
  readonly id: Id<string>;
4338
4298
  readonly version: Version;
@@ -4400,5 +4360,5 @@ declare function captureAggregateSnapshot<TAggregate extends SnapshotAggregate,
4400
4360
  */
4401
4361
  declare function reconstituteAggregateFromSnapshot<TAggregate extends SnapshotAggregate, TSnapshotState>(model: SnapshotModel<TAggregate, TSnapshotState>, id: TAggregate["id"], snapshot: AggregateSnapshot<unknown>): TAggregate;
4402
4362
  //#endregion
4403
- export { type AbsentStreamPages, type Aggregate, type AggregateClass, type AggregateCommitToken, type AggregateConfig, AggregateDeletedError, type AggregateDeletedErrorOptions, type AggregateIdentity, type AggregateIdentityMismatchOptions, AggregateNotFoundError, type AggregateNotFoundErrorOptions, type AggregatePersistence, type AggregatePersistenceWrite, type AggregateSnapshot, AggregateTrackingError, type AggregateTrackingErrorOptions, type AggregateTrackingReason, type AggregateWriteIntent, type AggregateWriteRegistration, type AnyDomainEvent, type AnyUnstampedDomainEvent, type AppendOnlyWriteRegistration, CapabilityRegistryConflictError, type ClassifyConcurrencyConflictOptions, type ClockFactory, type Command, CommandBus, type CommandBusOptions, type CommandCommitOriginCandidate, type CommandHandler, type CommandMessageContent, type CommandMessageRelationships, type CommandOutboxCommitCandidate, type CommandOutboxMapper, type CommandOutboxWriter, CommitError, type CommitPosition, type CommitRegistration, type CommitRegistrationOptions, type CommittedDomainEvent, type CompatibleRepositoryDefinitions, ConcurrencyConflictError, type ConcurrencyConflictErrorOptions, type ConcurrencyConflictReason, type CreateDomainEventOptions, type CreateDomainEventStampOptions, type CreateStampedDomainEventOptions, type DeadLetterDeadline, type DeadLetterRecord, DeadlineProcessor, type DeadlineProcessorObservers, type DeadlineProcessorOptions, type DeadlineStore, type DeepOmitKey, type DeepOmitOptions, type DeepOmitPathSegment, type DeliveryFailureAssessment, type DeliveryFailureClassifier, type DeliveryFailureKind, DirectStateMutationError, type DirectStateMutationErrorOptions, type DispatchTrackingOutbox, DomainError, type DomainErrorClass, type DomainEvent, type DomainEventFactory, type DomainEventFactoryOptions, type DomainEventStamp, type DomainEventStampFactory, type DomainEventStampProvider, type DomainEventValidationCode, DomainEventValidationError, type DomainEventValidationField, type DueDeadline, DuplicateAggregateError, type DuplicateAggregateErrorOptions, DuplicateEventIdError, type DuplicateEventIdErrorOptions, DuplicateHandlerRegistrationError, type DuplicateHandlerRegistrationErrorOptions, type DurableCommandMessage, Entity, type EntityConfig, ErrorMapperFailedError, type ErrorMapperFailedErrorOptions, type EventBus, EventBusClosedError, type EventBusObservers, type EventBusOptions, EventCollectionError, type EventCommitCandidate, type EventCommitCandidatePosition, type EventHandler, type EventIdFactory, type EventMetadata, EventSourcedAggregate, type EventStore, type EventStoreAppendOptions, type EventStreamPageReason, type EventStreamReader, type ExecutionContext, type FlushStatementReason, FoldReturnedNoStateError, ForeignEventError, HandBuiltEventError, type HandlerFailureReport, type ICommandBus, type IEntity, type IQueryBus, type Id, type IdGenerator, type IdempotencyClaim, type IdempotencyClaimHandle, IdempotencyClaimLostError, type IdempotencyClaimLostErrorOptions, IdempotencyCompletionWithoutClaimError, IdempotencyInFlightError, type IdempotencyInFlightErrorOptions, IdempotencyKeyReuseError, type IdempotencyKeyReuseErrorOptions, type IdempotencyLease, type IdempotencyOperationErrorContext, type IdempotencyReconciliation, type IdempotencyReconciliationDecision, IdempotencyReconciliationRequiredError, type IdempotencyReconciliationRequiredErrorOptions, type IdempotencyStore, type IdempotentCommitRequest, type IdempotentCommitResult, type IdempotentExecution, type Identifiable, IdentityMap, InMemoryCapacityExceededError, type InMemoryCapacityExceededErrorOptions, InMemoryDeadlineStore, type InMemoryDeadlineStoreOptions, InMemoryEventStore, type InMemoryEventStoreOptions, InMemoryIdempotencyStore, type InMemoryIdempotencyStoreOptions, InMemoryOutbox, type InMemoryOutboxOptions, InMemoryProjectionCheckpointStore, type InMemoryProjectionCheckpointStoreOptions, InMemorySnapshotStore, type InMemorySnapshotStoreOptions, type InMemoryTransaction, type InMemoryTransactionParticipant, InMemoryTransactionScope, InProcessEventBus, InfrastructureError, type IntegrationMessage, type IntegrationMessageContent, type IntegrationMessageMapper, type IntegrationMessageRelationships, InvalidCommandMessageError, InvalidEventStreamPageError, type InvalidEventStreamPageErrorOptions, InvalidFlushStatementError, type InvalidFlushStatementErrorOptions, InvalidIntegrationMessageError, InvalidRepositoryAdapterError, type InvalidRepositoryAdapterErrorOptions, type InvalidRepositoryAdapterReason, InvalidRepositoryDefinitionError, InvalidVersionError, type JsonObject, type JsonPrimitive, type JsonValue, type KitErrorCode, type KitErrorOptions, type MatchedRows, MisattributedEventError, MissingEntityIdError, MissingFoldError, MissingHandlerError, NestedUnitOfWorkError, type OnceOptions, type Outbox, OutboxDispatcher, type OutboxDispatcherObservers, type OutboxDispatcherOptions, type OutboxRecord, type OutboxSink, type OutboxWriter, type PendingDomainEvent, PendingEventBatchMismatchError, type PendingEventBatchMismatchErrorOptions, PendingEventLimitExceededError, type PendingEventLimitExceededErrorOptions, type PersistenceChanges, type PersistenceLifecycle, type PersistenceModel, type PhysicalRemovalRegistration, type PinTargetVersionOptions, type PinnedTargetVersion, type ProjectOptions, type Projection, type ProjectionBatchResult, type ProjectionCheckpoint, type ProjectionCheckpointStore, type ProjectionEventHandler, type ProjectionFromHandlersOptions, ProjectionGapError, type ProjectionHandlers, ProjectionIdentityViolationError, ProjectionOrderViolationError, type ProjectionPosition, ProjectionReceiptViolationError, Projector, type ProjectorOptions, PrototypePollutionError, type PublishAbortedReport, type PublishChainState, type PublishChainStore, PublishDepthExceededError, type PublishOptions, type PublishedCommand, type Query, QueryBus, type QueryBusOptions, type QueryHandler, type ReachableStreamPages, type ReadStreamOptions, type ReadStreamPagesOptions, ReentrantEventRecordingError, type ReentrantEventRecordingErrorOptions, ReplayRejectedError, type ReplayRejectedErrorOptions, ReplayTargetMismatchError, type ReplayTargetMismatchErrorOptions, type ReplayTargetMismatchReason, type ReplayableAggregate, type ReplayableStreamPages, type RepositoriesOf, type Repository, type RepositoryDefinition, type RepositoryDefinitionOptions, RepositoryErrorMappingFailedError, type RepositoryErrorMappingFailedErrorOptions, type RepositoryTracking, type RetryPolicy, RetryingTransactionScope, RollbackError, type RunOptions, type SharedDomainEventStampOptions, SnapshotCorruptedError, type SnapshotModel, SnapshotSchemaMismatchError, type SnapshotSchemaMismatchErrorOptions, type SnapshotStore, SnapshotTimeValidationError, SnapshotVersionNotRestoredError, type SnapshotVersionNotRestoredErrorOptions, Specification, type SpecificationComposite, StateStoredAggregate, type StateValidator, type StreamPages, type StreamReadResult, type SubscriptionThresholdReport, TransactionClosedError, type TransactionScope, type TransactionalOptions, UnitOfWork, type UnitOfWorkContext, type UnitOfWorkDeps, type UnitOfWorkIdentityMap, UnmanagedInstanceError, UnprojectableEventError, type UnreachableStreamPages, UnregisteredChangesError, type UnregisteredChangesErrorOptions, UnregisteredHandlerError, type UnregisteredHandlerErrorOptions, UnreplayableAggregateError, type UnreplayableAggregateErrorOptions, type UnstampedDomainEvent, type UnstampedDomainEventOf, ValueObject, type ValueObjectOf, type Version, type VersionedFlushStatements, type VersionedWrite, type WithCommitDeps, type WithCommitWorkResult, type WithIdempotentCommitDeps, captureAggregateSnapshot, classifyConcurrencyConflict, copyMetadata, copyPlainState, createDomainEvent, createDomainEventFactory, createIntegrationMessage, createStampedDomainEvent, decodeIntegrationMessage, deepEqual, deepFreeze, deepOmit, defaultDomainEventFactory, defineRepository, defineSnapshotModel, domainErrorToResult, encodeIntegrationMessage, entityIds, eventBusSink, findEntityById, hasEntityId, ignoreProjectionEvent, integrationMessageToCommittedEvent, isDomainErrorLike, isInfrastructureErrorLike, isPositionAfter, isWiringErrorLike, mergeMetadata, outboxWriterAcceptingEventLoss, pinTargetVersion, projectionFromHandlers, readStreamPages, reconstituteAggregateFromHistory, reconstituteAggregateFromSnapshot, reconstituteAggregateFromStreamPages, removeEntityById, replaceEntityById, routeEventsToCommandOutbox, sameEntity, sameVersion, specification, stampPendingEvents, toVersion, updateEntityById, valueObject, valueObjectEquals, valueObjectEqualsExcept, valueObjectOrError, valueObjectOrIssues, versionedFlush, withCommit, withIdempotentCommit };
4363
+ export { type AbsentStreamPages, type Aggregate, type AggregateClass, type AggregateCommitToken, type AggregateConfig, AggregateDeletedError, type AggregateDeletedErrorOptions, type AggregateIdentity, type AggregateIdentityMismatchOptions, AggregateNotFoundError, type AggregateNotFoundErrorOptions, type AggregatePersistence, type AggregatePersistenceWrite, type AggregateSnapshot, AggregateTrackingError, type AggregateTrackingErrorOptions, type AggregateTrackingReason, type AggregateWriteIntent, type AggregateWriteRegistration, type AnyDomainEvent, type AnyUnstampedDomainEvent, type AppendOnlyWriteRegistration, CapabilityRegistryConflictError, type ClassifyConcurrencyConflictOptions, type ClockFactory, type Command, CommandBus, type CommandBusOptions, type CommandCommitOriginCandidate, type CommandHandler, type CommandMessageContent, type CommandMessageRelationships, type CommandOutboxCommitCandidate, type CommandOutboxMapper, type CommandOutboxWriter, CommitError, type CommitPosition, type CommitRegistration, type CommitRegistrationOptions, type CommittedDomainEvent, type CompatibleRepositoryDefinitions, ConcurrencyConflictError, type ConcurrencyConflictErrorOptions, type ConcurrencyConflictReason, type CreateDomainEventOptions, type CreateDomainEventStampOptions, type CreateEventOptions, type CreateStampedDomainEventOptions, type DeadLetterDeadline, type DeadLetterRecord, DeadlineProcessor, type DeadlineProcessorObservers, type DeadlineProcessorOptions, type DeadlineStore, type DeepOmitKey, type DeepOmitOptions, type DeepOmitPathSegment, type DeliveryFailureAssessment, type DeliveryFailureClassifier, type DeliveryFailureKind, DirectStateMutationError, type DirectStateMutationErrorOptions, type DispatchTrackingOutbox, DomainError, type DomainErrorClass, type DomainEvent, type DomainEventFactory, type DomainEventFactoryOptions, type DomainEventStamp, type DomainEventStampFactory, type DomainEventStampProvider, type DomainEventValidationCode, DomainEventValidationError, type DomainEventValidationField, type DueDeadline, DuplicateAggregateError, type DuplicateAggregateErrorOptions, DuplicateEventIdError, type DuplicateEventIdErrorOptions, DuplicateHandlerRegistrationError, type DuplicateHandlerRegistrationErrorOptions, type DurableCommandMessage, Entity, type EntityConfig, ErrorMapperFailedError, type ErrorMapperFailedErrorOptions, type EventBus, EventBusClosedError, type EventBusObservers, type EventBusOptions, EventCollectionError, type EventCommitCandidate, type EventCommitCandidatePosition, type EventHandler, type EventIdFactory, type EventMetadata, EventSourcedAggregate, type EventStore, type EventStoreAppendOptions, type EventStreamPageReason, type EventStreamReader, type ExecutionContext, type FlushStatementReason, type Fold, FoldReturnedNoStateError, ForeignEventError, HandBuiltEventError, type HandlerFailureReport, type ICommandBus, type IEntity, type IQueryBus, type Id, type IdGenerator, type IdempotencyClaim, type IdempotencyClaimHandle, IdempotencyClaimLostError, type IdempotencyClaimLostErrorOptions, IdempotencyCompletionWithoutClaimError, IdempotencyInFlightError, type IdempotencyInFlightErrorOptions, IdempotencyKeyReuseError, type IdempotencyKeyReuseErrorOptions, type IdempotencyLease, type IdempotencyOperationErrorContext, type IdempotencyReconciliation, type IdempotencyReconciliationDecision, IdempotencyReconciliationRequiredError, type IdempotencyReconciliationRequiredErrorOptions, type IdempotencyStore, type IdempotentCommitRequest, type IdempotentCommitResult, type IdempotentExecution, type Identifiable, IdentityMap, InMemoryCapacityExceededError, type InMemoryCapacityExceededErrorOptions, InMemoryDeadlineStore, type InMemoryDeadlineStoreOptions, InMemoryEventStore, type InMemoryEventStoreOptions, InMemoryIdempotencyStore, type InMemoryIdempotencyStoreOptions, InMemoryOutbox, type InMemoryOutboxOptions, InMemoryProjectionCheckpointStore, type InMemoryProjectionCheckpointStoreOptions, InMemorySnapshotStore, type InMemorySnapshotStoreOptions, type InMemoryTransaction, type InMemoryTransactionParticipant, InMemoryTransactionScope, InProcessEventBus, InfrastructureError, type IntegrationMessage, type IntegrationMessageContent, type IntegrationMessageMapper, type IntegrationMessageRelationships, InvalidCommandMessageError, InvalidEventStreamPageError, type InvalidEventStreamPageErrorOptions, InvalidFlushStatementError, type InvalidFlushStatementErrorOptions, InvalidIntegrationMessageError, InvalidRepositoryAdapterError, type InvalidRepositoryAdapterErrorOptions, type InvalidRepositoryAdapterReason, InvalidRepositoryDefinitionError, InvalidVersionError, type JsonObject, type JsonPrimitive, type JsonValue, type KitErrorCode, type KitErrorOptions, type MatchedRows, MisattributedEventError, MissingEntityIdError, MissingFoldError, MissingHandlerError, NestedUnitOfWorkError, type OnceOptions, type Outbox, OutboxDispatcher, type OutboxDispatcherObservers, type OutboxDispatcherOptions, type OutboxRecord, type OutboxSink, type OutboxWriter, type PendingDomainEvent, PendingEventBatchMismatchError, type PendingEventBatchMismatchErrorOptions, PendingEventLimitExceededError, type PendingEventLimitExceededErrorOptions, type PersistenceChanges, type PersistenceLifecycle, type PersistenceModel, type PhysicalRemovalRegistration, type PinTargetVersionOptions, type PinnedTargetVersion, type ProjectOptions, type Projection, type ProjectionBatchResult, type ProjectionCheckpoint, type ProjectionCheckpointStore, type ProjectionEventHandler, type ProjectionFromHandlersOptions, ProjectionGapError, type ProjectionHandlers, ProjectionIdentityViolationError, ProjectionOrderViolationError, type ProjectionPosition, ProjectionReceiptViolationError, Projector, type ProjectorOptions, PrototypePollutionError, type PublishAbortedReport, type PublishChainState, type PublishChainStore, PublishDepthExceededError, type PublishOptions, type PublishedCommand, type Query, QueryBus, type QueryBusOptions, type QueryHandler, type ReachableStreamPages, type ReadStreamOptions, type ReadStreamPagesOptions, ReentrantEventRecordingError, type ReentrantEventRecordingErrorOptions, ReplayRejectedError, type ReplayRejectedErrorOptions, ReplayTargetMismatchError, type ReplayTargetMismatchErrorOptions, type ReplayTargetMismatchReason, type ReplayableAggregate, type ReplayableStreamPages, type RepositoriesOf, type Repository, type RepositoryDefinition, type RepositoryDefinitionOptions, RepositoryErrorMappingFailedError, type RepositoryErrorMappingFailedErrorOptions, type RepositoryTracking, type RetryPolicy, RetryingTransactionScope, RollbackError, type RunOptions, type SharedDomainEventStampOptions, SnapshotCorruptedError, type SnapshotModel, SnapshotSchemaMismatchError, type SnapshotSchemaMismatchErrorOptions, type SnapshotStore, SnapshotTimeValidationError, SnapshotVersionNotRestoredError, type SnapshotVersionNotRestoredErrorOptions, Specification, type SpecificationComposite, StateStoredAggregate, type StateValidator, type StreamPages, type StreamReadResult, type SubscriptionThresholdReport, TransactionClosedError, type TransactionScope, type TransactionalOptions, UnitOfWork, type UnitOfWorkContext, type UnitOfWorkDeps, type UnitOfWorkIdentityMap, UnmanagedInstanceError, UnprojectableEventError, type UnreachableStreamPages, UnregisteredChangesError, type UnregisteredChangesErrorOptions, UnregisteredHandlerError, type UnregisteredHandlerErrorOptions, UnreplayableAggregateError, type UnreplayableAggregateErrorOptions, type UnstampedDomainEvent, type UnstampedDomainEventOf, ValueObject, type ValueObjectOf, type Version, type VersionedFlushStatements, type VersionedWrite, type WithCommitDeps, type WithCommitWorkResult, type WithIdempotentCommitDeps, captureAggregateSnapshot, classifyConcurrencyConflict, copyMetadata, copyPlainState, createDomainEvent, createDomainEventFactory, createIntegrationMessage, createStampedDomainEvent, decodeIntegrationMessage, deepEqual, deepFreeze, deepOmit, defaultDomainEventFactory, defineRepository, defineSnapshotModel, domainErrorToResult, encodeIntegrationMessage, entityIds, eventBusSink, findEntityById, hasEntityId, ignoreProjectionEvent, integrationMessageToCommittedEvent, isDomainErrorLike, isInfrastructureErrorLike, isPositionAfter, isWiringErrorLike, mergeMetadata, outboxWriterAcceptingEventLoss, pinTargetVersion, projectionFromHandlers, readStreamPages, reconstituteAggregateFromHistory, reconstituteAggregateFromSnapshot, reconstituteAggregateFromStreamPages, removeEntityById, replaceEntityById, routeEventsToCommandOutbox, sameEntity, sameVersion, specification, stampPendingEvents, toVersion, updateEntityById, valueObject, valueObjectEquals, valueObjectEqualsExcept, valueObjectOrError, valueObjectOrIssues, versionedFlush, withCommit, withIdempotentCommit };
4404
4364
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -404,6 +404,42 @@ function captureObserverFunctions(context, observers, required) {
404
404
  return Object.freeze(captured);
405
405
  }
406
406
 
407
+ //#endregion
408
+ //#region src/domain/aggregate/pending-event-recording.ts
409
+ const { registry: capabilities$1, require } = createGlobalCapabilityRegistry(Symbol.for("@shirudo/ddd-kit/pending-event-recording-registry/v2"));
410
+ /** Resolves the recording capability or throws `UnmanagedInstanceError`. */
411
+ function requirePendingEventRecordingCapability(aggregate, operation) {
412
+ return require(aggregate, operation, "aggregate");
413
+ }
414
+ function registerPendingEventRecordingCapability(aggregate, capability) {
415
+ capabilities$1.set(aggregate, Object.freeze(capability));
416
+ }
417
+
418
+ //#endregion
419
+ //#region src/application/unit-of-work/stamp-pending-events.ts
420
+ function stampPendingEvents(aggregate, source, stampOptions) {
421
+ return recordWith(requirePendingEventRecordingCapability(aggregate, "stampPendingEvents"), source, stampOptions);
422
+ }
423
+ /**
424
+ * Stamps the pending events that are still unstamped when the commit
425
+ * registers an aggregate. An aggregate whose pending events all carry a
426
+ * stamp stays untouched: an explicit {@link stampPendingEvents} call keeps
427
+ * its stamps, and an instance without the recording capability passes when
428
+ * it has nothing to stamp.
429
+ */
430
+ function stampAtRegistration(aggregate, factory, stampOptions, operation) {
431
+ if (aggregate.pendingEvents.every(isStampedDomainEvent)) return;
432
+ recordWith(requirePendingEventRecordingCapability(aggregate, operation), factory, stampOptions);
433
+ }
434
+ function recordWith(capability, source, stampOptions) {
435
+ const shared = {
436
+ occurredAt: stampOptions?.occurredAt,
437
+ metadata: stampOptions?.metadata
438
+ };
439
+ const createStamp = typeof source === "function" ? source : () => source.createStamp(shared);
440
+ return capability.record((event, index) => createStamp(event, index));
441
+ }
442
+
407
443
  //#endregion
408
444
  //#region src/application/cqrs/handler.ts
409
445
  /**
@@ -417,7 +453,7 @@ function registrationDiverged(record) {
417
453
  return record.aggregate.version !== record.version || pending.length !== record.events.length || record.events.some((event, index) => event !== pending[index]);
418
454
  }
419
455
  /** One token registry per transactional callback attempt. */
420
- function createCommitTokenScope() {
456
+ function createCommitTokenScope(stamp) {
421
457
  const recordsByToken = /* @__PURE__ */ new WeakMap();
422
458
  const tokensByAggregate = /* @__PURE__ */ new WeakMap();
423
459
  let issuedTokenCount = 0;
@@ -436,8 +472,8 @@ function createCommitTokenScope() {
436
472
  }
437
473
  const eventLifecycle = requirePendingEventLifecycleCapability(aggregate, "withCommit registration");
438
474
  const token = Object.freeze(Object.create(null));
475
+ stamp(aggregate, "withCommit registration");
439
476
  const events = aggregate.pendingEvents;
440
- for (const event of events) if (!isStampedDomainEvent(event)) throw new EventCollectionError(`withCommit: event "${event.type}" has not been stamped. Call stampPendingEvents(aggregate, createStamp) in the application shell before persistence or outbox collection.`, event.type);
441
477
  const persistedVersion = eventLifecycle.persistedVersion();
442
478
  tokensByAggregate.set(aggregate, token);
443
479
  recordsByToken.set(token, {
@@ -609,17 +645,21 @@ async function withCommit(deps, fn) {
609
645
  }
610
646
  /**
611
647
  * {@link withCommit} with a check per attempt before the commit. The scope
612
- * receives `onAttemptStart` in its transactional options.
648
+ * receives `onAttemptStart` in its transactional options. The callback also
649
+ * receives the stamping step of the registration, so that a caller that
650
+ * records the batch before it registers can stamp first.
613
651
  */
614
652
  async function withCheckedCommit(deps, fn, onAttemptStart) {
615
653
  const postCommitTimeoutMs = deps.postCommitTimeoutMs ?? 3e4;
616
654
  assertTimerDelay("withCommit", "postCommitTimeoutMs", postCommitTimeoutMs);
617
655
  if (deps.signal?.aborted) throw abortReason(deps.signal, "withCommit aborted before opening a transaction");
656
+ const eventStampFactory = deps.eventStampFactory ?? defaultDomainEventFactory;
657
+ const stamp = (aggregate, operation) => stampAtRegistration(aggregate, eventStampFactory, deps.stampOptions, operation);
618
658
  const { result, commitRecords, events } = await deps.scope.transactional(async (ctx) => {
619
- const tokenScope = createCommitTokenScope();
659
+ const tokenScope = createCommitTokenScope(stamp);
620
660
  let fnResult;
621
661
  try {
622
- fnResult = await fn(ctx, tokenScope.registration);
662
+ fnResult = await fn(ctx, tokenScope.registration, stamp);
623
663
  } finally {
624
664
  tokenScope.close();
625
665
  }
@@ -631,24 +671,22 @@ async function withCheckedCommit(deps, fn, onAttemptStart) {
631
671
  const registeredId = registered.aggregateId;
632
672
  const registeredType = registered.aggregateType;
633
673
  return record.events.map((event, index) => {
634
- if (!isStampedDomainEvent(event)) throw new EventCollectionError(`withCommit: event "${event.type}" has not been stamped. Call stampPendingEvents(aggregate, createStamp) in the application shell before persistence or outbox collection.`, event.type);
635
- const recordedEvent = event;
636
674
  const commitSize = record.events.length;
637
- const aggregateId = recordedEvent.aggregateId;
638
- const aggregateType = recordedEvent.aggregateType;
675
+ const aggregateId = event.aggregateId;
676
+ const aggregateType = event.aggregateType;
639
677
  const missing = [];
640
678
  if (!aggregateId) missing.push("aggregateId");
641
679
  if (!aggregateType) missing.push("aggregateType");
642
- if (!aggregateId || !aggregateType) throw new EventCollectionError(`withCommit: event "${recordedEvent.type}" is missing ${missing.join(" and ")}. Use this.createEvent(type, payload) inside aggregate methods instead of createDomainEvent(...); createEvent auto-injects aggregateId and aggregateType. Outbox dispatchers and projection handlers rely on the envelope source.`, recordedEvent.type);
643
- if (aggregateId !== registeredId || aggregateType !== registeredType) throw new EventCollectionError(`withCommit: event "${recordedEvent.type}" belongs to ${describeAggregateIdentity({
680
+ if (!aggregateId || !aggregateType) throw new EventCollectionError(`withCommit: event "${event.type}" is missing ${missing.join(" and ")}. Use this.createEvent(type, payload) inside aggregate methods instead of createDomainEvent(...); createEvent auto-injects aggregateId and aggregateType. Outbox dispatchers and projection handlers rely on the envelope source.`, event.type);
681
+ if (aggregateId !== registeredId || aggregateType !== registeredType) throw new EventCollectionError(`withCommit: event "${event.type}" belongs to ${describeAggregateIdentity({
644
682
  aggregateType,
645
683
  aggregateId
646
684
  })} but was registered under ${describeAggregateIdentity({
647
685
  aggregateType: registeredType,
648
686
  aggregateId: registeredId
649
- })}. The aggregate base classes attribute every event to the aggregate on every recording path; an instance from another package copy must stamp it the same way.`, recordedEvent.type);
687
+ })}. The aggregate base classes attribute every event to the aggregate on every recording path; an instance from another package copy must stamp it the same way.`, event.type);
650
688
  return Object.freeze({
651
- event: recordedEvent,
689
+ event,
652
690
  source: Object.freeze({
653
691
  aggregateId,
654
692
  aggregateType
@@ -1280,8 +1318,8 @@ var DeadlineProcessor = class extends PollLoop {
1280
1318
  assertTimerDelay("DeadlineProcessor", "storageTimeoutMs", this.storageTimeoutMs);
1281
1319
  }
1282
1320
  /**
1283
- * One full delivery pass (the `run`/`drainOnce` shell lives on
1284
- * {@link PollLoop}): delivers due deadlines batch by batch until
1321
+ * One full delivery pass (the `run`/`drainOnce` shell lives on the
1322
+ * shared poll loop): delivers due deadlines batch by batch until
1285
1323
  * nothing is due or a cycle contained a failure.
1286
1324
  */
1287
1325
  async pass(signal) {
@@ -2599,29 +2637,6 @@ function wiringErrorInCauseChain(error) {
2599
2637
  return findInCauseChain(error, (link) => isWiringErrorLike(link) ? link : void 0);
2600
2638
  }
2601
2639
 
2602
- //#endregion
2603
- //#region src/domain/aggregate/pending-event-recording.ts
2604
- const { registry: capabilities$1, require } = createGlobalCapabilityRegistry(Symbol.for("@shirudo/ddd-kit/pending-event-recording-registry/v2"));
2605
- /** Resolves the recording capability or throws `UnmanagedInstanceError`. */
2606
- function requirePendingEventRecordingCapability(aggregate, operation) {
2607
- return require(aggregate, operation, "aggregate");
2608
- }
2609
- function registerPendingEventRecordingCapability(aggregate, capability) {
2610
- capabilities$1.set(aggregate, Object.freeze(capability));
2611
- }
2612
-
2613
- //#endregion
2614
- //#region src/application/unit-of-work/stamp-pending-events.ts
2615
- function stampPendingEvents(aggregate, source, stampOptions) {
2616
- const capability = requirePendingEventRecordingCapability(aggregate, "stampPendingEvents");
2617
- const shared = {
2618
- occurredAt: stampOptions?.occurredAt,
2619
- metadata: stampOptions?.metadata
2620
- };
2621
- const createStamp = typeof source === "function" ? source : () => source.createStamp(shared);
2622
- return capability.record((event, index) => createStamp(event, index));
2623
- }
2624
-
2625
2640
  //#endregion
2626
2641
  //#region src/application/unit-of-work/repository-facade.ts
2627
2642
  /**
@@ -3078,6 +3093,7 @@ function capabilityFor(baseline, operation) {
3078
3093
  */
3079
3094
  var Session = class {
3080
3095
  commitRegistration;
3096
+ stamp;
3081
3097
  _registeredWrites = [];
3082
3098
  _commitTokens = /* @__PURE__ */ new Set();
3083
3099
  _identityMap = new IdentityMap();
@@ -3100,8 +3116,9 @@ var Session = class {
3100
3116
  */
3101
3117
  _lateRegistrationsFailAttempt = false;
3102
3118
  _registrationDuringFlush;
3103
- constructor(commitRegistration) {
3119
+ constructor(commitRegistration, stamp) {
3104
3120
  this.commitRegistration = commitRegistration;
3121
+ this.stamp = stamp;
3105
3122
  }
3106
3123
  get identityMap() {
3107
3124
  this.assertOpen("tracking.identityMap");
@@ -3224,6 +3241,7 @@ var Session = class {
3224
3241
  }
3225
3242
  /** Registers persistence intent and commit registration as one operation. */
3226
3243
  registerWrite(entry, intent, definition) {
3244
+ if (entry.registration === void 0) this.stamp(entry.aggregate, `repository.${intent}`);
3227
3245
  const newlyRegistered = this.registerIntent(entry, intent);
3228
3246
  try {
3229
3247
  if (intent === "remove") this.registerRemovedCommit(entry.aggregate, definition, entry.expectedVersion);
@@ -3622,10 +3640,12 @@ var UnitOfWork = class {
3622
3640
  onPersisted: this.deps.onPersisted,
3623
3641
  onPersistError: this.deps.onPersistError,
3624
3642
  postCommitTimeoutMs: this.deps.postCommitTimeoutMs,
3625
- signal: options?.signal
3626
- }, async (tx, registration) => {
3643
+ signal: options?.signal,
3644
+ eventStampFactory: this.deps.eventStampFactory,
3645
+ stampOptions: options?.stampOptions
3646
+ }, async (tx, registration, stamp) => {
3627
3647
  session?.close();
3628
- const s = new Session(registration);
3648
+ const s = new Session(registration, stamp);
3629
3649
  session = s;
3630
3650
  sessions.push(s);
3631
3651
  const current = startAttempt();
@@ -4449,8 +4469,8 @@ function entityIds(entities) {
4449
4469
  * tracking, the kit-internal post-commit acknowledgement capability,
4450
4470
  * the `markReconstituted` post-load marker, and the `createEvent` helper
4451
4471
  * that auto-injects `aggregateId` + `aggregateType` on every event the
4452
- * aggregate emits. The application shell stamps the pending decisions
4453
- * with `stampPendingEvents` before persistence.
4472
+ * aggregate emits. The commit stamps the pending decisions when it
4473
+ * registers the aggregate, before persistence.
4454
4474
  *
4455
4475
  * Consumers do NOT extend this class directly; extend
4456
4476
  * `StateStoredAggregate` for state-stored aggregates or
@@ -4768,8 +4788,8 @@ var BaseAggregate = class extends Entity {
4768
4788
  * Creates the immutable business fact accepted by this aggregate without
4769
4789
  * reading a clock, generating an id, or attaching tracing metadata.
4770
4790
  *
4771
- * The application shell stamps pending events after the domain operation
4772
- * and before persistence. Payload schema version stays here, next to the
4791
+ * The commit stamps pending events when it registers the aggregate,
4792
+ * before persistence. Payload schema version stays here, next to the
4773
4793
  * concrete event producer, rather than in shell-owned stamp data.
4774
4794
  */
4775
4795
  createEvent(type, payload, options) {
@@ -5232,7 +5252,8 @@ var NotSpecification = class extends Specification {
5232
5252
  //#region src/domain/value-object/value-object-or-issues.ts
5233
5253
  /**
5234
5254
  * Builds an immutable value object while collecting **all** validation
5235
- * violations into a single {@link ValidationError}, instead of failing on the
5255
+ * violations into a single `ValidationError` of `@shirudo/base-error`,
5256
+ * instead of failing on the
5236
5257
  * first one. This is the Result-first, multi-error counterpart to
5237
5258
  * `valueObjectOrError` (which returns a single string message).
5238
5259
  *
@@ -6663,8 +6684,8 @@ var OutboxDispatcher = class extends PollLoop {
6663
6684
  assertTimerDelay("OutboxDispatcher", "storageTimeoutMs", this.storageTimeoutMs);
6664
6685
  }
6665
6686
  /**
6666
- * One full dispatch pass (the `run`/`drainOnce` shell lives on
6667
- * {@link PollLoop}): dispatches pending records batch by batch until
6687
+ * One full dispatch pass (the `run`/`drainOnce` shell lives on the
6688
+ * shared poll loop): dispatches pending records batch by batch until
6668
6689
  * the backlog is empty or a failure stops progress. A `"stopped"`
6669
6690
  * pass leaves the failed record pending (or dead-lettered by a
6670
6691
  * tracking outbox); the next cycle retries it.
@@ -6765,7 +6786,10 @@ var OutboxDispatcher = class extends PollLoop {
6765
6786
  function copyForStorage(event, index, stream) {
6766
6787
  const subject = `InMemoryEventStore.append (stream ${describeAggregateIdentity(stream)}): events[${index}]`;
6767
6788
  if (event === null || typeof event !== "object") throw new TypeError(`${subject} is not an event object`);
6768
- return copyFreezableState(event, subject);
6789
+ const copy = copyFreezableState(event, subject);
6790
+ const eventId = copy.eventId;
6791
+ if (typeof eventId !== "string" || eventId === "") throw new TypeError(`${subject} has no eventId. Stamp the event before the append: the commit stamps it when a write registers its aggregate, and stampPendingEvents stamps it before that.`);
6792
+ return copy;
6769
6793
  }
6770
6794
  function assertStreamPosition(name, value) {
6771
6795
  if (value !== void 0 && (!Number.isSafeInteger(value) || value < 0)) throw new RangeError(`InMemoryEventStore: ${name} must be a non-negative safe integer, got ${String(value)}`);