aves-sdk 1.5.1 → 1.7.0
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 +224 -0
- package/README.md +354 -195
- package/dist/index.d.mts +5002 -1087
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
- package/docs/guida-implementazione-booking-ops.md +0 -551
- package/docs/spike-api-aves-analisi.md +0 -196
package/README.md
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
# AVES SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Type-safe** - Full TypeScript support with inferred types
|
|
8
|
-
- **Runtime validation** - Input/output validation using Valibot
|
|
9
|
-
- **XML handling** - Automatic XML ↔ JSON conversion
|
|
10
|
-
- **Case transformation** - Automatic camelCase ↔ PascalCase conversion
|
|
11
|
-
- **Functional error handling** - Result pattern for type-safe error handling
|
|
3
|
+
Type-safe TypeScript SDK for the AVES XML REST API. Handles XML parsing, Valibot validation, camelCase ↔ PascalCase / `@` attrs, and returns `Result` instead of throwing.
|
|
12
4
|
|
|
13
5
|
## Installation
|
|
14
6
|
|
|
@@ -22,292 +14,459 @@ pnpm add aves-sdk
|
|
|
22
14
|
bun add aves-sdk
|
|
23
15
|
```
|
|
24
16
|
|
|
25
|
-
## Quick
|
|
17
|
+
## Quick start
|
|
26
18
|
|
|
27
19
|
```typescript
|
|
28
20
|
import { AvesClient } from 'aves-sdk';
|
|
29
21
|
|
|
30
|
-
const client = new AvesClient(
|
|
31
|
-
'https://api.example.com',
|
|
32
|
-
'
|
|
33
|
-
'TOKEN',
|
|
34
|
-
'02' //
|
|
35
|
-
|
|
22
|
+
const client = new AvesClient({
|
|
23
|
+
baseURL: 'https://api.example.com',
|
|
24
|
+
hostID: '000000', // 6 digits
|
|
25
|
+
xtoken: 'TOKEN',
|
|
26
|
+
languageCode: '02', // optional: 01=Italian, 02=English
|
|
27
|
+
timeoutMs: 30_000, // optional
|
|
28
|
+
});
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
const searchResult = await client.search({
|
|
30
|
+
const result = await client.search({
|
|
39
31
|
searchType: 'CODE',
|
|
40
32
|
recordCode: '508558',
|
|
41
33
|
});
|
|
42
34
|
|
|
43
|
-
if (
|
|
44
|
-
console.log(
|
|
35
|
+
if (result.success) {
|
|
36
|
+
console.log(result.data.masterRecordList);
|
|
45
37
|
} else {
|
|
46
|
-
console.error(
|
|
38
|
+
console.error(result.error.kind, result.error.message, result.error.code);
|
|
47
39
|
}
|
|
40
|
+
```
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
const upsertResult = await client.upsertRecord({
|
|
51
|
-
name: 'John Doe',
|
|
52
|
-
email: 'john@example.com',
|
|
53
|
-
address: '123 Main St',
|
|
54
|
-
zipCode: '12345',
|
|
55
|
-
cityName: 'New York',
|
|
56
|
-
stateCode: 'USA',
|
|
57
|
-
languageCode: '02',
|
|
58
|
-
// ... other fields
|
|
59
|
-
});
|
|
42
|
+
## Client shape
|
|
60
43
|
|
|
61
|
-
|
|
62
|
-
console.log(upsertResult.data.customerRecordRS?.customerRecordCode);
|
|
63
|
-
} else {
|
|
64
|
-
console.error(
|
|
65
|
-
upsertResult.error.errorCode,
|
|
66
|
-
upsertResult.error.errorDescription
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
```
|
|
44
|
+
`AvesClient` is a facade over domain clients. **Prefer namespaces.** Flat methods are auto-bound from domain prototypes (no hand-maintained forwarders).
|
|
70
45
|
|
|
71
|
-
|
|
46
|
+
```typescript
|
|
47
|
+
// canonical
|
|
48
|
+
await client.booking.createBooking(params);
|
|
49
|
+
await client.packages.searchPackages(params);
|
|
50
|
+
await client.master.search({ searchType: 'CODE', recordCode: '508558' });
|
|
51
|
+
|
|
52
|
+
// flat compat (same implementations)
|
|
53
|
+
await client.createBooking(params);
|
|
54
|
+
await client.searchPackages(params);
|
|
55
|
+
await client.search({ searchType: 'CODE', recordCode: '508558' });
|
|
56
|
+
```
|
|
72
57
|
|
|
73
|
-
|
|
58
|
+
| Namespace | Methods |
|
|
59
|
+
| --------- | ------- |
|
|
60
|
+
| `master` | `search`, `upsertRecord` |
|
|
61
|
+
| `booking` | `createBooking`, `modBookingServices`, `modBookingHeader`, `cancelBooking`, `setBookingStatus`, `setBookingServiceStatus`, `insertFilePaymentList`, `searchBookingFiles` |
|
|
62
|
+
| `packages` | `searchPackages`, `searchTopServices`, `getPackageDetail`, `commitPackage` |
|
|
74
63
|
|
|
75
|
-
|
|
64
|
+
### Constructor
|
|
76
65
|
|
|
77
66
|
```typescript
|
|
78
|
-
new AvesClient(
|
|
67
|
+
new AvesClient(options: AvesClientOptions, deps?: AvesClientDeps)
|
|
79
68
|
```
|
|
80
69
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
| Option | Description |
|
|
71
|
+
| ------ | ----------- |
|
|
72
|
+
| `baseURL` | AVES API base URL |
|
|
73
|
+
| `hostID` | 6-digit host id |
|
|
74
|
+
| `xtoken` | Auth token |
|
|
75
|
+
| `languageCode` | Optional 2-digit language code |
|
|
76
|
+
| `timeoutMs` | Optional request timeout (default 30000) |
|
|
77
|
+
|
|
78
|
+
Optional DI (tests / custom transport):
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import {
|
|
82
|
+
AvesClient,
|
|
83
|
+
AvesTransport,
|
|
84
|
+
BookingClient,
|
|
85
|
+
} from 'aves-sdk';
|
|
86
|
+
|
|
87
|
+
const transport = new AvesTransport(options);
|
|
88
|
+
new AvesClient(options, {
|
|
89
|
+
transport,
|
|
90
|
+
booking: new BookingClient(transport),
|
|
91
|
+
});
|
|
92
|
+
```
|
|
85
93
|
|
|
86
|
-
|
|
94
|
+
### Result type
|
|
87
95
|
|
|
88
|
-
|
|
96
|
+
All methods return `Result<T, AvesError>` — they do **not** throw.
|
|
89
97
|
|
|
90
|
-
|
|
98
|
+
```typescript
|
|
99
|
+
type Result<T, E> =
|
|
100
|
+
| { success: true; data: T }
|
|
101
|
+
| { success: false; error: E };
|
|
102
|
+
```
|
|
91
103
|
|
|
92
|
-
|
|
104
|
+
---
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
- **`'NAME'`** - Requires `name`, optionally `city`
|
|
96
|
-
- **`'VATCODE'`** - Requires `vatCode`, optionally `phoneNumber`
|
|
97
|
-
- **`'ZONE'`** - Requires `zipCode` and `countyCode`, optionally `city`
|
|
98
|
-
- **`'CATEGORY'`** - Requires `categoryCode`
|
|
99
|
-
- **`'EMAIL'`** - Requires `email`
|
|
100
|
-
- **`'LASTMODDATE'`** - Requires `lastModificationDate` with `minDate` and `maxDate`
|
|
101
|
-
- **`'SEARCH_FIELD'`** - Requires `searchFieldValue`
|
|
102
|
-
- **`'EXTERNAL_REF_CODE'`** - Requires `searchFieldValue`
|
|
106
|
+
## Master records
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
### `search(params)` / `client.master.search`
|
|
105
109
|
|
|
106
|
-
|
|
110
|
+
| `searchType` | Required fields |
|
|
111
|
+
| ------------ | --------------- |
|
|
112
|
+
| `'CODE'` | `recordCode` |
|
|
113
|
+
| `'NAME'` | `name` (+ optional `city`) |
|
|
114
|
+
| `'VATCODE'` | `vatCode` (+ optional `phoneNumber`) |
|
|
115
|
+
| `'ZONE'` | `zipCode`, `countyCode` (+ optional `city`) |
|
|
116
|
+
| `'CATEGORY'` | `categoryCode` |
|
|
117
|
+
| `'EMAIL'` | `email` |
|
|
118
|
+
| `'LASTMODDATE'` | `lastModificationDate: { minDate, maxDate }` |
|
|
119
|
+
| `'SEARCH_FIELD'` / `'EXTERNAL_REF_CODE'` | `searchFieldValue` |
|
|
107
120
|
|
|
108
121
|
```typescript
|
|
109
|
-
|
|
110
|
-
const byCode = await client.search({
|
|
122
|
+
const byCode = await client.master.search({
|
|
111
123
|
searchType: 'CODE',
|
|
112
124
|
recordCode: '508558',
|
|
113
125
|
});
|
|
114
126
|
|
|
115
|
-
if (byCode.success) {
|
|
116
|
-
console.log('Found records:', byCode.data.masterRecordList);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Search by email
|
|
120
127
|
const byEmail = await client.search({
|
|
121
128
|
searchType: 'EMAIL',
|
|
122
129
|
email: 'user@example.com',
|
|
123
130
|
});
|
|
131
|
+
```
|
|
124
132
|
|
|
125
|
-
|
|
126
|
-
console.log('Found:', byEmail.data.masterRecordList);
|
|
127
|
-
} else {
|
|
128
|
-
console.error('Error:', byEmail.error.message);
|
|
129
|
-
}
|
|
133
|
+
### `upsertRecord(record)` / `client.master.upsertRecord`
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
`insertCriteria`: `'S'` always insert · `'N'` skip if exists · `'T'` update all (default) · `'M'` secondary fields only.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const result = await client.upsertRecord({
|
|
139
|
+
name: 'Jane Smith',
|
|
140
|
+
email: 'jane@example.com',
|
|
141
|
+
languageCode: '02',
|
|
142
|
+
insertCriteria: 'T',
|
|
136
143
|
});
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
if (result.success) {
|
|
146
|
+
console.log(result.data.masterRecordDetail?.recordCode);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Booking
|
|
153
|
+
|
|
154
|
+
`*List` request fields are **flat arrays of Detail objects**. The SDK wraps them to AVES List/Detail XML.
|
|
155
|
+
|
|
156
|
+
| Method | Purpose |
|
|
157
|
+
| ------ | ------- |
|
|
158
|
+
| `createBooking` | Create booking file |
|
|
159
|
+
| `modBookingServices` | Add/replace services, assign package, delete/nullify lines |
|
|
160
|
+
| `modBookingHeader` | Header only (pax, notes, billing) — no costs |
|
|
161
|
+
| `cancelBooking` | Delete booking file |
|
|
162
|
+
| `setBookingStatus` | Change file status (`CANCELED` / `NULLIFIED` / …) |
|
|
163
|
+
| `setBookingServiceStatus` | Nullify a single service line |
|
|
164
|
+
| `insertFilePaymentList` | Register payments |
|
|
165
|
+
| `searchBookingFiles` | Search practices (`FILE_CODE`, `PAX_NAME`, `PACKAGE_CODE`, `OTHER`) |
|
|
166
|
+
|
|
167
|
+
### Create
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const created = await client.booking.createBooking({
|
|
171
|
+
customerDetail: { recordCode: '138311' },
|
|
172
|
+
bookingFileStatus: { value: 'QUOTATION' },
|
|
173
|
+
startDate: '2015-01-22T00:00:00',
|
|
174
|
+
endDate: '2015-01-25T00:00:00',
|
|
175
|
+
selectedServiceList: [
|
|
176
|
+
{
|
|
177
|
+
sCode: 'HT00110840',
|
|
178
|
+
ssCode: 'DL',
|
|
179
|
+
avesServiceType: 'TOP',
|
|
180
|
+
toServiceType: 'RESIDENCE',
|
|
181
|
+
startDate: '2015-01-22T00:00:00',
|
|
182
|
+
endDate: '2015-01-25T00:00:00',
|
|
183
|
+
qty: '1',
|
|
184
|
+
pax: '2',
|
|
185
|
+
paxAssociated: [],
|
|
186
|
+
avesSession: '1',
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
passengerList: [
|
|
190
|
+
{
|
|
191
|
+
rph: '001',
|
|
192
|
+
roomRph: '001',
|
|
193
|
+
name: 'ADULTI 001',
|
|
194
|
+
categoryCode: 'AD', // required on create
|
|
195
|
+
sex: 'M',
|
|
196
|
+
},
|
|
197
|
+
],
|
|
144
198
|
});
|
|
145
199
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
200
|
+
if (created.success) {
|
|
201
|
+
console.log(created.data.bookingFileDetail?.bookingFileCode);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Modify services / package / overwrite lines
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
await client.booking.modBookingServices({
|
|
209
|
+
customerRecordCode: '138311',
|
|
210
|
+
bookingFileCode: '14/036654',
|
|
211
|
+
selectedPackageDetail: {
|
|
212
|
+
pCode: '2014MDE0000010',
|
|
213
|
+
startDate: '2015-01-22T00:00:00',
|
|
214
|
+
endDate: '2015-01-25T00:00:00',
|
|
152
215
|
},
|
|
216
|
+
cancellableBookedServiceList: [
|
|
217
|
+
{
|
|
218
|
+
cancelOperationType: 'DELETE', // or 'NULLIFY'
|
|
219
|
+
serviceRefType: 'RPH',
|
|
220
|
+
serviceRefValue: '001',
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
selectedServiceList: [
|
|
224
|
+
{
|
|
225
|
+
sCode: 'HT00110840',
|
|
226
|
+
avesServiceType: 'TOP',
|
|
227
|
+
toServiceType: 'RESIDENCE',
|
|
228
|
+
startDate: '2015-01-22T00:00:00',
|
|
229
|
+
endDate: '2015-01-25T00:00:00',
|
|
230
|
+
qty: '1',
|
|
231
|
+
pax: '2',
|
|
232
|
+
avesSession: '1',
|
|
233
|
+
bookedServiceRef: '001',
|
|
234
|
+
serviceFare: { currencyCode: 'EUR', cost: '100.00', price: '120.00' },
|
|
235
|
+
},
|
|
236
|
+
],
|
|
153
237
|
});
|
|
154
238
|
```
|
|
155
239
|
|
|
156
|
-
|
|
240
|
+
### Payments
|
|
157
241
|
|
|
158
|
-
|
|
242
|
+
Requires `bookingFileCode` **or** `bookingFileRefCode`.
|
|
243
|
+
|
|
244
|
+
`paymentUser` is serialized as an XML **attribute** on `FilePaymentListRQ` (`PaymentUser="…"`), not as a child element.
|
|
159
245
|
|
|
160
246
|
```typescript
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
languageCode: string, // Required
|
|
175
|
-
categoryCode: string,
|
|
176
|
-
recordCode: string, // Optional: 6-digit code
|
|
177
|
-
insertCriteria: 'S' | 'N' | 'T' | 'M', // Optional: defaults to 'T'
|
|
178
|
-
// ... see types for full list
|
|
247
|
+
await client.booking.insertFilePaymentList({
|
|
248
|
+
bookingFileCode: '18/000172',
|
|
249
|
+
paymentUser: 'MLDN',
|
|
250
|
+
enableMultiplePayments: true,
|
|
251
|
+
operationType: 'AbsoluteAmountsInsertion',
|
|
252
|
+
filePaymentList: [
|
|
253
|
+
{
|
|
254
|
+
paymentDate: '2018-09-08',
|
|
255
|
+
paymentNote: 'INCASSO',
|
|
256
|
+
amount: '100.00',
|
|
257
|
+
paymentType: 'B', // C cash | B bank | R card | …
|
|
258
|
+
},
|
|
259
|
+
],
|
|
179
260
|
});
|
|
261
|
+
```
|
|
180
262
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
263
|
+
### Header, status, cancel, search practices
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
await client.booking.modBookingHeader({
|
|
267
|
+
bookingFileCode: '14/000043',
|
|
268
|
+
bookingFileStartDate: '2014-04-28',
|
|
269
|
+
customerRecordCode: '103737',
|
|
270
|
+
passengerList: [{ rph: '001', name: 'ADULTI 001', sex: 'M' }],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
await client.booking.setBookingStatus({
|
|
274
|
+
customerRecordCode: '000170',
|
|
275
|
+
bookingFileCode: '14/000081',
|
|
276
|
+
fileStatus: { value: 'CANCELED' },
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
await client.booking.setBookingServiceStatus({
|
|
280
|
+
customerRecordCode: '000001',
|
|
281
|
+
bookingFileCode: '18/000252',
|
|
282
|
+
bookingServiceRef: '002',
|
|
283
|
+
bookingFileServiceStatus: 'NULLIFIED',
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
await client.booking.cancelBooking({
|
|
287
|
+
bookingFileCode: '14/000081',
|
|
288
|
+
customerRecordCode: '000170',
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
await client.booking.searchBookingFiles({
|
|
292
|
+
searchType: 'PACKAGE_CODE',
|
|
293
|
+
customerRecordCode: '138311',
|
|
294
|
+
packageCode: '2014MDE0000010',
|
|
295
|
+
});
|
|
187
296
|
```
|
|
188
297
|
|
|
189
|
-
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Package / Program catalog
|
|
190
301
|
|
|
191
|
-
|
|
192
|
-
- `'N'` - If record exists, do not update
|
|
193
|
-
- `'T'` - If record exists, update all fields (default)
|
|
194
|
-
- `'M'` - If record exists, update only secondary fields
|
|
302
|
+
There is **no** Create Package API in AVES XML 1.8.0. Search, detail, and commit (publish) are supported.
|
|
195
303
|
|
|
196
|
-
|
|
304
|
+
| Method | Purpose |
|
|
305
|
+
| ------ | ------- |
|
|
306
|
+
| `searchPackages` | Search programs (`avesSearchType`: `PACKAGE` / `PROGRAM`) |
|
|
307
|
+
| `searchTopServices` | Search TOP services (`avesSearchType`: `SERVICE`) |
|
|
308
|
+
| `getPackageDetail` | Package base info + service list |
|
|
309
|
+
| `commitPackage` | Publish existing package (`packageCode` only) |
|
|
197
310
|
|
|
198
311
|
```typescript
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
312
|
+
const packages = await client.packages.searchPackages({
|
|
313
|
+
baseSearch: {
|
|
314
|
+
customerRecordCode: '138311',
|
|
315
|
+
languageCode: '01',
|
|
316
|
+
currencyCode: 'EUR',
|
|
317
|
+
startDate: '2014-12-27T00:00:00',
|
|
318
|
+
endDate: '2015-01-03T00:00:00',
|
|
319
|
+
passengerList: [
|
|
320
|
+
{
|
|
321
|
+
rph: '001',
|
|
322
|
+
roomRph: '001',
|
|
323
|
+
name: 'ADULTI 001',
|
|
324
|
+
categoryCode: 'AD',
|
|
325
|
+
sex: 'M',
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
avesSearchType: 'PACKAGE',
|
|
330
|
+
paxQty: '1',
|
|
331
|
+
paxQtyCriteria: 'GREATER_OR_EQUAL',
|
|
332
|
+
servOrPackCode: '2014MDE0000010',
|
|
209
333
|
});
|
|
210
334
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
name: 'John Doe',
|
|
220
|
-
insertCriteria: 'S', // Always insert new
|
|
221
|
-
email: 'john@example.com',
|
|
222
|
-
languageCode: '02',
|
|
223
|
-
// ... other fields
|
|
335
|
+
const detail = await client.packages.getPackageDetail({
|
|
336
|
+
customerRecordCode: '001692',
|
|
337
|
+
packageCode: '2015F042',
|
|
338
|
+
startDate: '2015-05-02T00:00:00',
|
|
339
|
+
endDate: '2015-05-05T00:00:00',
|
|
340
|
+
selectedServiceList: [
|
|
341
|
+
{ serviceCode: 'PFRM04 PAR', packageRow: '01' },
|
|
342
|
+
],
|
|
224
343
|
});
|
|
225
344
|
|
|
226
|
-
|
|
227
|
-
console.log('Created:', newRecord.data.customerRecordRS?.customerRecordCode);
|
|
228
|
-
}
|
|
345
|
+
await client.packages.commitPackage({ packageCode: '14/PACKAGE001' });
|
|
229
346
|
```
|
|
230
347
|
|
|
231
|
-
|
|
348
|
+
---
|
|
232
349
|
|
|
233
|
-
|
|
350
|
+
## Errors
|
|
234
351
|
|
|
235
352
|
```typescript
|
|
236
|
-
import { AvesClient, AvesError
|
|
353
|
+
import { AvesClient, AvesError } from 'aves-sdk';
|
|
237
354
|
|
|
238
355
|
const result = await client.search({
|
|
239
356
|
searchType: 'CODE',
|
|
240
357
|
recordCode: '508558',
|
|
241
358
|
});
|
|
242
359
|
|
|
243
|
-
if (result.success) {
|
|
244
|
-
//
|
|
245
|
-
console.
|
|
246
|
-
} else {
|
|
247
|
-
// TypeScript knows result.error is AvesError
|
|
248
|
-
console.error('Status:', result.error.status); // 'ERROR' | 'TIMEOUT' | 'WARNING'
|
|
249
|
-
console.error('Error Code:', result.error.errorCode);
|
|
250
|
-
console.error('Description:', result.error.errorDescription);
|
|
360
|
+
if (!result.success) {
|
|
361
|
+
// kind: 'validation' | 'api' | 'unknown'
|
|
362
|
+
console.error(result.error.kind, result.error.message, result.error.code);
|
|
251
363
|
}
|
|
252
364
|
```
|
|
253
365
|
|
|
254
|
-
|
|
366
|
+
---
|
|
255
367
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
368
|
+
## Case transformation & wire shapes
|
|
369
|
+
|
|
370
|
+
- **Input**: camelCase (`recordCode`, `selectedServiceList`, `paymentUser`)
|
|
371
|
+
- **Wire**: PascalCase keys; `@`-prefixed keys become XML attributes
|
|
372
|
+
- **Output**: camelCase again
|
|
373
|
+
|
|
374
|
+
You do not handle XML attribute prefixes yourself. Attr vs element is decided by a **per-request `WireShape`** (`src/utils/wire-shapes.ts`), not a global field set.
|
|
375
|
+
|
|
376
|
+
### Pipeline
|
|
377
|
+
|
|
378
|
+
One outbound path for all ops:
|
|
379
|
+
|
|
380
|
+
1. Valibot validates camelCase input
|
|
381
|
+
2. `createApiSchema(schema, shape, wrap?)` → `toWireBody` (optional list wrap + empty `paxAssociated` normalize + `camelToPascalKeys`)
|
|
382
|
+
3. `AvesTransport.invokeOp` adds `RqHeader`, optional `bodyKey` nest, POSTs XML
|
|
259
383
|
|
|
260
|
-
|
|
384
|
+
Element-only roots use `elementOnlyWire` (`{}`). Master upsert nests under `bodyKey: "MasterRecordDetail"`; master search and booking spread fields at the RQ root.
|
|
261
385
|
|
|
262
|
-
|
|
263
|
-
- **No exceptions**: All errors are explicit in the return type
|
|
264
|
-
- **Functional style**: Easier to compose and chain operations
|
|
386
|
+
### Request shapes (one family per op)
|
|
265
387
|
|
|
266
|
-
|
|
388
|
+
| Shape | Used by |
|
|
389
|
+
| ----- | ------- |
|
|
390
|
+
| `masterRecordWire` / `searchMasterWire` | Upsert / Search master |
|
|
391
|
+
| `bookingFileWire` | CreateBooking, ModFileServices, ModFileHeader |
|
|
392
|
+
| `filePaymentListRequestWire` | InsertFilePaymentList (`paymentUser` root attr) |
|
|
393
|
+
| `searchFileWire` | SearchBookingFiles (date ranges / insurance attrs) |
|
|
394
|
+
| `setFileStatusWire` | SetBookingFileStatus |
|
|
395
|
+
| `packageDetailRequestWire` | GetPackageDetail |
|
|
396
|
+
| `baseSearchWire` + `avesSearchWire` | SearchAvesPackages / SearchTopServices |
|
|
397
|
+
| `elementOnlyWire` | CancelFile, SetFileServiceStatus, CommitPackage |
|
|
267
398
|
|
|
268
|
-
|
|
399
|
+
Editing CreateBooking shapes does not affect SearchFile or AvesSearch.
|
|
269
400
|
|
|
270
|
-
|
|
271
|
-
- **API**: Automatically converted to PascalCase with XML attributes (e.g., `@RecordCode`, `ZipCode`)
|
|
272
|
-
- **Output**: Automatically converted back to camelCase
|
|
401
|
+
---
|
|
273
402
|
|
|
274
|
-
|
|
403
|
+
## Architecture
|
|
275
404
|
|
|
276
|
-
|
|
405
|
+
`AvesClient` (`src/client.ts`) is a thin facade over DI-friendly domain clients:
|
|
277
406
|
|
|
278
|
-
|
|
407
|
+
| Module | Role |
|
|
408
|
+
| ------ | ---- |
|
|
409
|
+
| `AvesTransport` | HTTP + XML encode/decode + `invokeOp` (optional `bodyKey`) |
|
|
410
|
+
| `MasterRecordsClient` | search / upsert |
|
|
411
|
+
| `BookingClient` | booking file ops + search practices |
|
|
412
|
+
| `PackageCatalogClient` | package/program catalog |
|
|
413
|
+
| `client/types.ts` | `AvesClientDeps` / `AvesClientFlat` |
|
|
414
|
+
| `client/flat-aliases.ts` | bind flat compat methods from domain prototypes |
|
|
415
|
+
| `src/xml/` | XML root helpers + JSON ↔ XML |
|
|
416
|
+
|
|
417
|
+
Flat aliases (`client.createBooking`, …) are attached in the constructor — add a domain method once and both surfaces work.
|
|
418
|
+
|
|
419
|
+
Outbound path: validate → `createApiSchema` / `toWireBody` → `invokeOp` → XML POST.
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Types
|
|
279
424
|
|
|
280
425
|
```typescript
|
|
281
426
|
import type {
|
|
427
|
+
AvesClientOptions,
|
|
428
|
+
AvesClientDeps,
|
|
429
|
+
SearchMasterRecord,
|
|
282
430
|
SearchMasterRecordRS,
|
|
283
|
-
ManageMasterRecordRS,
|
|
284
431
|
MasterRecordDetail,
|
|
432
|
+
ManageMasterRecordRS,
|
|
433
|
+
BookingFileRQ,
|
|
434
|
+
BookingFileRS,
|
|
435
|
+
ModFileServicesRQ,
|
|
436
|
+
ModFileHeaderRQ,
|
|
437
|
+
CancelFileRQ,
|
|
438
|
+
SetFileStatusRQ,
|
|
439
|
+
SetFileServiceStatusRQ,
|
|
440
|
+
FilePaymentListRQ,
|
|
441
|
+
BookingStatusOnlyRS,
|
|
442
|
+
SearchBookingFileRQ,
|
|
443
|
+
SearchBookingFileRS,
|
|
444
|
+
AvesSearchRQ,
|
|
445
|
+
SearchPackageRS,
|
|
446
|
+
SearchServicesRS,
|
|
447
|
+
PackageDetailRQ,
|
|
448
|
+
PackageDetailRS,
|
|
449
|
+
CommitPackageRQ,
|
|
450
|
+
CommitPackageRS,
|
|
285
451
|
} from 'aves-sdk';
|
|
286
452
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
453
|
+
import {
|
|
454
|
+
AvesClient,
|
|
455
|
+
AvesTransport,
|
|
456
|
+
MasterRecordsClient,
|
|
457
|
+
BookingClient,
|
|
458
|
+
PackageCatalogClient,
|
|
459
|
+
AvesError,
|
|
460
|
+
} from 'aves-sdk';
|
|
291
461
|
```
|
|
292
462
|
|
|
293
|
-
## Validation
|
|
294
|
-
|
|
295
|
-
The SDK validates all inputs and outputs using Valibot schemas:
|
|
296
|
-
|
|
297
|
-
- **HostID**: Must be exactly 6 digits
|
|
298
|
-
- **RecordCode**: Must be exactly 6 characters
|
|
299
|
-
- **LanguageCode**: Must be exactly 2 digits (01=Italian, 02=English, etc.)
|
|
300
|
-
- **Search parameters**: Required fields are enforced based on `searchType` using discriminated unions
|
|
301
|
-
- All other fields follow the AVES API specification
|
|
302
|
-
|
|
303
|
-
Invalid data will return an error `Result` before making the API request. All validation happens on camelCase input, making it easy to use. TypeScript will also provide type checking and autocomplete based on the selected `searchType`.
|
|
304
|
-
|
|
305
463
|
## License
|
|
306
464
|
|
|
307
465
|
MIT
|
|
308
466
|
|
|
309
467
|
## Links
|
|
310
468
|
|
|
469
|
+
- [Changelog](./CHANGELOG.md)
|
|
311
470
|
- [GitHub Repository](https://github.com/simoneguglielmi/aves-sdk)
|
|
312
471
|
- [NPM Package](https://npmjs.com/package/aves-sdk)
|
|
313
472
|
- [Issue Tracker](https://github.com/simoneguglielmi/aves-sdk/issues)
|