@woltz/rich-domain 1.2.4 → 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 (94) hide show
  1. package/dist/aggregate-changes.d.ts +56 -14
  2. package/dist/aggregate-changes.d.ts.map +1 -1
  3. package/dist/aggregate-changes.js +103 -23
  4. package/dist/aggregate-changes.js.map +1 -1
  5. package/dist/base-entity.d.ts +1 -1
  6. package/dist/base-entity.d.ts.map +1 -1
  7. package/dist/base-entity.js +28 -13
  8. package/dist/base-entity.js.map +1 -1
  9. package/dist/change-tracker.d.ts +2 -1
  10. package/dist/change-tracker.d.ts.map +1 -1
  11. package/dist/change-tracker.js +61 -35
  12. package/dist/change-tracker.js.map +1 -1
  13. package/dist/criteria.d.ts +7 -15
  14. package/dist/criteria.d.ts.map +1 -1
  15. package/dist/criteria.js +105 -81
  16. package/dist/criteria.js.map +1 -1
  17. package/dist/domain-event-bus.js +4 -4
  18. package/dist/domain-event-bus.js.map +1 -1
  19. package/dist/domain-event.js +3 -0
  20. package/dist/domain-event.js.map +1 -1
  21. package/dist/entity-changes.js +1 -0
  22. package/dist/entity-changes.js.map +1 -1
  23. package/dist/entity-schema-registry.d.ts +137 -3
  24. package/dist/entity-schema-registry.d.ts.map +1 -1
  25. package/dist/entity-schema-registry.js +160 -7
  26. package/dist/entity-schema-registry.js.map +1 -1
  27. package/dist/exceptions.js +26 -1
  28. package/dist/exceptions.js.map +1 -1
  29. package/dist/id.js +2 -0
  30. package/dist/id.js.map +1 -1
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/paginated-result.d.ts +4 -4
  34. package/dist/paginated-result.d.ts.map +1 -1
  35. package/dist/paginated-result.js +14 -19
  36. package/dist/paginated-result.js.map +1 -1
  37. package/dist/repository/unit-of-work.js +3 -7
  38. package/dist/repository/unit-of-work.js.map +1 -1
  39. package/dist/types/change-tracker.d.ts +30 -0
  40. package/dist/types/change-tracker.d.ts.map +1 -1
  41. package/dist/types/criteria.d.ts +1 -4
  42. package/dist/types/criteria.d.ts.map +1 -1
  43. package/dist/types/domain.d.ts +2 -1
  44. package/dist/types/domain.d.ts.map +1 -1
  45. package/dist/types/utils.d.ts +2 -2
  46. package/dist/utils/helpers.d.ts +1 -0
  47. package/dist/utils/helpers.d.ts.map +1 -1
  48. package/dist/utils/helpers.js +23 -0
  49. package/dist/utils/helpers.js.map +1 -1
  50. package/dist/validation-error.d.ts +15 -1
  51. package/dist/validation-error.d.ts.map +1 -1
  52. package/dist/validation-error.js +46 -3
  53. package/dist/validation-error.js.map +1 -1
  54. package/dist/value-object.d.ts +1 -1
  55. package/dist/value-object.d.ts.map +1 -1
  56. package/dist/value-object.js +30 -2
  57. package/dist/value-object.js.map +1 -1
  58. package/package.json +17 -3
  59. package/src/aggregate-changes.ts +133 -24
  60. package/src/base-entity.ts +22 -11
  61. package/src/change-tracker.ts +113 -54
  62. package/src/criteria.ts +151 -109
  63. package/src/entity-schema-registry.ts +256 -6
  64. package/src/index.ts +1 -1
  65. package/src/paginated-result.ts +21 -29
  66. package/src/types/change-tracker.ts +31 -0
  67. package/src/types/criteria.ts +1 -4
  68. package/src/types/domain.ts +2 -1
  69. package/src/types/utils.ts +2 -2
  70. package/src/utils/helpers.ts +28 -0
  71. package/src/validation-error.ts +54 -4
  72. package/src/value-object.ts +6 -1
  73. package/.versionrc.json +0 -21
  74. package/CHANGELOG.md +0 -163
  75. package/tests/aggregate-changes.test.ts +0 -284
  76. package/tests/criteria.test.ts +0 -716
  77. package/tests/depth/deep-tracking.test.ts +0 -554
  78. package/tests/domain-events.test.ts +0 -431
  79. package/tests/entity-equality.test.ts +0 -464
  80. package/tests/entity-schema-registry.test.ts +0 -382
  81. package/tests/entity-validation.test.ts +0 -252
  82. package/tests/history-tracker.spec.ts +0 -439
  83. package/tests/id.test.ts +0 -338
  84. package/tests/load-test/data.json +0 -347211
  85. package/tests/load-test/entities.ts +0 -97
  86. package/tests/load-test/generate-data.ts +0 -81
  87. package/tests/load-test/lead-to-domain.mapper.ts +0 -24
  88. package/tests/load-test/load.test.ts +0 -38
  89. package/tests/repository.test.ts +0 -635
  90. package/tests/to-json.test.ts +0 -99
  91. package/tests/utils.ts +0 -290
  92. package/tests/value-object-validation.test.ts +0 -219
  93. package/tests/value-objects.test.ts +0 -80
  94. package/tsconfig.json +0 -9
@@ -1,716 +0,0 @@
1
- import { Pagination } from "../src";
2
- import { Criteria } from "../src/criteria";
3
- import { PaginatedResult } from "../src/paginated-result";
4
- import { CriteriaAdapter } from "../src/types";
5
- import { Post } from "./utils";
6
-
7
- interface TestUser {
8
- id: string;
9
- name: string;
10
- email: string;
11
- age: number;
12
- status: "active" | "inactive";
13
- createdAt: Date;
14
- }
15
-
16
- interface UserWithPostsDto {
17
- id: string;
18
- name: string;
19
- posts: { title: string; content: string }[];
20
- leads: {
21
- contact: {
22
- name: string;
23
- };
24
- }[];
25
- }
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
-
38
- const testUsers: TestUser[] = [
39
- {
40
- id: "1",
41
- name: "Alice",
42
- email: "alice@example.com",
43
- age: 25,
44
- status: "active",
45
- createdAt: new Date("2024-01-01"),
46
- },
47
- {
48
- id: "2",
49
- name: "Bob",
50
- email: "bob@example.com",
51
- age: 30,
52
- status: "active",
53
- createdAt: new Date("2024-02-01"),
54
- },
55
- {
56
- id: "3",
57
- name: "Charlie",
58
- email: "charlie@test.com",
59
- age: 35,
60
- status: "inactive",
61
- createdAt: new Date("2024-03-01"),
62
- },
63
- {
64
- id: "4",
65
- name: "Diana",
66
- email: "diana@example.com",
67
- age: 28,
68
- status: "active",
69
- createdAt: new Date("2024-04-01"),
70
- },
71
- {
72
- id: "5",
73
- name: "Eve",
74
- email: "eve@test.com",
75
- age: 22,
76
- status: "inactive",
77
- createdAt: new Date("2024-05-01"),
78
- },
79
- ];
80
-
81
- describe("Criteria", () => {
82
- describe("Fluent API", () => {
83
- it("should create empty criteria", () => {
84
- const criteria = Criteria.create<TestUser>();
85
- expect(criteria.hasFilters()).toBe(false);
86
- expect(criteria.hasOrders()).toBe(false);
87
- expect(criteria.hasPagination()).toBe(true);
88
- });
89
-
90
- it("should chain methods fluently", () => {
91
- const criteria = Criteria.create<TestUser>()
92
- .where("status", "equals", "active")
93
- .where("age", "greaterThan", 18)
94
- .orderBy("name", "asc")
95
- .paginate(1, 10);
96
-
97
- expect(criteria.hasFilters()).toBe(true);
98
- expect(criteria.hasOrders()).toBe(true);
99
- expect(criteria.hasPagination()).toBe(true);
100
- });
101
-
102
- it("should use shorthand methods", () => {
103
- const criteria = Criteria.create<TestUser>()
104
- .whereEquals("status", "active")
105
- .whereContains("name", "ali")
106
- .whereIn("age", [25, 30, 35])
107
- .orderByDesc("createdAt");
108
-
109
- const filters = criteria.getFilters();
110
- expect(filters).toHaveLength(3);
111
- expect(filters[0].operator).toBe("equals");
112
- expect(filters[1].operator).toBe("contains");
113
- expect(filters[2].operator).toBe("in");
114
- });
115
- });
116
-
117
- describe("Filtering", () => {
118
- it("should filter by search", () => {
119
- const criteria = Criteria.create<TestUser>().search(["name"], "Bob");
120
-
121
- const result = PaginatedResult.fromArray(testUsers, criteria);
122
- expect(result.data).toHaveLength(1);
123
- expect(result.data[0].name).toBe("Bob");
124
- });
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
-
139
- it("should filter by greaterThan", () => {
140
- const criteria = Criteria.create<TestUser>().where(
141
- "age",
142
- "greaterThan",
143
- 28
144
- );
145
- const result = PaginatedResult.fromArray(testUsers, criteria);
146
- expect(result.data).toHaveLength(2);
147
- expect(result.data.map((u) => u.name)).toEqual(["Bob", "Charlie"]);
148
- });
149
-
150
- it("should filter by lessThan", () => {
151
- const criteria = Criteria.create<TestUser>().where("age", "lessThan", 26);
152
- const result = PaginatedResult.fromArray(testUsers, criteria);
153
- expect(result.data).toHaveLength(2);
154
- expect(result.data.map((u) => u.name)).toEqual(["Alice", "Eve"]);
155
- });
156
-
157
- it("should filter by contains", () => {
158
- const criteria = Criteria.create<TestUser>().whereContains(
159
- "email",
160
- "example"
161
- );
162
- const result = PaginatedResult.fromArray(testUsers, criteria);
163
- expect(result.data).toHaveLength(3);
164
- });
165
-
166
- it("should filter by startsWith", () => {
167
- const criteria = Criteria.create<TestUser>().where(
168
- "name",
169
- "startsWith",
170
- "A"
171
- );
172
- const result = PaginatedResult.fromArray(testUsers, criteria);
173
- expect(result.data).toHaveLength(1);
174
- expect(result.data[0].name).toBe("Alice");
175
- });
176
-
177
- it("should filter by endsWith", () => {
178
- const criteria = Criteria.create<TestUser>().where(
179
- "email",
180
- "endsWith",
181
- ".com"
182
- );
183
- const result = PaginatedResult.fromArray(testUsers, criteria);
184
- expect(result.data).toHaveLength(5);
185
- });
186
-
187
- it("should filter by in", () => {
188
- const criteria = Criteria.create<TestUser>().whereIn("age", [25, 35]);
189
- const result = PaginatedResult.fromArray(testUsers, criteria);
190
- expect(result.data).toHaveLength(2);
191
- expect(result.data.map((u) => u.name)).toEqual(["Alice", "Charlie"]);
192
- });
193
-
194
- it("should filter by notIn", () => {
195
- const criteria = Criteria.create<TestUser>().where(
196
- "age",
197
- "notIn",
198
- [25, 35]
199
- );
200
- const result = PaginatedResult.fromArray(testUsers, criteria);
201
- expect(result.data).toHaveLength(3);
202
- });
203
-
204
- it("should filter by between", () => {
205
- const criteria = Criteria.create<TestUser>().whereBetween("age", 25, 30);
206
- const result = PaginatedResult.fromArray(testUsers, criteria);
207
- expect(result.data).toHaveLength(3);
208
- expect(result.data.map((u) => u.name)).toEqual(["Alice", "Bob", "Diana"]);
209
- });
210
-
211
- it("should combine multiple filters", () => {
212
- const criteria = Criteria.create<TestUser>()
213
- .whereEquals("status", "active")
214
- .where("age", "greaterThan", 25);
215
-
216
- const result = PaginatedResult.fromArray(testUsers, criteria);
217
- expect(result.data).toHaveLength(2);
218
- expect(result.data.map((u) => u.name)).toEqual(["Bob", "Diana"]);
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
- });
234
- });
235
-
236
- describe("Ordering", () => {
237
- it("should order by ascending", () => {
238
- const criteria = Criteria.create<TestUser>().orderByAsc("age");
239
- const result = PaginatedResult.fromArray(testUsers, criteria);
240
- const ages = result.data.map((u) => u.age);
241
- expect(ages).toEqual([22, 25, 28, 30, 35]);
242
- });
243
-
244
- it("should order by descending", () => {
245
- const criteria = Criteria.create<TestUser>().orderByDesc("age");
246
- const result = PaginatedResult.fromArray(testUsers, criteria);
247
- const ages = result.data.map((u) => u.age);
248
- expect(ages).toEqual([35, 30, 28, 25, 22]);
249
- });
250
-
251
- it("should order by string field", () => {
252
- const criteria = Criteria.create<TestUser>().orderByAsc("name");
253
- const result = PaginatedResult.fromArray(testUsers, criteria);
254
- const names = result.data.map((u) => u.name);
255
- expect(names).toEqual(["Alice", "Bob", "Charlie", "Diana", "Eve"]);
256
- });
257
-
258
- it("should order by date field", () => {
259
- const criteria = Criteria.create<TestUser>().orderByDesc("createdAt");
260
- const result = PaginatedResult.fromArray(testUsers, criteria);
261
- const names = result.data.map((u) => u.name);
262
- expect(names).toEqual(["Eve", "Diana", "Charlie", "Bob", "Alice"]);
263
- });
264
- });
265
-
266
- describe("Pagination", () => {
267
- it("should paginate results", () => {
268
- const criteria = Criteria.create<TestUser>().paginate(1, 2);
269
- const result = PaginatedResult.fromArray(testUsers, criteria);
270
-
271
- expect(result.data).toHaveLength(2);
272
- expect(result.meta.page).toBe(1);
273
- expect(result.meta.limit).toBe(2);
274
- expect(result.meta.total).toBe(5);
275
- expect(result.meta.totalPages).toBe(3);
276
- expect(result.meta.hasNext).toBe(true);
277
- expect(result.meta.hasPrevious).toBe(false);
278
- });
279
-
280
- it("should apply limit shorthand", () => {
281
- const criteria = Criteria.create<TestUser>().limit(3);
282
- const result = PaginatedResult.fromArray(testUsers, criteria);
283
-
284
- expect(result.data).toHaveLength(3);
285
- expect(result.meta.page).toBe(1);
286
- });
287
-
288
- it("should create pagination meta", () => {
289
- const pagination = { page: 2, limit: 10, offset: 10 };
290
- const meta = PaginatedResult.createMeta(pagination, 45);
291
-
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);
298
- });
299
- });
300
-
301
- describe("Serialization", () => {
302
- it("should convert to object", () => {
303
- const criteria = Criteria.create<TestUser>()
304
- .whereEquals("status", "active")
305
- .orderByDesc("age")
306
- .paginate(1, 10);
307
-
308
- const obj = criteria.toJSON();
309
-
310
- expect(obj.filters).toHaveLength(1);
311
- expect(obj.orders).toHaveLength(1);
312
- expect(obj.pagination).toBeDefined();
313
- expect(obj.pagination?.page).toBe(1);
314
- });
315
-
316
- it("should create from object", () => {
317
- const criteria = Criteria.fromObject<TestUser>({
318
- filters: [{ field: "status", operator: "equals", value: "active" }],
319
- orders: [{ field: "age", direction: "desc" }],
320
- pagination: { page: 1, limit: 10, offset: 0 },
321
- });
322
-
323
- expect(criteria.getFilters()).toHaveLength(1);
324
- expect(criteria.getOrders()).toHaveLength(1);
325
- expect(criteria.getPagination()?.page).toBe(1);
326
- });
327
-
328
- it("should clone criteria", () => {
329
- const original = Criteria.create<TestUser>()
330
- .whereEquals("status", "active")
331
- .orderByDesc("age");
332
-
333
- const cloned = original.clone();
334
- cloned.whereEquals("age", 30);
335
-
336
- expect(original.getFilters()).toHaveLength(1);
337
- expect(cloned.getFilters()).toHaveLength(2);
338
- });
339
-
340
- it("should deserialize pagination result with entities", () => {
341
- const pagination: Pagination = { page: 1, limit: 10, offset: 0 };
342
-
343
- const data = [
344
- new Post({
345
- title: "Post 1",
346
- content: "Content 1",
347
- published: true,
348
- comments: [],
349
- }),
350
- new Post({
351
- title: "Post 2",
352
- content: "Content 2",
353
- published: true,
354
- comments: [],
355
- }),
356
- ];
357
-
358
- const total = data.length;
359
-
360
- const paginationResult = PaginatedResult.create(data, pagination, total);
361
-
362
- const result = paginationResult.toJSON();
363
-
364
- expect(result.data).toHaveLength(2);
365
- expect(result.data[0].title).toBe("Post 1");
366
- expect(result.data[1].title).toBe("Post 2");
367
- expect(result.meta.total).toBe(total);
368
- expect(result.meta.totalPages).toBe(1);
369
- expect(result.meta.page).toBe(1);
370
- expect(result.meta.limit).toBe(10);
371
- });
372
-
373
- it("should deserialize pagination result with plain objects", () => {
374
- const pagination: Pagination = { page: 1, limit: 10, offset: 0 };
375
- const total = testUsers.length;
376
-
377
- const paginationResult = PaginatedResult.create(
378
- testUsers,
379
- pagination,
380
- total
381
- );
382
-
383
- const result = paginationResult.toJSON();
384
-
385
- expect(result.data).toHaveLength(testUsers.length);
386
- expect(result.data.map((u) => u.name)).toEqual(
387
- testUsers.map((u) => u.name)
388
- );
389
- expect(result.meta.total).toBe(total);
390
- expect(result.meta.totalPages).toBe(1);
391
- expect(result.meta.page).toBe(1);
392
- expect(result.meta.limit).toBe(10);
393
- });
394
- });
395
-
396
- describe("Criteria from Query Params", () => {
397
- it("should create criteria from query params", () => {
398
- const queryParams = {
399
- "status:equals": "active",
400
- "age:greaterThan": "25",
401
- orderBy: "age",
402
- orderDirection: "desc",
403
- page: "1",
404
- limit: "2",
405
- };
406
-
407
- const criteria = Criteria.fromQueryParams<TestUser>(queryParams);
408
- expect(criteria.getFilters()).toHaveLength(2);
409
- expect(criteria.getOrders()).toHaveLength(1);
410
- expect(criteria.getPagination()?.page).toBe(1);
411
- expect(criteria.getPagination()?.limit).toBe(2);
412
- });
413
- });
414
-
415
- describe("Quantifiers", () => {
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
-
430
- it("should create filter with whereSome", () => {
431
- const criteria = Criteria.create<UserWithPostsDto>().whereSome(
432
- "posts.title",
433
- "contains",
434
- "test"
435
- );
436
-
437
- const filters = criteria.getFilters();
438
-
439
- expect(filters).toHaveLength(1);
440
- expect(filters[0].field).toBe("posts.title");
441
- expect(filters[0].operator).toBe("contains");
442
- expect(filters[0].value).toBe("test");
443
- expect(filters[0].options?.quantifier).toBe("some");
444
- });
445
-
446
- it("should create filter with whereEvery", () => {
447
- const criteria = Criteria.create<UserWithPostsDto>().whereEvery(
448
- "posts.title",
449
- "contains",
450
- "test"
451
- );
452
-
453
- const filters = criteria.getFilters();
454
- expect(filters).toHaveLength(1);
455
- expect(filters[0].options?.quantifier).toBe("every");
456
- });
457
-
458
- it("should create filter with whereNone", () => {
459
- const criteria = Criteria.create<UserWithPostsDto>().whereNone(
460
- "posts.title",
461
- "contains",
462
- "test"
463
- );
464
-
465
- const filters = criteria.getFilters();
466
- expect(filters).toHaveLength(1);
467
- expect(filters[0].options?.quantifier).toBe("none");
468
- });
469
-
470
- it("should parse quantifier from query params with @some", () => {
471
- const queryParams = {
472
- "posts.title:contains@some": "test",
473
- };
474
-
475
- const criteria =
476
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
477
- const filters = criteria.getFilters();
478
-
479
- expect(filters).toHaveLength(1);
480
- expect(filters[0].field).toBe("posts.title");
481
- expect(filters[0].operator).toBe("contains");
482
- expect(filters[0].value).toBe("test");
483
- expect(filters[0].options?.quantifier).toBe("some");
484
- });
485
-
486
- it("should work with in operator and quantifier", () => {
487
- const queryParams = {
488
- "posts.title:in@some": "test1,test2,test3",
489
- };
490
-
491
- const criteria =
492
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
493
- const filters = criteria.getFilters();
494
-
495
- expect(filters).toHaveLength(1);
496
- expect(filters[0].operator).toBe("in");
497
- expect(filters[0].value).toEqual(["test1", "test2", "test3"]);
498
- expect(filters[0].options?.quantifier).toBe("some");
499
- });
500
-
501
- it("should work with between operator and quantifier", () => {
502
- const queryParams = {
503
- "posts.likes:between@some": "10,100",
504
- };
505
-
506
- const criteria =
507
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
508
- const filters = criteria.getFilters();
509
-
510
- expect(filters).toHaveLength(1);
511
- expect(filters[0].operator).toBe("between");
512
- expect(filters[0].value).toEqual([10, 100]);
513
- expect(filters[0].options?.quantifier).toBe("some");
514
- });
515
-
516
- it("should throw error for invalid quantifier", () => {
517
- const queryParams = {
518
- "posts.title:contains@invalid": "test",
519
- };
520
-
521
- expect(() => {
522
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
523
- }).toThrow("Invalid quantifier");
524
- });
525
-
526
- it("should handle multiple filters with mixed quantifiers", () => {
527
- const queryParams = {
528
- "posts.title:contains@some": "test",
529
- "posts.content:equals@every": "content",
530
- "name:contains": "John",
531
- };
532
-
533
- const criteria =
534
- Criteria.fromQueryParams<UserWithPostsDto>(queryParams);
535
- const filters = criteria.getFilters();
536
-
537
- expect(filters).toHaveLength(3);
538
- expect(filters[0].options?.quantifier).toBe("some");
539
- expect(filters[1].options?.quantifier).toBe("every");
540
- expect(filters[2].options).toBeUndefined();
541
- });
542
- });
543
-
544
- describe("fromObject with quantifier", () => {
545
- it("should create criteria from object with quantifier", () => {
546
- const criteria = Criteria.fromObject<UserWithPostsDto>({
547
- filters: [
548
- {
549
- field: "posts.title",
550
- operator: "contains",
551
- value: "test",
552
- options: { quantifier: "some" },
553
- },
554
- ],
555
- });
556
-
557
- const filters = criteria.getFilters();
558
- expect(filters).toHaveLength(1);
559
- expect(filters[0].options?.quantifier).toBe("some");
560
- });
561
-
562
- it("should preserve quantifier when cloning", () => {
563
- const original = Criteria.create<UserWithPostsDto>().whereSome(
564
- "posts.title",
565
- "contains",
566
- "test"
567
- );
568
-
569
- const cloned = original.clone();
570
- const filters = cloned.getFilters();
571
-
572
- expect(filters).toHaveLength(1);
573
- expect(filters[0].options?.quantifier).toBe("some");
574
- });
575
- });
576
-
577
- describe("toJSON with quantifier", () => {
578
- it("should serialize quantifier to JSON", () => {
579
- const criteria = Criteria.create<UserWithPostsDto>()
580
- .whereSome("posts.title", "contains", "test")
581
- .whereEvery("posts.content", "equals", "content");
582
-
583
- const json = criteria.toJSON();
584
-
585
- expect(json.filters).toHaveLength(2);
586
- expect(json.filters[0].options?.quantifier).toBe("some");
587
- expect(json.filters[1].options?.quantifier).toBe("every");
588
- });
589
- });
590
- });
591
-
592
- describe("Adapter", () => {
593
- type UserInDatabase = {
594
- id: string;
595
- name: string;
596
- user_posts: { title: string; content: string }[];
597
- leads: {
598
- contact: {
599
- fullName: string;
600
- };
601
- };
602
- };
603
- const UserWithPostsAdapter: CriteriaAdapter<
604
- UserWithPostsDto,
605
- UserInDatabase
606
- > = {
607
- posts: "user_posts",
608
- "leads.contact.name": "leads.contact.fullName",
609
- };
610
-
611
- let criteria: Criteria<UserWithPostsDto>;
612
- beforeEach(() => {
613
- criteria =
614
- Criteria.create<UserWithPostsDto>().useAdapter(UserWithPostsAdapter);
615
- });
616
-
617
- it("should resolve field path", () => {
618
- const filters = criteria
619
- .where("leads.contact.name", "contains", "John")
620
- .getFilters();
621
-
622
- expect(filters[0].field).toBe("leads.contact.fullName");
623
- });
624
-
625
- it("should resolve field path from query params", () => {
626
- const queryParams = {
627
- "posts:contains": "test",
628
- };
629
-
630
- const result = Criteria.fromQueryParams<UserWithPostsDto>(
631
- queryParams,
632
- UserWithPostsAdapter
633
- );
634
- const filters = result.getFilters();
635
-
636
- expect(filters).toHaveLength(1);
637
- expect(filters[0].field).toBe("user_posts");
638
- expect(filters[0].operator).toBe("contains");
639
- expect(filters[0].value).toBe("test");
640
- });
641
-
642
- it("should resolve field path from object", () => {
643
- const criteria = Criteria.fromObject<UserWithPostsDto>(
644
- {
645
- filters: [
646
- {
647
- field: "posts",
648
- operator: "in",
649
- value: [{ title: "test" }] as any,
650
- },
651
- ],
652
- },
653
- UserWithPostsAdapter
654
- );
655
- const filters = criteria.getFilters();
656
- expect(filters).toHaveLength(1);
657
- expect(filters[0].field).toBe("user_posts");
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
- });
715
- });
716
- });