@glion/encode-escapes 0.27.0 → 0.28.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
@@ -4,7 +4,9 @@ Unified plugin to encode special characters as HL7v2 escape sequences in literal
4
4
 
5
5
  ## What it does
6
6
 
7
- Walks an HL7v2 AST and encodes delimiter characters found in `subcomponent` node values as HL7v2 escape sequences, so a tree that has been modified in memory can be safely serialized back to HL7v2 text without breaking the delimiter structure. It is the inverse of [`@glion/decode-escapes`](https://github.com/rethinkhealth/glion/tree/main/packages/decode-escapes): encode then decode round-trips to the original values.
7
+ Walks an HL7v2 AST and encodes delimiter and control characters found in `subcomponent` node values as HL7v2 escape sequences. It is the inverse of [`@glion/decode-escapes`](https://github.com/rethinkhealth/glion/tree/main/packages/decode-escapes): encode then decode round-trips to the original values.
8
+
9
+ `@glion/to-hl7v2` encodes escape sequences while serializing, so this plugin is for a tree written with `encodeEscapes: false`.
8
10
 
9
11
  ## Install
10
12
 
@@ -22,7 +24,7 @@ import { unified } from "unified";
22
24
  // After modifying AST values that may contain delimiters:
23
25
  const file = await unified()
24
26
  .use(hl7v2EncodeEscapes) // encode special chars as escape sequences
25
- .use(hl7v2ToHl7v2) // serialize AST to HL7v2 text
27
+ .use(hl7v2ToHl7v2, { encodeEscapes: false }) // serialize AST to HL7v2 text
26
28
  .stringify(tree);
27
29
  ```
28
30
 
@@ -52,7 +54,7 @@ Encode special characters as HL7v2 escape sequences in literal nodes.
52
54
 
53
55
  ###### Parameters
54
56
 
55
- - `options.delimiters` (optional) — Override delimiters. If omitted, the plugin reads them from `Root.data.delimiters` (set by the parser). Defaults to the HL7 standard (`| ^ ~ & \`).
57
+ - `options.delimiters` (optional) — Override delimiters. If omitted, the plugin reads them from `file.data.delimiters` (set by `@glion/annotate-delimiters`). Defaults to the HL7 standard (`| ^ ~ & \`).
56
58
 
57
59
  ###### Returns
58
60
 
@@ -60,18 +62,19 @@ Nothing (`undefined`). Mutates the AST in-place.
60
62
 
61
63
  ## Behavior
62
64
 
63
- The plugin visits every `subcomponent` node and rewrites `node.value`, substituting delimiter and control characters with their HL7v2 escape sequences:
65
+ The plugin visits every `subcomponent` node and rewrites `node.value`, substituting delimiter and control characters with their HL7v2 escape sequences. A formatting command such as `\H\`, `\N\`, or `\.sp 2\`, a locally defined sequence (`\Z…\`), and a character set switch (`\Cxxyy\`, `\Mxxyyzz\`) are written as they are.
64
66
 
65
- | Input | Output |
66
- | ----- | ------------------------------ |
67
- | `\|` | `\F\` (field separator) |
68
- | `^` | `\S\` (component separator) |
69
- | `~` | `\R\` (repetition separator) |
70
- | `&` | `\T\` (subcomponent separator) |
71
- | `\` | `\E\` (escape character) |
72
- | `\r` | `\.br\` (line break directive) |
67
+ | Input | Output |
68
+ | ----------------------------- | ------------------------------ |
69
+ | `\|` | `\F\` (field separator) |
70
+ | `^` | `\S\` (component separator) |
71
+ | `~` | `\R\` (repetition separator) |
72
+ | `&` | `\T\` (subcomponent separator) |
73
+ | `\` | `\E\` (escape character) |
74
+ | `\r` | `\.br\` (line break directive) |
75
+ | other characters below U+0020 | `\Xdd\` (hexadecimal escape) |
73
76
 
74
- Delimiter resolution order: `Root.data.delimiters` (preferred, set by the parser) → `options.delimiters` → HL7 defaults.
77
+ Delimiter resolution order: `options.delimiters` → `file.data.delimiters` (set by `@glion/annotate-delimiters`) → HL7 defaults.
75
78
 
76
79
  This is the inverse of [`@glion/decode-escapes`](https://github.com/rethinkhealth/glion/tree/main/packages/decode-escapes):
77
80
 
package/dist/index.d.ts CHANGED
@@ -4,13 +4,9 @@ export interface HL7v2EncodeOptions {
4
4
  delimiters?: Partial<Delimiters>;
5
5
  }
6
6
  /**
7
- * Unified plugin to encode special characters as HL7v2 escape sequences
8
- * in subcomponent literals.
9
- *
10
- * - Encodes delimiter characters: | → \F, ^ → \S, ~ → \R, & → \T\
11
- * - Encodes escape character: \ → \E\
12
- * - Encodes segment delimiter: \r → .br\
13
- * - Skips MSH-1 and MSH-2 (they define the delimiters and must not be escaped)
7
+ * Unified plugin to encode special characters as HL7v2 escape sequences in
8
+ * subcomponent literals, for a tree that `@glion/to-hl7v2` writes with
9
+ * `encodeEscapes: false`. MSH-1 and MSH-2 are left as they are.
14
10
  *
15
11
  * Delimiter resolution order:
16
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAyB,MAAM,YAAY,CAAC;AAE1E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CA+BtE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAS,IAAI,EAAE,MAAM,YAAY,CAAC;AAI1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGtC,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAyBtE,CAAC"}
package/dist/index.js CHANGED
@@ -1,14 +1,11 @@
1
- import { DEFAULT_DELIMITERS } from "@glion/utils";
2
- import { SKIP, visit } from "unist-util-visit";
1
+ import { selectAll } from "@glion/util-query";
2
+ import { visit } from "@glion/util-visit";
3
+ import { DEFAULT_DELIMITERS, encodeEscapes } from "@glion/utils";
3
4
  //#region src/index.ts
4
5
  /**
5
- * Unified plugin to encode special characters as HL7v2 escape sequences
6
- * in subcomponent literals.
7
- *
8
- * - Encodes delimiter characters: | → \F, ^ → \S, ~ → \R, & → \T\
9
- * - Encodes escape character: \ → \E\
10
- * - Encodes segment delimiter: \r → .br\
11
- * - Skips MSH-1 and MSH-2 (they define the delimiters and must not be escaped)
6
+ * Unified plugin to encode special characters as HL7v2 escape sequences in
7
+ * subcomponent literals, for a tree that `@glion/to-hl7v2` writes with
8
+ * `encodeEscapes: false`. MSH-1 and MSH-2 are left as they are.
12
9
  *
13
10
  * Delimiter resolution order:
14
11
  *
@@ -17,46 +14,17 @@ import { SKIP, visit } from "unist-util-visit";
17
14
  * 3. `DEFAULT_DELIMITERS`
18
15
  */
19
16
  const hl7v2EncodeEscapes = (options) => (tree, file) => {
20
- const charToEscape = buildEscapeMap({
17
+ const d = {
21
18
  ...file.data.delimiters ?? DEFAULT_DELIMITERS,
22
19
  ...options?.delimiters
23
- });
24
- visit(tree, (node, index, parent) => {
25
- if (node.type === "field" && index !== void 0 && index < 2 && parent?.type === "segment" && parent.name === "MSH") return SKIP;
26
- if (node.type === "subcomponent") node.value = encode(node.value, charToEscape);
20
+ };
21
+ const encodingCharacters = new Set([...selectAll(tree, "MSH.1"), ...selectAll(tree, "MSH.2")].map((match) => match.node));
22
+ visit(tree, "subcomponent", (node, ancestors) => {
23
+ if (node.value === void 0 || ancestors.some((ancestor) => encodingCharacters.has(ancestor))) return;
24
+ node.value = encodeEscapes(node.value, d);
27
25
  });
28
26
  return tree;
29
27
  };
30
- /**
31
- * Build a map from literal characters to their HL7v2 escape sequences.
32
- * The escape character must be mapped first to avoid double-encoding.
33
- */
34
- function buildEscapeMap(d) {
35
- return new Map([
36
- [d.escape, `${d.escape}E${d.escape}`],
37
- [d.field, `${d.escape}F${d.escape}`],
38
- [d.component, `${d.escape}S${d.escape}`],
39
- [d.repetition, `${d.escape}R${d.escape}`],
40
- [d.subcomponent, `${d.escape}T${d.escape}`],
41
- [d.segment, `${d.escape}.br${d.escape}`]
42
- ]);
43
- }
44
- /**
45
- * Encode special characters in a value as HL7v2 escape sequences.
46
- *
47
- * @param value - The value to encode.
48
- * @param charToEscape - Map of characters to their escape sequences.
49
- * @returns The encoded value.
50
- */
51
- function encode(value, charToEscape) {
52
- if (!value) return value;
53
- let encoded = "";
54
- for (const ch of value) {
55
- const escape = charToEscape.get(ch);
56
- encoded += escape ?? ch;
57
- }
58
- return encoded;
59
- }
60
28
  //#endregion
61
29
  export { hl7v2EncodeEscapes };
62
30
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Delimiters, Root, Segment, Subcomponent } from \"@glion/ast\";\nimport { DEFAULT_DELIMITERS } from \"@glion/utils\";\nimport type { Plugin } from \"unified\";\nimport { SKIP, visit } from \"unist-util-visit\";\nimport type { VFile } from \"vfile\";\n\nexport interface HL7v2EncodeOptions {\n delimiters?: Partial<Delimiters>;\n}\n\n/**\n * Unified plugin to encode special characters as HL7v2 escape sequences\n * in subcomponent literals.\n *\n * - Encodes delimiter characters: | → \\F, ^ → \\S, ~ → \\R, & → \\T\\\n * - Encodes escape character: \\ → \\E\\\n * - Encodes segment delimiter: \\r → .br\\\n * - Skips MSH-1 and MSH-2 (they define the delimiters and must not be escaped)\n *\n * Delimiter resolution order:\n *\n * 1. `options.delimiters` (explicit override)\n * 2. `file.data.delimiters` (set by hl7v2-annotate-delimiters)\n * 3. `DEFAULT_DELIMITERS`\n */\nexport const hl7v2EncodeEscapes: Plugin<[HL7v2EncodeOptions?], Root, Root> =\n (options) => (tree: Root, file: VFile) => {\n const d = {\n ...((file.data.delimiters as Delimiters | undefined) ??\n DEFAULT_DELIMITERS),\n ...options?.delimiters,\n };\n\n const charToEscape = buildEscapeMap(d);\n\n visit(tree, (node, index, parent) => {\n // Skip MSH-1 (index 0) and MSH-2 (index 1) — they define delimiters\n if (\n node.type === \"field\" &&\n index !== undefined &&\n index < 2 &&\n parent?.type === \"segment\" &&\n (parent as Segment).name === \"MSH\"\n ) {\n return SKIP;\n }\n\n if (node.type === \"subcomponent\") {\n (node as Subcomponent).value = encode(\n (node as Subcomponent).value,\n charToEscape\n );\n }\n });\n\n return tree;\n };\n\n/**\n * Build a map from literal characters to their HL7v2 escape sequences.\n * The escape character must be mapped first to avoid double-encoding.\n */\nfunction buildEscapeMap(d: Delimiters): ReadonlyMap<string, string> {\n return new Map<string, string>([\n [d.escape, `${d.escape}E${d.escape}`],\n [d.field, `${d.escape}F${d.escape}`],\n [d.component, `${d.escape}S${d.escape}`],\n [d.repetition, `${d.escape}R${d.escape}`],\n [d.subcomponent, `${d.escape}T${d.escape}`],\n [d.segment, `${d.escape}.br${d.escape}`],\n ]);\n}\n\n/**\n * Encode special characters in a value as HL7v2 escape sequences.\n *\n * @param value - The value to encode.\n * @param charToEscape - Map of characters to their escape sequences.\n * @returns The encoded value.\n */\nfunction encode(\n value: string,\n charToEscape: ReadonlyMap<string, string>\n): string {\n if (!value) {\n return value;\n }\n\n let encoded = \"\";\n\n for (const ch of value) {\n const escape = charToEscape.get(ch);\n encoded += escape ?? ch;\n }\n\n return encoded;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyBA,MAAa,sBACV,aAAa,MAAY,SAAgB;CAOxC,MAAM,eAAe,eAAe;EALlC,GAAK,KAAK,KAAK,cACb;EACF,GAAG,SAAS;EAGuB,CAAC;AAEtC,OAAM,OAAO,MAAM,OAAO,WAAW;AAEnC,MACE,KAAK,SAAS,WACd,UAAU,KAAA,KACV,QAAQ,KACR,QAAQ,SAAS,aAChB,OAAmB,SAAS,MAE7B,QAAO;AAGT,MAAI,KAAK,SAAS,eACf,MAAsB,QAAQ,OAC5B,KAAsB,OACvB,aACD;GAEH;AAEF,QAAO;;;;;;AAOX,SAAS,eAAe,GAA4C;AAClE,QAAO,IAAI,IAAoB;EAC7B,CAAC,EAAE,QAAQ,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS;EACrC,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS;EACpC,CAAC,EAAE,WAAW,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS;EACxC,CAAC,EAAE,YAAY,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS;EACzC,CAAC,EAAE,cAAc,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS;EAC3C,CAAC,EAAE,SAAS,GAAG,EAAE,OAAO,KAAK,EAAE,SAAS;EACzC,CAAC;;;;;;;;;AAUJ,SAAS,OACP,OACA,cACQ;AACR,KAAI,CAAC,MACH,QAAO;CAGT,IAAI,UAAU;AAEd,MAAK,MAAM,MAAM,OAAO;EACtB,MAAM,SAAS,aAAa,IAAI,GAAG;AACnC,aAAW,UAAU;;AAGvB,QAAO"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { Delimiters, Nodes, Root } from \"@glion/ast\";\nimport { selectAll } from \"@glion/util-query\";\nimport { visit } from \"@glion/util-visit\";\nimport { DEFAULT_DELIMITERS, encodeEscapes } from \"@glion/utils\";\nimport type { Plugin } from \"unified\";\nimport type { VFile } from \"vfile\";\n\nexport interface HL7v2EncodeOptions {\n delimiters?: Partial<Delimiters>;\n}\n\n/**\n * Unified plugin to encode special characters as HL7v2 escape sequences in\n * subcomponent literals, for a tree that `@glion/to-hl7v2` writes with\n * `encodeEscapes: false`. MSH-1 and MSH-2 are left as they are.\n *\n * Delimiter resolution order:\n *\n * 1. `options.delimiters` (explicit override)\n * 2. `file.data.delimiters` (set by hl7v2-annotate-delimiters)\n * 3. `DEFAULT_DELIMITERS`\n */\nexport const hl7v2EncodeEscapes: Plugin<[HL7v2EncodeOptions?], Root, Root> =\n (options) => (tree: Root, file: VFile) => {\n const d = {\n ...((file.data.delimiters as Delimiters | undefined) ??\n DEFAULT_DELIMITERS),\n ...options?.delimiters,\n };\n\n const encodingCharacters = new Set<Nodes>(\n [...selectAll(tree, \"MSH.1\"), ...selectAll(tree, \"MSH.2\")].map(\n (match) => match.node\n )\n );\n\n visit(tree, \"subcomponent\", (node, ancestors) => {\n if (\n node.value === undefined ||\n ancestors.some((ancestor) => encodingCharacters.has(ancestor))\n ) {\n return;\n }\n node.value = encodeEscapes(node.value, d);\n });\n\n return tree;\n };\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,MAAa,sBACV,aAAa,MAAY,SAAgB;CACxC,MAAM,IAAI;EACR,GAAK,KAAK,KAAK,cACb;EACF,GAAG,SAAS;EACb;CAED,MAAM,qBAAqB,IAAI,IAC7B,CAAC,GAAG,UAAU,MAAM,QAAQ,EAAE,GAAG,UAAU,MAAM,QAAQ,CAAC,CAAC,KACxD,UAAU,MAAM,KAClB,CACF;AAED,OAAM,MAAM,iBAAiB,MAAM,cAAc;AAC/C,MACE,KAAK,UAAU,KAAA,KACf,UAAU,MAAM,aAAa,mBAAmB,IAAI,SAAS,CAAC,CAE9D;AAEF,OAAK,QAAQ,cAAc,KAAK,OAAO,EAAE;GACzC;AAEF,QAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glion/encode-escapes",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "hl7v2 plugin to encode special characters as hl7v2 escape sequences",
5
5
  "keywords": [
6
6
  "health",
@@ -34,8 +34,9 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "unified": "11.0.5",
37
- "unist-util-visit": "5.1.0",
38
- "@glion/utils": "0.27.0"
37
+ "@glion/util-query": "0.28.0",
38
+ "@glion/util-visit": "0.28.0",
39
+ "@glion/utils": "0.28.0"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@stryker-mutator/core": "10.0.0",
@@ -48,10 +49,10 @@
48
49
  "typescript": "6.0.3",
49
50
  "vfile": "^6.0.3",
50
51
  "vitest": "4.1.11",
51
- "@glion/ast": "0.27.0",
52
- "@glion/builder": "0.27.0",
52
+ "@glion/ast": "0.28.0",
53
+ "@glion/builder": "0.28.0",
53
54
  "@glion/check-readme": "0.18.0",
54
- "@glion/decode-escapes": "0.27.0",
55
+ "@glion/decode-escapes": "0.28.0",
55
56
  "@glion/testing": "0.18.0",
56
57
  "@glion/tsconfig": "0.18.0"
57
58
  },