@vytches/ddd-aggregates 0.26.0 → 0.28.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/README.md +145 -1332
- package/dist/index.cjs +1 -1
- package/dist/index.js +140 -535
- package/package.json +7 -7
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;
|