@glion/lint-profile-required-fields 0.23.0 → 0.25.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 +19 -15
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -20
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @glion/lint-profile-required-fields
|
|
2
2
|
|
|
3
|
-
Lint rule
|
|
3
|
+
Lint rule that reports a required field that is missing or empty.
|
|
4
4
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
7
|
-
Flags
|
|
7
|
+
Flags a field that the HL7v2 standard for the message's version marks `R` (required) when it is missing or empty. Segments the standard does not define, such as Z-segments, are skipped. It needs the plugin `@glion/annotate-profile-context` to run before it; `@glion/preset-lint-profile-recommended` includes it.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -16,21 +16,22 @@ npm install @glion/lint-profile-required-fields
|
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
18
|
import { hl7v2AnnotateProfileContext } from "@glion/annotate-profile-context";
|
|
19
|
-
import { hl7v2Parser } from "@glion/parser";
|
|
20
19
|
import hl7v2LintRequiredFields from "@glion/lint-profile-required-fields";
|
|
20
|
+
import { parseHL7v2 } from "@glion/parser";
|
|
21
21
|
import { unified } from "unified";
|
|
22
|
+
import { VFile } from "vfile";
|
|
22
23
|
import { reporter } from "vfile-reporter";
|
|
23
24
|
|
|
24
25
|
const message = [
|
|
25
26
|
"MSH|^~\\&|SENDER|FAC|RECV|RFAC|20250601120000||ADT^A01^ADT_A01|MSG00001|P|2.5",
|
|
26
|
-
"PID|1
|
|
27
|
+
"PID|1||||DOE^JANE||19800101|F",
|
|
27
28
|
].join("\r");
|
|
28
29
|
|
|
29
|
-
const file =
|
|
30
|
-
|
|
30
|
+
const file = new VFile();
|
|
31
|
+
await unified()
|
|
31
32
|
.use(hl7v2AnnotateProfileContext)
|
|
32
33
|
.use(hl7v2LintRequiredFields)
|
|
33
|
-
.
|
|
34
|
+
.run(parseHL7v2(message), file);
|
|
34
35
|
|
|
35
36
|
console.error(reporter([file]));
|
|
36
37
|
```
|
|
@@ -39,9 +40,7 @@ console.error(reporter([file]));
|
|
|
39
40
|
|
|
40
41
|
### `unified().use(hl7v2LintRequiredFields)`
|
|
41
42
|
|
|
42
|
-
A `unified` lint rule plugin. Takes no options.
|
|
43
|
-
|
|
44
|
-
The plugin reads `file.data.profile` (populated by `@glion/annotate-profile-context`). If no profile context is present the rule exits silently. For each segment, it iterates `fieldDef.requiredSequences` and checks that the corresponding field at that 1-based position is present and non-empty.
|
|
43
|
+
A `unified` lint rule plugin. Takes no options. Reports one message for each required field that is missing or empty.
|
|
45
44
|
|
|
46
45
|
```ts
|
|
47
46
|
import type { Plugin } from "unified";
|
|
@@ -53,11 +52,11 @@ export default hl7v2LintRequiredFields;
|
|
|
53
52
|
|
|
54
53
|
## What it checks
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
Every field marked `R` in the message's version is present and not empty. For example, in v2.5 `PID-3` (Patient Identifier List) and `PID-5` (Patient Name) are required.
|
|
57
56
|
|
|
58
57
|
### Valid
|
|
59
58
|
|
|
60
|
-
`PID-3`
|
|
59
|
+
`PID-3` and `PID-5` both have values:
|
|
61
60
|
|
|
62
61
|
```hl7
|
|
63
62
|
MSH|^~\&|SENDER|FAC|RECV|RFAC|20250601120000||ADT^A01^ADT_A01|MSG00001|P|2.5
|
|
@@ -66,7 +65,7 @@ PID|1||PATID1234^^^HOSP^MR||DOE^JANE||19800101|F
|
|
|
66
65
|
|
|
67
66
|
### Invalid
|
|
68
67
|
|
|
69
|
-
`PID-3` is empty
|
|
68
|
+
`PID-3` is empty:
|
|
70
69
|
|
|
71
70
|
```hl7
|
|
72
71
|
MSH|^~\&|SENDER|FAC|RECV|RFAC|20250601120000||ADT^A01^ADT_A01|MSG00001|P|2.5
|
|
@@ -76,10 +75,15 @@ PID|1||||DOE^JANE||19800101|F
|
|
|
76
75
|
Reported message:
|
|
77
76
|
|
|
78
77
|
```
|
|
79
|
-
|
|
78
|
+
Field `PID-3` (Patient Identifier List) is empty; it is required.
|
|
80
79
|
```
|
|
81
80
|
|
|
82
|
-
|
|
81
|
+
A segment that ends before a required field, such as `PID|1`, produces one message per required field it does not reach:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
Field `PID-3` (Patient Identifier List) is not present; it is required.
|
|
85
|
+
Field `PID-5` (Patient Name) is not present; it is required.
|
|
86
|
+
```
|
|
83
87
|
|
|
84
88
|
## Part of Glion
|
|
85
89
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,9 @@ import type { Root } from "@glion/ast";
|
|
|
2
2
|
/**
|
|
3
3
|
* Lint rule that validates required fields per HL7v2 profile.
|
|
4
4
|
*
|
|
5
|
-
* For each segment, checks that
|
|
6
|
-
*
|
|
5
|
+
* For each segment, checks that every field whose optionality is `R` is
|
|
6
|
+
* present and non-empty. Fields whose optionality is `C`, `B`, or `W` are not
|
|
7
|
+
* checked.
|
|
7
8
|
*
|
|
8
9
|
* Segments without a known profile (e.g., Z-segments) are silently skipped.
|
|
9
10
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAYvC;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,uBAAuB,mDAyC5B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,16 @@ import { SKIP, visit } from "@glion/util-visit";
|
|
|
2
2
|
import { isEmptyNode } from "@glion/utils";
|
|
3
3
|
import { lintRule } from "unified-lint-rule";
|
|
4
4
|
//#region src/index.ts
|
|
5
|
+
const messages = {
|
|
6
|
+
absentField: (field, name) => `Field \`${field}\` (${name}) is not present; it is required.`,
|
|
7
|
+
emptyField: (field, name) => `Field \`${field}\` (${name}) is empty; it is required.`
|
|
8
|
+
};
|
|
5
9
|
/**
|
|
6
10
|
* Lint rule that validates required fields per HL7v2 profile.
|
|
7
11
|
*
|
|
8
|
-
* For each segment, checks that
|
|
9
|
-
*
|
|
12
|
+
* For each segment, checks that every field whose optionality is `R` is
|
|
13
|
+
* present and non-empty. Fields whose optionality is `C`, `B`, or `W` are not
|
|
14
|
+
* checked.
|
|
10
15
|
*
|
|
11
16
|
* Segments without a known profile (e.g., Z-segments) are silently skipped.
|
|
12
17
|
*
|
|
@@ -18,26 +23,31 @@ import { lintRule } from "unified-lint-rule";
|
|
|
18
23
|
* unified().use(hl7v2AnnotateProfileContext).use(hl7v2LintRequiredFields);
|
|
19
24
|
* ```;
|
|
20
25
|
*/
|
|
21
|
-
const hl7v2LintRequiredFields = lintRule({
|
|
26
|
+
const hl7v2LintRequiredFields = lintRule({
|
|
27
|
+
origin: "hl7v2-lint:required-fields",
|
|
28
|
+
url: "https://github.com/rethinkhealth/glion/tree/main/packages/lint-profile-required-fields#readme"
|
|
29
|
+
}, (tree, file) => {
|
|
22
30
|
const ctx = file.data.profile;
|
|
23
31
|
if (!ctx) return;
|
|
24
|
-
visit(tree, "segment", (
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
27
|
-
for (const sequence of
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
visit(tree, "segment", (segment, segmentAncestors) => {
|
|
33
|
+
const fields = ctx.fields.get(segment.name);
|
|
34
|
+
if (!fields) return SKIP;
|
|
35
|
+
for (const sequence of fields.requiredSequences) {
|
|
36
|
+
const id = `${segment.name}-${sequence}`;
|
|
37
|
+
const name = fields.bySequence.get(sequence)?.name ?? "";
|
|
38
|
+
const field = segment.children[sequence - 1];
|
|
39
|
+
if (!field) file.message(messages.absentField(id, name), {
|
|
40
|
+
ancestors: [...segmentAncestors, segment],
|
|
41
|
+
place: segment.position
|
|
42
|
+
});
|
|
43
|
+
else if (isEmptyNode(field)) file.message(messages.emptyField(id, name), {
|
|
44
|
+
ancestors: [
|
|
45
|
+
...segmentAncestors,
|
|
46
|
+
segment,
|
|
47
|
+
field
|
|
48
|
+
],
|
|
49
|
+
place: field.position
|
|
50
|
+
});
|
|
41
51
|
}
|
|
42
52
|
return SKIP;
|
|
43
53
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// oxlint-disable-next-line no-unused-vars -- triggers VFile DataMap augmentation\nimport type { ProfileContext } from \"@glion/annotate-profile-context\";\nimport type { Root } from \"@glion/ast\";\nimport { SKIP, visit } from \"@glion/util-visit\";\nimport { isEmptyNode } from \"@glion/utils\";\nimport { lintRule } from \"unified-lint-rule\";\n\n/**\n * Lint rule that validates required fields per HL7v2 profile.\n *\n * For each segment, checks that
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["// oxlint-disable-next-line no-unused-vars -- triggers VFile DataMap augmentation\nimport type { ProfileContext } from \"@glion/annotate-profile-context\";\nimport type { Root } from \"@glion/ast\";\nimport { SKIP, visit } from \"@glion/util-visit\";\nimport { isEmptyNode } from \"@glion/utils\";\nimport { lintRule } from \"unified-lint-rule\";\n\nconst messages = {\n absentField: (field: string, name: string) =>\n `Field \\`${field}\\` (${name}) is not present; it is required.`,\n emptyField: (field: string, name: string) =>\n `Field \\`${field}\\` (${name}) is empty; it is required.`,\n} as const;\n\n/**\n * Lint rule that validates required fields per HL7v2 profile.\n *\n * For each segment, checks that every field whose optionality is `R` is\n * present and non-empty. Fields whose optionality is `C`, `B`, or `W` are not\n * checked.\n *\n * Segments without a known profile (e.g., Z-segments) are silently skipped.\n *\n * Requires `@glion/annotate-profile-context` to run first\n * so that `file.data.fields` is populated.\n *\n * @example\n * ```typescript\n * unified().use(hl7v2AnnotateProfileContext).use(hl7v2LintRequiredFields);\n * ```;\n */\nconst hl7v2LintRequiredFields = lintRule<Root>(\n {\n origin: \"hl7v2-lint:required-fields\",\n url: \"https://github.com/rethinkhealth/glion/tree/main/packages/lint-profile-required-fields#readme\",\n },\n (tree, file) => {\n const ctx = file.data.profile;\n if (!ctx) {\n return;\n }\n\n visit(tree, \"segment\", (segment, segmentAncestors) => {\n const fields = ctx.fields.get(segment.name);\n if (!fields) {\n return SKIP;\n }\n\n // Loop over the profile's required fields, not the message's: a field\n // the segment ends before has no node to visit.\n for (const sequence of fields.requiredSequences) {\n const id = `${segment.name}-${sequence}`;\n const name = fields.bySequence.get(sequence)?.name ?? \"\";\n const field = segment.children[sequence - 1];\n\n if (!field) {\n // Reported on the segment, the closest node the message has.\n file.message(messages.absentField(id, name), {\n ancestors: [...segmentAncestors, segment],\n place: segment.position,\n });\n } else if (isEmptyNode(field)) {\n file.message(messages.emptyField(id, name), {\n ancestors: [...segmentAncestors, segment, field],\n place: field.position,\n });\n }\n }\n\n return SKIP;\n });\n }\n);\n\nexport default hl7v2LintRequiredFields;\n"],"mappings":";;;;AAOA,MAAM,WAAW;CACf,cAAc,OAAe,SAC3B,WAAW,MAAM,MAAM,KAAK;CAC9B,aAAa,OAAe,SAC1B,WAAW,MAAM,MAAM,KAAK;CAC/B;;;;;;;;;;;;;;;;;;AAmBD,MAAM,0BAA0B,SAC9B;CACE,QAAQ;CACR,KAAK;CACN,GACA,MAAM,SAAS;CACd,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,CAAC,IACH;AAGF,OAAM,MAAM,YAAY,SAAS,qBAAqB;EACpD,MAAM,SAAS,IAAI,OAAO,IAAI,QAAQ,KAAK;AAC3C,MAAI,CAAC,OACH,QAAO;AAKT,OAAK,MAAM,YAAY,OAAO,mBAAmB;GAC/C,MAAM,KAAK,GAAG,QAAQ,KAAK,GAAG;GAC9B,MAAM,OAAO,OAAO,WAAW,IAAI,SAAS,EAAE,QAAQ;GACtD,MAAM,QAAQ,QAAQ,SAAS,WAAW;AAE1C,OAAI,CAAC,MAEH,MAAK,QAAQ,SAAS,YAAY,IAAI,KAAK,EAAE;IAC3C,WAAW,CAAC,GAAG,kBAAkB,QAAQ;IACzC,OAAO,QAAQ;IAChB,CAAC;YACO,YAAY,MAAM,CAC3B,MAAK,QAAQ,SAAS,WAAW,IAAI,KAAK,EAAE;IAC1C,WAAW;KAAC,GAAG;KAAkB;KAAS;KAAM;IAChD,OAAO,MAAM;IACd,CAAC;;AAIN,SAAO;GACP;EAEL"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glion/lint-profile-required-fields",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Lint rule to validate required fields per HL7v2 profile",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"health",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"unified-lint-rule": "3.0.1",
|
|
37
|
-
"@glion/ast": "0.
|
|
38
|
-
"@glion/util-visit": "0.
|
|
39
|
-
"@glion/utils": "0.
|
|
37
|
+
"@glion/ast": "0.25.0",
|
|
38
|
+
"@glion/util-visit": "0.25.0",
|
|
39
|
+
"@glion/utils": "0.25.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "25.6.0",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"unified": "^11.0.5",
|
|
48
48
|
"vfile": "^6.0.3",
|
|
49
49
|
"vitest": "4.1.11",
|
|
50
|
-
"@glion/annotate-profile-context": "0.
|
|
51
|
-
"@glion/builder": "0.
|
|
50
|
+
"@glion/annotate-profile-context": "0.25.0",
|
|
51
|
+
"@glion/builder": "0.25.0",
|
|
52
52
|
"@glion/check-readme": "0.18.0",
|
|
53
53
|
"@glion/testing": "0.18.0",
|
|
54
54
|
"@glion/tsconfig": "0.18.0"
|