@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/CHANGELOG.md +233 -0
- package/README.md +27 -0
- package/dist/index.js +304 -45
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
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.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(
|
|
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) {
|
|
@@ -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
|
-
|
|
1752
|
-
|
|
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
|
|
1756
|
-
|
|
1757
|
-
|
|
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
|
-
|
|
1762
|
-
|
|
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(
|
|
1902
|
-
const
|
|
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
|
|
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
|
-
/**
|
|
2168
|
-
|
|
2169
|
-
|
|
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
|
-
*
|
|
2206
|
-
*
|
|
2207
|
-
*
|
|
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 (`A` → `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
|
-
*
|
|
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.
|
|
2603
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
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 (
|
|
3097
|
-
brS01,
|
|
3332
|
+
// Tax categories (one family per category)
|
|
3098
3333
|
brS05,
|
|
3099
|
-
|
|
3100
|
-
|
|
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);
|