UNPKG

@myea/wordpress-mcp-handler

Version:

Advanced WordPress MCP request handler with intelligent search, multi-locale support, and comprehensive content management capabilities

202 lines 8.19 kB
export class WordPressOperationError extends Error { code; details; recoverable; retryAfter; constructor(error) { super(error.message); this.name = 'WordPressOperationError'; this.code = error.code; this.details = error.details; this.recoverable = error.recoverable; this.retryAfter = error.retryAfter; } } export const WP_ERROR_CODES = { // Network & Connection Errors CONNECTION_FAILED: 'CONNECTION_FAILED', TIMEOUT: 'TIMEOUT', AUTHENTICATION_FAILED: 'AUTHENTICATION_FAILED', UNAUTHORIZED: 'UNAUTHORIZED', // Validation Errors INVALID_POST_TYPE: 'INVALID_POST_TYPE', INVALID_STATUS: 'INVALID_STATUS', INVALID_PARAMETERS: 'INVALID_PARAMETERS', INVALID_MIME_TYPE: 'INVALID_MIME_TYPE', INVALID_USER: 'INVALID_USER', // Content Errors POST_NOT_FOUND: 'POST_NOT_FOUND', PAGE_NOT_FOUND: 'PAGE_NOT_FOUND', MEDIA_NOT_FOUND: 'MEDIA_NOT_FOUND', USER_NOT_FOUND: 'USER_NOT_FOUND', CATEGORY_NOT_FOUND: 'CATEGORY_NOT_FOUND', TAG_NOT_FOUND: 'TAG_NOT_FOUND', COMMENT_NOT_FOUND: 'COMMENT_NOT_FOUND', // Operation Errors CREATE_FAILED: 'CREATE_FAILED', UPDATE_FAILED: 'UPDATE_FAILED', DELETE_FAILED: 'DELETE_FAILED', UPLOAD_FAILED: 'UPLOAD_FAILED', PUBLISH_FAILED: 'PUBLISH_FAILED', QUERY_FAILED: 'QUERY_FAILED', // WordPress Specific Errors PLUGIN_ERROR: 'PLUGIN_ERROR', THEME_ERROR: 'THEME_ERROR', MULTISITE_ERROR: 'MULTISITE_ERROR', CAPABILITY_ERROR: 'CAPABILITY_ERROR', // System Errors INSUFFICIENT_PERMISSIONS: 'INSUFFICIENT_PERMISSIONS', SYSTEM_ERROR: 'SYSTEM_ERROR', RATE_LIMITED: 'RATE_LIMITED', FILE_TOO_LARGE: 'FILE_TOO_LARGE', DISK_SPACE_FULL: 'DISK_SPACE_FULL', }; /** * Create standardized error responses for WordPress operations */ export function createWordPressError(code, message, details, recoverable = false, retryAfter) { return new WordPressOperationError({ code, message, details, recoverable, retryAfter }); } /** * Handle HTTP errors from WordPress REST API responses */ export function handleWordPressHttpError(error, operation) { if (error.response) { const status = error.response.status; const data = error.response.data; const wpErrorCode = data?.code; const wpErrorMessage = data?.message; switch (status) { case 400: return createWordPressError(WP_ERROR_CODES.INVALID_PARAMETERS, wpErrorMessage || 'Invalid request parameters.', { status, data, wpErrorCode }); case 401: return createWordPressError(WP_ERROR_CODES.AUTHENTICATION_FAILED, 'Authentication failed. Check WordPress credentials.', { status, data, wpErrorCode }); case 403: return createWordPressError(WP_ERROR_CODES.INSUFFICIENT_PERMISSIONS, wpErrorMessage || 'Insufficient permissions for this operation.', { status, data, operation, wpErrorCode }); case 404: const notFoundCode = getNotFoundErrorCode(operation); return createWordPressError(notFoundCode, wpErrorMessage || 'Resource not found in WordPress.', { status, data, operation, wpErrorCode }); case 413: return createWordPressError(WP_ERROR_CODES.FILE_TOO_LARGE, 'File too large for upload.', { status, data, wpErrorCode }); case 415: return createWordPressError(WP_ERROR_CODES.INVALID_MIME_TYPE, 'Unsupported media type.', { status, data, wpErrorCode }); case 429: const retryAfter = error.response.headers['retry-after']; return createWordPressError(WP_ERROR_CODES.RATE_LIMITED, 'Rate limit exceeded. Please try again later.', { status, data, wpErrorCode }, true, retryAfter ? parseInt(retryAfter) * 1000 : 60000); case 500: return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, wpErrorMessage || 'WordPress internal server error.', { status, data, wpErrorCode }, true, 30000); case 502: case 503: return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, 'WordPress service temporarily unavailable.', { status, data, wpErrorCode }, true, 30000); default: return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, `HTTP ${status}: ${wpErrorMessage || data?.message || 'Unknown error'}`, { status, data, operation, wpErrorCode }); } } else if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') { return createWordPressError(WP_ERROR_CODES.CONNECTION_FAILED, 'Cannot connect to WordPress instance. Check host and network.', { originalError: error.message }, true, 5000); } else if (error.code === 'ETIMEDOUT') { return createWordPressError(WP_ERROR_CODES.TIMEOUT, 'Request to WordPress timed out.', { originalError: error.message }, true, 10000); } else { return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, `Unexpected error during ${operation}: ${error.message}`, { originalError: error.message }); } } /** * Get appropriate not found error code based on operation */ function getNotFoundErrorCode(operation) { if (operation.includes('post')) return WP_ERROR_CODES.POST_NOT_FOUND; if (operation.includes('page')) return WP_ERROR_CODES.PAGE_NOT_FOUND; if (operation.includes('media')) return WP_ERROR_CODES.MEDIA_NOT_FOUND; if (operation.includes('user')) return WP_ERROR_CODES.USER_NOT_FOUND; if (operation.includes('category')) return WP_ERROR_CODES.CATEGORY_NOT_FOUND; if (operation.includes('tag')) return WP_ERROR_CODES.TAG_NOT_FOUND; if (operation.includes('comment')) return WP_ERROR_CODES.COMMENT_NOT_FOUND; return WP_ERROR_CODES.SYSTEM_ERROR; } /** * Safe execution wrapper with retry logic */ export async function safeExecute(operation, operationName, maxRetries = 3) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await operation(); } catch (error) { lastError = error instanceof WordPressOperationError ? error : handleWordPressHttpError(error, operationName); // Don't retry if error is not recoverable if (!lastError.recoverable || attempt === maxRetries) { break; } // Wait before retrying const delay = lastError.retryAfter || Math.pow(2, attempt) * 1000; console.warn(`[${operationName}] Attempt ${attempt} failed, retrying in ${delay}ms:`, lastError.message); await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } /** * Validate WordPress operation parameters */ export function validateWordPressOperation(postType, status, mimeType) { const errors = []; if (!postType || typeof postType !== 'string') { errors.push('Post type is required and must be a string'); } if (status && typeof status !== 'string') { errors.push('Status must be a string'); } if (mimeType && typeof mimeType !== 'string') { errors.push('MIME type must be a string'); } if (errors.length > 0) { throw createWordPressError(WP_ERROR_CODES.INVALID_PARAMETERS, 'Validation failed: ' + errors.join(', '), { errors }); } } /** * Create standardized success response */ export function createSuccessResponse(data, operation) { return { success: true, operation, timestamp: new Date().toISOString(), data }; } /** * Create standardized error response */ export function createErrorResponse(error, operation) { return { success: false, operation, timestamp: new Date().toISOString(), error: { code: error.code, message: error.message, details: error.details, recoverable: error.recoverable, retryAfter: error.retryAfter } }; } //# sourceMappingURL=error-handler.js.map