@ontemper/edi 1.1.6 → 1.1.7

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/README.md CHANGED
@@ -54,10 +54,15 @@ const x12Document = await edi.toX12({
54
54
  input: x12Interchanges[0],
55
55
  });
56
56
 
57
- // Generate acknowledgment
58
- const acknowledgment = await edi.acknowledgeX12({
57
+ // Generate and serialize acknowledgments
58
+ const acknowledgments = await edi.acknowledgeX12({
59
59
  input: x12Interchanges[0],
60
60
  });
61
+
62
+ for (const acknowledgment of acknowledgments) {
63
+ // Apply partner-specific outbound envelope IDs here when needed.
64
+ const acknowledgmentX12 = await edi.toX12({ input: acknowledgment });
65
+ }
61
66
  ```
62
67
 
63
68
  ## API Reference
@@ -104,7 +109,7 @@ const x12Document = await edi.toX12({
104
109
 
105
110
  **Returns:** X12 document as string
106
111
 
107
- **Control number auto-stamping:** Leave `InterchangeControlNumber_13`, `GroupControlNumber_6`, and `TransactionSetControlNumber_02` empty or omit them. The SDK fetches atomic, per-trading-partner control numbers from a centralized counter service. If values are already set, they are left unchanged.
112
+ **Control number auto-stamping:** Leave `InterchangeControlNumber_13`, `GroupControlNumber_6`, and `TransactionSetControlNumber_02` empty or omit them. Empty and all-zero ISA13, GS06, or ST02 values are treated as unassigned. Valid non-zero values are preserved.
108
113
 
109
114
  #### `validateX12(options: ValidateX12Options): Promise<OperationResult>`
110
115
 
@@ -122,21 +127,29 @@ const validationResult = await edi.validateX12({
122
127
 
123
128
  **Returns:** Validation result with operation details
124
129
 
125
- #### `acknowledgeX12(options: AcknolwedgeX12Options): Promise<OperationResult>`
130
+ #### `acknowledgeX12(options: AcknolwedgeX12Options): Promise<X12Interchange[]>`
126
131
 
127
- Generates acknowledgment for X12 interchange objects.
132
+ Generates acknowledgment interchange objects. Apply any partner-specific outbound envelope IDs, then serialize
133
+ each acknowledgment with `toX12()`. During serialization, `toX12()` assigns control numbers using the final
134
+ sender and receiver. The method always returns an array because one input can produce more than one
135
+ acknowledgment.
128
136
 
129
137
  ```typescript
130
- const acknowledgment = await edi.acknowledgeX12({
138
+ const acknowledgments = await edi.acknowledgeX12({
131
139
  input: x12InterchangeObject,
132
140
  });
141
+
142
+ for (const acknowledgment of acknowledgments) {
143
+ // Apply partner-specific outbound envelope IDs here when needed.
144
+ const serialized = await edi.toX12({ input: acknowledgment });
145
+ }
133
146
  ```
134
147
 
135
148
  **Parameters:**
136
149
 
137
150
  - `options.input` - The X12 interchange object to acknowledge
138
151
 
139
- **Returns:** Acknowledgment result with operation details
152
+ **Returns:** The generated acknowledgment interchanges
140
153
 
141
154
  ## Error Handling
142
155
 
@@ -320,9 +333,13 @@ IEA*1*000001000`;
320
333
  const x12Output = await edi.toX12({ input: interchange });
321
334
  logger.info({ x12Output }, 'Converted back to X12');
322
335
 
323
- // Generate acknowledgment
324
- const acknowledgment = await edi.acknowledgeX12({ input: interchange });
325
- logger.info({ acknowledgment }, 'Generated acknowledgment');
336
+ // Generate and serialize acknowledgments. toX12 assigns control numbers
337
+ // after any partner-specific outbound envelope changes.
338
+ const acknowledgments = await edi.acknowledgeX12({ input: interchange });
339
+ for (const acknowledgment of acknowledgments) {
340
+ const acknowledgmentX12 = await edi.toX12({ input: acknowledgment });
341
+ logger.info({ acknowledgmentX12 }, 'Generated acknowledgment');
342
+ }
326
343
  }
327
344
  } catch (error) {
328
345
  logger.error({ err: error }, 'EDI processing failed');
@@ -350,7 +367,9 @@ const current = await getCurrentControlNumber('ISA13', 'ZZ:SENDER', 'ZZ:RECEIVER
350
367
  await setControlNumber('ISA13', 'ZZ:SENDER', 'ZZ:RECEIVER', 1000);
351
368
  ```
352
369
 
353
- In most cases you don't need these directly — `toX12()` calls `getNextControlNumber` automatically for any empty ISA13/GS06 fields.
370
+ In most cases you don't need these directly — `toX12()` automatically stamps any empty or all-zero
371
+ ISA13/GS06/ST02 fields. ISA13 and GS06 use `getNextControlNumber`; ST02 is assigned `"0001"`. This includes the
372
+ zero placeholders returned by acknowledgment generation.
354
373
  Once a counter has incremented, the API rejects later `setControlNumber` calls to avoid resetting active sequences.
355
374
  Retried `setControlNumber` calls with the same already-stored seed are treated as idempotent no-ops.
356
375
  The SDK surfaces that as a `ControlNumberError` with `code: "counter_already_started"`:
package/dist/index.d.ts CHANGED
@@ -42,10 +42,10 @@ export declare class TemperEdiClient {
42
42
  private unwrapError;
43
43
  fromX12({ input }: FromX12Options): Promise<X12Interchange[]>;
44
44
  /**
45
- * Stamp ISA13/GS06 control numbers on the interchange if not already set.
45
+ * Stamp ISA13/GS06/ST02 control numbers on the interchange if not already set.
46
46
  * Uses the Restate-backed counter service for atomic, per-trading-partner sequencing.
47
47
  * ST02 is always "0001" (one transaction per file per ticket).
48
- * If a control number is already set, it is left unchanged (opt-out for manual overrides).
48
+ * Zero-filled values from acknowledgment generation are placeholders, not manual overrides.
49
49
  */
50
50
  private stampControlNumbers;
51
51
  toX12({ input }: ToX12Options): Promise<unknown>;
package/dist/index.js CHANGED
@@ -80,6 +80,10 @@ const isEdiInfrastructureError = (error) => error instanceof EdiInfrastructureEr
80
80
  exports.isEdiInfrastructureError = isEdiInfrastructureError;
81
81
  const buildEdiPayload = (edi) => ({ type: 'edi', edi });
82
82
  const buildEdiX12Payload = (operation, x12) => buildEdiPayload({ operation, type: 'x12', x12 });
83
+ const needsControlNumber = (value) => {
84
+ const normalized = value?.trim();
85
+ return !normalized || /^0+$/.test(normalized);
86
+ };
83
87
  const ediAxios = (0, unnbound_logger_sdk_1.traceAxios)(axios_1.default.create(), { getPayload: internal_1.internal });
84
88
  class TemperEdiClient {
85
89
  X12;
@@ -144,10 +148,10 @@ class TemperEdiClient {
144
148
  }, (o) => buildEdiX12Payload('fromX12', { input, output: o?.result }));
145
149
  }
146
150
  /**
147
- * Stamp ISA13/GS06 control numbers on the interchange if not already set.
151
+ * Stamp ISA13/GS06/ST02 control numbers on the interchange if not already set.
148
152
  * Uses the Restate-backed counter service for atomic, per-trading-partner sequencing.
149
153
  * ST02 is always "0001" (one transaction per file per ticket).
150
- * If a control number is already set, it is left unchanged (opt-out for manual overrides).
154
+ * Zero-filled values from acknowledgment generation are placeholders, not manual overrides.
151
155
  */
152
156
  async stampControlNumbers(input) {
153
157
  const { getNextControlNumber } = await import('./control-numbers.js');
@@ -160,12 +164,13 @@ class TemperEdiClient {
160
164
  const receiverKey = `${receiverQual}:${receiverId}`;
161
165
  // Phase 1: Fetch all needed control numbers (no mutations yet).
162
166
  // If any fetch fails, no fields are mutated — avoids partial stamping.
163
- const isa13 = !isa.InterchangeControlNumber_13
167
+ const isa13 = needsControlNumber(isa.InterchangeControlNumber_13)
164
168
  ? await getNextControlNumber('ISA13', senderKey, receiverKey, 9)
165
169
  : null;
166
170
  const gs06Values = [];
167
- for (let i = 0; i < input.Groups.length; i++) {
168
- if (!input.Groups[i].GS.GroupControlNumber_6) {
171
+ const groups = input.Groups ?? [];
172
+ for (let i = 0; i < groups.length; i++) {
173
+ if (needsControlNumber(groups[i].GS.GroupControlNumber_6)) {
169
174
  const value = await getNextControlNumber('GS06', senderKey, receiverKey, 9);
170
175
  gs06Values.push({ index: i, value });
171
176
  }
@@ -178,16 +183,16 @@ class TemperEdiClient {
178
183
  }
179
184
  }
180
185
  for (const { index, value } of gs06Values) {
181
- const group = input.Groups[index];
186
+ const group = groups[index];
182
187
  group.GS.GroupControlNumber_6 = value;
183
188
  if (group.GETrailers?.length) {
184
189
  group.GETrailers[0].GroupControlNumber_2 = value;
185
190
  }
186
191
  }
187
192
  // ST02 — always "0001" (pure assignment, no external calls)
188
- for (const group of input.Groups) {
193
+ for (const group of groups) {
189
194
  for (const tx of group.Transactions) {
190
- if (!tx?.ST?.TransactionSetControlNumber_02) {
195
+ if (needsControlNumber(tx?.ST?.TransactionSetControlNumber_02)) {
191
196
  if (tx?.ST)
192
197
  tx.ST.TransactionSetControlNumber_02 = '0001';
193
198
  if (tx?.SE)
@@ -46,9 +46,13 @@ IEA*1*000001000`;
46
46
 
47
47
  logger.info({ validated }, 'X12 validated.');
48
48
 
49
- const acknowledged = await edi.acknowledgeX12({ input: x12Interchange });
49
+ const acknowledgments = await edi.acknowledgeX12({ input: x12Interchange });
50
50
 
51
- logger.info({ acknowledged }, 'X12 acknowledged.');
51
+ for (const acknowledgment of acknowledgments) {
52
+ // Apply partner-specific outbound envelope IDs before serialization when needed.
53
+ const acknowledgmentX12 = await edi.toX12({ input: acknowledgment });
54
+ logger.info({ acknowledgmentX12 }, 'X12 acknowledgment generated.');
55
+ }
52
56
  }),
53
57
  );
54
58
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ontemper/edi",
3
3
  "description": "An EDI client with structured logging.",
4
- "version": "1.1.6",
4
+ "version": "1.1.7",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "author": "Unnbound Team",