@glion/transform-profile-groups 0.21.0 → 0.23.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
@@ -81,6 +81,7 @@ const definition: EventSchema = {
81
81
  { type: "segment", name: "PID", optional: false, repeating: false },
82
82
  {
83
83
  type: "group",
84
+ id: "VISIT",
84
85
  name: "VISIT",
85
86
  optional: false,
86
87
  repeating: false,
@@ -142,11 +143,11 @@ Async transformer. Mutates the tree in place and returns it.
142
143
 
143
144
  ###### Throws
144
145
 
145
- When the event schema is invalid: it has no elements, a segment or group has no name, a group has no elements, a choice has no alternatives, or a choice alternative can match no segment. When a bundled event schema fails to load.
146
+ When the event schema is invalid: it has no elements, a segment has no name, a group has no ID, no name, or no elements, a choice has no alternatives, or a choice alternative can match no segment. When a bundled event schema fails to load.
146
147
 
147
148
  ## Behavior
148
149
 
149
- - Each `Group` has `type: "group"`, the group's `name` from the standard, such as `ORDER_OBSERVATION`, and a `position` from the start of its first segment to the end of its last.
150
+ - Each `Group` has `type: "group"`, the group's `id`, such as `ORDER_OBSERVATION` or `PATIENT_VISIT`, its `name` in title case, such as `Order Observation` or `Patient Visit`, and a `position` from the start of its first segment to the end of its last.
150
151
  - Each occurrence of a repeating group is its own `Group` node: two results give two `OBSERVATION` nodes.
151
152
  - A choice, the standard's `< OBR | RXO >`, adds no node: the chosen segment sits in the enclosing group.
152
153
  - A group occurrence that holds no segment is left out.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,IAAI,EAAW,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,uEAAuE;AACvE,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,KAAK,CAAC;CACb;AAED,uDAAuD;AACvD,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;OAOG;IACH,UAAU,CAAC,EACP,WAAW,GACX,CAAC,CACC,OAAO,EAAE,6BAA6B,KACnC,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC;IACrE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAgDD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAC9C;IAAC,6BAA6B,CAAC;CAAC,EAChC,IAAI,EACJ,IAAI,CA6CL,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,IAAI,EAAW,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,uEAAuE;AACvE,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,KAAK,CAAC;CACb;AAED,uDAAuD;AACvD,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;OAOG;IACH,UAAU,CAAC,EACP,WAAW,GACX,CAAC,CACC,OAAO,EAAE,6BAA6B,KACnC,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC;IACrE;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAqDD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAC9C;IAAC,6BAA6B,CAAC;CAAC,EAChC,IAAI,EACJ,IAAI,CA6CL,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -27,6 +27,7 @@ const nodeOf = (match, segments) => {
27
27
  const children = match.children.map((child) => nodeOf(child, segments));
28
28
  const group = {
29
29
  children,
30
+ id: match.id,
30
31
  name: match.name,
31
32
  type: "group"
32
33
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Group, Root, Segment } from \"@glion/ast\";\nimport type { EventSchema, SegmentMatch } from \"@glion/profiles\";\nimport { profiles, runner } from \"@glion/profiles\";\nimport { value } from \"@glion/util-query\";\nimport { EXIT, SKIP, visit } from \"@glion/util-visit\";\nimport type { Plugin } from \"unified\";\nimport type { VFile } from \"vfile\";\n\n/** The message a `definition` function chooses an event schema for. */\nexport interface TransformProfileGroupsContext {\n tree: Root;\n file: VFile;\n}\n\n/** Options for {@link hl7v2TransformProfileGroups}. */\nexport interface TransformProfileGroupsOptions {\n /**\n * The event schema to group by, or a function that returns the one to use\n * for a message. Default: the schema MSH-9 names.\n *\n * When the function returns `undefined`, the plugin uses the schema MSH-9\n * names. Return the same object for every message that uses a schema: a\n * new object is compiled on its first run.\n */\n definition?:\n | EventSchema\n | ((\n context: TransformProfileGroupsContext\n ) => EventSchema | undefined | Promise<EventSchema | undefined>);\n /**\n * Whether a Z-segment (a segment ID that starts with `Z`) that the event\n * schema does not name fits at any position. HL7v2 allows local Z-segments\n * in any message and segment group (v2.5.1 §2.11). When `false`, such a\n * segment leaves the tree flat. Default: `true`.\n */\n allowZSegments?: boolean;\n}\n\n/**\n * The bundled event schema MSH-12 and MSH-9 name: MSH-9.3, or MSH-9.1 and\n * MSH-9.2, which the events store resolves through the version's event map.\n * `undefined` when MSH-12 is empty or the version bundles no such schema.\n */\nconst bundledEventSchema = async (\n tree: Root\n): Promise<EventSchema | undefined> => {\n const version = value(tree, \"MSH-12.1\")?.value;\n if (!version) {\n return;\n }\n const id =\n value(tree, \"MSH-9.3\")?.value ||\n `${value(tree, \"MSH-9.1\")?.value ?? \"\"}_${value(tree, \"MSH-9.2\")?.value ?? \"\"}`;\n return await profiles.events.load(version, id);\n};\n\n/**\n * The tree node of one match: the segment it indexes, or a `Group` of its\n * children's nodes, positioned from the start of its first segment to the\n * end of its last.\n */\nconst nodeOf = (\n match: SegmentMatch,\n segments: readonly Segment[]\n): Group | Segment => {\n if (typeof match === \"number\") {\n const segment = segments[match];\n if (!segment) {\n throw new Error(\n `@glion/transform-profile-groups: a match indexes segment ${match} of ${segments.length}; this is a bug, please report it`\n );\n }\n return segment;\n }\n const children = match.children.map((child) => nodeOf(child, segments));\n const group: Group = { children, name: match.name, type: \"group\" };\n const start = children[0]?.position?.start;\n const end = children.at(-1)?.position?.end;\n if (start && end) {\n group.position = { end, start };\n }\n return group;\n};\n\n/**\n * Unified plugin that nests a message's segments in the segment groups its\n * event schema defines, such as `PATIENT_RESULT` and `ORDER_OBSERVATION`.\n *\n * Groups by `options.definition`, or else by the schema MSH-12 and MSH-9\n * name. Replaces the root's children with `Group` nodes holding the same\n * segment nodes, in the same order. A group's position spans its first to its\n * last segment. A Z-segment the schema does not name goes right after the\n * segment before it, in that segment's group, unless `options.allowZSegments`\n * is `false`.\n *\n * Runs only on a tree that holds segments and no group: a tree that already\n * holds a group, from this plugin or another, is left as it is, and\n * `options.definition` is not called for it. Also leaves the tree\n * unchanged when there is no event schema or when the segments do not fit\n * it. Idempotent.\n *\n * @throws {Error} When the event schema is invalid (see `runner`), or a\n * bundled event schema fails to load.\n */\nexport const hl7v2TransformProfileGroups: Plugin<\n [TransformProfileGroupsOptions?],\n Root,\n Root\n> = (options) => async (tree, file) => {\n // A group means the tree is grouped already, by this plugin or another.\n let grouped = false;\n const segments: Segment[] = [];\n visit(tree, (node) => {\n if (node.type === \"group\") {\n grouped = true;\n return EXIT;\n }\n if (node.type === \"segment\") {\n segments.push(node);\n return SKIP;\n }\n });\n if (grouped || segments.length === 0) {\n return tree;\n }\n\n const definition = options?.definition;\n const chosen =\n typeof definition === \"function\"\n ? await definition({ file, tree })\n : definition;\n const schema = chosen ?? (await bundledEventSchema(tree));\n if (!schema) {\n return tree;\n }\n\n const result = runner(\n schema,\n segments.map((segment) => segment.name),\n { allowZSegments: options?.allowZSegments }\n );\n switch (result.type) {\n case \"matched\": {\n tree.children = result.groups.map((match) => nodeOf(match, segments));\n break;\n }\n case \"mismatched\":\n case \"incomplete\": {\n break;\n }\n }\n return tree;\n};\n\nexport default hl7v2TransformProfileGroups;\n"],"mappings":";;;;;;;;;AA2CA,MAAM,qBAAqB,OACzB,SACqC;CACrC,MAAM,UAAU,MAAM,MAAM,WAAW,EAAE;AACzC,KAAI,CAAC,QACH;CAEF,MAAM,KACJ,MAAM,MAAM,UAAU,EAAE,SACxB,GAAG,MAAM,MAAM,UAAU,EAAE,SAAS,GAAG,GAAG,MAAM,MAAM,UAAU,EAAE,SAAS;AAC7E,QAAO,MAAM,SAAS,OAAO,KAAK,SAAS,GAAG;;;;;;;AAQhD,MAAM,UACJ,OACA,aACoB;AACpB,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,UAAU,SAAS;AACzB,MAAI,CAAC,QACH,OAAM,IAAI,MACR,4DAA4D,MAAM,MAAM,SAAS,OAAO,mCACzF;AAEH,SAAO;;CAET,MAAM,WAAW,MAAM,SAAS,KAAK,UAAU,OAAO,OAAO,SAAS,CAAC;CACvE,MAAM,QAAe;EAAE;EAAU,MAAM,MAAM;EAAM,MAAM;EAAS;CAClE,MAAM,QAAQ,SAAS,IAAI,UAAU;CACrC,MAAM,MAAM,SAAS,GAAG,GAAG,EAAE,UAAU;AACvC,KAAI,SAAS,IACX,OAAM,WAAW;EAAE;EAAK;EAAO;AAEjC,QAAO;;;;;;;;;;;;;;;;;;;;;;AAuBT,MAAa,+BAIR,YAAY,OAAO,MAAM,SAAS;CAErC,IAAI,UAAU;CACd,MAAM,WAAsB,EAAE;AAC9B,OAAM,OAAO,SAAS;AACpB,MAAI,KAAK,SAAS,SAAS;AACzB,aAAU;AACV,UAAO;;AAET,MAAI,KAAK,SAAS,WAAW;AAC3B,YAAS,KAAK,KAAK;AACnB,UAAO;;GAET;AACF,KAAI,WAAW,SAAS,WAAW,EACjC,QAAO;CAGT,MAAM,aAAa,SAAS;CAK5B,MAAM,UAHJ,OAAO,eAAe,aAClB,MAAM,WAAW;EAAE;EAAM;EAAM,CAAC,GAChC,eACoB,MAAM,mBAAmB,KAAK;AACxD,KAAI,CAAC,OACH,QAAO;CAGT,MAAM,SAAS,OACb,QACA,SAAS,KAAK,YAAY,QAAQ,KAAK,EACvC,EAAE,gBAAgB,SAAS,gBAAgB,CAC5C;AACD,SAAQ,OAAO,MAAf;EACE,KAAK;AACH,QAAK,WAAW,OAAO,OAAO,KAAK,UAAU,OAAO,OAAO,SAAS,CAAC;AACrE;EAEF,KAAK;EACL,KAAK,aACH;;AAGJ,QAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Group, Root, Segment } from \"@glion/ast\";\nimport type { EventSchema, SegmentMatch } from \"@glion/profiles\";\nimport { profiles, runner } from \"@glion/profiles\";\nimport { value } from \"@glion/util-query\";\nimport { EXIT, SKIP, visit } from \"@glion/util-visit\";\nimport type { Plugin } from \"unified\";\nimport type { VFile } from \"vfile\";\n\n/** The message a `definition` function chooses an event schema for. */\nexport interface TransformProfileGroupsContext {\n tree: Root;\n file: VFile;\n}\n\n/** Options for {@link hl7v2TransformProfileGroups}. */\nexport interface TransformProfileGroupsOptions {\n /**\n * The event schema to group by, or a function that returns the one to use\n * for a message. Default: the schema MSH-9 names.\n *\n * When the function returns `undefined`, the plugin uses the schema MSH-9\n * names. Return the same object for every message that uses a schema: a\n * new object is compiled on its first run.\n */\n definition?:\n | EventSchema\n | ((\n context: TransformProfileGroupsContext\n ) => EventSchema | undefined | Promise<EventSchema | undefined>);\n /**\n * Whether a Z-segment (a segment ID that starts with `Z`) that the event\n * schema does not name fits at any position. HL7v2 allows local Z-segments\n * in any message and segment group (v2.5.1 §2.11). When `false`, such a\n * segment leaves the tree flat. Default: `true`.\n */\n allowZSegments?: boolean;\n}\n\n/**\n * The bundled event schema MSH-12 and MSH-9 name: MSH-9.3, or MSH-9.1 and\n * MSH-9.2, which the events store resolves through the version's event map.\n * `undefined` when MSH-12 is empty or the version bundles no such schema.\n */\nconst bundledEventSchema = async (\n tree: Root\n): Promise<EventSchema | undefined> => {\n const version = value(tree, \"MSH-12.1\")?.value;\n if (!version) {\n return;\n }\n const id =\n value(tree, \"MSH-9.3\")?.value ||\n `${value(tree, \"MSH-9.1\")?.value ?? \"\"}_${value(tree, \"MSH-9.2\")?.value ?? \"\"}`;\n return await profiles.events.load(version, id);\n};\n\n/**\n * The tree node of one match: the segment it indexes, or a `Group` of its\n * children's nodes, positioned from the start of its first segment to the\n * end of its last.\n */\nconst nodeOf = (\n match: SegmentMatch,\n segments: readonly Segment[]\n): Group | Segment => {\n if (typeof match === \"number\") {\n const segment = segments[match];\n if (!segment) {\n throw new Error(\n `@glion/transform-profile-groups: a match indexes segment ${match} of ${segments.length}; this is a bug, please report it`\n );\n }\n return segment;\n }\n const children = match.children.map((child) => nodeOf(child, segments));\n const group: Group = {\n children,\n id: match.id,\n name: match.name,\n type: \"group\",\n };\n const start = children[0]?.position?.start;\n const end = children.at(-1)?.position?.end;\n if (start && end) {\n group.position = { end, start };\n }\n return group;\n};\n\n/**\n * Unified plugin that nests a message's segments in the segment groups its\n * event schema defines, such as `PATIENT_RESULT` and `ORDER_OBSERVATION`.\n *\n * Groups by `options.definition`, or else by the schema MSH-12 and MSH-9\n * name. Replaces the root's children with `Group` nodes holding the same\n * segment nodes, in the same order. A group's position spans its first to its\n * last segment. A Z-segment the schema does not name goes right after the\n * segment before it, in that segment's group, unless `options.allowZSegments`\n * is `false`.\n *\n * Runs only on a tree that holds segments and no group: a tree that already\n * holds a group, from this plugin or another, is left as it is, and\n * `options.definition` is not called for it. Also leaves the tree\n * unchanged when there is no event schema or when the segments do not fit\n * it. Idempotent.\n *\n * @throws {Error} When the event schema is invalid (see `runner`), or a\n * bundled event schema fails to load.\n */\nexport const hl7v2TransformProfileGroups: Plugin<\n [TransformProfileGroupsOptions?],\n Root,\n Root\n> = (options) => async (tree, file) => {\n // A group means the tree is grouped already, by this plugin or another.\n let grouped = false;\n const segments: Segment[] = [];\n visit(tree, (node) => {\n if (node.type === \"group\") {\n grouped = true;\n return EXIT;\n }\n if (node.type === \"segment\") {\n segments.push(node);\n return SKIP;\n }\n });\n if (grouped || segments.length === 0) {\n return tree;\n }\n\n const definition = options?.definition;\n const chosen =\n typeof definition === \"function\"\n ? await definition({ file, tree })\n : definition;\n const schema = chosen ?? (await bundledEventSchema(tree));\n if (!schema) {\n return tree;\n }\n\n const result = runner(\n schema,\n segments.map((segment) => segment.name),\n { allowZSegments: options?.allowZSegments }\n );\n switch (result.type) {\n case \"matched\": {\n tree.children = result.groups.map((match) => nodeOf(match, segments));\n break;\n }\n case \"mismatched\":\n case \"incomplete\": {\n break;\n }\n }\n return tree;\n};\n\nexport default hl7v2TransformProfileGroups;\n"],"mappings":";;;;;;;;;AA2CA,MAAM,qBAAqB,OACzB,SACqC;CACrC,MAAM,UAAU,MAAM,MAAM,WAAW,EAAE;AACzC,KAAI,CAAC,QACH;CAEF,MAAM,KACJ,MAAM,MAAM,UAAU,EAAE,SACxB,GAAG,MAAM,MAAM,UAAU,EAAE,SAAS,GAAG,GAAG,MAAM,MAAM,UAAU,EAAE,SAAS;AAC7E,QAAO,MAAM,SAAS,OAAO,KAAK,SAAS,GAAG;;;;;;;AAQhD,MAAM,UACJ,OACA,aACoB;AACpB,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,UAAU,SAAS;AACzB,MAAI,CAAC,QACH,OAAM,IAAI,MACR,4DAA4D,MAAM,MAAM,SAAS,OAAO,mCACzF;AAEH,SAAO;;CAET,MAAM,WAAW,MAAM,SAAS,KAAK,UAAU,OAAO,OAAO,SAAS,CAAC;CACvE,MAAM,QAAe;EACnB;EACA,IAAI,MAAM;EACV,MAAM,MAAM;EACZ,MAAM;EACP;CACD,MAAM,QAAQ,SAAS,IAAI,UAAU;CACrC,MAAM,MAAM,SAAS,GAAG,GAAG,EAAE,UAAU;AACvC,KAAI,SAAS,IACX,OAAM,WAAW;EAAE;EAAK;EAAO;AAEjC,QAAO;;;;;;;;;;;;;;;;;;;;;;AAuBT,MAAa,+BAIR,YAAY,OAAO,MAAM,SAAS;CAErC,IAAI,UAAU;CACd,MAAM,WAAsB,EAAE;AAC9B,OAAM,OAAO,SAAS;AACpB,MAAI,KAAK,SAAS,SAAS;AACzB,aAAU;AACV,UAAO;;AAET,MAAI,KAAK,SAAS,WAAW;AAC3B,YAAS,KAAK,KAAK;AACnB,UAAO;;GAET;AACF,KAAI,WAAW,SAAS,WAAW,EACjC,QAAO;CAGT,MAAM,aAAa,SAAS;CAK5B,MAAM,UAHJ,OAAO,eAAe,aAClB,MAAM,WAAW;EAAE;EAAM;EAAM,CAAC,GAChC,eACoB,MAAM,mBAAmB,KAAK;AACxD,KAAI,CAAC,OACH,QAAO;CAGT,MAAM,SAAS,OACb,QACA,SAAS,KAAK,YAAY,QAAQ,KAAK,EACvC,EAAE,gBAAgB,SAAS,gBAAgB,CAC5C;AACD,SAAQ,OAAO,MAAf;EACE,KAAK;AACH,QAAK,WAAW,OAAO,OAAO,KAAK,UAAU,OAAO,OAAO,SAAS,CAAC;AACrE;EAEF,KAAK;EACL,KAAK,aACH;;AAGJ,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glion/transform-profile-groups",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Unified plugin to group HL7v2 segments into the segment groups their event schema defines",
5
5
  "keywords": [
6
6
  "group",
@@ -40,10 +40,10 @@
40
40
  "dependencies": {
41
41
  "unified": "11.0.5",
42
42
  "vfile": "6.0.3",
43
- "@glion/ast": "0.21.0",
44
- "@glion/profiles": "0.21.0",
45
- "@glion/util-visit": "0.21.0",
46
- "@glion/util-query": "0.21.0"
43
+ "@glion/ast": "0.23.0",
44
+ "@glion/profiles": "0.23.0",
45
+ "@glion/util-visit": "0.23.0",
46
+ "@glion/util-query": "0.23.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "25.6.0",
@@ -52,13 +52,13 @@
52
52
  "tsdown": "0.21.10",
53
53
  "typescript": "6.0.3",
54
54
  "vitest": "4.1.11",
55
+ "@glion/annotate-profile-context": "0.23.0",
56
+ "@glion/annotate-profile-fields": "0.23.0",
55
57
  "@glion/check-readme": "0.18.0",
56
- "@glion/annotate-profile-context": "0.21.0",
57
- "@glion/annotate-profile-fields": "0.21.0",
58
- "@glion/parser": "0.21.0",
58
+ "@glion/parser": "0.23.0",
59
59
  "@glion/testing": "0.18.0",
60
60
  "@glion/tsconfig": "0.18.0",
61
- "@glion/to-hl7v2": "0.21.0"
61
+ "@glion/to-hl7v2": "0.23.0"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=22"