@woltz/rich-domain 1.0.0 → 1.2.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +37 -86
  2. package/LICENSE +20 -20
  3. package/dist/base-entity.d.ts +1 -1
  4. package/dist/base-entity.d.ts.map +1 -1
  5. package/dist/base-entity.js +15 -19
  6. package/dist/base-entity.js.map +1 -1
  7. package/dist/constants.js +1 -4
  8. package/dist/constants.js.map +1 -1
  9. package/dist/criteria.d.ts +24 -14
  10. package/dist/criteria.d.ts.map +1 -1
  11. package/dist/criteria.js +154 -67
  12. package/dist/criteria.js.map +1 -1
  13. package/dist/deep-proxy.d.ts.map +1 -1
  14. package/dist/deep-proxy.js +19 -9
  15. package/dist/deep-proxy.js.map +1 -1
  16. package/dist/domain-event-bus.d.ts +5 -6
  17. package/dist/domain-event-bus.d.ts.map +1 -1
  18. package/dist/domain-event-bus.js +4 -17
  19. package/dist/domain-event-bus.js.map +1 -1
  20. package/dist/domain-event.d.ts +1 -31
  21. package/dist/domain-event.d.ts.map +1 -1
  22. package/dist/domain-event.js +4 -7
  23. package/dist/domain-event.js.map +1 -1
  24. package/dist/entity.d.ts +2 -2
  25. package/dist/entity.js +3 -8
  26. package/dist/entity.js.map +1 -1
  27. package/dist/exceptions.d.ts +251 -0
  28. package/dist/exceptions.d.ts.map +1 -0
  29. package/dist/exceptions.js +321 -0
  30. package/dist/exceptions.js.map +1 -0
  31. package/dist/id.d.ts.map +1 -1
  32. package/dist/id.js +14 -7
  33. package/dist/id.js.map +1 -1
  34. package/dist/index.d.ts +2 -4
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +11 -40
  37. package/dist/index.js.map +1 -1
  38. package/dist/mapper.js +1 -5
  39. package/dist/mapper.js.map +1 -1
  40. package/dist/paginated-result.d.ts.map +1 -1
  41. package/dist/paginated-result.js +17 -9
  42. package/dist/paginated-result.js.map +1 -1
  43. package/dist/repository/base-repository.js +4 -11
  44. package/dist/repository/base-repository.js.map +1 -1
  45. package/dist/repository/index.d.ts +1 -2
  46. package/dist/repository/index.d.ts.map +1 -1
  47. package/dist/repository/index.js +3 -26
  48. package/dist/repository/index.js.map +1 -1
  49. package/dist/repository/unit-of-work.d.ts +0 -11
  50. package/dist/repository/unit-of-work.d.ts.map +1 -1
  51. package/dist/repository/unit-of-work.js +2 -43
  52. package/dist/repository/unit-of-work.js.map +1 -1
  53. package/dist/types/criteria.d.ts +31 -7
  54. package/dist/types/criteria.d.ts.map +1 -1
  55. package/dist/types/criteria.js +1 -4
  56. package/dist/types/criteria.js.map +1 -1
  57. package/dist/types/domain-event.d.ts +32 -0
  58. package/dist/types/domain-event.d.ts.map +1 -0
  59. package/dist/types/domain-event.js +2 -0
  60. package/dist/types/domain-event.js.map +1 -0
  61. package/dist/types/domain.d.ts +2 -2
  62. package/dist/types/domain.d.ts.map +1 -1
  63. package/dist/types/domain.js +1 -2
  64. package/dist/types/history-tracker.d.ts +1 -1
  65. package/dist/types/history-tracker.d.ts.map +1 -1
  66. package/dist/types/history-tracker.js +1 -2
  67. package/dist/types/index.d.ts +1 -0
  68. package/dist/types/index.d.ts.map +1 -1
  69. package/dist/types/index.js +7 -22
  70. package/dist/types/index.js.map +1 -1
  71. package/dist/types/standard-schema.js +1 -2
  72. package/dist/types/unit-of-work.js +1 -2
  73. package/dist/types/utils.js +1 -2
  74. package/dist/utils/criteria-operator-validation.d.ts +5 -0
  75. package/dist/utils/criteria-operator-validation.d.ts.map +1 -0
  76. package/dist/utils/criteria-operator-validation.js +143 -0
  77. package/dist/utils/criteria-operator-validation.js.map +1 -0
  78. package/dist/utils/helpers.d.ts +2 -0
  79. package/dist/utils/helpers.d.ts.map +1 -0
  80. package/dist/utils/helpers.js +10 -0
  81. package/dist/utils/helpers.js.map +1 -0
  82. package/dist/validation-error.js +3 -9
  83. package/dist/validation-error.js.map +1 -1
  84. package/dist/value-object.d.ts +1 -1
  85. package/dist/value-object.d.ts.map +1 -1
  86. package/dist/value-object.js +7 -14
  87. package/dist/value-object.js.map +1 -1
  88. package/eslint.config.js +9 -3
  89. package/jest.config.js +1 -1
  90. package/package.json +14 -20
  91. package/src/base-entity.ts +3 -3
  92. package/src/criteria.ts +268 -87
  93. package/src/deep-proxy.ts +50 -38
  94. package/src/domain-event-bus.ts +152 -166
  95. package/src/domain-event.ts +53 -90
  96. package/src/entity.ts +16 -16
  97. package/src/exceptions.ts +435 -0
  98. package/src/id.ts +107 -94
  99. package/src/index.ts +32 -8
  100. package/src/paginated-result.ts +15 -3
  101. package/src/repository/index.ts +1 -6
  102. package/src/repository/unit-of-work.ts +1 -44
  103. package/src/types/criteria.ts +95 -17
  104. package/src/types/domain-event.ts +38 -0
  105. package/src/types/domain.ts +2 -3
  106. package/src/types/history-tracker.ts +1 -1
  107. package/src/types/index.ts +1 -0
  108. package/src/utils/criteria-operator-validation.ts +171 -0
  109. package/src/utils/helpers.ts +6 -0
  110. package/src/validation-error.ts +97 -97
  111. package/src/value-object.ts +3 -6
  112. package/tests/criteria.test.ts +324 -1
  113. package/tests/domain-events.test.ts +431 -445
  114. package/tests/entity-validation.test.ts +1 -1
  115. package/tests/entity.test.ts +33 -33
  116. package/tests/repository.test.ts +4 -2
  117. package/tests/utils.ts +254 -151
  118. package/tests/value-object-validation.test.ts +0 -9
  119. package/tsconfig.json +2 -24
  120. package/.github/workflows/ci.yml +0 -40
  121. package/.husky/commit-msg +0 -1
  122. package/.husky/pre-commit +0 -1
  123. package/.vscode/settings.json +0 -3
  124. package/commitlint.config.js +0 -23
  125. package/dist/repository/in-memory-repository.d.ts +0 -50
  126. package/dist/repository/in-memory-repository.d.ts.map +0 -1
  127. package/dist/repository/in-memory-repository.js +0 -97
  128. package/dist/repository/in-memory-repository.js.map +0 -1
  129. package/src/repository/in-memory-repository.ts +0 -116
@@ -0,0 +1,171 @@
1
+ import {
2
+ ArrayOperators,
3
+ BooleanOperators,
4
+ DateOperators,
5
+ FILTER_OPERATORS,
6
+ FilterOperator,
7
+ NumberOperators,
8
+ StringOperators,
9
+ } from "../types";
10
+
11
+ export function isValidOperatorForType(
12
+ value: unknown,
13
+ operator: FilterOperator
14
+ ): boolean {
15
+ // Handle null/undefined
16
+ if (value === null || value === undefined) {
17
+ return ["isNull", "isNotNull", "equals", "notEquals"].includes(operator);
18
+ }
19
+
20
+ // Special case: between operator with array [min, max]
21
+ if (operator === "between" && Array.isArray(value) && value.length === 2) {
22
+ // Validate based on the type of the first element
23
+ const elementType = typeof value[0];
24
+ if (elementType === "number" || value[0] instanceof Date) {
25
+ return true;
26
+ }
27
+ return false;
28
+ }
29
+
30
+ const valueType = typeof value;
31
+
32
+ // String operators
33
+ if (valueType === "string") {
34
+ const validOps: StringOperators[] = [
35
+ "equals",
36
+ "notEquals",
37
+ "contains",
38
+ "startsWith",
39
+ "endsWith",
40
+ "in",
41
+ "notIn",
42
+ "isNull",
43
+ "isNotNull",
44
+ ];
45
+ return validOps.includes(operator as StringOperators);
46
+ }
47
+
48
+ // Number operators
49
+ if (valueType === "number") {
50
+ const validOps: NumberOperators[] = [
51
+ "equals",
52
+ "notEquals",
53
+ "greaterThan",
54
+ "greaterThanOrEqual",
55
+ "lessThan",
56
+ "lessThanOrEqual",
57
+ "in",
58
+ "notIn",
59
+ "between",
60
+ "isNull",
61
+ "isNotNull",
62
+ ];
63
+ return validOps.includes(operator as NumberOperators);
64
+ }
65
+
66
+ // Boolean operators
67
+ if (valueType === "boolean") {
68
+ const validOps: BooleanOperators[] = [
69
+ "equals",
70
+ "notEquals",
71
+ "isNull",
72
+ "isNotNull",
73
+ ];
74
+ return validOps.includes(operator as BooleanOperators);
75
+ }
76
+
77
+ // Date operators
78
+ if (value instanceof Date) {
79
+ const validOps: DateOperators[] = [
80
+ "equals",
81
+ "notEquals",
82
+ "greaterThan",
83
+ "greaterThanOrEqual",
84
+ "lessThan",
85
+ "lessThanOrEqual",
86
+ "in",
87
+ "notIn",
88
+ "between",
89
+ "isNull",
90
+ "isNotNull",
91
+ ];
92
+ return validOps.includes(operator as DateOperators);
93
+ }
94
+
95
+ // Array operators
96
+ if (Array.isArray(value)) {
97
+ const validOps: ArrayOperators[] = ["in", "notIn", "isNull", "isNotNull"];
98
+ return validOps.includes(operator as ArrayOperators);
99
+ }
100
+
101
+ // For unknown types, allow all operators
102
+ return true;
103
+ }
104
+
105
+ export function getValidOperatorsForType(value: unknown): FilterOperator[] {
106
+ if (value === null || value === undefined) {
107
+ return ["isNull", "isNotNull", "equals", "notEquals"];
108
+ }
109
+
110
+ const valueType = typeof value;
111
+
112
+ if (valueType === "string" && Number.isNaN(Number(value))) {
113
+ return [
114
+ "equals",
115
+ "notEquals",
116
+ "contains",
117
+ "startsWith",
118
+ "endsWith",
119
+ "in",
120
+ "notIn",
121
+ "isNull",
122
+ "isNotNull",
123
+ ];
124
+ }
125
+
126
+ if (valueType === "number") {
127
+ return [
128
+ "equals",
129
+ "notEquals",
130
+ "greaterThan",
131
+ "greaterThanOrEqual",
132
+ "lessThan",
133
+ "lessThanOrEqual",
134
+ "in",
135
+ "notIn",
136
+ "between",
137
+ "isNull",
138
+ "isNotNull",
139
+ ];
140
+ }
141
+
142
+ if (valueType === "boolean") {
143
+ return ["equals", "notEquals", "isNull", "isNotNull"];
144
+ }
145
+
146
+ if (value instanceof Date) {
147
+ return [
148
+ "equals",
149
+ "notEquals",
150
+ "greaterThan",
151
+ "greaterThanOrEqual",
152
+ "lessThan",
153
+ "lessThanOrEqual",
154
+ "in",
155
+ "notIn",
156
+ "between",
157
+ "isNull",
158
+ "isNotNull",
159
+ ];
160
+ }
161
+
162
+ if (Array.isArray(value)) {
163
+ return ["in", "notIn", "isNull", "isNotNull"];
164
+ }
165
+
166
+ return [...FILTER_OPERATORS];
167
+ }
168
+
169
+ export function isOperator(value: string): value is FilterOperator {
170
+ return FILTER_OPERATORS.includes(value as FilterOperator);
171
+ }
@@ -0,0 +1,6 @@
1
+ export function parseQueryValue(value: string): any {
2
+ if (!isNaN(Number(value))) return Number(value); // number
3
+ if (value === "true" || value === "false") return value === "true"; // boolean
4
+ if (!isNaN(Date.parse(value))) return new Date(value); // Date
5
+ return value; // string
6
+ }
@@ -1,97 +1,97 @@
1
- // ============================================================================
2
- // Validation Error - Domain Validation Errors
3
- // ============================================================================
4
-
5
- export interface ValidationIssue {
6
- path: string[];
7
- message: string;
8
- }
9
-
10
- export class ValidationError extends Error {
11
- public readonly issues: ValidationIssue[];
12
- public readonly __isValidationError = true; // Brand for identification
13
-
14
- constructor(issues: ValidationIssue[], message?: string) {
15
- const errorMessage =
16
- message || `Validation failed: ${issues.map(i => i.message).join(', ')}`;
17
- super(errorMessage);
18
- this.name = 'ValidationError';
19
- this.issues = issues;
20
-
21
- // Maintain proper stack trace
22
- if (Error.captureStackTrace) {
23
- Error.captureStackTrace(this, ValidationError);
24
- }
25
- }
26
-
27
- /**
28
- * Check if an error is a ValidationError (works across module boundaries)
29
- */
30
- static isValidationError(error: unknown): error is ValidationError {
31
- if (error instanceof ValidationError) {
32
- return true;
33
- }
34
- // Check by duck typing for cross-module compatibility
35
- return (
36
- error instanceof Error &&
37
- error.name === 'ValidationError' &&
38
- 'issues' in error &&
39
- Array.isArray((error as any).issues)
40
- );
41
- }
42
-
43
- /**
44
- * Get all error messages as a simple array
45
- */
46
- getMessages(): string[] {
47
- return this.issues.map(i => i.message);
48
- }
49
-
50
- /**
51
- * Get errors for a specific field path
52
- */
53
- getErrorsForPath(path: string): ValidationIssue[] {
54
- return this.issues.filter(i => i.path.join('.') === path);
55
- }
56
-
57
- /**
58
- * Check if a specific path has errors
59
- */
60
- hasErrorsForPath(path: string): boolean {
61
- return this.getErrorsForPath(path).length > 0;
62
- }
63
-
64
- /**
65
- * Convert to a plain object for serialization
66
- */
67
- toJSON(): { name: string; message: string; issues: ValidationIssue[] } {
68
- return {
69
- name: this.name,
70
- message: this.message,
71
- issues: this.issues,
72
- };
73
- }
74
- }
75
-
76
- /**
77
- * Helper to create a single validation issue
78
- */
79
- export function createValidationIssue(
80
- path: string | string[],
81
- message: string
82
- ): ValidationIssue {
83
- return {
84
- path: Array.isArray(path) ? path : path.split('.'),
85
- message,
86
- };
87
- }
88
-
89
- /**
90
- * Helper to throw a validation error with a single issue
91
- */
92
- export function throwValidationError(
93
- path: string | string[],
94
- message: string
95
- ): never {
96
- throw new ValidationError([createValidationIssue(path, message)]);
97
- }
1
+ // ============================================================================
2
+ // Validation Error - Domain Validation Errors
3
+ // ============================================================================
4
+
5
+ export interface ValidationIssue {
6
+ path: string[];
7
+ message: string;
8
+ }
9
+
10
+ export class ValidationError extends Error {
11
+ public readonly issues: ValidationIssue[];
12
+ public readonly __isValidationError = true; // Brand for identification
13
+
14
+ constructor(issues: ValidationIssue[], message?: string) {
15
+ const errorMessage =
16
+ message || `Validation failed: ${issues.map(i => i.message).join(', ')}`;
17
+ super(errorMessage);
18
+ this.name = 'ValidationError';
19
+ this.issues = issues;
20
+
21
+ // Maintain proper stack trace
22
+ if (Error.captureStackTrace) {
23
+ Error.captureStackTrace(this, ValidationError);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Check if an error is a ValidationError (works across module boundaries)
29
+ */
30
+ static isValidationError(error: unknown): error is ValidationError {
31
+ if (error instanceof ValidationError) {
32
+ return true;
33
+ }
34
+ // Check by duck typing for cross-module compatibility
35
+ return (
36
+ error instanceof Error &&
37
+ error.name === 'ValidationError' &&
38
+ 'issues' in error &&
39
+ Array.isArray((error as any).issues)
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Get all error messages as a simple array
45
+ */
46
+ getMessages(): string[] {
47
+ return this.issues.map(i => i.message);
48
+ }
49
+
50
+ /**
51
+ * Get errors for a specific field path
52
+ */
53
+ getErrorsForPath(path: string): ValidationIssue[] {
54
+ return this.issues.filter(i => i.path.join('.') === path);
55
+ }
56
+
57
+ /**
58
+ * Check if a specific path has errors
59
+ */
60
+ hasErrorsForPath(path: string): boolean {
61
+ return this.getErrorsForPath(path).length > 0;
62
+ }
63
+
64
+ /**
65
+ * Convert to a plain object for serialization
66
+ */
67
+ toJSON(): { name: string; message: string; issues: ValidationIssue[] } {
68
+ return {
69
+ name: this.name,
70
+ message: this.message,
71
+ issues: this.issues,
72
+ };
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Helper to create a single validation issue
78
+ */
79
+ export function createValidationIssue(
80
+ path: string | string[],
81
+ message: string
82
+ ): ValidationIssue {
83
+ return {
84
+ path: Array.isArray(path) ? path : path.split('.'),
85
+ message,
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Helper to throw a validation error with a single issue
91
+ */
92
+ export function throwValidationError(
93
+ path: string | string[],
94
+ message: string
95
+ ): never {
96
+ throw new ValidationError([createValidationIssue(path, message)]);
97
+ }
@@ -3,7 +3,7 @@
3
3
  // ============================================================================
4
4
 
5
5
  import { ValidationError } from "./validation-error";
6
- import { IDomainEvent } from "./domain-event";
6
+ import { IDomainEvent } from ".";
7
7
  import {
8
8
  VOHooks,
9
9
  ValidationConfig,
@@ -11,6 +11,7 @@ import {
11
11
  EntityValidation,
12
12
  } from "./types";
13
13
  import { DEFAULT_VALIDATION_CONFIG } from "./constants";
14
+ import { DomainError } from "./exceptions";
14
15
 
15
16
  // Helper to get static properties from constructor
16
17
  function getStaticProperty<T>(
@@ -50,11 +51,7 @@ export abstract class ValueObject<T> {
50
51
  ...validation?.config,
51
52
  };
52
53
 
53
- // Apply defaultValues
54
54
  let finalProps = { ...props } as T;
55
- if (hooks?.defaultValues) {
56
- finalProps = { ...hooks.defaultValues, ...props } as T;
57
- }
58
55
 
59
56
  // Validate schema on creation
60
57
  if (this.domainSchema && this.validationConfig.onCreate) {
@@ -84,7 +81,7 @@ export abstract class ValueObject<T> {
84
81
  const result = this.domainSchema["~standard"].validate(props);
85
82
 
86
83
  if (result instanceof Promise) {
87
- throw new Error(
84
+ throw new DomainError(
88
85
  "Async validation not supported in constructor. Use sync validation schema."
89
86
  );
90
87
  }