@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
@@ -74,17 +74,17 @@ export type FieldType =
74
74
  * A single filter rule
75
75
  *
76
76
  * Examples:
77
- * - { field_path: 'firm.stage', operator: 'eq', value: 'Series A' }
78
- * - { field_path: 'recipe.cuisine', operator: 'in', value: ['Italian', 'French'] }
79
- * - { field_path: 'contact.email', operator: 'isnotnull', value: null }
80
- * - { field_path: 'organization.tags', operator: 'has_tag', value: 'enterprise' }
77
+ * - { fieldPath: 'firm.stage', operator: 'eq', value: 'Series A' }
78
+ * - { fieldPath: 'recipe.cuisine', operator: 'in', value: ['Italian', 'French'] }
79
+ * - { fieldPath: 'contact.email', operator: 'isnotnull', value: null }
80
+ * - { fieldPath: 'organization.tags', operator: 'has_tag', value: 'enterprise' }
81
81
  */
82
82
  export interface FilterRule {
83
83
  /**
84
84
  * Dot-notation path to field
85
85
  * Examples: 'name', 'firm.stage', 'profile.linkedin_url', 'tags', 'metrics.arr_usd'
86
86
  */
87
- field_path: string;
87
+ fieldPath: string;
88
88
 
89
89
  /** Comparison operator */
90
90
  operator: Operator;
@@ -156,22 +156,22 @@ export interface FunnelStage<TEntity = any> {
156
156
  * - AND: All rules must match
157
157
  * - OR: At least one rule must match
158
158
  */
159
- filter_logic: FilterLogic;
159
+ filterLogic: FilterLogic;
160
160
 
161
161
  /** Filter rules to evaluate */
162
162
  rules: FilterRule[];
163
163
 
164
164
  /** Action when rules match */
165
- match_action: MatchAction;
165
+ matchAction: MatchAction;
166
166
 
167
167
  /** Action when rules don't match */
168
- no_match_action: NoMatchAction;
168
+ noMatchAction: NoMatchAction;
169
169
 
170
170
  /** Tags to add when rules match */
171
- match_tags?: string[];
171
+ matchTags?: string[];
172
172
 
173
173
  /** Tags to add when rules don't match */
174
- no_match_tags?: string[];
174
+ noMatchTags?: string[];
175
175
 
176
176
  /**
177
177
  * Context to add to entity when rules match
@@ -180,13 +180,13 @@ export interface FunnelStage<TEntity = any> {
180
180
  * - { stage: 'qualified_leads', reason: 'high_fit_score' }
181
181
  * - { tier: 'premium', discount: 0.2 }
182
182
  */
183
- match_context?: Record<string, any>;
183
+ matchContext?: Record<string, any>;
184
184
 
185
185
  /**
186
186
  * Optional: Custom evaluation function
187
187
  * For complex logic that can't be expressed with rules
188
188
  */
189
- custom_evaluator?: (entity: TEntity) => boolean;
189
+ customEvaluator?: (entity: TEntity) => boolean;
190
190
  }
191
191
 
192
192
  // ============================================================================
@@ -225,28 +225,28 @@ export interface Funnel<TEntity = any> {
225
225
  * Type of entities this funnel processes
226
226
  * Used for field registry lookup
227
227
  */
228
- input_type: InputType;
228
+ inputType: InputType;
229
229
 
230
230
  /** Ordered stages */
231
231
  stages: FunnelStage<TEntity>[];
232
232
 
233
233
  /** Created timestamp */
234
- created_at: Date | string;
234
+ createdAt: Date | string;
235
235
 
236
236
  /** Last updated timestamp */
237
- updated_at: Date | string;
237
+ updatedAt: Date | string;
238
238
 
239
239
  /** Owner user ID */
240
- owner_id?: string;
240
+ ownerId?: string;
241
241
 
242
242
  /** Team/company ID for multi-tenancy */
243
- team_id?: string;
243
+ teamId?: string;
244
244
 
245
245
  /**
246
246
  * Optional: Tags to automatically add to all entities
247
247
  * that complete the funnel
248
248
  */
249
- completion_tags?: string[];
249
+ completionTags?: string[];
250
250
 
251
251
  /**
252
252
  * Optional: Metadata for custom integrations
@@ -282,34 +282,34 @@ export type TriggerType =
282
282
  */
283
283
  export interface StageStats {
284
284
  /** Stage ID */
285
- stage_id: string;
285
+ stageId: string;
286
286
 
287
287
  /** Stage name */
288
- stage_name: string;
288
+ stageName: string;
289
289
 
290
290
  /** Entities that entered this stage */
291
- input_count: number;
291
+ inputCount: number;
292
292
 
293
293
  /** Entities that matched rules */
294
- matched_count: number;
294
+ matchedCount: number;
295
295
 
296
296
  /** Entities that didn't match rules */
297
- not_matched_count: number;
297
+ notMatchedCount: number;
298
298
 
299
299
  /** Entities excluded at this stage */
300
- excluded_count: number;
300
+ excludedCount: number;
301
301
 
302
302
  /** Entities tagged at this stage */
303
- tagged_count: number;
303
+ taggedCount: number;
304
304
 
305
305
  /** Entities that continued to next stage */
306
- continued_count: number;
306
+ continuedCount: number;
307
307
 
308
308
  /** Execution time in milliseconds */
309
- duration_ms?: number;
309
+ durationMs?: number;
310
310
 
311
311
  /** Error count */
312
- error_count?: number;
312
+ errorCount?: number;
313
313
  }
314
314
 
315
315
  /**
@@ -320,7 +320,7 @@ export interface FunnelRun {
320
320
  id: string;
321
321
 
322
322
  /** Funnel ID */
323
- funnel_id: string;
323
+ funnelId: string;
324
324
 
325
325
  /** Funnel snapshot (copy of funnel at run time) */
326
326
  funnel?: Funnel;
@@ -329,40 +329,40 @@ export interface FunnelRun {
329
329
  status: FunnelRunStatus;
330
330
 
331
331
  /** How run was triggered */
332
- trigger_type: TriggerType;
332
+ triggerType: TriggerType;
333
333
 
334
334
  /** User who triggered (if manual) */
335
- triggered_by?: string;
335
+ triggeredBy?: string;
336
336
 
337
337
  /** Started timestamp */
338
- started_at: Date | string;
338
+ startedAt: Date | string;
339
339
 
340
340
  /** Completed timestamp */
341
- completed_at?: Date | string;
341
+ completedAt?: Date | string;
342
342
 
343
343
  /** Duration in milliseconds */
344
- duration_ms?: number;
344
+ durationMs?: number;
345
345
 
346
346
  /** Total entities input */
347
- total_input: number;
347
+ totalInput: number;
348
348
 
349
349
  /** Total entities matched (in output) */
350
- total_matched: number;
350
+ totalMatched: number;
351
351
 
352
352
  /** Total entities excluded */
353
- total_excluded: number;
353
+ totalExcluded: number;
354
354
 
355
355
  /** Total entities tagged */
356
- total_tagged: number;
356
+ totalTagged: number;
357
357
 
358
358
  /** Per-stage statistics */
359
- stage_stats: Record<string, StageStats>;
359
+ stageStats: Record<string, StageStats>;
360
360
 
361
361
  /** Error message (if failed) */
362
362
  error?: string;
363
363
 
364
364
  /** Stack trace (if failed) */
365
- stack_trace?: string;
365
+ stackTrace?: string;
366
366
 
367
367
  /** Metadata */
368
368
  metadata?: Record<string, any>;
@@ -379,16 +379,16 @@ export interface FunnelResult<TEntity = any> {
379
379
  matched: boolean;
380
380
 
381
381
  /** Stage where entity was excluded (if excluded) */
382
- excluded_at_stage?: string;
382
+ excludedAtStage?: string;
383
383
 
384
384
  /** Tags accumulated during processing */
385
- accumulated_tags: string[];
385
+ accumulatedTags: string[];
386
386
 
387
387
  /** Context accumulated during processing */
388
388
  context: Record<string, any>;
389
389
 
390
390
  /** Stage-by-stage results */
391
- stage_results?: StageResult[];
391
+ stageResults?: StageResult[];
392
392
  }
393
393
 
394
394
  /**
@@ -396,25 +396,25 @@ export interface FunnelResult<TEntity = any> {
396
396
  */
397
397
  export interface StageResult {
398
398
  /** Stage ID */
399
- stage_id: string;
399
+ stageId: string;
400
400
 
401
401
  /** Stage name */
402
- stage_name: string;
402
+ stageName: string;
403
403
 
404
404
  /** Whether rules matched */
405
405
  matched: boolean;
406
406
 
407
407
  /** Rule evaluation results */
408
- rule_results?: RuleResult[];
408
+ ruleResults?: RuleResult[];
409
409
 
410
410
  /** Action taken */
411
411
  action: MatchAction | NoMatchAction;
412
412
 
413
413
  /** Tags added */
414
- tags_added?: string[];
414
+ tagsAdded?: string[];
415
415
 
416
416
  /** Context added */
417
- context_added?: Record<string, any>;
417
+ contextAdded?: Record<string, any>;
418
418
 
419
419
  /** Whether entity was excluded */
420
420
  excluded: boolean;
@@ -428,7 +428,7 @@ export interface StageResult {
428
428
  */
429
429
  export interface RuleResult {
430
430
  /** Rule field path */
431
- field_path: string;
431
+ fieldPath: string;
432
432
 
433
433
  /** Rule operator */
434
434
  operator: Operator;
@@ -437,7 +437,7 @@ export interface RuleResult {
437
437
  value: any;
438
438
 
439
439
  /** Actual value from entity */
440
- actual_value: any;
440
+ actualValue: any;
441
441
 
442
442
  /** Whether rule matched */
443
443
  matched: boolean;
@@ -460,10 +460,10 @@ export type ValidOperators = Operator[];
460
460
  */
461
461
  export interface FieldConstraints {
462
462
  /** Minimum value (numbers, dates) */
463
- min_value?: number | string;
463
+ minValue?: number | string;
464
464
 
465
465
  /** Maximum value (numbers, dates) */
466
- max_value?: number | string;
466
+ maxValue?: number | string;
467
467
 
468
468
  /** Allowed values (enums) */
469
469
  choices?: any[];
@@ -513,7 +513,7 @@ export interface FieldDefinition {
513
513
  examples?: any[];
514
514
 
515
515
  /** Related fields (for UI suggestions) */
516
- related_fields?: string[];
516
+ relatedFields?: string[];
517
517
  }
518
518
 
519
519
  /**
@@ -523,13 +523,13 @@ export interface FieldDefinition {
523
523
  */
524
524
  export interface FieldRegistry {
525
525
  /** Entity type this registry is for */
526
- entity_type: string;
526
+ entityType: string;
527
527
 
528
528
  /** Available fields */
529
529
  fields: FieldDefinition[];
530
530
 
531
531
  /** Field lookup by name */
532
- field_map?: Map<string, FieldDefinition>;
532
+ fieldMap?: Map<string, FieldDefinition>;
533
533
 
534
534
  /** Categories (for UI grouping) */
535
535
  categories?: string[];
@@ -573,7 +573,7 @@ export type FieldPath<T, Prefix extends string = ''> = {
573
573
  */
574
574
  export type CreateFunnelInput<TEntity = any> = Omit<
575
575
  Funnel<TEntity>,
576
- 'id' | 'created_at' | 'updated_at'
576
+ 'id' | 'createdAt' | 'updatedAt'
577
577
  >;
578
578
 
579
579
  /**
@@ -625,7 +625,7 @@ export function isStage<TEntity = any>(value: unknown): value is FunnelStage<TEn
625
625
  typeof s.id === 'string' &&
626
626
  typeof s.name === 'string' &&
627
627
  typeof s.order === 'number' &&
628
- ['AND', 'OR'].includes(s.filter_logic) &&
628
+ ['AND', 'OR'].includes(s.filterLogic) &&
629
629
  Array.isArray(s.rules)
630
630
  );
631
631
  }
@@ -638,7 +638,7 @@ export function isFilterRule(value: unknown): value is FilterRule {
638
638
  return (
639
639
  typeof r === 'object' &&
640
640
  r !== null &&
641
- typeof r.field_path === 'string' &&
641
+ typeof r.fieldPath === 'string' &&
642
642
  typeof r.operator === 'string' &&
643
643
  r.value !== undefined
644
644
  );
@@ -653,7 +653,7 @@ export function isFunnelRun(value: unknown): value is FunnelRun {
653
653
  typeof r === 'object' &&
654
654
  r !== null &&
655
655
  typeof r.id === 'string' &&
656
- typeof r.funnel_id === 'string' &&
656
+ typeof r.funnelId === 'string' &&
657
657
  ['pending', 'running', 'completed', 'failed', 'cancelled'].includes(r.status)
658
658
  );
659
659
  }
@@ -670,7 +670,7 @@ export function isFunnelResult<TEntity = any>(
670
670
  r !== null &&
671
671
  r.entity !== undefined &&
672
672
  typeof r.matched === 'boolean' &&
673
- Array.isArray(r.accumulated_tags)
673
+ Array.isArray(r.accumulatedTags)
674
674
  );
675
675
  }
676
676
 
@@ -758,8 +758,8 @@ export function isValidOperator(
758
758
  export function validateFilterRule(rule: FilterRule): string[] {
759
759
  const errors: string[] = [];
760
760
 
761
- if (!rule.field_path) {
762
- errors.push('field_path is required');
761
+ if (!rule.fieldPath) {
762
+ errors.push('fieldPath is required');
763
763
  }
764
764
 
765
765
  if (!rule.operator) {
@@ -791,8 +791,8 @@ export function validateStage<TEntity = any>(
791
791
  errors.push('order must be a number');
792
792
  }
793
793
 
794
- if (!['AND', 'OR'].includes(stage.filter_logic)) {
795
- errors.push('filter_logic must be AND or OR');
794
+ if (!['AND', 'OR'].includes(stage.filterLogic)) {
795
+ errors.push('filterLogic must be AND or OR');
796
796
  }
797
797
 
798
798
  if (!Array.isArray(stage.rules)) {