@woltz/rich-domain 1.9.1 → 1.9.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.
Files changed (95) hide show
  1. package/dist/cjs/core/aggregate-changes.d.ts +14 -0
  2. package/dist/cjs/core/aggregate-changes.d.ts.map +1 -1
  3. package/dist/cjs/core/aggregate-changes.js +18 -0
  4. package/dist/cjs/core/aggregate-changes.js.map +1 -1
  5. package/dist/cjs/core/base-entity.d.ts +2 -0
  6. package/dist/cjs/core/base-entity.d.ts.map +1 -1
  7. package/dist/cjs/core/base-entity.js +39 -41
  8. package/dist/cjs/core/base-entity.js.map +1 -1
  9. package/dist/cjs/core/change-tracker.d.ts +8 -0
  10. package/dist/cjs/core/change-tracker.d.ts.map +1 -1
  11. package/dist/cjs/core/change-tracker.js +36 -6
  12. package/dist/cjs/core/change-tracker.js.map +1 -1
  13. package/dist/cjs/core/value-object.d.ts.map +1 -1
  14. package/dist/cjs/core/value-object.js +3 -5
  15. package/dist/cjs/core/value-object.js.map +1 -1
  16. package/dist/cjs/repository/entity-schema-registry.d.ts +56 -3
  17. package/dist/cjs/repository/entity-schema-registry.d.ts.map +1 -1
  18. package/dist/cjs/repository/entity-schema-registry.js +61 -6
  19. package/dist/cjs/repository/entity-schema-registry.js.map +1 -1
  20. package/dist/cjs/utils/helpers.d.ts +1 -0
  21. package/dist/cjs/utils/helpers.d.ts.map +1 -1
  22. package/dist/cjs/utils/helpers.js +4 -0
  23. package/dist/cjs/utils/helpers.js.map +1 -1
  24. package/dist/esm/core/aggregate-changes.d.ts +14 -0
  25. package/dist/esm/core/aggregate-changes.d.ts.map +1 -1
  26. package/dist/esm/core/aggregate-changes.js +18 -0
  27. package/dist/esm/core/aggregate-changes.js.map +1 -1
  28. package/dist/esm/core/base-entity.d.ts +2 -0
  29. package/dist/esm/core/base-entity.d.ts.map +1 -1
  30. package/dist/esm/core/base-entity.js +37 -39
  31. package/dist/esm/core/base-entity.js.map +1 -1
  32. package/dist/esm/core/change-tracker.d.ts +8 -0
  33. package/dist/esm/core/change-tracker.d.ts.map +1 -1
  34. package/dist/esm/core/change-tracker.js +36 -6
  35. package/dist/esm/core/change-tracker.js.map +1 -1
  36. package/dist/esm/core/value-object.d.ts.map +1 -1
  37. package/dist/esm/core/value-object.js +1 -3
  38. package/dist/esm/core/value-object.js.map +1 -1
  39. package/dist/esm/repository/entity-schema-registry.d.ts +56 -3
  40. package/dist/esm/repository/entity-schema-registry.d.ts.map +1 -1
  41. package/dist/esm/repository/entity-schema-registry.js +61 -6
  42. package/dist/esm/repository/entity-schema-registry.js.map +1 -1
  43. package/dist/esm/utils/helpers.d.ts +1 -0
  44. package/dist/esm/utils/helpers.d.ts.map +1 -1
  45. package/dist/esm/utils/helpers.js +3 -0
  46. package/dist/esm/utils/helpers.js.map +1 -1
  47. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  48. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  49. package/dist/tsconfig.types.tsbuildinfo +1 -1
  50. package/dist/types/core/aggregate-changes.d.ts +14 -0
  51. package/dist/types/core/aggregate-changes.d.ts.map +1 -1
  52. package/dist/types/core/base-entity.d.ts +2 -0
  53. package/dist/types/core/base-entity.d.ts.map +1 -1
  54. package/dist/types/core/change-tracker.d.ts +8 -0
  55. package/dist/types/core/change-tracker.d.ts.map +1 -1
  56. package/dist/types/core/value-object.d.ts.map +1 -1
  57. package/dist/types/repository/entity-schema-registry.d.ts +56 -3
  58. package/dist/types/repository/entity-schema-registry.d.ts.map +1 -1
  59. package/dist/types/utils/helpers.d.ts +1 -0
  60. package/dist/types/utils/helpers.d.ts.map +1 -1
  61. package/package.json +68 -67
  62. package/src/constants.ts +82 -0
  63. package/src/core/aggregate-changes.ts +466 -0
  64. package/src/core/base-aggregate.ts +76 -0
  65. package/src/core/base-entity.ts +552 -0
  66. package/src/core/change-tracker.ts +1327 -0
  67. package/src/core/domain-event.ts +41 -0
  68. package/src/core/entity-changes.ts +146 -0
  69. package/src/core/entity.ts +13 -0
  70. package/src/core/id.ts +124 -0
  71. package/src/core/index.ts +9 -0
  72. package/src/core/value-object.ts +179 -0
  73. package/src/criteria.ts +574 -0
  74. package/src/exceptions.ts +549 -0
  75. package/src/index.ts +74 -0
  76. package/src/repository/base-repository.ts +81 -0
  77. package/src/repository/entity-schema-registry.ts +620 -0
  78. package/src/repository/index.ts +5 -0
  79. package/src/repository/mapper.ts +7 -0
  80. package/src/repository/paginated-result.ts +251 -0
  81. package/src/repository/unit-of-work.ts +76 -0
  82. package/src/types/change-tracker.ts +268 -0
  83. package/src/types/criteria.ts +197 -0
  84. package/src/types/domain-event.ts +29 -0
  85. package/src/types/domain.ts +41 -0
  86. package/src/types/event-bus.ts +17 -0
  87. package/src/types/index.ts +9 -0
  88. package/src/types/outbox-store.ts +97 -0
  89. package/src/types/standard-schema.ts +19 -0
  90. package/src/types/unit-of-work.ts +46 -0
  91. package/src/types/utils.ts +24 -0
  92. package/src/utils/criteria-operator-validation.ts +209 -0
  93. package/src/utils/crypto.ts +31 -0
  94. package/src/utils/helpers.ts +50 -0
  95. package/src/validation-error.ts +219 -0
@@ -0,0 +1,31 @@
1
+ const customCrypto = {
2
+ randomUUID: () => {
3
+ if (
4
+ typeof globalThis !== "undefined" &&
5
+ globalThis?.crypto &&
6
+ typeof globalThis.crypto.randomUUID === "function"
7
+ ) {
8
+ return globalThis.crypto.randomUUID();
9
+ }
10
+
11
+ const hexChars = "0123456789abcdef";
12
+ let uuid = "";
13
+
14
+ for (let i = 0; i < 36; i++) {
15
+ if (i === 8 || i === 13 || i === 18 || i === 23) {
16
+ uuid += "-";
17
+ } else if (i === 14) {
18
+ uuid += "4";
19
+ } else if (i === 19) {
20
+ uuid += hexChars.charAt(Math.floor(Math.random() * 4) + 8);
21
+ } else {
22
+ uuid += hexChars.charAt(Math.floor(Math.random() * 16));
23
+ }
24
+ }
25
+
26
+ return uuid;
27
+ },
28
+ };
29
+
30
+ export const UUID = customCrypto.randomUUID;
31
+ export default UUID;
@@ -0,0 +1,50 @@
1
+ export function parseQueryValue(value: unknown): any {
2
+ if (value === null || value === undefined) return value;
3
+ if (typeof value === "boolean") return value;
4
+ if (typeof value === "number") return value;
5
+ if (value instanceof Date) return value;
6
+ if (Array.isArray(value)) return value;
7
+ if (typeof value === "object") return value;
8
+
9
+ const str = String(value);
10
+ if (str === "true" || str === "false") return str === "true";
11
+ if (str.trim() !== "" && !isNaN(Number(str))) return Number(str);
12
+ if (!isNaN(Date.parse(str))) return new Date(str);
13
+
14
+ return str;
15
+ }
16
+
17
+ export function levenshteinDistance(a: string, b: string): number {
18
+ const matrix: number[][] = [];
19
+
20
+ for (let i = 0; i <= b.length; i++) {
21
+ matrix[i] = [i];
22
+ }
23
+
24
+ for (let j = 0; j <= a.length; j++) {
25
+ matrix[0][j] = j;
26
+ }
27
+
28
+ for (let i = 1; i <= b.length; i++) {
29
+ for (let j = 1; j <= a.length; j++) {
30
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
31
+ matrix[i][j] = matrix[i - 1][j - 1];
32
+ } else {
33
+ matrix[i][j] = Math.min(
34
+ matrix[i - 1][j - 1] + 1, // substitution
35
+ matrix[i][j - 1] + 1, // insertion
36
+ matrix[i - 1][j] + 1 // deletion
37
+ );
38
+ }
39
+ }
40
+ }
41
+
42
+ return matrix[b.length][a.length];
43
+ }
44
+
45
+ export function getStaticProperty<T>(
46
+ instance: any,
47
+ propertyName: string
48
+ ): T | undefined {
49
+ return instance.constructor[propertyName];
50
+ }
@@ -0,0 +1,219 @@
1
+ export interface ValidationIssue {
2
+ path: string[];
3
+ message: string;
4
+ }
5
+
6
+ export type FormattedValidationError = {
7
+ path: string;
8
+ message: string;
9
+ };
10
+
11
+ export class ValidationIssueCollector {
12
+ private issues: ValidationIssue[] = [];
13
+
14
+ add(path: string | string[], message: string): void {
15
+ this.issues.push(createValidationIssue(path, message));
16
+ }
17
+
18
+ getIssues(): readonly ValidationIssue[] {
19
+ return this.issues;
20
+ }
21
+
22
+ hasIssues(): boolean {
23
+ return this.issues.length > 0;
24
+ }
25
+
26
+ clear(): void {
27
+ this.issues = [];
28
+ }
29
+
30
+ toValidationError(options?: {
31
+ entityName?: string;
32
+ }): ValidationError | undefined {
33
+ if (!this.hasIssues()) {
34
+ return undefined;
35
+ }
36
+ return new ValidationError([...this.issues], options);
37
+ }
38
+ }
39
+
40
+ export class ValidationError extends Error {
41
+ public readonly issues: ValidationIssue[];
42
+ public readonly __isValidationError = true;
43
+ public readonly entityName?: string;
44
+
45
+ constructor(
46
+ issues: ValidationIssue[],
47
+ options?: { message?: string; entityName?: string }
48
+ ) {
49
+ const errorMessage =
50
+ options?.message ||
51
+ ValidationError.formatMessage(issues, options?.entityName);
52
+ super(errorMessage);
53
+ this.name = "ValidationError";
54
+ this.issues = issues;
55
+ this.entityName = options?.entityName;
56
+
57
+ if (Error.captureStackTrace) {
58
+ Error.captureStackTrace(this, ValidationError);
59
+ }
60
+ }
61
+
62
+ static fromIssues(
63
+ issues: ValidationIssue[],
64
+ options?: { message?: string; entityName?: string }
65
+ ): ValidationError {
66
+ return new ValidationError(issues, options);
67
+ }
68
+
69
+ static merge(
70
+ existing: ValidationError | undefined,
71
+ extra: ValidationIssue[],
72
+ options?: { entityName?: string }
73
+ ): ValidationError | undefined {
74
+ if (!existing && extra.length === 0) {
75
+ return undefined;
76
+ }
77
+
78
+ const allIssues = [...(existing?.issues ?? []), ...extra];
79
+ return new ValidationError(allIssues, {
80
+ entityName: options?.entityName ?? existing?.entityName,
81
+ });
82
+ }
83
+
84
+ private static formatMessage(
85
+ issues: ValidationIssue[],
86
+ entityName?: string
87
+ ): string {
88
+ const entityPrefix = entityName ? `[${entityName}] ` : "";
89
+
90
+ if (issues.length === 0) {
91
+ return `${entityPrefix}Validation failed`;
92
+ }
93
+
94
+ if (issues.length === 1) {
95
+ const issue = issues[0];
96
+ const pathStr =
97
+ issue.path.length > 0 ? ` at "${issue.path.join(".")}"` : "";
98
+ return `${entityPrefix}Validation failed${pathStr}: ${issue.message}`;
99
+ }
100
+
101
+ const errorLines = issues
102
+ .map((issue, index) => {
103
+ const pathStr =
104
+ issue.path.length > 0 ? ` at "${issue.path.join(".")}"` : "";
105
+ return ` ${index + 1}. ${issue.message}${pathStr}`;
106
+ })
107
+ .join("\n");
108
+
109
+ return `${entityPrefix}Validation failed with ${issues.length} error(s):\n${errorLines}`;
110
+ }
111
+
112
+ private static normalizePath(path: string | string[]): string {
113
+ return Array.isArray(path) ? path.join(".") : path;
114
+ }
115
+
116
+ /**
117
+ * Check if an error is a ValidationError (works across module boundaries)
118
+ */
119
+ static isValidationError(error: unknown): error is ValidationError {
120
+ if (error instanceof ValidationError) {
121
+ return true;
122
+ }
123
+ return (
124
+ error instanceof Error &&
125
+ error.name === "ValidationError" &&
126
+ "issues" in error &&
127
+ Array.isArray((error as any).issues)
128
+ );
129
+ }
130
+
131
+ /**
132
+ * Get all error messages as a simple array
133
+ */
134
+ getMessages(): string[] {
135
+ return this.issues.map((i) => i.message);
136
+ }
137
+
138
+ /**
139
+ * Get errors for a specific field path
140
+ */
141
+ getErrorsForPath(path: string | string[]): ValidationIssue[] {
142
+ const normalized = ValidationError.normalizePath(path);
143
+ return this.issues.filter((i) => i.path.join(".") === normalized);
144
+ }
145
+
146
+ /**
147
+ * Check if a specific path has errors
148
+ */
149
+ hasErrorsForPath(path: string | string[]): boolean {
150
+ return this.getErrorsForPath(path).length > 0;
151
+ }
152
+
153
+ /**
154
+ * Convert to a plain object for serialization
155
+ */
156
+ toJSON(): {
157
+ name: string;
158
+ message: string;
159
+ issues: ValidationIssue[];
160
+ entityName?: string;
161
+ } {
162
+ return {
163
+ name: this.name,
164
+ message: this.message,
165
+ issues: this.issues,
166
+ entityName: this.entityName,
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Get validation errors formatted for UI/API consumption
172
+ */
173
+ getFormattedErrors(): FormattedValidationError[] {
174
+ return this.issues.map((issue) => ({
175
+ path: issue.path.length > 0 ? issue.path.join(".") : "",
176
+ message: issue.message,
177
+ }));
178
+ }
179
+
180
+ /**
181
+ * Get a summary of the error for logging
182
+ */
183
+ getSummary(): string {
184
+ const entityPrefix = this.entityName ? `[${this.entityName}] ` : "";
185
+ const paths = this.issues
186
+ .filter((i) => i.path.length > 0)
187
+ .map((i) => i.path.join("."));
188
+
189
+ if (paths.length === 0) {
190
+ return `${entityPrefix}Validation failed with ${this.issues.length} error(s)`;
191
+ }
192
+
193
+ const uniquePaths = Array.from(new Set(paths));
194
+ return `${entityPrefix}Validation failed on: ${uniquePaths.join(", ")} (${this.issues.length} error(s))`;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Helper to create a single validation issue
200
+ */
201
+ export function createValidationIssue(
202
+ path: string | string[],
203
+ message: string
204
+ ): ValidationIssue {
205
+ return {
206
+ path: Array.isArray(path) ? path : path.split("."),
207
+ message,
208
+ };
209
+ }
210
+
211
+ /**
212
+ * Helper to throw a validation error with a single issue
213
+ */
214
+ export function throwValidationError(
215
+ path: string | string[],
216
+ message: string
217
+ ): never {
218
+ throw new ValidationError([createValidationIssue(path, message)]);
219
+ }