@getpeppr/cli 0.6.0 → 0.7.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 ADDED
@@ -0,0 +1,206 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@getpeppr/cli` are documented here.
4
+
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
+
7
+ ## [0.7.0] — 2026-08-16
8
+
9
+ Rebundle SDK 4.0.0 (GPR-1061). `getpeppr send --watch` could not confirm a
10
+ delivery: it polled every 2 seconds, was handed an invented `submitted` every
11
+ time, and after 60 seconds printed *"Timeout — invoice was sent but delivery not
12
+ confirmed"* on invoices that had in fact been delivered. The status was never
13
+ measured; the SDK was supplying it.
14
+
15
+ The SDK now refuses a status the gateway did not send, so `--watch` stops
16
+ reporting a timeout it cannot justify. When the gateway answers without a
17
+ status, the watch ends immediately with `⚠ Watch error:` and the reason, instead
18
+ of waiting a minute to say nothing.
19
+
20
+ ### Changed
21
+
22
+ - `send --watch` surfaces a gateway protocol fault instead of polling to
23
+ timeout. Minor rather than patch: a script that read the 60-second timeout as
24
+ "not delivered yet" will now see an error much sooner — which is the point,
25
+ since that timeout was never evidence of anything.
26
+ - **`send --watch` exits 1 when the watch itself fails.** It used to print the
27
+ error and exit 0, leaving the send-time `submitted` on stdout: an automation
28
+ gating on the exit code read a broken API as a confirmed delivery. A watch you
29
+ asked for and could not run is not a success.
30
+
31
+ A watch that merely **times out** still exits 0, unchanged. The send worked;
32
+ delivery is simply unconfirmed within the window. That is a different fact
33
+ from "the API answered something the SDK refused to interpret".
34
+
35
+ ## [0.6.0] — 2026-08-12
36
+
37
+ Rebundle SDK 3.2.0 (GPR-1012, GPR-1020). Minor rather than patch: `validate` now
38
+ **fails** documents it used to pass, so a pipeline gating on its exit code will
39
+ start rejecting invoices — which is the point, since those invoices were being
40
+ refused at send time anyway.
41
+
42
+ Measured against the published `0.5.4` on the same file, before and after:
43
+
44
+ | `vatCategory` | 0.5.4 said | 0.6.0 says |
45
+ | --- | --- | --- |
46
+ | `L` (IGIC) | *nothing* — passed silently | `unsupported_vat_category`, explained |
47
+ | `M` (IPSI) | *nothing* — passed silently | `unsupported_vat_category`, explained |
48
+ | `B` (split payment) | `invalid VAT category` | `unsupported_vat_category` |
49
+ | `ae` | `invalid VAT category` | `BR-CL-17` + a note that codes are case-sensitive |
50
+
51
+ ### Changed
52
+
53
+ - `validate` reports unroutable VAT categories instead of passing them. `L` and
54
+ `M` are valid EN 16931 codes our provider has no vocabulary for, so a document
55
+ carrying one is refused with a 422 at send time. `0.5.4` said nothing about
56
+ them, which meant the offline validator's verdict and the gateway's disagreed
57
+ on exactly the case where the developer needed the warning.
58
+ - `validate` no longer emits the rule id `PEPPOL-EN16931-R006`. That id does not
59
+ exist in Peppol rulebook v3.0.20 — the family stops at R005, R007 and R008 —
60
+ so the CLI was citing a rule a reader could not look up. Violations now carry
61
+ `BR-CL-17` (a genuine EN 16931 rule) or `unsupported_vat_category`.
62
+ - `validate` accepts `B` (Italian split payment) as a real VAT category. It was
63
+ reported invalid, which is the more expensive direction of error: telling a
64
+ developer the network refuses a code it accepts closes a corridor that is open.
65
+ - A `vatCategory` of `null` is treated as absent rather than reported as the
66
+ literal string `"null"` — the shape any JSON serialiser emits for an unset
67
+ field, and the shape the gateway has always accepted.
68
+
69
+ ### Unchanged
70
+
71
+ - `lookup`, `login`, `logout` and `send` behave identically.
72
+ - The scheme and identifier fixes from SDK 3.0.0 (`GB:CRN` removal, `GB:VAT` and
73
+ Leitweg-ID patterns) are **not** in this bundle and never were: those symbols
74
+ live in a module the CLI does not import, and tsup tree-shakes them out.
75
+ Verified on the built artifact — `SCHEMES_BY_COUNTRY`, `validatePeppolIdentifier`
76
+ and `LEITWEG` are absent while `buildInvoiceXml` and `validateSchematron` are
77
+ present, so the absence is real and not a broken grep. SDK 3.1.0 was type-only
78
+ and had nothing to bundle.
79
+
80
+ ## [0.5.4] — 2026-07-24
81
+
82
+ ### Changed
83
+
84
+ - Rebundle SDK 2.5.0 (GPR-868): `directory.search()` pagination meta remap, `WebhookEventType` phantom values removed. No CLI command change (`lookup` uses its own directory client).
85
+
86
+ ## [0.5.3] — 2026-07-20
87
+
88
+ ### Changed
89
+
90
+ - `init` templates now target the getpeppr sandbox test receiver, so a scaffolded invoice can be sent successfully on the first try in sandbox (GPR-828).
91
+
92
+ ## [0.5.2] — 2026-07-16
93
+
94
+ ### Changed
95
+
96
+ - Rebundle SDK 2.4.0 (GPR-134): `validate`/`convert` now apply the France country rules (FR-01 SIREN/SIRET checksums with the corrected La Poste model, FR-02 VAT key alphabet, FR-03 DGFiP VAT key formula) locally.
97
+
98
+ ## [0.5.1] — 2026-07-16
99
+
100
+ ### Fixed
101
+
102
+ - Rebundle SDK 2.3.0 (GPR-838): `convert`/`validate` now emit EN 16931 BR-CO-17-compliant UBL — group-level VAT rounding replaced per-line accumulation, which could drift the payable amount by one cent on sub-cent line amounts.
103
+
104
+ ## [0.5.0] — 2026-07-13
105
+
106
+ ### Changed
107
+
108
+ - `send --watch` stops on the terminal `no_action` status (recipient not deliverable) and `send` exits 1 on it (GPR-830). Rebundles SDK 2.1.0 (`invoice.undeliverable` webhook event, `waitFor` terminal semantics).
109
+
110
+ ## [0.4.7] — 2026-06-21
111
+
112
+ ### Changed
113
+
114
+ - Rebundle SDK 2.0.0 (bundle hygiene, no command change).
115
+
116
+ ## [0.4.6] — 2026-06-19
117
+
118
+ ### Changed
119
+
120
+ - Rebundle SDK 1.9.0 (GPR-738: `invoices.list()` blank `number`/`createdAt` fix).
121
+
122
+ ## [0.4.5] — 2026-06-04
123
+
124
+ ### Changed
125
+
126
+ - Rebundle SDK 1.5.2 (directory response alignment).
127
+
128
+ ## [0.4.4] — 2026-06-04
129
+
130
+ ### Changed
131
+
132
+ - Rebundle SDK 1.5.1 (patch train).
133
+
134
+ ## [0.4.3] — 2026-05-24
135
+
136
+ ### Changed
137
+
138
+ - Rebundle SDK 1.5.0 (server validation contract updates).
139
+
140
+ ## [0.4.2] — 2026-05-07
141
+
142
+ ### Fixed
143
+
144
+ - Repaired published package imports (#289) — same packaging fix as SDK 1.4.2.
145
+
146
+ ## [0.4.1] — 2026-05-07
147
+
148
+ ### Changed
149
+
150
+ - Version alignment with SDK 1.4.1, package metadata normalization (#287, #290).
151
+
152
+ ## [0.4.0] — 2026-05-01
153
+
154
+ ### Added
155
+
156
+ - `DirectoryError` class for directory lookup failures (GPR-414 PR-B, #270).
157
+
158
+ ### Fixed
159
+
160
+ - `lookup`/search requests now carry `AbortSignal.timeout(15s)` and an HTTP ok-check — previously a slow Peppol Directory response could hang the command forever.
161
+
162
+ ## [0.3.1] — 2026-04-27
163
+
164
+ ### Changed
165
+
166
+ - README documents the `send`, `login` and `logout` commands.
167
+
168
+ ## [0.3.0] — 2026-04-27
169
+
170
+ ### Added
171
+
172
+ - `send [file]` — send an invoice to the Peppol network via the getpeppr API (`--watch`, `--prod`) (GPR-278).
173
+ - `login` / `logout` — store/remove the API key in `~/.config/getpeppr/credentials.json` (mode 0600).
174
+ - SDK now bundled into the published artifact via tsup `noExternal` (fixes `ERR_UNSUPPORTED_DIR_IMPORT` on user installs). 7 commands total.
175
+
176
+ ## [0.2.2] — 2026-04-09
177
+
178
+ ### Changed
179
+
180
+ - README documents the `init`, `convert` and `lookup` commands.
181
+
182
+ ## [0.2.1] — 2026-04-09
183
+
184
+ ### Changed
185
+
186
+ - Belgian seller templates for `init`.
187
+
188
+ ## [0.2.0] — 2026-04-09
189
+
190
+ ### Added
191
+
192
+ - `init [file]` — scaffold an invoice/credit-note JSON template (GPR-275).
193
+ - `convert <file>` — JSON → Peppol BIS 3.0 UBL XML on stdout (GPR-276).
194
+ - `lookup <peppolId>` — Peppol Directory participant search (GPR-277).
195
+
196
+ ## [0.1.1] — 2026-04-07
197
+
198
+ ### Changed
199
+
200
+ - npm README and website docs page (GPR-159).
201
+
202
+ ## [0.1.0] — 2026-04-07
203
+
204
+ ### Added
205
+
206
+ - Initial release: `getpeppr validate <file>` — offline Peppol invoice validation running the SDK's three validators locally, no account required (GPR-265/266/267). Exit codes: 0 = valid, 1 = validation errors, 2 = fatal.
package/README.md CHANGED
@@ -271,6 +271,16 @@ const result = await peppol.invoices.send(invoice);
271
271
 
272
272
  Sign up at [getpeppr.dev](https://getpeppr.dev) to get your API key.
273
273
 
274
+ ## Release notes
275
+
276
+ `validate` runs the Peppol rules bundled into the version you installed, so its
277
+ verdict can change between releases — which matters most if you gate a pipeline
278
+ on its exit code. Two places tell you what moved:
279
+
280
+ - `CHANGELOG.md`, shipped inside the package (`node_modules/@getpeppr/cli/CHANGELOG.md`).
281
+ - [getpeppr.dev/news](https://getpeppr.dev/news) for the same notes on the web,
282
+ alongside gateway and SDK changes.
283
+
274
284
  ## License
275
285
 
276
286
  MIT
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.0.0";
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) {
@@ -1748,21 +1808,67 @@ function detectMimeType(filename) {
1748
1808
  return "application/octet-stream";
1749
1809
  }
1750
1810
  }
1751
- function parseSendResult(result) {
1752
- const rawStatus = result.status == null ? void 0 : String(result.status);
1811
+ var PROTOCOL_ERROR_BODY_LIMIT = 2e3;
1812
+ function cloneForHook(body) {
1813
+ try {
1814
+ return structuredClone(body);
1815
+ } catch {
1816
+ return "[response body could not be copied for logging]";
1817
+ }
1818
+ }
1819
+ function boundedBody(raw) {
1820
+ let serialised;
1821
+ try {
1822
+ serialised = JSON.stringify(raw) ?? String(raw);
1823
+ } catch {
1824
+ serialised = "[unserialisable response body]";
1825
+ }
1826
+ if (serialised.length <= PROTOCOL_ERROR_BODY_LIMIT)
1827
+ return serialised;
1828
+ let head = serialised.slice(0, PROTOCOL_ERROR_BODY_LIMIT);
1829
+ if (/[\uD800-\uDBFF]$/.test(head))
1830
+ head = head.slice(0, -1);
1831
+ return `${head}\u2026 [truncated, ${serialised.length} chars]`;
1832
+ }
1833
+ function requireRecordBody(raw, surface) {
1834
+ if (isRecord(raw))
1835
+ return raw;
1836
+ 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));
1837
+ }
1838
+ function requireWireStatus(candidate, raw, surface) {
1839
+ if (typeof candidate === "string" && candidate.trim() !== "")
1840
+ return candidate;
1841
+ 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));
1842
+ }
1843
+ function optionalWireId(value) {
1844
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
1845
+ }
1846
+ function parseSendResult(body) {
1847
+ const result = requireRecordBody(body, "this request");
1848
+ const rawStatus = requireWireStatus(result.status, result, "this request");
1753
1849
  const sendResult = {
1754
1850
  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()),
1851
+ status: mapStatus(rawStatus),
1852
+ rawStatus,
1853
+ // A TYPE guard like the identifiers below, not a cast. This line used to
1854
+ // read `as string | undefined`, which types a number as a string and hands
1855
+ // a consumer an AS4 id that never existed.
1856
+ peppolMessageId: optionalWireId(result.peppolMessageId ?? result.peppol_message_id),
1758
1857
  ublXml: result.ublXml,
1759
1858
  warnings: Array.isArray(result.warnings) ? result.warnings : void 0
1760
1859
  };
1761
- if (rawStatus !== void 0)
1762
- sendResult.rawStatus = rawStatus;
1860
+ const createdAt = result.createdAt ?? result.created_at;
1861
+ if (createdAt != null)
1862
+ sendResult.createdAt = String(createdAt);
1763
1863
  const detail = parseStatusDetail(result.detail);
1764
1864
  if (detail)
1765
1865
  sendResult.detail = detail;
1866
+ const submissionId = optionalWireId(result.submissionId);
1867
+ if (submissionId)
1868
+ sendResult.submissionId = submissionId;
1869
+ const providerDocumentId = optionalWireId(result.providerDocumentId);
1870
+ if (providerDocumentId)
1871
+ sendResult.providerDocumentId = providerDocumentId;
1766
1872
  return sendResult;
1767
1873
  }
1768
1874
  function parseDirectoryEntry(result) {
@@ -1898,18 +2004,24 @@ function parseLegalEntity(raw) {
1898
2004
  }
1899
2005
  return le;
1900
2006
  }
1901
- function parseInvoiceSummary(raw) {
1902
- const rawStatus = (raw.state ?? raw.status) == null ? void 0 : String(raw.state ?? raw.status);
2007
+ function parseInvoiceSummary(row) {
2008
+ const raw = requireRecordBody(row, "this invoice row");
2009
+ const rawStatus = requireWireStatus(raw.state ?? raw.status, raw, "this invoice row");
1903
2010
  const summary = {
1904
2011
  id: String(raw.id ?? ""),
1905
2012
  number: String(raw.invoiceNumber ?? raw.number ?? ""),
1906
- status: mapStatus(rawStatus ?? "submitted")
2013
+ status: mapStatus(rawStatus),
2014
+ rawStatus
1907
2015
  };
1908
- if (rawStatus !== void 0)
1909
- summary.rawStatus = rawStatus;
1910
2016
  const detail = parseStatusDetail(raw.detail);
1911
2017
  if (detail)
1912
2018
  summary.detail = detail;
2019
+ const submissionId = optionalWireId(raw.submissionId);
2020
+ if (submissionId)
2021
+ summary.submissionId = submissionId;
2022
+ const providerDocumentId = optionalWireId(raw.providerDocumentId);
2023
+ if (providerDocumentId)
2024
+ summary.providerDocumentId = providerDocumentId;
1913
2025
  if (raw.createdAt != null)
1914
2026
  summary.createdAt = String(raw.createdAt);
1915
2027
  if (typeof raw.isCreditNote === "boolean")
@@ -1991,6 +2103,16 @@ var PeppolValidationError = class extends PeppolError {
1991
2103
  this.name = "PeppolValidationError";
1992
2104
  }
1993
2105
  };
2106
+ var PeppolProtocolError = class extends PeppolError {
2107
+ field;
2108
+ responseBody;
2109
+ constructor(message, field, responseBody) {
2110
+ super(message);
2111
+ this.field = field;
2112
+ this.responseBody = responseBody;
2113
+ this.name = "PeppolProtocolError";
2114
+ }
2115
+ };
1994
2116
  var PeppolApiError = class extends PeppolError {
1995
2117
  statusCode;
1996
2118
  responseBody;
@@ -2026,6 +2148,22 @@ var Peppol = class {
2026
2148
  contacts;
2027
2149
  bankAccounts;
2028
2150
  transports;
2151
+ /**
2152
+ * Sub-tenant Legal Entities — **platform accounts only**.
2153
+ *
2154
+ * Requires a platform account and a **master API key**. With a standard key
2155
+ * every call here fails with 403 `master_key_required`.
2156
+ *
2157
+ * Onboarding your OWN company is not done through this API: your legal entity
2158
+ * is managed in the console, on the Peppol identity page. This surface is for
2159
+ * platforms that onboard their customers as sub-tenants.
2160
+ *
2161
+ * **Getting access:** platform mode is enabled by getpeppr on your account —
2162
+ * email hello@getpeppr.dev to have it switched on. Once it is, you create the
2163
+ * master key yourself at https://console.getpeppr.dev/api-keys.
2164
+ *
2165
+ * @see https://getpeppr.dev/docs/platform/legal-entities/
2166
+ */
2029
2167
  legalEntities;
2030
2168
  constructor(config) {
2031
2169
  if (!config.apiKey) {
@@ -2164,9 +2302,23 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
2164
2302
  listAll(options) {
2165
2303
  return paginate((offset, limit) => this.adapter.listInvoices({ ...options, offset, limit }), options);
2166
2304
  }
2167
- /** Get the status of a sent invoice */
2168
- async getStatus(documentId) {
2169
- return this.adapter.getStatus(documentId);
2305
+ /**
2306
+ * Get the status of a sent invoice.
2307
+ *
2308
+ * @param options.includeEvidence Ask the gateway to read the sending evidence
2309
+ * from the Peppol network so the result carries `peppolMessageId`. Costs one
2310
+ * provider round trip, so it is off by default; if the document has not gone
2311
+ * out yet, or the read fails, the field is simply absent and everything else
2312
+ * is unaffected.
2313
+ *
2314
+ * @example
2315
+ * ```ts
2316
+ * const status = await peppol.invoices.getStatus(id);
2317
+ * const proof = await peppol.invoices.getStatus(id, { includeEvidence: true });
2318
+ * ```
2319
+ */
2320
+ async getStatus(documentId, options) {
2321
+ return this.adapter.getStatus(documentId, options);
2170
2322
  }
2171
2323
  /**
2172
2324
  * Export an invoice in a specific format (e.g., PDF, UBL XML).
@@ -2580,6 +2732,13 @@ var LegalEntityOperations = class {
2580
2732
  /**
2581
2733
  * Create a sub-tenant Legal Entity for one of your customers.
2582
2734
  *
2735
+ * **Platform accounts only — requires a master API key.** Platform mode is
2736
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2737
+ * https://console.getpeppr.dev/api-keys.
2738
+ *
2739
+ * Your own company's legal entity is managed in the console, on the Peppol
2740
+ * identity page; this creates an entity for a customer of yours.
2741
+ *
2583
2742
  * Idempotent on `externalId`: repeated calls with the same `externalId` return
2584
2743
  * the existing entity (HTTP 200) instead of creating a duplicate. Transient 5xx
2585
2744
  * failures are NOT auto-retried unless you pass `options.idempotencyKey`.
@@ -2599,19 +2758,37 @@ var LegalEntityOperations = class {
2599
2758
  return this.adapter.createLegalEntity(input, options);
2600
2759
  }
2601
2760
  /**
2602
- * Fetch a single sub-tenant Legal Entity by id. For production entities the
2603
- * `status` reflects the attestation lifecycle (awaiting_authz → attested → active).
2761
+ * Fetch a single sub-tenant Legal Entity by id.
2762
+ *
2763
+ * **Platform accounts only — requires a master API key.** Platform mode is
2764
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2765
+ * https://console.getpeppr.dev/api-keys.
2766
+ *
2767
+ * For production entities the `status` reflects the attestation lifecycle
2768
+ * (awaiting_authz → attested → active).
2604
2769
  */
2605
2770
  async get(id) {
2606
2771
  return this.adapter.getLegalEntity(id);
2607
2772
  }
2608
- /** List your sub-tenant Legal Entities, newest first. */
2773
+ /**
2774
+ * List your sub-tenant Legal Entities, newest first.
2775
+ *
2776
+ * **Platform accounts only — requires a master API key.** Platform mode is
2777
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2778
+ * https://console.getpeppr.dev/api-keys.
2779
+ *
2780
+ * This lists the customers you have onboarded, never your own legal entity.
2781
+ */
2609
2782
  async list(options) {
2610
2783
  return this.adapter.listLegalEntities(options);
2611
2784
  }
2612
2785
  /**
2613
2786
  * Async iterator over all sub-tenant Legal Entities, handling pagination.
2614
2787
  *
2788
+ * **Platform accounts only — requires a master API key.** Platform mode is
2789
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2790
+ * https://console.getpeppr.dev/api-keys.
2791
+ *
2615
2792
  * @example
2616
2793
  * ```ts
2617
2794
  * for await (const le of peppol.legalEntities.listAll()) console.log(le.id, le.status);
@@ -2620,7 +2797,14 @@ var LegalEntityOperations = class {
2620
2797
  listAll(options) {
2621
2798
  return paginate((offset, limit) => this.adapter.listLegalEntities({ ...options, offset, limit }), options);
2622
2799
  }
2623
- /** Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable for audit. */
2800
+ /**
2801
+ * Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable
2802
+ * for audit.
2803
+ *
2804
+ * **Platform accounts only — requires a master API key.** Platform mode is
2805
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2806
+ * https://console.getpeppr.dev/api-keys.
2807
+ */
2624
2808
  async archive(id) {
2625
2809
  return this.adapter.archiveLegalEntity(id);
2626
2810
  }
@@ -2628,6 +2812,10 @@ var LegalEntityOperations = class {
2628
2812
  * Request a sub-tenant attestation (production only). Emails the co-branded
2629
2813
  * confirmation link to the sub-tenant contact and returns the pending status.
2630
2814
  *
2815
+ * **Platform accounts only — requires a master API key.** Platform mode is
2816
+ * enabled by getpeppr: email hello@getpeppr.dev, then create the key at
2817
+ * https://console.getpeppr.dev/api-keys.
2818
+ *
2631
2819
  * Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
2632
2820
  * re-issuing mints a fresh token, so a retried call is safe.
2633
2821
  *
@@ -4051,6 +4239,7 @@ function registerSendCommand(program2) {
4051
4239
  const dashboardUrl = `${DASHBOARD_BASE}/${result.id}`;
4052
4240
  let finalStatus = result.status;
4053
4241
  let timedOut = false;
4242
+ let watchFailed = false;
4054
4243
  if (flags.watch) {
4055
4244
  const onTransition = (s) => {
4056
4245
  if (!flags.quiet && !flags.json) {
@@ -4070,6 +4259,7 @@ function registerSendCommand(program2) {
4070
4259
  const msg = e instanceof Error ? e.message : String(e);
4071
4260
  process.stderr.write(`${pc6.yellow("\u26A0")} Watch error: ${msg}
4072
4261
  `);
4262
+ watchFailed = true;
4073
4263
  }
4074
4264
  if (timedOut) {
4075
4265
  process.stderr.write(
@@ -4090,7 +4280,7 @@ function registerSendCommand(program2) {
4090
4280
  mode
4091
4281
  );
4092
4282
  if (output) process.stdout.write(output + "\n");
4093
- if (finalStatus === "rejected" || finalStatus === "failed" || finalStatus === "no_action") {
4283
+ if (watchFailed || finalStatus === "rejected" || finalStatus === "failed" || finalStatus === "no_action") {
4094
4284
  process.exit(1);
4095
4285
  }
4096
4286
  process.exit(0);