@woltz/rich-domain 1.2.0 → 1.2.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 (143) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/aggregate-changes.d.ts +164 -0
  3. package/dist/aggregate-changes.d.ts.map +1 -0
  4. package/dist/aggregate-changes.js +281 -0
  5. package/dist/aggregate-changes.js.map +1 -0
  6. package/dist/base-entity.d.ts +32 -8
  7. package/dist/base-entity.d.ts.map +1 -1
  8. package/dist/base-entity.js +86 -93
  9. package/dist/base-entity.js.map +1 -1
  10. package/dist/change-tracker.d.ts +97 -0
  11. package/dist/change-tracker.d.ts.map +1 -0
  12. package/dist/change-tracker.js +758 -0
  13. package/dist/change-tracker.js.map +1 -0
  14. package/dist/constants.d.ts +7 -1
  15. package/dist/constants.d.ts.map +1 -1
  16. package/dist/constants.js +65 -0
  17. package/dist/constants.js.map +1 -1
  18. package/dist/criteria.d.ts +3 -3
  19. package/dist/criteria.d.ts.map +1 -1
  20. package/dist/criteria.js +6 -4
  21. package/dist/criteria.js.map +1 -1
  22. package/dist/crypto.d.ts +3 -0
  23. package/dist/crypto.d.ts.map +1 -0
  24. package/dist/crypto.js +29 -0
  25. package/dist/crypto.js.map +1 -0
  26. package/dist/domain-event.d.ts.map +1 -1
  27. package/dist/domain-event.js +0 -3
  28. package/dist/domain-event.js.map +1 -1
  29. package/dist/entity-changes.d.ts +84 -0
  30. package/dist/entity-changes.d.ts.map +1 -0
  31. package/dist/entity-changes.js +131 -0
  32. package/dist/entity-changes.js.map +1 -0
  33. package/dist/entity-schema-registry.d.ts +148 -0
  34. package/dist/entity-schema-registry.d.ts.map +1 -0
  35. package/dist/entity-schema-registry.js +213 -0
  36. package/dist/entity-schema-registry.js.map +1 -0
  37. package/dist/entity.d.ts +0 -6
  38. package/dist/entity.d.ts.map +1 -1
  39. package/dist/entity.js +0 -9
  40. package/dist/entity.js.map +1 -1
  41. package/dist/id.d.ts +11 -10
  42. package/dist/id.d.ts.map +1 -1
  43. package/dist/id.js +4 -28
  44. package/dist/id.js.map +1 -1
  45. package/dist/index.d.ts +9 -5
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +8 -11
  48. package/dist/index.js.map +1 -1
  49. package/dist/mapper.d.ts +1 -1
  50. package/dist/mapper.d.ts.map +1 -1
  51. package/dist/mapper.js.map +1 -1
  52. package/dist/paginated-result.d.ts.map +1 -1
  53. package/dist/paginated-result.js +0 -15
  54. package/dist/paginated-result.js.map +1 -1
  55. package/dist/repository/base-repository.d.ts +7 -33
  56. package/dist/repository/base-repository.d.ts.map +1 -1
  57. package/dist/repository/base-repository.js +0 -27
  58. package/dist/repository/base-repository.js.map +1 -1
  59. package/dist/repository/index.d.ts.map +1 -1
  60. package/dist/repository/index.js +0 -6
  61. package/dist/repository/index.js.map +1 -1
  62. package/dist/repository/unit-of-work.d.ts +0 -25
  63. package/dist/repository/unit-of-work.d.ts.map +1 -1
  64. package/dist/repository/unit-of-work.js +0 -28
  65. package/dist/repository/unit-of-work.js.map +1 -1
  66. package/dist/types/change-tracker.d.ts +196 -0
  67. package/dist/types/change-tracker.d.ts.map +1 -0
  68. package/dist/types/change-tracker.js +2 -0
  69. package/dist/types/change-tracker.js.map +1 -0
  70. package/dist/types/criteria.d.ts +5 -1
  71. package/dist/types/criteria.d.ts.map +1 -1
  72. package/dist/types/domain.d.ts +4 -6
  73. package/dist/types/domain.d.ts.map +1 -1
  74. package/dist/types/index.d.ts +1 -1
  75. package/dist/types/index.d.ts.map +1 -1
  76. package/dist/types/index.js +1 -1
  77. package/dist/types/index.js.map +1 -1
  78. package/dist/types/utils.d.ts +0 -1
  79. package/dist/types/utils.d.ts.map +1 -1
  80. package/dist/utils/criteria-operator-validation.d.ts +1 -0
  81. package/dist/utils/criteria-operator-validation.d.ts.map +1 -1
  82. package/dist/utils/criteria-operator-validation.js +39 -17
  83. package/dist/utils/criteria-operator-validation.js.map +1 -1
  84. package/dist/validation-error.d.ts.map +1 -1
  85. package/dist/validation-error.js +1 -6
  86. package/dist/validation-error.js.map +1 -1
  87. package/dist/value-object.d.ts +57 -8
  88. package/dist/value-object.d.ts.map +1 -1
  89. package/dist/value-object.js +49 -22
  90. package/dist/value-object.js.map +1 -1
  91. package/package.json +2 -1
  92. package/src/aggregate-changes.ts +335 -0
  93. package/src/base-entity.ts +102 -109
  94. package/src/change-tracker.ts +1062 -0
  95. package/src/constants.ts +75 -1
  96. package/src/criteria.ts +11 -4
  97. package/src/crypto.ts +31 -0
  98. package/src/domain-event.ts +0 -4
  99. package/src/entity-changes.ts +146 -0
  100. package/src/entity-schema-registry.ts +255 -0
  101. package/src/entity.ts +0 -11
  102. package/src/id.ts +17 -26
  103. package/src/index.ts +15 -19
  104. package/src/mapper.ts +4 -1
  105. package/src/paginated-result.ts +0 -21
  106. package/src/repository/base-repository.ts +7 -38
  107. package/src/repository/index.ts +0 -9
  108. package/src/repository/unit-of-work.ts +0 -29
  109. package/src/types/change-tracker.ts +233 -0
  110. package/src/types/criteria.ts +6 -1
  111. package/src/types/domain.ts +4 -8
  112. package/src/types/index.ts +1 -1
  113. package/src/types/utils.ts +0 -9
  114. package/src/utils/criteria-operator-validation.ts +57 -19
  115. package/src/validation-error.ts +1 -7
  116. package/src/value-object.ts +84 -24
  117. package/tests/aggregate-changes.test.ts +284 -0
  118. package/tests/criteria.test.ts +122 -161
  119. package/tests/entity-equality.test.ts +38 -61
  120. package/tests/entity-schema-registry.test.ts +382 -0
  121. package/tests/entity-validation.test.ts +7 -94
  122. package/tests/history-tracker.spec.ts +349 -617
  123. package/tests/id.test.ts +41 -44
  124. package/tests/load-test/data.json +346041 -0
  125. package/tests/load-test/entities.ts +97 -0
  126. package/tests/load-test/generate-data.ts +81 -0
  127. package/tests/load-test/lead-to-domain.mapper.ts +24 -0
  128. package/tests/load-test/load.test.ts +38 -0
  129. package/tests/repository.test.ts +30 -54
  130. package/tests/to-json.test.ts +14 -18
  131. package/tests/utils.ts +138 -102
  132. package/tests/value-objects.test.ts +57 -29
  133. package/dist/deep-proxy.d.ts +0 -36
  134. package/dist/deep-proxy.d.ts.map +0 -1
  135. package/dist/deep-proxy.js +0 -384
  136. package/dist/deep-proxy.js.map +0 -1
  137. package/dist/types/history-tracker.d.ts +0 -36
  138. package/dist/types/history-tracker.d.ts.map +0 -1
  139. package/dist/types/history-tracker.js +0 -2
  140. package/dist/types/history-tracker.js.map +0 -1
  141. package/src/deep-proxy.ts +0 -447
  142. package/src/types/history-tracker.ts +0 -45
  143. package/tests/entity.test.ts +0 -33
@@ -24,6 +24,17 @@ interface UserWithPostsDto {
24
24
  }[];
25
25
  }
26
26
 
27
+ interface UserWithNestedObject {
28
+ id: string;
29
+ name: string;
30
+ address: {
31
+ street: string;
32
+ city: string;
33
+ country: string;
34
+ };
35
+ tags: string[];
36
+ }
37
+
27
38
  const testUsers: TestUser[] = [
28
39
  {
29
40
  id: "1",
@@ -68,26 +79,12 @@ const testUsers: TestUser[] = [
68
79
  ];
69
80
 
70
81
  describe("Criteria", () => {
71
- describe("Search", () => {
72
- it("should search by all items ignoring pagination and limit", () => {
73
- const criteria = Criteria.create<TestUser>()
74
- .search(["name"], "Eve")
75
- .paginate(3, 1);
76
- const result = PaginatedResult.fromArray(testUsers, criteria);
77
- expect(result.data).toHaveLength(1);
78
- expect(result.data[0].name).toBe("Eve");
79
- expect(result.meta.total).toBe(1);
80
- expect(result.meta.totalPages).toBe(1);
81
- expect(result.meta.page).toBe(1);
82
- expect(result.meta.limit).toBe(1);
83
- });
84
- });
85
82
  describe("Fluent API", () => {
86
83
  it("should create empty criteria", () => {
87
84
  const criteria = Criteria.create<TestUser>();
88
85
  expect(criteria.hasFilters()).toBe(false);
89
86
  expect(criteria.hasOrders()).toBe(false);
90
- expect(criteria.hasPagination()).toBe(true); // Default pagination is set
87
+ expect(criteria.hasPagination()).toBe(true);
91
88
  });
92
89
 
93
90
  it("should chain methods fluently", () => {
@@ -118,27 +115,6 @@ describe("Criteria", () => {
118
115
  });
119
116
 
120
117
  describe("Filtering", () => {
121
- it("should filter by equals", () => {
122
- const criteria = Criteria.create<TestUser>().whereEquals(
123
- "status",
124
- "active"
125
- );
126
- const result = PaginatedResult.fromArray(testUsers, criteria);
127
- expect(result.data).toHaveLength(3);
128
- expect(result.data.every((u) => u.status === "active")).toBe(true);
129
- });
130
-
131
- it("should filter by notEquals", () => {
132
- const criteria = Criteria.create<TestUser>().where(
133
- "status",
134
- "notEquals",
135
- "active"
136
- );
137
- const result = PaginatedResult.fromArray(testUsers, criteria);
138
- expect(result.data).toHaveLength(2);
139
- expect(result.data.every((u) => u.status === "inactive")).toBe(true);
140
- });
141
-
142
118
  it("should filter by search", () => {
143
119
  const criteria = Criteria.create<TestUser>().search(["name"], "Bob");
144
120
 
@@ -147,6 +123,19 @@ describe("Criteria", () => {
147
123
  expect(result.data[0].name).toBe("Bob");
148
124
  });
149
125
 
126
+ it("should search by all items ignoring pagination and limit", () => {
127
+ const criteria = Criteria.create<TestUser>()
128
+ .search(["name"], "Eve")
129
+ .paginate(3, 1);
130
+ const result = PaginatedResult.fromArray(testUsers, criteria);
131
+ expect(result.data).toHaveLength(1);
132
+ expect(result.data[0].name).toBe("Eve");
133
+ expect(result.meta.total).toBe(1);
134
+ expect(result.meta.totalPages).toBe(1);
135
+ expect(result.meta.page).toBe(1);
136
+ expect(result.meta.limit).toBe(1);
137
+ });
138
+
150
139
  it("should filter by greaterThan", () => {
151
140
  const criteria = Criteria.create<TestUser>().where(
152
141
  "age",
@@ -228,6 +217,20 @@ describe("Criteria", () => {
228
217
  expect(result.data).toHaveLength(2);
229
218
  expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
230
219
  });
220
+
221
+ it("should filter, order, and paginate", () => {
222
+ const criteria = Criteria.create<TestUser>()
223
+ .whereEquals("status", "active")
224
+ .orderByDesc("age")
225
+ .paginate(1, 2);
226
+
227
+ const result = PaginatedResult.fromArray(testUsers, criteria);
228
+
229
+ expect(result.data).toHaveLength(2);
230
+ expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
231
+ expect(result.meta.total).toBe(3);
232
+ expect(result.meta.totalPages).toBe(2);
233
+ });
231
234
  });
232
235
 
233
236
  describe("Ordering", () => {
@@ -274,35 +277,6 @@ describe("Criteria", () => {
274
277
  expect(result.meta.hasPrevious).toBe(false);
275
278
  });
276
279
 
277
- it("should get second page", () => {
278
- const criteria = Criteria.create<TestUser>().paginate(2, 2);
279
- const result = PaginatedResult.fromArray(testUsers, criteria);
280
-
281
- expect(result.data).toHaveLength(2);
282
- expect(result.data.map((u) => u.name)).toEqual(["Charlie", "Diana"]);
283
- expect(result.meta.page).toBe(2);
284
- expect(result.meta.hasNext).toBe(true);
285
- expect(result.meta.hasPrevious).toBe(true);
286
- });
287
-
288
- it("should get last page", () => {
289
- const criteria = Criteria.create<TestUser>().paginate(3, 2);
290
- const result = PaginatedResult.fromArray(testUsers, criteria);
291
-
292
- expect(result.data).toHaveLength(1);
293
- expect(result.data[0].name).toBe("Eve");
294
- expect(result.meta.hasNext).toBe(false);
295
- expect(result.meta.hasPrevious).toBe(true);
296
- });
297
-
298
- it("should handle empty page", () => {
299
- const criteria = Criteria.create<TestUser>().paginate(10, 2);
300
- const result = PaginatedResult.fromArray(testUsers, criteria);
301
-
302
- expect(result.data).toHaveLength(0);
303
- expect(result.meta.total).toBe(5);
304
- });
305
-
306
280
  it("should apply limit shorthand", () => {
307
281
  const criteria = Criteria.create<TestUser>().limit(3);
308
282
  const result = PaginatedResult.fromArray(testUsers, criteria);
@@ -310,21 +284,17 @@ describe("Criteria", () => {
310
284
  expect(result.data).toHaveLength(3);
311
285
  expect(result.meta.page).toBe(1);
312
286
  });
313
- });
314
-
315
- describe("Combined Operations", () => {
316
- it("should filter, order, and paginate", () => {
317
- const criteria = Criteria.create<TestUser>()
318
- .whereEquals("status", "active")
319
- .orderByDesc("age")
320
- .paginate(1, 2);
321
287
 
322
- const result = PaginatedResult.fromArray(testUsers, criteria);
288
+ it("should create pagination meta", () => {
289
+ const pagination = { page: 2, limit: 10, offset: 10 };
290
+ const meta = PaginatedResult.createMeta(pagination, 45);
323
291
 
324
- expect(result.data).toHaveLength(2);
325
- expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
326
- expect(result.meta.total).toBe(3);
327
- expect(result.meta.totalPages).toBe(2);
292
+ expect(meta.page).toBe(2);
293
+ expect(meta.limit).toBe(10);
294
+ expect(meta.total).toBe(45);
295
+ expect(meta.totalPages).toBe(5);
296
+ expect(meta.hasNext).toBe(true);
297
+ expect(meta.hasPrevious).toBe(true);
328
298
  });
329
299
  });
330
300
 
@@ -374,12 +344,14 @@ describe("Criteria", () => {
374
344
  new Post({
375
345
  title: "Post 1",
376
346
  content: "Content 1",
377
- likes: 1,
347
+ published: true,
348
+ comments: [],
378
349
  }),
379
350
  new Post({
380
351
  title: "Post 2",
381
352
  content: "Content 2",
382
- likes: 2,
353
+ published: true,
354
+ comments: [],
383
355
  }),
384
356
  ];
385
357
 
@@ -421,30 +393,6 @@ describe("Criteria", () => {
421
393
  });
422
394
  });
423
395
 
424
- describe("Helper Functions", () => {
425
- it("should create pagination meta", () => {
426
- const pagination = { page: 2, limit: 10, offset: 10 };
427
- const meta = PaginatedResult.createMeta(pagination, 45);
428
-
429
- expect(meta.page).toBe(2);
430
- expect(meta.limit).toBe(10);
431
- expect(meta.total).toBe(45);
432
- expect(meta.totalPages).toBe(5);
433
- expect(meta.hasNext).toBe(true);
434
- expect(meta.hasPrevious).toBe(true);
435
- });
436
-
437
- it("should create paginated result", () => {
438
- const data = [{ id: "1" }, { id: "2" }];
439
- const pagination = { page: 1, limit: 2, offset: 0 };
440
- const result = PaginatedResult.create(data, pagination, data.length);
441
-
442
- expect(result.data).toEqual(data);
443
- expect(result.meta.total).toBe(data.length);
444
- expect(result.meta.totalPages).toBe(1);
445
- });
446
- });
447
-
448
396
  describe("Criteria from Query Params", () => {
449
397
  it("should create criteria from query params", () => {
450
398
  const queryParams = {
@@ -466,6 +414,19 @@ describe("Criteria", () => {
466
414
 
467
415
  describe("Quantifiers", () => {
468
416
  describe("Fluent API methods", () => {
417
+ it("should use quantifiers horthand methods", () => {
418
+ // CHECK: Devemos permitir o encadeamento de metodos quantifiers?
419
+ const criteria = Criteria.create<TestUser>()
420
+ .whereSome("name", "contains", "ali")
421
+ .whereEvery("name", "contains", "ali")
422
+ .whereNone("name", "contains", "ali");
423
+
424
+ const filters = criteria.getFilters().map((f) => f.options?.quantifier);
425
+
426
+ expect(filters).toHaveLength(3);
427
+ expect(filters).toEqual(["some", "every", "none"]);
428
+ });
429
+
469
430
  it("should create filter with whereSome", () => {
470
431
  const criteria = Criteria.create<UserWithPostsDto>().whereSome(
471
432
  "posts.title",
@@ -506,20 +467,6 @@ describe("Criteria", () => {
506
467
  expect(filters[0].options?.quantifier).toBe("none");
507
468
  });
508
469
 
509
- it("should create filter without quantifier using where", () => {
510
- const criteria = Criteria.create<UserWithPostsDto>().where(
511
- "posts.title",
512
- "contains",
513
- "test"
514
- );
515
-
516
- const filters = criteria.getFilters();
517
- expect(filters).toHaveLength(1);
518
- expect(filters[0].options).toBeUndefined();
519
- });
520
- });
521
-
522
- describe("fromQueryParams with quantifier", () => {
523
470
  it("should parse quantifier from query params with @some", () => {
524
471
  const queryParams = {
525
472
  "posts.title:contains@some": "test",
@@ -536,32 +483,6 @@ describe("Criteria", () => {
536
483
  expect(filters[0].options?.quantifier).toBe("some");
537
484
  });
538
485
 
539
- it("should parse quantifier from query params with @every", () => {
540
- const queryParams = {
541
- "posts.title:equals@every": "test",
542
- };
543
-
544
- const criteria =
545
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
546
- const filters = criteria.getFilters();
547
-
548
- expect(filters).toHaveLength(1);
549
- expect(filters[0].options?.quantifier).toBe("every");
550
- });
551
-
552
- it("should parse quantifier from query params with @none", () => {
553
- const queryParams = {
554
- "posts.title:contains@none": "spam",
555
- };
556
-
557
- const criteria =
558
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
559
- const filters = criteria.getFilters();
560
-
561
- expect(filters).toHaveLength(1);
562
- expect(filters[0].options?.quantifier).toBe("none");
563
- });
564
-
565
486
  it("should work with in operator and quantifier", () => {
566
487
  const queryParams = {
567
488
  "posts.title:in@some": "test1,test2,test3",
@@ -592,22 +513,6 @@ describe("Criteria", () => {
592
513
  expect(filters[0].options?.quantifier).toBe("some");
593
514
  });
594
515
 
595
- it("should maintain backward compatibility without quantifier", () => {
596
- const queryParams = {
597
- "posts.title:contains": "test",
598
- };
599
-
600
- const criteria =
601
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
602
- const filters = criteria.getFilters();
603
-
604
- expect(filters).toHaveLength(1);
605
- expect(filters[0].field).toBe("posts.title");
606
- expect(filters[0].operator).toBe("contains");
607
- expect(filters[0].value).toBe("test");
608
- expect(filters[0].options).toBeUndefined();
609
- });
610
-
611
516
  it("should throw error for invalid quantifier", () => {
612
517
  const queryParams = {
613
518
  "posts.title:contains@invalid": "test",
@@ -741,7 +646,7 @@ describe("Criteria", () => {
741
646
  {
742
647
  field: "posts",
743
648
  operator: "in",
744
- value: [{ title: "test", content: "test" }],
649
+ value: [{ title: "test" }] as any,
745
650
  },
746
651
  ],
747
652
  },
@@ -751,5 +656,61 @@ describe("Criteria", () => {
751
656
  expect(filters).toHaveLength(1);
752
657
  expect(filters[0].field).toBe("user_posts");
753
658
  });
659
+
660
+ it("should only allow nested paths in fromObject for object fields", () => {
661
+ // Only nested paths should work for object fields
662
+ const criteria = Criteria.fromObject<UserWithNestedObject>({
663
+ filters: [
664
+ {
665
+ field: "address.street",
666
+ operator: "equals",
667
+ value: "123 Main St",
668
+ },
669
+ {
670
+ field: "address.city",
671
+ operator: "equals",
672
+ value: "New York",
673
+ },
674
+ {
675
+ field: "id",
676
+ operator: "equals",
677
+ value: "user-1",
678
+ },
679
+ ],
680
+ });
681
+
682
+ expect(criteria.getFilters()).toHaveLength(3);
683
+ });
684
+ });
685
+
686
+ describe("Nested Object Field Paths", () => {
687
+ it("should only allow nested paths for object fields, not the root", () => {
688
+ const criteria = Criteria.create<UserWithNestedObject>();
689
+
690
+ // These should work - nested paths
691
+ criteria.whereEquals("address.city", "123 Main St");
692
+ criteria.whereEquals("address.city", "New York");
693
+ criteria.whereEquals("address.country", "USA");
694
+
695
+ // These should work - primitives and arrays
696
+ criteria.whereEquals("id", "user-1");
697
+ criteria.whereEquals("name", "John Doe");
698
+ criteria.where("tags", "in", ["tag1", "tag2"]);
699
+
700
+ const filters = criteria.getFilters();
701
+ expect(filters).toHaveLength(6);
702
+ });
703
+
704
+ it("should work with array of objects (keeps root + nested)", () => {
705
+ const criteria = Criteria.create<UserWithPostsDto>();
706
+
707
+ // These should work - array root and nested paths
708
+ criteria.where("posts", "in", []);
709
+ criteria.whereEquals("posts.title", "Test Post");
710
+ criteria.whereEquals("posts.content", "Test Content");
711
+
712
+ const filters = criteria.getFilters();
713
+ expect(filters).toHaveLength(3);
714
+ });
754
715
  });
755
716
  });