@beliq/sdk 0.3.0 → 0.4.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/README.md CHANGED
@@ -26,13 +26,33 @@ const account = await beliq.me();
26
26
  const generated = await beliq.generate({
27
27
  standard: 'xrechnung',
28
28
  verify: true,
29
+ // The XRechnung CIUS asks for more than plain EN 16931: a seller contact
30
+ // (BR-DE-2), payment instructions (BR-DE-1), a VAT breakdown (BR-CO-18) and an
31
+ // electronic address for each party. examples/invoice.json is this same shape.
29
32
  invoice: {
30
33
  number: 'INV-2026-001',
31
34
  issueDate: '2026-01-15',
32
35
  currencyCode: 'EUR',
33
- seller: { name: 'Seller GmbH', address: { city: 'Berlin', postalCode: '10115', countryCode: 'DE' } },
34
- buyer: { name: 'Buyer GmbH', address: { city: 'Munich', postalCode: '80331', countryCode: 'DE' } },
36
+ buyerReference: '04011000-12345-06',
37
+ seller: {
38
+ name: 'Seller GmbH',
39
+ vatId: 'DE123456789',
40
+ contactName: 'Anna Muster',
41
+ email: 'billing@seller.example',
42
+ phone: '+49 30 1234567',
43
+ address: { street: 'Hauptstr. 1', city: 'Berlin', postalCode: '10115', countryCode: 'DE' },
44
+ peppol: { schemeId: '9930', id: 'DE123456789' },
45
+ },
46
+ buyer: {
47
+ name: 'Buyer GmbH',
48
+ vatId: 'DE987654321',
49
+ email: 'ap@buyer.example',
50
+ address: { street: 'Marktweg 2', city: 'Munich', postalCode: '80331', countryCode: 'DE' },
51
+ peppol: { schemeId: '9930', id: 'DE987654321' },
52
+ },
35
53
  lines: [{ description: 'Consulting', quantity: 10, unitCode: 'HUR', unitPrice: 100, lineTotal: 1000, vatRate: 19, vatCategoryCode: 'S' }],
54
+ taxSummary: [{ vatCategoryCode: 'S', vatRate: 19, taxableAmount: 1000, taxAmount: 190 }],
55
+ paymentMeans: { typeCode: '58', iban: 'DE89370400440532013000' },
36
56
  totalNetAmount: 1000,
37
57
  totalTaxAmount: 190,
38
58
  totalGrossAmount: 1190,
@@ -88,6 +108,11 @@ costs you a second document.
88
108
  the work may still be running on beliq's side, so retrying risks producing a
89
109
  second document rather than recovering the first.
90
110
 
111
+ A `429` carrying `QUOTA_EXCEEDED` is not retried either. `RATE_LIMITED` and
112
+ `ACCOUNT_THROTTLED` clear on their own, but a spent monthly allowance only returns
113
+ when your billing window turns, so the error is raised straight away and names the
114
+ cause instead of sleeping against it.
115
+
91
116
  The default deadline is generous because beliq runs the full Schematron rule set
92
117
  over each document, and a generate or validate can legitimately take tens of
93
118
  seconds. If you lower it, keep it above the latency you actually see: a deadline
@@ -160,6 +185,7 @@ npm install
160
185
  npm run build # tsup: dual ESM + CJS + d.ts
161
186
  npm run typecheck
162
187
  npm run lint
188
+ npm run scrub:check # no em-dash in any tracked file
163
189
  npm test # unit tests (no network)
164
190
  BELIQ_API_KEY=blq_xxx npm run test:integration # hits the live API; draws quota
165
191
  ```
package/dist/index.cjs CHANGED
@@ -255,6 +255,11 @@ var LIVE_CONVERT_TARGET_FORMATS = [
255
255
 
256
256
  // src/transport.ts
257
257
  var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503]);
258
+ var QUOTA_EXHAUSTED_CODE = "QUOTA_EXCEEDED";
259
+ function isRetryable(status, code) {
260
+ if (!RETRYABLE_STATUSES.has(status)) return false;
261
+ return !(status === 429 && code === QUOTA_EXHAUSTED_CODE);
262
+ }
258
263
  var MAX_RETRY_AFTER_MS = 3e4;
259
264
  var BACKOFF_BASE_MS = 500;
260
265
  function authHeaders(config) {
@@ -302,12 +307,13 @@ async function send(config, req) {
302
307
  });
303
308
  const bytes = new Uint8Array(await res.arrayBuffer());
304
309
  if (res.ok) return { status: res.status, headers: res.headers, bytes };
305
- lastError = errorFromResponse(res.status, bytes);
306
- if (!RETRYABLE_STATUSES.has(res.status) || attempt === config.maxRetries) {
307
- throw lastError;
310
+ const apiError = errorFromResponse(res.status, bytes);
311
+ lastError = apiError;
312
+ if (!isRetryable(apiError.status, apiError.code) || attempt === config.maxRetries) {
313
+ throw apiError;
308
314
  }
309
315
  } catch (err) {
310
- if (err instanceof BeliqApiError && !RETRYABLE_STATUSES.has(err.status)) throw err;
316
+ if (err instanceof BeliqApiError && !isRetryable(err.status, err.code)) throw err;
311
317
  if (controller.signal.aborted) {
312
318
  throw new BeliqApiError(
313
319
  `beliq: request to ${req.path} exceeded ${config.timeoutMs}ms`,