@glion/lint-profile-required-fields 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 Rethink Health
3
+ Copyright (c) 2023 Rethink Health, SUARL
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -75,7 +75,7 @@ PID|1||||DOE^JANE||19800101|F
75
75
  Reported message:
76
76
 
77
77
  ```
78
- Field `PID-3` (Patient Identifier List) is empty; it is required.
78
+ The field is empty; it is required.
79
79
  ```
80
80
 
81
81
  A segment that ends before a required field, such as `PID|1`, produces one message per required field it does not reach:
@@ -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;AAYvC;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,uBAAuB,mDAyC5B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAavC;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,uBAAuB,mDA+C5B,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { SKIP, visit } from "@glion/util-visit";
2
- import { isEmptyNode } from "@glion/utils";
2
+ import { Optionality, isEmptyNode } from "@glion/utils";
3
3
  import { lintRule } from "unified-lint-rule";
4
4
  //#region src/index.ts
5
5
  const messages = {
6
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.`
7
+ absentFieldWithoutName: (field) => `Field \`${field}\` is not present; it is required.`,
8
+ emptyField: "The field is empty; it is required."
8
9
  };
9
10
  /**
10
11
  * Lint rule that validates required fields per HL7v2 profile.
@@ -32,15 +33,14 @@ const hl7v2LintRequiredFields = lintRule({
32
33
  visit(tree, "segment", (segment, segmentAncestors) => {
33
34
  const fields = ctx.fields.get(segment.name);
34
35
  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), {
36
+ for (const profile of fields.bySequence.values()) {
37
+ if (profile.optionality !== Optionality.Required) continue;
38
+ const field = segment.children[profile.sequence - 1];
39
+ if (!field) file.message(profile.name === void 0 ? messages.absentFieldWithoutName(profile.id) : messages.absentField(profile.id, profile.name), {
40
40
  ancestors: [...segmentAncestors, segment],
41
41
  place: segment.position
42
42
  });
43
- else if (isEmptyNode(field)) file.message(messages.emptyField(id, name), {
43
+ else if (isEmptyNode(field)) file.message(messages.emptyField, {
44
44
  ancestors: [
45
45
  ...segmentAncestors,
46
46
  segment,
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\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"}
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, Optionality } 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 absentFieldWithoutName: (field: string) =>\n `Field \\`${field}\\` is not present; it is required.`,\n emptyField: \"The field 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 fields, not the message's: a field the\n // segment ends before has no node to visit.\n for (const profile of fields.bySequence.values()) {\n if (profile.optionality !== Optionality.Required) {\n continue;\n }\n const field = segment.children[profile.sequence - 1];\n\n if (!field) {\n // Reported on the segment, the closest node the message has.\n file.message(\n profile.name === undefined\n ? messages.absentFieldWithoutName(profile.id)\n : messages.absentField(profile.id, profile.name),\n {\n ancestors: [...segmentAncestors, segment],\n place: segment.position,\n }\n );\n } else if (isEmptyNode(field)) {\n file.message(messages.emptyField, {\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,yBAAyB,UACvB,WAAW,MAAM;CACnB,YAAY;CACb;;;;;;;;;;;;;;;;;;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,WAAW,OAAO,WAAW,QAAQ,EAAE;AAChD,OAAI,QAAQ,gBAAgB,YAAY,SACtC;GAEF,MAAM,QAAQ,QAAQ,SAAS,QAAQ,WAAW;AAElD,OAAI,CAAC,MAEH,MAAK,QACH,QAAQ,SAAS,KAAA,IACb,SAAS,uBAAuB,QAAQ,GAAG,GAC3C,SAAS,YAAY,QAAQ,IAAI,QAAQ,KAAK,EAClD;IACE,WAAW,CAAC,GAAG,kBAAkB,QAAQ;IACzC,OAAO,QAAQ;IAChB,CACF;YACQ,YAAY,MAAM,CAC3B,MAAK,QAAQ,SAAS,YAAY;IAChC,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.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Lint rule to validate required fields per HL7v2 profile",
5
5
  "keywords": [
6
6
  "health",
@@ -34,11 +34,13 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "unified-lint-rule": "3.0.1",
37
- "@glion/ast": "0.26.0",
38
- "@glion/util-visit": "0.26.0",
39
- "@glion/utils": "0.26.0"
37
+ "@glion/ast": "0.27.0",
38
+ "@glion/util-visit": "0.27.0",
39
+ "@glion/utils": "0.27.0"
40
40
  },
41
41
  "devDependencies": {
42
+ "@stryker-mutator/core": "10.0.0",
43
+ "@stryker-mutator/vitest-runner": "10.0.0",
42
44
  "@types/node": "25.6.0",
43
45
  "@vitest/coverage-v8": "4.1.11",
44
46
  "publint": "0.3.18",
@@ -47,11 +49,11 @@
47
49
  "unified": "^11.0.5",
48
50
  "vfile": "^6.0.3",
49
51
  "vitest": "4.1.11",
50
- "@glion/annotate-profile-context": "0.26.0",
51
- "@glion/builder": "0.26.0",
52
- "@glion/check-readme": "0.18.0",
52
+ "@glion/builder": "0.27.0",
53
+ "@glion/annotate-profile-context": "0.27.0",
53
54
  "@glion/testing": "0.18.0",
54
- "@glion/tsconfig": "0.18.0"
55
+ "@glion/tsconfig": "0.18.0",
56
+ "@glion/check-readme": "0.18.0"
55
57
  },
56
58
  "engines": {
57
59
  "node": ">=22"
@@ -60,9 +62,10 @@
60
62
  "build": "tsdown && tsc --emitDeclarationOnly",
61
63
  "check-types": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json",
62
64
  "check:readme": "glion-check-readme",
65
+ "mutate": "stryker run",
66
+ "publint": "publint --strict",
63
67
  "test": "vitest run",
64
68
  "test:coverage": "vitest run --coverage",
65
- "test:watch": "vitest",
66
- "publint": "publint --strict"
69
+ "test:watch": "vitest"
67
70
  }
68
71
  }