@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
package/src/criteria.ts CHANGED
@@ -1,69 +1,93 @@
1
+ import { InvalidCriteriaError } from "./exceptions";
1
2
  import {
3
+ CriteriaAdapter,
4
+ CriteriaOptions,
2
5
  FieldPath,
3
6
  Filter,
4
- FILTER_OPERATORS,
5
7
  FilterOperator,
6
8
  FilterValueFor,
9
+ OperatorsForType,
7
10
  Order,
8
11
  OrderDirection,
9
12
  Pagination,
10
13
  PathValue,
14
+ Search,
11
15
  TypedFilter,
12
16
  } from "./types";
17
+ import {
18
+ isValidOperatorForType,
19
+ getValidOperatorsForType,
20
+ isOperator,
21
+ } from "./utils/criteria-operator-validation";
22
+ import { parseQueryValue } from "./utils/helpers";
13
23
 
14
- // ============================================================================
15
- // Filter Types
16
- // ============================================================================
17
-
18
- export class Criteria<T = unknown> {
24
+ export class Criteria<T = any> {
19
25
  private _filters: Filter<FieldPath<T>, any>[] = [];
20
26
  private _orders: Order[] = [];
21
27
  private _pagination: Pagination = { page: 1, limit: 20, offset: 0 };
22
- private _search?: {
23
- fields: FieldPath<T>[];
24
- value: string;
25
- };
28
+ private _search?: Search<T>;
29
+ private _adapter?: CriteriaAdapter<any, any>;
26
30
 
27
31
  private constructor() {}
28
32
 
29
- /**
30
- * Create a new Criteria instance
31
- */
32
- static create<T = unknown>(): Criteria<T> {
33
+ static create<T = any>(): Criteria<T> {
33
34
  return new Criteria<T>();
34
35
  }
35
36
 
36
- /**
37
- * Add a filter condition
38
- */
37
+ useAdapter<A extends CriteriaAdapter<any, any>>(map: A): this {
38
+ this._adapter = map;
39
+ return this;
40
+ }
41
+
42
+ getAdapter(): CriteriaAdapter<any, any> | undefined {
43
+ return this._adapter;
44
+ }
45
+
46
+ where<K extends FieldPath<T>>(
47
+ field: K,
48
+ operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
49
+ value?: FilterValueFor<PathValue<T, K>>,
50
+ options?: CriteriaOptions
51
+ ): this;
52
+
39
53
  where<K extends FieldPath<T>>(
40
54
  field: K,
41
55
  operator: FilterOperator,
42
- value?: FilterValueFor<PathValue<T, K>>
56
+ value?: FilterValueFor<PathValue<T, K>>,
57
+ options?: CriteriaOptions
43
58
  ): this {
59
+ this.validateOperator(operator, value);
60
+
44
61
  this._filters.push({
45
- field,
62
+ field: this.resolveFieldPath(field),
46
63
  operator,
47
64
  value,
65
+ options,
48
66
  });
49
67
  return this;
50
68
  }
51
69
 
52
- // === Shorthand methods (tipados) ===
53
-
54
70
  whereEquals<K extends FieldPath<T>>(field: K, value: PathValue<T, K>): this {
55
- return this.where(field, "equals", value);
71
+ return this.where(
72
+ field,
73
+ "equals" as OperatorsForType<PathValue<T, K>>,
74
+ value
75
+ );
56
76
  }
57
77
 
58
78
  whereContains<K extends FieldPath<T>>(
59
79
  field: K,
60
80
  value: PathValue<T, K>
61
81
  ): this {
62
- return this.where(field, "contains", value);
82
+ return this.where(
83
+ field,
84
+ "contains" as OperatorsForType<PathValue<T, K>>,
85
+ value
86
+ );
63
87
  }
64
88
 
65
89
  whereIn<K extends FieldPath<T>>(field: K, values: PathValue<T, K>[]): this {
66
- return this.where(field, "in", values);
90
+ return this.where(field, "in" as OperatorsForType<PathValue<T, K>>, values);
67
91
  }
68
92
 
69
93
  whereBetween<K extends FieldPath<T>>(
@@ -71,28 +95,51 @@ export class Criteria<T = unknown> {
71
95
  min: PathValue<T, K>,
72
96
  max: PathValue<T, K>
73
97
  ): this {
74
- return this.where(field, "between", [min, max] as [
75
- PathValue<T, K>,
76
- PathValue<T, K>
77
- ]);
98
+ return this.where(
99
+ field,
100
+ "between" as OperatorsForType<PathValue<T, K>>,
101
+ [min, max] as [PathValue<T, K>, PathValue<T, K>]
102
+ );
78
103
  }
79
104
 
80
105
  whereNull<K extends FieldPath<T>>(field: K): this {
81
- return this.where(field, "isNull");
106
+ return this.where(field, "isNull" as OperatorsForType<PathValue<T, K>>);
82
107
  }
83
108
 
84
109
  whereNotNull<K extends FieldPath<T>>(field: K): this {
85
- return this.where(field, "isNotNull");
110
+ return this.where(field, "isNotNull" as OperatorsForType<PathValue<T, K>>);
86
111
  }
87
112
 
88
- // === OrderBy ===
113
+ whereSome<K extends FieldPath<T>>(
114
+ field: K,
115
+ operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
116
+ value?: FilterValueFor<PathValue<T, K>>
117
+ ): this {
118
+ return this.where(field, operator, value, { quantifier: "some" });
119
+ }
120
+
121
+ whereEvery<K extends FieldPath<T>>(
122
+ field: K,
123
+ operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
124
+ value?: FilterValueFor<PathValue<T, K>>
125
+ ): this {
126
+ return this.where(field, operator, value, { quantifier: "every" });
127
+ }
128
+
129
+ whereNone<K extends FieldPath<T>>(
130
+ field: K,
131
+ operator: OperatorsForType<NonNullable<PathValue<T, K>>>,
132
+ value?: FilterValueFor<PathValue<T, K>>
133
+ ): this {
134
+ return this.where(field, operator, value, { quantifier: "none" });
135
+ }
89
136
 
90
137
  orderBy<K extends FieldPath<T>>(
91
138
  field: K,
92
139
  direction: OrderDirection = "asc"
93
140
  ): this {
94
141
  this._orders.push({
95
- field: String(field),
142
+ field: this.resolveFieldPath(field),
96
143
  direction,
97
144
  });
98
145
  return this;
@@ -106,13 +153,9 @@ export class Criteria<T = unknown> {
106
153
  return this.orderBy(field, "desc");
107
154
  }
108
155
 
109
- // --------------------------------------------------------------------------
110
- // Search (tipado)
111
- // --------------------------------------------------------------------------
112
-
113
156
  search<K extends FieldPath<T>>(fields: K[], value: string): this {
114
157
  this._search = {
115
- fields,
158
+ fields: fields.map(this.resolveFieldPath),
116
159
  value,
117
160
  };
118
161
  return this;
@@ -123,11 +166,14 @@ export class Criteria<T = unknown> {
123
166
  }
124
167
 
125
168
  getSearch() {
126
- return this._search;
169
+ return this._search
170
+ ? {
171
+ fields: this._search.fields.map(this.resolveFieldPath),
172
+ value: this._search.value,
173
+ }
174
+ : undefined;
127
175
  }
128
176
 
129
- // === Pagination ===
130
-
131
177
  paginate(page: number, limit: number): this {
132
178
  if (page < 1) page = 1;
133
179
  if (limit < 1) limit = 10;
@@ -144,14 +190,20 @@ export class Criteria<T = unknown> {
144
190
  return this.paginate(1, limit);
145
191
  }
146
192
 
147
- // === Getters ===
148
-
149
193
  getFilters(): Filter[] {
150
- return this._filters;
194
+ return this._filters.map((filter) => ({
195
+ field: this.resolveFieldPath(filter.field),
196
+ operator: filter.operator,
197
+ value: filter.value,
198
+ options: filter.options,
199
+ }));
151
200
  }
152
201
 
153
202
  getOrders(): Order[] {
154
- return this._orders;
203
+ return this._orders.map((order) => ({
204
+ field: this.resolveFieldPath(order.field as FieldPath<T>),
205
+ direction: order.direction,
206
+ }));
155
207
  }
156
208
 
157
209
  getPagination(): Pagination {
@@ -170,47 +222,132 @@ export class Criteria<T = unknown> {
170
222
  return this._pagination !== undefined;
171
223
  }
172
224
 
173
- // === Utilities ===
174
-
175
225
  clone(): Criteria<T> {
176
226
  const cloned = Criteria.create<T>();
177
- cloned._filters = [...this._filters];
178
- cloned._orders = [...this._orders];
227
+ cloned._filters = [
228
+ ...this._filters.map((filter) => ({
229
+ field: this.resolveFieldPath(filter.field),
230
+ operator: filter.operator,
231
+ value: filter.value,
232
+ options: filter.options,
233
+ })),
234
+ ];
235
+ cloned._orders = [
236
+ ...this._orders.map((order) => ({
237
+ field: this.resolveFieldPath(order.field as FieldPath<T>),
238
+ direction: order.direction,
239
+ })),
240
+ ];
179
241
  cloned._pagination = { ...this._pagination };
242
+ cloned._search = this._search
243
+ ? {
244
+ fields: this._search.fields.map(this.resolveFieldPath),
245
+ value: this._search.value,
246
+ }
247
+ : undefined;
248
+
249
+ if (this._adapter) {
250
+ cloned.useAdapter(this._adapter);
251
+ }
252
+
180
253
  return cloned;
181
254
  }
182
255
 
183
256
  toJSON() {
184
257
  return {
185
- filters: this._filters,
186
- orders: this._orders,
258
+ filters: this._filters.map((filter) => ({
259
+ field: this.resolveFieldPath(filter.field),
260
+ operator: filter.operator,
261
+ value: filter.value,
262
+ options: filter.options,
263
+ })),
264
+ orders: this._orders.map((order) => ({
265
+ field: this.resolveFieldPath(order.field as FieldPath<T>),
266
+ direction: order.direction,
267
+ })),
187
268
  pagination: this._pagination,
188
- search: this._search,
269
+ search: this._search
270
+ ? {
271
+ fields: this._search.fields.map(this.resolveFieldPath),
272
+ value: this._search.value,
273
+ }
274
+ : undefined,
189
275
  };
190
276
  }
191
277
 
192
- static fromObject<T>(obj: {
193
- filters?: TypedFilter<T>[];
194
- orders?: Order[];
195
- pagination?: Pagination;
196
- search?: { fields: FieldPath<T>[]; value: string };
197
- }): Criteria<T> {
278
+ static fromObject<T>(
279
+ obj: {
280
+ filters?: TypedFilter<T>[];
281
+ orders?: Order[];
282
+ pagination?: Pagination;
283
+ search?: { fields: FieldPath<T>[]; value: string };
284
+ },
285
+ adapter?: CriteriaAdapter<any, any>
286
+ ): Criteria<T> {
198
287
  const criteria = Criteria.create<T>();
199
- if (obj.filters) criteria._filters = [...obj.filters];
200
- if (obj.orders) criteria._orders = [...obj.orders];
288
+
289
+ if (adapter) {
290
+ criteria.useAdapter(adapter);
291
+ }
292
+
293
+ if (obj.filters) {
294
+ for (const filter of obj.filters) {
295
+ filter.field = criteria.resolveFieldPath(filter.field);
296
+ criteria.validateOperator(filter.operator, filter.value);
297
+ }
298
+ criteria._filters = [...obj.filters];
299
+ }
300
+ if (obj.orders)
301
+ criteria._orders = [
302
+ ...obj.orders.map((order) => ({
303
+ field: criteria.resolveFieldPath(order.field as FieldPath<T>),
304
+ direction: order.direction,
305
+ })),
306
+ ];
201
307
  if (obj.pagination) criteria._pagination = { ...obj.pagination };
202
- if (obj.search) criteria._search = { ...obj.search };
308
+ if (obj.search)
309
+ criteria._search = {
310
+ ...obj.search,
311
+ fields: obj.search.fields.map(criteria.resolveFieldPath),
312
+ };
203
313
 
204
314
  return criteria;
205
315
  }
206
316
 
207
- static fromQueryParams<T>(query: Record<string, any>): Criteria<T> {
317
+ protected resolveFieldPath(field: FieldPath<T>): FieldPath<T> {
318
+ if (!this?._adapter) return field;
319
+
320
+ if (this._adapter[field]) {
321
+ return this._adapter[field] as FieldPath<T>;
322
+ }
323
+
324
+ const parts = field.split(".");
325
+ for (let i = parts.length; i > 0; i--) {
326
+ const prefix = parts.slice(0, i).join(".");
327
+ if (this._adapter[prefix]) {
328
+ const rest = parts.slice(i).join(".");
329
+ return rest
330
+ ? (`${this._adapter[prefix]}.${rest}` as FieldPath<T>)
331
+ : (this._adapter[prefix] as FieldPath<T>);
332
+ }
333
+ }
334
+
335
+ return field;
336
+ }
337
+
338
+ static fromQueryParams<T>(
339
+ query: Record<string, any>,
340
+ adapter?: CriteriaAdapter<any, any>
341
+ ): Criteria<T> {
208
342
  const criteria = Criteria.create<T>();
209
343
 
344
+ if (adapter) {
345
+ criteria.useAdapter(adapter);
346
+ }
347
+
210
348
  for (const [key, value] of Object.entries(query)) {
211
- // Pagination
212
349
  if (key === "page") {
213
- continue; // We'll handle pagination after
350
+ continue;
214
351
  }
215
352
  if (key === "limit") {
216
353
  continue;
@@ -219,34 +356,78 @@ export class Criteria<T = unknown> {
219
356
  continue;
220
357
  }
221
358
 
222
- const [field, operatorRaw] = key.split(":");
359
+ const [field, operatorWithQuantifier] = key.split(":");
223
360
 
224
- if (!operatorRaw || !field) continue;
361
+ if (!operatorWithQuantifier || !field) continue;
362
+
363
+ const [operatorRaw, quantifierRaw] = operatorWithQuantifier.split("@");
225
364
  const operator = isOperator(operatorRaw) ? operatorRaw : null;
226
- if (!operator) throw new Error(`Invalid filter operator: ${operatorRaw}`);
365
+ if (!operator) {
366
+ throw new InvalidCriteriaError(`Invalid filter operator`, operatorRaw);
367
+ }
368
+
369
+ const validQuantifiers = ["some", "every", "none"];
370
+ const quantifier =
371
+ quantifierRaw && validQuantifiers.includes(quantifierRaw)
372
+ ? (quantifierRaw as CriteriaOptions["quantifier"])
373
+ : undefined;
374
+
375
+ if (quantifierRaw && !quantifier) {
376
+ throw new InvalidCriteriaError(
377
+ `Invalid quantifier. Valid values: ${validQuantifiers.join(", ")}`,
378
+ quantifierRaw
379
+ );
380
+ }
381
+
382
+ const options: CriteriaOptions | undefined = quantifier
383
+ ? { quantifier }
384
+ : undefined;
227
385
 
228
386
  let parsedValue: any = value;
229
387
 
388
+ const resolvedField = criteria.resolveFieldPath(field as FieldPath<T>);
389
+
230
390
  if (operator === "between") {
231
391
  parsedValue = value
232
392
  .split(",")
233
393
  .map((v: any) => parseQueryValue(v.trim()));
234
394
  if (parsedValue.length === 2) {
235
- criteria.whereBetween(field as any, parsedValue[0], parsedValue[1]);
395
+ criteria.where(
396
+ resolvedField,
397
+ "between" as OperatorsForType<PathValue<T, FieldPath<T>>>,
398
+ [parsedValue[0], parsedValue[1]] as [
399
+ PathValue<T, FieldPath<T>>,
400
+ PathValue<T, FieldPath<T>>
401
+ ],
402
+ options
403
+ );
236
404
  }
237
405
  continue;
238
406
  }
239
407
 
240
408
  if (operator === "in" || operator === "notIn") {
241
409
  parsedValue = value.split(",").map(parseQueryValue);
242
- criteria.where(field as any, operator, parsedValue);
410
+ criteria.where(
411
+ field as any,
412
+ operator as OperatorsForType<PathValue<T, FieldPath<T>>>,
413
+ parsedValue,
414
+ options
415
+ );
243
416
  continue;
244
417
  }
245
418
 
246
- criteria.where(field as any, operator, parseQueryValue(value));
419
+ const parsedFinalValue = parseQueryValue(value);
420
+
421
+ criteria.validateOperator(operator, parsedFinalValue);
422
+
423
+ criteria.where(
424
+ field as FieldPath<T>,
425
+ operator as OperatorsForType<PathValue<T, FieldPath<T>>>,
426
+ parsedFinalValue,
427
+ options
428
+ );
247
429
  }
248
430
 
249
- // Pagination
250
431
  const page = query.page ? parseInt(query.page) : undefined;
251
432
  const limit = query.limit ? parseInt(query.limit) : undefined;
252
433
 
@@ -254,12 +435,14 @@ export class Criteria<T = unknown> {
254
435
  criteria.paginate(page, limit);
255
436
  }
256
437
 
257
- // Sorting
258
438
  if (query.orderBy) {
259
439
  const sortParts = query.orderBy.split(",");
260
440
  sortParts.forEach((part: string) => {
261
441
  const [field, direction] = part.split(":");
262
- criteria.orderBy(field as any, (direction as OrderDirection) || "asc");
442
+ criteria.orderBy(
443
+ field as FieldPath<T>,
444
+ (direction as OrderDirection) || "asc"
445
+ );
263
446
  });
264
447
  }
265
448
 
@@ -268,24 +451,22 @@ export class Criteria<T = unknown> {
268
451
  .split(",")
269
452
  .filter(Boolean) as FieldPath<T>[];
270
453
 
271
- criteria.search(fields, query.search as string);
454
+ const resolvedFields = fields.map(criteria.resolveFieldPath);
455
+ criteria.search(resolvedFields, query.search as string);
272
456
  }
273
457
 
274
458
  return criteria;
275
459
  }
276
- }
277
-
278
- // ============================================================================
279
- // Helper Functions
280
- // ============================================================================
281
460
 
282
- function parseQueryValue(value: string): any {
283
- if (!isNaN(Number(value))) return Number(value); // number
284
- if (value === "true" || value === "false") return value === "true"; // boolean
285
- if (!isNaN(Date.parse(value))) return new Date(value); // Date
286
- return value; // string
287
- }
288
-
289
- function isOperator(value: string): value is FilterOperator {
290
- return FILTER_OPERATORS.includes(value as FilterOperator);
461
+ private validateOperator(operator: FilterOperator, value: any): void {
462
+ if (value !== undefined && !isValidOperatorForType(value, operator)) {
463
+ const validOps = getValidOperatorsForType(value);
464
+ throw new InvalidCriteriaError(
465
+ `Operator "${operator}" is not valid for type "${typeof value}". Valid operators: ${validOps.join(
466
+ ", "
467
+ )}`,
468
+ operator
469
+ );
470
+ }
471
+ }
291
472
  }