import { ref, onMounted, onUnmounted } from 'vue' import type { KbxApiOperationId, KbxExperimentAssignmentsResponse, KbxExperimentId } from '@kbx/contracts' import { kbxApi } from '../http/generated/kbxApiClient' import { kbxTelemetry } from '../telemetry/kbxTelemetryClient' import { createExperimentRolloutConnection } from './createExperimentRolloutConnection' const screenCache=new Map>() const listeners=new Mapvoid>>() let realtimeStarted=false const realtime=createExperimentRolloutConnection() realtime.onChanged(event=>{screenCache.delete(event.screenId);for(const refresh of listeners.get(event.screenId)??[])void refresh()}) async function ensureRealtime(){if(realtimeStarted)return;realtimeStarted=true;try{await realtime.start()}catch{realtimeStarted=false}} function assignments(screenId:string,force=false){if(force)screenCache.delete(screenId);if(!screenCache.has(screenId))screenCache.set(screenId,kbxApi.request('common.experiments.assignments' as KbxApiOperationId,{query:{screenId}}));return screenCache.get(screenId)!} export function useKbxExperiment(screenId:string,screenVersion:string,experimentId:KbxExperimentId,surface:string){ const variant=ref('control'),enrolled=ref(false);let lastExposure='' const refresh=async()=>{ try{const result=await assignments(screenId,true);const assignment=result.assignments.find(x=>x.experimentId===experimentId);if(!assignment||!assignment.enrolled){enrolled.value=false;variant.value='control';lastExposure='';kbxTelemetry.clearExperiment(screenId);return}enrolled.value=true;variant.value=assignment.variant;kbxTelemetry.bindExperiment(screenId,assignment.experimentId,assignment.variant);const exposure=`${assignment.experimentId}:${assignment.variant}`;if(lastExposure!==exposure){lastExposure=exposure;kbxTelemetry.track('experiment.exposed',{screenId,screenVersion,attributes:{surface}})}}catch{/* experiment infrastructure must not block business UI */} } onMounted(async()=>{const set=listeners.get(screenId)??new Set<()=>void>();set.add(refresh);listeners.set(screenId,set);await ensureRealtime();await refresh()}) onUnmounted(()=>{listeners.get(screenId)?.delete(refresh);kbxTelemetry.clearExperiment(screenId)}) return {variant,enrolled,is:(value:string)=>variant.value===value,refresh} }