kanmi-perf-revenue 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +347 -173
  2. package/dist/empirical/ab-testing.d.ts +83 -0
  3. package/dist/empirical/ab-testing.d.ts.map +1 -0
  4. package/dist/empirical/ab-testing.js +281 -0
  5. package/dist/empirical/ab-testing.js.map +1 -0
  6. package/dist/empirical/alerting.d.ts +85 -0
  7. package/dist/empirical/alerting.d.ts.map +1 -0
  8. package/dist/empirical/alerting.js +358 -0
  9. package/dist/empirical/alerting.js.map +1 -0
  10. package/dist/empirical/attribution.d.ts +80 -0
  11. package/dist/empirical/attribution.d.ts.map +1 -0
  12. package/dist/empirical/attribution.js +305 -0
  13. package/dist/empirical/attribution.js.map +1 -0
  14. package/dist/empirical/cohort.d.ts +75 -0
  15. package/dist/empirical/cohort.d.ts.map +1 -0
  16. package/dist/empirical/cohort.js +305 -0
  17. package/dist/empirical/cohort.js.map +1 -0
  18. package/dist/empirical/conversion-curve.d.ts +7 -10
  19. package/dist/empirical/conversion-curve.d.ts.map +1 -1
  20. package/dist/empirical/conversion-curve.js +37 -4
  21. package/dist/empirical/conversion-curve.js.map +1 -1
  22. package/dist/empirical/correlation.d.ts +91 -0
  23. package/dist/empirical/correlation.d.ts.map +1 -0
  24. package/dist/empirical/correlation.js +461 -0
  25. package/dist/empirical/correlation.js.map +1 -0
  26. package/dist/empirical/data-import.d.ts +22 -0
  27. package/dist/empirical/data-import.d.ts.map +1 -1
  28. package/dist/empirical/data-import.js +44 -0
  29. package/dist/empirical/data-import.js.map +1 -1
  30. package/dist/empirical/datadog-product-analytics.d.ts +192 -0
  31. package/dist/empirical/datadog-product-analytics.d.ts.map +1 -0
  32. package/dist/empirical/datadog-product-analytics.js +632 -0
  33. package/dist/empirical/datadog-product-analytics.js.map +1 -0
  34. package/dist/empirical/datadog-session-query.d.ts +32 -0
  35. package/dist/empirical/datadog-session-query.d.ts.map +1 -1
  36. package/dist/empirical/datadog-session-query.js +238 -17
  37. package/dist/empirical/datadog-session-query.js.map +1 -1
  38. package/dist/empirical/engagement-analysis.d.ts +112 -0
  39. package/dist/empirical/engagement-analysis.d.ts.map +1 -0
  40. package/dist/empirical/engagement-analysis.js +354 -0
  41. package/dist/empirical/engagement-analysis.js.map +1 -0
  42. package/dist/empirical/export.d.ts +75 -0
  43. package/dist/empirical/export.d.ts.map +1 -0
  44. package/dist/empirical/export.js +392 -0
  45. package/dist/empirical/export.js.map +1 -0
  46. package/dist/empirical/forecasting.d.ts +80 -0
  47. package/dist/empirical/forecasting.d.ts.map +1 -0
  48. package/dist/empirical/forecasting.js +287 -0
  49. package/dist/empirical/forecasting.js.map +1 -0
  50. package/dist/empirical/funnel.d.ts +66 -0
  51. package/dist/empirical/funnel.d.ts.map +1 -0
  52. package/dist/empirical/funnel.js +293 -0
  53. package/dist/empirical/funnel.js.map +1 -0
  54. package/dist/empirical/history.d.ts +198 -0
  55. package/dist/empirical/history.d.ts.map +1 -0
  56. package/dist/empirical/history.js +396 -0
  57. package/dist/empirical/history.js.map +1 -0
  58. package/dist/empirical/index.d.ts +41 -16
  59. package/dist/empirical/index.d.ts.map +1 -1
  60. package/dist/empirical/index.js +96 -13
  61. package/dist/empirical/index.js.map +1 -1
  62. package/dist/empirical/interactions.d.ts +89 -0
  63. package/dist/empirical/interactions.d.ts.map +1 -0
  64. package/dist/empirical/interactions.js +346 -0
  65. package/dist/empirical/interactions.js.map +1 -0
  66. package/dist/empirical/opportunity-calculator.d.ts +6 -18
  67. package/dist/empirical/opportunity-calculator.d.ts.map +1 -1
  68. package/dist/empirical/opportunity-calculator.js +19 -1
  69. package/dist/empirical/opportunity-calculator.js.map +1 -1
  70. package/dist/empirical/report.d.ts +3 -11
  71. package/dist/empirical/report.d.ts.map +1 -1
  72. package/dist/empirical/report.js +11 -7
  73. package/dist/empirical/report.js.map +1 -1
  74. package/dist/empirical/roi-calculator.d.ts +104 -0
  75. package/dist/empirical/roi-calculator.d.ts.map +1 -0
  76. package/dist/empirical/roi-calculator.js +403 -0
  77. package/dist/empirical/roi-calculator.js.map +1 -0
  78. package/dist/empirical/seasonality.d.ts +80 -0
  79. package/dist/empirical/seasonality.d.ts.map +1 -0
  80. package/dist/empirical/seasonality.js +340 -0
  81. package/dist/empirical/seasonality.js.map +1 -0
  82. package/dist/empirical/segmentation.d.ts +135 -0
  83. package/dist/empirical/segmentation.d.ts.map +1 -0
  84. package/dist/empirical/segmentation.js +379 -0
  85. package/dist/empirical/segmentation.js.map +1 -0
  86. package/dist/empirical/statistics.d.ts +118 -0
  87. package/dist/empirical/statistics.d.ts.map +1 -0
  88. package/dist/empirical/statistics.js +344 -0
  89. package/dist/empirical/statistics.js.map +1 -0
  90. package/dist/empirical/sweet-spot.d.ts +81 -0
  91. package/dist/empirical/sweet-spot.d.ts.map +1 -0
  92. package/dist/empirical/sweet-spot.js +198 -0
  93. package/dist/empirical/sweet-spot.js.map +1 -0
  94. package/package.json +2 -2
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Prediction & Forecasting
3
+ *
4
+ * Project future revenue based on planned performance improvements.
5
+ * Uses empirical data and improvement scenarios.
6
+ *
7
+ * @author Kanmi Obasa <i@kanmiobasa.com>
8
+ */
9
+ // =============================================================================
10
+ // CORE FORECASTING
11
+ // =============================================================================
12
+ const MONTH_NAMES = [
13
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
14
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
15
+ ];
16
+ /**
17
+ * Calculate baseline metrics from sessions.
18
+ */
19
+ function calculateBaseline(sessions) {
20
+ const conversions = sessions.filter(s => s.has_purchase);
21
+ const cvr = sessions.length > 0 ? conversions.length / sessions.length : 0;
22
+ const totalRevenue = conversions.reduce((sum, s) => sum + s.purchase_value, 0);
23
+ const aov = conversions.length > 0 ? totalRevenue / conversions.length : 100;
24
+ // Assume data is ~2 weeks, project to monthly
25
+ const multiplier = 2;
26
+ return {
27
+ monthlyRevenue: totalRevenue * multiplier,
28
+ monthlySessions: sessions.length * multiplier,
29
+ cvr,
30
+ aov,
31
+ };
32
+ }
33
+ /**
34
+ * Estimate CVR lift from improvement scenario.
35
+ */
36
+ function estimateLift(scenario, sessions, curve) {
37
+ // Use curve data if available
38
+ if (curve && curve.buckets.length > 0) {
39
+ // Find sessions that would be improved
40
+ const getValue = (s) => {
41
+ switch (scenario.metric) {
42
+ case 'lcp': return s.lcp_ms;
43
+ case 'inp': return s.inp_ms;
44
+ case 'cls': return s.cls;
45
+ case 'fcp': return s.fcp_ms;
46
+ case 'ttfb': return s.ttfb_ms;
47
+ default: return null;
48
+ }
49
+ };
50
+ const sessionsAboveTarget = sessions.filter(s => {
51
+ const value = getValue(s);
52
+ return value !== null && value > scenario.targetP75;
53
+ });
54
+ const sessionsAtTarget = sessions.filter(s => {
55
+ const value = getValue(s);
56
+ return value !== null && value <= scenario.targetP75;
57
+ });
58
+ if (sessionsAboveTarget.length >= 50 && sessionsAtTarget.length >= 50) {
59
+ const currentCvr = sessionsAboveTarget.filter(s => s.has_purchase).length / sessionsAboveTarget.length;
60
+ const targetCvr = sessionsAtTarget.filter(s => s.has_purchase).length / sessionsAtTarget.length;
61
+ // Only sessions above target will improve
62
+ const proportionAffected = sessionsAboveTarget.length / sessions.length;
63
+ const lift = (targetCvr - currentCvr) * proportionAffected;
64
+ return lift * scenario.confidence;
65
+ }
66
+ }
67
+ // Fallback to benchmark estimates
68
+ return estimateLiftFromBenchmarks(scenario);
69
+ }
70
+ /**
71
+ * Benchmark-based lift estimation.
72
+ */
73
+ function estimateLiftFromBenchmarks(scenario) {
74
+ // Rough estimates based on industry data
75
+ const benchmarks = {
76
+ lcp: 0.003, // ~0.3% per 100ms improvement
77
+ inp: 0.002, // ~0.2% per 50ms improvement
78
+ cls: 0.005, // ~0.5% per 0.1 improvement
79
+ fcp: 0.002,
80
+ ttfb: 0.0015,
81
+ };
82
+ const baseRate = benchmarks[scenario.metric] || 0.002;
83
+ // Assume moving from poor (4000ms LCP) to target
84
+ const estimatedDelta = {
85
+ lcp: Math.max(0, 4000 - scenario.targetP75),
86
+ inp: Math.max(0, 500 - scenario.targetP75),
87
+ cls: Math.max(0, 0.25 - scenario.targetP75),
88
+ fcp: Math.max(0, 3000 - scenario.targetP75),
89
+ ttfb: Math.max(0, 1800 - scenario.targetP75),
90
+ };
91
+ const delta = estimatedDelta[scenario.metric] || 0;
92
+ const lift = (delta / 100) * baseRate;
93
+ return lift * scenario.confidence * 0.5; // Conservative estimate
94
+ }
95
+ /**
96
+ * Generate revenue forecast for a single improvement scenario.
97
+ */
98
+ export function forecastRevenue(scenario, sessions, curve, config = {}) {
99
+ const forecastMonths = config.forecastMonths || 12;
100
+ const growthRate = config.growthRate || 0; // Monthly baseline growth
101
+ const seasonality = config.seasonalityFactors || Array(12).fill(1);
102
+ const baseline = calculateBaseline(sessions);
103
+ const cvrLift = estimateLift(scenario, sessions, curve);
104
+ const periods = [];
105
+ let cumulativeAdditional = 0;
106
+ const startMonth = new Date().getMonth();
107
+ for (let i = 0; i < forecastMonths; i++) {
108
+ const monthIndex = (startMonth + i) % 12;
109
+ const seasonFactor = seasonality[monthIndex] || 1;
110
+ // Baseline grows over time
111
+ const growthFactor = Math.pow(1 + growthRate, i);
112
+ const baselineRevenue = baseline.monthlyRevenue * growthFactor * seasonFactor;
113
+ // Additional revenue from CVR improvement
114
+ const additionalConversions = baseline.monthlySessions * growthFactor * cvrLift;
115
+ const additionalRevenue = additionalConversions * baseline.aov * seasonFactor;
116
+ cumulativeAdditional += additionalRevenue;
117
+ periods.push({
118
+ month: i + 1,
119
+ monthName: MONTH_NAMES[monthIndex],
120
+ baselineRevenue,
121
+ forecastedRevenue: baselineRevenue + additionalRevenue,
122
+ additionalRevenue,
123
+ cumulativeAdditional,
124
+ });
125
+ }
126
+ const totalAdditionalRevenue = cumulativeAdditional;
127
+ const avgMonthlyLift = totalAdditionalRevenue / forecastMonths;
128
+ const baselineAnnual = periods.reduce((sum, p) => sum + p.baselineRevenue, 0);
129
+ const revenueGrowthPercent = baselineAnnual > 0
130
+ ? (totalAdditionalRevenue / baselineAnnual) * 100
131
+ : 0;
132
+ const assumptions = [
133
+ `Base CVR: ${(baseline.cvr * 100).toFixed(2)}%`,
134
+ `Expected CVR lift: ${(cvrLift * 100).toFixed(3)}%`,
135
+ `Average Order Value: $${baseline.aov.toFixed(2)}`,
136
+ `Confidence: ${(scenario.confidence * 100).toFixed(0)}%`,
137
+ `Monthly sessions: ${baseline.monthlySessions.toLocaleString()}`,
138
+ ];
139
+ return {
140
+ scenario,
141
+ periods,
142
+ summary: {
143
+ totalAdditionalRevenue,
144
+ avgMonthlyLift,
145
+ revenueGrowthPercent,
146
+ },
147
+ assumptions,
148
+ };
149
+ }
150
+ /**
151
+ * Generate combined forecast for multiple improvements.
152
+ */
153
+ export function forecastMultipleImprovements(scenarios, sessions, curves, config = {}) {
154
+ const forecastMonths = config.forecastMonths || 12;
155
+ const synergisticBonus = config.synergisticBonus || 1.1; // 10% bonus for multiple improvements
156
+ const forecasts = [];
157
+ for (const scenario of scenarios) {
158
+ const curve = curves[scenario.metric] || null;
159
+ const forecast = forecastRevenue(scenario, sessions, curve, { forecastMonths });
160
+ forecasts.push(forecast);
161
+ }
162
+ // Combined forecast (with synergy bonus if multiple improvements)
163
+ const combinedPeriods = [];
164
+ let combinedCumulative = 0;
165
+ const synergyMultiplier = scenarios.length > 1 ? synergisticBonus : 1;
166
+ for (let i = 0; i < forecastMonths; i++) {
167
+ const baselineRevenue = forecasts[0]?.periods[i]?.baselineRevenue || 0;
168
+ // Sum additional revenue from all improvements
169
+ let totalAdditional = 0;
170
+ for (const forecast of forecasts) {
171
+ totalAdditional += forecast.periods[i]?.additionalRevenue || 0;
172
+ }
173
+ // Apply synergy bonus
174
+ totalAdditional *= synergyMultiplier;
175
+ combinedCumulative += totalAdditional;
176
+ combinedPeriods.push({
177
+ month: i + 1,
178
+ monthName: forecasts[0]?.periods[i]?.monthName || '',
179
+ baselineRevenue,
180
+ forecastedRevenue: baselineRevenue + totalAdditional,
181
+ additionalRevenue: totalAdditional,
182
+ cumulativeAdditional: combinedCumulative,
183
+ });
184
+ }
185
+ const recommendations = [];
186
+ // Find highest impact scenario
187
+ const sorted = [...forecasts].sort((a, b) => b.summary.totalAdditionalRevenue - a.summary.totalAdditionalRevenue);
188
+ if (sorted.length > 0) {
189
+ const top = sorted[0];
190
+ recommendations.push(`Prioritize ${top.scenario.name}: $${top.summary.totalAdditionalRevenue.toLocaleString()} potential annual impact`);
191
+ }
192
+ if (scenarios.length > 1) {
193
+ recommendations.push(`Combined improvements yield ${((synergyMultiplier - 1) * 100).toFixed(0)}% synergy bonus`);
194
+ }
195
+ return {
196
+ forecasts,
197
+ combined: {
198
+ periods: combinedPeriods,
199
+ totalAdditionalRevenue: combinedCumulative,
200
+ netImpact: combinedCumulative,
201
+ },
202
+ recommendations,
203
+ };
204
+ }
205
+ // =============================================================================
206
+ // REPORT GENERATION
207
+ // =============================================================================
208
+ /**
209
+ * Generate markdown report for forecast.
210
+ */
211
+ export function generateForecastMarkdown(forecast) {
212
+ const lines = [];
213
+ lines.push('## Revenue Forecast');
214
+ lines.push('');
215
+ lines.push(`### ${forecast.scenario.name}`);
216
+ lines.push('');
217
+ lines.push(`**Target**: ${forecast.scenario.metric.toUpperCase()} p75 ≤ ${forecast.scenario.targetP75}`);
218
+ lines.push('');
219
+ // Summary
220
+ lines.push('### Summary');
221
+ lines.push('');
222
+ lines.push(`| Metric | Value |`);
223
+ lines.push(`|--------|-------|`);
224
+ lines.push(`| 12-Month Additional Revenue | $${forecast.summary.totalAdditionalRevenue.toLocaleString()} |`);
225
+ lines.push(`| Average Monthly Lift | $${forecast.summary.avgMonthlyLift.toLocaleString()} |`);
226
+ lines.push(`| Revenue Growth | ${forecast.summary.revenueGrowthPercent.toFixed(2)}% |`);
227
+ lines.push('');
228
+ // Assumptions
229
+ lines.push('### Assumptions');
230
+ lines.push('');
231
+ for (const assumption of forecast.assumptions) {
232
+ lines.push(`- ${assumption}`);
233
+ }
234
+ lines.push('');
235
+ // Monthly breakdown
236
+ lines.push('### Monthly Breakdown');
237
+ lines.push('');
238
+ lines.push('| Month | Baseline | Forecasted | Additional | Cumulative |');
239
+ lines.push('|-------|----------|------------|------------|------------|');
240
+ for (const period of forecast.periods) {
241
+ lines.push(`| ${period.monthName} | $${period.baselineRevenue.toLocaleString()} | $${period.forecastedRevenue.toLocaleString()} | $${period.additionalRevenue.toLocaleString()} | $${period.cumulativeAdditional.toLocaleString()} |`);
242
+ }
243
+ lines.push('');
244
+ return lines.join('\n');
245
+ }
246
+ /**
247
+ * Generate multi-scenario comparison markdown.
248
+ */
249
+ export function generateMultiForecastMarkdown(multiForecast) {
250
+ const lines = [];
251
+ lines.push('## Multi-Scenario Revenue Forecast');
252
+ lines.push('');
253
+ // Scenario comparison
254
+ lines.push('### Scenario Comparison');
255
+ lines.push('');
256
+ lines.push('| Scenario | 12-Month Impact | Avg Monthly | Growth % |');
257
+ lines.push('|----------|-----------------|-------------|----------|');
258
+ for (const forecast of multiForecast.forecasts) {
259
+ lines.push(`| ${forecast.scenario.name} | $${forecast.summary.totalAdditionalRevenue.toLocaleString()} | $${forecast.summary.avgMonthlyLift.toLocaleString()} | ${forecast.summary.revenueGrowthPercent.toFixed(2)}% |`);
260
+ }
261
+ lines.push('');
262
+ // Combined impact
263
+ lines.push('### Combined Impact');
264
+ lines.push('');
265
+ lines.push(`**Total 12-Month Additional Revenue**: $${multiForecast.combined.totalAdditionalRevenue.toLocaleString()}`);
266
+ lines.push('');
267
+ // Recommendations
268
+ if (multiForecast.recommendations.length > 0) {
269
+ lines.push('### Recommendations');
270
+ lines.push('');
271
+ for (const rec of multiForecast.recommendations) {
272
+ lines.push(`- ${rec}`);
273
+ }
274
+ lines.push('');
275
+ }
276
+ // Combined monthly breakdown
277
+ lines.push('### Combined Monthly Breakdown');
278
+ lines.push('');
279
+ lines.push('| Month | Baseline | Combined Forecast | Combined Additional |');
280
+ lines.push('|-------|----------|-------------------|---------------------|');
281
+ for (const period of multiForecast.combined.periods) {
282
+ lines.push(`| ${period.monthName} | $${period.baselineRevenue.toLocaleString()} | $${period.forecastedRevenue.toLocaleString()} | $${period.additionalRevenue.toLocaleString()} |`);
283
+ }
284
+ lines.push('');
285
+ return lines.join('\n');
286
+ }
287
+ //# sourceMappingURL=forecasting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forecasting.js","sourceRoot":"","sources":["../../src/empirical/forecasting.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Funnel Analysis
3
+ *
4
+ * Analyzes where in the user journey performance matters most.
5
+ * Tracks progression through funnel stages and identifies
6
+ * performance-related drop-offs.
7
+ *
8
+ * @author Kanmi Obasa <i@kanmiobasa.com>
9
+ */
10
+ import type { SessionData } from './datadog-session-query.js';
11
+ export type FunnelStage = 'landing' | 'browse' | 'product' | 'cart' | 'checkout' | 'purchase';
12
+ export interface FunnelStageStats {
13
+ stage: FunnelStage;
14
+ sessions: number;
15
+ conversions: number;
16
+ dropoffRate: number;
17
+ avgLcp: number | null;
18
+ avgInp: number | null;
19
+ /** CVR difference between good vs poor performance at this stage */
20
+ performanceImpact: number | null;
21
+ }
22
+ export interface FunnelStep {
23
+ from: FunnelStage;
24
+ to: FunnelStage;
25
+ sessions: number;
26
+ progressions: number;
27
+ progressionRate: number;
28
+ avgLcp: number | null;
29
+ /** Performance-based progression rates */
30
+ goodPerfProgressionRate: number | null;
31
+ poorPerfProgressionRate: number | null;
32
+ performanceGap: number | null;
33
+ }
34
+ export interface FunnelReport {
35
+ stages: FunnelStageStats[];
36
+ steps: FunnelStep[];
37
+ overallConversionRate: number;
38
+ biggestDropoff: {
39
+ step: string;
40
+ dropoffRate: number;
41
+ performanceGap: number | null;
42
+ } | null;
43
+ performanceHotspots: Array<{
44
+ stage: FunnelStage;
45
+ impact: number;
46
+ message: string;
47
+ }>;
48
+ recommendations: string[];
49
+ }
50
+ /**
51
+ * Infer funnel stage from session data.
52
+ */
53
+ export declare function inferFunnelStage(session: SessionData): FunnelStage;
54
+ /**
55
+ * Infer deepest stage reached from conversion events.
56
+ */
57
+ export declare function inferDeepestStage(session: SessionData): FunnelStage;
58
+ /**
59
+ * Analyze funnel progression and performance impact.
60
+ */
61
+ export declare function analyzeFunnel(sessions: SessionData[]): FunnelReport;
62
+ /**
63
+ * Generate markdown report for funnel analysis.
64
+ */
65
+ export declare function generateFunnelMarkdown(report: FunnelReport): string;
66
+ //# sourceMappingURL=funnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"funnel.d.ts","sourceRoot":"","sources":["../../src/empirical/funnel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAM9D,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9F,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,oEAAoE;IACpE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,WAAW,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,0CAA0C;IAC1C,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE;QACd,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;KAC/B,GAAG,IAAI,CAAC;IACT,mBAAmB,EAAE,KAAK,CAAC;QACzB,KAAK,EAAE,WAAW,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAMD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAsClE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,CAmBnE;AAQD;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,YAAY,CAiMnE;AAMD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAwEnE"}
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Funnel Analysis
3
+ *
4
+ * Analyzes where in the user journey performance matters most.
5
+ * Tracks progression through funnel stages and identifies
6
+ * performance-related drop-offs.
7
+ *
8
+ * @author Kanmi Obasa <i@kanmiobasa.com>
9
+ */
10
+ // =============================================================================
11
+ // STAGE DETECTION
12
+ // =============================================================================
13
+ /**
14
+ * Infer funnel stage from session data.
15
+ */
16
+ export function inferFunnelStage(session) {
17
+ // Use page_type if available
18
+ const pageType = session.page_type?.toLowerCase() || '';
19
+ if (pageType.includes('checkout') || pageType.includes('payment')) {
20
+ return 'checkout';
21
+ }
22
+ if (pageType.includes('cart') || pageType.includes('basket')) {
23
+ return 'cart';
24
+ }
25
+ if (pageType.includes('product') || pageType.includes('pdp') || pageType.includes('item')) {
26
+ return 'product';
27
+ }
28
+ if (pageType.includes('category') ||
29
+ pageType.includes('listing') ||
30
+ pageType.includes('search') ||
31
+ pageType.includes('browse') ||
32
+ pageType.includes('plp')) {
33
+ return 'browse';
34
+ }
35
+ if (pageType.includes('home') || pageType.includes('landing') || pageType === '/') {
36
+ return 'landing';
37
+ }
38
+ // Infer from page views
39
+ const pageViews = session.page_views || 1;
40
+ if (session.has_purchase) {
41
+ return 'purchase';
42
+ }
43
+ if (pageViews === 1) {
44
+ return 'landing';
45
+ }
46
+ if (pageViews <= 3) {
47
+ return 'browse';
48
+ }
49
+ return 'product';
50
+ }
51
+ /**
52
+ * Infer deepest stage reached from conversion events.
53
+ */
54
+ export function inferDeepestStage(session) {
55
+ if (session.has_purchase) {
56
+ return 'purchase';
57
+ }
58
+ const events = session.conversions || [];
59
+ const eventNames = events.map((e) => e.name.toLowerCase());
60
+ if (eventNames.some((n) => n.includes('checkout') || n.includes('payment'))) {
61
+ return 'checkout';
62
+ }
63
+ if (eventNames.some((n) => n.includes('cart') || n.includes('add_to_cart'))) {
64
+ return 'cart';
65
+ }
66
+ if (eventNames.some((n) => n.includes('product_view') || n.includes('view_item'))) {
67
+ return 'product';
68
+ }
69
+ return inferFunnelStage(session);
70
+ }
71
+ // =============================================================================
72
+ // FUNNEL ANALYSIS
73
+ // =============================================================================
74
+ const STAGE_ORDER = ['landing', 'browse', 'product', 'cart', 'checkout', 'purchase'];
75
+ /**
76
+ * Analyze funnel progression and performance impact.
77
+ */
78
+ export function analyzeFunnel(sessions) {
79
+ // Group sessions by deepest stage reached
80
+ const stageData = new Map();
81
+ for (const stage of STAGE_ORDER) {
82
+ stageData.set(stage, []);
83
+ }
84
+ for (const session of sessions) {
85
+ const deepestStage = inferDeepestStage(session);
86
+ stageData.get(deepestStage).push(session);
87
+ }
88
+ // Calculate cumulative counts (users who reached at least this stage)
89
+ const cumulativeCounts = new Map();
90
+ let cumulative = sessions.length;
91
+ for (let i = 0; i < STAGE_ORDER.length; i++) {
92
+ cumulativeCounts.set(STAGE_ORDER[i], cumulative);
93
+ // Subtract those who dropped off at this stage
94
+ if (i < STAGE_ORDER.length - 1) {
95
+ cumulative -= stageData.get(STAGE_ORDER[i]).length;
96
+ }
97
+ }
98
+ // Calculate stage statistics
99
+ const stages = [];
100
+ for (let i = 0; i < STAGE_ORDER.length; i++) {
101
+ const stage = STAGE_ORDER[i];
102
+ const sessionsAtStage = sessions.filter((s) => {
103
+ const deepest = inferDeepestStage(s);
104
+ return STAGE_ORDER.indexOf(deepest) >= i;
105
+ });
106
+ const sessionsCount = sessionsAtStage.length;
107
+ const conversions = sessionsAtStage.filter((s) => s.has_purchase).length;
108
+ const dropoffCount = stageData.get(stage).length;
109
+ const dropoffRate = sessionsCount > 0 ? dropoffCount / sessionsCount : 0;
110
+ // Calculate performance metrics
111
+ const lcpValues = sessionsAtStage.map((s) => s.lcp_ms).filter((v) => v !== null);
112
+ const inpValues = sessionsAtStage.map((s) => s.inp_ms).filter((v) => v !== null);
113
+ // Performance impact: compare CVR for good vs poor LCP
114
+ let performanceImpact = null;
115
+ const goodPerf = sessionsAtStage.filter((s) => s.lcp_ms !== null && s.lcp_ms <= 2500);
116
+ const poorPerf = sessionsAtStage.filter((s) => s.lcp_ms !== null && s.lcp_ms >= 4000);
117
+ if (goodPerf.length >= 10 && poorPerf.length >= 10) {
118
+ const goodCvr = goodPerf.filter((s) => s.has_purchase).length / goodPerf.length;
119
+ const poorCvr = poorPerf.filter((s) => s.has_purchase).length / poorPerf.length;
120
+ performanceImpact = goodCvr - poorCvr;
121
+ }
122
+ stages.push({
123
+ stage,
124
+ sessions: sessionsCount,
125
+ conversions,
126
+ dropoffRate,
127
+ avgLcp: lcpValues.length > 0 ? lcpValues.reduce((a, b) => a + b, 0) / lcpValues.length : null,
128
+ avgInp: inpValues.length > 0 ? inpValues.reduce((a, b) => a + b, 0) / inpValues.length : null,
129
+ performanceImpact,
130
+ });
131
+ }
132
+ // Calculate step progressions
133
+ const steps = [];
134
+ for (let i = 0; i < STAGE_ORDER.length - 1; i++) {
135
+ const fromStage = STAGE_ORDER[i];
136
+ const toStage = STAGE_ORDER[i + 1];
137
+ const fromSessions = sessions.filter((s) => {
138
+ const deepest = inferDeepestStage(s);
139
+ return STAGE_ORDER.indexOf(deepest) >= i;
140
+ });
141
+ const toSessions = sessions.filter((s) => {
142
+ const deepest = inferDeepestStage(s);
143
+ return STAGE_ORDER.indexOf(deepest) >= i + 1;
144
+ });
145
+ const progressionRate = fromSessions.length > 0 ? toSessions.length / fromSessions.length : 0;
146
+ // Performance-based progression
147
+ const goodPerfFrom = fromSessions.filter((s) => s.lcp_ms !== null && s.lcp_ms <= 2500);
148
+ const poorPerfFrom = fromSessions.filter((s) => s.lcp_ms !== null && s.lcp_ms >= 4000);
149
+ let goodPerfProgressionRate = null;
150
+ let poorPerfProgressionRate = null;
151
+ if (goodPerfFrom.length >= 10) {
152
+ const goodPerfTo = goodPerfFrom.filter((s) => {
153
+ const deepest = inferDeepestStage(s);
154
+ return STAGE_ORDER.indexOf(deepest) >= i + 1;
155
+ });
156
+ goodPerfProgressionRate = goodPerfTo.length / goodPerfFrom.length;
157
+ }
158
+ if (poorPerfFrom.length >= 10) {
159
+ const poorPerfTo = poorPerfFrom.filter((s) => {
160
+ const deepest = inferDeepestStage(s);
161
+ return STAGE_ORDER.indexOf(deepest) >= i + 1;
162
+ });
163
+ poorPerfProgressionRate = poorPerfTo.length / poorPerfFrom.length;
164
+ }
165
+ const performanceGap = goodPerfProgressionRate !== null && poorPerfProgressionRate !== null
166
+ ? goodPerfProgressionRate - poorPerfProgressionRate
167
+ : null;
168
+ const lcpValues = fromSessions.map((s) => s.lcp_ms).filter((v) => v !== null);
169
+ steps.push({
170
+ from: fromStage,
171
+ to: toStage,
172
+ sessions: fromSessions.length,
173
+ progressions: toSessions.length,
174
+ progressionRate,
175
+ avgLcp: lcpValues.length > 0 ? lcpValues.reduce((a, b) => a + b, 0) / lcpValues.length : null,
176
+ goodPerfProgressionRate,
177
+ poorPerfProgressionRate,
178
+ performanceGap,
179
+ });
180
+ }
181
+ // Find biggest dropoff
182
+ const significantSteps = steps.filter((s) => s.sessions >= 100);
183
+ let biggestDropoff = null;
184
+ if (significantSteps.length > 0) {
185
+ const sorted = [...significantSteps].sort((a, b) => 1 - a.progressionRate - (1 - b.progressionRate));
186
+ const worst = sorted[0];
187
+ biggestDropoff = {
188
+ step: `${worst.from} → ${worst.to}`,
189
+ dropoffRate: 1 - worst.progressionRate,
190
+ performanceGap: worst.performanceGap,
191
+ };
192
+ }
193
+ // Find performance hotspots
194
+ const performanceHotspots = [];
195
+ for (const step of steps) {
196
+ if (step.performanceGap !== null && step.performanceGap > 0.05) {
197
+ performanceHotspots.push({
198
+ stage: step.from,
199
+ impact: step.performanceGap,
200
+ message: `${(step.performanceGap * 100).toFixed(1)}% higher progression with good performance at ${step.from}`,
201
+ });
202
+ }
203
+ }
204
+ // Generate recommendations
205
+ const recommendations = [];
206
+ if (biggestDropoff && biggestDropoff.dropoffRate > 0.5) {
207
+ recommendations.push(`Focus on ${biggestDropoff.step} - ${(biggestDropoff.dropoffRate * 100).toFixed(0)}% drop-off rate`);
208
+ }
209
+ if (performanceHotspots.length > 0) {
210
+ const topHotspot = performanceHotspots.sort((a, b) => b.impact - a.impact)[0];
211
+ recommendations.push(`Prioritize performance on ${topHotspot.stage} pages - ${topHotspot.message}`);
212
+ }
213
+ // Check for checkout performance
214
+ const checkoutStep = steps.find((s) => s.from === 'cart' && s.to === 'checkout');
215
+ if (checkoutStep && checkoutStep.performanceGap && checkoutStep.performanceGap > 0.1) {
216
+ recommendations.push(`Critical: Checkout performance impacts ${(checkoutStep.performanceGap * 100).toFixed(0)}% of cart abandonment`);
217
+ }
218
+ const overallConversionRate = sessions.length > 0 ? sessions.filter((s) => s.has_purchase).length / sessions.length : 0;
219
+ return {
220
+ stages,
221
+ steps,
222
+ overallConversionRate,
223
+ biggestDropoff,
224
+ performanceHotspots,
225
+ recommendations,
226
+ };
227
+ }
228
+ // =============================================================================
229
+ // REPORT GENERATION
230
+ // =============================================================================
231
+ /**
232
+ * Generate markdown report for funnel analysis.
233
+ */
234
+ export function generateFunnelMarkdown(report) {
235
+ const lines = [];
236
+ lines.push('## Funnel Analysis');
237
+ lines.push('');
238
+ lines.push(`Overall Conversion Rate: **${(report.overallConversionRate * 100).toFixed(2)}%**`);
239
+ lines.push('');
240
+ // Recommendations
241
+ if (report.recommendations.length > 0) {
242
+ lines.push('### Recommendations');
243
+ lines.push('');
244
+ for (const rec of report.recommendations) {
245
+ lines.push(`- ${rec}`);
246
+ }
247
+ lines.push('');
248
+ }
249
+ // Funnel visualization
250
+ lines.push('### Funnel Progression');
251
+ lines.push('');
252
+ lines.push('| Stage | Sessions | CVR | Drop-off | Perf Impact |');
253
+ lines.push('|-------|----------|-----|----------|-------------|');
254
+ for (const stage of report.stages) {
255
+ const perfImpact = stage.performanceImpact
256
+ ? `${stage.performanceImpact > 0 ? '+' : ''}${(stage.performanceImpact * 100).toFixed(2)}%`
257
+ : 'N/A';
258
+ const cvr = stage.sessions > 0 ? (stage.conversions / stage.sessions * 100).toFixed(2) : '0.00';
259
+ lines.push(`| ${stage.stage} | ${stage.sessions.toLocaleString()} | ${cvr}% | ${(stage.dropoffRate * 100).toFixed(1)}% | ${perfImpact} |`);
260
+ }
261
+ lines.push('');
262
+ // Step-by-step analysis
263
+ lines.push('### Step Analysis');
264
+ lines.push('');
265
+ lines.push('| Step | Progression | Good Perf | Poor Perf | Gap |');
266
+ lines.push('|------|-------------|-----------|-----------|-----|');
267
+ for (const step of report.steps) {
268
+ const goodPerf = step.goodPerfProgressionRate
269
+ ? `${(step.goodPerfProgressionRate * 100).toFixed(1)}%`
270
+ : 'N/A';
271
+ const poorPerf = step.poorPerfProgressionRate
272
+ ? `${(step.poorPerfProgressionRate * 100).toFixed(1)}%`
273
+ : 'N/A';
274
+ const gap = step.performanceGap
275
+ ? `${step.performanceGap > 0 ? '+' : ''}${(step.performanceGap * 100).toFixed(1)}%`
276
+ : 'N/A';
277
+ lines.push(`| ${step.from} → ${step.to} | ${(step.progressionRate * 100).toFixed(1)}% | ${goodPerf} | ${poorPerf} | ${gap} |`);
278
+ }
279
+ lines.push('');
280
+ // Performance Hotspots
281
+ if (report.performanceHotspots.length > 0) {
282
+ lines.push('### Performance Hotspots');
283
+ lines.push('');
284
+ lines.push('Stages where performance has the highest impact on progression:');
285
+ lines.push('');
286
+ for (const hotspot of report.performanceHotspots) {
287
+ lines.push(`- **${hotspot.stage}**: ${hotspot.message}`);
288
+ }
289
+ lines.push('');
290
+ }
291
+ return lines.join('\n');
292
+ }
293
+ //# sourceMappingURL=funnel.js.map