@startsimpli/funnels 0.1.3 → 0.1.5

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 (151) hide show
  1. package/package.json +9 -31
  2. package/src/api/README.md +507 -0
  3. package/src/api/adapter.ts +106 -0
  4. package/src/api/client.test.ts +640 -0
  5. package/src/api/client.ts +385 -0
  6. package/src/api/default-adapter.ts +243 -0
  7. package/src/api/index.ts +24 -0
  8. package/src/components/FilterRuleEditor/ARCHITECTURE.md +354 -0
  9. package/src/components/FilterRuleEditor/FieldSelector.tsx +91 -0
  10. package/src/components/FilterRuleEditor/FilterRuleEditor.stories.tsx +462 -0
  11. package/src/components/FilterRuleEditor/FilterRuleEditor.test.tsx +520 -0
  12. package/src/components/FilterRuleEditor/FilterRuleEditor.tsx +225 -0
  13. package/src/components/FilterRuleEditor/LogicToggle.tsx +64 -0
  14. package/src/components/FilterRuleEditor/OperatorSelector.tsx +75 -0
  15. package/src/components/FilterRuleEditor/README.md +291 -0
  16. package/src/components/FilterRuleEditor/RuleRow.tsx +246 -0
  17. package/src/components/FilterRuleEditor/ValueInputs/BooleanValueInput.tsx +54 -0
  18. package/src/components/FilterRuleEditor/ValueInputs/ChoiceValueInput.tsx +83 -0
  19. package/src/components/FilterRuleEditor/ValueInputs/DateValueInput.tsx +70 -0
  20. package/src/components/FilterRuleEditor/ValueInputs/MultiChoiceValueInput.tsx +132 -0
  21. package/src/components/FilterRuleEditor/ValueInputs/NumberValueInput.tsx +73 -0
  22. package/src/components/FilterRuleEditor/ValueInputs/TextValueInput.tsx +50 -0
  23. package/src/components/FilterRuleEditor/ValueInputs/index.ts +12 -0
  24. package/src/components/FilterRuleEditor/constants.ts +64 -0
  25. package/src/components/FilterRuleEditor/index.ts +14 -0
  26. package/src/components/FunnelCard/DESIGN.md +447 -0
  27. package/src/components/FunnelCard/FunnelCard.stories.tsx +484 -0
  28. package/src/components/FunnelCard/FunnelCard.test.ts +257 -0
  29. package/src/components/FunnelCard/FunnelCard.test.tsx +336 -0
  30. package/src/components/FunnelCard/FunnelCard.tsx +204 -0
  31. package/src/components/FunnelCard/FunnelStats.tsx +68 -0
  32. package/src/components/FunnelCard/IMPLEMENTATION_SUMMARY.md +505 -0
  33. package/src/components/FunnelCard/INSTALLATION.md +304 -0
  34. package/src/components/FunnelCard/MatchBar.tsx +49 -0
  35. package/src/components/FunnelCard/README.md +294 -0
  36. package/src/components/FunnelCard/StageIndicator.tsx +62 -0
  37. package/src/components/FunnelCard/StatusBadge.tsx +52 -0
  38. package/src/components/FunnelCard/index.ts +14 -0
  39. package/src/components/FunnelPreview/EntityCard.tsx +72 -0
  40. package/src/components/FunnelPreview/FunnelPreview.stories.tsx +227 -0
  41. package/src/components/FunnelPreview/FunnelPreview.test.tsx +316 -0
  42. package/src/components/FunnelPreview/FunnelPreview.tsx +249 -0
  43. package/src/components/FunnelPreview/LoadingPreview.tsx +60 -0
  44. package/src/components/FunnelPreview/PreviewStats.tsx +78 -0
  45. package/src/components/FunnelPreview/README.md +337 -0
  46. package/src/components/FunnelPreview/StageBreakdown.tsx +94 -0
  47. package/src/components/FunnelPreview/example.tsx +286 -0
  48. package/src/components/FunnelPreview/index.ts +14 -0
  49. package/src/components/FunnelRunHistory/COMPONENT_SUMMARY.md +246 -0
  50. package/src/components/FunnelRunHistory/FunnelRunHistory.stories.tsx +272 -0
  51. package/src/components/FunnelRunHistory/FunnelRunHistory.test.tsx +323 -0
  52. package/src/components/FunnelRunHistory/FunnelRunHistory.tsx +329 -0
  53. package/src/components/FunnelRunHistory/README.md +325 -0
  54. package/src/components/FunnelRunHistory/RunActions.tsx +168 -0
  55. package/src/components/FunnelRunHistory/RunDetailsModal.tsx +221 -0
  56. package/src/components/FunnelRunHistory/RunFilters.tsx +128 -0
  57. package/src/components/FunnelRunHistory/RunRow.tsx +122 -0
  58. package/src/components/FunnelRunHistory/RunStatusBadge.tsx +75 -0
  59. package/src/components/FunnelRunHistory/StageBreakdownList.tsx +110 -0
  60. package/src/components/FunnelRunHistory/index.ts +51 -0
  61. package/src/components/FunnelRunHistory/types.ts +40 -0
  62. package/src/components/FunnelRunHistory/utils.test.ts +126 -0
  63. package/src/components/FunnelRunHistory/utils.ts +100 -0
  64. package/src/components/FunnelStageBuilder/AddStageButton.tsx +52 -0
  65. package/src/components/FunnelStageBuilder/FunnelStageBuilder.css +413 -0
  66. package/src/components/FunnelStageBuilder/FunnelStageBuilder.stories.tsx +312 -0
  67. package/src/components/FunnelStageBuilder/FunnelStageBuilder.test.tsx +304 -0
  68. package/src/components/FunnelStageBuilder/FunnelStageBuilder.tsx +321 -0
  69. package/src/components/FunnelStageBuilder/README.md +341 -0
  70. package/src/components/FunnelStageBuilder/StageActions.test.tsx +205 -0
  71. package/src/components/FunnelStageBuilder/StageActions.tsx +126 -0
  72. package/src/components/FunnelStageBuilder/StageCard.tsx +202 -0
  73. package/src/components/FunnelStageBuilder/StageForm.tsx +262 -0
  74. package/src/components/FunnelStageBuilder/TagInput.test.tsx +178 -0
  75. package/src/components/FunnelStageBuilder/TagInput.tsx +129 -0
  76. package/src/components/FunnelStageBuilder/index.ts +21 -0
  77. package/src/components/FunnelVisualFlow/FlowLegend.tsx +77 -0
  78. package/{dist/components/index.css → src/components/FunnelVisualFlow/FunnelVisualFlow.css} +89 -13
  79. package/src/components/FunnelVisualFlow/FunnelVisualFlow.stories.tsx +254 -0
  80. package/src/components/FunnelVisualFlow/FunnelVisualFlow.test.tsx +208 -0
  81. package/src/components/FunnelVisualFlow/FunnelVisualFlow.tsx +229 -0
  82. package/src/components/FunnelVisualFlow/README.md +323 -0
  83. package/src/components/FunnelVisualFlow/StageNode.tsx +188 -0
  84. package/src/components/FunnelVisualFlow/example.tsx +227 -0
  85. package/src/components/FunnelVisualFlow/index.ts +10 -0
  86. package/src/components/index.ts +102 -0
  87. package/src/core/README.md +307 -0
  88. package/src/core/engine.test.ts +1087 -0
  89. package/src/core/engine.ts +329 -0
  90. package/src/core/evaluator.example.ts +353 -0
  91. package/src/core/evaluator.test.ts +639 -0
  92. package/src/core/evaluator.ts +261 -0
  93. package/src/core/field-resolver.example.ts +175 -0
  94. package/src/core/field-resolver.test.ts +541 -0
  95. package/src/core/field-resolver.ts +247 -0
  96. package/src/core/index.ts +34 -0
  97. package/src/core/operators.test.ts +539 -0
  98. package/src/core/operators.ts +241 -0
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/useDebouncedValue.ts +28 -0
  101. package/src/index.ts +155 -0
  102. package/src/store/README.md +342 -0
  103. package/src/store/create-funnel-store.test.ts +686 -0
  104. package/src/store/create-funnel-store.ts +538 -0
  105. package/src/store/index.ts +9 -0
  106. package/src/store/types.ts +294 -0
  107. package/src/stories/CrossDomain.stories.tsx +149 -0
  108. package/src/stories/Welcome.stories.tsx +81 -0
  109. package/src/stories/demo-data/index.ts +3 -0
  110. package/src/stories/demo-data/investors.ts +216 -0
  111. package/src/stories/demo-data/leads.ts +223 -0
  112. package/src/stories/demo-data/recipes.ts +217 -0
  113. package/src/test/setup.ts +5 -0
  114. package/src/types/index.ts +843 -0
  115. package/dist/client-3ESO2NHy.d.ts +0 -310
  116. package/dist/client-CZu03ACp.d.cts +0 -310
  117. package/dist/components/index.cjs +0 -3243
  118. package/dist/components/index.cjs.map +0 -1
  119. package/dist/components/index.css.map +0 -1
  120. package/dist/components/index.d.cts +0 -726
  121. package/dist/components/index.d.ts +0 -726
  122. package/dist/components/index.js +0 -3196
  123. package/dist/components/index.js.map +0 -1
  124. package/dist/core/index.cjs +0 -500
  125. package/dist/core/index.cjs.map +0 -1
  126. package/dist/core/index.d.cts +0 -359
  127. package/dist/core/index.d.ts +0 -359
  128. package/dist/core/index.js +0 -486
  129. package/dist/core/index.js.map +0 -1
  130. package/dist/hooks/index.cjs +0 -21
  131. package/dist/hooks/index.cjs.map +0 -1
  132. package/dist/hooks/index.d.cts +0 -11
  133. package/dist/hooks/index.d.ts +0 -11
  134. package/dist/hooks/index.js +0 -19
  135. package/dist/hooks/index.js.map +0 -1
  136. package/dist/index-BGDEXbuz.d.cts +0 -434
  137. package/dist/index-BGDEXbuz.d.ts +0 -434
  138. package/dist/index.cjs +0 -4499
  139. package/dist/index.cjs.map +0 -1
  140. package/dist/index.css +0 -198
  141. package/dist/index.css.map +0 -1
  142. package/dist/index.d.cts +0 -99
  143. package/dist/index.d.ts +0 -99
  144. package/dist/index.js +0 -4421
  145. package/dist/index.js.map +0 -1
  146. package/dist/store/index.cjs +0 -391
  147. package/dist/store/index.cjs.map +0 -1
  148. package/dist/store/index.d.cts +0 -225
  149. package/dist/store/index.d.ts +0 -225
  150. package/dist/store/index.js +0 -388
  151. package/dist/store/index.js.map +0 -1
@@ -1,486 +0,0 @@
1
- // src/core/field-resolver.ts
2
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
3
- function parseFieldPath(fieldPath) {
4
- const segments = [];
5
- let current = "";
6
- let inBracket = false;
7
- for (let i = 0; i < fieldPath.length; i++) {
8
- const char = fieldPath[i];
9
- if (char === "[") {
10
- if (current) {
11
- segments.push(current);
12
- current = "";
13
- }
14
- inBracket = true;
15
- } else if (char === "]") {
16
- if (current) {
17
- segments.push(current);
18
- current = "";
19
- }
20
- inBracket = false;
21
- } else if (char === "." && !inBracket) {
22
- if (current) {
23
- segments.push(current);
24
- current = "";
25
- }
26
- } else {
27
- current += char;
28
- }
29
- }
30
- if (current) {
31
- segments.push(current);
32
- }
33
- return segments;
34
- }
35
- function resolveField(entity, fieldPath) {
36
- if (entity == null) {
37
- return void 0;
38
- }
39
- if (!fieldPath || fieldPath.trim() === "") {
40
- return void 0;
41
- }
42
- const segments = parseFieldPath(fieldPath);
43
- let current = entity;
44
- for (const segment of segments) {
45
- if (current == null) {
46
- return void 0;
47
- }
48
- if (DANGEROUS_KEYS.has(segment)) {
49
- return void 0;
50
- }
51
- if (typeof current === "object" && segment in current) {
52
- current = current[segment];
53
- } else {
54
- return void 0;
55
- }
56
- }
57
- return current;
58
- }
59
- function setField(entity, fieldPath, value) {
60
- if (entity == null) {
61
- throw new Error("Cannot set field on null or undefined entity");
62
- }
63
- if (!fieldPath || fieldPath.trim() === "") {
64
- throw new Error("Field path cannot be empty");
65
- }
66
- const segments = parseFieldPath(fieldPath);
67
- let current = entity;
68
- for (let i = 0; i < segments.length - 1; i++) {
69
- const segment = segments[i];
70
- const nextSegment = segments[i + 1];
71
- if (DANGEROUS_KEYS.has(segment)) {
72
- throw new Error(`Dangerous field path segment: "${segment}"`);
73
- }
74
- if (!(segment in current)) {
75
- const isNextArray = /^\d+$/.test(nextSegment);
76
- current[segment] = isNextArray ? [] : {};
77
- }
78
- current = current[segment];
79
- }
80
- const lastSegment = segments[segments.length - 1];
81
- if (DANGEROUS_KEYS.has(lastSegment)) {
82
- throw new Error(`Dangerous field path segment: "${lastSegment}"`);
83
- }
84
- current[lastSegment] = value;
85
- }
86
- function hasField(entity, fieldPath) {
87
- const value = resolveField(entity, fieldPath);
88
- return value !== void 0;
89
- }
90
- function getFields(entity, fieldPaths) {
91
- const result = {};
92
- for (const path of fieldPaths) {
93
- result[path] = resolveField(entity, path);
94
- }
95
- return result;
96
- }
97
-
98
- // src/core/operators.ts
99
- function applyOperator(operator, actual, expected) {
100
- switch (operator) {
101
- // ========================================================================
102
- // Equality
103
- // ========================================================================
104
- case "eq":
105
- return compareValues(actual, expected, (a, e) => a === e);
106
- case "ne":
107
- return compareValues(actual, expected, (a, e) => a !== e);
108
- // ========================================================================
109
- // Comparison (numbers, dates, strings)
110
- // ========================================================================
111
- case "gt":
112
- return compareValues(actual, expected, (a, e) => a > e);
113
- case "lt":
114
- return compareValues(actual, expected, (a, e) => a < e);
115
- case "gte":
116
- return compareValues(actual, expected, (a, e) => a >= e);
117
- case "lte":
118
- return compareValues(actual, expected, (a, e) => a <= e);
119
- // ========================================================================
120
- // String operations (case-insensitive)
121
- // ========================================================================
122
- case "contains":
123
- if (actual == null) return false;
124
- if (expected == null) return false;
125
- return String(actual).toLowerCase().includes(String(expected).toLowerCase());
126
- case "not_contains":
127
- if (actual == null) return true;
128
- if (expected == null) return true;
129
- return !String(actual).toLowerCase().includes(String(expected).toLowerCase());
130
- case "startswith":
131
- if (actual == null) return false;
132
- if (expected == null) return false;
133
- return String(actual).toLowerCase().startsWith(String(expected).toLowerCase());
134
- case "endswith":
135
- if (actual == null) return false;
136
- if (expected == null) return false;
137
- return String(actual).toLowerCase().endsWith(String(expected).toLowerCase());
138
- case "matches":
139
- if (actual == null) return false;
140
- if (expected == null) return false;
141
- try {
142
- const regex = new RegExp(String(expected));
143
- return regex.test(String(actual));
144
- } catch {
145
- return false;
146
- }
147
- // ========================================================================
148
- // Array/Set operations
149
- // ========================================================================
150
- case "in":
151
- if (!Array.isArray(expected)) return false;
152
- return expected.some((item) => compareValues(actual, item, (a, e) => a === e));
153
- case "not_in":
154
- if (!Array.isArray(expected)) return true;
155
- return !expected.some((item) => compareValues(actual, item, (a, e) => a === e));
156
- case "has_any":
157
- if (!Array.isArray(actual)) return false;
158
- if (!Array.isArray(expected)) return false;
159
- return expected.some(
160
- (expectedItem) => actual.some((actualItem) => compareValues(actualItem, expectedItem, (a, e) => a === e))
161
- );
162
- case "has_all":
163
- if (!Array.isArray(actual)) return false;
164
- if (!Array.isArray(expected)) return false;
165
- return expected.every(
166
- (expectedItem) => actual.some((actualItem) => compareValues(actualItem, expectedItem, (a, e) => a === e))
167
- );
168
- // ========================================================================
169
- // Null checks
170
- // ========================================================================
171
- case "isnull":
172
- return actual === null || actual === void 0;
173
- case "isnotnull":
174
- return actual !== null && actual !== void 0;
175
- // ========================================================================
176
- // Tag operations (tags are arrays of strings)
177
- // ========================================================================
178
- case "has_tag":
179
- if (!Array.isArray(actual)) return false;
180
- if (expected == null) return false;
181
- const expectedTag = String(expected).toLowerCase();
182
- return actual.some((tag) => String(tag).toLowerCase() === expectedTag);
183
- case "not_has_tag":
184
- if (!Array.isArray(actual)) return true;
185
- if (expected == null) return true;
186
- const expectedTagNot = String(expected).toLowerCase();
187
- return !actual.some((tag) => String(tag).toLowerCase() === expectedTagNot);
188
- // ========================================================================
189
- // Boolean
190
- // ========================================================================
191
- case "is_true":
192
- return actual === true;
193
- case "is_false":
194
- return actual === false;
195
- default:
196
- throw new Error(`Unknown operator: ${operator}`);
197
- }
198
- }
199
- function compareValues(actual, expected, compare) {
200
- if (actual === null || actual === void 0) {
201
- return expected === null || expected === void 0;
202
- }
203
- if (expected === null || expected === void 0) {
204
- return false;
205
- }
206
- if (isDate(actual) && isDate(expected)) {
207
- const actualTs = toTimestamp(actual);
208
- const expectedTs = toTimestamp(expected);
209
- if (actualTs === null || expectedTs === null) return false;
210
- return compare(actualTs, expectedTs);
211
- }
212
- if (isDate(actual) || isDate(expected)) {
213
- const actualTs = toTimestamp(actual);
214
- const expectedTs = toTimestamp(expected);
215
- if (actualTs === null || expectedTs === null) return false;
216
- return compare(actualTs, expectedTs);
217
- }
218
- if (typeof actual === "number" && typeof expected === "number") {
219
- return compare(actual, expected);
220
- }
221
- if (isNumeric(actual) && isNumeric(expected)) {
222
- return compare(Number(actual), Number(expected));
223
- }
224
- return compare(actual, expected);
225
- }
226
- function isDate(value) {
227
- if (value instanceof Date) return true;
228
- if (typeof value !== "string") return false;
229
- const datePattern = /^\d{4}-\d{2}-\d{2}|^\d{1,2}\/\d{1,2}\/\d{4}/;
230
- return datePattern.test(value);
231
- }
232
- function toTimestamp(value) {
233
- if (value instanceof Date) {
234
- const ts = value.getTime();
235
- return isNaN(ts) ? null : ts;
236
- }
237
- if (typeof value === "string" || typeof value === "number") {
238
- const date = new Date(value);
239
- const ts = date.getTime();
240
- return isNaN(ts) ? null : ts;
241
- }
242
- return null;
243
- }
244
- function isNumeric(value) {
245
- if (typeof value === "number") return !isNaN(value);
246
- if (typeof value !== "string") return false;
247
- return !isNaN(Number(value)) && value.trim() !== "";
248
- }
249
-
250
- // src/core/evaluator.ts
251
- function evaluateRule(entity, rule) {
252
- const actualValue = resolveField(entity, rule.field_path);
253
- const result = applyOperator(rule.operator, actualValue, rule.value);
254
- return rule.negate ? !result : result;
255
- }
256
- function evaluateRuleWithResult(entity, rule) {
257
- try {
258
- const actualValue = resolveField(entity, rule.field_path);
259
- const operatorResult = applyOperator(rule.operator, actualValue, rule.value);
260
- const matched = rule.negate ? !operatorResult : operatorResult;
261
- return {
262
- field_path: rule.field_path,
263
- operator: rule.operator,
264
- value: rule.value,
265
- actual_value: actualValue,
266
- matched
267
- };
268
- } catch (error) {
269
- return {
270
- field_path: rule.field_path,
271
- operator: rule.operator,
272
- value: rule.value,
273
- actual_value: void 0,
274
- matched: false,
275
- error: error instanceof Error ? error.message : String(error)
276
- };
277
- }
278
- }
279
- function evaluateRulesAND(entity, rules) {
280
- if (!rules || rules.length === 0) return true;
281
- return rules.every((rule) => evaluateRule(entity, rule));
282
- }
283
- function evaluateRulesOR(entity, rules) {
284
- if (!rules || rules.length === 0) return true;
285
- return rules.some((rule) => evaluateRule(entity, rule));
286
- }
287
- function evaluateRules(entity, rules, logic = "AND") {
288
- return logic === "AND" ? evaluateRulesAND(entity, rules) : evaluateRulesOR(entity, rules);
289
- }
290
- function evaluateRulesWithResults(entity, rules, logic = "AND") {
291
- const ruleResults = rules.map((rule) => evaluateRuleWithResult(entity, rule));
292
- const matched = logic === "AND" ? ruleResults.every((r) => r.matched) : ruleResults.some((r) => r.matched);
293
- return {
294
- matched,
295
- logic,
296
- rule_results: ruleResults
297
- };
298
- }
299
- function filterEntities(entities, rules, logic = "AND") {
300
- if (!entities || entities.length === 0) return [];
301
- if (!rules || rules.length === 0) return entities;
302
- return entities.filter((entity) => evaluateRules(entity, rules, logic));
303
- }
304
-
305
- // src/core/engine.ts
306
- function evaluateRule2(_entity, _rule) {
307
- throw new Error("Not implemented - BEAD: fund-your-startup-a0b8. evaluateRule must import from rule evaluator.");
308
- }
309
- var FunnelEngine = class {
310
- /**
311
- * Execute a funnel on a set of entities
312
- *
313
- * @param funnel - The funnel definition to execute
314
- * @param entities - Input entities to process
315
- * @returns ExecutionResult with matched/excluded entities and stats
316
- */
317
- execute(funnel, entities) {
318
- const startTime = Date.now();
319
- const results = entities.map((entity) => ({
320
- entity,
321
- matched: true,
322
- // Start as matched, exclude as needed
323
- accumulated_tags: [],
324
- context: {},
325
- stage_results: []
326
- }));
327
- const stageStats = {};
328
- const errors = [];
329
- const sortedStages = [...funnel.stages].sort((a, b) => a.order - b.order);
330
- for (const stage of sortedStages) {
331
- const stageStartTime = Date.now();
332
- const inputEntities = results.filter((r) => r.matched && !r.excluded_at_stage);
333
- const stats = {
334
- stage_id: stage.id,
335
- stage_name: stage.name,
336
- input_count: inputEntities.length,
337
- matched_count: 0,
338
- not_matched_count: 0,
339
- excluded_count: 0,
340
- tagged_count: 0,
341
- continued_count: 0,
342
- error_count: 0
343
- };
344
- for (const result of inputEntities) {
345
- try {
346
- const stageResult = this.processStage(stage, result.entity);
347
- result.stage_results.push(stageResult);
348
- if (stageResult.matched) {
349
- stats.matched_count++;
350
- } else {
351
- stats.not_matched_count++;
352
- }
353
- if (stageResult.tags_added && stageResult.tags_added.length > 0) {
354
- result.accumulated_tags.push(...stageResult.tags_added);
355
- stats.tagged_count++;
356
- }
357
- if (stageResult.context_added) {
358
- result.context = { ...result.context, ...stageResult.context_added };
359
- }
360
- if (stageResult.excluded) {
361
- result.matched = false;
362
- result.excluded_at_stage = stage.id;
363
- stats.excluded_count++;
364
- } else if (stageResult.continued) {
365
- stats.continued_count++;
366
- }
367
- } catch (error) {
368
- stats.error_count++;
369
- errors.push(`Stage ${stage.name}: ${error instanceof Error ? error.message : String(error)}`);
370
- }
371
- }
372
- stats.duration_ms = Date.now() - stageStartTime;
373
- stageStats[stage.id] = stats;
374
- }
375
- if (funnel.completion_tags && funnel.completion_tags.length > 0) {
376
- for (const result of results) {
377
- if (result.matched) {
378
- result.accumulated_tags.push(...funnel.completion_tags);
379
- }
380
- }
381
- }
382
- const matched = results.filter((r) => r.matched);
383
- const excluded = results.filter((r) => !r.matched);
384
- const totalTagged = results.filter((r) => r.accumulated_tags.length > 0).length;
385
- return {
386
- matched,
387
- excluded,
388
- total_input: entities.length,
389
- total_matched: matched.length,
390
- total_excluded: excluded.length,
391
- total_tagged: totalTagged,
392
- stage_stats: stageStats,
393
- duration_ms: Date.now() - startTime,
394
- errors: errors.length > 0 ? errors : void 0
395
- };
396
- }
397
- /**
398
- * Process a single entity through a stage
399
- *
400
- * @param stage - The stage to process
401
- * @param entity - The entity to evaluate
402
- * @returns StageResult with match status and actions taken
403
- */
404
- processStage(stage, entity) {
405
- const ruleResults = [];
406
- let matched = false;
407
- if (stage.custom_evaluator) {
408
- try {
409
- matched = stage.custom_evaluator(entity);
410
- } catch (error) {
411
- matched = false;
412
- }
413
- } else if (stage.rules.length === 0) {
414
- matched = true;
415
- } else {
416
- for (const rule of stage.rules) {
417
- const ruleResult = evaluateRule2();
418
- ruleResults.push(ruleResult);
419
- }
420
- if (stage.filter_logic === "AND") {
421
- matched = ruleResults.every((r) => r.matched);
422
- } else {
423
- matched = ruleResults.some((r) => r.matched);
424
- }
425
- }
426
- let action;
427
- let tagsAdded = [];
428
- let contextAdded;
429
- let excluded = false;
430
- let continued = false;
431
- if (matched) {
432
- action = stage.match_action;
433
- if (stage.match_tags && stage.match_tags.length > 0) {
434
- tagsAdded = [...stage.match_tags];
435
- }
436
- if (stage.match_context) {
437
- contextAdded = stage.match_context;
438
- }
439
- switch (stage.match_action) {
440
- case "continue":
441
- continued = true;
442
- break;
443
- case "tag":
444
- excluded = true;
445
- break;
446
- case "tag_continue":
447
- continued = true;
448
- break;
449
- case "output":
450
- continued = false;
451
- break;
452
- }
453
- } else {
454
- action = stage.no_match_action;
455
- if (stage.no_match_tags && stage.no_match_tags.length > 0) {
456
- tagsAdded = [...stage.no_match_tags];
457
- }
458
- switch (stage.no_match_action) {
459
- case "continue":
460
- continued = true;
461
- break;
462
- case "exclude":
463
- excluded = true;
464
- break;
465
- case "tag_exclude":
466
- excluded = true;
467
- break;
468
- }
469
- }
470
- return {
471
- stage_id: stage.id,
472
- stage_name: stage.name,
473
- matched,
474
- rule_results: ruleResults.length > 0 ? ruleResults : void 0,
475
- action,
476
- tags_added: tagsAdded.length > 0 ? tagsAdded : void 0,
477
- context_added: contextAdded,
478
- excluded,
479
- continued
480
- };
481
- }
482
- };
483
-
484
- export { FunnelEngine, applyOperator, evaluateRule, evaluateRuleWithResult, evaluateRules, evaluateRulesAND, evaluateRulesOR, evaluateRulesWithResults, filterEntities, getFields, hasField, resolveField, setField };
485
- //# sourceMappingURL=index.js.map
486
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/core/field-resolver.ts","../../src/core/operators.ts","../../src/core/evaluator.ts","../../src/core/engine.ts"],"names":["evaluateRule"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @startsimpli/funnels - Field Resolver Utility\n *\n * Safely resolves and sets field values using dot-notation paths on arbitrary objects.\n * Used by rule evaluator and execution engine to access entity properties.\n *\n * Examples:\n * - resolveField({name: 'John'}, 'name') → 'John'\n * - resolveField({firm: {stage: 'Seed'}}, 'firm.stage') → 'Seed'\n * - resolveField({tags: ['a', 'b']}, 'tags[0]') → 'a'\n * - resolveField({investors: [{email: 'a@b.com'}]}, 'investors[0].email') → 'a@b.com'\n */\n\n/** Segments that would allow prototype pollution. */\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/**\n * Parse a field path into segments\n *\n * Examples:\n * - 'name' → ['name']\n * - 'firm.stage' → ['firm', 'stage']\n * - 'tags[0]' → ['tags', '0']\n * - 'investors[2].email' → ['investors', '2', 'email']\n * - 'company.address.city.zipCode' → ['company', 'address', 'city', 'zipCode']\n */\nfunction parseFieldPath(fieldPath: string): string[] {\n const segments: string[] = [];\n let current = '';\n let inBracket = false;\n\n for (let i = 0; i < fieldPath.length; i++) {\n const char = fieldPath[i];\n\n if (char === '[') {\n if (current) {\n segments.push(current);\n current = '';\n }\n inBracket = true;\n } else if (char === ']') {\n if (current) {\n segments.push(current);\n current = '';\n }\n inBracket = false;\n } else if (char === '.' && !inBracket) {\n if (current) {\n segments.push(current);\n current = '';\n }\n } else {\n current += char;\n }\n }\n\n if (current) {\n segments.push(current);\n }\n\n return segments;\n}\n\n/**\n * Resolve a field value from an entity using dot-notation path\n *\n * Features:\n * - Dot notation: 'firm.name', 'recipe.ingredients.name'\n * - Array access: 'tags[0]', 'investors[2].email'\n * - Nested paths: 'company.address.city.zipCode'\n * - Safe navigation: Returns undefined for missing paths (no errors)\n * - Type preservation: Maintains number/boolean/date types\n *\n * @param entity - The object to resolve the field from\n * @param fieldPath - Dot-notation path to the field\n * @returns The resolved value, or undefined if path doesn't exist\n *\n * @example\n * ```typescript\n * const investor = {\n * name: 'John Doe',\n * firm: { stage: 'Seed', aum: 100000000 },\n * tags: ['active', 'qualified'],\n * metadata: { score: 85 }\n * };\n *\n * resolveField(investor, 'name'); // 'John Doe'\n * resolveField(investor, 'firm.stage'); // 'Seed'\n * resolveField(investor, 'tags[0]'); // 'active'\n * resolveField(investor, 'metadata.score'); // 85\n * resolveField(investor, 'firm.missing'); // undefined\n * ```\n */\nexport function resolveField<T>(entity: T, fieldPath: string): any {\n // Handle null/undefined entity\n if (entity == null) {\n return undefined;\n }\n\n // Handle empty path\n if (!fieldPath || fieldPath.trim() === '') {\n return undefined;\n }\n\n const segments = parseFieldPath(fieldPath);\n let current: any = entity;\n\n for (const segment of segments) {\n // Check if current is null/undefined before accessing\n if (current == null) {\n return undefined;\n }\n\n // Block prototype pollution\n if (DANGEROUS_KEYS.has(segment)) {\n return undefined;\n }\n\n // Handle array/object access\n if (typeof current === 'object' && segment in current) {\n current = current[segment];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n/**\n * Set a field value on an entity using dot-notation path\n *\n * Features:\n * - Creates intermediate objects/arrays as needed\n * - Handles array index assignment\n * - Mutates the entity in place\n * - Safe for missing intermediate paths\n *\n * @param entity - The object to set the field on\n * @param fieldPath - Dot-notation path to the field\n * @param value - The value to set\n *\n * @example\n * ```typescript\n * const investor = { name: 'John' };\n *\n * setField(investor, 'firm.stage', 'Series A');\n * // investor = { name: 'John', firm: { stage: 'Series A' } }\n *\n * setField(investor, 'tags[0]', 'qualified');\n * // investor.tags = ['qualified']\n *\n * setField(investor, 'metadata.score', 85);\n * // investor.metadata = { score: 85 }\n * ```\n */\nexport function setField<T>(entity: T, fieldPath: string, value: any): void {\n // Handle null/undefined entity\n if (entity == null) {\n throw new Error('Cannot set field on null or undefined entity');\n }\n\n // Handle empty path\n if (!fieldPath || fieldPath.trim() === '') {\n throw new Error('Field path cannot be empty');\n }\n\n const segments = parseFieldPath(fieldPath);\n let current: any = entity;\n\n // Navigate to parent of target field\n for (let i = 0; i < segments.length - 1; i++) {\n const segment = segments[i];\n const nextSegment = segments[i + 1];\n\n // Block prototype pollution\n if (DANGEROUS_KEYS.has(segment)) {\n throw new Error(`Dangerous field path segment: \"${segment}\"`);\n }\n\n // Create intermediate object or array if missing\n if (!(segment in current)) {\n // Check if next segment is numeric (array index)\n const isNextArray = /^\\d+$/.test(nextSegment);\n current[segment] = isNextArray ? [] : {};\n }\n\n current = current[segment];\n }\n\n // Set the final value\n const lastSegment = segments[segments.length - 1];\n if (DANGEROUS_KEYS.has(lastSegment)) {\n throw new Error(`Dangerous field path segment: \"${lastSegment}\"`);\n }\n current[lastSegment] = value;\n}\n\n/**\n * Check if a field path exists on an entity\n *\n * @param entity - The object to check\n * @param fieldPath - Dot-notation path to check\n * @returns true if the path exists and has a defined value\n *\n * @example\n * ```typescript\n * const investor = { name: 'John', firm: { stage: 'Seed' } };\n *\n * hasField(investor, 'name'); // true\n * hasField(investor, 'firm.stage'); // true\n * hasField(investor, 'firm.missing'); // false\n * hasField(investor, 'tags[0]'); // false\n * ```\n */\nexport function hasField<T>(entity: T, fieldPath: string): boolean {\n const value = resolveField(entity, fieldPath);\n return value !== undefined;\n}\n\n/**\n * Get multiple field values at once\n *\n * @param entity - The object to resolve fields from\n * @param fieldPaths - Array of field paths to resolve\n * @returns Object mapping field paths to their values\n *\n * @example\n * ```typescript\n * const investor = { name: 'John', firm: { stage: 'Seed' }, tags: ['active'] };\n *\n * getFields(investor, ['name', 'firm.stage', 'tags[0]']);\n * // { name: 'John', 'firm.stage': 'Seed', 'tags[0]': 'active' }\n * ```\n */\nexport function getFields<T>(\n entity: T,\n fieldPaths: string[]\n): Record<string, any> {\n const result: Record<string, any> = {};\n\n for (const path of fieldPaths) {\n result[path] = resolveField(entity, path);\n }\n\n return result;\n}\n","/**\n * @startsimpli/funnels - Operator Implementation\n *\n * BRUTALLY GENERIC operator logic that works with ANY data type.\n *\n * Each operator is a pure function: (actual, expected) => boolean\n */\n\nimport type { Operator } from '../types';\n\n/**\n * Apply an operator to compare actual vs expected values\n *\n * @param operator - The comparison operator\n * @param actual - The actual value from entity\n * @param expected - The expected value from rule\n * @returns Whether the comparison passes\n */\nexport function applyOperator(\n operator: Operator,\n actual: any,\n expected: any\n): boolean {\n switch (operator) {\n // ========================================================================\n // Equality\n // ========================================================================\n case 'eq':\n return compareValues(actual, expected, (a, e) => a === e);\n\n case 'ne':\n return compareValues(actual, expected, (a, e) => a !== e);\n\n // ========================================================================\n // Comparison (numbers, dates, strings)\n // ========================================================================\n case 'gt':\n return compareValues(actual, expected, (a, e) => a > e);\n\n case 'lt':\n return compareValues(actual, expected, (a, e) => a < e);\n\n case 'gte':\n return compareValues(actual, expected, (a, e) => a >= e);\n\n case 'lte':\n return compareValues(actual, expected, (a, e) => a <= e);\n\n // ========================================================================\n // String operations (case-insensitive)\n // ========================================================================\n case 'contains':\n if (actual == null) return false;\n if (expected == null) return false;\n return String(actual).toLowerCase().includes(String(expected).toLowerCase());\n\n case 'not_contains':\n if (actual == null) return true; // null doesn't contain anything\n if (expected == null) return true;\n return !String(actual).toLowerCase().includes(String(expected).toLowerCase());\n\n case 'startswith':\n if (actual == null) return false;\n if (expected == null) return false;\n return String(actual).toLowerCase().startsWith(String(expected).toLowerCase());\n\n case 'endswith':\n if (actual == null) return false;\n if (expected == null) return false;\n return String(actual).toLowerCase().endsWith(String(expected).toLowerCase());\n\n case 'matches':\n if (actual == null) return false;\n if (expected == null) return false;\n try {\n const regex = new RegExp(String(expected));\n return regex.test(String(actual));\n } catch {\n // Invalid regex pattern\n return false;\n }\n\n // ========================================================================\n // Array/Set operations\n // ========================================================================\n case 'in':\n // Value is in array: actual IN expected[]\n if (!Array.isArray(expected)) return false;\n return expected.some(item => compareValues(actual, item, (a, e) => a === e));\n\n case 'not_in':\n // Value is not in array: actual NOT IN expected[]\n if (!Array.isArray(expected)) return true;\n return !expected.some(item => compareValues(actual, item, (a, e) => a === e));\n\n case 'has_any':\n // Array has any of values: actual[] HAS ANY expected[]\n if (!Array.isArray(actual)) return false;\n if (!Array.isArray(expected)) return false;\n return expected.some(expectedItem =>\n actual.some(actualItem => compareValues(actualItem, expectedItem, (a, e) => a === e))\n );\n\n case 'has_all':\n // Array has all values: actual[] HAS ALL expected[]\n if (!Array.isArray(actual)) return false;\n if (!Array.isArray(expected)) return false;\n return expected.every(expectedItem =>\n actual.some(actualItem => compareValues(actualItem, expectedItem, (a, e) => a === e))\n );\n\n // ========================================================================\n // Null checks\n // ========================================================================\n case 'isnull':\n return actual === null || actual === undefined;\n\n case 'isnotnull':\n return actual !== null && actual !== undefined;\n\n // ========================================================================\n // Tag operations (tags are arrays of strings)\n // ========================================================================\n case 'has_tag':\n if (!Array.isArray(actual)) return false;\n if (expected == null) return false;\n // Case-insensitive tag match\n const expectedTag = String(expected).toLowerCase();\n return actual.some(tag => String(tag).toLowerCase() === expectedTag);\n\n case 'not_has_tag':\n if (!Array.isArray(actual)) return true; // No tags = doesn't have tag\n if (expected == null) return true;\n const expectedTagNot = String(expected).toLowerCase();\n return !actual.some(tag => String(tag).toLowerCase() === expectedTagNot);\n\n // ========================================================================\n // Boolean\n // ========================================================================\n case 'is_true':\n return actual === true;\n\n case 'is_false':\n return actual === false;\n\n default:\n throw new Error(`Unknown operator: ${operator}`);\n }\n}\n\n/**\n * Compare values with type normalization\n *\n * Handles:\n * - Numbers: direct comparison\n * - Dates: convert to timestamps\n * - Strings: direct comparison (operators handle case-sensitivity)\n * - null/undefined: handle gracefully\n */\nfunction compareValues(\n actual: any,\n expected: any,\n compare: (a: any, e: any) => boolean\n): boolean {\n // Handle null/undefined\n if (actual === null || actual === undefined) {\n return expected === null || expected === undefined;\n }\n if (expected === null || expected === undefined) {\n return false;\n }\n\n // Both are dates - convert to timestamps\n if (isDate(actual) && isDate(expected)) {\n const actualTs = toTimestamp(actual);\n const expectedTs = toTimestamp(expected);\n if (actualTs === null || expectedTs === null) return false;\n return compare(actualTs, expectedTs);\n }\n\n // One is date - try to parse the other\n if (isDate(actual) || isDate(expected)) {\n const actualTs = toTimestamp(actual);\n const expectedTs = toTimestamp(expected);\n if (actualTs === null || expectedTs === null) return false;\n return compare(actualTs, expectedTs);\n }\n\n // Both are numbers\n if (typeof actual === 'number' && typeof expected === 'number') {\n return compare(actual, expected);\n }\n\n // Try to convert to numbers if they look numeric\n if (isNumeric(actual) && isNumeric(expected)) {\n return compare(Number(actual), Number(expected));\n }\n\n // Default: direct comparison\n return compare(actual, expected);\n}\n\n/**\n * Check if value is a Date object or date string\n */\nfunction isDate(value: any): boolean {\n if (value instanceof Date) return true;\n if (typeof value !== 'string') return false;\n\n // Check if string looks like a date\n const datePattern = /^\\d{4}-\\d{2}-\\d{2}|^\\d{1,2}\\/\\d{1,2}\\/\\d{4}/;\n return datePattern.test(value);\n}\n\n/**\n * Convert value to timestamp (milliseconds since epoch)\n * Returns null if conversion fails\n */\nfunction toTimestamp(value: any): number | null {\n if (value instanceof Date) {\n const ts = value.getTime();\n return isNaN(ts) ? null : ts;\n }\n\n if (typeof value === 'string' || typeof value === 'number') {\n const date = new Date(value);\n const ts = date.getTime();\n return isNaN(ts) ? null : ts;\n }\n\n return null;\n}\n\n/**\n * Check if value is numeric (number or numeric string)\n */\nfunction isNumeric(value: any): boolean {\n if (typeof value === 'number') return !isNaN(value);\n if (typeof value !== 'string') return false;\n return !isNaN(Number(value)) && value.trim() !== '';\n}\n","/**\n * @startsimpli/funnels - Rule Evaluator\n *\n * BRUTALLY GENERIC rule evaluation engine.\n *\n * Evaluates FilterRule against ANY entity type by:\n * 1. Resolving field value from entity\n * 2. Applying operator comparison\n * 3. Handling negate flag\n * 4. Returning boolean result\n */\n\nimport type { FilterRule, RuleResult } from '../types';\nimport { resolveField } from './field-resolver';\nimport { applyOperator } from './operators';\n\n/**\n * Evaluate a single filter rule against an entity\n *\n * @param entity - The entity to evaluate\n * @param rule - The filter rule to apply\n * @returns Whether the rule matches\n *\n * @example\n * ```typescript\n * const investor = { name: 'John', firm: { stage: 'Series A' } };\n *\n * evaluateRule(investor, {\n * field_path: 'firm.stage',\n * operator: 'eq',\n * value: 'Series A'\n * }); // true\n *\n * evaluateRule(investor, {\n * field_path: 'firm.stage',\n * operator: 'eq',\n * value: 'Seed',\n * negate: true\n * }); // true (negated: stage !== 'Seed')\n * ```\n */\nexport function evaluateRule<T>(entity: T, rule: FilterRule): boolean {\n // Resolve field value from entity\n const actualValue = resolveField(entity, rule.field_path);\n\n // Apply operator\n const result = applyOperator(rule.operator, actualValue, rule.value);\n\n // Handle negate flag\n return rule.negate ? !result : result;\n}\n\n/**\n * Evaluate a rule and return detailed result with diagnostics\n *\n * @param entity - The entity to evaluate\n * @param rule - The filter rule to apply\n * @returns Detailed rule result with actual value and match status\n *\n * @example\n * ```typescript\n * const result = evaluateRuleWithResult(investor, {\n * field_path: 'firm.stage',\n * operator: 'eq',\n * value: 'Series A'\n * });\n * // {\n * // field_path: 'firm.stage',\n * // operator: 'eq',\n * // value: 'Series A',\n * // actual_value: 'Series A',\n * // matched: true\n * // }\n * ```\n */\nexport function evaluateRuleWithResult<T>(\n entity: T,\n rule: FilterRule\n): RuleResult {\n try {\n // Resolve field value\n const actualValue = resolveField(entity, rule.field_path);\n\n // Apply operator\n const operatorResult = applyOperator(rule.operator, actualValue, rule.value);\n\n // Handle negate flag\n const matched = rule.negate ? !operatorResult : operatorResult;\n\n return {\n field_path: rule.field_path,\n operator: rule.operator,\n value: rule.value,\n actual_value: actualValue,\n matched,\n };\n } catch (error) {\n // Handle evaluation errors gracefully\n return {\n field_path: rule.field_path,\n operator: rule.operator,\n value: rule.value,\n actual_value: undefined,\n matched: false,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Evaluate multiple rules with AND logic\n *\n * @param entity - The entity to evaluate\n * @param rules - Array of filter rules\n * @returns Whether ALL rules match\n *\n * @example\n * ```typescript\n * const investor = { name: 'John', firm: { stage: 'Series A', aum: 100000000 } };\n *\n * evaluateRulesAND(investor, [\n * { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },\n * { field_path: 'firm.aum', operator: 'gte', value: 50000000 }\n * ]); // true (both rules match)\n * ```\n */\nexport function evaluateRulesAND<T>(entity: T, rules: FilterRule[]): boolean {\n if (!rules || rules.length === 0) return true; // Empty rules = match\n\n return rules.every(rule => evaluateRule(entity, rule));\n}\n\n/**\n * Evaluate multiple rules with OR logic\n *\n * @param entity - The entity to evaluate\n * @param rules - Array of filter rules\n * @returns Whether AT LEAST ONE rule matches\n *\n * @example\n * ```typescript\n * const investor = { name: 'John', firm: { stage: 'Seed' } };\n *\n * evaluateRulesOR(investor, [\n * { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },\n * { field_path: 'firm.stage', operator: 'eq', value: 'Seed' }\n * ]); // true (second rule matches)\n * ```\n */\nexport function evaluateRulesOR<T>(entity: T, rules: FilterRule[]): boolean {\n if (!rules || rules.length === 0) return true; // Empty rules = match\n\n return rules.some(rule => evaluateRule(entity, rule));\n}\n\n/**\n * Evaluate multiple rules with specified logic (AND/OR)\n *\n * @param entity - The entity to evaluate\n * @param rules - Array of filter rules\n * @param logic - Combination logic ('AND' or 'OR')\n * @returns Whether rules match based on logic\n *\n * @example\n * ```typescript\n * evaluateRules(investor, rules, 'AND'); // All must match\n * evaluateRules(investor, rules, 'OR'); // At least one must match\n * ```\n */\nexport function evaluateRules<T>(\n entity: T,\n rules: FilterRule[],\n logic: 'AND' | 'OR' = 'AND'\n): boolean {\n return logic === 'AND'\n ? evaluateRulesAND(entity, rules)\n : evaluateRulesOR(entity, rules);\n}\n\n/**\n * Evaluate rules and return detailed results for each rule\n *\n * @param entity - The entity to evaluate\n * @param rules - Array of filter rules\n * @param logic - Combination logic ('AND' or 'OR')\n * @returns Object with overall match status and per-rule results\n *\n * @example\n * ```typescript\n * const result = evaluateRulesWithResults(investor, [\n * { field_path: 'firm.stage', operator: 'eq', value: 'Series A' },\n * { field_path: 'firm.aum', operator: 'gte', value: 50000000 }\n * ], 'AND');\n * // {\n * // matched: true,\n * // logic: 'AND',\n * // rule_results: [\n * // { field_path: 'firm.stage', operator: 'eq', value: 'Series A', actual_value: 'Series A', matched: true },\n * // { field_path: 'firm.aum', operator: 'gte', value: 50000000, actual_value: 100000000, matched: true }\n * // ]\n * // }\n * ```\n */\nexport function evaluateRulesWithResults<T>(\n entity: T,\n rules: FilterRule[],\n logic: 'AND' | 'OR' = 'AND'\n): {\n matched: boolean;\n logic: 'AND' | 'OR';\n rule_results: RuleResult[];\n} {\n const ruleResults = rules.map(rule => evaluateRuleWithResult(entity, rule));\n\n const matched =\n logic === 'AND'\n ? ruleResults.every(r => r.matched)\n : ruleResults.some(r => r.matched);\n\n return {\n matched,\n logic,\n rule_results: ruleResults,\n };\n}\n\n/**\n * Filter an array of entities using rules\n *\n * @param entities - Array of entities to filter\n * @param rules - Filter rules to apply\n * @param logic - Combination logic ('AND' or 'OR')\n * @returns Entities that match the rules\n *\n * @example\n * ```typescript\n * const investors = [\n * { name: 'John', firm: { stage: 'Seed' } },\n * { name: 'Jane', firm: { stage: 'Series A' } },\n * { name: 'Bob', firm: { stage: 'Series B' } }\n * ];\n *\n * filterEntities(investors, [\n * { field_path: 'firm.stage', operator: 'in', value: ['Series A', 'Series B'] }\n * ]);\n * // [\n * // { name: 'Jane', firm: { stage: 'Series A' } },\n * // { name: 'Bob', firm: { stage: 'Series B' } }\n * // ]\n * ```\n */\nexport function filterEntities<T>(\n entities: T[],\n rules: FilterRule[],\n logic: 'AND' | 'OR' = 'AND'\n): T[] {\n if (!entities || entities.length === 0) return [];\n if (!rules || rules.length === 0) return entities; // No rules = return all\n\n return entities.filter(entity => evaluateRules(entity, rules, logic));\n}\n","/**\n * @startsimpli/funnels - Core Funnel Execution Engine\n *\n * BRUTALLY GENERIC - Works for ANY entity type:\n * - Investors, firms, contacts, organizations\n * - Recipes, ingredients, users\n * - Leads, tasks, projects\n * - GitHub repos, pull requests, issues\n *\n * The engine processes entities through stages sequentially,\n * evaluating rules and applying actions based on match results.\n */\n\nimport type {\n Funnel,\n FunnelStage,\n FilterRule,\n MatchAction,\n NoMatchAction,\n FunnelResult,\n StageResult,\n RuleResult,\n StageStats,\n} from '../types';\n\n// ============================================================================\n// Execution Result Types\n// ============================================================================\n\n/**\n * Complete result of funnel execution\n */\nexport interface ExecutionResult<T = any> {\n /** Entities that matched and are in output */\n matched: FunnelResult<T>[];\n\n /** Entities that were excluded */\n excluded: FunnelResult<T>[];\n\n /** Total entities processed */\n total_input: number;\n\n /** Total entities that matched */\n total_matched: number;\n\n /** Total entities excluded */\n total_excluded: number;\n\n /** Total entities tagged */\n total_tagged: number;\n\n /** Per-stage statistics */\n stage_stats: Record<string, StageStats>;\n\n /** Execution duration in milliseconds */\n duration_ms: number;\n\n /** Errors encountered (non-fatal) */\n errors?: string[];\n}\n\n// ============================================================================\n// BEAD: fund-your-startup-a0b8 - Funnel engine evaluateRule is a non-functional stub\n// evaluateRule always returns error='Evaluator not implemented yet' — funnels cannot match anything\n// resolveField should import from /packages/funnels/src/core/field-resolver.ts\n// ============================================================================\n\nfunction evaluateRule<T>(_entity: T, _rule: FilterRule): RuleResult {\n throw new Error('Not implemented - BEAD: fund-your-startup-a0b8. evaluateRule must import from rule evaluator.');\n // return {\n // field_path: _rule.field_path,\n // operator: _rule.operator,\n // value: _rule.value,\n // actual_value: undefined,\n // matched: false,\n // error: 'Evaluator not implemented yet',\n // };\n}\n\nfunction resolveField<T>(_entity: T, _fieldPath: string): any {\n throw new Error('Not implemented - BEAD: fund-your-startup-a0b8. resolveField must import from field-resolver.ts.');\n // const keys = _fieldPath.split('.');\n // let value: any = _entity;\n // for (const key of keys) {\n // if (value === null || value === undefined) return undefined;\n // value = value[key];\n // }\n // return value;\n}\n\n// ============================================================================\n// Funnel Engine\n// ============================================================================\n\n/**\n * Core funnel execution engine\n *\n * Processes entities through stages sequentially, evaluating rules\n * and applying actions based on match results.\n *\n * Generic over entity type T - works with ANY data structure.\n */\nexport class FunnelEngine<T = any> {\n /**\n * Execute a funnel on a set of entities\n *\n * @param funnel - The funnel definition to execute\n * @param entities - Input entities to process\n * @returns ExecutionResult with matched/excluded entities and stats\n */\n execute(funnel: Funnel<T>, entities: T[]): ExecutionResult<T> {\n const startTime = Date.now();\n\n // Initialize results\n const results: FunnelResult<T>[] = entities.map(entity => ({\n entity,\n matched: true, // Start as matched, exclude as needed\n accumulated_tags: [],\n context: {},\n stage_results: [],\n }));\n\n const stageStats: Record<string, StageStats> = {};\n const errors: string[] = [];\n\n // Sort stages by order\n const sortedStages = [...funnel.stages].sort((a, b) => a.order - b.order);\n\n // Process each stage sequentially\n for (const stage of sortedStages) {\n const stageStartTime = Date.now();\n\n // Track entities entering this stage\n const inputEntities = results.filter(r => r.matched && !r.excluded_at_stage);\n\n const stats: StageStats = {\n stage_id: stage.id,\n stage_name: stage.name,\n input_count: inputEntities.length,\n matched_count: 0,\n not_matched_count: 0,\n excluded_count: 0,\n tagged_count: 0,\n continued_count: 0,\n error_count: 0,\n };\n\n // Process each entity through this stage\n for (const result of inputEntities) {\n try {\n const stageResult = this.processStage(stage, result.entity);\n\n // Update entity result\n result.stage_results!.push(stageResult);\n\n // Apply stage result to entity\n if (stageResult.matched) {\n stats.matched_count++;\n } else {\n stats.not_matched_count++;\n }\n\n if (stageResult.tags_added && stageResult.tags_added.length > 0) {\n result.accumulated_tags.push(...stageResult.tags_added);\n stats.tagged_count++;\n }\n\n if (stageResult.context_added) {\n result.context = { ...result.context, ...stageResult.context_added };\n }\n\n if (stageResult.excluded) {\n result.matched = false;\n result.excluded_at_stage = stage.id;\n stats.excluded_count++;\n } else if (stageResult.continued) {\n stats.continued_count++;\n }\n } catch (error) {\n stats.error_count!++;\n errors.push(`Stage ${stage.name}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n stats.duration_ms = Date.now() - stageStartTime;\n stageStats[stage.id] = stats;\n }\n\n // Apply completion tags to matched entities\n if (funnel.completion_tags && funnel.completion_tags.length > 0) {\n for (const result of results) {\n if (result.matched) {\n result.accumulated_tags.push(...funnel.completion_tags);\n }\n }\n }\n\n // Separate matched and excluded\n const matched = results.filter(r => r.matched);\n const excluded = results.filter(r => !r.matched);\n\n // Count unique tagged entities\n const totalTagged = results.filter(r => r.accumulated_tags.length > 0).length;\n\n return {\n matched,\n excluded,\n total_input: entities.length,\n total_matched: matched.length,\n total_excluded: excluded.length,\n total_tagged: totalTagged,\n stage_stats: stageStats,\n duration_ms: Date.now() - startTime,\n errors: errors.length > 0 ? errors : undefined,\n };\n }\n\n /**\n * Process a single entity through a stage\n *\n * @param stage - The stage to process\n * @param entity - The entity to evaluate\n * @returns StageResult with match status and actions taken\n */\n private processStage(stage: FunnelStage<T>, entity: T): StageResult {\n // Evaluate all rules\n const ruleResults: RuleResult[] = [];\n let matched = false;\n\n if (stage.custom_evaluator) {\n // Use custom evaluator if provided (highest priority)\n try {\n matched = stage.custom_evaluator(entity);\n } catch (error) {\n matched = false;\n }\n } else if (stage.rules.length === 0) {\n // No rules and no custom evaluator = always match\n matched = true;\n } else {\n // Evaluate rules with filter logic\n for (const rule of stage.rules) {\n const ruleResult = evaluateRule(entity, rule);\n ruleResults.push(ruleResult);\n }\n\n // Apply filter logic (AND/OR)\n if (stage.filter_logic === 'AND') {\n // All rules must match\n matched = ruleResults.every(r => r.matched);\n } else {\n // At least one rule must match\n matched = ruleResults.some(r => r.matched);\n }\n }\n\n // Determine action based on match result\n let action: MatchAction | NoMatchAction;\n let tagsAdded: string[] = [];\n let contextAdded: Record<string, any> | undefined;\n let excluded = false;\n let continued = false;\n\n if (matched) {\n action = stage.match_action;\n\n // Add match tags\n if (stage.match_tags && stage.match_tags.length > 0) {\n tagsAdded = [...stage.match_tags];\n }\n\n // Add match context\n if (stage.match_context) {\n contextAdded = stage.match_context;\n }\n\n // Apply match action\n switch (stage.match_action) {\n case 'continue':\n continued = true;\n break;\n case 'tag':\n // Tags added, stop processing\n excluded = true;\n break;\n case 'tag_continue':\n // Tags added, continue processing\n continued = true;\n break;\n case 'output':\n // Add to output, stop processing (but not excluded)\n continued = false;\n break;\n }\n } else {\n action = stage.no_match_action;\n\n // Add no-match tags\n if (stage.no_match_tags && stage.no_match_tags.length > 0) {\n tagsAdded = [...stage.no_match_tags];\n }\n\n // Apply no-match action\n switch (stage.no_match_action) {\n case 'continue':\n continued = true;\n break;\n case 'exclude':\n excluded = true;\n break;\n case 'tag_exclude':\n excluded = true;\n break;\n }\n }\n\n return {\n stage_id: stage.id,\n stage_name: stage.name,\n matched,\n rule_results: ruleResults.length > 0 ? ruleResults : undefined,\n action,\n tags_added: tagsAdded.length > 0 ? tagsAdded : undefined,\n context_added: contextAdded,\n excluded,\n continued,\n };\n }\n}\n"]}
@@ -1,21 +0,0 @@
1
- 'use strict';
2
-
3
- var react = require('react');
4
-
5
- // src/hooks/useDebouncedValue.ts
6
- function useDebouncedValue(value, delay = 300) {
7
- const [debouncedValue, setDebouncedValue] = react.useState(value);
8
- react.useEffect(() => {
9
- const handler = setTimeout(() => {
10
- setDebouncedValue(value);
11
- }, delay);
12
- return () => {
13
- clearTimeout(handler);
14
- };
15
- }, [value, delay]);
16
- return debouncedValue;
17
- }
18
-
19
- exports.useDebouncedValue = useDebouncedValue;
20
- //# sourceMappingURL=index.cjs.map
21
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/hooks/useDebouncedValue.ts"],"names":["useState","useEffect"],"mappings":";;;;;AAWO,SAAS,iBAAA,CAAqB,KAAA,EAAU,KAAA,GAAgB,GAAA,EAAQ;AACrE,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIA,eAAY,KAAK,CAAA;AAE7D,EAAAC,eAAA,CAAU,MAAM;AAEd,IAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,MAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA,IACzB,GAAG,KAAK,CAAA;AAGR,IAAA,OAAO,MAAM;AACX,MAAA,YAAA,CAAa,OAAO,CAAA;AAAA,IACtB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,KAAK,CAAC,CAAA;AAEjB,EAAA,OAAO,cAAA;AACT","file":"index.cjs","sourcesContent":["/**\n * useDebouncedValue Hook\n *\n * Returns a debounced version of the input value that only updates\n * after the specified delay has elapsed without changes.\n *\n * Use for expensive computations that shouldn't run on every keystroke.\n */\n\nimport { useEffect, useState } from 'react';\n\nexport function useDebouncedValue<T>(value: T, delay: number = 300): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n // Set timeout to update debounced value after delay\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n // Cleanup: cancel timeout if value changes before delay elapses\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n"]}
@@ -1,11 +0,0 @@
1
- /**
2
- * useDebouncedValue Hook
3
- *
4
- * Returns a debounced version of the input value that only updates
5
- * after the specified delay has elapsed without changes.
6
- *
7
- * Use for expensive computations that shouldn't run on every keystroke.
8
- */
9
- declare function useDebouncedValue<T>(value: T, delay?: number): T;
10
-
11
- export { useDebouncedValue };
@@ -1,11 +0,0 @@
1
- /**
2
- * useDebouncedValue Hook
3
- *
4
- * Returns a debounced version of the input value that only updates
5
- * after the specified delay has elapsed without changes.
6
- *
7
- * Use for expensive computations that shouldn't run on every keystroke.
8
- */
9
- declare function useDebouncedValue<T>(value: T, delay?: number): T;
10
-
11
- export { useDebouncedValue };