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

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.js CHANGED
@@ -1,5 +1,6 @@
1
- import { $ as isDomainErrorLike, A as MissingHandlerError, B as SnapshotCorruptedError, C as InvalidEventStreamPageError, D as MisattributedEventError, E as KitWiringError, F as ProjectionOrderViolationError, G as UnmintedEventError, H as SnapshotVersionNotRestoredError, I as ProjectionReceiptViolationError, J as UnreplayableAggregateError, K as UnprojectableEventError, L as ReentrantEventRecordingError, M as PendingEventLimitExceededError, N as ProjectionGapError, O as MissingEntityIdError, P as ProjectionIdentityViolationError, Q as detachAggregateIdentity, R as ReplayRejectedError, S as InvalidCommandMessageError, T as InvalidVersionError, U as UnenrolledChangesError, V as SnapshotSchemaMismatchError, W as UnmanagedInstanceError, X as attributeConflictIntent, Y as assertNoHostileOwnProtoKey, Z as describeAggregateIdentity, _ as IdempotencyInFlightError, a as DirectStateMutationError, b as InMemoryCapacityExceededError, c as DuplicateEventIdError, d as EventHarvestError, et as isInfrastructureErrorLike, f as FoldReturnedNoStateError, g as IdempotencyCompletionWithoutClaimError, h as IdempotencyClaimLostError, i as ConcurrencyConflictError, j as PendingEventBatchMismatchError, k as MissingFoldError, l as DuplicateHandlerRegistrationError, m as HostileStateKeyError, n as AggregateNotFoundError, nt as rewriteErrorMessage, o as DomainError, p as ForeignEventError, q as UnregisteredHandlerError, r as CapabilityRegistryConflictError, s as DuplicateAggregateError, t as AggregateDeletedError, tt as isWiringErrorLike, u as ErrorMapperFailedError, v as IdempotencyKeyReuseError, w as InvalidIntegrationMessageError, x as InfrastructureError, y as IdempotencyReconciliationRequiredError, z as ReplayTargetMismatchError } from "./chunks/kit-errors.js";
2
- import { A as SnapshotTimeValidationError, B as voWithValidation, C as defaultDomainEventFactory, D as mintUncommittedDomainEvent, E as mergeMetadata, F as ValueObject, G as isBuiltInObject, H as deepOmit, I as deepFreeze, K as isIntrinsicConstructorPrototype, L as vo, M as detachState, N as hasCooperativeBrand, O as recordDomainEvent, P as stampCooperativeBrand, R as voEquals, S as createUncommittedDomainEvent, T as isUncommittedDomainEvent, U as deepEqual, V as deepEqualExcept, W as findPropertyDescriptor, _ as adoptUncommittedDomainEvent, a as isDispatchTrackingOutbox, b as createDomainEventFactory, c as runBoundedExecution, d as assertPositiveInteger, f as assertPositiveSafeInteger, g as adoptRecordedDomainEvent, h as waitRejectingOnAbort, i as assertPageWithinWindow, j as detachFreezableState, k as DomainEventValidationError, l as MAX_TIMER_DELAY_MS, m as abortReason, n as readStreamPages, o as DEFAULT_EXECUTION_TIMEOUT_MS, p as assertTimerDelay, q as isWeakMap, r as assertPageNotEmpty, s as ownerSignalOf, t as pinTargetVersion, u as assertNonNegativeSafeInteger, v as copyMetadata, w as isRecordedDomainEvent, x as createDomainEventFromFacts, y as createDomainEvent, z as voEqualsExcept } from "./chunks/read-stream-pages.js";
1
+ import { $ as isDomainErrorLike, A as MissingHandlerError, B as ReplayTargetMismatchError, C as InvalidEventStreamPageError, D as MisattributedEventError, E as KitWiringError, F as ProjectionOrderViolationError, G as UnprojectableEventError, H as SnapshotSchemaMismatchError, I as ProjectionReceiptViolationError, J as UnreplayableAggregateError, K as UnregisteredChangesError, L as PrototypePollutionError, M as PendingEventLimitExceededError, N as ProjectionGapError, O as MissingEntityIdError, P as ProjectionIdentityViolationError, Q as describeAggregateIdentity, R as ReentrantEventRecordingError, S as InvalidCommandMessageError, T as InvalidVersionError, U as SnapshotVersionNotRestoredError, V as SnapshotCorruptedError, W as UnmanagedInstanceError, X as attributeConflictIntent, Y as assertNoOwnProtoKey, Z as copyAggregateIdentity, _ as IdempotencyInFlightError, a as DirectStateMutationError, b as InMemoryCapacityExceededError, c as DuplicateEventIdError, d as EventCollectionError, et as isInfrastructureErrorLike, f as FoldReturnedNoStateError, g as IdempotencyCompletionWithoutClaimError, h as IdempotencyClaimLostError, i as ConcurrencyConflictError, j as PendingEventBatchMismatchError, k as MissingFoldError, l as DuplicateHandlerRegistrationError, m as HandBuiltEventError, n as AggregateNotFoundError, nt as rewriteErrorMessage, o as DomainError, p as ForeignEventError, q as UnregisteredHandlerError, r as CapabilityRegistryConflictError, s as DuplicateAggregateError, t as AggregateDeletedError, tt as isWiringErrorLike, u as ErrorMapperFailedError, v as IdempotencyKeyReuseError, w as InvalidIntegrationMessageError, x as InfrastructureError, y as IdempotencyReconciliationRequiredError, z as ReplayRejectedError } from "./chunks/kit-errors.js";
2
+ import { a as valueObjectEqualsExcept, c as applyCooperativeBrand, d as deepEqual, i as valueObjectEquals, l as hasCooperativeBrand, n as deepFreeze, o as valueObjectOrError, r as valueObject, t as ValueObject, u as deepOmit, v as isWeakMap } from "./chunks/value-object.js";
3
+ import { A as copyFreezableState, C as defaultDomainEventFactory, D as stampDomainEvent, E as mergeMetadata, O as DomainEventValidationError, S as createStampedDomainEvent, T as isUnstampedDomainEvent, _ as adoptUnstampedDomainEvent, a as isDispatchTrackingOutbox, b as createDomainEvent, c as runBoundedExecution, d as assertPositiveInteger, f as assertPositiveSafeInteger, g as adoptStampedDomainEvent, h as waitRejectingOnAbort, i as assertPageWithinWindow, j as copyPlainState, k as SnapshotTimeValidationError, l as MAX_TIMER_DELAY_MS, m as abortReason, n as readStreamPages, o as DEFAULT_EXECUTION_TIMEOUT_MS, p as assertTimerDelay, r as assertPageNotEmpty, s as ownerSignalOf, t as pinTargetVersion, u as assertNonNegativeSafeInteger, v as buildUnstampedDomainEvent, w as isStampedDomainEvent, x as createDomainEventFactory, y as copyMetadata } from "./chunks/read-stream-pages.js";
3
4
  import { err, ok } from "@shirudo/result";
4
5
  import { ValidationError, someChainRetryable } from "@shirudo/base-error";
5
6
 
@@ -407,11 +408,11 @@ function captureObserverFunctions(context, observers, required) {
407
408
  //#region src/application/cqrs/handler.ts
408
409
  /**
409
410
  * True when the aggregate's live version or pending-event batch no longer
410
- * matches its enrollment-time snapshot. Shared by the duplicate-enrollment
411
- * gate and the harvest-time recheck: both must reject the same divergence,
412
- * or events recorded after enrollment would be silently dropped.
411
+ * matches its registration-time snapshot. Shared by the duplicate-registration
412
+ * gate and the collection-time recheck: both must reject the same divergence,
413
+ * or events recorded after registration would be silently dropped.
413
414
  */
414
- function enrollmentDiverged(record) {
415
+ function registrationDiverged(record) {
415
416
  const pending = record.aggregate.pendingEvents;
416
417
  return record.aggregate.version !== record.version || pending.length !== record.events.length || record.events.some((event, index) => event !== pending[index]);
417
418
  }
@@ -419,24 +420,24 @@ function enrollmentDiverged(record) {
419
420
  function createCommitTokenScope() {
420
421
  const recordsByToken = /* @__PURE__ */ new WeakMap();
421
422
  const tokensByAggregate = /* @__PURE__ */ new WeakMap();
422
- let mintedTokenCount = 0;
423
+ let issuedTokenCount = 0;
423
424
  let open = true;
424
- const enroll = (aggregate, disposition, options) => {
425
- if (!open) throw new EventHarvestError("withCommit: commit enrollment was used after its work callback settled. Await every repository write and return its token before leaving the callback.");
425
+ const register = (aggregate, disposition, options) => {
426
+ if (!open) throw new EventCollectionError("withCommit: commit registration was used after its work callback settled. Await every repository write and return its token before leaving the callback.");
426
427
  const existing = tokensByAggregate.get(aggregate);
427
428
  if (existing) {
428
429
  const record = recordsByToken.get(existing);
429
- if (!record) throw new EventHarvestError("withCommit: internal commit-token registry is inconsistent.");
430
- if (record.disposition === "deleted" && disposition === "saved") throw new EventHarvestError(`withCommit: aggregate ${describeAggregateIdentity(aggregate.aggregateIdentity)} was enrolled as saved after it was enrolled as deleted in the same transaction.`);
431
- if (options?.expectedVersion !== void 0 && options.expectedVersion !== record.expectedVersion) throw new EventHarvestError(`withCommit: aggregate ${describeAggregateIdentity(aggregate.aggregateIdentity)} was re-enrolled with expectedVersion ${String(options.expectedVersion)}, but its enrollment recorded ${String(record.expectedVersion)}. Duplicate enrollment must assert the same OCC baseline or none.`);
432
- if (enrollmentDiverged(record)) throw new EventHarvestError(`withCommit: aggregate ${describeAggregateIdentity(aggregate.aggregateIdentity)} changed after its commit batch was enrolled. Register persistence intent last.`);
430
+ if (!record) throw new EventCollectionError("withCommit: internal commit-token registry is inconsistent.");
431
+ if (record.disposition === "deleted" && disposition === "saved") throw new EventCollectionError(`withCommit: aggregate ${describeAggregateIdentity(aggregate.aggregateIdentity)} was registered as saved after it was registered as deleted in the same transaction.`);
432
+ if (options?.expectedVersion !== void 0 && options.expectedVersion !== record.expectedVersion) throw new EventCollectionError(`withCommit: aggregate ${describeAggregateIdentity(aggregate.aggregateIdentity)} was re-registered with expectedVersion ${String(options.expectedVersion)}, but its registration recorded ${String(record.expectedVersion)}. Duplicate registration must assert the same OCC baseline or none.`);
433
+ if (registrationDiverged(record)) throw new EventCollectionError(`withCommit: aggregate ${describeAggregateIdentity(aggregate.aggregateIdentity)} changed after its commit batch was registered. Register persistence intent last.`);
433
434
  if (disposition === "deleted") record.disposition = "deleted";
434
435
  return existing;
435
436
  }
436
- const eventLifecycle = requirePendingEventLifecycleCapability(aggregate, "withCommit enrollment");
437
+ const eventLifecycle = requirePendingEventLifecycleCapability(aggregate, "withCommit registration");
437
438
  const token = Object.freeze(Object.create(null));
438
439
  const events = aggregate.pendingEvents;
439
- for (const event of events) if (!isRecordedDomainEvent(event)) throw new EventHarvestError(`withCommit: event "${event.type}" has not been recorded. Call recordPendingEvents(aggregate, createStamp) in the application shell before persistence or outbox harvest.`, event.type);
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);
440
441
  const persistedVersion = eventLifecycle.persistedVersion();
441
442
  tokensByAggregate.set(aggregate, token);
442
443
  recordsByToken.set(token, {
@@ -448,32 +449,32 @@ function createCommitTokenScope() {
448
449
  persistedVersion,
449
450
  events
450
451
  });
451
- mintedTokenCount += 1;
452
+ issuedTokenCount += 1;
452
453
  return token;
453
454
  };
454
455
  return {
455
- enrollment: Object.freeze({
456
- enrollSaved: (aggregate, options) => enroll(aggregate, "saved", options),
457
- enrollDeleted: (aggregate, options) => enroll(aggregate, "deleted", options)
456
+ registration: Object.freeze({
457
+ registerSaved: (aggregate, options) => register(aggregate, "saved", options),
458
+ registerRemoved: (aggregate, options) => register(aggregate, "deleted", options)
458
459
  }),
459
460
  close: () => {
460
461
  open = false;
461
462
  },
462
463
  resolve: (tokens) => {
463
- if (!Array.isArray(tokens)) throw new EventHarvestError("withCommit: the work callback must return `commits` containing tokens from the current enrollment capability. Naked aggregate arrays are not commit evidence.");
464
+ if (!Array.isArray(tokens)) throw new EventCollectionError("withCommit: the work callback must return `commits` containing tokens from the current registration capability. Naked aggregate arrays are not commit evidence.");
464
465
  const seen = /* @__PURE__ */ new Set();
465
466
  const records = [];
466
467
  for (const token of tokens) {
467
- if (token === null || typeof token !== "object" && typeof token !== "function") throw new EventHarvestError("withCommit: a commit token was not minted by this callback's enrollment capability. Forged and stale tokens are rejected.");
468
+ if (token === null || typeof token !== "object" && typeof token !== "function") throw new EventCollectionError("withCommit: a commit token was not issued by this callback's registration capability. Forged and stale tokens are rejected.");
468
469
  const tokenObject = token;
469
470
  const record = recordsByToken.get(tokenObject);
470
- if (!record) throw new EventHarvestError("withCommit: a commit token was not minted by this callback's enrollment capability. Forged and stale tokens are rejected.");
471
+ if (!record) throw new EventCollectionError("withCommit: a commit token was not issued by this callback's registration capability. Forged and stale tokens are rejected.");
471
472
  if (seen.has(tokenObject)) continue;
472
473
  seen.add(tokenObject);
473
- if (enrollmentDiverged(record)) throw new EventHarvestError(`withCommit: aggregate ${describeAggregateIdentity(record.aggregate.aggregateIdentity)} changed after its commit batch was enrolled; events recorded after enrollment are not part of the attested write and would be silently dropped. Make domain decisions first, write, and enroll last.`);
474
+ if (registrationDiverged(record)) throw new EventCollectionError(`withCommit: aggregate ${describeAggregateIdentity(record.aggregate.aggregateIdentity)} changed after its commit batch was registered; events recorded after registration are not part of the attested write and would be silently dropped. Make domain decisions first, write, and register last.`);
474
475
  records.push(record);
475
476
  }
476
- if (seen.size !== mintedTokenCount) throw new EventHarvestError("withCommit: every token minted by the current enrollment capability must be returned in `commits`. If an enrolled write must not commit, throw so the transaction rolls back.");
477
+ if (seen.size !== issuedTokenCount) throw new EventCollectionError("withCommit: every token issued by the current registration capability must be returned in `commits`. If a registered write must not commit, throw so the transaction rolls back.");
477
478
  return records;
478
479
  }
479
480
  };
@@ -481,29 +482,29 @@ function createCommitTokenScope() {
481
482
  /**
482
483
  * Helper for executing a write Use Case inside a transaction scope.
483
484
  *
484
- * The use-case callback receives an invocation-scoped enrollment capability
485
+ * The use-case callback receives an invocation-scoped registration capability
485
486
  * and returns opaque commit tokens for the repository writes that completed
486
- * in the transaction. `withCommit` owns the post-commit lifecycle (harvest,
487
+ * in the transaction. `withCommit` owns the post-commit lifecycle (collection,
487
488
  * outbox, mark-persisted, publish). A naked aggregate is not commit evidence:
488
489
  * merely touching or constructing one must never make it look persisted.
489
490
  *
490
- * **Trust boundary.** A token proves invocation-local enrollment, not that the
491
+ * **Trust boundary.** A token proves invocation-local registration, not that the
491
492
  * kit inspected a database write; a generic transaction helper cannot observe
492
- * adapter internals. Repository code must enroll only writes participating in
493
+ * adapter internals. Repository code must register only writes participating in
493
494
  * this transaction. `UnitOfWork` centralizes that rule in repository methods.
494
495
  * The opaque, scoped token prevents accidental aggregate smuggling and stale
495
496
  * reuse; it is not a security boundary against code that deliberately lies to
496
497
  * its own persistence capability.
497
498
  *
498
499
  * Order of operations:
499
- * 1. `fn(ctx, enrollment)` runs inside `scope.transactional(...)`; domain
500
+ * 1. `fn(ctx, registration)` runs inside `scope.transactional(...)`; domain
500
501
  * mutations + repo writes happen here. After a repository write has
501
- * enrolled an aggregate, the callback includes that opaque token in its
502
+ * registered an aggregate, the callback includes that opaque token in its
502
503
  * `commits` result. Tokens are invocation-bound: forged or stale tokens
503
- * fail before harvest. `ctx` is whatever transaction handle the `scope`
504
+ * fail before collection. `ctx` is whatever transaction handle the `scope`
504
505
  * exposes (Drizzle `tx`, Prisma `tx`, Mongo session, or `undefined` for
505
506
  * context-free scopes).
506
- * 2. **Still inside the transaction**, `withCommit` harvests every
507
+ * 2. **Still inside the transaction**, `withCommit` collects every
507
508
  * aggregate's `pendingEvents` and writes them via `outbox.add` (so
508
509
  * events persist atomically with the state change). Skipped when no
509
510
  * events were recorded. Each bare domain event is composed into an
@@ -513,7 +514,7 @@ function createCommitTokenScope() {
513
514
  * resulting `CommittedDomainEvent`. The domain event itself is never
514
515
  * stamped or copied.
515
516
  *
516
- * **Harvest order.** Events are concatenated in the order
517
+ * **Collection order.** Events are concatenated in the order
517
518
  * tokens appear in the returned `commits` array, then in
518
519
  * each aggregate's `pendingEvents` order (insertion order via
519
520
  * `apply` / `commit` / `addDomainEvent`). So tokens for `[a, b]`
@@ -537,7 +538,7 @@ function createCommitTokenScope() {
537
538
  * across aggregates at delivery time.
538
539
  * 3. The transaction commits.
539
540
  * 4. **After** the commit, a non-exported capability acknowledges every
540
- * saved enrollment and discards pending events for deleted enrollments.
541
+ * saved registration and discards pending events for deleted registrations.
541
542
  * Only after the complete commit set is clean does the optional
542
543
  * application-shell `onPersisted(aggregate, version, context)` observer run for
543
544
  * saved aggregates. Deleted rows never trigger that observer.
@@ -567,16 +568,16 @@ function createCommitTokenScope() {
567
568
  * If the transaction rolls back, no acknowledgement occurs: the aggregate
568
569
  * keeps its pending events, so the caller can retry or discard the instance.
569
570
  *
570
- * Enrollment captures an exact version and event batch. Re-enrolling the same
571
+ * Registration captures an exact version and event batch. Re-registering the same
571
572
  * aggregate after it changes rejects. `UnitOfWork` additionally seals the
572
- * adapter persistence projection and rejects later mutation before flush. For
573
- * direct `withCommit` use, make domain decisions first, write, and enroll last.
573
+ * adapter persistence capture and rejects later mutation before flush. For
574
+ * direct `withCommit` use, make domain decisions first, write, and register last.
574
575
  *
575
- * **Duplicate enrollment is idempotent by reference.** Enrolling the same
576
+ * **Duplicate registration is idempotent by reference.** Registering the same
576
577
  * instance repeatedly returns the same token, and a repeated token in
577
- * `commits` is harvested once. A repeat call that omits `expectedVersion`
578
+ * `commits` is collected once. A repeat call that omits `expectedVersion`
578
579
  * makes no OCC assertion; only a supplied value that contradicts the
579
- * enrollment-time baseline rejects. Each event lands in the outbox exactly once
580
+ * registration-time baseline rejects. Each event lands in the outbox exactly once
580
581
  * and post-commit acknowledgement runs exactly once. Two
581
582
  * *different* instances with the same logical id cannot be detected
582
583
  * at this layer; that is a Repository contract violation (failure to
@@ -587,19 +588,19 @@ function createCommitTokenScope() {
587
588
  *
588
589
  * @example Tx-bound repos (Drizzle, Prisma, Mongo, …)
589
590
  * ```typescript
590
- * const result = await withCommit({ outbox, bus, scope }, async (tx, enrollment) => {
591
+ * const result = await withCommit({ outbox, bus, scope }, async (tx, registration) => {
591
592
  * const orderRepository = makeOrderRepository(tx); // your factory binds tx to the repo
592
593
  * const order = await orderRepository.getById(orderId);
593
594
  * order.confirm();
594
595
  * await persistOrder(tx, order); // low-level adapter write
595
- * const commit = enrollment.enrollSaved(order); // attest the repository write
596
+ * const commit = registration.registerSaved(order); // attest the repository write
596
597
  * return { result: order.id, commits: [commit] };
597
598
  * });
598
599
  * ```
599
600
  */
600
601
  async function withCommit(deps, fn) {
601
- return withCheckedCommit(deps, async (ctx, enrollment) => {
602
- const { result, commits } = await fn(ctx, enrollment);
602
+ return withCheckedCommit(deps, async (ctx, registration) => {
603
+ const { result, commits } = await fn(ctx, registration);
603
604
  return {
604
605
  result,
605
606
  commits
@@ -618,19 +619,19 @@ async function withCheckedCommit(deps, fn, onAttemptStart) {
618
619
  const tokenScope = createCommitTokenScope();
619
620
  let fnResult;
620
621
  try {
621
- fnResult = await fn(ctx, tokenScope.enrollment);
622
+ fnResult = await fn(ctx, tokenScope.registration);
622
623
  } finally {
623
624
  tokenScope.close();
624
625
  }
625
626
  try {
626
627
  const commitRecords = tokenScope.resolve(fnResult.commits);
627
628
  const candidates = commitRecords.flatMap((record) => {
628
- const enrolled = record.aggregate.aggregateIdentity;
629
- if (record.events.length > 0 && record.persistedVersion !== void 0 && record.version <= record.persistedVersion) throw new EventHarvestError(`withCommit: aggregate ${describeAggregateIdentity(enrolled)} recorded events but did not advance its version beyond the persisted version (${String(record.persistedVersion)}). An eventful commit needs a unique cursor; use StateStoredAggregate.setState(currentState, event) instead of addDomainEvent(event) alone.`);
630
- const enrolledId = enrolled.aggregateId;
631
- const enrolledType = enrolled.aggregateType;
629
+ const registered = record.aggregate.aggregateIdentity;
630
+ if (record.events.length > 0 && record.persistedVersion !== void 0 && record.version <= record.persistedVersion) throw new EventCollectionError(`withCommit: aggregate ${describeAggregateIdentity(registered)} recorded events but did not advance its version beyond the persisted version (${String(record.persistedVersion)}). An eventful commit needs a unique cursor; use StateStoredAggregate.setState(currentState, event) instead of addDomainEvent(event) alone.`);
631
+ const registeredId = registered.aggregateId;
632
+ const registeredType = registered.aggregateType;
632
633
  return record.events.map((event, index) => {
633
- if (!isRecordedDomainEvent(event)) throw new EventHarvestError(`withCommit: event "${event.type}" has not been recorded. Call recordPendingEvents(aggregate, createStamp) in the application shell before persistence or outbox harvest.`, event.type);
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);
634
635
  const recordedEvent = event;
635
636
  const commitSize = record.events.length;
636
637
  const aggregateId = recordedEvent.aggregateId;
@@ -638,14 +639,14 @@ async function withCheckedCommit(deps, fn, onAttemptStart) {
638
639
  const missing = [];
639
640
  if (!aggregateId) missing.push("aggregateId");
640
641
  if (!aggregateType) missing.push("aggregateType");
641
- if (!aggregateId || !aggregateType) throw new EventHarvestError(`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);
642
- if (aggregateId !== enrolledId || aggregateType !== enrolledType) throw new EventHarvestError(`withCommit: event "${recordedEvent.type}" belongs to ${describeAggregateIdentity({
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({
643
644
  aggregateType,
644
645
  aggregateId
645
- })} but was enrolled under ${describeAggregateIdentity({
646
- aggregateType: enrolledType,
647
- aggregateId: enrolledId
648
- })}. The aggregate base classes stamp the aggregate identity on every recording path; an instance from another package copy must stamp it the same way.`, recordedEvent.type);
646
+ })} but was registered under ${describeAggregateIdentity({
647
+ aggregateType: registeredType,
648
+ 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);
649
650
  return Object.freeze({
650
651
  event: recordedEvent,
651
652
  source: Object.freeze({
@@ -660,12 +661,12 @@ async function withCheckedCommit(deps, fn, onAttemptStart) {
660
661
  });
661
662
  });
662
663
  });
663
- const endedSources = commitRecords.filter((record) => record.disposition === "deleted").map((record) => detachAggregateIdentity(record.aggregate.aggregateIdentity));
664
+ const endedSources = commitRecords.filter((record) => record.disposition === "deleted").map((record) => copyAggregateIdentity(record.aggregate.aggregateIdentity));
664
665
  if (candidates.length > 0) await deps.outbox.add(Object.freeze(candidates));
665
666
  if (endedSources.length > 0) await deps.outbox.endEventSources(Object.freeze(endedSources));
666
667
  fnResult.checkBeforeCommit?.();
667
668
  if (candidates.length > 0 || endedSources.length > 0) {
668
- for (const record of commitRecords) if (enrollmentDiverged(record)) throw new EventHarvestError(`withCommit: aggregate ${describeAggregateIdentity(record.aggregate.aggregateIdentity)} changed while its outbox write ran. Await every change inside the work callback.`);
669
+ for (const record of commitRecords) if (registrationDiverged(record)) throw new EventCollectionError(`withCommit: aggregate ${describeAggregateIdentity(record.aggregate.aggregateIdentity)} changed while its outbox write ran. Await every change inside the work callback.`);
669
670
  }
670
671
  return {
671
672
  result: fnResult.result,
@@ -1406,7 +1407,7 @@ function positiveSafeInteger(value) {
1406
1407
  *
1407
1408
  * On its own, the store is the reference for the leased family: it cannot
1408
1409
  * see commits or rollbacks. Claims and staged outcomes carry bounded leases,
1409
- * while every mutation compares the store-minted token. An expired pending
1410
+ * while every mutation compares the store-issued token. An expired pending
1410
1411
  * claim may be replaced; an expired staged outcome cannot be guessed away and
1411
1412
  * instead returns `reconciliation-required`. Only an authoritative
1412
1413
  * `committed` / `not-committed` decision can settle it.
@@ -1669,20 +1670,20 @@ function startLeaseHeartbeat(store, claim) {
1669
1670
  failure: () => heartbeatFailure
1670
1671
  };
1671
1672
  }
1672
- function scopeWorkEnrollment(parent) {
1673
+ function scopeWorkRegistration(parent) {
1673
1674
  let open = true;
1674
1675
  const assertOpen = () => {
1675
- if (!open) throw new EventHarvestError("withIdempotentCommit: commit enrollment was used after the user work callback settled. Await every repository write before returning from the callback.");
1676
+ if (!open) throw new EventCollectionError("withIdempotentCommit: commit registration was used after the user work callback settled. Await every repository write before returning from the callback.");
1676
1677
  };
1677
1678
  return {
1678
- enrollment: Object.freeze({
1679
- enrollSaved: (aggregate) => {
1679
+ registration: Object.freeze({
1680
+ registerSaved: (...args) => {
1680
1681
  assertOpen();
1681
- return parent.enrollSaved(aggregate);
1682
+ return parent.registerSaved(...args);
1682
1683
  },
1683
- enrollDeleted: (aggregate) => {
1684
+ registerRemoved: (...args) => {
1684
1685
  assertOpen();
1685
- return parent.enrollDeleted(aggregate);
1686
+ return parent.registerRemoved(...args);
1686
1687
  }
1687
1688
  }),
1688
1689
  close: () => {
@@ -1705,12 +1706,12 @@ function scopeWorkEnrollment(parent) {
1705
1706
  * 2. A fresh claim carries an opaque ownership token. For a leased store the
1706
1707
  * wrapper renews it at `renewAfterMs` until the transaction callback is
1707
1708
  * ready to commit. A renewal failure rejects before commit and releases
1708
- * the claim. `fn(ctx, enrollment, execution)` receives the same token so a
1709
+ * the claim. `fn(ctx, registration, execution)` receives the same token so a
1709
1710
  * source-side marker can make later reconciliation conclusive.
1710
1711
  * 3. `store.complete(ctx, claim, fn's result)` stages or completes the outcome
1711
- * in the same transaction as aggregate writes and outbox. The enrollment
1712
+ * in the same transaction as aggregate writes and outbox. The registration
1712
1713
  * capability is sealed and its token array copied before `complete` can
1713
- * yield, so leaked callback state cannot change the harvest receipt.
1714
+ * yield, so leaked callback state cannot change the collection receipt.
1714
1715
  * 4. After commit, `store.confirm(claim)` finalizes a leased store's staged
1715
1716
  * outcome; it is a no-op for transactional stores. A failure cannot reject
1716
1717
  * an already committed write, so it is sent to `onIdempotencyError` and the
@@ -1798,7 +1799,7 @@ async function withIdempotentCommit(deps, request, fn) {
1798
1799
  const outcome = await withCommit({
1799
1800
  ...deps,
1800
1801
  scope
1801
- }, async (ctx, enrollment) => {
1802
+ }, async (ctx, registration) => {
1802
1803
  let claim = await store.claim(ctx, request.key, request.fingerprint);
1803
1804
  if (claim.status === "reconciliation-required") {
1804
1805
  const decision = deps.reconcileIdempotency ? await deps.reconcileIdempotency(claim.reconciliation, ctx) : "unknown";
@@ -1817,16 +1818,16 @@ async function withIdempotentCommit(deps, request, fn) {
1817
1818
  };
1818
1819
  attempt.claim = claim.claim;
1819
1820
  attempt.heartbeat = startLeaseHeartbeat(store, claim.claim);
1820
- const workEnrollment = scopeWorkEnrollment(enrollment);
1821
+ const workRegistration = scopeWorkRegistration(registration);
1821
1822
  let work;
1822
1823
  try {
1823
- work = await fn(ctx, workEnrollment.enrollment, {
1824
+ work = await fn(ctx, workRegistration.registration, {
1824
1825
  key: request.key,
1825
1826
  fingerprint: request.fingerprint,
1826
1827
  claimToken: claim.claim.token
1827
1828
  });
1828
1829
  } finally {
1829
- workEnrollment.close();
1830
+ workRegistration.close();
1830
1831
  }
1831
1832
  const result = work.result;
1832
1833
  const commits = Array.isArray(work.commits) ? Object.freeze([...work.commits]) : work.commits;
@@ -1841,7 +1842,7 @@ async function withIdempotentCommit(deps, request, fn) {
1841
1842
  });
1842
1843
  if (!outcome.replayed) {
1843
1844
  const committedClaim = attempt.claim;
1844
- if (!committedClaim) throw new EventHarvestError("withIdempotentCommit: a fresh result committed without its claim receipt.");
1845
+ if (!committedClaim) throw new EventCollectionError("withIdempotentCommit: a fresh result committed without its claim receipt.");
1845
1846
  try {
1846
1847
  await store.confirm(committedClaim);
1847
1848
  } catch (confirmError) {
@@ -2438,7 +2439,7 @@ var RepositoryErrorMappingFailedError = class extends KitWiringError {
2438
2439
  mapperCause;
2439
2440
  constructor(options) {
2440
2441
  super("REPOSITORY_ERROR_MAPPING_FAILED", `The repository error mapper failed for ${options.intent} of aggregate ${describeAggregateIdentity(options.identity)}. The original persistence failure is preserved as cause; the mapper failure is available as mapperCause.`, options.persistenceError);
2441
- this.identity = detachAggregateIdentity(options.identity);
2442
+ this.identity = copyAggregateIdentity(options.identity);
2442
2443
  this.intent = options.intent;
2443
2444
  this.mapperCause = options.mapperError;
2444
2445
  }
@@ -2463,7 +2464,7 @@ var InvalidFlushStatementError = class extends KitWiringError {
2463
2464
  classifierCause;
2464
2465
  constructor(options) {
2465
2466
  super("INVALID_FLUSH_STATEMENT", flushStatementReasonMessage(options), options.cause);
2466
- this.identity = detachAggregateIdentity(options.identity);
2467
+ this.identity = copyAggregateIdentity(options.identity);
2467
2468
  this.intent = options.intent ?? null;
2468
2469
  this.reason = options.reason;
2469
2470
  this.received = options.received;
@@ -2510,7 +2511,7 @@ var AggregateTrackingError = class extends KitWiringError {
2510
2511
  registeredIntent;
2511
2512
  constructor(options) {
2512
2513
  super("AGGREGATE_TRACKING", trackingReasonMessage(options));
2513
- this.identity = detachAggregateIdentity(options.identity);
2514
+ this.identity = copyAggregateIdentity(options.identity);
2514
2515
  this.operation = options.operation;
2515
2516
  this.reason = options.reason;
2516
2517
  this.registeredIntent = options.registeredIntent;
@@ -2543,10 +2544,10 @@ function trackingReasonMessage(options) {
2543
2544
  * A `CommitError` is the **potentially transient** post-completion
2544
2545
  * failure (a commit-time serialization failure is the classic case), so
2545
2546
  * it is the one a retrying caller should consider re-running. The
2546
- * deterministic post-completion failure, a harvest-guard violation (an
2547
+ * deterministic post-completion failure, a collection-guard violation (an
2547
2548
  * event missing `aggregateId` / `aggregateType`, or an eventful persisted
2548
2549
  * aggregate that did not advance its version), is a programming bug and surfaces as
2549
- * {@link EventHarvestError} instead, which does NOT extend
2550
+ * {@link EventCollectionError} instead, which does NOT extend
2550
2551
  * `InfrastructureError`, so it stays out of retry paths by construction.
2551
2552
  */
2552
2553
  var CommitError = class extends InfrastructureError {
@@ -2610,9 +2611,9 @@ function registerPendingEventRecordingCapability(aggregate, capability) {
2610
2611
  }
2611
2612
 
2612
2613
  //#endregion
2613
- //#region src/application/unit-of-work/record-pending-events.ts
2614
- function recordPendingEvents(aggregate, source, stampOptions) {
2615
- const capability = requirePendingEventRecordingCapability(aggregate, "recordPendingEvents");
2614
+ //#region src/application/unit-of-work/stamp-pending-events.ts
2615
+ function stampPendingEvents(aggregate, source, stampOptions) {
2616
+ const capability = requirePendingEventRecordingCapability(aggregate, "stampPendingEvents");
2616
2617
  const shared = {
2617
2618
  occurredAt: stampOptions?.occurredAt,
2618
2619
  metadata: stampOptions?.metadata
@@ -2836,7 +2837,7 @@ function deleteRepositoryFacadeProperty(state, target, property) {
2836
2837
  * `AggregatePersistence` implementations: two `findById(id)` calls in the same
2837
2838
  * unit of work MUST return the same instance, because commit-token
2838
2839
  * write registration dedupes by JavaScript object identity. Two instances for
2839
- * one logical aggregate can otherwise produce two tokens, two harvests,
2840
+ * one logical aggregate can otherwise produce two tokens, two collections,
2840
2841
  * and two post-commit lifecycle calls.
2841
2842
  *
2842
2843
  * Storage is two-level (per-type stores created lazily), so
@@ -2903,7 +2904,7 @@ var IdentityMap = class {
2903
2904
  * - Registering a DIFFERENT instance for an occupied type+id throws:
2904
2905
  * that is precisely the identity-map violation this class exists
2905
2906
  * to prevent (the repository hydrated twice instead of checking
2906
- * {@link get} first), and letting it pass would double-harvest
2907
+ * {@link get} first), and letting it pass would double-collect
2907
2908
  * events downstream.
2908
2909
  * - Registering a type+id that was {@link delete}d in this unit of
2909
2910
  * work throws `AggregateDeletedError`: deletion is final within
@@ -2918,7 +2919,7 @@ var IdentityMap = class {
2918
2919
  this._stores.set(type, store);
2919
2920
  }
2920
2921
  const existing = store.get(id);
2921
- if (existing !== void 0 && existing !== aggregate) throw new Error(`IdentityMap: a different instance is already registered for ${describeAggregateIdentity(aggregate.aggregateIdentity)}. Check get() before hydrating: two live instances of one aggregate break the one-instance-per-unit-of-work contract that exactly-once event harvest relies on.`);
2922
+ if (existing !== void 0 && existing !== aggregate) throw new Error(`IdentityMap: a different instance is already registered for ${describeAggregateIdentity(aggregate.aggregateIdentity)}. Check get() before hydrating: two live instances of one aggregate break the one-instance-per-unit-of-work contract that exactly-once event collection relies on.`);
2922
2923
  store.set(id, aggregate);
2923
2924
  if (aggregate !== null && typeof aggregate === "object" && !this._pendingAtRegistration.has(aggregate)) {
2924
2925
  const pending = pendingEventCountOf(aggregate);
@@ -2929,7 +2930,7 @@ var IdentityMap = class {
2929
2930
  * Registered instances that have recorded MORE pending events than they
2930
2931
  * carried when first registered (loaded). Used by the unit of work's
2931
2932
  * end-of-run guard: an aggregate that gained events after load but was
2932
- * never enrolled would silently drop them. A read-only load, or a
2933
+ * never registered would silently drop them. A read-only load, or a
2933
2934
  * reconstitution that already carried events, shows no increase and is
2934
2935
  * not reported.
2935
2936
  */
@@ -3008,7 +3009,7 @@ function insertPersistenceBaseline(model) {
3008
3009
  return createBaselineToken(model, void 0, "new");
3009
3010
  }
3010
3011
  /**
3011
- * Captures the aggregate's current adapter projection using the capability
3012
+ * Captures the aggregate's current adapter capture using the capability
3012
3013
  * carried by an existing baseline. Used to seal persistence-last registration.
3013
3014
  */
3014
3015
  function recapturePersistenceBaseline(baseline, aggregate) {
@@ -3020,7 +3021,7 @@ function recapturePersistenceBaseline(baseline, aggregate) {
3020
3021
  });
3021
3022
  }
3022
3023
  /**
3023
- * Recaptures the adapter projection and reports whether it drifted from the
3024
+ * Recaptures the adapter capture and reports whether it drifted from the
3024
3025
  * baseline's stored capture, using the model's `captureEquals` when supplied
3025
3026
  * and structural deep equality otherwise.
3026
3027
  *
@@ -3031,8 +3032,8 @@ function recapturePersistenceBaseline(baseline, aggregate) {
3031
3032
  * question independent of the model's diffing strategy. A `"new"` lifecycle
3032
3033
  * baseline has no stored capture and never reports drift.
3033
3034
  */
3034
- function persistenceProjectionDrifted(baseline, aggregate) {
3035
- const capability = capabilityFor(baseline, "persistenceProjectionDrifted");
3035
+ function persistenceCaptureDrifted(baseline, aggregate) {
3036
+ const capability = capabilityFor(baseline, "persistenceCaptureDrifted");
3036
3037
  if (capability.lifecycle === "new") return false;
3037
3038
  return !capability.captureEquals(capability.baseline, capability.capture(aggregate));
3038
3039
  }
@@ -3076,7 +3077,7 @@ function capabilityFor(baseline, operation) {
3076
3077
  * the public API.
3077
3078
  */
3078
3079
  var Session = class {
3079
- commitEnrollment;
3080
+ commitRegistration;
3080
3081
  _registeredWrites = [];
3081
3082
  _commitTokens = /* @__PURE__ */ new Set();
3082
3083
  _identityMap = new IdentityMap();
@@ -3092,15 +3093,15 @@ var Session = class {
3092
3093
  /**
3093
3094
  * True from the start of the flush until the last check before the
3094
3095
  * commit ran or the run ended. A write registered in that window would
3095
- * be committed and harvested without its flush, so it is rejected, and
3096
+ * be committed and collected without its flush, so it is rejected, and
3096
3097
  * the first rejection also fails the attempt, even when the caller
3097
3098
  * swallows it. After the window, a registration reports a closed unit of
3098
3099
  * work: nothing can fail the attempt any more.
3099
3100
  */
3100
3101
  _lateRegistrationsFailAttempt = false;
3101
3102
  _registrationDuringFlush;
3102
- constructor(commitEnrollment) {
3103
- this.commitEnrollment = commitEnrollment;
3103
+ constructor(commitRegistration) {
3104
+ this.commitRegistration = commitRegistration;
3104
3105
  }
3105
3106
  get identityMap() {
3106
3107
  this.assertOpen("tracking.identityMap");
@@ -3195,7 +3196,7 @@ var Session = class {
3195
3196
  throw error;
3196
3197
  }
3197
3198
  }
3198
- /** Two live instances of one identity would each flush and harvest. */
3199
+ /** Two live instances of one identity would each flush and collect. */
3199
3200
  assertIdentityNotTaken(aggregate, definition) {
3200
3201
  const tracked = this._identityMap.get(definition.aggregate, aggregate.id);
3201
3202
  if (tracked === void 0 || tracked === aggregate) return;
@@ -3221,7 +3222,7 @@ var Session = class {
3221
3222
  const loaded = this.loadedEntryFor(aggregate, "remove", definition);
3222
3223
  this.registerWrite(loaded, "remove", definition);
3223
3224
  }
3224
- /** Registers persistence intent and commit enrollment as one operation. */
3225
+ /** Registers persistence intent and commit registration as one operation. */
3225
3226
  registerWrite(entry, intent, definition) {
3226
3227
  const newlyRegistered = this.registerIntent(entry, intent);
3227
3228
  try {
@@ -3279,7 +3280,7 @@ var Session = class {
3279
3280
  this._registeredWrites.push(entry);
3280
3281
  return true;
3281
3282
  }
3282
- /** Restores the pre-registration state when commit enrollment rejects. */
3283
+ /** Restores the pre-registration state when commit registration rejects. */
3283
3284
  rollbackIntentRegistration(entry) {
3284
3285
  const index = this._registeredWrites.lastIndexOf(entry);
3285
3286
  if (index >= 0) this._registeredWrites.splice(index, 1);
@@ -3289,7 +3290,7 @@ var Session = class {
3289
3290
  const registration = entry.registration;
3290
3291
  if (registration === void 0) return;
3291
3292
  const currentEvents = entry.aggregate.pendingEvents;
3292
- const persistenceChanged = persistenceProjectionDrifted(registration.baseline, entry.aggregate);
3293
+ const persistenceChanged = persistenceCaptureDrifted(registration.baseline, entry.aggregate);
3293
3294
  const sameEvents = currentEvents.length === registration.events.length && currentEvents.every((event, index) => event === registration.events[index]);
3294
3295
  if (registration.version !== entry.aggregate.version || !sameEvents || persistenceChanged) throw new AggregateTrackingError({
3295
3296
  identity: entry.aggregate.aggregateIdentity,
@@ -3301,13 +3302,13 @@ var Session = class {
3301
3302
  registerSavedCommit(aggregate, definition, expectedVersion) {
3302
3303
  this.assertOpen("repository.add/update");
3303
3304
  if (this.isRemovedInstance(aggregate) || this._identityMap.isDeleted(definition.aggregate, aggregate.id)) throw new AggregateDeletedError({ identity: aggregate.aggregateIdentity });
3304
- const token = this.commitEnrollment.enrollSaved(aggregate, { expectedVersion });
3305
+ const token = this.commitRegistration.registerSaved(aggregate, { expectedVersion });
3305
3306
  this._commitTokens.add(token);
3306
3307
  return token;
3307
3308
  }
3308
3309
  registerRemovedCommit(aggregate, definition, expectedVersion) {
3309
3310
  this.assertOpen("repository.remove");
3310
- const token = this.commitEnrollment.enrollDeleted(aggregate, { expectedVersion });
3311
+ const token = this.commitRegistration.registerRemoved(aggregate, { expectedVersion });
3311
3312
  this._identityMap.delete(definition.aggregate, aggregate.id);
3312
3313
  this._commitTokens.add(token);
3313
3314
  return token;
@@ -3326,7 +3327,7 @@ var Session = class {
3326
3327
  this.assertUnchangedAfterRegistration(entry, "commit");
3327
3328
  continue;
3328
3329
  }
3329
- if (entry.lifecycle === "loaded" && (entry.aggregate.version !== entry.expectedVersion || persistenceProjectionDrifted(entry.baseline, entry.aggregate))) throw new UnenrolledChangesError({
3330
+ if (entry.lifecycle === "loaded" && (entry.aggregate.version !== entry.expectedVersion || persistenceCaptureDrifted(entry.baseline, entry.aggregate))) throw new UnregisteredChangesError({
3330
3331
  identity: entry.aggregate.aggregateIdentity,
3331
3332
  appendOnly: entry.definition.appendOnly === true
3332
3333
  });
@@ -3334,7 +3335,7 @@ var Session = class {
3334
3335
  for (const instance of this._identityMap.instancesWithNewPendingEvents()) {
3335
3336
  if (instance !== null && typeof instance === "object" && this.registrationOf(instance) !== void 0) continue;
3336
3337
  const aggregate = instance;
3337
- throw new UnenrolledChangesError({
3338
+ throw new UnregisteredChangesError({
3338
3339
  identity: aggregate.aggregateIdentity,
3339
3340
  appendOnly: this._trackingByAggregate.get(aggregate)?.definition.appendOnly === true
3340
3341
  });
@@ -3524,7 +3525,7 @@ function defineRepository() {
3524
3525
  const builder = (definition) => {
3525
3526
  const branded = { ...definition };
3526
3527
  assertRepositoryDefinitionMembers(branded);
3527
- stampCooperativeBrand(branded, repositoryDefinitionBrand);
3528
+ applyCooperativeBrand(branded, repositoryDefinitionBrand);
3528
3529
  return Object.freeze(branded);
3529
3530
  };
3530
3531
  return builder;
@@ -3535,7 +3536,7 @@ function defineRepository() {
3535
3536
  * transaction and either persist completely or not at all.
3536
3537
  *
3537
3538
  * Built ON TOP of `withCommit` - the commit orchestration (event
3538
- * harvest into the outbox inside the transaction, internal acknowledgement
3539
+ * collection into the outbox inside the transaction, internal acknowledgement
3539
3540
  * after the commit, best-effort in-process publish last) is inherited,
3540
3541
  * not reimplemented. What this layer adds:
3541
3542
  *
@@ -3603,7 +3604,7 @@ var UnitOfWork = class {
3603
3604
  * Execute one unit of work: open the transaction, hand the callback
3604
3605
  * tx-bound repositories, commit on resolve, roll back on throw,
3605
3606
  * run the post-commit lifecycle (acknowledge, observe, publish) for every
3606
- * enrolled aggregate. Returns the callback's result.
3607
+ * registered aggregate. Returns the callback's result.
3607
3608
  */
3608
3609
  async run(work, options) {
3609
3610
  if (options?.signal?.aborted) throw abortReason(options.signal, "UnitOfWork.run aborted before opening a transaction");
@@ -3622,9 +3623,9 @@ var UnitOfWork = class {
3622
3623
  onPersistError: this.deps.onPersistError,
3623
3624
  postCommitTimeoutMs: this.deps.postCommitTimeoutMs,
3624
3625
  signal: options?.signal
3625
- }, async (tx, enrollment) => {
3626
+ }, async (tx, registration) => {
3626
3627
  session?.close();
3627
- const s = new Session(enrollment);
3628
+ const s = new Session(registration);
3628
3629
  session = s;
3629
3630
  sessions.push(s);
3630
3631
  const current = startAttempt();
@@ -3712,7 +3713,7 @@ function startAttempt() {
3712
3713
  * {@link RollbackError}.
3713
3714
  * - `workCompleted`: the callback finished; the failure is post-completion.
3714
3715
  * The checks run in this order. First, a wiring error in the rejection (a
3715
- * harvest-guard violation, or an aggregate that changed after its
3716
+ * collection-guard violation, or an aggregate that changed after its
3716
3717
  * registration) is a deterministic programming bug and passes through; it
3717
3718
  * does NOT extend `InfrastructureError`, so a retry-on-Infrastructure
3718
3719
  * handler skips it. It is thrown inside `scope.transactional()`, so a
@@ -4097,7 +4098,7 @@ var Entity = class {
4097
4098
  * `TState` publicly would expose the aggregate's live object graph and
4098
4099
  * let nested mutation bypass behavior, validation, versioning, and
4099
4100
  * dirty tracking. Concrete entities should expose business-meaningful queries or
4100
- * detached immutable DTOs (`deepFreeze(detachState(this.state))` for a
4101
+ * immutable DTOs (`deepFreeze(copyPlainState(this.state))` for a
4101
4102
  * plain-data state). Snapshot projection belongs to the persistence
4102
4103
  * adapter, which captures the aggregate from outside through those
4103
4104
  * queries and DTOs rather than asking the entity to create its own
@@ -4130,7 +4131,7 @@ var Entity = class {
4130
4131
  * widens to the whole graph: NESTED objects are frozen in place too.
4131
4132
  *
4132
4133
  * @throws MissingEntityIdError when `id` is not a non-blank string.
4133
- * @throws HostileStateKeyError when a plain-object, null-prototype,
4134
+ * @throws PrototypePollutionError when a plain-object, null-prototype,
4134
4135
  * or array state carries an own `"__proto__"` data key; validate and
4135
4136
  * strip untrusted input at the boundary.
4136
4137
  */
@@ -4154,7 +4155,7 @@ var Entity = class {
4154
4155
  * ownership transfer and is frozen in place; see the constructor.
4155
4156
  *
4156
4157
  * @param newState - The new state
4157
- * @throws HostileStateKeyError when the state carries an own
4158
+ * @throws PrototypePollutionError when the state carries an own
4158
4159
  * `"__proto__"` data key; the previous state is kept.
4159
4160
  */
4160
4161
  setState(newState) {
@@ -4215,15 +4216,15 @@ function freezeShallow(value) {
4215
4216
  * @internal Shared by the kit's own entity subclasses; not part of the
4216
4217
  * public API.
4217
4218
  */
4218
- function assertStateHasNoHostileOwnKey(state, subject) {
4219
+ function assertStateHasNoOwnProtoKey(state, subject) {
4219
4220
  if (state === null || typeof state !== "object") return;
4220
4221
  if (Array.isArray(state)) {
4221
- assertNoHostileOwnProtoKey(state, subject);
4222
+ assertNoOwnProtoKey(state, subject);
4222
4223
  return;
4223
4224
  }
4224
4225
  const proto = Object.getPrototypeOf(state);
4225
4226
  if (proto !== Object.prototype && proto !== null) return;
4226
- assertNoHostileOwnProtoKey(state, subject);
4227
+ assertNoOwnProtoKey(state, subject);
4227
4228
  }
4228
4229
  /**
4229
4230
  * Returns a shallow copy for plain objects and arrays so the subsequent
@@ -4235,7 +4236,7 @@ function assertStateHasNoHostileOwnKey(state, subject) {
4235
4236
  */
4236
4237
  function shallowCopyOwned(value) {
4237
4238
  if (value === null || typeof value !== "object") return value;
4238
- assertStateHasNoHostileOwnKey(value, "Entity state");
4239
+ assertStateHasNoOwnProtoKey(value, "Entity state");
4239
4240
  if (Array.isArray(value)) {
4240
4241
  const copy = [...value];
4241
4242
  for (const key of Reflect.ownKeys(value)) {
@@ -4344,7 +4345,7 @@ function removeEntityById(entities, id) {
4344
4345
  /**
4345
4346
  * Updates an entity with the given ID in the collection.
4346
4347
  * Returns a new array with the updated entity.
4347
- * Structural sharing for adapter-owned persistence projections: returns
4348
+ * Structural sharing for adapter-owned persistence captures: returns
4348
4349
  * the ORIGINAL array when nothing changed (no match, or the element kept
4349
4350
  * its reference), so a partial-write adapter can skip the untouched
4350
4351
  * collection; a new array only when an
@@ -4383,7 +4384,7 @@ function updateEntityById(entities, id, updater) {
4383
4384
  /**
4384
4385
  * Replaces an entity with the given ID in the collection.
4385
4386
  * Returns a new array with the replaced entity.
4386
- * Structural sharing for adapter-owned persistence projections: returns
4387
+ * Structural sharing for adapter-owned persistence captures: returns
4387
4388
  * the ORIGINAL array when nothing changed (no match, or the element kept
4388
4389
  * its reference), so a partial-write adapter can skip the untouched
4389
4390
  * collection; a new array only when an
@@ -4448,8 +4449,8 @@ function entityIds(entities) {
4448
4449
  * tracking, the kit-internal post-commit acknowledgement capability,
4449
4450
  * the `markReconstituted` post-load marker, and the `createEvent` helper
4450
4451
  * that auto-injects `aggregateId` + `aggregateType` on every event the
4451
- * aggregate emits. The application shell records the pending decisions
4452
- * with `recordPendingEvents` before persistence.
4452
+ * aggregate emits. The application shell stamps the pending decisions
4453
+ * with `stampPendingEvents` before persistence.
4453
4454
  *
4454
4455
  * Consumers do NOT extend this class directly; extend
4455
4456
  * `StateStoredAggregate` for state-stored aggregates or
@@ -4509,24 +4510,24 @@ var BaseAggregate = class extends Entity {
4509
4510
  registerPendingEventRecordingCapability(this, { record: (createStamp) => this.recordPendingDecisions(createStamp) });
4510
4511
  }
4511
4512
  /**
4512
- * Stamps every uncommitted decision in the pending list with identity
4513
- * and time from `createStamp`, passes an already recorded event through,
4513
+ * Stamps every unstamped decision in the pending list with identity
4514
+ * and time from `createStamp`, passes an already stamped event through,
4514
4515
  * and replaces the list atomically. Three guards run before the
4515
- * assignment: an event that no kit constructor minted is rejected
4516
- * ({@link UnmintedEventError}); a stamp provider that recorded a new
4516
+ * assignment: an event that no kit constructor built is rejected
4517
+ * ({@link HandBuiltEventError}); a stamp provider that recorded a new
4517
4518
  * decision on this aggregate mid-map is rejected
4518
4519
  * ({@link ReentrantEventRecordingError}); and two decisions that
4519
4520
  * received one eventId are rejected ({@link DuplicateEventIdError}).
4520
- * When a guard fires, every decision stays unrecorded.
4521
+ * When a guard fires, every decision stays unstamped.
4521
4522
  */
4522
4523
  recordPendingDecisions(createStamp) {
4523
4524
  const stamped = this._pendingEvents;
4524
4525
  const stampedCount = stamped.length;
4525
4526
  const recorded = stamped.map((event, index) => {
4526
4527
  const candidate = event;
4527
- if (isRecordedDomainEvent(candidate)) return candidate;
4528
- if (!isUncommittedDomainEvent(candidate)) throw new UnmintedEventError(event.type);
4529
- return recordDomainEvent(candidate, createStamp(candidate, index));
4528
+ if (isStampedDomainEvent(candidate)) return candidate;
4529
+ if (!isUnstampedDomainEvent(candidate)) throw new HandBuiltEventError(event.type);
4530
+ return stampDomainEvent(candidate, createStamp(candidate, index));
4530
4531
  });
4531
4532
  if (this._pendingEvents !== stamped || this._pendingEvents.length !== stampedCount) throw new ReentrantEventRecordingError({ identity: this.aggregateIdentity });
4532
4533
  const seenEventIds = /* @__PURE__ */ new Set();
@@ -4549,7 +4550,7 @@ var BaseAggregate = class extends Entity {
4549
4550
  /**
4550
4551
  * Post-commit cleanup for the deleted disposition. The row is gone, so
4551
4552
  * there is no persisted version to advance: stamping the marker from the
4552
- * live instance would make a later legitimate re-enrollment of this
4553
+ * live instance would make a later legitimate re-registration of this
4553
4554
  * instance trip the unique-cursor guard for a row that does not exist.
4554
4555
  */
4555
4556
  discardPendingEventsAfterDeletion(events) {
@@ -4636,12 +4637,12 @@ var BaseAggregate = class extends Entity {
4636
4637
  this._persistedVersion = restored;
4637
4638
  }
4638
4639
  /**
4639
- * Appends a domain event to the pending list. The event must be minted
4640
+ * Appends a domain event to the pending list. The event must be kit-built
4640
4641
  * by a kit constructor; a missing `aggregateId` or `aggregateType` is
4641
4642
  * stamped from this aggregate, and an aggregate identity that names another
4642
4643
  * aggregate throws {@link MisattributedEventError} before anything is
4643
4644
  * recorded. Each append is one fact. A decision appended twice becomes
4644
- * two facts with distinct ids. A recorded event that is already pending
4645
+ * two facts with distinct ids. A stamped event that is already pending
4645
4646
  * throws {@link DuplicateEventIdError}, and a list at `maxPendingEvents`
4646
4647
  * throws {@link PendingEventLimitExceededError}, before anything is
4647
4648
  * recorded. Prefer the higher-level `StateStoredAggregate.setState()`
@@ -4654,23 +4655,23 @@ var BaseAggregate = class extends Entity {
4654
4655
  * application shell rejects it; use `setState(currentState, event)`.
4655
4656
  */
4656
4657
  addDomainEvent(event) {
4657
- const stamped = this.stampNewEventIdentity(event);
4658
+ const stamped = this.attributeNewEvent(event);
4658
4659
  this.assertEventIdsNotPending([stamped]);
4659
4660
  this.assertPendingEventLimit(1);
4660
4661
  this.appendStampedEvent(stamped);
4661
4662
  }
4662
4663
  /**
4663
4664
  * One identity per fact, checked before the change becomes observable:
4664
- * a recorded event whose `eventId` is already pending, or appears twice
4665
+ * a stamped event whose `eventId` is already pending, or appears twice
4665
4666
  * in one batch, throws {@link DuplicateEventIdError}. A decision has no
4666
4667
  * `eventId` yet, so a stamp provider that reuses one is caught at
4667
4668
  * recording instead.
4668
4669
  */
4669
4670
  assertEventIdsNotPending(batch) {
4670
4671
  const pendingIds = /* @__PURE__ */ new Set();
4671
- for (const pending of this._pendingEvents) if (isRecordedDomainEvent(pending)) pendingIds.add(pending.eventId);
4672
+ for (const pending of this._pendingEvents) if (isStampedDomainEvent(pending)) pendingIds.add(pending.eventId);
4672
4673
  for (const event of batch) {
4673
- if (!isRecordedDomainEvent(event)) continue;
4674
+ if (!isStampedDomainEvent(event)) continue;
4674
4675
  if (pendingIds.has(event.eventId)) throw new DuplicateEventIdError({
4675
4676
  identity: this.aggregateIdentity,
4676
4677
  eventId: event.eventId
@@ -4698,7 +4699,7 @@ var BaseAggregate = class extends Entity {
4698
4699
  }
4699
4700
  /**
4700
4701
  * Appends an event that the caller already passed through
4701
- * {@link stampNewEventIdentity}, {@link assertEventIdsNotPending} and
4702
+ * {@link attributeNewEvent}, {@link assertEventIdsNotPending} and
4702
4703
  * {@link assertPendingEventLimit}. `setState()` and `apply()` run the
4703
4704
  * three before the state moves and append afterwards, so each gate
4704
4705
  * runs once per event.
@@ -4719,15 +4720,15 @@ var BaseAggregate = class extends Entity {
4719
4720
  * present-but-foreign `aggregateId` / `aggregateType` is a wiring bug and
4720
4721
  * throws {@link MisattributedEventError}; missing fields are filled in
4721
4722
  * from the aggregate, so a recorded event always carries its full
4722
- * aggregate identity and can never fail the harvest or the replay guard
4723
- * later. The mint gate
4724
- * runs first, so an unminted event fails before anything else. The
4723
+ * aggregate identity and can never fail the collection or the replay guard
4724
+ * later. The kit-built check
4725
+ * runs first, so a hand-built event fails before anything else. The
4725
4726
  * stamped copy is frozen like the original (payload and metadata are
4726
4727
  * shared, already deep-frozen by the constructors); a fully stamped
4727
4728
  * event is returned as is.
4728
4729
  */
4729
- stampNewEventIdentity(event) {
4730
- this.assertMintedEvent(event);
4730
+ attributeNewEvent(event) {
4731
+ this.assertKitBuiltEvent(event);
4731
4732
  const { aggregateId, aggregateType } = event;
4732
4733
  const idForeign = aggregateId !== void 0 && aggregateId !== this.id;
4733
4734
  const typeForeign = aggregateType !== void 0 && aggregateType !== this.aggregateType;
@@ -4748,32 +4749,31 @@ var BaseAggregate = class extends Entity {
4748
4749
  aggregateId: this.id,
4749
4750
  aggregateType: this.aggregateType
4750
4751
  };
4751
- return isRecordedDomainEvent(event) ? adoptRecordedDomainEvent(copy) : adoptUncommittedDomainEvent(copy);
4752
+ return isStampedDomainEvent(event) ? adoptStampedDomainEvent(copy) : adoptUnstampedDomainEvent(copy);
4752
4753
  }
4753
4754
  /**
4754
- * Mint gate for every recording path: only an event that a kit
4755
- * constructor produced passes. The kit marks two shapes: a recorded
4756
- * event carries the recorded brand (`createDomainEvent`,
4757
- * `createDomainEventFromFacts`) and a decision carries the uncommitted
4758
- * brand (`createUncommittedDomainEvent`, the aggregate `createEvent`
4759
- * helper). Either brand implies deeply frozen with defensively copied
4755
+ * Kit-built check for every recording path: only an event that a kit
4756
+ * constructor produced passes. The kit marks two shapes: a stamped
4757
+ * event carries the stamped brand (`createDomainEvent`,
4758
+ * `createStampedDomainEvent`) and a decision carries the unstamped
4759
+ * brand (the aggregate `createEvent` helper). Either brand implies deeply frozen with defensively copied
4760
4760
  * payload and metadata, a guarantee no frozen-ness probe can establish
4761
4761
  * (a shallow-frozen literal with mutable nested data would fool it).
4762
4762
  * O(1): one brand check per shape.
4763
4763
  */
4764
- assertMintedEvent(event) {
4765
- if (!isRecordedDomainEvent(event) && !isUncommittedDomainEvent(event)) throw new UnmintedEventError(event.type);
4764
+ assertKitBuiltEvent(event) {
4765
+ if (!isStampedDomainEvent(event) && !isUnstampedDomainEvent(event)) throw new HandBuiltEventError(event.type);
4766
4766
  }
4767
4767
  /**
4768
4768
  * Creates the immutable business fact accepted by this aggregate without
4769
4769
  * reading a clock, generating an id, or attaching tracing metadata.
4770
4770
  *
4771
- * The application shell records pending events after the domain operation
4771
+ * The application shell stamps pending events after the domain operation
4772
4772
  * and before persistence. Payload schema version stays here, next to the
4773
- * concrete event producer, rather than in shell-owned recording data.
4773
+ * concrete event producer, rather than in shell-owned stamp data.
4774
4774
  */
4775
4775
  createEvent(type, payload, options) {
4776
- return mintUncommittedDomainEvent(type, payload, {
4776
+ return buildUnstampedDomainEvent(type, payload, {
4777
4777
  ...options,
4778
4778
  aggregateId: this.id,
4779
4779
  aggregateType: this.aggregateType
@@ -4785,7 +4785,7 @@ var BaseAggregate = class extends Entity {
4785
4785
  * `EventSourcedAggregate.replayHistory`: a target carrying unflushed
4786
4786
  * `pendingEvents` throws {@link UnreplayableAggregateError} BEFORE anything
4787
4787
  * moves. A restore advances the aggregate's current version, so unflushed
4788
- * events recorded against the old version would later be harvested claiming
4788
+ * events recorded against the old version would later be collected claiming
4789
4789
  * a version baseline they were never part of. When the discard is
4790
4790
  * deliberate, discard this dirty instance and reconstitute a fresh aggregate
4791
4791
  * instead of mutating persistence lifecycle state publicly.
@@ -4793,8 +4793,8 @@ var BaseAggregate = class extends Entity {
4793
4793
  * Deliberately a module-level function, not a class method: it MUST not be
4794
4794
  * overridable by consumer subclasses (a no-op override would silently
4795
4795
  * disable the guard at every call site). The callers pass the count of
4796
- * the private list; the commit harvest reads the public `pendingEvents`
4797
- * getter, so a subclass that overrides the getter changes the harvest,
4796
+ * the private list; the commit collection reads the public `pendingEvents`
4797
+ * getter, so a subclass that overrides the getter changes the collection,
4798
4798
  * not this guard.
4799
4799
  *
4800
4800
  * @internal Shared by the aggregate flavours in this package; not part of
@@ -4926,12 +4926,12 @@ var EventSourcedAggregate = class extends BaseAggregate {
4926
4926
  * @param event - The domain event to apply
4927
4927
  */
4928
4928
  apply(event) {
4929
- const stamped = this.stampNewEventIdentity(event);
4929
+ const stamped = this.attributeNewEvent(event);
4930
4930
  this.assertEventIdsNotPending([stamped]);
4931
4931
  this.assertPendingEventLimit(1);
4932
4932
  this.validateEvent(stamped);
4933
4933
  const next = freezeEntityState(this, this.fold(stamped));
4934
- assertStateHasNoHostileOwnKey(next, "Aggregate state");
4934
+ assertStateHasNoOwnProtoKey(next, "Aggregate state");
4935
4935
  assertStateInvariant(this, next);
4936
4936
  this.bumpVersion();
4937
4937
  storeTrustedState(this, next);
@@ -4959,7 +4959,7 @@ var EventSourcedAggregate = class extends BaseAggregate {
4959
4959
  * stream corruption) after the all-or-nothing rollback. History
4960
4960
  * events without the optional aggregate identity fields pass unchecked (the
4961
4961
  * fields are optional on the event shape); NEW events are covered
4962
- * by the stricter `stampNewEventIdentity` on the apply path.
4962
+ * by the stricter `attributeNewEvent` on the apply path.
4963
4963
  */
4964
4964
  assertReplayedEventBelongsHere(event) {
4965
4965
  const idMismatch = event.aggregateId !== void 0 && event.aggregateId !== this.id;
@@ -5020,7 +5020,7 @@ var EventSourcedAggregate = class extends BaseAggregate {
5020
5020
  this.assertReplayedEventBelongsHere(event);
5021
5021
  storeTrustedState(this, this.fold(event));
5022
5022
  }
5023
- assertStateHasNoHostileOwnKey(this.state, "Aggregate state");
5023
+ assertStateHasNoOwnProtoKey(this.state, "Aggregate state");
5024
5024
  this.markReconstituted(startVersion + history.length);
5025
5025
  } catch (e) {
5026
5026
  storeTrustedState(this, previousState);
@@ -5057,20 +5057,20 @@ function reconstituteAggregateFromHistory(createReplayTarget, history) {
5057
5057
  * The aggregate owns identity, valid domain state, behavior, its current
5058
5058
  * domain version, and pending domain events. It deliberately does not own a
5059
5059
  * database baseline or dirty-key bookkeeping. A repository adapter defines
5060
- * its own persistence projection; the application shell retains that opaque
5060
+ * its own persistence capture; the application shell retains that opaque
5061
5061
  * baseline and derives the adapter's change set at flush.
5062
5062
  */
5063
5063
  var StateStoredAggregate = class extends BaseAggregate {
5064
5064
  /**
5065
5065
  * Replaces the state, advances the OCC version, and records the events
5066
- * of the change, in that order. State validation, the event mint gate,
5066
+ * of the change, in that order. State validation, the kit-built check,
5067
5067
  * the aggregate identity check of the event, the pending event-id check,
5068
5068
  * and the pending event limit check run before the change becomes
5069
5069
  * observable, so a rejected decision records nothing and moves nothing.
5070
5070
  * Without events the call is a plain versioned state change.
5071
5071
  */
5072
5072
  setState(newState, events = []) {
5073
- const stamped = (Array.isArray(events) ? events : [events]).map((event) => this.stampNewEventIdentity(event));
5073
+ const stamped = (Array.isArray(events) ? events : [events]).map((event) => this.attributeNewEvent(event));
5074
5074
  this.assertEventIdsNotPending(stamped);
5075
5075
  this.assertPendingEventLimit(stamped.length);
5076
5076
  const next = this.nextVersion();
@@ -5229,684 +5229,16 @@ var NotSpecification = class extends Specification {
5229
5229
  };
5230
5230
 
5231
5231
  //#endregion
5232
- //#region src/domain/state-machine/errors.ts
5233
- /** No transition is defined for the input in the current state. */
5234
- var InvalidDomainTransitionError = class extends DomainError {
5235
- state;
5236
- inputType;
5237
- constructor(state, inputType) {
5238
- super({
5239
- code: "INVALID_DOMAIN_TRANSITION",
5240
- message: `No domain transition from "${state}" on "${inputType}".`
5241
- });
5242
- this.state = state;
5243
- this.inputType = inputType;
5244
- }
5245
- };
5246
- /** A defined transition was rejected by its domain guard. */
5247
- var DomainTransitionGuardRejectedError = class extends DomainError {
5248
- state;
5249
- inputType;
5250
- constructor(state, inputType) {
5251
- super({
5252
- code: "DOMAIN_TRANSITION_GUARD_REJECTED",
5253
- message: `Domain transition guard rejected "${inputType}" from "${state}".`
5254
- });
5255
- this.state = state;
5256
- this.inputType = inputType;
5257
- }
5258
- };
5259
- /** The machine definition violates its runtime contract. */
5260
- var InvalidDomainMachineDefinitionError = class extends KitWiringError {
5261
- constructor(message, cause) {
5262
- super("INVALID_DOMAIN_MACHINE_DEFINITION", message, cause);
5263
- }
5264
- };
5265
- /** Context contains unsupported or unsafe runtime data. */
5266
- var InvalidDomainMachineContextError = class extends KitWiringError {
5267
- constructor(message, cause) {
5268
- super("INVALID_DOMAIN_MACHINE_CONTEXT", message, cause);
5269
- }
5270
- };
5271
- /** A supplied or produced snapshot is malformed or violates invariants. */
5272
- var InvalidDomainMachineSnapshotError = class extends KitWiringError {
5273
- constructor(message, cause) {
5274
- super("INVALID_DOMAIN_MACHINE_SNAPSHOT", message, cause);
5275
- }
5276
- };
5277
- /** An input is malformed or contains unsupported runtime data. */
5278
- var InvalidDomainMachineInputError = class extends KitWiringError {
5279
- constructor(message, cause) {
5280
- super("INVALID_DOMAIN_MACHINE_INPUT", message, cause);
5281
- }
5282
- };
5283
- /** A guard returned a value other than `boolean` or `DomainError`. */
5284
- var InvalidDomainTransitionGuardResultError = class extends KitWiringError {
5285
- constructor(message, cause) {
5286
- super("INVALID_DOMAIN_TRANSITION_GUARD_RESULT", message, cause);
5287
- }
5288
- };
5289
- /** A reducer returned a malformed result or unsupported output data. */
5290
- var InvalidDomainTransitionResultError = class extends KitWiringError {
5291
- constructor(message, cause) {
5292
- super("INVALID_DOMAIN_TRANSITION_RESULT", message, cause);
5293
- }
5294
- };
5295
- /** A callback attempted to evaluate the same stateful machine recursively. */
5296
- var ReentrantDomainStateMachineEvaluationError = class extends KitWiringError {
5297
- constructor() {
5298
- super("REENTRANT_DOMAIN_STATE_MACHINE_EVALUATION", "Domain state machine callbacks cannot evaluate the same machine.");
5299
- }
5300
- };
5301
-
5302
- //#endregion
5303
- //#region src/domain/state-machine/machine-data.ts
5304
- const DOMAIN_MACHINE_DATA_MAX_DEPTH = 256;
5305
- const DOMAIN_MACHINE_DATA_MAX_NODES = 1e4;
5306
- const DOMAIN_MACHINE_DATA_MAX_PROPERTIES = 1e5;
5307
- function copyDomainMachineOutputs(outputs) {
5308
- try {
5309
- const copiedOutputs = cloneDomainMachineDataValue(outputs ?? [], createDomainTransitionOutputError);
5310
- return deepFreeze(copiedOutputs);
5311
- } catch (cause) {
5312
- if (cause instanceof InvalidDomainTransitionResultError) throw cause;
5313
- throw new InvalidDomainTransitionResultError("Domain transition result outputs must contain cloneable, deeply immutable data.", cause);
5314
- }
5315
- }
5316
- function copyDomainMachineInput(input) {
5317
- try {
5318
- return deepFreeze(cloneDomainMachineDataValue(input, createDomainMachineInputError));
5319
- } catch (cause) {
5320
- if (cause instanceof InvalidDomainMachineInputError) throw cause;
5321
- throw new InvalidDomainMachineInputError("Domain machine input must contain cloneable, deeply immutable data.", cause);
5322
- }
5323
- }
5324
- function copyDomainMachineContext(context) {
5325
- try {
5326
- return deepFreeze(cloneDomainMachineDataValue(context, createDomainMachineContextError));
5327
- } catch (cause) {
5328
- if (cause instanceof InvalidDomainMachineContextError) throw cause;
5329
- throw new InvalidDomainMachineContextError("Domain machine context must contain cloneable, deeply immutable data.", cause);
5330
- }
5331
- }
5332
- function createDomainMachineContextError(message, cause) {
5333
- return new InvalidDomainMachineContextError(message, cause);
5334
- }
5335
- function createDomainMachineInputError(message, cause) {
5336
- return new InvalidDomainMachineInputError(message, cause);
5337
- }
5338
- function createDomainTransitionOutputError(message, cause) {
5339
- return new InvalidDomainTransitionResultError(message, cause);
5340
- }
5341
- function cloneDomainMachineDataValue(value, errorFactory, seen = /* @__PURE__ */ new WeakMap(), traversal = {
5342
- nodes: 0,
5343
- properties: 0
5344
- }, depth = 0) {
5345
- if (typeof value === "function") throw errorFactory("Domain machine data cannot contain function values.");
5346
- if (value === null || typeof value !== "object") return value;
5347
- const source = value;
5348
- if (depth > DOMAIN_MACHINE_DATA_MAX_DEPTH) throw errorFactory(`Domain machine data exceeds the maximum depth of ${DOMAIN_MACHINE_DATA_MAX_DEPTH}.`);
5349
- const existing = seen.get(source);
5350
- if (existing !== void 0) return existing;
5351
- traversal.nodes += 1;
5352
- if (traversal.nodes > DOMAIN_MACHINE_DATA_MAX_NODES) throw errorFactory(`Domain machine data contains more than ${DOMAIN_MACHINE_DATA_MAX_NODES.toLocaleString("en-US")} object nodes.`);
5353
- const toStringTagDescriptor = findPropertyDescriptor(source, Symbol.toStringTag);
5354
- if (toStringTagDescriptor !== void 0 && !("value" in toStringTagDescriptor)) throw errorFactory("Domain machine data cannot contain accessor properties.");
5355
- if (Array.isArray(value)) {
5356
- if (!isIntrinsicArrayPrototype(Object.getPrototypeOf(value))) throw errorFactory("Domain machine data cannot contain custom Array instances.");
5357
- const cloned = new Array(value.length);
5358
- seen.set(source, cloned);
5359
- for (const key of readDomainMachineDataKeys(source, errorFactory, traversal)) {
5360
- const descriptor = Object.getOwnPropertyDescriptor(source, key);
5361
- if (!descriptor) continue;
5362
- if (!("value" in descriptor)) throw errorFactory("Domain machine data cannot contain accessor properties.");
5363
- if (key === "length") continue;
5364
- descriptor.value = cloneDomainMachineDataValue(descriptor.value, errorFactory, seen, traversal, depth + 1);
5365
- Object.defineProperty(cloned, key, descriptor);
5366
- }
5367
- return cloned;
5368
- }
5369
- const prototype = Object.getPrototypeOf(source);
5370
- if (prototype !== null && !isIntrinsicObjectPrototype(prototype)) throw errorFactory("Domain machine data cannot contain custom class instances.");
5371
- const tag = Object.prototype.toString.call(source);
5372
- if (isBuiltInObject(source, tag) || ArrayBuffer.isView(source)) throw errorFactory(`Domain machine data cannot contain ${tag.slice(8, -1)} object values.`);
5373
- const cloned = Object.create(prototype === null ? null : Object.prototype);
5374
- seen.set(source, cloned);
5375
- for (const key of readDomainMachineDataKeys(source, errorFactory, traversal)) {
5376
- const descriptor = Object.getOwnPropertyDescriptor(source, key);
5377
- if (!descriptor) continue;
5378
- if (!("value" in descriptor)) throw errorFactory("Domain machine data cannot contain accessor properties.");
5379
- descriptor.value = cloneDomainMachineDataValue(descriptor.value, errorFactory, seen, traversal, depth + 1);
5380
- Object.defineProperty(cloned, key, descriptor);
5381
- }
5382
- return cloned;
5383
- }
5384
- function readDomainMachineDataKeys(value, errorFactory, traversal) {
5385
- const keys = Reflect.ownKeys(value);
5386
- traversal.properties += keys.length;
5387
- if (traversal.properties > DOMAIN_MACHINE_DATA_MAX_PROPERTIES) throw errorFactory(`Domain machine data contains more than ${DOMAIN_MACHINE_DATA_MAX_PROPERTIES.toLocaleString("en-US")} own properties.`);
5388
- return keys;
5389
- }
5390
- function isRecord(value) {
5391
- return typeof value === "object" && value !== null && !Array.isArray(value);
5392
- }
5393
- function isPlainRecord(value) {
5394
- if (!isRecord(value)) return false;
5395
- const prototype = Object.getPrototypeOf(value);
5396
- return prototype === null || isIntrinsicObjectPrototype(prototype);
5397
- }
5398
- function isIntrinsicArrayPrototype(prototype) {
5399
- if (prototype === null || !Array.isArray(prototype)) return false;
5400
- if (!isIntrinsicConstructorPrototype(prototype, "Array")) return false;
5401
- const parentPrototype = Object.getPrototypeOf(prototype);
5402
- return parentPrototype !== null && isIntrinsicObjectPrototype(parentPrototype);
5403
- }
5404
- function isIntrinsicObjectPrototype(prototype) {
5405
- return Object.getPrototypeOf(prototype) === null && isIntrinsicConstructorPrototype(prototype, "Object");
5406
- }
5407
- function hasOwn(value, key) {
5408
- return Object.hasOwn(value, key);
5409
- }
5410
-
5411
- //#endregion
5412
- //#region src/domain/state-machine/definition.ts
5413
- const DOMAIN_MACHINE_DEFINITION_KEYS = /* @__PURE__ */ new Set([
5414
- "initial",
5415
- "initialContext",
5416
- "validateSnapshot",
5417
- "states"
5418
- ]);
5419
- const DOMAIN_MACHINE_STATE_NODE_KEYS = /* @__PURE__ */ new Set([
5420
- "terminal",
5421
- "validateContext",
5422
- "on"
5423
- ]);
5424
- const DOMAIN_MACHINE_TRANSITION_KEYS = /* @__PURE__ */ new Set([
5425
- "target",
5426
- "guard",
5427
- "reduce"
5428
- ]);
5429
- function copyDomainMachineDefinition(definition) {
5430
- const states = readDomainMachineDefinitionProperty(definition, "states");
5431
- const copiedStates = Object.create(null);
5432
- for (const state of Object.keys(states)) {
5433
- const node = readDomainMachineDefinitionProperty(states, state);
5434
- const transitions = readOptionalDomainMachineDefinitionProperty(node, "on");
5435
- const copiedTransitions = Object.create(null);
5436
- for (const inputType of Object.keys(transitions ?? {})) {
5437
- const transition = readDomainMachineDefinitionProperty(transitions, inputType);
5438
- if (transition) {
5439
- const copiedTransition = Object.freeze({
5440
- target: readDomainMachineDefinitionProperty(transition, "target"),
5441
- guard: readOptionalDomainMachineDefinitionProperty(transition, "guard"),
5442
- reduce: readOptionalDomainMachineDefinitionProperty(transition, "reduce")
5443
- });
5444
- Object.defineProperty(copiedTransitions, inputType, {
5445
- value: copiedTransition,
5446
- enumerable: true
5447
- });
5448
- }
5449
- }
5450
- Object.defineProperty(copiedStates, state, {
5451
- value: Object.freeze({
5452
- terminal: readOptionalDomainMachineDefinitionProperty(node, "terminal"),
5453
- validateContext: readOptionalDomainMachineDefinitionProperty(node, "validateContext"),
5454
- on: Object.freeze(copiedTransitions)
5455
- }),
5456
- enumerable: true
5457
- });
5458
- }
5459
- return Object.freeze({
5460
- initial: readDomainMachineDefinitionProperty(definition, "initial"),
5461
- initialContext: readDomainMachineDefinitionProperty(definition, "initialContext"),
5462
- validateSnapshot: readOptionalDomainMachineDefinitionProperty(definition, "validateSnapshot"),
5463
- states: Object.freeze(copiedStates)
5464
- });
5465
- }
5466
- /**
5467
- * Registry of definitions produced by `prepareDomainMachineDefinition`:
5468
- * validated, defensively copied, deeply frozen. The runtime membership
5469
- * proof lives in this module-private WeakSet (the stable copies are
5470
- * frozen and must stay pure data, so no runtime brand property); the
5471
- * compile-time proof is the required type brand on
5472
- * {@link PreparedDomainMachineDefinition}. Only
5473
- * `prepareDomainMachineDefinition` below adds to the set, so membership
5474
- * always implies validated + copied + frozen.
5475
- */
5476
- const preparedDefinitions = /* @__PURE__ */ new WeakSet();
5477
- /**
5478
- * The entry-point normalization every pure function and the
5479
- * `DomainStateMachine` constructor share: a prepared definition passes
5480
- * through untouched (already validated, copied, frozen); anything else
5481
- * pays the documented per-call validate-and-copy.
5482
- */
5483
- function ensureStableDomainMachineDefinition(definition) {
5484
- if (preparedDefinitions.has(definition)) return definition;
5485
- validateDomainMachineDefinition(definition);
5486
- const stable = copyDomainMachineDefinition(definition);
5487
- validateDomainMachineDefinition(stable);
5488
- return stable;
5489
- }
5490
- /**
5491
- * Validates and stabilizes a machine definition ONCE, for repeated use
5492
- * with the pure functions. Without it, `transitionDomainState` and
5493
- * `canTransitionDomainState` re-validate and defensively re-copy the
5494
- * WHOLE definition on every call (the documented safety of the raw
5495
- * path); on a hot dispatch path that is avoidable O(definition) work.
5496
- * The `DomainStateMachine` class does the equivalent once in its
5497
- * constructor; this export brings the same amortization to pure-API
5498
- * users:
5499
- *
5500
- * ```ts
5501
- * const prepared = prepareDomainMachineDefinition(orderLifecycle);
5502
- * // per dispatch: no re-validation, no definition copy
5503
- * const outcome = transitionDomainState(prepared, snapshot, input);
5504
- * ```
5505
- *
5506
- * The returned definition is a deeply frozen copy, isolated from later
5507
- * mutation of the input object. Preparing an already-prepared
5508
- * definition returns it unchanged.
5509
- */
5510
- function prepareDomainMachineDefinition(definition) {
5511
- const stable = ensureStableDomainMachineDefinition(definition);
5512
- preparedDefinitions.add(stable);
5513
- return stable;
5514
- }
5515
- function getTransition(definition, state, input) {
5516
- const transitions = definition.states[state].on;
5517
- if (!transitions || !hasOwn(transitions, input.type)) return void 0;
5518
- return transitions[input.type];
5519
- }
5520
- function validateDomainMachineDefinition(definition) {
5521
- const candidate = definition;
5522
- if (!isPlainRecord(candidate)) throw new InvalidDomainMachineDefinitionError("Domain machine definition must be a plain object.");
5523
- assertDomainMachineDefinitionDataProperties(candidate, DOMAIN_MACHINE_DEFINITION_KEYS);
5524
- const initial = readDomainMachineDefinitionProperty(candidate, "initial");
5525
- if (typeof initial !== "string") throw new InvalidDomainMachineDefinitionError("Domain machine initial state must be a string data property.");
5526
- if (typeof readDomainMachineDefinitionProperty(candidate, "initialContext") !== "function") throw new InvalidDomainMachineDefinitionError("Domain machine initialContext must be a function data property.");
5527
- const validateSnapshot = readOptionalDomainMachineDefinitionProperty(candidate, "validateSnapshot");
5528
- if (validateSnapshot !== void 0 && typeof validateSnapshot !== "function") throw new InvalidDomainMachineDefinitionError("Domain machine validateSnapshot must be a function data property.");
5529
- const statesCandidate = readDomainMachineDefinitionProperty(candidate, "states");
5530
- if (!isPlainRecord(statesCandidate)) throw new InvalidDomainMachineDefinitionError("Domain machine states must be a plain object data property.");
5531
- const states = statesCandidate;
5532
- assertDomainMachineDefinitionEntryMap(states, "state");
5533
- if (!hasOwn(states, initial)) throw new InvalidDomainMachineDefinitionError(`Initial domain machine state "${initial}" is not defined.`);
5534
- for (const state of Object.keys(states)) {
5535
- const node = readDomainMachineDefinitionProperty(states, state);
5536
- if (!isPlainRecord(node)) throw new InvalidDomainMachineDefinitionError(`Domain machine state "${state}" must be a plain object data property.`);
5537
- assertDomainMachineDefinitionDataProperties(node, DOMAIN_MACHINE_STATE_NODE_KEYS);
5538
- const terminal = readOptionalDomainMachineDefinitionProperty(node, "terminal");
5539
- if (terminal !== void 0 && typeof terminal !== "boolean") throw new InvalidDomainMachineDefinitionError(`Domain machine state "${state}" terminal flag must be a boolean.`);
5540
- const validateContext = readOptionalDomainMachineDefinitionProperty(node, "validateContext");
5541
- if (validateContext !== void 0 && typeof validateContext !== "function") throw new InvalidDomainMachineDefinitionError(`Domain machine state "${state}" validateContext must be a function.`);
5542
- const transitions = readOptionalDomainMachineDefinitionProperty(node, "on");
5543
- if (transitions !== void 0 && !isPlainRecord(transitions)) throw new InvalidDomainMachineDefinitionError(`Domain machine state "${state}" transitions must be a plain object.`);
5544
- if (isPlainRecord(transitions)) assertDomainMachineDefinitionEntryMap(transitions, "input");
5545
- const inputTypes = Object.keys(transitions ?? {});
5546
- if (terminal === true && inputTypes.length > 0) throw new InvalidDomainMachineDefinitionError(`Terminal domain machine state "${state}" cannot declare transitions.`);
5547
- for (const inputType of inputTypes) {
5548
- const transition = readDomainMachineDefinitionProperty(transitions, inputType);
5549
- if (!isPlainRecord(transition)) throw new InvalidDomainMachineDefinitionError(`Domain transition from "${state}" on "${inputType}" must be a plain object.`);
5550
- assertDomainMachineDefinitionDataProperties(transition, DOMAIN_MACHINE_TRANSITION_KEYS);
5551
- const target = readDomainMachineDefinitionProperty(transition, "target");
5552
- if (typeof target !== "string") throw new InvalidDomainMachineDefinitionError(`Domain transition from "${state}" on "${inputType}" must target a string state.`);
5553
- if (!hasOwn(states, target)) throw new InvalidDomainMachineDefinitionError(`Domain transition from "${state}" on "${inputType}" targets unknown state "${target}".`);
5554
- const guard = readOptionalDomainMachineDefinitionProperty(transition, "guard");
5555
- if (guard !== void 0 && typeof guard !== "function") throw new InvalidDomainMachineDefinitionError(`Domain transition from "${state}" on "${inputType}" guard must be a function.`);
5556
- const reduce = readOptionalDomainMachineDefinitionProperty(transition, "reduce");
5557
- if (reduce !== void 0 && typeof reduce !== "function") throw new InvalidDomainMachineDefinitionError(`Domain transition from "${state}" on "${inputType}" reduce must be a function.`);
5558
- }
5559
- }
5560
- }
5561
- function assertDomainMachineDefinitionDataProperties(value, allowedKeys) {
5562
- for (const key of Reflect.ownKeys(value)) {
5563
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
5564
- if (descriptor !== void 0 && !("value" in descriptor)) throw new InvalidDomainMachineDefinitionError("Domain machine definition must contain data properties only.");
5565
- if (allowedKeys !== void 0 && !allowedKeys.has(key)) throw new InvalidDomainMachineDefinitionError(`Domain machine definition contains unknown property "${String(key)}".`);
5566
- }
5567
- }
5568
- function assertDomainMachineDefinitionEntryMap(value, entryName) {
5569
- assertDomainMachineDefinitionDataProperties(value);
5570
- for (const key of Reflect.ownKeys(value)) {
5571
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
5572
- if (typeof key !== "string" || descriptor?.enumerable !== true) throw new InvalidDomainMachineDefinitionError(`Domain machine ${entryName} names must be enumerable string properties.`);
5573
- }
5574
- }
5575
- function readDomainMachineDefinitionProperty(value, key) {
5576
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
5577
- if (descriptor === void 0 || !("value" in descriptor)) throw new InvalidDomainMachineDefinitionError("Domain machine definition must contain data properties only.");
5578
- return descriptor.value;
5579
- }
5580
- function readOptionalDomainMachineDefinitionProperty(value, key) {
5581
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
5582
- if (descriptor === void 0) return void 0;
5583
- if (!("value" in descriptor)) throw new InvalidDomainMachineDefinitionError("Domain machine definition must contain data properties only.");
5584
- return descriptor.value;
5585
- }
5586
-
5587
- //#endregion
5588
- //#region src/domain/state-machine/snapshot.ts
5589
- const DOMAIN_TRANSITION_RESULT_KEYS = /* @__PURE__ */ new Set(["context", "outputs"]);
5590
- function prepareDomainMachineSnapshot(definition, snapshot) {
5591
- validateDomainMachineSnapshot(definition, snapshot);
5592
- const preparedSnapshot = createDomainMachineSnapshot(snapshot);
5593
- validateDomainMachineSnapshot(definition, preparedSnapshot);
5594
- validateDomainMachineSnapshotInvariant(definition, preparedSnapshot);
5595
- return preparedSnapshot;
5596
- }
5597
- function createDomainMachineSnapshotFromPreparedContext(state, context) {
5598
- return Object.freeze({
5599
- state,
5600
- context
5601
- });
5602
- }
5603
- function createDomainMachineSnapshot(snapshot) {
5604
- return Object.freeze({
5605
- state: readDomainMachineSnapshotState(snapshot),
5606
- context: copyDomainMachineContext(readDomainMachineSnapshotContext(snapshot))
5607
- });
5608
- }
5609
- function validateDomainMachineSnapshot(definition, snapshot) {
5610
- if (!isRecord(snapshot)) throw new InvalidDomainMachineSnapshotError("Domain machine snapshot must be an object.");
5611
- const state = readDomainMachineSnapshotState(snapshot);
5612
- readDomainMachineSnapshotContext(snapshot);
5613
- if (!hasOwn(definition.states, state)) throw new InvalidDomainMachineSnapshotError(`Domain machine snapshot state "${state}" is not defined.`);
5614
- }
5615
- function validateDomainMachineSnapshotInvariant(definition, snapshot) {
5616
- const stateNode = definition.states[snapshot.state];
5617
- if (stateNode.validateContext !== void 0) {
5618
- const validContext = stateNode.validateContext({
5619
- state: snapshot.state,
5620
- context: snapshot.context
5621
- });
5622
- if (typeof validContext !== "boolean") throw new InvalidDomainMachineDefinitionError(`Domain machine state "${snapshot.state}" validateContext must return a boolean.`);
5623
- if (!validContext) throw new InvalidDomainMachineSnapshotError(`Domain machine snapshot violates the context invariant for state "${snapshot.state}".`);
5624
- }
5625
- if (definition.validateSnapshot === void 0) return;
5626
- const valid = definition.validateSnapshot(snapshot);
5627
- if (typeof valid !== "boolean") throw new InvalidDomainMachineDefinitionError("Domain machine validateSnapshot must return a boolean.");
5628
- if (!valid) throw new InvalidDomainMachineSnapshotError(`Domain machine snapshot violates invariants for state "${snapshot.state}".`);
5629
- }
5630
- function readDomainMachineSnapshotState(snapshot) {
5631
- const stateDescriptor = Object.getOwnPropertyDescriptor(snapshot, "state");
5632
- if (stateDescriptor === void 0 || !("value" in stateDescriptor) || typeof stateDescriptor.value !== "string") throw new InvalidDomainMachineSnapshotError("Domain machine snapshot state must be a string data property.");
5633
- return stateDescriptor.value;
5634
- }
5635
- function readDomainMachineSnapshotContext(snapshot) {
5636
- const contextDescriptor = Object.getOwnPropertyDescriptor(snapshot, "context");
5637
- if (contextDescriptor === void 0 || !("value" in contextDescriptor)) throw new InvalidDomainMachineSnapshotError("Domain machine snapshot context must be present as a data property.");
5638
- return contextDescriptor.value;
5639
- }
5640
- function validateDomainMachineInput(input) {
5641
- if (!isDomainMachineInput(input)) throw new InvalidDomainMachineInputError("Domain machine input must be an object with a string type.");
5642
- }
5643
- function isDomainMachineInput(input) {
5644
- if (!isRecord(input)) return false;
5645
- const typeDescriptor = Object.getOwnPropertyDescriptor(input, "type");
5646
- return typeDescriptor !== void 0 && "value" in typeDescriptor && typeof typeDescriptor.value === "string";
5647
- }
5648
- function resolveDomainTransitionGuardResult(result) {
5649
- if (typeof result === "boolean") return { allowed: result };
5650
- if (result instanceof DomainError) return {
5651
- allowed: false,
5652
- rejection: result
5653
- };
5654
- throw new InvalidDomainTransitionGuardResultError("Domain transition guard must return a boolean or DomainError.");
5655
- }
5656
- function validateDomainTransitionResult(result) {
5657
- if (result === void 0) return;
5658
- if (!isPlainRecord(result)) throw new InvalidDomainTransitionResultError("Domain transition result must be a plain object when returned.");
5659
- for (const key of Reflect.ownKeys(result)) {
5660
- if (!DOMAIN_TRANSITION_RESULT_KEYS.has(key)) throw new InvalidDomainTransitionResultError(`Domain transition result contains unknown property "${String(key)}".`);
5661
- const descriptor = Object.getOwnPropertyDescriptor(result, key);
5662
- if (descriptor !== void 0 && !("value" in descriptor)) throw new InvalidDomainTransitionResultError("Domain transition result must contain data properties only.");
5663
- }
5664
- const outputs = readDomainTransitionResultOutputs(result);
5665
- if (outputs !== void 0 && !Array.isArray(outputs)) throw new InvalidDomainTransitionResultError("Domain transition result outputs must be an array when provided.");
5666
- }
5667
- function readDomainTransitionResultContext(result) {
5668
- if (result === void 0) return { hasContext: false };
5669
- const contextDescriptor = Object.getOwnPropertyDescriptor(result, "context");
5670
- if (contextDescriptor === void 0) return { hasContext: false };
5671
- if (!("value" in contextDescriptor)) throw new InvalidDomainTransitionResultError("Domain transition result context must be a data property when provided.");
5672
- return {
5673
- hasContext: true,
5674
- context: contextDescriptor.value
5675
- };
5676
- }
5677
- function readDomainTransitionResultOutputs(result) {
5678
- if (result === void 0) return void 0;
5679
- const outputsDescriptor = Object.getOwnPropertyDescriptor(result, "outputs");
5680
- if (outputsDescriptor === void 0) return void 0;
5681
- if (!("value" in outputsDescriptor)) throw new InvalidDomainTransitionResultError("Domain transition result outputs must be a data property when provided.");
5682
- return outputsDescriptor.value;
5683
- }
5684
-
5685
- //#endregion
5686
- //#region src/domain/state-machine/transition.ts
5687
- /** Creates and validates a fresh initial snapshot from a machine definition. */
5688
- function createInitialDomainMachineSnapshot(definition) {
5689
- return createInitialDomainMachineSnapshotFromPrepared(ensureStableDomainMachineDefinition(definition));
5690
- }
5691
- function createInitialDomainMachineSnapshotFromPrepared(definition) {
5692
- const snapshot = createDomainMachineSnapshot({
5693
- state: definition.initial,
5694
- context: definition.initialContext()
5695
- });
5696
- validateDomainMachineSnapshotInvariant(definition, snapshot);
5697
- return snapshot;
5698
- }
5699
- /**
5700
- * Checks whether an input currently has an allowed transition.
5701
- *
5702
- * Returns `false` for missing transitions, terminal states, rejected guards,
5703
- * and inputs without an own string `type` property. Invalid payload data for a
5704
- * matching transition and broken guard code still throw structured errors.
5705
- */
5706
- function canTransitionDomainState(definition, snapshot, input) {
5707
- const stableDefinition = ensureStableDomainMachineDefinition(definition);
5708
- return canTransitionPreparedDomainState(stableDefinition, prepareDomainMachineSnapshot(stableDefinition, snapshot), input);
5709
- }
5710
- function canTransitionPreparedDomainState(definition, snapshot, input) {
5711
- if (!isDomainMachineInput(input)) return false;
5712
- if (definition.states[snapshot.state].terminal === true) return false;
5713
- const transition = getTransition(definition, snapshot.state, input);
5714
- if (!transition) return false;
5715
- const currentInput = copyDomainMachineInput(input);
5716
- if (!transition.guard) return true;
5717
- const guardResult = transition.guard({
5718
- state: snapshot.state,
5719
- context: snapshot.context,
5720
- input: currentInput
5721
- });
5722
- return resolveDomainTransitionGuardResult(guardResult).allowed;
5723
- }
5724
- /**
5725
- * Applies one input without mutating the input definition or snapshot.
5726
- *
5727
- * @throws {@link InvalidDomainTransitionError} when no transition is defined.
5728
- * @throws {@link DomainTransitionGuardRejectedError} when its guard rejects.
5729
- * @throws A concrete `DomainError` returned by a rejecting guard.
5730
- */
5731
- function transitionDomainState(definition, snapshot, input) {
5732
- const stableDefinition = ensureStableDomainMachineDefinition(definition);
5733
- return transitionPreparedDomainState(stableDefinition, prepareDomainMachineSnapshot(stableDefinition, snapshot), input);
5734
- }
5735
- function transitionPreparedDomainState(definition, snapshot, input) {
5736
- validateDomainMachineInput(input);
5737
- const from = snapshot.state;
5738
- const transition = definition.states[from].terminal === true ? void 0 : getTransition(definition, from, input);
5739
- if (!transition) throw new InvalidDomainTransitionError(from, input.type);
5740
- const currentInput = copyDomainMachineInput(input);
5741
- const guardResult = transition.guard === void 0 ? true : transition.guard({
5742
- state: from,
5743
- context: snapshot.context,
5744
- input: currentInput
5745
- });
5746
- const guardDecision = resolveDomainTransitionGuardResult(guardResult);
5747
- if (!guardDecision.allowed) {
5748
- if (guardDecision.rejection !== void 0) throw guardDecision.rejection;
5749
- throw new DomainTransitionGuardRejectedError(from, currentInput.type);
5750
- }
5751
- const result = transition.reduce?.({
5752
- state: from,
5753
- context: snapshot.context,
5754
- input: currentInput
5755
- });
5756
- validateDomainTransitionResult(result);
5757
- const contextResult = readDomainTransitionResultContext(result);
5758
- const nextContext = contextResult.hasContext ? contextResult.context : snapshot.context;
5759
- const nextSnapshot = nextContext === snapshot.context ? createDomainMachineSnapshotFromPreparedContext(transition.target, snapshot.context) : createDomainMachineSnapshot({
5760
- state: transition.target,
5761
- context: nextContext
5762
- });
5763
- validateDomainMachineSnapshotInvariant(definition, nextSnapshot);
5764
- return Object.freeze({
5765
- from,
5766
- to: transition.target,
5767
- snapshot: nextSnapshot,
5768
- outputs: copyDomainMachineOutputs(readDomainTransitionResultOutputs(result))
5769
- });
5770
- }
5771
-
5772
- //#endregion
5773
- //#region src/domain/state-machine/analyzer.ts
5774
- /**
5775
- * Inspects the declarative transition graph without executing definition callbacks.
5776
- * Guarded edges are treated as possible edges, so diagnostics never claim more
5777
- * runtime reachability than the static graph can prove.
5778
- */
5779
- function analyzeDomainMachineDefinition(definition) {
5780
- const stableDefinition = ensureStableDomainMachineDefinition(definition);
5781
- const states = Object.keys(stableDefinition.states).sort(compareStrings);
5782
- const outgoing = /* @__PURE__ */ new Map();
5783
- const incoming = /* @__PURE__ */ new Map();
5784
- const transitions = [];
5785
- for (const state of states) {
5786
- outgoing.set(state, []);
5787
- incoming.set(state, []);
5788
- }
5789
- for (const state of states) {
5790
- const stateTransitions = stableDefinition.states[state].on;
5791
- const inputTypes = Object.keys(stateTransitions ?? {}).sort(compareStrings);
5792
- for (const inputType of inputTypes) {
5793
- const transition = stateTransitions?.[inputType];
5794
- if (transition === void 0) continue;
5795
- outgoing.get(state)?.push(transition.target);
5796
- incoming.get(transition.target)?.push(state);
5797
- transitions.push(Object.freeze({
5798
- state,
5799
- inputType,
5800
- target: transition.target,
5801
- guarded: transition.guard !== void 0
5802
- }));
5803
- }
5804
- }
5805
- const structurallyReachable = visitGraph([stableDefinition.initial], outgoing);
5806
- const statesWithTerminalPath = visitGraph(states.filter((state) => stableDefinition.states[state].terminal === true), incoming);
5807
- const diagnostics = [];
5808
- for (const state of states) if (!structurallyReachable.has(state)) diagnostics.push(Object.freeze({
5809
- code: "unreachable-state",
5810
- state
5811
- }));
5812
- for (const state of states) if (stableDefinition.states[state].terminal !== true && outgoing.get(state)?.length === 0) diagnostics.push(Object.freeze({
5813
- code: "structural-dead-end",
5814
- state
5815
- }));
5816
- for (const state of states) if (!statesWithTerminalPath.has(state)) diagnostics.push(Object.freeze({
5817
- code: "no-terminal-path",
5818
- state
5819
- }));
5820
- return Object.freeze({
5821
- diagnostics: Object.freeze(diagnostics),
5822
- transitions: Object.freeze(transitions),
5823
- structurallyReachableStates: Object.freeze(states.filter((state) => structurallyReachable.has(state))),
5824
- statesWithTerminalPath: Object.freeze(states.filter((state) => statesWithTerminalPath.has(state)))
5825
- });
5826
- }
5827
- function visitGraph(startStates, edges) {
5828
- const visited = /* @__PURE__ */ new Set();
5829
- const pending = [...startStates];
5830
- while (pending.length > 0) {
5831
- const state = pending.pop();
5832
- if (state === void 0 || visited.has(state)) continue;
5833
- visited.add(state);
5834
- for (const next of edges.get(state) ?? []) if (!visited.has(next)) pending.push(next);
5835
- }
5836
- return visited;
5837
- }
5838
- function compareStrings(left, right) {
5839
- return left < right ? -1 : left > right ? 1 : 0;
5840
- }
5841
-
5842
- //#endregion
5843
- //#region src/domain/state-machine/domain-state-machine.ts
5844
- /**
5845
- * Stateful convenience wrapper around the pure domain transition functions.
5846
- *
5847
- * Persist {@link snapshot}, not the machine instance. Pass a restored snapshot
5848
- * to the second constructor overload to validate and reconstitute a machine.
5849
- */
5850
- var DomainStateMachine = class {
5851
- definition;
5852
- #snapshot;
5853
- #evaluating = false;
5854
- constructor(definition, ...snapshotInput) {
5855
- this.definition = ensureStableDomainMachineDefinition(definition);
5856
- const [suppliedSnapshot] = snapshotInput;
5857
- if (suppliedSnapshot !== void 0) this.#snapshot = prepareDomainMachineSnapshot(this.definition, suppliedSnapshot);
5858
- else this.#snapshot = createInitialDomainMachineSnapshotFromPrepared(this.definition);
5859
- }
5860
- /** Returns a defensive, deeply frozen copy of the current snapshot. */
5861
- get snapshot() {
5862
- return createDomainMachineSnapshot(this.#snapshot);
5863
- }
5864
- /** Current named control state. */
5865
- get state() {
5866
- return this.#snapshot.state;
5867
- }
5868
- /** Current deeply readonly context. */
5869
- get context() {
5870
- return this.#snapshot.context;
5871
- }
5872
- /** Whether the current state permanently forbids outgoing transitions. */
5873
- isTerminal() {
5874
- return this.definition.states[this.state].terminal === true;
5875
- }
5876
- /** Checks a transition without changing the current snapshot. */
5877
- can(input) {
5878
- return this.evaluate(() => canTransitionPreparedDomainState(this.definition, this.#snapshot, input));
5879
- }
5880
- /** Applies an input and advances the current snapshot on success. */
5881
- dispatch(input) {
5882
- return this.evaluate(() => {
5883
- const result = transitionPreparedDomainState(this.definition, this.#snapshot, input);
5884
- this.#snapshot = result.snapshot;
5885
- return result;
5886
- });
5887
- }
5888
- evaluate(operation) {
5889
- if (this.#evaluating) throw new ReentrantDomainStateMachineEvaluationError();
5890
- this.#evaluating = true;
5891
- try {
5892
- return operation();
5893
- } finally {
5894
- this.#evaluating = false;
5895
- }
5896
- }
5897
- };
5898
-
5899
- //#endregion
5900
- //#region src/domain/value-object/vo-validated.ts
5232
+ //#region src/domain/value-object/value-object-or-issues.ts
5901
5233
  /**
5902
5234
  * Builds an immutable value object while collecting **all** validation
5903
5235
  * violations into a single {@link ValidationError}, instead of failing on the
5904
5236
  * first one. This is the Result-first, multi-error counterpart to
5905
- * `voWithValidation` (which returns a single string message).
5237
+ * `valueObjectOrError` (which returns a single string message).
5906
5238
  *
5907
5239
  * The `validate` callback receives a fresh `ValidationError` to push field
5908
5240
  * issues onto (via `addIssue` / `addIssues`) and the raw input. When no issue
5909
- * was recorded the input is frozen into a `VO<T>` and returned as `Ok`;
5241
+ * was recorded the input is frozen into a `ValueObjectOf<T>` and returned as `Ok`;
5910
5242
  * otherwise the populated `ValidationError` is returned as `Err`.
5911
5243
  *
5912
5244
  * `ValidationError` comes from `@shirudo/base-error`; import it from there to
@@ -5916,7 +5248,7 @@ var DomainStateMachine = class {
5916
5248
  *
5917
5249
  * @example
5918
5250
  * ```ts
5919
- * const result = voValidated(
5251
+ * const result = valueObjectOrIssues(
5920
5252
  * { email, age },
5921
5253
  * (issues, m) => {
5922
5254
  * if (!isEmail(m.email))
@@ -5929,10 +5261,10 @@ var DomainStateMachine = class {
5929
5261
  * // result.isErr() → result.error.publicIssues() has both violations
5930
5262
  * ```
5931
5263
  */
5932
- function voValidated(t, validate, message = "Validation failed") {
5264
+ function valueObjectOrIssues(t, validate, message = "Validation failed") {
5933
5265
  const issues = new ValidationError(message);
5934
5266
  validate(issues, t);
5935
- return issues.hasIssues() ? err(issues) : ok(vo(t));
5267
+ return issues.hasIssues() ? err(issues) : ok(valueObject(t));
5936
5268
  }
5937
5269
 
5938
5270
  //#endregion
@@ -6041,7 +5373,7 @@ var PublishChainTracker = class {
6041
5373
  stateBySignal = /* @__PURE__ */ new WeakMap();
6042
5374
  syncWindowState;
6043
5375
  constructor(store) {
6044
- if (store !== void 0 && (typeof store.run !== "function" || typeof store.getStore !== "function")) throw new TypeError("EventBusImpl.chainStore must provide run and getStore");
5376
+ if (store !== void 0 && (typeof store.run !== "function" || typeof store.getStore !== "function")) throw new TypeError("InProcessEventBus.chainStore must provide run and getStore");
6045
5377
  this.store = store;
6046
5378
  }
6047
5379
  /** Depth, path and enclosing state that a publication on `signal` inherits. */
@@ -6152,7 +5484,7 @@ function toHandlerError(reason) {
6152
5484
  /** Bound for one publish chain. Real nesting stays far below this. */
6153
5485
  const DEFAULT_MAX_PUBLISH_DEPTH = 32;
6154
5486
  /** Marks the catch-all subscriptions in the reported-once set. */
6155
- const CATCH_ALL = Symbol("EventBusImpl.subscribeAll");
5487
+ const CATCH_ALL = Symbol("InProcessEventBus.subscribeAll");
6156
5488
  /**
6157
5489
  * Subscriptions for one event type above which the bus reports a leak. A
6158
5490
  * fan-out of projections stays far below this.
@@ -6166,7 +5498,7 @@ const DEFAULT_MAX_SUBSCRIPTIONS_PER_EVENT_TYPE = 32;
6166
5498
  *
6167
5499
  * @example
6168
5500
  * ```typescript
6169
- * const bus = new EventBusImpl<OrderEvent>();
5501
+ * const bus = new InProcessEventBus<OrderEvent>();
6170
5502
  *
6171
5503
  * bus.subscribe("OrderCreated", async (event) => {
6172
5504
  * await sendEmail(event.payload.customerId);
@@ -6226,7 +5558,7 @@ const DEFAULT_MAX_SUBSCRIPTIONS_PER_EVENT_TYPE = 32;
6226
5558
  * the signal.
6227
5559
  *
6228
5560
  */
6229
- var EventBusImpl = class {
5561
+ var InProcessEventBus = class {
6230
5562
  handlers = /* @__PURE__ */ new Map();
6231
5563
  catchAllHandlers = [];
6232
5564
  maxPublishDepth;
@@ -6237,12 +5569,12 @@ var EventBusImpl = class {
6237
5569
  observers;
6238
5570
  reportedAt = /* @__PURE__ */ new Map();
6239
5571
  constructor(options = {}) {
6240
- if (options.maxPublishDepth !== void 0) assertPositiveInteger("EventBusImpl", "maxPublishDepth", options.maxPublishDepth);
6241
- if (options.maxSubscriptionsPerEventType !== void 0) assertPositiveInteger("EventBusImpl", "maxSubscriptionsPerEventType", options.maxSubscriptionsPerEventType);
5572
+ if (options.maxPublishDepth !== void 0) assertPositiveInteger("InProcessEventBus", "maxPublishDepth", options.maxPublishDepth);
5573
+ if (options.maxSubscriptionsPerEventType !== void 0) assertPositiveInteger("InProcessEventBus", "maxSubscriptionsPerEventType", options.maxSubscriptionsPerEventType);
6242
5574
  this.chain = new PublishChainTracker(options.chainStore);
6243
5575
  this.maxPublishDepth = options.maxPublishDepth ?? DEFAULT_MAX_PUBLISH_DEPTH;
6244
5576
  this.maxSubscriptionsPerEventType = options.maxSubscriptionsPerEventType ?? DEFAULT_MAX_SUBSCRIPTIONS_PER_EVENT_TYPE;
6245
- this.observers = options.observers === void 0 ? void 0 : captureObserverFunctions("EventBusImpl", options.observers, [
5577
+ this.observers = options.observers === void 0 ? void 0 : captureObserverFunctions("InProcessEventBus", options.observers, [
6246
5578
  "onSubscriptionThresholdExceeded",
6247
5579
  "onHandlerError",
6248
5580
  "onPublishAborted"
@@ -6515,7 +5847,7 @@ function decodeIntegrationMessage(serialized) {
6515
5847
  }
6516
5848
  }
6517
5849
  /**
6518
- * Composes a validated public message into a minted local projector input.
5850
+ * Composes a validated public message into a kit-built local projector input.
6519
5851
  * Relationship headers become local event metadata. The public JSON schema is
6520
5852
  * retained; producer-private domain types are not reconstructed.
6521
5853
  */
@@ -6668,9 +6000,9 @@ function outboxWriterAcceptingEventLoss() {
6668
6000
  * another aggregate version. Without an `InMemoryTransactionScope`, it keeps
6669
6001
  * the first add of a rolled-back attempt. Dead-lettered and acknowledged
6670
6002
  * retries must match the complete original candidate receipt. Reusing an `eventId` for another source or commit position
6671
- * throws {@link EventHarvestError} while the pending, dead-letter, or bounded
6003
+ * throws {@link EventCollectionError} while the pending, dead-letter, or bounded
6672
6004
  * dispatched receipt still proves the collision. `add` also throws
6673
- * {@link EventHarvestError} for a malformed candidate: a part that is not an
6005
+ * {@link EventCollectionError} for a malformed candidate: a part that is not an
6674
6006
  * object, an `eventId` or `type` that is not an own string data property, a
6675
6007
  * source field that is not a string, or a position that is not a valid
6676
6008
  * commit position. Insertion order is preserved:
@@ -6685,7 +6017,7 @@ function outboxWriterAcceptingEventLoss() {
6685
6017
  * To link future eventful commits, the implementation also retains one
6686
6018
  * source cursor per qualified aggregate after dispatch. `endEventSources`
6687
6019
  * marks the cursor of a removed aggregate as ended, and a new event of that
6688
- * source then throws {@link EventHarvestError}. Consequently a
6020
+ * source then throws {@link EventCollectionError}. Consequently a
6689
6021
  * long-lived instance is bounded only when `maxRecords` and `maxSources` are
6690
6022
  * configured, plus `maxRetainedDispatchedEventIds`; use a durable adapter with
6691
6023
  * an explicit source-head lifecycle and an event-id unique key for unbounded
@@ -6717,10 +6049,10 @@ function outboxWriterAcceptingEventLoss() {
6717
6049
  *
6718
6050
  * @example
6719
6051
  * ```ts
6720
- * import { InMemoryOutbox, EventBusImpl } from "@shirudo/ddd-kit";
6052
+ * import { InMemoryOutbox, InProcessEventBus } from "@shirudo/ddd-kit";
6721
6053
  *
6722
6054
  * const outbox = new InMemoryOutbox<OrderEvent>();
6723
- * const bus = new EventBusImpl<OrderEvent>();
6055
+ * const bus = new InProcessEventBus<OrderEvent>();
6724
6056
  *
6725
6057
  * const uow = makeOrderUnitOfWork({ outbox, bus });
6726
6058
  * await uow.run(async ({ repositories }) => {
@@ -7022,7 +6354,7 @@ var InMemoryOutbox = class {
7022
6354
  if (this.pending.has(event.eventId) || this.dead.has(event.eventId) || this.dispatchedEventIds.has(event.eventId)) return;
7023
6355
  const cursor = this.sourceCursors.get(sourceKey);
7024
6356
  if (cursor !== void 0 && (position.aggregateVersion < cursor.aggregateVersion || position.aggregateVersion === cursor.aggregateVersion && cursor.eventIdsBySequence.has(position.commitSequence))) return;
7025
- throw new EventHarvestError(`InMemoryOutbox rejected event "${event.eventId}" for ${describeAggregateIdentity(source)}: the aggregate was removed, so its event source ended. The kit does not support an aggregate that is created again under a removed identity. Give the new aggregate a new id.`, event.type);
6357
+ throw new EventCollectionError(`InMemoryOutbox rejected event "${event.eventId}" for ${describeAggregateIdentity(source)}: the aggregate was removed, so its event source ended. The kit does not support an aggregate that is created again under a removed identity. Give the new aggregate a new id.`, event.type);
7026
6358
  }
7027
6359
  /**
7028
6360
  * Event ids of the batch that the source head names at their position
@@ -7091,7 +6423,7 @@ function assertSameCandidateReceipt(event, received, recorded) {
7091
6423
  }
7092
6424
  /**
7093
6425
  * The lenient variant for PENDING records only: an outbox without an
7094
- * `InMemoryTransactionScope` cannot observe a rollback, so a re-harvested
6426
+ * `InMemoryTransactionScope` cannot observe a rollback, so a re-collected
7095
6427
  * event may legitimately arrive at a new aggregateVersion. Index and commit
7096
6428
  * cardinality stay immutable.
7097
6429
  */
@@ -7100,7 +6432,7 @@ function assertSameCandidateReceiptAllowingVersionRefresh(event, received, recor
7100
6432
  }
7101
6433
  function assertReceiptShape(event, received, recorded, allowAggregateVersionRefresh) {
7102
6434
  if ((allowAggregateVersionRefresh || received.aggregateVersion === recorded.aggregateVersion) && received.commitSequence === recorded.commitSequence && received.commitSize === recorded.commitSize) return;
7103
- throw new EventHarvestError(`InMemoryOutbox rejected event "${event.eventId}": its commit candidate changed from (${recorded.aggregateVersion}, ${recorded.commitSequence}; commitSize=${recorded.commitSize}) to (${received.aggregateVersion}, ${received.commitSequence}; commitSize=${received.commitSize}). An exact redelivery must keep its source position immutable.`, event.type);
6435
+ throw new EventCollectionError(`InMemoryOutbox rejected event "${event.eventId}": its commit candidate changed from (${recorded.aggregateVersion}, ${recorded.commitSequence}; commitSize=${recorded.commitSize}) to (${received.aggregateVersion}, ${received.commitSequence}; commitSize=${received.commitSize}). An exact redelivery must keep its source position immutable.`, event.type);
7104
6436
  }
7105
6437
  /**
7106
6438
  * The fields of a candidate that the outbox reads, read once and checked.
@@ -7112,7 +6444,7 @@ function assertReceiptShape(event, received, recorded, allowAggregateVersionRefr
7112
6444
  function ownCandidate(candidate, index) {
7113
6445
  let subject = `the candidate at index ${index}`;
7114
6446
  let eventType;
7115
- const rejection = (field, expected, observed) => new EventHarvestError(`InMemoryOutbox rejected ${subject}: ${field} must be ${expected}, but it is ${observed}. Pass the candidates that withCommit builds.`, eventType);
6447
+ const rejection = (field, expected, observed) => new EventCollectionError(`InMemoryOutbox rejected ${subject}: ${field} must be ${expected}, but it is ${observed}. Pass the candidates that withCommit builds.`, eventType);
7116
6448
  if (typeof candidate !== "object" || candidate === null) throw rejection("the candidate", "an object", describeRejected(candidate));
7117
6449
  const { event, source, position } = candidate;
7118
6450
  if (typeof event !== "object" || event === null) throw rejection("event", "an object", describeRejected(event));
@@ -7162,17 +6494,17 @@ function refill(map, entries) {
7162
6494
  for (const [key, value] of entries) map.set(key, value);
7163
6495
  }
7164
6496
  function commitSizeMismatchError(event, source, position, recordedCommitSize) {
7165
- return new EventHarvestError(`InMemoryOutbox rejected event "${event.eventId}" for ${describeAggregateIdentity(source)}: aggregate version ${position.aggregateVersion} was already recorded with commitSize ${recordedCommitSize}, not ${position.commitSize}. An aggregate that was removed and created again under the same identity causes this too; the kit does not support that, so give the new aggregate a new id.`, event.type);
6497
+ return new EventCollectionError(`InMemoryOutbox rejected event "${event.eventId}" for ${describeAggregateIdentity(source)}: aggregate version ${position.aggregateVersion} was already recorded with commitSize ${recordedCommitSize}, not ${position.commitSize}. An aggregate that was removed and created again under the same identity causes this too; the kit does not support that, so give the new aggregate a new id.`, event.type);
7166
6498
  }
7167
6499
  function positionOwnedError(event, source, position, positionOwner) {
7168
- return new EventHarvestError(`InMemoryOutbox rejected event "${event.eventId}" for ${describeAggregateIdentity(source)}: source position (${position.aggregateVersion}, ${position.commitSequence}) is already owned by event "${positionOwner}". One qualified source position must identify exactly one immutable event. An aggregate that was removed and created again under the same identity causes this too; the kit does not support that, so give the new aggregate a new id.`, event.type);
6500
+ return new EventCollectionError(`InMemoryOutbox rejected event "${event.eventId}" for ${describeAggregateIdentity(source)}: source position (${position.aggregateVersion}, ${position.commitSequence}) is already owned by event "${positionOwner}". One qualified source position must identify exactly one immutable event. An aggregate that was removed and created again under the same identity causes this too; the kit does not support that, so give the new aggregate a new id.`, event.type);
7169
6501
  }
7170
6502
  function staleHeadError(event, source, position, staleHeadVersion) {
7171
- return new EventHarvestError(`InMemoryOutbox rejected stale event "${event.eventId}" for ${describeAggregateIdentity(source)} at aggregate version ${position.aggregateVersion}: the event-source head is already ${staleHeadVersion}. There are two possible causes. First, the aggregate was removed and created again under the same identity. The kit does not support this, because the versions of the new aggregate restart below the head. Give the new aggregate a new id. Second, this is a retry of an event whose dispatched-id receipt expired. Use a durable outbox with a transactional eventId unique key for unbounded idempotency.`, event.type);
6503
+ return new EventCollectionError(`InMemoryOutbox rejected stale event "${event.eventId}" for ${describeAggregateIdentity(source)} at aggregate version ${position.aggregateVersion}: the event-source head is already ${staleHeadVersion}. There are two possible causes. First, the aggregate was removed and created again under the same identity. The kit does not support this, because the versions of the new aggregate restart below the head. Give the new aggregate a new id. Second, this is a retry of an event whose dispatched-id receipt expired. Use a durable outbox with a transactional eventId unique key for unbounded idempotency.`, event.type);
7172
6504
  }
7173
6505
  function assertSameEventSource(event, received, recorded) {
7174
6506
  if (received.aggregateType === recorded.aggregateType && received.aggregateId === recorded.aggregateId) return;
7175
- throw new EventHarvestError(`InMemoryOutbox rejected eventId collision for "${event.eventId}": it already belongs to ${describeAggregateIdentity(recorded)}, but was received for ${describeAggregateIdentity(received)}. An eventId must identify one immutable event across all aggregate sources.`, event.type);
6507
+ throw new EventCollectionError(`InMemoryOutbox rejected eventId collision for "${event.eventId}": it already belongs to ${describeAggregateIdentity(recorded)}, but was received for ${describeAggregateIdentity(received)}. An eventId must identify one immutable event across all aggregate sources.`, event.type);
7176
6508
  }
7177
6509
 
7178
6510
  //#endregion
@@ -7433,7 +6765,7 @@ var OutboxDispatcher = class extends PollLoop {
7433
6765
  function copyForStorage(event, index, stream) {
7434
6766
  const subject = `InMemoryEventStore.append (stream ${describeAggregateIdentity(stream)}): events[${index}]`;
7435
6767
  if (event === null || typeof event !== "object") throw new TypeError(`${subject} is not an event object`);
7436
- return detachFreezableState(event, subject);
6768
+ return copyFreezableState(event, subject);
7437
6769
  }
7438
6770
  function assertStreamPosition(name, value) {
7439
6771
  if (value !== void 0 && (!Number.isSafeInteger(value) || value < 0)) throw new RangeError(`InMemoryEventStore: ${name} must be a non-negative safe integer, got ${String(value)}`);
@@ -7492,7 +6824,7 @@ var InMemoryEventStore = class {
7492
6824
  async append(stream, events, options) {
7493
6825
  const batch = Array.from(events);
7494
6826
  if (batch.length === 0) return;
7495
- const identity = detachAggregateIdentity(stream);
6827
+ const identity = copyAggregateIdentity(stream);
7496
6828
  const expectedVersion = options.expectedVersion;
7497
6829
  const key = encodeAggregateIdentity(identity);
7498
6830
  this.existingStreamForAppend(identity, key, expectedVersion, batch.length);
@@ -7770,7 +7102,7 @@ function defaultSleep(ms, signal) {
7770
7102
  *
7771
7103
  * **Classification is by error, not by guesswork.** Only errors the
7772
7104
  * `isRetryable` predicate accepts are retried; everything else (a
7773
- * `DomainError`, `EventHarvestError`, `UnenrolledChangesError`,
7105
+ * `DomainError`, `EventCollectionError`, `UnregisteredChangesError`,
7774
7106
  * `DuplicateAggregateError`, a non-Error throw) surfaces immediately.
7775
7107
  * After `maxAttempts` the last error is rethrown unchanged, so a caller
7776
7108
  * can still match `ConcurrencyConflictError` and map it to HTTP 409.
@@ -7936,12 +7268,12 @@ var InMemorySnapshotStore = class {
7936
7268
  //#region src/persistence/snapshot-store/snapshot-model.ts
7937
7269
  /** Type-inference helper for declaring an adapter-owned snapshot model. */
7938
7270
  function defineSnapshotModel(model) {
7939
- const detached = Object.freeze({ ...model });
7940
- assertSnapshotModel(detached);
7941
- return detached;
7271
+ const copy = Object.freeze({ ...model });
7272
+ assertSnapshotModel(copy);
7273
+ return copy;
7942
7274
  }
7943
7275
  /**
7944
- * Captures a detached persistence envelope at an application-supplied time.
7276
+ * Captures an independent persistence envelope at an application-supplied time.
7945
7277
  * The application decides when snapshotting is worthwhile; this function does
7946
7278
  * not read a clock or perform I/O. The envelope is deep-frozen, its `state`
7947
7279
  * and its `snapshotAt` included: a write into the captured state or a
@@ -7952,7 +7284,7 @@ function defineSnapshotModel(model) {
7952
7284
  function captureAggregateSnapshot(model, aggregate, snapshotAt) {
7953
7285
  assertSnapshotModel(model);
7954
7286
  const recordedAt = copySnapshotAt(snapshotAt);
7955
- const state = detachState(model.capture(aggregate));
7287
+ const state = copyPlainState(model.capture(aggregate));
7956
7288
  return deepFreeze({
7957
7289
  state,
7958
7290
  version: aggregate.version,
@@ -7991,8 +7323,8 @@ function reconstituteAggregateFromSnapshot(model, id, snapshot) {
7991
7323
  let aggregate;
7992
7324
  try {
7993
7325
  let state;
7994
- if (storedSchemaVersion === model.schemaVersion) state = detachState(snapshot.state);
7995
- else if (model.migrate) state = detachState(model.migrate(detachState(snapshot.state), storedSchemaVersion));
7326
+ if (storedSchemaVersion === model.schemaVersion) state = copyPlainState(snapshot.state);
7327
+ else if (model.migrate) state = copyPlainState(model.migrate(copyPlainState(snapshot.state), storedSchemaVersion));
7996
7328
  else throw new SnapshotSchemaMismatchError({
7997
7329
  identity: {
7998
7330
  aggregateType: model.aggregateType,
@@ -8031,5 +7363,5 @@ function copySnapshotAt(snapshotAt) {
8031
7363
  }
8032
7364
 
8033
7365
  //#endregion
8034
- export { AggregateDeletedError, AggregateNotFoundError, AggregateTrackingError, CapabilityRegistryConflictError, CommandBus, CommitError, ConcurrencyConflictError, DeadlineProcessor, DirectStateMutationError, DomainError, DomainEventValidationError, DomainStateMachine, DomainTransitionGuardRejectedError, DuplicateAggregateError, DuplicateEventIdError, DuplicateHandlerRegistrationError, Entity, ErrorMapperFailedError, EventBusClosedError, EventBusImpl, EventHarvestError, EventSourcedAggregate, FoldReturnedNoStateError, ForeignEventError, HostileStateKeyError, IdempotencyClaimLostError, IdempotencyCompletionWithoutClaimError, IdempotencyInFlightError, IdempotencyKeyReuseError, IdempotencyReconciliationRequiredError, IdentityMap, InMemoryCapacityExceededError, InMemoryDeadlineStore, InMemoryEventStore, InMemoryIdempotencyStore, InMemoryOutbox, InMemoryProjectionCheckpointStore, InMemorySnapshotStore, InMemoryTransactionScope, InfrastructureError, InvalidCommandMessageError, InvalidDomainMachineContextError, InvalidDomainMachineDefinitionError, InvalidDomainMachineInputError, InvalidDomainMachineSnapshotError, InvalidDomainTransitionError, InvalidDomainTransitionGuardResultError, InvalidDomainTransitionResultError, InvalidEventStreamPageError, InvalidFlushStatementError, InvalidIntegrationMessageError, InvalidRepositoryAdapterError, InvalidRepositoryDefinitionError, InvalidVersionError, MisattributedEventError, MissingEntityIdError, MissingFoldError, MissingHandlerError, NestedUnitOfWorkError, OutboxDispatcher, PendingEventBatchMismatchError, PendingEventLimitExceededError, ProjectionGapError, ProjectionIdentityViolationError, ProjectionOrderViolationError, ProjectionReceiptViolationError, Projector, PublishDepthExceededError, QueryBus, ReentrantDomainStateMachineEvaluationError, ReentrantEventRecordingError, ReplayRejectedError, ReplayTargetMismatchError, RepositoryErrorMappingFailedError, RetryingTransactionScope, RollbackError, SnapshotCorruptedError, SnapshotSchemaMismatchError, SnapshotTimeValidationError, SnapshotVersionNotRestoredError, Specification, StateStoredAggregate, TransactionClosedError, UnenrolledChangesError, UnitOfWork, UnmanagedInstanceError, UnmintedEventError, UnprojectableEventError, UnregisteredHandlerError, UnreplayableAggregateError, ValueObject, analyzeDomainMachineDefinition, canTransitionDomainState, captureAggregateSnapshot, capturePersistenceBaseline, classifyConcurrencyConflict, copyMetadata, createDomainEvent, createDomainEventFactory, createDomainEventFromFacts, createInitialDomainMachineSnapshot, createIntegrationMessage, createUncommittedDomainEvent, decodeIntegrationMessage, deepEqual, deepEqualExcept, deepFreeze, deepOmit, defaultDomainEventFactory, defineRepository, defineSnapshotModel, derivePersistenceChanges, detachState, domainErrorToResult, encodeIntegrationMessage, entityIds, eventBusSink, findEntityById, freezeShallow, hasEntityId, ignoreProjectionEvent, insertPersistenceBaseline, integrationMessageToCommittedEvent, isDomainErrorLike, isInfrastructureErrorLike, isPositionAfter, isWiringErrorLike, mergeMetadata, outboxWriterAcceptingEventLoss, persistenceProjectionDrifted, pinTargetVersion, prepareDomainMachineDefinition, projectionFromHandlers, readStreamPages, recapturePersistenceBaseline, reconstituteAggregateFromHistory, reconstituteAggregateFromSnapshot, reconstituteAggregateFromStreamPages, recordDomainEvent, recordPendingEvents, removeEntityById, replaceEntityById, routeEventsToCommandOutbox, sameEntity, sameVersion, specification, toVersion, transitionDomainState, updateEntityById, versionedFlush, vo, voEquals, voEqualsExcept, voValidated, voWithValidation, withCommit, withIdempotentCommit };
7366
+ export { AggregateDeletedError, AggregateNotFoundError, AggregateTrackingError, CapabilityRegistryConflictError, CommandBus, CommitError, ConcurrencyConflictError, DeadlineProcessor, DirectStateMutationError, DomainError, DomainEventValidationError, DuplicateAggregateError, DuplicateEventIdError, DuplicateHandlerRegistrationError, Entity, ErrorMapperFailedError, EventBusClosedError, EventCollectionError, EventSourcedAggregate, FoldReturnedNoStateError, ForeignEventError, HandBuiltEventError, IdempotencyClaimLostError, IdempotencyCompletionWithoutClaimError, IdempotencyInFlightError, IdempotencyKeyReuseError, IdempotencyReconciliationRequiredError, IdentityMap, InMemoryCapacityExceededError, InMemoryDeadlineStore, InMemoryEventStore, InMemoryIdempotencyStore, InMemoryOutbox, InMemoryProjectionCheckpointStore, InMemorySnapshotStore, InMemoryTransactionScope, InProcessEventBus, InfrastructureError, InvalidCommandMessageError, InvalidEventStreamPageError, InvalidFlushStatementError, InvalidIntegrationMessageError, InvalidRepositoryAdapterError, InvalidRepositoryDefinitionError, InvalidVersionError, MisattributedEventError, MissingEntityIdError, MissingFoldError, MissingHandlerError, NestedUnitOfWorkError, OutboxDispatcher, PendingEventBatchMismatchError, PendingEventLimitExceededError, ProjectionGapError, ProjectionIdentityViolationError, ProjectionOrderViolationError, ProjectionReceiptViolationError, Projector, PrototypePollutionError, PublishDepthExceededError, QueryBus, ReentrantEventRecordingError, ReplayRejectedError, ReplayTargetMismatchError, RepositoryErrorMappingFailedError, RetryingTransactionScope, RollbackError, SnapshotCorruptedError, SnapshotSchemaMismatchError, SnapshotTimeValidationError, SnapshotVersionNotRestoredError, Specification, StateStoredAggregate, TransactionClosedError, UnitOfWork, UnmanagedInstanceError, UnprojectableEventError, UnregisteredChangesError, UnregisteredHandlerError, UnreplayableAggregateError, ValueObject, 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 };
8035
7367
  //# sourceMappingURL=index.js.map