sabre-client 0.3.0-next.2 → 0.4.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 +89 -15
- package/README.md +86 -15
- package/dist/index.d.ts +68 -3
- package/dist/index.js +112 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1491,14 +1491,16 @@ var TRANSIENT_CODES = /* @__PURE__ */ new Set([
|
|
|
1491
1491
|
var PREVIOUS_ENTRY_ACTIVE_RETRIES = 3;
|
|
1492
1492
|
var PREVIOUS_ENTRY_ACTIVE_DELAY_MS = 1e3;
|
|
1493
1493
|
var PREVIOUS_ENTRY_ACTIVE = /PREVIOUS ENTRY ACTIVE/i;
|
|
1494
|
-
var
|
|
1494
|
+
var PARSER_OPTIONS = {
|
|
1495
1495
|
ignoreAttributes: false,
|
|
1496
1496
|
attributeNamePrefix: "@",
|
|
1497
1497
|
removeNSPrefix: true,
|
|
1498
1498
|
parseTagValue: false,
|
|
1499
1499
|
parseAttributeValue: false,
|
|
1500
1500
|
processEntities: true
|
|
1501
|
-
}
|
|
1501
|
+
};
|
|
1502
|
+
var parser = new XMLParser(PARSER_OPTIONS);
|
|
1503
|
+
var untrimmedParser = new XMLParser({ ...PARSER_OPTIONS, trimValues: false });
|
|
1502
1504
|
var SoapTransport = class {
|
|
1503
1505
|
#config;
|
|
1504
1506
|
#log;
|
|
@@ -1738,11 +1740,11 @@ function scrubToken(text, security) {
|
|
|
1738
1740
|
const token = security.binarySecurityToken;
|
|
1739
1741
|
return text.split(token).join(REDACTED).split(xmlEscape(token)).join(REDACTED);
|
|
1740
1742
|
}
|
|
1741
|
-
function parseEnvelope(text) {
|
|
1743
|
+
function parseEnvelope(text, xmlParser = parser) {
|
|
1742
1744
|
if (text.trim() === "") return void 0;
|
|
1743
1745
|
let parsed;
|
|
1744
1746
|
try {
|
|
1745
|
-
parsed =
|
|
1747
|
+
parsed = xmlParser.parse(text);
|
|
1746
1748
|
} catch {
|
|
1747
1749
|
return void 0;
|
|
1748
1750
|
}
|
|
@@ -1807,6 +1809,10 @@ function retryReason(error, idempotent) {
|
|
|
1807
1809
|
if (error.status === void 0 || TRANSIENT_STATUS.has(error.status)) return "transient";
|
|
1808
1810
|
return void 0;
|
|
1809
1811
|
}
|
|
1812
|
+
function untrimmedPayload(raw) {
|
|
1813
|
+
const document = parseEnvelope(raw, untrimmedParser);
|
|
1814
|
+
return document === void 0 ? void 0 : asNode(firstElement(document.Body)[1]);
|
|
1815
|
+
}
|
|
1810
1816
|
function firstElement(node2) {
|
|
1811
1817
|
for (const [key, value] of Object.entries(node2)) {
|
|
1812
1818
|
if (!key.startsWith("@") && key !== "#text") return [key, Array.isArray(value) ? value[0] : value];
|
|
@@ -2511,11 +2517,63 @@ function validateSelection(selection) {
|
|
|
2511
2517
|
});
|
|
2512
2518
|
}
|
|
2513
2519
|
|
|
2520
|
+
// src/session/command.ts
|
|
2521
|
+
var SABRE_COMMAND_SERVICE = "sabreCommand";
|
|
2522
|
+
var NAMESPACE5 = "http://webservices.sabre.com/sabreXML/2011/10";
|
|
2523
|
+
var MAX_COMMAND_LENGTH = 1023;
|
|
2524
|
+
var DOUBLE_WIDTH = /* @__PURE__ */ new Set(["!", "%", "$", "&", "\xA5", "\xA7"]);
|
|
2525
|
+
function fail4(message) {
|
|
2526
|
+
throw new TypeError(`sabre-client: ${message}`);
|
|
2527
|
+
}
|
|
2528
|
+
function sabreCommandPayload(command) {
|
|
2529
|
+
return `<SabreCommandLLSRQ xmlns="${NAMESPACE5}" Version="2.0.0" ReturnHostCommand="true"><Request Output="SCREEN"><HostCommand>${xmlEscape(command)}</HostCommand></Request></SabreCommandLLSRQ>`;
|
|
2530
|
+
}
|
|
2531
|
+
function commandToken(command) {
|
|
2532
|
+
return (command.trim().split(/[¥‡\s]/u)[0] ?? "").slice(0, 4);
|
|
2533
|
+
}
|
|
2534
|
+
function sabreCommandLength(command) {
|
|
2535
|
+
let length = 0;
|
|
2536
|
+
for (const char of command) length += DOUBLE_WIDTH.has(char) ? 2 : 1;
|
|
2537
|
+
return length;
|
|
2538
|
+
}
|
|
2539
|
+
function sabreCommandRequest(command, options) {
|
|
2540
|
+
if (typeof command !== "string" || command.trim() === "") fail4("command es requerido y debe ser un string no vac\xEDo");
|
|
2541
|
+
const length = sabreCommandLength(command);
|
|
2542
|
+
if (length > MAX_COMMAND_LENGTH) {
|
|
2543
|
+
fail4(`command mide ${length} y el m\xE1ximo es ${MAX_COMMAND_LENGTH} (!, %, $, &, \xA5 y \xA7 cuentan 2); Sabre responder\xEDa REQUEST EXCEEDED MAXIMUM LENGTH`);
|
|
2544
|
+
}
|
|
2545
|
+
const { idempotent = false, ...requestOptions } = options;
|
|
2546
|
+
if (typeof idempotent !== "boolean") fail4("idempotent debe ser un boolean");
|
|
2547
|
+
return {
|
|
2548
|
+
service: SABRE_COMMAND_SERVICE,
|
|
2549
|
+
action: "SabreCommandLLSRQ",
|
|
2550
|
+
payload: sabreCommandPayload(command),
|
|
2551
|
+
idempotent,
|
|
2552
|
+
operation: "command",
|
|
2553
|
+
...requestOptions
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
function parseSabreCommandResponse(response) {
|
|
2557
|
+
if (response.payloadName !== "SabreCommandLLSRS") {
|
|
2558
|
+
throw new SabreGatewayError(`${SABRE_COMMAND_SERVICE}: respuesta no reconocida (${response.payloadName})`, {
|
|
2559
|
+
protocol: "soap",
|
|
2560
|
+
status: response.status,
|
|
2561
|
+
retryable: false,
|
|
2562
|
+
raw: response.raw
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
return {
|
|
2566
|
+
text: textOf((untrimmedPayload(response.raw) ?? response.payload).Response) ?? "",
|
|
2567
|
+
warnings: response.warnings,
|
|
2568
|
+
messageId: response.messageId
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2514
2572
|
// src/session/session.ts
|
|
2515
2573
|
var OTA_NAMESPACE = "http://www.opentravel.org/OTA/2002/11";
|
|
2516
2574
|
var SWS_NAMESPACE = "http://webservices.sabre.com";
|
|
2517
2575
|
var sessionSend = /* @__PURE__ */ Symbol("sabre-client.session.send");
|
|
2518
|
-
function
|
|
2576
|
+
function fail5(message) {
|
|
2519
2577
|
throw new TypeError(`sabre-client: ${message}`);
|
|
2520
2578
|
}
|
|
2521
2579
|
var SESSION_ERROR_KINDS = /* @__PURE__ */ new Map([
|
|
@@ -2587,6 +2645,18 @@ var SabreSession = class {
|
|
|
2587
2645
|
async ping(options = {}) {
|
|
2588
2646
|
checkPingResponse(await this[sessionSend]({ ...pingRequest(), ...options }));
|
|
2589
2647
|
}
|
|
2648
|
+
/**
|
|
2649
|
+
* Manda un comando de host (`SabreCommandLLSRQ` 2.0.0) y devuelve la pantalla como texto, sin
|
|
2650
|
+
* interpretarla. Es una vía de escape: sin tipos ni garantías, y un comando puede cambiar la
|
|
2651
|
+
* configuración del PCC o emitir. El comando va tal cual; el separador de Sabre Red (`‡`) se
|
|
2652
|
+
* escribe `¥`. Sin reintento salvo `{ idempotent: true }`, para comandos de solo lectura. La
|
|
2653
|
+
* pantalla llega sin recortar, y más de 1023 caracteres (contados como Sabre) lanza sin red.
|
|
2654
|
+
*/
|
|
2655
|
+
async command(command, options = {}) {
|
|
2656
|
+
const request = sabreCommandRequest(command, options);
|
|
2657
|
+
this.#transport.log.emit("debug", "sabre.session.command", { command: commandToken(command) });
|
|
2658
|
+
return parseSabreCommandResponse(await this[sessionSend](request));
|
|
2659
|
+
}
|
|
2590
2660
|
/**
|
|
2591
2661
|
* Cierra la sesión con `SessionCloseRQ`: Sabre descarta el AAA e invalida el token. Es
|
|
2592
2662
|
* idempotente: las llamadas siguientes devuelven la misma promesa, sin volver a la red. Si el
|
|
@@ -2614,11 +2684,11 @@ var SabreSession = class {
|
|
|
2614
2684
|
return this.#send(request);
|
|
2615
2685
|
}
|
|
2616
2686
|
async #send(request) {
|
|
2617
|
-
if (this.#closing !== void 0)
|
|
2687
|
+
if (this.#closing !== void 0) fail5("session closed");
|
|
2618
2688
|
if (this.#unusable !== void 0) {
|
|
2619
2689
|
throw new TypeError(`sabre-client: session unusable (${this.#unusable.kind})`, { cause: this.#unusable });
|
|
2620
2690
|
}
|
|
2621
|
-
if (this.#busy)
|
|
2691
|
+
if (this.#busy) fail5("session busy");
|
|
2622
2692
|
this.#busy = true;
|
|
2623
2693
|
try {
|
|
2624
2694
|
const response = await this.#transport.request({ ...request, ...this.#envelopeFields() });
|
|
@@ -2773,7 +2843,7 @@ function usernameToken(credentials) {
|
|
|
2773
2843
|
}
|
|
2774
2844
|
async function openSession(transport, credentials, options = {}) {
|
|
2775
2845
|
const { keepAlive = false, ...requestOptions } = options;
|
|
2776
|
-
if (typeof keepAlive !== "boolean")
|
|
2846
|
+
if (typeof keepAlive !== "boolean") fail5("keepAlive debe ser un boolean");
|
|
2777
2847
|
const conversationId = globalThis.crypto.randomUUID();
|
|
2778
2848
|
const request = {
|
|
2779
2849
|
service: "sessionCreate",
|
|
@@ -2876,6 +2946,38 @@ var SabreLegacy = class {
|
|
|
2876
2946
|
}
|
|
2877
2947
|
};
|
|
2878
2948
|
|
|
2949
|
+
// src/auth/credentials.ts
|
|
2950
|
+
var DEFAULT_DOMAIN = "AA";
|
|
2951
|
+
function fail6(message) {
|
|
2952
|
+
throw new TypeError(`sabre-client: ${message}`);
|
|
2953
|
+
}
|
|
2954
|
+
function requireString2(value, field) {
|
|
2955
|
+
if (typeof value !== "string" || value.trim() === "") fail6(`${field} es requerido y debe ser un string no vac\xEDo`);
|
|
2956
|
+
}
|
|
2957
|
+
function buildCredentials(options) {
|
|
2958
|
+
if (typeof options !== "object" || options === null) fail6("buildCredentials recibe un objeto");
|
|
2959
|
+
const authVersion = options.authVersion ?? "v3";
|
|
2960
|
+
if (authVersion !== "v3" && authVersion !== "v2") {
|
|
2961
|
+
fail6(`authVersion debe ser 'v3' o 'v2' (recibido: ${JSON.stringify(authVersion)})`);
|
|
2962
|
+
}
|
|
2963
|
+
const { user, pcc, password, clientId, clientSecret } = options;
|
|
2964
|
+
const domain = options.domain ?? DEFAULT_DOMAIN;
|
|
2965
|
+
requireString2(user, "user");
|
|
2966
|
+
requireString2(password, "password");
|
|
2967
|
+
if (!isPseudoCityCode(pcc)) fail6("pcc debe tener 3 o 4 caracteres alfanum\xE9ricos");
|
|
2968
|
+
requireString2(domain, "domain");
|
|
2969
|
+
if (/[-:]/.test(domain)) fail6("domain no puede contener '-' ni ':'");
|
|
2970
|
+
if (clientId === void 0 !== (clientSecret === void 0)) {
|
|
2971
|
+
fail6("clientId y clientSecret van juntos: pasa los dos o ninguno");
|
|
2972
|
+
}
|
|
2973
|
+
if (authVersion === "v3" || clientId !== void 0) {
|
|
2974
|
+
requireString2(clientId, authVersion === "v3" ? "clientId (requerido con authVersion v3)" : "clientId");
|
|
2975
|
+
requireString2(clientSecret, authVersion === "v3" ? "clientSecret (requerido con authVersion v3)" : "clientSecret");
|
|
2976
|
+
}
|
|
2977
|
+
const username = authVersion === "v3" ? `${user}-${pcc}-${domain}` : `V1:${user}:${pcc}:${domain}`;
|
|
2978
|
+
return clientId === void 0 ? { username, password } : { clientId, clientSecret, username, password };
|
|
2979
|
+
}
|
|
2980
|
+
|
|
2879
2981
|
// src/queue/host-date.ts
|
|
2880
2982
|
var MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
|
|
2881
2983
|
var DAY_MS = 864e5;
|
|
@@ -3258,7 +3360,7 @@ function fareComponent(fareComponentId, component) {
|
|
|
3258
3360
|
}
|
|
3259
3361
|
|
|
3260
3362
|
// src/index.ts
|
|
3261
|
-
var VERSION = "0.
|
|
3363
|
+
var VERSION = "0.4.0";
|
|
3262
3364
|
export {
|
|
3263
3365
|
Sabre,
|
|
3264
3366
|
SabreBusinessError,
|
|
@@ -3270,6 +3372,7 @@ export {
|
|
|
3270
3372
|
SabreWarning,
|
|
3271
3373
|
VERSION,
|
|
3272
3374
|
buildBookingFlightDetails,
|
|
3375
|
+
buildCredentials,
|
|
3273
3376
|
buildRevalidateItineraryRequest,
|
|
3274
3377
|
buildSeatAvailabilityRequest,
|
|
3275
3378
|
parseQueueParagraph,
|