/** * EDIFACT Validator & Alternative Parser (Main Process only) * Uses ts-edifact library as a parallel layer alongside the custom DelforParser. * * IMPORTANT: This file runs in the Node.js Main Process only. * Do NOT include this file in index.html or use window.EDIBridge here. * * Exposed via IPC (see main.js): * 'validate-edifact' → validateEdifact(content) * 'parse-edifact-lib' → parseWithLib(content) * * ts-edifact API: * const { Reader } = require('ts-edifact'); * const reader = new Reader(); // instanciate per call (stateful) * const segments = reader.parse(str); // returns { name, elements: string[][] }[] */ 'use strict'; /** * Parse EDIFACT content using ts-edifact Reader. * Returns a clean segment array for comparison with the custom DelforParser. * * Segment format: { name: 'UNH', elements: [['1'], ['DELFOR','D','04A','UN']] } * * @param {string} content - Raw EDIFACT content (string) * @returns {{ segments: Array, segmentCount: number, error?: string }} */ function parseWithLib(content) { try { const { Reader, NullValidator, Parser } = require('ts-edifact'); // Normalize line endings const normalized = content.replace(/\r\n/g, '').replace(/\r/g, '').trim(); // Each Reader instance is stateful — create a new one per call const reader = new Reader(); // --- FIX: Disable strict segment validation & Enable UNOC --- const validator = new NullValidator(); if (typeof validator.define !== 'function') validator.define = () => { }; reader.validator = validator; reader.parser = new Parser(validator); reader.defined = true; reader.encoding('UNOC'); // Allow lowercase and special chars // Re-setup Reader callbacks for the new parser const result = reader.result; let elements = reader.elements; let components = reader.components; reader.parser.onOpenSegment = function (segment) { elements = []; result.push({ name: segment, elements: elements }); }; reader.parser.onElement = function () { components = []; elements.push(components); }; reader.parser.onComponent = function (value) { components.push(value); }; const segments = reader.parse(normalized); return { segments, segmentCount: segments.length }; } catch (e) { return { segments: [], segmentCount: 0, error: e.message }; } } /** * Validate EDIFACT content using ts-edifact library + structural checks. * * Performs: * 1. Syntax parsing via ts-edifact Reader (catches malformed EDI) * 2. Structural checks: UNB/UNZ envelope, UNH/UNT balance, BGM presence * 3. UNT segment count verification * 4. Message type & version detection * * @param {string} content - Raw EDIFACT content * @returns {{ valid: boolean, errors: string[], warnings: string[], stats: object }} */ function validateEdifact(content) { const errors = []; const warnings = []; const stats = {}; try { const { Reader, NullValidator, Parser } = require('ts-edifact'); // --- Step 1: Parse with ts-edifact --- let segments = null; try { const normalized = content.replace(/\r\n/g, '').replace(/\r/g, '').trim(); const reader = new Reader(); // --- FIX: Disable strict segment validation & Enable UNOC --- const validator = new NullValidator(); if (typeof validator.define !== 'function') validator.define = () => { }; reader.validator = validator; reader.parser = new Parser(validator); reader.defined = true; reader.encoding('UNOC'); // Re-setup Reader callbacks const result = reader.result; let elements = reader.elements; let components = reader.components; reader.parser.onOpenSegment = function (segment) { elements = []; result.push({ name: segment, elements: elements }); }; reader.parser.onElement = function () { components = []; elements.push(components); }; reader.parser.onComponent = function (value) { components.push(value); }; segments = reader.parse(normalized); } catch (parseErr) { errors.push('Syntaxfehler: ' + parseErr.message); } if (!segments || !Array.isArray(segments)) { return { valid: errors.length === 0, errors, warnings, stats }; } // Build a flat tag list for quick lookups const tags = segments.map(s => s.name); stats.segmentCount = segments.length; stats.allSegments = tags; // --- Step 2: Interchange envelope (UNB / UNZ) --- if (!tags.includes('UNB')) { warnings.push('Kein UNB-Segment (Interchange-Header) gefunden.'); } else { const unb = segments.find(s => s.name === 'UNB'); if (unb && unb.elements[1]) stats.sender = unb.elements[1][0] || ''; if (unb && unb.elements[2]) stats.receiver = unb.elements[2][0] || ''; } if (!tags.includes('UNZ')) warnings.push('Kein UNZ-Segment (Interchange-Trailer) gefunden.'); // --- Step 3: Message envelope (UNH / UNT) --- const unhCount = tags.filter(t => t === 'UNH').length; const untCount = tags.filter(t => t === 'UNT').length; stats.messageCount = unhCount; if (unhCount === 0) { errors.push('Kein UNH-Segment (Message-Header) gefunden.'); } else { // Detect message type from first UNH++::: const unh = segments.find(s => s.name === 'UNH'); if (unh && unh.elements[1]) { stats.messageType = unh.elements[1][0] || ''; stats.messageVersion = (unh.elements[1][1] || '') + (unh.elements[1][2] || ''); stats.controlRef = unh.elements[0]?.[0] || ''; } } if (untCount === 0) { errors.push('Kein UNT-Segment (Message-Trailer) gefunden.'); } if (unhCount !== untCount) { errors.push(`UNH/UNT-Anzahl stimmt nicht überein: ${unhCount} × UNH vs ${untCount} × UNT.`); } // --- Step 4: UNT segment count check --- // UNT++' — segCount includes UNH and UNT itself const unt = segments.find(s => s.name === 'UNT'); if (unt && unt.elements[0]) { const declared = parseInt(unt.elements[0][0]); // Find the slice from UNH to UNT (inclusive) const unhIdx = segments.findIndex(s => s.name === 'UNH'); const untIdx = segments.findIndex(s => s.name === 'UNT'); if (!isNaN(declared) && unhIdx >= 0 && untIdx >= 0) { const actual = untIdx - unhIdx + 1; if (declared !== actual) { warnings.push( `UNT deklariert ${declared} Segmente, gezählt (UNH→UNT): ${actual}.` ); } } } // --- Step 5: BGM check --- if (!tags.includes('BGM')) { warnings.push('Kein BGM-Segment (Nachrichtenbeginn) gefunden.'); } else { const bgm = segments.find(s => s.name === 'BGM'); if (bgm && bgm.elements[0]) stats.documentCode = bgm.elements[0][0] || ''; } } catch (e) { errors.push('Unerwarteter Validierungsfehler: ' + e.message); } return { valid: errors.length === 0, errors, warnings, stats }; } module.exports = { validateEdifact, parseWithLib };