@neuroequality/neuroadapt-core
Version:
Core sensory, cognitive, and preference management for NeuroAdapt SDK
209 lines (208 loc) • 6.33 kB
JavaScript
import { EventEmitter } from "eventemitter3";
import { ZodError } from "zod";
import { PreferencesUpdateSchema, PreferencesSchema, SCHEMA_VERSION } from "./schemas.js";
import { LocalStoragePreferenceStorage } from "./storage.js";
import { defaultMigrationRegistry } from "./migration.js";
class PreferenceStore extends EventEmitter {
constructor(config = {}) {
super();
this.isInitialized = false;
this.storage = config.storage || new LocalStoragePreferenceStorage();
this.migrationRegistry = config.migrationRegistry || defaultMigrationRegistry;
this.autoSave = config.autoSave ?? true;
this.storageKey = config.storageKey || "default";
this.preferences = this.getDefaultPreferences();
}
/**
* Initialize the store by loading preferences from storage
*/
async initialize() {
try {
const stored = await this.storage.get(this.storageKey);
if (stored) {
this.preferences = await this.migrationRegistry.migrate(stored);
this.emit("loaded", this.preferences);
}
this.isInitialized = true;
} catch (error) {
const errorObj = error instanceof Error ? error : new Error(String(error));
this.emit("error", errorObj);
this.isInitialized = true;
}
}
/**
* Get current preferences
*/
getPreferences() {
return structuredClone(this.preferences);
}
/**
* Update preferences with validation
*/
async updatePreferences(updates) {
if (!this.isInitialized) {
await this.initialize();
}
try {
const validatedUpdate = PreferencesUpdateSchema.parse(updates);
const previousState = structuredClone(this.preferences);
const newPreferences = this.mergePreferences(this.preferences, validatedUpdate);
const validatedPreferences = PreferencesSchema.parse(newPreferences);
validatedPreferences.lastModified = (/* @__PURE__ */ new Date()).toISOString();
this.preferences = validatedPreferences;
this.emit("change", updates, previousState);
if (this.autoSave) {
await this.save();
}
} catch (error) {
if (error instanceof ZodError) {
const validationErrors = error.errors.map((err) => ({
path: err.path.map(String),
message: err.message,
code: err.code
}));
this.emit("invalid", validationErrors);
throw new Error(`Validation failed: ${validationErrors.map((e) => e.message).join(", ")}`);
}
throw error;
}
}
/**
* Save preferences to storage
*/
async save() {
try {
await this.storage.set(this.storageKey, this.preferences);
this.emit("saved", this.preferences);
} catch (error) {
const errorObj = error instanceof Error ? error : new Error(String(error));
this.emit("error", errorObj);
throw errorObj;
}
}
/**
* Reset preferences to defaults
*/
async reset() {
const previousState = structuredClone(this.preferences);
this.preferences = this.getDefaultPreferences();
this.emit("change", this.preferences, previousState);
if (this.autoSave) {
await this.save();
}
}
/**
* Export preferences as JSON
*/
export() {
return JSON.stringify(this.preferences, null, 2);
}
/**
* Import preferences from JSON
*/
async import(json) {
try {
const imported = JSON.parse(json);
const migrated = await this.migrationRegistry.migrate(imported);
const validated = PreferencesSchema.parse(migrated);
const previousState = structuredClone(this.preferences);
this.preferences = validated;
this.emit("change", validated, previousState);
if (this.autoSave) {
await this.save();
}
} catch (error) {
if (error instanceof SyntaxError) {
throw new Error("Invalid JSON format");
}
if (error instanceof ZodError) {
const validationErrors = error.errors.map((err) => ({
path: err.path.map(String),
message: err.message,
code: err.code
}));
this.emit("invalid", validationErrors);
throw new Error(`Invalid preferences format: ${validationErrors.map((e) => e.message).join(", ")}`);
}
throw error;
}
}
/**
* Get specific preference section
*/
getSensoryPreferences() {
return structuredClone(this.preferences.sensory);
}
getCognitivePreferences() {
return structuredClone(this.preferences.cognitive);
}
getAIPreferences() {
return structuredClone(this.preferences.ai);
}
getVRPreferences() {
return structuredClone(this.preferences.vr);
}
/**
* Check if store is initialized
*/
isReady() {
return this.isInitialized;
}
/**
* Deep merge preferences with updates
*/
mergePreferences(current, updates) {
return {
...current,
...updates,
sensory: updates.sensory ? { ...current.sensory, ...updates.sensory } : current.sensory,
cognitive: updates.cognitive ? { ...current.cognitive, ...updates.cognitive } : current.cognitive,
ai: updates.ai ? { ...current.ai, ...updates.ai } : current.ai,
vr: updates.vr ? { ...current.vr, ...updates.vr } : current.vr,
metadata: updates.metadata ? { ...current.metadata, ...updates.metadata } : current.metadata
};
}
/**
* Get default preferences
*/
getDefaultPreferences() {
return {
schemaVersion: SCHEMA_VERSION,
lastModified: (/* @__PURE__ */ new Date()).toISOString(),
sensory: {
motionReduction: false,
highContrast: false,
colorVisionFilter: "none",
fontSize: 1,
reducedFlashing: false,
darkMode: false
},
cognitive: {
readingSpeed: "medium",
explanationLevel: "detailed",
processingPace: "standard",
chunkSize: 5,
allowInterruptions: true,
preferVisualCues: false
},
ai: {
tone: "neutral",
responseLength: "standard",
consistencyLevel: "moderate",
useAnalogies: true,
allowUndo: true
},
vr: {
comfortRadius: 1.5,
safeSpaceEnabled: true,
locomotionType: "comfort",
personalSpace: 1,
panicButtonEnabled: true
}
};
}
}
export {
PreferenceStore
};
//# sourceMappingURL=store.js.map