sabre-client 0.6.0 → 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/README.es.md +226 -12
- package/README.md +227 -12
- package/dist/index.d.ts +297 -4
- package/dist/index.js +707 -165
- package/dist/index.js.map +1 -1
- package/dist/{reservation-history-C6giSdFd.d.ts → reservation-history-BRKQBHsj.d.ts} +150 -1
- package/dist/testing.d.ts +116 -5
- package/dist/testing.js +388 -27
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
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
|
|
219
|
+
const text2 = match?.text ?? (typeof error.text === "string" ? error.text : "");
|
|
204
220
|
const known = match?.entry;
|
|
205
|
-
const code = known?.code ?? error.code ??
|
|
206
|
-
return new SabreBusinessError(`${service}: ${
|
|
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
|
|
217
|
-
const entry = catalog.find((candidate) =>
|
|
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(
|
|
642
|
+
function base64(text2) {
|
|
627
643
|
let binary = "";
|
|
628
|
-
for (const byte of new TextEncoder().encode(
|
|
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
|
|
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
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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 (${
|
|
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
|
|
1421
|
-
if (capture !== void 0) capture.last = { status: response.status, headers: response.headers, body:
|
|
1422
|
-
const parsed = parseBody(
|
|
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(
|
|
1447
|
-
if (
|
|
1663
|
+
function parseBody(text2) {
|
|
1664
|
+
if (text2 === "") return void 0;
|
|
1448
1665
|
try {
|
|
1449
|
-
return JSON.parse(
|
|
1666
|
+
return JSON.parse(text2);
|
|
1450
1667
|
} catch {
|
|
1451
|
-
return
|
|
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
|
|
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 (${
|
|
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
|
|
1648
|
-
capture.last = { status: response.status, headers: response.headers, body:
|
|
1649
|
-
const document = parseEnvelope(
|
|
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,
|
|
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(
|
|
1664
|
-
raw:
|
|
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,
|
|
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:
|
|
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,
|
|
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:
|
|
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:
|
|
2005
|
+
raw: text2
|
|
1703
2006
|
});
|
|
1704
2007
|
}
|
|
1705
2008
|
const results = asNode(payload.ApplicationResults);
|
|
1706
|
-
const warnings = asArray(results?.Warning).map((
|
|
1707
|
-
const entry = resultEntry(
|
|
1708
|
-
return new SabreWarning(`${service}: ${entry.description}`, { service, code: entry.code, raw:
|
|
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((
|
|
1711
|
-
const entry = resultEntry(
|
|
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:
|
|
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:
|
|
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(
|
|
1822
|
-
if (!("binarySecurityToken" in security) || security.binarySecurityToken === "") return
|
|
2124
|
+
function scrubToken(text2, security) {
|
|
2125
|
+
if (!("binarySecurityToken" in security) || security.binarySecurityToken === "") return text2;
|
|
1823
2126
|
const token = security.binarySecurityToken;
|
|
1824
|
-
return
|
|
2127
|
+
return text2.split(token).join(REDACTED).split(xmlEscape(token)).join(REDACTED);
|
|
1825
2128
|
}
|
|
1826
|
-
function parseEnvelope(
|
|
1827
|
-
if (
|
|
2129
|
+
function parseEnvelope(text2, xmlParser = parser) {
|
|
2130
|
+
if (text2.trim() === "") return void 0;
|
|
1828
2131
|
let parsed;
|
|
1829
2132
|
try {
|
|
1830
|
-
parsed = xmlParser.parse(
|
|
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,
|
|
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:
|
|
2159
|
+
raw: text2,
|
|
1857
2160
|
...code === void 0 || code === "" ? {} : { code }
|
|
1858
2161
|
});
|
|
1859
2162
|
}
|
|
1860
|
-
function resultEntry(
|
|
1861
|
-
const entry = asNode(
|
|
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(
|
|
1900
|
-
for (const [key, value] of Object.entries(
|
|
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
|
|
1916
|
-
const
|
|
1917
|
-
return typeof
|
|
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
|
|
1921
|
-
for (const key of path)
|
|
1922
|
-
return nonEmpty2(textOf(
|
|
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
|
|
2233
|
+
function describe3(error) {
|
|
1931
2234
|
return error instanceof Error ? error.message : String(error);
|
|
1932
2235
|
}
|
|
1933
2236
|
|
|
@@ -2026,9 +2329,9 @@ function isMessageQueue(queue) {
|
|
|
2026
2329
|
return queue === MESSAGE_QUEUE;
|
|
2027
2330
|
}
|
|
2028
2331
|
function toQueueRef(value) {
|
|
2029
|
-
const
|
|
2030
|
-
if (
|
|
2031
|
-
return /^\d+$/.test(
|
|
2332
|
+
const text2 = value?.trim();
|
|
2333
|
+
if (text2 === void 0 || text2 === "") return void 0;
|
|
2334
|
+
return /^\d+$/.test(text2) ? Number(text2) : text2;
|
|
2032
2335
|
}
|
|
2033
2336
|
|
|
2034
2337
|
// src/queue/queue-access.ts
|
|
@@ -2074,12 +2377,12 @@ function node(value) {
|
|
|
2074
2377
|
return typeof value === "object" && value !== null ? value : void 0;
|
|
2075
2378
|
}
|
|
2076
2379
|
function attribute(value, name) {
|
|
2077
|
-
const
|
|
2078
|
-
return typeof
|
|
2380
|
+
const text2 = node(value)?.[`@${name}`];
|
|
2381
|
+
return typeof text2 === "string" && text2.trim() !== "" ? text2.trim() : void 0;
|
|
2079
2382
|
}
|
|
2080
2383
|
function element(value) {
|
|
2081
|
-
const
|
|
2082
|
-
return
|
|
2384
|
+
const text2 = textOf(Array.isArray(value) ? value[0] : value)?.trim();
|
|
2385
|
+
return text2 === void 0 || text2 === "" ? void 0 : text2;
|
|
2083
2386
|
}
|
|
2084
2387
|
function textLines(raw, payloadName) {
|
|
2085
2388
|
let parsed;
|
|
@@ -2095,16 +2398,16 @@ function textLines(raw, payloadName) {
|
|
|
2095
2398
|
function paragraphLines(raw) {
|
|
2096
2399
|
return textLines(raw, "QueueAccessRS");
|
|
2097
2400
|
}
|
|
2098
|
-
function isEndOfDisplay(
|
|
2099
|
-
return
|
|
2401
|
+
function isEndOfDisplay(text2) {
|
|
2402
|
+
return text2?.trim() === END_OF_DISPLAY;
|
|
2100
2403
|
}
|
|
2101
2404
|
function isEndOfDisplayResult(error) {
|
|
2102
2405
|
return resultHasText(error, END_OF_DISPLAY);
|
|
2103
2406
|
}
|
|
2104
|
-
function resultHasText(error,
|
|
2407
|
+
function resultHasText(error, text2) {
|
|
2105
2408
|
const prefix = `${error.service}: `;
|
|
2106
2409
|
const description = error.message.startsWith(prefix) ? error.message.slice(prefix.length) : error.message;
|
|
2107
|
-
return description.split(/ \/ | \(|\)/).some((part) => part.trim() ===
|
|
2410
|
+
return description.split(/ \/ | \(|\)/).some((part) => part.trim() === text2);
|
|
2108
2411
|
}
|
|
2109
2412
|
function queueEndError(error, service) {
|
|
2110
2413
|
if (!(error instanceof SabreBusinessError) || error.service !== service) return void 0;
|
|
@@ -2185,8 +2488,8 @@ function parseHostCount(value) {
|
|
|
2185
2488
|
const match = /^[.\s]*(\d+)\s*$/.exec(value ?? "");
|
|
2186
2489
|
return match === null ? void 0 : Number(match[1]);
|
|
2187
2490
|
}
|
|
2188
|
-
function attribute2(
|
|
2189
|
-
const value =
|
|
2491
|
+
function attribute2(node4, name) {
|
|
2492
|
+
const value = node4?.[`@${name}`];
|
|
2190
2493
|
return typeof value === "string" ? value : void 0;
|
|
2191
2494
|
}
|
|
2192
2495
|
function parseQueueCountResponse(response) {
|
|
@@ -2249,8 +2552,8 @@ function malformed(detail, response) {
|
|
|
2249
2552
|
});
|
|
2250
2553
|
}
|
|
2251
2554
|
function nonEmpty3(value) {
|
|
2252
|
-
const
|
|
2253
|
-
return
|
|
2555
|
+
const text2 = value?.trim();
|
|
2556
|
+
return text2 === void 0 || text2 === "" ? void 0 : text2;
|
|
2254
2557
|
}
|
|
2255
2558
|
|
|
2256
2559
|
// src/queue/queue-list.ts
|
|
@@ -2321,13 +2624,13 @@ function queueActivityRequest(pcc, options, target = {}) {
|
|
|
2321
2624
|
...options
|
|
2322
2625
|
};
|
|
2323
2626
|
}
|
|
2324
|
-
function attribute3(
|
|
2325
|
-
const value = (Array.isArray(
|
|
2627
|
+
function attribute3(node4, name) {
|
|
2628
|
+
const value = (Array.isArray(node4) ? node4[0] : node4)?.[`@${name}`];
|
|
2326
2629
|
return typeof value === "string" ? value : void 0;
|
|
2327
2630
|
}
|
|
2328
2631
|
function nonEmpty4(value) {
|
|
2329
|
-
const
|
|
2330
|
-
return
|
|
2632
|
+
const text2 = value?.trim();
|
|
2633
|
+
return text2 === void 0 || text2 === "" ? void 0 : text2;
|
|
2331
2634
|
}
|
|
2332
2635
|
function parseQueueActivityResponse(response) {
|
|
2333
2636
|
const queues = [];
|
|
@@ -2352,10 +2655,10 @@ function parseQueueActivityResponse(response) {
|
|
|
2352
2655
|
...respondedAtField(response.raw)
|
|
2353
2656
|
};
|
|
2354
2657
|
}
|
|
2355
|
-
function counters(
|
|
2658
|
+
function counters(node4, label, response) {
|
|
2356
2659
|
const result = {};
|
|
2357
2660
|
for (const [element2, key] of COUNTERS) {
|
|
2358
|
-
const raw = attribute3(
|
|
2661
|
+
const raw = attribute3(node4[element2], "Count");
|
|
2359
2662
|
const count = parseHostCount(raw);
|
|
2360
2663
|
if (count === void 0) throw malformed2(`${label} con ${element2} Count="${raw ?? ""}"`, response);
|
|
2361
2664
|
result[key] = count;
|
|
@@ -2409,13 +2712,13 @@ function signatureLocator(lines) {
|
|
|
2409
2712
|
}
|
|
2410
2713
|
return void 0;
|
|
2411
2714
|
}
|
|
2412
|
-
function isEnd(response,
|
|
2413
|
-
return response.warnings.some(isEndOfDisplayResult) || isEndOfDisplay(
|
|
2715
|
+
function isEnd(response, text2) {
|
|
2716
|
+
return response.warnings.some(isEndOfDisplayResult) || isEndOfDisplay(text2.find((line) => line.trim() !== ""));
|
|
2414
2717
|
}
|
|
2415
2718
|
function parsePlaceStep(response) {
|
|
2416
|
-
const
|
|
2417
|
-
if (isEnd(response,
|
|
2418
|
-
const locator = signatureLocator(
|
|
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);
|
|
2419
2722
|
if (locator === void 0) {
|
|
2420
2723
|
throw new SabreGatewayError(`${QUEUE_PLACE_SERVICE}: respuesta no reconocida (sin l\xEDnea de firma del PNR ni fin de cola)`, {
|
|
2421
2724
|
protocol: "soap",
|
|
@@ -2424,11 +2727,11 @@ function parsePlaceStep(response) {
|
|
|
2424
2727
|
raw: response.raw
|
|
2425
2728
|
});
|
|
2426
2729
|
}
|
|
2427
|
-
return { done: false, kind: "pnr", item: { locator, paragraph:
|
|
2730
|
+
return { done: false, kind: "pnr", item: { locator, paragraph: text2 }, warnings: response.warnings, ...respondedAtField(response.raw) };
|
|
2428
2731
|
}
|
|
2429
2732
|
function parsePlaceUnknown(response) {
|
|
2430
|
-
const
|
|
2431
|
-
if (isEnd(response,
|
|
2733
|
+
const text2 = textLines(response.raw, "QueuePlaceRS");
|
|
2734
|
+
if (isEnd(response, text2) || signatureLocator(text2) !== void 0) return parsePlaceStep(response);
|
|
2432
2735
|
return parsePlaceResult(response);
|
|
2433
2736
|
}
|
|
2434
2737
|
function parsePlaceResult(response) {
|
|
@@ -2812,15 +3115,248 @@ function validateSelection(selection) {
|
|
|
2812
3115
|
});
|
|
2813
3116
|
}
|
|
2814
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
|
+
|
|
2815
3351
|
// src/reservation/reservation-history.ts
|
|
2816
3352
|
var RESERVATION_HISTORY_SERVICE = "itineraryHistory";
|
|
2817
|
-
var
|
|
3353
|
+
var NAMESPACE7 = "http://webservices.sabre.com/sabreXML/2011/10";
|
|
2818
3354
|
var LOCATOR = /^[A-Z0-9]{6}$/;
|
|
2819
3355
|
function isRecordLocator(value) {
|
|
2820
3356
|
return typeof value === "string" && LOCATOR.test(value);
|
|
2821
3357
|
}
|
|
2822
3358
|
function reservationHistoryPayload(locator) {
|
|
2823
|
-
return `<TravelItineraryHistoryRQ Version="2.4.0" xmlns="${
|
|
3359
|
+
return `<TravelItineraryHistoryRQ Version="2.4.0" xmlns="${NAMESPACE7}"><UniqueID ID="${xmlEscape(locator)}"/></TravelItineraryHistoryRQ>`;
|
|
2824
3360
|
}
|
|
2825
3361
|
function reservationHistoryRequest(locator, options) {
|
|
2826
3362
|
return {
|
|
@@ -2832,76 +3368,76 @@ function reservationHistoryRequest(locator, options) {
|
|
|
2832
3368
|
...options
|
|
2833
3369
|
};
|
|
2834
3370
|
}
|
|
2835
|
-
function
|
|
3371
|
+
function node3(value) {
|
|
2836
3372
|
const first = Array.isArray(value) ? value[0] : value;
|
|
2837
3373
|
return typeof first === "object" && first !== null ? first : void 0;
|
|
2838
3374
|
}
|
|
2839
|
-
function
|
|
2840
|
-
const
|
|
2841
|
-
if (typeof
|
|
2842
|
-
const trimmed =
|
|
3375
|
+
function attribute5(value, name) {
|
|
3376
|
+
const text2 = node3(value)?.[`@${name}`];
|
|
3377
|
+
if (typeof text2 !== "string") return void 0;
|
|
3378
|
+
const trimmed = text2.trim();
|
|
2843
3379
|
return trimmed === "" ? void 0 : trimmed;
|
|
2844
3380
|
}
|
|
2845
|
-
function
|
|
3381
|
+
function defined2(fields) {
|
|
2846
3382
|
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0));
|
|
2847
3383
|
}
|
|
2848
|
-
function
|
|
3384
|
+
function integer2(value) {
|
|
2849
3385
|
return value !== void 0 && /^\d+$/.test(value) ? Number(value) : void 0;
|
|
2850
3386
|
}
|
|
2851
3387
|
function parseReservationHistoryResponse(response) {
|
|
2852
|
-
const transactions = asArray(response.payload.HistoricalInfo).map((info) => transaction(
|
|
3388
|
+
const transactions = asArray(response.payload.HistoricalInfo).map((info) => transaction(node3(info) ?? {}, response));
|
|
2853
3389
|
return { transactions, warnings: response.warnings, ...respondedAtField(response.raw) };
|
|
2854
3390
|
}
|
|
2855
3391
|
function transaction(info, response) {
|
|
2856
|
-
const source =
|
|
2857
|
-
const flightSegments = asArray(
|
|
2858
|
-
(item) => asArray(
|
|
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))
|
|
2859
3395
|
);
|
|
2860
3396
|
return {
|
|
2861
|
-
...
|
|
2862
|
-
rph:
|
|
2863
|
-
createdAt:
|
|
2864
|
-
agent:
|
|
2865
|
-
agentDutyCode:
|
|
2866
|
-
agentFunctionCode:
|
|
2867
|
-
pcc:
|
|
2868
|
-
aaaPcc:
|
|
2869
|
-
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")
|
|
2870
3406
|
}),
|
|
2871
3407
|
flightSegments,
|
|
2872
3408
|
generalInfo: asArray(info.GeneralInfo).map((entry) => textEntry(entry, "GeneralInfo", response)),
|
|
2873
|
-
remarks: asArray(
|
|
3409
|
+
remarks: asArray(node3(info.RemarkInfo)?.Remark).map((entry) => textEntry(entry, "Remark", response)),
|
|
2874
3410
|
accounting: asArray(info.AccountingInfo).map(
|
|
2875
|
-
(entry) =>
|
|
3411
|
+
(entry) => defined2({ action: attribute5(entry, "Action"), type: attribute5(entry, "Type"), linkCode: attribute5(entry, "LinkCode") })
|
|
2876
3412
|
)
|
|
2877
3413
|
};
|
|
2878
3414
|
}
|
|
2879
3415
|
function textEntry(entry, label, response) {
|
|
2880
|
-
const action =
|
|
2881
|
-
if (action === void 0) throw
|
|
2882
|
-
const texts = asArray(
|
|
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) ?? "");
|
|
2883
3419
|
return { action, text: texts.join("\n") };
|
|
2884
3420
|
}
|
|
2885
3421
|
function flightSegment(segment, response) {
|
|
2886
|
-
const action =
|
|
2887
|
-
if (action === void 0) throw
|
|
3422
|
+
const action = attribute5(segment, "Action");
|
|
3423
|
+
if (action === void 0) throw malformed4("FlightSegment sin Action", response);
|
|
2888
3424
|
return {
|
|
2889
3425
|
action,
|
|
2890
|
-
...
|
|
2891
|
-
status:
|
|
2892
|
-
marketingAirline:
|
|
2893
|
-
flightNumber:
|
|
2894
|
-
bookingClass:
|
|
2895
|
-
origin:
|
|
2896
|
-
destination:
|
|
2897
|
-
departureDateTime:
|
|
2898
|
-
arrivalDateTime:
|
|
2899
|
-
numberInParty:
|
|
2900
|
-
supplierRef:
|
|
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")
|
|
2901
3437
|
})
|
|
2902
3438
|
};
|
|
2903
3439
|
}
|
|
2904
|
-
function
|
|
3440
|
+
function malformed4(detail, response) {
|
|
2905
3441
|
return new SabreGatewayError(`${RESERVATION_HISTORY_SERVICE}: respuesta no reconocida (${detail})`, {
|
|
2906
3442
|
protocol: "soap",
|
|
2907
3443
|
status: response.status,
|
|
@@ -2948,14 +3484,14 @@ var SessionReservation = class {
|
|
|
2948
3484
|
|
|
2949
3485
|
// src/session/command.ts
|
|
2950
3486
|
var SABRE_COMMAND_SERVICE = "sabreCommand";
|
|
2951
|
-
var
|
|
3487
|
+
var NAMESPACE8 = "http://webservices.sabre.com/sabreXML/2011/10";
|
|
2952
3488
|
var MAX_COMMAND_LENGTH = 1023;
|
|
2953
3489
|
var DOUBLE_WIDTH = /* @__PURE__ */ new Set(["!", "%", "$", "&", "\xA5", "\xA7"]);
|
|
2954
3490
|
function fail5(message) {
|
|
2955
3491
|
throw new TypeError(`sabre-client: ${message}`);
|
|
2956
3492
|
}
|
|
2957
3493
|
function sabreCommandPayload(command) {
|
|
2958
|
-
return `<SabreCommandLLSRQ xmlns="${
|
|
3494
|
+
return `<SabreCommandLLSRQ xmlns="${NAMESPACE8}" Version="2.0.0" ReturnHostCommand="true"><Request Output="SCREEN"><HostCommand>${xmlEscape(command)}</HostCommand></Request></SabreCommandLLSRQ>`;
|
|
2959
3495
|
}
|
|
2960
3496
|
function commandToken(command) {
|
|
2961
3497
|
return (command.trim().split(/[¥‡\s]/u)[0] ?? "").slice(0, 4);
|
|
@@ -3034,6 +3570,8 @@ var SabreSession = class {
|
|
|
3034
3570
|
queue;
|
|
3035
3571
|
/** La reserva: el historial de un PNR, dentro de esta sesión. */
|
|
3036
3572
|
reservation;
|
|
3573
|
+
/** Reportes del PCC: las ventas del día (`dailySalesSummary`), dentro de esta sesión. */
|
|
3574
|
+
reporting;
|
|
3037
3575
|
#transport;
|
|
3038
3576
|
#pcc;
|
|
3039
3577
|
#conversationId;
|
|
@@ -3062,6 +3600,7 @@ var SabreSession = class {
|
|
|
3062
3600
|
log: internal.transport.log
|
|
3063
3601
|
});
|
|
3064
3602
|
this.reservation = new SessionReservation({ send: (request) => this[sessionSend](request) });
|
|
3603
|
+
this.reporting = new SessionReporting({ send: (request) => this[sessionSend](request), pcc: internal.pcc });
|
|
3065
3604
|
if (internal.keepAlive) this.#scheduleKeepAlive(KEEP_ALIVE_IDLE_MS);
|
|
3066
3605
|
}
|
|
3067
3606
|
/**
|
|
@@ -3226,8 +3765,8 @@ function checkPingResponse(response) {
|
|
|
3226
3765
|
const error = asArray(errors?.Error)[0];
|
|
3227
3766
|
if (error !== void 0) {
|
|
3228
3767
|
const type = error["@Type"];
|
|
3229
|
-
const
|
|
3230
|
-
throw new SabreGatewayError(`ping: OTA_PingRS con error${
|
|
3768
|
+
const text2 = textOf(error)?.trim();
|
|
3769
|
+
throw new SabreGatewayError(`ping: OTA_PingRS con error${text2 ? `: ${text2}` : ""}`, {
|
|
3231
3770
|
protocol: "soap",
|
|
3232
3771
|
status: response.status,
|
|
3233
3772
|
...typeof type === "string" && type !== "" && { code: type },
|
|
@@ -3417,22 +3956,22 @@ function buildCredentials(options) {
|
|
|
3417
3956
|
var MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
3418
3957
|
var DAY_MS = 864e5;
|
|
3419
3958
|
var HALF_YEAR_DAYS = 184;
|
|
3420
|
-
function resolveHostDate(
|
|
3959
|
+
function resolveHostDate(text2, referenceDate, options = {}) {
|
|
3421
3960
|
const reference = referenceDay(referenceDate);
|
|
3422
3961
|
const direction = options.direction ?? "past";
|
|
3423
3962
|
if (direction !== "past" && direction !== "nearest") {
|
|
3424
3963
|
throw new TypeError("sabre-client: direction debe ser 'past' o 'nearest'");
|
|
3425
3964
|
}
|
|
3426
|
-
if (typeof
|
|
3427
|
-
const parsed = parseHostDate(
|
|
3965
|
+
if (typeof text2 !== "string") return void 0;
|
|
3966
|
+
const parsed = parseHostDate(text2.trim().toUpperCase());
|
|
3428
3967
|
if (parsed === void 0) return void 0;
|
|
3429
3968
|
const year = parsed.year ?? resolveYear(parsed.month, parsed.day, reference, direction);
|
|
3430
3969
|
if (year === void 0 || !isValidDate(year, parsed.month, parsed.day)) return void 0;
|
|
3431
3970
|
const date = `${pad(year, 4)}-${pad(parsed.month, 2)}-${pad(parsed.day, 2)}`;
|
|
3432
3971
|
return parsed.time === void 0 ? date : `${date}T${parsed.time}`;
|
|
3433
3972
|
}
|
|
3434
|
-
function parseHostDate(
|
|
3435
|
-
let match = /^(\d{1,2})([A-Z]{3})(\d{2})?$/.exec(
|
|
3973
|
+
function parseHostDate(text2) {
|
|
3974
|
+
let match = /^(\d{1,2})([A-Z]{3})(\d{2})?$/.exec(text2);
|
|
3436
3975
|
if (match !== null) {
|
|
3437
3976
|
const month2 = MONTHS.indexOf(match[2]) + 1;
|
|
3438
3977
|
if (month2 === 0) return void 0;
|
|
@@ -3442,7 +3981,7 @@ function parseHostDate(text) {
|
|
|
3442
3981
|
day: Number(match[1])
|
|
3443
3982
|
};
|
|
3444
3983
|
}
|
|
3445
|
-
match = /^(?:(\d{4})-)?(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/.exec(
|
|
3984
|
+
match = /^(?:(\d{4})-)?(\d{2})-(\d{2})(?:T(\d{2}):(\d{2}))?$/.exec(text2);
|
|
3446
3985
|
if (match === null) return void 0;
|
|
3447
3986
|
const [, year, month, day, hours, minutes] = match;
|
|
3448
3987
|
if (hours !== void 0 && (Number(hours) > 23 || Number(minutes) > 59)) return void 0;
|
|
@@ -3643,8 +4182,8 @@ function parseSegment(line, referenceDate) {
|
|
|
3643
4182
|
text: line.trim()
|
|
3644
4183
|
};
|
|
3645
4184
|
}
|
|
3646
|
-
function parseTicketingEntry(number,
|
|
3647
|
-
const ticketed = TICKETED.exec(
|
|
4185
|
+
function parseTicketingEntry(number, text2, referenceDate) {
|
|
4186
|
+
const ticketed = TICKETED.exec(text2);
|
|
3648
4187
|
if (ticketed !== null) {
|
|
3649
4188
|
const [, date, pseudoCityCode, agentSine] = ticketed;
|
|
3650
4189
|
return {
|
|
@@ -3653,10 +4192,10 @@ function parseTicketingEntry(number, text, referenceDate) {
|
|
|
3653
4192
|
...date !== void 0 && withDate(date, referenceDate),
|
|
3654
4193
|
...pseudoCityCode !== void 0 && { pseudoCityCode },
|
|
3655
4194
|
...agentSine !== void 0 && { agentSine },
|
|
3656
|
-
text
|
|
4195
|
+
text: text2
|
|
3657
4196
|
};
|
|
3658
4197
|
}
|
|
3659
|
-
const arrangement = ARRANGEMENT.exec(
|
|
4198
|
+
const arrangement = ARRANGEMENT.exec(text2);
|
|
3660
4199
|
if (arrangement !== null) {
|
|
3661
4200
|
const [, code, pseudoCityCode, date] = arrangement;
|
|
3662
4201
|
return {
|
|
@@ -3664,11 +4203,11 @@ function parseTicketingEntry(number, text, referenceDate) {
|
|
|
3664
4203
|
code,
|
|
3665
4204
|
...date !== void 0 && withDate(date, referenceDate),
|
|
3666
4205
|
...pseudoCityCode !== void 0 && { pseudoCityCode },
|
|
3667
|
-
text
|
|
4206
|
+
text: text2
|
|
3668
4207
|
};
|
|
3669
4208
|
}
|
|
3670
|
-
const ticket = TICKET.exec(
|
|
3671
|
-
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 };
|
|
3672
4211
|
return void 0;
|
|
3673
4212
|
}
|
|
3674
4213
|
function lastSignatureIndex(lines) {
|
|
@@ -3689,12 +4228,12 @@ function parseSignature(line, referenceDate) {
|
|
|
3689
4228
|
locator
|
|
3690
4229
|
};
|
|
3691
4230
|
}
|
|
3692
|
-
function withDate(
|
|
3693
|
-
const date = resolveHostDate(
|
|
4231
|
+
function withDate(text2, referenceDate) {
|
|
4232
|
+
const date = resolveHostDate(text2, referenceDate, { direction: "nearest" });
|
|
3694
4233
|
return date === void 0 ? {} : { date };
|
|
3695
4234
|
}
|
|
3696
|
-
function hostTime(
|
|
3697
|
-
const match = /^(\d{1,2})(\d{2})([APNM])?$/.exec(
|
|
4235
|
+
function hostTime(text2) {
|
|
4236
|
+
const match = /^(\d{1,2})(\d{2})([APNM])?$/.exec(text2);
|
|
3698
4237
|
if (match === null) return void 0;
|
|
3699
4238
|
let hours = Number(match[1]);
|
|
3700
4239
|
const minutes = Number(match[2]);
|
|
@@ -3714,9 +4253,9 @@ function hostTime(text) {
|
|
|
3714
4253
|
// src/queue/queue-place-remark.ts
|
|
3715
4254
|
var MONTHS2 = /* @__PURE__ */ new Set(["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]);
|
|
3716
4255
|
var REMARK = /^([A-Z0-9]{3,4})(\d{2})([A-Z]{3})(\d{1,3})\/(\d{1,3})$/;
|
|
3717
|
-
function parseQueuePlaceRemark(
|
|
3718
|
-
if (typeof
|
|
3719
|
-
const match = REMARK.exec(
|
|
4256
|
+
function parseQueuePlaceRemark(text2) {
|
|
4257
|
+
if (typeof text2 !== "string") return null;
|
|
4258
|
+
const match = REMARK.exec(text2.trim());
|
|
3720
4259
|
if (match === null) return null;
|
|
3721
4260
|
const [, pcc, day, month, queueText, picText] = match;
|
|
3722
4261
|
const dayNumber2 = Number(day);
|
|
@@ -3811,8 +4350,9 @@ function fareComponent(fareComponentId, component) {
|
|
|
3811
4350
|
}
|
|
3812
4351
|
|
|
3813
4352
|
// src/index.ts
|
|
3814
|
-
var VERSION = "0.
|
|
4353
|
+
var VERSION = "0.7.0";
|
|
3815
4354
|
export {
|
|
4355
|
+
MAX_COMMAND_LENGTH,
|
|
3816
4356
|
MESSAGE_QUEUE,
|
|
3817
4357
|
Sabre,
|
|
3818
4358
|
SabreBusinessError,
|
|
@@ -3821,6 +4361,7 @@ export {
|
|
|
3821
4361
|
SabreLegacy,
|
|
3822
4362
|
SabreOrchestrationError,
|
|
3823
4363
|
SabreSessionError,
|
|
4364
|
+
SabreUncertainOutcomeError,
|
|
3824
4365
|
SabreWarning,
|
|
3825
4366
|
VERSION,
|
|
3826
4367
|
buildBookingFlightDetails,
|
|
@@ -3830,6 +4371,7 @@ export {
|
|
|
3830
4371
|
isMessageQueue,
|
|
3831
4372
|
parseQueueParagraph,
|
|
3832
4373
|
parseQueuePlaceRemark,
|
|
3833
|
-
resolveHostDate
|
|
4374
|
+
resolveHostDate,
|
|
4375
|
+
sabreCommandLength
|
|
3834
4376
|
};
|
|
3835
4377
|
//# sourceMappingURL=index.js.map
|