@getpeppr/cli 0.4.5 → 0.4.7

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
@@ -1016,7 +1016,7 @@ function validateInvoice(input) {
1016
1016
  }
1017
1017
  if (input.roundingAmount !== void 0 && input.roundingAmount !== null) {
1018
1018
  if (input.roundingAmount < -0.99 || input.roundingAmount > 0.99) {
1019
- errors.push(error("roundingAmount", `Rounding amount must be between -0.99 and 0.99, got ${input.roundingAmount}`, void 0, "B2BRouter stores rounding as integer cents (\xB199). Use values like 0.50 or -0.25."));
1019
+ errors.push(error("roundingAmount", `Rounding amount must be between -0.99 and 0.99, got ${input.roundingAmount}`, void 0, "Rounding is stored as integer cents (\xB199). Use values like 0.50 or -0.25."));
1020
1020
  }
1021
1021
  }
1022
1022
  if (!input.buyerReference && !input.orderReference) {
@@ -1059,7 +1059,7 @@ function validateInvoice(input) {
1059
1059
  }
1060
1060
 
1061
1061
  // ../sdk/dist/version.js
1062
- var SDK_VERSION = "1.5.2";
1062
+ var SDK_VERSION = "2.0.0";
1063
1063
 
1064
1064
  // ../sdk/dist/core/client.js
1065
1065
  function findHeaderCaseInsensitive(headers, name) {
@@ -1286,12 +1286,7 @@ var GetpepprAdapter = class {
1286
1286
  const invoices = result.invoices ?? result.data ?? [];
1287
1287
  const meta = result.meta;
1288
1288
  return {
1289
- data: invoices.map((inv) => ({
1290
- id: String(inv.id ?? ""),
1291
- number: String(inv.number ?? ""),
1292
- status: mapStatus(String(inv.state ?? inv.status ?? "submitted")),
1293
- createdAt: inv.created_at ? String(inv.created_at) : void 0
1294
- })),
1289
+ data: invoices.map(parseInvoiceSummary),
1295
1290
  meta: {
1296
1291
  totalCount: Number(meta?.total_count ?? invoices.length),
1297
1292
  offset: Number(meta?.offset ?? options?.offset ?? 0),
@@ -1408,22 +1403,24 @@ var GetpepprAdapter = class {
1408
1403
  params.set("dateTo", options.dateTo);
1409
1404
  const query = params.toString() ? `?${params.toString()}` : "";
1410
1405
  const result = await this.request("GET", `/events${query}`);
1411
- const events = result.events ?? result.data ?? [];
1406
+ const events = result.data ?? [];
1412
1407
  const meta = result.meta;
1413
1408
  return {
1414
1409
  data: events.map((evt) => ({
1415
- name: String(evt.name ?? ""),
1416
- text: String(evt.text ?? ""),
1417
- notes: evt.notes ? String(evt.notes) : void 0,
1418
- createdAt: String(evt.createdAt ?? evt.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
1419
- invoice: evt.invoice,
1420
- contact: evt.contact
1410
+ id: String(evt.id ?? ""),
1411
+ eventType: String(evt.eventType ?? evt.event_type ?? ""),
1412
+ documentId: evt.documentId ?? evt.document_id ?? null,
1413
+ metadata: evt.metadata ?? null,
1414
+ createdAt: String(evt.createdAt ?? evt.created_at ?? "")
1421
1415
  })),
1422
1416
  meta: {
1423
1417
  totalCount: Number(meta?.total_count ?? meta?.totalCount ?? events.length),
1424
1418
  offset: Number(meta?.offset ?? options?.offset ?? 0),
1425
1419
  limit: Number(meta?.limit ?? options?.limit ?? 25),
1426
- hasMore: meta ? Number(meta.total_count ?? meta.totalCount) > Number(meta.offset) + Number(meta.limit) : false,
1420
+ // Prefer the gateway's authoritative has_more; fall back to computing it
1421
+ // from total_count/offset/limit so listAll() paginates correctly even if
1422
+ // a response omits the flag.
1423
+ hasMore: meta?.has_more != null || meta?.hasMore != null ? Boolean(meta.has_more ?? meta.hasMore) : Number(meta?.total_count ?? meta?.totalCount ?? 0) > Number(meta?.offset ?? options?.offset ?? 0) + Number(meta?.limit ?? options?.limit ?? 0),
1427
1424
  truncated: Boolean(meta?.truncated ?? false)
1428
1425
  }
1429
1426
  };
@@ -1491,6 +1488,58 @@ var GetpepprAdapter = class {
1491
1488
  async deleteContact(id) {
1492
1489
  await this.request("DELETE", `/contacts/${id}`);
1493
1490
  }
1491
+ async createLegalEntity(input, options) {
1492
+ const headers = {};
1493
+ if (options?.idempotencyKey)
1494
+ headers["Idempotency-Key"] = options.idempotencyKey;
1495
+ const result = await this.request("POST", "/legal-entities", input, headers);
1496
+ return parseLegalEntity(result);
1497
+ }
1498
+ async getLegalEntity(id) {
1499
+ const result = await this.request("GET", `/legal-entities/${id}`);
1500
+ return parseLegalEntity(result);
1501
+ }
1502
+ async listLegalEntities(options) {
1503
+ const params = new URLSearchParams();
1504
+ if (options?.limit != null)
1505
+ params.set("limit", String(options.limit));
1506
+ if (options?.offset != null)
1507
+ params.set("offset", String(options.offset));
1508
+ const query = params.toString() ? `?${params.toString()}` : "";
1509
+ const result = await this.request("GET", `/legal-entities${query}`);
1510
+ const rows = result.data ?? [];
1511
+ const pagination = result.pagination;
1512
+ return {
1513
+ data: rows.map(parseLegalEntity),
1514
+ meta: {
1515
+ totalCount: Number(pagination?.total_count ?? rows.length),
1516
+ offset: Number(pagination?.offset ?? options?.offset ?? 0),
1517
+ limit: Number(pagination?.limit ?? options?.limit ?? 50),
1518
+ hasMore: Boolean(pagination?.has_more ?? false),
1519
+ truncated: false
1520
+ }
1521
+ };
1522
+ }
1523
+ async archiveLegalEntity(id) {
1524
+ const result = await this.request("DELETE", `/legal-entities/${id}`);
1525
+ return {
1526
+ id: String(result.id ?? id),
1527
+ externalId: result.externalId != null ? String(result.externalId) : null,
1528
+ status: "archived"
1529
+ };
1530
+ }
1531
+ async requestLegalEntityAttestation(id, input, options) {
1532
+ const headers = {};
1533
+ if (options?.idempotencyKey)
1534
+ headers["Idempotency-Key"] = options.idempotencyKey;
1535
+ const result = await this.request("POST", `/legal-entities/${id}/attestation`, input, headers);
1536
+ return {
1537
+ id: String(result.id ?? id),
1538
+ externalId: result.externalId != null ? String(result.externalId) : null,
1539
+ status: String(result.status ?? ""),
1540
+ expiresAt: String(result.expiresAt ?? "")
1541
+ };
1542
+ }
1494
1543
  async listBankAccounts(options) {
1495
1544
  const params = new URLSearchParams();
1496
1545
  if (options?.limit != null)
@@ -1667,6 +1716,44 @@ function parseContact(raw) {
1667
1716
  contact.directoryLastChecked = String(raw.directoryLastChecked);
1668
1717
  return contact;
1669
1718
  }
1719
+ function parseLegalEntity(raw) {
1720
+ const idObj = raw.identifier;
1721
+ const le = {
1722
+ id: String(raw.id ?? ""),
1723
+ externalId: raw.externalId != null ? String(raw.externalId) : null,
1724
+ companyName: raw.companyName != null ? String(raw.companyName) : null,
1725
+ country: raw.country != null ? String(raw.country) : null,
1726
+ identifier: idObj && idObj.scheme != null && idObj.value != null ? { scheme: String(idObj.scheme), value: String(idObj.value) } : null,
1727
+ status: String(raw.status ?? "pending"),
1728
+ environment: String(raw.environment ?? ""),
1729
+ createdAt: String(raw.createdAt ?? "")
1730
+ };
1731
+ if (raw.verificationDetail != null) {
1732
+ le.verificationDetail = raw.verificationDetail;
1733
+ }
1734
+ return le;
1735
+ }
1736
+ function parseInvoiceSummary(raw) {
1737
+ const summary = {
1738
+ id: String(raw.id ?? ""),
1739
+ number: String(raw.invoiceNumber ?? raw.number ?? ""),
1740
+ status: mapStatus(String(raw.state ?? raw.status ?? "submitted"))
1741
+ };
1742
+ if (raw.createdAt != null)
1743
+ summary.createdAt = String(raw.createdAt);
1744
+ if (typeof raw.isCreditNote === "boolean")
1745
+ summary.isCreditNote = raw.isCreditNote;
1746
+ if (raw.recipientName != null)
1747
+ summary.recipientName = String(raw.recipientName);
1748
+ if (raw.totalAmount != null && Number.isFinite(Number(raw.totalAmount))) {
1749
+ summary.totalAmount = Number(raw.totalAmount);
1750
+ }
1751
+ if (raw.currency != null)
1752
+ summary.currency = String(raw.currency);
1753
+ if (raw.environment != null)
1754
+ summary.environment = String(raw.environment);
1755
+ return summary;
1756
+ }
1670
1757
  function parseBankAccount(raw) {
1671
1758
  const account = {
1672
1759
  id: String(raw.id ?? ""),
@@ -1745,6 +1832,19 @@ var PeppolApiError = class extends PeppolError {
1745
1832
  this.name = "PeppolApiError";
1746
1833
  this.retryAfterMs = retryAfterMs;
1747
1834
  }
1835
+ /**
1836
+ * The gateway's machine-readable error code, parsed from the JSON response body
1837
+ * (e.g. "le_cap_exceeded", "identifier_immutable", "legal_entity_locked", "forbidden").
1838
+ * Returns undefined when the body is not JSON or carries no string `code`.
1839
+ */
1840
+ get code() {
1841
+ try {
1842
+ const parsed = JSON.parse(this.responseBody);
1843
+ return typeof parsed?.code === "string" ? parsed.code : void 0;
1844
+ } catch {
1845
+ return void 0;
1846
+ }
1847
+ }
1748
1848
  };
1749
1849
  var Peppol = class {
1750
1850
  adapter;
@@ -1755,6 +1855,7 @@ var Peppol = class {
1755
1855
  contacts;
1756
1856
  bankAccounts;
1757
1857
  transports;
1858
+ legalEntities;
1758
1859
  constructor(config) {
1759
1860
  if (!config.apiKey) {
1760
1861
  throw new PeppolError("API key is required. Sign up at https://console.getpeppr.dev to get your sandbox key.");
@@ -1767,6 +1868,7 @@ var Peppol = class {
1767
1868
  this.contacts = new ContactOperations(this.adapter);
1768
1869
  this.bankAccounts = new BankAccountOperations(this.adapter);
1769
1870
  this.transports = new TransportOperations(this.adapter);
1871
+ this.legalEntities = new LegalEntityOperations(this.adapter);
1770
1872
  }
1771
1873
  /**
1772
1874
  * Validate an invoice without sending it.
@@ -1812,7 +1914,7 @@ var InvoiceOperations = class {
1812
1914
  }
1813
1915
  /**
1814
1916
  * Create a draft invoice without sending it.
1815
- * Validates input client-side, then creates the invoice on B2BRouter.
1917
+ * Validates input client-side, then creates the invoice via the gateway.
1816
1918
  * Use `sendById()` to send the draft when ready.
1817
1919
  *
1818
1920
  * @example
@@ -1931,7 +2033,7 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
1931
2033
  /**
1932
2034
  * Import an invoice from a file (XML, PDF, JSON).
1933
2035
  * The file is base64-encoded and sent to the gateway, which forwards it
1934
- * as multipart/form-data to B2BRouter's import endpoint.
2036
+ * as multipart/form-data to the provider's import endpoint.
1935
2037
  *
1936
2038
  * @example
1937
2039
  * ```ts
@@ -1993,7 +2095,7 @@ ${validation.errors.map((e) => ` - ${e.field}: ${e.message}${e.suggestion ? ` (
1993
2095
  }
1994
2096
  /**
1995
2097
  * Transition an invoice to a new state.
1996
- * B2BRouter validates the state machine — invalid transitions return an error.
2098
+ * The gateway validates the state machine — invalid transitions return an error.
1997
2099
  *
1998
2100
  * @example
1999
2101
  * ```ts
@@ -2288,6 +2390,74 @@ var ContactOperations = class {
2288
2390
  return paginate((offset, limit) => this.adapter.listContacts({ ...options, offset, limit }), options);
2289
2391
  }
2290
2392
  };
2393
+ var LegalEntityOperations = class {
2394
+ adapter;
2395
+ constructor(adapter) {
2396
+ this.adapter = adapter;
2397
+ }
2398
+ /**
2399
+ * Create a sub-tenant Legal Entity for one of your customers.
2400
+ *
2401
+ * Idempotent on `externalId`: repeated calls with the same `externalId` return
2402
+ * the existing entity (HTTP 200) instead of creating a duplicate. Transient 5xx
2403
+ * failures are NOT auto-retried unless you pass `options.idempotencyKey`.
2404
+ *
2405
+ * @example
2406
+ * ```ts
2407
+ * const le = await peppol.legalEntities.create({
2408
+ * externalId: "tenant-42",
2409
+ * companyName: "Acme Health AB",
2410
+ * country: "SE",
2411
+ * address: { line1: "Storgatan 1", city: "Stockholm", zip: "11122" },
2412
+ * identifier: { scheme: "0007", value: "5560000001" },
2413
+ * }, { idempotencyKey: "tenant-42-create" });
2414
+ * ```
2415
+ */
2416
+ async create(input, options) {
2417
+ return this.adapter.createLegalEntity(input, options);
2418
+ }
2419
+ /**
2420
+ * Fetch a single sub-tenant Legal Entity by id. For production entities the
2421
+ * `status` reflects the attestation lifecycle (awaiting_authz → attested → active).
2422
+ */
2423
+ async get(id) {
2424
+ return this.adapter.getLegalEntity(id);
2425
+ }
2426
+ /** List your sub-tenant Legal Entities, newest first. */
2427
+ async list(options) {
2428
+ return this.adapter.listLegalEntities(options);
2429
+ }
2430
+ /**
2431
+ * Async iterator over all sub-tenant Legal Entities, handling pagination.
2432
+ *
2433
+ * @example
2434
+ * ```ts
2435
+ * for await (const le of peppol.legalEntities.listAll()) console.log(le.id, le.status);
2436
+ * ```
2437
+ */
2438
+ listAll(options) {
2439
+ return paginate((offset, limit) => this.adapter.listLegalEntities({ ...options, offset, limit }), options);
2440
+ }
2441
+ /** Archive (soft-delete) a sub-tenant Legal Entity. The id stays resolvable for audit. */
2442
+ async archive(id) {
2443
+ return this.adapter.archiveLegalEntity(id);
2444
+ }
2445
+ /**
2446
+ * Request a sub-tenant attestation (production only). Emails the co-branded
2447
+ * confirmation link to the sub-tenant contact and returns the pending status.
2448
+ *
2449
+ * Transient failures are NOT auto-retried unless you pass `options.idempotencyKey`;
2450
+ * re-issuing mints a fresh token, so a retried call is safe.
2451
+ *
2452
+ * @example
2453
+ * ```ts
2454
+ * await peppol.legalEntities.requestAttestation(le.id, { contactEmail: "owner@acme.example" });
2455
+ * ```
2456
+ */
2457
+ async requestAttestation(id, input, options) {
2458
+ return this.adapter.requestLegalEntityAttestation(id, input, options);
2459
+ }
2460
+ };
2291
2461
  var BankAccountOperations = class {
2292
2462
  adapter;
2293
2463
  constructor(adapter) {
@@ -2917,6 +3087,11 @@ function registerInitCommand(program2) {
2917
3087
  1. Edit the file with your invoice data
2918
3088
  2. Validate: getpeppr validate ${filename}
2919
3089
  3. Convert to XML: getpeppr convert ${filename}
3090
+ 4. Send: getpeppr send ${filename}
3091
+
3092
+ ${pc2.dim("Sandbox note:")} this template includes VAT. To send it on a sandbox
3093
+ account, first register your VAT in Settings \u2192 Peppol Identity, or set each
3094
+ line's "vatCategory" to "O" (outside the scope of VAT) for a no-VAT test send.
2920
3095
  `);
2921
3096
  process.exit(0);
2922
3097
  }