@kadoa/node-sdk 0.0.2 → 0.1.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/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/kadoa-sdk.ts
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/detect-entity.ts
1883
- async function fetchEntityFields(app, options) {
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("Link is required for entity field detection", {
1904
+ throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
1886
1905
  code: "VALIDATION_ERROR",
1887
1906
  details: { options }
1888
1907
  });
1889
1908
  }
1890
- const url = new URL("/v4/entity", app.baseUrl || "https://api.kadoa.com");
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("API key is required for entity detection", {
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
- const requestBody = {
1912
- link: options.link,
1913
- location: options.location || { type: "auto" },
1914
- navigationMode: options.navigationMode || "single-page"
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("Network error while fetching entity fields", {
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
- let errorData;
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("Failed to parse entity response", {
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("No entity predictions returned from the API", {
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/run-extraction.ts
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 fetchWorkflowData(sdkInstance, workflowId, limit = 100) {
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.v4WorkflowsWorkflowIdDataGet({
2056
- workflowId,
2057
- limit
2055
+ const response = await workflowsApi.v4WorkflowsPost({
2056
+ v4WorkflowsPostRequest: request
2058
2057
  });
2059
- return response.data.data ?? [];
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
- const e = wrapKadoaError(error, {
2062
- message: "Failed to retrieve extracted data from workflow",
2063
- details: { workflowId, limit }
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 waitForWorkflowCompletion(sdkInstance, workflowId, pollingInterval = 5e3, maxWaitTime = 3e5) {
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
- try {
2075
- const response = await workflowsApi.v4WorkflowsWorkflowIdGet({
2076
- workflowId
2077
- });
2078
- const workflow = response.data;
2079
- if (workflow.state !== previousState || workflow.runState !== previousRunState) {
2080
- sdkInstance.emit(
2081
- "extraction:status_changed",
2082
- {
2083
- workflowId,
2084
- previousState,
2085
- previousRunState,
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
- await new Promise((resolve) => setTimeout(resolve, pollingInterval));
2098
- } catch (error) {
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
- const timeoutError = new KadoaSdkException(
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
- async function runExtraction(sdkInstance, options) {
2120
+
2121
+ // src/extraction/extraction-runner.ts
2122
+ function validateExtractionOptions(options) {
2113
2123
  if (!options.urls || options.urls.length === 0) {
2114
- const e = new KadoaSdkException(
2115
- "At least one URL is required for extraction",
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: options.urls[0],
2125
- location: options.location || { type: "auto" },
2126
- navigationMode: options.navigationMode || "single-page"
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: options.urls[0]
2149
+ url: config.urls[0]
2134
2150
  },
2135
2151
  "extraction",
2136
2152
  {
2137
- navigationMode: options.navigationMode,
2138
- location: options.location
2153
+ navigationMode: config.navigationMode,
2154
+ location: config.location
2139
2155
  }
2140
2156
  );
2141
- const response = await getWorkflowsApi(sdkInstance).v4WorkflowsPost({
2142
- v4WorkflowsPostRequest: {
2143
- urls: options.urls,
2144
- navigationMode: options.navigationMode || "single-page",
2145
- entity: entityPrediction.entity,
2146
- name: options.name || "Untitled Workflow",
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: options.name || "Untitled Workflow",
2170
- urls: options.urls
2168
+ name: config.name,
2169
+ urls: config.urls
2171
2170
  },
2172
2171
  "extraction"
2173
2172
  );
2174
- const workflow = await waitForWorkflowCompletion(
2175
- sdkInstance,
2176
- workflowId,
2177
- options.pollingInterval,
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 successfulRunStates = ["FINISHED", "SUCCESS"];
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
- const e = new KadoaSdkException(
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
- const e = wrapKadoaError(error, {
2239
- message: "Data extraction failed for the provided URLs",
2233
+ throw wrapKadoaError(error, {
2234
+ message: ERROR_MESSAGES.EXTRACTION_FAILED,
2240
2235
  details: { urls: options.urls }
2241
2236
  });
2242
- throw e;
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