@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 +206 -0
- package/README.md +10 -0
- package/dist/index.js +218 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
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 = "
|
|
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(
|
|
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
|
-
|
|
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
|
|
1389
|
-
const
|
|
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
|
|
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(
|
|
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
|
-
|
|
1752
|
-
|
|
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
|
|
1756
|
-
|
|
1757
|
-
|
|
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
|
-
|
|
1762
|
-
|
|
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(
|
|
1902
|
-
const
|
|
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
|
|
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
|
-
/**
|
|
2168
|
-
|
|
2169
|
-
|
|
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.
|
|
2603
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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);
|