@cosyte/mllp 0.0.9 → 0.0.10

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,20 +1,150 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
- Versions and publishing are managed with [Changesets](https://github.com/changesets/changesets);
8
- this file is maintained by hand (Changesets handles the version bump and publish only).
9
-
10
- ## [Unreleased]
11
-
12
- `@cosyte/mllp` is published on npm and stays on the `0.0.x`-until-first-alpha ladder, so the API can
13
- change with no deprecation cycle. The sentence this replaces said the first pre-alpha release "will
14
- ship" the surface below, which stopped being true the day the package first published and had been
15
- false ever since. **No version literal is written here, on purpose**: what is published is a fact
16
- about the registry (`npm view @cosyte/mllp version`) and about `package.json`, and a number copied
17
- into prose here is a claim that goes stale on its own.
3
+ ## 0.0.10
4
+ ### Patch Changes
5
+
6
+ - 5dbc828: `CHANGELOG.md` ships inside the tarball and is now written by the release rather than by hand, so it stops describing this package's already-published code as unreleased and starts carrying a version heading for every release.
7
+
8
+ `.changeset/config.json` set `"changelog": false`, which is a legal value that makes Changesets bump
9
+ the version and write no changelog at all. So no release ever wrote a version heading here, and
10
+ nothing ever rolled `[Unreleased]` over. Every published version of this package carried a changelog
11
+ with **no version headings in it**: one `[Unreleased]` heading spanning the entire history, and a
12
+ preamble in the future tense about a release that had already gone out. `CHANGELOG.md` is listed in
13
+ `package.json` `files`, so that was text on the disk of everyone who installed the package, not
14
+ internal bookkeeping.
15
+
16
+ **The flag is what changed, not the prose.** Correcting the sentence by hand leaves the mechanism
17
+ that wrote it, and the next release drifts the same way. `changelog` now names the generator that
18
+ ships with Changesets, so a release writes its own version heading and its own entry from the
19
+ changesets it consumed, and **the changeset summary is now the changelog entry**. Nothing new is
20
+ depended on: the generator is an entry point of `@changesets/cli`, already a dev dependency.
21
+
22
+ **The file's shape changed with it, deliberately.** Changesets prepends a release by replacing the
23
+ first newline in the file, so exactly one line can sit above generated output. The hand-written
24
+ preamble sat on line 3, which means a release would have inserted itself between the heading and the
25
+ preamble and split the header in two. The hand-maintained history has therefore moved under a
26
+ `## Released before this file was generated` heading, with an accurate preamble in place of the old
27
+ one. Four pieces of hand-workflow scaffolding were dropped and no entry was reworded or re-sorted:
28
+ the `[Unreleased]` heading, its link definition at the foot of the file, and the two empty section
29
+ stubs waiting for the next hand-written entry. The history is left as it was written rather than
30
+ split into version sections, because the file never recorded which release any entry went out in
31
+ and that text is already on disk in published copies.
32
+
33
+ **The release's Prettier pass is turned off, and that is specific to this package.** Changesets runs
34
+ the document it writes through Prettier unless `"prettier": false` says otherwise. This repo's
35
+ markdown is deliberately not Prettier-managed: `.prettierignore` lists `*.md`, so the archived
36
+ history has never been Prettier-canonical, and leaving the pass on would not tidy it but would
37
+ rewrite already-published text on every release. Measured on this archive: emphasis markers, list
38
+ bullets and continuation indents all move, and one paragraph whose bold span contains an inline code
39
+ literal ending in `**` comes back with the spaces around that literal eaten, which is corruption
40
+ inside a shipped tarball rather than a formatting preference. A sibling package that does
41
+ Prettier-manage its markdown needs the opposite setting, so this is not a value to copy between
42
+ repos.
43
+
44
+ Pinned by `test/scripts/changelog-generation.test.ts`, which runs the real `changeset version`
45
+ against the real `CHANGELOG.md` and the real config in a throwaway package rather than
46
+ reimplementing where the tool inserts text, and which builds that config from the committed one so a
47
+ setting added later is exercised instead of silently diverging. Ten of its thirteen cases are red
48
+ against the previous state, measured on the tree this change was written against rather than
49
+ recalled. **The rule it enforces is that nothing but the H1 sits above the first heading, and it is
50
+ asserted on the released document as well as the committed one**: a rule phrased as "the archive
51
+ heading comes second" holds only until the first release writes its own version heading there, which
52
+ would have redded the first Version PR this configuration ever opened, and `prepublishOnly` runs the
53
+ same suite under `changeset publish`. Version headings are compared as whole headings and never as
54
+ substrings, which the suite demonstrates rather than asserts: on a released document whose only
55
+ version heading is `## 0.0.10`, a substring search for `## 0.0.1` answers yes to a heading that is
56
+ not there. Three controls: the same inputs with `"changelog": false` must write no version heading
57
+ at all, so the flag is proved load-bearing rather than incidental; the same inputs with the Prettier
58
+ pass back on must change the archived history, so that setting is proved load-bearing too; and the
59
+ old file shape must reproduce the split header, so the shape rule is demonstrated rather than
60
+ asserted.
61
+
62
+ One upstream behaviour is worth knowing before debugging a release, and is recorded in that file:
63
+ Changesets wraps the changelog write in a try/catch that only warns. A tree whose declared Prettier
64
+ config cannot be resolved bumps the version, consumes the changeset, and writes no changelog at all.
65
+ Reproduced here on this repo's inputs. A release that publishes with an unchanged changelog is that
66
+ failure, not a setting that quietly reverted.
67
+
68
+ `.changeset/README.md` said to keep the release notes in `CHANGELOG.md` by hand and now says to
69
+ write them in the changeset. No runtime code, no public API, no warning code, no framing or
70
+ transport behaviour changed.
71
+ - b713572: Scan the test sources the PHI commit-gate was never opening, and stop one healthy scan root
72
+ vouching for an empty one. Tooling and documentation only: no runtime code, no public API and no
73
+ published artifact changes.
74
+
75
+ **A `.ts` source under the fixture root was scanned by neither route.** The walk covered all of
76
+ `test/`, but the read filter then dropped every `.ts` file under it, and this repo's fixture corpus
77
+ is overwhelmingly `.ts`: 72 of the 76 tracked files under `test/` were removed from the gate,
78
+ leaving it standing on three framed binaries, and twelve of the 72 carried inline `PID|` literals.
79
+ This is an HL7 transport package, so a message written straight into a test source is the most
80
+ common fixture shape here, not an exotic one.
81
+
82
+ **The remedy is two-sided, and either half alone would have shipped nothing.** Widening the
83
+ enumeration is the visible half: a blanket extension rule cannot tell a file that carries violator
84
+ literals on purpose from one that carries them by accident, so it is replaced by an explicit
85
+ per-path exemption, which is a reviewed act exactly like adding an allow-list token. The half that
86
+ actually finds anything is recognition. Every detector in the scanner assumed the file **is** the
87
+ document and worked from a segment id at the start of a line, so in a source whose lines start with
88
+ `const` or with a quote it matched nothing: a probe carrying a full patient identity in a string
89
+ literal exited 0 "OK, no hits" even when named explicitly on the command line, which bypasses the
90
+ enumeration entirely, while the identical payload written to a message file reported all five
91
+ fields. Embedded segments are now recovered out of string literals, with TypeScript escapes
92
+ resolved so the encoding characters an HL7 header declares are the ones the scanner reads, and with
93
+ a template placeholder neither guessed at nor dropped. The recogniser anchors on the HL7 field
94
+ separator: anchoring on any delimiter turns prose and identifiers into segments and would have
95
+ driven the name backstop over English words, and a gate that reds on prose is a gate someone turns
96
+ off. Five synthetic tokens the widening made visible for
97
+ the first time, including a deliberate leak canary, are now declared in the allow-list.
98
+
99
+ **A sweep that observed nothing under one root still reported OK.** The guard that refuses an
100
+ empty sweep counted every root together, so it could only fire when all of them came back empty and
101
+ one healthy root masked an empty one indefinitely: with the fixture root emptied, the run printed
102
+ "OK, no hits" and exited 0 on the strength of the source root alone, under a comment asserting the
103
+ sweep "always reaches" the fixture corpus that nothing checked. A denominator would not have caught
104
+ it and would have looked like a fix, because a count counts the roots that did exist. The check is
105
+ now per root and keyed on the roots the walk actually entered, so an absent root stays legitimate
106
+ while an emptied one refuses.
107
+
108
+ **An unmerged path was enumerated by nothing.** It has no single staged blob, so the status filter
109
+ deletes the record and the pre-commit route printed "OK, no hits" over a conflicted fixture whose
110
+ both sides carried live-shaped identifiers. It now refuses, because neither side of a conflict is
111
+ what a commit would contain, so reading one would be a claim about content that may never exist.
112
+ Minor, and honestly so: the commit itself is refused earlier by git, so this is not a route by
113
+ which anything reaches a commit. What it fixes is the gate answering a question it cannot answer.
114
+
115
+ 17 new cases, 11 of which run red against the base scanner; the other six are controls that pin
116
+ the absence of false positives, the `src/` decision that this work deliberately does not reverse,
117
+ and the recogniser's disclosed limits.
118
+
119
+ ## Released before this file was generated
120
+
121
+ Every release section above this heading is written by
122
+ [Changesets](https://github.com/changesets/changesets) from the changesets in `.changeset/`, newest
123
+ release first. The release writes its own version heading, so nothing above this line is maintained
124
+ by hand, and a change is recorded by adding a changeset rather than by editing this file.
125
+
126
+ Everything below this heading was maintained by hand. It sat under a single `[Unreleased]` heading
127
+ that no release ever rolled over, which is why a published tarball went on describing its own
128
+ already-shipped contents as unreleased. That is also why the sections below repeat: entries were
129
+ appended in waves under a fresh set of `### Added` / `### Fixed` headings, and the file never
130
+ recorded which release any wave went out in. The history is therefore left exactly as it was
131
+ written rather than re-sorted into version sections, because there is nothing in it to sort by, and
132
+ because this is the text that installed copies already carry on disk.
133
+
134
+ Only four things were dropped, all of them scaffolding for the hand-written workflow that no longer
135
+ runs: the `[Unreleased]` heading itself, its link definition at the foot of the file, and the two
136
+ empty section stubs (`### Deprecated` and the final `### Security`) that were waiting to receive the
137
+ next hand-written entry. A note that used to sit under `[Unreleased]`, explaining that an earlier
138
+ future-tense sentence had been corrected in place, is replaced by this heading and these
139
+ paragraphs. No entry was reworded.
140
+
141
+ The entries below follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the generated
142
+ sections above use the format Changesets writes, which is a version heading and a list of the
143
+ changes that release consumed. Versions follow the cosyte pre-alpha ladder, `0.0.x` until first
144
+ alpha, rather than [Semantic Versioning](https://semver.org/spec/v2.0.0.html) alone, so the API can
145
+ change with no deprecation cycle. **No version literal is written in this preamble, on purpose**:
146
+ what is published is a fact about the registry (`npm view @cosyte/mllp version`) and about
147
+ `package.json`, and a number copied into prose here is a claim that goes stale on its own.
18
148
 
19
149
  ### Added
20
150
 
@@ -80,6 +210,65 @@ into prose here is a claim that goes stale on its own.
80
210
 
81
211
  ### Security
82
212
 
213
+ - **The PHI scanner reads the test sources it was never opening, and refuses per walk root
214
+ (`PHI-SCAN-WALK-ROOT-SCOPE`, `PHI-SCAN-OBSERVED-NOTHING-IS-GLOBAL`).** Both reproduced on
215
+ `3daf2e9` before anything was touched. This repo's scope was **re-derived, not ported**: its walk
216
+ roots are `test/` and `src/` (a sibling roots at `test/fixtures/` instead), and the two refusal
217
+ defects a sibling still carries are **not open here**, because a regular-file root, a dangling
218
+ link at a root and a missing allow-list already exit **2**.
219
+
220
+ **The `.ts` hole.** `test/` was walked in full and then every `.ts` file under it was dropped by
221
+ the read filter, so a tracked test source reached **neither** route. Measured: `test/` tracked 72
222
+ `.ts` files, 3 `.frame.bin` and 1 `.md`, so the exclusion removed **72 of 76** tracked files and
223
+ left the gate standing on three framed binaries; **twelve** of the 72 carried inline `PID|`
224
+ literals. For a transport package whose subject matter is HL7 v2, a message written straight into
225
+ a test source is the ordinary fixture shape.
226
+
227
+ **The remedy is two-sided, and the enumeration is the half that finds nothing on its own.** A
228
+ blanket extension rule cannot distinguish a file carrying violator literals **on purpose** from
229
+ one carrying them **by accident**, so it is replaced by an explicit per-path exemption. But every
230
+ detector assumed **the file is the document** and worked from a segment id at the START of a
231
+ line, which in a source beginning with `const` or a quote matches nothing: measured, a probe
232
+ carrying a full `PID` in a string literal exited **0** `OK, no hits` even when **named explicitly
233
+ on argv**, which bypasses the enumeration entirely, while the identical payload written to a
234
+ `.hl7` file reported all five fields (`PID-3` / `PID-5` / `PID-7` / `PID-11` and the address).
235
+ Segments are now recovered from string literals with TypeScript escapes resolved, so a doubled
236
+ backslash in a header yields the real encoding characters, and a template placeholder is replaced
237
+ by a non-letter, non-digit token rather than guessed at or dropped. The recogniser anchors on the
238
+ HL7 **field separator**: anchoring on any delimiter, which is what the file-level rule does,
239
+ matched prose and identifiers (`ack-from-hl7`, `ERR_MODULE_NOT_FOUND`, `net.createConnection`),
240
+ which would have driven the unknown-segment name backstop over English words. **No match count is
241
+ recorded, on purpose:** that figure was wrong four times and is not reproducible by an independent
242
+ reader, because raw anchor matches are an upper bound on extractor runs rather than the same
243
+ quantity. The derivation command is in `documentation/agent-notes.md`. On this corpus every
244
+ recovered id is a real segment id,
245
+ **which is an observation about the tree and not a property of the rule**: the recogniser cannot
246
+ tell a comment from a literal, and a clean Z-segment written in one does red. The anchor covers
247
+ four spellings, including a **real newline**, without which the idiomatic multi-line template
248
+ literal yielded only its header run, and header segments are skipped, so a full patient identity
249
+ on the following lines reported clean. A run ends on the quote that OPENED it, because ending on
250
+ any quote truncated a name at an apostrophe and silently dropped every field after it. Five
251
+ synthetic tokens surfaced for the first time, including a deliberate leak canary, are now declared
252
+ in `scripts/phi-allow-list.txt`. Disclosed cost, three things rather than one: a custom field
253
+ separator is not recovered at all, an escape-anchored run still ends at the first quote of any
254
+ kind, and a segment split across a concatenation is recovered only as far as the first operand.
255
+
256
+ **The observed-nothing guard was global.** It counted every root together, so it could fire only
257
+ when all of them came back empty and one healthy root masked an empty one indefinitely. Measured:
258
+ with `test/` emptied and `src/` intact, the sweep printed `OK, no hits` and exited **0**, under a
259
+ comment asserting that `all` mode "always reaches" the fixture corpus, which nothing checked. **A
260
+ denominator is not the remedy and would have looked like one**, because a count counts the roots
261
+ that did exist. The check is now per root and keyed on the roots the walk actually entered, so an
262
+ **absent** root stays legitimate while an **emptied** one refuses (exit 2, naming the root).
263
+
264
+ **An unmerged path was enumerated by nothing.** It has no single staged blob, so
265
+ `--diff-filter=AMT` deletes the record; measured, `--staged` exited **0** over a conflicted
266
+ fixture whose both stages carried live-shaped `PID-3` / `PID-5` / `PID-7` values. It refuses now,
267
+ and reading a stage is refused as the remedy because neither side of a conflict is what a commit
268
+ would contain. Scoped honestly as **minor**: `git commit` refuses an unmerged index before the
269
+ hook runs, so this is not a route by which anything reaches a commit, and what it fixes is the
270
+ gate answering a question it cannot answer.
271
+
83
272
  - **The PHI scanner enumerates a staged rename, reads a blob that replaced a walk root, and
84
273
  refuses a root that is not a directory (`PHI-SCAN-RENAME-BLIND-AT-PRECOMMIT`).** Three holes, two
85
274
  of them on `--staged`, which is this repo's **pre-commit** gate. All three were reproduced on
@@ -1170,8 +1359,6 @@ into prose here is a claim that goes stale on its own.
1170
1359
 
1171
1360
  - **`mitata` benchmark dependency and the `bench` script**: the script had no benchmark files.
1172
1361
 
1173
- ### Deprecated
1174
-
1175
1362
  ### Fixed
1176
1363
 
1177
1364
  - **Bind errors no longer crash a server with no `'error'` listener (Phase 8 residuals, MLLP-8.1).**
@@ -1201,7 +1388,3 @@ into prose here is a claim that goes stale on its own.
1201
1388
  `'listening'`/`'securityWarning'` handler is deliberately **too late**: the bind wins and
1202
1389
  `listen()` resolves (use `close()` to shut down), so aborted-mid-emit can never strand
1203
1390
  `listening: true` on a closed socket.
1204
-
1205
- ### Security
1206
-
1207
- [Unreleased]: https://github.com/cosyte/mllp/commits/main
package/dist/index.cjs CHANGED
@@ -3438,7 +3438,7 @@ async function createStarterClient(opts) {
3438
3438
  }
3439
3439
 
3440
3440
  // src/index.ts
3441
- var VERSION = "0.0.9";
3441
+ var VERSION = "0.0.10";
3442
3442
 
3443
3443
  exports.Connection = Connection;
3444
3444
  exports.FrameReader = FrameReader;