@rethinkhealth/hl7v2-utils 0.2.14 → 0.2.15
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/dist/index.d.ts +3 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +104 -2
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +154 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
component: string;
|
|
5
|
-
repetition: string;
|
|
6
|
-
subcomponent: string;
|
|
7
|
-
escape: string;
|
|
8
|
-
segment: string;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Utility: check if a node is semantically empty
|
|
12
|
-
*/
|
|
13
|
-
export declare function isEmptyNode(node: Nodes | null | undefined): boolean;
|
|
1
|
+
/** biome-ignore-all lint/performance/noBarrelFile: fine */
|
|
2
|
+
export type { PathParts } from './types';
|
|
3
|
+
export { DEFAULT_DELIMITERS, formatPath, getSegmentId, isEmptyNode, pathFromIndices, } from './utils';
|
|
14
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,YAAY,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,eAAe,GAChB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/utils.ts
|
|
2
2
|
var DEFAULT_DELIMITERS = {
|
|
3
3
|
field: "|",
|
|
4
4
|
component: "^",
|
|
@@ -25,8 +25,110 @@ function isEmptyNode(node) {
|
|
|
25
25
|
}
|
|
26
26
|
return false;
|
|
27
27
|
}
|
|
28
|
+
function validateHL7Number(value, componentName) {
|
|
29
|
+
if (!Number.isInteger(value)) {
|
|
30
|
+
throw new Error(`${componentName} must be an integer, got: ${value}`);
|
|
31
|
+
}
|
|
32
|
+
if (value < 1) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`${componentName} must be positive (1-based), got: ${value}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function validateASTIndex(value, componentName) {
|
|
39
|
+
if (!Number.isInteger(value)) {
|
|
40
|
+
throw new Error(`${componentName} must be an integer, got: ${value}`);
|
|
41
|
+
}
|
|
42
|
+
if (value < 0) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`${componentName} must be non-negative (0-based), got: ${value}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
var SEGMENT_ID_PATTERN = /^[A-Z0-9]{2,4}$/;
|
|
49
|
+
function validateSegmentId(segmentId) {
|
|
50
|
+
if (!segmentId || typeof segmentId !== "string") {
|
|
51
|
+
throw new Error(`segmentId must be a non-empty string, got: ${segmentId}`);
|
|
52
|
+
}
|
|
53
|
+
if (segmentId.trim() !== segmentId) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`segmentId cannot have leading/trailing whitespace, got: "${segmentId}"`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
if (!SEGMENT_ID_PATTERN.test(segmentId)) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`segmentId must be 2-4 uppercase letters/numbers, got: "${segmentId}"`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getSegmentId(seg) {
|
|
65
|
+
return seg.children[0]?.children[0]?.children[0]?.children[0]?.value ?? null;
|
|
66
|
+
}
|
|
67
|
+
function formatPath(parts) {
|
|
68
|
+
const { segmentId, field, repetition, component, subcomponent } = parts;
|
|
69
|
+
validateSegmentId(segmentId);
|
|
70
|
+
if (field != null) {
|
|
71
|
+
validateHL7Number(field, "field");
|
|
72
|
+
}
|
|
73
|
+
if (repetition != null) {
|
|
74
|
+
validateHL7Number(repetition, "repetition");
|
|
75
|
+
}
|
|
76
|
+
if (component != null) {
|
|
77
|
+
validateHL7Number(component, "component");
|
|
78
|
+
}
|
|
79
|
+
if (subcomponent != null) {
|
|
80
|
+
validateHL7Number(subcomponent, "subcomponent");
|
|
81
|
+
}
|
|
82
|
+
let out = segmentId;
|
|
83
|
+
if (typeof field === "number") {
|
|
84
|
+
out += `-${field}`;
|
|
85
|
+
}
|
|
86
|
+
if (typeof repetition === "number") {
|
|
87
|
+
out += `[${repetition}]`;
|
|
88
|
+
}
|
|
89
|
+
if (typeof component === "number") {
|
|
90
|
+
out += `.${component}`;
|
|
91
|
+
}
|
|
92
|
+
if (typeof subcomponent === "number") {
|
|
93
|
+
out += `.${subcomponent}`;
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
function pathFromIndices(parts) {
|
|
98
|
+
const {
|
|
99
|
+
segmentId,
|
|
100
|
+
fieldIndex,
|
|
101
|
+
repetitionIndex,
|
|
102
|
+
componentIndex,
|
|
103
|
+
subcomponentIndex
|
|
104
|
+
} = parts;
|
|
105
|
+
validateSegmentId(segmentId);
|
|
106
|
+
if (fieldIndex != null) {
|
|
107
|
+
validateHL7Number(fieldIndex, "fieldIndex");
|
|
108
|
+
}
|
|
109
|
+
if (repetitionIndex != null) {
|
|
110
|
+
validateASTIndex(repetitionIndex, "repetitionIndex");
|
|
111
|
+
}
|
|
112
|
+
if (componentIndex != null) {
|
|
113
|
+
validateASTIndex(componentIndex, "componentIndex");
|
|
114
|
+
}
|
|
115
|
+
if (subcomponentIndex != null) {
|
|
116
|
+
validateASTIndex(subcomponentIndex, "subcomponentIndex");
|
|
117
|
+
}
|
|
118
|
+
return formatPath({
|
|
119
|
+
segmentId,
|
|
120
|
+
field: fieldIndex,
|
|
121
|
+
// fieldIndex is already HL7 field number (or undefined)
|
|
122
|
+
repetition: repetitionIndex != null ? repetitionIndex + 1 : void 0,
|
|
123
|
+
component: componentIndex != null ? componentIndex + 1 : void 0,
|
|
124
|
+
subcomponent: subcomponentIndex != null ? subcomponentIndex + 1 : void 0
|
|
125
|
+
});
|
|
126
|
+
}
|
|
28
127
|
export {
|
|
29
128
|
DEFAULT_DELIMITERS,
|
|
30
|
-
|
|
129
|
+
formatPath,
|
|
130
|
+
getSegmentId,
|
|
131
|
+
isEmptyNode,
|
|
132
|
+
pathFromIndices
|
|
31
133
|
};
|
|
32
134
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Nodes } from '@rethinkhealth/hl7v2-ast';\n\n// -------------\n// Delimiters\n// -------------\n\nexport const DEFAULT_DELIMITERS = {\n field: '|',\n component: '^',\n repetition: '~',\n subcomponent: '&',\n escape: '\\\\',\n segment: '\\r',\n};\n\n// -------------\n// General\n// -------------\n\n/**\n * Utility: check if a node is semantically empty\n */\nexport function isEmptyNode(node: Nodes | null | undefined): boolean {\n if (!node) {\n return true;\n }\n\n // If node has a \"value\" property (Subcomponent, maybe Component)\n if ('value' in node) {\n return !node.value || node.value.trim() === '';\n }\n\n // If node has children (Field, Component, Repetition, Segment, Root, etc.)\n if ('children' in node) {\n if (!node.children || node.children.length === 0) {\n return true;\n }\n\n // If node has more than one child, then it is considered non-empty\n if (node.children.length > 1) {\n return false;\n }\n\n // If node has only one child, then it is considered empty if the child is also empty\n return isEmptyNode(node.children[0]);\n }\n\n // Fallback: consider unknown node as non-empty\n return false;\n}\n"],"mappings":";AAMO,IAAM,qBAAqB;AAAA,EAChC,OAAO;AAAA,EACP,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,SAAS;AACX;AASO,SAAS,YAAY,MAAyC;AACnE,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAGA,MAAI,WAAW,MAAM;AACnB,WAAO,CAAC,KAAK,SAAS,KAAK,MAAM,KAAK,MAAM;AAAA,EAC9C;AAGA,MAAI,cAAc,MAAM;AACtB,QAAI,CAAC,KAAK,YAAY,KAAK,SAAS,WAAW,GAAG;AAChD,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,aAAO;AAAA,IACT;AAGA,WAAO,YAAY,KAAK,SAAS,CAAC,CAAC;AAAA,EACrC;AAGA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts"],"sourcesContent":["import type { Nodes, Segment } from '@rethinkhealth/hl7v2-ast';\nimport type { PathParts } from './types';\n\n// -------------\n// Delimiters\n// -------------\n\nexport const DEFAULT_DELIMITERS = {\n field: '|',\n component: '^',\n repetition: '~',\n subcomponent: '&',\n escape: '\\\\',\n segment: '\\r',\n};\n\n// -------------\n// General\n// -------------\n\n/**\n * Utility: check if a node is semantically empty\n */\nexport function isEmptyNode(node: Nodes | null | undefined): boolean {\n if (!node) {\n return true;\n }\n\n // If node has a \"value\" property (Subcomponent, maybe Component)\n if ('value' in node) {\n return !node.value || node.value.trim() === '';\n }\n\n // If node has children (Field, Component, Repetition, Segment, Root, etc.)\n if ('children' in node) {\n if (!node.children || node.children.length === 0) {\n return true;\n }\n\n // If node has more than one child, then it is considered non-empty\n if (node.children.length > 1) {\n return false;\n }\n\n // If node has only one child, then it is considered empty if the child is also empty\n return isEmptyNode(node.children[0]);\n }\n\n // Fallback: consider unknown node as non-empty\n return false;\n}\n\n/**\n * Validates that a numeric value is a valid HL7 component number.\n *\n * @param value - The value to validate\n * @param componentName - Name of the component for error messages\n * @throws {Error} If the value is not a valid positive integer\n */\nfunction validateHL7Number(value: number, componentName: string): void {\n if (!Number.isInteger(value)) {\n throw new Error(`${componentName} must be an integer, got: ${value}`);\n }\n if (value < 1) {\n throw new Error(\n `${componentName} must be positive (1-based), got: ${value}`\n );\n }\n}\n\n/**\n * Validates that an AST index is a valid non-negative integer.\n *\n * @param value - The value to validate\n * @param componentName - Name of the component for error messages\n * @throws {Error} If the value is not a valid non-negative integer\n */\nfunction validateASTIndex(value: number, componentName: string): void {\n if (!Number.isInteger(value)) {\n throw new Error(`${componentName} must be an integer, got: ${value}`);\n }\n if (value < 0) {\n throw new Error(\n `${componentName} must be non-negative (0-based), got: ${value}`\n );\n }\n}\n\n// Regex pattern for valid segment IDs (2-4 uppercase letters/numbers)\nconst SEGMENT_ID_PATTERN = /^[A-Z0-9]{2,4}$/;\n\n/**\n * Validates a segment ID string.\n *\n * @param segmentId - The segment ID to validate\n * @throws {Error} If the segment ID is invalid\n */\nfunction validateSegmentId(segmentId: string): void {\n if (!segmentId || typeof segmentId !== 'string') {\n throw new Error(`segmentId must be a non-empty string, got: ${segmentId}`);\n }\n if (segmentId.trim() !== segmentId) {\n throw new Error(\n `segmentId cannot have leading/trailing whitespace, got: \"${segmentId}\"`\n );\n }\n if (!SEGMENT_ID_PATTERN.test(segmentId)) {\n throw new Error(\n `segmentId must be 2-4 uppercase letters/numbers, got: \"${segmentId}\"`\n );\n }\n}\n\n/**\n * Extracts the segment ID from the segment's first field.\n *\n * Traverses the AST structure to find the segment identifier stored\n * in the first field's first repetition's first component's first\n * subcomponent value.\n *\n * @param seg - The segment AST node to extract the ID from\n * @returns The segment ID string (e.g., 'PID', 'MSH'), or null if not found\n *\n * @example\n * ```ts\n * const segmentId = getSegmentId(segmentNode);\n * if (segmentId === 'PID') {\n * console.log('Processing patient identification segment');\n * }\n * ```\n */\nexport function getSegmentId(seg: Segment): string | null {\n return seg.children[0]?.children[0]?.children[0]?.children[0]?.value ?? null;\n}\n\n/**\n * Builds an HL7 path string from structured path components.\n *\n * Constructs a standardized HL7 path string using the format:\n * `SEGMENT-FIELD[REPETITION].COMPONENT.SUBCOMPONENT`\n *\n * **IMPORTANT: All numeric components use 1-based HL7 numbering.**\n * This function expects HL7-standard 1-based numbers, not 0-based AST indices.\n * Use `pathFromIndices()` if you have 0-based AST indices to convert.\n *\n * Components are added incrementally - if a field is not provided,\n * none of its child components will be included in the path.\n *\n * @param parts - Path components using HL7 1-based numbering\n * @param parts.segmentId - Segment identifier (required, 2-4 uppercase chars)\n * @param parts.field - HL7 field number (1-based, optional, must be ≥1)\n * @param parts.repetition - Repetition number (1-based, optional, must be ≥1)\n * @param parts.component - Component number (1-based, optional, must be ≥1)\n * @param parts.subcomponent - Subcomponent number (1-based, optional, must be ≥1)\n * @returns Formatted HL7 path string\n * @throws {Error} If any numeric component is not a positive integer\n *\n * @example\n * ```ts\n * // Segment only\n * formatPath({ segmentId: 'PID' });\n * // Result: \"PID\"\n *\n * // Field path (HL7 field 5 = 1-based)\n * formatPath({ segmentId: 'PID', field: 5 });\n * // Result: \"PID-5\"\n *\n * // Complete path with HL7 1-based numbering\n * formatPath({\n * segmentId: 'PID',\n * field: 5, // HL7 field 5 (1-based)\n * repetition: 1, // First repetition (1-based)\n * component: 2, // Second component (1-based)\n * subcomponent: 1 // First subcomponent (1-based)\n * });\n * // Result: \"PID-5[1].2.1\"\n *\n * // Error cases - these will throw:\n * formatPath({ segmentId: 'PID', field: 0 }); // Error: field must be ≥1\n * formatPath({ segmentId: 'PID', field: 1.5 }); // Error: must be integer\n * formatPath({ segmentId: '' }); // Error: invalid segment ID\n * ```\n */\nexport function formatPath(parts: PathParts): string {\n const { segmentId, field, repetition, component, subcomponent } = parts;\n\n // Validate segment ID\n validateSegmentId(segmentId);\n\n // Validate numeric components (all should be 1-based positive integers)\n if (field != null) {\n validateHL7Number(field, 'field');\n }\n if (repetition != null) {\n validateHL7Number(repetition, 'repetition');\n }\n if (component != null) {\n validateHL7Number(component, 'component');\n }\n if (subcomponent != null) {\n validateHL7Number(subcomponent, 'subcomponent');\n }\n\n // Build path string\n let out = segmentId;\n if (typeof field === 'number') {\n out += `-${field}`;\n }\n if (typeof repetition === 'number') {\n out += `[${repetition}]`;\n }\n if (typeof component === 'number') {\n out += `.${component}`;\n }\n if (typeof subcomponent === 'number') {\n out += `.${subcomponent}`;\n }\n return out;\n}\n\n/**\n * Converts AST indices to 1-based HL7 path format, including only present components.\n *\n * Helper function for converting AST traversal indices to HL7 path strings.\n * Only includes path components that are explicitly provided (not undefined).\n * This prevents generating incomplete paths like \"PID-[1]\" when field is missing.\n *\n * **IMPORTANT: Mixed indexing convention:**\n * - `fieldIndex`: HL7 field number (1-based)\n * - Other indices: 0-based AST indices, converted to 1-based HL7 numbering\n *\n * @param parts - AST index-based path components\n * @param parts.segmentId - Segment identifier (required, 2-4 uppercase chars)\n * @param parts.fieldIndex - HL7 field number (1-based, optional, must be ≥1)\n * @param parts.repetitionIndex - 0-based repetition index (optional, must be ≥0)\n * @param parts.componentIndex - 0-based component index (optional, must be ≥0)\n * @param parts.subcomponentIndex - 0-based subcomponent index (optional, must be ≥0)\n * @returns Formatted HL7 path string with only present components\n * @throws {Error} If any provided index is invalid\n *\n * @example\n * ```ts\n * // Full path with all components\n * pathFromIndices({\n * segmentId: 'PID',\n * fieldIndex: 4, // HL7 field number -> HL7 field 4\n * repetitionIndex: 0, // 0-based AST index -> HL7 [1]\n * componentIndex: 1 // 0-based AST index -> HL7 .2\n * });\n * // Result: \"PID-4[1].2\"\n *\n * // Partial path - only includes present components\n * pathFromIndices({\n * segmentId: 'PID',\n * fieldIndex: 5 // Only field provided\n * });\n * // Result: \"PID-5\" (not \"PID-5[undefined]\")\n *\n * pathFromIndices({\n * segmentId: 'PID' // Only segment provided\n * });\n * // Result: \"PID\" (segment-level path)\n *\n * // Common usage during AST traversal\n * visit(root, (node, index, parent) => {\n * if (isComponent(node)) {\n * const path = pathFromIndices({\n * segmentId: getSegmentId(segmentNode),\n * fieldIndex: fieldNumber, // HL7 field number (1-based)\n * repetitionIndex: repIndex, // 0-based from parent.children[repIndex]\n * componentIndex: compIndex // 0-based from parent.children[compIndex]\n * // subcomponentIndex omitted if not relevant\n * });\n *\n * reportDiagnostic(file, {\n * code: 'invalid_value',\n * message: 'Component value is invalid',\n * source: 'field',\n * path // Clean path: \"PID-4[1].2\"\n * }, { ruleId: 'field:value' });\n * }\n * });\n * ```\n */\nexport function pathFromIndices(parts: {\n segmentId: string;\n fieldIndex?: number; // HL7 field number (1-based)\n repetitionIndex?: number; // 0-based AST index\n componentIndex?: number; // 0-based AST index\n subcomponentIndex?: number; // 0-based AST index\n}): string {\n const {\n segmentId,\n fieldIndex,\n repetitionIndex,\n componentIndex,\n subcomponentIndex,\n } = parts;\n\n // Validate segment ID\n validateSegmentId(segmentId);\n\n // Validate provided indices (undefined values are skipped)\n if (fieldIndex != null) {\n validateHL7Number(fieldIndex, 'fieldIndex'); // fieldIndex is HL7 field number (1-based)\n }\n if (repetitionIndex != null) {\n validateASTIndex(repetitionIndex, 'repetitionIndex');\n }\n if (componentIndex != null) {\n validateASTIndex(componentIndex, 'componentIndex');\n }\n if (subcomponentIndex != null) {\n validateASTIndex(subcomponentIndex, 'subcomponentIndex');\n }\n\n // Build path with only present components\n return formatPath({\n segmentId,\n field: fieldIndex, // fieldIndex is already HL7 field number (or undefined)\n repetition: repetitionIndex != null ? repetitionIndex + 1 : undefined,\n component: componentIndex != null ? componentIndex + 1 : undefined,\n subcomponent: subcomponentIndex != null ? subcomponentIndex + 1 : undefined,\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Components for building HL7 path strings.
|
|
3
|
+
*
|
|
4
|
+
* All numeric components use 1-based indexing to match HL7 conventions,
|
|
5
|
+
* except where explicitly noted for AST traversal helpers.
|
|
6
|
+
*/
|
|
7
|
+
export type PathParts = {
|
|
8
|
+
segmentId: string;
|
|
9
|
+
field?: number;
|
|
10
|
+
repetition?: number;
|
|
11
|
+
component?: number;
|
|
12
|
+
subcomponent?: number;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Nodes, Segment } from '@rethinkhealth/hl7v2-ast';
|
|
2
|
+
import type { PathParts } from './types';
|
|
3
|
+
export declare const DEFAULT_DELIMITERS: {
|
|
4
|
+
field: string;
|
|
5
|
+
component: string;
|
|
6
|
+
repetition: string;
|
|
7
|
+
subcomponent: string;
|
|
8
|
+
escape: string;
|
|
9
|
+
segment: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Utility: check if a node is semantically empty
|
|
13
|
+
*/
|
|
14
|
+
export declare function isEmptyNode(node: Nodes | null | undefined): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Extracts the segment ID from the segment's first field.
|
|
17
|
+
*
|
|
18
|
+
* Traverses the AST structure to find the segment identifier stored
|
|
19
|
+
* in the first field's first repetition's first component's first
|
|
20
|
+
* subcomponent value.
|
|
21
|
+
*
|
|
22
|
+
* @param seg - The segment AST node to extract the ID from
|
|
23
|
+
* @returns The segment ID string (e.g., 'PID', 'MSH'), or null if not found
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const segmentId = getSegmentId(segmentNode);
|
|
28
|
+
* if (segmentId === 'PID') {
|
|
29
|
+
* console.log('Processing patient identification segment');
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function getSegmentId(seg: Segment): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Builds an HL7 path string from structured path components.
|
|
36
|
+
*
|
|
37
|
+
* Constructs a standardized HL7 path string using the format:
|
|
38
|
+
* `SEGMENT-FIELD[REPETITION].COMPONENT.SUBCOMPONENT`
|
|
39
|
+
*
|
|
40
|
+
* **IMPORTANT: All numeric components use 1-based HL7 numbering.**
|
|
41
|
+
* This function expects HL7-standard 1-based numbers, not 0-based AST indices.
|
|
42
|
+
* Use `pathFromIndices()` if you have 0-based AST indices to convert.
|
|
43
|
+
*
|
|
44
|
+
* Components are added incrementally - if a field is not provided,
|
|
45
|
+
* none of its child components will be included in the path.
|
|
46
|
+
*
|
|
47
|
+
* @param parts - Path components using HL7 1-based numbering
|
|
48
|
+
* @param parts.segmentId - Segment identifier (required, 2-4 uppercase chars)
|
|
49
|
+
* @param parts.field - HL7 field number (1-based, optional, must be ≥1)
|
|
50
|
+
* @param parts.repetition - Repetition number (1-based, optional, must be ≥1)
|
|
51
|
+
* @param parts.component - Component number (1-based, optional, must be ≥1)
|
|
52
|
+
* @param parts.subcomponent - Subcomponent number (1-based, optional, must be ≥1)
|
|
53
|
+
* @returns Formatted HL7 path string
|
|
54
|
+
* @throws {Error} If any numeric component is not a positive integer
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* // Segment only
|
|
59
|
+
* formatPath({ segmentId: 'PID' });
|
|
60
|
+
* // Result: "PID"
|
|
61
|
+
*
|
|
62
|
+
* // Field path (HL7 field 5 = 1-based)
|
|
63
|
+
* formatPath({ segmentId: 'PID', field: 5 });
|
|
64
|
+
* // Result: "PID-5"
|
|
65
|
+
*
|
|
66
|
+
* // Complete path with HL7 1-based numbering
|
|
67
|
+
* formatPath({
|
|
68
|
+
* segmentId: 'PID',
|
|
69
|
+
* field: 5, // HL7 field 5 (1-based)
|
|
70
|
+
* repetition: 1, // First repetition (1-based)
|
|
71
|
+
* component: 2, // Second component (1-based)
|
|
72
|
+
* subcomponent: 1 // First subcomponent (1-based)
|
|
73
|
+
* });
|
|
74
|
+
* // Result: "PID-5[1].2.1"
|
|
75
|
+
*
|
|
76
|
+
* // Error cases - these will throw:
|
|
77
|
+
* formatPath({ segmentId: 'PID', field: 0 }); // Error: field must be ≥1
|
|
78
|
+
* formatPath({ segmentId: 'PID', field: 1.5 }); // Error: must be integer
|
|
79
|
+
* formatPath({ segmentId: '' }); // Error: invalid segment ID
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function formatPath(parts: PathParts): string;
|
|
83
|
+
/**
|
|
84
|
+
* Converts AST indices to 1-based HL7 path format, including only present components.
|
|
85
|
+
*
|
|
86
|
+
* Helper function for converting AST traversal indices to HL7 path strings.
|
|
87
|
+
* Only includes path components that are explicitly provided (not undefined).
|
|
88
|
+
* This prevents generating incomplete paths like "PID-[1]" when field is missing.
|
|
89
|
+
*
|
|
90
|
+
* **IMPORTANT: Mixed indexing convention:**
|
|
91
|
+
* - `fieldIndex`: HL7 field number (1-based)
|
|
92
|
+
* - Other indices: 0-based AST indices, converted to 1-based HL7 numbering
|
|
93
|
+
*
|
|
94
|
+
* @param parts - AST index-based path components
|
|
95
|
+
* @param parts.segmentId - Segment identifier (required, 2-4 uppercase chars)
|
|
96
|
+
* @param parts.fieldIndex - HL7 field number (1-based, optional, must be ≥1)
|
|
97
|
+
* @param parts.repetitionIndex - 0-based repetition index (optional, must be ≥0)
|
|
98
|
+
* @param parts.componentIndex - 0-based component index (optional, must be ≥0)
|
|
99
|
+
* @param parts.subcomponentIndex - 0-based subcomponent index (optional, must be ≥0)
|
|
100
|
+
* @returns Formatted HL7 path string with only present components
|
|
101
|
+
* @throws {Error} If any provided index is invalid
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* // Full path with all components
|
|
106
|
+
* pathFromIndices({
|
|
107
|
+
* segmentId: 'PID',
|
|
108
|
+
* fieldIndex: 4, // HL7 field number -> HL7 field 4
|
|
109
|
+
* repetitionIndex: 0, // 0-based AST index -> HL7 [1]
|
|
110
|
+
* componentIndex: 1 // 0-based AST index -> HL7 .2
|
|
111
|
+
* });
|
|
112
|
+
* // Result: "PID-4[1].2"
|
|
113
|
+
*
|
|
114
|
+
* // Partial path - only includes present components
|
|
115
|
+
* pathFromIndices({
|
|
116
|
+
* segmentId: 'PID',
|
|
117
|
+
* fieldIndex: 5 // Only field provided
|
|
118
|
+
* });
|
|
119
|
+
* // Result: "PID-5" (not "PID-5[undefined]")
|
|
120
|
+
*
|
|
121
|
+
* pathFromIndices({
|
|
122
|
+
* segmentId: 'PID' // Only segment provided
|
|
123
|
+
* });
|
|
124
|
+
* // Result: "PID" (segment-level path)
|
|
125
|
+
*
|
|
126
|
+
* // Common usage during AST traversal
|
|
127
|
+
* visit(root, (node, index, parent) => {
|
|
128
|
+
* if (isComponent(node)) {
|
|
129
|
+
* const path = pathFromIndices({
|
|
130
|
+
* segmentId: getSegmentId(segmentNode),
|
|
131
|
+
* fieldIndex: fieldNumber, // HL7 field number (1-based)
|
|
132
|
+
* repetitionIndex: repIndex, // 0-based from parent.children[repIndex]
|
|
133
|
+
* componentIndex: compIndex // 0-based from parent.children[compIndex]
|
|
134
|
+
* // subcomponentIndex omitted if not relevant
|
|
135
|
+
* });
|
|
136
|
+
*
|
|
137
|
+
* reportDiagnostic(file, {
|
|
138
|
+
* code: 'invalid_value',
|
|
139
|
+
* message: 'Component value is invalid',
|
|
140
|
+
* source: 'field',
|
|
141
|
+
* path // Clean path: "PID-4[1].2"
|
|
142
|
+
* }, { ruleId: 'field:value' });
|
|
143
|
+
* }
|
|
144
|
+
* });
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
export declare function pathFromIndices(parts: {
|
|
148
|
+
segmentId: string;
|
|
149
|
+
fieldIndex?: number;
|
|
150
|
+
repetitionIndex?: number;
|
|
151
|
+
componentIndex?: number;
|
|
152
|
+
subcomponentIndex?: number;
|
|
153
|
+
}): string;
|
|
154
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMzC,eAAO,MAAM,kBAAkB;;;;;;;CAO9B,CAAC;AAMF;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CA2BnE;AA+DD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAExD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAmCnD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,GAAG,MAAM,CAkCT"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rethinkhealth/hl7v2-utils",
|
|
3
3
|
"description": "hl7v2 utilities",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.15",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Melek Somai",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"typescript": "^5.8.3",
|
|
25
25
|
"unist-builder": "^4.0.0",
|
|
26
26
|
"vitest": "^3.2.4",
|
|
27
|
-
"@rethinkhealth/hl7v2-ast": "0.2.14",
|
|
28
27
|
"@rethinkhealth/testing": "0.0.1",
|
|
28
|
+
"@rethinkhealth/hl7v2-ast": "0.2.15",
|
|
29
29
|
"@rethinkhealth/tsconfig": "0.0.1"
|
|
30
30
|
},
|
|
31
31
|
"repository": "rethinkhealth/hl7v2.git",
|