@ontemper/edi 1.2.0-beta.2 → 1.2.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +23 -59
- package/dist/control-numbers.d.ts +11 -31
- package/dist/control-numbers.js +31 -54
- package/dist/index.d.ts +7 -7
- package/dist/index.js +11 -10
- package/examples/node-edi.ts +0 -1
- package/package.json +6 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
## [1.1.7](https://github.com/ontemper/temper/releases/tag/%40ontemper%2Fedi-v1.1.7) (2026-07-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* stamp zero acknowledgment envelope control numbers ([#2421](https://github.com/ontemper/temper/pull/2421))
|
|
7
|
+
|
|
8
|
+
## [1.0.13](https://github.com/unnbounddev/unnbound-sdks/compare/unnbound-edi-sdk-v1.0.12...unnbound-edi-sdk-v1.0.13) (2025-11-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* setup ci/cd ([dd2640f](https://github.com/unnbounddev/unnbound-sdks/commit/dd2640f01e39f1342f3e08dc882164b50331c7bb))
|
|
14
|
+
* setup ci/cd ([520c03a](https://github.com/unnbounddev/unnbound-sdks/commit/520c03aacd978d7923b3bcf45c4f74a870be019d))
|
|
15
|
+
|
|
16
|
+
## [1.0.12](https://github.com/unnbounddev/unnbound-sdks/compare/unnbound-edi-sdk-v1.0.11...unnbound-edi-sdk-v1.0.12) (2025-11-25)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* remove unused files ([6dc00c2](https://github.com/unnbounddev/unnbound-sdks/commit/6dc00c20b092b17e9eb2108ef83dc5beafdef3f5))
|
|
22
|
+
* setup ci/cd ([d3759c1](https://github.com/unnbounddev/unnbound-sdks/commit/d3759c14f7863e4c8d2f5e80bdbbf2446df79f1a))
|
|
23
|
+
* setup ci/cd ([7e06fc8](https://github.com/unnbounddev/unnbound-sdks/commit/7e06fc84ae7bafba3e9c2c0225f9f9d614fa9207))
|
package/README.md
CHANGED
|
@@ -354,79 +354,42 @@ run().catch(console.error);
|
|
|
354
354
|
|
|
355
355
|
The SDK manages ISA13 and GS06 control numbers for each sender and receiver pair. `toX12()` automatically stamps
|
|
356
356
|
empty or all-zero ISA13, GS06, and ST02 fields. It preserves valid non-zero values. ISA13 and GS06 use the
|
|
357
|
-
centralized
|
|
357
|
+
centralized counter service. ST02 is assigned `"0001"`.
|
|
358
358
|
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
Each Temper environment has a separate counter ledger. Production allocations continue the partner's production
|
|
360
|
+
sequence. Sandbox and staging allocations do not consume production control numbers. A ledger rolls over after
|
|
361
|
+
`999999999`, so the next value is `000000001`.
|
|
361
362
|
|
|
362
|
-
###
|
|
363
|
+
### Automatic environment routing
|
|
364
|
+
|
|
365
|
+
Temper injects `TEMPER_ENVIRONMENT` into workflows. The SDK uses `UNNBOUND_ENVIRONMENT` only when
|
|
366
|
+
`TEMPER_ENVIRONMENT` is absent. The value must be `sandbox`, `staging`, or `production`. A missing or invalid value
|
|
367
|
+
causes a `ControlNumberError` with the `control_number_environment_missing` code before any HTTP request.
|
|
363
368
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
last-used number, so the next allocation returns the seed plus one. To make the next ISA13 value 4601, seed 4600:
|
|
369
|
+
`getNextControlNumber()`, `getCurrentControlNumber()`, and `toX12()` use this environment. They do not accept an
|
|
370
|
+
environment override. ISA15 (`UsageIndicator_15`) does not select a counter, and `toX12()` does not change it.
|
|
367
371
|
|
|
368
372
|
```typescript
|
|
369
|
-
import {
|
|
370
|
-
getControlNumberPolicy,
|
|
371
|
-
getCurrentControlNumber,
|
|
372
|
-
setControlNumber,
|
|
373
|
-
setControlNumberPolicy,
|
|
374
|
-
} from '@ontemper/edi';
|
|
373
|
+
import { getCurrentControlNumber, getNextControlNumber, setControlNumber } from '@ontemper/edi';
|
|
375
374
|
|
|
376
375
|
const senderId = 'ZZ:SENDER';
|
|
377
376
|
const receiverId = 'ZZ:RECEIVER';
|
|
378
377
|
|
|
379
|
-
await
|
|
380
|
-
|
|
381
|
-
// Seed the production lane explicitly before it starts.
|
|
382
|
-
await setControlNumber('ISA13', senderId, receiverId, 4600, 9, { lane: 'production' });
|
|
383
|
-
|
|
384
|
-
const policy = await getControlNumberPolicy(senderId, receiverId);
|
|
385
|
-
const production = await getCurrentControlNumber('ISA13', senderId, receiverId, 9, { lane: 'production' });
|
|
386
|
-
const test = await getCurrentControlNumber('ISA13', senderId, receiverId, 9, { lane: 'test' });
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
`getControlNumberPolicy()` returns `configured` and `separateCounters`. After test traffic starts, you cannot turn
|
|
390
|
-
separate counters off. `setControlNumberPolicy()` reports this as a `ControlNumberError` with the
|
|
391
|
-
`counter_policy_locked` code.
|
|
392
|
-
|
|
393
|
-
### Automatic environment routing
|
|
394
|
-
|
|
395
|
-
The SDK reads `UNNBOUND_ENVIRONMENT` and sends it with counter operations only when its value is `sandbox`,
|
|
396
|
-
`staging`, or `production`. The service selects the lane:
|
|
397
|
-
|
|
398
|
-
| Policy | Production environment | Sandbox or staging |
|
|
399
|
-
| --- | --- | --- |
|
|
400
|
-
| No policy row | Production lane | Production lane |
|
|
401
|
-
| `separateCounters: false` | Production lane | Production lane |
|
|
402
|
-
| `separateCounters: true` | Production lane | Test lane |
|
|
403
|
-
|
|
404
|
-
Older SDK versions do not send the environment, so their operations use the production lane. This is the historical
|
|
405
|
-
sequence that all environments use when separation is off. ISA15
|
|
406
|
-
(`UsageIndicator_15`) never selects a counter. Set ISA15 independently to the value required by the interchange.
|
|
407
|
-
`toX12()` does not change it.
|
|
408
|
-
|
|
409
|
-
### Manage counters directly
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
import { getCurrentControlNumber, getNextControlNumber, setControlNumber } from '@ontemper/edi';
|
|
378
|
+
const next = await getNextControlNumber('ISA13', senderId, receiverId);
|
|
379
|
+
const current = await getCurrentControlNumber('ISA13', senderId, receiverId);
|
|
413
380
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// The seed is last-used. To allocate 4601 next, seed 4600.
|
|
417
|
-
await setControlNumber('ISA13', 'ZZ:SENDER', 'ZZ:RECEIVER', 4600, 9, { lane: 'production' });
|
|
381
|
+
// Legacy production migration: store 4599 as the last-used value.
|
|
382
|
+
await setControlNumber('ISA13', senderId, receiverId, 4599);
|
|
418
383
|
```
|
|
419
384
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
accept a lane.
|
|
385
|
+
`setControlNumber()` is the legacy production setter. Its value is the last-used number, so setting `4599` makes the
|
|
386
|
+
next allocation `4600`. It uses the flat production route and does not use the environment variables.
|
|
423
387
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
starts with a `ControlNumberError` whose code is `counter_already_started`.
|
|
388
|
+
Use the Builder agent `set_edi_control_number` tool for per-environment seeding. The tool value is the next number.
|
|
389
|
+
For example, seeding `4600` makes the next allocation `4600`. Seeding is forward-only. Re-seeding the same value is
|
|
390
|
+
a no-op.
|
|
428
391
|
|
|
429
|
-
These helpers require `UNNBOUND_API_URL
|
|
392
|
+
These helpers require `UNNBOUND_API_URL`. Temper configures it in workflow environments.
|
|
430
393
|
|
|
431
394
|
## Requirements
|
|
432
395
|
|
|
@@ -434,6 +397,7 @@ These helpers require `UNNBOUND_API_URL`, which is preconfigured in workflow env
|
|
|
434
397
|
- TypeScript (for TypeScript projects)
|
|
435
398
|
- The EDI connection env — `UNNBOUND_EDI_BASE_URL`, plus `UNNBOUND_EDI_API_KEY` where the path needs a credential — is platform-injected per environment in Temper workflows; nothing to configure. Supply a key yourself only for the standalone legacy EDINation-cloud path.
|
|
436
399
|
- `UNNBOUND_API_URL` (for control number auto-stamping, pre-configured in workflow environments)
|
|
400
|
+
- `TEMPER_ENVIRONMENT` for counter routing, with `UNNBOUND_ENVIRONMENT` as the legacy fallback
|
|
437
401
|
|
|
438
402
|
## Dependencies
|
|
439
403
|
|
|
@@ -1,27 +1,9 @@
|
|
|
1
1
|
/** @public */
|
|
2
|
-
export type
|
|
3
|
-
/** @public */
|
|
4
|
-
export type ControlNumberLane = 'production' | 'test';
|
|
5
|
-
/** @public */
|
|
6
|
-
export interface ControlNumberLaneOptions {
|
|
7
|
-
readonly lane?: ControlNumberLane;
|
|
8
|
-
}
|
|
2
|
+
export type TemperEnvironment = 'sandbox' | 'staging' | 'production';
|
|
9
3
|
/** @public */
|
|
10
|
-
export
|
|
11
|
-
readonly separateCounters: boolean;
|
|
12
|
-
}
|
|
13
|
-
/** @public */
|
|
14
|
-
export interface SetControlNumberPolicyResponse {
|
|
15
|
-
readonly tradingPartnerKey: string;
|
|
16
|
-
readonly separateCounters: boolean;
|
|
17
|
-
}
|
|
18
|
-
/** @public */
|
|
19
|
-
export interface GetControlNumberPolicyResponse {
|
|
20
|
-
readonly configured: boolean;
|
|
21
|
-
readonly separateCounters: boolean;
|
|
22
|
-
}
|
|
4
|
+
export type ControlNumberCounterName = 'ISA13' | 'GS06';
|
|
23
5
|
/** @public */
|
|
24
|
-
export type ControlNumberErrorCode = 'counter_already_started' | '
|
|
6
|
+
export type ControlNumberErrorCode = 'counter_already_started' | 'control_number_environment_missing' | 'control_number_bad_request' | 'control_number_unauthorized' | 'control_number_unknown_error';
|
|
25
7
|
interface ControlNumberErrorOptions extends ErrorOptions {
|
|
26
8
|
code: ControlNumberErrorCode;
|
|
27
9
|
message: string;
|
|
@@ -32,12 +14,12 @@ export declare class ControlNumberError extends Error {
|
|
|
32
14
|
constructor({ code, message, ...options }: ControlNumberErrorOptions);
|
|
33
15
|
}
|
|
34
16
|
/** @public */
|
|
35
|
-
export declare const isControlNumberError: (error:
|
|
17
|
+
export declare const isControlNumberError: (error: ErrorOptions['cause']) => error is ControlNumberError;
|
|
36
18
|
/**
|
|
37
19
|
* Get the next control number for an EDI counter.
|
|
38
20
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
21
|
+
* The centralized service isolates counters by Temper environment.
|
|
22
|
+
* It rolls 999999999 over to 1.
|
|
41
23
|
*
|
|
42
24
|
* @param counterName - Counter type: 'ISA13' or 'GS06'
|
|
43
25
|
* @param senderId - Sender qualifier + ID (e.g. "ZZ:SENDER123")
|
|
@@ -50,14 +32,12 @@ export declare function getNextControlNumber(counterName: ControlNumberCounterNa
|
|
|
50
32
|
* Get the current control number value without incrementing.
|
|
51
33
|
*/
|
|
52
34
|
/** @public */
|
|
53
|
-
export declare function getCurrentControlNumber(counterName: ControlNumberCounterName, senderId: string, receiverId: string, maxDigits?: number
|
|
35
|
+
export declare function getCurrentControlNumber(counterName: ControlNumberCounterName, senderId: string, receiverId: string, maxDigits?: number): Promise<string>;
|
|
54
36
|
/**
|
|
55
|
-
* Set
|
|
37
|
+
* Set the last-used production control number through the legacy endpoint.
|
|
38
|
+
*
|
|
39
|
+
* Use the Builder agent `set_edi_control_number` tool for per-environment seeding.
|
|
56
40
|
*/
|
|
57
41
|
/** @public */
|
|
58
|
-
export declare function setControlNumber(counterName: ControlNumberCounterName, senderId: string, receiverId: string, value: number, maxDigits?: number
|
|
59
|
-
/** @public */
|
|
60
|
-
export declare function setControlNumberPolicy(senderId: string, receiverId: string, { separateCounters }: SetControlNumberPolicyOptions): Promise<SetControlNumberPolicyResponse>;
|
|
61
|
-
/** @public */
|
|
62
|
-
export declare function getControlNumberPolicy(senderId: string, receiverId: string): Promise<GetControlNumberPolicyResponse>;
|
|
42
|
+
export declare function setControlNumber(counterName: ControlNumberCounterName, senderId: string, receiverId: string, value: number, maxDigits?: number): Promise<string>;
|
|
63
43
|
export {};
|
package/dist/control-numbers.js
CHANGED
|
@@ -37,8 +37,6 @@ exports.isControlNumberError = exports.ControlNumberError = void 0;
|
|
|
37
37
|
exports.getNextControlNumber = getNextControlNumber;
|
|
38
38
|
exports.getCurrentControlNumber = getCurrentControlNumber;
|
|
39
39
|
exports.setControlNumber = setControlNumber;
|
|
40
|
-
exports.setControlNumberPolicy = setControlNumberPolicy;
|
|
41
|
-
exports.getControlNumberPolicy = getControlNumberPolicy;
|
|
42
40
|
const axios_1 = __importStar(require("axios"));
|
|
43
41
|
let _client = null;
|
|
44
42
|
function getApiClient() {
|
|
@@ -80,20 +78,24 @@ exports.ControlNumberError = ControlNumberError;
|
|
|
80
78
|
/** @public */
|
|
81
79
|
const isControlNumberError = (error) => error instanceof ControlNumberError;
|
|
82
80
|
exports.isControlNumberError = isControlNumberError;
|
|
83
|
-
const
|
|
84
|
-
const environment = process.env.UNNBOUND_ENVIRONMENT;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
const requireEnvironment = () => {
|
|
82
|
+
const environment = process.env.TEMPER_ENVIRONMENT ?? process.env.UNNBOUND_ENVIRONMENT;
|
|
83
|
+
if (environment === 'sandbox' || environment === 'staging' || environment === 'production') {
|
|
84
|
+
return environment;
|
|
85
|
+
}
|
|
86
|
+
throw new ControlNumberError({
|
|
87
|
+
code: 'control_number_environment_missing',
|
|
88
|
+
message: "TEMPER_ENVIRONMENT or UNNBOUND_ENVIRONMENT must be set to 'sandbox', 'staging', or 'production' because automatic control-number routing needs it.",
|
|
89
|
+
});
|
|
88
90
|
};
|
|
89
|
-
function unwrapControlNumberError(error,
|
|
91
|
+
function unwrapControlNumberError(error, environmentScoped = false) {
|
|
90
92
|
if (error instanceof ControlNumberError)
|
|
91
93
|
throw error;
|
|
92
94
|
if ((0, axios_1.isAxiosError)(error)) {
|
|
93
|
-
if (error.response?.status ===
|
|
95
|
+
if (environmentScoped && error.response?.status === 404) {
|
|
94
96
|
throw new ControlNumberError({
|
|
95
|
-
code: '
|
|
96
|
-
message: '
|
|
97
|
+
code: 'control_number_unknown_error',
|
|
98
|
+
message: 'The Temper API does not support environment-scoped EDI control numbers yet.',
|
|
97
99
|
cause: error,
|
|
98
100
|
});
|
|
99
101
|
}
|
|
@@ -107,7 +109,7 @@ function unwrapControlNumberError(error, operation) {
|
|
|
107
109
|
if (error.response?.status === 400) {
|
|
108
110
|
throw new ControlNumberError({
|
|
109
111
|
code: 'control_number_bad_request',
|
|
110
|
-
message: "Invalid EDI control number request. Use counter name 'ISA13' or 'GS06' with trading partner IDs
|
|
112
|
+
message: "Invalid EDI control number request. Use counter name 'ISA13' or 'GS06' with trading partner IDs.",
|
|
111
113
|
cause: error,
|
|
112
114
|
});
|
|
113
115
|
}
|
|
@@ -126,25 +128,23 @@ function unwrapControlNumberError(error, operation) {
|
|
|
126
128
|
});
|
|
127
129
|
}
|
|
128
130
|
async function requestControlNumber(operation, input) {
|
|
129
|
-
const environment =
|
|
130
|
-
const
|
|
131
|
+
const environment = requireEnvironment();
|
|
132
|
+
const path = `/api/v2/internal/edi/control-numbers/${environment}/${operation}`;
|
|
131
133
|
try {
|
|
132
134
|
const response = operation === 'count'
|
|
133
|
-
? await getApiClient().get(
|
|
134
|
-
|
|
135
|
-
})
|
|
136
|
-
: await getApiClient().post(`/api/v2/internal/edi/control-numbers/${operation}`, request);
|
|
135
|
+
? await getApiClient().get(path, { params: input })
|
|
136
|
+
: await getApiClient().post(path, input);
|
|
137
137
|
return response.data;
|
|
138
138
|
}
|
|
139
139
|
catch (error) {
|
|
140
|
-
return unwrapControlNumberError(error,
|
|
140
|
+
return unwrapControlNumberError(error, true);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
/**
|
|
144
144
|
* Get the next control number for an EDI counter.
|
|
145
145
|
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
146
|
+
* The centralized service isolates counters by Temper environment.
|
|
147
|
+
* It rolls 999999999 over to 1.
|
|
148
148
|
*
|
|
149
149
|
* @param counterName - Counter type: 'ISA13' or 'GS06'
|
|
150
150
|
* @param senderId - Sender qualifier + ID (e.g. "ZZ:SENDER123")
|
|
@@ -163,52 +163,29 @@ async function getNextControlNumber(counterName, senderId, receiverId, maxDigits
|
|
|
163
163
|
* Get the current control number value without incrementing.
|
|
164
164
|
*/
|
|
165
165
|
/** @public */
|
|
166
|
-
async function getCurrentControlNumber(counterName, senderId, receiverId, maxDigits = 9
|
|
166
|
+
async function getCurrentControlNumber(counterName, senderId, receiverId, maxDigits = 9) {
|
|
167
167
|
const data = await requestControlNumber('count', {
|
|
168
168
|
tradingPartnerKey: `${senderId}:${receiverId}`,
|
|
169
169
|
counterName,
|
|
170
|
-
...(options?.lane ? { lane: options.lane } : {}),
|
|
171
170
|
});
|
|
172
171
|
return String(data.value).padStart(maxDigits, '0');
|
|
173
172
|
}
|
|
174
173
|
/**
|
|
175
|
-
* Set
|
|
174
|
+
* Set the last-used production control number through the legacy endpoint.
|
|
175
|
+
*
|
|
176
|
+
* Use the Builder agent `set_edi_control_number` tool for per-environment seeding.
|
|
176
177
|
*/
|
|
177
178
|
/** @public */
|
|
178
|
-
async function setControlNumber(counterName, senderId, receiverId, value, maxDigits = 9
|
|
179
|
-
const data = await requestControlNumber('set', {
|
|
180
|
-
tradingPartnerKey: `${senderId}:${receiverId}`,
|
|
181
|
-
counterName,
|
|
182
|
-
value,
|
|
183
|
-
...(options?.lane ? { lane: options.lane } : {}),
|
|
184
|
-
});
|
|
185
|
-
return String(data.value).padStart(maxDigits, '0');
|
|
186
|
-
}
|
|
187
|
-
/** @public */
|
|
188
|
-
async function setControlNumberPolicy(senderId, receiverId, { separateCounters }) {
|
|
179
|
+
async function setControlNumber(counterName, senderId, receiverId, value, maxDigits = 9) {
|
|
189
180
|
try {
|
|
190
|
-
const
|
|
181
|
+
const { data } = await getApiClient().post('/api/v2/internal/edi/control-numbers/set', {
|
|
191
182
|
tradingPartnerKey: `${senderId}:${receiverId}`,
|
|
192
|
-
|
|
183
|
+
counterName,
|
|
184
|
+
value,
|
|
193
185
|
});
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
catch (error) {
|
|
197
|
-
return unwrapControlNumberError(error, 'policy');
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
/** @public */
|
|
201
|
-
async function getControlNumberPolicy(senderId, receiverId) {
|
|
202
|
-
try {
|
|
203
|
-
const response = await getApiClient().get('/api/v2/internal/edi/control-numbers/policy', {
|
|
204
|
-
params: { tradingPartnerKey: `${senderId}:${receiverId}` },
|
|
205
|
-
});
|
|
206
|
-
return {
|
|
207
|
-
configured: response.data.configured,
|
|
208
|
-
separateCounters: response.data.separateCounters,
|
|
209
|
-
};
|
|
186
|
+
return String(data.value).padStart(maxDigits, '0');
|
|
210
187
|
}
|
|
211
188
|
catch (error) {
|
|
212
|
-
return unwrapControlNumberError(error
|
|
189
|
+
return unwrapControlNumberError(error);
|
|
213
190
|
}
|
|
214
191
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export type {
|
|
3
|
-
export { ControlNumberError,
|
|
1
|
+
import { type X12Interchange } from './edination-client';
|
|
2
|
+
export type { ControlNumberErrorCode } from './control-numbers';
|
|
3
|
+
export { ControlNumberError, getCurrentControlNumber, getNextControlNumber, isControlNumberError, setControlNumber, } from './control-numbers';
|
|
4
4
|
export * from './edination-client/model';
|
|
5
5
|
interface UnnboundErrorOptions<C extends string = string> extends ErrorOptions {
|
|
6
6
|
message: string;
|
|
@@ -14,7 +14,7 @@ export type TemperEdiClientErrorCode = 'edi_read_error' | 'edi_write_error' | 'e
|
|
|
14
14
|
export declare class TemperEdiClientError extends UnnboundError<TemperEdiClientErrorCode> {
|
|
15
15
|
constructor({ message, code, ...o }: UnnboundErrorOptions<TemperEdiClientErrorCode>);
|
|
16
16
|
}
|
|
17
|
-
export declare const isTemperEdiClientError: (error:
|
|
17
|
+
export declare const isTemperEdiClientError: (error: ErrorOptions['cause']) => error is TemperEdiClientError;
|
|
18
18
|
/**
|
|
19
19
|
* A retryable failure that does not indicate an invalid EDI document.
|
|
20
20
|
*/
|
|
@@ -22,7 +22,7 @@ export declare class EdiInfrastructureError extends UnnboundError<'infrastructur
|
|
|
22
22
|
readonly retryable = true;
|
|
23
23
|
constructor({ message, ...o }: Omit<UnnboundErrorOptions<'infrastructure_error'>, 'code'>);
|
|
24
24
|
}
|
|
25
|
-
export declare const isEdiInfrastructureError: (error:
|
|
25
|
+
export declare const isEdiInfrastructureError: (error: ErrorOptions['cause']) => error is EdiInfrastructureError;
|
|
26
26
|
export interface FromX12Options {
|
|
27
27
|
input: unknown;
|
|
28
28
|
}
|
|
@@ -43,12 +43,12 @@ export declare class TemperEdiClient {
|
|
|
43
43
|
fromX12({ input }: FromX12Options): Promise<X12Interchange[]>;
|
|
44
44
|
/**
|
|
45
45
|
* Stamp ISA13/GS06/ST02 control numbers on the interchange if not already set.
|
|
46
|
-
* Uses the
|
|
46
|
+
* Uses the centralized per-environment service for atomic partner sequencing.
|
|
47
47
|
* ST02 is always "0001" (one transaction per file per ticket).
|
|
48
48
|
* Zero-filled values from acknowledgment generation are placeholders, not manual overrides.
|
|
49
49
|
*/
|
|
50
50
|
private stampControlNumbers;
|
|
51
|
-
toX12({ input }: ToX12Options): Promise<
|
|
51
|
+
toX12({ input }: ToX12Options): Promise<File>;
|
|
52
52
|
validateX12({ input }: ValidateX12Options): Promise<import("./edination-client").OperationResult>;
|
|
53
53
|
acknowledgeX12({ input }: AcknolwedgeX12Options): Promise<X12Interchange[]>;
|
|
54
54
|
}
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
36
36
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.TemperEdiClient = exports.isEdiInfrastructureError = exports.EdiInfrastructureError = exports.isTemperEdiClientError = exports.TemperEdiClientError = exports.
|
|
39
|
+
exports.TemperEdiClient = exports.isEdiInfrastructureError = exports.EdiInfrastructureError = exports.isTemperEdiClientError = exports.TemperEdiClientError = exports.setControlNumber = exports.isControlNumberError = exports.getNextControlNumber = exports.getCurrentControlNumber = exports.ControlNumberError = void 0;
|
|
40
40
|
const axios_1 = __importStar(require("axios"));
|
|
41
41
|
const unnbound_logger_sdk_1 = require("unnbound-logger-sdk");
|
|
42
42
|
const internal_1 = require("unnbound-logger-sdk/dist/internal");
|
|
@@ -44,12 +44,10 @@ const control_numbers_1 = require("./control-numbers");
|
|
|
44
44
|
const edination_client_1 = require("./edination-client");
|
|
45
45
|
var control_numbers_2 = require("./control-numbers");
|
|
46
46
|
Object.defineProperty(exports, "ControlNumberError", { enumerable: true, get: function () { return control_numbers_2.ControlNumberError; } });
|
|
47
|
-
Object.defineProperty(exports, "getControlNumberPolicy", { enumerable: true, get: function () { return control_numbers_2.getControlNumberPolicy; } });
|
|
48
47
|
Object.defineProperty(exports, "getCurrentControlNumber", { enumerable: true, get: function () { return control_numbers_2.getCurrentControlNumber; } });
|
|
49
48
|
Object.defineProperty(exports, "getNextControlNumber", { enumerable: true, get: function () { return control_numbers_2.getNextControlNumber; } });
|
|
50
49
|
Object.defineProperty(exports, "isControlNumberError", { enumerable: true, get: function () { return control_numbers_2.isControlNumberError; } });
|
|
51
50
|
Object.defineProperty(exports, "setControlNumber", { enumerable: true, get: function () { return control_numbers_2.setControlNumber; } });
|
|
52
|
-
Object.defineProperty(exports, "setControlNumberPolicy", { enumerable: true, get: function () { return control_numbers_2.setControlNumberPolicy; } });
|
|
53
51
|
__exportStar(require("./edination-client/model"), exports);
|
|
54
52
|
class UnnboundError extends Error {
|
|
55
53
|
code;
|
|
@@ -120,12 +118,13 @@ class TemperEdiClient {
|
|
|
120
118
|
});
|
|
121
119
|
// Gateway normalises EdiFabric's camelCase to PascalCase before forwarding
|
|
122
120
|
const responseData = error.response?.data;
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
let details;
|
|
122
|
+
if (responseData instanceof Object &&
|
|
125
123
|
'Details' in responseData &&
|
|
126
|
-
Array.isArray(responseData.Details)
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
Array.isArray(responseData.Details) &&
|
|
125
|
+
responseData.Details.every((detail) => String(detail) === detail)) {
|
|
126
|
+
details = responseData.Details.map(String);
|
|
127
|
+
}
|
|
129
128
|
if (status === 400 && details?.length) {
|
|
130
129
|
throw new TemperEdiClientError({
|
|
131
130
|
message: `EDI validation failed: ${details.join('; ')}`,
|
|
@@ -145,6 +144,7 @@ class TemperEdiClient {
|
|
|
145
144
|
}
|
|
146
145
|
fromX12({ input }) {
|
|
147
146
|
return (0, unnbound_logger_sdk_1.startSpan)('X12 to JSON', () => {
|
|
147
|
+
// SAFETY: generated client accepts File; Buffer/Blob inputs are valid File-like bodies
|
|
148
148
|
return this.X12.x12ReadPost({ body: input })
|
|
149
149
|
.then(this.unwrap.bind(this))
|
|
150
150
|
.catch((error) => this.unwrapError(error, 'edi_read_error'));
|
|
@@ -152,7 +152,7 @@ class TemperEdiClient {
|
|
|
152
152
|
}
|
|
153
153
|
/**
|
|
154
154
|
* Stamp ISA13/GS06/ST02 control numbers on the interchange if not already set.
|
|
155
|
-
* Uses the
|
|
155
|
+
* Uses the centralized per-environment service for atomic partner sequencing.
|
|
156
156
|
* ST02 is always "0001" (one transaction per file per ticket).
|
|
157
157
|
* Zero-filled values from acknowledgment generation are placeholders, not manual overrides.
|
|
158
158
|
*/
|
|
@@ -201,11 +201,12 @@ class TemperEdiClient {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
async toX12({ input }) {
|
|
204
|
+
// Auto-stamp control numbers before writing X12
|
|
204
205
|
try {
|
|
205
206
|
await this.stampControlNumbers(input);
|
|
206
207
|
}
|
|
207
208
|
catch (error) {
|
|
208
|
-
//
|
|
209
|
+
// Workflows can still serialize documents whose control numbers were set manually.
|
|
209
210
|
unnbound_logger_sdk_1.logger.warn({ err: error }, '[EDI SDK] Failed to auto-stamp control numbers, proceeding without');
|
|
210
211
|
}
|
|
211
212
|
return (0, unnbound_logger_sdk_1.startSpan)('JSON to X12', () => {
|
package/examples/node-edi.ts
CHANGED
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.2.0-beta.
|
|
4
|
+
"version": "1.2.0-beta.4",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"author": "Unnbound Team",
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"axios": "1.18.0",
|
|
19
|
-
"unnbound-logger-sdk": "3.
|
|
19
|
+
"unnbound-logger-sdk": "3.1.1"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/jest": "^29.5.12",
|
|
23
23
|
"@types/node": "^24.12.2",
|
|
24
|
-
"vitest": "^4.0.15"
|
|
24
|
+
"vitest": "^4.0.15",
|
|
25
|
+
"zod": "3.25.76"
|
|
25
26
|
},
|
|
26
27
|
"files": [
|
|
27
28
|
"examples/*",
|
|
@@ -37,8 +38,8 @@
|
|
|
37
38
|
"build": "tsc",
|
|
38
39
|
"test": "vitest run src",
|
|
39
40
|
"typecheck": "tsc --noEmit",
|
|
40
|
-
"format": "
|
|
41
|
-
"format:check": "
|
|
41
|
+
"format": "oxfmt --write .",
|
|
42
|
+
"format:check": "oxfmt --check .",
|
|
42
43
|
"start:example": "tsx watch examples/node-edi.ts",
|
|
43
44
|
"version:bump": "npm version patch",
|
|
44
45
|
"release": "pnpm run build && pnpm publish --access public",
|