@woltz/rich-domain 1.9.0 → 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 (144) 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/domain-event.d.ts +3 -0
  14. package/dist/cjs/core/domain-event.d.ts.map +1 -1
  15. package/dist/cjs/core/domain-event.js +8 -1
  16. package/dist/cjs/core/domain-event.js.map +1 -1
  17. package/dist/cjs/core/value-object.d.ts.map +1 -1
  18. package/dist/cjs/core/value-object.js +3 -5
  19. package/dist/cjs/core/value-object.js.map +1 -1
  20. package/dist/cjs/criteria.d.ts +1 -1
  21. package/dist/cjs/index.d.ts +1 -1
  22. package/dist/cjs/index.d.ts.map +1 -1
  23. package/dist/cjs/repository/entity-schema-registry.d.ts +56 -3
  24. package/dist/cjs/repository/entity-schema-registry.d.ts.map +1 -1
  25. package/dist/cjs/repository/entity-schema-registry.js +61 -6
  26. package/dist/cjs/repository/entity-schema-registry.js.map +1 -1
  27. package/dist/cjs/types/index.d.ts +1 -0
  28. package/dist/cjs/types/index.d.ts.map +1 -1
  29. package/dist/cjs/types/index.js +1 -0
  30. package/dist/cjs/types/index.js.map +1 -1
  31. package/dist/cjs/types/outbox-store.d.ts +91 -0
  32. package/dist/cjs/types/outbox-store.d.ts.map +1 -0
  33. package/dist/cjs/types/outbox-store.js +3 -0
  34. package/dist/cjs/types/outbox-store.js.map +1 -0
  35. package/dist/cjs/utils/helpers.d.ts +1 -0
  36. package/dist/cjs/utils/helpers.d.ts.map +1 -1
  37. package/dist/cjs/utils/helpers.js +4 -0
  38. package/dist/cjs/utils/helpers.js.map +1 -1
  39. package/dist/esm/core/aggregate-changes.d.ts +14 -0
  40. package/dist/esm/core/aggregate-changes.d.ts.map +1 -1
  41. package/dist/esm/core/aggregate-changes.js +18 -0
  42. package/dist/esm/core/aggregate-changes.js.map +1 -1
  43. package/dist/esm/core/base-entity.d.ts +2 -0
  44. package/dist/esm/core/base-entity.d.ts.map +1 -1
  45. package/dist/esm/core/base-entity.js +37 -39
  46. package/dist/esm/core/base-entity.js.map +1 -1
  47. package/dist/esm/core/change-tracker.d.ts +8 -0
  48. package/dist/esm/core/change-tracker.d.ts.map +1 -1
  49. package/dist/esm/core/change-tracker.js +36 -6
  50. package/dist/esm/core/change-tracker.js.map +1 -1
  51. package/dist/esm/core/domain-event.d.ts +3 -0
  52. package/dist/esm/core/domain-event.d.ts.map +1 -1
  53. package/dist/esm/core/domain-event.js +5 -1
  54. package/dist/esm/core/domain-event.js.map +1 -1
  55. package/dist/esm/core/value-object.d.ts.map +1 -1
  56. package/dist/esm/core/value-object.js +1 -3
  57. package/dist/esm/core/value-object.js.map +1 -1
  58. package/dist/esm/criteria.d.ts +1 -1
  59. package/dist/esm/index.d.ts +1 -1
  60. package/dist/esm/index.d.ts.map +1 -1
  61. package/dist/esm/repository/entity-schema-registry.d.ts +56 -3
  62. package/dist/esm/repository/entity-schema-registry.d.ts.map +1 -1
  63. package/dist/esm/repository/entity-schema-registry.js +61 -6
  64. package/dist/esm/repository/entity-schema-registry.js.map +1 -1
  65. package/dist/esm/types/index.d.ts +1 -0
  66. package/dist/esm/types/index.d.ts.map +1 -1
  67. package/dist/esm/types/index.js +1 -0
  68. package/dist/esm/types/index.js.map +1 -1
  69. package/dist/esm/types/outbox-store.d.ts +91 -0
  70. package/dist/esm/types/outbox-store.d.ts.map +1 -0
  71. package/dist/esm/types/outbox-store.js +2 -0
  72. package/dist/esm/types/outbox-store.js.map +1 -0
  73. package/dist/esm/utils/helpers.d.ts +1 -0
  74. package/dist/esm/utils/helpers.d.ts.map +1 -1
  75. package/dist/esm/utils/helpers.js +3 -0
  76. package/dist/esm/utils/helpers.js.map +1 -1
  77. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  78. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  79. package/dist/tsconfig.types.tsbuildinfo +1 -1
  80. package/dist/types/core/aggregate-changes.d.ts +14 -0
  81. package/dist/types/core/aggregate-changes.d.ts.map +1 -1
  82. package/dist/types/core/base-entity.d.ts +2 -0
  83. package/dist/types/core/base-entity.d.ts.map +1 -1
  84. package/dist/types/core/change-tracker.d.ts +8 -0
  85. package/dist/types/core/change-tracker.d.ts.map +1 -1
  86. package/dist/types/core/domain-event.d.ts +3 -0
  87. package/dist/types/core/domain-event.d.ts.map +1 -1
  88. package/dist/types/core/value-object.d.ts.map +1 -1
  89. package/dist/types/criteria.d.ts +1 -1
  90. package/dist/types/index.d.ts +1 -1
  91. package/dist/types/index.d.ts.map +1 -1
  92. package/dist/types/repository/entity-schema-registry.d.ts +56 -3
  93. package/dist/types/repository/entity-schema-registry.d.ts.map +1 -1
  94. package/dist/types/types/index.d.ts +1 -0
  95. package/dist/types/types/index.d.ts.map +1 -1
  96. package/dist/types/types/outbox-store.d.ts +91 -0
  97. package/dist/types/types/outbox-store.d.ts.map +1 -0
  98. package/dist/types/utils/helpers.d.ts +1 -0
  99. package/dist/types/utils/helpers.d.ts.map +1 -1
  100. package/package.json +68 -67
  101. package/src/constants.ts +82 -0
  102. package/src/core/aggregate-changes.ts +466 -0
  103. package/src/core/base-aggregate.ts +76 -0
  104. package/src/core/base-entity.ts +552 -0
  105. package/src/core/change-tracker.ts +1327 -0
  106. package/src/core/domain-event.ts +41 -0
  107. package/src/core/entity-changes.ts +146 -0
  108. package/src/core/entity.ts +13 -0
  109. package/src/core/id.ts +124 -0
  110. package/src/core/index.ts +9 -0
  111. package/src/core/value-object.ts +179 -0
  112. package/src/criteria.ts +574 -0
  113. package/src/exceptions.ts +549 -0
  114. package/src/index.ts +74 -0
  115. package/src/repository/base-repository.ts +81 -0
  116. package/src/repository/entity-schema-registry.ts +620 -0
  117. package/src/repository/index.ts +5 -0
  118. package/src/repository/mapper.ts +7 -0
  119. package/src/repository/paginated-result.ts +251 -0
  120. package/src/repository/unit-of-work.ts +76 -0
  121. package/src/types/change-tracker.ts +268 -0
  122. package/src/types/criteria.ts +197 -0
  123. package/src/types/domain-event.ts +29 -0
  124. package/src/types/domain.ts +41 -0
  125. package/src/types/event-bus.ts +17 -0
  126. package/src/types/index.ts +9 -0
  127. package/src/types/outbox-store.ts +97 -0
  128. package/src/types/standard-schema.ts +19 -0
  129. package/src/types/unit-of-work.ts +46 -0
  130. package/src/types/utils.ts +24 -0
  131. package/src/utils/criteria-operator-validation.ts +209 -0
  132. package/src/utils/crypto.ts +31 -0
  133. package/src/utils/helpers.ts +50 -0
  134. package/src/validation-error.ts +219 -0
  135. package/dist/cjs/t.d.ts +0 -2
  136. package/dist/cjs/t.d.ts.map +0 -1
  137. package/dist/cjs/t.js +0 -96
  138. package/dist/cjs/t.js.map +0 -1
  139. package/dist/esm/t.d.ts +0 -2
  140. package/dist/esm/t.d.ts.map +0 -1
  141. package/dist/esm/t.js +0 -94
  142. package/dist/esm/t.js.map +0 -1
  143. package/dist/types/t.d.ts +0 -2
  144. package/dist/types/t.d.ts.map +0 -1
@@ -0,0 +1,549 @@
1
+ /**
2
+ * Base exception class for all Rich Domain exceptions
3
+ */
4
+ abstract class DomainException extends Error {
5
+ public readonly code: string;
6
+ public readonly timestamp: Date;
7
+ public readonly __isDomainException = true;
8
+
9
+ constructor(message: string, code?: string) {
10
+ super(message);
11
+ this.name = this.constructor.name;
12
+ this.code = code || this.constructor.name;
13
+ this.timestamp = new Date();
14
+
15
+ if (Error.captureStackTrace) {
16
+ Error.captureStackTrace(this, this.constructor);
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Check if an error is a DomainException
22
+ */
23
+ static isDomainException(error: unknown): error is DomainException {
24
+ return (
25
+ error instanceof DomainException ||
26
+ (error instanceof Error &&
27
+ "__isDomainException" in error &&
28
+ (error as any).__isDomainException === true)
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Convert to JSON for serialization
34
+ */
35
+ toJSON(): {
36
+ name: string;
37
+ message: string;
38
+ code: string;
39
+ timestamp: string;
40
+ } {
41
+ return {
42
+ name: this.name,
43
+ message: this.message,
44
+ code: this.code,
45
+ timestamp: this.timestamp.toISOString(),
46
+ };
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Thrown when a domain rule or business logic is violated
52
+ */
53
+ export class DomainError extends DomainException {
54
+ constructor(message: string, code?: string) {
55
+ super(message, code || "DOMAIN_ERROR");
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Thrown for general application errors that don't fit other domain exceptions.
61
+ */
62
+ export class ApplicationError extends DomainException {
63
+ constructor(message: string, code?: string) {
64
+ super(message, code || "APPLICATION_ERROR");
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Thrown when authentication is required but not provided or invalid
70
+ */
71
+ export class UnauthorizedError extends DomainException {
72
+ public readonly resource?: string;
73
+ public readonly action?: string;
74
+
75
+ constructor(message?: string, resource?: string, action?: string) {
76
+ const defaultMessage =
77
+ message || "Authentication required to access this resource";
78
+
79
+ super(defaultMessage, "UNAUTHORIZED");
80
+ this.resource = resource;
81
+ this.action = action;
82
+ }
83
+
84
+ toJSON() {
85
+ return {
86
+ ...super.toJSON(),
87
+ resource: this.resource,
88
+ action: this.action,
89
+ };
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Thrown when the user is authenticated but doesn't have permission to perform an action
95
+ */
96
+ export class ForbiddenError extends DomainException {
97
+ public readonly resource?: string;
98
+ public readonly action?: string;
99
+ public readonly userId?: string;
100
+
101
+ constructor(
102
+ message?: string,
103
+ resource?: string,
104
+ action?: string,
105
+ userId?: string
106
+ ) {
107
+ const defaultMessage =
108
+ message || "You don't have permission to perform this action";
109
+
110
+ super(defaultMessage, "FORBIDDEN");
111
+ this.resource = resource;
112
+ this.action = action;
113
+ this.userId = userId;
114
+ }
115
+
116
+ toJSON() {
117
+ return {
118
+ ...super.toJSON(),
119
+ resource: this.resource,
120
+ action: this.action,
121
+ userId: this.userId,
122
+ };
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Thrown when a request is malformed or contains invalid data
128
+ */
129
+ export class BadRequestError extends DomainException {
130
+ public readonly field?: string;
131
+ public readonly reason?: string;
132
+
133
+ constructor(message: string, field?: string, reason?: string) {
134
+ super(message, "BAD_REQUEST");
135
+ this.field = field;
136
+ this.reason = reason;
137
+ }
138
+
139
+ toJSON() {
140
+ return {
141
+ ...super.toJSON(),
142
+ field: this.field,
143
+ reason: this.reason,
144
+ };
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Thrown when an operation exceeds the allowed time limit
150
+ */
151
+ export class TimeoutError extends DomainException {
152
+ public readonly operation: string;
153
+ public readonly timeoutMs?: number;
154
+
155
+ constructor(operation: string, timeoutMs?: number, message?: string) {
156
+ const defaultMessage = `Operation '${operation}' timed out${
157
+ timeoutMs ? ` after ${timeoutMs}ms` : ""
158
+ }`;
159
+
160
+ super(message || defaultMessage, "TIMEOUT_ERROR");
161
+ this.operation = operation;
162
+ this.timeoutMs = timeoutMs;
163
+ }
164
+
165
+ toJSON() {
166
+ return {
167
+ ...super.toJSON(),
168
+ operation: this.operation,
169
+ timeoutMs: this.timeoutMs,
170
+ };
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Thrown when rate limit is exceeded
176
+ */
177
+ export class RateLimitError extends DomainException {
178
+ public readonly limit: number;
179
+ public readonly windowMs: number;
180
+ public readonly retryAfter?: number;
181
+
182
+ constructor(
183
+ limit: number,
184
+ windowMs: number,
185
+ retryAfter?: number,
186
+ message?: string
187
+ ) {
188
+ const defaultMessage = `Rate limit exceeded: ${limit} requests per ${windowMs}ms`;
189
+
190
+ super(message || defaultMessage, "RATE_LIMIT_ERROR");
191
+ this.limit = limit;
192
+ this.windowMs = windowMs;
193
+ this.retryAfter = retryAfter;
194
+ }
195
+
196
+ toJSON() {
197
+ return {
198
+ ...super.toJSON(),
199
+ limit: this.limit,
200
+ windowMs: this.windowMs,
201
+ retryAfter: this.retryAfter,
202
+ };
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Thrown when an entity or aggregate is not found
208
+ */
209
+ export class EntityNotFoundError extends DomainException {
210
+ public readonly entityType: string;
211
+ public readonly entityId?: string;
212
+
213
+ constructor(entityType: string, entityId?: string, message?: string) {
214
+ const defaultMessage = entityId
215
+ ? `${entityType} with id '${entityId}' not found`
216
+ : `${entityType} not found`;
217
+
218
+ super(message || defaultMessage, "ENTITY_NOT_FOUND");
219
+ this.entityType = entityType;
220
+ this.entityId = entityId;
221
+ }
222
+
223
+ toJSON() {
224
+ return {
225
+ ...super.toJSON(),
226
+ entityType: this.entityType,
227
+ entityId: this.entityId,
228
+ };
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Thrown when trying to create an entity that already exists
234
+ */
235
+ export class EntityAlreadyExistsError extends DomainException {
236
+ public readonly entityType: string;
237
+ public readonly entityId?: string;
238
+
239
+ constructor(entityType: string, entityId?: string, message?: string) {
240
+ const defaultMessage = entityId
241
+ ? `${entityType} with id '${entityId}' already exists`
242
+ : `${entityType} already exists`;
243
+
244
+ super(message || defaultMessage, "ENTITY_ALREADY_EXISTS");
245
+ this.entityType = entityType;
246
+ this.entityId = entityId;
247
+ }
248
+
249
+ toJSON() {
250
+ return {
251
+ ...super.toJSON(),
252
+ entityType: this.entityType,
253
+ entityId: this.entityId,
254
+ };
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Base exception for repository operations
260
+ */
261
+ export class RepositoryError extends DomainException {
262
+ constructor(message: string, code?: string) {
263
+ super(message, code || "REPOSITORY_ERROR");
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Thrown when a persistence operation fails
269
+ */
270
+ export class PersistenceError extends RepositoryError {
271
+ public readonly operation: string;
272
+ public readonly cause?: Error;
273
+
274
+ constructor(operation: string, message?: string, cause?: Error) {
275
+ const defaultMessage = `Persistence operation '${operation}' failed${
276
+ message ? `: ${message}` : ""
277
+ }`;
278
+
279
+ super(defaultMessage, "PERSISTENCE_ERROR");
280
+ this.operation = operation;
281
+ this.cause = cause;
282
+ }
283
+
284
+ toJSON() {
285
+ return {
286
+ ...super.toJSON(),
287
+ operation: this.operation,
288
+ cause: this.cause?.message,
289
+ };
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Thrown when a concurrency conflict occurs (optimistic locking)
295
+ */
296
+ export class ConcurrencyError extends RepositoryError {
297
+ public readonly entityType: string;
298
+ public readonly entityId: string;
299
+
300
+ constructor(entityType: string, entityId: string, message?: string) {
301
+ const defaultMessage =
302
+ message ||
303
+ `Concurrency conflict detected for ${entityType} with id '${entityId}'`;
304
+
305
+ super(defaultMessage, "CONCURRENCY_ERROR");
306
+ this.entityType = entityType;
307
+ this.entityId = entityId;
308
+ }
309
+
310
+ toJSON() {
311
+ return {
312
+ ...super.toJSON(),
313
+ entityType: this.entityType,
314
+ entityId: this.entityId,
315
+ };
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Thrown when a database constraint is violated
321
+ */
322
+ export class ConstraintViolationError extends RepositoryError {
323
+ public readonly constraint: string;
324
+
325
+ constructor(constraint: string, message?: string) {
326
+ const defaultMessage =
327
+ message || `Database constraint '${constraint}' violated`;
328
+
329
+ super(defaultMessage, "CONSTRAINT_VIOLATION");
330
+ this.constraint = constraint;
331
+ }
332
+
333
+ toJSON() {
334
+ return {
335
+ ...super.toJSON(),
336
+ constraint: this.constraint,
337
+ };
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Thrown when a value object has invalid data
343
+ */
344
+ export class InvalidValueObjectError extends DomainException {
345
+ public readonly valueObjectType: string;
346
+ public readonly invalidValue?: any;
347
+
348
+ constructor(valueObjectType: string, message: string, invalidValue?: any) {
349
+ super(message, "INVALID_VALUE_OBJECT");
350
+ this.valueObjectType = valueObjectType;
351
+ this.invalidValue = invalidValue;
352
+ }
353
+
354
+ toJSON() {
355
+ return {
356
+ ...super.toJSON(),
357
+ valueObjectType: this.valueObjectType,
358
+ invalidValue: this.invalidValue,
359
+ };
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Thrown when a domain event operation fails
365
+ */
366
+ export class DomainEventError extends DomainException {
367
+ public readonly eventType?: string;
368
+
369
+ constructor(message: string, eventType?: string) {
370
+ super(message, "DOMAIN_EVENT_ERROR");
371
+ this.eventType = eventType;
372
+ }
373
+
374
+ toJSON() {
375
+ return {
376
+ ...super.toJSON(),
377
+ eventType: this.eventType,
378
+ };
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Thrown when an event handler fails
384
+ */
385
+ export class EventHandlerError extends DomainEventError {
386
+ public readonly handlerName: string;
387
+ public readonly cause?: Error;
388
+
389
+ constructor(handlerName: string, eventType: string, cause?: Error) {
390
+ const message = `Event handler '${handlerName}' failed for event '${eventType}'${
391
+ cause ? `: ${cause.message}` : ""
392
+ }`;
393
+
394
+ super(message, eventType);
395
+ this.handlerName = handlerName;
396
+ this.cause = cause;
397
+ }
398
+
399
+ toJSON() {
400
+ return {
401
+ ...super.toJSON(),
402
+ handlerName: this.handlerName,
403
+ cause: this.cause?.message,
404
+ };
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Thrown when a criteria or query is invalid
410
+ */
411
+ export class InvalidCriteriaError extends DomainException {
412
+ public readonly field?: string;
413
+
414
+ constructor(message: string, field?: string) {
415
+ super(message, "INVALID_CRITERIA");
416
+ this.field = field;
417
+ }
418
+
419
+ toJSON() {
420
+ return {
421
+ ...super.toJSON(),
422
+ field: this.field,
423
+ };
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Thrown when a transaction operation fails
429
+ */
430
+ export class TransactionError extends DomainException {
431
+ public readonly operation: string;
432
+ public readonly cause?: Error;
433
+
434
+ constructor(operation: string, message?: string, cause?: Error) {
435
+ const defaultMessage = `Transaction ${operation} failed${
436
+ message ? `: ${message}` : ""
437
+ }`;
438
+
439
+ super(defaultMessage, "TRANSACTION_ERROR");
440
+ this.operation = operation;
441
+ this.cause = cause;
442
+ }
443
+
444
+ toJSON() {
445
+ return {
446
+ ...super.toJSON(),
447
+ operation: this.operation,
448
+ cause: this.cause?.message,
449
+ };
450
+ }
451
+ }
452
+
453
+ /**
454
+ * Thrown when an unexpected or unknown error occurs
455
+ */
456
+ export class UnknownError extends DomainException {
457
+ public readonly originalError?: Error;
458
+
459
+ constructor(message?: string, originalError?: Error) {
460
+ const defaultMessage =
461
+ message || originalError?.message || "An unknown error occurred";
462
+
463
+ super(defaultMessage, "UNKNOWN_ERROR");
464
+ this.originalError = originalError;
465
+ }
466
+
467
+ toJSON() {
468
+ return {
469
+ ...super.toJSON(),
470
+ originalError: this.originalError?.message,
471
+ };
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Thrown when a feature is not implemented yet
477
+ */
478
+ export class NotImplementedError extends DomainException {
479
+ public readonly feature: string;
480
+
481
+ constructor(feature: string, message?: string) {
482
+ const defaultMessage = message || `Feature '${feature}' is not implemented`;
483
+
484
+ super(defaultMessage, "NOT_IMPLEMENTED");
485
+ this.feature = feature;
486
+ }
487
+
488
+ toJSON() {
489
+ return {
490
+ ...super.toJSON(),
491
+ feature: this.feature,
492
+ };
493
+ }
494
+ }
495
+
496
+ /**
497
+ * Thrown when a required configuration is missing
498
+ */
499
+ export class ConfigurationError extends DomainException {
500
+ public readonly configKey?: string;
501
+
502
+ constructor(message: string, configKey?: string) {
503
+ super(message, "CONFIGURATION_ERROR");
504
+ this.configKey = configKey;
505
+ }
506
+
507
+ toJSON() {
508
+ return {
509
+ ...super.toJSON(),
510
+ configKey: this.configKey,
511
+ };
512
+ }
513
+ }
514
+
515
+ /**
516
+ * Thrown when mapping between domain and persistence fails
517
+ */
518
+ export class MapperError extends DomainException {
519
+ public readonly direction: "toDomain" | "toPersistence";
520
+ public readonly entityType: string;
521
+ public readonly cause?: Error;
522
+
523
+ constructor(
524
+ direction: "toDomain" | "toPersistence",
525
+ entityType: string,
526
+ message?: string,
527
+ cause?: Error
528
+ ) {
529
+ const defaultMessage =
530
+ message ||
531
+ `Failed to map ${entityType} ${
532
+ direction === "toDomain" ? "to domain" : "to persistence"
533
+ }`;
534
+
535
+ super(defaultMessage, "MAPPER_ERROR");
536
+ this.direction = direction;
537
+ this.entityType = entityType;
538
+ this.cause = cause;
539
+ }
540
+
541
+ toJSON() {
542
+ return {
543
+ ...super.toJSON(),
544
+ direction: this.direction,
545
+ entityType: this.entityType,
546
+ cause: this.cause?.message,
547
+ };
548
+ }
549
+ }
package/src/index.ts ADDED
@@ -0,0 +1,74 @@
1
+ export * from "./validation-error.js";
2
+ export * from "./core/domain-event.js";
3
+ export * from "./exceptions.js";
4
+ export * from "./criteria.js";
5
+ export { isValidOperatorForType } from "./utils/criteria-operator-validation.js";
6
+ export {
7
+ ARRAY_OPERATORS,
8
+ BOOLEAN_OPERATORS,
9
+ DATE_OPERATORS,
10
+ NUMBER_OPERATORS,
11
+ STRING_OPERATORS,
12
+ FILTER_OPERATORS,
13
+ } from "./constants.js";
14
+ export {
15
+ Id,
16
+ Entity,
17
+ Aggregate,
18
+ ValueObject,
19
+ AggregateChanges,
20
+ DomainEvent,
21
+ } from "./core/index.js";
22
+ export {
23
+ type PaginatedJsonResult,
24
+ type CollectionConfig,
25
+ Repository,
26
+ Mapper,
27
+ UnitOfWork,
28
+ PaginatedResult,
29
+ ReadRepository,
30
+ WriteAndRead,
31
+ WriteRepository,
32
+ BaseTransactionContext,
33
+ EntitySchemaRegistry,
34
+ } from "./repository/index.js";
35
+ export type {
36
+ EntityHooks,
37
+ Filter,
38
+ EntityValidation,
39
+ IDomainEvent,
40
+ VOValidation,
41
+ VOHooks,
42
+ ValidationConfig,
43
+ Primitive,
44
+ TransactionContext,
45
+ PaginationMeta,
46
+ Pagination,
47
+ OrderDirection,
48
+ Order,
49
+ IUnitOfWork,
50
+ FieldPath,
51
+ FilterOperator,
52
+ Search,
53
+ FilterValueFor,
54
+ PathValue,
55
+ OperatorsForType,
56
+ DateOperators,
57
+ NumberOperators,
58
+ BatchDeleteOperation,
59
+ BatchCreateOperation,
60
+ BatchUpdateOperation,
61
+ BatchOperations,
62
+ BatchCreateItem,
63
+ BatchUpdateItem,
64
+ BatchDeleteItem,
65
+ StringOperators,
66
+ BooleanOperators,
67
+ ArrayOperators,
68
+ CriteriaOptions,
69
+ IDomainEventBus,
70
+ IOutboxStore,
71
+ OutboxEntryData,
72
+ OutboxFetchResult,
73
+ OutboxStatus,
74
+ } from "./types/index.js";
@@ -0,0 +1,81 @@
1
+ import type { Aggregate } from "../core/entity.js";
2
+ import type { Criteria } from "../criteria.js";
3
+ import { PaginatedResult } from "./paginated-result.js";
4
+ import { Mapper } from "./mapper.js";
5
+
6
+ export abstract class ReadRepository<Agg extends Aggregate<any>> {
7
+ /**
8
+ * Find entities based on criteria.
9
+ * @param criteria - The criteria to use for the search. If not provided, all entities will be returned. (optional)
10
+ * @returns A promise that resolves to a paginated result of entities.
11
+ */
12
+ abstract find(criteria?: Criteria<Agg>): Promise<PaginatedResult<Agg>>;
13
+ abstract findById(id: string): Promise<Agg | null>;
14
+ abstract findManyByIds(ids: string[]): Promise<Agg[]>;
15
+ /**
16
+ * Count the number of entities based on criteria.
17
+ * @param criteria - The criteria to use for the count. If not provided, all entities will be counted. (optional)
18
+ * @returns A promise that resolves to the number of entities.
19
+ */
20
+ abstract count(criteria?: Criteria<Agg>): Promise<number>;
21
+ /**
22
+ * Check if an entity exists based on its identifier.
23
+ * @param id - The identifier of the entity to check.
24
+ * @returns A promise that resolves to a boolean.
25
+ */
26
+ abstract exists(id: string): Promise<boolean>;
27
+ }
28
+
29
+ export abstract class WriteRepository<Agg extends Aggregate<any>> {
30
+ /**
31
+ *
32
+ * Save or update an entity.
33
+ * @param entity - The entity to save.
34
+ * @returns void
35
+ */
36
+ abstract save(entity: Agg): Promise<void>;
37
+ abstract delete(entity: Agg): Promise<void>;
38
+ }
39
+
40
+ export abstract class WriteAndRead<Agg extends Aggregate<any>> {
41
+ /**
42
+ * Find entities based on criteria.
43
+ * @param criteria - The criteria to use for the search. If not provided, all entities will be returned. (optional)
44
+ * @returns A promise that resolves to a paginated result of entities.
45
+ */
46
+ abstract find(criteria?: Criteria<Agg>): Promise<PaginatedResult<Agg>>;
47
+ abstract findById(id: string): Promise<Agg | null>;
48
+ abstract findManyByIds(ids: string[]): Promise<Agg[]>;
49
+ /**
50
+ *
51
+ * Save or update an entity.
52
+ * @param entity - The entity to save.
53
+ * @returns void
54
+ */
55
+ abstract save(entity: Agg): Promise<void>;
56
+ abstract delete(entity: Agg): Promise<void>;
57
+ /**
58
+ * Count the number of entities based on criteria.
59
+ * @param criteria - The criteria to use for the count. If not provided, all entities will be counted. (optional)
60
+ * @returns A promise that resolves to the number of entities.
61
+ */
62
+ abstract count(criteria?: Criteria<Agg>): Promise<number>;
63
+ /**
64
+ * Check if an entity exists based on its identifier.
65
+ * @param id - The identifier of the entity to check.
66
+ * @returns A promise that resolves to a boolean.
67
+ */
68
+ abstract exists(id: string): Promise<boolean>;
69
+ }
70
+
71
+ export abstract class Repository<
72
+ TDomain extends Aggregate<any>,
73
+ > extends WriteAndRead<TDomain> {
74
+ protected abstract readonly toDomainMapper: Mapper<unknown, TDomain>;
75
+ protected abstract readonly toPersistenceMapper: Mapper<TDomain, unknown>;
76
+ /**
77
+ * Provide the model name of the repository. Usually the table name in the database.
78
+ * @returns The model name of the repository.
79
+ */
80
+ protected abstract get model(): any;
81
+ }