@rethinkhealth/hl7v2-utils 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -49
- package/dist/constants.d.ts +3 -3
- package/dist/constraints.d.ts +40 -38
- package/dist/constraints.d.ts.map +1 -1
- package/dist/index.js +78 -73
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +5 -5
- package/dist/utils.d.ts.map +1 -1
- package/package.json +18 -18
package/README.md
CHANGED
|
@@ -32,27 +32,27 @@ Report a diagnostic to a VFile. This function is the standard way to report issu
|
|
|
32
32
|
#### Example
|
|
33
33
|
|
|
34
34
|
```typescript
|
|
35
|
-
import { report } from
|
|
36
|
-
import type { Diagnostic } from
|
|
37
|
-
import { VFile } from
|
|
35
|
+
import { report } from "@rethinkhealth/hl7v2-utils";
|
|
36
|
+
import type { Diagnostic } from "@rethinkhealth/hl7v2-utils";
|
|
37
|
+
import { VFile } from "vfile";
|
|
38
38
|
|
|
39
39
|
// Define a diagnostic rule
|
|
40
40
|
const requiredFieldRule: Diagnostic = {
|
|
41
|
-
type:
|
|
42
|
-
namespace:
|
|
43
|
-
code:
|
|
44
|
-
title:
|
|
45
|
-
description:
|
|
46
|
-
severity:
|
|
41
|
+
type: "lint",
|
|
42
|
+
namespace: "field",
|
|
43
|
+
code: "required",
|
|
44
|
+
title: "Required Field Missing",
|
|
45
|
+
description: "A required field is missing from the segment.",
|
|
46
|
+
severity: "error",
|
|
47
47
|
message: (ctx) => `Field '${ctx.fieldPath}' is required`,
|
|
48
|
-
helpUrl:
|
|
48
|
+
helpUrl: "https://example.com/docs/required-field",
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
// Report the diagnostic
|
|
52
52
|
const file = new VFile();
|
|
53
53
|
report(file, requiredFieldRule, {
|
|
54
|
-
context: { fieldPath:
|
|
55
|
-
node: segmentNode
|
|
54
|
+
context: { fieldPath: "PID-5" },
|
|
55
|
+
node: segmentNode,
|
|
56
56
|
});
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -81,25 +81,25 @@ The function is optimized for performance with O(n) time complexity where n is t
|
|
|
81
81
|
#### Example
|
|
82
82
|
|
|
83
83
|
```typescript
|
|
84
|
-
import { getByteLength } from
|
|
85
|
-
import type { Field } from
|
|
84
|
+
import { getByteLength } from "@rethinkhealth/hl7v2-utils";
|
|
85
|
+
import type { Field } from "@rethinkhealth/hl7v2-ast";
|
|
86
86
|
|
|
87
87
|
const field: Field = {
|
|
88
|
-
type:
|
|
88
|
+
type: "field",
|
|
89
89
|
children: [
|
|
90
90
|
{
|
|
91
|
-
type:
|
|
91
|
+
type: "field-repetition",
|
|
92
92
|
children: [
|
|
93
93
|
{
|
|
94
|
-
type:
|
|
94
|
+
type: "component",
|
|
95
95
|
children: [
|
|
96
|
-
{ type:
|
|
97
|
-
{ type:
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
}
|
|
102
|
-
]
|
|
96
|
+
{ type: "subcomponent", value: "SMITH" },
|
|
97
|
+
{ type: "subcomponent", value: "JOHN" },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
// Calculate: SMITH&JOHN = 5 + 1 + 4 = 10 bytes
|
|
@@ -142,25 +142,25 @@ The function is optimized for performance with O(n) time complexity where n is t
|
|
|
142
142
|
#### Example
|
|
143
143
|
|
|
144
144
|
```typescript
|
|
145
|
-
import { getLength } from
|
|
146
|
-
import type { Field } from
|
|
145
|
+
import { getLength } from "@rethinkhealth/hl7v2-utils";
|
|
146
|
+
import type { Field } from "@rethinkhealth/hl7v2-ast";
|
|
147
147
|
|
|
148
148
|
const field: Field = {
|
|
149
|
-
type:
|
|
149
|
+
type: "field",
|
|
150
150
|
children: [
|
|
151
151
|
{
|
|
152
|
-
type:
|
|
152
|
+
type: "field-repetition",
|
|
153
153
|
children: [
|
|
154
154
|
{
|
|
155
|
-
type:
|
|
155
|
+
type: "component",
|
|
156
156
|
children: [
|
|
157
|
-
{ type:
|
|
158
|
-
{ type:
|
|
159
|
-
]
|
|
160
|
-
}
|
|
161
|
-
]
|
|
162
|
-
}
|
|
163
|
-
]
|
|
157
|
+
{ type: "subcomponent", value: "SMITH" },
|
|
158
|
+
{ type: "subcomponent", value: "JOHN" },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
164
|
};
|
|
165
165
|
|
|
166
166
|
// Calculate: SMITH&JOHN = 5 + 1 + 4 = 10 characters
|
|
@@ -177,11 +177,11 @@ const length = getLength(field); // Returns: 10
|
|
|
177
177
|
#### Comparison with `getByteLength`
|
|
178
178
|
|
|
179
179
|
```typescript
|
|
180
|
-
import { getLength, getByteLength } from
|
|
180
|
+
import { getLength, getByteLength } from "@rethinkhealth/hl7v2-utils";
|
|
181
181
|
|
|
182
|
-
const subcomponent = { type:
|
|
182
|
+
const subcomponent = { type: "subcomponent", value: "café" };
|
|
183
183
|
|
|
184
|
-
getLength(subcomponent);
|
|
184
|
+
getLength(subcomponent); // Returns: 4 (4 characters)
|
|
185
185
|
getByteLength(subcomponent); // Returns: 5 (5 bytes in UTF-8: c-a-f-C3-A9)
|
|
186
186
|
```
|
|
187
187
|
|
|
@@ -194,16 +194,16 @@ The package provides stateless, composable functions to validate HL7v2 AST nodes
|
|
|
194
194
|
All conformance functions return a `ValidationResult` object:
|
|
195
195
|
|
|
196
196
|
```typescript
|
|
197
|
-
type ValidationResult =
|
|
197
|
+
type ValidationResult =
|
|
198
198
|
| { ok: true }
|
|
199
|
-
| {
|
|
200
|
-
ok: false;
|
|
201
|
-
error: {
|
|
202
|
-
code: string;
|
|
199
|
+
| {
|
|
200
|
+
ok: false;
|
|
201
|
+
error: {
|
|
202
|
+
code: string;
|
|
203
203
|
message: string;
|
|
204
204
|
expected?: string | number | Array<string | number>;
|
|
205
205
|
actual?: string | number | Array<string | number>;
|
|
206
|
-
}
|
|
206
|
+
};
|
|
207
207
|
};
|
|
208
208
|
```
|
|
209
209
|
|
|
@@ -216,10 +216,10 @@ Checks if a node satisfies the optionality (usage) constraint.
|
|
|
216
216
|
- **Returns**: `ValidationResult`
|
|
217
217
|
|
|
218
218
|
```typescript
|
|
219
|
-
import { checkOptionality } from
|
|
219
|
+
import { checkOptionality } from "@rethinkhealth/hl7v2-utils";
|
|
220
220
|
|
|
221
221
|
// 'R' (Required), 'RE' (Required or Empty), 'O' (Optional), 'X' (Not Supported)
|
|
222
|
-
const result = checkOptionality(myFieldNode,
|
|
222
|
+
const result = checkOptionality(myFieldNode, "R");
|
|
223
223
|
|
|
224
224
|
if (!result.ok) {
|
|
225
225
|
console.error(result.error.message); // "is required but missing"
|
|
@@ -236,7 +236,7 @@ Checks if a field has the correct number of repetitions.
|
|
|
236
236
|
- **Returns**: `ValidationResult`
|
|
237
237
|
|
|
238
238
|
```typescript
|
|
239
|
-
import { checkCardinality } from
|
|
239
|
+
import { checkCardinality } from "@rethinkhealth/hl7v2-utils";
|
|
240
240
|
|
|
241
241
|
// Field must repeat between 1 and 5 times
|
|
242
242
|
const result = checkCardinality(myFieldNode, 1, 5);
|
|
@@ -252,7 +252,7 @@ Checks if the content of a node falls within the minimum and maximum length.
|
|
|
252
252
|
- **Returns**: `ValidationResult`
|
|
253
253
|
|
|
254
254
|
```typescript
|
|
255
|
-
import { checkLength } from
|
|
255
|
+
import { checkLength } from "@rethinkhealth/hl7v2-utils";
|
|
256
256
|
|
|
257
257
|
// Content length must be between 1 and 10 characters
|
|
258
258
|
const result = checkLength(myNode, 10, 1);
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export declare const DEFAULT_DELIMITERS: {
|
|
2
|
-
field: string;
|
|
3
2
|
component: string;
|
|
4
|
-
repetition: string;
|
|
5
|
-
subcomponent: string;
|
|
6
3
|
escape: string;
|
|
4
|
+
field: string;
|
|
5
|
+
repetition: string;
|
|
7
6
|
segment: string;
|
|
7
|
+
subcomponent: string;
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constraints.d.ts
CHANGED
|
@@ -1,50 +1,28 @@
|
|
|
1
1
|
import type { Field, Nodes } from "@rethinkhealth/hl7v2-ast";
|
|
2
2
|
export type ValidationErrorCode = "MISSING" | "EMPTY" | "UNEXPECTED_CONTENT" | "CARDINALITY_UNDERFLOW" | "CARDINALITY_OVERFLOW" | "LENGTH_UNDERFLOW" | "LENGTH_OVERFLOW" | "VALUE_NOT_IN_TABLE";
|
|
3
|
-
export
|
|
3
|
+
export interface ValidationError {
|
|
4
4
|
code: ValidationErrorCode;
|
|
5
5
|
message: string;
|
|
6
6
|
expected?: string | number | Array<string | number>;
|
|
7
7
|
actual?: string | number | Array<string | number>;
|
|
8
|
-
}
|
|
9
|
-
export
|
|
8
|
+
}
|
|
9
|
+
export interface ValidationSuccess {
|
|
10
10
|
ok: true;
|
|
11
|
-
}
|
|
12
|
-
export
|
|
11
|
+
}
|
|
12
|
+
export interface ValidationFailure {
|
|
13
13
|
ok: false;
|
|
14
14
|
error: ValidationError;
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
export type ValidationResult = ValidationSuccess | ValidationFailure;
|
|
17
17
|
export declare const OptionalityCode: {
|
|
18
|
-
readonly Required: "R";
|
|
19
|
-
/**
|
|
20
|
-
* Required, but may be empty.
|
|
21
|
-
*
|
|
22
|
-
* @remarks
|
|
23
|
-
* The use of the RE usage code is qualified with the “if data is known”
|
|
24
|
-
* clause. The sender must interpret the clause as “the capability must always
|
|
25
|
-
* be supported, and data must always be sent if known”. To clarify, the
|
|
26
|
-
* sender does not determine whether the data should be sent; to be conformant
|
|
27
|
-
* to the rule, the data must be sent. There is a misconception where the RE
|
|
28
|
-
* usage is interpreted as “the capability must always be supported, and data
|
|
29
|
-
* may or may not be sent even when known based on a condition external to the
|
|
30
|
-
* profile specification”.
|
|
31
|
-
*
|
|
32
|
-
* The receiving application must process in a meaningful way the information
|
|
33
|
-
* conveyed by an element with an “RE” usage designation.
|
|
34
|
-
* The receiving application must process the message if the element is omitted
|
|
35
|
-
* (that is, an exception must not be raised because the element is missing). A
|
|
36
|
-
* receiving application must not raise an exception due to the presence of a
|
|
37
|
-
* required element.
|
|
38
|
-
*/
|
|
39
|
-
readonly RequiredOrEmpty: "RE";
|
|
40
18
|
/**
|
|
41
|
-
*
|
|
19
|
+
* Backward Compatible
|
|
42
20
|
*
|
|
43
|
-
* There are no implementation requirements. The “
|
|
44
|
-
*
|
|
45
|
-
*
|
|
21
|
+
* There are no implementation requirements. The “B” usage indicates that the
|
|
22
|
+
* element is retained for backwards compatibility of the element. Another
|
|
23
|
+
* usage indicator may be assigned in a derived profile.
|
|
46
24
|
*/
|
|
47
|
-
readonly
|
|
25
|
+
readonly BackwardCompatible: "B";
|
|
48
26
|
/**
|
|
49
27
|
* Undeclared / Conditional.
|
|
50
28
|
*
|
|
@@ -61,13 +39,37 @@ export declare const OptionalityCode: {
|
|
|
61
39
|
*/
|
|
62
40
|
readonly NotSupported: "X";
|
|
63
41
|
/**
|
|
64
|
-
*
|
|
42
|
+
* Optional
|
|
65
43
|
*
|
|
66
|
-
* There are no implementation requirements. The “
|
|
67
|
-
*
|
|
68
|
-
*
|
|
44
|
+
* There are no implementation requirements. The “O” usage designation is a
|
|
45
|
+
* placeholder indicating that the usage for this element has not yet been
|
|
46
|
+
* specified.
|
|
69
47
|
*/
|
|
70
|
-
readonly
|
|
48
|
+
readonly Optional: "O";
|
|
49
|
+
/**
|
|
50
|
+
* Required
|
|
51
|
+
*/
|
|
52
|
+
readonly Required: "R";
|
|
53
|
+
/**
|
|
54
|
+
* Required, but may be empty.
|
|
55
|
+
*
|
|
56
|
+
* The use of the RE usage code is qualified with the “if data is known”
|
|
57
|
+
* clause. The sender must interpret the clause as “the capability must always
|
|
58
|
+
* be supported, and data must always be sent if known”. To clarify, the
|
|
59
|
+
* sender does not determine whether the data should be sent; to be conformant
|
|
60
|
+
* to the rule, the data must be sent. There is a misconception where the RE
|
|
61
|
+
* usage is interpreted as “the capability must always be supported, and data
|
|
62
|
+
* may or may not be sent even when known based on a condition external to the
|
|
63
|
+
* profile specification”.
|
|
64
|
+
*
|
|
65
|
+
* The receiving application must process in a meaningful way the information
|
|
66
|
+
* conveyed by an element with an “RE” usage designation.
|
|
67
|
+
* The receiving application must process the message if the element is omitted
|
|
68
|
+
* (that is, an exception must not be raised because the element is missing). A
|
|
69
|
+
* receiving application must not raise an exception due to the presence of a
|
|
70
|
+
* required element.
|
|
71
|
+
*/
|
|
72
|
+
readonly RequiredOrEmpty: "RE";
|
|
71
73
|
/**
|
|
72
74
|
* Withdrawn
|
|
73
75
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAI7D,MAAM,MAAM,mBAAmB,GAE3B,SAAS,GACT,OAAO,GACP,oBAAoB,GACpB,uBAAuB,GACvB,sBAAsB,GACtB,kBAAkB,GAClB,iBAAiB,GAEjB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,IAAI,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAErE,eAAO,MAAM,eAAe;IAC1B;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;;OAKG;;IAGH;;;;;;OAMG;;IAGH;;OAEG;;IAGH;;;;;;;;;;;;;;;;;;OAkBG;;IAGH;;;;;;OAMG;;CAEK,CAAC;AAEX,MAAM,MAAM,eAAe,GACzB,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC;AAEzD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,KAAK,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,GAAG,GAAG,GAChB,gBAAgB,CAoClB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,KAAK,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,EACX,GAAG,SAAI,GACN,gBAAgB,CAoClB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,KAAK,GAAG,SAAS,EACvB,WAAW,EAAE,eAAe,GAAG,MAAM,GACpC,gBAAgB,CAsDlB"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/constants.ts
|
|
2
2
|
var DEFAULT_DELIMITERS = {
|
|
3
|
-
field: "|",
|
|
4
3
|
component: "^",
|
|
5
|
-
repetition: "~",
|
|
6
|
-
subcomponent: "&",
|
|
7
4
|
escape: "\\",
|
|
8
|
-
|
|
5
|
+
field: "|",
|
|
6
|
+
repetition: "~",
|
|
7
|
+
segment: "\r",
|
|
8
|
+
subcomponent: "&"
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
// src/utils.ts
|
|
@@ -34,10 +34,8 @@ function getByteLength(node) {
|
|
|
34
34
|
if ("value" in node) {
|
|
35
35
|
return Buffer.byteLength(node.value, "utf8");
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
0
|
|
40
|
-
);
|
|
37
|
+
const nameLength = node.type === "segment" ? Buffer.byteLength(node.name, "utf8") : 0;
|
|
38
|
+
return nameLength + node.children.reduce((total, child) => total + getByteLength(child), 0);
|
|
41
39
|
}
|
|
42
40
|
function getLength(node) {
|
|
43
41
|
if (!node) {
|
|
@@ -46,41 +44,20 @@ function getLength(node) {
|
|
|
46
44
|
if ("value" in node) {
|
|
47
45
|
return node.value.length;
|
|
48
46
|
}
|
|
49
|
-
|
|
47
|
+
const nameLength = node.type === "segment" ? node.name.length : 0;
|
|
48
|
+
return nameLength + node.children.reduce((total, child) => total + getLength(child), 0);
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
// src/constraints.ts
|
|
53
52
|
var OptionalityCode = {
|
|
54
|
-
Required: "R",
|
|
55
|
-
/**
|
|
56
|
-
* Required, but may be empty.
|
|
57
|
-
*
|
|
58
|
-
* @remarks
|
|
59
|
-
* The use of the RE usage code is qualified with the “if data is known”
|
|
60
|
-
* clause. The sender must interpret the clause as “the capability must always
|
|
61
|
-
* be supported, and data must always be sent if known”. To clarify, the
|
|
62
|
-
* sender does not determine whether the data should be sent; to be conformant
|
|
63
|
-
* to the rule, the data must be sent. There is a misconception where the RE
|
|
64
|
-
* usage is interpreted as “the capability must always be supported, and data
|
|
65
|
-
* may or may not be sent even when known based on a condition external to the
|
|
66
|
-
* profile specification”.
|
|
67
|
-
*
|
|
68
|
-
* The receiving application must process in a meaningful way the information
|
|
69
|
-
* conveyed by an element with an “RE” usage designation.
|
|
70
|
-
* The receiving application must process the message if the element is omitted
|
|
71
|
-
* (that is, an exception must not be raised because the element is missing). A
|
|
72
|
-
* receiving application must not raise an exception due to the presence of a
|
|
73
|
-
* required element.
|
|
74
|
-
*/
|
|
75
|
-
RequiredOrEmpty: "RE",
|
|
76
53
|
/**
|
|
77
|
-
*
|
|
54
|
+
* Backward Compatible
|
|
78
55
|
*
|
|
79
|
-
* There are no implementation requirements. The “
|
|
80
|
-
*
|
|
81
|
-
*
|
|
56
|
+
* There are no implementation requirements. The “B” usage indicates that the
|
|
57
|
+
* element is retained for backwards compatibility of the element. Another
|
|
58
|
+
* usage indicator may be assigned in a derived profile.
|
|
82
59
|
*/
|
|
83
|
-
|
|
60
|
+
BackwardCompatible: "B",
|
|
84
61
|
/**
|
|
85
62
|
* Undeclared / Conditional.
|
|
86
63
|
*
|
|
@@ -97,13 +74,37 @@ var OptionalityCode = {
|
|
|
97
74
|
*/
|
|
98
75
|
NotSupported: "X",
|
|
99
76
|
/**
|
|
100
|
-
*
|
|
77
|
+
* Optional
|
|
101
78
|
*
|
|
102
|
-
* There are no implementation requirements. The “
|
|
103
|
-
*
|
|
104
|
-
*
|
|
79
|
+
* There are no implementation requirements. The “O” usage designation is a
|
|
80
|
+
* placeholder indicating that the usage for this element has not yet been
|
|
81
|
+
* specified.
|
|
105
82
|
*/
|
|
106
|
-
|
|
83
|
+
Optional: "O",
|
|
84
|
+
/**
|
|
85
|
+
* Required
|
|
86
|
+
*/
|
|
87
|
+
Required: "R",
|
|
88
|
+
/**
|
|
89
|
+
* Required, but may be empty.
|
|
90
|
+
*
|
|
91
|
+
* The use of the RE usage code is qualified with the “if data is known”
|
|
92
|
+
* clause. The sender must interpret the clause as “the capability must always
|
|
93
|
+
* be supported, and data must always be sent if known”. To clarify, the
|
|
94
|
+
* sender does not determine whether the data should be sent; to be conformant
|
|
95
|
+
* to the rule, the data must be sent. There is a misconception where the RE
|
|
96
|
+
* usage is interpreted as “the capability must always be supported, and data
|
|
97
|
+
* may or may not be sent even when known based on a condition external to the
|
|
98
|
+
* profile specification”.
|
|
99
|
+
*
|
|
100
|
+
* The receiving application must process in a meaningful way the information
|
|
101
|
+
* conveyed by an element with an “RE” usage designation.
|
|
102
|
+
* The receiving application must process the message if the element is omitted
|
|
103
|
+
* (that is, an exception must not be raised because the element is missing). A
|
|
104
|
+
* receiving application must not raise an exception due to the presence of a
|
|
105
|
+
* required element.
|
|
106
|
+
*/
|
|
107
|
+
RequiredOrEmpty: "RE",
|
|
107
108
|
/**
|
|
108
109
|
* Withdrawn
|
|
109
110
|
*
|
|
@@ -123,24 +124,24 @@ function checkCardinality(node, min, max) {
|
|
|
123
124
|
const count = node?.children ? node.children.length : 0;
|
|
124
125
|
if (count < min) {
|
|
125
126
|
return {
|
|
126
|
-
ok: false,
|
|
127
127
|
error: {
|
|
128
|
+
actual: count,
|
|
128
129
|
code: "CARDINALITY_UNDERFLOW",
|
|
129
|
-
message: `has ${count} repetitions but requires at least ${min}`,
|
|
130
130
|
expected: min,
|
|
131
|
-
|
|
132
|
-
}
|
|
131
|
+
message: `has ${count} repetitions but requires at least ${min}`
|
|
132
|
+
},
|
|
133
|
+
ok: false
|
|
133
134
|
};
|
|
134
135
|
}
|
|
135
136
|
if (max !== "*" && count > max) {
|
|
136
137
|
return {
|
|
137
|
-
ok: false,
|
|
138
138
|
error: {
|
|
139
|
+
actual: count,
|
|
139
140
|
code: "CARDINALITY_OVERFLOW",
|
|
140
|
-
message: `has ${count} repetitions but allows at most ${max}`,
|
|
141
141
|
expected: max,
|
|
142
|
-
|
|
143
|
-
}
|
|
142
|
+
message: `has ${count} repetitions but allows at most ${max}`
|
|
143
|
+
},
|
|
144
|
+
ok: false
|
|
144
145
|
};
|
|
145
146
|
}
|
|
146
147
|
return { ok: true };
|
|
@@ -155,24 +156,24 @@ function checkLength(node, max, min = 0) {
|
|
|
155
156
|
const length = getLength(node);
|
|
156
157
|
if (length < min) {
|
|
157
158
|
return {
|
|
158
|
-
ok: false,
|
|
159
159
|
error: {
|
|
160
|
+
actual: length,
|
|
160
161
|
code: "LENGTH_UNDERFLOW",
|
|
161
|
-
message: `has length ${length} but requires at least ${min}`,
|
|
162
162
|
expected: min,
|
|
163
|
-
|
|
164
|
-
}
|
|
163
|
+
message: `has length ${length} but requires at least ${min}`
|
|
164
|
+
},
|
|
165
|
+
ok: false
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
if (length > max) {
|
|
168
169
|
return {
|
|
169
|
-
ok: false,
|
|
170
170
|
error: {
|
|
171
|
+
actual: length,
|
|
171
172
|
code: "LENGTH_OVERFLOW",
|
|
172
|
-
message: `has length ${length} but allows at most ${max}`,
|
|
173
173
|
expected: max,
|
|
174
|
-
|
|
175
|
-
}
|
|
174
|
+
message: `has length ${length} but allows at most ${max}`
|
|
175
|
+
},
|
|
176
|
+
ok: false
|
|
176
177
|
};
|
|
177
178
|
}
|
|
178
179
|
return { ok: true };
|
|
@@ -180,48 +181,52 @@ function checkLength(node, max, min = 0) {
|
|
|
180
181
|
function checkOptionality(node, optionality) {
|
|
181
182
|
const code = optionality.toUpperCase();
|
|
182
183
|
switch (code) {
|
|
183
|
-
case OptionalityCode.Required:
|
|
184
|
+
case OptionalityCode.Required: {
|
|
184
185
|
if (!node) {
|
|
185
186
|
return {
|
|
186
|
-
ok: false,
|
|
187
187
|
error: {
|
|
188
188
|
code: "MISSING",
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
189
|
+
expected: "R",
|
|
190
|
+
message: "is required but missing"
|
|
191
|
+
},
|
|
192
|
+
ok: false
|
|
192
193
|
};
|
|
193
194
|
}
|
|
194
195
|
if (isEmptyNode(node)) {
|
|
195
196
|
return {
|
|
196
|
-
ok: false,
|
|
197
197
|
error: {
|
|
198
198
|
code: "EMPTY",
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
199
|
+
expected: "R",
|
|
200
|
+
message: "is required but empty"
|
|
201
|
+
},
|
|
202
|
+
ok: false
|
|
202
203
|
};
|
|
203
204
|
}
|
|
204
205
|
return { ok: true };
|
|
205
|
-
|
|
206
|
+
}
|
|
207
|
+
case OptionalityCode.NotSupported: {
|
|
206
208
|
if (node && !isEmptyNode(node)) {
|
|
207
209
|
return {
|
|
208
|
-
ok: false,
|
|
209
210
|
error: {
|
|
210
211
|
code: "UNEXPECTED_CONTENT",
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
212
|
+
expected: "X",
|
|
213
|
+
message: "is not supported but present"
|
|
214
|
+
},
|
|
215
|
+
ok: false
|
|
214
216
|
};
|
|
215
217
|
}
|
|
216
218
|
return { ok: true };
|
|
219
|
+
}
|
|
217
220
|
case OptionalityCode.RequiredOrEmpty:
|
|
218
221
|
case OptionalityCode.Optional:
|
|
219
222
|
case OptionalityCode.Conditional:
|
|
220
223
|
case OptionalityCode.BackwardCompatible:
|
|
221
|
-
case OptionalityCode.Withdrawn:
|
|
224
|
+
case OptionalityCode.Withdrawn: {
|
|
222
225
|
return { ok: true };
|
|
223
|
-
|
|
226
|
+
}
|
|
227
|
+
default: {
|
|
224
228
|
return { ok: true };
|
|
229
|
+
}
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
232
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/constraints.ts"],"sourcesContent":["// -------------\n// Delimiters\n// -------------\n\nexport const DEFAULT_DELIMITERS = {\n field: \"|\",\n component: \"^\",\n repetition: \"~\",\n subcomponent: \"&\",\n escape: \"\\\\\",\n segment: \"\\r\",\n};\n","import type { Nodes } from \"@rethinkhealth/hl7v2-ast\";\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// Byte Length\n// -------------\n\n/**\n * Calculate the byte length of any HL7v2 AST node.\n *\n * For literal nodes (Subcomponent, SegmentHeader), returns the UTF-8 byte length of the value.\n * For parent nodes, recursively calculates the length of all children. Delimiters are NOT included.\n *\n * @param node - The HL7v2 AST node to measure\n * @returns The total byte length of the node content\n *\n * @example\n * ```ts\n * const field: Field = { type: \"field\", children: [...] };\n * const length = getByteLength(field); // e.g., 42\n * ```\n */\nexport function getByteLength(node: Nodes | null | undefined): number {\n if (!node) {\n return 0;\n }\n\n if (\"value\" in node) {\n return Buffer.byteLength(node.value, \"utf8\");\n }\n\n return node.children.reduce(\n (total, child) => total + getByteLength(child),\n 0\n );\n}\n\n// -------------\n// Length\n// -------------\n\n/**\n * Calculate the string length of any HL7v2 AST node.\n *\n * For literal nodes (Subcomponent, SegmentHeader), returns `value.length`.\n * For parent nodes, recursively calculates the length of all children. Delimiters are NOT included.\n *\n * Note: Returns JavaScript string length (UTF-16 code units). For UTF-8 byte\n * length (e.g., for wire protocol), use `getByteLength` instead.\n *\n * @param node - The HL7v2 AST node to measure\n * @returns The total string length of the node content\n *\n * @example\n * ```ts\n * const field: Field = { type: \"field\", children: [...] };\n * const length = getLength(field); // e.g., 42\n * ```\n */\nexport function getLength(node: Nodes | null | undefined): number {\n if (!node) {\n return 0;\n }\n\n if (\"value\" in node) {\n return node.value.length;\n }\n\n return node.children.reduce((total, child) => total + getLength(child), 0);\n}\n","import type { Field, Nodes } from \"@rethinkhealth/hl7v2-ast\";\nimport { getLength, isEmptyNode } from \"./utils\";\n\nexport type ValidationErrorCode =\n // Constraints\n | \"MISSING\"\n | \"EMPTY\"\n | \"UNEXPECTED_CONTENT\"\n | \"CARDINALITY_UNDERFLOW\"\n | \"CARDINALITY_OVERFLOW\"\n | \"LENGTH_UNDERFLOW\"\n | \"LENGTH_OVERFLOW\"\n // Value Sets\n | \"VALUE_NOT_IN_TABLE\";\n\nexport type ValidationError = {\n code: ValidationErrorCode;\n message: string;\n expected?: string | number | Array<string | number>;\n actual?: string | number | Array<string | number>;\n};\n\nexport type ValidationSuccess = {\n ok: true;\n};\n\nexport type ValidationFailure = {\n ok: false;\n error: ValidationError;\n};\n\nexport type ValidationResult = ValidationSuccess | ValidationFailure;\n\nexport const OptionalityCode = {\n Required: \"R\",\n /**\n * Required, but may be empty.\n *\n * @remarks\n * The use of the RE usage code is qualified with the “if data is known”\n * clause. The sender must interpret the clause as “the capability must always\n * be supported, and data must always be sent if known”. To clarify, the\n * sender does not determine whether the data should be sent; to be conformant\n * to the rule, the data must be sent. There is a misconception where the RE\n * usage is interpreted as “the capability must always be supported, and data\n * may or may not be sent even when known based on a condition external to the\n * profile specification”.\n *\n * The receiving application must process in a meaningful way the information\n * conveyed by an element with an “RE” usage designation.\n * The receiving application must process the message if the element is omitted\n * (that is, an exception must not be raised because the element is missing). A\n * receiving application must not raise an exception due to the presence of a\n * required element.\n */\n RequiredOrEmpty: \"RE\",\n /**\n * Optional\n *\n * There are no implementation requirements. The “O” usage designation is a\n * placeholder indicating that the usage for this element has not yet been\n * specified.\n */\n Optional: \"O\",\n /**\n * Undeclared / Conditional.\n *\n * There are no implementation requirements. The “C” usage designation is a\n * placeholder indicating that the usage for this element has not yet been\n * specified.\n */\n Conditional: \"C\",\n /**\n * Not Supported.\n *\n * There are no implementation requirements. The application must not value an\n * element with an “X” usage designation.\n */\n NotSupported: \"X\",\n /**\n * Backward Compatible\n *\n * There are no implementation requirements. The “B” usage indicates that the\n * element is retained for backwards compatibility of the element. Another\n * usage indicator may be assigned in a derived profile.\n */\n BackwardCompatible: \"B\",\n /**\n * Withdrawn\n *\n * The element has been withdrawn from the standard. There are no\n * implementation requirements. The application must not value an element with\n * a \"W\" usage designation.\n */\n Withdrawn: \"W\",\n} as const;\n\nexport type OptionalityCode =\n (typeof OptionalityCode)[keyof typeof OptionalityCode];\n\n/**\n * Checks if a field satisfies the cardinality constraint.\n */\nexport function checkCardinality(\n node: Field | undefined,\n min: number,\n max: number | \"*\"\n): ValidationResult {\n if (min < 0 || (max !== \"*\" && max < 0)) {\n throw new Error(\"Min and max lengths must be non-negative\");\n }\n\n if (max !== \"*\" && min > max) {\n throw new Error(\"Min length cannot be greater than max length\");\n }\n\n const count = node?.children ? node.children.length : 0;\n\n if (count < min) {\n return {\n ok: false,\n error: {\n code: \"CARDINALITY_UNDERFLOW\",\n message: `has ${count} repetitions but requires at least ${min}`,\n expected: min,\n actual: count,\n },\n };\n }\n\n if (max !== \"*\" && count > max) {\n return {\n ok: false,\n error: {\n code: \"CARDINALITY_OVERFLOW\",\n message: `has ${count} repetitions but allows at most ${max}`,\n expected: max,\n actual: count,\n },\n };\n }\n\n return { ok: true };\n}\n\n/**\n * Checks if a node satisfies the length constraint.\n */\nexport function checkLength(\n node: Nodes | undefined,\n max: number,\n min = 0\n): ValidationResult {\n if (min < 0 || max < 0) {\n throw new Error(\"Min and max lengths must be non-negative\");\n }\n\n if (min > max) {\n throw new Error(\"Min length cannot be greater than max length\");\n }\n\n const length = getLength(node);\n\n if (length < min) {\n return {\n ok: false,\n error: {\n code: \"LENGTH_UNDERFLOW\",\n message: `has length ${length} but requires at least ${min}`,\n expected: min,\n actual: length,\n },\n };\n }\n\n if (length > max) {\n return {\n ok: false,\n error: {\n code: \"LENGTH_OVERFLOW\",\n message: `has length ${length} but allows at most ${max}`,\n expected: max,\n actual: length,\n },\n };\n }\n\n return { ok: true };\n}\n\n/**\n * Checks if a node satisfies the optionality constraint.\n */\nexport function checkOptionality(\n node: Nodes | undefined,\n optionality: OptionalityCode | string\n): ValidationResult {\n const code = optionality.toUpperCase();\n\n switch (code) {\n case OptionalityCode.Required:\n if (!node) {\n return {\n ok: false,\n error: {\n code: \"MISSING\",\n message: \"is required but missing\",\n expected: \"R\",\n },\n };\n }\n if (isEmptyNode(node)) {\n return {\n ok: false,\n error: {\n code: \"EMPTY\",\n message: \"is required but empty\",\n expected: \"R\",\n },\n };\n }\n return { ok: true };\n\n case OptionalityCode.NotSupported:\n if (node && !isEmptyNode(node)) {\n return {\n ok: false,\n error: {\n code: \"UNEXPECTED_CONTENT\",\n message: \"is not supported but present\",\n expected: \"X\",\n },\n };\n }\n return { ok: true };\n\n case OptionalityCode.RequiredOrEmpty:\n case OptionalityCode.Optional:\n case OptionalityCode.Conditional:\n case OptionalityCode.BackwardCompatible:\n case OptionalityCode.Withdrawn:\n return { ok: true };\n\n default:\n return { ok: true };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/utils.ts","../src/constraints.ts"],"sourcesContent":["// -------------\n// Delimiters\n// -------------\n\nexport const DEFAULT_DELIMITERS = {\n component: \"^\",\n escape: \"\\\\\",\n field: \"|\",\n repetition: \"~\",\n segment: \"\\r\",\n subcomponent: \"&\",\n};\n","import type { Nodes } from \"@rethinkhealth/hl7v2-ast\";\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// Byte Length\n// -------------\n\n/**\n * Calculate the byte length of any HL7v2 AST node.\n *\n * For literal nodes (Subcomponent), returns the UTF-8 byte length of the value.\n * For parent nodes, recursively calculates the length of all children. Delimiters are NOT included.\n *\n * @param node - The HL7v2 AST node to measure\n * @returns The total byte length of the node content\n *\n * @example\n * ```ts\n * const field: Field = { type: \"field\", children: [...] };\n * const length = getByteLength(field); // e.g., 42\n * ```\n */\nexport function getByteLength(node?: Nodes | null | undefined): number {\n if (!node) {\n return 0;\n }\n\n if (\"value\" in node) {\n return Buffer.byteLength(node.value, \"utf8\");\n }\n\n const nameLength =\n node.type === \"segment\" ? Buffer.byteLength(node.name, \"utf8\") : 0;\n\n return (\n nameLength +\n node.children.reduce((total, child) => total + getByteLength(child), 0)\n );\n}\n\n// -------------\n// Length\n// -------------\n\n/**\n * Calculate the string length of any HL7v2 AST node.\n *\n * For literal nodes (Subcomponent), returns `value.length`.\n * For parent nodes, recursively calculates the length of all children. Delimiters are NOT included.\n *\n * Note: Returns JavaScript string length (UTF-16 code units). For UTF-8 byte\n * length (e.g., for wire protocol), use `getByteLength` instead.\n *\n * @param node - The HL7v2 AST node to measure\n * @returns The total string length of the node content\n *\n * @example\n * ```ts\n * const field: Field = { type: \"field\", children: [...] };\n * const length = getLength(field); // e.g., 42\n * ```\n */\nexport function getLength(node?: Nodes | null | undefined): number {\n if (!node) {\n return 0;\n }\n\n if (\"value\" in node) {\n return node.value.length;\n }\n\n const nameLength = node.type === \"segment\" ? node.name.length : 0;\n\n return (\n nameLength +\n node.children.reduce((total, child) => total + getLength(child), 0)\n );\n}\n","import type { Field, Nodes } from \"@rethinkhealth/hl7v2-ast\";\n\nimport { getLength, isEmptyNode } from \"./utils\";\n\nexport type ValidationErrorCode =\n // Constraints\n | \"MISSING\"\n | \"EMPTY\"\n | \"UNEXPECTED_CONTENT\"\n | \"CARDINALITY_UNDERFLOW\"\n | \"CARDINALITY_OVERFLOW\"\n | \"LENGTH_UNDERFLOW\"\n | \"LENGTH_OVERFLOW\"\n // Value Sets\n | \"VALUE_NOT_IN_TABLE\";\n\nexport interface ValidationError {\n code: ValidationErrorCode;\n message: string;\n expected?: string | number | Array<string | number>;\n actual?: string | number | Array<string | number>;\n}\n\nexport interface ValidationSuccess {\n ok: true;\n}\n\nexport interface ValidationFailure {\n ok: false;\n error: ValidationError;\n}\n\nexport type ValidationResult = ValidationSuccess | ValidationFailure;\n\nexport const OptionalityCode = {\n /**\n * Backward Compatible\n *\n * There are no implementation requirements. The “B” usage indicates that the\n * element is retained for backwards compatibility of the element. Another\n * usage indicator may be assigned in a derived profile.\n */\n BackwardCompatible: \"B\",\n\n /**\n * Undeclared / Conditional.\n *\n * There are no implementation requirements. The “C” usage designation is a\n * placeholder indicating that the usage for this element has not yet been\n * specified.\n */\n Conditional: \"C\",\n\n /**\n * Not Supported.\n *\n * There are no implementation requirements. The application must not value an\n * element with an “X” usage designation.\n */\n NotSupported: \"X\",\n\n /**\n * Optional\n *\n * There are no implementation requirements. The “O” usage designation is a\n * placeholder indicating that the usage for this element has not yet been\n * specified.\n */\n Optional: \"O\",\n\n /**\n * Required\n */\n Required: \"R\",\n\n /**\n * Required, but may be empty.\n *\n * The use of the RE usage code is qualified with the “if data is known”\n * clause. The sender must interpret the clause as “the capability must always\n * be supported, and data must always be sent if known”. To clarify, the\n * sender does not determine whether the data should be sent; to be conformant\n * to the rule, the data must be sent. There is a misconception where the RE\n * usage is interpreted as “the capability must always be supported, and data\n * may or may not be sent even when known based on a condition external to the\n * profile specification”.\n *\n * The receiving application must process in a meaningful way the information\n * conveyed by an element with an “RE” usage designation.\n * The receiving application must process the message if the element is omitted\n * (that is, an exception must not be raised because the element is missing). A\n * receiving application must not raise an exception due to the presence of a\n * required element.\n */\n RequiredOrEmpty: \"RE\",\n\n /**\n * Withdrawn\n *\n * The element has been withdrawn from the standard. There are no\n * implementation requirements. The application must not value an element with\n * a \"W\" usage designation.\n */\n Withdrawn: \"W\",\n} as const;\n\nexport type OptionalityCode =\n (typeof OptionalityCode)[keyof typeof OptionalityCode];\n\n/**\n * Checks if a field satisfies the cardinality constraint.\n */\nexport function checkCardinality(\n node: Field | undefined,\n min: number,\n max: number | \"*\"\n): ValidationResult {\n if (min < 0 || (max !== \"*\" && max < 0)) {\n throw new Error(\"Min and max lengths must be non-negative\");\n }\n\n if (max !== \"*\" && min > max) {\n throw new Error(\"Min length cannot be greater than max length\");\n }\n\n const count = node?.children ? node.children.length : 0;\n\n if (count < min) {\n return {\n error: {\n actual: count,\n code: \"CARDINALITY_UNDERFLOW\",\n expected: min,\n message: `has ${count} repetitions but requires at least ${min}`,\n },\n ok: false,\n };\n }\n\n if (max !== \"*\" && count > max) {\n return {\n error: {\n actual: count,\n code: \"CARDINALITY_OVERFLOW\",\n expected: max,\n message: `has ${count} repetitions but allows at most ${max}`,\n },\n ok: false,\n };\n }\n\n return { ok: true };\n}\n\n/**\n * Checks if a node satisfies the length constraint.\n */\nexport function checkLength(\n node: Nodes | undefined,\n max: number,\n min = 0\n): ValidationResult {\n if (min < 0 || max < 0) {\n throw new Error(\"Min and max lengths must be non-negative\");\n }\n\n if (min > max) {\n throw new Error(\"Min length cannot be greater than max length\");\n }\n\n const length = getLength(node);\n\n if (length < min) {\n return {\n error: {\n actual: length,\n code: \"LENGTH_UNDERFLOW\",\n expected: min,\n message: `has length ${length} but requires at least ${min}`,\n },\n ok: false,\n };\n }\n\n if (length > max) {\n return {\n error: {\n actual: length,\n code: \"LENGTH_OVERFLOW\",\n expected: max,\n message: `has length ${length} but allows at most ${max}`,\n },\n ok: false,\n };\n }\n\n return { ok: true };\n}\n\n/**\n * Checks if a node satisfies the optionality constraint.\n */\nexport function checkOptionality(\n node: Nodes | undefined,\n optionality: OptionalityCode | string\n): ValidationResult {\n const code = optionality.toUpperCase();\n\n switch (code) {\n case OptionalityCode.Required: {\n if (!node) {\n return {\n error: {\n code: \"MISSING\",\n expected: \"R\",\n message: \"is required but missing\",\n },\n ok: false,\n };\n }\n if (isEmptyNode(node)) {\n return {\n error: {\n code: \"EMPTY\",\n expected: \"R\",\n message: \"is required but empty\",\n },\n ok: false,\n };\n }\n return { ok: true };\n }\n\n case OptionalityCode.NotSupported: {\n if (node && !isEmptyNode(node)) {\n return {\n error: {\n code: \"UNEXPECTED_CONTENT\",\n expected: \"X\",\n message: \"is not supported but present\",\n },\n ok: false,\n };\n }\n return { ok: true };\n }\n\n case OptionalityCode.RequiredOrEmpty:\n case OptionalityCode.Optional:\n case OptionalityCode.Conditional:\n case OptionalityCode.BackwardCompatible:\n case OptionalityCode.Withdrawn: {\n return { ok: true };\n }\n\n default: {\n return { ok: true };\n }\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/utils.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import type { Nodes } from "@rethinkhealth/hl7v2-ast";
|
|
|
2
2
|
/**
|
|
3
3
|
* Utility: check if a node is semantically empty
|
|
4
4
|
*/
|
|
5
|
-
export declare function isEmptyNode(node
|
|
5
|
+
export declare function isEmptyNode(node?: Nodes | null | undefined): boolean;
|
|
6
6
|
/**
|
|
7
7
|
* Calculate the byte length of any HL7v2 AST node.
|
|
8
8
|
*
|
|
9
|
-
* For literal nodes (Subcomponent
|
|
9
|
+
* For literal nodes (Subcomponent), returns the UTF-8 byte length of the value.
|
|
10
10
|
* For parent nodes, recursively calculates the length of all children. Delimiters are NOT included.
|
|
11
11
|
*
|
|
12
12
|
* @param node - The HL7v2 AST node to measure
|
|
@@ -18,11 +18,11 @@ export declare function isEmptyNode(node: Nodes | null | undefined): boolean;
|
|
|
18
18
|
* const length = getByteLength(field); // e.g., 42
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
export declare function getByteLength(node
|
|
21
|
+
export declare function getByteLength(node?: Nodes | null | undefined): number;
|
|
22
22
|
/**
|
|
23
23
|
* Calculate the string length of any HL7v2 AST node.
|
|
24
24
|
*
|
|
25
|
-
* For literal nodes (Subcomponent
|
|
25
|
+
* For literal nodes (Subcomponent), returns `value.length`.
|
|
26
26
|
* For parent nodes, recursively calculates the length of all children. Delimiters are NOT included.
|
|
27
27
|
*
|
|
28
28
|
* Note: Returns JavaScript string length (UTF-16 code units). For UTF-8 byte
|
|
@@ -37,5 +37,5 @@ export declare function getByteLength(node: Nodes | null | undefined): number;
|
|
|
37
37
|
* const length = getLength(field); // e.g., 42
|
|
38
38
|
* ```
|
|
39
39
|
*/
|
|
40
|
-
export declare function getLength(node
|
|
40
|
+
export declare function getLength(node?: Nodes | null | undefined): number;
|
|
41
41
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAMtD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAMtD;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CA2BpE;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAgBrE;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAejE"}
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rethinkhealth/hl7v2-utils",
|
|
3
|
+
"version": "0.5.0",
|
|
3
4
|
"description": "hl7v2 utilities",
|
|
4
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"health",
|
|
7
|
+
"healthcare",
|
|
8
|
+
"hl7",
|
|
9
|
+
"hl7v2",
|
|
10
|
+
"nodejs",
|
|
11
|
+
"typescript"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://www.rethinkhealth.io/hl7v2/docs",
|
|
5
14
|
"license": "MIT",
|
|
6
15
|
"author": {
|
|
7
16
|
"name": "Melek Somai",
|
|
8
17
|
"email": "melek@rethinkhealth.io"
|
|
9
18
|
},
|
|
10
|
-
"
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
19
|
+
"repository": "rethinkhealth/hl7v2.git",
|
|
12
20
|
"files": [
|
|
13
21
|
"dist"
|
|
14
22
|
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
15
25
|
"exports": {
|
|
16
26
|
".": "./dist/index.js"
|
|
17
27
|
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
18
31
|
"devDependencies": {
|
|
19
32
|
"@types/node": "24.10.1",
|
|
20
33
|
"@types/unist": "^3.0.3",
|
|
21
|
-
"@vitest/coverage-v8": "4.0.
|
|
34
|
+
"@vitest/coverage-v8": "4.0.18",
|
|
22
35
|
"tsup": "8.5.1",
|
|
23
36
|
"typescript": "^5.9.3",
|
|
24
37
|
"unist-builder": "^4.0.0",
|
|
25
38
|
"vfile": "^6.0.3",
|
|
26
39
|
"vitest": "4.0.14",
|
|
27
|
-
"@rethinkhealth/hl7v2-ast": "0.
|
|
40
|
+
"@rethinkhealth/hl7v2-ast": "0.5.0",
|
|
28
41
|
"@rethinkhealth/testing": "0.0.2",
|
|
29
42
|
"@rethinkhealth/tsconfig": "0.0.1"
|
|
30
43
|
},
|
|
31
44
|
"engines": {
|
|
32
45
|
"node": ">=18"
|
|
33
46
|
},
|
|
34
|
-
"repository": "rethinkhealth/hl7v2.git",
|
|
35
|
-
"homepage": "https://www.rethinkhealth.io/hl7v2/docs",
|
|
36
|
-
"keywords": [
|
|
37
|
-
"health",
|
|
38
|
-
"healthcare",
|
|
39
|
-
"hl7",
|
|
40
|
-
"hl7v2",
|
|
41
|
-
"nodejs",
|
|
42
|
-
"typescript"
|
|
43
|
-
],
|
|
44
47
|
"packageManager": "pnpm@10.14.0",
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public"
|
|
47
|
-
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"build": "tsup && tsc --emitDeclarationOnly",
|
|
50
50
|
"check-types": "tsc --noEmit",
|