aves-sdk 4.0.0 → 5.0.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 CHANGED
@@ -5,6 +5,118 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ <a id="v5.0.0"></a>
9
+ ## [5.0.0] - 2026-08-24
10
+
11
+ Effect-native core: **Effect Schema** + **`@effect/platform` HttpClient**, Promise `Result` facade at the edge, optional Layers / `ManagedRuntime` for Effect apps.
12
+
13
+ ### Added
14
+
15
+ - `makeAvesRuntime(options, deps?)` — `ManagedRuntime` over `avesClientLayer` for `yield* AvesBooking` / `AvesMaster` / `AvesPackages` / `AvesTransport`
16
+ - `avesClientLayer` / `AvesClientLive` — composable Effect Layers (`config → http → transport → domains`)
17
+ - Domain Tags: `AvesBooking`, `AvesMaster`, `AvesPackages`, `AvesTransport`, `AvesHttp`, `AvesConfig`
18
+ - `createAvesClient` — plain-object Promise facade (same shape as `AvesClient`)
19
+ - Public `Result` / `ok` / `err`; `isAvesError`; tagged errors `AvesValidationError` / `AvesApiError` / `AvesUnknownError` (+ factories)
20
+ - Test DI: `deps.httpClient` (`HttpClient.make`) and/or `deps.http` / domain service overrides
21
+
22
+ ### Changed
23
+
24
+ - **Breaking:** Validation stack is **Effect Schema** (`effect`). Valibot is removed.
25
+ - **Breaking:** HTTP is **`@effect/platform`** (`FetchHttpClient` by default). Undici is no longer a dependency; do not use `MockAgent` / `setGlobalDispatcher` for SDK tests — inject `HttpClient.make` via `deps.httpClient`.
26
+ - **Breaking:** Domains are Effect-native (`Effect.Effect<…, AvesError>`). The Promise API is a thin `toPromiseFacade` edge on `createAvesClient` / `AvesClient`.
27
+ - **Breaking:** `client.transport` is **removed** from the Promise facade. Effect programs use `yield* AvesTransport` (or domain Tags) with `makeAvesRuntime` / `avesClientLayer`.
28
+ - **Breaking:** `AvesError` is a **tagged union**, not a single constructible class. Prefer `isAvesError(e)`, `e instanceof AvesApiError`, or `Effect.catchTag("AvesApiError", …)`. Promise `error.kind` (`validation` | `api` | `unknown`) is unchanged.
29
+ - **Breaking:** DI no longer uses `new AvesTransport(options)` / `new BookingClient(transport)`. Use `AvesClientDeps`: `{ httpClient?, http?, transport?, master?, booking?, packages? }`.
30
+ - Internal layout: `src/client/{booking,master,packages,http,transport,config}/` (types / tag / service / layer).
31
+ - Published types shrank from 1.21 MB to 404 kB (npm tarball 115 kB → 72 kB): op params/results and the domain service types are now written against named types instead of inferred, so the declaration bundle references `BookingInput` & co. instead of inlining every schema structure. The public API is unchanged — `src/client/{ops,services}.test-d.ts` pin each op and domain method against the schema the transport actually uses.
32
+
33
+ ### Migration (4.x → 5.x)
34
+
35
+ 1. **Install** — `aves-sdk@5` pulls `effect` and `@effect/platform`. No Valibot/undici required.
36
+ 2. **Promise apps** — keep `new AvesClient(options)` / `createAvesClient(options)`. Import `Result` from `aves-sdk` if you typed it locally. Stop reading `client.transport`.
37
+ 3. **Errors** — replace `instanceof AvesError` with `isAvesError(error)` (or the concrete tagged class). `error.kind` / `message` / `status` / `code` still work on the Promise path.
38
+ 4. **Tests / DI** — replace undici `MockAgent` with platform mocks:
39
+
40
+ ```ts
41
+ import { HttpClient, HttpClientResponse } from "@effect/platform";
42
+ import { Effect } from "effect";
43
+ import { AvesClient } from "aves-sdk";
44
+
45
+ const httpClient = HttpClient.make((request, url) =>
46
+ Effect.succeed(
47
+ HttpClientResponse.fromWeb(
48
+ request,
49
+ new Response(`<SearchMasterRecordRS><RsStatus Status="OK"/></SearchMasterRecordRS>`, {
50
+ status: 200,
51
+ }),
52
+ ),
53
+ ),
54
+ );
55
+
56
+ const client = new AvesClient(options, { httpClient });
57
+ ```
58
+
59
+ 5. **Effect apps** — use Layers instead of the Promise facade:
60
+
61
+ ```ts
62
+ import { Effect } from "effect";
63
+ import { AvesMaster, makeAvesRuntime } from "aves-sdk";
64
+
65
+ const runtime = makeAvesRuntime(options);
66
+ const records = await runtime.runPromise(
67
+ Effect.gen(function* () {
68
+ const master = yield* AvesMaster;
69
+ return yield* master.search({ searchType: "CODE", recordCode: "508558" });
70
+ }),
71
+ );
72
+ await runtime.dispose();
73
+ ```
74
+
75
+ Or `Effect.provide(avesClientLayer(options, deps))` without a ManagedRuntime. Recover with `Effect.catchTag("AvesApiError", …)` / `"AvesValidationError"` / `"AvesUnknownError"`.
76
+
77
+ XML wire shapes, facade aliases, and domain method names from 4.x are unchanged.
78
+
79
+ <a id="v4.1.0"></a>
80
+ ## [4.1.0] - 2026-08-07
81
+
82
+ ### Added
83
+
84
+ - `client.booking.exportData(params)` — **ExportBookingData** (`BookingDataExportRQ` / `BookingDataExportRS`). The only AVES operation that reads a booking file back whole: header, passengers, booked services with per-line `amountsDetail`, file-level `bookedFileAmounts`, and **registered payments**. `addPayments` finally has a read counterpart.
85
+ - `ExtraInfo` lookup tables on the response: `currencyList`, `vatList`, `nationList`, `travelAgentList`, `programList`, `statisticList`, `priceOffertList`, `userList`, `passengerCategoryList`, `masterDataSet`, `masterDataSetExtraInfo`. `nationList[].territoriality` (`IN_UE` / `OUT_UE` / `MIXED_UE`) is the VAT-regime input.
86
+ - Object enums (+ schemas) for every closed picklist the export returns: `ExportType`, `Territoriality`, `StatisticType`, `ToSubServiceType`, `SellingType`, `Printable`, `PrintType`, `DeadlineStatus`, `CommissionIncomeType`, `CommissionOwedType`.
87
+ - `RecordTypeWire` — `RecordType` plus `NOT_SET`, for read responses. Mirrors the existing `BookingFileStatus` / `BookingFileStatusWire` split.
88
+ - Remaining documented export structures are now typed: `customerProcessedPrintList`, `instalmentPlanList`, `supplierInstalmentPlanList`, file- and service-level `deadlineList`, `commissionIncomeDetails`, `commissionOwedDetails`.
89
+ - Types `ExportBookingDataRQ` / `ExportBookingDataRS`, `ExportedBookingFile`, `ExportExtraInfo`, and facade aliases `BookingExportInput` / `BookingExportResult`.
90
+ - Facade output aliases: `bookingFileList` → `bookings`, `bookedServices` → `services`, `amountsDetail` → `amounts`, `bookedFileAmounts` → `totals`.
91
+
92
+ ### Fixed
93
+
94
+ - `master.search` / `master.upsert` responses no longer reject a record whose `RecordType` is `NOT_SET`. The response validation schema was reusing the request picklist, which omits it. Server-only fields AVES returns on read (`AreaCode`, `LastDateContact`, `UseSupplierDataOnTravelDoc`, `BookingEnabled`, `PrivacyPolicyAccepted`, and the misspelled `ModifitedDate`) are no longer dropped.
95
+
96
+ Note for TypeScript consumers: `MasterRecordDetailResponse["recordType"]` widens from `RecordType` to `RecordTypeWire`, so an exhaustive `switch` over it now needs a `NOT_SET` branch. Input types are unchanged.
97
+ - Facade arrays now expose public aliases when **iterated**, not only when indexed. `withPublicAliases` bound array methods to the raw target, so `for…of`, destructuring, spread, `Array.from`, `map`, `filter`, `find`, `forEach` and `at` all handed back rows without their alias keys — `const [p] = result.data.payments` yielded a row with `paymentDate` but no aliases, while `result.data.payments[0]` worked. Methods now run with `this` bound to the proxy. This also makes `indexOf` / `includes` consistent: `arr.indexOf(arr[0])` returned `-1` before, since it compared wrapped items against raw ones.
98
+
99
+ ### Changed
100
+
101
+ - `BoolishSchema` moved from `schemas/booking-shared.ts` to `schemas/common.ts`, next to `StringishSchema` — it is a cross-domain wire primitive, and `master-record` / `package-catalog` were already importing it out of the booking module. Internal only; it is not part of the published entry point.
102
+
103
+ ### Notes
104
+
105
+ - Request roots are `BookingDataExport*`, not `ExportBookingData*`: the spec's section tables name the endpoint, while its index and both worked XML examples name the elements.
106
+ - `limitRange` enforces the documented bounds (`skip` ≥ 0, `take` ≤ 1000) before the request leaves the SDK.
107
+ - Wire misspellings are normalized on the way in: `PaumentNote` → `paymentNote`, `FirstConfemationDate` → `firstConfirmationDate`, `UsersList` → `userList`, `TOSubServiceType` → `toSubServiceType`.
108
+ - `extraInfo.masterDataSet` rows validate as full master records, reusing the schema behind `master.search`.
109
+ - `regimeType` and `customerPayAt` are the only response fields left as plain strings: Booking.txt:11312 documents no value list for the first, and the second appears nowhere in the response table — only in the example, as `OUR_AGENCY`. There is nothing to validate against; an enum built from one sample would reject every other value.
110
+ - `Printable` values are rejoined from a spec table that hard-wraps identifiers mid-token. If a live response is ever rejected on that field, check the reconstruction against Booking.txt:11261-11289 first.
111
+
112
+ <a id="v4.0.1"></a>
113
+ ## [4.0.1] - 2026-08-06
114
+
115
+ ### Added
116
+
117
+ - Copy `CHANGELOG.md` into `dist/` on build (`tsdown` `copy`) and include `dist/CHANGELOG.md` in published `files`.
118
+
119
+ <a id="v4.0.0"></a>
8
120
  ## [4.0.0] - 2026-08-06
9
121
 
10
122
  ### Added
@@ -50,6 +162,7 @@ await client.packages.search(params);
50
162
  await client.master.upsert(record);
51
163
  ```
52
164
 
165
+ <a id="v3.1.1"></a>
53
166
  ## [3.1.1] - 2026-08-06
54
167
 
55
168
  ### Added
@@ -70,9 +183,10 @@ await client.master.upsert(record);
70
183
  - WeakMap cache for `itemShape` / `encodeShapeFor` on static WireShape refs.
71
184
  - `toWireBody` single shape-driven walk (list wrap + pax normalize + wire keys).
72
185
  - Zero-copy `withPublicAliases` via hardened lazy Proxy (WeakMap identity, proto-pollution blocked).
73
- - ADR 0001 Phase 2a: `createResponseSchema` camelizes Valibot output **in place** (`pascalToCamelKeysInPlace`) — no second deep-copy. See [`docs/adr/0001-validate-during-camelize.md`](docs/adr/0001-validate-during-camelize.md).
186
+ - `createResponseSchema` camelizes Valibot output **in place** (`pascalToCamelKeysInPlace`) — no second deep-copy.
74
187
  - Performance harness: `yarn test:bench` (Vitest bench) and `AVES_PERF=1 yarn test:perf` (relative hot-path asserts).
75
188
 
189
+ <a id="v3.0.0"></a>
76
190
  ## [3.0.0] - 2026-08-06
77
191
 
78
192
  ### Changed
@@ -119,6 +233,7 @@ if (result.success) {
119
233
 
120
234
  3.1.x on top of 3.0 is additive (facade aliases / internal transport). No further breaking migration from 3.0 → 3.1.1.
121
235
 
236
+ <a id="v2.0.1"></a>
122
237
  ## [2.0.1] - 2026-08-05
123
238
 
124
239
  ### Changed
@@ -126,6 +241,7 @@ if (result.success) {
126
241
  - Omit source maps from the published package; `files` now ships only
127
242
  `dist/index.mjs` and `dist/index.d.mts`.
128
243
 
244
+ <a id="v2.0.0"></a>
129
245
  ## [2.0.0] - 2026-08-05
130
246
 
131
247
  ### Changed
@@ -149,6 +265,7 @@ if (result.success) {
149
265
 
150
266
  ---
151
267
 
268
+ <a id="v1.9.0"></a>
152
269
  ## [1.9.0] - 2026-08-03
153
270
 
154
271
  ### Changed
@@ -169,6 +286,7 @@ if (result.success) {
169
286
 
170
287
  ---
171
288
 
289
+ <a id="v1.8.0"></a>
172
290
  ## [1.8.0] - 2026-08-03
173
291
 
174
292
  ### Added
@@ -184,6 +302,7 @@ if (result.success) {
184
302
 
185
303
  ---
186
304
 
305
+ <a id="v1.7.0"></a>
187
306
  ## [1.7.0] - 2026-08-03
188
307
 
189
308
  ### Added
@@ -212,6 +331,7 @@ if (result.success) {
212
331
 
213
332
  ---
214
333
 
334
+ <a id="v1.6.0"></a>
215
335
  ## [1.6.0] - 2026-07-29
216
336
 
217
337
  ### Changed
@@ -223,6 +343,7 @@ if (result.success) {
223
343
 
224
344
  ---
225
345
 
346
+ <a id="v1.5.1"></a>
226
347
  ## [1.5.1] - 2026-07-29
227
348
 
228
349
  ### Changed
@@ -232,6 +353,7 @@ if (result.success) {
232
353
 
233
354
  ---
234
355
 
356
+ <a id="v1.5.0"></a>
235
357
  ## [1.5.0] - 2026-07-29
236
358
 
237
359
  ### Added
@@ -242,6 +364,7 @@ if (result.success) {
242
364
 
243
365
  ---
244
366
 
367
+ <a id="v1.4.0"></a>
245
368
  ## [1.4.0] - 2026-07-29
246
369
 
247
370
  ### Added
@@ -256,6 +379,7 @@ if (result.success) {
256
379
 
257
380
  ---
258
381
 
382
+ <a id="v1.3.4"></a>
259
383
  ## [1.3.4] - 2026-03-09
260
384
 
261
385
  ### Added
@@ -273,6 +397,7 @@ if (result.success) {
273
397
 
274
398
  ---
275
399
 
400
+ <a id="v1.3.3"></a>
276
401
  ## [1.3.3] - 2026-02-26
277
402
 
278
403
  ### Fixed
@@ -281,6 +406,7 @@ if (result.success) {
281
406
 
282
407
  ---
283
408
 
409
+ <a id="v1.3.2"></a>
284
410
  ## [1.3.2] - 2026-02-10
285
411
 
286
412
  ### Added
@@ -294,6 +420,7 @@ if (result.success) {
294
420
 
295
421
  ---
296
422
 
423
+ <a id="v1.3.1"></a>
297
424
  ## [1.3.1] - 2026-02-01
298
425
 
299
426
  ### Changed
@@ -303,6 +430,7 @@ if (result.success) {
303
430
 
304
431
  ---
305
432
 
433
+ <a id="v1.3.0"></a>
306
434
  ## [1.3.0] - 2026-02-01
307
435
 
308
436
  ### Added
@@ -318,6 +446,7 @@ if (result.success) {
318
446
 
319
447
  ---
320
448
 
449
+ <a id="v1.2.16"></a>
321
450
  ## [1.2.16] - 2026-01-27
322
451
 
323
452
  ### Changed
@@ -326,6 +455,7 @@ if (result.success) {
326
455
 
327
456
  ---
328
457
 
458
+ <a id="v1.2.15"></a>
329
459
  ## [1.2.15] - 2026-01-27
330
460
 
331
461
  ### Fixed
@@ -334,6 +464,7 @@ if (result.success) {
334
464
 
335
465
  ---
336
466
 
467
+ <a id="v1.2.14"></a>
337
468
  ## [1.2.14] - 2026-01-27
338
469
 
339
470
  ### Changed
@@ -342,6 +473,7 @@ if (result.success) {
342
473
 
343
474
  ---
344
475
 
476
+ <a id="v1.2.13"></a>
345
477
  ## [1.2.13] - 2026-01-27
346
478
 
347
479
  ### Added
@@ -358,6 +490,7 @@ if (result.success) {
358
490
 
359
491
  ---
360
492
 
493
+ <a id="v1.2.12"></a>
361
494
  ## [1.2.12] - 2026-01-26
362
495
 
363
496
  ### Added
@@ -376,6 +509,7 @@ if (result.success) {
376
509
 
377
510
  ---
378
511
 
512
+ <a id="v1.2.11"></a>
379
513
  ## [1.2.11] - 2026-01-21
380
514
 
381
515
  ### Fixed
@@ -384,6 +518,7 @@ if (result.success) {
384
518
 
385
519
  ---
386
520
 
521
+ <a id="v1.0.3"></a>
387
522
  ## [1.0.3] - 2025-10-04
388
523
 
389
524
  Early tagged releases (`v1.0.0` … `v1.0.3`) from the initial NestJS-oriented AVES SDK work. Detailed commit notes for those tags are limited in the current branch history.
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 / `@` attrs, and returns `Result` instead of throwing.
3
+ Type-safe TypeScript SDK for the AVES XML REST API. Handles XML parsing, **Effect Schema** validation, camelCase ↔ PascalCase / `@` attrs, and `@effect/platform` HTTP. Public domain methods return `Result` (they do not throw). Effect apps can also `yield*` domain Tags via Layers / `makeAvesRuntime`.
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,61 +14,102 @@ pnpm add aves-sdk
14
14
  bun add aves-sdk
15
15
  ```
16
16
 
17
- ## Quick start
17
+ `aves-sdk` depends on `effect` and `@effect/platform`.
18
+
19
+ ## Quick start (Promise facade)
18
20
 
19
21
  ```typescript
20
- import { AvesClient } from 'aves-sdk';
22
+ import { AvesClient, isAvesError, type Result } from "aves-sdk";
21
23
 
22
24
  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
25
+ baseURL: "https://api.example.com",
26
+ hostID: "000000", // 6 digits
27
+ xtoken: "TOKEN",
28
+ languageCode: "02", // optional: 01=Italian, 02=English
27
29
  timeoutMs: 30_000, // optional
28
30
  });
29
31
 
30
- const result = await client.master.search({
31
- searchType: 'CODE',
32
- recordCode: '508558',
32
+ const result: Result<unknown> = await client.master.search({
33
+ searchType: "CODE",
34
+ recordCode: "508558",
33
35
  });
34
36
 
35
37
  if (result.success) {
36
38
  const [record] = result.data;
37
39
  console.log(record?.recordCode);
38
- } else {
40
+ } else if (isAvesError(result.error)) {
39
41
  console.error(result.error.kind, result.error.message, result.error.code);
40
42
  }
41
43
  ```
42
44
 
45
+ `createAvesClient(options, deps?)` returns the same Promise API as a plain object.
46
+
47
+ ## Quick start (Effect)
48
+
49
+ ```typescript
50
+ import { Effect } from "effect";
51
+ import { AvesMaster, makeAvesRuntime } from "aves-sdk";
52
+
53
+ const runtime = makeAvesRuntime({
54
+ baseURL: "https://api.example.com",
55
+ hostID: "000000",
56
+ xtoken: "TOKEN",
57
+ });
58
+
59
+ const records = await runtime.runPromise(
60
+ Effect.gen(function* () {
61
+ const master = yield* AvesMaster;
62
+ return yield* master.search({
63
+ searchType: "CODE",
64
+ recordCode: "508558",
65
+ });
66
+ }).pipe(
67
+ Effect.catchTag("AvesApiError", (e) =>
68
+ Effect.succeed({ failed: true as const, code: e.code }),
69
+ ),
70
+ ),
71
+ );
72
+
73
+ await runtime.dispose();
74
+ ```
75
+
76
+ Also: `Effect.provide(avesClientLayer(options, deps))` or `AvesClientLive(options)` for the full stack including `AvesConfig` / `AvesHttp`.
77
+
43
78
  ## Client shape
44
79
 
45
- `AvesClient` is a facade over domain clients. Operations are namespaced by domain.
80
+ `AvesClient` / `createAvesClient` expose **Promise** domain namespaces only (`master`, `booking`, `packages`). There is no `client.transport` on the Promise facade — use `AvesTransport` with Layers for Effect programs.
46
81
 
47
82
  ```typescript
48
83
  await client.booking.create(params);
84
+ await client.booking.exportData(params);
49
85
  await client.packages.search(params);
50
- await client.master.search({ searchType: 'CODE', recordCode: '508558' });
86
+ await client.master.search({ searchType: "CODE", recordCode: "508558" });
51
87
  ```
52
88
 
53
89
  Migration from 1.x: insert the appropriate domain namespace (for example,
54
- `client.search` → `client.master.search`). See the [2.0.0 migration notes](./CHANGELOG.md#200---2026-08-05).
90
+ `client.search` → `client.master.search`). See the [2.0.0 migration notes](https://github.com/simoneguglielmi/aves-sdk/blob/main/CHANGELOG.md#v2.0.0).
55
91
 
56
92
  Migration from 2.x: `master.search` success is a flat array — see the
57
- [3.0.0 migration notes](./CHANGELOG.md#300---2026-08-06).
93
+ [3.0.0 migration notes](https://github.com/simoneguglielmi/aves-sdk/blob/main/CHANGELOG.md#v3.0.0).
58
94
 
59
95
  Migration from 3.x: domain method names were shortened — see the
60
- [4.0.0 migration notes](./CHANGELOG.md#400---2026-08-06).
96
+ [4.0.0 migration notes](https://github.com/simoneguglielmi/aves-sdk/blob/main/CHANGELOG.md#v4.0.0).
97
+
98
+ Migration from 4.x: Effect Schema + platform HTTP + tagged errors — see the
99
+ [5.0.0 migration notes](https://github.com/simoneguglielmi/aves-sdk/blob/main/CHANGELOG.md#v5.0.0).
61
100
 
62
101
  | Namespace | Methods |
63
102
  | --------- | ------- |
64
103
  | `master` | `search`, `upsert` |
65
- | `booking` | `create`, `updateServices`, `updateHeader`, `cancel`, `setStatus`, `setServiceStatus`, `addPayments`, `search` |
104
+ | `booking` | `create`, `updateServices`, `updateHeader`, `cancel`, `setStatus`, `setServiceStatus`, `addPayments`, `search`, `exportData` |
66
105
  | `packages` | `search`, `searchServices`, `get`, `commit` |
67
106
 
68
107
  ### Constructor
69
108
 
70
109
  ```typescript
71
110
  new AvesClient(options: AvesClientOptions, deps?: AvesClientDeps)
111
+ // or
112
+ createAvesClient(options, deps?)
72
113
  ```
73
114
 
74
115
  | Option | Description |
@@ -79,28 +120,36 @@ new AvesClient(options: AvesClientOptions, deps?: AvesClientDeps)
79
120
  | `languageCode` | Optional 2-digit language code |
80
121
  | `timeoutMs` | Optional request timeout (default 30000) |
81
122
 
82
- Optional DI (tests / custom transport):
123
+ Optional DI (tests / custom HTTP):
83
124
 
84
125
  ```typescript
85
- import {
86
- AvesClient,
87
- AvesTransport,
88
- BookingClient,
89
- } from 'aves-sdk';
126
+ import { HttpClient, HttpClientResponse } from "@effect/platform";
127
+ import { Effect } from "effect";
128
+ import { AvesClient } from "aves-sdk";
129
+
130
+ const httpClient = HttpClient.make((request, url) =>
131
+ Effect.succeed(
132
+ HttpClientResponse.fromWeb(
133
+ request,
134
+ new Response(xmlBody, { status: 200 }),
135
+ ),
136
+ ),
137
+ );
90
138
 
91
- const transport = new AvesTransport(options);
92
139
  new AvesClient(options, {
93
- transport,
94
- booking: new BookingClient(transport),
140
+ httpClient,
141
+ // or: http, transport, master, booking, packages service overrides
95
142
  });
96
143
  ```
97
144
 
98
145
  ### Result type
99
146
 
100
- All methods return `Result<T, AvesError>` — they do **not** throw.
147
+ Promise domain methods return `Result<T, AvesError>` — they do **not** throw. `Result` is exported from `aves-sdk`.
101
148
 
102
149
  ```typescript
103
- type Result<T, E> =
150
+ import type { Result } from "aves-sdk";
151
+
152
+ type Result<T, E = Error> =
104
153
  | { success: true; data: T }
105
154
  | { success: false; error: E };
106
155
  ```
@@ -108,7 +157,7 @@ type Result<T, E> =
108
157
  ## Simple facade names
109
158
 
110
159
  The facade exposes concise aliases for the AVES payload vocabulary. Inbound
111
- dual keys are owned by Valibot input schemas (`facadeObject` / `coalesceAliases`)
160
+ dual keys are owned by Effect Schema input schemas (`facadeObject` / `coalesceAliases`)
112
161
  and coalesced to AVES camelCase before wire encoding. Outbound success payloads
113
162
  keep AVES names and add concise compatibility aliases. XML shapes are unchanged.
114
163
 
@@ -410,6 +459,7 @@ if (result.success) {
410
459
  | `setServiceStatus` | Nullify a single service line |
411
460
  | `addPayments` | Register payments |
412
461
  | `search` | Search practices (`FILE_CODE`, `PAX_NAME`, `PACKAGE_CODE`, `OTHER`) |
462
+ | `exportData` | Read practices back whole, **incl. registered payments and amounts** |
413
463
 
414
464
  ### Create
415
465
 
@@ -543,6 +593,54 @@ await client.booking.search({
543
593
  });
544
594
  ```
545
595
 
596
+ ### Read a practice back (`exportData`)
597
+
598
+ `search` returns the header and booked services. `exportData` is the only AVES
599
+ operation that also returns **registered payments**, per-line amounts and file
600
+ totals — use it to reconcile what was written with `addPayments` / `create`.
601
+
602
+ Every field is an optional filter. With none set AVES exports everything the
603
+ credentials can reach, so pass `limitRange` for anything broader than one
604
+ `bookingFileCode` (`skip` ≥ 0, `take` ≤ 1000 — enforced before sending).
605
+
606
+ ```typescript
607
+ const result = await client.booking.exportData({
608
+ bookingFileCode: '14/036654',
609
+ // or filter a window:
610
+ // lastModificationDate: { minDate: '2026-08-01', maxDate: '2026-08-07' },
611
+ // statusLists: ['CONFIRMED'],
612
+ // limitRange: { skip: 0, take: 500 },
613
+ });
614
+
615
+ if (result.success) {
616
+ const file = result.data.bookingFileList?.[0];
617
+
618
+ for (const payment of file?.paymentList ?? []) {
619
+ console.log(payment.paymentDate, payment.amount, payment.paymentType);
620
+ }
621
+
622
+ for (const service of file?.bookedServices ?? []) {
623
+ console.log(service.serviceCode, service.amountsDetail?.costWithTax);
624
+ }
625
+
626
+ console.log(file?.bookedFileAmounts?.customerDueAmount);
627
+
628
+ // Lookup tables the exported codes resolve against.
629
+ // `territoriality` (IN_UE | OUT_UE | MIXED_UE) drives the VAT regime.
630
+ console.log(result.data.extraInfo?.nationList);
631
+ console.log(result.data.extraInfo?.vatList);
632
+ }
633
+ ```
634
+
635
+ `extraInfo.masterDataSet` rows are validated as full master records — the same
636
+ shape `client.master.search` returns. Read responses accept `RecordType:
637
+ 'NOT_SET'` via `RecordTypeWire`; the request-side `RecordType` stays strict.
638
+
639
+ Every picklist the spec closes is validated, so an undocumented value fails the
640
+ whole response rather than reaching your code. Two fields are deliberately
641
+ plain strings because AVES documents no values for them: `regimeType` (no list
642
+ given) and `customerPayAt` (absent from the response table entirely).
643
+
546
644
  ---
547
645
 
548
646
  ## Package / Program catalog
@@ -597,20 +695,29 @@ if (detail.success) console.log(detail.data.pCode, detail.data.serviceList?.[0])
597
695
 
598
696
  ## Errors
599
697
 
698
+ `AvesError` is a tagged union: `AvesValidationError` | `AvesApiError` | `AvesUnknownError`.
699
+ Each has `_tag` (for `Effect.catchTag`) and Promise-friendly `kind` (`validation` | `api` | `unknown`).
700
+
600
701
  ```typescript
601
- import { AvesClient, AvesError } from 'aves-sdk';
702
+ import { AvesClient, AvesApiError, isAvesError } from "aves-sdk";
602
703
 
603
704
  const result = await client.master.search({
604
- searchType: 'CODE',
605
- recordCode: '508558',
705
+ searchType: "CODE",
706
+ recordCode: "508558",
606
707
  });
607
708
 
608
709
  if (!result.success) {
609
- // kind: 'validation' | 'api' | 'unknown'
610
- console.error(result.error.kind, result.error.message, result.error.code);
710
+ if (isAvesError(result.error)) {
711
+ console.error(result.error.kind, result.error.message, result.error.code);
712
+ }
713
+ if (result.error instanceof AvesApiError) {
714
+ console.error(result.error._tag, result.error.status);
715
+ }
611
716
  }
612
717
  ```
613
718
 
719
+ Effect programs recover with `Effect.catchTag("AvesApiError", …)` (also `"AvesValidationError"`, `"AvesUnknownError"`).
720
+
614
721
  ---
615
722
 
616
723
  ## Case transformation & wire shapes
@@ -626,9 +733,9 @@ You do not handle XML attribute prefixes yourself. Attr vs element is decided by
626
733
 
627
734
  One outbound path for all ops:
628
735
 
629
- 1. Valibot validates camelCase input
736
+ 1. Effect Schema validates camelCase input
630
737
  2. `createApiSchema(schema, shape, wrap?)` → `toWireBody` (optional list wrap + `paxAssociated` string[]/empty normalize + `camelToPascalKeys`)
631
- 3. `AvesTransport.invokeOp` adds `RqHeader`, optional `bodyKey` nest, POSTs XML
738
+ 3. Transport `invoke` / `ops.*` adds `RqHeader`, optional `bodyKey` nest, POSTs XML
632
739
 
633
740
  Inbound: `createResponseSchema` / `createFlattenedResponseSchema` / `createListResponseSchema` → camelCase + optional detail spread or typed list RS; `listDetailApiSchema` unwraps wire `*List`/`*Detail` to flat arrays; `coalesceWireAliases` normalizes dialect attrs. Input aliases: `valueFieldSchema`, `coalesceCustomerRecordCode`, `coalesceListHead`. Shared primitives: `LanguageCodeSchema`, `StringishSchema`, `DateRangeSchema`, `StatusOnlyResponseSchema`.
634
741
 
@@ -653,18 +760,23 @@ Editing CreateBooking shapes does not affect SearchFile or AvesSearch.
653
760
 
654
761
  ## Architecture
655
762
 
656
- `AvesClient` (`src/client.ts`) is a thin facade over DI-friendly domain clients:
763
+ Promise edge: `createAvesClient` / `AvesClient` → `toPromiseFacade` on domain services.
764
+ Effect edge: `makeAvesRuntime` / `avesClientLayer` / `AvesClientLive` → `yield*` Tags.
657
765
 
658
766
  | Module | Role |
659
767
  | ------ | ---- |
660
- | `AvesTransport` | HTTP + XML encode/decode + `invokeOp` (optional `bodyKey`) |
661
- | `MasterRecordsClient` | search / upsert |
662
- | `BookingClient` | booking file ops + search practices |
663
- | `PackageCatalogClient` | package/program catalog |
664
- | `client/types.ts` | `AvesClientDeps` |
768
+ | `src/client.ts` | Promise facade + `makeAvesRuntime` |
769
+ | `src/client/layer.ts` | `avesClientLayer` / `AvesClientLive` composition |
770
+ | `src/client/booking/` | Booking Tag / service / layer |
771
+ | `src/client/master/` | Master-records Tag / service / layer |
772
+ | `src/client/packages/` | Package catalog Tag / service / layer |
773
+ | `src/client/transport/` | `invoke` / `ops` / response reader |
774
+ | `src/client/http/` | XML POST via `@effect/platform` HttpClient |
775
+ | `src/client/config/` | `AvesConfig` |
776
+ | `src/schemas/` | Effect Schema RQ/RS |
665
777
  | `src/xml/` | XML root helpers + JSON ↔ XML |
666
778
 
667
- Outbound path: validate → `createApiSchema` / `toWireBody` → `invokeOp` → XML POST.
779
+ Outbound path: Schema decode → `createApiSchema` / `toWireBody` → transport → HTTP POST.
668
780
 
669
781
  ---
670
782
 
@@ -673,24 +785,30 @@ Outbound path: validate → `createApiSchema` / `toWireBody` → `invokeOp` →
673
785
  Operations are available only through `client.master`, `client.booking`, and
674
786
  `client.packages`; flat aliases were removed in 2.0.0. When upgrading from 2.x,
675
787
  also update `master.search` consumers for the flat array success payload (3.0.0).
676
- See the [CHANGELOG migration notes](./CHANGELOG.md).
788
+ See the [CHANGELOG migration notes](https://github.com/simoneguglielmi/aves-sdk/blob/main/CHANGELOG.md).
677
789
 
678
790
  ```typescript
679
791
  import type {
680
792
  AvesClientOptions,
681
793
  AvesClientDeps,
794
+ AvesError,
682
795
  AvesSearchRQ,
683
796
  BookingFileRQ,
797
+ Result,
684
798
  SearchMasterRecord,
685
- } from 'aves-sdk';
799
+ } from "aves-sdk";
686
800
 
687
801
  import {
688
802
  AvesClient,
803
+ createAvesClient,
804
+ makeAvesRuntime,
805
+ AvesMaster,
806
+ AvesBooking,
807
+ AvesPackages,
689
808
  AvesTransport,
690
- MasterRecordsClient,
691
- BookingClient,
692
- PackageCatalogClient,
693
- AvesError,
809
+ avesClientLayer,
810
+ isAvesError,
811
+ AvesApiError,
694
812
  // object enums
695
813
  AvesSearchType,
696
814
  BookingFileStatus,
@@ -699,7 +817,7 @@ import {
699
817
  PaxQtyCriteria,
700
818
  PaymentType,
701
819
  SearchMasterType,
702
- } from 'aves-sdk';
820
+ } from "aves-sdk";
703
821
 
704
822
  PaxQtyCriteria.GREATER_OR_EQUAL
705
823
  AvesSearchType.PACKAGE
@@ -713,7 +831,7 @@ MIT
713
831
 
714
832
  ## Links
715
833
 
716
- - [Changelog](./CHANGELOG.md)
834
+ - [Changelog](https://github.com/simoneguglielmi/aves-sdk/blob/main/CHANGELOG.md)
717
835
  - [GitHub Repository](https://github.com/simoneguglielmi/aves-sdk)
718
836
  - [NPM Package](https://npmjs.com/package/aves-sdk)
719
837
  - [Issue Tracker](https://github.com/simoneguglielmi/aves-sdk/issues)