@glion/util-query 0.25.0 → 0.27.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/LICENSE +1 -1
- package/README.md +90 -65
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/format.d.ts +9 -8
- package/dist/format.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +306 -584
- package/dist/index.js.map +1 -1
- package/dist/matches.d.ts +8 -8
- package/dist/matches.d.ts.map +1 -1
- package/dist/parse.d.ts +3 -5
- package/dist/parse.d.ts.map +1 -1
- package/dist/resolve.d.ts +27 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/select.d.ts +28 -31
- package/dist/select.d.ts.map +1 -1
- package/dist/types.d.ts +23 -29
- package/dist/types.d.ts.map +1 -1
- package/dist/value.d.ts +9 -7
- package/dist/value.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/set.d.ts +0 -35
- package/dist/set.d.ts.map +0 -1
- package/dist/utils.d.ts +0 -33
- package/dist/utils.d.ts.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/parse.ts","../src/utils.ts","../src/select.ts","../src/value.ts","../src/delimiters.ts","../src/format.ts","../src/matches.ts","../src/set.ts"],"sourcesContent":["import type { GroupLocator, PathParts } from \"./types\";\n\n/**\n * Regular expression for canonical HL7 paths.\n *\n * Format:\n * [GROUP[N]-]...[GROUP[N]-]NAME[N][-FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]]\n *\n * **Design Philosophy**: Groups vs Segments are distinguished by POSITION and\n * USAGE, not name length:\n *\n * - **Navigation (Groups)**: Names appearing as prefixes (before the final name)\n * are treated as group navigation: `ORDER-ORC`, `ORDER-TIMING-TQ1`\n * - **Target (Segments)**: The final name in the path is the query target.\n * Whether it's a segment or group is determined by:\n *\n * 1. Field access (-.digit): Definitively a segment → `ORC-1`, `ORDER-ORC-1`\n * 2. No field access: Query the AST to see what exists → `ORC`, `ORDER`\n *\n * This approach:\n *\n * - Makes no assumptions about name length (works with standard 3-char segments\n * AND edge cases)\n * - Lets the path structure indicate intent (field access = segment internals)\n * - Allows the AST to be the source of truth for what exists\n *\n * Examples:\n *\n * - `MSH-3` → MSH is segment (has field access)\n * - `ORDER-ORC` → ORDER is navigation, ORC is target (could be segment or nested\n * group)\n * - `ORDER-ORC-1` → ORDER is navigation, ORC is segment (has field access)\n */\nconst PATH_REGEX =\n /^((?:[A-Z][A-Z0-9_]*(?:\\[\\d+\\])?-)*)([A-Z][A-Z0-9_]*)(?:\\[(\\d+)\\])?(?:-(\\d+)(?:(?:\\[(\\d+)\\])?(?:\\.(\\d+)(?:\\.(\\d+))?)?)?)?$/;\n\n/**\n * Cache for parsed paths to avoid re-parsing the same path multiple times.\n * No eviction — the realistic set of unique paths in an application is\n * small (50-100 entries) and the memory cost is negligible.\n */\nconst parseCache = new Map<string, PathParts>();\n\nexport function clearParseCache(): void {\n parseCache.clear();\n}\n\nexport function getParseCacheSize(): number {\n return parseCache.size;\n}\n\n/**\n * Pre-computed PathParts for frequently accessed MSH fields.\n *\n * Multiple plugins and lint rules read MSH-9 (message type) and MSH-12\n * (version) on every message. Skipping regex parsing and cache lookups\n * for these paths eliminates ~100ns per call.\n */\nconst HOT_PATHS: Record<string, PathParts> = {\n \"MSH-10\": { field: 10, segment: { name: \"MSH\" } },\n \"MSH-12\": { field: 12, segment: { name: \"MSH\" } },\n \"MSH-3\": { field: 3, segment: { name: \"MSH\" } },\n \"MSH-4\": { field: 4, segment: { name: \"MSH\" } },\n \"MSH-5\": { field: 5, segment: { name: \"MSH\" } },\n \"MSH-6\": { field: 6, segment: { name: \"MSH\" } },\n \"MSH-9\": { field: 9, segment: { name: \"MSH\" } },\n \"MSH-9.1\": { component: 1, field: 9, segment: { name: \"MSH\" } },\n \"MSH-9.2\": { component: 2, field: 9, segment: { name: \"MSH\" } },\n \"MSH-9.3\": { component: 3, field: 9, segment: { name: \"MSH\" } },\n};\n\n/**\n * Parse an HL7 path string into structured parts.\n * Results are memoized for performance. Common MSH paths use\n * pre-computed constants to skip parsing entirely.\n *\n * @example\n * ```typescript\n * parse(\"PID-5[1].2.1\");\n * // {\n * // segment: { name: 'PID' },\n * // field: 5,\n * // repetition: 1,\n * // component: 2,\n * // subcomponent: 1\n * // }\n * ```;\n *\n * @param path - HL7 path string to parse\n * @returns Structured path components\n */\nexport function parse(path: string): PathParts {\n // Hot path: pre-computed results for common MSH field reads\n const hot = HOT_PATHS[path];\n if (hot) {\n return hot;\n }\n\n // Check cache\n const cached = parseCache.get(path);\n if (cached) {\n return cached;\n }\n\n const result = parseImpl(path);\n parseCache.set(path, result);\n return result;\n}\n\n/**\n * Internal implementation of path parsing.\n */\n// oxlint-disable-next-line complexity/complexity -- path grammar: one branch per optional path part\nfunction parseImpl(path: string): PathParts {\n if (!path || typeof path !== \"string\") {\n throw new Error(`Path must be a non-empty string, got: ${path}`);\n }\n\n const trimmed = path.trim();\n if (trimmed !== path) {\n throw new Error(`Path cannot contain leading/trailing spaces: \"${path}\"`);\n }\n\n const match = PATH_REGEX.exec(path);\n if (!match) {\n throw new Error(\n `Invalid HL7 path format: \"${path}\". Expected [GROUP[N]-]...NAME[N][-FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]].`\n );\n }\n\n const [\n ,\n groupsRaw,\n segmentName,\n segmentRep,\n fieldStr,\n repetitionStr,\n componentStr,\n subcomponentStr,\n ] = match;\n\n if (!segmentName) {\n throw new Error(`Path must include a segment identifier: \"${path}\"`);\n }\n\n const parts: PathParts = {\n segment: { name: segmentName },\n };\n\n if (segmentRep) {\n const repetition = Number.parseInt(segmentRep, 10);\n if (repetition < 1) {\n throw new Error(\n `Segment repetition must be ≥1, got: ${segmentRep} in \"${path}\"`\n );\n }\n parts.segment.repetition = repetition;\n }\n\n if (groupsRaw) {\n const groups: GroupLocator[] = [];\n for (const groupMatch of groupsRaw.matchAll(\n /([A-Z][A-Z0-9_]*)(?:\\[(\\d+)\\])?-/g\n )) {\n const name = groupMatch[1];\n if (!name) {\n continue;\n }\n\n const repStr = groupMatch[2];\n const group: GroupLocator = { name };\n if (repStr) {\n const repetition = Number.parseInt(repStr, 10);\n if (repetition < 1) {\n throw new Error(\n `Group repetition must be ≥1, got: ${repStr} in \"${path}\"`\n );\n }\n group.repetition = repetition;\n }\n groups.push(group);\n }\n if (groups.length > 0) {\n parts.groups = groups;\n }\n }\n\n if (fieldStr) {\n const field = Number.parseInt(fieldStr, 10);\n if (field < 1) {\n throw new Error(`Field number must be ≥1, got: ${fieldStr}`);\n }\n parts.field = field;\n }\n\n if (repetitionStr) {\n const repetition = Number.parseInt(repetitionStr, 10);\n if (repetition < 1) {\n throw new Error(`Repetition number must be ≥1, got: ${repetitionStr}`);\n }\n parts.repetition = repetition;\n }\n\n if (componentStr) {\n const component = Number.parseInt(componentStr, 10);\n if (component < 1) {\n throw new Error(`Component number must be ≥1, got: ${componentStr}`);\n }\n parts.component = component;\n }\n\n if (subcomponentStr) {\n const subcomponent = Number.parseInt(subcomponentStr, 10);\n if (subcomponent < 1) {\n throw new Error(\n `Subcomponent number must be ≥1, got: ${subcomponentStr}`\n );\n }\n parts.subcomponent = subcomponent;\n }\n\n return parts;\n}\n","// -------------\n// Internal Helpers\n// -------------\n\nimport type {\n Component,\n Field,\n FieldRepetition,\n Group,\n Nodes,\n Root,\n Segment,\n Subcomponent,\n} from \"@glion/ast\";\n\nimport type { GroupLocator, PathParts } from \"./types\";\n\nexport function locateSegmentOrGroup(\n root: Root,\n parts: PathParts\n): [Segment | Group, Nodes[]] | null {\n const ancestors: Nodes[] = [root];\n const scope = followGroups(root, parts.groups ?? [], ancestors);\n if (!scope) {\n return null;\n }\n\n const targetName = parts.segment.name;\n const targetRepetition = parts.segment.repetition;\n\n // Try to find a segment first\n const matchedSegments = collectSegments(scope, targetName, ancestors);\n if (matchedSegments.length > 0) {\n const index = (targetRepetition ?? 1) - 1;\n const match = matchedSegments[index];\n if (match) {\n return [match.segment, match.ancestors];\n }\n }\n\n // If no segment found, try to find a group\n const matchedGroups = collectGroups(scope, targetName, ancestors);\n if (matchedGroups.length > 0) {\n const index = (targetRepetition ?? 1) - 1;\n const match = matchedGroups[index];\n if (match) {\n return [match.group, match.ancestors];\n }\n }\n\n return null;\n}\n\nexport function locateSegment(\n root: Root,\n parts: PathParts\n): [Segment, Nodes[]] | null {\n const ancestors: Nodes[] = [root];\n const scope = followGroups(root, parts.groups ?? [], ancestors);\n if (!scope) {\n return null;\n }\n\n const matchedSegments = collectSegments(scope, parts.segment.name, ancestors);\n if (matchedSegments.length === 0) {\n return null;\n }\n\n const index = (parts.segment.repetition ?? 1) - 1;\n const match = matchedSegments[index];\n if (!match) {\n return null;\n }\n\n return [match.segment, match.ancestors];\n}\n\nexport function followGroups(\n root: Root,\n groups: GroupLocator[],\n ancestorChain: Nodes[]\n): Array<Segment | Group> | null {\n let children = filterSegmentsAndGroups(root.children);\n\n for (const locator of groups) {\n const matchedGroups = children.filter(\n // Stryker disable next-line ConditionalExpression: only a group has an id; the type check narrows for TypeScript\n (node): node is Group => node.type === \"group\" && node.id === locator.name\n );\n\n if (matchedGroups.length === 0) {\n return null;\n }\n\n const index = (locator.repetition ?? 1) - 1;\n const selected = matchedGroups[index];\n if (!selected) {\n return null;\n }\n\n ancestorChain.push(selected);\n children = filterSegmentsAndGroups(selected.children);\n }\n\n return children;\n}\n\n/**\n * Collect all possible scopes when groups can match multiple instances.\n * Used by selectAll to find nodes across all matching groups.\n */\nexport function collectAllScopes(\n root: Root,\n groups: GroupLocator[]\n): { scope: (Segment | Group)[]; ancestors: Nodes[] }[] {\n if (groups.length === 0) {\n return [\n { ancestors: [root], scope: filterSegmentsAndGroups(root.children) },\n ];\n }\n\n const results: { scope: (Segment | Group)[]; ancestors: Nodes[] }[] = [];\n const firstLocator = groups[0];\n const remainingGroups = groups.slice(1);\n\n if (!firstLocator) {\n return [\n { ancestors: [root], scope: filterSegmentsAndGroups(root.children) },\n ];\n }\n\n const children = filterSegmentsAndGroups(root.children);\n const matchingGroups = children.filter(\n (node): node is Group =>\n // Stryker disable next-line ConditionalExpression: only a group has an id; the type check narrows for TypeScript\n node.type === \"group\" && node.id === firstLocator.name\n );\n\n // If a specific repetition is requested, only follow that one\n if (firstLocator.repetition !== undefined) {\n const index = firstLocator.repetition - 1;\n const selected = matchingGroups[index];\n if (!selected) {\n return [];\n }\n const ancestors = [root, selected];\n return collectAllScopesRecursive(\n selected.children,\n remainingGroups,\n ancestors\n );\n }\n\n // Otherwise, follow ALL matching groups\n for (const group of matchingGroups) {\n const ancestors = [root, group];\n const subScopes = collectAllScopesRecursive(\n group.children,\n remainingGroups,\n ancestors\n );\n results.push(...subScopes);\n }\n\n return results;\n}\n\nexport function collectAllScopesRecursive(\n children: Nodes[],\n groups: GroupLocator[],\n ancestors: Nodes[]\n): { scope: (Segment | Group)[]; ancestors: Nodes[] }[] {\n if (groups.length === 0) {\n return [{ ancestors, scope: filterSegmentsAndGroups(children) }];\n }\n\n const results: { scope: (Segment | Group)[]; ancestors: Nodes[] }[] = [];\n const firstLocator = groups[0];\n const remainingGroups = groups.slice(1);\n\n if (!firstLocator) {\n return [{ ancestors, scope: filterSegmentsAndGroups(children) }];\n }\n\n const filteredChildren = filterSegmentsAndGroups(children);\n const matchingGroups = filteredChildren.filter(\n (node): node is Group =>\n // Stryker disable next-line ConditionalExpression: only a group has an id; the type check narrows for TypeScript\n node.type === \"group\" && node.id === firstLocator.name\n );\n\n // If a specific repetition is requested, only follow that one\n if (firstLocator.repetition !== undefined) {\n const index = firstLocator.repetition - 1;\n const selected = matchingGroups[index];\n if (!selected) {\n return [];\n }\n const newAncestors = [...ancestors, selected];\n return collectAllScopesRecursive(\n selected.children,\n remainingGroups,\n newAncestors\n );\n }\n\n // Otherwise, follow ALL matching groups\n for (const group of matchingGroups) {\n const newAncestors = [...ancestors, group];\n const subScopes = collectAllScopesRecursive(\n group.children,\n remainingGroups,\n newAncestors\n );\n results.push(...subScopes);\n }\n\n return results;\n}\n\nexport function collectSegments(\n nodes: (Segment | Group)[],\n targetSegmentName: string,\n ancestors: Nodes[]\n): { segment: Segment; ancestors: Nodes[] }[] {\n const result: { segment: Segment; ancestors: Nodes[] }[] = [];\n\n for (const node of nodes) {\n if (node.type === \"segment\") {\n const name = getSegmentName(node);\n if (name === targetSegmentName) {\n result.push({ ancestors: [...ancestors], segment: node });\n }\n }\n\n if (node.type === \"group\") {\n const nextAncestors = [...ancestors, node];\n result.push(\n ...collectSegments(\n filterSegmentsAndGroups(node.children),\n targetSegmentName,\n nextAncestors\n )\n );\n }\n }\n\n return result;\n}\n\nexport function collectGroups(\n nodes: (Segment | Group)[],\n targetGroupId: string,\n ancestors: Nodes[]\n): { group: Group; ancestors: Nodes[] }[] {\n const result: { group: Group; ancestors: Nodes[] }[] = [];\n\n for (const node of nodes) {\n // Stryker disable next-line ConditionalExpression: only a group has an id; the type check narrows for TypeScript\n if (node.type === \"group\" && node.id === targetGroupId) {\n result.push({ ancestors: [...ancestors], group: node });\n }\n\n if (node.type === \"group\") {\n const nextAncestors = [...ancestors, node];\n result.push(\n ...collectGroups(\n filterSegmentsAndGroups(node.children),\n targetGroupId,\n nextAncestors\n )\n );\n }\n }\n\n return result;\n}\n\nexport function locateField(\n segment: Segment,\n fieldNum: number,\n parentAncestors: Nodes[]\n): [Field, Nodes[]] | null {\n const fields = getFields(segment);\n const field = fields[fieldNum - 1];\n if (!field) {\n return null;\n }\n\n return [field, [...parentAncestors, segment]];\n}\n\nexport function locateRepetition(\n field: Field,\n repetition: number,\n parentAncestors: Nodes[]\n): [FieldRepetition, Nodes[]] | null {\n if (repetition < 1) {\n return null;\n }\n\n const node = field.children?.[repetition - 1];\n if (!node) {\n return null;\n }\n\n return [node as FieldRepetition, [...parentAncestors, field]];\n}\n\nexport function locateComponent(\n repetition: FieldRepetition,\n componentNum: number,\n parentAncestors: Nodes[]\n): [Component, Nodes[]] | null {\n if (componentNum < 1) {\n return null;\n }\n\n const component = repetition.children?.[componentNum - 1];\n if (!component) {\n return null;\n }\n\n return [component as Component, [...parentAncestors, repetition]];\n}\n\nexport function locateSubcomponent(\n component: Component,\n subcomponentNum: number,\n parentAncestors: Nodes[]\n): [Subcomponent, Nodes[]] | null {\n if (subcomponentNum < 1) {\n return null;\n }\n\n const node = component.children?.[subcomponentNum - 1];\n if (!node) {\n return null;\n }\n\n return [node as Subcomponent, [...parentAncestors, component]];\n}\n\nexport function getSegmentName(segment: Segment): string {\n return segment.name;\n}\n\nexport function getFields(segment: Segment): Field[] {\n return segment.children;\n}\n\nexport function filterSegmentsAndGroups(nodes: Nodes[]): (Segment | Group)[] {\n return nodes.filter(\n (node): node is Segment | Group =>\n node.type === \"segment\" || node.type === \"group\"\n );\n}\n","import type { Group, Nodes, Root, Segment } from \"@glion/ast\";\n\nimport { parse } from \"./parse\";\nimport type { InferNodeType } from \"./types\";\nimport {\n collectAllScopes,\n collectGroups,\n collectSegments,\n locateComponent,\n locateField,\n locateRepetition,\n locateSegment,\n locateSegmentOrGroup,\n locateSubcomponent,\n} from \"./utils\";\n\n/**\n * Select the first node at the given path and return it with its ancestor\n * chain.\n *\n * This function finds the first matching node for the given path. For paths\n * that can match multiple nodes (e.g., \"PID\" when multiple PID segments exist),\n * use {@link selectAll} to retrieve all matches.\n *\n * @example\n * ```typescript\n * const result = select(root, \"PID-5\");\n * if (result) {\n * result.node; // Type: Field\n * result.ancestors; // [Root, Segment]\n * }\n * ```;\n *\n * @param root - The root node to search from\n * @param path - The HL7 path string\n * @returns Object containing the node and its ancestors, or null if not found\n */\nexport function select<Path extends string>(\n root: Root,\n path: Path\n): { node: InferNodeType<Path>; ancestors: Nodes[] } | null {\n const parts = parse(path);\n\n // If no field access, we might be selecting a segment OR a group\n if (parts.field === undefined) {\n const result = locateSegmentOrGroup(root, parts);\n if (!result) {\n return null;\n }\n return {\n ancestors: result[1],\n node: result[0] as unknown as InferNodeType<Path>,\n };\n }\n\n // Field access means we're definitely accessing a segment\n const segmentResult = locateSegment(root, parts);\n if (!segmentResult) {\n return null;\n }\n const [segment, segmentAncestors] = segmentResult;\n\n const fieldResult = locateField(segment, parts.field, segmentAncestors);\n if (!fieldResult) {\n return null;\n }\n const [field, fieldAncestors] = fieldResult;\n\n if (\n parts.component === undefined &&\n parts.subcomponent === undefined &&\n parts.repetition === undefined\n ) {\n return {\n ancestors: fieldAncestors,\n node: field as unknown as InferNodeType<Path>,\n };\n }\n\n const repetitionResult = locateRepetition(\n field,\n parts.repetition ?? 1,\n fieldAncestors\n );\n if (!repetitionResult) {\n return null;\n }\n const [repetitionNode, repetitionAncestors] = repetitionResult;\n\n if (parts.component === undefined) {\n return {\n ancestors: repetitionAncestors,\n node: repetitionNode as unknown as InferNodeType<Path>,\n };\n }\n\n const componentResult = locateComponent(\n repetitionNode,\n parts.component,\n repetitionAncestors\n );\n if (!componentResult) {\n return null;\n }\n const [componentNode, componentAncestors] = componentResult;\n\n if (parts.subcomponent === undefined) {\n return {\n ancestors: componentAncestors,\n node: componentNode as unknown as InferNodeType<Path>,\n };\n }\n\n const subResult = locateSubcomponent(\n componentNode,\n parts.subcomponent,\n componentAncestors\n );\n if (!subResult) {\n return null;\n }\n\n return {\n ancestors: subResult[1],\n node: subResult[0] as unknown as InferNodeType<Path>,\n };\n}\n\n/**\n * Select all nodes that match the given path.\n *\n * This function returns all matching nodes for paths that can match multiple\n * elements. For example, \"PID\" will return all PID segments in the message, and\n * \"OBX-5\" will return all observation values.\n *\n * @example\n * ```typescript\n * // Get all OBX segments\n * const results = selectAll(root, \"OBX\");\n * for (const { node } of results) {\n * console.log(node.type); // 'segment'\n * }\n *\n * // Get all patient name fields\n * const names = selectAll(root, \"PID-5\");\n * ```;\n *\n * @param root - The root node to search from\n * @param path - The HL7 path string (must not include repetition index)\n * @returns Array of objects containing nodes and their ancestor chains\n */\n// oxlint-disable-next-line complexity/complexity -- path walker: one branch per scope level (group, segment, field, component, subcomponent)\nexport function selectAll<Path extends string>(\n root: Root,\n path: Path\n): { node: InferNodeType<Path>; ancestors: Nodes[] }[] {\n const parts = parse(path);\n\n // If path includes a specific repetition, delegate to select()\n if (\n parts.segment.repetition !== undefined ||\n parts.repetition !== undefined\n ) {\n const result = select(root, path);\n return result ? [result] : [];\n }\n\n const results: { node: InferNodeType<Path>; ancestors: Nodes[] }[] = [];\n\n // Get all matching segments from all possible scopes\n const scopes = collectAllScopes(root, parts.groups ?? []);\n const segments: { segment: Segment; ancestors: Nodes[] }[] = [];\n const includeGroups = parts.field === undefined;\n const groups: { group: Group; ancestors: Nodes[] }[] = [];\n\n for (const { scope, ancestors } of scopes) {\n const matchingSegments = collectSegments(\n scope,\n parts.segment.name,\n ancestors\n );\n segments.push(...matchingSegments);\n\n if (includeGroups) {\n const matchingGroups = collectGroups(\n scope,\n parts.segment.name,\n ancestors\n );\n for (const { group, ancestors: groupAncestors } of matchingGroups) {\n groups.push({ ancestors: groupAncestors, group });\n }\n }\n }\n\n // If path is segment-only, return all matching segments and groups\n if (parts.field === undefined) {\n for (const { segment, ancestors } of segments) {\n results.push({\n ancestors,\n node: segment as InferNodeType<Path>,\n });\n }\n\n for (const { group, ancestors } of groups) {\n results.push({\n ancestors,\n node: group as InferNodeType<Path>,\n });\n }\n return results;\n }\n\n // For each segment, try to get the field/component/subcomponent\n for (const { segment, ancestors: segmentAncestors } of segments) {\n const fieldResult = locateField(segment, parts.field, segmentAncestors);\n if (!fieldResult) {\n continue;\n }\n const [field, fieldAncestors] = fieldResult;\n\n if (\n parts.component === undefined &&\n parts.subcomponent === undefined &&\n parts.repetition === undefined\n ) {\n results.push({\n ancestors: fieldAncestors,\n node: field as unknown as InferNodeType<Path>,\n });\n continue;\n }\n\n // For field repetitions, components, and subcomponents,\n // we need to iterate through all repetitions\n if (!field.children) {\n continue;\n }\n\n for (const repetitionNode of field.children) {\n const repetitionAncestors = [...fieldAncestors, field];\n\n if (parts.component === undefined) {\n results.push({\n ancestors: repetitionAncestors,\n node: repetitionNode as unknown as InferNodeType<Path>,\n });\n continue;\n }\n\n const componentResult = locateComponent(\n repetitionNode,\n parts.component,\n repetitionAncestors\n );\n if (!componentResult) {\n continue;\n }\n const [componentNode, componentAncestors] = componentResult;\n\n if (parts.subcomponent === undefined) {\n results.push({\n ancestors: componentAncestors,\n node: componentNode as unknown as InferNodeType<Path>,\n });\n continue;\n }\n\n const subResult = locateSubcomponent(\n componentNode,\n parts.subcomponent,\n componentAncestors\n );\n if (subResult) {\n results.push({\n ancestors: subResult[1],\n node: subResult[0] as unknown as InferNodeType<Path>,\n });\n }\n }\n }\n\n return results;\n}\n","import type { Nodes, Root } from \"@glion/ast\";\n\nimport { select } from \"./select\";\n\n/**\n * Get the string value at the path, drilling down through single-child nodes\n * when needed. Also returns the node and ancestor chain for context.\n *\n * This is a convenience function that automatically traverses through container\n * nodes (Field → FieldRepetition → Component → Subcomponent) to extract the\n * primary string value. When a container has multiple children, the first child\n * is selected (e.g., `value(tree, \"MSH-12\")` returns the first component of a\n * composite VID field).\n *\n * Nodes with no children return `value: null` to differentiate between empty\n * strings (\"\") and missing values.\n *\n * @example\n * ```typescript\n * const result = value(root, \"PID-5.1\");\n * if (result) {\n * console.log(result.value); // string | null\n * console.log(result.node.type); // 'subcomponent'\n * console.log(result.ancestors); // [Root, Segment, Field, ...]\n * }\n * ```;\n *\n * @param root - The root node to search from\n * @param path - The HL7 path string\n * @returns Object containing the value, node, and ancestors, or null if not\n * found\n */\nexport function value(\n root: Root,\n path: string\n): { value: string | null; node: Nodes; ancestors: Nodes[] } | null {\n const selectResult = select(root, path);\n if (!selectResult) {\n return null;\n }\n\n let { node } = selectResult;\n let { ancestors } = selectResult;\n\n // Drill down through single children until we reach a subcomponent\n while (node.type !== \"subcomponent\") {\n // Segments and groups can't be drilled down\n if (\n node.type === \"segment\" ||\n node.type === \"group\" ||\n node.type === \"root\"\n ) {\n return null;\n }\n\n // Type guard: at this point, node must be Field | FieldRepetition | Component\n if (!(\"children\" in node && node.children)) {\n return null;\n }\n\n if (node.children.length === 0) {\n return {\n ancestors: [...ancestors],\n node,\n value: null,\n };\n }\n\n const next = node.children[0];\n if (!next) {\n return null;\n }\n\n ancestors = [...ancestors, node];\n node = next;\n }\n\n // At this point, node.type === \"subcomponent\" is guaranteed\n return {\n ancestors,\n node,\n value: node.value,\n };\n}\n","import type { Delimiters, Root } from \"@glion/ast\";\nimport { DEFAULT_DELIMITERS } from \"@glion/utils\";\n\nimport { value } from \"./value\";\n\n/** Keys of MSH-2 encoding characters in positional order. */\nconst MSH2_KEYS = [\n \"component\",\n \"repetition\",\n \"escape\",\n \"subcomponent\",\n] as const;\n\n/**\n * Extract delimiters from a Root AST by reading MSH-1 and MSH-2.\n *\n * MSH-1 contains the field separator character (e.g., `|`).\n * MSH-2 contains up to 4 encoding characters: component, repetition,\n * escape, and subcomponent (e.g., `^~\\&`).\n *\n * Falls back to DEFAULT_DELIMITERS for any values that cannot be derived.\n *\n * @param root - The Root AST node to extract delimiters from.\n * @returns The resolved Delimiters object.\n */\nexport function delimiters(root: Root): Delimiters {\n const result = { ...DEFAULT_DELIMITERS };\n\n // MSH-1: field separator (single character)\n const msh1 = value(root, \"MSH-1\")?.value;\n if (msh1 && msh1.length === 1) {\n result.field = msh1;\n }\n\n // MSH-2: encoding characters (up to 4 positional chars)\n const msh2 = value(root, \"MSH-2\")?.value;\n if (msh2) {\n const chars = [...msh2];\n for (const [i, key] of MSH2_KEYS.entries()) {\n const ch = chars[i];\n if (ch) {\n result[key] = ch;\n }\n }\n }\n\n return result;\n}\n","import type { Group, Nodes, Segment } from \"@glion/ast\";\n\n/** What a path names a node by: a segment's name, or a group's ID. */\nconst keyOf = (node: Segment | Group): string | undefined =>\n node.type === \"group\" ? node.id : node.name;\n\n/**\n * Format a node and its ancestor chain into a canonical HL7v2 path string.\n *\n * This is the inverse of `select`: given a node and the ancestors\n * returned by `visit` or `select`, it produces the path string that\n * would select that node.\n *\n * @example\n * ```typescript\n * visit(tree, \"field\", (node, ancestors) => {\n * format(node, ancestors); // \"PID-5\"\n * });\n *\n * const result = select(root, \"PID-5.2\");\n * format(result.node, result.ancestors); // \"PID-5.2\"\n * ```;\n *\n * @param node - The target AST node\n * @param ancestors - Ancestor chain from root to parent (as provided by\n * visit/select)\n * @returns Canonical path string (e.g., \"PID-5.2\", \"ORDER-ORC-1\"), or `null` if the\n * ancestor chain contains no segment or group\n */\n// oxlint-disable-next-line complexity\nexport function format(node: Nodes, ancestors: Nodes[]): string | null {\n let result: string | null = null;\n const chain = [...ancestors, node];\n\n for (const [i, current] of chain.entries()) {\n const parent = chain[i - 1];\n\n switch (current.type) {\n case \"root\": {\n break;\n }\n\n case \"group\":\n case \"segment\": {\n const pos = positionOf(current, parent);\n result = (result ?? \"\") + keyOf(current);\n if (pos > 1) {\n result += `[${pos}]`;\n }\n if (current.type === \"group\" && current !== node) {\n result += \"-\";\n }\n break;\n }\n\n case \"field\": {\n result += `-${positionOf(current, parent)}`;\n break;\n }\n\n case \"field-repetition\": {\n const pos = positionOf(current, parent);\n if (pos > 1 || current === node) {\n result += `[${pos}]`;\n }\n break;\n }\n\n case \"component\": {\n result += `.${positionOf(current, parent)}`;\n break;\n }\n\n case \"subcomponent\": {\n result += `.${positionOf(current, parent)}`;\n break;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Compute the 1-based position of a node among its parent's children.\n *\n * For segments and groups, counts only siblings with the same name (a\n * group's ID): the 2nd PID in [MSH, PID, OBX, PID] is PID[2], not PID[4].\n *\n * For positional nodes (fields, repetitions, components, subcomponents),\n * uses the array index directly.\n */\nfunction positionOf(node: Nodes, parent: Nodes | undefined): number {\n if (!parent || !(\"children\" in parent)) {\n return 1;\n }\n\n const children = parent.children as Nodes[];\n\n // Named nodes: count siblings with the same key before this node\n // Stryker disable next-line ConditionalExpression: a positional node's siblings share its type and have no name, so counting them equals its index\n if (node.type === \"group\" || node.type === \"segment\") {\n const key = keyOf(node);\n let count = 0;\n for (const child of children as (Segment | Group)[]) {\n if (child === node) {\n return count + 1;\n }\n if (child.type === node.type && keyOf(child) === key) {\n count++;\n }\n }\n throw new Error(\"format(): node not found in parent's children array\");\n }\n\n // Positional nodes: use array index\n const idx = children.indexOf(node);\n if (idx === -1) {\n throw new Error(\"format(): node not found in parent's children array\");\n }\n return idx + 1;\n}\n","import type { Root } from \"@glion/ast\";\n\nimport { select } from \"./select\";\n\n/**\n * Check if a node exists at the given path.\n *\n * This is a convenience function equivalent to `select(root, path) !== null`,\n * but more semantically clear for existence checks.\n *\n * @example\n * ```typescript\n * if (matches(root, \"PID-5\")) {\n * console.log(\"Patient has a name field\");\n * }\n *\n * if (!matches(root, \"OBX-5\")) {\n * throw new Error(\"Missing observation value\");\n * }\n * ```;\n *\n * @param root - The root node to search from\n * @param path - The HL7 path string\n * @returns True if the path resolves to a node, false otherwise\n */\nexport function matches(root: Root, path: string): boolean {\n return select(root, path) !== null;\n}\n","import type {\n Component,\n Field,\n FieldRepetition,\n Root,\n Subcomponent,\n} from \"@glion/ast\";\n\nimport { parse } from \"./parse\";\nimport { locateSegment } from \"./utils\";\n\n/**\n * Set a string value at the given HL7 path, creating all missing intermediate\n * nodes as needed (fields, repetitions, components, subcomponents).\n *\n * This is the write counterpart to {@link value}. It handles the full HL7v2\n * path syntax including group navigation and segment repetitions.\n *\n * Padding nodes are created with empty children arrays — they represent\n * structurally absent nodes, not empty values. Only the target node gets\n * a fully populated chain down to the subcomponent.\n *\n * @example\n * ```typescript\n * // Update existing value\n * set(tree, \"MSH-12\", \"2.7.1\");\n *\n * // Create missing MSH-9.3 (fills gaps with empty components)\n * set(tree, \"MSH-9.3\", \"ADT_A01\");\n *\n * // Set a specific repetition\n * set(tree, \"PID-3[2]\", \"ID-002\");\n *\n * // Set a subcomponent\n * set(tree, \"PID-5.1.2\", \"suffix\");\n * ```;\n *\n * @param root - The root node to modify\n * @param path - The HL7 path string (must include at least a field)\n * @param val - The string value to set\n * @throws When the path has no field specifier (segment-only paths)\n * @throws When the target segment does not exist in the tree\n */\nexport function set(root: Root, path: string, val: string): void {\n const parts = parse(path);\n\n if (parts.field === undefined) {\n throw new Error(`Cannot set value on segment-only path: \"${path}\"`);\n }\n\n // Use locateSegment for full path support (groups, segment repetitions)\n const segmentResult = locateSegment(root, parts);\n if (!segmentResult) {\n throw new Error(`Segment \"${parts.segment.name}\" not found in tree`);\n }\n const [segment] = segmentResult;\n\n // Ensure fields exist up to the target index\n const fieldIndex = parts.field - 1;\n padArray(segment.children, fieldIndex + 1, stubField);\n\n const field = segment.children[fieldIndex]!;\n\n // Ensure repetitions exist up to the target index\n const repIndex = (parts.repetition ?? 1) - 1;\n padArray(field.children, repIndex + 1, stubRepetition);\n\n const repetition = field.children[repIndex]!;\n\n // Ensure components exist up to the target index\n const compIndex = (parts.component ?? 1) - 1;\n padArray(repetition.children, compIndex + 1, stubComponent);\n\n const component = repetition.children[compIndex]!;\n\n // Ensure subcomponents exist up to the target index\n const subIndex = (parts.subcomponent ?? 1) - 1;\n padArray(component.children, subIndex + 1, stubSubcomponent);\n\n // Set the value\n component.children[subIndex]!.value = val;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Pad an array to a target length using a factory function.\n */\nfunction padArray<T>(arr: T[], targetLength: number, factory: () => T): void {\n while (arr.length < targetLength) {\n arr.push(factory());\n }\n}\n\n/**\n * Stub nodes for padding — created with empty children arrays to represent\n * structurally absent nodes. These are distinguishable from nodes with empty\n * values (which have a subcomponent with value \"\").\n */\n\nfunction stubField(): Field {\n return { children: [], type: \"field\" } as Field;\n}\n\nfunction stubRepetition(): FieldRepetition {\n return { children: [], type: \"field-repetition\" } as FieldRepetition;\n}\n\nfunction stubComponent(): Component {\n return { children: [], type: \"component\" } as Component;\n}\n\nfunction stubSubcomponent(): Subcomponent {\n return { type: \"subcomponent\", value: \"\" } as Subcomponent;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/parse.ts","../src/resolve.ts","../src/select.ts","../src/value.ts","../src/delimiters.ts","../src/format.ts","../src/matches.ts"],"sourcesContent":["/**\n * The typed failure for `@glion/util-query`. Branch on `code`; each code is\n * one cause.\n *\n * @module\n */\n\n/**\n * Machine-readable codes for what a path got wrong:\n *\n * - `EMPTY_PATH` — the path is empty.\n * - `INVALID_PATH` — the path does not follow the path grammar.\n */\nexport const QueryErrorCode = {\n EMPTY_PATH: \"EMPTY_PATH\",\n INVALID_PATH: \"INVALID_PATH\",\n} as const;\n\nexport type QueryErrorCode =\n (typeof QueryErrorCode)[keyof typeof QueryErrorCode];\n\nfunction sentence(code: QueryErrorCode, path: string): string {\n switch (code) {\n case \"EMPTY_PATH\": {\n return \"The path is empty; a path is `[GROUP[N].]...NAME[N][.FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]]`, or `FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]` read from a segment, with positions from 1, such as `PID.5.1` or `5.1`.\";\n }\n case \"INVALID_PATH\": {\n return `Path \\`${path}\\` does not follow the path grammar; a path is \\`[GROUP[N].]...NAME[N][.FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]]\\`, or \\`FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]\\` read from a segment, with positions from 1, such as \\`PID.5.1\\` or \\`5.1\\`.`;\n }\n }\n}\n\n/** A path that `@glion/util-query` cannot read. */\nexport class QueryError extends Error {\n override readonly name = \"QueryError\";\n readonly code: QueryErrorCode;\n readonly path: string;\n\n constructor(code: QueryErrorCode, path: string) {\n super(sentence(code, path));\n this.code = code;\n this.path = path;\n }\n}\n","import { QueryError } from \"./errors\";\nimport type { GroupLocator, PathParts } from \"./types\";\n\n/**\n * Canonical HL7 path grammar, every step separated with `.`:\n *\n * [GROUP[N].]...NAME[N][.FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]]\n * FIELD[REPETITION][.COMPONENT[.SUBCOMPONENT]]\n *\n * Names start with a letter and positions are numbers from 1, so the first\n * numeric step is the field. The final name is the query target. Field\n * access makes it a segment (`ORDER.ORC.1`); without it the AST decides\n * whether it names a segment or a group (`ORDER.ORC`, `ORDER`). A path that\n * starts with the field names no segment (`5.1`).\n */\nconst PATH_REGEX =\n /^(?:([A-Z][A-Z0-9_]*(?:\\[[1-9]\\d*\\])?(?:\\.[A-Z][A-Z0-9_]*(?:\\[[1-9]\\d*\\])?)*)(?:\\.(?=[1-9])|$))?(?:([1-9]\\d*)(?:\\[([1-9]\\d*)\\])?(?:\\.([1-9]\\d*)(?:\\.([1-9]\\d*))?)?)?$/;\n\nconst NAVIGATION_STEP_REGEX = /([A-Z][A-Z0-9_]*)(?:\\[(\\d+)\\])?/g;\n\n/** Parts of the MSH paths that plugins and lint rules read on every message. */\nconst PRECOMPUTED_PATHS = new Map<string, () => PathParts>([\n [\"MSH.3\", () => ({ field: 3, segment: { name: \"MSH\" } })],\n [\"MSH.4\", () => ({ field: 4, segment: { name: \"MSH\" } })],\n [\"MSH.5\", () => ({ field: 5, segment: { name: \"MSH\" } })],\n [\"MSH.6\", () => ({ field: 6, segment: { name: \"MSH\" } })],\n [\"MSH.9\", () => ({ field: 9, segment: { name: \"MSH\" } })],\n [\"MSH.9.1\", () => ({ component: 1, field: 9, segment: { name: \"MSH\" } })],\n [\"MSH.9.2\", () => ({ component: 2, field: 9, segment: { name: \"MSH\" } })],\n [\"MSH.9.3\", () => ({ component: 3, field: 9, segment: { name: \"MSH\" } })],\n [\"MSH.10\", () => ({ field: 10, segment: { name: \"MSH\" } })],\n [\"MSH.12\", () => ({ field: 12, segment: { name: \"MSH\" } })],\n [\"MSH.12.1\", () => ({ component: 1, field: 12, segment: { name: \"MSH\" } })],\n]);\n\n/**\n * Parse an HL7 path string into structured parts.\n *\n * Returns a new object on every call.\n *\n * @example\n * ```typescript\n * parse(\"PID.5[1].2.1\");\n * // {\n * // segment: { name: 'PID' },\n * // field: 5,\n * // repetition: 1,\n * // component: 2,\n * // subcomponent: 1\n * // }\n * ```;\n *\n * @param path - HL7 path string to parse\n * @returns Structured path components\n */\nexport function parse(path: string): PathParts {\n const precomputed = PRECOMPUTED_PATHS.get(path);\n // Stryker disable next-line ConditionalExpression,BlockStatement: the grammar parses a precomputed path to the same parts\n if (precomputed) {\n return precomputed();\n }\n\n if (path === \"\") {\n throw new QueryError(\"EMPTY_PATH\", path);\n }\n\n const match = PATH_REGEX.exec(path);\n if (!match) {\n throw new QueryError(\"INVALID_PATH\", path);\n }\n\n const [, navigation = \"\", field, repetition, component, subcomponent] = match;\n const steps: GroupLocator[] = [];\n for (const [, name, position] of navigation.matchAll(NAVIGATION_STEP_REGEX)) {\n steps.push(\n position === undefined\n ? { name: name as string }\n : { name: name as string, repetition: Number(position) }\n );\n }\n\n const parts: PathParts = {};\n const segment = steps.pop();\n if (segment !== undefined) {\n parts.segment = segment;\n }\n if (steps.length > 0) {\n parts.groups = steps;\n }\n if (field !== undefined) {\n parts.field = Number(field);\n }\n if (repetition !== undefined) {\n parts.repetition = Number(repetition);\n }\n if (component !== undefined) {\n parts.component = Number(component);\n }\n if (subcomponent !== undefined) {\n parts.subcomponent = Number(subcomponent);\n }\n return parts;\n}\n","import type { Group, Nodes, Root, Segment } from \"@glion/ast\";\n\nimport type { GroupLocator, PathParts } from \"./types\";\n\n/** A node a path resolves to, with its ancestors from the start node. */\nexport interface Match {\n node: Nodes;\n ancestors: Nodes[];\n}\n\n/** The name a path gives a node: a segment's name, or a group's ID. */\nexport const keyOf = (node: Nodes): string | undefined => {\n switch (node.type) {\n // Stryker disable StringLiteral,BlockStatement,ConditionalExpression: an unnamed node has no key, whichever arm it reaches\n case \"root\":\n case \"field\":\n case \"field-repetition\":\n case \"component\":\n case \"subcomponent\": {\n return undefined;\n }\n // Stryker restore StringLiteral,BlockStatement,ConditionalExpression\n case \"segment\": {\n return node.name;\n }\n case \"group\": {\n return node.id;\n }\n }\n};\n\n/**\n * The `position`th of `children` (1-based) when given; otherwise every one\n * when `every` is set, or the first.\n */\nfunction pick<T>(\n children: T[],\n position: number | undefined,\n every: boolean\n): T[] {\n if (position !== undefined) {\n const child = children[position - 1];\n return child === undefined ? [] : [child];\n }\n return every ? children : children.slice(0, 1);\n}\n\n/**\n * The children of `parent` a path step names `name`, segments and groups\n * together: the `position`th (1-based) when given; otherwise every one when\n * `every` is set, or the first.\n */\nexport function stepChildren(\n parent: Root | Segment | Group,\n name: string,\n position: number | undefined,\n every: boolean\n): (Segment | Group)[] {\n const result: (Segment | Group)[] = [];\n const target = position ?? 1;\n let count = 0;\n for (const child of parent.children) {\n if (keyOf(child) !== name) {\n continue;\n }\n if (every && position === undefined) {\n result.push(child as Segment | Group);\n continue;\n }\n count += 1;\n if (count === target) {\n return [child as Segment | Group];\n }\n }\n return result;\n}\n\ninterface Scope {\n node: Root | Segment | Group;\n ancestors: Nodes[];\n}\n\n/** The children of each scope that `step` names, with their ancestors. */\nfunction advance(scopes: Scope[], step: GroupLocator, every: boolean): Scope[] {\n const next: Scope[] = [];\n for (const scope of scopes) {\n for (const node of stepChildren(\n scope.node,\n step.name,\n step.repetition,\n every\n )) {\n next.push({ ancestors: [...scope.ancestors, scope.node], node });\n }\n }\n return next;\n}\n\n/**\n * The field, repetitions, components, or subcomponents `parts` addresses in\n * `segment`.\n */\nfunction resolveField(\n segment: Segment,\n ancestors: Nodes[],\n fieldNumber: number,\n parts: PathParts,\n every: boolean\n): Match[] {\n const field = segment.children[fieldNumber - 1];\n if (!field) {\n return [];\n }\n const fieldAncestors = [...ancestors, segment];\n if (parts.repetition === undefined && parts.component === undefined) {\n return [{ ancestors: fieldAncestors, node: field }];\n }\n\n const matches: Match[] = [];\n for (const repetition of pick(\n field.children ?? [],\n parts.repetition,\n every\n )) {\n const repetitionAncestors = [...fieldAncestors, field];\n if (parts.component === undefined) {\n matches.push({ ancestors: repetitionAncestors, node: repetition });\n continue;\n }\n\n const component = repetition.children?.[parts.component - 1];\n if (!component) {\n continue;\n }\n const componentAncestors = [...repetitionAncestors, repetition];\n if (parts.subcomponent === undefined) {\n matches.push({ ancestors: componentAncestors, node: component });\n continue;\n }\n\n const subcomponent = component.children?.[parts.subcomponent - 1];\n if (subcomponent) {\n matches.push({\n ancestors: [...componentAncestors, component],\n node: subcomponent,\n });\n }\n }\n return matches;\n}\n\n/**\n * The nodes `parts` addresses under `start`, in document order, each with its\n * own ancestors array, which begins at `start`.\n *\n * Every step names a direct child of the node before it; the first step\n * names a direct child of `start`. A step's `[n]` counts the children with\n * that name, segments and groups together. A step without `[n]` matches the\n * first such child, or every one when `every` is set; so does a field\n * without a repetition when the path names a component.\n */\nexport function resolve(\n start: Root | Group | Segment,\n parts: PathParts,\n every: boolean\n): Match[] {\n let scopes: Scope[] = [{ ancestors: [], node: start }];\n for (const group of parts.groups ?? []) {\n scopes = advance(scopes, group, every);\n }\n if (parts.segment !== undefined) {\n scopes = advance(scopes, parts.segment, every);\n }\n\n if (parts.field === undefined) {\n return scopes;\n }\n\n const matches: Match[] = [];\n for (const { node, ancestors } of scopes) {\n if (node.type !== \"segment\") {\n continue;\n }\n for (const match of resolveField(\n node,\n ancestors,\n parts.field,\n parts,\n every\n )) {\n matches.push(match);\n }\n }\n return matches;\n}\n","import type { Group, Nodes, Root, Segment } from \"@glion/ast\";\n\nimport { parse } from \"./parse\";\nimport { resolve } from \"./resolve\";\nimport type { InferNodeType } from \"./types\";\n\n/**\n * The node `path` addresses under `node`, with its ancestors from `node`, or\n * `null`.\n *\n * Every step of `path` names a direct child of the node before it; the first\n * step names a direct child of `node`, so a path read from a segment starts\n * with the field. A step without `[n]` names the first such child, so\n * `select` returns `null` when that child does not lead to a match, even if a\n * later one does; `selectAll` returns every match.\n *\n * @example\n * ```typescript\n * select(root, \"PID.5\"); // { node: Field, ancestors: [Root, Segment] }\n * select(root, \"PATIENT_RESULT.PATIENT.PID.5\");\n * select(segment, \"5.1\"); // { node: Component, ancestors: [Segment, Field, FieldRepetition] }\n * ```;\n *\n * @param node - The message, group, or segment to read from.\n * @param path - The path to the node.\n * @returns The node and its ancestors, or `null` when nothing matches.\n */\nexport function select<Path extends string>(\n node: Root | Group | Segment,\n path: Path\n): { node: InferNodeType<Path>; ancestors: Nodes[] } | null {\n const [match] = resolve(node, parse(path), false);\n return (match ?? null) as {\n node: InferNodeType<Path>;\n ancestors: Nodes[];\n } | null;\n}\n\n/**\n * Every node `path` addresses under `node`, with its ancestors from `node`, in\n * document order.\n *\n * Every step of `path` names a direct child of the node before it; the first\n * step names a direct child of `node`, so a path read from a segment starts\n * with the field. A step without `[n]` matches every such child, and so does\n * a field without a repetition when `path` names a component.\n *\n * @example\n * ```typescript\n * selectAll(root, \"OBX.5\"); // OBX-5 of every top-level OBX\n * selectAll(root, \"OBX.1[2]\"); // the second repetition of OBX-1 in every OBX\n * selectAll(segment, \"3.1\"); // the first component of every repetition of field 3\n * ```;\n *\n * @param node - The message, group, or segment to read from.\n * @param path - The path to the nodes.\n * @returns The nodes and their ancestors; empty when nothing matches.\n */\nexport function selectAll<Path extends string>(\n node: Root | Group | Segment,\n path: Path\n): { node: InferNodeType<Path>; ancestors: Nodes[] }[] {\n return resolve(node, parse(path), true) as {\n node: InferNodeType<Path>;\n ancestors: Nodes[];\n }[];\n}\n","import type { Group, Nodes, Root, Segment } from \"@glion/ast\";\n\nimport { select } from \"./select\";\n\n/**\n * Get the string value at the path, drilling down through single-child nodes\n * when needed. Also returns the node and ancestor chain for context.\n *\n * This is a convenience function that automatically traverses through container\n * nodes (Field → FieldRepetition → Component → Subcomponent) to extract the\n * primary string value. When a container has multiple children, the first child\n * is selected (e.g., `value(tree, \"MSH.12\")` returns the first component of a\n * composite VID field).\n *\n * Nodes with no children return `value: null` to differentiate between empty\n * strings (\"\") and missing values.\n *\n * @example\n * ```typescript\n * const result = value(root, \"PID.5.1\");\n * if (result) {\n * console.log(result.value); // string | null\n * console.log(result.node.type); // 'subcomponent'\n * console.log(result.ancestors); // [Root, Segment, Field, ...]\n * }\n *\n * value(segment, \"5.1\"); // the same value, read from the PID segment\n * ```;\n *\n * @param node - The message, group, or segment to read from\n * @param path - The HL7 path string\n * @returns Object containing the value, node, and ancestors from `node`, or\n * null if not found\n */\nexport function value(\n node: Root | Group | Segment,\n path: string\n): { value: string | null; node: Nodes; ancestors: Nodes[] } | null {\n const selected = select(node, path);\n return selected ? drill(selected.node, selected.ancestors) : null;\n}\n\n/**\n * The first subcomponent under `node`, with its value, or `node` with value\n * `null` when a field, repetition, or component on the way has no children.\n * `null` for a root, group, or segment.\n */\nfunction drill(\n node: Nodes,\n ancestors: Nodes[]\n): { value: string | null; node: Nodes; ancestors: Nodes[] } | null {\n switch (node.type) {\n // Stryker disable next-line StringLiteral: select() never returns the root\n case \"root\":\n case \"group\":\n case \"segment\": {\n return null;\n }\n case \"subcomponent\": {\n return { ancestors, node, value: node.value };\n }\n case \"field\":\n case \"field-repetition\":\n case \"component\": {\n const first = node.children?.[0];\n return first\n ? drill(first, [...ancestors, node])\n : { ancestors, node, value: null };\n }\n }\n}\n","import type { Delimiters, Root } from \"@glion/ast\";\nimport { DEFAULT_DELIMITERS } from \"@glion/utils\";\n\nimport { value } from \"./value\";\n\n/** Keys of MSH-2 encoding characters in positional order. */\nconst MSH2_KEYS = [\n \"component\",\n \"repetition\",\n \"escape\",\n \"subcomponent\",\n] as const;\n\n/**\n * Extract delimiters from a Root AST by reading MSH-1 and MSH-2.\n *\n * MSH-1 contains the field separator character (e.g., `|`).\n * MSH-2 contains up to 4 encoding characters: component, repetition,\n * escape, and subcomponent (e.g., `^~\\&`).\n *\n * Falls back to DEFAULT_DELIMITERS for any values that cannot be derived.\n *\n * @param root - The Root AST node to extract delimiters from.\n * @returns The resolved Delimiters object.\n */\nexport function delimiters(root: Root): Delimiters {\n const result = { ...DEFAULT_DELIMITERS };\n\n // MSH-1: field separator (single character)\n const msh1 = value(root, \"MSH.1\")?.value;\n if (msh1 && msh1.length === 1) {\n result.field = msh1;\n }\n\n // MSH-2: encoding characters (up to 4 positional chars)\n const msh2 = value(root, \"MSH.2\")?.value;\n if (msh2) {\n const chars = [...msh2];\n for (const [i, key] of MSH2_KEYS.entries()) {\n const ch = chars[i];\n if (ch) {\n result[key] = ch;\n }\n }\n }\n\n return result;\n}\n","import type { Group, Nodes, Root, Segment } from \"@glion/ast\";\n\nimport { keyOf, stepChildren } from \"./resolve\";\n\n/**\n * Format a node and its ancestor chain into a canonical HL7v2 path string.\n *\n * When `ancestors` starts at the root, `select(root, path)` returns `node`:\n * the path names every group, and `[n]` counts the siblings with the same\n * name, segments and groups together.\n *\n * @example\n * ```typescript\n * visit(tree, \"field\", (node, ancestors) => {\n * format(node, ancestors); // \"PID.5\"\n * });\n *\n * const result = select(root, \"PID.5.2\");\n * format(result.node, result.ancestors); // \"PID.5.2\"\n * ```;\n *\n * @param node - The target AST node\n * @param ancestors - Ancestor chain from root to parent (as provided by\n * visit/select)\n * @returns Canonical path string (e.g., \"PID.5.2\", \"ORDER.ORC.1\"), or `null` if the\n * ancestor chain contains no segment or group, or an ancestor does not\n * hold the next node in the chain\n */\nexport function format(node: Nodes, ancestors: Nodes[]): string | null {\n const chain = [...ancestors, node];\n const steps: string[] = [];\n let positions = \"\";\n\n for (const [index, current] of chain.entries()) {\n const position = positionOf(current, chain[index - 1]);\n if (position === 0) {\n return null;\n }\n switch (current.type) {\n case \"root\": {\n break;\n }\n case \"group\":\n case \"segment\": {\n steps.push(\n position > 1 ? `${keyOf(current)}[${position}]` : `${keyOf(current)}`\n );\n break;\n }\n case \"field-repetition\": {\n if (position > 1 || current === node) {\n positions += `[${position}]`;\n }\n break;\n }\n case \"field\":\n case \"component\":\n case \"subcomponent\": {\n positions += `.${position}`;\n break;\n }\n }\n }\n\n return steps.length === 0 ? null : steps.join(\".\") + positions;\n}\n\n/**\n * The 1-based position of `node` among the children of `parent`: for a\n * segment or group, its `[n]` counted as `select` counts it; otherwise its\n * index. 1 without a parent; 0 when `parent` does not hold `node`.\n */\nfunction positionOf(node: Nodes, parent: Nodes | undefined): number {\n if (parent === undefined || !(\"children\" in parent)) {\n return 1;\n }\n switch (node.type) {\n case \"group\":\n case \"segment\": {\n return (\n stepChildren(\n parent as Root | Segment | Group,\n keyOf(node) as string,\n undefined,\n true\n ).indexOf(node) + 1\n );\n }\n // Stryker disable next-line StringLiteral: the root is first in the chain, so it has no parent and returns above\n case \"root\":\n case \"field\":\n case \"field-repetition\":\n case \"component\":\n case \"subcomponent\": {\n return (parent.children as Nodes[]).indexOf(node) + 1;\n }\n }\n}\n","import type { Group, Root, Segment } from \"@glion/ast\";\n\nimport { select } from \"./select\";\n\n/**\n * Whether `select(node, path)` finds a node.\n *\n * A step without `[n]` names the first such child, so `matches` is `false`\n * when that child does not lead to a match, even if a later one does.\n *\n * @example\n * ```typescript\n * if (matches(root, \"PID.5\")) {\n * console.log(\"Patient has a name field\");\n * }\n *\n * if (!matches(root, \"OBX.5\")) {\n * throw new Error(\"Missing observation value\");\n * }\n * ```;\n *\n * @param node - The message, group, or segment to read from\n * @param path - The HL7 path string\n * @returns True if the path resolves to a node, false otherwise\n */\nexport function matches(node: Root | Group | Segment, path: string): boolean {\n return select(node, path) !== null;\n}\n"],"mappings":"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"}
|
package/dist/matches.d.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type { Root } from "@glion/ast";
|
|
1
|
+
import type { Group, Root, Segment } from "@glion/ast";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Whether `select(node, path)` finds a node.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* A step without `[n]` names the first such child, so `matches` is `false`
|
|
6
|
+
* when that child does not lead to a match, even if a later one does.
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```typescript
|
|
10
|
-
* if (matches(root, "PID
|
|
10
|
+
* if (matches(root, "PID.5")) {
|
|
11
11
|
* console.log("Patient has a name field");
|
|
12
12
|
* }
|
|
13
13
|
*
|
|
14
|
-
* if (!matches(root, "OBX
|
|
14
|
+
* if (!matches(root, "OBX.5")) {
|
|
15
15
|
* throw new Error("Missing observation value");
|
|
16
16
|
* }
|
|
17
17
|
* ```;
|
|
18
18
|
*
|
|
19
|
-
* @param
|
|
19
|
+
* @param node - The message, group, or segment to read from
|
|
20
20
|
* @param path - The HL7 path string
|
|
21
21
|
* @returns True if the path resolves to a node, false otherwise
|
|
22
22
|
*/
|
|
23
|
-
export declare function matches(
|
|
23
|
+
export declare function matches(node: Root | Group | Segment, path: string): boolean;
|
|
24
24
|
//# sourceMappingURL=matches.d.ts.map
|
package/dist/matches.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matches.d.ts","sourceRoot":"","sources":["../src/matches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"matches.d.ts","sourceRoot":"","sources":["../src/matches.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAIvD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3E"}
|
package/dist/parse.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import type { PathParts } from "./types";
|
|
2
|
-
export declare function clearParseCache(): void;
|
|
3
|
-
export declare function getParseCacheSize(): number;
|
|
4
2
|
/**
|
|
5
3
|
* Parse an HL7 path string into structured parts.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
*
|
|
5
|
+
* Returns a new object on every call.
|
|
8
6
|
*
|
|
9
7
|
* @example
|
|
10
8
|
* ```typescript
|
|
11
|
-
* parse("PID
|
|
9
|
+
* parse("PID.5[1].2.1");
|
|
12
10
|
* // {
|
|
13
11
|
* // segment: { name: 'PID' },
|
|
14
12
|
* // field: 5,
|
package/dist/parse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,SAAS,CAAC;AAkCvD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CA+C7C"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Group, Nodes, Root, Segment } from "@glion/ast";
|
|
2
|
+
import type { PathParts } from "./types";
|
|
3
|
+
/** A node a path resolves to, with its ancestors from the start node. */
|
|
4
|
+
export interface Match {
|
|
5
|
+
node: Nodes;
|
|
6
|
+
ancestors: Nodes[];
|
|
7
|
+
}
|
|
8
|
+
/** The name a path gives a node: a segment's name, or a group's ID. */
|
|
9
|
+
export declare const keyOf: (node: Nodes) => string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* The children of `parent` a path step names `name`, segments and groups
|
|
12
|
+
* together: the `position`th (1-based) when given; otherwise every one when
|
|
13
|
+
* `every` is set, or the first.
|
|
14
|
+
*/
|
|
15
|
+
export declare function stepChildren(parent: Root | Segment | Group, name: string, position: number | undefined, every: boolean): (Segment | Group)[];
|
|
16
|
+
/**
|
|
17
|
+
* The nodes `parts` addresses under `start`, in document order, each with its
|
|
18
|
+
* own ancestors array, which begins at `start`.
|
|
19
|
+
*
|
|
20
|
+
* Every step names a direct child of the node before it; the first step
|
|
21
|
+
* names a direct child of `start`. A step's `[n]` counts the children with
|
|
22
|
+
* that name, segments and groups together. A step without `[n]` matches the
|
|
23
|
+
* first such child, or every one when `every` is set; so does a field
|
|
24
|
+
* without a repetition when the path names a component.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolve(start: Root | Group | Segment, parts: PathParts, every: boolean): Match[];
|
|
27
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE9D,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,SAAS,CAAC;AAEvD,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,KAAK,CAAC;IACZ,SAAS,EAAE,KAAK,EAAE,CAAC;CACpB;AAED,uEAAuE;AACvE,eAAO,MAAM,KAAK,GAAI,MAAM,KAAK,KAAG,MAAM,GAAG,SAkB5C,CAAC;AAkBF;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,KAAK,EAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,KAAK,EAAE,OAAO,GACb,CAAC,OAAO,GAAG,KAAK,CAAC,EAAE,CAkBrB;AA4ED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CACrB,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,EAC7B,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,OAAO,GACb,KAAK,EAAE,CA6BT"}
|
package/dist/select.d.ts
CHANGED
|
@@ -1,54 +1,51 @@
|
|
|
1
|
-
import type { Nodes, Root } from "@glion/ast";
|
|
1
|
+
import type { Group, Nodes, Root, Segment } from "@glion/ast";
|
|
2
2
|
import type { InferNodeType } from "./types";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The node `path` addresses under `node`, with its ancestors from `node`, or
|
|
5
|
+
* `null`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Every step of `path` names a direct child of the node before it; the first
|
|
8
|
+
* step names a direct child of `node`, so a path read from a segment starts
|
|
9
|
+
* with the field. A step without `[n]` names the first such child, so
|
|
10
|
+
* `select` returns `null` when that child does not lead to a match, even if a
|
|
11
|
+
* later one does; `selectAll` returns every match.
|
|
10
12
|
*
|
|
11
13
|
* @example
|
|
12
14
|
* ```typescript
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* result.ancestors; // [Root, Segment]
|
|
17
|
-
* }
|
|
15
|
+
* select(root, "PID.5"); // { node: Field, ancestors: [Root, Segment] }
|
|
16
|
+
* select(root, "PATIENT_RESULT.PATIENT.PID.5");
|
|
17
|
+
* select(segment, "5.1"); // { node: Component, ancestors: [Segment, Field, FieldRepetition] }
|
|
18
18
|
* ```;
|
|
19
19
|
*
|
|
20
|
-
* @param
|
|
21
|
-
* @param path - The
|
|
22
|
-
* @returns
|
|
20
|
+
* @param node - The message, group, or segment to read from.
|
|
21
|
+
* @param path - The path to the node.
|
|
22
|
+
* @returns The node and its ancestors, or `null` when nothing matches.
|
|
23
23
|
*/
|
|
24
|
-
export declare function select<Path extends string>(
|
|
24
|
+
export declare function select<Path extends string>(node: Root | Group | Segment, path: Path): {
|
|
25
25
|
node: InferNodeType<Path>;
|
|
26
26
|
ancestors: Nodes[];
|
|
27
27
|
} | null;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* Every node `path` addresses under `node`, with its ancestors from `node`, in
|
|
30
|
+
* document order.
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
32
|
+
* Every step of `path` names a direct child of the node before it; the first
|
|
33
|
+
* step names a direct child of `node`, so a path read from a segment starts
|
|
34
|
+
* with the field. A step without `[n]` matches every such child, and so does
|
|
35
|
+
* a field without a repetition when `path` names a component.
|
|
34
36
|
*
|
|
35
37
|
* @example
|
|
36
38
|
* ```typescript
|
|
37
|
-
* //
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* console.log(node.type); // 'segment'
|
|
41
|
-
* }
|
|
42
|
-
*
|
|
43
|
-
* // Get all patient name fields
|
|
44
|
-
* const names = selectAll(root, "PID-5");
|
|
39
|
+
* selectAll(root, "OBX.5"); // OBX-5 of every top-level OBX
|
|
40
|
+
* selectAll(root, "OBX.1[2]"); // the second repetition of OBX-1 in every OBX
|
|
41
|
+
* selectAll(segment, "3.1"); // the first component of every repetition of field 3
|
|
45
42
|
* ```;
|
|
46
43
|
*
|
|
47
|
-
* @param
|
|
48
|
-
* @param path - The
|
|
49
|
-
* @returns
|
|
44
|
+
* @param node - The message, group, or segment to read from.
|
|
45
|
+
* @param path - The path to the nodes.
|
|
46
|
+
* @returns The nodes and their ancestors; empty when nothing matches.
|
|
50
47
|
*/
|
|
51
|
-
export declare function selectAll<Path extends string>(
|
|
48
|
+
export declare function selectAll<Path extends string>(node: Root | Group | Segment, path: Path): {
|
|
52
49
|
node: InferNodeType<Path>;
|
|
53
50
|
ancestors: Nodes[];
|
|
54
51
|
}[];
|
package/dist/select.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,MAAM,CAAC,IAAI,SAAS,MAAM,EACxC,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,EAC5B,IAAI,EAAE,IAAI,GACT;IAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAAC,SAAS,EAAE,KAAK,EAAE,CAAA;CAAE,GAAG,IAAI,CAM1D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,IAAI,SAAS,MAAM,EAC3C,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,EAC5B,IAAI,EAAE,IAAI,GACT;IAAE,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAAC,SAAS,EAAE,KAAK,EAAE,CAAA;CAAE,EAAE,CAKrD"}
|
package/dist/types.d.ts
CHANGED
|
@@ -9,53 +9,47 @@ export interface SegmentLocator {
|
|
|
9
9
|
}
|
|
10
10
|
export interface PathParts {
|
|
11
11
|
groups?: GroupLocator[];
|
|
12
|
-
segment
|
|
12
|
+
segment?: SegmentLocator;
|
|
13
13
|
field?: number;
|
|
14
14
|
repetition?: number;
|
|
15
15
|
component?: number;
|
|
16
16
|
subcomponent?: number;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Infer the node a field reference names from the steps after the field
|
|
20
|
+
* number: none, a component, or a component and a subcomponent.
|
|
20
21
|
*/
|
|
21
|
-
type
|
|
22
|
+
type InferFieldNodeType<FieldStep extends string, Rest extends string[]> = Rest extends [] ? FieldStep extends `${string}[${string}]` ? FieldRepetition : Field : Rest extends [string] ? Component : Subcomponent;
|
|
23
|
+
/** Split a path into its `.`-separated steps. */
|
|
24
|
+
type Steps<Path extends string> = Path extends `${infer Step}.${infer Rest}` ? [Step, ...Steps<Rest>] : [Path];
|
|
22
25
|
/**
|
|
23
|
-
*
|
|
26
|
+
* Skip the named steps of a path. The first step that starts with a field
|
|
27
|
+
* number decides the node; without one, the path names a segment or a group.
|
|
24
28
|
*/
|
|
25
|
-
type
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
type LastPart<Path extends string> = Path extends `${infer _Before}-${infer After}` ? LastPart<After> : Path;
|
|
29
|
+
type InferFromSteps<S extends string[]> = S extends [
|
|
30
|
+
infer Step extends string,
|
|
31
|
+
...infer Rest extends string[]
|
|
32
|
+
] ? Step extends `${number}` | `${number}[${string}]` ? InferFieldNodeType<Step, Rest> : InferFromSteps<Rest> : Segment | Group;
|
|
30
33
|
/**
|
|
31
34
|
* Infer the exact node type from an HL7 path string at compile time.
|
|
32
35
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* to least specific to ensure correct matching.
|
|
36
|
+
* Without a field number, the path names a segment or a group, and the AST
|
|
37
|
+
* decides which. With one, the steps after it decide the node.
|
|
36
38
|
*
|
|
37
39
|
* @example
|
|
38
40
|
* ```typescript
|
|
39
|
-
* // No field access = could be Segment OR Group (AST determines which)
|
|
40
41
|
* type T1 = InferNodeType<"PID">; // Segment | Group
|
|
41
42
|
* type T2 = InferNodeType<"ORDER">; // Segment | Group
|
|
42
|
-
* type T3 = InferNodeType<"ORDER
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* type
|
|
46
|
-
* type
|
|
47
|
-
* type
|
|
48
|
-
* type
|
|
49
|
-
* type
|
|
50
|
-
* type T9 = InferNodeType<"PID-4[1].1.1">; // Subcomponent (with repetition)
|
|
51
|
-
* type T10 = InferNodeType<"ORDER-OBR-5">; // Field (has field number = OBR is segment)
|
|
52
|
-
*
|
|
53
|
-
* // Key insight: Field access tells us it's definitively a segment
|
|
54
|
-
* // - No field access: Could be segment or group → Segment | Group
|
|
55
|
-
* // - Has field access (-N): Must be a segment → Field/Component/Subcomponent
|
|
43
|
+
* type T3 = InferNodeType<"ORDER.OBR">; // Segment | Group
|
|
44
|
+
* type T4 = InferNodeType<"PID.5">; // Field
|
|
45
|
+
* type T5 = InferNodeType<"PID.5[1]">; // FieldRepetition
|
|
46
|
+
* type T6 = InferNodeType<"PID.5.2">; // Component
|
|
47
|
+
* type T7 = InferNodeType<"PID.5[1].2">; // Component
|
|
48
|
+
* type T8 = InferNodeType<"PID.5.2.1">; // Subcomponent
|
|
49
|
+
* type T9 = InferNodeType<"PID.4[1].1.1">; // Subcomponent
|
|
50
|
+
* type T10 = InferNodeType<"ORDER.OBR.5">; // Field
|
|
56
51
|
* ```;
|
|
57
52
|
*/
|
|
58
|
-
export type InferNodeType<Path extends string> = string extends Path ? Nodes :
|
|
59
|
-
Segment | Group;
|
|
53
|
+
export type InferNodeType<Path extends string> = string extends Path ? Nodes : InferFromSteps<Steps<Path>>;
|
|
60
54
|
export {};
|
|
61
55
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EACL,eAAe,EACf,KAAK,EACL,KAAK,EACL,OAAO,EACP,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,KAAK,EACL,eAAe,EACf,KAAK,EACL,KAAK,EACL,OAAO,EACP,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,KAAK,kBAAkB,CACrB,SAAS,SAAS,MAAM,EACxB,IAAI,SAAS,MAAM,EAAE,IACnB,IAAI,SAAS,EAAE,GACf,SAAS,SAAS,GAAG,MAAM,IAAI,MAAM,GAAG,GACtC,eAAe,GACf,KAAK,GACP,IAAI,SAAS,CAAC,MAAM,CAAC,GACnB,SAAS,GACT,YAAY,CAAC;AAEnB,iDAAiD;AACjD,KAAK,KAAK,CAAC,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GACxE,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,GACtB,CAAC,IAAI,CAAC,CAAC;AAEX;;;GAGG;AACH,KAAK,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,CAAC,SAAS;IAClD,MAAM,IAAI,SAAS,MAAM;IACzB,GAAG,MAAM,IAAI,SAAS,MAAM,EAAE;CAC/B,GACG,IAAI,SAAS,GAAG,MAAM,EAAE,GAAG,GAAG,MAAM,IAAI,MAAM,GAAG,GAC/C,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,GAC9B,cAAc,CAAC,IAAI,CAAC,GACtB,OAAO,GAAG,KAAK,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,aAAa,CAAC,IAAI,SAAS,MAAM,IAAI,MAAM,SAAS,IAAI,GAChE,KAAK,GACL,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC"}
|
package/dist/value.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Nodes, Root } from "@glion/ast";
|
|
1
|
+
import type { Group, Nodes, Root, Segment } from "@glion/ast";
|
|
2
2
|
/**
|
|
3
3
|
* Get the string value at the path, drilling down through single-child nodes
|
|
4
4
|
* when needed. Also returns the node and ancestor chain for context.
|
|
@@ -6,7 +6,7 @@ import type { Nodes, Root } from "@glion/ast";
|
|
|
6
6
|
* This is a convenience function that automatically traverses through container
|
|
7
7
|
* nodes (Field → FieldRepetition → Component → Subcomponent) to extract the
|
|
8
8
|
* primary string value. When a container has multiple children, the first child
|
|
9
|
-
* is selected (e.g., `value(tree, "MSH
|
|
9
|
+
* is selected (e.g., `value(tree, "MSH.12")` returns the first component of a
|
|
10
10
|
* composite VID field).
|
|
11
11
|
*
|
|
12
12
|
* Nodes with no children return `value: null` to differentiate between empty
|
|
@@ -14,20 +14,22 @@ import type { Nodes, Root } from "@glion/ast";
|
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```typescript
|
|
17
|
-
* const result = value(root, "PID
|
|
17
|
+
* const result = value(root, "PID.5.1");
|
|
18
18
|
* if (result) {
|
|
19
19
|
* console.log(result.value); // string | null
|
|
20
20
|
* console.log(result.node.type); // 'subcomponent'
|
|
21
21
|
* console.log(result.ancestors); // [Root, Segment, Field, ...]
|
|
22
22
|
* }
|
|
23
|
+
*
|
|
24
|
+
* value(segment, "5.1"); // the same value, read from the PID segment
|
|
23
25
|
* ```;
|
|
24
26
|
*
|
|
25
|
-
* @param
|
|
27
|
+
* @param node - The message, group, or segment to read from
|
|
26
28
|
* @param path - The HL7 path string
|
|
27
|
-
* @returns Object containing the value, node, and ancestors
|
|
28
|
-
* found
|
|
29
|
+
* @returns Object containing the value, node, and ancestors from `node`, or
|
|
30
|
+
* null if not found
|
|
29
31
|
*/
|
|
30
|
-
export declare function value(
|
|
32
|
+
export declare function value(node: Root | Group | Segment, path: string): {
|
|
31
33
|
value: string | null;
|
|
32
34
|
node: Nodes;
|
|
33
35
|
ancestors: Nodes[];
|
package/dist/value.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"value.d.ts","sourceRoot":"","sources":["../src/value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"value.d.ts","sourceRoot":"","sources":["../src/value.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,KAAK,CACnB,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,OAAO,EAC5B,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,KAAK,EAAE,CAAA;CAAE,GAAG,IAAI,CAGlE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glion/util-query",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Query HL7v2 AST nodes by HL7 location paths",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"health",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@glion/ast": "0.
|
|
37
|
-
"@glion/utils": "0.
|
|
36
|
+
"@glion/ast": "0.27.0",
|
|
37
|
+
"@glion/utils": "0.27.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@stryker-mutator/core": "10.0.0",
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
"tsdown": "0.21.10",
|
|
47
47
|
"typescript": "6.0.3",
|
|
48
48
|
"vitest": "4.1.11",
|
|
49
|
-
"@glion/builder": "0.
|
|
49
|
+
"@glion/builder": "0.27.0",
|
|
50
50
|
"@glion/check-readme": "0.18.0",
|
|
51
|
-
"@glion/parser": "0.
|
|
51
|
+
"@glion/parser": "0.27.0",
|
|
52
52
|
"@glion/testing": "0.18.0",
|
|
53
53
|
"@glion/tsconfig": "0.18.0"
|
|
54
54
|
},
|
package/dist/set.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Root } from "@glion/ast";
|
|
2
|
-
/**
|
|
3
|
-
* Set a string value at the given HL7 path, creating all missing intermediate
|
|
4
|
-
* nodes as needed (fields, repetitions, components, subcomponents).
|
|
5
|
-
*
|
|
6
|
-
* This is the write counterpart to {@link value}. It handles the full HL7v2
|
|
7
|
-
* path syntax including group navigation and segment repetitions.
|
|
8
|
-
*
|
|
9
|
-
* Padding nodes are created with empty children arrays — they represent
|
|
10
|
-
* structurally absent nodes, not empty values. Only the target node gets
|
|
11
|
-
* a fully populated chain down to the subcomponent.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* // Update existing value
|
|
16
|
-
* set(tree, "MSH-12", "2.7.1");
|
|
17
|
-
*
|
|
18
|
-
* // Create missing MSH-9.3 (fills gaps with empty components)
|
|
19
|
-
* set(tree, "MSH-9.3", "ADT_A01");
|
|
20
|
-
*
|
|
21
|
-
* // Set a specific repetition
|
|
22
|
-
* set(tree, "PID-3[2]", "ID-002");
|
|
23
|
-
*
|
|
24
|
-
* // Set a subcomponent
|
|
25
|
-
* set(tree, "PID-5.1.2", "suffix");
|
|
26
|
-
* ```;
|
|
27
|
-
*
|
|
28
|
-
* @param root - The root node to modify
|
|
29
|
-
* @param path - The HL7 path string (must include at least a field)
|
|
30
|
-
* @param val - The string value to set
|
|
31
|
-
* @throws When the path has no field specifier (segment-only paths)
|
|
32
|
-
* @throws When the target segment does not exist in the tree
|
|
33
|
-
*/
|
|
34
|
-
export declare function set(root: Root, path: string, val: string): void;
|
|
35
|
-
//# sourceMappingURL=set.d.ts.map
|
package/dist/set.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../src/set.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,IAAI,EAEL,MAAM,YAAY,CAAC;AAKpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAsC/D"}
|