@vytches/ddd-aggregates 0.30.0 → 0.31.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/LLMGUIDE.md +99 -31
- package/README.md +5 -1
- package/dist/aggregate-interfaces.d.ts +16 -184
- package/dist/capabilities/audit-capability.d.ts +1 -1
- package/dist/capabilities/event-sourcing-capability.d.ts +1 -1
- package/dist/capabilities/snapshot-capability.d.ts +1 -1
- package/dist/capabilities/versioning-capability.d.ts +15 -2
- package/dist/core/aggregate-root.builder.d.ts +1 -1
- package/dist/core/aggregate-root.d.ts +171 -5
- package/dist/core/aggregate-utilities.d.ts +2 -2
- package/dist/core/entity.d.ts +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +298 -139
- package/package.json +8 -7
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { IAggregateCapability, IAggregateConstructorParams, IAggregateEventHandler, IAggregateRoot } from '../aggregate-interfaces';
|
|
2
|
-
import { Capability, CapabilityConstructor, EntityId, IDomainEvent, IEventMetadata } from '
|
|
2
|
+
import { Capability, CapabilityConstructor, EntityId, IDomainEvent, IEventMetadata } from '@vytches/ddd-contracts';
|
|
3
|
+
/**
|
|
4
|
+
* VF-023 (D-2, AC5, BREAKING, CRITICAL F-H4): module-private gating token
|
|
5
|
+
* for {@link AggregateRoot._internal_setState}. This is exported from this
|
|
6
|
+
* module (`./core/aggregate-root`) — NOT from the package's public barrel
|
|
7
|
+
* (`index.ts`) — so it is reachable only via a deep import within this
|
|
8
|
+
* package's own source (e.g. `SnapshotCapability`), never via
|
|
9
|
+
* `@vytches/ddd-aggregates`. Consumers cannot obtain this token, and
|
|
10
|
+
* therefore cannot call `_internal_setState` at all: TypeScript rejects the
|
|
11
|
+
* call without it, and even a `as any` cast would need the exact symbol
|
|
12
|
+
* reference, which is not exported publicly.
|
|
13
|
+
*
|
|
14
|
+
* Previously `_internal_setState` was a plain public method reachable via
|
|
15
|
+
* any `as any`/duck-typed cast — no compile-time or run-time barrier
|
|
16
|
+
* prevented domain code from bypassing `apply()`/`loadFromHistory()` and
|
|
17
|
+
* corrupting the version/event-log invariant directly.
|
|
18
|
+
*/
|
|
19
|
+
export declare const INTERNAL_STATE_TOKEN: unique symbol;
|
|
3
20
|
/**
|
|
4
21
|
* Base class for aggregate roots — the *transactional consistency boundary*
|
|
5
22
|
* of a Domain-Driven Design model. Per Eric Evans (Blue Book, ch. 6):
|
|
@@ -12,7 +29,7 @@ import { Capability, CapabilityConstructor, EntityId, IDomainEvent, IEventMetada
|
|
|
12
29
|
*
|
|
13
30
|
* `AggregateRoot<TId>` provides:
|
|
14
31
|
*
|
|
15
|
-
* - **Identity** via `EntityId<TId>` (`getId()`)
|
|
32
|
+
* - **Identity** via `EntityId<TId>` (`getId()`, `equals()`)
|
|
16
33
|
* - **Versioning** for optimistic locking (`getVersion()`,
|
|
17
34
|
* `getInitialVersion()`)
|
|
18
35
|
* - **Event emission** via `apply()` — append a `DomainEvent`, increment
|
|
@@ -102,6 +119,20 @@ export declare class AggregateRoot<TId = string> implements IAggregateRoot<TId>
|
|
|
102
119
|
private _version;
|
|
103
120
|
private _initialVersion;
|
|
104
121
|
private _domainEvents;
|
|
122
|
+
/**
|
|
123
|
+
* VP-012b (D2, F15/F5): memoized, deep-frozen view returned by
|
|
124
|
+
* {@link getDomainEvents}. Rebuilt lazily on next read only when
|
|
125
|
+
* `_domainEventsCacheDirty` is true — see `_invalidateDomainEventsCache()`.
|
|
126
|
+
*/
|
|
127
|
+
private _domainEventsCache;
|
|
128
|
+
/**
|
|
129
|
+
* VP-012b (D2): dirty flag for `_domainEventsCache`. Set true by
|
|
130
|
+
* `_invalidateDomainEventsCache()`, the single helper called from every
|
|
131
|
+
* site that mutates `_domainEvents` (apply, commit, transformDomainEvents,
|
|
132
|
+
* loadFromHistory, _internal_setState). Starts true so the first
|
|
133
|
+
* `getDomainEvents()` call always builds the cache.
|
|
134
|
+
*/
|
|
135
|
+
private _domainEventsCacheDirty;
|
|
105
136
|
private _eventHandlers;
|
|
106
137
|
private _capabilities;
|
|
107
138
|
/**
|
|
@@ -111,35 +142,141 @@ export declare class AggregateRoot<TId = string> implements IAggregateRoot<TId>
|
|
|
111
142
|
* loops or malicious replay payloads.
|
|
112
143
|
*/
|
|
113
144
|
private readonly _maxEvents;
|
|
145
|
+
/**
|
|
146
|
+
* VF-023 (D-4, AC6, non-breaking addition): controls what happens when
|
|
147
|
+
* `apply()` or `loadFromHistory()` processes an event for which no
|
|
148
|
+
* handler was registered via `registerEventHandler()`. Defaults to
|
|
149
|
+
* `"warn"` (preserves the previous silent-but-logged-nowhere behavior's
|
|
150
|
+
* intent without a breaking default) — the event is still recorded /
|
|
151
|
+
* replayed, but a warning is logged via the library's internal logger.
|
|
152
|
+
* Set to `"throw"` to fail fast instead, useful in tests or strict
|
|
153
|
+
* environments where a missing handler indicates a real bug.
|
|
154
|
+
*/
|
|
155
|
+
private readonly _onMissingHandler;
|
|
114
156
|
constructor(params: IAggregateConstructorParams<TId>);
|
|
115
157
|
/**
|
|
116
158
|
* Get the unique identifier of this aggregate
|
|
117
159
|
*/
|
|
118
160
|
getId(): EntityId<TId>;
|
|
119
161
|
/**
|
|
162
|
+
* Identity-based equality, matching {@link Entity.equals}. Two aggregates
|
|
163
|
+
* are equal iff they have the same id (compared via `EntityId.equals()`);
|
|
164
|
+
* attribute/version differences are ignored.
|
|
165
|
+
*
|
|
166
|
+
* VF-023 (D-5, AC10, non-breaking addition): `AggregateRoot` does not
|
|
167
|
+
* extend `Entity` (it implements `IAggregateRoot<TId>` directly), so this
|
|
168
|
+
* mirrors `Entity.equals()` rather than inheriting it.
|
|
169
|
+
*
|
|
170
|
+
* @param other - Aggregate to compare against. May be `null` or `undefined`.
|
|
171
|
+
* @returns `true` if both aggregates have the same id; `false` otherwise.
|
|
172
|
+
*/
|
|
173
|
+
equals(other: AggregateRoot<TId> | null | undefined): boolean;
|
|
174
|
+
/**
|
|
175
|
+
* The version increments by one each time `apply()` successfully records a
|
|
176
|
+
* domain event; compare it against the persisted version to detect
|
|
177
|
+
* concurrent modification (optimistic locking).
|
|
120
178
|
*
|
|
121
179
|
* @returns {number} Current version number for optimistic locking
|
|
122
180
|
*/
|
|
123
181
|
getVersion(): number;
|
|
124
182
|
/**
|
|
183
|
+
* The version this aggregate had when it was loaded (or as of the last
|
|
184
|
+
* `commit()`), letting callers work out how many events have been applied
|
|
185
|
+
* since without recomputing from the event log.
|
|
125
186
|
*
|
|
126
187
|
* @returns {number} Initial version when aggregate was loaded
|
|
127
188
|
*/
|
|
128
189
|
getInitialVersion(): number;
|
|
129
190
|
/**
|
|
191
|
+
* Whether this aggregate has recorded domain events since its last
|
|
192
|
+
* `commit()` — use it to decide whether persistence/dispatch work is
|
|
193
|
+
* needed at all.
|
|
130
194
|
*
|
|
131
195
|
* @returns {boolean} True if aggregate has uncommitted domain events
|
|
132
196
|
*/
|
|
133
197
|
hasChanges(): boolean;
|
|
134
198
|
/**
|
|
199
|
+
* VF-023 (D-3, AC11, BREAKING): the returned array and every event object
|
|
200
|
+
* it contains (including nested `payload`/`metadata`) are now deep-frozen
|
|
201
|
+
* via `LibUtils.deepFreeze` — the same immutability guarantee applied to
|
|
202
|
+
* `BaseValueObject` values (see `base-value-object.ts`). Previously this
|
|
203
|
+
* returned only a shallow array copy: the array itself couldn't be
|
|
204
|
+
* mutated by callers, but the event objects (and their nested payload
|
|
205
|
+
* data) inside it could still be mutated in place, silently corrupting
|
|
206
|
+
* state shared with `_domainEvents` (the same object references are
|
|
207
|
+
* stored internally — events are never re-cloned after creation).
|
|
135
208
|
*
|
|
209
|
+
* VP-012b (D2, BREAKING behavior change, non-breaking signature): the
|
|
210
|
+
* returned array reference is now **stable between calls** — the same
|
|
211
|
+
* frozen array object is returned on every call until the next mutation
|
|
212
|
+
* (`apply()`, `commit()`, `loadFromHistory()`, or
|
|
213
|
+
* `transformDomainEvents()`), instead of a fresh array being built on
|
|
214
|
+
* every call as before. Content equality (`toEqual`) is unaffected either
|
|
215
|
+
* way. Code that relies on *identity* — e.g. asserting `not.toBe`/`!==`
|
|
216
|
+
* between two `getDomainEvents()` calls to prove "each call returns a new
|
|
217
|
+
* array" — will observe the new stable-reference behavior and must be
|
|
218
|
+
* updated; asserting `.toBe`/`===` between two calls made with no
|
|
219
|
+
* intervening mutation now holds, where it previously did not. This is
|
|
220
|
+
* safe to alias: the returned array and its contents are frozen, so
|
|
221
|
+
* holding onto the reference across a mutation still reflects the state
|
|
222
|
+
* as of the call that produced it (the cache is not that same array
|
|
223
|
+
* anymore after invalidation — a stored old reference is simply stale
|
|
224
|
+
* data, never mutated out from under the caller).
|
|
136
225
|
* @returns {ReadonlyArray<IDomainEvent>} Array of uncommitted domain events
|
|
137
226
|
*/
|
|
138
227
|
getDomainEvents(): ReadonlyArray<IDomainEvent>;
|
|
228
|
+
/**
|
|
229
|
+
* VP-012b (D2): single invalidation point for `_domainEventsCache`,
|
|
230
|
+
* called from every site that mutates `_domainEvents` — `apply()`,
|
|
231
|
+
* `commit()`, `transformDomainEvents()`, `loadFromHistory()`, and
|
|
232
|
+
* `_internal_setState()`. Keeping invalidation in one helper (rather than
|
|
233
|
+
* setting the flag ad hoc at each call site) means a future mutation site
|
|
234
|
+
* cannot silently forget to invalidate.
|
|
235
|
+
*/
|
|
236
|
+
private _invalidateDomainEventsCache;
|
|
139
237
|
/**
|
|
140
238
|
* Clear all uncommitted domain events and update initial version
|
|
141
239
|
*/
|
|
142
240
|
commit(): void;
|
|
241
|
+
/**
|
|
242
|
+
* Rewrite the payload and/or metadata of the uncommitted domain events.
|
|
243
|
+
*
|
|
244
|
+
* This is an infrastructure boundary API, in the same family as `commit()`:
|
|
245
|
+
* call it from a repository immediately before persisting, never from the
|
|
246
|
+
* domain or application layer. An aggregate records events through `apply()`;
|
|
247
|
+
* it does not rewrite its own history.
|
|
248
|
+
*
|
|
249
|
+
* It exists for data that only the persistence boundary knows — a
|
|
250
|
+
* crypto-shredding key id resolved at save time, a correlation id assigned at
|
|
251
|
+
* dispatch, an encrypted payload. Doing this by hand from outside is not
|
|
252
|
+
* possible: `getDomainEvents()` deep-freezes what it returns, and the
|
|
253
|
+
* internal list is private. The transformation has to land here rather than
|
|
254
|
+
* on a local copy inside `save()`, because the event dispatcher reads the
|
|
255
|
+
* aggregate's events again — a local copy would leave the in-process event
|
|
256
|
+
* bus publishing the untransformed originals.
|
|
257
|
+
*
|
|
258
|
+
* Only `payload` and `metadata` can be replaced. `eventName`, identity, and
|
|
259
|
+
* the number and order of events are fixed: changing those would desync
|
|
260
|
+
* handlers on replay or break the version invariant. Return nothing to leave
|
|
261
|
+
* an event as it is.
|
|
262
|
+
*
|
|
263
|
+
* Identity, prototype and `instanceof` survive, and the event's constructor
|
|
264
|
+
* is never called, so event classes with their own constructor arguments are
|
|
265
|
+
* safe.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* // inside a repository's save(), before persisting:
|
|
269
|
+
* order.transformDomainEvents(event => ({
|
|
270
|
+
* payload: encrypt(event.payload, key),
|
|
271
|
+
* metadata: { userSpecificKeyId: key.id },
|
|
272
|
+
* }));
|
|
273
|
+
*
|
|
274
|
+
* @param transform - Called per event; returns the parts to replace, or nothing to keep it
|
|
275
|
+
*/
|
|
276
|
+
transformDomainEvents<P = unknown>(transform: (event: Readonly<IDomainEvent<P>>, index: number) => {
|
|
277
|
+
payload?: P;
|
|
278
|
+
metadata?: Partial<IEventMetadata>;
|
|
279
|
+
} | void): void;
|
|
143
280
|
/**
|
|
144
281
|
* @param {string} eventType - Event type to register handler for
|
|
145
282
|
* @param {IAggregateEventHandler<T>} handler - Event handler function
|
|
@@ -151,9 +288,28 @@ export declare class AggregateRoot<TId = string> implements IAggregateRoot<TId>
|
|
|
151
288
|
* @param {string | IDomainEvent<P>} eventTypeOrEvent - Event type string or complete event object
|
|
152
289
|
* @param {P} payload - Event payload data
|
|
153
290
|
* @param {Partial<IEventMetadata>} metadata - Optional event metadata
|
|
291
|
+
* @throws {Error} when the aggregate's configured maxEvents limit would be exceeded
|
|
292
|
+
* @example Hitting the maxEvents guard
|
|
293
|
+
* ```typescript
|
|
294
|
+
* class Order extends AggregateRoot<string> {
|
|
295
|
+
* record(customerId: string, amount: number): void {
|
|
296
|
+
* this.apply('OrderCreated', { customerId, amount });
|
|
297
|
+
* }
|
|
298
|
+
* }
|
|
299
|
+
*
|
|
300
|
+
* const order = new Order({ id: EntityId.create(), version: 0, maxEvents: 1 });
|
|
301
|
+
* order.record('c-1', 100); // ok, 1 event recorded
|
|
302
|
+
* order.record('c-1', 100); // throws: maxEvents limit of 1 exceeded
|
|
303
|
+
* ```
|
|
154
304
|
*/
|
|
155
305
|
protected apply<P = unknown>(eventTypeOrEvent: string | IDomainEvent<P>, payload?: P, metadata?: Partial<IEventMetadata>): void;
|
|
156
306
|
private handleEvent;
|
|
307
|
+
/**
|
|
308
|
+
* VF-023 (D-4, AC6): called from `handleEvent()`, which is shared by both
|
|
309
|
+
* `apply()` (live) and `loadFromHistory()` (replay) — so the missing-
|
|
310
|
+
* handler policy applies identically to both paths.
|
|
311
|
+
*/
|
|
312
|
+
private reportMissingHandler;
|
|
157
313
|
/**
|
|
158
314
|
* Reconstruct aggregate state from historical domain events
|
|
159
315
|
* @param {IDomainEvent[]} events - Array of events to replay for state reconstruction
|
|
@@ -190,10 +346,20 @@ export declare class AggregateRoot<TId = string> implements IAggregateRoot<TId>
|
|
|
190
346
|
getCapabilityTypes(): string[];
|
|
191
347
|
private static sanitizeMetadata;
|
|
192
348
|
/**
|
|
193
|
-
* Internal method for capabilities to access private state
|
|
194
|
-
*
|
|
349
|
+
* Internal method for capabilities to access private state.
|
|
350
|
+
*
|
|
351
|
+
* VF-023 (D-2, AC5, BREAKING, CRITICAL F-H4): gated by
|
|
352
|
+
* {@link INTERNAL_STATE_TOKEN}, a module-private `unique symbol` that is
|
|
353
|
+
* exported only from this module (not from the package's public barrel).
|
|
354
|
+
* Callers must supply the exact token reference to invoke this method —
|
|
355
|
+
* consumers of `@vytches/ddd-aggregates` cannot obtain it, so this method
|
|
356
|
+
* is unreachable from outside this package's own source, even via
|
|
357
|
+
* `as unknown as {...}` casts (the token value itself, not just its type,
|
|
358
|
+
* is required and cannot be forged).
|
|
359
|
+
*
|
|
360
|
+
* @throws {TypeError} if `token` does not match {@link INTERNAL_STATE_TOKEN}
|
|
195
361
|
*/
|
|
196
|
-
_internal_setState(state: {
|
|
362
|
+
_internal_setState(token: typeof INTERNAL_STATE_TOKEN, state: {
|
|
197
363
|
version: number;
|
|
198
364
|
initialVersion: number;
|
|
199
365
|
domainEvents: IDomainEvent[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Result } from '
|
|
2
|
-
import { CapabilityConstructor, Capability, IAggregateCapability, IAggregateSnapshot } from '
|
|
1
|
+
import { Result } from '@vytches/ddd-utils';
|
|
2
|
+
import { CapabilityConstructor, Capability, IAggregateCapability, IAggregateSnapshot } from '@vytches/ddd-contracts';
|
|
3
3
|
import { AggregateError } from '../aggregate-errors';
|
|
4
4
|
import { SnapshotCapability } from '../capabilities/snapshot-capability';
|
|
5
5
|
import { VersioningCapability } from '../capabilities/versioning-capability';
|
package/dist/core/entity.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("@vytches/ddd-contracts"),S=require("@vytches/ddd-contracts/internal"),h=require("@vytches/ddd-utils"),o=require("@vytches/ddd-domain-primitives");class p extends l.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 s=t;if(i<a&&this.upcasters.has(t.eventName)){const u=this.upcasters.get(t.eventName);for(let c=i;c<a;c++){const b=u.get(c);if(!b){S.internalLogger.warn("VersioningCapability: Missing upcaster for event version — event will be processed without upcast",{eventName:t.eventName,fromVersion:c,toVersion:c+1});continue}const V=b.upcast(s.payload,s.metadata);s={...s,payload:V,metadata:{...s.metadata,version:c+1}}}}const g=e.get(s.eventName);g&&g(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}}const _=Symbol("vytches-ddd-aggregate-internal-state");class d{constructor(t){this._version=0,this._initialVersion=0,this._domainEvents=[],this._domainEventsCache=Object.freeze([]),this._domainEventsCacheDirty=!0,this._eventHandlers=new Map,this._capabilities=new l.CapabilityRegistry,this._id=t.id,this._version=t.version||0,this._initialVersion=t.version||0,this._maxEvents=t.maxEvents,this._onMissingHandler=t.onMissingHandler??"warn"}getId(){return this._id}equals(t){return t==null?!1:t===this?!0:t instanceof d?this._id.equals(t._id):!1}getVersion(){return this._version}getInitialVersion(){return this._initialVersion}hasChanges(){return this._domainEvents.length>0}getDomainEvents(){if(this._domainEventsCacheDirty){const t=new WeakSet,e=this._domainEvents.map(i=>h.LibUtils.deepFreeze(i,t));this._domainEventsCache=Object.freeze(e),this._domainEventsCacheDirty=!1}return this._domainEventsCache}_invalidateDomainEventsCache(){this._domainEventsCacheDirty=!0}commit(){this._domainEvents=[],this._initialVersion=this._version,this._invalidateDomainEventsCache()}transformDomainEvents(t){this._domainEvents=this._domainEvents.map((e,i)=>{const a=t(e,i);return a?l.enrichEvent(e,a):e}),this._invalidateDomainEventsCache()}registerEventHandler(t,e){return this._eventHandlers.set(t,e),this}apply(t,e,i){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.`);const a=this._version+1;let s,g;typeof t=="string"?(s=l.createDomainEvent(t,e,i),g=s.metadata):(s=t,g=i?{...s.metadata,...i}:s.metadata);const u=d.sanitizeMetadata({...g,aggregateId:this._id.toString(),aggregateType:this.constructor.name,aggregateVersion:a,timestamp:g?.timestamp??new Date}),c=Object.assign(Object.create(Object.getPrototypeOf(s)),s,{metadata:u});this._version=a,this._domainEvents.push(c),this._invalidateDomainEventsCache(),this.handleEvent(c)}handleEvent(t){const e=this.getCapability(p);if(e)e.handleVersionedEvent(t,this._eventHandlers);else{const i=this._eventHandlers.get(t.eventName);i?i(t.payload,t.metadata):this.reportMissingHandler(t.eventName)}}reportMissingHandler(t){const e=`Aggregate ${this.constructor.name} (id=${this._id.toString()}) has no registered handler for event "${t}". State will not be updated for this event. Register a handler via registerEventHandler('${t}', ...) before applying/replaying this event, or pass onMissingHandler: "warn" explicitly to silence this in the constructor params.`;if(this._onMissingHandler==="throw")throw new Error(e);S.internalLogger.warn(e,{aggregateType:this.constructor.name,aggregateId:this._id.toString(),eventName:t})}loadFromHistory(t){this._version=this._initialVersion,this._domainEvents=[],this._invalidateDomainEventsCache();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,e){if(t!==_)throw new TypeError("_internal_setState: invalid or missing internal state token. This method is restricted to capabilities within @vytches/ddd-aggregates.");this._version=e.version,this._initialVersion=e.initialVersion,this._domainEvents=[...e.domainEvents],this._invalidateDomainEventsCache()}}class m extends l.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,s)=>{if(t[a.eventName]=(t[a.eventName]||0)+1,s>0){const g=this.auditLog[s-1];if(g){const u=new Date(g.timestamp).getTime(),c=new Date(a.timestamp).getTime();e+=c-u}}});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-${h.LibUtils.getUUID()}`,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 s=`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(s,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}`,s={code:o.DomainErrorCode.MissingValue,data:{eventType:t,fromVersion:e,toVersion:i}};return new r(a,s)}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 f extends l.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 y extends l.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(),s=(i.length>0?i[i.length-1]:null)?.metadata?.eventId,u={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&&(u.metadata=e()),u}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),this.aggregate._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 l.EntityId?t.id:new l.EntityId(t.id,"text"),version:t.version||0};return new v(e)}withSnapshots(){return this.capabilities.push({capability:new y}),this}withVersioning(){return this.capabilities.push({capability:new p}),this}withAudit(){return this.capabilities.push({capability:new m}),this}withEventSourcing(t){const e=new f;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||d,i=new e(this.params);for(const{capability:a,configure:s}of this.capabilities)i.addCapability(a),s&&s(a);return i}buildWithAllCapabilities(t){return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t)}}function I(n){return v.create(n)}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 w(n){return n.hasCapability(y)}function C(n){return n.hasCapability(p)}function A(n){return n.hasCapability(m)}function D(n){return n.hasCapability(f)}function $(n){if(!w(n))throw r.featureNotEnabled("snapshot");return n}function M(n){return w(n)?h.Result.ok(n):h.Result.fail(r.featureNotEnabled("snapshot"))}function N(n){if(!C(n))throw r.featureNotEnabled("versioning");return n}function T(n){return C(n)?h.Result.ok(n):h.Result.fail(r.featureNotEnabled("versioning"))}function L(n){if(!A(n))throw r.featureNotEnabled("audit");return n}function F(n){return A(n)?h.Result.ok(n):h.Result.fail(r.featureNotEnabled("audit"))}function H(n){if(!D(n))throw r.featureNotEnabled("eventSourcing");return n}function x(n){return D(n)?h.Result.ok(n):h.Result.fail(r.featureNotEnabled("eventSourcing"))}function U(n){return n.getCapabilityTypes()}function O(n,t){return t.every(e=>n.hasCapability(e))}exports.AggregateBuilder=v;exports.AggregateError=r;exports.AggregateRoot=d;exports.AuditCapability=m;exports.Entity=E;exports.EventSourcingCapability=f;exports.SnapshotCapability=y;exports.VersioningCapability=p;exports.aggregateBuilder=I;exports.asAuditAggregate=L;exports.asEventSourcingAggregate=H;exports.asSnapshotAggregate=$;exports.asVersioningAggregate=N;exports.getAggregateCapabilities=U;exports.hasAllCapabilities=O;exports.tryAsAuditAggregate=F;exports.tryAsEventSourcingAggregate=x;exports.tryAsSnapshotAggregate=M;exports.tryAsVersioningAggregate=T;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { AuditCapability } from './capabilities/audit-capability';
|
|
|
5
5
|
export { EventSourcingCapability } from './capabilities/event-sourcing-capability';
|
|
6
6
|
export { SnapshotCapability } from './capabilities/snapshot-capability';
|
|
7
7
|
export { VersioningCapability } from './capabilities/versioning-capability';
|
|
8
|
-
export type { IAggregateRoot, IAggregateCapability, IAggregateConstructorParams, IAggregateEventHandler,
|
|
8
|
+
export type { IAggregateRoot, IAggregateCapability, IAggregateConstructorParams, IAggregateEventHandler, } from './aggregate-interfaces';
|
|
9
9
|
export { AggregateError } from './aggregate-errors';
|
|
10
10
|
export type { AggregateWithSnapshotCapability, AggregateWithVersioningCapability, AggregateWithAuditCapability, AggregateWithEventSourcingCapability, } from './core/aggregate-utilities';
|
|
11
11
|
export { asSnapshotAggregate, tryAsSnapshotAggregate, asVersioningAggregate, tryAsVersioningAggregate, asAuditAggregate, tryAsAuditAggregate, asEventSourcingAggregate, tryAsEventSourcingAggregate, getAggregateCapabilities, hasAllCapabilities, } from './core/aggregate-utilities';
|