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