@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.
Files changed (50) hide show
  1. package/dist/aggregate-changes.js +1 -1
  2. package/dist/aggregate-changes.js.map +1 -1
  3. package/dist/base-entity.d.ts.map +1 -1
  4. package/dist/base-entity.js +17 -5
  5. package/dist/base-entity.js.map +1 -1
  6. package/dist/change-tracker.d.ts +1 -1
  7. package/dist/change-tracker.d.ts.map +1 -1
  8. package/dist/change-tracker.js +20 -8
  9. package/dist/change-tracker.js.map +1 -1
  10. package/dist/criteria.js +6 -5
  11. package/dist/criteria.js.map +1 -1
  12. package/dist/domain-event-bus.js +4 -4
  13. package/dist/domain-event-bus.js.map +1 -1
  14. package/dist/domain-event.js +3 -0
  15. package/dist/domain-event.js.map +1 -1
  16. package/dist/entity-changes.js +1 -0
  17. package/dist/entity-changes.js.map +1 -1
  18. package/dist/entity-schema-registry.d.ts +4 -0
  19. package/dist/entity-schema-registry.d.ts.map +1 -1
  20. package/dist/entity-schema-registry.js +8 -6
  21. package/dist/entity-schema-registry.js.map +1 -1
  22. package/dist/exceptions.js +26 -1
  23. package/dist/exceptions.js.map +1 -1
  24. package/dist/id.js +2 -0
  25. package/dist/id.js.map +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/paginated-result.d.ts.map +1 -1
  29. package/dist/paginated-result.js +9 -0
  30. package/dist/paginated-result.js.map +1 -1
  31. package/dist/repository/unit-of-work.js +3 -7
  32. package/dist/repository/unit-of-work.js.map +1 -1
  33. package/dist/types/domain.d.ts +0 -1
  34. package/dist/types/domain.d.ts.map +1 -1
  35. package/dist/validation-error.d.ts +15 -1
  36. package/dist/validation-error.d.ts.map +1 -1
  37. package/dist/validation-error.js +46 -3
  38. package/dist/validation-error.js.map +1 -1
  39. package/dist/value-object.d.ts.map +1 -1
  40. package/dist/value-object.js +29 -1
  41. package/dist/value-object.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/base-entity.ts +9 -3
  44. package/src/change-tracker.ts +6 -16
  45. package/src/entity-schema-registry.ts +8 -5
  46. package/src/index.ts +1 -1
  47. package/src/paginated-result.ts +12 -0
  48. package/src/types/domain.ts +0 -1
  49. package/src/validation-error.ts +54 -4
  50. package/src/value-object.ts +5 -0
@@ -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, oldArray, [
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
- throw new ConfigurationError(
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,
@@ -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
 
@@ -15,7 +15,6 @@ export type VOValidation<T> = DomainValidation<T>;
15
15
 
16
16
  export interface VOHooks<T, E> {
17
17
  onBeforeCreate?: (props: T) => void;
18
- onBeforeUpdate?: (entity: E, snapshot: T) => boolean;
19
18
  onCreate?: (entity: E) => void;
20
19
  rules?: (entity: E) => void;
21
20
  }
@@ -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
  /**
@@ -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) {