@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.cjs CHANGED
@@ -105,7 +105,10 @@ function detectDelimiters(raw) {
105
105
  if (raw.charAt(pos) !== element) {
106
106
  throw new X12ParseError(
107
107
  FATAL_CODES.X12_INVALID_DELIMITERS,
108
- `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.`,
108
+ // The detected separator is a single byte read out of the input, so
109
+ // it is not echoed: the diagnostic is which fixed ISA position broke,
110
+ // and the byte itself is at `raw[3]` for anyone who needs it.
111
+ `The element separator detected at ISA byte 4 is missing at fixed ISA byte ${String(pos + 1)} - ISA element layout is not 005010-conformant.`,
109
112
  fatalPosition,
110
113
  snip
111
114
  );
@@ -139,182 +142,256 @@ var WARNING_CODES = {
139
142
  X12_UNKNOWN_CLAIM_STATUS: "X12_UNKNOWN_CLAIM_STATUS",
140
143
  X12_834_UNKNOWN_MAINTENANCE_TYPE: "X12_834_UNKNOWN_MAINTENANCE_TYPE"
141
144
  };
142
- function controlNumberMismatch(position, pair, header, trailer) {
145
+ var CONTROL_NUMBER_PAIRS = {
146
+ /** ISA-13 against IEA-02. */
147
+ INTERCHANGE: "interchange",
148
+ /** GS-06 against GE-02. */
149
+ GROUP: "group",
150
+ /** ST-02 against SE-02. */
151
+ TRANSACTION: "transaction"
152
+ };
153
+ var UNEXPECTED_SEGMENT_CONTEXTS = {
154
+ /** A `TA1` (envelope-level by spec) appeared inside an open functional group. */
155
+ TA1_INSIDE_GROUP: "ta1-inside-group",
156
+ /** A `GE` appeared with no open `GS`. */
157
+ GE_WITHOUT_GS: "ge-without-gs",
158
+ /** An `ST` appeared with no open functional group. */
159
+ ST_WITHOUT_GS: "st-without-gs",
160
+ /** An `SE` appeared with no open transaction set. */
161
+ SE_WITHOUT_ST: "se-without-st",
162
+ /** A body segment appeared outside any open transaction set. */
163
+ BODY_OUTSIDE_TRANSACTION: "body-outside-transaction"
164
+ };
165
+ var BALANCE_INVARIANTS = {
166
+ /** `CLP-04 + Σ(claim CAS + line CAS) == CLP-03`. */
167
+ CLAIM: "claim",
168
+ /** `SVC-03 + Σ(line CAS) == SVC-02`. */
169
+ SERVICE_LINE: "service-line",
170
+ /** `Σ(CLP-04) - Σ(PLB amounts) == BPR-02`. */
171
+ REMIT_TOTAL: "remit-total"
172
+ };
173
+ var REQUIRED_LOOPS = {
174
+ /** Loop 2000A, Billing Provider HL. */
175
+ BILLING_PROVIDER_2000A: "2000A",
176
+ /** Loop 2000B, Subscriber HL. */
177
+ SUBSCRIBER_2000B: "2000B",
178
+ /** Loop 2010BA, Subscriber Name. */
179
+ SUBSCRIBER_NAME_2010BA: "2010BA",
180
+ /** Loop 2010BB, Payer Name. */
181
+ PAYER_NAME_2010BB: "2010BB"
182
+ };
183
+ var WARNING_MESSAGES = {
184
+ 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.",
185
+ 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.",
186
+ 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.",
187
+ 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.',
188
+ 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.",
189
+ 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.",
190
+ 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 })`.",
191
+ 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).",
192
+ X12_MISSING_IEA: "Interchange has no IEA trailer - input is truncated.",
193
+ X12_MISSING_GE: "Functional group has no GE trailer - group is truncated.",
194
+ X12_MISSING_SE: "Transaction set has no SE trailer - transaction is truncated.",
195
+ X12_DANGLING_RELEASE_CHAR: "Release character (`?`) appears at end of element/segment with no following byte to escape - preserved verbatim.",
196
+ 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`.",
197
+ 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`.",
198
+ 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`.",
199
+ 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`.",
200
+ 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`.",
201
+ 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.",
202
+ 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.",
203
+ 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.",
204
+ 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.",
205
+ 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.",
206
+ 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.",
207
+ 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.",
208
+ 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".',
209
+ X12_MISSING_REQUIRED_LOOP_2000A: 'Missing required loop "2000A" (Billing Provider HL): no Billing Provider HL precedes the CLM.',
210
+ X12_MISSING_REQUIRED_LOOP_2000B: 'Missing required loop "2000B" (Subscriber HL): no Subscriber HL precedes the CLM.',
211
+ X12_MISSING_REQUIRED_LOOP_2010BA: 'Missing required loop "2010BA" (Subscriber Name): no Subscriber Name follows the Subscriber HL.',
212
+ X12_MISSING_REQUIRED_LOOP_2010BB: 'Missing required loop "2010BB" (Payer Name): no Payer Name follows the Subscriber HL.',
213
+ 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.`,
214
+ 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.",
215
+ 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.",
216
+ 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."
217
+ };
218
+ var ALL_WARNING_MESSAGES = new Set(
219
+ Object.values(WARNING_MESSAGES)
220
+ );
221
+ var UNEXPECTED_SEGMENT_MESSAGES = {
222
+ [UNEXPECTED_SEGMENT_CONTEXTS.TA1_INSIDE_GROUP]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_TA1_INSIDE_GROUP,
223
+ [UNEXPECTED_SEGMENT_CONTEXTS.GE_WITHOUT_GS]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_GE_WITHOUT_GS,
224
+ [UNEXPECTED_SEGMENT_CONTEXTS.ST_WITHOUT_GS]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_ST_WITHOUT_GS,
225
+ [UNEXPECTED_SEGMENT_CONTEXTS.SE_WITHOUT_ST]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_SE_WITHOUT_ST,
226
+ [UNEXPECTED_SEGMENT_CONTEXTS.BODY_OUTSIDE_TRANSACTION]: WARNING_MESSAGES.X12_UNEXPECTED_SEGMENT_BODY_OUTSIDE_TRANSACTION
227
+ };
228
+ var CONTROL_NUMBER_PAIR_MESSAGES = {
229
+ [CONTROL_NUMBER_PAIRS.INTERCHANGE]: WARNING_MESSAGES.X12_CONTROL_NUMBER_MISMATCH_INTERCHANGE,
230
+ [CONTROL_NUMBER_PAIRS.GROUP]: WARNING_MESSAGES.X12_CONTROL_NUMBER_MISMATCH_GROUP,
231
+ [CONTROL_NUMBER_PAIRS.TRANSACTION]: WARNING_MESSAGES.X12_CONTROL_NUMBER_MISMATCH_TRANSACTION
232
+ };
233
+ var BALANCE_INVARIANT_MESSAGES = {
234
+ [BALANCE_INVARIANTS.CLAIM]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_CLAIM,
235
+ [BALANCE_INVARIANTS.SERVICE_LINE]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_SERVICE_LINE,
236
+ [BALANCE_INVARIANTS.REMIT_TOTAL]: WARNING_MESSAGES.X12_835_REMIT_BALANCE_MISMATCH_REMIT_TOTAL
237
+ };
238
+ var REQUIRED_LOOP_MESSAGES = {
239
+ [REQUIRED_LOOPS.BILLING_PROVIDER_2000A]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000A,
240
+ [REQUIRED_LOOPS.SUBSCRIBER_2000B]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2000B,
241
+ [REQUIRED_LOOPS.SUBSCRIBER_NAME_2010BA]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2010BA,
242
+ [REQUIRED_LOOPS.PAYER_NAME_2010BB]: WARNING_MESSAGES.X12_MISSING_REQUIRED_LOOP_2010BB
243
+ };
244
+ function controlNumberMismatch(position, pair) {
143
245
  return {
144
246
  code: WARNING_CODES.X12_CONTROL_NUMBER_MISMATCH,
145
- message: `Control number mismatch (${pair}): header="${header}", trailer="${trailer}".`,
247
+ message: CONTROL_NUMBER_PAIR_MESSAGES[pair],
146
248
  position
147
249
  };
148
250
  }
149
- function pre005010(position, declared) {
251
+ function pre005010(position) {
150
252
  return {
151
253
  code: WARNING_CODES.X12_PRE_005010,
152
- message: `ISA-12 declares version "${declared}", not the HIPAA baseline "00501".`,
254
+ message: WARNING_MESSAGES.X12_PRE_005010,
153
255
  position
154
256
  };
155
257
  }
156
- function groupCountMismatch(position, declared, actual) {
258
+ function groupCountMismatch(position) {
157
259
  return {
158
260
  code: WARNING_CODES.X12_GROUP_COUNT_MISMATCH,
159
- message: `IEA-01 declares ${declared} group(s); ${String(actual)} were present.`,
261
+ message: WARNING_MESSAGES.X12_GROUP_COUNT_MISMATCH,
160
262
  position
161
263
  };
162
264
  }
163
- function transactionCountMismatch(position, declared, actual) {
265
+ function transactionCountMismatch(position) {
164
266
  return {
165
267
  code: WARNING_CODES.X12_TRANSACTION_COUNT_MISMATCH,
166
- message: `GE-01 declares ${declared} transaction(s); ${String(actual)} were present.`,
268
+ message: WARNING_MESSAGES.X12_TRANSACTION_COUNT_MISMATCH,
167
269
  position
168
270
  };
169
271
  }
170
- function segmentCountMismatch(position, declared, actual) {
272
+ function segmentCountMismatch(position) {
171
273
  return {
172
274
  code: WARNING_CODES.X12_SEGMENT_COUNT_MISMATCH,
173
- message: `SE-01 declares ${declared} segment(s); ${String(actual)} were present.`,
275
+ message: WARNING_MESSAGES.X12_SEGMENT_COUNT_MISMATCH,
174
276
  position
175
277
  };
176
278
  }
177
- function trailingGarbage(position, byteCount) {
279
+ function trailingGarbage(position) {
178
280
  return {
179
281
  code: WARNING_CODES.X12_TRAILING_GARBAGE,
180
- message: `${String(byteCount)} byte(s) followed the IEA terminator - preserved verbatim on \`trailingBytes\`.`,
282
+ message: WARNING_MESSAGES.X12_TRAILING_GARBAGE,
181
283
  position
182
284
  };
183
285
  }
184
286
  function missingIea(position) {
185
287
  return {
186
288
  code: WARNING_CODES.X12_MISSING_IEA,
187
- message: "Interchange has no IEA trailer - input is truncated.",
289
+ message: WARNING_MESSAGES.X12_MISSING_IEA,
188
290
  position
189
291
  };
190
292
  }
191
293
  function missingGe(position) {
192
294
  return {
193
295
  code: WARNING_CODES.X12_MISSING_GE,
194
- message: "Functional group has no GE trailer - group is truncated.",
296
+ message: WARNING_MESSAGES.X12_MISSING_GE,
195
297
  position
196
298
  };
197
299
  }
198
300
  function missingSe(position) {
199
301
  return {
200
302
  code: WARNING_CODES.X12_MISSING_SE,
201
- message: "Transaction set has no SE trailer - transaction is truncated.",
303
+ message: WARNING_MESSAGES.X12_MISSING_SE,
202
304
  position
203
305
  };
204
306
  }
205
307
  function danglingReleaseChar(position) {
206
308
  return {
207
309
  code: WARNING_CODES.X12_DANGLING_RELEASE_CHAR,
208
- message: "Release character (`?`) appears at end of element/segment with no following byte to escape - preserved verbatim.",
310
+ message: WARNING_MESSAGES.X12_DANGLING_RELEASE_CHAR,
209
311
  position
210
312
  };
211
313
  }
212
- var SEGMENT_ID_SHAPE_RE = /^[A-Z][A-Z0-9]{1,2}$/u;
213
- function unexpectedSegment(position, segmentId, context) {
214
- const safeId = SEGMENT_ID_SHAPE_RE.test(segmentId) ? segmentId : "(non-spec)";
314
+ function unexpectedSegment(position, context) {
215
315
  return {
216
316
  code: WARNING_CODES.X12_UNEXPECTED_SEGMENT,
217
- message: `Unexpected "${safeId}" segment - ${context}; segment is preserved on the prior open container if any.`,
317
+ message: UNEXPECTED_SEGMENT_MESSAGES[context],
218
318
  position
219
319
  };
220
320
  }
221
- var CODE_LIST_VALUE_SHAPE_RE = /^[A-Z0-9]{1,5}$/u;
222
- function remitBalanceMismatch(position, invariant, spec, computed, delta) {
321
+ function remitBalanceMismatch(position, invariant) {
223
322
  return {
224
323
  code: WARNING_CODES.X12_835_REMIT_BALANCE_MISMATCH,
225
- message: `835 balance invariant violated [${invariant}]: spec="${spec}", computed="${computed}", delta="${delta}".`,
324
+ message: BALANCE_INVARIANT_MESSAGES[invariant],
226
325
  position
227
326
  };
228
327
  }
229
- function unknownCarc(position, code) {
230
- const safe = CODE_LIST_VALUE_SHAPE_RE.test(code) ? code : "(non-spec)";
328
+ function unknownCarc(position) {
231
329
  return {
232
330
  code: WARNING_CODES.X12_UNKNOWN_CARC,
233
- message: `Unknown CARC "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
331
+ message: WARNING_MESSAGES.X12_UNKNOWN_CARC,
234
332
  position
235
333
  };
236
334
  }
237
- function unknownRarc(position, code) {
238
- const safe = CODE_LIST_VALUE_SHAPE_RE.test(code) ? code : "(non-spec)";
335
+ function unknownRarc(position) {
239
336
  return {
240
337
  code: WARNING_CODES.X12_UNKNOWN_RARC,
241
- message: `Unknown RARC "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
338
+ message: WARNING_MESSAGES.X12_UNKNOWN_RARC,
242
339
  position
243
340
  };
244
341
  }
245
- var HL_ID_SHAPE_RE = /^[0-9]{1,4}$/u;
246
- var HL_LEVEL_CODE_SHAPE_RE = /^[0-9]{2}$/u;
247
- var HI_QUALIFIER_SHAPE_RE = /^[A-Z][A-Z0-9]{1,2}$/u;
248
- function hlParentMismatch(position, hlId, declaredParentId) {
249
- const safeHl = HL_ID_SHAPE_RE.test(hlId) ? hlId : "(non-spec)";
250
- const safeParent = HL_ID_SHAPE_RE.test(declaredParentId) ? declaredParentId : "(non-spec)";
342
+ function hlParentMismatch(position) {
251
343
  return {
252
344
  code: WARNING_CODES.X12_HL_PARENT_MISMATCH,
253
- 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.`,
345
+ message: WARNING_MESSAGES.X12_HL_PARENT_MISMATCH,
254
346
  position
255
347
  };
256
348
  }
257
- function hlParentLevelInvalid(position, hlId, hlLevel, parentHlId, parentLevel, expectedParentLevel) {
258
- const safeHl = HL_ID_SHAPE_RE.test(hlId) ? hlId : "(non-spec)";
259
- const safeLevel = HL_LEVEL_CODE_SHAPE_RE.test(hlLevel) ? hlLevel : "(non-spec)";
260
- const safeParent = HL_ID_SHAPE_RE.test(parentHlId) ? parentHlId : "(non-spec)";
261
- const safeParentLevel = HL_LEVEL_CODE_SHAPE_RE.test(parentLevel) ? parentLevel : "(non-spec)";
262
- const safeExpected = HL_LEVEL_CODE_SHAPE_RE.test(expectedParentLevel) ? expectedParentLevel : "(non-spec)";
349
+ function hlParentLevelInvalid(position) {
263
350
  return {
264
351
  code: WARNING_CODES.X12_HL_PARENT_LEVEL_INVALID,
265
- message: `HL "${safeHl}" (level "${safeLevel}") declares parent "${safeParent}" with level "${safeParentLevel}" but TR3 requires parent level "${safeExpected}".`,
352
+ message: WARNING_MESSAGES.X12_HL_PARENT_LEVEL_INVALID,
266
353
  position
267
354
  };
268
355
  }
269
- function unknownHiQualifier(position, qualifier) {
270
- const safe = HI_QUALIFIER_SHAPE_RE.test(qualifier) ? qualifier : "(non-spec)";
356
+ function unknownHiQualifier(position) {
271
357
  return {
272
358
  code: WARNING_CODES.X12_UNKNOWN_HI_QUALIFIER,
273
- message: `Unknown HI qualifier "${safe}" - qualifier is outside the bundled HI_QUALIFIERS snapshot; verbatim qualifier + code preserved, codeSystem resolves to "unknown".`,
359
+ message: WARNING_MESSAGES.X12_UNKNOWN_HI_QUALIFIER,
274
360
  position
275
361
  };
276
362
  }
277
- var LOOP_ID_SHAPE_RE = /^[0-9A-Z]{3,6}$/u;
278
- function missingRequiredLoop(position, loopId, rationale) {
279
- const safe = LOOP_ID_SHAPE_RE.test(loopId) ? loopId : "(non-spec)";
363
+ function missingRequiredLoop(position, loop) {
280
364
  return {
281
365
  code: WARNING_CODES.X12_MISSING_REQUIRED_LOOP,
282
- message: `Missing required loop "${safe}": ${rationale}`,
366
+ message: REQUIRED_LOOP_MESSAGES[loop],
283
367
  position
284
368
  };
285
369
  }
286
- var ICR_SHAPE_RE = /^[0-9A-Z]{3,16}$/u;
287
- function unknown837Variant(position, implementationConventionReference) {
288
- const safe = implementationConventionReference !== void 0 && ICR_SHAPE_RE.test(implementationConventionReference) ? implementationConventionReference : "(non-spec)";
370
+ function unknown837Variant(position) {
289
371
  return {
290
372
  code: WARNING_CODES.X12_837_UNKNOWN_VARIANT,
291
- 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.`,
373
+ message: WARNING_MESSAGES.X12_837_UNKNOWN_VARIANT,
292
374
  position
293
375
  };
294
376
  }
295
- var CLAIM_STATUS_CATEGORY_SHAPE_RE = /^[A-Z][0-9A-Z]{1,2}$/u;
296
- function unknownClaimStatusCategory(position, code) {
297
- const safe = CLAIM_STATUS_CATEGORY_SHAPE_RE.test(code) ? code : "(non-spec)";
377
+ function unknownClaimStatusCategory(position) {
298
378
  return {
299
379
  code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
300
- message: `Unknown claim status category (CSCC) "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
380
+ message: WARNING_MESSAGES.X12_UNKNOWN_CLAIM_STATUS_CATEGORY,
301
381
  position
302
382
  };
303
383
  }
304
- function unknownClaimStatus(position, code) {
305
- const safe = CODE_LIST_VALUE_SHAPE_RE.test(code) ? code : "(non-spec)";
384
+ function unknownClaimStatus(position) {
306
385
  return {
307
386
  code: WARNING_CODES.X12_UNKNOWN_CLAIM_STATUS,
308
- message: `Unknown claim status code (CSC) "${safe}" - code is outside the bundled snapshot; verbatim value preserved, description unavailable.`,
387
+ message: WARNING_MESSAGES.X12_UNKNOWN_CLAIM_STATUS,
309
388
  position
310
389
  };
311
390
  }
312
- var MAINTENANCE_TYPE_SHAPE_RE = /^[0-9]{3}$/u;
313
- function unknownMaintenanceType(position, code) {
314
- const safe = MAINTENANCE_TYPE_SHAPE_RE.test(code) ? code : "(non-spec)";
391
+ function unknownMaintenanceType(position) {
315
392
  return {
316
393
  code: WARNING_CODES.X12_834_UNKNOWN_MAINTENANCE_TYPE,
317
- message: `Unknown 834 maintenance type "${safe}" - INS-03/HD-01 code is outside the bundled snapshot; verbatim value preserved, action NOT inferred.`,
394
+ message: WARNING_MESSAGES.X12_834_UNKNOWN_MAINTENANCE_TYPE,
318
395
  position
319
396
  };
320
397
  }
@@ -675,6 +752,12 @@ function renderCanonical(signum, magnitude, scale) {
675
752
  }
676
753
 
677
754
  // src/parser/segment.ts
755
+ var SEGMENT_ID_SHAPE_RE = /^[A-Z][A-Z0-9]{1,2}$/u;
756
+ var NON_SPEC_SEGMENT_ID = "(non-spec)";
757
+ function boundSegmentId(first) {
758
+ if (first.length === 0) return "";
759
+ return SEGMENT_ID_SHAPE_RE.test(first) ? first : NON_SPEC_SEGMENT_ID;
760
+ }
678
761
  function decodeSegment(raw, delimiters, emit, position) {
679
762
  const elements = Object.freeze(splitWithRelease(raw, delimiters.element));
680
763
  if (raw.length > 0 && raw.endsWith(RELEASE_CHAR)) {
@@ -684,8 +767,7 @@ function decodeSegment(raw, delimiters, emit, position) {
684
767
  emit(danglingReleaseChar({ ...position, elementIndex: elements.length - 1 }));
685
768
  }
686
769
  }
687
- const id = elements[0] ?? "";
688
- return Object.freeze({ id, raw, elements });
770
+ return Object.freeze({ id: boundSegmentId(elements[0] ?? ""), raw, elements });
689
771
  }
690
772
  function parseSegmentPath(path) {
691
773
  if (path.length === 0) {
@@ -849,8 +931,11 @@ function decodeIsa(raw, delimiters) {
849
931
  }
850
932
  function stripLeadingNewlines(text, start) {
851
933
  let i = start;
852
- if (text.charCodeAt(i) === 13) i++;
853
- if (text.charCodeAt(i) === 10) i++;
934
+ while (i < text.length) {
935
+ const code = text.charCodeAt(i);
936
+ if (code !== 13 && code !== 10) break;
937
+ i++;
938
+ }
854
939
  return i;
855
940
  }
856
941
  function findUnescapedTerminator(text, from, term) {
@@ -903,15 +988,45 @@ function decodeEnvelope(raw, delimiters) {
903
988
  const isa = decodeIsa(raw, delimiters);
904
989
  const isa12 = el(isa.elements, 12);
905
990
  if (isa12 !== "00501") {
906
- warnings.push(pre005010({ segmentIndex: 0, interchangeIndex: 0, elementIndex: 12 }, isa12));
991
+ warnings.push(pre005010({ segmentIndex: 0, interchangeIndex: 0, elementIndex: 12 }));
907
992
  }
908
993
  const { segments } = splitSegments(raw, ISA_MIN_LENGTH, delimiters);
909
994
  const groups = [];
910
995
  const ta1Segments = [];
996
+ const orphanSegments = [];
911
997
  let iea;
912
998
  let trailingBytes;
913
999
  let currentGroup;
914
1000
  let currentTx;
1001
+ const currentAnchor = () => {
1002
+ if (currentGroup === void 0) {
1003
+ return { kind: "interchange", groupIndex: groups.length };
1004
+ }
1005
+ if (currentTx === void 0) {
1006
+ return {
1007
+ kind: "group",
1008
+ groupIndex: groups.length,
1009
+ transactionIndex: currentGroup.transactions.length
1010
+ };
1011
+ }
1012
+ return {
1013
+ kind: "transaction",
1014
+ groupIndex: groups.length,
1015
+ transactionIndex: currentGroup.transactions.length,
1016
+ segmentOffset: currentTx.rawSegments.length
1017
+ };
1018
+ };
1019
+ const recordOrphan = (segmentText, position, context) => {
1020
+ const anchor = currentAnchor();
1021
+ warnings.push(unexpectedSegment(position, context));
1022
+ orphanSegments.push({
1023
+ raw: segmentText,
1024
+ segment: decodeSegment(segmentText, delimiters, collectWarning, position),
1025
+ segmentIndex: position.segmentIndex,
1026
+ context,
1027
+ anchor
1028
+ });
1029
+ };
915
1030
  const finalizeTx = (tx, group, se) => {
916
1031
  if (se === void 0) {
917
1032
  warnings.push(
@@ -944,16 +1059,12 @@ function decodeEnvelope(raw, delimiters) {
944
1059
  const actual = group.transactions.length;
945
1060
  if (declared !== String(actual)) {
946
1061
  warnings.push(
947
- transactionCountMismatch(
948
- {
949
- segmentIndex: group.startSegIdx,
950
- interchangeIndex: 0,
951
- groupIndex: groups.length,
952
- elementIndex: 1
953
- },
954
- declared,
955
- actual
956
- )
1062
+ transactionCountMismatch({
1063
+ segmentIndex: group.startSegIdx,
1064
+ interchangeIndex: 0,
1065
+ groupIndex: groups.length,
1066
+ elementIndex: 1
1067
+ })
957
1068
  );
958
1069
  }
959
1070
  const gsControl = el(group.gs.elements, 6);
@@ -967,9 +1078,7 @@ function decodeEnvelope(raw, delimiters) {
967
1078
  groupIndex: groups.length,
968
1079
  elementIndex: 2
969
1080
  },
970
- "GS-06/GE-02",
971
- gsControl,
972
- geControl
1081
+ CONTROL_NUMBER_PAIRS.GROUP
973
1082
  )
974
1083
  );
975
1084
  }
@@ -991,16 +1100,14 @@ function decodeEnvelope(raw, delimiters) {
991
1100
  ta1Segments.push({ raw: segmentText, elements });
992
1101
  break;
993
1102
  }
994
- warnings.push(
995
- unexpectedSegment(
996
- {
997
- segmentIndex: segIdx,
998
- interchangeIndex: 0,
999
- groupIndex: groups.length
1000
- },
1001
- "TA1",
1002
- "TA1 segment is envelope-level - appeared inside an open functional group"
1003
- )
1103
+ recordOrphan(
1104
+ segmentText,
1105
+ {
1106
+ segmentIndex: segIdx,
1107
+ interchangeIndex: 0,
1108
+ groupIndex: groups.length
1109
+ },
1110
+ UNEXPECTED_SEGMENT_CONTEXTS.TA1_INSIDE_GROUP
1004
1111
  );
1005
1112
  break;
1006
1113
  }
@@ -1021,12 +1128,10 @@ function decodeEnvelope(raw, delimiters) {
1021
1128
  }
1022
1129
  case "GE": {
1023
1130
  if (currentGroup === void 0) {
1024
- warnings.push(
1025
- unexpectedSegment(
1026
- { segmentIndex: segIdx, interchangeIndex: 0 },
1027
- "GE",
1028
- "no open functional group"
1029
- )
1131
+ recordOrphan(
1132
+ segmentText,
1133
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1134
+ UNEXPECTED_SEGMENT_CONTEXTS.GE_WITHOUT_GS
1030
1135
  );
1031
1136
  break;
1032
1137
  }
@@ -1040,12 +1145,10 @@ function decodeEnvelope(raw, delimiters) {
1040
1145
  }
1041
1146
  case "ST": {
1042
1147
  if (currentGroup === void 0) {
1043
- warnings.push(
1044
- unexpectedSegment(
1045
- { segmentIndex: segIdx, interchangeIndex: 0 },
1046
- "ST",
1047
- "no open functional group (missing GS)"
1048
- )
1148
+ recordOrphan(
1149
+ segmentText,
1150
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1151
+ UNEXPECTED_SEGMENT_CONTEXTS.ST_WITHOUT_GS
1049
1152
  );
1050
1153
  break;
1051
1154
  }
@@ -1068,12 +1171,10 @@ function decodeEnvelope(raw, delimiters) {
1068
1171
  }
1069
1172
  case "SE": {
1070
1173
  if (currentTx === void 0 || currentGroup === void 0) {
1071
- warnings.push(
1072
- unexpectedSegment(
1073
- { segmentIndex: segIdx, interchangeIndex: 0 },
1074
- "SE",
1075
- "no open transaction set"
1076
- )
1174
+ recordOrphan(
1175
+ segmentText,
1176
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1177
+ UNEXPECTED_SEGMENT_CONTEXTS.SE_WITHOUT_ST
1077
1178
  );
1078
1179
  break;
1079
1180
  }
@@ -1098,9 +1199,7 @@ function decodeEnvelope(raw, delimiters) {
1098
1199
  transactionIndex: currentGroup.transactions.length,
1099
1200
  elementIndex: 2
1100
1201
  },
1101
- "ST-02/SE-02",
1102
- stControl,
1103
- seControl
1202
+ CONTROL_NUMBER_PAIRS.TRANSACTION
1104
1203
  )
1105
1204
  );
1106
1205
  }
@@ -1119,11 +1218,7 @@ function decodeEnvelope(raw, delimiters) {
1119
1218
  const declared = el(elements, 1);
1120
1219
  if (declared !== String(groups.length)) {
1121
1220
  warnings.push(
1122
- groupCountMismatch(
1123
- { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 },
1124
- declared,
1125
- groups.length
1126
- )
1221
+ groupCountMismatch({ segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 })
1127
1222
  );
1128
1223
  }
1129
1224
  const isaControl = el(isa.elements, 13);
@@ -1132,9 +1227,7 @@ function decodeEnvelope(raw, delimiters) {
1132
1227
  warnings.push(
1133
1228
  controlNumberMismatch(
1134
1229
  { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 2 },
1135
- "ISA-13/IEA-02",
1136
- isaControl,
1137
- ieaControl
1230
+ CONTROL_NUMBER_PAIRS.INTERCHANGE
1138
1231
  )
1139
1232
  );
1140
1233
  }
@@ -1142,15 +1235,14 @@ function decodeEnvelope(raw, delimiters) {
1142
1235
  if (tail.length > 0) {
1143
1236
  const joined = tail + delimiters.segment;
1144
1237
  trailingBytes = joined;
1145
- warnings.push(
1146
- trailingGarbage({ segmentIndex: segIdx + 1, interchangeIndex: 0 }, joined.length)
1147
- );
1238
+ warnings.push(trailingGarbage({ segmentIndex: segIdx + 1, interchangeIndex: 0 }));
1148
1239
  }
1149
1240
  return {
1150
1241
  isa,
1151
1242
  groups: Object.freeze(groups.slice()),
1152
1243
  iea,
1153
1244
  ta1Segments: Object.freeze(ta1Segments.slice()),
1245
+ orphanSegments: Object.freeze(orphanSegments.slice()),
1154
1246
  trailingBytes,
1155
1247
  warnings: Object.freeze(warnings.slice())
1156
1248
  };
@@ -1166,12 +1258,10 @@ function decodeEnvelope(raw, delimiters) {
1166
1258
  currentTx.segments.push(decoded);
1167
1259
  currentTx.rawSegments.push(segmentText);
1168
1260
  } else if (name.length > 0) {
1169
- warnings.push(
1170
- unexpectedSegment(
1171
- { segmentIndex: segIdx, interchangeIndex: 0 },
1172
- name,
1173
- "body segment outside any open transaction set"
1174
- )
1261
+ recordOrphan(
1262
+ segmentText,
1263
+ { segmentIndex: segIdx, interchangeIndex: 0 },
1264
+ UNEXPECTED_SEGMENT_CONTEXTS.BODY_OUTSIDE_TRANSACTION
1175
1265
  );
1176
1266
  }
1177
1267
  break;
@@ -1190,6 +1280,7 @@ function decodeEnvelope(raw, delimiters) {
1190
1280
  groups: Object.freeze(groups.slice()),
1191
1281
  iea,
1192
1282
  ta1Segments: Object.freeze(ta1Segments.slice()),
1283
+ orphanSegments: Object.freeze(orphanSegments.slice()),
1193
1284
  trailingBytes,
1194
1285
  warnings: Object.freeze(warnings.slice())
1195
1286
  };
@@ -1215,7 +1306,7 @@ function parseX12(raw, options = {}) {
1215
1306
  first.code,
1216
1307
  first.message,
1217
1308
  first.position,
1218
- snippet(text)
1309
+ ""
1219
1310
  );
1220
1311
  }
1221
1312
  }
@@ -1235,6 +1326,7 @@ function parseX12(raw, options = {}) {
1235
1326
  delimiters,
1236
1327
  groups: decoded.groups,
1237
1328
  ta1Segments: decoded.ta1Segments,
1329
+ orphanSegments: decoded.orphanSegments,
1238
1330
  warnings: decoded.warnings,
1239
1331
  ...decoded.trailingBytes !== void 0 ? { trailingBytes: decoded.trailingBytes } : {},
1240
1332
  ...profile !== void 0 ? { profile } : {}
@@ -1751,14 +1843,7 @@ function checkClaimBalance(claim, position) {
1751
1843
  );
1752
1844
  const computed = claim.totalPaymentAmount.add(claimCasSum).add(lineCasSum);
1753
1845
  if (computed.equals(claim.totalChargeAmount)) return void 0;
1754
- const delta = computed.subtract(claim.totalChargeAmount);
1755
- return remitBalanceMismatch(
1756
- position,
1757
- "CLP-04 + \u03A3(claim CAS + line CAS) == CLP-03",
1758
- claim.totalChargeAmount.toString(),
1759
- computed.toString(),
1760
- delta.toString()
1761
- );
1846
+ return remitBalanceMismatch(position, BALANCE_INVARIANTS.CLAIM);
1762
1847
  }
1763
1848
  function checkServiceLineBalance(claim, position) {
1764
1849
  const out = [];
@@ -1768,14 +1853,10 @@ function checkServiceLineBalance(claim, position) {
1768
1853
  const lineCasSum = sumAmounts(line.adjustments);
1769
1854
  const computed = line.paymentAmount.add(lineCasSum);
1770
1855
  if (computed.equals(line.chargeAmount)) continue;
1771
- const delta = computed.subtract(line.chargeAmount);
1772
1856
  out.push(
1773
1857
  remitBalanceMismatch(
1774
1858
  { ...position, segmentIndex: position.segmentIndex + i + 1 },
1775
- "SVC-03 + \u03A3(line CAS) == SVC-02",
1776
- line.chargeAmount.toString(),
1777
- computed.toString(),
1778
- delta.toString()
1859
+ BALANCE_INVARIANTS.SERVICE_LINE
1779
1860
  )
1780
1861
  );
1781
1862
  }
@@ -1792,14 +1873,7 @@ function checkRemitTotalBalance(bpr02, claims, providerAdjustments, position) {
1792
1873
  );
1793
1874
  const computed = claimSum.subtract(plbSum);
1794
1875
  if (computed.equals(bpr02)) return void 0;
1795
- const delta = computed.subtract(bpr02);
1796
- return remitBalanceMismatch(
1797
- position,
1798
- "\u03A3(CLP-04) - \u03A3(PLB amounts) == BPR-02",
1799
- bpr02.toString(),
1800
- computed.toString(),
1801
- delta.toString()
1802
- );
1876
+ return remitBalanceMismatch(position, BALANCE_INVARIANTS.REMIT_TOTAL);
1803
1877
  }
1804
1878
  function sumAmounts(adjustments) {
1805
1879
  return adjustments.reduce(
@@ -1814,6 +1888,7 @@ function get835(delimiters, tx) {
1814
1888
  const warnings = [];
1815
1889
  const body = tx.se === void 0 ? tx.segments.slice(1) : tx.segments.slice(1, -1);
1816
1890
  let payment = EMPTY_HEADER;
1891
+ let bprSegmentIndex = 0;
1817
1892
  const traces = [];
1818
1893
  let payer;
1819
1894
  let payee;
@@ -1842,6 +1917,7 @@ function get835(delimiters, tx) {
1842
1917
  switch (seg.id) {
1843
1918
  case "BPR": {
1844
1919
  payment = decodeBpr(seg, delimiters);
1920
+ bprSegmentIndex = position.segmentIndex;
1845
1921
  break;
1846
1922
  }
1847
1923
  case "TRN": {
@@ -1915,7 +1991,7 @@ function get835(delimiters, tx) {
1915
1991
  case "CLP": {
1916
1992
  flushClaim();
1917
1993
  lastParty = void 0;
1918
- currentClaim = openClaim(seg, delimiters);
1994
+ currentClaim = openClaim(seg, delimiters, position.segmentIndex);
1919
1995
  break;
1920
1996
  }
1921
1997
  case "CAS": {
@@ -1961,7 +2037,7 @@ function get835(delimiters, tx) {
1961
2037
  const remarks = seg.id === "MIA" ? collectMiaRemarks(seg, delimiters) : collectMoaRemarks(seg, delimiters);
1962
2038
  for (const code of remarks) {
1963
2039
  const entry = lookupRarc(code);
1964
- if (entry === void 0) warnings.push(unknownRarc(position, code));
2040
+ if (entry === void 0) warnings.push(unknownRarc(position));
1965
2041
  currentClaim.remarks.push({ system: "HE", code, description: entry?.description });
1966
2042
  }
1967
2043
  break;
@@ -2009,7 +2085,7 @@ function get835(delimiters, tx) {
2009
2085
  payment.totalActualPayment,
2010
2086
  finalClaims,
2011
2087
  providerAdjustments,
2012
- { segmentIndex: 0, transactionIndex: 0 }
2088
+ { segmentIndex: bprSegmentIndex, transactionIndex: 0 }
2013
2089
  );
2014
2090
  if (remitTotalWarn !== void 0) warnings.push(remitTotalWarn);
2015
2091
  return Object.freeze({
@@ -2099,7 +2175,7 @@ function decodeLq(seg, delimiters, warnings, position) {
2099
2175
  let description;
2100
2176
  if (system === "HE") {
2101
2177
  const entry = lookupRarc(code);
2102
- if (entry === void 0) warnings.push(unknownRarc(position, code));
2178
+ if (entry === void 0) warnings.push(unknownRarc(position));
2103
2179
  description = entry?.description;
2104
2180
  }
2105
2181
  return Object.freeze({ system, code, description });
@@ -2131,7 +2207,7 @@ function decodeCasAdjustments(seg, delimiters, warnings, position) {
2131
2207
  if (reasonCode === void 0 && amount === void 0) continue;
2132
2208
  const code = reasonCode ?? "";
2133
2209
  const entry = code === "" ? void 0 : lookupCarc(code);
2134
- if (entry === void 0 && code !== "") warnings.push(unknownCarc(position, code));
2210
+ if (entry === void 0 && code !== "") warnings.push(unknownCarc(position));
2135
2211
  out.push(
2136
2212
  Object.freeze({
2137
2213
  groupCode,
@@ -2171,14 +2247,13 @@ function decodeNm1(seg, delimiters) {
2171
2247
  })
2172
2248
  };
2173
2249
  }
2174
- function openClaim(seg, delimiters) {
2250
+ function openClaim(seg, delimiters, clpSegmentIndex) {
2175
2251
  const claimStatusCode = elementValue(seg, 2, delimiters);
2176
2252
  const claimStatusDescription = lookupClpStatus(claimStatusCode)?.description;
2177
2253
  const facilityTypeCode = componentOptional(seg, 8, 1, delimiters) ?? elementOptional(seg, 8, delimiters);
2178
2254
  const claimFrequencyCode = componentOptional(seg, 8, 3, delimiters);
2179
2255
  return {
2180
- clpSegmentIndex: 0,
2181
- // populated in the future when we surface positions
2256
+ clpSegmentIndex,
2182
2257
  patientControlNumber: elementValue(seg, 1, delimiters),
2183
2258
  claimStatusCode,
2184
2259
  claimStatusDescription,
@@ -2371,10 +2446,32 @@ var Remit835BuildError = class extends Error {
2371
2446
  }
2372
2447
  };
2373
2448
 
2449
+ // src/builder/caller-value.ts
2450
+ var BUILD_REFUSAL_VALUE_MAX_LENGTH = 63;
2451
+ var MAX_LENGTH_DIGITS = 10;
2452
+ var BUILD_REFUSAL_VALUE_MAX_RENDERED = BUILD_REFUSAL_VALUE_MAX_LENGTH + 2 + 1 + " ( characters)".length + MAX_LENGTH_DIGITS;
2453
+ function renderCallerValue(value) {
2454
+ const text = coerce(value);
2455
+ if (text.length <= BUILD_REFUSAL_VALUE_MAX_LENGTH) return `"${text}"`;
2456
+ return `"${text.slice(0, BUILD_REFUSAL_VALUE_MAX_LENGTH)}\u2026" (${String(text.length)} characters)`;
2457
+ }
2458
+ function coerce(value) {
2459
+ if (typeof value === "string") return value;
2460
+ try {
2461
+ return String(value);
2462
+ } catch {
2463
+ return "[unrenderable value]";
2464
+ }
2465
+ }
2466
+
2374
2467
  // src/transactions/remit/build-835.ts
2375
2468
  var X221A1_VERSION_RELEASE = "005010X221A1";
2376
2469
  var X12_835_FUNCTIONAL_ID = "HP";
2377
2470
  var X12_AGENCY_CODE = "X";
2471
+ var UNANCHORED_BUILD_POSITION = Object.freeze({
2472
+ segmentIndex: 0,
2473
+ transactionIndex: 0
2474
+ });
2378
2475
  function build835(spec) {
2379
2476
  const { envelope } = spec;
2380
2477
  enforceStructuralSpec(spec);
@@ -2548,7 +2645,7 @@ function enforceStructuralSpec(spec) {
2548
2645
  }
2549
2646
  }
2550
2647
  function enforceBalance(spec) {
2551
- const position = { segmentIndex: 0, transactionIndex: 0 };
2648
+ const position = UNANCHORED_BUILD_POSITION;
2552
2649
  const claims = spec.claims.map(materializeClaim);
2553
2650
  for (const claim of claims) {
2554
2651
  const claimWarn = checkClaimBalance(claim, position);
@@ -2854,7 +2951,7 @@ function padControl(value, width) {
2854
2951
  if (value.length < width) return "0".repeat(width - value.length) + value;
2855
2952
  throw new Remit835BuildError(
2856
2953
  REMIT_835_BUILD_ERROR_CODES.X12_835_BUILD_INVALID_SPEC,
2857
- `build835: control number "${value}" exceeds the ${String(width)}-char spec limit.`
2954
+ `build835: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
2858
2955
  );
2859
2956
  }
2860
2957
  function expandYY(yymmdd) {
@@ -2984,12 +3081,7 @@ function get837Claims(delimiters, tx, opts) {
2984
3081
  }
2985
3082
  const variant = explicitType ?? variantFromIcr ?? variantFromSegment ?? "unknown";
2986
3083
  if (variant === "unknown") {
2987
- warnings.push(
2988
- unknown837Variant(
2989
- { segmentIndex: 1, transactionIndex: 0 },
2990
- implementationConventionReference
2991
- )
2992
- );
3084
+ warnings.push(unknown837Variant({ segmentIndex: 1, transactionIndex: 0 }));
2993
3085
  }
2994
3086
  const hierarchies = [];
2995
3087
  const hlIndex = /* @__PURE__ */ new Map();
@@ -3230,28 +3322,16 @@ function get837Claims(delimiters, tx, opts) {
3230
3322
  flushClaim();
3231
3323
  if (currentBillingHl === void 0 || currentSubscriberHl === void 0 || currentSubscriberMember === void 0 || currentPayer === void 0) {
3232
3324
  if (currentBillingHl === void 0) {
3233
- warnings.push(
3234
- missingRequiredLoop(position, "2000A", "no Billing Provider HL precedes the CLM")
3235
- );
3325
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.BILLING_PROVIDER_2000A));
3236
3326
  }
3237
3327
  if (currentSubscriberHl === void 0) {
3238
- warnings.push(
3239
- missingRequiredLoop(position, "2000B", "no Subscriber HL precedes the CLM")
3240
- );
3328
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.SUBSCRIBER_2000B));
3241
3329
  }
3242
3330
  if (currentSubscriberMember === void 0 && currentSubscriberHl !== void 0) {
3243
- warnings.push(
3244
- missingRequiredLoop(
3245
- position,
3246
- "2010BA",
3247
- "no Subscriber Name follows the Subscriber HL"
3248
- )
3249
- );
3331
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.SUBSCRIBER_NAME_2010BA));
3250
3332
  }
3251
3333
  if (currentPayer === void 0 && currentSubscriberHl !== void 0) {
3252
- warnings.push(
3253
- missingRequiredLoop(position, "2010BB", "no Payer Name follows the Subscriber HL")
3254
- );
3334
+ warnings.push(missingRequiredLoop(position, REQUIRED_LOOPS.PAYER_NAME_2010BB));
3255
3335
  }
3256
3336
  }
3257
3337
  currentClaim = openClaim2(seg, delimiters, {
@@ -3396,30 +3476,21 @@ function validateHl(hl, index, position, warnings) {
3396
3476
  const expectedParent = EXPECTED_PARENT_LEVEL[hl.levelCode];
3397
3477
  if (expectedParent === void 0) {
3398
3478
  if (hl.levelCode === HL_LEVEL_CODES.INFORMATION_SOURCE && hl.parentHlId !== void 0) {
3399
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
3479
+ warnings.push(hlParentMismatch(position));
3400
3480
  }
3401
3481
  return;
3402
3482
  }
3403
3483
  if (hl.parentHlId === void 0) {
3404
- warnings.push(hlParentMismatch(position, hl.hlId, ""));
3484
+ warnings.push(hlParentMismatch(position));
3405
3485
  return;
3406
3486
  }
3407
3487
  const parent = index.get(hl.parentHlId);
3408
3488
  if (parent === void 0) {
3409
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
3489
+ warnings.push(hlParentMismatch(position));
3410
3490
  return;
3411
3491
  }
3412
3492
  if (parent.levelCode !== expectedParent) {
3413
- warnings.push(
3414
- hlParentLevelInvalid(
3415
- position,
3416
- hl.hlId,
3417
- hl.levelCode,
3418
- parent.hlId,
3419
- parent.levelCode,
3420
- expectedParent
3421
- )
3422
- );
3493
+ warnings.push(hlParentLevelInvalid(position));
3423
3494
  }
3424
3495
  }
3425
3496
  function decodeSbr(seg, delimiters) {
@@ -3520,7 +3591,7 @@ function decodeHi(seg, delimiters, warnings, position) {
3520
3591
  const codeSystem = resolved?.system ?? "unknown";
3521
3592
  const category = resolved?.category ?? "unknown";
3522
3593
  if (qualifier !== void 0 && resolved === void 0) {
3523
- warnings.push(unknownHiQualifier(position, qualifier));
3594
+ warnings.push(unknownHiQualifier(position));
3524
3595
  }
3525
3596
  out.push(
3526
3597
  Object.freeze({
@@ -3563,7 +3634,7 @@ function decodeCas(seg, delimiters, warnings, position) {
3563
3634
  if (reasonCode === void 0 && amount === void 0) continue;
3564
3635
  const code = reasonCode ?? "";
3565
3636
  const entry = code === "" ? void 0 : lookupCarc(code);
3566
- if (entry === void 0 && code !== "") warnings.push(unknownCarc(position, code));
3637
+ if (entry === void 0 && code !== "") warnings.push(unknownCarc(position));
3567
3638
  out.push(
3568
3639
  Object.freeze({
3569
3640
  groupCode,
@@ -4229,7 +4300,7 @@ function enforceClaim(variant, claim, locator) {
4229
4300
  if (line.variant !== variant) {
4230
4301
  throw new Claim837BuildError(
4231
4302
  CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC,
4232
- `build837${variant}: claim at ${locator} has a "${line.variant}" service line at index ${String(l)}; every line must be "${variant}".`
4303
+ `build837${variant}: claim at ${locator} has a ${renderCallerValue(line.variant)} service line at index ${String(l)}; every line must be "${variant}".`
4233
4304
  );
4234
4305
  }
4235
4306
  const code = line.variant === "I" ? line.revenueCode : line.procedureCode;
@@ -4573,7 +4644,7 @@ function padControl2(value, width) {
4573
4644
  if (value.length < width) return "0".repeat(width - value.length) + value;
4574
4645
  throw new Claim837BuildError(
4575
4646
  CLAIM_837_BUILD_ERROR_CODES.X12_837_BUILD_INVALID_SPEC,
4576
- `build837: control number "${value}" exceeds the ${String(width)}-char spec limit.`
4647
+ `build837: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
4577
4648
  );
4578
4649
  }
4579
4650
  function expandYY2(yymmdd) {
@@ -4805,30 +4876,21 @@ function validateHl2(hl, index, expectedParentLevel, position, warnings) {
4805
4876
  const expectedParent = hasExpectation ? expectedParentLevel[hl.levelCode] : void 0;
4806
4877
  if (expectedParent === void 0) {
4807
4878
  if (hasExpectation && hl.parentHlId !== void 0) {
4808
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
4879
+ warnings.push(hlParentMismatch(position));
4809
4880
  }
4810
4881
  return;
4811
4882
  }
4812
4883
  if (hl.parentHlId === void 0) {
4813
- warnings.push(hlParentMismatch(position, hl.hlId, ""));
4884
+ warnings.push(hlParentMismatch(position));
4814
4885
  return;
4815
4886
  }
4816
4887
  const parent = index.get(hl.parentHlId);
4817
4888
  if (parent === void 0) {
4818
- warnings.push(hlParentMismatch(position, hl.hlId, hl.parentHlId));
4889
+ warnings.push(hlParentMismatch(position));
4819
4890
  return;
4820
4891
  }
4821
4892
  if (parent.levelCode !== expectedParent) {
4822
- warnings.push(
4823
- hlParentLevelInvalid(
4824
- position,
4825
- hl.hlId,
4826
- hl.levelCode,
4827
- parent.hlId,
4828
- parent.levelCode,
4829
- expectedParent
4830
- )
4831
- );
4893
+ warnings.push(hlParentLevelInvalid(position));
4832
4894
  }
4833
4895
  }
4834
4896
 
@@ -5447,7 +5509,7 @@ function padControl3(value, width) {
5447
5509
  if (value.length < width) return "0".repeat(width - value.length) + value;
5448
5510
  throw new Eligibility271BuildError(
5449
5511
  ELIGIBILITY_271_BUILD_ERROR_CODES.X12_271_BUILD_INVALID_SPEC,
5450
- `build271: control number "${value}" exceeds the ${String(width)}-char spec limit.`
5512
+ `build271: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
5451
5513
  );
5452
5514
  }
5453
5515
  function expandYY3(yymmdd) {
@@ -5781,11 +5843,11 @@ function decodeStatusComposite(seg, n, delimiters, warnings, position) {
5781
5843
  if (categoryCode === void 0 && statusCode === void 0) return void 0;
5782
5844
  const category = categoryCode === void 0 ? void 0 : lookupClaimStatusCategory(categoryCode);
5783
5845
  if (categoryCode !== void 0 && category === void 0) {
5784
- warnings.push(unknownClaimStatusCategory(position, categoryCode));
5846
+ warnings.push(unknownClaimStatusCategory(position));
5785
5847
  }
5786
5848
  const status = statusCode === void 0 ? void 0 : lookupClaimStatus(statusCode);
5787
5849
  if (statusCode !== void 0 && status === void 0) {
5788
- warnings.push(unknownClaimStatus(position, statusCode));
5850
+ warnings.push(unknownClaimStatus(position));
5789
5851
  }
5790
5852
  return Object.freeze({
5791
5853
  categoryCode: categoryCode ?? "",
@@ -6221,7 +6283,7 @@ function padControl4(value, width) {
6221
6283
  if (value.length < width) return "0".repeat(width - value.length) + value;
6222
6284
  throw new ClaimStatus277BuildError(
6223
6285
  CLAIM_STATUS_277_BUILD_ERROR_CODES.X12_277_BUILD_INVALID_SPEC,
6224
- `build277: control number "${value}" exceeds the ${String(width)}-char spec limit.`
6286
+ `build277: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
6225
6287
  );
6226
6288
  }
6227
6289
  function expandYY4(yymmdd) {
@@ -6878,7 +6940,7 @@ function padControl5(value, width) {
6878
6940
  if (value.length < width) return "0".repeat(width - value.length) + value;
6879
6941
  throw new Premium820BuildError(
6880
6942
  PREMIUM_820_BUILD_ERROR_CODES.X12_820_BUILD_INVALID_SPEC,
6881
- `build820: control number "${value}" exceeds the ${String(width)}-char spec limit.`
6943
+ `build820: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
6882
6944
  );
6883
6945
  }
6884
6946
  function expandYY5(yymmdd) {
@@ -7109,7 +7171,7 @@ function openEnrollment(seg, delimiters, position) {
7109
7171
  const entry = maintenanceTypeCode === "" ? void 0 : lookupMaintenanceType(maintenanceTypeCode);
7110
7172
  const warnings = [];
7111
7173
  if (maintenanceTypeCode !== "" && entry === void 0) {
7112
- warnings.push(unknownMaintenanceType(position, maintenanceTypeCode));
7174
+ warnings.push(unknownMaintenanceType(position));
7113
7175
  }
7114
7176
  return {
7115
7177
  subscriberIndicator: elementOptional(seg, 1, delimiters),
@@ -7149,7 +7211,7 @@ function openCoverage(seg, delimiters, position, warnings) {
7149
7211
  let maintenanceTypeDescription;
7150
7212
  if (maintenanceTypeCode !== void 0) {
7151
7213
  const entry = lookupMaintenanceType(maintenanceTypeCode);
7152
- if (entry === void 0) warnings.push(unknownMaintenanceType(position, maintenanceTypeCode));
7214
+ if (entry === void 0) warnings.push(unknownMaintenanceType(position));
7153
7215
  maintenanceTypeDescription = entry?.description;
7154
7216
  }
7155
7217
  return {
@@ -7393,7 +7455,7 @@ function enforceStructuralSpec6(spec) {
7393
7455
  if (lookupMaintenanceType(member.maintenanceTypeCode) === void 0) {
7394
7456
  throw new Enrollment834BuildError(
7395
7457
  ENROLLMENT_834_BUILD_ERROR_CODES.X12_834_BUILD_UNKNOWN_MAINTENANCE_TYPE,
7396
- `build834: member at index ${String(m)} has an unknown INS-03 maintenance type code "${member.maintenanceTypeCode}" (outside X12 Code Source 875).`
7458
+ `build834: member at index ${String(m)} has an unknown INS-03 maintenance type code ${renderCallerValue(member.maintenanceTypeCode)} (outside X12 Code Source 875).`
7397
7459
  );
7398
7460
  }
7399
7461
  for (let c = 0; c < (member.healthCoverages ?? []).length; c += 1) {
@@ -7403,7 +7465,7 @@ function enforceStructuralSpec6(spec) {
7403
7465
  if (hd01 !== void 0 && hd01 !== "" && lookupMaintenanceType(hd01) === void 0) {
7404
7466
  throw new Enrollment834BuildError(
7405
7467
  ENROLLMENT_834_BUILD_ERROR_CODES.X12_834_BUILD_UNKNOWN_MAINTENANCE_TYPE,
7406
- `build834: coverage at member[${String(m)}].healthCoverages[${String(c)}] has an unknown HD-01 maintenance type code "${hd01}" (outside X12 Code Source 875).`
7468
+ `build834: coverage at member[${String(m)}].healthCoverages[${String(c)}] has an unknown HD-01 maintenance type code ${renderCallerValue(hd01)} (outside X12 Code Source 875).`
7407
7469
  );
7408
7470
  }
7409
7471
  }
@@ -7552,7 +7614,7 @@ function padControl6(value, width) {
7552
7614
  if (value.length < width) return "0".repeat(width - value.length) + value;
7553
7615
  throw new Enrollment834BuildError(
7554
7616
  ENROLLMENT_834_BUILD_ERROR_CODES.X12_834_BUILD_INVALID_SPEC,
7555
- `build834: control number "${value}" exceeds the ${String(width)}-char spec limit.`
7617
+ `build834: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
7556
7618
  );
7557
7619
  }
7558
7620
  function expandYY6(yymmdd) {
@@ -7862,7 +7924,7 @@ function decodeHiDiagnoses(seg, delimiters, warnings, position) {
7862
7924
  const resolved = qualifier === void 0 ? void 0 : resolveHiQualifier(qualifier);
7863
7925
  const codeSystem = resolved?.system ?? "unknown";
7864
7926
  if (qualifier !== void 0 && resolved === void 0) {
7865
- warnings.push(unknownHiQualifier(position, qualifier));
7927
+ warnings.push(unknownHiQualifier(position));
7866
7928
  }
7867
7929
  out.push(Object.freeze({ qualifier: qualifier ?? "", code: code ?? "", codeSystem }));
7868
7930
  }
@@ -8208,7 +8270,7 @@ function padControl7(value, width) {
8208
8270
  if (value.length < width) return "0".repeat(width - value.length) + value;
8209
8271
  throw new ServicesReview278BuildError(
8210
8272
  AUTH_278_BUILD_ERROR_CODES.X12_278_BUILD_INVALID_SPEC,
8211
- `build278: control number "${value}" exceeds the ${String(width)}-char spec limit.`
8273
+ `build278: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
8212
8274
  );
8213
8275
  }
8214
8276
  function expandYY7(yymmdd) {
@@ -8810,7 +8872,7 @@ function enforceAcceptIsClean(group) {
8810
8872
  if (functionalIsAccept && functionalHasErrorCodes) {
8811
8873
  throw new AckBuildError(
8812
8874
  ACK_BUILD_ERROR_CODES.X12_ACK_ACCEPT_WITH_ERRORS,
8813
- `build999: AK9-01 was 'A' (Accept) but AK9-05..AK9-09 carried ${String(
8875
+ `build999: AK9-01 was 'A' (Accept) but AK9-05..AK9-09 carried ${renderCallerValue(
8814
8876
  (group.syntaxErrorCodes ?? []).length
8815
8877
  )} syntax error code(s). Use 'E' (Accept, with errors noted) or a reject disposition instead.`
8816
8878
  );
@@ -8821,13 +8883,13 @@ function enforceAcceptIsClean(group) {
8821
8883
  if (responseIsAccept && responseHasErrors) {
8822
8884
  throw new AckBuildError(
8823
8885
  ACK_BUILD_ERROR_CODES.X12_ACK_ACCEPT_WITH_ERRORS,
8824
- `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.`
8886
+ `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.`
8825
8887
  );
8826
8888
  }
8827
8889
  if (functionalIsAccept && (!responseIsAccept || responseHasErrors)) {
8828
8890
  throw new AckBuildError(
8829
8891
  ACK_BUILD_ERROR_CODES.X12_ACK_ACCEPT_WITH_ERRORS,
8830
- `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.`
8892
+ `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.`
8831
8893
  );
8832
8894
  }
8833
8895
  }
@@ -8839,25 +8901,25 @@ function enforceCountInvariants(group) {
8839
8901
  if (declared < 0 || received < 0 || accepted < 0) {
8840
8902
  throw new AckBuildError(
8841
8903
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8842
- `build999: AK9 counts must be non-negative (declared=${String(declared)}, received=${String(received)}, accepted=${String(accepted)}).`
8904
+ `build999: AK9 counts must be non-negative (declared=${renderCallerValue(declared)}, received=${renderCallerValue(received)}, accepted=${renderCallerValue(accepted)}).`
8843
8905
  );
8844
8906
  }
8845
8907
  if (accepted > received) {
8846
8908
  throw new AckBuildError(
8847
8909
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8848
- `build999: AK9-04 accepted (${String(accepted)}) cannot exceed AK9-03 received (${String(received)}).`
8910
+ `build999: AK9-04 accepted (${renderCallerValue(accepted)}) cannot exceed AK9-03 received (${renderCallerValue(received)}).`
8849
8911
  );
8850
8912
  }
8851
8913
  if (received > declared) {
8852
8914
  throw new AckBuildError(
8853
8915
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8854
- `build999: AK9-03 received (${String(received)}) cannot exceed AK9-02 declared (${String(declared)}).`
8916
+ `build999: AK9-03 received (${renderCallerValue(received)}) cannot exceed AK9-02 declared (${renderCallerValue(declared)}).`
8855
8917
  );
8856
8918
  }
8857
8919
  if (group.transactionResponses.length !== received) {
8858
8920
  throw new AckBuildError(
8859
8921
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8860
- `build999: AK9-03 received (${String(received)}) must equal the number of supplied transaction responses (${String(group.transactionResponses.length)}).`
8922
+ `build999: AK9-03 received (${renderCallerValue(received)}) must equal the number of supplied transaction responses (${renderCallerValue(group.transactionResponses.length)}).`
8861
8923
  );
8862
8924
  }
8863
8925
  }
@@ -8926,7 +8988,7 @@ function buildIk5(response, esc, elementSeparator, segmentTerminator) {
8926
8988
  if (codes.length > 5) {
8927
8989
  throw new AckBuildError(
8928
8990
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8929
- `build999: IK5 accepts up to 5 syntax error codes (IK5-02..IK5-06); ${String(codes.length)} supplied.`
8991
+ `build999: IK5 accepts up to 5 syntax error codes (IK5-02..IK5-06); ${renderCallerValue(codes.length)} supplied.`
8930
8992
  );
8931
8993
  }
8932
8994
  for (const code of codes) parts.push(esc(code));
@@ -8944,7 +9006,7 @@ function buildAk9(group, esc, elementSeparator, segmentTerminator) {
8944
9006
  if (codes.length > 5) {
8945
9007
  throw new AckBuildError(
8946
9008
  ACK_BUILD_ERROR_CODES.X12_ACK_COUNT_MISMATCH,
8947
- `build999: AK9 accepts up to 5 syntax error codes (AK9-05..AK9-09); ${String(codes.length)} supplied.`
9009
+ `build999: AK9 accepts up to 5 syntax error codes (AK9-05..AK9-09); ${renderCallerValue(codes.length)} supplied.`
8948
9010
  );
8949
9011
  }
8950
9012
  for (const code of codes) parts.push(esc(code));
@@ -8963,7 +9025,7 @@ function padControl8(value, width) {
8963
9025
  if (value.length < width) return "0".repeat(width - value.length) + value;
8964
9026
  throw new AckBuildError(
8965
9027
  ACK_BUILD_ERROR_CODES.X12_ACK_INVALID_SPEC,
8966
- `build999: control number "${value}" exceeds the ${String(width)}-char spec limit.`
9028
+ `build999: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
8967
9029
  );
8968
9030
  }
8969
9031
  function expandYY8(yymmdd) {
@@ -8993,7 +9055,7 @@ function enforceAcceptIsClean2(spec) {
8993
9055
  if (spec.ackCode === TA1_ACK_CODES.A && spec.noteCode !== "000") {
8994
9056
  throw new AckBuildError(
8995
9057
  ACK_BUILD_ERROR_CODES.X12_TA1_ACCEPT_WITH_NOTE,
8996
- `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.`
9058
+ `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.`
8997
9059
  );
8998
9060
  }
8999
9061
  }
@@ -9010,18 +9072,39 @@ function serializeX12(interchange, opts = {}) {
9010
9072
  };
9011
9073
  let out = interchange.isa.raw;
9012
9074
  let segIdx = 0;
9075
+ const orphans = interchange.orphanSegments ?? [];
9076
+ const placed = /* @__PURE__ */ new Set();
9077
+ const flushOrphans = (slot) => {
9078
+ let written = 0;
9079
+ for (const [i, orphan] of orphans.entries()) {
9080
+ if (placed.has(i)) continue;
9081
+ if (!slot(orphan.anchor)) continue;
9082
+ placed.add(i);
9083
+ out += orphan.raw + term;
9084
+ segIdx++;
9085
+ written++;
9086
+ }
9087
+ return written;
9088
+ };
9013
9089
  for (const ta1 of interchange.ta1Segments) {
9014
9090
  out += ta1.raw + term;
9015
9091
  segIdx++;
9016
9092
  }
9017
9093
  for (const [g, group] of interchange.groups.entries()) {
9094
+ flushOrphans((a) => a?.kind === "interchange" && a.groupIndex === g);
9018
9095
  const gsSegIdx = ++segIdx;
9019
9096
  out += group.gs.raw + term;
9020
9097
  for (const [t, tx] of group.transactions.entries()) {
9021
- const stSegIdx = segIdx + 1;
9022
- const segCount = tx.rawSegments.length;
9098
+ flushOrphans((a) => a?.kind === "group" && a.groupIndex === g && a.transactionIndex === t);
9023
9099
  const lastIdx = tx.rawSegments.length - 1;
9100
+ let segCount = tx.rawSegments.length;
9101
+ let stSegIdx = segIdx + 1;
9024
9102
  for (const [k, raw] of tx.rawSegments.entries()) {
9103
+ const inside = flushOrphans(
9104
+ (a) => a?.kind === "transaction" && a.groupIndex === g && a.transactionIndex === t && a.segmentOffset === k
9105
+ );
9106
+ if (k === 0) stSegIdx = segIdx + 1;
9107
+ else segCount += inside;
9025
9108
  segIdx++;
9026
9109
  if (recompute && tx.se !== void 0 && k === lastIdx) {
9027
9110
  out += substituteElement(tx.se.elements, 1, String(segCount), elementSep) + term;
@@ -9029,21 +9112,20 @@ function serializeX12(interchange, opts = {}) {
9029
9112
  out += raw + term;
9030
9113
  }
9031
9114
  }
9115
+ flushOrphans(
9116
+ (a) => a?.kind === "transaction" && a.groupIndex === g && a.transactionIndex === t && a.segmentOffset === tx.rawSegments.length
9117
+ );
9032
9118
  if (specClean && tx.se !== void 0) {
9033
9119
  const declaredSe = elementAt(tx.se.elements, 1);
9034
9120
  if (declaredSe !== String(segCount)) {
9035
9121
  emit(
9036
- segmentCountMismatch(
9037
- {
9038
- segmentIndex: stSegIdx,
9039
- interchangeIndex: 0,
9040
- groupIndex: g,
9041
- transactionIndex: t,
9042
- elementIndex: 1
9043
- },
9044
- declaredSe,
9045
- segCount
9046
- )
9122
+ segmentCountMismatch({
9123
+ segmentIndex: stSegIdx,
9124
+ interchangeIndex: 0,
9125
+ groupIndex: g,
9126
+ transactionIndex: t,
9127
+ elementIndex: 1
9128
+ })
9047
9129
  );
9048
9130
  }
9049
9131
  const st02 = elementAt(tx.st.elements, 2);
@@ -9058,14 +9140,15 @@ function serializeX12(interchange, opts = {}) {
9058
9140
  transactionIndex: t,
9059
9141
  elementIndex: 2
9060
9142
  },
9061
- "ST-02/SE-02",
9062
- st02,
9063
- se02
9143
+ CONTROL_NUMBER_PAIRS.TRANSACTION
9064
9144
  )
9065
9145
  );
9066
9146
  }
9067
9147
  }
9068
9148
  }
9149
+ flushOrphans(
9150
+ (a) => a?.kind === "group" && a.groupIndex === g && a.transactionIndex === group.transactions.length
9151
+ );
9069
9152
  if (group.ge !== void 0) {
9070
9153
  const txCount = group.transactions.length;
9071
9154
  segIdx++;
@@ -9078,11 +9161,12 @@ function serializeX12(interchange, opts = {}) {
9078
9161
  const declaredGe = elementAt(group.ge.elements, 1);
9079
9162
  if (declaredGe !== String(txCount)) {
9080
9163
  emit(
9081
- transactionCountMismatch(
9082
- { segmentIndex: gsSegIdx, interchangeIndex: 0, groupIndex: g, elementIndex: 1 },
9083
- declaredGe,
9084
- txCount
9085
- )
9164
+ transactionCountMismatch({
9165
+ segmentIndex: gsSegIdx,
9166
+ interchangeIndex: 0,
9167
+ groupIndex: g,
9168
+ elementIndex: 1
9169
+ })
9086
9170
  );
9087
9171
  }
9088
9172
  const gs06 = elementAt(group.gs.elements, 6);
@@ -9091,15 +9175,15 @@ function serializeX12(interchange, opts = {}) {
9091
9175
  emit(
9092
9176
  controlNumberMismatch(
9093
9177
  { segmentIndex: gsSegIdx, interchangeIndex: 0, groupIndex: g, elementIndex: 2 },
9094
- "GS-06/GE-02",
9095
- gs06,
9096
- ge02
9178
+ CONTROL_NUMBER_PAIRS.GROUP
9097
9179
  )
9098
9180
  );
9099
9181
  }
9100
9182
  }
9101
9183
  }
9102
9184
  }
9185
+ flushOrphans((a) => a?.kind === "interchange" && a.groupIndex === interchange.groups.length);
9186
+ flushOrphans(() => true);
9103
9187
  if (interchange.iea !== void 0) {
9104
9188
  const groupCount = interchange.groups.length;
9105
9189
  segIdx++;
@@ -9111,13 +9195,7 @@ function serializeX12(interchange, opts = {}) {
9111
9195
  if (specClean) {
9112
9196
  const declaredIea = elementAt(interchange.iea.elements, 1);
9113
9197
  if (declaredIea !== String(groupCount)) {
9114
- emit(
9115
- groupCountMismatch(
9116
- { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 },
9117
- declaredIea,
9118
- groupCount
9119
- )
9120
- );
9198
+ emit(groupCountMismatch({ segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 1 }));
9121
9199
  }
9122
9200
  const isa13 = elementAt(interchange.isa.elements, 13);
9123
9201
  const iea02 = elementAt(interchange.iea.elements, 2);
@@ -9125,9 +9203,7 @@ function serializeX12(interchange, opts = {}) {
9125
9203
  emit(
9126
9204
  controlNumberMismatch(
9127
9205
  { segmentIndex: segIdx, interchangeIndex: 0, elementIndex: 2 },
9128
- "ISA-13/IEA-02",
9129
- isa13,
9130
- iea02
9206
+ CONTROL_NUMBER_PAIRS.INTERCHANGE
9131
9207
  )
9132
9208
  );
9133
9209
  }
@@ -9270,7 +9346,11 @@ function buildTransaction(tx, esc, elementSeparator, segmentTerminator) {
9270
9346
  if (segment.length === 0 || (segment[0] ?? "") === "") {
9271
9347
  throw new X12BuildError(
9272
9348
  X12_BUILD_ERROR_CODES.X12_BUILD_INVALID_SPEC,
9273
- `buildInterchange: a segment spec in transaction "${esc(tx.transactionSetIdCode)}" has no segment id.`
9349
+ // Rendered from the RAW id, not the `?`-escaped one. `esc` can double
9350
+ // the length, so escaping first made the reported "(N characters)" the
9351
+ // escaped length rather than the caller's: a 100-character all-`?` id
9352
+ // reported 200. The escape exists for the wire, and this is a message.
9353
+ `buildInterchange: a segment spec in transaction ${renderCallerValue(tx.transactionSetIdCode)} has no segment id.`
9274
9354
  );
9275
9355
  }
9276
9356
  bodySegments += joinSeg2(segment.map(esc), elementSeparator, segmentTerminator);
@@ -9296,7 +9376,7 @@ function padControl9(value, width) {
9296
9376
  if (value.length < width) return "0".repeat(width - value.length) + value;
9297
9377
  throw new X12BuildError(
9298
9378
  X12_BUILD_ERROR_CODES.X12_BUILD_INVALID_SPEC,
9299
- `buildInterchange: control number "${value}" exceeds the ${String(width)}-char spec limit.`
9379
+ `buildInterchange: control number ${renderCallerValue(value)} exceeds the ${String(width)}-char spec limit.`
9300
9380
  );
9301
9381
  }
9302
9382
  function expandYY9(yymmdd) {
@@ -9590,9 +9670,10 @@ var profiles = Object.freeze({
9590
9670
  });
9591
9671
 
9592
9672
  // src/index.ts
9593
- var VERSION = "0.0.3";
9673
+ var VERSION = "0.0.5";
9594
9674
 
9595
9675
  exports.ACK_BUILD_ERROR_CODES = ACK_BUILD_ERROR_CODES;
9676
+ exports.ALL_WARNING_MESSAGES = ALL_WARNING_MESSAGES;
9596
9677
  exports.AUTH_278_BUILD_ERROR_CODES = AUTH_278_BUILD_ERROR_CODES;
9597
9678
  exports.AUTH_278_LOOP_2000A = AUTH_278_LOOP_2000A;
9598
9679
  exports.AUTH_278_LOOP_2000B = AUTH_278_LOOP_2000B;
@@ -9601,6 +9682,9 @@ exports.AUTH_278_LOOP_2000D = AUTH_278_LOOP_2000D;
9601
9682
  exports.AUTH_278_LOOP_2000E = AUTH_278_LOOP_2000E;
9602
9683
  exports.AUTH_278_LOOP_2000F = AUTH_278_LOOP_2000F;
9603
9684
  exports.AckBuildError = AckBuildError;
9685
+ exports.BALANCE_INVARIANTS = BALANCE_INVARIANTS;
9686
+ exports.BUILD_REFUSAL_VALUE_MAX_LENGTH = BUILD_REFUSAL_VALUE_MAX_LENGTH;
9687
+ exports.BUILD_REFUSAL_VALUE_MAX_RENDERED = BUILD_REFUSAL_VALUE_MAX_RENDERED;
9604
9688
  exports.CARC = CARC;
9605
9689
  exports.CLAIM_837D_LOOP_2000A = CLAIM_837D_LOOP_2000A;
9606
9690
  exports.CLAIM_837D_LOOP_2300 = CLAIM_837D_LOOP_2300;
@@ -9625,6 +9709,7 @@ exports.CLAIM_STATUS_277_BUILD_ERROR_CODES = CLAIM_STATUS_277_BUILD_ERROR_CODES;
9625
9709
  exports.CLAIM_STATUS_CATEGORY_CODES = CLAIM_STATUS_CATEGORY_CODES;
9626
9710
  exports.CLAIM_STATUS_CODES = CLAIM_STATUS_CODES;
9627
9711
  exports.CLP_STATUS = CLP_STATUS;
9712
+ exports.CONTROL_NUMBER_PAIRS = CONTROL_NUMBER_PAIRS;
9628
9713
  exports.Claim837BuildError = Claim837BuildError;
9629
9714
  exports.ClaimStatus277BuildError = ClaimStatus277BuildError;
9630
9715
  exports.DELIMITER_POSITIONS = DELIMITER_POSITIONS;
@@ -9654,6 +9739,7 @@ exports.ISA_MIN_LENGTH = ISA_MIN_LENGTH;
9654
9739
  exports.LoopSpecDefinitionError = LoopSpecDefinitionError;
9655
9740
  exports.MAINTENANCE_TYPE_CODES = MAINTENANCE_TYPE_CODES;
9656
9741
  exports.NM1_QUALIFIERS = NM1_QUALIFIERS;
9742
+ exports.NON_SPEC_SEGMENT_ID = NON_SPEC_SEGMENT_ID;
9657
9743
  exports.PREMIUM_820_BUILD_ERROR_CODES = PREMIUM_820_BUILD_ERROR_CODES;
9658
9744
  exports.PREMIUM_820_LOOP_1000A = PREMIUM_820_LOOP_1000A;
9659
9745
  exports.PREMIUM_820_LOOP_1000B = PREMIUM_820_LOOP_1000B;
@@ -9670,6 +9756,7 @@ exports.REMIT_835_LOOP_1000B = REMIT_835_LOOP_1000B;
9670
9756
  exports.REMIT_835_LOOP_2000 = REMIT_835_LOOP_2000;
9671
9757
  exports.REMIT_835_LOOP_2100 = REMIT_835_LOOP_2100;
9672
9758
  exports.REMIT_835_LOOP_2110 = REMIT_835_LOOP_2110;
9759
+ exports.REQUIRED_LOOPS = REQUIRED_LOOPS;
9673
9760
  exports.Remit835BuildError = Remit835BuildError;
9674
9761
  exports.SERVICE_TYPE_CODES = SERVICE_TYPE_CODES;
9675
9762
  exports.STATUS_277_LOOP_2000A = STATUS_277_LOOP_2000A;
@@ -9682,6 +9769,7 @@ exports.STATUS_277_LOOP_2220 = STATUS_277_LOOP_2220;
9682
9769
  exports.ServicesReview278BuildError = ServicesReview278BuildError;
9683
9770
  exports.TA1_ACK_CODES = TA1_ACK_CODES;
9684
9771
  exports.TA1_NOTE_CODES = TA1_NOTE_CODES;
9772
+ exports.UNEXPECTED_SEGMENT_CONTEXTS = UNEXPECTED_SEGMENT_CONTEXTS;
9685
9773
  exports.VERSION = VERSION;
9686
9774
  exports.WARNING_CODES = WARNING_CODES;
9687
9775
  exports.X12BuildError = X12BuildError;
@@ -9758,6 +9846,7 @@ exports.partitionWarnings = partitionWarnings;
9758
9846
  exports.pre005010 = pre005010;
9759
9847
  exports.profiles = profiles;
9760
9848
  exports.remitBalanceMismatch = remitBalanceMismatch;
9849
+ exports.renderCallerValue = renderCallerValue;
9761
9850
  exports.resolveHiQualifier = resolveHiQualifier;
9762
9851
  exports.segmentCountMismatch = segmentCountMismatch;
9763
9852
  exports.serializeX12 = serializeX12;