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/README.es.md +265 -16
- package/README.md +266 -16
- package/dist/index.d.ts +354 -10
- package/dist/index.js +786 -181
- package/dist/index.js.map +1 -1
- package/dist/{reservation-history-BKRFlxpA.d.ts → reservation-history-BRKQBHsj.d.ts} +178 -3
- package/dist/testing.d.ts +127 -8
- package/dist/testing.js +811 -51
- 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
|
|
|
@@ -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
|
|
2012
|
-
if (
|
|
2013
|
-
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;
|
|
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
|
|
2060
|
-
return typeof
|
|
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
|
|
2064
|
-
return
|
|
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(
|
|
2081
|
-
return
|
|
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,
|
|
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() ===
|
|
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
|
|
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(
|
|
2171
|
-
const value =
|
|
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
|
|
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
|
|
2216
|
-
return
|
|
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
|
|
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(
|
|
2288
|
-
const value = (Array.isArray(
|
|
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
|
|
2293
|
-
return
|
|
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(
|
|
2658
|
+
function counters(node4, label, response) {
|
|
2319
2659
|
const result = {};
|
|
2320
2660
|
for (const [element2, key] of COUNTERS) {
|
|
2321
|
-
const raw = attribute3(
|
|
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,
|
|
2376
|
-
return response.warnings.some(isEndOfDisplayResult) || isEndOfDisplay(
|
|
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
|
|
2380
|
-
if (isEnd(response,
|
|
2381
|
-
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);
|
|
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:
|
|
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
|
|
2394
|
-
if (isEnd(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.
|
|
2451
|
-
* `
|
|
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
|
|
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).
|
|
2467
|
-
*
|
|
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
|
|
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
|
|
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="${
|
|
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
|
|
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
|
|
2777
|
-
const
|
|
2778
|
-
if (typeof
|
|
2779
|
-
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();
|
|
2780
3379
|
return trimmed === "" ? void 0 : trimmed;
|
|
2781
3380
|
}
|
|
2782
|
-
function
|
|
3381
|
+
function defined2(fields) {
|
|
2783
3382
|
return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0));
|
|
2784
3383
|
}
|
|
2785
|
-
function
|
|
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(
|
|
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 =
|
|
2794
|
-
const flightSegments = asArray(
|
|
2795
|
-
(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))
|
|
2796
3395
|
);
|
|
2797
3396
|
return {
|
|
2798
|
-
...
|
|
2799
|
-
rph:
|
|
2800
|
-
createdAt:
|
|
2801
|
-
agent:
|
|
2802
|
-
agentDutyCode:
|
|
2803
|
-
agentFunctionCode:
|
|
2804
|
-
pcc:
|
|
2805
|
-
aaaPcc:
|
|
2806
|
-
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(
|
|
3409
|
+
remarks: asArray(node3(info.RemarkInfo)?.Remark).map((entry) => textEntry(entry, "Remark", response)),
|
|
2811
3410
|
accounting: asArray(info.AccountingInfo).map(
|
|
2812
|
-
(entry) =>
|
|
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 =
|
|
2818
|
-
if (action === void 0) throw
|
|
2819
|
-
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) ?? "");
|
|
2820
3419
|
return { action, text: texts.join("\n") };
|
|
2821
3420
|
}
|
|
2822
3421
|
function flightSegment(segment, response) {
|
|
2823
|
-
const action =
|
|
2824
|
-
if (action === void 0) throw
|
|
3422
|
+
const action = attribute5(segment, "Action");
|
|
3423
|
+
if (action === void 0) throw malformed4("FlightSegment sin Action", response);
|
|
2825
3424
|
return {
|
|
2826
3425
|
action,
|
|
2827
|
-
...
|
|
2828
|
-
status:
|
|
2829
|
-
marketingAirline:
|
|
2830
|
-
flightNumber:
|
|
2831
|
-
bookingClass:
|
|
2832
|
-
origin:
|
|
2833
|
-
destination:
|
|
2834
|
-
departureDateTime:
|
|
2835
|
-
arrivalDateTime:
|
|
2836
|
-
numberInParty:
|
|
2837
|
-
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")
|
|
2838
3437
|
})
|
|
2839
3438
|
};
|
|
2840
3439
|
}
|
|
2841
|
-
function
|
|
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
|
|
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="${
|
|
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
|
|
3167
|
-
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}` : ""}`, {
|
|
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(
|
|
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
|
|
3364
|
-
const parsed = parseHostDate(
|
|
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(
|
|
3372
|
-
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);
|
|
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(
|
|
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,
|
|
3584
|
-
const ticketed = TICKETED.exec(
|
|
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(
|
|
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(
|
|
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(
|
|
3630
|
-
const date = resolveHostDate(
|
|
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(
|
|
3634
|
-
const match = /^(\d{1,2})(\d{2})([APNM])?$/.exec(
|
|
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(
|
|
3655
|
-
if (typeof
|
|
3656
|
-
const match = REMARK.exec(
|
|
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.
|
|
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
|