@glion/annotate-profile-datatypes 0.22.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-datatypes
2
2
 
3
- Unified plugin to annotate HL7v2 field repetition, component, and subcomponent nodes with datatype profile metadata.
3
+ Unified plugin that annotates HL7v2 field repetitions, components, and subcomponents with their datatype.
4
4
 
5
5
  ## What it does
6
6
 
7
- HL7v2 fields hold either a primitive value (like `ST` or `DT`) or a composite structure built from a datatype like `XPN` or `CWE` whose components and subcomponents each have their own datatype. This plugin walks each field repetition, component, and subcomponent, resolves the corresponding datatype from the profile context, and writes that metadata onto `node.data` using a stop-at-primitive cascade — annotation stops at the node where the primitive value actually lives. It requires `@glion/annotate-profile-context` and `@glion/annotate-profile-fields` to run first.
7
+ A field holds either a single value of a primitive datatype, such as `ST` or `DT`, or a composite datatype, such as `XPN` or `CWE`, whose components (and their subcomponents) each have a datatype of their own. This plugin puts that description on the tree: each field repetition gets its datatype, and each component and subcomponent gets its row in its parent datatype's component table. Annotation goes down only as far as the composite structure does, so the node with `kind: "primitive"` is the one that holds the value. It needs `@glion/annotate-profile-context` and `@glion/annotate-profile-fields` to run before it.
8
8
 
9
9
  ## Install
10
10
 
@@ -18,77 +18,57 @@ npm install @glion/annotate-profile-datatypes
18
18
  import { hl7v2AnnotateProfileContext } from "@glion/annotate-profile-context";
19
19
  import { hl7v2AnnotateProfileDatatypes } from "@glion/annotate-profile-datatypes";
20
20
  import { hl7v2AnnotateProfileFields } from "@glion/annotate-profile-fields";
21
- import { hl7v2Parser } from "@glion/parser";
21
+ import { parseHL7v2 } from "@glion/parser";
22
22
  import { unified } from "unified";
23
23
 
24
- const processor = unified()
25
- .use(hl7v2Parser)
26
- .use(hl7v2AnnotateProfileContext)
27
- .use(hl7v2AnnotateProfileFields)
28
- .use(hl7v2AnnotateProfileDatatypes);
29
-
30
- const file = await processor.process(
31
- "MSH|^~\\&|SENDER||RECEIVER||20241201||ADT^A01|MSG123|P|2.5\rPID|1||12345||Doe^John"
24
+ const tree = parseHL7v2(
25
+ "MSH|^~\\&|SENDER||RECEIVER||20241201||ADT^A01^ADT_A01|MSG123|P|2.5\rPID|1||12345||Doe^John"
32
26
  );
33
27
 
34
- // PID-5 field-repetition.data === { datatypeId: "XPN", kind: "composite", title: "Extended Person Name" }
35
- // PID-5.1 component.data === { id: "XPN.1", name: "Family Name", required: false, datatypeId: "FN", kind: "composite", title: "Family Name" }
36
- // PID-5.1.1 subcomponent.data === { id: "FN.1", name: "Surname", required: true, datatypeId: "ST", kind: "primitive", title: "String Data" }
28
+ await unified()
29
+ .use(hl7v2AnnotateProfileContext)
30
+ .use(hl7v2AnnotateProfileFields)
31
+ .use(hl7v2AnnotateProfileDatatypes)
32
+ .run(tree);
33
+
34
+ // PID-5, repetition: { datatypeId: "XPN", kind: "composite", title: "Extended Person Name" }
35
+ // PID-5.1, component: { id: "XPN.1", sequence: 1, name: "Family Name", optionality: "O",
36
+ // datatypeId: "FN", kind: "composite", title: "Family Name" }
37
+ // PID-5.1.1, subcomponent: { id: "FN.1", sequence: 1, name: "Surname", optionality: "R",
38
+ // datatypeId: "ST", kind: "primitive", title: "String Data" }
37
39
  ```
38
40
 
39
41
  ## API
40
42
 
41
- This package exports the named constant `hl7v2AnnotateProfileDatatypes`. The default export is the plugin itself.
43
+ ### `unified().use(hl7v2AnnotateProfileDatatypes)`
42
44
 
43
- ```ts
44
- import type { Root } from "@glion/ast";
45
- import type { Plugin } from "unified";
45
+ A `unified` plugin. Takes no options. The package exports it as `hl7v2AnnotateProfileDatatypes` and as its default export.
46
46
 
47
- export const hl7v2AnnotateProfileDatatypes: Plugin<[], Root, Root>;
48
- ```
49
-
50
- The plugin reads `file.data.profile` and `field.data.datatype` from upstream annotators. It writes to `FieldRepetitionData`, `ComponentData`, and `SubcomponentData` (all declared on the `@glion/ast` module augmentation).
47
+ Writes onto `node.data` of field repetitions, components, and subcomponents. Data already there is kept, except the properties below, which are overwritten.
51
48
 
52
49
  ## What it annotates
53
50
 
54
- This plugin visits three node types in order — `field-repetition`, `component`, and `subcomponent` — and writes datatype metadata to `node.data`. The cascade follows the composite structure: primitive fields stop at the repetition; composite fields with primitive components stop at the component; fully nested composites continue to the subcomponent.
55
-
56
- `kind: "primitive"` means the value lives on that node. `kind: "composite"` means inspect the children. Nodes on ancestor paths of the primitive carry no annotation from this plugin.
51
+ A field repetition gets its field's datatype:
57
52
 
58
- ### `node.data` (FieldRepetition, Component, Subcomponent)
53
+ | Property | Type | Description |
54
+ | ------------ | ---------------------------- | ----------------------------------------------------- |
55
+ | `datatypeId` | `string` | The datatype, such as `"XPN"` |
56
+ | `kind` | `"primitive" \| "composite"` | Whether the value is here or in the components |
57
+ | `title` | `string` | The datatype's name, such as `"Extended Person Name"` |
59
58
 
60
- After this plugin runs, the augmented interfaces are:
59
+ A component of a composite repetition, and a subcomponent of a composite component, get their row in the component table, with the same `kind` and `title` for their own datatype:
61
60
 
62
- ```ts
63
- declare module "@glion/ast" {
64
- interface FieldRepetitionData {
65
- datatypeId?: string; // e.g. "ST", "CWE", "XPN"
66
- kind?: "primitive" | "composite";
67
- title?: string; // e.g. "String Data", "Coded with Exceptions"
68
- }
69
-
70
- interface ComponentData {
71
- id?: string; // "<datatypeId>.<sequence>", e.g. "CWE.1", "XPN.2"
72
- name?: string; // e.g. "Family Name"
73
- required?: boolean;
74
- datatypeId?: string; // e.g. "ST", "ID", "FN"
75
- maxLength?: number;
76
- kind?: "primitive" | "composite";
77
- title?: string;
78
- }
79
-
80
- interface SubcomponentData {
81
- id?: string; // "<datatypeId>.<sequence>", e.g. "FN.1", "HD.2"
82
- name?: string;
83
- required?: boolean;
84
- datatypeId?: string;
85
- kind?: "primitive" | "composite";
86
- title?: string;
87
- }
88
- }
89
- ```
61
+ | Property | Type | Description |
62
+ | ------------- | ------------- | ------------------------------------------------------------ |
63
+ | `id` | `string` | The parent datatype and sequence, such as `"XPN.1"` |
64
+ | `sequence` | `number` | SEQ: the position in the parent datatype |
65
+ | `name` | `string` | COMPONENT NAME, such as `"Family Name"` |
66
+ | `optionality` | `Optionality` | OPT, one of `"R"`, `"RE"`, `"O"`, `"C"`, `"X"`, `"B"`, `"W"` |
67
+ | `datatypeId` | `string` | DT, such as `"FN"` |
68
+ | `table` | `string` | TBL#, such as `"HL70200"` |
69
+ | `maxLength` | `number` | LEN |
90
70
 
91
- Annotation is skipped for nodes whose datatype is not known to the profile (for example Z-segments or custom fields). The field-level `datatype` property is read from `field.data.datatype`, which is populated by `@glion/annotate-profile-fields`.
71
+ A property is absent when the standard leaves its column blank. Before v2.5 the standard prints no component tables, so components have no `optionality` or `table`. A withdrawn component, for example v2.7 `XPN.6`, has `optionality: "W"` and no datatype. Nodes whose datatype the profile does not know, such as the fields of Z-segments, are left as they are. Importing the package adds these properties to the `FieldRepetitionData`, `ComponentData`, and `SubcomponentData` types of `@glion/ast`.
92
72
 
93
73
  ## Part of Glion
94
74
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Root } from "@glion/ast";
2
+ import type { Optionality } from "@glion/utils";
2
3
  import type { Plugin } from "unified";
3
4
  declare module "@glion/ast" {
4
5
  interface FieldRepetitionData {
@@ -10,14 +11,18 @@ declare module "@glion/ast" {
10
11
  title?: string | undefined;
11
12
  }
12
13
  interface ComponentData {
14
+ /** Position in the parent datatype (SEQ). */
15
+ sequence?: number | undefined;
13
16
  /** Component identifier (e.g., "CWE.1", "XPN.2"). */
14
17
  id?: string | undefined;
15
18
  /** Component name from the parent datatype definition. */
16
19
  name?: string | undefined;
17
- /** Whether this component is required in the parent datatype. */
18
- required?: boolean | undefined;
20
+ /** OPT of this component in the parent datatype (e.g., "R", "O"). */
21
+ optionality?: Optionality | undefined;
19
22
  /** Datatype ID for this component (e.g., "ST", "ID"). */
20
23
  datatypeId?: string | undefined;
24
+ /** Table of this component's codes (e.g., "HL70200"). */
25
+ table?: string | undefined;
21
26
  /** Maximum allowed length for this component. */
22
27
  maxLength?: number | undefined;
23
28
  /** Datatype kind: "primitive" or "composite". */
@@ -26,14 +31,20 @@ declare module "@glion/ast" {
26
31
  title?: string | undefined;
27
32
  }
28
33
  interface SubcomponentData {
34
+ /** Position in the parent datatype (SEQ). */
35
+ sequence?: number | undefined;
29
36
  /** Subcomponent identifier (e.g., "FN.1", "HD.2"). */
30
37
  id?: string | undefined;
31
38
  /** Subcomponent name from the parent component's datatype definition. */
32
39
  name?: string | undefined;
33
- /** Whether this subcomponent is required. */
34
- required?: boolean | undefined;
40
+ /** OPT of this subcomponent in the component's datatype. */
41
+ optionality?: Optionality | undefined;
35
42
  /** Datatype ID for this subcomponent. */
36
43
  datatypeId?: string | undefined;
44
+ /** Table of this subcomponent's codes. */
45
+ table?: string | undefined;
46
+ /** Maximum allowed length for this subcomponent. */
47
+ maxLength?: number | undefined;
37
48
  /** Datatype kind: "primitive" or "composite". */
38
49
  kind?: string | undefined;
39
50
  /** Datatype title. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAqC,IAAI,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGtC,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,mBAAmB;QAC3B,6DAA6D;QAC7D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,iDAAiD;QACjD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,qEAAqE;QACrE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;IACD,UAAU,aAAa;QACrB,qDAAqD;QACrD,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,0DAA0D;QAC1D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,iEAAiE;QACjE,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/B,yDAAyD;QACzD,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,iDAAiD;QACjD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,iDAAiD;QACjD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,4CAA4C;QAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;IACD,UAAU,gBAAgB;QACxB,sDAAsD;QACtD,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,yEAAyE;QACzE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,6CAA6C;QAC7C,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/B,yCAAyC;QACzC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,iDAAiD;QACjD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,sBAAsB;QACtB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;CACF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAkG9D,CAAC;AAEJ,eAAe,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGtC,OAAO,QAAQ,YAAY,CAAC;IAC1B,UAAU,mBAAmB;QAC3B,6DAA6D;QAC7D,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,iDAAiD;QACjD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,qEAAqE;QACrE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;IACD,UAAU,aAAa;QACrB,6CAA6C;QAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,qDAAqD;QACrD,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,0DAA0D;QAC1D,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,qEAAqE;QACrE,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;QACtC,yDAAyD;QACzD,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,yDAAyD;QACzD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,iDAAiD;QACjD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,iDAAiD;QACjD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,4CAA4C;QAC5C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;IACD,UAAU,gBAAgB;QACxB,6CAA6C;QAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC9B,sDAAsD;QACtD,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,yEAAyE;QACzE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,4DAA4D;QAC5D,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;QACtC,yCAAyC;QACzC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAChC,0CAA0C;QAC1C,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,oDAAoD;QACpD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,iDAAiD;QACjD,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,sBAAsB;QACtB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;KAC5B;CACF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,6BAA6B,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CA2E9D,CAAC;AAEJ,eAAe,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -18,53 +18,46 @@ import { SKIP, visit } from "@glion/util-visit";
18
18
  * Requires the fields annotator to run first (preset guarantees ordering).
19
19
  */
20
20
  const hl7v2AnnotateProfileDatatypes = () => (tree, file) => {
21
- const ctx = file.data.profile;
22
- if (!ctx) return tree;
23
- visit(tree, "field-repetition", (rep, ancestors) => {
24
- const datatypeId = (ancestors.at(-1)?.data)?.datatype;
25
- const dtDef = datatypeId ? ctx.datatypes.get(datatypeId) : void 0;
26
- if (!dtDef) return;
27
- if (!rep.data) rep.data = {};
28
- rep.data.datatypeId = dtDef.id;
29
- rep.data.kind = dtDef.kind;
30
- if (dtDef.title !== void 0) rep.data.title = dtDef.title;
31
- });
32
- visit(tree, "component", (component, ancestors, info) => {
33
- const rep = ancestors.at(-1);
34
- if (rep?.data?.kind !== "composite" || !rep.data.datatypeId) return SKIP;
35
- const dtDef = ctx.datatypes.get(rep.data.datatypeId);
36
- if (!dtDef) return SKIP;
37
- const compProfile = dtDef.componentsBySequence.get(info.sequence);
38
- if (!compProfile) return SKIP;
39
- if (!component.data) component.data = {};
40
- component.data.id = `${dtDef.id}.${info.sequence}`;
41
- component.data.name = compProfile.name;
42
- component.data.required = compProfile.required;
43
- component.data.datatypeId = compProfile.datatypeId;
44
- if (compProfile.maxLength !== void 0) component.data.maxLength = compProfile.maxLength;
45
- const compDtDef = ctx.datatypes.get(compProfile.datatypeId);
46
- if (compDtDef) {
47
- component.data.kind = compDtDef.kind;
48
- if (compDtDef.title !== void 0) component.data.title = compDtDef.title;
49
- }
21
+ const datatypes = file.data.profile?.datatypes;
22
+ if (datatypes === void 0) return tree;
23
+ const findDatatype = (id) => id === void 0 ? void 0 : datatypes.get(id);
24
+ visit(tree, "field", (field) => {
25
+ const datatype = findDatatype(field.data?.datatype);
26
+ if (datatype === void 0) return SKIP;
27
+ visit(field, "field-repetition", (repetition) => {
28
+ Object.assign(repetition.data ??= {}, {
29
+ datatypeId: datatype.id,
30
+ kind: datatype.kind,
31
+ title: datatype.title
32
+ });
33
+ });
34
+ if (datatype.kind !== "composite") return SKIP;
35
+ visit(field, "component", (component, _, componentInfo) => {
36
+ const profile = datatype.componentsBySequence.get(componentInfo.sequence);
37
+ if (profile === void 0) return SKIP;
38
+ const componentDatatype = findDatatype(profile.datatypeId);
39
+ Object.assign(component.data ??= {}, {
40
+ id: `${datatype.id}.${componentInfo.sequence}`,
41
+ ...profile,
42
+ kind: componentDatatype?.kind,
43
+ title: componentDatatype?.title
44
+ });
45
+ if (componentDatatype?.kind !== "composite") return SKIP;
46
+ visit(component, "subcomponent", (subcomponent, __, subcomponentInfo) => {
47
+ const subcomponentProfile = componentDatatype.componentsBySequence.get(subcomponentInfo.sequence);
48
+ if (subcomponentProfile === void 0) return;
49
+ const subcomponentDatatype = findDatatype(subcomponentProfile.datatypeId);
50
+ Object.assign(subcomponent.data ??= {}, {
51
+ id: `${componentDatatype.id}.${subcomponentInfo.sequence}`,
52
+ ...subcomponentProfile,
53
+ kind: subcomponentDatatype?.kind,
54
+ title: subcomponentDatatype?.title
55
+ });
56
+ });
57
+ return SKIP;
58
+ });
50
59
  return SKIP;
51
60
  });
52
- visit(tree, "subcomponent", (subcomponent, ancestors, info) => {
53
- const component = ancestors.at(-1);
54
- if (component?.data?.kind !== "composite" || !component.data.datatypeId) return;
55
- const subProfile = ctx.datatypes.get(component.data.datatypeId)?.componentsBySequence.get(info.sequence);
56
- if (!subProfile) return;
57
- if (!subcomponent.data) subcomponent.data = {};
58
- subcomponent.data.id = `${component.data.datatypeId}.${info.sequence}`;
59
- subcomponent.data.name = subProfile.name;
60
- subcomponent.data.required = subProfile.required;
61
- subcomponent.data.datatypeId = subProfile.datatypeId;
62
- const subDtDef = ctx.datatypes.get(subProfile.datatypeId);
63
- if (subDtDef) {
64
- subcomponent.data.kind = subDtDef.kind;
65
- if (subDtDef.title !== void 0) subcomponent.data.title = subDtDef.title;
66
- }
67
- });
68
61
  return tree;
69
62
  };
70
63
  //#endregion
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 { Component, Field, FieldRepetition, Root } from \"@glion/ast\";\nimport { SKIP, visit } from \"@glion/util-visit\";\nimport type { Plugin } from \"unified\";\nimport type { VFile } from \"vfile\";\n\ndeclare module \"@glion/ast\" {\n interface FieldRepetitionData {\n /** Datatype ID for this field (e.g., \"ST\", \"CWE\", \"MSG\"). */\n datatypeId?: string | undefined;\n /** Datatype kind: \"primitive\" or \"composite\". */\n kind?: string | undefined;\n /** Datatype title (e.g., \"String Data\", \"Coded with Exceptions\"). */\n title?: string | undefined;\n }\n interface ComponentData {\n /** Component identifier (e.g., \"CWE.1\", \"XPN.2\"). */\n id?: string | undefined;\n /** Component name from the parent datatype definition. */\n name?: string | undefined;\n /** Whether this component is required in the parent datatype. */\n required?: boolean | undefined;\n /** Datatype ID for this component (e.g., \"ST\", \"ID\"). */\n datatypeId?: string | undefined;\n /** Maximum allowed length for this component. */\n maxLength?: number | undefined;\n /** Datatype kind: \"primitive\" or \"composite\". */\n kind?: string | undefined;\n /** Datatype title (e.g., \"String Data\"). */\n title?: string | undefined;\n }\n interface SubcomponentData {\n /** Subcomponent identifier (e.g., \"FN.1\", \"HD.2\"). */\n id?: string | undefined;\n /** Subcomponent name from the parent component's datatype definition. */\n name?: string | undefined;\n /** Whether this subcomponent is required. */\n required?: boolean | undefined;\n /** Datatype ID for this subcomponent. */\n datatypeId?: string | undefined;\n /** Datatype kind: \"primitive\" or \"composite\". */\n kind?: string | undefined;\n /** Datatype title. */\n title?: string | undefined;\n }\n}\n\n/**\n * Unified plugin that annotates FieldRepetition, Component, and Subcomponent\n * nodes with datatype profile metadata using a stop-at-primitive cascade.\n *\n * Annotation flows down the AST and stops at the node where the primitive\n * value lives:\n *\n * - Primitive field → stops at FieldRepetition\n * - Composite field with primitive components → stops at Component\n * - Composite field with composite components → stops at Subcomponent\n *\n * `kind: \"primitive\"` = the value is here.\n * `kind: \"composite\"` = look at children.\n * No annotation = the value lives on an ancestor.\n *\n * Requires the fields annotator to run first (preset guarantees ordering).\n */\nexport const hl7v2AnnotateProfileDatatypes: Plugin<[], Root, Root> =\n () => (tree: Root, file: VFile) => {\n const ctx = file.data.profile;\n if (!ctx) {\n return tree;\n }\n\n // Pass 1: field-repetitions — entry point for the cascade\n visit(tree, \"field-repetition\", (rep, ancestors) => {\n const field = ancestors.at(-1) as Field | undefined;\n const datatypeId = (field?.data as Record<string, unknown> | undefined)\n ?.datatype as string | undefined;\n\n const dtDef = datatypeId ? ctx.datatypes.get(datatypeId) : undefined;\n if (!dtDef) {\n return;\n }\n\n if (!rep.data) {\n rep.data = {};\n }\n rep.data.datatypeId = dtDef.id;\n rep.data.kind = dtDef.kind;\n if (dtDef.title !== undefined) {\n rep.data.title = dtDef.title;\n }\n });\n\n // Pass 2: components — only when parent repetition is composite\n visit(tree, \"component\", (component, ancestors, info) => {\n const rep = ancestors.at(-1) as FieldRepetition | undefined;\n if (rep?.data?.kind !== \"composite\" || !rep.data.datatypeId) {\n return SKIP;\n }\n\n const dtDef = ctx.datatypes.get(rep.data.datatypeId);\n if (!dtDef) {\n return SKIP;\n }\n\n const compProfile = dtDef.componentsBySequence.get(info.sequence);\n if (!compProfile) {\n return SKIP;\n }\n\n if (!component.data) {\n component.data = {};\n }\n component.data.id = `${dtDef.id}.${info.sequence}`;\n component.data.name = compProfile.name;\n component.data.required = compProfile.required;\n component.data.datatypeId = compProfile.datatypeId;\n if (compProfile.maxLength !== undefined) {\n component.data.maxLength = compProfile.maxLength;\n }\n\n const compDtDef = ctx.datatypes.get(compProfile.datatypeId);\n if (compDtDef) {\n component.data.kind = compDtDef.kind;\n if (compDtDef.title !== undefined) {\n component.data.title = compDtDef.title;\n }\n }\n\n return SKIP;\n });\n\n // Pass 3: subcomponents — only when parent component is composite\n visit(tree, \"subcomponent\", (subcomponent, ancestors, info) => {\n const component = ancestors.at(-1) as Component | undefined;\n if (component?.data?.kind !== \"composite\" || !component.data.datatypeId) {\n return;\n }\n\n const compDtDef = ctx.datatypes.get(component.data.datatypeId);\n const subProfile = compDtDef?.componentsBySequence.get(info.sequence);\n if (!subProfile) {\n return;\n }\n\n if (!subcomponent.data) {\n subcomponent.data = {};\n }\n subcomponent.data.id = `${component.data.datatypeId}.${info.sequence}`;\n subcomponent.data.name = subProfile.name;\n subcomponent.data.required = subProfile.required;\n subcomponent.data.datatypeId = subProfile.datatypeId;\n\n const subDtDef = ctx.datatypes.get(subProfile.datatypeId);\n if (subDtDef) {\n subcomponent.data.kind = subDtDef.kind;\n if (subDtDef.title !== undefined) {\n subcomponent.data.title = subDtDef.title;\n }\n }\n });\n\n return tree;\n };\n\nexport default hl7v2AnnotateProfileDatatypes;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAiEA,MAAa,uCACJ,MAAY,SAAgB;CACjC,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,CAAC,IACH,QAAO;AAIT,OAAM,MAAM,qBAAqB,KAAK,cAAc;EAElD,MAAM,cADQ,UAAU,GAAG,GACF,EAAE,OACvB;EAEJ,MAAM,QAAQ,aAAa,IAAI,UAAU,IAAI,WAAW,GAAG,KAAA;AAC3D,MAAI,CAAC,MACH;AAGF,MAAI,CAAC,IAAI,KACP,KAAI,OAAO,EAAE;AAEf,MAAI,KAAK,aAAa,MAAM;AAC5B,MAAI,KAAK,OAAO,MAAM;AACtB,MAAI,MAAM,UAAU,KAAA,EAClB,KAAI,KAAK,QAAQ,MAAM;GAEzB;AAGF,OAAM,MAAM,cAAc,WAAW,WAAW,SAAS;EACvD,MAAM,MAAM,UAAU,GAAG,GAAG;AAC5B,MAAI,KAAK,MAAM,SAAS,eAAe,CAAC,IAAI,KAAK,WAC/C,QAAO;EAGT,MAAM,QAAQ,IAAI,UAAU,IAAI,IAAI,KAAK,WAAW;AACpD,MAAI,CAAC,MACH,QAAO;EAGT,MAAM,cAAc,MAAM,qBAAqB,IAAI,KAAK,SAAS;AACjE,MAAI,CAAC,YACH,QAAO;AAGT,MAAI,CAAC,UAAU,KACb,WAAU,OAAO,EAAE;AAErB,YAAU,KAAK,KAAK,GAAG,MAAM,GAAG,GAAG,KAAK;AACxC,YAAU,KAAK,OAAO,YAAY;AAClC,YAAU,KAAK,WAAW,YAAY;AACtC,YAAU,KAAK,aAAa,YAAY;AACxC,MAAI,YAAY,cAAc,KAAA,EAC5B,WAAU,KAAK,YAAY,YAAY;EAGzC,MAAM,YAAY,IAAI,UAAU,IAAI,YAAY,WAAW;AAC3D,MAAI,WAAW;AACb,aAAU,KAAK,OAAO,UAAU;AAChC,OAAI,UAAU,UAAU,KAAA,EACtB,WAAU,KAAK,QAAQ,UAAU;;AAIrC,SAAO;GACP;AAGF,OAAM,MAAM,iBAAiB,cAAc,WAAW,SAAS;EAC7D,MAAM,YAAY,UAAU,GAAG,GAAG;AAClC,MAAI,WAAW,MAAM,SAAS,eAAe,CAAC,UAAU,KAAK,WAC3D;EAIF,MAAM,aADY,IAAI,UAAU,IAAI,UAAU,KAAK,WACvB,EAAE,qBAAqB,IAAI,KAAK,SAAS;AACrE,MAAI,CAAC,WACH;AAGF,MAAI,CAAC,aAAa,KAChB,cAAa,OAAO,EAAE;AAExB,eAAa,KAAK,KAAK,GAAG,UAAU,KAAK,WAAW,GAAG,KAAK;AAC5D,eAAa,KAAK,OAAO,WAAW;AACpC,eAAa,KAAK,WAAW,WAAW;AACxC,eAAa,KAAK,aAAa,WAAW;EAE1C,MAAM,WAAW,IAAI,UAAU,IAAI,WAAW,WAAW;AACzD,MAAI,UAAU;AACZ,gBAAa,KAAK,OAAO,SAAS;AAClC,OAAI,SAAS,UAAU,KAAA,EACrB,cAAa,KAAK,QAAQ,SAAS;;GAGvC;AAEF,QAAO"}
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\";\n// oxlint-disable-next-line no-unused-vars -- triggers FieldData augmentation\nimport type { hl7v2AnnotateProfileFields } from \"@glion/annotate-profile-fields\";\nimport type { Root } from \"@glion/ast\";\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 FieldRepetitionData {\n /** Datatype ID for this field (e.g., \"ST\", \"CWE\", \"MSG\"). */\n datatypeId?: string | undefined;\n /** Datatype kind: \"primitive\" or \"composite\". */\n kind?: string | undefined;\n /** Datatype title (e.g., \"String Data\", \"Coded with Exceptions\"). */\n title?: string | undefined;\n }\n interface ComponentData {\n /** Position in the parent datatype (SEQ). */\n sequence?: number | undefined;\n /** Component identifier (e.g., \"CWE.1\", \"XPN.2\"). */\n id?: string | undefined;\n /** Component name from the parent datatype definition. */\n name?: string | undefined;\n /** OPT of this component in the parent datatype (e.g., \"R\", \"O\"). */\n optionality?: Optionality | undefined;\n /** Datatype ID for this component (e.g., \"ST\", \"ID\"). */\n datatypeId?: string | undefined;\n /** Table of this component's codes (e.g., \"HL70200\"). */\n table?: string | undefined;\n /** Maximum allowed length for this component. */\n maxLength?: number | undefined;\n /** Datatype kind: \"primitive\" or \"composite\". */\n kind?: string | undefined;\n /** Datatype title (e.g., \"String Data\"). */\n title?: string | undefined;\n }\n interface SubcomponentData {\n /** Position in the parent datatype (SEQ). */\n sequence?: number | undefined;\n /** Subcomponent identifier (e.g., \"FN.1\", \"HD.2\"). */\n id?: string | undefined;\n /** Subcomponent name from the parent component's datatype definition. */\n name?: string | undefined;\n /** OPT of this subcomponent in the component's datatype. */\n optionality?: Optionality | undefined;\n /** Datatype ID for this subcomponent. */\n datatypeId?: string | undefined;\n /** Table of this subcomponent's codes. */\n table?: string | undefined;\n /** Maximum allowed length for this subcomponent. */\n maxLength?: number | undefined;\n /** Datatype kind: \"primitive\" or \"composite\". */\n kind?: string | undefined;\n /** Datatype title. */\n title?: string | undefined;\n }\n}\n\n/**\n * Unified plugin that annotates FieldRepetition, Component, and Subcomponent\n * nodes with datatype profile metadata using a stop-at-primitive cascade.\n *\n * Annotation flows down the AST and stops at the node where the primitive\n * value lives:\n *\n * - Primitive field → stops at FieldRepetition\n * - Composite field with primitive components → stops at Component\n * - Composite field with composite components → stops at Subcomponent\n *\n * `kind: \"primitive\"` = the value is here.\n * `kind: \"composite\"` = look at children.\n * No annotation = the value lives on an ancestor.\n *\n * Requires the fields annotator to run first (preset guarantees ordering).\n */\nexport const hl7v2AnnotateProfileDatatypes: Plugin<[], Root, Root> =\n () => (tree: Root, file: VFile) => {\n const datatypes = file.data.profile?.datatypes;\n if (datatypes === undefined) {\n return tree;\n }\n\n const findDatatype = (id: string | undefined) =>\n id === undefined ? undefined : datatypes.get(id);\n\n visit(tree, \"field\", (field) => {\n const datatype = findDatatype(field.data?.datatype);\n if (datatype === undefined) {\n return SKIP;\n }\n\n visit(field, \"field-repetition\", (repetition) => {\n Object.assign((repetition.data ??= {}), {\n datatypeId: datatype.id,\n kind: datatype.kind,\n title: datatype.title,\n });\n });\n if (datatype.kind !== \"composite\") {\n return SKIP;\n }\n\n visit(field, \"component\", (component, _, componentInfo) => {\n const profile = datatype.componentsBySequence.get(\n componentInfo.sequence\n );\n if (profile === undefined) {\n return SKIP;\n }\n\n const componentDatatype = findDatatype(profile.datatypeId);\n Object.assign((component.data ??= {}), {\n id: `${datatype.id}.${componentInfo.sequence}`,\n ...profile,\n kind: componentDatatype?.kind,\n title: componentDatatype?.title,\n });\n if (componentDatatype?.kind !== \"composite\") {\n return SKIP;\n }\n\n visit(\n component,\n \"subcomponent\",\n (subcomponent, __, subcomponentInfo) => {\n const subcomponentProfile =\n componentDatatype.componentsBySequence.get(\n subcomponentInfo.sequence\n );\n if (subcomponentProfile === undefined) {\n return;\n }\n\n const subcomponentDatatype = findDatatype(\n subcomponentProfile.datatypeId\n );\n Object.assign((subcomponent.data ??= {}), {\n id: `${componentDatatype.id}.${subcomponentInfo.sequence}`,\n ...subcomponentProfile,\n kind: subcomponentDatatype?.kind,\n title: subcomponentDatatype?.title,\n });\n }\n );\n return SKIP;\n });\n return SKIP;\n });\n\n return tree;\n };\n\nexport default hl7v2AnnotateProfileDatatypes;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA8EA,MAAa,uCACJ,MAAY,SAAgB;CACjC,MAAM,YAAY,KAAK,KAAK,SAAS;AACrC,KAAI,cAAc,KAAA,EAChB,QAAO;CAGT,MAAM,gBAAgB,OACpB,OAAO,KAAA,IAAY,KAAA,IAAY,UAAU,IAAI,GAAG;AAElD,OAAM,MAAM,UAAU,UAAU;EAC9B,MAAM,WAAW,aAAa,MAAM,MAAM,SAAS;AACnD,MAAI,aAAa,KAAA,EACf,QAAO;AAGT,QAAM,OAAO,qBAAqB,eAAe;AAC/C,UAAO,OAAQ,WAAW,SAAS,EAAE,EAAG;IACtC,YAAY,SAAS;IACrB,MAAM,SAAS;IACf,OAAO,SAAS;IACjB,CAAC;IACF;AACF,MAAI,SAAS,SAAS,YACpB,QAAO;AAGT,QAAM,OAAO,cAAc,WAAW,GAAG,kBAAkB;GACzD,MAAM,UAAU,SAAS,qBAAqB,IAC5C,cAAc,SACf;AACD,OAAI,YAAY,KAAA,EACd,QAAO;GAGT,MAAM,oBAAoB,aAAa,QAAQ,WAAW;AAC1D,UAAO,OAAQ,UAAU,SAAS,EAAE,EAAG;IACrC,IAAI,GAAG,SAAS,GAAG,GAAG,cAAc;IACpC,GAAG;IACH,MAAM,mBAAmB;IACzB,OAAO,mBAAmB;IAC3B,CAAC;AACF,OAAI,mBAAmB,SAAS,YAC9B,QAAO;AAGT,SACE,WACA,iBACC,cAAc,IAAI,qBAAqB;IACtC,MAAM,sBACJ,kBAAkB,qBAAqB,IACrC,iBAAiB,SAClB;AACH,QAAI,wBAAwB,KAAA,EAC1B;IAGF,MAAM,uBAAuB,aAC3B,oBAAoB,WACrB;AACD,WAAO,OAAQ,aAAa,SAAS,EAAE,EAAG;KACxC,IAAI,GAAG,kBAAkB,GAAG,GAAG,iBAAiB;KAChD,GAAG;KACH,MAAM,sBAAsB;KAC5B,OAAO,sBAAsB;KAC9B,CAAC;KAEL;AACD,UAAO;IACP;AACF,SAAO;GACP;AAEF,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glion/annotate-profile-datatypes",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Unified plugin to annotate HL7v2 field repetition, component, and subcomponent nodes with datatype profile metadata",
5
5
  "keywords": [
6
6
  "annotate",
@@ -40,8 +40,10 @@
40
40
  "dependencies": {
41
41
  "unified": "11.0.5",
42
42
  "vfile": "6.0.3",
43
- "@glion/util-visit": "0.22.0",
44
- "@glion/ast": "0.22.0"
43
+ "@glion/annotate-profile-fields": "0.24.0",
44
+ "@glion/ast": "0.24.0",
45
+ "@glion/util-visit": "0.24.0",
46
+ "@glion/utils": "0.24.0"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@types/node": "25.6.0",
@@ -50,9 +52,8 @@
50
52
  "tsdown": "0.21.10",
51
53
  "typescript": "6.0.3",
52
54
  "vitest": "4.1.11",
53
- "@glion/annotate-profile-context": "0.22.0",
54
- "@glion/annotate-profile-fields": "0.22.0",
55
- "@glion/builder": "0.22.0",
55
+ "@glion/annotate-profile-context": "0.24.0",
56
+ "@glion/builder": "0.24.0",
56
57
  "@glion/check-readme": "0.18.0",
57
58
  "@glion/testing": "0.18.0",
58
59
  "@glion/tsconfig": "0.18.0"