@cosyte/x12 0.0.2 → 0.0.4

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/dist/index.mjs CHANGED
@@ -103,7 +103,10 @@ function detectDelimiters(raw) {
103
103
  if (raw.charAt(pos) !== element) {
104
104
  throw new X12ParseError(
105
105
  FATAL_CODES.X12_INVALID_DELIMITERS,
106
- `Element separator "${element}" was detected at ISA byte 4 but is missing at fixed ISA byte ${String(pos + 1)} - ISA element layout is not 005010-conformant.`,
106
+ // The detected separator is a single byte read out of the input, so
107
+ // it is not echoed: the diagnostic is which fixed ISA position broke,
108
+ // and the byte itself is at `raw[3]` for anyone who needs it.
109
+ `The element separator detected at ISA byte 4 is missing at fixed ISA byte ${String(pos + 1)} - ISA element layout is not 005010-conformant.`,
107
110
  fatalPosition,
108
111
  snip
109
112
  );
@@ -137,182 +140,256 @@ var WARNING_CODES = {
137
140
  X12_UNKNOWN_CLAIM_STATUS: "X12_UNKNOWN_CLAIM_STATUS",
138
141
  X12_834_UNKNOWN_MAINTENANCE_TYPE: "X12_834_UNKNOWN_MAINTENANCE_TYPE"
139
142
  };
140
- function controlNumberMismatch(position, pair, header, trailer) {
143
+ var CONTROL_NUMBER_PAIRS = {
144
+ /** ISA-13 against IEA-02. */
145
+ INTERCHANGE: "interchange",
146
+ /** GS-06 against GE-02. */
147
+ GROUP: "group",
148
+ /** ST-02 against SE-02. */
149
+ TRANSACTION: "transaction"
150
+ };
151
+ var UNEXPECTED_SEGMENT_CONTEXTS = {
152
+ /** A `TA1` (envelope-level by spec) appeared inside an open functional group. */
153
+ TA1_INSIDE_GROUP: "ta1-inside-group",
154
+ /** A `GE` appeared with no open `GS`. */
155
+ GE_WITHOUT_GS: "ge-without-gs",
156
+ /** An `ST` appeared with no open functional group. */
157
+ ST_WITHOUT_GS: "st-without-gs",
158
+ /** An `SE` appeared with no open transaction set. */
159
+ SE_WITHOUT_ST: "se-without-st",
160
+ /** A body segment appeared outside any open transaction set. */
161
+ BODY_OUTSIDE_TRANSACTION: "body-outside-transaction"
162
+ };
163
+ var BALANCE_INVARIANTS = {
164
+ /** `CLP-04 + Σ(claim CAS + line CAS) == CLP-03`. */
165
+ CLAIM: "claim",
166
+ /** `SVC-03 + Σ(line CAS) == SVC-02`. */
167
+ SERVICE_LINE: "service-line",
168
+ /** `Σ(CLP-04) - Σ(PLB amounts) == BPR-02`. */
169
+ REMIT_TOTAL: "remit-total"
170
+ };
171
+ var REQUIRED_LOOPS = {
172
+ /** Loop 2000A, Billing Provider HL. */
173
+ BILLING_PROVIDER_2000A: "2000A",
174
+ /** Loop 2000B, Subscriber HL. */
175
+ SUBSCRIBER_2000B: "2000B",
176
+ /** Loop 2010BA, Subscriber Name. */
177
+ SUBSCRIBER_NAME_2010BA: "2010BA",
178
+ /** Loop 2010BB, Payer Name. */
179
+ PAYER_NAME_2010BB: "2010BB"
180
+ };
181
+ var WARNING_MESSAGES = {
182
+ X12_CONTROL_NUMBER_MISMATCH_INTERCHANGE: "Control number mismatch: ISA-13 (header) and IEA-02 (trailer) disagree. Both values are preserved verbatim on the model; neither is echoed here.",
183
+ 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.",
184
+ 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.",
185
+ 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.',
186
+ 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.",
187
+ 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.",
188
+ 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 })`.",
189
+ X12_TRAILING_GARBAGE: "Bytes followed the IEA segment terminator, preserved verbatim on `trailingBytes`. Common cause: a second interchange concatenated into the same file (multi-ISA, out of v1 scope).",
190
+ X12_MISSING_IEA: "Interchange has no IEA trailer - input is truncated.",
191
+ X12_MISSING_GE: "Functional group has no GE trailer - group is truncated.",
192
+ X12_MISSING_SE: "Transaction set has no SE trailer - transaction is truncated.",
193
+ X12_DANGLING_RELEASE_CHAR: "Release character (`?`) appears at end of element/segment with no following byte to escape - preserved verbatim.",
194
+ X12_UNEXPECTED_SEGMENT_TA1_INSIDE_GROUP: "Unexpected segment: TA1 is envelope-level but appeared inside an open functional group, so it is NOT captured on `ta1Segments` and `parseTA1` will not read it. It is retained verbatim on `orphanSegments` at the matching `position.segmentIndex`.",
195
+ X12_UNEXPECTED_SEGMENT_GE_WITHOUT_GS: "Unexpected segment: a GE appeared with no open functional group, so it closes nothing and no group is recorded for it. It is retained verbatim on `orphanSegments` at the matching `position.segmentIndex`.",
196
+ X12_UNEXPECTED_SEGMENT_ST_WITHOUT_GS: "Unexpected segment: an ST appeared with no open functional group (missing GS), so no transaction set is opened and nothing that follows it is bound to one. It is retained verbatim on `orphanSegments` at the matching `position.segmentIndex`.",
197
+ X12_UNEXPECTED_SEGMENT_SE_WITHOUT_ST: "Unexpected segment: an SE appeared with no open transaction set, so it closes nothing and no transaction is recorded for it. It is retained verbatim on `orphanSegments` at the matching `position.segmentIndex`.",
198
+ X12_UNEXPECTED_SEGMENT_BODY_OUTSIDE_TRANSACTION: "Unexpected segment: a body segment appeared outside any open transaction set, so it is not bound to a transaction and no `get*` reader will see it. It is retained verbatim on `orphanSegments` at the matching `position.segmentIndex`.",
199
+ X12_835_REMIT_BALANCE_MISMATCH_CLAIM: "835 balance invariant violated [CLP-04 + \u03A3(claim CAS + line CAS) == CLP-03]: the claim does not balance. Every amount is preserved verbatim on the model as an X12Decimal and is NEVER silently rebalanced.",
200
+ X12_835_REMIT_BALANCE_MISMATCH_SERVICE_LINE: "835 balance invariant violated [SVC-03 + \u03A3(line CAS) == SVC-02]: the service line does not balance. Every amount is preserved verbatim on the model as an X12Decimal and is NEVER silently rebalanced.",
201
+ X12_835_REMIT_BALANCE_MISMATCH_REMIT_TOTAL: "835 balance invariant violated [\u03A3(CLP-04) - \u03A3(PLB amounts) == BPR-02]: the remittance total does not balance. Every amount is preserved verbatim on the model as an X12Decimal and is NEVER silently rebalanced.",
202
+ X12_UNKNOWN_CARC: "Unknown CARC: the claim adjustment reason code is outside the bundled snapshot. The verbatim code is preserved on the adjustment; only its description is unavailable.",
203
+ X12_UNKNOWN_RARC: "Unknown RARC: the remittance advice remark code is outside the bundled snapshot. The verbatim code is preserved on the remark; only its description is unavailable.",
204
+ X12_HL_PARENT_MISMATCH: "HL-02 declares a parent id that no earlier HL in this transaction set emitted as its HL-01. The declared pointer is preserved verbatim and the hierarchy is NEVER silently re-numbered.",
205
+ X12_HL_PARENT_LEVEL_INVALID: "HL-03 is inconsistent with the declared parent's HL-03 per the TR3 hierarchy. Both level codes are preserved verbatim and the hierarchy is NEVER silently re-parented.",
206
+ X12_UNKNOWN_HI_QUALIFIER: 'Unknown HI qualifier: the composite qualifier is outside the bundled HI_QUALIFIERS snapshot. The verbatim qualifier and code are preserved; codeSystem resolves to "unknown".',
207
+ X12_MISSING_REQUIRED_LOOP_2000A: 'Missing required loop "2000A" (Billing Provider HL): no Billing Provider HL precedes the CLM.',
208
+ X12_MISSING_REQUIRED_LOOP_2000B: 'Missing required loop "2000B" (Subscriber HL): no Subscriber HL precedes the CLM.',
209
+ X12_MISSING_REQUIRED_LOOP_2010BA: 'Missing required loop "2010BA" (Subscriber Name): no Subscriber Name follows the Subscriber HL.',
210
+ X12_MISSING_REQUIRED_LOOP_2010BB: 'Missing required loop "2010BB" (Payer Name): no Payer Name follows the Subscriber HL.',
211
+ X12_837_UNKNOWN_VARIANT: `837 variant could not be resolved: ST-03's implementation convention reference is not one of "005010X222A2" / "005010X223A3" / "005010X224A2", and no SVx service-line segment was present to fall back on. The verbatim reference is preserved on the model.`,
212
+ X12_UNKNOWN_CLAIM_STATUS_CATEGORY: "Unknown claim status category (CSCC): the STC composite's first component is outside the bundled snapshot. The verbatim code is preserved on the status; only its description is unavailable.",
213
+ X12_UNKNOWN_CLAIM_STATUS: "Unknown claim status code (CSC): the STC composite's second component is outside the bundled snapshot. The verbatim code is preserved on the status; only its description is unavailable.",
214
+ X12_834_UNKNOWN_MAINTENANCE_TYPE: "Unknown 834 maintenance type: the INS-03/HD-01 code is outside the bundled snapshot. The verbatim code is preserved on the enrollment and the action is NEVER inferred."
215
+ };
216
+ var ALL_WARNING_MESSAGES = new Set(
217
+ Object.values(WARNING_MESSAGES)
218
+ );
219
+ var UNEXPECTED_SEGMENT_MESSAGES = {
220
+ [UNEXPECTED_SEGMENT_CONTEXTS.TA1_INSIDE_GROUP]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_TA1_INSIDE_GROUP,
221
+ [UNEXPECTED_SEGMENT_CONTEXTS.GE_WITHOUT_GS]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_GE_WITHOUT_GS,
222
+ [UNEXPECTED_SEGMENT_CONTEXTS.ST_WITHOUT_GS]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_ST_WITHOUT_GS,
223
+ [UNEXPECTED_SEGMENT_CONTEXTS.SE_WITHOUT_ST]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_SE_WITHOUT_ST,
224
+ [UNEXPECTED_SEGMENT_CONTEXTS.BODY_OUTSIDE_TRANSACTION]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_BODY_OUTSIDE_TRANSACTION
225
+ };
226
+ var CONTROL_NUMBER_PAIR_MESSAGES = {
227
+ [CONTROL_NUMBER_PAIRS.INTERCHANGE]: WARNING_MESSAGES.X12_CONTROL_NUMBER_MISMATCH_INTERCHANGE,
228
+ [CONTROL_NUMBER_PAIRS.GROUP]: WARNING_MESSAGES.X12_CONTROL_NUMBER_MISMATCH_GROUP,
229
+ [CONTROL_NUMBER_PAIRS.TRANSACTION]: WARNING_MESSAGES.X12_CONTROL_NUMBER_MISMATCH_TRANSACTION
230
+ };
231
+ var BALANCE_INVARIANT_MESSAGES = {
232
+ [BALANCE_INVARIANTS.CLAIM]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_CLAIM,
233
+ [BALANCE_INVARIANTS.SERVICE_LINE]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_SERVICE_LINE,
234
+ [BALANCE_INVARIANTS.REMIT_TOTAL]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_REMIT_TOTAL
235
+ };
236
+ var REQUIRED_LOOP_MESSAGES = {
237
+ [REQUIRED_LOOPS.BILLING_PROVIDER_2000A]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000A,
238
+ [REQUIRED_LOOPS.SUBSCRIBER_2000B]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000B,
239
+ [REQUIRED_LOOPS.SUBSCRIBER_NAME_2010BA]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2010BA,
240
+ [REQUIRED_LOOPS.PAYER_NAME_2010BB]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2010BB
241
+ };
242
+ function controlNumberMismatch(position, pair) {
141
243
  return {
142
244
  code: WARNING_CODES.X12_CONTROL_NUMBER_MISMATCH,
143
- message: `Control number mismatch (${pair}): header="${header}", trailer="${trailer}".`,
245
+ message: CONTROL_NUMBER_PAIR_MESSAGES[pair],
144
246
  position
145
247
  };
146
248
  }
147
- function pre005010(position, declared) {
249
+ function pre005010(position) {
148
250
  return {
149
251
  code: WARNING_CODES.X12_PRE_005010,
150
- message: `ISA-12 declares version "${declared}", not the HIPAA baseline "00501".`,
252
+ message: WARNING_MESSAGES.X12_PRE_005010,
151
253
  position
152
254
  };
153
255
  }
154
- function groupCountMismatch(position, declared, actual) {
256
+ function groupCountMismatch(position) {
155
257
  return {
156
258
  code: WARNING_CODES.X12_GROUP_COUNT_MISMATCH,
157
- message: `IEA-01 declares ${declared} group(s); ${String(actual)} were present.`,
259
+ message: WARNING_MESSAGES.X12_GROUP_COUNT_MISMATCH,
158
260
  position
159
261
  };
160
262
  }
161
- function transactionCountMismatch(position, declared, actual) {
263
+ function transactionCountMismatch(position) {
162
264
  return {
163
265
  code: WARNING_CODES.X12_TRANSACTION_COUNT_MISMATCH,
164
- message: `GE-01 declares ${declared} transaction(s); ${String(actual)} were present.`,
266
+ message: WARNING_MESSAGES.X12_TRANSACTION_COUNT_MISMATCH,
165
267
  position
166
268
  };
167
269
  }
168
- function segmentCountMismatch(position, declared, actual) {
270
+ function segmentCountMismatch(position) {
169
271
  return {
170
272
  code: WARNING_CODES.X12_SEGMENT_COUNT_MISMATCH,
171
- message: `SE-01 declares ${declared} segment(s); ${String(actual)} were present.`,
273
+ message: WARNING_MESSAGES.X12_SEGMENT_COUNT_MISMATCH,
172
274
  position
173
275
  };
174
276
  }
175
- function trailingGarbage(position, byteCount) {
277
+ function trailingGarbage(position) {
176
278
  return {
177
279
  code: WARNING_CODES.X12_TRAILING_GARBAGE,
178
- message: `${String(byteCount)} byte(s) followed the IEA terminator - preserved verbatim on \`trailingBytes\`.`,
280
+ message: WARNING_MESSAGES.X12_TRAILING_GARBAGE,
179
281
  position
180
282
  };
181
283
  }
182
284
  function missingIea(position) {
183
285
  return {
184
286
  code: WARNING_CODES.X12_MISSING_IEA,
185
- message: "Interchange has no IEA trailer - input is truncated.",
287
+ message: WARNING_MESSAGES.X12_MISSING_IEA,
186
288
  position
187
289
  };
188
290
  }
189
291
  function missingGe(position) {
190
292
  return {
191
293
  code: WARNING_CODES.X12_MISSING_GE,
192
- message: "Functional group has no GE trailer - group is truncated.",
294
+ message: WARNING_MESSAGES.X12_MISSING_GE,
193
295
  position
194
296
  };
195
297
  }
196
298
  function missingSe(position) {
197
299
  return {
198
300
  code: WARNING_CODES.X12_MISSING_SE,
199
- message: "Transaction set has no SE trailer - transaction is truncated.",
301
+ message: WARNING_MESSAGES.X12_MISSING_SE,
200
302
  position
201
303
  };
202
304
  }
203
305
  function danglingReleaseChar(position) {
204
306
  return {
205
307
  code: WARNING_CODES.X12_DANGLING_RELEASE_CHAR,
206
- message: "Release character (`?`) appears at end of element/segment with no following byte to escape - preserved verbatim.",
308
+ message: WARNING_MESSAGES.X12_DANGLING_RELEASE_CHAR,
207
309
  position
208
310
  };
209
311
  }
210
- var SEGMENT_ID_SHAPE_RE = /^[A-Z][A-Z0-9]{1,2}$/u;
211
- function unexpectedSegment(position, segmentId, context) {
212
- const safeId = SEGMENT_ID_SHAPE_RE.test(segmentId) ? segmentId : "(non-spec)";
312
+ function unexpectedSegment(position, context) {
213
313
  return {
214
314
  code: WARNING_CODES.X12_UNEXPECTED_SEGMENT,
215
- message: `Unexpected "${safeId}" segment - ${context}; segment is preserved on the prior open container if any.`,
315
+ message: UNEXPECTED_SEGMENT_MESSAGES[context],
216
316
  position
217
317
  };
218
318
  }
219
- var CODE_LIST_VALUE_SHAPE_RE = /^[A-Z0-9]{1,5}$/u;
220
- function remitBalanceMismatch(position, invariant, spec, computed, delta) {
319
+ function remitBalanceMismatch(position, invariant) {
221
320
  return {
222
321
  code: WARNING_CODES.X12_835_REMIT_BALANCE_MISMATCH,
223
- message: `835 balance invariant violated [${invariant}]: spec="${spec}", computed="${computed}", delta="${delta}".`,
322
+ message: BALANCE_INVARIANT_MESSAGES[invariant],
224
323
  position
225
324
  };
226
325
  }
227
- function unknownCarc(position, code) {
228
- const safe = CODE_LIST_VALUE_SHAPE_RE.test(code) ? code : "(non-spec)";
326
+ function unknownCarc(position) {
229
327
  return {
230
328
  code: WARNING_CODES.X12_UNKNOWN_CARC,
231
- message: `Unknown CARC "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
329
+ message: WARNING_MESSAGES.X12_UNKNOWN_CARC,
232
330
  position
233
331
  };
234
332
  }
235
- function unknownRarc(position, code) {
236
- const safe = CODE_LIST_VALUE_SHAPE_RE.test(code) ? code : "(non-spec)";
333
+ function unknownRarc(position) {
237
334
  return {
238
335
  code: WARNING_CODES.X12_UNKNOWN_RARC,
239
- message: `Unknown RARC "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
336
+ message: WARNING_MESSAGES.X12_UNKNOWN_RARC,
240
337
  position
241
338
  };
242
339
  }
243
- var HL_ID_SHAPE_RE = /^[0-9]{1,4}$/u;
244
- var HL_LEVEL_CODE_SHAPE_RE = /^[0-9]{2}$/u;
245
- var HI_QUALIFIER_SHAPE_RE = /^[A-Z][A-Z0-9]{1,2}$/u;
246
- function hlParentMismatch(position, hlId, declaredParentId) {
247
- const safeHl = HL_ID_SHAPE_RE.test(hlId) ? hlId : "(non-spec)";
248
- const safeParent = HL_ID_SHAPE_RE.test(declaredParentId) ? declaredParentId : "(non-spec)";
340
+ function hlParentMismatch(position) {
249
341
  return {
250
342
  code: WARNING_CODES.X12_HL_PARENT_MISMATCH,
251
- message: `HL "${safeHl}" declares parent "${safeParent}" but no earlier HL with that id was emitted - parser preserves the declared pointer verbatim and NEVER silently re-numbers.`,
343
+ message: WARNING_MESSAGES.X12_HL_PARENT_MISMATCH,
252
344
  position
253
345
  };
254
346
  }
255
- function hlParentLevelInvalid(position, hlId, hlLevel, parentHlId, parentLevel, expectedParentLevel) {
256
- const safeHl = HL_ID_SHAPE_RE.test(hlId) ? hlId : "(non-spec)";
257
- const safeLevel = HL_LEVEL_CODE_SHAPE_RE.test(hlLevel) ? hlLevel : "(non-spec)";
258
- const safeParent = HL_ID_SHAPE_RE.test(parentHlId) ? parentHlId : "(non-spec)";
259
- const safeParentLevel = HL_LEVEL_CODE_SHAPE_RE.test(parentLevel) ? parentLevel : "(non-spec)";
260
- const safeExpected = HL_LEVEL_CODE_SHAPE_RE.test(expectedParentLevel) ? expectedParentLevel : "(non-spec)";
347
+ function hlParentLevelInvalid(position) {
261
348
  return {
262
349
  code: WARNING_CODES.X12_HL_PARENT_LEVEL_INVALID,
263
- message: `HL "${safeHl}" (level "${safeLevel}") declares parent "${safeParent}" with level "${safeParentLevel}" but TR3 requires parent level "${safeExpected}".`,
350
+ message: WARNING_MESSAGES.X12_HL_PARENT_LEVEL_INVALID,
264
351
  position
265
352
  };
266
353
  }
267
- function unknownHiQualifier(position, qualifier) {
268
- const safe = HI_QUALIFIER_SHAPE_RE.test(qualifier) ? qualifier : "(non-spec)";
354
+ function unknownHiQualifier(position) {
269
355
  return {
270
356
  code: WARNING_CODES.X12_UNKNOWN_HI_QUALIFIER,
271
- message: `Unknown HI qualifier "${safe}" - qualifier is outside the bundled HI_QUALIFIERS snapshot; verbatim qualifier + code preserved, codeSystem resolves to "unknown".`,
357
+ message: WARNING_MESSAGES.X12_UNKNOWN_HI_QUALIFIER,
272
358
  position
273
359
  };
274
360
  }
275
- var LOOP_ID_SHAPE_RE = /^[0-9A-Z]{3,6}$/u;
276
- function missingRequiredLoop(position, loopId, rationale) {
277
- const safe = LOOP_ID_SHAPE_RE.test(loopId) ? loopId : "(non-spec)";
361
+ function missingRequiredLoop(position, loop) {
278
362
  return {
279
363
  code: WARNING_CODES.X12_MISSING_REQUIRED_LOOP,
280
- message: `Missing required loop "${safe}": ${rationale}`,
364
+ message: REQUIRED_LOOP_MESSAGES[loop],
281
365
  position
282
366
  };
283
367
  }
284
- var ICR_SHAPE_RE = /^[0-9A-Z]{3,16}$/u;
285
- function unknown837Variant(position, implementationConventionReference) {
286
- const safe = implementationConventionReference !== void 0 && ICR_SHAPE_RE.test(implementationConventionReference) ? implementationConventionReference : "(non-spec)";
368
+ function unknown837Variant(position) {
287
369
  return {
288
370
  code: WARNING_CODES.X12_837_UNKNOWN_VARIANT,
289
- message: `837 variant could not be resolved - ST-03 implementation convention reference "${safe}" is not one of "005010X222A2" / "X223A3" / "X224A2"; no SVx service-line segment was seen to fall back on.`,
371
+ message: WARNING_MESSAGES.X12_837_UNKNOWN_VARIANT,
290
372
  position
291
373
  };
292
374
  }
293
- var CLAIM_STATUS_CATEGORY_SHAPE_RE = /^[A-Z][0-9A-Z]{1,2}$/u;
294
- function unknownClaimStatusCategory(position, code) {
295
- const safe = CLAIM_STATUS_CATEGORY_SHAPE_RE.test(code) ? code : "(non-spec)";
375
+ function unknownClaimStatusCategory(position) {
296
376
  return {
297
377
  code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
298
- message: `Unknown claim status category (CSCC) "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
378
+ message: WARNING_MESSAGES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
299
379
  position
300
380
  };
301
381
  }
302
- function unknownClaimStatus(position, code) {
303
- const safe = CODE_LIST_VALUE_SHAPE_RE.test(code) ? code : "(non-spec)";
382
+ function unknownClaimStatus(position) {
304
383
  return {
305
384
  code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS,
306
- message: `Unknown claim status code (CSC) "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
385
+ message: WARNING_MESSAGES.X12_UNKNOWN_CLAIM_STATUS,
307
386
  position
308
387
  };
309
388
  }
310
- var MAINTENANCE_TYPE_SHAPE_RE = /^[0-9]{3}$/u;
311
- function unknownMaintenanceType(position, code) {
312
- const safe = MAINTENANCE_TYPE_SHAPE_RE.test(code) ? code : "(non-spec)";
389
+ function unknownMaintenanceType(position) {
313
390
  return {
314
391
  code: WARNING_CODES.X12_834_UNKNOWN_MAINTENANCE_TYPE,
315
- message: `Unknown 834 maintenance type "${safe}" - INS-03/HD-01 code is outside the bundled snapshot; verbatim value preserved, action NOT inferred.`,
392
+ message: WARNING_MESSAGES.X12_834_UNKNOWN_MAINTENANCE_TYPE,
316
393
  position
317
394
  };
318
395
  }
@@ -673,6 +750,12 @@ function renderCanonical(signum, magnitude, scale) {
673
750
  }
674
751
 
675
752
  // src/parser/segment.ts
753
+ var SEGMENT_ID_SHAPE_RE = /^[A-Z][A-Z0-9]{1,2}$/u;
754
+ var NON_SPEC_SEGMENT_ID = "(non-spec)";
755
+ function boundSegmentId(first) {
756
+ if (first.length === 0) return "";
757
+ return SEGMENT_ID_SHAPE_RE.test(first) ? first : NON_SPEC_SEGMENT_ID;
758
+ }
676
759
  function decodeSegment(raw, delimiters, emit, position) {
677
760
  const elements = Object.freeze(splitWithRelease(raw, delimiters.element));
678
761
  if (raw.length > 0 && raw.endsWith(RELEASE_CHAR)) {
@@ -682,8 +765,7 @@ function decodeSegment(raw, delimiters, emit, position) {
682
765
  emit(danglingReleaseChar({ ...position, elementIndex: elements.length - 1 }));
683
766
  }
684
767
  }
685
- const id = elements[0] ?? "";
686
- return Object.freeze({ id, raw, elements });
768
+ return Object.freeze({ id: boundSegmentId(elements[0] ?? ""), raw, elements });
687
769
  }
688
770
  function parseSegmentPath(path) {
689
771
  if (path.length === 0) {
@@ -847,8 +929,11 @@ function decodeIsa(raw, delimiters) {
847
929
  }
848
930
  function stripLeadingNewlines(text, start) {
849
931
  let i = start;
850
- if (text.charCodeAt(i) === 13) i++;
851
- if (text.charCodeAt(i) === 10) i++;
932
+ while (i < text.length) {
933
+ const code = text.charCodeAt(i);
934
+ if (code !== 13 && code !== 10) break;
935
+ i++;
936
+ }
852
937
  return i;
853
938
  }
854
939
  function findUnescapedTerminator(text, from, term) {
@@ -901,15 +986,45 @@ function decodeEnvelope(raw, delimiters) {
901
986
  const isa = decodeIsa(raw, delimiters);
902
987
  const isa12 = el(isa.elements, 12);
903
988
  if (isa12 !== "00501") {
904
- warnings.push(pre005010({ segmentIndex: 0, interchangeIndex: 0, elementIndex: 12 }, isa12));
989
+ warnings.push(pre005010({ segmentIndex: 0, interchangeIndex: 0, elementIndex: 12 }));
905
990
  }
906
991
  const { segments } = splitSegments(raw, ISA_MIN_LENGTH, delimiters);
907
992
  const groups = [];
908
993
  const ta1Segments = [];
994
+ const orphanSegments = [];
909
995
  let iea;
910
996
  let trailingBytes;
911
997
  let currentGroup;
912
998
  let currentTx;
999
+ const currentAnchor = () => {
1000
+ if (currentGroup === void 0) {
1001
+ return { kind: "interchange", groupIndex: groups.length };
1002
+ }
1003
+ if (currentTx === void 0) {
1004
+ return {
1005
+ kind: "group",
1006
+ groupIndex: groups.length,
1007
+ transactionIndex: currentGroup.transactions.length
1008
+ };
1009
+ }
1010
+ return {
1011
+ kind: "transaction",
1012
+ groupIndex: groups.length,
1013
+ transactionIndex: currentGroup.transactions.length,
1014
+ segmentOffset: currentTx.rawSegments.length
1015
+ };
1016
+ };
1017
+ const recordOrphan = (segmentText, position, context) => {
1018
+ const anchor = currentAnchor();
1019
+ warnings.push(unexpectedSegment(position, context));
1020
+ orphanSegments.push({
1021
+ raw: segmentText,
1022
+ segment: decodeSegment(segmentText, delimiters, collectWarning, position),
1023
+ segmentIndex: position.segmentIndex,
1024
+ context,
1025
+ anchor
1026
+ });
1027
+ };
913
1028
  const finalizeTx = (tx, group, se) => {
914
1029
  if (se === void 0) {
915
1030
  warnings.push(
@@ -942,16 +1057,12 @@ function decodeEnvelope(raw, delimiters) {
942
1057
  const actual = group.transactions.length;
943
1058
  if (declared !== String(actual)) {
944
1059
  warnings.push(
945
- transactionCountMismatch(
946
- {
947
- segmentIndex: group.startSegIdx,
948
- interchangeIndex: 0,
949
- groupIndex: groups.length,
950
- elementIndex: 1
951
- },
952
- declared,
953
- actual
954
- )
1060
+ transactionCountMismatch({
1061
+ segmentIndex: group.startSegIdx,
1062
+ interchangeIndex: 0,
1063
+ groupIndex: groups.length,
1064
+ elementIndex: 1
1065
+ })
955
1066
  );
956
1067
  }
957
1068
  const gsControl = el(group.gs.elements, 6);
@@ -965,9 +1076,7 @@ function decodeEnvelope(raw, delimiters) {
965
1076
  groupIndex: groups.length,
966
1077
  elementIndex: 2
967
1078
  },
968
- "GS-06/GE-02",
969
- gsControl,
970
- geControl
1079
+ CONTROL_NUMBER_PAIRS.GROUP
971
1080
  )
972
1081
  );
973
1082
  }
@@ -989,16 +1098,14 @@ function decodeEnvelope(raw, delimiters) {
989
1098
  ta1Segments.push({ raw: segmentText, elements });
990
1099
  break;
991
1100
  }
992
- warnings.push(
993
- unexpectedSegment(
994
- {
995
- segmentIndex: segIdx,
996
- interchangeIndex: 0,
997
- groupIndex: groups.length
998
- },
999
- "TA1",
1000
- "TA1 segment is envelope-level - appeared inside an open functional group"
1001
- )
1101
+ recordOrphan(
1102
+ segmentText,
1103
+ {
1104
+ segmentIndex: segIdx,
1105
+ interchangeIndex: 0,
1106
+ groupIndex: groups.length
1107
+ },
1108
+ UNEXPECTED_SEGMENT_CONTEXTS.TA1_INSIDE_GROUP
1002
1109
  );
1003
1110
  break;
1004
1111
  }
@@ -1019,12 +1126,10 @@ function decodeEnvelope(raw, delimiters) {
1019
1126
  }
1020
1127
  case "GE": {
1021
1128
  if (currentGroup === void 0) {
1022
- warnings.push(
1023
- unexpectedSegment(
1024
- { segmentIndex: segIdx, interchangeIndex: 0 },
1025
- "GE",
1026
- "no open functional group"
1027
- )
1129
+ recordOrphan(
1130
+ segmentText,
1131
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1132
+ UNEXPECTED_SEGMENT_CONTEXTS.GE_WITHOUT_GS
1028
1133
  );
1029
1134
  break;
1030
1135
  }
@@ -1038,12 +1143,10 @@ function decodeEnvelope(raw, delimiters) {
1038
1143
  }
1039
1144
  case "ST": {
1040
1145
  if (currentGroup === void 0) {
1041
- warnings.push(
1042
- unexpectedSegment(
1043
- { segmentIndex: segIdx, interchangeIndex: 0 },
1044
- "ST",
1045
- "no open functional group (missing GS)"
1046
- )
1146
+ recordOrphan(
1147
+ segmentText,
1148
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1149
+ UNEXPECTED_SEGMENT_CONTEXTS.ST_WITHOUT_GS
1047
1150
  );
1048
1151
  break;
1049
1152
  }
@@ -1066,12 +1169,10 @@ function decodeEnvelope(raw, delimiters) {
1066
1169
  }
1067
1170
  case "SE": {
1068
1171
  if (currentTx === void 0 || currentGroup === void 0) {
1069
- warnings.push(
1070
- unexpectedSegment(
1071
- { segmentIndex: segIdx, interchangeIndex: 0 },
1072
- "SE",
1073
- "no open transaction set"
1074
- )
1172
+ recordOrphan(
1173
+ segmentText,
1174
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1175
+ UNEXPECTED_SEGMENT_CONTEXTS.SE_WITHOUT_ST
1075
1176
  );
1076
1177
  break;
1077
1178
  }
@@ -1096,9 +1197,7 @@ function decodeEnvelope(raw, delimiters) {
1096
1197
  transactionIndex: currentGroup.transactions.length,
1097
1198
  elementIndex: 2
1098
1199
  },
1099
- "ST-02/SE-02",
1100
- stControl,
1101
- seControl
1200
+ CONTROL_NUMBER_PAIRS.TRANSACTION
1102
1201
  )
1103
1202
  );
1104
1203
  }
@@ -1117,11 +1216,7 @@ function decodeEnvelope(raw, delimiters) {
1117
1216
  const declared = el(elements, 1);
1118
1217
  if (declared !== String(groups.length)) {
1119
1218
  warnings.push(
1120
- groupCountMismatch(
1121
- { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 },
1122
- declared,
1123
- groups.length
1124
- )
1219
+ groupCountMismatch({ segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 })
1125
1220
  );
1126
1221
  }
1127
1222
  const isaControl = el(isa.elements, 13);
@@ -1130,9 +1225,7 @@ function decodeEnvelope(raw, delimiters) {
1130
1225
  warnings.push(
1131
1226
  controlNumberMismatch(
1132
1227
  { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 2 },
1133
- "ISA-13/IEA-02",
1134
- isaControl,
1135
- ieaControl
1228
+ CONTROL_NUMBER_PAIRS.INTERCHANGE
1136
1229
  )
1137
1230
  );
1138
1231
  }
@@ -1140,15 +1233,14 @@ function decodeEnvelope(raw, delimiters) {
1140
1233
  if (tail.length > 0) {
1141
1234
  const joined = tail + delimiters.segment;
1142
1235
  trailingBytes = joined;
1143
- warnings.push(
1144
- trailingGarbage({ segmentIndex: segIdx + 1, interchangeIndex: 0 }, joined.length)
1145
- );
1236
+ warnings.push(trailingGarbage({ segmentIndex: segIdx + 1, interchangeIndex: 0 }));
1146
1237
  }
1147
1238
  return {
1148
1239
  isa,
1149
1240
  groups: Object.freeze(groups.slice()),
1150
1241
  iea,
1151
1242
  ta1Segments: Object.freeze(ta1Segments.slice()),
1243
+ orphanSegments: Object.freeze(orphanSegments.slice()),
1152
1244
  trailingBytes,
1153
1245
  warnings: Object.freeze(warnings.slice())
1154
1246
  };
@@ -1164,12 +1256,10 @@ function decodeEnvelope(raw, delimiters) {
1164
1256
  currentTx.segments.push(decoded);
1165
1257
  currentTx.rawSegments.push(segmentText);
1166
1258
  } else if (name.length > 0) {
1167
- warnings.push(
1168
- unexpectedSegment(
1169
- { segmentIndex: segIdx, interchangeIndex: 0 },
1170
- name,
1171
- "body segment outside any open transaction set"
1172
- )
1259
+ recordOrphan(
1260
+ segmentText,
1261
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1262
+ UNEXPECTED_SEGMENT_CONTEXTS.BODY_OUTSIDE_TRANSACTION
1173
1263
  );
1174
1264
  }
1175
1265
  break;
@@ -1188,6 +1278,7 @@ function decodeEnvelope(raw, delimiters) {
1188
1278
  groups: Object.freeze(groups.slice()),
1189
1279
  iea,
1190
1280
  ta1Segments: Object.freeze(ta1Segments.slice()),
1281
+ orphanSegments: Object.freeze(orphanSegments.slice()),
1191
1282
  trailingBytes,
1192
1283
  warnings: Object.freeze(warnings.slice())
1193
1284
  };
@@ -1213,7 +1304,7 @@ function parseX12(raw, options = {}) {
1213
1304
  first.code,
1214
1305
  first.message,
1215
1306
  first.position,
1216
- snippet(text)
1307
+ ""
1217
1308
  );
1218
1309
  }
1219
1310
  }
@@ -1233,6 +1324,7 @@ function parseX12(raw, options = {}) {
1233
1324
  delimiters,
1234
1325
  groups: decoded.groups,
1235
1326
  ta1Segments: decoded.ta1Segments,
1327
+ orphanSegments: decoded.orphanSegments,
1236
1328
  warnings: decoded.warnings,
1237
1329
  ...decoded.trailingBytes !== void 0 ? { trailingBytes: decoded.trailingBytes } : {},
1238
1330
  ...profile !== void 0 ? { profile } : {}
@@ -1749,14 +1841,7 @@ function checkClaimBalance(claim, position) {
1749
1841
  );
1750
1842
  const computed = claim.totalPaymentAmount.add(claimCasSum).add(lineCasSum);
1751
1843
  if (computed.equals(claim.totalChargeAmount)) return void 0;
1752
- const delta = computed.subtract(claim.totalChargeAmount);
1753
- return remitBalanceMismatch(
1754
- position,
1755
- "CLP-04 + \u03A3(claim CAS + line CAS) == CLP-03",
1756
- claim.totalChargeAmount.toString(),
1757
- computed.toString(),
1758
- delta.toString()
1759
- );
1844
+ return remitBalanceMismatch(position, BALANCE_INVARIANTS.CLAIM);
1760
1845
  }
1761
1846
  function checkServiceLineBalance(claim, position) {
1762
1847
  const out = [];
@@ -1766,14 +1851,10 @@ function checkServiceLineBalance(claim, position) {
1766
1851
  const lineCasSum = sumAmounts(line.adjustments);
1767
1852
  const computed = line.paymentAmount.add(lineCasSum);
1768
1853
  if (computed.equals(line.chargeAmount)) continue;
1769
- const delta = computed.subtract(line.chargeAmount);
1770
1854
  out.push(
1771
1855
  remitBalanceMismatch(
1772
1856
  { ...position, segmentIndex: position.segmentIndex + i + 1 },
1773
- "SVC-03 + \u03A3(line CAS) == SVC-02",
1774
- line.chargeAmount.toString(),
1775
- computed.toString(),
1776
- delta.toString()
1857
+ BALANCE_INVARIANTS.SERVICE_LINE
1777
1858
  )
1778
1859
  );
1779
1860
  }
@@ -1790,14 +1871,7 @@ function checkRemitTotalBalance(bpr02, claims, providerAdjustments, position) {
1790
1871
  );
1791
1872
  const computed = claimSum.subtract(plbSum);
1792
1873
  if (computed.equals(bpr02)) return void 0;
1793
- const delta = computed.subtract(bpr02);
1794
- return remitBalanceMismatch(
1795
- position,
1796
- "\u03A3(CLP-04) - \u03A3(PLB amounts) == BPR-02",
1797
- bpr02.toString(),
1798
- computed.toString(),
1799
- delta.toString()
1800
- );
1874
+ return remitBalanceMismatch(position, BALANCE_INVARIANTS.REMIT_TOTAL);
1801
1875
  }
1802
1876
  function sumAmounts(adjustments) {
1803
1877
  return adjustments.reduce(
@@ -1913,7 +1987,7 @@ function get835(delimiters, tx) {
1913
1987
  case "CLP": {
1914
1988
  flushClaim();
1915
1989
  lastParty = void 0;
1916
- currentClaim = openClaim(seg, delimiters);
1990
+ currentClaim = openClaim(seg, delimiters, position.segmentIndex);
1917
1991
  break;
1918
1992
  }
1919
1993
  case "CAS": {
@@ -1959,7 +2033,7 @@ function get835(delimiters, tx) {
1959
2033
  const remarks = seg.id === "MIA" ? collectMiaRemarks(seg, delimiters) : collectMoaRemarks(seg, delimiters);
1960
2034
  for (const code of remarks) {
1961
2035
  const entry = lookupRarc(code);
1962
- if (entry === void 0) warnings.push(unknownRarc(position, code));
2036
+ if (entry === void 0) warnings.push(unknownRarc(position));
1963
2037
  currentClaim.remarks.push({ system: "HE", code, description: entry?.description });
1964
2038
  }
1965
2039
  break;
@@ -2097,7 +2171,7 @@ function decodeLq(seg, delimiters, warnings, position) {
2097
2171
  let description;
2098
2172
  if (system === "HE") {
2099
2173
  const entry = lookupRarc(code);
2100
- if (entry === void 0) warnings.push(unknownRarc(position, code));
2174
+ if (entry === void 0) warnings.push(unknownRarc(position));
2101
2175
  description = entry?.description;
2102
2176
  }
2103
2177
  return Object.freeze({ system, code, description });
@@ -2129,7 +2203,7 @@ function decodeCasAdjustments(seg, delimiters, warnings, position) {
2129
2203
  if (reasonCode === void 0 && amount === void 0) continue;
2130
2204
  const code = reasonCode ?? "";
2131
2205
  const entry = code === "" ? void 0 : lookupCarc(code);
2132
- if (entry === void 0 && code !== "") warnings.push(unknownCarc(position, code));
2206
+ if (entry === void 0 && code !== "") warnings.push(unknownCarc(position));
2133
2207
  out.push(
2134
2208
  Object.freeze({
2135
2209
  groupCode,
@@ -2169,14 +2243,13 @@ function decodeNm1(seg, delimiters) {
2169
2243
  })
2170
2244
  };
2171
2245
  }
2172
- function openClaim(seg, delimiters) {
2246
+ function openClaim(seg, delimiters, clpSegmentIndex) {
2173
2247
  const claimStatusCode = elementValue(seg, 2, delimiters);
2174
2248
  const claimStatusDescription = lookupClpStatus(claimStatusCode)?.description;
2175
2249
  const facilityTypeCode = componentOptional(seg, 8, 1, delimiters) ?? elementOptional(seg, 8, delimiters);
2176
2250
  const claimFrequencyCode = componentOptional(seg, 8, 3, delimiters);
2177
2251
  return {
2178
- clpSegmentIndex: 0,
2179
- // populated in the future when we surface positions
2252
+ clpSegmentIndex,
2180
2253
  patientControlNumber: elementValue(seg, 1, delimiters),
2181
2254
  claimStatusCode,
2182
2255
  claimStatusDescription,
@@ -2982,12 +3055,7 @@ function get837Claims(delimiters, tx, opts) {
2982
3055
  }
2983
3056
  const variant = explicitType ?? variantFromIcr ?? variantFromSegment ?? "unknown";
2984
3057
  if (variant === "unknown") {
2985
- warnings.push(
2986
- unknown837Variant(
2987
- { segmentIndex: 1, transactionIndex: 0 },
2988
- implementationConventionReference
2989
- )
2990
- );
3058
+ warnings.push(unknown837Variant({ segmentIndex: 1, transactionIndex: 0 }));
2991
3059
  }
2992
3060
  const hierarchies = [];
2993
3061
  const hlIndex = /* @__PURE__ */ new Map();
@@ -3228,28 +3296,16 @@ function get837Claims(delimiters, tx, opts) {
3228
3296
  flushClaim();
3229
3297
  if (currentBillingHl === void 0 || currentSubscriberHl === void 0 || currentSubscriberMember === void 0 || currentPayer === void 0) {
3230
3298
  if (currentBillingHl === void 0) {
3231
- warnings.push(
3232
- missingRequiredLoop(position, "2000A", "no Billing Provider HL precedes the CLM")
3233
- );
3299
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.BILLING_PROVIDER_2000A));
3234
3300
  }
3235
3301
  if (currentSubscriberHl === void 0) {
3236
- warnings.push(
3237
- missingRequiredLoop(position, "2000B", "no Subscriber HL precedes the CLM")
3238
- );
3302
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.SUBSCRIBER_2000B));
3239
3303
  }
3240
3304
  if (currentSubscriberMember === void 0 && currentSubscriberHl !== void 0) {
3241
- warnings.push(
3242
- missingRequiredLoop(
3243
- position,
3244
- "2010BA",
3245
- "no Subscriber Name follows the Subscriber HL"
3246
- )
3247
- );
3305
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.SUBSCRIBER_NAME_2010BA));
3248
3306
  }
3249
3307
  if (currentPayer === void 0 && currentSubscriberHl !== void 0) {
3250
- warnings.push(
3251
- missingRequiredLoop(position, "2010BB", "no Payer Name follows the Subscriber HL")
3252
- );
3308
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.PAYER_NAME_2010BB));
3253
3309
  }
3254
3310
  }
3255
3311
  currentClaim = openClaim2(seg, delimiters, {
@@ -3394,30 +3450,21 @@ function validateHl(hl, index, position, warnings) {
3394
3450
  const expectedParent = EXPECTED_PARENT_LEVEL[hl.levelCode];
3395
3451
  if (expectedParent === void 0) {
3396
3452
  if (hl.levelCode === HL_LEVEL_CODES.INFORMATION_SOURCE && hl.parentHlId !== void 0) {
3397
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
3453
+ warnings.push(hlParentMismatch(position));
3398
3454
  }
3399
3455
  return;
3400
3456
  }
3401
3457
  if (hl.parentHlId === void 0) {
3402
- warnings.push(hlParentMismatch(position, hl.hlId, ""));
3458
+ warnings.push(hlParentMismatch(position));
3403
3459
  return;
3404
3460
  }
3405
3461
  const parent = index.get(hl.parentHlId);
3406
3462
  if (parent === void 0) {
3407
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
3463
+ warnings.push(hlParentMismatch(position));
3408
3464
  return;
3409
3465
  }
3410
3466
  if (parent.levelCode !== expectedParent) {
3411
- warnings.push(
3412
- hlParentLevelInvalid(
3413
- position,
3414
- hl.hlId,
3415
- hl.levelCode,
3416
- parent.hlId,
3417
- parent.levelCode,
3418
- expectedParent
3419
- )
3420
- );
3467
+ warnings.push(hlParentLevelInvalid(position));
3421
3468
  }
3422
3469
  }
3423
3470
  function decodeSbr(seg, delimiters) {
@@ -3518,7 +3565,7 @@ function decodeHi(seg, delimiters, warnings, position) {
3518
3565
  const codeSystem = resolved?.system ?? "unknown";
3519
3566
  const category = resolved?.category ?? "unknown";
3520
3567
  if (qualifier !== void 0 && resolved === void 0) {
3521
- warnings.push(unknownHiQualifier(position, qualifier));
3568
+ warnings.push(unknownHiQualifier(position));
3522
3569
  }
3523
3570
  out.push(
3524
3571
  Object.freeze({
@@ -3561,7 +3608,7 @@ function decodeCas(seg, delimiters, warnings, position) {
3561
3608
  if (reasonCode === void 0 && amount === void 0) continue;
3562
3609
  const code = reasonCode ?? "";
3563
3610
  const entry = code === "" ? void 0 : lookupCarc(code);
3564
- if (entry === void 0 && code !== "") warnings.push(unknownCarc(position, code));
3611
+ if (entry === void 0 && code !== "") warnings.push(unknownCarc(position));
3565
3612
  out.push(
3566
3613
  Object.freeze({
3567
3614
  groupCode,
@@ -4803,30 +4850,21 @@ function validateHl2(hl, index, expectedParentLevel, position, warnings) {
4803
4850
  const expectedParent = hasExpectation ? expectedParentLevel[hl.levelCode] : void 0;
4804
4851
  if (expectedParent === void 0) {
4805
4852
  if (hasExpectation && hl.parentHlId !== void 0) {
4806
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
4853
+ warnings.push(hlParentMismatch(position));
4807
4854
  }
4808
4855
  return;
4809
4856
  }
4810
4857
  if (hl.parentHlId === void 0) {
4811
- warnings.push(hlParentMismatch(position, hl.hlId, ""));
4858
+ warnings.push(hlParentMismatch(position));
4812
4859
  return;
4813
4860
  }
4814
4861
  const parent = index.get(hl.parentHlId);
4815
4862
  if (parent === void 0) {
4816
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
4863
+ warnings.push(hlParentMismatch(position));
4817
4864
  return;
4818
4865
  }
4819
4866
  if (parent.levelCode !== expectedParent) {
4820
- warnings.push(
4821
- hlParentLevelInvalid(
4822
- position,
4823
- hl.hlId,
4824
- hl.levelCode,
4825
- parent.hlId,
4826
- parent.levelCode,
4827
- expectedParent
4828
- )
4829
- );
4867
+ warnings.push(hlParentLevelInvalid(position));
4830
4868
  }
4831
4869
  }
4832
4870
 
@@ -5779,11 +5817,11 @@ function decodeStatusComposite(seg, n, delimiters, warnings, position) {
5779
5817
  if (categoryCode === void 0 && statusCode === void 0) return void 0;
5780
5818
  const category = categoryCode === void 0 ? void 0 : lookupClaimStatusCategory(categoryCode);
5781
5819
  if (categoryCode !== void 0 && category === void 0) {
5782
- warnings.push(unknownClaimStatusCategory(position, categoryCode));
5820
+ warnings.push(unknownClaimStatusCategory(position));
5783
5821
  }
5784
5822
  const status = statusCode === void 0 ? void 0 : lookupClaimStatus(statusCode);
5785
5823
  if (statusCode !== void 0 && status === void 0) {
5786
- warnings.push(unknownClaimStatus(position, statusCode));
5824
+ warnings.push(unknownClaimStatus(position));
5787
5825
  }
5788
5826
  return Object.freeze({
5789
5827
  categoryCode: categoryCode ?? "",
@@ -7107,7 +7145,7 @@ function openEnrollment(seg, delimiters, position) {
7107
7145
  const entry = maintenanceTypeCode === "" ? void 0 : lookupMaintenanceType(maintenanceTypeCode);
7108
7146
  const warnings = [];
7109
7147
  if (maintenanceTypeCode !== "" && entry === void 0) {
7110
- warnings.push(unknownMaintenanceType(position, maintenanceTypeCode));
7148
+ warnings.push(unknownMaintenanceType(position));
7111
7149
  }
7112
7150
  return {
7113
7151
  subscriberIndicator: elementOptional(seg, 1, delimiters),
@@ -7147,7 +7185,7 @@ function openCoverage(seg, delimiters, position, warnings) {
7147
7185
  let maintenanceTypeDescription;
7148
7186
  if (maintenanceTypeCode !== void 0) {
7149
7187
  const entry = lookupMaintenanceType(maintenanceTypeCode);
7150
- if (entry === void 0) warnings.push(unknownMaintenanceType(position, maintenanceTypeCode));
7188
+ if (entry === void 0) warnings.push(unknownMaintenanceType(position));
7151
7189
  maintenanceTypeDescription = entry?.description;
7152
7190
  }
7153
7191
  return {
@@ -7860,7 +7898,7 @@ function decodeHiDiagnoses(seg, delimiters, warnings, position) {
7860
7898
  const resolved = qualifier === void 0 ? void 0 : resolveHiQualifier(qualifier);
7861
7899
  const codeSystem = resolved?.system ?? "unknown";
7862
7900
  if (qualifier !== void 0 && resolved === void 0) {
7863
- warnings.push(unknownHiQualifier(position, qualifier));
7901
+ warnings.push(unknownHiQualifier(position));
7864
7902
  }
7865
7903
  out.push(Object.freeze({ qualifier: qualifier ?? "", code: code ?? "", codeSystem }));
7866
7904
  }
@@ -9008,18 +9046,39 @@ function serializeX12(interchange, opts = {}) {
9008
9046
  };
9009
9047
  let out = interchange.isa.raw;
9010
9048
  let segIdx = 0;
9049
+ const orphans = interchange.orphanSegments ?? [];
9050
+ const placed = /* @__PURE__ */ new Set();
9051
+ const flushOrphans = (slot) => {
9052
+ let written = 0;
9053
+ for (const [i, orphan] of orphans.entries()) {
9054
+ if (placed.has(i)) continue;
9055
+ if (!slot(orphan.anchor)) continue;
9056
+ placed.add(i);
9057
+ out += orphan.raw + term;
9058
+ segIdx++;
9059
+ written++;
9060
+ }
9061
+ return written;
9062
+ };
9011
9063
  for (const ta1 of interchange.ta1Segments) {
9012
9064
  out += ta1.raw + term;
9013
9065
  segIdx++;
9014
9066
  }
9015
9067
  for (const [g, group] of interchange.groups.entries()) {
9068
+ flushOrphans((a) => a?.kind === "interchange" && a.groupIndex === g);
9016
9069
  const gsSegIdx = ++segIdx;
9017
9070
  out += group.gs.raw + term;
9018
9071
  for (const [t, tx] of group.transactions.entries()) {
9019
- const stSegIdx = segIdx + 1;
9020
- const segCount = tx.rawSegments.length;
9072
+ flushOrphans((a) => a?.kind === "group" && a.groupIndex === g && a.transactionIndex === t);
9021
9073
  const lastIdx = tx.rawSegments.length - 1;
9074
+ let segCount = tx.rawSegments.length;
9075
+ let stSegIdx = segIdx + 1;
9022
9076
  for (const [k, raw] of tx.rawSegments.entries()) {
9077
+ const inside = flushOrphans(
9078
+ (a) => a?.kind === "transaction" && a.groupIndex === g && a.transactionIndex === t && a.segmentOffset === k
9079
+ );
9080
+ if (k === 0) stSegIdx = segIdx + 1;
9081
+ else segCount += inside;
9023
9082
  segIdx++;
9024
9083
  if (recompute && tx.se !== void 0 && k === lastIdx) {
9025
9084
  out += substituteElement(tx.se.elements, 1, String(segCount), elementSep) + term;
@@ -9027,21 +9086,20 @@ function serializeX12(interchange, opts = {}) {
9027
9086
  out += raw + term;
9028
9087
  }
9029
9088
  }
9089
+ flushOrphans(
9090
+ (a) => a?.kind === "transaction" && a.groupIndex === g && a.transactionIndex === t && a.segmentOffset === tx.rawSegments.length
9091
+ );
9030
9092
  if (specClean && tx.se !== void 0) {
9031
9093
  const declaredSe = elementAt(tx.se.elements, 1);
9032
9094
  if (declaredSe !== String(segCount)) {
9033
9095
  emit(
9034
- segmentCountMismatch(
9035
- {
9036
- segmentIndex: stSegIdx,
9037
- interchangeIndex: 0,
9038
- groupIndex: g,
9039
- transactionIndex: t,
9040
- elementIndex: 1
9041
- },
9042
- declaredSe,
9043
- segCount
9044
- )
9096
+ segmentCountMismatch({
9097
+ segmentIndex: stSegIdx,
9098
+ interchangeIndex: 0,
9099
+ groupIndex: g,
9100
+ transactionIndex: t,
9101
+ elementIndex: 1
9102
+ })
9045
9103
  );
9046
9104
  }
9047
9105
  const st02 = elementAt(tx.st.elements, 2);
@@ -9056,14 +9114,15 @@ function serializeX12(interchange, opts = {}) {
9056
9114
  transactionIndex: t,
9057
9115
  elementIndex: 2
9058
9116
  },
9059
- "ST-02/SE-02",
9060
- st02,
9061
- se02
9117
+ CONTROL_NUMBER_PAIRS.TRANSACTION
9062
9118
  )
9063
9119
  );
9064
9120
  }
9065
9121
  }
9066
9122
  }
9123
+ flushOrphans(
9124
+ (a) => a?.kind === "group" && a.groupIndex === g && a.transactionIndex === group.transactions.length
9125
+ );
9067
9126
  if (group.ge !== void 0) {
9068
9127
  const txCount = group.transactions.length;
9069
9128
  segIdx++;
@@ -9076,11 +9135,12 @@ function serializeX12(interchange, opts = {}) {
9076
9135
  const declaredGe = elementAt(group.ge.elements, 1);
9077
9136
  if (declaredGe !== String(txCount)) {
9078
9137
  emit(
9079
- transactionCountMismatch(
9080
- { segmentIndex: gsSegIdx, interchangeIndex: 0, groupIndex: g, elementIndex: 1 },
9081
- declaredGe,
9082
- txCount
9083
- )
9138
+ transactionCountMismatch({
9139
+ segmentIndex: gsSegIdx,
9140
+ interchangeIndex: 0,
9141
+ groupIndex: g,
9142
+ elementIndex: 1
9143
+ })
9084
9144
  );
9085
9145
  }
9086
9146
  const gs06 = elementAt(group.gs.elements, 6);
@@ -9089,15 +9149,15 @@ function serializeX12(interchange, opts = {}) {
9089
9149
  emit(
9090
9150
  controlNumberMismatch(
9091
9151
  { segmentIndex: gsSegIdx, interchangeIndex: 0, groupIndex: g, elementIndex: 2 },
9092
- "GS-06/GE-02",
9093
- gs06,
9094
- ge02
9152
+ CONTROL_NUMBER_PAIRS.GROUP
9095
9153
  )
9096
9154
  );
9097
9155
  }
9098
9156
  }
9099
9157
  }
9100
9158
  }
9159
+ flushOrphans((a) => a?.kind === "interchange" && a.groupIndex === interchange.groups.length);
9160
+ flushOrphans(() => true);
9101
9161
  if (interchange.iea !== void 0) {
9102
9162
  const groupCount = interchange.groups.length;
9103
9163
  segIdx++;
@@ -9109,13 +9169,7 @@ function serializeX12(interchange, opts = {}) {
9109
9169
  if (specClean) {
9110
9170
  const declaredIea = elementAt(interchange.iea.elements, 1);
9111
9171
  if (declaredIea !== String(groupCount)) {
9112
- emit(
9113
- groupCountMismatch(
9114
- { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 },
9115
- declaredIea,
9116
- groupCount
9117
- )
9118
- );
9172
+ emit(groupCountMismatch({ segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 }));
9119
9173
  }
9120
9174
  const isa13 = elementAt(interchange.isa.elements, 13);
9121
9175
  const iea02 = elementAt(interchange.iea.elements, 2);
@@ -9123,9 +9177,7 @@ function serializeX12(interchange, opts = {}) {
9123
9177
  emit(
9124
9178
  controlNumberMismatch(
9125
9179
  { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 2 },
9126
- "ISA-13/IEA-02",
9127
- isa13,
9128
- iea02
9180
+ CONTROL_NUMBER_PAIRS.INTERCHANGE
9129
9181
  )
9130
9182
  );
9131
9183
  }
@@ -9588,8 +9640,8 @@ var profiles = Object.freeze({
9588
9640
  });
9589
9641
 
9590
9642
  // src/index.ts
9591
- var VERSION = "0.0.2";
9643
+ var VERSION = "0.0.4";
9592
9644
 
9593
- export { ACK_BUILD_ERROR_CODES, AUTH_278_BUILD_ERROR_CODES, AUTH_278_LOOP_2000A, AUTH_278_LOOP_2000B, AUTH_278_LOOP_2000C, AUTH_278_LOOP_2000D, AUTH_278_LOOP_2000E, AUTH_278_LOOP_2000F, AckBuildError, CARC, CLAIM_837D_LOOP_2000A, CLAIM_837D_LOOP_2300, CLAIM_837D_LOOP_2400, CLAIM_837I_LOOP_2000A, CLAIM_837I_LOOP_2300, CLAIM_837I_LOOP_2400, CLAIM_837P_LOOP_2000A, CLAIM_837P_LOOP_2300, CLAIM_837P_LOOP_2400, CLAIM_837P_LOOP_2410, CLAIM_837_BUILD_ERROR_CODES, CLAIM_837_LOOP_1000A, CLAIM_837_LOOP_1000B, CLAIM_837_LOOP_2010AA, CLAIM_837_LOOP_2010BA, CLAIM_837_LOOP_2010BB, CLAIM_837_LOOP_2010CA, CLAIM_837_LOOP_2430, CLAIM_ADJUSTMENT_GROUP_CODES, CLAIM_STATUS_277_BUILD_ERROR_CODES, CLAIM_STATUS_CATEGORY_CODES, CLAIM_STATUS_CODES, CLP_STATUS, Claim837BuildError, ClaimStatus277BuildError, DELIMITER_POSITIONS, ELIGIBILITY_271_BUILD_ERROR_CODES, ELIGIBILITY_271_LOOP_2000A, ELIGIBILITY_271_LOOP_2000B, ELIGIBILITY_271_LOOP_2000C, ELIGIBILITY_271_LOOP_2000D, ELIGIBILITY_271_LOOP_2100C, ELIGIBILITY_271_LOOP_2100D, ELIGIBILITY_271_LOOP_2110, ENROLLMENT_834_BUILD_ERROR_CODES, ENROLLMENT_834_LOOP_1000A, ENROLLMENT_834_LOOP_1000B, ENROLLMENT_834_LOOP_2000, ENROLLMENT_834_LOOP_2100A, ENROLLMENT_834_LOOP_2300, ENROLLMENT_834_LOOP_2320, Eligibility271BuildError, Enrollment834BuildError, FATAL_CODES, HI_QUALIFIERS, HL_LEVEL_CODES, IK3_SYNTAX_ERROR_CODES, IK4_SYNTAX_ERROR_CODES, ISA_MIN_LENGTH, LoopSpecDefinitionError, MAINTENANCE_TYPE_CODES, NM1_QUALIFIERS, PREMIUM_820_BUILD_ERROR_CODES, PREMIUM_820_LOOP_1000A, PREMIUM_820_LOOP_1000B, PREMIUM_820_LOOP_2000A, PREMIUM_820_LOOP_2100A, PREMIUM_820_LOOP_2300A, PREMIUM_820_LOOP_2310A, Premium820BuildError, RARC, RELEASE_CHAR, REMIT_835_BUILD_ERROR_CODES, REMIT_835_LOOP_1000A, REMIT_835_LOOP_1000B, REMIT_835_LOOP_2000, REMIT_835_LOOP_2100, REMIT_835_LOOP_2110, Remit835BuildError, SERVICE_TYPE_CODES, STATUS_277_LOOP_2000A, STATUS_277_LOOP_2000B, STATUS_277_LOOP_2000C, STATUS_277_LOOP_2000D, STATUS_277_LOOP_2000E, STATUS_277_LOOP_2200, STATUS_277_LOOP_2220, ServicesReview278BuildError, TA1_ACK_CODES, TA1_NOTE_CODES, VERSION, WARNING_CODES, X12BuildError, X12Decimal, X12ParseError, X12ProfileError, X12_ACK_DISPOSITION_CODES, X12_BUILD_ERROR_CODES, build271, build277, build277CA, build278Request, build278Response, build820, build834, build835, build837D, build837I, build837P, build999, buildInterchange, buildTA1, checkClaimBalance, checkRemitTotalBalance, checkServiceLineBalance, collectElementValues, componentOptional, controlNumberMismatch, danglingReleaseChar, decodeSegment, defineLoopSpec, defineProfile, detectDelimiters, elementDecimal, elementDecimalOrZero, elementOptional, elementValue, escapeRelease, get271Eligibility, get277CADisposition, get277Status, get278Request, get278Response, get820Payments, get834Enrollments, get834Header, get835, get837Claims, getAllSegmentValues, getDefaultProfile, getSegmentValue, groupCountMismatch, hlParentLevelInvalid, hlParentMismatch, isAcceptDisposition, isClaimAdjustmentGroupCode, isDiagnosisQualifier, isProcedureQualifier, lookupCarc, lookupClaimStatus, lookupClaimStatusCategory, lookupClpStatus, lookupMaintenanceType, lookupRarc, lookupServiceType, missingGe, missingIea, missingRequiredLoop, missingSe, parse999, parseTA1, parseX12, partitionWarnings, pre005010, profiles, remitBalanceMismatch, resolveHiQualifier, segmentCountMismatch, serializeX12, setDefaultProfile, trailingGarbage, transactionCountMismatch, unescapeRelease, unexpectedSegment, unknown837Variant, unknownCarc, unknownClaimStatus, unknownClaimStatusCategory, unknownHiQualifier, unknownMaintenanceType, unknownRarc };
9645
+ export { ACK_BUILD_ERROR_CODES, ALL_WARNING_MESSAGES, AUTH_278_BUILD_ERROR_CODES, AUTH_278_LOOP_2000A, AUTH_278_LOOP_2000B, AUTH_278_LOOP_2000C, AUTH_278_LOOP_2000D, AUTH_278_LOOP_2000E, AUTH_278_LOOP_2000F, AckBuildError, BALANCE_INVARIANTS, CARC, CLAIM_837D_LOOP_2000A, CLAIM_837D_LOOP_2300, CLAIM_837D_LOOP_2400, CLAIM_837I_LOOP_2000A, CLAIM_837I_LOOP_2300, CLAIM_837I_LOOP_2400, CLAIM_837P_LOOP_2000A, CLAIM_837P_LOOP_2300, CLAIM_837P_LOOP_2400, CLAIM_837P_LOOP_2410, CLAIM_837_BUILD_ERROR_CODES, CLAIM_837_LOOP_1000A, CLAIM_837_LOOP_1000B, CLAIM_837_LOOP_2010AA, CLAIM_837_LOOP_2010BA, CLAIM_837_LOOP_2010BB, CLAIM_837_LOOP_2010CA, CLAIM_837_LOOP_2430, CLAIM_ADJUSTMENT_GROUP_CODES, CLAIM_STATUS_277_BUILD_ERROR_CODES, CLAIM_STATUS_CATEGORY_CODES, CLAIM_STATUS_CODES, CLP_STATUS, CONTROL_NUMBER_PAIRS, Claim837BuildError, ClaimStatus277BuildError, DELIMITER_POSITIONS, ELIGIBILITY_271_BUILD_ERROR_CODES, ELIGIBILITY_271_LOOP_2000A, ELIGIBILITY_271_LOOP_2000B, ELIGIBILITY_271_LOOP_2000C, ELIGIBILITY_271_LOOP_2000D, ELIGIBILITY_271_LOOP_2100C, ELIGIBILITY_271_LOOP_2100D, ELIGIBILITY_271_LOOP_2110, ENROLLMENT_834_BUILD_ERROR_CODES, ENROLLMENT_834_LOOP_1000A, ENROLLMENT_834_LOOP_1000B, ENROLLMENT_834_LOOP_2000, ENROLLMENT_834_LOOP_2100A, ENROLLMENT_834_LOOP_2300, ENROLLMENT_834_LOOP_2320, Eligibility271BuildError, Enrollment834BuildError, FATAL_CODES, HI_QUALIFIERS, HL_LEVEL_CODES, IK3_SYNTAX_ERROR_CODES, IK4_SYNTAX_ERROR_CODES, ISA_MIN_LENGTH, LoopSpecDefinitionError, MAINTENANCE_TYPE_CODES, NM1_QUALIFIERS, NON_SPEC_SEGMENT_ID, PREMIUM_820_BUILD_ERROR_CODES, PREMIUM_820_LOOP_1000A, PREMIUM_820_LOOP_1000B, PREMIUM_820_LOOP_2000A, PREMIUM_820_LOOP_2100A, PREMIUM_820_LOOP_2300A, PREMIUM_820_LOOP_2310A, Premium820BuildError, RARC, RELEASE_CHAR, REMIT_835_BUILD_ERROR_CODES, REMIT_835_LOOP_1000A, REMIT_835_LOOP_1000B, REMIT_835_LOOP_2000, REMIT_835_LOOP_2100, REMIT_835_LOOP_2110, REQUIRED_LOOPS, Remit835BuildError, SERVICE_TYPE_CODES, STATUS_277_LOOP_2000A, STATUS_277_LOOP_2000B, STATUS_277_LOOP_2000C, STATUS_277_LOOP_2000D, STATUS_277_LOOP_2000E, STATUS_277_LOOP_2200, STATUS_277_LOOP_2220, ServicesReview278BuildError, TA1_ACK_CODES, TA1_NOTE_CODES, UNEXPECTED_SEGMENT_CONTEXTS, VERSION, WARNING_CODES, X12BuildError, X12Decimal, X12ParseError, X12ProfileError, X12_ACK_DISPOSITION_CODES, X12_BUILD_ERROR_CODES, build271, build277, build277CA, build278Request, build278Response, build820, build834, build835, build837D, build837I, build837P, build999, buildInterchange, buildTA1, checkClaimBalance, checkRemitTotalBalance, checkServiceLineBalance, collectElementValues, componentOptional, controlNumberMismatch, danglingReleaseChar, decodeSegment, defineLoopSpec, defineProfile, detectDelimiters, elementDecimal, elementDecimalOrZero, elementOptional, elementValue, escapeRelease, get271Eligibility, get277CADisposition, get277Status, get278Request, get278Response, get820Payments, get834Enrollments, get834Header, get835, get837Claims, getAllSegmentValues, getDefaultProfile, getSegmentValue, groupCountMismatch, hlParentLevelInvalid, hlParentMismatch, isAcceptDisposition, isClaimAdjustmentGroupCode, isDiagnosisQualifier, isProcedureQualifier, lookupCarc, lookupClaimStatus, lookupClaimStatusCategory, lookupClpStatus, lookupMaintenanceType, lookupRarc, lookupServiceType, missingGe, missingIea, missingRequiredLoop, missingSe, parse999, parseTA1, parseX12, partitionWarnings, pre005010, profiles, remitBalanceMismatch, resolveHiQualifier, segmentCountMismatch, serializeX12, setDefaultProfile, trailingGarbage, transactionCountMismatch, unescapeRelease, unexpectedSegment, unknown837Variant, unknownCarc, unknownClaimStatus, unknownClaimStatusCategory, unknownHiQualifier, unknownMaintenanceType, unknownRarc };
9594
9646
  //# sourceMappingURL=index.mjs.map
9595
9647
  //# sourceMappingURL=index.mjs.map