Openstatus www.openstatus.dev
at 4c0f4c00a38753a5d0dfd7e7b7b7706dec6f1503 156 lines 4.4 kB view raw
1import type { WorkspacePlan } from "../workspaces/validation"; 2import { allPlans } from "./config"; 3import { type Addons, type Limits, limitsSchema } from "./schema"; 4 5export function getLimit<T extends keyof Limits>(limits: Limits, limit: T) { 6 return limits[limit] || allPlans.free.limits[limit]; 7} 8 9export function getLimits(plan: WorkspacePlan | null) { 10 return allPlans[plan || "free"].limits; 11} 12 13export function getPlanConfig(plan: WorkspacePlan | null) { 14 return allPlans[plan || "free"]; 15} 16 17export function getCurrency({ 18 continent, 19 country, 20}: { 21 continent: string; 22 country: string; 23}) { 24 if (country === "IN") { 25 return "INR"; 26 } 27 if (continent === "EU") { 28 return "EUR"; 29 } 30 return "USD"; 31} 32 33type PriceObject = { 34 USD: number; 35 EUR: number; 36 INR: number; 37}; 38 39type PriceConfig = { 40 value: number; 41 locale: string; 42 currency: string; 43}; 44 45function getLocaleForCurrency(currency: string): string { 46 return currency === "EUR" ? "fr-FR" : "en-US"; 47} 48 49function resolvePriceConfig( 50 price: PriceObject, 51 currency?: string, 52): PriceConfig { 53 const effectiveCurrency = currency && currency in price ? currency : "USD"; 54 const value = price[effectiveCurrency as keyof PriceObject]; 55 const locale = getLocaleForCurrency(effectiveCurrency); 56 57 return { value, locale, currency: effectiveCurrency }; 58} 59 60export function getPriceConfig(plan: WorkspacePlan, currency?: string) { 61 const planConfig = allPlans[plan]; 62 return resolvePriceConfig(planConfig.price, currency); 63} 64 65export function getAddonPriceConfig( 66 plan: WorkspacePlan, 67 addon: keyof Addons, 68 currency?: string, 69) { 70 const addonConfig = allPlans[plan].addons[addon]; 71 if (!addonConfig) { 72 return null; 73 } 74 return resolvePriceConfig(addonConfig.price, currency); 75} 76 77export function getPlansForLimit( 78 currentPlan: WorkspacePlan, 79 limit: keyof Limits, 80): WorkspacePlan[] { 81 const currentLimitValue = allPlans[currentPlan].limits[limit]; 82 const planOrder: WorkspacePlan[] = ["free", "starter", "team"]; 83 84 // Get plans that come after the current plan 85 const availablePlans = planOrder.filter((plan) => { 86 const planIndex = planOrder.indexOf(plan); 87 const currentIndex = planOrder.indexOf(currentPlan); 88 return planIndex > currentIndex; 89 }); 90 91 // Filter plans based on the limit feature value 92 return availablePlans.filter((plan) => { 93 const planLimitValue = allPlans[plan].limits[limit]; 94 95 // For boolean limits, only show plans where the feature is enabled 96 if (typeof currentLimitValue === "boolean") { 97 return planLimitValue === true; 98 } 99 100 // For numeric limits, show plans with higher values 101 if ( 102 typeof currentLimitValue === "number" && 103 typeof planLimitValue === "number" 104 ) { 105 return planLimitValue > currentLimitValue; 106 } 107 108 // For array limits (e.g., periodicity, regions), show plans with more options 109 if (Array.isArray(currentLimitValue) && Array.isArray(planLimitValue)) { 110 return planLimitValue.length > currentLimitValue.length; 111 } 112 113 // For string limits (e.g., data-retention), check if it's "better" 114 // This is a simple heuristic - could be improved based on specific needs 115 if ( 116 typeof currentLimitValue === "string" && 117 typeof planLimitValue === "string" 118 ) { 119 return planLimitValue !== currentLimitValue; 120 } 121 122 // For "Unlimited" string literal in members 123 if (planLimitValue === "Unlimited") { 124 return true; 125 } 126 127 return false; 128 }); 129} 130 131/** 132 * Update an addon value in limits 133 * @param limits - Current workspace limits 134 * @param addon - Addon key to update 135 * @param value - The value to set (boolean for toggle addons, number for quantity addons) 136 * @returns Updated limits object 137 */ 138export function updateAddonInLimits( 139 limits: Limits, 140 addon: keyof Addons, 141 value: boolean | number, 142): Limits { 143 const currentValue = limits[addon]; 144 const newLimits = { ...limits }; 145 146 // Infer addon type from the limit field type and set the value 147 if (typeof currentValue === "boolean" && typeof value === "boolean") { 148 // Toggle addon: set boolean value 149 (newLimits[addon] as boolean) = value; 150 } else if (typeof currentValue === "number" && typeof value === "number") { 151 // Quantity addon: set numeric value (ensure it doesn't go below 0) 152 (newLimits[addon] as number) = Math.max(0, value); 153 } 154 155 return limitsSchema.parse(newLimits); 156}