Skip to main content
Glama
mutation-validator.js5.45 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.mutationValidator = exports.MutationValidator = exports.DEFAULT_MUTATION_TRACKING_OPTIONS = void 0; const crypto_1 = require("crypto"); exports.DEFAULT_MUTATION_TRACKING_OPTIONS = { enabled: true, maxWorkflowSizeKb: 500, validateQuality: true, sanitize: true, }; class MutationValidator { constructor(options = {}) { this.options = { ...exports.DEFAULT_MUTATION_TRACKING_OPTIONS, ...options }; } validate(data) { const errors = []; const warnings = []; if (!this.isValidWorkflow(data.workflowBefore)) { errors.push('Invalid workflow_before structure'); } if (!this.isValidWorkflow(data.workflowAfter)) { errors.push('Invalid workflow_after structure'); } const beforeSizeKb = this.getWorkflowSizeKb(data.workflowBefore); const afterSizeKb = this.getWorkflowSizeKb(data.workflowAfter); if (beforeSizeKb > this.options.maxWorkflowSizeKb) { errors.push(`workflow_before size (${beforeSizeKb}KB) exceeds maximum (${this.options.maxWorkflowSizeKb}KB)`); } if (afterSizeKb > this.options.maxWorkflowSizeKb) { errors.push(`workflow_after size (${afterSizeKb}KB) exceeds maximum (${this.options.maxWorkflowSizeKb}KB)`); } if (!this.hasMeaningfulChange(data.workflowBefore, data.workflowAfter)) { warnings.push('No meaningful change detected between before and after workflows'); } if (!data.userIntent || data.userIntent.trim().length === 0) { warnings.push('User intent is empty'); } else if (data.userIntent.trim().length < 5) { warnings.push('User intent is too short (less than 5 characters)'); } else if (data.userIntent.length > 1000) { warnings.push('User intent is very long (over 1000 characters)'); } if (!data.operations || data.operations.length === 0) { errors.push('No operations provided'); } if (data.validationBefore && data.validationAfter) { if (typeof data.validationBefore.valid !== 'boolean') { warnings.push('Invalid validation_before structure'); } if (typeof data.validationAfter.valid !== 'boolean') { warnings.push('Invalid validation_after structure'); } } if (data.durationMs !== undefined) { if (data.durationMs < 0) { errors.push('Duration cannot be negative'); } if (data.durationMs > 300000) { warnings.push('Duration is very long (over 5 minutes)'); } } return { valid: errors.length === 0, errors, warnings, }; } isValidWorkflow(workflow) { if (!workflow || typeof workflow !== 'object') { return false; } if (!Array.isArray(workflow.nodes)) { return false; } if (!workflow.connections || typeof workflow.connections !== 'object') { return false; } return true; } getWorkflowSizeKb(workflow) { try { const json = JSON.stringify(workflow); return json.length / 1024; } catch { return 0; } } hasMeaningfulChange(workflowBefore, workflowAfter) { try { const hashBefore = this.hashWorkflow(workflowBefore); const hashAfter = this.hashWorkflow(workflowAfter); return hashBefore !== hashAfter; } catch { return false; } } hashWorkflow(workflow) { try { const json = JSON.stringify(workflow); return (0, crypto_1.createHash)('sha256').update(json).digest('hex').substring(0, 16); } catch { return ''; } } shouldExclude(data) { if (!data.mutationSuccess && !data.mutationError) { return true; } if (!this.hasMeaningfulChange(data.workflowBefore, data.workflowAfter)) { return true; } const beforeSizeKb = this.getWorkflowSizeKb(data.workflowBefore); const afterSizeKb = this.getWorkflowSizeKb(data.workflowAfter); if (beforeSizeKb > this.options.maxWorkflowSizeKb || afterSizeKb > this.options.maxWorkflowSizeKb) { return true; } return false; } isDuplicate(workflowBefore, workflowAfter, operations, recentMutations) { const hashBefore = this.hashWorkflow(workflowBefore); const hashAfter = this.hashWorkflow(workflowAfter); const operationsHash = this.hashOperations(operations); return recentMutations.some((m) => m.hashBefore === hashBefore && m.hashAfter === hashAfter && this.hashOperations(m.operations) === operationsHash); } hashOperations(operations) { try { const json = JSON.stringify(operations); return (0, crypto_1.createHash)('sha256').update(json).digest('hex').substring(0, 16); } catch { return ''; } } } exports.MutationValidator = MutationValidator; exports.mutationValidator = new MutationValidator(); //# sourceMappingURL=mutation-validator.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/czlonkowski/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server