@taxora/sdk 1.1.0 → 1.2.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 +91 -1
- package/dist/index.cjs +524 -126
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +188 -10
- package/dist/index.d.ts +188 -10
- package/dist/index.js +519 -126
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -60,37 +60,299 @@ var AuthenticationException = class extends TaxoraException {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
+
// src/exceptions/apiErrorMessage.ts
|
|
64
|
+
var MAX_LENGTH = 500;
|
|
65
|
+
var MAX_PLAIN_LENGTH = 200;
|
|
66
|
+
var REASON_PHRASES = {
|
|
67
|
+
400: "Bad Request",
|
|
68
|
+
401: "Unauthorized",
|
|
69
|
+
403: "Forbidden",
|
|
70
|
+
404: "Not Found",
|
|
71
|
+
405: "Method Not Allowed",
|
|
72
|
+
408: "Request Timeout",
|
|
73
|
+
409: "Conflict",
|
|
74
|
+
413: "Payload Too Large",
|
|
75
|
+
422: "Unprocessable Entity",
|
|
76
|
+
429: "Too Many Requests",
|
|
77
|
+
500: "Internal Server Error",
|
|
78
|
+
502: "Bad Gateway",
|
|
79
|
+
503: "Service Unavailable",
|
|
80
|
+
504: "Gateway Timeout"
|
|
81
|
+
};
|
|
82
|
+
function describeApiError(body, statusCode) {
|
|
83
|
+
return jsonErrorMessage(body) ?? plainErrorMessage(body) ?? statusErrorMessage(statusCode);
|
|
84
|
+
}
|
|
85
|
+
function detailApiError(body, statusCode) {
|
|
86
|
+
return jsonErrorMessage(body) ?? plainErrorMessage(body) ?? statusPhrase(statusCode);
|
|
87
|
+
}
|
|
88
|
+
function jsonErrorMessage(body) {
|
|
89
|
+
if (body === null || body === void 0 || body.trim() === "") return null;
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(body);
|
|
93
|
+
} catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
97
|
+
const payload = parsed;
|
|
98
|
+
for (const key of ["message", "error"]) {
|
|
99
|
+
const value = payload[key];
|
|
100
|
+
if (typeof value === "string") {
|
|
101
|
+
const message = sanitize(value);
|
|
102
|
+
if (message !== null) return message;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const errors = payload["errors"];
|
|
106
|
+
if (typeof errors === "object" && errors !== null) {
|
|
107
|
+
return fromErrorBag(errors);
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
function statusErrorMessage(statusCode) {
|
|
112
|
+
return `Taxora API request failed (${statusPhrase(statusCode)}).`;
|
|
113
|
+
}
|
|
114
|
+
function statusPhrase(statusCode) {
|
|
115
|
+
const phrase = REASON_PHRASES[statusCode];
|
|
116
|
+
return `HTTP ${statusCode}${phrase ? ` ${phrase}` : ""}`;
|
|
117
|
+
}
|
|
118
|
+
function plainErrorMessage(body) {
|
|
119
|
+
if (body === null || body === void 0) return null;
|
|
120
|
+
const value = body.replace(/\s+/g, " ").trim();
|
|
121
|
+
if (value === "" || value.length > MAX_PLAIN_LENGTH) return null;
|
|
122
|
+
if (value.includes("<") || value.includes("{")) return null;
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
function fromErrorBag(errors) {
|
|
126
|
+
for (const [field, messages] of Object.entries(errors)) {
|
|
127
|
+
for (const message of Array.isArray(messages) ? messages : [messages]) {
|
|
128
|
+
if (typeof message !== "string") continue;
|
|
129
|
+
const sanitized = sanitize(message);
|
|
130
|
+
if (sanitized === null) continue;
|
|
131
|
+
return field !== "" ? `${field}: ${sanitized}` : sanitized;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function sanitize(value) {
|
|
137
|
+
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
138
|
+
if (collapsed === "") return null;
|
|
139
|
+
if (/<!doctype\s+html|<html[\s>]/i.test(collapsed)) return null;
|
|
140
|
+
return collapsed.length > MAX_LENGTH ? `${collapsed.slice(0, MAX_LENGTH - 1)}\u2026` : collapsed;
|
|
141
|
+
}
|
|
142
|
+
|
|
63
143
|
// src/exceptions/HttpException.ts
|
|
64
|
-
var HttpException = class extends TaxoraException {
|
|
144
|
+
var HttpException = class _HttpException extends TaxoraException {
|
|
65
145
|
statusCode;
|
|
66
146
|
responseBody;
|
|
67
|
-
|
|
147
|
+
/** Raw `Retry-After` header of the failed response, when it carried one. */
|
|
148
|
+
retryAfter;
|
|
149
|
+
constructor(message, statusCode, responseBody, context = {}, retryAfter = null) {
|
|
68
150
|
super(message, context);
|
|
69
151
|
this.name = "HttpException";
|
|
70
152
|
this.statusCode = statusCode;
|
|
71
153
|
this.responseBody = responseBody;
|
|
154
|
+
this.retryAfter = retryAfter;
|
|
72
155
|
Object.setPrototypeOf(this, new.target.prototype);
|
|
73
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* The final failure after the SDK retried a transient gateway error, e.g.
|
|
159
|
+
* "Taxora VAT validation failed after 3 attempts (HTTP 504 Gateway Timeout)."
|
|
160
|
+
*/
|
|
161
|
+
static afterAttempts(operation, attempts, last) {
|
|
162
|
+
const detail = detailApiError(last.getResponseBody(), last.getStatusCode());
|
|
163
|
+
return new _HttpException(
|
|
164
|
+
`Taxora ${operation} failed after ${attempts} attempts (${detail}).`,
|
|
165
|
+
last.getStatusCode(),
|
|
166
|
+
last.getResponseBody(),
|
|
167
|
+
{ ...last.context, attempts, cause: last },
|
|
168
|
+
last.getRetryAfter()
|
|
169
|
+
);
|
|
170
|
+
}
|
|
74
171
|
getStatusCode() {
|
|
75
172
|
return this.statusCode;
|
|
76
173
|
}
|
|
77
174
|
getResponseBody() {
|
|
78
175
|
return this.responseBody;
|
|
79
176
|
}
|
|
177
|
+
getRetryAfter() {
|
|
178
|
+
return this.retryAfter;
|
|
179
|
+
}
|
|
80
180
|
};
|
|
81
181
|
|
|
182
|
+
// src/http/RetryPolicy.ts
|
|
183
|
+
var DEFAULT_RETRYABLE_STATUS_CODES = [502, 503, 504];
|
|
184
|
+
var RETRY_AFTER_ONLY_STATUS_CODES = [429];
|
|
185
|
+
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
186
|
+
"ECONNRESET",
|
|
187
|
+
"ECONNREFUSED",
|
|
188
|
+
"ECONNABORTED",
|
|
189
|
+
"EPIPE",
|
|
190
|
+
"ETIMEDOUT",
|
|
191
|
+
"EAI_AGAIN",
|
|
192
|
+
"EHOSTUNREACH",
|
|
193
|
+
"ENETUNREACH",
|
|
194
|
+
"ENETDOWN",
|
|
195
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
196
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
197
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
198
|
+
"UND_ERR_SOCKET"
|
|
199
|
+
]);
|
|
200
|
+
var RetryPolicy = class _RetryPolicy {
|
|
201
|
+
maxAttempts;
|
|
202
|
+
initialDelayMs;
|
|
203
|
+
backoffMultiplier;
|
|
204
|
+
maxDelayMs;
|
|
205
|
+
retryableStatusCodes;
|
|
206
|
+
respectRetryAfter;
|
|
207
|
+
maxRetryAfterMs;
|
|
208
|
+
retryOnNetworkErrors;
|
|
209
|
+
sleeper;
|
|
210
|
+
constructor(options = {}) {
|
|
211
|
+
this.maxAttempts = options.maxAttempts ?? 3;
|
|
212
|
+
this.initialDelayMs = options.initialDelayMs ?? 500;
|
|
213
|
+
this.backoffMultiplier = options.backoffMultiplier ?? 2;
|
|
214
|
+
this.maxDelayMs = options.maxDelayMs ?? 5e3;
|
|
215
|
+
this.retryableStatusCodes = options.retryableStatusCodes ?? DEFAULT_RETRYABLE_STATUS_CODES;
|
|
216
|
+
this.respectRetryAfter = options.respectRetryAfter ?? true;
|
|
217
|
+
this.maxRetryAfterMs = options.maxRetryAfterMs ?? 1e4;
|
|
218
|
+
this.retryOnNetworkErrors = options.retryOnNetworkErrors ?? true;
|
|
219
|
+
this.sleeper = options.sleeper;
|
|
220
|
+
if (this.maxAttempts < 1) throw new Error("maxAttempts must be at least 1.");
|
|
221
|
+
if (this.initialDelayMs < 0 || this.maxDelayMs < 0) throw new Error("Delays must not be negative.");
|
|
222
|
+
if (this.backoffMultiplier < 1) throw new Error("backoffMultiplier must be at least 1.");
|
|
223
|
+
}
|
|
224
|
+
/** No retrying at all — every failure is surfaced on the first attempt. */
|
|
225
|
+
static disabled() {
|
|
226
|
+
return new _RetryPolicy({ maxAttempts: 1 });
|
|
227
|
+
}
|
|
228
|
+
/** Retry immediately, without waiting (mainly useful in tests). */
|
|
229
|
+
static withoutDelay(maxAttempts = 3) {
|
|
230
|
+
return new _RetryPolicy({ maxAttempts, initialDelayMs: 0, maxDelayMs: 0 });
|
|
231
|
+
}
|
|
232
|
+
isRetryable(statusCode, retryAfterHeader) {
|
|
233
|
+
const retryAfterMs = this.retryAfterMs(retryAfterHeader);
|
|
234
|
+
if (retryAfterMs !== null && retryAfterMs > this.maxRetryAfterMs) return false;
|
|
235
|
+
if (this.retryableStatusCodes.includes(statusCode)) return true;
|
|
236
|
+
return retryAfterMs !== null && RETRY_AFTER_ONLY_STATUS_CODES.includes(statusCode);
|
|
237
|
+
}
|
|
238
|
+
/** @param attempt Number of attempts made so far (1 = the first one just failed). */
|
|
239
|
+
shouldRetry(statusCode, attempt, retryAfterHeader) {
|
|
240
|
+
return attempt < this.maxAttempts && this.isRetryable(statusCode, retryAfterHeader);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* A connection-level failure: the request never got an answer, so repeating it
|
|
244
|
+
* is as safe as repeating a gateway timeout.
|
|
245
|
+
*/
|
|
246
|
+
isRetryableError(error) {
|
|
247
|
+
if (!this.retryOnNetworkErrors || !(error instanceof Error)) return false;
|
|
248
|
+
if (error.name === "AbortError" || error.name === "TimeoutError") return true;
|
|
249
|
+
const code = error.code ?? error.cause?.code;
|
|
250
|
+
if (typeof code === "string" && NETWORK_ERROR_CODES.has(code)) return true;
|
|
251
|
+
return error instanceof TypeError && /fetch failed|network|failed to fetch/i.test(error.message);
|
|
252
|
+
}
|
|
253
|
+
shouldRetryError(error, attempt) {
|
|
254
|
+
return attempt < this.maxAttempts && this.isRetryableError(error);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* The wait the API asked for, in milliseconds — seconds or HTTP-date form — or
|
|
258
|
+
* null when there is none, it cannot be parsed, or the header is ignored.
|
|
259
|
+
*/
|
|
260
|
+
retryAfterMs(header, now = /* @__PURE__ */ new Date()) {
|
|
261
|
+
if (!this.respectRetryAfter || header === null || header === void 0) return null;
|
|
262
|
+
const value = header.trim();
|
|
263
|
+
if (value === "") return null;
|
|
264
|
+
if (/^\d+$/.test(value)) return Number(value) * 1e3;
|
|
265
|
+
const date = Date.parse(value);
|
|
266
|
+
if (Number.isNaN(date)) return null;
|
|
267
|
+
return Math.max(0, date - now.getTime());
|
|
268
|
+
}
|
|
269
|
+
/** The wait before the next attempt: what the API asked for, else exponential backoff. */
|
|
270
|
+
delayMsForAttempt(attempt, retryAfterHeader) {
|
|
271
|
+
const retryAfterMs = this.retryAfterMs(retryAfterHeader);
|
|
272
|
+
if (retryAfterMs !== null) return Math.min(retryAfterMs, this.maxRetryAfterMs);
|
|
273
|
+
if (this.initialDelayMs === 0) return 0;
|
|
274
|
+
const delay = Math.round(this.initialDelayMs * this.backoffMultiplier ** Math.max(0, attempt - 1));
|
|
275
|
+
return Math.max(0, Math.min(delay, this.maxDelayMs));
|
|
276
|
+
}
|
|
277
|
+
async sleepBeforeRetry(attempt, retryAfterHeader) {
|
|
278
|
+
const delayMs = this.delayMsForAttempt(attempt, retryAfterHeader);
|
|
279
|
+
if (delayMs <= 0) return;
|
|
280
|
+
if (this.sleeper) {
|
|
281
|
+
await this.sleeper(delayMs);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/http/withRetries.ts
|
|
289
|
+
async function withRetries(policy, operation, request) {
|
|
290
|
+
let attempt = 1;
|
|
291
|
+
for (; ; ) {
|
|
292
|
+
try {
|
|
293
|
+
return await request();
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (!(error instanceof HttpException)) {
|
|
296
|
+
if (!policy.shouldRetryError(error, attempt)) throw error;
|
|
297
|
+
await policy.sleepBeforeRetry(attempt);
|
|
298
|
+
attempt++;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const retryAfter = error.getRetryAfter();
|
|
302
|
+
if (!policy.shouldRetry(error.getStatusCode(), attempt, retryAfter)) {
|
|
303
|
+
throw attempt > 1 && policy.isRetryable(error.getStatusCode(), retryAfter) ? HttpException.afterAttempts(operation, attempt, error) : error;
|
|
304
|
+
}
|
|
305
|
+
await policy.sleepBeforeRetry(attempt, retryAfter);
|
|
306
|
+
attempt++;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function withResponseRetries(policy, operation, send) {
|
|
311
|
+
let attempt = 1;
|
|
312
|
+
for (; ; ) {
|
|
313
|
+
let response;
|
|
314
|
+
try {
|
|
315
|
+
response = await send();
|
|
316
|
+
} catch (error) {
|
|
317
|
+
if (!policy.shouldRetryError(error, attempt)) throw error;
|
|
318
|
+
await policy.sleepBeforeRetry(attempt);
|
|
319
|
+
attempt++;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const retryAfter = response.headers.get("retry-after");
|
|
323
|
+
if (!policy.shouldRetry(response.status, attempt, retryAfter)) {
|
|
324
|
+
if (attempt > 1 && policy.isRetryable(response.status, retryAfter)) {
|
|
325
|
+
const body = await response.text();
|
|
326
|
+
const last = new HttpException(
|
|
327
|
+
describeApiError(body, response.status),
|
|
328
|
+
response.status,
|
|
329
|
+
body,
|
|
330
|
+
{},
|
|
331
|
+
retryAfter
|
|
332
|
+
);
|
|
333
|
+
throw HttpException.afterAttempts(operation, attempt, last);
|
|
334
|
+
}
|
|
335
|
+
return response;
|
|
336
|
+
}
|
|
337
|
+
await policy.sleepBeforeRetry(attempt, retryAfter);
|
|
338
|
+
attempt++;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
82
342
|
// src/endpoints/AuthEndpoint.ts
|
|
83
343
|
var AuthEndpoint = class {
|
|
84
|
-
constructor(baseUrl, apiKey, tokenStorage, httpClient) {
|
|
344
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
85
345
|
this.baseUrl = baseUrl;
|
|
86
346
|
this.apiKey = apiKey;
|
|
87
347
|
this.tokenStorage = tokenStorage;
|
|
88
348
|
this.httpClient = httpClient;
|
|
349
|
+
this.retryPolicy = retryPolicy;
|
|
89
350
|
}
|
|
90
351
|
baseUrl;
|
|
91
352
|
apiKey;
|
|
92
353
|
tokenStorage;
|
|
93
354
|
httpClient;
|
|
355
|
+
retryPolicy;
|
|
94
356
|
async login(identifier, password, device, loginIdentifier = LoginIdentifier.EMAIL) {
|
|
95
357
|
const deviceName = device ?? hostname();
|
|
96
358
|
const body = {
|
|
@@ -103,24 +365,32 @@ var AuthEndpoint = class {
|
|
|
103
365
|
} else {
|
|
104
366
|
body["email"] = identifier;
|
|
105
367
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const responseText = await response.text();
|
|
114
|
-
if (response.status === 401) {
|
|
115
|
-
throw new AuthenticationException("Authentication failed: invalid credentials", {
|
|
116
|
-
status: 401,
|
|
117
|
-
body: responseText
|
|
368
|
+
const data = await withRetries(this.retryPolicy, "login", async () => {
|
|
369
|
+
const response = await this.httpClient.request("POST", `${this.baseUrl}/auth/login`, {
|
|
370
|
+
headers: {
|
|
371
|
+
"Content-Type": "application/json",
|
|
372
|
+
"x-api-key": this.apiKey
|
|
373
|
+
},
|
|
374
|
+
body: JSON.stringify(body)
|
|
118
375
|
});
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
376
|
+
const responseText = await response.text();
|
|
377
|
+
if (response.status === 401) {
|
|
378
|
+
throw new AuthenticationException("Authentication failed: invalid credentials", {
|
|
379
|
+
status: 401,
|
|
380
|
+
body: responseText
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (!response.ok) {
|
|
384
|
+
throw new HttpException(
|
|
385
|
+
describeApiError(responseText, response.status),
|
|
386
|
+
response.status,
|
|
387
|
+
responseText,
|
|
388
|
+
{},
|
|
389
|
+
response.headers.get("retry-after")
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
return this.parseResponse(responseText, response.status);
|
|
393
|
+
});
|
|
124
394
|
const token = Token.fromArray(data);
|
|
125
395
|
this.tokenStorage.set(token);
|
|
126
396
|
return token;
|
|
@@ -137,28 +407,36 @@ var AuthEndpoint = class {
|
|
|
137
407
|
if (storedToken) {
|
|
138
408
|
headers["Authorization"] = `Bearer ${storedToken.accessToken}`;
|
|
139
409
|
}
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
410
|
+
const data = await withRetries(this.retryPolicy, "token refresh", async () => {
|
|
411
|
+
const response = await this.httpClient.request("POST", `${this.baseUrl}/auth/refresh`, { headers });
|
|
412
|
+
const responseText = await response.text();
|
|
413
|
+
if (response.status === 401) {
|
|
414
|
+
throw new AuthenticationException("Token refresh failed: unauthorized", { status: 401, body: responseText });
|
|
415
|
+
}
|
|
416
|
+
if (!response.ok) {
|
|
417
|
+
throw new HttpException(
|
|
418
|
+
describeApiError(responseText, response.status),
|
|
419
|
+
response.status,
|
|
420
|
+
responseText,
|
|
421
|
+
{},
|
|
422
|
+
response.headers.get("retry-after")
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return this.parseResponse(responseText, response.status);
|
|
426
|
+
});
|
|
149
427
|
const token = Token.fromArray(data);
|
|
150
428
|
this.tokenStorage.set(token);
|
|
151
429
|
return token;
|
|
152
430
|
}
|
|
153
|
-
parseResponse(responseText) {
|
|
431
|
+
parseResponse(responseText, statusCode) {
|
|
154
432
|
let parsed;
|
|
155
433
|
try {
|
|
156
434
|
parsed = JSON.parse(responseText);
|
|
157
435
|
} catch {
|
|
158
|
-
throw new HttpException("Invalid JSON response",
|
|
436
|
+
throw new HttpException("Invalid JSON response", statusCode, responseText);
|
|
159
437
|
}
|
|
160
438
|
if (typeof parsed !== "object" || parsed === null) {
|
|
161
|
-
throw new HttpException("Unexpected response format",
|
|
439
|
+
throw new HttpException("Unexpected response format", statusCode, responseText);
|
|
162
440
|
}
|
|
163
441
|
const obj = parsed;
|
|
164
442
|
if (obj["data"] !== void 0 && typeof obj["data"] === "object" && obj["data"] !== null) {
|
|
@@ -182,16 +460,18 @@ function normalizeCompanyResource(data) {
|
|
|
182
460
|
|
|
183
461
|
// src/endpoints/CompanyEndpoint.ts
|
|
184
462
|
var CompanyEndpoint = class {
|
|
185
|
-
constructor(baseUrl, apiKey, tokenStorage, httpClient) {
|
|
463
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
186
464
|
this.baseUrl = baseUrl;
|
|
187
465
|
this.apiKey = apiKey;
|
|
188
466
|
this.tokenStorage = tokenStorage;
|
|
189
467
|
this.httpClient = httpClient;
|
|
468
|
+
this.retryPolicy = retryPolicy;
|
|
190
469
|
}
|
|
191
470
|
baseUrl;
|
|
192
471
|
apiKey;
|
|
193
472
|
tokenStorage;
|
|
194
473
|
httpClient;
|
|
474
|
+
retryPolicy;
|
|
195
475
|
async get() {
|
|
196
476
|
const headers = {
|
|
197
477
|
"Content-Type": "application/json",
|
|
@@ -201,25 +481,33 @@ var CompanyEndpoint = class {
|
|
|
201
481
|
if (token) {
|
|
202
482
|
headers["Authorization"] = `Bearer ${token.accessToken}`;
|
|
203
483
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
484
|
+
return withRetries(this.retryPolicy, "company request", async () => {
|
|
485
|
+
const response = await this.httpClient.request("GET", `${this.baseUrl}/company`, { headers });
|
|
486
|
+
const responseText = await response.text();
|
|
487
|
+
if (!response.ok) {
|
|
488
|
+
throw new HttpException(
|
|
489
|
+
describeApiError(responseText, response.status),
|
|
490
|
+
response.status,
|
|
491
|
+
responseText,
|
|
492
|
+
{},
|
|
493
|
+
response.headers.get("retry-after")
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
let parsed;
|
|
497
|
+
try {
|
|
498
|
+
parsed = JSON.parse(responseText);
|
|
499
|
+
} catch {
|
|
500
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
501
|
+
}
|
|
502
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
503
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
504
|
+
}
|
|
505
|
+
const obj = parsed;
|
|
506
|
+
if (obj["data"] !== void 0 && typeof obj["data"] === "object" && obj["data"] !== null) {
|
|
507
|
+
return normalizeCompanyResource(obj["data"]);
|
|
508
|
+
}
|
|
509
|
+
return normalizeCompanyResource(obj);
|
|
510
|
+
});
|
|
223
511
|
}
|
|
224
512
|
};
|
|
225
513
|
|
|
@@ -932,16 +1220,18 @@ var TRANSACTION_TYPES = [
|
|
|
932
1220
|
"crossborder_inbound"
|
|
933
1221
|
];
|
|
934
1222
|
var EReportingEndpoint = class {
|
|
935
|
-
constructor(baseUrl, apiKey, tokenStorage, httpClient) {
|
|
1223
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
936
1224
|
this.baseUrl = baseUrl;
|
|
937
1225
|
this.apiKey = apiKey;
|
|
938
1226
|
this.tokenStorage = tokenStorage;
|
|
939
1227
|
this.httpClient = httpClient;
|
|
1228
|
+
this.retryPolicy = retryPolicy;
|
|
940
1229
|
}
|
|
941
1230
|
baseUrl;
|
|
942
1231
|
apiKey;
|
|
943
1232
|
tokenStorage;
|
|
944
1233
|
httpClient;
|
|
1234
|
+
retryPolicy;
|
|
945
1235
|
// ---------------------------------------------------------------------
|
|
946
1236
|
// Enrollments
|
|
947
1237
|
// ---------------------------------------------------------------------
|
|
@@ -1335,7 +1625,8 @@ var EReportingEndpoint = class {
|
|
|
1335
1625
|
async sendRequest(method, url, body) {
|
|
1336
1626
|
const options = { headers: this.buildHeaders() };
|
|
1337
1627
|
if (body) options.body = JSON.stringify(body);
|
|
1338
|
-
|
|
1628
|
+
const send = () => this.httpClient.request(method, url, options);
|
|
1629
|
+
return method === "GET" ? withResponseRetries(this.retryPolicy, "e-reporting request", send) : send();
|
|
1339
1630
|
}
|
|
1340
1631
|
/** Multipart upload: Content-Type is left to fetch so the boundary is set correctly. */
|
|
1341
1632
|
async sendMultipartRequest(method, url, form) {
|
|
@@ -1348,16 +1639,22 @@ var EReportingEndpoint = class {
|
|
|
1348
1639
|
throw this.buildValidationException(responseText);
|
|
1349
1640
|
}
|
|
1350
1641
|
if (!response.ok) {
|
|
1351
|
-
throw new HttpException(
|
|
1642
|
+
throw new HttpException(
|
|
1643
|
+
describeApiError(responseText, response.status),
|
|
1644
|
+
response.status,
|
|
1645
|
+
responseText,
|
|
1646
|
+
{},
|
|
1647
|
+
response.headers.get("retry-after")
|
|
1648
|
+
);
|
|
1352
1649
|
}
|
|
1353
1650
|
let parsed;
|
|
1354
1651
|
try {
|
|
1355
1652
|
parsed = JSON.parse(responseText);
|
|
1356
1653
|
} catch {
|
|
1357
|
-
throw new HttpException("Invalid JSON response",
|
|
1654
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
1358
1655
|
}
|
|
1359
1656
|
if (typeof parsed !== "object" || parsed === null) {
|
|
1360
|
-
throw new HttpException("Unexpected response format",
|
|
1657
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
1361
1658
|
}
|
|
1362
1659
|
const obj = parsed;
|
|
1363
1660
|
if (obj["data"] !== void 0 && typeof obj["data"] === "object" && obj["data"] !== null) {
|
|
@@ -1375,7 +1672,13 @@ var EReportingEndpoint = class {
|
|
|
1375
1672
|
throw this.buildValidationException(responseText);
|
|
1376
1673
|
}
|
|
1377
1674
|
if (!response.ok) {
|
|
1378
|
-
throw new HttpException(
|
|
1675
|
+
throw new HttpException(
|
|
1676
|
+
describeApiError(responseText, response.status),
|
|
1677
|
+
response.status,
|
|
1678
|
+
responseText,
|
|
1679
|
+
{},
|
|
1680
|
+
response.headers.get("retry-after")
|
|
1681
|
+
);
|
|
1379
1682
|
}
|
|
1380
1683
|
}
|
|
1381
1684
|
/**
|
|
@@ -1419,6 +1722,29 @@ var EReportingEndpoint = class {
|
|
|
1419
1722
|
}
|
|
1420
1723
|
};
|
|
1421
1724
|
|
|
1725
|
+
// src/enums/SmartEnrichmentMode.ts
|
|
1726
|
+
var SmartEnrichmentMode = {
|
|
1727
|
+
DEFAULT: "default",
|
|
1728
|
+
COMPLEX: "complex"
|
|
1729
|
+
};
|
|
1730
|
+
var VALUES5 = Object.values(SmartEnrichmentMode);
|
|
1731
|
+
function toSmartEnrichmentMode(value) {
|
|
1732
|
+
if (typeof value === "string") {
|
|
1733
|
+
const normalized = value.trim().toLowerCase();
|
|
1734
|
+
if (VALUES5.includes(normalized)) {
|
|
1735
|
+
return normalized;
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
return void 0;
|
|
1739
|
+
}
|
|
1740
|
+
var DESCRIPTIONS5 = {
|
|
1741
|
+
[SmartEnrichmentMode.DEFAULT]: "One provider searches; a second is consulted only if it finds nothing.",
|
|
1742
|
+
[SmartEnrichmentMode.COMPLEX]: "Two providers search independently and are cross-checked against the tax authority. Costs more."
|
|
1743
|
+
};
|
|
1744
|
+
function describeSmartEnrichmentMode(mode) {
|
|
1745
|
+
return DESCRIPTIONS5[mode];
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1422
1748
|
// src/enums/SmartEnrichmentStatus.ts
|
|
1423
1749
|
var SmartEnrichmentStatus = {
|
|
1424
1750
|
FOUND: "found",
|
|
@@ -1430,17 +1756,17 @@ var SmartEnrichmentStatus = {
|
|
|
1430
1756
|
FAILED: "failed",
|
|
1431
1757
|
UNKNOWN: "unknown"
|
|
1432
1758
|
};
|
|
1433
|
-
var
|
|
1759
|
+
var VALUES6 = Object.values(SmartEnrichmentStatus);
|
|
1434
1760
|
function toSmartEnrichmentStatus(value) {
|
|
1435
1761
|
if (typeof value === "string") {
|
|
1436
1762
|
const normalized = value.trim().toLowerCase();
|
|
1437
|
-
if (
|
|
1763
|
+
if (VALUES6.includes(normalized)) {
|
|
1438
1764
|
return normalized;
|
|
1439
1765
|
}
|
|
1440
1766
|
}
|
|
1441
1767
|
return SmartEnrichmentStatus.UNKNOWN;
|
|
1442
1768
|
}
|
|
1443
|
-
var
|
|
1769
|
+
var DESCRIPTIONS6 = {
|
|
1444
1770
|
[SmartEnrichmentStatus.FOUND]: "A VAT number was resolved and confirmed.",
|
|
1445
1771
|
[SmartEnrichmentStatus.NO_VAT_EXISTS]: "The company was identified but has no VAT number.",
|
|
1446
1772
|
[SmartEnrichmentStatus.NOT_FOUND]: "No sufficiently-confident match could be resolved.",
|
|
@@ -1451,12 +1777,12 @@ var DESCRIPTIONS5 = {
|
|
|
1451
1777
|
[SmartEnrichmentStatus.UNKNOWN]: "Unknown status."
|
|
1452
1778
|
};
|
|
1453
1779
|
function describeSmartEnrichmentStatus(status) {
|
|
1454
|
-
return
|
|
1780
|
+
return DESCRIPTIONS6[status];
|
|
1455
1781
|
}
|
|
1456
1782
|
|
|
1457
1783
|
// src/dto/SmartEnrichmentResource.ts
|
|
1458
1784
|
var SmartEnrichmentResource = class _SmartEnrichmentResource {
|
|
1459
|
-
constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source) {
|
|
1785
|
+
constructor(status, vatNumber, vatType, confidence, matchedCompanyName, matchedAddress, country, source, mode = void 0, providerVerdicts = [], addressQuality = void 0) {
|
|
1460
1786
|
this.status = status;
|
|
1461
1787
|
this.vatNumber = vatNumber;
|
|
1462
1788
|
this.vatType = vatType;
|
|
@@ -1465,6 +1791,9 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
|
|
|
1465
1791
|
this.matchedAddress = matchedAddress;
|
|
1466
1792
|
this.country = country;
|
|
1467
1793
|
this.source = source;
|
|
1794
|
+
this.mode = mode;
|
|
1795
|
+
this.providerVerdicts = providerVerdicts;
|
|
1796
|
+
this.addressQuality = addressQuality;
|
|
1468
1797
|
}
|
|
1469
1798
|
status;
|
|
1470
1799
|
vatNumber;
|
|
@@ -1474,6 +1803,9 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
|
|
|
1474
1803
|
matchedAddress;
|
|
1475
1804
|
country;
|
|
1476
1805
|
source;
|
|
1806
|
+
mode;
|
|
1807
|
+
providerVerdicts;
|
|
1808
|
+
addressQuality;
|
|
1477
1809
|
static fromArray(data) {
|
|
1478
1810
|
const str = (key) => {
|
|
1479
1811
|
const v = data[key];
|
|
@@ -1483,6 +1815,8 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
|
|
|
1483
1815
|
const v = data[key];
|
|
1484
1816
|
return typeof v === "number" ? v : void 0;
|
|
1485
1817
|
};
|
|
1818
|
+
const verdicts = data["providerVerdicts"];
|
|
1819
|
+
const quality = data["addressQuality"];
|
|
1486
1820
|
return new _SmartEnrichmentResource(
|
|
1487
1821
|
toSmartEnrichmentStatus(data["status"]),
|
|
1488
1822
|
str("vatNumber"),
|
|
@@ -1491,7 +1825,10 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
|
|
|
1491
1825
|
str("matchedCompanyName"),
|
|
1492
1826
|
str("matchedAddress"),
|
|
1493
1827
|
str("country"),
|
|
1494
|
-
str("source")
|
|
1828
|
+
str("source"),
|
|
1829
|
+
toSmartEnrichmentMode(data["mode"]),
|
|
1830
|
+
Array.isArray(verdicts) ? verdicts.filter((v) => typeof v === "object" && v !== null) : [],
|
|
1831
|
+
typeof quality === "object" && quality !== null ? quality : void 0
|
|
1495
1832
|
);
|
|
1496
1833
|
}
|
|
1497
1834
|
toArray() {
|
|
@@ -1503,7 +1840,10 @@ var SmartEnrichmentResource = class _SmartEnrichmentResource {
|
|
|
1503
1840
|
matchedCompanyName: this.matchedCompanyName ?? null,
|
|
1504
1841
|
matchedAddress: this.matchedAddress ?? null,
|
|
1505
1842
|
country: this.country ?? null,
|
|
1506
|
-
source: this.source ?? null
|
|
1843
|
+
source: this.source ?? null,
|
|
1844
|
+
mode: this.mode ?? null,
|
|
1845
|
+
providerVerdicts: this.providerVerdicts,
|
|
1846
|
+
addressQuality: this.addressQuality ?? null
|
|
1507
1847
|
};
|
|
1508
1848
|
}
|
|
1509
1849
|
};
|
|
@@ -1731,16 +2071,18 @@ var STATISTICS_INTERVALS = ["day", "week", "month"];
|
|
|
1731
2071
|
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
1732
2072
|
var DEFAULT_POLL_TIMEOUT_MS = 12e4;
|
|
1733
2073
|
var SmartEnrichmentEndpoint = class {
|
|
1734
|
-
constructor(baseUrl, apiKey, tokenStorage, httpClient) {
|
|
2074
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
1735
2075
|
this.baseUrl = baseUrl;
|
|
1736
2076
|
this.apiKey = apiKey;
|
|
1737
2077
|
this.tokenStorage = tokenStorage;
|
|
1738
2078
|
this.httpClient = httpClient;
|
|
2079
|
+
this.retryPolicy = retryPolicy;
|
|
1739
2080
|
}
|
|
1740
2081
|
baseUrl;
|
|
1741
2082
|
apiKey;
|
|
1742
2083
|
tokenStorage;
|
|
1743
2084
|
httpClient;
|
|
2085
|
+
retryPolicy;
|
|
1744
2086
|
/**
|
|
1745
2087
|
* Single reverse lookup. Returns a confident result synchronously, or a `processing`
|
|
1746
2088
|
* job (poll with get(job.jobId) or waitForCompletion(job.jobId)) when deeper resolution
|
|
@@ -1755,6 +2097,7 @@ var SmartEnrichmentEndpoint = class {
|
|
|
1755
2097
|
if (input.street !== void 0) body["street"] = input.street;
|
|
1756
2098
|
if (input.postalCode !== void 0) body["postalCode"] = input.postalCode;
|
|
1757
2099
|
if (input.city !== void 0) body["city"] = input.city;
|
|
2100
|
+
if (input.mode !== void 0) body["mode"] = input.mode;
|
|
1758
2101
|
const response = await this.sendRequest("POST", `${this.baseUrl}/smart-enrichment`, body);
|
|
1759
2102
|
const data = await this.parseJsonResponse(response, [200, 202]);
|
|
1760
2103
|
return SmartEnrichmentJob.fromArray(data);
|
|
@@ -1762,13 +2105,18 @@ var SmartEnrichmentEndpoint = class {
|
|
|
1762
2105
|
/**
|
|
1763
2106
|
* Bulk batch — always async. Returns a `processing` job; results arrive via the
|
|
1764
2107
|
* `enrichment.completed` webhook and are retrievable via get(job.jobId).
|
|
2108
|
+
*
|
|
2109
|
+
* @param mode Search depth for every row in the batch. A row carrying its own `mode` overrides
|
|
2110
|
+
* it, so a caller can pay for `complex` on just the rows that need it.
|
|
1765
2111
|
*/
|
|
1766
|
-
async bulkLookup(items) {
|
|
2112
|
+
async bulkLookup(items, mode) {
|
|
1767
2113
|
if (items.length === 0) {
|
|
1768
2114
|
throw new TaxoraException("bulkLookup requires at least one item.");
|
|
1769
2115
|
}
|
|
1770
2116
|
items.forEach((item, index) => this.validateInput(item, `items[${index}]`));
|
|
1771
|
-
const
|
|
2117
|
+
const body = { items };
|
|
2118
|
+
if (mode !== void 0) body["mode"] = mode;
|
|
2119
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/smart-enrichment/bulk`, body);
|
|
1772
2120
|
const data = await this.parseJsonResponse(response, [200, 202]);
|
|
1773
2121
|
return SmartEnrichmentJob.fromArray(data);
|
|
1774
2122
|
}
|
|
@@ -1910,24 +2258,31 @@ var SmartEnrichmentEndpoint = class {
|
|
|
1910
2258
|
async sendRequest(method, url, body) {
|
|
1911
2259
|
const options = { headers: this.buildHeaders() };
|
|
1912
2260
|
if (body) options.body = JSON.stringify(body);
|
|
1913
|
-
|
|
2261
|
+
const send = () => this.httpClient.request(method, url, options);
|
|
2262
|
+
return method === "GET" ? withResponseRetries(this.retryPolicy, "Smart Enrichment request", send) : send();
|
|
1914
2263
|
}
|
|
1915
2264
|
async parseJsonResponse(response, acceptedStatuses = [200]) {
|
|
1916
2265
|
const responseText = await response.text();
|
|
1917
2266
|
if (response.status === 422) {
|
|
1918
|
-
throw new ValidationException("Validation failed", this.parseValidationErrors(responseText));
|
|
2267
|
+
throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
|
|
1919
2268
|
}
|
|
1920
2269
|
if (!response.ok && !acceptedStatuses.includes(response.status)) {
|
|
1921
|
-
throw new HttpException(
|
|
2270
|
+
throw new HttpException(
|
|
2271
|
+
describeApiError(responseText, response.status),
|
|
2272
|
+
response.status,
|
|
2273
|
+
responseText,
|
|
2274
|
+
{},
|
|
2275
|
+
response.headers.get("retry-after")
|
|
2276
|
+
);
|
|
1922
2277
|
}
|
|
1923
2278
|
let parsed;
|
|
1924
2279
|
try {
|
|
1925
2280
|
parsed = JSON.parse(responseText);
|
|
1926
2281
|
} catch {
|
|
1927
|
-
throw new HttpException("Invalid JSON response",
|
|
2282
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
1928
2283
|
}
|
|
1929
2284
|
if (typeof parsed !== "object" || parsed === null) {
|
|
1930
|
-
throw new HttpException("Unexpected response format",
|
|
2285
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
1931
2286
|
}
|
|
1932
2287
|
const obj = parsed;
|
|
1933
2288
|
if (obj["data"] !== void 0 && (typeof obj["data"] === "object" || Array.isArray(obj["data"]))) {
|
|
@@ -1943,19 +2298,25 @@ var SmartEnrichmentEndpoint = class {
|
|
|
1943
2298
|
async parseRawJson(response) {
|
|
1944
2299
|
const responseText = await response.text();
|
|
1945
2300
|
if (response.status === 422) {
|
|
1946
|
-
throw new ValidationException("Validation failed", this.parseValidationErrors(responseText));
|
|
2301
|
+
throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
|
|
1947
2302
|
}
|
|
1948
2303
|
if (!response.ok) {
|
|
1949
|
-
throw new HttpException(
|
|
2304
|
+
throw new HttpException(
|
|
2305
|
+
describeApiError(responseText, response.status),
|
|
2306
|
+
response.status,
|
|
2307
|
+
responseText,
|
|
2308
|
+
{},
|
|
2309
|
+
response.headers.get("retry-after")
|
|
2310
|
+
);
|
|
1950
2311
|
}
|
|
1951
2312
|
let parsed;
|
|
1952
2313
|
try {
|
|
1953
2314
|
parsed = JSON.parse(responseText);
|
|
1954
2315
|
} catch {
|
|
1955
|
-
throw new HttpException("Invalid JSON response",
|
|
2316
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
1956
2317
|
}
|
|
1957
2318
|
if (typeof parsed !== "object" || parsed === null) {
|
|
1958
|
-
throw new HttpException("Unexpected response format",
|
|
2319
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
1959
2320
|
}
|
|
1960
2321
|
return parsed;
|
|
1961
2322
|
}
|
|
@@ -1966,10 +2327,16 @@ var SmartEnrichmentEndpoint = class {
|
|
|
1966
2327
|
async parseCsvResponse(response) {
|
|
1967
2328
|
const responseText = await response.text();
|
|
1968
2329
|
if (response.status === 422) {
|
|
1969
|
-
throw new ValidationException("Validation failed", this.parseValidationErrors(responseText));
|
|
2330
|
+
throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", this.parseValidationErrors(responseText));
|
|
1970
2331
|
}
|
|
1971
2332
|
if (!response.ok) {
|
|
1972
|
-
throw new HttpException(
|
|
2333
|
+
throw new HttpException(
|
|
2334
|
+
describeApiError(responseText, response.status),
|
|
2335
|
+
response.status,
|
|
2336
|
+
responseText,
|
|
2337
|
+
{},
|
|
2338
|
+
response.headers.get("retry-after")
|
|
2339
|
+
);
|
|
1973
2340
|
}
|
|
1974
2341
|
return responseText;
|
|
1975
2342
|
}
|
|
@@ -2006,14 +2373,14 @@ var VatState = {
|
|
|
2006
2373
|
function getFailedVatStates() {
|
|
2007
2374
|
return [VatState.INVALID, VatState.FRAUD];
|
|
2008
2375
|
}
|
|
2009
|
-
var
|
|
2376
|
+
var DESCRIPTIONS7 = {
|
|
2010
2377
|
[VatState.VALID]: "The VAT number is valid and active.",
|
|
2011
2378
|
[VatState.INVALID]: "The VAT number is invalid or not registered.",
|
|
2012
2379
|
[VatState.FRAUD]: "The VAT number has been flagged as potentially fraudulent.",
|
|
2013
2380
|
[VatState.UNKNOWN]: "The VAT number state could not be determined."
|
|
2014
2381
|
};
|
|
2015
2382
|
function describeVatState(state) {
|
|
2016
|
-
return
|
|
2383
|
+
return DESCRIPTIONS7[state];
|
|
2017
2384
|
}
|
|
2018
2385
|
|
|
2019
2386
|
// src/dto/CompanyAddress.ts
|
|
@@ -2478,16 +2845,18 @@ var VatValidationAddressInput = class _VatValidationAddressInput {
|
|
|
2478
2845
|
// src/endpoints/VatEndpoint.ts
|
|
2479
2846
|
var DATE_REGEX2 = /^\d{4}-\d{2}-\d{2}$/;
|
|
2480
2847
|
var VatEndpoint = class {
|
|
2481
|
-
constructor(baseUrl, apiKey, tokenStorage, httpClient) {
|
|
2848
|
+
constructor(baseUrl, apiKey, tokenStorage, httpClient, retryPolicy = new RetryPolicy()) {
|
|
2482
2849
|
this.baseUrl = baseUrl;
|
|
2483
2850
|
this.apiKey = apiKey;
|
|
2484
2851
|
this.tokenStorage = tokenStorage;
|
|
2485
2852
|
this.httpClient = httpClient;
|
|
2853
|
+
this.retryPolicy = retryPolicy;
|
|
2486
2854
|
}
|
|
2487
2855
|
baseUrl;
|
|
2488
2856
|
apiKey;
|
|
2489
2857
|
tokenStorage;
|
|
2490
2858
|
httpClient;
|
|
2859
|
+
retryPolicy;
|
|
2491
2860
|
async validate(vatUid, companyName, provider, addressInput) {
|
|
2492
2861
|
const body = { vat_uid: vatUid };
|
|
2493
2862
|
if (companyName) body["company_name"] = companyName;
|
|
@@ -2495,44 +2864,44 @@ var VatEndpoint = class {
|
|
|
2495
2864
|
if (addressInput) {
|
|
2496
2865
|
body["address_input"] = addressInput instanceof VatValidationAddressInput ? addressInput.toArray() : addressInput;
|
|
2497
2866
|
}
|
|
2498
|
-
return this.
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
if (attemptsLeft <= 1) {
|
|
2504
|
-
const text = await response.text();
|
|
2505
|
-
throw new HttpException("VAT validation timed out after multiple attempts", 504, text);
|
|
2506
|
-
}
|
|
2507
|
-
return this.validateWithGatewayRetry(body, attemptsLeft - 1);
|
|
2508
|
-
}
|
|
2509
|
-
const data = await this.parseJsonResponse(response);
|
|
2510
|
-
return VatResource.fromArray(data);
|
|
2867
|
+
return withRetries(this.retryPolicy, "VAT validation", async () => {
|
|
2868
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/vat/validate`, body);
|
|
2869
|
+
const data = await this.parseJsonResponse(response);
|
|
2870
|
+
return VatResource.fromArray(data);
|
|
2871
|
+
});
|
|
2511
2872
|
}
|
|
2512
2873
|
async validateMultiple(vatUids, companyNames, provider) {
|
|
2513
2874
|
const body = { vat_uids: vatUids };
|
|
2514
2875
|
if (companyNames) body["company_names"] = companyNames;
|
|
2515
2876
|
if (provider) body["source"] = provider;
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2877
|
+
return withRetries(this.retryPolicy, "VAT validation", async () => {
|
|
2878
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/vat/validate/multiple`, body);
|
|
2879
|
+
const data = await this.parseJsonResponse(response);
|
|
2880
|
+
return VatCollection.fromResponse(data);
|
|
2881
|
+
});
|
|
2519
2882
|
}
|
|
2520
2883
|
async validateSchema(vatUid) {
|
|
2521
2884
|
const body = { vat_uid: vatUid };
|
|
2522
|
-
|
|
2523
|
-
|
|
2885
|
+
return withRetries(this.retryPolicy, "VAT schema check", async () => {
|
|
2886
|
+
const response = await this.sendRequest("POST", `${this.baseUrl}/vat/validate/schema`, body);
|
|
2887
|
+
return this.parseJsonResponse(response);
|
|
2888
|
+
});
|
|
2524
2889
|
}
|
|
2525
2890
|
async state(vatUid) {
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2891
|
+
return withRetries(this.retryPolicy, "VAT state lookup", async () => {
|
|
2892
|
+
const response = await this.sendRequest("GET", `${this.baseUrl}/vat/state/${encodeURIComponent(vatUid)}`);
|
|
2893
|
+
const data = await this.parseJsonResponse(response);
|
|
2894
|
+
return VatResource.fromArray(data);
|
|
2895
|
+
});
|
|
2529
2896
|
}
|
|
2530
2897
|
async history(vatUid) {
|
|
2531
2898
|
let url = `${this.baseUrl}/vat/history`;
|
|
2532
2899
|
if (vatUid) url += `?vat_uid=${encodeURIComponent(vatUid)}`;
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2900
|
+
return withRetries(this.retryPolicy, "VAT history request", async () => {
|
|
2901
|
+
const response = await this.sendRequest("GET", url);
|
|
2902
|
+
const data = await this.parseJsonResponse(response);
|
|
2903
|
+
return VatCollection.fromResponse(data);
|
|
2904
|
+
});
|
|
2536
2905
|
}
|
|
2537
2906
|
async search(term, perPage) {
|
|
2538
2907
|
const params = new URLSearchParams();
|
|
@@ -2540,16 +2909,20 @@ var VatEndpoint = class {
|
|
|
2540
2909
|
if (perPage !== void 0) params.set("per_page", String(perPage));
|
|
2541
2910
|
const queryString = params.toString();
|
|
2542
2911
|
const url = `${this.baseUrl}/vat/search${queryString ? `?${queryString}` : ""}`;
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2912
|
+
return withRetries(this.retryPolicy, "VAT search", async () => {
|
|
2913
|
+
const response = await this.sendRequest("GET", url);
|
|
2914
|
+
const data = await this.parseJsonResponse(response);
|
|
2915
|
+
return VatCollection.fromResponse(data);
|
|
2916
|
+
});
|
|
2546
2917
|
}
|
|
2547
2918
|
async certificate(uuid, lang) {
|
|
2548
2919
|
let url = `${this.baseUrl}/vat/certificate/${encodeURIComponent(uuid)}`;
|
|
2549
2920
|
if (lang) url += `?lang=${lang}`;
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2921
|
+
return withRetries(this.retryPolicy, "VAT certificate download", async () => {
|
|
2922
|
+
const response = await this.sendBinaryRequest("GET", url);
|
|
2923
|
+
const buffer = await response.arrayBuffer();
|
|
2924
|
+
return new Uint8Array(buffer);
|
|
2925
|
+
});
|
|
2553
2926
|
}
|
|
2554
2927
|
async certificatesBulkExport(fromDate, toDate, countries, lang) {
|
|
2555
2928
|
return this.exportRequest(`${this.baseUrl}/vat/certificates/bulk-export`, fromDate, toDate, countries, lang);
|
|
@@ -2573,9 +2946,11 @@ var VatEndpoint = class {
|
|
|
2573
2946
|
}
|
|
2574
2947
|
async downloadBulkExport(exportId) {
|
|
2575
2948
|
const url = `${this.baseUrl}/vat/certificates/bulk-export/${encodeURIComponent(exportId)}/download`;
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2949
|
+
return withRetries(this.retryPolicy, "VAT export download", async () => {
|
|
2950
|
+
const response = await this.sendBinaryRequest("GET", url);
|
|
2951
|
+
const buffer = await response.arrayBuffer();
|
|
2952
|
+
return new Uint8Array(buffer);
|
|
2953
|
+
});
|
|
2579
2954
|
}
|
|
2580
2955
|
buildHeaders() {
|
|
2581
2956
|
const headers = {
|
|
@@ -2599,7 +2974,13 @@ var VatEndpoint = class {
|
|
|
2599
2974
|
const response = await this.httpClient.request(method, url, { headers });
|
|
2600
2975
|
if (!response.ok) {
|
|
2601
2976
|
const text = await response.text();
|
|
2602
|
-
throw new HttpException(
|
|
2977
|
+
throw new HttpException(
|
|
2978
|
+
describeApiError(text, response.status),
|
|
2979
|
+
response.status,
|
|
2980
|
+
text,
|
|
2981
|
+
{},
|
|
2982
|
+
response.headers.get("retry-after")
|
|
2983
|
+
);
|
|
2603
2984
|
}
|
|
2604
2985
|
return response;
|
|
2605
2986
|
}
|
|
@@ -2614,19 +2995,25 @@ var VatEndpoint = class {
|
|
|
2614
2995
|
}
|
|
2615
2996
|
} catch {
|
|
2616
2997
|
}
|
|
2617
|
-
throw new ValidationException("Validation failed", errors);
|
|
2998
|
+
throw new ValidationException(jsonErrorMessage(responseText) ?? "Validation failed", errors);
|
|
2618
2999
|
}
|
|
2619
3000
|
if (!response.ok && !acceptedStatuses.includes(response.status)) {
|
|
2620
|
-
throw new HttpException(
|
|
3001
|
+
throw new HttpException(
|
|
3002
|
+
describeApiError(responseText, response.status),
|
|
3003
|
+
response.status,
|
|
3004
|
+
responseText,
|
|
3005
|
+
{},
|
|
3006
|
+
response.headers.get("retry-after")
|
|
3007
|
+
);
|
|
2621
3008
|
}
|
|
2622
3009
|
let parsed;
|
|
2623
3010
|
try {
|
|
2624
3011
|
parsed = JSON.parse(responseText);
|
|
2625
3012
|
} catch {
|
|
2626
|
-
throw new HttpException("Invalid JSON response",
|
|
3013
|
+
throw new HttpException("Invalid JSON response", response.status, responseText);
|
|
2627
3014
|
}
|
|
2628
3015
|
if (typeof parsed !== "object" || parsed === null) {
|
|
2629
|
-
throw new HttpException("Unexpected response format",
|
|
3016
|
+
throw new HttpException("Unexpected response format", response.status, responseText);
|
|
2630
3017
|
}
|
|
2631
3018
|
const obj = parsed;
|
|
2632
3019
|
if (obj["data"] !== void 0 && (typeof obj["data"] === "object" || Array.isArray(obj["data"]))) {
|
|
@@ -2725,7 +3112,7 @@ var AuthRetryHttpClient = class {
|
|
|
2725
3112
|
};
|
|
2726
3113
|
|
|
2727
3114
|
// src/version.ts
|
|
2728
|
-
var SDK_VERSION = "1.
|
|
3115
|
+
var SDK_VERSION = "1.2.0";
|
|
2729
3116
|
|
|
2730
3117
|
// src/http/SdkVersionHttpClient.ts
|
|
2731
3118
|
var SDK_VERSION_HEADER = "X-Taxora-SDK-Version";
|
|
@@ -2757,14 +3144,15 @@ var TaxoraClient = class {
|
|
|
2757
3144
|
const environment = options.environment ?? Environment.SANDBOX;
|
|
2758
3145
|
const tokenStorage = options.tokenStorage ?? new InMemoryTokenStorage();
|
|
2759
3146
|
const httpClient = options.httpClient ?? new FetchHttpClient();
|
|
3147
|
+
const retryPolicy = options.retryPolicy ?? new RetryPolicy();
|
|
2760
3148
|
const versionedClient = new SdkVersionHttpClient(httpClient);
|
|
2761
3149
|
this.baseUrl = getBaseUrl(environment);
|
|
2762
|
-
this.auth = new AuthEndpoint(this.baseUrl, options.apiKey, tokenStorage, versionedClient);
|
|
3150
|
+
this.auth = new AuthEndpoint(this.baseUrl, options.apiKey, tokenStorage, versionedClient, retryPolicy);
|
|
2763
3151
|
const retryClient = new AuthRetryHttpClient(versionedClient, tokenStorage, () => this.auth.refresh());
|
|
2764
|
-
this.vat = new VatEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient);
|
|
2765
|
-
this.company = new CompanyEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient);
|
|
2766
|
-
this.smartEnrichment = new SmartEnrichmentEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient);
|
|
2767
|
-
this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient);
|
|
3152
|
+
this.vat = new VatEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
3153
|
+
this.company = new CompanyEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
3154
|
+
this.smartEnrichment = new SmartEnrichmentEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
3155
|
+
this.eReporting = new EReportingEndpoint(this.baseUrl, options.apiKey, tokenStorage, retryClient, retryPolicy);
|
|
2768
3156
|
}
|
|
2769
3157
|
};
|
|
2770
3158
|
|
|
@@ -2812,6 +3200,7 @@ export {
|
|
|
2812
3200
|
LoginIdentifier,
|
|
2813
3201
|
ProviderDocument,
|
|
2814
3202
|
ProviderDocumentLine,
|
|
3203
|
+
RetryPolicy,
|
|
2815
3204
|
RevenueStatistics,
|
|
2816
3205
|
ScoreBreakdown,
|
|
2817
3206
|
SireneLookupResult,
|
|
@@ -2819,6 +3208,7 @@ export {
|
|
|
2819
3208
|
SmartEnrichmentHistoryPage,
|
|
2820
3209
|
SmartEnrichmentHistoryRow,
|
|
2821
3210
|
SmartEnrichmentJob,
|
|
3211
|
+
SmartEnrichmentMode,
|
|
2822
3212
|
SmartEnrichmentResource,
|
|
2823
3213
|
SmartEnrichmentStatistics,
|
|
2824
3214
|
SmartEnrichmentStatus,
|
|
@@ -2834,10 +3224,12 @@ export {
|
|
|
2834
3224
|
VatResource,
|
|
2835
3225
|
VatState,
|
|
2836
3226
|
VatValidationAddressInput,
|
|
3227
|
+
describeApiError,
|
|
2837
3228
|
describeComplianceEnrollmentStatus,
|
|
2838
3229
|
describeComplianceTaxReportState,
|
|
2839
3230
|
describeComplianceTransactionState,
|
|
2840
3231
|
describeComplianceTransactionType,
|
|
3232
|
+
describeSmartEnrichmentMode,
|
|
2841
3233
|
describeSmartEnrichmentStatus,
|
|
2842
3234
|
describeVatState,
|
|
2843
3235
|
getBaseUrl,
|
|
@@ -2846,6 +3238,7 @@ export {
|
|
|
2846
3238
|
toComplianceTaxReportState,
|
|
2847
3239
|
toComplianceTransactionState,
|
|
2848
3240
|
toComplianceTransactionType,
|
|
3241
|
+
toSmartEnrichmentMode,
|
|
2849
3242
|
toSmartEnrichmentStatus
|
|
2850
3243
|
};
|
|
2851
3244
|
//# sourceMappingURL=index.js.map
|