@vytches/ddd-aggregates 0.29.3 → 0.31.0-alpha.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 +84 -17
- 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 +93 -6
- 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 +255 -168
- package/package.json +8 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Capability as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
class p extends
|
|
1
|
+
import { Capability as d, CapabilityRegistry as M, createDomainEvent as T, EntityId as f } from "@vytches/ddd-contracts";
|
|
2
|
+
import { internalLogger as y } from "@vytches/ddd-contracts/internal";
|
|
3
|
+
import { LibUtils as b, Result as l } from "@vytches/ddd-utils";
|
|
4
|
+
import { IDomainError as L, DomainErrorCode as g } from "@vytches/ddd-domain-primitives";
|
|
5
|
+
class p extends d {
|
|
6
6
|
constructor() {
|
|
7
7
|
super(...arguments), this.type = "versioning", this.upcasters = /* @__PURE__ */ new Map();
|
|
8
8
|
}
|
|
@@ -27,40 +27,53 @@ class p extends l {
|
|
|
27
27
|
this.upcasters.has(t) || this.upcasters.set(t, /* @__PURE__ */ new Map()), this.upcasters.get(t).set(e, i);
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Applies registered upcasters to bring `event` from its stored version up
|
|
31
|
+
* to the aggregate's current version, then invokes the matching handler.
|
|
32
|
+
*
|
|
33
|
+
* **Upcasting only activates if `event.metadata.targetVersion` is set.**
|
|
34
|
+
* This is NOT inferred automatically from registered upcasters or from
|
|
35
|
+
* any aggregate-level "current version" — the caller (typically your
|
|
36
|
+
* event-sourcing replay/load path) must explicitly stamp each historical
|
|
37
|
+
* event's metadata with `targetVersion` before calling this method. If
|
|
38
|
+
* `targetVersion` is absent, `currentVersion` falls back to `eventVersion`
|
|
39
|
+
* (i.e. no upcasting happens — the event is passed through unchanged even
|
|
40
|
+
* if upcasters are registered for it).
|
|
41
|
+
*
|
|
42
|
+
* @param {IDomainEvent} event - Event to process with version handling;
|
|
43
|
+
* set `event.metadata.targetVersion` to enable upcasting
|
|
31
44
|
* @param {Map<string, IAggregateEventHandler>} handlers - Map of event handlers
|
|
32
45
|
*/
|
|
33
46
|
handleVersionedEvent(t, e) {
|
|
34
|
-
const i = t.metadata?.version || 1,
|
|
47
|
+
const i = t.metadata?.version || 1, s = t.metadata?.targetVersion || i;
|
|
35
48
|
let n = t;
|
|
36
|
-
if (i <
|
|
37
|
-
const
|
|
38
|
-
for (let
|
|
39
|
-
const
|
|
40
|
-
if (!
|
|
41
|
-
|
|
42
|
-
"Missing upcaster for event version — event will be processed without upcast",
|
|
49
|
+
if (i < s && this.upcasters.has(t.eventName)) {
|
|
50
|
+
const h = this.upcasters.get(t.eventName);
|
|
51
|
+
for (let c = i; c < s; c++) {
|
|
52
|
+
const m = h.get(c);
|
|
53
|
+
if (!m) {
|
|
54
|
+
y.warn(
|
|
55
|
+
"VersioningCapability: Missing upcaster for event version — event will be processed without upcast",
|
|
43
56
|
{
|
|
44
57
|
eventName: t.eventName,
|
|
45
|
-
fromVersion:
|
|
46
|
-
toVersion:
|
|
58
|
+
fromVersion: c,
|
|
59
|
+
toVersion: c + 1
|
|
47
60
|
}
|
|
48
61
|
);
|
|
49
62
|
continue;
|
|
50
63
|
}
|
|
51
|
-
const
|
|
64
|
+
const N = m.upcast(n.payload, n.metadata);
|
|
52
65
|
n = {
|
|
53
66
|
...n,
|
|
54
|
-
payload:
|
|
67
|
+
payload: N,
|
|
55
68
|
metadata: {
|
|
56
69
|
...n.metadata,
|
|
57
|
-
version:
|
|
70
|
+
version: c + 1
|
|
58
71
|
}
|
|
59
72
|
};
|
|
60
73
|
}
|
|
61
74
|
}
|
|
62
|
-
const
|
|
63
|
-
|
|
75
|
+
const o = e.get(n.eventName);
|
|
76
|
+
o && o(n.payload, n.metadata);
|
|
64
77
|
}
|
|
65
78
|
/**
|
|
66
79
|
* @returns {string[]} Array of event types that have registered upcasters
|
|
@@ -99,9 +112,10 @@ class p extends l {
|
|
|
99
112
|
return t;
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
|
-
|
|
115
|
+
const E = Symbol("vytches-ddd-aggregate-internal-state");
|
|
116
|
+
class u {
|
|
103
117
|
constructor(t) {
|
|
104
|
-
this._version = 0, this._initialVersion = 0, this._domainEvents = [], this._eventHandlers = /* @__PURE__ */ new Map(), this._capabilities = new
|
|
118
|
+
this._version = 0, this._initialVersion = 0, this._domainEvents = [], this._eventHandlers = /* @__PURE__ */ new Map(), this._capabilities = new M(), this._id = t.id, this._version = t.version || 0, this._initialVersion = t.version || 0, this._maxEvents = t.maxEvents, this._onMissingHandler = t.onMissingHandler ?? "warn";
|
|
105
119
|
}
|
|
106
120
|
// ==========================================
|
|
107
121
|
// CORE AGGREGATE FUNCTIONALITY
|
|
@@ -113,6 +127,24 @@ class v {
|
|
|
113
127
|
return this._id;
|
|
114
128
|
}
|
|
115
129
|
/**
|
|
130
|
+
* Identity-based equality, matching {@link Entity.equals}. Two aggregates
|
|
131
|
+
* are equal iff they have the same id (compared via `EntityId.equals()`);
|
|
132
|
+
* attribute/version differences are ignored.
|
|
133
|
+
*
|
|
134
|
+
* VF-023 (D-5, AC10, non-breaking addition): `AggregateRoot` does not
|
|
135
|
+
* extend `Entity` (it implements `IAggregateRoot<TId>` directly), so this
|
|
136
|
+
* mirrors `Entity.equals()` rather than inheriting it.
|
|
137
|
+
*
|
|
138
|
+
* @param other - Aggregate to compare against. May be `null` or `undefined`.
|
|
139
|
+
* @returns `true` if both aggregates have the same id; `false` otherwise.
|
|
140
|
+
*/
|
|
141
|
+
equals(t) {
|
|
142
|
+
return t == null ? !1 : t === this ? !0 : t instanceof u ? this._id.equals(t._id) : !1;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The version increments by one each time `apply()` successfully records a
|
|
146
|
+
* domain event; compare it against the persisted version to detect
|
|
147
|
+
* concurrent modification (optimistic locking).
|
|
116
148
|
*
|
|
117
149
|
* @returns {number} Current version number for optimistic locking
|
|
118
150
|
*/
|
|
@@ -120,6 +152,9 @@ class v {
|
|
|
120
152
|
return this._version;
|
|
121
153
|
}
|
|
122
154
|
/**
|
|
155
|
+
* The version this aggregate had when it was loaded (or as of the last
|
|
156
|
+
* `commit()`), letting callers work out how many events have been applied
|
|
157
|
+
* since without recomputing from the event log.
|
|
123
158
|
*
|
|
124
159
|
* @returns {number} Initial version when aggregate was loaded
|
|
125
160
|
*/
|
|
@@ -127,6 +162,9 @@ class v {
|
|
|
127
162
|
return this._initialVersion;
|
|
128
163
|
}
|
|
129
164
|
/**
|
|
165
|
+
* Whether this aggregate has recorded domain events since its last
|
|
166
|
+
* `commit()` — use it to decide whether persistence/dispatch work is
|
|
167
|
+
* needed at all.
|
|
130
168
|
*
|
|
131
169
|
* @returns {boolean} True if aggregate has uncommitted domain events
|
|
132
170
|
*/
|
|
@@ -134,11 +172,20 @@ class v {
|
|
|
134
172
|
return this._domainEvents.length > 0;
|
|
135
173
|
}
|
|
136
174
|
/**
|
|
137
|
-
*
|
|
175
|
+
* VF-023 (D-3, AC11, BREAKING): the returned array and every event object
|
|
176
|
+
* it contains (including nested `payload`/`metadata`) are now deep-frozen
|
|
177
|
+
* via `LibUtils.deepFreeze` — the same immutability guarantee applied to
|
|
178
|
+
* `BaseValueObject` values (see `base-value-object.ts`). Previously this
|
|
179
|
+
* returned only a shallow array copy: the array itself couldn't be
|
|
180
|
+
* mutated by callers, but the event objects (and their nested payload
|
|
181
|
+
* data) inside it could still be mutated in place, silently corrupting
|
|
182
|
+
* state shared with `_domainEvents` (the same object references are
|
|
183
|
+
* stored internally — events are never re-cloned after creation).
|
|
138
184
|
* @returns {ReadonlyArray<IDomainEvent>} Array of uncommitted domain events
|
|
139
185
|
*/
|
|
140
186
|
getDomainEvents() {
|
|
141
|
-
|
|
187
|
+
const t = this._domainEvents.map((e) => b.deepFreeze(e));
|
|
188
|
+
return Object.freeze(t);
|
|
142
189
|
}
|
|
143
190
|
/**
|
|
144
191
|
* Clear all uncommitted domain events and update initial version
|
|
@@ -162,27 +209,40 @@ class v {
|
|
|
162
209
|
* @param {string | IDomainEvent<P>} eventTypeOrEvent - Event type string or complete event object
|
|
163
210
|
* @param {P} payload - Event payload data
|
|
164
211
|
* @param {Partial<IEventMetadata>} metadata - Optional event metadata
|
|
212
|
+
* @throws {Error} when the aggregate's configured maxEvents limit would be exceeded
|
|
213
|
+
* @example Hitting the maxEvents guard
|
|
214
|
+
* ```typescript
|
|
215
|
+
* class Order extends AggregateRoot<string> {
|
|
216
|
+
* record(customerId: string, amount: number): void {
|
|
217
|
+
* this.apply('OrderCreated', { customerId, amount });
|
|
218
|
+
* }
|
|
219
|
+
* }
|
|
220
|
+
*
|
|
221
|
+
* const order = new Order({ id: EntityId.create(), version: 0, maxEvents: 1 });
|
|
222
|
+
* order.record('c-1', 100); // ok, 1 event recorded
|
|
223
|
+
* order.record('c-1', 100); // throws: maxEvents limit of 1 exceeded
|
|
224
|
+
* ```
|
|
165
225
|
*/
|
|
166
226
|
apply(t, e, i) {
|
|
167
|
-
this._version++;
|
|
168
|
-
let a, n;
|
|
169
|
-
typeof t == "string" ? (a = N(t, e, i), n = a.metadata) : (a = t, n = i ? { ...a.metadata, ...i } : a.metadata);
|
|
170
|
-
const c = v.sanitizeMetadata({
|
|
171
|
-
...n,
|
|
172
|
-
aggregateId: this._id.toString(),
|
|
173
|
-
aggregateType: this.constructor.name,
|
|
174
|
-
aggregateVersion: this._version,
|
|
175
|
-
timestamp: n?.timestamp ?? /* @__PURE__ */ new Date()
|
|
176
|
-
}), g = Object.assign(
|
|
177
|
-
Object.create(Object.getPrototypeOf(a)),
|
|
178
|
-
a,
|
|
179
|
-
{ metadata: c }
|
|
180
|
-
);
|
|
181
227
|
if (this._maxEvents !== void 0 && this._domainEvents.length >= this._maxEvents)
|
|
182
228
|
throw new Error(
|
|
183
229
|
`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.`
|
|
184
230
|
);
|
|
185
|
-
this.
|
|
231
|
+
const s = this._version + 1;
|
|
232
|
+
let n, o;
|
|
233
|
+
typeof t == "string" ? (n = T(t, e, i), o = n.metadata) : (n = t, o = i ? { ...n.metadata, ...i } : n.metadata);
|
|
234
|
+
const h = u.sanitizeMetadata({
|
|
235
|
+
...o,
|
|
236
|
+
aggregateId: this._id.toString(),
|
|
237
|
+
aggregateType: this.constructor.name,
|
|
238
|
+
aggregateVersion: s,
|
|
239
|
+
timestamp: o?.timestamp ?? /* @__PURE__ */ new Date()
|
|
240
|
+
}), c = Object.assign(
|
|
241
|
+
Object.create(Object.getPrototypeOf(n)),
|
|
242
|
+
n,
|
|
243
|
+
{ metadata: h }
|
|
244
|
+
);
|
|
245
|
+
this._version = s, this._domainEvents.push(c), this.handleEvent(c);
|
|
186
246
|
}
|
|
187
247
|
handleEvent(t) {
|
|
188
248
|
const e = this.getCapability(p);
|
|
@@ -190,9 +250,24 @@ class v {
|
|
|
190
250
|
e.handleVersionedEvent(t, this._eventHandlers);
|
|
191
251
|
else {
|
|
192
252
|
const i = this._eventHandlers.get(t.eventName);
|
|
193
|
-
i
|
|
253
|
+
i ? i(t.payload, t.metadata) : this.reportMissingHandler(t.eventName);
|
|
194
254
|
}
|
|
195
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* VF-023 (D-4, AC6): called from `handleEvent()`, which is shared by both
|
|
258
|
+
* `apply()` (live) and `loadFromHistory()` (replay) — so the missing-
|
|
259
|
+
* handler policy applies identically to both paths.
|
|
260
|
+
*/
|
|
261
|
+
reportMissingHandler(t) {
|
|
262
|
+
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.`;
|
|
263
|
+
if (this._onMissingHandler === "throw")
|
|
264
|
+
throw new Error(e);
|
|
265
|
+
y.warn(e, {
|
|
266
|
+
aggregateType: this.constructor.name,
|
|
267
|
+
aggregateId: this._id.toString(),
|
|
268
|
+
eventName: t
|
|
269
|
+
});
|
|
270
|
+
}
|
|
196
271
|
/**
|
|
197
272
|
* Reconstruct aggregate state from historical domain events
|
|
198
273
|
* @param {IDomainEvent[]} events - Array of events to replay for state reconstruction
|
|
@@ -263,14 +338,28 @@ class v {
|
|
|
263
338
|
// INTERNAL ACCESS FOR CAPABILITIES
|
|
264
339
|
// ==========================================
|
|
265
340
|
/**
|
|
266
|
-
* Internal method for capabilities to access private state
|
|
267
|
-
*
|
|
341
|
+
* Internal method for capabilities to access private state.
|
|
342
|
+
*
|
|
343
|
+
* VF-023 (D-2, AC5, BREAKING, CRITICAL F-H4): gated by
|
|
344
|
+
* {@link INTERNAL_STATE_TOKEN}, a module-private `unique symbol` that is
|
|
345
|
+
* exported only from this module (not from the package's public barrel).
|
|
346
|
+
* Callers must supply the exact token reference to invoke this method —
|
|
347
|
+
* consumers of `@vytches/ddd-aggregates` cannot obtain it, so this method
|
|
348
|
+
* is unreachable from outside this package's own source, even via
|
|
349
|
+
* `as unknown as {...}` casts (the token value itself, not just its type,
|
|
350
|
+
* is required and cannot be forged).
|
|
351
|
+
*
|
|
352
|
+
* @throws {TypeError} if `token` does not match {@link INTERNAL_STATE_TOKEN}
|
|
268
353
|
*/
|
|
269
|
-
_internal_setState(t) {
|
|
270
|
-
|
|
354
|
+
_internal_setState(t, e) {
|
|
355
|
+
if (t !== E)
|
|
356
|
+
throw new TypeError(
|
|
357
|
+
"_internal_setState: invalid or missing internal state token. This method is restricted to capabilities within @vytches/ddd-aggregates."
|
|
358
|
+
);
|
|
359
|
+
this._version = e.version, this._initialVersion = e.initialVersion, this._domainEvents = [...e.domainEvents];
|
|
271
360
|
}
|
|
272
361
|
}
|
|
273
|
-
class
|
|
362
|
+
class w extends d {
|
|
274
363
|
constructor() {
|
|
275
364
|
super(...arguments), this.type = "audit", this.auditLog = [];
|
|
276
365
|
}
|
|
@@ -284,8 +373,8 @@ class b extends l {
|
|
|
284
373
|
this.aggregate = t;
|
|
285
374
|
const e = this.aggregate;
|
|
286
375
|
typeof e.apply == "function" && (this.originalApply = e.apply, this.originalApply && (this.aggregate.apply = (...i) => {
|
|
287
|
-
const
|
|
288
|
-
return this.recordAuditEvent(),
|
|
376
|
+
const s = this.originalApply.call(this.aggregate, ...i);
|
|
377
|
+
return this.recordAuditEvent(), s;
|
|
289
378
|
}));
|
|
290
379
|
}
|
|
291
380
|
detach() {
|
|
@@ -306,12 +395,12 @@ class b extends l {
|
|
|
306
395
|
getAuditStatistics() {
|
|
307
396
|
const t = {};
|
|
308
397
|
let e = 0;
|
|
309
|
-
this.auditLog.forEach((
|
|
310
|
-
if (t[
|
|
311
|
-
const
|
|
312
|
-
if (
|
|
313
|
-
const
|
|
314
|
-
e +=
|
|
398
|
+
this.auditLog.forEach((s, n) => {
|
|
399
|
+
if (t[s.eventName] = (t[s.eventName] || 0) + 1, n > 0) {
|
|
400
|
+
const o = this.auditLog[n - 1];
|
|
401
|
+
if (o) {
|
|
402
|
+
const h = new Date(o.timestamp).getTime(), c = new Date(s.timestamp).getTime();
|
|
403
|
+
e += c - h;
|
|
315
404
|
}
|
|
316
405
|
}
|
|
317
406
|
});
|
|
@@ -334,7 +423,7 @@ class b extends l {
|
|
|
334
423
|
*/
|
|
335
424
|
recordEvent(t) {
|
|
336
425
|
const e = {
|
|
337
|
-
eventId: t.metadata?.eventId || `audit-${
|
|
426
|
+
eventId: t.metadata?.eventId || `audit-${b.getUUID()}`,
|
|
338
427
|
eventName: t.eventName,
|
|
339
428
|
aggregateId: this.aggregate.getId().toString(),
|
|
340
429
|
aggregateType: this.aggregate.constructor.name,
|
|
@@ -363,8 +452,8 @@ class b extends l {
|
|
|
363
452
|
*/
|
|
364
453
|
getEventsByTimeRange(t, e) {
|
|
365
454
|
return this.auditLog.filter((i) => {
|
|
366
|
-
const
|
|
367
|
-
return
|
|
455
|
+
const s = new Date(i.timestamp).getTime();
|
|
456
|
+
return s >= t.getTime() && s <= e.getTime();
|
|
368
457
|
});
|
|
369
458
|
}
|
|
370
459
|
/**
|
|
@@ -380,13 +469,13 @@ class b extends l {
|
|
|
380
469
|
return this.auditLog[this.auditLog.length - 1] || null;
|
|
381
470
|
}
|
|
382
471
|
}
|
|
383
|
-
class r extends
|
|
472
|
+
class r extends L {
|
|
384
473
|
/**
|
|
385
474
|
* Error for invalid arguments provided to the aggregate
|
|
386
475
|
*/
|
|
387
476
|
static invalidArguments(t, e) {
|
|
388
477
|
const i = {
|
|
389
|
-
code:
|
|
478
|
+
code: g.InvalidParameter,
|
|
390
479
|
data: e
|
|
391
480
|
};
|
|
392
481
|
return new r(t, i);
|
|
@@ -394,24 +483,24 @@ class r extends M {
|
|
|
394
483
|
/**
|
|
395
484
|
* Error for version conflicts in the aggregate
|
|
396
485
|
*/
|
|
397
|
-
static versionConflict(t, e, i,
|
|
398
|
-
const n = `Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${
|
|
399
|
-
code:
|
|
486
|
+
static versionConflict(t, e, i, s) {
|
|
487
|
+
const n = `Version conflict: Aggregate ${t} with ID ${e} has version ${i}, but expected ${s}`, o = {
|
|
488
|
+
code: g.ValidationFailed,
|
|
400
489
|
data: {
|
|
401
490
|
aggregateType: t,
|
|
402
491
|
aggregateId: e,
|
|
403
492
|
currentVersion: i,
|
|
404
|
-
expectedVersion:
|
|
493
|
+
expectedVersion: s
|
|
405
494
|
}
|
|
406
495
|
};
|
|
407
|
-
return new r(n,
|
|
496
|
+
return new r(n, o);
|
|
408
497
|
}
|
|
409
498
|
/**
|
|
410
499
|
* Error for when a required feature is not enabled
|
|
411
500
|
*/
|
|
412
501
|
static featureNotEnabled(t) {
|
|
413
502
|
const e = `Feature '${t}' is not enabled on aggregate`, i = {
|
|
414
|
-
code:
|
|
503
|
+
code: g.ValidationFailed,
|
|
415
504
|
data: {
|
|
416
505
|
feature: t
|
|
417
506
|
}
|
|
@@ -422,113 +511,113 @@ class r extends M {
|
|
|
422
511
|
* Error for when a required method is not implemented
|
|
423
512
|
*/
|
|
424
513
|
static methodNotImplemented(t, e) {
|
|
425
|
-
const i = `Method '${t}' must be implemented by ${e} to use this feature`,
|
|
426
|
-
code:
|
|
514
|
+
const i = `Method '${t}' must be implemented by ${e} to use this feature`, s = {
|
|
515
|
+
code: g.MissingValue,
|
|
427
516
|
data: {
|
|
428
517
|
methodName: t,
|
|
429
518
|
aggregateType: e
|
|
430
519
|
}
|
|
431
520
|
};
|
|
432
|
-
return new r(i,
|
|
521
|
+
return new r(i, s);
|
|
433
522
|
}
|
|
434
523
|
/**
|
|
435
524
|
* Error for invalid snapshot
|
|
436
525
|
*/
|
|
437
526
|
static invalidSnapshot(t, e) {
|
|
438
|
-
const i = `Invalid snapshot for aggregate ${t}${e ? `: ${e}` : ""}`,
|
|
439
|
-
code:
|
|
527
|
+
const i = `Invalid snapshot for aggregate ${t}${e ? `: ${e}` : ""}`, s = {
|
|
528
|
+
code: g.InvalidFormat,
|
|
440
529
|
data: {
|
|
441
530
|
aggregateType: t,
|
|
442
531
|
reason: e
|
|
443
532
|
}
|
|
444
533
|
};
|
|
445
|
-
return new r(i,
|
|
534
|
+
return new r(i, s);
|
|
446
535
|
}
|
|
447
536
|
/**
|
|
448
537
|
* Error for ID mismatch during snapshot restoration
|
|
449
538
|
*/
|
|
450
539
|
static idMismatch(t, e) {
|
|
451
|
-
const i = `ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`,
|
|
452
|
-
code:
|
|
540
|
+
const i = `ID mismatch: Snapshot is for ID ${t}, but aggregate has ID ${e}`, s = {
|
|
541
|
+
code: g.ValidationFailed,
|
|
453
542
|
data: {
|
|
454
543
|
snapshotId: t,
|
|
455
544
|
aggregateId: e
|
|
456
545
|
}
|
|
457
546
|
};
|
|
458
|
-
return new r(i,
|
|
547
|
+
return new r(i, s);
|
|
459
548
|
}
|
|
460
549
|
/**
|
|
461
550
|
* Error for type mismatch during snapshot restoration
|
|
462
551
|
*/
|
|
463
552
|
static typeMismatch(t, e) {
|
|
464
|
-
const i = `Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`,
|
|
465
|
-
code:
|
|
553
|
+
const i = `Aggregate type mismatch: Snapshot is for type ${t}, but trying to load into ${e}`, s = {
|
|
554
|
+
code: g.ValidationFailed,
|
|
466
555
|
data: {
|
|
467
556
|
snapshotType: t,
|
|
468
557
|
aggregateType: e
|
|
469
558
|
}
|
|
470
559
|
};
|
|
471
|
-
return new r(i,
|
|
560
|
+
return new r(i, s);
|
|
472
561
|
}
|
|
473
562
|
/**
|
|
474
563
|
* Error for duplicate upcaster registration
|
|
475
564
|
*/
|
|
476
565
|
static duplicateUpcaster(t, e) {
|
|
477
|
-
const i = `Upcaster for event ${t} version ${e} already exists`,
|
|
478
|
-
code:
|
|
566
|
+
const i = `Upcaster for event ${t} version ${e} already exists`, s = {
|
|
567
|
+
code: g.DuplicateEntry,
|
|
479
568
|
data: {
|
|
480
569
|
eventType: t,
|
|
481
570
|
sourceVersion: e
|
|
482
571
|
}
|
|
483
572
|
};
|
|
484
|
-
return new r(i,
|
|
573
|
+
return new r(i, s);
|
|
485
574
|
}
|
|
486
575
|
/**
|
|
487
576
|
* Error for missing upcaster
|
|
488
577
|
*/
|
|
489
578
|
static missingUpcaster(t, e, i) {
|
|
490
|
-
const
|
|
491
|
-
code:
|
|
579
|
+
const s = `Missing upcaster for event ${t} from version ${e} to ${i}`, n = {
|
|
580
|
+
code: g.MissingValue,
|
|
492
581
|
data: {
|
|
493
582
|
eventType: t,
|
|
494
583
|
fromVersion: e,
|
|
495
584
|
toVersion: i
|
|
496
585
|
}
|
|
497
586
|
};
|
|
498
|
-
return new r(
|
|
587
|
+
return new r(s, n);
|
|
499
588
|
}
|
|
500
589
|
/**
|
|
501
590
|
* Error for configuration problems
|
|
502
591
|
*/
|
|
503
592
|
static configurationError(t, e) {
|
|
504
|
-
const i = `Configuration error in ${t}: ${e}`,
|
|
505
|
-
code:
|
|
593
|
+
const i = `Configuration error in ${t}: ${e}`, s = {
|
|
594
|
+
code: g.ValidationFailed,
|
|
506
595
|
data: {
|
|
507
596
|
aggregateType: t,
|
|
508
597
|
configurationError: e
|
|
509
598
|
}
|
|
510
599
|
};
|
|
511
|
-
return new r(i,
|
|
600
|
+
return new r(i, s);
|
|
512
601
|
}
|
|
513
602
|
/**
|
|
514
603
|
* Error for when apply method interception fails (e.g. method not found)
|
|
515
604
|
*/
|
|
516
605
|
static cannotInterceptApplyMethod(t, e) {
|
|
517
|
-
const i = `Cannot intercept apply method - method not found on aggregate ${t}`,
|
|
518
|
-
code:
|
|
606
|
+
const i = `Cannot intercept apply method - method not found on aggregate ${t}`, s = {
|
|
607
|
+
code: g.InternalError,
|
|
519
608
|
data: {
|
|
520
609
|
aggergateId: t,
|
|
521
610
|
...e
|
|
522
611
|
}
|
|
523
612
|
};
|
|
524
|
-
return new r(i,
|
|
613
|
+
return new r(i, s);
|
|
525
614
|
}
|
|
526
615
|
/**
|
|
527
616
|
* Error for when event store is not configured
|
|
528
617
|
*/
|
|
529
618
|
static eventStoreNotConfigured(t) {
|
|
530
619
|
const e = "Event store not configured", i = {
|
|
531
|
-
code:
|
|
620
|
+
code: g.MissingValue,
|
|
532
621
|
data: t
|
|
533
622
|
};
|
|
534
623
|
return new r(e, i);
|
|
@@ -537,17 +626,17 @@ class r extends M {
|
|
|
537
626
|
* Error for when aggregate does not support event replay
|
|
538
627
|
*/
|
|
539
628
|
static aggregateDoesNotSupportReplay(t, e) {
|
|
540
|
-
const i = `Aggregate ${t} does not support event replay`,
|
|
541
|
-
code:
|
|
629
|
+
const i = `Aggregate ${t} does not support event replay`, s = {
|
|
630
|
+
code: g.MethodNotSupported,
|
|
542
631
|
data: {
|
|
543
632
|
aggregateType: t,
|
|
544
633
|
...e
|
|
545
634
|
}
|
|
546
635
|
};
|
|
547
|
-
return new r(i,
|
|
636
|
+
return new r(i, s);
|
|
548
637
|
}
|
|
549
638
|
}
|
|
550
|
-
class
|
|
639
|
+
class S extends d {
|
|
551
640
|
constructor() {
|
|
552
641
|
super(...arguments), this.type = "eventSourcing", this.eventStore = null;
|
|
553
642
|
}
|
|
@@ -630,11 +719,11 @@ class E extends l {
|
|
|
630
719
|
this.aggregate.constructor.name,
|
|
631
720
|
"Event store not configured"
|
|
632
721
|
);
|
|
633
|
-
const i = await this.eventStore.getEventsAfterVersion(t, e),
|
|
634
|
-
|
|
722
|
+
const i = await this.eventStore.getEventsAfterVersion(t, e), s = this.aggregate;
|
|
723
|
+
s.loadFromHistory && i.length > 0 && s.loadFromHistory(i);
|
|
635
724
|
}
|
|
636
725
|
}
|
|
637
|
-
class
|
|
726
|
+
class _ extends d {
|
|
638
727
|
constructor() {
|
|
639
728
|
super(...arguments), this.type = "snapshot", this._snapshot = null;
|
|
640
729
|
}
|
|
@@ -656,7 +745,7 @@ class S extends l {
|
|
|
656
745
|
* @returns {IAggregateSnapshot<TState, TMeta>} Created snapshot with state and metadata
|
|
657
746
|
*/
|
|
658
747
|
createSnapshot(t, e) {
|
|
659
|
-
const i = this.aggregate.getDomainEvents(), n = (i.length > 0 ? i[i.length - 1] : null)?.metadata?.eventId,
|
|
748
|
+
const i = this.aggregate.getDomainEvents(), n = (i.length > 0 ? i[i.length - 1] : null)?.metadata?.eventId, h = {
|
|
660
749
|
aggregateId: this.aggregate.getId().toString(),
|
|
661
750
|
version: this.aggregate.getVersion(),
|
|
662
751
|
aggregateType: this.aggregate.constructor.name,
|
|
@@ -664,7 +753,7 @@ class S extends l {
|
|
|
664
753
|
timestamp: /* @__PURE__ */ new Date(),
|
|
665
754
|
...n !== void 0 && { lastEventId: n }
|
|
666
755
|
};
|
|
667
|
-
return e && (
|
|
756
|
+
return e && (h.metadata = e()), h;
|
|
668
757
|
}
|
|
669
758
|
/**
|
|
670
759
|
* @param {IAggregateSnapshot<TState, TMeta>} snapshot - Snapshot to restore from
|
|
@@ -675,14 +764,12 @@ class S extends l {
|
|
|
675
764
|
restoreFromSnapshot(t, e, i) {
|
|
676
765
|
if (!t || !t.state)
|
|
677
766
|
throw r.invalidSnapshot(this.aggregate.constructor.name, "Invalid snapshot");
|
|
678
|
-
const
|
|
679
|
-
if (t.aggregateId !==
|
|
680
|
-
throw r.idMismatch(t.aggregateId,
|
|
767
|
+
const s = this.aggregate.getId().toString();
|
|
768
|
+
if (t.aggregateId !== s)
|
|
769
|
+
throw r.idMismatch(t.aggregateId, s);
|
|
681
770
|
if (t.aggregateType !== this.aggregate.constructor.name)
|
|
682
771
|
throw r.typeMismatch(t.aggregateType, this.aggregate.constructor.name);
|
|
683
|
-
e(t.state), t.metadata && i && i(t.metadata)
|
|
684
|
-
const n = this.aggregate;
|
|
685
|
-
n._internal_setState && n._internal_setState({
|
|
772
|
+
e(t.state), t.metadata && i && i(t.metadata), this.aggregate._internal_setState(E, {
|
|
686
773
|
version: t.version,
|
|
687
774
|
initialVersion: t.version,
|
|
688
775
|
domainEvents: []
|
|
@@ -715,7 +802,7 @@ class S extends l {
|
|
|
715
802
|
return this._snapshot = null, t;
|
|
716
803
|
}
|
|
717
804
|
}
|
|
718
|
-
class
|
|
805
|
+
class v {
|
|
719
806
|
constructor(t) {
|
|
720
807
|
this.capabilities = [], this.params = t;
|
|
721
808
|
}
|
|
@@ -724,13 +811,13 @@ class m {
|
|
|
724
811
|
*/
|
|
725
812
|
static create(t) {
|
|
726
813
|
const e = {
|
|
727
|
-
id: t.id instanceof
|
|
814
|
+
id: t.id instanceof f ? t.id : new f(t.id, "text"),
|
|
728
815
|
version: t.version || 0
|
|
729
816
|
};
|
|
730
|
-
return new
|
|
817
|
+
return new v(e);
|
|
731
818
|
}
|
|
732
819
|
withSnapshots() {
|
|
733
|
-
return this.capabilities.push({ capability: new
|
|
820
|
+
return this.capabilities.push({ capability: new _() }), this;
|
|
734
821
|
}
|
|
735
822
|
/**
|
|
736
823
|
* @returns {this} Builder instance for method chaining
|
|
@@ -742,19 +829,19 @@ class m {
|
|
|
742
829
|
* @returns {this} Builder instance for method chaining
|
|
743
830
|
*/
|
|
744
831
|
withAudit() {
|
|
745
|
-
return this.capabilities.push({ capability: new
|
|
832
|
+
return this.capabilities.push({ capability: new w() }), this;
|
|
746
833
|
}
|
|
747
834
|
/**
|
|
748
835
|
* @param {IEventStore} [eventStore] - Optional event store for event sourcing
|
|
749
836
|
* @returns {this} Builder instance for method chaining
|
|
750
837
|
*/
|
|
751
838
|
withEventSourcing(t) {
|
|
752
|
-
const e = new
|
|
839
|
+
const e = new S();
|
|
753
840
|
return this.capabilities.push({
|
|
754
841
|
capability: e,
|
|
755
842
|
configure: (i) => {
|
|
756
|
-
const
|
|
757
|
-
t ?
|
|
843
|
+
const s = i;
|
|
844
|
+
t ? s.setEventStore(t) : this.eventStore && s.setEventStore(this.eventStore);
|
|
758
845
|
}
|
|
759
846
|
}), this;
|
|
760
847
|
}
|
|
@@ -777,9 +864,9 @@ class m {
|
|
|
777
864
|
return this.eventStore = t, this;
|
|
778
865
|
}
|
|
779
866
|
build(t) {
|
|
780
|
-
const e = t ||
|
|
781
|
-
for (const { capability:
|
|
782
|
-
i.addCapability(
|
|
867
|
+
const e = t || u, i = new e(this.params);
|
|
868
|
+
for (const { capability: s, configure: n } of this.capabilities)
|
|
869
|
+
i.addCapability(s), n && n(s);
|
|
783
870
|
return i;
|
|
784
871
|
}
|
|
785
872
|
/**
|
|
@@ -790,10 +877,10 @@ class m {
|
|
|
790
877
|
return this.withSnapshots().withVersioning().withAudit().withEventSourcing().build(t);
|
|
791
878
|
}
|
|
792
879
|
}
|
|
793
|
-
function
|
|
794
|
-
return
|
|
880
|
+
function U(a) {
|
|
881
|
+
return v.create(a);
|
|
795
882
|
}
|
|
796
|
-
class
|
|
883
|
+
class C {
|
|
797
884
|
constructor(t) {
|
|
798
885
|
this._id = t;
|
|
799
886
|
}
|
|
@@ -812,77 +899,77 @@ class w {
|
|
|
812
899
|
* @returns `true` if both entities have the same id; `false` otherwise.
|
|
813
900
|
*/
|
|
814
901
|
equals(t) {
|
|
815
|
-
return t == null ? !1 : t === this ? !0 : t instanceof
|
|
902
|
+
return t == null ? !1 : t === this ? !0 : t instanceof C ? this._id.equals(t._id) : !1;
|
|
816
903
|
}
|
|
817
904
|
}
|
|
818
|
-
function
|
|
819
|
-
return
|
|
905
|
+
function V(a) {
|
|
906
|
+
return a.hasCapability(_);
|
|
820
907
|
}
|
|
821
|
-
function
|
|
822
|
-
return
|
|
908
|
+
function I(a) {
|
|
909
|
+
return a.hasCapability(p);
|
|
823
910
|
}
|
|
824
|
-
function
|
|
825
|
-
return
|
|
911
|
+
function A(a) {
|
|
912
|
+
return a.hasCapability(w);
|
|
826
913
|
}
|
|
827
|
-
function
|
|
828
|
-
return
|
|
914
|
+
function $(a) {
|
|
915
|
+
return a.hasCapability(S);
|
|
829
916
|
}
|
|
830
|
-
function
|
|
831
|
-
if (!
|
|
917
|
+
function O(a) {
|
|
918
|
+
if (!V(a))
|
|
832
919
|
throw r.featureNotEnabled("snapshot");
|
|
833
|
-
return
|
|
920
|
+
return a;
|
|
834
921
|
}
|
|
835
|
-
function
|
|
836
|
-
return
|
|
922
|
+
function j(a) {
|
|
923
|
+
return V(a) ? l.ok(a) : l.fail(r.featureNotEnabled("snapshot"));
|
|
837
924
|
}
|
|
838
|
-
function
|
|
839
|
-
if (!
|
|
925
|
+
function k(a) {
|
|
926
|
+
if (!I(a))
|
|
840
927
|
throw r.featureNotEnabled("versioning");
|
|
841
|
-
return
|
|
928
|
+
return a;
|
|
842
929
|
}
|
|
843
|
-
function
|
|
844
|
-
return
|
|
930
|
+
function P(a) {
|
|
931
|
+
return I(a) ? l.ok(a) : l.fail(r.featureNotEnabled("versioning"));
|
|
845
932
|
}
|
|
846
|
-
function
|
|
847
|
-
if (!
|
|
933
|
+
function W(a) {
|
|
934
|
+
if (!A(a))
|
|
848
935
|
throw r.featureNotEnabled("audit");
|
|
849
|
-
return
|
|
936
|
+
return a;
|
|
850
937
|
}
|
|
851
|
-
function
|
|
852
|
-
return
|
|
938
|
+
function z(a) {
|
|
939
|
+
return A(a) ? l.ok(a) : l.fail(r.featureNotEnabled("audit"));
|
|
853
940
|
}
|
|
854
|
-
function
|
|
855
|
-
if (
|
|
941
|
+
function B(a) {
|
|
942
|
+
if (!$(a))
|
|
856
943
|
throw r.featureNotEnabled("eventSourcing");
|
|
857
|
-
return
|
|
944
|
+
return a;
|
|
858
945
|
}
|
|
859
|
-
function R(
|
|
860
|
-
return
|
|
946
|
+
function R(a) {
|
|
947
|
+
return $(a) ? l.ok(a) : l.fail(r.featureNotEnabled("eventSourcing"));
|
|
861
948
|
}
|
|
862
|
-
function
|
|
863
|
-
return
|
|
949
|
+
function q(a) {
|
|
950
|
+
return a.getCapabilityTypes();
|
|
864
951
|
}
|
|
865
|
-
function
|
|
866
|
-
return t.every((e) =>
|
|
952
|
+
function K(a, t) {
|
|
953
|
+
return t.every((e) => a.hasCapability(e));
|
|
867
954
|
}
|
|
868
955
|
export {
|
|
869
|
-
|
|
956
|
+
v as AggregateBuilder,
|
|
870
957
|
r as AggregateError,
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
958
|
+
u as AggregateRoot,
|
|
959
|
+
w as AuditCapability,
|
|
960
|
+
C as Entity,
|
|
961
|
+
S as EventSourcingCapability,
|
|
962
|
+
_ as SnapshotCapability,
|
|
876
963
|
p as VersioningCapability,
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
964
|
+
U as aggregateBuilder,
|
|
965
|
+
W as asAuditAggregate,
|
|
966
|
+
B as asEventSourcingAggregate,
|
|
967
|
+
O as asSnapshotAggregate,
|
|
968
|
+
k as asVersioningAggregate,
|
|
969
|
+
q as getAggregateCapabilities,
|
|
970
|
+
K as hasAllCapabilities,
|
|
971
|
+
z as tryAsAuditAggregate,
|
|
885
972
|
R as tryAsEventSourcingAggregate,
|
|
886
|
-
|
|
887
|
-
|
|
973
|
+
j as tryAsSnapshotAggregate,
|
|
974
|
+
P as tryAsVersioningAggregate
|
|
888
975
|
};
|