UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

140 lines 5.91 kB
/** * 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