kanmi-perf-revenue 1.0.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.
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Empirical Performance Revenue Engine
4
+ *
5
+ * A simpler, more transparent approach to performance-revenue analysis.
6
+ * Uses actual session-level data to build empirical conversion curves.
7
+ *
8
+ * Key differences from coefficient-based models:
9
+ * - No assumed "0.5% CVR per 100ms LCP" coefficients
10
+ * - Measures actual CVR at each performance bucket from YOUR data
11
+ * - More honest about what we know vs. assume
12
+ *
13
+ * Usage:
14
+ * // With Datadog credentials
15
+ * const result = await analyzeWithDatadog({
16
+ * apiKey: 'xxx',
17
+ * appKey: 'xxx',
18
+ * startDate: '2024-01-01',
19
+ * endDate: '2024-01-14',
20
+ * });
21
+ *
22
+ * // With mock data (for testing)
23
+ * const result = analyzeWithMockData({
24
+ * sessionCount: 50000,
25
+ * startDate: '2024-01-01',
26
+ * endDate: '2024-01-14',
27
+ * });
28
+ *
29
+ * @author Kanmi Obasa <i@kanmiobasa.com>
30
+ */
31
+ import { queryDatadogSessions, generateMockSessions, } from './datadog-session-query.js';
32
+ import { buildAllCurves } from './conversion-curve.js';
33
+ import { calculateAllOpportunities, getTopOpportunity, } from './opportunity-calculator.js';
34
+ import { generateReport } from './report.js';
35
+ import { importFromFile } from './data-import.js';
36
+ // =============================================================================
37
+ // MAIN ANALYSIS FUNCTIONS
38
+ // =============================================================================
39
+ /**
40
+ * Run full analysis using Datadog RUM data.
41
+ */
42
+ export async function analyzeWithDatadog(options) {
43
+ // Query Datadog for session data
44
+ const queryResult = await queryDatadogSessions(options);
45
+ return processSessionData(queryResult, {
46
+ clientName: options.clientName,
47
+ period: queryResult.metadata.period,
48
+ });
49
+ }
50
+ /**
51
+ * Run analysis with mock data (for testing/demo).
52
+ */
53
+ export function analyzeWithMockData(options) {
54
+ // Generate mock session data
55
+ const queryResult = generateMockSessions({
56
+ count: options.sessionCount,
57
+ startDate: options.startDate,
58
+ endDate: options.endDate,
59
+ baseConversionRate: options.baseConversionRate,
60
+ });
61
+ return processSessionData(queryResult, {
62
+ clientName: options.clientName,
63
+ period: queryResult.metadata.period,
64
+ });
65
+ }
66
+ /**
67
+ * Run analysis from CSV or JSON file.
68
+ * Bring your own data from any source.
69
+ */
70
+ export function analyzeFromFile(options) {
71
+ const queryResult = importFromFile(options.filePath, {
72
+ startDate: options.startDate,
73
+ endDate: options.endDate,
74
+ });
75
+ return processSessionData(queryResult, {
76
+ clientName: options.clientName,
77
+ period: queryResult.metadata.period,
78
+ });
79
+ }
80
+ /**
81
+ * Process session data through the full analysis pipeline.
82
+ */
83
+ function processSessionData(queryResult, config) {
84
+ const { sessions, metadata } = queryResult;
85
+ // Build empirical conversion curves
86
+ const curves = buildAllCurves(sessions);
87
+ // Calculate opportunities
88
+ const opportunities = calculateAllOpportunities(curves, {
89
+ periodDays: metadata.period.days,
90
+ });
91
+ // Find top opportunity
92
+ const topOpp = getTopOpportunity(opportunities);
93
+ const topOpportunity = topOpp
94
+ ? {
95
+ metric: topOpp.metric,
96
+ monthlyRevenue: topOpp.scenario.conservative_monthly_revenue,
97
+ summary: opportunities[topOpp.metric].recommendation.summary,
98
+ }
99
+ : null;
100
+ // Generate report
101
+ const reportConfig = {
102
+ clientName: config.clientName,
103
+ period: config.period,
104
+ };
105
+ const report = generateReport({ curves, opportunities }, reportConfig);
106
+ return {
107
+ curves,
108
+ opportunities,
109
+ topOpportunity,
110
+ report,
111
+ metadata: {
112
+ totalSessions: metadata.total_sessions,
113
+ sessionsWithLcp: metadata.sessions_with_lcp,
114
+ sessionsWithConversions: metadata.sessions_with_conversions,
115
+ period: metadata.period,
116
+ },
117
+ };
118
+ }
119
+ // =============================================================================
120
+ // CLI ENTRY POINT
121
+ // =============================================================================
122
+ /**
123
+ * CLI entry point.
124
+ */
125
+ export async function main() {
126
+ const args = process.argv.slice(2);
127
+ if (args.includes('--help') || args.includes('-h')) {
128
+ printHelp();
129
+ return;
130
+ }
131
+ // Check for file import mode
132
+ const filePath = getArg(args, '--file');
133
+ const clientName = getArg(args, '--client');
134
+ if (filePath) {
135
+ console.log(`Importing data from ${filePath}...\n`);
136
+ try {
137
+ const result = analyzeFromFile({
138
+ filePath,
139
+ clientName,
140
+ startDate: getArg(args, '--start'),
141
+ endDate: getArg(args, '--end'),
142
+ });
143
+ console.log(result.report);
144
+ printSummary(result);
145
+ return;
146
+ }
147
+ catch (error) {
148
+ console.error('Import failed:', error);
149
+ process.exit(1);
150
+ }
151
+ }
152
+ // Check for demo mode
153
+ const demoMode = args.includes('--demo');
154
+ if (demoMode) {
155
+ console.log('Running in demo mode with mock data...\n');
156
+ const result = analyzeWithMockData({
157
+ sessionCount: 50000,
158
+ startDate: '2024-01-01',
159
+ endDate: '2024-01-14',
160
+ clientName: clientName || 'Demo Company',
161
+ baseConversionRate: 0.03,
162
+ });
163
+ console.log(result.report);
164
+ printSummary(result);
165
+ return;
166
+ }
167
+ // Check for Datadog credentials
168
+ const apiKey = process.env.DD_API_KEY || getArg(args, '--api-key');
169
+ const appKey = process.env.DD_APP_KEY || getArg(args, '--app-key');
170
+ const startDate = getArg(args, '--start') || getDefaultStartDate();
171
+ const endDate = getArg(args, '--end') || getDefaultEndDate();
172
+ if (!apiKey || !appKey) {
173
+ console.error('Error: Datadog credentials required.');
174
+ console.error('Set DD_API_KEY and DD_APP_KEY environment variables,');
175
+ console.error('or use --api-key and --app-key arguments.');
176
+ console.error('\nOr run with --demo for mock data demo.\n');
177
+ printHelp();
178
+ process.exit(1);
179
+ }
180
+ console.log(`Analyzing ${startDate} to ${endDate}...\n`);
181
+ try {
182
+ const result = await analyzeWithDatadog({
183
+ apiKey,
184
+ appKey,
185
+ startDate,
186
+ endDate,
187
+ clientName,
188
+ });
189
+ console.log(result.report);
190
+ }
191
+ catch (error) {
192
+ console.error('Analysis failed:', error);
193
+ process.exit(1);
194
+ }
195
+ }
196
+ // =============================================================================
197
+ // HELPERS
198
+ // =============================================================================
199
+ function printHelp() {
200
+ console.log(`
201
+ Empirical Performance Revenue Engine
202
+
203
+ Usage:
204
+ npx tsx src/empirical/index.ts [options]
205
+
206
+ Data Sources (choose one):
207
+ --file PATH Import from CSV or JSON file (recommended)
208
+ --demo Run with mock data (for testing)
209
+ --api-key KEY Datadog API key (or set DD_API_KEY env var)
210
+
211
+ Options:
212
+ --start DATE Start date (YYYY-MM-DD). Default: 14 days ago
213
+ --end DATE End date (YYYY-MM-DD). Default: today
214
+ --client NAME Client name for report header
215
+ --help, -h Show this help message
216
+
217
+ Examples:
218
+ # Import from your own CSV/JSON
219
+ npx tsx src/empirical/index.ts --file sessions.csv --client "Acme Corp"
220
+
221
+ # Demo with mock data
222
+ npx tsx src/empirical/index.ts --demo
223
+
224
+ # With Datadog credentials
225
+ DD_API_KEY=xxx DD_APP_KEY=xxx npx tsx src/empirical/index.ts
226
+
227
+ CSV/JSON Format:
228
+ Required columns: session_id, converted (or has_purchase)
229
+ Optional: lcp_ms, inp_ms, cls, order_value, device, page_type
230
+ `);
231
+ }
232
+ function printSummary(result) {
233
+ console.log('\n--- Analysis Summary ---\n');
234
+ console.log(`Total Sessions: ${result.metadata.totalSessions.toLocaleString()}`);
235
+ console.log(`Sessions with LCP: ${result.metadata.sessionsWithLcp.toLocaleString()}`);
236
+ console.log(`Conversions: ${result.metadata.sessionsWithConversions.toLocaleString()}`);
237
+ if (result.topOpportunity) {
238
+ console.log(`\nTop Opportunity: ${result.topOpportunity.metric.toUpperCase()}`);
239
+ console.log(`Monthly Revenue: $${result.topOpportunity.monthlyRevenue.toLocaleString()}`);
240
+ }
241
+ }
242
+ function getArg(args, flag) {
243
+ const index = args.indexOf(flag);
244
+ if (index !== -1 && args[index + 1]) {
245
+ return args[index + 1];
246
+ }
247
+ return undefined;
248
+ }
249
+ function getDefaultStartDate() {
250
+ const date = new Date();
251
+ date.setDate(date.getDate() - 14);
252
+ return date.toISOString().split('T')[0];
253
+ }
254
+ function getDefaultEndDate() {
255
+ return new Date().toISOString().split('T')[0];
256
+ }
257
+ // Re-export functions for library use
258
+ export { queryDatadogSessions, generateMockSessions } from './datadog-session-query.js';
259
+ export { buildConversionCurve, buildAllCurves, buildSegmentedCurves } from './conversion-curve.js';
260
+ export { calculateOpportunity, calculateAllOpportunities, getTopOpportunity } from './opportunity-calculator.js';
261
+ export { generateReport } from './report.js';
262
+ export { importFromFile, importFromCsv, importFromJson } from './data-import.js';
263
+ // Run if executed directly
264
+ if (import.meta.url === `file://${process.argv[1]}`) {
265
+ main().catch(console.error);
266
+ }
267
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/empirical/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Revenue Opportunity Calculator
3
+ *
4
+ * Calculates revenue opportunity from distribution shifts.
5
+ * Given an empirical conversion curve, we calculate:
6
+ * "If X sessions move from slow bucket to fast bucket, revenue increases by $Y"
7
+ *
8
+ * This is based on MEASURED CVR differences, not assumed coefficients.
9
+ *
10
+ * @author Kanmi Obasa <i@kanmiobasa.com>
11
+ */
12
+ import type { ConversionCurve } from './conversion-curve.js';
13
+ export interface OpportunityScenario {
14
+ /** Descriptive name for the scenario */
15
+ name: string;
16
+ /** Target metric value in ms (sessions above this are "slow") */
17
+ target_ms: number;
18
+ /** Current sessions above target (slow) */
19
+ slow_sessions: number;
20
+ /** Current sessions at or below target (fast) */
21
+ fast_sessions: number;
22
+ /** Measured CVR for slow sessions */
23
+ slow_cvr: number;
24
+ /** Measured CVR for fast sessions */
25
+ fast_cvr: number;
26
+ /** Absolute CVR difference (fast - slow) */
27
+ cvr_delta: number;
28
+ /** Relative CVR improvement ((fast - slow) / slow) */
29
+ relative_improvement: number;
30
+ /** Additional conversions if ALL slow sessions became fast */
31
+ max_additional_conversions: number;
32
+ /** Additional monthly revenue (max scenario) */
33
+ max_monthly_revenue: number;
34
+ /** Conservative estimate (50% of sessions improved) */
35
+ conservative_monthly_revenue: number;
36
+ /** Average order value used */
37
+ aov: number;
38
+ /** Statistical confidence */
39
+ confidence: 'high' | 'medium' | 'low';
40
+ }
41
+ export interface OpportunityReport {
42
+ /** The metric analyzed */
43
+ metric: 'lcp' | 'inp' | 'cls';
44
+ /** Analysis period */
45
+ period: {
46
+ days: number;
47
+ monthly_factor: number;
48
+ };
49
+ /** Scenarios at different target thresholds */
50
+ scenarios: OpportunityScenario[];
51
+ /** Recommended priority based on opportunity size */
52
+ recommendation: {
53
+ priority: 'high' | 'medium' | 'low';
54
+ target_ms: number;
55
+ monthly_opportunity: number;
56
+ summary: string;
57
+ };
58
+ /** Raw curve data for transparency */
59
+ curve_summary: {
60
+ total_sessions: number;
61
+ overall_cvr: number;
62
+ overall_aov: number;
63
+ cvr_range: number;
64
+ };
65
+ }
66
+ export interface OpportunityConfig {
67
+ /** Period in days for the data (for monthly normalization) */
68
+ periodDays: number;
69
+ /** Custom target thresholds to evaluate. Default: CWV thresholds */
70
+ targetThresholds?: number[];
71
+ /** Minimum sessions to consider scenario valid. Default: 50 */
72
+ minSessionsForScenario?: number;
73
+ }
74
+ /**
75
+ * Calculate revenue opportunity from an empirical conversion curve.
76
+ *
77
+ * The core calculation:
78
+ * 1. Count sessions above target threshold (slow)
79
+ * 2. Get measured CVR for fast vs slow buckets
80
+ * 3. Calculate: additional_conversions = slow_sessions × (fast_cvr - slow_cvr)
81
+ * 4. Calculate: revenue = additional_conversions × AOV
82
+ */
83
+ export declare function calculateOpportunity(curve: ConversionCurve, config: OpportunityConfig): OpportunityReport;
84
+ /**
85
+ * Calculate opportunity for all CWV metrics.
86
+ */
87
+ export declare function calculateAllOpportunities(curves: {
88
+ lcp: ConversionCurve;
89
+ inp: ConversionCurve;
90
+ cls: ConversionCurve;
91
+ }, config: OpportunityConfig): {
92
+ lcp: OpportunityReport;
93
+ inp: OpportunityReport;
94
+ cls: OpportunityReport;
95
+ };
96
+ /**
97
+ * Get the single highest-impact opportunity across all metrics.
98
+ */
99
+ export declare function getTopOpportunity(opportunities: {
100
+ lcp: OpportunityReport;
101
+ inp: OpportunityReport;
102
+ cls: OpportunityReport;
103
+ }): {
104
+ metric: 'lcp' | 'inp' | 'cls';
105
+ scenario: OpportunityScenario;
106
+ } | null;
107
+ //# sourceMappingURL=opportunity-calculator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-calculator.d.ts","sourceRoot":"","sources":["../../src/empirical/opportunity-calculator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,uBAAuB,CAAC;AAM/E,MAAM,WAAW,mBAAmB;IAClC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,0BAA0B,EAAE,MAAM,CAAC;IACnC,gDAAgD;IAChD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,4BAA4B,EAAE,MAAM,CAAC;IACrC,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,0BAA0B;IAC1B,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAC9B,sBAAsB;IACtB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,+CAA+C;IAC/C,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,qDAAqD;IACrD,cAAc,EAAE;QACd,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC;QAClB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,sCAAsC;IACtC,aAAa,EAAE;QACb,cAAc,EAAE,MAAM,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,+DAA+D;IAC/D,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAQD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,iBAAiB,GACxB,iBAAiB,CA6CnB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE;IAAE,GAAG,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,eAAe,CAAA;CAAE,EAC5E,MAAM,EAAE,iBAAiB,GACxB;IAAE,GAAG,EAAE,iBAAiB,CAAC;IAAC,GAAG,EAAE,iBAAiB,CAAC;IAAC,GAAG,EAAE,iBAAiB,CAAA;CAAE,CAM5E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE;IAAE,GAAG,EAAE,iBAAiB,CAAC;IAAC,GAAG,EAAE,iBAAiB,CAAC;IAAC,GAAG,EAAE,iBAAiB,CAAA;CAAE,GACxF;IAAE,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IAAC,QAAQ,EAAE,mBAAmB,CAAA;CAAE,GAAG,IAAI,CAoBzE"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Revenue Opportunity Calculator
3
+ *
4
+ * Calculates revenue opportunity from distribution shifts.
5
+ * Given an empirical conversion curve, we calculate:
6
+ * "If X sessions move from slow bucket to fast bucket, revenue increases by $Y"
7
+ *
8
+ * This is based on MEASURED CVR differences, not assumed coefficients.
9
+ *
10
+ * @author Kanmi Obasa <i@kanmiobasa.com>
11
+ */
12
+ // =============================================================================
13
+ // OPPORTUNITY CALCULATOR
14
+ // =============================================================================
15
+ const STANDARD_MONTH_DAYS = 30;
16
+ /**
17
+ * Calculate revenue opportunity from an empirical conversion curve.
18
+ *
19
+ * The core calculation:
20
+ * 1. Count sessions above target threshold (slow)
21
+ * 2. Get measured CVR for fast vs slow buckets
22
+ * 3. Calculate: additional_conversions = slow_sessions × (fast_cvr - slow_cvr)
23
+ * 4. Calculate: revenue = additional_conversions × AOV
24
+ */
25
+ export function calculateOpportunity(curve, config) {
26
+ const { periodDays, minSessionsForScenario = 50, } = config;
27
+ const monthlyFactor = STANDARD_MONTH_DAYS / periodDays;
28
+ const targets = config.targetThresholds || getDefaultTargets(curve.metric);
29
+ const scenarios = [];
30
+ for (const targetMs of targets) {
31
+ const scenario = calculateScenarioAtTarget(curve, targetMs, monthlyFactor, minSessionsForScenario);
32
+ if (scenario) {
33
+ scenarios.push(scenario);
34
+ }
35
+ }
36
+ // Sort by opportunity size
37
+ scenarios.sort((a, b) => b.conservative_monthly_revenue - a.conservative_monthly_revenue);
38
+ // Generate recommendation
39
+ const recommendation = generateRecommendation(scenarios, curve);
40
+ return {
41
+ metric: curve.metric,
42
+ period: {
43
+ days: periodDays,
44
+ monthly_factor: monthlyFactor,
45
+ },
46
+ scenarios,
47
+ recommendation,
48
+ curve_summary: {
49
+ total_sessions: curve.summary.total_sessions,
50
+ overall_cvr: curve.summary.overall_cvr,
51
+ overall_aov: curve.summary.overall_aov,
52
+ cvr_range: curve.summary.cvr_range,
53
+ },
54
+ };
55
+ }
56
+ /**
57
+ * Calculate opportunity for all CWV metrics.
58
+ */
59
+ export function calculateAllOpportunities(curves, config) {
60
+ return {
61
+ lcp: calculateOpportunity(curves.lcp, config),
62
+ inp: calculateOpportunity(curves.inp, config),
63
+ cls: calculateOpportunity(curves.cls, config),
64
+ };
65
+ }
66
+ /**
67
+ * Get the single highest-impact opportunity across all metrics.
68
+ */
69
+ export function getTopOpportunity(opportunities) {
70
+ const allScenarios = [];
71
+ for (const [metric, report] of Object.entries(opportunities)) {
72
+ for (const scenario of report.scenarios) {
73
+ allScenarios.push({
74
+ metric: metric,
75
+ scenario,
76
+ });
77
+ }
78
+ }
79
+ if (allScenarios.length === 0)
80
+ return null;
81
+ // Sort by conservative revenue opportunity
82
+ allScenarios.sort((a, b) => b.scenario.conservative_monthly_revenue - a.scenario.conservative_monthly_revenue);
83
+ return allScenarios[0];
84
+ }
85
+ // =============================================================================
86
+ // SCENARIO CALCULATION
87
+ // =============================================================================
88
+ function calculateScenarioAtTarget(curve, targetMs, monthlyFactor, minSessions) {
89
+ // Partition buckets into fast (at or below target) and slow (above target)
90
+ const fastBuckets = [];
91
+ const slowBuckets = [];
92
+ for (const bucket of curve.buckets) {
93
+ if (bucket.upper_ms <= targetMs) {
94
+ fastBuckets.push(bucket);
95
+ }
96
+ else if (bucket.lower_ms >= targetMs) {
97
+ slowBuckets.push(bucket);
98
+ }
99
+ else {
100
+ // Bucket spans the threshold - split proportionally
101
+ const belowPortion = (targetMs - bucket.lower_ms) / (bucket.upper_ms - bucket.lower_ms);
102
+ const abovePortion = 1 - belowPortion;
103
+ if (belowPortion > 0) {
104
+ fastBuckets.push({
105
+ ...bucket,
106
+ sessions: Math.round(bucket.sessions * belowPortion),
107
+ conversions: Math.round(bucket.conversions * belowPortion),
108
+ revenue: bucket.revenue * belowPortion,
109
+ });
110
+ }
111
+ if (abovePortion > 0) {
112
+ slowBuckets.push({
113
+ ...bucket,
114
+ sessions: Math.round(bucket.sessions * abovePortion),
115
+ conversions: Math.round(bucket.conversions * abovePortion),
116
+ revenue: bucket.revenue * abovePortion,
117
+ });
118
+ }
119
+ }
120
+ }
121
+ const fastSessions = fastBuckets.reduce((sum, b) => sum + b.sessions, 0);
122
+ const slowSessions = slowBuckets.reduce((sum, b) => sum + b.sessions, 0);
123
+ // Need minimum sessions in both groups for valid scenario
124
+ if (fastSessions < minSessions || slowSessions < minSessions) {
125
+ return null;
126
+ }
127
+ const fastConversions = fastBuckets.reduce((sum, b) => sum + b.conversions, 0);
128
+ const slowConversions = slowBuckets.reduce((sum, b) => sum + b.conversions, 0);
129
+ const fastCvr = fastConversions / fastSessions;
130
+ const slowCvr = slowConversions / slowSessions;
131
+ const cvrDelta = fastCvr - slowCvr;
132
+ // If slow sessions already convert better, no opportunity
133
+ if (cvrDelta <= 0) {
134
+ return null;
135
+ }
136
+ const totalRevenue = fastBuckets.reduce((sum, b) => sum + b.revenue, 0) +
137
+ slowBuckets.reduce((sum, b) => sum + b.revenue, 0);
138
+ const totalConversions = fastConversions + slowConversions;
139
+ const aov = totalConversions > 0 ? totalRevenue / totalConversions : 0;
140
+ // Calculate opportunity
141
+ // If all slow sessions achieved fast CVR, how many additional conversions?
142
+ const maxAdditionalConversions = slowSessions * cvrDelta;
143
+ const maxRevenue = maxAdditionalConversions * aov * monthlyFactor;
144
+ // Conservative: assume only 50% of sessions can realistically be improved
145
+ const conservativeRevenue = maxRevenue * 0.5;
146
+ // Determine confidence
147
+ let confidence = 'low';
148
+ const minBucketSessions = Math.min(...fastBuckets.map((b) => b.sessions), ...slowBuckets.map((b) => b.sessions));
149
+ if (minBucketSessions >= 100) {
150
+ confidence = 'high';
151
+ }
152
+ else if (minBucketSessions >= 30) {
153
+ confidence = 'medium';
154
+ }
155
+ const metricLabel = curve.metric.toUpperCase();
156
+ const targetLabel = formatTargetLabel(targetMs, curve.metric);
157
+ return {
158
+ name: `Improve ${metricLabel} to ${targetLabel}`,
159
+ target_ms: targetMs,
160
+ slow_sessions: slowSessions,
161
+ fast_sessions: fastSessions,
162
+ slow_cvr: slowCvr,
163
+ fast_cvr: fastCvr,
164
+ cvr_delta: cvrDelta,
165
+ relative_improvement: slowCvr > 0 ? cvrDelta / slowCvr : 0,
166
+ max_additional_conversions: Math.round(maxAdditionalConversions * monthlyFactor),
167
+ max_monthly_revenue: Math.round(maxRevenue),
168
+ conservative_monthly_revenue: Math.round(conservativeRevenue),
169
+ aov,
170
+ confidence,
171
+ };
172
+ }
173
+ // =============================================================================
174
+ // RECOMMENDATION GENERATION
175
+ // =============================================================================
176
+ function generateRecommendation(scenarios, curve) {
177
+ if (scenarios.length === 0) {
178
+ return {
179
+ priority: 'low',
180
+ target_ms: curve.thresholds.good,
181
+ monthly_opportunity: 0,
182
+ summary: `No significant ${curve.metric.toUpperCase()} opportunity detected. Current performance distribution shows minimal CVR variation.`,
183
+ };
184
+ }
185
+ // Find the best scenario (highest conservative revenue with good confidence)
186
+ const viableScenarios = scenarios.filter((s) => s.confidence !== 'low');
187
+ const bestScenario = viableScenarios[0] || scenarios[0];
188
+ // Determine priority
189
+ let priority = 'low';
190
+ if (bestScenario.conservative_monthly_revenue >= 10000) {
191
+ priority = 'high';
192
+ }
193
+ else if (bestScenario.conservative_monthly_revenue >= 2500) {
194
+ priority = 'medium';
195
+ }
196
+ const metricLabel = curve.metric.toUpperCase();
197
+ const targetLabel = formatTargetLabel(bestScenario.target_ms, curve.metric);
198
+ const slowPct = ((bestScenario.slow_sessions / curve.summary.total_sessions) * 100).toFixed(0);
199
+ const cvrLiftPct = (bestScenario.relative_improvement * 100).toFixed(1);
200
+ return {
201
+ priority,
202
+ target_ms: bestScenario.target_ms,
203
+ monthly_opportunity: bestScenario.conservative_monthly_revenue,
204
+ summary: `${slowPct}% of sessions have ${metricLabel} > ${targetLabel}. These sessions convert ${cvrLiftPct}% worse than faster sessions. Improving them could generate ~$${formatCurrency(bestScenario.conservative_monthly_revenue)}/month.`,
205
+ };
206
+ }
207
+ // =============================================================================
208
+ // HELPERS
209
+ // =============================================================================
210
+ function getDefaultTargets(metric) {
211
+ switch (metric) {
212
+ case 'lcp':
213
+ return [2500, 3000, 4000]; // Good threshold, mid, poor threshold
214
+ case 'inp':
215
+ return [200, 300, 500];
216
+ case 'cls':
217
+ return [100, 150, 250]; // Scaled by 1000
218
+ }
219
+ }
220
+ function formatTargetLabel(targetMs, metric) {
221
+ if (metric === 'cls') {
222
+ return (targetMs / 1000).toFixed(2);
223
+ }
224
+ if (targetMs >= 1000) {
225
+ return `${(targetMs / 1000).toFixed(1)}s`;
226
+ }
227
+ return `${targetMs}ms`;
228
+ }
229
+ function formatCurrency(value) {
230
+ if (value >= 1_000_000) {
231
+ return `${(value / 1_000_000).toFixed(1)}M`;
232
+ }
233
+ if (value >= 1_000) {
234
+ return `${(value / 1_000).toFixed(0)}K`;
235
+ }
236
+ return value.toFixed(0);
237
+ }
238
+ //# sourceMappingURL=opportunity-calculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunity-calculator.js","sourceRoot":"","sources":["../../src/empirical/opportunity-calculator.ts"],"names":[],"mappings":"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"}