@kadoa/node-sdk 0.0.2 → 0.1.1
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 +54 -67
- package/dist/index.d.mts +171 -164
- package/dist/index.d.ts +171 -164
- package/dist/index.js +394 -368
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +394 -368
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,190 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var events = require('events');
|
|
3
4
|
var globalAxios2 = require('axios');
|
|
5
|
+
var object = require('es-toolkit/object');
|
|
4
6
|
var url = require('url');
|
|
5
|
-
var events = require('events');
|
|
6
7
|
|
|
7
8
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
9
|
|
|
9
10
|
var globalAxios2__default = /*#__PURE__*/_interopDefault(globalAxios2);
|
|
10
11
|
|
|
11
|
-
// src/
|
|
12
|
+
// src/events/index.ts
|
|
13
|
+
var KadoaEventEmitter = class extends events.EventEmitter {
|
|
14
|
+
/**
|
|
15
|
+
* Emit a typed SDK event
|
|
16
|
+
*/
|
|
17
|
+
emit(eventName, payload, source = "sdk", metadata) {
|
|
18
|
+
const event = {
|
|
19
|
+
type: eventName,
|
|
20
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
21
|
+
source,
|
|
22
|
+
payload,
|
|
23
|
+
metadata
|
|
24
|
+
};
|
|
25
|
+
return super.emit("event", event);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Subscribe to SDK events
|
|
29
|
+
*/
|
|
30
|
+
onEvent(listener) {
|
|
31
|
+
return super.on("event", listener);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to SDK events (once)
|
|
35
|
+
*/
|
|
36
|
+
onceEvent(listener) {
|
|
37
|
+
return super.once("event", listener);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Unsubscribe from SDK events
|
|
41
|
+
*/
|
|
42
|
+
offEvent(listener) {
|
|
43
|
+
return super.off("event", listener);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Remove all event listeners
|
|
47
|
+
*/
|
|
48
|
+
removeAllEventListeners() {
|
|
49
|
+
return super.removeAllListeners("event");
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/exceptions/kadoa-sdk.exception.ts
|
|
54
|
+
var KadoaSdkException = class _KadoaSdkException extends Error {
|
|
55
|
+
constructor(message, options) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "KadoaSdkException";
|
|
58
|
+
this.code = options?.code ?? "UNKNOWN";
|
|
59
|
+
this.details = options?.details;
|
|
60
|
+
if (options && "cause" in options) this.cause = options.cause;
|
|
61
|
+
Error.captureStackTrace?.(this, _KadoaSdkException);
|
|
62
|
+
}
|
|
63
|
+
static from(error, details) {
|
|
64
|
+
if (error instanceof _KadoaSdkException) return error;
|
|
65
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unexpected error";
|
|
66
|
+
return new _KadoaSdkException(message, {
|
|
67
|
+
code: "UNKNOWN",
|
|
68
|
+
details,
|
|
69
|
+
cause: error
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
toJSON() {
|
|
73
|
+
return {
|
|
74
|
+
name: this.name,
|
|
75
|
+
message: this.message,
|
|
76
|
+
code: this.code,
|
|
77
|
+
details: this.details
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
toString() {
|
|
81
|
+
return [this.name, this.code, this.message].filter(Boolean).join(": ");
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/exceptions/http.exception.ts
|
|
86
|
+
var KadoaHttpException = class _KadoaHttpException extends KadoaSdkException {
|
|
87
|
+
constructor(message, options) {
|
|
88
|
+
super(message, {
|
|
89
|
+
code: options?.code,
|
|
90
|
+
details: options?.details,
|
|
91
|
+
cause: options?.cause
|
|
92
|
+
});
|
|
93
|
+
this.name = "KadoaHttpException";
|
|
94
|
+
this.httpStatus = options?.httpStatus;
|
|
95
|
+
this.requestId = options?.requestId;
|
|
96
|
+
this.endpoint = options?.endpoint;
|
|
97
|
+
this.method = options?.method;
|
|
98
|
+
this.responseBody = options?.responseBody;
|
|
99
|
+
}
|
|
100
|
+
static fromAxiosError(error, extra) {
|
|
101
|
+
const status = error.response?.status;
|
|
102
|
+
const requestId = error.response?.headers?.["x-request-id"] || error.response?.headers?.["x-amzn-requestid"];
|
|
103
|
+
const method = error.config?.method?.toUpperCase();
|
|
104
|
+
const url = error.config?.url;
|
|
105
|
+
return new _KadoaHttpException(extra?.message || error.message, {
|
|
106
|
+
code: _KadoaHttpException.mapStatusToCode(error),
|
|
107
|
+
httpStatus: status,
|
|
108
|
+
requestId,
|
|
109
|
+
endpoint: url,
|
|
110
|
+
method,
|
|
111
|
+
responseBody: error.response?.data,
|
|
112
|
+
details: extra?.details,
|
|
113
|
+
cause: error
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
toJSON() {
|
|
117
|
+
return {
|
|
118
|
+
...super.toJSON(),
|
|
119
|
+
httpStatus: this.httpStatus,
|
|
120
|
+
requestId: this.requestId,
|
|
121
|
+
endpoint: this.endpoint,
|
|
122
|
+
method: this.method,
|
|
123
|
+
responseBody: this.responseBody
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
static mapStatusToCode(error) {
|
|
127
|
+
const status = error.response?.status;
|
|
128
|
+
if (!status) {
|
|
129
|
+
return error.code === "ECONNABORTED" ? "TIMEOUT" : error.request ? "NETWORK_ERROR" : "UNKNOWN";
|
|
130
|
+
}
|
|
131
|
+
if (status === 401 || status === 403) return "AUTH_ERROR";
|
|
132
|
+
if (status === 404) return "NOT_FOUND";
|
|
133
|
+
if (status === 408) return "TIMEOUT";
|
|
134
|
+
if (status === 429) return "RATE_LIMITED";
|
|
135
|
+
if (status >= 400 && status < 500) return "VALIDATION_ERROR";
|
|
136
|
+
if (status >= 500) return "HTTP_ERROR";
|
|
137
|
+
return "UNKNOWN";
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
function isKadoaSdkException(error) {
|
|
141
|
+
return error instanceof KadoaSdkException;
|
|
142
|
+
}
|
|
143
|
+
function isKadoaHttpException(error) {
|
|
144
|
+
return error instanceof KadoaHttpException;
|
|
145
|
+
}
|
|
146
|
+
function wrapKadoaError(error, extra) {
|
|
147
|
+
if (error instanceof globalAxios2.AxiosError)
|
|
148
|
+
return KadoaHttpException.fromAxiosError(error, extra);
|
|
149
|
+
return KadoaSdkException.from(error, extra?.details);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/extraction/constants.ts
|
|
153
|
+
var DEFAULT_OPTIONS = {
|
|
154
|
+
pollingInterval: 5e3,
|
|
155
|
+
maxWaitTime: 3e5,
|
|
156
|
+
navigationMode: "single-page",
|
|
157
|
+
location: { type: "auto" },
|
|
158
|
+
name: "Untitled Workflow",
|
|
159
|
+
dataLimit: 100
|
|
160
|
+
};
|
|
161
|
+
var MAX_DATA_LIMIT = 99999;
|
|
162
|
+
var TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
|
|
163
|
+
"FINISHED",
|
|
164
|
+
"SUCCESS",
|
|
165
|
+
"FAILED",
|
|
166
|
+
"ERROR",
|
|
167
|
+
"STOPPED",
|
|
168
|
+
"CANCELLED"
|
|
169
|
+
]);
|
|
170
|
+
var SUCCESSFUL_RUN_STATES = /* @__PURE__ */ new Set(["FINISHED", "SUCCESS"]);
|
|
171
|
+
var ENTITY_API_ENDPOINT = "/v4/entity";
|
|
172
|
+
var DEFAULT_API_BASE_URL = "https://api.kadoa.com";
|
|
173
|
+
var ERROR_MESSAGES = {
|
|
174
|
+
NO_URLS: "At least one URL is required for extraction",
|
|
175
|
+
NO_API_KEY: "API key is required for entity detection",
|
|
176
|
+
LINK_REQUIRED: "Link is required for entity field detection",
|
|
177
|
+
NO_WORKFLOW_ID: "Failed to start extraction process - no ID received",
|
|
178
|
+
NO_PREDICTIONS: "No entity predictions returned from the API",
|
|
179
|
+
PARSE_ERROR: "Failed to parse entity response",
|
|
180
|
+
NETWORK_ERROR: "Network error while fetching entity fields",
|
|
181
|
+
AUTH_FAILED: "Authentication failed. Please check your API key",
|
|
182
|
+
RATE_LIMITED: "Rate limit exceeded. Please try again later",
|
|
183
|
+
SERVER_ERROR: "Server error while fetching entity fields",
|
|
184
|
+
DATA_FETCH_FAILED: "Failed to retrieve extracted data from workflow",
|
|
185
|
+
PROGRESS_CHECK_FAILED: "Failed to check extraction progress",
|
|
186
|
+
EXTRACTION_FAILED: "Data extraction failed for the provided URLs"
|
|
187
|
+
};
|
|
12
188
|
var BASE_PATH = "https://api.kadoa.com".replace(/\/+$/, "");
|
|
13
189
|
var BaseAPI = class {
|
|
14
190
|
constructor(configuration, basePath = BASE_PATH, axios2 = globalAxios2__default.default) {
|
|
@@ -1693,180 +1869,6 @@ var Configuration = class {
|
|
|
1693
1869
|
return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === "application/json-patch+json");
|
|
1694
1870
|
}
|
|
1695
1871
|
};
|
|
1696
|
-
var KadoaEventEmitter = class extends events.EventEmitter {
|
|
1697
|
-
/**
|
|
1698
|
-
* Emit a typed SDK event
|
|
1699
|
-
*/
|
|
1700
|
-
emit(eventName, payload, source = "sdk", metadata) {
|
|
1701
|
-
const event = {
|
|
1702
|
-
type: eventName,
|
|
1703
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
1704
|
-
source,
|
|
1705
|
-
payload,
|
|
1706
|
-
metadata
|
|
1707
|
-
};
|
|
1708
|
-
return super.emit("event", event);
|
|
1709
|
-
}
|
|
1710
|
-
/**
|
|
1711
|
-
* Subscribe to SDK events
|
|
1712
|
-
*/
|
|
1713
|
-
onEvent(listener) {
|
|
1714
|
-
return super.on("event", listener);
|
|
1715
|
-
}
|
|
1716
|
-
/**
|
|
1717
|
-
* Subscribe to SDK events (once)
|
|
1718
|
-
*/
|
|
1719
|
-
onceEvent(listener) {
|
|
1720
|
-
return super.once("event", listener);
|
|
1721
|
-
}
|
|
1722
|
-
/**
|
|
1723
|
-
* Unsubscribe from SDK events
|
|
1724
|
-
*/
|
|
1725
|
-
offEvent(listener) {
|
|
1726
|
-
return super.off("event", listener);
|
|
1727
|
-
}
|
|
1728
|
-
/**
|
|
1729
|
-
* Remove all event listeners
|
|
1730
|
-
*/
|
|
1731
|
-
removeAllEventListeners() {
|
|
1732
|
-
return super.removeAllListeners("event");
|
|
1733
|
-
}
|
|
1734
|
-
};
|
|
1735
|
-
|
|
1736
|
-
// src/kadoa-sdk.ts
|
|
1737
|
-
function initializeSdk(config) {
|
|
1738
|
-
const baseUrl = config.baseUrl || "https://api.kadoa.com";
|
|
1739
|
-
const configParams = {
|
|
1740
|
-
apiKey: config.apiKey,
|
|
1741
|
-
basePath: baseUrl
|
|
1742
|
-
};
|
|
1743
|
-
const configuration = new Configuration(configParams);
|
|
1744
|
-
const axiosInstance = globalAxios2__default.default.create({
|
|
1745
|
-
timeout: config.timeout || 3e4
|
|
1746
|
-
});
|
|
1747
|
-
const events = new KadoaEventEmitter();
|
|
1748
|
-
return {
|
|
1749
|
-
configuration,
|
|
1750
|
-
axiosInstance,
|
|
1751
|
-
baseUrl,
|
|
1752
|
-
events,
|
|
1753
|
-
emit: (eventName, payload, source, metadata) => {
|
|
1754
|
-
events.emit(eventName, payload, source, metadata);
|
|
1755
|
-
},
|
|
1756
|
-
onEvent: (listener) => {
|
|
1757
|
-
events.onEvent(listener);
|
|
1758
|
-
},
|
|
1759
|
-
offEvent: (listener) => {
|
|
1760
|
-
events.offEvent(listener);
|
|
1761
|
-
}
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
function dispose(sdkInstance) {
|
|
1765
|
-
if (sdkInstance?.events) {
|
|
1766
|
-
sdkInstance.events.removeAllListeners();
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
|
-
// src/exceptions/kadoa-sdk.exception.ts
|
|
1771
|
-
var KadoaSdkException = class _KadoaSdkException extends Error {
|
|
1772
|
-
constructor(message, options) {
|
|
1773
|
-
super(message);
|
|
1774
|
-
this.name = "KadoaSdkException";
|
|
1775
|
-
this.code = options?.code ?? "UNKNOWN";
|
|
1776
|
-
this.details = options?.details;
|
|
1777
|
-
if (options && "cause" in options) this.cause = options.cause;
|
|
1778
|
-
Error.captureStackTrace?.(this, _KadoaSdkException);
|
|
1779
|
-
}
|
|
1780
|
-
static from(error, details) {
|
|
1781
|
-
if (error instanceof _KadoaSdkException) return error;
|
|
1782
|
-
const message = error instanceof Error ? error.message : typeof error === "string" ? error : "Unexpected error";
|
|
1783
|
-
return new _KadoaSdkException(message, {
|
|
1784
|
-
code: "UNKNOWN",
|
|
1785
|
-
details,
|
|
1786
|
-
cause: error
|
|
1787
|
-
});
|
|
1788
|
-
}
|
|
1789
|
-
toJSON() {
|
|
1790
|
-
return {
|
|
1791
|
-
name: this.name,
|
|
1792
|
-
message: this.message,
|
|
1793
|
-
code: this.code,
|
|
1794
|
-
details: this.details
|
|
1795
|
-
};
|
|
1796
|
-
}
|
|
1797
|
-
toString() {
|
|
1798
|
-
return [this.name, this.code, this.message].filter(Boolean).join(": ");
|
|
1799
|
-
}
|
|
1800
|
-
};
|
|
1801
|
-
|
|
1802
|
-
// src/exceptions/http.exception.ts
|
|
1803
|
-
var KadoaHttpException = class _KadoaHttpException extends KadoaSdkException {
|
|
1804
|
-
constructor(message, options) {
|
|
1805
|
-
super(message, {
|
|
1806
|
-
code: options?.code,
|
|
1807
|
-
details: options?.details,
|
|
1808
|
-
cause: options?.cause
|
|
1809
|
-
});
|
|
1810
|
-
this.name = "KadoaHttpException";
|
|
1811
|
-
this.httpStatus = options?.httpStatus;
|
|
1812
|
-
this.requestId = options?.requestId;
|
|
1813
|
-
this.endpoint = options?.endpoint;
|
|
1814
|
-
this.method = options?.method;
|
|
1815
|
-
this.responseBody = options?.responseBody;
|
|
1816
|
-
}
|
|
1817
|
-
static fromAxiosError(error, extra) {
|
|
1818
|
-
const status = error.response?.status;
|
|
1819
|
-
const requestId = error.response?.headers?.["x-request-id"] || error.response?.headers?.["x-amzn-requestid"];
|
|
1820
|
-
const method = error.config?.method?.toUpperCase();
|
|
1821
|
-
const url = error.config?.url;
|
|
1822
|
-
return new _KadoaHttpException(extra?.message || error.message, {
|
|
1823
|
-
code: _KadoaHttpException.mapStatusToCode(error),
|
|
1824
|
-
httpStatus: status,
|
|
1825
|
-
requestId,
|
|
1826
|
-
endpoint: url,
|
|
1827
|
-
method,
|
|
1828
|
-
responseBody: error.response?.data,
|
|
1829
|
-
details: extra?.details,
|
|
1830
|
-
cause: error
|
|
1831
|
-
});
|
|
1832
|
-
}
|
|
1833
|
-
toJSON() {
|
|
1834
|
-
return {
|
|
1835
|
-
...super.toJSON(),
|
|
1836
|
-
httpStatus: this.httpStatus,
|
|
1837
|
-
requestId: this.requestId,
|
|
1838
|
-
endpoint: this.endpoint,
|
|
1839
|
-
method: this.method,
|
|
1840
|
-
responseBody: this.responseBody
|
|
1841
|
-
};
|
|
1842
|
-
}
|
|
1843
|
-
static mapStatusToCode(error) {
|
|
1844
|
-
const status = error.response?.status;
|
|
1845
|
-
if (!status) {
|
|
1846
|
-
return error.code === "ECONNABORTED" ? "TIMEOUT" : error.request ? "NETWORK_ERROR" : "UNKNOWN";
|
|
1847
|
-
}
|
|
1848
|
-
if (status === 401 || status === 403) return "AUTH_ERROR";
|
|
1849
|
-
if (status === 404) return "NOT_FOUND";
|
|
1850
|
-
if (status === 408) return "TIMEOUT";
|
|
1851
|
-
if (status === 429) return "RATE_LIMITED";
|
|
1852
|
-
if (status >= 400 && status < 500) return "VALIDATION_ERROR";
|
|
1853
|
-
if (status >= 500) return "HTTP_ERROR";
|
|
1854
|
-
return "UNKNOWN";
|
|
1855
|
-
}
|
|
1856
|
-
};
|
|
1857
|
-
|
|
1858
|
-
// src/exceptions/utils.ts
|
|
1859
|
-
function isKadoaSdkException(error) {
|
|
1860
|
-
return error instanceof KadoaSdkException;
|
|
1861
|
-
}
|
|
1862
|
-
function isKadoaHttpException(error) {
|
|
1863
|
-
return error instanceof KadoaHttpException;
|
|
1864
|
-
}
|
|
1865
|
-
function wrapKadoaError(error, extra) {
|
|
1866
|
-
if (error instanceof globalAxios2.AxiosError)
|
|
1867
|
-
return KadoaHttpException.fromAxiosError(error, extra);
|
|
1868
|
-
return KadoaSdkException.from(error, extra?.details);
|
|
1869
|
-
}
|
|
1870
1872
|
|
|
1871
1873
|
// src/api-client.ts
|
|
1872
1874
|
var workflowsApiCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -1879,20 +1881,37 @@ function getWorkflowsApi(app) {
|
|
|
1879
1881
|
return api;
|
|
1880
1882
|
}
|
|
1881
1883
|
|
|
1882
|
-
// src/extraction/
|
|
1883
|
-
async function
|
|
1884
|
+
// src/extraction/data-fetcher.ts
|
|
1885
|
+
async function fetchWorkflowData(sdkInstance, workflowId, limit = DEFAULT_OPTIONS.dataLimit) {
|
|
1886
|
+
const workflowsApi = getWorkflowsApi(sdkInstance);
|
|
1887
|
+
try {
|
|
1888
|
+
const response = await workflowsApi.v4WorkflowsWorkflowIdDataGet({
|
|
1889
|
+
workflowId,
|
|
1890
|
+
limit
|
|
1891
|
+
});
|
|
1892
|
+
return response.data.data ?? [];
|
|
1893
|
+
} catch (error) {
|
|
1894
|
+
throw wrapKadoaError(error, {
|
|
1895
|
+
message: ERROR_MESSAGES.DATA_FETCH_FAILED,
|
|
1896
|
+
details: { workflowId, limit }
|
|
1897
|
+
});
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
// src/extraction/entity-detector.ts
|
|
1902
|
+
function validateEntityOptions(options) {
|
|
1884
1903
|
if (!options.link) {
|
|
1885
|
-
throw new KadoaSdkException(
|
|
1904
|
+
throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
|
|
1886
1905
|
code: "VALIDATION_ERROR",
|
|
1887
1906
|
details: { options }
|
|
1888
1907
|
});
|
|
1889
1908
|
}
|
|
1890
|
-
|
|
1909
|
+
}
|
|
1910
|
+
async function buildRequestHeaders(config) {
|
|
1891
1911
|
const headers = {
|
|
1892
1912
|
"Content-Type": "application/json",
|
|
1893
1913
|
Accept: "application/json"
|
|
1894
1914
|
};
|
|
1895
|
-
const config = app.configuration;
|
|
1896
1915
|
if (config?.apiKey) {
|
|
1897
1916
|
if (typeof config.apiKey === "function") {
|
|
1898
1917
|
const apiKeyValue = await config.apiKey("X-API-Key");
|
|
@@ -1903,16 +1922,71 @@ async function fetchEntityFields(app, options) {
|
|
|
1903
1922
|
headers["X-API-Key"] = config.apiKey;
|
|
1904
1923
|
}
|
|
1905
1924
|
} else {
|
|
1906
|
-
throw new KadoaSdkException(
|
|
1925
|
+
throw new KadoaSdkException(ERROR_MESSAGES.NO_API_KEY, {
|
|
1907
1926
|
code: "AUTH_ERROR",
|
|
1908
1927
|
details: { hasConfig: !!config, hasApiKey: !!config?.apiKey }
|
|
1909
1928
|
});
|
|
1910
1929
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1930
|
+
return headers;
|
|
1931
|
+
}
|
|
1932
|
+
function getErrorCodeFromStatus(status) {
|
|
1933
|
+
if (status === 401 || status === 403) return "AUTH_ERROR";
|
|
1934
|
+
if (status === 404) return "NOT_FOUND";
|
|
1935
|
+
if (status === 429) return "RATE_LIMITED";
|
|
1936
|
+
if (status >= 400 && status < 500) return "VALIDATION_ERROR";
|
|
1937
|
+
if (status >= 500) return "HTTP_ERROR";
|
|
1938
|
+
return "UNKNOWN";
|
|
1939
|
+
}
|
|
1940
|
+
async function handleErrorResponse(response, url, link) {
|
|
1941
|
+
let errorData;
|
|
1942
|
+
let errorText = "";
|
|
1943
|
+
try {
|
|
1944
|
+
errorText = await response.text();
|
|
1945
|
+
errorData = JSON.parse(errorText);
|
|
1946
|
+
} catch {
|
|
1947
|
+
errorData = { message: errorText || response.statusText };
|
|
1948
|
+
}
|
|
1949
|
+
const baseErrorOptions = {
|
|
1950
|
+
httpStatus: response.status,
|
|
1951
|
+
endpoint: url.toString(),
|
|
1952
|
+
method: "POST",
|
|
1953
|
+
responseBody: errorData,
|
|
1954
|
+
details: {
|
|
1955
|
+
url: url.toString(),
|
|
1956
|
+
link
|
|
1957
|
+
}
|
|
1915
1958
|
};
|
|
1959
|
+
if (response.status === 401) {
|
|
1960
|
+
throw new KadoaHttpException(ERROR_MESSAGES.AUTH_FAILED, {
|
|
1961
|
+
...baseErrorOptions,
|
|
1962
|
+
code: "AUTH_ERROR"
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
if (response.status === 429) {
|
|
1966
|
+
throw new KadoaHttpException(ERROR_MESSAGES.RATE_LIMITED, {
|
|
1967
|
+
...baseErrorOptions,
|
|
1968
|
+
code: "RATE_LIMITED"
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
if (response.status >= 500) {
|
|
1972
|
+
throw new KadoaHttpException(ERROR_MESSAGES.SERVER_ERROR, {
|
|
1973
|
+
...baseErrorOptions,
|
|
1974
|
+
code: "HTTP_ERROR"
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
throw new KadoaHttpException(
|
|
1978
|
+
`Failed to fetch entity fields: ${errorData?.message || response.statusText}`,
|
|
1979
|
+
{
|
|
1980
|
+
...baseErrorOptions,
|
|
1981
|
+
code: getErrorCodeFromStatus(response.status)
|
|
1982
|
+
}
|
|
1983
|
+
);
|
|
1984
|
+
}
|
|
1985
|
+
async function fetchEntityFields(app, options) {
|
|
1986
|
+
validateEntityOptions(options);
|
|
1987
|
+
const url = new URL(ENTITY_API_ENDPOINT, app.baseUrl || DEFAULT_API_BASE_URL);
|
|
1988
|
+
const headers = await buildRequestHeaders(app.configuration);
|
|
1989
|
+
const requestBody = options;
|
|
1916
1990
|
let response;
|
|
1917
1991
|
try {
|
|
1918
1992
|
response = await fetch(url.toString(), {
|
|
@@ -1921,7 +1995,7 @@ async function fetchEntityFields(app, options) {
|
|
|
1921
1995
|
body: JSON.stringify(requestBody)
|
|
1922
1996
|
});
|
|
1923
1997
|
} catch (error) {
|
|
1924
|
-
throw new KadoaSdkException(
|
|
1998
|
+
throw new KadoaSdkException(ERROR_MESSAGES.NETWORK_ERROR, {
|
|
1925
1999
|
code: "NETWORK_ERROR",
|
|
1926
2000
|
details: {
|
|
1927
2001
|
url: url.toString(),
|
|
@@ -1931,89 +2005,13 @@ async function fetchEntityFields(app, options) {
|
|
|
1931
2005
|
});
|
|
1932
2006
|
}
|
|
1933
2007
|
if (!response.ok) {
|
|
1934
|
-
|
|
1935
|
-
let errorText = "";
|
|
1936
|
-
try {
|
|
1937
|
-
errorText = await response.text();
|
|
1938
|
-
errorData = JSON.parse(errorText);
|
|
1939
|
-
} catch {
|
|
1940
|
-
errorData = { message: errorText || response.statusText };
|
|
1941
|
-
}
|
|
1942
|
-
const getErrorCode = (status) => {
|
|
1943
|
-
if (status === 401 || status === 403) return "AUTH_ERROR";
|
|
1944
|
-
if (status === 404) return "NOT_FOUND";
|
|
1945
|
-
if (status === 429) return "RATE_LIMITED";
|
|
1946
|
-
if (status >= 400 && status < 500) return "VALIDATION_ERROR";
|
|
1947
|
-
if (status >= 500) return "HTTP_ERROR";
|
|
1948
|
-
return "UNKNOWN";
|
|
1949
|
-
};
|
|
1950
|
-
if (response.status === 401) {
|
|
1951
|
-
throw new KadoaHttpException(
|
|
1952
|
-
"Authentication failed. Please check your API key",
|
|
1953
|
-
{
|
|
1954
|
-
code: "AUTH_ERROR",
|
|
1955
|
-
httpStatus: response.status,
|
|
1956
|
-
endpoint: url.toString(),
|
|
1957
|
-
method: "POST",
|
|
1958
|
-
responseBody: errorData,
|
|
1959
|
-
details: {
|
|
1960
|
-
url: url.toString(),
|
|
1961
|
-
link: options.link
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
);
|
|
1965
|
-
} else if (response.status === 429) {
|
|
1966
|
-
throw new KadoaHttpException(
|
|
1967
|
-
"Rate limit exceeded. Please try again later",
|
|
1968
|
-
{
|
|
1969
|
-
code: "RATE_LIMITED",
|
|
1970
|
-
httpStatus: response.status,
|
|
1971
|
-
endpoint: url.toString(),
|
|
1972
|
-
method: "POST",
|
|
1973
|
-
responseBody: errorData,
|
|
1974
|
-
details: {
|
|
1975
|
-
url: url.toString(),
|
|
1976
|
-
link: options.link
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
);
|
|
1980
|
-
} else if (response.status >= 500) {
|
|
1981
|
-
throw new KadoaHttpException(
|
|
1982
|
-
"Server error while fetching entity fields",
|
|
1983
|
-
{
|
|
1984
|
-
code: "HTTP_ERROR",
|
|
1985
|
-
httpStatus: response.status,
|
|
1986
|
-
endpoint: url.toString(),
|
|
1987
|
-
method: "POST",
|
|
1988
|
-
responseBody: errorData,
|
|
1989
|
-
details: {
|
|
1990
|
-
url: url.toString(),
|
|
1991
|
-
link: options.link
|
|
1992
|
-
}
|
|
1993
|
-
}
|
|
1994
|
-
);
|
|
1995
|
-
} else {
|
|
1996
|
-
throw new KadoaHttpException(
|
|
1997
|
-
`Failed to fetch entity fields: ${errorData?.message || response.statusText}`,
|
|
1998
|
-
{
|
|
1999
|
-
code: getErrorCode(response.status),
|
|
2000
|
-
httpStatus: response.status,
|
|
2001
|
-
endpoint: url.toString(),
|
|
2002
|
-
method: "POST",
|
|
2003
|
-
responseBody: errorData,
|
|
2004
|
-
details: {
|
|
2005
|
-
url: url.toString(),
|
|
2006
|
-
link: options.link
|
|
2007
|
-
}
|
|
2008
|
-
}
|
|
2009
|
-
);
|
|
2010
|
-
}
|
|
2008
|
+
await handleErrorResponse(response, url, options.link);
|
|
2011
2009
|
}
|
|
2012
2010
|
let data;
|
|
2013
2011
|
try {
|
|
2014
2012
|
data = await response.json();
|
|
2015
2013
|
} catch (error) {
|
|
2016
|
-
throw new KadoaSdkException(
|
|
2014
|
+
throw new KadoaSdkException(ERROR_MESSAGES.PARSE_ERROR, {
|
|
2017
2015
|
code: "INTERNAL_ERROR",
|
|
2018
2016
|
details: {
|
|
2019
2017
|
url: url.toString(),
|
|
@@ -2023,7 +2021,7 @@ async function fetchEntityFields(app, options) {
|
|
|
2023
2021
|
});
|
|
2024
2022
|
}
|
|
2025
2023
|
if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
|
|
2026
|
-
throw new KadoaSdkException(
|
|
2024
|
+
throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
|
|
2027
2025
|
code: "NOT_FOUND",
|
|
2028
2026
|
details: {
|
|
2029
2027
|
success: data.success,
|
|
@@ -2036,150 +2034,148 @@ async function fetchEntityFields(app, options) {
|
|
|
2036
2034
|
return data.entityPrediction[0];
|
|
2037
2035
|
}
|
|
2038
2036
|
|
|
2039
|
-
// src/extraction/
|
|
2040
|
-
var TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
|
|
2041
|
-
"FINISHED",
|
|
2042
|
-
"SUCCESS",
|
|
2043
|
-
"FAILED",
|
|
2044
|
-
"ERROR",
|
|
2045
|
-
"STOPPED",
|
|
2046
|
-
"CANCELLED"
|
|
2047
|
-
]);
|
|
2037
|
+
// src/extraction/workflow-manager.ts
|
|
2048
2038
|
function isTerminalRunState(runState) {
|
|
2049
2039
|
if (!runState) return false;
|
|
2050
2040
|
return TERMINAL_RUN_STATES.has(runState.toUpperCase());
|
|
2051
2041
|
}
|
|
2052
|
-
async function
|
|
2042
|
+
async function createWorkflow(sdkInstance, config) {
|
|
2053
2043
|
const workflowsApi = getWorkflowsApi(sdkInstance);
|
|
2044
|
+
const request = {
|
|
2045
|
+
urls: config.urls,
|
|
2046
|
+
navigationMode: config.navigationMode,
|
|
2047
|
+
entity: config.entity,
|
|
2048
|
+
name: config.name,
|
|
2049
|
+
fields: config.fields,
|
|
2050
|
+
bypassPreview: true,
|
|
2051
|
+
limit: MAX_DATA_LIMIT,
|
|
2052
|
+
tags: ["sdk"]
|
|
2053
|
+
};
|
|
2054
2054
|
try {
|
|
2055
|
-
const response = await workflowsApi.
|
|
2056
|
-
|
|
2057
|
-
limit
|
|
2055
|
+
const response = await workflowsApi.v4WorkflowsPost({
|
|
2056
|
+
v4WorkflowsPostRequest: request
|
|
2058
2057
|
});
|
|
2059
|
-
|
|
2058
|
+
const workflowId = response.data.workflowId;
|
|
2059
|
+
if (!workflowId) {
|
|
2060
|
+
throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
|
|
2061
|
+
code: "INTERNAL_ERROR",
|
|
2062
|
+
details: { urls: config.urls }
|
|
2063
|
+
});
|
|
2064
|
+
}
|
|
2065
|
+
return workflowId;
|
|
2060
2066
|
} catch (error) {
|
|
2061
|
-
|
|
2062
|
-
message: "Failed to
|
|
2063
|
-
details:
|
|
2067
|
+
throw wrapKadoaError(error, {
|
|
2068
|
+
message: "Failed to create workflow",
|
|
2069
|
+
details: config
|
|
2064
2070
|
});
|
|
2065
|
-
throw e;
|
|
2066
2071
|
}
|
|
2067
2072
|
}
|
|
2068
|
-
async function
|
|
2073
|
+
async function getWorkflowStatus(sdkInstance, workflowId) {
|
|
2069
2074
|
const workflowsApi = getWorkflowsApi(sdkInstance);
|
|
2075
|
+
try {
|
|
2076
|
+
const response = await workflowsApi.v4WorkflowsWorkflowIdGet({
|
|
2077
|
+
workflowId
|
|
2078
|
+
});
|
|
2079
|
+
return response.data;
|
|
2080
|
+
} catch (error) {
|
|
2081
|
+
throw wrapKadoaError(error, {
|
|
2082
|
+
message: ERROR_MESSAGES.PROGRESS_CHECK_FAILED,
|
|
2083
|
+
details: { workflowId }
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
async function waitForWorkflowCompletion(sdkInstance, workflowId, options) {
|
|
2088
|
+
const pollingInterval = options.pollingInterval;
|
|
2089
|
+
const maxWaitTime = options.maxWaitTime;
|
|
2070
2090
|
const startTime = Date.now();
|
|
2071
2091
|
let previousState;
|
|
2072
2092
|
let previousRunState;
|
|
2073
2093
|
while (Date.now() - startTime < maxWaitTime) {
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
currentState: workflow.state,
|
|
2087
|
-
currentRunState: workflow.runState
|
|
2088
|
-
},
|
|
2089
|
-
"extraction"
|
|
2090
|
-
);
|
|
2091
|
-
previousState = workflow.state;
|
|
2092
|
-
previousRunState = workflow.runState;
|
|
2093
|
-
}
|
|
2094
|
-
if (isTerminalRunState(workflow.runState)) {
|
|
2095
|
-
return workflow;
|
|
2094
|
+
const workflow = await getWorkflowStatus(sdkInstance, workflowId);
|
|
2095
|
+
if (workflow.state !== previousState || workflow.runState !== previousRunState) {
|
|
2096
|
+
const statusChange = {
|
|
2097
|
+
workflowId,
|
|
2098
|
+
previousState,
|
|
2099
|
+
previousRunState,
|
|
2100
|
+
currentState: workflow.state,
|
|
2101
|
+
currentRunState: workflow.runState
|
|
2102
|
+
};
|
|
2103
|
+
sdkInstance.emit("extraction:status_changed", statusChange, "extraction");
|
|
2104
|
+
if (options?.onStatusChange) {
|
|
2105
|
+
options.onStatusChange(statusChange);
|
|
2096
2106
|
}
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
const e = wrapKadoaError(error, {
|
|
2100
|
-
message: "Failed to check extraction progress",
|
|
2101
|
-
details: { workflowId }
|
|
2102
|
-
});
|
|
2103
|
-
throw e;
|
|
2107
|
+
previousState = workflow.state;
|
|
2108
|
+
previousRunState = workflow.runState;
|
|
2104
2109
|
}
|
|
2110
|
+
if (isTerminalRunState(workflow.runState)) {
|
|
2111
|
+
return workflow;
|
|
2112
|
+
}
|
|
2113
|
+
await new Promise((resolve) => setTimeout(resolve, pollingInterval));
|
|
2105
2114
|
}
|
|
2106
|
-
|
|
2115
|
+
throw new KadoaSdkException(
|
|
2107
2116
|
`Extraction did not complete within ${maxWaitTime / 1e3} seconds`,
|
|
2108
2117
|
{ code: "TIMEOUT", details: { workflowId, maxWaitTime } }
|
|
2109
2118
|
);
|
|
2110
|
-
throw timeoutError;
|
|
2111
2119
|
}
|
|
2112
|
-
|
|
2120
|
+
|
|
2121
|
+
// src/extraction/extraction-runner.ts
|
|
2122
|
+
function validateExtractionOptions(options) {
|
|
2113
2123
|
if (!options.urls || options.urls.length === 0) {
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
code: "VALIDATION_ERROR"
|
|
2118
|
-
}
|
|
2119
|
-
);
|
|
2120
|
-
throw e;
|
|
2124
|
+
throw new KadoaSdkException(ERROR_MESSAGES.NO_URLS, {
|
|
2125
|
+
code: "VALIDATION_ERROR"
|
|
2126
|
+
});
|
|
2121
2127
|
}
|
|
2128
|
+
}
|
|
2129
|
+
function isExtractionSuccessful(runState) {
|
|
2130
|
+
return runState ? SUCCESSFUL_RUN_STATES.has(runState.toUpperCase()) : false;
|
|
2131
|
+
}
|
|
2132
|
+
async function runExtraction(sdkInstance, options) {
|
|
2133
|
+
validateExtractionOptions(options);
|
|
2134
|
+
const config = object.merge(
|
|
2135
|
+
DEFAULT_OPTIONS,
|
|
2136
|
+
options
|
|
2137
|
+
);
|
|
2122
2138
|
try {
|
|
2123
2139
|
const entityPrediction = await fetchEntityFields(sdkInstance, {
|
|
2124
|
-
link:
|
|
2125
|
-
location:
|
|
2126
|
-
navigationMode:
|
|
2140
|
+
link: config.urls[0],
|
|
2141
|
+
location: config.location,
|
|
2142
|
+
navigationMode: config.navigationMode
|
|
2127
2143
|
});
|
|
2128
2144
|
sdkInstance.emit(
|
|
2129
2145
|
"entity:detected",
|
|
2130
2146
|
{
|
|
2131
2147
|
entity: entityPrediction.entity,
|
|
2132
2148
|
fields: entityPrediction.fields,
|
|
2133
|
-
url:
|
|
2149
|
+
url: config.urls[0]
|
|
2134
2150
|
},
|
|
2135
2151
|
"extraction",
|
|
2136
2152
|
{
|
|
2137
|
-
navigationMode:
|
|
2138
|
-
location:
|
|
2153
|
+
navigationMode: config.navigationMode,
|
|
2154
|
+
location: config.location
|
|
2139
2155
|
}
|
|
2140
2156
|
);
|
|
2141
|
-
const
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
fields: entityPrediction.fields,
|
|
2148
|
-
bypassPreview: true,
|
|
2149
|
-
limit: 99999,
|
|
2150
|
-
// no limits for SDK
|
|
2151
|
-
tags: ["sdk"]
|
|
2152
|
-
}
|
|
2157
|
+
const workflowId = await createWorkflow(sdkInstance, {
|
|
2158
|
+
urls: config.urls,
|
|
2159
|
+
navigationMode: config.navigationMode,
|
|
2160
|
+
entity: entityPrediction.entity,
|
|
2161
|
+
fields: entityPrediction.fields,
|
|
2162
|
+
name: config.name
|
|
2153
2163
|
});
|
|
2154
|
-
const workflowId = response.data.workflowId;
|
|
2155
|
-
if (!workflowId) {
|
|
2156
|
-
const e = new KadoaSdkException(
|
|
2157
|
-
"Failed to start extraction process - no ID received",
|
|
2158
|
-
{
|
|
2159
|
-
code: "INTERNAL_ERROR",
|
|
2160
|
-
details: { urls: options.urls }
|
|
2161
|
-
}
|
|
2162
|
-
);
|
|
2163
|
-
throw e;
|
|
2164
|
-
}
|
|
2165
2164
|
sdkInstance.emit(
|
|
2166
2165
|
"extraction:started",
|
|
2167
2166
|
{
|
|
2168
2167
|
workflowId,
|
|
2169
|
-
name:
|
|
2170
|
-
urls:
|
|
2168
|
+
name: config.name,
|
|
2169
|
+
urls: config.urls
|
|
2171
2170
|
},
|
|
2172
2171
|
"extraction"
|
|
2173
2172
|
);
|
|
2174
|
-
const workflow = await waitForWorkflowCompletion(
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
options.maxWaitTime
|
|
2179
|
-
);
|
|
2173
|
+
const workflow = await waitForWorkflowCompletion(sdkInstance, workflowId, {
|
|
2174
|
+
pollingInterval: config.pollingInterval,
|
|
2175
|
+
maxWaitTime: config.maxWaitTime
|
|
2176
|
+
});
|
|
2180
2177
|
let data;
|
|
2181
|
-
const
|
|
2182
|
-
const isSuccess = workflow.runState && successfulRunStates.includes(workflow.runState.toUpperCase());
|
|
2178
|
+
const isSuccess = isExtractionSuccessful(workflow.runState);
|
|
2183
2179
|
if (isSuccess) {
|
|
2184
2180
|
data = await fetchWorkflowData(sdkInstance, workflowId);
|
|
2185
2181
|
if (data) {
|
|
@@ -2216,7 +2212,7 @@ async function runExtraction(sdkInstance, options) {
|
|
|
2216
2212
|
},
|
|
2217
2213
|
"extraction"
|
|
2218
2214
|
);
|
|
2219
|
-
|
|
2215
|
+
throw new KadoaSdkException(
|
|
2220
2216
|
`Extraction completed with unexpected status: ${workflow.runState}`,
|
|
2221
2217
|
{
|
|
2222
2218
|
code: "INTERNAL_ERROR",
|
|
@@ -2227,7 +2223,6 @@ async function runExtraction(sdkInstance, options) {
|
|
|
2227
2223
|
}
|
|
2228
2224
|
}
|
|
2229
2225
|
);
|
|
2230
|
-
throw e;
|
|
2231
2226
|
}
|
|
2232
2227
|
return {
|
|
2233
2228
|
workflowId,
|
|
@@ -2235,11 +2230,42 @@ async function runExtraction(sdkInstance, options) {
|
|
|
2235
2230
|
data
|
|
2236
2231
|
};
|
|
2237
2232
|
} catch (error) {
|
|
2238
|
-
|
|
2239
|
-
message:
|
|
2233
|
+
throw wrapKadoaError(error, {
|
|
2234
|
+
message: ERROR_MESSAGES.EXTRACTION_FAILED,
|
|
2240
2235
|
details: { urls: options.urls }
|
|
2241
2236
|
});
|
|
2242
|
-
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
function initializeSdk(config) {
|
|
2240
|
+
const baseUrl = config.baseUrl || "https://api.kadoa.com";
|
|
2241
|
+
const configParams = {
|
|
2242
|
+
apiKey: config.apiKey,
|
|
2243
|
+
basePath: baseUrl
|
|
2244
|
+
};
|
|
2245
|
+
const configuration = new Configuration(configParams);
|
|
2246
|
+
const axiosInstance = globalAxios2__default.default.create({
|
|
2247
|
+
timeout: config.timeout || 3e4
|
|
2248
|
+
});
|
|
2249
|
+
const events = new KadoaEventEmitter();
|
|
2250
|
+
return {
|
|
2251
|
+
configuration,
|
|
2252
|
+
axiosInstance,
|
|
2253
|
+
baseUrl,
|
|
2254
|
+
events,
|
|
2255
|
+
emit: (eventName, payload, source, metadata) => {
|
|
2256
|
+
events.emit(eventName, payload, source, metadata);
|
|
2257
|
+
},
|
|
2258
|
+
onEvent: (listener) => {
|
|
2259
|
+
events.onEvent(listener);
|
|
2260
|
+
},
|
|
2261
|
+
offEvent: (listener) => {
|
|
2262
|
+
events.offEvent(listener);
|
|
2263
|
+
}
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
function dispose(sdkInstance) {
|
|
2267
|
+
if (sdkInstance?.events) {
|
|
2268
|
+
sdkInstance.events.removeAllListeners();
|
|
2243
2269
|
}
|
|
2244
2270
|
}
|
|
2245
2271
|
|