@getpeppr/cli 0.11.1 → 0.12.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/CHANGELOG.md CHANGED
@@ -4,6 +4,32 @@ All notable changes to `@getpeppr/cli` are documented here.
4
4
 
5
5
  The CLI bundles `@getpeppr/sdk` into its published artifact (tsup `noExternal`), so `validate`, `convert` and `init` run the SDK's validators, country rules and UBL builder locally — an SDK release only reaches CLI users once the CLI is rebundled. Rebundle-only releases are listed with the SDK version they ship.
6
6
 
7
+ ## [0.12.0] — 2026-09-06
8
+
9
+ Rebundles `@getpeppr/sdk` 5.3.0 (GPR-1280).
10
+
11
+ - `getpeppr validate` and `getpeppr convert --validate` now enforce the three
12
+ Peppol billing-profile rules on `invoiceTypeCode` (`P0100`, `P0101`,
13
+ `P0112`) because they validate or build UBL locally (GPR-884, GPR-1273).
14
+ Documents outside the profile now fail these commands where they previously
15
+ passed — pipelines gating on the exit code change verdict, hence the minor.
16
+ - The `structure` section rule IDs now designate the rules they actually
17
+ verify (GPR-1267): buyer name `BR-06` → `BR-07`, buyer street/city/postal
18
+ code → `GETPEPPR-BUYER-ADDRESS`, line VAT rate `BR-CO-17` →
19
+ `GETPEPPR-LINE-VAT-RATE`, `taxCurrencyRate` `BR-53` →
20
+ `GETPEPPR-TAX-CURRENCY-RATE`, the buyer-reference warning `BR-10` →
21
+ `PEPPOL-EN16931-R003`, and the due-date warning carries no id.
22
+ - The `structure` section no longer repeats the country advisory warnings
23
+ that the `countryRules` section already reports: every warning is
24
+ displayed and counted once, and `totalWarnings` drops accordingly
25
+ (GPR-1267). Scripts that relied on the duplicated entries in
26
+ `structure.warnings` must read them from `countryRules.warnings`.
27
+ - `getpeppr send` now uses an explicit JSON-send preflight aligned with
28
+ `POST /v1/invoices` (GPR-1285). Because that route currently ignores
29
+ `invoiceTypeCode`, profile rules `P0100`, `P0101`, and `P0112` no longer
30
+ block that inert field before sending. Structured `BR-CL-01` validation
31
+ and all other applicable offline and country checks remain active.
32
+
7
33
  ## [0.11.1] — 2026-09-04
8
34
 
9
35
  Rebundles `@getpeppr/sdk` 5.2.0 (GPR-1084).
package/README.md CHANGED
@@ -126,6 +126,14 @@ API key resolution order: `--key` flag → `GETPEPPR_API_KEY` env var → stored
126
126
 
127
127
  Send an invoice through the getpeppr API to the Peppol network. Targets sandbox by default; pass `--prod` for production.
128
128
 
129
+ `getpeppr send` runs a JSON preflight aligned with `POST /v1/invoices`. That
130
+ endpoint currently ignores `invoiceTypeCode` and lets the provider generate the
131
+ UBL, so the send preflight does not apply `P0100`, `P0101`, or `P0112` to that
132
+ inert field. Structure checks including `BR-CL-01`, plus every other applicable
133
+ offline and country check, remain active. Use `getpeppr validate` or
134
+ `getpeppr convert --validate` for full local-UBL semantics; both retain those
135
+ profile checks because they validate or build the UBL locally.
136
+
129
137
  ```bash
130
138
  # Send a JSON file
131
139
  getpeppr send invoice.json
@@ -154,7 +162,7 @@ getpeppr send invoice.json --prod
154
162
  | `--attachment` | Attach the bundled test PDF |
155
163
  | `--watch` | Poll status until terminal (60s timeout) |
156
164
  | `-y, --yes` | Skip the `--prod` confirmation prompt |
157
- | `--no-validate` | Skip local pre-validation |
165
+ | `--no-validate` | Skip the JSON send preflight |
158
166
  | `--json` | Machine-readable JSON output |
159
167
  | `--quiet` | Exit code only, no output |
160
168
 
@@ -232,10 +240,11 @@ GETPEPPR_API_KEY=$SANDBOX_KEY getpeppr send invoice.json --json
232
240
 
233
241
  ## What it validates
234
242
 
235
- The CLI runs three validation engines from the [@getpeppr/sdk](https://www.npmjs.com/package/@getpeppr/sdk):
243
+ `getpeppr validate` and `getpeppr convert --validate` run three validation
244
+ engines from the [@getpeppr/sdk](https://www.npmjs.com/package/@getpeppr/sdk):
236
245
 
237
246
  1. **Structure** — Required fields, type checks, format validation
238
- 2. **Offline Checks** — 37 registered partial pre-flight checks: exact network-rule equivalents plus explicit `GETPEPPR-*` diagnostics. The legacy provider-capability diagnostic `unsupported_vat_category` can also appear, but is outside that count. This is not a Peppol conformance verdict.
247
+ 2. **Offline Checks** — 40 registered partial pre-flight checks: exact network-rule equivalents plus explicit `GETPEPPR-*` diagnostics. The legacy provider-capability diagnostic `unsupported_vat_category` can also appear, but is outside that count. This is not a Peppol conformance verdict.
239
248
  3. **Country Rules** — Belgium (BE), France (FR), Italy (IT), Netherlands (NL), Germany (DE)
240
249
 
241
250
  Scaffolding (`init`), validation, and conversion run fully offline — no API key or network connection required. Only `lookup`, `send`, and `whoami` need a network connection. Before a sandbox `send`, the CLI reads `GET /v1/identity`: its own generated fixture is adapted to the returned O/0 or AE/0 profile, while a user-edited file is never rewritten silently and is refused locally if its tax mode conflicts with the sender.
@@ -294,7 +303,7 @@ This is the exact template generated by `getpeppr init`. See the [full type refe
294
303
 
295
304
  ## Ready to send?
296
305
 
297
- Once your invoice validates, send it to the Peppol network — straight from the CLI:
306
+ When your invoice JSON is ready, send it to the Peppol network — straight from the CLI:
298
307
 
299
308
  ```bash
300
309
  getpeppr login # store your sandbox key once
package/dist/index.js CHANGED
@@ -130,7 +130,7 @@ Validating: ${pc.bold(filename)}
130
130
  return lines.join("\n");
131
131
  }
132
132
 
133
- // ../sdk/dist/core/canonical-schemes.js
133
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/canonical-schemes.js
134
134
  var ALIAS_TO_EAS = /* @__PURE__ */ new Map([
135
135
  ["AD:VAT", "9922"],
136
136
  ["AE:TIN", "0235"],
@@ -356,7 +356,7 @@ function countryForScheme(scheme) {
356
356
  var CANONICAL_SCHEME_COUNT = ALIAS_TO_EAS.size;
357
357
  var SCHEME_COUNTRY_COUNT = EAS_TO_COUNTRY.size;
358
358
 
359
- // ../sdk/dist/core/peppol-id.js
359
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/peppol-id.js
360
360
  function isWellFormedPeppolId(peppolId) {
361
361
  if (!peppolId.includes(":"))
362
362
  return false;
@@ -382,7 +382,7 @@ function parsePeppolId(peppolId) {
382
382
  };
383
383
  }
384
384
 
385
- // ../sdk/dist/core/iso6523-icd-codes.js
385
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/iso6523-icd-codes.js
386
386
  var ICD_CODES = /* @__PURE__ */ new Set([
387
387
  "0002",
388
388
  "0003",
@@ -645,7 +645,7 @@ function canCarryPartyIdentification(scheme, context) {
645
645
  return scheme === "SEPA" && context !== "AccountingCustomerParty";
646
646
  }
647
647
 
648
- // ../sdk/dist/core/ubl-builder.js
648
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/ubl-builder.js
649
649
  var UBL_NS = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2";
650
650
  var CAC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2";
651
651
  var CBC_NS = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2";
@@ -1296,7 +1296,7 @@ function buildCreditNoteXml(input) {
1296
1296
  </CreditNote>`;
1297
1297
  }
1298
1298
 
1299
- // ../sdk/dist/core/checksums/luhn.js
1299
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/checksums/luhn.js
1300
1300
  function isValidLuhn(input) {
1301
1301
  if (typeof input !== "string")
1302
1302
  return false;
@@ -1337,7 +1337,7 @@ function isValidLuhnSiret(input) {
1337
1337
  return false;
1338
1338
  }
1339
1339
 
1340
- // ../sdk/dist/core/country-rules.js
1340
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/country-rules.js
1341
1341
  function warn(field, message, ruleId) {
1342
1342
  return { field, message, ruleId };
1343
1343
  }
@@ -1478,7 +1478,7 @@ function validateCountryRules(input) {
1478
1478
  return { errors, warnings };
1479
1479
  }
1480
1480
 
1481
- // ../sdk/dist/core/code-lists.js
1481
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/code-lists.js
1482
1482
  var CURRENCIES = /* @__PURE__ */ new Map([
1483
1483
  ["EUR", { code: "EUR", name: "Euro", minorUnits: 2 }],
1484
1484
  ["USD", { code: "USD", name: "US Dollar", minorUnits: 2 }],
@@ -1681,7 +1681,7 @@ function getCreditNoteTypeCodes() {
1681
1681
  return [...CREDIT_NOTE_TYPE_CODES];
1682
1682
  }
1683
1683
 
1684
- // ../sdk/dist/core/validator.js
1684
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/validator.js
1685
1685
  function error(field, message, ruleId, suggestion) {
1686
1686
  return { field, message, ruleId, suggestion };
1687
1687
  }
@@ -1698,16 +1698,19 @@ function assertString(value, fieldPath, errors) {
1698
1698
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
1699
1699
  var ALLOWANCE_CHARGE_CONTRACT_RULE = "GETPEPPR-ALLOWANCE-CHARGE-AMOUNT";
1700
1700
  var DERIVED_AMOUNT_CONTRACT_RULE2 = "GETPEPPR-DERIVED-AMOUNT";
1701
+ var BUYER_ADDRESS_DELIVERY_RULE = "GETPEPPR-BUYER-ADDRESS";
1702
+ var LINE_VAT_RATE_RULE = "GETPEPPR-LINE-VAT-RATE";
1703
+ var TAX_CURRENCY_RATE_RULE = "GETPEPPR-TAX-CURRENCY-RATE";
1701
1704
  function survivesCents2(value) {
1702
1705
  return Number.isFinite(value) && Number.isFinite(value * 100);
1703
1706
  }
1704
1707
  function validateParty(party, path) {
1705
1708
  const errors = [];
1706
1709
  if (party.name === void 0 || party.name === null || party.name === "") {
1707
- errors.push(error(`${path}.name`, "Business name is required", "BR-06"));
1710
+ errors.push(error(`${path}.name`, "Business name is required", "BR-07"));
1708
1711
  } else if (!assertString(party.name, `${path}.name`, errors)) {
1709
1712
  } else if (!party.name.trim()) {
1710
- errors.push(error(`${path}.name`, "Business name is required", "BR-06"));
1713
+ errors.push(error(`${path}.name`, "Business name is required", "BR-07"));
1711
1714
  }
1712
1715
  if (party.peppolId === void 0 || party.peppolId === null || party.peppolId === "") {
1713
1716
  errors.push(error(`${path}.peppolId`, "Peppol participant ID is required", void 0, 'Format: "scheme:id", e.g. "0208:0685660237" for Belgian companies'));
@@ -1726,22 +1729,22 @@ function validateParty(party, path) {
1726
1729
  function validateBuyerAddress(party, path) {
1727
1730
  const errors = [];
1728
1731
  if (party.street === void 0 || party.street === null || party.street === "") {
1729
- errors.push(error(`${path}.street`, "Street address is required for the buyer", "BR-50", 'e.g. "123 Business Street"'));
1732
+ errors.push(error(`${path}.street`, "Street address is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "123 Business Street"'));
1730
1733
  } else if (!assertString(party.street, `${path}.street`, errors)) {
1731
1734
  } else if (!party.street.trim()) {
1732
- errors.push(error(`${path}.street`, "Street address is required for the buyer", "BR-50", 'e.g. "123 Business Street"'));
1735
+ errors.push(error(`${path}.street`, "Street address is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "123 Business Street"'));
1733
1736
  }
1734
1737
  if (party.city === void 0 || party.city === null || party.city === "") {
1735
- errors.push(error(`${path}.city`, "City is required for the buyer", "BR-51", 'e.g. "Brussels"'));
1738
+ errors.push(error(`${path}.city`, "City is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "Brussels"'));
1736
1739
  } else if (!assertString(party.city, `${path}.city`, errors)) {
1737
1740
  } else if (!party.city.trim()) {
1738
- errors.push(error(`${path}.city`, "City is required for the buyer", "BR-51", 'e.g. "Brussels"'));
1741
+ errors.push(error(`${path}.city`, "City is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "Brussels"'));
1739
1742
  }
1740
1743
  if (party.postalCode === void 0 || party.postalCode === null || party.postalCode === "") {
1741
- errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", "BR-53", 'e.g. "1000"'));
1744
+ errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "1000"'));
1742
1745
  } else if (!assertString(party.postalCode, `${path}.postalCode`, errors)) {
1743
1746
  } else if (!party.postalCode.trim()) {
1744
- errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", "BR-53", 'e.g. "1000"'));
1747
+ errors.push(error(`${path}.postalCode`, "Postal code is required for the buyer", BUYER_ADDRESS_DELIVERY_RULE, 'e.g. "1000"'));
1745
1748
  }
1746
1749
  return errors;
1747
1750
  }
@@ -1770,7 +1773,7 @@ function validateLine(line, index, isCreditNote = false) {
1770
1773
  }
1771
1774
  }
1772
1775
  if (line.vatRate === void 0 || line.vatRate === null) {
1773
- errors.push(error(`${path}.vatRate`, "VAT rate is required", "BR-CO-17"));
1776
+ errors.push(error(`${path}.vatRate`, "VAT rate is required", LINE_VAT_RATE_RULE));
1774
1777
  } else if (line.vatRate < 0 || line.vatRate > 100) {
1775
1778
  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."));
1776
1779
  }
@@ -1945,10 +1948,25 @@ function validateInvoice(input) {
1945
1948
  }
1946
1949
  }
1947
1950
  if (!input.buyerReference && !input.orderReference) {
1948
- warnings.push(warning("buyerReference", "Either buyerReference or orderReference is required by Peppol BIS 3.0 (BT-10).", "BR-10"));
1951
+ warnings.push(warning(
1952
+ "buyerReference",
1953
+ "Either buyerReference or orderReference is required by Peppol BIS 3.0 (BT-10).",
1954
+ // GPR-1267 — PEPPOL-EN16931-R003 IS this rule: "A buyer reference or
1955
+ // purchase order reference MUST be provided." BR-10 is the buyer
1956
+ // postal address.
1957
+ "PEPPOL-EN16931-R003"
1958
+ ));
1949
1959
  }
1950
1960
  if (input.taxCurrency && input.taxCurrency !== (input.currency ?? "EUR") && !input.taxCurrencyRate) {
1951
- errors.push(error("taxCurrencyRate", "Tax currency rate is required when taxCurrency differs from document currency", "BR-53", "Set taxCurrencyRate to the exchange rate from document currency to tax currency"));
1961
+ errors.push(error(
1962
+ "taxCurrencyRate",
1963
+ "Tax currency rate is required when taxCurrency differs from document currency",
1964
+ // GPR-1267 — BR-53 mandates the BT-111 total in accounting currency,
1965
+ // which the builder derives itself; the client-supplied rate is an
1966
+ // input-contract requirement, not that rule.
1967
+ TAX_CURRENCY_RATE_RULE,
1968
+ "Set taxCurrencyRate to the exchange rate from document currency to tax currency"
1969
+ ));
1952
1970
  }
1953
1971
  if (input.taxCurrency && input.taxCurrency === (input.currency ?? "EUR")) {
1954
1972
  warnings.push(warning("taxCurrency", "Tax currency is the same as document currency \u2014 TaxCurrencyCode will be omitted"));
@@ -1963,7 +1981,7 @@ function validateInvoice(input) {
1963
1981
  errors.push(error("taxCurrency", `Invalid tax currency code: "${input.taxCurrency}"`, void 0, 'Use ISO 4217 (e.g., "EUR", "USD")'));
1964
1982
  }
1965
1983
  if (!input.dueDate) {
1966
- warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms.", "BR-09"));
1984
+ warnings.push(warning("dueDate", "No due date specified. Recommended for payment terms."));
1967
1985
  }
1968
1986
  if (!input.to?.vatNumber) {
1969
1987
  warnings.push(warning("to.vatNumber", "Buyer VAT number not provided. May be required for B2B."));
@@ -1983,7 +2001,7 @@ function validateInvoice(input) {
1983
2001
  };
1984
2002
  }
1985
2003
 
1986
- // ../sdk/dist/core/schematron.js
2004
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/schematron.js
1987
2005
  function violation(ruleId, severity, message, field) {
1988
2006
  return { ruleId, severity, message, field };
1989
2007
  }
@@ -2380,6 +2398,90 @@ var buyerPeppolIdRequired = (input) => {
2380
2398
  }
2381
2399
  return [];
2382
2400
  };
2401
+ var PROFILE_INVOICE_TYPE_CODES = /* @__PURE__ */ new Set([
2402
+ "71",
2403
+ "80",
2404
+ "82",
2405
+ "84",
2406
+ "102",
2407
+ "218",
2408
+ "219",
2409
+ "326",
2410
+ "331",
2411
+ "380",
2412
+ "382",
2413
+ "383",
2414
+ "384",
2415
+ "386",
2416
+ "388",
2417
+ "393",
2418
+ "395",
2419
+ "553",
2420
+ "575",
2421
+ "623",
2422
+ "780",
2423
+ "817",
2424
+ "870",
2425
+ "875",
2426
+ "876",
2427
+ "877"
2428
+ ]);
2429
+ var PROFILE_CREDIT_NOTE_TYPE_CODES = /* @__PURE__ */ new Set([
2430
+ "381",
2431
+ "396",
2432
+ "81",
2433
+ "83",
2434
+ "532"
2435
+ ]);
2436
+ function normalizeXmlSpace(value) {
2437
+ return value.replace(/[\u0009\u000A\u000D\u0020]+/g, " ").replace(/^ /, "").replace(/ $/, "");
2438
+ }
2439
+ function normalizedTypeCode(value, fallback) {
2440
+ if (value === void 0 || value === null)
2441
+ return String(fallback);
2442
+ if (typeof value === "number" && Number.isFinite(value))
2443
+ return String(value);
2444
+ if (typeof value === "string" && value.length <= 16)
2445
+ return normalizeXmlSpace(value);
2446
+ return void 0;
2447
+ }
2448
+ function normalizedCountry(value) {
2449
+ return typeof value === "string" ? normalizeXmlSpace(value).toUpperCase() : void 0;
2450
+ }
2451
+ var invoiceTypeCodeForProfile = (input) => {
2452
+ if (input.isCreditNote)
2453
+ return [];
2454
+ const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 380);
2455
+ if (invoiceTypeCode !== void 0 && PROFILE_INVOICE_TYPE_CODES.has(invoiceTypeCode))
2456
+ return [];
2457
+ return [
2458
+ violation("PEPPOL-EN16931-P0100", "error", "Invoice type code is not allowed by the Peppol billing profile used by the local UBL builder.", "invoiceTypeCode")
2459
+ ];
2460
+ };
2461
+ var creditNoteTypeCodeForProfile = (input) => {
2462
+ if (!input.isCreditNote)
2463
+ return [];
2464
+ const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 381);
2465
+ if (invoiceTypeCode !== void 0 && PROFILE_CREDIT_NOTE_TYPE_CODES.has(invoiceTypeCode))
2466
+ return [];
2467
+ return [
2468
+ violation("PEPPOL-EN16931-P0101", "error", "Credit note type code is not allowed by the Peppol billing profile used by the local UBL builder.", "invoiceTypeCode")
2469
+ ];
2470
+ };
2471
+ var germanInvoiceTypeCode = (input) => {
2472
+ if (input.isCreditNote)
2473
+ return [];
2474
+ const invoiceTypeCode = normalizedTypeCode(input.invoiceTypeCode, 380);
2475
+ if (invoiceTypeCode !== "326" && invoiceTypeCode !== "384")
2476
+ return [];
2477
+ const sellerCountry = normalizedCountry(input.from?.country);
2478
+ const buyerCountry = normalizedCountry(input.to?.country);
2479
+ if (sellerCountry === "DE" && buyerCountry === "DE")
2480
+ return [];
2481
+ return [
2482
+ violation("PEPPOL-EN16931-P0112", "error", "Invoice type code 326 or 384 is only allowed when both buyer and seller are German organizations.", "invoiceTypeCode")
2483
+ ];
2484
+ };
2383
2485
  var vatCategoryCodes = (input) => {
2384
2486
  const violations = [];
2385
2487
  const sendable = SENDABLE_VAT_CATEGORIES.join(", ");
@@ -2445,6 +2547,9 @@ var ALL_RULES = [
2445
2547
  brIc10,
2446
2548
  // Peppol-specific
2447
2549
  buyerPeppolIdRequired,
2550
+ invoiceTypeCodeForProfile,
2551
+ creditNoteTypeCodeForProfile,
2552
+ germanInvoiceTypeCode,
2448
2553
  vatCategoryCodes,
2449
2554
  unitCodeRecognised
2450
2555
  ];
@@ -2505,10 +2610,13 @@ var SDK_SCHEMATRON_RULE_IDS = [
2505
2610
  "BR-O-10",
2506
2611
  "BR-IC-10",
2507
2612
  "GETPEPPR-BUYER-PEPPOL-ID-REQUIRED",
2613
+ "PEPPOL-EN16931-P0100",
2614
+ "PEPPOL-EN16931-P0101",
2615
+ "PEPPOL-EN16931-P0112",
2508
2616
  "GETPEPPR-UNIT-CODE-RECOGNISED"
2509
2617
  ];
2510
2618
 
2511
- // ../sdk/dist/core/status-precedence.js
2619
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/status-precedence.js
2512
2620
  var STATUS_PRECEDENCE = [
2513
2621
  { status: "failed", family: "terminal-failure" },
2514
2622
  { status: "rejected", family: "terminal-failure" },
@@ -2532,10 +2640,10 @@ function statusFamily(status) {
2532
2640
  }
2533
2641
  var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
2534
2642
 
2535
- // ../sdk/dist/version.js
2643
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/version.js
2536
2644
  var SDK_VERSION = "5.2.0";
2537
2645
 
2538
- // ../sdk/dist/core/api-result.js
2646
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/api-result.js
2539
2647
  var API_RESULT_HEADER_NAMES = {
2540
2648
  requestId: "Getpeppr-Request-Id",
2541
2649
  resultCode: "Getpeppr-Result-Code",
@@ -2652,7 +2760,7 @@ function parseApiResultHeaders(headers) {
2652
2760
  return Object.keys(result).length === 0 ? void 0 : result;
2653
2761
  }
2654
2762
 
2655
- // ../sdk/dist/core/client.js
2763
+ // ../../../../../../Users/sylvain/Code/Projets/getpeppr/packages/sdk/dist/core/client.js
2656
2764
  function normalizeHeaderValue(value) {
2657
2765
  return value.replace(/^[\t\n\r ]+|[\t\n\r ]+$/g, "");
2658
2766
  }
@@ -4424,6 +4532,8 @@ var DirectoryOperations = class {
4424
4532
  }
4425
4533
  /**
4426
4534
  * Search the Peppol Directory for participants.
4535
+ * Pagination is exact and participant-based. Queries wider than the public
4536
+ * Directory's accessible result window are rejected; add narrower criteria.
4427
4537
  *
4428
4538
  * @example
4429
4539
  * ```ts
@@ -4889,14 +4999,32 @@ var TransportOperations = class {
4889
4999
  };
4890
5000
 
4891
5001
  // src/commands/validate.ts
4892
- function runValidation(input) {
5002
+ var SEND_INERT_INVOICE_TYPE_PROFILE_RULES = /* @__PURE__ */ new Set([
5003
+ "PEPPOL-EN16931-P0100",
5004
+ "PEPPOL-EN16931-P0101",
5005
+ "PEPPOL-EN16931-P0112"
5006
+ ]);
5007
+ function isSendRelevantSchematronViolation(violation2) {
5008
+ return !(violation2.field === "invoiceTypeCode" && SEND_INERT_INVOICE_TYPE_PROFILE_RULES.has(violation2.ruleId));
5009
+ }
5010
+ function runMergedValidation(input, schematronFilter) {
4893
5011
  const structure = validateInvoice(input);
4894
- const schematron = validateSchematron(input);
5012
+ const completeSchematron = validateSchematron(input);
5013
+ const schematron = {
5014
+ errors: completeSchematron.errors.filter(schematronFilter),
5015
+ warnings: completeSchematron.warnings
5016
+ };
4895
5017
  const countryRules = validateCountryRules(input);
5018
+ const countryKeys = new Set(
5019
+ countryRules.warnings.map((w) => JSON.stringify([w.field, w.message, w.ruleId]))
5020
+ );
5021
+ const structureWarnings = structure.warnings.filter(
5022
+ (w) => !countryKeys.has(JSON.stringify([w.field, w.message, w.ruleId]))
5023
+ );
4896
5024
  const totalErrors = structure.errors.length + schematron.errors.length + countryRules.errors.length;
4897
- const totalWarnings = structure.warnings.length + schematron.warnings.length + countryRules.warnings.length;
5025
+ const totalWarnings = structureWarnings.length + schematron.warnings.length + countryRules.warnings.length;
4898
5026
  return {
4899
- structure: { errors: structure.errors, warnings: structure.warnings },
5027
+ structure: { errors: structure.errors, warnings: structureWarnings },
4900
5028
  schematron: { errors: schematron.errors, warnings: schematron.warnings },
4901
5029
  countryRules: {
4902
5030
  errors: countryRules.errors,
@@ -4907,6 +5035,12 @@ function runValidation(input) {
4907
5035
  valid: totalErrors === 0
4908
5036
  };
4909
5037
  }
5038
+ function runValidation(input) {
5039
+ return runMergedValidation(input, () => true);
5040
+ }
5041
+ function runSendPreflight(input) {
5042
+ return runMergedValidation(input, isSendRelevantSchematronViolation);
5043
+ }
4910
5044
  function registerValidateCommand(program2) {
4911
5045
  program2.command("validate").description("Validate a Peppol invoice JSON file").argument("<file>", "path to invoice JSON file").option("--json", "output results as JSON").option("--quiet", "exit code only, no output").action(async (file, options) => {
4912
5046
  const input = readAndValidateInvoiceJson(file);
@@ -5754,7 +5888,7 @@ function formatSendResult(result, mode) {
5754
5888
  var API_BASE = "https://api.getpeppr.dev/v1";
5755
5889
  var LOCAL_BASE = "http://localhost:3001/api/v1";
5756
5890
  function registerSendCommand(program2) {
5757
- 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) => {
5891
+ 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 the JSON send preflight").option("--json", "output JSON").option("--quiet", "exit code only, no output").action(async (file, flags) => {
5758
5892
  let auth;
5759
5893
  try {
5760
5894
  auth = resolveApiKey({
@@ -5828,7 +5962,7 @@ function registerSendCommand(program2) {
5828
5962
  }
5829
5963
  }
5830
5964
  if (flags.validate !== false) {
5831
- const result2 = runValidation(payload);
5965
+ const result2 = runSendPreflight(payload);
5832
5966
  if (!result2.valid) {
5833
5967
  process.stderr.write(
5834
5968
  `${pc6.red("\u2717")} Pre-validation failed (${result2.totalErrors} errors). Use --no-validate to skip.