@startsimpli/funnels 0.4.6 → 0.4.8

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 (46) hide show
  1. package/package.json +1 -1
  2. package/src/api/client.test.ts +76 -76
  3. package/src/api/client.ts +16 -16
  4. package/src/components/FilterRuleEditor/FilterRuleEditor.stories.tsx +24 -24
  5. package/src/components/FilterRuleEditor/FilterRuleEditor.test.tsx +39 -39
  6. package/src/components/FilterRuleEditor/FilterRuleEditor.tsx +1 -1
  7. package/src/components/FilterRuleEditor/RuleRow.tsx +8 -8
  8. package/src/components/FunnelCard/FunnelCard.stories.tsx +118 -118
  9. package/src/components/FunnelCard/FunnelCard.test.ts +48 -48
  10. package/src/components/FunnelCard/FunnelCard.test.tsx +40 -40
  11. package/src/components/FunnelCard/FunnelCard.tsx +3 -3
  12. package/src/components/FunnelPreview/FunnelPreview.test.tsx +63 -63
  13. package/src/components/FunnelPreview/FunnelPreview.tsx +11 -11
  14. package/src/components/FunnelPreview/StageBreakdown.tsx +5 -5
  15. package/src/components/FunnelPreview/example.tsx +43 -43
  16. package/src/components/FunnelRunHistory/FunnelRunHistory.stories.tsx +70 -70
  17. package/src/components/FunnelRunHistory/FunnelRunHistory.test.tsx +63 -63
  18. package/src/components/FunnelRunHistory/FunnelRunHistory.tsx +12 -12
  19. package/src/components/FunnelRunHistory/RunDetailsModal.tsx +5 -5
  20. package/src/components/FunnelRunHistory/RunFilters.tsx +8 -8
  21. package/src/components/FunnelRunHistory/RunRow.tsx +7 -7
  22. package/src/components/FunnelRunHistory/StageBreakdownList.tsx +9 -9
  23. package/src/components/FunnelRunHistory/types.ts +6 -6
  24. package/src/components/FunnelStageBuilder/FunnelStageBuilder.stories.tsx +41 -41
  25. package/src/components/FunnelStageBuilder/FunnelStageBuilder.test.tsx +6 -6
  26. package/src/components/FunnelStageBuilder/FunnelStageBuilder.tsx +5 -5
  27. package/src/components/FunnelStageBuilder/StageActions.test.tsx +7 -7
  28. package/src/components/FunnelStageBuilder/StageActions.tsx +4 -4
  29. package/src/components/FunnelStageBuilder/StageCard.tsx +3 -3
  30. package/src/components/FunnelStageBuilder/StageForm.tsx +14 -14
  31. package/src/components/FunnelVisualFlow/FunnelVisualFlow.stories.tsx +42 -42
  32. package/src/components/FunnelVisualFlow/FunnelVisualFlow.test.tsx +31 -31
  33. package/src/components/FunnelVisualFlow/FunnelVisualFlow.tsx +4 -4
  34. package/src/components/FunnelVisualFlow/StageNode.tsx +8 -8
  35. package/src/components/FunnelVisualFlow/example.tsx +7 -7
  36. package/src/core/engine.test.ts +271 -271
  37. package/src/core/engine.ts +60 -60
  38. package/src/core/evaluator.example.ts +31 -31
  39. package/src/core/evaluator.test.ts +92 -92
  40. package/src/core/evaluator.ts +23 -23
  41. package/src/core/field-resolver.example.ts +1 -1
  42. package/src/core/field-resolver.test.ts +4 -4
  43. package/src/store/create-funnel-store.test.ts +32 -32
  44. package/src/store/create-funnel-store.ts +3 -3
  45. package/src/store/types.ts +2 -2
  46. package/src/types/index.ts +64 -64
@@ -29,7 +29,7 @@ interface TestEntity {
29
29
  score?: number;
30
30
  active?: boolean;
31
31
  tags?: string[];
32
- created_at?: Date | string;
32
+ createdAt?: Date | string;
33
33
  firm?: {
34
34
  name?: string;
35
35
  stage?: string;
@@ -53,7 +53,7 @@ const sampleEntity: TestEntity = {
53
53
  score: 85.5,
54
54
  active: true,
55
55
  tags: ['qualified', 'active', 'premium'],
56
- created_at: new Date('2024-01-15'),
56
+ createdAt: new Date('2024-01-15'),
57
57
  firm: {
58
58
  name: 'Acme Ventures',
59
59
  stage: 'Series A',
@@ -75,32 +75,32 @@ const sampleEntity: TestEntity = {
75
75
 
76
76
  describe('Equality Operators', () => {
77
77
  it('eq: should match equal values', () => {
78
- const rule: FilterRule = { field_path: 'name', operator: 'eq', value: 'John Doe' };
78
+ const rule: FilterRule = { fieldPath: 'name', operator: 'eq', value: 'John Doe' };
79
79
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
80
80
  });
81
81
 
82
82
  it('eq: should not match different values', () => {
83
- const rule: FilterRule = { field_path: 'name', operator: 'eq', value: 'Jane Doe' };
83
+ const rule: FilterRule = { fieldPath: 'name', operator: 'eq', value: 'Jane Doe' };
84
84
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
85
85
  });
86
86
 
87
87
  it('eq: should match numbers', () => {
88
- const rule: FilterRule = { field_path: 'age', operator: 'eq', value: 35 };
88
+ const rule: FilterRule = { fieldPath: 'age', operator: 'eq', value: 35 };
89
89
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
90
90
  });
91
91
 
92
92
  it('eq: should match nested fields', () => {
93
- const rule: FilterRule = { field_path: 'firm.stage', operator: 'eq', value: 'Series A' };
93
+ const rule: FilterRule = { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' };
94
94
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
95
95
  });
96
96
 
97
97
  it('ne: should match different values', () => {
98
- const rule: FilterRule = { field_path: 'name', operator: 'ne', value: 'Jane Doe' };
98
+ const rule: FilterRule = { fieldPath: 'name', operator: 'ne', value: 'Jane Doe' };
99
99
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
100
100
  });
101
101
 
102
102
  it('ne: should not match equal values', () => {
103
- const rule: FilterRule = { field_path: 'name', operator: 'ne', value: 'John Doe' };
103
+ const rule: FilterRule = { fieldPath: 'name', operator: 'ne', value: 'John Doe' };
104
104
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
105
105
  });
106
106
  });
@@ -111,56 +111,56 @@ describe('Equality Operators', () => {
111
111
 
112
112
  describe('Comparison Operators', () => {
113
113
  it('gt: should match greater values', () => {
114
- const rule: FilterRule = { field_path: 'age', operator: 'gt', value: 30 };
114
+ const rule: FilterRule = { fieldPath: 'age', operator: 'gt', value: 30 };
115
115
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
116
116
  });
117
117
 
118
118
  it('gt: should not match equal or lesser values', () => {
119
- const rule: FilterRule = { field_path: 'age', operator: 'gt', value: 35 };
119
+ const rule: FilterRule = { fieldPath: 'age', operator: 'gt', value: 35 };
120
120
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
121
121
  });
122
122
 
123
123
  it('lt: should match lesser values', () => {
124
- const rule: FilterRule = { field_path: 'age', operator: 'lt', value: 40 };
124
+ const rule: FilterRule = { fieldPath: 'age', operator: 'lt', value: 40 };
125
125
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
126
126
  });
127
127
 
128
128
  it('lt: should not match equal or greater values', () => {
129
- const rule: FilterRule = { field_path: 'age', operator: 'lt', value: 35 };
129
+ const rule: FilterRule = { fieldPath: 'age', operator: 'lt', value: 35 };
130
130
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
131
131
  });
132
132
 
133
133
  it('gte: should match greater or equal values', () => {
134
- const rule1: FilterRule = { field_path: 'age', operator: 'gte', value: 35 };
135
- const rule2: FilterRule = { field_path: 'age', operator: 'gte', value: 30 };
134
+ const rule1: FilterRule = { fieldPath: 'age', operator: 'gte', value: 35 };
135
+ const rule2: FilterRule = { fieldPath: 'age', operator: 'gte', value: 30 };
136
136
  expect(evaluateRule(sampleEntity, rule1)).toBe(true);
137
137
  expect(evaluateRule(sampleEntity, rule2)).toBe(true);
138
138
  });
139
139
 
140
140
  it('gte: should not match lesser values', () => {
141
- const rule: FilterRule = { field_path: 'age', operator: 'gte', value: 40 };
141
+ const rule: FilterRule = { fieldPath: 'age', operator: 'gte', value: 40 };
142
142
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
143
143
  });
144
144
 
145
145
  it('lte: should match lesser or equal values', () => {
146
- const rule1: FilterRule = { field_path: 'age', operator: 'lte', value: 35 };
147
- const rule2: FilterRule = { field_path: 'age', operator: 'lte', value: 40 };
146
+ const rule1: FilterRule = { fieldPath: 'age', operator: 'lte', value: 35 };
147
+ const rule2: FilterRule = { fieldPath: 'age', operator: 'lte', value: 40 };
148
148
  expect(evaluateRule(sampleEntity, rule1)).toBe(true);
149
149
  expect(evaluateRule(sampleEntity, rule2)).toBe(true);
150
150
  });
151
151
 
152
152
  it('lte: should not match greater values', () => {
153
- const rule: FilterRule = { field_path: 'age', operator: 'lte', value: 30 };
153
+ const rule: FilterRule = { fieldPath: 'age', operator: 'lte', value: 30 };
154
154
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
155
155
  });
156
156
 
157
157
  it('should compare decimal numbers', () => {
158
- const rule: FilterRule = { field_path: 'score', operator: 'gt', value: 85 };
158
+ const rule: FilterRule = { fieldPath: 'score', operator: 'gt', value: 85 };
159
159
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
160
160
  });
161
161
 
162
162
  it('should compare nested numbers', () => {
163
- const rule: FilterRule = { field_path: 'firm.aum', operator: 'gte', value: 100000000 };
163
+ const rule: FilterRule = { fieldPath: 'firm.aum', operator: 'gte', value: 100000000 };
164
164
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
165
165
  });
166
166
  });
@@ -171,57 +171,57 @@ describe('Comparison Operators', () => {
171
171
 
172
172
  describe('String Operators', () => {
173
173
  it('contains: should match substring (case-insensitive)', () => {
174
- const rule: FilterRule = { field_path: 'name', operator: 'contains', value: 'john' };
174
+ const rule: FilterRule = { fieldPath: 'name', operator: 'contains', value: 'john' };
175
175
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
176
176
  });
177
177
 
178
178
  it('contains: should not match non-existent substring', () => {
179
- const rule: FilterRule = { field_path: 'name', operator: 'contains', value: 'jane' };
179
+ const rule: FilterRule = { fieldPath: 'name', operator: 'contains', value: 'jane' };
180
180
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
181
181
  });
182
182
 
183
183
  it('contains: should match nested string', () => {
184
- const rule: FilterRule = { field_path: 'firm.name', operator: 'contains', value: 'acme' };
184
+ const rule: FilterRule = { fieldPath: 'firm.name', operator: 'contains', value: 'acme' };
185
185
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
186
186
  });
187
187
 
188
188
  it('not_contains: should match when substring absent', () => {
189
- const rule: FilterRule = { field_path: 'name', operator: 'not_contains', value: 'jane' };
189
+ const rule: FilterRule = { fieldPath: 'name', operator: 'not_contains', value: 'jane' };
190
190
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
191
191
  });
192
192
 
193
193
  it('not_contains: should not match when substring present', () => {
194
- const rule: FilterRule = { field_path: 'name', operator: 'not_contains', value: 'john' };
194
+ const rule: FilterRule = { fieldPath: 'name', operator: 'not_contains', value: 'john' };
195
195
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
196
196
  });
197
197
 
198
198
  it('startswith: should match prefix (case-insensitive)', () => {
199
- const rule: FilterRule = { field_path: 'name', operator: 'startswith', value: 'john' };
199
+ const rule: FilterRule = { fieldPath: 'name', operator: 'startswith', value: 'john' };
200
200
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
201
201
  });
202
202
 
203
203
  it('startswith: should not match non-prefix', () => {
204
- const rule: FilterRule = { field_path: 'name', operator: 'startswith', value: 'doe' };
204
+ const rule: FilterRule = { fieldPath: 'name', operator: 'startswith', value: 'doe' };
205
205
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
206
206
  });
207
207
 
208
208
  it('endswith: should match suffix (case-insensitive)', () => {
209
- const rule: FilterRule = { field_path: 'name', operator: 'endswith', value: 'doe' };
209
+ const rule: FilterRule = { fieldPath: 'name', operator: 'endswith', value: 'doe' };
210
210
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
211
211
  });
212
212
 
213
213
  it('endswith: should not match non-suffix', () => {
214
- const rule: FilterRule = { field_path: 'name', operator: 'endswith', value: 'john' };
214
+ const rule: FilterRule = { fieldPath: 'name', operator: 'endswith', value: 'john' };
215
215
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
216
216
  });
217
217
 
218
218
  it('matches: should match regex pattern', () => {
219
- const rule: FilterRule = { field_path: 'email', operator: 'matches', value: '^[a-z]+@example\\.com$' };
219
+ const rule: FilterRule = { fieldPath: 'email', operator: 'matches', value: '^[a-z]+@example\\.com$' };
220
220
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
221
221
  });
222
222
 
223
223
  it('matches: should not match non-matching pattern', () => {
224
- const rule: FilterRule = { field_path: 'email', operator: 'matches', value: '^[0-9]+@' };
224
+ const rule: FilterRule = { fieldPath: 'email', operator: 'matches', value: '^[0-9]+@' };
225
225
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
226
226
  });
227
227
  });
@@ -232,42 +232,42 @@ describe('String Operators', () => {
232
232
 
233
233
  describe('Array Operators', () => {
234
234
  it('in: should match value in array', () => {
235
- const rule: FilterRule = { field_path: 'firm.stage', operator: 'in', value: ['Seed', 'Series A', 'Series B'] };
235
+ const rule: FilterRule = { fieldPath: 'firm.stage', operator: 'in', value: ['Seed', 'Series A', 'Series B'] };
236
236
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
237
237
  });
238
238
 
239
239
  it('in: should not match value not in array', () => {
240
- const rule: FilterRule = { field_path: 'firm.stage', operator: 'in', value: ['Seed', 'Series B'] };
240
+ const rule: FilterRule = { fieldPath: 'firm.stage', operator: 'in', value: ['Seed', 'Series B'] };
241
241
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
242
242
  });
243
243
 
244
244
  it('not_in: should match value not in array', () => {
245
- const rule: FilterRule = { field_path: 'firm.stage', operator: 'not_in', value: ['Seed', 'Series B'] };
245
+ const rule: FilterRule = { fieldPath: 'firm.stage', operator: 'not_in', value: ['Seed', 'Series B'] };
246
246
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
247
247
  });
248
248
 
249
249
  it('not_in: should not match value in array', () => {
250
- const rule: FilterRule = { field_path: 'firm.stage', operator: 'not_in', value: ['Series A', 'Series B'] };
250
+ const rule: FilterRule = { fieldPath: 'firm.stage', operator: 'not_in', value: ['Series A', 'Series B'] };
251
251
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
252
252
  });
253
253
 
254
254
  it('has_any: should match when array has any value', () => {
255
- const rule: FilterRule = { field_path: 'tags', operator: 'has_any', value: ['qualified', 'inactive'] };
255
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_any', value: ['qualified', 'inactive'] };
256
256
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
257
257
  });
258
258
 
259
259
  it('has_any: should not match when array has no values', () => {
260
- const rule: FilterRule = { field_path: 'tags', operator: 'has_any', value: ['inactive', 'suspended'] };
260
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_any', value: ['inactive', 'suspended'] };
261
261
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
262
262
  });
263
263
 
264
264
  it('has_all: should match when array has all values', () => {
265
- const rule: FilterRule = { field_path: 'tags', operator: 'has_all', value: ['qualified', 'active'] };
265
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_all', value: ['qualified', 'active'] };
266
266
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
267
267
  });
268
268
 
269
269
  it('has_all: should not match when array missing some values', () => {
270
- const rule: FilterRule = { field_path: 'tags', operator: 'has_all', value: ['qualified', 'inactive'] };
270
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_all', value: ['qualified', 'inactive'] };
271
271
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
272
272
  });
273
273
  });
@@ -286,37 +286,37 @@ describe('Null Operators', () => {
286
286
  };
287
287
 
288
288
  it('isnull: should match null values', () => {
289
- const rule: FilterRule = { field_path: 'email', operator: 'isnull', value: null };
289
+ const rule: FilterRule = { fieldPath: 'email', operator: 'isnull', value: null };
290
290
  expect(evaluateRule(entityWithNulls, rule)).toBe(true);
291
291
  });
292
292
 
293
293
  it('isnull: should match undefined values', () => {
294
- const rule: FilterRule = { field_path: 'age', operator: 'isnull', value: null };
294
+ const rule: FilterRule = { fieldPath: 'age', operator: 'isnull', value: null };
295
295
  expect(evaluateRule(entityWithNulls, rule)).toBe(true);
296
296
  });
297
297
 
298
298
  it('isnull: should not match defined values', () => {
299
- const rule: FilterRule = { field_path: 'name', operator: 'isnull', value: null };
299
+ const rule: FilterRule = { fieldPath: 'name', operator: 'isnull', value: null };
300
300
  expect(evaluateRule(entityWithNulls, rule)).toBe(false);
301
301
  });
302
302
 
303
303
  it('isnull: should match nested null', () => {
304
- const rule: FilterRule = { field_path: 'profile.linkedin_url', operator: 'isnull', value: null };
304
+ const rule: FilterRule = { fieldPath: 'profile.linkedin_url', operator: 'isnull', value: null };
305
305
  expect(evaluateRule(entityWithNulls, rule)).toBe(true);
306
306
  });
307
307
 
308
308
  it('isnotnull: should match defined values', () => {
309
- const rule: FilterRule = { field_path: 'name', operator: 'isnotnull', value: null };
309
+ const rule: FilterRule = { fieldPath: 'name', operator: 'isnotnull', value: null };
310
310
  expect(evaluateRule(entityWithNulls, rule)).toBe(true);
311
311
  });
312
312
 
313
313
  it('isnotnull: should not match null values', () => {
314
- const rule: FilterRule = { field_path: 'email', operator: 'isnotnull', value: null };
314
+ const rule: FilterRule = { fieldPath: 'email', operator: 'isnotnull', value: null };
315
315
  expect(evaluateRule(entityWithNulls, rule)).toBe(false);
316
316
  });
317
317
 
318
318
  it('isnotnull: should not match undefined values', () => {
319
- const rule: FilterRule = { field_path: 'age', operator: 'isnotnull', value: null };
319
+ const rule: FilterRule = { fieldPath: 'age', operator: 'isnotnull', value: null };
320
320
  expect(evaluateRule(entityWithNulls, rule)).toBe(false);
321
321
  });
322
322
  });
@@ -327,22 +327,22 @@ describe('Null Operators', () => {
327
327
 
328
328
  describe('Tag Operators', () => {
329
329
  it('has_tag: should match tag in array (case-insensitive)', () => {
330
- const rule: FilterRule = { field_path: 'tags', operator: 'has_tag', value: 'QUALIFIED' };
330
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_tag', value: 'QUALIFIED' };
331
331
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
332
332
  });
333
333
 
334
334
  it('has_tag: should not match missing tag', () => {
335
- const rule: FilterRule = { field_path: 'tags', operator: 'has_tag', value: 'inactive' };
335
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_tag', value: 'inactive' };
336
336
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
337
337
  });
338
338
 
339
339
  it('not_has_tag: should match when tag absent', () => {
340
- const rule: FilterRule = { field_path: 'tags', operator: 'not_has_tag', value: 'inactive' };
340
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'not_has_tag', value: 'inactive' };
341
341
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
342
342
  });
343
343
 
344
344
  it('not_has_tag: should not match when tag present', () => {
345
- const rule: FilterRule = { field_path: 'tags', operator: 'not_has_tag', value: 'qualified' };
345
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'not_has_tag', value: 'qualified' };
346
346
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
347
347
  });
348
348
  });
@@ -353,24 +353,24 @@ describe('Tag Operators', () => {
353
353
 
354
354
  describe('Boolean Operators', () => {
355
355
  it('is_true: should match true values', () => {
356
- const rule: FilterRule = { field_path: 'active', operator: 'is_true', value: null };
356
+ const rule: FilterRule = { fieldPath: 'active', operator: 'is_true', value: null };
357
357
  expect(evaluateRule(sampleEntity, rule)).toBe(true);
358
358
  });
359
359
 
360
360
  it('is_true: should not match false values', () => {
361
361
  const entity = { ...sampleEntity, active: false };
362
- const rule: FilterRule = { field_path: 'active', operator: 'is_true', value: null };
362
+ const rule: FilterRule = { fieldPath: 'active', operator: 'is_true', value: null };
363
363
  expect(evaluateRule(entity, rule)).toBe(false);
364
364
  });
365
365
 
366
366
  it('is_false: should match false values', () => {
367
367
  const entity = { ...sampleEntity, active: false };
368
- const rule: FilterRule = { field_path: 'active', operator: 'is_false', value: null };
368
+ const rule: FilterRule = { fieldPath: 'active', operator: 'is_false', value: null };
369
369
  expect(evaluateRule(entity, rule)).toBe(true);
370
370
  });
371
371
 
372
372
  it('is_false: should not match true values', () => {
373
- const rule: FilterRule = { field_path: 'active', operator: 'is_false', value: null };
373
+ const rule: FilterRule = { fieldPath: 'active', operator: 'is_false', value: null };
374
374
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
375
375
  });
376
376
  });
@@ -382,7 +382,7 @@ describe('Boolean Operators', () => {
382
382
  describe('Date Operators', () => {
383
383
  it('should compare Date objects', () => {
384
384
  const rule: FilterRule = {
385
- field_path: 'created_at',
385
+ fieldPath: 'createdAt',
386
386
  operator: 'gt',
387
387
  value: new Date('2024-01-01'),
388
388
  };
@@ -390,9 +390,9 @@ describe('Date Operators', () => {
390
390
  });
391
391
 
392
392
  it('should compare date strings', () => {
393
- const entity = { ...sampleEntity, created_at: '2024-01-15' };
393
+ const entity = { ...sampleEntity, createdAt: '2024-01-15' };
394
394
  const rule: FilterRule = {
395
- field_path: 'created_at',
395
+ fieldPath: 'createdAt',
396
396
  operator: 'gte',
397
397
  value: '2024-01-15',
398
398
  };
@@ -400,9 +400,9 @@ describe('Date Operators', () => {
400
400
  });
401
401
 
402
402
  it('should handle date equality', () => {
403
- const entity = { ...sampleEntity, created_at: '2024-01-15' };
403
+ const entity = { ...sampleEntity, createdAt: '2024-01-15' };
404
404
  const rule: FilterRule = {
405
- field_path: 'created_at',
405
+ fieldPath: 'createdAt',
406
406
  operator: 'eq',
407
407
  value: new Date('2024-01-15'),
408
408
  };
@@ -417,7 +417,7 @@ describe('Date Operators', () => {
417
417
  describe('Negate Flag', () => {
418
418
  it('should negate equality', () => {
419
419
  const rule: FilterRule = {
420
- field_path: 'name',
420
+ fieldPath: 'name',
421
421
  operator: 'eq',
422
422
  value: 'John Doe',
423
423
  negate: true,
@@ -427,7 +427,7 @@ describe('Negate Flag', () => {
427
427
 
428
428
  it('should negate inequality', () => {
429
429
  const rule: FilterRule = {
430
- field_path: 'name',
430
+ fieldPath: 'name',
431
431
  operator: 'ne',
432
432
  value: 'John Doe',
433
433
  negate: true,
@@ -437,7 +437,7 @@ describe('Negate Flag', () => {
437
437
 
438
438
  it('should negate contains', () => {
439
439
  const rule: FilterRule = {
440
- field_path: 'name',
440
+ fieldPath: 'name',
441
441
  operator: 'contains',
442
442
  value: 'john',
443
443
  negate: true,
@@ -452,31 +452,31 @@ describe('Negate Flag', () => {
452
452
 
453
453
  describe('Edge Cases', () => {
454
454
  it('should handle missing nested paths', () => {
455
- const rule: FilterRule = { field_path: 'firm.missing.field', operator: 'eq', value: 'test' };
455
+ const rule: FilterRule = { fieldPath: 'firm.missing.field', operator: 'eq', value: 'test' };
456
456
  expect(evaluateRule(sampleEntity, rule)).toBe(false);
457
457
  });
458
458
 
459
459
  it('should handle empty strings', () => {
460
460
  const entity = { name: '' };
461
- const rule: FilterRule = { field_path: 'name', operator: 'eq', value: '' };
461
+ const rule: FilterRule = { fieldPath: 'name', operator: 'eq', value: '' };
462
462
  expect(evaluateRule(entity, rule)).toBe(true);
463
463
  });
464
464
 
465
465
  it('should handle empty arrays', () => {
466
466
  const entity = { tags: [] };
467
- const rule: FilterRule = { field_path: 'tags', operator: 'has_tag', value: 'test' };
467
+ const rule: FilterRule = { fieldPath: 'tags', operator: 'has_tag', value: 'test' };
468
468
  expect(evaluateRule(entity, rule)).toBe(false);
469
469
  });
470
470
 
471
471
  it('should handle zero values', () => {
472
472
  const entity = { score: 0 };
473
- const rule: FilterRule = { field_path: 'score', operator: 'eq', value: 0 };
473
+ const rule: FilterRule = { fieldPath: 'score', operator: 'eq', value: 0 };
474
474
  expect(evaluateRule(entity, rule)).toBe(true);
475
475
  });
476
476
 
477
477
  it('should handle numeric strings in comparisons', () => {
478
478
  const entity = { age: '35' };
479
- const rule: FilterRule = { field_path: 'age', operator: 'gt', value: 30 };
479
+ const rule: FilterRule = { fieldPath: 'age', operator: 'gt', value: 30 };
480
480
  expect(evaluateRule(entity, rule)).toBe(true);
481
481
  });
482
482
  });
@@ -487,9 +487,9 @@ describe('Edge Cases', () => {
487
487
 
488
488
  describe('Multiple Rules', () => {
489
489
  const rules: FilterRule[] = [
490
- { field_path: 'age', operator: 'gte', value: 30 },
491
- { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },
492
- { field_path: 'tags', operator: 'has_tag', value: 'qualified' },
490
+ { fieldPath: 'age', operator: 'gte', value: 30 },
491
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' },
492
+ { fieldPath: 'tags', operator: 'has_tag', value: 'qualified' },
493
493
  ];
494
494
 
495
495
  it('AND: should match when all rules pass', () => {
@@ -499,23 +499,23 @@ describe('Multiple Rules', () => {
499
499
  it('AND: should not match when any rule fails', () => {
500
500
  const failingRules = [
501
501
  ...rules,
502
- { field_path: 'age', operator: 'lt', value: 30 } as FilterRule,
502
+ { fieldPath: 'age', operator: 'lt', value: 30 } as FilterRule,
503
503
  ];
504
504
  expect(evaluateRulesAND(sampleEntity, failingRules)).toBe(false);
505
505
  });
506
506
 
507
507
  it('OR: should match when any rule passes', () => {
508
508
  const orRules: FilterRule[] = [
509
- { field_path: 'age', operator: 'lt', value: 30 }, // false
510
- { field_path: 'firm.stage', operator: 'eq', value: 'Series A' }, // true
509
+ { fieldPath: 'age', operator: 'lt', value: 30 }, // false
510
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' }, // true
511
511
  ];
512
512
  expect(evaluateRulesOR(sampleEntity, orRules)).toBe(true);
513
513
  });
514
514
 
515
515
  it('OR: should not match when all rules fail', () => {
516
516
  const orRules: FilterRule[] = [
517
- { field_path: 'age', operator: 'lt', value: 30 },
518
- { field_path: 'firm.stage', operator: 'eq', value: 'Seed' },
517
+ { fieldPath: 'age', operator: 'lt', value: 30 },
518
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Seed' },
519
519
  ];
520
520
  expect(evaluateRulesOR(sampleEntity, orRules)).toBe(false);
521
521
  });
@@ -526,8 +526,8 @@ describe('Multiple Rules', () => {
526
526
 
527
527
  it('evaluateRules: should use specified logic', () => {
528
528
  const orRules: FilterRule[] = [
529
- { field_path: 'age', operator: 'lt', value: 30 },
530
- { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },
529
+ { fieldPath: 'age', operator: 'lt', value: 30 },
530
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' },
531
531
  ];
532
532
  expect(evaluateRules(sampleEntity, orRules, 'OR')).toBe(true);
533
533
  });
@@ -544,35 +544,35 @@ describe('Multiple Rules', () => {
544
544
 
545
545
  describe('Detailed Results', () => {
546
546
  it('should return detailed rule result', () => {
547
- const rule: FilterRule = { field_path: 'firm.stage', operator: 'eq', value: 'Series A' };
547
+ const rule: FilterRule = { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' };
548
548
  const result = evaluateRuleWithResult(sampleEntity, rule);
549
549
 
550
550
  expect(result).toEqual({
551
- field_path: 'firm.stage',
551
+ fieldPath: 'firm.stage',
552
552
  operator: 'eq',
553
553
  value: 'Series A',
554
- actual_value: 'Series A',
554
+ actualValue: 'Series A',
555
555
  matched: true,
556
556
  });
557
557
  });
558
558
 
559
559
  it('should return detailed results for multiple rules', () => {
560
560
  const rules: FilterRule[] = [
561
- { field_path: 'age', operator: 'gte', value: 30 },
562
- { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },
561
+ { fieldPath: 'age', operator: 'gte', value: 30 },
562
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' },
563
563
  ];
564
564
 
565
565
  const result = evaluateRulesWithResults(sampleEntity, rules, 'AND');
566
566
 
567
567
  expect(result.matched).toBe(true);
568
568
  expect(result.logic).toBe('AND');
569
- expect(result.rule_results).toHaveLength(2);
570
- expect(result.rule_results[0].matched).toBe(true);
571
- expect(result.rule_results[1].matched).toBe(true);
569
+ expect(result.ruleResults).toHaveLength(2);
570
+ expect(result.ruleResults[0].matched).toBe(true);
571
+ expect(result.ruleResults[1].matched).toBe(true);
572
572
  });
573
573
 
574
574
  it('should handle errors in rule evaluation', () => {
575
- const rule: FilterRule = { field_path: 'name', operator: 'matches' as any, value: '[invalid-regex' };
575
+ const rule: FilterRule = { fieldPath: 'name', operator: 'matches' as any, value: '[invalid-regex' };
576
576
  const result = evaluateRuleWithResult(sampleEntity, rule);
577
577
 
578
578
  expect(result.matched).toBe(false);
@@ -593,7 +593,7 @@ describe('Filter Entities', () => {
593
593
 
594
594
  it('should filter entities with single rule', () => {
595
595
  const rules: FilterRule[] = [
596
- { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },
596
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' },
597
597
  ];
598
598
 
599
599
  const result = filterEntities(entities, rules);
@@ -603,8 +603,8 @@ describe('Filter Entities', () => {
603
603
 
604
604
  it('should filter entities with multiple AND rules', () => {
605
605
  const rules: FilterRule[] = [
606
- { field_path: 'age', operator: 'gte', value: 30 },
607
- { field_path: 'firm.stage', operator: 'in', value: ['Series A', 'Series B'] },
606
+ { fieldPath: 'age', operator: 'gte', value: 30 },
607
+ { fieldPath: 'firm.stage', operator: 'in', value: ['Series A', 'Series B'] },
608
608
  ];
609
609
 
610
610
  const result = filterEntities(entities, rules, 'AND');
@@ -614,8 +614,8 @@ describe('Filter Entities', () => {
614
614
 
615
615
  it('should filter entities with OR rules', () => {
616
616
  const rules: FilterRule[] = [
617
- { field_path: 'age', operator: 'lt', value: 30 },
618
- { field_path: 'firm.stage', operator: 'eq', value: 'Series B' },
617
+ { fieldPath: 'age', operator: 'lt', value: 30 },
618
+ { fieldPath: 'firm.stage', operator: 'eq', value: 'Series B' },
619
619
  ];
620
620
 
621
621
  const result = filterEntities(entities, rules, 'OR');
@@ -630,7 +630,7 @@ describe('Filter Entities', () => {
630
630
 
631
631
  it('should return empty array for no matches', () => {
632
632
  const rules: FilterRule[] = [
633
- { field_path: 'age', operator: 'gt', value: 100 },
633
+ { fieldPath: 'age', operator: 'gt', value: 100 },
634
634
  ];
635
635
 
636
636
  const result = filterEntities(entities, rules);