neat-edifact
v0.6.0
Published
A TypeScript UN/EDIFACT interchange parser. Zero dependencies, strict and non-strict modes.
Maintainers
Readme
neat-edifact
A TypeScript parser for UN/EDIFACT interchanges. Give it a raw EDIFACT string, get back typed Interchange, Message, and Segment objects. That's it.
It handles the syntax layer only — no segment group logic, no domain-specific knowledge, no opinions about what your messages mean. If you need BAPLIE parsing or similar, build that on top of this.
Install
npm install neat-edifact
pnpm add neat-edifactUsage
import { Parser } from 'neat-edifact'
const raw = `UNB+UNOA:2+SENDER+RECEIVER+260424:0811+000001'
UNH+000001+BAPLIE:D:95B:UN:SMDG20'
BGM++V123456+9'
UNT+3+000001'
UNZ+1+000001'`
const result = new Parser(raw).parse()
const interchange = result.first();
console.log(interchange.senderId) // 'SENDER'
console.log(interchange.messages[0].messageType) // 'BAPLIE'
console.log(interchange.messages[0].segments[0].tag) // 'BGM'parse() always returns InterchangeResult — even for single interchange files. Just use result.first() for the common case.
Strict mode
By default the parser is lenient. It'll do its best with whatever you give it — orphaned segments, missing UNZ, mismatched references, it'll handle it all gracefully.
If you want it to throw on any of that, pass true as the second argument:
new Parser(raw, true).parse()Strict mode enforces:
- First segment must be
UNB, last must beUNZ - Only one
UNB/UNZpair - Every
UNHmust have a matchingUNT - No segments outside a
UNH/UNTpair - Control references must match between
UNB/UNZandUNH/UNT - Segment and message counts must match what's declared
UNA
If the file has a UNA service string advice, the parser reads it automatically and uses those delimiters. If not, it falls back to the EDIFACT defaults (+, :, ', ?). You don't need to do anything.
API
Parser
new Parser(rawContent: string, strict?: boolean)
parser.parse(): InterchangeResultInterchangeResult
interchangeResult.first()
interchangeResult.firstOrFail()
interchangeResult.at(0)
interchangeResult.atOrFail(1)
interchangeResult.length()
interchangeResult.all()
interchangeResult.errors()
interchangeResult.hasErrors()
interchangeResult.isValid()Interchange
interchange.syntaxIdentifier // e.g. 'UNOA'
interchange.syntaxVersion // e.g. '2'
interchange.senderId
interchange.recipientId
interchange.date // 'YYMMDD'
interchange.time // 'HHMM'
interchange.controlReference
interchange.declaredMessageCount
interchange.messages // Message[]Message
message.messageReferenceNumber
message.messageType // e.g. 'BAPLIE'
message.messageVersion // e.g. 'D'
message.messageRelease // e.g. '95B'
message.controllingAgency
message.associationCode
message.declaredSegmentCount
message.segments // Segment[]Segment
segment.tag // e.g. 'LOC'
segment.getDataElement(0) // DataElement | undefined
segment.getDataElement(0)?.Value // first component of that element
segment.getDataElement(0)?.getComponent(1)?.value // specific componentErrors
import { EdifactSyntaxError, EdifactEnvelopeError, EdifactValidationError, InterchangeNotFoundError } from 'neat-edifact'
EdifactSyntaxError— malformed input, e.g. a segment with no tagEdifactEnvelopeError— missing or brokenUNB/UNZ/UNH/UNTstructureEdifactValidationError— count or reference mismatches (strict mode only)InterchangeNotFoundError— thrown by.firstOrFail()when the result is empty, or.atOrFail(n)when no interchange exists at that index
Notes
- Multiple interchanges in one file are supported in non-strict mode — each
UNBproduces a separateInterchangeon theInterchangeResult - The release character (
?by default) is handled correctly at all levels - Zero runtime dependencies
