@neuroequality/neuroadapt-ai
Version:
AI-powered accessibility personalization for neurodivergent users
389 lines (388 loc) • 13.1 kB
JavaScript
import { EventEmitter as l } from "eventemitter3";
import { z as n } from "zod";
const u = n.object({
timestamp: n.number(),
type: n.enum(["click", "scroll", "focus", "blur", "keypress", "resize", "preference_change"]),
target: n.string().optional(),
value: n.unknown().optional(),
context: n.record(n.string(), n.unknown()).optional()
}), m = n.object({
eventId: n.string().uuid(),
userId: n.string().optional(),
sessionId: n.string(),
timestamp: n.number(),
event: n.string(),
properties: n.record(n.string(), n.unknown()).optional(),
preferences: n.record(n.string(), n.unknown()).optional(),
context: n.object({
userAgent: n.string().optional(),
viewport: n.object({
width: n.number(),
height: n.number()
}).optional(),
url: n.string().optional(),
referrer: n.string().optional()
}).optional()
});
class f extends l {
constructor(e = {}) {
super(), this.currentSession = null, this.sessions = [], this.eventBuffer = [], this.patternCache = /* @__PURE__ */ new Map(), this.anomalyBaselines = /* @__PURE__ */ new Map(), this.config = {
sessionTimeout: e.sessionTimeout || 18e5,
// 30 minutes
maxSessionEvents: e.maxSessionEvents || 1e3,
enableRealTimeAnalysis: e.enableRealTimeAnalysis ?? !0,
patternDetectionThreshold: e.patternDetectionThreshold || 0.7,
anomalyDetectionSensitivity: e.anomalyDetectionSensitivity || 0.8,
privacyMode: e.privacyMode ?? !0,
bufferSize: e.bufferSize || 100
};
}
/**
* Start a new analytics session
*/
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;
}
/**
* End current session
*/
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());
}
/**
* Track user interaction
*/
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)));
}
}
/**
* Track custom analytics event
*/
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)));
}
}
/**
* Track preference change
*/
trackPreferenceChange(e, t) {
const s = {
timestamp: Date.now(),
type: "preference_change",
value: e,
context: t
};
this.trackInteraction(s);
}
/**
* Get interaction metrics for current session
*/
getSessionMetrics() {
if (!this.currentSession) return null;
const e = this.currentSession.interactions, t = Date.now() - this.currentSession.startTime;
return this.calculateInteractionMetrics(e, t);
}
/**
* Get engagement metrics for current session
*/
getEngagementMetrics() {
if (!this.currentSession) return null;
const e = this.currentSession.events, t = Date.now() - this.currentSession.startTime;
return this.calculateEngagementMetrics(e, t);
}
/**
* Analyze behavior patterns across sessions
*/
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;
}
/**
* Generate behavioral insights
*/
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;
}
/**
* Export analytics data
*/
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);
}
/**
* Clear all analytics data
*/
clearData() {
this.sessions = [], this.eventBuffer = [], this.patternCache.clear(), this.anomalyBaselines.clear(), this.currentSession && this.endSession();
}
/**
* Clean up and stop analytics
*/
destroy() {
this.endSession(), this.clearSessionTimeout(), this.removeAllListeners();
}
// Private methods
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 = /* @__PURE__ */ 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,
// Would calculate based on error events
completionRate: 0.85
// Placeholder - would calculate based on goal completion
};
}
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),
// Normalized
clickThroughRate: a / Math.max(1, s),
taskCompletionRate: 0.8
// Placeholder
};
}
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: 0.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: 0.75,
frequency: 0.6,
contexts: ["multiple_sessions"],
impact: "positive",
recommendations: ["Optimize preferences access"]
}
];
}
detectInteractionPatterns() {
return [
{
id: "preference_adjustment_frequency",
type: "preference",
pattern: "frequent_font_size_changes",
confidence: 0.8,
frequency: 0.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: 0.7,
frequency: 0.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: 0.8,
confidence: 0.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: 0.6,
confidence: 0.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: 0.7,
confidence: 0.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: 0.5,
confidence: 0.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(`
`);
}
}
export {
f as B
};
//# sourceMappingURL=behavior-analytics-Dfx-HuYy.js.map