@kadoa/node-sdk 0.3.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,53 +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
- maxRecords: 99999
160
- };
161
- var TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
162
- "FINISHED",
163
- "SUCCESS",
164
- "FAILED",
165
- "ERROR",
166
- "STOPPED",
167
- "CANCELLED"
168
- ]);
169
- var SUCCESSFUL_RUN_STATES = /* @__PURE__ */ new Set(["FINISHED", "SUCCESS"]);
170
- var ENTITY_API_ENDPOINT = "/v4/entity";
171
- var DEFAULT_API_BASE_URL = "https://api.kadoa.com";
172
- var ERROR_MESSAGES = {
173
- NO_URLS: "At least one URL is required for extraction",
174
- NO_API_KEY: "API key is required for entity detection",
175
- LINK_REQUIRED: "Link is required for entity field detection",
176
- NO_WORKFLOW_ID: "Failed to start extraction process - no ID received",
177
- NO_PREDICTIONS: "No entity predictions returned from the API",
178
- PARSE_ERROR: "Failed to parse entity response",
179
- NETWORK_ERROR: "Network error while fetching entity fields",
180
- AUTH_FAILED: "Authentication failed. Please check your API key",
181
- RATE_LIMITED: "Rate limit exceeded. Please try again later",
182
- SERVER_ERROR: "Server error while fetching entity fields",
183
- DATA_FETCH_FAILED: "Failed to retrieve extracted data from workflow",
184
- PROGRESS_CHECK_FAILED: "Failed to check extraction progress",
185
- EXTRACTION_FAILED: "Data extraction failed for the provided URLs"
186
- };
187
218
  var BASE_PATH = "https://api.kadoa.com".replace(/\/+$/, "");
188
219
  var BaseAPI = class {
189
220
  constructor(configuration, basePath = BASE_PATH, axios2 = globalAxios2__default.default) {
@@ -1869,411 +1900,549 @@ var Configuration = class {
1869
1900
  }
1870
1901
  };
1871
1902
 
1872
- // src/api-client.ts
1903
+ // src/core/patterns/command.ts
1904
+ var Command = class {
1905
+ };
1906
+
1907
+ // src/core/http/client-factory.ts
1873
1908
  var workflowsApiCache = /* @__PURE__ */ new WeakMap();
1874
- function getWorkflowsApi(sdk) {
1875
- let api = workflowsApiCache.get(sdk);
1909
+ function getWorkflowsApi(client) {
1910
+ let api = workflowsApiCache.get(client);
1876
1911
  if (!api) {
1877
- api = new WorkflowsApi(sdk.configuration, sdk.baseUrl, sdk.axiosInstance);
1878
- workflowsApiCache.set(sdk, api);
1912
+ api = new WorkflowsApi(
1913
+ client.configuration,
1914
+ client.baseUrl,
1915
+ client.axiosInstance
1916
+ );
1917
+ workflowsApiCache.set(client, api);
1879
1918
  }
1880
1919
  return api;
1881
1920
  }
1882
1921
 
1883
- // src/extraction/data-fetcher.ts
1884
- async function fetchWorkflowData(sdkInstance, workflowId, limit = DEFAULT_OPTIONS.dataLimit) {
1885
- const workflowsApi = getWorkflowsApi(sdkInstance);
1886
- try {
1887
- const response = await workflowsApi.v4WorkflowsWorkflowIdDataGet({
1888
- workflowId,
1889
- limit
1890
- });
1891
- return response.data.data ?? [];
1892
- } catch (error) {
1893
- throw wrapKadoaError(error, {
1894
- message: ERROR_MESSAGES.DATA_FETCH_FAILED,
1895
- details: { workflowId, limit }
1896
- });
1922
+ // src/modules/extraction/services/data-fetcher.service.ts
1923
+ var DataFetcherService = class {
1924
+ constructor(client) {
1925
+ this.client = client;
1897
1926
  }
1898
- }
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
+ };
1899
1950
 
1900
- // src/extraction/entity-detector.ts
1901
- function validateEntityOptions(options) {
1902
- if (!options.link) {
1903
- throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
1904
- code: "VALIDATION_ERROR",
1905
- details: { options }
1906
- });
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;
1907
1959
  }
1908
- }
1909
- async function buildRequestHeaders(config) {
1910
- const headers = {
1911
- "Content-Type": "application/json",
1912
- Accept: "application/json"
1913
- };
1914
- if (config?.apiKey) {
1915
- if (typeof config.apiKey === "function") {
1916
- const apiKeyValue = await config.apiKey("X-API-Key");
1917
- if (apiKeyValue) {
1918
- headers["X-API-Key"] = apiKeyValue;
1919
- }
1920
- } else if (typeof config.apiKey === "string") {
1921
- 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
+ });
1922
1997
  }
1923
- } else {
1924
- throw new KadoaSdkException(ERROR_MESSAGES.NO_API_KEY, {
1925
- code: "AUTH_ERROR",
1926
- details: { hasConfig: !!config, hasApiKey: !!config?.apiKey }
1927
- });
1928
1998
  }
1929
- return headers;
1930
- }
1931
- function getErrorCodeFromStatus(status) {
1932
- if (status === 401 || status === 403) return "AUTH_ERROR";
1933
- if (status === 404) return "NOT_FOUND";
1934
- if (status === 429) return "RATE_LIMITED";
1935
- if (status >= 400 && status < 500) return "VALIDATION_ERROR";
1936
- if (status >= 500) return "HTTP_ERROR";
1937
- return "UNKNOWN";
1938
- }
1939
- async function handleErrorResponse(response, url, link) {
1940
- let errorData;
1941
- let errorText = "";
1942
- try {
1943
- errorText = await response.text();
1944
- errorData = JSON.parse(errorText);
1945
- } catch {
1946
- errorData = { message: errorText || response.statusText };
1947
- }
1948
- const baseErrorOptions = {
1949
- httpStatus: response.status,
1950
- endpoint: url.toString(),
1951
- method: "POST",
1952
- responseBody: errorData,
1953
- details: {
1954
- url: url.toString(),
1955
- 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
+ });
1956
2008
  }
1957
- };
1958
- if (response.status === 401) {
1959
- throw new KadoaHttpException(ERROR_MESSAGES.AUTH_FAILED, {
1960
- ...baseErrorOptions,
1961
- code: "AUTH_ERROR"
1962
- });
1963
2009
  }
1964
- if (response.status === 429) {
1965
- throw new KadoaHttpException(ERROR_MESSAGES.RATE_LIMITED, {
1966
- ...baseErrorOptions,
1967
- code: "RATE_LIMITED"
1968
- });
1969
- }
1970
- if (response.status >= 500) {
1971
- throw new KadoaHttpException(ERROR_MESSAGES.SERVER_ERROR, {
1972
- ...baseErrorOptions,
1973
- code: "HTTP_ERROR"
1974
- });
1975
- }
1976
- throw new KadoaHttpException(
1977
- `Failed to fetch entity fields: ${errorData?.message || response.statusText}`,
1978
- {
1979
- ...baseErrorOptions,
1980
- 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
+ });
1981
2033
  }
1982
- );
1983
- }
1984
- async function fetchEntityFields(sdk, options) {
1985
- validateEntityOptions(options);
1986
- const url = new URL(ENTITY_API_ENDPOINT, sdk.baseUrl || DEFAULT_API_BASE_URL);
1987
- const headers = await buildRequestHeaders(sdk.configuration);
1988
- const requestBody = options;
1989
- let response;
1990
- try {
1991
- response = await fetch(url.toString(), {
1992
- method: "POST",
1993
- headers,
1994
- body: JSON.stringify(requestBody)
1995
- });
1996
- } catch (error) {
1997
- throw new KadoaSdkException(ERROR_MESSAGES.NETWORK_ERROR, {
1998
- code: "NETWORK_ERROR",
1999
- details: {
2000
- url: url.toString(),
2001
- link: options.link
2002
- },
2003
- cause: error
2004
- });
2005
- }
2006
- if (!response.ok) {
2007
- await handleErrorResponse(response, url, options.link);
2034
+ return headers;
2008
2035
  }
2009
- let data;
2010
- try {
2011
- data = await response.json();
2012
- } catch (error) {
2013
- throw new KadoaSdkException(ERROR_MESSAGES.PARSE_ERROR, {
2014
- code: "INTERNAL_ERROR",
2015
- details: {
2016
- url: url.toString(),
2017
- link: options.link
2018
- },
2019
- cause: error
2020
- });
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;
2021
2050
  }
2022
- if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
2023
- throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
2024
- code: "NOT_FOUND",
2025
- details: {
2026
- success: data.success,
2027
- hasPredictions: !!data.entityPrediction,
2028
- predictionCount: data.entityPrediction?.length || 0,
2029
- link: options.link
2030
- }
2031
- });
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());
2032
2057
  }
2033
- return data.entityPrediction[0];
2034
- }
2035
-
2036
- // src/extraction/workflow-manager.ts
2037
- function isTerminalRunState(runState) {
2038
- if (!runState) return false;
2039
- return TERMINAL_RUN_STATES.has(runState.toUpperCase());
2040
- }
2041
- async function createWorkflow(sdkInstance, config) {
2042
- const workflowsApi = getWorkflowsApi(sdkInstance);
2043
- const request = {
2044
- urls: config.urls,
2045
- navigationMode: config.navigationMode,
2046
- entity: config.entity,
2047
- name: config.name,
2048
- fields: config.fields,
2049
- bypassPreview: true,
2050
- limit: config.maxRecords,
2051
- tags: ["sdk"]
2052
- };
2053
- try {
2054
- const response = await workflowsApi.v4WorkflowsPost({
2055
- v4WorkflowsPostRequest: request
2056
- });
2057
- const workflowId = response.data.workflowId;
2058
- if (!workflowId) {
2059
- throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
2060
- code: "INTERNAL_ERROR",
2061
- 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
2062
2089
  });
2063
2090
  }
2064
- return workflowId;
2065
- } catch (error) {
2066
- throw wrapKadoaError(error, {
2067
- message: "Failed to create workflow",
2068
- details: config
2069
- });
2070
2091
  }
2071
- }
2072
- async function getWorkflowStatus(sdkInstance, workflowId) {
2073
- const workflowsApi = getWorkflowsApi(sdkInstance);
2074
- try {
2075
- const response = await workflowsApi.v4WorkflowsWorkflowIdGet({
2076
- workflowId
2077
- });
2078
- return response.data;
2079
- } catch (error) {
2080
- throw wrapKadoaError(error, {
2081
- message: ERROR_MESSAGES.PROGRESS_CHECK_FAILED,
2082
- details: { workflowId }
2083
- });
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
+ }
2084
2108
  }
2085
- }
2086
- async function waitForWorkflowCompletion(sdkInstance, workflowId, options) {
2087
- const pollingInterval = options.pollingInterval;
2088
- const maxWaitTime = options.maxWaitTime;
2089
- const startTime = Date.now();
2090
- let previousState;
2091
- let previousRunState;
2092
- while (Date.now() - startTime < maxWaitTime) {
2093
- const workflow = await getWorkflowStatus(sdkInstance, workflowId);
2094
- if (workflow.state !== previousState || workflow.runState !== previousRunState) {
2095
- const statusChange = {
2096
- workflowId,
2097
- previousState,
2098
- previousRunState,
2099
- currentState: workflow.state,
2100
- currentRunState: workflow.runState
2101
- };
2102
- sdkInstance.emit("extraction:status_changed", statusChange, "extraction");
2103
- if (options?.onStatusChange) {
2104
- 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
+ }
2105
2139
  }
2106
- previousState = workflow.state;
2107
- previousRunState = workflow.runState;
2108
- }
2109
- if (isTerminalRunState(workflow.runState)) {
2110
- return workflow;
2140
+ if (this.isTerminalRunState(currentStatus.runState)) {
2141
+ return currentStatus;
2142
+ }
2143
+ lastStatus = currentStatus;
2144
+ await new Promise((resolve) => setTimeout(resolve, pollingInterval));
2111
2145
  }
2112
- 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
+ });
2113
2155
  }
2114
- throw new KadoaSdkException(
2115
- `Extraction did not complete within ${maxWaitTime / 1e3} seconds`,
2116
- { code: "TIMEOUT", details: { workflowId, maxWaitTime } }
2117
- );
2118
- }
2156
+ };
2119
2157
 
2120
- // src/extraction/extraction-runner.ts
2121
- function validateExtractionOptions(options) {
2122
- if (!options.urls || options.urls.length === 0) {
2123
- throw new KadoaSdkException(ERROR_MESSAGES.NO_URLS, {
2124
- code: "VALIDATION_ERROR"
2125
- });
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);
2126
2175
  }
2127
- }
2128
- function isExtractionSuccessful(runState) {
2129
- return runState ? SUCCESSFUL_RUN_STATES.has(runState.toUpperCase()) : false;
2130
- }
2131
- async function runExtraction(sdkInstance, options) {
2132
- validateExtractionOptions(options);
2133
- const config = object.merge(
2134
- DEFAULT_OPTIONS,
2135
- options
2136
- );
2137
- try {
2138
- const entityPrediction = await fetchEntityFields(sdkInstance, {
2139
- link: config.urls[0],
2140
- location: config.location,
2141
- navigationMode: config.navigationMode
2142
- });
2143
- sdkInstance.emit(
2144
- "entity:detected",
2145
- {
2146
- entity: entityPrediction.entity,
2147
- fields: entityPrediction.fields,
2148
- url: config.urls[0]
2149
- },
2150
- "extraction",
2151
- {
2152
- navigationMode: config.navigationMode,
2153
- location: config.location
2154
- }
2155
- );
2156
- const workflowId = await createWorkflow(sdkInstance, {
2157
- entity: entityPrediction.entity,
2158
- fields: entityPrediction.fields,
2159
- ...config
2160
- });
2161
- sdkInstance.emit(
2162
- "extraction:started",
2163
- {
2164
- workflowId,
2165
- name: config.name,
2166
- urls: config.urls
2167
- },
2168
- "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
2169
2184
  );
2170
- const workflow = await waitForWorkflowCompletion(sdkInstance, workflowId, {
2171
- ...config,
2172
- pollingInterval: config.pollingInterval,
2173
- maxWaitTime: config.maxWaitTime
2174
- });
2175
- let data;
2176
- const isSuccess = isExtractionSuccessful(workflow.runState);
2177
- if (isSuccess) {
2178
- data = await fetchWorkflowData(sdkInstance, workflowId);
2179
- if (data) {
2180
- sdkInstance.emit(
2181
- "extraction:data_available",
2182
- {
2183
- workflowId,
2184
- recordCount: data.length,
2185
- isPartial: false
2186
- },
2187
- "extraction"
2188
- );
2189
- }
2190
- sdkInstance.emit(
2191
- "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",
2192
2193
  {
2193
- workflowId,
2194
- success: true,
2195
- finalRunState: workflow.runState,
2196
- finalState: workflow.state,
2197
- recordCount: data?.length
2194
+ entity: entityPrediction.entity,
2195
+ fields: entityPrediction.fields,
2196
+ url: config.urls[0]
2198
2197
  },
2199
- "extraction"
2198
+ "extraction",
2199
+ {
2200
+ navigationMode: config.navigationMode,
2201
+ location: config.location
2202
+ }
2200
2203
  );
2201
- } else {
2202
- sdkInstance.emit(
2203
- "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",
2204
2211
  {
2205
2212
  workflowId,
2206
- success: false,
2207
- finalRunState: workflow.runState,
2208
- finalState: workflow.state,
2209
- error: `Extraction completed with unexpected status: ${workflow.runState}`
2213
+ name: config.name,
2214
+ urls: config.urls
2210
2215
  },
2211
2216
  "extraction"
2212
2217
  );
2213
- throw new KadoaSdkException(
2214
- `Extraction completed with unexpected status: ${workflow.runState}`,
2215
- {
2216
- code: "INTERNAL_ERROR",
2217
- 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
+ {
2218
2256
  workflowId,
2219
- runState: workflow.runState,
2220
- 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
+ }
2221
2273
  }
2222
- }
2223
- );
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
+ });
2224
2286
  }
2225
- return {
2226
- workflowId,
2227
- workflow,
2228
- 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
+ }
2229
2352
  };
2230
- } catch (error) {
2231
- throw wrapKadoaError(error, {
2232
- message: ERROR_MESSAGES.EXTRACTION_FAILED,
2233
- 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
+ }
2234
2361
  });
2362
+ this._events = new KadoaEventEmitter();
2363
+ this.extraction = new ExtractionModule(this);
2235
2364
  }
2236
- }
2237
- function initializeSdk(config) {
2238
- const baseUrl = config.baseUrl || "https://api.kadoa.com";
2239
- const configParams = {
2240
- apiKey: config.apiKey,
2241
- basePath: baseUrl
2242
- };
2243
- const configuration = new Configuration(configParams);
2244
- const axiosInstance = globalAxios2__default.default.create({
2245
- timeout: config.timeout || 3e4
2246
- });
2247
- const events = new KadoaEventEmitter();
2248
- return {
2249
- configuration,
2250
- axiosInstance,
2251
- baseUrl,
2252
- events,
2253
- emit: (eventName, payload, source, metadata) => {
2254
- events.emit(eventName, payload, source, metadata);
2255
- },
2256
- onEvent: (listener) => {
2257
- events.onEvent(listener);
2258
- },
2259
- offEvent: (listener) => {
2260
- events.offEvent(listener);
2261
- }
2262
- };
2263
- }
2264
- function dispose(sdkInstance) {
2265
- if (sdkInstance?.events) {
2266
- 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);
2267
2372
  }
2268
- }
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
+ };
2269
2441
 
2442
+ exports.ERROR_MESSAGES = ERROR_MESSAGES;
2443
+ exports.KadoaClient = KadoaClient;
2270
2444
  exports.KadoaEventEmitter = KadoaEventEmitter;
2271
2445
  exports.KadoaHttpException = KadoaHttpException;
2272
2446
  exports.KadoaSdkException = KadoaSdkException;
2273
- exports.dispose = dispose;
2274
- exports.initializeSdk = initializeSdk;
2275
- exports.isKadoaHttpException = isKadoaHttpException;
2276
- exports.isKadoaSdkException = isKadoaSdkException;
2277
- exports.runExtraction = runExtraction;
2278
2447
  //# sourceMappingURL=index.js.map
2279
2448
  //# sourceMappingURL=index.js.map