@beliq/sdk 0.2.0 → 0.3.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
@@ -64,6 +64,36 @@ const beliq = new Beliq({ apiKey: process.env.BELIQ_API_KEY! });
64
64
  if (!beliq.livemode) console.warn('running against the sandbox');
65
65
  ```
66
66
 
67
+ ## Timeouts and retries
68
+
69
+ The client retries transient failures for you, so you do not have to reimplement
70
+ backoff around it.
71
+
72
+ ```ts
73
+ new Beliq({
74
+ apiKey: 'blq_live_...',
75
+ timeoutMs: 90_000, // per-attempt deadline (default)
76
+ maxRetries: 3, // extra attempts after the first (default)
77
+ });
78
+ ```
79
+
80
+ Both defaults are exported as `DEFAULT_TIMEOUT_MS` and `DEFAULT_MAX_RETRIES`, so
81
+ you can derive from them instead of repeating the numbers.
82
+
83
+ Only `429`, `502` and `503` are retried, honouring the server's `Retry-After`
84
+ with jitter. beliq refunds the document's quota unit on a `503`, so a retry never
85
+ costs you a second document.
86
+
87
+ `504` and this client's own timeout are deliberately **not** retried: both mean
88
+ the work may still be running on beliq's side, so retrying risks producing a
89
+ second document rather than recovering the first.
90
+
91
+ The default deadline is generous because beliq runs the full Schematron rule set
92
+ over each document, and a generate or validate can legitimately take tens of
93
+ seconds. If you lower it, keep it above the latency you actually see: a deadline
94
+ shorter than the server's own turns completed work into an unknown outcome. Set
95
+ `timeoutMs: 0` to disable it, and `maxRetries: 0` to handle retrying yourself.
96
+
67
97
  ## API
68
98
 
69
99
  | Method | Endpoint | Input | Returns |
@@ -0,0 +1,85 @@
1
+ type PlainObject = Record<string, unknown>;
2
+ declare function isPlainObject(value: unknown): value is PlainObject;
3
+ /** Deep-merge `source` into `target` (source wins). Arrays and scalars overwrite. */
4
+ declare function mergeDeep(target: PlainObject, source: PlainObject): PlainObject;
5
+ /** Drop undefined/empty entries so optional query params are omitted, not sent blank. */
6
+ declare function compactQuery(query: PlainObject): PlainObject;
7
+ /** Document bytes a caller may pass to validate/parse/convert. */
8
+ type DocumentInput = string | Uint8Array | ArrayBuffer | ArrayBufferView;
9
+ declare function toBytes(input: DocumentInput): Uint8Array;
10
+ declare function decodeUtf8(bytes: Uint8Array): string;
11
+ /** Sniff `application/pdf` vs `application/xml` from the leading bytes. */
12
+ declare function sniffContentType(bytes: Uint8Array): string;
13
+
14
+ declare const DEFAULT_BASE_URL = "https://api.beliq.eu";
15
+ /**
16
+ * Per-attempt deadline. Sits above the API's own worst case so the server is
17
+ * always the one to answer: a client that gives up first abandons work that is
18
+ * still running, and cannot tell whether the document was produced.
19
+ */
20
+ declare const DEFAULT_TIMEOUT_MS = 90000;
21
+ /** Extra attempts after the first, for 429 / 502 / 503 only. */
22
+ declare const DEFAULT_MAX_RETRIES = 3;
23
+ declare const LIVE_GENERATE_STANDARDS: readonly ["xrechnung", "zugferd", "facturx", "peppol-bis"];
24
+ /** A named generate target: the API `standard` plus the `profile`/`facturxProfile`/`output` it needs. */
25
+ interface GeneratePreset {
26
+ id: string;
27
+ label: string;
28
+ standard: (typeof LIVE_GENERATE_STANDARDS)[number];
29
+ output: 'xml' | 'pdf';
30
+ /** API `profile`; omitted lets the engine pick the standard's default. */
31
+ profile?: string;
32
+ /** API `facturxProfile`; Factur-X / ZUGFeRD only. */
33
+ facturxProfile?: string;
34
+ }
35
+ /**
36
+ * Named generate targets surfaced to end users (connector dropdowns), mirroring
37
+ * the public set on beliq.eu's own generator. NLCIUS is a Peppol BIS profile,
38
+ * not a standalone standard, so it is reachable here rather than through
39
+ * LIVE_GENERATE_STANDARDS or the Factur-X-only LIVE_PROFILES.
40
+ */
41
+ declare const LIVE_GENERATE_PRESETS: readonly GeneratePreset[];
42
+ /**
43
+ * The Factur-X granularity values offered publicly. Kept as the flat list the
44
+ * hybrid-PDF family shares; anything choosing a profile for a specific standard
45
+ * wants LIVE_PROFILES_BY_STANDARD instead, which is what the API enforces.
46
+ */
47
+ declare const LIVE_PROFILES: readonly ["basicwl", "en16931", "extended", "extended-ctc-fr"];
48
+ /**
49
+ * Which profiles each standard accepts, publicly-offered values only.
50
+ *
51
+ * `profile` is not a free enum: the engine pins it per standard and answers a
52
+ * pair outside the table with `422 PROFILE_STANDARD_MISMATCH`
53
+ * (beliq-engine `app/routes/generate.py`, ALLOWED_PROFILES_FOR_STANDARD). A
54
+ * surface that offers one flat profile list therefore offers values that cannot
55
+ * succeed: none of the Factur-X granularity values is legal for `xrechnung` or
56
+ * `peppol-bis`, and `extended-ctc-fr` is the AFNOR XP Z12-012 France CTC overlay
57
+ * with no ZUGFeRD-branded counterpart.
58
+ *
59
+ * Narrower than the engine's own table in two places, both deliberate: the
60
+ * `minimum` and `basic` Factur-X profiles are engine-supported but withheld
61
+ * (FNFE-MPE source gating, mirroring beliq-types SUPPORTED_FACTURX_PROFILE_IDS),
62
+ * and the standards outside LIVE_GENERATE_STANDARDS are absent entirely.
63
+ *
64
+ * `scripts/check-profile-drift.mjs` compares this against the engine's table.
65
+ */
66
+ declare const LIVE_PROFILES_BY_STANDARD: {
67
+ readonly xrechnung: readonly ["xrechnung"];
68
+ readonly 'peppol-bis': readonly ["peppol", "romania-ro-cius", "netherlands-nlcius"];
69
+ readonly zugferd: readonly ["basicwl", "en16931", "extended"];
70
+ readonly facturx: readonly ["basicwl", "en16931", "extended", "extended-ctc-fr"];
71
+ };
72
+ /** The profiles a caller may choose for `standard`; empty for an unknown standard. */
73
+ declare function profilesForStandard(standard: string): readonly string[];
74
+ /**
75
+ * Whether `profile` is legal for `standard`. An unknown standard passes: the API
76
+ * is the authority on values this list does not carry, and a client-side guess
77
+ * would refuse a request the server would have accepted.
78
+ */
79
+ declare function isProfileAllowedForStandard(standard: string, profile: string): boolean;
80
+ declare const LIVE_VALIDATE_FORMATS: readonly ["auto", "cii", "ubl"];
81
+ declare const LIVE_PARSE_FORMATS: readonly ["auto", "cii", "ubl"];
82
+ declare const LIVE_CONVERT_SOURCE_FORMATS: readonly ["auto", "cii", "ubl", "zugferd", "facturx", "xrechnung", "peppol-bis"];
83
+ declare const LIVE_CONVERT_TARGET_FORMATS: readonly ["cii", "ubl", "zugferd", "facturx", "xrechnung", "peppol-bis"];
84
+
85
+ export { DEFAULT_BASE_URL as D, type GeneratePreset as G, LIVE_CONVERT_SOURCE_FORMATS as L, type PlainObject as P, type DocumentInput as a, LIVE_CONVERT_TARGET_FORMATS as b, LIVE_GENERATE_PRESETS as c, LIVE_GENERATE_STANDARDS as d, LIVE_PARSE_FORMATS as e, LIVE_PROFILES as f, LIVE_PROFILES_BY_STANDARD as g, LIVE_VALIDATE_FORMATS as h, compactQuery as i, decodeUtf8 as j, isPlainObject as k, isProfileAllowedForStandard as l, mergeDeep as m, DEFAULT_MAX_RETRIES as n, DEFAULT_TIMEOUT_MS as o, profilesForStandard as p, sniffContentType as s, toBytes as t };
@@ -0,0 +1,85 @@
1
+ type PlainObject = Record<string, unknown>;
2
+ declare function isPlainObject(value: unknown): value is PlainObject;
3
+ /** Deep-merge `source` into `target` (source wins). Arrays and scalars overwrite. */
4
+ declare function mergeDeep(target: PlainObject, source: PlainObject): PlainObject;
5
+ /** Drop undefined/empty entries so optional query params are omitted, not sent blank. */
6
+ declare function compactQuery(query: PlainObject): PlainObject;
7
+ /** Document bytes a caller may pass to validate/parse/convert. */
8
+ type DocumentInput = string | Uint8Array | ArrayBuffer | ArrayBufferView;
9
+ declare function toBytes(input: DocumentInput): Uint8Array;
10
+ declare function decodeUtf8(bytes: Uint8Array): string;
11
+ /** Sniff `application/pdf` vs `application/xml` from the leading bytes. */
12
+ declare function sniffContentType(bytes: Uint8Array): string;
13
+
14
+ declare const DEFAULT_BASE_URL = "https://api.beliq.eu";
15
+ /**
16
+ * Per-attempt deadline. Sits above the API's own worst case so the server is
17
+ * always the one to answer: a client that gives up first abandons work that is
18
+ * still running, and cannot tell whether the document was produced.
19
+ */
20
+ declare const DEFAULT_TIMEOUT_MS = 90000;
21
+ /** Extra attempts after the first, for 429 / 502 / 503 only. */
22
+ declare const DEFAULT_MAX_RETRIES = 3;
23
+ declare const LIVE_GENERATE_STANDARDS: readonly ["xrechnung", "zugferd", "facturx", "peppol-bis"];
24
+ /** A named generate target: the API `standard` plus the `profile`/`facturxProfile`/`output` it needs. */
25
+ interface GeneratePreset {
26
+ id: string;
27
+ label: string;
28
+ standard: (typeof LIVE_GENERATE_STANDARDS)[number];
29
+ output: 'xml' | 'pdf';
30
+ /** API `profile`; omitted lets the engine pick the standard's default. */
31
+ profile?: string;
32
+ /** API `facturxProfile`; Factur-X / ZUGFeRD only. */
33
+ facturxProfile?: string;
34
+ }
35
+ /**
36
+ * Named generate targets surfaced to end users (connector dropdowns), mirroring
37
+ * the public set on beliq.eu's own generator. NLCIUS is a Peppol BIS profile,
38
+ * not a standalone standard, so it is reachable here rather than through
39
+ * LIVE_GENERATE_STANDARDS or the Factur-X-only LIVE_PROFILES.
40
+ */
41
+ declare const LIVE_GENERATE_PRESETS: readonly GeneratePreset[];
42
+ /**
43
+ * The Factur-X granularity values offered publicly. Kept as the flat list the
44
+ * hybrid-PDF family shares; anything choosing a profile for a specific standard
45
+ * wants LIVE_PROFILES_BY_STANDARD instead, which is what the API enforces.
46
+ */
47
+ declare const LIVE_PROFILES: readonly ["basicwl", "en16931", "extended", "extended-ctc-fr"];
48
+ /**
49
+ * Which profiles each standard accepts, publicly-offered values only.
50
+ *
51
+ * `profile` is not a free enum: the engine pins it per standard and answers a
52
+ * pair outside the table with `422 PROFILE_STANDARD_MISMATCH`
53
+ * (beliq-engine `app/routes/generate.py`, ALLOWED_PROFILES_FOR_STANDARD). A
54
+ * surface that offers one flat profile list therefore offers values that cannot
55
+ * succeed: none of the Factur-X granularity values is legal for `xrechnung` or
56
+ * `peppol-bis`, and `extended-ctc-fr` is the AFNOR XP Z12-012 France CTC overlay
57
+ * with no ZUGFeRD-branded counterpart.
58
+ *
59
+ * Narrower than the engine's own table in two places, both deliberate: the
60
+ * `minimum` and `basic` Factur-X profiles are engine-supported but withheld
61
+ * (FNFE-MPE source gating, mirroring beliq-types SUPPORTED_FACTURX_PROFILE_IDS),
62
+ * and the standards outside LIVE_GENERATE_STANDARDS are absent entirely.
63
+ *
64
+ * `scripts/check-profile-drift.mjs` compares this against the engine's table.
65
+ */
66
+ declare const LIVE_PROFILES_BY_STANDARD: {
67
+ readonly xrechnung: readonly ["xrechnung"];
68
+ readonly 'peppol-bis': readonly ["peppol", "romania-ro-cius", "netherlands-nlcius"];
69
+ readonly zugferd: readonly ["basicwl", "en16931", "extended"];
70
+ readonly facturx: readonly ["basicwl", "en16931", "extended", "extended-ctc-fr"];
71
+ };
72
+ /** The profiles a caller may choose for `standard`; empty for an unknown standard. */
73
+ declare function profilesForStandard(standard: string): readonly string[];
74
+ /**
75
+ * Whether `profile` is legal for `standard`. An unknown standard passes: the API
76
+ * is the authority on values this list does not carry, and a client-side guess
77
+ * would refuse a request the server would have accepted.
78
+ */
79
+ declare function isProfileAllowedForStandard(standard: string, profile: string): boolean;
80
+ declare const LIVE_VALIDATE_FORMATS: readonly ["auto", "cii", "ubl"];
81
+ declare const LIVE_PARSE_FORMATS: readonly ["auto", "cii", "ubl"];
82
+ declare const LIVE_CONVERT_SOURCE_FORMATS: readonly ["auto", "cii", "ubl", "zugferd", "facturx", "xrechnung", "peppol-bis"];
83
+ declare const LIVE_CONVERT_TARGET_FORMATS: readonly ["cii", "ubl", "zugferd", "facturx", "xrechnung", "peppol-bis"];
84
+
85
+ export { DEFAULT_BASE_URL as D, type GeneratePreset as G, LIVE_CONVERT_SOURCE_FORMATS as L, type PlainObject as P, type DocumentInput as a, LIVE_CONVERT_TARGET_FORMATS as b, LIVE_GENERATE_PRESETS as c, LIVE_GENERATE_STANDARDS as d, LIVE_PARSE_FORMATS as e, LIVE_PROFILES as f, LIVE_PROFILES_BY_STANDARD as g, LIVE_VALIDATE_FORMATS as h, compactQuery as i, decodeUtf8 as j, isPlainObject as k, isProfileAllowedForStandard as l, mergeDeep as m, DEFAULT_MAX_RETRIES as n, DEFAULT_TIMEOUT_MS as o, profilesForStandard as p, sniffContentType as s, toBytes as t };
package/dist/helpers.cjs CHANGED
@@ -27,12 +27,15 @@ __export(helpers_exports, {
27
27
  LIVE_GENERATE_STANDARDS: () => LIVE_GENERATE_STANDARDS,
28
28
  LIVE_PARSE_FORMATS: () => LIVE_PARSE_FORMATS,
29
29
  LIVE_PROFILES: () => LIVE_PROFILES,
30
+ LIVE_PROFILES_BY_STANDARD: () => LIVE_PROFILES_BY_STANDARD,
30
31
  LIVE_VALIDATE_FORMATS: () => LIVE_VALIDATE_FORMATS,
31
32
  buildRequest: () => buildRequest,
32
33
  compactQuery: () => compactQuery,
33
34
  decodeUtf8: () => decodeUtf8,
34
35
  isPlainObject: () => isPlainObject,
36
+ isProfileAllowedForStandard: () => isProfileAllowedForStandard,
35
37
  mergeDeep: () => mergeDeep,
38
+ profilesForStandard: () => profilesForStandard,
36
39
  sniffContentType: () => sniffContentType,
37
40
  toBytes: () => toBytes
38
41
  });
@@ -171,6 +174,19 @@ var LIVE_GENERATE_PRESETS = [
171
174
  { id: "nlcius", label: "NLCIUS", standard: "peppol-bis", output: "xml", profile: "netherlands-nlcius" }
172
175
  ];
173
176
  var LIVE_PROFILES = ["basicwl", "en16931", "extended", "extended-ctc-fr"];
177
+ var LIVE_PROFILES_BY_STANDARD = {
178
+ xrechnung: ["xrechnung"],
179
+ "peppol-bis": ["peppol", "romania-ro-cius", "netherlands-nlcius"],
180
+ zugferd: ["basicwl", "en16931", "extended"],
181
+ facturx: ["basicwl", "en16931", "extended", "extended-ctc-fr"]
182
+ };
183
+ function profilesForStandard(standard) {
184
+ return LIVE_PROFILES_BY_STANDARD[standard] ?? [];
185
+ }
186
+ function isProfileAllowedForStandard(standard, profile) {
187
+ const allowed = profilesForStandard(standard);
188
+ return allowed.length === 0 || allowed.includes(profile);
189
+ }
174
190
  var LIVE_VALIDATE_FORMATS = ["auto", "cii", "ubl"];
175
191
  var LIVE_PARSE_FORMATS = ["auto", "cii", "ubl"];
176
192
  var LIVE_CONVERT_SOURCE_FORMATS = [
@@ -199,12 +215,15 @@ var LIVE_CONVERT_TARGET_FORMATS = [
199
215
  LIVE_GENERATE_STANDARDS,
200
216
  LIVE_PARSE_FORMATS,
201
217
  LIVE_PROFILES,
218
+ LIVE_PROFILES_BY_STANDARD,
202
219
  LIVE_VALIDATE_FORMATS,
203
220
  buildRequest,
204
221
  compactQuery,
205
222
  decodeUtf8,
206
223
  isPlainObject,
224
+ isProfileAllowedForStandard,
207
225
  mergeDeep,
226
+ profilesForStandard,
208
227
  sniffContentType,
209
228
  toBytes
210
229
  });
@@ -1,5 +1,5 @@
1
- import { P as PlainObject } from './constants-D3j_o7th.cjs';
2
- export { D as DEFAULT_BASE_URL, a as DocumentInput, G as GeneratePreset, L as LIVE_CONVERT_SOURCE_FORMATS, b as LIVE_CONVERT_TARGET_FORMATS, c as LIVE_GENERATE_PRESETS, d as LIVE_GENERATE_STANDARDS, e as LIVE_PARSE_FORMATS, f as LIVE_PROFILES, g as LIVE_VALIDATE_FORMATS, h as compactQuery, i as decodeUtf8, j as isPlainObject, m as mergeDeep, s as sniffContentType, t as toBytes } from './constants-D3j_o7th.cjs';
1
+ import { P as PlainObject } from './constants-BZuNzqbV.cjs';
2
+ export { D as DEFAULT_BASE_URL, a as DocumentInput, G as GeneratePreset, L as LIVE_CONVERT_SOURCE_FORMATS, b as LIVE_CONVERT_TARGET_FORMATS, c as LIVE_GENERATE_PRESETS, d as LIVE_GENERATE_STANDARDS, e as LIVE_PARSE_FORMATS, f as LIVE_PROFILES, g as LIVE_PROFILES_BY_STANDARD, h as LIVE_VALIDATE_FORMATS, i as compactQuery, j as decodeUtf8, k as isPlainObject, l as isProfileAllowedForStandard, m as mergeDeep, p as profilesForStandard, s as sniffContentType, t as toBytes } from './constants-BZuNzqbV.cjs';
3
3
 
4
4
  type Operation = 'me' | 'generate' | 'validate' | 'parse' | 'convert';
5
5
  /** 'json' => parse the `{ success, data }` envelope; 'binary' => return raw bytes. */
package/dist/helpers.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as PlainObject } from './constants-D3j_o7th.js';
2
- export { D as DEFAULT_BASE_URL, a as DocumentInput, G as GeneratePreset, L as LIVE_CONVERT_SOURCE_FORMATS, b as LIVE_CONVERT_TARGET_FORMATS, c as LIVE_GENERATE_PRESETS, d as LIVE_GENERATE_STANDARDS, e as LIVE_PARSE_FORMATS, f as LIVE_PROFILES, g as LIVE_VALIDATE_FORMATS, h as compactQuery, i as decodeUtf8, j as isPlainObject, m as mergeDeep, s as sniffContentType, t as toBytes } from './constants-D3j_o7th.js';
1
+ import { P as PlainObject } from './constants-BZuNzqbV.js';
2
+ export { D as DEFAULT_BASE_URL, a as DocumentInput, G as GeneratePreset, L as LIVE_CONVERT_SOURCE_FORMATS, b as LIVE_CONVERT_TARGET_FORMATS, c as LIVE_GENERATE_PRESETS, d as LIVE_GENERATE_STANDARDS, e as LIVE_PARSE_FORMATS, f as LIVE_PROFILES, g as LIVE_PROFILES_BY_STANDARD, h as LIVE_VALIDATE_FORMATS, i as compactQuery, j as decodeUtf8, k as isPlainObject, l as isProfileAllowedForStandard, m as mergeDeep, p as profilesForStandard, s as sniffContentType, t as toBytes } from './constants-BZuNzqbV.js';
3
3
 
4
4
  type Operation = 'me' | 'generate' | 'validate' | 'parse' | 'convert';
5
5
  /** 'json' => parse the `{ success, data }` envelope; 'binary' => return raw bytes. */
package/dist/helpers.js CHANGED
@@ -131,6 +131,19 @@ var LIVE_GENERATE_PRESETS = [
131
131
  { id: "nlcius", label: "NLCIUS", standard: "peppol-bis", output: "xml", profile: "netherlands-nlcius" }
132
132
  ];
133
133
  var LIVE_PROFILES = ["basicwl", "en16931", "extended", "extended-ctc-fr"];
134
+ var LIVE_PROFILES_BY_STANDARD = {
135
+ xrechnung: ["xrechnung"],
136
+ "peppol-bis": ["peppol", "romania-ro-cius", "netherlands-nlcius"],
137
+ zugferd: ["basicwl", "en16931", "extended"],
138
+ facturx: ["basicwl", "en16931", "extended", "extended-ctc-fr"]
139
+ };
140
+ function profilesForStandard(standard) {
141
+ return LIVE_PROFILES_BY_STANDARD[standard] ?? [];
142
+ }
143
+ function isProfileAllowedForStandard(standard, profile) {
144
+ const allowed = profilesForStandard(standard);
145
+ return allowed.length === 0 || allowed.includes(profile);
146
+ }
134
147
  var LIVE_VALIDATE_FORMATS = ["auto", "cii", "ubl"];
135
148
  var LIVE_PARSE_FORMATS = ["auto", "cii", "ubl"];
136
149
  var LIVE_CONVERT_SOURCE_FORMATS = [
@@ -158,12 +171,15 @@ export {
158
171
  LIVE_GENERATE_STANDARDS,
159
172
  LIVE_PARSE_FORMATS,
160
173
  LIVE_PROFILES,
174
+ LIVE_PROFILES_BY_STANDARD,
161
175
  LIVE_VALIDATE_FORMATS,
162
176
  buildRequest,
163
177
  compactQuery,
164
178
  decodeUtf8,
165
179
  isPlainObject,
180
+ isProfileAllowedForStandard,
166
181
  mergeDeep,
182
+ profilesForStandard,
167
183
  sniffContentType,
168
184
  toBytes
169
185
  };
package/dist/index.cjs CHANGED
@@ -23,13 +23,18 @@ __export(index_exports, {
23
23
  Beliq: () => Beliq,
24
24
  BeliqApiError: () => BeliqApiError,
25
25
  DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
26
+ DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
27
+ DEFAULT_TIMEOUT_MS: () => DEFAULT_TIMEOUT_MS,
26
28
  LIVE_CONVERT_SOURCE_FORMATS: () => LIVE_CONVERT_SOURCE_FORMATS,
27
29
  LIVE_CONVERT_TARGET_FORMATS: () => LIVE_CONVERT_TARGET_FORMATS,
28
30
  LIVE_GENERATE_PRESETS: () => LIVE_GENERATE_PRESETS,
29
31
  LIVE_GENERATE_STANDARDS: () => LIVE_GENERATE_STANDARDS,
30
32
  LIVE_PARSE_FORMATS: () => LIVE_PARSE_FORMATS,
31
33
  LIVE_PROFILES: () => LIVE_PROFILES,
32
- LIVE_VALIDATE_FORMATS: () => LIVE_VALIDATE_FORMATS
34
+ LIVE_PROFILES_BY_STANDARD: () => LIVE_PROFILES_BY_STANDARD,
35
+ LIVE_VALIDATE_FORMATS: () => LIVE_VALIDATE_FORMATS,
36
+ isProfileAllowedForStandard: () => isProfileAllowedForStandard,
37
+ profilesForStandard: () => profilesForStandard
33
38
  });
34
39
  module.exports = __toCommonJS(index_exports);
35
40
 
@@ -204,6 +209,8 @@ function errorFromResponse(status, bytes) {
204
209
 
205
210
  // src/constants.ts
206
211
  var DEFAULT_BASE_URL = "https://api.beliq.eu";
212
+ var DEFAULT_TIMEOUT_MS = 9e4;
213
+ var DEFAULT_MAX_RETRIES = 3;
207
214
  var LIVE_GENERATE_STANDARDS = ["xrechnung", "zugferd", "facturx", "peppol-bis"];
208
215
  var LIVE_GENERATE_PRESETS = [
209
216
  { id: "xrechnung", label: "XRechnung", standard: "xrechnung", output: "xml" },
@@ -213,6 +220,19 @@ var LIVE_GENERATE_PRESETS = [
213
220
  { id: "nlcius", label: "NLCIUS", standard: "peppol-bis", output: "xml", profile: "netherlands-nlcius" }
214
221
  ];
215
222
  var LIVE_PROFILES = ["basicwl", "en16931", "extended", "extended-ctc-fr"];
223
+ var LIVE_PROFILES_BY_STANDARD = {
224
+ xrechnung: ["xrechnung"],
225
+ "peppol-bis": ["peppol", "romania-ro-cius", "netherlands-nlcius"],
226
+ zugferd: ["basicwl", "en16931", "extended"],
227
+ facturx: ["basicwl", "en16931", "extended", "extended-ctc-fr"]
228
+ };
229
+ function profilesForStandard(standard) {
230
+ return LIVE_PROFILES_BY_STANDARD[standard] ?? [];
231
+ }
232
+ function isProfileAllowedForStandard(standard, profile) {
233
+ const allowed = profilesForStandard(standard);
234
+ return allowed.length === 0 || allowed.includes(profile);
235
+ }
216
236
  var LIVE_VALIDATE_FORMATS = ["auto", "cii", "ubl"];
217
237
  var LIVE_PARSE_FORMATS = ["auto", "cii", "ubl"];
218
238
  var LIVE_CONVERT_SOURCE_FORMATS = [
@@ -234,6 +254,9 @@ var LIVE_CONVERT_TARGET_FORMATS = [
234
254
  ];
235
255
 
236
256
  // src/transport.ts
257
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503]);
258
+ var MAX_RETRY_AFTER_MS = 3e4;
259
+ var BACKOFF_BASE_MS = 500;
237
260
  function authHeaders(config) {
238
261
  return config.auth === "bearer" ? { Authorization: `Bearer ${config.apiKey}` } : { "X-API-Key": config.apiKey };
239
262
  }
@@ -247,19 +270,58 @@ function buildUrl(baseUrl, path, query) {
247
270
  const qs = params.toString();
248
271
  return qs ? `${baseUrl}${path}?${qs}` : `${baseUrl}${path}`;
249
272
  }
273
+ function retryDelayMs(res, attempt) {
274
+ const header = res?.headers.get("retry-after");
275
+ if (header) {
276
+ const seconds = Number(header);
277
+ if (Number.isFinite(seconds) && seconds >= 0) {
278
+ return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS) + Math.random() * 250;
279
+ }
280
+ }
281
+ const backoff = BACKOFF_BASE_MS * 2 ** attempt;
282
+ return backoff + Math.random() * backoff;
283
+ }
284
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
250
285
  async function send(config, req) {
251
286
  const headers = { ...authHeaders(config) };
252
287
  if (req.contentType) headers["Content-Type"] = req.contentType;
253
288
  if (req.accept) headers["Accept"] = req.accept;
254
289
  const body = req.jsonBody !== void 0 ? JSON.stringify(req.jsonBody) : req.rawBody;
255
- const res = await config.fetchImpl(buildUrl(config.baseUrl, req.path, req.query), {
256
- method: req.method,
257
- headers,
258
- body
259
- });
260
- const bytes = new Uint8Array(await res.arrayBuffer());
261
- if (!res.ok) throw errorFromResponse(res.status, bytes);
262
- return { status: res.status, headers: res.headers, bytes };
290
+ const url = buildUrl(config.baseUrl, req.path, req.query);
291
+ let lastError;
292
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
293
+ const controller = new AbortController();
294
+ const timer = config.timeoutMs > 0 ? setTimeout(() => controller.abort(), config.timeoutMs) : void 0;
295
+ let res;
296
+ try {
297
+ res = await config.fetchImpl(url, {
298
+ method: req.method,
299
+ headers,
300
+ body,
301
+ signal: controller.signal
302
+ });
303
+ const bytes = new Uint8Array(await res.arrayBuffer());
304
+ 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;
308
+ }
309
+ } catch (err) {
310
+ if (err instanceof BeliqApiError && !RETRYABLE_STATUSES.has(err.status)) throw err;
311
+ if (controller.signal.aborted) {
312
+ throw new BeliqApiError(
313
+ `beliq: request to ${req.path} exceeded ${config.timeoutMs}ms`,
314
+ { status: 0, code: "TIMEOUT" }
315
+ );
316
+ }
317
+ lastError = err;
318
+ if (attempt === config.maxRetries) throw err;
319
+ } finally {
320
+ if (timer) clearTimeout(timer);
321
+ }
322
+ await sleep(retryDelayMs(res, attempt));
323
+ }
324
+ throw lastError;
263
325
  }
264
326
 
265
327
  // src/client.ts
@@ -330,7 +392,9 @@ var Beliq = class {
330
392
  apiKey: options.apiKey,
331
393
  baseUrl: (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, ""),
332
394
  auth: options.auth ?? "header",
333
- fetchImpl
395
+ fetchImpl,
396
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
397
+ maxRetries: options.maxRetries ?? DEFAULT_MAX_RETRIES
334
398
  };
335
399
  }
336
400
  /** Account, plan, and quota context for the key. Does not consume quota. */
@@ -431,11 +495,16 @@ var Beliq = class {
431
495
  Beliq,
432
496
  BeliqApiError,
433
497
  DEFAULT_BASE_URL,
498
+ DEFAULT_MAX_RETRIES,
499
+ DEFAULT_TIMEOUT_MS,
434
500
  LIVE_CONVERT_SOURCE_FORMATS,
435
501
  LIVE_CONVERT_TARGET_FORMATS,
436
502
  LIVE_GENERATE_PRESETS,
437
503
  LIVE_GENERATE_STANDARDS,
438
504
  LIVE_PARSE_FORMATS,
439
505
  LIVE_PROFILES,
440
- LIVE_VALIDATE_FORMATS
506
+ LIVE_PROFILES_BY_STANDARD,
507
+ LIVE_VALIDATE_FORMATS,
508
+ isProfileAllowedForStandard,
509
+ profilesForStandard
441
510
  });