@getpeppr/cli 0.9.0 → 0.10.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
@@ -623,7 +623,10 @@ var ICD_CODES = /* @__PURE__ */ new Set([
623
623
  "0242",
624
624
  "0243",
625
625
  "0244",
626
- "0245"
626
+ "0245",
627
+ "0246",
628
+ "0247",
629
+ "0248"
627
630
  ]);
628
631
  var ISO6523_ICD_CODES = Object.freeze({
629
632
  has: (v) => ICD_CODES.has(v),
@@ -717,10 +720,41 @@ function formatVatRate(vatRate, field) {
717
720
  }
718
721
  return String(vatRate);
719
722
  }
720
- function formatBaseQuantity(baseQuantity, field) {
723
+ function assertValidBaseQuantity(baseQuantity, field) {
721
724
  if (typeof baseQuantity !== "number" || !Number.isFinite(baseQuantity) || baseQuantity <= 0) {
722
725
  throw new UblBuilderInputError("baseQuantity must be a finite number greater than zero.", field, "PEPPOL-EN16931-R121");
723
726
  }
727
+ }
728
+ var ADJUSTMENT_CONTRACT_RULE = "GETPEPPR-ALLOWANCE-CHARGE-AMOUNT";
729
+ var ADJUSTMENT_AMOUNT_MESSAGE = "Allowance and charge amounts must be finite numbers greater than or equal to zero. An allowance reduces the amount and a charge increases it; encode the direction in the field, never in the sign.";
730
+ var DERIVED_AMOUNT_CONTRACT_RULE = "GETPEPPR-DERIVED-AMOUNT";
731
+ var NON_FINITE_DERIVED_AMOUNT_MESSAGE = "Derived amount is not finite (overflow). Reduce quantities, prices or adjustment amounts so every total stays representable.";
732
+ function survivesCents(value) {
733
+ return Number.isFinite(value) && Number.isFinite(value * 100);
734
+ }
735
+ function assertValidAdjustmentAmounts(items, field) {
736
+ if (items === void 0)
737
+ return;
738
+ if (!Array.isArray(items)) {
739
+ throw new UblBuilderInputError(`${field} must be an array \u2014 omit the field instead of sending ${items === null ? "null" : typeof items}`, field, ADJUSTMENT_CONTRACT_RULE);
740
+ }
741
+ for (const [i, item] of items.entries()) {
742
+ const amount = item?.amount;
743
+ if (item === null || typeof item !== "object" || typeof amount !== "number" || !Number.isFinite(amount) || amount < 0) {
744
+ throw new UblBuilderInputError(ADJUSTMENT_AMOUNT_MESSAGE, `${field}[${i}].amount`, ADJUSTMENT_CONTRACT_RULE);
745
+ }
746
+ }
747
+ }
748
+ function assertDocumentAdjustmentAmounts(input) {
749
+ for (const [i, line] of input.lines.entries()) {
750
+ assertValidAdjustmentAmounts(line.allowances, `lines[${i}].allowances`);
751
+ assertValidAdjustmentAmounts(line.charges, `lines[${i}].charges`);
752
+ }
753
+ assertValidAdjustmentAmounts(input.allowances, "allowances");
754
+ assertValidAdjustmentAmounts(input.charges, "charges");
755
+ }
756
+ function formatBaseQuantity(baseQuantity, field) {
757
+ assertValidBaseQuantity(baseQuantity, field);
724
758
  const numeric = String(baseQuantity);
725
759
  const exponentMarker = numeric.search(/[eE]/);
726
760
  if (exponentMarker === -1)
@@ -763,8 +797,8 @@ function buildPartyXml(party, role) {
763
797
  <cac:Party>
764
798
  <cbc:EndpointID schemeID="${escapeXml(endpointScheme)}">${escapeXml(endpointId)}</cbc:EndpointID>
765
799
  ${// ⛔ DEUX listes, pas une. `BR-CL-25` juge l'EndpointID ci-dessus contre la
766
- // liste EAS (101 valeurs, jusqu'à `9959`) ; `BR-CL-10` juge CE champ-ci
767
- // contre la liste ISO 6523 ICD (240 valeurs, `0002`–`0245`). `9932` (GB),
800
+ // liste EAS (104 valeurs, jusqu'à `9959`) ; `BR-CL-10` juge CE champ-ci
801
+ // contre la liste ISO 6523 ICD (243 valeurs, `0002`–`0248`). `9932` (GB),
768
802
  // `9935` (IE) et `9930` (DE) sont légaux là-haut et FATALS ici.
769
803
  //
770
804
  // Le champ (BT-29) est optionnel : l'omettre est la seule écriture
@@ -952,14 +986,21 @@ function buildLineAllowanceChargeXml(reason, amount, isCharge, currency) {
952
986
  <cbc:Amount currencyID="${escapeXml(currency)}">${formatAmount(amount)}</cbc:Amount>
953
987
  </cac:AllowanceCharge>`;
954
988
  }
955
- function calculateLineExtensionAmount(line) {
956
- const base = line.quantity * line.unitPrice;
989
+ function calculateLineExtensionAmount(line, lineIndex) {
990
+ if (line.baseQuantity !== void 0) {
991
+ assertValidBaseQuantity(line.baseQuantity, `lines[${lineIndex}].baseQuantity`);
992
+ }
993
+ const base = line.quantity * line.unitPrice / (line.baseQuantity ?? 1);
957
994
  const lineAllowances = (line.allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
958
995
  const lineCharges = (line.charges ?? []).reduce((sum, c) => sum + c.amount, 0);
959
- return base - lineAllowances + lineCharges;
996
+ const total = base - lineAllowances + lineCharges;
997
+ if (!survivesCents(total)) {
998
+ throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, `lines[${lineIndex}]`, DERIVED_AMOUNT_CONTRACT_RULE);
999
+ }
1000
+ return total;
960
1001
  }
961
1002
  function buildDocumentLineXml(line, index, currency, lineTag, qtyTag) {
962
- const lineTotal = calculateLineExtensionAmount(line);
1003
+ const lineTotal = calculateLineExtensionAmount(line, index);
963
1004
  const unit = resolveUnitCode(line.unit ?? DEFAULT_UNIT);
964
1005
  const vatCategory = line.vatCategory ?? "S";
965
1006
  const lineAllowancesXml = (line.allowances ?? []).map((a) => buildLineAllowanceChargeXml(a.reason, a.amount, false, currency)).join("");
@@ -1035,7 +1076,7 @@ function calculateTaxSubtotals(lines, allowances, charges, options = {}) {
1035
1076
  }
1036
1077
  }
1037
1078
  for (const [index, line] of lines.entries()) {
1038
- addToGroup(line.vatCategory ?? "S", line.vatRate, calculateLineExtensionAmount(line), line.taxExemptReason, `lines[${index}]`);
1079
+ addToGroup(line.vatCategory ?? "S", line.vatRate, calculateLineExtensionAmount(line, index), line.taxExemptReason, `lines[${index}]`);
1039
1080
  }
1040
1081
  for (const [index, a] of (allowances ?? []).entries()) {
1041
1082
  addToGroup(a.vatCategory ?? "S", a.vatRate, -a.amount, a.taxExemptReason, `allowances[${index}]`);
@@ -1050,19 +1091,29 @@ function calculateTaxSubtotals(lines, allowances, charges, options = {}) {
1050
1091
  }
1051
1092
  }
1052
1093
  }
1053
- return Array.from(groups.values()).map((subtotal) => ({
1054
- ...subtotal,
1055
- taxAmount: round2(subtotal.taxableAmount * (subtotal.vatRate / 100))
1056
- }));
1094
+ return Array.from(groups.values()).map((subtotal) => {
1095
+ const taxableAmount = round2(subtotal.taxableAmount);
1096
+ const taxAmount = round2(taxableAmount * (subtotal.vatRate / 100));
1097
+ if (!survivesCents(taxableAmount) || !survivesCents(taxAmount)) {
1098
+ throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, "totals", DERIVED_AMOUNT_CONTRACT_RULE);
1099
+ }
1100
+ return { ...subtotal, taxableAmount, taxAmount };
1101
+ });
1057
1102
  }
1058
1103
  function calculateDocumentTotals(lines, allowances, charges, options = {}) {
1059
1104
  const taxSubtotals = calculateTaxSubtotals(lines, allowances, charges, options);
1060
- const lineExtensionAmount = lines.reduce((sum, line) => sum + calculateLineExtensionAmount(line), 0);
1105
+ const lineExtensionAmount = lines.reduce((sum, line, index) => sum + calculateLineExtensionAmount(line, index), 0);
1061
1106
  const allowanceTotalAmount = (allowances ?? []).reduce((sum, a) => sum + a.amount, 0);
1062
1107
  const chargeTotalAmount = (charges ?? []).reduce((sum, c) => sum + c.amount, 0);
1063
1108
  const taxExclusiveAmount = round2(lineExtensionAmount - allowanceTotalAmount + chargeTotalAmount);
1109
+ if (!survivesCents(allowanceTotalAmount) || !survivesCents(chargeTotalAmount) || !survivesCents(taxExclusiveAmount)) {
1110
+ throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, "totals", DERIVED_AMOUNT_CONTRACT_RULE);
1111
+ }
1064
1112
  const totalTax = round2(taxSubtotals.reduce((sum, st) => sum + st.taxAmount, 0));
1065
1113
  const taxInclusiveAmount = round2(taxExclusiveAmount + totalTax);
1114
+ if (!survivesCents(totalTax) || !survivesCents(taxInclusiveAmount)) {
1115
+ throw new UblBuilderInputError(NON_FINITE_DERIVED_AMOUNT_MESSAGE, "totals", DERIVED_AMOUNT_CONTRACT_RULE);
1116
+ }
1066
1117
  return {
1067
1118
  lineExtensionAmount,
1068
1119
  allowanceTotalAmount,
@@ -1147,6 +1198,7 @@ function buildOrderReferenceXml(orderReference, salesOrderReference) {
1147
1198
  return parts.join("");
1148
1199
  }
1149
1200
  function buildInvoiceXml(input) {
1201
+ assertDocumentAdjustmentAmounts(input);
1150
1202
  const currency = input.currency ?? "EUR";
1151
1203
  const date = formatDate(input.date);
1152
1204
  const dueDate = input.dueDate ? formatDate(input.dueDate) : void 0;
@@ -1194,6 +1246,7 @@ function buildInvoiceXml(input) {
1194
1246
  </Invoice>`;
1195
1247
  }
1196
1248
  function buildCreditNoteXml(input) {
1249
+ assertDocumentAdjustmentAmounts(input);
1197
1250
  const currency = input.currency ?? "EUR";
1198
1251
  const date = formatDate(input.date);
1199
1252
  const dueDate = input.dueDate ? formatDate(input.dueDate) : void 0;
@@ -1437,8 +1490,13 @@ var CURRENCIES = /* @__PURE__ */ new Map([
1437
1490
  ["CZK", { code: "CZK", name: "Czech Koruna", minorUnits: 2 }],
1438
1491
  ["HUF", { code: "HUF", name: "Hungarian Forint", minorUnits: 2 }],
1439
1492
  ["RON", { code: "RON", name: "Romanian Leu", minorUnits: 2 }],
1440
- ["BGN", { code: "BGN", name: "Bulgarian Lev", minorUnits: 2 }],
1441
- ["HRK", { code: "HRK", name: "Croatian Kuna", minorUnits: 2 }],
1493
+ // ⛔ BGN and HRK are GONE (GPR-1205). Neither is in BR-CL-04/BR-CL-05 of the
1494
+ // graved 3.0.21 rulebooks Bulgaria and Croatia both joined the euro — so
1495
+ // accepting them here would wave through a document the network refuses, which
1496
+ // is the exact failure this release closes. XCG replaces ANG (never carried
1497
+ // here) for the Dutch Caribbean. `currency-codes-network-parity.test.ts` reds
1498
+ // if this table ever readmits a code the rulebooks reject.
1499
+ ["XCG", { code: "XCG", name: "Caribbean Guilder", minorUnits: 2 }],
1442
1500
  ["ISK", { code: "ISK", name: "Icelandic Krona", minorUnits: 0 }],
1443
1501
  ["TRY", { code: "TRY", name: "Turkish Lira", minorUnits: 2 }],
1444
1502
  ["JPY", { code: "JPY", name: "Japanese Yen", minorUnits: 0 }],
@@ -1564,6 +1622,11 @@ function assertString(value, fieldPath, errors) {
1564
1622
  return true;
1565
1623
  }
1566
1624
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
1625
+ var ALLOWANCE_CHARGE_CONTRACT_RULE = "GETPEPPR-ALLOWANCE-CHARGE-AMOUNT";
1626
+ var DERIVED_AMOUNT_CONTRACT_RULE2 = "GETPEPPR-DERIVED-AMOUNT";
1627
+ function survivesCents2(value) {
1628
+ return Number.isFinite(value) && Number.isFinite(value * 100);
1629
+ }
1567
1630
  function validateParty(party, path) {
1568
1631
  const errors = [];
1569
1632
  if (party.name === void 0 || party.name === null || party.name === "") {
@@ -1637,6 +1700,31 @@ function validateLine(line, index, isCreditNote = false) {
1637
1700
  } else if (line.vatRate < 0 || line.vatRate > 100) {
1638
1701
  errors.push(error(`${path}.vatRate`, `VAT rate must be between 0 and 100, got ${line.vatRate}`, void 0, "Use 0 for zero-rated, 21 for standard Belgian VAT, etc."));
1639
1702
  }
1703
+ const adjustmentItems = {
1704
+ allowances: Array.isArray(line.allowances) ? line.allowances : [],
1705
+ charges: Array.isArray(line.charges) ? line.charges : []
1706
+ };
1707
+ for (const kind of ["allowances", "charges"]) {
1708
+ const items = line[kind];
1709
+ if (items === void 0)
1710
+ continue;
1711
+ if (!Array.isArray(items)) {
1712
+ errors.push(error(`${path}.${kind}`, `${kind} must be an array \u2014 omit the field instead of sending ${items === null ? "null" : typeof items}`, ALLOWANCE_CHARGE_CONTRACT_RULE));
1713
+ continue;
1714
+ }
1715
+ for (const [i, item] of items.entries()) {
1716
+ const amount = item?.amount;
1717
+ if (typeof amount !== "number" || !Number.isFinite(amount) || amount < 0) {
1718
+ errors.push(error(`${path}.${kind}[${i}].amount`, `Allowance and charge amounts must be zero or positive finite numbers, got ${String(amount)}`, ALLOWANCE_CHARGE_CONTRACT_RULE, "An allowance reduces the amount and a charge increases it \u2014 encode the direction in the field, never in the sign."));
1719
+ }
1720
+ }
1721
+ }
1722
+ const bq = typeof line.baseQuantity === "number" && line.baseQuantity > 0 ? line.baseQuantity : 1;
1723
+ const derivedNet = (line.quantity ?? 0) * (line.unitPrice ?? 0) / bq - adjustmentItems.allowances.reduce((sum, a) => sum + (a?.amount ?? 0), 0) + adjustmentItems.charges.reduce((sum, c) => sum + (c?.amount ?? 0), 0);
1724
+ const derivedVat = derivedNet * ((typeof line.vatRate === "number" ? line.vatRate : 0) / 100);
1725
+ if (!survivesCents2(derivedNet) || !survivesCents2(derivedVat)) {
1726
+ errors.push(error(path, "Derived amount is not finite (overflow). Reduce quantities, prices or adjustment amounts so every total stays representable.", DERIVED_AMOUNT_CONTRACT_RULE2));
1727
+ }
1640
1728
  return errors;
1641
1729
  }
1642
1730
  function validateInvoice(input) {
@@ -1713,15 +1801,52 @@ function validateInvoice(input) {
1713
1801
  if (value === void 0)
1714
1802
  continue;
1715
1803
  if (!Array.isArray(value)) {
1716
- errors.push(error(field, `${field} must be an array`, void 0));
1804
+ errors.push(error(field, `${field} must be an array \u2014 omit the field instead of sending ${value === null ? "null" : typeof value}`, ALLOWANCE_CHARGE_CONTRACT_RULE));
1717
1805
  continue;
1718
1806
  }
1719
1807
  for (const [index, item] of value.entries()) {
1720
1808
  if (item === null || typeof item !== "object") {
1721
1809
  errors.push(error(`${field}[${index}]`, `${field}[${index}] must be an object`, void 0));
1810
+ continue;
1811
+ }
1812
+ const amount = item.amount;
1813
+ if (typeof amount !== "number" || !Number.isFinite(amount) || amount < 0) {
1814
+ errors.push(error(`${field}[${index}].amount`, `Allowance and charge amounts must be zero or positive finite numbers, got ${String(amount)}`, ALLOWANCE_CHARGE_CONTRACT_RULE, "An allowance reduces the amount and a charge increases it \u2014 encode the direction in the field, never in the sign."));
1722
1815
  }
1723
1816
  }
1724
1817
  }
1818
+ if (Array.isArray(input.lines)) {
1819
+ const itemsOf = (value) => Array.isArray(value) ? value : [];
1820
+ const amountOf = (item) => typeof item?.amount === "number" ? item.amount : 0;
1821
+ const lineNets = input.lines.reduce((sum, line) => {
1822
+ if (typeof line !== "object" || line === null)
1823
+ return sum;
1824
+ const lbq = typeof line.baseQuantity === "number" && line.baseQuantity > 0 ? line.baseQuantity : 1;
1825
+ return sum + (line.quantity ?? 0) * (line.unitPrice ?? 0) / lbq - itemsOf(line.allowances).reduce((s, a) => s + amountOf(a), 0) + itemsOf(line.charges).reduce((s, c) => s + amountOf(c), 0);
1826
+ }, 0);
1827
+ const allowanceTotal = itemsOf(input.allowances).reduce((s, a) => s + amountOf(a), 0);
1828
+ const chargeTotal = itemsOf(input.charges).reduce((s, c) => s + amountOf(c), 0);
1829
+ const vatOf = (items, sign) => {
1830
+ if (!Array.isArray(items))
1831
+ return 0;
1832
+ return items.reduce((s, it) => {
1833
+ const rec = it;
1834
+ const amount = typeof rec?.amount === "number" ? rec.amount : 0;
1835
+ const rate = typeof rec?.vatRate === "number" ? rec.vatRate : 0;
1836
+ return s + sign * amount * (rate / 100);
1837
+ }, 0);
1838
+ };
1839
+ const docVat = vatOf(input.allowances, -1) + vatOf(input.charges, 1) + input.lines.reduce((s, line) => {
1840
+ if (typeof line !== "object" || line === null)
1841
+ return s;
1842
+ const lbq = typeof line.baseQuantity === "number" && line.baseQuantity > 0 ? line.baseQuantity : 1;
1843
+ const net = (line.quantity ?? 0) * (line.unitPrice ?? 0) / lbq;
1844
+ return s + net * ((typeof line.vatRate === "number" ? line.vatRate : 0) / 100);
1845
+ }, 0);
1846
+ if (!survivesCents2(lineNets - allowanceTotal + chargeTotal) || !survivesCents2(docVat)) {
1847
+ errors.push(error("totals", "Derived amount is not finite (overflow). Reduce quantities, prices or adjustment amounts so every total stays representable.", DERIVED_AMOUNT_CONTRACT_RULE2));
1848
+ }
1849
+ }
1725
1850
  if (input.date) {
1726
1851
  if (!ISO_DATE_RE.test(input.date)) {
1727
1852
  errors.push(error("date", `Invalid date format: "${input.date}"`, void 0, "Use ISO 8601: YYYY-MM-DD"));
@@ -2331,18 +2456,171 @@ function statusFamily(status) {
2331
2456
  var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
2332
2457
 
2333
2458
  // ../sdk/dist/version.js
2334
- var SDK_VERSION = "4.8.0";
2459
+ var SDK_VERSION = "5.0.0";
2460
+
2461
+ // ../sdk/dist/core/api-result.js
2462
+ var API_RESULT_HEADER_NAMES = {
2463
+ requestId: "Getpeppr-Request-Id",
2464
+ resultCode: "Getpeppr-Result-Code",
2465
+ resultMessage: "Getpeppr-Result-Message",
2466
+ retryable: "Getpeppr-Retryable",
2467
+ remediation: "Getpeppr-Remediation",
2468
+ docs: "Getpeppr-Result-Docs"
2469
+ };
2470
+ var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
2471
+ function stripControls(value) {
2472
+ return value.replace(CONTROL_CHARACTERS, " ");
2473
+ }
2474
+ function safeDocsUrl(value) {
2475
+ if (typeof value !== "string")
2476
+ return null;
2477
+ let parsed;
2478
+ try {
2479
+ parsed = new URL(value);
2480
+ } catch {
2481
+ return null;
2482
+ }
2483
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
2484
+ return null;
2485
+ return parsed.href;
2486
+ }
2487
+ var MAX_BYTES = {
2488
+ requestId: 256,
2489
+ code: 256,
2490
+ message: 1024,
2491
+ remediation: 64,
2492
+ docs: 2048,
2493
+ // ⚠️ A COST bound, not a correctness one — and the distinction is measured.
2494
+ // When this field was still trimmed, a huge padded value could normalise into
2495
+ // `true`, so the ceiling changed the verdict. Now that the token is compared
2496
+ // verbatim, no string can be both over-long and equal to "true"/"false": a
2497
+ // mutation removing this ceiling SURVIVES the suite, and correctly so. It
2498
+ // stays to bound the whitespace/control scan on an absurd value, and it is
2499
+ // the one entry here with no test — deliberately, since any assertion would
2500
+ // be satisfied by both answers.
2501
+ retryable: 32
2502
+ };
2503
+ var STRICT_HTTPS_URL = /^https:\/\/[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?(?::\d{1,5})?(?:\/[^\s\\]*)?$/i;
2504
+ var TEXT_ENCODER = new TextEncoder();
2505
+ function withinBudget(raw, maxBytes) {
2506
+ if (raw === null)
2507
+ return void 0;
2508
+ return TEXT_ENCODER.encode(raw).length > maxBytes ? void 0 : raw;
2509
+ }
2510
+ function readSentence(raw, maxBytes) {
2511
+ const bounded = withinBudget(raw, maxBytes);
2512
+ if (bounded === void 0)
2513
+ return void 0;
2514
+ const cleaned = stripControls(bounded).trim();
2515
+ return cleaned === "" ? void 0 : cleaned;
2516
+ }
2517
+ function readMachineToken(raw, maxBytes) {
2518
+ const bounded = withinBudget(raw, maxBytes);
2519
+ if (bounded === void 0)
2520
+ return void 0;
2521
+ if (bounded === "")
2522
+ return void 0;
2523
+ if (new RegExp(CONTROL_CHARACTERS.source).test(bounded))
2524
+ return void 0;
2525
+ if (/\s/u.test(bounded))
2526
+ return void 0;
2527
+ return bounded;
2528
+ }
2529
+ function readBoolean(raw) {
2530
+ const token = readMachineToken(raw, MAX_BYTES.retryable);
2531
+ if (token === void 0)
2532
+ return void 0;
2533
+ if (token === "true")
2534
+ return true;
2535
+ if (token === "false")
2536
+ return false;
2537
+ return void 0;
2538
+ }
2539
+ function readDocsUrl(raw) {
2540
+ const token = readMachineToken(raw, MAX_BYTES.docs);
2541
+ if (token === void 0)
2542
+ return void 0;
2543
+ if (!STRICT_HTTPS_URL.test(token))
2544
+ return void 0;
2545
+ const parsed = safeDocsUrl(token);
2546
+ if (parsed === null)
2547
+ return void 0;
2548
+ const url = new URL(parsed);
2549
+ if (url.protocol !== "https:")
2550
+ return void 0;
2551
+ if (url.username !== "" || url.password !== "")
2552
+ return void 0;
2553
+ return url.href;
2554
+ }
2555
+ function parseApiResultHeaders(headers) {
2556
+ const requestId = readMachineToken(headers.get(API_RESULT_HEADER_NAMES.requestId), MAX_BYTES.requestId);
2557
+ const code = readMachineToken(headers.get(API_RESULT_HEADER_NAMES.resultCode), MAX_BYTES.code);
2558
+ const message = readSentence(headers.get(API_RESULT_HEADER_NAMES.resultMessage), MAX_BYTES.message);
2559
+ const retryable = readBoolean(headers.get(API_RESULT_HEADER_NAMES.retryable));
2560
+ const remediation = readMachineToken(headers.get(API_RESULT_HEADER_NAMES.remediation), MAX_BYTES.remediation);
2561
+ const docs = readDocsUrl(headers.get(API_RESULT_HEADER_NAMES.docs));
2562
+ const result = {};
2563
+ if (requestId !== void 0)
2564
+ result.requestId = requestId;
2565
+ if (code !== void 0)
2566
+ result.code = code;
2567
+ if (message !== void 0)
2568
+ result.message = message;
2569
+ if (retryable !== void 0)
2570
+ result.retryable = retryable;
2571
+ if (remediation !== void 0)
2572
+ result.remediation = remediation;
2573
+ if (docs !== void 0)
2574
+ result.docs = docs;
2575
+ return Object.keys(result).length === 0 ? void 0 : result;
2576
+ }
2335
2577
 
2336
2578
  // ../sdk/dist/core/client.js
2337
- function findHeaderCaseInsensitive(headers, name) {
2579
+ function normalizeHeaderValue(value) {
2580
+ return value.replace(/^[\t\n\r ]+|[\t\n\r ]+$/g, "");
2581
+ }
2582
+ function isCarriableHeaderValue(value) {
2583
+ for (let i = 0; i < value.length; i++) {
2584
+ const code = value.charCodeAt(i);
2585
+ const carriable = code === 9 || code >= 32 && code <= 126 || code >= 128 && code <= 255;
2586
+ if (!carriable)
2587
+ return false;
2588
+ }
2589
+ return true;
2590
+ }
2591
+ function idempotencyKeyRefusal(message) {
2592
+ return new PeppolValidationError(`Invalid idempotency key: ${message}`, {
2593
+ valid: false,
2594
+ errors: [{ field: "idempotencyKey", message }],
2595
+ warnings: []
2596
+ });
2597
+ }
2598
+ function applyIdempotencyKey(headers, options) {
2599
+ const key = options?.idempotencyKey;
2600
+ if (key === void 0 || key === null)
2601
+ return;
2602
+ if (typeof key !== "string") {
2603
+ throw idempotencyKeyRefusal(`expected a string, received ${Array.isArray(key) ? "an array" : `a ${typeof key}`}.`);
2604
+ }
2605
+ const normalized = normalizeHeaderValue(key);
2606
+ if (normalized === "") {
2607
+ throw idempotencyKeyRefusal("the key is blank once HTTP whitespace is stripped, so it would reach the gateway empty and protect nothing. Pass a non-blank key, or omit the option.");
2608
+ }
2609
+ if (!isCarriableHeaderValue(normalized)) {
2610
+ throw idempotencyKeyRefusal("the key contains a character no HTTP header can carry. A header value may hold only HTAB, space, U+0021-U+007E and U+0080-U+00FF (RFC 9110 field-value) \u2014 so every control character other than the tab, plus DEL and anything above U+00FF, is refused by the transport itself.");
2611
+ }
2612
+ headers["Idempotency-Key"] = normalized;
2613
+ }
2614
+ function carriesUsableIdempotencyKey(headers) {
2338
2615
  if (!headers)
2339
- return void 0;
2340
- const target = name.toLowerCase();
2341
- for (const [key, value] of Object.entries(headers)) {
2342
- if (key.toLowerCase() === target)
2343
- return value;
2616
+ return false;
2617
+ for (const [name, value] of Object.entries(headers)) {
2618
+ if (name.toLowerCase() !== "idempotency-key")
2619
+ continue;
2620
+ if (typeof value === "string" && normalizeHeaderValue(value) !== "")
2621
+ return true;
2344
2622
  }
2345
- return void 0;
2623
+ return false;
2346
2624
  }
2347
2625
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
2348
2626
  function sleep(ms) {
@@ -2369,33 +2647,16 @@ function parseRetryAfter(headerValue) {
2369
2647
  }
2370
2648
  return void 0;
2371
2649
  }
2372
- var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
2373
- function stripControls(value) {
2374
- return value.replace(CONTROL_CHARACTERS, " ");
2375
- }
2376
2650
  function readOwn(source, key) {
2377
2651
  return Object.hasOwn(source, key) ? source[key] : void 0;
2378
2652
  }
2379
- function readSentence(source, key) {
2653
+ function readSentence2(source, key) {
2380
2654
  const value = readOwn(source, key);
2381
2655
  if (typeof value !== "string")
2382
2656
  return null;
2383
2657
  const cleaned = stripControls(value).trim();
2384
2658
  return cleaned === "" ? null : cleaned;
2385
2659
  }
2386
- function safeDocsUrl(value) {
2387
- if (typeof value !== "string")
2388
- return null;
2389
- let parsed;
2390
- try {
2391
- parsed = new URL(value);
2392
- } catch {
2393
- return null;
2394
- }
2395
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
2396
- return null;
2397
- return parsed.href;
2398
- }
2399
2660
  function formatApiErrorMessage(status, rawBody) {
2400
2661
  const verbatim = `getpeppr API error (${status}): ${stripControls(rawBody)}`;
2401
2662
  let parsed;
@@ -2407,7 +2668,7 @@ function formatApiErrorMessage(status, rawBody) {
2407
2668
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2408
2669
  return verbatim;
2409
2670
  }
2410
- const sentence = readSentence(parsed, "message") ?? readSentence(parsed, "error");
2671
+ const sentence = readSentence2(parsed, "message") ?? readSentence2(parsed, "error");
2411
2672
  if (sentence === null)
2412
2673
  return verbatim;
2413
2674
  const link = safeDocsUrl(readOwn(parsed, "docs"));
@@ -2415,6 +2676,8 @@ function formatApiErrorMessage(status, rawBody) {
2415
2676
  }
2416
2677
  function isRetryableError(error2) {
2417
2678
  if (error2 instanceof PeppolApiError) {
2679
+ if (error2.retryable !== void 0)
2680
+ return error2.retryable;
2418
2681
  return RETRYABLE_STATUS_CODES.has(error2.statusCode);
2419
2682
  }
2420
2683
  if (error2 instanceof Error && error2.name === "AbortError") {
@@ -2456,7 +2719,7 @@ var GetpepprAdapter = class {
2456
2719
  lastError = err;
2457
2720
  const is429 = err instanceof PeppolApiError && err.statusCode === 429;
2458
2721
  const isSafeMethod = /^(GET|DELETE|HEAD)$/i.test(method);
2459
- const hasIdempotencyKey = !!findHeaderCaseInsensitive(extraHeaders, "Idempotency-Key");
2722
+ const hasIdempotencyKey = carriesUsableIdempotencyKey(extraHeaders);
2460
2723
  const canRetry = is429 || isSafeMethod || hasIdempotencyKey;
2461
2724
  if (attempt < maxRetries && canRetry && isRetryableError(err)) {
2462
2725
  const retryAfterMs = err instanceof PeppolApiError ? err.retryAfterMs : void 0;
@@ -2499,6 +2762,7 @@ var GetpepprAdapter = class {
2499
2762
  body: body ? JSON.stringify(body) : void 0,
2500
2763
  signal: controller.signal
2501
2764
  });
2765
+ const result = parseApiResultHeaders(response.headers);
2502
2766
  if (!response.ok) {
2503
2767
  const errorBody = await response.text().catch(() => "Unknown error");
2504
2768
  const retryAfterMs = response.status === 429 ? parseRetryAfter(response.headers.get("Retry-After")) : void 0;
@@ -2509,12 +2773,13 @@ var GetpepprAdapter = class {
2509
2773
  headers: Object.fromEntries(response.headers.entries()),
2510
2774
  body: errorBody,
2511
2775
  durationMs: Date.now() - startTime,
2512
- timestamp: Date.now()
2776
+ timestamp: Date.now(),
2777
+ result: cloneResultForHook(result)
2513
2778
  });
2514
2779
  } catch {
2515
2780
  }
2516
2781
  }
2517
- throw new PeppolApiError(formatApiErrorMessage(response.status, errorBody), response.status, errorBody, retryAfterMs);
2782
+ throw new PeppolApiError(formatApiErrorMessage(response.status, errorBody), response.status, errorBody, retryAfterMs, result);
2518
2783
  }
2519
2784
  if (response.status === 204) {
2520
2785
  if (this.onResponse) {
@@ -2524,7 +2789,8 @@ var GetpepprAdapter = class {
2524
2789
  headers: Object.fromEntries(response.headers.entries()),
2525
2790
  body: void 0,
2526
2791
  durationMs: Date.now() - startTime,
2527
- timestamp: Date.now()
2792
+ timestamp: Date.now(),
2793
+ result: cloneResultForHook(result)
2528
2794
  });
2529
2795
  } catch {
2530
2796
  }
@@ -2535,7 +2801,20 @@ var GetpepprAdapter = class {
2535
2801
  try {
2536
2802
  responseBody = await response.json();
2537
2803
  } catch {
2538
- throw new PeppolApiError(`getpeppr API error: unexpected response format (status ${response.status})`, response.status, "Response body is not valid JSON");
2804
+ if (this.onResponse) {
2805
+ try {
2806
+ this.onResponse({
2807
+ status: response.status,
2808
+ headers: Object.fromEntries(response.headers.entries()),
2809
+ body: void 0,
2810
+ durationMs: Date.now() - startTime,
2811
+ timestamp: Date.now(),
2812
+ result: cloneResultForHook(result)
2813
+ });
2814
+ } catch {
2815
+ }
2816
+ }
2817
+ throw new PeppolApiError(`getpeppr API error: unexpected response format (status ${response.status})`, response.status, "Response body is not valid JSON", void 0, result);
2539
2818
  }
2540
2819
  if (this.onResponse) {
2541
2820
  try {
@@ -2554,7 +2833,8 @@ var GetpepprAdapter = class {
2554
2833
  // swallows everything — so the hook fires with a marker instead.
2555
2834
  body: cloneForHook(responseBody),
2556
2835
  durationMs: Date.now() - startTime,
2557
- timestamp: Date.now()
2836
+ timestamp: Date.now(),
2837
+ result: cloneResultForHook(result)
2558
2838
  });
2559
2839
  } catch {
2560
2840
  }
@@ -2568,9 +2848,7 @@ var GetpepprAdapter = class {
2568
2848
  // legacy `_draft` marker, which the current gateway rejects explicitly.
2569
2849
  async sendInvoice(input, options) {
2570
2850
  const headers = {};
2571
- if (options?.idempotencyKey) {
2572
- headers["Idempotency-Key"] = options.idempotencyKey;
2573
- }
2851
+ applyIdempotencyKey(headers, options);
2574
2852
  if (options?.validateRecipient) {
2575
2853
  headers["X-Validate-Recipient"] = options.validateRecipient === true ? "warn" : String(options.validateRecipient);
2576
2854
  }
@@ -2579,17 +2857,17 @@ var GetpepprAdapter = class {
2579
2857
  }
2580
2858
  async createInvoice(input, options) {
2581
2859
  const headers = {};
2582
- if (options?.idempotencyKey) {
2583
- headers["Idempotency-Key"] = options.idempotencyKey;
2584
- }
2860
+ applyIdempotencyKey(headers, options);
2585
2861
  if (options?.validateRecipient) {
2586
2862
  headers["X-Validate-Recipient"] = options.validateRecipient === true ? "warn" : String(options.validateRecipient);
2587
2863
  }
2588
2864
  const result = await this.request("POST", "/invoices", { ...input, _draft: true }, headers);
2589
2865
  return parseSendResult(result);
2590
2866
  }
2591
- async sendInvoiceById(id) {
2592
- await this.request("POST", `/invoices/send/${id}`);
2867
+ async sendInvoiceById(id, options) {
2868
+ const headers = {};
2869
+ applyIdempotencyKey(headers, options);
2870
+ await this.request("POST", `/invoices/send/${id}`, void 0, headers);
2593
2871
  }
2594
2872
  async sendCreditNote(input) {
2595
2873
  const result = await this.request("POST", "/credit-notes", input);
@@ -2695,6 +2973,7 @@ var GetpepprAdapter = class {
2695
2973
  headers: requestHeaders,
2696
2974
  signal: controller.signal
2697
2975
  });
2976
+ const result = parseApiResultHeaders(response.headers);
2698
2977
  if (!response.ok) {
2699
2978
  const errorBody = await response.text().catch(() => "Unknown error");
2700
2979
  const retryAfterMs = response.status === 429 ? parseRetryAfter(response.headers.get("Retry-After")) : void 0;
@@ -2705,12 +2984,13 @@ var GetpepprAdapter = class {
2705
2984
  headers: Object.fromEntries(response.headers.entries()),
2706
2985
  body: errorBody,
2707
2986
  durationMs: Date.now() - startTime,
2708
- timestamp: Date.now()
2987
+ timestamp: Date.now(),
2988
+ result: cloneResultForHook(result)
2709
2989
  });
2710
2990
  } catch {
2711
2991
  }
2712
2992
  }
2713
- throw new PeppolApiError(formatApiErrorMessage(response.status, errorBody), response.status, errorBody, retryAfterMs);
2993
+ throw new PeppolApiError(formatApiErrorMessage(response.status, errorBody), response.status, errorBody, retryAfterMs, result);
2714
2994
  }
2715
2995
  const responseBody = await response.arrayBuffer();
2716
2996
  if (this.onResponse) {
@@ -2720,7 +3000,8 @@ var GetpepprAdapter = class {
2720
3000
  headers: Object.fromEntries(response.headers.entries()),
2721
3001
  body: `[ArrayBuffer: ${responseBody.byteLength} bytes]`,
2722
3002
  durationMs: Date.now() - startTime,
2723
- timestamp: Date.now()
3003
+ timestamp: Date.now(),
3004
+ result: cloneResultForHook(result)
2724
3005
  });
2725
3006
  } catch {
2726
3007
  }
@@ -2771,8 +3052,10 @@ var GetpepprAdapter = class {
2771
3052
  }
2772
3053
  };
2773
3054
  }
2774
- async acknowledgeInvoice(id) {
2775
- const result = await this.request("POST", `/invoices/${id}/ack`);
3055
+ async acknowledgeInvoice(id, options) {
3056
+ const headers = {};
3057
+ applyIdempotencyKey(headers, options);
3058
+ const result = await this.request("POST", `/invoices/${id}/ack`, void 0, headers);
2776
3059
  return parseSendResult(result);
2777
3060
  }
2778
3061
  async updateInvoice(id, input) {
@@ -2823,8 +3106,10 @@ var GetpepprAdapter = class {
2823
3106
  const result = await this.request("GET", `/contacts/${id}`);
2824
3107
  return parseContact(result);
2825
3108
  }
2826
- async createContact(input) {
2827
- const result = await this.request("POST", "/contacts", input);
3109
+ async createContact(input, options) {
3110
+ const headers = {};
3111
+ applyIdempotencyKey(headers, options);
3112
+ const result = await this.request("POST", "/contacts", input, headers);
2828
3113
  return parseContact(result);
2829
3114
  }
2830
3115
  async updateContact(id, input) {
@@ -2836,8 +3121,7 @@ var GetpepprAdapter = class {
2836
3121
  }
2837
3122
  async createLegalEntity(input, options) {
2838
3123
  const headers = {};
2839
- if (options?.idempotencyKey)
2840
- headers["Idempotency-Key"] = options.idempotencyKey;
3124
+ applyIdempotencyKey(headers, options);
2841
3125
  const result = await this.request("POST", "/legal-entities", input, headers);
2842
3126
  return parseLegalEntity(result);
2843
3127
  }
@@ -2876,8 +3160,7 @@ var GetpepprAdapter = class {
2876
3160
  }
2877
3161
  async requestLegalEntityAttestation(id, input, options) {
2878
3162
  const headers = {};
2879
- if (options?.idempotencyKey)
2880
- headers["Idempotency-Key"] = options.idempotencyKey;
3163
+ applyIdempotencyKey(headers, options);
2881
3164
  const result = await this.request("POST", `/legal-entities/${id}/attestation`, input, headers);
2882
3165
  return {
2883
3166
  id: String(result.id ?? id),
@@ -2915,8 +3198,10 @@ var GetpepprAdapter = class {
2915
3198
  const result = await this.request("GET", `/bank-accounts/${id}`);
2916
3199
  return parseBankAccount(result);
2917
3200
  }
2918
- async createBankAccount(input) {
2919
- const result = await this.request("POST", "/bank-accounts", input);
3201
+ async createBankAccount(input, options) {
3202
+ const headers = {};
3203
+ applyIdempotencyKey(headers, options);
3204
+ const result = await this.request("POST", "/bank-accounts", input, headers);
2920
3205
  return parseBankAccount(result);
2921
3206
  }
2922
3207
  async updateBankAccount(id, input) {
@@ -2953,7 +3238,9 @@ var GetpepprAdapter = class {
2953
3238
  // tout en salissant le corps des envois standards.
2954
3239
  ...options.sender ? { sender: options.sender } : {}
2955
3240
  };
2956
- const result = await this.request("POST", "/invoices/import", body);
3241
+ const headers = {};
3242
+ applyIdempotencyKey(headers, options);
3243
+ const result = await this.request("POST", "/invoices/import", body, headers);
2957
3244
  return parseSendResult(result);
2958
3245
  }
2959
3246
  async listTransportTypes() {
@@ -3007,6 +3294,9 @@ function detectMimeType(filename) {
3007
3294
  }
3008
3295
  }
3009
3296
  var PROTOCOL_ERROR_BODY_LIMIT = 2e3;
3297
+ function cloneResultForHook(result) {
3298
+ return result === void 0 ? void 0 : { ...result };
3299
+ }
3010
3300
  function cloneForHook(body) {
3011
3301
  try {
3012
3302
  return structuredClone(body);
@@ -3209,12 +3499,24 @@ function parseLegalEntity(raw) {
3209
3499
  country: raw.country != null ? String(raw.country) : null,
3210
3500
  identifier: idObj && idObj.scheme != null && idObj.value != null ? { scheme: String(idObj.scheme), value: String(idObj.value) } : null,
3211
3501
  status: String(raw.status ?? "pending"),
3502
+ networkDiscovery: raw.networkDiscovery && typeof raw.networkDiscovery === "object" ? raw.networkDiscovery : { state: "pending", attempts: 0 },
3212
3503
  environment: String(raw.environment ?? ""),
3213
3504
  createdAt: String(raw.createdAt ?? "")
3214
3505
  };
3215
3506
  if (raw.verificationDetail != null) {
3216
3507
  le.verificationDetail = raw.verificationDetail;
3217
3508
  }
3509
+ if (raw.registrationDetail && typeof raw.registrationDetail === "object") {
3510
+ const reason = raw.registrationDetail.reason;
3511
+ const safeReasons = [
3512
+ "already_registered",
3513
+ "invalid_format",
3514
+ "provider_error"
3515
+ ];
3516
+ le.registrationDetail = {
3517
+ reason: safeReasons.includes(reason) ? reason : "provider_error"
3518
+ };
3519
+ }
3218
3520
  return le;
3219
3521
  }
3220
3522
  function parseAccountIdentity(raw) {
@@ -3437,14 +3739,95 @@ var PeppolProtocolError = class extends PeppolError {
3437
3739
  var PeppolApiError = class extends PeppolError {
3438
3740
  statusCode;
3439
3741
  responseBody;
3440
- /** Parsed Retry-After delay in milliseconds (present on 429 responses) */
3742
+ /**
3743
+ * Parsed `Retry-After` delay in milliseconds.
3744
+ *
3745
+ * `undefined` unless this response is a **429** AND carried a readable
3746
+ * `Retry-After`. No other status reads that header, whatever its remediation
3747
+ * says — measured, all 22 `retry_after` entries in the catalogue are 429s.
3748
+ * The gateway does not attach the header to every throttled answer either.
3749
+ */
3441
3750
  retryAfterMs;
3442
- constructor(message, statusCode, responseBody, retryAfterMs) {
3751
+ /**
3752
+ * The canonical result the gateway declared for this response, read from its
3753
+ * six headers — no body parsing required.
3754
+ *
3755
+ * `undefined` against a gateway that has not activated the result catalogue,
3756
+ * and behind any hop that strips unknown headers. The flattened accessors
3757
+ * below all read from here, so they are `undefined` together.
3758
+ */
3759
+ result;
3760
+ constructor(message, statusCode, responseBody, retryAfterMs, result) {
3443
3761
  super(message);
3444
3762
  this.statusCode = statusCode;
3445
3763
  this.responseBody = responseBody;
3446
3764
  this.name = "PeppolApiError";
3447
3765
  this.retryAfterMs = retryAfterMs;
3766
+ this.result = result;
3767
+ }
3768
+ /**
3769
+ * Stable getpeppr result code for this failure (e.g. `"auth.api_key_invalid"`).
3770
+ *
3771
+ * ⛔ NOT the same field as {@link code}, and they can both be present with
3772
+ * different values: this one is the catalogue's global code, `code` is the
3773
+ * route's own sub-reason from the body.
3774
+ *
3775
+ * `undefined` when the gateway sent no result headers.
3776
+ */
3777
+ get resultCode() {
3778
+ return this.result?.code;
3779
+ }
3780
+ /**
3781
+ * The catalogue's sentence for {@link resultCode}.
3782
+ *
3783
+ * ⚠️ Usually SHORTER on detail than `.message`, which is built from the
3784
+ * response body and can name the offending field or rule. Show `.message` to
3785
+ * a human; use this one when you want the stable phrasing.
3786
+ *
3787
+ * `undefined` when the gateway sent no result headers.
3788
+ */
3789
+ get resultMessage() {
3790
+ return this.result?.message;
3791
+ }
3792
+ /**
3793
+ * Server-generated correlation id for this exact request. Quote it to support.
3794
+ *
3795
+ * `undefined` when the gateway sent no result headers — which includes every
3796
+ * response from a deployment predating the catalogue.
3797
+ */
3798
+ get requestId() {
3799
+ return this.result?.requestId;
3800
+ }
3801
+ /**
3802
+ * Whether retrying this same request can succeed, per the catalogue.
3803
+ *
3804
+ * ⚠️ `undefined` means "the gateway did not say", NOT "no" — the SDK then
3805
+ * falls back to its historic status policy. A `false` here is an explicit
3806
+ * refusal and the SDK will not retry, whatever the status.
3807
+ */
3808
+ get retryable() {
3809
+ return this.result?.retryable;
3810
+ }
3811
+ /**
3812
+ * What to do about it: `"none"`, `"fix_request"`, `"authenticate"`,
3813
+ * `"retry"`, `"retry_after"`, `"wait"` or `"contact_support"` today.
3814
+ *
3815
+ * Typed open — a value added server-side reaches you rather than vanishing.
3816
+ * `undefined` when the gateway sent no result headers.
3817
+ */
3818
+ get remediation() {
3819
+ return this.result?.remediation;
3820
+ }
3821
+ /**
3822
+ * Documentation link for {@link resultCode}, when the catalogue provides one.
3823
+ *
3824
+ * `undefined` when the gateway sent no result headers, when the catalogue
3825
+ * entry has no docs link, or when the value was not a plain `https://` URL
3826
+ * (`http:`, credentials in the authority, and anything the URL parser would
3827
+ * have to repair are all refused).
3828
+ */
3829
+ get docs() {
3830
+ return this.result?.docs;
3448
3831
  }
3449
3832
  /**
3450
3833
  * The gateway's machine-readable error code, parsed from the JSON response body
@@ -3617,8 +4000,8 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
3617
4000
  * always returns 501. Submit the final document with `invoices.send()`.
3618
4001
  * @throws {PeppolApiError} 501 with the current gateway provider
3619
4002
  */
3620
- async sendById(id) {
3621
- return this.adapter.sendInvoiceById(id);
4003
+ async sendById(id, options) {
4004
+ return this.adapter.sendInvoiceById(id, options);
3622
4005
  }
3623
4006
  /**
3624
4007
  * Send an invoice via Peppol.
@@ -3792,8 +4175,8 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
3792
4175
  * acknowledgement and always returns 501.
3793
4176
  * @throws {PeppolApiError} 501 with the current gateway provider
3794
4177
  */
3795
- async acknowledge(id) {
3796
- return this.adapter.acknowledgeInvoice(id);
4178
+ async acknowledge(id, options) {
4179
+ return this.adapter.acknowledgeInvoice(id, options);
3797
4180
  }
3798
4181
  /**
3799
4182
  * Request an update to an existing invoice.
@@ -4083,8 +4466,8 @@ var ContactOperations = class {
4083
4466
  * });
4084
4467
  * ```
4085
4468
  */
4086
- async create(input) {
4087
- return this.adapter.createContact(input);
4469
+ async create(input, options) {
4470
+ return this.adapter.createContact(input, options);
4088
4471
  }
4089
4472
  /**
4090
4473
  * Update an existing contact.
@@ -4309,8 +4692,8 @@ var BankAccountOperations = class {
4309
4692
  * });
4310
4693
  * ```
4311
4694
  */
4312
- async create(input) {
4313
- return this.adapter.createBankAccount(input);
4695
+ async create(input, options) {
4696
+ return this.adapter.createBankAccount(input, options);
4314
4697
  }
4315
4698
  /**
4316
4699
  * Update an existing bank account.
@@ -5261,6 +5644,12 @@ async function pollUntilTerminal(client, documentId, options = {}) {
5261
5644
  return { finalStatus: lastStatus, timedOut: true };
5262
5645
  }
5263
5646
 
5647
+ // src/lib/dashboard-url.ts
5648
+ var DASHBOARD_INVOICES_BASE = "https://console.getpeppr.dev/invoices";
5649
+ function dashboardUrlForSendResult(result) {
5650
+ return `${DASHBOARD_INVOICES_BASE}/${result.id}`;
5651
+ }
5652
+
5264
5653
  // src/formatters/send-result.ts
5265
5654
  import pc5 from "picocolors";
5266
5655
  function formatSendResult(result, mode) {
@@ -5286,7 +5675,6 @@ function formatSendResult(result, mode) {
5286
5675
  // src/commands/send.ts
5287
5676
  var API_BASE = "https://api.getpeppr.dev/v1";
5288
5677
  var LOCAL_BASE = "http://localhost:3001/api/v1";
5289
- var DASHBOARD_BASE = "https://console.getpeppr.dev/invoices";
5290
5678
  function registerSendCommand(program2) {
5291
5679
  program2.command("send").description("Send an invoice to the Peppol network via getpeppr API").argument("[file]", "optional path to invoice JSON (mutex with --to/--amount/...)").option("--prod", "target production (live keys + confirmation)").option("--local", "target localhost:3001 dev server").option("--key <key>", "override API key \u2014 for CI/scripted use only; visible in `ps` and shell history. Prefer GETPEPPR_API_KEY env var.").option("--to <peppol-id>", "recipient peppol id (e.g., 9925:BE0314595348)").option("--country <iso>", "recipient ISO 3166-1 alpha-2 country override (e.g., BE)").option("--amount <number>", "line amount in major currency units (decimal allowed)").option("--currency <iso>", "ISO 4217 currency (default EUR)").option("--desc <text>", "line description").option("--attachment", "attach the test PDF").option("--watch", "poll status until a terminal state (60s timeout)").option("-y, --yes", "skip --prod confirmation prompt").option("--no-validate", "skip pre-validation locally").option("--json", "output JSON").option("--quiet", "exit code only, no output").action(async (file, flags) => {
5292
5680
  let auth;
@@ -5390,7 +5778,7 @@ function registerSendCommand(program2) {
5390
5778
  `);
5391
5779
  process.exit(1);
5392
5780
  }
5393
- const dashboardUrl = `${DASHBOARD_BASE}/${result.id}`;
5781
+ const dashboardUrl = dashboardUrlForSendResult(result);
5394
5782
  let finalStatus = result.status;
5395
5783
  let timedOut = false;
5396
5784
  let watchFailed = false;