@getpeppr/cli 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -735,6 +735,129 @@ function validateCountryRules(input) {
735
735
  return { errors, warnings };
736
736
  }
737
737
 
738
+ // ../sdk/dist/core/code-lists.js
739
+ var CURRENCIES = /* @__PURE__ */ new Map([
740
+ ["EUR", { code: "EUR", name: "Euro", minorUnits: 2 }],
741
+ ["USD", { code: "USD", name: "US Dollar", minorUnits: 2 }],
742
+ ["GBP", { code: "GBP", name: "Pound Sterling", minorUnits: 2 }],
743
+ ["CHF", { code: "CHF", name: "Swiss Franc", minorUnits: 2 }],
744
+ ["DKK", { code: "DKK", name: "Danish Krone", minorUnits: 2 }],
745
+ ["NOK", { code: "NOK", name: "Norwegian Krone", minorUnits: 2 }],
746
+ ["SEK", { code: "SEK", name: "Swedish Krona", minorUnits: 2 }],
747
+ ["PLN", { code: "PLN", name: "Polish Zloty", minorUnits: 2 }],
748
+ ["CZK", { code: "CZK", name: "Czech Koruna", minorUnits: 2 }],
749
+ ["HUF", { code: "HUF", name: "Hungarian Forint", minorUnits: 2 }],
750
+ ["RON", { code: "RON", name: "Romanian Leu", minorUnits: 2 }],
751
+ ["BGN", { code: "BGN", name: "Bulgarian Lev", minorUnits: 2 }],
752
+ ["HRK", { code: "HRK", name: "Croatian Kuna", minorUnits: 2 }],
753
+ ["ISK", { code: "ISK", name: "Icelandic Krona", minorUnits: 0 }],
754
+ ["TRY", { code: "TRY", name: "Turkish Lira", minorUnits: 2 }],
755
+ ["JPY", { code: "JPY", name: "Japanese Yen", minorUnits: 0 }],
756
+ ["CNY", { code: "CNY", name: "Chinese Yuan", minorUnits: 2 }],
757
+ ["KRW", { code: "KRW", name: "South Korean Won", minorUnits: 0 }],
758
+ ["INR", { code: "INR", name: "Indian Rupee", minorUnits: 2 }],
759
+ ["SGD", { code: "SGD", name: "Singapore Dollar", minorUnits: 2 }],
760
+ ["AUD", { code: "AUD", name: "Australian Dollar", minorUnits: 2 }],
761
+ ["NZD", { code: "NZD", name: "New Zealand Dollar", minorUnits: 2 }],
762
+ ["CAD", { code: "CAD", name: "Canadian Dollar", minorUnits: 2 }],
763
+ ["BRL", { code: "BRL", name: "Brazilian Real", minorUnits: 2 }],
764
+ ["MXN", { code: "MXN", name: "Mexican Peso", minorUnits: 2 }],
765
+ ["ZAR", { code: "ZAR", name: "South African Rand", minorUnits: 2 }],
766
+ ["AED", { code: "AED", name: "UAE Dirham", minorUnits: 2 }],
767
+ ["SAR", { code: "SAR", name: "Saudi Riyal", minorUnits: 2 }],
768
+ ["ILS", { code: "ILS", name: "Israeli Shekel", minorUnits: 2 }],
769
+ ["HKD", { code: "HKD", name: "Hong Kong Dollar", minorUnits: 2 }],
770
+ ["TWD", { code: "TWD", name: "Taiwan Dollar", minorUnits: 2 }]
771
+ ]);
772
+ function getCurrency(code) {
773
+ return CURRENCIES.get(code.toUpperCase());
774
+ }
775
+ var EAS_SCHEMES = [
776
+ { code: "0002", name: "System Information et Repertoire des Entreprises et des Etablissements (SIRENE)", country: "FR" },
777
+ { code: "0007", name: "Organisationsnummer", country: "SE" },
778
+ { code: "0009", name: "SIRET-CODE", country: "FR" },
779
+ { code: "0088", name: "EAN Location Code (GLN)" },
780
+ { code: "0096", name: "Danish Chamber of Commerce (P-nummer)", country: "DK" },
781
+ { code: "0184", name: "Danish Central Business Register (CVR)", country: "DK" },
782
+ { code: "0190", name: "Dutch Chamber of Commerce (KVK)", country: "NL" },
783
+ { code: "0191", name: "Organisatie Identificatie Nummer (OIN)", country: "NL" },
784
+ { code: "0192", name: "Danish SE-number (Erhvervsstyrelsen)", country: "DK" },
785
+ { code: "0195", name: "Singapore Unique Entity Number (UEN)", country: "SG" },
786
+ { code: "0196", name: "Icelandic Kennitala", country: "IS" },
787
+ { code: "0198", name: "Danish ERST id (Erhvervsstyrelsen)", country: "DK" },
788
+ { code: "0200", name: "Lithuanian Legal Entity Register (GRIS)", country: "LT" },
789
+ { code: "0201", name: "Italian Codice Destinatario", country: "IT" },
790
+ { code: "0202", name: "Italian Fiscal Code (Codice Fiscale)", country: "IT" },
791
+ { code: "0204", name: "German Leitweg-ID", country: "DE" },
792
+ { code: "0208", name: "Belgian Enterprise Number (KBO/BCE)", country: "BE" },
793
+ { code: "0209", name: "German Creditor Identifier (GS1)", country: "DE" },
794
+ { code: "0210", name: "Italian Codice Fiscale (per IPA)", country: "IT" },
795
+ { code: "0211", name: "Italian Partita IVA (VAT number)", country: "IT" },
796
+ { code: "0212", name: "Finnish OVT code", country: "FI" },
797
+ { code: "0213", name: "Finnish OP identifier", country: "FI" }
798
+ ];
799
+ var EAS_BY_CODE = new Map(EAS_SCHEMES.map((s) => [s.code, s]));
800
+ var UNIT_ALIAS_MAP = {
801
+ each: "EA",
802
+ piece: "EA",
803
+ pieces: "EA",
804
+ hour: "HUR",
805
+ hours: "HUR",
806
+ day: "DAY",
807
+ days: "DAY",
808
+ week: "WEE",
809
+ weeks: "WEE",
810
+ month: "MON",
811
+ months: "MON",
812
+ year: "ANN",
813
+ years: "ANN",
814
+ kilogram: "KGM",
815
+ kg: "KGM",
816
+ meter: "MTR",
817
+ metre: "MTR",
818
+ liter: "LTR",
819
+ litre: "LTR",
820
+ unit: "C62",
821
+ units: "C62",
822
+ set: "SET",
823
+ sets: "SET",
824
+ pack: "PK",
825
+ packs: "PK",
826
+ minute: "MIN",
827
+ minutes: "MIN",
828
+ second: "SEC",
829
+ seconds: "SEC",
830
+ tonne: "TNE",
831
+ ton: "TNE",
832
+ "square metre": "MTK",
833
+ "square meter": "MTK",
834
+ sqm: "MTK"
835
+ };
836
+ var UNIT_CODES = /* @__PURE__ */ new Map([
837
+ ["EA", "Each"],
838
+ ["HUR", "Hour"],
839
+ ["DAY", "Day"],
840
+ ["WEE", "Week"],
841
+ ["MON", "Month"],
842
+ ["ANN", "Year"],
843
+ ["MIN", "Minute"],
844
+ ["SEC", "Second"],
845
+ ["KGM", "Kilogram"],
846
+ ["MTR", "Metre"],
847
+ ["LTR", "Litre"],
848
+ ["MTK", "Square metre"],
849
+ ["TNE", "Tonne"],
850
+ ["C62", "One (unit)"],
851
+ ["SET", "Set"],
852
+ ["PK", "Pack"]
853
+ ]);
854
+ function resolveUnit(input) {
855
+ return UNIT_ALIAS_MAP[input.toLowerCase()] ?? input;
856
+ }
857
+ function getAllUnits() {
858
+ return Array.from(UNIT_CODES.entries()).map(([code, name]) => ({ code, name })).sort((a, b) => a.code.localeCompare(b.code));
859
+ }
860
+
738
861
  // ../sdk/dist/core/validator.js
739
862
  function error(field, message, ruleId, suggestion) {
740
863
  return { field, message, ruleId, suggestion };
@@ -742,19 +865,31 @@ function error(field, message, ruleId, suggestion) {
742
865
  function warning(field, message, ruleId) {
743
866
  return { field, message, ruleId };
744
867
  }
868
+ function assertString(value, fieldPath, errors) {
869
+ if (typeof value !== "string") {
870
+ errors.push(error(fieldPath, `Expected string, received ${value === null ? "null" : typeof value}`, void 0, "Check your payload \u2014 this field must be a text value"));
871
+ return false;
872
+ }
873
+ return true;
874
+ }
745
875
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
746
876
  function validateParty(party, path) {
747
877
  const errors = [];
748
- if (!party.name?.trim()) {
878
+ if (party.name === void 0 || party.name === null || party.name === "") {
879
+ errors.push(error(`${path}.name`, "Business name is required", "BR-06"));
880
+ } else if (!assertString(party.name, `${path}.name`, errors)) {
881
+ } else if (!party.name.trim()) {
749
882
  errors.push(error(`${path}.name`, "Business name is required", "BR-06"));
750
883
  }
751
- if (!party.peppolId) {
884
+ if (party.peppolId === void 0 || party.peppolId === null || party.peppolId === "") {
752
885
  errors.push(error(`${path}.peppolId`, "Peppol participant ID is required", void 0, 'Format: "scheme:id", e.g. "0208:BE0123456789" for Belgian companies'));
886
+ } else if (!assertString(party.peppolId, `${path}.peppolId`, errors)) {
753
887
  } else if (!party.peppolId.includes(":")) {
754
888
  errors.push(error(`${path}.peppolId`, `Invalid Peppol ID format: "${party.peppolId}"`, void 0, 'Must be "scheme:id" format. Common schemes: 0208 (Belgium), 0009 (France SIRET), 0204 (Germany Leitweg)'));
755
889
  }
756
- if (!party.country) {
890
+ if (party.country === void 0 || party.country === null || party.country === "") {
757
891
  errors.push(error(`${path}.country`, "Country code is required", "BR-11"));
892
+ } else if (!assertString(party.country, `${path}.country`, errors)) {
758
893
  } else if (party.country.length !== 2) {
759
894
  errors.push(error(`${path}.country`, `Invalid country code: "${party.country}"`, void 0, "Must be ISO 3166-1 alpha-2 (e.g., BE, FR, DE, NL)"));
760
895
  }
@@ -762,13 +897,22 @@ function validateParty(party, path) {
762
897
  }
763
898
  function validateBuyerAddress(party, path) {
764
899
  const errors = [];
765
- if (!party.street?.trim()) {
900
+ if (party.street === void 0 || party.street === null || party.street === "") {
901
+ errors.push(error(`${path}.street`, "Street address is required for the buyer", "BR-50", 'e.g. "123 Business Street"'));
902
+ } else if (!assertString(party.street, `${path}.street`, errors)) {
903
+ } else if (!party.street.trim()) {
766
904
  errors.push(error(`${path}.street`, "Street address is required for the buyer", "BR-50", 'e.g. "123 Business Street"'));
767
905
  }
768
- if (!party.city?.trim()) {
906
+ if (party.city === void 0 || party.city === null || party.city === "") {
907
+ errors.push(error(`${path}.city`, "City is required for the buyer", "BR-51", 'e.g. "Brussels"'));
908
+ } else if (!assertString(party.city, `${path}.city`, errors)) {
909
+ } else if (!party.city.trim()) {
769
910
  errors.push(error(`${path}.city`, "City is required for the buyer", "BR-51", 'e.g. "Brussels"'));
770
911
  }
771
- if (!party.postalCode?.trim()) {
912
+ if (party.postalCode === void 0 || party.postalCode === null || party.postalCode === "") {
913
+ errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", "BR-53", 'e.g. "1000"'));
914
+ } else if (!assertString(party.postalCode, `${path}.postalCode`, errors)) {
915
+ } else if (!party.postalCode.trim()) {
772
916
  errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", "BR-53", 'e.g. "1000"'));
773
917
  }
774
918
  return errors;
@@ -776,7 +920,10 @@ function validateBuyerAddress(party, path) {
776
920
  function validateLine(line, index, isCreditNote = false) {
777
921
  const errors = [];
778
922
  const path = `lines[${index}]`;
779
- if (!line.description?.trim()) {
923
+ if (line.description === void 0 || line.description === null || line.description === "") {
924
+ errors.push(error(`${path}.description`, "Line item description is required", "BR-25"));
925
+ } else if (!assertString(line.description, `${path}.description`, errors)) {
926
+ } else if (!line.description.trim()) {
780
927
  errors.push(error(`${path}.description`, "Line item description is required", "BR-25"));
781
928
  }
782
929
  if (line.quantity === void 0 || line.quantity === null) {
@@ -799,15 +946,24 @@ function validateLine(line, index, isCreditNote = false) {
799
946
  function validateInvoice(input) {
800
947
  const errors = [];
801
948
  const warnings = [];
802
- if (!input.number?.trim()) {
949
+ if (input.number === void 0 || input.number === null || input.number === "") {
950
+ errors.push(error("number", "Invoice number is required", "BR-02", "Must be unique per supplier"));
951
+ } else if (!assertString(input.number, "number", errors)) {
952
+ } else if (!input.number.trim()) {
803
953
  errors.push(error("number", "Invoice number is required", "BR-02", "Must be unique per supplier"));
804
954
  }
805
955
  const VALID_TYPE_CODES = [380, 381, 383, 384, 386, 389, 751];
806
956
  if (input.invoiceTypeCode != null && !VALID_TYPE_CODES.includes(input.invoiceTypeCode)) {
807
957
  errors.push(error("invoiceTypeCode", `Invalid invoice type code: ${input.invoiceTypeCode}`, void 0, "Valid codes: 380, 381, 383, 384, 386, 389, 751"));
808
958
  }
809
- if (input.isCreditNote && !input.invoiceReference?.trim()) {
810
- errors.push(error("invoiceReference", "Reference to the original invoice is required for credit notes", void 0, 'Set invoiceReference to the original invoice number (e.g., "INV-001")'));
959
+ if (input.isCreditNote) {
960
+ const ref = input.invoiceReference;
961
+ if (ref === void 0 || ref === null || ref === "") {
962
+ errors.push(error("invoiceReference", "Reference to the original invoice is required for credit notes", void 0, 'Set invoiceReference to the original invoice number (e.g., "INV-001")'));
963
+ } else if (!assertString(ref, "invoiceReference", errors)) {
964
+ } else if (!ref.trim()) {
965
+ errors.push(error("invoiceReference", "Reference to the original invoice is required for credit notes", void 0, 'Set invoiceReference to the original invoice number (e.g., "INV-001")'));
966
+ }
811
967
  }
812
968
  if (input.from) {
813
969
  warnings.push(warning("from", "Seller info is determined by your API key. The 'from' field is deprecated and ignored."));
@@ -819,13 +975,19 @@ function validateInvoice(input) {
819
975
  errors.push(...validateBuyerAddress(input.to, "to"));
820
976
  }
821
977
  if (input.payeeParty) {
822
- if (!input.payeeParty.name?.trim()) {
978
+ const ppName = input.payeeParty.name;
979
+ if (ppName === void 0 || ppName === null || ppName === "") {
980
+ errors.push(error("payeeParty.name", "Payee party name is required", "BR-17"));
981
+ } else if (!assertString(ppName, "payeeParty.name", errors)) {
982
+ } else if (!ppName.trim()) {
823
983
  errors.push(error("payeeParty.name", "Payee party name is required", "BR-17"));
824
984
  }
825
- if (!input.payeeParty.peppolId) {
985
+ const ppId = input.payeeParty.peppolId;
986
+ if (ppId === void 0 || ppId === null || ppId === "") {
826
987
  errors.push(error("payeeParty.peppolId", "Payee party Peppol ID is required", void 0, 'Format: "scheme:id", e.g. "0208:BE0123456789"'));
827
- } else if (!input.payeeParty.peppolId.includes(":")) {
828
- errors.push(error("payeeParty.peppolId", `Invalid Peppol ID format: "${input.payeeParty.peppolId}"`, void 0, 'Must be "scheme:id" format'));
988
+ } else if (!assertString(ppId, "payeeParty.peppolId", errors)) {
989
+ } else if (!ppId.includes(":")) {
990
+ errors.push(error("payeeParty.peppolId", `Invalid Peppol ID format: "${ppId}"`, void 0, 'Must be "scheme:id" format'));
829
991
  }
830
992
  }
831
993
  if (!input.lines || input.lines.length === 0) {
@@ -867,6 +1029,12 @@ function validateInvoice(input) {
867
1029
  if (input.taxCurrencyRate !== void 0 && input.taxCurrencyRate <= 0) {
868
1030
  errors.push(error("taxCurrencyRate", `Tax currency rate must be positive, got ${input.taxCurrencyRate}`, void 0, "Set to the exchange rate from document currency to tax currency"));
869
1031
  }
1032
+ if (input.currency && !getCurrency(input.currency)) {
1033
+ errors.push(error("currency", `Invalid currency code: "${input.currency}"`, void 0, 'Use ISO 4217 (e.g., "EUR", "USD", "GBP", "JPY"). See https://getpeppr.dev/docs/types/#currency'));
1034
+ }
1035
+ if (input.taxCurrency && !getCurrency(input.taxCurrency)) {
1036
+ errors.push(error("taxCurrency", `Invalid tax currency code: "${input.taxCurrency}"`, void 0, 'Use ISO 4217 (e.g., "EUR", "USD")'));
1037
+ }
870
1038
  if (!input.dueDate) {
871
1039
  warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms.", "BR-09"));
872
1040
  }
@@ -889,9 +1057,19 @@ function validateInvoice(input) {
889
1057
  }
890
1058
 
891
1059
  // ../sdk/dist/version.js
892
- var SDK_VERSION = "1.3.0";
1060
+ var SDK_VERSION = "1.4.0";
893
1061
 
894
1062
  // ../sdk/dist/core/client.js
1063
+ function findHeaderCaseInsensitive(headers, name) {
1064
+ if (!headers)
1065
+ return void 0;
1066
+ const target = name.toLowerCase();
1067
+ for (const [key, value] of Object.entries(headers)) {
1068
+ if (key.toLowerCase() === target)
1069
+ return value;
1070
+ }
1071
+ return void 0;
1072
+ }
895
1073
  var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
896
1074
  function sleep(ms) {
897
1075
  return new Promise((resolve4) => setTimeout(resolve4, ms));
@@ -960,7 +1138,7 @@ var GetpepprAdapter = class {
960
1138
  lastError = err;
961
1139
  const is429 = err instanceof PeppolApiError && err.statusCode === 429;
962
1140
  const isSafeMethod = /^(GET|DELETE|HEAD)$/i.test(method);
963
- const hasIdempotencyKey = !!extraHeaders?.["Idempotency-Key"];
1141
+ const hasIdempotencyKey = !!findHeaderCaseInsensitive(extraHeaders, "Idempotency-Key");
964
1142
  const canRetry = is429 || isSafeMethod || hasIdempotencyKey;
965
1143
  if (attempt < maxRetries && canRetry && isRetryableError(err)) {
966
1144
  const retryAfterMs = err instanceof PeppolApiError ? err.retryAfterMs : void 0;
@@ -1493,11 +1671,16 @@ var VALID_STATUSES = /* @__PURE__ */ new Set([
1493
1671
  "under_query",
1494
1672
  "conditionally_accepted",
1495
1673
  "partially_paid",
1496
- "no_action"
1674
+ "no_action",
1675
+ "unknown"
1676
+ // explicitly known: gateway may emit this when it can't map a Storecove status
1497
1677
  ]);
1498
1678
  function mapStatus(raw) {
1499
1679
  const s = raw.toLowerCase();
1500
- return VALID_STATUSES.has(s) ? s : "unknown";
1680
+ if (VALID_STATUSES.has(s))
1681
+ return s;
1682
+ console.warn(`[getpeppr] Unknown gateway status received: "${raw}" \u2014 please report to support@getpeppr.dev. Coercing to "unknown".`);
1683
+ return "unknown";
1501
1684
  }
1502
1685
  var PeppolError = class extends Error {
1503
1686
  constructor(message) {
@@ -1575,12 +1758,14 @@ async function* paginate(fetchPage, options) {
1575
1758
  let offset = 0;
1576
1759
  while (true) {
1577
1760
  const page = await fetchPage(offset, pageSize);
1761
+ if (page.data.length === 0)
1762
+ break;
1578
1763
  for (const item of page.data) {
1579
1764
  yield item;
1580
1765
  }
1581
1766
  if (!page.meta.hasMore)
1582
1767
  break;
1583
- offset += pageSize;
1768
+ offset += page.data.length;
1584
1769
  }
1585
1770
  }
1586
1771
  var InvoiceOperations = class {
@@ -2062,7 +2247,7 @@ var ContactOperations = class {
2062
2247
  * ```
2063
2248
  */
2064
2249
  listAll(options) {
2065
- return paginate((offset, limit) => this.adapter.listContacts({ ...options, offset, limit }));
2250
+ return paginate((offset, limit) => this.adapter.listContacts({ ...options, offset, limit }), options);
2066
2251
  }
2067
2252
  };
2068
2253
  var BankAccountOperations = class {
@@ -2143,7 +2328,7 @@ var BankAccountOperations = class {
2143
2328
  * ```
2144
2329
  */
2145
2330
  listAll(options) {
2146
- return paginate((offset, limit) => this.adapter.listBankAccounts({ ...options, offset, limit }));
2331
+ return paginate((offset, limit) => this.adapter.listBankAccounts({ ...options, offset, limit }), options);
2147
2332
  }
2148
2333
  };
2149
2334
  var TransportOperations = class {
@@ -2225,93 +2410,6 @@ var TransportOperations = class {
2225
2410
  }
2226
2411
  };
2227
2412
 
2228
- // ../sdk/dist/core/code-lists.js
2229
- var EAS_SCHEMES = [
2230
- { code: "0002", name: "System Information et Repertoire des Entreprises et des Etablissements (SIRENE)", country: "FR" },
2231
- { code: "0007", name: "Organisationsnummer", country: "SE" },
2232
- { code: "0009", name: "SIRET-CODE", country: "FR" },
2233
- { code: "0088", name: "EAN Location Code (GLN)" },
2234
- { code: "0096", name: "Danish Chamber of Commerce (P-nummer)", country: "DK" },
2235
- { code: "0184", name: "Danish Central Business Register (CVR)", country: "DK" },
2236
- { code: "0190", name: "Dutch Chamber of Commerce (KVK)", country: "NL" },
2237
- { code: "0191", name: "Organisatie Identificatie Nummer (OIN)", country: "NL" },
2238
- { code: "0192", name: "Danish SE-number (Erhvervsstyrelsen)", country: "DK" },
2239
- { code: "0195", name: "Singapore Unique Entity Number (UEN)", country: "SG" },
2240
- { code: "0196", name: "Icelandic Kennitala", country: "IS" },
2241
- { code: "0198", name: "Danish ERST id (Erhvervsstyrelsen)", country: "DK" },
2242
- { code: "0200", name: "Lithuanian Legal Entity Register (GRIS)", country: "LT" },
2243
- { code: "0201", name: "Italian Codice Destinatario", country: "IT" },
2244
- { code: "0202", name: "Italian Fiscal Code (Codice Fiscale)", country: "IT" },
2245
- { code: "0204", name: "German Leitweg-ID", country: "DE" },
2246
- { code: "0208", name: "Belgian Enterprise Number (KBO/BCE)", country: "BE" },
2247
- { code: "0209", name: "German Creditor Identifier (GS1)", country: "DE" },
2248
- { code: "0210", name: "Italian Codice Fiscale (per IPA)", country: "IT" },
2249
- { code: "0211", name: "Italian Partita IVA (VAT number)", country: "IT" },
2250
- { code: "0212", name: "Finnish OVT code", country: "FI" },
2251
- { code: "0213", name: "Finnish OP identifier", country: "FI" }
2252
- ];
2253
- var EAS_BY_CODE = new Map(EAS_SCHEMES.map((s) => [s.code, s]));
2254
- var UNIT_ALIAS_MAP = {
2255
- each: "EA",
2256
- piece: "EA",
2257
- pieces: "EA",
2258
- hour: "HUR",
2259
- hours: "HUR",
2260
- day: "DAY",
2261
- days: "DAY",
2262
- week: "WEE",
2263
- weeks: "WEE",
2264
- month: "MON",
2265
- months: "MON",
2266
- year: "ANN",
2267
- years: "ANN",
2268
- kilogram: "KGM",
2269
- kg: "KGM",
2270
- meter: "MTR",
2271
- metre: "MTR",
2272
- liter: "LTR",
2273
- litre: "LTR",
2274
- unit: "C62",
2275
- units: "C62",
2276
- set: "SET",
2277
- sets: "SET",
2278
- pack: "PK",
2279
- packs: "PK",
2280
- minute: "MIN",
2281
- minutes: "MIN",
2282
- second: "SEC",
2283
- seconds: "SEC",
2284
- tonne: "TNE",
2285
- ton: "TNE",
2286
- "square metre": "MTK",
2287
- "square meter": "MTK",
2288
- sqm: "MTK"
2289
- };
2290
- var UNIT_CODES = /* @__PURE__ */ new Map([
2291
- ["EA", "Each"],
2292
- ["HUR", "Hour"],
2293
- ["DAY", "Day"],
2294
- ["WEE", "Week"],
2295
- ["MON", "Month"],
2296
- ["ANN", "Year"],
2297
- ["MIN", "Minute"],
2298
- ["SEC", "Second"],
2299
- ["KGM", "Kilogram"],
2300
- ["MTR", "Metre"],
2301
- ["LTR", "Litre"],
2302
- ["MTK", "Square metre"],
2303
- ["TNE", "Tonne"],
2304
- ["C62", "One (unit)"],
2305
- ["SET", "Set"],
2306
- ["PK", "Pack"]
2307
- ]);
2308
- function resolveUnit(input) {
2309
- return UNIT_ALIAS_MAP[input.toLowerCase()] ?? input;
2310
- }
2311
- function getAllUnits() {
2312
- return Array.from(UNIT_CODES.entries()).map(([code, name]) => ({ code, name })).sort((a, b) => a.code.localeCompare(b.code));
2313
- }
2314
-
2315
2413
  // ../sdk/dist/core/schematron.js
2316
2414
  function violation(ruleId, severity, message, field) {
2317
2415
  return { ruleId, severity, message, field };
@@ -2839,6 +2937,14 @@ import pc4 from "picocolors";
2839
2937
 
2840
2938
  // src/lib/peppol-directory.ts
2841
2939
  var BASE_URL = "https://directory.peppol.eu/search/1.0/json";
2940
+ var DirectoryError = class extends Error {
2941
+ status;
2942
+ constructor(message, status) {
2943
+ super(message);
2944
+ this.name = "DirectoryError";
2945
+ this.status = status;
2946
+ }
2947
+ };
2842
2948
  function stripQuotes(name) {
2843
2949
  if (name.startsWith('"') && name.endsWith('"') && name.length >= 2) {
2844
2950
  return name.slice(1, -1);
@@ -2888,7 +2994,16 @@ function parseMatch(raw) {
2888
2994
  async function lookupParticipant(scheme, id) {
2889
2995
  const participantParam = `iso6523-actorid-upis::${scheme}:${id}`;
2890
2996
  const url = `${BASE_URL}?participant=${encodeURIComponent(participantParam)}`;
2891
- const response = await fetch(url);
2997
+ const response = await fetch(url, {
2998
+ signal: AbortSignal.timeout(15e3),
2999
+ headers: { "User-Agent": "@getpeppr/cli" }
3000
+ });
3001
+ if (!response.ok) {
3002
+ throw new DirectoryError(
3003
+ `Lookup failed: HTTP ${response.status} ${response.statusText}`,
3004
+ response.status
3005
+ );
3006
+ }
2892
3007
  const data = await response.json();
2893
3008
  if (!data.matches || data.matches.length === 0) {
2894
3009
  return null;
@@ -2900,7 +3015,16 @@ async function searchParticipants(opts) {
2900
3015
  if (opts.name) params.set("name", opts.name);
2901
3016
  if (opts.country) params.set("country", opts.country);
2902
3017
  const url = `${BASE_URL}?${params.toString()}`;
2903
- const response = await fetch(url);
3018
+ const response = await fetch(url, {
3019
+ signal: AbortSignal.timeout(15e3),
3020
+ headers: { "User-Agent": "@getpeppr/cli" }
3021
+ });
3022
+ if (!response.ok) {
3023
+ throw new DirectoryError(
3024
+ `Search failed: HTTP ${response.status} ${response.statusText}`,
3025
+ response.status
3026
+ );
3027
+ }
2904
3028
  const data = await response.json();
2905
3029
  const allMatches = (data.matches ?? []).map(parseMatch);
2906
3030
  const limit = opts.limit ?? 10;
@@ -3066,7 +3190,12 @@ async function handleLookup(peppolId, options) {
3066
3190
  let result;
3067
3191
  try {
3068
3192
  result = await lookupParticipant(parsed.scheme, parsed.id);
3069
- } catch {
3193
+ } catch (err) {
3194
+ if (err instanceof DirectoryError) {
3195
+ exitWithError(
3196
+ `${pc4.red("\u2717")} Peppol Directory returned an error (HTTP ${err.status ?? "unknown"}). Try again later.`
3197
+ );
3198
+ }
3070
3199
  exitWithError(
3071
3200
  `${pc4.red("\u2717")} Could not reach Peppol Directory. Check your internet connection.`
3072
3201
  );
@@ -3103,7 +3232,12 @@ async function handleSearch(options) {
3103
3232
  country: options.country,
3104
3233
  limit
3105
3234
  });
3106
- } catch {
3235
+ } catch (err) {
3236
+ if (err instanceof DirectoryError) {
3237
+ exitWithError(
3238
+ `${pc4.red("\u2717")} Peppol Directory returned an error (HTTP ${err.status ?? "unknown"}). Try again later.`
3239
+ );
3240
+ }
3107
3241
  exitWithError(
3108
3242
  `${pc4.red("\u2717")} Could not reach Peppol Directory. Check your internet connection.`
3109
3243
  );