@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Rethink Health
3
+ Copyright (c) 2023 Rethink Health, SUARL
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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, depth, and HL7v2-aware sequence numbers.
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. Each call receives the node, its ancestors (root-to-parent), and a `VisitInfo` record with index, HL7v2 sequence number, depth, and extracted metadata. Supports filtering by type string, property object, or predicate, and exposes `SKIP`/`EXIT` control actions. Delegates core traversal to `unist-util-visit-parents`.
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 { visit, EXIT, SKIP } from "@glion/util-visit";
19
- import { parse } from "@glion/parser";
18
+ import { parseHL7v2 } from "@glion/parser";
19
+ import { SKIP, visit } from "@glion/util-visit";
20
20
 
21
- const message = parse("MSH|^~\\&|...\rPID|...");
22
-
23
- // Visit all segments
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
- // Find fields with parent context
29
- visit(message, "field", (node, ancestors, info) => {
30
- const segment = ancestors.find((n) => n.type === "segment");
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 processing of sensitive segments
35
- visit(message, (node, ancestors, info) => {
36
- if (node.type === "segment" && info.metadata?.header === "NTE") {
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., `{ name: "PATIENT_GROUP" }`). 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.
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
- #### Important: test vs visitor functions
59
+ #### Test vs visitor functions
64
60
 
65
- **If you pass a function as the second argument, it is always treated as a Visitor, never as a Test.**
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
- // WRONG — testFn will be treated as a visitor, not a test
69
- visit(ast, (node) => node.type === 'segment', ...); // Missing visitor!
70
-
71
- // CORRECT — Explicit 3-argument form
72
- visit(ast, (node) => node.type === 'segment', (node, ancestors, info) => {
73
- console.log('Visiting segment');
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` — Array of ancestor nodes from root to parent (not including current node).
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 `void` — Continue traversal normally.
101
- - `SKIP` — Skip children of current node.
102
- - `EXIT` — Stop traversal immediately.
103
-
104
- ### `VisitInfo`
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
- ```typescript
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, "field", (node, ancestors, info) => {
142
- if (isEmptyNode(node)) {
143
- ancestors.at(-1).children.splice(info.index, 1);
144
- return info.index;
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
- ### Automatic metadata extraction
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, // Main traversal function
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
- VisitInfo, // { index, sequence, depth, metadata }
169
- Visitor, // (node, ancestors, info) => VisitorResult
170
- VisitorResult, // Return type from visitor
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
- visit(ast, { name: "PATIENT_GROUP" }, (node, ancestors, info) => {
190
- console.log("Inside PATIENT_GROUP");
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
- ### Custom test function
136
+ ### Read the ancestors
195
137
 
196
138
  ```typescript
197
- // Visit fields in MSH segment only
198
- visit(
199
- ast,
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
- ### Control flow: skip children
145
+ ### Read the segment groups above a segment
221
146
 
222
147
  ```typescript
223
- visit(ast, (node, ancestors, info) => {
224
- if (node.type === "segment" && info.metadata?.header === "OBX") {
225
- return SKIP; // Don't process OBX segment children
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
- ### Control flow: exit early
156
+ ### Stop at the first match
231
157
 
232
158
  ```typescript
233
159
  import { EXIT } from "@glion/util-visit";
234
160
 
235
- let found = false;
236
- visit(ast, "field", (node, ancestors, info) => {
237
- if (/* some condition */) {
238
- found = true;
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 { s, f, c } from "@glion/builder";
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
- // Traverse from segment (not root)
253
- visit(segment, "field", (node, ancestors, info) => {
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 Visitor
2
+ * HL7v2 AST visitor, typed for `@glion/ast`, with the semantics of
3
+ * `unist-util-visit-parents`.
3
4
  *
4
- * Wraps unist-util-visit-parents to add HL7v2-specific context
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, VisitInfo, Visitor } from "./types";
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;
@@ -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;AAGxC,OAAO,KAAK,EAAE,IAAI,EAAa,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,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGnE,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"}
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, visitParents } from "unist-util-visit-parents";
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
- * Visit nodes in an HL7 AST tree.
50
- * Wraps unist-util-visit-parents to add HL7v2-specific context.
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
- * @param tree - The tree to traverse (can be any node type, not just Root)
53
- * @param test - Optional test to filter nodes (type string, partial match, or
54
- * Test function)
55
- * @param visitor - Function called for each matching node
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
- * **Important**: If you pass a function as the second argument, it is always
58
- * treated as a Visitor, never as a Test. To use a Test function, you MUST
59
- * provide both the test and visitor parameters: `visit(tree, testFn,
60
- * visitorFn)`.
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
- const predicate = createTest(test);
71
- let currentIndex = 0;
72
- const captureIndex = (_node, index) => {
73
- currentIndex = index ?? 0;
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[], info: VisitInfo) => VisitorResult;
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.
@@ -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;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IAEjB,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IAEd,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC/C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,IAAI,CAC7C,IAAI,EAAE,CAAC,EACP,SAAS,EAAE,KAAK,EAAE,EAClB,IAAI,EAAE,SAAS,KACZ,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"}
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.26.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.26.0",
49
- "@glion/builder": "0.26.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.26.0",
51
+ "@glion/parser": "0.27.1",
52
52
  "@glion/testing": "0.18.0",
53
53
  "@glion/tsconfig": "0.18.0"
54
54
  },