sabre-client 0.5.1 → 0.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/dist/index.js CHANGED
@@ -59,6 +59,22 @@ var SabreBusinessError = class extends SabreError {
59
59
  this.errors = options.errors ?? [];
60
60
  }
61
61
  };
62
+ var SabreUncertainOutcomeError = class extends SabreError {
63
+ name = "SabreUncertainOutcomeError";
64
+ operation;
65
+ confirmationId;
66
+ reason;
67
+ tickets;
68
+ warnings;
69
+ constructor(message, options) {
70
+ super(message, { ...options, retryable: false });
71
+ this.operation = options.operation;
72
+ this.confirmationId = options.confirmationId;
73
+ this.reason = options.reason;
74
+ this.tickets = options.tickets ?? [];
75
+ this.warnings = options.warnings ?? [];
76
+ }
77
+ };
62
78
  var SabreWarning = class extends SabreError {
63
79
  name = "SabreWarning";
64
80
  service;
@@ -200,10 +216,10 @@ function classifyItineraryMessages(body, service, catalog) {
200
216
  const error = messages.find((message) => message.severity?.toLowerCase() === "error");
201
217
  if (error === void 0) return void 0;
202
218
  const match = findInCatalog(messages, catalog);
203
- const text = match?.text ?? (typeof error.text === "string" ? error.text : "");
219
+ const text2 = match?.text ?? (typeof error.text === "string" ? error.text : "");
204
220
  const known = match?.entry;
205
- const code = known?.code ?? error.code ?? text;
206
- return new SabreBusinessError(`${service}: ${text || code}`, {
221
+ const code = known?.code ?? error.code ?? text2;
222
+ return new SabreBusinessError(`${service}: ${text2 || code}`, {
207
223
  service,
208
224
  code,
209
225
  retryable: known?.retryable ?? false,
@@ -213,8 +229,8 @@ function classifyItineraryMessages(body, service, catalog) {
213
229
  function findInCatalog(messages, catalog) {
214
230
  for (const message of messages) {
215
231
  if (typeof message.text !== "string") continue;
216
- const text = message.text.toLowerCase();
217
- const entry = catalog.find((candidate) => text.includes(candidate.code.toLowerCase()));
232
+ const text2 = message.text.toLowerCase();
233
+ const entry = catalog.find((candidate) => text2.includes(candidate.code.toLowerCase()));
218
234
  if (entry !== void 0) return { entry, text: message.text };
219
235
  }
220
236
  return void 0;
@@ -623,9 +639,9 @@ function buildTokenRequestV2(credentials) {
623
639
  function buildTokenRequest(authVersion, credentials) {
624
640
  return authVersion === "v3" ? buildTokenRequestV3(credentials) : buildTokenRequestV2(credentials);
625
641
  }
626
- function base64(text) {
642
+ function base64(text2) {
627
643
  let binary = "";
628
- for (const byte of new TextEncoder().encode(text)) binary += String.fromCharCode(byte);
644
+ for (const byte of new TextEncoder().encode(text2)) binary += String.fromCharCode(byte);
629
645
  return btoa(binary);
630
646
  }
631
647
 
@@ -798,25 +814,217 @@ var AuthManager = class {
798
814
  }
799
815
  };
800
816
 
817
+ // src/tickets/fulfill-tickets.ts
818
+ var FULFILL_TICKETS_SERVICE = "fulfillFlightTickets";
819
+ var FULFILL_TICKETS_OPERATION = "tickets.fulfill";
820
+ var PATH3 = "/v1/trip/orders/fulfillFlightTickets";
821
+ var NO_NEW_TICKETS = /no new tickets have been issued/i;
822
+ var GATEWAY_5XX = /* @__PURE__ */ new Set([500, 502, 503, 504]);
823
+ function validateFulfillTicketsRequest(request, options) {
824
+ const fail8 = (message) => {
825
+ throw new TypeError(`sabre-client: tickets.fulfill ${message}`);
826
+ };
827
+ if (!isObject2(request) || !nonEmpty(request.confirmationId)) fail8("espera { confirmationId: string, fulfillments: [...] }");
828
+ if (!Array.isArray(request.fulfillments) || request.fulfillments.length === 0 || request.fulfillments.length > 99) {
829
+ fail8("necesita fulfillments con 1 a 99 elementos");
830
+ }
831
+ for (const [index, fulfillment] of request.fulfillments.entries()) {
832
+ const qualifiers = isObject2(fulfillment) ? fulfillment.ticketingQualifiers : void 0;
833
+ if (qualifiers?.commissionAmount !== void 0 && qualifiers.commissionPercentage !== void 0) {
834
+ fail8(`no combina commissionAmount con commissionPercentage (fulfillments[${index}])`);
835
+ }
836
+ }
837
+ const printers = request.designatePrinters ?? [];
838
+ if (printers.length > 1 && printers.some((printer) => printer.profileNumber !== void 0)) {
839
+ fail8("manda un perfil de impresoras solo, en un \xFAnico elemento de designatePrinters");
840
+ }
841
+ const retries = options.retry?.maxRetries;
842
+ if (retries !== void 0 && retries !== 0) fail8("no se reintenta nunca: retry.maxRetries solo puede ser 0");
843
+ }
844
+ async function fulfillTickets(transport, request, options = {}) {
845
+ validateFulfillTicketsRequest(request, options);
846
+ const { confirmationId } = request;
847
+ const body = { acceptPriceChanges: false, priceQuoteExpirationMethod: "Quit", ...request };
848
+ let sent = false;
849
+ const userOnRawRequest = options.onRawRequest;
850
+ const hooks = rawHooks(
851
+ {
852
+ onRawResponse: options.onRawResponse,
853
+ // `onRawRequest` llega si algún intento llamó a `fetch`: así se sabe si el request salió.
854
+ onRawRequest: (raw) => {
855
+ sent = true;
856
+ userOnRawRequest?.(raw);
857
+ }
858
+ },
859
+ FULFILL_TICKETS_OPERATION
860
+ );
861
+ let response;
862
+ try {
863
+ response = await transport.request({
864
+ method: "POST",
865
+ path: PATH3,
866
+ service: FULFILL_TICKETS_SERVICE,
867
+ idempotent: false,
868
+ retry: { maxRetries: 0 },
869
+ body,
870
+ classify: (parsed) => classifyFulfillTickets(parsed, confirmationId),
871
+ ...options.signal === void 0 ? {} : { signal: options.signal },
872
+ ...hooks
873
+ });
874
+ } catch (error) {
875
+ throw uncertainInFlight(error, sent, confirmationId, FULFILL_TICKETS_OPERATION, FULFILL_TICKETS_SERVICE);
876
+ }
877
+ return toFulfillTicketsResponse(response, confirmationId);
878
+ }
879
+ function uncertainInFlight(error, sent, confirmationId, operation, service) {
880
+ if (error instanceof SabreUncertainOutcomeError) return error;
881
+ const make = (reason, detail) => new SabreUncertainOutcomeError(`${service}: no se sabe si se hizo (${detail}). Verifica antes de reintentar.`, {
882
+ operation,
883
+ confirmationId,
884
+ reason,
885
+ cause: error
886
+ });
887
+ if (error instanceof Error && error.name === "AbortError") return sent ? make("ABORTED", "abort con el request ya mandado") : error;
888
+ if (error instanceof SabreGatewayError && error.protocol === "rest") {
889
+ if (error.status === void 0) return make("TRANSPORT", "fallo de red sin respuesta");
890
+ if (GATEWAY_5XX.has(error.status)) return make("GATEWAY_5XX", `HTTP ${error.status} del gateway`);
891
+ }
892
+ return error;
893
+ }
894
+ function classifyFulfillTickets(body, confirmationId) {
895
+ if (!isObject2(body)) {
896
+ return uncertain("UNPARSEABLE", "respuesta 2xx sin un objeto JSON", { confirmationId, raw: body });
897
+ }
898
+ const response = body;
899
+ const items = allItems(response);
900
+ const warnings = items.filter(isWarning).map((item) => toWarning(FULFILL_TICKETS_SERVICE, item));
901
+ const fatal = items.filter((item) => !isWarning(item));
902
+ const tickets = ticketsOf(response);
903
+ const reported = tickets.map(uncertainTicket);
904
+ const context = { confirmationId, tickets: reported, warnings, raw: body };
905
+ const has = (type) => items.find((item) => item.type === type);
906
+ const retrieve = has("UNABLE_TO_RETRIEVE_TICKETS");
907
+ if (retrieve) return uncertain("UNABLE_TO_RETRIEVE_TICKETS", describe(retrieve), context);
908
+ const notConfirmed = has("FULFILLMENT_NOT_CONFIRMED");
909
+ if (notConfirmed) return uncertain("FULFILLMENT_NOT_CONFIRMED", describe(notConfirmed), context);
910
+ const timeout = fatal.find((item) => item.type === "INTERNAL_SERVER_TIMEOUT" && !NO_NEW_TICKETS.test(item.description ?? ""));
911
+ if (timeout) return uncertain("INTERNAL_SERVER_TIMEOUT", describe(timeout), context);
912
+ const downline = fatal.find((item) => item.type === "DOWNLINE_SERVICE_ERROR");
913
+ if (downline) return uncertain("DOWNLINE_SERVICE_ERROR", describe(downline), context);
914
+ const card = fatal.find((item) => item.type === "PAYMENT_CARD_ISSUE");
915
+ if (card) return uncertain("PAYMENT_CARD_ISSUE", describe(card), context);
916
+ const notCommitted = tickets.filter((ticket) => ticket.isCommitted === false);
917
+ if (notCommitted.length > 0) {
918
+ return uncertain("NOT_COMMITTED", `${notCommitted.length} boleto(s) emitido(s) sin guardar en el PNR (ghost ticket)`, context);
919
+ }
920
+ if (tickets.length > 0) return void 0;
921
+ const [first, ...rest] = fatal;
922
+ if (first !== void 0) {
923
+ return toBusinessError(FULFILL_TICKETS_SERVICE, first, { warnings, errors: rest.map((item) => toBusinessError(FULFILL_TICKETS_SERVICE, item)) });
924
+ }
925
+ return uncertain("FULFILLMENT_NOT_CONFIRMED", "respuesta 2xx sin boletos ni errores", context);
926
+ }
927
+ function toFulfillTicketsResponse(response, confirmationId) {
928
+ const items = allItems(response);
929
+ const warnings = items.filter(isWarning).map((item) => toWarning(FULFILL_TICKETS_SERVICE, item));
930
+ const errors = items.filter((item) => !isWarning(item)).map((item) => toBusinessError(FULFILL_TICKETS_SERVICE, item));
931
+ const partial = errors.length > 0 || warnings.some((warning) => warning.code === "PARTIAL_FULFILLMENT");
932
+ return {
933
+ status: partial ? "partial" : "issued",
934
+ confirmationId,
935
+ tickets: ticketsOf(response),
936
+ warnings,
937
+ errors,
938
+ ...response.timestamp === void 0 ? {} : { timestamp: response.timestamp },
939
+ ...response.request === void 0 ? {} : { request: response.request }
940
+ };
941
+ }
942
+ function allItems(response) {
943
+ return [...responseErrors(response), ...Array.isArray(response.warnings) ? responseErrors({ errors: response.warnings }) : []];
944
+ }
945
+ function ticketsOf(response) {
946
+ return Array.isArray(response.tickets) ? response.tickets.filter((ticket) => isObject2(ticket) && nonEmpty(ticket.number)) : [];
947
+ }
948
+ function uncertainTicket(ticket) {
949
+ return {
950
+ number: ticket.number,
951
+ ...ticket.ticketStatusCode === void 0 ? {} : { ticketStatusCode: ticket.ticketStatusCode },
952
+ ...ticket.isCommitted === void 0 ? {} : { isCommitted: ticket.isCommitted }
953
+ };
954
+ }
955
+ function uncertain(reason, detail, context) {
956
+ return new SabreUncertainOutcomeError(`${FULFILL_TICKETS_SERVICE}: no se sabe si emiti\xF3 (${reason}: ${detail}). Verifica con tickets.verify antes de reintentar.`, {
957
+ operation: FULFILL_TICKETS_OPERATION,
958
+ ...context,
959
+ reason
960
+ });
961
+ }
962
+ function describe(item) {
963
+ return nonEmpty(item.description) ? item.description : item.type;
964
+ }
965
+
801
966
  // src/booking/cancel-booking.ts
802
967
  var CANCEL_BOOKING_SERVICE = "cancelBooking";
803
- var PATH3 = "/v1/trip/orders/cancelBooking";
968
+ var CANCEL_BOOKING_OPERATION = "booking.cancel";
969
+ var END_TRANSACTION_PROBLEM = "END_TRANSACTION_PROBLEM";
970
+ var PATH4 = "/v1/trip/orders/cancelBooking";
804
971
  var NO_ITEMS_CANCELLED = "NO_ITEMS_CANCELLED";
805
972
  var ITEM_LISTS = ["flights", "hotels", "cars", "trains", "cruises", "segments"];
806
973
  async function cancelBooking(transport, request, options = {}) {
807
974
  validateCancelBookingRequest(request);
808
- const body = await transport.request({
809
- method: "POST",
810
- path: PATH3,
811
- service: CANCEL_BOOKING_SERVICE,
812
- idempotent: false,
813
- body: request,
814
- classify: classifyCancelBooking,
815
- ...options.signal === void 0 ? {} : { signal: options.signal },
816
- ...rawHooks(options, "booking.cancel")
817
- });
975
+ const voiding = request.flightTicketOperation === "VOID";
976
+ let sent = false;
977
+ const hooks = voiding ? rawHooks(
978
+ {
979
+ onRawResponse: options.onRawResponse,
980
+ // `onRawRequest` llega si algún intento llamó a `fetch`: así se sabe si el void salió.
981
+ onRawRequest: (raw) => {
982
+ sent = true;
983
+ options.onRawRequest?.(raw);
984
+ }
985
+ },
986
+ CANCEL_BOOKING_OPERATION
987
+ ) : rawHooks(options, CANCEL_BOOKING_OPERATION);
988
+ let body;
989
+ try {
990
+ body = await transport.request({
991
+ method: "POST",
992
+ path: PATH4,
993
+ service: CANCEL_BOOKING_SERVICE,
994
+ idempotent: false,
995
+ body: request,
996
+ classify: voiding ? (parsed) => classifyVoid(parsed, request.confirmationId) : classifyCancelBooking,
997
+ ...options.signal === void 0 ? {} : { signal: options.signal },
998
+ ...hooks
999
+ });
1000
+ } catch (error) {
1001
+ if (!voiding) throw error;
1002
+ throw uncertainInFlight(error, sent, request.confirmationId, CANCEL_BOOKING_OPERATION, CANCEL_BOOKING_SERVICE);
1003
+ }
818
1004
  return toCancelBookingResponse(body);
819
1005
  }
1006
+ function classifyVoid(body, confirmationId) {
1007
+ const voidUncertain = (reason, detail, warnings = []) => new SabreUncertainOutcomeError(`${CANCEL_BOOKING_SERVICE}: no se sabe si el void se hizo (${reason}: ${detail}). Verifica con tickets.verify o booking.get antes de reintentar.`, {
1008
+ operation: CANCEL_BOOKING_OPERATION,
1009
+ confirmationId,
1010
+ reason,
1011
+ tickets: isObject2(body) ? voidedOf(body).map((number) => ({ number })) : [],
1012
+ warnings,
1013
+ raw: body
1014
+ });
1015
+ if (!isObject2(body)) return voidUncertain("UNPARSEABLE", "respuesta 2xx sin un objeto JSON");
1016
+ const failure = classifyCancelBooking(body);
1017
+ if (failure !== void 0) return failure;
1018
+ const items = responseErrors(body);
1019
+ const pending = items.find((item) => item.type === END_TRANSACTION_PROBLEM);
1020
+ if (pending !== void 0) {
1021
+ return voidUncertain("END_TRANSACTION_PROBLEM", pending.description ?? "Confirmation is pending", items.filter(isWarning).map(warningOf));
1022
+ }
1023
+ return void 0;
1024
+ }
1025
+ function voidedOf(body) {
1026
+ return Array.isArray(body.voidedTickets) ? body.voidedTickets.filter(nonEmpty) : [];
1027
+ }
820
1028
  function validateCancelBookingRequest(request) {
821
1029
  if (!isObject2(request) || !nonEmpty(request.confirmationId)) {
822
1030
  throw new TypeError("sabre-client: booking.cancel espera { confirmationId: string, ... }");
@@ -975,7 +1183,7 @@ function amount(value) {
975
1183
 
976
1184
  // src/booking/create-booking.ts
977
1185
  var CREATE_BOOKING_SERVICE = "createBooking";
978
- var PATH4 = "/v1/trip/orders/createBooking";
1186
+ var PATH5 = "/v1/trip/orders/createBooking";
979
1187
  var UNABLE_TO_RETRIEVE_BOOKING = "UNABLE_TO_RETRIEVE_BOOKING";
980
1188
  async function createBooking(transport, request, options = {}) {
981
1189
  validateCreateBookingRequest(request);
@@ -984,7 +1192,7 @@ async function createBooking(transport, request, options = {}) {
984
1192
  }
985
1193
  const body = await transport.request({
986
1194
  method: "POST",
987
- path: PATH4,
1195
+ path: PATH5,
988
1196
  service: CREATE_BOOKING_SERVICE,
989
1197
  idempotent: false,
990
1198
  body: request,
@@ -1068,7 +1276,7 @@ function warningOf2(item) {
1068
1276
 
1069
1277
  // src/booking/get-booking.ts
1070
1278
  var GET_BOOKING_SERVICE = "getBooking";
1071
- var PATH5 = "/v1/trip/orders/getBooking";
1279
+ var PATH6 = "/v1/trip/orders/getBooking";
1072
1280
  var RETRYABLE_TYPES = /* @__PURE__ */ new Set(["INTERNAL_SERVER_TIMEOUT", "DOWNLINE_SERVICE_FAILURE"]);
1073
1281
  async function getBooking(transport, request, options = {}) {
1074
1282
  if (!isObject2(request) || !nonEmpty(request.confirmationId)) {
@@ -1076,7 +1284,7 @@ async function getBooking(transport, request, options = {}) {
1076
1284
  }
1077
1285
  const body = await transport.request({
1078
1286
  method: "POST",
1079
- path: PATH5,
1287
+ path: PATH6,
1080
1288
  service: GET_BOOKING_SERVICE,
1081
1289
  idempotent: true,
1082
1290
  body: request,
@@ -1160,6 +1368,15 @@ var BookingManagement = class {
1160
1368
  * Nunca se reintenta automáticamente. Lanza `SabreBusinessError` si algo falló o si Sabre
1161
1369
  * responde `NO_ITEMS_CANCELLED` (no se canceló nada), aunque llegue como aviso. Con
1162
1370
  * `END_TRANSACTION_PROBLEM` en `warnings`, la confirmación quedó pendiente: verifica con `get`.
1371
+ *
1372
+ * **Void** (`flightTicketOperation: 'VOID'`, ADR-031): anula los boletos y es **irreversible**.
1373
+ * Con `cancelAll` y `HALT_ON_ERROR`, una sola llamada anula y cancela la reserva; `voidedTickets`
1374
+ * trae los números anulados (CERT, 2026-11-13). Un fallo de red, un 5xx, un cuerpo ilegible, un
1375
+ * abort con el request ya mandado o `END_TRANSACTION_PROBLEM` lanzan `SabreUncertainOutcomeError`:
1376
+ * no se sabe si se anuló. Verifica con `sabre.tickets.verify` (un boleto anulado queda en `TV`) o
1377
+ * con `get` antes de reintentar. `VOID` sobre una reserva sin boletos lanza `NO_ITEMS_CANCELLED`
1378
+ * con `UNABLE_TO_VOID_TICKET` y **no cancela nada**: para una reserva sin emitir, cancela sin
1379
+ * `VOID`.
1163
1380
  */
1164
1381
  cancel(request, options) {
1165
1382
  return cancelBooking(this.#transport, request, options);
@@ -1409,7 +1626,7 @@ var RestTransport = class {
1409
1626
  });
1410
1627
  } catch (cause) {
1411
1628
  if (isAbort(cause, request.signal)) throw cause;
1412
- const error = new SabreGatewayError(`${request.service}: fallo de red (${describe(cause)})`, {
1629
+ const error = new SabreGatewayError(`${request.service}: fallo de red (${describe2(cause)})`, {
1413
1630
  protocol: "rest",
1414
1631
  retryable: true,
1415
1632
  cause
@@ -1417,9 +1634,9 @@ var RestTransport = class {
1417
1634
  this.#log.emit("error", "sabre.request.end", { ...logContext, durationMs: Date.now() - startedAt, error });
1418
1635
  throw error;
1419
1636
  }
1420
- const text = await response.text();
1421
- if (capture !== void 0) capture.last = { status: response.status, headers: response.headers, body: text };
1422
- const parsed = parseBody(text);
1637
+ const text2 = await response.text();
1638
+ if (capture !== void 0) capture.last = { status: response.status, headers: response.headers, body: text2 };
1639
+ const parsed = parseBody(text2);
1423
1640
  const endContext = { ...logContext, status: response.status, durationMs: Date.now() - startedAt };
1424
1641
  if (!response.ok) {
1425
1642
  const error = gatewayError(request.service, response.status, parsed);
@@ -1443,12 +1660,12 @@ var RestTransport = class {
1443
1660
  return url.toString();
1444
1661
  }
1445
1662
  };
1446
- function parseBody(text) {
1447
- if (text === "") return void 0;
1663
+ function parseBody(text2) {
1664
+ if (text2 === "") return void 0;
1448
1665
  try {
1449
- return JSON.parse(text);
1666
+ return JSON.parse(text2);
1450
1667
  } catch {
1451
- return text;
1668
+ return text2;
1452
1669
  }
1453
1670
  }
1454
1671
  function gatewayError(service, status, body) {
@@ -1490,10 +1707,93 @@ function isUnauthorized(error) {
1490
1707
  function isAbort(error, signal) {
1491
1708
  return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
1492
1709
  }
1493
- function describe(error) {
1710
+ function describe2(error) {
1494
1711
  return error instanceof Error ? error.message : String(error);
1495
1712
  }
1496
1713
 
1714
+ // src/tickets/verify-tickets.ts
1715
+ var LIVE_STATUS = /* @__PURE__ */ new Set(["TE", "ME", "TO"]);
1716
+ async function verifyTickets(transport, request, options = {}, now = () => /* @__PURE__ */ new Date()) {
1717
+ if (!isObject2(request) || !nonEmpty(request.confirmationId)) {
1718
+ throw new TypeError("sabre-client: tickets.verify espera { confirmationId: string, ... }");
1719
+ }
1720
+ for (const key of ["knownTickets", "expectedTickets"]) {
1721
+ const list = request[key];
1722
+ if (list !== void 0 && (!Array.isArray(list) || !list.every(nonEmpty))) {
1723
+ throw new TypeError(`sabre-client: tickets.verify espera ${key} como lista de n\xFAmeros de boleto`);
1724
+ }
1725
+ }
1726
+ const booking = await getBooking(
1727
+ transport,
1728
+ { confirmationId: request.confirmationId, ...request.targetPcc === void 0 ? {} : { targetPcc: request.targetPcc } },
1729
+ options
1730
+ );
1731
+ const checkedAt = now().toISOString();
1732
+ const known = new Set(request.knownTickets ?? []);
1733
+ const live = (booking.flightTickets ?? []).filter(isLive).map(toVerified);
1734
+ const fresh = live.filter((ticket) => !known.has(ticket.number));
1735
+ const expected = request.expectedTickets ?? [];
1736
+ const missing = expected.filter((number) => !live.some((ticket) => ticket.number === number));
1737
+ if (missing.length > 0) return { status: "notCommitted", tickets: fresh, missing, checkedAt };
1738
+ if (fresh.length > 0 || expected.length > 0) return { status: "issued", tickets: fresh, checkedAt };
1739
+ return { status: "notFound", checkedAt };
1740
+ }
1741
+ function isLive(ticket) {
1742
+ return isObject2(ticket) && nonEmpty(ticket.number) && ticket.ticketStatusCode !== void 0 && LIVE_STATUS.has(ticket.ticketStatusCode);
1743
+ }
1744
+ function toVerified(ticket) {
1745
+ return {
1746
+ number: ticket.number,
1747
+ ...ticket.ticketStatusCode === void 0 ? {} : { ticketStatusCode: ticket.ticketStatusCode },
1748
+ ...ticket.ticketStatusName === void 0 ? {} : { ticketStatusName: ticket.ticketStatusName },
1749
+ ...ticket.date === void 0 ? {} : { date: ticket.date },
1750
+ ...ticket.ticketingPcc === void 0 ? {} : { ticketingPcc: ticket.ticketingPcc }
1751
+ };
1752
+ }
1753
+
1754
+ // src/tickets/tickets-management.ts
1755
+ var TicketsManagement = class {
1756
+ #transport;
1757
+ /** @internal Lo construye el cliente `Sabre`. */
1758
+ constructor(transport) {
1759
+ this.#transport = transport;
1760
+ }
1761
+ /**
1762
+ * Fulfill Flight Tickets: emite los boletos (y EMD) de una reserva con su PQ guardado. **Mueve
1763
+ * dinero y nunca se reintenta**, ni ante un fallo de transporte ni ante un error que Sabre marque
1764
+ * como temporal (ADR-031). Pasar `retry.maxRetries` distinto de 0 lanza `TypeError` sin red.
1765
+ *
1766
+ * El SDK manda `acceptPriceChanges: false` y `priceQuoteExpirationMethod: 'Quit'` salvo que se
1767
+ * pasen otros valores: una emisión automática no paga un precio que nadie vio.
1768
+ *
1769
+ * Desenlaces:
1770
+ * - **Emitido** (`status: 'issued'`): todos los boletos con `isCommitted: true`.
1771
+ * - **Parcial** (`status: 'partial'`): hay boletos emitidos y algo faltó (`PARTIAL_FULFILLMENT`
1772
+ * o un error junto con boletos). **No es una emisión completa**: hay pasajeros sin boleto.
1773
+ * Guarda los emitidos y verifica antes de completar.
1774
+ * - **No emitido**: lanza `SabreBusinessError` (p. ej. `PRICE_MISMATCH`, `BOOKING_FULFILLED`) o
1775
+ * `SabreGatewayError` (400, 401, 403, 429).
1776
+ * - **Incierto**: lanza `SabreUncertainOutcomeError`. Verifica con `verify`, pasando sus
1777
+ * `tickets` como `expectedTickets`, antes de pensar en reintentar.
1778
+ *
1779
+ * `BOOKING_FULFILLED` quiere decir que la reserva ya tiene boletos, **no** que sean de esta
1780
+ * emisión: verifica con `verify`. Los errores de configuración del PCC (comisión, impresora,
1781
+ * Secure Flight) llegan como `DOWNLINE_SERVICE_ERROR` y hoy se tratan como incierto (CERT,
1782
+ * 2026-11-12).
1783
+ */
1784
+ fulfill(request, options) {
1785
+ return fulfillTickets(this.#transport, request, options);
1786
+ }
1787
+ /**
1788
+ * ¿Se emitió? Lee la reserva con Get Booking y devuelve `issued`, `notCommitted` o `notFound`,
1789
+ * por el `ticketStatusCode` de cada boleto. Nunca emite. Un `notFound` no prueba que no se haya
1790
+ * emitido: ver `VerifyTicketsResponse`.
1791
+ */
1792
+ verify(request, options) {
1793
+ return verifyTickets(this.#transport, request, options);
1794
+ }
1795
+ };
1796
+
1497
1797
  // src/sabre.ts
1498
1798
  var Sabre = class {
1499
1799
  #config;
@@ -1502,6 +1802,8 @@ var Sabre = class {
1502
1802
  air;
1503
1803
  /** Booking Management: creación de reservas. */
1504
1804
  booking;
1805
+ /** Emisión de boletos (Fulfill Flight Tickets) y su verificación. Mueve dinero: ver ADR-031. */
1806
+ tickets;
1505
1807
  constructor(config) {
1506
1808
  this.#config = resolveConfig(config);
1507
1809
  this.#transport = new RestTransport(this.#config);
@@ -1509,6 +1811,7 @@ var Sabre = class {
1509
1811
  this.#transport.setAuthorizationProvider(auth.bearerProvider());
1510
1812
  this.air = new Air(this.#transport);
1511
1813
  this.booking = new BookingManagement(this.#transport);
1814
+ this.tickets = new TicketsManagement(this.#transport);
1512
1815
  }
1513
1816
  get environment() {
1514
1817
  return this.#config.environment;
@@ -1635,7 +1938,7 @@ var SoapTransport = class {
1635
1938
  });
1636
1939
  } catch (cause) {
1637
1940
  if (isAbort2(cause, request.signal)) throw cause;
1638
- const error = new SabreGatewayError(`${request.service}: fallo de red (${describe2(cause)})`, {
1941
+ const error = new SabreGatewayError(`${request.service}: fallo de red (${describe3(cause)})`, {
1639
1942
  protocol: "soap",
1640
1943
  retryable: false,
1641
1944
  cause
@@ -1644,9 +1947,9 @@ var SoapTransport = class {
1644
1947
  throw error;
1645
1948
  }
1646
1949
  const original = await response.text();
1647
- const text = redactSoapEnvelope(scrubToken(original, request.security));
1648
- capture.last = { status: response.status, headers: response.headers, body: text };
1649
- const document = parseEnvelope(text);
1950
+ const text2 = redactSoapEnvelope(scrubToken(original, request.security));
1951
+ capture.last = { status: response.status, headers: response.headers, body: text2 };
1952
+ const document = parseEnvelope(text2);
1650
1953
  const messageId = stringAt(document?.Header, "MessageHeader", "MessageData", "MessageId");
1651
1954
  const endContext = {
1652
1955
  ...logContext,
@@ -1655,13 +1958,13 @@ var SoapTransport = class {
1655
1958
  ...messageId === void 0 ? {} : { messageId }
1656
1959
  };
1657
1960
  try {
1658
- const result = this.#classify(request, response, document, text);
1961
+ const result = this.#classify(request, response, document, text2);
1659
1962
  this.#log.emit("info", "sabre.request.end", { ...endContext, warnings: result.warnings.length });
1660
1963
  return {
1661
1964
  ...result,
1662
1965
  messageId,
1663
- respondedAt: respondedAtOf(text),
1664
- raw: text,
1966
+ respondedAt: respondedAtOf(text2),
1967
+ raw: text2,
1665
1968
  securityToken: request.readSecurityToken === true ? readSecurityToken(original) : void 0,
1666
1969
  status: response.status
1667
1970
  };
@@ -1672,18 +1975,18 @@ var SoapTransport = class {
1672
1975
  }
1673
1976
  }
1674
1977
  /** Orden de detección: fault (gateway), luego `stl:Error` (negocio), luego data. */
1675
- #classify(request, response, document, text) {
1978
+ #classify(request, response, document, text2) {
1676
1979
  const { service } = request;
1677
1980
  if (document === void 0) {
1678
1981
  throw new SabreGatewayError(`${service}: HTTP ${response.status}, la respuesta no es un sobre SOAP`, {
1679
1982
  protocol: "soap",
1680
1983
  status: response.status,
1681
1984
  retryable: false,
1682
- raw: text
1985
+ raw: text2
1683
1986
  });
1684
1987
  }
1685
1988
  const fault = asNode(document.Body.Fault);
1686
- if (fault !== void 0) throw faultError(service, response.status, fault, text);
1989
+ if (fault !== void 0) throw faultError(service, response.status, fault, text2);
1687
1990
  const [payloadName, payloadValue] = firstElement(document.Body);
1688
1991
  const payload = asNode(payloadValue);
1689
1992
  if (payloadName === void 0 || payload === void 0) {
@@ -1691,7 +1994,7 @@ var SoapTransport = class {
1691
1994
  protocol: "soap",
1692
1995
  status: response.status,
1693
1996
  retryable: false,
1694
- raw: text
1997
+ raw: text2
1695
1998
  });
1696
1999
  }
1697
2000
  if (!response.ok) {
@@ -1699,20 +2002,20 @@ var SoapTransport = class {
1699
2002
  protocol: "soap",
1700
2003
  status: response.status,
1701
2004
  retryable: false,
1702
- raw: text
2005
+ raw: text2
1703
2006
  });
1704
2007
  }
1705
2008
  const results = asNode(payload.ApplicationResults);
1706
- const warnings = asArray(results?.Warning).map((node3) => {
1707
- const entry = resultEntry(node3);
1708
- return new SabreWarning(`${service}: ${entry.description}`, { service, code: entry.code, raw: text });
2009
+ const warnings = asArray(results?.Warning).map((node4) => {
2010
+ const entry = resultEntry(node4);
2011
+ return new SabreWarning(`${service}: ${entry.description}`, { service, code: entry.code, raw: text2 });
1709
2012
  });
1710
- const errors = asArray(results?.Error).map((node3) => {
1711
- const entry = resultEntry(node3);
2013
+ const errors = asArray(results?.Error).map((node4) => {
2014
+ const entry = resultEntry(node4);
1712
2015
  return new SabreBusinessError(`${service}: ${entry.description}`, {
1713
2016
  service,
1714
2017
  code: entry.code,
1715
- raw: text,
2018
+ raw: text2,
1716
2019
  retryable: PREVIOUS_ENTRY_ACTIVE.test(entry.description)
1717
2020
  });
1718
2021
  });
@@ -1721,7 +2024,7 @@ var SoapTransport = class {
1721
2024
  throw new SabreBusinessError(first.message, {
1722
2025
  service,
1723
2026
  code: first.code,
1724
- raw: text,
2027
+ raw: text2,
1725
2028
  retryable: first.retryable,
1726
2029
  warnings,
1727
2030
  errors: rest
@@ -1818,16 +2121,16 @@ function securityHeader(security) {
1818
2121
  function wantsRawRequest(request) {
1819
2122
  return request.onRawRequest !== void 0 && "binarySecurityToken" in request.security;
1820
2123
  }
1821
- function scrubToken(text, security) {
1822
- if (!("binarySecurityToken" in security) || security.binarySecurityToken === "") return text;
2124
+ function scrubToken(text2, security) {
2125
+ if (!("binarySecurityToken" in security) || security.binarySecurityToken === "") return text2;
1823
2126
  const token = security.binarySecurityToken;
1824
- return text.split(token).join(REDACTED).split(xmlEscape(token)).join(REDACTED);
2127
+ return text2.split(token).join(REDACTED).split(xmlEscape(token)).join(REDACTED);
1825
2128
  }
1826
- function parseEnvelope(text, xmlParser = parser) {
1827
- if (text.trim() === "") return void 0;
2129
+ function parseEnvelope(text2, xmlParser = parser) {
2130
+ if (text2.trim() === "") return void 0;
1828
2131
  let parsed;
1829
2132
  try {
1830
- parsed = xmlParser.parse(text);
2133
+ parsed = xmlParser.parse(text2);
1831
2134
  } catch {
1832
2135
  return void 0;
1833
2136
  }
@@ -1842,7 +2145,7 @@ function readSecurityToken(original) {
1842
2145
  return token === void 0 || token.trim() === "" ? void 0 : token.trim();
1843
2146
  }
1844
2147
  var USG_CODE = /\bUSG_[A-Z0-9_]+\b/;
1845
- function faultError(service, status, fault, text) {
2148
+ function faultError(service, status, fault, text2) {
1846
2149
  const faultcode = textOf(fault.faultcode);
1847
2150
  const faultstring = textOf(fault.faultstring);
1848
2151
  const haystack = [faultcode, faultstring, JSON.stringify(fault.detail ?? "")].join(" ");
@@ -1853,12 +2156,12 @@ function faultError(service, status, fault, text) {
1853
2156
  protocol: "soap",
1854
2157
  status,
1855
2158
  retryable,
1856
- raw: text,
2159
+ raw: text2,
1857
2160
  ...code === void 0 || code === "" ? {} : { code }
1858
2161
  });
1859
2162
  }
1860
- function resultEntry(node3) {
1861
- const entry = asNode(node3) ?? {};
2163
+ function resultEntry(node4) {
2164
+ const entry = asNode(node4) ?? {};
1862
2165
  const messages = [];
1863
2166
  const shortTexts = [];
1864
2167
  let code;
@@ -1896,8 +2199,8 @@ function untrimmedPayload(raw) {
1896
2199
  const document = parseEnvelope(raw, untrimmedParser);
1897
2200
  return document === void 0 ? void 0 : asNode(firstElement(document.Body)[1]);
1898
2201
  }
1899
- function firstElement(node3) {
1900
- for (const [key, value] of Object.entries(node3)) {
2202
+ function firstElement(node4) {
2203
+ for (const [key, value] of Object.entries(node4)) {
1901
2204
  if (!key.startsWith("@") && key !== "#text") return [key, Array.isArray(value) ? value[0] : value];
1902
2205
  }
1903
2206
  return [void 0, void 0];
@@ -1912,14 +2215,14 @@ function asArray(value) {
1912
2215
  }
1913
2216
  function textOf(value) {
1914
2217
  if (typeof value === "string") return value;
1915
- const node3 = asNode(value);
1916
- const text = node3?.["#text"];
1917
- return typeof text === "string" ? text : node3 === void 0 ? void 0 : "";
2218
+ const node4 = asNode(value);
2219
+ const text2 = node4?.["#text"];
2220
+ return typeof text2 === "string" ? text2 : node4 === void 0 ? void 0 : "";
1918
2221
  }
1919
2222
  function stringAt(root2, ...path) {
1920
- let node3 = root2;
1921
- for (const key of path) node3 = asNode(node3)?.[key];
1922
- return nonEmpty2(textOf(node3));
2223
+ let node4 = root2;
2224
+ for (const key of path) node4 = asNode(node4)?.[key];
2225
+ return nonEmpty2(textOf(node4));
1923
2226
  }
1924
2227
  function nonEmpty2(value) {
1925
2228
  return typeof value === "string" && value !== "" ? value : void 0;
@@ -1927,7 +2230,7 @@ function nonEmpty2(value) {
1927
2230
  function isAbort2(error, signal) {
1928
2231
  return signal?.aborted === true || error instanceof Error && error.name === "AbortError";
1929
2232
  }
1930
- function describe2(error) {
2233
+ function describe3(error) {
1931
2234
  return error instanceof Error ? error.message : String(error);
1932
2235
  }
1933
2236
 
@@ -2004,13 +2307,31 @@ function queueRefAttribute(queue) {
2004
2307
  if (typeof queue === "string") return `Number="${xmlEscape(queue)}"`;
2005
2308
  return `Name="${xmlEscape(queue.name)}"`;
2006
2309
  }
2310
+ function validateQueueRange(value, label) {
2311
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail3(`${label} debe ser { start, end }`);
2312
+ const { start, end } = value;
2313
+ for (const [name, bound] of [["start", start], ["end", end]]) {
2314
+ if (typeof bound !== "number" || !Number.isInteger(bound) || bound < MIN_QUEUE_NUMBER || bound > MAX_QUEUE_NUMBER) {
2315
+ fail3(`${label}.${name} debe ser un n\xFAmero de cola entero de ${MIN_QUEUE_NUMBER} a ${MAX_QUEUE_NUMBER}`);
2316
+ }
2317
+ }
2318
+ if (start >= end) fail3(`${label}.start debe ser menor que ${label}.end (para una sola cola, usa queue)`);
2319
+ }
2320
+ function queueIdentifierXml(pcc, target = {}) {
2321
+ const pccAttribute = `PseudoCityCode="${xmlEscape(pcc)}"`;
2322
+ if (target.range !== void 0) {
2323
+ return `<QueueIdentifier ${pccAttribute}><Range Start="${target.range.start}" End="${target.range.end}"/></QueueIdentifier>`;
2324
+ }
2325
+ if (target.queue !== void 0) return `<QueueIdentifier ${queueRefAttribute(target.queue)} ${pccAttribute}/>`;
2326
+ return `<QueueIdentifier ${pccAttribute}/>`;
2327
+ }
2007
2328
  function isMessageQueue(queue) {
2008
2329
  return queue === MESSAGE_QUEUE;
2009
2330
  }
2010
2331
  function toQueueRef(value) {
2011
- const text = value?.trim();
2012
- if (text === void 0 || text === "") return void 0;
2013
- return /^\d+$/.test(text) ? Number(text) : text;
2332
+ const text2 = value?.trim();
2333
+ if (text2 === void 0 || text2 === "") return void 0;
2334
+ return /^\d+$/.test(text2) ? Number(text2) : text2;
2014
2335
  }
2015
2336
 
2016
2337
  // src/queue/queue-access.ts
@@ -2056,12 +2377,12 @@ function node(value) {
2056
2377
  return typeof value === "object" && value !== null ? value : void 0;
2057
2378
  }
2058
2379
  function attribute(value, name) {
2059
- const text = node(value)?.[`@${name}`];
2060
- return typeof text === "string" && text.trim() !== "" ? text.trim() : void 0;
2380
+ const text2 = node(value)?.[`@${name}`];
2381
+ return typeof text2 === "string" && text2.trim() !== "" ? text2.trim() : void 0;
2061
2382
  }
2062
2383
  function element(value) {
2063
- const text = textOf(Array.isArray(value) ? value[0] : value)?.trim();
2064
- return text === void 0 || text === "" ? void 0 : text;
2384
+ const text2 = textOf(Array.isArray(value) ? value[0] : value)?.trim();
2385
+ return text2 === void 0 || text2 === "" ? void 0 : text2;
2065
2386
  }
2066
2387
  function textLines(raw, payloadName) {
2067
2388
  let parsed;
@@ -2077,16 +2398,16 @@ function textLines(raw, payloadName) {
2077
2398
  function paragraphLines(raw) {
2078
2399
  return textLines(raw, "QueueAccessRS");
2079
2400
  }
2080
- function isEndOfDisplay(text) {
2081
- return text?.trim() === END_OF_DISPLAY;
2401
+ function isEndOfDisplay(text2) {
2402
+ return text2?.trim() === END_OF_DISPLAY;
2082
2403
  }
2083
2404
  function isEndOfDisplayResult(error) {
2084
2405
  return resultHasText(error, END_OF_DISPLAY);
2085
2406
  }
2086
- function resultHasText(error, text) {
2407
+ function resultHasText(error, text2) {
2087
2408
  const prefix = `${error.service}: `;
2088
2409
  const description = error.message.startsWith(prefix) ? error.message.slice(prefix.length) : error.message;
2089
- return description.split(/ \/ | \(|\)/).some((part) => part.trim() === text);
2410
+ return description.split(/ \/ | \(|\)/).some((part) => part.trim() === text2);
2090
2411
  }
2091
2412
  function queueEndError(error, service) {
2092
2413
  if (!(error instanceof SabreBusinessError) || error.service !== service) return void 0;
@@ -2150,14 +2471,14 @@ function lineFields(value) {
2150
2471
  var QUEUE_COUNT_SERVICE = "queueCount";
2151
2472
  var NAMESPACE2 = "http://webservices.sabre.com/sabreXML/2011/10";
2152
2473
  var TOTAL_TYPES = { MESSAGES: "messages", SPECIALS: "specials", PNRS: "pnrs" };
2153
- function queueCountPayload(pcc) {
2154
- return `<QueueCountRQ Version="2.2.1" xmlns="${NAMESPACE2}"><QueueInfo><QueueIdentifier PseudoCityCode="${xmlEscape(pcc)}"/></QueueInfo></QueueCountRQ>`;
2474
+ function queueCountPayload(pcc, target = {}) {
2475
+ return `<QueueCountRQ Version="2.2.1" xmlns="${NAMESPACE2}"><QueueInfo>${queueIdentifierXml(pcc, target)}</QueueInfo>${selectionXml(target.selection ?? [])}</QueueCountRQ>`;
2155
2476
  }
2156
- function queueCountRequest(pcc, options) {
2477
+ function queueCountRequest(pcc, options, target = {}) {
2157
2478
  return {
2158
2479
  service: QUEUE_COUNT_SERVICE,
2159
2480
  action: "QueueCountLLSRQ",
2160
- payload: queueCountPayload(pcc),
2481
+ payload: queueCountPayload(pcc, target),
2161
2482
  idempotent: true,
2162
2483
  operation: "queue.count",
2163
2484
  ...options
@@ -2167,15 +2488,18 @@ function parseHostCount(value) {
2167
2488
  const match = /^[.\s]*(\d+)\s*$/.exec(value ?? "");
2168
2489
  return match === null ? void 0 : Number(match[1]);
2169
2490
  }
2170
- function attribute2(node3, name) {
2171
- const value = node3?.[`@${name}`];
2491
+ function attribute2(node4, name) {
2492
+ const value = node4?.[`@${name}`];
2172
2493
  return typeof value === "string" ? value : void 0;
2173
2494
  }
2174
2495
  function parseQueueCountResponse(response) {
2175
2496
  const queues = [];
2497
+ const additionalInfo = [];
2176
2498
  let countedAt;
2499
+ additionalInfo.push(...readAdditionalInfo(response.payload.AdditionalQueueInfo));
2177
2500
  for (const info of asArray(response.payload.QueueInfo)) {
2178
2501
  countedAt ??= nonEmpty3(attribute2(info, "DateTime"));
2502
+ additionalInfo.push(...readAdditionalInfo(info.AdditionalQueueInfo));
2179
2503
  for (const identifier2 of asArray(info.QueueIdentifier)) {
2180
2504
  const number = attribute2(identifier2, "Number");
2181
2505
  const name = nonEmpty3(attribute2(identifier2, "Name"));
@@ -2185,11 +2509,13 @@ function parseQueueCountResponse(response) {
2185
2509
  throw malformed(`QueueIdentifier con Number="${number ?? ""}" y Count="${attribute2(identifier2, "Count") ?? ""}"`, response);
2186
2510
  }
2187
2511
  queues.push({ queue, count });
2512
+ additionalInfo.push(...readAdditionalInfo(identifier2.AdditionalQueueInfo, queue));
2188
2513
  }
2189
2514
  }
2190
2515
  const totals = {};
2191
2516
  for (const total of asArray(response.payload.Totals)) {
2192
- const key = TOTAL_TYPES[(attribute2(total, "Type") ?? "").toUpperCase()];
2517
+ const type = attribute2(total, "Type");
2518
+ const key = type === void 0 ? "unfiltered" : TOTAL_TYPES[type.toUpperCase()];
2193
2519
  if (key === void 0) continue;
2194
2520
  const count = parseHostCount(attribute2(total, "Count"));
2195
2521
  if (count === void 0) throw malformed(`Totals ${key} con Count="${attribute2(total, "Count") ?? ""}"`, response);
@@ -2197,12 +2523,26 @@ function parseQueueCountResponse(response) {
2197
2523
  }
2198
2524
  return {
2199
2525
  queues,
2526
+ ...additionalInfo.length === 0 ? {} : { additionalInfo },
2200
2527
  totals,
2201
2528
  ...countedAt === void 0 ? {} : { countedAt },
2202
2529
  warnings: response.warnings,
2203
2530
  ...respondedAtField(response.raw)
2204
2531
  };
2205
2532
  }
2533
+ function readAdditionalInfo(value, queue) {
2534
+ return asArray(value).map((element2) => {
2535
+ const freeText = nonEmpty3(attribute2(element2, "FreeText"));
2536
+ const pseudoCityCode = nonEmpty3(attribute2(element2, "PseudoCityCode"));
2537
+ const queueNumber = nonEmpty3(attribute2(element2, "QueueNumber"));
2538
+ return {
2539
+ ...queue === void 0 ? {} : { queue },
2540
+ ...freeText === void 0 ? {} : { freeText },
2541
+ ...pseudoCityCode === void 0 ? {} : { pseudoCityCode },
2542
+ ...queueNumber === void 0 ? {} : { queueNumber }
2543
+ };
2544
+ });
2545
+ }
2206
2546
  function malformed(detail, response) {
2207
2547
  return new SabreGatewayError(`${QUEUE_COUNT_SERVICE}: respuesta no reconocida (${detail})`, {
2208
2548
  protocol: "soap",
@@ -2212,8 +2552,8 @@ function malformed(detail, response) {
2212
2552
  });
2213
2553
  }
2214
2554
  function nonEmpty3(value) {
2215
- const text = value?.trim();
2216
- return text === void 0 || text === "" ? void 0 : text;
2555
+ const text2 = value?.trim();
2556
+ return text2 === void 0 || text2 === "" ? void 0 : text2;
2217
2557
  }
2218
2558
 
2219
2559
  // src/queue/queue-list.ts
@@ -2271,26 +2611,26 @@ var COUNTERS = [
2271
2611
  ["LeftMessageToContact", "leftMessageToContact"],
2272
2612
  ["UnableToReach", "unableToReach"]
2273
2613
  ];
2274
- function queueActivityPayload(pcc) {
2275
- return `<QueueAnalysisRQ Version="2.0.0" xmlns="${NAMESPACE4}"><QueueInfo><QueueIdentifier PseudoCityCode="${xmlEscape(pcc)}"/></QueueInfo></QueueAnalysisRQ>`;
2614
+ function queueActivityPayload(pcc, target = {}) {
2615
+ return `<QueueAnalysisRQ Version="2.0.0" xmlns="${NAMESPACE4}"><QueueInfo>${queueIdentifierXml(pcc, target)}</QueueInfo></QueueAnalysisRQ>`;
2276
2616
  }
2277
- function queueActivityRequest(pcc, options) {
2617
+ function queueActivityRequest(pcc, options, target = {}) {
2278
2618
  return {
2279
2619
  service: QUEUE_ACTIVITY_SERVICE,
2280
2620
  action: "QueueAnalysisLLSRQ",
2281
- payload: queueActivityPayload(pcc),
2621
+ payload: queueActivityPayload(pcc, target),
2282
2622
  idempotent: true,
2283
2623
  operation: "queue.activity",
2284
2624
  ...options
2285
2625
  };
2286
2626
  }
2287
- function attribute3(node3, name) {
2288
- const value = (Array.isArray(node3) ? node3[0] : node3)?.[`@${name}`];
2627
+ function attribute3(node4, name) {
2628
+ const value = (Array.isArray(node4) ? node4[0] : node4)?.[`@${name}`];
2289
2629
  return typeof value === "string" ? value : void 0;
2290
2630
  }
2291
2631
  function nonEmpty4(value) {
2292
- const text = value?.trim();
2293
- return text === void 0 || text === "" ? void 0 : text;
2632
+ const text2 = value?.trim();
2633
+ return text2 === void 0 || text2 === "" ? void 0 : text2;
2294
2634
  }
2295
2635
  function parseQueueActivityResponse(response) {
2296
2636
  const queues = [];
@@ -2315,10 +2655,10 @@ function parseQueueActivityResponse(response) {
2315
2655
  ...respondedAtField(response.raw)
2316
2656
  };
2317
2657
  }
2318
- function counters(node3, label, response) {
2658
+ function counters(node4, label, response) {
2319
2659
  const result = {};
2320
2660
  for (const [element2, key] of COUNTERS) {
2321
- const raw = attribute3(node3[element2], "Count");
2661
+ const raw = attribute3(node4[element2], "Count");
2322
2662
  const count = parseHostCount(raw);
2323
2663
  if (count === void 0) throw malformed2(`${label} con ${element2} Count="${raw ?? ""}"`, response);
2324
2664
  result[key] = count;
@@ -2372,13 +2712,13 @@ function signatureLocator(lines) {
2372
2712
  }
2373
2713
  return void 0;
2374
2714
  }
2375
- function isEnd(response, text) {
2376
- return response.warnings.some(isEndOfDisplayResult) || isEndOfDisplay(text.find((line) => line.trim() !== ""));
2715
+ function isEnd(response, text2) {
2716
+ return response.warnings.some(isEndOfDisplayResult) || isEndOfDisplay(text2.find((line) => line.trim() !== ""));
2377
2717
  }
2378
2718
  function parsePlaceStep(response) {
2379
- const text = textLines(response.raw, "QueuePlaceRS");
2380
- if (isEnd(response, text)) return endStep(response.warnings, "end-of-display", response.raw);
2381
- const locator = signatureLocator(text);
2719
+ const text2 = textLines(response.raw, "QueuePlaceRS");
2720
+ if (isEnd(response, text2)) return endStep(response.warnings, "end-of-display", response.raw);
2721
+ const locator = signatureLocator(text2);
2382
2722
  if (locator === void 0) {
2383
2723
  throw new SabreGatewayError(`${QUEUE_PLACE_SERVICE}: respuesta no reconocida (sin l\xEDnea de firma del PNR ni fin de cola)`, {
2384
2724
  protocol: "soap",
@@ -2387,11 +2727,11 @@ function parsePlaceStep(response) {
2387
2727
  raw: response.raw
2388
2728
  });
2389
2729
  }
2390
- return { done: false, kind: "pnr", item: { locator, paragraph: text }, warnings: response.warnings, ...respondedAtField(response.raw) };
2730
+ return { done: false, kind: "pnr", item: { locator, paragraph: text2 }, warnings: response.warnings, ...respondedAtField(response.raw) };
2391
2731
  }
2392
2732
  function parsePlaceUnknown(response) {
2393
- const text = textLines(response.raw, "QueuePlaceRS");
2394
- if (isEnd(response, text) || signatureLocator(text) !== void 0) return parsePlaceStep(response);
2733
+ const text2 = textLines(response.raw, "QueuePlaceRS");
2734
+ if (isEnd(response, text2) || signatureLocator(text2) !== void 0) return parsePlaceStep(response);
2395
2735
  return parsePlaceResult(response);
2396
2736
  }
2397
2737
  function parsePlaceResult(response) {
@@ -2446,13 +2786,27 @@ var QueueManagement = class {
2446
2786
  * más los totales. Cada `queue` de la respuesta se puede pasar tal cual a las demás operaciones
2447
2787
  * de cola.
2448
2788
  *
2789
+ * Sin filtros cuenta todo el PCC. Con `queue`, solo esa cola; con `range`, cada cola del rango,
2790
+ * también las vacías. Con `selection` (que necesita `queue`), solo los PNR que cumplen: con el
2791
+ * mismo `QD`, da lo mismo que `list` (CERT y producción, #213).
2792
+ *
2449
2793
  * Se puede llamar dentro o fuera de una cola, y no cambia `state`. Es una lectura: se reintenta
2450
- * ante fallos transitorios. Un PCC que no tiene 3 o 4 caracteres alfanuméricos lanza
2451
- * `TypeError` sin red.
2794
+ * ante fallos transitorios. Lanzan `TypeError` sin red: un PCC que no tiene 3 o 4 caracteres
2795
+ * alfanuméricos, una `queue` o un `range` inválidos, `queue` y `range` juntos, una `selection`
2796
+ * inválida (como en `access`) o una `selection` sin `queue`.
2452
2797
  */
2453
2798
  async count(request = {}, options = {}) {
2799
+ if (typeof request !== "object" || request === null) fail4("queue.count recibe { pcc?, queue?, range?, selection? }");
2454
2800
  const pcc = this.#pcc(request.pcc, "pcc");
2455
- const response = await this.#context.send(queueCountRequest(pcc, options));
2801
+ const { queue, range } = validateQueueTarget(request, "queue.count");
2802
+ const selection = validateSelection(request.selection);
2803
+ if (selection.length > 0 && queue === void 0) {
2804
+ fail4(
2805
+ range === void 0 ? "selection en count necesita queue: sin cola, Sabre responde QUEUE SELECTED NOT VALID OR CLOSED" : "selection en count necesita queue, no range: con un rango, Sabre responde KEYS NOT ALLOWED"
2806
+ );
2807
+ }
2808
+ const target = { ...queue === void 0 ? {} : { queue }, ...range === void 0 ? {} : { range }, ...selection.length === 0 ? {} : { selection } };
2809
+ const response = await this.#context.send(queueCountRequest(pcc, options, target));
2456
2810
  return parseQueueCountResponse(response);
2457
2811
  }
2458
2812
  /**
@@ -2463,13 +2817,18 @@ var QueueManagement = class {
2463
2817
  * (Q-097): el SDK los devuelve sin interpretar.
2464
2818
  *
2465
2819
  * Se puede llamar dentro o fuera de una cola, y no cambia `state`. Es una lectura: se reintenta
2466
- * ante fallos transitorios. No pide duty code 9 (CERT y producción, Q-098). Un PCC que no tiene 3
2467
- * o 4 caracteres alfanuméricos lanza `TypeError` sin red.
2820
+ * ante fallos transitorios. No pide duty code 9 (CERT y producción, Q-098).
2821
+ *
2822
+ * Sin filtros analiza todo el PCC. Con `queue`, solo esa cola; con `range`, cada cola del rango,
2823
+ * también las que tienen todos los contadores en 0. Lanzan `TypeError` sin red: un PCC que no
2824
+ * tiene 3 o 4 caracteres alfanuméricos, una `queue` o un `range` inválidos, y `queue` y `range`
2825
+ * juntos.
2468
2826
  */
2469
2827
  async activity(request = {}, options = {}) {
2470
- if (typeof request !== "object" || request === null) fail4("queue.activity recibe { pcc? }");
2828
+ if (typeof request !== "object" || request === null) fail4("queue.activity recibe { pcc?, queue?, range? }");
2471
2829
  const pcc = this.#pcc(request.pcc, "pcc");
2472
- const response = await this.#context.send(queueActivityRequest(pcc, options));
2830
+ const target = validateQueueTarget(request, "queue.activity");
2831
+ const response = await this.#context.send(queueActivityRequest(pcc, options, target));
2473
2832
  return parseQueueActivityResponse(response);
2474
2833
  }
2475
2834
  /**
@@ -2735,6 +3094,13 @@ function validateLocators(locators) {
2735
3094
  return locator;
2736
3095
  });
2737
3096
  }
3097
+ function validateQueueTarget(request, operation) {
3098
+ const { queue, range } = request;
3099
+ if (queue !== void 0) validateQueueRef(queue, "queue");
3100
+ if (range !== void 0) validateQueueRange(range, "range");
3101
+ if (queue !== void 0 && range !== void 0) fail4(`${operation} recibe queue o range, no los dos: Sabre no los combina`);
3102
+ return { ...queue === void 0 ? {} : { queue }, ...range === void 0 ? {} : { range } };
3103
+ }
2738
3104
  function validateSelection(selection) {
2739
3105
  if (selection === void 0) return [];
2740
3106
  if (!Array.isArray(selection)) fail4("selection debe ser una lista de { function, criteria }");
@@ -2749,15 +3115,248 @@ function validateSelection(selection) {
2749
3115
  });
2750
3116
  }
2751
3117
 
3118
+ // src/reporting/daily-sales-summary.ts
3119
+ var DAILY_SALES_SUMMARY_SERVICE = "dailySalesSummary";
3120
+ var NAMESPACE6 = "http://www.sabre.com/ns/Ticketing/AsrServices/1.0";
3121
+ var CLIENT_TIMEOUT_SECS = 25;
3122
+ var PCC2 = /^[A-Z0-9]{3,4}$/;
3123
+ var DATE = /^\d{4}-\d{2}-\d{2}$/;
3124
+ var DOCUMENT_TYPES = ["TKT", "EMD", "RFND", "RFEMD", "MCO"];
3125
+ var SETTLEMENT_TYPES = ["TAT", "TCH", "TKT", "NDC"];
3126
+ function validateDailySalesSummaryRequest(request) {
3127
+ const fail8 = (detail) => {
3128
+ throw new TypeError(`sabre-client: reporting.dailySalesSummary: ${detail}`);
3129
+ };
3130
+ if (request.pcc !== void 0 && !PCC2.test(request.pcc)) fail8("pcc debe tener 3 o 4 letras may\xFAsculas o d\xEDgitos");
3131
+ if (request.date !== void 0 && (!DATE.test(request.date) || Number.isNaN(Date.parse(`${request.date}T00:00:00Z`)))) {
3132
+ fail8("date debe tener la forma 'YYYY-MM-DD'");
3133
+ }
3134
+ if (request.documentType !== void 0 && !DOCUMENT_TYPES.includes(request.documentType)) fail8(`documentType debe ser ${DOCUMENT_TYPES.join(", ")}`);
3135
+ if (request.settlementType !== void 0 && !SETTLEMENT_TYPES.includes(request.settlementType)) fail8(`settlementType debe ser ${SETTLEMENT_TYPES.join(", ")}`);
3136
+ if (request.airline !== void 0) {
3137
+ const { code, number } = request.airline;
3138
+ if (code === void 0 && number === void 0) fail8("airline necesita code o number");
3139
+ if (code !== void 0 && !/^[A-Z0-9]{2}$/.test(code)) fail8("airline.code debe tener 2 caracteres");
3140
+ if (number !== void 0 && !/^\d{3}$/.test(number)) fail8("airline.number debe tener 3 d\xEDgitos");
3141
+ }
3142
+ if (request.documentNumber !== void 0 && !/^\d{10,15}$/.test(request.documentNumber)) fail8("documentNumber debe tener de 10 a 15 d\xEDgitos");
3143
+ }
3144
+ function dailySalesSummaryPayload(request = {}) {
3145
+ const { pcc, date, documentType, settlementType, airline, documentNumber } = request;
3146
+ const airlineXml = airline === void 0 ? "" : `<Airline>${airline.code === void 0 ? "" : `<AirlineCode>${xmlEscape(airline.code)}</AirlineCode>`}${airline.number === void 0 ? "" : `<AirlineNumber>${xmlEscape(airline.number)}</AirlineNumber>`}</Airline>`;
3147
+ const criteria = [
3148
+ pcc === void 0 ? "" : `<PseudoCityCode>${xmlEscape(pcc)}</PseudoCityCode>`,
3149
+ airlineXml,
3150
+ date === void 0 ? "" : `<ReportDate>${xmlEscape(date)}</ReportDate>`,
3151
+ settlementType === void 0 ? "" : `<SettlementType>${xmlEscape(settlementType)}</SettlementType>`,
3152
+ documentType === void 0 ? "" : `<DocumentType>${xmlEscape(documentType)}</DocumentType>`,
3153
+ documentNumber === void 0 ? "" : `<DocumentNumber>${xmlEscape(documentNumber)}</DocumentNumber>`
3154
+ ].join("");
3155
+ return `<DailySalesSummaryRQ xmlns="${NAMESPACE6}" version="1.2.2"><Header/><SelectionCriteria isCacheLateLargeReports="false" clientTimeoutSecs="${CLIENT_TIMEOUT_SECS}">${criteria}</SelectionCriteria></DailySalesSummaryRQ>`;
3156
+ }
3157
+ function dailySalesSummaryRequest(request, options) {
3158
+ return {
3159
+ service: DAILY_SALES_SUMMARY_SERVICE,
3160
+ action: "TKT_TravelAgencyReportsRQ",
3161
+ payload: dailySalesSummaryPayload(request),
3162
+ idempotent: true,
3163
+ operation: "reporting.dailySalesSummary",
3164
+ ...options
3165
+ };
3166
+ }
3167
+ var node2 = (value) => {
3168
+ const first = Array.isArray(value) ? value[0] : value;
3169
+ return typeof first === "object" && first !== null ? first : void 0;
3170
+ };
3171
+ var attribute4 = (value, name) => {
3172
+ const found = node2(value)?.[`@${name}`];
3173
+ return typeof found === "string" ? found : void 0;
3174
+ };
3175
+ var text = (value) => {
3176
+ const found = textOf(Array.isArray(value) ? value[0] : value)?.trim();
3177
+ return found === void 0 || found === "" ? void 0 : found;
3178
+ };
3179
+ var flag = (value) => value === "true" ? true : value === "false" ? false : void 0;
3180
+ var integer = (value) => value !== void 0 && /^\d+$/.test(value) ? Number(value) : void 0;
3181
+ function defined(entries) {
3182
+ return Object.fromEntries(Object.entries(entries).filter(([, value]) => value !== void 0));
3183
+ }
3184
+ function summaryErrors(payload) {
3185
+ const results = [node2(payload.Header)?.Results, payload.Results];
3186
+ return results.flatMap(
3187
+ (value) => asArray(node2(value)?.Error).map((error) => {
3188
+ const entry = node2(error);
3189
+ const message = text(entry?.Message) ?? text(entry?.Description) ?? text(error) ?? "";
3190
+ const code = text(entry?.ErrorCode) ?? text(entry?.Code) ?? attribute4(error, "code") ?? attribute4(entry?.Message, "code") ?? /^\s*(\d{4,6})\b/.exec(message)?.[1] ?? "UNKNOWN";
3191
+ const status = attribute4(error, "status");
3192
+ return defined({ status, code, message });
3193
+ })
3194
+ );
3195
+ }
3196
+ function failedDocumentsOf(message) {
3197
+ const list = message.slice(message.indexOf(":") + 1);
3198
+ return [...list.matchAll(/\d{7,}/g)].map((match) => match[0]);
3199
+ }
3200
+ function readTransaction(value, response) {
3201
+ const transaction2 = node2(value) ?? {};
3202
+ const details = node2(transaction2.DocumentDetails) ?? {};
3203
+ const documentType = text(transaction2.DocumentType);
3204
+ const documentNumber = text(details.DocumentNumber);
3205
+ const transactionCode = text(details.TransactionCode);
3206
+ const transactionDateTime = text(transaction2.TransactionDateTime);
3207
+ if (documentType === void 0 || documentNumber === void 0 || transactionCode === void 0 || transactionDateTime === void 0) {
3208
+ throw malformed3("Transaction sin DocumentType, DocumentNumber, TransactionCode o TransactionDateTime", response);
3209
+ }
3210
+ const airline = transaction2.AirlineCode;
3211
+ const commission = node2(transaction2.Commission);
3212
+ const payments = node2(transaction2.Payments);
3213
+ const documentPayment = node2(payments?.DocumentPayment);
3214
+ return defined({
3215
+ documentType,
3216
+ transactionCode,
3217
+ documentStatusCode: text(details.DocumentStatusCode),
3218
+ settlementType: text(transaction2.SettlementType) ?? "",
3219
+ pnrLocator: text(transaction2.PnrLocator),
3220
+ passengerName: text(transaction2.PassengerName),
3221
+ documentNumber,
3222
+ lastConjunctiveDocumentNumber: text(details.LastConjunctiveDocumentNumber),
3223
+ stockType: text(details.StockType),
3224
+ airline: defined({ code: text(airline) ?? "", number: attribute4(airline, "AirlineNumber") }),
3225
+ commission: commission === void 0 ? void 0 : defined({ amount: text(commission.Amount), percent: text(commission.Percent) }),
3226
+ payments: payments === void 0 ? void 0 : {
3227
+ currencyCode: text(payments.CurrencyCode) ?? "",
3228
+ total: text(documentPayment?.PaymentTotal) ?? "",
3229
+ details: asArray(documentPayment?.PaymentDetail).map(
3230
+ (detail) => defined({
3231
+ fop: attribute4(detail, "fop") ?? "",
3232
+ category: attribute4(detail, "category") ?? "",
3233
+ cardType: attribute4(detail, "cardType"),
3234
+ amount: text(node2(detail)?.PaymentAmount) ?? ""
3235
+ })
3236
+ )
3237
+ },
3238
+ agentSine: text(transaction2.AgentSine) ?? "",
3239
+ agentPseudoCity: text(transaction2.AgentPseudoCity),
3240
+ transactionDateTime,
3241
+ electronicDocument: flag(attribute4(value, "electronicDocument")),
3242
+ nonInteractive: flag(attribute4(value, "nonInteractive")),
3243
+ itineraryType: attribute4(value, "itineraryType"),
3244
+ automatedRefund: flag(attribute4(value, "automatedRefund"))
3245
+ });
3246
+ }
3247
+ function readTotals(report) {
3248
+ const amountOf = (value) => {
3249
+ const amount2 = text(value);
3250
+ return amount2 === void 0 ? void 0 : defined({ amount: amount2, count: integer(attribute4(value, "count")) });
3251
+ };
3252
+ return asArray(report?.TotalDailySales).flatMap((total) => {
3253
+ const transactionType = text(node2(total)?.TransactionType) ?? "";
3254
+ return asArray(node2(total)?.SalesBySettlement).flatMap((settlement) => {
3255
+ const settlementType = text(node2(settlement)?.SettlementType) ?? "";
3256
+ return asArray(node2(settlement)?.SaleTotals).map((totals) => {
3257
+ const sale = node2(totals);
3258
+ return defined({
3259
+ transactionType,
3260
+ settlementType,
3261
+ currencyCode: text(sale?.CurrencyCode),
3262
+ cash: amountOf(sale?.CashTotals),
3263
+ credit: amountOf(sale?.CreditTotals),
3264
+ commission: amountOf(sale?.CommissionTotals)
3265
+ });
3266
+ });
3267
+ });
3268
+ });
3269
+ }
3270
+ function parseDailySalesSummaryResponse(response, fallbackPcc) {
3271
+ const payload = response.payload;
3272
+ const errors = summaryErrors(payload);
3273
+ const fatal = errors.filter((error) => error.status?.toLowerCase() !== "incomplete");
3274
+ const [first, ...rest] = fatal;
3275
+ if (first !== void 0) {
3276
+ throw new SabreBusinessError(`${DAILY_SALES_SUMMARY_SERVICE}: ${first.code} ${first.message}`.trim(), {
3277
+ service: DAILY_SALES_SUMMARY_SERVICE,
3278
+ code: first.code,
3279
+ raw: response.raw,
3280
+ errors: rest.map(
3281
+ (error) => new SabreBusinessError(`${DAILY_SALES_SUMMARY_SERVICE}: ${error.code} ${error.message}`.trim(), { service: DAILY_SALES_SUMMARY_SERVICE, code: error.code, raw: response.raw })
3282
+ )
3283
+ });
3284
+ }
3285
+ const incomplete = errors.filter((error) => error.status?.toLowerCase() === "incomplete");
3286
+ const header = node2(payload.DSHeader);
3287
+ const report = node2(payload.DailySalesReport);
3288
+ const reportId = attribute4(payload, "reportId");
3289
+ const queryStillRunning = attribute4(payload, "isQueryStillRunning") === "true";
3290
+ const deferred = reportId !== void 0 || queryStillRunning;
3291
+ return defined({
3292
+ status: deferred ? "deferred" : incomplete.length > 0 ? "incomplete" : "complete",
3293
+ pcc: text(header?.PseudoCityCode) ?? fallbackPcc,
3294
+ agencyName: text(header?.AgencyName),
3295
+ reportDate: text(report?.ReportDate) ?? text(header?.ReportDate),
3296
+ transactions: asArray(report?.Transaction).map((transaction2) => readTransaction(transaction2, response)),
3297
+ totals: readTotals(report),
3298
+ failedDocuments: incomplete.length > 0 ? incomplete.flatMap((error) => failedDocumentsOf(error.message)) : void 0,
3299
+ deferred: deferred ? defined({
3300
+ reportId,
3301
+ chunkNumber: integer(attribute4(payload, "chunkNumber")),
3302
+ maxChunks: integer(attribute4(payload, "maxChunks")),
3303
+ queryStillRunning
3304
+ }) : void 0,
3305
+ respondedAt: respondedAtField(response.raw).respondedAt
3306
+ });
3307
+ }
3308
+ function malformed3(detail, response) {
3309
+ return new SabreGatewayError(`${DAILY_SALES_SUMMARY_SERVICE}: respuesta no reconocida (${detail})`, {
3310
+ protocol: "soap",
3311
+ retryable: false,
3312
+ raw: response.raw
3313
+ });
3314
+ }
3315
+
3316
+ // src/reporting/session-reporting.ts
3317
+ var SessionReporting = class {
3318
+ #context;
3319
+ /** @internal Lo construye `SabreSession`. */
3320
+ constructor(context) {
3321
+ this.#context = context;
3322
+ }
3323
+ /**
3324
+ * Ventas del día: el «Summary» del DQB (`TKT_TravelAgencyReportsRQ` / `DailySalesSummaryRQ`
3325
+ * 1.2.2). Una transacción por movimiento del PCC en el día: ventas, reembolsos, voids, EMD y MCO,
3326
+ * sin tope. Sin `date`, da el día en curso, hasta minutos antes de la llamada (producción,
3327
+ * 2026-11-12). **Trae datos del pasajero** (`passengerName`).
3328
+ *
3329
+ * - Las fechas y horas (`transactionDateTime`, `reportDate`) llegan **tal como vienen, sin zona**.
3330
+ * En producción vienen en hora local del PCC; el SDK no las convierte.
3331
+ * - Solo `status: 'complete'` es el día entero. `incomplete` trae `failedDocuments`, y `deferred`
3332
+ * quiere decir que Sabre lo dejó en caché o sigue corriendo: hay que volver a pedirlo. El SDK
3333
+ * no pide `CachedReportRQ`.
3334
+ * - Los importes van como string.
3335
+ *
3336
+ * Otro PCC necesita Branch Access: sin él, Sabre responde `45100` y el SDK lanza
3337
+ * `SabreBusinessError` (`service: 'dailySalesSummary'`, `code: '45100'`). Lo mismo con `21212`
3338
+ * (versión) y cualquier otro error de la respuesta. Un fallo de autorización del gateway
3339
+ * (`USG_AUTHORIZATION_FAILED`) sale como los demás de la sesión. Un filtro con forma
3340
+ * equivocada lanza `TypeError` sin red.
3341
+ *
3342
+ * Es una lectura: se reintenta ante fallos de transporte, pero no ante errores de negocio.
3343
+ */
3344
+ async dailySalesSummary(request = {}, options = {}) {
3345
+ validateDailySalesSummaryRequest(request);
3346
+ const response = await this.#context.send(dailySalesSummaryRequest(request, options));
3347
+ return parseDailySalesSummaryResponse(response, request.pcc ?? this.#context.pcc);
3348
+ }
3349
+ };
3350
+
2752
3351
  // src/reservation/reservation-history.ts
2753
3352
  var RESERVATION_HISTORY_SERVICE = "itineraryHistory";
2754
- var NAMESPACE6 = "http://webservices.sabre.com/sabreXML/2011/10";
3353
+ var NAMESPACE7 = "http://webservices.sabre.com/sabreXML/2011/10";
2755
3354
  var LOCATOR = /^[A-Z0-9]{6}$/;
2756
3355
  function isRecordLocator(value) {
2757
3356
  return typeof value === "string" && LOCATOR.test(value);
2758
3357
  }
2759
3358
  function reservationHistoryPayload(locator) {
2760
- return `<TravelItineraryHistoryRQ Version="2.4.0" xmlns="${NAMESPACE6}"><UniqueID ID="${xmlEscape(locator)}"/></TravelItineraryHistoryRQ>`;
3359
+ return `<TravelItineraryHistoryRQ Version="2.4.0" xmlns="${NAMESPACE7}"><UniqueID ID="${xmlEscape(locator)}"/></TravelItineraryHistoryRQ>`;
2761
3360
  }
2762
3361
  function reservationHistoryRequest(locator, options) {
2763
3362
  return {
@@ -2769,76 +3368,76 @@ function reservationHistoryRequest(locator, options) {
2769
3368
  ...options
2770
3369
  };
2771
3370
  }
2772
- function node2(value) {
3371
+ function node3(value) {
2773
3372
  const first = Array.isArray(value) ? value[0] : value;
2774
3373
  return typeof first === "object" && first !== null ? first : void 0;
2775
3374
  }
2776
- function attribute4(value, name) {
2777
- const text = node2(value)?.[`@${name}`];
2778
- if (typeof text !== "string") return void 0;
2779
- const trimmed = text.trim();
3375
+ function attribute5(value, name) {
3376
+ const text2 = node3(value)?.[`@${name}`];
3377
+ if (typeof text2 !== "string") return void 0;
3378
+ const trimmed = text2.trim();
2780
3379
  return trimmed === "" ? void 0 : trimmed;
2781
3380
  }
2782
- function defined(fields) {
3381
+ function defined2(fields) {
2783
3382
  return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0));
2784
3383
  }
2785
- function integer(value) {
3384
+ function integer2(value) {
2786
3385
  return value !== void 0 && /^\d+$/.test(value) ? Number(value) : void 0;
2787
3386
  }
2788
3387
  function parseReservationHistoryResponse(response) {
2789
- const transactions = asArray(response.payload.HistoricalInfo).map((info) => transaction(node2(info) ?? {}, response));
3388
+ const transactions = asArray(response.payload.HistoricalInfo).map((info) => transaction(node3(info) ?? {}, response));
2790
3389
  return { transactions, warnings: response.warnings, ...respondedAtField(response.raw) };
2791
3390
  }
2792
3391
  function transaction(info, response) {
2793
- const source = node2(node2(info.ItineraryRef)?.Source);
2794
- const flightSegments = asArray(node2(node2(info.ItineraryInfo)?.ReservationItems)?.Item).flatMap(
2795
- (item) => asArray(node2(item)?.FlightSegment).map((segment) => flightSegment(node2(segment) ?? {}, response))
3392
+ const source = node3(node3(info.ItineraryRef)?.Source);
3393
+ const flightSegments = asArray(node3(node3(info.ItineraryInfo)?.ReservationItems)?.Item).flatMap(
3394
+ (item) => asArray(node3(item)?.FlightSegment).map((segment) => flightSegment(node3(segment) ?? {}, response))
2796
3395
  );
2797
3396
  return {
2798
- ...defined({
2799
- rph: integer(attribute4(info, "RPH")),
2800
- createdAt: attribute4(source, "CreateDateTime"),
2801
- agent: attribute4(source, "CreationAgent"),
2802
- agentDutyCode: attribute4(source, "CreationAgentDutyCode"),
2803
- agentFunctionCode: attribute4(source, "CreationAgentFunctionCode"),
2804
- pcc: attribute4(source, "PseudoCityCode"),
2805
- aaaPcc: attribute4(source, "AAA_PseudoCityCode") ?? attribute4(source, "AAAPseudoCityCode"),
2806
- receivedFrom: attribute4(source, "ReceivedFrom")
3397
+ ...defined2({
3398
+ rph: integer2(attribute5(info, "RPH")),
3399
+ createdAt: attribute5(source, "CreateDateTime"),
3400
+ agent: attribute5(source, "CreationAgent"),
3401
+ agentDutyCode: attribute5(source, "CreationAgentDutyCode"),
3402
+ agentFunctionCode: attribute5(source, "CreationAgentFunctionCode"),
3403
+ pcc: attribute5(source, "PseudoCityCode"),
3404
+ aaaPcc: attribute5(source, "AAA_PseudoCityCode") ?? attribute5(source, "AAAPseudoCityCode"),
3405
+ receivedFrom: attribute5(source, "ReceivedFrom")
2807
3406
  }),
2808
3407
  flightSegments,
2809
3408
  generalInfo: asArray(info.GeneralInfo).map((entry) => textEntry(entry, "GeneralInfo", response)),
2810
- remarks: asArray(node2(info.RemarkInfo)?.Remark).map((entry) => textEntry(entry, "Remark", response)),
3409
+ remarks: asArray(node3(info.RemarkInfo)?.Remark).map((entry) => textEntry(entry, "Remark", response)),
2811
3410
  accounting: asArray(info.AccountingInfo).map(
2812
- (entry) => defined({ action: attribute4(entry, "Action"), type: attribute4(entry, "Type"), linkCode: attribute4(entry, "LinkCode") })
3411
+ (entry) => defined2({ action: attribute5(entry, "Action"), type: attribute5(entry, "Type"), linkCode: attribute5(entry, "LinkCode") })
2813
3412
  )
2814
3413
  };
2815
3414
  }
2816
3415
  function textEntry(entry, label, response) {
2817
- const action = attribute4(entry, "Action");
2818
- if (action === void 0) throw malformed3(`${label} sin Action`, response);
2819
- const texts = asArray(node2(entry)?.Text).map((text) => textOf(text) ?? "");
3416
+ const action = attribute5(entry, "Action");
3417
+ if (action === void 0) throw malformed4(`${label} sin Action`, response);
3418
+ const texts = asArray(node3(entry)?.Text).map((text2) => textOf(text2) ?? "");
2820
3419
  return { action, text: texts.join("\n") };
2821
3420
  }
2822
3421
  function flightSegment(segment, response) {
2823
- const action = attribute4(segment, "Action");
2824
- if (action === void 0) throw malformed3("FlightSegment sin Action", response);
3422
+ const action = attribute5(segment, "Action");
3423
+ if (action === void 0) throw malformed4("FlightSegment sin Action", response);
2825
3424
  return {
2826
3425
  action,
2827
- ...defined({
2828
- status: attribute4(segment, "Status"),
2829
- marketingAirline: attribute4(segment.MarketingAirline, "Code"),
2830
- flightNumber: attribute4(segment, "FlightNumber") ?? attribute4(segment.MarketingAirline, "FlightNumber"),
2831
- bookingClass: attribute4(segment, "ResBookDesigCode"),
2832
- origin: attribute4(segment.OriginLocation, "LocationCode"),
2833
- destination: attribute4(segment.DestinationLocation, "LocationCode"),
2834
- departureDateTime: attribute4(segment, "DepartureDateTime"),
2835
- arrivalDateTime: attribute4(segment, "ArrivalDateTime"),
2836
- numberInParty: integer(attribute4(segment, "NumberInParty")),
2837
- supplierRef: attribute4(segment.SupplierRef, "ID")
3426
+ ...defined2({
3427
+ status: attribute5(segment, "Status"),
3428
+ marketingAirline: attribute5(segment.MarketingAirline, "Code"),
3429
+ flightNumber: attribute5(segment, "FlightNumber") ?? attribute5(segment.MarketingAirline, "FlightNumber"),
3430
+ bookingClass: attribute5(segment, "ResBookDesigCode"),
3431
+ origin: attribute5(segment.OriginLocation, "LocationCode"),
3432
+ destination: attribute5(segment.DestinationLocation, "LocationCode"),
3433
+ departureDateTime: attribute5(segment, "DepartureDateTime"),
3434
+ arrivalDateTime: attribute5(segment, "ArrivalDateTime"),
3435
+ numberInParty: integer2(attribute5(segment, "NumberInParty")),
3436
+ supplierRef: attribute5(segment.SupplierRef, "ID")
2838
3437
  })
2839
3438
  };
2840
3439
  }
2841
- function malformed3(detail, response) {
3440
+ function malformed4(detail, response) {
2842
3441
  return new SabreGatewayError(`${RESERVATION_HISTORY_SERVICE}: respuesta no reconocida (${detail})`, {
2843
3442
  protocol: "soap",
2844
3443
  status: response.status,
@@ -2885,14 +3484,14 @@ var SessionReservation = class {
2885
3484
 
2886
3485
  // src/session/command.ts
2887
3486
  var SABRE_COMMAND_SERVICE = "sabreCommand";
2888
- var NAMESPACE7 = "http://webservices.sabre.com/sabreXML/2011/10";
3487
+ var NAMESPACE8 = "http://webservices.sabre.com/sabreXML/2011/10";
2889
3488
  var MAX_COMMAND_LENGTH = 1023;
2890
3489
  var DOUBLE_WIDTH = /* @__PURE__ */ new Set(["!", "%", "$", "&", "\xA5", "\xA7"]);
2891
3490
  function fail5(message) {
2892
3491
  throw new TypeError(`sabre-client: ${message}`);
2893
3492
  }
2894
3493
  function sabreCommandPayload(command) {
2895
- return `<SabreCommandLLSRQ xmlns="${NAMESPACE7}" Version="2.0.0" ReturnHostCommand="true"><Request Output="SCREEN"><HostCommand>${xmlEscape(command)}</HostCommand></Request></SabreCommandLLSRQ>`;
3494
+ return `<SabreCommandLLSRQ xmlns="${NAMESPACE8}" Version="2.0.0" ReturnHostCommand="true"><Request Output="SCREEN"><HostCommand>${xmlEscape(command)}</HostCommand></Request></SabreCommandLLSRQ>`;
2896
3495
  }
2897
3496
  function commandToken(command) {
2898
3497
  return (command.trim().split(/[¥‡\s]/u)[0] ?? "").slice(0, 4);
@@ -2971,6 +3570,8 @@ var SabreSession = class {
2971
3570
  queue;
2972
3571
  /** La reserva: el historial de un PNR, dentro de esta sesión. */
2973
3572
  reservation;
3573
+ /** Reportes del PCC: las ventas del día (`dailySalesSummary`), dentro de esta sesión. */
3574
+ reporting;
2974
3575
  #transport;
2975
3576
  #pcc;
2976
3577
  #conversationId;
@@ -2999,6 +3600,7 @@ var SabreSession = class {
2999
3600
  log: internal.transport.log
3000
3601
  });
3001
3602
  this.reservation = new SessionReservation({ send: (request) => this[sessionSend](request) });
3603
+ this.reporting = new SessionReporting({ send: (request) => this[sessionSend](request), pcc: internal.pcc });
3002
3604
  if (internal.keepAlive) this.#scheduleKeepAlive(KEEP_ALIVE_IDLE_MS);
3003
3605
  }
3004
3606
  /**
@@ -3163,8 +3765,8 @@ function checkPingResponse(response) {
3163
3765
  const error = asArray(errors?.Error)[0];
3164
3766
  if (error !== void 0) {
3165
3767
  const type = error["@Type"];
3166
- const text = textOf(error)?.trim();
3167
- throw new SabreGatewayError(`ping: OTA_PingRS con error${text ? `: ${text}` : ""}`, {
3768
+ const text2 = textOf(error)?.trim();
3769
+ throw new SabreGatewayError(`ping: OTA_PingRS con error${text2 ? `: ${text2}` : ""}`, {
3168
3770
  protocol: "soap",
3169
3771
  status: response.status,
3170
3772
  ...typeof type === "string" && type !== "" && { code: type },
@@ -3354,22 +3956,22 @@ function buildCredentials(options) {
3354
3956
  var MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
3355
3957
  var DAY_MS = 864e5;
3356
3958
  var HALF_YEAR_DAYS = 184;
3357
- function resolveHostDate(text, referenceDate, options = {}) {
3959
+ function resolveHostDate(text2, referenceDate, options = {}) {
3358
3960
  const reference = referenceDay(referenceDate);
3359
3961
  const direction = options.direction ?? "past";
3360
3962
  if (direction !== "past" && direction !== "nearest") {
3361
3963
  throw new TypeError("sabre-client: direction debe ser 'past' o 'nearest'");
3362
3964
  }
3363
- if (typeof text !== "string") return void 0;
3364
- const parsed = parseHostDate(text.trim().toUpperCase());
3965
+ if (typeof text2 !== "string") return void 0;
3966
+ const parsed = parseHostDate(text2.trim().toUpperCase());
3365
3967
  if (parsed === void 0) return void 0;
3366
3968
  const year = parsed.year ?? resolveYear(parsed.month, parsed.day, reference, direction);
3367
3969
  if (year === void 0 || !isValidDate(year, parsed.month, parsed.day)) return void 0;
3368
3970
  const date = `${pad(year, 4)}-${pad(parsed.month, 2)}-${pad(parsed.day, 2)}`;
3369
3971
  return parsed.time === void 0 ? date : `${date}T${parsed.time}`;
3370
3972
  }
3371
- function parseHostDate(text) {
3372
- let match = /^(\d{1,2})([A-Z]{3})(\d{2})?$/.exec(text);
3973
+ function parseHostDate(text2) {
3974
+ let match = /^(\d{1,2})([A-Z]{3})(\d{2})?$/.exec(text2);
3373
3975
  if (match !== null) {
3374
3976
  const month2 = MONTHS.indexOf(match[2]) + 1;
3375
3977
  if (month2 === 0) return void 0;
@@ -3379,7 +3981,7 @@ function parseHostDate(text) {
3379
3981
  day: Number(match[1])
3380
3982
  };
3381
3983
  }
3382
- match = /^(?:(\d{4})-)?(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/.exec(text);
3984
+ match = /^(?:(\d{4})-)?(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/.exec(text2);
3383
3985
  if (match === null) return void 0;
3384
3986
  const [, year, month, day, hours, minutes] = match;
3385
3987
  if (hours !== void 0 && (Number(hours) > 23 || Number(minutes) > 59)) return void 0;
@@ -3580,8 +4182,8 @@ function parseSegment(line, referenceDate) {
3580
4182
  text: line.trim()
3581
4183
  };
3582
4184
  }
3583
- function parseTicketingEntry(number, text, referenceDate) {
3584
- const ticketed = TICKETED.exec(text);
4185
+ function parseTicketingEntry(number, text2, referenceDate) {
4186
+ const ticketed = TICKETED.exec(text2);
3585
4187
  if (ticketed !== null) {
3586
4188
  const [, date, pseudoCityCode, agentSine] = ticketed;
3587
4189
  return {
@@ -3590,10 +4192,10 @@ function parseTicketingEntry(number, text, referenceDate) {
3590
4192
  ...date !== void 0 && withDate(date, referenceDate),
3591
4193
  ...pseudoCityCode !== void 0 && { pseudoCityCode },
3592
4194
  ...agentSine !== void 0 && { agentSine },
3593
- text
4195
+ text: text2
3594
4196
  };
3595
4197
  }
3596
- const arrangement = ARRANGEMENT.exec(text);
4198
+ const arrangement = ARRANGEMENT.exec(text2);
3597
4199
  if (arrangement !== null) {
3598
4200
  const [, code, pseudoCityCode, date] = arrangement;
3599
4201
  return {
@@ -3601,11 +4203,11 @@ function parseTicketingEntry(number, text, referenceDate) {
3601
4203
  code,
3602
4204
  ...date !== void 0 && withDate(date, referenceDate),
3603
4205
  ...pseudoCityCode !== void 0 && { pseudoCityCode },
3604
- text
4206
+ text: text2
3605
4207
  };
3606
4208
  }
3607
- const ticket = TICKET.exec(text);
3608
- if (ticket !== null) return { number, code: ticket[1], ticketNumber: ticket[2], text };
4209
+ const ticket = TICKET.exec(text2);
4210
+ if (ticket !== null) return { number, code: ticket[1], ticketNumber: ticket[2], text: text2 };
3609
4211
  return void 0;
3610
4212
  }
3611
4213
  function lastSignatureIndex(lines) {
@@ -3626,12 +4228,12 @@ function parseSignature(line, referenceDate) {
3626
4228
  locator
3627
4229
  };
3628
4230
  }
3629
- function withDate(text, referenceDate) {
3630
- const date = resolveHostDate(text, referenceDate, { direction: "nearest" });
4231
+ function withDate(text2, referenceDate) {
4232
+ const date = resolveHostDate(text2, referenceDate, { direction: "nearest" });
3631
4233
  return date === void 0 ? {} : { date };
3632
4234
  }
3633
- function hostTime(text) {
3634
- const match = /^(\d{1,2})(\d{2})([APNM])?$/.exec(text);
4235
+ function hostTime(text2) {
4236
+ const match = /^(\d{1,2})(\d{2})([APNM])?$/.exec(text2);
3635
4237
  if (match === null) return void 0;
3636
4238
  let hours = Number(match[1]);
3637
4239
  const minutes = Number(match[2]);
@@ -3651,9 +4253,9 @@ function hostTime(text) {
3651
4253
  // src/queue/queue-place-remark.ts
3652
4254
  var MONTHS2 = /* @__PURE__ */ new Set(["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]);
3653
4255
  var REMARK = /^([A-Z0-9]{3,4})(\d{2})([A-Z]{3})(\d{1,3})\/(\d{1,3})$/;
3654
- function parseQueuePlaceRemark(text) {
3655
- if (typeof text !== "string") return null;
3656
- const match = REMARK.exec(text.trim());
4256
+ function parseQueuePlaceRemark(text2) {
4257
+ if (typeof text2 !== "string") return null;
4258
+ const match = REMARK.exec(text2.trim());
3657
4259
  if (match === null) return null;
3658
4260
  const [, pcc, day, month, queueText, picText] = match;
3659
4261
  const dayNumber2 = Number(day);
@@ -3748,8 +4350,9 @@ function fareComponent(fareComponentId, component) {
3748
4350
  }
3749
4351
 
3750
4352
  // src/index.ts
3751
- var VERSION = "0.5.1";
4353
+ var VERSION = "0.7.0";
3752
4354
  export {
4355
+ MAX_COMMAND_LENGTH,
3753
4356
  MESSAGE_QUEUE,
3754
4357
  Sabre,
3755
4358
  SabreBusinessError,
@@ -3758,6 +4361,7 @@ export {
3758
4361
  SabreLegacy,
3759
4362
  SabreOrchestrationError,
3760
4363
  SabreSessionError,
4364
+ SabreUncertainOutcomeError,
3761
4365
  SabreWarning,
3762
4366
  VERSION,
3763
4367
  buildBookingFlightDetails,
@@ -3767,6 +4371,7 @@ export {
3767
4371
  isMessageQueue,
3768
4372
  parseQueueParagraph,
3769
4373
  parseQueuePlaceRemark,
3770
- resolveHostDate
4374
+ resolveHostDate,
4375
+ sabreCommandLength
3771
4376
  };
3772
4377
  //# sourceMappingURL=index.js.map