@kadoa/node-sdk 0.12.1 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  import globalAxios5, { isAxiosError, AxiosError } from 'axios';
2
- import { v4 } from 'uuid';
3
- import { URL, URLSearchParams } from 'url';
4
2
  import createDebug from 'debug';
5
- import { merge } from 'es-toolkit';
6
- import { z } from 'zod';
3
+ import { upperFirst, camelCase, merge } from 'es-toolkit';
4
+ import { URL, URLSearchParams } from 'url';
7
5
  import assert from 'assert';
6
+ import { z } from 'zod';
7
+ import { v4 } from 'uuid';
8
8
 
9
9
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
10
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -13,12 +13,127 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
13
13
  throw Error('Dynamic require of "' + x + '" is not supported');
14
14
  });
15
15
 
16
- // src/internal/runtime/exceptions/base.exception.ts
16
+ // src/domains/extraction/extraction.acl.ts
17
+ var FetchDataOptions = class {
18
+ };
19
+ var SchemaFieldDataType = {
20
+ Text: "TEXT",
21
+ Number: "NUMBER",
22
+ Date: "DATE",
23
+ Url: "URL",
24
+ Email: "EMAIL",
25
+ Image: "IMAGE",
26
+ Video: "VIDEO",
27
+ Phone: "PHONE",
28
+ Boolean: "BOOLEAN",
29
+ Location: "LOCATION",
30
+ Array: "ARRAY",
31
+ Object: "OBJECT"
32
+ };
33
+
34
+ // src/runtime/pagination/paginator.ts
35
+ var PagedIterator = class {
36
+ constructor(fetchPage) {
37
+ this.fetchPage = fetchPage;
38
+ }
39
+ /**
40
+ * Fetch all items across all pages
41
+ * @param options Base options (page will be overridden)
42
+ * @returns Array of all items
43
+ */
44
+ async fetchAll(options = {}) {
45
+ const allItems = [];
46
+ let currentPage = 1;
47
+ let hasMore = true;
48
+ while (hasMore) {
49
+ const result = await this.fetchPage({ ...options, page: currentPage });
50
+ allItems.push(...result.data);
51
+ const pagination = result.pagination;
52
+ hasMore = pagination.page !== void 0 && pagination.totalPages !== void 0 && pagination.page < pagination.totalPages;
53
+ currentPage++;
54
+ }
55
+ return allItems;
56
+ }
57
+ /**
58
+ * Create an async iterator for pages
59
+ * @param options Base options (page will be overridden)
60
+ * @returns Async generator that yields pages
61
+ */
62
+ async *pages(options = {}) {
63
+ let currentPage = 1;
64
+ let hasMore = true;
65
+ while (hasMore) {
66
+ const result = await this.fetchPage({ ...options, page: currentPage });
67
+ yield result;
68
+ const pagination = result.pagination;
69
+ hasMore = pagination.page !== void 0 && pagination.totalPages !== void 0 && pagination.page < pagination.totalPages;
70
+ currentPage++;
71
+ }
72
+ }
73
+ /**
74
+ * Create an async iterator for individual items
75
+ * @param options Base options (page will be overridden)
76
+ * @returns Async generator that yields items
77
+ */
78
+ async *items(options = {}) {
79
+ for await (const page of this.pages(options)) {
80
+ for (const item of page.data) {
81
+ yield item;
82
+ }
83
+ }
84
+ }
85
+ };
86
+
87
+ // src/domains/extraction/services/data-fetcher.service.ts
88
+ var DataFetcherService = class {
89
+ constructor(workflowsApi) {
90
+ this.workflowsApi = workflowsApi;
91
+ this.defaultLimit = 100;
92
+ }
93
+ /**
94
+ * Fetch a page of workflow data
95
+ */
96
+ async fetchData(options) {
97
+ const response = await this.workflowsApi.v4WorkflowsWorkflowIdDataGet({
98
+ ...options,
99
+ page: options.page ?? 1,
100
+ limit: options.limit ?? this.defaultLimit
101
+ });
102
+ const result = response.data;
103
+ return result;
104
+ }
105
+ /**
106
+ * Fetch all pages of workflow data
107
+ */
108
+ async fetchAllData(options) {
109
+ const iterator = new PagedIterator(
110
+ (pageOptions) => this.fetchData({ ...options, ...pageOptions })
111
+ );
112
+ return iterator.fetchAll({ limit: options.limit ?? this.defaultLimit });
113
+ }
114
+ /**
115
+ * Create an async iterator for paginated data fetching
116
+ */
117
+ async *fetchDataPages(options) {
118
+ const iterator = new PagedIterator(
119
+ (pageOptions) => this.fetchData({ ...options, ...pageOptions })
120
+ );
121
+ for await (const page of iterator.pages({
122
+ limit: options.limit ?? this.defaultLimit
123
+ })) {
124
+ yield page;
125
+ }
126
+ }
127
+ };
128
+
129
+ // src/runtime/exceptions/base.exception.ts
17
130
  var KadoaErrorCode = {
18
131
  AUTH_ERROR: "AUTH_ERROR",
19
132
  VALIDATION_ERROR: "VALIDATION_ERROR",
20
133
  BAD_REQUEST: "BAD_REQUEST",
21
- NOT_FOUND: "NOT_FOUND"};
134
+ NOT_FOUND: "NOT_FOUND",
135
+ INTERNAL_ERROR: "INTERNAL_ERROR"
136
+ };
22
137
  var _KadoaSdkException = class _KadoaSdkException extends Error {
23
138
  constructor(message, options) {
24
139
  super(message);
@@ -100,7 +215,10 @@ _KadoaSdkException.ERROR_MESSAGES = {
100
215
  // Schema specific errors
101
216
  SCHEMA_NOT_FOUND: "Schema not found",
102
217
  SCHEMA_FETCH_ERROR: "Failed to fetch schema",
103
- SCHEMAS_FETCH_ERROR: "Failed to fetch schemas"
218
+ SCHEMAS_FETCH_ERROR: "Failed to fetch schemas",
219
+ SCHEMA_CREATE_FAILED: "Failed to create schema",
220
+ SCHEMA_UPDATE_FAILED: "Failed to update schema",
221
+ SCHEMA_DELETE_FAILED: "Failed to delete schema"
104
222
  };
105
223
  var KadoaSdkException = _KadoaSdkException;
106
224
  var ERROR_MESSAGES = KadoaSdkException.ERROR_MESSAGES;
@@ -191,6 +309,128 @@ var KadoaHttpException = class _KadoaHttpException extends KadoaSdkException {
191
309
  return "UNKNOWN";
192
310
  }
193
311
  };
312
+ var createLogger = (namespace) => createDebug(`kadoa:${namespace}`);
313
+ var logger = {
314
+ client: createLogger("client"),
315
+ wss: createLogger("wss"),
316
+ extraction: createLogger("extraction"),
317
+ http: createLogger("http"),
318
+ workflow: createLogger("workflow"),
319
+ crawl: createLogger("crawl"),
320
+ notifications: createLogger("notifications"),
321
+ schemas: createLogger("schemas"),
322
+ validation: createLogger("validation")
323
+ };
324
+ var _SchemaBuilder = class _SchemaBuilder {
325
+ constructor() {
326
+ this.fields = [];
327
+ }
328
+ entity(entityName) {
329
+ this.entityName = entityName;
330
+ return this;
331
+ }
332
+ /**
333
+ * Add a structured field to the schema
334
+ * @param name - Field name (alphanumeric only)
335
+ * @param description - Field description
336
+ * @param dataType - Data type (STRING, NUMBER, BOOLEAN, etc.)
337
+ * @param options - Optional field configuration
338
+ */
339
+ field(name, description, dataType, options) {
340
+ this.validateFieldName(name);
341
+ const requiresExample = _SchemaBuilder.TYPES_REQUIRING_EXAMPLE.includes(dataType);
342
+ if (requiresExample && !options?.example) {
343
+ throw new KadoaSdkException(
344
+ `Field "${name}" with type ${dataType} requires an example`,
345
+ { code: "VALIDATION_ERROR", details: { name, dataType } }
346
+ );
347
+ }
348
+ this.fields.push({
349
+ name,
350
+ description,
351
+ dataType,
352
+ fieldType: "SCHEMA",
353
+ example: options?.example,
354
+ isKey: options?.isKey
355
+ });
356
+ return this;
357
+ }
358
+ /**
359
+ * Add a classification field to categorize content
360
+ * @param name - Field name (alphanumeric only)
361
+ * @param description - Field description
362
+ * @param categories - Array of category definitions
363
+ */
364
+ classify(name, description, categories) {
365
+ this.validateFieldName(name);
366
+ this.fields.push({
367
+ name,
368
+ description,
369
+ fieldType: "CLASSIFICATION",
370
+ categories
371
+ });
372
+ return this;
373
+ }
374
+ /**
375
+ * Add raw page content to extract
376
+ * @param name - Raw content format(s): "html", "markdown", or "url"
377
+ */
378
+ raw(name) {
379
+ const names = Array.isArray(name) ? name : [name];
380
+ for (const name2 of names) {
381
+ const fieldName = `raw${upperFirst(camelCase(name2))}`;
382
+ if (this.fields.some((field) => field.name === fieldName)) {
383
+ continue;
384
+ }
385
+ this.fields.push({
386
+ name: fieldName,
387
+ description: `Raw page content in ${name2.toUpperCase()} format`,
388
+ fieldType: "METADATA",
389
+ metadataKey: name2
390
+ });
391
+ }
392
+ return this;
393
+ }
394
+ build() {
395
+ if (!this.entityName) {
396
+ throw new KadoaSdkException("Entity name is required", {
397
+ code: "VALIDATION_ERROR",
398
+ details: { entityName: this.entityName }
399
+ });
400
+ }
401
+ return {
402
+ entityName: this.entityName,
403
+ fields: this.fields
404
+ };
405
+ }
406
+ validateFieldName(name) {
407
+ if (!_SchemaBuilder.FIELD_NAME_PATTERN.test(name)) {
408
+ throw new KadoaSdkException(
409
+ `Field name "${name}" must be alphanumeric only (no underscores or special characters)`,
410
+ {
411
+ code: "VALIDATION_ERROR",
412
+ details: { name, pattern: "^[A-Za-z0-9]+$" }
413
+ }
414
+ );
415
+ }
416
+ const lowerName = name.toLowerCase();
417
+ if (this.fields.some((f) => f.name.toLowerCase() === lowerName)) {
418
+ throw new KadoaSdkException(`Duplicate field name: "${name}"`, {
419
+ code: "VALIDATION_ERROR",
420
+ details: { name }
421
+ });
422
+ }
423
+ }
424
+ };
425
+ _SchemaBuilder.FIELD_NAME_PATTERN = /^[A-Za-z0-9]+$/;
426
+ _SchemaBuilder.TYPES_REQUIRING_EXAMPLE = [
427
+ "STRING",
428
+ "IMAGE",
429
+ "LINK",
430
+ "OBJECT",
431
+ "ARRAY"
432
+ ];
433
+ var SchemaBuilder = _SchemaBuilder;
194
434
  var BASE_PATH = "https://api.kadoa.com".replace(/\/+$/, "");
195
435
  var BaseAPI = class {
196
436
  constructor(configuration, basePath = BASE_PATH, axios2 = globalAxios5) {
@@ -2603,7 +2843,7 @@ var WorkflowsApiAxiosParamCreator = function(configuration) {
2603
2843
  /**
2604
2844
  * Retrieves a list of workflows with pagination and search capabilities
2605
2845
  * @summary Get a list of workflows
2606
- * @param {string} [search] Search term to filter workflows by name or URL
2846
+ * @param {string} [search] Search term to filter workflows by name, URL, or workflow ID
2607
2847
  * @param {number} [skip] Number of items to skip
2608
2848
  * @param {number} [limit] Maximum number of items to return
2609
2849
  * @param {V4WorkflowsGetStateEnum} [state] Filter workflows by state
@@ -3437,7 +3677,7 @@ var WorkflowsApiFp = function(configuration) {
3437
3677
  /**
3438
3678
  * Retrieves a list of workflows with pagination and search capabilities
3439
3679
  * @summary Get a list of workflows
3440
- * @param {string} [search] Search term to filter workflows by name or URL
3680
+ * @param {string} [search] Search term to filter workflows by name, URL, or workflow ID
3441
3681
  * @param {number} [skip] Number of items to skip
3442
3682
  * @param {number} [limit] Maximum number of items to return
3443
3683
  * @param {V4WorkflowsGetStateEnum} [state] Filter workflows by state
@@ -4045,624 +4285,277 @@ var Configuration = class {
4045
4285
  }
4046
4286
  };
4047
4287
 
4048
- // src/version.ts
4049
- var SDK_VERSION = "0.12.1";
4050
- var SDK_NAME = "kadoa-node-sdk";
4051
- var SDK_LANGUAGE = "node";
4052
-
4053
- // src/internal/runtime/config/constants.ts
4054
- var PUBLIC_API_URI = process.env.KADOA_PUBLIC_API_URI ?? "https://api.kadoa.com";
4055
- var WSS_API_URI = process.env.KADOA_WSS_API_URI ?? "wss://realtime.kadoa.com";
4056
- var REALTIME_API_URI = process.env.KADOA_REALTIME_API_URI ?? "https://realtime.kadoa.com";
4057
- var createLogger = (namespace) => createDebug(`kadoa:${namespace}`);
4058
- var logger = {
4059
- client: createLogger("client"),
4060
- wss: createLogger("wss"),
4061
- extraction: createLogger("extraction"),
4062
- http: createLogger("http"),
4063
- workflow: createLogger("workflow"),
4064
- crawl: createLogger("crawl"),
4065
- notifications: createLogger("notifications"),
4066
- schemas: createLogger("schemas"),
4067
- validation: createLogger("validation")
4068
- };
4069
-
4070
- // src/internal/domains/realtime/realtime.ts
4071
- var debug = logger.wss;
4072
- if (typeof WebSocket === "undefined") {
4073
- global.WebSocket = __require("ws");
4074
- }
4075
- var Realtime = class {
4076
- constructor(config) {
4077
- this.lastHeartbeat = Date.now();
4078
- this.isConnecting = false;
4079
- this.eventListeners = /* @__PURE__ */ new Set();
4080
- this.connectionListeners = /* @__PURE__ */ new Set();
4081
- this.errorListeners = /* @__PURE__ */ new Set();
4082
- if (!config.teamApiKey.startsWith("tk-")) {
4288
+ // src/domains/schemas/schemas.service.ts
4289
+ var debug = logger.schemas;
4290
+ var SchemasService = class {
4291
+ constructor(client) {
4292
+ this.schemasApi = new SchemasApi(client.configuration);
4293
+ }
4294
+ /**
4295
+ * Create a schema builder with fluent API and inline create support.
4296
+ */
4297
+ builder(entityName) {
4298
+ const service = this;
4299
+ return new class extends SchemaBuilder {
4300
+ constructor() {
4301
+ super();
4302
+ this.entity(entityName);
4303
+ }
4304
+ async create(name) {
4305
+ const built = this.build();
4306
+ return service.createSchema({
4307
+ name: name || built.entityName,
4308
+ entity: built.entityName,
4309
+ fields: built.fields
4310
+ });
4311
+ }
4312
+ }();
4313
+ }
4314
+ /**
4315
+ * Get a schema by ID
4316
+ */
4317
+ async getSchema(schemaId) {
4318
+ debug("Fetching schema with ID: %s", schemaId);
4319
+ const response = await this.schemasApi.v4SchemasSchemaIdGet({
4320
+ schemaId
4321
+ });
4322
+ const schemaData = response.data.data;
4323
+ if (!schemaData) {
4083
4324
  throw new KadoaSdkException(
4084
- "Realtime connection requires a team API key (starting with 'tk-'). Provided key does not appear to be a team API key.",
4325
+ `${ERROR_MESSAGES.SCHEMA_NOT_FOUND}: ${schemaId}`,
4085
4326
  {
4086
- code: "AUTH_ERROR",
4087
- details: { providedKeyPrefix: config.teamApiKey.substring(0, 3) }
4088
- }
4089
- );
4090
- }
4091
- this.teamApiKey = config.teamApiKey;
4092
- this.heartbeatInterval = config.heartbeatInterval || 1e4;
4093
- this.reconnectDelay = config.reconnectDelay || 5e3;
4094
- this.missedHeartbeatsLimit = config.missedHeartbeatsLimit || 3e4;
4095
- }
4096
- async connect() {
4097
- if (this.isConnecting) return;
4098
- this.isConnecting = true;
4099
- try {
4100
- const response = await fetch(`${PUBLIC_API_URI}/v4/oauth2/token`, {
4101
- method: "POST",
4102
- headers: {
4103
- "Content-Type": "application/json",
4104
- "x-api-key": `${this.teamApiKey}`,
4105
- "x-sdk-version": SDK_VERSION
4327
+ code: KadoaErrorCode.NOT_FOUND,
4328
+ details: { schemaId }
4106
4329
  }
4107
- });
4108
- const { access_token, team_id } = await response.json();
4109
- this.socket = new WebSocket(
4110
- `${WSS_API_URI}?access_token=${access_token}`
4111
4330
  );
4112
- this.socket.onopen = () => {
4113
- this.isConnecting = false;
4114
- this.lastHeartbeat = Date.now();
4115
- if (this.socket?.readyState === WebSocket.OPEN) {
4116
- this.socket.send(
4117
- JSON.stringify({
4118
- action: "subscribe",
4119
- channel: team_id
4120
- })
4121
- );
4122
- debug("Connected to WebSocket");
4123
- this.notifyConnectionListeners(true);
4124
- }
4125
- this.startHeartbeatCheck();
4126
- };
4127
- this.socket.onmessage = (event) => {
4128
- try {
4129
- const data = JSON.parse(event.data);
4130
- if (data.type === "heartbeat") {
4131
- this.handleHeartbeat();
4132
- } else {
4133
- if (data?.id) {
4134
- fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
4135
- method: "POST",
4136
- headers: { "Content-Type": "application/json" },
4137
- body: JSON.stringify({ id: data.id })
4138
- });
4139
- }
4140
- this.notifyEventListeners(data);
4141
- }
4142
- } catch (err) {
4143
- debug("Failed to parse incoming message: %O", err);
4144
- }
4145
- };
4146
- this.socket.onclose = () => {
4147
- debug("WebSocket disconnected. Attempting to reconnect...");
4148
- this.isConnecting = false;
4149
- this.stopHeartbeatCheck();
4150
- this.notifyConnectionListeners(false, "Connection closed");
4151
- setTimeout(() => this.connect(), this.reconnectDelay);
4152
- };
4153
- this.socket.onerror = (error) => {
4154
- debug("WebSocket error: %O", error);
4155
- this.isConnecting = false;
4156
- this.notifyErrorListeners(error);
4157
- };
4158
- } catch (err) {
4159
- debug("Failed to connect: %O", err);
4160
- this.isConnecting = false;
4161
- setTimeout(() => this.connect(), this.reconnectDelay);
4162
- }
4163
- }
4164
- handleHeartbeat() {
4165
- debug("Heartbeat received");
4166
- this.lastHeartbeat = Date.now();
4167
- }
4168
- notifyEventListeners(event) {
4169
- this.eventListeners.forEach((listener) => {
4170
- try {
4171
- listener(event);
4172
- } catch (error) {
4173
- debug("Error in event listener: %O", error);
4174
- }
4175
- });
4176
- }
4177
- notifyConnectionListeners(connected, reason) {
4178
- this.connectionListeners.forEach((listener) => {
4179
- try {
4180
- listener(connected, reason);
4181
- } catch (error) {
4182
- debug("Error in connection listener: %O", error);
4183
- }
4184
- });
4185
- }
4186
- notifyErrorListeners(error) {
4187
- this.errorListeners.forEach((listener) => {
4188
- try {
4189
- listener(error);
4190
- } catch (error2) {
4191
- debug("Error in error listener: %O", error2);
4192
- }
4193
- });
4194
- }
4195
- startHeartbeatCheck() {
4196
- this.missedHeartbeatCheckTimer = setInterval(() => {
4197
- if (Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
4198
- debug("No heartbeat received in 30 seconds! Closing connection.");
4199
- this.socket?.close();
4200
- }
4201
- }, this.heartbeatInterval);
4202
- }
4203
- stopHeartbeatCheck() {
4204
- if (this.missedHeartbeatCheckTimer) {
4205
- clearInterval(this.missedHeartbeatCheckTimer);
4206
4331
  }
4332
+ return schemaData;
4207
4333
  }
4208
4334
  /**
4209
- * Subscribe to realtime events
4210
- * @param listener Function to handle incoming events
4211
- * @returns Function to unsubscribe
4335
+ * List all schemas
4212
4336
  */
4213
- onEvent(listener) {
4214
- this.eventListeners.add(listener);
4215
- return () => {
4216
- this.eventListeners.delete(listener);
4217
- };
4337
+ async listSchemas() {
4338
+ const response = await this.schemasApi.v4SchemasGet();
4339
+ return response.data.data;
4218
4340
  }
4219
4341
  /**
4220
- * Subscribe to connection state changes
4221
- * @param listener Function to handle connection state changes
4222
- * @returns Function to unsubscribe
4342
+ * Create a new schema
4223
4343
  */
4224
- onConnection(listener) {
4225
- this.connectionListeners.add(listener);
4226
- return () => {
4227
- this.connectionListeners.delete(listener);
4228
- };
4344
+ async createSchema(body) {
4345
+ debug("Creating schema with name: %s", body.name);
4346
+ const response = await this.schemasApi.v4SchemasPost({
4347
+ createSchemaBody: body
4348
+ });
4349
+ const schemaId = response.data.schemaId;
4350
+ if (!schemaId) {
4351
+ throw new KadoaSdkException(ERROR_MESSAGES.SCHEMA_CREATE_FAILED, {
4352
+ code: KadoaErrorCode.INTERNAL_ERROR
4353
+ });
4354
+ }
4355
+ return this.getSchema(schemaId);
4229
4356
  }
4230
4357
  /**
4231
- * Subscribe to errors
4232
- * @param listener Function to handle errors
4233
- * @returns Function to unsubscribe
4358
+ * Update an existing schema
4234
4359
  */
4235
- onError(listener) {
4236
- this.errorListeners.add(listener);
4237
- return () => {
4238
- this.errorListeners.delete(listener);
4239
- };
4240
- }
4241
- close() {
4242
- if (this.socket) {
4243
- this.stopHeartbeatCheck();
4244
- this.socket.close();
4245
- this.socket = void 0;
4246
- }
4247
- this.eventListeners.clear();
4248
- this.connectionListeners.clear();
4249
- this.errorListeners.clear();
4360
+ async updateSchema(schemaId, body) {
4361
+ debug("Updating schema with ID: %s", schemaId);
4362
+ await this.schemasApi.v4SchemasSchemaIdPut({
4363
+ schemaId,
4364
+ updateSchemaBody: body
4365
+ });
4366
+ return this.getSchema(schemaId);
4250
4367
  }
4251
- isConnected() {
4252
- return this.socket?.readyState === WebSocket.OPEN;
4368
+ /**
4369
+ * Delete a schema
4370
+ */
4371
+ async deleteSchema(schemaId) {
4372
+ debug("Deleting schema with ID: %s", schemaId);
4373
+ await this.schemasApi.v4SchemasSchemaIdDelete({
4374
+ schemaId
4375
+ });
4253
4376
  }
4254
4377
  };
4255
4378
 
4256
- // src/modules/extraction.module.ts
4257
- var ExtractionModule = class {
4258
- constructor(extractionService, dataFetcherService, channelsService, settingsService, workflowsCoreService) {
4259
- this.extractionService = extractionService;
4260
- this.dataFetcherService = dataFetcherService;
4261
- this.channelsService = channelsService;
4262
- this.settingsService = settingsService;
4263
- this.workflowsCoreService = workflowsCoreService;
4379
+ // src/domains/extraction/services/entity-resolver.service.ts
4380
+ var ENTITY_API_ENDPOINT = "/v4/entity";
4381
+ var EntityResolverService = class {
4382
+ constructor(client) {
4383
+ this.client = client;
4384
+ this.schemasService = new SchemasService(client);
4264
4385
  }
4265
4386
  /**
4266
- * Run extraction workflow using dynamic entity detection
4267
- *
4268
- * @param options Extraction configuration options including optional notification settings
4269
- * @returns ExtractionResult containing workflow ID, workflow details, and first page of extracted data
4270
- *
4271
- * @example Simple extraction with AI detection
4272
- * ```typescript
4273
- * const result = await client.extraction.run({
4274
- * urls: ['https://example.com'],
4275
- * name: 'My Extraction'
4276
- * });
4277
- * ```
4278
- *
4279
- * @example With notifications
4280
- * ```typescript
4281
- * const result = await client.extraction.run({
4282
- * urls: ['https://example.com'],
4283
- * name: 'My Extraction',
4284
- * notifications: {
4285
- * events: ['workflow_completed', 'workflow_failed'],
4286
- * channels: {
4287
- * email: true,
4288
- * slack: { channelId: 'slack-channel-id' }
4289
- * }
4290
- * }
4291
- * });
4292
- * ```
4387
+ * Resolves entity and fields from the provided entity configuration
4293
4388
  *
4294
- * @see {@link KadoaClient.extract} For more flexible extraction configuration using the builder API
4389
+ * @param entityConfig The entity configuration to resolve
4390
+ * @param options Additional options for AI detection
4391
+ * @returns Resolved entity with fields
4295
4392
  */
4296
- async run(options) {
4297
- return await this.extractionService.executeExtraction({
4298
- ...options,
4299
- mode: "run"
4393
+ async resolveEntity(entityConfig, options) {
4394
+ if (entityConfig === "ai-detection") {
4395
+ if (!options?.link) {
4396
+ throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
4397
+ code: "VALIDATION_ERROR",
4398
+ details: { entityConfig, options }
4399
+ });
4400
+ }
4401
+ const entityPrediction = await this.fetchEntityFields({
4402
+ link: options.link,
4403
+ location: options.location,
4404
+ navigationMode: options.navigationMode
4405
+ });
4406
+ return {
4407
+ entity: entityPrediction.entity,
4408
+ fields: entityPrediction.fields
4409
+ };
4410
+ } else if (entityConfig) {
4411
+ if ("schemaId" in entityConfig) {
4412
+ const schema = await this.schemasService.getSchema(
4413
+ entityConfig.schemaId
4414
+ );
4415
+ return {
4416
+ entity: schema.entity ?? "",
4417
+ fields: schema.schema
4418
+ };
4419
+ } else if ("name" in entityConfig && "fields" in entityConfig) {
4420
+ return {
4421
+ entity: entityConfig.name,
4422
+ fields: entityConfig.fields
4423
+ };
4424
+ }
4425
+ }
4426
+ throw new KadoaSdkException(ERROR_MESSAGES.ENTITY_INVARIANT_VIOLATION, {
4427
+ details: {
4428
+ entity: entityConfig
4429
+ }
4300
4430
  });
4301
4431
  }
4302
4432
  /**
4303
- * Submit extraction workflow for background processing
4304
- *
4305
- * @param options Extraction configuration options including optional notification settings
4306
- * @returns SubmitExtractionResult containing workflow ID
4307
- *
4308
- * @example
4309
- * ```typescript
4310
- * const result = await client.extraction.submit({
4311
- * urls: ['https://example.com'],
4312
- * name: 'My Extraction',
4313
- * notifications: {
4314
- * events: 'all',
4315
- * channels: {
4316
- * email: true
4317
- * }
4318
- * }
4319
- * });
4320
- * ```
4433
+ * Fetches entity fields dynamically from the /v4/entity endpoint.
4434
+ * This is a workaround implementation using native fetch since the endpoint
4435
+ * is not yet included in the OpenAPI specification.
4321
4436
  *
4322
- * @see {@link KadoaClient.extract} For more flexible extraction configuration using the builder API
4437
+ * @param options Request options including the link to analyze
4438
+ * @returns EntityPrediction containing the detected entity type and fields
4323
4439
  */
4324
- async submit(options) {
4325
- return await this.extractionService.executeExtraction({
4326
- ...options,
4327
- mode: "submit"
4440
+ async fetchEntityFields(options) {
4441
+ this.validateEntityOptions(options);
4442
+ const url = `${this.client.baseUrl}${ENTITY_API_ENDPOINT}`;
4443
+ const requestBody = options;
4444
+ const response = await this.client.axiosInstance.post(url, requestBody, {
4445
+ headers: {
4446
+ "Content-Type": "application/json",
4447
+ Accept: "application/json",
4448
+ "x-api-key": this.client.apiKey
4449
+ }
4328
4450
  });
4451
+ const data = response.data;
4452
+ if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
4453
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
4454
+ code: "NOT_FOUND",
4455
+ details: {
4456
+ success: data.success,
4457
+ hasPredictions: !!data.entityPrediction,
4458
+ predictionCount: data.entityPrediction?.length || 0,
4459
+ link: options.link
4460
+ }
4461
+ });
4462
+ }
4463
+ return data.entityPrediction[0];
4329
4464
  }
4330
4465
  /**
4331
- * Run a workflow with variables and optional limit
4466
+ * Validates entity request options
4332
4467
  */
4333
- async runJob(workflowId, input) {
4334
- return this.workflowsCoreService.runWorkflow(workflowId, input);
4468
+ validateEntityOptions(options) {
4469
+ if (!options.link) {
4470
+ throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
4471
+ code: "VALIDATION_ERROR",
4472
+ details: { options }
4473
+ });
4474
+ }
4475
+ }
4476
+ };
4477
+ var debug2 = logger.extraction;
4478
+ var SUCCESSFUL_RUN_STATES = /* @__PURE__ */ new Set(["FINISHED", "SUCCESS"]);
4479
+ var DEFAULT_OPTIONS = {
4480
+ mode: "run",
4481
+ pollingInterval: 5e3,
4482
+ maxWaitTime: 3e5,
4483
+ navigationMode: "single-page",
4484
+ location: { type: "auto" },
4485
+ name: "Untitled Workflow",
4486
+ bypassPreview: true,
4487
+ autoStart: true
4488
+ };
4489
+ var ExtractionService = class {
4490
+ constructor(workflowsCoreService, dataFetcherService, entityResolverService, notificationSetupService, notificationChannelsService, notificationSettingsService) {
4491
+ this.workflowsCoreService = workflowsCoreService;
4492
+ this.dataFetcherService = dataFetcherService;
4493
+ this.entityResolverService = entityResolverService;
4494
+ this.notificationSetupService = notificationSetupService;
4495
+ this.notificationChannelsService = notificationChannelsService;
4496
+ this.notificationSettingsService = notificationSettingsService;
4497
+ }
4498
+ /**
4499
+ * Run an extraction workflow and wait for completion.
4500
+ */
4501
+ async run(options) {
4502
+ return await this.executeExtraction({ ...options, mode: "run" });
4503
+ }
4504
+ /**
4505
+ * Submit an extraction workflow for asynchronous processing.
4506
+ */
4507
+ async submit(options) {
4508
+ return await this.executeExtraction({ ...options, mode: "submit" });
4509
+ }
4510
+ /**
4511
+ * Trigger a workflow run without waiting for completion.
4512
+ */
4513
+ async runJob(workflowId, input) {
4514
+ return await this.workflowsCoreService.runWorkflow(workflowId, input);
4335
4515
  }
4336
4516
  /**
4337
- * Run a workflow and wait for it to complete
4517
+ * Trigger a workflow run and wait for the job to complete.
4338
4518
  */
4339
4519
  async runJobAndWait(workflowId, input) {
4340
4520
  const result = await this.workflowsCoreService.runWorkflow(
4341
4521
  workflowId,
4342
4522
  input
4343
4523
  );
4344
- return this.workflowsCoreService.waitForJobCompletion(
4524
+ return await this.workflowsCoreService.waitForJobCompletion(
4345
4525
  workflowId,
4346
- result.jobId
4526
+ result.jobId || ""
4347
4527
  );
4348
4528
  }
4349
4529
  /**
4350
- * Fetch paginated data from a workflow
4351
- *
4352
- * @param options Options for fetching data including workflowId and pagination parameters
4353
- * @returns Paginated workflow data with metadata
4354
- *
4355
- * @example
4356
- * ```typescript
4357
- * // Fetch first page
4358
- * const firstPage = await client.extraction.fetchData({
4359
- * workflowId: 'workflow-id',
4360
- * page: 1,
4361
- * limit: 100
4362
- * });
4363
- *
4364
- * // Iterate through all pages
4365
- * for await (const page of client.extraction.fetchDataPages({ workflowId: 'workflow-id' })) {
4366
- * console.log(`Processing ${page.data.length} records`);
4367
- * }
4368
- * ```
4530
+ * Fetch a single page of extraction data.
4369
4531
  */
4370
4532
  async fetchData(options) {
4371
- return this.dataFetcherService.fetchData(options);
4533
+ return await this.dataFetcherService.fetchData(options);
4372
4534
  }
4373
4535
  /**
4374
- * Fetch all data from a workflow across all pages
4375
- *
4376
- * @param options Options for fetching data
4377
- * @returns All workflow data combined from all pages
4378
- *
4379
- * @example
4380
- * ```typescript
4381
- * const allData = await client.extraction.fetchAllData({
4382
- * workflowId: 'workflow-id'
4383
- * });
4384
- * ```
4536
+ * Fetch all extraction data across all pages.
4385
4537
  */
4386
4538
  async fetchAllData(options) {
4387
- return this.dataFetcherService.fetchAllData(options);
4539
+ return await this.dataFetcherService.fetchAllData(options);
4388
4540
  }
4389
4541
  /**
4390
- * Create an async iterator for paginated data fetching
4391
- *
4392
- * @param options Options for fetching data
4393
- * @returns Async iterator that yields pages of data
4394
- *
4395
- * @example
4396
- * ```typescript
4397
- * for await (const page of client.extraction.fetchDataPages({ workflowId: 'workflow-id' })) {
4398
- * console.log(`Page ${page.pagination.page}: ${page.data.length} records`);
4399
- * }
4400
- * ```
4542
+ * Iterate through extraction data pages.
4401
4543
  */
4402
4544
  fetchDataPages(options) {
4403
4545
  return this.dataFetcherService.fetchDataPages(options);
4404
4546
  }
4405
4547
  /**
4406
- * Get notification channels for a workflow
4407
- *
4408
- * @param workflowId The workflow ID
4409
- * @returns Array of notification channels
4410
- *
4411
- * @example
4412
- * ```typescript
4413
- * const channels = await client.extraction.getNotificationChannels('workflow-id');
4414
- * ```
4548
+ * List notification channels for a workflow.
4415
4549
  */
4416
4550
  async getNotificationChannels(workflowId) {
4417
- return this.channelsService.listChannels({ workflowId });
4551
+ return await this.notificationChannelsService.listChannels({ workflowId });
4418
4552
  }
4419
4553
  /**
4420
- * Get notification settings for a workflow
4421
- *
4422
- * @param workflowId The workflow ID
4423
- * @returns Array of notification settings
4424
- *
4425
- * @example
4426
- * ```typescript
4427
- * const settings = await client.extraction.getNotificationSettings('workflow-id');
4428
- * ```
4554
+ * List notification settings for a workflow.
4429
4555
  */
4430
4556
  async getNotificationSettings(workflowId) {
4431
- return this.settingsService.listSettings({ workflowId });
4432
- }
4433
- };
4434
-
4435
- // src/modules/notifications.module.ts
4436
- var NotificationsModule = class {
4437
- constructor(channelsService, settingsService, channelSetupService) {
4438
- this.channelsService = channelsService;
4439
- this.settingsService = settingsService;
4440
- this.channelSetupService = channelSetupService;
4441
- }
4442
- async setupForWorkflow(requestData) {
4443
- const existingSettings = await this.settingsService.listSettings({
4444
- workflowId: requestData.workflowId
4445
- });
4446
- if (existingSettings.length > 0) {
4447
- throw new KadoaSdkException("Settings already exist", {
4448
- code: KadoaErrorCode.BAD_REQUEST,
4449
- details: {
4450
- workflowId: requestData.workflowId
4451
- }
4452
- });
4453
- }
4454
- return this.channelSetupService.setup({
4455
- workflowId: requestData.workflowId,
4456
- events: requestData.events,
4457
- channels: requestData.channels
4458
- });
4459
- }
4460
- async setupForWorkspace(requestData) {
4461
- const existingSettings = await this.settingsService.listSettings({});
4462
- if (existingSettings.length > 0) {
4463
- throw new KadoaSdkException("Workspace settings already exist", {
4464
- code: KadoaErrorCode.BAD_REQUEST
4465
- });
4466
- }
4467
- return this.channelSetupService.setup({
4468
- events: requestData.events,
4469
- channels: requestData.channels
4470
- });
4471
- }
4472
- /**
4473
- * Get the channels service
4474
- */
4475
- get channels() {
4476
- return this.channelsService;
4477
- }
4478
- /**
4479
- * Get the settings service
4480
- */
4481
- get settings() {
4482
- return this.settingsService;
4483
- }
4484
- };
4485
-
4486
- // src/modules/schemas.module.ts
4487
- var SchemasModule = class {
4488
- constructor(service) {
4489
- this.service = service;
4490
- }
4491
- };
4492
-
4493
- // src/modules/user.module.ts
4494
- var UserModule = class {
4495
- constructor(userService) {
4496
- this.userService = userService;
4497
- }
4498
- /**
4499
- * Get the underlying UserService instance
4500
- * @returns UserService instance
4501
- */
4502
- get service() {
4503
- return this.userService;
4504
- }
4505
- /**
4506
- * Get current user details
4507
- * @returns KadoaUser details
4508
- */
4509
- async getCurrentUser() {
4510
- return this.userService.getCurrentUser();
4511
- }
4512
- };
4513
-
4514
- // src/modules/validation.module.ts
4515
- var ValidationModule = class {
4516
- constructor(coreService, rulesService) {
4517
- this.coreService = coreService;
4518
- this.rulesService = rulesService;
4519
- }
4520
- listRules(options) {
4521
- return this.rulesService.listRules(options);
4522
- }
4523
- getRuleByName(name) {
4524
- return this.rulesService.getRuleByName(name);
4525
- }
4526
- createRule(data) {
4527
- return this.rulesService.createRule(data);
4528
- }
4529
- generateRule(data) {
4530
- return this.rulesService.generateRule(data);
4531
- }
4532
- generateRules(data) {
4533
- return this.rulesService.generateRules(data);
4534
- }
4535
- bulkApproveRules(data) {
4536
- return this.rulesService.bulkApproveRules(data);
4537
- }
4538
- bulkDeleteRules(data) {
4539
- return this.rulesService.bulkDeleteRules(data);
4540
- }
4541
- deleteAllRules(data) {
4542
- return this.rulesService.deleteAllRules(data);
4543
- }
4544
- listWorkflowValidations(workflowId, jobId) {
4545
- return this.coreService.listWorkflowValidations({ workflowId, jobId });
4546
- }
4547
- scheduleValidation(workflowId, jobId) {
4548
- return this.coreService.scheduleValidation(workflowId, jobId);
4549
- }
4550
- waitUntilCompleted(validationId, options) {
4551
- return this.coreService.waitUntilCompleted(validationId, options);
4552
- }
4553
- getValidationDetails(validationId) {
4554
- return this.coreService.getValidationDetails(validationId);
4555
- }
4556
- getLatestValidation(workflowId, jobId) {
4557
- return this.coreService.getLatestValidation(workflowId, jobId);
4558
- }
4559
- getValidationAnomalies(validationId) {
4560
- return this.coreService.getValidationAnomalies(validationId);
4561
- }
4562
- getValidationAnomaliesByRule(validationId, ruleName) {
4563
- return this.coreService.getValidationAnomaliesByRule(
4564
- validationId,
4565
- ruleName
4566
- );
4567
- }
4568
- toggleValidationEnabled(workflowId) {
4569
- return this.coreService.toggleValidationEnabled(workflowId);
4570
- }
4571
- };
4572
-
4573
- // src/modules/workflows.module.ts
4574
- var WorkflowsModule = class {
4575
- constructor(core) {
4576
- this.core = core;
4577
- }
4578
- async get(workflowId) {
4579
- return this.core.get(workflowId);
4580
- }
4581
- async list(filters) {
4582
- return this.core.list(filters);
4583
- }
4584
- async getByName(name) {
4585
- return this.core.getByName(name);
4586
- }
4587
- async create(input) {
4588
- return this.core.create(input);
4589
- }
4590
- async cancel(workflowId) {
4591
- return this.core.cancel(workflowId);
4592
- }
4593
- async approve(workflowId) {
4594
- return this.core.resume(workflowId);
4595
- }
4596
- async resume(workflowId) {
4597
- return this.core.resume(workflowId);
4598
- }
4599
- async wait(workflowId, options) {
4600
- return this.core.wait(workflowId, options);
4601
- }
4602
- /**
4603
- * Get job status directly without polling workflow details
4604
- */
4605
- async getJobStatus(workflowId, jobId) {
4606
- return this.core.getJobStatus(workflowId, jobId);
4607
- }
4608
- /**
4609
- * Wait for a job to complete using the job status endpoint
4610
- */
4611
- async waitForJobCompletion(workflowId, jobId, options) {
4612
- return this.core.waitForJobCompletion(workflowId, jobId, options);
4613
- }
4614
- };
4615
-
4616
- // src/internal/domains/user/user.service.ts
4617
- var UserService = class {
4618
- constructor(client) {
4619
- this.client = client;
4620
- }
4621
- /**
4622
- * Get current user details
4623
- * @returns User details
4624
- */
4625
- async getCurrentUser() {
4626
- const response = await this.client.axiosInstance.get("/v5/user", {
4627
- baseURL: this.client.baseUrl,
4628
- headers: {
4629
- "x-api-key": this.client.apiKey,
4630
- "Content-Type": "application/json"
4631
- }
4632
- });
4633
- const userData = response.data;
4634
- if (!userData || !userData.userId) {
4635
- throw new KadoaSdkException("Invalid user data received");
4636
- }
4637
- return {
4638
- userId: userData.userId,
4639
- email: userData.email,
4640
- featureFlags: userData.featureFlags || []
4641
- };
4642
- }
4643
- };
4644
- var debug2 = logger.extraction;
4645
- var SUCCESSFUL_RUN_STATES = /* @__PURE__ */ new Set(["FINISHED", "SUCCESS"]);
4646
- var DEFAULT_OPTIONS = {
4647
- mode: "run",
4648
- pollingInterval: 5e3,
4649
- maxWaitTime: 3e5,
4650
- navigationMode: "single-page",
4651
- location: { type: "auto" },
4652
- name: "Untitled Workflow",
4653
- bypassPreview: true,
4654
- autoStart: true
4655
- };
4656
- var ExtractionService = class {
4657
- constructor(workflowsCoreService, dataFetcherService, entityResolverService, notificationSetupService) {
4658
- this.workflowsCoreService = workflowsCoreService;
4659
- this.dataFetcherService = dataFetcherService;
4660
- this.entityResolverService = entityResolverService;
4661
- this.notificationSetupService = notificationSetupService;
4557
+ return await this.notificationSettingsService.listSettings({ workflowId });
4662
4558
  }
4663
- /**
4664
- * execute extraction workflow
4665
- */
4666
4559
  async executeExtraction(options) {
4667
4560
  this.validateOptions(options);
4668
4561
  const config = merge(
@@ -4785,54 +4678,228 @@ var ExtractionService = class {
4785
4678
  return runState ? SUCCESSFUL_RUN_STATES.has(runState.toUpperCase()) : false;
4786
4679
  }
4787
4680
  };
4788
- var NotificationChannelType = {
4789
- EMAIL: "EMAIL",
4790
- SLACK: "SLACK",
4791
- WEBHOOK: "WEBHOOK",
4792
- WEBSOCKET: "WEBSOCKET"
4793
- };
4794
- var emailChannelConfigSchema = z.object({
4795
- recipients: z.array(z.email()).min(1, "Recipients are required for email channel"),
4796
- from: z.email().refine(
4797
- (email) => email.endsWith("@kadoa.com"),
4798
- "From email address must end with @kadoa.com"
4799
- ).optional()
4800
- });
4801
- var _NotificationChannelsService = class _NotificationChannelsService {
4802
- constructor(notificationsApi, userService) {
4803
- this.api = notificationsApi;
4804
- this.userService = userService;
4681
+ var debug3 = logger.extraction;
4682
+ var ExtractionBuilderService = class {
4683
+ constructor(workflowsCoreService, entityResolverService, dataFetcherService, notificationSetupService) {
4684
+ this.workflowsCoreService = workflowsCoreService;
4685
+ this.entityResolverService = entityResolverService;
4686
+ this.dataFetcherService = dataFetcherService;
4687
+ this.notificationSetupService = notificationSetupService;
4805
4688
  }
4806
- async listChannels(filters) {
4807
- const response = await this.api.v5NotificationsChannelsGet(filters);
4808
- const data = response.data.data?.channels;
4809
- if (!data) {
4810
- throw KadoaHttpException.wrap(response, {
4811
- message: "Failed to list channels"
4812
- });
4813
- }
4814
- return data;
4689
+ get options() {
4690
+ assert(this._options, "Options are not set");
4691
+ return this._options;
4815
4692
  }
4816
- /**
4817
- * List all channels (both workflow-specific and workspace-level)
4818
- * This is useful for finding workspace-level channels like WebSocket channels
4819
- * that might not be associated with a specific workflow
4820
- */
4821
- async listAllChannels(workflowId) {
4822
- if (!workflowId) {
4823
- return this.listChannels({});
4824
- }
4825
- const [workflowChannels, workspaceChannels] = await Promise.all([
4826
- this.listChannels({ workflowId }),
4827
- this.listChannels({})
4828
- ]);
4829
- const allChannels = [...workflowChannels];
4830
- workspaceChannels.forEach((channel) => {
4831
- if (!allChannels.find((c) => c.id === channel.id)) {
4832
- allChannels.push(channel);
4833
- }
4834
- });
4835
- return allChannels;
4693
+ get notificationOptions() {
4694
+ return this._notificationOptions;
4695
+ }
4696
+ get monitoringOptions() {
4697
+ return this._monitoringOptions;
4698
+ }
4699
+ get workflowId() {
4700
+ assert(this._workflowId, "Workflow ID is not set");
4701
+ return this._workflowId;
4702
+ }
4703
+ get jobId() {
4704
+ assert(this._jobId, "Job ID is not set");
4705
+ return this._jobId;
4706
+ }
4707
+ extract({
4708
+ urls,
4709
+ name,
4710
+ description,
4711
+ navigationMode,
4712
+ extraction
4713
+ }) {
4714
+ let entity = "ai-detection";
4715
+ if (extraction) {
4716
+ const result = extraction(new SchemaBuilder());
4717
+ if ("schemaId" in result) {
4718
+ entity = { schemaId: result.schemaId };
4719
+ } else {
4720
+ const builtSchema = result.build();
4721
+ entity = { name: builtSchema.entityName, fields: builtSchema.fields };
4722
+ }
4723
+ }
4724
+ this._options = {
4725
+ urls,
4726
+ name,
4727
+ description,
4728
+ navigationMode: navigationMode || "single-page",
4729
+ entity,
4730
+ bypassPreview: false
4731
+ };
4732
+ return this;
4733
+ }
4734
+ withNotifications(options) {
4735
+ this._notificationOptions = options;
4736
+ return this;
4737
+ }
4738
+ withMonitoring(options) {
4739
+ this._monitoringOptions = options;
4740
+ return this;
4741
+ }
4742
+ bypassPreview() {
4743
+ assert(this._options, "Options are not set");
4744
+ this._options.bypassPreview = true;
4745
+ return this;
4746
+ }
4747
+ setInterval(options) {
4748
+ assert(this._options, "Options are not set");
4749
+ if ("interval" in options) {
4750
+ this._options.interval = options.interval;
4751
+ } else {
4752
+ this._options.interval = "CUSTOM";
4753
+ this._options.schedules = options.schedules;
4754
+ }
4755
+ return this;
4756
+ }
4757
+ setLocation(options) {
4758
+ assert(this._options, "Options are not set");
4759
+ this._options.location = options;
4760
+ return this;
4761
+ }
4762
+ async create() {
4763
+ assert(this._options, "Options are not set");
4764
+ const { urls, name, description, navigationMode, entity } = this.options;
4765
+ const resolvedEntity = typeof entity === "object" && "schemaId" in entity ? void 0 : await this.entityResolverService.resolveEntity(entity, {
4766
+ link: urls[0],
4767
+ location: this._options.location,
4768
+ navigationMode
4769
+ });
4770
+ if (!resolvedEntity) {
4771
+ throw new KadoaSdkException(ERROR_MESSAGES.ENTITY_FETCH_FAILED, {
4772
+ code: "VALIDATION_ERROR",
4773
+ details: { entity }
4774
+ });
4775
+ }
4776
+ const workflow = await this.workflowsCoreService.create({
4777
+ urls,
4778
+ name,
4779
+ description,
4780
+ navigationMode,
4781
+ monitoring: this._monitoringOptions,
4782
+ schemaId: typeof entity === "object" && "schemaId" in entity ? entity.schemaId : void 0,
4783
+ entity: resolvedEntity.entity,
4784
+ fields: resolvedEntity.fields,
4785
+ autoStart: false,
4786
+ interval: this._options.interval,
4787
+ schedules: this._options.schedules
4788
+ });
4789
+ if (this._notificationOptions) {
4790
+ await this.notificationSetupService.setup({
4791
+ ...this._notificationOptions,
4792
+ workflowId: workflow.id
4793
+ });
4794
+ }
4795
+ this._workflowId = workflow.id;
4796
+ return this;
4797
+ }
4798
+ async run(options) {
4799
+ assert(this._options, "Options are not set");
4800
+ assert(this._workflowId, "Workflow ID is not set");
4801
+ const startedJob = await this.workflowsCoreService.runWorkflow(
4802
+ this._workflowId,
4803
+ { variables: options?.variables, limit: options?.limit }
4804
+ );
4805
+ assert(startedJob.jobId, "Job ID is not set");
4806
+ debug3("Job started: %O", startedJob);
4807
+ this._jobId = startedJob.jobId;
4808
+ const finishedJob = await this.workflowsCoreService.waitForJobCompletion(
4809
+ this._workflowId,
4810
+ startedJob.jobId
4811
+ );
4812
+ debug3("Job finished: %O", finishedJob);
4813
+ return this;
4814
+ }
4815
+ async submit(options) {
4816
+ assert(this._options, "Options are not set");
4817
+ assert(this._workflowId, "Workflow ID is not set");
4818
+ const submittedJob = await this.workflowsCoreService.runWorkflow(
4819
+ this._workflowId,
4820
+ { variables: options?.variables, limit: options?.limit }
4821
+ );
4822
+ assert(submittedJob.jobId, "Job ID is not set");
4823
+ debug3("Job submitted: %O", submittedJob);
4824
+ this._jobId = submittedJob.jobId;
4825
+ return {
4826
+ workflowId: this._workflowId,
4827
+ jobId: this._jobId
4828
+ };
4829
+ }
4830
+ async fetchData(options) {
4831
+ assert(this._workflowId, "Workflow ID is not set");
4832
+ assert(this._jobId, "Job ID is not set");
4833
+ return this.dataFetcherService.fetchData({
4834
+ workflowId: this._workflowId,
4835
+ runId: this._jobId,
4836
+ page: options.page ?? 1,
4837
+ limit: options.limit ?? 100,
4838
+ ...options
4839
+ });
4840
+ }
4841
+ async fetchAllData(options) {
4842
+ assert(this._jobId, "Job ID is not set");
4843
+ assert(this._workflowId, "Workflow ID is not set");
4844
+ return this.dataFetcherService.fetchAllData({
4845
+ workflowId: this._workflowId,
4846
+ runId: this._jobId,
4847
+ ...options
4848
+ });
4849
+ }
4850
+ };
4851
+
4852
+ // src/domains/notifications/notifications.acl.ts
4853
+ var NotificationChannelType = {
4854
+ EMAIL: "EMAIL",
4855
+ SLACK: "SLACK",
4856
+ WEBHOOK: "WEBHOOK",
4857
+ WEBSOCKET: "WEBSOCKET"
4858
+ };
4859
+
4860
+ // src/domains/notifications/notification-channels.service.ts
4861
+ var emailChannelConfigSchema = z.object({
4862
+ recipients: z.array(z.email()).min(1, "Recipients are required for email channel"),
4863
+ from: z.email().refine(
4864
+ (email) => email.endsWith("@kadoa.com"),
4865
+ "From email address must end with @kadoa.com"
4866
+ ).optional()
4867
+ });
4868
+ var _NotificationChannelsService = class _NotificationChannelsService {
4869
+ constructor(notificationsApi, userService) {
4870
+ this.api = notificationsApi;
4871
+ this.userService = userService;
4872
+ }
4873
+ async listChannels(filters) {
4874
+ const response = await this.api.v5NotificationsChannelsGet(filters);
4875
+ const data = response.data.data?.channels;
4876
+ if (!data) {
4877
+ throw KadoaHttpException.wrap(response, {
4878
+ message: "Failed to list channels"
4879
+ });
4880
+ }
4881
+ return data;
4882
+ }
4883
+ /**
4884
+ * List all channels (both workflow-specific and workspace-level)
4885
+ * This is useful for finding workspace-level channels like WebSocket channels
4886
+ * that might not be associated with a specific workflow
4887
+ */
4888
+ async listAllChannels(workflowId) {
4889
+ if (!workflowId) {
4890
+ return this.listChannels({});
4891
+ }
4892
+ const [workflowChannels, workspaceChannels] = await Promise.all([
4893
+ this.listChannels({ workflowId }),
4894
+ this.listChannels({})
4895
+ ]);
4896
+ const allChannels = [...workflowChannels];
4897
+ workspaceChannels.forEach((channel) => {
4898
+ if (!allChannels.find((c) => c.id === channel.id)) {
4899
+ allChannels.push(channel);
4900
+ }
4901
+ });
4902
+ return allChannels;
4836
4903
  }
4837
4904
  async deleteChannel(channelId) {
4838
4905
  const response = await this.api.v5NotificationsChannelsChannelIdDelete({
@@ -4935,7 +5002,7 @@ var _NotificationChannelsService = class _NotificationChannelsService {
4935
5002
  _NotificationChannelsService.DEFAULT_CHANNEL_NAME = "default";
4936
5003
  var NotificationChannelsService = _NotificationChannelsService;
4937
5004
 
4938
- // src/internal/domains/notifications/notification-settings.service.ts
5005
+ // src/domains/notifications/notification-settings.service.ts
4939
5006
  var NotificationSettingsService = class {
4940
5007
  constructor(notificationsApi) {
4941
5008
  this.api = notificationsApi;
@@ -4977,360 +5044,48 @@ var NotificationSettingsService = class {
4977
5044
  }
4978
5045
  };
4979
5046
 
4980
- // src/internal/runtime/pagination/paginator.ts
4981
- var PagedIterator = class {
4982
- constructor(fetchPage) {
4983
- this.fetchPage = fetchPage;
5047
+ // src/domains/notifications/notification-setup.service.ts
5048
+ var debug4 = logger.notifications;
5049
+ var NotificationSetupService = class {
5050
+ constructor(channelsService, settingsService) {
5051
+ this.channelsService = channelsService;
5052
+ this.settingsService = settingsService;
4984
5053
  }
4985
5054
  /**
4986
- * Fetch all items across all pages
4987
- * @param options Base options (page will be overridden)
4988
- * @returns Array of all items
5055
+ * Setup notification settings for a specific workflow ensuring no duplicates exist.
4989
5056
  */
4990
- async fetchAll(options = {}) {
4991
- const allItems = [];
4992
- let currentPage = 1;
4993
- let hasMore = true;
4994
- while (hasMore) {
4995
- const result = await this.fetchPage({ ...options, page: currentPage });
4996
- allItems.push(...result.data);
4997
- const pagination = result.pagination;
4998
- hasMore = pagination.page !== void 0 && pagination.totalPages !== void 0 && pagination.page < pagination.totalPages;
4999
- currentPage++;
5057
+ async setupForWorkflow(requestData) {
5058
+ const existingSettings = await this.settingsService.listSettings({
5059
+ workflowId: requestData.workflowId
5060
+ });
5061
+ if (existingSettings.length > 0) {
5062
+ throw new KadoaSdkException("Settings already exist", {
5063
+ code: KadoaErrorCode.BAD_REQUEST,
5064
+ details: {
5065
+ workflowId: requestData.workflowId
5066
+ }
5067
+ });
5000
5068
  }
5001
- return allItems;
5069
+ return this.setup({
5070
+ workflowId: requestData.workflowId,
5071
+ events: requestData.events,
5072
+ channels: requestData.channels
5073
+ });
5002
5074
  }
5003
5075
  /**
5004
- * Create an async iterator for pages
5005
- * @param options Base options (page will be overridden)
5006
- * @returns Async generator that yields pages
5076
+ * Setup notification settings at the workspace level ensuring no duplicates exist.
5007
5077
  */
5008
- async *pages(options = {}) {
5009
- let currentPage = 1;
5010
- let hasMore = true;
5011
- while (hasMore) {
5012
- const result = await this.fetchPage({ ...options, page: currentPage });
5013
- yield result;
5014
- const pagination = result.pagination;
5015
- hasMore = pagination.page !== void 0 && pagination.totalPages !== void 0 && pagination.page < pagination.totalPages;
5016
- currentPage++;
5078
+ async setupForWorkspace(requestData) {
5079
+ const existingSettings = await this.settingsService.listSettings({});
5080
+ if (existingSettings.length > 0) {
5081
+ throw new KadoaSdkException("Workspace settings already exist", {
5082
+ code: KadoaErrorCode.BAD_REQUEST
5083
+ });
5017
5084
  }
5018
- }
5019
- /**
5020
- * Create an async iterator for individual items
5021
- * @param options Base options (page will be overridden)
5022
- * @returns Async generator that yields items
5023
- */
5024
- async *items(options = {}) {
5025
- for await (const page of this.pages(options)) {
5026
- for (const item of page.data) {
5027
- yield item;
5028
- }
5029
- }
5030
- }
5031
- };
5032
-
5033
- // src/internal/domains/extraction/services/data-fetcher.service.ts
5034
- var DataFetcherService = class {
5035
- constructor(workflowsApi) {
5036
- this.workflowsApi = workflowsApi;
5037
- this.defaultLimit = 100;
5038
- }
5039
- /**
5040
- * Fetch a page of workflow data
5041
- */
5042
- async fetchData(options) {
5043
- const response = await this.workflowsApi.v4WorkflowsWorkflowIdDataGet({
5044
- ...options,
5045
- page: options.page ?? 1,
5046
- limit: options.limit ?? this.defaultLimit
5047
- });
5048
- const result = response.data;
5049
- return result;
5050
- }
5051
- /**
5052
- * Fetch all pages of workflow data
5053
- */
5054
- async fetchAllData(options) {
5055
- const iterator = new PagedIterator(
5056
- (pageOptions) => this.fetchData({ ...options, ...pageOptions })
5057
- );
5058
- return iterator.fetchAll({ limit: options.limit ?? this.defaultLimit });
5059
- }
5060
- /**
5061
- * Create an async iterator for paginated data fetching
5062
- */
5063
- async *fetchDataPages(options) {
5064
- const iterator = new PagedIterator(
5065
- (pageOptions) => this.fetchData({ ...options, ...pageOptions })
5066
- );
5067
- for await (const page of iterator.pages({
5068
- limit: options.limit ?? this.defaultLimit
5069
- })) {
5070
- yield page;
5071
- }
5072
- }
5073
- };
5074
-
5075
- // src/internal/runtime/utils/polling.ts
5076
- var DEFAULT_POLLING_OPTIONS = {
5077
- pollIntervalMs: 1e3,
5078
- timeoutMs: 5 * 60 * 1e3
5079
- };
5080
- var POLLING_ERROR_CODES = {
5081
- ABORTED: "ABORTED",
5082
- TIMEOUT: "TIMEOUT"
5083
- };
5084
- async function pollUntil(pollFn, isComplete, options = {}) {
5085
- const internalOptions = {
5086
- ...DEFAULT_POLLING_OPTIONS,
5087
- ...options
5088
- };
5089
- const pollInterval = Math.max(250, internalOptions.pollIntervalMs);
5090
- const timeoutMs = internalOptions.timeoutMs;
5091
- const start = Date.now();
5092
- let attempts = 0;
5093
- while (Date.now() - start < timeoutMs) {
5094
- if (internalOptions.abortSignal?.aborted) {
5095
- throw new KadoaSdkException("Polling operation was aborted", {
5096
- code: POLLING_ERROR_CODES.ABORTED
5097
- });
5098
- }
5099
- attempts++;
5100
- const current = await pollFn();
5101
- if (isComplete(current)) {
5102
- return {
5103
- result: current,
5104
- attempts,
5105
- duration: Date.now() - start
5106
- };
5107
- }
5108
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
5109
- }
5110
- throw new KadoaSdkException(
5111
- `Polling operation timed out after ${timeoutMs}ms`,
5112
- {
5113
- code: POLLING_ERROR_CODES.TIMEOUT,
5114
- details: {
5115
- timeoutMs,
5116
- attempts,
5117
- duration: Date.now() - start
5118
- }
5119
- }
5120
- );
5121
- }
5122
-
5123
- // src/internal/domains/workflows/types.ts
5124
- var TERMINAL_JOB_STATES = /* @__PURE__ */ new Set([
5125
- "FINISHED",
5126
- "FAILED",
5127
- "NOT_SUPPORTED",
5128
- "FAILED_INSUFFICIENT_FUNDS"
5129
- ]);
5130
-
5131
- // src/internal/domains/workflows/workflows-core.service.ts
5132
- var TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
5133
- "FINISHED",
5134
- "SUCCESS",
5135
- "FAILED",
5136
- "ERROR",
5137
- "STOPPED",
5138
- "CANCELLED"
5139
- ]);
5140
- var debug3 = logger.workflow;
5141
- var WorkflowsCoreService = class {
5142
- constructor(workflowsApi) {
5143
- this.workflowsApi = workflowsApi;
5144
- }
5145
- async create(input) {
5146
- const request = {
5147
- urls: input.urls,
5148
- name: input.name,
5149
- schemaId: input.schemaId,
5150
- description: input.description,
5151
- navigationMode: input.navigationMode,
5152
- entity: input.entity,
5153
- fields: input.fields,
5154
- bypassPreview: input.bypassPreview ?? true,
5155
- tags: input.tags,
5156
- interval: input.interval,
5157
- monitoring: input.monitoring,
5158
- location: input.location,
5159
- autoStart: input.autoStart,
5160
- schedules: input.schedules
5161
- };
5162
- const response = await this.workflowsApi.v4WorkflowsPost({
5163
- createWorkflowBody: request
5164
- });
5165
- const workflowId = response.data?.workflowId;
5166
- if (!workflowId) {
5167
- throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
5168
- code: "INTERNAL_ERROR",
5169
- details: {
5170
- response: response.data
5171
- }
5172
- });
5173
- }
5174
- return { id: workflowId };
5175
- }
5176
- async get(id) {
5177
- const response = await this.workflowsApi.v4WorkflowsWorkflowIdGet({
5178
- workflowId: id
5179
- });
5180
- return response.data;
5181
- }
5182
- async list(filters) {
5183
- const response = await this.workflowsApi.v4WorkflowsGet(filters);
5184
- return response.data?.workflows ?? [];
5185
- }
5186
- async getByName(name) {
5187
- const response = await this.workflowsApi.v4WorkflowsGet({
5188
- search: name
5189
- });
5190
- return response.data?.workflows?.[0];
5191
- }
5192
- async cancel(id) {
5193
- await this.workflowsApi.v4WorkflowsWorkflowIdDelete({
5194
- workflowId: id
5195
- });
5196
- }
5197
- async resume(id) {
5198
- await this.workflowsApi.v4WorkflowsWorkflowIdResumePut({
5199
- workflowId: id
5200
- });
5201
- }
5202
- /**
5203
- * Wait for a workflow to reach the target state or a terminal state if no target state is provided
5204
- */
5205
- async wait(id, options) {
5206
- let last;
5207
- const result = await pollUntil(
5208
- async () => {
5209
- const current = await this.get(id);
5210
- if (last?.state !== current.state || last?.runState !== current.runState) {
5211
- debug3(
5212
- "workflow %s state: [workflowState: %s, jobState: %s]",
5213
- id,
5214
- current.state,
5215
- current.runState
5216
- );
5217
- }
5218
- last = current;
5219
- return current;
5220
- },
5221
- (current) => {
5222
- if (options?.targetState && current.state === options.targetState) {
5223
- return true;
5224
- }
5225
- if (current.runState && TERMINAL_RUN_STATES.has(current.runState.toUpperCase()) && current.state !== "QUEUED") {
5226
- return true;
5227
- }
5228
- return false;
5229
- },
5230
- options
5231
- );
5232
- return result.result;
5233
- }
5234
- /**
5235
- * Run a workflow with variables and optional limit
5236
- */
5237
- async runWorkflow(workflowId, input) {
5238
- const response = await this.workflowsApi.v4WorkflowsWorkflowIdRunPut({
5239
- workflowId,
5240
- v4WorkflowsWorkflowIdRunPutRequest: {
5241
- variables: input.variables,
5242
- limit: input.limit
5243
- }
5244
- });
5245
- const jobId = response.data?.jobId;
5246
- if (!jobId) {
5247
- throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
5248
- code: "INTERNAL_ERROR",
5249
- details: {
5250
- response: response.data
5251
- }
5252
- });
5253
- }
5254
- return {
5255
- jobId,
5256
- message: response.data?.message,
5257
- status: response.data?.status
5258
- };
5259
- }
5260
- /**
5261
- * Get job status directly without polling workflow details
5262
- */
5263
- async getJobStatus(workflowId, jobId) {
5264
- const response = await this.workflowsApi.v4WorkflowsWorkflowIdJobsJobIdGet({
5265
- workflowId,
5266
- jobId
5267
- });
5268
- return response.data;
5269
- }
5270
- /**
5271
- * Wait for a job to reach the target state or a terminal state
5272
- */
5273
- async waitForJobCompletion(workflowId, jobId, options) {
5274
- let last;
5275
- const result = await pollUntil(
5276
- async () => {
5277
- const current = await this.getJobStatus(workflowId, jobId);
5278
- if (last?.state !== current.state) {
5279
- debug3("job %s state: %s", jobId, current.state);
5280
- }
5281
- last = current;
5282
- return current;
5283
- },
5284
- (current) => {
5285
- if (options?.targetStatus && current.state === options.targetStatus) {
5286
- return true;
5287
- }
5288
- if (current.state && TERMINAL_JOB_STATES.has(current.state)) {
5289
- return true;
5290
- }
5291
- return false;
5292
- },
5293
- options
5294
- );
5295
- return result.result;
5296
- }
5297
- };
5298
-
5299
- // src/internal/domains/schemas/schemas.service.ts
5300
- var debug4 = logger.schemas;
5301
- var SchemasService = class {
5302
- constructor(client) {
5303
- this.schemasApi = new SchemasApi(client.configuration);
5304
- }
5305
- /**
5306
- * Get a schema by ID
5307
- * //todo: use proper schema type for response from generated client (when avaialble)
5308
- */
5309
- async getSchema(schemaId) {
5310
- debug4("Fetching schema with ID: %s", schemaId);
5311
- const response = await this.schemasApi.v4SchemasSchemaIdGet({
5312
- schemaId
5313
- });
5314
- const schemaData = response.data.data;
5315
- if (!schemaData) {
5316
- throw new KadoaSdkException(
5317
- `${ERROR_MESSAGES.SCHEMA_NOT_FOUND}: ${schemaId}`,
5318
- {
5319
- code: KadoaErrorCode.NOT_FOUND,
5320
- details: { schemaId }
5321
- }
5322
- );
5323
- }
5324
- return schemaData;
5325
- }
5326
- };
5327
-
5328
- // src/internal/domains/notifications/notification-setup.service.ts
5329
- var debug5 = logger.notifications;
5330
- var NotificationSetupService = class {
5331
- constructor(channelsService, settingsService) {
5332
- this.channelsService = channelsService;
5333
- this.settingsService = settingsService;
5085
+ return this.setup({
5086
+ events: requestData.events,
5087
+ channels: requestData.channels
5088
+ });
5334
5089
  }
5335
5090
  /**
5336
5091
  * Complete workflow notification setup including channels and settings
@@ -5339,10 +5094,10 @@ var NotificationSetupService = class {
5339
5094
  * @returns Array of created notification settings
5340
5095
  */
5341
5096
  async setup(requestData) {
5342
- requestData.workflowId ? debug5(
5097
+ requestData.workflowId ? debug4(
5343
5098
  "Setting up notifications for workflow %s",
5344
5099
  requestData.workflowId
5345
- ) : debug5("Setting up notifications for workspace");
5100
+ ) : debug4("Setting up notifications for workspace");
5346
5101
  const channels = await this.setupChannels({
5347
5102
  workflowId: requestData.workflowId,
5348
5103
  channels: requestData.channels || {}
@@ -5350,7 +5105,7 @@ var NotificationSetupService = class {
5350
5105
  const events = requestData.events || "all";
5351
5106
  const eventTypes = events === "all" ? await this.settingsService.listAllEvents() : events;
5352
5107
  const channelIds = channels.map((channel) => channel.id).filter(Boolean);
5353
- debug5(
5108
+ debug4(
5354
5109
  "Creating notification settings for workflow %s: %O",
5355
5110
  requestData.workflowId,
5356
5111
  {
@@ -5369,7 +5124,7 @@ var NotificationSetupService = class {
5369
5124
  });
5370
5125
  })
5371
5126
  );
5372
- debug5(
5127
+ debug4(
5373
5128
  requestData.workflowId ? "Successfully setup notifications for workflow %s" : "Successfully setup notifications for workspace",
5374
5129
  requestData.workflowId
5375
5130
  );
@@ -5451,7 +5206,7 @@ var NotificationSetupService = class {
5451
5206
  (channel2) => channel2.channelType === channelType && channel2.name === NotificationChannelsService.DEFAULT_CHANNEL_NAME
5452
5207
  );
5453
5208
  if (existingChannel) {
5454
- debug5("Using existing default channel: %O", {
5209
+ debug4("Using existing default channel: %O", {
5455
5210
  workflowId,
5456
5211
  channelType,
5457
5212
  channelId: existingChannel.id
@@ -5459,7 +5214,7 @@ var NotificationSetupService = class {
5459
5214
  return existingChannel;
5460
5215
  }
5461
5216
  const channel = await this.channelsService.createChannel(channelType);
5462
- debug5("Created default channel %O", {
5217
+ debug4("Created default channel %O", {
5463
5218
  workflowId,
5464
5219
  channelType,
5465
5220
  channel
@@ -5484,7 +5239,7 @@ var NotificationSetupService = class {
5484
5239
  (channel2) => channel2.channelType === channelType && (channel2.name || NotificationChannelsService.DEFAULT_CHANNEL_NAME) === channelName
5485
5240
  );
5486
5241
  if (existingChannel) {
5487
- debug5("Using existing channel: %O", {
5242
+ debug4("Using existing channel: %O", {
5488
5243
  workflowId,
5489
5244
  channelType,
5490
5245
  channelName,
@@ -5496,725 +5251,741 @@ var NotificationSetupService = class {
5496
5251
  name: channelName,
5497
5252
  config
5498
5253
  });
5499
- debug5("Created channel with custom config %O", {
5254
+ debug4("Created channel with custom config %O", {
5500
5255
  workflowId,
5501
5256
  channelType,
5502
5257
  channelName,
5503
5258
  channel
5504
5259
  });
5505
- return channel;
5506
- })
5507
- );
5508
- return channels;
5509
- }
5510
- };
5511
-
5512
- // src/internal/domains/validation/validation-core.service.ts
5513
- var ValidationCoreService = class {
5514
- constructor(client) {
5515
- this.validationApi = new DataValidationApi(
5516
- client.configuration,
5517
- client.baseUrl,
5518
- client.axiosInstance
5519
- );
5520
- }
5521
- async listWorkflowValidations(filters) {
5522
- const response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdJobsJobIdValidationsGet(
5523
- filters
5524
- );
5525
- if (response.status !== 200) {
5526
- throw KadoaHttpException.wrap(response.data, {
5527
- message: "Failed to list workflow validations"
5528
- });
5529
- }
5530
- return response.data;
5531
- }
5532
- async getValidationDetails(validationId) {
5533
- const response = await this.validationApi.v4DataValidationValidationsValidationIdGet({
5534
- validationId
5535
- });
5536
- if (response.status !== 200 || response.data.error) {
5537
- throw KadoaHttpException.wrap(response.data, {
5538
- message: "Failed to get validation details"
5539
- });
5540
- }
5541
- return response.data;
5542
- }
5543
- async scheduleValidation(workflowId, jobId) {
5544
- const response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdJobsJobIdValidatePost(
5545
- {
5546
- workflowId,
5547
- jobId
5548
- }
5549
- );
5550
- if (response.status !== 200 || response.data.error) {
5551
- throw KadoaHttpException.wrap(response.data, {
5552
- message: response.data.message || "Failed to schedule validation"
5553
- });
5554
- }
5555
- return response.data;
5556
- }
5557
- async toggleValidationEnabled(workflowId) {
5558
- const response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdValidationTogglePut(
5559
- {
5560
- workflowId
5561
- }
5562
- );
5563
- if (response.status !== 200 || response.data.error) {
5564
- throw KadoaHttpException.wrap(response.data, {
5565
- message: response.data.message || "Failed to toggle validation"
5566
- });
5567
- }
5568
- return response.data;
5569
- }
5570
- async getLatestValidation(workflowId, jobId) {
5571
- let response;
5572
- if (jobId) {
5573
- response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdJobsJobIdValidationsLatestGet(
5574
- {
5575
- workflowId,
5576
- jobId
5577
- }
5578
- );
5579
- } else {
5580
- response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdValidationsLatestGet(
5581
- {
5582
- workflowId
5583
- }
5584
- );
5585
- }
5586
- if (response.status !== 200 || response.data.error) {
5587
- throw KadoaHttpException.wrap(response.data, {
5588
- message: "Failed to get latest validation"
5589
- });
5590
- }
5591
- return response.data;
5592
- }
5593
- async getValidationAnomalies(validationId) {
5594
- const response = await this.validationApi.v4DataValidationValidationsValidationIdAnomaliesGet(
5595
- {
5596
- validationId
5597
- }
5598
- );
5599
- if (response.status !== 200) {
5600
- throw KadoaHttpException.wrap(response.data, {
5601
- message: "Failed to get validation anomalies"
5602
- });
5603
- }
5604
- return response.data;
5605
- }
5606
- async getValidationAnomaliesByRule(validationId, ruleName) {
5607
- const response = await this.validationApi.v4DataValidationValidationsValidationIdAnomaliesRulesRuleNameGet(
5608
- {
5609
- validationId,
5610
- ruleName
5611
- }
5612
- );
5613
- if (response.status !== 200) {
5614
- throw KadoaHttpException.wrap(response.data, {
5615
- message: "Failed to get validation anomalies by rule"
5616
- });
5617
- }
5618
- return response.data;
5619
- }
5620
- async waitUntilCompleted(validationId, options) {
5621
- const result = await pollUntil(
5622
- async () => {
5623
- const current = await this.getValidationDetails(validationId);
5624
- if (current.error) {
5625
- throw new KadoaSdkException(`Validation failed: ${current.error}`, {
5626
- code: "VALIDATION_ERROR",
5627
- details: { validationId, error: current.error }
5628
- });
5629
- }
5630
- return current;
5631
- },
5632
- (result2) => result2.completedAt != null,
5633
- options
5260
+ return channel;
5261
+ })
5634
5262
  );
5635
- return result.result;
5263
+ return channels;
5636
5264
  }
5637
5265
  };
5638
- var ValidationRulesService = class {
5639
- constructor(client) {
5640
- this.validationApi = new DataValidationApi(
5641
- client.configuration,
5642
- client.baseUrl,
5643
- client.axiosInstance
5644
- );
5645
- }
5646
- async listRules(options) {
5647
- const response = await this.validationApi.v4DataValidationRulesGet(options);
5648
- if (response.status !== 200 || response.data.error) {
5649
- throw KadoaHttpException.wrap(response.data.data, {
5650
- message: "Failed to list validation rules"
5651
- });
5266
+
5267
+ // src/runtime/config/constants.ts
5268
+ var PUBLIC_API_URI = process.env.KADOA_PUBLIC_API_URI ?? "https://api.kadoa.com";
5269
+ var WSS_API_URI = process.env.KADOA_WSS_API_URI ?? "wss://realtime.kadoa.com";
5270
+ var REALTIME_API_URI = process.env.KADOA_REALTIME_API_URI ?? "https://realtime.kadoa.com";
5271
+
5272
+ // src/version.ts
5273
+ var SDK_VERSION = "0.14.1";
5274
+ var SDK_NAME = "kadoa-node-sdk";
5275
+ var SDK_LANGUAGE = "node";
5276
+
5277
+ // src/domains/realtime/realtime.ts
5278
+ var debug5 = logger.wss;
5279
+ if (typeof WebSocket === "undefined") {
5280
+ global.WebSocket = __require("ws");
5281
+ }
5282
+ var Realtime = class {
5283
+ constructor(config) {
5284
+ this.lastHeartbeat = Date.now();
5285
+ this.isConnecting = false;
5286
+ this.eventListeners = /* @__PURE__ */ new Set();
5287
+ this.connectionListeners = /* @__PURE__ */ new Set();
5288
+ this.errorListeners = /* @__PURE__ */ new Set();
5289
+ if (!config.teamApiKey.startsWith("tk-")) {
5290
+ throw new KadoaSdkException(
5291
+ "Realtime connection requires a team API key (starting with 'tk-'). Provided key does not appear to be a team API key.",
5292
+ {
5293
+ code: "AUTH_ERROR",
5294
+ details: { providedKeyPrefix: config.teamApiKey.substring(0, 3) }
5295
+ }
5296
+ );
5652
5297
  }
5653
- return response.data;
5298
+ this.teamApiKey = config.teamApiKey;
5299
+ this.heartbeatInterval = config.heartbeatInterval || 1e4;
5300
+ this.reconnectDelay = config.reconnectDelay || 5e3;
5301
+ this.missedHeartbeatsLimit = config.missedHeartbeatsLimit || 3e4;
5654
5302
  }
5655
- async getRuleById(ruleId) {
5656
- const response = await this.validationApi.v4DataValidationRulesRuleIdGet({
5657
- ruleId
5658
- });
5659
- if (response.status !== 200 || response.data.error) {
5660
- throw KadoaHttpException.wrap(response.data.data, {
5661
- message: "Failed to get validation rule by id"
5303
+ async connect() {
5304
+ if (this.isConnecting) return;
5305
+ this.isConnecting = true;
5306
+ try {
5307
+ const response = await fetch(`${PUBLIC_API_URI}/v4/oauth2/token`, {
5308
+ method: "POST",
5309
+ headers: {
5310
+ "Content-Type": "application/json",
5311
+ "x-api-key": `${this.teamApiKey}`,
5312
+ "x-sdk-version": SDK_VERSION
5313
+ }
5662
5314
  });
5315
+ const { access_token, team_id } = await response.json();
5316
+ this.socket = new WebSocket(
5317
+ `${WSS_API_URI}?access_token=${access_token}`
5318
+ );
5319
+ this.socket.onopen = () => {
5320
+ this.isConnecting = false;
5321
+ this.lastHeartbeat = Date.now();
5322
+ if (this.socket?.readyState === WebSocket.OPEN) {
5323
+ this.socket.send(
5324
+ JSON.stringify({
5325
+ action: "subscribe",
5326
+ channel: team_id
5327
+ })
5328
+ );
5329
+ debug5("Connected to WebSocket");
5330
+ this.notifyConnectionListeners(true);
5331
+ }
5332
+ this.startHeartbeatCheck();
5333
+ };
5334
+ this.socket.onmessage = (event) => {
5335
+ try {
5336
+ const data = JSON.parse(event.data);
5337
+ if (data.type === "heartbeat") {
5338
+ this.handleHeartbeat();
5339
+ } else {
5340
+ if (data?.id) {
5341
+ fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
5342
+ method: "POST",
5343
+ headers: { "Content-Type": "application/json" },
5344
+ body: JSON.stringify({ id: data.id })
5345
+ });
5346
+ }
5347
+ this.notifyEventListeners(data);
5348
+ }
5349
+ } catch (err) {
5350
+ debug5("Failed to parse incoming message: %O", err);
5351
+ }
5352
+ };
5353
+ this.socket.onclose = () => {
5354
+ debug5("WebSocket disconnected. Attempting to reconnect...");
5355
+ this.isConnecting = false;
5356
+ this.stopHeartbeatCheck();
5357
+ this.notifyConnectionListeners(false, "Connection closed");
5358
+ setTimeout(() => this.connect(), this.reconnectDelay);
5359
+ };
5360
+ this.socket.onerror = (error) => {
5361
+ debug5("WebSocket error: %O", error);
5362
+ this.isConnecting = false;
5363
+ this.notifyErrorListeners(error);
5364
+ };
5365
+ } catch (err) {
5366
+ debug5("Failed to connect: %O", err);
5367
+ this.isConnecting = false;
5368
+ setTimeout(() => this.connect(), this.reconnectDelay);
5663
5369
  }
5664
- return response.data.data;
5665
5370
  }
5666
- async getRuleByName(name) {
5667
- const response = await this.validationApi.v4DataValidationRulesGet();
5668
- if (response.status !== 200 || response.data.error) {
5669
- throw KadoaHttpException.wrap(response.data.data, {
5670
- message: "Failed to get validation rule by name"
5671
- });
5672
- }
5673
- return response.data.data?.find((rule) => rule.name === name);
5371
+ handleHeartbeat() {
5372
+ debug5("Heartbeat received");
5373
+ this.lastHeartbeat = Date.now();
5674
5374
  }
5675
- async createRule(data) {
5676
- const response = await this.validationApi.v4DataValidationRulesPost({
5677
- createRule: data
5375
+ notifyEventListeners(event) {
5376
+ this.eventListeners.forEach((listener) => {
5377
+ try {
5378
+ listener(event);
5379
+ } catch (error) {
5380
+ debug5("Error in event listener: %O", error);
5381
+ }
5678
5382
  });
5679
- if (response.status !== 200 || response.data.error) {
5680
- throw KadoaHttpException.wrap(response.data.data, {
5681
- message: response.data.message || "Failed to create validation rule"
5682
- });
5683
- }
5684
- return response.data.data;
5685
5383
  }
5686
- async updateRule(ruleId, updateData) {
5687
- const response = await this.validationApi.v4DataValidationRulesRuleIdPut({
5688
- ruleId,
5689
- updateRule: updateData
5384
+ notifyConnectionListeners(connected, reason) {
5385
+ this.connectionListeners.forEach((listener) => {
5386
+ try {
5387
+ listener(connected, reason);
5388
+ } catch (error) {
5389
+ debug5("Error in connection listener: %O", error);
5390
+ }
5690
5391
  });
5691
- if (response.status !== 200 || response.data.error) {
5692
- throw KadoaHttpException.wrap(response.data.data, {
5693
- message: response.data.message || "Failed to update validation rule"
5694
- });
5695
- }
5696
- return response.data.data;
5697
- }
5698
- async disableRule(data) {
5699
- const response = await this.validationApi.v4DataValidationRulesRuleIdDisablePost(data);
5700
- if (response.status !== 200 || response.data.error) {
5701
- throw KadoaHttpException.wrap(response.data.data, {
5702
- message: response.data.message || "Failed to disable validation rule"
5703
- });
5704
- }
5705
- return response.data.data;
5706
5392
  }
5707
- async generateRule(data) {
5708
- const response = await this.validationApi.v4DataValidationRulesActionsGeneratePost({
5709
- generateRule: data
5393
+ notifyErrorListeners(error) {
5394
+ this.errorListeners.forEach((listener) => {
5395
+ try {
5396
+ listener(error);
5397
+ } catch (error2) {
5398
+ debug5("Error in error listener: %O", error2);
5399
+ }
5710
5400
  });
5711
- if (response.status !== 200 || response.data.error) {
5712
- throw KadoaHttpException.wrap(response.data.data, {
5713
- message: response.data.message || "Failed to generate validation rule"
5714
- });
5715
- }
5716
- return response.data.data;
5717
5401
  }
5718
- async generateRules(data) {
5719
- const response = await this.validationApi.v4DataValidationRulesActionsGenerateRulesPost({
5720
- generateRules: data
5721
- });
5722
- if (response.status !== 200 || response.data.error) {
5723
- throw KadoaHttpException.wrap(response.data.data, {
5724
- message: response.data.message || "Failed to generate validation rules"
5725
- });
5726
- }
5727
- return response.data.data;
5402
+ startHeartbeatCheck() {
5403
+ this.missedHeartbeatCheckTimer = setInterval(() => {
5404
+ if (Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
5405
+ debug5("No heartbeat received in 30 seconds! Closing connection.");
5406
+ this.socket?.close();
5407
+ }
5408
+ }, this.heartbeatInterval);
5728
5409
  }
5729
- async bulkApproveRules(data) {
5730
- const response = await this.validationApi.v4DataValidationRulesActionsBulkApprovePost({
5731
- bulkApproveRules: data
5732
- });
5733
- if (response.status !== 200 || response.data.error) {
5734
- throw KadoaHttpException.wrap(response.data.data, {
5735
- message: response.data.message || "Failed to bulk approve validation rules"
5736
- });
5410
+ stopHeartbeatCheck() {
5411
+ if (this.missedHeartbeatCheckTimer) {
5412
+ clearInterval(this.missedHeartbeatCheckTimer);
5737
5413
  }
5738
- return response.data.data;
5739
5414
  }
5740
- async bulkDeleteRules(data) {
5741
- const response = await this.validationApi.v4DataValidationRulesActionsBulkDeletePost({
5742
- bulkDeleteRules: data
5743
- });
5744
- if (response.status !== 200 || response.data.error) {
5745
- throw KadoaHttpException.wrap(response.data.data, {
5746
- message: response.data.message || "Failed to bulk delete validation rules"
5747
- });
5415
+ /**
5416
+ * Subscribe to realtime events
5417
+ * @param listener Function to handle incoming events
5418
+ * @returns Function to unsubscribe
5419
+ */
5420
+ onEvent(listener) {
5421
+ this.eventListeners.add(listener);
5422
+ return () => {
5423
+ this.eventListeners.delete(listener);
5424
+ };
5425
+ }
5426
+ /**
5427
+ * Subscribe to connection state changes
5428
+ * @param listener Function to handle connection state changes
5429
+ * @returns Function to unsubscribe
5430
+ */
5431
+ onConnection(listener) {
5432
+ this.connectionListeners.add(listener);
5433
+ return () => {
5434
+ this.connectionListeners.delete(listener);
5435
+ };
5436
+ }
5437
+ /**
5438
+ * Subscribe to errors
5439
+ * @param listener Function to handle errors
5440
+ * @returns Function to unsubscribe
5441
+ */
5442
+ onError(listener) {
5443
+ this.errorListeners.add(listener);
5444
+ return () => {
5445
+ this.errorListeners.delete(listener);
5446
+ };
5447
+ }
5448
+ close() {
5449
+ if (this.socket) {
5450
+ this.stopHeartbeatCheck();
5451
+ this.socket.close();
5452
+ this.socket = void 0;
5748
5453
  }
5749
- return response.data.data;
5454
+ this.eventListeners.clear();
5455
+ this.connectionListeners.clear();
5456
+ this.errorListeners.clear();
5750
5457
  }
5751
- async deleteAllRules(data) {
5752
- const response = await this.validationApi.v4DataValidationRulesActionsDeleteAllDelete({
5753
- deleteRuleWithReason: data
5754
- });
5755
- if (response.status !== 200 || response.data.error) {
5756
- throw KadoaHttpException.wrap(response.data.data, {
5757
- message: response.data.message || "Failed to delete all validation rules"
5758
- });
5759
- }
5760
- return response.data.data;
5458
+ isConnected() {
5459
+ return this.socket?.readyState === WebSocket.OPEN;
5761
5460
  }
5762
5461
  };
5763
5462
 
5764
- // src/internal/domains/extraction/services/entity-resolver.service.ts
5765
- var ENTITY_API_ENDPOINT = "/v4/entity";
5766
- var EntityResolverService = class {
5463
+ // src/domains/user/user.service.ts
5464
+ var UserService = class {
5767
5465
  constructor(client) {
5768
5466
  this.client = client;
5769
- this.schemasService = new SchemasService(client);
5770
5467
  }
5771
5468
  /**
5772
- * Resolves entity and fields from the provided entity configuration
5773
- *
5774
- * @param entityConfig The entity configuration to resolve
5775
- * @param options Additional options for AI detection
5776
- * @returns Resolved entity with fields
5469
+ * Get current user details
5470
+ * @returns User details
5777
5471
  */
5778
- async resolveEntity(entityConfig, options) {
5779
- if (entityConfig === "ai-detection") {
5780
- if (!options?.link) {
5781
- throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
5782
- code: "VALIDATION_ERROR",
5783
- details: { entityConfig, options }
5784
- });
5472
+ async getCurrentUser() {
5473
+ const response = await this.client.axiosInstance.get("/v5/user", {
5474
+ baseURL: this.client.baseUrl,
5475
+ headers: {
5476
+ "x-api-key": this.client.apiKey,
5477
+ "Content-Type": "application/json"
5785
5478
  }
5786
- const entityPrediction = await this.fetchEntityFields({
5787
- link: options.link,
5788
- location: options.location,
5789
- navigationMode: options.navigationMode
5479
+ });
5480
+ const userData = response.data;
5481
+ if (!userData || !userData.userId) {
5482
+ throw new KadoaSdkException("Invalid user data received");
5483
+ }
5484
+ return {
5485
+ userId: userData.userId,
5486
+ email: userData.email,
5487
+ featureFlags: userData.featureFlags || []
5488
+ };
5489
+ }
5490
+ };
5491
+
5492
+ // src/runtime/utils/polling.ts
5493
+ var DEFAULT_POLLING_OPTIONS = {
5494
+ pollIntervalMs: 1e3,
5495
+ timeoutMs: 5 * 60 * 1e3
5496
+ };
5497
+ var POLLING_ERROR_CODES = {
5498
+ ABORTED: "ABORTED",
5499
+ TIMEOUT: "TIMEOUT"
5500
+ };
5501
+ async function pollUntil(pollFn, isComplete, options = {}) {
5502
+ const internalOptions = {
5503
+ ...DEFAULT_POLLING_OPTIONS,
5504
+ ...options
5505
+ };
5506
+ const pollInterval = Math.max(250, internalOptions.pollIntervalMs);
5507
+ const timeoutMs = internalOptions.timeoutMs;
5508
+ const start = Date.now();
5509
+ let attempts = 0;
5510
+ while (Date.now() - start < timeoutMs) {
5511
+ if (internalOptions.abortSignal?.aborted) {
5512
+ throw new KadoaSdkException("Polling operation was aborted", {
5513
+ code: POLLING_ERROR_CODES.ABORTED
5790
5514
  });
5515
+ }
5516
+ attempts++;
5517
+ const current = await pollFn();
5518
+ if (isComplete(current)) {
5791
5519
  return {
5792
- entity: entityPrediction.entity,
5793
- fields: entityPrediction.fields
5520
+ result: current,
5521
+ attempts,
5522
+ duration: Date.now() - start
5794
5523
  };
5795
- } else if (entityConfig) {
5796
- if ("schemId" in entityConfig) {
5797
- const schema = await this.schemasService.getSchema(
5798
- entityConfig.schemId
5799
- );
5800
- return {
5801
- entity: schema.entity,
5802
- fields: schema.schema
5803
- };
5804
- } else if ("name" in entityConfig && "fields" in entityConfig) {
5805
- return {
5806
- entity: entityConfig.name,
5807
- fields: entityConfig.fields
5808
- };
5809
- }
5810
5524
  }
5811
- throw new KadoaSdkException(ERROR_MESSAGES.ENTITY_INVARIANT_VIOLATION, {
5812
- details: {
5813
- entity: entityConfig
5814
- }
5815
- });
5525
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
5816
5526
  }
5817
- /**
5818
- * Fetches entity fields dynamically from the /v4/entity endpoint.
5819
- * This is a workaround implementation using native fetch since the endpoint
5820
- * is not yet included in the OpenAPI specification.
5821
- *
5822
- * @param options Request options including the link to analyze
5823
- * @returns EntityPrediction containing the detected entity type and fields
5824
- */
5825
- async fetchEntityFields(options) {
5826
- this.validateEntityOptions(options);
5827
- const url = `${this.client.baseUrl}${ENTITY_API_ENDPOINT}`;
5828
- const requestBody = options;
5829
- const response = await this.client.axiosInstance.post(url, requestBody, {
5830
- headers: {
5831
- "Content-Type": "application/json",
5832
- Accept: "application/json",
5833
- "x-api-key": this.client.apiKey
5527
+ throw new KadoaSdkException(
5528
+ `Polling operation timed out after ${timeoutMs}ms`,
5529
+ {
5530
+ code: POLLING_ERROR_CODES.TIMEOUT,
5531
+ details: {
5532
+ timeoutMs,
5533
+ attempts,
5534
+ duration: Date.now() - start
5834
5535
  }
5835
- });
5836
- const data = response.data;
5837
- if (!data.success || !data.entityPrediction || data.entityPrediction.length === 0) {
5838
- throw new KadoaSdkException(ERROR_MESSAGES.NO_PREDICTIONS, {
5839
- code: "NOT_FOUND",
5840
- details: {
5841
- success: data.success,
5842
- hasPredictions: !!data.entityPrediction,
5843
- predictionCount: data.entityPrediction?.length || 0,
5844
- link: options.link
5845
- }
5846
- });
5847
5536
  }
5848
- return data.entityPrediction[0];
5537
+ );
5538
+ }
5539
+
5540
+ // src/domains/validation/validation-core.service.ts
5541
+ var ValidationCoreService = class {
5542
+ constructor(client) {
5543
+ this.validationApi = new DataValidationApi(
5544
+ client.configuration,
5545
+ client.baseUrl,
5546
+ client.axiosInstance
5547
+ );
5849
5548
  }
5850
- /**
5851
- * Validates entity request options
5852
- */
5853
- validateEntityOptions(options) {
5854
- if (!options.link) {
5855
- throw new KadoaSdkException(ERROR_MESSAGES.LINK_REQUIRED, {
5856
- code: "VALIDATION_ERROR",
5857
- details: { options }
5549
+ async listWorkflowValidations(filters) {
5550
+ const response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdJobsJobIdValidationsGet(
5551
+ filters
5552
+ );
5553
+ if (response.status !== 200) {
5554
+ throw KadoaHttpException.wrap(response.data, {
5555
+ message: "Failed to list workflow validations"
5858
5556
  });
5859
5557
  }
5558
+ return response.data;
5860
5559
  }
5861
- };
5862
-
5863
- // src/internal/domains/extraction/builders/extraction-builder.ts
5864
- var ExtractionBuilder = class {
5865
- constructor() {
5866
- this.fields = [];
5867
- }
5868
- /**
5869
- * Start defining a custom schema with fields
5870
- * @param name - The entity name (e.g., "Product", "Article")
5871
- */
5872
- schema(name) {
5873
- if (this.schemaId) {
5874
- throw new KadoaSdkException(
5875
- "Cannot use schema() after useSchema() - they are mutually exclusive",
5876
- { code: "VALIDATION_ERROR" }
5877
- );
5560
+ async getValidationDetails(validationId) {
5561
+ const response = await this.validationApi.v4DataValidationValidationsValidationIdGet({
5562
+ validationId
5563
+ });
5564
+ if (response.status !== 200 || response.data.error) {
5565
+ throw KadoaHttpException.wrap(response.data, {
5566
+ message: "Failed to get validation details"
5567
+ });
5878
5568
  }
5879
- this.schemaName = name;
5880
- return new SchemaBuilder(this);
5569
+ return response.data;
5881
5570
  }
5882
- /**
5883
- * Use an existing schema by ID
5884
- * @param schemaId - The schema ID to reference
5885
- */
5886
- useSchema(schemaId) {
5887
- if (this.schemaName || this.fields.length > 0) {
5888
- throw new KadoaSdkException(
5889
- "Cannot use useSchema() after schema() or field definitions - they are mutually exclusive",
5890
- { code: "VALIDATION_ERROR" }
5891
- );
5571
+ async scheduleValidation(workflowId, jobId) {
5572
+ const response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdJobsJobIdValidatePost(
5573
+ {
5574
+ workflowId,
5575
+ jobId
5576
+ }
5577
+ );
5578
+ if (response.status !== 200 || response.data.error) {
5579
+ throw KadoaHttpException.wrap(response.data, {
5580
+ message: response.data.message || "Failed to schedule validation"
5581
+ });
5892
5582
  }
5893
- this.schemaId = schemaId;
5894
- return this;
5583
+ return response.data;
5895
5584
  }
5896
- /**
5897
- * Extract raw page content without transformation
5898
- * @param format - Raw content format(s): "html", "markdown", or "url"
5899
- */
5900
- raw(format) {
5901
- const formats = Array.isArray(format) ? format : [format];
5902
- const nameMap = {
5903
- html: "rawHtml",
5904
- markdown: "rawMarkdown",
5905
- url: "rawUrl"
5906
- };
5907
- const metadataKeyMap = {
5908
- html: "HTML",
5909
- markdown: "MARKDOWN",
5910
- url: "PAGE_URL"
5911
- };
5912
- for (const f of formats) {
5913
- const fieldName = nameMap[f];
5914
- if (this.fields.some((field) => field.name === fieldName)) {
5915
- continue;
5585
+ async toggleValidationEnabled(workflowId) {
5586
+ const response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdValidationTogglePut(
5587
+ {
5588
+ workflowId
5916
5589
  }
5917
- this.fields.push({
5918
- name: fieldName,
5919
- description: `Raw page content in ${f.toUpperCase()} format`,
5920
- fieldType: "METADATA",
5921
- metadataKey: metadataKeyMap[f]
5590
+ );
5591
+ if (response.status !== 200 || response.data.error) {
5592
+ throw KadoaHttpException.wrap(response.data, {
5593
+ message: response.data.message || "Failed to toggle validation"
5922
5594
  });
5923
5595
  }
5924
- return this;
5925
- }
5926
- /**
5927
- * Get the fields array (internal use)
5928
- */
5929
- getFields() {
5930
- return this.fields;
5931
- }
5932
- /**
5933
- * Get the schema name (internal use)
5934
- */
5935
- getSchemaName() {
5936
- return this.schemaName;
5937
- }
5938
- /**
5939
- * Get the schema ID (internal use)
5940
- */
5941
- getSchemaId() {
5942
- return this.schemaId;
5943
- }
5944
- };
5945
- var _SchemaBuilder = class _SchemaBuilder extends ExtractionBuilder {
5946
- constructor(parentBuilder) {
5947
- super();
5948
- this.fields = parentBuilder.getFields();
5949
- this.schemaName = parentBuilder.getSchemaName();
5950
- this.schemaId = parentBuilder.getSchemaId();
5596
+ return response.data;
5951
5597
  }
5952
- /**
5953
- * Add a structured field to the schema
5954
- * @param name - Field name (alphanumeric only)
5955
- * @param description - Field description
5956
- * @param dataType - Data type (STRING, NUMBER, BOOLEAN, etc.)
5957
- * @param options - Optional field configuration
5958
- */
5959
- field(name, description, dataType, options) {
5960
- if (!_SchemaBuilder.FIELD_NAME_PATTERN.test(name)) {
5961
- throw new KadoaSdkException(
5962
- `Field name "${name}" must be alphanumeric only (no underscores or special characters)`,
5598
+ async getLatestValidation(workflowId, jobId) {
5599
+ let response;
5600
+ if (jobId) {
5601
+ response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdJobsJobIdValidationsLatestGet(
5602
+ {
5603
+ workflowId,
5604
+ jobId
5605
+ }
5606
+ );
5607
+ } else {
5608
+ response = await this.validationApi.v4DataValidationWorkflowsWorkflowIdValidationsLatestGet(
5963
5609
  {
5964
- code: "VALIDATION_ERROR",
5965
- details: { name, pattern: "^[A-Za-z0-9]+$" }
5610
+ workflowId
5966
5611
  }
5967
5612
  );
5968
5613
  }
5969
- const lowerName = name.toLowerCase();
5970
- if (this.fields.some((f) => f.name.toLowerCase() === lowerName)) {
5971
- throw new KadoaSdkException(`Duplicate field name: "${name}"`, {
5972
- code: "VALIDATION_ERROR",
5973
- details: { name }
5614
+ if (response.status !== 200 || response.data?.error) {
5615
+ throw KadoaHttpException.wrap(response.data, {
5616
+ message: "Failed to get latest validation"
5974
5617
  });
5975
5618
  }
5976
- const requiresExample = _SchemaBuilder.TYPES_REQUIRING_EXAMPLE.includes(dataType);
5977
- if (requiresExample && !options?.example) {
5978
- throw new KadoaSdkException(
5979
- `Field "${name}" with type ${dataType} requires an example`,
5980
- { code: "VALIDATION_ERROR", details: { name, dataType } }
5981
- );
5982
- }
5983
- this.fields.push({
5984
- name,
5985
- description,
5986
- dataType,
5987
- fieldType: "SCHEMA",
5988
- example: options?.example,
5989
- isKey: options?.isKey
5990
- });
5991
- return this;
5619
+ return response.data;
5992
5620
  }
5993
- /**
5994
- * Add a classification field to categorize content
5995
- * @param name - Field name (alphanumeric only)
5996
- * @param description - Field description
5997
- * @param categories - Array of category definitions
5998
- */
5999
- classify(name, description, categories) {
6000
- if (!_SchemaBuilder.FIELD_NAME_PATTERN.test(name)) {
6001
- throw new KadoaSdkException(
6002
- `Field name "${name}" must be alphanumeric only (no underscores or special characters)`,
6003
- {
6004
- code: "VALIDATION_ERROR",
6005
- details: { name, pattern: "^[A-Za-z0-9]+$" }
6006
- }
6007
- );
5621
+ async getValidationAnomalies(validationId) {
5622
+ const response = await this.validationApi.v4DataValidationValidationsValidationIdAnomaliesGet(
5623
+ {
5624
+ validationId
5625
+ }
5626
+ );
5627
+ if (response.status !== 200) {
5628
+ throw KadoaHttpException.wrap(response.data, {
5629
+ message: "Failed to get validation anomalies"
5630
+ });
6008
5631
  }
6009
- const lowerName = name.toLowerCase();
6010
- if (this.fields.some((f) => f.name.toLowerCase() === lowerName)) {
6011
- throw new KadoaSdkException(`Duplicate field name: "${name}"`, {
6012
- code: "VALIDATION_ERROR",
6013
- details: { name }
5632
+ return response.data;
5633
+ }
5634
+ async getValidationAnomaliesByRule(validationId, ruleName) {
5635
+ const response = await this.validationApi.v4DataValidationValidationsValidationIdAnomaliesRulesRuleNameGet(
5636
+ {
5637
+ validationId,
5638
+ ruleName
5639
+ }
5640
+ );
5641
+ if (response.status !== 200) {
5642
+ throw KadoaHttpException.wrap(response.data, {
5643
+ message: "Failed to get validation anomalies by rule"
6014
5644
  });
6015
5645
  }
6016
- this.fields.push({
6017
- name,
6018
- description,
6019
- fieldType: "CLASSIFICATION",
6020
- categories
6021
- });
6022
- return this;
5646
+ return response.data;
6023
5647
  }
6024
- /**
6025
- * Add raw page content alongside structured fields
6026
- * @param format - Raw content format(s): "html", "markdown", or "url"
6027
- */
6028
- raw(format) {
6029
- return super.raw(format);
5648
+ async waitUntilCompleted(validationId, options) {
5649
+ const result = await pollUntil(
5650
+ async () => {
5651
+ const current = await this.getValidationDetails(validationId);
5652
+ if (current.error) {
5653
+ throw new KadoaSdkException(`Validation failed: ${current.error}`, {
5654
+ code: "VALIDATION_ERROR",
5655
+ details: { validationId, error: current.error }
5656
+ });
5657
+ }
5658
+ return current;
5659
+ },
5660
+ (result2) => result2.completedAt != null,
5661
+ options
5662
+ );
5663
+ return result.result;
6030
5664
  }
6031
5665
  };
6032
- _SchemaBuilder.FIELD_NAME_PATTERN = /^[A-Za-z0-9]+$/;
6033
- _SchemaBuilder.TYPES_REQUIRING_EXAMPLE = [
6034
- "STRING",
6035
- "IMAGE",
6036
- "LINK",
6037
- "OBJECT",
6038
- "ARRAY"
6039
- ];
6040
- var SchemaBuilder = _SchemaBuilder;
6041
-
6042
- // src/internal/domains/extraction/services/extraction-builder.service.ts
6043
- var debug7 = logger.extraction;
6044
- var ExtractionBuilderService = class {
6045
- constructor(workflowsCoreService, entityResolverService, dataFetcherService, notificationSetupService) {
6046
- this.workflowsCoreService = workflowsCoreService;
6047
- this.entityResolverService = entityResolverService;
6048
- this.dataFetcherService = dataFetcherService;
6049
- this.notificationSetupService = notificationSetupService;
5666
+ var ValidationRulesService = class {
5667
+ constructor(client) {
5668
+ this.validationApi = new DataValidationApi(
5669
+ client.configuration,
5670
+ client.baseUrl,
5671
+ client.axiosInstance
5672
+ );
6050
5673
  }
6051
- get options() {
6052
- assert(this._options, "Options are not set");
6053
- return this._options;
5674
+ async listRules(options) {
5675
+ const response = await this.validationApi.v4DataValidationRulesGet(options);
5676
+ if (response.status !== 200 || response.data.error) {
5677
+ throw KadoaHttpException.wrap(response.data.data, {
5678
+ message: "Failed to list validation rules"
5679
+ });
5680
+ }
5681
+ return response.data;
6054
5682
  }
6055
- get notificationOptions() {
6056
- return this._notificationOptions;
5683
+ async getRuleById(ruleId) {
5684
+ const response = await this.validationApi.v4DataValidationRulesRuleIdGet({
5685
+ ruleId
5686
+ });
5687
+ if (response.status !== 200 || response.data.error) {
5688
+ throw KadoaHttpException.wrap(response.data.data, {
5689
+ message: "Failed to get validation rule by id"
5690
+ });
5691
+ }
5692
+ return response.data.data;
6057
5693
  }
6058
- get monitoringOptions() {
6059
- return this._monitoringOptions;
5694
+ async getRuleByName(name) {
5695
+ const response = await this.validationApi.v4DataValidationRulesGet();
5696
+ if (response.status !== 200 || response.data.error) {
5697
+ throw KadoaHttpException.wrap(response.data.data, {
5698
+ message: "Failed to get validation rule by name"
5699
+ });
5700
+ }
5701
+ return response.data.data?.find((rule) => rule.name === name);
6060
5702
  }
6061
- get workflowId() {
6062
- assert(this._workflowId, "Workflow ID is not set");
6063
- return this._workflowId;
5703
+ async createRule(data) {
5704
+ const response = await this.validationApi.v4DataValidationRulesPost({
5705
+ createRule: data
5706
+ });
5707
+ if (response.status !== 200 || response.data.error) {
5708
+ throw KadoaHttpException.wrap(response.data.data, {
5709
+ message: response.data.message || "Failed to create validation rule"
5710
+ });
5711
+ }
5712
+ return response.data.data;
6064
5713
  }
6065
- get jobId() {
6066
- assert(this._jobId, "Job ID is not set");
6067
- return this._jobId;
5714
+ async updateRule(ruleId, updateData) {
5715
+ const response = await this.validationApi.v4DataValidationRulesRuleIdPut({
5716
+ ruleId,
5717
+ updateRule: updateData
5718
+ });
5719
+ if (response.status !== 200 || response.data.error) {
5720
+ throw KadoaHttpException.wrap(response.data.data, {
5721
+ message: response.data.message || "Failed to update validation rule"
5722
+ });
5723
+ }
5724
+ return response.data.data;
6068
5725
  }
6069
- extract({
6070
- urls,
6071
- name,
6072
- description,
6073
- navigationMode,
6074
- extraction
6075
- }) {
6076
- let entity = "ai-detection";
6077
- if (extraction) {
6078
- const builder = extraction(new ExtractionBuilder());
6079
- const schemaId = builder.getSchemaId();
6080
- const schemaName = builder.getSchemaName();
6081
- const fields = builder.getFields();
6082
- if (schemaId) {
6083
- entity = { schemId: schemaId };
6084
- } else if (schemaName && fields.length > 0) {
6085
- entity = { name: schemaName, fields };
6086
- } else if (fields.length > 0 && !schemaName) {
6087
- entity = { name: "RawExtraction", fields };
6088
- } else {
6089
- entity = "ai-detection";
6090
- }
5726
+ async disableRule(data) {
5727
+ const response = await this.validationApi.v4DataValidationRulesRuleIdDisablePost(data);
5728
+ if (response.status !== 200 || response.data.error) {
5729
+ throw KadoaHttpException.wrap(response.data.data, {
5730
+ message: response.data.message || "Failed to disable validation rule"
5731
+ });
6091
5732
  }
6092
- this._options = {
6093
- urls,
6094
- name,
6095
- description,
6096
- navigationMode: navigationMode || "single-page",
6097
- entity,
6098
- bypassPreview: false
6099
- };
6100
- return this;
5733
+ return response.data.data;
6101
5734
  }
6102
- withNotifications(options) {
6103
- this._notificationOptions = options;
6104
- return this;
5735
+ async generateRule(data) {
5736
+ const response = await this.validationApi.v4DataValidationRulesActionsGeneratePost({
5737
+ generateRule: data
5738
+ });
5739
+ if (response.status !== 200 || response.data.error) {
5740
+ throw KadoaHttpException.wrap(response.data.data, {
5741
+ message: response.data.message || "Failed to generate validation rule"
5742
+ });
5743
+ }
5744
+ return response.data.data;
6105
5745
  }
6106
- withMonitoring(options) {
6107
- this._monitoringOptions = options;
6108
- return this;
5746
+ async generateRules(data) {
5747
+ const response = await this.validationApi.v4DataValidationRulesActionsGenerateRulesPost({
5748
+ generateRules: data
5749
+ });
5750
+ if (response.status !== 200 || response.data.error) {
5751
+ throw KadoaHttpException.wrap(response.data.data, {
5752
+ message: response.data.message || "Failed to generate validation rules"
5753
+ });
5754
+ }
5755
+ return response.data.data;
6109
5756
  }
6110
- bypassPreview() {
6111
- assert(this._options, "Options are not set");
6112
- this._options.bypassPreview = true;
6113
- return this;
5757
+ async bulkApproveRules(data) {
5758
+ const response = await this.validationApi.v4DataValidationRulesActionsBulkApprovePost({
5759
+ bulkApproveRules: data
5760
+ });
5761
+ if (response.status !== 200 || response.data.error) {
5762
+ throw KadoaHttpException.wrap(response.data.data, {
5763
+ message: response.data.message || "Failed to bulk approve validation rules"
5764
+ });
5765
+ }
5766
+ return response.data.data;
6114
5767
  }
6115
- setInterval(options) {
6116
- assert(this._options, "Options are not set");
6117
- if ("interval" in options) {
6118
- this._options.interval = options.interval;
6119
- } else {
6120
- this._options.interval = "CUSTOM";
6121
- this._options.schedules = options.schedules;
5768
+ async bulkDeleteRules(data) {
5769
+ const response = await this.validationApi.v4DataValidationRulesActionsBulkDeletePost({
5770
+ bulkDeleteRules: data
5771
+ });
5772
+ if (response.status !== 200 || response.data.error) {
5773
+ throw KadoaHttpException.wrap(response.data.data, {
5774
+ message: response.data.message || "Failed to bulk delete validation rules"
5775
+ });
6122
5776
  }
6123
- return this;
6124
- }
6125
- setLocation(options) {
6126
- assert(this._options, "Options are not set");
6127
- this._options.location = options;
6128
- return this;
5777
+ return response.data.data;
6129
5778
  }
6130
- async create() {
6131
- assert(this._options, "Options are not set");
6132
- const { urls, name, description, navigationMode, entity } = this.options;
6133
- const resolvedEntity = typeof entity === "object" && "schemId" in entity ? void 0 : await this.entityResolverService.resolveEntity(entity, {
6134
- link: urls[0],
6135
- location: this._options.location,
6136
- navigationMode
5779
+ async deleteAllRules(data) {
5780
+ const response = await this.validationApi.v4DataValidationRulesActionsDeleteAllDelete({
5781
+ deleteRuleWithReason: data
6137
5782
  });
6138
- if (!resolvedEntity) {
6139
- throw new KadoaSdkException(ERROR_MESSAGES.ENTITY_FETCH_FAILED, {
6140
- code: "VALIDATION_ERROR",
6141
- details: { entity }
5783
+ if (response.status !== 200 || response.data.error) {
5784
+ throw KadoaHttpException.wrap(response.data.data, {
5785
+ message: response.data.message || "Failed to delete all validation rules"
6142
5786
  });
6143
5787
  }
6144
- const workflow = await this.workflowsCoreService.create({
6145
- urls,
6146
- name,
6147
- description,
6148
- navigationMode,
6149
- monitoring: this._monitoringOptions,
6150
- schemaId: typeof entity === "object" && "schemId" in entity ? entity.schemId : void 0,
6151
- entity: resolvedEntity.entity,
6152
- fields: resolvedEntity.fields,
6153
- autoStart: false,
6154
- interval: this._options.interval,
6155
- schedules: this._options.schedules
5788
+ return response.data.data;
5789
+ }
5790
+ };
5791
+
5792
+ // src/domains/workflows/workflows.acl.ts
5793
+ var JobStateEnum = {
5794
+ Finished: "FINISHED",
5795
+ Failed: "FAILED",
5796
+ NotSupported: "NOT_SUPPORTED",
5797
+ FailedInsufficientFunds: "FAILED_INSUFFICIENT_FUNDS"
5798
+ };
5799
+
5800
+ // src/domains/workflows/workflows-core.service.ts
5801
+ var TERMINAL_JOB_STATES = /* @__PURE__ */ new Set([
5802
+ JobStateEnum.Finished,
5803
+ JobStateEnum.Failed,
5804
+ JobStateEnum.NotSupported,
5805
+ JobStateEnum.FailedInsufficientFunds
5806
+ ]);
5807
+ var TERMINAL_RUN_STATES = /* @__PURE__ */ new Set([
5808
+ "FINISHED",
5809
+ "SUCCESS",
5810
+ "FAILED",
5811
+ "ERROR",
5812
+ "STOPPED",
5813
+ "CANCELLED"
5814
+ ]);
5815
+ var debug6 = logger.workflow;
5816
+ var WorkflowsCoreService = class {
5817
+ constructor(workflowsApi) {
5818
+ this.workflowsApi = workflowsApi;
5819
+ }
5820
+ async create(input) {
5821
+ const request = {
5822
+ urls: input.urls,
5823
+ name: input.name,
5824
+ schemaId: input.schemaId,
5825
+ description: input.description,
5826
+ navigationMode: input.navigationMode,
5827
+ entity: input.entity,
5828
+ fields: input.fields,
5829
+ bypassPreview: input.bypassPreview ?? true,
5830
+ tags: input.tags,
5831
+ interval: input.interval,
5832
+ monitoring: input.monitoring,
5833
+ location: input.location,
5834
+ autoStart: input.autoStart,
5835
+ schedules: input.schedules
5836
+ };
5837
+ const response = await this.workflowsApi.v4WorkflowsPost({
5838
+ createWorkflowBody: request
6156
5839
  });
6157
- if (this._notificationOptions) {
6158
- await this.notificationSetupService.setup({
6159
- ...this._notificationOptions,
6160
- workflowId: workflow.id
5840
+ const workflowId = response.data?.workflowId;
5841
+ if (!workflowId) {
5842
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
5843
+ code: "INTERNAL_ERROR",
5844
+ details: {
5845
+ response: response.data
5846
+ }
6161
5847
  });
6162
5848
  }
6163
- this._workflowId = workflow.id;
6164
- return this;
5849
+ return { id: workflowId };
6165
5850
  }
6166
- async run(options) {
6167
- assert(this._options, "Options are not set");
6168
- assert(this._workflowId, "Workflow ID is not set");
6169
- const startedJob = await this.workflowsCoreService.runWorkflow(
6170
- this._workflowId,
6171
- { variables: options?.variables, limit: options?.limit }
6172
- );
6173
- debug7("Job started: %O", startedJob);
6174
- this._jobId = startedJob.jobId;
6175
- const finishedJob = await this.workflowsCoreService.waitForJobCompletion(
6176
- this._workflowId,
6177
- startedJob.jobId
6178
- );
6179
- debug7("Job finished: %O", finishedJob);
6180
- return this;
5851
+ async get(id) {
5852
+ const response = await this.workflowsApi.v4WorkflowsWorkflowIdGet({
5853
+ workflowId: id
5854
+ });
5855
+ return response.data;
6181
5856
  }
6182
- async submit(options) {
6183
- assert(this._options, "Options are not set");
6184
- assert(this._workflowId, "Workflow ID is not set");
6185
- const submittedJob = await this.workflowsCoreService.runWorkflow(
6186
- this._workflowId,
6187
- { variables: options?.variables, limit: options?.limit }
5857
+ async list(filters) {
5858
+ const response = await this.workflowsApi.v4WorkflowsGet(filters);
5859
+ return response.data?.workflows ?? [];
5860
+ }
5861
+ async getByName(name) {
5862
+ const response = await this.workflowsApi.v4WorkflowsGet({
5863
+ search: name
5864
+ });
5865
+ return response.data?.workflows?.[0];
5866
+ }
5867
+ async cancel(id) {
5868
+ await this.workflowsApi.v4WorkflowsWorkflowIdDelete({
5869
+ workflowId: id
5870
+ });
5871
+ }
5872
+ async resume(id) {
5873
+ await this.workflowsApi.v4WorkflowsWorkflowIdResumePut({
5874
+ workflowId: id
5875
+ });
5876
+ }
5877
+ /**
5878
+ * Wait for a workflow to reach the target state or a terminal state if no target state is provided
5879
+ */
5880
+ async wait(id, options) {
5881
+ let last;
5882
+ const result = await pollUntil(
5883
+ async () => {
5884
+ const current = await this.get(id);
5885
+ if (last?.state !== current.state || last?.runState !== current.runState) {
5886
+ debug6(
5887
+ "workflow %s state: [workflowState: %s, jobState: %s]",
5888
+ id,
5889
+ current.state,
5890
+ current.runState
5891
+ );
5892
+ }
5893
+ last = current;
5894
+ return current;
5895
+ },
5896
+ (current) => {
5897
+ if (options?.targetState && current.state === options.targetState) {
5898
+ return true;
5899
+ }
5900
+ if (current.runState && TERMINAL_RUN_STATES.has(current.runState.toUpperCase()) && current.state !== "QUEUED") {
5901
+ return true;
5902
+ }
5903
+ return false;
5904
+ },
5905
+ options
6188
5906
  );
6189
- debug7("Job submitted: %O", submittedJob);
6190
- this._jobId = submittedJob.jobId;
5907
+ return result.result;
5908
+ }
5909
+ /**
5910
+ * Run a workflow with variables and optional limit
5911
+ */
5912
+ async runWorkflow(workflowId, input) {
5913
+ const response = await this.workflowsApi.v4WorkflowsWorkflowIdRunPut({
5914
+ workflowId,
5915
+ v4WorkflowsWorkflowIdRunPutRequest: {
5916
+ variables: input.variables,
5917
+ limit: input.limit
5918
+ }
5919
+ });
5920
+ const jobId = response.data?.jobId;
5921
+ if (!jobId) {
5922
+ throw new KadoaSdkException(ERROR_MESSAGES.NO_WORKFLOW_ID, {
5923
+ code: "INTERNAL_ERROR",
5924
+ details: {
5925
+ response: response.data
5926
+ }
5927
+ });
5928
+ }
6191
5929
  return {
6192
- workflowId: this._workflowId,
6193
- jobId: this._jobId
5930
+ jobId,
5931
+ message: response.data?.message,
5932
+ status: response.data?.status
6194
5933
  };
6195
5934
  }
6196
- async fetchData(options) {
6197
- assert(this._workflowId, "Workflow ID is not set");
6198
- assert(this._jobId, "Job ID is not set");
6199
- return this.dataFetcherService.fetchData({
6200
- workflowId: this._workflowId,
6201
- runId: this._jobId,
6202
- page: options.page ?? 1,
6203
- limit: options.limit ?? 100,
6204
- ...options
5935
+ /**
5936
+ * Get job status directly without polling workflow details
5937
+ */
5938
+ async getJobStatus(workflowId, jobId) {
5939
+ const response = await this.workflowsApi.v4WorkflowsWorkflowIdJobsJobIdGet({
5940
+ workflowId,
5941
+ jobId
6205
5942
  });
5943
+ return response.data;
6206
5944
  }
6207
- async fetchAllData(options) {
6208
- assert(this._jobId, "Job ID is not set");
6209
- assert(this._workflowId, "Workflow ID is not set");
6210
- return this.dataFetcherService.fetchAllData({
6211
- workflowId: this._workflowId,
6212
- runId: this._jobId,
6213
- ...options
6214
- });
5945
+ /**
5946
+ * Wait for a job to reach the target state or a terminal state
5947
+ */
5948
+ async waitForJobCompletion(workflowId, jobId, options) {
5949
+ let last;
5950
+ const result = await pollUntil(
5951
+ async () => {
5952
+ const current = await this.getJobStatus(workflowId, jobId);
5953
+ if (last?.state !== current.state) {
5954
+ debug6("job %s state: %s", jobId, current.state);
5955
+ }
5956
+ last = current;
5957
+ return current;
5958
+ },
5959
+ (current) => {
5960
+ if (options?.targetStatus && current.state === options.targetStatus) {
5961
+ return true;
5962
+ }
5963
+ if (current.state && TERMINAL_JOB_STATES.has(current.state)) {
5964
+ return true;
5965
+ }
5966
+ return false;
5967
+ },
5968
+ options
5969
+ );
5970
+ return result.result;
6215
5971
  }
6216
5972
  };
6217
5973
 
5974
+ // src/domains/validation/validation.facade.ts
5975
+ function createValidationDomain(core, rules) {
5976
+ return {
5977
+ rules,
5978
+ schedule: (workflowId, jobId) => core.scheduleValidation(workflowId, jobId),
5979
+ listWorkflowValidations: (filters) => core.listWorkflowValidations(filters),
5980
+ getValidationDetails: (validationId) => core.getValidationDetails(validationId),
5981
+ toggleEnabled: (workflowId) => core.toggleValidationEnabled(workflowId),
5982
+ getLatest: (workflowId, jobId) => core.getLatestValidation(workflowId, jobId),
5983
+ getAnomalies: (validationId) => core.getValidationAnomalies(validationId),
5984
+ getAnomaliesByRule: (validationId, ruleName) => core.getValidationAnomaliesByRule(validationId, ruleName),
5985
+ waitUntilCompleted: (validationId, options) => core.waitUntilCompleted(validationId, options)
5986
+ };
5987
+ }
5988
+
6218
5989
  // src/kadoa-client.ts
6219
5990
  var KadoaClient = class {
6220
5991
  constructor(config) {
@@ -6291,7 +6062,9 @@ var KadoaClient = class {
6291
6062
  workflowsCoreService,
6292
6063
  dataFetcherService,
6293
6064
  entityResolverService,
6294
- channelSetupService
6065
+ channelSetupService,
6066
+ channelsService,
6067
+ settingsService
6295
6068
  );
6296
6069
  this._extractionBuilderService = new ExtractionBuilderService(
6297
6070
  workflowsCoreService,
@@ -6299,22 +6072,19 @@ var KadoaClient = class {
6299
6072
  dataFetcherService,
6300
6073
  channelSetupService
6301
6074
  );
6302
- this.user = new UserModule(userService);
6303
- this.extraction = new ExtractionModule(
6304
- extractionService,
6305
- dataFetcherService,
6306
- channelsService,
6307
- settingsService,
6308
- workflowsCoreService
6309
- );
6310
- this.workflow = new WorkflowsModule(workflowsCoreService);
6311
- this.schema = new SchemasModule(schemasService);
6312
- this.notification = new NotificationsModule(
6313
- channelsService,
6314
- settingsService,
6315
- channelSetupService
6316
- );
6317
- this.validation = new ValidationModule(coreService, rulesService);
6075
+ this.user = userService;
6076
+ this.extraction = extractionService;
6077
+ this.workflow = workflowsCoreService;
6078
+ this.schema = schemasService;
6079
+ this.notification = {
6080
+ channels: channelsService,
6081
+ settings: settingsService,
6082
+ setup: channelSetupService,
6083
+ configure: (options) => channelSetupService.setup(options),
6084
+ setupForWorkflow: (request) => channelSetupService.setupForWorkflow(request),
6085
+ setupForWorkspace: (request) => channelSetupService.setupForWorkspace(request)
6086
+ };
6087
+ this.validation = createValidationDomain(coreService, rulesService);
6318
6088
  if (config.enableRealtime && config.realtimeConfig?.autoConnect !== false) {
6319
6089
  this.connectRealtime();
6320
6090
  }
@@ -6453,6 +6223,6 @@ var KadoaClient = class {
6453
6223
  }
6454
6224
  };
6455
6225
 
6456
- export { ERROR_MESSAGES, KadoaClient, KadoaHttpException, KadoaSdkException, SchemasModule, ValidationModule, pollUntil };
6226
+ export { DataFetcherService, ERROR_MESSAGES, EntityResolverService, ExtractionBuilderService, ExtractionService, FetchDataOptions, KadoaClient, KadoaHttpException, KadoaSdkException, NotificationChannelType, NotificationChannelsService, V5NotificationsSettingsGetEventTypeEnum as NotificationSettingsEventTypeEnum, NotificationSettingsService, NotificationSetupService, Realtime, SchemaBuilder, SchemaFieldDataType, SchemasService, UserService, ValidationCoreService, ValidationRulesService, WorkflowsCoreService, pollUntil };
6457
6227
  //# sourceMappingURL=index.mjs.map
6458
6228
  //# sourceMappingURL=index.mjs.map