@cosyte/x12 0.0.18 → 0.1.1
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 +123 -4
- package/KNOWN-LIMITATIONS.md +435 -36
- package/README.md +268 -24
- package/dist/index.cjs +5900 -1153
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8031 -1458
- package/dist/index.d.ts +8031 -1458
- package/dist/index.mjs +5813 -1154
- package/dist/index.mjs.map +1 -1
- package/package.json +58 -27
package/CHANGELOG.md
CHANGED
|
@@ -7,8 +7,72 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.1] - 2026-10-07
|
|
11
|
+
|
|
12
|
+
**What 0.1.1 means for you.** The 271 now reads and builds the procedure a benefit line applies to
|
|
13
|
+
(EB-13), which dental payers use for per-procedure coinsurance, frequency, age and maximum limits.
|
|
14
|
+
If you build an `X12EligibilityBenefit` object literal yourself, such as a test double, add
|
|
15
|
+
`procedure: undefined` to it. Code that reads benefits from `get271` needs no change.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- **271 EB-13 read.** Each benefit's `procedure` carries the qualifier (EB-13-1), the code
|
|
20
|
+
(EB-13-2), up to four modifiers in transmitted order (EB-13-3 to EB-13-6) and the description
|
|
21
|
+
(EB-13-7) when the sender sent one, and is `undefined` when EB-13 is absent or has no qualifier.
|
|
22
|
+
Components are split on the component separator the interchange declares in ISA-16. New
|
|
23
|
+
package-root type export: `X12EligibilityProcedure`.
|
|
24
|
+
- **271 EB-13 emit.** A 271 benefit takes an optional `procedure` (`Build271ProcedureSpec`, a new
|
|
25
|
+
package-root type export) with a qualifier, a code and modifiers, written as one composite. The
|
|
26
|
+
builder refuses an empty qualifier or code, an empty modifier, and more than four modifiers: a
|
|
27
|
+
fifth would land in the description position, and an empty one would shift later modifiers on
|
|
28
|
+
read. The description is not written.
|
|
29
|
+
- The 270 reader's EQ-02 procedure decodes exactly as before and now shares its decoder with the 271.
|
|
30
|
+
|
|
31
|
+
## [0.1.0] - 2026-09-25
|
|
32
|
+
|
|
33
|
+
**What 0.1 means for you.** This release is the first whose public API we treat as settled: the
|
|
34
|
+
exported readers, builders, types and warning codes are the surface we keep stable, and the warning
|
|
35
|
+
registry only ever gains codes. It covers typed read and typed emit for 270, 271, 276, 277 and
|
|
36
|
+
277CA, 278 request and response, 820, 834, 835, 837P, 837I and 837D, 999 and TA1, plus the 277
|
|
37
|
+
request for additional information (`006020X313`) and the 275 that answers it (`006020X314`) at the
|
|
38
|
+
base 006020 structure; every amount as an exact `X12Decimal`; and a lenient parser whose only fatal
|
|
39
|
+
failures are the four structural ones. While the package is below 1.0, a breaking change bumps the
|
|
40
|
+
minor version and is called out here with its migration; a fix that changes no public value ships
|
|
41
|
+
as a patch. Upgrading from 0.0.x is itself breaking in five places, each described in the entries
|
|
42
|
+
below: `CodeListMeta`, `X12Eligibility` and `X12ProfileDescription` gain a required member,
|
|
43
|
+
`X12ClaimStatusResponse` gains a third `transactionType`, and `build278Response` declares
|
|
44
|
+
`005010X217`. Not covered yet: a byte-exact round trip in general (`KNOWN-LIMITATIONS.md` lists the
|
|
45
|
+
cases), implementation-guide usage checks and attachment decoding for the two 006020 guides,
|
|
46
|
+
non-healthcare sets, EDIFACT, transport, revisions before 005010, and any bundled CORE Code
|
|
47
|
+
Combinations table.
|
|
48
|
+
|
|
10
49
|
### Added
|
|
11
50
|
|
|
51
|
+
- **`toObject`, `toISO` and `toDate`: one date conversion surface, shared with every `@cosyte/*`
|
|
52
|
+
parser.** Three new package-root exports read a date off any typed reader, plus `DateParts`,
|
|
53
|
+
`ToDateOptions` and `X12DateValue`. Purely additive: no exported name is removed, renamed or
|
|
54
|
+
altered, `parseDocumentDate` keeps its own contract including its throwing one, no parse and no
|
|
55
|
+
emit behaviour moves, and there is still not a single runtime dependency.
|
|
56
|
+
|
|
57
|
+
The parameter is structural, so all eight `X12*Date` types are accepted through the same three
|
|
58
|
+
names with no per-transaction variant. What is decoded is exactly what this package already parses
|
|
59
|
+
or builds and nothing more: `D8`, a single `CCYYMMDD` day, and `RD8`, a `CCYYMMDD-CCYYMMDD` range.
|
|
60
|
+
`D8` converts to `{ year, month, day }` with `month` spec-native 1 to 12. Everything else answers
|
|
61
|
+
`undefined` and nothing ever throws: a qualifier outside that pair, an absent one (which is how
|
|
62
|
+
`X12PremiumDate` and `X12EnrollmentDate` reach this surface, since neither surfaces DTP-02 or
|
|
63
|
+
DTM-03 at all), a value whose digits do not match the shape its qualifier declares, and a day that
|
|
64
|
+
is not on the calendar.
|
|
65
|
+
|
|
66
|
+
**`RD8` converts to `undefined` from all three, deliberately.** An interval is not a point in
|
|
67
|
+
time; returning the first endpoint would be a quiet wrong answer with a right-looking shape.
|
|
68
|
+
**`toDate` returns an instant only when the caller states the zone**, through
|
|
69
|
+
`assumeOffsetMinutes`: no X12 date element decoded here carries a UTC offset, the host machine's
|
|
70
|
+
timezone is never read, and UTC is never assumed. A four-digit year below 100 stays that year.
|
|
71
|
+
|
|
72
|
+
Held by `test/conversion-surface.test.ts`, the same eleven-row case table every sibling parser
|
|
73
|
+
carries, five of whose rows are recorded as skips with a written reason and a live test measuring
|
|
74
|
+
the property behind each.
|
|
75
|
+
|
|
12
76
|
- **`pnpm check:no-internal-refs` - a gate on internal project bookkeeping reaching a consumer**
|
|
13
77
|
(`X12-NO-INTERNAL-REFS-GATE`), with `.github/workflows/no-internal-refs.yml` running it on every
|
|
14
78
|
push and PR. Documentation and tooling only: the `src/` half of the diff is comment-only and
|
|
@@ -48,6 +112,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
48
112
|
|
|
49
113
|
### Changed
|
|
50
114
|
|
|
115
|
+
- **`build278Response` declares `005010X217` in GS-08 and ST-03, where it declared `005010X216`.**
|
|
116
|
+
This changes what goes on the wire: a trading partner that routed a built 278 response on either
|
|
117
|
+
element sees the new value. `005010X217` is the one guide 45 CFR 162.920 names for the 278 in both
|
|
118
|
+
directions and the one `build278Request` already wrote; `005010X216` is the 278 notification
|
|
119
|
+
guide, so a receiver routing on it could file a prior authorization decision as an admission or
|
|
120
|
+
discharge notification. Only those two identifiers move: the 278 body is unchanged, and so is the
|
|
121
|
+
read side, where a 278 declaring `005010X216` still carries `X12_GUIDE_NOT_IMPLEMENTED` on both
|
|
122
|
+
278 readers. The builder's own output now reads back through `get278Response` with no
|
|
123
|
+
guide-mismatch warning, and the 278 response row of `X12_TR3_CONFORMANCE` records no divergence:
|
|
124
|
+
its `note` is `null`, like the request row's.
|
|
125
|
+
|
|
51
126
|
- **`WARNING_MESSAGES.X12_PRE_005010` stops asserting what ISA-12 declares.** The message a consumer
|
|
52
127
|
reads off `w.message` said _"ISA-12 declares a version other than the HIPAA baseline `00501`, so
|
|
53
128
|
the input may diverge from 005010 semantics. The declared version is preserved verbatim on the
|
|
@@ -340,6 +415,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
340
415
|
|
|
341
416
|
### Fixed
|
|
342
417
|
|
|
418
|
+
- **🩺 A BDS or BIN segment's binary data is framed by the octet count its length element declares,
|
|
419
|
+
not by delimiters.** A BDS-03 or BIN-02 carrying the segment terminator used to end the segment
|
|
420
|
+
inside the data, and one carrying the element or component separator was split, so an attachment
|
|
421
|
+
came back in fragments and every segment after it was mis-framed. The data element now holds
|
|
422
|
+
exactly the declared octets whatever bytes they are, `getSegmentValue` / `getAllSegmentValues` /
|
|
423
|
+
`elementValue` return it verbatim, and `serializeX12` writes the segment back unchanged in both
|
|
424
|
+
modes. Four warning codes are added, additions only, each value-free and anchored at the segment:
|
|
425
|
+
**`X12_BINARY_DATA_TRUNCATED`**, **`X12_BINARY_LENGTH_INVALID`** (no length is inferred; the
|
|
426
|
+
segment is framed by its delimiters), **`X12_BINARY_LENGTH_MISMATCH`** (the data is the declared
|
|
427
|
+
span and the bytes after it stay on `raw`) and **`X12_BINARY_LENGTH_UNVERIFIABLE`** (a string span
|
|
428
|
+
holding a character above U+00FF; a `Buffer` never raises it). The length element is never
|
|
429
|
+
rewritten, and BDS-01's filter is not applied.
|
|
430
|
+
|
|
431
|
+
- **🩺 Every typed reader now checks the guide a transaction set declares, and warns when it is not
|
|
432
|
+
one that reader implements** (`X12-17`). Each reader used to admit a transaction set on ST-01
|
|
433
|
+
alone, so a 277 declaring `006020X313` (a claim request for additional information) came back
|
|
434
|
+
from `get277Status` labelled `claim-status`, and a 004010, 006020 or 008020 document of any type
|
|
435
|
+
was decoded against 005010 positions with nothing on the warning channel.
|
|
436
|
+
|
|
437
|
+
Two warning codes are added, additions only: **`X12_GUIDE_NOT_IMPLEMENTED`** where the declared
|
|
438
|
+
guide is outside the reader's set, and **`X12_GUIDE_NOT_DECLARED`** where nothing is declared.
|
|
439
|
+
Both are anchored at the ST, both messages are frozen literals that echo nothing, and their
|
|
440
|
+
factories `guideNotImplemented` / `guideNotDeclared` take a position only. The declaration is
|
|
441
|
+
ST-03, decoded of release escapes; **where ST-03 is absent or empty, GS-08 of the enclosing group,
|
|
442
|
+
decoded the same way**, which a parsed `X12TransactionSet` now carries on an optional `gs` member so
|
|
443
|
+
every reader keeps its `(delimiters, tx)` call. Where ST-03 is non-empty it alone decides, and a
|
|
444
|
+
disagreement with GS-08 is not reconciled. The implemented set is derived from
|
|
445
|
+
`X12_TR3_CONFORMANCE` (`tr3` plus `cfrAdopted` per row), plus every identifier the 837 variant
|
|
446
|
+
table resolves. The reading is still decoded and returned; nothing is refused, dropped or
|
|
447
|
+
re-decoded, and a document declaring an implemented guide reads exactly as before.
|
|
448
|
+
|
|
449
|
+
**Three things move for a consumer.** `X12ClaimStatusResponse.transactionType` widens to
|
|
450
|
+
`"claim-status" | "claim-acknowledgment" | "unrecognized-guide"`, the third set where
|
|
451
|
+
`get277Status`'s declared guide is outside its set or absent, **so an exhaustive `switch` over the
|
|
452
|
+
old two values stops compiling**; `get277CADisposition` admits what it admitted before. **A 278
|
|
453
|
+
declaring `005010X216` now warns on both 278 readers**, since the regulation names `005010X217` for
|
|
454
|
+
both directions, and `build278Response` still writes `005010X216`, so its round trip carries the
|
|
455
|
+
code until that builder moves. A 999 declaring the base `005010X231` warns too, because the
|
|
456
|
+
conformance row names only the errata.
|
|
457
|
+
|
|
343
458
|
- **🩺 `implementationConventionReference` is POST-`?`-unescape in every typed reader that publishes
|
|
344
459
|
it** (`X12-ST03-READ-NOT-RELEASE-AWARE`). A behaviour change on any document whose `ST-03` carries
|
|
345
460
|
a release escape.
|
|
@@ -3533,10 +3648,14 @@ EV/SS`), and HL-04 has-child flag from the nested input tree, so an
|
|
|
3533
3648
|
result types. Six synthetic fixtures + unit tests + byte-flip fuzz
|
|
3534
3649
|
(never-throw outside the 4 Tier-3 fatals) across every Phase 6
|
|
3535
3650
|
fixture.
|
|
3536
|
-
- **Known limitations
|
|
3537
|
-
HSD detail, and III / LS / LE markers in
|
|
3538
|
-
claim-summary roll-ups in a 277CA Loop 2200,
|
|
3539
|
-
`tx.segments` verbatim
|
|
3651
|
+
- **Known limitations recorded for this release:** AAA
|
|
3652
|
+
request-validation segments, HSD detail, and III / LS / LE markers in
|
|
3653
|
+
the 271, plus QTY / AMT claim-summary roll-ups in a 277CA Loop 2200,
|
|
3654
|
+
are preserved on `tx.segments` verbatim, and this release carried
|
|
3655
|
+
none of them onto the typed model. The 271 AAA half of that list is
|
|
3656
|
+
CLOSED by a later release, which surfaces every AAA
|
|
3657
|
+
request-validation segment on `X12Eligibility.aaaConditions` with its
|
|
3658
|
+
level and both codes; the rest of the list stands.
|
|
3540
3659
|
- **Phase 5: 837 Healthcare Claim, TR3s `005010X222A2` (Professional),
|
|
3541
3660
|
`005010X223A3` (Institutional), `005010X224A2` (Dental).** The
|
|
3542
3661
|
claim-creation surface: the volume side of HIPAA EDI traffic.
|