@rethinkhealth/hl7v2-ast 0.2.3 → 0.2.5

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.
Files changed (3) hide show
  1. package/README.md +67 -76
  2. package/index.d.ts +272 -34
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -2,23 +2,20 @@
2
2
 
3
3
  **H**ealth **L**evel **7** Version 2 **A**bstract **S**yntax **T**ree.
4
4
 
5
- ***
6
-
7
- **hl7v2-ast** is a specification for representing HL7v2 messages as an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). It implements **[unist](https://github.com/syntax-tree/unist)** and provides a structured representation of HL7v2 segments, fields, components, and subcomponents.
5
+ **hl7v2-ast** is a specification for representing HL7v2 messages as an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). It implements **[unist](https://github.com/syntax-tree/unist)** and provides a structured, lossless representation of HL7v2 segments, fields, field repetitions, components, and subcomponents.
8
6
 
9
7
  ## Introduction
10
8
 
11
- This document defines a format for representing HL7v2 messages as an abstract syntax tree.
9
+ This document defines a format for representing HL7v2 messages as an abstract syntax tree.
12
10
 
13
- **hl7v2-ast** was created to support parsing, validation, and transformation of HL7v2 messages in a structured way.
11
+ **hl7v2-ast** was created to support parsing, validation, transformation, and linting of HL7v2 messages in a structured way.
14
12
 
15
13
  The specification follows the [Unist](https://github.com/syntax-tree/unist) model to benefit from the ecosystem of utilities and the [Unified](https://unifiedjs.com) processing pipeline.
16
14
 
17
15
  ### Where this specification fits
18
16
 
19
- - **hl7v2-ast** extends [unist](https://github.com/syntax-tree/unist) with HL7-specific node types.
20
- - Integrates with editor tooling, validators, and transformers.
21
-
17
+ * **hl7v2-ast** extends [unist](https://github.com/syntax-tree/unist) with HL7-specific node types.
18
+ * Integrates with editor tooling, validators, and transformers.
22
19
 
23
20
  ## Types
24
21
 
@@ -28,6 +25,25 @@ TypeScript types are published with the package:
28
25
  npm install @rethinkhealth/hl7v2-ast
29
26
  ```
30
27
 
28
+ ---
29
+
30
+ ## Node Hierarchy
31
+
32
+ The AST reflects the full HL7v2 delimiter hierarchy:
33
+
34
+ ```
35
+ root
36
+ └── segment
37
+ └── field (|)
38
+ └── field-repetition (~)
39
+ └── component (^)
40
+ └── subcomponent (&)
41
+ ```
42
+
43
+ * Every **field** always contains one or more `field-repetition` nodes, even if there is no `~`.
44
+ * Every **component** always contains one or more `subcomponent` nodes, even if there is no `&`.
45
+ * Only `subcomponent` nodes carry `value`.
46
+
31
47
  ## Nodes (abstract)
32
48
 
33
49
  ### `Literal`
@@ -38,8 +54,7 @@ interface Literal <: UnistLiteral {
38
54
  }
39
55
  ```
40
56
 
41
- **Literal** represents a leaf HL7v2 node containing a `value`, such as a field,
42
- component, or subcomponent.
57
+ Represents a leaf HL7v2 node containing a value. In this AST, the leaf is always a `subcomponent`.
43
58
 
44
59
  ### `Parent`
45
60
 
@@ -49,38 +64,45 @@ interface Parent <: UnistParent {
49
64
  }
50
65
  ```
51
66
 
52
- **Parent** represents a container node in HL7v2, such as `message` or `segment`.
67
+ Represents a container node such as a `segment`, `field`, or `component`.
53
68
 
54
- ## Nodes
69
+ ## Nodes (concrete)
55
70
 
56
- ### `Message`
71
+ ### `Root`
57
72
 
58
73
  ```idl
59
- interface Message <: Parent {
60
- type: 'message'
61
- children: [Segment]
74
+ interface Root <: Parent {
75
+ type: 'root'
76
+ children: [Segment | Group]
62
77
  }
63
78
  ```
64
79
 
65
- **Message** is the root of an HL7v2 document. It contains one or more `Segment` nodes.
80
+ Root of an HL7v2 AST. Can represent a full message or a fragment.
81
+
82
+ ---
66
83
 
67
84
  ### `Segment`
68
85
 
69
86
  ```idl
70
87
  interface Segment <: Parent {
71
88
  type: 'segment'
72
- name: string
73
- index: number
74
- delimiter: string
75
89
  children: [Field]
76
90
  }
77
91
  ```
78
92
 
79
- **Segment** represents an HL7v2 segment such as `MSH`, `PID`, or `OBX`.
93
+ Represents an HL7v2 segment such as `MSH`, `PID`, or `OBX`.
80
94
 
81
- * `name` is the 3-letter segment code.
82
- * `index` is the segment's position in the message.
83
- * `delimiter` is the field separator used.
95
+ ### `Group`
96
+
97
+ ```idl
98
+ interface Group <: Parent {
99
+ type: 'group'
100
+ name: string
101
+ children: [Segment]
102
+ }
103
+ ```
104
+
105
+ Represents a repeating or optional group of related segments (e.g., ORC+OBR+OBX).
84
106
 
85
107
  ### `Field`
86
108
 
@@ -88,16 +110,23 @@ interface Segment <: Parent {
88
110
  interface Field <: Parent {
89
111
  type: 'field'
90
112
  index: number
91
- value?: string
92
- delimiter?: string
93
- children?: [Component]
113
+ children: [FieldRepetition]
94
114
  }
95
115
  ```
96
116
 
97
- **Field** represents a field within a segment.
117
+ Represents a field inside a segment. **Always** contains one or more `field-repetition` nodes.
118
+
119
+ ### `FieldRepetition`
98
120
 
99
- * If the field contains components, it is a `Parent`.
100
- * If not, it is a `Literal` with a `value`.
121
+ ```idl
122
+ interface FieldRepetition <: Parent {
123
+ type: 'field-repetition'
124
+ index?: number
125
+ children: [Component]
126
+ }
127
+ ```
128
+
129
+ Represents one `~`-separated instance of a field. **Always** contains one or more `component` nodes.
101
130
 
102
131
  ### `Component`
103
132
 
@@ -105,15 +134,11 @@ interface Field <: Parent {
105
134
  interface Component <: Parent {
106
135
  type: 'component'
107
136
  index: number
108
- value?: string
109
- delimiter?: string
110
- children?: [Subcomponent]
137
+ children: [Subcomponent]
111
138
  }
112
139
  ```
113
140
 
114
- **Component** represents a component within a field.
115
-
116
- * Contains `Subcomponent` nodes if further split.
141
+ Represents a `^`-separated component. **Always** contains one or more `subcomponent` nodes.
117
142
 
118
143
  ### `Subcomponent`
119
144
 
@@ -125,11 +150,11 @@ interface Subcomponent <: Literal {
125
150
  }
126
151
  ```
127
152
 
128
- **Subcomponent** represents the smallest value unit in an HL7v2 message.
153
+ Represents an `&`-separated subcomponent and holds the actual text value.
129
154
 
130
155
  ## Position
131
156
 
132
- All nodes may include a `position` property following [unist]((https://github.com/syntax-tree/unist)):
157
+ All nodes may include a `position` property following [unist](https://github.com/syntax-tree/unist):
133
158
 
134
159
  ```idl
135
160
  interface Position {
@@ -138,50 +163,17 @@ interface Position {
138
163
  }
139
164
 
140
165
  interface Point {
141
- line: number // 1-based segment line
142
- column: number // 1-based character column within the segment
143
- offset: number // 0-based character index in the entire message
144
- }
145
- ```
146
-
147
- Example:
148
-
149
- ```json
150
- {
151
- "type": "field",
152
- "index": 1,
153
- "value": "DOE^JOHN",
154
- "position": {
155
- "start": { "line": 2, "column": 5, "offset": 48 },
156
- "end": { "line": 2, "column": 14, "offset": 57 }
157
- }
166
+ line: number
167
+ column: number
168
+ offset: number
158
169
  }
159
170
  ```
160
171
 
161
- ## Delimiters
162
-
163
- HL7v2 messages use configurable delimiters. **hl7v2-ast** tracks delimiters per node for round-tripping.
164
-
165
- Default:
166
-
167
- ```json
168
- {
169
- "field": "|",
170
- "component": "^",
171
- "subcomponent": "&",
172
- "repetition": "~",
173
- "escape": "\\",
174
- "segment": "\r"
175
- }
176
- ```
177
-
178
- MSH-1 and MSH-2 are auto-detected unless overridden.
179
-
180
172
  ## Content model
181
173
 
182
174
  ```idl
183
175
  type HL7v2Content =
184
- Message | Segment | Field | Component | Subcomponent
176
+ Root | Segment | Group | Field | FieldRepetition | Component | Subcomponent
185
177
  ```
186
178
 
187
179
  ## Extensions
@@ -192,7 +184,6 @@ The AST is designed for:
192
184
  * **Annotation plugins** (map to FHIR, metadata)
193
185
  * **Transformers** (to JSON, FHIR, XML)
194
186
 
195
-
196
187
  ## Contributing
197
188
 
198
189
  We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for more details.
package/index.d.ts CHANGED
@@ -1,59 +1,297 @@
1
- import type { Node, Position } from 'unist';
1
+ import type {
2
+ Data as UnistData,
3
+ Literal as UnistLiteral,
4
+ Node as UnistNode,
5
+ Parent as UnistParent,
6
+ } from 'unist';
7
+
8
+ // ## Abstract nodes
9
+
10
+ /**
11
+ * Union of registered HL7v2 literals.
12
+ *
13
+ * To register custom HL7v2 nodes, add them to {@link RootContentMap} and other
14
+ * places where relevant.
15
+ * They will be automatically added here.
16
+ */
17
+ export type Literals = Extract<Nodes, UnistLiteral>;
18
+
19
+ /**
20
+ * Union of registered HL7v2 parents.
21
+ *
22
+ * To register custom HL7v2 nodes, add them to {@link RootContentMap} and other
23
+ * places where relevant.
24
+ * They will be automatically added here.
25
+ */
26
+ export type Parents = Extract<Nodes, UnistParent>;
27
+
28
+ /**
29
+ * Union of registered HL7v2 nodes.
30
+ *
31
+ * To register custom HL7v2 nodes, add them to {@link RootContentMap} and other
32
+ * places where relevant.
33
+ * They will be automatically added here.
34
+ */
35
+ export type Nodes = Root | RootContent;
2
36
 
3
37
  /**
4
- * HL7v2 Node specification following the Unist spec
38
+ * Info associated with HL7v2Node nodes by the ecosystem.
39
+ *
40
+ * This space is guaranteed to never be specified by unist or HL7v2Node.
41
+ * But you can use it in utilities and plugins to store data.
5
42
  *
6
- * @see https://github.com/syntax-tree/unist#node
43
+ * This type can be augmented to register custom data.
44
+ * For example:
45
+ *
46
+ * ```ts
47
+ * declare module '@rethinkhealth/hl7v2-ast' {
48
+ * interface Data {
49
+ * // `someNode.data.myId` is typed as `number | undefined`
50
+ * myId?: number | undefined
51
+ * }
52
+ * }
53
+ * ```
7
54
  */
8
- export interface HL7v2Node extends Node {
55
+ export interface Data extends UnistData {}
56
+
57
+ /**
58
+ * Abstract HL7v2 node.
59
+ *
60
+ * This interface is supposed to be extended.
61
+ * If you can use {@link Literal} or {@link Parent}, you should.
62
+ *
63
+ * For a union of all registered HL7v2 nodes, see {@link Nodes}.
64
+ */
65
+ export interface Node extends UnistNode {
9
66
  /**
10
- * The type of the node.
11
- *
12
- * There are 6 types of nodes:
13
- * - **root**: The root node of the AST. This is the top-level node of the AST.
14
- * - **segment**: A segment node (e.g. PID, MSH, etc.)
15
- * - **header**: A header node (e.g. MSH, EVN, etc.)
16
- * - **field**: A field node (e.g. PID.1, MSH.1, etc.)
17
- * - **component**: A component node (e.g. PID.1.1, MSH.1.1, etc.)
18
- * - **subcomponent**: A subcomponent node (e.g. PID.1.1.1, MSH.1.1.1, etc.)
67
+ * Info from the ecosystem.
19
68
  */
20
- type: 'root' | 'segment' | 'header' | 'field' | 'component' | 'subcomponent';
69
+ data?: Data | undefined;
70
+ }
21
71
 
72
+ /**
73
+ * Abstract HL7v2 node that contains the smallest possible value.
74
+ *
75
+ * This interface is supposed to be extended if you make custom HL7v2 nodes.
76
+ *
77
+ * For a union of all registered mdast literals, see {@link Literals}.
78
+ */
79
+ export interface Literal extends Node {
22
80
  /**
23
- * The name of the node (e.g. "PID", "MSH", "EVN", etc.)
81
+ * Plain-text value.
24
82
  */
25
- name?: string;
83
+ value: string;
84
+ }
26
85
 
86
+ /**
87
+ * Abstract HL7v2 node that contains other HL7v2 nodes (*children*).
88
+ *
89
+ * This interface is supposed to be extended if you make custom HL7v2 nodes.
90
+ *
91
+ * For a union of all registered mdast parents, see {@link Parents}.
92
+ */
93
+ export interface Parent extends Node {
27
94
  /**
28
- * The index of the node in the parent.
29
- *
30
- * This is valuable in HL7v2 where fields and components' order is
31
- * relevant.
95
+ * List of children.
32
96
  */
33
- index?: number;
97
+ children: RootContent[];
98
+ }
34
99
 
100
+ // ## Content Maps
101
+
102
+ /**
103
+ * Union of registered mdast nodes that can occur in {@link Root}.
104
+ *
105
+ * To register custom mdast nodes, add them to {@link RootContentMap}.
106
+ * They will be automatically added here.
107
+ */
108
+ export type RootContent = RootContentMap[keyof RootContentMap];
109
+
110
+ /**
111
+ * Registry of all mdast nodes that can occur as children of {@link Root}.
112
+ *
113
+ * > **Note**: {@link Root} does not need to be an entire document.
114
+ * > it can also be a fragment.
115
+ *
116
+ * This interface can be augmented to register custom node types:
117
+ *
118
+ * ```ts
119
+ * declare module 'mdast' {
120
+ * interface RootContentMap {
121
+ * // Allow using toml nodes defined by `remark-frontmatter`.
122
+ * toml: TOML;
123
+ * }
124
+ * }
125
+ * ```
126
+ *
127
+ * For a union of all {@link Root} children, see {@link RootContent}.
128
+ */
129
+ export interface RootContentMap {
130
+ segment: Segment;
131
+ group: Group;
132
+ field: Field;
133
+ fieldRepetition: FieldRepetition;
134
+ component: Component;
135
+ subcomponent: Subcomponent;
136
+ }
137
+
138
+ // ## Concrete nodes
139
+
140
+ /**
141
+ * Document fragment or a whole document.
142
+ *
143
+ * Should be used as the root of a tree and must not be used as a child.
144
+ */
145
+ export interface Root extends Parent {
146
+ /**
147
+ * Node type of HL7v2 root.
148
+ */
149
+ type: 'root';
35
150
  /**
36
- * The value of the node. This is the raw value of the node.
37
- *
38
- * TODO: We might consider removing this property and use the children
39
- * property instead.
151
+ * Data associated with the mdast root.
40
152
  */
41
- value?: string;
153
+ data?: RootData | undefined;
154
+ }
155
+
156
+ /**
157
+ * Info associated with HL7v2 root nodes by the ecosystem.
158
+ */
159
+ export interface RootData extends Data {}
42
160
 
161
+ /**
162
+ * HL7v2 segment.
163
+ */
164
+ export interface Segment extends Parent {
165
+ /**
166
+ * Node type of HL7v2 segment.
167
+ */
168
+ type: 'segment';
43
169
  /**
44
- * The delimiter of the node.
170
+ * Children of block quote.
45
171
  */
46
- delimiter?: string;
172
+ children: Field[];
173
+ /**
174
+ * Data associated with the mdast block quote.
175
+ */
176
+ data?: SegmentData | undefined;
177
+ }
47
178
 
179
+ /**
180
+ * Info associated with HL7v2 segment nodes by the ecosystem.
181
+ */
182
+ export interface SegmentData extends Data {}
183
+
184
+ /**
185
+ * HL7v2 group.
186
+ *
187
+ * A group is a set of segments that appear together in a defined order within a message.
188
+ * They are often used when a message needs to repeat a set of related segments together
189
+ * rather than just a single segment.
190
+ *
191
+ * Groups can be required/optional and repeating/non-repeating, just like segments.
192
+ */
193
+ export interface Group extends Parent {
194
+ /**
195
+ * Node type of HL7v2 segment.
196
+ */
197
+ type: 'group';
198
+ /**
199
+ * Children of block quote.
200
+ */
201
+ children: Segment[];
202
+ /**
203
+ * Data associated with the mdast block quote.
204
+ */
205
+ data?: GroupData | undefined;
206
+ }
207
+
208
+ /**
209
+ * Info associated with HL7v2 segment nodes by the ecosystem.
210
+ */
211
+ export interface GroupData extends Data {}
212
+
213
+ /**
214
+ * HL7v2 field.
215
+ */
216
+ export interface Field extends Parent {
217
+ /**
218
+ * Node type of HL7v2 field.
219
+ */
220
+ type: 'field';
221
+ /**
222
+ * Children of field.
223
+ */
224
+ children: FieldRepetition[];
225
+ /**
226
+ * Data associated with the mdast block quote.
227
+ */
228
+ data?: FieldData | undefined;
229
+ }
230
+
231
+ /**
232
+ * Info associated with HL7v2 segment nodes by the ecosystem.
233
+ */
234
+ export interface FieldData extends Data {}
235
+
236
+ /**
237
+ * HL7v2 field repetition.
238
+ */
239
+ export interface FieldRepetition extends Parent {
240
+ /**
241
+ * Node type of HL7v2 field repetition.
242
+ */
243
+ type: 'field-repetition';
244
+ /**
245
+ * Children of field repetition.
246
+ */
247
+ children: Component[];
248
+ /**
249
+ * Data associated with the mdast block quote.
250
+ */
251
+ data?: FieldRepetitionData | undefined;
252
+ }
253
+
254
+ /**
255
+ * Info associated with HL7v2 field repetition nodes by the ecosystem.
256
+ */
257
+ export interface FieldRepetitionData extends Data {}
258
+
259
+ /**
260
+ * HL7v2 component.
261
+ */
262
+ export interface Component extends Parent {
263
+ /**
264
+ * Node type of HL7v2 component.
265
+ */
266
+ type: 'component';
267
+ /**
268
+ * Children of component.
269
+ */
270
+ children: Subcomponent[];
48
271
  /**
49
- * The children of the node.
272
+ * Data associated with the mdast block quote.
50
273
  */
51
- children?: HL7v2Node[];
274
+ data?: ComponentData | undefined;
275
+ }
52
276
 
277
+ /**
278
+ * Info associated with HL7v2 segment nodes by the ecosystem.
279
+ */
280
+ export interface ComponentData extends Data {}
281
+
282
+ /**
283
+ * HL7v2 subcomponent.
284
+ */
285
+ export interface Subcomponent extends Literal {
53
286
  /**
54
- * The position of the node in the source text.
55
- *
56
- * This is used for round-tripping the AST back to the original text.
287
+ * Node type of HL7v2 subcomponent.
57
288
  */
58
- position?: Position;
289
+ type: 'subcomponent';
290
+
291
+ /**
292
+ * Data associated with the mdast block quote.
293
+ */
294
+ data?: SubcomponentData | undefined;
59
295
  }
296
+
297
+ export interface SubcomponentData extends Data {}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rethinkhealth/hl7v2-ast",
3
3
  "description": "HL7v2 is a specification for representing HL7v2 messages as an abstract syntax tree. It implements the unist spec.",
4
- "version": "0.2.3",
4
+ "version": "0.2.5",
5
5
  "license": "MIT",
6
6
  "author": {
7
7
  "name": "Melek Somai",
@@ -33,6 +33,6 @@
33
33
  "access": "public"
34
34
  },
35
35
  "scripts": {
36
- "check-types": "tsc --noEmit"
36
+ "check-types": "tsc ./index.d.ts --noEmit"
37
37
  }
38
38
  }