@getpeppr/cli 0.6.0 → 0.7.1

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
@@ -1161,7 +1161,7 @@ function statusFamily(status) {
1161
1161
  var TERMINAL_FAILURE_STATUSES = STATUS_PRECEDENCE.filter((e) => e.family === "terminal-failure").map((e) => e.status);
1162
1162
 
1163
1163
  // ../sdk/dist/version.js
1164
- var SDK_VERSION = "3.2.0";
1164
+ var SDK_VERSION = "4.1.1";
1165
1165
 
1166
1166
  // ../sdk/dist/core/client.js
1167
1167
  function findHeaderCaseInsensitive(headers, name) {
@@ -1199,6 +1199,50 @@ function parseRetryAfter(headerValue) {
1199
1199
  }
1200
1200
  return void 0;
1201
1201
  }
1202
+ var CONTROL_CHARACTERS = /[\u0000-\u001F\u007F-\u009F]/g;
1203
+ function stripControls(value) {
1204
+ return value.replace(CONTROL_CHARACTERS, " ");
1205
+ }
1206
+ function readOwn(source, key) {
1207
+ return Object.hasOwn(source, key) ? source[key] : void 0;
1208
+ }
1209
+ function readSentence(source, key) {
1210
+ const value = readOwn(source, key);
1211
+ if (typeof value !== "string")
1212
+ return null;
1213
+ const cleaned = stripControls(value).trim();
1214
+ return cleaned === "" ? null : cleaned;
1215
+ }
1216
+ function safeDocsUrl(value) {
1217
+ if (typeof value !== "string")
1218
+ return null;
1219
+ let parsed;
1220
+ try {
1221
+ parsed = new URL(value);
1222
+ } catch {
1223
+ return null;
1224
+ }
1225
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
1226
+ return null;
1227
+ return parsed.href;
1228
+ }
1229
+ function formatApiErrorMessage(status, rawBody) {
1230
+ const verbatim = `getpeppr API error (${status}): ${stripControls(rawBody)}`;
1231
+ let parsed;
1232
+ try {
1233
+ parsed = JSON.parse(rawBody);
1234
+ } catch {
1235
+ return verbatim;
1236
+ }
1237
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1238
+ return verbatim;
1239
+ }
1240
+ const sentence = readSentence(parsed, "message") ?? readSentence(parsed, "error");
1241
+ if (sentence === null)
1242
+ return verbatim;
1243
+ const link = safeDocsUrl(readOwn(parsed, "docs"));
1244
+ return `getpeppr API error (${status}): ${sentence}${link ? ` See ${link}` : ""}`;
1245
+ }
1202
1246
  function isRetryableError(error2) {
1203
1247
  if (error2 instanceof PeppolApiError) {
1204
1248
  return RETRYABLE_STATUS_CODES.has(error2.statusCode);
@@ -1300,7 +1344,7 @@ var GetpepprAdapter = class {
1300
1344
  } catch {
1301
1345
  }
1302
1346
  }
1303
- throw new PeppolApiError(`getpeppr API error (${response.status}): ${errorBody}`, response.status, errorBody, retryAfterMs);
1347
+ throw new PeppolApiError(formatApiErrorMessage(response.status, errorBody), response.status, errorBody, retryAfterMs);
1304
1348
  }
1305
1349
  if (response.status === 204) {
1306
1350
  if (this.onResponse) {
@@ -1328,7 +1372,17 @@ var GetpepprAdapter = class {
1328
1372
  this.onResponse({
1329
1373
  status: response.status,
1330
1374
  headers: Object.fromEntries(response.headers.entries()),
1331
- body: responseBody,
1375
+ // GPR-1061 — a COPY, not the live object. The hook used to receive
1376
+ // the very body the parsers then read: a hook that redacts fields
1377
+ // before logging them (an entirely reasonable hook) could delete
1378
+ // `status` and make the SDK blame the gateway for the omission.
1379
+ //
1380
+ // The clone is not guaranteed: structuredClone throws RangeError
1381
+ // past roughly 3000 levels of nesting. Falling back to the live
1382
+ // object would reopen the mutation above, and letting the throw
1383
+ // escape would silently drop the log — the surrounding catch
1384
+ // swallows everything — so the hook fires with a marker instead.
1385
+ body: cloneForHook(responseBody),
1332
1386
  durationMs: Date.now() - startTime,
1333
1387
  timestamp: Date.now()
1334
1388
  });
@@ -1385,8 +1439,13 @@ var GetpepprAdapter = class {
1385
1439
  params.set("include", "lines");
1386
1440
  const query = params.toString() ? `?${params.toString()}` : "";
1387
1441
  const result = await this.request("GET", `/invoices${query}`);
1388
- const invoices = result.invoices ?? result.data ?? [];
1389
- const meta = result.meta;
1442
+ const envelope = requireRecordBody(result, "the invoice list");
1443
+ const rows = envelope.invoices ?? envelope.data ?? [];
1444
+ if (!Array.isArray(rows)) {
1445
+ throw new PeppolProtocolError("The getpeppr API answered the invoice list without an array of invoices. Please report this response to support@getpeppr.dev.", "invoices", boundedBody(envelope));
1446
+ }
1447
+ const invoices = rows;
1448
+ const meta = envelope.meta;
1390
1449
  return {
1391
1450
  data: invoices.map(parseInvoiceSummary),
1392
1451
  meta: {
@@ -1398,8 +1457,9 @@ var GetpepprAdapter = class {
1398
1457
  }
1399
1458
  };
1400
1459
  }
1401
- async getStatus(documentId) {
1402
- const result = await this.request("GET", `/invoices/${documentId}`);
1460
+ async getStatus(documentId, options) {
1461
+ const query = options?.includeEvidence ? "?include=evidence" : "";
1462
+ const result = await this.request("GET", `/invoices/${documentId}${query}`);
1403
1463
  return parseSendResult(result);
1404
1464
  }
1405
1465
  async lookupDirectory(scheme, id) {
@@ -1479,7 +1539,7 @@ var GetpepprAdapter = class {
1479
1539
  } catch {
1480
1540
  }
1481
1541
  }
1482
- throw new PeppolApiError(`getpeppr API error (${response.status}): ${errorBody}`, response.status, errorBody, retryAfterMs);
1542
+ throw new PeppolApiError(formatApiErrorMessage(response.status, errorBody), response.status, errorBody, retryAfterMs);
1483
1543
  }
1484
1544
  const responseBody = await response.arrayBuffer();
1485
1545
  if (this.onResponse) {
@@ -1693,7 +1753,11 @@ var GetpepprAdapter = class {
1693
1753
  const body = {
1694
1754
  file: arrayBufferToBase64(options.file),
1695
1755
  filename: options.filename,
1696
- mimeType: options.mimeType ?? detectMimeType(options.filename)
1756
+ mimeType: options.mimeType ?? detectMimeType(options.filename),
1757
+ // Declared, never derived from the document: routing decides delivery, and
1758
+ // parsing a caller's XML for a destination would put a parse error on the
1759
+ // "whose invoice goes where" path.
1760
+ to: options.to
1697
1761
  };
1698
1762
  const result = await this.request("POST", "/invoices/import", body);
1699
1763
  return parseSendResult(result);
@@ -1748,21 +1812,71 @@ function detectMimeType(filename) {
1748
1812
  return "application/octet-stream";
1749
1813
  }
1750
1814
  }
1751
- function parseSendResult(result) {
1752
- const rawStatus = result.status == null ? void 0 : String(result.status);
1815
+ var PROTOCOL_ERROR_BODY_LIMIT = 2e3;
1816
+ function cloneForHook(body) {
1817
+ try {
1818
+ return structuredClone(body);
1819
+ } catch {
1820
+ return "[response body could not be copied for logging]";
1821
+ }
1822
+ }
1823
+ function boundedBody(raw) {
1824
+ let serialised;
1825
+ try {
1826
+ serialised = JSON.stringify(raw) ?? String(raw);
1827
+ } catch {
1828
+ serialised = "[unserialisable response body]";
1829
+ }
1830
+ if (serialised.length <= PROTOCOL_ERROR_BODY_LIMIT)
1831
+ return serialised;
1832
+ let head = serialised.slice(0, PROTOCOL_ERROR_BODY_LIMIT);
1833
+ if (/[\uD800-\uDBFF]$/.test(head))
1834
+ head = head.slice(0, -1);
1835
+ return `${head}\u2026 [truncated, ${serialised.length} chars]`;
1836
+ }
1837
+ function requireRecordBody(raw, surface) {
1838
+ if (isRecord(raw))
1839
+ return raw;
1840
+ throw new PeppolProtocolError(`The getpeppr API answered ${surface} with a body that is not an object. Please report this response to support@getpeppr.dev.`, "body", boundedBody(raw));
1841
+ }
1842
+ function requireWireStatus(candidate, raw, surface) {
1843
+ if (typeof candidate === "string" && candidate.trim() !== "")
1844
+ return candidate;
1845
+ throw new PeppolProtocolError(`The getpeppr API answered ${surface} without a status. The SDK will not invent one \u2014 please report this response to support@getpeppr.dev.`, "status", boundedBody(raw));
1846
+ }
1847
+ function optionalWireId(value) {
1848
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
1849
+ }
1850
+ function parseSendResult(body) {
1851
+ const result = requireRecordBody(body, "this request");
1852
+ const rawStatus = requireWireStatus(result.status, result, "this request");
1753
1853
  const sendResult = {
1754
1854
  id: String(result.id ?? ""),
1755
- status: mapStatus(rawStatus ?? "submitted"),
1756
- peppolMessageId: result.peppolMessageId ?? result.peppol_message_id,
1757
- createdAt: String(result.createdAt ?? result.updatedAt ?? result.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
1855
+ status: mapStatus(rawStatus),
1856
+ rawStatus,
1857
+ // A TYPE guard like the identifiers below, not a cast. This line used to
1858
+ // read `as string | undefined`, which types a number as a string and hands
1859
+ // a consumer an AS4 id that never existed.
1860
+ peppolMessageId: optionalWireId(result.peppolMessageId ?? result.peppol_message_id),
1758
1861
  ublXml: result.ublXml,
1759
1862
  warnings: Array.isArray(result.warnings) ? result.warnings : void 0
1760
1863
  };
1761
- if (rawStatus !== void 0)
1762
- sendResult.rawStatus = rawStatus;
1864
+ const createdAt = result.createdAt ?? result.created_at;
1865
+ if (createdAt != null)
1866
+ sendResult.createdAt = String(createdAt);
1763
1867
  const detail = parseStatusDetail(result.detail);
1764
1868
  if (detail)
1765
1869
  sendResult.detail = detail;
1870
+ const rulebook = result.rulebook;
1871
+ if (typeof rulebook === "object" && rulebook !== null && typeof rulebook.peppol === "string" && typeof rulebook.verifiedAt === "string") {
1872
+ sendResult.rulebook = rulebook;
1873
+ }
1874
+ const submissionId = optionalWireId(result.submissionId);
1875
+ if (submissionId)
1876
+ sendResult.submissionId = submissionId;
1877
+ const providerDocumentId = optionalWireId(result.providerDocumentId);
1878
+ if (providerDocumentId)
1879
+ sendResult.providerDocumentId = providerDocumentId;
1766
1880
  return sendResult;
1767
1881
  }
1768
1882
  function parseDirectoryEntry(result) {
@@ -1898,18 +2012,24 @@ function parseLegalEntity(raw) {
1898
2012
  }
1899
2013
  return le;
1900
2014
  }
1901
- function parseInvoiceSummary(raw) {
1902
- const rawStatus = (raw.state ?? raw.status) == null ? void 0 : String(raw.state ?? raw.status);
2015
+ function parseInvoiceSummary(row) {
2016
+ const raw = requireRecordBody(row, "this invoice row");
2017
+ const rawStatus = requireWireStatus(raw.state ?? raw.status, raw, "this invoice row");
1903
2018
  const summary = {
1904
2019
  id: String(raw.id ?? ""),
1905
2020
  number: String(raw.invoiceNumber ?? raw.number ?? ""),
1906
- status: mapStatus(rawStatus ?? "submitted")
2021
+ status: mapStatus(rawStatus),
2022
+ rawStatus
1907
2023
  };
1908
- if (rawStatus !== void 0)
1909
- summary.rawStatus = rawStatus;
1910
2024
  const detail = parseStatusDetail(raw.detail);
1911
2025
  if (detail)
1912
2026
  summary.detail = detail;
2027
+ const submissionId = optionalWireId(raw.submissionId);
2028
+ if (submissionId)
2029
+ summary.submissionId = submissionId;
2030
+ const providerDocumentId = optionalWireId(raw.providerDocumentId);
2031
+ if (providerDocumentId)
2032
+ summary.providerDocumentId = providerDocumentId;
1913
2033
  if (raw.createdAt != null)
1914
2034
  summary.createdAt = String(raw.createdAt);
1915
2035
  if (typeof raw.isCreditNote === "boolean")
@@ -1991,6 +2111,16 @@ var PeppolValidationError = class extends PeppolError {
1991
2111
  this.name = "PeppolValidationError";
1992
2112
  }
1993
2113
  };
2114
+ var PeppolProtocolError = class extends PeppolError {
2115
+ field;
2116
+ responseBody;
2117
+ constructor(message, field, responseBody) {
2118
+ super(message);
2119
+ this.field = field;
2120
+ this.responseBody = responseBody;
2121
+ this.name = "PeppolProtocolError";
2122
+ }
2123
+ };
1994
2124
  var PeppolApiError = class extends PeppolError {
1995
2125
  statusCode;
1996
2126
  responseBody;
@@ -2026,6 +2156,22 @@ var Peppol = class {
2026
2156
  contacts;
2027
2157
  bankAccounts;
2028
2158
  transports;
2159
+ /**
2160
+ * Sub-tenant Legal Entities — **platform accounts only**.
2161
+ *
2162
+ * Requires a platform account and a **master API key**. With a standard key
2163
+ * every call here fails with 403 `master_key_required`.
2164
+ *
2165
+ * Onboarding your OWN company is not done through this API: your legal entity
2166
+ * is managed in the console, on the Peppol identity page. This surface is for
2167
+ * platforms that onboard their customers as sub-tenants.
2168
+ *
2169
+ * **Getting access:** platform mode is enabled by getpeppr on your account —
2170
+ * email hello@getpeppr.dev to have it switched on. Once it is, you create the
2171
+ * master key yourself at https://console.getpeppr.dev/api-keys.
2172
+ *
2173
+ * @see https://getpeppr.dev/docs/platform/legal-entities/
2174
+ */
2029
2175
  legalEntities;
2030
2176
  constructor(config) {
2031
2177
  if (!config.apiKey) {
@@ -2164,9 +2310,23 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2164
2310
  listAll(options) {
2165
2311
  return paginate((offset, limit) => this.adapter.listInvoices({ ...options, offset, limit }), options);
2166
2312
  }
2167
- /** Get the status of a sent invoice */
2168
- async getStatus(documentId) {
2169
- return this.adapter.getStatus(documentId);
2313
+ /**
2314
+ * Get the status of a sent invoice.
2315
+ *
2316
+ * @param options.includeEvidence Ask the gateway to read the sending evidence
2317
+ * from the Peppol network so the result carries `peppolMessageId`. Costs one
2318
+ * provider round trip, so it is off by default; if the document has not gone
2319
+ * out yet, or the read fails, the field is simply absent and everything else
2320
+ * is unaffected.
2321
+ *
2322
+ * @example
2323
+ * ```ts
2324
+ * const status = await peppol.invoices.getStatus(id);
2325
+ * const proof = await peppol.invoices.getStatus(id, { includeEvidence: true });
2326
+ * ```
2327
+ */
2328
+ async getStatus(documentId, options) {
2329
+ return this.adapter.getStatus(documentId, options);
2170
2330
  }
2171
2331
  /**
2172
2332
  * Export an invoice in a specific format (e.g., PDF, UBL XML).
@@ -2202,9 +2362,30 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2202
2362
  return this.adapter.validateDocumentServer(input);
2203
2363
  }
2204
2364
  /**
2205
- * Import an invoice from a file (XML, PDF, JSON).
2206
- * The file is base64-encoded and sent to the gateway, which forwards it
2207
- * as multipart/form-data to the provider's import endpoint.
2365
+ * Send a UBL Invoice or CreditNote you built yourself.
2366
+ *
2367
+ * getpeppr does not regenerate, normalise, or repair the document we
2368
+ * forward the bytes you supplied, unchanged, to the network. Only UBL Invoice
2369
+ * and CreditNote are accepted — a PDF, a CII document, or an XML that is
2370
+ * neither is refused. The file is base64-encoded into a JSON body; there is
2371
+ * no multipart upload.
2372
+ *
2373
+ * ⚠️ Byte-for-byte equality is NOT guaranteed, because the network
2374
+ * re-serialises the document in transit. Measured 2026-08-18 on a test
2375
+ * document: namespace declarations come back reordered, numeric character
2376
+ * references are resolved (`&#65;` → `A`), whitespace inside tags is dropped,
2377
+ * and no element was added, removed or altered. That is one document and four
2378
+ * kinds of difference — indicative, not a warranty of what is preserved.
2379
+ * **If you seal your documents, hash a canonical form (C14N) rather than the
2380
+ * raw bytes.**
2381
+ *
2382
+ * `to` is required and is never read from the document. Routing decides
2383
+ * delivery, the document travels as payload, and getpeppr will not guess a
2384
+ * destination by parsing your XML.
2385
+ *
2386
+ * Before transmission the document is validated against the complete official
2387
+ * OpenPeppol rulebooks. A document violating a `fatal` rule is refused and is
2388
+ * NOT sent; the error names the rule.
2208
2389
  *
2209
2390
  * @example
2210
2391
  * ```ts
@@ -2212,10 +2393,29 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2212
2393
  * const result = await peppol.invoices.importFile({
2213
2394
  * file: xmlBytes,
2214
2395
  * filename: "invoice.xml",
2396
+ * to: { peppolId: "0208:0685660237" },
2215
2397
  * });
2216
2398
  * console.log(result.id, result.status);
2217
2399
  * ```
2218
- * @throws {PeppolApiError} 501 if the gateway provider does not support file import
2400
+ *
2401
+ * @throws {PeppolApiError} 400 — `invalid_base64`, or a missing `file` /
2402
+ * `filename`. `missing_recipient` when `to.peppolId` is absent.
2403
+ * @throws {PeppolApiError} 422 — the document was refused and NOT sent. Two
2404
+ * families, and they do NOT retry the same way:
2405
+ *
2406
+ * - **The document was rejected** (`validation_failed`, `not_ubl_document`,
2407
+ * `document_too_complex`, `undecodable_document`, `unsupported_encoding`).
2408
+ * Terminal: the same bytes fail identically forever. Fix the document —
2409
+ * retrying is pure waste, and `validation_failed` names the rule.
2410
+ * - **The account may not send right now** (`peppol_identity_incomplete`,
2411
+ * `peppol_identity_not_verified`, `platform_billing_not_active`,
2412
+ * `production_access_expired`). ⛔ NOT terminal: these describe account
2413
+ * state, and account state changes — a verification completes, a contract
2414
+ * is activated. The identical document will go through once it does.
2415
+ *
2416
+ * Treating the second family as terminal costs a customer a real invoice;
2417
+ * treating the first as retryable costs an infinite loop. See the API
2418
+ * reference for the full list.
2219
2419
  */
2220
2420
  async importFile(options) {
2221
2421
  return this.adapter.importInvoice(options);
@@ -2580,6 +2780,13 @@ var LegalEntityOperations = class {
2580
2780
  /**
2581
2781
  * Create a sub-tenant Legal Entity for one of your customers.
2582
2782
  *
2783
+ * **Platform accounts only — requires a master API key.** Platform mode is
2784
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2785
+ * https://console.getpeppr.dev/api-keys.
2786
+ *
2787
+ * Your own company's legal entity is managed in the console, on the Peppol
2788
+ * identity page; this creates an entity for a customer of yours.
2789
+ *
2583
2790
  * Idempotent on `externalId`: repeated calls with the same `externalId` return
2584
2791
  * the existing entity (HTTP 200) instead of creating a duplicate. Transient 5xx
2585
2792
  * failures are NOT auto-retried unless you pass `options.idempotencyKey`.
@@ -2599,19 +2806,37 @@ var LegalEntityOperations = class {
2599
2806
  return this.adapter.createLegalEntity(input, options);
2600
2807
  }
2601
2808
  /**
2602
- * Fetch a single sub-tenant Legal Entity by id. For production entities the
2603
- * `status` reflects the attestation lifecycle (awaiting_authz → attested → active).
2809
+ * Fetch a single sub-tenant Legal Entity by id.
2810
+ *
2811
+ * **Platform accounts only — requires a master API key.** Platform mode is
2812
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2813
+ * https://console.getpeppr.dev/api-keys.
2814
+ *
2815
+ * For production entities the `status` reflects the attestation lifecycle
2816
+ * (awaiting_authz → attested → active).
2604
2817
  */
2605
2818
  async get(id) {
2606
2819
  return this.adapter.getLegalEntity(id);
2607
2820
  }
2608
- /** List your sub-tenant Legal Entities, newest first. */
2821
+ /**
2822
+ * List your sub-tenant Legal Entities, newest first.
2823
+ *
2824
+ * **Platform accounts only — requires a master API key.** Platform mode is
2825
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2826
+ * https://console.getpeppr.dev/api-keys.
2827
+ *
2828
+ * This lists the customers you have onboarded, never your own legal entity.
2829
+ */
2609
2830
  async list(options) {
2610
2831
  return this.adapter.listLegalEntities(options);
2611
2832
  }
2612
2833
  /**
2613
2834
  * Async iterator over all sub-tenant Legal Entities, handling pagination.
2614
2835
  *
2836
+ * **Platform accounts only — requires a master API key.** Platform mode is
2837
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2838
+ * https://console.getpeppr.dev/api-keys.
2839
+ *
2615
2840
  * @example
2616
2841
  * ```ts
2617
2842
  * for await (const le of peppol.legalEntities.listAll()) console.log(le.id, le.status);
@@ -2620,7 +2845,14 @@ var LegalEntityOperations = class {
2620
2845
  listAll(options) {
2621
2846
  return paginate((offset, limit) => this.adapter.listLegalEntities({ ...options, offset, limit }), options);
2622
2847
  }
2623
- /** Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable for audit. */
2848
+ /**
2849
+ * Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable
2850
+ * for audit.
2851
+ *
2852
+ * **Platform accounts only — requires a master API key.** Platform mode is
2853
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2854
+ * https://console.getpeppr.dev/api-keys.
2855
+ */
2624
2856
  async archive(id) {
2625
2857
  return this.adapter.archiveLegalEntity(id);
2626
2858
  }
@@ -2628,6 +2860,10 @@ var LegalEntityOperations = class {
2628
2860
  * Request a sub-tenant attestation (production only). Emails the co-branded
2629
2861
  * confirmation link to the sub-tenant contact and returns the pending status.
2630
2862
  *
2863
+ * **Platform accounts only — requires a master API key.** Platform mode is
2864
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2865
+ * https://console.getpeppr.dev/api-keys.
2866
+ *
2631
2867
  * Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
2632
2868
  * re-issuing mints a fresh token, so a retried call is safe.
2633
2869
  *
@@ -2986,7 +3222,7 @@ var brCo16 = (input) => {
2986
3222
  }
2987
3223
  return [];
2988
3224
  };
2989
- var brS01 = (input) => {
3225
+ var brS05 = (input) => {
2990
3226
  const violations = [];
2991
3227
  if (!input.lines)
2992
3228
  return violations;
@@ -2994,43 +3230,43 @@ var brS01 = (input) => {
2994
3230
  const line = input.lines[i];
2995
3231
  const category = line.vatCategory ?? "S";
2996
3232
  if (category === "S" && (line.vatRate === void 0 || line.vatRate <= 0)) {
2997
- violations.push(violation("BR-S-01", "error", `Line ${i}: standard rate (S) requires vatRate > 0, got ${line.vatRate ?? "undefined"}.`, `lines[${i}].vatRate`));
3233
+ violations.push(violation("BR-S-05", "error", `Line ${i}: standard rate (S) requires vatRate > 0, got ${line.vatRate ?? "undefined"}.`, `lines[${i}].vatRate`));
2998
3234
  }
2999
3235
  }
3000
3236
  return violations;
3001
3237
  };
3002
- var brS05 = (input) => {
3238
+ var brZ05 = (input) => {
3003
3239
  const violations = [];
3004
3240
  if (!input.lines)
3005
3241
  return violations;
3006
3242
  for (let i = 0; i < input.lines.length; i++) {
3007
3243
  const line = input.lines[i];
3008
3244
  if (line.vatCategory === "Z" && line.vatRate !== 0) {
3009
- violations.push(violation("BR-S-05", "error", `Line ${i}: zero-rated (Z) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3245
+ violations.push(violation("BR-Z-05", "error", `Line ${i}: zero-rated (Z) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3010
3246
  }
3011
3247
  }
3012
3248
  return violations;
3013
3249
  };
3014
- var brS06 = (input) => {
3250
+ var brE05 = (input) => {
3015
3251
  const violations = [];
3016
3252
  if (!input.lines)
3017
3253
  return violations;
3018
3254
  for (let i = 0; i < input.lines.length; i++) {
3019
3255
  const line = input.lines[i];
3020
3256
  if (line.vatCategory === "E" && line.vatRate !== 0) {
3021
- violations.push(violation("BR-S-06", "error", `Line ${i}: exempt (E) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3257
+ violations.push(violation("BR-E-05", "error", `Line ${i}: exempt (E) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3022
3258
  }
3023
3259
  }
3024
3260
  return violations;
3025
3261
  };
3026
- var brS08 = (input) => {
3262
+ var brAe05 = (input) => {
3027
3263
  const violations = [];
3028
3264
  if (!input.lines)
3029
3265
  return violations;
3030
3266
  for (let i = 0; i < input.lines.length; i++) {
3031
3267
  const line = input.lines[i];
3032
3268
  if (line.vatCategory === "AE" && line.vatRate !== 0) {
3033
- violations.push(violation("BR-S-08", "error", `Line ${i}: reverse charge (AE) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3269
+ violations.push(violation("BR-AE-05", "error", `Line ${i}: reverse charge (AE) requires vatRate = 0, got ${line.vatRate}.`, `lines[${i}].vatRate`));
3034
3270
  }
3035
3271
  }
3036
3272
  return violations;
@@ -3093,11 +3329,11 @@ var ALL_RULES = [
3093
3329
  brCo13,
3094
3330
  brCo15,
3095
3331
  brCo16,
3096
- // Tax categories (BR-S)
3097
- brS01,
3332
+ // Tax categories (one family per category)
3098
3333
  brS05,
3099
- brS06,
3100
- brS08,
3334
+ brZ05,
3335
+ brE05,
3336
+ brAe05,
3101
3337
  // Peppol-specific
3102
3338
  peppolR004,
3103
3339
  vatCategoryCodes,
@@ -3118,10 +3354,31 @@ function validateSchematron(input) {
3118
3354
  }
3119
3355
  return {
3120
3356
  valid: errors.length === 0,
3357
+ coverage: { rulesChecked: SDK_SCHEMATRON_RULE_IDS.length, ofNetworkFatalRules: "partial" },
3121
3358
  errors,
3122
3359
  warnings
3123
3360
  };
3124
3361
  }
3362
+ var SDK_SCHEMATRON_RULE_IDS = [
3363
+ "BR-02",
3364
+ "BR-03",
3365
+ "BR-06",
3366
+ "BR-07",
3367
+ "BR-08",
3368
+ "BR-09",
3369
+ "BR-10",
3370
+ "BR-CL-17",
3371
+ "BR-CO-10",
3372
+ "BR-CO-13",
3373
+ "BR-CO-15",
3374
+ "BR-CO-16",
3375
+ "BR-S-05",
3376
+ "BR-Z-05",
3377
+ "BR-E-05",
3378
+ "BR-AE-05",
3379
+ "PEPPOL-EN16931-R004",
3380
+ "PEPPOL-EN16931-R080"
3381
+ ];
3125
3382
 
3126
3383
  // src/commands/validate.ts
3127
3384
  function runValidation(input) {
@@ -4051,6 +4308,7 @@ function registerSendCommand(program2) {
4051
4308
  const dashboardUrl = `${DASHBOARD_BASE}/${result.id}`;
4052
4309
  let finalStatus = result.status;
4053
4310
  let timedOut = false;
4311
+ let watchFailed = false;
4054
4312
  if (flags.watch) {
4055
4313
  const onTransition = (s) => {
4056
4314
  if (!flags.quiet && !flags.json) {
@@ -4070,6 +4328,7 @@ function registerSendCommand(program2) {
4070
4328
  const msg = e instanceof Error ? e.message : String(e);
4071
4329
  process.stderr.write(`${pc6.yellow("\u26A0")} Watch error: ${msg}
4072
4330
  `);
4331
+ watchFailed = true;
4073
4332
  }
4074
4333
  if (timedOut) {
4075
4334
  process.stderr.write(
@@ -4090,7 +4349,7 @@ function registerSendCommand(program2) {
4090
4349
  mode
4091
4350
  );
4092
4351
  if (output) process.stdout.write(output + "\n");
4093
- if (finalStatus === "rejected" || finalStatus === "failed" || finalStatus === "no_action") {
4352
+ if (watchFailed || finalStatus === "rejected" || finalStatus === "failed" || finalStatus === "no_action") {
4094
4353
  process.exit(1);
4095
4354
  }
4096
4355
  process.exit(0);