@woltz/rich-domain 1.2.1 → 1.2.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/CHANGELOG.md +25 -0
- package/dist/base-entity.d.ts +1 -1
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +3 -41
- package/dist/base-entity.js.map +1 -1
- package/dist/{history-tracker.d.ts → change-tracker.d.ts} +3 -3
- package/dist/change-tracker.d.ts.map +1 -0
- package/dist/{history-tracker.js → change-tracker.js} +9 -56
- package/dist/change-tracker.js.map +1 -0
- package/dist/constants.d.ts +7 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +65 -0
- package/dist/constants.js.map +1 -1
- package/dist/criteria.d.ts.map +1 -1
- package/dist/criteria.js +6 -4
- package/dist/criteria.js.map +1 -1
- package/dist/domain-event.d.ts.map +1 -1
- package/dist/domain-event.js +0 -3
- package/dist/domain-event.js.map +1 -1
- package/dist/entity-changes.d.ts.map +1 -1
- package/dist/entity-changes.js +0 -4
- package/dist/entity-changes.js.map +1 -1
- package/dist/entity-schema-registry.d.ts.map +1 -1
- package/dist/entity-schema-registry.js +2 -8
- package/dist/entity-schema-registry.js.map +1 -1
- package/dist/entity.d.ts +0 -6
- package/dist/entity.d.ts.map +1 -1
- package/dist/entity.js +0 -9
- package/dist/entity.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -12
- package/dist/index.js.map +1 -1
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +0 -15
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/base-repository.d.ts +1 -1
- package/dist/repository/base-repository.d.ts.map +1 -1
- package/dist/repository/index.d.ts.map +1 -1
- package/dist/repository/index.js +0 -6
- package/dist/repository/index.js.map +1 -1
- package/dist/repository/unit-of-work.d.ts.map +1 -1
- package/dist/repository/unit-of-work.js +0 -3
- package/dist/repository/unit-of-work.js.map +1 -1
- package/dist/types/change-tracker.d.ts +10 -0
- package/dist/types/change-tracker.d.ts.map +1 -1
- package/dist/types/domain.d.ts +4 -6
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/criteria-operator-validation.d.ts +1 -0
- package/dist/utils/criteria-operator-validation.d.ts.map +1 -1
- package/dist/utils/criteria-operator-validation.js +39 -17
- package/dist/utils/criteria-operator-validation.js.map +1 -1
- package/dist/validation-error.d.ts.map +1 -1
- package/dist/validation-error.js +1 -3
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.d.ts +1 -1
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +0 -1
- package/dist/value-object.js.map +1 -1
- package/package.json +1 -1
- package/src/base-entity.ts +4 -51
- package/src/{history-tracker.ts → change-tracker.ts} +20 -72
- package/src/constants.ts +75 -1
- package/src/criteria.ts +9 -3
- package/src/domain-event.ts +0 -4
- package/src/entity-changes.ts +0 -5
- package/src/entity-schema-registry.ts +2 -22
- package/src/entity.ts +0 -11
- package/src/index.ts +15 -20
- package/src/paginated-result.ts +0 -21
- package/src/repository/base-repository.ts +1 -1
- package/src/repository/index.ts +0 -9
- package/src/repository/unit-of-work.ts +0 -4
- package/src/types/change-tracker.ts +16 -4
- package/src/types/domain.ts +4 -8
- package/src/types/index.ts +1 -1
- package/src/utils/criteria-operator-validation.ts +57 -19
- package/src/validation-error.ts +1 -3
- package/src/value-object.ts +1 -2
- package/tests/history-tracker.spec.ts +1 -1
- package/dist/history-tracker.d.ts.map +0 -1
- package/dist/history-tracker.js.map +0 -1
- package/dist/types/history-tracker.d.ts +0 -47
- package/dist/types/history-tracker.d.ts.map +0 -1
- package/dist/types/history-tracker.js +0 -2
- package/dist/types/history-tracker.js.map +0 -1
- package/src/types/history-tracker.ts +0 -58
|
@@ -27,7 +27,7 @@ export type OnChangeValidator = (
|
|
|
27
27
|
* - Generates AggregateChanges for persistence
|
|
28
28
|
* - Supports validation on change via onChangeValidator
|
|
29
29
|
*/
|
|
30
|
-
export class
|
|
30
|
+
export class ChangeTracker {
|
|
31
31
|
private history: HistoryEntry[] = [];
|
|
32
32
|
private originalValues: Map<string, any> = new Map();
|
|
33
33
|
private trackedArrays: Map<string, ArrayState> = new Map();
|
|
@@ -41,7 +41,7 @@ export class HistoryTracker {
|
|
|
41
41
|
private depth: number = 0,
|
|
42
42
|
private parentId?: string,
|
|
43
43
|
private parentEntity?: string,
|
|
44
|
-
private rootTracker?:
|
|
44
|
+
private rootTracker?: ChangeTracker
|
|
45
45
|
) {
|
|
46
46
|
if (!rootTracker) {
|
|
47
47
|
this.rootTracker = this;
|
|
@@ -60,10 +60,6 @@ export class HistoryTracker {
|
|
|
60
60
|
this.getRootTracker().onChangeValidator = validator;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// ============================================================================
|
|
64
|
-
// Initial State Capture
|
|
65
|
-
// ============================================================================
|
|
66
|
-
|
|
67
63
|
private captureInitialState(): void {
|
|
68
64
|
if (this.depth > 0) return;
|
|
69
65
|
this.captureEntityState(this.target, this.rootEntityName, "", 0);
|
|
@@ -153,10 +149,6 @@ export class HistoryTracker {
|
|
|
153
149
|
});
|
|
154
150
|
}
|
|
155
151
|
|
|
156
|
-
// ============================================================================
|
|
157
|
-
// Proxy Creation
|
|
158
|
-
// ============================================================================
|
|
159
|
-
|
|
160
152
|
createProxy(): any {
|
|
161
153
|
const handler: ProxyHandler<any> = {
|
|
162
154
|
get: (target, prop, receiver) => {
|
|
@@ -177,7 +169,7 @@ export class HistoryTracker {
|
|
|
177
169
|
}
|
|
178
170
|
|
|
179
171
|
if (this.isEntityOrVO(value)) {
|
|
180
|
-
const nestedTracker = new
|
|
172
|
+
const nestedTracker = new ChangeTracker(
|
|
181
173
|
value,
|
|
182
174
|
this.getEntityName(value),
|
|
183
175
|
currentPath,
|
|
@@ -200,7 +192,6 @@ export class HistoryTracker {
|
|
|
200
192
|
return true;
|
|
201
193
|
}
|
|
202
194
|
|
|
203
|
-
// Call validator before making the change
|
|
204
195
|
const rootTracker = this.getRootTracker();
|
|
205
196
|
if (rootTracker.onChangeValidator) {
|
|
206
197
|
try {
|
|
@@ -210,21 +201,17 @@ export class HistoryTracker {
|
|
|
210
201
|
newValue
|
|
211
202
|
);
|
|
212
203
|
if (result === false) {
|
|
213
|
-
|
|
214
|
-
return true; // Return true to not throw, but don't apply change
|
|
204
|
+
return true;
|
|
215
205
|
}
|
|
216
206
|
} catch (error) {
|
|
217
|
-
// Validator threw an error - propagate it
|
|
218
207
|
throw error;
|
|
219
208
|
}
|
|
220
209
|
}
|
|
221
210
|
|
|
222
|
-
// Store original value
|
|
223
211
|
if (!rootTracker.originalValues.has(currentPath)) {
|
|
224
212
|
rootTracker.originalValues.set(currentPath, oldValue);
|
|
225
213
|
}
|
|
226
214
|
|
|
227
|
-
// Record in history
|
|
228
215
|
rootTracker.history.push({
|
|
229
216
|
path: currentPath,
|
|
230
217
|
previousValue: oldValue,
|
|
@@ -234,7 +221,6 @@ export class HistoryTracker {
|
|
|
234
221
|
|
|
235
222
|
const result = Reflect.set(target, prop, newValue, receiver);
|
|
236
223
|
|
|
237
|
-
// Handle special cases
|
|
238
224
|
if (Array.isArray(newValue)) {
|
|
239
225
|
this.handleArrayAssignment(currentPath, oldValue);
|
|
240
226
|
} else if (this.isEntityOrVO(newValue) || this.isEntityOrVO(oldValue)) {
|
|
@@ -284,14 +270,12 @@ export class HistoryTracker {
|
|
|
284
270
|
return function (...args: any[]) {
|
|
285
271
|
const oldArray = target.slice();
|
|
286
272
|
|
|
287
|
-
// Call validator before array mutation
|
|
288
273
|
if (rootTracker.onChangeValidator) {
|
|
289
274
|
try {
|
|
290
|
-
const result = rootTracker.onChangeValidator(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
);
|
|
275
|
+
const result = rootTracker.onChangeValidator(path, oldArray, [
|
|
276
|
+
...oldArray,
|
|
277
|
+
...args,
|
|
278
|
+
]);
|
|
295
279
|
if (result === false) {
|
|
296
280
|
return undefined;
|
|
297
281
|
}
|
|
@@ -317,7 +301,7 @@ export class HistoryTracker {
|
|
|
317
301
|
|
|
318
302
|
if (!isNaN(Number(prop)) && tracker.isEntityOrVO(value)) {
|
|
319
303
|
const nestedPath = `${path}[${String(prop)}]`;
|
|
320
|
-
const nestedTracker = new
|
|
304
|
+
const nestedTracker = new ChangeTracker(
|
|
321
305
|
value,
|
|
322
306
|
tracker.getEntityName(value),
|
|
323
307
|
nestedPath,
|
|
@@ -336,7 +320,6 @@ export class HistoryTracker {
|
|
|
336
320
|
if (!isNaN(Number(prop))) {
|
|
337
321
|
const oldArray = target.slice();
|
|
338
322
|
|
|
339
|
-
// Call validator before array item change
|
|
340
323
|
if (rootTracker.onChangeValidator) {
|
|
341
324
|
try {
|
|
342
325
|
const result = rootTracker.onChangeValidator(
|
|
@@ -368,10 +351,6 @@ export class HistoryTracker {
|
|
|
368
351
|
});
|
|
369
352
|
}
|
|
370
353
|
|
|
371
|
-
// ============================================================================
|
|
372
|
-
// getChanges() - Main Method
|
|
373
|
-
// ============================================================================
|
|
374
|
-
|
|
375
354
|
/**
|
|
376
355
|
* Returns all detected changes as AggregateChanges.
|
|
377
356
|
*/
|
|
@@ -388,7 +367,7 @@ export class HistoryTracker {
|
|
|
388
367
|
|
|
389
368
|
private analyzeRootChanges(
|
|
390
369
|
changes: AggregateChanges<any>,
|
|
391
|
-
rootTracker:
|
|
370
|
+
rootTracker: ChangeTracker
|
|
392
371
|
): void {
|
|
393
372
|
const changedFields: Record<string, any> = {};
|
|
394
373
|
let hasChanges = false;
|
|
@@ -420,7 +399,7 @@ export class HistoryTracker {
|
|
|
420
399
|
|
|
421
400
|
private analyzeCollectionChanges(
|
|
422
401
|
changes: AggregateChanges<any>,
|
|
423
|
-
rootTracker:
|
|
402
|
+
rootTracker: ChangeTracker
|
|
424
403
|
): void {
|
|
425
404
|
const allTrackedArrays = new Map<string, ArrayState>();
|
|
426
405
|
const processedArrays = new Set<any>();
|
|
@@ -456,7 +435,6 @@ export class HistoryTracker {
|
|
|
456
435
|
const itemEntityName = this.getEntityName(item);
|
|
457
436
|
changes.addCreate(itemEntityName, item, depth, parentId, parentEntity);
|
|
458
437
|
|
|
459
|
-
// Recursively mark nested items as created
|
|
460
438
|
this.markNestedItemsAsCreated(item, depth, changes);
|
|
461
439
|
}
|
|
462
440
|
|
|
@@ -482,7 +460,6 @@ export class HistoryTracker {
|
|
|
482
460
|
const deleteId = id || key!;
|
|
483
461
|
changes.addDelete(itemEntityName, deleteId, item, depth);
|
|
484
462
|
|
|
485
|
-
// Recursively mark nested items as deleted using ORIGINAL state
|
|
486
463
|
this.markNestedItemsAsDeleted(item, depth, changes, rootTracker);
|
|
487
464
|
}
|
|
488
465
|
}
|
|
@@ -508,7 +485,6 @@ export class HistoryTracker {
|
|
|
508
485
|
if (propName === "id") continue;
|
|
509
486
|
|
|
510
487
|
if (Array.isArray(value)) {
|
|
511
|
-
// Process all items in the array
|
|
512
488
|
for (const nestedItem of value) {
|
|
513
489
|
if (this.isEntityOrVO(nestedItem)) {
|
|
514
490
|
const nestedId = this.getEntityId(nestedItem);
|
|
@@ -523,8 +499,11 @@ export class HistoryTracker {
|
|
|
523
499
|
this.getEntityName(item)
|
|
524
500
|
);
|
|
525
501
|
|
|
526
|
-
|
|
527
|
-
|
|
502
|
+
this.markNestedItemsAsCreated(
|
|
503
|
+
nestedItem,
|
|
504
|
+
parentDepth + 1,
|
|
505
|
+
changes
|
|
506
|
+
);
|
|
528
507
|
}
|
|
529
508
|
}
|
|
530
509
|
}
|
|
@@ -540,22 +519,16 @@ export class HistoryTracker {
|
|
|
540
519
|
item: any,
|
|
541
520
|
parentDepth: number,
|
|
542
521
|
changes: AggregateChanges<any>,
|
|
543
|
-
rootTracker:
|
|
522
|
+
rootTracker: ChangeTracker
|
|
544
523
|
): void {
|
|
545
524
|
if (!item || typeof item !== "object") return;
|
|
546
525
|
|
|
547
|
-
// Get the ID to look up the original state
|
|
548
526
|
const itemId = this.getEntityId(item);
|
|
549
527
|
if (!itemId) return;
|
|
550
528
|
|
|
551
|
-
// Look through all tracked arrays to find nested items
|
|
552
529
|
for (const [, arrayState] of rootTracker.trackedArrays) {
|
|
553
|
-
// Check if this array belongs to our deleted item
|
|
554
530
|
if (arrayState.metadata.parentId === itemId) {
|
|
555
|
-
// Use the CLONED (original) state to get the items
|
|
556
|
-
// Note: cloned items are JSON objects, not Entity/VO instances
|
|
557
531
|
for (const nestedItem of arrayState.cloned) {
|
|
558
|
-
// Cloned items are JSON objects with an 'id' property
|
|
559
532
|
const id =
|
|
560
533
|
typeof nestedItem === "object" && nestedItem !== null
|
|
561
534
|
? nestedItem.id
|
|
@@ -564,7 +537,6 @@ export class HistoryTracker {
|
|
|
564
537
|
const entityName = arrayState.metadata.entityName;
|
|
565
538
|
changes.addDelete(entityName, id, nestedItem, parentDepth + 1);
|
|
566
539
|
|
|
567
|
-
// Recursively process this item's nested arrays
|
|
568
540
|
this.markNestedJsonItemAsDeleted(
|
|
569
541
|
id,
|
|
570
542
|
parentDepth + 1,
|
|
@@ -585,12 +557,10 @@ export class HistoryTracker {
|
|
|
585
557
|
itemId: string,
|
|
586
558
|
parentDepth: number,
|
|
587
559
|
changes: AggregateChanges<any>,
|
|
588
|
-
rootTracker:
|
|
560
|
+
rootTracker: ChangeTracker
|
|
589
561
|
): void {
|
|
590
|
-
// Look through all tracked arrays to find nested items of this parent
|
|
591
562
|
for (const [, arrayState] of rootTracker.trackedArrays) {
|
|
592
563
|
if (arrayState.metadata.parentId === itemId) {
|
|
593
|
-
// Process all items in this nested array
|
|
594
564
|
for (const nestedJsonItem of arrayState.cloned) {
|
|
595
565
|
if (typeof nestedJsonItem !== "object" || nestedJsonItem === null)
|
|
596
566
|
continue;
|
|
@@ -606,7 +576,6 @@ export class HistoryTracker {
|
|
|
606
576
|
parentDepth + 1
|
|
607
577
|
);
|
|
608
578
|
|
|
609
|
-
// Recursively process further nesting
|
|
610
579
|
this.markNestedJsonItemAsDeleted(
|
|
611
580
|
nestedId,
|
|
612
581
|
parentDepth + 1,
|
|
@@ -614,7 +583,6 @@ export class HistoryTracker {
|
|
|
614
583
|
rootTracker
|
|
615
584
|
);
|
|
616
585
|
} else {
|
|
617
|
-
// Value object - try to extract identity key
|
|
618
586
|
const key = this.extractIdentityKeyFromJson(
|
|
619
587
|
nestedJsonItem,
|
|
620
588
|
arrayState.original
|
|
@@ -640,20 +608,16 @@ export class HistoryTracker {
|
|
|
640
608
|
jsonItem: any,
|
|
641
609
|
originalArray: any[]
|
|
642
610
|
): string | undefined {
|
|
643
|
-
// Try to find the original ValueObject to get its identity key
|
|
644
611
|
for (const originalItem of originalArray) {
|
|
645
612
|
if (this.isEntityOrVO(originalItem)) {
|
|
646
613
|
const originalJson = this.deepClone(originalItem);
|
|
647
|
-
// Check if this matches our JSON item (rough comparison)
|
|
648
614
|
if (JSON.stringify(originalJson) === JSON.stringify(jsonItem)) {
|
|
649
|
-
// Found the matching original item - extract its identity key
|
|
650
615
|
const key = this.getItemKey(originalItem);
|
|
651
616
|
if (key) return key;
|
|
652
617
|
}
|
|
653
618
|
}
|
|
654
619
|
}
|
|
655
620
|
|
|
656
|
-
// Fallback: if it has an id, use that
|
|
657
621
|
if (jsonItem.id) return jsonItem.id;
|
|
658
622
|
|
|
659
623
|
return undefined;
|
|
@@ -692,7 +656,7 @@ export class HistoryTracker {
|
|
|
692
656
|
|
|
693
657
|
private analyzeEntityChanges(
|
|
694
658
|
changes: AggregateChanges<any>,
|
|
695
|
-
rootTracker:
|
|
659
|
+
rootTracker: ChangeTracker
|
|
696
660
|
): void {
|
|
697
661
|
for (const [path, trackedItem] of rootTracker.trackedEntities) {
|
|
698
662
|
if (path === "root") continue;
|
|
@@ -720,7 +684,6 @@ export class HistoryTracker {
|
|
|
720
684
|
case "deleted":
|
|
721
685
|
const id = this.getEntityId(originalValue);
|
|
722
686
|
if (id) {
|
|
723
|
-
// Use originalEntity instead of originalValue to preserve entity instance
|
|
724
687
|
changes.addDelete(entityName, id, originalEntity, depth);
|
|
725
688
|
}
|
|
726
689
|
break;
|
|
@@ -728,7 +691,6 @@ export class HistoryTracker {
|
|
|
728
691
|
case "replaced":
|
|
729
692
|
const oldId = this.getEntityId(originalValue);
|
|
730
693
|
if (oldId) {
|
|
731
|
-
// Use originalEntity instead of originalValue to preserve entity instance
|
|
732
694
|
changes.addDelete(entityName, oldId, originalEntity, depth);
|
|
733
695
|
}
|
|
734
696
|
changes.addCreate(
|
|
@@ -762,10 +724,6 @@ export class HistoryTracker {
|
|
|
762
724
|
}
|
|
763
725
|
}
|
|
764
726
|
|
|
765
|
-
// ============================================================================
|
|
766
|
-
// Change Detection Helpers
|
|
767
|
-
// ============================================================================
|
|
768
|
-
|
|
769
727
|
private detectEntityChangeState(
|
|
770
728
|
previous: any,
|
|
771
729
|
current: any
|
|
@@ -864,10 +822,6 @@ export class HistoryTracker {
|
|
|
864
822
|
return changes;
|
|
865
823
|
}
|
|
866
824
|
|
|
867
|
-
// ============================================================================
|
|
868
|
-
// Internal Handlers
|
|
869
|
-
// ============================================================================
|
|
870
|
-
|
|
871
825
|
private handleArrayAssignment(path: string, oldValue: any): void {
|
|
872
826
|
const rootTracker = this.getRootTracker();
|
|
873
827
|
|
|
@@ -892,7 +846,6 @@ export class HistoryTracker {
|
|
|
892
846
|
const existingTracked = rootTracker.trackedEntities.get(path);
|
|
893
847
|
|
|
894
848
|
rootTracker.trackedEntities.set(path, {
|
|
895
|
-
// Preserve original entity, or use oldValue if this is the first change
|
|
896
849
|
entity: existingTracked?.entity || oldValue,
|
|
897
850
|
metadata: {
|
|
898
851
|
entityName,
|
|
@@ -901,16 +854,11 @@ export class HistoryTracker {
|
|
|
901
854
|
parentEntity: this.rootEntityName,
|
|
902
855
|
path,
|
|
903
856
|
},
|
|
904
|
-
// Preserve original state
|
|
905
857
|
originalState: existingTracked?.originalState,
|
|
906
858
|
});
|
|
907
859
|
}
|
|
908
860
|
|
|
909
|
-
|
|
910
|
-
// Utility Methods
|
|
911
|
-
// ============================================================================
|
|
912
|
-
|
|
913
|
-
private getRootTracker(): HistoryTracker {
|
|
861
|
+
private getRootTracker(): ChangeTracker {
|
|
914
862
|
return this.rootTracker || this;
|
|
915
863
|
}
|
|
916
864
|
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,81 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ArrayOperators,
|
|
3
|
+
BooleanOperators,
|
|
4
|
+
DateOperators,
|
|
5
|
+
FilterOperator,
|
|
6
|
+
NumberOperators,
|
|
7
|
+
StringOperators,
|
|
8
|
+
ValidationConfig,
|
|
9
|
+
} from ".";
|
|
2
10
|
|
|
3
11
|
export const DEFAULT_VALIDATION_CONFIG: Required<ValidationConfig> = {
|
|
4
12
|
onCreate: true,
|
|
5
13
|
onUpdate: true,
|
|
6
14
|
throwOnError: true,
|
|
7
15
|
};
|
|
16
|
+
|
|
17
|
+
export const ARRAY_OPERATORS: ArrayOperators[] = [
|
|
18
|
+
"in",
|
|
19
|
+
"notIn",
|
|
20
|
+
"isNull",
|
|
21
|
+
"isNotNull",
|
|
22
|
+
];
|
|
23
|
+
export const BOOLEAN_OPERATORS: BooleanOperators[] = [
|
|
24
|
+
"equals",
|
|
25
|
+
"notEquals",
|
|
26
|
+
"isNull",
|
|
27
|
+
"isNotNull",
|
|
28
|
+
];
|
|
29
|
+
export const DATE_OPERATORS: DateOperators[] = [
|
|
30
|
+
"equals",
|
|
31
|
+
"notEquals",
|
|
32
|
+
"greaterThan",
|
|
33
|
+
"greaterThanOrEqual",
|
|
34
|
+
"lessThan",
|
|
35
|
+
"lessThanOrEqual",
|
|
36
|
+
"in",
|
|
37
|
+
"notIn",
|
|
38
|
+
"between",
|
|
39
|
+
"isNull",
|
|
40
|
+
"isNotNull",
|
|
41
|
+
];
|
|
42
|
+
export const NUMBER_OPERATORS: NumberOperators[] = [
|
|
43
|
+
"equals",
|
|
44
|
+
"notEquals",
|
|
45
|
+
"greaterThan",
|
|
46
|
+
"greaterThanOrEqual",
|
|
47
|
+
"lessThan",
|
|
48
|
+
"lessThanOrEqual",
|
|
49
|
+
"in",
|
|
50
|
+
"notIn",
|
|
51
|
+
"between",
|
|
52
|
+
"isNull",
|
|
53
|
+
"isNotNull",
|
|
54
|
+
];
|
|
55
|
+
export const STRING_OPERATORS: StringOperators[] = [
|
|
56
|
+
"equals",
|
|
57
|
+
"notEquals",
|
|
58
|
+
"contains",
|
|
59
|
+
"startsWith",
|
|
60
|
+
"endsWith",
|
|
61
|
+
"in",
|
|
62
|
+
"notIn",
|
|
63
|
+
"isNull",
|
|
64
|
+
"isNotNull",
|
|
65
|
+
];
|
|
66
|
+
export const FILTER_OPERATORS: FilterOperator[] = [
|
|
67
|
+
"equals",
|
|
68
|
+
"notEquals",
|
|
69
|
+
"greaterThan",
|
|
70
|
+
"greaterThanOrEqual",
|
|
71
|
+
"lessThan",
|
|
72
|
+
"lessThanOrEqual",
|
|
73
|
+
"contains",
|
|
74
|
+
"startsWith",
|
|
75
|
+
"endsWith",
|
|
76
|
+
"in",
|
|
77
|
+
"notIn",
|
|
78
|
+
"between",
|
|
79
|
+
"isNull",
|
|
80
|
+
"isNotNull",
|
|
81
|
+
];
|
package/src/criteria.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
isValidOperatorForType,
|
|
20
20
|
getValidOperatorsForType,
|
|
21
21
|
isOperator,
|
|
22
|
+
sanitizeFieldValue,
|
|
22
23
|
} from "./utils/criteria-operator-validation";
|
|
23
24
|
import { parseQueryValue } from "./utils/helpers";
|
|
24
25
|
|
|
@@ -460,10 +461,15 @@ export class Criteria<T = any> {
|
|
|
460
461
|
}
|
|
461
462
|
|
|
462
463
|
private validateOperator(operator: FilterOperator, value: any): void {
|
|
463
|
-
|
|
464
|
-
|
|
464
|
+
const sanitizedValue = sanitizeFieldValue(value, operator);
|
|
465
|
+
|
|
466
|
+
if (
|
|
467
|
+
sanitizedValue !== undefined &&
|
|
468
|
+
!isValidOperatorForType(sanitizedValue, operator)
|
|
469
|
+
) {
|
|
470
|
+
const validOps = getValidOperatorsForType(sanitizedValue);
|
|
465
471
|
throw new InvalidCriteriaError(
|
|
466
|
-
`Operator "${operator}" is not valid for type "${typeof
|
|
472
|
+
`Operator "${operator}" is not valid for type "${typeof sanitizedValue}". Valid operators: ${validOps.join(
|
|
467
473
|
", "
|
|
468
474
|
)}`,
|
|
469
475
|
operator
|
package/src/domain-event.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Domain Events - Event-Driven Architecture Support
|
|
3
|
-
// ============================================================================
|
|
4
|
-
|
|
5
1
|
import { IDomainEvent } from ".";
|
|
6
2
|
import { Id } from "./id";
|
|
7
3
|
|
package/src/entity-changes.ts
CHANGED
|
@@ -8,17 +8,14 @@ import { Id } from "./id";
|
|
|
8
8
|
export interface EntitySchema {
|
|
9
9
|
/** Entity name in the domain (e.g., 'User', 'Post') */
|
|
10
10
|
entity: string;
|
|
11
|
-
|
|
12
11
|
/** Table name in the database (e.g., 'users', 'blog_posts') */
|
|
13
12
|
table: string;
|
|
14
|
-
|
|
15
13
|
/**
|
|
16
14
|
* Field mapping: domain → database.
|
|
17
15
|
* Only include fields with different names.
|
|
18
16
|
* @example { email: 'user_email', createdAt: 'created_at' }
|
|
19
17
|
*/
|
|
20
18
|
fields?: Record<string, string>;
|
|
21
|
-
|
|
22
19
|
/**
|
|
23
20
|
* FK configuration for parent relation.
|
|
24
21
|
*/
|
|
@@ -148,15 +145,11 @@ export class EntitySchemaRegistry {
|
|
|
148
145
|
mapFields(entity: string, data: Record<string, any>): MappedEntityData {
|
|
149
146
|
const fields = this.getFieldsMap(entity);
|
|
150
147
|
const result: MappedEntityData = {};
|
|
151
|
-
|
|
152
148
|
for (const [key, value] of Object.entries(data)) {
|
|
153
|
-
if (this.isEntityOrCollection(value))
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
149
|
+
if (this.isEntityOrCollection(value)) continue;
|
|
156
150
|
const mappedKey = fields[key] ?? key;
|
|
157
151
|
result[mappedKey] = this.normalizeValue(value);
|
|
158
152
|
}
|
|
159
|
-
|
|
160
153
|
return result;
|
|
161
154
|
}
|
|
162
155
|
|
|
@@ -173,26 +166,18 @@ export class EntitySchemaRegistry {
|
|
|
173
166
|
): MappedEntityData {
|
|
174
167
|
const fields = this.getFieldsMap(entity);
|
|
175
168
|
const result: MappedEntityData = {};
|
|
176
|
-
|
|
177
|
-
// Map ID if it is an Entity or has entity-like structure
|
|
178
169
|
const hasId = (domainEntity as any).id;
|
|
179
170
|
if (hasId) {
|
|
180
|
-
// Extract id value
|
|
181
171
|
const idValue = hasId.value ?? hasId;
|
|
182
172
|
result["id"] = idValue;
|
|
183
173
|
}
|
|
184
|
-
|
|
185
|
-
// Get props
|
|
186
174
|
const props = (domainEntity as any).props || domainEntity;
|
|
187
|
-
|
|
188
175
|
for (const [key, value] of Object.entries(props)) {
|
|
189
|
-
if (key === "id") continue;
|
|
176
|
+
if (key === "id") continue;
|
|
190
177
|
if (this.isEntityOrCollection(value)) continue;
|
|
191
|
-
|
|
192
178
|
const mappedKey = fields[key] ?? key;
|
|
193
179
|
result[mappedKey] = this.normalizeValue(value);
|
|
194
180
|
}
|
|
195
|
-
|
|
196
181
|
return result;
|
|
197
182
|
}
|
|
198
183
|
|
|
@@ -206,7 +191,6 @@ export class EntitySchemaRegistry {
|
|
|
206
191
|
getParentFk(entity: string, parentId: string): Record<string, string> | null {
|
|
207
192
|
const schema = this.getSchema(entity);
|
|
208
193
|
if (!schema.parentFk) return null;
|
|
209
|
-
|
|
210
194
|
return { [schema.parentFk.field]: parentId };
|
|
211
195
|
}
|
|
212
196
|
|
|
@@ -250,12 +234,9 @@ export class EntitySchemaRegistry {
|
|
|
250
234
|
if (Array.isArray(value)) return true;
|
|
251
235
|
if (value instanceof Entity) return true;
|
|
252
236
|
if (value instanceof ValueObject) return true;
|
|
253
|
-
|
|
254
|
-
// Checks if it has the structure of an Entity (object with 'id' that has 'value')
|
|
255
237
|
if (typeof value === 'object' && value.id && typeof value.id === 'object' && 'value' in value.id) {
|
|
256
238
|
return true;
|
|
257
239
|
}
|
|
258
|
-
|
|
259
240
|
return false;
|
|
260
241
|
}
|
|
261
242
|
|
|
@@ -267,7 +248,6 @@ export class EntitySchemaRegistry {
|
|
|
267
248
|
if (value instanceof Id) return value.value;
|
|
268
249
|
if (value instanceof Date) return value;
|
|
269
250
|
if (typeof value === "object" && "value" in value) {
|
|
270
|
-
// Might be an ID or other wrapper
|
|
271
251
|
return value.value;
|
|
272
252
|
}
|
|
273
253
|
return value;
|
package/src/entity.ts
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Entity & Aggregate Classes
|
|
3
|
-
// ============================================================================
|
|
4
|
-
|
|
5
1
|
import { BaseEntity } from "./base-entity";
|
|
6
2
|
import { BaseProps } from "./types";
|
|
7
3
|
|
|
8
|
-
/**
|
|
9
|
-
* Entity - Has identity and lifecycle, but is not an aggregate root
|
|
10
|
-
*/
|
|
11
4
|
export class Entity<T extends BaseProps> extends BaseEntity<T> {}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Aggregate - Aggregate root that manages a consistency boundary
|
|
15
|
-
*/
|
|
16
5
|
export class Aggregate<T extends BaseProps> extends BaseEntity<T> {}
|
package/src/index.ts
CHANGED
|
@@ -1,29 +1,16 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// Rich Domain Library - Main Exports
|
|
3
|
-
// ============================================================================
|
|
4
|
-
|
|
5
|
-
// Core Classes
|
|
6
|
-
export { Id } from "./id";
|
|
7
|
-
export { Entity, Aggregate } from "./entity";
|
|
8
|
-
export { ValueObject } from "./value-object";
|
|
9
|
-
export { Mapper } from "./mapper";
|
|
10
|
-
export { EntitySchemaRegistry } from "./entity-schema-registry";
|
|
11
|
-
|
|
12
1
|
export * from "./validation-error";
|
|
13
|
-
|
|
14
|
-
// Domain Events
|
|
15
2
|
export * from "./domain-event";
|
|
16
3
|
export * from "./domain-event-bus";
|
|
17
4
|
export * from "./exceptions";
|
|
18
|
-
|
|
19
|
-
// Criteria
|
|
20
5
|
export * from "./criteria";
|
|
21
6
|
export * from "./paginated-result";
|
|
22
|
-
|
|
23
|
-
// Repository
|
|
24
7
|
export * from "./repository";
|
|
25
|
-
|
|
26
|
-
|
|
8
|
+
export { Id } from "./id";
|
|
9
|
+
export { Entity, Aggregate } from "./entity";
|
|
10
|
+
export { ValueObject } from "./value-object";
|
|
11
|
+
export { Mapper } from "./mapper";
|
|
12
|
+
export { EntitySchemaRegistry } from "./entity-schema-registry";
|
|
13
|
+
export { AggregateChanges } from "./aggregate-changes";
|
|
27
14
|
export {
|
|
28
15
|
DomainEventHandler,
|
|
29
16
|
EntityHooks,
|
|
@@ -41,7 +28,6 @@ export {
|
|
|
41
28
|
Order,
|
|
42
29
|
IUnitOfWork,
|
|
43
30
|
IDomainEventHandler,
|
|
44
|
-
EntityId,
|
|
45
31
|
FieldPath,
|
|
46
32
|
FilterOperator,
|
|
47
33
|
Search,
|
|
@@ -55,3 +41,12 @@ export {
|
|
|
55
41
|
ArrayOperators,
|
|
56
42
|
CriteriaOptions,
|
|
57
43
|
} from "./types";
|
|
44
|
+
export {
|
|
45
|
+
ARRAY_OPERATORS,
|
|
46
|
+
BOOLEAN_OPERATORS,
|
|
47
|
+
DATE_OPERATORS,
|
|
48
|
+
NUMBER_OPERATORS,
|
|
49
|
+
STRING_OPERATORS,
|
|
50
|
+
FILTER_OPERATORS,
|
|
51
|
+
} from "./constants";
|
|
52
|
+
export { isValidOperatorForType } from "./utils/criteria-operator-validation";
|