Type Alias: X12OrphanAnchor
X12OrphanAnchor = {
groupIndex:number;kind:"interchange"; } | {groupIndex:number;kind:"group";transactionIndex:number; } | {groupIndex:number;kind:"transaction";segmentOffset:number;transactionIndex:number; }
Where an orphan sat in the STRUCTURE of the interchange, as opposed to
where it sat in the input byte stream. This is what lets serializeX12
put an orphan back without guessing.
X12OrphanSegment.segmentIndex cannot do that job. It indexes the INPUT
stream, and the emit is not in input order: ta1Segments are hoisted ahead
of the groups, and a doubled terminator's zero-length segment occupies an
input index that is never emitted. Either one shifts the output's indices
away from the input's, so replaying by index splices the orphan into
whatever occupies that slot - measurably, into an 835's ST..SE body,
where a re-parse reports nothing. An anchor names a slot in the typed tree
instead, which survives both reorderings because it does not mention bytes.
Three kinds, one per structural level:
"interchange"- between the ISA and the IEA but outside every functional group.groupIndexis the number of groups that had already closed, so the orphan is emitted immediately beforeix.groups[groupIndex](or before the IEA whengroupIndex === ix.groups.length)."group"- insideix.groups[groupIndex]but outside every transaction set in it.transactionIndexis the number of transactions that had already closed, so the orphan is emitted immediately before that group'stransactions[transactionIndex](or before itsGEwhentransactionIndex === transactions.length)."transaction"- inside an openST..SE, which only aTA1can be, since every other segment arriving there is body content.segmentOffsetis the number ofrawSegmentsalready collected, so the orphan is emitted immediately beforerawSegments[segmentOffset]. It is never0(theSTis alwaysrawSegments[0]) and never exceedsrawSegments.length. Such an orphan is written back BETWEEN theSTand theSE, soserializeX12counts it toward SE-01 in spec-clean mode even though it is not ontx.rawSegments- it is a segment of that transaction set per X12.6.
An anchor is a POSITION, so reshaping the model invalidates it. The
indices address ix.groups, that group's transactions, and that
transaction's rawSegments as they stand on the interchange you pass to
serializeX12. Filter or reorder any of those and an orphan's anchor may
still resolve while naming a different slot, and it will be emitted there
with no warning. An anchor that resolves to nothing is emitted at
interchange level before the IEA rather than dropped. Re-parse rather than
hand-edit if you need anchors to stay meaningful.
Union Members
Type Literal
{ groupIndex: number; kind: "interchange"; }
groupIndex
readonlygroupIndex:number
How many functional groups had closed before this segment arrived.
kind
readonlykind:"interchange"
Type Literal
{ groupIndex: number; kind: "group"; transactionIndex: number; }
groupIndex
readonlygroupIndex:number
Index into ix.groups of the group that was open.
kind
readonlykind:"group"
transactionIndex
readonlytransactionIndex:number
How many transaction sets in that group had closed.
Type Literal
{ groupIndex: number; kind: "transaction"; segmentOffset: number; transactionIndex: number; }
groupIndex
readonlygroupIndex:number
Index into ix.groups of the group that was open.
kind
readonlykind:"transaction"
segmentOffset
readonlysegmentOffset:number
How many of that set's rawSegments had been collected.
transactionIndex
readonlytransactionIndex:number
Index into that group's transactions of the set that was open.
Example
import { parseX12 } from "@cosyte/x12";
const ix = parseX12(raw);
for (const o of ix.orphanSegments) {
if (o.anchor.kind === "group") console.warn(o.anchor.groupIndex);
}