@cosyte/x12 0.0.3 → 0.0.5

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(
@@ -1812,6 +1886,7 @@ function get835(delimiters, tx) {
1812
1886
  const warnings = [];
1813
1887
  const body = tx.se === void 0 ? tx.segments.slice(1) : tx.segments.slice(1, -1);
1814
1888
  let payment = EMPTY_HEADER;
1889
+ let bprSegmentIndex = 0;
1815
1890
  const traces = [];
1816
1891
  let payer;
1817
1892
  let payee;
@@ -1840,6 +1915,7 @@ function get835(delimiters, tx) {
1840
1915
  switch (seg.id) {
1841
1916
  case "BPR": {
1842
1917
  payment = decodeBpr(seg, delimiters);
1918
+ bprSegmentIndex = position.segmentIndex;
1843
1919
  break;
1844
1920
  }
1845
1921
  case "TRN": {
@@ -1913,7 +1989,7 @@ function get835(delimiters, tx) {
1913
1989
  case "CLP": {
1914
1990
  flushClaim();
1915
1991
  lastParty = void 0;
1916
- currentClaim = openClaim(seg, delimiters);
1992
+ currentClaim = openClaim(seg, delimiters, position.segmentIndex);
1917
1993
  break;
1918
1994
  }
1919
1995
  case "CAS": {
@@ -1959,7 +2035,7 @@ function get835(delimiters, tx) {
1959
2035
  const remarks = seg.id === "MIA" ? collectMiaRemarks(seg, delimiters) : collectMoaRemarks(seg, delimiters);
1960
2036
  for (const code of remarks) {
1961
2037
  const entry = lookupRarc(code);
1962
- if (entry === void 0) warnings.push(unknownRarc(position, code));
2038
+ if (entry === void 0) warnings.push(unknownRarc(position));
1963
2039
  currentClaim.remarks.push({ system: "HE", code, description: entry?.description });
1964
2040
  }
1965
2041
  break;
@@ -2007,7 +2083,7 @@ function get835(delimiters, tx) {
2007
2083
  payment.totalActualPayment,
2008
2084
  finalClaims,
2009
2085
  providerAdjustments,
2010
- { segmentIndex: 0, transactionIndex: 0 }
2086
+ { segmentIndex: bprSegmentIndex, transactionIndex: 0 }
2011
2087
  );
2012
2088
  if (remitTotalWarn !== void 0) warnings.push(remitTotalWarn);
2013
2089
  return Object.freeze({
@@ -2097,7 +2173,7 @@ function decodeLq(seg, delimiters, warnings, position) {
2097
2173
  let description;
2098
2174
  if (system === "HE") {
2099
2175
  const entry = lookupRarc(code);
2100
- if (entry === void 0) warnings.push(unknownRarc(position, code));
2176
+ if (entry === void 0) warnings.push(unknownRarc(position));
2101
2177
  description = entry?.description;
2102
2178
  }
2103
2179
  return Object.freeze({ system, code, description });
@@ -2129,7 +2205,7 @@ function decodeCasAdjustments(seg, delimiters, warnings, position) {
2129
2205
  if (reasonCode === void 0 && amount === void 0) continue;
2130
2206
  const code = reasonCode ?? "";
2131
2207
  const entry = code === "" ? void 0 : lookupCarc(code);
2132
- if (entry === void 0 && code !== "") warnings.push(unknownCarc(position, code));
2208
+ if (entry === void 0 && code !== "") warnings.push(unknownCarc(position));
2133
2209
  out.push(
2134
2210
  Object.freeze({
2135
2211
  groupCode,
@@ -2169,14 +2245,13 @@ function decodeNm1(seg, delimiters) {
2169
2245
  })
2170
2246
  };
2171
2247
  }
2172
- function openClaim(seg, delimiters) {
2248
+ function openClaim(seg, delimiters, clpSegmentIndex) {
2173
2249
  const claimStatusCode = elementValue(seg, 2, delimiters);
2174
2250
  const claimStatusDescription = lookupClpStatus(claimStatusCode)?.description;
2175
2251
  const facilityTypeCode = componentOptional(seg, 8, 1, delimiters) ?? elementOptional(seg, 8, delimiters);
2176
2252
  const claimFrequencyCode = componentOptional(seg, 8, 3, delimiters);
2177
2253
  return {
2178
- clpSegmentIndex: 0,
2179
- // populated in the future when we surface positions
2254
+ clpSegmentIndex,
2180
2255
  patientControlNumber: elementValue(seg, 1, delimiters),
2181
2256
  claimStatusCode,
2182
2257
  claimStatusDescription,
@@ -2369,10 +2444,32 @@ var Remit835BuildError = class extends Error {
2369
2444
  }
2370
2445
  };
2371
2446
 
2447
+ // src/builder/caller-value.ts
2448
+ var BUILD_REFUSAL_VALUE_MAX_LENGTH = 63;
2449
+ var MAX_LENGTH_DIGITS = 10;
2450
+ var BUILD_REFUSAL_VALUE_MAX_RENDERED = BUILD_REFUSAL_VALUE_MAX_LENGTH + 2 + 1 + " ( characters)".length + MAX_LENGTH_DIGITS;
2451
+ function renderCallerValue(value) {
2452
+ const text = coerce(value);
2453
+ if (text.length <= BUILD_REFUSAL_VALUE_MAX_LENGTH) return `"${text}"`;
2454
+ return `"${text.slice(0, BUILD_REFUSAL_VALUE_MAX_LENGTH)}\u2026" (${String(text.length)} characters)`;
2455
+ }
2456
+ function coerce(value) {
2457
+ if (typeof value === "string") return value;
2458
+ try {
2459
+ return String(value);
2460
+ } catch {
2461
+ return "[unrenderable value]";
2462
+ }
2463
+ }
2464
+
2372
2465
  // src/transactions/remit/build-835.ts
2373
2466
  var X221A1_VERSION_RELEASE = "005010X221A1";
2374
2467
  var X12_835_FUNCTIONAL_ID = "HP";
2375
2468
  var X12_AGENCY_CODE = "X";
2469
+ var UNANCHORED_BUILD_POSITION = Object.freeze({
2470
+ segmentIndex: 0,
2471
+ transactionIndex: 0
2472
+ });
2376
2473
  function build835(spec) {
2377
2474
  const { envelope } = spec;
2378
2475
  enforceStructuralSpec(spec);
@@ -2546,7 +2643,7 @@ function enforceStructuralSpec(spec) {
2546
2643
  }
2547
2644
  }
2548
2645
  function enforceBalance(spec) {
2549
- const position = { segmentIndex: 0, transactionIndex: 0 };
2646
+ const position = UNANCHORED_BUILD_POSITION;
2550
2647
  const claims = spec.claims.map(materializeClaim);
2551
2648
  for (const claim of claims) {
2552
2649
  const claimWarn = checkClaimBalance(claim, position);
@@ -2852,7 +2949,7 @@ function padControl(value, width) {
2852
2949
  if (value.length < width) return "0".repeat(width - value.length) + value;
2853
2950
  throw new Remit835BuildError(
2854
2951
  REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
2855
- `build835: control number "${value}" exceeds the ${String(width)}-char spec limit.`
2952
+ `build835: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
2856
2953
  );
2857
2954
  }
2858
2955
  function expandYY(yymmdd) {
@@ -2982,12 +3079,7 @@ function get837Claims(delimiters, tx, opts) {
2982
3079
  }
2983
3080
  const variant = explicitType ?? variantFromIcr ?? variantFromSegment ?? "unknown";
2984
3081
  if (variant === "unknown") {
2985
- warnings.push(
2986
- unknown837Variant(
2987
- { segmentIndex: 1, transactionIndex: 0 },
2988
- implementationConventionReference
2989
- )
2990
- );
3082
+ warnings.push(unknown837Variant({ segmentIndex: 1, transactionIndex: 0 }));
2991
3083
  }
2992
3084
  const hierarchies = [];
2993
3085
  const hlIndex = /* @__PURE__ */ new Map();
@@ -3228,28 +3320,16 @@ function get837Claims(delimiters, tx, opts) {
3228
3320
  flushClaim();
3229
3321
  if (currentBillingHl === void 0 || currentSubscriberHl === void 0 || currentSubscriberMember === void 0 || currentPayer === void 0) {
3230
3322
  if (currentBillingHl === void 0) {
3231
- warnings.push(
3232
- missingRequiredLoop(position, "2000A", "no Billing Provider HL precedes the CLM")
3233
- );
3323
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.BILLING_PROVIDER_2000A));
3234
3324
  }
3235
3325
  if (currentSubscriberHl === void 0) {
3236
- warnings.push(
3237
- missingRequiredLoop(position, "2000B", "no Subscriber HL precedes the CLM")
3238
- );
3326
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.SUBSCRIBER_2000B));
3239
3327
  }
3240
3328
  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
- );
3329
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.SUBSCRIBER_NAME_2010BA));
3248
3330
  }
3249
3331
  if (currentPayer === void 0 && currentSubscriberHl !== void 0) {
3250
- warnings.push(
3251
- missingRequiredLoop(position, "2010BB", "no Payer Name follows the Subscriber HL")
3252
- );
3332
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.PAYER_NAME_2010BB));
3253
3333
  }
3254
3334
  }
3255
3335
  currentClaim = openClaim2(seg, delimiters, {
@@ -3394,30 +3474,21 @@ function validateHl(hl, index, position, warnings) {
3394
3474
  const expectedParent = EXPECTED_PARENT_LEVEL[hl.levelCode];
3395
3475
  if (expectedParent === void 0) {
3396
3476
  if (hl.levelCode === HL_LEVEL_CODES.INFORMATION_SOURCE && hl.parentHlId !== void 0) {
3397
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
3477
+ warnings.push(hlParentMismatch(position));
3398
3478
  }
3399
3479
  return;
3400
3480
  }
3401
3481
  if (hl.parentHlId === void 0) {
3402
- warnings.push(hlParentMismatch(position, hl.hlId, ""));
3482
+ warnings.push(hlParentMismatch(position));
3403
3483
  return;
3404
3484
  }
3405
3485
  const parent = index.get(hl.parentHlId);
3406
3486
  if (parent === void 0) {
3407
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
3487
+ warnings.push(hlParentMismatch(position));
3408
3488
  return;
3409
3489
  }
3410
3490
  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
- );
3491
+ warnings.push(hlParentLevelInvalid(position));
3421
3492
  }
3422
3493
  }
3423
3494
  function decodeSbr(seg, delimiters) {
@@ -3518,7 +3589,7 @@ function decodeHi(seg, delimiters, warnings, position) {
3518
3589
  const codeSystem = resolved?.system ?? "unknown";
3519
3590
  const category = resolved?.category ?? "unknown";
3520
3591
  if (qualifier !== void 0 && resolved === void 0) {
3521
- warnings.push(unknownHiQualifier(position, qualifier));
3592
+ warnings.push(unknownHiQualifier(position));
3522
3593
  }
3523
3594
  out.push(
3524
3595
  Object.freeze({
@@ -3561,7 +3632,7 @@ function decodeCas(seg, delimiters, warnings, position) {
3561
3632
  if (reasonCode === void 0 && amount === void 0) continue;
3562
3633
  const code = reasonCode ?? "";
3563
3634
  const entry = code === "" ? void 0 : lookupCarc(code);
3564
- if (entry === void 0 && code !== "") warnings.push(unknownCarc(position, code));
3635
+ if (entry === void 0 && code !== "") warnings.push(unknownCarc(position));
3565
3636
  out.push(
3566
3637
  Object.freeze({
3567
3638
  groupCode,
@@ -4227,7 +4298,7 @@ function enforceClaim(variant, claim, locator) {
4227
4298
  if (line.variant !== variant) {
4228
4299
  throw new Claim837BuildError(
4229
4300
  CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC,
4230
- `build837${variant}: claim at ${locator} has a "${line.variant}" service line at index ${String(l)}; every line must be "${variant}".`
4301
+ `build837${variant}: claim at ${locator} has a ${renderCallerValue(line.variant)} service line at index ${String(l)}; every line must be "${variant}".`
4231
4302
  );
4232
4303
  }
4233
4304
  const code = line.variant === "I" ? line.revenueCode : line.procedureCode;
@@ -4571,7 +4642,7 @@ function padControl2(value, width) {
4571
4642
  if (value.length < width) return "0".repeat(width - value.length) + value;
4572
4643
  throw new Claim837BuildError(
4573
4644
  CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC,
4574
- `build837: control number "${value}" exceeds the ${String(width)}-char spec limit.`
4645
+ `build837: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
4575
4646
  );
4576
4647
  }
4577
4648
  function expandYY2(yymmdd) {
@@ -4803,30 +4874,21 @@ function validateHl2(hl, index, expectedParentLevel, position, warnings) {
4803
4874
  const expectedParent = hasExpectation ? expectedParentLevel[hl.levelCode] : void 0;
4804
4875
  if (expectedParent === void 0) {
4805
4876
  if (hasExpectation && hl.parentHlId !== void 0) {
4806
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
4877
+ warnings.push(hlParentMismatch(position));
4807
4878
  }
4808
4879
  return;
4809
4880
  }
4810
4881
  if (hl.parentHlId === void 0) {
4811
- warnings.push(hlParentMismatch(position, hl.hlId, ""));
4882
+ warnings.push(hlParentMismatch(position));
4812
4883
  return;
4813
4884
  }
4814
4885
  const parent = index.get(hl.parentHlId);
4815
4886
  if (parent === void 0) {
4816
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
4887
+ warnings.push(hlParentMismatch(position));
4817
4888
  return;
4818
4889
  }
4819
4890
  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
- );
4891
+ warnings.push(hlParentLevelInvalid(position));
4830
4892
  }
4831
4893
  }
4832
4894
 
@@ -5445,7 +5507,7 @@ function padControl3(value, width) {
5445
5507
  if (value.length < width) return "0".repeat(width - value.length) + value;
5446
5508
  throw new Eligibility271BuildError(
5447
5509
  ELIGIBILITY_271_BUILD_ERROR_CODES.X12_271_BUILD_INVALID_SPEC,
5448
- `build271: control number "${value}" exceeds the ${String(width)}-char spec limit.`
5510
+ `build271: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
5449
5511
  );
5450
5512
  }
5451
5513
  function expandYY3(yymmdd) {
@@ -5779,11 +5841,11 @@ function decodeStatusComposite(seg, n, delimiters, warnings, position) {
5779
5841
  if (categoryCode === void 0 && statusCode === void 0) return void 0;
5780
5842
  const category = categoryCode === void 0 ? void 0 : lookupClaimStatusCategory(categoryCode);
5781
5843
  if (categoryCode !== void 0 && category === void 0) {
5782
- warnings.push(unknownClaimStatusCategory(position, categoryCode));
5844
+ warnings.push(unknownClaimStatusCategory(position));
5783
5845
  }
5784
5846
  const status = statusCode === void 0 ? void 0 : lookupClaimStatus(statusCode);
5785
5847
  if (statusCode !== void 0 && status === void 0) {
5786
- warnings.push(unknownClaimStatus(position, statusCode));
5848
+ warnings.push(unknownClaimStatus(position));
5787
5849
  }
5788
5850
  return Object.freeze({
5789
5851
  categoryCode: categoryCode ?? "",
@@ -6219,7 +6281,7 @@ function padControl4(value, width) {
6219
6281
  if (value.length < width) return "0".repeat(width - value.length) + value;
6220
6282
  throw new ClaimStatus277BuildError(
6221
6283
  CLAIM_STATUS_277_BUILD_ERROR_CODES.X12_277_BUILD_INVALID_SPEC,
6222
- `build277: control number "${value}" exceeds the ${String(width)}-char spec limit.`
6284
+ `build277: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
6223
6285
  );
6224
6286
  }
6225
6287
  function expandYY4(yymmdd) {
@@ -6876,7 +6938,7 @@ function padControl5(value, width) {
6876
6938
  if (value.length < width) return "0".repeat(width - value.length) + value;
6877
6939
  throw new Premium820BuildError(
6878
6940
  PREMIUM_820_BUILD_ERROR_CODES.X12_820_BUILD_INVALID_SPEC,
6879
- `build820: control number "${value}" exceeds the ${String(width)}-char spec limit.`
6941
+ `build820: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
6880
6942
  );
6881
6943
  }
6882
6944
  function expandYY5(yymmdd) {
@@ -7107,7 +7169,7 @@ function openEnrollment(seg, delimiters, position) {
7107
7169
  const entry = maintenanceTypeCode === "" ? void 0 : lookupMaintenanceType(maintenanceTypeCode);
7108
7170
  const warnings = [];
7109
7171
  if (maintenanceTypeCode !== "" && entry === void 0) {
7110
- warnings.push(unknownMaintenanceType(position, maintenanceTypeCode));
7172
+ warnings.push(unknownMaintenanceType(position));
7111
7173
  }
7112
7174
  return {
7113
7175
  subscriberIndicator: elementOptional(seg, 1, delimiters),
@@ -7147,7 +7209,7 @@ function openCoverage(seg, delimiters, position, warnings) {
7147
7209
  let maintenanceTypeDescription;
7148
7210
  if (maintenanceTypeCode !== void 0) {
7149
7211
  const entry = lookupMaintenanceType(maintenanceTypeCode);
7150
- if (entry === void 0) warnings.push(unknownMaintenanceType(position, maintenanceTypeCode));
7212
+ if (entry === void 0) warnings.push(unknownMaintenanceType(position));
7151
7213
  maintenanceTypeDescription = entry?.description;
7152
7214
  }
7153
7215
  return {
@@ -7391,7 +7453,7 @@ function enforceStructuralSpec6(spec) {
7391
7453
  if (lookupMaintenanceType(member.maintenanceTypeCode) === void 0) {
7392
7454
  throw new Enrollment834BuildError(
7393
7455
  ENROLLMENT_834_BUILD_ERROR_CODES.X12_834_BUILD_UNKNOWN_MAINTENANCE_TYPE,
7394
- `build834: member at index ${String(m)} has an unknown INS-03 maintenance type code "${member.maintenanceTypeCode}" (outside X12 Code Source 875).`
7456
+ `build834: member at index ${String(m)} has an unknown INS-03 maintenance type code ${renderCallerValue(member.maintenanceTypeCode)} (outside X12 Code Source 875).`
7395
7457
  );
7396
7458
  }
7397
7459
  for (let c = 0; c < (member.healthCoverages ?? []).length; c += 1) {
@@ -7401,7 +7463,7 @@ function enforceStructuralSpec6(spec) {
7401
7463
  if (hd01 !== void 0 && hd01 !== "" && lookupMaintenanceType(hd01) === void 0) {
7402
7464
  throw new Enrollment834BuildError(
7403
7465
  ENROLLMENT_834_BUILD_ERROR_CODES.X12_834_BUILD_UNKNOWN_MAINTENANCE_TYPE,
7404
- `build834: coverage at member[${String(m)}].healthCoverages[${String(c)}] has an unknown HD-01 maintenance type code "${hd01}" (outside X12 Code Source 875).`
7466
+ `build834: coverage at member[${String(m)}].healthCoverages[${String(c)}] has an unknown HD-01 maintenance type code ${renderCallerValue(hd01)} (outside X12 Code Source 875).`
7405
7467
  );
7406
7468
  }
7407
7469
  }
@@ -7550,7 +7612,7 @@ function padControl6(value, width) {
7550
7612
  if (value.length < width) return "0".repeat(width - value.length) + value;
7551
7613
  throw new Enrollment834BuildError(
7552
7614
  ENROLLMENT_834_BUILD_ERROR_CODES.X12_834_BUILD_INVALID_SPEC,
7553
- `build834: control number "${value}" exceeds the ${String(width)}-char spec limit.`
7615
+ `build834: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
7554
7616
  );
7555
7617
  }
7556
7618
  function expandYY6(yymmdd) {
@@ -7860,7 +7922,7 @@ function decodeHiDiagnoses(seg, delimiters, warnings, position) {
7860
7922
  const resolved = qualifier === void 0 ? void 0 : resolveHiQualifier(qualifier);
7861
7923
  const codeSystem = resolved?.system ?? "unknown";
7862
7924
  if (qualifier !== void 0 && resolved === void 0) {
7863
- warnings.push(unknownHiQualifier(position, qualifier));
7925
+ warnings.push(unknownHiQualifier(position));
7864
7926
  }
7865
7927
  out.push(Object.freeze({ qualifier: qualifier ?? "", code: code ?? "", codeSystem }));
7866
7928
  }
@@ -8206,7 +8268,7 @@ function padControl7(value, width) {
8206
8268
  if (value.length < width) return "0".repeat(width - value.length) + value;
8207
8269
  throw new ServicesReview278BuildError(
8208
8270
  AUTH_278_BUILD_ERROR_CODES.X12_278_BUILD_INVALID_SPEC,
8209
- `build278: control number "${value}" exceeds the ${String(width)}-char spec limit.`
8271
+ `build278: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
8210
8272
  );
8211
8273
  }
8212
8274
  function expandYY7(yymmdd) {
@@ -8808,7 +8870,7 @@ function enforceAcceptIsClean(group) {
8808
8870
  if (functionalIsAccept && functionalHasErrorCodes) {
8809
8871
  throw new AckBuildError(
8810
8872
  ACK_BUILD_ERROR_CODES.X12_ACK_ACCEPT_WITH_ERRORS,
8811
- `build999: AK9-01 was 'A' (Accept) but AK9-05..AK9-09 carried ${String(
8873
+ `build999: AK9-01 was 'A' (Accept) but AK9-05..AK9-09 carried ${renderCallerValue(
8812
8874
  (group.syntaxErrorCodes ?? []).length
8813
8875
  )} syntax error code(s). Use 'E' (Accept, with errors noted) or a reject disposition instead.`
8814
8876
  );
@@ -8819,13 +8881,13 @@ function enforceAcceptIsClean(group) {
8819
8881
  if (responseIsAccept && responseHasErrors) {
8820
8882
  throw new AckBuildError(
8821
8883
  ACK_BUILD_ERROR_CODES.X12_ACK_ACCEPT_WITH_ERRORS,
8822
- `build999: AK2 (ST-02 ${response.transactionSetControlNumber}) IK5-01 was 'A' (Accept) but the response carried error payload. Use 'E' (Accept, with errors noted) or a reject disposition instead.`
8884
+ `build999: AK2 (ST-02 ${renderCallerValue(response.transactionSetControlNumber)}) IK5-01 was 'A' (Accept) but the response carried error payload. Use 'E' (Accept, with errors noted) or a reject disposition instead.`
8823
8885
  );
8824
8886
  }
8825
8887
  if (functionalIsAccept && (!responseIsAccept || responseHasErrors)) {
8826
8888
  throw new AckBuildError(
8827
8889
  ACK_BUILD_ERROR_CODES.X12_ACK_ACCEPT_WITH_ERRORS,
8828
- `build999: AK9-01 was 'A' (Accept) but transaction response (ST-02 ${response.transactionSetControlNumber}) reported a non-accept disposition or carried errors. Lift the functional disposition to 'P' (Partial), 'E' (Accept, errors noted), or a reject.`
8890
+ `build999: AK9-01 was 'A' (Accept) but transaction response (ST-02 ${renderCallerValue(response.transactionSetControlNumber)}) reported a non-accept disposition or carried errors. Lift the functional disposition to 'P' (Partial), 'E' (Accept, errors noted), or a reject.`
8829
8891
  );
8830
8892
  }
8831
8893
  }
@@ -8837,25 +8899,25 @@ function enforceCountInvariants(group) {
8837
8899
  if (declared < 0 || received < 0 || accepted < 0) {
8838
8900
  throw new AckBuildError(
8839
8901
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8840
- `build999: AK9 counts must be non-negative (declared=${String(declared)}, received=${String(received)}, accepted=${String(accepted)}).`
8902
+ `build999: AK9 counts must be non-negative (declared=${renderCallerValue(declared)}, received=${renderCallerValue(received)}, accepted=${renderCallerValue(accepted)}).`
8841
8903
  );
8842
8904
  }
8843
8905
  if (accepted > received) {
8844
8906
  throw new AckBuildError(
8845
8907
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8846
- `build999: AK9-04 accepted (${String(accepted)}) cannot exceed AK9-03 received (${String(received)}).`
8908
+ `build999: AK9-04 accepted (${renderCallerValue(accepted)}) cannot exceed AK9-03 received (${renderCallerValue(received)}).`
8847
8909
  );
8848
8910
  }
8849
8911
  if (received > declared) {
8850
8912
  throw new AckBuildError(
8851
8913
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8852
- `build999: AK9-03 received (${String(received)}) cannot exceed AK9-02 declared (${String(declared)}).`
8914
+ `build999: AK9-03 received (${renderCallerValue(received)}) cannot exceed AK9-02 declared (${renderCallerValue(declared)}).`
8853
8915
  );
8854
8916
  }
8855
8917
  if (group.transactionResponses.length !== received) {
8856
8918
  throw new AckBuildError(
8857
8919
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8858
- `build999: AK9-03 received (${String(received)}) must equal the number of supplied transaction responses (${String(group.transactionResponses.length)}).`
8920
+ `build999: AK9-03 received (${renderCallerValue(received)}) must equal the number of supplied transaction responses (${renderCallerValue(group.transactionResponses.length)}).`
8859
8921
  );
8860
8922
  }
8861
8923
  }
@@ -8924,7 +8986,7 @@ function buildIk5(response, esc, elementSeparator, segmentTerminator) {
8924
8986
  if (codes.length > 5) {
8925
8987
  throw new AckBuildError(
8926
8988
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8927
- `build999: IK5 accepts up to 5 syntax error codes (IK5-02..IK5-06); ${String(codes.length)} supplied.`
8989
+ `build999: IK5 accepts up to 5 syntax error codes (IK5-02..IK5-06); ${renderCallerValue(codes.length)} supplied.`
8928
8990
  );
8929
8991
  }
8930
8992
  for (const code of codes) parts.push(esc(code));
@@ -8942,7 +9004,7 @@ function buildAk9(group, esc, elementSeparator, segmentTerminator) {
8942
9004
  if (codes.length > 5) {
8943
9005
  throw new AckBuildError(
8944
9006
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8945
- `build999: AK9 accepts up to 5 syntax error codes (AK9-05..AK9-09); ${String(codes.length)} supplied.`
9007
+ `build999: AK9 accepts up to 5 syntax error codes (AK9-05..AK9-09); ${renderCallerValue(codes.length)} supplied.`
8946
9008
  );
8947
9009
  }
8948
9010
  for (const code of codes) parts.push(esc(code));
@@ -8961,7 +9023,7 @@ function padControl8(value, width) {
8961
9023
  if (value.length < width) return "0".repeat(width - value.length) + value;
8962
9024
  throw new AckBuildError(
8963
9025
  ACK_BUILD_ERROR_CODES.X12_ACK_INVALID_SPEC,
8964
- `build999: control number "${value}" exceeds the ${String(width)}-char spec limit.`
9026
+ `build999: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
8965
9027
  );
8966
9028
  }
8967
9029
  function expandYY8(yymmdd) {
@@ -8991,7 +9053,7 @@ function enforceAcceptIsClean2(spec) {
8991
9053
  if (spec.ackCode === TA1_ACK_CODES.A && spec.noteCode !== "000") {
8992
9054
  throw new AckBuildError(
8993
9055
  ACK_BUILD_ERROR_CODES.X12_TA1_ACCEPT_WITH_NOTE,
8994
- `buildTA1: TA1-04 was "A" (Accept) but TA1-05 carried note "${spec.noteCode}". An accept must cite "000" (no error). Use ackCode "E" (accept, errors noted) when the inbound had defects you elected to ignore.`
9056
+ `buildTA1: TA1-04 was "A" (Accept) but TA1-05 carried note ${renderCallerValue(spec.noteCode)}. An accept must cite "000" (no error). Use ackCode "E" (accept, errors noted) when the inbound had defects you elected to ignore.`
8995
9057
  );
8996
9058
  }
8997
9059
  }
@@ -9008,18 +9070,39 @@ function serializeX12(interchange, opts = {}) {
9008
9070
  };
9009
9071
  let out = interchange.isa.raw;
9010
9072
  let segIdx = 0;
9073
+ const orphans = interchange.orphanSegments ?? [];
9074
+ const placed = /* @__PURE__ */ new Set();
9075
+ const flushOrphans = (slot) => {
9076
+ let written = 0;
9077
+ for (const [i, orphan] of orphans.entries()) {
9078
+ if (placed.has(i)) continue;
9079
+ if (!slot(orphan.anchor)) continue;
9080
+ placed.add(i);
9081
+ out += orphan.raw + term;
9082
+ segIdx++;
9083
+ written++;
9084
+ }
9085
+ return written;
9086
+ };
9011
9087
  for (const ta1 of interchange.ta1Segments) {
9012
9088
  out += ta1.raw + term;
9013
9089
  segIdx++;
9014
9090
  }
9015
9091
  for (const [g, group] of interchange.groups.entries()) {
9092
+ flushOrphans((a) => a?.kind === "interchange" && a.groupIndex === g);
9016
9093
  const gsSegIdx = ++segIdx;
9017
9094
  out += group.gs.raw + term;
9018
9095
  for (const [t, tx] of group.transactions.entries()) {
9019
- const stSegIdx = segIdx + 1;
9020
- const segCount = tx.rawSegments.length;
9096
+ flushOrphans((a) => a?.kind === "group" && a.groupIndex === g && a.transactionIndex === t);
9021
9097
  const lastIdx = tx.rawSegments.length - 1;
9098
+ let segCount = tx.rawSegments.length;
9099
+ let stSegIdx = segIdx + 1;
9022
9100
  for (const [k, raw] of tx.rawSegments.entries()) {
9101
+ const inside = flushOrphans(
9102
+ (a) => a?.kind === "transaction" && a.groupIndex === g && a.transactionIndex === t && a.segmentOffset === k
9103
+ );
9104
+ if (k === 0) stSegIdx = segIdx + 1;
9105
+ else segCount += inside;
9023
9106
  segIdx++;
9024
9107
  if (recompute && tx.se !== void 0 && k === lastIdx) {
9025
9108
  out += substituteElement(tx.se.elements, 1, String(segCount), elementSep) + term;
@@ -9027,21 +9110,20 @@ function serializeX12(interchange, opts = {}) {
9027
9110
  out += raw + term;
9028
9111
  }
9029
9112
  }
9113
+ flushOrphans(
9114
+ (a) => a?.kind === "transaction" && a.groupIndex === g && a.transactionIndex === t && a.segmentOffset === tx.rawSegments.length
9115
+ );
9030
9116
  if (specClean && tx.se !== void 0) {
9031
9117
  const declaredSe = elementAt(tx.se.elements, 1);
9032
9118
  if (declaredSe !== String(segCount)) {
9033
9119
  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
- )
9120
+ segmentCountMismatch({
9121
+ segmentIndex: stSegIdx,
9122
+ interchangeIndex: 0,
9123
+ groupIndex: g,
9124
+ transactionIndex: t,
9125
+ elementIndex: 1
9126
+ })
9045
9127
  );
9046
9128
  }
9047
9129
  const st02 = elementAt(tx.st.elements, 2);
@@ -9056,14 +9138,15 @@ function serializeX12(interchange, opts = {}) {
9056
9138
  transactionIndex: t,
9057
9139
  elementIndex: 2
9058
9140
  },
9059
- "ST-02/SE-02",
9060
- st02,
9061
- se02
9141
+ CONTROL_NUMBER_PAIRS.TRANSACTION
9062
9142
  )
9063
9143
  );
9064
9144
  }
9065
9145
  }
9066
9146
  }
9147
+ flushOrphans(
9148
+ (a) => a?.kind === "group" && a.groupIndex === g && a.transactionIndex === group.transactions.length
9149
+ );
9067
9150
  if (group.ge !== void 0) {
9068
9151
  const txCount = group.transactions.length;
9069
9152
  segIdx++;
@@ -9076,11 +9159,12 @@ function serializeX12(interchange, opts = {}) {
9076
9159
  const declaredGe = elementAt(group.ge.elements, 1);
9077
9160
  if (declaredGe !== String(txCount)) {
9078
9161
  emit(
9079
- transactionCountMismatch(
9080
- { segmentIndex: gsSegIdx, interchangeIndex: 0, groupIndex: g, elementIndex: 1 },
9081
- declaredGe,
9082
- txCount
9083
- )
9162
+ transactionCountMismatch({
9163
+ segmentIndex: gsSegIdx,
9164
+ interchangeIndex: 0,
9165
+ groupIndex: g,
9166
+ elementIndex: 1
9167
+ })
9084
9168
  );
9085
9169
  }
9086
9170
  const gs06 = elementAt(group.gs.elements, 6);
@@ -9089,15 +9173,15 @@ function serializeX12(interchange, opts = {}) {
9089
9173
  emit(
9090
9174
  controlNumberMismatch(
9091
9175
  { segmentIndex: gsSegIdx, interchangeIndex: 0, groupIndex: g, elementIndex: 2 },
9092
- "GS-06/GE-02",
9093
- gs06,
9094
- ge02
9176
+ CONTROL_NUMBER_PAIRS.GROUP
9095
9177
  )
9096
9178
  );
9097
9179
  }
9098
9180
  }
9099
9181
  }
9100
9182
  }
9183
+ flushOrphans((a) => a?.kind === "interchange" && a.groupIndex === interchange.groups.length);
9184
+ flushOrphans(() => true);
9101
9185
  if (interchange.iea !== void 0) {
9102
9186
  const groupCount = interchange.groups.length;
9103
9187
  segIdx++;
@@ -9109,13 +9193,7 @@ function serializeX12(interchange, opts = {}) {
9109
9193
  if (specClean) {
9110
9194
  const declaredIea = elementAt(interchange.iea.elements, 1);
9111
9195
  if (declaredIea !== String(groupCount)) {
9112
- emit(
9113
- groupCountMismatch(
9114
- { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 },
9115
- declaredIea,
9116
- groupCount
9117
- )
9118
- );
9196
+ emit(groupCountMismatch({ segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 }));
9119
9197
  }
9120
9198
  const isa13 = elementAt(interchange.isa.elements, 13);
9121
9199
  const iea02 = elementAt(interchange.iea.elements, 2);
@@ -9123,9 +9201,7 @@ function serializeX12(interchange, opts = {}) {
9123
9201
  emit(
9124
9202
  controlNumberMismatch(
9125
9203
  { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 2 },
9126
- "ISA-13/IEA-02",
9127
- isa13,
9128
- iea02
9204
+ CONTROL_NUMBER_PAIRS.INTERCHANGE
9129
9205
  )
9130
9206
  );
9131
9207
  }
@@ -9268,7 +9344,11 @@ function buildTransaction(tx, esc, elementSeparator, segmentTerminator) {
9268
9344
  if (segment.length === 0 || (segment[0] ?? "") === "") {
9269
9345
  throw new X12BuildError(
9270
9346
  X12_BUILD_ERROR_CODES.X12_BUILD_INVALID_SPEC,
9271
- `buildInterchange: a segment spec in transaction "${esc(tx.transactionSetIdCode)}" has no segment id.`
9347
+ // Rendered from the RAW id, not the `?`-escaped one. `esc` can double
9348
+ // the length, so escaping first made the reported "(N characters)" the
9349
+ // escaped length rather than the caller's: a 100-character all-`?` id
9350
+ // reported 200. The escape exists for the wire, and this is a message.
9351
+ `buildInterchange: a segment spec in transaction ${renderCallerValue(tx.transactionSetIdCode)} has no segment id.`
9272
9352
  );
9273
9353
  }
9274
9354
  bodySegments += joinSeg2(segment.map(esc), elementSeparator, segmentTerminator);
@@ -9294,7 +9374,7 @@ function padControl9(value, width) {
9294
9374
  if (value.length < width) return "0".repeat(width - value.length) + value;
9295
9375
  throw new X12BuildError(
9296
9376
  X12_BUILD_ERROR_CODES.X12_BUILD_INVALID_SPEC,
9297
- `buildInterchange: control number "${value}" exceeds the ${String(width)}-char spec limit.`
9377
+ `buildInterchange: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
9298
9378
  );
9299
9379
  }
9300
9380
  function expandYY9(yymmdd) {
@@ -9588,8 +9668,8 @@ var profiles = Object.freeze({
9588
9668
  });
9589
9669
 
9590
9670
  // src/index.ts
9591
- var VERSION = "0.0.3";
9671
+ var VERSION = "0.0.5";
9592
9672
 
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 };
9673
+ 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, BUILD_REFUSAL_VALUE_MAX_LENGTH, BUILD_REFUSAL_VALUE_MAX_RENDERED, 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, renderCallerValue, resolveHiQualifier, segmentCountMismatch, serializeX12, setDefaultProfile, trailingGarbage, transactionCountMismatch, unescapeRelease, unexpectedSegment, unknown837Variant, unknownCarc, unknownClaimStatus, unknownClaimStatusCategory, unknownHiQualifier, unknownMaintenanceType, unknownRarc };
9594
9674
  //# sourceMappingURL=index.mjs.map
9595
9675
  //# sourceMappingURL=index.mjs.map