@shirudo/ddd-kit 0.16.0 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2 +1,882 @@
1
- var F=Object.defineProperty;var o=(t,e)=>F(t,"name",{value:e,configurable:true});function Z(t,e=0){return {state:t,version:e,pendingEvents:[]}}o(Z,"aggregate");function ee(t,e){return {...t,pendingEvents:[...t.pendingEvents,e]}}o(ee,"withEvent");function te(t){return {...t,version:t.version+1}}o(te,"bump");function M(t,e,r){return {type:t,payload:e,occurredAt:r?.occurredAt??new Date,version:r?.version??1,metadata:r?.metadata}}o(M,"createDomainEvent");function re(t,e,r,n){return M(t,e,{...n,metadata:r})}o(re,"createDomainEventWithMetadata");function ne(t,e){return {...t.metadata??{},...e??{}}}o(ne,"copyMetadata");function oe(...t){return Object.assign({},...t.filter(Boolean))}o(oe,"mergeMetadata");function se(t,e){return t.id===e.id&&t.version===e.version}o(se,"sameAggregate");function v(t,e){if(e.endsWith("Array]")||ArrayBuffer.isView(t)||e==="[object ArrayBuffer]"||e==="[object SharedArrayBuffer]")return true;let r=t.constructor;if(r&&typeof r=="function"){let a=r.name;if(a&&typeof globalThis<"u"&&a in globalThis&&globalThis[a]===r){let s=Object.getPrototypeOf(t);if(s!==Object.prototype&&s!==null)return true}}return new Set(["[object Date]","[object RegExp]","[object Map]","[object Set]","[object WeakMap]","[object WeakSet]","[object Promise]","[object Error]","[object Boolean]","[object Number]","[object String]"]).has(e)}o(v,"isBuiltInObject");var O=Object.prototype,b=O.toString,K=O.hasOwnProperty;function m(t,e){return h(t,e,new WeakMap)}o(m,"deepEqual");function h(t,e,r){if(t===e)return true;let n=typeof t,a=typeof e;if(n!=="object"||t===null||a!=="object"||e===null)return n==="number"&&a==="number"?Number.isNaN(t)&&Number.isNaN(e):false;let s=t,i=e,g=r.get(s);if(g!==void 0)return g===i;if(r.set(s,i),ArrayBuffer.isView(s)||ArrayBuffer.isView(i)){if(!ArrayBuffer.isView(s)||!ArrayBuffer.isView(i))return false;let c=b.call(s),l=b.call(i);if(c!==l)return false;if(c==="[object DataView]"){let T=s,P=i;if(T.byteLength!==P.byteLength)return false;let Q=T.byteLength;for(let R=0;R<Q;R++)if(T.getUint8(R)!==P.getUint8(R))return false;return true}let u=s,d=i,p=u.length;if(p!==d.length)return false;for(let T=0;T<p;T++)if(u[T]!==d[T])return false;return true}let y=b.call(s),x=b.call(i);if(y!==x)return false;switch(y){case "[object Array]":{let c=s,l=i,u=c.length;if(u!==l.length)return false;for(let d=0;d<u;d++)if(!h(c[d],l[d],r))return false;return true}case "[object Map]":{let c=s,l=i;if(c.size!==l.size)return false;for(let[u,d]of c){if(!l.has(u))return false;let p=l.get(u);if(!h(d,p,r))return false}return true}case "[object Set]":{let c=s,l=i;if(c.size!==l.size)return false;for(let u of c)if(!l.has(u))return false;return true}case "[object Date]":{let c=s.getTime(),l=i.getTime();return c===l}case "[object RegExp]":{let c=s,l=i;return c.source===l.source&&c.flags===l.flags}case "[object Boolean]":case "[object Number]":case "[object String]":return s.valueOf()===i.valueOf();default:{if(v(s,y)&&v(i,x))return s===i;let c=Object.keys(s),l=Object.keys(i),u=Object.getOwnPropertySymbols(s),d=Object.getOwnPropertySymbols(i);if(c.length!==l.length||u.length!==d.length)return false;for(let p of c)if(!K.call(i,p))return false;for(let p of u)if(!Object.getOwnPropertySymbols(i).includes(p))return false;for(let p of c)if(!h(s[p],i[p],r))return false;for(let p of u)if(!h(s[p],i[p],r))return false;return true}}}o(h,"deepEqualInner");var w=class{static{o(this,"Entity");}id;get state(){return this._state}_state;constructor(e,r){if(e==null)throw new Error("Entity ID cannot be null or undefined");this.id=e,this._state=r,this.validateState(this._state);}validateState(e){}setState(e){this.validateState(e),this._state=e;}};function Ee(t,e){return m(t.id,e.id)}o(Ee,"sameEntity");function me(t,e){return t.find(r=>m(r.id,e))}o(me,"findEntityById");function Te(t,e){return t.some(r=>m(r.id,e))}o(Te,"hasEntityId");function ye(t,e){return t.filter(r=>!m(r.id,e))}o(ye,"removeEntityById");function ge(t,e,r){return t.map(n=>m(n.id,e)?r(n):n)}o(ge,"updateEntityById");function ve(t,e,r){return t.map(n=>m(n.id,e)?r:n)}o(ve,"replaceEntityById");function he(t){return t.map(e=>e.id)}o(he,"entityIds");var k=class extends w{static{o(this,"AggregateRoot");}version=0;_config;_autoVersionBump;_domainEvents=[];get domainEvents(){return this._domainEvents}clearDomainEvents(){this._domainEvents=[];}constructor(e,r,n){super(e,r),this._config=n??{},this._autoVersionBump=this._config.autoVersionBump??false;}addDomainEvent(e){this._domainEvents.push(e);}bumpVersion(){this.version=this.version+1;}setState(e,r){super.setState(e),(r??this._autoVersionBump)&&this.bumpVersion();}createSnapshot(){return {state:{...this._state},version:this.version,snapshotAt:new Date}}restoreFromSnapshot(e){this.validateState(e.state),this._state=e.state,this.version=e.version;}};function E(t){return {ok:true,value:t}}o(E,"ok");function f(t){return {ok:false,error:t}}o(f,"err");var j=class extends k{static{o(this,"AggregateEventSourced");}_eventConfig;_eventAutoVersionBump;constructor(e,r,n){super(e,r,n),this._eventConfig=n??{},this._eventAutoVersionBump=this._eventConfig.autoVersionBump??true;}get pendingEvents(){return this.domainEvents}clearPendingEvents(){this.clearDomainEvents();}validateEvent(e){return E(true)}apply(e,r=true){let n=this.validateEvent(e);if(!n.ok)return f(`Event validation failed for ${e.type}: ${n.error}`);let a=this.handlers[e.type];return a?(this._state=a(this._state,e),r&&(this.addDomainEvent(e),this._eventAutoVersionBump&&(this.version=this.version+1)),E()):f(`Missing handler for event type: ${e.type}`)}applyUnsafe(e,r=true){let n=this.validateEvent(e);if(!n.ok)throw new Error(`Event validation failed for ${e.type}: ${n.error}`);let a=this.handlers[e.type];if(!a)throw new Error(`Missing handler for event type: ${e.type}`);this._state=a(this._state,e),r&&(this.addDomainEvent(e),this._eventAutoVersionBump&&(this.version=this.version+1));}bumpVersion(){this.version=this.version+1;}loadFromHistory(e){for(let r of e){let n=this.apply(r,false);if(!n.ok)return n}return this.version=e.length,E()}hasPendingEvents(){return this.domainEvents.length>0}getEventCount(){return this.domainEvents.length}getLatestEvent(){let e=this.domainEvents;return e[e.length-1]}restoreFromSnapshotWithEvents(e,r){this._state=e.state,this.version=e.version;for(let n of r){let a=this.apply(n,false);if(!a.ok)return a}return this.version=e.version+r.length,E()}};var V=class{static{o(this,"CommandBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);if(!r)return f(`No handler registered for command type: ${e.type}`);try{return await r(e)}catch(n){return f(n instanceof Error?n.message:String(n))}}};function Je(t,e){return t.uow.transactional(async()=>{let{result:r,events:n}=await e();return await t.outbox.add(n),t.bus&&await t.bus.publish(n),r})}o(Je,"withCommit");var B=class{static{o(this,"QueryBus");}handlers=new Map;register(e,r){this.handlers.set(e,r);}async execute(e){let r=this.handlers.get(e.type);if(!r)return f(`No handler registered for query type: ${e.type}`);try{let n=await r(e);return E(n)}catch(n){return f(n instanceof Error?n.message:String(n))}}async executeUnsafe(e){let r=this.handlers.get(e.type);if(!r)throw new Error(`No handler registered for query type: ${e.type}`);return r(e)}};function rt(t,e){return t?E(true):f(e)}o(rt,"guard");var D=class{static{o(this,"EventBusImpl");}handlers=new Map;subscribe(e,r){let n=e;this.handlers.has(n)||this.handlers.set(n,new Set);let a=this.handlers.get(n);return a.add(r),()=>{a.delete(r),a.size===0&&this.handlers.delete(n);}}once(e){return new Promise(r=>{let n=this.subscribe(e,a=>{n(),r(a);});})}async publish(e){let r=[];for(let n of e){let a=this.handlers.get(n.type);if(a){let s=await Promise.allSettled(Array.from(a).map(i=>i(n)));for(let i of s)i.status==="rejected"&&r.push(i.reason instanceof Error?i.reason:new Error(String(i.reason)));}}if(r.length===1)throw r[0];if(r.length>1)throw new AggregateError(r,"Multiple event handlers failed")}};function S(t,e){return I(t,e,[],new WeakMap)}o(S,"deepOmit");function I(t,e,r,n){if(t===null||typeof t!=="object")return t;let s=t,i=n.get(s);if(i!==void 0)return i;let g=Object.prototype.toString.call(s);if(g==="[object Array]"){let u=s,d=new Array(u.length);n.set(s,d);for(let p=0;p<u.length;p++)r.push(p),d[p]=I(u[p],e,r,n),r.pop();return d}if(v(s,g))return t;let y=Object.create(Object.getPrototypeOf(s));n.set(s,y);let x=Object.keys(s),c=Object.getOwnPropertySymbols(s),l=[...x,...c];for(let u of l)X(u,r,e)||(r.push(u),y[u]=I(s[u],e,r,n),r.pop());return y}o(I,"omitInternal");function X(t,e,r){return !!(r.ignoreKeys?.includes(t)||r.ignoreKeyPredicate?.(t,e))}o(X,"shouldIgnoreKey");function _(t,e,r){let n=S(t,r),a=S(e,r);return m(n,a)}o(_,"deepEqualExcept");function A(t,e=new WeakSet){if(t===null||typeof t!="object"||e.has(t))return t;e.add(t);let r=Object.getOwnPropertyNames(t);for(let n of r){let a=t[n];a&&(typeof a=="object"||Array.isArray(a))&&A(a,e);}return Object.freeze(t)}o(A,"deepFreeze");function C(t){return A({...t})}o(C,"vo");function yt(t,e){return m(t,e)}o(yt,"voEquals");function gt(t,e,r){return _(t,e,r)}o(gt,"voEqualsExcept");function vt(t,e,r){return e(t)?E(C(t)):f(r??`Validation failed for value object: ${JSON.stringify(t)}`)}o(vt,"voWithValidation");function ht(t,e,r){if(!e(t))throw new Error(r??`Validation failed for value object: ${JSON.stringify(t)}`);return C(t)}o(ht,"voWithValidationUnsafe");var U=class{static{o(this,"ValueObject");}props;constructor(e){this.validate(e),this.props=A({...e});}validate(e){}equals(e){return e==null||this.constructor!==e.constructor?false:m(this.props,e.props)}clone(e){let r=this.constructor;return new r({...this.props,...e||{}})}toJSON(){return this.props}};export{j as AggregateEventSourced,k as AggregateRoot,V as CommandBus,w as Entity,D as EventBusImpl,B as QueryBus,U as ValueObject,Z as aggregate,te as bump,ne as copyMetadata,M as createDomainEvent,re as createDomainEventWithMetadata,A as deepFreeze,he as entityIds,me as findEntityById,rt as guard,Te as hasEntityId,oe as mergeMetadata,ye as removeEntityById,ve as replaceEntityById,se as sameAggregate,Ee as sameEntity,ge as updateEntityById,C as vo,yt as voEquals,gt as voEqualsExcept,vt as voWithValidation,ht as voWithValidationUnsafe,Je as withCommit,ee as withEvent};//# sourceMappingURL=index.js.map
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ // src/aggregate/domain-event.ts
5
+ function createDomainEvent(type, payload, options) {
6
+ return {
7
+ type,
8
+ payload,
9
+ occurredAt: options?.occurredAt ?? /* @__PURE__ */ new Date(),
10
+ version: options?.version ?? 1,
11
+ metadata: options?.metadata
12
+ };
13
+ }
14
+ __name(createDomainEvent, "createDomainEvent");
15
+ function createDomainEventWithMetadata(type, payload, metadata, options) {
16
+ return createDomainEvent(type, payload, {
17
+ ...options,
18
+ metadata
19
+ });
20
+ }
21
+ __name(createDomainEventWithMetadata, "createDomainEventWithMetadata");
22
+ function copyMetadata(sourceEvent, additionalMetadata) {
23
+ return {
24
+ ...sourceEvent.metadata ?? {},
25
+ ...additionalMetadata ?? {}
26
+ };
27
+ }
28
+ __name(copyMetadata, "copyMetadata");
29
+ function mergeMetadata(...metadataObjects) {
30
+ return Object.assign({}, ...metadataObjects.filter(Boolean));
31
+ }
32
+ __name(mergeMetadata, "mergeMetadata");
33
+
34
+ // src/aggregate/aggregate.ts
35
+ function aggregate(state, version = 0) {
36
+ return { state, version };
37
+ }
38
+ __name(aggregate, "aggregate");
39
+ function bump(agg) {
40
+ return { ...agg, version: agg.version + 1 };
41
+ }
42
+ __name(bump, "bump");
43
+ function sameVersion(a, b) {
44
+ return a.id === b.id && a.version === b.version;
45
+ }
46
+ __name(sameVersion, "sameVersion");
47
+
48
+ // src/utils/array/is-built-in.ts
49
+ function isBuiltInObject(obj, tag) {
50
+ if (tag.endsWith("Array]")) {
51
+ return true;
52
+ }
53
+ if (ArrayBuffer.isView(obj)) {
54
+ return true;
55
+ }
56
+ if (tag === "[object ArrayBuffer]" || tag === "[object SharedArrayBuffer]") {
57
+ return true;
58
+ }
59
+ const objConstructor = obj.constructor;
60
+ if (objConstructor && typeof objConstructor === "function") {
61
+ const constructorName = objConstructor.name;
62
+ if (constructorName && typeof globalThis !== "undefined" && constructorName in globalThis && globalThis[constructorName] === objConstructor) {
63
+ const proto = Object.getPrototypeOf(obj);
64
+ if (proto !== Object.prototype && proto !== null) {
65
+ return true;
66
+ }
67
+ }
68
+ }
69
+ const knownBuiltInTags = /* @__PURE__ */ new Set([
70
+ "[object Date]",
71
+ "[object RegExp]",
72
+ "[object Map]",
73
+ "[object Set]",
74
+ "[object WeakMap]",
75
+ "[object WeakSet]",
76
+ "[object Promise]",
77
+ "[object Error]",
78
+ "[object Boolean]",
79
+ "[object Number]",
80
+ "[object String]"
81
+ ]);
82
+ return knownBuiltInTags.has(tag);
83
+ }
84
+ __name(isBuiltInObject, "isBuiltInObject");
85
+
86
+ // src/utils/array/deep-equal.ts
87
+ var objProto = Object.prototype;
88
+ var objToString = objProto.toString;
89
+ var objHasOwn = objProto.hasOwnProperty;
90
+ function deepEqual(a, b) {
91
+ return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
92
+ }
93
+ __name(deepEqual, "deepEqual");
94
+ function deepEqualInner(a, b, visited) {
95
+ if (a === b) return true;
96
+ const typeA = typeof a;
97
+ const typeB = typeof b;
98
+ if (typeA !== "object" || a === null || typeB !== "object" || b === null) {
99
+ if (typeA === "number" && typeB === "number") {
100
+ return Number.isNaN(a) && Number.isNaN(b);
101
+ }
102
+ return false;
103
+ }
104
+ const objA = a;
105
+ const objB = b;
106
+ const cached = visited.get(objA);
107
+ if (cached !== void 0) {
108
+ return cached === objB;
109
+ }
110
+ visited.set(objA, objB);
111
+ if (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {
112
+ if (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;
113
+ const tagA2 = objToString.call(objA);
114
+ const tagB2 = objToString.call(objB);
115
+ if (tagA2 !== tagB2) return false;
116
+ if (tagA2 === "[object DataView]") {
117
+ const viewA = objA;
118
+ const viewB = objB;
119
+ if (viewA.byteLength !== viewB.byteLength) return false;
120
+ const len2 = viewA.byteLength;
121
+ for (let i = 0; i < len2; i++) {
122
+ if (viewA.getUint8(i) !== viewB.getUint8(i)) return false;
123
+ }
124
+ return true;
125
+ }
126
+ const arrA = objA;
127
+ const arrB = objB;
128
+ const len = arrA.length;
129
+ if (len !== arrB.length) return false;
130
+ for (let i = 0; i < len; i++) {
131
+ if (arrA[i] !== arrB[i]) return false;
132
+ }
133
+ return true;
134
+ }
135
+ const tagA = objToString.call(objA);
136
+ const tagB = objToString.call(objB);
137
+ if (tagA !== tagB) return false;
138
+ switch (tagA) {
139
+ case "[object Array]": {
140
+ const arrA = objA;
141
+ const arrB = objB;
142
+ const len = arrA.length;
143
+ if (len !== arrB.length) return false;
144
+ for (let i = 0; i < len; i++) {
145
+ if (!deepEqualInner(arrA[i], arrB[i], visited)) return false;
146
+ }
147
+ return true;
148
+ }
149
+ case "[object Map]": {
150
+ const mapA = objA;
151
+ const mapB = objB;
152
+ if (mapA.size !== mapB.size) return false;
153
+ for (const [key, valA] of mapA) {
154
+ if (!mapB.has(key)) return false;
155
+ const valB = mapB.get(key);
156
+ if (!deepEqualInner(valA, valB, visited)) return false;
157
+ }
158
+ return true;
159
+ }
160
+ case "[object Set]": {
161
+ const setA = objA;
162
+ const setB = objB;
163
+ if (setA.size !== setB.size) return false;
164
+ for (const value of setA) {
165
+ if (!setB.has(value)) return false;
166
+ }
167
+ return true;
168
+ }
169
+ case "[object Date]": {
170
+ const timeA = objA.getTime();
171
+ const timeB = objB.getTime();
172
+ return timeA === timeB;
173
+ }
174
+ case "[object RegExp]": {
175
+ const regA = objA;
176
+ const regB = objB;
177
+ return regA.source === regB.source && regA.flags === regB.flags;
178
+ }
179
+ case "[object Boolean]":
180
+ case "[object Number]":
181
+ case "[object String]": {
182
+ return objA.valueOf() === objB.valueOf();
183
+ }
184
+ default: {
185
+ if (isBuiltInObject(objA, tagA) && isBuiltInObject(objB, tagB)) {
186
+ return objA === objB;
187
+ }
188
+ const stringKeysA = Object.keys(objA);
189
+ const stringKeysB = Object.keys(objB);
190
+ const symbolKeysA = Object.getOwnPropertySymbols(objA);
191
+ const symbolKeysB = Object.getOwnPropertySymbols(objB);
192
+ if (stringKeysA.length !== stringKeysB.length) return false;
193
+ if (symbolKeysA.length !== symbolKeysB.length) return false;
194
+ for (const key of stringKeysA) {
195
+ if (!objHasOwn.call(objB, key)) return false;
196
+ }
197
+ for (const key of symbolKeysA) {
198
+ if (!Object.getOwnPropertySymbols(objB).includes(key)) return false;
199
+ }
200
+ for (const key of stringKeysA) {
201
+ if (!deepEqualInner(objA[key], objB[key], visited)) {
202
+ return false;
203
+ }
204
+ }
205
+ for (const key of symbolKeysA) {
206
+ if (!deepEqualInner(objA[key], objB[key], visited)) {
207
+ return false;
208
+ }
209
+ }
210
+ return true;
211
+ }
212
+ }
213
+ }
214
+ __name(deepEqualInner, "deepEqualInner");
215
+
216
+ // src/entity/entity.ts
217
+ var Entity = class {
218
+ static {
219
+ __name(this, "Entity");
220
+ }
221
+ id;
222
+ /**
223
+ * Returns the current state of the entity.
224
+ * State is readonly from outside to enforce encapsulation.
225
+ */
226
+ get state() {
227
+ return this._state;
228
+ }
229
+ /**
230
+ * The state is 'protected' so that only the subclass can modify it.
231
+ * Subclasses can mutate this directly or use helper methods.
232
+ */
233
+ _state;
234
+ constructor(id, initialState) {
235
+ if (id === null || id === void 0) {
236
+ throw new Error("Entity ID cannot be null or undefined");
237
+ }
238
+ this.id = id;
239
+ this._state = initialState;
240
+ this.validateState(this._state);
241
+ }
242
+ /**
243
+ * Optional validation hook to ensure state invariants.
244
+ * Called during construction and whenever helpful.
245
+ * Override this method to implement validation logic.
246
+ *
247
+ * @param state - The state to validate
248
+ * @throws Error if validation fails
249
+ */
250
+ validateState(_state) {
251
+ }
252
+ /**
253
+ * Sets the state of the entity.
254
+ * This is a convenience method for state mutations.
255
+ * Automatically validates the newState using `validateState()`.
256
+ *
257
+ * @param newState - The new state
258
+ */
259
+ setState(newState) {
260
+ this.validateState(newState);
261
+ this._state = newState;
262
+ }
263
+ };
264
+ function sameEntity(a, b) {
265
+ return deepEqual(a.id, b.id);
266
+ }
267
+ __name(sameEntity, "sameEntity");
268
+ function findEntityById(entities, id) {
269
+ return entities.find((entity) => deepEqual(entity.id, id));
270
+ }
271
+ __name(findEntityById, "findEntityById");
272
+ function hasEntityId(entities, id) {
273
+ return entities.some((entity) => deepEqual(entity.id, id));
274
+ }
275
+ __name(hasEntityId, "hasEntityId");
276
+ function removeEntityById(entities, id) {
277
+ return entities.filter((entity) => !deepEqual(entity.id, id));
278
+ }
279
+ __name(removeEntityById, "removeEntityById");
280
+ function updateEntityById(entities, id, updater) {
281
+ return entities.map((entity) => deepEqual(entity.id, id) ? updater(entity) : entity);
282
+ }
283
+ __name(updateEntityById, "updateEntityById");
284
+ function replaceEntityById(entities, id, replacement) {
285
+ return entities.map((entity) => deepEqual(entity.id, id) ? replacement : entity);
286
+ }
287
+ __name(replaceEntityById, "replaceEntityById");
288
+ function entityIds(entities) {
289
+ return entities.map((entity) => entity.id);
290
+ }
291
+ __name(entityIds, "entityIds");
292
+
293
+ // src/aggregate/aggregate-root.ts
294
+ var AggregateRoot = class extends Entity {
295
+ static {
296
+ __name(this, "AggregateRoot");
297
+ }
298
+ _version = 0;
299
+ get version() {
300
+ return this._version;
301
+ }
302
+ setVersion(version) {
303
+ this._version = version;
304
+ }
305
+ _config;
306
+ _autoVersionBump;
307
+ _domainEvents = [];
308
+ /**
309
+ * Returns a read-only list of domain events recorded by this aggregate.
310
+ * These events are side-effects of state changes.
311
+ */
312
+ get domainEvents() {
313
+ return this._domainEvents;
314
+ }
315
+ /**
316
+ * Clears the list of recorded domain events.
317
+ * Call this after dispatching the events.
318
+ */
319
+ clearDomainEvents() {
320
+ this._domainEvents = [];
321
+ }
322
+ constructor(id, initialState, config) {
323
+ super(id, initialState);
324
+ this._config = config ?? {};
325
+ this._autoVersionBump = this._config.autoVersionBump ?? false;
326
+ }
327
+ /**
328
+ * Adds a domain event to the aggregate's list of changes.
329
+ * Use this to record side-effects that should be published.
330
+ *
331
+ * @param event - The domain event to add
332
+ */
333
+ addDomainEvent(event) {
334
+ this._domainEvents.push(event);
335
+ }
336
+ /**
337
+ * Manually bumps the aggregate version.
338
+ * Call this after state changes for Optimistic Concurrency Control.
339
+ *
340
+ * If `autoVersionBump` is enabled, this is called automatically
341
+ * when using `setState()`.
342
+ */
343
+ bumpVersion() {
344
+ this.setVersion(this._version + 1);
345
+ }
346
+ /**
347
+ * Sets the state and optionally bumps the version automatically.
348
+ * This is a convenience method for state mutations.
349
+ * Automatically validates the newState using `validateState()`.
350
+ * Overrides Entity.setState to add version bumping.
351
+ *
352
+ * @param newState - The new state
353
+ * @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)
354
+ */
355
+ setState(newState, bumpVersion) {
356
+ super.setState(newState);
357
+ const shouldBump = bumpVersion ?? this._autoVersionBump;
358
+ if (shouldBump) {
359
+ this.bumpVersion();
360
+ }
361
+ }
362
+ /**
363
+ * Creates a snapshot of the current aggregate state.
364
+ * Useful for performance optimization, backup/restore, and audit trails.
365
+ *
366
+ * @returns A snapshot containing the current state and version
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * const snapshot = aggregate.createSnapshot();
371
+ * await snapshotRepository.save(aggregate.id, snapshot);
372
+ * ```
373
+ */
374
+ createSnapshot() {
375
+ return {
376
+ state: structuredClone(this._state),
377
+ version: this.version,
378
+ snapshotAt: /* @__PURE__ */ new Date()
379
+ };
380
+ }
381
+ /**
382
+ * Restores the aggregate from a snapshot.
383
+ * This is useful for loading aggregates from snapshots instead of
384
+ * rebuilding them from scratch.
385
+ * Validates the restored state.
386
+ *
387
+ * @param snapshot - The snapshot to restore from
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * const snapshot = await snapshotRepository.getLatest(aggregateId);
392
+ * aggregate.restoreFromSnapshot(snapshot);
393
+ * ```
394
+ */
395
+ restoreFromSnapshot(snapshot) {
396
+ this.validateState(snapshot.state);
397
+ this._state = snapshot.state;
398
+ this.setVersion(snapshot.version);
399
+ }
400
+ };
401
+
402
+ // src/core/result/result.ts
403
+ function ok(value) {
404
+ return { ok: true, value };
405
+ }
406
+ __name(ok, "ok");
407
+ function err(error) {
408
+ return { ok: false, error };
409
+ }
410
+ __name(err, "err");
411
+
412
+ // src/aggregate/event-sourced-aggregate.ts
413
+ var EventSourcedAggregate = class extends Entity {
414
+ static {
415
+ __name(this, "EventSourcedAggregate");
416
+ }
417
+ // --- Version management (own, not inherited from AggregateRoot) ---
418
+ _version = 0;
419
+ get version() {
420
+ return this._version;
421
+ }
422
+ setVersion(version) {
423
+ this._version = version;
424
+ }
425
+ // --- Event tracking ---
426
+ _pendingEvents = [];
427
+ _autoVersionBump;
428
+ get pendingEvents() {
429
+ return this._pendingEvents;
430
+ }
431
+ clearPendingEvents() {
432
+ this._pendingEvents = [];
433
+ }
434
+ constructor(id, initialState, config) {
435
+ super(id, initialState);
436
+ this._autoVersionBump = config?.autoVersionBump ?? true;
437
+ }
438
+ // --- Event application ---
439
+ /**
440
+ * Validates an event before it is applied.
441
+ * Override this method to add custom validation logic.
442
+ * Return `ok(true)` if the event is valid, `err(message)` otherwise.
443
+ */
444
+ validateEvent(_event) {
445
+ return ok(true);
446
+ }
447
+ /**
448
+ * Applies an event to change the state and adds it to pending events.
449
+ * Returns a Result type instead of throwing an error.
450
+ *
451
+ * @param event - The domain event to apply
452
+ * @param isNew - Whether the event is new (needs persisting) or from history replay
453
+ */
454
+ apply(event, isNew = true) {
455
+ const validation = this.validateEvent(event);
456
+ if (!validation.ok) {
457
+ return err(
458
+ `Event validation failed for ${event.type}: ${validation.error}`
459
+ );
460
+ }
461
+ const handler = this.handlers[event.type];
462
+ if (!handler) {
463
+ return err(`Missing handler for event type: ${event.type}`);
464
+ }
465
+ this._state = handler(
466
+ this._state,
467
+ event
468
+ );
469
+ if (isNew) {
470
+ this._pendingEvents.push(event);
471
+ if (this._autoVersionBump) {
472
+ this.setVersion(this._version + 1);
473
+ }
474
+ }
475
+ return ok();
476
+ }
477
+ /**
478
+ * Applies an event to change the state and adds it to pending events.
479
+ * Throws an error if validation fails or handler is missing.
480
+ */
481
+ applyUnsafe(event, isNew = true) {
482
+ const validation = this.validateEvent(event);
483
+ if (!validation.ok) {
484
+ throw new Error(
485
+ `Event validation failed for ${event.type}: ${validation.error}`
486
+ );
487
+ }
488
+ const handler = this.handlers[event.type];
489
+ if (!handler) {
490
+ throw new Error(`Missing handler for event type: ${event.type}`);
491
+ }
492
+ this._state = handler(
493
+ this._state,
494
+ event
495
+ );
496
+ if (isNew) {
497
+ this._pendingEvents.push(event);
498
+ if (this._autoVersionBump) {
499
+ this.setVersion(this._version + 1);
500
+ }
501
+ }
502
+ }
503
+ /**
504
+ * Manually bumps the aggregate version.
505
+ * Only needed if `autoVersionBump` is disabled.
506
+ */
507
+ bumpVersion() {
508
+ this.setVersion(this._version + 1);
509
+ }
510
+ // --- History & Snapshots ---
511
+ /**
512
+ * Reconstitutes the aggregate from an event history.
513
+ * Sets the version to the number of events in the history.
514
+ */
515
+ loadFromHistory(history) {
516
+ for (const event of history) {
517
+ const result = this.apply(event, false);
518
+ if (!result.ok) {
519
+ return result;
520
+ }
521
+ }
522
+ this.setVersion(history.length);
523
+ return ok();
524
+ }
525
+ hasPendingEvents() {
526
+ return this._pendingEvents.length > 0;
527
+ }
528
+ getEventCount() {
529
+ return this._pendingEvents.length;
530
+ }
531
+ getLatestEvent() {
532
+ return this._pendingEvents[this._pendingEvents.length - 1];
533
+ }
534
+ /**
535
+ * Creates a snapshot of the current aggregate state.
536
+ */
537
+ createSnapshot() {
538
+ return {
539
+ state: structuredClone(this._state),
540
+ version: this._version,
541
+ snapshotAt: /* @__PURE__ */ new Date()
542
+ };
543
+ }
544
+ /**
545
+ * Restores the aggregate from a snapshot and applies events that occurred after.
546
+ */
547
+ restoreFromSnapshotWithEvents(snapshot, eventsAfterSnapshot) {
548
+ this._state = snapshot.state;
549
+ this.setVersion(snapshot.version);
550
+ for (const event of eventsAfterSnapshot) {
551
+ const result = this.apply(event, false);
552
+ if (!result.ok) {
553
+ return result;
554
+ }
555
+ }
556
+ this.setVersion(snapshot.version + eventsAfterSnapshot.length);
557
+ return ok();
558
+ }
559
+ };
560
+
561
+ // src/app/command-bus.ts
562
+ var CommandBus = class {
563
+ static {
564
+ __name(this, "CommandBus");
565
+ }
566
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
567
+ handlers = /* @__PURE__ */ new Map();
568
+ register(commandType, handler) {
569
+ this.handlers.set(commandType, handler);
570
+ }
571
+ async execute(command) {
572
+ const handler = this.handlers.get(command.type);
573
+ if (!handler) {
574
+ return err(`No handler registered for command type: ${command.type}`);
575
+ }
576
+ try {
577
+ return await handler(command);
578
+ } catch (error) {
579
+ return err(
580
+ error instanceof Error ? error.message : String(error)
581
+ );
582
+ }
583
+ }
584
+ };
585
+
586
+ // src/app/handler.ts
587
+ function withCommit(deps, fn) {
588
+ return deps.uow.transactional(async () => {
589
+ const { result, events } = await fn();
590
+ await deps.outbox.add(events);
591
+ if (deps.bus) await deps.bus.publish(events);
592
+ return result;
593
+ });
594
+ }
595
+ __name(withCommit, "withCommit");
596
+
597
+ // src/app/query-bus.ts
598
+ var QueryBus = class {
599
+ static {
600
+ __name(this, "QueryBus");
601
+ }
602
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
603
+ handlers = /* @__PURE__ */ new Map();
604
+ register(queryType, handler) {
605
+ this.handlers.set(queryType, handler);
606
+ }
607
+ async execute(query) {
608
+ const handler = this.handlers.get(query.type);
609
+ if (!handler) {
610
+ return err(`No handler registered for query type: ${query.type}`);
611
+ }
612
+ try {
613
+ const result = await handler(query);
614
+ return ok(result);
615
+ } catch (error) {
616
+ return err(
617
+ error instanceof Error ? error.message : String(error)
618
+ );
619
+ }
620
+ }
621
+ async executeUnsafe(query) {
622
+ const handler = this.handlers.get(query.type);
623
+ if (!handler) {
624
+ throw new Error(`No handler registered for query type: ${query.type}`);
625
+ }
626
+ return handler(query);
627
+ }
628
+ };
629
+
630
+ // src/core/guard.ts
631
+ function guard(cond, error) {
632
+ return cond ? ok(true) : err(error);
633
+ }
634
+ __name(guard, "guard");
635
+
636
+ // src/events/event-bus.ts
637
+ var EventBusImpl = class {
638
+ static {
639
+ __name(this, "EventBusImpl");
640
+ }
641
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
642
+ handlers = /* @__PURE__ */ new Map();
643
+ subscribe(eventType, handler) {
644
+ const type = eventType;
645
+ if (!this.handlers.has(type)) {
646
+ this.handlers.set(type, /* @__PURE__ */ new Set());
647
+ }
648
+ const handlersForType = this.handlers.get(type);
649
+ handlersForType.add(handler);
650
+ return () => {
651
+ handlersForType.delete(handler);
652
+ if (handlersForType.size === 0) {
653
+ this.handlers.delete(type);
654
+ }
655
+ };
656
+ }
657
+ once(eventType) {
658
+ return new Promise((resolve) => {
659
+ const unsubscribe = this.subscribe(eventType, (event) => {
660
+ unsubscribe();
661
+ resolve(event);
662
+ });
663
+ });
664
+ }
665
+ async publish(events) {
666
+ const errors = [];
667
+ for (const event of events) {
668
+ const handlersForType = this.handlers.get(event.type);
669
+ if (handlersForType) {
670
+ const results = await Promise.allSettled(
671
+ Array.from(handlersForType).map((handler) => handler(event))
672
+ );
673
+ for (const result of results) {
674
+ if (result.status === "rejected") {
675
+ errors.push(
676
+ result.reason instanceof Error ? result.reason : new Error(String(result.reason))
677
+ );
678
+ }
679
+ }
680
+ }
681
+ }
682
+ if (errors.length === 1) {
683
+ throw errors[0];
684
+ }
685
+ if (errors.length > 1) {
686
+ throw new AggregateError(errors, "Multiple event handlers failed");
687
+ }
688
+ }
689
+ };
690
+
691
+ // src/utils/array/deep-omit.ts
692
+ function deepOmit(value, options) {
693
+ const visited = /* @__PURE__ */ new WeakMap();
694
+ return omitInternal(value, options, [], visited);
695
+ }
696
+ __name(deepOmit, "deepOmit");
697
+ function omitInternal(value, options, path, visited) {
698
+ if (value === null) return value;
699
+ const type = typeof value;
700
+ if (type !== "object") return value;
701
+ const obj = value;
702
+ const cached = visited.get(obj);
703
+ if (cached !== void 0) {
704
+ return cached;
705
+ }
706
+ const tag = Object.prototype.toString.call(obj);
707
+ if (tag === "[object Array]") {
708
+ const arr = obj;
709
+ const clone2 = new Array(arr.length);
710
+ visited.set(obj, clone2);
711
+ for (let i = 0; i < arr.length; i++) {
712
+ path.push(i);
713
+ clone2[i] = omitInternal(arr[i], options, path, visited);
714
+ path.pop();
715
+ }
716
+ return clone2;
717
+ }
718
+ if (isBuiltInObject(obj, tag)) {
719
+ return value;
720
+ }
721
+ const clone = Object.create(Object.getPrototypeOf(obj));
722
+ visited.set(obj, clone);
723
+ const stringKeys = Object.keys(obj);
724
+ const symbolKeys = Object.getOwnPropertySymbols(obj);
725
+ const keys = [...stringKeys, ...symbolKeys];
726
+ for (const key of keys) {
727
+ if (shouldIgnoreKey(key, path, options)) continue;
728
+ path.push(key);
729
+ clone[key] = omitInternal(
730
+ obj[key],
731
+ options,
732
+ path,
733
+ visited
734
+ );
735
+ path.pop();
736
+ }
737
+ return clone;
738
+ }
739
+ __name(omitInternal, "omitInternal");
740
+ function shouldIgnoreKey(key, path, options) {
741
+ if (options.ignoreKeys?.includes(key)) {
742
+ return true;
743
+ }
744
+ if (options.ignoreKeyPredicate?.(key, path)) {
745
+ return true;
746
+ }
747
+ return false;
748
+ }
749
+ __name(shouldIgnoreKey, "shouldIgnoreKey");
750
+
751
+ // src/utils/array/deep-equal-except.ts
752
+ function deepEqualExcept(a, b, options) {
753
+ const prunedA = deepOmit(a, options);
754
+ const prunedB = deepOmit(b, options);
755
+ return deepEqual(prunedA, prunedB);
756
+ }
757
+ __name(deepEqualExcept, "deepEqualExcept");
758
+
759
+ // src/value-object/value-object.ts
760
+ function deepFreeze(obj, visited = /* @__PURE__ */ new WeakSet()) {
761
+ if (obj === null || typeof obj !== "object") {
762
+ return obj;
763
+ }
764
+ if (visited.has(obj)) {
765
+ return obj;
766
+ }
767
+ visited.add(obj);
768
+ const propNames = Object.getOwnPropertyNames(obj);
769
+ for (const name of propNames) {
770
+ const value = obj[name];
771
+ if (value && (typeof value === "object" || Array.isArray(value))) {
772
+ deepFreeze(value, visited);
773
+ }
774
+ }
775
+ return Object.freeze(obj);
776
+ }
777
+ __name(deepFreeze, "deepFreeze");
778
+ function vo(t) {
779
+ return deepFreeze({ ...t });
780
+ }
781
+ __name(vo, "vo");
782
+ function voEquals(a, b) {
783
+ return deepEqual(a, b);
784
+ }
785
+ __name(voEquals, "voEquals");
786
+ function voEqualsExcept(a, b, options) {
787
+ return deepEqualExcept(a, b, options);
788
+ }
789
+ __name(voEqualsExcept, "voEqualsExcept");
790
+ function voWithValidation(t, validate, errorMessage) {
791
+ if (!validate(t)) {
792
+ return err(
793
+ errorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`
794
+ );
795
+ }
796
+ return ok(vo(t));
797
+ }
798
+ __name(voWithValidation, "voWithValidation");
799
+ function voWithValidationUnsafe(t, validate, errorMessage) {
800
+ if (!validate(t)) {
801
+ throw new Error(
802
+ errorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`
803
+ );
804
+ }
805
+ return vo(t);
806
+ }
807
+ __name(voWithValidationUnsafe, "voWithValidationUnsafe");
808
+ var ValueObject = class {
809
+ static {
810
+ __name(this, "ValueObject");
811
+ }
812
+ props;
813
+ /**
814
+ * Creates a new ValueObject.
815
+ * The properties are deeply frozen to ensure immutability.
816
+ *
817
+ * @param props - The properties of the value object
818
+ * @example
819
+ * ```ts
820
+ * class Money extends ValueObject<{ amount: number; currency: string }> {
821
+ * constructor(props: { amount: number; currency: string }) {
822
+ * super(props);
823
+ * }
824
+ *
825
+ * protected validate(props: { amount: number; currency: string }): void {
826
+ * if (props.amount < 0) throw new Error("Amount cannot be negative");
827
+ * }
828
+ * }
829
+ * ```
830
+ */
831
+ constructor(props) {
832
+ this.validate(props);
833
+ this.props = deepFreeze({ ...props });
834
+ }
835
+ /**
836
+ * Optional validation hook that can be overridden by subclasses.
837
+ * Should throw an error if validation fails.
838
+ *
839
+ * @param props - The properties to validate
840
+ * @throws Error if validation fails
841
+ */
842
+ validate(props) {
843
+ }
844
+ /**
845
+ * Checks if this value object is equal to another.
846
+ * Uses deep equality comparison on the properties and checks for constructor equality.
847
+ *
848
+ * @param other - The other value object to compare
849
+ * @returns true if the properties are deeply equal and constructors match
850
+ */
851
+ equals(other) {
852
+ if (other === null || other === void 0) {
853
+ return false;
854
+ }
855
+ if (this.constructor !== other.constructor) {
856
+ return false;
857
+ }
858
+ return deepEqual(this.props, other.props);
859
+ }
860
+ /**
861
+ * Creates a clone of the value object with optional property overrides.
862
+ *
863
+ * @param props - Optional properties to override
864
+ * @returns A new instance of the value object
865
+ */
866
+ clone(props) {
867
+ const Constructor = this.constructor;
868
+ return new Constructor({ ...this.props, ...props || {} });
869
+ }
870
+ /**
871
+ * Serializes the value object to its raw properties for JSON operations.
872
+ *
873
+ * @returns The raw properties object
874
+ */
875
+ toJSON() {
876
+ return this.props;
877
+ }
878
+ };
879
+
880
+ export { AggregateRoot, CommandBus, Entity, EventBusImpl, EventSourcedAggregate, QueryBus, ValueObject, aggregate, bump, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepFreeze, entityIds, findEntityById, guard, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, sameEntity, sameVersion, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, voWithValidationUnsafe, withCommit };
881
+ //# sourceMappingURL=index.js.map
2
882
  //# sourceMappingURL=index.js.map