@glion/lint-profile-extra-fields 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @glion/lint-profile-extra-fields
2
2
 
3
- Lint rule that warns when a segment contains fields beyond the maximum sequence defined in its profile.
3
+ Lint rule that warns when a segment has more fields than its profile defines.
4
4
 
5
5
  ## What it does
6
6
 
7
- Flags fields whose sequence number exceeds the highest field position defined in the HL7v2 profile for that segment at the message's version. The rule reads profile context attached by `@glion/annotate-profile-context`, computes the maximum defined sequence per segment, and reports each field past that boundary. Segments without a known profile (for example Z-segments) are silently skipped.
7
+ Flags a segment with more fields than the HL7v2 standard defines for it in the message's version. The rule reads profile context attached by `@glion/annotate-profile-context`. Segments without a known profile (for example Z-segments) are skipped.
8
8
 
9
9
  ## Install
10
10
 
@@ -41,7 +41,7 @@ console.error(reporter([file]));
41
41
 
42
42
  A `unified` lint rule plugin. Takes no options.
43
43
 
44
- Reads `file.data.profile`. For each segment it looks up the field definition set and takes the largest key (a 1-based HL7 sequence number) as the maximum. It then walks the segment's fields and reports each field whose sequence is greater than that maximum.
44
+ Reports one message for each segment with too many fields, with the number of fields in `actual`.
45
45
 
46
46
  ```ts
47
47
  import type { Plugin } from "unified";
@@ -53,7 +53,7 @@ export default hl7v2LintExtraFields;
53
53
 
54
54
  ## What it checks
55
55
 
56
- This rule flags fields past the defined range for a segment. `MSH` in v2.5 ends at `MSH-21`, so any field at sequence 22 or higher is unexpected for that segment and version.
56
+ A segment has at most as many fields as its profile defines. `MSH` in v2.5 ends at `MSH-21`, so it allows 21 fields.
57
57
 
58
58
  ### Valid
59
59
 
@@ -66,7 +66,7 @@ PID|1||PATID1234^^^HOSP^MR||DOE^JANE||19800101|F
66
66
 
67
67
  ### Invalid
68
68
 
69
- `MSH` carries a field at sequence 22, which is past the v2.5 maximum of `MSH-21`:
69
+ `MSH` has 22 fields, one more than v2.5 defines:
70
70
 
71
71
  ```hl7
72
72
  MSH|^~\&|SENDER|FAC|RECV|RFAC|20250601120000||ADT^A01^ADT_A01|MSG00001|P|2.5||||||||||EXTRA
@@ -76,10 +76,10 @@ PID|1||PATID1234^^^HOSP^MR||DOE^JANE||19800101|F
76
76
  Reported message:
77
77
 
78
78
  ```
79
- Field MSH-22 is beyond the defined fields for MSH (max: 21 in v2.5)
79
+ Segment `MSH` has 22 fields; it allows at most 21 fields.
80
80
  ```
81
81
 
82
- The reported max comes from the profile's highest sequence number and the version is taken from the annotated profile context. One message is reported per extra field.
82
+ The version is taken from the annotated profile context. A segment is reported once, however many fields it has past its last.
83
83
 
84
84
  ## Part of Glion
85
85
 
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Root } from "@glion/ast";
2
2
  /**
3
- * Lint rule that warns when a segment contains fields beyond the maximum
4
- * sequence number defined in its profile.
3
+ * Lint rule that reports a segment with more fields than its profile
4
+ * defines.
5
5
  *
6
6
  * Segments without a known profile (e.g., Z-segments) are silently skipped.
7
7
  *
@@ -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;AAIvC;;;;;;;;;;GAUG;AACH,QAAA,MAAM,oBAAoB,mDAoCzB,CAAC;AAaF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAUvC;;;;;;;;;;GAUG;AACH,QAAA,MAAM,oBAAoB,mDAiCzB,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { SKIP, visit } from "@glion/util-visit";
2
+ import pluralize from "pluralize";
2
3
  import { lintRule } from "unified-lint-rule";
3
4
  //#region src/index.ts
5
+ const messages = { tooManyFields: (segmentId, count, max) => `Segment \`${segmentId}\` has ${pluralize("field", count, true)}; it allows at most ${pluralize("field", max, true)}.` };
4
6
  /**
5
- * Lint rule that warns when a segment contains fields beyond the maximum
6
- * sequence number defined in its profile.
7
+ * Lint rule that reports a segment with more fields than its profile
8
+ * defines.
7
9
  *
8
10
  * Segments without a known profile (e.g., Z-segments) are silently skipped.
9
11
  *
@@ -12,34 +14,27 @@ import { lintRule } from "unified-lint-rule";
12
14
  * unified().use(hl7v2LintExtraFields);
13
15
  * ```;
14
16
  */
15
- const hl7v2LintExtraFields = lintRule({ origin: "hl7v2-lint:extra-fields" }, (tree, file) => {
17
+ const hl7v2LintExtraFields = lintRule({
18
+ origin: "hl7v2-lint:extra-fields",
19
+ url: "https://github.com/rethinkhealth/glion/tree/main/packages/lint-profile-extra-fields#readme"
20
+ }, (tree, file) => {
16
21
  const ctx = file.data.profile;
17
22
  if (!ctx) return;
18
23
  visit(tree, "segment", (segment, segmentAncestors) => {
19
- const fieldDef = ctx.fields.get(segment.name);
20
- if (!fieldDef) return SKIP;
21
- const maxSequence = maxKey(fieldDef.bySequence);
22
- if (maxSequence === 0) return SKIP;
23
- visit(segment, "field", (fieldNode, _fieldAncestors, info) => {
24
- if (info.sequence > maxSequence) file.message(`Field ${segment.name}-${info.sequence} is beyond the defined fields for ${segment.name} (max: ${maxSequence} in v${ctx.version})`, {
25
- ancestors: [
26
- ...segmentAncestors,
27
- segment,
28
- fieldNode
29
- ],
30
- place: fieldNode.position
24
+ const fields = ctx.fields.get(segment.name);
25
+ if (!fields) return SKIP;
26
+ const max = Math.max(0, ...fields.bySequence.keys());
27
+ const count = segment.children.length;
28
+ if (count > max) {
29
+ const message = file.message(messages.tooManyFields(segment.name, count, max), {
30
+ ancestors: [...segmentAncestors, segment],
31
+ place: segment.position
31
32
  });
32
- return SKIP;
33
- });
33
+ message.actual = String(count);
34
+ }
34
35
  return SKIP;
35
36
  });
36
37
  });
37
- /** Return the highest numeric key in a Map, or 0 if empty. */
38
- function maxKey(map) {
39
- let max = 0;
40
- for (const key of map.keys()) if (key > max) max = key;
41
- return max;
42
- }
43
38
  //#endregion
44
39
  export { hl7v2LintExtraFields as default };
45
40
 
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 { lintRule } from \"unified-lint-rule\";\n\n/**\n * Lint rule that warns when a segment contains fields beyond the maximum\n * sequence number defined in its profile.\n *\n * Segments without a known profile (e.g., Z-segments) are silently skipped.\n *\n * @example\n * ```typescript\n * unified().use(hl7v2LintExtraFields);\n * ```;\n */\nconst hl7v2LintExtraFields = lintRule<Root>(\n { origin: \"hl7v2-lint:extra-fields\" },\n (tree, file) => {\n const ctx = file.data.profile;\n if (!ctx) {\n return;\n }\n\n visit(tree, \"segment\", (segment, segmentAncestors) => {\n const fieldDef = ctx.fields.get(segment.name);\n if (!fieldDef) {\n return SKIP;\n }\n\n const maxSequence = maxKey(fieldDef.bySequence);\n if (maxSequence === 0) {\n return SKIP;\n }\n\n visit(segment, \"field\", (fieldNode, _fieldAncestors, info) => {\n if (info.sequence > maxSequence) {\n file.message(\n `Field ${segment.name}-${info.sequence} is beyond the defined fields for ${segment.name} (max: ${maxSequence} in v${ctx.version})`,\n {\n ancestors: [...segmentAncestors, segment, fieldNode],\n place: fieldNode.position,\n }\n );\n }\n\n return SKIP;\n });\n\n return SKIP;\n });\n }\n);\n\n/** Return the highest numeric key in a Map, or 0 if empty. */\nfunction maxKey(map: ReadonlyMap<number, unknown>): number {\n let max = 0;\n for (const key of map.keys()) {\n if (key > max) {\n max = key;\n }\n }\n return max;\n}\n\nexport default hl7v2LintExtraFields;\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAM,uBAAuB,SAC3B,EAAE,QAAQ,2BAA2B,GACpC,MAAM,SAAS;CACd,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI,CAAC,IACH;AAGF,OAAM,MAAM,YAAY,SAAS,qBAAqB;EACpD,MAAM,WAAW,IAAI,OAAO,IAAI,QAAQ,KAAK;AAC7C,MAAI,CAAC,SACH,QAAO;EAGT,MAAM,cAAc,OAAO,SAAS,WAAW;AAC/C,MAAI,gBAAgB,EAClB,QAAO;AAGT,QAAM,SAAS,UAAU,WAAW,iBAAiB,SAAS;AAC5D,OAAI,KAAK,WAAW,YAClB,MAAK,QACH,SAAS,QAAQ,KAAK,GAAG,KAAK,SAAS,oCAAoC,QAAQ,KAAK,SAAS,YAAY,OAAO,IAAI,QAAQ,IAChI;IACE,WAAW;KAAC,GAAG;KAAkB;KAAS;KAAU;IACpD,OAAO,UAAU;IAClB,CACF;AAGH,UAAO;IACP;AAEF,SAAO;GACP;EAEL;;AAGD,SAAS,OAAO,KAA2C;CACzD,IAAI,MAAM;AACV,MAAK,MAAM,OAAO,IAAI,MAAM,CAC1B,KAAI,MAAM,IACR,OAAM;AAGV,QAAO"}
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 pluralize from \"pluralize\";\nimport { lintRule } from \"unified-lint-rule\";\n\nconst messages = {\n tooManyFields: (segmentId: string, count: number, max: number) =>\n `Segment \\`${segmentId}\\` has ${pluralize(\"field\", count, true)}; it allows at most ${pluralize(\"field\", max, true)}.`,\n} as const;\n\n/**\n * Lint rule that reports a segment with more fields than its profile\n * defines.\n *\n * Segments without a known profile (e.g., Z-segments) are silently skipped.\n *\n * @example\n * ```typescript\n * unified().use(hl7v2LintExtraFields);\n * ```;\n */\nconst hl7v2LintExtraFields = lintRule<Root>(\n {\n origin: \"hl7v2-lint:extra-fields\",\n url: \"https://github.com/rethinkhealth/glion/tree/main/packages/lint-profile-extra-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 const max = Math.max(0, ...fields.bySequence.keys());\n const count = segment.children.length;\n if (count > max) {\n const message = file.message(\n messages.tooManyFields(segment.name, count, max),\n {\n ancestors: [...segmentAncestors, segment],\n place: segment.position,\n }\n );\n message.actual = String(count);\n }\n\n return SKIP;\n });\n }\n);\n\nexport default hl7v2LintExtraFields;\n"],"mappings":";;;;AAOA,MAAM,WAAW,EACf,gBAAgB,WAAmB,OAAe,QAChD,aAAa,UAAU,SAAS,UAAU,SAAS,OAAO,KAAK,CAAC,sBAAsB,UAAU,SAAS,KAAK,KAAK,CAAC,IACvH;;;;;;;;;;;;AAaD,MAAM,uBAAuB,SAC3B;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;EAGT,MAAM,MAAM,KAAK,IAAI,GAAG,GAAG,OAAO,WAAW,MAAM,CAAC;EACpD,MAAM,QAAQ,QAAQ,SAAS;AAC/B,MAAI,QAAQ,KAAK;GACf,MAAM,UAAU,KAAK,QACnB,SAAS,cAAc,QAAQ,MAAM,OAAO,IAAI,EAChD;IACE,WAAW,CAAC,GAAG,kBAAkB,QAAQ;IACzC,OAAO,QAAQ;IAChB,CACF;AACD,WAAQ,SAAS,OAAO,MAAM;;AAGhC,SAAO;GACP;EAEL"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glion/lint-profile-extra-fields",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Lint rule that warns when a segment contains fields beyond the maximum sequence defined in its profile",
5
5
  "keywords": [
6
6
  "health",
@@ -33,12 +33,14 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
+ "pluralize": "8.0.0",
36
37
  "unified-lint-rule": "3.0.1",
37
- "@glion/ast": "0.22.0",
38
- "@glion/util-visit": "0.22.0"
38
+ "@glion/ast": "0.24.0",
39
+ "@glion/util-visit": "0.24.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/node": "25.6.0",
43
+ "@types/pluralize": "^0.0.33",
42
44
  "@vitest/coverage-v8": "4.1.11",
43
45
  "publint": "0.3.18",
44
46
  "tsdown": "0.21.10",
@@ -46,8 +48,8 @@
46
48
  "unified": "^11.0.5",
47
49
  "vfile": "^6.0.3",
48
50
  "vitest": "4.1.11",
49
- "@glion/annotate-profile-context": "0.22.0",
50
- "@glion/builder": "0.22.0",
51
+ "@glion/builder": "0.24.0",
52
+ "@glion/annotate-profile-context": "0.24.0",
51
53
  "@glion/check-readme": "0.18.0",
52
54
  "@glion/testing": "0.18.0",
53
55
  "@glion/tsconfig": "0.18.0"