sabre-client 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -223,6 +223,22 @@ function isObject(value) {
223
223
  return typeof value === "object" && value !== null;
224
224
  }
225
225
 
226
+ // src/http/raw-request.ts
227
+ function rawRequestHeaders(headers) {
228
+ const copy = new Headers(headers);
229
+ copy.delete("Authorization");
230
+ return copy;
231
+ }
232
+ function rawHooks(options, operation) {
233
+ const { onRawResponse, onRawRequest } = options;
234
+ if (onRawResponse === void 0 && onRawRequest === void 0) return {};
235
+ return {
236
+ operation,
237
+ ...onRawResponse === void 0 ? {} : { onRawResponse },
238
+ ...onRawRequest === void 0 ? {} : { onRawRequest }
239
+ };
240
+ }
241
+
226
242
  // src/air/bargain-finder-max.ts
227
243
  var BARGAIN_FINDER_MAX_SERVICE = "bargainFinderMax";
228
244
  var PATH = "/v5/offers/shop";
@@ -248,7 +264,7 @@ async function bargainFinderMax(transport, request, options = {}) {
248
264
  classify: classifyBargainFinderMax,
249
265
  ...options.retry === void 0 ? {} : { retry: options.retry },
250
266
  ...options.signal === void 0 ? {} : { signal: options.signal },
251
- ...options.onRawResponse === void 0 ? {} : { operation: "air.bargainFinderMax", onRawResponse: options.onRawResponse }
267
+ ...rawHooks(options, "air.bargainFinderMax")
252
268
  });
253
269
  return denormalizeGroupedItineraryResponse(body.groupedItineraryResponse, BARGAIN_FINDER_MAX_SERVICE);
254
270
  }
@@ -327,7 +343,7 @@ async function send(transport, path, service, request, options) {
327
343
  classify: (raw) => classifyGetSeats(raw, service),
328
344
  ...options.retry === void 0 ? {} : { retry: options.retry },
329
345
  ...options.signal === void 0 ? {} : { signal: options.signal },
330
- ...options.onRawResponse === void 0 ? {} : { operation: `air.${service}`, onRawResponse: options.onRawResponse }
346
+ ...rawHooks(options, `air.${service}`)
331
347
  });
332
348
  return {
333
349
  response: body.response,
@@ -418,7 +434,7 @@ async function revalidateItinerary(transport, request, options = {}) {
418
434
  classify: classifyRevalidateItinerary,
419
435
  ...options.retry === void 0 ? {} : { retry: options.retry },
420
436
  ...options.signal === void 0 ? {} : { signal: options.signal },
421
- ...options.onRawResponse === void 0 ? {} : { operation: "air.revalidateItinerary", onRawResponse: options.onRawResponse }
437
+ ...rawHooks(options, "air.revalidateItinerary")
422
438
  });
423
439
  return denormalizeGroupedItineraryResponse(body.groupedItineraryResponse, REVALIDATE_ITINERARY_SERVICE);
424
440
  }
@@ -797,7 +813,7 @@ async function cancelBooking(transport, request, options = {}) {
797
813
  body: request,
798
814
  classify: classifyCancelBooking,
799
815
  ...options.signal === void 0 ? {} : { signal: options.signal },
800
- ...options.onRawResponse === void 0 ? {} : { operation: "booking.cancel", onRawResponse: options.onRawResponse }
816
+ ...rawHooks(options, "booking.cancel")
801
817
  });
802
818
  return toCancelBookingResponse(body);
803
819
  }
@@ -974,7 +990,7 @@ async function createBooking(transport, request, options = {}) {
974
990
  body: request,
975
991
  classify: classifyCreateBooking,
976
992
  ...options.signal === void 0 ? {} : { signal: options.signal },
977
- ...options.onRawResponse === void 0 ? {} : { operation: "booking.create", onRawResponse: options.onRawResponse }
993
+ ...rawHooks(options, "booking.create")
978
994
  });
979
995
  return toCreateBookingResponse(body);
980
996
  }
@@ -1066,7 +1082,7 @@ async function getBooking(transport, request, options = {}) {
1066
1082
  body: request,
1067
1083
  classify: classifyGetBooking,
1068
1084
  ...options.signal === void 0 ? {} : { signal: options.signal },
1069
- ...options.onRawResponse === void 0 ? {} : { operation: "booking.get", onRawResponse: options.onRawResponse }
1085
+ ...rawHooks(options, "booking.get")
1070
1086
  });
1071
1087
  return toGetBookingResponse(body);
1072
1088
  }
@@ -1227,7 +1243,6 @@ function resolveConfig(config) {
1227
1243
  requireString(credentials.clientId, "credentials.clientId (requerido con authVersion v3)");
1228
1244
  requireString(credentials.clientSecret, "credentials.clientSecret (requerido con authVersion v3)");
1229
1245
  }
1230
- if (config.pcc !== void 0) requireString(config.pcc, "pcc");
1231
1246
  const { logger } = config;
1232
1247
  if (logger !== void 0) {
1233
1248
  if (!isObject3(logger)) fail("logger debe ser un objeto con debug/info/warn/error");
@@ -1248,12 +1263,12 @@ function resolveConfig(config) {
1248
1263
  const fetchImpl = config.fetch ?? globalThis.fetch;
1249
1264
  if (typeof fetchImpl !== "function") fail("no hay fetch disponible: pasa config.fetch");
1250
1265
  return {
1266
+ // Ya validado arriba: con v3 trae clientId/clientSecret; la marca de versión ya se resolvió.
1251
1267
  credentials: Object.freeze({ ...credentials }),
1252
1268
  environment,
1253
1269
  baseUrl: BASE_URLS[environment],
1254
1270
  soapBaseUrl: SOAP_BASE_URLS[environment],
1255
1271
  authVersion,
1256
- pcc: config.pcc,
1257
1272
  logger,
1258
1273
  retry: Object.freeze({ ...DEFAULT_RETRY, ...definedOnly(config.retry ?? {}) }),
1259
1274
  // Enlazado al global para evitar "Illegal invocation" en algunos runtimes.
@@ -1290,10 +1305,12 @@ var RestTransport = class {
1290
1305
  }
1291
1306
  async request(request) {
1292
1307
  if (request.retry !== void 0) validateRetryOptions(request.retry, "retry");
1293
- const capture = request.onRawResponse === void 0 ? void 0 : { attempts: 0 };
1308
+ const wantsRequest = request.onRawRequest !== void 0 && request.auth !== false;
1309
+ const capture = request.onRawResponse === void 0 && !wantsRequest ? void 0 : { attempts: 0 };
1294
1310
  try {
1295
1311
  return await this.#attempts(request, capture);
1296
1312
  } finally {
1313
+ if (wantsRequest && capture?.request !== void 0) this.#deliverRawRequest(request, capture.request, capture.attempts);
1297
1314
  if (capture?.last !== void 0) this.#deliverRaw(request, { ...capture.last, attempts: capture.attempts });
1298
1315
  }
1299
1316
  }
@@ -1347,6 +1364,19 @@ var RestTransport = class {
1347
1364
  });
1348
1365
  }
1349
1366
  }
1367
+ /** Igual que `#deliverRaw`: un callback que lanza no rompe la operación. */
1368
+ #deliverRawRequest(request, raw, attempts) {
1369
+ const operation = request.operation ?? request.service;
1370
+ try {
1371
+ request.onRawRequest?.({ operation, protocol: "rest", ...raw, attempts });
1372
+ } catch (error) {
1373
+ this.#log.emit("warn", "sabre.request.rawRequestHookFailed", {
1374
+ service: request.service,
1375
+ operation,
1376
+ errorName: error instanceof Error ? error.name : typeof error
1377
+ });
1378
+ }
1379
+ }
1350
1380
  async #authorizationFor(request) {
1351
1381
  if (request.auth === false || this.#authorization === void 0) return void 0;
1352
1382
  return this.#authorization.get();
@@ -1363,12 +1393,18 @@ var RestTransport = class {
1363
1393
  this.#log.emit("debug", "sabre.request.start", logContext);
1364
1394
  const startedAt = Date.now();
1365
1395
  let response;
1366
- if (capture !== void 0) capture.attempts++;
1396
+ const body = request.body === void 0 ? void 0 : form ? String(request.body) : JSON.stringify(request.body);
1397
+ if (capture !== void 0) {
1398
+ capture.attempts++;
1399
+ if (request.onRawRequest !== void 0 && request.auth !== false) {
1400
+ capture.request = { method: request.method, url, headers: rawRequestHeaders(headers), body };
1401
+ }
1402
+ }
1367
1403
  try {
1368
1404
  response = await this.#config.fetch(url, {
1369
1405
  method: request.method,
1370
1406
  headers,
1371
- ...request.body === void 0 ? {} : { body: form ? String(request.body) : JSON.stringify(request.body) },
1407
+ ...body === void 0 ? {} : { body },
1372
1408
  ...request.signal === void 0 ? {} : { signal: request.signal }
1373
1409
  });
1374
1410
  } catch (cause) {
@@ -1383,20 +1419,20 @@ var RestTransport = class {
1383
1419
  }
1384
1420
  const text = await response.text();
1385
1421
  if (capture !== void 0) capture.last = { status: response.status, headers: response.headers, body: text };
1386
- const body = parseBody(text);
1422
+ const parsed = parseBody(text);
1387
1423
  const endContext = { ...logContext, status: response.status, durationMs: Date.now() - startedAt };
1388
1424
  if (!response.ok) {
1389
- const error = gatewayError(request.service, response.status, body);
1425
+ const error = gatewayError(request.service, response.status, parsed);
1390
1426
  this.#log.emit("error", "sabre.request.end", { ...endContext, error });
1391
1427
  throw error;
1392
1428
  }
1393
- const serviceError = request.classify?.(body, response);
1429
+ const serviceError = request.classify?.(parsed, response);
1394
1430
  if (serviceError !== void 0) {
1395
1431
  this.#log.emit("warn", "sabre.request.end", { ...endContext, error: serviceError });
1396
1432
  throw serviceError;
1397
1433
  }
1398
1434
  this.#log.emit("info", "sabre.request.end", endContext);
1399
- return body;
1435
+ return parsed;
1400
1436
  }
1401
1437
  #url(request) {
1402
1438
  const path = request.path.startsWith("/") ? request.path : `/${request.path}`;
@@ -1499,6 +1535,7 @@ var TRANSIENT_CODES = /* @__PURE__ */ new Set([
1499
1535
  var PREVIOUS_ENTRY_ACTIVE_RETRIES = 3;
1500
1536
  var PREVIOUS_ENTRY_ACTIVE_DELAY_MS = 1e3;
1501
1537
  var PREVIOUS_ENTRY_ACTIVE = /PREVIOUS ENTRY ACTIVE/i;
1538
+ var SOAP_HEADERS = { "Content-Type": "text/xml; charset=utf-8", Accept: "text/xml" };
1502
1539
  var PARSER_OPTIONS = {
1503
1540
  ignoreAttributes: false,
1504
1541
  attributeNamePrefix: "@",
@@ -1534,6 +1571,7 @@ var SoapTransport = class {
1534
1571
  try {
1535
1572
  return await this.#attempts(request, capture);
1536
1573
  } finally {
1574
+ if (capture.request !== void 0) this.#deliverRawRequest(request, capture.request, capture.attempts);
1537
1575
  if (capture.last !== void 0) this.#deliverRaw(request, { ...capture.last, attempts: capture.attempts });
1538
1576
  }
1539
1577
  }
@@ -1580,10 +1618,18 @@ var SoapTransport = class {
1580
1618
  const startedAt = Date.now();
1581
1619
  let response;
1582
1620
  capture.attempts++;
1621
+ if (wantsRawRequest(request)) {
1622
+ capture.request = {
1623
+ method: "POST",
1624
+ url: this.#config.soapBaseUrl,
1625
+ headers: rawRequestHeaders(SOAP_HEADERS),
1626
+ body: redactSoapEnvelope(scrubToken(envelope, request.security))
1627
+ };
1628
+ }
1583
1629
  try {
1584
1630
  response = await this.#config.fetch(this.#config.soapBaseUrl, {
1585
1631
  method: "POST",
1586
- headers: { "Content-Type": "text/xml; charset=utf-8", Accept: "text/xml" },
1632
+ headers: SOAP_HEADERS,
1587
1633
  body: envelope,
1588
1634
  ...request.signal === void 0 ? {} : { signal: request.signal }
1589
1635
  });
@@ -1614,6 +1660,7 @@ var SoapTransport = class {
1614
1660
  return {
1615
1661
  ...result,
1616
1662
  messageId,
1663
+ respondedAt: respondedAtOf(text),
1617
1664
  raw: text,
1618
1665
  securityToken: request.readSecurityToken === true ? readSecurityToken(original) : void 0,
1619
1666
  status: response.status
@@ -1656,12 +1703,12 @@ var SoapTransport = class {
1656
1703
  });
1657
1704
  }
1658
1705
  const results = asNode(payload.ApplicationResults);
1659
- const warnings = asArray(results?.Warning).map((node2) => {
1660
- const entry = resultEntry(node2);
1706
+ const warnings = asArray(results?.Warning).map((node3) => {
1707
+ const entry = resultEntry(node3);
1661
1708
  return new SabreWarning(`${service}: ${entry.description}`, { service, code: entry.code, raw: text });
1662
1709
  });
1663
- const errors = asArray(results?.Error).map((node2) => {
1664
- const entry = resultEntry(node2);
1710
+ const errors = asArray(results?.Error).map((node3) => {
1711
+ const entry = resultEntry(node3);
1665
1712
  return new SabreBusinessError(`${service}: ${entry.description}`, {
1666
1713
  service,
1667
1714
  code: entry.code,
@@ -1707,6 +1754,19 @@ var SoapTransport = class {
1707
1754
  ].join("");
1708
1755
  }
1709
1756
  /** Un callback que lanza no rompe la operación (mismo criterio que REST). */
1757
+ #deliverRawRequest(request, raw, attempts) {
1758
+ const operation = request.operation ?? request.service;
1759
+ try {
1760
+ request.onRawRequest?.({ operation, protocol: "soap", ...raw, attempts });
1761
+ } catch (error) {
1762
+ this.#log.emit("warn", "sabre.request.rawRequestHookFailed", {
1763
+ service: request.service,
1764
+ operation,
1765
+ errorName: error instanceof Error ? error.name : typeof error
1766
+ });
1767
+ }
1768
+ }
1769
+ /** Un callback que lanza no rompe la operación (mismo criterio que REST). */
1710
1770
  #deliverRaw(request, raw) {
1711
1771
  if (request.onRawResponse === void 0) return;
1712
1772
  const operation = request.operation ?? request.service;
@@ -1721,6 +1781,18 @@ var SoapTransport = class {
1721
1781
  }
1722
1782
  }
1723
1783
  };
1784
+ function respondedAtOf(raw) {
1785
+ if (typeof raw !== "string") return void 0;
1786
+ const header = /<(?:[\w-]+:)?Header\b[\s\S]*?<\/(?:[\w-]+:)?Header>/.exec(raw)?.[0];
1787
+ const value = header === void 0 ? void 0 : /<(?:[\w-]+:)?Timestamp>\s*([^<\s]+)\s*</.exec(header)?.[1];
1788
+ const match = value === void 0 ? null : /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|[+-]\d{2}:\d{2})?$/.exec(value);
1789
+ if (match === null) return void 0;
1790
+ return `${match[1]}${match[2] ?? "Z"}`;
1791
+ }
1792
+ function respondedAtField(raw) {
1793
+ const respondedAt = respondedAtOf(raw);
1794
+ return respondedAt === void 0 ? {} : { respondedAt };
1795
+ }
1724
1796
  function xmlEscape(value) {
1725
1797
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
1726
1798
  }
@@ -1743,6 +1815,9 @@ function securityHeader(security) {
1743
1815
  "</wsse:Security>"
1744
1816
  ].join("");
1745
1817
  }
1818
+ function wantsRawRequest(request) {
1819
+ return request.onRawRequest !== void 0 && "binarySecurityToken" in request.security;
1820
+ }
1746
1821
  function scrubToken(text, security) {
1747
1822
  if (!("binarySecurityToken" in security) || security.binarySecurityToken === "") return text;
1748
1823
  const token = security.binarySecurityToken;
@@ -1782,8 +1857,8 @@ function faultError(service, status, fault, text) {
1782
1857
  ...code === void 0 || code === "" ? {} : { code }
1783
1858
  });
1784
1859
  }
1785
- function resultEntry(node2) {
1786
- const entry = asNode(node2) ?? {};
1860
+ function resultEntry(node3) {
1861
+ const entry = asNode(node3) ?? {};
1787
1862
  const messages = [];
1788
1863
  const shortTexts = [];
1789
1864
  let code;
@@ -1821,8 +1896,8 @@ function untrimmedPayload(raw) {
1821
1896
  const document = parseEnvelope(raw, untrimmedParser);
1822
1897
  return document === void 0 ? void 0 : asNode(firstElement(document.Body)[1]);
1823
1898
  }
1824
- function firstElement(node2) {
1825
- for (const [key, value] of Object.entries(node2)) {
1899
+ function firstElement(node3) {
1900
+ for (const [key, value] of Object.entries(node3)) {
1826
1901
  if (!key.startsWith("@") && key !== "#text") return [key, Array.isArray(value) ? value[0] : value];
1827
1902
  }
1828
1903
  return [void 0, void 0];
@@ -1837,14 +1912,14 @@ function asArray(value) {
1837
1912
  }
1838
1913
  function textOf(value) {
1839
1914
  if (typeof value === "string") return value;
1840
- const node2 = asNode(value);
1841
- const text = node2?.["#text"];
1842
- return typeof text === "string" ? text : node2 === void 0 ? void 0 : "";
1915
+ const node3 = asNode(value);
1916
+ const text = node3?.["#text"];
1917
+ return typeof text === "string" ? text : node3 === void 0 ? void 0 : "";
1843
1918
  }
1844
1919
  function stringAt(root2, ...path) {
1845
- let node2 = root2;
1846
- for (const key of path) node2 = asNode(node2)?.[key];
1847
- return nonEmpty2(textOf(node2));
1920
+ let node3 = root2;
1921
+ for (const key of path) node3 = asNode(node3)?.[key];
1922
+ return nonEmpty2(textOf(node3));
1848
1923
  }
1849
1924
  function nonEmpty2(value) {
1850
1925
  return typeof value === "string" && value !== "" ? value : void 0;
@@ -1901,27 +1976,41 @@ import { XMLParser as XMLParser2 } from "fast-xml-parser";
1901
1976
  // src/queue/queue-ref.ts
1902
1977
  var MIN_QUEUE_NUMBER = 0;
1903
1978
  var MAX_QUEUE_NUMBER = 511;
1979
+ var MESSAGE_QUEUE = "S";
1980
+ var ALPHA_QUEUE = /^[A-Z]{1,2}$/;
1981
+ function fail3(message) {
1982
+ throw new TypeError(`sabre-client: ${message}`);
1983
+ }
1904
1984
  function validateQueueRef(value, label) {
1905
1985
  if (typeof value === "number") {
1906
1986
  if (Number.isInteger(value) && value >= MIN_QUEUE_NUMBER && value <= MAX_QUEUE_NUMBER) return;
1907
- throw new TypeError(
1908
- `sabre-client: ${label} debe ser un n\xFAmero de cola entero de ${MIN_QUEUE_NUMBER} a ${MAX_QUEUE_NUMBER}, o { name }`
1909
- );
1987
+ fail3(`${label} debe ser un n\xFAmero de cola entero de ${MIN_QUEUE_NUMBER} a ${MAX_QUEUE_NUMBER}, una cola alfab\xE9tica ('S') o { name }`);
1988
+ }
1989
+ if (typeof value === "string") {
1990
+ if (ALPHA_QUEUE.test(value)) return;
1991
+ if (/^\d+$/.test(value.trim())) fail3(`${label}: una cola num\xE9rica va como n\xFAmero (${Number(value)}), no como string`);
1992
+ fail3(`${label} debe ser una cola alfab\xE9tica de una o dos letras may\xFAsculas (p. ej. 'S')`);
1910
1993
  }
1911
1994
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1912
1995
  const { name } = value;
1913
- if (typeof name === "string" && name.trim() !== "") return;
1914
- throw new TypeError(`sabre-client: ${label}.name debe ser un string no vac\xEDo`);
1996
+ if (typeof name !== "string" || name.trim() === "") fail3(`${label}.name debe ser un string no vac\xEDo`);
1997
+ if (ALPHA_QUEUE.test(name)) fail3(`${label}: '${name}' es una cola alfab\xE9tica: pasa '${name}', no { name: '${name}' }`);
1998
+ return;
1915
1999
  }
1916
- throw new TypeError(`sabre-client: ${label} debe ser un n\xFAmero de cola (0\u2013511) o { name }`);
2000
+ fail3(`${label} debe ser un n\xFAmero de cola (0\u2013511), una cola alfab\xE9tica ('S') o { name }`);
1917
2001
  }
1918
2002
  function queueRefAttribute(queue) {
1919
- return typeof queue === "number" ? `Number="${queue}"` : `Name="${xmlEscape(queue.name)}"`;
2003
+ if (typeof queue === "number") return `Number="${queue}"`;
2004
+ if (typeof queue === "string") return `Number="${xmlEscape(queue)}"`;
2005
+ return `Name="${xmlEscape(queue.name)}"`;
2006
+ }
2007
+ function isMessageQueue(queue) {
2008
+ return queue === MESSAGE_QUEUE;
1920
2009
  }
1921
2010
  function toQueueRef(value) {
1922
2011
  const text = value?.trim();
1923
2012
  if (text === void 0 || text === "") return void 0;
1924
- return /^\d+$/.test(text) ? Number(text) : { name: text };
2013
+ return /^\d+$/.test(text) ? Number(text) : text;
1925
2014
  }
1926
2015
 
1927
2016
  // src/queue/queue-access.ts
@@ -1939,9 +2028,11 @@ function root(body) {
1939
2028
  function identifier(queue, pcc) {
1940
2029
  return `<QueueIdentifier ${queueRefAttribute(queue)} PseudoCityCode="${xmlEscape(pcc)}"/>`;
1941
2030
  }
2031
+ function selectionXml(selection) {
2032
+ return selection.map((filter) => `<Selection Function="${xmlEscape(filter.function)}" Criteria="${xmlEscape(filter.criteria)}"/>`).join("");
2033
+ }
1942
2034
  function queueAccessPayload(queue, pcc, selection = []) {
1943
- const filters = selection.map((filter) => `<Selection Function="${xmlEscape(filter.function)}" Criteria="${xmlEscape(filter.criteria)}"/>`).join("");
1944
- return root(`${identifier(queue, pcc)}${filters}`);
2035
+ return root(`${identifier(queue, pcc)}${selectionXml(selection)}`);
1945
2036
  }
1946
2037
  function queueNavigationPayload(action, from = []) {
1947
2038
  return root(`<Navigation Action="${action}"/>${from.map((target) => identifier(target.queue, target.pcc)).join("")}`);
@@ -2007,17 +2098,21 @@ function queueEndError(error, service) {
2007
2098
  function isOffQueueError(error) {
2008
2099
  return error instanceof SabreBusinessError && [error, ...error.errors].some((result) => resultHasText(result, OFF_QUEUE));
2009
2100
  }
2010
- function endStep(warnings, reason = "end-of-display") {
2011
- return { done: true, reason, message: reason === "empty" ? QUEUE_EMPTY : END_OF_DISPLAY, warnings };
2101
+ function endStep(warnings, reason = "end-of-display", raw) {
2102
+ return { done: true, reason, message: reason === "empty" ? QUEUE_EMPTY : END_OF_DISPLAY, warnings, ...respondedAtField(raw) };
2012
2103
  }
2013
- function parseQueueStep(response) {
2104
+ function parseQueueStep(response, { messages = false } = {}) {
2014
2105
  const { warnings } = response;
2015
2106
  const paragraph = paragraphLines(response.raw);
2016
2107
  const firstLine = paragraph.find((line) => line.trim() !== "");
2017
- if (isEndOfDisplay(firstLine) || warnings.some(isEndOfDisplayResult)) return endStep(warnings);
2018
- const fields = lineFields(response.payload.Line);
2108
+ if (isEndOfDisplay(firstLine) || warnings.some(isEndOfDisplayResult)) return endStep(warnings, "end-of-display", response.raw);
2109
+ if (messages && response.payload.Line === void 0 && firstLine !== void 0) {
2110
+ return { done: false, kind: "message", message: { text: paragraph }, warnings, ...respondedAtField(response.raw) };
2111
+ }
2112
+ const fields = messages ? void 0 : lineFields(response.payload.Line);
2019
2113
  if (fields === void 0) {
2020
- throw new SabreGatewayError(`${QUEUE_ACCESS_SERVICE}: respuesta no reconocida (sin Line/UniqueID ni fin de cola)`, {
2114
+ const expected = messages ? "sin Paragraph de mensaje ni fin de cola en la cola S" : "sin Line/UniqueID ni fin de cola";
2115
+ throw new SabreGatewayError(`${QUEUE_ACCESS_SERVICE}: respuesta no reconocida (${expected})`, {
2021
2116
  protocol: "soap",
2022
2117
  status: response.status,
2023
2118
  retryable: false,
@@ -2025,7 +2120,7 @@ function parseQueueStep(response) {
2025
2120
  });
2026
2121
  }
2027
2122
  const item = { ...fields, paragraph };
2028
- return { done: false, item, warnings };
2123
+ return { done: false, kind: "pnr", item, warnings, ...respondedAtField(response.raw) };
2029
2124
  }
2030
2125
  function lineFields(value) {
2031
2126
  const line = node(value);
@@ -2072,8 +2167,8 @@ function parseHostCount(value) {
2072
2167
  const match = /^[.\s]*(\d+)\s*$/.exec(value ?? "");
2073
2168
  return match === null ? void 0 : Number(match[1]);
2074
2169
  }
2075
- function attribute2(node2, name) {
2076
- const value = node2?.[`@${name}`];
2170
+ function attribute2(node3, name) {
2171
+ const value = node3?.[`@${name}`];
2077
2172
  return typeof value === "string" ? value : void 0;
2078
2173
  }
2079
2174
  function parseQueueCountResponse(response) {
@@ -2083,7 +2178,8 @@ function parseQueueCountResponse(response) {
2083
2178
  countedAt ??= nonEmpty3(attribute2(info, "DateTime"));
2084
2179
  for (const identifier2 of asArray(info.QueueIdentifier)) {
2085
2180
  const number = attribute2(identifier2, "Number");
2086
- const queue = toQueueRef(number);
2181
+ const name = nonEmpty3(attribute2(identifier2, "Name"));
2182
+ const queue = number === void 0 && name !== void 0 ? { name } : toQueueRef(number);
2087
2183
  const count = parseHostCount(attribute2(identifier2, "Count"));
2088
2184
  if (queue === void 0 || count === void 0) {
2089
2185
  throw malformed(`QueueIdentifier con Number="${number ?? ""}" y Count="${attribute2(identifier2, "Count") ?? ""}"`, response);
@@ -2099,7 +2195,13 @@ function parseQueueCountResponse(response) {
2099
2195
  if (count === void 0) throw malformed(`Totals ${key} con Count="${attribute2(total, "Count") ?? ""}"`, response);
2100
2196
  totals[key] = count;
2101
2197
  }
2102
- return { queues, totals, ...countedAt === void 0 ? {} : { countedAt }, warnings: response.warnings };
2198
+ return {
2199
+ queues,
2200
+ totals,
2201
+ ...countedAt === void 0 ? {} : { countedAt },
2202
+ warnings: response.warnings,
2203
+ ...respondedAtField(response.raw)
2204
+ };
2103
2205
  }
2104
2206
  function malformed(detail, response) {
2105
2207
  return new SabreGatewayError(`${QUEUE_COUNT_SERVICE}: respuesta no reconocida (${detail})`, {
@@ -2116,9 +2218,10 @@ function nonEmpty3(value) {
2116
2218
 
2117
2219
  // src/queue/queue-list.ts
2118
2220
  var NAMESPACE3 = "http://webservices.sabre.com/sabreXML/2011/10";
2119
- function queueListPayload(queue, pcc, primaryPassenger) {
2221
+ var LIST_SELECTION_FUNCTIONS = ["QD", "AK"];
2222
+ function queueListPayload(queue, pcc, primaryPassenger, selection = []) {
2120
2223
  const list = primaryPassenger ? '<List Ind="true" PrimaryPassenger="true"/>' : '<List Ind="true"/>';
2121
- return `<QueueAccessRQ Version="2.1.1" xmlns="${NAMESPACE3}"><QueueIdentifier ${queueRefAttribute(queue)} PseudoCityCode="${xmlEscape(pcc)}">${list}</QueueIdentifier></QueueAccessRQ>`;
2224
+ return `<QueueAccessRQ Version="2.1.1" xmlns="${NAMESPACE3}"><QueueIdentifier ${queueRefAttribute(queue)} PseudoCityCode="${xmlEscape(pcc)}">${list}</QueueIdentifier>${selectionXml(selection)}</QueueAccessRQ>`;
2122
2225
  }
2123
2226
  function queueListRequest(payload, options) {
2124
2227
  return {
@@ -2130,14 +2233,14 @@ function queueListRequest(payload, options) {
2130
2233
  ...options
2131
2234
  };
2132
2235
  }
2133
- function emptyList(warnings, paragraph = []) {
2134
- return { locators: [], entries: [], paragraph, warnings };
2236
+ function emptyList(warnings, paragraph = [], raw) {
2237
+ return { locators: [], entries: [], paragraph, warnings, ...respondedAtField(raw) };
2135
2238
  }
2136
2239
  function parseQueueList(response) {
2137
2240
  const { warnings } = response;
2138
2241
  const paragraph = paragraphLines(response.raw);
2139
2242
  const firstLine = paragraph.find((line) => line.trim() !== "");
2140
- if (isEndOfDisplay(firstLine) || warnings.some(isEndOfDisplayResult)) return emptyList(warnings, paragraph);
2243
+ if (isEndOfDisplay(firstLine) || warnings.some(isEndOfDisplayResult)) return emptyList(warnings, paragraph, response.raw);
2141
2244
  const entries = asArray(response.payload.Line).map((line) => {
2142
2245
  const fields = lineFields(line);
2143
2246
  if (fields === void 0) {
@@ -2152,12 +2255,88 @@ function parseQueueList(response) {
2152
2255
  const position = number !== void 0 && /^\d+$/.test(number) ? Number(number) : void 0;
2153
2256
  return { ...position === void 0 ? {} : { position }, ...fields };
2154
2257
  });
2155
- return { locators: entries.map((entry) => entry.locator), entries, paragraph, warnings };
2258
+ return { locators: entries.map((entry) => entry.locator), entries, paragraph, warnings, ...respondedAtField(response.raw) };
2259
+ }
2260
+
2261
+ // src/queue/queue-activity.ts
2262
+ var QUEUE_ACTIVITY_SERVICE = "queueAnalysis";
2263
+ var NAMESPACE4 = "http://webservices.sabre.com/sabreXML/2011/10";
2264
+ var COUNTERS = [
2265
+ ["NumMessages", "numMessages"],
2266
+ ["Ended", "ended"],
2267
+ ["Ignored", "ignored"],
2268
+ ["Moved", "moved"],
2269
+ ["Removed", "removed"],
2270
+ ["Transferred", "transferred"],
2271
+ ["LeftMessageToContact", "leftMessageToContact"],
2272
+ ["UnableToReach", "unableToReach"]
2273
+ ];
2274
+ function queueActivityPayload(pcc) {
2275
+ return `<QueueAnalysisRQ Version="2.0.0" xmlns="${NAMESPACE4}"><QueueInfo><QueueIdentifier PseudoCityCode="${xmlEscape(pcc)}"/></QueueInfo></QueueAnalysisRQ>`;
2276
+ }
2277
+ function queueActivityRequest(pcc, options) {
2278
+ return {
2279
+ service: QUEUE_ACTIVITY_SERVICE,
2280
+ action: "QueueAnalysisLLSRQ",
2281
+ payload: queueActivityPayload(pcc),
2282
+ idempotent: true,
2283
+ operation: "queue.activity",
2284
+ ...options
2285
+ };
2286
+ }
2287
+ function attribute3(node3, name) {
2288
+ const value = (Array.isArray(node3) ? node3[0] : node3)?.[`@${name}`];
2289
+ return typeof value === "string" ? value : void 0;
2290
+ }
2291
+ function nonEmpty4(value) {
2292
+ const text = value?.trim();
2293
+ return text === void 0 || text === "" ? void 0 : text;
2294
+ }
2295
+ function parseQueueActivityResponse(response) {
2296
+ const queues = [];
2297
+ let analyzedAt;
2298
+ for (const info of asArray(response.payload.QueueInfo)) {
2299
+ analyzedAt ??= nonEmpty4(attribute3(info, "DateTime"));
2300
+ for (const identifier2 of asArray(info.QueueIdentifier)) {
2301
+ const number = attribute3(identifier2, "Number");
2302
+ const name = nonEmpty4(attribute3(identifier2, "Name"));
2303
+ const queue = number === void 0 && name !== void 0 ? { name } : toQueueRef(number);
2304
+ if (queue === void 0) throw malformed2(`QueueIdentifier con Number="${number ?? ""}"`, response);
2305
+ queues.push({ queue, ...counters(identifier2, `la cola ${number ?? name}`, response) });
2306
+ }
2307
+ }
2308
+ const totalsNode = asArray(response.payload.Totals)[0];
2309
+ const totals = totalsNode === void 0 ? void 0 : counters(totalsNode, "Totals", response);
2310
+ return {
2311
+ queues,
2312
+ ...totals === void 0 ? {} : { totals },
2313
+ ...analyzedAt === void 0 ? {} : { analyzedAt },
2314
+ warnings: response.warnings,
2315
+ ...respondedAtField(response.raw)
2316
+ };
2317
+ }
2318
+ function counters(node3, label, response) {
2319
+ const result = {};
2320
+ for (const [element2, key] of COUNTERS) {
2321
+ const raw = attribute3(node3[element2], "Count");
2322
+ const count = parseHostCount(raw);
2323
+ if (count === void 0) throw malformed2(`${label} con ${element2} Count="${raw ?? ""}"`, response);
2324
+ result[key] = count;
2325
+ }
2326
+ return result;
2327
+ }
2328
+ function malformed2(detail, response) {
2329
+ return new SabreGatewayError(`${QUEUE_ACTIVITY_SERVICE}: respuesta no reconocida (${detail})`, {
2330
+ protocol: "soap",
2331
+ status: response.status,
2332
+ retryable: false,
2333
+ raw: response.raw
2334
+ });
2156
2335
  }
2157
2336
 
2158
2337
  // src/queue/queue-place.ts
2159
2338
  var QUEUE_PLACE_SERVICE = "queuePlace";
2160
- var NAMESPACE4 = "http://webservices.sabre.com/sabreXML/2011/10";
2339
+ var NAMESPACE5 = "http://webservices.sabre.com/sabreXML/2011/10";
2161
2340
  var MAX_PLACE_TO = 10;
2162
2341
  var MAX_PLACE_LOCATORS = 6;
2163
2342
  var INSTRUCTION_CODE_RANGES = [
@@ -2173,7 +2352,7 @@ function queuePlacePayload(to, locators = []) {
2173
2352
  return `<QueueIdentifier ${queueRefAttribute(target.queue)}${pic} PseudoCityCode="${xmlEscape(target.pcc)}"/>`;
2174
2353
  }).join("");
2175
2354
  const ids = locators.map((locator) => `<UniqueID ID="${xmlEscape(locator)}"/>`).join("");
2176
- return `<QueuePlaceRQ Version="2.0.4" xmlns="${NAMESPACE4}"><QueueInfo>${identifiers}${ids}</QueueInfo></QueuePlaceRQ>`;
2355
+ return `<QueuePlaceRQ Version="2.0.4" xmlns="${NAMESPACE5}"><QueueInfo>${identifiers}${ids}</QueueInfo></QueuePlaceRQ>`;
2177
2356
  }
2178
2357
  function queuePlaceRequest(payload, options) {
2179
2358
  return {
@@ -2198,7 +2377,7 @@ function isEnd(response, text) {
2198
2377
  }
2199
2378
  function parsePlaceStep(response) {
2200
2379
  const text = textLines(response.raw, "QueuePlaceRS");
2201
- if (isEnd(response, text)) return endStep(response.warnings);
2380
+ if (isEnd(response, text)) return endStep(response.warnings, "end-of-display", response.raw);
2202
2381
  const locator = signatureLocator(text);
2203
2382
  if (locator === void 0) {
2204
2383
  throw new SabreGatewayError(`${QUEUE_PLACE_SERVICE}: respuesta no reconocida (sin l\xEDnea de firma del PNR ni fin de cola)`, {
@@ -2208,7 +2387,7 @@ function parsePlaceStep(response) {
2208
2387
  raw: response.raw
2209
2388
  });
2210
2389
  }
2211
- return { done: false, item: { locator, paragraph: text }, warnings: response.warnings };
2390
+ return { done: false, kind: "pnr", item: { locator, paragraph: text }, warnings: response.warnings, ...respondedAtField(response.raw) };
2212
2391
  }
2213
2392
  function parsePlaceUnknown(response) {
2214
2393
  const text = textLines(response.raw, "QueuePlaceRS");
@@ -2231,11 +2410,16 @@ function parsePlaceResult(response) {
2231
2410
  ...placedAt === void 0 ? {} : { placedAt }
2232
2411
  };
2233
2412
  });
2234
- return { placements, text: textLines(response.raw, "QueuePlaceRS"), warnings: response.warnings };
2413
+ return {
2414
+ placements,
2415
+ text: textLines(response.raw, "QueuePlaceRS"),
2416
+ warnings: response.warnings,
2417
+ ...respondedAtField(response.raw)
2418
+ };
2235
2419
  }
2236
2420
 
2237
2421
  // src/queue/queue-management.ts
2238
- function fail3(message) {
2422
+ function fail4(message) {
2239
2423
  throw new TypeError(`sabre-client: ${message}`);
2240
2424
  }
2241
2425
  function rejectedBySabre(error) {
@@ -2244,6 +2428,8 @@ function rejectedBySabre(error) {
2244
2428
  var QueueManagement = class {
2245
2429
  #context;
2246
2430
  #state = "outside";
2431
+ /** Si la última cola a la que se entró es la `S`: ahí cada paso es un mensaje, no un PNR. */
2432
+ #messages = false;
2247
2433
  /** @internal Lo construye `SabreSession`. */
2248
2434
  constructor(context) {
2249
2435
  this.#context = context;
@@ -2269,31 +2455,61 @@ var QueueManagement = class {
2269
2455
  const response = await this.#context.send(queueCountRequest(pcc, options));
2270
2456
  return parseQueueCountResponse(response);
2271
2457
  }
2458
+ /**
2459
+ * Get Queue Activity (`QueueAnalysisLLSRQ` 2.0.0, el `QA` del host): por cada cola del PCC, los
2460
+ * ocho contadores de actividad (`numMessages`, `ended`, `ignored`, `moved`, `removed`,
2461
+ * `transferred`, `leftMessageToContact` y `unableToReach`), más los totales. Cada `queue` se puede
2462
+ * pasar tal cual a las demás operaciones de cola. Ninguna fuente dice desde cuándo cuentan
2463
+ * (Q-097): el SDK los devuelve sin interpretar.
2464
+ *
2465
+ * Se puede llamar dentro o fuera de una cola, y no cambia `state`. Es una lectura: se reintenta
2466
+ * ante fallos transitorios. No pide duty code 9 (CERT y producción, Q-098). Un PCC que no tiene 3
2467
+ * o 4 caracteres alfanuméricos lanza `TypeError` sin red.
2468
+ */
2469
+ async activity(request = {}, options = {}) {
2470
+ if (typeof request !== "object" || request === null) fail4("queue.activity recibe { pcc? }");
2471
+ const pcc = this.#pcc(request.pcc, "pcc");
2472
+ const response = await this.#context.send(queueActivityRequest(pcc, options));
2473
+ return parseQueueActivityResponse(response);
2474
+ }
2272
2475
  /**
2273
2476
  * La lista de localizadores de una cola (`QueueAccessLLSRQ` con `List`), sin recorrerla. Con
2274
2477
  * `primaryPassenger: true`, cada entrada trae también el pasajero principal. Una cola vacía da
2275
2478
  * una lista vacía.
2276
2479
  *
2480
+ * No pagina: Sabre devuelve la cola entera en una respuesta, también con más de 1000 PNR. Con
2481
+ * `selection`, solo los PNR que cumplen el filtro; Sabre acepta solo `QD` o `AK` en la lista.
2482
+ *
2277
2483
  * No cambia `state`. Es una lectura: se reintenta ante fallos transitorios. Lanza `TypeError`
2278
- * sin red si `queue` o `pcc` no son válidos, o si `primaryPassenger` no es un boolean.
2484
+ * sin red si `queue` o `pcc` no son válidos, si `primaryPassenger` no es un boolean, o si
2485
+ * `selection` no es válida o trae un código que no es `QD` ni `AK`.
2279
2486
  */
2280
2487
  async list(request, options = {}) {
2281
- if (typeof request !== "object" || request === null) fail3("queue.list recibe { queue, pcc?, primaryPassenger? }");
2488
+ if (typeof request !== "object" || request === null) fail4("queue.list recibe { queue, pcc?, primaryPassenger?, selection? }");
2282
2489
  validateQueueRef(request.queue, "queue");
2490
+ if (isMessageQueue(request.queue)) fail4("la cola 'S' es la de mensajes del PCC: no tiene PNR y Sabre no la lista");
2283
2491
  const pcc = this.#pcc(request.pcc, "pcc");
2284
2492
  const { primaryPassenger = false } = request;
2285
- if (typeof primaryPassenger !== "boolean") fail3("primaryPassenger debe ser un boolean");
2286
- const send2 = queueListRequest(queueListPayload(request.queue, pcc, primaryPassenger), options);
2493
+ if (typeof primaryPassenger !== "boolean") fail4("primaryPassenger debe ser un boolean");
2494
+ const selection = validateSelection(request.selection);
2495
+ selection.forEach((filter, index) => {
2496
+ if (!LIST_SELECTION_FUNCTIONS.includes(filter.function)) {
2497
+ fail4(`selection[${index}].function en list solo puede ser ${LIST_SELECTION_FUNCTIONS.join(" o ")}: Sabre no acepta otro c\xF3digo en la lista`);
2498
+ }
2499
+ });
2500
+ const send2 = queueListRequest(queueListPayload(request.queue, pcc, primaryPassenger, selection), options);
2287
2501
  try {
2288
2502
  return parseQueueList(await this.#context.send(send2));
2289
2503
  } catch (error) {
2290
2504
  const end = queueEndError(error, send2.service);
2291
- if (end !== void 0) return emptyList([...end.error.warnings]);
2505
+ if (end !== void 0) return emptyList([...end.error.warnings], [], end.error.raw);
2292
2506
  throw error;
2293
2507
  }
2294
2508
  }
2295
2509
  /**
2296
- * Access Queue (`QueueAccessLLSRQ` 2.1.1): entra a una cola y devuelve su primer PNR. Una cola
2510
+ * Access Queue (`QueueAccessLLSRQ` 2.1.1): entra a una cola y devuelve su primer PNR
2511
+ * (`kind: 'pnr'`). En la cola `S`, la de mensajes, devuelve el primer mensaje
2512
+ * (`kind: 'message'`), y `ignore` pasa al siguiente sin borrarlo (CERT, 2026-11-06). Una cola
2297
2513
  * vacía, o sin PNR que cumplan `selection`, devuelve `{ done: true }` y la sesión sigue afuera.
2298
2514
  *
2299
2515
  * Lanza `TypeError` sin red si la sesión ya está dentro de una cola (o en `'unknown'`: hay que
@@ -2302,12 +2518,13 @@ var QueueManagement = class {
2302
2518
  * `'unknown'`.
2303
2519
  */
2304
2520
  async access(request, options = {}) {
2305
- if (this.#state === "inside") fail3("la sesi\xF3n ya est\xE1 dentro de una cola: sal con queue.exit() antes de entrar a otra");
2306
- if (this.#state === "unknown") fail3("no se sabe si la sesi\xF3n sigue dentro de una cola: llama a queue.exit() antes de entrar");
2307
- if (typeof request !== "object" || request === null) fail3("queue.access recibe { queue, pcc?, selection? }");
2521
+ if (this.#state === "inside") fail4("la sesi\xF3n ya est\xE1 dentro de una cola: sal con queue.exit() antes de entrar a otra");
2522
+ if (this.#state === "unknown") fail4("no se sabe si la sesi\xF3n sigue dentro de una cola: llama a queue.exit() antes de entrar");
2523
+ if (typeof request !== "object" || request === null) fail4("queue.access recibe { queue, pcc?, selection? }");
2308
2524
  validateQueueRef(request.queue, "queue");
2309
2525
  const pcc = this.#pcc(request.pcc, "pcc");
2310
2526
  const selection = validateSelection(request.selection);
2527
+ this.#messages = isMessageQueue(request.queue);
2311
2528
  return this.#step(queueAccessRequest(queueAccessPayload(request.queue, pcc, selection), "queue.access", options));
2312
2529
  }
2313
2530
  /**
@@ -2320,7 +2537,8 @@ var QueueManagement = class {
2320
2537
  }
2321
2538
  /**
2322
2539
  * Saca el PNR actual de la cola y pasa al siguiente (`QR`). Con `from` (de 1 a 3 colas), lo saca
2323
- * de esas colas a la vez. Fuera de una cola lanza `TypeError` sin red.
2540
+ * de esas colas a la vez. Fuera de una cola lanza `TypeError` sin red. En la cola `S` manda `QR`
2541
+ * igual, pero no hay evidencia de qué hace Sabre con el mensaje.
2324
2542
  */
2325
2543
  async remove(request = {}, options = {}) {
2326
2544
  this.#requireQueue("remove");
@@ -2367,10 +2585,11 @@ var QueueManagement = class {
2367
2585
  * `options` se aplica a cada llamada a Sabre del recorrido.
2368
2586
  */
2369
2587
  async process(request, callback, options = {}) {
2370
- if (typeof request !== "object" || request === null) fail3("queue.process recibe { queue, pcc?, selection?, limit? }");
2371
- if (typeof callback !== "function") fail3("queue.process necesita un callback");
2588
+ if (typeof request !== "object" || request === null) fail4("queue.process recibe { queue, pcc?, selection?, limit? }");
2589
+ if (typeof callback !== "function") fail4("queue.process necesita un callback");
2372
2590
  const { limit, ...access } = request;
2373
- if (limit !== void 0 && !(Number.isInteger(limit) && limit >= 1)) fail3("limit debe ser un entero mayor o igual a 1");
2591
+ if (isMessageQueue(access.queue)) fail4("queue.process no recorre la cola 'S', la de mensajes: usa queue.access() e queue.ignore()");
2592
+ if (limit !== void 0 && !(Number.isInteger(limit) && limit >= 1)) fail4("limit debe ser un entero mayor o igual a 1");
2374
2593
  const startedOutside = this.#state === "outside";
2375
2594
  let visited = 0;
2376
2595
  try {
@@ -2380,6 +2599,7 @@ var QueueManagement = class {
2380
2599
  await this.exit(options);
2381
2600
  return { visited, ended: "limit" };
2382
2601
  }
2602
+ if (step.kind !== "pnr") throw new TypeError("sabre-client: queue.process recibi\xF3 un mensaje en vez de un PNR");
2383
2603
  const action = await callback(step.item);
2384
2604
  visited++;
2385
2605
  step = await this.#processAction(action, options);
@@ -2411,17 +2631,20 @@ var QueueManagement = class {
2411
2631
  }
2412
2632
  }
2413
2633
  async place(request, options = {}) {
2414
- if (typeof request !== "object" || request === null) fail3("queue.place recibe { to, locators? }");
2634
+ if (typeof request !== "object" || request === null) fail4("queue.place recibe { to, locators? }");
2415
2635
  const to = this.#placeTo(request.to);
2416
2636
  const locators = validateLocators(request.locators);
2637
+ if (this.#messages && this.#state !== "outside") {
2638
+ fail4("queue.place no se puede dentro de la cola 'S': lo que hay en pantalla es un mensaje, no un PNR");
2639
+ }
2417
2640
  if (locators.length > 0 && this.#state === "inside") {
2418
- fail3("queue.place con locators no se puede dentro de una cola, donde coloca el PNR actual: sal con queue.exit() antes");
2641
+ fail4("queue.place con locators no se puede dentro de una cola, donde coloca el PNR actual: sal con queue.exit() antes");
2419
2642
  }
2420
2643
  if (locators.length > 0 && this.#state === "unknown") {
2421
- fail3("no se sabe si la sesi\xF3n sigue dentro de una cola: llama a queue.exit() antes de colocar por localizador");
2644
+ fail4("no se sabe si la sesi\xF3n sigue dentro de una cola: llama a queue.exit() antes de colocar por localizador");
2422
2645
  }
2423
2646
  if (to.some((target) => target.instructionCode === void 0)) {
2424
- fail3("queue.place necesita instructionCode en cada destino: Sabre lo exige, dentro y fuera de una cola");
2647
+ fail4("queue.place necesita instructionCode en cada destino: Sabre lo exige, dentro y fuera de una cola");
2425
2648
  }
2426
2649
  const send2 = queuePlaceRequest(queuePlacePayload(to, locators), options);
2427
2650
  if (this.#state === "outside") return parsePlaceResult(await this.#context.send(send2));
@@ -2429,7 +2652,8 @@ var QueueManagement = class {
2429
2652
  }
2430
2653
  /** Manda una operación que avanza y actualiza `state` con el resultado. */
2431
2654
  #step(request) {
2432
- return this.#advance(request, parseQueueStep);
2655
+ const messages = this.#messages;
2656
+ return this.#advance(request, (response) => parseQueueStep(response, { messages }));
2433
2657
  }
2434
2658
  /**
2435
2659
  * Manda una operación que puede mover el cursor. Un `QueueStep` fija el estado: `'outside'` con
@@ -2443,7 +2667,7 @@ var QueueManagement = class {
2443
2667
  const end = queueEndError(error, request.service);
2444
2668
  if (end !== void 0) {
2445
2669
  this.#state = "outside";
2446
- return endStep([...end.error.warnings], end.reason);
2670
+ return endStep([...end.error.warnings], end.reason, end.error.raw);
2447
2671
  }
2448
2672
  this.#onError(error);
2449
2673
  throw error;
@@ -2464,14 +2688,14 @@ var QueueManagement = class {
2464
2688
  }
2465
2689
  #placeTo(to) {
2466
2690
  if (!Array.isArray(to) || to.length === 0 || to.length > MAX_PLACE_TO) {
2467
- fail3(`to debe ser una lista de 1 a ${MAX_PLACE_TO} colas`);
2691
+ fail4(`to debe ser una lista de 1 a ${MAX_PLACE_TO} colas`);
2468
2692
  }
2469
2693
  return to.map((target, index) => {
2470
- if (typeof target !== "object" || target === null) fail3(`to[${index}] debe ser { queue, pcc?, instructionCode }`);
2694
+ if (typeof target !== "object" || target === null) fail4(`to[${index}] debe ser { queue, pcc?, instructionCode }`);
2471
2695
  const { queue, pcc, instructionCode } = target;
2472
2696
  validateQueueRef(queue, `to[${index}].queue`);
2473
2697
  if (instructionCode !== void 0 && !isInstructionCode(instructionCode)) {
2474
- fail3(`to[${index}].instructionCode debe ser un entero de 0 a 191 o de 200 a 254`);
2698
+ fail4(`to[${index}].instructionCode debe ser un entero de 0 a 191 o de 200 a 254`);
2475
2699
  }
2476
2700
  return {
2477
2701
  queue,
@@ -2481,20 +2705,20 @@ var QueueManagement = class {
2481
2705
  });
2482
2706
  }
2483
2707
  #requireQueue(operation) {
2484
- if (this.#state === "outside") fail3(`queue.${operation} necesita estar dentro de una cola: entra con queue.access()`);
2708
+ if (this.#state === "outside") fail4(`queue.${operation} necesita estar dentro de una cola: entra con queue.access()`);
2485
2709
  }
2486
2710
  #pcc(value, label) {
2487
2711
  if (value === void 0) return this.#context.pcc;
2488
- if (!isPseudoCityCode(value)) fail3(`${label} debe tener 3 o 4 caracteres alfanum\xE9ricos`);
2712
+ if (!isPseudoCityCode(value)) fail4(`${label} debe tener 3 o 4 caracteres alfanum\xE9ricos`);
2489
2713
  return value;
2490
2714
  }
2491
2715
  #removeFrom(from) {
2492
2716
  if (from === void 0) return [];
2493
2717
  if (!Array.isArray(from) || from.length === 0 || from.length > MAX_REMOVE_FROM) {
2494
- fail3(`from debe ser una lista de 1 a ${MAX_REMOVE_FROM} colas`);
2718
+ fail4(`from debe ser una lista de 1 a ${MAX_REMOVE_FROM} colas`);
2495
2719
  }
2496
2720
  return from.map((target, index) => {
2497
- if (typeof target !== "object" || target === null) fail3(`from[${index}] debe ser { queue, pcc? }`);
2721
+ if (typeof target !== "object" || target === null) fail4(`from[${index}] debe ser { queue, pcc? }`);
2498
2722
  const { queue, pcc } = target;
2499
2723
  validateQueueRef(queue, `from[${index}].queue`);
2500
2724
  return { queue, pcc: this.#pcc(pcc, `from[${index}].pcc`) };
@@ -2504,37 +2728,171 @@ var QueueManagement = class {
2504
2728
  function validateLocators(locators) {
2505
2729
  if (locators === void 0) return [];
2506
2730
  if (!Array.isArray(locators) || locators.length === 0 || locators.length > MAX_PLACE_LOCATORS) {
2507
- fail3(`locators debe ser una lista de 1 a ${MAX_PLACE_LOCATORS} localizadores`);
2731
+ fail4(`locators debe ser una lista de 1 a ${MAX_PLACE_LOCATORS} localizadores`);
2508
2732
  }
2509
2733
  return locators.map((locator, index) => {
2510
- if (typeof locator !== "string" || locator.trim() === "") fail3(`locators[${index}] debe ser un string no vac\xEDo`);
2734
+ if (typeof locator !== "string" || locator.trim() === "") fail4(`locators[${index}] debe ser un string no vac\xEDo`);
2511
2735
  return locator;
2512
2736
  });
2513
2737
  }
2514
2738
  function validateSelection(selection) {
2515
2739
  if (selection === void 0) return [];
2516
- if (!Array.isArray(selection)) fail3("selection debe ser una lista de { function, criteria }");
2517
- if (selection.length > MAX_SELECTIONS) fail3(`selection admite hasta ${MAX_SELECTIONS} filtros`);
2740
+ if (!Array.isArray(selection)) fail4("selection debe ser una lista de { function, criteria }");
2741
+ if (selection.length > MAX_SELECTIONS) fail4(`selection admite hasta ${MAX_SELECTIONS} filtros`);
2518
2742
  return selection.map((filter, index) => {
2519
2743
  const { function: fn, criteria } = typeof filter === "object" && filter !== null ? filter : {};
2520
- if (typeof fn !== "string" || fn.trim() === "") fail3(`selection[${index}].function debe ser un string no vac\xEDo`);
2744
+ if (typeof fn !== "string" || fn.trim() === "") fail4(`selection[${index}].function debe ser un string no vac\xEDo`);
2521
2745
  if (typeof criteria !== "string" || criteria.trim() === "") {
2522
- fail3(`selection[${index}].criteria debe ser un string no vac\xEDo`);
2746
+ fail4(`selection[${index}].criteria debe ser un string no vac\xEDo`);
2523
2747
  }
2524
2748
  return { function: fn, criteria };
2525
2749
  });
2526
2750
  }
2527
2751
 
2752
+ // src/reservation/reservation-history.ts
2753
+ var RESERVATION_HISTORY_SERVICE = "itineraryHistory";
2754
+ var NAMESPACE6 = "http://webservices.sabre.com/sabreXML/2011/10";
2755
+ var LOCATOR = /^[A-Z0-9]{6}$/;
2756
+ function isRecordLocator(value) {
2757
+ return typeof value === "string" && LOCATOR.test(value);
2758
+ }
2759
+ function reservationHistoryPayload(locator) {
2760
+ return `<TravelItineraryHistoryRQ Version="2.4.0" xmlns="${NAMESPACE6}"><UniqueID ID="${xmlEscape(locator)}"/></TravelItineraryHistoryRQ>`;
2761
+ }
2762
+ function reservationHistoryRequest(locator, options) {
2763
+ return {
2764
+ service: RESERVATION_HISTORY_SERVICE,
2765
+ action: "TravelItineraryHistoryLLSRQ",
2766
+ payload: reservationHistoryPayload(locator),
2767
+ idempotent: true,
2768
+ operation: "reservation.history",
2769
+ ...options
2770
+ };
2771
+ }
2772
+ function node2(value) {
2773
+ const first = Array.isArray(value) ? value[0] : value;
2774
+ return typeof first === "object" && first !== null ? first : void 0;
2775
+ }
2776
+ function attribute4(value, name) {
2777
+ const text = node2(value)?.[`@${name}`];
2778
+ if (typeof text !== "string") return void 0;
2779
+ const trimmed = text.trim();
2780
+ return trimmed === "" ? void 0 : trimmed;
2781
+ }
2782
+ function defined(fields) {
2783
+ return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0));
2784
+ }
2785
+ function integer(value) {
2786
+ return value !== void 0 && /^\d+$/.test(value) ? Number(value) : void 0;
2787
+ }
2788
+ function parseReservationHistoryResponse(response) {
2789
+ const transactions = asArray(response.payload.HistoricalInfo).map((info) => transaction(node2(info) ?? {}, response));
2790
+ return { transactions, warnings: response.warnings, ...respondedAtField(response.raw) };
2791
+ }
2792
+ function transaction(info, response) {
2793
+ const source = node2(node2(info.ItineraryRef)?.Source);
2794
+ const flightSegments = asArray(node2(node2(info.ItineraryInfo)?.ReservationItems)?.Item).flatMap(
2795
+ (item) => asArray(node2(item)?.FlightSegment).map((segment) => flightSegment(node2(segment) ?? {}, response))
2796
+ );
2797
+ return {
2798
+ ...defined({
2799
+ rph: integer(attribute4(info, "RPH")),
2800
+ createdAt: attribute4(source, "CreateDateTime"),
2801
+ agent: attribute4(source, "CreationAgent"),
2802
+ agentDutyCode: attribute4(source, "CreationAgentDutyCode"),
2803
+ agentFunctionCode: attribute4(source, "CreationAgentFunctionCode"),
2804
+ pcc: attribute4(source, "PseudoCityCode"),
2805
+ aaaPcc: attribute4(source, "AAA_PseudoCityCode") ?? attribute4(source, "AAAPseudoCityCode"),
2806
+ receivedFrom: attribute4(source, "ReceivedFrom")
2807
+ }),
2808
+ flightSegments,
2809
+ generalInfo: asArray(info.GeneralInfo).map((entry) => textEntry(entry, "GeneralInfo", response)),
2810
+ remarks: asArray(node2(info.RemarkInfo)?.Remark).map((entry) => textEntry(entry, "Remark", response)),
2811
+ accounting: asArray(info.AccountingInfo).map(
2812
+ (entry) => defined({ action: attribute4(entry, "Action"), type: attribute4(entry, "Type"), linkCode: attribute4(entry, "LinkCode") })
2813
+ )
2814
+ };
2815
+ }
2816
+ function textEntry(entry, label, response) {
2817
+ const action = attribute4(entry, "Action");
2818
+ if (action === void 0) throw malformed3(`${label} sin Action`, response);
2819
+ const texts = asArray(node2(entry)?.Text).map((text) => textOf(text) ?? "");
2820
+ return { action, text: texts.join("\n") };
2821
+ }
2822
+ function flightSegment(segment, response) {
2823
+ const action = attribute4(segment, "Action");
2824
+ if (action === void 0) throw malformed3("FlightSegment sin Action", response);
2825
+ return {
2826
+ action,
2827
+ ...defined({
2828
+ status: attribute4(segment, "Status"),
2829
+ marketingAirline: attribute4(segment.MarketingAirline, "Code"),
2830
+ flightNumber: attribute4(segment, "FlightNumber") ?? attribute4(segment.MarketingAirline, "FlightNumber"),
2831
+ bookingClass: attribute4(segment, "ResBookDesigCode"),
2832
+ origin: attribute4(segment.OriginLocation, "LocationCode"),
2833
+ destination: attribute4(segment.DestinationLocation, "LocationCode"),
2834
+ departureDateTime: attribute4(segment, "DepartureDateTime"),
2835
+ arrivalDateTime: attribute4(segment, "ArrivalDateTime"),
2836
+ numberInParty: integer(attribute4(segment, "NumberInParty")),
2837
+ supplierRef: attribute4(segment.SupplierRef, "ID")
2838
+ })
2839
+ };
2840
+ }
2841
+ function malformed3(detail, response) {
2842
+ return new SabreGatewayError(`${RESERVATION_HISTORY_SERVICE}: respuesta no reconocida (${detail})`, {
2843
+ protocol: "soap",
2844
+ status: response.status,
2845
+ retryable: false,
2846
+ raw: response.raw
2847
+ });
2848
+ }
2849
+
2850
+ // src/reservation/session-reservation.ts
2851
+ var SessionReservation = class {
2852
+ #context;
2853
+ /** @internal Lo construye `SabreSession`. */
2854
+ constructor(context) {
2855
+ this.#context = context;
2856
+ }
2857
+ /**
2858
+ * Get Itinerary History (`TravelItineraryHistoryLLSRQ` 2.4.0, el `*H` del host): el historial
2859
+ * del PNR, una entrada por transacción. Cada una trae quién la firmó, cuándo y desde qué PCC, y
2860
+ * los elementos que cambió con su `action`: los segmentos de vuelo con su estado en ese momento,
2861
+ * y SSR, ticketing, teléfonos, nombres y emails como texto en `generalInfo`. Lo que no se tipa
2862
+ * queda en la respuesta cruda (`onRawResponse`). **Trae datos del pasajero.**
2863
+ *
2864
+ * El historial no registra las colocaciones en cola, ni las del sistema ni un `place` manual
2865
+ * (CERT, 2026-11-06, Q-097): para eso están `queue.list`/`access` o los remarks `QUEUE_PLACE` de
2866
+ * Get Booking.
2867
+ *
2868
+ * **Dentro de una cola, Sabre lo rechaza** para otro PNR (CERT, 2026-11-06, Q-102): lanza
2869
+ * `SabreBusinessError` con `NAK6 -NOT ALLOWED- FINISH OR IGNORE OTHER ACTIVITY`. El PNR del AAA
2870
+ * no cambia, `queue.state` sigue en `'inside'` y el recorrido sigue con `ignore`. Conviene
2871
+ * llamarlo fuera de una cola.
2872
+ *
2873
+ * Es una lectura: se reintenta ante fallos transitorios. Un `locator` que no es de 6 caracteres
2874
+ * alfanuméricos en mayúscula lanza `TypeError` sin red. Un PNR que no existe sale como
2875
+ * `SabreBusinessError` (`service: 'itineraryHistory'`).
2876
+ */
2877
+ async history(locator, options = {}) {
2878
+ if (!isRecordLocator(locator)) {
2879
+ throw new TypeError("sabre-client: reservation.history necesita un localizador de 6 caracteres alfanum\xE9ricos en may\xFAscula");
2880
+ }
2881
+ const response = await this.#context.send(reservationHistoryRequest(locator, options));
2882
+ return parseReservationHistoryResponse(response);
2883
+ }
2884
+ };
2885
+
2528
2886
  // src/session/command.ts
2529
2887
  var SABRE_COMMAND_SERVICE = "sabreCommand";
2530
- var NAMESPACE5 = "http://webservices.sabre.com/sabreXML/2011/10";
2888
+ var NAMESPACE7 = "http://webservices.sabre.com/sabreXML/2011/10";
2531
2889
  var MAX_COMMAND_LENGTH = 1023;
2532
2890
  var DOUBLE_WIDTH = /* @__PURE__ */ new Set(["!", "%", "$", "&", "\xA5", "\xA7"]);
2533
- function fail4(message) {
2891
+ function fail5(message) {
2534
2892
  throw new TypeError(`sabre-client: ${message}`);
2535
2893
  }
2536
2894
  function sabreCommandPayload(command) {
2537
- return `<SabreCommandLLSRQ xmlns="${NAMESPACE5}" Version="2.0.0" ReturnHostCommand="true"><Request Output="SCREEN"><HostCommand>${xmlEscape(command)}</HostCommand></Request></SabreCommandLLSRQ>`;
2895
+ return `<SabreCommandLLSRQ xmlns="${NAMESPACE7}" Version="2.0.0" ReturnHostCommand="true"><Request Output="SCREEN"><HostCommand>${xmlEscape(command)}</HostCommand></Request></SabreCommandLLSRQ>`;
2538
2896
  }
2539
2897
  function commandToken(command) {
2540
2898
  return (command.trim().split(/[¥‡\s]/u)[0] ?? "").slice(0, 4);
@@ -2545,13 +2903,13 @@ function sabreCommandLength(command) {
2545
2903
  return length;
2546
2904
  }
2547
2905
  function sabreCommandRequest(command, options) {
2548
- if (typeof command !== "string" || command.trim() === "") fail4("command es requerido y debe ser un string no vac\xEDo");
2906
+ if (typeof command !== "string" || command.trim() === "") fail5("command es requerido y debe ser un string no vac\xEDo");
2549
2907
  const length = sabreCommandLength(command);
2550
2908
  if (length > MAX_COMMAND_LENGTH) {
2551
- fail4(`command mide ${length} y el m\xE1ximo es ${MAX_COMMAND_LENGTH} (!, %, $, &, \xA5 y \xA7 cuentan 2); Sabre responder\xEDa REQUEST EXCEEDED MAXIMUM LENGTH`);
2909
+ fail5(`command mide ${length} y el m\xE1ximo es ${MAX_COMMAND_LENGTH} (!, %, $, &, \xA5 y \xA7 cuentan 2); Sabre responder\xEDa REQUEST EXCEEDED MAXIMUM LENGTH`);
2552
2910
  }
2553
2911
  const { idempotent = false, ...requestOptions } = options;
2554
- if (typeof idempotent !== "boolean") fail4("idempotent debe ser un boolean");
2912
+ if (typeof idempotent !== "boolean") fail5("idempotent debe ser un boolean");
2555
2913
  return {
2556
2914
  service: SABRE_COMMAND_SERVICE,
2557
2915
  action: "SabreCommandLLSRQ",
@@ -2573,7 +2931,8 @@ function parseSabreCommandResponse(response) {
2573
2931
  return {
2574
2932
  text: textOf((untrimmedPayload(response.raw) ?? response.payload).Response) ?? "",
2575
2933
  warnings: response.warnings,
2576
- messageId: response.messageId
2934
+ messageId: response.messageId,
2935
+ ...respondedAtField(response.raw)
2577
2936
  };
2578
2937
  }
2579
2938
 
@@ -2581,7 +2940,7 @@ function parseSabreCommandResponse(response) {
2581
2940
  var OTA_NAMESPACE = "http://www.opentravel.org/OTA/2002/11";
2582
2941
  var SWS_NAMESPACE = "http://webservices.sabre.com";
2583
2942
  var sessionSend = /* @__PURE__ */ Symbol("sabre-client.session.send");
2584
- function fail5(message) {
2943
+ function fail6(message) {
2585
2944
  throw new TypeError(`sabre-client: ${message}`);
2586
2945
  }
2587
2946
  var SESSION_ERROR_KINDS = /* @__PURE__ */ new Map([
@@ -2610,6 +2969,8 @@ var KEEP_ALIVE_IDLE_MS = 13 * 6e4;
2610
2969
  var SabreSession = class {
2611
2970
  /** Queue Management: contar, recorrer y colocar en colas, dentro de esta sesión. */
2612
2971
  queue;
2972
+ /** La reserva: el historial de un PNR, dentro de esta sesión. */
2973
+ reservation;
2613
2974
  #transport;
2614
2975
  #pcc;
2615
2976
  #conversationId;
@@ -2637,6 +2998,7 @@ var SabreSession = class {
2637
2998
  pcc: internal.pcc,
2638
2999
  log: internal.transport.log
2639
3000
  });
3001
+ this.reservation = new SessionReservation({ send: (request) => this[sessionSend](request) });
2640
3002
  if (internal.keepAlive) this.#scheduleKeepAlive(KEEP_ALIVE_IDLE_MS);
2641
3003
  }
2642
3004
  /**
@@ -2692,11 +3054,11 @@ var SabreSession = class {
2692
3054
  return this.#send(request);
2693
3055
  }
2694
3056
  async #send(request) {
2695
- if (this.#closing !== void 0) fail5("session closed");
3057
+ if (this.#closing !== void 0) fail6("session closed");
2696
3058
  if (this.#unusable !== void 0) {
2697
3059
  throw new TypeError(`sabre-client: session unusable (${this.#unusable.kind})`, { cause: this.#unusable });
2698
3060
  }
2699
- if (this.#busy) fail5("session busy");
3061
+ if (this.#busy) fail6("session busy");
2700
3062
  this.#busy = true;
2701
3063
  try {
2702
3064
  const response = await this.#transport.request({ ...request, ...this.#envelopeFields() });
@@ -2711,6 +3073,7 @@ var SabreSession = class {
2711
3073
  }
2712
3074
  async #sendClose(options) {
2713
3075
  if (this.#unusable !== void 0) return;
3076
+ const { onRawRequest: _skipped, ...closeOptions } = options;
2714
3077
  this.#busy = true;
2715
3078
  try {
2716
3079
  await this.#transport.request({
@@ -2721,7 +3084,7 @@ var SabreSession = class {
2721
3084
  // Solo se reintenta ante `USG_*_BUSY`: tras un fallo de red no se sabe si ya se cerró.
2722
3085
  idempotent: false,
2723
3086
  operation: "close",
2724
- ...options
3087
+ ...closeOptions
2725
3088
  });
2726
3089
  } catch (error) {
2727
3090
  throw this.#markUnusable(error);
@@ -2851,7 +3214,7 @@ function usernameToken(credentials) {
2851
3214
  }
2852
3215
  async function openSession(transport, credentials, options = {}) {
2853
3216
  const { keepAlive = false, ...requestOptions } = options;
2854
- if (typeof keepAlive !== "boolean") fail5("keepAlive debe ser un boolean");
3217
+ if (typeof keepAlive !== "boolean") fail6("keepAlive debe ser un boolean");
2855
3218
  const conversationId = globalThis.crypto.randomUUID();
2856
3219
  const request = {
2857
3220
  service: "sessionCreate",
@@ -2956,27 +3319,27 @@ var SabreLegacy = class {
2956
3319
 
2957
3320
  // src/auth/credentials.ts
2958
3321
  var DEFAULT_DOMAIN = "AA";
2959
- function fail6(message) {
3322
+ function fail7(message) {
2960
3323
  throw new TypeError(`sabre-client: ${message}`);
2961
3324
  }
2962
3325
  function requireString2(value, field) {
2963
- if (typeof value !== "string" || value.trim() === "") fail6(`${field} es requerido y debe ser un string no vac\xEDo`);
3326
+ if (typeof value !== "string" || value.trim() === "") fail7(`${field} es requerido y debe ser un string no vac\xEDo`);
2964
3327
  }
2965
3328
  function buildCredentials(options) {
2966
- if (typeof options !== "object" || options === null) fail6("buildCredentials recibe un objeto");
3329
+ if (typeof options !== "object" || options === null) fail7("buildCredentials recibe un objeto");
2967
3330
  const authVersion = options.authVersion ?? "v3";
2968
3331
  if (authVersion !== "v3" && authVersion !== "v2") {
2969
- fail6(`authVersion debe ser 'v3' o 'v2' (recibido: ${JSON.stringify(authVersion)})`);
3332
+ fail7(`authVersion debe ser 'v3' o 'v2' (recibido: ${JSON.stringify(authVersion)})`);
2970
3333
  }
2971
3334
  const { user, pcc, password, clientId, clientSecret } = options;
2972
3335
  const domain = options.domain ?? DEFAULT_DOMAIN;
2973
3336
  requireString2(user, "user");
2974
3337
  requireString2(password, "password");
2975
- if (!isPseudoCityCode(pcc)) fail6("pcc debe tener 3 o 4 caracteres alfanum\xE9ricos");
3338
+ if (!isPseudoCityCode(pcc)) fail7("pcc debe tener 3 o 4 caracteres alfanum\xE9ricos");
2976
3339
  requireString2(domain, "domain");
2977
- if (/[-:]/.test(domain)) fail6("domain no puede contener '-' ni ':'");
3340
+ if (/[-:]/.test(domain)) fail7("domain no puede contener '-' ni ':'");
2978
3341
  if (clientId === void 0 !== (clientSecret === void 0)) {
2979
- fail6("clientId y clientSecret van juntos: pasa los dos o ninguno");
3342
+ fail7("clientId y clientSecret van juntos: pasa los dos o ninguno");
2980
3343
  }
2981
3344
  if (authVersion === "v3" || clientId !== void 0) {
2982
3345
  requireString2(clientId, authVersion === "v3" ? "clientId (requerido con authVersion v3)" : "clientId");
@@ -3285,6 +3648,22 @@ function hostTime(text) {
3285
3648
  return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
3286
3649
  }
3287
3650
 
3651
+ // src/queue/queue-place-remark.ts
3652
+ var MONTHS2 = /* @__PURE__ */ new Set(["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]);
3653
+ var REMARK = /^([A-Z0-9]{3,4})(\d{2})([A-Z]{3})(\d{1,3})\/(\d{1,3})$/;
3654
+ function parseQueuePlaceRemark(text) {
3655
+ if (typeof text !== "string") return null;
3656
+ const match = REMARK.exec(text.trim());
3657
+ if (match === null) return null;
3658
+ const [, pcc, day, month, queueText, picText] = match;
3659
+ const dayNumber2 = Number(day);
3660
+ if (dayNumber2 < 1 || dayNumber2 > 31 || !MONTHS2.has(month)) return null;
3661
+ const queue = Number(queueText);
3662
+ const pic = Number(picText);
3663
+ if (queue > MAX_QUEUE_NUMBER || !isInstructionCode(pic)) return null;
3664
+ return { pcc, date: `${day}${month}`, queue, pic };
3665
+ }
3666
+
3288
3667
  // src/air/seat-availability-request.ts
3289
3668
  function buildSeatAvailabilityRequest(source) {
3290
3669
  const { group, itinerary, segments: only, currencyCode } = source;
@@ -3369,7 +3748,7 @@ function fareComponent(fareComponentId, component) {
3369
3748
  }
3370
3749
 
3371
3750
  // src/index.ts
3372
- var VERSION = "0.4.1";
3751
+ var VERSION = "0.5.0";
3373
3752
  export {
3374
3753
  Sabre,
3375
3754
  SabreBusinessError,
@@ -3385,6 +3764,7 @@ export {
3385
3764
  buildRevalidateItineraryRequest,
3386
3765
  buildSeatAvailabilityRequest,
3387
3766
  parseQueueParagraph,
3767
+ parseQueuePlaceRemark,
3388
3768
  resolveHostDate
3389
3769
  };
3390
3770
  //# sourceMappingURL=index.js.map