aves-sdk 1.6.0 → 1.8.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AVES SDK
2
2
 
3
- Type-safe TypeScript SDK for the AVES XML REST API. Handles XML parsing, Valibot validation, camelCase ↔ PascalCase, and returns `Result` instead of throwing.
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.
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,7 +14,7 @@ pnpm add aves-sdk
14
14
  bun add aves-sdk
15
15
  ```
16
16
 
17
- ## Quick Start
17
+ ## Quick start
18
18
 
19
19
  ```typescript
20
20
  import { AvesClient } from 'aves-sdk';
@@ -27,67 +27,99 @@ const client = new AvesClient({
27
27
  timeoutMs: 30_000, // optional
28
28
  });
29
29
 
30
- const searchResult = await client.search({
30
+ const result = await client.search({
31
31
  searchType: 'CODE',
32
32
  recordCode: '508558',
33
33
  });
34
34
 
35
- if (searchResult.success) {
36
- console.log(searchResult.data.masterRecordList);
35
+ if (result.success) {
36
+ console.log(result.data.masterRecordList);
37
37
  } else {
38
- console.error(
39
- searchResult.error.kind,
40
- searchResult.error.message,
41
- searchResult.error.code,
42
- );
38
+ console.error(result.error.kind, result.error.message, result.error.code);
43
39
  }
44
40
  ```
45
41
 
46
- ## API Reference
42
+ ## Client shape
47
43
 
48
- ### `AvesClient`
44
+ `AvesClient` is a facade over domain clients. **Prefer namespaces.** Flat methods are auto-bound from domain prototypes (no hand-maintained forwarders).
49
45
 
50
- #### Constructor
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
+ ```
57
+
58
+ | Namespace | Methods |
59
+ | --------- | ------- |
60
+ | `master` | `search`, `upsertRecord` |
61
+ | `booking` | `createBooking`, `modBookingServices`, `modBookingHeader`, `cancelBooking`, `setBookingStatus`, `setBookingServiceStatus`, `insertFilePaymentList`, `searchBookingFiles` |
62
+ | `packages` | `searchPackages`, `searchTopServices`, `getPackageDetail`, `commitPackage` |
63
+
64
+ ### Constructor
51
65
 
52
66
  ```typescript
53
- new AvesClient(options: AvesClientOptions)
67
+ new AvesClient(options: AvesClientOptions, deps?: AvesClientDeps)
54
68
  ```
55
69
 
56
- | Option | Description |
57
- | -------------- | ------------------------------ |
58
- | `baseURL` | AVES API base URL |
59
- | `hostID` | 6-digit host id |
60
- | `xtoken` | Auth token |
70
+ | Option | Description |
71
+ | ------ | ----------- |
72
+ | `baseURL` | AVES API base URL |
73
+ | `hostID` | 6-digit host id |
74
+ | `xtoken` | Auth token |
61
75
  | `languageCode` | Optional 2-digit language code |
62
- | `timeoutMs` | Optional request timeout |
76
+ | `timeoutMs` | Optional request timeout (default 30000) |
63
77
 
64
- All methods return `Result<T, AvesError>`:
78
+ Optional DI (tests / custom transport):
65
79
 
66
80
  ```typescript
67
- type Result<T, E> = { success: true; data: T } | { success: false; error: E };
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
+ });
68
92
  ```
69
93
 
70
- ---
94
+ ### Result type
95
+
96
+ All methods return `Result<T, AvesError>` — they do **not** throw.
97
+
98
+ ```typescript
99
+ type Result<T, E> =
100
+ | { success: true; data: T }
101
+ | { success: false; error: E };
102
+ ```
71
103
 
72
- ### Master records
104
+ ---
73
105
 
74
- #### `search(params)`
106
+ ## Master records
75
107
 
76
- Search by `searchType`. TypeScript enforces the required fields per variant.
108
+ ### `search(params)` / `client.master.search`
77
109
 
78
- | `searchType` | Required fields |
79
- | ---------------------------------------- | -------------------------------------------- |
80
- | `'CODE'` | `recordCode` |
81
- | `'NAME'` | `name` (+ optional `city`) |
82
- | `'VATCODE'` | `vatCode` (+ optional `phoneNumber`) |
83
- | `'ZONE'` | `zipCode`, `countyCode` (+ optional `city`) |
84
- | `'CATEGORY'` | `categoryCode` |
85
- | `'EMAIL'` | `email` |
86
- | `'LASTMODDATE'` | `lastModificationDate: { minDate, maxDate }` |
87
- | `'SEARCH_FIELD'` / `'EXTERNAL_REF_CODE'` | `searchFieldValue` |
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` |
88
120
 
89
121
  ```typescript
90
- const byCode = await client.search({
122
+ const byCode = await client.master.search({
91
123
  searchType: 'CODE',
92
124
  recordCode: '508558',
93
125
  });
@@ -98,23 +130,14 @@ const byEmail = await client.search({
98
130
  });
99
131
  ```
100
132
 
101
- #### `upsertRecord(record)`
102
-
103
- Insert or update a master record. `insertCriteria` defaults to `'T'`.
133
+ ### `upsertRecord(record)` / `client.master.upsertRecord`
104
134
 
105
- - `'S'` — always insert
106
- - `'N'` — do not update if exists
107
- - `'T'` — update all fields if exists (default)
108
- - `'M'` — update secondary fields only
135
+ `insertCriteria`: `'S'` always insert · `'N'` skip if exists · `'T'` update all (default) · `'M'` secondary fields only.
109
136
 
110
137
  ```typescript
111
138
  const result = await client.upsertRecord({
112
139
  name: 'Jane Smith',
113
140
  email: 'jane@example.com',
114
- address: '456 Oak Ave',
115
- zipCode: '67890',
116
- cityName: 'Los Angeles',
117
- stateCode: 'USA',
118
141
  languageCode: '02',
119
142
  insertCriteria: 'T',
120
143
  });
@@ -126,24 +149,25 @@ if (result.success) {
126
149
 
127
150
  ---
128
151
 
129
- ### Booking
152
+ ## Booking
130
153
 
131
- `*List` request fields are **flat arrays of Detail objects**. The SDK wraps them to AVES List/Detail XML automatically.
154
+ `*List` request fields are **flat arrays of Detail objects**. The SDK wraps them to AVES List/Detail XML.
132
155
 
133
- | Method | Purpose |
134
- | ------------------------- | ---------------------------------------------------------- |
135
- | `createBooking` | Create booking file |
136
- | `modBookingServices` | Add/replace services, assign package, delete/nullify lines |
137
- | `modBookingHeader` | Header only (pax, notes, billing) — no costs |
138
- | `cancelBooking` | Delete booking file |
139
- | `setBookingStatus` | Change file status (`CANCELED` / `NULLIFIED` / …) |
140
- | `setBookingServiceStatus` | Nullify a single service line |
141
- | `insertFilePaymentList` | Register payments on a booking |
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`) |
142
166
 
143
- #### `createBooking(params)`
167
+ ### Create
144
168
 
145
169
  ```typescript
146
- const created = await client.createBooking({
170
+ const created = await client.booking.createBooking({
147
171
  customerDetail: { recordCode: '138311' },
148
172
  bookingFileStatus: { value: 'QUOTATION' },
149
173
  startDate: '2015-01-22T00:00:00',
@@ -178,10 +202,10 @@ if (created.success) {
178
202
  }
179
203
  ```
180
204
 
181
- #### `modBookingServices(params)`
205
+ ### Modify services / package / overwrite lines
182
206
 
183
207
  ```typescript
184
- const result = await client.modBookingServices({
208
+ await client.booking.modBookingServices({
185
209
  customerRecordCode: '138311',
186
210
  bookingFileCode: '14/036654',
187
211
  selectedPackageDetail: {
@@ -213,13 +237,16 @@ const result = await client.modBookingServices({
213
237
  });
214
238
  ```
215
239
 
216
- #### `insertFilePaymentList(params)`
240
+ ### Payments
217
241
 
218
242
  Requires `bookingFileCode` **or** `bookingFileRefCode`.
219
243
 
244
+ `paymentUser` is serialized as an XML **attribute** on `FilePaymentListRQ` (`PaymentUser="…"`), not as a child element.
245
+
220
246
  ```typescript
221
- const paid = await client.insertFilePaymentList({
247
+ await client.booking.insertFilePaymentList({
222
248
  bookingFileCode: '18/000172',
249
+ paymentUser: 'MLDN',
223
250
  enableMultiplePayments: true,
224
251
  operationType: 'AbsoluteAmountsInsertion',
225
252
  filePaymentList: [
@@ -233,38 +260,91 @@ const paid = await client.insertFilePaymentList({
233
260
  });
234
261
  ```
235
262
 
236
- #### Other booking ops
263
+ ### Header, status, cancel, search practices
237
264
 
238
265
  ```typescript
239
- await client.modBookingHeader({
266
+ await client.booking.modBookingHeader({
240
267
  bookingFileCode: '14/000043',
241
268
  bookingFileStartDate: '2014-04-28',
242
269
  customerRecordCode: '103737',
243
270
  passengerList: [{ rph: '001', name: 'ADULTI 001', sex: 'M' }],
244
271
  });
245
272
 
246
- await client.setBookingStatus({
273
+ await client.booking.setBookingStatus({
247
274
  customerRecordCode: '000170',
248
275
  bookingFileCode: '14/000081',
249
276
  fileStatus: { value: 'CANCELED' },
250
277
  });
251
278
 
252
- await client.setBookingServiceStatus({
279
+ await client.booking.setBookingServiceStatus({
253
280
  customerRecordCode: '000001',
254
281
  bookingFileCode: '18/000252',
255
282
  bookingServiceRef: '002',
256
283
  bookingFileServiceStatus: 'NULLIFIED',
257
284
  });
258
285
 
259
- await client.cancelBooking({
286
+ await client.booking.cancelBooking({
260
287
  bookingFileCode: '14/000081',
261
288
  customerRecordCode: '000170',
262
289
  });
290
+
291
+ await client.booking.searchBookingFiles({
292
+ searchType: 'PACKAGE_CODE',
293
+ customerRecordCode: '138311',
294
+ packageCode: '2014MDE0000010',
295
+ });
296
+ ```
297
+
298
+ ---
299
+
300
+ ## Package / Program catalog
301
+
302
+ There is **no** Create Package API in AVES XML 1.8.0. Search, detail, and commit (publish) are supported.
303
+
304
+ | Method | Purpose |
305
+ | ------ | ------- |
306
+ | `searchPackages` | Search programs (defaults `avesSearchType: 'PACKAGE'`) |
307
+ | `searchTopServices` | Search TOP services (defaults `avesSearchType: 'SERVICE'`) |
308
+ | `getPackageDetail` | Package base info + service list |
309
+ | `commitPackage` | Publish existing package (`packageCode` only) |
310
+
311
+ `paxQty` defaults to `passengerList.length`; `paxQtyCriteria` defaults to `GREATER_OR_EQUAL`. `languageCode` can be omitted when set on `AvesClient` options.
312
+
313
+ ```typescript
314
+ const packages = await client.packages.searchPackages({
315
+ customerRecordCode: '138311',
316
+ languageCode: '01',
317
+ currencyCode: 'EUR',
318
+ startDate: '2014-12-27T00:00:00',
319
+ endDate: '2015-01-03T00:00:00',
320
+ passengerList: [
321
+ {
322
+ rph: '001',
323
+ roomRph: '001',
324
+ name: 'ADULTI 001',
325
+ categoryCode: 'AD',
326
+ sex: 'M',
327
+ },
328
+ ],
329
+ servOrPackCode: '2014MDE0000010',
330
+ });
331
+
332
+ const detail = await client.packages.getPackageDetail({
333
+ customerRecordCode: '001692',
334
+ packageCode: '2015F042',
335
+ startDate: '2015-05-02T00:00:00',
336
+ endDate: '2015-05-05T00:00:00',
337
+ selectedServiceList: [
338
+ { serviceCode: 'PFRM04 PAR', packageRow: '01' },
339
+ ],
340
+ });
341
+
342
+ await client.packages.commitPackage({ packageCode: '14/PACKAGE001' });
263
343
  ```
264
344
 
265
345
  ---
266
346
 
267
- ## Error Handling
347
+ ## Errors
268
348
 
269
349
  ```typescript
270
350
  import { AvesClient, AvesError } from 'aves-sdk';
@@ -275,38 +355,100 @@ const result = await client.search({
275
355
  });
276
356
 
277
357
  if (!result.success) {
278
- // AvesError: kind ('validation' | 'api' | 'unknown'), message, status?, code?
358
+ // kind: 'validation' | 'api' | 'unknown'
279
359
  console.error(result.error.kind, result.error.message, result.error.code);
280
360
  }
281
361
  ```
282
362
 
283
- ## Case Transformation
363
+ ---
364
+
365
+ ## Case transformation & wire shapes
284
366
 
285
- - **Input**: camelCase (`recordCode`, `selectedServiceList`)
286
- - **Wire**: PascalCase / `@` attributes for XML
367
+ - **Input**: camelCase (`recordCode`, `selectedServiceList`, `paymentUser`)
368
+ - **Wire**: PascalCase keys; `@`-prefixed keys become XML attributes
287
369
  - **Output**: camelCase again
288
370
 
289
- You do not handle XML attribute prefixes yourself.
371
+ 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.
372
+
373
+ ### Pipeline
374
+
375
+ One outbound path for all ops:
376
+
377
+ 1. Valibot validates camelCase input
378
+ 2. `createApiSchema(schema, shape, wrap?)` → `toWireBody` (optional list wrap + empty `paxAssociated` normalize + `camelToPascalKeys`)
379
+ 3. `AvesTransport.invokeOp` adds `RqHeader`, optional `bodyKey` nest, POSTs XML
380
+
381
+ Element-only roots use `elementOnlyWire` (`{}`). Master upsert nests under `bodyKey: "MasterRecordDetail"`; master search and booking spread fields at the RQ root.
382
+
383
+ ### Request shapes (one family per op)
384
+
385
+ | Shape | Used by |
386
+ | ----- | ------- |
387
+ | `masterRecordWire` / `searchMasterWire` | Upsert / Search master |
388
+ | `bookingFileWire` | CreateBooking, ModFileServices, ModFileHeader |
389
+ | `filePaymentListRequestWire` | InsertFilePaymentList (`paymentUser` root attr) |
390
+ | `searchFileWire` | SearchBookingFiles (date ranges / insurance attrs) |
391
+ | `setFileStatusWire` | SetBookingFileStatus |
392
+ | `packageDetailRequestWire` | GetPackageDetail |
393
+ | `baseSearchWire` + `avesSearchWire` | SearchAvesPackages / SearchTopServices |
394
+ | `elementOnlyWire` | CancelFile, SetFileServiceStatus, CommitPackage |
395
+
396
+ Editing CreateBooking shapes does not affect SearchFile or AvesSearch.
397
+
398
+ ---
399
+
400
+ ## Architecture
401
+
402
+ `AvesClient` (`src/client.ts`) is a thin facade over DI-friendly domain clients:
403
+
404
+ | Module | Role |
405
+ | ------ | ---- |
406
+ | `AvesTransport` | HTTP + XML encode/decode + `invokeOp` (optional `bodyKey`) |
407
+ | `MasterRecordsClient` | search / upsert |
408
+ | `BookingClient` | booking file ops + search practices |
409
+ | `PackageCatalogClient` | package/program catalog |
410
+ | `client/types.ts` | `AvesClientDeps` / `AvesClientFlat` |
411
+ | `client/flat-aliases.ts` | bind flat compat methods from domain prototypes |
412
+ | `src/xml/` | XML root helpers + JSON ↔ XML |
413
+
414
+ Flat aliases (`client.createBooking`, …) are attached in the constructor — add a domain method once and both surfaces work.
415
+
416
+ Outbound path: validate → `createApiSchema` / `toWireBody` → `invokeOp` → XML POST.
417
+
418
+ ---
290
419
 
291
420
  ## Types
292
421
 
293
422
  ```typescript
294
423
  import type {
295
424
  AvesClientOptions,
296
- SearchMasterRecord,
297
- SearchMasterRecordRS,
298
- MasterRecordDetail,
299
- ManageMasterRecordRS,
425
+ AvesClientDeps,
426
+ AvesSearchRQ,
300
427
  BookingFileRQ,
301
- BookingFileRS,
302
- ModFileServicesRQ,
303
- ModFileHeaderRQ,
304
- CancelFileRQ,
305
- SetFileStatusRQ,
306
- SetFileServiceStatusRQ,
307
- FilePaymentListRQ,
308
- BookingStatusOnlyRS,
428
+ SearchMasterRecord,
429
+ } from 'aves-sdk';
430
+
431
+ import {
432
+ AvesClient,
433
+ AvesTransport,
434
+ MasterRecordsClient,
435
+ BookingClient,
436
+ PackageCatalogClient,
437
+ AvesError,
438
+ // object enums
439
+ AvesSearchType,
440
+ BookingFileStatus,
441
+ InsertCriteria,
442
+ PassengerCategory,
443
+ PaxQtyCriteria,
444
+ PaymentType,
445
+ SearchMasterType,
309
446
  } from 'aves-sdk';
447
+
448
+ PaxQtyCriteria.GREATER_OR_EQUAL
449
+ AvesSearchType.PACKAGE
450
+ PaymentType.B
451
+ InsertCriteria.T
310
452
  ```
311
453
 
312
454
  ## License
@@ -315,6 +457,7 @@ MIT
315
457
 
316
458
  ## Links
317
459
 
460
+ - [Changelog](./CHANGELOG.md)
318
461
  - [GitHub Repository](https://github.com/simoneguglielmi/aves-sdk)
319
462
  - [NPM Package](https://npmjs.com/package/aves-sdk)
320
463
  - [Issue Tracker](https://github.com/simoneguglielmi/aves-sdk/issues)