@kadoa/node-sdk 0.13.0 → 0.15.0

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