@glion/util-visit 0.26.0 → 0.27.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/LICENSE +1 -1
- package/README.md +63 -256
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -26
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +1 -21
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @glion/util-visit
|
|
2
2
|
|
|
3
|
-
Visitor for traversing HL7v2 AST trees with ancestor context
|
|
3
|
+
Visitor for traversing HL7v2 AST trees with ancestor context.
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
7
|
-
Walks an HL7v2 AST from any starting node — Root, Segment, Field, Component, or Subcomponent — calling a visitor function for each matching node
|
|
7
|
+
Walks an HL7v2 AST from any starting node — Root, Group, Segment, Field, Component, or Subcomponent — calling a visitor function for each matching node with the node and its ancestors, from the node the walk starts at to the node's parent. Supports filtering by type string, property object, or predicate, and exposes `SKIP`/`EXIT` control actions. Visits in the same order, and reads a visitor's result the same way, as `unist-util-visit-parents`, with types from `@glion/ast`.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -15,25 +15,21 @@ npm install @glion/util-visit
|
|
|
15
15
|
## Use
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { parseHL7v2 } from "@glion/parser";
|
|
19
|
+
import { SKIP, visit } from "@glion/util-visit";
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
visit(message, "segment", (node, ancestors, info) => {
|
|
25
|
-
console.log(`Segment: ${info.metadata?.header} at depth ${info.depth}`);
|
|
26
|
-
});
|
|
21
|
+
const tree = parseHL7v2(
|
|
22
|
+
"MSH|^~\\&|LAB|FAC|EMR|RFAC|20250601120000||ORU^R01^ORU_R01|MSG00001|P|2.5\rPID|1||12345\rNTE|1||note"
|
|
23
|
+
);
|
|
27
24
|
|
|
28
|
-
//
|
|
29
|
-
visit(
|
|
30
|
-
|
|
31
|
-
console.log(`Field ${info.sequence} in segment`);
|
|
25
|
+
// Visit every segment
|
|
26
|
+
visit(tree, "segment", (segment) => {
|
|
27
|
+
console.log(segment.name); // "MSH", "PID", "NTE"
|
|
32
28
|
});
|
|
33
29
|
|
|
34
|
-
// Skip
|
|
35
|
-
visit(
|
|
36
|
-
if (node.type === "segment" &&
|
|
30
|
+
// Skip the fields of NTE segments
|
|
31
|
+
visit(tree, (node) => {
|
|
32
|
+
if (node.type === "segment" && node.name === "NTE") {
|
|
37
33
|
return SKIP;
|
|
38
34
|
}
|
|
39
35
|
});
|
|
@@ -45,14 +41,14 @@ visit(message, (node, ancestors, info) => {
|
|
|
45
41
|
|
|
46
42
|
### `visit(tree, test, visitor)`
|
|
47
43
|
|
|
48
|
-
Visit nodes in an HL7v2 AST tree.
|
|
44
|
+
Visit nodes in an HL7v2 AST tree, in document order.
|
|
49
45
|
|
|
50
46
|
#### Parameters
|
|
51
47
|
|
|
52
48
|
- `tree` (`Nodes`) — Tree to traverse. Can be any node type, not just `Root`.
|
|
53
49
|
- `test` (`string | Partial<Nodes> | Test`, optional) — Filter:
|
|
54
50
|
- `string` — Match nodes by type (e.g., `"segment"`).
|
|
55
|
-
- `Partial<Nodes>` — Match nodes whose own properties equal (`===`) each value (e.g., `{
|
|
51
|
+
- `Partial<Nodes>` — Match nodes whose own properties equal (`===`) each value (e.g., `{ id: "PATIENT" }`). A property the node does not own reads as `undefined`: an `undefined` value matches an absent property, and a key naming an inherited member (`constructor`, `toString`) matches no node.
|
|
56
52
|
- `Test` — Custom function `(node, ancestors) => boolean`.
|
|
57
53
|
- `visitor` (`Visitor`) — Function called for each matching node.
|
|
58
54
|
|
|
@@ -60,23 +56,18 @@ Visit nodes in an HL7v2 AST tree.
|
|
|
60
56
|
|
|
61
57
|
`void`.
|
|
62
58
|
|
|
63
|
-
####
|
|
59
|
+
#### Test vs visitor functions
|
|
64
60
|
|
|
65
|
-
|
|
61
|
+
A function passed as the second argument is always the visitor, never a test. A test function needs the three-argument form:
|
|
66
62
|
|
|
67
63
|
```typescript
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// CORRECT — Use string or object for simple tests
|
|
77
|
-
visit(ast, 'segment', (node, ancestors, info) => {
|
|
78
|
-
console.log('Visiting segment');
|
|
79
|
-
});
|
|
64
|
+
visit(
|
|
65
|
+
tree,
|
|
66
|
+
(node) => node.type === "segment",
|
|
67
|
+
(segment) => {
|
|
68
|
+
console.log(segment.type); // "segment"
|
|
69
|
+
}
|
|
70
|
+
);
|
|
80
71
|
```
|
|
81
72
|
|
|
82
73
|
#### Visitor function
|
|
@@ -84,290 +75,106 @@ visit(ast, 'segment', (node, ancestors, info) => {
|
|
|
84
75
|
```typescript
|
|
85
76
|
type Visitor<T extends Nodes = Nodes> = (
|
|
86
77
|
node: T,
|
|
87
|
-
ancestors: Nodes[]
|
|
88
|
-
info: VisitInfo
|
|
78
|
+
ancestors: Nodes[]
|
|
89
79
|
) => VisitorResult;
|
|
90
80
|
```
|
|
91
81
|
|
|
92
82
|
The visitor receives:
|
|
93
83
|
|
|
94
84
|
- `node` — Current AST node.
|
|
95
|
-
- `ancestors` —
|
|
96
|
-
- `info` — Visit information with index, sequence, depth, and metadata.
|
|
85
|
+
- `ancestors` — Ancestor nodes from the node the walk starts at to the parent, not including the current node. Empty for the starting node.
|
|
97
86
|
|
|
98
87
|
The visitor can return:
|
|
99
88
|
|
|
100
|
-
- `undefined` or `
|
|
101
|
-
- `SKIP` — Skip children of current node.
|
|
102
|
-
- `EXIT` — Stop traversal
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
interface VisitInfo {
|
|
108
|
-
/** 0-based index among the parent's children when the node is visited */
|
|
109
|
-
index: number;
|
|
110
|
-
|
|
111
|
-
/** `index` + 1, the HL7v2 sequence number */
|
|
112
|
-
sequence: number;
|
|
113
|
-
|
|
114
|
-
/** 1-based depth in tree (root = 1) */
|
|
115
|
-
depth: number;
|
|
116
|
-
|
|
117
|
-
/** Metadata (e.g., { name: "MSH" } or { name: "PATIENT" }) */
|
|
118
|
-
metadata: Record<string, unknown> | undefined;
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
`index` and `sequence` represent the node's **position in the tree**, not its position among filtered results:
|
|
89
|
+
- `undefined` or `CONTINUE` — Continue traversal, into the node's children.
|
|
90
|
+
- `SKIP` — Skip the children of the current node.
|
|
91
|
+
- `EXIT` — Stop traversal.
|
|
92
|
+
- An index — Continue at that index among the current node's siblings.
|
|
93
|
+
- `[action, index]` — Both.
|
|
123
94
|
|
|
124
|
-
|
|
125
|
-
// Structure: MSH segment with fields at positions 1, 2, 3, 4
|
|
126
|
-
// Filter matches only field 3
|
|
127
|
-
visit(
|
|
128
|
-
ast,
|
|
129
|
-
(n) => n.type === "field" && hasContent(n),
|
|
130
|
-
(node, ancestors, info) => {
|
|
131
|
-
console.log(info.sequence); // 3 (position in segment, not "1st match")
|
|
132
|
-
}
|
|
133
|
-
);
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
This is correct because HL7v2 paths like `PID.3` refer to tree positions, not filtered positions.
|
|
137
|
-
|
|
138
|
-
The position is read when the node is visited, so it reflects siblings a visitor inserted or removed earlier in the same visit. A visitor that removes the current node can return `info.index` to continue with the sibling that moved into its place:
|
|
95
|
+
A visitor that removes the current node returns its former index to continue with the sibling that moved into its place:
|
|
139
96
|
|
|
140
97
|
```typescript
|
|
141
|
-
visit(segment, "
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
98
|
+
visit(tree, { type: "segment", name: "NTE" }, (segment, ancestors) => {
|
|
99
|
+
const parent = ancestors.at(-1) as Root | Group;
|
|
100
|
+
const index = parent.children.indexOf(segment);
|
|
101
|
+
parent.children.splice(index, 1);
|
|
102
|
+
return index;
|
|
146
103
|
});
|
|
147
104
|
```
|
|
148
105
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
The `metadata` field is populated automatically:
|
|
152
|
-
|
|
153
|
-
| Node type | Metadata key | Description |
|
|
154
|
-
| --------- | ------------ | ------------------------------------------- |
|
|
155
|
-
| `segment` | `header` | Segment identifier (e.g., `"MSH"`, `"PID"`) |
|
|
156
|
-
| `group` | `name` | Group name (e.g., `"PATIENT_GROUP"`) |
|
|
106
|
+
`indexOf` scans the parent's children, so removing many children of one parent this way costs time in the square of their number. To remove many, filter `parent.children` once from the parent's visitor instead.
|
|
157
107
|
|
|
158
108
|
### Exports
|
|
159
109
|
|
|
160
110
|
```typescript
|
|
161
111
|
import {
|
|
162
|
-
visit, //
|
|
112
|
+
visit, // Traversal function
|
|
163
113
|
EXIT, // Return to stop traversal
|
|
164
114
|
SKIP, // Return to skip children
|
|
165
115
|
} from "@glion/util-visit";
|
|
166
116
|
|
|
167
117
|
import type {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
Test, // Filter predicate
|
|
118
|
+
Visitor, // (node, ancestors) => VisitorResult
|
|
119
|
+
VisitorResult, // Return type of a visitor
|
|
120
|
+
Test, // Filter: type string, property object, predicate, or null
|
|
172
121
|
Predicate, // (node, ancestors) => boolean
|
|
173
122
|
} from "@glion/util-visit";
|
|
174
123
|
```
|
|
175
124
|
|
|
176
125
|
## Examples
|
|
177
126
|
|
|
178
|
-
### Filter by node type
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
visit(ast, "segment", (node, ancestors, info) => {
|
|
182
|
-
console.log(`Found segment: ${info.metadata?.header}`);
|
|
183
|
-
});
|
|
184
|
-
```
|
|
185
|
-
|
|
186
127
|
### Filter by properties
|
|
187
128
|
|
|
188
129
|
```typescript
|
|
189
|
-
|
|
190
|
-
|
|
130
|
+
// A group's `id` is its key in paths; its `name` is the title for display
|
|
131
|
+
visit(tree, { type: "group", id: "PATIENT" }, (group) => {
|
|
132
|
+
console.log(group.name); // "Patient"
|
|
191
133
|
});
|
|
192
134
|
```
|
|
193
135
|
|
|
194
|
-
###
|
|
136
|
+
### Read the ancestors
|
|
195
137
|
|
|
196
138
|
```typescript
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
(node, ancestors) => {
|
|
201
|
-
const parent = ancestors.at(-1);
|
|
202
|
-
return node.type === "field" && parent?.type === "segment";
|
|
203
|
-
},
|
|
204
|
-
(node, ancestors, info) => {
|
|
205
|
-
console.log(`Field at sequence ${info.sequence}`);
|
|
206
|
-
}
|
|
207
|
-
);
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Access parent and ancestors
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
visit(ast, "component", (node, ancestors, info) => {
|
|
214
|
-
const parent = ancestors.at(-1);
|
|
215
|
-
const segment = ancestors.findLast((n) => n.type === "segment");
|
|
216
|
-
console.log(`Component at depth ${info.depth}`);
|
|
139
|
+
visit(tree, "component", (component, ancestors) => {
|
|
140
|
+
const parent = ancestors.at(-1); // the field repetition
|
|
141
|
+
const segment = ancestors.findLast((node) => node.type === "segment");
|
|
217
142
|
});
|
|
218
143
|
```
|
|
219
144
|
|
|
220
|
-
###
|
|
145
|
+
### Read the segment groups above a segment
|
|
221
146
|
|
|
222
147
|
```typescript
|
|
223
|
-
visit(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
148
|
+
visit(tree, "segment", (segment, ancestors) => {
|
|
149
|
+
const groups = ancestors
|
|
150
|
+
.filter((node): node is Group => node.type === "group")
|
|
151
|
+
.map((group) => group.id);
|
|
152
|
+
console.log(`${segment.name}: ${groups.join(" > ")}`); // "PID: PATIENT_RESULT > PATIENT"
|
|
227
153
|
});
|
|
228
154
|
```
|
|
229
155
|
|
|
230
|
-
###
|
|
156
|
+
### Stop at the first match
|
|
231
157
|
|
|
232
158
|
```typescript
|
|
233
159
|
import { EXIT } from "@glion/util-visit";
|
|
234
160
|
|
|
235
|
-
let
|
|
236
|
-
visit(
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return EXIT; // Stop traversal completely
|
|
240
|
-
}
|
|
161
|
+
let first: Segment | undefined;
|
|
162
|
+
visit(tree, { type: "segment", name: "OBX" }, (segment) => {
|
|
163
|
+
first = segment as Segment;
|
|
164
|
+
return EXIT;
|
|
241
165
|
});
|
|
242
166
|
```
|
|
243
167
|
|
|
244
168
|
### Start from any node
|
|
245
169
|
|
|
246
170
|
```typescript
|
|
247
|
-
import {
|
|
171
|
+
import { c, f, s } from "@glion/builder";
|
|
248
172
|
|
|
249
|
-
// Create a standalone segment
|
|
250
173
|
const segment = s("PID", f(c("value1")), f(c("value2")));
|
|
251
174
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
console.log(`Field at sequence ${info.sequence}`);
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Track nesting levels
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
visit(ast, (node, ancestors, info) => {
|
|
262
|
-
const indent = " ".repeat(info.depth - 1);
|
|
263
|
-
console.log(`${indent}${node.type} [${info.sequence}]`);
|
|
175
|
+
visit(segment, "field", (field, ancestors) => {
|
|
176
|
+
console.log(ancestors.length); // 1: the segment
|
|
264
177
|
});
|
|
265
|
-
// root [1]
|
|
266
|
-
// segment [1]
|
|
267
|
-
// segment-header [0]
|
|
268
|
-
// field [1]
|
|
269
|
-
// field-repetition [1]
|
|
270
|
-
// component [1]
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
### Group hierarchy navigation
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
visit(ast, "segment", (node, ancestors, info) => {
|
|
277
|
-
const groups = ancestors
|
|
278
|
-
.filter((n) => n.type === "group")
|
|
279
|
-
.map((n) => (n as any).name)
|
|
280
|
-
.filter((name): name is string => typeof name === "string");
|
|
281
|
-
|
|
282
|
-
console.log(`${info.metadata?.header} is in groups: ${groups.join(" > ")}`);
|
|
283
|
-
});
|
|
284
|
-
// PID is in groups: PATIENT_GROUP
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### Validate required fields
|
|
288
|
-
|
|
289
|
-
```typescript
|
|
290
|
-
function validateRequiredFields(ast: Root): string[] {
|
|
291
|
-
const errors: string[] = [];
|
|
292
|
-
|
|
293
|
-
visit(ast, "segment", (node, ancestors, info) => {
|
|
294
|
-
const segment = node as Segment;
|
|
295
|
-
const header = info.metadata?.header;
|
|
296
|
-
|
|
297
|
-
if (header === "MSH" && segment.children.length < 12) {
|
|
298
|
-
errors.push("MSH segment missing required fields");
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (header === "PID") {
|
|
302
|
-
const patientId = segment.children[3];
|
|
303
|
-
if (!patientId || patientId.children.length === 0) {
|
|
304
|
-
errors.push("PID segment missing required Patient ID (PID.3)");
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
return errors;
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### Extract data with context
|
|
314
|
-
|
|
315
|
-
```typescript
|
|
316
|
-
interface PatientName {
|
|
317
|
-
name: string;
|
|
318
|
-
sequence: number;
|
|
319
|
-
inGroup?: string;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function extractPatientNames(ast: Root): PatientName[] {
|
|
323
|
-
const names: PatientName[] = [];
|
|
324
|
-
|
|
325
|
-
visit(ast, "segment", (node, ancestors, info) => {
|
|
326
|
-
if (info.metadata?.header !== "PID") return;
|
|
327
|
-
|
|
328
|
-
const segment = node as Segment;
|
|
329
|
-
const nameField = segment.children[5];
|
|
330
|
-
if (nameField?.children[0]?.children[0]) {
|
|
331
|
-
const nameComponent = nameField.children[0].children[0];
|
|
332
|
-
const name = (nameComponent.children[0] as Subcomponent)?.value || "";
|
|
333
|
-
const groupAncestor = ancestors.find(
|
|
334
|
-
(n): n is Group => n.type === "group"
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
names.push({
|
|
338
|
-
name,
|
|
339
|
-
sequence: info.sequence,
|
|
340
|
-
inGroup: groupAncestor?.name,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
return names;
|
|
346
|
-
}
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### Find first match and exit
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
function findFirstObservation(ast: Root, targetCode: string): string | null {
|
|
353
|
-
let result: string | null = null;
|
|
354
|
-
|
|
355
|
-
visit(ast, "segment", (node, ancestors, info) => {
|
|
356
|
-
if (info.metadata?.header !== "OBX") return;
|
|
357
|
-
|
|
358
|
-
const segment = node as Segment;
|
|
359
|
-
const identifierField = segment.children[3];
|
|
360
|
-
const code = identifierField?.children[0]?.children[0]?.children[0]?.value;
|
|
361
|
-
|
|
362
|
-
if (code === targetCode) {
|
|
363
|
-
const valueField = segment.children[5];
|
|
364
|
-
result = valueField?.children[0]?.children[0]?.children[0]?.value || null;
|
|
365
|
-
return EXIT;
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
return result;
|
|
370
|
-
}
|
|
371
178
|
```
|
|
372
179
|
|
|
373
180
|
## Part of Glion
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HL7v2 AST
|
|
2
|
+
* HL7v2 AST visitor, typed for `@glion/ast`, with the semantics of
|
|
3
|
+
* `unist-util-visit-parents`.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* (index, sequence, depth, metadata) while delegating core traversal.
|
|
5
|
+
* @module
|
|
6
6
|
*/
|
|
7
7
|
import type { Nodes } from "@glion/ast";
|
|
8
8
|
import type { Test, Visitor } from "./types";
|
|
9
9
|
export type { VisitorResult } from "unist-util-visit-parents";
|
|
10
10
|
export { EXIT, SKIP } from "unist-util-visit-parents";
|
|
11
|
-
export type { Predicate, Test,
|
|
11
|
+
export type { Predicate, Test, Visitor } from "./types";
|
|
12
12
|
export declare function visit(tree: Nodes, visitor: Visitor): void;
|
|
13
13
|
export declare function visit<Type extends Nodes["type"]>(tree: Nodes, test: Type, visitor: Visitor<Extract<Nodes, {
|
|
14
14
|
type: Type;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAIxC,OAAO,KAAK,EAAa,IAAI,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGxD,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAEtD,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGxD,wBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;AAC3D,wBAAgB,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,MAAM,CAAC,EAC9C,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC,GAC/C,IAAI,CAAC;AACR,wBAAgB,KAAK,CAAC,CAAC,SAAS,KAAK,EACnC,IAAI,EAAE,KAAK,EACX,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EACb,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAClB,IAAI,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EXIT, SKIP,
|
|
1
|
+
import { EXIT, EXIT as EXIT$1, SKIP, SKIP as SKIP$1 } from "unist-util-visit-parents";
|
|
2
2
|
//#region src/utils.ts
|
|
3
3
|
/**
|
|
4
4
|
* The predicate a `visit` test describes.
|
|
@@ -46,18 +46,35 @@ function createTest(test) {
|
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/index.ts
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* Visits `tree` and every node below it, depth-first and in document order,
|
|
50
|
+
* calling `visitor` with each node that passes `test` and that node's
|
|
51
|
+
* ancestors.
|
|
51
52
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* `test` is a node type (`"segment"`), an object whose own properties a node
|
|
54
|
+
* must equal (`{ name: "PID" }`), or a function of `(node, ancestors)`. With no
|
|
55
|
+
* test, every node passes. A function passed as the second argument is always
|
|
56
|
+
* the visitor; a test function needs the three-argument form.
|
|
56
57
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* The visitor's result decides what the walk does next:
|
|
59
|
+
*
|
|
60
|
+
* - Nothing: enter the node's children, then go on.
|
|
61
|
+
* - `SKIP`: do not enter the node's children.
|
|
62
|
+
* - `EXIT`: stop the whole walk.
|
|
63
|
+
* - A number: go on at that index among the node's siblings, after the visitor
|
|
64
|
+
* removed or inserted siblings. `[action, index]` combines both.
|
|
65
|
+
*
|
|
66
|
+
* `ancestors` runs from `tree` to the node's parent. Siblings share one array:
|
|
67
|
+
* a visitor MUST NOT change it, and MAY keep it, since a later visit never
|
|
68
|
+
* changes it either.
|
|
69
|
+
*
|
|
70
|
+
* With a type test, the walk does not enter a node that cannot contain that
|
|
71
|
+
* type in an HL7v2 tree (Root → Group → Segment → Field → FieldRepetition →
|
|
72
|
+
* Component → Subcomponent): `visit(tree, "segment", …)` never enters a
|
|
73
|
+
* segment. Nodes of a type `@glion/ast` does not define are always entered.
|
|
74
|
+
*
|
|
75
|
+
* @param tree - The node the walk starts at, of any type.
|
|
76
|
+
* @param test - Which nodes the visitor is called with.
|
|
77
|
+
* @param visitor - Called with each node that passes `test`.
|
|
61
78
|
*/
|
|
62
79
|
function visit(tree, arg2, arg3) {
|
|
63
80
|
let test = null;
|
|
@@ -67,23 +84,52 @@ function visit(tree, arg2, arg3) {
|
|
|
67
84
|
test = arg2;
|
|
68
85
|
visitor = arg3;
|
|
69
86
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return true;
|
|
75
|
-
};
|
|
76
|
-
visitParents(tree, captureIndex, (node, ancestors) => {
|
|
77
|
-
if (!predicate(node, ancestors)) return;
|
|
78
|
-
const info = {
|
|
79
|
-
depth: ancestors.length + 1,
|
|
80
|
-
index: currentIndex,
|
|
81
|
-
metadata: "name" in node ? { name: node.name } : void 0,
|
|
82
|
-
sequence: currentIndex + 1
|
|
83
|
-
};
|
|
84
|
-
return visitor(node, ancestors, info);
|
|
87
|
+
walk(tree, [], {
|
|
88
|
+
predicate: createTest(test),
|
|
89
|
+
unmatchableBelow: UNMATCHABLE_BELOW.get(test),
|
|
90
|
+
visitor
|
|
85
91
|
});
|
|
86
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* The depth of each `@glion/ast` node type in an HL7v2 tree. A group may hold
|
|
95
|
+
* groups, so it is the one type found below its own depth.
|
|
96
|
+
*/
|
|
97
|
+
const DEPTH = new Map([
|
|
98
|
+
["root", 0],
|
|
99
|
+
["group", 1],
|
|
100
|
+
["segment", 2],
|
|
101
|
+
["field", 3],
|
|
102
|
+
["field-repetition", 4],
|
|
103
|
+
["component", 5],
|
|
104
|
+
["subcomponent", 6]
|
|
105
|
+
]);
|
|
106
|
+
/**
|
|
107
|
+
* For each node type, the node types whose children cannot include a node of
|
|
108
|
+
* that type. Any other test, such as an object or a function, finds nothing.
|
|
109
|
+
*/
|
|
110
|
+
const UNMATCHABLE_BELOW = new Map([...DEPTH].map(([type, depth]) => [type, new Set([...DEPTH].filter(([parent, parentDepth]) => parentDepth >= depth && !(parent === "group" && type === "group")).map(([parent]) => parent))]));
|
|
111
|
+
/**
|
|
112
|
+
* Visits `node`, then its children in order, and returns the visitor's result
|
|
113
|
+
* for `node`, or the first EXIT a descendant's visitor returns.
|
|
114
|
+
*
|
|
115
|
+
* Reads `children` after the visitor runs, and again after each child, so a
|
|
116
|
+
* visitor may change its own children or its parent's.
|
|
117
|
+
*/
|
|
118
|
+
function walk(node, ancestors, context) {
|
|
119
|
+
const result = context.predicate(node, ancestors) ? context.visitor(node, ancestors) : void 0;
|
|
120
|
+
const action = Array.isArray(result) ? result[0] : result;
|
|
121
|
+
if (action === EXIT$1 || action === SKIP$1 || !("children" in node) || context.unmatchableBelow?.has(node.type)) return result;
|
|
122
|
+
const childAncestors = [...ancestors, node];
|
|
123
|
+
let index = 0;
|
|
124
|
+
while (index < node.children.length) {
|
|
125
|
+
const child = node.children[index];
|
|
126
|
+
const childResult = walk(child, childAncestors, context);
|
|
127
|
+
if ((Array.isArray(childResult) ? childResult[0] : childResult) === EXIT$1) return childResult;
|
|
128
|
+
const next = Array.isArray(childResult) ? childResult[1] : childResult;
|
|
129
|
+
index = typeof next === "number" ? next : index + 1;
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
87
133
|
//#endregion
|
|
88
134
|
export { EXIT, SKIP, visit };
|
|
89
135
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["import type { Nodes } from \"@glion/ast\";\n\nimport type { Predicate, Test } from \"./types\";\n\n/**\n * The predicate a `visit` test describes.\n *\n * - `null` matches every node.\n * - A string matches nodes whose `type` equals it.\n * - A function is the predicate.\n * - An object matches nodes whose own property equals (`===`) each of its values.\n * A property the node does not own reads as `undefined`, so an `undefined`\n * value matches an absent property, and a key that names an inherited member\n * (`constructor`, `toString`, an own `__proto__`) matches no node. The\n * object's entries are read once, when the predicate is created.\n *\n * @example\n * ```typescript\n * const msh = s(\"MSH\", f(\"|\"));\n *\n * createTest(null)(msh, []); // true\n * createTest(\"segment\")(msh, []); // true\n * createTest(\"field\")(msh, []); // false\n *\n * createTest({ type: \"segment\", name: \"MSH\" })(msh, []); // true\n * createTest({ name: \"PID\" })(msh, []); // false\n *\n * // `undefined` matches a property the node does not have\n * createTest({ name: undefined })(f(\"|\"), []); // true\n *\n * // An inherited member is never matched\n * createTest({ constructor: Object })(msh, []); // false\n *\n * // A function receives the ancestors as well\n * const inGroup = createTest((_node, ancestors) => ancestors.length > 1);\n * ```;\n *\n * @param test - `null`, a node type, a property object, or a predicate.\n * @returns The predicate.\n */\nexport function createTest(test: Test<Nodes>): Predicate {\n if (test === null) {\n return () => true;\n }\n if (typeof test === \"string\") {\n return (node) => node.type === test;\n }\n if (typeof test === \"function\") {\n // Used as is: `visit` calls it with `(node, ancestors)`, so it may match on\n // a node's position in the tree as well as on the node itself.\n return test;\n }\n\n // Read once here rather than per visited node: a visit calls the predicate\n // for every node, and a test object changed during the visit does not change\n // which nodes match.\n const entries = Object.entries(test);\n\n return (node) =>\n entries.every(\n ([key, value]) =>\n // `Object.hasOwn` limits the match to the node's own properties. Without\n // it, `node[key]` also finds inherited members, so `{ constructor: x }`\n // or `{ toString: x }` would be compared against `Object.prototype`.\n // `Reflect.get(node, key)` is `node[key]`; it compiles for an arbitrary\n // string key, which `Nodes` (no index signature) does not allow.\n (Object.hasOwn(node, key) ? Reflect.get(node, key) : undefined) ===\n value\n );\n}\n","/**\n * HL7v2 AST Visitor\n *\n * Wraps unist-util-visit-parents to add HL7v2-specific context\n * (index, sequence, depth, metadata) while delegating core traversal.\n */\n\nimport type { Nodes } from \"@glion/ast\";\nimport { visitParents } from \"unist-util-visit-parents\";\n\nimport type { Test, VisitInfo, Visitor } from \"./types\";\nimport { createTest } from \"./utils\";\n\nexport type { VisitorResult } from \"unist-util-visit-parents\";\n\nexport { EXIT, SKIP } from \"unist-util-visit-parents\";\n\nexport type { Predicate, Test, VisitInfo, Visitor } from \"./types\";\n\n// Overload signatures\nexport function visit(tree: Nodes, visitor: Visitor): void;\nexport function visit<Type extends Nodes[\"type\"]>(\n tree: Nodes,\n test: Type,\n visitor: Visitor<Extract<Nodes, { type: Type }>>\n): void;\nexport function visit<T extends Nodes>(\n tree: Nodes,\n test: Test<T>,\n visitor: Visitor<T>\n): void;\n\n/**\n * Visit nodes in an HL7 AST tree.\n * Wraps unist-util-visit-parents to add HL7v2-specific context.\n *\n * @param tree - The tree to traverse (can be any node type, not just Root)\n * @param test - Optional test to filter nodes (type string, partial match, or\n * Test function)\n * @param visitor - Function called for each matching node\n *\n * **Important**: If you pass a function as the second argument, it is always\n * treated as a Visitor, never as a Test. To use a Test function, you MUST\n * provide both the test and visitor parameters: `visit(tree, testFn,\n * visitorFn)`.\n */\nexport function visit<T extends Nodes>(\n tree: Nodes,\n arg2: Visitor<T> | Test<T>,\n arg3?: Visitor<T>\n): void {\n let test: Test<T> = null;\n let visitor: Visitor<T>;\n\n if (arg3 === undefined) {\n visitor = arg2 as Visitor<T>;\n } else {\n test = arg2 as Test<T>;\n visitor = arg3;\n }\n\n const predicate = createTest(test as Test<Nodes>);\n\n // visit-parents passes a node's index among its parent's children only to\n // its test, `test(node, index, parent)`, never to its visitor. It calls the\n // test and, when the test passes, the visitor on that same node with nothing\n // in between (unist-util-visit-parents 6.0.2, lib/index.js; the version is\n // pinned). `captureIndex` is that test: it stores the index for the visitor\n // to read and passes every node. The index is the node's position when it\n // is visited, so it reflects siblings a visitor inserted or removed earlier.\n let currentIndex = 0;\n const captureIndex = (_node: unknown, index?: number): boolean => {\n // The node `visit` starts from has no parent, so it has no index.\n currentIndex = index ?? 0;\n return true;\n };\n\n visitParents(tree, captureIndex, (node, ancestors) => {\n // The caller's test runs here, not in `captureIndex`: it may read\n // `ancestors`, which visit-parents gives only to the visitor.\n if (!predicate(node, ancestors)) {\n // Returning nothing means \"continue\": visit-parents still walks into\n // this node's children, so `visit(tree, \"field\", …)` reaches the fields\n // of segments it does not match.\n return;\n }\n\n const info: VisitInfo = {\n depth: ancestors.length + 1,\n index: currentIndex,\n metadata: \"name\" in node ? { name: node.name } : undefined,\n sequence: currentIndex + 1,\n };\n\n // visit-parents reads the visitor's return value to decide what to visit\n // next, so the caller's result is passed through unchanged:\n // - nothing or CONTINUE: walk into this node's children;\n // - SKIP: skip this node's children;\n // - EXIT: stop the walk;\n // - a number: continue at that index among this node's siblings, as after\n // removing the current node with `splice(info.index, 1)`;\n // - [action, index]: both.\n // Calling the visitor without returning its result would turn every one\n // of these into \"continue\".\n return visitor(node as T, ancestors, info);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,WAAW,MAA8B;AACvD,KAAI,SAAS,KACX,cAAa;AAEf,KAAI,OAAO,SAAS,SAClB,SAAQ,SAAS,KAAK,SAAS;AAEjC,KAAI,OAAO,SAAS,WAGlB,QAAO;CAMT,MAAM,UAAU,OAAO,QAAQ,KAAK;AAEpC,SAAQ,SACN,QAAQ,OACL,CAAC,KAAK,YAMJ,OAAO,OAAO,MAAM,IAAI,GAAG,QAAQ,IAAI,MAAM,IAAI,GAAG,KAAA,OACrD,MACH;;;;;;;;;;;;;;;;;;ACtBL,SAAgB,MACd,MACA,MACA,MACM;CACN,IAAI,OAAgB;CACpB,IAAI;AAEJ,KAAI,SAAS,KAAA,EACX,WAAU;MACL;AACL,SAAO;AACP,YAAU;;CAGZ,MAAM,YAAY,WAAW,KAAoB;CASjD,IAAI,eAAe;CACnB,MAAM,gBAAgB,OAAgB,UAA4B;AAEhE,iBAAe,SAAS;AACxB,SAAO;;AAGT,cAAa,MAAM,eAAe,MAAM,cAAc;AAGpD,MAAI,CAAC,UAAU,MAAM,UAAU,CAI7B;EAGF,MAAM,OAAkB;GACtB,OAAO,UAAU,SAAS;GAC1B,OAAO;GACP,UAAU,UAAU,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,KAAA;GACjD,UAAU,eAAe;GAC1B;AAYD,SAAO,QAAQ,MAAW,WAAW,KAAK;GAC1C"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["EXIT","SKIP"],"sources":["../src/utils.ts","../src/index.ts"],"sourcesContent":["import type { Nodes } from \"@glion/ast\";\n\nimport type { Predicate, Test } from \"./types\";\n\n/**\n * The predicate a `visit` test describes.\n *\n * - `null` matches every node.\n * - A string matches nodes whose `type` equals it.\n * - A function is the predicate.\n * - An object matches nodes whose own property equals (`===`) each of its values.\n * A property the node does not own reads as `undefined`, so an `undefined`\n * value matches an absent property, and a key that names an inherited member\n * (`constructor`, `toString`, an own `__proto__`) matches no node. The\n * object's entries are read once, when the predicate is created.\n *\n * @example\n * ```typescript\n * const msh = s(\"MSH\", f(\"|\"));\n *\n * createTest(null)(msh, []); // true\n * createTest(\"segment\")(msh, []); // true\n * createTest(\"field\")(msh, []); // false\n *\n * createTest({ type: \"segment\", name: \"MSH\" })(msh, []); // true\n * createTest({ name: \"PID\" })(msh, []); // false\n *\n * // `undefined` matches a property the node does not have\n * createTest({ name: undefined })(f(\"|\"), []); // true\n *\n * // An inherited member is never matched\n * createTest({ constructor: Object })(msh, []); // false\n *\n * // A function receives the ancestors as well\n * const inGroup = createTest((_node, ancestors) => ancestors.length > 1);\n * ```;\n *\n * @param test - `null`, a node type, a property object, or a predicate.\n * @returns The predicate.\n */\nexport function createTest(test: Test<Nodes>): Predicate {\n if (test === null) {\n return () => true;\n }\n if (typeof test === \"string\") {\n return (node) => node.type === test;\n }\n if (typeof test === \"function\") {\n // Used as is: `visit` calls it with `(node, ancestors)`, so it may match on\n // a node's position in the tree as well as on the node itself.\n return test;\n }\n\n // Read once here rather than per visited node: a visit calls the predicate\n // for every node, and a test object changed during the visit does not change\n // which nodes match.\n const entries = Object.entries(test);\n\n return (node) =>\n entries.every(\n ([key, value]) =>\n // `Object.hasOwn` limits the match to the node's own properties. Without\n // it, `node[key]` also finds inherited members, so `{ constructor: x }`\n // or `{ toString: x }` would be compared against `Object.prototype`.\n // `Reflect.get(node, key)` is `node[key]`; it compiles for an arbitrary\n // string key, which `Nodes` (no index signature) does not allow.\n (Object.hasOwn(node, key) ? Reflect.get(node, key) : undefined) ===\n value\n );\n}\n","/**\n * HL7v2 AST visitor, typed for `@glion/ast`, with the semantics of\n * `unist-util-visit-parents`.\n *\n * @module\n */\n\nimport type { Nodes } from \"@glion/ast\";\nimport { EXIT, SKIP } from \"unist-util-visit-parents\";\nimport type { VisitorResult } from \"unist-util-visit-parents\";\n\nimport type { Predicate, Test, Visitor } from \"./types\";\nimport { createTest } from \"./utils\";\n\nexport type { VisitorResult } from \"unist-util-visit-parents\";\n\nexport { EXIT, SKIP } from \"unist-util-visit-parents\";\n\nexport type { Predicate, Test, Visitor } from \"./types\";\n\n// Overload signatures\nexport function visit(tree: Nodes, visitor: Visitor): void;\nexport function visit<Type extends Nodes[\"type\"]>(\n tree: Nodes,\n test: Type,\n visitor: Visitor<Extract<Nodes, { type: Type }>>\n): void;\nexport function visit<T extends Nodes>(\n tree: Nodes,\n test: Test<T>,\n visitor: Visitor<T>\n): void;\n\n/**\n * Visits `tree` and every node below it, depth-first and in document order,\n * calling `visitor` with each node that passes `test` and that node's\n * ancestors.\n *\n * `test` is a node type (`\"segment\"`), an object whose own properties a node\n * must equal (`{ name: \"PID\" }`), or a function of `(node, ancestors)`. With no\n * test, every node passes. A function passed as the second argument is always\n * the visitor; a test function needs the three-argument form.\n *\n * The visitor's result decides what the walk does next:\n *\n * - Nothing: enter the node's children, then go on.\n * - `SKIP`: do not enter the node's children.\n * - `EXIT`: stop the whole walk.\n * - A number: go on at that index among the node's siblings, after the visitor\n * removed or inserted siblings. `[action, index]` combines both.\n *\n * `ancestors` runs from `tree` to the node's parent. Siblings share one array:\n * a visitor MUST NOT change it, and MAY keep it, since a later visit never\n * changes it either.\n *\n * With a type test, the walk does not enter a node that cannot contain that\n * type in an HL7v2 tree (Root → Group → Segment → Field → FieldRepetition →\n * Component → Subcomponent): `visit(tree, \"segment\", …)` never enters a\n * segment. Nodes of a type `@glion/ast` does not define are always entered.\n *\n * @param tree - The node the walk starts at, of any type.\n * @param test - Which nodes the visitor is called with.\n * @param visitor - Called with each node that passes `test`.\n */\nexport function visit<T extends Nodes>(\n tree: Nodes,\n arg2: Visitor<T> | Test<T>,\n arg3?: Visitor<T>\n): void {\n let test: Test<T> = null;\n let visitor: Visitor<T>;\n\n if (arg3 === undefined) {\n visitor = arg2 as Visitor<T>;\n } else {\n test = arg2 as Test<T>;\n visitor = arg3;\n }\n\n walk(tree, [], {\n predicate: createTest(test as Test<Nodes>),\n unmatchableBelow: UNMATCHABLE_BELOW.get(test),\n visitor: visitor as Visitor<Nodes>,\n });\n}\n\n/** What one `visit` call holds fixed for every node it walks. */\ninterface Walk {\n predicate: Predicate;\n /** Node types whose children cannot pass the test. */\n unmatchableBelow: ReadonlySet<string> | undefined;\n visitor: Visitor<Nodes>;\n}\n\n/**\n * The depth of each `@glion/ast` node type in an HL7v2 tree. A group may hold\n * groups, so it is the one type found below its own depth.\n */\nconst DEPTH: ReadonlyMap<string, number> = new Map([\n [\"root\", 0],\n [\"group\", 1],\n [\"segment\", 2],\n [\"field\", 3],\n [\"field-repetition\", 4],\n [\"component\", 5],\n [\"subcomponent\", 6],\n]);\n\n/**\n * For each node type, the node types whose children cannot include a node of\n * that type. Any other test, such as an object or a function, finds nothing.\n */\nconst UNMATCHABLE_BELOW: ReadonlyMap<unknown, ReadonlySet<string>> = new Map(\n [...DEPTH].map(([type, depth]) => [\n type,\n new Set(\n [...DEPTH]\n .filter(\n ([parent, parentDepth]) =>\n parentDepth >= depth && !(parent === \"group\" && type === \"group\")\n )\n .map(([parent]) => parent)\n ),\n ])\n);\n\n/**\n * Visits `node`, then its children in order, and returns the visitor's result\n * for `node`, or the first EXIT a descendant's visitor returns.\n *\n * Reads `children` after the visitor runs, and again after each child, so a\n * visitor may change its own children or its parent's.\n */\nfunction walk(node: Nodes, ancestors: Nodes[], context: Walk): VisitorResult {\n const result = context.predicate(node, ancestors)\n ? context.visitor(node, ancestors)\n : undefined;\n const action = Array.isArray(result) ? result[0] : result;\n\n if (\n action === EXIT ||\n action === SKIP ||\n !(\"children\" in node) ||\n context.unmatchableBelow?.has(node.type)\n ) {\n return result;\n }\n\n // One array for all of this node's children: the visitor's contract forbids\n // changing it, so siblings can share it.\n const childAncestors = [...ancestors, node];\n let index = 0;\n\n while (index < node.children.length) {\n const child = node.children[index] as Nodes;\n const childResult = walk(child, childAncestors, context);\n const childAction = Array.isArray(childResult)\n ? childResult[0]\n : childResult;\n\n if (childAction === EXIT) {\n return childResult;\n }\n\n // A child's visitor that returns an index (alone or as `[action, index]`)\n // names the sibling to visit next; otherwise the walk moves one along.\n const next = Array.isArray(childResult) ? childResult[1] : childResult;\n index = typeof next === \"number\" ? next : index + 1;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,WAAW,MAA8B;AACvD,KAAI,SAAS,KACX,cAAa;AAEf,KAAI,OAAO,SAAS,SAClB,SAAQ,SAAS,KAAK,SAAS;AAEjC,KAAI,OAAO,SAAS,WAGlB,QAAO;CAMT,MAAM,UAAU,OAAO,QAAQ,KAAK;AAEpC,SAAQ,SACN,QAAQ,OACL,CAAC,KAAK,YAMJ,OAAO,OAAO,MAAM,IAAI,GAAG,QAAQ,IAAI,MAAM,IAAI,GAAG,KAAA,OACrD,MACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACJL,SAAgB,MACd,MACA,MACA,MACM;CACN,IAAI,OAAgB;CACpB,IAAI;AAEJ,KAAI,SAAS,KAAA,EACX,WAAU;MACL;AACL,SAAO;AACP,YAAU;;AAGZ,MAAK,MAAM,EAAE,EAAE;EACb,WAAW,WAAW,KAAoB;EAC1C,kBAAkB,kBAAkB,IAAI,KAAK;EACpC;EACV,CAAC;;;;;;AAeJ,MAAM,QAAqC,IAAI,IAAI;CACjD,CAAC,QAAQ,EAAE;CACX,CAAC,SAAS,EAAE;CACZ,CAAC,WAAW,EAAE;CACd,CAAC,SAAS,EAAE;CACZ,CAAC,oBAAoB,EAAE;CACvB,CAAC,aAAa,EAAE;CAChB,CAAC,gBAAgB,EAAE;CACpB,CAAC;;;;;AAMF,MAAM,oBAA+D,IAAI,IACvE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,WAAW,CAChC,MACA,IAAI,IACF,CAAC,GAAG,MAAM,CACP,QACE,CAAC,QAAQ,iBACR,eAAe,SAAS,EAAE,WAAW,WAAW,SAAS,SAC5D,CACA,KAAK,CAAC,YAAY,OAAO,CAC7B,CACF,CAAC,CACH;;;;;;;;AASD,SAAS,KAAK,MAAa,WAAoB,SAA8B;CAC3E,MAAM,SAAS,QAAQ,UAAU,MAAM,UAAU,GAC7C,QAAQ,QAAQ,MAAM,UAAU,GAChC,KAAA;CACJ,MAAM,SAAS,MAAM,QAAQ,OAAO,GAAG,OAAO,KAAK;AAEnD,KACE,WAAWA,UACX,WAAWC,UACX,EAAE,cAAc,SAChB,QAAQ,kBAAkB,IAAI,KAAK,KAAK,CAExC,QAAO;CAKT,MAAM,iBAAiB,CAAC,GAAG,WAAW,KAAK;CAC3C,IAAI,QAAQ;AAEZ,QAAO,QAAQ,KAAK,SAAS,QAAQ;EACnC,MAAM,QAAQ,KAAK,SAAS;EAC5B,MAAM,cAAc,KAAK,OAAO,gBAAgB,QAAQ;AAKxD,OAJoB,MAAM,QAAQ,YAAY,GAC1C,YAAY,KACZ,iBAEgBD,OAClB,QAAO;EAKT,MAAM,OAAO,MAAM,QAAQ,YAAY,GAAG,YAAY,KAAK;AAC3D,UAAQ,OAAO,SAAS,WAAW,OAAO,QAAQ;;AAGpD,QAAO"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,33 +1,13 @@
|
|
|
1
1
|
import type { Nodes } from "@glion/ast";
|
|
2
2
|
import type { VisitorResult } from "unist-util-visit-parents";
|
|
3
|
-
/**
|
|
4
|
-
* Visit information computed for each node.
|
|
5
|
-
* This is HL7v2-specific context not provided by unist-util-visit-parents.
|
|
6
|
-
*/
|
|
7
|
-
export interface VisitInfo {
|
|
8
|
-
/**
|
|
9
|
-
* 0-based index among the parent's children when the node is visited, or 0
|
|
10
|
-
* for the node the visit starts from. Reflects changes a visitor made to
|
|
11
|
-
* those children earlier in the same visit.
|
|
12
|
-
*/
|
|
13
|
-
index: number;
|
|
14
|
-
/** `index` + 1, the HL7v2 sequence number. */
|
|
15
|
-
sequence: number;
|
|
16
|
-
/** 1-based depth in tree (root = 1) */
|
|
17
|
-
depth: number;
|
|
18
|
-
/** Metadata extracted from node (e.g., { name: "MSH" } or { name: "PATIENT" }) */
|
|
19
|
-
metadata: Record<string, unknown> | undefined;
|
|
20
|
-
}
|
|
21
3
|
/**
|
|
22
4
|
* A function called for each node during traversal.
|
|
23
|
-
* Extends unist's visitor signature with HL7v2-specific context.
|
|
24
5
|
*
|
|
25
6
|
* @param node - Current node being visited
|
|
26
7
|
* @param ancestors - Array of ancestor nodes from root to parent
|
|
27
|
-
* @param info - HL7v2-specific visit info (index, sequence, depth, metadata)
|
|
28
8
|
* @returns VisitorResult to control traversal (EXIT, SKIP, etc.)
|
|
29
9
|
*/
|
|
30
|
-
export type Visitor<T extends Nodes = Nodes> = (node: T, ancestors: Nodes[]
|
|
10
|
+
export type Visitor<T extends Nodes = Nodes> = (node: T, ancestors: Nodes[]) => VisitorResult;
|
|
31
11
|
/**
|
|
32
12
|
* Predicate function to test if a node matches filter criteria.
|
|
33
13
|
* Compatible with unist-util-is predicates but uses our Nodes type.
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,IAAI,CAC7C,IAAI,EAAE,CAAC,EACP,SAAS,EAAE,KAAK,EAAE,KACf,aAAa,CAAC;AAEnB;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;AAErE;;;;;;;;;;GAUG;AACH,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,IACpC,MAAM,GACN,OAAO,CAAC,CAAC,CAAC,GACV,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,GAChD,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,GAC9C,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glion/util-visit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"description": "An AST visitor for HL7v2 messages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"health",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"tsdown": "0.21.10",
|
|
46
46
|
"typescript": "6.0.3",
|
|
47
47
|
"vitest": "4.1.11",
|
|
48
|
-
"@glion/ast": "0.
|
|
49
|
-
"@glion/builder": "0.
|
|
48
|
+
"@glion/ast": "0.27.1",
|
|
49
|
+
"@glion/builder": "0.27.1",
|
|
50
50
|
"@glion/check-readme": "0.18.0",
|
|
51
|
-
"@glion/parser": "0.
|
|
51
|
+
"@glion/parser": "0.27.1",
|
|
52
52
|
"@glion/testing": "0.18.0",
|
|
53
53
|
"@glion/tsconfig": "0.18.0"
|
|
54
54
|
},
|