Assessor_2.0/src/app/engine-preferences.ts

60 lines
2.6 KiB
TypeScript

import { useSyncExternalStore } from 'react';
// Core-owned activation contract: engines consume it without depending on each other.
export const ENGINES = [
{ id: 'core', label: 'Core Engine', required: true },
{ id: 'context', label: 'Context Engine', required: false },
{ id: 'report', label: 'Report Engine', required: false },
{ id: 'remediation', label: 'Remediation Engine', required: false },
{ id: 'compliance', label: 'Compliance Engine', required: false }
] as const;
export type EngineId = typeof ENGINES[number]['id'];
export const ENGINE_PREFERENCES_KEY = 'ASSESSOR.engines.v1';
const defaults = Object.freeze({ core: true, context: true, report: true, remediation: true, compliance: true });
type Preferences = Readonly<Record<EngineId, boolean>>;
let snapshot: Preferences | undefined;
const listeners = new Set<() => void>();
export function readEnginePreferences(): Preferences {
if (snapshot) return snapshot;
try {
const saved = JSON.parse(window.localStorage.getItem(ENGINE_PREFERENCES_KEY) ?? '{}');
snapshot = Object.freeze({ core: true, context: saved?.context !== false, report: saved?.report !== false, remediation: saved?.remediation !== false,
compliance: saved?.compliance !== false });
} catch { snapshot = defaults; }
return snapshot;
}
export function setEngineEnabled(engine: EngineId, enabled: boolean): void {
if (engine === 'core') return;
const current = readEnginePreferences();
if (current[engine] === enabled) return;
snapshot = Object.freeze({ ...current, [engine]: enabled });
try { window.localStorage.setItem(ENGINE_PREFERENCES_KEY, JSON.stringify(snapshot)); }
catch { /* A blocked storage keeps the selection for this session. */ }
listeners.forEach(listener => listener());
}
function subscribe(listener: () => void) {
listeners.add(listener);
const changed = (event: StorageEvent) => {
if (event.key !== ENGINE_PREFERENCES_KEY && event.key !== null) return;
snapshot = undefined;
listener();
};
window.addEventListener('storage', changed);
return () => { listeners.delete(listener); window.removeEventListener('storage', changed); };
}
/** Calls the listener on every engine choice, including one made in another tab; returns the unsubscribe. */
export function subscribeEnginePreferences(listener: () => void): () => void {
return subscribe(listener);
}
export function useEnginePreferences(): Preferences {
return useSyncExternalStore(subscribe, readEnginePreferences, () => defaults);
}
/** Test support; call after unmounting consumers. */
export function resetEnginePreferencesForTests(): void { snapshot = undefined; }