Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
194 lines
4.5 KiB
JavaScript
194 lines
4.5 KiB
JavaScript
/**
|
|
* Workflow Persistence - Phase 8 Enterprise Pattern
|
|
*
|
|
* Saves and restores workflow states to localStorage and server,
|
|
* enabling crash recovery and session restoration.
|
|
*/
|
|
|
|
import store from '../stores/app-store.js';
|
|
|
|
class WorkflowPersistence {
|
|
constructor() {
|
|
this.storageKey = 'dss-workflow-state';
|
|
this.maxSnapshots = 10;
|
|
this.autoSaveInterval = 30000; // 30 seconds
|
|
this.isAutosaving = false;
|
|
}
|
|
|
|
/**
|
|
* Take a workflow snapshot of current state
|
|
*/
|
|
snapshot() {
|
|
const state = store.get();
|
|
const snapshot = {
|
|
id: `snapshot-${Date.now()}`,
|
|
timestamp: new Date().toISOString(),
|
|
data: {
|
|
currentPage: state.currentPage,
|
|
sidebarOpen: state.sidebarOpen,
|
|
user: state.user,
|
|
team: state.team,
|
|
role: state.role,
|
|
figmaConnected: state.figmaConnected,
|
|
figmaFileKey: state.figmaFileKey,
|
|
selectedProject: state.projects[0]?.id || null,
|
|
}
|
|
};
|
|
return snapshot;
|
|
}
|
|
|
|
/**
|
|
* Save snapshot to localStorage with versioning
|
|
*/
|
|
saveSnapshot(snapshot = null) {
|
|
try {
|
|
const snap = snapshot || this.snapshot();
|
|
const snapshots = this.getSnapshots();
|
|
|
|
// Keep only latest N snapshots
|
|
snapshots.unshift(snap);
|
|
snapshots.splice(this.maxSnapshots);
|
|
|
|
localStorage.setItem(this.storageKey, JSON.stringify(snapshots));
|
|
return snap.id;
|
|
} catch (e) {
|
|
console.error('[WorkflowPersistence] Failed to save snapshot:', e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all saved snapshots
|
|
*/
|
|
getSnapshots() {
|
|
try {
|
|
const stored = localStorage.getItem(this.storageKey);
|
|
return stored ? JSON.parse(stored) : [];
|
|
} catch (e) {
|
|
console.warn('[WorkflowPersistence] Failed to load snapshots:', e);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get latest snapshot
|
|
*/
|
|
getLatestSnapshot() {
|
|
const snapshots = this.getSnapshots();
|
|
return snapshots[0] || null;
|
|
}
|
|
|
|
/**
|
|
* Get snapshot by ID
|
|
*/
|
|
getSnapshot(id) {
|
|
const snapshots = this.getSnapshots();
|
|
return snapshots.find(s => s.id === id) || null;
|
|
}
|
|
|
|
/**
|
|
* Restore workflow from snapshot
|
|
*/
|
|
restoreSnapshot(id) {
|
|
const snapshot = this.getSnapshot(id);
|
|
if (!snapshot) {
|
|
console.warn('[WorkflowPersistence] Snapshot not found:', id);
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const data = snapshot.data;
|
|
store.set({
|
|
currentPage: data.currentPage,
|
|
sidebarOpen: data.sidebarOpen,
|
|
user: data.user,
|
|
team: data.team,
|
|
role: data.role,
|
|
figmaConnected: data.figmaConnected,
|
|
figmaFileKey: data.figmaFileKey,
|
|
});
|
|
return true;
|
|
} catch (e) {
|
|
console.error('[WorkflowPersistence] Failed to restore snapshot:', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear all snapshots
|
|
*/
|
|
clearSnapshots() {
|
|
localStorage.removeItem(this.storageKey);
|
|
}
|
|
|
|
/**
|
|
* Delete specific snapshot
|
|
*/
|
|
deleteSnapshot(id) {
|
|
const snapshots = this.getSnapshots();
|
|
const filtered = snapshots.filter(s => s.id !== id);
|
|
localStorage.setItem(this.storageKey, JSON.stringify(filtered));
|
|
}
|
|
|
|
/**
|
|
* Start auto-saving workflow every N milliseconds
|
|
*/
|
|
startAutoSave(interval = this.autoSaveInterval) {
|
|
if (this.isAutosaving) return;
|
|
|
|
this.isAutosaving = true;
|
|
this.autoSaveTimer = setInterval(() => {
|
|
this.saveSnapshot();
|
|
}, interval);
|
|
|
|
console.log('[WorkflowPersistence] Auto-save enabled');
|
|
}
|
|
|
|
/**
|
|
* Stop auto-saving
|
|
*/
|
|
stopAutoSave() {
|
|
if (this.autoSaveTimer) {
|
|
clearInterval(this.autoSaveTimer);
|
|
this.isAutosaving = false;
|
|
console.log('[WorkflowPersistence] Auto-save disabled');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export snapshots as JSON file
|
|
*/
|
|
exportSnapshots() {
|
|
const snapshots = this.getSnapshots();
|
|
const data = {
|
|
exportDate: new Date().toISOString(),
|
|
version: '1.0',
|
|
snapshots: snapshots
|
|
};
|
|
return JSON.stringify(data, null, 2);
|
|
}
|
|
|
|
/**
|
|
* Import snapshots from JSON data
|
|
*/
|
|
importSnapshots(jsonData) {
|
|
try {
|
|
const data = JSON.parse(jsonData);
|
|
if (!Array.isArray(data.snapshots)) {
|
|
throw new Error('Invalid snapshot format');
|
|
}
|
|
localStorage.setItem(this.storageKey, JSON.stringify(data.snapshots));
|
|
return true;
|
|
} catch (e) {
|
|
console.error('[WorkflowPersistence] Failed to import snapshots:', e);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create and export singleton
|
|
const persistence = new WorkflowPersistence();
|
|
|
|
export { WorkflowPersistence };
|
|
export default persistence;
|