@cosyte/transform 0.0.7 → 0.0.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - ad5f2f2: No runtime impact: the repository's own PHI commit-gate read 31 of its 102 tracked files, and it now reads 101 of its 103 and refuses when it cannot account for the rest.
8
+
9
+ Both of the gate's enumerating routes covered `test/fixtures/` and `src/` only. Seventy-one tracked files were read by neither of them, twenty-seven of those under `test/`, and eight of those carried inline HL7 v2 patient-identification segments with names, dates of birth and medical record numbers in them. The sharper half is that `test/fixtures/` has never existed in this repository, on any commit: the walk's existence check returned on its first line for that root on every run the gate has ever made, and every one of those runs printed a clean result and exited zero. An unopened root and a clean one are indistinguishable from the outside.
10
+
11
+ Neither a file count nor an existence check detects that, and both were considered and rejected: a count counts the roots that did exist, and refusing a missing root leaves an emptied one reporting clean. So the walk now covers every tracked directory plus the files at the repository root, and reconciles what it actually opened against the list of files version control actually carries. A tracked path the walk did not open now refuses, naming each one. A declared root that is a symbolic link, dangling or not, refuses too: the existence check follows a link, so a dangling root read as absent and the whole corpus went unscanned while the run reported success.
12
+
13
+ Enumerating more files buys the cross-cutting social security number and email checks and nothing else, and measured on this repository those two find nothing at all in the eight fixture files: they carry no dashed social security number and no email address. What they carry is names, dates of birth, record numbers, one undashed social security number, a street address and two telephone numbers. So a structured pass ships alongside the existing one, never in place of it, reading HL7 v2 patient, next-of-kin, guarantor and insurance segments field by field and component by component. It finds segments inline rather than assuming a file is a message, because this package ships no standalone message file at all: every fixture is a string literal inside TypeScript.
14
+
15
+ Every value the structured pass reports is checked against the reviewed synthetic-fixture declaration list, which gains entries for this repository's placeholders. Each was read by hand first and each is named in that file rather than removed, because removing them would destroy the evidence the audit happened. Seventy files are newly read, sixty-nine of which already existed and were read by hand; nothing patient-identifying was found in any of them. The two that stay unread are the vendored compressed archives, whose stored bytes are not the text they carry, and each is declared by its exact path.
16
+
17
+ One detection is subtracted and it is the only one. The package manifest carries the publisher's own contact address, which the email check cannot tell from a patient's, so scanning the manifest at all required declaring that one address. It is declared with a path as well as an address, so the same address in any other file still reports and any other address in the manifest still reports, and the cost is that naming the manifest directly no longer reports it. Every other outcome is unchanged or newly caught: a hundred and sixty-eight before-and-after cases, fourteen path shapes by four payload shapes by all three ways the gate can be invoked, with thirty-seven that reported before still reporting, seventy-four newly caught and fifty-six unchanged and quiet.
18
+
19
+ Two further blind spots were found while grading this change and were closed rather than written down, because both reported a clean result over content a reader would expect to be caught: a person's name spelled with any character outside the plain English alphabet, and a whole message pasted into a single string with its separators written as escapes. A wrong field position was corrected too, where an insurance segment's relationship code was reported as a telephone number.
20
+
21
+ The way the gate's own limits are written changed with them, and that is the more useful half. Grading three times showed that a list of what a scanner does not catch cannot be kept true, because every clause of every segment of the standard would have to appear on it, and both versions of that list were measured incomplete in the direction that flatters the gate. The scanner now states the opposite way round: exactly which fields it reads, with anything not named there not checked. That claim can be checked against the code, and the suite now checks it from both sides in a single run, so a field added to the reader without being added to the statement, or dropped from the reader while the statement keeps promising it, both turn the build red. It also records which of those field numbers are corroborated by something in this repository and which are not, because that is where the remaining risk sits.
22
+
23
+ Several limits stay disclosed rather than quietly closed, including that the reconciliation compares path names and not the bytes stored at them, and the complete list of what the gate still cannot see is written at the top of the scanner itself.
24
+
25
+ ## 0.0.8
26
+
27
+ ### Patch Changes
28
+
29
+ - 9fdde50: No runtime impact: punctuation only, plus a new repository-internal check that keeps it that way.
30
+
31
+ The Cosyte brand voice does not use the em dash (U+2014). This package carried 659 of them across
32
+ 75 of its 98 tracked files, including the README, every documentation page that publishes to the
33
+ documentation site, and the source doc comments that compile into the shipped type declarations and
34
+ render in an editor on hover. All 609 that were in scope are rewritten with a period, a colon, a
35
+ comma or parentheses, chosen by what each sentence meant rather than by one blanket substitution.
36
+
37
+ No exported name, type, issue code, fatal code or documented behaviour changed. Two strings changed
38
+ punctuation and nothing else, and both belong to developer tooling rather than to the published
39
+ package: the PHI scanner's clean-run line, and one diagnostic from the check that verifies this
40
+ repository's own contributor instructions.
41
+
42
+ Two files still carry the character and each is an exemption with a written reason. The changelog's
43
+ dated archive below its "Released before this file was generated" heading is a frozen record whose
44
+ entries are byte identical to the tarballs they shipped in, and rewriting it would destroy the
45
+ evidence a changelog exists to hold. The vendored third-party tarball under `vendor/` holds the
46
+ character's bytes by coincidence inside a compressed stream, which no edit can remove.
47
+
48
+ The check that enforces the rule lands in the same change as the sweep, on purpose: a check arriving
49
+ before its sweep turns the build red on arrival, and a sweep arriving before its check lets the
50
+ character grow back. It reads bytes directly rather than shelling out, refuses to report a clean
51
+ result whenever it cannot prove it read its subject, and holds its own source to the same rule by
52
+ assembling every banned spelling at runtime instead of writing one down.
53
+
3
54
  ## 0.0.7
4
55
 
5
56
  ### Patch Changes
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  # @cosyte/transform
9
9
 
10
- > HL7 v2 → FHIR R4 transformation for Node.js and TypeScript — **IG-grounded, fail-safe, value-free
10
+ > HL7 v2 → FHIR R4 transformation for Node.js and TypeScript: **IG-grounded, fail-safe, value-free
11
11
  > diagnostics; never a confident wrong FHIR value**.
12
12
 
13
13
  `@cosyte/transform` is the healthcare **transformation** layer of the cosyte suite. Unlike the
@@ -18,9 +18,9 @@ grounded on the official **HL7 Version 2 to FHIR** Implementation Guide (`hl7.fh
18
18
  > **Status:** pre-alpha (`0.0.x`), published to npm. This release ships the
19
19
  > six safety-critical datatype converters and the value-free diagnostic channel, the
20
20
  > message-level assembly, HL7 v2 **ADT → FHIR Patient + Encounter**, the **ORU^R01 → FHIR
21
- > DiagnosticReport + Observation** results graph, the order-entry graph — **ORM_O01 /
22
- > OML_O21 → ServiceRequest** and **RXO → MedicationRequest** — the thin IG singles —
23
- > **VXU_V04 → Immunization**, **SIU_S12 → Appointment**, **MDM_T02 → DocumentReference** — all
21
+ > DiagnosticReport + Observation** results graph, the order-entry graph (**ORM_O01 /
22
+ > OML_O21 → ServiceRequest** and **RXO → MedicationRequest**), the thin IG singles
23
+ > (**VXU_V04 → Immunization**, **SIU_S12 → Appointment**, **MDM_T02 → DocumentReference**), all
24
24
  > via `toFhir(msg)`, and **terminology value translation** of coded fields: route/site,
25
25
  > appointment type, order priority, and substitution are now value-translated through their IG
26
26
  > `mappedVia` ConceptMaps via `toFhirCodeableConceptVia`, fail-safe on any code the IG leaves unmapped.
@@ -33,7 +33,7 @@ grounded on the official **HL7 Version 2 to FHIR** Implementation Guide (`hl7.fh
33
33
  npm install @cosyte/transform @cosyte/hl7 @cosyte/fhir
34
34
  ```
35
35
 
36
- `@cosyte/hl7` and `@cosyte/fhir` are **peer dependencies** — the transform maps between the models
36
+ `@cosyte/hl7` and `@cosyte/fhir` are **peer dependencies**: the transform maps between the models
37
37
  they own. Its own third-party runtime dependencies are **zero**.
38
38
 
39
39
  **That command does not work yet.** This package is published, but `@cosyte/fhir` is not on the
@@ -53,21 +53,21 @@ const { value, issues } = toFhirHumanName({
53
53
  // value: a FHIR HumanName node; issues: [] (clean, fully mapped)
54
54
  ```
55
55
 
56
- Each converter returns `{ value, issues }` — the FHIR datatype node it could faithfully produce, plus
56
+ Each converter returns `{ value, issues }`: the FHIR datatype node it could faithfully produce, plus
57
57
  the value-free diagnostics it raised.
58
58
 
59
59
  ## The fail-safe rule
60
60
 
61
- Every conversion is grounded on the IG and **refuses to guess**. On any ambiguity —
61
+ Every conversion is grounded on the IG and **refuses to guess**. On any of these ambiguities:
62
62
 
63
63
  - a v2 timestamp with a time-of-day but **no timezone** (FHIR forbids time without a zone),
64
64
  - an assigning authority that can't be **resolved to a system URI** (never synthesized from a bare
65
- namespace — that would merge two patients),
65
+ namespace, which would merge two patients),
66
66
  - a code with an **unrecognized or absent coding system**,
67
67
  - a unit that isn't valid **UCUM** (magnitudes are never converted),
68
68
 
69
- — the converter produces what it _can_ (often reduced in precision) and raises a **typed, value-free
70
- `TransformIssue`**: a stable code, the v2 location, and the FHIR path — never a value. Render a list
69
+ the converter produces what it _can_ (often reduced in precision) and raises a **typed, value-free
70
+ `TransformIssue`**: a stable code, the v2 location, and the FHIR path, never a value. Render a list
71
71
  of issues as a FHIR `OperationOutcome` with `toOperationOutcome(issues)`.
72
72
 
73
73
  ## The six datatype converters
@@ -83,8 +83,8 @@ of issues as a FHIR `OperationOutcome` with `toOperationOutcome(issues)`.
83
83
 
84
84
  ## Assemble a message
85
85
 
86
- `toFhir(msg)` takes a parsed `@cosyte/hl7` **ADT** message and returns a FHIR R4 **message `Bundle`** —
87
- a `MessageHeader`, then the `Patient` and `Encounter` (and `RelatedPerson`) it describes — plus the
86
+ `toFhir(msg)` takes a parsed `@cosyte/hl7` **ADT** message and returns a FHIR R4 **message `Bundle`**:
87
+ a `MessageHeader`, then the `Patient` and `Encounter` (and `RelatedPerson`) it describes, plus the
88
88
  value-free issues, each map grounded firsthand on the IG's segment/table ConceptMaps.
89
89
 
90
90
  ```ts
@@ -105,19 +105,19 @@ const { bundle, issues } = toFhir(parseHL7(raw), {
105
105
  | NK1 | `RelatedPerson` | NK1-2/3/4 → `name`/`relationship`/`address` |
106
106
 
107
107
  The fail-safe rule holds at the message level: an unmapped patient class, a naked timestamp, or an
108
- unresolvable authority becomes a typed issue — never a fabricated value. A trigger the IG has no
108
+ unresolvable authority becomes a typed issue, never a fabricated value. A trigger the IG has no
109
109
  **message** map for is assembled from the segment maps and flagged, never invented; every emitted
110
110
  resource is validated against `@cosyte/fhir` before it ships.
111
111
 
112
112
  The same `toFhir(msg)` handles the other message families: **ORU^R01** → `DiagnosticReport` (OBR) +
113
- `Observation` (OBX), and the order-entry graph — **ORM_O01 / OML_O21** ORC/OBR →
114
- `ServiceRequest`, **RXO** (+ RXR route) → `MedicationRequest`, with `ServiceRequest.status`
113
+ `Observation` (OBX), and the order-entry graph, **ORM_O01 / OML_O21** ORC/OBR →
114
+ `ServiceRequest` and **RXO** (+ RXR route) → `MedicationRequest`, with `ServiceRequest.status`
115
115
  grounded on the HL70119 → request-status ConceptMap and withheld when it cannot be grounded, and a
116
116
  `MedicationRequest` whose IG-ungrounded status is the honest `unknown` rather than a guess. `RXE` has no
117
117
  STU1 IG map and is flagged, never assembled.
118
118
 
119
119
  The thin single-trigger families complete the IG-covered message set: **VXU_V04** RXA (+
120
- RXR route, ORC) → `Immunization` (status via the IG's three conditioned rows — a delete action →
120
+ RXR route, ORC) → `Immunization` (status via the IG's three conditioned rows: a delete action →
121
121
  `entered-in-error`, an unvalued RXA-20 → `completed`, else the HL70322 → event-status ConceptMap, with a
122
122
  valued-but-unmapped code withheld), **SIU_S12** SCH/AIS/PID → `Appointment` (status via the
123
123
  HL70278 → appointmentstatus ConceptMap, the Patient wired as the required participant, its IG-unsourced
@@ -126,12 +126,12 @@ grounded only for TXA-19 `AV` → `current`, the document body base64-encoded ve
126
126
  interpreted). Timezone-naked instants are dropped and flagged, never assigned a fabricated UTC offset.
127
127
 
128
128
  `toFhirCodeableConceptVia(cwe, map)` applies a license-clean IG value ConceptMap
129
- (transcribed and verified firsthand against the raw published IG JSON) — **RXR** route/site
129
+ (transcribed and verified firsthand against the raw published IG JSON): **RXR** route/site
130
130
  (HL70162/HL70550), **SCH-8** appointment type (HL70277), **RXO-9** substitution (HL70161), and **OBR-5**
131
- priority (HL70485) — translating the source table code to its FHIR target coding, additively (the raw
131
+ priority (HL70485), translating the source table code to its FHIR target coding, additively (the raw
132
132
  coding is preserved alongside the derived one). It is fail-safe by refusal: a code the IG map leaves in
133
133
  its `(unmapped)` group is flagged, never coerced to a neighbour. Fields whose IG target is **SNOMED CT**
134
- (RXR-4 method, SCH-7 reason) stay structural — SNOMED is not bundled (BYO ConceptMap) — and fields the
134
+ (RXR-4 method, SCH-7 reason) stay structural, because SNOMED is not bundled (BYO ConceptMap), and fields the
135
135
  IG ships no value map for (TXA-2 document type, RXA-5 vaccine code) are carried as-is, never invented.
136
136
 
137
137
  ## License
package/dist/index.cjs CHANGED
@@ -7,16 +7,16 @@ var crypto = require('crypto');
7
7
  // src/diagnostics/codes.ts
8
8
  var ISSUE_CODES = {
9
9
  /** A v2 DTM/TS carried a time-of-day but no timezone offset. FHIR forbids time-without-zone, so
10
- * the value was reduced to date precision (or an explicit sender offset applied via options) —
10
+ * the value was reduced to date precision (or an explicit sender offset applied via options),
11
11
  * **never** a guessed UTC. */
12
12
  TRANSFORM_TIMESTAMP_NO_TIMEZONE: "TRANSFORM_TIMESTAMP_NO_TIMEZONE",
13
13
  /** A v2 DTM/TS could not be parsed into a valid FHIR date/time; no value was emitted. */
14
14
  TRANSFORM_TIMESTAMP_INVALID: "TRANSFORM_TIMESTAMP_INVALID",
15
15
  /** The assigning authority (HD) could not be resolved to a FHIR `Identifier.system` URI via the
16
- * NamingSystem registry. The identifier is emitted with its value and **no** system — a system URI
16
+ * NamingSystem registry. The identifier is emitted with its value and **no** system: a system URI
17
17
  * is **never** synthesized from HD.1 alone. */
18
18
  TRANSFORM_IDENTIFIER_SYSTEM_UNRESOLVED: "TRANSFORM_IDENTIFIER_SYSTEM_UNRESOLVED",
19
- /** A coded value could not be mapped to a FHIR concept — either it carried no coding-system context
19
+ /** A coded value could not be mapped to a FHIR concept: either it carried no coding-system context
20
20
  * (CWE.3 absent), or it is a table code the IG ConceptMap has **no target** for (e.g. an OBX-11/
21
21
  * OBR-25 result status the HL70085/HL70123 map does not carry, or an OBX-8 abnormal flag absent from
22
22
  * HL70078). The original code is preserved / the FHIR value left absent; **never** coerced to a
@@ -26,14 +26,14 @@ var ISSUE_CODES = {
26
26
  * be emitted. The code is preserved; a URI is **never** invented. */
27
27
  TRANSFORM_CODE_SYSTEM_UNRESOLVED: "TRANSFORM_CODE_SYSTEM_UNRESOLVED",
28
28
  /** A unit was not a valid UCUM code (or was not declared UCUM). It is preserved verbatim in
29
- * `Quantity.unit`; `Quantity.code`/`.system` are left absent — a UCUM code is **never** fabricated
29
+ * `Quantity.unit`; `Quantity.code`/`.system` are left absent: a UCUM code is **never** fabricated
30
30
  * and a magnitude is **never** converted. */
31
31
  TRANSFORM_UNIT_NOT_UCUM: "TRANSFORM_UNIT_NOT_UCUM",
32
- /** A v2 numeric value (NM) could not be represented as a FHIR `decimal` without altering it — e.g.
32
+ /** A v2 numeric value (NM) could not be represented as a FHIR `decimal` without altering it, e.g.
33
33
  * a leading `+`, a leading zero, or a trailing dot, which FHIR's `decimal` lexical form forbids. No
34
34
  * `Quantity` value is emitted rather than a canonicalized (and therefore altered) magnitude. */
35
35
  TRANSFORM_QUANTITY_VALUE_INVALID: "TRANSFORM_QUANTITY_VALUE_INVALID",
36
- /** A populated source element was not carried to its FHIR target and was dropped — either because
36
+ /** A populated source element was not carried to its FHIR target and was dropped, either because
37
37
  * the IG map defines no target, or because that target's conversion is **not implemented**
38
38
  * (e.g. XTN→ContactPoint telecom, PV1 participants). Surfaced here rather than silently discarded,
39
39
  * never a fabricated value. */
@@ -46,13 +46,13 @@ var ISSUE_CODES = {
46
46
  TRANSFORM_ADDRESS_USE_UNMAPPED: "TRANSFORM_ADDRESS_USE_UNMAPPED",
47
47
  /** The message's trigger event has no IG *message* map, so its resource graph was assembled from
48
48
  * the reusable IG *segment* maps rather than a message map. The graph is best-effort and flagged
49
- * as such — never a fabricated message map. */
49
+ * as such, never a fabricated message map. */
50
50
  TRANSFORM_SEGMENT_ASSEMBLED: "TRANSFORM_SEGMENT_ASSEMBLED",
51
51
  /** A produced FHIR resource failed `@cosyte/fhir` R4 structural validation and was **withheld**
52
- * from the bundle rather than emitted as a silently-invalid resource — the conservative-emit gate. */
52
+ * from the bundle rather than emitted as a silently-invalid resource: the conservative-emit gate. */
53
53
  TRANSFORM_RESOURCE_INVALID: "TRANSFORM_RESOURCE_INVALID",
54
54
  /** A FHIR-required element's value could not be faithfully derived from the source, so it was
55
- * emitted with a `data-absent-reason` extension (value `unknown`) rather than fabricated — e.g.
55
+ * emitted with a `data-absent-reason` extension (value `unknown`) rather than fabricated, e.g.
56
56
  * `MessageHeader.source.endpoint` from an MSH-3 application namespace that is not a URL. */
57
57
  TRANSFORM_REQUIRED_ELEMENT_UNKNOWN: "TRANSFORM_REQUIRED_ELEMENT_UNKNOWN"
58
58
  };
@@ -165,7 +165,7 @@ function renderIssue(i) {
165
165
  prop("severity", fhir.primitive(i.severity)),
166
166
  prop("code", fhir.primitive(fhirIssueTypeFor(i.code))),
167
167
  prop("details", details),
168
- // `diagnostics` carries the v2 source location — positional metadata, never a value.
168
+ // `diagnostics` carries the v2 source location: positional metadata, never a value.
169
169
  prop("diagnostics", fhir.primitive(i.v2Location))
170
170
  ];
171
171
  if (i.fhirPath !== void 0) {
@@ -1609,7 +1609,7 @@ var EMIT_SCHEMAS = Object.freeze([
1609
1609
  },
1610
1610
  {
1611
1611
  // Immunization: status (1..1 code), vaccineCode (1..1 CodeableConcept), patient (1..1 Reference), and
1612
- // occurrence[x] (1..1) are all required — the assembler only ever emits the occurrenceDateTime form, so
1612
+ // occurrence[x] (1..1) are all required, and the assembler only ever emits the occurrenceDateTime form, so
1613
1613
  // it is required here. An RXA with no groundable status/vaccine/occurrence, or no bundle Patient, is
1614
1614
  // withheld rather than emitted incomplete.
1615
1615
  type: "Immunization",
@@ -1621,7 +1621,7 @@ var EMIT_SCHEMAS = Object.freeze([
1621
1621
  }
1622
1622
  },
1623
1623
  {
1624
- // Appointment: status (1..1 code) and participant (1..* BackboneElement) are both required — an
1624
+ // Appointment: status (1..1 code) and participant (1..* BackboneElement) are both required, so an
1625
1625
  // Appointment whose SCH-25 status could not be grounded via HL70278, or that has no resolvable Patient
1626
1626
  // participant, is withheld rather than emitted with a guessed status or no participant.
1627
1627
  type: "Appointment",
@@ -1631,7 +1631,7 @@ var EMIT_SCHEMAS = Object.freeze([
1631
1631
  }
1632
1632
  },
1633
1633
  {
1634
- // DocumentReference: status (1..1 code) and content (1..* BackboneElement) are both required — a
1634
+ // DocumentReference: status (1..1 code) and content (1..* BackboneElement) are both required, so a
1635
1635
  // reference whose TXA-19 is not "AV" (no groundable status), or that carries no document body from its
1636
1636
  // OBX segments, is withheld rather than emitted with a guessed status or referencing nothing.
1637
1637
  type: "DocumentReference",
@@ -2647,7 +2647,7 @@ function toFhir(msg, opts = {}) {
2647
2647
  }
2648
2648
 
2649
2649
  // src/index.ts
2650
- var VERSION = "0.0.7";
2650
+ var VERSION = "0.0.9";
2651
2651
 
2652
2652
  exports.ADDRESS_TYPE_MAP = ADDRESS_TYPE_MAP;
2653
2653
  exports.ADDRESS_USE_MAP = ADDRESS_USE_MAP;