@cosyte/x12 0.0.16 → 0.0.17
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 +102 -0
- package/KNOWN-LIMITATIONS.md +64 -0
- package/dist/index.cjs +15 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -30
- package/dist/index.d.ts +84 -30
- package/dist/index.mjs +15 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,8 +7,110 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **🩺 `X12_ISA_EXTRA_ELEMENT_SEPARATOR` - the ISA element split now has an arity check**
|
|
13
|
+
(`X12-ISA-ELEMENT-ARITY`). Additions-only: no decoding moved, no existing warning was suppressed or
|
|
14
|
+
narrowed, and nothing is re-framed.
|
|
15
|
+
|
|
16
|
+
`decodeIsa` splits the 105-byte ISA element area on the element separator, and the comment above it
|
|
17
|
+
asserted the result was "exactly 17 entries by construction because the element-separator-position
|
|
18
|
+
guard in `delimiters.ts` already verified the layout". That guard verifies the separator at all 16
|
|
19
|
+
fixed 005010 positions, which bounds the split from BELOW - it can never come out short. It was
|
|
20
|
+
never bounded from above. An ISA element value carrying that same byte splits again, so the element
|
|
21
|
+
comes back a prefix and everything after it is displaced. **How far is not derivable from
|
|
22
|
+
`isa.elements`**: more than one element can carry an extra separator, and one sitting between two
|
|
23
|
+
of them is displaced less than one sitting after both. `isa.raw` plus the ISA's fixed widths is the
|
|
24
|
+
only route back.
|
|
25
|
+
|
|
26
|
+
Planting the element separator inside each of the 16 fixed elements of one conformant interchange,
|
|
27
|
+
measured at `0.0.16`:
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
planted in split parts warnings at 0.0.16
|
|
31
|
+
ISA-01..10 18 X12_PRE_005010, X12_CONTROL_NUMBER_MISMATCH
|
|
32
|
+
ISA-11 - X12_INVALID_DELIMITERS (Tier-3, reached first)
|
|
33
|
+
ISA-12 18 X12_PRE_005010, X12_CONTROL_NUMBER_MISMATCH
|
|
34
|
+
ISA-13 18 X12_CONTROL_NUMBER_MISMATCH
|
|
35
|
+
ISA-14 18 (none)
|
|
36
|
+
ISA-15 18 (none)
|
|
37
|
+
ISA-16 - X12_INVALID_DELIMITERS (Tier-3, reached first)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
ISA-11 and ISA-16 ARE the in-band repetition and component separator declarations, so the plant
|
|
41
|
+
collides with them and the Tier-3 refusal reaches it before any of this. That is a boundary of the
|
|
42
|
+
probe, not a property of those two elements.
|
|
43
|
+
|
|
44
|
+
On the `ISA-01..10` and `ISA-12` rows the document declares `00501` at ISA-12's own fixed offset and
|
|
45
|
+
`X12_PRE_005010` fires anyway; `elements[13]`, the interchange control number and the reassociation
|
|
46
|
+
key, answers `"00501"`; and `elements[15]`, the test/production usage indicator, answers `"0"` on a
|
|
47
|
+
document whose ISA-15 says `P`. The `ISA-14` and `ISA-15` rows were reported on no channel at all.
|
|
48
|
+
|
|
49
|
+
`parseX12` raises the new warning BEFORE the ISA-derived checks that read `elements[12]` and
|
|
50
|
+
`elements[13]` by index, because when it fires those two may be reading a displaced element rather
|
|
51
|
+
than the one they name. `{ strict: true }` therefore escalates on it rather than on the warning that
|
|
52
|
+
follows. **That is a statement about `ix.warnings` and `onWarning` only.** `serializeX12(ix,
|
|
53
|
+
{ specClean: true })` reconciles ISA-13 against IEA-02 off `isa.elements[13]` with no arity
|
|
54
|
+
awareness and never raises this code, so on that channel a lone `X12_CONTROL_NUMBER_MISMATCH` can be
|
|
55
|
+
a displaced read of a control number that matches byte for byte at its fixed span, and the absence
|
|
56
|
+
of this warning there is not evidence the header framed. Pre-existing, disclosed, not fixed.
|
|
57
|
+
|
|
58
|
+
**It is a report, not a repair.** A byte that is both an element's content under the ISA's fixed
|
|
59
|
+
widths and the separator that same segment declares in-band has two readings; the interchange is
|
|
60
|
+
not 005010-conformant either way, and nothing anyone here has read settles which reading to take,
|
|
61
|
+
so `isa.elements` is left exactly as the split produced it and `isa.raw` still carries all 106
|
|
62
|
+
bytes, which is the route back.
|
|
63
|
+
|
|
64
|
+
**The emit side is untouched, and this is not a build-side guard.** The fixed-width ISA slots go
|
|
65
|
+
through `pad` / `padControl` and never through the caller escaper, so `buildInterchange` still
|
|
66
|
+
writes such a caller value straight onto the wire. It does now surface there, because
|
|
67
|
+
`buildInterchange` returns `parseX12` of the bytes it just wrote:
|
|
68
|
+
`interchangeControlNumber: "0000*0001"` came back with ISA-13 reading `"0000"`, ISA-15 reading
|
|
69
|
+
`"0"` instead of `"P"`, IEA-02 displaced the same way so the control-number reconciliation agreed
|
|
70
|
+
with the misreading, and `warnings: []`.
|
|
71
|
+
|
|
10
72
|
### Documentation
|
|
11
73
|
|
|
74
|
+
- **`IeaSegment`, `GsSegment`, `GeSegment`, `Ta1Segment` and `X12FunctionalGroup` no longer document
|
|
75
|
+
a raw envelope element as the value it holds** (`X12-ENVELOPE-VALUE-EXAMPLES`). **Documentation
|
|
76
|
+
only: the diff is comment-only, and `dist/index.mjs` and `dist/index.cjs` are byte-identical
|
|
77
|
+
before and after, so no behaviour moved.** The corrected text ships in `dist/index.d.ts`.
|
|
78
|
+
|
|
79
|
+
**Element values on those segment types are stored RAW, pre-`?`-unescape.** Their JSDoc named the
|
|
80
|
+
index and then named the value it holds - "IEA-02 interchange control number - must match ISA-13",
|
|
81
|
+
"GS-06 group control number", "TA1-01 (echoes the prior interchange's ISA-13 control number)" -
|
|
82
|
+
and repeated the promise inside an `@example` fence, which is the form a consumer copies. Measured
|
|
83
|
+
on one interchange where each documented non-ISA slot carries the sender-intended value `A*B`,
|
|
84
|
+
written on the wire in released form `A?*B`, **every one of those cells reads back `"A?*B"` and
|
|
85
|
+
not `"A*B"`.**
|
|
86
|
+
|
|
87
|
+
The same correction is applied to `parseX12`'s own `@example`, which handed out `// "HC"` at
|
|
88
|
+
`gs.elements[1]`.
|
|
89
|
+
|
|
90
|
+
On a `TA1` that `buildTA1` emitted and `parseX12` read back:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
stated TA1-01 emitted TA1 elements[1] parseTA1()
|
|
94
|
+
"000000001" TA1*000000001*260601*1200*A*000 "000000001" "000000001"
|
|
95
|
+
"0000*0001" TA1*0000?*0001*260601*1200*A*000 "0000?*0001" "0000*0001"
|
|
96
|
+
"0000~0001" TA1*0000?~0001*260601*1200*A*000 "0000?~0001" "0000~0001"
|
|
97
|
+
"00000001?" TA1*00000001??*260601*1200*A*000 "00000001??" "00000001?"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Every row `warnings: []`. TA1-01 is the reassociation key, so a consumer following the shipped
|
|
101
|
+
`@example` compared framed bytes against the ISA-13 it was acknowledging. `parseTA1` already read
|
|
102
|
+
the same bytes correctly, so the package disagreed with itself.
|
|
103
|
+
|
|
104
|
+
**Each block now carries the label `X12Segment` has always carried** - "Element values are stored
|
|
105
|
+
RAW, pre-`?`-unescape" - and the `@example` pointers keep their index and lose the promise. The
|
|
106
|
+
slot mapping (`elements[2]` = IEA-02) was true and is kept, and so are the element names, the
|
|
107
|
+
formats and the code-list citations, restated where they are true: as facts about the segment's
|
|
108
|
+
fields rather than about a raw element. No accessor, guard or test was added, and **no route is
|
|
109
|
+
named as the route.**
|
|
110
|
+
|
|
111
|
+
**The `IsaSegment` block is deliberately unchanged**, and no rule about the ISA is stated here in
|
|
112
|
+
either direction.
|
|
113
|
+
|
|
12
114
|
- **The documentation carriers that told you to read an envelope element's logical value through
|
|
13
115
|
`getSegmentValue` are corrected** (`X12-ENVELOPE-VALUE-ROUTES`). **Documentation and tests only: no
|
|
14
116
|
runtime line changed and the emitted JS is byte-identical, so no behaviour moved.**
|
package/KNOWN-LIMITATIONS.md
CHANGED
|
@@ -16,6 +16,70 @@ model.
|
|
|
16
16
|
|
|
17
17
|
## Data / decode boundaries
|
|
18
18
|
|
|
19
|
+
- **🩺 An ISA element carrying the element separator makes the header split into more than
|
|
20
|
+
`ISA` + 16 elements. Through `0.0.16` no check saw that at all; it is now reported as
|
|
21
|
+
`X12_ISA_EXTRA_ELEMENT_SEPARATOR`** (`X12-ISA-ELEMENT-ARITY`). **Nothing is
|
|
22
|
+
re-framed: this is a report, not a repair, and no existing warning was suppressed or narrowed.**
|
|
23
|
+
|
|
24
|
+
`detectDelimiters` verifies the element separator at all 16 fixed 005010 byte positions, so the
|
|
25
|
+
ISA split can never come out SHORT. It was never bounded from ABOVE. An element value carrying
|
|
26
|
+
that same byte splits again, so that element comes back a **prefix** and everything after it is
|
|
27
|
+
**displaced**. **🛑 How far is NOT derivable from `isa.elements`, and no arithmetic on it works.**
|
|
28
|
+
More than one element can carry an extra separator, and an element sitting between two of them is
|
|
29
|
+
displaced less than one sitting after both; `isa.raw` plus the ISA's fixed widths is the only route
|
|
30
|
+
back. Measured on one conformant interchange, planting the element separator inside each of the 16
|
|
31
|
+
fixed elements in turn:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
planted in split parts warnings through 0.0.16
|
|
35
|
+
ISA-01..10 18 X12_PRE_005010, X12_CONTROL_NUMBER_MISMATCH
|
|
36
|
+
ISA-11 - X12_INVALID_DELIMITERS (Tier-3, thrown before any of this)
|
|
37
|
+
ISA-12 18 X12_PRE_005010, X12_CONTROL_NUMBER_MISMATCH
|
|
38
|
+
ISA-13 18 X12_CONTROL_NUMBER_MISMATCH
|
|
39
|
+
ISA-14 18 (none)
|
|
40
|
+
ISA-15 18 (none)
|
|
41
|
+
ISA-16 - X12_INVALID_DELIMITERS (Tier-3, thrown before any of this)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
ISA-11 and ISA-16 **are** the in-band repetition and component separator declarations, so planting
|
|
45
|
+
the element separator there collides with them and the Tier-3 refusal reaches it first. That is a
|
|
46
|
+
boundary of the probe, not a claim that those two elements are special.
|
|
47
|
+
|
|
48
|
+
**What the displaced reads cost, on the rows that had a warning as much as on the rows that had
|
|
49
|
+
none.** On the `ISA-01..10` and `ISA-12` rows the interchange declares `00501` at ISA-12's own
|
|
50
|
+
fixed offset and `X12_PRE_005010` fires anyway, because `elements[12]` has become the repetition
|
|
51
|
+
separator; `elements[13]`, the interchange control number and the reassociation key, answers
|
|
52
|
+
`"00501"`; and `elements[15]`, the test/production usage indicator, answers `"0"` on a document
|
|
53
|
+
whose ISA-15 says `P`. On the `ISA-13` row the control number is a prefix. **The `ISA-14` and
|
|
54
|
+
`ISA-15` rows carried `warnings: []`** while the usage indicator read empty.
|
|
55
|
+
|
|
56
|
+
**The route back is `isa.raw`**, which still carries all 106 bytes verbatim, and the ISA's fixed
|
|
57
|
+
widths make the transmitted span of any element recoverable from it. Recovering it is **your**
|
|
58
|
+
decision, not this library's: a byte that is both an element's content under those fixed widths and
|
|
59
|
+
the separator the same segment declares in-band has two readings, the interchange is not
|
|
60
|
+
005010-conformant either way, and nothing anyone here has read says which reading to take. So the
|
|
61
|
+
parser reports that the header did not frame and leaves `isa.elements` exactly as the split
|
|
62
|
+
produced it.
|
|
63
|
+
|
|
64
|
+
**When this warning is present, treat every other ISA-derived diagnostic on that interchange as
|
|
65
|
+
provisional.** `parseX12` raises it ahead of the ones it raises after it for that reason, which
|
|
66
|
+
also means `{ strict: true }` escalates on it rather than on the displaced-value warning that
|
|
67
|
+
follows. **🩺 Read that as a statement about `ix.warnings` and `onWarning`, and about nothing
|
|
68
|
+
else.** `serializeX12(ix, { specClean: true })` reconciles ISA-13 against IEA-02 off
|
|
69
|
+
`isa.elements[13]` with no arity awareness and **never raises this code at all**, so on that
|
|
70
|
+
channel a lone `X12_CONTROL_NUMBER_MISMATCH` can be a displaced read of a control number that in
|
|
71
|
+
fact matches byte for byte at its fixed span, and **the absence of this warning there is not
|
|
72
|
+
evidence the header framed**. Pre-existing, disclosed, not fixed here.
|
|
73
|
+
|
|
74
|
+
**The emit side is unchanged and is NOT guarded by this.** The fixed-width ISA slots go through
|
|
75
|
+
`pad` / `padControl` and never through the caller escaper, so `buildInterchange` still writes a
|
|
76
|
+
caller value carrying the element separator straight onto the wire. What changed is that
|
|
77
|
+
`buildInterchange` returns `parseX12` of the bytes it just wrote, so the report now reaches that
|
|
78
|
+
path too: `interchangeControlNumber: "0000*0001"` used to come back with ISA-13 reading `"0000"`,
|
|
79
|
+
ISA-15 reading `"0"` instead of `"P"`, IEA-02 displaced the same way so the control-number
|
|
80
|
+
reconciliation **agreed with the misreading**, and `warnings: []`. Refusing such a value on emit is
|
|
81
|
+
a decision about the build side and is not taken here.
|
|
82
|
+
|
|
19
83
|
- **🩺 `parseTA1`'s five decoded fields are POST-`?`-unescape as of this release, and an EMPTY
|
|
20
84
|
TA1-02 / TA1-03 / TA1-04 / TA1-05 is REFUSED on emit. Both are behaviour changes**
|
|
21
85
|
(`X12-TA1-RESIDUALS`). They are one slice because they are the two ends of the same disagreement:
|
package/dist/index.cjs
CHANGED
|
@@ -121,6 +121,7 @@ function detectDelimiters(raw) {
|
|
|
121
121
|
var WARNING_CODES = {
|
|
122
122
|
X12_CONTROL_NUMBER_MISMATCH: "X12_CONTROL_NUMBER_MISMATCH",
|
|
123
123
|
X12_PRE_005010: "X12_PRE_005010",
|
|
124
|
+
X12_ISA_EXTRA_ELEMENT_SEPARATOR: "X12_ISA_EXTRA_ELEMENT_SEPARATOR",
|
|
124
125
|
X12_GROUP_COUNT_MISMATCH: "X12_GROUP_COUNT_MISMATCH",
|
|
125
126
|
X12_TRANSACTION_COUNT_MISMATCH: "X12_TRANSACTION_COUNT_MISMATCH",
|
|
126
127
|
X12_SEGMENT_COUNT_MISMATCH: "X12_SEGMENT_COUNT_MISMATCH",
|
|
@@ -196,6 +197,7 @@ var WARNING_MESSAGES = {
|
|
|
196
197
|
X12_CONTROL_NUMBER_MISMATCH_GROUP: "Control number mismatch: GS-06 (header) and GE-02 (trailer) disagree. Both values are preserved verbatim on the model; neither is echoed here.",
|
|
197
198
|
X12_CONTROL_NUMBER_MISMATCH_TRANSACTION: "Control number mismatch: ST-02 (header) and SE-02 (trailer) disagree. Both values are preserved verbatim on the model; neither is echoed here.",
|
|
198
199
|
X12_PRE_005010: 'ISA-12 declares a version other than the HIPAA baseline "00501", so the input may diverge from 005010 semantics. The declared version is preserved verbatim on the model.',
|
|
200
|
+
X12_ISA_EXTRA_ELEMENT_SEPARATOR: "The ISA element area carries at least one element separator beyond the 16 sitting at their fixed 005010 byte positions, so the header does not split into `ISA` plus 16 elements. `isa.elements` is that split as it came out: an element containing an extra separator comes back a prefix and everything after it is displaced. How far is NOT derivable from `isa.elements`, so any other ISA-derived diagnostic on this interchange may be reporting a displaced value. All 106 bytes are preserved verbatim on `isa.raw`, which with the ISA's fixed widths is the route back. Which reading is correct - the byte as data under the ISA's fixed widths, or as a separator - is NOT decided here, and nothing is re-framed.",
|
|
199
201
|
X12_GROUP_COUNT_MISMATCH: "IEA-01 does not equal the number of GS..GE groups actually present in the interchange. The declared count is preserved verbatim on the model and is NEVER silently corrected.",
|
|
200
202
|
X12_TRANSACTION_COUNT_MISMATCH: "GE-01 does not equal the number of ST..SE transaction sets actually present in the group. The declared count is preserved verbatim on the model and is NEVER silently corrected.",
|
|
201
203
|
X12_SEGMENT_COUNT_MISMATCH: "SE-01 does not equal the number of segments actually present in the transaction set it closes. The declared count is preserved verbatim and is corrected only on `serializeX12(ix, { specClean: true, recomputeCounts: true })`.",
|
|
@@ -284,6 +286,13 @@ function pre005010(position) {
|
|
|
284
286
|
position
|
|
285
287
|
};
|
|
286
288
|
}
|
|
289
|
+
function isaExtraElementSeparator(position) {
|
|
290
|
+
return {
|
|
291
|
+
code: WARNING_CODES.X12_ISA_EXTRA_ELEMENT_SEPARATOR,
|
|
292
|
+
message: WARNING_MESSAGES.X12_ISA_EXTRA_ELEMENT_SEPARATOR,
|
|
293
|
+
position
|
|
294
|
+
};
|
|
295
|
+
}
|
|
287
296
|
function groupCountMismatch(position) {
|
|
288
297
|
return {
|
|
289
298
|
code: WARNING_CODES.X12_GROUP_COUNT_MISMATCH,
|
|
@@ -1048,6 +1057,7 @@ function collectElementValues(seg, start, end, delimiters) {
|
|
|
1048
1057
|
}
|
|
1049
1058
|
|
|
1050
1059
|
// src/parser/envelope.ts
|
|
1060
|
+
var ISA_DECODED_PART_COUNT = 17;
|
|
1051
1061
|
function decodeIsa(raw, delimiters) {
|
|
1052
1062
|
const isaRaw = raw.slice(0, ISA_MIN_LENGTH);
|
|
1053
1063
|
const isaHead = isaRaw.slice(0, ISA_MIN_LENGTH - 1);
|
|
@@ -1114,6 +1124,9 @@ function decodeEnvelope(raw, delimiters) {
|
|
|
1114
1124
|
warnings.push(w);
|
|
1115
1125
|
};
|
|
1116
1126
|
const isa = decodeIsa(raw, delimiters);
|
|
1127
|
+
if (isa.elements.length !== ISA_DECODED_PART_COUNT) {
|
|
1128
|
+
warnings.push(isaExtraElementSeparator({ segmentIndex: 0, interchangeIndex: 0 }));
|
|
1129
|
+
}
|
|
1117
1130
|
const isa12 = el(isa.elements, 12);
|
|
1118
1131
|
if (isa12 !== "00501") {
|
|
1119
1132
|
warnings.push(pre005010({ segmentIndex: 0, interchangeIndex: 0, elementIndex: 12 }));
|
|
@@ -10723,7 +10736,7 @@ var profiles = Object.freeze({
|
|
|
10723
10736
|
});
|
|
10724
10737
|
|
|
10725
10738
|
// src/index.ts
|
|
10726
|
-
var VERSION = "0.0.
|
|
10739
|
+
var VERSION = "0.0.17";
|
|
10727
10740
|
|
|
10728
10741
|
exports.ACK_BUILD_ERROR_CODES = ACK_BUILD_ERROR_CODES;
|
|
10729
10742
|
exports.ALL_WARNING_MESSAGES = ALL_WARNING_MESSAGES;
|
|
@@ -10885,6 +10898,7 @@ exports.isAcceptDisposition = isAcceptDisposition;
|
|
|
10885
10898
|
exports.isClaimAdjustmentGroupCode = isClaimAdjustmentGroupCode;
|
|
10886
10899
|
exports.isDiagnosisQualifier = isDiagnosisQualifier;
|
|
10887
10900
|
exports.isProcedureQualifier = isProcedureQualifier;
|
|
10901
|
+
exports.isaExtraElementSeparator = isaExtraElementSeparator;
|
|
10888
10902
|
exports.lookupCarc = lookupCarc;
|
|
10889
10903
|
exports.lookupClaimStatus = lookupClaimStatus;
|
|
10890
10904
|
exports.lookupClaimStatusCategory = lookupClaimStatusCategory;
|