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:
324
admin-ui/js/core/messaging.js
Normal file
324
admin-ui/js/core/messaging.js
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* DSS Notification Service
|
||||
*
|
||||
* Centralized messaging system with structured formats, error taxonomy,
|
||||
* and correlation IDs for enterprise-grade error tracking and user feedback.
|
||||
*
|
||||
* @module messaging
|
||||
*/
|
||||
|
||||
// Event bus for pub/sub notifications
|
||||
const bus = new EventTarget();
|
||||
|
||||
// Event name constant
|
||||
export const NOTIFICATION_EVENT = 'dss-notification';
|
||||
|
||||
/**
|
||||
* Notification severity types
|
||||
*/
|
||||
export const NotificationType = {
|
||||
SUCCESS: 'success',
|
||||
ERROR: 'error',
|
||||
WARNING: 'warning',
|
||||
INFO: 'info',
|
||||
};
|
||||
|
||||
/**
|
||||
* Error taxonomy for structured error handling
|
||||
*/
|
||||
export const ErrorCode = {
|
||||
// User errors (E1xxx)
|
||||
USER_INPUT_INVALID: 'E1001',
|
||||
USER_ACTION_FORBIDDEN: 'E1002',
|
||||
USER_NOT_AUTHENTICATED: 'E1003',
|
||||
|
||||
// Validation errors (E2xxx)
|
||||
VALIDATION_FAILED: 'E2001',
|
||||
VALIDATION_MISSING_FIELD: 'E2002',
|
||||
VALIDATION_INVALID_FORMAT: 'E2003',
|
||||
|
||||
// API errors (E3xxx)
|
||||
API_REQUEST_FAILED: 'E3001',
|
||||
API_TIMEOUT: 'E3002',
|
||||
API_UNAUTHORIZED: 'E3003',
|
||||
API_NOT_FOUND: 'E3004',
|
||||
API_SERVER_ERROR: 'E3005',
|
||||
|
||||
// System errors (E4xxx)
|
||||
SYSTEM_UNEXPECTED: 'E4001',
|
||||
SYSTEM_NETWORK: 'E4002',
|
||||
SYSTEM_STORAGE: 'E4003',
|
||||
|
||||
// Integration errors (E5xxx)
|
||||
FIGMA_CONNECTION_FAILED: 'E5001',
|
||||
FIGMA_INVALID_KEY: 'E5002',
|
||||
FIGMA_API_ERROR: 'E5003',
|
||||
|
||||
// Success codes (S1xxx)
|
||||
SUCCESS_OPERATION: 'S1001',
|
||||
SUCCESS_CREATED: 'S1002',
|
||||
SUCCESS_UPDATED: 'S1003',
|
||||
SUCCESS_DELETED: 'S1004',
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate correlation ID for request tracking
|
||||
* @returns {string} UUID v4 correlation ID
|
||||
*/
|
||||
function generateCorrelationId() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
const r = Math.random() * 16 | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Message queue for persistence
|
||||
*/
|
||||
class MessageQueue {
|
||||
constructor(maxSize = 50) {
|
||||
this.maxSize = maxSize;
|
||||
this.storageKey = 'dss_message_queue';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add message to queue
|
||||
* @param {Object} message - Notification message
|
||||
*/
|
||||
add(message) {
|
||||
try {
|
||||
const queue = this.getAll();
|
||||
queue.unshift(message);
|
||||
|
||||
// Keep only last maxSize messages
|
||||
const trimmed = queue.slice(0, this.maxSize);
|
||||
|
||||
localStorage.setItem(this.storageKey, JSON.stringify(trimmed));
|
||||
} catch (error) {
|
||||
console.warn('Failed to persist message to queue:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all messages from queue
|
||||
* @returns {Array} Array of messages
|
||||
*/
|
||||
getAll() {
|
||||
try {
|
||||
const data = localStorage.getItem(this.storageKey);
|
||||
return data ? JSON.parse(data) : [];
|
||||
} catch (error) {
|
||||
console.warn('Failed to read message queue:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the message queue
|
||||
*/
|
||||
clear() {
|
||||
try {
|
||||
localStorage.removeItem(this.storageKey);
|
||||
} catch (error) {
|
||||
console.warn('Failed to clear message queue:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent errors for debugging
|
||||
* @param {number} limit - Max number of errors to return
|
||||
* @returns {Array} Recent error messages
|
||||
*/
|
||||
getRecentErrors(limit = 10) {
|
||||
const queue = this.getAll();
|
||||
return queue
|
||||
.filter(msg => msg.type === NotificationType.ERROR)
|
||||
.slice(0, limit);
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton message queue
|
||||
const messageQueue = new MessageQueue();
|
||||
|
||||
/**
|
||||
* Send a notification
|
||||
*
|
||||
* @param {Object} detail - Notification details
|
||||
* @param {string} detail.message - User-facing message
|
||||
* @param {NotificationType} [detail.type=INFO] - Notification type
|
||||
* @param {string} [detail.code] - Machine-readable error code
|
||||
* @param {Object} [detail.metadata] - Additional context for logging
|
||||
* @param {string} [detail.correlationId] - Optional correlation ID (auto-generated if not provided)
|
||||
* @param {number} [detail.duration] - Auto-dismiss duration in ms (0 = no auto-dismiss)
|
||||
*
|
||||
* @example
|
||||
* notify({
|
||||
* message: 'Project created successfully',
|
||||
* type: NotificationType.SUCCESS,
|
||||
* code: ErrorCode.SUCCESS_CREATED,
|
||||
* metadata: { projectId: 'demo-ds' }
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* notify({
|
||||
* message: 'Failed to connect to Figma',
|
||||
* type: NotificationType.ERROR,
|
||||
* code: ErrorCode.FIGMA_CONNECTION_FAILED,
|
||||
* metadata: { fileKey: 'abc123', endpoint: '/figma/file' },
|
||||
* correlationId: 'custom-id-123'
|
||||
* });
|
||||
*/
|
||||
export function notify(detail) {
|
||||
const notification = {
|
||||
id: generateCorrelationId(),
|
||||
message: detail.message,
|
||||
type: detail.type || NotificationType.INFO,
|
||||
code: detail.code || null,
|
||||
metadata: detail.metadata || {},
|
||||
correlationId: detail.correlationId || generateCorrelationId(),
|
||||
timestamp: new Date().toISOString(),
|
||||
duration: detail.duration !== undefined ? detail.duration : 5000, // Default 5s
|
||||
};
|
||||
|
||||
// Persist to queue
|
||||
messageQueue.add(notification);
|
||||
|
||||
// Dispatch event
|
||||
bus.dispatchEvent(new CustomEvent(NOTIFICATION_EVENT, {
|
||||
detail: notification
|
||||
}));
|
||||
|
||||
// Log for debugging
|
||||
const logMethod = notification.type === NotificationType.ERROR ? 'error' : 'log';
|
||||
console[logMethod]('[DSS Notification]', {
|
||||
message: notification.message,
|
||||
code: notification.code,
|
||||
correlationId: notification.correlationId,
|
||||
metadata: notification.metadata,
|
||||
});
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to notifications
|
||||
*
|
||||
* @param {Function} callback - Called when notification is sent
|
||||
* @returns {Function} Unsubscribe function
|
||||
*
|
||||
* @example
|
||||
* const unsubscribe = subscribe((notification) => {
|
||||
* console.log('Notification:', notification.message);
|
||||
* });
|
||||
*
|
||||
* // Later: unsubscribe();
|
||||
*/
|
||||
export function subscribe(callback) {
|
||||
const handler = (event) => callback(event.detail);
|
||||
bus.addEventListener(NOTIFICATION_EVENT, handler);
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => {
|
||||
bus.removeEventListener(NOTIFICATION_EVENT, handler);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Send success notification
|
||||
* @param {string} message - Success message
|
||||
* @param {string} [code] - Success code
|
||||
* @param {Object} [metadata] - Additional context
|
||||
*/
|
||||
export function notifySuccess(message, code = ErrorCode.SUCCESS_OPERATION, metadata = {}) {
|
||||
return notify({
|
||||
message,
|
||||
type: NotificationType.SUCCESS,
|
||||
code,
|
||||
metadata,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Send error notification
|
||||
* @param {string} message - Error message
|
||||
* @param {string} [code] - Error code
|
||||
* @param {Object} [metadata] - Additional context
|
||||
*/
|
||||
export function notifyError(message, code = ErrorCode.SYSTEM_UNEXPECTED, metadata = {}) {
|
||||
return notify({
|
||||
message,
|
||||
type: NotificationType.ERROR,
|
||||
code,
|
||||
metadata,
|
||||
duration: 0, // Errors don't auto-dismiss
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Send warning notification
|
||||
* @param {string} message - Warning message
|
||||
* @param {Object} [metadata] - Additional context
|
||||
*/
|
||||
export function notifyWarning(message, metadata = {}) {
|
||||
return notify({
|
||||
message,
|
||||
type: NotificationType.WARNING,
|
||||
metadata,
|
||||
duration: 7000, // Warnings stay a bit longer
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Send info notification
|
||||
* @param {string} message - Info message
|
||||
* @param {Object} [metadata] - Additional context
|
||||
*/
|
||||
export function notifyInfo(message, metadata = {}) {
|
||||
return notify({
|
||||
message,
|
||||
type: NotificationType.INFO,
|
||||
metadata,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message history
|
||||
* @returns {Array} All persisted messages
|
||||
*/
|
||||
export function getMessageHistory() {
|
||||
return messageQueue.getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent errors for debugging
|
||||
* @param {number} limit - Max number of errors
|
||||
* @returns {Array} Recent errors
|
||||
*/
|
||||
export function getRecentErrors(limit = 10) {
|
||||
return messageQueue.getRecentErrors(limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear message history
|
||||
*/
|
||||
export function clearMessageHistory() {
|
||||
messageQueue.clear();
|
||||
}
|
||||
|
||||
// Export singleton queue for advanced use cases
|
||||
export { messageQueue };
|
||||
|
||||
export default {
|
||||
notify,
|
||||
subscribe,
|
||||
notifySuccess,
|
||||
notifyError,
|
||||
notifyWarning,
|
||||
notifyInfo,
|
||||
getMessageHistory,
|
||||
getRecentErrors,
|
||||
clearMessageHistory,
|
||||
NotificationType,
|
||||
ErrorCode,
|
||||
};
|
||||
Reference in New Issue
Block a user