mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-01-30 06:22:04 +00:00
144 lines
5.3 KiB
JavaScript
144 lines
5.3 KiB
JavaScript
"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
|