@kadoa/node-sdk 0.2.0 → 0.4.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
@@ -2,14 +2,14 @@
2
2
 
3
3
  var events = require('events');
4
4
  var globalAxios2 = require('axios');
5
- var object = require('es-toolkit/object');
6
5
  var url = require('url');
6
+ var object = require('es-toolkit/object');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
10
10
  var globalAxios2__default = /*#__PURE__*/_interopDefault(globalAxios2);
11
11
 
12
- // src/events/index.ts
12
+ // src/core/events/event-emitter.ts
13
13
  var KadoaEventEmitter = class extends events.EventEmitter {
14
14
  /**
15
15
  * Emit a typed SDK event
@@ -50,8 +50,8 @@ var KadoaEventEmitter = class extends events.EventEmitter {
50
50
  }
51
51
  };
52
52
 
53
- // src/exceptions/kadoa-sdk.exception.ts
54
- var KadoaSdkException = class _KadoaSdkException extends Error {
53
+ // src/core/exceptions/base.exception.ts
54
+ var _KadoaSdkException = class _KadoaSdkException extends Error {
55
55
  constructor(message, options) {
56
56
  super(message);
57
57
  this.name = "KadoaSdkException";
@@ -80,9 +80,54 @@ var KadoaSdkException = class _KadoaSdkException extends Error {
80
80
  toString() {
81
81
  return [this.name, this.code, this.message].filter(Boolean).join(": ");
82
82
  }
83
+ toDetailedString() {
84
+ const parts = [`${this.name}: ${this.message}`, `Code: ${this.code}`];
85
+ if (this.details && Object.keys(this.details).length > 0) {
86
+ parts.push(`Details: ${JSON.stringify(this.details, null, 2)}`);
87
+ }
88
+ if (this.cause) {
89
+ parts.push(`Cause: ${this.cause}`);
90
+ }
91
+ return parts.join("\n");
92
+ }
93
+ static isInstance(error) {
94
+ return error instanceof _KadoaSdkException;
95
+ }
96
+ static wrap(error, extra) {
97
+ if (error instanceof _KadoaSdkException) return error;
98
+ const message = extra?.message || (error instanceof Error ? error.message : typeof error === "string" ? error : "Unexpected error");
99
+ return new _KadoaSdkException(message, {
100
+ code: "UNKNOWN",
101
+ details: extra?.details,
102
+ cause: error
103
+ });
104
+ }
83
105
  };
84
-
85
- // src/exceptions/http.exception.ts
106
+ _KadoaSdkException.ERROR_MESSAGES = {
107
+ // General errors
108
+ CONFIG_ERROR: "Invalid configuration provided",
109
+ AUTH_FAILED: "Authentication failed. Please check your API key",
110
+ RATE_LIMITED: "Rate limit exceeded. Please try again later",
111
+ NETWORK_ERROR: "Network error occurred",
112
+ SERVER_ERROR: "Server error occurred",
113
+ PARSE_ERROR: "Failed to parse response",
114
+ // Workflow specific errors
115
+ NO_WORKFLOW_ID: "Failed to start extraction process - no ID received",
116
+ WORKFLOW_CREATE_FAILED: "Failed to create workflow",
117
+ WORKFLOW_TIMEOUT: "Workflow processing timed out",
118
+ WORKFLOW_UNEXPECTED_STATUS: "Extraction completed with unexpected status",
119
+ PROGRESS_CHECK_FAILED: "Failed to check extraction progress",
120
+ DATA_FETCH_FAILED: "Failed to retrieve extracted data from workflow",
121
+ // Extraction specific errors
122
+ NO_URLS: "At least one URL is required for extraction",
123
+ NO_API_KEY: "API key is required for entity detection",
124
+ LINK_REQUIRED: "Link is required for entity field detection",
125
+ NO_PREDICTIONS: "No entity predictions returned from the API",
126
+ EXTRACTION_FAILED: "Data extraction failed for the provided URLs",
127
+ ENTITY_FETCH_FAILED: "Failed to fetch entity fields"
128
+ };
129
+ var KadoaSdkException = _KadoaSdkException;
130
+ var ERROR_MESSAGES = KadoaSdkException.ERROR_MESSAGES;
86
131
  var KadoaHttpException = class _KadoaHttpException extends KadoaSdkException {
87
132
  constructor(message, options) {
88
133
  super(message, {
@@ -123,10 +168,43 @@ var KadoaHttpException = class _KadoaHttpException extends KadoaSdkException {
123
168
  responseBody: this.responseBody
124
169
  };
125
170
  }
126
- static mapStatusToCode(error) {
127
- const status = error.response?.status;
171
+ toDetailedString() {
172
+ const parts = [`${this.name}: ${this.message}`, `Code: ${this.code}`];
173
+ if (this.httpStatus) {
174
+ parts.push(`HTTP Status: ${this.httpStatus}`);
175
+ }
176
+ if (this.method && this.endpoint) {
177
+ parts.push(`Request: ${this.method} ${this.endpoint}`);
178
+ }
179
+ if (this.requestId) {
180
+ parts.push(`Request ID: ${this.requestId}`);
181
+ }
182
+ if (this.responseBody) {
183
+ parts.push(
184
+ `Response Body: ${JSON.stringify(this.responseBody, null, 2)}`
185
+ );
186
+ }
187
+ if (this.details && Object.keys(this.details).length > 0) {
188
+ parts.push(`Details: ${JSON.stringify(this.details, null, 2)}`);
189
+ }
190
+ if (this.cause) {
191
+ parts.push(`Cause: ${this.cause}`);
192
+ }
193
+ return parts.join("\n");
194
+ }
195
+ static wrap(error, extra) {
196
+ if (error instanceof _KadoaHttpException) return error;
197
+ if (error instanceof KadoaSdkException) return error;
198
+ if (globalAxios2.isAxiosError(error)) {
199
+ return _KadoaHttpException.fromAxiosError(error, extra);
200
+ }
201
+ return KadoaSdkException.wrap(error, extra);
202
+ }
203
+ static mapStatusToCode(errorOrStatus) {
204
+ const status = typeof errorOrStatus === "number" ? errorOrStatus : errorOrStatus.response?.status;
128
205
  if (!status) {
129
- return error.code === "ECONNABORTED" ? "TIMEOUT" : error.request ? "NETWORK_ERROR" : "UNKNOWN";
206
+ if (typeof errorOrStatus === "number") return "UNKNOWN";
207
+ return errorOrStatus.code === "ECONNABORTED" ? "TIMEOUT" : errorOrStatus.request ? "NETWORK_ERROR" : "UNKNOWN";
130
208
  }
131
209
  if (status === 401 || status === 403) return "AUTH_ERROR";
132
210
  if (status === 404) return "NOT_FOUND";
@@ -137,54 +215,6 @@ var KadoaHttpException = class _KadoaHttpException extends KadoaSdkException {
137
215
  return "UNKNOWN";
138
216
  }
139
217
  };
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
- };
188
218
  var BASE_PATH = "https://api.kadoa.com".replace(/\/+$/, "");
189
219
  var BaseAPI = class {
190
220
  constructor(configuration, basePath = BASE_PATH, axios2 = globalAxios2__default.default) {
@@ -1870,412 +1900,549 @@ var Configuration = class {
1870
1900
  }
1871
1901
  };
1872
1902
 
1873
- // src/api-client.ts
1903
+ // src/core/patterns/command.ts
1904
+ var Command = class {
1905
+ };
1906
+
1907
+ // src/core/http/client-factory.ts
1874
1908
  var workflowsApiCache = /* @__PURE__ */ new WeakMap();
1875
- function getWorkflowsApi(app) {
1876
- let api = workflowsApiCache.get(app);
1909
+ function getWorkflowsApi(client) {
1910
+ let api = workflowsApiCache.get(client);
1877
1911
  if (!api) {
1878
- api = new WorkflowsApi(app.configuration, app.baseUrl, app.axiosInstance);
1879
- workflowsApiCache.set(app, api);
1912
+ api = new WorkflowsApi(
1913
+ client.configuration,
1914
+ client.baseUrl,
1915
+ client.axiosInstance
1916
+ );
1917
+ workflowsApiCache.set(client, api);
1880
1918
  }
1881
1919
  return api;
1882
1920
  }
1883
1921
 
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
- });
1922
+ // src/modules/extraction/services/data-fetcher.service.ts
1923
+ var DataFetcherService = class {
1924
+ constructor(client) {
1925
+ this.client = client;
1898
1926
  }
1899
- }
1927
+ /**
1928
+ * Fetch extracted data from a workflow
1929
+ *
1930
+ * @param workflowId The workflow ID to fetch data from
1931
+ * @param limit Maximum number of records to retrieve
1932
+ * @returns Array of extracted data objects
1933
+ */
1934
+ async fetchWorkflowData(workflowId, limit) {
1935
+ const workflowsApi = getWorkflowsApi(this.client);
1936
+ try {
1937
+ const response = await workflowsApi.v4WorkflowsWorkflowIdDataGet({
1938
+ workflowId,
1939
+ limit
1940
+ });
1941
+ return response.data.data ?? [];
1942
+ } catch (error) {
1943
+ throw KadoaHttpException.wrap(error, {
1944
+ message: ERROR_MESSAGES.DATA_FETCH_FAILED,
1945
+ details: { workflowId, limit }
1946
+ });
1947
+ }
1948
+ }
1949
+ };
1900
1950
 
1901
- // src/extraction/entity-detector.ts
1902
- function validateEntityOptions(options) {
1903
- if (!options.link) {
1904
- throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
1905
- code: "VALIDATION_ERROR",
1906
- details: { options }
1907
- });
1951
+ // src/core/config/constants.ts
1952
+ var DEFAULT_API_BASE_URL = "https://api.kadoa.com";
1953
+
1954
+ // src/modules/extraction/services/entity-detector.service.ts
1955
+ var ENTITY_API_ENDPOINT = "/v4/entity";
1956
+ var EntityDetectorService = class {
1957
+ constructor(client) {
1958
+ this.client = client;
1908
1959
  }
1909
- }
1910
- async function buildRequestHeaders(config) {
1911
- const headers = {
1912
- "Content-Type": "application/json",
1913
- Accept: "application/json"
1914
- };
1915
- if (config?.apiKey) {
1916
- if (typeof config.apiKey === "function") {
1917
- const apiKeyValue = await config.apiKey("X-API-Key");
1918
- if (apiKeyValue) {
1919
- headers["X-API-Key"] = apiKeyValue;
1920
- }
1921
- } else if (typeof config.apiKey === "string") {
1922
- headers["X-API-Key"] = config.apiKey;
1960
+ /**
1961
+ * Fetches entity fields dynamically from the /v4/entity endpoint.
1962
+ * This is a workaround implementation using native fetch since the endpoint
1963
+ * is not yet included in the OpenAPI specification.
1964
+ *
1965
+ * @param options Request options including the link to analyze
1966
+ * @returns EntityPrediction containing the detected entity type and fields
1967
+ */
1968
+ async fetchEntityFields(options) {
1969
+ this.validateEntityOptions(options);
1970
+ const url = `${this.client.baseUrl || DEFAULT_API_BASE_URL}${ENTITY_API_ENDPOINT}`;
1971
+ const headers = await this.buildRequestHeaders();
1972
+ const requestBody = options;
1973
+ try {
1974
+ const response = await this.client.axiosInstance.post(url, requestBody, {
1975
+ headers
1976
+ });
1977
+ const data = response.data;
1978
+ if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
1979
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
1980
+ code: "NOT_FOUND",
1981
+ details: {
1982
+ success: data.success,
1983
+ hasPredictions: !!data.entityPrediction,
1984
+ predictionCount: data.entityPrediction?.length || 0,
1985
+ link: options.link
1986
+ }
1987
+ });
1988
+ }
1989
+ return data.entityPrediction[0];
1990
+ } catch (error) {
1991
+ throw KadoaHttpException.wrap(error, {
1992
+ details: {
1993
+ url,
1994
+ link: options.link
1995
+ }
1996
+ });
1923
1997
  }
1924
- } else {
1925
- throw new KadoaSdkException(ERROR_MESSAGES.NO_API_KEY, {
1926
- code: "AUTH_ERROR",
1927
- details: { hasConfig: !!config, hasApiKey: !!config?.apiKey }
1928
- });
1929
1998
  }
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
1999
+ /**
2000
+ * Validates entity request options
2001
+ */
2002
+ validateEntityOptions(options) {
2003
+ if (!options.link) {
2004
+ throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
2005
+ code: "VALIDATION_ERROR",
2006
+ details: { options }
2007
+ });
1957
2008
  }
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
2009
  }
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)
2010
+ /**
2011
+ * Builds request headers including API key authentication
2012
+ */
2013
+ async buildRequestHeaders() {
2014
+ const headers = {
2015
+ "Content-Type": "application/json",
2016
+ Accept: "application/json"
2017
+ };
2018
+ const config = this.client.configuration;
2019
+ if (config?.apiKey) {
2020
+ if (typeof config.apiKey === "function") {
2021
+ const apiKeyValue = await config.apiKey("X-API-Key");
2022
+ if (apiKeyValue) {
2023
+ headers["X-API-Key"] = apiKeyValue;
2024
+ }
2025
+ } else if (typeof config.apiKey === "string") {
2026
+ headers["X-API-Key"] = config.apiKey;
2027
+ }
2028
+ } else {
2029
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_API_KEY, {
2030
+ code: "AUTH_ERROR",
2031
+ details: { hasConfig: !!config, hasApiKey: !!config?.apiKey }
2032
+ });
1982
2033
  }
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;
1990
- let response;
1991
- try {
1992
- response = await fetch(url.toString(), {
1993
- method: "POST",
1994
- headers,
1995
- body: JSON.stringify(requestBody)
1996
- });
1997
- } catch (error) {
1998
- throw new KadoaSdkException(ERROR_MESSAGES.NETWORK_ERROR, {
1999
- code: "NETWORK_ERROR",
2000
- details: {
2001
- url: url.toString(),
2002
- link: options.link
2003
- },
2004
- cause: error
2005
- });
2034
+ return headers;
2006
2035
  }
2007
- if (!response.ok) {
2008
- await handleErrorResponse(response, url, options.link);
2009
- }
2010
- let data;
2011
- try {
2012
- data = await response.json();
2013
- } catch (error) {
2014
- throw new KadoaSdkException(ERROR_MESSAGES.PARSE_ERROR, {
2015
- code: "INTERNAL_ERROR",
2016
- details: {
2017
- url: url.toString(),
2018
- link: options.link
2019
- },
2020
- cause: error
2021
- });
2036
+ };
2037
+
2038
+ // src/modules/extraction/services/workflow-manager.service.ts
2039
+ var TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
2040
+ "FINISHED",
2041
+ "SUCCESS",
2042
+ "FAILED",
2043
+ "ERROR",
2044
+ "STOPPED",
2045
+ "CANCELLED"
2046
+ ]);
2047
+ var WorkflowManagerService = class {
2048
+ constructor(client) {
2049
+ this.client = client;
2022
2050
  }
2023
- if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
2024
- throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
2025
- code: "NOT_FOUND",
2026
- details: {
2027
- success: data.success,
2028
- hasPredictions: !!data.entityPrediction,
2029
- predictionCount: data.entityPrediction?.length || 0,
2030
- link: options.link
2031
- }
2032
- });
2051
+ /**
2052
+ * Check if a workflow runState is terminal (finished processing)
2053
+ */
2054
+ isTerminalRunState(runState) {
2055
+ if (!runState) return false;
2056
+ return TERMINAL_RUN_STATES.has(runState.toUpperCase());
2033
2057
  }
2034
- return data.entityPrediction[0];
2035
- }
2036
-
2037
- // src/extraction/workflow-manager.ts
2038
- function isTerminalRunState(runState) {
2039
- if (!runState) return false;
2040
- return TERMINAL_RUN_STATES.has(runState.toUpperCase());
2041
- }
2042
- async function createWorkflow(sdkInstance, config) {
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
- try {
2055
- const response = await workflowsApi.v4WorkflowsPost({
2056
- v4WorkflowsPostRequest: request
2057
- });
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 }
2058
+ /**
2059
+ * Creates a new workflow with the provided configuration
2060
+ */
2061
+ async createWorkflow(config) {
2062
+ const workflowsApi = getWorkflowsApi(this.client);
2063
+ const request = {
2064
+ urls: config.urls,
2065
+ navigationMode: config.navigationMode,
2066
+ entity: config.entity,
2067
+ name: config.name,
2068
+ fields: config.fields,
2069
+ bypassPreview: true,
2070
+ limit: config.maxRecords,
2071
+ tags: ["sdk"]
2072
+ };
2073
+ try {
2074
+ const response = await workflowsApi.v4WorkflowsPost({
2075
+ v4WorkflowsPostRequest: request
2076
+ });
2077
+ const workflowId = response.data.workflowId;
2078
+ if (!workflowId) {
2079
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
2080
+ code: "INTERNAL_ERROR",
2081
+ details: { response: response.data }
2082
+ });
2083
+ }
2084
+ return workflowId;
2085
+ } catch (error) {
2086
+ throw KadoaHttpException.wrap(error, {
2087
+ message: ERROR_MESSAGES.WORKFLOW_CREATE_FAILED,
2088
+ details: config
2063
2089
  });
2064
2090
  }
2065
- return workflowId;
2066
- } catch (error) {
2067
- throw wrapKadoaError(error, {
2068
- message: "Failed to create workflow",
2069
- details: config
2070
- });
2071
2091
  }
2072
- }
2073
- async function getWorkflowStatus(sdkInstance, workflowId) {
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
- });
2092
+ /**
2093
+ * Gets the current status of a workflow
2094
+ */
2095
+ async getWorkflowStatus(workflowId) {
2096
+ const workflowsApi = getWorkflowsApi(this.client);
2097
+ try {
2098
+ const response = await workflowsApi.v4WorkflowsWorkflowIdGet({
2099
+ workflowId
2100
+ });
2101
+ return response.data;
2102
+ } catch (error) {
2103
+ throw KadoaHttpException.wrap(error, {
2104
+ message: ERROR_MESSAGES.PROGRESS_CHECK_FAILED,
2105
+ details: { workflowId }
2106
+ });
2107
+ }
2085
2108
  }
2086
- }
2087
- async function waitForWorkflowCompletion(sdkInstance, workflowId, options) {
2088
- const pollingInterval = options.pollingInterval;
2089
- const maxWaitTime = options.maxWaitTime;
2090
- const startTime = Date.now();
2091
- let previousState;
2092
- let previousRunState;
2093
- while (Date.now() - startTime < maxWaitTime) {
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);
2109
+ /**
2110
+ * Waits for a workflow to complete processing
2111
+ *
2112
+ * @param workflowId The workflow ID to monitor
2113
+ * @param pollingInterval How often to check the status (in milliseconds)
2114
+ * @param maxWaitTime Maximum time to wait before timing out (in milliseconds)
2115
+ * @param onStatusChange Optional callback for status changes
2116
+ * @returns The final workflow status
2117
+ */
2118
+ async waitForWorkflowCompletion(workflowId, pollingInterval, maxWaitTime, onStatusChange) {
2119
+ const startTime = Date.now();
2120
+ let lastStatus;
2121
+ while (Date.now() - startTime < maxWaitTime) {
2122
+ const currentStatus = await this.getWorkflowStatus(workflowId);
2123
+ if (lastStatus?.state !== currentStatus.state || lastStatus?.runState !== currentStatus.runState) {
2124
+ const eventPayload = {
2125
+ workflowId,
2126
+ previousState: lastStatus?.state,
2127
+ previousRunState: lastStatus?.runState,
2128
+ currentState: currentStatus.state,
2129
+ currentRunState: currentStatus.runState
2130
+ };
2131
+ this.client.emit(
2132
+ "extraction:status_changed",
2133
+ eventPayload,
2134
+ "extraction"
2135
+ );
2136
+ if (onStatusChange) {
2137
+ onStatusChange(lastStatus, currentStatus);
2138
+ }
2106
2139
  }
2107
- previousState = workflow.state;
2108
- previousRunState = workflow.runState;
2109
- }
2110
- if (isTerminalRunState(workflow.runState)) {
2111
- return workflow;
2140
+ if (this.isTerminalRunState(currentStatus.runState)) {
2141
+ return currentStatus;
2142
+ }
2143
+ lastStatus = currentStatus;
2144
+ await new Promise((resolve) => setTimeout(resolve, pollingInterval));
2112
2145
  }
2113
- await new Promise((resolve) => setTimeout(resolve, pollingInterval));
2146
+ throw new KadoaSdkException(ERROR_MESSAGES.WORKFLOW_TIMEOUT, {
2147
+ code: "TIMEOUT",
2148
+ details: {
2149
+ workflowId,
2150
+ maxWaitTime,
2151
+ lastState: lastStatus?.state,
2152
+ lastRunState: lastStatus?.runState
2153
+ }
2154
+ });
2114
2155
  }
2115
- throw new KadoaSdkException(
2116
- `Extraction did not complete within ${maxWaitTime / 1e3} seconds`,
2117
- { code: "TIMEOUT", details: { workflowId, maxWaitTime } }
2118
- );
2119
- }
2156
+ };
2120
2157
 
2121
- // src/extraction/extraction-runner.ts
2122
- function validateExtractionOptions(options) {
2123
- if (!options.urls || options.urls.length === 0) {
2124
- throw new KadoaSdkException(ERROR_MESSAGES.NO_URLS, {
2125
- code: "VALIDATION_ERROR"
2126
- });
2158
+ // src/modules/extraction/commands/run-extraction.command.ts
2159
+ var SUCCESSFUL_RUN_STATES = /* @__PURE__ */ new Set(["FINISHED", "SUCCESS"]);
2160
+ var DEFAULT_OPTIONS = {
2161
+ pollingInterval: 5e3,
2162
+ maxWaitTime: 3e5,
2163
+ navigationMode: "single-page",
2164
+ location: { type: "auto" },
2165
+ name: "Untitled Workflow",
2166
+ maxRecords: 1e3
2167
+ };
2168
+ var RunExtractionCommand = class extends Command {
2169
+ constructor(client) {
2170
+ super();
2171
+ this.client = client;
2172
+ this.dataFetcher = new DataFetcherService(client);
2173
+ this.entityDetector = new EntityDetectorService(client);
2174
+ this.workflowManager = new WorkflowManagerService(client);
2127
2175
  }
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
- );
2138
- try {
2139
- const entityPrediction = await fetchEntityFields(sdkInstance, {
2140
- link: config.urls[0],
2141
- location: config.location,
2142
- navigationMode: config.navigationMode
2143
- });
2144
- sdkInstance.emit(
2145
- "entity:detected",
2146
- {
2147
- entity: entityPrediction.entity,
2148
- fields: entityPrediction.fields,
2149
- url: config.urls[0]
2150
- },
2151
- "extraction",
2152
- {
2153
- navigationMode: config.navigationMode,
2154
- location: config.location
2155
- }
2156
- );
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
2163
- });
2164
- sdkInstance.emit(
2165
- "extraction:started",
2166
- {
2167
- workflowId,
2168
- name: config.name,
2169
- urls: config.urls
2170
- },
2171
- "extraction"
2176
+ /**
2177
+ * Execute the extraction workflow
2178
+ */
2179
+ async execute(options) {
2180
+ this.validateOptions(options);
2181
+ const config = object.merge(
2182
+ DEFAULT_OPTIONS,
2183
+ options
2172
2184
  );
2173
- const workflow = await waitForWorkflowCompletion(sdkInstance, workflowId, {
2174
- pollingInterval: config.pollingInterval,
2175
- maxWaitTime: config.maxWaitTime
2176
- });
2177
- let data;
2178
- const isSuccess = isExtractionSuccessful(workflow.runState);
2179
- if (isSuccess) {
2180
- data = await fetchWorkflowData(sdkInstance, workflowId);
2181
- if (data) {
2182
- sdkInstance.emit(
2183
- "extraction:data_available",
2184
- {
2185
- workflowId,
2186
- recordCount: data.length,
2187
- isPartial: false
2188
- },
2189
- "extraction"
2190
- );
2191
- }
2192
- sdkInstance.emit(
2193
- "extraction:completed",
2185
+ try {
2186
+ const entityPrediction = await this.entityDetector.fetchEntityFields({
2187
+ link: config.urls[0],
2188
+ location: config.location,
2189
+ navigationMode: config.navigationMode
2190
+ });
2191
+ this.client.emit(
2192
+ "entity:detected",
2194
2193
  {
2195
- workflowId,
2196
- success: true,
2197
- finalRunState: workflow.runState,
2198
- finalState: workflow.state,
2199
- recordCount: data?.length
2194
+ entity: entityPrediction.entity,
2195
+ fields: entityPrediction.fields,
2196
+ url: config.urls[0]
2200
2197
  },
2201
- "extraction"
2198
+ "extraction",
2199
+ {
2200
+ navigationMode: config.navigationMode,
2201
+ location: config.location
2202
+ }
2202
2203
  );
2203
- } else {
2204
- sdkInstance.emit(
2205
- "extraction:completed",
2204
+ const workflowId = await this.workflowManager.createWorkflow({
2205
+ entity: entityPrediction.entity,
2206
+ fields: entityPrediction.fields,
2207
+ ...config
2208
+ });
2209
+ this.client.emit(
2210
+ "extraction:started",
2206
2211
  {
2207
2212
  workflowId,
2208
- success: false,
2209
- finalRunState: workflow.runState,
2210
- finalState: workflow.state,
2211
- error: `Extraction completed with unexpected status: ${workflow.runState}`
2213
+ name: config.name,
2214
+ urls: config.urls
2212
2215
  },
2213
2216
  "extraction"
2214
2217
  );
2215
- throw new KadoaSdkException(
2216
- `Extraction completed with unexpected status: ${workflow.runState}`,
2217
- {
2218
- code: "INTERNAL_ERROR",
2219
- details: {
2218
+ const workflow = await this.workflowManager.waitForWorkflowCompletion(
2219
+ workflowId,
2220
+ config.pollingInterval,
2221
+ config.maxWaitTime
2222
+ );
2223
+ let data;
2224
+ const isSuccess = this.isExtractionSuccessful(workflow.runState);
2225
+ if (isSuccess) {
2226
+ data = await this.dataFetcher.fetchWorkflowData(
2227
+ workflowId,
2228
+ config.maxRecords
2229
+ );
2230
+ if (data) {
2231
+ this.client.emit(
2232
+ "extraction:data_available",
2233
+ {
2234
+ workflowId,
2235
+ recordCount: data.length,
2236
+ isPartial: false
2237
+ },
2238
+ "extraction"
2239
+ );
2240
+ }
2241
+ this.client.emit(
2242
+ "extraction:completed",
2243
+ {
2244
+ workflowId,
2245
+ success: true,
2246
+ finalRunState: workflow.runState,
2247
+ finalState: workflow.state,
2248
+ recordCount: data?.length
2249
+ },
2250
+ "extraction"
2251
+ );
2252
+ } else {
2253
+ this.client.emit(
2254
+ "extraction:completed",
2255
+ {
2220
2256
  workflowId,
2221
- runState: workflow.runState,
2222
- state: workflow.state
2257
+ success: false,
2258
+ finalRunState: workflow.runState,
2259
+ finalState: workflow.state,
2260
+ error: `Extraction completed with unexpected status: ${workflow.runState}`
2261
+ },
2262
+ "extraction"
2263
+ );
2264
+ throw new KadoaSdkException(
2265
+ `${ERROR_MESSAGES.WORKFLOW_UNEXPECTED_STATUS}: ${workflow.runState}`,
2266
+ {
2267
+ code: "INTERNAL_ERROR",
2268
+ details: {
2269
+ workflowId,
2270
+ runState: workflow.runState,
2271
+ state: workflow.state
2272
+ }
2223
2273
  }
2224
- }
2225
- );
2274
+ );
2275
+ }
2276
+ return {
2277
+ workflowId,
2278
+ workflow,
2279
+ data
2280
+ };
2281
+ } catch (error) {
2282
+ throw KadoaHttpException.wrap(error, {
2283
+ message: ERROR_MESSAGES.EXTRACTION_FAILED,
2284
+ details: { urls: options.urls }
2285
+ });
2226
2286
  }
2227
- return {
2228
- workflowId,
2229
- workflow,
2230
- data
2287
+ }
2288
+ /**
2289
+ * Validates extraction options
2290
+ * @private
2291
+ */
2292
+ validateOptions(options) {
2293
+ if (!options.urls || options.urls.length === 0) {
2294
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_URLS, {
2295
+ code: "VALIDATION_ERROR"
2296
+ });
2297
+ }
2298
+ }
2299
+ /**
2300
+ * Checks if extraction was successful
2301
+ * @private
2302
+ */
2303
+ isExtractionSuccessful(runState) {
2304
+ return runState ? SUCCESSFUL_RUN_STATES.has(runState.toUpperCase()) : false;
2305
+ }
2306
+ };
2307
+
2308
+ // src/modules/extraction/extraction.module.ts
2309
+ var ExtractionModule = class {
2310
+ constructor(client) {
2311
+ this.runExtractionCommand = new RunExtractionCommand(client);
2312
+ }
2313
+ /**
2314
+ * Run extraction workflow using dynamic entity detection
2315
+ *
2316
+ * @param options Extraction configuration options
2317
+ * @returns ExtractionResult containing workflow ID, workflow details, and extracted data
2318
+ *
2319
+ * @example
2320
+ * ```typescript
2321
+ * const result = await client.extraction.run({
2322
+ * urls: ['https://example.com'],
2323
+ * name: 'My Extraction'
2324
+ * });
2325
+ * ```
2326
+ */
2327
+ async run(options) {
2328
+ return this.runExtractionCommand.execute(options);
2329
+ }
2330
+ };
2331
+
2332
+ // src/version.ts
2333
+ var SDK_VERSION = "0.4.0";
2334
+ var SDK_NAME = "kadoa-node-sdk";
2335
+ var SDK_LANGUAGE = "node";
2336
+
2337
+ // src/kadoa-client.ts
2338
+ var KadoaClient = class {
2339
+ constructor(config) {
2340
+ this._baseUrl = config.baseUrl || "https://api.kadoa.com";
2341
+ this._timeout = config.timeout || 3e4;
2342
+ const configParams = {
2343
+ apiKey: config.apiKey,
2344
+ basePath: this._baseUrl,
2345
+ baseOptions: {
2346
+ headers: {
2347
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`,
2348
+ "X-SDK-Version": SDK_VERSION,
2349
+ "X-SDK-Language": SDK_LANGUAGE
2350
+ }
2351
+ }
2231
2352
  };
2232
- } catch (error) {
2233
- throw wrapKadoaError(error, {
2234
- message: ERROR_MESSAGES.EXTRACTION_FAILED,
2235
- details: { urls: options.urls }
2353
+ this._configuration = new Configuration(configParams);
2354
+ this._axiosInstance = globalAxios2__default.default.create({
2355
+ timeout: this._timeout,
2356
+ headers: {
2357
+ "User-Agent": `${SDK_NAME}/${SDK_VERSION}`,
2358
+ "X-SDK-Version": SDK_VERSION,
2359
+ "X-SDK-Language": SDK_LANGUAGE
2360
+ }
2236
2361
  });
2362
+ this._events = new KadoaEventEmitter();
2363
+ this.extraction = new ExtractionModule(this);
2237
2364
  }
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();
2365
+ /**
2366
+ * Register an event listener
2367
+ *
2368
+ * @param listener Function to handle events
2369
+ */
2370
+ onEvent(listener) {
2371
+ this._events.onEvent(listener);
2269
2372
  }
2270
- }
2373
+ /**
2374
+ * Remove an event listener
2375
+ *
2376
+ * @param listener Function to remove from event handlers
2377
+ */
2378
+ offEvent(listener) {
2379
+ this._events.offEvent(listener);
2380
+ }
2381
+ /**
2382
+ * Emit an event
2383
+ * @internal
2384
+ *
2385
+ * @param eventName The name of the event
2386
+ * @param payload The event payload
2387
+ * @param source Optional source identifier
2388
+ * @param metadata Optional metadata
2389
+ */
2390
+ emit(eventName, payload, source, metadata) {
2391
+ this._events.emit(eventName, payload, source, metadata);
2392
+ }
2393
+ /**
2394
+ * Get the underlying configuration
2395
+ *
2396
+ * @returns The configuration object
2397
+ */
2398
+ get configuration() {
2399
+ return this._configuration;
2400
+ }
2401
+ /**
2402
+ * Get the axios instance
2403
+ *
2404
+ * @returns The axios instance
2405
+ */
2406
+ get axiosInstance() {
2407
+ return this._axiosInstance;
2408
+ }
2409
+ /**
2410
+ * Get the base URL
2411
+ *
2412
+ * @returns The base URL
2413
+ */
2414
+ get baseUrl() {
2415
+ return this._baseUrl;
2416
+ }
2417
+ /**
2418
+ * Get the timeout value
2419
+ *
2420
+ * @returns The timeout in milliseconds
2421
+ */
2422
+ get timeout() {
2423
+ return this._timeout;
2424
+ }
2425
+ /**
2426
+ * Get the event emitter
2427
+ * @internal
2428
+ *
2429
+ * @returns The event emitter
2430
+ */
2431
+ get events() {
2432
+ return this._events;
2433
+ }
2434
+ /**
2435
+ * Dispose of the client and clean up resources
2436
+ */
2437
+ dispose() {
2438
+ this._events?.removeAllListeners();
2439
+ }
2440
+ };
2271
2441
 
2442
+ exports.ERROR_MESSAGES = ERROR_MESSAGES;
2443
+ exports.KadoaClient = KadoaClient;
2272
2444
  exports.KadoaEventEmitter = KadoaEventEmitter;
2273
2445
  exports.KadoaHttpException = KadoaHttpException;
2274
2446
  exports.KadoaSdkException = KadoaSdkException;
2275
- exports.dispose = dispose;
2276
- exports.initializeSdk = initializeSdk;
2277
- exports.isKadoaHttpException = isKadoaHttpException;
2278
- exports.isKadoaSdkException = isKadoaSdkException;
2279
- exports.runExtraction = runExtraction;
2280
2447
  //# sourceMappingURL=index.js.map
2281
2448
  //# sourceMappingURL=index.js.map