kommo-aiatende-api 1.2.2 → 1.2.4
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.d.mts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +81 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +81 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -41,12 +41,30 @@ var AuthManager = class {
|
|
|
41
41
|
// src/resources/leads/index.ts
|
|
42
42
|
import qs from "qs";
|
|
43
43
|
|
|
44
|
+
// src/utils/idempotency.ts
|
|
45
|
+
import { randomUUID } from "crypto";
|
|
46
|
+
function ensureRequestId(item) {
|
|
47
|
+
if (item.request_id) return item;
|
|
48
|
+
return { ...item, request_id: randomUUID() };
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
// src/base.ts
|
|
45
52
|
var BaseResource = class {
|
|
46
53
|
client;
|
|
47
54
|
constructor(client) {
|
|
48
55
|
this.client = client;
|
|
49
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* POST de criação em lote marcado como seguro para retry: cada item recebe um
|
|
59
|
+
* request_id (se ainda não tiver um) para que a Kommo deduplique reenvios após
|
|
60
|
+
* falhas transitórias de rede, e a requisição fica elegível ao retry automático.
|
|
61
|
+
*/
|
|
62
|
+
async postIdempotent(url, items) {
|
|
63
|
+
const payload = items.map(ensureRequestId);
|
|
64
|
+
const config = { _idempotentRetry: true };
|
|
65
|
+
const response = await this.client.post(url, payload, config);
|
|
66
|
+
return response.data;
|
|
67
|
+
}
|
|
50
68
|
};
|
|
51
69
|
|
|
52
70
|
// src/resources/leads/index.ts
|
|
@@ -118,8 +136,7 @@ var LeadsResource = class extends BaseResource {
|
|
|
118
136
|
}
|
|
119
137
|
async create(items) {
|
|
120
138
|
const payload = Array.isArray(items) ? items : [items];
|
|
121
|
-
|
|
122
|
-
return response.data;
|
|
139
|
+
return this.postIdempotent("/api/v4/leads", payload);
|
|
123
140
|
}
|
|
124
141
|
async update(items) {
|
|
125
142
|
const payload = Array.isArray(items) ? items : [items];
|
|
@@ -132,8 +149,7 @@ var LeadsResource = class extends BaseResource {
|
|
|
132
149
|
}
|
|
133
150
|
async createComplex(items) {
|
|
134
151
|
const payload = Array.isArray(items) ? items : [items];
|
|
135
|
-
|
|
136
|
-
return response.data;
|
|
152
|
+
return this.postIdempotent("/api/v4/leads/complex", payload);
|
|
137
153
|
}
|
|
138
154
|
async listLossReasons() {
|
|
139
155
|
const response = await this.client.get("/api/v4/leads/loss_reasons");
|
|
@@ -210,8 +226,7 @@ var ContactsResource = class extends BaseResource {
|
|
|
210
226
|
}
|
|
211
227
|
async create(items) {
|
|
212
228
|
const payload = Array.isArray(items) ? items : [items];
|
|
213
|
-
|
|
214
|
-
return response.data;
|
|
229
|
+
return this.postIdempotent("/api/v4/contacts", payload);
|
|
215
230
|
}
|
|
216
231
|
async update(items) {
|
|
217
232
|
const payload = Array.isArray(items) ? items : [items];
|
|
@@ -678,8 +693,7 @@ var CompaniesResource = class extends BaseResource {
|
|
|
678
693
|
}
|
|
679
694
|
async create(items) {
|
|
680
695
|
const payload = Array.isArray(items) ? items : [items];
|
|
681
|
-
|
|
682
|
-
return response.data;
|
|
696
|
+
return this.postIdempotent("/api/v4/companies", payload);
|
|
683
697
|
}
|
|
684
698
|
async update(items) {
|
|
685
699
|
const payload = Array.isArray(items) ? items : [items];
|
|
@@ -1394,6 +1408,61 @@ var KommoApiError = class extends Error {
|
|
|
1394
1408
|
}
|
|
1395
1409
|
};
|
|
1396
1410
|
|
|
1411
|
+
// src/http/retry.ts
|
|
1412
|
+
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["get", "delete", "patch", "put", "head", "options"]);
|
|
1413
|
+
var TRANSIENT_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
1414
|
+
"ECONNRESET",
|
|
1415
|
+
"ECONNABORTED",
|
|
1416
|
+
"ETIMEDOUT",
|
|
1417
|
+
"EAI_AGAIN",
|
|
1418
|
+
"EPIPE"
|
|
1419
|
+
]);
|
|
1420
|
+
function isTransientNetworkError(error) {
|
|
1421
|
+
if (error.response) return false;
|
|
1422
|
+
if (error.code && TRANSIENT_ERROR_CODES.has(error.code)) return true;
|
|
1423
|
+
return /socket hang up|network error/i.test(error.message || "");
|
|
1424
|
+
}
|
|
1425
|
+
function isRetryableRequest(config) {
|
|
1426
|
+
const method = (config.method || "get").toLowerCase();
|
|
1427
|
+
if (IDEMPOTENT_METHODS.has(method)) return true;
|
|
1428
|
+
return method === "post" && config._idempotentRetry === true;
|
|
1429
|
+
}
|
|
1430
|
+
function isRateLimited(error) {
|
|
1431
|
+
return error.response?.status === 429;
|
|
1432
|
+
}
|
|
1433
|
+
function retryAfterMs(error) {
|
|
1434
|
+
const header = error.response?.headers?.["retry-after"];
|
|
1435
|
+
if (!header) return null;
|
|
1436
|
+
const seconds = Number(header);
|
|
1437
|
+
if (!Number.isNaN(seconds)) return seconds * 1e3;
|
|
1438
|
+
const dateMs = Date.parse(header);
|
|
1439
|
+
if (!Number.isNaN(dateMs)) return Math.max(0, dateMs - Date.now());
|
|
1440
|
+
return null;
|
|
1441
|
+
}
|
|
1442
|
+
function wait(ms) {
|
|
1443
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1444
|
+
}
|
|
1445
|
+
function attachRetryInterceptor(client, retryConfig = {}) {
|
|
1446
|
+
const { retries = 2, baseDelayMs = 300, maxDelayMs = 3e3 } = retryConfig;
|
|
1447
|
+
client.interceptors.response.use(void 0, async (error) => {
|
|
1448
|
+
const config = error.config;
|
|
1449
|
+
const rateLimited = isRateLimited(error);
|
|
1450
|
+
if (!config || retries <= 0 || !rateLimited && (!isTransientNetworkError(error) || !isRetryableRequest(config))) {
|
|
1451
|
+
throw error;
|
|
1452
|
+
}
|
|
1453
|
+
const attempt = config._retryCount ?? 0;
|
|
1454
|
+
if (attempt >= retries) {
|
|
1455
|
+
throw error;
|
|
1456
|
+
}
|
|
1457
|
+
config._retryCount = attempt + 1;
|
|
1458
|
+
const exponential = Math.min(maxDelayMs, baseDelayMs * 2 ** attempt);
|
|
1459
|
+
const backoff = rateLimited ? retryAfterMs(error) ?? exponential : exponential;
|
|
1460
|
+
const jitter = backoff * 0.3 * Math.random();
|
|
1461
|
+
await wait(backoff + jitter);
|
|
1462
|
+
return client.request(config);
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1397
1466
|
// src/client.ts
|
|
1398
1467
|
var KommoClient = class {
|
|
1399
1468
|
config;
|
|
@@ -1433,6 +1502,7 @@ var KommoClient = class {
|
|
|
1433
1502
|
const baseURL = `https://${config.domain}.kommo.com`;
|
|
1434
1503
|
this.httpClient = axios2.create({
|
|
1435
1504
|
baseURL,
|
|
1505
|
+
timeout: config.timeout ?? 3e4,
|
|
1436
1506
|
headers: {
|
|
1437
1507
|
"Content-Type": "application/json"
|
|
1438
1508
|
},
|
|
@@ -1440,6 +1510,9 @@ var KommoClient = class {
|
|
|
1440
1510
|
return qs19.stringify(params, { arrayFormat: "brackets" });
|
|
1441
1511
|
}
|
|
1442
1512
|
});
|
|
1513
|
+
if (config.retry !== false) {
|
|
1514
|
+
attachRetryInterceptor(this.httpClient, config.retry || {});
|
|
1515
|
+
}
|
|
1443
1516
|
this.httpClient.interceptors.response.use(
|
|
1444
1517
|
(response) => response,
|
|
1445
1518
|
(error) => {
|