UNPKG

@neuroequality/neuroadapt-ai

Version:

AI-powered accessibility personalization for neurodivergent users

4 lines (3 loc) 9.59 kB
"use strict";const l=require("eventemitter3"),n=require("zod"),u=n.z.object({timestamp:n.z.number(),type:n.z.enum(["click","scroll","focus","blur","keypress","resize","preference_change"]),target:n.z.string().optional(),value:n.z.unknown().optional(),context:n.z.record(n.z.string(),n.z.unknown()).optional()}),m=n.z.object({eventId:n.z.string().uuid(),userId:n.z.string().optional(),sessionId:n.z.string(),timestamp:n.z.number(),event:n.z.string(),properties:n.z.record(n.z.string(),n.z.unknown()).optional(),preferences:n.z.record(n.z.string(),n.z.unknown()).optional(),context:n.z.object({userAgent:n.z.string().optional(),viewport:n.z.object({width:n.z.number(),height:n.z.number()}).optional(),url:n.z.string().optional(),referrer:n.z.string().optional()}).optional()});class p extends l.EventEmitter{constructor(e={}){super(),this.currentSession=null,this.sessions=[],this.eventBuffer=[],this.patternCache=new Map,this.anomalyBaselines=new Map,this.config={sessionTimeout:e.sessionTimeout||18e5,maxSessionEvents:e.maxSessionEvents||1e3,enableRealTimeAnalysis:e.enableRealTimeAnalysis??!0,patternDetectionThreshold:e.patternDetectionThreshold||.7,anomalyDetectionSensitivity:e.anomalyDetectionSensitivity||.8,privacyMode:e.privacyMode??!0,bufferSize:e.bufferSize||100}}startSession(e){const t=e||this.generateSessionId();return this.currentSession&&this.endSession(),this.currentSession={sessionId:t,startTime:Date.now(),events:[],interactions:[],metadata:{userAgent:typeof navigator<"u"?navigator.userAgent:"unknown",viewport:this.getViewportInfo(),timezone:Intl.DateTimeFormat().resolvedOptions().timeZone}},this.resetSessionTimeout(),t}endSession(){this.currentSession&&(this.currentSession.endTime=Date.now(),this.sessions.push({...this.currentSession}),this.sessions.length>50&&(this.sessions=this.sessions.slice(-50)),this.config.enableRealTimeAnalysis&&this.analyzeSession(this.currentSession),this.currentSession=null,this.clearSessionTimeout())}trackInteraction(e){try{u.parse(e),this.currentSession||this.startSession(),this.currentSession.interactions.push(e),this.resetSessionTimeout();const t={eventId:crypto.randomUUID(),sessionId:this.currentSession.sessionId,timestamp:e.timestamp,event:`interaction.${e.type}`,properties:{target:e.target,value:e.value},context:e.context};this.trackEvent(t),this.config.enableRealTimeAnalysis&&this.detectRealTimePatterns()}catch(t){this.emit("error",t instanceof Error?t:new Error(String(t)))}}trackEvent(e){try{const t={eventId:e.eventId||crypto.randomUUID(),sessionId:e.sessionId||this.currentSession?.sessionId||"no-session",timestamp:e.timestamp||Date.now(),event:e.event||"unknown",properties:e.properties,context:e.context};m.parse(t),this.currentSession&&this.currentSession.events.push(t),this.eventBuffer.push(t),this.eventBuffer.length>this.config.bufferSize&&(this.eventBuffer=this.eventBuffer.slice(-this.config.bufferSize)),this.emit("data-collected",t)}catch(t){this.emit("error",t instanceof Error?t:new Error(String(t)))}}trackPreferenceChange(e,t){const s={timestamp:Date.now(),type:"preference_change",value:e,context:t};this.trackInteraction(s)}getSessionMetrics(){if(!this.currentSession)return null;const e=this.currentSession.interactions,t=Date.now()-this.currentSession.startTime;return this.calculateInteractionMetrics(e,t)}getEngagementMetrics(){if(!this.currentSession)return null;const e=this.currentSession.events,t=Date.now()-this.currentSession.startTime;return this.calculateEngagementMetrics(e,t)}analyzeBehaviorPatterns(){const e=[];return e.push(...this.detectNavigationPatterns()),e.push(...this.detectInteractionPatterns()),e.push(...this.detectTemporalPatterns()),e.forEach(t=>{this.patternCache.set(t.id,t)}),e}generateInsights(){const e=[];return e.push(...this.generateAccessibilityInsights()),e.push(...this.generateUsabilityInsights()),e.push(...this.generatePerformanceInsights()),e.push(...this.generateEngagementInsights()),e.forEach(t=>{this.emit("insight-generated",t)}),e}exportData(e={}){const t={sessions:this.sessions.map(s=>({...s,interactions:e.includeInteractions?s.interactions:[]})),patterns:Array.from(this.patternCache.values()),summary:this.generateSummaryStatistics()};return e.format==="csv"?this.convertToCSV(t):JSON.stringify(t,null,2)}clearData(){this.sessions=[],this.eventBuffer=[],this.patternCache.clear(),this.anomalyBaselines.clear(),this.currentSession&&this.endSession()}destroy(){this.endSession(),this.clearSessionTimeout(),this.removeAllListeners()}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}getViewportInfo(){return typeof window<"u"?{width:window.innerWidth,height:window.innerHeight}:{width:0,height:0}}resetSessionTimeout(){this.clearSessionTimeout(),this.sessionTimeout=setTimeout(()=>{this.endSession()},this.config.sessionTimeout)}clearSessionTimeout(){this.sessionTimeout&&(clearTimeout(this.sessionTimeout),this.sessionTimeout=void 0)}calculateInteractionMetrics(e,t){const s=e.length/t*6e4;let i=0;for(let r=1;r<e.length;r++)i+=e[r].timestamp-e[r-1].timestamp;const a=i/Math.max(1,e.length-1),o=new Map;e.forEach(r=>{o.set(r.type,(o.get(r.type)||0)+1)});const c=Array.from(o.entries()).sort(([,r],[,h])=>h-r).slice(0,3).map(([r])=>r);return{totalInteractions:e.length,interactionRate:s,avgTimeBetweenInteractions:a,dominantInteractionTypes:c,errorRate:0,completionRate:.85}}calculateEngagementMetrics(e,t){const s=e.filter(o=>o.event.includes("page")).length,i=e.filter(o=>o.event.includes("scroll")).length,a=e.filter(o=>o.event.includes("click")).length;return{sessionDuration:t,bounceRate:s<=1?1:0,pageViews:s,scrollDepth:Math.min(i/10,1),clickThroughRate:a/Math.max(1,s),taskCompletionRate:.8}}detectRealTimePatterns(){if(!this.currentSession||this.currentSession.interactions.length<5)return;const e=this.currentSession.interactions.slice(-10),t=e.filter(s=>s.type==="scroll");if(t.length>5){const s={id:`rapid_scroll_${Date.now()}`,type:"interaction",pattern:"rapid_scrolling",confidence:.8,frequency:t.length/e.length,contexts:["current_session"],impact:"negative",recommendations:["Enable motion reduction","Increase content chunking"]};this.emit("pattern-detected",s)}}analyzeSession(e){const t=e.endTime||Date.now(),s=this.calculateInteractionMetrics(e.interactions,t-e.startTime);if(s.interactionRate>10){const i={id:`high_interaction_rate_${e.sessionId}`,type:"unusual_pattern",description:"Unusually high interaction rate detected",severity:"medium",affectedMetrics:["interaction_rate"],timestamp:t,context:{sessionId:e.sessionId,rate:s.interactionRate}};this.emit("anomaly-detected",i)}}detectNavigationPatterns(){return[{id:"common_navigation_path",type:"navigation",pattern:"home -> settings -> preferences",confidence:.75,frequency:.6,contexts:["multiple_sessions"],impact:"positive",recommendations:["Optimize preferences access"]}]}detectInteractionPatterns(){return[{id:"preference_adjustment_frequency",type:"preference",pattern:"frequent_font_size_changes",confidence:.8,frequency:.4,contexts:["accessibility_settings"],impact:"neutral",recommendations:["Provide font size presets","Add visual preview"]}]}detectTemporalPatterns(){return[{id:"time_of_day_usage",type:"temporal",pattern:"evening_high_contrast_preference",confidence:.7,frequency:.5,contexts:["evening_sessions"],impact:"positive",recommendations:["Auto-enable high contrast in evening"]}]}generateAccessibilityInsights(){return[{id:"font_size_insight",category:"accessibility",title:"Font Size Optimization Opportunity",description:"Users frequently adjust font size, indicating default may be suboptimal",evidence:["High frequency of font size changes","Consistent upward adjustments"],actionItems:["Increase default font size","Add font size presets"],priority:.8,confidence:.75}]}generateUsabilityInsights(){return[{id:"navigation_efficiency",category:"usability",title:"Settings Access Could Be Streamlined",description:"Users take multiple steps to reach accessibility settings",evidence:["Long navigation paths to preferences","Multiple back-and-forth patterns"],actionItems:["Add accessibility quick toggle","Improve settings discoverability"],priority:.6,confidence:.7}]}generatePerformanceInsights(){return[{id:"interaction_lag",category:"performance",title:"Potential Input Lag Detected",description:"Longer than expected intervals between user actions and responses",evidence:["Delayed interaction patterns","Repeated identical actions"],actionItems:["Optimize response times","Add loading indicators"],priority:.7,confidence:.65}]}generateEngagementInsights(){return[{id:"session_length",category:"engagement",title:"Positive Engagement with Accessibility Features",description:"Users spend significant time exploring accessibility options",evidence:["Long sessions in accessibility settings","Multiple feature trials"],actionItems:["Highlight successful adaptations","Provide usage tips"],priority:.5,confidence:.8}]}generateSummaryStatistics(){const e=this.sessions.length,t=this.sessions.reduce((i,a)=>i+a.interactions.length,0),s=this.sessions.reduce((i,a)=>i+((a.endTime||Date.now())-a.startTime),0)/Math.max(1,e);return{totalSessions:e,totalInteractions:t,avgSessionDuration:s,avgInteractionsPerSession:t/Math.max(1,e),patternsDetected:this.patternCache.size}}convertToCSV(e){const t=["sessionId","startTime","endTime","interactionCount","eventCount"],s=e.sessions.map(i=>[i.sessionId,i.startTime,i.endTime||"",i.interactions.length,i.events.length]);return[t.join(","),...s.map(i=>i.join(","))].join(` `)}}exports.BehaviorAnalytics=p; //# sourceMappingURL=behavior-analytics-DOSpG3Xv.cjs.map