@glion/annotate-profile-fields 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @glion/annotate-profile-fields
2
2
 
3
- Unified plugin to annotate HL7v2 field nodes with profile metadata.
3
+ Unified plugin that annotates each HL7v2 field with its row in the segment attribute table.
4
4
 
5
5
  ## What it does
6
6
 
7
- Enriches every `field` node in a parsed HL7v2 tree with metadata drawn from the HL7v2 specification profile — field name, datatype, required/repeatable flags, maximum length, and table reference — so the AST becomes self-describing. The plugin reads the message version from MSH-12, loads the matching field definitions from `@glion/profiles`, and spreads the profile properties onto `field.data`. Unknown segments (Z-segments) and unsupported versions are silently skipped.
7
+ Puts the HL7v2 standard's description of each field on the field itself: its ID, name, optionality, repetitions, datatype, maximum length, and table, for the version in MSH-12. The tree can then be read without looking anything up. It needs `@glion/annotate-profile-context`, which loads the profile, to run before it. Fields of segments the standard does not define, such as Z-segments, are left as they are.
8
8
 
9
9
  ## Install
10
10
 
@@ -15,74 +15,57 @@ npm install @glion/annotate-profile-fields
15
15
  ## Use
16
16
 
17
17
  ```ts
18
+ import { hl7v2AnnotateProfileContext } from "@glion/annotate-profile-context";
18
19
  import { hl7v2AnnotateProfileFields } from "@glion/annotate-profile-fields";
19
- import { hl7v2Parser } from "@glion/parser";
20
+ import { parseHL7v2 } from "@glion/parser";
20
21
  import { unified } from "unified";
21
22
 
22
- const processor = unified().use(hl7v2Parser).use(hl7v2AnnotateProfileFields);
23
+ const tree = parseHL7v2(
24
+ "MSH|^~\\&|SENDER||RECEIVER||20241201||ADT^A01^ADT_A01|MSG123|P|2.5\rPID|1||12345||Doe^John"
25
+ );
23
26
 
24
- const message =
25
- "MSH|^~\\&|SENDER||RECEIVER||20241201||ADT^A01^ADT_A01|MSG123|P|2.5\rPID|1||12345||Doe^John";
27
+ await unified()
28
+ .use(hl7v2AnnotateProfileContext)
29
+ .use(hl7v2AnnotateProfileFields)
30
+ .run(tree);
26
31
 
27
- const tree = processor.parse(message);
28
- await processor.run(tree);
29
-
30
- // Field nodes now carry profile metadata on field.data
31
- // e.g., PID-3: { id: "PID-3", name: "Patient Identifier List", required: true, repeatable: true, datatype: "CX", ... }
32
+ // PID-3 field.data:
33
+ // {
34
+ // sequence: 3, id: "PID-3", name: "Patient Identifier List", item: "106",
35
+ // optionality: "R", repetitions: "unbounded", datatype: "CX", maxLength: 250
36
+ // }
32
37
  ```
33
38
 
34
39
  ## API
35
40
 
36
- This package exports the named constant `hl7v2AnnotateProfileFields`. The default export is the plugin itself.
37
-
38
41
  ### `unified().use(hl7v2AnnotateProfileFields)`
39
42
 
40
- Annotate Field nodes with profile metadata. The plugin:
41
-
42
- 1. Reads the HL7v2 version from MSH-12.
43
- 2. Loads field definitions for all segments in the message from `@glion/profiles`.
44
- 3. Visits each Field node and spreads the matching profile properties onto `field.data`.
43
+ A `unified` plugin. Takes no options. The package exports it as `hl7v2AnnotateProfileFields` and as its default export.
45
44
 
46
- ###### Returns
47
-
48
- Async transformer (`async function (Root) => Root`).
45
+ Writes the field's row onto `field.data` of every field the profile describes, empty fields included. Data already on `field.data` is kept, except the properties below, which are overwritten.
49
46
 
50
47
  ## What it annotates
51
48
 
52
- Importing this package augments the `FieldData` interface from `@glion/ast`:
53
-
54
- | Property | Type | Description |
55
- | ------------ | --------- | ---------------------------------------------------- |
56
- | `id` | `string` | Field identifier (e.g., `"MSH-9"`, `"PID-3"`) |
57
- | `name` | `string` | Human-readable field name (e.g., `"Patient Name"`) |
58
- | `required` | `boolean` | Whether this field is required in the segment |
59
- | `repeatable` | `boolean` | Whether this field supports repetition |
60
- | `datatype` | `string` | Datatype ID (e.g., `"XPN"`, `"ST"`, `"CWE"`) |
61
- | `maxLength` | `number` | Maximum allowed length (when defined in the profile) |
62
- | `table` | `string` | Table reference for coded fields (e.g., `"HL70001"`) |
63
- | `item` | `string` | HL7 specification item number |
64
-
65
- All properties are optional (`undefined` when not available in the profile).
49
+ | Property | Type | Column |
50
+ | ------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
51
+ | `sequence` | `number` | SEQ: the field's position in the segment |
52
+ | `id` | `string` | The segment ID and sequence, such as `"PID-3"` |
53
+ | `name` | `string` | ELEMENT NAME, such as `"Patient Identifier List"` |
54
+ | `optionality` | `"R" \| "RE" \| "O" \| "C" \| "X" \| "B" \| "W"` | OPT: required, required but may be empty, optional, conditional, not used, backward compatible, withdrawn |
55
+ | `repetitions` | `number \| "unbounded"` | RP/#: the most repetitions the field may have; `1` when it does not repeat |
56
+ | `datatype` | `string` | DT, such as `"CX"` |
57
+ | `maxLength` | `number` | LEN |
58
+ | `table` | `string` | TBL#, such as `"HL70001"` |
59
+ | `item` | `string` | ITEM# |
66
60
 
67
- ### Accessing field metadata
61
+ A property is absent when the standard leaves its column blank for that field. A withdrawn field, for example v2.6 `DG1-2`, has `optionality: "W"` and no `datatype`. Importing the package adds these properties to the `FieldData` type of `@glion/ast`.
68
62
 
69
63
  ```ts
70
64
  import { visit } from "@glion/util-visit";
71
65
 
72
- // After running the annotator...
73
- visit(tree, "field", (node) => {
74
- if (node.data?.required && node.data?.name) {
75
- console.log(`Required field: ${node.data.id} (${node.data.name})`);
76
- }
77
- });
78
- ```
79
-
80
- ### Finding coded fields
81
-
82
- ```ts
83
- visit(tree, "field", (node) => {
84
- if (node.data?.table) {
85
- console.log(`${node.data.id} uses table ${node.data.table}`);
66
+ visit(tree, "field", (field) => {
67
+ if (field.data?.optionality === "R") {
68
+ console.log(`${field.data.id} (${field.data.name}) is required`);
86
69
  }
87
70
  });
88
71
  ```
package/dist/index.d.ts CHANGED
@@ -1,15 +1,19 @@
1
1
  import type { Root } from "@glion/ast";
2
+ import type { FieldRepetitions } from "@glion/profiles";
3
+ import type { Optionality } from "@glion/utils";
2
4
  import type { Plugin } from "unified";
3
5
  declare module "@glion/ast" {
4
6
  interface FieldData {
7
+ /** Position of this field in its segment (SEQ). */
8
+ sequence?: number | undefined;
5
9
  /** Field identifier (e.g., "MSH-9", "PID-3"). */
6
10
  id?: string | undefined;
7
11
  /** Human-readable field name from the profile (e.g., "Patient Name"). */
8
12
  name?: string | undefined;
9
- /** Whether this field is required in the segment. */
10
- required?: boolean | undefined;
11
- /** Whether this field supports repetition. */
12
- repeatable?: boolean | undefined;
13
+ /** HL7v2 optionality of this field (OPT). */
14
+ optionality?: Optionality | undefined;
15
+ /** Most occurrences this field may have (RP/#), or `"unbounded"`. */
16
+ repetitions?: FieldRepetitions | undefined;
13
17
  /** Datatype ID for this field (e.g., "XPN", "ST", "CWE"). */
14
18
  datatype?: string | undefined;
15
19
  /** Maximum allowed length for this field. */
@@ -24,8 +28,8 @@ declare module "@glion/ast" {
24
28
  * Unified plugin that annotates Field nodes with profile metadata.
25
29
  *
26
30
  * Reads field definitions from `file.data.fields` (populated by
27
- * `@glion/annotate-profile-context`) and spreads the profile
28
- * properties directly onto each `field.data`.
31
+ * `@glion/annotate-profile-context`) and copies each field's profile, as
32
+ * the profile gives it, onto `field.data`.
29
33
  *
30
34
  * Unknown segments (Z-segments) and unsupported versions are silently skipped.
31
35
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,IAAI,EAAW,MAAM,YAAY,CAAC;AAG3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGtC,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,SAAS;QACjB,iDAAiD;QACjD,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,yEAAyE;QACzE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,qDAAqD;QACrD,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/B,8CAA8C;QAC9C,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACjC,6DAA6D;QAC7D,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,6CAA6C;QAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,0DAA0D;QAC1D,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,qCAAqC;QACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B;CACF;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CA8B3D,CAAC;AAmBJ,eAAe,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGtC,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,SAAS;QACjB,mDAAmD;QACnD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,iDAAiD;QACjD,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,yEAAyE;QACzE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,6CAA6C;QAC7C,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;QACtC,qEAAqE;QACrE,WAAW,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;QAC3C,6DAA6D;QAC7D,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,6CAA6C;QAC7C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,0DAA0D;QAC1D,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,qCAAqC;QACrC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B;CACF;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CA0B3D,CAAC;AAEJ,eAAe,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@ import { SKIP, visit } from "@glion/util-visit";
4
4
  * Unified plugin that annotates Field nodes with profile metadata.
5
5
  *
6
6
  * Reads field definitions from `file.data.fields` (populated by
7
- * `@glion/annotate-profile-context`) and spreads the profile
8
- * properties directly onto each `field.data`.
7
+ * `@glion/annotate-profile-context`) and copies each field's profile, as
8
+ * the profile gives it, onto `field.data`.
9
9
  *
10
10
  * Unknown segments (Z-segments) and unsupported versions are silently skipped.
11
11
  */
@@ -17,22 +17,11 @@ const hl7v2AnnotateProfileFields = () => (tree, file) => {
17
17
  if (segment?.type !== "segment") return SKIP;
18
18
  const profile = ctx.fields.get(segment.name)?.bySequence.get(info.sequence);
19
19
  if (!profile) return SKIP;
20
- if (!node.data) node.data = {};
21
- spreadFieldProfile(node.data, profile);
20
+ Object.assign(node.data ??= {}, profile);
22
21
  return SKIP;
23
22
  });
24
23
  return tree;
25
24
  };
26
- function spreadFieldProfile(data, profile) {
27
- data.id = profile.id;
28
- data.name = profile.name;
29
- data.required = profile.required;
30
- data.repeatable = profile.repeatable;
31
- data.datatype = profile.datatype;
32
- if (profile.maxLength !== void 0) data.maxLength = profile.maxLength;
33
- if (profile.table !== void 0) data.table = profile.table;
34
- if (profile.item !== void 0) data.item = profile.item;
35
- }
36
25
  //#endregion
37
26
  export { hl7v2AnnotateProfileFields as default, hl7v2AnnotateProfileFields };
38
27
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// oxlint-disable-next-line no-unused-vars -- triggers VFile DataMap augmentation\nimport type { ProfileContext } from \"@glion/annotate-profile-context\";\nimport type { FieldData, Root, Segment } from \"@glion/ast\";\nimport type { FieldProfile } from \"@glion/profiles\";\nimport { SKIP, visit } from \"@glion/util-visit\";\nimport type { Plugin } from \"unified\";\nimport type { VFile } from \"vfile\";\n\ndeclare module \"@glion/ast\" {\n interface FieldData {\n /** Field identifier (e.g., \"MSH-9\", \"PID-3\"). */\n id?: string | undefined;\n /** Human-readable field name from the profile (e.g., \"Patient Name\"). */\n name?: string | undefined;\n /** Whether this field is required in the segment. */\n required?: boolean | undefined;\n /** Whether this field supports repetition. */\n repeatable?: boolean | undefined;\n /** Datatype ID for this field (e.g., \"XPN\", \"ST\", \"CWE\"). */\n datatype?: string | undefined;\n /** Maximum allowed length for this field. */\n maxLength?: number | undefined;\n /** Table reference for coded fields (e.g., \"HL70001\"). */\n table?: string | undefined;\n /** HL7 specification item number. */\n item?: string | undefined;\n }\n}\n\n/**\n * Unified plugin that annotates Field nodes with profile metadata.\n *\n * Reads field definitions from `file.data.fields` (populated by\n * `@glion/annotate-profile-context`) and spreads the profile\n * properties directly onto each `field.data`.\n *\n * Unknown segments (Z-segments) and unsupported versions are silently skipped.\n */\nexport const hl7v2AnnotateProfileFields: Plugin<[], Root, Root> =\n () => (tree: Root, file: VFile) => {\n const ctx = file.data.profile;\n if (!ctx) {\n return tree;\n }\n\n // Annotate each field with its profile\n visit(tree, \"field\", (node, ancestors, info) => {\n const segment = ancestors.at(-1) as Segment | undefined;\n if (segment?.type !== \"segment\") {\n return SKIP;\n }\n\n const profile = ctx.fields\n .get(segment.name)\n ?.bySequence.get(info.sequence);\n if (!profile) {\n return SKIP;\n }\n\n if (!node.data) {\n node.data = {};\n }\n\n spreadFieldProfile(node.data, profile);\n return SKIP;\n });\n\n return tree;\n };\n\nfunction spreadFieldProfile(data: FieldData, profile: FieldProfile): void {\n data.id = profile.id;\n data.name = profile.name;\n data.required = profile.required;\n data.repeatable = profile.repeatable;\n data.datatype = profile.datatype;\n if (profile.maxLength !== undefined) {\n data.maxLength = profile.maxLength;\n }\n if (profile.table !== undefined) {\n data.table = profile.table;\n }\n if (profile.item !== undefined) {\n data.item = profile.item;\n }\n}\n\nexport default hl7v2AnnotateProfileFields;\n"],"mappings":";;;;;;;;;;;AAsCA,MAAa,oCACJ,MAAY,SAAgB;CACjC,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,CAAC,IACH,QAAO;AAIT,OAAM,MAAM,UAAU,MAAM,WAAW,SAAS;EAC9C,MAAM,UAAU,UAAU,GAAG,GAAG;AAChC,MAAI,SAAS,SAAS,UACpB,QAAO;EAGT,MAAM,UAAU,IAAI,OACjB,IAAI,QAAQ,KAAK,EAChB,WAAW,IAAI,KAAK,SAAS;AACjC,MAAI,CAAC,QACH,QAAO;AAGT,MAAI,CAAC,KAAK,KACR,MAAK,OAAO,EAAE;AAGhB,qBAAmB,KAAK,MAAM,QAAQ;AACtC,SAAO;GACP;AAEF,QAAO;;AAGX,SAAS,mBAAmB,MAAiB,SAA6B;AACxE,MAAK,KAAK,QAAQ;AAClB,MAAK,OAAO,QAAQ;AACpB,MAAK,WAAW,QAAQ;AACxB,MAAK,aAAa,QAAQ;AAC1B,MAAK,WAAW,QAAQ;AACxB,KAAI,QAAQ,cAAc,KAAA,EACxB,MAAK,YAAY,QAAQ;AAE3B,KAAI,QAAQ,UAAU,KAAA,EACpB,MAAK,QAAQ,QAAQ;AAEvB,KAAI,QAAQ,SAAS,KAAA,EACnB,MAAK,OAAO,QAAQ"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// oxlint-disable-next-line no-unused-vars -- triggers VFile DataMap augmentation\nimport type { ProfileContext } from \"@glion/annotate-profile-context\";\nimport type { Root, Segment } from \"@glion/ast\";\nimport type { FieldRepetitions } from \"@glion/profiles\";\nimport { SKIP, visit } from \"@glion/util-visit\";\nimport type { Optionality } from \"@glion/utils\";\nimport type { Plugin } from \"unified\";\nimport type { VFile } from \"vfile\";\n\ndeclare module \"@glion/ast\" {\n interface FieldData {\n /** Position of this field in its segment (SEQ). */\n sequence?: number | undefined;\n /** Field identifier (e.g., \"MSH-9\", \"PID-3\"). */\n id?: string | undefined;\n /** Human-readable field name from the profile (e.g., \"Patient Name\"). */\n name?: string | undefined;\n /** HL7v2 optionality of this field (OPT). */\n optionality?: Optionality | undefined;\n /** Most occurrences this field may have (RP/#), or `\"unbounded\"`. */\n repetitions?: FieldRepetitions | undefined;\n /** Datatype ID for this field (e.g., \"XPN\", \"ST\", \"CWE\"). */\n datatype?: string | undefined;\n /** Maximum allowed length for this field. */\n maxLength?: number | undefined;\n /** Table reference for coded fields (e.g., \"HL70001\"). */\n table?: string | undefined;\n /** HL7 specification item number. */\n item?: string | undefined;\n }\n}\n\n/**\n * Unified plugin that annotates Field nodes with profile metadata.\n *\n * Reads field definitions from `file.data.fields` (populated by\n * `@glion/annotate-profile-context`) and copies each field's profile, as\n * the profile gives it, onto `field.data`.\n *\n * Unknown segments (Z-segments) and unsupported versions are silently skipped.\n */\nexport const hl7v2AnnotateProfileFields: Plugin<[], Root, Root> =\n () => (tree: Root, file: VFile) => {\n const ctx = file.data.profile;\n if (!ctx) {\n return tree;\n }\n\n // Annotate each field with its profile\n visit(tree, \"field\", (node, ancestors, info) => {\n const segment = ancestors.at(-1) as Segment | undefined;\n if (segment?.type !== \"segment\") {\n return SKIP;\n }\n\n const profile = ctx.fields\n .get(segment.name)\n ?.bySequence.get(info.sequence);\n if (!profile) {\n return SKIP;\n }\n\n Object.assign((node.data ??= {}), profile);\n return SKIP;\n });\n\n return tree;\n };\n\nexport default hl7v2AnnotateProfileFields;\n"],"mappings":";;;;;;;;;;;AAyCA,MAAa,oCACJ,MAAY,SAAgB;CACjC,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,CAAC,IACH,QAAO;AAIT,OAAM,MAAM,UAAU,MAAM,WAAW,SAAS;EAC9C,MAAM,UAAU,UAAU,GAAG,GAAG;AAChC,MAAI,SAAS,SAAS,UACpB,QAAO;EAGT,MAAM,UAAU,IAAI,OACjB,IAAI,QAAQ,KAAK,EAChB,WAAW,IAAI,KAAK,SAAS;AACjC,MAAI,CAAC,QACH,QAAO;AAGT,SAAO,OAAQ,KAAK,SAAS,EAAE,EAAG,QAAQ;AAC1C,SAAO;GACP;AAEF,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glion/annotate-profile-fields",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "description": "Unified plugin to annotate HL7v2 field nodes with profile metadata",
5
5
  "keywords": [
6
6
  "annotate",
@@ -39,8 +39,9 @@
39
39
  "dependencies": {
40
40
  "unified": "11.0.5",
41
41
  "vfile": "6.0.3",
42
- "@glion/ast": "0.23.0",
43
- "@glion/util-visit": "0.23.0"
42
+ "@glion/ast": "0.24.0",
43
+ "@glion/util-visit": "0.24.0",
44
+ "@glion/utils": "0.24.0"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@types/node": "25.6.0",
@@ -49,10 +50,11 @@
49
50
  "tsdown": "0.21.10",
50
51
  "typescript": "6.0.3",
51
52
  "vitest": "4.1.11",
52
- "@glion/annotate-profile-context": "0.23.0",
53
- "@glion/builder": "0.23.0",
53
+ "@glion/builder": "0.24.0",
54
+ "@glion/annotate-profile-context": "0.24.0",
54
55
  "@glion/check-readme": "0.18.0",
55
- "@glion/profiles": "0.23.0",
56
+ "@glion/profiles": "0.24.0",
57
+ "@glion/parser": "0.24.0",
56
58
  "@glion/testing": "0.18.0",
57
59
  "@glion/tsconfig": "0.18.0"
58
60
  },