@rethinkhealth/hl7v2-parser 0.2.30 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +80 -50
- package/dist/index.js.map +1 -1
- package/dist/processor.d.ts.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -47,6 +47,34 @@ function runPreprocessors(ctx, steps) {
|
|
|
47
47
|
|
|
48
48
|
// src/processor.ts
|
|
49
49
|
import { isEmptyNode } from "@rethinkhealth/hl7v2-utils";
|
|
50
|
+
function createSubcomponent(start) {
|
|
51
|
+
return {
|
|
52
|
+
type: "subcomponent",
|
|
53
|
+
value: "",
|
|
54
|
+
position: { start, end: start }
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function createComponent(start) {
|
|
58
|
+
return {
|
|
59
|
+
type: "component",
|
|
60
|
+
children: [createSubcomponent(start)],
|
|
61
|
+
position: { start, end: start }
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createFieldRepetition(start) {
|
|
65
|
+
return {
|
|
66
|
+
type: "field-repetition",
|
|
67
|
+
children: [createComponent(start)],
|
|
68
|
+
position: { start, end: start }
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function createField(start) {
|
|
72
|
+
return {
|
|
73
|
+
type: "field",
|
|
74
|
+
children: [createFieldRepetition(start)],
|
|
75
|
+
position: { start, end: start }
|
|
76
|
+
};
|
|
77
|
+
}
|
|
50
78
|
function createParserCore(ctx) {
|
|
51
79
|
const root = {
|
|
52
80
|
type: "root",
|
|
@@ -62,8 +90,20 @@ function createParserCore(ctx) {
|
|
|
62
90
|
let currentSub = null;
|
|
63
91
|
let segmentHasContent = false;
|
|
64
92
|
let lastContentEnd = null;
|
|
93
|
+
let documentEnd = null;
|
|
65
94
|
let expectingSegmentName = true;
|
|
66
95
|
let justSetSegmentName = false;
|
|
96
|
+
let rootStartSet = false;
|
|
97
|
+
const resetState = () => {
|
|
98
|
+
seg = null;
|
|
99
|
+
field = null;
|
|
100
|
+
rep = null;
|
|
101
|
+
comp = null;
|
|
102
|
+
currentSub = null;
|
|
103
|
+
segmentHasContent = false;
|
|
104
|
+
lastContentEnd = null;
|
|
105
|
+
expectingSegmentName = true;
|
|
106
|
+
};
|
|
67
107
|
const openSegment = (name, position) => {
|
|
68
108
|
const header = {
|
|
69
109
|
type: "segment-header",
|
|
@@ -90,67 +130,53 @@ function createParserCore(ctx) {
|
|
|
90
130
|
"Cannot open field without an active segment. TEXT token with segment name must precede field content."
|
|
91
131
|
);
|
|
92
132
|
}
|
|
93
|
-
field =
|
|
133
|
+
field = createField(start);
|
|
94
134
|
seg.children.push(field);
|
|
95
|
-
rep =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
position: { start, end: start }
|
|
99
|
-
};
|
|
100
|
-
field.children.push(rep);
|
|
101
|
-
comp = { type: "component", children: [], position: { start, end: start } };
|
|
102
|
-
rep.children.push(comp);
|
|
103
|
-
currentSub = null;
|
|
135
|
+
rep = field.children[0];
|
|
136
|
+
comp = rep.children[0];
|
|
137
|
+
currentSub = comp.children[0];
|
|
104
138
|
segmentHasContent = true;
|
|
105
139
|
};
|
|
106
140
|
const openRepetition = (start) => {
|
|
107
141
|
if (!field) {
|
|
108
142
|
openField(start);
|
|
143
|
+
return;
|
|
109
144
|
}
|
|
110
|
-
rep =
|
|
111
|
-
type: "field-repetition",
|
|
112
|
-
children: [],
|
|
113
|
-
position: { start, end: start }
|
|
114
|
-
};
|
|
145
|
+
rep = createFieldRepetition(start);
|
|
115
146
|
field.children.push(rep);
|
|
116
|
-
comp =
|
|
117
|
-
|
|
118
|
-
currentSub = null;
|
|
147
|
+
comp = rep.children[0] ?? null;
|
|
148
|
+
currentSub = comp?.children[0] ?? null;
|
|
119
149
|
segmentHasContent = true;
|
|
120
150
|
};
|
|
121
151
|
const openComponent = (start) => {
|
|
122
152
|
if (!field) {
|
|
123
153
|
openField(start);
|
|
154
|
+
return;
|
|
124
155
|
}
|
|
125
156
|
if (!rep) {
|
|
126
|
-
rep =
|
|
127
|
-
type: "field-repetition",
|
|
128
|
-
children: [],
|
|
129
|
-
position: { start, end: start }
|
|
130
|
-
};
|
|
157
|
+
rep = createFieldRepetition(start);
|
|
131
158
|
field.children.push(rep);
|
|
132
159
|
}
|
|
133
|
-
comp =
|
|
160
|
+
comp = createComponent(start);
|
|
134
161
|
rep.children.push(comp);
|
|
135
|
-
currentSub = null;
|
|
162
|
+
currentSub = comp.children[0] ?? null;
|
|
136
163
|
segmentHasContent = true;
|
|
137
164
|
};
|
|
138
165
|
const ensureForText = (start) => {
|
|
139
166
|
if (!field) {
|
|
140
167
|
openField(start);
|
|
168
|
+
return;
|
|
141
169
|
}
|
|
142
170
|
if (!rep) {
|
|
143
171
|
openRepetition(start);
|
|
172
|
+
return;
|
|
144
173
|
}
|
|
145
174
|
if (!comp) {
|
|
146
175
|
openComponent(start);
|
|
176
|
+
return;
|
|
147
177
|
}
|
|
148
178
|
if (!currentSub) {
|
|
149
|
-
currentSub =
|
|
150
|
-
type: "subcomponent",
|
|
151
|
-
value: "",
|
|
152
|
-
position: { start, end: start }
|
|
153
|
-
};
|
|
179
|
+
currentSub = createSubcomponent(start);
|
|
154
180
|
comp.children.push(currentSub);
|
|
155
181
|
segmentHasContent = true;
|
|
156
182
|
}
|
|
@@ -178,36 +204,25 @@ function createParserCore(ctx) {
|
|
|
178
204
|
const endPos = lastContentEnd || tok.position.start;
|
|
179
205
|
updatePositionsToEnd(endPos);
|
|
180
206
|
dropTrailingEmptyFieldIfPresent();
|
|
181
|
-
|
|
182
|
-
field = null;
|
|
183
|
-
rep = null;
|
|
184
|
-
comp = null;
|
|
185
|
-
currentSub = null;
|
|
186
|
-
segmentHasContent = false;
|
|
187
|
-
lastContentEnd = null;
|
|
188
|
-
expectingSegmentName = true;
|
|
207
|
+
resetState();
|
|
189
208
|
return;
|
|
190
209
|
}
|
|
191
210
|
case "FIELD_DELIM": {
|
|
192
211
|
lastContentEnd = tok.position.end;
|
|
212
|
+
documentEnd = tok.position.end;
|
|
193
213
|
if (justSetSegmentName) {
|
|
194
214
|
justSetSegmentName = false;
|
|
195
215
|
return;
|
|
196
216
|
}
|
|
197
217
|
if (!field) {
|
|
198
218
|
openField(tok.position.start);
|
|
199
|
-
comp.children.push({
|
|
200
|
-
type: "subcomponent",
|
|
201
|
-
value: "",
|
|
202
|
-
position: { start: tok.position.start, end: tok.position.start }
|
|
203
|
-
});
|
|
204
|
-
segmentHasContent = true;
|
|
205
219
|
}
|
|
206
220
|
openField(tok.position.end);
|
|
207
221
|
return;
|
|
208
222
|
}
|
|
209
223
|
case "REPETITION_DELIM": {
|
|
210
224
|
lastContentEnd = tok.position.end;
|
|
225
|
+
documentEnd = tok.position.end;
|
|
211
226
|
if (!field) {
|
|
212
227
|
openField(tok.position.start);
|
|
213
228
|
}
|
|
@@ -216,34 +231,41 @@ function createParserCore(ctx) {
|
|
|
216
231
|
}
|
|
217
232
|
case "COMPONENT_DELIM": {
|
|
218
233
|
lastContentEnd = tok.position.end;
|
|
234
|
+
documentEnd = tok.position.end;
|
|
219
235
|
openComponent(tok.position.end);
|
|
220
236
|
return;
|
|
221
237
|
}
|
|
222
238
|
case "SUBCOMP_DELIM": {
|
|
223
239
|
lastContentEnd = tok.position.end;
|
|
240
|
+
documentEnd = tok.position.end;
|
|
224
241
|
if (!comp) {
|
|
225
242
|
openComponent(tok.position.start);
|
|
226
243
|
}
|
|
227
|
-
currentSub =
|
|
228
|
-
|
|
229
|
-
value: "",
|
|
230
|
-
position: { start: tok.position.end, end: tok.position.end }
|
|
231
|
-
};
|
|
232
|
-
comp.children.push(currentSub);
|
|
244
|
+
currentSub = createSubcomponent(tok.position.end);
|
|
245
|
+
comp?.children.push(currentSub);
|
|
233
246
|
segmentHasContent = true;
|
|
234
247
|
return;
|
|
235
248
|
}
|
|
236
249
|
case "TEXT": {
|
|
237
250
|
const val = tok.value ?? "";
|
|
251
|
+
if (!rootStartSet) {
|
|
252
|
+
root.position = {
|
|
253
|
+
start: tok.position.start,
|
|
254
|
+
end: tok.position.start
|
|
255
|
+
};
|
|
256
|
+
rootStartSet = true;
|
|
257
|
+
}
|
|
238
258
|
if (expectingSegmentName) {
|
|
239
259
|
openSegment(val, tok.position);
|
|
240
260
|
lastContentEnd = tok.position.end;
|
|
261
|
+
documentEnd = tok.position.end;
|
|
241
262
|
return;
|
|
242
263
|
}
|
|
243
264
|
ensureForText(tok.position.start);
|
|
244
265
|
currentSub.value += val;
|
|
245
266
|
updatePositionsToEnd(tok.position.end);
|
|
246
267
|
lastContentEnd = tok.position.end;
|
|
268
|
+
documentEnd = tok.position.end;
|
|
247
269
|
segmentHasContent = true;
|
|
248
270
|
return;
|
|
249
271
|
}
|
|
@@ -260,6 +282,14 @@ function createParserCore(ctx) {
|
|
|
260
282
|
if (seg && !segmentHasContent) {
|
|
261
283
|
root.children.pop();
|
|
262
284
|
}
|
|
285
|
+
if (root.position && documentEnd) {
|
|
286
|
+
root.position.end = documentEnd;
|
|
287
|
+
} else if (!root.position) {
|
|
288
|
+
root.position = {
|
|
289
|
+
start: { line: 1, column: 1, offset: 0 },
|
|
290
|
+
end: { line: 1, column: 1, offset: 0 }
|
|
291
|
+
};
|
|
292
|
+
}
|
|
263
293
|
return root;
|
|
264
294
|
};
|
|
265
295
|
function dropTrailingEmptyFieldIfPresent() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/parser.ts","../src/preprocessor.ts","../src/processor.ts","../src/tokenizer.ts"],"sourcesContent":["import type { Root } from \"@rethinkhealth/hl7v2-ast\";\nimport { DEFAULT_DELIMITERS } from \"@rethinkhealth/hl7v2-utils\";\nimport type { Plugin } from \"unified\";\nimport { defaultPreprocessors, runPreprocessors } from \"./preprocessor\";\nimport { parseHL7v2FromIterator } from \"./processor\";\nimport { HL7v2Tokenizer } from \"./tokenizer\";\nimport type { ParseOptions, ParserContext, Token, Tokenizer } from \"./types\";\n\nfunction* iterateTokenizerSync(t: Tokenizer): Iterable<Token> {\n for (let tok = t.next(); tok; tok = t.next()) {\n yield tok;\n }\n}\n\nexport function parseHL7v2(input: string, opts: ParseOptions): Root {\n let ctx: ParserContext = {\n input,\n delimiters: {\n ...DEFAULT_DELIMITERS,\n ...opts.delimiters,\n },\n };\n // Run preprocessing\n ctx = runPreprocessors(ctx, opts.preprocess || defaultPreprocessors);\n // Run tokenizer\n const tokenizer = new HL7v2Tokenizer();\n // Reset tokenizer.\n tokenizer.reset(ctx);\n // Parse\n return parseHL7v2FromIterator(iterateTokenizerSync(tokenizer), ctx);\n}\n\nconst hl7v2Parser: Plugin<[ParseOptions?], string, Root> = function (\n options = {}\n) {\n this.parser = (document: string) => parseHL7v2(document, options);\n};\n\nexport default hl7v2Parser;\n","import { DEFAULT_DELIMITERS } from \"@rethinkhealth/hl7v2-utils\";\nimport type { ParserContext, PreprocessorStep } from \"./types\";\n\n// PreprocessorStep is declared in `types.ts` to avoid circular imports\n\n/**\n * Step: strip UTF-8 BOM.\n */\nexport const stripBOM: PreprocessorStep = (ctx) => {\n if (ctx.input.charCodeAt(0) === 0xfe_ff) {\n ctx.input = ctx.input.slice(1);\n }\n return ctx;\n};\n\n/**\n * Step: normalize newlines to the default delimiters.\n */\nexport const normalizeNewlines: PreprocessorStep = (ctx) => {\n ctx.input = ctx.input.replace(/\\r?\\n/g, ctx.delimiters.segment);\n return ctx;\n};\n\n/**\n * Step: auto-detect delimiters from MSH.\n */\nexport const detectDelimiters: PreprocessorStep = (ctx) => {\n if (ctx.input.startsWith(\"MSH\")) {\n const field = ctx.input.charAt(3) || DEFAULT_DELIMITERS.field;\n const enc = ctx.input.slice(4, 8);\n const component = enc.charAt(0) || DEFAULT_DELIMITERS.component;\n const repetition = enc.charAt(1) || DEFAULT_DELIMITERS.repetition;\n const _escape = enc.charAt(2) || DEFAULT_DELIMITERS.escape;\n const subcomponent = enc.charAt(3) || DEFAULT_DELIMITERS.subcomponent;\n const segment = DEFAULT_DELIMITERS.segment;\n\n ctx.delimiters = {\n field,\n component,\n repetition,\n escape: _escape,\n subcomponent,\n segment,\n };\n }\n return ctx;\n};\n\n/**\n * Default preprocessing pipeline.\n */\nexport const defaultPreprocessors: PreprocessorStep[] = [\n stripBOM,\n normalizeNewlines,\n detectDelimiters,\n];\n\n/**\n * Run the pipeline to initialize ParserContext.\n */\nexport function runPreprocessors(\n ctx: ParserContext,\n steps: PreprocessorStep[]\n): ParserContext {\n for (const step of steps) {\n // biome-ignore lint/style/noParameterAssign: this is necessary to keep no-copy\n ctx = step(ctx);\n }\n\n return ctx;\n}\n","// src/parser.ts\n\nimport type {\n Component,\n Field,\n FieldRepetition,\n Root,\n Segment,\n SegmentHeader,\n Subcomponent,\n} from \"@rethinkhealth/hl7v2-ast\";\nimport { isEmptyNode } from \"@rethinkhealth/hl7v2-utils\";\nimport type { ParserContext, Position, Token } from \"./types\";\n\n// Shared core: process a single token into mutable parse state\nfunction createParserCore(ctx: ParserContext) {\n const root: Root = {\n type: \"root\",\n children: [],\n data: {\n delimiters: ctx.delimiters,\n },\n };\n\n let seg: Segment | null = null;\n let field: Field | null = null;\n let rep: FieldRepetition | null = null;\n let comp: Component | null = null;\n let currentSub: Subcomponent | null = null;\n let segmentHasContent = false;\n let lastContentEnd: Position[\"end\"] | null = null;\n let expectingSegmentName = true; // Start expecting a segment name\n let justSetSegmentName = false; // Track if we just set the segment name\n\n const openSegment = (name: string, position: Position) => {\n const header: SegmentHeader = {\n type: \"segment-header\",\n value: name,\n position: { ...position },\n };\n seg = {\n type: \"segment\",\n children: [header],\n position: { start: position.start, end: position.end },\n };\n root.children.push(seg);\n field = null;\n rep = null;\n comp = null;\n currentSub = null;\n segmentHasContent = false;\n justSetSegmentName = true; // Mark that we just set the segment name\n expectingSegmentName = false;\n };\n\n const openField = (start: Position[\"start\"]) => {\n if (!seg) {\n throw new Error(\n \"Cannot open field without an active segment. TEXT token with segment name must precede field content.\"\n );\n }\n field = { type: \"field\", children: [], position: { start, end: start } };\n seg.children.push(field);\n rep = {\n type: \"field-repetition\",\n children: [],\n position: { start, end: start },\n };\n field.children.push(rep);\n comp = { type: \"component\", children: [], position: { start, end: start } };\n rep.children.push(comp);\n currentSub = null;\n segmentHasContent = true;\n };\n\n const openRepetition = (start: Position[\"start\"]) => {\n if (!field) {\n openField(start);\n }\n rep = {\n type: \"field-repetition\",\n children: [],\n position: { start, end: start },\n };\n // biome-ignore lint/style/noNonNullAssertion: field is ensured above\n field!.children.push(rep);\n comp = { type: \"component\", children: [], position: { start, end: start } };\n rep.children.push(comp);\n currentSub = null;\n segmentHasContent = true;\n };\n\n const openComponent = (start: Position[\"start\"]) => {\n if (!field) {\n openField(start);\n }\n if (!rep) {\n rep = {\n type: \"field-repetition\",\n children: [],\n position: { start, end: start },\n };\n // biome-ignore lint/style/noNonNullAssertion: field is ensured above\n field!.children.push(rep);\n }\n comp = { type: \"component\", children: [], position: { start, end: start } };\n // biome-ignore lint/style/noNonNullAssertion: rep is ensured above\n rep!.children.push(comp);\n currentSub = null;\n segmentHasContent = true;\n };\n\n const ensureForText = (start: Position[\"start\"]) => {\n if (!field) {\n openField(start);\n }\n if (!rep) {\n openRepetition(start);\n }\n if (!comp) {\n openComponent(start);\n }\n if (!currentSub) {\n currentSub = {\n type: \"subcomponent\",\n value: \"\",\n position: { start, end: start },\n };\n // biome-ignore lint/style/noNonNullAssertion: comp is ensured above\n comp!.children.push(currentSub);\n segmentHasContent = true;\n }\n };\n\n const updatePositionsToEnd = (endPos: Position[\"end\"]) => {\n if (currentSub?.position) {\n currentSub.position.end = endPos;\n }\n if (comp?.position) {\n comp.position.end = endPos;\n }\n if (rep?.position) {\n rep.position.end = endPos;\n }\n if (field?.position) {\n field.position.end = endPos;\n }\n if (seg?.position) {\n seg.position.end = endPos;\n }\n };\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: State machine requires handling all token types\n const processToken = (tok: Token) => {\n switch (tok.type) {\n case \"SEGMENT_END\": {\n // Use the last content position instead of the segment delimiter position\n const endPos = lastContentEnd || tok.position.start;\n updatePositionsToEnd(endPos);\n dropTrailingEmptyFieldIfPresent();\n // Close current segment (if any) and reset; do not auto-open next segment\n seg = null;\n field = null;\n rep = null;\n comp = null;\n currentSub = null;\n segmentHasContent = false;\n lastContentEnd = null;\n expectingSegmentName = true; // Next TEXT will be a segment name\n return;\n }\n case \"FIELD_DELIM\": {\n lastContentEnd = tok.position.end;\n // Skip the first field delimiter after setting segment name (it separates segment name from fields)\n if (justSetSegmentName) {\n justSetSegmentName = false;\n return;\n }\n // Leading field delimiter implies an empty first field\n if (!field) {\n // Open an empty first field and record an empty subcomponent slot\n openField(tok.position.start);\n // biome-ignore lint/style/noNonNullAssertion: comp is initialized in openField\n comp!.children.push({\n type: \"subcomponent\",\n value: \"\",\n position: { start: tok.position.start, end: tok.position.start },\n });\n segmentHasContent = true;\n }\n openField(tok.position.end);\n return;\n }\n case \"REPETITION_DELIM\": {\n lastContentEnd = tok.position.end;\n if (!field) {\n openField(tok.position.start);\n }\n openRepetition(tok.position.end);\n return;\n }\n case \"COMPONENT_DELIM\": {\n lastContentEnd = tok.position.end;\n openComponent(tok.position.end);\n return;\n }\n case \"SUBCOMP_DELIM\": {\n lastContentEnd = tok.position.end;\n if (!comp) {\n openComponent(tok.position.start);\n }\n // Start a new empty subcomponent slot\n currentSub = {\n type: \"subcomponent\",\n value: \"\",\n position: { start: tok.position.end, end: tok.position.end },\n };\n // biome-ignore lint/style/noNonNullAssertion: comp is ensured above\n comp!.children.push(currentSub);\n segmentHasContent = true;\n return;\n }\n case \"TEXT\": {\n const val = tok.value ?? \"\";\n\n // If we're expecting a segment name, this TEXT is the segment name\n if (expectingSegmentName) {\n // `tok.position` is always available for TEXT tokens\n openSegment(val, tok.position);\n lastContentEnd = tok.position.end;\n return;\n }\n\n // Otherwise, it's regular field content\n ensureForText(tok.position.start);\n // biome-ignore lint/style/noNonNullAssertion: ensured above\n currentSub!.value += val;\n updatePositionsToEnd(tok.position.end);\n lastContentEnd = tok.position.end;\n segmentHasContent = true;\n return;\n }\n default: {\n throw new Error(`Unexpected token type: ${tok.type}`);\n }\n }\n };\n\n // Do not pre-open a segment; lazily open upon first non-SEGMENT_END token.\n\n const finalize = () => {\n // Handle input that ends without an explicit segment delimiter as above.\n if (lastContentEnd && seg) {\n updatePositionsToEnd(lastContentEnd);\n }\n dropTrailingEmptyFieldIfPresent();\n if (seg && !segmentHasContent) {\n // Drop trailing empty segment if no content was added\n root.children.pop();\n }\n return root;\n };\n\n function dropTrailingEmptyFieldIfPresent() {\n if (!seg || seg.children.length <= 1) {\n return;\n }\n // Drop only the final trailing empty field (created by the last delimiter),\n // preserving any intentional empty fields immediately before it.\n const lastChild = seg.children.at(-1);\n if (lastChild?.type !== \"field\") {\n return;\n }\n const lastField = lastChild as Field;\n if (isEmptyNode(lastField)) {\n seg.children.pop();\n }\n }\n\n return { processToken, finalize, root };\n}\n\n// Sync convenience wrapper over a sync Iterable token source\nexport function parseHL7v2FromIterator(\n tokens: Iterable<Token>,\n ctx: ParserContext\n): Root {\n const core = createParserCore(ctx);\n for (const tok of tokens) {\n core.processToken(tok);\n }\n return core.finalize();\n}\n","// src/tokenizer.ts\nimport type { Delimiters } from \"@rethinkhealth/hl7v2-ast\";\nimport type {\n ParserContext,\n Position,\n Token,\n Tokenizer,\n TokenType,\n} from \"./types\";\n\nconst MSH_SEGMENT_START = 0;\nconst MSH_SEGMENT_END = 3;\nconst MSH_FIELD_SEPERATOR_START = 3;\nconst MSH_FIELD_SEPERATOR_END = 4;\nconst MSH_FIELD_DELIMITER_START = 4;\nconst MSH_FIELD_DELIMITER_END = 8;\n\nexport class HL7v2Tokenizer implements Tokenizer, Iterable<Token> {\n private input = \"\";\n private i = 0;\n private line = 1;\n private col = 1;\n private delims!: Delimiters;\n\n // Only-run-once MSH bootstrap at the start of the file\n private didMshBootstrap = false;\n private pendingBootstrap: null | Array<\n | { kind: \"TEXT\"; value: string; advance: number }\n | { kind: \"FIELD_DELIM\"; advance: number }\n > = null; // queue to emit TEXT('MSH'), FIELD_DELIM, TEXT('^~\\\\&')\n\n reset(ctx: ParserContext) {\n this.input = ctx.input;\n this.delims = ctx.delimiters;\n this.i = 0;\n this.line = 1;\n this.col = 1;\n this.didMshBootstrap = false;\n this.pendingBootstrap = null;\n\n // Prepare a one-time bootstrap if file starts with MSH\n if (this.input.startsWith(\"MSH\")) {\n // Precompute MSH and MSH.2; MSH.1 is the field delimiter char at index 3\n const msh = this._slice(MSH_SEGMENT_START, MSH_SEGMENT_END);\n const msh1 = this._slice(\n MSH_FIELD_SEPERATOR_START,\n MSH_FIELD_SEPERATOR_END\n ); // the field delimiter char at index 3\n const msh2 = this._slice(\n MSH_FIELD_DELIMITER_START,\n MSH_FIELD_DELIMITER_END\n ); // may be shorter than 4 if truncated\n this.pendingBootstrap = [\n { kind: \"TEXT\", value: msh, advance: msh.length },\n { kind: \"FIELD_DELIM\", advance: 0 },\n { kind: \"TEXT\", value: msh1, advance: msh1.length },\n { kind: \"FIELD_DELIM\", advance: 0 },\n { kind: \"TEXT\", value: msh2, advance: msh2.length },\n ];\n // NOTE: we have not advanced indices yet; we will as we emit tokens\n }\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: The cognitive complexity of this method is justified because it must handle the HL7v2 MSH segment bootstrap logic and multiple tokenization cases in a single method for performance and maintainability.\n next(): Token | null {\n const s = this.input;\n const n = s.length;\n if (this.i >= n && !this.pendingBootstrap?.length) {\n return null;\n }\n\n const start = { offset: this.i, line: this.line, column: this.col };\n\n // ---- One-time MSH bootstrap at file start ----\n if (!this.didMshBootstrap && this.pendingBootstrap) {\n const step = this.pendingBootstrap.shift();\n if (step) {\n if (step.kind === \"TEXT\") {\n const take = Math.min(step.advance, n - this.i);\n const out = step.value.slice(0, take);\n this._fastAdvance(out);\n if (this.pendingBootstrap.length === 0) {\n this.pendingBootstrap = null;\n this.didMshBootstrap = true;\n }\n return this._tok(\"TEXT\", out, start);\n }\n // FIELD_DELIM: advance exactly one char (the delimiter) and emit a FIELD_DELIM token\n this._advance(Math.min(step.advance, n - this.i));\n if (this.pendingBootstrap.length === 0) {\n this.pendingBootstrap = null;\n this.didMshBootstrap = true;\n }\n return this._tok(\"FIELD_DELIM\", undefined, start);\n }\n }\n\n // ---- Normal tokenization (delimiters + text) ----\n if (this.i >= n) {\n return null;\n }\n const ch = s.charAt(this.i);\n\n if (ch === this.delims.segment) {\n this._advance(1, true);\n return this._tok(\"SEGMENT_END\", undefined, start);\n }\n if (ch === this.delims.field) {\n this._advance(1);\n return this._tok(\"FIELD_DELIM\", undefined, start);\n }\n if (ch === this.delims.repetition) {\n this._advance(1);\n return this._tok(\"REPETITION_DELIM\", undefined, start);\n }\n if (ch === this.delims.component) {\n this._advance(1);\n return this._tok(\"COMPONENT_DELIM\", undefined, start);\n }\n if (ch === this.delims.subcomponent) {\n this._advance(1);\n return this._tok(\"SUBCOMP_DELIM\", undefined, start);\n }\n\n // TEXT until next delimiter or end\n let j = this.i;\n const seg = this.delims.segment,\n fld = this.delims.field,\n rep = this.delims.repetition,\n cmp = this.delims.component,\n sub = this.delims.subcomponent;\n\n while (j < s.length) {\n const c = s.charAt(j);\n if (c === seg || c === fld || c === rep || c === cmp || c === sub) {\n break;\n }\n j++;\n }\n\n const val = s.slice(this.i, j);\n this._fastAdvance(val);\n return this._tok(\"TEXT\", val, start);\n }\n\n // ---- helpers ----\n private _slice(start: number, end: number): string {\n return this.input.slice(start, Math.min(end, this.input.length));\n }\n\n private _advance(n: number, isNewline = false) {\n this.i += n;\n if (isNewline) {\n this.line += 1;\n this.col = 1;\n } else {\n this.col += n;\n }\n }\n\n private _fastAdvance(chunk: string) {\n const parts = chunk.split(this.delims.segment);\n if (parts.length > 1) {\n this.line += parts.length - 1;\n this.col = (parts.at(-1)?.length ?? 0) + 1;\n } else {\n this.col += chunk.length;\n }\n this.i += chunk.length;\n }\n\n private _tok(\n type: TokenType,\n value: string | undefined,\n start: Position[\"start\"]\n ): Token {\n const end = { offset: this.i, line: this.line, column: this.col };\n return { type, value, position: { start, end } };\n }\n\n // Iterable protocol (sync)\n [Symbol.iterator](): Iterator<Token> {\n return {\n next: () => {\n const t = this.next();\n if (t == null) {\n return { done: true, value: undefined as unknown as Token };\n }\n return { done: false, value: t };\n },\n };\n }\n}\n"],"mappings":"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","names":["DEFAULT_DELIMITERS","DEFAULT_DELIMITERS"]}
|
|
1
|
+
{"version":3,"sources":["../src/parser.ts","../src/preprocessor.ts","../src/processor.ts","../src/tokenizer.ts"],"sourcesContent":["import type { Root } from \"@rethinkhealth/hl7v2-ast\";\nimport { DEFAULT_DELIMITERS } from \"@rethinkhealth/hl7v2-utils\";\nimport type { Plugin } from \"unified\";\nimport { defaultPreprocessors, runPreprocessors } from \"./preprocessor\";\nimport { parseHL7v2FromIterator } from \"./processor\";\nimport { HL7v2Tokenizer } from \"./tokenizer\";\nimport type { ParseOptions, ParserContext, Token, Tokenizer } from \"./types\";\n\nfunction* iterateTokenizerSync(t: Tokenizer): Iterable<Token> {\n for (let tok = t.next(); tok; tok = t.next()) {\n yield tok;\n }\n}\n\nexport function parseHL7v2(input: string, opts: ParseOptions): Root {\n let ctx: ParserContext = {\n input,\n delimiters: {\n ...DEFAULT_DELIMITERS,\n ...opts.delimiters,\n },\n };\n // Run preprocessing\n ctx = runPreprocessors(ctx, opts.preprocess || defaultPreprocessors);\n // Run tokenizer\n const tokenizer = new HL7v2Tokenizer();\n // Reset tokenizer.\n tokenizer.reset(ctx);\n // Parse\n return parseHL7v2FromIterator(iterateTokenizerSync(tokenizer), ctx);\n}\n\nconst hl7v2Parser: Plugin<[ParseOptions?], string, Root> = function (\n options = {}\n) {\n this.parser = (document: string) => parseHL7v2(document, options);\n};\n\nexport default hl7v2Parser;\n","import { DEFAULT_DELIMITERS } from \"@rethinkhealth/hl7v2-utils\";\nimport type { ParserContext, PreprocessorStep } from \"./types\";\n\n// PreprocessorStep is declared in `types.ts` to avoid circular imports\n\n/**\n * Step: strip UTF-8 BOM.\n */\nexport const stripBOM: PreprocessorStep = (ctx) => {\n if (ctx.input.charCodeAt(0) === 0xfe_ff) {\n ctx.input = ctx.input.slice(1);\n }\n return ctx;\n};\n\n/**\n * Step: normalize newlines to the default delimiters.\n */\nexport const normalizeNewlines: PreprocessorStep = (ctx) => {\n ctx.input = ctx.input.replace(/\\r?\\n/g, ctx.delimiters.segment);\n return ctx;\n};\n\n/**\n * Step: auto-detect delimiters from MSH.\n */\nexport const detectDelimiters: PreprocessorStep = (ctx) => {\n if (ctx.input.startsWith(\"MSH\")) {\n const field = ctx.input.charAt(3) || DEFAULT_DELIMITERS.field;\n const enc = ctx.input.slice(4, 8);\n const component = enc.charAt(0) || DEFAULT_DELIMITERS.component;\n const repetition = enc.charAt(1) || DEFAULT_DELIMITERS.repetition;\n const _escape = enc.charAt(2) || DEFAULT_DELIMITERS.escape;\n const subcomponent = enc.charAt(3) || DEFAULT_DELIMITERS.subcomponent;\n const segment = DEFAULT_DELIMITERS.segment;\n\n ctx.delimiters = {\n field,\n component,\n repetition,\n escape: _escape,\n subcomponent,\n segment,\n };\n }\n return ctx;\n};\n\n/**\n * Default preprocessing pipeline.\n */\nexport const defaultPreprocessors: PreprocessorStep[] = [\n stripBOM,\n normalizeNewlines,\n detectDelimiters,\n];\n\n/**\n * Run the pipeline to initialize ParserContext.\n */\nexport function runPreprocessors(\n ctx: ParserContext,\n steps: PreprocessorStep[]\n): ParserContext {\n for (const step of steps) {\n // biome-ignore lint/style/noParameterAssign: this is necessary to keep no-copy\n ctx = step(ctx);\n }\n\n return ctx;\n}\n","// src/parser.ts\n\nimport type {\n Component,\n Field,\n FieldRepetition,\n Root,\n Segment,\n SegmentHeader,\n Subcomponent,\n} from \"@rethinkhealth/hl7v2-ast\";\nimport { isEmptyNode } from \"@rethinkhealth/hl7v2-utils\";\nimport type { ParserContext, Position, Token } from \"./types\";\n\n// Helper to create an empty subcomponent at a given position\nfunction createSubcomponent(start: Position[\"start\"]): Subcomponent {\n return {\n type: \"subcomponent\",\n value: \"\",\n position: { start, end: start },\n };\n}\n\n// Helper to create a component with an initial empty subcomponent\nfunction createComponent(start: Position[\"start\"]): Component {\n return {\n type: \"component\",\n children: [createSubcomponent(start)],\n position: { start, end: start },\n };\n}\n\n// Helper to create a field repetition with an initial component\nfunction createFieldRepetition(start: Position[\"start\"]): FieldRepetition {\n return {\n type: \"field-repetition\",\n children: [createComponent(start)],\n position: { start, end: start },\n };\n}\n\n// Helper to create a field with an initial repetition\nfunction createField(start: Position[\"start\"]): Field {\n return {\n type: \"field\",\n children: [createFieldRepetition(start)],\n position: { start, end: start },\n };\n}\n\n// Shared core: process a single token into mutable parse state\nfunction createParserCore(ctx: ParserContext) {\n const root: Root = {\n type: \"root\",\n children: [],\n data: {\n delimiters: ctx.delimiters,\n },\n };\n\n let seg: Segment | null = null;\n let field: Field | null = null;\n let rep: FieldRepetition | null = null;\n let comp: Component | null = null;\n let currentSub: Subcomponent | null = null;\n let segmentHasContent = false;\n let lastContentEnd: Position[\"end\"] | null = null;\n let documentEnd: Position[\"end\"] | null = null; // Track document-level end position\n let expectingSegmentName = true; // Start expecting a segment name\n let justSetSegmentName = false; // Track if we just set the segment name\n let rootStartSet = false; // Track if we've set the root start position\n\n const resetState = () => {\n seg = null;\n field = null;\n rep = null;\n comp = null;\n currentSub = null;\n segmentHasContent = false;\n lastContentEnd = null;\n expectingSegmentName = true;\n };\n\n const openSegment = (name: string, position: Position) => {\n const header: SegmentHeader = {\n type: \"segment-header\",\n value: name,\n position: { ...position },\n };\n seg = {\n type: \"segment\",\n children: [header],\n position: { start: position.start, end: position.end },\n };\n root.children.push(seg);\n // Reset field-level state\n field = null;\n rep = null;\n comp = null;\n currentSub = null;\n segmentHasContent = false;\n justSetSegmentName = true;\n expectingSegmentName = false;\n };\n\n const openField = (start: Position[\"start\"]) => {\n if (!seg) {\n throw new Error(\n \"Cannot open field without an active segment. TEXT token with segment name must precede field content.\"\n );\n }\n field = createField(start);\n seg.children.push(field);\n // biome-ignore lint/style/noNonNullAssertion: createField guarantees at least one child at each level\n rep = field.children[0]!;\n // biome-ignore lint/style/noNonNullAssertion: createFieldRepetition guarantees at least one child\n comp = rep.children[0]!;\n // biome-ignore lint/style/noNonNullAssertion: createComponent guarantees at least one child\n currentSub = comp.children[0]!;\n segmentHasContent = true;\n };\n\n const openRepetition = (start: Position[\"start\"]) => {\n if (!field) {\n openField(start);\n return; // openField already created everything including subcomponent\n }\n rep = createFieldRepetition(start);\n field.children.push(rep);\n comp = rep.children[0] ?? null;\n currentSub = comp?.children[0] ?? null;\n segmentHasContent = true;\n };\n\n const openComponent = (start: Position[\"start\"]) => {\n if (!field) {\n openField(start);\n return; // openField already created everything including subcomponent\n }\n if (!rep) {\n rep = createFieldRepetition(start);\n field.children.push(rep);\n }\n comp = createComponent(start);\n rep.children.push(comp);\n currentSub = comp.children[0] ?? null;\n segmentHasContent = true;\n };\n\n const ensureForText = (start: Position[\"start\"]) => {\n if (!field) {\n openField(start);\n return; // Everything is already set up by openField\n }\n if (!rep) {\n openRepetition(start);\n return; // Everything is already set up by openRepetition\n }\n if (!comp) {\n openComponent(start);\n return; // Everything is already set up by openComponent\n }\n if (!currentSub) {\n currentSub = createSubcomponent(start);\n comp.children.push(currentSub);\n segmentHasContent = true;\n }\n };\n\n const updatePositionsToEnd = (endPos: Position[\"end\"]) => {\n if (currentSub?.position) {\n currentSub.position.end = endPos;\n }\n if (comp?.position) {\n comp.position.end = endPos;\n }\n if (rep?.position) {\n rep.position.end = endPos;\n }\n if (field?.position) {\n field.position.end = endPos;\n }\n if (seg?.position) {\n seg.position.end = endPos;\n }\n };\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: State machine requires handling all token types\n const processToken = (tok: Token) => {\n switch (tok.type) {\n case \"SEGMENT_END\": {\n // Use the last content position instead of the segment delimiter position\n const endPos = lastContentEnd || tok.position.start;\n updatePositionsToEnd(endPos);\n dropTrailingEmptyFieldIfPresent();\n resetState();\n return;\n }\n case \"FIELD_DELIM\": {\n lastContentEnd = tok.position.end;\n documentEnd = tok.position.end;\n // Skip the first field delimiter after setting segment name (it separates segment name from fields)\n if (justSetSegmentName) {\n justSetSegmentName = false;\n return;\n }\n // Leading field delimiter implies an empty first field\n if (!field) {\n // Open an empty first field (already has empty subcomponent from openField)\n openField(tok.position.start);\n }\n openField(tok.position.end);\n return;\n }\n case \"REPETITION_DELIM\": {\n lastContentEnd = tok.position.end;\n documentEnd = tok.position.end;\n if (!field) {\n openField(tok.position.start);\n }\n openRepetition(tok.position.end);\n return;\n }\n case \"COMPONENT_DELIM\": {\n lastContentEnd = tok.position.end;\n documentEnd = tok.position.end;\n openComponent(tok.position.end);\n return;\n }\n case \"SUBCOMP_DELIM\": {\n lastContentEnd = tok.position.end;\n documentEnd = tok.position.end;\n if (!comp) {\n openComponent(tok.position.start);\n }\n currentSub = createSubcomponent(tok.position.end);\n comp?.children.push(currentSub);\n segmentHasContent = true;\n return;\n }\n case \"TEXT\": {\n const val = tok.value ?? \"\";\n\n // Set root start position on first token\n if (!rootStartSet) {\n root.position = {\n start: tok.position.start,\n end: tok.position.start,\n };\n rootStartSet = true;\n }\n\n // If we're expecting a segment name, this TEXT is the segment name\n if (expectingSegmentName) {\n // `tok.position` is always available for TEXT tokens\n openSegment(val, tok.position);\n lastContentEnd = tok.position.end;\n documentEnd = tok.position.end;\n return;\n }\n\n // Otherwise, it's regular field content\n ensureForText(tok.position.start);\n // biome-ignore lint/style/noNonNullAssertion: ensured above\n currentSub!.value += val;\n updatePositionsToEnd(tok.position.end);\n lastContentEnd = tok.position.end;\n documentEnd = tok.position.end;\n segmentHasContent = true;\n return;\n }\n default: {\n throw new Error(`Unexpected token type: ${tok.type}`);\n }\n }\n };\n\n // Do not pre-open a segment; lazily open upon first non-SEGMENT_END token.\n\n const finalize = () => {\n // Handle input that ends without an explicit segment delimiter as above.\n if (lastContentEnd && seg) {\n updatePositionsToEnd(lastContentEnd);\n }\n dropTrailingEmptyFieldIfPresent();\n if (seg && !segmentHasContent) {\n // Drop trailing empty segment if no content was added\n root.children.pop();\n }\n\n // Set root end position once at finalization using document-level end\n if (root.position && documentEnd) {\n root.position.end = documentEnd;\n } else if (!root.position) {\n // Empty document - set default position\n root.position = {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n };\n }\n\n return root;\n };\n\n function dropTrailingEmptyFieldIfPresent() {\n if (!seg || seg.children.length <= 1) {\n return;\n }\n // Drop only the final trailing empty field (created by the last delimiter),\n // preserving any intentional empty fields immediately before it.\n const lastChild = seg.children.at(-1);\n if (lastChild?.type !== \"field\") {\n return;\n }\n const lastField = lastChild as Field;\n if (isEmptyNode(lastField)) {\n seg.children.pop();\n }\n }\n\n return { processToken, finalize, root };\n}\n\n// Sync convenience wrapper over a sync Iterable token source\nexport function parseHL7v2FromIterator(\n tokens: Iterable<Token>,\n ctx: ParserContext\n): Root {\n const core = createParserCore(ctx);\n for (const tok of tokens) {\n core.processToken(tok);\n }\n return core.finalize();\n}\n","// src/tokenizer.ts\nimport type { Delimiters } from \"@rethinkhealth/hl7v2-ast\";\nimport type {\n ParserContext,\n Position,\n Token,\n Tokenizer,\n TokenType,\n} from \"./types\";\n\nconst MSH_SEGMENT_START = 0;\nconst MSH_SEGMENT_END = 3;\nconst MSH_FIELD_SEPERATOR_START = 3;\nconst MSH_FIELD_SEPERATOR_END = 4;\nconst MSH_FIELD_DELIMITER_START = 4;\nconst MSH_FIELD_DELIMITER_END = 8;\n\nexport class HL7v2Tokenizer implements Tokenizer, Iterable<Token> {\n private input = \"\";\n private i = 0;\n private line = 1;\n private col = 1;\n private delims!: Delimiters;\n\n // Only-run-once MSH bootstrap at the start of the file\n private didMshBootstrap = false;\n private pendingBootstrap: null | Array<\n | { kind: \"TEXT\"; value: string; advance: number }\n | { kind: \"FIELD_DELIM\"; advance: number }\n > = null; // queue to emit TEXT('MSH'), FIELD_DELIM, TEXT('^~\\\\&')\n\n reset(ctx: ParserContext) {\n this.input = ctx.input;\n this.delims = ctx.delimiters;\n this.i = 0;\n this.line = 1;\n this.col = 1;\n this.didMshBootstrap = false;\n this.pendingBootstrap = null;\n\n // Prepare a one-time bootstrap if file starts with MSH\n if (this.input.startsWith(\"MSH\")) {\n // Precompute MSH and MSH.2; MSH.1 is the field delimiter char at index 3\n const msh = this._slice(MSH_SEGMENT_START, MSH_SEGMENT_END);\n const msh1 = this._slice(\n MSH_FIELD_SEPERATOR_START,\n MSH_FIELD_SEPERATOR_END\n ); // the field delimiter char at index 3\n const msh2 = this._slice(\n MSH_FIELD_DELIMITER_START,\n MSH_FIELD_DELIMITER_END\n ); // may be shorter than 4 if truncated\n this.pendingBootstrap = [\n { kind: \"TEXT\", value: msh, advance: msh.length },\n { kind: \"FIELD_DELIM\", advance: 0 },\n { kind: \"TEXT\", value: msh1, advance: msh1.length },\n { kind: \"FIELD_DELIM\", advance: 0 },\n { kind: \"TEXT\", value: msh2, advance: msh2.length },\n ];\n // NOTE: we have not advanced indices yet; we will as we emit tokens\n }\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: The cognitive complexity of this method is justified because it must handle the HL7v2 MSH segment bootstrap logic and multiple tokenization cases in a single method for performance and maintainability.\n next(): Token | null {\n const s = this.input;\n const n = s.length;\n if (this.i >= n && !this.pendingBootstrap?.length) {\n return null;\n }\n\n const start = { offset: this.i, line: this.line, column: this.col };\n\n // ---- One-time MSH bootstrap at file start ----\n if (!this.didMshBootstrap && this.pendingBootstrap) {\n const step = this.pendingBootstrap.shift();\n if (step) {\n if (step.kind === \"TEXT\") {\n const take = Math.min(step.advance, n - this.i);\n const out = step.value.slice(0, take);\n this._fastAdvance(out);\n if (this.pendingBootstrap.length === 0) {\n this.pendingBootstrap = null;\n this.didMshBootstrap = true;\n }\n return this._tok(\"TEXT\", out, start);\n }\n // FIELD_DELIM: advance exactly one char (the delimiter) and emit a FIELD_DELIM token\n this._advance(Math.min(step.advance, n - this.i));\n if (this.pendingBootstrap.length === 0) {\n this.pendingBootstrap = null;\n this.didMshBootstrap = true;\n }\n return this._tok(\"FIELD_DELIM\", undefined, start);\n }\n }\n\n // ---- Normal tokenization (delimiters + text) ----\n if (this.i >= n) {\n return null;\n }\n const ch = s.charAt(this.i);\n\n if (ch === this.delims.segment) {\n this._advance(1, true);\n return this._tok(\"SEGMENT_END\", undefined, start);\n }\n if (ch === this.delims.field) {\n this._advance(1);\n return this._tok(\"FIELD_DELIM\", undefined, start);\n }\n if (ch === this.delims.repetition) {\n this._advance(1);\n return this._tok(\"REPETITION_DELIM\", undefined, start);\n }\n if (ch === this.delims.component) {\n this._advance(1);\n return this._tok(\"COMPONENT_DELIM\", undefined, start);\n }\n if (ch === this.delims.subcomponent) {\n this._advance(1);\n return this._tok(\"SUBCOMP_DELIM\", undefined, start);\n }\n\n // TEXT until next delimiter or end\n let j = this.i;\n const seg = this.delims.segment,\n fld = this.delims.field,\n rep = this.delims.repetition,\n cmp = this.delims.component,\n sub = this.delims.subcomponent;\n\n while (j < s.length) {\n const c = s.charAt(j);\n if (c === seg || c === fld || c === rep || c === cmp || c === sub) {\n break;\n }\n j++;\n }\n\n const val = s.slice(this.i, j);\n this._fastAdvance(val);\n return this._tok(\"TEXT\", val, start);\n }\n\n // ---- helpers ----\n private _slice(start: number, end: number): string {\n return this.input.slice(start, Math.min(end, this.input.length));\n }\n\n private _advance(n: number, isNewline = false) {\n this.i += n;\n if (isNewline) {\n this.line += 1;\n this.col = 1;\n } else {\n this.col += n;\n }\n }\n\n private _fastAdvance(chunk: string) {\n const parts = chunk.split(this.delims.segment);\n if (parts.length > 1) {\n this.line += parts.length - 1;\n this.col = (parts.at(-1)?.length ?? 0) + 1;\n } else {\n this.col += chunk.length;\n }\n this.i += chunk.length;\n }\n\n private _tok(\n type: TokenType,\n value: string | undefined,\n start: Position[\"start\"]\n ): Token {\n const end = { offset: this.i, line: this.line, column: this.col };\n return { type, value, position: { start, end } };\n }\n\n // Iterable protocol (sync)\n [Symbol.iterator](): Iterator<Token> {\n return {\n next: () => {\n const t = this.next();\n if (t == null) {\n return { done: true, value: undefined as unknown as Token };\n }\n return { done: false, value: t };\n },\n };\n }\n}\n"],"mappings":"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","names":["DEFAULT_DELIMITERS","DEFAULT_DELIMITERS"]}
|
package/dist/processor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,IAAI,EAIL,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,aAAa,EAAY,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../src/processor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,IAAI,EAIL,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,aAAa,EAAY,KAAK,EAAE,MAAM,SAAS,CAAC;AAwT9D,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EACvB,GAAG,EAAE,aAAa,GACjB,IAAI,CAMN"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rethinkhealth/hl7v2-parser",
|
|
3
3
|
"description": "hl7v2 plugin to parse hl7v2 messages",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Melek Somai",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"unified": "11.0.5",
|
|
20
|
-
"@rethinkhealth/hl7v2-utils": "0.
|
|
20
|
+
"@rethinkhealth/hl7v2-utils": "0.3.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "24.10.0",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"tsup": "8.5.0",
|
|
27
27
|
"typescript": "^5.9.3",
|
|
28
28
|
"vitest": "^4.0.6",
|
|
29
|
-
"@rethinkhealth/hl7v2-ast": "0.
|
|
30
|
-
"@rethinkhealth/
|
|
31
|
-
"@rethinkhealth/
|
|
29
|
+
"@rethinkhealth/hl7v2-ast": "0.3.0",
|
|
30
|
+
"@rethinkhealth/tsconfig": "0.0.1",
|
|
31
|
+
"@rethinkhealth/testing": "0.0.2"
|
|
32
32
|
},
|
|
33
33
|
"repository": "rethinkhealth/hl7v2.git",
|
|
34
34
|
"homepage": "https://www.rethinkhealth.io/hl7v2/docs",
|