@woltz/rich-domain 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aggregate-changes.js +1 -1
- package/dist/aggregate-changes.js.map +1 -1
- package/dist/base-entity.d.ts.map +1 -1
- package/dist/base-entity.js +17 -5
- package/dist/base-entity.js.map +1 -1
- package/dist/change-tracker.d.ts +1 -1
- package/dist/change-tracker.d.ts.map +1 -1
- package/dist/change-tracker.js +20 -8
- package/dist/change-tracker.js.map +1 -1
- package/dist/criteria.js +6 -5
- package/dist/criteria.js.map +1 -1
- package/dist/domain-event-bus.js +4 -4
- package/dist/domain-event-bus.js.map +1 -1
- package/dist/domain-event.js +3 -0
- package/dist/domain-event.js.map +1 -1
- package/dist/entity-changes.js +1 -0
- package/dist/entity-changes.js.map +1 -1
- package/dist/entity-schema-registry.d.ts +4 -0
- package/dist/entity-schema-registry.d.ts.map +1 -1
- package/dist/entity-schema-registry.js +8 -6
- package/dist/entity-schema-registry.js.map +1 -1
- package/dist/exceptions.js +26 -1
- package/dist/exceptions.js.map +1 -1
- package/dist/id.js +2 -0
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/paginated-result.d.ts.map +1 -1
- package/dist/paginated-result.js +9 -0
- package/dist/paginated-result.js.map +1 -1
- package/dist/repository/unit-of-work.js +3 -7
- package/dist/repository/unit-of-work.js.map +1 -1
- package/dist/types/domain.d.ts +0 -1
- package/dist/types/domain.d.ts.map +1 -1
- package/dist/validation-error.d.ts +15 -1
- package/dist/validation-error.d.ts.map +1 -1
- package/dist/validation-error.js +46 -3
- package/dist/validation-error.js.map +1 -1
- package/dist/value-object.d.ts.map +1 -1
- package/dist/value-object.js +29 -1
- package/dist/value-object.js.map +1 -1
- package/package.json +1 -1
- package/src/base-entity.ts +9 -3
- package/src/change-tracker.ts +6 -16
- package/src/entity-schema-registry.ts +8 -5
- package/src/index.ts +1 -1
- package/src/paginated-result.ts +12 -0
- package/src/types/domain.ts +0 -1
- package/src/validation-error.ts +54 -4
- package/src/value-object.ts +5 -0
package/src/change-tracker.ts
CHANGED
|
@@ -9,11 +9,7 @@ import { AggregateChanges } from "./aggregate-changes";
|
|
|
9
9
|
* Callback for validation on property change.
|
|
10
10
|
* Return false to reject the change, or throw an error.
|
|
11
11
|
*/
|
|
12
|
-
export type OnChangeValidator = (
|
|
13
|
-
path: string,
|
|
14
|
-
oldValue: any,
|
|
15
|
-
newValue: any
|
|
16
|
-
) => boolean | void;
|
|
12
|
+
export type OnChangeValidator = (path: string, newValue: any) => boolean | void;
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Tracks changes in Aggregates using Proxy.
|
|
@@ -39,7 +35,9 @@ export class ChangeTracker {
|
|
|
39
35
|
private rootEntityName: string,
|
|
40
36
|
private path: string = "",
|
|
41
37
|
private depth: number = 0,
|
|
38
|
+
// @ts-expect-error - This is a private property
|
|
42
39
|
private parentId?: string,
|
|
40
|
+
// @ts-expect-error - This is a private property
|
|
43
41
|
private parentEntity?: string,
|
|
44
42
|
private rootTracker?: ChangeTracker
|
|
45
43
|
) {
|
|
@@ -195,11 +193,7 @@ export class ChangeTracker {
|
|
|
195
193
|
const rootTracker = this.getRootTracker();
|
|
196
194
|
if (rootTracker.onChangeValidator) {
|
|
197
195
|
try {
|
|
198
|
-
const result = rootTracker.onChangeValidator(
|
|
199
|
-
currentPath,
|
|
200
|
-
oldValue,
|
|
201
|
-
newValue
|
|
202
|
-
);
|
|
196
|
+
const result = rootTracker.onChangeValidator(currentPath, newValue);
|
|
203
197
|
if (result === false) {
|
|
204
198
|
return true;
|
|
205
199
|
}
|
|
@@ -272,7 +266,7 @@ export class ChangeTracker {
|
|
|
272
266
|
|
|
273
267
|
if (rootTracker.onChangeValidator) {
|
|
274
268
|
try {
|
|
275
|
-
const result = rootTracker.onChangeValidator(path,
|
|
269
|
+
const result = rootTracker.onChangeValidator(path, [
|
|
276
270
|
...oldArray,
|
|
277
271
|
...args,
|
|
278
272
|
]);
|
|
@@ -322,11 +316,7 @@ export class ChangeTracker {
|
|
|
322
316
|
|
|
323
317
|
if (rootTracker.onChangeValidator) {
|
|
324
318
|
try {
|
|
325
|
-
const result = rootTracker.onChangeValidator(
|
|
326
|
-
path,
|
|
327
|
-
oldArray,
|
|
328
|
-
newValue
|
|
329
|
-
);
|
|
319
|
+
const result = rootTracker.onChangeValidator(path, newValue);
|
|
330
320
|
if (result === false) {
|
|
331
321
|
return true;
|
|
332
322
|
}
|
|
@@ -160,6 +160,13 @@ export class EntitySchemaRegistry {
|
|
|
160
160
|
return schema;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Gets all registered schemas.
|
|
165
|
+
*/
|
|
166
|
+
getAllSchemas(): EntitySchema[] {
|
|
167
|
+
return Array.from(this.schemas.values());
|
|
168
|
+
}
|
|
169
|
+
|
|
163
170
|
/**
|
|
164
171
|
* Tries to get the schema of an entity, returns null if not found.
|
|
165
172
|
* @param entity - Entity name.
|
|
@@ -459,11 +466,7 @@ export class EntitySchemaRegistry {
|
|
|
459
466
|
const availableCollections = Object.keys(collections);
|
|
460
467
|
|
|
461
468
|
if (availableCollections.length === 0) {
|
|
462
|
-
|
|
463
|
-
`EntitySchemaRegistry: Entity '${entity}' has no collections configured, ` +
|
|
464
|
-
`but received operation for relation '${relationField}'. ` +
|
|
465
|
-
`Did you forget to configure collections in the schema?`
|
|
466
|
-
);
|
|
469
|
+
return;
|
|
467
470
|
}
|
|
468
471
|
|
|
469
472
|
if (!collections[relationField]) {
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { ValueObject } from "./value-object";
|
|
|
11
11
|
export { Mapper } from "./mapper";
|
|
12
12
|
export { EntitySchemaRegistry } from "./entity-schema-registry";
|
|
13
13
|
export { AggregateChanges } from "./aggregate-changes";
|
|
14
|
-
export {
|
|
14
|
+
export type {
|
|
15
15
|
DomainEventHandler,
|
|
16
16
|
EntityHooks,
|
|
17
17
|
Filter,
|
package/src/paginated-result.ts
CHANGED
|
@@ -81,6 +81,18 @@ export class PaginatedResult<T> {
|
|
|
81
81
|
pagination.offset,
|
|
82
82
|
pagination.offset + pagination.limit
|
|
83
83
|
);
|
|
84
|
+
|
|
85
|
+
const search = criteria.getSearch();
|
|
86
|
+
|
|
87
|
+
if (search) {
|
|
88
|
+
result = result.filter((item) => {
|
|
89
|
+
const values = Object.values(item as Record<string, unknown>);
|
|
90
|
+
return values.some((val) =>
|
|
91
|
+
String(val).toLowerCase().includes(search.toLowerCase())
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
84
96
|
return PaginatedResult.create(result, pagination, total);
|
|
85
97
|
}
|
|
86
98
|
|
package/src/types/domain.ts
CHANGED
package/src/validation-error.ts
CHANGED
|
@@ -6,19 +6,41 @@ export interface ValidationIssue {
|
|
|
6
6
|
export class ValidationError extends Error {
|
|
7
7
|
public readonly issues: ValidationIssue[];
|
|
8
8
|
public readonly __isValidationError = true;
|
|
9
|
+
public readonly entityName?: string;
|
|
9
10
|
|
|
10
|
-
constructor(issues: ValidationIssue[], message?: string) {
|
|
11
|
-
const errorMessage =
|
|
12
|
-
message || `Validation failed: ${issues.map(i => i.message).join(', ')}`;
|
|
11
|
+
constructor(issues: ValidationIssue[], options?: { message?: string; entityName?: string }) {
|
|
12
|
+
const errorMessage = options?.message || ValidationError.formatMessage(issues, options?.entityName);
|
|
13
13
|
super(errorMessage);
|
|
14
14
|
this.name = 'ValidationError';
|
|
15
15
|
this.issues = issues;
|
|
16
|
+
this.entityName = options?.entityName;
|
|
16
17
|
|
|
17
18
|
if (Error.captureStackTrace) {
|
|
18
19
|
Error.captureStackTrace(this, ValidationError);
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
private static formatMessage(issues: ValidationIssue[], entityName?: string): string {
|
|
24
|
+
const entityPrefix = entityName ? `[${entityName}] ` : '';
|
|
25
|
+
|
|
26
|
+
if (issues.length === 0) {
|
|
27
|
+
return `${entityPrefix}Validation failed`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (issues.length === 1) {
|
|
31
|
+
const issue = issues[0];
|
|
32
|
+
const pathStr = issue.path.length > 0 ? ` at "${issue.path.join('.')}"` : '';
|
|
33
|
+
return `${entityPrefix}Validation failed${pathStr}: ${issue.message}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const errorLines = issues.map((issue, index) => {
|
|
37
|
+
const pathStr = issue.path.length > 0 ? ` at "${issue.path.join('.')}"` : '';
|
|
38
|
+
return ` ${index + 1}. ${issue.message}${pathStr}`;
|
|
39
|
+
}).join('\n');
|
|
40
|
+
|
|
41
|
+
return `${entityPrefix}Validation failed with ${issues.length} error(s):\n${errorLines}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
22
44
|
/**
|
|
23
45
|
* Check if an error is a ValidationError (works across module boundaries)
|
|
24
46
|
*/
|
|
@@ -58,13 +80,41 @@ export class ValidationError extends Error {
|
|
|
58
80
|
/**
|
|
59
81
|
* Convert to a plain object for serialization
|
|
60
82
|
*/
|
|
61
|
-
toJSON(): { name: string; message: string; issues: ValidationIssue[] } {
|
|
83
|
+
toJSON(): { name: string; message: string; issues: ValidationIssue[]; entityName?: string } {
|
|
62
84
|
return {
|
|
63
85
|
name: this.name,
|
|
64
86
|
message: this.message,
|
|
65
87
|
issues: this.issues,
|
|
88
|
+
entityName: this.entityName,
|
|
66
89
|
};
|
|
67
90
|
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get a formatted string with all validation errors
|
|
94
|
+
*/
|
|
95
|
+
getFormattedErrors(): string {
|
|
96
|
+
return this.issues.map((issue) => {
|
|
97
|
+
const pathStr = issue.path.length > 0 ? ` [${issue.path.join('.')}]` : '';
|
|
98
|
+
return `${pathStr} ${issue.message}`;
|
|
99
|
+
}).join('\n');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get a summary of the error for logging
|
|
104
|
+
*/
|
|
105
|
+
getSummary(): string {
|
|
106
|
+
const entityPrefix = this.entityName ? `[${this.entityName}] ` : '';
|
|
107
|
+
const paths = this.issues
|
|
108
|
+
.filter(i => i.path.length > 0)
|
|
109
|
+
.map(i => i.path.join('.'));
|
|
110
|
+
|
|
111
|
+
if (paths.length === 0) {
|
|
112
|
+
return `${entityPrefix}Validation failed with ${this.issues.length} error(s)`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const uniquePaths = Array.from(new Set(paths));
|
|
116
|
+
return `${entityPrefix}Validation failed on: ${uniquePaths.join(', ')} (${this.issues.length} error(s))`;
|
|
117
|
+
}
|
|
68
118
|
}
|
|
69
119
|
|
|
70
120
|
/**
|
package/src/value-object.ts
CHANGED
|
@@ -24,6 +24,7 @@ export type IdentityKeyDefinition<T> = (keyof T)[] | keyof T;
|
|
|
24
24
|
export abstract class ValueObject<T> {
|
|
25
25
|
protected readonly props!: T;
|
|
26
26
|
private validationConfig: Required<ValidationConfig>;
|
|
27
|
+
// @ts-expect-error - This is a private property
|
|
27
28
|
private domainHooks?: VOHooks<T, any>;
|
|
28
29
|
private domainSchema?: StandardSchema<T>;
|
|
29
30
|
private domainEvents: IDomainEvent[] = [];
|
|
@@ -57,6 +58,10 @@ export abstract class ValueObject<T> {
|
|
|
57
58
|
);
|
|
58
59
|
const hooks = getStaticProperty<VOHooks<T, any>>(this, "hooks");
|
|
59
60
|
|
|
61
|
+
if (hooks?.onBeforeCreate) {
|
|
62
|
+
hooks.onBeforeCreate(props as T);
|
|
63
|
+
}
|
|
64
|
+
|
|
60
65
|
this.domainHooks = hooks;
|
|
61
66
|
|
|
62
67
|
if (validation?.schema) {
|