@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
140 lines • 5.91 kB
JavaScript
/**
* Traffic Allocation Calculator
* Handles the mathematical challenge of dividing traffic across multiple variations
* when perfect division isn't possible (e.g., 100% ÷ 3 = 33.333...%)
*/
export class TrafficAllocationCalculator {
/**
* Calculate traffic allocation in basis points for multiple variations
* @param numVariations - Number of variations to split traffic between
* @returns Array of basis points allocations (10000 = 100%)
*/
static calculateBasisPointAllocations(numVariations) {
if (numVariations <= 0) {
throw new Error('Number of variations must be greater than 0');
}
const totalBasisPoints = 10000; // 100%
const baseAllocation = Math.floor(totalBasisPoints / numVariations);
const remainder = totalBasisPoints % numVariations;
// Create array with base allocation for all variations
const allocations = new Array(numVariations).fill(baseAllocation);
// Distribute remainder to last variations for consistency
// This ensures the last variation(s) get the extra basis points
for (let i = 0; i < remainder; i++) {
allocations[numVariations - 1 - i]++;
}
// Verify total equals 10000
const total = allocations.reduce((sum, val) => sum + val, 0);
if (total !== totalBasisPoints) {
throw new Error(`Traffic allocation error: total ${total} != ${totalBasisPoints}`);
}
return allocations;
}
/**
* Calculate traffic allocation as percentages for display
* @param numVariations - Number of variations
* @returns Array of percentage strings (e.g., ["33.33", "33.33", "33.34"])
*/
static calculatePercentageAllocations(numVariations) {
const basisPoints = this.calculateBasisPointAllocations(numVariations);
return basisPoints.map(bp => (bp / 100).toFixed(2));
}
/**
* Generate variation allocations for Optimizely API format
* @param variationKeys - Array of variation keys
* @returns Object with variation allocations in API format
*/
static generateVariationAllocations(variationKeys) {
const allocations = this.calculateBasisPointAllocations(variationKeys.length);
const result = {};
variationKeys.forEach((key, index) => {
result[key] = {
percentage_included: allocations[index]
};
});
return result;
}
/**
* Convert percentage string to basis points
* @param percentage - Percentage as string (e.g., "33.33") or basis points (e.g., "5000")
* @returns Basis points (e.g., 3333)
*/
static percentageToBasisPoints(percentage) {
const value = typeof percentage === 'string' ? parseFloat(percentage) : percentage;
if (isNaN(value) || value < 0) {
throw new Error(`Invalid percentage: ${percentage}`);
}
// Smart detection: if value > 100, assume it's already in basis points
if (value > 100) {
// Validate it's a reasonable basis points value (0-10000)
if (value > 10000) {
throw new Error(`Invalid basis points value: ${percentage} (must be between 0-10000)`);
}
return Math.round(value);
}
// Otherwise treat as percentage and convert to basis points
return Math.round(value * 100);
}
/**
* Convert basis points to percentage
* @param basisPoints - Basis points (e.g., 3333)
* @returns Percentage (e.g., 33.33)
*/
static basisPointsToPercentage(basisPoints) {
return basisPoints / 100;
}
/**
* Get human-readable allocation description
* @param numVariations - Number of variations
* @returns Description of traffic allocation
*/
static getAllocationDescription(numVariations) {
const percentages = this.calculatePercentageAllocations(numVariations);
const uniquePercentages = [...new Set(percentages)];
if (uniquePercentages.length === 1) {
// Even split
return `${percentages[0]}% each (${numVariations} variations)`;
}
else {
// Uneven split due to remainder
const basePercent = percentages[0];
const higherPercent = percentages[percentages.length - 1];
const numWithBase = percentages.filter(p => p === basePercent).length;
const numWithHigher = percentages.filter(p => p === higherPercent).length;
return `${numWithBase} variations get ${basePercent}%, ${numWithHigher} variation(s) get ${higherPercent}%`;
}
}
/**
* Examples of common allocations
*/
static getCommonExamples() {
return {
2: {
basisPoints: [5000, 5000],
percentages: ["50.00", "50.00"],
description: "Even 50/50 split"
},
3: {
basisPoints: [3333, 3333, 3334],
percentages: ["33.33", "33.33", "33.34"],
description: "Last variation gets extra 0.01%"
},
4: {
basisPoints: [2500, 2500, 2500, 2500],
percentages: ["25.00", "25.00", "25.00", "25.00"],
description: "Even 25% split"
},
5: {
basisPoints: [2000, 2000, 2000, 2000, 2000],
percentages: ["20.00", "20.00", "20.00", "20.00", "20.00"],
description: "Even 20% split"
},
7: {
basisPoints: [1428, 1428, 1428, 1429, 1429, 1429, 1429],
percentages: ["14.28", "14.28", "14.28", "14.29", "14.29", "14.29", "14.29"],
description: "Last 4 variations get extra 0.01%"
}
};
}
}
//# sourceMappingURL=TrafficAllocationCalculator.js.map