@rethinkhealth/hl7v2-parser 0.2.0 → 0.2.1
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/README.md +66 -0
- package/dist/index.js +482 -176
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts +10 -4
- package/dist/parser.d.ts.map +1 -1
- package/dist/pipeline/core/message.d.ts +16 -0
- package/dist/pipeline/core/message.d.ts.map +1 -0
- package/dist/pipeline/core/parsers/component.d.ts +13 -0
- package/dist/pipeline/core/parsers/component.d.ts.map +1 -0
- package/dist/pipeline/core/parsers/field.d.ts +13 -0
- package/dist/pipeline/core/parsers/field.d.ts.map +1 -0
- package/dist/pipeline/core/parsers/segment-msh.d.ts +14 -0
- package/dist/pipeline/core/parsers/segment-msh.d.ts.map +1 -0
- package/dist/pipeline/core/parsers/segment.d.ts +13 -0
- package/dist/pipeline/core/parsers/segment.d.ts.map +1 -0
- package/dist/pipeline/core/parsers/subcomponent.d.ts +10 -0
- package/dist/pipeline/core/parsers/subcomponent.d.ts.map +1 -0
- package/dist/pipeline/core/registry.d.ts +30 -0
- package/dist/pipeline/core/registry.d.ts.map +1 -0
- package/dist/pipeline/index.d.ts +10 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/interfaces.d.ts +109 -0
- package/dist/pipeline/interfaces.d.ts.map +1 -0
- package/dist/{utils.d.ts → pipeline/utils/index.d.ts} +6 -2
- package/dist/pipeline/utils/index.d.ts.map +1 -0
- package/dist/pipeline/validation/basic-validation.d.ts +17 -0
- package/dist/pipeline/validation/basic-validation.d.ts.map +1 -0
- package/package.json +3 -3
- package/dist/utils.d.ts.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# HL7v2 Parser
|
|
2
|
+
|
|
3
|
+
This package is a utility that takes a raw HL7v2 message as input and turns it into an HL7v2 syntax tree following the [@rethinkhealth/hl7v2-ast](../hl7v2-ast/README.md) definition.
|
|
4
|
+
|
|
5
|
+
This utility is a low level project. It’s used in [@rethinkhealth/hl7v2](../hl7v2/), which focusses on making it easier to transform content by abstracting these internals away.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## When should I use this?
|
|
10
|
+
|
|
11
|
+
If you want to handle syntax trees manually, use this. For an easier time processing content, use the [@rethinkhealth/hl7v2](../hl7v2/) ecosystem instead.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). In Node.js (version 16+), install with npm:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @rethinkhealth/hl7v2-parser
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Use
|
|
24
|
+
|
|
25
|
+
### Basic Usage
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { hl7v2Parser } from '@rethinkhealth/hl7v2-parser';
|
|
29
|
+
|
|
30
|
+
// Sample HL7v2 message
|
|
31
|
+
const message = `MSH|^~\\&|SENDING_APP|SENDING_FAC|RECEIVING_APP|RECEIVING_FAC|20110613061611||ADT^A04|12345|P|2.3
|
|
32
|
+
PID|1||123456||SMITH^JOHN^J||19800101|M|||123 MAIN ST^^ANYTOWN^ST^12345
|
|
33
|
+
PV1|1|I|ICU^101^1|||DOCTOR123^SMITH^JOHN^MD`;
|
|
34
|
+
|
|
35
|
+
// Parse using the unified processor
|
|
36
|
+
const processor = hl7v2Parser();
|
|
37
|
+
const result = processor.parse(message);
|
|
38
|
+
|
|
39
|
+
console.log(`Parsed ${result.children?.length} segments`);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Contributing
|
|
45
|
+
|
|
46
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for more details.
|
|
47
|
+
|
|
48
|
+
1. Fork the repository
|
|
49
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
50
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
51
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
52
|
+
5. Open a Pull Request
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Code of Conduct
|
|
57
|
+
|
|
58
|
+
To ensure a welcoming and positive environment, we have a [Code of Conduct](CODE_OF_CONDUCT.md) that all contributors and participants are expected to adhere to.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
Copyright 2025 Rethink Health, SUARL. All rights reserved.
|
|
65
|
+
|
|
66
|
+
This program is licensed to you under the terms of the [MIT License](https://opensource.org/licenses/MIT). This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the [LICENSE](LICENSE) file for details.
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,11 @@ var DEFAULT_DELIMITERS = {
|
|
|
5
5
|
subcomponent: "&",
|
|
6
6
|
repetition: "~",
|
|
7
7
|
escape: "\\",
|
|
8
|
-
segment: "\
|
|
9
|
-
// default to \
|
|
8
|
+
segment: "\n"
|
|
9
|
+
// default to \n (more common in modern systems)
|
|
10
10
|
};
|
|
11
11
|
var EMPTY_MESSAGE = {
|
|
12
|
-
type: "
|
|
12
|
+
type: "root",
|
|
13
13
|
delimiter: DEFAULT_DELIMITERS.segment,
|
|
14
14
|
children: [],
|
|
15
15
|
position: {
|
|
@@ -18,9 +18,13 @@ var EMPTY_MESSAGE = {
|
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
// src/utils.ts
|
|
21
|
+
// src/pipeline/utils/index.ts
|
|
22
22
|
function detectDelimitersFromMSH(raw, segmentDelimiter) {
|
|
23
|
-
const
|
|
23
|
+
const detectedSegmentDelimiter = detectSegmentDelimiter(
|
|
24
|
+
raw,
|
|
25
|
+
segmentDelimiter
|
|
26
|
+
);
|
|
27
|
+
const firstLineEnd = raw.indexOf(detectedSegmentDelimiter);
|
|
24
28
|
const mshSegment = firstLineEnd >= 0 ? raw.slice(0, firstLineEnd) : raw;
|
|
25
29
|
if (!mshSegment.startsWith("MSH")) {
|
|
26
30
|
return {};
|
|
@@ -39,7 +43,7 @@ function detectDelimitersFromMSH(raw, segmentDelimiter) {
|
|
|
39
43
|
repetition: encodingChars[1],
|
|
40
44
|
escape: encodingChars[2],
|
|
41
45
|
subcomponent: encodingChars[3],
|
|
42
|
-
segment:
|
|
46
|
+
segment: detectedSegmentDelimiter
|
|
43
47
|
};
|
|
44
48
|
}
|
|
45
49
|
function splitByString(text, delimiter) {
|
|
@@ -62,210 +66,512 @@ function splitByString(text, delimiter) {
|
|
|
62
66
|
});
|
|
63
67
|
return result;
|
|
64
68
|
}
|
|
69
|
+
function detectSegmentDelimiter(message, fallback = "\n") {
|
|
70
|
+
const possibleDelimiters = ["\r\n", "\n", "\r"];
|
|
71
|
+
for (const delimiter of possibleDelimiters) {
|
|
72
|
+
if (message.includes(delimiter)) {
|
|
73
|
+
return delimiter;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return fallback;
|
|
77
|
+
}
|
|
65
78
|
|
|
66
|
-
// src/
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return
|
|
79
|
+
// src/pipeline/core/parsers/subcomponent.ts
|
|
80
|
+
var SubcomponentParser = class {
|
|
81
|
+
canParse(input) {
|
|
82
|
+
return input.type === "subcomponent";
|
|
70
83
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
parse(input) {
|
|
85
|
+
if (!this.canParse(input)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
type: "subcomponent",
|
|
90
|
+
index: input.subcomponentIndex,
|
|
91
|
+
value: input.text,
|
|
92
|
+
position: {
|
|
93
|
+
start: { line: input.line, column: input.column, offset: input.start },
|
|
94
|
+
end: {
|
|
95
|
+
line: input.line,
|
|
96
|
+
column: input.column + input.text.length,
|
|
97
|
+
offset: input.end
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/pipeline/core/parsers/component.ts
|
|
105
|
+
var ComponentParser = class {
|
|
106
|
+
subcomponentParser = new SubcomponentParser();
|
|
107
|
+
canParse(input) {
|
|
108
|
+
return input.type === "component";
|
|
109
|
+
}
|
|
110
|
+
parse(input) {
|
|
111
|
+
if (!this.canParse(input)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const baseNode = {
|
|
115
|
+
type: "component",
|
|
116
|
+
index: input.componentIndex,
|
|
117
|
+
position: {
|
|
118
|
+
start: { line: input.line, column: input.column, offset: input.start },
|
|
119
|
+
end: {
|
|
120
|
+
line: input.line,
|
|
121
|
+
column: input.column + input.text.length,
|
|
122
|
+
offset: input.end
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
if (input.text.includes(input.context.delimiters.subcomponent)) {
|
|
127
|
+
return {
|
|
128
|
+
...baseNode,
|
|
129
|
+
delimiter: input.context.delimiters.subcomponent,
|
|
130
|
+
children: this.parseSubcomponents(input)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return { ...baseNode, value: input.text };
|
|
134
|
+
}
|
|
135
|
+
parseSubcomponents(input) {
|
|
136
|
+
const subcomponents = this.splitByDelimiter(
|
|
137
|
+
input.text,
|
|
138
|
+
input.context.delimiters.subcomponent
|
|
80
139
|
);
|
|
81
|
-
|
|
140
|
+
const nodes = [];
|
|
141
|
+
for (let i = 0; i < subcomponents.length; i++) {
|
|
142
|
+
const sub = subcomponents[i];
|
|
143
|
+
if (!sub) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const subNode = this.subcomponentParser.parse({
|
|
147
|
+
type: "subcomponent",
|
|
148
|
+
text: sub.value,
|
|
149
|
+
start: input.start + sub.start,
|
|
150
|
+
end: input.start + sub.end,
|
|
151
|
+
index: i,
|
|
152
|
+
subcomponentIndex: i,
|
|
153
|
+
line: input.line,
|
|
154
|
+
column: input.column + sub.start,
|
|
155
|
+
context: input.context
|
|
156
|
+
});
|
|
157
|
+
if (subNode) {
|
|
158
|
+
nodes.push(subNode);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return nodes;
|
|
82
162
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
163
|
+
splitByDelimiter(text, delimiter) {
|
|
164
|
+
const result = [];
|
|
165
|
+
let lastIndex = 0;
|
|
166
|
+
let index = text.indexOf(delimiter);
|
|
167
|
+
while (index !== -1) {
|
|
168
|
+
result.push({
|
|
169
|
+
value: text.slice(lastIndex, index),
|
|
170
|
+
start: lastIndex,
|
|
171
|
+
end: index
|
|
172
|
+
});
|
|
173
|
+
lastIndex = index + delimiter.length;
|
|
174
|
+
index = text.indexOf(delimiter, lastIndex);
|
|
175
|
+
}
|
|
176
|
+
result.push({
|
|
177
|
+
value: text.slice(lastIndex),
|
|
178
|
+
start: lastIndex,
|
|
179
|
+
end: text.length
|
|
180
|
+
});
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// src/pipeline/core/parsers/field.ts
|
|
186
|
+
var FieldParser = class {
|
|
187
|
+
componentParser = new ComponentParser();
|
|
188
|
+
canParse(input) {
|
|
189
|
+
return input.type === "field";
|
|
190
|
+
}
|
|
191
|
+
parse(input) {
|
|
192
|
+
if (!this.canParse(input)) {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const baseNode = {
|
|
196
|
+
type: input.fieldIndex === 0 ? "header" : "field",
|
|
197
|
+
index: input.fieldIndex,
|
|
198
|
+
position: {
|
|
199
|
+
start: { line: input.line, column: input.column, offset: input.start },
|
|
200
|
+
end: {
|
|
201
|
+
line: input.line,
|
|
202
|
+
column: input.column + input.text.length,
|
|
203
|
+
offset: input.end
|
|
204
|
+
}
|
|
94
205
|
}
|
|
206
|
+
};
|
|
207
|
+
if (input.isEncodingField) {
|
|
208
|
+
return { ...baseNode, value: input.text };
|
|
95
209
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (let i = 0; i < segments.length; i++) {
|
|
99
|
-
const seg = segments[i];
|
|
100
|
-
if (!seg) {
|
|
101
|
-
throw new Error("Invalid message");
|
|
210
|
+
if (input.fieldIndex === 0) {
|
|
211
|
+
return { ...baseNode, value: input.text };
|
|
102
212
|
}
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
213
|
+
if (input.text.includes(input.context.delimiters.component)) {
|
|
214
|
+
return {
|
|
215
|
+
...baseNode,
|
|
216
|
+
delimiter: input.context.delimiters.component,
|
|
217
|
+
children: this.parseComponents(input)
|
|
218
|
+
};
|
|
106
219
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
activeDelimiters
|
|
220
|
+
return { ...baseNode, value: input.text };
|
|
221
|
+
}
|
|
222
|
+
parseComponents(input) {
|
|
223
|
+
const components = this.splitByDelimiter(
|
|
224
|
+
input.text,
|
|
225
|
+
input.context.delimiters.component
|
|
114
226
|
);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
227
|
+
const nodes = [];
|
|
228
|
+
for (let i = 0; i < components.length; i++) {
|
|
229
|
+
const comp = components[i];
|
|
230
|
+
if (!comp) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const componentNode = this.componentParser.parse({
|
|
234
|
+
type: "component",
|
|
235
|
+
text: comp.value,
|
|
236
|
+
start: input.start + comp.start,
|
|
237
|
+
end: input.start + comp.end,
|
|
238
|
+
index: i,
|
|
239
|
+
componentIndex: i,
|
|
240
|
+
line: input.line,
|
|
241
|
+
column: input.column + comp.start,
|
|
242
|
+
context: input.context
|
|
243
|
+
});
|
|
244
|
+
if (componentNode) {
|
|
245
|
+
nodes.push(componentNode);
|
|
118
246
|
}
|
|
119
|
-
messageNode.children.push(segmentNode);
|
|
120
247
|
}
|
|
121
|
-
|
|
248
|
+
return nodes;
|
|
122
249
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
children: [],
|
|
136
|
-
position: {
|
|
137
|
-
start: { line, column: 1, offset: segmentStart },
|
|
138
|
-
end: { line, column: segmentText.length + 1, offset: segmentEnd }
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
for (let i = 0; i < fields.length; i++) {
|
|
142
|
-
const f = fields[i];
|
|
143
|
-
if (!f) {
|
|
144
|
-
throw new Error("Invalid message");
|
|
250
|
+
splitByDelimiter(text, delimiter) {
|
|
251
|
+
const result = [];
|
|
252
|
+
let lastIndex = 0;
|
|
253
|
+
let index = text.indexOf(delimiter);
|
|
254
|
+
while (index !== -1) {
|
|
255
|
+
result.push({
|
|
256
|
+
value: text.slice(lastIndex, index),
|
|
257
|
+
start: lastIndex,
|
|
258
|
+
end: index
|
|
259
|
+
});
|
|
260
|
+
lastIndex = index + delimiter.length;
|
|
261
|
+
index = text.indexOf(delimiter, lastIndex);
|
|
145
262
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
f.start + 1
|
|
153
|
-
) : createFieldNode(
|
|
154
|
-
f.value,
|
|
155
|
-
i,
|
|
156
|
-
segmentStart + f.start,
|
|
157
|
-
segmentStart + f.end,
|
|
158
|
-
line,
|
|
159
|
-
f.start + 1,
|
|
160
|
-
delimiters
|
|
161
|
-
);
|
|
162
|
-
segmentNode.children.push(node);
|
|
263
|
+
result.push({
|
|
264
|
+
value: text.slice(lastIndex),
|
|
265
|
+
start: lastIndex,
|
|
266
|
+
end: text.length
|
|
267
|
+
});
|
|
268
|
+
return result;
|
|
163
269
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
function createFieldNode(fieldText, fieldIndex, fieldStart, fieldEnd, line, column, delimiters) {
|
|
178
|
-
const node = {
|
|
179
|
-
type: "field",
|
|
180
|
-
index: fieldIndex,
|
|
181
|
-
position: {
|
|
182
|
-
start: { line, column, offset: fieldStart },
|
|
183
|
-
end: { line, column: column + fieldText.length, offset: fieldEnd }
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/pipeline/core/parsers/segment.ts
|
|
273
|
+
var SegmentParser = class {
|
|
274
|
+
segmentType = "DEFAULT";
|
|
275
|
+
fieldParser = new FieldParser();
|
|
276
|
+
canParse(input) {
|
|
277
|
+
return input.type === "segment" && input.text.trim().length > 0;
|
|
278
|
+
}
|
|
279
|
+
parse(input) {
|
|
280
|
+
if (!this.canParse(input)) {
|
|
281
|
+
return null;
|
|
184
282
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
283
|
+
const fields = splitByString(input.text, input.context.delimiters.field);
|
|
284
|
+
if (!fields[0] || fields.length === 0) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
const segmentNode = {
|
|
288
|
+
type: "segment",
|
|
289
|
+
name: fields[0].value,
|
|
290
|
+
index: input.index,
|
|
291
|
+
delimiter: input.context.delimiters.field,
|
|
292
|
+
children: [],
|
|
293
|
+
position: {
|
|
294
|
+
start: { line: input.line, column: 1, offset: input.start },
|
|
295
|
+
end: {
|
|
296
|
+
line: input.line,
|
|
297
|
+
column: input.text.length + 1,
|
|
298
|
+
offset: input.end
|
|
299
|
+
}
|
|
300
|
+
}
|
|
197
301
|
};
|
|
302
|
+
segmentNode.children = this.parseFields(fields, input);
|
|
303
|
+
return segmentNode;
|
|
198
304
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
305
|
+
parseFields(fields, input) {
|
|
306
|
+
const children = [];
|
|
307
|
+
for (let i = 0; i < fields.length; i++) {
|
|
308
|
+
const field = fields[i];
|
|
309
|
+
if (!field) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const fieldNode = this.fieldParser.parse({
|
|
313
|
+
type: "field",
|
|
314
|
+
text: field.value,
|
|
315
|
+
start: input.start + field.start,
|
|
316
|
+
end: input.start + field.end,
|
|
317
|
+
index: i,
|
|
318
|
+
fieldIndex: i,
|
|
319
|
+
line: input.line,
|
|
320
|
+
column: field.start + 1,
|
|
321
|
+
context: input.context
|
|
322
|
+
});
|
|
323
|
+
if (fieldNode) {
|
|
324
|
+
children.push(fieldNode);
|
|
325
|
+
}
|
|
208
326
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
327
|
+
return children;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
// src/pipeline/core/parsers/segment-msh.ts
|
|
332
|
+
var MSHSegmentParser = class {
|
|
333
|
+
segmentType = "MSH";
|
|
334
|
+
fieldParser = new FieldParser();
|
|
335
|
+
canParse(input) {
|
|
336
|
+
return input.type === "segment" && input.segmentType === "MSH";
|
|
337
|
+
}
|
|
338
|
+
parse(input) {
|
|
339
|
+
if (!this.canParse(input)) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
const fields = this.extractMSHFields(
|
|
343
|
+
input.text,
|
|
344
|
+
input.context.delimiters.field
|
|
345
|
+
);
|
|
346
|
+
const segmentNode = {
|
|
347
|
+
type: "segment",
|
|
348
|
+
name: "MSH",
|
|
349
|
+
index: input.index,
|
|
350
|
+
delimiter: input.context.delimiters.field,
|
|
351
|
+
children: [],
|
|
213
352
|
position: {
|
|
214
|
-
start: { line, column:
|
|
353
|
+
start: { line: input.line, column: 1, offset: input.start },
|
|
215
354
|
end: {
|
|
216
|
-
line,
|
|
217
|
-
column:
|
|
218
|
-
offset:
|
|
355
|
+
line: input.line,
|
|
356
|
+
column: input.text.length + 1,
|
|
357
|
+
offset: input.end
|
|
219
358
|
}
|
|
220
359
|
}
|
|
221
360
|
};
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
361
|
+
segmentNode.children = this.parseFields(fields, input);
|
|
362
|
+
return segmentNode;
|
|
363
|
+
}
|
|
364
|
+
extractMSHFields(segmentText, fieldDelimiter) {
|
|
365
|
+
const msh1 = segmentText[3] ?? "";
|
|
366
|
+
const msh2 = segmentText.slice(4, 8) ?? "";
|
|
367
|
+
const rest = segmentText.slice(9);
|
|
368
|
+
const restFields = splitByString(rest, fieldDelimiter);
|
|
369
|
+
return [
|
|
370
|
+
{ value: "MSH", start: 0, end: 3 },
|
|
371
|
+
{ value: msh1, start: 3, end: 4 },
|
|
372
|
+
{ value: msh2, start: 4, end: 8, isEncodingField: true },
|
|
373
|
+
...restFields.map((f) => ({
|
|
374
|
+
value: f.value ?? "",
|
|
375
|
+
start: 9 + (f.start ?? 0),
|
|
376
|
+
end: 9 + (f.end ?? 0)
|
|
377
|
+
}))
|
|
378
|
+
];
|
|
379
|
+
}
|
|
380
|
+
parseFields(fields, input) {
|
|
381
|
+
const children = [];
|
|
382
|
+
for (let i = 0; i < fields.length; i++) {
|
|
383
|
+
const field = fields[i];
|
|
384
|
+
if (!field) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
const fieldNode = this.fieldParser.parse({
|
|
388
|
+
type: "field",
|
|
389
|
+
text: field.value,
|
|
390
|
+
start: input.start + field.start,
|
|
391
|
+
end: input.start + field.end,
|
|
392
|
+
index: i,
|
|
393
|
+
fieldIndex: i,
|
|
394
|
+
line: input.line,
|
|
395
|
+
column: field.start + 1,
|
|
396
|
+
isEncodingField: field.isEncodingField,
|
|
397
|
+
context: input.context
|
|
233
398
|
});
|
|
234
|
-
|
|
235
|
-
|
|
399
|
+
if (fieldNode) {
|
|
400
|
+
children.push(fieldNode);
|
|
401
|
+
}
|
|
236
402
|
}
|
|
403
|
+
return children;
|
|
237
404
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
start: {
|
|
251
|
-
line,
|
|
252
|
-
column: column + s.start,
|
|
253
|
-
offset: componentStart + s.start
|
|
254
|
-
},
|
|
255
|
-
end: {
|
|
256
|
-
line,
|
|
257
|
-
column: column + s.start + s.value.length,
|
|
258
|
-
offset: componentStart + s.end
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
// src/pipeline/core/registry.ts
|
|
408
|
+
var SegmentParserRegistry = class {
|
|
409
|
+
parsers = /* @__PURE__ */ new Map();
|
|
410
|
+
defaultParser;
|
|
411
|
+
constructor(customParsers) {
|
|
412
|
+
this.defaultParser = new SegmentParser();
|
|
413
|
+
this.register(new MSHSegmentParser());
|
|
414
|
+
if (customParsers) {
|
|
415
|
+
for (const [, parser] of customParsers) {
|
|
416
|
+
this.register(parser);
|
|
259
417
|
}
|
|
260
418
|
}
|
|
261
|
-
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Register a segment parser for a specific segment type
|
|
422
|
+
*/
|
|
423
|
+
register(parser) {
|
|
424
|
+
this.parsers.set(parser.segmentType, parser);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get the appropriate parser for a segment type
|
|
428
|
+
*/
|
|
429
|
+
getParser(segmentType) {
|
|
430
|
+
return this.parsers.get(segmentType) || this.defaultParser;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Check if a parser is registered for a segment type
|
|
434
|
+
*/
|
|
435
|
+
hasParser(segmentType) {
|
|
436
|
+
return this.parsers.has(segmentType);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Unregister a parser for a segment type
|
|
440
|
+
*/
|
|
441
|
+
unregister(segmentType) {
|
|
442
|
+
return this.parsers.delete(segmentType);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Get all registered segment types
|
|
446
|
+
*/
|
|
447
|
+
getRegisteredTypes() {
|
|
448
|
+
return Array.from(this.parsers.keys());
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
// src/pipeline/core/message.ts
|
|
453
|
+
var HL7MessageParser = class {
|
|
454
|
+
segmentRegistry;
|
|
455
|
+
options;
|
|
456
|
+
constructor(options = {}) {
|
|
457
|
+
this.options = options;
|
|
458
|
+
this.segmentRegistry = new SegmentParserRegistry(options.customParsers);
|
|
459
|
+
}
|
|
460
|
+
canParse(input) {
|
|
461
|
+
return input.type === "message" && input.text.trim().length > 0;
|
|
462
|
+
}
|
|
463
|
+
parse(input) {
|
|
464
|
+
if (!this.canParse(input)) {
|
|
465
|
+
return EMPTY_MESSAGE;
|
|
466
|
+
}
|
|
467
|
+
const context = this.createParseContext(input.text);
|
|
468
|
+
const segments = splitByString(input.text, context.delimiters.segment);
|
|
469
|
+
const messageNode = {
|
|
470
|
+
type: "root",
|
|
471
|
+
delimiter: context.delimiters.segment,
|
|
472
|
+
children: [],
|
|
473
|
+
position: {
|
|
474
|
+
start: { line: 1, column: 1, offset: 0 },
|
|
475
|
+
end: {
|
|
476
|
+
line: segments.length,
|
|
477
|
+
column: (segments.at(-1)?.value.length ?? 0) + 1,
|
|
478
|
+
offset: input.text.length
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
let currentLine = 1;
|
|
483
|
+
for (let i = 0; i < segments.length; i++) {
|
|
484
|
+
const seg = segments[i];
|
|
485
|
+
const hasContent = seg?.value.trim();
|
|
486
|
+
if (!seg) {
|
|
487
|
+
currentLine++;
|
|
488
|
+
continue;
|
|
489
|
+
}
|
|
490
|
+
if (!hasContent) {
|
|
491
|
+
currentLine++;
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
const segmentNode = this.parseSegment(seg, currentLine, i, context);
|
|
495
|
+
if (segmentNode) {
|
|
496
|
+
messageNode.children = messageNode.children || [];
|
|
497
|
+
messageNode.children.push(segmentNode);
|
|
498
|
+
}
|
|
499
|
+
currentLine++;
|
|
500
|
+
}
|
|
501
|
+
return this.applyValidationHooks(messageNode, context);
|
|
502
|
+
}
|
|
503
|
+
createParseContext(rawMessage) {
|
|
504
|
+
const baseDelimiters = {
|
|
505
|
+
...DEFAULT_DELIMITERS,
|
|
506
|
+
...this.options.delimiters
|
|
507
|
+
};
|
|
508
|
+
let activeDelimiters = baseDelimiters;
|
|
509
|
+
if (this.options.autoDetectDelimiters !== false) {
|
|
510
|
+
const detected = detectDelimitersFromMSH(
|
|
511
|
+
rawMessage,
|
|
512
|
+
baseDelimiters.segment
|
|
513
|
+
);
|
|
514
|
+
activeDelimiters = { ...baseDelimiters, ...detected };
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
delimiters: activeDelimiters,
|
|
518
|
+
options: this.options,
|
|
519
|
+
currentLine: 1,
|
|
520
|
+
totalOffset: 0
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
parseSegment(segment, line, index, context) {
|
|
524
|
+
const segmentType = segment.value.slice(0, 3);
|
|
525
|
+
const parser = this.segmentRegistry.getParser(segmentType);
|
|
526
|
+
return parser.parse({
|
|
527
|
+
type: "segment",
|
|
528
|
+
text: segment.value,
|
|
529
|
+
start: segment.start,
|
|
530
|
+
end: segment.end,
|
|
531
|
+
index,
|
|
532
|
+
segmentType,
|
|
533
|
+
line,
|
|
534
|
+
context
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
applyValidationHooks(node, context) {
|
|
538
|
+
if (!this.options.validationHooks) {
|
|
539
|
+
return node;
|
|
540
|
+
}
|
|
541
|
+
for (const hook of this.options.validationHooks) {
|
|
542
|
+
const result = hook.validate(node, context);
|
|
543
|
+
if (!result.isValid) {
|
|
544
|
+
throw new Error(result.errors?.[0] ?? "Validation error");
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return node;
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// src/parser.ts
|
|
552
|
+
function fromHL7v2Pipeline(rawMessage, options = {}) {
|
|
553
|
+
const parser = new HL7MessageParser(options);
|
|
554
|
+
const result = parser.parse({
|
|
555
|
+
type: "message",
|
|
556
|
+
text: rawMessage,
|
|
557
|
+
start: 0,
|
|
558
|
+
end: rawMessage.length,
|
|
559
|
+
index: 0,
|
|
560
|
+
context: {
|
|
561
|
+
delimiters: options.delimiters || {},
|
|
562
|
+
options,
|
|
563
|
+
currentLine: 1,
|
|
564
|
+
totalOffset: 0
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
return result || EMPTY_MESSAGE;
|
|
262
568
|
}
|
|
263
569
|
|
|
264
570
|
// src/processor.ts
|
|
265
571
|
var hl7v2Parser = function(options = {}) {
|
|
266
572
|
const self = this;
|
|
267
573
|
function parser(value) {
|
|
268
|
-
return
|
|
574
|
+
return fromHL7v2Pipeline(value, options);
|
|
269
575
|
}
|
|
270
576
|
self.parser = parser;
|
|
271
577
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/parser.ts","../src/processor.ts"],"sourcesContent":["import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { HL7v2Delimiters } from './types';\n\n/**\n * Default HL7v2 delimiters\n */\nexport const DEFAULT_DELIMITERS: HL7v2Delimiters = {\n field: '|',\n component: '^',\n subcomponent: '&',\n repetition: '~',\n escape: '\\\\',\n segment: '\\r', // default to \\r\n};\n\n/**\n * Empty message node\n */\nexport const EMPTY_MESSAGE: HL7v2Node = {\n type: 'message',\n delimiter: DEFAULT_DELIMITERS.segment,\n children: [],\n position: {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n },\n};\n","import type { HL7v2Delimiters } from './types';\n\n/**\n * Auto-detect custom delimiters from MSH-1 and MSH-2\n */\nexport function detectDelimitersFromMSH(\n raw: string,\n segmentDelimiter: string\n): Partial<HL7v2Delimiters> {\n const firstLineEnd = raw.indexOf(segmentDelimiter);\n const mshSegment = firstLineEnd >= 0 ? raw.slice(0, firstLineEnd) : raw;\n\n if (!mshSegment.startsWith('MSH')) {\n return {};\n }\n\n const fieldDelimiter = mshSegment[3];\n\n if (!fieldDelimiter) {\n return {};\n }\n\n const encodingChars = mshSegment.slice(4, 8);\n\n if (encodingChars.length !== 4) {\n return {};\n }\n\n return {\n field: fieldDelimiter,\n component: encodingChars[0],\n repetition: encodingChars[1],\n escape: encodingChars[2],\n subcomponent: encodingChars[3],\n segment: segmentDelimiter,\n };\n}\n\n/**\n * Split text by string delimiter, preserving positions\n */\nexport function splitByString(\n text: string,\n delimiter: string\n): Array<{ value: string; start: number; end: number }> {\n const result: { value: string; start: number; end: number }[] = [];\n let lastIndex = 0;\n let index = text.indexOf(delimiter);\n\n while (index !== -1) {\n result.push({\n value: text.slice(lastIndex, index),\n start: lastIndex,\n end: index,\n });\n lastIndex = index + delimiter.length;\n index = text.indexOf(delimiter, lastIndex);\n }\n\n result.push({\n value: text.slice(lastIndex),\n start: lastIndex,\n end: text.length,\n });\n return result;\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport { DEFAULT_DELIMITERS, EMPTY_MESSAGE } from './constants';\nimport type { HL7v2Delimiters, ParseOptions } from './types';\nimport { detectDelimitersFromMSH, splitByString } from './utils';\n\n/**\n * Parse an HL7v2 message into a Unist-compatible DOM tree.\n * Tracks lines/columns and preserves delimiters for round-tripping.\n */\nexport function fromHL7v2(\n rawMessage: string,\n options: ParseOptions = {}\n): HL7v2Node {\n if (!rawMessage.trim()) {\n return EMPTY_MESSAGE;\n }\n\n // Merge user-defined delimiters with defaults\n const baseDelimiters: HL7v2Delimiters = {\n ...DEFAULT_DELIMITERS,\n ...options.delimiters,\n };\n\n // Auto-detect from MSH-2 if enabled\n let activeDelimiters = baseDelimiters;\n if (options.autoDetectDelimiters !== false) {\n const detected = detectDelimitersFromMSH(\n rawMessage,\n baseDelimiters.segment\n );\n activeDelimiters = { ...baseDelimiters, ...detected };\n }\n\n // Split into segments\n const segments = splitByString(rawMessage, activeDelimiters.segment);\n\n const messageNode: HL7v2Node = {\n type: 'message',\n delimiter: activeDelimiters.segment,\n children: [],\n position: {\n start: { line: 1, column: 1, offset: 0 },\n end: {\n line: segments.length,\n column: (segments.at(-1)?.value.length ?? 0) + 1,\n offset: rawMessage.length,\n },\n },\n };\n\n let currentLine = 1;\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i];\n\n if (!seg) {\n // TODO: Handle this case with a better error message\n throw new Error('Invalid message');\n }\n\n if (!seg.value.trim()) {\n currentLine++;\n continue;\n }\n\n const segmentNode = parseSegment(\n seg.value,\n seg.start,\n seg.end,\n currentLine,\n i,\n activeDelimiters\n );\n if (segmentNode) {\n if (!messageNode.children) {\n messageNode.children = [];\n }\n\n messageNode.children.push(segmentNode);\n }\n currentLine++;\n }\n\n return messageNode;\n}\n\n/**\n * Parse a single segment into fields and components\n */\nfunction parseSegment(\n segmentText: string,\n segmentStart: number,\n segmentEnd: number,\n line: number,\n segmentIndex: number,\n delimiters: HL7v2Delimiters\n): HL7v2Node | null {\n // 1. Split into fields\n const fields = splitByString(segmentText, delimiters.field);\n\n // 2. If no fields, return null\n if (!fields[0] || fields.length === 0) {\n return null;\n }\n\n const segmentNode: HL7v2Node = {\n type: 'segment',\n name: fields[0].value,\n index: segmentIndex,\n delimiter: delimiters.field,\n children: [],\n position: {\n start: { line, column: 1, offset: segmentStart },\n end: { line, column: segmentText.length + 1, offset: segmentEnd },\n },\n };\n\n for (let i = 0; i < fields.length; i++) {\n const f = fields[i];\n\n if (!f) {\n // TODO: Handle this case with a better error message\n throw new Error('Invalid message');\n }\n\n // Create header node for first field (segment identifier), field node for others\n const node =\n i === 0\n ? createHeaderNode(\n f.value,\n i,\n segmentStart + f.start,\n segmentStart + f.end,\n line,\n f.start + 1\n )\n : createFieldNode(\n f.value,\n i,\n segmentStart + f.start,\n segmentStart + f.end,\n line,\n f.start + 1,\n delimiters\n );\n\n // biome-ignore lint/style/noNonNullAssertion: This is defined always\n segmentNode.children!.push(node);\n }\n\n return segmentNode;\n}\n\n/**\n * Create a header node for segment identifiers\n */\nfunction createHeaderNode(\n headerText: string,\n headerIndex: number,\n headerStart: number,\n headerEnd: number,\n line: number,\n column: number\n): HL7v2Node {\n return {\n type: 'header',\n index: headerIndex,\n position: {\n start: { line, column, offset: headerStart },\n end: { line, column: column + headerText.length, offset: headerEnd },\n },\n value: headerText,\n };\n}\n\n/**\n * Create a field node, parsing components if needed\n */\nfunction createFieldNode(\n fieldText: string,\n fieldIndex: number,\n fieldStart: number,\n fieldEnd: number,\n line: number,\n column: number,\n delimiters: HL7v2Delimiters\n): HL7v2Node {\n const node: HL7v2Node = {\n type: 'field',\n index: fieldIndex,\n position: {\n start: { line, column, offset: fieldStart },\n end: { line, column: column + fieldText.length, offset: fieldEnd },\n },\n };\n\n if (fieldText.includes(delimiters.component)) {\n return {\n ...node,\n delimiter: delimiters.component,\n children: parseComponents(\n fieldText,\n fieldStart,\n line,\n column,\n delimiters\n ),\n };\n }\n\n return { ...node, value: fieldText };\n}\n\n/**\n * Parse components within a field\n */\nfunction parseComponents(\n fieldText: string,\n fieldStart: number,\n line: number,\n column: number,\n delimiters: HL7v2Delimiters\n): HL7v2Node[] {\n const comps = splitByString(fieldText, delimiters.component);\n const nodes: HL7v2Node[] = [];\n\n for (let i = 0; i < comps.length; i++) {\n const c = comps[i];\n\n if (!c) {\n // TODO: Handle this case with a better error message\n throw new Error('Invalid message');\n }\n\n const startColumn = column + c.start;\n const base: HL7v2Node = {\n type: 'component',\n index: i,\n position: {\n start: { line, column: startColumn, offset: fieldStart + c.start },\n end: {\n line,\n column: startColumn + c.value.length,\n offset: fieldStart + c.end,\n },\n },\n };\n\n if (c.value.includes(delimiters.subcomponent)) {\n nodes.push({\n ...base,\n delimiter: delimiters.subcomponent,\n children: parseSubcomponents(\n c.value,\n fieldStart + c.start,\n line,\n startColumn,\n delimiters\n ),\n });\n } else {\n nodes.push({ ...base, value: c.value });\n }\n }\n\n return nodes;\n}\n\n/**\n * Parse subcomponents within a component\n */\nfunction parseSubcomponents(\n componentText: string,\n componentStart: number,\n line: number,\n column: number,\n delimiters: HL7v2Delimiters\n): HL7v2Node[] {\n const subs = splitByString(componentText, delimiters.subcomponent);\n\n if (!subs[0]) {\n // TODO: Handle this case with a better error message\n throw new Error('Invalid message');\n }\n\n return subs.map((s, i) => ({\n type: 'subcomponent',\n index: i,\n value: s.value,\n position: {\n start: {\n line,\n column: column + s.start,\n offset: componentStart + s.start,\n },\n end: {\n line,\n column: column + s.start + s.value.length,\n offset: componentStart + s.end,\n },\n },\n }));\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { Plugin } from 'unified';\nimport { fromHL7v2 } from './parser';\nimport type { ParseOptions } from './types';\n\nconst hl7v2Parser: Plugin<[ParseOptions?], undefined, HL7v2Node> = function (\n options: ParseOptions = {}\n): void {\n // biome-ignore lint/complexity/noUselessThisAlias: this is a plugin\n const self = this;\n\n function parser(this: unknown, value: string): HL7v2Node {\n return fromHL7v2(value, options);\n }\n\n self.parser = parser;\n};\n\nexport default hl7v2Parser;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/pipeline/utils/index.ts","../src/pipeline/core/parsers/subcomponent.ts","../src/pipeline/core/parsers/component.ts","../src/pipeline/core/parsers/field.ts","../src/pipeline/core/parsers/segment.ts","../src/pipeline/core/parsers/segment-msh.ts","../src/pipeline/core/registry.ts","../src/pipeline/core/message.ts","../src/parser.ts","../src/processor.ts"],"sourcesContent":["import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { HL7v2Delimiters } from './types';\n\n/**\n * Default HL7v2 delimiters\n */\nexport const DEFAULT_DELIMITERS: HL7v2Delimiters = {\n field: '|',\n component: '^',\n subcomponent: '&',\n repetition: '~',\n escape: '\\\\',\n segment: '\\n', // default to \\n (more common in modern systems)\n};\n\n/**\n * Empty message node\n */\nexport const EMPTY_MESSAGE: HL7v2Node = {\n type: 'root',\n delimiter: DEFAULT_DELIMITERS.segment,\n children: [],\n position: {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n },\n};\n","import type { HL7v2Delimiters } from '../../types';\n\n/**\n * Auto-detect custom delimiters from MSH-1 and MSH-2\n */\nexport function detectDelimitersFromMSH(\n raw: string,\n segmentDelimiter: string\n): Partial<HL7v2Delimiters> {\n const detectedSegmentDelimiter = detectSegmentDelimiter(\n raw,\n segmentDelimiter\n );\n\n const firstLineEnd = raw.indexOf(detectedSegmentDelimiter);\n\n // If there are multiple segments, use the first one. Otherwise, use the entire message.\n const mshSegment = firstLineEnd >= 0 ? raw.slice(0, firstLineEnd) : raw;\n\n if (!mshSegment.startsWith('MSH')) {\n return {};\n }\n\n const fieldDelimiter = mshSegment[3];\n\n if (!fieldDelimiter) {\n return {};\n }\n\n const encodingChars = mshSegment.slice(4, 8);\n\n if (encodingChars.length !== 4) {\n return {};\n }\n\n return {\n field: fieldDelimiter,\n component: encodingChars[0],\n repetition: encodingChars[1],\n escape: encodingChars[2],\n subcomponent: encodingChars[3],\n segment: detectedSegmentDelimiter,\n };\n}\n\n/**\n * Split text by string delimiter, preserving positions\n */\nexport function splitByString(\n text: string,\n delimiter: string\n): Array<{ value: string; start: number; end: number }> {\n const result: { value: string; start: number; end: number }[] = [];\n let lastIndex = 0;\n let index = text.indexOf(delimiter);\n\n while (index !== -1) {\n result.push({\n value: text.slice(lastIndex, index),\n start: lastIndex,\n end: index,\n });\n lastIndex = index + delimiter.length;\n index = text.indexOf(delimiter, lastIndex);\n }\n\n result.push({\n value: text.slice(lastIndex),\n start: lastIndex,\n end: text.length,\n });\n return result;\n}\n\n/**\n * Detect the segment delimiter used in the message\n */\nexport function detectSegmentDelimiter(\n message: string,\n fallback = '\\n'\n): string {\n // Look for common segment delimiters in order of preference\n const possibleDelimiters = ['\\r\\n', '\\n', '\\r'];\n\n for (const delimiter of possibleDelimiters) {\n if (message.includes(delimiter)) {\n return delimiter;\n }\n }\n\n // Use the provided fallback if no delimiter found\n return fallback;\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type {\n ISubcomponentParser,\n SubcomponentParseInput,\n} from '../../interfaces';\n\n/**\n * Default subcomponent parser - leaf level parser\n */\nexport class SubcomponentParser implements ISubcomponentParser {\n canParse(input: SubcomponentParseInput): boolean {\n return input.type === 'subcomponent';\n }\n\n parse(input: SubcomponentParseInput): HL7v2Node | null {\n if (!this.canParse(input)) {\n return null;\n }\n\n return {\n type: 'subcomponent',\n index: input.subcomponentIndex,\n value: input.text,\n position: {\n start: { line: input.line, column: input.column, offset: input.start },\n end: {\n line: input.line,\n column: input.column + input.text.length,\n offset: input.end,\n },\n },\n };\n }\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { ComponentParseInput, IComponentParser } from '../../interfaces';\nimport { SubcomponentParser } from './subcomponent';\n\n/**\n * Default component parser that handles subcomponents\n */\nexport class ComponentParser implements IComponentParser {\n private subcomponentParser = new SubcomponentParser();\n\n canParse(input: ComponentParseInput): boolean {\n return input.type === 'component';\n }\n\n parse(input: ComponentParseInput): HL7v2Node | null {\n if (!this.canParse(input)) {\n return null;\n }\n\n const baseNode: HL7v2Node = {\n type: 'component',\n index: input.componentIndex,\n position: {\n start: { line: input.line, column: input.column, offset: input.start },\n end: {\n line: input.line,\n column: input.column + input.text.length,\n offset: input.end,\n },\n },\n };\n\n // Check if component contains subcomponents\n if (input.text.includes(input.context.delimiters.subcomponent)) {\n return {\n ...baseNode,\n delimiter: input.context.delimiters.subcomponent,\n children: this.parseSubcomponents(input),\n };\n }\n\n return { ...baseNode, value: input.text };\n }\n\n private parseSubcomponents(input: ComponentParseInput): HL7v2Node[] {\n const subcomponents = this.splitByDelimiter(\n input.text,\n input.context.delimiters.subcomponent\n );\n const nodes: HL7v2Node[] = [];\n\n for (let i = 0; i < subcomponents.length; i++) {\n const sub = subcomponents[i];\n if (!sub) {\n continue;\n }\n\n const subNode = this.subcomponentParser.parse({\n type: 'subcomponent',\n text: sub.value,\n start: input.start + sub.start,\n end: input.start + sub.end,\n index: i,\n subcomponentIndex: i,\n line: input.line,\n column: input.column + sub.start,\n context: input.context,\n });\n\n if (subNode) {\n nodes.push(subNode);\n }\n }\n\n return nodes;\n }\n\n private splitByDelimiter(\n text: string,\n delimiter: string\n ): Array<{ value: string; start: number; end: number }> {\n const result: { value: string; start: number; end: number }[] = [];\n let lastIndex = 0;\n let index = text.indexOf(delimiter);\n\n while (index !== -1) {\n result.push({\n value: text.slice(lastIndex, index),\n start: lastIndex,\n end: index,\n });\n lastIndex = index + delimiter.length;\n index = text.indexOf(delimiter, lastIndex);\n }\n\n result.push({\n value: text.slice(lastIndex),\n start: lastIndex,\n end: text.length,\n });\n\n return result;\n }\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { FieldParseInput, IFieldParser } from '../../interfaces';\nimport { ComponentParser } from './component';\n\n/**\n * Default field parser that handles components\n */\nexport class FieldParser implements IFieldParser {\n private componentParser = new ComponentParser();\n\n canParse(input: FieldParseInput): boolean {\n return input.type === 'field';\n }\n\n parse(input: FieldParseInput): HL7v2Node | null {\n if (!this.canParse(input)) {\n return null;\n }\n\n const baseNode: HL7v2Node = {\n type: input.fieldIndex === 0 ? 'header' : 'field',\n index: input.fieldIndex,\n position: {\n start: { line: input.line, column: input.column, offset: input.start },\n end: {\n line: input.line,\n column: input.column + input.text.length,\n offset: input.end,\n },\n },\n };\n\n // Handle encoding field (MSH-2) - no component parsing\n if (input.isEncodingField) {\n return { ...baseNode, value: input.text };\n }\n\n // Handle header (first field of segment)\n if (input.fieldIndex === 0) {\n return { ...baseNode, value: input.text };\n }\n\n // Check if field contains components\n if (input.text.includes(input.context.delimiters.component)) {\n return {\n ...baseNode,\n delimiter: input.context.delimiters.component,\n children: this.parseComponents(input),\n };\n }\n\n return { ...baseNode, value: input.text };\n }\n\n private parseComponents(input: FieldParseInput): HL7v2Node[] {\n const components = this.splitByDelimiter(\n input.text,\n input.context.delimiters.component\n );\n const nodes: HL7v2Node[] = [];\n\n for (let i = 0; i < components.length; i++) {\n const comp = components[i];\n if (!comp) {\n continue;\n }\n\n const componentNode = this.componentParser.parse({\n type: 'component',\n text: comp.value,\n start: input.start + comp.start,\n end: input.start + comp.end,\n index: i,\n componentIndex: i,\n line: input.line,\n column: input.column + comp.start,\n context: input.context,\n });\n\n if (componentNode) {\n nodes.push(componentNode);\n }\n }\n\n return nodes;\n }\n\n private splitByDelimiter(\n text: string,\n delimiter: string\n ): Array<{ value: string; start: number; end: number }> {\n const result: { value: string; start: number; end: number }[] = [];\n let lastIndex = 0;\n let index = text.indexOf(delimiter);\n\n while (index !== -1) {\n result.push({\n value: text.slice(lastIndex, index),\n start: lastIndex,\n end: index,\n });\n lastIndex = index + delimiter.length;\n index = text.indexOf(delimiter, lastIndex);\n }\n\n result.push({\n value: text.slice(lastIndex),\n start: lastIndex,\n end: text.length,\n });\n\n return result;\n }\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { ISegmentParser, SegmentParseInput } from '../../interfaces';\nimport { splitByString } from '../../utils';\nimport { FieldParser } from './field';\n\n/**\n * Default segment parser for standard HL7v2 segments\n */\nexport class SegmentParser implements ISegmentParser {\n segmentType = 'DEFAULT';\n private fieldParser = new FieldParser();\n\n canParse(input: SegmentParseInput): boolean {\n return input.type === 'segment' && input.text.trim().length > 0;\n }\n\n parse(input: SegmentParseInput): HL7v2Node | null {\n if (!this.canParse(input)) {\n return null;\n }\n\n const fields = splitByString(input.text, input.context.delimiters.field);\n if (!fields[0] || fields.length === 0) {\n return null;\n }\n\n const segmentNode: HL7v2Node = {\n type: 'segment',\n name: fields[0].value,\n index: input.index,\n delimiter: input.context.delimiters.field,\n children: [],\n position: {\n start: { line: input.line, column: 1, offset: input.start },\n end: {\n line: input.line,\n column: input.text.length + 1,\n offset: input.end,\n },\n },\n };\n\n segmentNode.children = this.parseFields(fields, input);\n return segmentNode;\n }\n\n private parseFields(\n fields: Array<{ value: string; start: number; end: number }>,\n input: SegmentParseInput\n ): HL7v2Node[] {\n const children: HL7v2Node[] = [];\n\n for (let i = 0; i < fields.length; i++) {\n const field = fields[i];\n if (!field) {\n continue;\n }\n\n const fieldNode = this.fieldParser.parse({\n type: 'field',\n text: field.value,\n start: input.start + field.start,\n end: input.start + field.end,\n index: i,\n fieldIndex: i,\n line: input.line,\n column: field.start + 1,\n context: input.context,\n });\n\n if (fieldNode) {\n children.push(fieldNode);\n }\n }\n\n return children;\n }\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { ISegmentParser, SegmentParseInput } from '../../interfaces';\nimport { splitByString } from '../../utils';\nimport { FieldParser } from './field';\n\n/**\n * Specialized parser for MSH segments with special field handling\n */\nexport class MSHSegmentParser implements ISegmentParser {\n segmentType = 'MSH';\n private fieldParser = new FieldParser();\n\n canParse(input: SegmentParseInput): boolean {\n return input.type === 'segment' && input.segmentType === 'MSH';\n }\n\n parse(input: SegmentParseInput): HL7v2Node | null {\n if (!this.canParse(input)) {\n return null;\n }\n\n const fields = this.extractMSHFields(\n input.text,\n input.context.delimiters.field\n );\n\n const segmentNode: HL7v2Node = {\n type: 'segment',\n name: 'MSH',\n index: input.index,\n delimiter: input.context.delimiters.field,\n children: [],\n position: {\n start: { line: input.line, column: 1, offset: input.start },\n end: {\n line: input.line,\n column: input.text.length + 1,\n offset: input.end,\n },\n },\n };\n\n segmentNode.children = this.parseFields(fields, input);\n return segmentNode;\n }\n\n private extractMSHFields(\n segmentText: string,\n fieldDelimiter: string\n ): Array<{\n value: string;\n start: number;\n end: number;\n isEncodingField?: boolean;\n }> {\n const msh1 = segmentText[3] ?? '';\n const msh2 = segmentText.slice(4, 8) ?? '';\n const rest = segmentText.slice(9); // Skip the field delimiter at position 8\n const restFields = splitByString(rest, fieldDelimiter);\n\n return [\n { value: 'MSH', start: 0, end: 3 },\n { value: msh1, start: 3, end: 4 },\n { value: msh2, start: 4, end: 8, isEncodingField: true },\n ...restFields.map((f) => ({\n value: f.value ?? '',\n start: 9 + (f.start ?? 0),\n end: 9 + (f.end ?? 0),\n })),\n ];\n }\n\n private parseFields(\n fields: Array<{\n value: string;\n start: number;\n end: number;\n isEncodingField?: boolean;\n }>,\n input: SegmentParseInput\n ): HL7v2Node[] {\n const children: HL7v2Node[] = [];\n\n for (let i = 0; i < fields.length; i++) {\n const field = fields[i];\n if (!field) {\n continue;\n }\n\n const fieldNode = this.fieldParser.parse({\n type: 'field',\n text: field.value,\n start: input.start + field.start,\n end: input.start + field.end,\n index: i,\n fieldIndex: i,\n line: input.line,\n column: field.start + 1,\n isEncodingField: field.isEncodingField,\n context: input.context,\n });\n\n if (fieldNode) {\n children.push(fieldNode);\n }\n }\n\n return children;\n }\n}\n","import type { ISegmentParser } from '../interfaces';\nimport { SegmentParser } from './parsers/segment';\nimport { MSHSegmentParser } from './parsers/segment-msh';\n\n/**\n * Registry for segment parsers with fallback strategy\n */\nexport class SegmentParserRegistry {\n private parsers = new Map<string, ISegmentParser>();\n private defaultParser: ISegmentParser;\n\n constructor(customParsers?: Map<string, ISegmentParser>) {\n this.defaultParser = new SegmentParser();\n\n // Register built-in parsers\n this.register(new MSHSegmentParser());\n\n // Register custom parsers if provided\n if (customParsers) {\n for (const [, parser] of customParsers) {\n this.register(parser);\n }\n }\n }\n\n /**\n * Register a segment parser for a specific segment type\n */\n register(parser: ISegmentParser): void {\n this.parsers.set(parser.segmentType, parser);\n }\n\n /**\n * Get the appropriate parser for a segment type\n */\n getParser(segmentType: string): ISegmentParser {\n return this.parsers.get(segmentType) || this.defaultParser;\n }\n\n /**\n * Check if a parser is registered for a segment type\n */\n hasParser(segmentType: string): boolean {\n return this.parsers.has(segmentType);\n }\n\n /**\n * Unregister a parser for a segment type\n */\n unregister(segmentType: string): boolean {\n return this.parsers.delete(segmentType);\n }\n\n /**\n * Get all registered segment types\n */\n getRegisteredTypes(): string[] {\n return Array.from(this.parsers.keys());\n }\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport { DEFAULT_DELIMITERS, EMPTY_MESSAGE } from '../../constants';\nimport type { HL7v2Delimiters } from '../../types';\nimport type {\n IMessageParser,\n MessageParseInput,\n ParseContext,\n ParseOptions,\n} from '../interfaces';\nimport { detectDelimitersFromMSH, splitByString } from '../utils';\nimport { SegmentParserRegistry } from './registry';\n\n/**\n * Main message parser that orchestrates the parsing pipeline\n */\nexport class HL7MessageParser implements IMessageParser {\n private segmentRegistry: SegmentParserRegistry;\n\n private options: ParseOptions;\n\n constructor(options: ParseOptions = {}) {\n this.options = options;\n this.segmentRegistry = new SegmentParserRegistry(options.customParsers);\n }\n\n canParse(input: MessageParseInput): boolean {\n return input.type === 'message' && input.text.trim().length > 0;\n }\n\n parse(input: MessageParseInput): HL7v2Node | null {\n if (!this.canParse(input)) {\n return EMPTY_MESSAGE;\n }\n\n const context = this.createParseContext(input.text);\n const segments = splitByString(input.text, context.delimiters.segment);\n\n const messageNode: HL7v2Node = {\n type: 'root',\n delimiter: context.delimiters.segment,\n children: [],\n position: {\n start: { line: 1, column: 1, offset: 0 },\n end: {\n line: segments.length,\n column: (segments.at(-1)?.value.length ?? 0) + 1,\n offset: input.text.length,\n },\n },\n };\n\n let currentLine = 1;\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i];\n const hasContent = seg?.value.trim();\n if (!seg) {\n currentLine++;\n continue;\n }\n if (!hasContent) {\n currentLine++;\n continue;\n }\n\n const segmentNode = this.parseSegment(seg, currentLine, i, context);\n if (segmentNode) {\n messageNode.children = messageNode.children || [];\n messageNode.children.push(segmentNode);\n }\n currentLine++;\n }\n\n return this.applyValidationHooks(messageNode, context);\n }\n\n private createParseContext(rawMessage: string): ParseContext {\n // Merge user-defined delimiters with defaults\n const baseDelimiters: HL7v2Delimiters = {\n ...DEFAULT_DELIMITERS,\n ...this.options.delimiters,\n };\n\n // Auto-detect from MSH-2 if enabled\n let activeDelimiters = baseDelimiters;\n if (this.options.autoDetectDelimiters !== false) {\n const detected = detectDelimitersFromMSH(\n rawMessage,\n baseDelimiters.segment\n );\n activeDelimiters = { ...baseDelimiters, ...detected };\n }\n\n return {\n delimiters: activeDelimiters,\n options: this.options,\n currentLine: 1,\n totalOffset: 0,\n };\n }\n\n private parseSegment(\n segment: { value: string; start: number; end: number },\n line: number,\n index: number,\n context: ParseContext\n ): HL7v2Node | null {\n const segmentType = segment.value.slice(0, 3);\n const parser = this.segmentRegistry.getParser(segmentType);\n\n return parser.parse({\n type: 'segment',\n text: segment.value,\n start: segment.start,\n end: segment.end,\n index,\n segmentType,\n line,\n context,\n });\n }\n\n private applyValidationHooks(\n node: HL7v2Node,\n context: ParseContext\n ): HL7v2Node {\n if (!this.options.validationHooks) {\n return node;\n }\n\n for (const hook of this.options.validationHooks) {\n const result = hook.validate(node, context);\n if (!result.isValid) {\n // TODO: Handle validation errors\n throw new Error(result.errors?.[0] ?? 'Validation error');\n }\n }\n\n return node;\n }\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport { EMPTY_MESSAGE } from './constants';\nimport { HL7MessageParser } from './pipeline/core/message';\nimport type { ParseContext, ParseOptions } from './pipeline/interfaces';\n\n/**\n * Pipeline-based HL7v2 parser.\n *\n * This parser is a wrapper around the HL7MessageParser class.\n * It is used to parse HL7v2 messages into an AST.\n *\n * @param rawMessage - The HL7v2 message to parse.\n * @param options - The options for the parser.\n * @returns The parsed HL7v2 message.\n */\nexport function fromHL7v2Pipeline(\n rawMessage: string,\n options: ParseOptions = {}\n): HL7v2Node {\n const parser = new HL7MessageParser(options);\n\n const result = parser.parse({\n type: 'message',\n text: rawMessage,\n start: 0,\n end: rawMessage.length,\n index: 0,\n context: {\n delimiters: options.delimiters || {},\n options,\n currentLine: 1,\n totalOffset: 0,\n } as ParseContext,\n });\n\n return result || EMPTY_MESSAGE;\n}\n","import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';\nimport type { Plugin } from 'unified';\nimport { fromHL7v2Pipeline } from './parser';\nimport type { ParseOptions } from './types';\n\nconst hl7v2Parser: Plugin<[ParseOptions?], undefined, HL7v2Node> = function (\n options: ParseOptions = {}\n): void {\n // biome-ignore lint/complexity/noUselessThisAlias: this is a plugin\n const self = this;\n\n function parser(this: unknown, value: string): HL7v2Node {\n return fromHL7v2Pipeline(value, options);\n }\n\n self.parser = parser;\n};\n\nexport default hl7v2Parser;\n"],"mappings":"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","names":[]}
|
package/dist/parser.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
-
import type { ParseOptions } from './
|
|
2
|
+
import type { ParseOptions } from './pipeline/interfaces';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Pipeline-based HL7v2 parser.
|
|
5
|
+
*
|
|
6
|
+
* This parser is a wrapper around the HL7MessageParser class.
|
|
7
|
+
* It is used to parse HL7v2 messages into an AST.
|
|
8
|
+
*
|
|
9
|
+
* @param rawMessage - The HL7v2 message to parse.
|
|
10
|
+
* @param options - The options for the parser.
|
|
11
|
+
* @returns The parsed HL7v2 message.
|
|
6
12
|
*/
|
|
7
|
-
export declare function
|
|
13
|
+
export declare function fromHL7v2Pipeline(rawMessage: string, options?: ParseOptions): HL7v2Node;
|
|
8
14
|
//# sourceMappingURL=parser.d.ts.map
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAExE;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,YAAiB,GACzB,SAAS,CAkBX"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { IMessageParser, MessageParseInput, ParseOptions } from '../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Main message parser that orchestrates the parsing pipeline
|
|
5
|
+
*/
|
|
6
|
+
export declare class HL7MessageParser implements IMessageParser {
|
|
7
|
+
private segmentRegistry;
|
|
8
|
+
private options;
|
|
9
|
+
constructor(options?: ParseOptions);
|
|
10
|
+
canParse(input: MessageParseInput): boolean;
|
|
11
|
+
parse(input: MessageParseInput): HL7v2Node | null;
|
|
12
|
+
private createParseContext;
|
|
13
|
+
private parseSegment;
|
|
14
|
+
private applyValidationHooks;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/pipeline/core/message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EAEjB,YAAY,EACb,MAAM,eAAe,CAAC;AAIvB;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,eAAe,CAAwB;IAE/C,OAAO,CAAC,OAAO,CAAe;gBAElB,OAAO,GAAE,YAAiB;IAKtC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO;IAI3C,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;IA8CjD,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,YAAY;IAqBpB,OAAO,CAAC,oBAAoB;CAkB7B"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { ComponentParseInput, IComponentParser } from '../../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Default component parser that handles subcomponents
|
|
5
|
+
*/
|
|
6
|
+
export declare class ComponentParser implements IComponentParser {
|
|
7
|
+
private subcomponentParser;
|
|
8
|
+
canParse(input: ComponentParseInput): boolean;
|
|
9
|
+
parse(input: ComponentParseInput): HL7v2Node | null;
|
|
10
|
+
private parseSubcomponents;
|
|
11
|
+
private splitByDelimiter;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/core/parsers/component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAG9E;;GAEG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,kBAAkB,CAA4B;IAEtD,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO;IAI7C,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI;IA8BnD,OAAO,CAAC,kBAAkB;IAiC1B,OAAO,CAAC,gBAAgB;CA0BzB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { FieldParseInput, IFieldParser } from '../../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Default field parser that handles components
|
|
5
|
+
*/
|
|
6
|
+
export declare class FieldParser implements IFieldParser {
|
|
7
|
+
private componentParser;
|
|
8
|
+
canParse(input: FieldParseInput): boolean;
|
|
9
|
+
parse(input: FieldParseInput): HL7v2Node | null;
|
|
10
|
+
private parseComponents;
|
|
11
|
+
private splitByDelimiter;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/core/parsers/field.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGtE;;GAEG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,OAAO,CAAC,eAAe,CAAyB;IAEhD,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAIzC,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI;IAwC/C,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,gBAAgB;CA0BzB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { ISegmentParser, SegmentParseInput } from '../../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Specialized parser for MSH segments with special field handling
|
|
5
|
+
*/
|
|
6
|
+
export declare class MSHSegmentParser implements ISegmentParser {
|
|
7
|
+
segmentType: string;
|
|
8
|
+
private fieldParser;
|
|
9
|
+
canParse(input: SegmentParseInput): boolean;
|
|
10
|
+
parse(input: SegmentParseInput): HL7v2Node | null;
|
|
11
|
+
private extractMSHFields;
|
|
12
|
+
private parseFields;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=segment-msh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-msh.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/core/parsers/segment-msh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI1E;;GAEG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,WAAW,SAAS;IACpB,OAAO,CAAC,WAAW,CAAqB;IAExC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO;IAI3C,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;IA8BjD,OAAO,CAAC,gBAAgB;IA0BxB,OAAO,CAAC,WAAW;CAqCpB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { ISegmentParser, SegmentParseInput } from '../../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Default segment parser for standard HL7v2 segments
|
|
5
|
+
*/
|
|
6
|
+
export declare class SegmentParser implements ISegmentParser {
|
|
7
|
+
segmentType: string;
|
|
8
|
+
private fieldParser;
|
|
9
|
+
canParse(input: SegmentParseInput): boolean;
|
|
10
|
+
parse(input: SegmentParseInput): HL7v2Node | null;
|
|
11
|
+
private parseFields;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=segment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/core/parsers/segment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI1E;;GAEG;AACH,qBAAa,aAAc,YAAW,cAAc;IAClD,WAAW,SAAa;IACxB,OAAO,CAAC,WAAW,CAAqB;IAExC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO;IAI3C,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI;IA8BjD,OAAO,CAAC,WAAW;CA+BpB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { ISubcomponentParser, SubcomponentParseInput } from '../../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Default subcomponent parser - leaf level parser
|
|
5
|
+
*/
|
|
6
|
+
export declare class SubcomponentParser implements ISubcomponentParser {
|
|
7
|
+
canParse(input: SubcomponentParseInput): boolean;
|
|
8
|
+
parse(input: SubcomponentParseInput): HL7v2Node | null;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=subcomponent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subcomponent.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/core/parsers/subcomponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,kBAAkB,CAAC;AAE1B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;IAC5D,QAAQ,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO;IAIhD,KAAK,CAAC,KAAK,EAAE,sBAAsB,GAAG,SAAS,GAAG,IAAI;CAmBvD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ISegmentParser } from '../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* Registry for segment parsers with fallback strategy
|
|
4
|
+
*/
|
|
5
|
+
export declare class SegmentParserRegistry {
|
|
6
|
+
private parsers;
|
|
7
|
+
private defaultParser;
|
|
8
|
+
constructor(customParsers?: Map<string, ISegmentParser>);
|
|
9
|
+
/**
|
|
10
|
+
* Register a segment parser for a specific segment type
|
|
11
|
+
*/
|
|
12
|
+
register(parser: ISegmentParser): void;
|
|
13
|
+
/**
|
|
14
|
+
* Get the appropriate parser for a segment type
|
|
15
|
+
*/
|
|
16
|
+
getParser(segmentType: string): ISegmentParser;
|
|
17
|
+
/**
|
|
18
|
+
* Check if a parser is registered for a segment type
|
|
19
|
+
*/
|
|
20
|
+
hasParser(segmentType: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Unregister a parser for a segment type
|
|
23
|
+
*/
|
|
24
|
+
unregister(segmentType: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Get all registered segment types
|
|
27
|
+
*/
|
|
28
|
+
getRegisteredTypes(): string[];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/pipeline/core/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD;;GAEG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAAiB;gBAE1B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;IAcvD;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAItC;;OAEG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc;IAI9C;;OAEG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIvC;;OAEG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIxC;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;CAG/B"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { HL7MessageParser } from './core/message';
|
|
2
|
+
export { ComponentParser } from './core/parsers/component';
|
|
3
|
+
export { FieldParser } from './core/parsers/field';
|
|
4
|
+
export { SegmentParser } from './core/parsers/segment';
|
|
5
|
+
export { MSHSegmentParser } from './core/parsers/segment-msh';
|
|
6
|
+
export { SubcomponentParser } from './core/parsers/subcomponent';
|
|
7
|
+
export { SegmentParserRegistry } from './core/registry';
|
|
8
|
+
export type { ComponentParseInput, FieldParseInput, IComponentParser, IFieldParser, IMessageParser, ISegmentParser, ISubcomponentParser, MessageParseInput, ParseContext, ParseInput, ParseOptions, Parser, PipelineStage, SegmentParseInput, SubcomponentParseInput, ValidationHook, ValidationResult, } from './interfaces';
|
|
9
|
+
export { BasicValidationHook, SegmentRequirementValidationHook, } from './validation/basic-validation';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pipeline/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,MAAM,EACN,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { HL7v2Delimiters } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Context passed between pipeline stages
|
|
5
|
+
*/
|
|
6
|
+
export interface ParseContext {
|
|
7
|
+
delimiters: HL7v2Delimiters;
|
|
8
|
+
options: ParseOptions;
|
|
9
|
+
currentLine: number;
|
|
10
|
+
totalOffset: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse options for the pipeline
|
|
14
|
+
*/
|
|
15
|
+
export interface ParseOptions {
|
|
16
|
+
delimiters?: Partial<HL7v2Delimiters>;
|
|
17
|
+
autoDetectDelimiters?: boolean;
|
|
18
|
+
customParsers?: Map<string, ISegmentParser>;
|
|
19
|
+
validationHooks?: ValidationHook[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Input for any parser stage
|
|
23
|
+
*/
|
|
24
|
+
export interface ParseInput {
|
|
25
|
+
text: string;
|
|
26
|
+
start: number;
|
|
27
|
+
end: number;
|
|
28
|
+
index: number;
|
|
29
|
+
context: ParseContext;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Base interface for all parsers
|
|
33
|
+
*/
|
|
34
|
+
export interface Parser<TInput extends ParseInput, TOutput extends HL7v2Node> {
|
|
35
|
+
parse(input: TInput): TOutput | null;
|
|
36
|
+
canParse(input: TInput): boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Message-level parser interface
|
|
40
|
+
*/
|
|
41
|
+
export interface IMessageParser extends Parser<MessageParseInput, HL7v2Node> {
|
|
42
|
+
}
|
|
43
|
+
export interface MessageParseInput extends ParseInput {
|
|
44
|
+
type: 'message';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Segment-level parser interface
|
|
48
|
+
*/
|
|
49
|
+
export interface ISegmentParser extends Parser<SegmentParseInput, HL7v2Node> {
|
|
50
|
+
segmentType: string;
|
|
51
|
+
}
|
|
52
|
+
export interface SegmentParseInput extends ParseInput {
|
|
53
|
+
type: 'segment';
|
|
54
|
+
segmentType: string;
|
|
55
|
+
line: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Field-level parser interface
|
|
59
|
+
*/
|
|
60
|
+
export interface IFieldParser extends Parser<FieldParseInput, HL7v2Node> {
|
|
61
|
+
}
|
|
62
|
+
export interface FieldParseInput extends ParseInput {
|
|
63
|
+
type: 'field';
|
|
64
|
+
fieldIndex: number;
|
|
65
|
+
line: number;
|
|
66
|
+
column: number;
|
|
67
|
+
isEncodingField?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Component-level parser interface
|
|
71
|
+
*/
|
|
72
|
+
export interface IComponentParser extends Parser<ComponentParseInput, HL7v2Node> {
|
|
73
|
+
}
|
|
74
|
+
export interface ComponentParseInput extends ParseInput {
|
|
75
|
+
type: 'component';
|
|
76
|
+
componentIndex: number;
|
|
77
|
+
line: number;
|
|
78
|
+
column: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Subcomponent-level parser interface
|
|
82
|
+
*/
|
|
83
|
+
export interface ISubcomponentParser extends Parser<SubcomponentParseInput, HL7v2Node> {
|
|
84
|
+
}
|
|
85
|
+
export interface SubcomponentParseInput extends ParseInput {
|
|
86
|
+
type: 'subcomponent';
|
|
87
|
+
subcomponentIndex: number;
|
|
88
|
+
line: number;
|
|
89
|
+
column: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Validation hook interface
|
|
93
|
+
*/
|
|
94
|
+
export interface ValidationHook {
|
|
95
|
+
validate(node: HL7v2Node, context: ParseContext): ValidationResult;
|
|
96
|
+
}
|
|
97
|
+
export interface ValidationResult {
|
|
98
|
+
isValid: boolean;
|
|
99
|
+
warnings?: string[];
|
|
100
|
+
errors?: string[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Pipeline stage interface
|
|
104
|
+
*/
|
|
105
|
+
export interface PipelineStage<TInput extends ParseInput, TOutput extends HL7v2Node> {
|
|
106
|
+
process(input: TInput): TOutput | null;
|
|
107
|
+
name: string;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=interfaces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/pipeline/interfaces.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HL7v2Delimiters } from '
|
|
1
|
+
import type { HL7v2Delimiters } from '../../types';
|
|
2
2
|
/**
|
|
3
3
|
* Auto-detect custom delimiters from MSH-1 and MSH-2
|
|
4
4
|
*/
|
|
@@ -11,4 +11,8 @@ export declare function splitByString(text: string, delimiter: string): Array<{
|
|
|
11
11
|
start: number;
|
|
12
12
|
end: number;
|
|
13
13
|
}>;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Detect the segment delimiter used in the message
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectSegmentDelimiter(message: string, fallback?: string): string;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pipeline/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,eAAe,CAAC,CAmC1B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBtD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,QAAQ,SAAO,GACd,MAAM,CAYR"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HL7v2Node } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { ParseContext, ValidationHook, ValidationResult } from '../interfaces';
|
|
3
|
+
/**
|
|
4
|
+
* Basic validation hook that checks for common HL7v2 message structure issues
|
|
5
|
+
*/
|
|
6
|
+
export declare class BasicValidationHook implements ValidationHook {
|
|
7
|
+
validate(node: HL7v2Node, _context: ParseContext): ValidationResult;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Custom validation hook for specific segment requirements
|
|
11
|
+
*/
|
|
12
|
+
export declare class SegmentRequirementValidationHook implements ValidationHook {
|
|
13
|
+
private requirements;
|
|
14
|
+
constructor(requirements: Map<string, number>);
|
|
15
|
+
validate(node: HL7v2Node, _context: ParseContext): ValidationResult;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=basic-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"basic-validation.d.ts","sourceRoot":"","sources":["../../../src/pipeline/validation/basic-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACxD,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,gBAAgB;CAmCpE;AAED;;GAEG;AACH,qBAAa,gCAAiC,YAAW,cAAc;IACrE,OAAO,CAAC,YAAY,CAAsB;gBAE9B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAI7C,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,gBAAgB;CAuBpE"}
|
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.2.
|
|
4
|
+
"version": "0.2.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Melek Somai",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"tsup": "8.5.0",
|
|
27
27
|
"typescript": "^5.8.3",
|
|
28
28
|
"vitest": "^3.2.4",
|
|
29
|
+
"@rethinkhealth/hl7v2-ast": "0.2.1",
|
|
29
30
|
"@rethinkhealth/testing": "0.0.0",
|
|
30
|
-
"@rethinkhealth/tsconfig": "0.0.0"
|
|
31
|
-
"@rethinkhealth/hl7v2-ast": "0.1.0"
|
|
31
|
+
"@rethinkhealth/tsconfig": "0.0.0"
|
|
32
32
|
},
|
|
33
33
|
"repository": "rethinkhealth/hl7v2.git",
|
|
34
34
|
"homepage": "https://www.rethinkhealth.io/hl7v2/docs",
|
package/dist/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBtD"}
|