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

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,1145 @@
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
+ import { err, ok } from '@shirudo/result';
2
+
3
+ var __defProp = Object.defineProperty;
4
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
5
+
6
+ // src/utils/array/is-built-in.ts
7
+ var BUILT_IN_TAGS = /* @__PURE__ */ new Set([
8
+ "[object Date]",
9
+ "[object RegExp]",
10
+ "[object Map]",
11
+ "[object Set]",
12
+ "[object WeakMap]",
13
+ "[object WeakSet]",
14
+ "[object Promise]",
15
+ "[object Error]",
16
+ "[object Boolean]",
17
+ "[object Number]",
18
+ "[object String]",
19
+ "[object ArrayBuffer]",
20
+ "[object SharedArrayBuffer]",
21
+ "[object DataView]"
22
+ ]);
23
+ function isBuiltInObject(obj, tag) {
24
+ if (tag.endsWith("Array]")) return true;
25
+ if (ArrayBuffer.isView(obj)) return true;
26
+ return BUILT_IN_TAGS.has(tag);
27
+ }
28
+ __name(isBuiltInObject, "isBuiltInObject");
29
+
30
+ // src/utils/array/deep-equal.ts
31
+ var objProto = Object.prototype;
32
+ var objToString = objProto.toString;
33
+ var objHasOwn = objProto.hasOwnProperty;
34
+ function deepEqual(a, b) {
35
+ return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
36
+ }
37
+ __name(deepEqual, "deepEqual");
38
+ function deepEqualInner(a, b, visited) {
39
+ if (a === b) return true;
40
+ const typeA = typeof a;
41
+ const typeB = typeof b;
42
+ if (typeA !== "object" || a === null || typeB !== "object" || b === null) {
43
+ if (typeA === "number" && typeB === "number") {
44
+ return Number.isNaN(a) && Number.isNaN(b);
45
+ }
46
+ return false;
47
+ }
48
+ const objA = a;
49
+ const objB = b;
50
+ let cachedBs = visited.get(objA);
51
+ if (cachedBs?.has(objB)) {
52
+ return true;
53
+ }
54
+ if (!cachedBs) {
55
+ cachedBs = /* @__PURE__ */ new WeakSet();
56
+ visited.set(objA, cachedBs);
57
+ }
58
+ cachedBs.add(objB);
59
+ if (ArrayBuffer.isView(objA) || ArrayBuffer.isView(objB)) {
60
+ if (!ArrayBuffer.isView(objA) || !ArrayBuffer.isView(objB)) return false;
61
+ const tagA2 = objToString.call(objA);
62
+ const tagB2 = objToString.call(objB);
63
+ if (tagA2 !== tagB2) return false;
64
+ if (tagA2 === "[object DataView]") {
65
+ const viewA = objA;
66
+ const viewB = objB;
67
+ if (viewA.byteLength !== viewB.byteLength) return false;
68
+ const len2 = viewA.byteLength;
69
+ for (let i = 0; i < len2; i++) {
70
+ if (viewA.getUint8(i) !== viewB.getUint8(i)) return false;
71
+ }
72
+ return true;
73
+ }
74
+ const arrA = objA;
75
+ const arrB = objB;
76
+ const len = arrA.length;
77
+ if (len !== arrB.length) return false;
78
+ for (let i = 0; i < len; i++) {
79
+ if (arrA[i] !== arrB[i]) return false;
80
+ }
81
+ return true;
82
+ }
83
+ const tagA = objToString.call(objA);
84
+ const tagB = objToString.call(objB);
85
+ if (tagA !== tagB) return false;
86
+ switch (tagA) {
87
+ case "[object Array]": {
88
+ const arrA = objA;
89
+ const arrB = objB;
90
+ const len = arrA.length;
91
+ if (len !== arrB.length) return false;
92
+ for (let i = 0; i < len; i++) {
93
+ if (!deepEqualInner(arrA[i], arrB[i], visited)) return false;
94
+ }
95
+ return true;
96
+ }
97
+ case "[object Map]": {
98
+ const mapA = objA;
99
+ const mapB = objB;
100
+ if (mapA.size !== mapB.size) return false;
101
+ for (const [key, valA] of mapA) {
102
+ if (!mapB.has(key)) return false;
103
+ const valB = mapB.get(key);
104
+ if (!deepEqualInner(valA, valB, visited)) return false;
105
+ }
106
+ return true;
107
+ }
108
+ case "[object Set]": {
109
+ const setA = objA;
110
+ const setB = objB;
111
+ if (setA.size !== setB.size) return false;
112
+ for (const value of setA) {
113
+ if (!setB.has(value)) return false;
114
+ }
115
+ return true;
116
+ }
117
+ case "[object Date]": {
118
+ const timeA = objA.getTime();
119
+ const timeB = objB.getTime();
120
+ return timeA === timeB;
121
+ }
122
+ case "[object RegExp]": {
123
+ const regA = objA;
124
+ const regB = objB;
125
+ return regA.source === regB.source && regA.flags === regB.flags;
126
+ }
127
+ case "[object Boolean]":
128
+ case "[object Number]":
129
+ case "[object String]": {
130
+ return objA.valueOf() === objB.valueOf();
131
+ }
132
+ default: {
133
+ if (isBuiltInObject(objA, tagA) && isBuiltInObject(objB, tagB)) {
134
+ return objA === objB;
135
+ }
136
+ const recA = objA;
137
+ const recB = objB;
138
+ const stringKeysA = Object.keys(objA);
139
+ const stringKeysB = Object.keys(objB);
140
+ if (stringKeysA.length !== stringKeysB.length) return false;
141
+ const symbolKeysA = Object.getOwnPropertySymbols(objA);
142
+ const symbolKeysB = Object.getOwnPropertySymbols(objB);
143
+ if (symbolKeysA.length !== symbolKeysB.length) return false;
144
+ const symbolKeysBSet = new Set(symbolKeysB);
145
+ for (const key of stringKeysA) {
146
+ if (!objHasOwn.call(objB, key)) return false;
147
+ }
148
+ for (const key of symbolKeysA) {
149
+ if (!symbolKeysBSet.has(key)) return false;
150
+ }
151
+ for (const key of stringKeysA) {
152
+ if (!deepEqualInner(recA[key], recB[key], visited)) {
153
+ return false;
154
+ }
155
+ }
156
+ for (const key of symbolKeysA) {
157
+ if (!deepEqualInner(recA[key], recB[key], visited)) {
158
+ return false;
159
+ }
160
+ }
161
+ return true;
162
+ }
163
+ }
164
+ }
165
+ __name(deepEqualInner, "deepEqualInner");
166
+
167
+ // src/utils/array/deep-omit.ts
168
+ function deepOmit(value, options) {
169
+ const visited = /* @__PURE__ */ new WeakMap();
170
+ const ignoreKeys = options.ignoreKeys ? new Set(options.ignoreKeys) : void 0;
171
+ return omitInternal(value, options, ignoreKeys, [], visited);
172
+ }
173
+ __name(deepOmit, "deepOmit");
174
+ function omitInternal(value, options, ignoreKeys, path, visited) {
175
+ if (value === null) return value;
176
+ if (typeof value !== "object") return value;
177
+ const obj = value;
178
+ if (visited.has(obj)) {
179
+ return visited.get(obj);
180
+ }
181
+ const tag = Object.prototype.toString.call(obj);
182
+ if (tag === "[object Array]") {
183
+ const arr = obj;
184
+ const clone2 = new Array(arr.length);
185
+ visited.set(obj, clone2);
186
+ for (let i = 0; i < arr.length; i++) {
187
+ path.push(i);
188
+ clone2[i] = omitInternal(arr[i], options, ignoreKeys, path, visited);
189
+ path.pop();
190
+ }
191
+ return clone2;
192
+ }
193
+ if (isBuiltInObject(obj, tag)) {
194
+ const builtInClone = cloneBuiltIn(obj, tag);
195
+ visited.set(obj, builtInClone);
196
+ return builtInClone;
197
+ }
198
+ const clone = Object.create(Object.getPrototypeOf(obj));
199
+ visited.set(obj, clone);
200
+ const stringKeys = Object.keys(obj);
201
+ const symbolKeys = Object.getOwnPropertySymbols(obj);
202
+ for (const key of stringKeys) {
203
+ if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
204
+ path.push(key);
205
+ assignOwn(
206
+ clone,
207
+ key,
208
+ omitInternal(
209
+ obj[key],
210
+ options,
211
+ ignoreKeys,
212
+ path,
213
+ visited
214
+ )
215
+ );
216
+ path.pop();
217
+ }
218
+ for (const key of symbolKeys) {
219
+ if (shouldIgnoreKey(key, path, ignoreKeys, options)) continue;
220
+ path.push(key);
221
+ assignOwn(
222
+ clone,
223
+ key,
224
+ omitInternal(
225
+ obj[key],
226
+ options,
227
+ ignoreKeys,
228
+ path,
229
+ visited
230
+ )
231
+ );
232
+ path.pop();
233
+ }
234
+ return clone;
235
+ }
236
+ __name(omitInternal, "omitInternal");
237
+ function assignOwn(target, key, value) {
238
+ Object.defineProperty(target, key, {
239
+ value,
240
+ writable: true,
241
+ enumerable: true,
242
+ configurable: true
243
+ });
244
+ }
245
+ __name(assignOwn, "assignOwn");
246
+ function cloneBuiltIn(obj, tag) {
247
+ switch (tag) {
248
+ case "[object Date]":
249
+ return new Date(obj.getTime());
250
+ case "[object RegExp]": {
251
+ const re = obj;
252
+ const copy = new RegExp(re.source, re.flags);
253
+ copy.lastIndex = re.lastIndex;
254
+ return copy;
255
+ }
256
+ case "[object Map]": {
257
+ const m = obj;
258
+ return new Map(m);
259
+ }
260
+ case "[object Set]": {
261
+ const s = obj;
262
+ return new Set(s);
263
+ }
264
+ default:
265
+ return structuredClone(obj);
266
+ }
267
+ }
268
+ __name(cloneBuiltIn, "cloneBuiltIn");
269
+ function shouldIgnoreKey(key, path, ignoreKeys, options) {
270
+ if (ignoreKeys?.has(key)) return true;
271
+ if (options.ignoreKeyPredicate?.(key, path)) return true;
272
+ return false;
273
+ }
274
+ __name(shouldIgnoreKey, "shouldIgnoreKey");
275
+
276
+ // src/utils/array/deep-equal-except.ts
277
+ function deepEqualExcept(a, b, options) {
278
+ const prunedA = deepOmit(a, options);
279
+ const prunedB = deepOmit(b, options);
280
+ return deepEqual(prunedA, prunedB);
281
+ }
282
+ __name(deepEqualExcept, "deepEqualExcept");
283
+ function deepFreeze(obj, visited = /* @__PURE__ */ new WeakSet()) {
284
+ if (obj === null || typeof obj !== "object") {
285
+ return obj;
286
+ }
287
+ if (visited.has(obj)) {
288
+ return obj;
289
+ }
290
+ visited.add(obj);
291
+ const keys = Reflect.ownKeys(obj);
292
+ for (const key of keys) {
293
+ const value = obj[key];
294
+ if (value !== null && typeof value === "object") {
295
+ deepFreeze(value, visited);
296
+ }
297
+ }
298
+ return Object.freeze(obj);
299
+ }
300
+ __name(deepFreeze, "deepFreeze");
301
+ function vo(t) {
302
+ return deepFreeze(structuredClone(t));
303
+ }
304
+ __name(vo, "vo");
305
+ function voEquals(a, b) {
306
+ return deepEqual(a, b);
307
+ }
308
+ __name(voEquals, "voEquals");
309
+ function voEqualsExcept(a, b, options) {
310
+ return deepEqualExcept(a, b, options);
311
+ }
312
+ __name(voEqualsExcept, "voEqualsExcept");
313
+ function voWithValidation(t, validate, errorMessage) {
314
+ if (!validate(t)) {
315
+ return err(
316
+ errorMessage ?? `Validation failed for value object: ${JSON.stringify(t)}`
317
+ );
318
+ }
319
+ return ok(vo(t));
320
+ }
321
+ __name(voWithValidation, "voWithValidation");
322
+ var ValueObject = class {
323
+ static {
324
+ __name(this, "ValueObject");
325
+ }
326
+ props;
327
+ /**
328
+ * Creates a new ValueObject.
329
+ * The properties are deeply frozen to ensure immutability.
330
+ *
331
+ * @param props - The properties of the value object
332
+ * @example
333
+ * ```ts
334
+ * class Money extends ValueObject<{ amount: number; currency: string }> {
335
+ * constructor(props: { amount: number; currency: string }) {
336
+ * super(props);
337
+ * }
338
+ *
339
+ * protected validate(props: { amount: number; currency: string }): void {
340
+ * if (props.amount < 0) throw new Error("Amount cannot be negative");
341
+ * }
342
+ * }
343
+ * ```
344
+ */
345
+ constructor(props) {
346
+ this.validate(props);
347
+ this.props = deepFreeze({ ...props });
348
+ }
349
+ /**
350
+ * Optional validation hook that can be overridden by subclasses.
351
+ * Should throw an error if validation fails.
352
+ *
353
+ * @param props - The properties to validate
354
+ * @throws Error if validation fails
355
+ */
356
+ validate(props) {
357
+ }
358
+ /**
359
+ * Checks if this value object is equal to another.
360
+ * Uses deep equality comparison on the properties and checks for constructor equality.
361
+ *
362
+ * @param other - The other value object to compare
363
+ * @returns true if the properties are deeply equal and constructors match
364
+ */
365
+ equals(other) {
366
+ if (other === null || other === void 0) {
367
+ return false;
368
+ }
369
+ if (this.constructor !== other.constructor) {
370
+ return false;
371
+ }
372
+ return deepEqual(this.props, other.props);
373
+ }
374
+ /**
375
+ * Creates a clone of the value object with optional property overrides.
376
+ *
377
+ * @param props - Optional properties to override
378
+ * @returns A new instance of the value object
379
+ */
380
+ clone(props) {
381
+ const Constructor = this.constructor;
382
+ return new Constructor({ ...this.props, ...props || {} });
383
+ }
384
+ /**
385
+ * Serializes the value object to its raw properties for JSON operations.
386
+ *
387
+ * @returns The raw properties object
388
+ */
389
+ toJSON() {
390
+ return this.props;
391
+ }
392
+ };
393
+
394
+ // src/aggregate/domain-event.ts
395
+ var defaultEventIdFactory = /* @__PURE__ */ __name(() => crypto.randomUUID(), "defaultEventIdFactory");
396
+ var currentEventIdFactory = defaultEventIdFactory;
397
+ function setEventIdFactory(factory) {
398
+ currentEventIdFactory = factory;
399
+ }
400
+ __name(setEventIdFactory, "setEventIdFactory");
401
+ function resetEventIdFactory() {
402
+ currentEventIdFactory = defaultEventIdFactory;
403
+ }
404
+ __name(resetEventIdFactory, "resetEventIdFactory");
405
+ var defaultClockFactory = /* @__PURE__ */ __name(() => /* @__PURE__ */ new Date(), "defaultClockFactory");
406
+ var currentClockFactory = defaultClockFactory;
407
+ function setClockFactory(factory) {
408
+ currentClockFactory = factory;
409
+ }
410
+ __name(setClockFactory, "setClockFactory");
411
+ function resetClockFactory() {
412
+ currentClockFactory = defaultClockFactory;
413
+ }
414
+ __name(resetClockFactory, "resetClockFactory");
415
+ function createDomainEvent(type, payload, options) {
416
+ const event = {
417
+ eventId: options?.eventId ?? currentEventIdFactory(),
418
+ type,
419
+ aggregateId: options?.aggregateId,
420
+ aggregateType: options?.aggregateType,
421
+ payload,
422
+ occurredAt: options?.occurredAt ?? currentClockFactory(),
423
+ version: options?.version ?? 1,
424
+ metadata: options?.metadata
425
+ };
426
+ return deepFreeze(event);
427
+ }
428
+ __name(createDomainEvent, "createDomainEvent");
429
+ function createDomainEventWithMetadata(type, payload, metadata, options) {
430
+ return createDomainEvent(type, payload, {
431
+ ...options,
432
+ metadata
433
+ });
434
+ }
435
+ __name(createDomainEventWithMetadata, "createDomainEventWithMetadata");
436
+ function copyMetadata(sourceEvent, additionalMetadata) {
437
+ return {
438
+ ...sourceEvent.metadata ?? {},
439
+ ...additionalMetadata ?? {}
440
+ };
441
+ }
442
+ __name(copyMetadata, "copyMetadata");
443
+ function mergeMetadata(...metadataObjects) {
444
+ return Object.assign({}, ...metadataObjects.filter(Boolean));
445
+ }
446
+ __name(mergeMetadata, "mergeMetadata");
447
+
448
+ // src/aggregate/aggregate.ts
449
+ function sameVersion(a, b) {
450
+ return a.id === b.id && a.version === b.version;
451
+ }
452
+ __name(sameVersion, "sameVersion");
453
+
454
+ // src/entity/entity.ts
455
+ var Entity = class {
456
+ static {
457
+ __name(this, "Entity");
458
+ }
459
+ id;
460
+ /**
461
+ * Returns the current state of the entity.
462
+ *
463
+ * The state object is **shallowly frozen** — direct property writes
464
+ * (`entity.state.foo = …`) throw in strict mode, but writes to nested
465
+ * objects (`entity.state.address.zip = …`) bypass the freeze. For deep
466
+ * immutability either model nested data with `vo()` (which freezes
467
+ * deeply) or reach for a structural-sharing library like Immer at the
468
+ * App layer. The shallow contract is intentional: deep freezing on
469
+ * every state write is too expensive for hot paths, and DDD aggregates
470
+ * normally treat their own state as private (`Tell, Don't Ask`).
471
+ */
472
+ get state() {
473
+ return this._state;
474
+ }
475
+ /**
476
+ * The state is 'protected' so that only the subclass can modify it.
477
+ * Subclasses can mutate this directly or use helper methods.
478
+ */
479
+ _state;
480
+ constructor(id, initialState) {
481
+ if (id === null || id === void 0) {
482
+ throw new Error("Entity ID cannot be null or undefined");
483
+ }
484
+ this.id = id;
485
+ this._state = freezeShallow(initialState);
486
+ this.validateState(this._state);
487
+ }
488
+ /**
489
+ * Optional validation hook to ensure state invariants. Called during
490
+ * construction (from `Entity`'s constructor) and again on every
491
+ * `setState()` call. Throw to reject invalid state.
492
+ *
493
+ * **⚠️ Must not read subclass instance fields via `this`.** The
494
+ * constructor calls `validateState(initialState)` BEFORE the subclass's
495
+ * field initializers run, so `this.someField` is `undefined` at that
496
+ * point — a classic TypeScript/JavaScript constructor-ordering footgun.
497
+ * The `state` argument is the single source of truth; treat the method
498
+ * as pure with respect to `this`.
499
+ *
500
+ * If your invariants genuinely depend on per-instance configuration
501
+ * that isn't part of the state, pass that configuration into the state
502
+ * itself (DDD-canonical: the aggregate's state contains everything it
503
+ * needs) or perform the additional check after construction in a
504
+ * dedicated factory method.
505
+ *
506
+ * @param state - The state to validate
507
+ * @throws Error (or `DomainError` subclass) if validation fails
508
+ */
509
+ validateState(_state) {
510
+ }
511
+ /**
512
+ * Sets the state of the entity.
513
+ * This is a convenience method for state mutations.
514
+ * Automatically validates the newState using `validateState()`.
515
+ *
516
+ * @param newState - The new state
517
+ */
518
+ setState(newState) {
519
+ this.validateState(newState);
520
+ this._state = freezeShallow(newState);
521
+ }
522
+ };
523
+ function freezeShallow(value) {
524
+ if (value !== null && typeof value === "object") {
525
+ return Object.freeze(value);
526
+ }
527
+ return value;
528
+ }
529
+ __name(freezeShallow, "freezeShallow");
530
+ function sameEntity(a, b) {
531
+ return a.id === b.id;
532
+ }
533
+ __name(sameEntity, "sameEntity");
534
+ function findEntityById(entities, id) {
535
+ return entities.find((entity) => entity.id === id);
536
+ }
537
+ __name(findEntityById, "findEntityById");
538
+ function hasEntityId(entities, id) {
539
+ return entities.some((entity) => entity.id === id);
540
+ }
541
+ __name(hasEntityId, "hasEntityId");
542
+ function removeEntityById(entities, id) {
543
+ return entities.filter((entity) => entity.id !== id);
544
+ }
545
+ __name(removeEntityById, "removeEntityById");
546
+ function updateEntityById(entities, id, updater) {
547
+ return entities.map((entity) => entity.id === id ? updater(entity) : entity);
548
+ }
549
+ __name(updateEntityById, "updateEntityById");
550
+ function replaceEntityById(entities, id, replacement) {
551
+ return entities.map((entity) => entity.id === id ? replacement : entity);
552
+ }
553
+ __name(replaceEntityById, "replaceEntityById");
554
+ function entityIds(entities) {
555
+ return entities.map((entity) => entity.id);
556
+ }
557
+ __name(entityIds, "entityIds");
558
+
559
+ // src/aggregate/aggregate-root.ts
560
+ var AggregateRoot = class extends Entity {
561
+ static {
562
+ __name(this, "AggregateRoot");
563
+ }
564
+ _version = 0;
565
+ get version() {
566
+ return this._version;
567
+ }
568
+ setVersion(version) {
569
+ this._version = version;
570
+ }
571
+ _config;
572
+ _autoVersionBump;
573
+ _domainEvents = [];
574
+ /**
575
+ * Returns a read-only list of domain events recorded by this aggregate.
576
+ * These events are side-effects of state changes.
577
+ */
578
+ get domainEvents() {
579
+ return Object.freeze(this._domainEvents.slice());
580
+ }
581
+ /**
582
+ * Clears the list of recorded domain events.
583
+ * Call this after dispatching the events.
584
+ */
585
+ clearDomainEvents() {
586
+ this._domainEvents = [];
587
+ }
588
+ /**
589
+ * Post-save hook called by a `Repository.save()` implementation to push
590
+ * the persisted version back into the in-memory aggregate and clear the
591
+ * recorded domain events (they are now safely on the write side / in
592
+ * the outbox).
593
+ *
594
+ * Use this so `save()` can keep its `Promise<void>` return type: the
595
+ * caller holds the aggregate reference, which is up to date after this
596
+ * call.
597
+ */
598
+ markPersisted(version) {
599
+ this.setVersion(version);
600
+ this._domainEvents = [];
601
+ }
602
+ /**
603
+ * Mutates state and records the resulting domain events in the
604
+ * **canonical record-after-mutation order**. Use this instead of calling
605
+ * `setState` + `addDomainEvent` separately and you cannot trip the
606
+ * "event for a fact that never happened" footgun.
607
+ *
608
+ * Order of operations:
609
+ * 1. `setState(newState, true)` — runs `validateState` first.
610
+ * If it throws, the method propagates and **no event is recorded
611
+ * and no version is bumped**.
612
+ * 2. Each event in `events` is appended via `addDomainEvent`.
613
+ *
614
+ * `commit()` **always bumps the version**, regardless of the aggregate's
615
+ * `autoVersionBump` config. Recording a domain event implies "something
616
+ * happened that the outside world cares about", and optimistic-
617
+ * concurrency callers must see a fresh version every time. The config
618
+ * still governs the un-coupled `setState` path. If you need to mutate
619
+ * state without bumping (e.g. cosmetic caches), call `setState(newState,
620
+ * false)` and skip `commit` entirely.
621
+ *
622
+ * `events` accepts a single event or an array. Omit it (or pass `[]`)
623
+ * for state-only mutations.
624
+ *
625
+ * @example
626
+ * ```ts
627
+ * confirm(): void {
628
+ * if (this.state.status === "confirmed") {
629
+ * throw new OrderAlreadyConfirmedError(this.id);
630
+ * }
631
+ * this.commit(
632
+ * { ...this.state, status: "confirmed" },
633
+ * { type: "OrderConfirmed", orderId: this.id },
634
+ * );
635
+ * }
636
+ * ```
637
+ *
638
+ * `EventSourcedAggregate.apply()` enforces the same ordering
639
+ * structurally; `commit()` is the opt-in equivalent on `AggregateRoot`,
640
+ * where `setState` and `addDomainEvent` are otherwise decoupled and the
641
+ * ordering is convention-only.
642
+ *
643
+ * @param newState - The new state (validated by `validateState`)
644
+ * @param events - One event, an array of events, or none (default)
645
+ */
646
+ commit(newState, events = []) {
647
+ this.setState(newState, true);
648
+ const list = Array.isArray(events) ? events : [events];
649
+ for (const ev of list) {
650
+ this.addDomainEvent(ev);
651
+ }
652
+ }
653
+ constructor(id, initialState, config) {
654
+ super(id, initialState);
655
+ this._config = config ?? {};
656
+ this._autoVersionBump = this._config.autoVersionBump ?? false;
657
+ }
658
+ /**
659
+ * Records a domain event for later publication.
660
+ *
661
+ * **Ordering: record AFTER state mutation.** Vernon (IDDD §8) is
662
+ * explicit: a domain event describes something that has just happened
663
+ * to the aggregate — its existence implies the state change already
664
+ * occurred. Concretely:
665
+ *
666
+ * ```ts
667
+ * confirm(): void {
668
+ * if (this.state.status === "confirmed") {
669
+ * throw new OrderAlreadyConfirmedError(this.id);
670
+ * }
671
+ * this.setState({ ...this.state, status: "confirmed" }, true);
672
+ * this.addDomainEvent({ type: "OrderConfirmed", orderId: this.id });
673
+ * // ↑ post-mutation. The event represents the committed fact.
674
+ * }
675
+ * ```
676
+ *
677
+ * Recording before mutation is a footgun: if a subsequent invariant
678
+ * check throws, the event has already been queued but the state never
679
+ * actually changed — consumers see an event for a fact that did not
680
+ * happen.
681
+ *
682
+ * `EventSourcedAggregate.apply()` enforces this ordering structurally;
683
+ * `AggregateRoot` leaves it as a convention because the state-mutation
684
+ * path (`setState`) is decoupled from event recording.
685
+ *
686
+ * @param event - The domain event to record
687
+ */
688
+ addDomainEvent(event) {
689
+ this._domainEvents.push(event);
690
+ }
691
+ /**
692
+ * Manually bumps the aggregate version.
693
+ * Call this after state changes for Optimistic Concurrency Control.
694
+ *
695
+ * If `autoVersionBump` is enabled, this is called automatically
696
+ * when using `setState()`.
697
+ */
698
+ bumpVersion() {
699
+ this.setVersion(this._version + 1);
700
+ }
701
+ /**
702
+ * Sets the state and optionally bumps the version automatically.
703
+ * This is a convenience method for state mutations.
704
+ * Automatically validates the newState using `validateState()`.
705
+ * Overrides Entity.setState to add version bumping.
706
+ *
707
+ * @param newState - The new state
708
+ * @param bumpVersion - Whether to bump the version (defaults to autoVersionBump config)
709
+ */
710
+ setState(newState, bumpVersion) {
711
+ super.setState(newState);
712
+ const shouldBump = bumpVersion ?? this._autoVersionBump;
713
+ if (shouldBump) {
714
+ this.bumpVersion();
715
+ }
716
+ }
717
+ /**
718
+ * Creates a snapshot of the current aggregate state.
719
+ * Useful for performance optimization, backup/restore, and audit trails.
720
+ *
721
+ * @returns A snapshot containing the current state and version
722
+ *
723
+ * @example
724
+ * ```typescript
725
+ * const snapshot = aggregate.createSnapshot();
726
+ * await snapshotRepository.save(aggregate.id, snapshot);
727
+ * ```
728
+ */
729
+ createSnapshot() {
730
+ return {
731
+ state: structuredClone(this._state),
732
+ version: this.version,
733
+ snapshotAt: /* @__PURE__ */ new Date()
734
+ };
735
+ }
736
+ /**
737
+ * Restores the aggregate from a snapshot.
738
+ * This is useful for loading aggregates from snapshots instead of
739
+ * rebuilding them from scratch.
740
+ * Validates the restored state.
741
+ *
742
+ * @param snapshot - The snapshot to restore from
743
+ *
744
+ * @example
745
+ * ```typescript
746
+ * const snapshot = await snapshotRepository.getLatest(aggregateId);
747
+ * aggregate.restoreFromSnapshot(snapshot);
748
+ * ```
749
+ */
750
+ restoreFromSnapshot(snapshot) {
751
+ this.validateState(snapshot.state);
752
+ this._state = freezeShallow(snapshot.state);
753
+ this.setVersion(snapshot.version);
754
+ }
755
+ };
756
+
757
+ // src/core/errors.ts
758
+ var DomainError = class extends Error {
759
+ static {
760
+ __name(this, "DomainError");
761
+ }
762
+ constructor(message, options) {
763
+ super(message, options);
764
+ this.name = new.target.name;
765
+ Object.setPrototypeOf(this, new.target.prototype);
766
+ }
767
+ };
768
+ var MissingHandlerError = class extends DomainError {
769
+ constructor(eventType) {
770
+ super(`Missing handler for event type: ${eventType}`);
771
+ this.eventType = eventType;
772
+ }
773
+ static {
774
+ __name(this, "MissingHandlerError");
775
+ }
776
+ };
777
+ var AggregateNotFoundError = class extends DomainError {
778
+ constructor(aggregateType, id) {
779
+ super(`Aggregate not found: ${aggregateType}(${id})`);
780
+ this.aggregateType = aggregateType;
781
+ this.id = id;
782
+ }
783
+ static {
784
+ __name(this, "AggregateNotFoundError");
785
+ }
786
+ };
787
+ var ConcurrencyConflictError = class extends DomainError {
788
+ constructor(aggregateType, aggregateId, expectedVersion, actualVersion) {
789
+ super(
790
+ `Concurrency conflict on ${aggregateType}(${aggregateId}): expected version ${expectedVersion}, actual ${actualVersion}`
791
+ );
792
+ this.aggregateType = aggregateType;
793
+ this.aggregateId = aggregateId;
794
+ this.expectedVersion = expectedVersion;
795
+ this.actualVersion = actualVersion;
796
+ }
797
+ static {
798
+ __name(this, "ConcurrencyConflictError");
799
+ }
800
+ };
801
+
802
+ // src/aggregate/event-sourced-aggregate.ts
803
+ var EventSourcedAggregate = class extends Entity {
804
+ static {
805
+ __name(this, "EventSourcedAggregate");
806
+ }
807
+ // --- Version management (own, not inherited from AggregateRoot) ---
808
+ _version = 0;
809
+ get version() {
810
+ return this._version;
811
+ }
812
+ setVersion(version) {
813
+ this._version = version;
814
+ }
815
+ // --- Event tracking ---
816
+ _pendingEvents = [];
817
+ _autoVersionBump;
818
+ get pendingEvents() {
819
+ return Object.freeze(this._pendingEvents.slice());
820
+ }
821
+ clearPendingEvents() {
822
+ this._pendingEvents = [];
823
+ }
824
+ /**
825
+ * Post-save hook called by a `Repository.save()` implementation to push
826
+ * the persisted version back into the in-memory aggregate and clear the
827
+ * pending events (they are now in the event store / outbox). Lets
828
+ * `save()` keep its `Promise<void>` return type.
829
+ */
830
+ markPersisted(version) {
831
+ this.setVersion(version);
832
+ this._pendingEvents = [];
833
+ }
834
+ constructor(id, initialState, config) {
835
+ super(id, initialState);
836
+ this._autoVersionBump = config?.autoVersionBump ?? true;
837
+ }
838
+ // --- Event application ---
839
+ /**
840
+ * Validates an event before it is applied. Default is no-op.
841
+ * Subclasses override to throw a concrete `DomainError` subclass when
842
+ * the event violates an invariant in the current state.
843
+ */
844
+ validateEvent(_event) {
845
+ }
846
+ /**
847
+ * Applies an event: validates, locates the handler, computes the next
848
+ * state, then commits state + pending event + version bump atomically.
849
+ *
850
+ * Throws `DomainError` (or a subclass) on validation failure.
851
+ * Throws `MissingHandlerError` if no handler is registered for `event.type`.
852
+ *
853
+ * State is not mutated if any step throws — the handler is invoked into
854
+ * a local and only assigned to `_state` once all checks pass.
855
+ *
856
+ * The method is generic in the event tag `K`, so concrete callers
857
+ * (`this.apply(orderCreated)`) narrow to the literal tag and the
858
+ * dispatched handler is typed as `Handler<TState, Extract<TEvent, { type: K }>>`
859
+ * — no `as` cast required at the call site.
860
+ *
861
+ * @param event - The domain event to apply
862
+ * @param isNew - Whether the event is new (needs persisting) or replayed from history
863
+ */
864
+ apply(event, isNew = true) {
865
+ this.dispatchAndCommit(event, isNew);
866
+ }
867
+ /**
868
+ * Internal dispatch path used by `apply()` and the replay methods
869
+ * (`loadFromHistory`, `restoreFromSnapshotWithEvents`). The replay loop
870
+ * iterates over `TEvent[]` and therefore cannot supply a narrowed `K`
871
+ * generic, so this helper accepts `TEvent` and the discriminator is
872
+ * resolved via the (statically-sound) `handlers` map.
873
+ */
874
+ dispatchAndCommit(event, isNew) {
875
+ this.validateEvent(event);
876
+ const handler = this.handlers[event.type];
877
+ if (!handler) {
878
+ throw new MissingHandlerError(event.type);
879
+ }
880
+ const nextState = handler(this._state, event);
881
+ this._state = freezeShallow(nextState);
882
+ if (isNew) {
883
+ this._pendingEvents.push(event);
884
+ if (this._autoVersionBump) {
885
+ this.setVersion(this._version + 1);
886
+ }
887
+ }
888
+ }
889
+ /**
890
+ * Manually bumps the aggregate version.
891
+ * Only needed if `autoVersionBump` is disabled.
892
+ */
893
+ bumpVersion() {
894
+ this.setVersion(this._version + 1);
895
+ }
896
+ // --- History & Snapshots ---
897
+ /**
898
+ * Reconstitutes the aggregate from an event history. Catches `DomainError`
899
+ * thrown during replay and returns it as an `Err` — this is the
900
+ * infrastructure boundary, where event-stream corruption is an expected
901
+ * recoverable failure. Unexpected (non-DomainError) throws propagate.
902
+ *
903
+ * Version advances additively: the aggregate's pre-existing version plus
904
+ * `history.length`. A fresh aggregate (v=0) loading 3 events ends at v=3;
905
+ * an aggregate already at v=1 (e.g. after a creation event) loading
906
+ * 2 events ends at v=3, not v=2.
907
+ */
908
+ loadFromHistory(history) {
909
+ const startVersion = this._version;
910
+ for (const event of history) {
911
+ try {
912
+ this.dispatchAndCommit(event, false);
913
+ } catch (e) {
914
+ if (e instanceof DomainError) return err(e);
915
+ throw e;
916
+ }
917
+ }
918
+ this.setVersion(startVersion + history.length);
919
+ return ok();
920
+ }
921
+ hasPendingEvents() {
922
+ return this._pendingEvents.length > 0;
923
+ }
924
+ getEventCount() {
925
+ return this._pendingEvents.length;
926
+ }
927
+ getLatestEvent() {
928
+ return this._pendingEvents[this._pendingEvents.length - 1];
929
+ }
930
+ /**
931
+ * Creates a snapshot of the current aggregate state.
932
+ */
933
+ createSnapshot() {
934
+ return {
935
+ state: structuredClone(this._state),
936
+ version: this._version,
937
+ snapshotAt: /* @__PURE__ */ new Date()
938
+ };
939
+ }
940
+ /**
941
+ * Restores the aggregate from a snapshot and applies events that occurred
942
+ * after. Same infrastructure-boundary semantics as `loadFromHistory`:
943
+ * catches `DomainError` and returns it as an `Err`; non-domain throws
944
+ * propagate.
945
+ *
946
+ * All-or-nothing: if any event mid-stream throws a `DomainError`, the
947
+ * aggregate is rolled back to its pre-call state + version. Partial
948
+ * restoration is never observable to the caller.
949
+ */
950
+ restoreFromSnapshotWithEvents(snapshot, eventsAfterSnapshot) {
951
+ const previousState = this._state;
952
+ const previousVersion = this._version;
953
+ this._state = freezeShallow(snapshot.state);
954
+ this.setVersion(snapshot.version);
955
+ for (const event of eventsAfterSnapshot) {
956
+ try {
957
+ this.dispatchAndCommit(event, false);
958
+ } catch (e) {
959
+ this._state = previousState;
960
+ this.setVersion(previousVersion);
961
+ if (e instanceof DomainError) return err(e);
962
+ throw e;
963
+ }
964
+ }
965
+ this.setVersion(snapshot.version + eventsAfterSnapshot.length);
966
+ return ok();
967
+ }
968
+ };
969
+ var CommandBus = class {
970
+ static {
971
+ __name(this, "CommandBus");
972
+ }
973
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
974
+ handlers = /* @__PURE__ */ new Map();
975
+ register(commandType, handler) {
976
+ this.handlers.set(commandType, handler);
977
+ }
978
+ async execute(command) {
979
+ const handler = this.handlers.get(command.type);
980
+ if (!handler) {
981
+ return err(`No handler registered for command type: ${command.type}`);
982
+ }
983
+ try {
984
+ return await handler(command);
985
+ } catch (error) {
986
+ return err(
987
+ error instanceof Error ? error.message : String(error)
988
+ );
989
+ }
990
+ }
991
+ };
992
+
993
+ // src/app/handler.ts
994
+ async function withCommit(deps, fn) {
995
+ const { result, events } = await deps.scope.transactional(async () => {
996
+ const fnResult = await fn();
997
+ await deps.outbox.add(fnResult.events);
998
+ return fnResult;
999
+ });
1000
+ if (deps.bus) {
1001
+ await deps.bus.publish(events);
1002
+ }
1003
+ return result;
1004
+ }
1005
+ __name(withCommit, "withCommit");
1006
+ var QueryBus = class {
1007
+ static {
1008
+ __name(this, "QueryBus");
1009
+ }
1010
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1011
+ handlers = /* @__PURE__ */ new Map();
1012
+ register(queryType, handler) {
1013
+ this.handlers.set(queryType, handler);
1014
+ }
1015
+ async execute(query) {
1016
+ const handler = this.handlers.get(query.type);
1017
+ if (!handler) {
1018
+ return err(`No handler registered for query type: ${query.type}`);
1019
+ }
1020
+ try {
1021
+ const result = await handler(query);
1022
+ return ok(result);
1023
+ } catch (error) {
1024
+ return err(
1025
+ error instanceof Error ? error.message : String(error)
1026
+ );
1027
+ }
1028
+ }
1029
+ async executeUnsafe(query) {
1030
+ const handler = this.handlers.get(query.type);
1031
+ if (!handler) {
1032
+ throw new Error(`No handler registered for query type: ${query.type}`);
1033
+ }
1034
+ return handler(query);
1035
+ }
1036
+ };
1037
+
1038
+ // src/events/event-bus.ts
1039
+ var EventBusImpl = class {
1040
+ static {
1041
+ __name(this, "EventBusImpl");
1042
+ }
1043
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1044
+ handlers = /* @__PURE__ */ new Map();
1045
+ subscribe(eventType, handler) {
1046
+ const type = eventType;
1047
+ if (!this.handlers.has(type)) {
1048
+ this.handlers.set(type, []);
1049
+ }
1050
+ const handlersForType = this.handlers.get(type);
1051
+ const casted = handler;
1052
+ handlersForType.push(casted);
1053
+ let removed = false;
1054
+ return () => {
1055
+ if (removed) return;
1056
+ const idx = handlersForType.indexOf(casted);
1057
+ if (idx !== -1) {
1058
+ handlersForType.splice(idx, 1);
1059
+ removed = true;
1060
+ }
1061
+ if (handlersForType.length === 0) {
1062
+ this.handlers.delete(type);
1063
+ }
1064
+ };
1065
+ }
1066
+ once(eventType, options) {
1067
+ return new Promise((resolve, reject) => {
1068
+ if (options?.signal?.aborted) {
1069
+ reject(options.signal.reason ?? new Error("EventBus.once aborted"));
1070
+ return;
1071
+ }
1072
+ let timer;
1073
+ let settled = false;
1074
+ let abortListener;
1075
+ const cleanup = /* @__PURE__ */ __name(() => {
1076
+ if (settled) return;
1077
+ settled = true;
1078
+ unsubscribe();
1079
+ if (timer !== void 0) clearTimeout(timer);
1080
+ if (abortListener && options?.signal) {
1081
+ options.signal.removeEventListener("abort", abortListener);
1082
+ }
1083
+ }, "cleanup");
1084
+ const unsubscribe = this.subscribe(eventType, (event) => {
1085
+ cleanup();
1086
+ resolve(event);
1087
+ });
1088
+ if (options?.signal) {
1089
+ abortListener = /* @__PURE__ */ __name(() => {
1090
+ cleanup();
1091
+ reject(
1092
+ options.signal.reason ?? new Error("EventBus.once aborted")
1093
+ );
1094
+ }, "abortListener");
1095
+ options.signal.addEventListener("abort", abortListener);
1096
+ }
1097
+ if (typeof options?.timeoutMs === "number") {
1098
+ timer = setTimeout(() => {
1099
+ cleanup();
1100
+ reject(
1101
+ new Error(
1102
+ `EventBus.once timed out after ${options.timeoutMs}ms waiting for "${eventType}"`
1103
+ )
1104
+ );
1105
+ }, options.timeoutMs);
1106
+ }
1107
+ });
1108
+ }
1109
+ /**
1110
+ * See {@link EventBus.publish} for the full ordering / parallelism /
1111
+ * error-aggregation contract this implementation realises:
1112
+ * - events in input order, sequentially;
1113
+ * - handlers within one event in parallel via `Promise.allSettled`;
1114
+ * - errors collected and thrown after the batch (single Error, or
1115
+ * `AggregateError` for multiple failures).
1116
+ */
1117
+ async publish(events) {
1118
+ const errors = [];
1119
+ for (const event of events) {
1120
+ const handlersForType = this.handlers.get(event.type);
1121
+ if (handlersForType) {
1122
+ const results = await Promise.allSettled(
1123
+ handlersForType.slice().map((handler) => handler(event))
1124
+ );
1125
+ for (const result of results) {
1126
+ if (result.status === "rejected") {
1127
+ errors.push(
1128
+ result.reason instanceof Error ? result.reason : new Error(String(result.reason))
1129
+ );
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ if (errors.length === 1) {
1135
+ throw errors[0];
1136
+ }
1137
+ if (errors.length > 1) {
1138
+ throw new AggregateError(errors, "Multiple event handlers failed");
1139
+ }
1140
+ }
1141
+ };
1142
+
1143
+ export { AggregateNotFoundError, AggregateRoot, CommandBus, ConcurrencyConflictError, DomainError, Entity, EventBusImpl, EventSourcedAggregate, MissingHandlerError, QueryBus, ValueObject, copyMetadata, createDomainEvent, createDomainEventWithMetadata, deepEqual, deepEqualExcept, deepFreeze, deepOmit, entityIds, findEntityById, freezeShallow, hasEntityId, mergeMetadata, removeEntityById, replaceEntityById, resetClockFactory, resetEventIdFactory, sameEntity, sameVersion, setClockFactory, setEventIdFactory, updateEntityById, vo, voEquals, voEqualsExcept, voWithValidation, withCommit };
1144
+ //# sourceMappingURL=index.js.map
2
1145
  //# sourceMappingURL=index.js.map