@vytches/ddd-aggregates 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const T=require("/home/runner/work/ddd/ddd/packages/logging/dist/index.js"),c=require("/home/runner/work/ddd/ddd/packages/domain-primitives/dist/index.js");function D(){return globalThis.crypto.randomUUID()}function I(a,t,e){return{eventName:a,payload:t,metadata:{eventId:D(),timestamp:new Date,...e}}}class p{isType(t){return this.type===t}static get capabilityType(){throw new Error("Capability subclass must override static capabilityType getter")}}class ${constructor(){this.capabilities=new Map}register(t){return this.capabilities.set(t.type,t),this}get(t){const e=t.capabilityType;return this.capabilities.get(e)}getByType(t){return this.capabilities.get(t)}has(t){const e=t.capabilityType;return this.capabilities.has(e)}hasByType(t){return this.capabilities.has(t)}remove(t){const e=t.capabilityType;return this.capabilities.delete(e)}removeByType(t){return this.capabilities.delete(t)}getAll(){return Array.from(this.capabilities.values())}getTypes(){return Array.from(this.capabilities.keys())}clear(){this.capabilities.clear()}get size(){return this.capabilities.size}}class g{constructor(t,e="text"){this.value=t,this.type=e}getValue(){return this.value}getType(){return this.type}validate(t){return t!=null&&t!==void 0}equals(t){return t.getValue()===this.value&&t.getType()===this.type}isType(t){return this.type===t}toString(){return String(this.value)}toJSON(){return JSON.stringify({value:this.value,type:this.type})}static create(){const t="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const i=Math.random()*16|0;return(e==="x"?i:i&3|8).toString(16)});return new g(t,"uuid")}static createText(t){return new g(t,"text")}static createUuid(t){return new g(t,"uuid")}static createInteger(t){return new g(t.toString(),"integer")}static createBigInt(t){const e=typeof t=="bigint"?t.toString():t;return new g(e,"bigint")}static createWithRandomUUID(){return g.create()}static fromUUID(t){if(!t||typeof t!="string")throw new Error("entity identifier must be provided");if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(t))throw new Error("entity identifier must be valid UUID");return new g(t,"uuid")}static fromInteger(t){if(typeof t!="number"||t<0||!Number.isInteger(t))throw new Error("entity identifier must be a non-negative integer");return new g(t.toString(),"integer")}static fromBigInt(t){const e=typeof t=="bigint"?t.toString():t;if(!e||typeof e!="string")throw new Error("entity identifier must be a valid bigint");return new g(e,"bigint")}static fromText(t){if(!t||typeof t!="string")throw new Error("entity identifier must be provided");if(!/^[a-zA-Z0-9_-]+$/.test(t))throw new Error("entity identifier contains invalid characters");return new g(t,"text")}}class o{constructor(t,e,i){this._isSuccess=t,this._value=e,this._error=i}get isSuccess(){return this._isSuccess}get isFailure(){return!this._isSuccess}get value(){if(!this._isSuccess)throw new Error("Cannot get value of a failure result");return this._value}get error(){if(this._isSuccess)throw new Error("Cannot get error of a success result");return this._error}static ok(t){return new o(!0,t)}static empty(){return new o(!0,void 0)}static fail(t){return new o(!1,void 0,t)}static try(t){try{return o.ok(t())}catch(e){return o.fail(e instanceof Error?e:new Error(String(e)))}}map(t){return this.isFailure?o.fail(this.error):o.ok(t(this.value))}flatMap(t){return this.isFailure?o.fail(this.error):t(this.value)}match(t,e){return this.isSuccess?t(this.value):e(this.error)}tap(t){return this.isSuccess&&t(this.value),this}tapError(t){return this.isFailure&&t(this.error),this}mapError(t){return this.isSuccess?o.ok(this.value):o.fail(t(this.error))}static async tryAsync(t){try{const e=await t();return o.ok(e)}catch(e){return o.fail(e instanceof Error?e:new Error(String(e)))}}async mapAsync(t){if(this.isFailure)return o.fail(this.error);try{const e=await t(this.value);return o.ok(e)}catch(e){const i=e instanceof Error?e:new Error(String(e));return o.fail(i)}}async flatMapAsync(t){return this.isFailure?o.fail(this.error):await t(this.value)}}class d extends p{constructor(){super(...arguments),this.type="versioning",this.upcasters=new Map}static get capabilityType(){return"versioning"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.upcasters.clear()}registerUpcaster(t,e,i){this.upcasters.has(t)||this.upcasters.set(t,new Map),this.upcasters.get(t).set(e,i)}handleVersionedEvent(t,e){const i=t.metadata?.version||1,r=t.metadata?.targetVersion||i;let s=t;if(i<r&&this.upcasters.has(t.eventName)){const h=this.upcasters.get(t.eventName),m=T.Logger.forContext("VersioningCapability");for(let l=i;l<r;l++){const S=h.get(l);if(!S){m.warn("Missing upcaster for event version — event will be processed without upcast",{eventName:t.eventName,fromVersion:l,toVersion:l+1});continue}const _=S.upcast(s.payload,s.metadata);s={...s,payload:_,metadata:{...s.metadata,version:l+1}}}}const u=e.get(s.eventName);u&&u(s.payload,s.metadata)}getRegisteredEventTypes(){return Array.from(this.upcasters.keys())}hasUpcaster(t,e){return this.upcasters.get(t)?.has(e)||!1}getUpcastersForType(t){return this.upcasters.get(t)}clearUpcastersForType(t){this.upcasters.delete(t)}getTotalUpcasterCount(){let t=0;for(const e of this.upcasters.values())t+=e.size;return t}}class f{constructor(t){this._version=0,this._initialVersion=0,this._domainEvents=[],this._eventHandlers=new Map,this._capabilities=new $,this._id=t.id,this._version=t.version||0,this._initialVersion=t.version||0,this._maxEvents=t.maxEvents}getId(){return this._id}getVersion(){return this._version}getInitialVersion(){return this._initialVersion}hasChanges(){return this._domainEvents.length>0}getDomainEvents(){return[...this._domainEvents]}commit(){this._domainEvents=[],this._initialVersion=this._version}registerEventHandler(t,e){return this._eventHandlers.set(t,e),this}apply(t,e,i){this._version++;let r,s;typeof t=="string"?(r=I(t,e,i),s=r.metadata):(r=t,s=i?{...r.metadata,...i}:r.metadata);const u=f.sanitizeMetadata({...s,aggregateId:this._id.toString(),aggregateType:this.constructor.name,aggregateVersion:this._version,timestamp:s?.timestamp??new Date}),h=Object.assign(Object.create(Object.getPrototypeOf(r)),r,{metadata:u});if(this._maxEvents!==void 0&&this._domainEvents.length>=this._maxEvents)throw new Error(`Aggregate ${this.constructor.name} (id=${this._id.toString()}) exceeded maxEvents limit of ${this._maxEvents}. This usually indicates a runaway loop or replay of a corrupted event stream. Increase maxEvents in the aggregate constructor params if the limit is too restrictive for your domain.`);this._domainEvents.push(h),this.handleEvent(h)}handleEvent(t){const e=this.getCapability(d);if(e)e.handleVersionedEvent(t,this._eventHandlers);else{const i=this._eventHandlers.get(t.eventName);i&&i(t.payload,t.metadata)}}loadFromHistory(t){this._version=this._initialVersion,this._domainEvents=[];for(const e of t)this.handleEvent(e),this._version++;this._initialVersion=this._version}addCapability(t){return"attach"in t&&typeof t.attach=="function"&&t.attach(this),this._capabilities.register(t),this}getCapability(t){return this._capabilities.get(t)}hasCapability(t){return this._capabilities.has(t)}removeCapability(t){const e=this._capabilities.get(t);return e&&"detach"in e&&typeof e.detach=="function"&&e.detach(),this._capabilities.remove(t),this}getAllCapabilities(){return this._capabilities.getAll()}getCapabilityTypes(){return this._capabilities.getTypes()}static sanitizeMetadata(t){if(!Object.prototype.hasOwnProperty.call(t,"__proto__")&&!Object.prototype.hasOwnProperty.call(t,"constructor")&&!Object.prototype.hasOwnProperty.call(t,"prototype"))return t;const e={};for(const i of Object.keys(t))i==="__proto__"||i==="constructor"||i==="prototype"||(e[i]=t[i]);return e}_internal_setState(t){this._version=t.version,this._initialVersion=t.initialVersion,this._domainEvents=[...t.domainEvents]}}class y extends p{constructor(){super(...arguments),this.type="audit",this.auditLog=[]}static get capabilityType(){return"audit"}attach(t){this.aggregate=t;const e=this.aggregate;typeof e.apply=="function"&&(this.originalApply=e.apply,this.originalApply&&(this.aggregate.apply=(...i)=>{const r=this.originalApply.call(this.aggregate,...i);return this.recordAuditEvent(),r}))}detach(){this.aggregate&&this.originalApply&&(this.aggregate.apply=this.originalApply),this.aggregate=void 0,this.auditLog=[],this.originalApply=void 0}getAuditLog(){return[...this.auditLog]}clearAuditLog(){this.auditLog=[]}getAuditStatistics(){const t={};let e=0;this.auditLog.forEach((r,s)=>{if(t[r.eventName]=(t[r.eventName]||0)+1,s>0){const u=this.auditLog[s-1];if(u){const h=new Date(u.timestamp).getTime(),m=new Date(r.timestamp).getTime();e+=m-h}}});const i=this.auditLog.length>1?e/(this.auditLog.length-1):0;return{totalEvents:this.auditLog.length,eventsByType:t,averageTimeBetweenEvents:i}}recordAuditEvent(){const t=this.aggregate.getDomainEvents(),e=t[t.length-1];e&&this.recordEvent(e)}recordEvent(t){const e={eventId:t.metadata?.eventId||`audit-${Date.now()}-${Math.random()}`,eventName:t.eventName,aggregateId:this.aggregate.getId().toString(),aggregateType:this.aggregate.constructor.name,aggregateVersion:this.aggregate.getVersion(),timestamp:new Date,payload:t.payload,metadata:{...t.metadata,auditCapability:!0},actor:t.metadata?.userId};this.auditLog.push(e)}getEventsByName(t){return this.auditLog.filter(e=>e.eventName===t)}getEventsByTimeRange(t,e){return this.auditLog.filter(i=>{const r=new Date(i.timestamp).getTime();return r>=t.getTime()&&r<=e.getTime()})}getFirstEvent(){return this.auditLog[0]||null}getLastEvent(){return this.auditLog[this.auditLog.length-1]||null}}class n extends c.IDomainError{static invalidArguments(t,e){const i={code:c.DomainErrorCode.InvalidParameter,data:e};return new n(t,i)}static versionConflict(t,e,i,r){const s=`Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${r}`,u={code:c.DomainErrorCode.ValidationFailed,data:{aggregateType:t,aggregateId:e,currentVersion:i,expectedVersion:r}};return new n(s,u)}static featureNotEnabled(t){const e=`Feature '${t}' is not enabled on aggregate`,i={code:c.DomainErrorCode.ValidationFailed,data:{feature:t}};return new n(e,i)}static methodNotImplemented(t,e){const i=`Method '${t}' must be implemented by ${e} to use this feature`,r={code:c.DomainErrorCode.MissingValue,data:{methodName:t,aggregateType:e}};return new n(i,r)}static invalidSnapshot(t,e){const i=`Invalid snapshot for aggregate ${t}${e?`: ${e}`:""}`,r={code:c.DomainErrorCode.InvalidFormat,data:{aggregateType:t,reason:e}};return new n(i,r)}static idMismatch(t,e){const i=`ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`,r={code:c.DomainErrorCode.ValidationFailed,data:{snapshotId:t,aggregateId:e}};return new n(i,r)}static typeMismatch(t,e){const i=`Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`,r={code:c.DomainErrorCode.ValidationFailed,data:{snapshotType:t,aggregateType:e}};return new n(i,r)}static duplicateUpcaster(t,e){const i=`Upcaster for event ${t} version ${e} already exists`,r={code:c.DomainErrorCode.DuplicateEntry,data:{eventType:t,sourceVersion:e}};return new n(i,r)}static missingUpcaster(t,e,i){const r=`Missing upcaster for event ${t} from version ${e} to ${i}`,s={code:c.DomainErrorCode.MissingValue,data:{eventType:t,fromVersion:e,toVersion:i}};return new n(r,s)}static configurationError(t,e){const i=`Configuration error in ${t}: ${e}`,r={code:c.DomainErrorCode.ValidationFailed,data:{aggregateType:t,configurationError:e}};return new n(i,r)}static cannotInterceptApplyMethod(t,e){const i=`Cannot intercept apply method - method not found on aggregate ${t}`,r={code:c.DomainErrorCode.InternalError,data:{aggergateId:t,...e}};return new n(i,r)}static eventStoreNotConfigured(t){const e="Event store not configured",i={code:c.DomainErrorCode.MissingValue,data:t};return new n(e,i)}static aggregateDoesNotSupportReplay(t,e){const i=`Aggregate ${t} does not support event replay`,r={code:c.DomainErrorCode.MethodNotSupported,data:{aggregateType:t,...e}};return new n(i,r)}}class b extends p{constructor(){super(...arguments),this.type="eventSourcing",this.eventStore=null}static get capabilityType(){return"eventSourcing"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.eventStore=null}setEventStore(t){this.eventStore=t}getEventStore(){return this.eventStore}async loadFromEventStore(t){if(!this.eventStore)throw n.configurationError(this.aggregate.constructor.name,"Event store not configured");const e=await this.eventStore.getEvents(t);if(e.length===0)return;const i=this.aggregate;i.loadFromHistory&&i.loadFromHistory(e)}async saveToEventStore(){if(!this.eventStore)throw n.configurationError(this.aggregate.constructor.name,"Event store not configured");const t=this.aggregate.getDomainEvents();t.length!==0&&await this.eventStore.saveEvents(this.aggregate.getId().toString(),[...t],this.aggregate.getInitialVersion())}hasEventStore(){return this.eventStore!==null}getStreamName(){return`${this.aggregate.constructor.name}-${this.aggregate.getId().toString()}`}async loadFromVersion(t,e){if(!this.eventStore)throw n.configurationError(this.aggregate.constructor.name,"Event store not configured");const i=await this.eventStore.getEventsAfterVersion(t,e),r=this.aggregate;r.loadFromHistory&&i.length>0&&r.loadFromHistory(i)}}class w extends p{constructor(){super(...arguments),this.type="snapshot",this._snapshot=null}static get capabilityType(){return"snapshot"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this._snapshot=null}createSnapshot(t,e){const i=this.aggregate.getDomainEvents(),s=(i.length>0?i[i.length-1]:null)?.metadata?.eventId,h={aggregateId:this.aggregate.getId().toString(),version:this.aggregate.getVersion(),aggregateType:this.aggregate.constructor.name,state:t(),timestamp:new Date,...s!==void 0&&{lastEventId:s}};return e&&(h.metadata=e()),h}restoreFromSnapshot(t,e,i){if(!t||!t.state)throw n.invalidSnapshot(this.aggregate.constructor.name,"Invalid snapshot");const r=this.aggregate.getId().toString();if(t.aggregateId!==r)throw n.idMismatch(t.aggregateId,r);if(t.aggregateType!==this.aggregate.constructor.name)throw n.typeMismatch(t.aggregateType,this.aggregate.constructor.name);e(t.state),t.metadata&&i&&i(t.metadata);const s=this.aggregate;s._internal_setState&&s._internal_setState({version:t.version,initialVersion:t.version,domainEvents:[]})}saveTemporaryState(t){this._snapshot=this.createSnapshot(()=>t)}getLastSnapshotTimestamp(){return this._snapshot?.timestamp||null}saveSnapshot(t,e){this._snapshot=this.createSnapshot(t,e)}getPreviousState(){const t=this._snapshot;return this._snapshot=null,t}}class v{constructor(t){this.capabilities=[],this.params=t}static create(t){const e={id:t.id instanceof g?t.id:new g(t.id,"text"),version:t.version||0};return new v(e)}withSnapshots(){return this.capabilities.push({capability:new w}),this}withVersioning(){return this.capabilities.push({capability:new d}),this}withAudit(){return this.capabilities.push({capability:new y}),this}withEventSourcing(t){const e=new b;return this.capabilities.push({capability:e,configure:i=>{const r=i;t?r.setEventStore(t):this.eventStore&&r.setEventStore(this.eventStore)}}),this}withCapability(t,e){return this.capabilities.push({capability:t,configure:e?i=>e(i):void 0}),this}setEventStore(t){return this.eventStore=t,this}build(t){const e=t||f,i=new e(this.params);for(const{capability:r,configure:s}of this.capabilities)i.addCapability(r),s&&s(r);return i}buildWithAllCapabilities(t){return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t)}}function N(a){return v.create(a)}class E{constructor(t){this._id=t}getId(){return this._id}equals(t){return t==null?!1:t===this?!0:t instanceof E?this._id.equals(t._id):!1}}function A(a){return a.hasCapability(w)}function C(a){return a.hasCapability(d)}function x(a){return a.hasCapability(y)}function V(a){return a.hasCapability(b)}function M(a){if(!A(a))throw n.featureNotEnabled("snapshot");return a}function F(a){return A(a)?o.ok(a):o.fail(n.featureNotEnabled("snapshot"))}function L(a){if(!C(a))throw n.featureNotEnabled("versioning");return a}function U(a){return C(a)?o.ok(a):o.fail(n.featureNotEnabled("versioning"))}function k(a){if(!x(a))throw n.featureNotEnabled("audit");return a}function B(a){return x(a)?o.ok(a):o.fail(n.featureNotEnabled("audit"))}function O(a){if(!V(a))throw n.featureNotEnabled("eventSourcing");return a}function j(a){return V(a)?o.ok(a):o.fail(n.featureNotEnabled("eventSourcing"))}function H(a){return a.getCapabilityTypes()}function P(a,t){return t.every(e=>a.hasCapability(e))}exports.AggregateBuilder=v;exports.AggregateError=n;exports.AggregateRoot=f;exports.AuditCapability=y;exports.Entity=E;exports.EventSourcingCapability=b;exports.SnapshotCapability=w;exports.VersioningCapability=d;exports.aggregateBuilder=N;exports.asAuditAggregate=k;exports.asEventSourcingAggregate=O;exports.asSnapshotAggregate=M;exports.asVersioningAggregate=L;exports.getAggregateCapabilities=H;exports.hasAllCapabilities=P;exports.tryAsAuditAggregate=B;exports.tryAsEventSourcingAggregate=j;exports.tryAsSnapshotAggregate=F;exports.tryAsVersioningAggregate=U;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("@vytches/ddd-contracts"),D=require("@vytches/ddd-logging"),o=require("@vytches/ddd-domain-primitives"),u=require("@vytches/ddd-utils");class d extends h.Capability{constructor(){super(...arguments),this.type="versioning",this.upcasters=new Map}static get capabilityType(){return"versioning"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.upcasters.clear()}registerUpcaster(t,e,i){this.upcasters.has(t)||this.upcasters.set(t,new Map),this.upcasters.get(t).set(e,i)}handleVersionedEvent(t,e){const i=t.metadata?.version||1,a=t.metadata?.targetVersion||i;let n=t;if(i<a&&this.upcasters.has(t.eventName)){const c=this.upcasters.get(t.eventName),m=D.Logger.forContext("VersioningCapability");for(let l=i;l<a;l++){const S=c.get(l);if(!S){m.warn("Missing upcaster for event version — event will be processed without upcast",{eventName:t.eventName,fromVersion:l,toVersion:l+1});continue}const V=S.upcast(n.payload,n.metadata);n={...n,payload:V,metadata:{...n.metadata,version:l+1}}}}const g=e.get(n.eventName);g&&g(n.payload,n.metadata)}getRegisteredEventTypes(){return Array.from(this.upcasters.keys())}hasUpcaster(t,e){return this.upcasters.get(t)?.has(e)||!1}getUpcastersForType(t){return this.upcasters.get(t)}clearUpcastersForType(t){this.upcasters.delete(t)}getTotalUpcasterCount(){let t=0;for(const e of this.upcasters.values())t+=e.size;return t}}class p{constructor(t){this._version=0,this._initialVersion=0,this._domainEvents=[],this._eventHandlers=new Map,this._capabilities=new h.CapabilityRegistry,this._id=t.id,this._version=t.version||0,this._initialVersion=t.version||0,this._maxEvents=t.maxEvents}getId(){return this._id}getVersion(){return this._version}getInitialVersion(){return this._initialVersion}hasChanges(){return this._domainEvents.length>0}getDomainEvents(){return[...this._domainEvents]}commit(){this._domainEvents=[],this._initialVersion=this._version}registerEventHandler(t,e){return this._eventHandlers.set(t,e),this}apply(t,e,i){this._version++;let a,n;typeof t=="string"?(a=h.createDomainEvent(t,e,i),n=a.metadata):(a=t,n=i?{...a.metadata,...i}:a.metadata);const g=p.sanitizeMetadata({...n,aggregateId:this._id.toString(),aggregateType:this.constructor.name,aggregateVersion:this._version,timestamp:n?.timestamp??new Date}),c=Object.assign(Object.create(Object.getPrototypeOf(a)),a,{metadata:g});if(this._maxEvents!==void 0&&this._domainEvents.length>=this._maxEvents)throw new Error(`Aggregate ${this.constructor.name} (id=${this._id.toString()}) exceeded maxEvents limit of ${this._maxEvents}. This usually indicates a runaway loop or replay of a corrupted event stream. Increase maxEvents in the aggregate constructor params if the limit is too restrictive for your domain.`);this._domainEvents.push(c),this.handleEvent(c)}handleEvent(t){const e=this.getCapability(d);if(e)e.handleVersionedEvent(t,this._eventHandlers);else{const i=this._eventHandlers.get(t.eventName);i&&i(t.payload,t.metadata)}}loadFromHistory(t){this._version=this._initialVersion,this._domainEvents=[];for(const e of t)this.handleEvent(e),this._version++;this._initialVersion=this._version}addCapability(t){return"attach"in t&&typeof t.attach=="function"&&t.attach(this),this._capabilities.register(t),this}getCapability(t){return this._capabilities.get(t)}hasCapability(t){return this._capabilities.has(t)}removeCapability(t){const e=this._capabilities.get(t);return e&&"detach"in e&&typeof e.detach=="function"&&e.detach(),this._capabilities.remove(t),this}getAllCapabilities(){return this._capabilities.getAll()}getCapabilityTypes(){return this._capabilities.getTypes()}static sanitizeMetadata(t){if(!Object.prototype.hasOwnProperty.call(t,"__proto__")&&!Object.prototype.hasOwnProperty.call(t,"constructor")&&!Object.prototype.hasOwnProperty.call(t,"prototype"))return t;const e={};for(const i of Object.keys(t))i==="__proto__"||i==="constructor"||i==="prototype"||(e[i]=t[i]);return e}_internal_setState(t){this._version=t.version,this._initialVersion=t.initialVersion,this._domainEvents=[...t.domainEvents]}}class f extends h.Capability{constructor(){super(...arguments),this.type="audit",this.auditLog=[]}static get capabilityType(){return"audit"}attach(t){this.aggregate=t;const e=this.aggregate;typeof e.apply=="function"&&(this.originalApply=e.apply,this.originalApply&&(this.aggregate.apply=(...i)=>{const a=this.originalApply.call(this.aggregate,...i);return this.recordAuditEvent(),a}))}detach(){this.aggregate&&this.originalApply&&(this.aggregate.apply=this.originalApply),this.aggregate=void 0,this.auditLog=[],this.originalApply=void 0}getAuditLog(){return[...this.auditLog]}clearAuditLog(){this.auditLog=[]}getAuditStatistics(){const t={};let e=0;this.auditLog.forEach((a,n)=>{if(t[a.eventName]=(t[a.eventName]||0)+1,n>0){const g=this.auditLog[n-1];if(g){const c=new Date(g.timestamp).getTime(),m=new Date(a.timestamp).getTime();e+=m-c}}});const i=this.auditLog.length>1?e/(this.auditLog.length-1):0;return{totalEvents:this.auditLog.length,eventsByType:t,averageTimeBetweenEvents:i}}recordAuditEvent(){const t=this.aggregate.getDomainEvents(),e=t[t.length-1];e&&this.recordEvent(e)}recordEvent(t){const e={eventId:t.metadata?.eventId||`audit-${Date.now()}-${Math.random()}`,eventName:t.eventName,aggregateId:this.aggregate.getId().toString(),aggregateType:this.aggregate.constructor.name,aggregateVersion:this.aggregate.getVersion(),timestamp:new Date,payload:t.payload,metadata:{...t.metadata,auditCapability:!0},actor:t.metadata?.userId};this.auditLog.push(e)}getEventsByName(t){return this.auditLog.filter(e=>e.eventName===t)}getEventsByTimeRange(t,e){return this.auditLog.filter(i=>{const a=new Date(i.timestamp).getTime();return a>=t.getTime()&&a<=e.getTime()})}getFirstEvent(){return this.auditLog[0]||null}getLastEvent(){return this.auditLog[this.auditLog.length-1]||null}}class r extends o.IDomainError{static invalidArguments(t,e){const i={code:o.DomainErrorCode.InvalidParameter,data:e};return new r(t,i)}static versionConflict(t,e,i,a){const n=`Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${a}`,g={code:o.DomainErrorCode.ValidationFailed,data:{aggregateType:t,aggregateId:e,currentVersion:i,expectedVersion:a}};return new r(n,g)}static featureNotEnabled(t){const e=`Feature '${t}' is not enabled on aggregate`,i={code:o.DomainErrorCode.ValidationFailed,data:{feature:t}};return new r(e,i)}static methodNotImplemented(t,e){const i=`Method '${t}' must be implemented by ${e} to use this feature`,a={code:o.DomainErrorCode.MissingValue,data:{methodName:t,aggregateType:e}};return new r(i,a)}static invalidSnapshot(t,e){const i=`Invalid snapshot for aggregate ${t}${e?`: ${e}`:""}`,a={code:o.DomainErrorCode.InvalidFormat,data:{aggregateType:t,reason:e}};return new r(i,a)}static idMismatch(t,e){const i=`ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{snapshotId:t,aggregateId:e}};return new r(i,a)}static typeMismatch(t,e){const i=`Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{snapshotType:t,aggregateType:e}};return new r(i,a)}static duplicateUpcaster(t,e){const i=`Upcaster for event ${t} version ${e} already exists`,a={code:o.DomainErrorCode.DuplicateEntry,data:{eventType:t,sourceVersion:e}};return new r(i,a)}static missingUpcaster(t,e,i){const a=`Missing upcaster for event ${t} from version ${e} to ${i}`,n={code:o.DomainErrorCode.MissingValue,data:{eventType:t,fromVersion:e,toVersion:i}};return new r(a,n)}static configurationError(t,e){const i=`Configuration error in ${t}: ${e}`,a={code:o.DomainErrorCode.ValidationFailed,data:{aggregateType:t,configurationError:e}};return new r(i,a)}static cannotInterceptApplyMethod(t,e){const i=`Cannot intercept apply method - method not found on aggregate ${t}`,a={code:o.DomainErrorCode.InternalError,data:{aggergateId:t,...e}};return new r(i,a)}static eventStoreNotConfigured(t){const e="Event store not configured",i={code:o.DomainErrorCode.MissingValue,data:t};return new r(e,i)}static aggregateDoesNotSupportReplay(t,e){const i=`Aggregate ${t} does not support event replay`,a={code:o.DomainErrorCode.MethodNotSupported,data:{aggregateType:t,...e}};return new r(i,a)}}class y extends h.Capability{constructor(){super(...arguments),this.type="eventSourcing",this.eventStore=null}static get capabilityType(){return"eventSourcing"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this.eventStore=null}setEventStore(t){this.eventStore=t}getEventStore(){return this.eventStore}async loadFromEventStore(t){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const e=await this.eventStore.getEvents(t);if(e.length===0)return;const i=this.aggregate;i.loadFromHistory&&i.loadFromHistory(e)}async saveToEventStore(){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const t=this.aggregate.getDomainEvents();t.length!==0&&await this.eventStore.saveEvents(this.aggregate.getId().toString(),[...t],this.aggregate.getInitialVersion())}hasEventStore(){return this.eventStore!==null}getStreamName(){return`${this.aggregate.constructor.name}-${this.aggregate.getId().toString()}`}async loadFromVersion(t,e){if(!this.eventStore)throw r.configurationError(this.aggregate.constructor.name,"Event store not configured");const i=await this.eventStore.getEventsAfterVersion(t,e),a=this.aggregate;a.loadFromHistory&&i.length>0&&a.loadFromHistory(i)}}class E extends h.Capability{constructor(){super(...arguments),this.type="snapshot",this._snapshot=null}static get capabilityType(){return"snapshot"}attach(t){this.aggregate=t}detach(){this.aggregate=void 0,this._snapshot=null}createSnapshot(t,e){const i=this.aggregate.getDomainEvents(),n=(i.length>0?i[i.length-1]:null)?.metadata?.eventId,c={aggregateId:this.aggregate.getId().toString(),version:this.aggregate.getVersion(),aggregateType:this.aggregate.constructor.name,state:t(),timestamp:new Date,...n!==void 0&&{lastEventId:n}};return e&&(c.metadata=e()),c}restoreFromSnapshot(t,e,i){if(!t||!t.state)throw r.invalidSnapshot(this.aggregate.constructor.name,"Invalid snapshot");const a=this.aggregate.getId().toString();if(t.aggregateId!==a)throw r.idMismatch(t.aggregateId,a);if(t.aggregateType!==this.aggregate.constructor.name)throw r.typeMismatch(t.aggregateType,this.aggregate.constructor.name);e(t.state),t.metadata&&i&&i(t.metadata);const n=this.aggregate;n._internal_setState&&n._internal_setState({version:t.version,initialVersion:t.version,domainEvents:[]})}saveTemporaryState(t){this._snapshot=this.createSnapshot(()=>t)}getLastSnapshotTimestamp(){return this._snapshot?.timestamp||null}saveSnapshot(t,e){this._snapshot=this.createSnapshot(t,e)}getPreviousState(){const t=this._snapshot;return this._snapshot=null,t}}class v{constructor(t){this.capabilities=[],this.params=t}static create(t){const e={id:t.id instanceof h.EntityId?t.id:new h.EntityId(t.id,"text"),version:t.version||0};return new v(e)}withSnapshots(){return this.capabilities.push({capability:new E}),this}withVersioning(){return this.capabilities.push({capability:new d}),this}withAudit(){return this.capabilities.push({capability:new f}),this}withEventSourcing(t){const e=new y;return this.capabilities.push({capability:e,configure:i=>{const a=i;t?a.setEventStore(t):this.eventStore&&a.setEventStore(this.eventStore)}}),this}withCapability(t,e){return this.capabilities.push({capability:t,configure:e?i=>e(i):void 0}),this}setEventStore(t){return this.eventStore=t,this}build(t){const e=t||p,i=new e(this.params);for(const{capability:a,configure:n}of this.capabilities)i.addCapability(a),n&&n(a);return i}buildWithAllCapabilities(t){return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t)}}function I(s){return v.create(s)}class b{constructor(t){this._id=t}getId(){return this._id}equals(t){return t==null?!1:t===this?!0:t instanceof b?this._id.equals(t._id):!1}}function w(s){return s.hasCapability(E)}function C(s){return s.hasCapability(d)}function _(s){return s.hasCapability(f)}function A(s){return s.hasCapability(y)}function $(s){if(!w(s))throw r.featureNotEnabled("snapshot");return s}function N(s){return w(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("snapshot"))}function L(s){if(!C(s))throw r.featureNotEnabled("versioning");return s}function M(s){return C(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("versioning"))}function T(s){if(!_(s))throw r.featureNotEnabled("audit");return s}function F(s){return _(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("audit"))}function x(s){if(!A(s))throw r.featureNotEnabled("eventSourcing");return s}function R(s){return A(s)?u.Result.ok(s):u.Result.fail(r.featureNotEnabled("eventSourcing"))}function U(s){return s.getCapabilityTypes()}function H(s,t){return t.every(e=>s.hasCapability(e))}exports.AggregateBuilder=v;exports.AggregateError=r;exports.AggregateRoot=p;exports.AuditCapability=f;exports.Entity=b;exports.EventSourcingCapability=y;exports.SnapshotCapability=E;exports.VersioningCapability=d;exports.aggregateBuilder=I;exports.asAuditAggregate=T;exports.asEventSourcingAggregate=x;exports.asSnapshotAggregate=$;exports.asVersioningAggregate=L;exports.getAggregateCapabilities=U;exports.hasAllCapabilities=H;exports.tryAsAuditAggregate=F;exports.tryAsEventSourcingAggregate=R;exports.tryAsSnapshotAggregate=N;exports.tryAsVersioningAggregate=M;
package/dist/index.js CHANGED
@@ -1,403 +1,8 @@
1
- import { Logger as _ } from "/home/runner/work/ddd/ddd/packages/logging/dist/index.js";
2
- import { IDomainError as I, DomainErrorCode as c } from "/home/runner/work/ddd/ddd/packages/domain-primitives/dist/index.js";
3
- function $() {
4
- return globalThis.crypto.randomUUID();
5
- }
6
- function N(s, t, e) {
7
- return {
8
- eventName: s,
9
- payload: t,
10
- metadata: {
11
- eventId: $(),
12
- timestamp: /* @__PURE__ */ new Date(),
13
- ...e
14
- }
15
- };
16
- }
17
- class p {
18
- /**
19
- * Checks if this capability is of a specific type
20
- */
21
- isType(t) {
22
- return this.type === t;
23
- }
24
- /**
25
- * Static type getter for compile-time type extraction
26
- * Each capability must override this
27
- */
28
- static get capabilityType() {
29
- throw new Error("Capability subclass must override static capabilityType getter");
30
- }
31
- }
32
- class D {
33
- constructor() {
34
- this.capabilities = /* @__PURE__ */ new Map();
35
- }
36
- /**
37
- * Register a capability
38
- */
39
- register(t) {
40
- return this.capabilities.set(t.type, t), this;
41
- }
42
- /**
43
- * Get a capability by its constructor
44
- */
45
- get(t) {
46
- const e = t.capabilityType;
47
- return this.capabilities.get(e);
48
- }
49
- /**
50
- * Get a capability by its type string
51
- */
52
- getByType(t) {
53
- return this.capabilities.get(t);
54
- }
55
- /**
56
- * Check if a capability exists
57
- */
58
- has(t) {
59
- const e = t.capabilityType;
60
- return this.capabilities.has(e);
61
- }
62
- /**
63
- * Check if a capability exists by type
64
- */
65
- hasByType(t) {
66
- return this.capabilities.has(t);
67
- }
68
- /**
69
- * Remove a capability
70
- */
71
- remove(t) {
72
- const e = t.capabilityType;
73
- return this.capabilities.delete(e);
74
- }
75
- /**
76
- * Remove a capability by type
77
- */
78
- removeByType(t) {
79
- return this.capabilities.delete(t);
80
- }
81
- /**
82
- * Get all registered capabilities
83
- */
84
- getAll() {
85
- return Array.from(this.capabilities.values());
86
- }
87
- /**
88
- * Get all capability types
89
- */
90
- getTypes() {
91
- return Array.from(this.capabilities.keys());
92
- }
93
- /**
94
- * Clear all capabilities
95
- */
96
- clear() {
97
- this.capabilities.clear();
98
- }
99
- /**
100
- * Get the number of registered capabilities
101
- */
102
- get size() {
103
- return this.capabilities.size;
104
- }
105
- }
106
- class u {
107
- constructor(t, e = "text") {
108
- this.value = t, this.type = e;
109
- }
110
- /**
111
- * Returns the stored identifier value
112
- * @returns The identifier value
113
- * @example
114
- * ```typescript
115
- * const id = EntityId.fromText('user-123');
116
- * const value = id.getValue(); // Returns: 'user-123'
117
- * ```
118
- */
119
- getValue() {
120
- return this.value;
121
- }
122
- /**
123
- * Returns the identifier type (text, uuid, integer, bigint)
124
- * @returns The identifier type
125
- * @example
126
- * ```typescript
127
- * const id = EntityId.fromUUID('550e8400-e29b-41d4-a716-446655440000');
128
- * const type = id.getType(); // Returns: 'uuid'
129
- * ```
130
- */
131
- getType() {
132
- return this.type;
133
- }
134
- validate(t) {
135
- return t != null && t !== void 0;
136
- }
137
- /**
138
- * Compares this EntityId with another for equality
139
- * @returns True if entities have same value and type
140
- * @example
141
- * ```typescript
142
- * const id1 = EntityId.fromText('user-123');
143
- * const id2 = EntityId.fromText('user-123');
144
- * const isEqual = id1.equals(id2); // Returns: true
145
- * ```
146
- */
147
- equals(t) {
148
- return t.getValue() === this.value && t.getType() === this.type;
149
- }
150
- isType(t) {
151
- return this.type === t;
152
- }
153
- toString() {
154
- return String(this.value);
155
- }
156
- toJSON() {
157
- return JSON.stringify({
158
- value: this.value,
159
- type: this.type
160
- });
161
- }
162
- /**
163
- * Primary factory method - creates a new EntityId with a randomly generated UUID
164
- * @returns New EntityId with random UUID
165
- * @example
166
- * ```typescript
167
- * const id = EntityId.create();
168
- * console.log(id.getValue()); // Returns: '550e8400-e29b-41d4-a716-446655440000'
169
- * ```
170
- */
171
- static create() {
172
- const t = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => {
173
- const i = Math.random() * 16 | 0;
174
- return (e === "x" ? i : i & 3 | 8).toString(16);
175
- });
176
- return new u(t, "uuid");
177
- }
178
- /**
179
- * @deprecated Use `fromText()` instead. Will be removed in next major version.
180
- */
181
- static createText(t) {
182
- return new u(t, "text");
183
- }
184
- /**
185
- * @deprecated Use `fromUUID()` instead. Will be removed in next major version.
186
- */
187
- static createUuid(t) {
188
- return new u(t, "uuid");
189
- }
190
- /**
191
- * @deprecated Use `fromInteger()` instead. Will be removed in next major version.
192
- */
193
- static createInteger(t) {
194
- return new u(t.toString(), "integer");
195
- }
196
- /**
197
- * @deprecated Use `fromBigInt()` instead. Will be removed in next major version.
198
- */
199
- static createBigInt(t) {
200
- const e = typeof t == "bigint" ? t.toString() : t;
201
- return new u(e, "bigint");
202
- }
203
- /**
204
- * @deprecated Use `create()` instead. Will be removed in next major version.
205
- * Creates a new EntityId with a randomly generated UUID
206
- * @returns New EntityId with random UUID
207
- */
208
- static createWithRandomUUID() {
209
- return u.create();
210
- }
211
- /**
212
- * Creates an EntityId from a UUID string with validation
213
- * @returns EntityId from UUID string
214
- * @example
215
- * ```typescript
216
- * const id = EntityId.fromUUID('550e8400-e29b-41d4-a716-446655440000');
217
- * console.log(id.getType()); // Returns: 'uuid'
218
- * ```
219
- */
220
- static fromUUID(t) {
221
- if (!t || typeof t != "string")
222
- throw new Error("entity identifier must be provided");
223
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(t))
224
- throw new Error("entity identifier must be valid UUID");
225
- return new u(t, "uuid");
226
- }
227
- static fromInteger(t) {
228
- if (typeof t != "number" || t < 0 || !Number.isInteger(t))
229
- throw new Error("entity identifier must be a non-negative integer");
230
- return new u(t.toString(), "integer");
231
- }
232
- static fromBigInt(t) {
233
- const e = typeof t == "bigint" ? t.toString() : t;
234
- if (!e || typeof e != "string")
235
- throw new Error("entity identifier must be a valid bigint");
236
- return new u(e, "bigint");
237
- }
238
- /**
239
- * Creates an EntityId from a text string with validation
240
- * @returns EntityId from text string
241
- * @example
242
- * ```typescript
243
- * const id = EntityId.fromText('user-123');
244
- * console.log(id.getValue()); // Returns: 'user-123'
245
- * ```
246
- */
247
- static fromText(t) {
248
- if (!t || typeof t != "string")
249
- throw new Error("entity identifier must be provided");
250
- if (!/^[a-zA-Z0-9_-]+$/.test(t))
251
- throw new Error("entity identifier contains invalid characters");
252
- return new u(t, "text");
253
- }
254
- }
255
- class o {
256
- constructor(t, e, i) {
257
- this._isSuccess = t, this._value = e, this._error = i;
258
- }
259
- /**
260
- * Check if the result is successful
261
- */
262
- get isSuccess() {
263
- return this._isSuccess;
264
- }
265
- /**
266
- * Check if the result is a failure
267
- */
268
- get isFailure() {
269
- return !this._isSuccess;
270
- }
271
- /**
272
- * Get the success value
273
- * @throws Error if the result is a failure
274
- */
275
- get value() {
276
- if (!this._isSuccess)
277
- throw new Error("Cannot get value of a failure result");
278
- return this._value;
279
- }
280
- /**
281
- * Get the error value
282
- * @throws Error if the result is successful
283
- */
284
- get error() {
285
- if (this._isSuccess)
286
- throw new Error("Cannot get error of a success result");
287
- return this._error;
288
- }
289
- /**
290
- * Create a successful result
291
- * @param value - The success value (required — use Result.empty() for void operations)
292
- */
293
- static ok(t) {
294
- return new o(!0, t);
295
- }
296
- /**
297
- * Create a successful void result (no value).
298
- */
299
- static empty() {
300
- return new o(!0, void 0);
301
- }
302
- /**
303
- * Create a failure result
304
- * @param error - The error value
305
- */
306
- static fail(t) {
307
- return new o(!1, void 0, t);
308
- }
309
- /**
310
- * Try to execute a function and wrap the result
311
- * @param fn - The function to try
312
- * @returns A Result containing the function result or error
313
- */
314
- static try(t) {
315
- try {
316
- return o.ok(t());
317
- } catch (e) {
318
- return o.fail(e instanceof Error ? e : new Error(String(e)));
319
- }
320
- }
321
- /**
322
- * Transform the success value
323
- * @param fn - The transformation function
324
- */
325
- map(t) {
326
- return this.isFailure ? o.fail(this.error) : o.ok(t(this.value));
327
- }
328
- /**
329
- * Transform the success value with a function that returns a Result
330
- * @param fn - The transformation function
331
- */
332
- flatMap(t) {
333
- return this.isFailure ? o.fail(this.error) : t(this.value);
334
- }
335
- /**
336
- * Apply a function based on success or failure
337
- * @param onSuccess - Function to call on success
338
- * @param onFailure - Function to call on failure
339
- */
340
- match(t, e) {
341
- return this.isSuccess ? t(this.value) : e(this.error);
342
- }
343
- /**
344
- * Apply a side effect if the result is successful
345
- * @param fn - The side effect function
346
- */
347
- tap(t) {
348
- return this.isSuccess && t(this.value), this;
349
- }
350
- /**
351
- * Apply a side effect if the result is a failure
352
- * @param fn - The side effect function
353
- */
354
- tapError(t) {
355
- return this.isFailure && t(this.error), this;
356
- }
357
- /**
358
- * Transform the error value if the result is a failure
359
- * @param fn - The error transformation function
360
- */
361
- mapError(t) {
362
- return this.isSuccess ? o.ok(this.value) : o.fail(t(this.error));
363
- }
364
- /**
365
- * Try to execute an async function and wrap the result
366
- * @param fn - The async function to try
367
- * @returns A Promise of a Result containing the function result or error
368
- */
369
- static async tryAsync(t) {
370
- try {
371
- const e = await t();
372
- return o.ok(e);
373
- } catch (e) {
374
- return o.fail(e instanceof Error ? e : new Error(String(e)));
375
- }
376
- }
377
- /**
378
- * Transform the success value with an async function
379
- * @param fn - The async transformation function
380
- */
381
- async mapAsync(t) {
382
- if (this.isFailure)
383
- return o.fail(this.error);
384
- try {
385
- const e = await t(this.value);
386
- return o.ok(e);
387
- } catch (e) {
388
- const i = e instanceof Error ? e : new Error(String(e));
389
- return o.fail(i);
390
- }
391
- }
392
- /**
393
- * Transform the success value with an async function that returns a Result
394
- * @param fn - The async transformation function
395
- */
396
- async flatMapAsync(t) {
397
- return this.isFailure ? o.fail(this.error) : await t(this.value);
398
- }
399
- }
400
- class f extends p {
1
+ import { Capability as l, CapabilityRegistry as $, createDomainEvent as N, EntityId as y } from "@vytches/ddd-contracts";
2
+ import { Logger as L } from "@vytches/ddd-logging";
3
+ import { IDomainError as M, DomainErrorCode as o } from "@vytches/ddd-domain-primitives";
4
+ import { Result as h } from "@vytches/ddd-utils";
5
+ class p extends l {
401
6
  constructor() {
402
7
  super(...arguments), this.type = "versioning", this.upcasters = /* @__PURE__ */ new Map();
403
8
  }
@@ -426,36 +31,36 @@ class f extends p {
426
31
  * @param {Map<string, IAggregateEventHandler>} handlers - Map of event handlers
427
32
  */
428
33
  handleVersionedEvent(t, e) {
429
- const i = t.metadata?.version || 1, r = t.metadata?.targetVersion || i;
430
- let a = t;
431
- if (i < r && this.upcasters.has(t.eventName)) {
432
- const g = this.upcasters.get(t.eventName), d = _.forContext("VersioningCapability");
433
- for (let l = i; l < r; l++) {
434
- const y = g.get(l);
435
- if (!y) {
34
+ const i = t.metadata?.version || 1, a = t.metadata?.targetVersion || i;
35
+ let n = t;
36
+ if (i < a && this.upcasters.has(t.eventName)) {
37
+ const g = this.upcasters.get(t.eventName), d = L.forContext("VersioningCapability");
38
+ for (let u = i; u < a; u++) {
39
+ const f = g.get(u);
40
+ if (!f) {
436
41
  d.warn(
437
42
  "Missing upcaster for event version — event will be processed without upcast",
438
43
  {
439
44
  eventName: t.eventName,
440
- fromVersion: l,
441
- toVersion: l + 1
45
+ fromVersion: u,
46
+ toVersion: u + 1
442
47
  }
443
48
  );
444
49
  continue;
445
50
  }
446
- const T = y.upcast(a.payload, a.metadata);
447
- a = {
448
- ...a,
449
- payload: T,
51
+ const I = f.upcast(n.payload, n.metadata);
52
+ n = {
53
+ ...n,
54
+ payload: I,
450
55
  metadata: {
451
- ...a.metadata,
452
- version: l + 1
56
+ ...n.metadata,
57
+ version: u + 1
453
58
  }
454
59
  };
455
60
  }
456
61
  }
457
- const h = e.get(a.eventName);
458
- h && h(a.payload, a.metadata);
62
+ const c = e.get(n.eventName);
63
+ c && c(n.payload, n.metadata);
459
64
  }
460
65
  /**
461
66
  * @returns {string[]} Array of event types that have registered upcasters
@@ -496,7 +101,7 @@ class f extends p {
496
101
  }
497
102
  class v {
498
103
  constructor(t) {
499
- this._version = 0, this._initialVersion = 0, this._domainEvents = [], this._eventHandlers = /* @__PURE__ */ new Map(), this._capabilities = new D(), this._id = t.id, this._version = t.version || 0, this._initialVersion = t.version || 0, this._maxEvents = t.maxEvents;
104
+ this._version = 0, this._initialVersion = 0, this._domainEvents = [], this._eventHandlers = /* @__PURE__ */ new Map(), this._capabilities = new $(), this._id = t.id, this._version = t.version || 0, this._initialVersion = t.version || 0, this._maxEvents = t.maxEvents;
500
105
  }
501
106
  // ==========================================
502
107
  // CORE AGGREGATE FUNCTIONALITY
@@ -560,18 +165,18 @@ class v {
560
165
  */
561
166
  apply(t, e, i) {
562
167
  this._version++;
563
- let r, a;
564
- typeof t == "string" ? (r = N(t, e, i), a = r.metadata) : (r = t, a = i ? { ...r.metadata, ...i } : r.metadata);
565
- const h = v.sanitizeMetadata({
566
- ...a,
168
+ let a, n;
169
+ typeof t == "string" ? (a = N(t, e, i), n = a.metadata) : (a = t, n = i ? { ...a.metadata, ...i } : a.metadata);
170
+ const c = v.sanitizeMetadata({
171
+ ...n,
567
172
  aggregateId: this._id.toString(),
568
173
  aggregateType: this.constructor.name,
569
174
  aggregateVersion: this._version,
570
- timestamp: a?.timestamp ?? /* @__PURE__ */ new Date()
175
+ timestamp: n?.timestamp ?? /* @__PURE__ */ new Date()
571
176
  }), g = Object.assign(
572
- Object.create(Object.getPrototypeOf(r)),
573
- r,
574
- { metadata: h }
177
+ Object.create(Object.getPrototypeOf(a)),
178
+ a,
179
+ { metadata: c }
575
180
  );
576
181
  if (this._maxEvents !== void 0 && this._domainEvents.length >= this._maxEvents)
577
182
  throw new Error(
@@ -580,7 +185,7 @@ class v {
580
185
  this._domainEvents.push(g), this.handleEvent(g);
581
186
  }
582
187
  handleEvent(t) {
583
- const e = this.getCapability(f);
188
+ const e = this.getCapability(p);
584
189
  if (e)
585
190
  e.handleVersionedEvent(t, this._eventHandlers);
586
191
  else {
@@ -665,7 +270,7 @@ class v {
665
270
  this._version = t.version, this._initialVersion = t.initialVersion, this._domainEvents = [...t.domainEvents];
666
271
  }
667
272
  }
668
- class b extends p {
273
+ class b extends l {
669
274
  constructor() {
670
275
  super(...arguments), this.type = "audit", this.auditLog = [];
671
276
  }
@@ -679,8 +284,8 @@ class b extends p {
679
284
  this.aggregate = t;
680
285
  const e = this.aggregate;
681
286
  typeof e.apply == "function" && (this.originalApply = e.apply, this.originalApply && (this.aggregate.apply = (...i) => {
682
- const r = this.originalApply.call(this.aggregate, ...i);
683
- return this.recordAuditEvent(), r;
287
+ const a = this.originalApply.call(this.aggregate, ...i);
288
+ return this.recordAuditEvent(), a;
684
289
  }));
685
290
  }
686
291
  detach() {
@@ -701,11 +306,11 @@ class b extends p {
701
306
  getAuditStatistics() {
702
307
  const t = {};
703
308
  let e = 0;
704
- this.auditLog.forEach((r, a) => {
705
- if (t[r.eventName] = (t[r.eventName] || 0) + 1, a > 0) {
706
- const h = this.auditLog[a - 1];
707
- if (h) {
708
- const g = new Date(h.timestamp).getTime(), d = new Date(r.timestamp).getTime();
309
+ this.auditLog.forEach((a, n) => {
310
+ if (t[a.eventName] = (t[a.eventName] || 0) + 1, n > 0) {
311
+ const c = this.auditLog[n - 1];
312
+ if (c) {
313
+ const g = new Date(c.timestamp).getTime(), d = new Date(a.timestamp).getTime();
709
314
  e += d - g;
710
315
  }
711
316
  }
@@ -758,8 +363,8 @@ class b extends p {
758
363
  */
759
364
  getEventsByTimeRange(t, e) {
760
365
  return this.auditLog.filter((i) => {
761
- const r = new Date(i.timestamp).getTime();
762
- return r >= t.getTime() && r <= e.getTime();
366
+ const a = new Date(i.timestamp).getTime();
367
+ return a >= t.getTime() && a <= e.getTime();
763
368
  });
764
369
  }
765
370
  /**
@@ -775,174 +380,174 @@ class b extends p {
775
380
  return this.auditLog[this.auditLog.length - 1] || null;
776
381
  }
777
382
  }
778
- class n extends I {
383
+ class r extends M {
779
384
  /**
780
385
  * Error for invalid arguments provided to the aggregate
781
386
  */
782
387
  static invalidArguments(t, e) {
783
388
  const i = {
784
- code: c.InvalidParameter,
389
+ code: o.InvalidParameter,
785
390
  data: e
786
391
  };
787
- return new n(t, i);
392
+ return new r(t, i);
788
393
  }
789
394
  /**
790
395
  * Error for version conflicts in the aggregate
791
396
  */
792
- static versionConflict(t, e, i, r) {
793
- const a = `Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${r}`, h = {
794
- code: c.ValidationFailed,
397
+ static versionConflict(t, e, i, a) {
398
+ const n = `Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${a}`, c = {
399
+ code: o.ValidationFailed,
795
400
  data: {
796
401
  aggregateType: t,
797
402
  aggregateId: e,
798
403
  currentVersion: i,
799
- expectedVersion: r
404
+ expectedVersion: a
800
405
  }
801
406
  };
802
- return new n(a, h);
407
+ return new r(n, c);
803
408
  }
804
409
  /**
805
410
  * Error for when a required feature is not enabled
806
411
  */
807
412
  static featureNotEnabled(t) {
808
413
  const e = `Feature '${t}' is not enabled on aggregate`, i = {
809
- code: c.ValidationFailed,
414
+ code: o.ValidationFailed,
810
415
  data: {
811
416
  feature: t
812
417
  }
813
418
  };
814
- return new n(e, i);
419
+ return new r(e, i);
815
420
  }
816
421
  /**
817
422
  * Error for when a required method is not implemented
818
423
  */
819
424
  static methodNotImplemented(t, e) {
820
- const i = `Method '${t}' must be implemented by ${e} to use this feature`, r = {
821
- code: c.MissingValue,
425
+ const i = `Method '${t}' must be implemented by ${e} to use this feature`, a = {
426
+ code: o.MissingValue,
822
427
  data: {
823
428
  methodName: t,
824
429
  aggregateType: e
825
430
  }
826
431
  };
827
- return new n(i, r);
432
+ return new r(i, a);
828
433
  }
829
434
  /**
830
435
  * Error for invalid snapshot
831
436
  */
832
437
  static invalidSnapshot(t, e) {
833
- const i = `Invalid snapshot for aggregate ${t}${e ? `: ${e}` : ""}`, r = {
834
- code: c.InvalidFormat,
438
+ const i = `Invalid snapshot for aggregate ${t}${e ? `: ${e}` : ""}`, a = {
439
+ code: o.InvalidFormat,
835
440
  data: {
836
441
  aggregateType: t,
837
442
  reason: e
838
443
  }
839
444
  };
840
- return new n(i, r);
445
+ return new r(i, a);
841
446
  }
842
447
  /**
843
448
  * Error for ID mismatch during snapshot restoration
844
449
  */
845
450
  static idMismatch(t, e) {
846
- const i = `ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`, r = {
847
- code: c.ValidationFailed,
451
+ const i = `ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`, a = {
452
+ code: o.ValidationFailed,
848
453
  data: {
849
454
  snapshotId: t,
850
455
  aggregateId: e
851
456
  }
852
457
  };
853
- return new n(i, r);
458
+ return new r(i, a);
854
459
  }
855
460
  /**
856
461
  * Error for type mismatch during snapshot restoration
857
462
  */
858
463
  static typeMismatch(t, e) {
859
- const i = `Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`, r = {
860
- code: c.ValidationFailed,
464
+ const i = `Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`, a = {
465
+ code: o.ValidationFailed,
861
466
  data: {
862
467
  snapshotType: t,
863
468
  aggregateType: e
864
469
  }
865
470
  };
866
- return new n(i, r);
471
+ return new r(i, a);
867
472
  }
868
473
  /**
869
474
  * Error for duplicate upcaster registration
870
475
  */
871
476
  static duplicateUpcaster(t, e) {
872
- const i = `Upcaster for event ${t} version ${e} already exists`, r = {
873
- code: c.DuplicateEntry,
477
+ const i = `Upcaster for event ${t} version ${e} already exists`, a = {
478
+ code: o.DuplicateEntry,
874
479
  data: {
875
480
  eventType: t,
876
481
  sourceVersion: e
877
482
  }
878
483
  };
879
- return new n(i, r);
484
+ return new r(i, a);
880
485
  }
881
486
  /**
882
487
  * Error for missing upcaster
883
488
  */
884
489
  static missingUpcaster(t, e, i) {
885
- const r = `Missing upcaster for event ${t} from version ${e} to ${i}`, a = {
886
- code: c.MissingValue,
490
+ const a = `Missing upcaster for event ${t} from version ${e} to ${i}`, n = {
491
+ code: o.MissingValue,
887
492
  data: {
888
493
  eventType: t,
889
494
  fromVersion: e,
890
495
  toVersion: i
891
496
  }
892
497
  };
893
- return new n(r, a);
498
+ return new r(a, n);
894
499
  }
895
500
  /**
896
501
  * Error for configuration problems
897
502
  */
898
503
  static configurationError(t, e) {
899
- const i = `Configuration error in ${t}: ${e}`, r = {
900
- code: c.ValidationFailed,
504
+ const i = `Configuration error in ${t}: ${e}`, a = {
505
+ code: o.ValidationFailed,
901
506
  data: {
902
507
  aggregateType: t,
903
508
  configurationError: e
904
509
  }
905
510
  };
906
- return new n(i, r);
511
+ return new r(i, a);
907
512
  }
908
513
  /**
909
514
  * Error for when apply method interception fails (e.g. method not found)
910
515
  */
911
516
  static cannotInterceptApplyMethod(t, e) {
912
- const i = `Cannot intercept apply method - method not found on aggregate ${t}`, r = {
913
- code: c.InternalError,
517
+ const i = `Cannot intercept apply method - method not found on aggregate ${t}`, a = {
518
+ code: o.InternalError,
914
519
  data: {
915
520
  aggergateId: t,
916
521
  ...e
917
522
  }
918
523
  };
919
- return new n(i, r);
524
+ return new r(i, a);
920
525
  }
921
526
  /**
922
527
  * Error for when event store is not configured
923
528
  */
924
529
  static eventStoreNotConfigured(t) {
925
530
  const e = "Event store not configured", i = {
926
- code: c.MissingValue,
531
+ code: o.MissingValue,
927
532
  data: t
928
533
  };
929
- return new n(e, i);
534
+ return new r(e, i);
930
535
  }
931
536
  /**
932
537
  * Error for when aggregate does not support event replay
933
538
  */
934
539
  static aggregateDoesNotSupportReplay(t, e) {
935
- const i = `Aggregate ${t} does not support event replay`, r = {
936
- code: c.MethodNotSupported,
540
+ const i = `Aggregate ${t} does not support event replay`, a = {
541
+ code: o.MethodNotSupported,
937
542
  data: {
938
543
  aggregateType: t,
939
544
  ...e
940
545
  }
941
546
  };
942
- return new n(i, r);
547
+ return new r(i, a);
943
548
  }
944
549
  }
945
- class w extends p {
550
+ class E extends l {
946
551
  constructor() {
947
552
  super(...arguments), this.type = "eventSourcing", this.eventStore = null;
948
553
  }
@@ -976,7 +581,7 @@ class w extends p {
976
581
  */
977
582
  async loadFromEventStore(t) {
978
583
  if (!this.eventStore)
979
- throw n.configurationError(
584
+ throw r.configurationError(
980
585
  this.aggregate.constructor.name,
981
586
  "Event store not configured"
982
587
  );
@@ -991,7 +596,7 @@ class w extends p {
991
596
  */
992
597
  async saveToEventStore() {
993
598
  if (!this.eventStore)
994
- throw n.configurationError(
599
+ throw r.configurationError(
995
600
  this.aggregate.constructor.name,
996
601
  "Event store not configured"
997
602
  );
@@ -1021,15 +626,15 @@ class w extends p {
1021
626
  */
1022
627
  async loadFromVersion(t, e) {
1023
628
  if (!this.eventStore)
1024
- throw n.configurationError(
629
+ throw r.configurationError(
1025
630
  this.aggregate.constructor.name,
1026
631
  "Event store not configured"
1027
632
  );
1028
- const i = await this.eventStore.getEventsAfterVersion(t, e), r = this.aggregate;
1029
- r.loadFromHistory && i.length > 0 && r.loadFromHistory(i);
633
+ const i = await this.eventStore.getEventsAfterVersion(t, e), a = this.aggregate;
634
+ a.loadFromHistory && i.length > 0 && a.loadFromHistory(i);
1030
635
  }
1031
636
  }
1032
- class S extends p {
637
+ class S extends l {
1033
638
  constructor() {
1034
639
  super(...arguments), this.type = "snapshot", this._snapshot = null;
1035
640
  }
@@ -1051,13 +656,13 @@ class S extends p {
1051
656
  * @returns {IAggregateSnapshot<TState, TMeta>} Created snapshot with state and metadata
1052
657
  */
1053
658
  createSnapshot(t, e) {
1054
- const i = this.aggregate.getDomainEvents(), a = (i.length > 0 ? i[i.length - 1] : null)?.metadata?.eventId, g = {
659
+ const i = this.aggregate.getDomainEvents(), n = (i.length > 0 ? i[i.length - 1] : null)?.metadata?.eventId, g = {
1055
660
  aggregateId: this.aggregate.getId().toString(),
1056
661
  version: this.aggregate.getVersion(),
1057
662
  aggregateType: this.aggregate.constructor.name,
1058
663
  state: t(),
1059
664
  timestamp: /* @__PURE__ */ new Date(),
1060
- ...a !== void 0 && { lastEventId: a }
665
+ ...n !== void 0 && { lastEventId: n }
1061
666
  };
1062
667
  return e && (g.metadata = e()), g;
1063
668
  }
@@ -1069,15 +674,15 @@ class S extends p {
1069
674
  */
1070
675
  restoreFromSnapshot(t, e, i) {
1071
676
  if (!t || !t.state)
1072
- throw n.invalidSnapshot(this.aggregate.constructor.name, "Invalid snapshot");
1073
- const r = this.aggregate.getId().toString();
1074
- if (t.aggregateId !== r)
1075
- throw n.idMismatch(t.aggregateId, r);
677
+ throw r.invalidSnapshot(this.aggregate.constructor.name, "Invalid snapshot");
678
+ const a = this.aggregate.getId().toString();
679
+ if (t.aggregateId !== a)
680
+ throw r.idMismatch(t.aggregateId, a);
1076
681
  if (t.aggregateType !== this.aggregate.constructor.name)
1077
- throw n.typeMismatch(t.aggregateType, this.aggregate.constructor.name);
682
+ throw r.typeMismatch(t.aggregateType, this.aggregate.constructor.name);
1078
683
  e(t.state), t.metadata && i && i(t.metadata);
1079
- const a = this.aggregate;
1080
- a._internal_setState && a._internal_setState({
684
+ const n = this.aggregate;
685
+ n._internal_setState && n._internal_setState({
1081
686
  version: t.version,
1082
687
  initialVersion: t.version,
1083
688
  domainEvents: []
@@ -1119,7 +724,7 @@ class m {
1119
724
  */
1120
725
  static create(t) {
1121
726
  const e = {
1122
- id: t.id instanceof u ? t.id : new u(t.id, "text"),
727
+ id: t.id instanceof y ? t.id : new y(t.id, "text"),
1123
728
  version: t.version || 0
1124
729
  };
1125
730
  return new m(e);
@@ -1131,7 +736,7 @@ class m {
1131
736
  * @returns {this} Builder instance for method chaining
1132
737
  */
1133
738
  withVersioning() {
1134
- return this.capabilities.push({ capability: new f() }), this;
739
+ return this.capabilities.push({ capability: new p() }), this;
1135
740
  }
1136
741
  /**
1137
742
  * @returns {this} Builder instance for method chaining
@@ -1144,12 +749,12 @@ class m {
1144
749
  * @returns {this} Builder instance for method chaining
1145
750
  */
1146
751
  withEventSourcing(t) {
1147
- const e = new w();
752
+ const e = new E();
1148
753
  return this.capabilities.push({
1149
754
  capability: e,
1150
755
  configure: (i) => {
1151
- const r = i;
1152
- t ? r.setEventStore(t) : this.eventStore && r.setEventStore(this.eventStore);
756
+ const a = i;
757
+ t ? a.setEventStore(t) : this.eventStore && a.setEventStore(this.eventStore);
1153
758
  }
1154
759
  }), this;
1155
760
  }
@@ -1173,8 +778,8 @@ class m {
1173
778
  }
1174
779
  build(t) {
1175
780
  const e = t || v, i = new e(this.params);
1176
- for (const { capability: r, configure: a } of this.capabilities)
1177
- i.addCapability(r), a && a(r);
781
+ for (const { capability: a, configure: n } of this.capabilities)
782
+ i.addCapability(a), n && n(a);
1178
783
  return i;
1179
784
  }
1180
785
  /**
@@ -1185,10 +790,10 @@ class m {
1185
790
  return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t);
1186
791
  }
1187
792
  }
1188
- function L(s) {
793
+ function H(s) {
1189
794
  return m.create(s);
1190
795
  }
1191
- class E {
796
+ class w {
1192
797
  constructor(t) {
1193
798
  this._id = t;
1194
799
  }
@@ -1207,54 +812,54 @@ class E {
1207
812
  * @returns `true` if both entities have the same id; `false` otherwise.
1208
813
  */
1209
814
  equals(t) {
1210
- return t == null ? !1 : t === this ? !0 : t instanceof E ? this._id.equals(t._id) : !1;
815
+ return t == null ? !1 : t === this ? !0 : t instanceof w ? this._id.equals(t._id) : !1;
1211
816
  }
1212
817
  }
1213
- function x(s) {
818
+ function _(s) {
1214
819
  return s.hasCapability(S);
1215
820
  }
1216
821
  function C(s) {
1217
- return s.hasCapability(f);
822
+ return s.hasCapability(p);
1218
823
  }
1219
824
  function V(s) {
1220
825
  return s.hasCapability(b);
1221
826
  }
1222
827
  function A(s) {
1223
- return s.hasCapability(w);
828
+ return s.hasCapability(E);
1224
829
  }
1225
830
  function U(s) {
1226
- if (!x(s))
1227
- throw n.featureNotEnabled("snapshot");
831
+ if (!_(s))
832
+ throw r.featureNotEnabled("snapshot");
1228
833
  return s;
1229
834
  }
1230
- function k(s) {
1231
- return x(s) ? o.ok(s) : o.fail(n.featureNotEnabled("snapshot"));
1232
- }
1233
835
  function O(s) {
836
+ return _(s) ? h.ok(s) : h.fail(r.featureNotEnabled("snapshot"));
837
+ }
838
+ function P(s) {
1234
839
  if (!C(s))
1235
- throw n.featureNotEnabled("versioning");
840
+ throw r.featureNotEnabled("versioning");
1236
841
  return s;
1237
842
  }
1238
- function B(s) {
1239
- return C(s) ? o.ok(s) : o.fail(n.featureNotEnabled("versioning"));
843
+ function j(s) {
844
+ return C(s) ? h.ok(s) : h.fail(r.featureNotEnabled("versioning"));
1240
845
  }
1241
- function H(s) {
846
+ function k(s) {
1242
847
  if (!V(s))
1243
- throw n.featureNotEnabled("audit");
848
+ throw r.featureNotEnabled("audit");
1244
849
  return s;
1245
850
  }
1246
- function P(s) {
1247
- return V(s) ? o.ok(s) : o.fail(n.featureNotEnabled("audit"));
851
+ function B(s) {
852
+ return V(s) ? h.ok(s) : h.fail(r.featureNotEnabled("audit"));
1248
853
  }
1249
- function j(s) {
854
+ function W(s) {
1250
855
  if (!A(s))
1251
- throw n.featureNotEnabled("eventSourcing");
856
+ throw r.featureNotEnabled("eventSourcing");
1252
857
  return s;
1253
858
  }
1254
- function z(s) {
1255
- return A(s) ? o.ok(s) : o.fail(n.featureNotEnabled("eventSourcing"));
859
+ function R(s) {
860
+ return A(s) ? h.ok(s) : h.fail(r.featureNotEnabled("eventSourcing"));
1256
861
  }
1257
- function W(s) {
862
+ function z(s) {
1258
863
  return s.getCapabilityTypes();
1259
864
  }
1260
865
  function q(s, t) {
@@ -1262,22 +867,22 @@ function q(s, t) {
1262
867
  }
1263
868
  export {
1264
869
  m as AggregateBuilder,
1265
- n as AggregateError,
870
+ r as AggregateError,
1266
871
  v as AggregateRoot,
1267
872
  b as AuditCapability,
1268
- E as Entity,
1269
- w as EventSourcingCapability,
873
+ w as Entity,
874
+ E as EventSourcingCapability,
1270
875
  S as SnapshotCapability,
1271
- f as VersioningCapability,
1272
- L as aggregateBuilder,
1273
- H as asAuditAggregate,
1274
- j as asEventSourcingAggregate,
876
+ p as VersioningCapability,
877
+ H as aggregateBuilder,
878
+ k as asAuditAggregate,
879
+ W as asEventSourcingAggregate,
1275
880
  U as asSnapshotAggregate,
1276
- O as asVersioningAggregate,
1277
- W as getAggregateCapabilities,
881
+ P as asVersioningAggregate,
882
+ z as getAggregateCapabilities,
1278
883
  q as hasAllCapabilities,
1279
- P as tryAsAuditAggregate,
1280
- z as tryAsEventSourcingAggregate,
1281
- k as tryAsSnapshotAggregate,
1282
- B as tryAsVersioningAggregate
884
+ B as tryAsAuditAggregate,
885
+ R as tryAsEventSourcingAggregate,
886
+ O as tryAsSnapshotAggregate,
887
+ j as tryAsVersioningAggregate
1283
888
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vytches/ddd-aggregates",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Aggregate root implementations with capabilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -33,17 +33,17 @@
33
33
  "directory": "packages/aggregates"
34
34
  },
35
35
  "dependencies": {
36
- "@vytches/ddd-contracts": "0.26.0",
37
- "@vytches/ddd-domain-primitives": "0.26.0",
38
- "@vytches/ddd-logging": "0.26.0",
39
- "@vytches/ddd-value-objects": "0.26.0"
36
+ "@vytches/ddd-contracts": "0.27.0",
37
+ "@vytches/ddd-domain-primitives": "0.27.0",
38
+ "@vytches/ddd-logging": "0.27.0",
39
+ "@vytches/ddd-value-objects": "0.27.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "typescript": "^5.0.0",
43
43
  "vite": "^7.3.2",
44
44
  "vite-plugin-dts": "^4.0.0",
45
45
  "vitest": "^2.0.0",
46
- "@vytches/ddd-testing": "0.26.0"
46
+ "@vytches/ddd-testing": "0.27.0"
47
47
  },
48
48
  "nx": {
49
49
  "tags": [