Initial commit: Clean DSS implementation

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
This commit is contained in:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

View File

@@ -0,0 +1,266 @@
/**
* Error Recovery - Phase 8 Enterprise Pattern
*
* Handles crashes, recovers lost state, and provides
* resilience against errors and edge cases.
*/
import store from '../stores/app-store.js';
import auditLogger from './audit-logger.js';
import persistence from './workflow-persistence.js';
class ErrorRecovery {
constructor() {
this.errorHandlers = new Map();
this.recoveryPoints = [];
this.maxRecoveryPoints = 5;
this.setupGlobalErrorHandlers();
}
/**
* Setup global error handlers
*/
setupGlobalErrorHandlers() {
// Unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
this.handleError(event.reason, 'unhandled_promise');
// Prevent default error handling
event.preventDefault();
});
// Global errors
window.addEventListener('error', (event) => {
this.handleError(event.error, 'global_error');
});
// Log before unload (potential crash)
window.addEventListener('beforeunload', () => {
persistence.saveSnapshot();
auditLogger.logAction('session_end', {
cleanShutdown: true
});
});
}
/**
* Register error handler for specific error type
*/
registerHandler(errorType, handler) {
this.handlers.set(errorType, handler);
}
/**
* Create recovery point
*/
createRecoveryPoint(label = '') {
const point = {
id: `recovery-${Date.now()}`,
label,
timestamp: new Date().toISOString(),
snapshot: persistence.snapshot(),
logs: auditLogger.getLogs({ limit: 50 }),
state: store.get()
};
this.recoveryPoints.unshift(point);
if (this.recoveryPoints.length > this.maxRecoveryPoints) {
this.recoveryPoints.pop();
}
auditLogger.logAction('recovery_point_created', { label });
return point.id;
}
/**
* Get recovery points
*/
getRecoveryPoints() {
return this.recoveryPoints;
}
/**
* Recover from recovery point
*/
recover(pointId) {
const point = this.recoveryPoints.find(p => p.id === pointId);
if (!point) {
auditLogger.logWarning('Recovery point not found', { pointId });
return false;
}
try {
// Restore workflow state
persistence.restoreSnapshot(point.snapshot.id);
auditLogger.logAction('recovered_from_point', {
pointId,
label: point.label
});
return true;
} catch (e) {
this.handleError(e, 'recovery_failed');
return false;
}
}
/**
* Check if app is in crashed state
*/
detectCrash() {
const lastSnapshot = persistence.getLatestSnapshot();
if (!lastSnapshot) return false;
const lastActivityTime = new Date(lastSnapshot.timestamp);
const now = new Date();
const timeSinceLastActivity = now - lastActivityTime;
// If no activity in last 5 minutes and session started, likely crashed
return timeSinceLastActivity > 5 * 60 * 1000;
}
/**
* Main error handler
*/
handleError(error, context = 'unknown') {
const errorId = auditLogger.logError(error, context);
// Create recovery point before handling
const recoveryId = this.createRecoveryPoint(`Error recovery: ${context}`);
// Categorize error
const category = this.categorizeError(error);
// Apply recovery strategy
const recovery = this.getRecoveryStrategy(category);
if (recovery) {
recovery.execute(error, store, auditLogger);
}
// Notify user
this.notifyUser(error, category, errorId);
return { errorId, recoveryId, category };
}
/**
* Categorize error
*/
categorizeError(error) {
if (error.message.includes('Network')) return 'network';
if (error.message.includes('timeout')) return 'timeout';
if (error.message.includes('Permission')) return 'permission';
if (error.message.includes('Authentication')) return 'auth';
if (error.message.includes('not found')) return 'notfound';
return 'unknown';
}
/**
* Get recovery strategy for error category
*/
getRecoveryStrategy(category) {
const strategies = {
network: {
execute: (error, store, logger) => {
store.notify('Network error - retrying...', 'warning');
logger.logWarning('Network error detected', { retrying: true });
},
retryable: true
},
timeout: {
execute: (error, store, logger) => {
store.notify('Request timeout - please try again', 'warning');
logger.logWarning('Request timeout', { timeout: true });
},
retryable: true
},
auth: {
execute: (error, store, logger) => {
store.notify('Authentication required - redirecting to login', 'error');
window.location.hash = '#/login';
},
retryable: false
},
permission: {
execute: (error, store, logger) => {
store.notify('Access denied', 'error');
logger.logWarning('Permission denied', { error: error.message });
},
retryable: false
},
notfound: {
execute: (error, store, logger) => {
store.notify('Resource not found', 'warning');
},
retryable: false
}
};
return strategies[category] || strategies.unknown;
}
/**
* Notify user of error
*/
notifyUser(error, category, errorId) {
const messages = {
network: 'Network connection error. Please check your internet.',
timeout: 'Request took too long. Please try again.',
permission: 'You do not have permission to perform this action.',
auth: 'Your session has expired. Please log in again.',
notfound: 'The resource you requested could not be found.',
unknown: 'An unexpected error occurred. Please try again.'
};
const message = messages[category] || messages.unknown;
store.notify(`${message} (Error: ${errorId})`, 'error', 5000);
}
/**
* Retry operation with exponential backoff
*/
async retry(operation, maxRetries = 3, initialDelay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (e) {
if (i === maxRetries - 1) {
throw e;
}
const delay = initialDelay * Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, delay));
auditLogger.logWarning('Retrying operation', { attempt: i + 1, maxRetries });
}
}
}
/**
* Get crash report
*/
getCrashReport() {
const logs = auditLogger.getLogs();
const errorLogs = logs.filter(l => l.level === 'error');
return {
timestamp: new Date().toISOString(),
sessionId: auditLogger.sessionId,
totalErrors: errorLogs.length,
errors: errorLogs.slice(0, 10),
recoveryPoints: this.recoveryPoints,
lastSnapshot: persistence.getLatestSnapshot(),
statistics: auditLogger.getStats()
};
}
/**
* Export crash report
*/
exportCrashReport() {
const report = this.getCrashReport();
return JSON.stringify(report, null, 2);
}
}
// Create and export singleton
const errorRecovery = new ErrorRecovery();
export { ErrorRecovery };
export default errorRecovery;