├── packages ├── documents │ ├── index.ts │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── EdiParser.ts │ │ └── EdiDomX12Listener.ts ├── validator │ ├── index.ts │ ├── README.md │ ├── src │ │ ├── ValidatorHelpers.ts │ │ ├── EdiDomValidator.ts │ │ └── edi-dom.schema.json │ ├── package.json │ └── package-lock.json ├── x12 │ ├── index.ts │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── X12BaseLexer.ts │ │ ├── EdiX12ParserVisitor.ts │ │ └── EdiX12ParserListener.ts ├── fact │ ├── index.ts │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── EdiFactParserVisitor.ts │ │ ├── FactBaseLexer.ts │ │ └── EdiFactParserListener.ts ├── shared │ ├── index.ts │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── BaseLexer.ts │ │ ├── BaseTypes.ts │ │ ├── helpers.ts │ │ └── BaseCharStreams.ts └── dom │ ├── README.md │ ├── package-lock.json │ ├── index.ts │ ├── src │ ├── EdiDomNodeType.ts │ ├── EdiDomNodeAlias.ts │ ├── EdiDomGlobal.ts │ ├── EdiDomValue.ts │ ├── EdiDomTypes.ts │ ├── EdiDomHelpers.ts │ ├── EdiDomAbstractNode.ts │ ├── EdiDomComponent.ts │ ├── EdiDomRepeated.ts │ ├── EdiDomElement.ts │ ├── README.md │ ├── EdiDomGroup.ts │ ├── EdiDomRoot.ts │ ├── EdiDomMessage.ts │ ├── EdiDomSegment.ts │ └── EdiDomInterchange.ts │ ├── query │ ├── QueryDomWalker.ts │ ├── QueryEngineTypes.ts │ ├── helpers.ts │ ├── ElementSelectorVisitor.ts │ ├── README.md │ ├── QueryEngineList.ts │ ├── ElementSelectorListener.ts │ └── ElementSelectorLexer.ts │ └── package.json ├── lerna.json ├── tsconfig.eslint.json ├── .gitattributes ├── .mocharc.json ├── tsconfig.json ├── .sonarcloud.properties ├── .nycrc.json ├── test ├── data │ ├── EdifactSample.edi │ ├── 850_3.edi │ ├── 856.edi │ ├── 850_2.edi │ ├── 850.edi │ ├── 271.edi │ └── 850_fat.edi ├── QueryEngineSuite.ts └── EdiParserSuite.ts ├── LICENSE ├── gulpfile.ts ├── package.json ├── grammars ├── fact │ ├── EdiFactParser.g4 │ └── EdiFactLexer.g4 ├── query │ └── ElementSelector.g4 └── x12 │ ├── EdiX12Lexer.g4 │ └── EdiX12Parser.g4 ├── .gitignore └── README.md /packages/documents/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/EdiParser' 2 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.0.0" 6 | } 7 | -------------------------------------------------------------------------------- /packages/validator/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/EdiDomValidator' 2 | export * from './src/ValidatorHelpers' 3 | -------------------------------------------------------------------------------- /packages/x12/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/EdiX12Lexer' 2 | export * from './src/EdiX12Parser' 3 | export * from './src/EdiX12ParserListener' 4 | -------------------------------------------------------------------------------- /packages/fact/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/EdiFactLexer' 2 | export * from './src/EdiFactParser' 3 | export * from './src/EdiFactParserListener' 4 | -------------------------------------------------------------------------------- /packages/shared/index.ts: -------------------------------------------------------------------------------- 1 | export * as BaseCharStreams from './src/BaseCharStreams' 2 | export * from './src/BaseLexer' 3 | export * from './src/BaseTypes' 4 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "strictNullChecks": true 5 | }, 6 | "exclude": [] 7 | } 8 | -------------------------------------------------------------------------------- /packages/dom/README.md: -------------------------------------------------------------------------------- 1 | # `@js-edi/dom` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const dom = require('@js-edi/dom'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/x12/README.md: -------------------------------------------------------------------------------- 1 | # `@js-edi/x12` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const x12 = require('@js-edi/x12'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/fact/README.md: -------------------------------------------------------------------------------- 1 | # `@js-edi/fact` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const fact = require('@js-edi/fact'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/shared/README.md: -------------------------------------------------------------------------------- 1 | # `@js-edi/shared` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const shared = require('@js-edi/shared'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | packages/fact/src/EdiFact*.ts linguist-generated=true 2 | packages/dom/query/ElementSelector*.ts linguist-generated=true 3 | packages/x12/src/EdiX12*.ts linguist-generated=true 4 | -------------------------------------------------------------------------------- /packages/documents/README.md: -------------------------------------------------------------------------------- 1 | # `@js-edi/documents` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const documents = require('@js-edi/documents'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/validator/README.md: -------------------------------------------------------------------------------- 1 | # `@js-edi/validator` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const validator = require('@js-edi/validator'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "recursive": "./test", 3 | "extension": ["ts"], 4 | "require": ["ts-node/register", "source-map-support/register"], 5 | "reporter": ["spec"], 6 | "full-trace": true, 7 | "bail": false 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2019", 4 | "moduleResolution": "Node", 5 | "resolveJsonModule": true 6 | }, 7 | "exclude": [ 8 | "test", 9 | "gulpfile.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | # Global exclusions for generated, task, and test code files 2 | sonar.exclusions=docs/**/*,test/*Suite.ts,gulpfile.ts,packages/fact/src/EdiFact*.ts,packages/dom/query/ElementSelector*.ts,packages/x12/src/EdiX12*.ts 3 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@istanbuljs/nyc-config-typescript", 3 | "all": true, 4 | "include": ["packages/**/*.ts"], 5 | "exclude": [ 6 | "packages/fact/src/EdiFact*.ts", 7 | "packages/dom/query/ElementSelector*.ts", 8 | "packages/x12/src/EdiX12*.ts" 9 | ], 10 | "require": ["source-map-support/register"], 11 | "check-coverage": false, 12 | "reporter": "lcov" 13 | } 14 | -------------------------------------------------------------------------------- /test/data/EdifactSample.edi: -------------------------------------------------------------------------------- 1 | UNA:+.? ' 2 | UNB+IATB:1+6XPPC:ZZ+LHPPC:ZZ+940101:0950+1' 3 | UNH+1+PAORES:93:1:IA' 4 | MSG+1:45' 5 | IFT+3+XYZCOMPANY AVAILABILITY' 6 | ERC+A7V:1:AMD' 7 | IFT+3+NO MORE FLIGHTS' 8 | ODI+' 9 | TVL+240493:1000::1220+FRA+JFK+DL+400+C' 10 | PDI++C:3+Y::3+F::1' 11 | APD+74C:0:::6++++++6X' 12 | TVL+240493:1740::2030+JFK+MIA+DL+081+C' 13 | PDI++C:4' 14 | APD+EM2:0:1630::6+++++++DA' 15 | UNT+13+1' 16 | UNZ+1+1' 17 | -------------------------------------------------------------------------------- /packages/dom/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/dom", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "antlr4ts": { 8 | "version": "0.5.0-alpha.4", 9 | "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", 10 | "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/x12/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/x12", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "antlr4ts": { 8 | "version": "0.5.0-alpha.4", 9 | "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", 10 | "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/fact/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/fact", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "antlr4ts": { 8 | "version": "0.5.0-alpha.4", 9 | "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", 10 | "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/shared/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/shared", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "antlr4ts": { 8 | "version": "0.5.0-alpha.4", 9 | "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", 10 | "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/documents/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/documents", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "antlr4ts": { 8 | "version": "0.5.0-alpha.4", 9 | "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", 10 | "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/validator/src/ValidatorHelpers.ts: -------------------------------------------------------------------------------- 1 | import type { EdiDomNode } from '@js-edi/dom' 2 | import type { SchemaObject } from 'ajv' 3 | import { EdiDomValidator } from './EdiDomValidator' 4 | 5 | /** Helper function for validating a parsed EDI DOM or JSON EDI notation against a JSON Schema. */ 6 | export function ediDomValidate (object: any, schema?: SchemaObject): object is T { 7 | const validator = new EdiDomValidator() 8 | 9 | return validator.validate(object, schema) 10 | } 11 | -------------------------------------------------------------------------------- /packages/dom/index.ts: -------------------------------------------------------------------------------- 1 | export * from './src/EdiDomAbstractNode' 2 | export * from './src/EdiDomComponent' 3 | export * from './src/EdiDomElement' 4 | export * from './src/EdiDomGroup' 5 | export * from './src/EdiDomInterchange' 6 | export * from './src/EdiDomMessage' 7 | export * from './src/EdiDomNodeAlias' 8 | export * from './src/EdiDomNodeType' 9 | export * from './src/EdiDomRepeated' 10 | export * from './src/EdiDomRoot' 11 | export * from './src/EdiDomSegment' 12 | export * from './src/EdiDomTypes' 13 | export * from './src/EdiDomValue' 14 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomNodeType.ts: -------------------------------------------------------------------------------- 1 | /** Node types for each node class. */ 2 | export enum EdiDomNodeType { 3 | All = 'All', 4 | Root = 'Root', 5 | Interchange = 'Interchange', 6 | Group = 'Group', 7 | Message = 'Message', 8 | Segment = 'Segment', 9 | Element = 'Element', 10 | Repeated = 'Repeated', 11 | Component = 'Component', 12 | Value = 'Value' 13 | } 14 | 15 | /** Method for narrowing the type of node. */ 16 | export function isNodeType (nodeType: T | string): nodeType is T { 17 | return nodeType in EdiDomNodeType 18 | } 19 | -------------------------------------------------------------------------------- /test/data/850_3.edi: -------------------------------------------------------------------------------- 1 | ISA*00* *00* *12*0000000000 *12*0000000000 *160426*1301*U*00401*010001398*0*P*> 2 | GS*PO*0000000000*0000000000*20160426*1301*10000774*X*004010 3 | ST*850*8830 4 | BEG*00*NE*----------**20160426 5 | DTM*106*20160502 6 | N9*ZZ*0 7 | MSG*000000000001010700 BUYER --------- ------ SHIP TO: 000 -------- ----- --. SAN ANTONIO, TX 78245 ATTN. --------- ------ 8 | N1*VN*----- ------ --------- ---*ZZ*----- 9 | N3*----- ------ -- 10 | N4*HUNTINGTON BEACH*CA*92647 11 | N1*ST*PETCO - CORPORATE*92*100 12 | N3*9125 REHCO RD 13 | N4*SAN DIEGO*CA*92121 14 | PO1**1*KI*225**VN*UNKNOWN*PD*SU - SPARK - AQ FREEZER PUSHER*SK*000000000001010700 15 | CTT*000001**0000000.00*01 16 | SE*14*8830 17 | GE*1*10000774 18 | IEA*1*010001398 19 | -------------------------------------------------------------------------------- /packages/shared/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/shared", 3 | "version": "0.0.0", 4 | "description": "Shared classes, interfaces, and types for js-edi monorepo", 5 | "keywords": [ 6 | "js-edi" 7 | ], 8 | "author": "Aaron Huggins ", 9 | "homepage": "https://github.com/ahuggins-nhs/edi-parser/tree/main/packages/shared#readme", 10 | "license": "MIT", 11 | "main": "index.js", 12 | "publishConfig": { 13 | "access": "public" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 18 | }, 19 | "scripts": { 20 | "test": "echo \"Error: run tests from root\" && exit 1" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 24 | }, 25 | "dependencies": { 26 | "antlr4ts": "^0.5.0-alpha.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/shared/src/BaseLexer.ts: -------------------------------------------------------------------------------- 1 | import { CharStream, Lexer } from 'antlr4ts' 2 | import { EdiOptions } from './BaseTypes' 3 | 4 | export enum ControlChar { 5 | DataSeparator = 'DataSeparator', 6 | RepititionSeparator = 'RepititionSeparator', 7 | ComponentSeparator = 'ComponentSeparator', 8 | SegmentTerminator = 'SegmentTerminator', 9 | DecimalMark = 'DecimalMark', 10 | ReleaseIndicator = 'ReleaseIndicator', 11 | EndOfLine = 'EndOfLine' 12 | } 13 | 14 | export abstract class BaseLexer extends Lexer { 15 | constructor (input: CharStream) { 16 | super(input) 17 | 18 | this.ControlCharMap = new Map() 19 | } 20 | 21 | protected ControlCharMap: Map 22 | 23 | abstract handleControlChars (lexer: Record): void 24 | 25 | abstract getOptions (): EdiOptions 26 | 27 | abstract setOptions (options: EdiOptions): void 28 | } 29 | -------------------------------------------------------------------------------- /packages/shared/src/BaseTypes.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Options for parsing/reconstructing an EDI document. 3 | * In the case of EDIFACT, this represents the UNA segment. 4 | * In the case of EDIX12, this represents control characters in the ISA segment. 5 | */ 6 | export interface EdiOptions { 7 | /** The component separator for EDI documents. */ 8 | componentSeparator: string 9 | /** The data separator for EDI documents. */ 10 | dataSeparator: string 11 | /** The decimal mark for EDIFACT documents only. */ 12 | decimalMark?: ',' | '.' 13 | /** The release indicator for EDIFACT documents only. */ 14 | releaseIndicator?: string 15 | /** The repitition separator for EDI documents. */ 16 | repititionSeparator?: string 17 | /** The segment terminator for EDI documents. */ 18 | segmentTerminator: string 19 | /** The formatting mark for prettified EDI documents. */ 20 | endOfLine?: string 21 | } 22 | -------------------------------------------------------------------------------- /packages/x12/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/x12", 3 | "version": "0.0.0", 4 | "description": "A raw parser for EDI X12 which outputs an ANTLR4 ParseTree", 5 | "keywords": [ 6 | "edi", 7 | "x12", 8 | "parser", 9 | "antlr" 10 | ], 11 | "author": "Aaron Huggins ", 12 | "homepage": "https://github.com/ahuggins-nhs/edi-parser/tree/main/packages/x12#readme", 13 | "license": "MIT", 14 | "main": "index.js", 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 21 | }, 22 | "scripts": { 23 | "test": "echo \"Error: run tests from root\" && exit 1" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 27 | }, 28 | "dependencies": { 29 | "@js-edi/shared": "^0.0.0", 30 | "antlr4ts": "^0.5.0-alpha.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/fact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/fact", 3 | "version": "0.0.0", 4 | "description": "A raw parser for EDIFACT which outputs an ANTLR4 ParseTree", 5 | "keywords": [ 6 | "edi", 7 | "edifact", 8 | "parser", 9 | "antlr" 10 | ], 11 | "author": "Aaron Huggins ", 12 | "homepage": "https://github.com/ahuggins-nhs/edi-parser/tree/main/packages/fact#readme", 13 | "license": "MIT", 14 | "main": "index.js", 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 21 | }, 22 | "scripts": { 23 | "test": "echo \"Error: run tests from root\" && exit 1" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 27 | }, 28 | "dependencies": { 29 | "@js-edi/shared": "^0.0.0", 30 | "antlr4ts": "^0.5.0-alpha.4" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/dom/query/QueryDomWalker.ts: -------------------------------------------------------------------------------- 1 | import type { EdiDomNode } from '../src/EdiDomTypes' 2 | import { QueryEngineList } from './QueryEngineList' 3 | 4 | export class QueryDomWalker { 5 | constructor (iterator: Generator) { 6 | this.iterator = new QueryEngineList(iterator) 7 | this.position = 0 8 | } 9 | 10 | private readonly iterator: QueryEngineList 11 | private position: number 12 | 13 | /** Descend down the node iterator. */ 14 | * descend (): Generator { 15 | for (const node of this.iterator) { 16 | yield node 17 | 18 | this.position += 1 19 | } 20 | } 21 | 22 | /** Ascend up the array of visited nodes. */ 23 | * ascend (): Generator { 24 | for (let i = this.position; i > -1; i -= 1) yield this.iterator[i] 25 | } 26 | 27 | * fastforward (): Generator { 28 | for (let i = this.position; i < this.iterator.size; i += 1) yield this.iterator[i] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/data/856.edi: -------------------------------------------------------------------------------- 1 | ISA*01*0000000000*01*ABCCO *12*4405197800 *01*999999999 *111206*1719*-*00406*000000049*0*P*>~ 2 | GS*SH*4405197800*999999999*20111206*1045*49*X*004060~ 3 | ST*856*0008~ 4 | BSN*14*829716*20111206*142428*0002~ 5 | HL*1**S~ 6 | TD1*PCS*2****A3*60.310*LB~ 7 | TD5**2*XXXX**XXXX~ 8 | REF*BM*999999-001~ 9 | REF*CN*5787970539~ 10 | DTM*011*20111206~ 11 | N1*SH*1 EDI SOURCE~ 12 | N3*31875 SOLON RD~ 13 | N4*SOLON*OH*44139~ 14 | N1*OB*XYZ RETAIL~ 15 | N3*P O BOX 9999999~ 16 | N4*ATLANTA*GA*31139-0020**SN*9999~ 17 | N1*SF*1 EDI SOURCE~ 18 | N3*31875 SOLON ROAD~ 19 | N4*SOLON*OH*44139~ 20 | HL*2*1*O~ 21 | PRF*99999817***20111205~ 22 | HL*3*2*I~ 23 | LIN*1*VP*87787D*UP*999999310145~ 24 | SN1*1*24*EA~ 25 | PO4*1*24*EA~ 26 | PID*F****BLUE WIDGET~ 27 | HL*4*2*I~ 28 | LIN*2*VP*99887D*UP*999999311746~ 29 | SN1*2*6*EA~ 30 | PO4*1*6*EA~ 31 | PID*F****RED WIDGET~ 32 | HL*5*2*I~ 33 | LIN*3*VP*99887D*UP*999999311746~ 34 | SN1*3*6*EA~ 35 | PO4*1*6*EA~ 36 | PID*F****GREEN WIDGET~ 37 | CTT*4*30~ 38 | SE*31*0008~ 39 | GE*1*49~ 40 | IEA*1*000000049~ 41 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomNodeAlias.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomNodeType } from './EdiDomNodeType' 2 | import type { EdiDomNodeTagMap } from './EdiDomTypes' 3 | 4 | export const EdiDomNodeAlias: Record = { 5 | // Universal selector. 6 | '*': EdiDomNodeType.All, 7 | All: EdiDomNodeType.All, 8 | // Interchange aliases. 9 | Interchange: EdiDomNodeType.Interchange, 10 | ISA: EdiDomNodeType.Interchange, 11 | UNB: EdiDomNodeType.Interchange, 12 | // Functional group aliases. 13 | Group: EdiDomNodeType.Group, 14 | GS: EdiDomNodeType.Group, 15 | UNE: EdiDomNodeType.Group, 16 | // Message aliases. 17 | Message: EdiDomNodeType.Message, 18 | Transaction: EdiDomNodeType.Message, 19 | ST: EdiDomNodeType.Message, 20 | UNH: EdiDomNodeType.Message, 21 | // EDI DOM Node aliases. 22 | Root: EdiDomNodeType.Root, 23 | Segment: EdiDomNodeType.Segment, 24 | Repeated: EdiDomNodeType.Repeated, 25 | Element: EdiDomNodeType.Element, 26 | Component: EdiDomNodeType.Component, 27 | Value: EdiDomNodeType.Value 28 | } 29 | -------------------------------------------------------------------------------- /packages/dom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/dom", 3 | "version": "0.0.0", 4 | "description": "The JS-EDI Document Object Model for EDIFACT and EDI X12", 5 | "keywords": [ 6 | "edi", 7 | "edifact", 8 | "x12", 9 | "parser", 10 | "dom", 11 | "antlr", 12 | "js-edi" 13 | ], 14 | "author": "Aaron Huggins ", 15 | "homepage": "https://github.com/ahuggins-nhs/edi-parser/tree/main/packages/dom#readme", 16 | "license": "MIT", 17 | "main": "index.js", 18 | "directories": { 19 | "lib": "lib", 20 | "test": "__tests__" 21 | }, 22 | "files": [ 23 | "lib" 24 | ], 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 31 | }, 32 | "scripts": { 33 | "test": "echo \"Error: run tests from root\" && exit 1" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 37 | }, 38 | "dependencies": { 39 | "antlr4ts": "^0.5.0-alpha.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Aaron Huggins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomGlobal.ts: -------------------------------------------------------------------------------- 1 | import type { EdiDomComponent } from './EdiDomComponent' 2 | import type { EdiDomElement } from './EdiDomElement' 3 | import type { EdiDomGroup } from './EdiDomGroup' 4 | import type { EdiDomInterchange } from './EdiDomInterchange' 5 | import type { EdiDomMessage } from './EdiDomMessage' 6 | import type { EdiDomRepeated } from './EdiDomRepeated' 7 | import type { EdiDomRoot } from './EdiDomRoot' 8 | import type { EdiDomSegment } from './EdiDomSegment' 9 | import type { EdiDomValue } from './EdiDomValue' 10 | 11 | export interface EdiDomGlobalSpace { 12 | Root: typeof EdiDomRoot 13 | Interchange: typeof EdiDomInterchange 14 | Group: typeof EdiDomGroup 15 | Message: typeof EdiDomMessage 16 | Transaction: typeof EdiDomMessage 17 | Segment: typeof EdiDomSegment 18 | Repeated: typeof EdiDomRepeated 19 | Element: typeof EdiDomElement 20 | Component: typeof EdiDomComponent 21 | Value: typeof EdiDomValue 22 | } 23 | 24 | /** A global variable namespace which is filled-in as imports take place. 25 | * Meant for DOM internals only; not recommended to use or expose externally. 26 | * @hidden 27 | */ 28 | export const EdiDomGlobal: EdiDomGlobalSpace = {} as any 29 | -------------------------------------------------------------------------------- /packages/validator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/validator", 3 | "version": "0.0.0", 4 | "description": "Validator for JS-EDI using JSON Schema 7 notation", 5 | "keywords": [ 6 | "edi", 7 | "validation", 8 | "json", 9 | "schema", 10 | "edifact", 11 | "x12", 12 | "js-edi" 13 | ], 14 | "author": "Aaron Huggins ", 15 | "homepage": "https://github.com/ahuggins-nhs/edi-parser/tree/main/packages/validator#readme", 16 | "license": "MIT", 17 | "main": "index.js", 18 | "directories": { 19 | "lib": "lib", 20 | "test": "__tests__" 21 | }, 22 | "files": [ 23 | "lib" 24 | ], 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 31 | }, 32 | "scripts": { 33 | "test": "echo \"Error: run tests from root\" && exit 1" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 37 | }, 38 | "dependencies": { 39 | "ajv": "^7.2.3" 40 | }, 41 | "peerDependencies": { 42 | "@js-edi/dom": "^0.0.0" 43 | }, 44 | "devDependencies": { 45 | "@js-edi/dom": "^0.0.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/shared/src/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream' 2 | 3 | /** Convert a Readable stream to an Async Generator. Based on https://www.derpturkey.com/nodejs-async-generators-for-streaming */ 4 | export async function * streamToAsyncGenerator (reader: Readable, chunkSize?: number): AsyncGenerator { 5 | let readableEnded = false 6 | const signalEnd = new Promise(resolve => { 7 | reader.once('end', () => { 8 | // Manage readableEnded, because not all implementors of Readable set this to true on end. 9 | readableEnded = true 10 | 11 | resolve() 12 | }) 13 | }) 14 | 15 | // eslint-disable-next-line no-unmodified-loop-condition 16 | while (!readableEnded) { 17 | while (reader.readable) { 18 | const val: T = typeof chunkSize === 'number' 19 | ? reader.read(chunkSize) ?? reader.read() 20 | : reader.read() 21 | 22 | if (typeof val !== 'undefined' && val !== null) { 23 | yield val 24 | } else { 25 | break 26 | } 27 | } 28 | 29 | const signalReadable = new Promise(resolve => { 30 | reader.once('readable', resolve) 31 | }) 32 | 33 | await Promise.race([signalEnd, signalReadable]) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/validator/src/EdiDomValidator.ts: -------------------------------------------------------------------------------- 1 | import Ajv from 'ajv' 2 | import * as EdiDomSchema from './edi-dom.schema.json' 3 | import type { ValidateFunction, SchemaObject, ErrorObject } from 'ajv' 4 | 5 | export class EdiDomValidator { 6 | constructor () { 7 | this._ajv = new Ajv({ 8 | allErrors: true 9 | }) 10 | } 11 | 12 | private readonly _ajv: Ajv 13 | private _validator: ValidateFunction 14 | 15 | get errors (): ErrorObject[] { 16 | if (Array.isArray(this._validator.errors)) { 17 | return this._validator.errors 18 | } 19 | 20 | return [] 21 | } 22 | 23 | private compile (schema?: SchemaObject): void { 24 | this._validator = this._ajv.compile(schema ?? EdiDomSchema) 25 | } 26 | 27 | validate (object: T, schema?: SchemaObject): object is T { 28 | if (typeof schema === 'object') { 29 | if (typeof schema.definitions === 'object') { 30 | schema.definitions = { 31 | ...EdiDomSchema.definitions, 32 | ...schema.definitions 33 | } 34 | } else { 35 | schema.definitions = EdiDomSchema.definitions 36 | } 37 | } 38 | 39 | this.compile(schema) 40 | 41 | return this._validator(object) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/data/850_2.edi: -------------------------------------------------------------------------------- 1 | ISA*01*0000000000*01*ABCCO *12*4405197800 *01*999999999 *101127*1719*U*00400*000003438*0*P*> 2 | GS*PO*4405197800*999999999*20101127*1719*1421*X*004010VICS 3 | ST*850*000000010 4 | BEG*00*SA*08292233294**20101127*610385385 5 | REF*DP*038 6 | REF*PS*R 7 | ITD*14*3*2**45**46 8 | DTM*002*20101214 9 | PKG*F*68***PALLETIZE SHIPMENT 10 | PKG*F*66***REGULAR 11 | TD5*A*92*P3**SEE XYZ RETAIL ROUTING GUIDE 12 | N1*ST*XYZ RETAIL*9*0003947268292 13 | N3*31875 SOLON RD 14 | N4*SOLON*OH*44139 15 | PO1*1*120*EA*9.25*TE*CB*065322-117*PR*RO*VN*AB3542 16 | PID*F****SMALL WIDGET 17 | PO4*4*4*EA*PLT94**3*LR*15*CT 18 | PO1*2*220*EA*13.79*TE*CB*066850-116*PR*RO*VN*RD5322 19 | PID*F****MEDIUM WIDGET 20 | PO4*2*2*EA 21 | PO1*3*126*EA*10.99*TE*CB*060733>110*PR*RO*VN*XY5266 22 | PID*F****LARGE WIDGET 23 | PO4*6*1*EA*PLT94**3*LR*12*CT 24 | PO1*4*76*EA*4.35*TE*CB*065308-116*PR*RO*VN*VX2332 25 | PID*F****NANO WIDGET 26 | PO4*4*4*EA*PLT94**6*LR*19*CT 27 | PO1*5*72*EA*7.5*TE*CB*065374-118*PR*RO*VN*RV0524 28 | PID*F****BLUE WIDGET 29 | PO4*4*4*EA 30 | PO1*6*696*EA*9.55*TE*CB*067504-118*PR*RO*VN*DX1875 31 | PID*F****ORANGE WIDGET 32 | PO4*6*6*EA*PLT94**3*LR*10*CT 33 | CTT*6 34 | AMT*1*13045.94 35 | SE*33*000000010 36 | GE*1*1421 37 | IEA*1*000003438 38 | -------------------------------------------------------------------------------- /test/data/850.edi: -------------------------------------------------------------------------------- 1 | ISA*01*0000000000*01*ABCCO *12*4405197800 *01*999999999 *101127*1719*U*00400*000003438*0*P*>~ 2 | GS*PO*4405197800*999999999*20101127*1719*1421*X*004010VICS~ 3 | ST*850*000000010~ 4 | BEG*00*SA*08292233294**20101127*610385385~ 5 | REF*DP*038~ 6 | REF*PS*R~ 7 | ITD*14*3*2**45**46~ 8 | DTM*002*20101214~ 9 | PKG*F*68***PALLETIZE SHIPMENT~ 10 | PKG*F*66***REGULAR~ 11 | TD5*A*92*P3**SEE XYZ RETAIL ROUTING GUIDE~ 12 | N1*ST*XYZ RETAIL*9*0003947268292~ 13 | N3*31875 SOLON RD~ 14 | N4*SOLON*OH*44139~ 15 | PO1*1*120*EA*9.25*TE*CB*065322-117*PR*RO*VN*AB3542~ 16 | PID*F****SMALL WIDGET~ 17 | PO4*4*4*EA*PLT94**3*LR*15*CT~ 18 | PO1*2*220*EA*13.79*TE*CB*066850-116*PR*RO*VN*RD5322~ 19 | PID*F****MEDIUM WIDGET~ 20 | PO4*2*2*EA~ 21 | PO1*3*126*EA*10.99*TE*CB*060733-110*PR*RO*VN*XY5266~ 22 | PID*F****LARGE WIDGET~ 23 | PO4*6*1*EA*PLT94**3*LR*12*CT~ 24 | PO1*4*76*EA*4.35*TE*CB*065308-116*PR*RO*VN*VX2332~ 25 | PID*F****NANO WIDGET~ 26 | PO4*4*4*EA*PLT94**6*LR*19*CT~ 27 | PO1*5*72*EA*7.5*TE*CB*065374-118*PR*RO*VN*RV0524~ 28 | PID*F****BLUE WIDGET~ 29 | PO4*4*4*EA~ 30 | PO1*6*696*EA*9.55*TE*CB*067504-118*PR*RO*VN*DX1875~ 31 | PID*F****ORANGE WIDGET~ 32 | PO4*6*6*EA*PLT94**3*LR*10*CT~ 33 | CTT*6~ 34 | AMT*1*13045.94~ 35 | SE*33*000000010~ 36 | GE*1*1421~ 37 | IEA*1*000003438~ 38 | -------------------------------------------------------------------------------- /gulpfile.ts: -------------------------------------------------------------------------------- 1 | import * as shell from 'gulp-shell' 2 | import * as del from 'del' 3 | import { series } from 'gulp' 4 | 5 | export async function antlr4ts (): Promise { 6 | await shell.task([ 7 | 'antlr4ts -visitor grammars/fact/EdiFactLexer.g4 grammars/fact/EdiFactParser.g4 -o packages/fact/src', 8 | 'antlr4ts -visitor grammars/x12/EdiX12Lexer.g4 grammars/x12/EdiX12Parser.g4 -o packages/x12/src', 9 | 'antlr4ts -visitor grammars/query/ElementSelector.g4 -o packages/dom/query' 10 | ])() 11 | } 12 | 13 | export async function cleanup (): Promise { 14 | await del([ 15 | '**/*.d.ts', 16 | '**/*.js', 17 | '**/*.js.map' 18 | ], { 19 | ignore: ['**/node_modules/**'] 20 | }) 21 | } 22 | 23 | export async function mocha (): Promise { 24 | await shell.task('mocha')() 25 | } 26 | 27 | export async function nyc (): Promise { 28 | await shell.task('nyc mocha')() 29 | } 30 | 31 | export const test = series(cleanup, antlr4ts, mocha) 32 | export const coverage = series(cleanup, antlr4ts, nyc) 33 | 34 | export async function lint (): Promise { 35 | await shell.task('ts-standard --report codeframe')() 36 | } 37 | 38 | export async function fix (): Promise { 39 | await shell.task('ts-standard --fix --report codeframe')() 40 | } 41 | -------------------------------------------------------------------------------- /packages/documents/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/documents", 3 | "version": "0.0.0", 4 | "description": "Build, parse, query, validate, transform, serialize, and deserialize EDI in JavaScript", 5 | "keywords": [ 6 | "edi", 7 | "edifact", 8 | "x12", 9 | "parser", 10 | "builder", 11 | "validator", 12 | "transformer", 13 | "json" 14 | ], 15 | "author": "Aaron Huggins ", 16 | "homepage": "https://github.com/ahuggins-nhs/edi-parser/tree/main/packages/documents#readme", 17 | "license": "MIT", 18 | "main": "index.js", 19 | "directories": { 20 | "lib": "lib", 21 | "test": "__tests__" 22 | }, 23 | "files": [ 24 | "lib" 25 | ], 26 | "publishConfig": { 27 | "access": "public" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 32 | }, 33 | "scripts": { 34 | "test": "echo \"Error: run tests from root\" && exit 1" 35 | }, 36 | "bugs": { 37 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 38 | }, 39 | "dependencies": { 40 | "@js-edi/dom": "^0.0.0", 41 | "@js-edi/fact": "^0.0.0", 42 | "@js-edi/shared": "^0.0.0", 43 | "@js-edi/x12": "^0.0.0", 44 | "antlr4ts": "^0.5.0-alpha.4" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/data/271.edi: -------------------------------------------------------------------------------- 1 | ISA*00*Authorizat*00*Security I*ZZ*InterchangeSen *ZZ*Interchange Rec*141001*1037*^*00501*000031033*0*T*:~ 2 | GS*HS*Sample Sen*Sample Rec*20141001*1037*123456*X*005010X279A1~ 3 | ST*271*000000001*005010X279A1~ 4 | BHT*0022*11*7237581e2e400890ee96a51bc62ed0*20200308*1901~ 5 | HL*1**20*1~ 6 | NM1*PR*2*CMS*****PI*CMS~ 7 | HL*2*1*21*1~ 8 | NM1*1P*2*SINAI HADASSAH MEDICAL ASSOCIATES*****XX*1508165317~ 9 | HL*3*2*22*0~ 10 | TRN*1*2223255592*99Trizetto~ 11 | NM1*IL*1*BELMAR*ALBERT*R***MI*9UY4R33KV83~ 12 | N3*1029 SALEM AVE~ 13 | N4*WOODBURY*NJ*080966062~ 14 | DMG*D8*19500722*M~ 15 | DTP*307*RD8*20191230-20191230~ 16 | EB*1**88~ 17 | EB*1**30^42^45^48^49^69^76^83^A5^A7^AG^BT^BU^BV*MA~ 18 | DTP*291*D8*20150701~ 19 | EB*1**30^2^23^24^25^26^27^28^3^33^36^37^38^39^40^42^50^51^52^53^67^69^73^76^83^86^98^A4^A6^A8^AI^AJ^AK^AL^BT^BU^BV^DM^UC*MB~ 20 | DTP*291*D8*20150701~ 21 | EB*A**30*QM*Medicare Part B*27**0~ 22 | DTP*291*RD8*20190101-20191231~ 23 | EB*C**30*QM*Medicare Part A*26*0~ 24 | DTP*291*RD8*20190101-20191231~ 25 | EB*C**30*QM*Medicare Part B*23*0~ 26 | DTP*291*RD8*20190101-20191231~ 27 | EB*I**41^54~ 28 | EB*R***QM*NJ QMB Plan~ 29 | DTP*290*D8*20170201~ 30 | EB*R**88*OT~ 31 | REF*18*S5601~ 32 | REF*N6*008*SilverScript Choice~ 33 | DTP*292*D8*20170101~ 34 | LS*2120~ 35 | NM1*PR*2*SILVERSCRIPT INSURANCE COMPANY~ 36 | N3*445 Great Circle Road~ 37 | N4*Nashville*TN*37228~ 38 | PER*IC**TE*8664430934*UR*www.silverscript.com~ 39 | LE*2120~ 40 | SE*38*000000001~ 41 | GE*1*123456~ 42 | IEA*1*31033~ 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-edi", 3 | "version": "0.0.0", 4 | "description": "Next-gen EDI parser in Antlr4 grammar compiled by antlr4ts", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/ahuggins-nhs/edi-parser.git" 12 | }, 13 | "keywords": [ 14 | "edi", 15 | "x12", 16 | "parser" 17 | ], 18 | "author": "Aaron Huggins", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/ahuggins-nhs/edi-parser/issues" 22 | }, 23 | "homepage": "https://github.com/ahuggins-nhs/edi-parser#readme", 24 | "ts-standard": { 25 | "ignore": [ 26 | "packages/fact/src/EdiFact*.ts", 27 | "packages/dom/query/ElementSelector*.ts", 28 | "packages/x12/src/EdiX12*.ts" 29 | ] 30 | }, 31 | "devDependencies": { 32 | "antlr4ts": "^0.5.0-alpha.4", 33 | "@istanbuljs/nyc-config-typescript": "^1.0.1", 34 | "@types/gulp": "^4.0.8", 35 | "@types/mocha": "^8.2.1", 36 | "@types/node": "^14.14.31", 37 | "antlr4ts-cli": "^0.5.0-alpha.4", 38 | "del": "^6.0.0", 39 | "eslint": "^7.21.0", 40 | "gulp": "^4.0.2", 41 | "gulp-shell": "^0.8.0", 42 | "lerna": "^4.0.0", 43 | "mocha": "^8.3.0", 44 | "nyc": "^15.1.0", 45 | "source-map-support": "^0.5.19", 46 | "ts-node": "^9.1.1", 47 | "ts-standard": "^10.0.0", 48 | "typescript": "^4.2.2" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/dom/query/QueryEngineTypes.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ElementAdjacentSelectorContext, 3 | ElementPrecedentSelectorContext, 4 | ElementSiblingSelectorContext 5 | } from './ElementSelectorParser' 6 | import type { EdiDomAbstractNode } from '../src/EdiDomAbstractNode' 7 | import type { EdiDomNode } from '../src/EdiDomTypes' 8 | 9 | export interface QueryEngineOpts { 10 | selector: string 11 | node: EdiDomNode | EdiDomAbstractNode 12 | mode: QueryEngineMode 13 | } 14 | 15 | export type QueryEngineMode = 'strict' | 'loose' 16 | 17 | export type QueryDirection = 'ascend' | 'descend' | 'sibling' 18 | 19 | export type QueryCombinator = ElementPrecedentSelectorContext | ElementAdjacentSelectorContext | ElementSiblingSelectorContext 20 | 21 | export interface ElementReference { 22 | segmentId: string 23 | elementId: number 24 | } 25 | 26 | export interface ValueReference { 27 | ref: ElementReference 28 | value: string 29 | } 30 | 31 | export interface PredicateReference extends ValueReference { 32 | comparison: 'equals' | 'not' | 'contains' 33 | } 34 | 35 | interface QueryYieldResult { 36 | done?: false 37 | value: TYield 38 | } 39 | 40 | interface QueryReturnResult { 41 | done: true 42 | value: undefined 43 | } 44 | 45 | type QueryIteratorResult = QueryYieldResult | QueryReturnResult 46 | 47 | export interface QueryIterator extends Iterator { 48 | // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. 49 | next: (...args: [] | [any]) => QueryIteratorResult 50 | return?: (value?: any) => QueryIteratorResult 51 | throw?: (e?: any) => QueryIteratorResult 52 | [Symbol.iterator]: () => QueryIterator 53 | } 54 | -------------------------------------------------------------------------------- /packages/shared/src/BaseCharStreams.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CharStreams, 3 | CodePointBuffer, 4 | CodePointCharStream, 5 | IntStream 6 | } from 'antlr4ts' 7 | import { Readable } from 'stream' 8 | import { streamToAsyncGenerator } from './helpers' 9 | 10 | /** Creates a CharStream from a Node.js Readable binary stream. If no encoding is provided, UTF-8 is assumed. */ 11 | export async function fromNodeReadable (readable: Readable, encoding?: BufferEncoding, sourceName?: string): Promise { 12 | const buffers = [] 13 | const generator = streamToAsyncGenerator(readable) 14 | 15 | for await (const chunk of generator) { 16 | buffers.push(chunk) 17 | } 18 | 19 | return fromNodeBuffer(Buffer.concat(buffers), encoding, sourceName) 20 | } 21 | 22 | /** Creates a CharStream from a Node.js Buffer. If no encoding is provided, UTF-8 is assumed. */ 23 | export function fromNodeBuffer (buffer: Buffer, encoding?: BufferEncoding, sourceName?: string): CodePointCharStream { 24 | if (sourceName === undefined || sourceName === null || sourceName.length === 0) { 25 | sourceName = IntStream.UNKNOWN_SOURCE_NAME 26 | } 27 | 28 | if (encoding === undefined || encoding === null || encoding.length === 0) { 29 | encoding = 'utf8' 30 | } 31 | 32 | const contents = buffer.toString(encoding) 33 | const codePointBufferBuilder = CodePointBuffer.builder(contents.length) 34 | const cb = new Uint16Array(contents.length) 35 | 36 | for (let i = 0; i < contents.length; i++) { 37 | cb[i] = contents.charCodeAt(i) 38 | } 39 | 40 | codePointBufferBuilder.append(cb) 41 | 42 | return CodePointCharStream.fromBuffer(codePointBufferBuilder.build(), sourceName) 43 | } 44 | 45 | export const fromString = CharStreams.fromString 46 | -------------------------------------------------------------------------------- /grammars/fact/EdiFactParser.g4: -------------------------------------------------------------------------------- 1 | parser grammar EdiFactParser; 2 | 3 | options { 4 | tokenVocab=EdiFactLexer ; 5 | } 6 | 7 | // The document root. 8 | document: interchange+; 9 | 10 | // Document structure 11 | interchange: 12 | serviceStringAdvice 13 | interchangeHeader 14 | (group+ | message+) 15 | interchangeTrailer; 16 | 17 | group: 18 | groupHeader 19 | message+ 20 | groupTrailer; 21 | 22 | message: 23 | messageHeader 24 | segment+ 25 | messageTrailer; 26 | 27 | segment: 28 | Tag 29 | element+ 30 | segmentEnd; 31 | 32 | serviceStringAdvice: 33 | SvcStringAdvice 34 | ControlChar 35 | ControlChar 36 | ControlChar 37 | ControlChar 38 | ControlChar 39 | segmentEnd; 40 | 41 | // Segment header and trailer statements. 42 | interchangeHeader: 43 | InterchangeHeader 44 | element+ 45 | segmentEnd; 46 | 47 | interchangeTrailer: 48 | InterchangeTrailer 49 | element+ 50 | segmentEnd; 51 | 52 | groupHeader: 53 | GroupHeader 54 | element+ 55 | segmentEnd; 56 | 57 | groupTrailer: 58 | GroupTrailer 59 | element+ 60 | segmentEnd; 61 | 62 | messageHeader: MessageHeader element+ segmentEnd; 63 | 64 | messageTrailer: MessageTrailer element+ segmentEnd; 65 | 66 | // EDIFACT components. 67 | segmentEnd: ControlChar ControlChar | ControlChar | SegmentTerminator EndOfLine | SegmentTerminator; 68 | 69 | element: DataSeparator (value|component|repitition)*; 70 | 71 | repitition: (value|component)* (RepititionSeparator (value|component)*)+; 72 | 73 | component: value* (ComponentSeparator value*)+; 74 | 75 | value: 76 | Tag 77 | | Char 78 | | NonPrintable 79 | | SvcStringAdvice 80 | | InterchangeHeader 81 | | InterchangeTrailer 82 | | GroupHeader 83 | | GroupTrailer 84 | | MessageHeader 85 | | MessageTrailer; 86 | -------------------------------------------------------------------------------- /packages/validator/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@js-edi/validator", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "7.2.3", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.3.tgz", 10 | "integrity": "sha512-idv5WZvKVXDqKralOImQgPM9v6WOdLNa0IY3B3doOjw/YxRGT8I+allIJ6kd7Uaj+SF1xZUSU+nPM5aDNBVtnw==", 11 | "requires": { 12 | "fast-deep-equal": "^3.1.1", 13 | "json-schema-traverse": "^1.0.0", 14 | "require-from-string": "^2.0.2", 15 | "uri-js": "^4.2.2" 16 | } 17 | }, 18 | "fast-deep-equal": { 19 | "version": "3.1.3", 20 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 21 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" 22 | }, 23 | "json-schema-traverse": { 24 | "version": "1.0.0", 25 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 26 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" 27 | }, 28 | "punycode": { 29 | "version": "2.1.1", 30 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 31 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" 32 | }, 33 | "require-from-string": { 34 | "version": "2.0.2", 35 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 36 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" 37 | }, 38 | "uri-js": { 39 | "version": "4.4.1", 40 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 41 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 42 | "requires": { 43 | "punycode": "^2.1.0" 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # Antlr4 output 107 | *.interp 108 | *.tokens 109 | **/.antlr/** 110 | 111 | # Generated files 112 | *.js 113 | *.d.ts 114 | .tmp* -------------------------------------------------------------------------------- /packages/dom/src/EdiDomValue.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { EdiDomNodeType } from './EdiDomNodeType' 4 | import type { EdiDomNode } from './EdiDomTypes' 5 | import type { EdiDomComponent } from './EdiDomComponent' 6 | import type { EdiDomElement } from './EdiDomElement' 7 | import type { EdiDomRoot } from './EdiDomRoot' 8 | import type { EdiDomRepeated } from './EdiDomRepeated' 9 | 10 | export interface EdiJsonValue { 11 | text: string 12 | } 13 | 14 | /** Value types supported for detection. */ 15 | export type EdiDomValueType = 'alpha' | 'numeric' | 'alphanumeric' 16 | 17 | /** The base value in the object model. */ 18 | export class EdiDomValue extends EdiDomAbstractNode { 19 | constructor (text?: string) { 20 | super() 21 | this.nodeType = EdiDomNodeType.Value 22 | this.type = 'alphanumeric' 23 | this._text = text ?? '' 24 | // Remove no-op methods to guarantee they can never be called. 25 | delete this.addChildNode 26 | delete this.removeChildNode 27 | } 28 | 29 | nodeType: EdiDomNodeType.Value 30 | parent: EdiDomComponent | EdiDomElement | EdiDomRepeated 31 | root: EdiDomRoot 32 | /** A type derived from the contents of the value. */ 33 | type: EdiDomValueType 34 | /** An EdiDomValue cannot have child nodes. */ 35 | addChildNode: never 36 | /** An EdiDomValue cannot get child nodes. */ 37 | getChildNode: never 38 | /** An EdiDomValue cannot remove child nodes. */ 39 | removeChildNode: never 40 | protected _text: string 41 | 42 | get innerEDI (): string { 43 | return this._text 44 | } 45 | 46 | get outerEDI (): string { 47 | return this._text 48 | } 49 | 50 | /** The string contents of the value. */ 51 | get text (): string { 52 | return this._text 53 | } 54 | 55 | get textContent (): string { 56 | return this._text 57 | } 58 | 59 | /** The string contents of the value. */ 60 | set text (_text: string) { 61 | this._text = _text 62 | } 63 | 64 | * walk (): Generator { 65 | yield this 66 | } 67 | 68 | toJSON (): EdiJsonValue { 69 | return { 70 | text: this._text 71 | } 72 | } 73 | 74 | fromJSON (input: EdiJsonValue): void { 75 | this._text = input.text 76 | } 77 | } 78 | 79 | EdiDomGlobal.Value = EdiDomValue 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JS-EDI 2 | 3 | Work in progress, not fit for production use. Parser for both EDIX12 and EDIFACT leveraging Antlr4 grammar. 4 | 5 | If I could call it J-EDI, I would. May the force be with you. 6 | 7 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ahuggins-nhs_js-edi&metric=alert_status)](https://sonarcloud.io/dashboard?id=ahuggins-nhs_js-edi) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=ahuggins-nhs_js-edi&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=ahuggins-nhs_js-edi) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=ahuggins-nhs_js-edi&metric=security_rating)](https://sonarcloud.io/dashboard?id=ahuggins-nhs_js-edi) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=ahuggins-nhs_js-edi&metric=ncloc)](https://sonarcloud.io/dashboard?id=ahuggins-nhs_js-edi) 8 | 9 | # Features 10 | 11 | - Support for EDI X12 and EDIFACT 12 | - Parse natively in JS to a document object model (DOM) 13 | - Generate from a constructed dom 14 | - Query the dom using element selectors 15 | - Traverse as a hierarchical dom 16 | - Transform to and from JS objects 17 | - Serialize to and from JSON EDI notation 18 | - Validate parsed EDI using JSON Schema 19 | 20 | # What's done prior to alpha so far 21 | 22 | - EDI X12 support; parser in antlr4ts is fully functional, detects component and repeated elements 23 | - Document object model, supports detecting difference between EDI flavors and complexe nodes 24 | - Validator, works with the DOM to be able to validate a document with JSON Schema 25 | - Element selector query language 26 | 27 | # Known TO-DOs 28 | 29 | ## Before alpha release 30 | - Complete initial documentation 31 | 32 | ## Before beta release 33 | - Improve accuracy of parsing/handling EDIFACT 34 | - Better service string advice detection 35 | - Conform to EDIFACT standard with "release indicator" 36 | - Conform to EDIFACT charactar sets better (technically only UNOA and UNOB supported for now) 37 | 38 | ## Before release candidate 39 | - Add custom eventing in document parser 40 | - Events for each node type generated 41 | - on errors 42 | - on parse start and stop 43 | - Stabilize 44 | - Finish writing tests 45 | 46 | # Roadmap 47 | - Complete EDIFACT support and optimize grammar 48 | - Stabilize all APIs 49 | - Expose listener APIs 50 | - Write examples 51 | - Make tests more broadly specific to the parsing of grammars(??) 52 | -------------------------------------------------------------------------------- /test/data/850_fat.edi: -------------------------------------------------------------------------------- 1 | ISA*01*0000000000*01*ABCCO *12*4405197800 *01*999999999 *101127*1719*U*00400*000003438*0*P*>~ 2 | GS*PO*4405197800*999999999*20101127*1719*1421*X*004010VICS~ 3 | ST*850*000000010~ 4 | BEG*00*SA*08292233294**20101127*610385385~ 5 | REF*DP*038~ 6 | REF*PS*R~ 7 | ITD*14*3*2**45**46~ 8 | DTM*002*20101214~ 9 | PKG*F*68***PALLETIZE SHIPMENT~ 10 | PKG*F*66***REGULAR~ 11 | TD5*A*92*P3**SEE XYZ RETAIL ROUTING GUIDE~ 12 | N1*ST*XYZ RETAIL*9*0003947268292~ 13 | N3*31875 SOLON RD~ 14 | N4*SOLON*OH*44139~ 15 | PO1*1*120*EA*9.25*TE*CB*065322-117*PR*RO*VN*AB3542~ 16 | PID*F****SMALL WIDGET~ 17 | PO4*4*4*EA*PLT94**3*LR*15*CT~ 18 | PO1*2*220*EA*13.79*TE*CB*066850-116*PR*RO*VN*RD5322~ 19 | PID*F****MEDIUM WIDGET~ 20 | PO4*2*2*EA~ 21 | PO1*3*126*EA*10.99*TE*CB*060733-110*PR*RO*VN*XY5266~ 22 | PID*F****LARGE WIDGET~ 23 | PO4*6*1*EA*PLT94**3*LR*12*CT~ 24 | PO1*4*76*EA*4.35*TE*CB*065308-116*PR*RO*VN*VX2332~ 25 | PID*F****NANO WIDGET~ 26 | PO4*4*4*EA*PLT94**6*LR*19*CT~ 27 | PO1*5*72*EA*7.5*TE*CB*065374-118*PR*RO*VN*RV0524~ 28 | PID*F****BLUE WIDGET~ 29 | PO4*4*4*EA~ 30 | PO1*6*696*EA*9.55*TE*CB*067504-118*PR*RO*VN*DX1875~ 31 | PID*F****ORANGE WIDGET~ 32 | PO4*6*6*EA*PLT94**3*LR*10*CT~ 33 | CTT*6~ 34 | AMT*1*13045.94~ 35 | SE*33*000000010~ 36 | GE*1*1421~ 37 | IEA*1*000003438~ 38 | ISA*01*0000000000*01*ABCCO *12*4405197800 *01*999999999 *101127*1719*U*00400*000003438*0*P*>~ 39 | GS*PO*4405197800*999999999*20101127*1719*1421*X*004010VICS~ 40 | ST*850*000000010~ 41 | BEG*00*SA*08292233294**20101127*610385385~ 42 | REF*DP*038~ 43 | REF*PS*R~ 44 | ITD*14*3*2**45**46~ 45 | DTM*002*20101214~ 46 | PKG*F*68***PALLETIZE SHIPMENT~ 47 | PKG*F*66***REGULAR~ 48 | TD5*A*92*P3**SEE XYZ RETAIL ROUTING GUIDE~ 49 | N1*ST*XYZ RETAIL*9*0003947268292~ 50 | N3*31875 SOLON RD~ 51 | N4*SOLON*OH*44139~ 52 | PO1*1*120*EA*9.25*TE*CB*065322-117*PR*RO*VN*AB3542~ 53 | PID*F****SMALL WIDGET~ 54 | PO4*4*4*EA*PLT94**3*LR*15*CT~ 55 | PO1*2*220*EA*13.79*TE*CB*066850-116*PR*RO*VN*RD5322~ 56 | PID*F****MEDIUM WIDGET~ 57 | PO4*2*2*EA~ 58 | PO1*3*126*EA*10.99*TE*CB*060733-110*PR*RO*VN*XY5266~ 59 | PID*F****LARGE WIDGET~ 60 | PO4*6*1*EA*PLT94**3*LR*12*CT~ 61 | PO1*4*76*EA*4.35*TE*CB*065308-116*PR*RO*VN*VX2332~ 62 | PID*F****NANO WIDGET~ 63 | PO4*4*4*EA*PLT94**6*LR*19*CT~ 64 | PO1*5*72*EA*7.5*TE*CB*065374-118*PR*RO*VN*RV0524~ 65 | PID*F****BLUE WIDGET~ 66 | PO4*4*4*EA~ 67 | PO1*6*696*EA*9.55*TE*CB*067504-118*PR*RO*VN*DX1875~ 68 | PID*F****ORANGE WIDGET~ 69 | PO4*6*6*EA*PLT94**3*LR*10*CT~ 70 | CTT*6~ 71 | AMT*1*13045.94~ 72 | SE*33*000000010~ 73 | GE*1*1421~ 74 | IEA*1*000003438~ 75 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomTypes.ts: -------------------------------------------------------------------------------- 1 | import type { EdiDomComponent } from './EdiDomComponent' 2 | import type { EdiDomElement } from './EdiDomElement' 3 | import type { EdiDomGroup } from './EdiDomGroup' 4 | import type { EdiDomInterchange } from './EdiDomInterchange' 5 | import type { EdiDomMessage } from './EdiDomMessage' 6 | import type { EdiDomNodeType } from './EdiDomNodeType' 7 | import type { EdiDomRepeated } from './EdiDomRepeated' 8 | import type { EdiDomRoot } from './EdiDomRoot' 9 | import type { EdiDomSegment } from './EdiDomSegment' 10 | import type { EdiDomValue } from './EdiDomValue' 11 | 12 | /** 13 | * Options for parsing/reconstructing an EDI document. 14 | * In the case of EDIFACT, this represents the UNA segment. 15 | * In the case of EDIX12, this represents control characters in the ISA segment. 16 | */ 17 | export interface EdiDomOptions { 18 | /** The component separator for EDI documents. */ 19 | componentSeparator: string 20 | /** The data separator for EDI documents. */ 21 | dataSeparator: string 22 | /** The decimal mark for EDIFACT documents only. */ 23 | decimalMark?: ',' | '.' 24 | /** The release indicator for EDIFACT documents only. */ 25 | releaseIndicator?: string 26 | /** The repitition separator for EDI documents. */ 27 | repititionSeparator?: string 28 | /** The segment terminator for EDI documents. */ 29 | segmentTerminator: string 30 | /** The formatting mark for prettified EDI documents. */ 31 | endOfLine?: string 32 | } 33 | 34 | export type EdiDomContainer = EdiDomInterchange | EdiDomGroup | EdiDomMessage 35 | 36 | export type InterchangeChild = EdiDomGroup | EdiDomMessage 37 | 38 | export type ElementChild = RepeatedChild | EdiDomRepeated 39 | 40 | export type RepeatedChild = EdiDomComponent | EdiDomValue 41 | 42 | export type EdiDomNode = 43 | | EdiDomRoot 44 | | EdiDomInterchange 45 | | EdiDomGroup 46 | | EdiDomMessage 47 | | EdiDomSegment 48 | | EdiDomElement 49 | | EdiDomRepeated 50 | | EdiDomComponent 51 | | EdiDomValue 52 | 53 | export type EdiDomDocumentType = 'EDIFACT' | 'EDIX12' 54 | 55 | export interface EdiDomNodeTagMap extends Record { 56 | '*': EdiDomNode 57 | All: EdiDomNode 58 | Root: EdiDomRoot 59 | Interchange: EdiDomInterchange 60 | Group: EdiDomGroup 61 | Message: EdiDomMessage 62 | Transaction: EdiDomMessage 63 | Segment: EdiDomSegment 64 | Repeated: EdiDomRepeated 65 | Element: EdiDomElement 66 | Component: EdiDomComponent 67 | Value: EdiDomValue 68 | ISA: EdiDomInterchange 69 | GS: EdiDomGroup 70 | ST: EdiDomMessage 71 | UNB: EdiDomInterchange 72 | UNE: EdiDomGroup 73 | UNH: EdiDomMessage 74 | } 75 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomHelpers.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomGlobal } from './EdiDomGlobal' 2 | import type { EdiDomGroup } from './EdiDomGroup' 3 | import type { EdiDomInterchange } from './EdiDomInterchange' 4 | import type { EdiDomRoot } from './EdiDomRoot' 5 | import type { EdiDomSegment } from './EdiDomSegment' 6 | import type { EdiDomContainer, EdiDomNode, ElementChild } from './EdiDomTypes' 7 | 8 | /** Method for relating a child, parent, and root node. */ 9 | export function relate (child: EdiDomNode, parent: typeof child['parent'], root: EdiDomRoot): void { 10 | child.parent = parent 11 | 12 | for (const node of child.walk()) { 13 | node.root = root 14 | } 15 | } 16 | 17 | /** Method to destroy the relationship of a child to its parent and root nodes. */ 18 | export function unrelate (child: EdiDomNode): void { 19 | child.parent = undefined 20 | 21 | for (const node of child.walk()) { 22 | node.root = undefined 23 | } 24 | } 25 | 26 | /** Method for narrowing the type of array of nodes. */ 27 | export function isArrayType (array: any[], compare: T): array is T[] { 28 | return array.length === 0 || array[0].nodeType === compare.nodeType 29 | } 30 | 31 | export function containerFromJson > (recipient: R, donor: D, assignChildren: () => void): void { 32 | const genericSegment = (donorData: any): EdiDomSegment => { 33 | const domSegment = new EdiDomGlobal.Segment() 34 | 35 | domSegment.fromJSON(donorData) 36 | 37 | return domSegment 38 | } 39 | 40 | if ('header' in donor) { 41 | recipient.header = genericSegment(donor.header) 42 | } 43 | 44 | assignChildren() 45 | 46 | if ('trailer' in donor) { 47 | recipient.trailer = genericSegment(donor.trailer) 48 | } 49 | } 50 | 51 | export function assignMessagesFromJson (recipient: EdiDomInterchange | EdiDomGroup, donor: Record): void { 52 | if (Array.isArray(donor.messages) && donor.messages.length > 0) { 53 | for (const message of donor.messages) { 54 | const domMessage = new EdiDomGlobal.Message() 55 | 56 | domMessage.fromJSON(message) 57 | relate(domMessage, recipient, recipient.root) 58 | recipient.messages.push(domMessage) 59 | } 60 | } 61 | } 62 | 63 | export function messagesAsContent (node: EdiDomInterchange | EdiDomGroup): string { 64 | let content = '' 65 | 66 | if (Array.isArray(node.messages) && node.messages.length > 0) { 67 | for (const message of node.messages) { 68 | const innerContent = message.textContent.split('\n') 69 | 70 | content += ' ' + innerContent.join('\n ') + '\n' 71 | } 72 | } 73 | 74 | return content 75 | } -------------------------------------------------------------------------------- /grammars/query/ElementSelector.g4: -------------------------------------------------------------------------------- 1 | grammar ElementSelector; 2 | 3 | selector: 4 | segmentTag 5 | | elementSelector 6 | | parentSegmentSelector 7 | | hlPathSelector 8 | | loopPathSelector 9 | | elementValueSelector 10 | | elementNotValueSelector 11 | | elementContainsValueSelector 12 | | elementSiblingSelector 13 | | elementPrecedentSelector 14 | | elementAdjacentSelector 15 | ; 16 | 17 | // Element reference adjacent to selector. 18 | elementAdjacentSelector: 19 | (ElementReference | hlPathSelector | parentSegmentSelector | loopPathSelector) 20 | '~' 21 | (elementValueSelector | elementNotValueSelector | elementContainsValueSelector) 22 | ; 23 | // Element reference precedent to selector. 24 | elementPrecedentSelector: 25 | (ElementReference | hlPathSelector | parentSegmentSelector | loopPathSelector) 26 | '?' 27 | (elementValueSelector | elementNotValueSelector | elementContainsValueSelector) 28 | ; 29 | // Element reference which is a sibling to selector. 30 | elementSiblingSelector: 31 | (ElementReference | hlPathSelector | parentSegmentSelector | loopPathSelector) 32 | ':' 33 | (elementValueSelector | elementNotValueSelector | elementContainsValueSelector) 34 | ; 35 | // Element not value selector. 36 | elementContainsValueSelector: ElementReference '*' ElementValue; 37 | // Element not value selector. 38 | elementNotValueSelector: ElementReference '!' ElementValue; 39 | // Element value selector. 40 | elementValueSelector: ElementReference ElementValue; 41 | // Loop path selector. 42 | loopPathSelector: SegmentID '^' SegmentID '-' (ElementReference | parentSegmentSelector); 43 | // Element at HL path selector. 44 | hlPathSelector: 'HL' ('+' AnyCharacter)+ '-' (ElementReference | parentSegmentSelector); 45 | // Element at parent segment path selector. 46 | parentSegmentSelector: (SegmentID '-')+ ElementReference; 47 | // Element reference selector. 48 | elementSelector: ElementReference; 49 | // Segment tag selector. 50 | segmentTag: SegmentID; 51 | 52 | // Fragments for printable character detection. 53 | fragment Number: '\u0030'..'\u0039'; 54 | fragment Letter: '\u0041'..'\u005A' | '\u0061'..'\u007A'; 55 | fragment Special: '\u0020'..'\u002F' | '\u003A'..'\u0040' | '\u005B'..'\u0060' | '\u007B'..'\u007E'; 56 | fragment AlphaNumeric: Number | Letter; 57 | fragment SegmentID2: Letter AlphaNumeric; 58 | fragment SegmentID3: Letter AlphaNumeric AlphaNumeric; 59 | 60 | // Value identifier 61 | ElementValue: '[' ['"] (AnyCharacter|ElementID|SegmentID|ElementReference)* ['"] ']'; 62 | // Element reference 63 | ElementReference: SegmentID ElementID; 64 | // Element identifier. 65 | ElementID: Number Number; 66 | // Segment identifier. 67 | SegmentID: SegmentID2 | SegmentID3; 68 | // Any single unicode character. 69 | AnyCharacter: '\u0000'..'\uFFFF'; 70 | -------------------------------------------------------------------------------- /packages/dom/query/helpers.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomNodeAlias } from '../src/EdiDomNodeAlias' 2 | import type { TerminalNode } from 'antlr4ts/tree' 3 | import type { EdiDomNodeTagMap } from '../src/EdiDomTypes' 4 | import type { 5 | ElementReference, 6 | PredicateReference, 7 | QueryCombinator, 8 | ValueReference 9 | } from './QueryEngineTypes' 10 | import type { 11 | ElementContainsValueSelectorContext, 12 | ElementNotValueSelectorContext, 13 | ElementValueSelectorContext 14 | } from './ElementSelectorParser' 15 | 16 | /** Transform an element reference terminal node into ElementReference object. */ 17 | export function elementReference (ref: TerminalNode): ElementReference { 18 | if (typeof ref === 'undefined') return 19 | 20 | const index = ref.text.length === 5 ? 3 : 2 21 | 22 | return { 23 | segmentId: ref.text.substring(0, index).toUpperCase(), 24 | elementId: parseFloat(ref.text.substring(index)) 25 | } 26 | } 27 | 28 | export function elementValue (ref: TerminalNode): string { 29 | if (typeof ref === 'undefined') return 30 | 31 | return ref.text.substring(2, ref.text.length - 2) 32 | } 33 | 34 | export function valueReference ( 35 | selector: ElementValueSelectorContext | ElementNotValueSelectorContext | ElementContainsValueSelectorContext 36 | ): ValueReference { 37 | return { 38 | ref: elementReference(selector.ElementReference()), 39 | value: elementValue(selector.ElementValue()) 40 | } 41 | } 42 | 43 | export function predicateReference (selector: QueryCombinator): PredicateReference { 44 | if (typeof selector.elementValueSelector() === 'object') { 45 | return { 46 | ...valueReference(selector.elementValueSelector()), 47 | comparison: 'equals' 48 | } 49 | } else if (typeof selector.elementNotValueSelector() === 'object') { 50 | return { 51 | ...valueReference(selector.elementValueSelector()), 52 | comparison: 'not' 53 | } 54 | } else if (typeof selector.elementContainsValueSelector() === 'object') { 55 | return { 56 | ...valueReference(selector.elementValueSelector()), 57 | comparison: 'contains' 58 | } 59 | } 60 | } 61 | 62 | export function isNodeTag (selector: K | string): selector is K { 63 | return selector in EdiDomNodeAlias 64 | } 65 | 66 | export function isSegmentTag (tag: T | string): tag is T { 67 | const alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 68 | const alphanumeric = alpha + '0123456789' 69 | 70 | if (tag.length === 2) { 71 | return alpha.includes(tag.charAt(0)) && alphanumeric.includes(tag.charAt(1)) 72 | } else if (tag.length === 3) { 73 | return alpha.includes(tag.charAt(0)) && 74 | alphanumeric.includes(tag.charAt(1)) && 75 | alphanumeric.includes(tag.charAt(2)) 76 | } 77 | 78 | return false 79 | } 80 | -------------------------------------------------------------------------------- /grammars/x12/EdiX12Lexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar EdiX12Lexer; 2 | 3 | @lexer::header { 4 | // This statement may be removed once option `superClass` is correctly generated by antlr4ts. 5 | import { X12BaseLexer } from "./X12BaseLexer"; 6 | } 7 | 8 | options { 9 | superClass=X12BaseLexer; 10 | } 11 | 12 | tokens { 13 | DataSeparator, 14 | RepititionSeparator, 15 | ComponentSeparator, 16 | SegmentTerminator, 17 | EndOfLine 18 | } 19 | 20 | // Fragments for non-printable character detection. 21 | fragment SOH: '\u0001'; 22 | fragment STX: '\u0002'; 23 | fragment ETX: '\u0003'; 24 | fragment EOT: '\u0004'; 25 | fragment ENQ: '\u0005'; 26 | fragment ACK: '\u0006'; 27 | fragment BEL: '\u0007'; 28 | fragment HT: '\u0009'; 29 | fragment LF: '\u000A'; 30 | fragment VT: '\u000B'; 31 | fragment FF: '\u000C'; 32 | fragment CR: '\u000D'; 33 | fragment DC1: '\u0011'; 34 | fragment DC2: '\u0012'; 35 | fragment DC3: '\u0013'; 36 | fragment DC4: '\u0014'; 37 | fragment NAK: '\u0015'; 38 | fragment SYN: '\u0016'; 39 | fragment ETB: '\u0017'; 40 | fragment FS: '\u001C'; 41 | fragment GS: '\u001D'; 42 | fragment RS: '\u001E'; 43 | fragment US: '\u001F'; 44 | fragment AsciiControl: 45 | SOH | STX 46 | | ETX | EOT 47 | | ENQ | ACK 48 | | BEL | HT 49 | | LF | VT 50 | | FF | CR 51 | | DC1 | DC2 52 | | DC3 | DC4 53 | | NAK | SYN 54 | | ETB | FS 55 | | GS | RS 56 | | US; 57 | 58 | // Fragments for printable character detection. 59 | fragment Number: '\u0030'..'\u0039'; 60 | fragment Letter: '\u0041'..'\u005A' | '\u0061'..'\u007A'; 61 | fragment Special: '\u0020'..'\u002F' | '\u003A'..'\u0040' | '\u005B'..'\u0060' | '\u007B'..'\u007E'; 62 | 63 | // Special token for discovering the position of control characters for a given EDI document. 64 | ControlChar: 65 | Special { this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) } 66 | | CR LF { this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) } 67 | | CR { this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) } 68 | | LF { this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) } 69 | | AsciiControl { this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) }; 70 | 71 | // All valid printable characters. 72 | Char: 73 | Letter 74 | | Number 75 | | Special { !this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) }; 76 | 77 | // Reserved segment tags. 78 | InterchangeHeader: 'ISA'; 79 | InterchangeTrailer: 'IEA'; 80 | GroupHeader: 'GS'; 81 | GroupTrailer: 'GE'; 82 | TransactionHeader: 'ST'; 83 | TransactionTrailer: 'SE'; 84 | 85 | // All other segment tags. 86 | Tag: Letter (Letter|Number) (Letter|Number)*; 87 | 88 | // All non-printable and formatting marks 89 | NonPrintable: 90 | CR LF { !this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) } 91 | | AsciiControl { !this.isCtlCharPos }? { this.handleControlChars(EdiX12Lexer) }; 92 | -------------------------------------------------------------------------------- /grammars/fact/EdiFactLexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar EdiFactLexer; 2 | 3 | @lexer::header { 4 | // This statement may be removed once option `superClass` is correctly generated by antlr4ts. 5 | import { FactBaseLexer } from "./FactBaseLexer"; 6 | } 7 | 8 | options { 9 | superClass=FactBaseLexer; 10 | } 11 | 12 | tokens { 13 | DataSeparator, 14 | RepititionSeparator, 15 | ComponentSeparator, 16 | SegmentTerminator, 17 | DecimalMark, 18 | ReleaseIndicator, 19 | EndOfLine 20 | } 21 | 22 | // Fragments for non-printable character detection. 23 | fragment SOH: '\u0001'; 24 | fragment STX: '\u0002'; 25 | fragment ETX: '\u0003'; 26 | fragment EOT: '\u0004'; 27 | fragment ENQ: '\u0005'; 28 | fragment ACK: '\u0006'; 29 | fragment BEL: '\u0007'; 30 | fragment HT: '\u0009'; 31 | fragment LF: '\u000A'; 32 | fragment VT: '\u000B'; 33 | fragment FF: '\u000C'; 34 | fragment CR: '\u000D'; 35 | fragment DC1: '\u0011'; 36 | fragment DC2: '\u0012'; 37 | fragment DC3: '\u0013'; 38 | fragment DC4: '\u0014'; 39 | fragment NAK: '\u0015'; 40 | fragment SYN: '\u0016'; 41 | fragment ETB: '\u0017'; 42 | fragment FS: '\u001C'; 43 | fragment GS: '\u001D'; 44 | fragment RS: '\u001E'; 45 | fragment US: '\u001F'; 46 | fragment AsciiControl: 47 | SOH | STX 48 | | ETX | EOT 49 | | ENQ | ACK 50 | | BEL | HT 51 | | LF | VT 52 | | FF | CR 53 | | DC1 | DC2 54 | | DC3 | DC4 55 | | NAK | SYN 56 | | ETB | FS 57 | | GS | RS 58 | | US; 59 | 60 | // Fragments for printable character detection. 61 | fragment Number: '\u0030'..'\u0039'; 62 | fragment Letter: '\u0041'..'\u005A' | '\u0061'..'\u007A'; 63 | fragment Special: '\u0020'..'\u002F' | '\u003A'..'\u0040' | '\u005B'..'\u0060' | '\u007B'..'\u007E'; 64 | 65 | // Special token for discovering the position of control characters for a given EDI document. 66 | ControlChar: 67 | Special { this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) } 68 | | CR LF { this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) } 69 | | CR { this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) } 70 | | LF { this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) } 71 | | AsciiControl { this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) }; 72 | 73 | // All valid printable characters. 74 | Char: 75 | Letter 76 | | Number 77 | | Special { !this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) }; 78 | 79 | // Reserved segment tags. 80 | SvcStringAdvice: 'UNA' { this.handleUNA() }; 81 | InterchangeHeader: 'UNB'; 82 | InterchangeTrailer: 'UNZ'; 83 | GroupHeader: 'UNG'; 84 | GroupTrailer: 'UNE'; 85 | MessageHeader: 'UNH'; 86 | MessageTrailer: 'UNT'; 87 | TextSegment: 'TXT'; 88 | SectionControl: 'UNS'; 89 | 90 | // All other segment tags. 91 | Tag: Letter (Letter|Number) (Letter|Number)*; 92 | 93 | // All non-printable and formatting marks 94 | NonPrintable: 95 | CR LF { !this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) } 96 | | AsciiControl { !this.isCtlCharPos }? { this.handleControlChars(EdiFactLexer) }; 97 | -------------------------------------------------------------------------------- /grammars/x12/EdiX12Parser.g4: -------------------------------------------------------------------------------- 1 | parser grammar EdiX12Parser; 2 | 3 | options { 4 | tokenVocab = EdiX12Lexer ; 5 | } 6 | 7 | // The document root. 8 | document: interchange+; 9 | 10 | // Document structure 11 | interchange: 12 | interchangeHeader 13 | group+ 14 | interchangeTrailer; 15 | 16 | group: 17 | groupHeader 18 | transaction+ 19 | groupTrailer; 20 | 21 | transaction: 22 | transactionHeader 23 | segment+ 24 | transactionTrailer; 25 | 26 | segment: 27 | Tag 28 | (element|repitition)+ 29 | segmentEnd; 30 | 31 | // Segment header and trailer statements. 32 | interchangeHeader: 33 | InterchangeHeader 34 | interchangeElement 35 | interchangeElement 36 | interchangeElement 37 | interchangeElement 38 | interchangeElement 39 | interchangeElement 40 | interchangeElement 41 | interchangeElement 42 | interchangeElement 43 | interchangeElement 44 | interchangeElement 45 | interchangeElement 46 | interchangeElement 47 | interchangeElement 48 | interchangeElement 49 | interchangeElement 50 | segmentEnd; 51 | 52 | interchangeTrailer: 53 | InterchangeTrailer 54 | strictElement 55 | strictElement 56 | segmentEnd; 57 | 58 | groupHeader: 59 | GroupHeader 60 | strictElement 61 | strictElement 62 | strictElement 63 | strictElement 64 | strictElement 65 | strictElement 66 | strictElement 67 | strictElement 68 | segmentEnd; 69 | 70 | groupTrailer: 71 | GroupTrailer 72 | strictElement 73 | strictElement 74 | segmentEnd; 75 | 76 | transactionHeader: 77 | ( 78 | TransactionHeader 79 | strictElement 80 | strictElement 81 | strictElement 82 | segmentEnd 83 | ) 84 | | ( 85 | TransactionHeader 86 | strictElement 87 | strictElement 88 | segmentEnd 89 | ); 90 | 91 | transactionTrailer: 92 | TransactionTrailer 93 | strictElement 94 | strictElement 95 | segmentEnd; 96 | 97 | // X12 components. 98 | segmentEnd: ControlChar ControlChar | ControlChar | SegmentTerminator EndOfLine | SegmentTerminator; 99 | 100 | repitition: element repeatedElement+; 101 | element: DataSeparator (value|component)*; 102 | 103 | interchangeElement: dataCharElement | strictElement | repititionCharElement | componentCharElement; 104 | strictElement: DataSeparator value+; 105 | dataCharElement: (ControlChar | DataSeparator) value+; 106 | repititionCharElement: DataSeparator (ControlChar | RepititionSeparator | value); 107 | componentCharElement: DataSeparator (ControlChar | ComponentSeparator); 108 | 109 | repeatedElement: RepititionSeparator (value|component)*; 110 | 111 | component: value* (ComponentSeparator value*)+; 112 | 113 | value: 114 | Tag 115 | | Char 116 | | NonPrintable 117 | | InterchangeHeader 118 | | InterchangeTrailer 119 | | GroupHeader 120 | | GroupTrailer 121 | | TransactionHeader 122 | | TransactionTrailer; 123 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomAbstractNode.ts: -------------------------------------------------------------------------------- 1 | import { QueryEngine } from '../query/QueryEngine' 2 | import type { QueryEngineList } from '../query/QueryEngineList' 3 | import type { EdiDomElement } from './EdiDomElement' 4 | import type { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomRoot } from './EdiDomRoot' 6 | import type { EdiDomSegment } from './EdiDomSegment' 7 | import type { EdiDomNode, EdiDomNodeTagMap } from './EdiDomTypes' 8 | 9 | export abstract class EdiDomAbstractNode { 10 | protected _header?: EdiDomSegment 11 | protected _trailer?: EdiDomSegment 12 | protected _text?: string 13 | 14 | /** The node type indicating the edi DOm instance member; use this to determine a node's class instead of `instanceof`. */ 15 | abstract nodeType: EdiDomNodeType 16 | /** The parent node of this instance. A document root will recursively reference itself. */ 17 | abstract parent: EdiDomNode 18 | /** The absolute root node of this instance. */ 19 | abstract root: EdiDomRoot 20 | 21 | /** The child nodes of this node serialized to an EDI DOMString. */ 22 | abstract get innerEDI (): string 23 | 24 | /** This node and its children serialized to an EDI DOMString. */ 25 | abstract get outerEDI (): string 26 | 27 | /** Synonymous with `outerEDI`. */ 28 | abstract get text (): string 29 | 30 | /** This node and its children rendered to a white-space delimited text string. */ 31 | abstract get textContent (): string 32 | 33 | /** Add a child node to the dom. On value nodes, this is undefined. */ 34 | abstract addChildNode (child: EdiDomNode): void 35 | 36 | /** Get a child node from this node. */ 37 | abstract getChildNode (position: string | number): EdiDomNode 38 | 39 | /** Remove a child node from the dom. On value nodes, this is undefined. */ 40 | abstract removeChildNode (child: EdiDomNode): void 41 | 42 | /** Sequentially walk the Document Object Model starting with this node. */ 43 | abstract walk (): Generator 44 | 45 | /** Return a cleaned EdiDomNode for serialization; removes circular references and verbose node types. */ 46 | abstract toJSON (): any 47 | 48 | /** Populate an EdiDomNode from serialized JSON EDI notation. */ 49 | abstract fromJSON (json: any): void 50 | 51 | /** Returns the first element that is a descendant of node that matches selectors. */ 52 | querySelector (selector: K): EdiDomNodeTagMap[K] 53 | querySelector (selector: string): E 54 | querySelector (selector: string): EdiDomNode | QueryEngineList { 55 | const query = new QueryEngine({ selector, node: this, mode: 'strict' }) 56 | const evaluate = query.evaluate() 57 | const { value } = evaluate.next() 58 | 59 | return value 60 | } 61 | 62 | /** Returns all element descendants of node that match selectors. */ 63 | querySelectorAll (selector: K): QueryEngineList 64 | querySelectorAll (selector: string): QueryEngineList 65 | querySelectorAll (selector: string): QueryEngineList { 66 | const query = new QueryEngine({ selector, node: this, mode: 'strict' }) 67 | 68 | return query.list() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomComponent.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomElement } from './EdiDomElement' 6 | import type { EdiDomRoot } from './EdiDomRoot' 7 | import type { EdiDomNode } from './EdiDomTypes' 8 | import type { EdiDomValue, EdiJsonValue } from './EdiDomValue' 9 | import type { EdiDomRepeated } from './EdiDomRepeated' 10 | 11 | export interface EdiJsonComponent { 12 | values: EdiJsonValue[] 13 | } 14 | 15 | /** An intermediate value type in the object model, holding an array of values. */ 16 | export class EdiDomComponent extends EdiDomAbstractNode { 17 | constructor () { 18 | super() 19 | this.nodeType = EdiDomNodeType.Component 20 | this.values = [] 21 | } 22 | 23 | nodeType: EdiDomNodeType.Component 24 | parent: EdiDomElement | EdiDomRepeated 25 | /** One or more values of the component value. */ 26 | values: EdiDomValue[] 27 | /** The root of this instance. */ 28 | root: EdiDomRoot 29 | 30 | get innerEDI (): string { 31 | return this.values 32 | .map(value => value.text) 33 | .join(this.root.options.componentSeparator) 34 | } 35 | 36 | get outerEDI (): string { 37 | return this.innerEDI 38 | } 39 | 40 | /** The read-only text representation of this node. */ 41 | get text (): string { 42 | return this.innerEDI 43 | } 44 | 45 | get textContent (): string { 46 | return this.values 47 | .map(value => value.textContent) 48 | .join('\t') 49 | } 50 | 51 | /** Add a value to this componenet. */ 52 | addChildNode (child: EdiDomValue): void { 53 | if (child.nodeType === EdiDomNodeType.Value) { 54 | relate(child, this, this.root) 55 | this.values.push(child) 56 | } 57 | } 58 | 59 | /** Get a child value by zero-based index. */ 60 | getChildNode (index: number): EdiDomValue { 61 | return this.values[index] 62 | } 63 | 64 | /** Remove a child by DOM value instance. */ 65 | removeChildNode (child: EdiDomValue): void 66 | /** Remove a child by literal string value. */ 67 | removeChildNode (value: string): void 68 | removeChildNode (child: EdiDomValue | string): void { 69 | const index = typeof child === 'string' 70 | ? this.values.findIndex(value => value.text === child) 71 | : this.values.indexOf(child) 72 | 73 | if (index > -1) { 74 | unrelate(this.values[index]) 75 | this.values.splice(index, 1) 76 | } 77 | } 78 | 79 | * walk (): Generator { 80 | yield this 81 | 82 | for (const value of this.values) { 83 | for (const node of value.walk()) { 84 | yield node 85 | } 86 | } 87 | } 88 | 89 | toJSON (): EdiJsonComponent { 90 | return { 91 | values: this.values.map(value => value.toJSON()) 92 | } 93 | } 94 | 95 | fromJSON (input: EdiJsonComponent): void { 96 | if (Array.isArray(input.values)) { 97 | this.values = [] 98 | 99 | for (const value of input.values) { 100 | const domValue = new EdiDomGlobal.Value() 101 | 102 | domValue.fromJSON(value) 103 | relate(domValue, this, this.root) 104 | this.values.push(domValue) 105 | } 106 | } 107 | } 108 | } 109 | 110 | EdiDomGlobal.Component = EdiDomComponent 111 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomRepeated.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { isArrayType, relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomComponent, EdiJsonComponent } from './EdiDomComponent' 6 | import type { EdiDomElement } from './EdiDomElement' 7 | import type { EdiDomRoot } from './EdiDomRoot' 8 | import type { EdiDomNode, RepeatedChild } from './EdiDomTypes' 9 | import type { EdiDomValue, EdiJsonValue } from './EdiDomValue' 10 | 11 | export interface EdiJsonRepeated { 12 | repeats: EdiJsonComponent[] | EdiJsonValue[] 13 | } 14 | 15 | export class EdiDomRepeated extends EdiDomAbstractNode { 16 | constructor () { 17 | super() 18 | this.nodeType = EdiDomNodeType.Repeated 19 | this.repeats = [] 20 | } 21 | 22 | nodeType: EdiDomNodeType.Repeated 23 | parent: EdiDomElement 24 | root: EdiDomRoot 25 | repeats: EdiDomComponent[] | EdiDomValue[] 26 | 27 | get innerEDI (): string { 28 | return this.repeats 29 | .map(value => value.text) 30 | .join(this.root.options.repititionSeparator) 31 | } 32 | 33 | get outerEDI (): string { 34 | return this.innerEDI 35 | } 36 | 37 | get text (): string { 38 | return this.innerEDI 39 | } 40 | 41 | get textContent (): string { 42 | return this.repeats 43 | .map(value => ' ' + value.textContent) 44 | .join('\n') 45 | } 46 | 47 | addChildNode (child: RepeatedChild): void { 48 | switch (child.nodeType) { 49 | case EdiDomNodeType.Component: 50 | case EdiDomNodeType.Value: 51 | if (isArrayType(this.repeats, child)) { 52 | relate(child, this, this.root) 53 | this.repeats.push(child as any) 54 | } 55 | break 56 | } 57 | } 58 | 59 | getChildNode (index: number): RepeatedChild { 60 | return this.repeats[index] 61 | } 62 | 63 | removeChildNode (child: RepeatedChild): void { 64 | let index = -1 65 | 66 | switch (child.nodeType) { 67 | case EdiDomNodeType.Component: 68 | case EdiDomNodeType.Value: 69 | if (isArrayType(this.repeats, child)) { 70 | index = this.repeats.indexOf(child as any) 71 | 72 | if (index > -1) { 73 | unrelate(child) 74 | this.repeats.splice(index, 1) 75 | } 76 | } 77 | break 78 | } 79 | } 80 | 81 | * walk (): Generator { 82 | yield this 83 | 84 | for (const repeat of this.repeats) { 85 | for (const node of repeat.walk()) { 86 | yield node 87 | } 88 | } 89 | } 90 | 91 | toJSON (): EdiJsonRepeated { 92 | return { 93 | repeats: this.repeats.map(repeat => repeat.toJSON()) 94 | } 95 | } 96 | 97 | fromJSON (input: EdiJsonRepeated): void { 98 | if (Array.isArray(input.repeats)) { 99 | this.repeats = [] 100 | 101 | for (const repeat of input.repeats) { 102 | if ('values' in repeat) { 103 | const domComponent = new EdiDomGlobal.Component() 104 | 105 | domComponent.fromJSON(repeat) 106 | relate(domComponent, this, this.root) 107 | 108 | this.repeats.push(domComponent as any) 109 | } else { 110 | const domValue = new EdiDomGlobal.Value() 111 | 112 | domValue.fromJSON(repeat) 113 | relate(domValue, this, this.root) 114 | 115 | this.repeats.push(domValue as any) 116 | } 117 | } 118 | } 119 | } 120 | } 121 | 122 | EdiDomGlobal.Repeated = EdiDomRepeated 123 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomElement.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiJsonComponent } from './EdiDomComponent' 6 | import type { EdiDomRoot } from './EdiDomRoot' 7 | import type { EdiJsonRepeated } from './EdiDomRepeated' 8 | import type { EdiDomSegment } from './EdiDomSegment' 9 | import type { EdiDomNode, ElementChild } from './EdiDomTypes' 10 | import type { EdiJsonValue } from './EdiDomValue' 11 | 12 | export interface EdiJsonElement { 13 | value: EdiJsonComponent | EdiJsonRepeated | EdiJsonValue 14 | } 15 | 16 | /** An element containing one or more repeated elements, a component, or a value. */ 17 | export class EdiDomElement extends EdiDomAbstractNode { 18 | constructor () { 19 | super() 20 | this.nodeType = EdiDomNodeType.Element 21 | } 22 | 23 | nodeType: EdiDomNodeType.Element 24 | /** The value of this element. */ 25 | value: T 26 | /** The root of this instance. */ 27 | root: EdiDomRoot 28 | /** The parent of this instance. */ 29 | parent: EdiDomSegment | EdiDomElement 30 | 31 | get innerEDI (): string { 32 | return typeof this.value === 'object' ? this.value.text : '' 33 | } 34 | 35 | get outerEDI (): string { 36 | return this.root.options.dataSeparator + this.innerEDI 37 | } 38 | 39 | /** The read-only text representation of this node. */ 40 | get text (): string { 41 | return this.outerEDI 42 | } 43 | 44 | get textContent (): string { 45 | if (typeof this.value === 'object') { 46 | const innerContent = this.value.textContent.split('\n') 47 | 48 | return ' ' + innerContent.join('\n ') 49 | } 50 | 51 | return '' 52 | } 53 | 54 | /** Add an element, component, or value to this node. */ 55 | addChildNode (child: T): void { 56 | switch (child.nodeType) { 57 | case EdiDomNodeType.Component: 58 | case EdiDomNodeType.Repeated: 59 | case EdiDomNodeType.Value: 60 | relate(child, this, this.root) 61 | this.value = child 62 | break 63 | } 64 | } 65 | 66 | /** Get the child value or optionally a child repeated element at the first or a given position. */ 67 | getChildNode (): T { 68 | return this.value 69 | } 70 | 71 | /** Remove the child value or a child repeated element from this element. */ 72 | removeChildNode (): void { 73 | if (typeof this.value === 'object') { 74 | unrelate(this.value) 75 | } 76 | } 77 | 78 | * walk (): Generator { 79 | yield this 80 | 81 | if (typeof this.value === 'object') { 82 | for (const node of this.value.walk()) { 83 | yield node 84 | } 85 | } 86 | } 87 | 88 | toJSON (): EdiJsonElement { 89 | return { 90 | value: this.value.toJSON() 91 | } 92 | } 93 | 94 | fromJSON (input: EdiJsonElement): void { 95 | if ('repeats' in input.value) { 96 | const domRepeated = new EdiDomGlobal.Repeated() 97 | 98 | domRepeated.fromJSON(input.value) 99 | 100 | this.value = domRepeated as any 101 | } else if ('values' in input.value) { 102 | const domComponent = new EdiDomGlobal.Component() 103 | 104 | domComponent.fromJSON(input.value) 105 | 106 | this.value = domComponent as any 107 | } else { 108 | const domValue = new EdiDomGlobal.Value() 109 | 110 | domValue.fromJSON(input.value) 111 | 112 | this.value = domValue as any 113 | } 114 | 115 | relate(this.value, this, this.root) 116 | } 117 | } 118 | 119 | EdiDomGlobal.Element = EdiDomElement 120 | -------------------------------------------------------------------------------- /packages/dom/src/README.md: -------------------------------------------------------------------------------- 1 | # Electronic Data Interchange Document Object Model 2 | 3 | Data exchanged via Electronic Data Interchange (EDI) formats uses a hierarchical model. This lends itself to being expressed as a document tree, much like a markup language would. This readme describes the Document Object Model (DOM) provided by this library. 4 | 5 | ## DOM Hierarchy 6 | 7 | 1. All documents begin with a single Root. 8 | 2. All roots contain options for reconstructing the document, and at least one Interchange. 9 | 3. An interchange consists of either at least one Group, or (in the case of EDIFACT) at least one Message. 10 | 4. A group consists of at least one Message. 11 | 5. A message consists of at least one Segment. 12 | 6. A segment consists of zero or more Elements. 13 | 7. An element consists of one or more repeated elements, a Component value, or a Value. 14 | 8. A component value consists of one or more Values 15 | 9. A value consists of a single `string` and is the base node of the DOM. 16 | 17 | ## Method and data expectations in the DOM 18 | 19 | ### Rules 20 | 21 | 1. No `null` values. 22 | 2. No missing methods. 23 | 3. Always return a legal value, `undefined`, or throw an `Error`. 24 | 4. No side-effects 25 | 5. All nodes provide a `text` property 26 | 27 | ### Expectations 28 | 29 | This means developers should expect: 30 | 31 | - Non-existent, non-primitive values return `undefined`. 32 | > JavaScript `null` values are prohibited in the DOM. 33 | - Primitive `string` data return an empty `''` string by default. 34 | > Null, undefined, NaN, etc. are prohibited for emppty data. 35 | - Methods in the DOM exist and can be called on any node. 36 | > Return `undefined` for missing, non-existent, non-primitive values, or an empty `string` for missing data. 37 | - An unexpected or malformed DOM will throw an `Error` to prevent undesired side-effects. 38 | > All scenarios during DOM construction not meeting expectations should throw. 39 | - A single node can be re-serialized simply by accessing the `text` getter on the node. 40 | > This means the document root can implicitly provide the entire contents in EDIFACT or EDIX12 format. 41 | 42 | # Document Object Model API 43 | 44 | ## Global Methods 45 | 46 | ### `addChildNode` 47 | 48 | ### `fromJSON` 49 | 50 | ### `getChildNode` 51 | 52 | ### `toJSON` 53 | 54 | ### `querySelector` 55 | 56 | ### `querySelectorAll` 57 | 58 | ### `removeChildNode` 59 | 60 | ### `walk` 61 | 62 | ## Global Properties 63 | 64 | ### `innerEDI` 65 | 66 | ### `nodeType` 67 | 68 | ### `outerEDI` 69 | 70 | ### `parent` 71 | 72 | ### `root` 73 | 74 | ### `text` 75 | 76 | ### `textContent` 77 | 78 | ## `EdiDomRoot` Methods 79 | 80 | ### `createUNAString` 81 | 82 | ## `EdiDomRoot` Properties 83 | 84 | ### `documentType` 85 | 86 | ### `interchanges` 87 | 88 | ### `options` 89 | 90 | ## `EdiDomInterchange` Methods 91 | 92 | ### N/A 93 | 94 | ## `EdiDomInterchange` Properties 95 | 96 | ### `groups` 97 | 98 | ### `header` 99 | 100 | ### `messages` 101 | 102 | ### `trailer` 103 | 104 | ## `EdiDomGroup` Methods 105 | 106 | ### N/A 107 | 108 | ## `EdiDomGroup` Properties 109 | 110 | ### `header` 111 | 112 | ### `messages` 113 | 114 | ### `trailer` 115 | 116 | ## `EdiDomMessage` Methods 117 | 118 | ### N/A 119 | 120 | ## `EdiDomMessage` Properties 121 | 122 | ### `header` 123 | 124 | ### `segments` 125 | 126 | ### `trailer` 127 | 128 | ## `EdiDomSegment` Methods 129 | 130 | ### N/A 131 | 132 | ## `EdiDomSegment` Properties 133 | 134 | ### `elements` 135 | 136 | ### `tag` 137 | 138 | ## `EdiDomElement` Methods 139 | 140 | ### N/A 141 | 142 | ## `EdiDomElement` Properties 143 | 144 | ### `elements` 145 | 146 | ### `type` 147 | 148 | ### `value` 149 | 150 | ## `EdiDomComponent` Methods 151 | 152 | ### N/A 153 | 154 | ## `EdiDomComponent` Properties 155 | 156 | ### `values` 157 | 158 | ## `EdiDomValue` Methods 159 | 160 | ### N/A 161 | 162 | ## `EdiDomValue` Properties 163 | 164 | ### `type` 165 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomGroup.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { assignMessagesFromJson, containerFromJson, messagesAsContent, relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomMessage, EdiJsonMessage } from './EdiDomMessage' 6 | import type { EdiDomRoot } from './EdiDomRoot' 7 | import type { EdiDomSegment, EdiJsonSegment } from './EdiDomSegment' 8 | import type { EdiDomNode } from './EdiDomTypes' 9 | import type { EdiDomInterchange } from './EdiDomInterchange' 10 | 11 | export interface EdiJsonGroup { 12 | header: EdiJsonSegment 13 | messages: EdiJsonMessage[] 14 | trailer: EdiJsonSegment 15 | } 16 | 17 | /** An EDIFACT UNG message or an X12 ST transaction. */ 18 | export class EdiDomGroup extends EdiDomAbstractNode { 19 | constructor () { 20 | super() 21 | this.nodeType = EdiDomNodeType.Group 22 | this.messages = [] 23 | } 24 | 25 | nodeType: EdiDomNodeType.Group 26 | parent: EdiDomInterchange 27 | protected _header: EdiDomSegment<'UNG'|'GS'> 28 | /** The messages contained in this group. */ 29 | messages: EdiDomMessage[] 30 | /** The root of this instance. */ 31 | root: EdiDomRoot 32 | protected _trailer: EdiDomSegment<'UNE'|'GE'> 33 | 34 | /** The header of this group. */ 35 | get header (): EdiDomSegment<'UNG'|'GS'> { 36 | return this._header 37 | } 38 | 39 | /** The header of this group. */ 40 | set header (_header: EdiDomSegment<'UNG'|'GS'>) { 41 | relate(_header, this, this.root) 42 | this._header = _header 43 | } 44 | 45 | get innerEDI (): string { 46 | return this.messages.map(message => message.outerEDI).join('') 47 | } 48 | 49 | get outerEDI (): string { 50 | return this.header.outerEDI + this.innerEDI + this.trailer.outerEDI 51 | } 52 | 53 | get text (): string { 54 | return this.outerEDI 55 | } 56 | 57 | get textContent (): string { 58 | return `BEGIN Group\n${messagesAsContent(this)}END Group` 59 | } 60 | 61 | /** The trailer of this group. */ 62 | get trailer (): EdiDomSegment<'UNE'|'GE'> { 63 | return this._trailer 64 | } 65 | 66 | /** The trailer of this group. */ 67 | set trailer (_trailer: EdiDomSegment<'UNE'|'GE'>) { 68 | relate(_trailer, this, this.root) 69 | this._trailer = _trailer 70 | } 71 | 72 | /** Add a message to this group. */ 73 | addChildNode (child: EdiDomMessage): void { 74 | if (child.nodeType === EdiDomNodeType.Message) { 75 | relate(child, this, this.root) 76 | this.messages.push(child) 77 | } 78 | } 79 | 80 | /** Retrieve the message/transaction at the given zero-based index. */ 81 | getChildNode (index: number): EdiDomMessage { 82 | return this.messages[index] 83 | } 84 | 85 | /** Remove a message/transaction from this group and destroy all descendent relationships to this group. */ 86 | removeChildNode (child: EdiDomMessage): void { 87 | const index = this.messages.indexOf(child) 88 | 89 | if (index > -1) { 90 | unrelate(child) 91 | this.messages.splice(index, 1) 92 | } 93 | } 94 | 95 | * walk (): Generator { 96 | yield this 97 | if (typeof this.header === 'object') { 98 | for (const node of this.header.walk()) { 99 | yield node 100 | } 101 | } 102 | 103 | for (const message of this.messages) { 104 | for (const node of message.walk()) { 105 | yield node 106 | } 107 | } 108 | 109 | if (typeof this.trailer === 'object') { 110 | for (const node of this.trailer.walk()) { 111 | yield node 112 | } 113 | } 114 | } 115 | 116 | toJSON (): EdiJsonGroup { 117 | return { 118 | header: this.header.toJSON(), 119 | messages: this.messages.map(message => message.toJSON()), 120 | trailer: this.trailer.toJSON() 121 | } 122 | } 123 | 124 | fromJSON (input: EdiJsonGroup): void { 125 | containerFromJson(this, input, () => { 126 | assignMessagesFromJson(this, input) 127 | }) 128 | } 129 | } 130 | 131 | EdiDomGlobal.Group = EdiDomGroup 132 | -------------------------------------------------------------------------------- /test/QueryEngineSuite.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs' 2 | import { EdiParser } from '../packages/documents/index' 3 | import { strictEqual } from 'assert' 4 | import type { EdiDomSegment } from '../packages/dom/index' 5 | 6 | const parseLabel = ' query duration' 7 | const parser = new EdiParser({ 8 | throwOnError: true, 9 | contents: readFileSync('./test/data/856.edi', 'utf8') 10 | }) 11 | 12 | describe('QueryEngine', () => { 13 | before(() => { 14 | parser.parse() 15 | }) 16 | 17 | it('should query a single result', () => { 18 | const document = parser.documentRoot() 19 | console.time(parseLabel) 20 | const result = document.querySelector('GS01') 21 | console.timeEnd(parseLabel) 22 | 23 | strictEqual(result.nodeType, 'Element') 24 | }) 25 | 26 | it('should query multiple results', () => { 27 | const document = parser.documentRoot() 28 | console.time(parseLabel) 29 | const result = document.querySelectorAll('REF01') 30 | console.timeEnd(parseLabel) 31 | 32 | // console.log(Array.from(result).length) 33 | 34 | strictEqual(result.size, 2) 35 | }) 36 | 37 | it('should query results starting at current node', () => { 38 | const document = parser.documentRoot() 39 | const group = document.interchanges[0].groups[0] 40 | 41 | console.time(parseLabel) 42 | const result = group.querySelector('REF01') 43 | console.timeEnd(parseLabel) 44 | 45 | strictEqual(result.nodeType, 'Element') 46 | 47 | console.time(parseLabel) 48 | const result2 = group.querySelector('ISA01') 49 | console.timeEnd(parseLabel) 50 | 51 | strictEqual(typeof result2, 'undefined') 52 | 53 | console.time(parseLabel) 54 | const result3 = document.querySelector('ISA01') 55 | console.timeEnd(parseLabel) 56 | 57 | strictEqual(typeof result3, 'object') 58 | }) 59 | 60 | it('should query for interchanges', () => { 61 | const document = parser.documentRoot() 62 | 63 | console.time(parseLabel) 64 | const result = document.querySelector('Interchange') 65 | console.timeEnd(parseLabel) 66 | 67 | strictEqual(result.nodeType, 'Interchange') 68 | }) 69 | 70 | it('should query for all nodes', () => { 71 | const document = parser.documentRoot() 72 | 73 | console.time(parseLabel) 74 | const result = document.querySelector('All') 75 | console.timeEnd(parseLabel) 76 | 77 | strictEqual(result.nodeType, 'Root') 78 | }) 79 | 80 | it('should query for segments by tag', () => { 81 | const document = parser.documentRoot() 82 | 83 | console.time(parseLabel) 84 | const result = document.querySelector('HL') 85 | console.timeEnd(parseLabel) 86 | 87 | strictEqual(result.nodeType, 'Segment') 88 | strictEqual(result.tag, 'HL') 89 | }) 90 | 91 | it('should query for element by parent segment path', () => { 92 | const document = parser.documentRoot() 93 | 94 | console.time(parseLabel) 95 | const result = document.querySelector('GS-ST-BSN03') 96 | console.timeEnd(parseLabel) 97 | 98 | strictEqual(result.nodeType, 'Element') 99 | }) 100 | 101 | it('should query for element by hierarchical level', () => { 102 | const document = parser.documentRoot() 103 | 104 | console.time(parseLabel) 105 | const result = document.querySelector('HL+S+O+I-PO4-PID05') 106 | console.timeEnd(parseLabel) 107 | 108 | strictEqual(result.nodeType, 'Element') 109 | 110 | console.time(parseLabel) 111 | const result2 = document.querySelector('HL+S+O+I-PO401') 112 | console.timeEnd(parseLabel) 113 | 114 | strictEqual(result2.nodeType, 'Element') 115 | 116 | console.time(parseLabel) 117 | const result3 = document.querySelector('HL+S+O+I-HL03') 118 | console.timeEnd(parseLabel) 119 | 120 | strictEqual(result3.nodeType, 'Element') 121 | }) 122 | 123 | it('should query for element by loop context', () => { 124 | const document = parser.documentRoot() 125 | 126 | console.time(parseLabel) 127 | const result = document.querySelector('N1^N4-N301') 128 | console.timeEnd(parseLabel) 129 | 130 | strictEqual(result.nodeType, 'Element') 131 | 132 | console.time(parseLabel) 133 | const result2 = document.querySelector('N1^N4-N1-N301') 134 | console.timeEnd(parseLabel) 135 | 136 | strictEqual(result2.nodeType, 'Element') 137 | }) 138 | }) 139 | -------------------------------------------------------------------------------- /test/EdiParserSuite.ts: -------------------------------------------------------------------------------- 1 | import { strictEqual, throws } from 'assert' 2 | import { readFileSync } from 'fs' 3 | import { EdiParser } from '../packages/documents/index' 4 | 5 | const parseLabel = ' parse duration' 6 | 7 | describe('EdiParser', () => { 8 | it('should construct an instance', () => { 9 | const fake = '' 10 | const result1 = new EdiParser({ 11 | encoding: 'ascii', 12 | keepInitialListeners: true, 13 | contents: fake 14 | }) 15 | const parser1 = result1.getParser() 16 | 17 | strictEqual(result1 instanceof EdiParser, true) 18 | strictEqual(typeof parser1, 'object') 19 | 20 | const result2 = new EdiParser({ 21 | // Fake a readable to follow a code path, we're not really going to use it for now. 22 | contents: {} as any, 23 | ediType: 'EDIFACT' 24 | }) 25 | const parser2 = result2.getParser() 26 | 27 | strictEqual(result2 instanceof EdiParser, true) 28 | strictEqual(typeof parser2, 'undefined') 29 | }) 30 | 31 | it('should parse an EDIFACT document', () => { 32 | const fileName = './test/data/EdifactSample.edi' 33 | const contents = readFileSync(fileName) 34 | const parser = new EdiParser({ 35 | ediType: 'EDIFACT', 36 | keepInitialListeners: false, 37 | throwOnError: true, 38 | fileName, 39 | contents 40 | }) 41 | console.time(parseLabel) 42 | const result = parser.parse() 43 | console.timeEnd(parseLabel) 44 | 45 | // Document successfully returned. 46 | strictEqual(typeof result, 'object') 47 | // Parsed document tree matches original input. 48 | strictEqual(result.text, contents.toString('utf8')) 49 | }) 50 | 51 | it('should parse a complex EDIX12 version 5010 document', () => { 52 | const fileName = './test/data/271.edi' 53 | const contents = readFileSync(fileName) 54 | const parser = new EdiParser({ 55 | ediType: 'EDIX12', 56 | keepInitialListeners: false, 57 | throwOnError: true, 58 | fileName, 59 | contents 60 | }) 61 | console.time(parseLabel) 62 | const result = parser.documentRoot() 63 | console.timeEnd(parseLabel) 64 | 65 | // Document successfully returned. 66 | strictEqual(typeof result, 'object') 67 | // Parsed document tree matches original input. 68 | strictEqual(result.text, contents.toString('utf8')) 69 | }) 70 | 71 | it('should parse an EDIX12 version 4010 document and return a matching EDI DOM', () => { 72 | const fileName = './test/data/850_2.edi' 73 | const contents = readFileSync(fileName) 74 | const parser = new EdiParser({ 75 | ediType: 'EDIX12', 76 | keepInitialListeners: false, 77 | throwOnError: true, 78 | fileName, 79 | contents 80 | }) 81 | console.time(parseLabel) 82 | const result = parser.parse() 83 | console.timeEnd(parseLabel) 84 | 85 | const document = parser.documentRoot() 86 | 87 | // Document successfully returned. 88 | strictEqual(typeof result, 'object') 89 | // Parsed document tree matches original input. 90 | strictEqual(result.text, contents.toString('utf8')) 91 | // Constructed EDI DOM matches original input. 92 | strictEqual(document.text, contents.toString('utf8')) 93 | }) 94 | 95 | it('should parse an EDIX12 fat document', () => { 96 | const fileName = './test/data/850_fat.edi' 97 | const contents = readFileSync(fileName, 'utf8') 98 | const parser = new EdiParser({ 99 | ediType: 'EDIX12', 100 | keepInitialListeners: false, 101 | throwOnError: true, 102 | fileName, 103 | contents 104 | }) 105 | console.time(parseLabel) 106 | const result = parser.parse() 107 | console.timeEnd(parseLabel) 108 | 109 | // Document successfully returned. 110 | strictEqual(typeof result, 'object') 111 | // Parsed document tree matches original input. 112 | strictEqual(result.text, contents) 113 | }) 114 | 115 | it('should error on an incomplete document', () => { 116 | const fileName = './test/data/856.edi' 117 | const contents = readFileSync(fileName).slice(0, 300) 118 | const parser = new EdiParser({ 119 | ediType: 'EDIX12', 120 | keepInitialListeners: false, 121 | throwOnError: true, 122 | fileName, 123 | contents 124 | }) 125 | throws(() => { 126 | parser.parse() 127 | }) 128 | }) 129 | }) 130 | -------------------------------------------------------------------------------- /packages/dom/query/ElementSelectorVisitor.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/query/ElementSelector.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ParseTreeVisitor } from "antlr4ts/tree/ParseTreeVisitor"; 5 | 6 | import { SelectorContext } from "./ElementSelectorParser"; 7 | import { ElementAdjacentSelectorContext } from "./ElementSelectorParser"; 8 | import { ElementPrecedentSelectorContext } from "./ElementSelectorParser"; 9 | import { ElementSiblingSelectorContext } from "./ElementSelectorParser"; 10 | import { ElementContainsValueSelectorContext } from "./ElementSelectorParser"; 11 | import { ElementNotValueSelectorContext } from "./ElementSelectorParser"; 12 | import { ElementValueSelectorContext } from "./ElementSelectorParser"; 13 | import { LoopPathSelectorContext } from "./ElementSelectorParser"; 14 | import { HlPathSelectorContext } from "./ElementSelectorParser"; 15 | import { ParentSegmentSelectorContext } from "./ElementSelectorParser"; 16 | import { ElementSelectorContext } from "./ElementSelectorParser"; 17 | import { SegmentTagContext } from "./ElementSelectorParser"; 18 | 19 | 20 | /** 21 | * This interface defines a complete generic visitor for a parse tree produced 22 | * by `ElementSelectorParser`. 23 | * 24 | * @param The return type of the visit operation. Use `void` for 25 | * operations with no return type. 26 | */ 27 | export interface ElementSelectorVisitor extends ParseTreeVisitor { 28 | /** 29 | * Visit a parse tree produced by `ElementSelectorParser.selector`. 30 | * @param ctx the parse tree 31 | * @return the visitor result 32 | */ 33 | visitSelector?: (ctx: SelectorContext) => Result; 34 | 35 | /** 36 | * Visit a parse tree produced by `ElementSelectorParser.elementAdjacentSelector`. 37 | * @param ctx the parse tree 38 | * @return the visitor result 39 | */ 40 | visitElementAdjacentSelector?: (ctx: ElementAdjacentSelectorContext) => Result; 41 | 42 | /** 43 | * Visit a parse tree produced by `ElementSelectorParser.elementPrecedentSelector`. 44 | * @param ctx the parse tree 45 | * @return the visitor result 46 | */ 47 | visitElementPrecedentSelector?: (ctx: ElementPrecedentSelectorContext) => Result; 48 | 49 | /** 50 | * Visit a parse tree produced by `ElementSelectorParser.elementSiblingSelector`. 51 | * @param ctx the parse tree 52 | * @return the visitor result 53 | */ 54 | visitElementSiblingSelector?: (ctx: ElementSiblingSelectorContext) => Result; 55 | 56 | /** 57 | * Visit a parse tree produced by `ElementSelectorParser.elementContainsValueSelector`. 58 | * @param ctx the parse tree 59 | * @return the visitor result 60 | */ 61 | visitElementContainsValueSelector?: (ctx: ElementContainsValueSelectorContext) => Result; 62 | 63 | /** 64 | * Visit a parse tree produced by `ElementSelectorParser.elementNotValueSelector`. 65 | * @param ctx the parse tree 66 | * @return the visitor result 67 | */ 68 | visitElementNotValueSelector?: (ctx: ElementNotValueSelectorContext) => Result; 69 | 70 | /** 71 | * Visit a parse tree produced by `ElementSelectorParser.elementValueSelector`. 72 | * @param ctx the parse tree 73 | * @return the visitor result 74 | */ 75 | visitElementValueSelector?: (ctx: ElementValueSelectorContext) => Result; 76 | 77 | /** 78 | * Visit a parse tree produced by `ElementSelectorParser.loopPathSelector`. 79 | * @param ctx the parse tree 80 | * @return the visitor result 81 | */ 82 | visitLoopPathSelector?: (ctx: LoopPathSelectorContext) => Result; 83 | 84 | /** 85 | * Visit a parse tree produced by `ElementSelectorParser.hlPathSelector`. 86 | * @param ctx the parse tree 87 | * @return the visitor result 88 | */ 89 | visitHlPathSelector?: (ctx: HlPathSelectorContext) => Result; 90 | 91 | /** 92 | * Visit a parse tree produced by `ElementSelectorParser.parentSegmentSelector`. 93 | * @param ctx the parse tree 94 | * @return the visitor result 95 | */ 96 | visitParentSegmentSelector?: (ctx: ParentSegmentSelectorContext) => Result; 97 | 98 | /** 99 | * Visit a parse tree produced by `ElementSelectorParser.elementSelector`. 100 | * @param ctx the parse tree 101 | * @return the visitor result 102 | */ 103 | visitElementSelector?: (ctx: ElementSelectorContext) => Result; 104 | 105 | /** 106 | * Visit a parse tree produced by `ElementSelectorParser.segmentTag`. 107 | * @param ctx the parse tree 108 | * @return the visitor result 109 | */ 110 | visitSegmentTag?: (ctx: SegmentTagContext) => Result; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /packages/dom/query/README.md: -------------------------------------------------------------------------------- 1 | # Element Selector Query Language 2 | 3 | Element selectors define the nodes to which a query applies. Results are returned inclusively for descendants of the node which was queried. 4 | 5 | > Note: There are no selectors or combinators to select parent nodes, siblings of parents, or children of parent siblings 6 | 7 | ## Basic Selectors 8 | 9 | ### Universal selector 10 | - Selects all nodes. 11 | - **Syntax**: `*` | `All` 12 | - **Example**: `*` will match all nodes of the document. 13 | 14 | ### Type selector 15 | - Selects all nodes of the given node type. 16 | - **Syntax**: `Typename` 17 | - **Example**: `Segment` will match any EdiDomSegment node. 18 | 19 | ### Segment Tag selector 20 | - Selects all nodes of the given tag name. 21 | - **Syntax**: `TAG` 22 | - **Example**: `REF` will match any node with the tag of "REF". 23 | 24 | ### Element Reference selector 25 | - Selects all element nodes matching the reference. 26 | - **Syntax**: `TAG##` 27 | - **Example**: `ST01` will match any element node in segment "ST" at position "01" 28 | 29 | ## Element Value Selectors 30 | ### Element Reference Value selector 31 | - Selects all element nodes matching the reference and which equals the value. 32 | - **Syntax**: `TAG##['VALUE']` Single or duoble quotes are permitted. 33 | - **Example**: `N102['Kai']` will match any element node which strictly equals the word "Kai". 34 | 35 | ### Element Reference Not Value selector 36 | - Selects all element nodes matching the reference and which do not equal the value. 37 | - **Syntax**: `TAG##!['VALUE']` Single or duoble quotes are permitted. 38 | - **Example**: `N102!['Kai']` will match any element node which does not equal the word "Kai". 39 | 40 | ### Element Reference Contains Value selector 41 | - Selects all element nodes matching the reference and which contains the value. 42 | - **Syntax**: `TAG##*['VALUE']` Single or duoble quotes are permitted. 43 | - **Example**: `N102*['Kai']` will match any element node containing the word "Kai". 44 | 45 | ## Path Selectors 46 | 47 | ### Parent Segment selector 48 | - Selects all element nodes matching the given path of preceding segment tags. Ends with an **Element Reference selector**. 49 | - **Syntax**: `TAG...[-TAG]-TAG##` 50 | - **Example**: `BEG-REF-REF02` will match the element reference at "REF02" which is preceded, in order, by segment BEG and segment REF. 51 | 52 | ### Hierarchical Level selector 53 | - Selects all element nodes matching the given path through a hierarchical tree in the DOM. Ends with **Parent Segment selector** or **Element Reference selector**. 54 | - **Syntax**: `HL+CODE...[+CODE]-TAG##` | `HL+CODE...[+CODE]-TAG...[-TAG]-TAG##` 55 | - **Example**: `HL+S+O+P+I-PID05` will match the element reference in the "I" of a "SOPI" hierarchical level tree. 56 | 57 | ### Loop selector 58 | - Selects all element nodes inclusively within the matching loop start and end. Ends with **Parent Segment selector** or **Element Reference selector**. 59 | - **Syntax**: `START^END-TAG##` | `START^END-TAG...[-TAG]-TAG##` 60 | - **Example**: `LS^LE-REF-PID01` will match the element reference between segments "LS" and "LE at "PID01" which is preceded by segment REF. 61 | 62 | ## Combinator Selectors 63 | 64 | ### Sibling selector 65 | - Selects an element reference sibling to an element reference value. 66 | - **Syntax**: Selector `:` Value 67 | - The left-hand side must be strictly an **Element Reference selector** or any of the **Path Selectors**. 68 | - The right-hand side may be an **Element Reference selector** or any of the **Element Value Selectors** 69 | - **Example**: `N102:N101['SH']` will match any "N102" where its sibling "N101" equals the value "SH". 70 | 71 | ### Adjacent selector 72 | - Selects an element reference contextually adjacent to an element reference value. 73 | - **Syntax**: Selector `~` Value 74 | - The left-hand side must be strictly an **Element Reference selector** or any of the **Path Selectors**. 75 | - The right-hand side may be an **Element Reference selector** or any of the **Element Value Selectors** 76 | - **Example**: `HL+S+O+P+I-LIN03~PID05*['blue']` will match any "LIN03" within a hierarchical level "I" where an adjacent "PID05" contains the word "blue". 77 | 78 | ### Precedent selector 79 | - Selects an element reference contextually preceded by an element reference value. 80 | - **Syntax**: Selector `?` Value 81 | - The left-hand side must be strictly an **Element Reference selector** or any of the **Path Selectors**. 82 | - The right-hand side may be an **Element Reference selector** or any of the **Element Value Selectors** 83 | - **Example**: `N1^N4-N403?N101['SH']` will match any "N403" within a loop where a preceding "N101" within the loop equals the value "SH". 84 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomRoot.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomInterchange, EdiJsonInterchange } from './EdiDomInterchange' 6 | import type { EdiDomDocumentType, EdiDomNode, EdiDomOptions } from './EdiDomTypes' 7 | 8 | export interface EdiJsonRoot { 9 | options: EdiDomOptions 10 | interchanges: EdiJsonInterchange[] 11 | } 12 | 13 | /** The document root containing one or more interchanges. */ 14 | export class EdiDomRoot extends EdiDomAbstractNode { 15 | constructor () { 16 | super() 17 | this.nodeType = EdiDomNodeType.Root 18 | this.options = {} as unknown as EdiDomOptions 19 | this.interchanges = [] 20 | this.root = this 21 | this.parent = this 22 | } 23 | 24 | nodeType: EdiDomNodeType.Root 25 | /** Options for parsing/reconstructing an EDI document. */ 26 | options: EdiDomOptions 27 | /** The child interchanges for this document. */ 28 | interchanges: EdiDomInterchange[] 29 | /** Recursive self-reference for consistency. */ 30 | root: EdiDomRoot 31 | /** Recursive self-reference for consistency. */ 32 | parent: EdiDomRoot 33 | 34 | get innerEDI (): string { 35 | return this.interchanges.map(interchange => interchange.outerEDI).join('') 36 | } 37 | 38 | get outerEDI (): string { 39 | return this.innerEDI 40 | } 41 | 42 | get text (): string { 43 | return this.outerEDI 44 | } 45 | 46 | get textContent (): string { 47 | let content = `BEGIN Document\n` 48 | 49 | if (Array.isArray(this.interchanges) && this.interchanges.length > 0) { 50 | for (const interchange of this.interchanges) { 51 | const innerContent = interchange.textContent.split('\n') 52 | 53 | content += ' ' + innerContent.join('\n ') + '\n' 54 | } 55 | } 56 | 57 | return content + `END Document` 58 | } 59 | 60 | get documentType (): EdiDomDocumentType { 61 | const interchange = this.interchanges[0] 62 | 63 | if (typeof interchange === 'object') { 64 | const header = interchange.header 65 | 66 | if (typeof header === 'object') { 67 | const tag = header.tag 68 | 69 | switch (tag) { 70 | case 'ISA': 71 | return 'EDIX12' 72 | case 'UNB': 73 | return 'EDIFACT' 74 | } 75 | } 76 | } 77 | } 78 | 79 | /** Creates an UNA Service String Advice from options. */ 80 | createUNAString (): string { 81 | const contents = 'UNA' + 82 | this.options.componentSeparator + 83 | this.options.dataSeparator + 84 | (this.options.decimalMark ?? '.') + 85 | (this.options.releaseIndicator ?? '?') + 86 | (this.options.repititionSeparator ?? ' ') + 87 | this.options.segmentTerminator 88 | 89 | if (typeof this.options.endOfLine === 'string') { 90 | return contents + this.options.endOfLine 91 | } 92 | 93 | return contents 94 | } 95 | 96 | /** Add an interchange to this document. */ 97 | addChildNode (child: EdiDomInterchange): void { 98 | if (child.nodeType === EdiDomNodeType.Interchange) { 99 | relate(child, this, this) 100 | this.interchanges.push(child) 101 | } 102 | } 103 | 104 | /** Get an interchange by zer-based index in the root. */ 105 | getChildNode (index: number): EdiDomInterchange { 106 | return this.interchanges[index] 107 | } 108 | 109 | /** Remove an interchange from this root and destroy all descendent relationships to this root. */ 110 | removeChildNode (child: EdiDomInterchange): void { 111 | const index = this.interchanges.indexOf(child) 112 | 113 | if (index > -1) { 114 | unrelate(child) 115 | this.interchanges.splice(index, 1) 116 | } 117 | } 118 | 119 | /** Walk the document object model sequentially. */ 120 | * walk (): Generator { 121 | yield this 122 | 123 | for (const interchange of this.interchanges) { 124 | for (const node of interchange.walk()) { 125 | yield node 126 | } 127 | } 128 | } 129 | 130 | toJSON (): EdiJsonRoot { 131 | return { 132 | options: this.options, 133 | interchanges: this.interchanges.map(interchange => interchange.toJSON()) 134 | } 135 | } 136 | 137 | fromJSON (input: EdiJsonRoot): void { 138 | this.interchanges = [] 139 | 140 | if ('options' in input) { 141 | this.options = input.options 142 | } 143 | 144 | if (Array.isArray(input.interchanges)) { 145 | for (const interchange of input.interchanges) { 146 | const domInterchange = new EdiDomGlobal.Interchange() 147 | 148 | domInterchange.fromJSON(interchange) 149 | relate(domInterchange, this, this) 150 | this.interchanges.push(domInterchange) 151 | } 152 | } 153 | } 154 | } 155 | 156 | EdiDomGlobal.Root = EdiDomRoot 157 | -------------------------------------------------------------------------------- /packages/x12/src/X12BaseLexer.ts: -------------------------------------------------------------------------------- 1 | import { BaseLexer, ControlChar, EdiOptions } from '@js-edi/shared' 2 | 3 | enum ISAPos { 4 | DataSeparator = 4, 5 | RepititionSeparator = 83, 6 | ComponentSeparator = 105, 7 | SegmentTerminator = 106, 8 | EndOfLine = 107 9 | } 10 | 11 | export abstract class X12BaseLexer extends BaseLexer { 12 | get isDataSep (): boolean { 13 | return this.line === 1 && this.charPositionInLine === ISAPos.DataSeparator 14 | } 15 | 16 | get isRepititionSep (): boolean { 17 | return this.line === 1 && this.charPositionInLine === ISAPos.RepititionSeparator 18 | } 19 | 20 | get isComponentSep (): boolean { 21 | return this.line === 1 && this.charPositionInLine === ISAPos.ComponentSeparator 22 | } 23 | 24 | get isSegmentTerm (): boolean { 25 | return this.line === 1 && this.charPositionInLine === ISAPos.SegmentTerminator 26 | } 27 | 28 | get isEndOfLine (): boolean { 29 | const isCr = this.line === 1 && this.charPositionInLine === ISAPos.EndOfLine 30 | const isNl = this.line === 2 && this.charPositionInLine === 0 31 | 32 | return isCr || isNl 33 | } 34 | 35 | /** Is the current right-hand lexer position a control character definition? */ 36 | get isCtlCharPos (): boolean { 37 | return this.isDataSep || this.isRepititionSep || this.isComponentSep || this.isSegmentTerm || this.isEndOfLine 38 | } 39 | 40 | handleControlChars (lexer: Record): void { 41 | const charType = this.ControlCharMap.get(this.text) 42 | 43 | switch (true) { 44 | case this.isDataSep: 45 | this.ControlCharMap.set(this.text, ControlChar.DataSeparator) 46 | // this.type = lexer[ControlChar.DataSeparator] 47 | break 48 | case this.isRepititionSep: 49 | this.ControlCharMap.set(this.text, ControlChar.RepititionSeparator) 50 | // this.type = lexer[ControlChar.RepititionSeparator] 51 | break 52 | case this.isComponentSep: 53 | this.ControlCharMap.set(this.text, ControlChar.ComponentSeparator) 54 | // this.type = lexer[ControlChar.ComponentSeparator] 55 | break 56 | case this.isSegmentTerm: 57 | this.ControlCharMap.set(this.text, ControlChar.SegmentTerminator) 58 | // this.type = lexer[ControlChar.SegmentTerminator] 59 | break 60 | case this.isEndOfLine: 61 | // Only pick this up if end of line is a real formatting mark. 62 | if (this.text === '\r\n' || this.text === '\r' || this.text === '\n') { 63 | // If there is no segment terminator already found, then end of line is the terminator. 64 | if (Array.from(this.ControlCharMap.values()).includes(ControlChar.SegmentTerminator)) { 65 | this.ControlCharMap.set(this.text, ControlChar.EndOfLine) 66 | } else { 67 | this.ControlCharMap.set(this.text, ControlChar.SegmentTerminator) 68 | } 69 | } 70 | break 71 | default: 72 | if (charType in ControlChar) { 73 | this.type = lexer[charType] 74 | } else { 75 | this.type = lexer.Char 76 | } 77 | } 78 | } 79 | 80 | getOptions (): EdiOptions { 81 | const options: any = {} 82 | 83 | for (const [char, type] of this.ControlCharMap) { 84 | switch (type) { 85 | case ControlChar.ComponentSeparator: 86 | options.componentSeparator = char 87 | break 88 | case ControlChar.DataSeparator: 89 | options.dataSeparator = char 90 | break 91 | case ControlChar.EndOfLine: 92 | options.endOfLine = char 93 | break 94 | case ControlChar.RepititionSeparator: 95 | options.repititionSeparator = char 96 | break 97 | case ControlChar.SegmentTerminator: 98 | options.segmentTerminator = char 99 | break 100 | } 101 | } 102 | 103 | return options 104 | } 105 | 106 | setOptions (options: EdiOptions): void { 107 | const eolChars = '\r\n' 108 | const noEmptyString = (str: string): boolean => { 109 | return typeof str === 'string' && str.trim() !== '' 110 | } 111 | 112 | if (noEmptyString(options.componentSeparator)) { 113 | this.ControlCharMap.set(options.componentSeparator, ControlChar.ComponentSeparator) 114 | } 115 | 116 | if (noEmptyString(options.dataSeparator)) { 117 | this.ControlCharMap.set(options.dataSeparator, ControlChar.DataSeparator) 118 | } 119 | 120 | if (noEmptyString(options.endOfLine) || eolChars.includes(options.endOfLine)) { 121 | this.ControlCharMap.set(options.endOfLine, ControlChar.EndOfLine) 122 | } 123 | 124 | if (noEmptyString(options.repititionSeparator)) { 125 | this.ControlCharMap.set(options.repititionSeparator, ControlChar.RepititionSeparator) 126 | } 127 | 128 | if (noEmptyString(options.segmentTerminator)) { 129 | this.ControlCharMap.set(options.segmentTerminator, ControlChar.SegmentTerminator) 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /packages/documents/src/EdiParser.ts: -------------------------------------------------------------------------------- 1 | import { CharStream, CommonTokenStream, RecognitionException, Recognizer } from 'antlr4ts' 2 | import { EdiX12Lexer, EdiX12Parser } from '@js-edi/x12' 3 | import { EdiFactLexer, EdiFactParser } from '@js-edi/fact' 4 | import { BaseCharStreams } from '@js-edi/shared' 5 | import { EdiDomX12Listener } from './EdiDomX12Listener' 6 | import type { Readable } from 'stream' 7 | import type { DocumentContext as EdiFactDocumentContext } from '@js-edi/fact' 8 | import type { DocumentContext as EdiX12DocumentContext } from '@js-edi/x12' 9 | import type { EdiDomRoot } from '@js-edi/dom' 10 | 11 | export type DocumentContext = EdiX12DocumentContext | EdiFactDocumentContext 12 | export type EdiCustomLexer = EdiX12Lexer | EdiFactLexer 13 | export type EdiCustomParser = EdiX12Parser | EdiFactParser 14 | 15 | export type EdiDomListener = EdiDomX12Listener 16 | 17 | export interface EdiParserOpts { 18 | encoding?: BufferEncoding 19 | fileName?: string 20 | ediType?: 'EDIX12' | 'EDIFACT' 21 | keepInitialListeners?: boolean 22 | throwOnError?: boolean 23 | contents: string | Buffer 24 | } 25 | 26 | export class EdiParser { 27 | /** Create an asynchronous parser for a readable stream. */ 28 | constructor () 29 | /** Create a synchronous parser for a string or buffer. */ 30 | constructor (opts: EdiParserOpts) 31 | constructor (opts?: EdiParserOpts) { 32 | this.encoding = opts.encoding ?? 'utf8' 33 | this.fileName = opts.fileName ?? 'document.edi' 34 | this.parsed = false 35 | 36 | if (typeof opts.contents === 'string') { 37 | this.charStream = BaseCharStreams.fromString(opts.contents, this.fileName) 38 | this.createParser(opts) 39 | } else if (Buffer.isBuffer(opts.contents)) { 40 | this.charStream = BaseCharStreams.fromNodeBuffer(opts.contents, this.encoding, this.fileName) 41 | this.createParser(opts) 42 | } 43 | } 44 | 45 | fileName: string 46 | private listener: EdiDomListener 47 | private lexer: EdiCustomLexer 48 | private tokens: CommonTokenStream 49 | private parser: EdiCustomParser 50 | private charStream: CharStream 51 | private encoding: BufferEncoding 52 | private parsed: boolean 53 | 54 | private createParser (opts: Omit): void { 55 | const keepInitialListeners = opts.keepInitialListeners ?? false 56 | const throwOnError = opts.throwOnError ?? false 57 | 58 | switch (opts.ediType) { 59 | case 'EDIFACT': 60 | this.lexer = new EdiFactLexer(this.charStream) 61 | this.tokens = new CommonTokenStream(this.lexer) 62 | this.parser = new EdiFactParser(this.tokens) 63 | break 64 | case 'EDIX12': 65 | default: 66 | this.listener = new EdiDomX12Listener() 67 | this.lexer = new EdiX12Lexer(this.charStream) 68 | this.tokens = new CommonTokenStream(this.lexer) 69 | this.parser = new EdiX12Parser(this.tokens) 70 | this.parser.addParseListener(this.listener) 71 | break 72 | } 73 | 74 | if (typeof this.parser === 'object') { 75 | if (!keepInitialListeners) { 76 | this.parser.removeErrorListeners() 77 | } 78 | 79 | if (throwOnError) { 80 | this.parser.addErrorListener({ 81 | syntaxError( 82 | recognizer: Recognizer, 83 | offendingSymbol: T, 84 | line: number, 85 | charPositionInLine: number, 86 | msg: string, 87 | e?: RecognitionException 88 | ): void { 89 | throw new Error(`line ${line}:${charPositionInLine} ${msg}`) 90 | } 91 | }) 92 | } 93 | } 94 | } 95 | 96 | /** Get the internal parser instance. */ 97 | getParser (): EdiCustomParser { 98 | return this.parser 99 | } 100 | 101 | /** Synchronously parse to return an ANTLR4 ParseTree. */ 102 | parse (): DocumentContext { 103 | const tree = this.parser.document() 104 | this.parsed = true 105 | 106 | return tree 107 | } 108 | 109 | /** Asynchronously parse a Node Readable stream. */ 110 | async parseReadable (readable: Readable, opts: Omit): Promise { 111 | this.encoding = opts.encoding ?? 'utf8' 112 | this.fileName = opts.fileName ?? 'document.edi' 113 | this.parsed = false 114 | this.charStream = await BaseCharStreams.fromNodeReadable(readable, this.encoding, this.fileName) 115 | 116 | this.createParser(opts) 117 | 118 | const tree = this.parser.document() 119 | this.parsed = true 120 | 121 | return tree 122 | } 123 | 124 | /** 125 | * Synchronously parse to return an EDI DOM root; will simply return the DOM if `parse` or `parseReadable` have already been called. 126 | * If the parser has not been instantiated with options or `parseReadable` has not finished, this will return undefined. 127 | */ 128 | documentRoot (): EdiDomRoot { 129 | if (!this.parsed && typeof this.parser === 'object') this.parse() 130 | 131 | if (typeof this.listener === 'object') return this.listener.root 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomMessage.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { containerFromJson, relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomGroup } from './EdiDomGroup' 6 | import type { EdiDomInterchange } from './EdiDomInterchange' 7 | import type { EdiDomRoot } from './EdiDomRoot' 8 | import type { EdiDomSegment, EdiJsonSegment } from './EdiDomSegment' 9 | import type { EdiDomNode } from './EdiDomTypes' 10 | 11 | export interface EdiJsonMessage { 12 | header: EdiJsonSegment 13 | segments: EdiJsonSegment[] 14 | trailer: EdiJsonSegment 15 | } 16 | 17 | /** An EDIFACT UNH message or an X12 ST transaction. */ 18 | export class EdiDomMessage extends EdiDomAbstractNode { 19 | constructor () { 20 | super() 21 | this.nodeType = EdiDomNodeType.Message 22 | this.segments = [] 23 | } 24 | 25 | nodeType: EdiDomNodeType.Message 26 | parent: EdiDomInterchange | EdiDomGroup 27 | /** The header of this message. */ 28 | protected _header: EdiDomSegment<'UNH'|'ST'> 29 | /** The segments contained in this message. */ 30 | segments: EdiDomSegment[] 31 | /** The root of this instance. */ 32 | root: EdiDomRoot 33 | /** The trailer of this message. */ 34 | protected _trailer: EdiDomSegment<'UNT'|'SE'> 35 | 36 | /** The header of this message. */ 37 | get header (): EdiDomSegment<'UNH'|'ST'> { 38 | return this._header 39 | } 40 | 41 | /** The header of this message. */ 42 | set header (_header: EdiDomSegment<'UNH'|'ST'>) { 43 | relate(_header, this, this.root) 44 | this._header = _header 45 | } 46 | 47 | get innerEDI (): string { 48 | return this.segments.map(segment => segment.text).join('') 49 | } 50 | 51 | get outerEDI (): string { 52 | return this.header.outerEDI + this.innerEDI + this.trailer.outerEDI 53 | } 54 | 55 | get text (): string { 56 | return this.outerEDI 57 | } 58 | 59 | get textContent (): string { 60 | let content = `BEGIN Message\n` 61 | 62 | if (Array.isArray(this.segments) && this.segments.length > 0) { 63 | for (const segment of this.segments) { 64 | const innerContent = segment.textContent.split('\n') 65 | 66 | content += ' ' + innerContent.join('\n ') + '\n' 67 | } 68 | } 69 | 70 | return `${content}END Message` 71 | } 72 | 73 | /** The trailer of this message. */ 74 | get trailer (): EdiDomSegment<'UNT'|'SE'> { 75 | return this._trailer 76 | } 77 | 78 | /** The trailer of this message. */ 79 | set trailer (_trailer: EdiDomSegment<'UNT'|'SE'>) { 80 | relate(_trailer, this, this.root) 81 | this._trailer = _trailer 82 | } 83 | 84 | /** Add a segment to this message. */ 85 | addChildNode (child: EdiDomSegment): void { 86 | if (child.nodeType === EdiDomNodeType.Segment) { 87 | relate(child, this, this.root) 88 | this.segments.push(child) 89 | } 90 | } 91 | 92 | /** Retrieve the first available segment by tag or by zero-based index. */ 93 | getChildNode (indexOrTag: number | string): EdiDomSegment { 94 | if (typeof indexOrTag === 'number') { 95 | return this.segments[indexOrTag] 96 | } else if (typeof indexOrTag === 'string') { 97 | return this.segments.find(segment => segment.tag === indexOrTag) 98 | } 99 | } 100 | 101 | /** Remove a segment from this message and destroy all descendent relationships to this message. */ 102 | removeChildNode (child: EdiDomSegment): void { 103 | const index = this.segments.indexOf(child) 104 | 105 | if (index > -1) { 106 | unrelate(child) 107 | this.segments.splice(index, 1) 108 | } 109 | } 110 | 111 | * walk (): Generator { 112 | yield this 113 | if (typeof this.header === 'object') { 114 | for (const node of this.header.walk()) { 115 | yield node 116 | } 117 | } 118 | 119 | for (const segment of this.segments) { 120 | for (const node of segment.walk()) { 121 | yield node 122 | } 123 | } 124 | 125 | if (typeof this.trailer === 'object') { 126 | for (const node of this.trailer.walk()) { 127 | yield node 128 | } 129 | } 130 | } 131 | 132 | toJSON (): EdiJsonMessage { 133 | return { 134 | header: this.header.toJSON(), 135 | segments: this.segments.map(segment => segment.toJSON()), 136 | trailer: this.trailer.toJSON() 137 | } 138 | } 139 | 140 | fromJSON (input: EdiJsonMessage): void { 141 | this.segments = [] 142 | 143 | containerFromJson(this, input, () => { 144 | if (Array.isArray(input.segments)) { 145 | this.segments = [] 146 | 147 | for (const segment of input.segments) { 148 | const domSegment = new EdiDomGlobal.Segment() 149 | 150 | domSegment.fromJSON(segment) 151 | relate(domSegment, this, this.root) 152 | this.segments.push(domSegment) 153 | } 154 | } 155 | }) 156 | } 157 | } 158 | 159 | EdiDomGlobal.Message = EdiDomMessage 160 | EdiDomGlobal.Transaction = EdiDomMessage 161 | -------------------------------------------------------------------------------- /packages/fact/src/EdiFactParserVisitor.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/fact/EdiFactParser.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ParseTreeVisitor } from "antlr4ts/tree/ParseTreeVisitor"; 5 | 6 | import { DocumentContext } from "./EdiFactParser"; 7 | import { InterchangeContext } from "./EdiFactParser"; 8 | import { GroupContext } from "./EdiFactParser"; 9 | import { MessageContext } from "./EdiFactParser"; 10 | import { SegmentContext } from "./EdiFactParser"; 11 | import { ServiceStringAdviceContext } from "./EdiFactParser"; 12 | import { InterchangeHeaderContext } from "./EdiFactParser"; 13 | import { InterchangeTrailerContext } from "./EdiFactParser"; 14 | import { GroupHeaderContext } from "./EdiFactParser"; 15 | import { GroupTrailerContext } from "./EdiFactParser"; 16 | import { MessageHeaderContext } from "./EdiFactParser"; 17 | import { MessageTrailerContext } from "./EdiFactParser"; 18 | import { SegmentEndContext } from "./EdiFactParser"; 19 | import { ElementContext } from "./EdiFactParser"; 20 | import { RepititionContext } from "./EdiFactParser"; 21 | import { ComponentContext } from "./EdiFactParser"; 22 | import { ValueContext } from "./EdiFactParser"; 23 | 24 | 25 | /** 26 | * This interface defines a complete generic visitor for a parse tree produced 27 | * by `EdiFactParser`. 28 | * 29 | * @param The return type of the visit operation. Use `void` for 30 | * operations with no return type. 31 | */ 32 | export interface EdiFactParserVisitor extends ParseTreeVisitor { 33 | /** 34 | * Visit a parse tree produced by `EdiFactParser.document`. 35 | * @param ctx the parse tree 36 | * @return the visitor result 37 | */ 38 | visitDocument?: (ctx: DocumentContext) => Result; 39 | 40 | /** 41 | * Visit a parse tree produced by `EdiFactParser.interchange`. 42 | * @param ctx the parse tree 43 | * @return the visitor result 44 | */ 45 | visitInterchange?: (ctx: InterchangeContext) => Result; 46 | 47 | /** 48 | * Visit a parse tree produced by `EdiFactParser.group`. 49 | * @param ctx the parse tree 50 | * @return the visitor result 51 | */ 52 | visitGroup?: (ctx: GroupContext) => Result; 53 | 54 | /** 55 | * Visit a parse tree produced by `EdiFactParser.message`. 56 | * @param ctx the parse tree 57 | * @return the visitor result 58 | */ 59 | visitMessage?: (ctx: MessageContext) => Result; 60 | 61 | /** 62 | * Visit a parse tree produced by `EdiFactParser.segment`. 63 | * @param ctx the parse tree 64 | * @return the visitor result 65 | */ 66 | visitSegment?: (ctx: SegmentContext) => Result; 67 | 68 | /** 69 | * Visit a parse tree produced by `EdiFactParser.serviceStringAdvice`. 70 | * @param ctx the parse tree 71 | * @return the visitor result 72 | */ 73 | visitServiceStringAdvice?: (ctx: ServiceStringAdviceContext) => Result; 74 | 75 | /** 76 | * Visit a parse tree produced by `EdiFactParser.interchangeHeader`. 77 | * @param ctx the parse tree 78 | * @return the visitor result 79 | */ 80 | visitInterchangeHeader?: (ctx: InterchangeHeaderContext) => Result; 81 | 82 | /** 83 | * Visit a parse tree produced by `EdiFactParser.interchangeTrailer`. 84 | * @param ctx the parse tree 85 | * @return the visitor result 86 | */ 87 | visitInterchangeTrailer?: (ctx: InterchangeTrailerContext) => Result; 88 | 89 | /** 90 | * Visit a parse tree produced by `EdiFactParser.groupHeader`. 91 | * @param ctx the parse tree 92 | * @return the visitor result 93 | */ 94 | visitGroupHeader?: (ctx: GroupHeaderContext) => Result; 95 | 96 | /** 97 | * Visit a parse tree produced by `EdiFactParser.groupTrailer`. 98 | * @param ctx the parse tree 99 | * @return the visitor result 100 | */ 101 | visitGroupTrailer?: (ctx: GroupTrailerContext) => Result; 102 | 103 | /** 104 | * Visit a parse tree produced by `EdiFactParser.messageHeader`. 105 | * @param ctx the parse tree 106 | * @return the visitor result 107 | */ 108 | visitMessageHeader?: (ctx: MessageHeaderContext) => Result; 109 | 110 | /** 111 | * Visit a parse tree produced by `EdiFactParser.messageTrailer`. 112 | * @param ctx the parse tree 113 | * @return the visitor result 114 | */ 115 | visitMessageTrailer?: (ctx: MessageTrailerContext) => Result; 116 | 117 | /** 118 | * Visit a parse tree produced by `EdiFactParser.segmentEnd`. 119 | * @param ctx the parse tree 120 | * @return the visitor result 121 | */ 122 | visitSegmentEnd?: (ctx: SegmentEndContext) => Result; 123 | 124 | /** 125 | * Visit a parse tree produced by `EdiFactParser.element`. 126 | * @param ctx the parse tree 127 | * @return the visitor result 128 | */ 129 | visitElement?: (ctx: ElementContext) => Result; 130 | 131 | /** 132 | * Visit a parse tree produced by `EdiFactParser.repitition`. 133 | * @param ctx the parse tree 134 | * @return the visitor result 135 | */ 136 | visitRepitition?: (ctx: RepititionContext) => Result; 137 | 138 | /** 139 | * Visit a parse tree produced by `EdiFactParser.component`. 140 | * @param ctx the parse tree 141 | * @return the visitor result 142 | */ 143 | visitComponent?: (ctx: ComponentContext) => Result; 144 | 145 | /** 146 | * Visit a parse tree produced by `EdiFactParser.value`. 147 | * @param ctx the parse tree 148 | * @return the visitor result 149 | */ 150 | visitValue?: (ctx: ValueContext) => Result; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomSegment.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomElement, EdiJsonElement } from './EdiDomElement' 6 | import type { EdiDomRoot } from './EdiDomRoot' 7 | import type { EdiDomNode } from './EdiDomTypes' 8 | import type { EdiDomInterchange } from './EdiDomInterchange' 9 | import type { EdiDomGroup } from './EdiDomGroup' 10 | import type { EdiDomMessage } from './EdiDomMessage' 11 | 12 | export interface EdiJsonSegment { 13 | tag: string 14 | elements: EdiJsonElement[] 15 | } 16 | 17 | /** The segment of an EDI document. */ 18 | export class EdiDomSegment extends EdiDomAbstractNode { 19 | constructor (tag?: T) { 20 | super() 21 | this.nodeType = EdiDomNodeType.Segment 22 | this.tag = tag ?? ('' as T) 23 | this.elements = [] 24 | } 25 | 26 | nodeType: EdiDomNodeType.Segment 27 | parent: EdiDomInterchange | EdiDomGroup | EdiDomMessage 28 | /** The tag for this node. */ 29 | tag: T 30 | /** The child elements of this node. */ 31 | elements: EdiDomElement[] 32 | /** The root of this instance. */ 33 | root: EdiDomRoot 34 | 35 | get innerEDI (): string { 36 | return this.elements.map(element => element.text).join('') 37 | } 38 | 39 | get outerEDI (): string { 40 | const contents = this.tag + this.elements 41 | .map(element => element.text) 42 | .join('') + 43 | this.root.options.segmentTerminator 44 | 45 | if (typeof this.root.options.endOfLine === 'string') { 46 | return contents + this.root.options.endOfLine 47 | } 48 | 49 | return contents 50 | } 51 | 52 | get text (): string { 53 | return this.outerEDI 54 | } 55 | 56 | get textContent (): string { 57 | let content = `BEGIN Segment ${this.tag}\n` 58 | 59 | if (Array.isArray(this.elements) && this.elements.length > 0) { 60 | for (const element of this.elements) { 61 | const innerContent = element.textContent.split('\n') 62 | 63 | content += ' ' + innerContent.join('\n ') + '\n' 64 | } 65 | } 66 | 67 | return `${content}END Segment ${this.tag}` 68 | } 69 | 70 | private getZeroBasedIndex (position: string | number): number { 71 | let index = -1 72 | 73 | switch (typeof position) { 74 | case 'number': 75 | index = position - 1 76 | break 77 | case 'string': 78 | index = parseFloat(position) - 1 79 | if (Number.isNaN(index)) index = -1 80 | break 81 | } 82 | 83 | return index 84 | } 85 | 86 | /** Add a child element to this segment, optionally at a given 1-based position. */ 87 | addChildNode (child: EdiDomElement, position?: string | number): void { 88 | if (child.nodeType === EdiDomNodeType.Element) { 89 | const index = this.getZeroBasedIndex(position) 90 | 91 | relate(child, this, this.root) 92 | 93 | if (index > -1) { 94 | this.elements[index] = child 95 | 96 | for (let i = 0; i < index; i += 1) { 97 | if (typeof this.elements[i] === 'undefined') { 98 | this.elements[i] = new EdiDomGlobal.Element() 99 | relate(this.elements[i], this, this.root) 100 | } 101 | } 102 | } else { 103 | this.elements.push(child) 104 | } 105 | } 106 | } 107 | 108 | /** Get an element at a 1-based position. */ 109 | getChildNode (position: string | number): EdiDomElement { 110 | const index = typeof position === 'string' ? parseFloat(position) - 1 : position - 1 111 | 112 | return this.elements[index] 113 | } 114 | 115 | /** Remove an element by instance or 1-based position from this segment. */ 116 | removeChildNode (child: EdiDomElement): void 117 | removeChildNode (position: string | number): void 118 | removeChildNode (child: EdiDomElement | string | number): void { 119 | let element: EdiDomElement 120 | 121 | if (typeof child === 'object') { 122 | if (child.nodeType === EdiDomNodeType.Element) { 123 | const index = this.elements.indexOf(child) 124 | element = child 125 | 126 | if (index > -1) this.elements.splice(index, 1) 127 | } 128 | } else { 129 | const index = typeof child === 'string' ? parseFloat(child) - 1 : child - 1 130 | 131 | if (index > -1) { 132 | element = this.elements[index] 133 | 134 | this.elements.splice(index, 1) 135 | } 136 | } 137 | 138 | if (typeof element === 'object') unrelate(element) 139 | } 140 | 141 | * walk (): Generator { 142 | yield this 143 | 144 | for (const element of this.elements) { 145 | for (const node of element.walk()) { 146 | yield node 147 | } 148 | } 149 | } 150 | 151 | toJSON (): EdiJsonSegment { 152 | return { 153 | tag: this.tag, 154 | elements: this.elements.map(element => element.toJSON()) 155 | } 156 | } 157 | 158 | fromJSON (input: EdiJsonSegment): void { 159 | if ('tag' in input) { 160 | this.tag = input.tag as T 161 | } 162 | 163 | if (Array.isArray(input.elements)) { 164 | this.elements = [] 165 | 166 | for (const element of input.elements) { 167 | const domElement = new EdiDomGlobal.Element() 168 | 169 | domElement.fromJSON(element) 170 | relate(domElement, this, this.root) 171 | this.elements.push(domElement) 172 | } 173 | } 174 | } 175 | } 176 | 177 | EdiDomGlobal.Segment = EdiDomSegment 178 | -------------------------------------------------------------------------------- /packages/dom/query/QueryEngineList.ts: -------------------------------------------------------------------------------- 1 | import type { QueryIterator } from './QueryEngineTypes' 2 | 3 | /** Custom iterable for wrapping the query engine generator in a re-usable, re-iterable list. */ 4 | export class QueryEngineList implements Iterable { 5 | constructor (iterator?: QueryIterator) { 6 | this._values = [] 7 | this._iterator = iterator 8 | this._iterationFinished = typeof iterator === 'undefined' 9 | 10 | return new Proxy(this, { 11 | get (target: QueryEngineList, prop: string | symbol, receiver: any): any { 12 | if (typeof prop === 'string') { 13 | const key = parseFloat(prop) 14 | 15 | if (Number.isNaN(key)) { 16 | return target[prop] 17 | } else { 18 | return target.get(key) 19 | } 20 | } else { 21 | return target[prop] 22 | } 23 | }, 24 | set (target: QueryEngineList, prop: string | symbol, value: any, receiver: any): boolean { 25 | if (typeof prop === 'string') { 26 | const key = parseFloat(prop) 27 | 28 | if (Number.isNaN(key)) { 29 | target[prop] = value 30 | } else { 31 | target._values[key] = value 32 | } 33 | } else { 34 | target[prop] = value 35 | } 36 | 37 | return true 38 | } 39 | }) 40 | } 41 | 42 | private readonly _values: T[] 43 | private readonly _iterator?: QueryIterator 44 | private _iterationFinished: boolean 45 | [key: number]: T 46 | 47 | /** Get the size of this list. */ 48 | get size (): number { 49 | if (this._iterationFinished) { 50 | return this._values.length 51 | } 52 | 53 | let size = -1 54 | 55 | for (const [index] of this.entries()) { 56 | size = index 57 | } 58 | 59 | return size + 1 60 | } 61 | 62 | /** Add a value to this list. */ 63 | add (result: T): void { 64 | this._values.push(result) 65 | } 66 | 67 | /** Get a result by its index in the list. */ 68 | get (key: number): T { 69 | // We check if `key in ...` because we need to avoid weird cases where `indexOf` or `includes` may return `-1` 70 | if (key in this._values || this._iterationFinished) { // NOSONAR 71 | return this._values[key] 72 | } 73 | 74 | let result: T 75 | 76 | for (const [index, node] of this.entries()) { 77 | if (index === key) result = node 78 | } 79 | 80 | return result 81 | } 82 | 83 | /** Delete a result from this list by value or by index. */ 84 | delete (result: T | number): void { 85 | if (typeof result === 'number') { 86 | // We check if `key in ...` because we need to avoid weird cases where `indexOf` or `includes` may return `-1` 87 | if (result in this._values || this._iterationFinished) { // NOSONAR 88 | this._values.splice(result, 1) 89 | 90 | return 91 | } 92 | 93 | let i: number 94 | 95 | for (const [currentIndex] of this.entries()) { 96 | if (currentIndex === result) i = currentIndex 97 | } 98 | 99 | this._values.splice(i, 1) 100 | 101 | return 102 | } 103 | 104 | const index = this._values.indexOf(result) 105 | 106 | if (index > -1 || this._iterationFinished) { 107 | this._values.splice(index, 1) 108 | } 109 | } 110 | 111 | /** Implement iterable iterator for this class. */ 112 | [Symbol.iterator] (): IterableIterator { 113 | return this.values() 114 | } 115 | 116 | /** Get the entries for this list in the form of key/value pairs. */ 117 | * entries (): IterableIterator<[number, T]> { 118 | if (this._iterationFinished) { 119 | for (const [valueIndex, node] of this._values.entries()) { 120 | yield [valueIndex, node] 121 | } 122 | 123 | return 124 | } 125 | 126 | let index = 0 127 | 128 | if (this._values.length > 0) { 129 | for (const [valueIndex, node] of this._values.entries()) { 130 | yield [valueIndex, node] 131 | 132 | index++ 133 | } 134 | } 135 | 136 | for (const node of this._iterator) { 137 | this._values[index] = node 138 | 139 | yield [index, node] 140 | 141 | index++ 142 | 143 | if (this._values.length >= index) { 144 | for (let newIndex = index; newIndex < this._values.length; newIndex++) { 145 | yield [newIndex, this._values[newIndex]] 146 | } 147 | } 148 | } 149 | 150 | this._iterationFinished = true 151 | } 152 | 153 | /** Get all keys from this list. */ 154 | * keys (): IterableIterator { 155 | for (const [index] of this.entries()) yield index 156 | } 157 | 158 | /** Get all values from this list. */ 159 | * values (): IterableIterator { 160 | for (const entry of this.entries()) yield entry[1] 161 | } 162 | 163 | /** Execute a synchronous callback for each value in the list. */ 164 | forEach (callbackfn: (value: T, key: number, parent: QueryEngineList) => void, thisArg?: any): void { 165 | const fn: typeof callbackfn = typeof thisArg === 'undefined' 166 | ? callbackfn.bind(this) 167 | : callbackfn.bind(thisArg) 168 | 169 | for (const [key, value] of this.entries()) fn(value, key, this) 170 | } 171 | 172 | /** Map each value to the result of a synchronous callback. */ 173 | map (callbackfn: (value: T, index: number, parent: QueryEngineList) => U, thisArg?: any): QueryEngineList { 174 | const entries = this.entries() 175 | const fn: typeof callbackfn = typeof thisArg === 'undefined' 176 | ? callbackfn.bind(this) 177 | : callbackfn.bind(thisArg) 178 | const mapper = function * map (list: QueryEngineList): QueryIterator { 179 | for (const [key, value] of entries) { 180 | yield fn(value, key, list) 181 | } 182 | } 183 | 184 | return new QueryEngineList(mapper(this)) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /packages/dom/query/ElementSelectorListener.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/query/ElementSelector.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener"; 5 | 6 | import { SelectorContext } from "./ElementSelectorParser"; 7 | import { ElementAdjacentSelectorContext } from "./ElementSelectorParser"; 8 | import { ElementPrecedentSelectorContext } from "./ElementSelectorParser"; 9 | import { ElementSiblingSelectorContext } from "./ElementSelectorParser"; 10 | import { ElementContainsValueSelectorContext } from "./ElementSelectorParser"; 11 | import { ElementNotValueSelectorContext } from "./ElementSelectorParser"; 12 | import { ElementValueSelectorContext } from "./ElementSelectorParser"; 13 | import { LoopPathSelectorContext } from "./ElementSelectorParser"; 14 | import { HlPathSelectorContext } from "./ElementSelectorParser"; 15 | import { ParentSegmentSelectorContext } from "./ElementSelectorParser"; 16 | import { ElementSelectorContext } from "./ElementSelectorParser"; 17 | import { SegmentTagContext } from "./ElementSelectorParser"; 18 | 19 | 20 | /** 21 | * This interface defines a complete listener for a parse tree produced by 22 | * `ElementSelectorParser`. 23 | */ 24 | export interface ElementSelectorListener extends ParseTreeListener { 25 | /** 26 | * Enter a parse tree produced by `ElementSelectorParser.selector`. 27 | * @param ctx the parse tree 28 | */ 29 | enterSelector?: (ctx: SelectorContext) => void; 30 | /** 31 | * Exit a parse tree produced by `ElementSelectorParser.selector`. 32 | * @param ctx the parse tree 33 | */ 34 | exitSelector?: (ctx: SelectorContext) => void; 35 | 36 | /** 37 | * Enter a parse tree produced by `ElementSelectorParser.elementAdjacentSelector`. 38 | * @param ctx the parse tree 39 | */ 40 | enterElementAdjacentSelector?: (ctx: ElementAdjacentSelectorContext) => void; 41 | /** 42 | * Exit a parse tree produced by `ElementSelectorParser.elementAdjacentSelector`. 43 | * @param ctx the parse tree 44 | */ 45 | exitElementAdjacentSelector?: (ctx: ElementAdjacentSelectorContext) => void; 46 | 47 | /** 48 | * Enter a parse tree produced by `ElementSelectorParser.elementPrecedentSelector`. 49 | * @param ctx the parse tree 50 | */ 51 | enterElementPrecedentSelector?: (ctx: ElementPrecedentSelectorContext) => void; 52 | /** 53 | * Exit a parse tree produced by `ElementSelectorParser.elementPrecedentSelector`. 54 | * @param ctx the parse tree 55 | */ 56 | exitElementPrecedentSelector?: (ctx: ElementPrecedentSelectorContext) => void; 57 | 58 | /** 59 | * Enter a parse tree produced by `ElementSelectorParser.elementSiblingSelector`. 60 | * @param ctx the parse tree 61 | */ 62 | enterElementSiblingSelector?: (ctx: ElementSiblingSelectorContext) => void; 63 | /** 64 | * Exit a parse tree produced by `ElementSelectorParser.elementSiblingSelector`. 65 | * @param ctx the parse tree 66 | */ 67 | exitElementSiblingSelector?: (ctx: ElementSiblingSelectorContext) => void; 68 | 69 | /** 70 | * Enter a parse tree produced by `ElementSelectorParser.elementContainsValueSelector`. 71 | * @param ctx the parse tree 72 | */ 73 | enterElementContainsValueSelector?: (ctx: ElementContainsValueSelectorContext) => void; 74 | /** 75 | * Exit a parse tree produced by `ElementSelectorParser.elementContainsValueSelector`. 76 | * @param ctx the parse tree 77 | */ 78 | exitElementContainsValueSelector?: (ctx: ElementContainsValueSelectorContext) => void; 79 | 80 | /** 81 | * Enter a parse tree produced by `ElementSelectorParser.elementNotValueSelector`. 82 | * @param ctx the parse tree 83 | */ 84 | enterElementNotValueSelector?: (ctx: ElementNotValueSelectorContext) => void; 85 | /** 86 | * Exit a parse tree produced by `ElementSelectorParser.elementNotValueSelector`. 87 | * @param ctx the parse tree 88 | */ 89 | exitElementNotValueSelector?: (ctx: ElementNotValueSelectorContext) => void; 90 | 91 | /** 92 | * Enter a parse tree produced by `ElementSelectorParser.elementValueSelector`. 93 | * @param ctx the parse tree 94 | */ 95 | enterElementValueSelector?: (ctx: ElementValueSelectorContext) => void; 96 | /** 97 | * Exit a parse tree produced by `ElementSelectorParser.elementValueSelector`. 98 | * @param ctx the parse tree 99 | */ 100 | exitElementValueSelector?: (ctx: ElementValueSelectorContext) => void; 101 | 102 | /** 103 | * Enter a parse tree produced by `ElementSelectorParser.loopPathSelector`. 104 | * @param ctx the parse tree 105 | */ 106 | enterLoopPathSelector?: (ctx: LoopPathSelectorContext) => void; 107 | /** 108 | * Exit a parse tree produced by `ElementSelectorParser.loopPathSelector`. 109 | * @param ctx the parse tree 110 | */ 111 | exitLoopPathSelector?: (ctx: LoopPathSelectorContext) => void; 112 | 113 | /** 114 | * Enter a parse tree produced by `ElementSelectorParser.hlPathSelector`. 115 | * @param ctx the parse tree 116 | */ 117 | enterHlPathSelector?: (ctx: HlPathSelectorContext) => void; 118 | /** 119 | * Exit a parse tree produced by `ElementSelectorParser.hlPathSelector`. 120 | * @param ctx the parse tree 121 | */ 122 | exitHlPathSelector?: (ctx: HlPathSelectorContext) => void; 123 | 124 | /** 125 | * Enter a parse tree produced by `ElementSelectorParser.parentSegmentSelector`. 126 | * @param ctx the parse tree 127 | */ 128 | enterParentSegmentSelector?: (ctx: ParentSegmentSelectorContext) => void; 129 | /** 130 | * Exit a parse tree produced by `ElementSelectorParser.parentSegmentSelector`. 131 | * @param ctx the parse tree 132 | */ 133 | exitParentSegmentSelector?: (ctx: ParentSegmentSelectorContext) => void; 134 | 135 | /** 136 | * Enter a parse tree produced by `ElementSelectorParser.elementSelector`. 137 | * @param ctx the parse tree 138 | */ 139 | enterElementSelector?: (ctx: ElementSelectorContext) => void; 140 | /** 141 | * Exit a parse tree produced by `ElementSelectorParser.elementSelector`. 142 | * @param ctx the parse tree 143 | */ 144 | exitElementSelector?: (ctx: ElementSelectorContext) => void; 145 | 146 | /** 147 | * Enter a parse tree produced by `ElementSelectorParser.segmentTag`. 148 | * @param ctx the parse tree 149 | */ 150 | enterSegmentTag?: (ctx: SegmentTagContext) => void; 151 | /** 152 | * Exit a parse tree produced by `ElementSelectorParser.segmentTag`. 153 | * @param ctx the parse tree 154 | */ 155 | exitSegmentTag?: (ctx: SegmentTagContext) => void; 156 | } 157 | 158 | -------------------------------------------------------------------------------- /packages/dom/src/EdiDomInterchange.ts: -------------------------------------------------------------------------------- 1 | import { EdiDomAbstractNode } from './EdiDomAbstractNode' 2 | import { EdiDomGlobal } from './EdiDomGlobal' 3 | import { assignMessagesFromJson, containerFromJson, messagesAsContent, relate, unrelate } from './EdiDomHelpers' 4 | import { EdiDomNodeType } from './EdiDomNodeType' 5 | import type { EdiDomGroup, EdiJsonGroup } from './EdiDomGroup' 6 | import type { EdiDomMessage, EdiJsonMessage } from './EdiDomMessage' 7 | import type { EdiDomRoot } from './EdiDomRoot' 8 | import type { EdiDomSegment, EdiJsonSegment } from './EdiDomSegment' 9 | import type { EdiDomNode, InterchangeChild } from './EdiDomTypes' 10 | 11 | export interface EdiJsonInterchange { 12 | header: EdiJsonSegment 13 | groups?: EdiJsonGroup[] 14 | messages?: EdiJsonMessage[] 15 | trailer: EdiJsonSegment 16 | } 17 | 18 | export class EdiDomInterchange extends EdiDomAbstractNode { 19 | constructor () { 20 | super() 21 | this.nodeType = EdiDomNodeType.Interchange 22 | this.groups = [] 23 | this.messages = [] 24 | } 25 | 26 | nodeType: EdiDomNodeType.Interchange 27 | parent: EdiDomRoot 28 | protected _header: EdiDomSegment<'UNB'|'ISA'> 29 | /** The child groups of this interchange. */ 30 | groups: EdiDomGroup[] 31 | /** The child messages of this interchange; applies to EDIFACT. */ 32 | messages: EdiDomMessage[] 33 | /** The root of this instance. */ 34 | root: EdiDomRoot 35 | protected _trailer: EdiDomSegment<'UNZ'|'IEA'> 36 | 37 | /** The header of this interchange. */ 38 | get header (): EdiDomSegment<'UNB'|'ISA'> { 39 | return this._header 40 | } 41 | 42 | /** The header of this interchange. */ 43 | set header (_header: EdiDomSegment<'UNB'|'ISA'>) { 44 | relate(_header, this, this.root) 45 | this._header = _header 46 | } 47 | 48 | get innerEDI (): string { 49 | if (this.groups.length > 0) { 50 | return this.groups.map(group => group.outerEDI).join('') 51 | } 52 | 53 | return this.messages.map(message => message.outerEDI).join('') 54 | } 55 | 56 | get outerEDI (): string { 57 | const handleUNA = (): string => { 58 | return this.header.tag === 'UNB' 59 | ? this.root.createUNAString() 60 | : '' 61 | } 62 | 63 | return handleUNA() + this.header.outerEDI + this.innerEDI + this.trailer.outerEDI 64 | } 65 | 66 | get text (): string { 67 | return this.outerEDI 68 | } 69 | 70 | get textContent (): string { 71 | let content = `BEGIN Interchange\n` 72 | 73 | if (Array.isArray(this.groups) && this.groups.length > 0) { 74 | for (const group of this.groups) { 75 | const innerContent = group.textContent.split('\n') 76 | 77 | content += ' ' + innerContent.join('\n ') + '\n' 78 | } 79 | } 80 | 81 | return `${content}${messagesAsContent(this)}END Interchange` 82 | } 83 | 84 | /** The trailer of this interchange. */ 85 | get trailer (): EdiDomSegment<'UNZ'|'IEA'> { 86 | return this._trailer 87 | } 88 | 89 | /** The trailer of this interchange. */ 90 | set trailer (_trailer: EdiDomSegment<'UNZ'|'IEA'>) { 91 | relate(_trailer, this, this.root) 92 | this._trailer = _trailer 93 | } 94 | 95 | /** Add a group or message to this interchange. */ 96 | addChildNode (child: InterchangeChild): void { 97 | const relateToArray = (array: any[]): void => { 98 | relate(child, this, this.root) 99 | array.push(child) 100 | } 101 | 102 | switch (child.nodeType) { 103 | case EdiDomNodeType.Group: 104 | relateToArray(this.groups) 105 | break 106 | case EdiDomNodeType.Message: 107 | relateToArray(this.messages) 108 | break 109 | } 110 | } 111 | 112 | /** Get a child group or child message of this interchange. */ 113 | getChildNode (index: number): InterchangeChild { 114 | if (this.groups.length > 0) return this.groups[index] 115 | 116 | return this.messages[index] 117 | } 118 | 119 | /** Remove a child group or message from this interchange. */ 120 | removeChildNode (child: InterchangeChild): void { 121 | const unrelateFromArray = (array: any[]): void => { 122 | const index = array.indexOf(child) 123 | 124 | if (index > -1) { 125 | unrelate(child) 126 | array.splice(index, 1) 127 | } 128 | } 129 | 130 | switch (child.nodeType) { 131 | case EdiDomNodeType.Group: 132 | unrelateFromArray(this.groups) 133 | break 134 | case EdiDomNodeType.Message: 135 | unrelateFromArray(this.messages) 136 | break 137 | } 138 | } 139 | 140 | /** Walk the document object model from this node sequentially. */ 141 | * walk (): Generator { 142 | yield this 143 | if (typeof this.header === 'object') { 144 | for (const node of this.header.walk()) { 145 | yield node 146 | } 147 | } 148 | 149 | for (const group of this.groups) { 150 | for (const node of group.walk()) { 151 | yield node 152 | } 153 | } 154 | 155 | for (const message of this.messages) { 156 | for (const node of message.walk()) { 157 | yield node 158 | } 159 | } 160 | 161 | if (typeof this.trailer === 'object') { 162 | for (const node of this.trailer.walk()) { 163 | yield node 164 | } 165 | } 166 | } 167 | 168 | toJSON (): EdiJsonInterchange { 169 | // Guarantee the order of property serialization. 170 | const json: EdiJsonInterchange = { 171 | header: this.header.toJSON(), 172 | groups: undefined, 173 | messages: undefined, 174 | trailer: this.trailer.toJSON() 175 | } 176 | 177 | if (this.groups.length > 0) { 178 | json.groups = this.groups.map(group => group.toJSON()) 179 | delete json.messages 180 | 181 | return json 182 | } 183 | 184 | json.messages = this.messages.map(message => message.toJSON()) 185 | delete json.groups 186 | 187 | return json 188 | } 189 | 190 | fromJSON (input: EdiJsonInterchange): void { 191 | this.groups = [] 192 | this.messages = [] 193 | 194 | containerFromJson(this, input, () => { 195 | if (Array.isArray(input.groups) && input.groups.length > 0) { 196 | for (const group of input.groups) { 197 | const domGroup = new EdiDomGlobal.Group() 198 | 199 | domGroup.fromJSON(group) 200 | relate(domGroup, this, this.root) 201 | this.groups.push(domGroup) 202 | } 203 | } 204 | 205 | assignMessagesFromJson(this, input) 206 | }) 207 | } 208 | } 209 | 210 | EdiDomGlobal.Interchange = EdiDomInterchange 211 | -------------------------------------------------------------------------------- /packages/x12/src/EdiX12ParserVisitor.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/x12/EdiX12Parser.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ParseTreeVisitor } from "antlr4ts/tree/ParseTreeVisitor"; 5 | 6 | import { DocumentContext } from "./EdiX12Parser"; 7 | import { InterchangeContext } from "./EdiX12Parser"; 8 | import { GroupContext } from "./EdiX12Parser"; 9 | import { TransactionContext } from "./EdiX12Parser"; 10 | import { SegmentContext } from "./EdiX12Parser"; 11 | import { InterchangeHeaderContext } from "./EdiX12Parser"; 12 | import { InterchangeTrailerContext } from "./EdiX12Parser"; 13 | import { GroupHeaderContext } from "./EdiX12Parser"; 14 | import { GroupTrailerContext } from "./EdiX12Parser"; 15 | import { TransactionHeaderContext } from "./EdiX12Parser"; 16 | import { TransactionTrailerContext } from "./EdiX12Parser"; 17 | import { SegmentEndContext } from "./EdiX12Parser"; 18 | import { RepititionContext } from "./EdiX12Parser"; 19 | import { ElementContext } from "./EdiX12Parser"; 20 | import { InterchangeElementContext } from "./EdiX12Parser"; 21 | import { StrictElementContext } from "./EdiX12Parser"; 22 | import { DataCharElementContext } from "./EdiX12Parser"; 23 | import { RepititionCharElementContext } from "./EdiX12Parser"; 24 | import { ComponentCharElementContext } from "./EdiX12Parser"; 25 | import { RepeatedElementContext } from "./EdiX12Parser"; 26 | import { ComponentContext } from "./EdiX12Parser"; 27 | import { ValueContext } from "./EdiX12Parser"; 28 | 29 | 30 | /** 31 | * This interface defines a complete generic visitor for a parse tree produced 32 | * by `EdiX12Parser`. 33 | * 34 | * @param The return type of the visit operation. Use `void` for 35 | * operations with no return type. 36 | */ 37 | export interface EdiX12ParserVisitor extends ParseTreeVisitor { 38 | /** 39 | * Visit a parse tree produced by `EdiX12Parser.document`. 40 | * @param ctx the parse tree 41 | * @return the visitor result 42 | */ 43 | visitDocument?: (ctx: DocumentContext) => Result; 44 | 45 | /** 46 | * Visit a parse tree produced by `EdiX12Parser.interchange`. 47 | * @param ctx the parse tree 48 | * @return the visitor result 49 | */ 50 | visitInterchange?: (ctx: InterchangeContext) => Result; 51 | 52 | /** 53 | * Visit a parse tree produced by `EdiX12Parser.group`. 54 | * @param ctx the parse tree 55 | * @return the visitor result 56 | */ 57 | visitGroup?: (ctx: GroupContext) => Result; 58 | 59 | /** 60 | * Visit a parse tree produced by `EdiX12Parser.transaction`. 61 | * @param ctx the parse tree 62 | * @return the visitor result 63 | */ 64 | visitTransaction?: (ctx: TransactionContext) => Result; 65 | 66 | /** 67 | * Visit a parse tree produced by `EdiX12Parser.segment`. 68 | * @param ctx the parse tree 69 | * @return the visitor result 70 | */ 71 | visitSegment?: (ctx: SegmentContext) => Result; 72 | 73 | /** 74 | * Visit a parse tree produced by `EdiX12Parser.interchangeHeader`. 75 | * @param ctx the parse tree 76 | * @return the visitor result 77 | */ 78 | visitInterchangeHeader?: (ctx: InterchangeHeaderContext) => Result; 79 | 80 | /** 81 | * Visit a parse tree produced by `EdiX12Parser.interchangeTrailer`. 82 | * @param ctx the parse tree 83 | * @return the visitor result 84 | */ 85 | visitInterchangeTrailer?: (ctx: InterchangeTrailerContext) => Result; 86 | 87 | /** 88 | * Visit a parse tree produced by `EdiX12Parser.groupHeader`. 89 | * @param ctx the parse tree 90 | * @return the visitor result 91 | */ 92 | visitGroupHeader?: (ctx: GroupHeaderContext) => Result; 93 | 94 | /** 95 | * Visit a parse tree produced by `EdiX12Parser.groupTrailer`. 96 | * @param ctx the parse tree 97 | * @return the visitor result 98 | */ 99 | visitGroupTrailer?: (ctx: GroupTrailerContext) => Result; 100 | 101 | /** 102 | * Visit a parse tree produced by `EdiX12Parser.transactionHeader`. 103 | * @param ctx the parse tree 104 | * @return the visitor result 105 | */ 106 | visitTransactionHeader?: (ctx: TransactionHeaderContext) => Result; 107 | 108 | /** 109 | * Visit a parse tree produced by `EdiX12Parser.transactionTrailer`. 110 | * @param ctx the parse tree 111 | * @return the visitor result 112 | */ 113 | visitTransactionTrailer?: (ctx: TransactionTrailerContext) => Result; 114 | 115 | /** 116 | * Visit a parse tree produced by `EdiX12Parser.segmentEnd`. 117 | * @param ctx the parse tree 118 | * @return the visitor result 119 | */ 120 | visitSegmentEnd?: (ctx: SegmentEndContext) => Result; 121 | 122 | /** 123 | * Visit a parse tree produced by `EdiX12Parser.repitition`. 124 | * @param ctx the parse tree 125 | * @return the visitor result 126 | */ 127 | visitRepitition?: (ctx: RepititionContext) => Result; 128 | 129 | /** 130 | * Visit a parse tree produced by `EdiX12Parser.element`. 131 | * @param ctx the parse tree 132 | * @return the visitor result 133 | */ 134 | visitElement?: (ctx: ElementContext) => Result; 135 | 136 | /** 137 | * Visit a parse tree produced by `EdiX12Parser.interchangeElement`. 138 | * @param ctx the parse tree 139 | * @return the visitor result 140 | */ 141 | visitInterchangeElement?: (ctx: InterchangeElementContext) => Result; 142 | 143 | /** 144 | * Visit a parse tree produced by `EdiX12Parser.strictElement`. 145 | * @param ctx the parse tree 146 | * @return the visitor result 147 | */ 148 | visitStrictElement?: (ctx: StrictElementContext) => Result; 149 | 150 | /** 151 | * Visit a parse tree produced by `EdiX12Parser.dataCharElement`. 152 | * @param ctx the parse tree 153 | * @return the visitor result 154 | */ 155 | visitDataCharElement?: (ctx: DataCharElementContext) => Result; 156 | 157 | /** 158 | * Visit a parse tree produced by `EdiX12Parser.repititionCharElement`. 159 | * @param ctx the parse tree 160 | * @return the visitor result 161 | */ 162 | visitRepititionCharElement?: (ctx: RepititionCharElementContext) => Result; 163 | 164 | /** 165 | * Visit a parse tree produced by `EdiX12Parser.componentCharElement`. 166 | * @param ctx the parse tree 167 | * @return the visitor result 168 | */ 169 | visitComponentCharElement?: (ctx: ComponentCharElementContext) => Result; 170 | 171 | /** 172 | * Visit a parse tree produced by `EdiX12Parser.repeatedElement`. 173 | * @param ctx the parse tree 174 | * @return the visitor result 175 | */ 176 | visitRepeatedElement?: (ctx: RepeatedElementContext) => Result; 177 | 178 | /** 179 | * Visit a parse tree produced by `EdiX12Parser.component`. 180 | * @param ctx the parse tree 181 | * @return the visitor result 182 | */ 183 | visitComponent?: (ctx: ComponentContext) => Result; 184 | 185 | /** 186 | * Visit a parse tree produced by `EdiX12Parser.value`. 187 | * @param ctx the parse tree 188 | * @return the visitor result 189 | */ 190 | visitValue?: (ctx: ValueContext) => Result; 191 | } 192 | 193 | -------------------------------------------------------------------------------- /packages/dom/query/ElementSelectorLexer.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/query/ElementSelector.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ATN } from "antlr4ts/atn/ATN"; 5 | import { ATNDeserializer } from "antlr4ts/atn/ATNDeserializer"; 6 | import { CharStream } from "antlr4ts/CharStream"; 7 | import { Lexer } from "antlr4ts/Lexer"; 8 | import { LexerATNSimulator } from "antlr4ts/atn/LexerATNSimulator"; 9 | import { NotNull } from "antlr4ts/Decorators"; 10 | import { Override } from "antlr4ts/Decorators"; 11 | import { RuleContext } from "antlr4ts/RuleContext"; 12 | import { Vocabulary } from "antlr4ts/Vocabulary"; 13 | import { VocabularyImpl } from "antlr4ts/VocabularyImpl"; 14 | 15 | import * as Utils from "antlr4ts/misc/Utils"; 16 | 17 | 18 | export class ElementSelectorLexer extends Lexer { 19 | public static readonly T__0 = 1; 20 | public static readonly T__1 = 2; 21 | public static readonly T__2 = 3; 22 | public static readonly T__3 = 4; 23 | public static readonly T__4 = 5; 24 | public static readonly T__5 = 6; 25 | public static readonly T__6 = 7; 26 | public static readonly T__7 = 8; 27 | public static readonly T__8 = 9; 28 | public static readonly ElementValue = 10; 29 | public static readonly ElementReference = 11; 30 | public static readonly ElementID = 12; 31 | public static readonly SegmentID = 13; 32 | public static readonly AnyCharacter = 14; 33 | 34 | // tslint:disable:no-trailing-whitespace 35 | public static readonly channelNames: string[] = [ 36 | "DEFAULT_TOKEN_CHANNEL", "HIDDEN", 37 | ]; 38 | 39 | // tslint:disable:no-trailing-whitespace 40 | public static readonly modeNames: string[] = [ 41 | "DEFAULT_MODE", 42 | ]; 43 | 44 | public static readonly ruleNames: string[] = [ 45 | "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", 46 | "Number", "Letter", "Special", "AlphaNumeric", "SegmentID2", "SegmentID3", 47 | "ElementValue", "ElementReference", "ElementID", "SegmentID", "AnyCharacter", 48 | ]; 49 | 50 | private static readonly _LITERAL_NAMES: Array = [ 51 | undefined, "'~'", "'?'", "':'", "'*'", "'!'", "'^'", "'-'", "'HL'", "'+'", 52 | ]; 53 | private static readonly _SYMBOLIC_NAMES: Array = [ 54 | undefined, undefined, undefined, undefined, undefined, undefined, undefined, 55 | undefined, undefined, undefined, "ElementValue", "ElementReference", "ElementID", 56 | "SegmentID", "AnyCharacter", 57 | ]; 58 | public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(ElementSelectorLexer._LITERAL_NAMES, ElementSelectorLexer._SYMBOLIC_NAMES, []); 59 | 60 | // @Override 61 | // @NotNull 62 | public get vocabulary(): Vocabulary { 63 | return ElementSelectorLexer.VOCABULARY; 64 | } 65 | // tslint:enable:no-trailing-whitespace 66 | 67 | 68 | constructor(input: CharStream) { 69 | super(input); 70 | this._interp = new LexerATNSimulator(ElementSelectorLexer._ATN, this); 71 | } 72 | 73 | // @Override 74 | public get grammarFileName(): string { return "ElementSelector.g4"; } 75 | 76 | // @Override 77 | public get ruleNames(): string[] { return ElementSelectorLexer.ruleNames; } 78 | 79 | // @Override 80 | public get serializedATN(): string { return ElementSelectorLexer._serializedATN; } 81 | 82 | // @Override 83 | public get channelNames(): string[] { return ElementSelectorLexer.channelNames; } 84 | 85 | // @Override 86 | public get modeNames(): string[] { return ElementSelectorLexer.modeNames; } 87 | 88 | public static readonly _serializedATN: string = 89 | "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x02\x10i\b\x01\x04" + 90 | "\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06\x04" + 91 | "\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r\t\r" + 92 | "\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t\x12" + 93 | "\x04\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x03\x02\x03\x02\x03\x03\x03" + 94 | "\x03\x03\x04\x03\x04\x03\x05\x03\x05\x03\x06\x03\x06\x03\x07\x03\x07\x03" + 95 | "\b\x03\b\x03\t\x03\t\x03\t\x03\n\x03\n\x03\v\x03\v\x03\f\x03\f\x03\r\x03" + 96 | "\r\x03\x0E\x03\x0E\x05\x0EG\n\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x10\x03" + 97 | "\x10\x03\x10\x03\x10\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x07" + 98 | "\x11V\n\x11\f\x11\x0E\x11Y\v\x11\x03\x11\x03\x11\x03\x11\x03\x12\x03\x12" + 99 | "\x03\x12\x03\x13\x03\x13\x03\x13\x03\x14\x03\x14\x05\x14f\n\x14\x03\x15" + 100 | "\x03\x15\x02\x02\x02\x16\x03\x02\x03\x05\x02\x04\x07\x02\x05\t\x02\x06" + 101 | "\v\x02\x07\r\x02\b\x0F\x02\t\x11\x02\n\x13\x02\v\x15\x02\x02\x17\x02\x02" + 102 | "\x19\x02\x02\x1B\x02\x02\x1D\x02\x02\x1F\x02\x02!\x02\f#\x02\r%\x02\x0E" + 103 | "\'\x02\x0F)\x02\x10\x03\x02\x05\x04\x02C\\c|\x06\x02\"1\x03\x02\x02\x02\x17@\x03\x02\x02\x02\x19B\x03\x02" + 112 | "\x02\x02\x1BF\x03\x02\x02\x02\x1DH\x03\x02\x02\x02\x1FK\x03\x02\x02\x02" + 113 | "!O\x03\x02\x02\x02#]\x03\x02\x02\x02%`\x03\x02\x02\x02\'e\x03\x02\x02" + 114 | "\x02)g\x03\x02\x02\x02+,\x07\x80\x02\x02,\x04\x03\x02\x02\x02-.\x07A\x02" + 115 | "\x02.\x06\x03\x02\x02\x02/0\x07<\x02\x020\b\x03\x02\x02\x0212\x07,\x02" + 116 | "\x022\n\x03\x02\x02\x0234\x07#\x02\x024\f\x03\x02\x02\x0256\x07`\x02\x02" + 117 | "6\x0E\x03\x02\x02\x0278\x07/\x02\x028\x10\x03\x02\x02\x029:\x07J\x02\x02" + 118 | ":;\x07N\x02\x02;\x12\x03\x02\x02\x02<=\x07-\x02\x02=\x14\x03\x02\x02\x02" + 119 | ">?\x042;\x02?\x16\x03\x02\x02\x02@A\t\x02\x02\x02A\x18\x03\x02\x02\x02" + 120 | "BC\t\x03\x02\x02C\x1A\x03\x02\x02\x02DG\x05\x15\v\x02EG\x05\x17\f\x02" + 121 | "FD\x03\x02\x02\x02FE\x03\x02\x02\x02G\x1C\x03\x02\x02\x02HI\x05\x17\f" + 122 | "\x02IJ\x05\x1B\x0E\x02J\x1E\x03\x02\x02\x02KL\x05\x17\f\x02LM\x05\x1B" + 123 | "\x0E\x02MN\x05\x1B\x0E\x02N \x03\x02\x02\x02OP\x07]\x02\x02PW\t\x04\x02" + 124 | "\x02QV\x05)\x15\x02RV\x05%\x13\x02SV\x05\'\x14\x02TV\x05#\x12\x02UQ\x03" + 125 | "\x02\x02\x02UR\x03\x02\x02\x02US\x03\x02\x02\x02UT\x03\x02\x02\x02VY\x03" + 126 | "\x02\x02\x02WU\x03\x02\x02\x02WX\x03\x02\x02\x02XZ\x03\x02\x02\x02YW\x03" + 127 | "\x02\x02\x02Z[\t\x04\x02\x02[\\\x07_\x02\x02\\\"\x03\x02\x02\x02]^\x05" + 128 | "\'\x14\x02^_\x05%\x13\x02_$\x03\x02\x02\x02`a\x05\x15\v\x02ab\x05\x15" + 129 | "\v\x02b&\x03\x02\x02\x02cf\x05\x1D\x0F\x02df\x05\x1F\x10\x02ec\x03\x02" + 130 | "\x02\x02ed\x03\x02\x02\x02f(\x03\x02\x02\x02gh\x04\x02\x01\x02h*\x03\x02" + 131 | "\x02\x02\x07\x02FUWe\x02"; 132 | public static __ATN: ATN; 133 | public static get _ATN(): ATN { 134 | if (!ElementSelectorLexer.__ATN) { 135 | ElementSelectorLexer.__ATN = new ATNDeserializer().deserialize(Utils.toCharArray(ElementSelectorLexer._serializedATN)); 136 | } 137 | 138 | return ElementSelectorLexer.__ATN; 139 | } 140 | 141 | } 142 | 143 | -------------------------------------------------------------------------------- /packages/fact/src/FactBaseLexer.ts: -------------------------------------------------------------------------------- 1 | import { BaseLexer, ControlChar, EdiOptions } from '@js-edi/shared' 2 | 3 | /** Positions of EDIFACT delimiters in a UNA segment. */ 4 | enum UNAPos { 5 | ComponentSeparator = 4, 6 | DataSeparator, 7 | DecimalMark, 8 | ReleaseIndicator, 9 | RepititionSeparator, 10 | SegmentTerminator, 11 | EndOfLine 12 | } 13 | 14 | export abstract class FactBaseLexer extends BaseLexer { 15 | private _encounteredUNA: boolean 16 | 17 | /** Has the lexer encountered an UNA segment? */ 18 | get encounteredUNA (): boolean { 19 | return typeof this._encounteredUNA === 'boolean' 20 | ? this._encounteredUNA 21 | : false 22 | } 23 | 24 | /** Is the current right-hand lexer position a component separator definition? */ 25 | get isComponentSep (): boolean { 26 | return this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.ComponentSeparator 27 | } 28 | 29 | /** Is the current right-hand lexer position a data separator definition? */ 30 | get isDataSep (): boolean { 31 | return this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.DataSeparator 32 | } 33 | 34 | /** Is the current right-hand lexer position a decimal mark definition? */ 35 | get isDecimalMark (): boolean { 36 | return this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.DecimalMark 37 | } 38 | 39 | /** Is the current right-hand lexer position a release indicator definition? */ 40 | get isReleaseIndicator (): boolean { 41 | return this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.ReleaseIndicator 42 | } 43 | 44 | /** Is the current right-hand lexer position a repitition separator definition? */ 45 | get isRepititionSep (): boolean { 46 | return this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.RepititionSeparator 47 | } 48 | 49 | /** Is the current right-hand lexer position a segement terminator definition? */ 50 | get isSegmentTerm (): boolean { 51 | return this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.SegmentTerminator 52 | } 53 | 54 | /** Is the current right-hand lexer position an end-of-line definition? */ 55 | get isEndOfLine (): boolean { 56 | const isCr = this.encounteredUNA && this.line === 1 && this.charPositionInLine === UNAPos.EndOfLine 57 | const isNl = this.encounteredUNA && this.line === 2 && this.charPositionInLine === 0 58 | 59 | return isCr || isNl 60 | } 61 | 62 | /** Is the current right-hand lexer position a control character definition? */ 63 | get isCtlCharPos (): boolean { 64 | return this.isComponentSep || 65 | this.isDataSep || 66 | this.isDecimalMark || 67 | this.isReleaseIndicator || 68 | this.isRepititionSep || 69 | this.isSegmentTerm || 70 | this.isEndOfLine 71 | } 72 | 73 | /** Handles UNA segments encountered by the lexer. */ 74 | handleUNA (): void { 75 | if (this.text === 'UNA' && this.line === 1 && this.charPositionInLine === 3) { 76 | this._encounteredUNA = true 77 | } 78 | } 79 | 80 | /** Handles control characters encountered by the lexer. */ 81 | handleControlChars (lexer: Record): void { 82 | const charType = this.ControlCharMap.get(this.text) 83 | const charTypeHandler = (): void => { 84 | if (charType in ControlChar) { 85 | this.type = lexer[charType] 86 | } else { 87 | this.type = lexer.Char 88 | } 89 | } 90 | 91 | switch (true) { 92 | case !this.encounteredUNA: 93 | charTypeHandler() 94 | break 95 | case this.isComponentSep: 96 | this.ControlCharMap.set(this.text, ControlChar.ComponentSeparator) 97 | break 98 | case this.isDataSep: 99 | this.ControlCharMap.set(this.text, ControlChar.DataSeparator) 100 | break 101 | case this.isDecimalMark: 102 | this.ControlCharMap.set(this.text, ControlChar.DataSeparator) 103 | break 104 | case this.isReleaseIndicator: 105 | this.ControlCharMap.set(this.text, ControlChar.DataSeparator) 106 | break 107 | case this.isRepititionSep: 108 | this.ControlCharMap.set(this.text, ControlChar.RepititionSeparator) 109 | break 110 | case this.isSegmentTerm: 111 | this.ControlCharMap.set(this.text, ControlChar.SegmentTerminator) 112 | break 113 | case this.isEndOfLine: 114 | // Only pick this up if end of line is a real formatting mark. 115 | if (this.text === '\r\n' || this.text === '\r' || this.text === '\n') { 116 | this.ControlCharMap.set(this.text, ControlChar.EndOfLine) 117 | } 118 | break 119 | default: 120 | charTypeHandler() 121 | break 122 | } 123 | } 124 | 125 | getOptions (): EdiOptions { 126 | const options: any = {} 127 | 128 | for (const [char, type] of this.ControlCharMap) { 129 | switch (type) { 130 | case ControlChar.ComponentSeparator: 131 | options.componentSeparator = char 132 | break 133 | case ControlChar.DataSeparator: 134 | options.dataSeparator = char 135 | break 136 | case ControlChar.EndOfLine: 137 | options.endOfLine = char 138 | break 139 | case ControlChar.RepititionSeparator: 140 | options.repititionSeparator = char 141 | break 142 | case ControlChar.SegmentTerminator: 143 | options.segmentTerminator = char 144 | break 145 | case ControlChar.DecimalMark: 146 | options.decimalMark = char as any 147 | break 148 | case ControlChar.ReleaseIndicator: 149 | options.releaseIndicator = char 150 | break 151 | } 152 | } 153 | 154 | return options 155 | } 156 | 157 | /** Set the options for EDIFACT parsing if no UNA segment is provided. UNA segments take precedence over provided options. */ 158 | setOptions (options: EdiOptions): void { 159 | const eolChars = '\r\n' 160 | const noEmptyString = (str: string): boolean => { 161 | return typeof str === 'string' && str.trim() !== '' 162 | } 163 | 164 | if (noEmptyString(options.componentSeparator)) { 165 | this.ControlCharMap.set(options.componentSeparator, ControlChar.ComponentSeparator) 166 | } 167 | 168 | if (noEmptyString(options.dataSeparator)) { 169 | this.ControlCharMap.set(options.dataSeparator, ControlChar.DataSeparator) 170 | } 171 | 172 | if (noEmptyString(options.decimalMark)) { 173 | this.ControlCharMap.set(options.decimalMark, ControlChar.DecimalMark) 174 | } 175 | 176 | if (noEmptyString(options.endOfLine) || eolChars.includes(options.endOfLine)) { 177 | this.ControlCharMap.set(options.endOfLine, ControlChar.EndOfLine) 178 | } 179 | 180 | if (noEmptyString(options.releaseIndicator)) { 181 | this.ControlCharMap.set(options.releaseIndicator, ControlChar.ReleaseIndicator) 182 | } 183 | 184 | if (noEmptyString(options.repititionSeparator)) { 185 | this.ControlCharMap.set(options.repititionSeparator, ControlChar.RepititionSeparator) 186 | } 187 | 188 | if (noEmptyString(options.segmentTerminator)) { 189 | this.ControlCharMap.set(options.segmentTerminator, ControlChar.SegmentTerminator) 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /packages/fact/src/EdiFactParserListener.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/fact/EdiFactParser.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener"; 5 | 6 | import { DocumentContext } from "./EdiFactParser"; 7 | import { InterchangeContext } from "./EdiFactParser"; 8 | import { GroupContext } from "./EdiFactParser"; 9 | import { MessageContext } from "./EdiFactParser"; 10 | import { SegmentContext } from "./EdiFactParser"; 11 | import { ServiceStringAdviceContext } from "./EdiFactParser"; 12 | import { InterchangeHeaderContext } from "./EdiFactParser"; 13 | import { InterchangeTrailerContext } from "./EdiFactParser"; 14 | import { GroupHeaderContext } from "./EdiFactParser"; 15 | import { GroupTrailerContext } from "./EdiFactParser"; 16 | import { MessageHeaderContext } from "./EdiFactParser"; 17 | import { MessageTrailerContext } from "./EdiFactParser"; 18 | import { SegmentEndContext } from "./EdiFactParser"; 19 | import { ElementContext } from "./EdiFactParser"; 20 | import { RepititionContext } from "./EdiFactParser"; 21 | import { ComponentContext } from "./EdiFactParser"; 22 | import { ValueContext } from "./EdiFactParser"; 23 | 24 | 25 | /** 26 | * This interface defines a complete listener for a parse tree produced by 27 | * `EdiFactParser`. 28 | */ 29 | export interface EdiFactParserListener extends ParseTreeListener { 30 | /** 31 | * Enter a parse tree produced by `EdiFactParser.document`. 32 | * @param ctx the parse tree 33 | */ 34 | enterDocument?: (ctx: DocumentContext) => void; 35 | /** 36 | * Exit a parse tree produced by `EdiFactParser.document`. 37 | * @param ctx the parse tree 38 | */ 39 | exitDocument?: (ctx: DocumentContext) => void; 40 | 41 | /** 42 | * Enter a parse tree produced by `EdiFactParser.interchange`. 43 | * @param ctx the parse tree 44 | */ 45 | enterInterchange?: (ctx: InterchangeContext) => void; 46 | /** 47 | * Exit a parse tree produced by `EdiFactParser.interchange`. 48 | * @param ctx the parse tree 49 | */ 50 | exitInterchange?: (ctx: InterchangeContext) => void; 51 | 52 | /** 53 | * Enter a parse tree produced by `EdiFactParser.group`. 54 | * @param ctx the parse tree 55 | */ 56 | enterGroup?: (ctx: GroupContext) => void; 57 | /** 58 | * Exit a parse tree produced by `EdiFactParser.group`. 59 | * @param ctx the parse tree 60 | */ 61 | exitGroup?: (ctx: GroupContext) => void; 62 | 63 | /** 64 | * Enter a parse tree produced by `EdiFactParser.message`. 65 | * @param ctx the parse tree 66 | */ 67 | enterMessage?: (ctx: MessageContext) => void; 68 | /** 69 | * Exit a parse tree produced by `EdiFactParser.message`. 70 | * @param ctx the parse tree 71 | */ 72 | exitMessage?: (ctx: MessageContext) => void; 73 | 74 | /** 75 | * Enter a parse tree produced by `EdiFactParser.segment`. 76 | * @param ctx the parse tree 77 | */ 78 | enterSegment?: (ctx: SegmentContext) => void; 79 | /** 80 | * Exit a parse tree produced by `EdiFactParser.segment`. 81 | * @param ctx the parse tree 82 | */ 83 | exitSegment?: (ctx: SegmentContext) => void; 84 | 85 | /** 86 | * Enter a parse tree produced by `EdiFactParser.serviceStringAdvice`. 87 | * @param ctx the parse tree 88 | */ 89 | enterServiceStringAdvice?: (ctx: ServiceStringAdviceContext) => void; 90 | /** 91 | * Exit a parse tree produced by `EdiFactParser.serviceStringAdvice`. 92 | * @param ctx the parse tree 93 | */ 94 | exitServiceStringAdvice?: (ctx: ServiceStringAdviceContext) => void; 95 | 96 | /** 97 | * Enter a parse tree produced by `EdiFactParser.interchangeHeader`. 98 | * @param ctx the parse tree 99 | */ 100 | enterInterchangeHeader?: (ctx: InterchangeHeaderContext) => void; 101 | /** 102 | * Exit a parse tree produced by `EdiFactParser.interchangeHeader`. 103 | * @param ctx the parse tree 104 | */ 105 | exitInterchangeHeader?: (ctx: InterchangeHeaderContext) => void; 106 | 107 | /** 108 | * Enter a parse tree produced by `EdiFactParser.interchangeTrailer`. 109 | * @param ctx the parse tree 110 | */ 111 | enterInterchangeTrailer?: (ctx: InterchangeTrailerContext) => void; 112 | /** 113 | * Exit a parse tree produced by `EdiFactParser.interchangeTrailer`. 114 | * @param ctx the parse tree 115 | */ 116 | exitInterchangeTrailer?: (ctx: InterchangeTrailerContext) => void; 117 | 118 | /** 119 | * Enter a parse tree produced by `EdiFactParser.groupHeader`. 120 | * @param ctx the parse tree 121 | */ 122 | enterGroupHeader?: (ctx: GroupHeaderContext) => void; 123 | /** 124 | * Exit a parse tree produced by `EdiFactParser.groupHeader`. 125 | * @param ctx the parse tree 126 | */ 127 | exitGroupHeader?: (ctx: GroupHeaderContext) => void; 128 | 129 | /** 130 | * Enter a parse tree produced by `EdiFactParser.groupTrailer`. 131 | * @param ctx the parse tree 132 | */ 133 | enterGroupTrailer?: (ctx: GroupTrailerContext) => void; 134 | /** 135 | * Exit a parse tree produced by `EdiFactParser.groupTrailer`. 136 | * @param ctx the parse tree 137 | */ 138 | exitGroupTrailer?: (ctx: GroupTrailerContext) => void; 139 | 140 | /** 141 | * Enter a parse tree produced by `EdiFactParser.messageHeader`. 142 | * @param ctx the parse tree 143 | */ 144 | enterMessageHeader?: (ctx: MessageHeaderContext) => void; 145 | /** 146 | * Exit a parse tree produced by `EdiFactParser.messageHeader`. 147 | * @param ctx the parse tree 148 | */ 149 | exitMessageHeader?: (ctx: MessageHeaderContext) => void; 150 | 151 | /** 152 | * Enter a parse tree produced by `EdiFactParser.messageTrailer`. 153 | * @param ctx the parse tree 154 | */ 155 | enterMessageTrailer?: (ctx: MessageTrailerContext) => void; 156 | /** 157 | * Exit a parse tree produced by `EdiFactParser.messageTrailer`. 158 | * @param ctx the parse tree 159 | */ 160 | exitMessageTrailer?: (ctx: MessageTrailerContext) => void; 161 | 162 | /** 163 | * Enter a parse tree produced by `EdiFactParser.segmentEnd`. 164 | * @param ctx the parse tree 165 | */ 166 | enterSegmentEnd?: (ctx: SegmentEndContext) => void; 167 | /** 168 | * Exit a parse tree produced by `EdiFactParser.segmentEnd`. 169 | * @param ctx the parse tree 170 | */ 171 | exitSegmentEnd?: (ctx: SegmentEndContext) => void; 172 | 173 | /** 174 | * Enter a parse tree produced by `EdiFactParser.element`. 175 | * @param ctx the parse tree 176 | */ 177 | enterElement?: (ctx: ElementContext) => void; 178 | /** 179 | * Exit a parse tree produced by `EdiFactParser.element`. 180 | * @param ctx the parse tree 181 | */ 182 | exitElement?: (ctx: ElementContext) => void; 183 | 184 | /** 185 | * Enter a parse tree produced by `EdiFactParser.repitition`. 186 | * @param ctx the parse tree 187 | */ 188 | enterRepitition?: (ctx: RepititionContext) => void; 189 | /** 190 | * Exit a parse tree produced by `EdiFactParser.repitition`. 191 | * @param ctx the parse tree 192 | */ 193 | exitRepitition?: (ctx: RepititionContext) => void; 194 | 195 | /** 196 | * Enter a parse tree produced by `EdiFactParser.component`. 197 | * @param ctx the parse tree 198 | */ 199 | enterComponent?: (ctx: ComponentContext) => void; 200 | /** 201 | * Exit a parse tree produced by `EdiFactParser.component`. 202 | * @param ctx the parse tree 203 | */ 204 | exitComponent?: (ctx: ComponentContext) => void; 205 | 206 | /** 207 | * Enter a parse tree produced by `EdiFactParser.value`. 208 | * @param ctx the parse tree 209 | */ 210 | enterValue?: (ctx: ValueContext) => void; 211 | /** 212 | * Exit a parse tree produced by `EdiFactParser.value`. 213 | * @param ctx the parse tree 214 | */ 215 | exitValue?: (ctx: ValueContext) => void; 216 | } 217 | 218 | -------------------------------------------------------------------------------- /packages/validator/src/edi-dom.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "definitions": { 4 | "options": { 5 | "type": "object", 6 | "required": [ 7 | "dataSeparator", 8 | "componentSeparator", 9 | "segmentTerminator" 10 | ], 11 | "properties": { 12 | "componentSeparator": { 13 | "type": "string", 14 | "minLength": 1, 15 | "maxLength": 1 16 | }, 17 | "dataSeparator": { 18 | "type": "string", 19 | "minLength": 1, 20 | "maxLength": 1 21 | }, 22 | "decimalMark": { 23 | "type": "string", 24 | "minLength": 1, 25 | "maxLength": 1 26 | }, 27 | "releaseIndicator": { 28 | "type": "string", 29 | "minLength": 1, 30 | "maxLength": 1 31 | }, 32 | "repititionSeparator": { 33 | "type": "string", 34 | "minLength": 1, 35 | "maxLength": 1 36 | }, 37 | "segmentTerminator": { 38 | "type": "string", 39 | "minLength": 1, 40 | "maxLength": 1 41 | }, 42 | "endOfLine": { 43 | "type": "string", 44 | "minLength": 1, 45 | "maxLength": 2 46 | } 47 | }, 48 | "additionalProperties": true 49 | }, 50 | "root": { 51 | "type": "object", 52 | "required": [ 53 | "interchanges", 54 | "options" 55 | ], 56 | "properties": { 57 | "options": { 58 | "$ref": "#/definitions/options" 59 | }, 60 | "interchanges": { 61 | "type": "array", 62 | "minItems": 1, 63 | "items": { 64 | "$ref": "#/definitions/interchange" 65 | } 66 | } 67 | }, 68 | "additionalProperties": true 69 | }, 70 | "interchange": { 71 | "type": "object", 72 | "properties": { 73 | "header": { 74 | "type": "object", 75 | "$ref": "#/definitions/segment", 76 | "properties": { 77 | "tag": { 78 | "anyOf": [ 79 | { 80 | "const": "ISA" 81 | }, 82 | { 83 | "const": "UNB" 84 | } 85 | ] 86 | } 87 | } 88 | }, 89 | "trailer": { 90 | "type": "object", 91 | "$ref": "#/definitions/segment", 92 | "properties": { 93 | "tag": { 94 | "anyOf": [ 95 | { 96 | "const": "IEA" 97 | }, 98 | { 99 | "const": "UNZ" 100 | } 101 | ] 102 | } 103 | } 104 | } 105 | }, 106 | "anyOf": [ 107 | { 108 | "required": [ 109 | "header", 110 | "groups", 111 | "trailer" 112 | ], 113 | "properties": { 114 | "groups": { 115 | "type": "array", 116 | "minItems": 1, 117 | "items": { 118 | "$ref": "#/definitions/group" 119 | } 120 | } 121 | } 122 | }, 123 | { 124 | "required": [ 125 | "header", 126 | "messages", 127 | "trailer" 128 | ], 129 | "properties": { 130 | "messages": { 131 | "type": "array", 132 | "minItems": 1, 133 | "items": { 134 | "$ref": "#/definitions/message" 135 | } 136 | } 137 | } 138 | } 139 | ], 140 | "additionalProperties": true 141 | }, 142 | "group": { 143 | "type": "object", 144 | "required": [ 145 | "header", 146 | "messages", 147 | "trailer" 148 | ], 149 | "properties": { 150 | "header": { 151 | "type": "object", 152 | "$ref": "#/definitions/segment", 153 | "properties": { 154 | "tag": { 155 | "anyOf": [ 156 | { 157 | "const": "GS" 158 | }, 159 | { 160 | "const": "UNG" 161 | } 162 | ] 163 | } 164 | } 165 | }, 166 | "messages": { 167 | "type": "array", 168 | "minItems": 1, 169 | "items": { 170 | "$ref": "#/definitions/message" 171 | } 172 | }, 173 | "trailer": { 174 | "type": "object", 175 | "$ref": "#/definitions/segment", 176 | "properties": { 177 | "tag": { 178 | "anyOf": [ 179 | { 180 | "const": "GE" 181 | }, 182 | { 183 | "const": "UNE" 184 | } 185 | ] 186 | } 187 | } 188 | } 189 | }, 190 | "additionalProperties": true 191 | }, 192 | "message": { 193 | "type": "object", 194 | "required": [ 195 | "header", 196 | "segments", 197 | "trailer" 198 | ], 199 | "properties": { 200 | "header": { 201 | "type": "object", 202 | "$ref": "#/definitions/segment", 203 | "properties": { 204 | "tag": { 205 | "anyOf": [ 206 | { 207 | "const": "ST" 208 | }, 209 | { 210 | "const": "UNH" 211 | } 212 | ] 213 | } 214 | } 215 | }, 216 | "segments": { 217 | "type": "array", 218 | "minItems": 1, 219 | "items": { 220 | "$ref": "#/definitions/segment" 221 | } 222 | }, 223 | "trailer": { 224 | "type": "object", 225 | "$ref": "#/definitions/segment", 226 | "properties": { 227 | "tag": { 228 | "anyOf": [ 229 | { 230 | "const": "SE" 231 | }, 232 | { 233 | "const": "UNT" 234 | } 235 | ] 236 | } 237 | } 238 | } 239 | }, 240 | "additionalProperties": true 241 | }, 242 | "segment": { 243 | "type": "object", 244 | "required": [ 245 | "tag", 246 | "elements" 247 | ], 248 | "properties": { 249 | "tag": { 250 | "type": "string", 251 | "minLength": 2, 252 | "maxLength": 4 253 | }, 254 | "elements": { 255 | "type": "array", 256 | "maxItems": 99, 257 | "items": { 258 | "$ref": "#/definitions/element" 259 | } 260 | } 261 | }, 262 | "additionalProperties": true 263 | }, 264 | "element": { 265 | "type": "object", 266 | "required": [ 267 | "value" 268 | ], 269 | "properties": { 270 | "value": { 271 | "anyOf": [ 272 | { 273 | "$ref": "#/definitions/value" 274 | }, 275 | { 276 | "$ref": "#/definitions/component" 277 | }, 278 | { 279 | "$ref": "#/definitions/repeated" 280 | } 281 | ] 282 | } 283 | }, 284 | "additionalProperties": true 285 | }, 286 | "repeated": { 287 | "type": "object", 288 | "required": [ 289 | "repeats" 290 | ], 291 | "properties": { 292 | "repeats": { 293 | "type": "array", 294 | "minItems": 1, 295 | "items": { 296 | "anyOf": [ 297 | { 298 | "$ref": "#/definitions/value" 299 | }, 300 | { 301 | "$ref": "#/definitions/component" 302 | } 303 | ] 304 | } 305 | } 306 | }, 307 | "additionalProperties": true 308 | }, 309 | "component": { 310 | "type": "object", 311 | "required": [ 312 | "values" 313 | ], 314 | "properties": { 315 | "values": { 316 | "type": "array", 317 | "minItems": 1, 318 | "items": { 319 | "$ref": "#/definitions/value" 320 | } 321 | } 322 | }, 323 | "additionalProperties": true 324 | }, 325 | "value": { 326 | "type": "object", 327 | "required": [ 328 | "text" 329 | ], 330 | "properties": { 331 | "text": { 332 | "type": "string" 333 | } 334 | }, 335 | "additionalProperties": true 336 | } 337 | }, 338 | "$ref": "#/definitions/root" 339 | } 340 | -------------------------------------------------------------------------------- /packages/documents/src/EdiDomX12Listener.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EdiX12Lexer, 3 | RepititionCharElementContext 4 | } from '@js-edi/x12' 5 | import { 6 | EdiDomComponent, 7 | EdiDomElement, 8 | EdiDomGroup, 9 | EdiDomInterchange, 10 | EdiDomMessage, 11 | EdiDomRepeated, 12 | EdiDomRoot, 13 | EdiDomSegment, 14 | EdiDomValue 15 | } from '@js-edi/dom' 16 | import type { ErrorNode } from 'antlr4ts/tree' 17 | import type { 18 | ComponentCharElementContext, 19 | EdiX12ParserListener, 20 | ElementContext, 21 | GroupHeaderContext, 22 | GroupTrailerContext, 23 | InterchangeHeaderContext, 24 | InterchangeTrailerContext, 25 | RepeatedElementContext, 26 | RepititionContext, 27 | SegmentContext, 28 | StrictElementContext, 29 | TransactionHeaderContext, 30 | TransactionTrailerContext, 31 | ValueContext 32 | } from '@js-edi/x12' 33 | 34 | /** Produces an element which contains a component or a value, never a repeated element. */ 35 | function contextToElement (ctx: ElementContext | RepeatedElementContext): EdiDomElement { 36 | const domElement = new EdiDomElement() 37 | const ctxComponents = ctx.component() 38 | const ctxValues = ctx.value() 39 | 40 | if (Array.isArray(ctxComponents) && ctxComponents.length > 0) { 41 | const domComponent = new EdiDomComponent() 42 | 43 | domComponent.addChildNode(value(ctxValues)) 44 | 45 | for (const ctxComponent of ctxComponents) { 46 | domComponent.addChildNode(value(ctxComponent.value())) 47 | } 48 | 49 | domElement.addChildNode(domComponent) 50 | } else { 51 | domElement.addChildNode(value(ctxValues)) 52 | } 53 | 54 | return domElement 55 | } 56 | 57 | /** Converts a control char element from a parser context to a valid element in the dom. */ 58 | function controlCharElement (ctx: RepititionCharElementContext | ComponentCharElementContext): EdiDomValue { 59 | const domValue = new EdiDomValue() 60 | 61 | if (ctx instanceof RepititionCharElementContext) { 62 | const ctlChar = ctx.ControlChar() 63 | const repChar = ctx.RepititionSeparator() 64 | const valChar = ctx.value() 65 | 66 | if (typeof ctlChar === 'object' && typeof ctlChar.text === 'string') { 67 | domValue.text = ctlChar.text 68 | } else if (typeof repChar === 'object' && typeof repChar.text === 'string') { 69 | domValue.text = repChar.text 70 | } else { 71 | return value([valChar]) 72 | } 73 | } else { 74 | const ctlChar = ctx.ControlChar() 75 | const repChar = ctx.ComponentSeparator() 76 | 77 | if (typeof ctlChar === 'object' && typeof ctlChar.text === 'string') { 78 | domValue.text = ctlChar.text 79 | } else if (typeof repChar === 'object' && typeof repChar.text === 'string') { 80 | domValue.text = repChar.text 81 | } 82 | } 83 | 84 | return domValue 85 | } 86 | 87 | /** Produces a valid segment in the dom from a parser strict element context. */ 88 | function strictSegment (tag: T, ctxElements: StrictElementContext[]): EdiDomSegment { 89 | const domSegment = new EdiDomSegment(tag) 90 | 91 | if (Array.isArray(ctxElements)) { 92 | for (const ctxElement of ctxElements) { 93 | const domElement = new EdiDomElement() 94 | 95 | domElement.addChildNode(value(ctxElement.value())) 96 | domSegment.addChildNode(domElement) 97 | } 98 | } 99 | 100 | return domSegment 101 | } 102 | 103 | /** Safely convert a ParseTree value[] to a string value. */ 104 | function value (ctxValue: ValueContext[]): EdiDomValue { 105 | const domValue = new EdiDomValue() 106 | 107 | if (Array.isArray(ctxValue)) { 108 | domValue.text = ctxValue.map((ctx: ValueContext) => { 109 | if (typeof ctx === 'object' && typeof ctx.text === 'string') { 110 | return ctx.text 111 | } 112 | 113 | return '' 114 | }).join('') 115 | } 116 | 117 | return domValue 118 | } 119 | 120 | export class EdiDomX12Listener implements EdiX12ParserListener { 121 | constructor () { 122 | this.root = new EdiDomRoot() 123 | } 124 | 125 | /** The document root. */ 126 | root: EdiDomRoot 127 | /** The current interchange being constructed. */ 128 | private interchange: EdiDomInterchange 129 | /** The current group being constructed. */ 130 | private group: EdiDomGroup 131 | /** The current transaction being constructed. */ 132 | private message: EdiDomMessage 133 | /** The current segment being constructed. */ 134 | private segment: EdiDomSegment 135 | /** The current repitition being constructed. */ 136 | private repitition?: EdiDomRepeated 137 | 138 | /** Deal with errors as encountered. */ 139 | visitErrorNode (node: ErrorNode): void {} 140 | 141 | /** Set the current interchange and add it to the root. */ 142 | enterInterchange (): void { 143 | this.interchange = new EdiDomInterchange() 144 | this.root.addChildNode(this.interchange) 145 | } 146 | 147 | /** Set the current group and add it to the current interchange. */ 148 | enterGroup (): void { 149 | this.group = new EdiDomGroup() 150 | this.interchange.addChildNode(this.group) 151 | } 152 | 153 | /** Set the current message and add it to the current group. */ 154 | enterTransaction (): void { 155 | this.message = new EdiDomMessage() 156 | this.group.addChildNode(this.message) 157 | } 158 | 159 | /** Set the current segment and add it to the current message. */ 160 | enterSegment (): void { 161 | this.segment = new EdiDomSegment() 162 | this.message.addChildNode(this.segment) 163 | } 164 | 165 | exitSegment (ctx: SegmentContext): void { 166 | this.segment.tag = ctx.Tag().text 167 | } 168 | 169 | enterRepitition (): void { 170 | const element = new EdiDomElement() 171 | this.repitition = new EdiDomRepeated() 172 | 173 | element.addChildNode(this.repitition) 174 | this.segment.addChildNode(element) 175 | } 176 | 177 | exitRepitition (ctx: RepititionContext): void { 178 | const repitition = this.repitition ?? new EdiDomRepeated() 179 | const ctxRepititions = ctx.repeatedElement() 180 | 181 | if (Array.isArray(ctxRepititions) && ctxRepititions.length > 0) { 182 | for (const ctxRepitition of ctxRepititions) { 183 | const subElement = contextToElement(ctxRepitition) 184 | 185 | repitition.addChildNode(subElement.value) 186 | } 187 | } 188 | 189 | this.repitition = undefined 190 | } 191 | 192 | exitElement (ctx: ElementContext): void { 193 | const element = contextToElement(ctx) 194 | 195 | if (typeof this.repitition === 'object') { 196 | this.repitition.addChildNode(element.value) 197 | } else { 198 | this.segment.addChildNode(element) 199 | } 200 | } 201 | 202 | /** Get the discovered options and the ISA header for the current interchange. */ 203 | exitInterchangeHeader (ctx: InterchangeHeaderContext): void { 204 | const ctxElements = ctx.interchangeElement() 205 | const { tokenSource } = ctx.start 206 | this.interchange.header = new EdiDomSegment('ISA') 207 | 208 | if (tokenSource instanceof EdiX12Lexer) { 209 | this.root.options = tokenSource.getOptions() 210 | } 211 | 212 | if (Array.isArray(ctxElements)) { 213 | for (const ctxElement of ctxElements) { 214 | const element = new EdiDomElement() 215 | 216 | switch ('object') { 217 | case (typeof ctxElement.strictElement()): 218 | element.addChildNode(value(ctxElement.strictElement().value())) 219 | break 220 | case (typeof ctxElement.dataCharElement()): 221 | element.addChildNode(value(ctxElement.dataCharElement().value())) 222 | break 223 | case (typeof ctxElement.componentCharElement()): 224 | element.addChildNode(controlCharElement(ctxElement.componentCharElement())) 225 | break 226 | case (typeof ctxElement.repititionCharElement()): 227 | element.addChildNode(controlCharElement(ctxElement.repititionCharElement())) 228 | break 229 | } 230 | 231 | this.interchange.header.addChildNode(element) 232 | } 233 | } 234 | } 235 | 236 | /** Constructs the interchange trailer. */ 237 | exitInterchangeTrailer (ctx: InterchangeTrailerContext): void { 238 | this.interchange.trailer = strictSegment('IEA', ctx.strictElement()) 239 | } 240 | 241 | /** Constructs the group header. */ 242 | exitGroupHeader (ctx: GroupHeaderContext): void { 243 | this.group.header = strictSegment('GS', ctx.strictElement()) 244 | } 245 | 246 | /** Constructs the group trailer. */ 247 | exitGroupTrailer (ctx: GroupTrailerContext): void { 248 | this.group.trailer = strictSegment('GE', ctx.strictElement()) 249 | } 250 | 251 | /** Constructs the transaction header. */ 252 | exitTransactionHeader (ctx: TransactionHeaderContext): void { 253 | this.message.header = strictSegment('ST', ctx.strictElement()) 254 | } 255 | 256 | /** Constructs the transaction trailer. */ 257 | exitTransactionTrailer (ctx: TransactionTrailerContext): void { 258 | this.message.trailer = strictSegment('SE', ctx.strictElement()) 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /packages/x12/src/EdiX12ParserListener.ts: -------------------------------------------------------------------------------- 1 | // Generated from grammars/x12/EdiX12Parser.g4 by ANTLR 4.9.0-SNAPSHOT 2 | 3 | 4 | import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener"; 5 | 6 | import { DocumentContext } from "./EdiX12Parser"; 7 | import { InterchangeContext } from "./EdiX12Parser"; 8 | import { GroupContext } from "./EdiX12Parser"; 9 | import { TransactionContext } from "./EdiX12Parser"; 10 | import { SegmentContext } from "./EdiX12Parser"; 11 | import { InterchangeHeaderContext } from "./EdiX12Parser"; 12 | import { InterchangeTrailerContext } from "./EdiX12Parser"; 13 | import { GroupHeaderContext } from "./EdiX12Parser"; 14 | import { GroupTrailerContext } from "./EdiX12Parser"; 15 | import { TransactionHeaderContext } from "./EdiX12Parser"; 16 | import { TransactionTrailerContext } from "./EdiX12Parser"; 17 | import { SegmentEndContext } from "./EdiX12Parser"; 18 | import { RepititionContext } from "./EdiX12Parser"; 19 | import { ElementContext } from "./EdiX12Parser"; 20 | import { InterchangeElementContext } from "./EdiX12Parser"; 21 | import { StrictElementContext } from "./EdiX12Parser"; 22 | import { DataCharElementContext } from "./EdiX12Parser"; 23 | import { RepititionCharElementContext } from "./EdiX12Parser"; 24 | import { ComponentCharElementContext } from "./EdiX12Parser"; 25 | import { RepeatedElementContext } from "./EdiX12Parser"; 26 | import { ComponentContext } from "./EdiX12Parser"; 27 | import { ValueContext } from "./EdiX12Parser"; 28 | 29 | 30 | /** 31 | * This interface defines a complete listener for a parse tree produced by 32 | * `EdiX12Parser`. 33 | */ 34 | export interface EdiX12ParserListener extends ParseTreeListener { 35 | /** 36 | * Enter a parse tree produced by `EdiX12Parser.document`. 37 | * @param ctx the parse tree 38 | */ 39 | enterDocument?: (ctx: DocumentContext) => void; 40 | /** 41 | * Exit a parse tree produced by `EdiX12Parser.document`. 42 | * @param ctx the parse tree 43 | */ 44 | exitDocument?: (ctx: DocumentContext) => void; 45 | 46 | /** 47 | * Enter a parse tree produced by `EdiX12Parser.interchange`. 48 | * @param ctx the parse tree 49 | */ 50 | enterInterchange?: (ctx: InterchangeContext) => void; 51 | /** 52 | * Exit a parse tree produced by `EdiX12Parser.interchange`. 53 | * @param ctx the parse tree 54 | */ 55 | exitInterchange?: (ctx: InterchangeContext) => void; 56 | 57 | /** 58 | * Enter a parse tree produced by `EdiX12Parser.group`. 59 | * @param ctx the parse tree 60 | */ 61 | enterGroup?: (ctx: GroupContext) => void; 62 | /** 63 | * Exit a parse tree produced by `EdiX12Parser.group`. 64 | * @param ctx the parse tree 65 | */ 66 | exitGroup?: (ctx: GroupContext) => void; 67 | 68 | /** 69 | * Enter a parse tree produced by `EdiX12Parser.transaction`. 70 | * @param ctx the parse tree 71 | */ 72 | enterTransaction?: (ctx: TransactionContext) => void; 73 | /** 74 | * Exit a parse tree produced by `EdiX12Parser.transaction`. 75 | * @param ctx the parse tree 76 | */ 77 | exitTransaction?: (ctx: TransactionContext) => void; 78 | 79 | /** 80 | * Enter a parse tree produced by `EdiX12Parser.segment`. 81 | * @param ctx the parse tree 82 | */ 83 | enterSegment?: (ctx: SegmentContext) => void; 84 | /** 85 | * Exit a parse tree produced by `EdiX12Parser.segment`. 86 | * @param ctx the parse tree 87 | */ 88 | exitSegment?: (ctx: SegmentContext) => void; 89 | 90 | /** 91 | * Enter a parse tree produced by `EdiX12Parser.interchangeHeader`. 92 | * @param ctx the parse tree 93 | */ 94 | enterInterchangeHeader?: (ctx: InterchangeHeaderContext) => void; 95 | /** 96 | * Exit a parse tree produced by `EdiX12Parser.interchangeHeader`. 97 | * @param ctx the parse tree 98 | */ 99 | exitInterchangeHeader?: (ctx: InterchangeHeaderContext) => void; 100 | 101 | /** 102 | * Enter a parse tree produced by `EdiX12Parser.interchangeTrailer`. 103 | * @param ctx the parse tree 104 | */ 105 | enterInterchangeTrailer?: (ctx: InterchangeTrailerContext) => void; 106 | /** 107 | * Exit a parse tree produced by `EdiX12Parser.interchangeTrailer`. 108 | * @param ctx the parse tree 109 | */ 110 | exitInterchangeTrailer?: (ctx: InterchangeTrailerContext) => void; 111 | 112 | /** 113 | * Enter a parse tree produced by `EdiX12Parser.groupHeader`. 114 | * @param ctx the parse tree 115 | */ 116 | enterGroupHeader?: (ctx: GroupHeaderContext) => void; 117 | /** 118 | * Exit a parse tree produced by `EdiX12Parser.groupHeader`. 119 | * @param ctx the parse tree 120 | */ 121 | exitGroupHeader?: (ctx: GroupHeaderContext) => void; 122 | 123 | /** 124 | * Enter a parse tree produced by `EdiX12Parser.groupTrailer`. 125 | * @param ctx the parse tree 126 | */ 127 | enterGroupTrailer?: (ctx: GroupTrailerContext) => void; 128 | /** 129 | * Exit a parse tree produced by `EdiX12Parser.groupTrailer`. 130 | * @param ctx the parse tree 131 | */ 132 | exitGroupTrailer?: (ctx: GroupTrailerContext) => void; 133 | 134 | /** 135 | * Enter a parse tree produced by `EdiX12Parser.transactionHeader`. 136 | * @param ctx the parse tree 137 | */ 138 | enterTransactionHeader?: (ctx: TransactionHeaderContext) => void; 139 | /** 140 | * Exit a parse tree produced by `EdiX12Parser.transactionHeader`. 141 | * @param ctx the parse tree 142 | */ 143 | exitTransactionHeader?: (ctx: TransactionHeaderContext) => void; 144 | 145 | /** 146 | * Enter a parse tree produced by `EdiX12Parser.transactionTrailer`. 147 | * @param ctx the parse tree 148 | */ 149 | enterTransactionTrailer?: (ctx: TransactionTrailerContext) => void; 150 | /** 151 | * Exit a parse tree produced by `EdiX12Parser.transactionTrailer`. 152 | * @param ctx the parse tree 153 | */ 154 | exitTransactionTrailer?: (ctx: TransactionTrailerContext) => void; 155 | 156 | /** 157 | * Enter a parse tree produced by `EdiX12Parser.segmentEnd`. 158 | * @param ctx the parse tree 159 | */ 160 | enterSegmentEnd?: (ctx: SegmentEndContext) => void; 161 | /** 162 | * Exit a parse tree produced by `EdiX12Parser.segmentEnd`. 163 | * @param ctx the parse tree 164 | */ 165 | exitSegmentEnd?: (ctx: SegmentEndContext) => void; 166 | 167 | /** 168 | * Enter a parse tree produced by `EdiX12Parser.repitition`. 169 | * @param ctx the parse tree 170 | */ 171 | enterRepitition?: (ctx: RepititionContext) => void; 172 | /** 173 | * Exit a parse tree produced by `EdiX12Parser.repitition`. 174 | * @param ctx the parse tree 175 | */ 176 | exitRepitition?: (ctx: RepititionContext) => void; 177 | 178 | /** 179 | * Enter a parse tree produced by `EdiX12Parser.element`. 180 | * @param ctx the parse tree 181 | */ 182 | enterElement?: (ctx: ElementContext) => void; 183 | /** 184 | * Exit a parse tree produced by `EdiX12Parser.element`. 185 | * @param ctx the parse tree 186 | */ 187 | exitElement?: (ctx: ElementContext) => void; 188 | 189 | /** 190 | * Enter a parse tree produced by `EdiX12Parser.interchangeElement`. 191 | * @param ctx the parse tree 192 | */ 193 | enterInterchangeElement?: (ctx: InterchangeElementContext) => void; 194 | /** 195 | * Exit a parse tree produced by `EdiX12Parser.interchangeElement`. 196 | * @param ctx the parse tree 197 | */ 198 | exitInterchangeElement?: (ctx: InterchangeElementContext) => void; 199 | 200 | /** 201 | * Enter a parse tree produced by `EdiX12Parser.strictElement`. 202 | * @param ctx the parse tree 203 | */ 204 | enterStrictElement?: (ctx: StrictElementContext) => void; 205 | /** 206 | * Exit a parse tree produced by `EdiX12Parser.strictElement`. 207 | * @param ctx the parse tree 208 | */ 209 | exitStrictElement?: (ctx: StrictElementContext) => void; 210 | 211 | /** 212 | * Enter a parse tree produced by `EdiX12Parser.dataCharElement`. 213 | * @param ctx the parse tree 214 | */ 215 | enterDataCharElement?: (ctx: DataCharElementContext) => void; 216 | /** 217 | * Exit a parse tree produced by `EdiX12Parser.dataCharElement`. 218 | * @param ctx the parse tree 219 | */ 220 | exitDataCharElement?: (ctx: DataCharElementContext) => void; 221 | 222 | /** 223 | * Enter a parse tree produced by `EdiX12Parser.repititionCharElement`. 224 | * @param ctx the parse tree 225 | */ 226 | enterRepititionCharElement?: (ctx: RepititionCharElementContext) => void; 227 | /** 228 | * Exit a parse tree produced by `EdiX12Parser.repititionCharElement`. 229 | * @param ctx the parse tree 230 | */ 231 | exitRepititionCharElement?: (ctx: RepititionCharElementContext) => void; 232 | 233 | /** 234 | * Enter a parse tree produced by `EdiX12Parser.componentCharElement`. 235 | * @param ctx the parse tree 236 | */ 237 | enterComponentCharElement?: (ctx: ComponentCharElementContext) => void; 238 | /** 239 | * Exit a parse tree produced by `EdiX12Parser.componentCharElement`. 240 | * @param ctx the parse tree 241 | */ 242 | exitComponentCharElement?: (ctx: ComponentCharElementContext) => void; 243 | 244 | /** 245 | * Enter a parse tree produced by `EdiX12Parser.repeatedElement`. 246 | * @param ctx the parse tree 247 | */ 248 | enterRepeatedElement?: (ctx: RepeatedElementContext) => void; 249 | /** 250 | * Exit a parse tree produced by `EdiX12Parser.repeatedElement`. 251 | * @param ctx the parse tree 252 | */ 253 | exitRepeatedElement?: (ctx: RepeatedElementContext) => void; 254 | 255 | /** 256 | * Enter a parse tree produced by `EdiX12Parser.component`. 257 | * @param ctx the parse tree 258 | */ 259 | enterComponent?: (ctx: ComponentContext) => void; 260 | /** 261 | * Exit a parse tree produced by `EdiX12Parser.component`. 262 | * @param ctx the parse tree 263 | */ 264 | exitComponent?: (ctx: ComponentContext) => void; 265 | 266 | /** 267 | * Enter a parse tree produced by `EdiX12Parser.value`. 268 | * @param ctx the parse tree 269 | */ 270 | enterValue?: (ctx: ValueContext) => void; 271 | /** 272 | * Exit a parse tree produced by `EdiX12Parser.value`. 273 | * @param ctx the parse tree 274 | */ 275 | exitValue?: (ctx: ValueContext) => void; 276 | } 277 | 278 | --------------------------------------------------------------------------------