@uipath/uipath-typescript 1.3.7 → 1.3.9

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.
Files changed (47) hide show
  1. package/dist/assets/index.cjs +64 -274
  2. package/dist/assets/index.d.ts +1 -0
  3. package/dist/assets/index.mjs +64 -274
  4. package/dist/attachments/index.cjs +62 -271
  5. package/dist/attachments/index.d.ts +1 -0
  6. package/dist/attachments/index.mjs +62 -271
  7. package/dist/buckets/index.cjs +93 -274
  8. package/dist/buckets/index.d.ts +51 -1
  9. package/dist/buckets/index.mjs +93 -274
  10. package/dist/cases/index.cjs +580 -336
  11. package/dist/cases/index.d.ts +690 -3
  12. package/dist/cases/index.mjs +581 -337
  13. package/dist/conversational-agent/index.cjs +110 -285
  14. package/dist/conversational-agent/index.d.ts +63 -12
  15. package/dist/conversational-agent/index.mjs +110 -286
  16. package/dist/core/index.cjs +39 -289
  17. package/dist/core/index.d.ts +9 -98
  18. package/dist/core/index.mjs +40 -275
  19. package/dist/document-understanding/index.cjs +18 -1
  20. package/dist/document-understanding/index.d.ts +636 -610
  21. package/dist/document-understanding/index.mjs +18 -1
  22. package/dist/entities/index.cjs +64 -274
  23. package/dist/entities/index.d.ts +1 -0
  24. package/dist/entities/index.mjs +64 -274
  25. package/dist/feedback/index.cjs +313 -276
  26. package/dist/feedback/index.d.ts +418 -12
  27. package/dist/feedback/index.mjs +313 -276
  28. package/dist/index.cjs +777 -297
  29. package/dist/index.d.ts +2005 -721
  30. package/dist/index.mjs +777 -283
  31. package/dist/index.umd.js +966 -162
  32. package/dist/jobs/index.cjs +64 -274
  33. package/dist/jobs/index.d.ts +1 -0
  34. package/dist/jobs/index.mjs +64 -274
  35. package/dist/maestro-processes/index.cjs +1789 -1686
  36. package/dist/maestro-processes/index.d.ts +431 -2
  37. package/dist/maestro-processes/index.mjs +1790 -1687
  38. package/dist/processes/index.cjs +64 -274
  39. package/dist/processes/index.d.ts +1 -0
  40. package/dist/processes/index.mjs +64 -274
  41. package/dist/queues/index.cjs +64 -274
  42. package/dist/queues/index.d.ts +1 -0
  43. package/dist/queues/index.mjs +64 -274
  44. package/dist/tasks/index.cjs +64 -274
  45. package/dist/tasks/index.d.ts +1 -0
  46. package/dist/tasks/index.mjs +64 -274
  47. package/package.json +8 -10
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var sdkLogs = require('@opentelemetry/sdk-logs');
3
+ var coreTelemetry = require('@uipath/core-telemetry');
4
4
 
5
5
  /******************************************************************************
6
6
  Copyright (c) Microsoft Corporation.
@@ -45,465 +45,404 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
45
45
  };
46
46
 
47
47
  /**
48
- * Type guards for error response types
48
+ * Base path constants for different services
49
49
  */
50
- function isOrchestratorError(error) {
51
- return typeof error === 'object' &&
52
- error !== null &&
53
- 'message' in error &&
54
- 'errorCode' in error &&
55
- typeof error.message === 'string' &&
56
- typeof error.errorCode === 'number';
50
+ const PIMS_BASE = 'pims_';
51
+ const INSIGHTS_RTM_BASE = 'insightsrtm_';
52
+
53
+ /**
54
+ * Maestro Service Endpoints
55
+ */
56
+ /**
57
+ * Maestro Process Service Endpoints
58
+ */
59
+ const MAESTRO_ENDPOINTS = {
60
+ PROCESSES: {
61
+ GET_ALL: `${PIMS_BASE}/api/v1/processes/summary`},
62
+ INSTANCES: {
63
+ GET_ALL: `${PIMS_BASE}/api/v1/instances`,
64
+ GET_BY_ID: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}`,
65
+ GET_EXECUTION_HISTORY: (instanceId) => `${PIMS_BASE}/api/v1/spans/${instanceId}`,
66
+ GET_BPMN: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/bpmn`,
67
+ GET_VARIABLES: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/variables`,
68
+ CANCEL: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/cancel`,
69
+ PAUSE: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/pause`,
70
+ RESUME: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/resume`,
71
+ },
72
+ INCIDENTS: {
73
+ GET_ALL: `${PIMS_BASE}/api/v1/incidents/summary`,
74
+ GET_BY_PROCESS: (processKey) => `${PIMS_BASE}/api/v1/incidents/process/${processKey}`,
75
+ GET_BY_INSTANCE: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/incidents`,
76
+ },
77
+ INSIGHTS: {
78
+ /** Top processes ranked by run count */
79
+ TOP_PROCESSES_BY_RUN_COUNT: `${INSIGHTS_RTM_BASE}/agenticInstanceStatus/TopProcessesByRunCount`,
80
+ /** Top processes ranked by failure count */
81
+ TOP_PROCESSES_WITH_FAILURE: `${INSIGHTS_RTM_BASE}/agenticInstanceStatus/TopProcesseswithFailure`,
82
+ /** Instance status aggregated by date for time-series charts */
83
+ INSTANCE_STATUS_BY_DATE: `${INSIGHTS_RTM_BASE}/agenticInstanceStatus/InstanceStatusByDate`,
84
+ /** Top processes ranked by total duration */
85
+ TOP_PROCESSES_BY_DURATION: `${INSIGHTS_RTM_BASE}/agenticInstanceStatus/TopProcessesByDuration`,
86
+ },
87
+ };
88
+
89
+ /**
90
+ * Maestro Process Models
91
+ * Model classes for Maestro processes
92
+ */
93
+ /**
94
+ * Creates methods for a process object
95
+ *
96
+ * @param processData - The process data (response from API)
97
+ * @param service - The process service instance
98
+ * @returns Object containing process methods
99
+ */
100
+ function createProcessMethods(processData, service) {
101
+ return {
102
+ async getIncidents() {
103
+ if (!processData.processKey)
104
+ throw new Error('Process key is undefined');
105
+ if (!processData.folderKey)
106
+ throw new Error('Folder key is undefined');
107
+ return service.getIncidents(processData.processKey, processData.folderKey);
108
+ }
109
+ };
57
110
  }
58
- function isEntityError(error) {
59
- return typeof error === 'object' &&
60
- error !== null &&
61
- 'error' in error &&
62
- typeof error.error === 'string';
111
+ /**
112
+ * Creates an actionable process by combining API process data with operational methods.
113
+ *
114
+ * @param processData - The process data from API
115
+ * @param service - The process service instance
116
+ * @returns A process object with added methods
117
+ */
118
+ function createProcessWithMethods(processData, service) {
119
+ const methods = createProcessMethods(processData, service);
120
+ return Object.assign({}, processData, methods);
63
121
  }
64
- function isPimsError(error) {
65
- return typeof error === 'object' &&
66
- error !== null &&
67
- 'type' in error &&
68
- 'title' in error &&
69
- 'status' in error &&
70
- typeof error.type === 'string' &&
71
- typeof error.title === 'string' &&
72
- typeof error.status === 'number';
122
+
123
+ /**
124
+ * Builds the request body for Insights RTM "top" endpoints.
125
+ *
126
+ * @param startTime - Start of the time range to query
127
+ * @param endTime - End of the time range to query
128
+ * @param isCaseManagement - Whether to filter for case management processes
129
+ * @param options - Optional filters (packageId, processKey, version)
130
+ * @returns Request body for the Insights RTM endpoint
131
+ * @internal
132
+ */
133
+ function buildInsightsTopBody(startTime, endTime, isCaseManagement, options) {
134
+ return {
135
+ commonParams: {
136
+ startTime: startTime.getTime(),
137
+ endTime: endTime.getTime(),
138
+ isCaseManagement,
139
+ ...(options?.packageId ? { packageId: options.packageId } : {}),
140
+ ...(options?.processKey ? { processKey: options.processKey } : {}),
141
+ ...(options?.version ? { version: options.version } : {}),
142
+ }
143
+ };
144
+ }
145
+ /**
146
+ * Fetches instance status timeline from the Insights API.
147
+ * Shared implementation used by both MaestroProcessesService and CasesService.
148
+ *
149
+ * @param postFn - Bound post method from a BaseService subclass
150
+ * @param startTime - Start of the time range to query
151
+ * @param endTime - End of the time range to query
152
+ * @param isCaseManagement - Whether to filter for case management processes
153
+ * @param options - Optional settings for time bucketing granularity
154
+ * @returns Promise resolving to an array of instance status timeline entries
155
+ * @internal
156
+ */
157
+ async function fetchInstanceStatusTimeline(postFn, startTime, endTime, isCaseManagement, options) {
158
+ const response = await postFn(MAESTRO_ENDPOINTS.INSIGHTS.INSTANCE_STATUS_BY_DATE, {
159
+ commonParams: {
160
+ startTime: startTime.getTime(),
161
+ endTime: endTime.getTime(),
162
+ isCaseManagement,
163
+ },
164
+ timeSliceUnit: options?.groupBy,
165
+ timezoneOffset: new Date().getTimezoneOffset() * -1,
166
+ });
167
+ return response.data ?? [];
73
168
  }
74
169
 
75
170
  /**
76
- * HTTP status code constants for error handling
171
+ * Common constants used across the SDK
77
172
  */
78
- const HttpStatus = {
79
- // Client errors (4xx)
80
- BAD_REQUEST: 400,
81
- UNAUTHORIZED: 401,
82
- FORBIDDEN: 403,
83
- NOT_FOUND: 404,
84
- TOO_MANY_REQUESTS: 429,
85
- // Server errors (5xx)
86
- INTERNAL_SERVER_ERROR: 500,
87
- BAD_GATEWAY: 502,
88
- SERVICE_UNAVAILABLE: 503,
89
- GATEWAY_TIMEOUT: 504
90
- };
91
173
  /**
92
- * Error type constants for consistent error identification
174
+ * Prefix used for OData query parameters
93
175
  */
94
- const ErrorType = {
95
- AUTHENTICATION: 'AuthenticationError',
96
- AUTHORIZATION: 'AuthorizationError',
97
- VALIDATION: 'ValidationError',
98
- NOT_FOUND: 'NotFoundError',
99
- RATE_LIMIT: 'RateLimitError',
100
- SERVER: 'ServerError',
101
- NETWORK: 'NetworkError'
176
+ const ODATA_PREFIX = '$';
177
+ const UNKNOWN = 'Unknown';
178
+ const NO_INSTANCE = 'no-instance';
179
+ /**
180
+ * HTTP methods
181
+ */
182
+ const HTTP_METHODS = {
183
+ GET: 'GET',
184
+ POST: 'POST'};
185
+ /**
186
+ * Process Instance pagination constants for token-based pagination
187
+ */
188
+ const PROCESS_INSTANCE_PAGINATION = {
189
+ /** Field name for items in process instance response */
190
+ ITEMS_FIELD: 'instances',
191
+ /** Field name for continuation token in process instance response */
192
+ CONTINUATION_TOKEN_FIELD: 'nextPage'
102
193
  };
103
194
  /**
104
- * HTTP header constants for error handling
195
+ * OData OFFSET pagination parameter names (ODATA-style)
105
196
  */
106
- const HttpHeaders = {
107
- X_REQUEST_ID: 'x-request-id'
197
+ const ODATA_OFFSET_PARAMS = {
198
+ /** OData page size parameter name */
199
+ PAGE_SIZE_PARAM: '$top',
200
+ /** OData offset parameter name */
201
+ OFFSET_PARAM: '$skip',
202
+ /** OData count parameter name */
203
+ COUNT_PARAM: '$count'
108
204
  };
109
205
  /**
110
- * Standard error message constants
206
+ * Bucket TOKEN pagination parameter names
111
207
  */
112
- const ErrorMessages = {
113
- // Authentication errors
114
- AUTHENTICATION_FAILED: 'Authentication failed',
115
- // Authorization errors
116
- ACCESS_DENIED: 'Access denied',
117
- // Validation errors
118
- VALIDATION_FAILED: 'Validation failed',
119
- // Not found errors
120
- RESOURCE_NOT_FOUND: 'Resource not found',
121
- // Rate limit errors
122
- RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
123
- // Server errors
124
- INTERNAL_SERVER_ERROR: 'Internal Server error occurred',
125
- // Network errors
126
- NETWORK_ERROR: 'Network error occurred',
127
- REQUEST_TIMEOUT: 'Request timed out',
128
- REQUEST_ABORTED: 'Request was aborted',
208
+ const BUCKET_TOKEN_PARAMS = {
209
+ /** Bucket page size parameter name */
210
+ PAGE_SIZE_PARAM: 'takeHint',
211
+ /** Bucket token parameter name */
212
+ TOKEN_PARAM: 'continuationToken'
129
213
  };
130
214
  /**
131
- * Error name constants for network error identification
215
+ * Process Instance TOKEN pagination parameter names
132
216
  */
133
- const ErrorNames = {
134
- ABORT_ERROR: 'AbortError'};
217
+ const PROCESS_INSTANCE_TOKEN_PARAMS = {
218
+ /** Process instance page size parameter name */
219
+ PAGE_SIZE_PARAM: 'pageSize',
220
+ /** Process instance token parameter name */
221
+ TOKEN_PARAM: 'nextPage'
222
+ };
135
223
 
136
224
  /**
137
- * Parser for Orchestrator/Task error format
225
+ * Converts a UTC timestamp string (e.g., "5/8/2026 11:20:17 AM") to ISO 8601 UTC format.
226
+ * Returns the original value if parsing fails.
138
227
  */
139
- class OrchestratorErrorParser {
140
- canParse(errorBody) {
141
- return isOrchestratorError(errorBody);
142
- }
143
- parse(errorBody, response) {
144
- const error = errorBody;
145
- return {
146
- message: error.message,
147
- code: response?.status?.toString(),
148
- details: {
149
- errorCode: error.errorCode,
150
- traceId: error.traceId,
151
- originalResponse: error
152
- },
153
- requestId: error.traceId
154
- };
155
- }
156
- }
157
228
  /**
158
- * Parser for Entity (Data Fabric) error format
229
+ * Transforms data by mapping fields according to the provided field mapping
230
+ * @param data The source data to transform
231
+ * @param fieldMapping Object mapping source field names to target field names
232
+ * @returns Transformed data with mapped field names
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * // Single object transformation
237
+ * const data = { id: '123', userName: 'john' };
238
+ * const mapping = { id: 'userId', userName: 'name' };
239
+ * const result = transformData(data, mapping);
240
+ * // result = { userId: '123', name: 'john' }
241
+ *
242
+ * // Array transformation
243
+ * const dataArray = [
244
+ * { id: '123', userName: 'john' },
245
+ * { id: '456', userName: 'jane' }
246
+ * ];
247
+ * const result = transformData(dataArray, mapping);
248
+ * // result = [
249
+ * // { userId: '123', name: 'john' },
250
+ * // { userId: '456', name: 'jane' }
251
+ * // ]
252
+ * ```
159
253
  */
160
- class EntityErrorParser {
161
- canParse(errorBody) {
162
- return isEntityError(errorBody);
254
+ function transformData(data, fieldMapping) {
255
+ // Handle array of objects
256
+ if (Array.isArray(data)) {
257
+ return data.map(item => transformData(item, fieldMapping));
163
258
  }
164
- parse(errorBody, response) {
165
- const error = errorBody;
166
- return {
167
- message: error.error,
168
- code: response?.status?.toString(),
169
- details: {
170
- error: error.error,
171
- traceId: error.traceId,
172
- originalResponse: error
173
- },
174
- requestId: error.traceId
175
- };
259
+ // Handle single object
260
+ const result = { ...data };
261
+ for (const [sourceField, targetField] of Object.entries(fieldMapping)) {
262
+ if (sourceField in result) {
263
+ const value = result[sourceField];
264
+ delete result[sourceField];
265
+ result[targetField] = value;
266
+ }
176
267
  }
268
+ return result;
177
269
  }
178
270
  /**
179
- * Parser for PIMS error format
271
+ * Adds a prefix to specified keys in an object, returning a new object.
272
+ * Only the provided keys are prefixed; all others are left unchanged.
273
+ *
274
+ * @param obj The source object
275
+ * @param prefix The prefix to add (e.g., '$')
276
+ * @param keys The keys to prefix (e.g., ['expand', 'filter'])
277
+ * @returns A new object with specified keys prefixed
278
+ *
279
+ * @example
280
+ * addPrefixToKeys({ expand: 'a', foo: 1 }, '$', ['expand']) // { $expand: 'a', foo: 1 }
180
281
  */
181
- class PimsErrorParser {
182
- canParse(errorBody) {
183
- return isPimsError(errorBody);
184
- }
185
- parse(errorBody, response) {
186
- const error = errorBody;
187
- let message = error.title;
188
- // If there are validation errors, append them to the message for better visibility
189
- if (error.errors && Object.keys(error.errors).length > 0) {
190
- const errorMessages = Object.entries(error.errors)
191
- .map(([field, messages]) => `${field}: ${messages.join(', ')}`)
192
- .join('; ');
193
- message += `. Validation errors: ${errorMessages}`;
282
+ function addPrefixToKeys(obj, prefix, keys) {
283
+ const result = {};
284
+ for (const [key, value] of Object.entries(obj)) {
285
+ if (keys.includes(key)) {
286
+ result[`${prefix}${key}`] = value;
287
+ }
288
+ else {
289
+ result[key] = value;
194
290
  }
195
- return {
196
- message,
197
- code: response?.status?.toString(),
198
- details: {
199
- type: error.type,
200
- title: error.title,
201
- status: error.status,
202
- errors: error.errors,
203
- traceId: error.traceId,
204
- originalResponse: error
205
- },
206
- requestId: error.traceId
207
- };
208
291
  }
292
+ return result;
209
293
  }
294
+
210
295
  /**
211
- * Fallback parser for unrecognized formats
296
+ * Maps fields for Incident entities
212
297
  */
213
- class GenericErrorParser {
214
- canParse(_errorBody) {
215
- return true; // Always can parse as last resort
216
- }
217
- parse(errorBody, response) {
218
- // For unknown error formats, just pass through the raw error with fallback message
219
- const message = response?.statusText || 'An error occurred';
220
- return {
221
- message,
222
- code: response?.status?.toString(),
223
- details: {
224
- originalResponse: errorBody
225
- },
226
- };
227
- }
228
- }
298
+ const ProcessIncidentMap = {
299
+ errorTimeUtc: 'errorTime'
300
+ };
229
301
  /**
230
- * Main error response parser using Chain of Responsibility pattern
231
- *
232
- * This parser standardizes error responses from different UiPath services into a
233
- * consistent format, regardless of the original error structure.
234
- *
235
- * Supported formats:
236
- * 1. Orchestrator/Task: { message, errorCode, traceId }
237
- * 2. Entity (Data Fabric): { error, traceId }
238
- * 3. PIMS/Maestro: { type, title, status, errors?, traceId? }
239
- * 4. Generic: Fallback for any other format
240
- *
241
- * @example
242
- * const parser = new ErrorResponseParser();
243
- * const errorInfo = await parser.parse(response);
244
- * // errorInfo will have consistent structure regardless of service
302
+ * Maps fields for Incident Summary entities
245
303
  */
246
- class ErrorResponseParser {
247
- constructor() {
248
- this.strategies = [
249
- new OrchestratorErrorParser(),
250
- new EntityErrorParser(),
251
- new PimsErrorParser(),
252
- new GenericErrorParser() // Must be last
253
- ];
254
- }
304
+ const ProcessIncidentSummaryMap = {
305
+ firstTimeUtc: 'firstOccuranceTime'
306
+ };
307
+
308
+ /**
309
+ * Helpers for fetching BPMN XML and extracting element details used to annotate responses
310
+ */
311
+ class BpmnHelpers {
255
312
  /**
256
- * Parses error response body into standardized format
257
- * @param response - The HTTP response object
258
- * @returns Standardized error information
313
+ * Parse BPMN XML and extract element id → {name,type} used for incidents
259
314
  */
260
- async parse(response) {
315
+ static parseBpmnElementsForIncidents(bpmnXml) {
316
+ const elementInfo = {};
261
317
  try {
262
- const errorBody = await response.json();
263
- // Find the first strategy that can parse this error format
264
- const strategy = this.strategies.find(s => s.canParse(errorBody));
265
- // GenericErrorParser always returns true, so this will never be null
266
- return strategy.parse(errorBody, response);
318
+ // Find <bpmn:...> start tags and capture the element type.
319
+ // Then read 'id' and 'name' attributes from each tag.
320
+ const bpmnOpenTagRegex = /<bpmn:([A-Za-z][\w.-]*)\b[^>]*>/g;
321
+ for (const tagMatch of bpmnXml.matchAll(bpmnOpenTagRegex)) {
322
+ const [fullTag, elementType] = tagMatch;
323
+ // Extract attributes from the current tag text.
324
+ const idMatch = /\bid\s*=\s*"([^"]*)"/.exec(fullTag);
325
+ if (!idMatch) {
326
+ continue;
327
+ }
328
+ const elementId = idMatch[1];
329
+ const nameMatch = /\bname\s*=\s*"([^"]*)"/.exec(fullTag);
330
+ const name = nameMatch ? nameMatch[1] : '';
331
+ // Convert BPMN element type to human-readable format
332
+ const activityType = this.formatActivityTypeForIncidents(elementType);
333
+ const activityName = name || elementId;
334
+ elementInfo[elementId] = {
335
+ type: activityType,
336
+ name: activityName
337
+ };
338
+ }
267
339
  }
268
- catch {
269
- // Handle non-JSON responses
270
- const responseText = await response.text().catch(() => '');
271
- return {
272
- message: response.statusText,
273
- code: response.status.toString(),
274
- details: {
275
- parseError: 'Failed to parse error response as JSON',
276
- responseText
277
- },
278
- requestId: response.headers.get(HttpHeaders.X_REQUEST_ID) || undefined
279
- };
340
+ catch (error) {
341
+ console.warn('Failed to parse BPMN XML for incidents:', error);
280
342
  }
343
+ return elementInfo;
281
344
  }
282
- }
283
- // Export singleton instance
284
- const errorResponseParser = new ErrorResponseParser();
285
-
286
- /**
287
- * Base error class for all UiPath SDK errors
288
- * Extends Error for standard error handling compatibility
289
- */
290
- class UiPathError extends Error {
291
- constructor(type, params) {
292
- super(params.message);
293
- this.name = type;
294
- this.type = type;
295
- this.statusCode = params.statusCode;
296
- this.requestId = params.requestId;
297
- this.timestamp = new Date();
298
- // Maintains proper stack trace for where our error was thrown
299
- if (Error.captureStackTrace) {
300
- Error.captureStackTrace(this, this.constructor);
345
+ /**
346
+ * Format BPMN element type to human-readable activity type for incidents
347
+ */
348
+ static formatActivityTypeForIncidents(elementType) {
349
+ // Convert camelCase BPMN element types to human-readable format
350
+ // e.g., "serviceTask" -> "Service Task", "exclusiveGateway" -> "Exclusive Gateway"
351
+ return elementType
352
+ .replace(/([A-Z])/g, ' $1') // Add space before uppercase letters
353
+ .replace(/^./, str => str.toUpperCase()) // Capitalize first letter
354
+ .trim(); // Remove any leading/trailing spaces
355
+ }
356
+ /**
357
+ * Fetch BPMN via getBpmn and add element name/type to each incident
358
+ */
359
+ static async enrichIncidentsWithBpmnData(incidents, folderKey, service) {
360
+ // Check if all incidents have the same instanceId
361
+ const uniqueInstanceIds = [...new Set(incidents.map(i => i.instanceId))];
362
+ if (uniqueInstanceIds.length === 1) {
363
+ // Single instance optimization (in case of process instance incidents)
364
+ const elementInfo = await this.getBpmnElementInfo(uniqueInstanceIds[0], folderKey, service);
365
+ return incidents.map((incident) => this.transformIncidentWithBpmn(incident, elementInfo));
366
+ }
367
+ else {
368
+ // Multiple instances optimization (in case of process incidents)
369
+ return this.enrichMultipleInstanceIncidents(incidents, folderKey, service);
301
370
  }
302
371
  }
303
372
  /**
304
- * Returns a clean JSON representation of the error
373
+ * When incidents span multiple instances, fetch BPMN per instance and annotate
305
374
  */
306
- toJSON() {
307
- return {
308
- type: this.type,
309
- message: this.message,
310
- statusCode: this.statusCode,
311
- requestId: this.requestId,
312
- timestamp: this.timestamp
313
- };
375
+ static async enrichMultipleInstanceIncidents(incidents, folderKey, service) {
376
+ const groups = incidents.reduce((acc, incident) => {
377
+ const id = incident.instanceId || NO_INSTANCE;
378
+ (acc[id] = acc[id] || []).push(incident);
379
+ return acc;
380
+ }, {});
381
+ const results = await Promise.all(Object.entries(groups).map(async (entry) => {
382
+ const [instanceId, groupIncidents] = entry;
383
+ const elementInfo = await this.getBpmnElementInfo(instanceId, folderKey, service);
384
+ return groupIncidents.map((incident) => this.transformIncidentWithBpmn(incident, elementInfo));
385
+ }));
386
+ return results.flat();
314
387
  }
315
388
  /**
316
- * Returns detailed debug information including stack trace
389
+ * Retrieve BPMN XML for an instance and derive element id → {name,type}
317
390
  */
318
- getDebugInfo() {
391
+ static async getBpmnElementInfo(instanceId, folderKey, service) {
392
+ if (!instanceId || instanceId === NO_INSTANCE) {
393
+ return {};
394
+ }
395
+ try {
396
+ const bpmnXml = await service.getBpmn(instanceId, folderKey);
397
+ return this.parseBpmnElementsForIncidents(bpmnXml);
398
+ }
399
+ catch (error) {
400
+ console.warn(`Failed to get BPMN for instance ${instanceId}:`, error);
401
+ return {};
402
+ }
403
+ }
404
+ /**
405
+ * Transform a raw incident by attaching element name/type from BPMN
406
+ */
407
+ static transformIncidentWithBpmn(incident, elementInfo) {
408
+ const element = elementInfo[incident.elementId];
409
+ const transformed = transformData(incident, ProcessIncidentMap);
319
410
  return {
320
- ...this.toJSON(),
321
- stack: this.stack
411
+ ...transformed,
412
+ incidentElementActivityType: element?.type || UNKNOWN,
413
+ incidentElementActivityName: element?.name || UNKNOWN
322
414
  };
323
415
  }
324
416
  }
325
417
 
326
418
  /**
327
- * Error thrown when authentication fails (401 errors)
328
- * Common scenarios:
329
- * - Invalid credentials
330
- * - Expired token
331
- * - Missing authentication
332
- */
333
- class AuthenticationError extends UiPathError {
334
- constructor(params = {}) {
335
- super(ErrorType.AUTHENTICATION, {
336
- message: params.message || ErrorMessages.AUTHENTICATION_FAILED,
337
- statusCode: params.statusCode ?? HttpStatus.UNAUTHORIZED,
338
- requestId: params.requestId
339
- });
340
- }
341
- }
419
+ * SDK Telemetry constants.
420
+ *
421
+ * Only the SDK's identity (version, service name, role name, …) lives
422
+ * here. The Application Insights connection string is injected into
423
+ * `@uipath/core-telemetry` itself at publish time, and the generic attribute
424
+ * keys (`Version`, `Service`, `CloudOrganizationName`, …) are owned by
425
+ * `@uipath/core-telemetry` and consumed there — they are not part of the
426
+ * SDK's public API.
427
+ */
428
+ /** SDK version placeholder — patched by the SDK publish workflow. */
429
+ const CLOUD_ROLE_NAME = 'uipath-ts-sdk';
342
430
 
343
431
  /**
344
- * Error thrown when authorization fails (403 errors)
345
- * Common scenarios:
346
- * - Insufficient permissions
347
- * - Access denied to resource
348
- * - Invalid scope
349
- */
350
- class AuthorizationError extends UiPathError {
351
- constructor(params = {}) {
352
- super(ErrorType.AUTHORIZATION, {
353
- message: params.message || ErrorMessages.ACCESS_DENIED,
354
- statusCode: params.statusCode ?? HttpStatus.FORBIDDEN,
355
- requestId: params.requestId
356
- });
357
- }
358
- }
359
-
360
- /**
361
- * Error thrown when validation fails (400 errors or client-side validation)
362
- * Common scenarios:
363
- * - Invalid input parameters
364
- * - Missing required fields
365
- * - Invalid data format
366
- */
367
- class ValidationError extends UiPathError {
368
- constructor(params = {}) {
369
- super(ErrorType.VALIDATION, {
370
- message: params.message || ErrorMessages.VALIDATION_FAILED,
371
- statusCode: params.statusCode ?? HttpStatus.BAD_REQUEST,
372
- requestId: params.requestId
373
- });
374
- }
375
- }
376
-
377
- /**
378
- * Error thrown when a resource is not found (404 errors)
379
- * Common scenarios:
380
- * - Resource doesn't exist
381
- * - Invalid ID provided
382
- * - Resource deleted
383
- */
384
- class NotFoundError extends UiPathError {
385
- constructor(params = {}) {
386
- super(ErrorType.NOT_FOUND, {
387
- message: params.message || ErrorMessages.RESOURCE_NOT_FOUND,
388
- statusCode: params.statusCode ?? HttpStatus.NOT_FOUND,
389
- requestId: params.requestId
390
- });
391
- }
392
- }
393
-
394
- /**
395
- * Error thrown when rate limit is exceeded (429 errors)
396
- * Common scenarios:
397
- * - Too many requests in a time window
398
- * - API throttling
399
- */
400
- class RateLimitError extends UiPathError {
401
- constructor(params = {}) {
402
- super(ErrorType.RATE_LIMIT, {
403
- message: params.message || ErrorMessages.RATE_LIMIT_EXCEEDED,
404
- statusCode: params.statusCode ?? HttpStatus.TOO_MANY_REQUESTS,
405
- requestId: params.requestId
406
- });
407
- }
408
- }
409
-
410
- /**
411
- * Error thrown when server encounters an error (5xx errors)
412
- * Common scenarios:
413
- * - Internal server error
414
- * - Service unavailable
415
- * - Gateway timeout
416
- */
417
- class ServerError extends UiPathError {
418
- constructor(params = {}) {
419
- super(ErrorType.SERVER, {
420
- message: params.message || ErrorMessages.INTERNAL_SERVER_ERROR,
421
- statusCode: params.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR,
422
- requestId: params.requestId
423
- });
424
- }
425
- /**
426
- * Checks if this is a temporary error that might succeed on retry
427
- */
428
- get isRetryable() {
429
- return this.statusCode === HttpStatus.BAD_GATEWAY ||
430
- this.statusCode === HttpStatus.SERVICE_UNAVAILABLE ||
431
- this.statusCode === HttpStatus.GATEWAY_TIMEOUT;
432
- }
433
- }
434
-
435
- /**
436
- * Error thrown when network/connection issues occur
437
- * Common scenarios:
438
- * - Connection timeout
439
- * - DNS resolution failure
440
- * - Network unreachable
441
- * - Request aborted
442
- */
443
- class NetworkError extends UiPathError {
444
- constructor(params = {}) {
445
- super(ErrorType.NETWORK, {
446
- message: params.message || ErrorMessages.NETWORK_ERROR,
447
- statusCode: params.statusCode, // Network errors typically don't have HTTP status codes
448
- requestId: params.requestId
449
- });
450
- }
451
- }
452
-
453
- /**
454
- * Factory for creating typed errors based on HTTP status codes
455
- * Follows the Factory pattern for clean error instantiation
456
- */
457
- class ErrorFactory {
458
- /**
459
- * Creates appropriate error instance based on HTTP status code
460
- */
461
- static createFromHttpStatus(statusCode, errorInfo) {
462
- const { message, requestId } = errorInfo;
463
- // Map status codes to error types
464
- switch (statusCode) {
465
- case HttpStatus.BAD_REQUEST:
466
- return new ValidationError({ message, statusCode, requestId });
467
- case HttpStatus.UNAUTHORIZED:
468
- return new AuthenticationError({ message, statusCode, requestId });
469
- case HttpStatus.FORBIDDEN:
470
- return new AuthorizationError({ message, statusCode, requestId });
471
- case HttpStatus.NOT_FOUND:
472
- return new NotFoundError({ message, statusCode, requestId });
473
- case HttpStatus.TOO_MANY_REQUESTS:
474
- return new RateLimitError({ message, statusCode, requestId });
475
- default:
476
- // For 5xx errors or any other status code
477
- if (statusCode >= HttpStatus.INTERNAL_SERVER_ERROR) {
478
- return new ServerError({ message, statusCode, requestId });
479
- }
480
- // For unknown client errors, treat as validation error
481
- return new ValidationError({
482
- message: `${message} (HTTP ${statusCode})`,
483
- statusCode,
484
- requestId
485
- });
486
- }
487
- }
488
- /**
489
- * Creates a NetworkError from a fetch/network error
490
- */
491
- static createNetworkError(error) {
492
- let message = ErrorMessages.NETWORK_ERROR;
493
- if (error instanceof Error) {
494
- if (error.name === ErrorNames.ABORT_ERROR) {
495
- message = ErrorMessages.REQUEST_ABORTED;
496
- }
497
- else if (error.message.includes('timeout')) {
498
- message = ErrorMessages.REQUEST_TIMEOUT;
499
- }
500
- else {
501
- message = error.message;
502
- }
503
- }
504
- return new NetworkError({ message });
505
- }
506
- }
432
+ * UiPath TypeScript SDK Telemetry
433
+ *
434
+ * Constructs the SDK's own `TelemetryClient` and binds the SDK-local
435
+ * `track` / `trackEvent` to it. Each consumer of `@uipath/core-telemetry`
436
+ * does this independently, so events carry their own consumer's identity
437
+ * and tenant context.
438
+ */
439
+ // Keyed by `CLOUD_ROLE_NAME` so every SDK subpath bundle resolves to the
440
+ // same `TelemetryClient` instance at runtime. A single `initialize(...)`
441
+ // from the `UiPath` constructor therefore wires up `@track` decorators
442
+ // across every subpath bundle (`assets`, `feedback`, `tasks`, …).
443
+ const sdkClient = coreTelemetry.getOrCreateClient(CLOUD_ROLE_NAME);
444
+ const track = coreTelemetry.createTrack(sdkClient);
445
+ coreTelemetry.createTrackEvent(sdkClient);
507
446
 
508
447
  const FOLDER_KEY = 'X-UIPATH-FolderKey';
509
448
  const FOLDER_ID = 'X-UIPATH-OrganizationUnitId';
@@ -527,1489 +466,1399 @@ const RESPONSE_TYPES = {
527
466
  ARRAYBUFFER: 'arraybuffer'
528
467
  };
529
468
 
530
- class ApiClient {
531
- constructor(config, executionContext, tokenManager, clientConfig = {}) {
532
- this.config = config;
533
- this.executionContext = executionContext;
534
- this.clientConfig = clientConfig;
535
- this.tokenManager = tokenManager;
536
- }
537
- /**
538
- * Gets a valid authentication token, refreshing if necessary.
539
- * Used internally for API requests and exposed for services that need manual auth headers.
540
- *
541
- * @returns The valid token
542
- * @throws AuthenticationError if no token available or refresh fails
543
- */
544
- async getValidToken() {
545
- return this.tokenManager.getValidToken();
546
- }
547
- async getDefaultHeaders() {
548
- const token = await this.getValidToken();
549
- return {
550
- 'Authorization': `Bearer ${token}`,
551
- 'Content-Type': CONTENT_TYPES.JSON,
552
- ...this.clientConfig.headers
553
- };
554
- }
555
- async request(method, path, options = {}) {
556
- // Ensure path starts with a forward slash
557
- const normalizedPath = path.startsWith('/') ? path.substring(1) : path;
558
- // Construct URL with org and tenant names
559
- const url = new URL(`${this.config.orgName}/${this.config.tenantName}/${normalizedPath}`, this.config.baseUrl).toString();
560
- const isFormData = options.body instanceof FormData;
561
- const defaultHeaders = await this.getDefaultHeaders();
562
- if (isFormData) {
563
- delete defaultHeaders['Content-Type'];
564
- }
565
- const traceId = crypto.randomUUID().replace(/-/g, '');
566
- const spanId = crypto.randomUUID().replace(/-/g, '').slice(0, 16);
567
- const traceparentValue = `00-${traceId}-${spanId}-01`;
568
- const headers = {
569
- ...defaultHeaders,
570
- [TRACEPARENT]: traceparentValue,
571
- [UIPATH_TRACEPARENT_ID]: traceparentValue,
572
- ...options.headers
573
- };
574
- // Convert params to URLSearchParams
575
- const searchParams = new URLSearchParams();
576
- if (options.params) {
577
- Object.entries(options.params).forEach(([key, value]) => {
578
- searchParams.append(key, value.toString());
579
- });
580
- }
581
- const fullUrl = searchParams.toString() ? `${url}?${searchParams.toString()}` : url;
582
- let body = undefined;
583
- if (options.body) {
584
- body = isFormData ? options.body : JSON.stringify(options.body);
469
+ /**
470
+ * Creates headers object from key-value pairs
471
+ * @param headersObj - Object containing header key-value pairs
472
+ * @returns Headers object with all values converted to strings
473
+ *
474
+ * @example
475
+ * ```typescript
476
+ * // Single header
477
+ * const headers = createHeaders({ 'X-UIPATH-FolderKey': '1234567890' });
478
+ *
479
+ * // Multiple headers
480
+ * const headers = createHeaders({
481
+ * 'X-UIPATH-FolderKey': '1234567890',
482
+ * 'X-UIPATH-OrganizationUnitId': 123,
483
+ * 'Accept': 'application/json'
484
+ * });
485
+ *
486
+ * // Using constants
487
+ * import { FOLDER_KEY, FOLDER_ID } from '../constants/headers';
488
+ * const headers = createHeaders({
489
+ * [FOLDER_KEY]: 'abc-123',
490
+ * [FOLDER_ID]: 456
491
+ * });
492
+ *
493
+ * // Empty headers
494
+ * const headers = createHeaders();
495
+ * ```
496
+ */
497
+ function createHeaders(headersObj) {
498
+ const headers = {};
499
+ for (const [key, value] of Object.entries(headersObj)) {
500
+ if (value !== undefined && value !== null) {
501
+ headers[key] = value.toString();
585
502
  }
586
- try {
587
- const response = await fetch(fullUrl, {
588
- method,
589
- headers,
590
- body,
591
- signal: options.signal
592
- });
593
- if (!response.ok) {
594
- const errorInfo = await errorResponseParser.parse(response);
595
- throw ErrorFactory.createFromHttpStatus(response.status, errorInfo);
596
- }
597
- if (response.status === 204) {
598
- return undefined;
599
- }
600
- // Handle blob response type for binary data (e.g., file downloads)
601
- if (options.responseType === RESPONSE_TYPES.BLOB) {
602
- const blob = await response.blob();
603
- return blob;
604
- }
605
- // Check if we're expecting XML
606
- const acceptHeader = headers['Accept'] || headers['accept'];
607
- if (acceptHeader === CONTENT_TYPES.XML) {
608
- const text = await response.text();
609
- return text;
610
- }
611
- const text = await response.text();
612
- if (!text) {
613
- return undefined;
614
- }
615
- return JSON.parse(text);
616
- }
617
- catch (error) {
618
- // If it's already one of our errors, re-throw it
619
- if (error.type && error.type.includes('Error')) {
620
- throw error;
621
- }
622
- // Otherwise, it's likely a network error
623
- throw ErrorFactory.createNetworkError(error);
624
- }
625
- }
626
- async get(path, options = {}) {
627
- return this.request('GET', path, options);
628
- }
629
- async post(path, data, options = {}) {
630
- return this.request('POST', path, { ...options, body: data });
631
- }
632
- async put(path, data, options = {}) {
633
- return this.request('PUT', path, { ...options, body: data });
634
- }
635
- async patch(path, data, options = {}) {
636
- return this.request('PATCH', path, { ...options, body: data });
637
- }
638
- async delete(path, options = {}) {
639
- return this.request('DELETE', path, options);
640
503
  }
504
+ return headers;
641
505
  }
642
506
 
643
507
  /**
644
- * Pagination types supported by the SDK
508
+ * Type guards for error response types
645
509
  */
646
- var PaginationType;
647
- (function (PaginationType) {
648
- PaginationType["OFFSET"] = "offset";
649
- PaginationType["TOKEN"] = "token";
650
- })(PaginationType || (PaginationType = {}));
510
+ function isOrchestratorError(error) {
511
+ return typeof error === 'object' &&
512
+ error !== null &&
513
+ 'message' in error &&
514
+ 'errorCode' in error &&
515
+ typeof error.message === 'string' &&
516
+ typeof error.errorCode === 'number';
517
+ }
518
+ function isEntityError(error) {
519
+ return typeof error === 'object' &&
520
+ error !== null &&
521
+ 'error' in error &&
522
+ typeof error.error === 'string';
523
+ }
524
+ function isPimsError(error) {
525
+ return typeof error === 'object' &&
526
+ error !== null &&
527
+ 'type' in error &&
528
+ 'title' in error &&
529
+ 'status' in error &&
530
+ typeof error.type === 'string' &&
531
+ typeof error.title === 'string' &&
532
+ typeof error.status === 'number';
533
+ }
651
534
 
652
535
  /**
653
- * Collection of utility functions for working with objects
536
+ * HTTP status code constants for error handling
654
537
  */
538
+ const HttpStatus = {
539
+ // Client errors (4xx)
540
+ BAD_REQUEST: 400,
541
+ UNAUTHORIZED: 401,
542
+ FORBIDDEN: 403,
543
+ NOT_FOUND: 404,
544
+ TOO_MANY_REQUESTS: 429,
545
+ // Server errors (5xx)
546
+ INTERNAL_SERVER_ERROR: 500,
547
+ BAD_GATEWAY: 502,
548
+ SERVICE_UNAVAILABLE: 503,
549
+ GATEWAY_TIMEOUT: 504
550
+ };
655
551
  /**
656
- * Filters out undefined values from an object
657
- * @param obj The source object
658
- * @returns A new object without undefined values
659
- *
660
- * @example
661
- * ```typescript
662
- * // Object with undefined values
663
- * const options = {
664
- * name: 'test',
665
- * count: 5,
666
- * prefix: undefined,
667
- * suffix: null
668
- * };
669
- * const result = filterUndefined(options);
670
- * // result = { name: 'test', count: 5, suffix: null }
671
- * ```
552
+ * Error type constants for consistent error identification
672
553
  */
673
- function filterUndefined(obj) {
674
- const result = {};
675
- for (const [key, value] of Object.entries(obj)) {
676
- if (value !== undefined) {
677
- result[key] = value;
678
- }
679
- }
680
- return result;
681
- }
682
-
554
+ const ErrorType = {
555
+ AUTHENTICATION: 'AuthenticationError',
556
+ AUTHORIZATION: 'AuthorizationError',
557
+ VALIDATION: 'ValidationError',
558
+ NOT_FOUND: 'NotFoundError',
559
+ RATE_LIMIT: 'RateLimitError',
560
+ SERVER: 'ServerError',
561
+ NETWORK: 'NetworkError'
562
+ };
683
563
  /**
684
- * Utility functions for platform detection
564
+ * HTTP header constants for error handling
685
565
  */
566
+ const HttpHeaders = {
567
+ X_REQUEST_ID: 'x-request-id'
568
+ };
686
569
  /**
687
- * Checks if code is running in a browser environment
570
+ * Standard error message constants
688
571
  */
689
- const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
690
- isBrowser && window.self != window.top && window.location.href.includes('source=ActionCenter');
691
-
572
+ const ErrorMessages = {
573
+ // Authentication errors
574
+ AUTHENTICATION_FAILED: 'Authentication failed',
575
+ // Authorization errors
576
+ ACCESS_DENIED: 'Access denied',
577
+ // Validation errors
578
+ VALIDATION_FAILED: 'Validation failed',
579
+ // Not found errors
580
+ RESOURCE_NOT_FOUND: 'Resource not found',
581
+ // Rate limit errors
582
+ RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
583
+ // Server errors
584
+ INTERNAL_SERVER_ERROR: 'Internal Server error occurred',
585
+ // Network errors
586
+ NETWORK_ERROR: 'Network error occurred',
587
+ REQUEST_TIMEOUT: 'Request timed out',
588
+ REQUEST_ABORTED: 'Request was aborted',
589
+ };
692
590
  /**
693
- * Base64 encoding/decoding
591
+ * Error name constants for network error identification
694
592
  */
593
+ const ErrorNames = {
594
+ ABORT_ERROR: 'AbortError'};
595
+
695
596
  /**
696
- * Encodes a string to base64
697
- * @param str - The string to encode
698
- * @returns Base64 encoded string
597
+ * Parser for Orchestrator/Task error format
699
598
  */
700
- function encodeBase64(str) {
701
- // TextEncoder for UTF-8 encoding (works in both browser and Node.js)
702
- const encoder = new TextEncoder();
703
- const data = encoder.encode(str);
704
- // Convert Uint8Array to base64
705
- if (isBrowser) {
706
- // Browser environment
707
- // Convert Uint8Array to binary string then to base64
708
- const binaryString = Array.from(data, byte => String.fromCharCode(byte)).join('');
709
- return btoa(binaryString);
599
+ class OrchestratorErrorParser {
600
+ canParse(errorBody) {
601
+ return isOrchestratorError(errorBody);
710
602
  }
711
- else {
712
- // Node.js environment
713
- return Buffer.from(data).toString('base64');
603
+ parse(errorBody, response) {
604
+ const error = errorBody;
605
+ return {
606
+ message: error.message,
607
+ code: response?.status?.toString(),
608
+ details: {
609
+ errorCode: error.errorCode,
610
+ traceId: error.traceId,
611
+ originalResponse: error
612
+ },
613
+ requestId: error.traceId
614
+ };
714
615
  }
715
616
  }
716
617
  /**
717
- * Decodes a base64 string
718
- * @param base64 - The base64 string to decode
719
- * @returns Decoded string
618
+ * Parser for Entity (Data Fabric) error format
720
619
  */
721
- function decodeBase64(base64) {
722
- let bytes;
723
- if (isBrowser) {
724
- // Browser environment
725
- const binaryString = atob(base64);
726
- bytes = new Uint8Array(binaryString.length);
727
- for (let i = 0; i < binaryString.length; i++) {
728
- bytes[i] = binaryString.charCodeAt(i);
729
- }
620
+ class EntityErrorParser {
621
+ canParse(errorBody) {
622
+ return isEntityError(errorBody);
730
623
  }
731
- else {
732
- // Node.js environment
733
- bytes = new Uint8Array(Buffer.from(base64, 'base64'));
624
+ parse(errorBody, response) {
625
+ const error = errorBody;
626
+ return {
627
+ message: error.error,
628
+ code: response?.status?.toString(),
629
+ details: {
630
+ error: error.error,
631
+ traceId: error.traceId,
632
+ originalResponse: error
633
+ },
634
+ requestId: error.traceId
635
+ };
734
636
  }
735
- // TextDecoder for UTF-8 decoding (works in both browser and Node.js)
736
- const decoder = new TextDecoder();
737
- return decoder.decode(bytes);
738
637
  }
739
-
740
638
  /**
741
- * PaginationManager handles the conversion between uniform cursor-based pagination
742
- * and the specific pagination type for each service
639
+ * Parser for PIMS error format
743
640
  */
744
- class PaginationManager {
745
- /**
746
- * Create a pagination cursor for subsequent page requests
747
- */
748
- static createCursor({ pageInfo, type }) {
749
- if (!pageInfo.hasMore) {
750
- return undefined;
641
+ class PimsErrorParser {
642
+ canParse(errorBody) {
643
+ return isPimsError(errorBody);
644
+ }
645
+ parse(errorBody, response) {
646
+ const error = errorBody;
647
+ let message = error.title;
648
+ // If there are validation errors, append them to the message for better visibility
649
+ if (error.errors && Object.keys(error.errors).length > 0) {
650
+ const errorMessages = Object.entries(error.errors)
651
+ .map(([field, messages]) => `${field}: ${messages.join(', ')}`)
652
+ .join('; ');
653
+ message += `. Validation errors: ${errorMessages}`;
751
654
  }
752
- const cursorData = {
753
- type,
754
- pageSize: pageInfo.pageSize,
655
+ return {
656
+ message,
657
+ code: response?.status?.toString(),
658
+ details: {
659
+ type: error.type,
660
+ title: error.title,
661
+ status: error.status,
662
+ errors: error.errors,
663
+ traceId: error.traceId,
664
+ originalResponse: error
665
+ },
666
+ requestId: error.traceId
755
667
  };
756
- switch (type) {
757
- case PaginationType.OFFSET:
758
- if (pageInfo.currentPage) {
759
- cursorData.pageNumber = pageInfo.currentPage + 1;
760
- }
761
- break;
762
- case PaginationType.TOKEN:
763
- if (pageInfo.continuationToken) {
764
- cursorData.continuationToken = pageInfo.continuationToken;
765
- }
766
- else {
767
- return undefined; // No continuation token, can't continue
768
- }
769
- break;
770
- }
668
+ }
669
+ }
670
+ /**
671
+ * Fallback parser for unrecognized formats
672
+ */
673
+ class GenericErrorParser {
674
+ canParse(_errorBody) {
675
+ return true; // Always can parse as last resort
676
+ }
677
+ parse(errorBody, response) {
678
+ // For unknown error formats, just pass through the raw error with fallback message
679
+ const message = response?.statusText || 'An error occurred';
771
680
  return {
772
- value: encodeBase64(JSON.stringify(cursorData))
681
+ message,
682
+ code: response?.status?.toString(),
683
+ details: {
684
+ originalResponse: errorBody
685
+ },
773
686
  };
774
687
  }
688
+ }
689
+ /**
690
+ * Main error response parser using Chain of Responsibility pattern
691
+ *
692
+ * This parser standardizes error responses from different UiPath services into a
693
+ * consistent format, regardless of the original error structure.
694
+ *
695
+ * Supported formats:
696
+ * 1. Orchestrator/Task: { message, errorCode, traceId }
697
+ * 2. Entity (Data Fabric): { error, traceId }
698
+ * 3. PIMS/Maestro: { type, title, status, errors?, traceId? }
699
+ * 4. Generic: Fallback for any other format
700
+ *
701
+ * @example
702
+ * const parser = new ErrorResponseParser();
703
+ * const errorInfo = await parser.parse(response);
704
+ * // errorInfo will have consistent structure regardless of service
705
+ */
706
+ class ErrorResponseParser {
707
+ constructor() {
708
+ this.strategies = [
709
+ new OrchestratorErrorParser(),
710
+ new EntityErrorParser(),
711
+ new PimsErrorParser(),
712
+ new GenericErrorParser() // Must be last
713
+ ];
714
+ }
775
715
  /**
776
- * Create a paginated response with navigation cursors
716
+ * Parses error response body into standardized format
717
+ * @param response - The HTTP response object
718
+ * @returns Standardized error information
777
719
  */
778
- static createPaginatedResponse({ pageInfo, type }, items) {
779
- const nextCursor = PaginationManager.createCursor({ pageInfo, type });
780
- // Create previous page cursor if applicable
781
- let previousCursor = undefined;
782
- if (pageInfo.currentPage && pageInfo.currentPage > 1) {
783
- const prevCursorData = {
784
- type,
785
- pageNumber: pageInfo.currentPage - 1,
786
- pageSize: pageInfo.pageSize,
787
- };
788
- previousCursor = {
789
- value: encodeBase64(JSON.stringify(prevCursorData))
790
- };
720
+ async parse(response) {
721
+ try {
722
+ const errorBody = await response.json();
723
+ // Find the first strategy that can parse this error format
724
+ const strategy = this.strategies.find(s => s.canParse(errorBody));
725
+ // GenericErrorParser always returns true, so this will never be null
726
+ return strategy.parse(errorBody, response);
791
727
  }
792
- // Calculate total pages if we have totalCount and pageSize
793
- let totalPages = undefined;
794
- if (pageInfo.totalCount !== undefined && pageInfo.pageSize) {
795
- totalPages = Math.ceil(pageInfo.totalCount / pageInfo.pageSize);
728
+ catch {
729
+ // Handle non-JSON responses
730
+ const responseText = await response.text().catch(() => '');
731
+ return {
732
+ message: response.statusText,
733
+ code: response.status.toString(),
734
+ details: {
735
+ parseError: 'Failed to parse error response as JSON',
736
+ responseText
737
+ },
738
+ requestId: response.headers.get(HttpHeaders.X_REQUEST_ID) || undefined
739
+ };
796
740
  }
797
- // Determine if this pagination type supports page jumping
798
- const supportsPageJump = type === PaginationType.OFFSET;
799
- // Create the result object with all fields, then filter out undefined values
800
- const result = filterUndefined({
801
- items,
802
- totalCount: pageInfo.totalCount,
803
- hasNextPage: pageInfo.hasMore,
804
- nextCursor: nextCursor,
805
- previousCursor: previousCursor,
806
- currentPage: pageInfo.currentPage,
807
- totalPages,
808
- supportsPageJump
809
- });
810
- return result;
811
741
  }
812
742
  }
743
+ // Export singleton instance
744
+ const errorResponseParser = new ErrorResponseParser();
813
745
 
814
746
  /**
815
- * Creates headers object from key-value pairs
816
- * @param headersObj - Object containing header key-value pairs
817
- * @returns Headers object with all values converted to strings
818
- *
819
- * @example
820
- * ```typescript
821
- * // Single header
822
- * const headers = createHeaders({ 'X-UIPATH-FolderKey': '1234567890' });
823
- *
824
- * // Multiple headers
825
- * const headers = createHeaders({
826
- * 'X-UIPATH-FolderKey': '1234567890',
827
- * 'X-UIPATH-OrganizationUnitId': 123,
828
- * 'Accept': 'application/json'
829
- * });
830
- *
831
- * // Using constants
832
- * import { FOLDER_KEY, FOLDER_ID } from '../constants/headers';
833
- * const headers = createHeaders({
834
- * [FOLDER_KEY]: 'abc-123',
835
- * [FOLDER_ID]: 456
836
- * });
837
- *
838
- * // Empty headers
839
- * const headers = createHeaders();
840
- * ```
747
+ * Base error class for all UiPath SDK errors
748
+ * Extends Error for standard error handling compatibility
841
749
  */
842
- function createHeaders(headersObj) {
843
- const headers = {};
844
- for (const [key, value] of Object.entries(headersObj)) {
845
- if (value !== undefined && value !== null) {
846
- headers[key] = value.toString();
750
+ class UiPathError extends Error {
751
+ constructor(type, params) {
752
+ super(params.message);
753
+ this.name = type;
754
+ this.type = type;
755
+ this.statusCode = params.statusCode;
756
+ this.requestId = params.requestId;
757
+ this.timestamp = new Date();
758
+ // Maintains proper stack trace for where our error was thrown
759
+ if (Error.captureStackTrace) {
760
+ Error.captureStackTrace(this, this.constructor);
847
761
  }
848
762
  }
849
- return headers;
763
+ /**
764
+ * Returns a clean JSON representation of the error
765
+ */
766
+ toJSON() {
767
+ return {
768
+ type: this.type,
769
+ message: this.message,
770
+ statusCode: this.statusCode,
771
+ requestId: this.requestId,
772
+ timestamp: this.timestamp
773
+ };
774
+ }
775
+ /**
776
+ * Returns detailed debug information including stack trace
777
+ */
778
+ getDebugInfo() {
779
+ return {
780
+ ...this.toJSON(),
781
+ stack: this.stack
782
+ };
783
+ }
850
784
  }
851
785
 
852
786
  /**
853
- * Common constants used across the SDK
787
+ * Error thrown when authentication fails (401 errors)
788
+ * Common scenarios:
789
+ * - Invalid credentials
790
+ * - Expired token
791
+ * - Missing authentication
854
792
  */
793
+ class AuthenticationError extends UiPathError {
794
+ constructor(params = {}) {
795
+ super(ErrorType.AUTHENTICATION, {
796
+ message: params.message || ErrorMessages.AUTHENTICATION_FAILED,
797
+ statusCode: params.statusCode ?? HttpStatus.UNAUTHORIZED,
798
+ requestId: params.requestId
799
+ });
800
+ }
801
+ }
802
+
855
803
  /**
856
- * Prefix used for OData query parameters
804
+ * Error thrown when authorization fails (403 errors)
805
+ * Common scenarios:
806
+ * - Insufficient permissions
807
+ * - Access denied to resource
808
+ * - Invalid scope
857
809
  */
858
- const ODATA_PREFIX = '$';
859
- const UNKNOWN$1 = 'Unknown';
860
- const NO_INSTANCE = 'no-instance';
810
+ class AuthorizationError extends UiPathError {
811
+ constructor(params = {}) {
812
+ super(ErrorType.AUTHORIZATION, {
813
+ message: params.message || ErrorMessages.ACCESS_DENIED,
814
+ statusCode: params.statusCode ?? HttpStatus.FORBIDDEN,
815
+ requestId: params.requestId
816
+ });
817
+ }
818
+ }
819
+
861
820
  /**
862
- * HTTP methods
821
+ * Error thrown when validation fails (400 errors or client-side validation)
822
+ * Common scenarios:
823
+ * - Invalid input parameters
824
+ * - Missing required fields
825
+ * - Invalid data format
863
826
  */
864
- const HTTP_METHODS = {
865
- GET: 'GET',
866
- POST: 'POST'};
827
+ class ValidationError extends UiPathError {
828
+ constructor(params = {}) {
829
+ super(ErrorType.VALIDATION, {
830
+ message: params.message || ErrorMessages.VALIDATION_FAILED,
831
+ statusCode: params.statusCode ?? HttpStatus.BAD_REQUEST,
832
+ requestId: params.requestId
833
+ });
834
+ }
835
+ }
836
+
867
837
  /**
868
- * Process Instance pagination constants for token-based pagination
838
+ * Error thrown when a resource is not found (404 errors)
839
+ * Common scenarios:
840
+ * - Resource doesn't exist
841
+ * - Invalid ID provided
842
+ * - Resource deleted
869
843
  */
870
- const PROCESS_INSTANCE_PAGINATION = {
871
- /** Field name for items in process instance response */
872
- ITEMS_FIELD: 'instances',
873
- /** Field name for continuation token in process instance response */
874
- CONTINUATION_TOKEN_FIELD: 'nextPage'
875
- };
876
- /**
877
- * OData OFFSET pagination parameter names (ODATA-style)
878
- */
879
- const ODATA_OFFSET_PARAMS = {
880
- /** OData page size parameter name */
881
- PAGE_SIZE_PARAM: '$top',
882
- /** OData offset parameter name */
883
- OFFSET_PARAM: '$skip',
884
- /** OData count parameter name */
885
- COUNT_PARAM: '$count'
886
- };
887
- /**
888
- * Bucket TOKEN pagination parameter names
889
- */
890
- const BUCKET_TOKEN_PARAMS = {
891
- /** Bucket page size parameter name */
892
- PAGE_SIZE_PARAM: 'takeHint',
893
- /** Bucket token parameter name */
894
- TOKEN_PARAM: 'continuationToken'
895
- };
896
- /**
897
- * Process Instance TOKEN pagination parameter names
898
- */
899
- const PROCESS_INSTANCE_TOKEN_PARAMS = {
900
- /** Process instance page size parameter name */
901
- PAGE_SIZE_PARAM: 'pageSize',
902
- /** Process instance token parameter name */
903
- TOKEN_PARAM: 'nextPage'
904
- };
844
+ class NotFoundError extends UiPathError {
845
+ constructor(params = {}) {
846
+ super(ErrorType.NOT_FOUND, {
847
+ message: params.message || ErrorMessages.RESOURCE_NOT_FOUND,
848
+ statusCode: params.statusCode ?? HttpStatus.NOT_FOUND,
849
+ requestId: params.requestId
850
+ });
851
+ }
852
+ }
905
853
 
906
854
  /**
907
- * Transforms data by mapping fields according to the provided field mapping
908
- * @param data The source data to transform
909
- * @param fieldMapping Object mapping source field names to target field names
910
- * @returns Transformed data with mapped field names
911
- *
912
- * @example
913
- * ```typescript
914
- * // Single object transformation
915
- * const data = { id: '123', userName: 'john' };
916
- * const mapping = { id: 'userId', userName: 'name' };
917
- * const result = transformData(data, mapping);
918
- * // result = { userId: '123', name: 'john' }
919
- *
920
- * // Array transformation
921
- * const dataArray = [
922
- * { id: '123', userName: 'john' },
923
- * { id: '456', userName: 'jane' }
924
- * ];
925
- * const result = transformData(dataArray, mapping);
926
- * // result = [
927
- * // { userId: '123', name: 'john' },
928
- * // { userId: '456', name: 'jane' }
929
- * // ]
930
- * ```
855
+ * Error thrown when rate limit is exceeded (429 errors)
856
+ * Common scenarios:
857
+ * - Too many requests in a time window
858
+ * - API throttling
931
859
  */
932
- function transformData(data, fieldMapping) {
933
- // Handle array of objects
934
- if (Array.isArray(data)) {
935
- return data.map(item => transformData(item, fieldMapping));
936
- }
937
- // Handle single object
938
- const result = { ...data };
939
- for (const [sourceField, targetField] of Object.entries(fieldMapping)) {
940
- if (sourceField in result) {
941
- const value = result[sourceField];
942
- delete result[sourceField];
943
- result[targetField] = value;
944
- }
860
+ class RateLimitError extends UiPathError {
861
+ constructor(params = {}) {
862
+ super(ErrorType.RATE_LIMIT, {
863
+ message: params.message || ErrorMessages.RATE_LIMIT_EXCEEDED,
864
+ statusCode: params.statusCode ?? HttpStatus.TOO_MANY_REQUESTS,
865
+ requestId: params.requestId
866
+ });
945
867
  }
946
- return result;
947
868
  }
869
+
948
870
  /**
949
- * Adds a prefix to specified keys in an object, returning a new object.
950
- * Only the provided keys are prefixed; all others are left unchanged.
951
- *
952
- * @param obj The source object
953
- * @param prefix The prefix to add (e.g., '$')
954
- * @param keys The keys to prefix (e.g., ['expand', 'filter'])
955
- * @returns A new object with specified keys prefixed
956
- *
957
- * @example
958
- * addPrefixToKeys({ expand: 'a', foo: 1 }, '$', ['expand']) // { $expand: 'a', foo: 1 }
871
+ * Error thrown when server encounters an error (5xx errors)
872
+ * Common scenarios:
873
+ * - Internal server error
874
+ * - Service unavailable
875
+ * - Gateway timeout
959
876
  */
960
- function addPrefixToKeys(obj, prefix, keys) {
961
- const result = {};
962
- for (const [key, value] of Object.entries(obj)) {
963
- if (keys.includes(key)) {
964
- result[`${prefix}${key}`] = value;
965
- }
966
- else {
967
- result[key] = value;
968
- }
877
+ class ServerError extends UiPathError {
878
+ constructor(params = {}) {
879
+ super(ErrorType.SERVER, {
880
+ message: params.message || ErrorMessages.INTERNAL_SERVER_ERROR,
881
+ statusCode: params.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR,
882
+ requestId: params.requestId
883
+ });
884
+ }
885
+ /**
886
+ * Checks if this is a temporary error that might succeed on retry
887
+ */
888
+ get isRetryable() {
889
+ return this.statusCode === HttpStatus.BAD_GATEWAY ||
890
+ this.statusCode === HttpStatus.SERVICE_UNAVAILABLE ||
891
+ this.statusCode === HttpStatus.GATEWAY_TIMEOUT;
969
892
  }
970
- return result;
971
893
  }
972
894
 
973
895
  /**
974
- * Constants used throughout the pagination system
975
- */
976
- /** Maximum number of items that can be requested in a single page */
977
- const MAX_PAGE_SIZE = 1000;
978
- /** Default page size when jumpToPage is used without specifying pageSize */
979
- const DEFAULT_PAGE_SIZE = 50;
980
- /** Default field name for items in a paginated response */
981
- const DEFAULT_ITEMS_FIELD = 'value';
982
- /** Default field name for total count in a paginated response */
983
- const DEFAULT_TOTAL_COUNT_FIELD = '@odata.count';
984
- /**
985
- * Limits the page size to the maximum allowed value
986
- * @param pageSize - Requested page size
987
- * @returns Limited page size value
896
+ * Error thrown when network/connection issues occur
897
+ * Common scenarios:
898
+ * - Connection timeout
899
+ * - DNS resolution failure
900
+ * - Network unreachable
901
+ * - Request aborted
988
902
  */
989
- function getLimitedPageSize(pageSize) {
990
- if (pageSize === undefined || pageSize === null) {
991
- return DEFAULT_PAGE_SIZE;
903
+ class NetworkError extends UiPathError {
904
+ constructor(params = {}) {
905
+ super(ErrorType.NETWORK, {
906
+ message: params.message || ErrorMessages.NETWORK_ERROR,
907
+ statusCode: params.statusCode, // Network errors typically don't have HTTP status codes
908
+ requestId: params.requestId
909
+ });
992
910
  }
993
- return Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
994
911
  }
995
912
 
996
913
  /**
997
- * Helper functions for pagination that can be used across services
914
+ * Factory for creating typed errors based on HTTP status codes
915
+ * Follows the Factory pattern for clean error instantiation
998
916
  */
999
- class PaginationHelpers {
1000
- /**
1001
- * Checks if any pagination parameters are provided
1002
- *
1003
- * @param options - The options object to check
1004
- * @returns True if any pagination parameter is defined, false otherwise
1005
- */
1006
- static hasPaginationParameters(options = {}) {
1007
- const { cursor, pageSize, jumpToPage } = options;
1008
- return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined;
1009
- }
917
+ class ErrorFactory {
1010
918
  /**
1011
- * Parse a pagination cursor string into cursor data
919
+ * Creates appropriate error instance based on HTTP status code
1012
920
  */
1013
- static parseCursor(cursorString) {
1014
- try {
1015
- const cursorData = JSON.parse(decodeBase64(cursorString));
1016
- return cursorData;
1017
- }
1018
- catch {
1019
- throw new Error('Invalid pagination cursor');
921
+ static createFromHttpStatus(statusCode, errorInfo) {
922
+ const { message, requestId } = errorInfo;
923
+ // Map status codes to error types
924
+ switch (statusCode) {
925
+ case HttpStatus.BAD_REQUEST:
926
+ return new ValidationError({ message, statusCode, requestId });
927
+ case HttpStatus.UNAUTHORIZED:
928
+ return new AuthenticationError({ message, statusCode, requestId });
929
+ case HttpStatus.FORBIDDEN:
930
+ return new AuthorizationError({ message, statusCode, requestId });
931
+ case HttpStatus.NOT_FOUND:
932
+ return new NotFoundError({ message, statusCode, requestId });
933
+ case HttpStatus.TOO_MANY_REQUESTS:
934
+ return new RateLimitError({ message, statusCode, requestId });
935
+ default:
936
+ // For 5xx errors or any other status code
937
+ if (statusCode >= HttpStatus.INTERNAL_SERVER_ERROR) {
938
+ return new ServerError({ message, statusCode, requestId });
939
+ }
940
+ // For unknown client errors, treat as validation error
941
+ return new ValidationError({
942
+ message: `${message} (HTTP ${statusCode})`,
943
+ statusCode,
944
+ requestId
945
+ });
1020
946
  }
1021
947
  }
1022
948
  /**
1023
- * Validates cursor format and structure
1024
- *
1025
- * @param paginationOptions - The pagination options containing the cursor
1026
- * @param paginationType - Optional pagination type to validate against
949
+ * Creates a NetworkError from a fetch/network error
1027
950
  */
1028
- static validateCursor(paginationOptions, paginationType) {
1029
- if (paginationOptions.cursor !== undefined) {
1030
- if (!paginationOptions.cursor || typeof paginationOptions.cursor.value !== 'string' || !paginationOptions.cursor.value) {
1031
- throw new Error('cursor must contain a valid cursor string');
951
+ static createNetworkError(error) {
952
+ let message = ErrorMessages.NETWORK_ERROR;
953
+ if (error instanceof Error) {
954
+ if (error.name === ErrorNames.ABORT_ERROR) {
955
+ message = ErrorMessages.REQUEST_ABORTED;
1032
956
  }
1033
- try {
1034
- // Try to parse the cursor to validate it
1035
- const cursorData = PaginationHelpers.parseCursor(paginationOptions.cursor.value);
1036
- // If type is provided, validate cursor contains expected type information
1037
- if (paginationType) {
1038
- if (!cursorData.type) {
1039
- throw new Error('Invalid cursor: missing pagination type');
1040
- }
1041
- // Check pagination type compatibility
1042
- if (cursorData.type !== paginationType) {
1043
- throw new Error(`Pagination type mismatch: cursor is for ${cursorData.type} but service uses ${paginationType}`);
1044
- }
1045
- }
957
+ else if (error.message.includes('timeout')) {
958
+ message = ErrorMessages.REQUEST_TIMEOUT;
1046
959
  }
1047
- catch (error) {
1048
- if (error instanceof Error) {
1049
- // If it's already our error with specific message, pass it through
1050
- if (error.message.startsWith('Invalid cursor') ||
1051
- error.message.startsWith('Pagination type mismatch')) {
1052
- throw error;
1053
- }
1054
- }
1055
- throw new Error('Invalid pagination cursor format');
960
+ else {
961
+ message = error.message;
1056
962
  }
1057
963
  }
964
+ return new NetworkError({ message });
1058
965
  }
1059
- /**
1060
- * Comprehensive validation for pagination options
966
+ }
967
+
968
+ class ApiClient {
969
+ constructor(config, executionContext, tokenManager, clientConfig = {}) {
970
+ this.config = config;
971
+ this.executionContext = executionContext;
972
+ this.clientConfig = clientConfig;
973
+ this.tokenManager = tokenManager;
974
+ }
975
+ /**
976
+ * Gets a valid authentication token, refreshing if necessary.
977
+ * Used internally for API requests and exposed for services that need manual auth headers.
1061
978
  *
1062
- * @param options - The pagination options to validate
1063
- * @param paginationType - The pagination type these options will be used with
1064
- * @returns Processed pagination parameters ready for use
979
+ * @returns The valid token
980
+ * @throws AuthenticationError if no token available or refresh fails
1065
981
  */
1066
- static validatePaginationOptions(options, paginationType) {
1067
- // Validate pageSize
1068
- if (options.pageSize !== undefined && options.pageSize <= 0) {
1069
- throw new Error('pageSize must be a positive number');
982
+ async getValidToken() {
983
+ return this.tokenManager.getValidToken();
984
+ }
985
+ async getDefaultHeaders() {
986
+ const token = await this.getValidToken();
987
+ return {
988
+ 'Authorization': `Bearer ${token}`,
989
+ 'Content-Type': CONTENT_TYPES.JSON,
990
+ ...this.clientConfig.headers
991
+ };
992
+ }
993
+ async request(method, path, options = {}) {
994
+ // Ensure path starts with a forward slash
995
+ const normalizedPath = path.startsWith('/') ? path.substring(1) : path;
996
+ // Construct URL with org and tenant names
997
+ const url = new URL(`${this.config.orgName}/${this.config.tenantName}/${normalizedPath}`, this.config.baseUrl).toString();
998
+ const isFormData = options.body instanceof FormData;
999
+ const defaultHeaders = await this.getDefaultHeaders();
1000
+ if (isFormData) {
1001
+ delete defaultHeaders['Content-Type'];
1070
1002
  }
1071
- // Validate jumpToPage
1072
- if (options.jumpToPage !== undefined && options.jumpToPage <= 0) {
1073
- throw new Error('jumpToPage must be a positive number');
1003
+ const traceId = crypto.randomUUID().replace(/-/g, '');
1004
+ const spanId = crypto.randomUUID().replace(/-/g, '').slice(0, 16);
1005
+ const traceparentValue = `00-${traceId}-${spanId}-01`;
1006
+ const headers = {
1007
+ ...defaultHeaders,
1008
+ [TRACEPARENT]: traceparentValue,
1009
+ [UIPATH_TRACEPARENT_ID]: traceparentValue,
1010
+ ...options.headers
1011
+ };
1012
+ // Convert params to URLSearchParams
1013
+ const searchParams = new URLSearchParams();
1014
+ if (options.params) {
1015
+ Object.entries(options.params).forEach(([key, value]) => {
1016
+ searchParams.append(key, value.toString());
1017
+ });
1074
1018
  }
1075
- // Validate cursor
1076
- PaginationHelpers.validateCursor(options, paginationType);
1077
- // Validate service compatibility
1078
- if (options.jumpToPage !== undefined && paginationType === PaginationType.TOKEN) {
1079
- throw new Error('jumpToPage is not supported for token-based pagination. Use cursor-based navigation instead.');
1019
+ const fullUrl = searchParams.toString() ? `${url}?${searchParams.toString()}` : url;
1020
+ let body = undefined;
1021
+ if (options.body) {
1022
+ body = isFormData ? options.body : JSON.stringify(options.body);
1080
1023
  }
1081
- // Get processed parameters
1082
- return PaginationHelpers.getRequestParameters(options, paginationType);
1083
- }
1084
- /**
1085
- * Convert a unified pagination options to service-specific parameters
1086
- */
1087
- static getRequestParameters(options, paginationType) {
1088
- // Handle jumpToPage
1089
- if (options.jumpToPage !== undefined) {
1090
- const jumpToPageOptions = {
1091
- pageSize: options.pageSize,
1092
- pageNumber: options.jumpToPage
1093
- };
1094
- return filterUndefined(jumpToPageOptions);
1024
+ try {
1025
+ const response = await fetch(fullUrl, {
1026
+ method,
1027
+ headers,
1028
+ body,
1029
+ signal: options.signal
1030
+ });
1031
+ if (!response.ok) {
1032
+ const errorInfo = await errorResponseParser.parse(response);
1033
+ throw ErrorFactory.createFromHttpStatus(response.status, errorInfo);
1034
+ }
1035
+ if (response.status === 204) {
1036
+ return undefined;
1037
+ }
1038
+ // Handle blob response type for binary data (e.g., file downloads)
1039
+ if (options.responseType === RESPONSE_TYPES.BLOB) {
1040
+ const blob = await response.blob();
1041
+ return blob;
1042
+ }
1043
+ // Check if we're expecting XML
1044
+ const acceptHeader = headers['Accept'] || headers['accept'];
1045
+ if (acceptHeader === CONTENT_TYPES.XML) {
1046
+ const text = await response.text();
1047
+ return text;
1048
+ }
1049
+ const text = await response.text();
1050
+ if (!text) {
1051
+ return undefined;
1052
+ }
1053
+ return JSON.parse(text);
1095
1054
  }
1096
- // If no cursor is provided, it's a first page request
1097
- if (!options.cursor) {
1098
- const firstPageOptions = {
1099
- pageSize: options.pageSize,
1100
- // Only set pageNumber for OFFSET pagination
1101
- pageNumber: paginationType === PaginationType.OFFSET ? 1 : undefined
1102
- };
1103
- return filterUndefined(firstPageOptions);
1055
+ catch (error) {
1056
+ // If it's already one of our errors, re-throw it
1057
+ if (error.type && error.type.includes('Error')) {
1058
+ throw error;
1059
+ }
1060
+ // Otherwise, it's likely a network error
1061
+ throw ErrorFactory.createNetworkError(error);
1104
1062
  }
1105
- // Parse the cursor
1106
- try {
1107
- const cursorData = PaginationHelpers.parseCursor(options.cursor.value);
1108
- const cursorBasedOptions = {
1109
- pageSize: cursorData.pageSize || options.pageSize,
1110
- pageNumber: cursorData.pageNumber,
1111
- continuationToken: cursorData.continuationToken,
1112
- type: cursorData.type,
1113
- };
1114
- return filterUndefined(cursorBasedOptions);
1063
+ }
1064
+ async get(path, options = {}) {
1065
+ return this.request('GET', path, options);
1066
+ }
1067
+ async post(path, data, options = {}) {
1068
+ return this.request('POST', path, { ...options, body: data });
1069
+ }
1070
+ async put(path, data, options = {}) {
1071
+ return this.request('PUT', path, { ...options, body: data });
1072
+ }
1073
+ async patch(path, data, options = {}) {
1074
+ return this.request('PATCH', path, { ...options, body: data });
1075
+ }
1076
+ async delete(path, options = {}) {
1077
+ return this.request('DELETE', path, options);
1078
+ }
1079
+ }
1080
+
1081
+ /**
1082
+ * Pagination types supported by the SDK
1083
+ */
1084
+ var PaginationType;
1085
+ (function (PaginationType) {
1086
+ PaginationType["OFFSET"] = "offset";
1087
+ PaginationType["TOKEN"] = "token";
1088
+ })(PaginationType || (PaginationType = {}));
1089
+
1090
+ /**
1091
+ * Collection of utility functions for working with objects
1092
+ */
1093
+ /**
1094
+ * Resolves a field value from an object, supporting both direct keys (e.g., '@odata.count')
1095
+ * and dot-separated nested paths (e.g., 'pagination.totalCount').
1096
+ * Direct key match takes priority over nested traversal.
1097
+ */
1098
+ function resolveNestedField(data, fieldPath) {
1099
+ if (!data) {
1100
+ return undefined;
1101
+ }
1102
+ if (fieldPath in data) {
1103
+ return data[fieldPath];
1104
+ }
1105
+ if (!fieldPath.includes('.')) {
1106
+ return undefined;
1107
+ }
1108
+ let value = data;
1109
+ for (const part of fieldPath.split('.')) {
1110
+ value = value?.[part];
1111
+ }
1112
+ return value;
1113
+ }
1114
+ /**
1115
+ * Filters out undefined values from an object
1116
+ * @param obj The source object
1117
+ * @returns A new object without undefined values
1118
+ *
1119
+ * @example
1120
+ * ```typescript
1121
+ * // Object with undefined values
1122
+ * const options = {
1123
+ * name: 'test',
1124
+ * count: 5,
1125
+ * prefix: undefined,
1126
+ * suffix: null
1127
+ * };
1128
+ * const result = filterUndefined(options);
1129
+ * // result = { name: 'test', count: 5, suffix: null }
1130
+ * ```
1131
+ */
1132
+ function filterUndefined(obj) {
1133
+ const result = {};
1134
+ for (const [key, value] of Object.entries(obj)) {
1135
+ if (value !== undefined) {
1136
+ result[key] = value;
1115
1137
  }
1116
- catch {
1117
- throw new Error('Invalid pagination cursor');
1138
+ }
1139
+ return result;
1140
+ }
1141
+
1142
+ /**
1143
+ * Utility functions for platform detection
1144
+ */
1145
+ /**
1146
+ * Checks if code is running in a browser environment
1147
+ */
1148
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
1149
+ isBrowser && window.self != window.top && window.location.href.includes('source=ActionCenter');
1150
+
1151
+ /**
1152
+ * Base64 encoding/decoding
1153
+ */
1154
+ /**
1155
+ * Encodes a string to base64
1156
+ * @param str - The string to encode
1157
+ * @returns Base64 encoded string
1158
+ */
1159
+ function encodeBase64(str) {
1160
+ // TextEncoder for UTF-8 encoding (works in both browser and Node.js)
1161
+ const encoder = new TextEncoder();
1162
+ const data = encoder.encode(str);
1163
+ // Convert Uint8Array to base64
1164
+ if (isBrowser) {
1165
+ // Browser environment
1166
+ // Convert Uint8Array to binary string then to base64
1167
+ const binaryString = Array.from(data, byte => String.fromCharCode(byte)).join('');
1168
+ return btoa(binaryString);
1169
+ }
1170
+ else {
1171
+ // Node.js environment
1172
+ return Buffer.from(data).toString('base64');
1173
+ }
1174
+ }
1175
+ /**
1176
+ * Decodes a base64 string
1177
+ * @param base64 - The base64 string to decode
1178
+ * @returns Decoded string
1179
+ */
1180
+ function decodeBase64(base64) {
1181
+ let bytes;
1182
+ if (isBrowser) {
1183
+ // Browser environment
1184
+ const binaryString = atob(base64);
1185
+ bytes = new Uint8Array(binaryString.length);
1186
+ for (let i = 0; i < binaryString.length; i++) {
1187
+ bytes[i] = binaryString.charCodeAt(i);
1118
1188
  }
1119
1189
  }
1120
- /**
1121
- * Helper method for paginated resource retrieval
1122
- *
1123
- * @param params - Parameters for pagination
1124
- * @returns Promise resolving to a paginated result
1125
- */
1126
- static async getAllPaginated(params) {
1127
- const { serviceAccess, getEndpoint, folderId, paginationParams, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1128
- const endpoint = getEndpoint(folderId);
1129
- const headers = folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {};
1130
- const paginatedResponse = await serviceAccess.requestWithPagination(method, endpoint, paginationParams, {
1131
- headers,
1132
- params: additionalParams,
1133
- pagination: {
1134
- paginationType: options.paginationType || PaginationType.OFFSET,
1135
- itemsField: options.itemsField || DEFAULT_ITEMS_FIELD,
1136
- totalCountField: options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD,
1137
- continuationTokenField: options.continuationTokenField,
1138
- paginationParams: options.paginationParams
1139
- }
1140
- });
1141
- // Parse items - automatically handle JSON string responses
1142
- const rawItems = paginatedResponse.items;
1143
- const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1144
- const transformedItems = transformFn ? parsedItems.map(transformFn) : parsedItems;
1145
- return {
1146
- ...paginatedResponse,
1147
- items: transformedItems
1148
- };
1190
+ else {
1191
+ // Node.js environment
1192
+ bytes = new Uint8Array(Buffer.from(base64, 'base64'));
1149
1193
  }
1194
+ // TextDecoder for UTF-8 decoding (works in both browser and Node.js)
1195
+ const decoder = new TextDecoder();
1196
+ return decoder.decode(bytes);
1197
+ }
1198
+
1199
+ /**
1200
+ * PaginationManager handles the conversion between uniform cursor-based pagination
1201
+ * and the specific pagination type for each service
1202
+ */
1203
+ class PaginationManager {
1150
1204
  /**
1151
- * Helper method for non-paginated resource retrieval
1152
- *
1153
- * @param params - Parameters for non-paginated resource retrieval
1154
- * @returns Promise resolving to an object with data and totalCount
1205
+ * Create a pagination cursor for subsequent page requests
1155
1206
  */
1156
- static async getAllNonPaginated(params) {
1157
- const { serviceAccess, getAllEndpoint, getByFolderEndpoint, folderId, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1158
- // Set default field names
1159
- const itemsField = options.itemsField || DEFAULT_ITEMS_FIELD;
1160
- const totalCountField = options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD;
1161
- // Determine endpoint and headers based on folderId
1162
- const endpoint = folderId ? getByFolderEndpoint : getAllEndpoint;
1163
- const headers = folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {};
1164
- // Make the API call based on method
1165
- let response;
1166
- if (method === HTTP_METHODS.POST) {
1167
- response = await serviceAccess.post(endpoint, additionalParams, { headers });
1207
+ static createCursor({ pageInfo, type }) {
1208
+ if (!pageInfo.hasMore) {
1209
+ return undefined;
1168
1210
  }
1169
- else {
1170
- response = await serviceAccess.get(endpoint, {
1171
- params: additionalParams,
1172
- headers
1173
- });
1211
+ const cursorData = {
1212
+ type,
1213
+ pageSize: pageInfo.pageSize,
1214
+ };
1215
+ switch (type) {
1216
+ case PaginationType.OFFSET:
1217
+ if (pageInfo.currentPage) {
1218
+ cursorData.pageNumber = pageInfo.currentPage + 1;
1219
+ }
1220
+ break;
1221
+ case PaginationType.TOKEN:
1222
+ if (pageInfo.continuationToken) {
1223
+ cursorData.continuationToken = pageInfo.continuationToken;
1224
+ }
1225
+ else {
1226
+ return undefined; // No continuation token, can't continue
1227
+ }
1228
+ break;
1174
1229
  }
1175
- // Extract and transform items from response
1176
- // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1177
- const rawItems = Array.isArray(response.data) ? response.data : response.data?.[itemsField];
1178
- const totalCount = Array.isArray(response.data) ? undefined : response.data?.[totalCountField];
1179
- // Parse items - automatically handle JSON string responses
1180
- const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1181
- const items = transformFn ? parsedItems.map(transformFn) : parsedItems;
1182
1230
  return {
1183
- items,
1184
- totalCount
1231
+ value: encodeBase64(JSON.stringify(cursorData))
1185
1232
  };
1186
1233
  }
1187
1234
  /**
1188
- * Centralized getAll implementation that handles both paginated and non-paginated requests
1189
- *
1190
- * @param config - Configuration for the getAll operation
1191
- * @param options - Request options including pagination parameters
1192
- * @returns Promise resolving to either paginated or non-paginated response based on options
1235
+ * Create a paginated response with navigation cursors
1193
1236
  */
1194
- static async getAll(config, options) {
1195
- const optionsWithDefaults = options || {};
1196
- const { folderId, pageSize, cursor, jumpToPage, ...restOptions } = optionsWithDefaults;
1197
- // Determine if pagination is requested
1198
- const isPaginationRequested = PaginationHelpers.hasPaginationParameters(options || {});
1199
- // Process parameters (custom processing if provided, otherwise default)
1200
- let processedOptions = restOptions;
1201
- if (config.processParametersFn) {
1202
- processedOptions = config.processParametersFn(restOptions, folderId);
1237
+ static createPaginatedResponse({ pageInfo, type }, items) {
1238
+ const nextCursor = PaginationManager.createCursor({ pageInfo, type });
1239
+ // Create previous page cursor if applicable
1240
+ let previousCursor = undefined;
1241
+ if (pageInfo.currentPage && pageInfo.currentPage > 1) {
1242
+ const prevCursorData = {
1243
+ type,
1244
+ pageNumber: pageInfo.currentPage - 1,
1245
+ pageSize: pageInfo.pageSize,
1246
+ };
1247
+ previousCursor = {
1248
+ value: encodeBase64(JSON.stringify(prevCursorData))
1249
+ };
1203
1250
  }
1204
- // Apply ODATA prefix to keys (excluding specified keys)
1205
- const excludeKeys = config.excludeFromPrefix || [];
1206
- const keysToPrefix = Object.keys(processedOptions).filter(k => !excludeKeys.includes(k));
1207
- const prefixedOptions = addPrefixToKeys(processedOptions, ODATA_PREFIX, keysToPrefix);
1208
- // Default pagination options
1209
- const paginationOptions = {
1210
- paginationType: PaginationType.OFFSET,
1211
- itemsField: DEFAULT_ITEMS_FIELD,
1212
- totalCountField: DEFAULT_TOTAL_COUNT_FIELD,
1213
- ...config.pagination
1214
- };
1215
- // Paginated flow
1216
- if (isPaginationRequested) {
1217
- return PaginationHelpers.getAllPaginated({
1218
- serviceAccess: config.serviceAccess,
1219
- getEndpoint: config.getEndpoint,
1220
- folderId,
1221
- paginationParams: cursor ? { cursor, pageSize } : jumpToPage ? { jumpToPage, pageSize } : { pageSize },
1222
- additionalParams: prefixedOptions,
1223
- transformFn: config.transformFn,
1224
- method: config.method,
1225
- options: {
1226
- ...paginationOptions,
1227
- paginationParams: config.pagination?.paginationParams
1228
- }
1229
- }); // Type assertion needed due to conditional return
1251
+ // Calculate total pages if we have totalCount and pageSize
1252
+ let totalPages = undefined;
1253
+ if (pageInfo.totalCount !== undefined && pageInfo.pageSize) {
1254
+ totalPages = Math.ceil(pageInfo.totalCount / pageInfo.pageSize);
1230
1255
  }
1231
- // Non-paginated flow
1232
- const byFolderEndpoint = config.getByFolderEndpoint || config.getEndpoint(folderId);
1233
- return PaginationHelpers.getAllNonPaginated({
1234
- serviceAccess: config.serviceAccess,
1235
- getAllEndpoint: config.getEndpoint(),
1236
- getByFolderEndpoint: byFolderEndpoint,
1237
- folderId,
1238
- additionalParams: prefixedOptions,
1239
- transformFn: config.transformFn,
1240
- method: config.method,
1241
- options: {
1242
- itemsField: paginationOptions.itemsField,
1243
- totalCountField: paginationOptions.totalCountField
1244
- }
1256
+ // Determine if this pagination type supports page jumping
1257
+ const supportsPageJump = type === PaginationType.OFFSET;
1258
+ // Create the result object with all fields, then filter out undefined values
1259
+ const result = filterUndefined({
1260
+ items,
1261
+ totalCount: pageInfo.totalCount,
1262
+ hasNextPage: pageInfo.hasMore,
1263
+ nextCursor: nextCursor,
1264
+ previousCursor: previousCursor,
1265
+ currentPage: pageInfo.currentPage,
1266
+ totalPages,
1267
+ supportsPageJump
1245
1268
  });
1269
+ return result;
1246
1270
  }
1247
1271
  }
1248
1272
 
1249
1273
  /**
1250
- * SDK Internals Registry - Internal registry for SDK instances
1251
- *
1252
- * This class is NOT exported in the public API.
1253
- * It provides a secure way to share SDK internals between
1254
- * the UiPath class and service classes without exposing them publicly.
1255
- *
1256
- * @internal
1274
+ * Constants used throughout the pagination system
1257
1275
  */
1258
- // Global symbol key to ensure WeakMap is shared across module instances
1259
- // This prevents issues when core and service modules are bundled separately
1260
- const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
1261
- // Get or create the global WeakMap store
1262
- const getGlobalStore = () => {
1263
- const globalObj = globalThis;
1264
- if (!globalObj[REGISTRY_KEY]) {
1265
- globalObj[REGISTRY_KEY] = new WeakMap();
1276
+ /** Maximum number of items that can be requested in a single page */
1277
+ const MAX_PAGE_SIZE = 1000;
1278
+ /** Default page size when jumpToPage is used without specifying pageSize */
1279
+ const DEFAULT_PAGE_SIZE = 50;
1280
+ /** Default field name for items in a paginated response */
1281
+ const DEFAULT_ITEMS_FIELD = 'value';
1282
+ /** Default field name for total count in a paginated response */
1283
+ const DEFAULT_TOTAL_COUNT_FIELD = '@odata.count';
1284
+ /**
1285
+ * Limits the page size to the maximum allowed value
1286
+ * @param pageSize - Requested page size
1287
+ * @returns Limited page size value
1288
+ */
1289
+ function getLimitedPageSize(pageSize) {
1290
+ if (pageSize === undefined || pageSize === null) {
1291
+ return DEFAULT_PAGE_SIZE;
1266
1292
  }
1267
- return globalObj[REGISTRY_KEY];
1268
- };
1293
+ return Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
1294
+ }
1295
+
1269
1296
  /**
1270
- * Internal registry for SDK private components.
1271
- * Uses WeakMap to prevent memory leaks - entries are automatically
1272
- * garbage collected when the SDK instance is no longer referenced.
1273
- *
1274
- * Uses a global singleton pattern to ensure the same WeakMap is shared
1275
- * across separately bundled modules (core, entities, tasks, etc.).
1276
- *
1277
- * @internal - Not exported in public API
1297
+ * Helper functions for pagination that can be used across services
1278
1298
  */
1279
- class SDKInternalsRegistry {
1280
- // Use global store to ensure sharing across module bundles
1281
- static get store() {
1282
- return getGlobalStore();
1283
- }
1299
+ class PaginationHelpers {
1284
1300
  /**
1285
- * Register SDK instance internals
1286
- * Called by UiPath constructor
1301
+ * Checks if any pagination parameters are provided
1302
+ *
1303
+ * @param options - The options object to check
1304
+ * @returns True if any pagination parameter is defined, false otherwise
1287
1305
  */
1288
- static set(instance, internals) {
1289
- this.store.set(instance, internals);
1306
+ static hasPaginationParameters(options = {}) {
1307
+ const { cursor, pageSize, jumpToPage } = options;
1308
+ return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined;
1290
1309
  }
1291
1310
  /**
1292
- * Retrieve SDK instance internals
1293
- * Called by BaseService constructor
1311
+ * Parse a pagination cursor string into cursor data
1294
1312
  */
1295
- static get(instance) {
1296
- const internals = this.store.get(instance);
1297
- if (!internals) {
1298
- throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
1313
+ static parseCursor(cursorString) {
1314
+ try {
1315
+ const cursorData = JSON.parse(decodeBase64(cursorString));
1316
+ return cursorData;
1317
+ }
1318
+ catch {
1319
+ throw new Error('Invalid pagination cursor');
1299
1320
  }
1300
- return internals;
1301
1321
  }
1302
- }
1303
-
1304
- var _BaseService_apiClient;
1305
- /**
1306
- * Base class for all UiPath SDK services.
1307
- *
1308
- * Provides common functionality for authentication, configuration, and API communication.
1309
- * All service classes extend this base to inherit dependency injection and HTTP client access.
1310
- *
1311
- * This class implements the dependency injection pattern where services receive a configured
1312
- * UiPath instance. The ApiClient is created internally and handles all HTTP operations
1313
- * including authentication token management.
1314
- *
1315
- * @remarks
1316
- * Service classes should extend this base and call `super(uiPath)` in their constructor.
1317
- * Protected HTTP methods (get, post, put, patch, delete) are available to all subclasses.
1318
- *
1319
- */
1320
- class BaseService {
1321
1322
  /**
1322
- * Creates a base service instance with dependency injection.
1323
- *
1324
- * Extracts configuration, execution context, and token manager from the UiPath instance
1325
- * to initialize an authenticated API client. The ApiClient handles all HTTP operations
1326
- * and token management internally.
1327
- *
1328
- * @param instance - UiPath SDK instance providing authentication and configuration.
1329
- * Services receive this via dependency injection in the modular pattern.
1330
- * @param headers - Optional default headers to include in every request (e.g. `x-uipath-external-user-id` for
1331
- * CAS external-app auth)
1332
- *
1333
- * @example
1334
- * ```typescript
1335
- * // Services automatically call this via super()
1336
- * export class EntityService extends BaseService {
1337
- * constructor(instance: IUiPath) {
1338
- * super(instance); // Initializes the internal ApiClient
1339
- * }
1340
- * }
1341
- *
1342
- * // Usage in modular pattern
1343
- * import { UiPath } from '@uipath/uipath-typescript/core';
1344
- * import { Entities } from '@uipath/uipath-typescript/entities';
1323
+ * Validates cursor format and structure
1345
1324
  *
1346
- * const sdk = new UiPath(config);
1347
- * await sdk.initialize();
1348
- * const entities = new Entities(sdk);
1349
- * ```
1325
+ * @param paginationOptions - The pagination options containing the cursor
1326
+ * @param paginationType - Optional pagination type to validate against
1350
1327
  */
1351
- constructor(instance, headers) {
1352
- // Private field - not visible via Object.keys() or any reflection
1353
- _BaseService_apiClient.set(this, void 0);
1354
- const { config, context, tokenManager, folderKey } = SDKInternalsRegistry.get(instance);
1355
- __classPrivateFieldSet(this, _BaseService_apiClient, new ApiClient(config, context, tokenManager, headers ? { headers } : {}), "f");
1356
- this.config = { folderKey };
1328
+ static validateCursor(paginationOptions, paginationType) {
1329
+ if (paginationOptions.cursor !== undefined) {
1330
+ if (!paginationOptions.cursor || typeof paginationOptions.cursor.value !== 'string' || !paginationOptions.cursor.value) {
1331
+ throw new Error('cursor must contain a valid cursor string');
1332
+ }
1333
+ try {
1334
+ // Try to parse the cursor to validate it
1335
+ const cursorData = PaginationHelpers.parseCursor(paginationOptions.cursor.value);
1336
+ // If type is provided, validate cursor contains expected type information
1337
+ if (paginationType) {
1338
+ if (!cursorData.type) {
1339
+ throw new Error('Invalid cursor: missing pagination type');
1340
+ }
1341
+ // Check pagination type compatibility
1342
+ if (cursorData.type !== paginationType) {
1343
+ throw new Error(`Pagination type mismatch: cursor is for ${cursorData.type} but service uses ${paginationType}`);
1344
+ }
1345
+ }
1346
+ }
1347
+ catch (error) {
1348
+ if (error instanceof Error) {
1349
+ // If it's already our error with specific message, pass it through
1350
+ if (error.message.startsWith('Invalid cursor') ||
1351
+ error.message.startsWith('Pagination type mismatch')) {
1352
+ throw error;
1353
+ }
1354
+ }
1355
+ throw new Error('Invalid pagination cursor format');
1356
+ }
1357
+ }
1357
1358
  }
1358
1359
  /**
1359
- * Gets a valid authentication token, refreshing if necessary.
1360
- * Use this when you need to manually add Authorization headers (e.g., direct uploads).
1360
+ * Comprehensive validation for pagination options
1361
1361
  *
1362
- * @returns Promise resolving to a valid access token string
1363
- * @throws AuthenticationError if no token is available or refresh fails
1364
- */
1365
- async getValidAuthToken() {
1366
- return __classPrivateFieldGet(this, _BaseService_apiClient, "f").getValidToken();
1367
- }
1368
- /**
1369
- * Creates a service accessor for pagination helpers
1370
- * This allows pagination helpers to access protected methods without making them public
1362
+ * @param options - The pagination options to validate
1363
+ * @param paginationType - The pagination type these options will be used with
1364
+ * @returns Processed pagination parameters ready for use
1371
1365
  */
1372
- createPaginationServiceAccess() {
1373
- return {
1374
- get: (path, options) => this.get(path, options || {}),
1375
- post: (path, body, options) => this.post(path, body, options || {}),
1376
- requestWithPagination: (method, path, paginationOptions, options) => this.requestWithPagination(method, path, paginationOptions, options)
1377
- };
1378
- }
1379
- async request(method, path, options = {}) {
1380
- switch (method.toUpperCase()) {
1381
- case 'GET':
1382
- return this.get(path, options);
1383
- case 'POST':
1384
- return this.post(path, options.body, options);
1385
- case 'PUT':
1386
- return this.put(path, options.body, options);
1387
- case 'PATCH':
1388
- return this.patch(path, options.body, options);
1389
- case 'DELETE':
1390
- return this.delete(path, options);
1391
- default:
1392
- throw new Error(`Unsupported HTTP method: ${method}`);
1366
+ static validatePaginationOptions(options, paginationType) {
1367
+ // Validate pageSize
1368
+ if (options.pageSize !== undefined && options.pageSize <= 0) {
1369
+ throw new Error('pageSize must be a positive number');
1393
1370
  }
1394
- }
1395
- async requestWithSpec(spec) {
1396
- if (!spec.method || !spec.url) {
1397
- throw new Error('Request spec must include method and url');
1371
+ // Validate jumpToPage
1372
+ if (options.jumpToPage !== undefined && options.jumpToPage <= 0) {
1373
+ throw new Error('jumpToPage must be a positive number');
1398
1374
  }
1399
- return this.request(spec.method, spec.url, spec);
1400
- }
1401
- async get(path, options = {}) {
1402
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").get(path, options);
1403
- return { data: response };
1404
- }
1405
- async post(path, data, options = {}) {
1406
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").post(path, data, options);
1407
- return { data: response };
1408
- }
1409
- async put(path, data, options = {}) {
1410
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").put(path, data, options);
1411
- return { data: response };
1412
- }
1413
- async patch(path, data, options = {}) {
1414
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").patch(path, data, options);
1415
- return { data: response };
1416
- }
1417
- async delete(path, options = {}) {
1418
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").delete(path, options);
1419
- return { data: response };
1375
+ // Validate cursor
1376
+ PaginationHelpers.validateCursor(options, paginationType);
1377
+ // Validate service compatibility
1378
+ if (options.jumpToPage !== undefined && paginationType === PaginationType.TOKEN) {
1379
+ throw new Error('jumpToPage is not supported for token-based pagination. Use cursor-based navigation instead.');
1380
+ }
1381
+ // Get processed parameters
1382
+ return PaginationHelpers.getRequestParameters(options, paginationType);
1420
1383
  }
1421
1384
  /**
1422
- * Execute a request with cursor-based pagination
1385
+ * Convert a unified pagination options to service-specific parameters
1423
1386
  */
1424
- async requestWithPagination(method, path, paginationOptions, options) {
1425
- const paginationType = options.pagination.paginationType;
1426
- // Validate and prepare pagination parameters
1427
- const params = this.validateAndPreparePaginationParams(paginationType, paginationOptions);
1428
- // Prepare request parameters based on pagination type
1429
- const requestParams = this.preparePaginationRequestParams(paginationType, params, options.pagination);
1430
- // For POST requests, merge pagination params into body and set params to undefined; for GET, use query params
1431
- if (method.toUpperCase() === 'POST') {
1432
- const existingBody = (options.body && typeof options.body === 'object') ? options.body : {};
1433
- options.body = {
1434
- ...existingBody,
1435
- ...options.params,
1436
- ...requestParams
1387
+ static getRequestParameters(options, paginationType) {
1388
+ // Handle jumpToPage
1389
+ if (options.jumpToPage !== undefined) {
1390
+ const jumpToPageOptions = {
1391
+ pageSize: options.pageSize,
1392
+ pageNumber: options.jumpToPage
1437
1393
  };
1438
- options.params = undefined;
1394
+ return filterUndefined(jumpToPageOptions);
1439
1395
  }
1440
- else {
1441
- // Merge pagination parameters with existing parameters
1442
- options.params = {
1443
- ...options.params,
1444
- ...requestParams
1396
+ // If no cursor is provided, it's a first page request
1397
+ if (!options.cursor) {
1398
+ const firstPageOptions = {
1399
+ pageSize: options.pageSize,
1400
+ // Only set pageNumber for OFFSET pagination
1401
+ pageNumber: paginationType === PaginationType.OFFSET ? 1 : undefined
1402
+ };
1403
+ return filterUndefined(firstPageOptions);
1404
+ }
1405
+ // Parse the cursor
1406
+ try {
1407
+ const cursorData = PaginationHelpers.parseCursor(options.cursor.value);
1408
+ const cursorBasedOptions = {
1409
+ pageSize: cursorData.pageSize || options.pageSize,
1410
+ pageNumber: cursorData.pageNumber,
1411
+ continuationToken: cursorData.continuationToken,
1412
+ type: cursorData.type,
1445
1413
  };
1414
+ return filterUndefined(cursorBasedOptions);
1415
+ }
1416
+ catch {
1417
+ throw new Error('Invalid pagination cursor');
1446
1418
  }
1447
- // Make the request
1448
- const response = await this.request(method, path, options);
1449
- // Extract data from the response and create page result
1450
- return this.createPaginatedResponseFromResponse(response, params, paginationType, {
1451
- itemsField: options.pagination.itemsField,
1452
- totalCountField: options.pagination.totalCountField,
1453
- continuationTokenField: options.pagination.continuationTokenField
1454
- });
1455
1419
  }
1456
1420
  /**
1457
- * Validates and prepares pagination parameters from options
1421
+ * Helper method for paginated resource retrieval
1422
+ *
1423
+ * @param params - Parameters for pagination
1424
+ * @returns Promise resolving to a paginated result
1458
1425
  */
1459
- validateAndPreparePaginationParams(paginationType, paginationOptions) {
1460
- return PaginationHelpers.validatePaginationOptions(paginationOptions, paginationType);
1426
+ static async getAllPaginated(params) {
1427
+ const { serviceAccess, getEndpoint, folderId, paginationParams, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1428
+ const endpoint = getEndpoint(folderId);
1429
+ const headers = folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {};
1430
+ const paginatedResponse = await serviceAccess.requestWithPagination(method, endpoint, paginationParams, {
1431
+ headers,
1432
+ params: additionalParams,
1433
+ pagination: {
1434
+ paginationType: options.paginationType || PaginationType.OFFSET,
1435
+ itemsField: options.itemsField || DEFAULT_ITEMS_FIELD,
1436
+ totalCountField: options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD,
1437
+ continuationTokenField: options.continuationTokenField,
1438
+ paginationParams: options.paginationParams
1439
+ }
1440
+ });
1441
+ // Parse items - automatically handle JSON string responses
1442
+ const rawItems = paginatedResponse.items;
1443
+ const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1444
+ const transformedItems = transformFn ? parsedItems.map(transformFn) : parsedItems;
1445
+ return {
1446
+ ...paginatedResponse,
1447
+ items: transformedItems
1448
+ };
1461
1449
  }
1462
1450
  /**
1463
- * Prepares request parameters for pagination based on pagination type
1451
+ * Helper method for non-paginated resource retrieval
1452
+ *
1453
+ * @param params - Parameters for non-paginated resource retrieval
1454
+ * @returns Promise resolving to an object with data and totalCount
1464
1455
  */
1465
- preparePaginationRequestParams(paginationType, params, paginationConfig) {
1466
- const requestParams = {};
1467
- let limitedPageSize;
1468
- const paginationParams = paginationConfig?.paginationParams;
1469
- switch (paginationType) {
1470
- case PaginationType.OFFSET:
1471
- limitedPageSize = getLimitedPageSize(params.pageSize);
1472
- const pageSizeParam = paginationParams?.pageSizeParam || ODATA_OFFSET_PARAMS.PAGE_SIZE_PARAM;
1473
- const offsetParam = paginationParams?.offsetParam || ODATA_OFFSET_PARAMS.OFFSET_PARAM;
1474
- const countParam = paginationParams?.countParam || ODATA_OFFSET_PARAMS.COUNT_PARAM;
1475
- requestParams[pageSizeParam] = limitedPageSize;
1476
- if (params.pageNumber && params.pageNumber > 1) {
1477
- requestParams[offsetParam] = (params.pageNumber - 1) * limitedPageSize;
1478
- }
1479
- {
1480
- requestParams[countParam] = true;
1481
- }
1482
- break;
1483
- case PaginationType.TOKEN:
1484
- const tokenPageSizeParam = paginationParams?.pageSizeParam || BUCKET_TOKEN_PARAMS.PAGE_SIZE_PARAM;
1485
- const tokenParam = paginationParams?.tokenParam || BUCKET_TOKEN_PARAMS.TOKEN_PARAM;
1486
- if (params.pageSize) {
1487
- requestParams[tokenPageSizeParam] = getLimitedPageSize(params.pageSize);
1488
- }
1489
- if (params.continuationToken) {
1490
- requestParams[tokenParam] = params.continuationToken;
1491
- }
1492
- break;
1456
+ static async getAllNonPaginated(params) {
1457
+ const { serviceAccess, getAllEndpoint, getByFolderEndpoint, folderId, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1458
+ // Set default field names
1459
+ const itemsField = options.itemsField || DEFAULT_ITEMS_FIELD;
1460
+ const totalCountField = options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD;
1461
+ // Determine endpoint and headers based on folderId
1462
+ const endpoint = folderId ? getByFolderEndpoint : getAllEndpoint;
1463
+ const headers = folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {};
1464
+ // Make the API call based on method
1465
+ let response;
1466
+ if (method === HTTP_METHODS.POST) {
1467
+ response = await serviceAccess.post(endpoint, additionalParams, { headers });
1493
1468
  }
1494
- return requestParams;
1495
- }
1496
- /**
1497
- * Creates a paginated response from API response
1498
- */
1499
- createPaginatedResponseFromResponse(response, params, paginationType, fields) {
1500
- // Extract fields from response
1501
- const itemsField = fields.itemsField ||
1502
- (paginationType === PaginationType.TOKEN ? 'items' : 'value');
1503
- const totalCountField = fields.totalCountField || 'totalRecordCount';
1504
- const continuationTokenField = fields.continuationTokenField || 'continuationToken';
1505
- // Extract items and metadata
1469
+ else {
1470
+ response = await serviceAccess.get(endpoint, {
1471
+ params: additionalParams,
1472
+ headers
1473
+ });
1474
+ }
1475
+ // Extract and transform items from response
1506
1476
  // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1507
- const items = Array.isArray(response.data) ? response.data : (response.data[itemsField] || []);
1508
- const totalCount = Array.isArray(response.data) ? undefined : response.data[totalCountField];
1509
- const continuationToken = response.data[continuationTokenField];
1510
- // Determine if there are more pages
1511
- const hasMore = this.determineHasMorePages(paginationType, {
1512
- totalCount,
1513
- pageSize: params.pageSize,
1514
- currentPage: params.pageNumber || 1,
1515
- itemsCount: items.length,
1516
- continuationToken
1517
- });
1518
- // Create and return the page result
1519
- return PaginationManager.createPaginatedResponse({
1520
- pageInfo: {
1521
- hasMore,
1522
- totalCount,
1523
- currentPage: params.pageNumber,
1524
- pageSize: params.pageSize,
1525
- continuationToken
1526
- },
1527
- type: paginationType,
1528
- }, items);
1477
+ const rawItems = Array.isArray(response.data) ? response.data : response.data?.[itemsField];
1478
+ const rawTotalCount = Array.isArray(response.data) ? undefined : resolveNestedField(response.data, totalCountField);
1479
+ const totalCount = typeof rawTotalCount === 'number' ? rawTotalCount : undefined;
1480
+ // Parse items - automatically handle JSON string responses
1481
+ const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1482
+ const items = transformFn ? parsedItems.map(transformFn) : parsedItems;
1483
+ return {
1484
+ items,
1485
+ totalCount
1486
+ };
1529
1487
  }
1530
1488
  /**
1531
- * Determines if there are more pages based on pagination type and metadata
1489
+ * Centralized getAll implementation that handles both paginated and non-paginated requests
1490
+ *
1491
+ * @param config - Configuration for the getAll operation
1492
+ * @param options - Request options including pagination parameters
1493
+ * @returns Promise resolving to either paginated or non-paginated response based on options
1532
1494
  */
1533
- determineHasMorePages(paginationType, info) {
1534
- switch (paginationType) {
1535
- case PaginationType.OFFSET:
1536
- const effectivePageSize = info.pageSize ?? DEFAULT_PAGE_SIZE;
1537
- // If totalCount is available, use it for precise calculation
1538
- if (info.totalCount !== undefined) {
1539
- return (info.currentPage * effectivePageSize) < info.totalCount;
1540
- }
1541
- // Fallback when totalCount is not available
1542
- // NOTE: This code path should rarely be executed as the APIs typically return totalCount
1543
- return info.itemsCount === effectivePageSize;
1544
- case PaginationType.TOKEN:
1545
- return !!info.continuationToken;
1546
- default:
1547
- return false;
1495
+ static async getAll(config, options) {
1496
+ const optionsWithDefaults = options || {};
1497
+ const { folderId, pageSize, cursor, jumpToPage, ...restOptions } = optionsWithDefaults;
1498
+ // Determine if pagination is requested
1499
+ const isPaginationRequested = PaginationHelpers.hasPaginationParameters(options || {});
1500
+ // Process parameters (custom processing if provided, otherwise default)
1501
+ let processedOptions = restOptions;
1502
+ if (config.processParametersFn) {
1503
+ processedOptions = config.processParametersFn(restOptions, folderId);
1548
1504
  }
1549
- }
1550
- }
1551
- _BaseService_apiClient = new WeakMap();
1552
-
1553
- /**
1554
- * Base path constants for different services
1555
- */
1556
- const PIMS_BASE = 'pims_';
1557
-
1558
- /**
1559
- * Maestro Service Endpoints
1560
- */
1561
- /**
1562
- * Maestro Process Service Endpoints
1563
- */
1564
- const MAESTRO_ENDPOINTS = {
1565
- PROCESSES: {
1566
- GET_ALL: `${PIMS_BASE}/api/v1/processes/summary`},
1567
- INSTANCES: {
1568
- GET_ALL: `${PIMS_BASE}/api/v1/instances`,
1569
- GET_BY_ID: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}`,
1570
- GET_EXECUTION_HISTORY: (instanceId) => `${PIMS_BASE}/api/v1/spans/${instanceId}`,
1571
- GET_BPMN: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/bpmn`,
1572
- GET_VARIABLES: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/variables`,
1573
- CANCEL: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/cancel`,
1574
- PAUSE: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/pause`,
1575
- RESUME: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/resume`,
1576
- },
1577
- INCIDENTS: {
1578
- GET_ALL: `${PIMS_BASE}/api/v1/incidents/summary`,
1579
- GET_BY_PROCESS: (processKey) => `${PIMS_BASE}/api/v1/incidents/process/${processKey}`,
1580
- GET_BY_INSTANCE: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/incidents`,
1581
- }};
1582
-
1583
- /**
1584
- * Maestro Process Models
1585
- * Model classes for Maestro processes
1586
- */
1587
- /**
1588
- * Creates methods for a process object
1589
- *
1590
- * @param processData - The process data (response from API)
1591
- * @param service - The process service instance
1592
- * @returns Object containing process methods
1593
- */
1594
- function createProcessMethods(processData, service) {
1595
- return {
1596
- async getIncidents() {
1597
- if (!processData.processKey)
1598
- throw new Error('Process key is undefined');
1599
- if (!processData.folderKey)
1600
- throw new Error('Folder key is undefined');
1601
- return service.getIncidents(processData.processKey, processData.folderKey);
1505
+ // Apply ODATA prefix to keys (excluding specified keys)
1506
+ const excludeKeys = config.excludeFromPrefix || [];
1507
+ const keysToPrefix = Object.keys(processedOptions).filter(k => !excludeKeys.includes(k));
1508
+ const prefixedOptions = addPrefixToKeys(processedOptions, ODATA_PREFIX, keysToPrefix);
1509
+ // Default pagination options
1510
+ const paginationOptions = {
1511
+ paginationType: PaginationType.OFFSET,
1512
+ itemsField: DEFAULT_ITEMS_FIELD,
1513
+ totalCountField: DEFAULT_TOTAL_COUNT_FIELD,
1514
+ ...config.pagination
1515
+ };
1516
+ // Paginated flow
1517
+ if (isPaginationRequested) {
1518
+ return PaginationHelpers.getAllPaginated({
1519
+ serviceAccess: config.serviceAccess,
1520
+ getEndpoint: config.getEndpoint,
1521
+ folderId,
1522
+ paginationParams: cursor ? { cursor, pageSize } : jumpToPage ? { jumpToPage, pageSize } : { pageSize },
1523
+ additionalParams: prefixedOptions,
1524
+ transformFn: config.transformFn,
1525
+ method: config.method,
1526
+ options: {
1527
+ ...paginationOptions,
1528
+ paginationParams: config.pagination?.paginationParams
1529
+ }
1530
+ }); // Type assertion needed due to conditional return
1602
1531
  }
1603
- };
1604
- }
1605
- /**
1606
- * Creates an actionable process by combining API process data with operational methods.
1607
- *
1608
- * @param processData - The process data from API
1609
- * @param service - The process service instance
1610
- * @returns A process object with added methods
1611
- */
1612
- function createProcessWithMethods(processData, service) {
1613
- const methods = createProcessMethods(processData, service);
1614
- return Object.assign({}, processData, methods);
1532
+ // Non-paginated flow
1533
+ const byFolderEndpoint = config.getByFolderEndpoint || config.getEndpoint(folderId);
1534
+ return PaginationHelpers.getAllNonPaginated({
1535
+ serviceAccess: config.serviceAccess,
1536
+ getAllEndpoint: config.getEndpoint(),
1537
+ getByFolderEndpoint: byFolderEndpoint,
1538
+ folderId,
1539
+ additionalParams: prefixedOptions,
1540
+ transformFn: config.transformFn,
1541
+ method: config.method,
1542
+ options: {
1543
+ itemsField: paginationOptions.itemsField,
1544
+ totalCountField: paginationOptions.totalCountField
1545
+ }
1546
+ });
1547
+ }
1615
1548
  }
1616
1549
 
1617
1550
  /**
1618
- * Maps fields for Incident entities
1551
+ * SDK Internals Registry - Internal registry for SDK instances
1552
+ *
1553
+ * This class is NOT exported in the public API.
1554
+ * It provides a secure way to share SDK internals between
1555
+ * the UiPath class and service classes without exposing them publicly.
1556
+ *
1557
+ * @internal
1619
1558
  */
1620
- const ProcessIncidentMap = {
1621
- errorTimeUtc: 'errorTime'
1559
+ // Global symbol key to ensure WeakMap is shared across module instances
1560
+ // This prevents issues when core and service modules are bundled separately
1561
+ const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
1562
+ // Get or create the global WeakMap store
1563
+ const getGlobalStore = () => {
1564
+ const globalObj = globalThis;
1565
+ if (!globalObj[REGISTRY_KEY]) {
1566
+ globalObj[REGISTRY_KEY] = new WeakMap();
1567
+ }
1568
+ return globalObj[REGISTRY_KEY];
1622
1569
  };
1623
1570
  /**
1624
- * Maps fields for Incident Summary entities
1571
+ * Internal registry for SDK private components.
1572
+ * Uses WeakMap to prevent memory leaks - entries are automatically
1573
+ * garbage collected when the SDK instance is no longer referenced.
1574
+ *
1575
+ * Uses a global singleton pattern to ensure the same WeakMap is shared
1576
+ * across separately bundled modules (core, entities, tasks, etc.).
1577
+ *
1578
+ * @internal - Not exported in public API
1625
1579
  */
1626
- const ProcessIncidentSummaryMap = {
1627
- firstTimeUtc: 'firstOccuranceTime'
1628
- };
1629
-
1630
- /**
1631
- * Helpers for fetching BPMN XML and extracting element details used to annotate responses
1632
- */
1633
- class BpmnHelpers {
1634
- /**
1635
- * Parse BPMN XML and extract element id → {name,type} used for incidents
1636
- */
1637
- static parseBpmnElementsForIncidents(bpmnXml) {
1638
- const elementInfo = {};
1639
- try {
1640
- // Find <bpmn:...> start tags and capture the element type.
1641
- // Then read 'id' and 'name' attributes from each tag.
1642
- const bpmnOpenTagRegex = /<bpmn:([A-Za-z][\w.-]*)\b[^>]*>/g;
1643
- for (const tagMatch of bpmnXml.matchAll(bpmnOpenTagRegex)) {
1644
- const [fullTag, elementType] = tagMatch;
1645
- // Extract attributes from the current tag text.
1646
- const idMatch = /\bid\s*=\s*"([^"]*)"/.exec(fullTag);
1647
- if (!idMatch) {
1648
- continue;
1649
- }
1650
- const elementId = idMatch[1];
1651
- const nameMatch = /\bname\s*=\s*"([^"]*)"/.exec(fullTag);
1652
- const name = nameMatch ? nameMatch[1] : '';
1653
- // Convert BPMN element type to human-readable format
1654
- const activityType = this.formatActivityTypeForIncidents(elementType);
1655
- const activityName = name || elementId;
1656
- elementInfo[elementId] = {
1657
- type: activityType,
1658
- name: activityName
1659
- };
1660
- }
1661
- }
1662
- catch (error) {
1663
- console.warn('Failed to parse BPMN XML for incidents:', error);
1664
- }
1665
- return elementInfo;
1580
+ class SDKInternalsRegistry {
1581
+ // Use global store to ensure sharing across module bundles
1582
+ static get store() {
1583
+ return getGlobalStore();
1666
1584
  }
1667
1585
  /**
1668
- * Format BPMN element type to human-readable activity type for incidents
1586
+ * Register SDK instance internals
1587
+ * Called by UiPath constructor
1669
1588
  */
1670
- static formatActivityTypeForIncidents(elementType) {
1671
- // Convert camelCase BPMN element types to human-readable format
1672
- // e.g., "serviceTask" -> "Service Task", "exclusiveGateway" -> "Exclusive Gateway"
1673
- return elementType
1674
- .replace(/([A-Z])/g, ' $1') // Add space before uppercase letters
1675
- .replace(/^./, str => str.toUpperCase()) // Capitalize first letter
1676
- .trim(); // Remove any leading/trailing spaces
1589
+ static set(instance, internals) {
1590
+ this.store.set(instance, internals);
1677
1591
  }
1678
1592
  /**
1679
- * Fetch BPMN via getBpmn and add element name/type to each incident
1593
+ * Retrieve SDK instance internals
1594
+ * Called by BaseService constructor
1680
1595
  */
1681
- static async enrichIncidentsWithBpmnData(incidents, folderKey, service) {
1682
- // Check if all incidents have the same instanceId
1683
- const uniqueInstanceIds = [...new Set(incidents.map(i => i.instanceId))];
1684
- if (uniqueInstanceIds.length === 1) {
1685
- // Single instance optimization (in case of process instance incidents)
1686
- const elementInfo = await this.getBpmnElementInfo(uniqueInstanceIds[0], folderKey, service);
1687
- return incidents.map((incident) => this.transformIncidentWithBpmn(incident, elementInfo));
1688
- }
1689
- else {
1690
- // Multiple instances optimization (in case of process incidents)
1691
- return this.enrichMultipleInstanceIncidents(incidents, folderKey, service);
1596
+ static get(instance) {
1597
+ const internals = this.store.get(instance);
1598
+ if (!internals) {
1599
+ throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
1692
1600
  }
1601
+ return internals;
1693
1602
  }
1603
+ }
1604
+
1605
+ var _BaseService_apiClient;
1606
+ /**
1607
+ * Base class for all UiPath SDK services.
1608
+ *
1609
+ * Provides common functionality for authentication, configuration, and API communication.
1610
+ * All service classes extend this base to inherit dependency injection and HTTP client access.
1611
+ *
1612
+ * This class implements the dependency injection pattern where services receive a configured
1613
+ * UiPath instance. The ApiClient is created internally and handles all HTTP operations
1614
+ * including authentication token management.
1615
+ *
1616
+ * @remarks
1617
+ * Service classes should extend this base and call `super(uiPath)` in their constructor.
1618
+ * Protected HTTP methods (get, post, put, patch, delete) are available to all subclasses.
1619
+ *
1620
+ */
1621
+ class BaseService {
1694
1622
  /**
1695
- * When incidents span multiple instances, fetch BPMN per instance and annotate
1623
+ * Creates a base service instance with dependency injection.
1624
+ *
1625
+ * Extracts configuration, execution context, and token manager from the UiPath instance
1626
+ * to initialize an authenticated API client. The ApiClient handles all HTTP operations
1627
+ * and token management internally.
1628
+ *
1629
+ * @param instance - UiPath SDK instance providing authentication and configuration.
1630
+ * Services receive this via dependency injection in the modular pattern.
1631
+ * @param headers - Optional default headers to include in every request (e.g. `x-uipath-external-user-id` for
1632
+ * CAS external-app auth)
1633
+ *
1634
+ * @example
1635
+ * ```typescript
1636
+ * // Services automatically call this via super()
1637
+ * export class EntityService extends BaseService {
1638
+ * constructor(instance: IUiPath) {
1639
+ * super(instance); // Initializes the internal ApiClient
1640
+ * }
1641
+ * }
1642
+ *
1643
+ * // Usage in modular pattern
1644
+ * import { UiPath } from '@uipath/uipath-typescript/core';
1645
+ * import { Entities } from '@uipath/uipath-typescript/entities';
1646
+ *
1647
+ * const sdk = new UiPath(config);
1648
+ * await sdk.initialize();
1649
+ * const entities = new Entities(sdk);
1650
+ * ```
1696
1651
  */
1697
- static async enrichMultipleInstanceIncidents(incidents, folderKey, service) {
1698
- const groups = incidents.reduce((acc, incident) => {
1699
- const id = incident.instanceId || NO_INSTANCE;
1700
- (acc[id] = acc[id] || []).push(incident);
1701
- return acc;
1702
- }, {});
1703
- const results = await Promise.all(Object.entries(groups).map(async (entry) => {
1704
- const [instanceId, groupIncidents] = entry;
1705
- const elementInfo = await this.getBpmnElementInfo(instanceId, folderKey, service);
1706
- return groupIncidents.map((incident) => this.transformIncidentWithBpmn(incident, elementInfo));
1707
- }));
1708
- return results.flat();
1652
+ constructor(instance, headers) {
1653
+ // Private field - not visible via Object.keys() or any reflection
1654
+ _BaseService_apiClient.set(this, void 0);
1655
+ const { config, context, tokenManager, folderKey } = SDKInternalsRegistry.get(instance);
1656
+ __classPrivateFieldSet(this, _BaseService_apiClient, new ApiClient(config, context, tokenManager, headers ? { headers } : {}), "f");
1657
+ this.config = { folderKey };
1709
1658
  }
1710
1659
  /**
1711
- * Retrieve BPMN XML for an instance and derive element id → {name,type}
1660
+ * Gets a valid authentication token, refreshing if necessary.
1661
+ * Use this when you need to manually add Authorization headers (e.g., direct uploads).
1662
+ *
1663
+ * @returns Promise resolving to a valid access token string
1664
+ * @throws AuthenticationError if no token is available or refresh fails
1712
1665
  */
1713
- static async getBpmnElementInfo(instanceId, folderKey, service) {
1714
- if (!instanceId || instanceId === NO_INSTANCE) {
1715
- return {};
1716
- }
1717
- try {
1718
- const bpmnXml = await service.getBpmn(instanceId, folderKey);
1719
- return this.parseBpmnElementsForIncidents(bpmnXml);
1720
- }
1721
- catch (error) {
1722
- console.warn(`Failed to get BPMN for instance ${instanceId}:`, error);
1723
- return {};
1724
- }
1666
+ async getValidAuthToken() {
1667
+ return __classPrivateFieldGet(this, _BaseService_apiClient, "f").getValidToken();
1725
1668
  }
1726
1669
  /**
1727
- * Transform a raw incident by attaching element name/type from BPMN
1670
+ * Creates a service accessor for pagination helpers
1671
+ * This allows pagination helpers to access protected methods without making them public
1728
1672
  */
1729
- static transformIncidentWithBpmn(incident, elementInfo) {
1730
- const element = elementInfo[incident.elementId];
1731
- const transformed = transformData(incident, ProcessIncidentMap);
1673
+ createPaginationServiceAccess() {
1732
1674
  return {
1733
- ...transformed,
1734
- incidentElementActivityType: element?.type || UNKNOWN$1,
1735
- incidentElementActivityName: element?.name || UNKNOWN$1
1675
+ get: (path, options) => this.get(path, options || {}),
1676
+ post: (path, body, options) => this.post(path, body, options || {}),
1677
+ requestWithPagination: (method, path, paginationOptions, options) => this.requestWithPagination(method, path, paginationOptions, options)
1736
1678
  };
1737
1679
  }
1738
- }
1739
-
1740
- /**
1741
- * SDK Telemetry constants
1742
- */
1743
- // Connection string placeholder that will be replaced during build
1744
- const CONNECTION_STRING = "InstrumentationKey=a6efa11d-1feb-4508-9738-e13e12dcae5e;IngestionEndpoint=https://westeurope-5.in.applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;ApplicationId=7c58eb1c-9581-4ba6-839e-11725848a037";
1745
- // SDK Version placeholder
1746
- const SDK_VERSION = "1.3.7";
1747
- const VERSION = "Version";
1748
- const SERVICE = "Service";
1749
- const CLOUD_ORGANIZATION_NAME = "CloudOrganizationName";
1750
- const CLOUD_TENANT_NAME = "CloudTenantName";
1751
- const CLOUD_URL = "CloudUrl";
1752
- const CLOUD_CLIENT_ID = "CloudClientId";
1753
- const CLOUD_REDIRECT_URI = "CloudRedirectUri";
1754
- const APP_NAME = "ApplicationName";
1755
- const CLOUD_ROLE_NAME = "uipath-ts-sdk";
1756
- // Service and logger names
1757
- const SDK_SERVICE_NAME = "UiPath.TypeScript.Sdk";
1758
- const SDK_LOGGER_NAME = "uipath-ts-sdk-telemetry";
1759
- // Event names
1760
- const SDK_RUN_EVENT = "Sdk.Run";
1761
- // Default value for unknown/empty attributes
1762
- const UNKNOWN = "";
1763
-
1764
- /**
1765
- * Log exporter that sends ALL logs as Application Insights custom events
1766
- */
1767
- class ApplicationInsightsEventExporter {
1768
- constructor(connectionString) {
1769
- this.connectionString = connectionString;
1770
- }
1771
- export(logs, resultCallback) {
1772
- try {
1773
- logs.forEach(logRecord => {
1774
- this.sendAsCustomEvent(logRecord);
1775
- });
1776
- resultCallback({ code: 0 });
1777
- }
1778
- catch (error) {
1779
- console.debug('Failed to export logs to Application Insights:', error);
1780
- resultCallback({ code: 2, error });
1680
+ async request(method, path, options = {}) {
1681
+ switch (method.toUpperCase()) {
1682
+ case 'GET':
1683
+ return this.get(path, options);
1684
+ case 'POST':
1685
+ return this.post(path, options.body, options);
1686
+ case 'PUT':
1687
+ return this.put(path, options.body, options);
1688
+ case 'PATCH':
1689
+ return this.patch(path, options.body, options);
1690
+ case 'DELETE':
1691
+ return this.delete(path, options);
1692
+ default:
1693
+ throw new Error(`Unsupported HTTP method: ${method}`);
1781
1694
  }
1782
1695
  }
1783
- shutdown() {
1784
- return Promise.resolve();
1785
- }
1786
- sendAsCustomEvent(logRecord) {
1787
- // Get event name from body or attributes
1788
- const eventName = logRecord.body || SDK_RUN_EVENT;
1789
- const payload = {
1790
- name: 'Microsoft.ApplicationInsights.Event',
1791
- time: new Date().toISOString(),
1792
- iKey: this.extractInstrumentationKey(),
1793
- data: {
1794
- baseType: 'EventData',
1795
- baseData: {
1796
- ver: 2,
1797
- name: eventName,
1798
- properties: this.convertAttributesToProperties(logRecord.attributes || {})
1799
- }
1800
- },
1801
- tags: {
1802
- 'ai.cloud.role': CLOUD_ROLE_NAME,
1803
- 'ai.cloud.roleInstance': SDK_VERSION
1804
- }
1805
- };
1806
- this.sendToApplicationInsights(payload);
1807
- }
1808
- extractInstrumentationKey() {
1809
- const match = this.connectionString.match(/InstrumentationKey=([^;]+)/);
1810
- return match ? match[1] : '';
1811
- }
1812
- convertAttributesToProperties(attributes) {
1813
- const properties = {};
1814
- Object.entries(attributes || {}).forEach(([key, value]) => {
1815
- properties[key] = String(value);
1816
- });
1817
- return properties;
1818
- }
1819
- async sendToApplicationInsights(payload) {
1820
- try {
1821
- const ingestionEndpoint = this.extractIngestionEndpoint();
1822
- if (!ingestionEndpoint) {
1823
- console.debug('No ingestion endpoint found in connection string');
1824
- return;
1825
- }
1826
- const url = `${ingestionEndpoint}/v2/track`;
1827
- const response = await fetch(url, {
1828
- method: 'POST',
1829
- headers: {
1830
- 'Content-Type': 'application/json',
1831
- },
1832
- body: JSON.stringify(payload)
1833
- });
1834
- if (!response.ok) {
1835
- console.debug(`Failed to send event telemetry: ${response.status} ${response.statusText}`);
1836
- }
1837
- }
1838
- catch (error) {
1839
- console.debug('Error sending event telemetry to Application Insights:', error);
1696
+ async requestWithSpec(spec) {
1697
+ if (!spec.method || !spec.url) {
1698
+ throw new Error('Request spec must include method and url');
1840
1699
  }
1700
+ return this.request(spec.method, spec.url, spec);
1701
+ }
1702
+ async get(path, options = {}) {
1703
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").get(path, options);
1704
+ return { data: response };
1841
1705
  }
1842
- extractIngestionEndpoint() {
1843
- const match = this.connectionString.match(/IngestionEndpoint=([^;]+)/);
1844
- return match ? match[1] : '';
1706
+ async post(path, data, options = {}) {
1707
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").post(path, data, options);
1708
+ return { data: response };
1845
1709
  }
1846
- }
1847
- /**
1848
- * Singleton telemetry client
1849
- */
1850
- class TelemetryClient {
1851
- constructor() {
1852
- this.isInitialized = false;
1710
+ async put(path, data, options = {}) {
1711
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").put(path, data, options);
1712
+ return { data: response };
1853
1713
  }
1854
- static getInstance() {
1855
- if (!TelemetryClient.instance) {
1856
- TelemetryClient.instance = new TelemetryClient();
1857
- }
1858
- return TelemetryClient.instance;
1714
+ async patch(path, data, options = {}) {
1715
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").patch(path, data, options);
1716
+ return { data: response };
1717
+ }
1718
+ async delete(path, options = {}) {
1719
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").delete(path, options);
1720
+ return { data: response };
1859
1721
  }
1860
1722
  /**
1861
- * Initialize telemetry
1723
+ * Execute a request with cursor-based pagination
1862
1724
  */
1863
- initialize(config) {
1864
- if (this.isInitialized) {
1865
- return;
1866
- }
1867
- this.isInitialized = true;
1868
- if (config) {
1869
- this.telemetryContext = config;
1870
- }
1871
- try {
1872
- const connectionString = this.getConnectionString();
1873
- if (!connectionString) {
1874
- return;
1875
- }
1876
- this.setupTelemetryProvider(connectionString);
1725
+ async requestWithPagination(method, path, paginationOptions, options) {
1726
+ const paginationType = options.pagination.paginationType;
1727
+ // Validate and prepare pagination parameters
1728
+ const params = this.validateAndPreparePaginationParams(paginationType, paginationOptions);
1729
+ // Prepare request parameters based on pagination type
1730
+ const requestParams = this.preparePaginationRequestParams(paginationType, params, options.pagination);
1731
+ // For POST requests, merge pagination params into body and set params to undefined; for GET, use query params
1732
+ if (method.toUpperCase() === 'POST') {
1733
+ const existingBody = (options.body && typeof options.body === 'object') ? options.body : {};
1734
+ options.body = {
1735
+ ...existingBody,
1736
+ ...options.params,
1737
+ ...requestParams
1738
+ };
1739
+ options.params = undefined;
1877
1740
  }
1878
- catch (error) {
1879
- // Silent failure - telemetry errors shouldn't break functionality
1880
- console.debug('Failed to initialize OpenTelemetry:', error);
1741
+ else {
1742
+ // Merge pagination parameters with existing parameters
1743
+ options.params = {
1744
+ ...options.params,
1745
+ ...requestParams
1746
+ };
1881
1747
  }
1882
- }
1883
- getConnectionString() {
1884
- const connectionString = CONNECTION_STRING;
1885
- return connectionString;
1886
- }
1887
- setupTelemetryProvider(connectionString) {
1888
- const exporter = new ApplicationInsightsEventExporter(connectionString);
1889
- const processor = new sdkLogs.BatchLogRecordProcessor(exporter);
1890
- this.logProvider = new sdkLogs.LoggerProvider({
1891
- processors: [processor]
1748
+ // Make the request
1749
+ const response = await this.request(method, path, options);
1750
+ // Extract data from the response and create page result
1751
+ return this.createPaginatedResponseFromResponse(response, params, paginationType, {
1752
+ itemsField: options.pagination.itemsField,
1753
+ totalCountField: options.pagination.totalCountField,
1754
+ continuationTokenField: options.pagination.continuationTokenField
1892
1755
  });
1893
- this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
1894
1756
  }
1895
1757
  /**
1896
- * Track a telemetry event
1758
+ * Validates and prepares pagination parameters from options
1897
1759
  */
1898
- track(eventName, name, extraAttributes = {}) {
1899
- try {
1900
- // Skip if logger not initialized
1901
- if (!this.logger) {
1902
- return;
1903
- }
1904
- const finalDisplayName = name || eventName;
1905
- const attributes = this.getEnrichedAttributes(extraAttributes, eventName);
1906
- // Emit as log
1907
- this.logger.emit({
1908
- body: finalDisplayName,
1909
- attributes: attributes,
1910
- timestamp: Date.now(),
1911
- });
1912
- }
1913
- catch (error) {
1914
- // Silent failure
1915
- console.debug('Failed to track telemetry event:', error);
1760
+ validateAndPreparePaginationParams(paginationType, paginationOptions) {
1761
+ return PaginationHelpers.validatePaginationOptions(paginationOptions, paginationType);
1762
+ }
1763
+ /**
1764
+ * Prepares request parameters for pagination based on pagination type
1765
+ */
1766
+ preparePaginationRequestParams(paginationType, params, paginationConfig) {
1767
+ const requestParams = {};
1768
+ let limitedPageSize;
1769
+ const paginationParams = paginationConfig?.paginationParams;
1770
+ switch (paginationType) {
1771
+ case PaginationType.OFFSET:
1772
+ limitedPageSize = getLimitedPageSize(params.pageSize);
1773
+ const pageSizeParam = paginationParams?.pageSizeParam || ODATA_OFFSET_PARAMS.PAGE_SIZE_PARAM;
1774
+ const offsetParam = paginationParams?.offsetParam || ODATA_OFFSET_PARAMS.OFFSET_PARAM;
1775
+ const countParam = paginationParams?.countParam || ODATA_OFFSET_PARAMS.COUNT_PARAM;
1776
+ // When true (default), converts pageNumber to a skip/offset value (e.g., page 3 with pageSize 10 → skip 20).
1777
+ // When false, passes pageNumber directly as the offset param — used by APIs that accept a page number instead of a record offset.
1778
+ const convertToSkip = paginationParams?.convertToSkip ?? true;
1779
+ requestParams[pageSizeParam] = limitedPageSize;
1780
+ if (convertToSkip) {
1781
+ if (params.pageNumber && params.pageNumber > 1) {
1782
+ requestParams[offsetParam] = (params.pageNumber - 1) * limitedPageSize;
1783
+ }
1784
+ }
1785
+ else {
1786
+ requestParams[offsetParam] = params.pageNumber || 1;
1787
+ }
1788
+ {
1789
+ requestParams[countParam] = true;
1790
+ }
1791
+ break;
1792
+ case PaginationType.TOKEN:
1793
+ const tokenPageSizeParam = paginationParams?.pageSizeParam || BUCKET_TOKEN_PARAMS.PAGE_SIZE_PARAM;
1794
+ const tokenParam = paginationParams?.tokenParam || BUCKET_TOKEN_PARAMS.TOKEN_PARAM;
1795
+ if (params.pageSize) {
1796
+ requestParams[tokenPageSizeParam] = getLimitedPageSize(params.pageSize);
1797
+ }
1798
+ if (params.continuationToken) {
1799
+ requestParams[tokenParam] = params.continuationToken;
1800
+ }
1801
+ break;
1916
1802
  }
1803
+ return requestParams;
1917
1804
  }
1918
1805
  /**
1919
- * Get enriched attributes for telemetry events
1806
+ * Creates a paginated response from API response
1920
1807
  */
1921
- getEnrichedAttributes(extraAttributes, eventName) {
1922
- const attributes = {
1923
- [APP_NAME]: SDK_SERVICE_NAME,
1924
- [VERSION]: SDK_VERSION,
1925
- [SERVICE]: eventName,
1926
- [CLOUD_URL]: this.createCloudUrl(),
1927
- [CLOUD_ORGANIZATION_NAME]: this.telemetryContext?.orgName || UNKNOWN,
1928
- [CLOUD_TENANT_NAME]: this.telemetryContext?.tenantName || UNKNOWN,
1929
- [CLOUD_REDIRECT_URI]: this.telemetryContext?.redirectUri || UNKNOWN,
1930
- [CLOUD_CLIENT_ID]: this.telemetryContext?.clientId || UNKNOWN,
1931
- ...extraAttributes,
1932
- };
1933
- return attributes;
1808
+ createPaginatedResponseFromResponse(response, params, paginationType, fields) {
1809
+ // Extract fields from response
1810
+ const itemsField = fields.itemsField ||
1811
+ (paginationType === PaginationType.TOKEN ? 'items' : 'value');
1812
+ const totalCountField = fields.totalCountField || 'totalRecordCount';
1813
+ const continuationTokenField = fields.continuationTokenField || 'continuationToken';
1814
+ // Extract items and metadata
1815
+ // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1816
+ const items = Array.isArray(response.data) ? response.data : (response.data[itemsField] || []);
1817
+ const rawTotalCount = Array.isArray(response.data) ? undefined : resolveNestedField(response.data, totalCountField);
1818
+ const totalCount = typeof rawTotalCount === 'number' ? rawTotalCount : undefined;
1819
+ const continuationToken = response.data[continuationTokenField];
1820
+ // Determine if there are more pages
1821
+ const hasMore = this.determineHasMorePages(paginationType, {
1822
+ totalCount,
1823
+ pageSize: params.pageSize,
1824
+ currentPage: params.pageNumber || 1,
1825
+ itemsCount: items.length,
1826
+ continuationToken
1827
+ });
1828
+ // Create and return the page result
1829
+ return PaginationManager.createPaginatedResponse({
1830
+ pageInfo: {
1831
+ hasMore,
1832
+ totalCount,
1833
+ currentPage: params.pageNumber,
1834
+ pageSize: params.pageSize,
1835
+ continuationToken
1836
+ },
1837
+ type: paginationType,
1838
+ }, items);
1934
1839
  }
1935
1840
  /**
1936
- * Create cloud URL from base URL, organization ID, and tenant ID
1841
+ * Determines if there are more pages based on pagination type and metadata
1937
1842
  */
1938
- createCloudUrl() {
1939
- const baseUrl = this.telemetryContext?.baseUrl;
1940
- const orgId = this.telemetryContext?.orgName;
1941
- const tenantId = this.telemetryContext?.tenantName;
1942
- if (!baseUrl || !orgId || !tenantId) {
1943
- return UNKNOWN;
1843
+ determineHasMorePages(paginationType, info) {
1844
+ switch (paginationType) {
1845
+ case PaginationType.OFFSET:
1846
+ const effectivePageSize = info.pageSize ?? DEFAULT_PAGE_SIZE;
1847
+ // If totalCount is available, use it for precise calculation
1848
+ if (info.totalCount !== undefined) {
1849
+ return (info.currentPage * effectivePageSize) < info.totalCount;
1850
+ }
1851
+ // Fallback when totalCount is not available
1852
+ // NOTE: This code path should rarely be executed as the APIs typically return totalCount
1853
+ return info.itemsCount === effectivePageSize;
1854
+ case PaginationType.TOKEN:
1855
+ return !!info.continuationToken;
1856
+ default:
1857
+ return false;
1944
1858
  }
1945
- return `${baseUrl}/${orgId}/${tenantId}`;
1946
1859
  }
1947
1860
  }
1948
- // Export singleton instance
1949
- const telemetryClient = TelemetryClient.getInstance();
1950
-
1951
- /**
1952
- * SDK Track decorator and function for telemetry
1953
- */
1954
- /**
1955
- * Common tracking logic shared between method and function decorators
1956
- */
1957
- function createTrackedFunction(originalFunction, nameOrOptions, fallbackName, opts) {
1958
- return function (...args) {
1959
- // Determine if we should track this call
1960
- let shouldTrack = true;
1961
- if (opts.condition !== undefined) {
1962
- if (typeof opts.condition === 'function') {
1963
- shouldTrack = opts.condition.apply(this, args);
1964
- }
1965
- else {
1966
- shouldTrack = opts.condition;
1967
- }
1968
- }
1969
- // Track the event if enabled
1970
- if (shouldTrack) {
1971
- // Use the full name provided in the decorator (e.g., "Queue.GetAll")
1972
- const serviceMethod = typeof nameOrOptions === 'string'
1973
- ? nameOrOptions
1974
- : fallbackName;
1975
- // Use 'Sdk.Run' as the name and serviceMethod as the service
1976
- telemetryClient.track(serviceMethod, SDK_RUN_EVENT, opts.attributes);
1977
- }
1978
- // Execute the original function
1979
- return originalFunction.apply(this, args);
1980
- };
1981
- }
1982
- /**
1983
- * Track decorator that can be used to automatically track function calls
1984
- *
1985
- * Usage:
1986
- * @track("Service.Method")
1987
- * function myFunction() { ... }
1988
- *
1989
- * @track("Queue.GetAll")
1990
- * async getAll() { ... }
1991
- *
1992
- * @track("Tasks.Create")
1993
- * async create() { ... }
1994
- *
1995
- * @track("Assets.Update", { condition: false })
1996
- * function myFunction() { ... }
1997
- *
1998
- * @track("Processes.Start", { attributes: { customProp: "value" } })
1999
- * function myFunction() { ... }
2000
- */
2001
- function track(nameOrOptions, options) {
2002
- return function decorator(_target, propertyKey, descriptor) {
2003
- const opts = typeof nameOrOptions === 'object' ? nameOrOptions : {};
2004
- if (descriptor && typeof descriptor.value === 'function') {
2005
- // Method decorator
2006
- descriptor.value = createTrackedFunction(descriptor.value, nameOrOptions, propertyKey || 'unknown_method', opts);
2007
- return descriptor;
2008
- }
2009
- // Function decorator
2010
- return (originalFunction) => createTrackedFunction(originalFunction, nameOrOptions, originalFunction.name || 'unknown_function', opts);
2011
- };
2012
- }
1861
+ _BaseService_apiClient = new WeakMap();
2013
1862
 
2014
1863
  /**
2015
1864
  * Creates methods for a process instance
@@ -2121,6 +1970,41 @@ exports.DebugMode = void 0;
2121
1970
  * Case Instance Types
2122
1971
  * Types and interfaces for Maestro case instance management
2123
1972
  */
1973
+ /**
1974
+ * SLA status for a case instance
1975
+ */
1976
+ var SlaSummaryStatus;
1977
+ (function (SlaSummaryStatus) {
1978
+ /** Case is within SLA deadline */
1979
+ SlaSummaryStatus["ON_TRACK"] = "On Track";
1980
+ /** Case is approaching SLA deadline based on at-risk percentage threshold */
1981
+ SlaSummaryStatus["AT_RISK"] = "At Risk";
1982
+ /** Case has exceeded SLA deadline */
1983
+ SlaSummaryStatus["OVERDUE"] = "Overdue";
1984
+ /** Case instance has completed */
1985
+ SlaSummaryStatus["COMPLETED"] = "Completed";
1986
+ /** SLA status cannot be determined (no SLA deadline defined) */
1987
+ SlaSummaryStatus["UNKNOWN"] = "Unknown";
1988
+ })(SlaSummaryStatus || (SlaSummaryStatus = {}));
1989
+ /**
1990
+ * Instance status values for case instances and process instances
1991
+ */
1992
+ var InstanceStatus;
1993
+ (function (InstanceStatus) {
1994
+ /** Instance status not yet populated by the backend */
1995
+ InstanceStatus["UNKNOWN"] = "";
1996
+ InstanceStatus["CANCELLED"] = "Cancelled";
1997
+ InstanceStatus["CANCELING"] = "Canceling";
1998
+ InstanceStatus["COMPLETED"] = "Completed";
1999
+ InstanceStatus["FAULTED"] = "Faulted";
2000
+ InstanceStatus["PAUSED"] = "Paused";
2001
+ InstanceStatus["PAUSING"] = "Pausing";
2002
+ InstanceStatus["PENDING"] = "Pending";
2003
+ InstanceStatus["RESUMING"] = "Resuming";
2004
+ InstanceStatus["RETRYING"] = "Retrying";
2005
+ InstanceStatus["RUNNING"] = "Running";
2006
+ InstanceStatus["UPGRADING"] = "Upgrading";
2007
+ })(InstanceStatus || (InstanceStatus = {}));
2124
2008
  /**
2125
2009
  * Case stage task type
2126
2010
  */
@@ -2155,6 +2039,8 @@ var EscalationTriggerType;
2155
2039
  (function (EscalationTriggerType) {
2156
2040
  EscalationTriggerType["SLA_BREACHED"] = "sla-breached";
2157
2041
  EscalationTriggerType["AT_RISK"] = "at-risk";
2042
+ /** Default value when no escalation rule is defined */
2043
+ EscalationTriggerType["NONE"] = "None";
2158
2044
  })(EscalationTriggerType || (EscalationTriggerType = {}));
2159
2045
  /**
2160
2046
  * SLA duration unit
@@ -2167,6 +2053,40 @@ var SLADurationUnit;
2167
2053
  SLADurationUnit["MONTHS"] = "m";
2168
2054
  })(SLADurationUnit || (SLADurationUnit = {}));
2169
2055
 
2056
+ /**
2057
+ * Insights Types
2058
+ * Shared types for Maestro insights analytics endpoints
2059
+ */
2060
+ /**
2061
+ * Time bucketing granularity for insights time-series queries.
2062
+ *
2063
+ * Controls how data points are grouped on the time axis.
2064
+ */
2065
+ exports.TimeInterval = void 0;
2066
+ (function (TimeInterval) {
2067
+ /** Group data points by hour */
2068
+ TimeInterval["Hour"] = "HOUR";
2069
+ /** Group data points by day */
2070
+ TimeInterval["Day"] = "DAY";
2071
+ /** Group data points by week */
2072
+ TimeInterval["Week"] = "WEEK";
2073
+ })(exports.TimeInterval || (exports.TimeInterval = {}));
2074
+ /**
2075
+ * Final instance statuses returned by the instance status timeline endpoint.
2076
+ *
2077
+ * Only includes statuses where the instance has finished execution — Completed, Faulted, or Cancelled.
2078
+ * Active statuses like Running or Paused are not included.
2079
+ */
2080
+ exports.InstanceFinalStatus = void 0;
2081
+ (function (InstanceFinalStatus) {
2082
+ /** Instance completed successfully */
2083
+ InstanceFinalStatus["Completed"] = "Completed";
2084
+ /** Instance encountered an error */
2085
+ InstanceFinalStatus["Faulted"] = "Faulted";
2086
+ /** Instance was cancelled */
2087
+ InstanceFinalStatus["Cancelled"] = "Cancelled";
2088
+ })(exports.InstanceFinalStatus || (exports.InstanceFinalStatus = {}));
2089
+
2170
2090
  /**
2171
2091
  * Maps fields for Process Instance entities to ensure consistent naming
2172
2092
  */
@@ -2542,6 +2462,177 @@ class MaestroProcessesService extends BaseService {
2542
2462
  // Fetch BPMN XML and add element name/type to each incident
2543
2463
  return BpmnHelpers.enrichIncidentsWithBpmnData(rawResponse.data || [], folderKey, this.processInstancesService);
2544
2464
  }
2465
+ /**
2466
+ * Get the top 5 processes ranked by run count within a time range.
2467
+ *
2468
+ * Returns an array of up to 5 processes sorted by how many times they were executed,
2469
+ * useful for identifying the most active processes in a given period.
2470
+ *
2471
+ * @param startTime - Start of the time range to query
2472
+ * @param endTime - End of the time range to query
2473
+ * @param options - Optional filters (packageId, processKey, version)
2474
+ * @returns Promise resolving to an array of {@link ProcessGetTopRunCountResponse}
2475
+ * @example
2476
+ * ```typescript
2477
+ * import { MaestroProcesses } from '@uipath/uipath-typescript/maestro-processes';
2478
+ *
2479
+ * const maestroProcesses = new MaestroProcesses(sdk);
2480
+ *
2481
+ * // Get top processes by run count for the last 7 days
2482
+ * const topProcesses = await maestroProcesses.getTopRunCount(
2483
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2484
+ * new Date()
2485
+ * );
2486
+ *
2487
+ * for (const process of topProcesses) {
2488
+ * console.log(`${process.packageId}: ${process.runCount} runs`);
2489
+ * }
2490
+ * ```
2491
+ *
2492
+ * @example
2493
+ * ```typescript
2494
+ * // Get top processes by run count for a specific package
2495
+ * const filtered = await maestroProcesses.getTopRunCount(
2496
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2497
+ * new Date(),
2498
+ * { packageId: '<packageId>' }
2499
+ * );
2500
+ * ```
2501
+ */
2502
+ async getTopRunCount(startTime, endTime, options) {
2503
+ const { data } = await this.post(MAESTRO_ENDPOINTS.INSIGHTS.TOP_PROCESSES_BY_RUN_COUNT, buildInsightsTopBody(startTime, endTime, false, options));
2504
+ return (data ?? []).map(process => ({ ...process, name: process.packageId }));
2505
+ }
2506
+ /**
2507
+ * Get all instances status counts aggregated by date for maestro processes.
2508
+ *
2509
+ * Returns time-grouped counts of instances grouped by status (Completed, Faulted, Cancelled),
2510
+ * useful for rendering time-series charts. Use `groupBy` to control the time bucket size
2511
+ * (hour, day, or week) — defaults to day if not provided.
2512
+ *
2513
+ * @param startTime - Start of the time range to query
2514
+ * @param endTime - End of the time range to query
2515
+ * @param options - Optional settings for time bucketing granularity
2516
+ * @returns Promise resolving to an array of {@link InstanceStatusTimelineResponse}
2517
+ *
2518
+ * @example
2519
+ * ```typescript
2520
+ * // Get daily instance status for the last 7 days
2521
+ * const now = new Date();
2522
+ * const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
2523
+ * const statuses = await maestroProcesses.getInstanceStatusTimeline(sevenDaysAgo, now);
2524
+ *
2525
+ * for (const entry of statuses) {
2526
+ * console.log(`${entry.startTime} — ${entry.status}: ${entry.count}`);
2527
+ * }
2528
+ * ```
2529
+ *
2530
+ * @example
2531
+ * ```typescript
2532
+ * import { TimeInterval } from '@uipath/uipath-typescript/maestro-processes';
2533
+ *
2534
+ * // Get hourly breakdown
2535
+ * const statuses = await maestroProcesses.getInstanceStatusTimeline(startTime, endTime, {
2536
+ * groupBy: TimeInterval.Hour,
2537
+ * });
2538
+ * ```
2539
+ *
2540
+ * @example
2541
+ * ```typescript
2542
+ * // Get all-time data (from Unix epoch to now)
2543
+ * const allTime = await maestroProcesses.getInstanceStatusTimeline(new Date(0), new Date());
2544
+ * ```
2545
+ */
2546
+ async getInstanceStatusTimeline(startTime, endTime, options) {
2547
+ return fetchInstanceStatusTimeline(this.post.bind(this), startTime, endTime, false, options);
2548
+ }
2549
+ /**
2550
+ * Get the top 10 processes ranked by failure count within a time range.
2551
+ *
2552
+ * Returns an array of up to 10 processes sorted by how many instances faulted,
2553
+ * useful for identifying the most error-prone processes in a given period.
2554
+ *
2555
+ * @param startTime - Start of the time range to query
2556
+ * @param endTime - End of the time range to query
2557
+ * @param options - Optional filters (packageId, processKey, version)
2558
+ * @returns Promise resolving to an array of {@link ProcessGetTopFaultedCountResponse}
2559
+ * @example
2560
+ * ```typescript
2561
+ * import { MaestroProcesses } from '@uipath/uipath-typescript/maestro-processes';
2562
+ *
2563
+ * const maestroProcesses = new MaestroProcesses(sdk);
2564
+ *
2565
+ * // Get top processes by faulted count for the last 7 days
2566
+ * const topFailing = await maestroProcesses.getTopFaultedCount(
2567
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2568
+ * new Date()
2569
+ * );
2570
+ *
2571
+ * for (const process of topFailing) {
2572
+ * console.log(`${process.packageId}: ${process.faultedCount} failures`);
2573
+ * }
2574
+ * ```
2575
+ *
2576
+ * @example
2577
+ * ```typescript
2578
+ * // Get top processes by faulted count for a specific package
2579
+ * const filtered = await maestroProcesses.getTopFaultedCount(
2580
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2581
+ * new Date(),
2582
+ * { packageId: '<packageId>' }
2583
+ * );
2584
+ * ```
2585
+ */
2586
+ async getTopFaultedCount(startTime, endTime, options) {
2587
+ const { data } = await this.post(MAESTRO_ENDPOINTS.INSIGHTS.TOP_PROCESSES_WITH_FAILURE, buildInsightsTopBody(startTime, endTime, false, options));
2588
+ return (data ?? []).map(item => ({
2589
+ packageId: item.packageId,
2590
+ processKey: item.processKey,
2591
+ faultedCount: item.runCount,
2592
+ name: item.packageId,
2593
+ }));
2594
+ }
2595
+ /**
2596
+ * Get the top 5 processes ranked by total duration within a time range.
2597
+ *
2598
+ * Returns an array of up to 5 processes sorted by their total execution time,
2599
+ * useful for identifying the longest-running processes in a given period.
2600
+ *
2601
+ * @param startTime - Start of the time range to query
2602
+ * @param endTime - End of the time range to query
2603
+ * @param options - Optional filters (packageId, processKey, version)
2604
+ * @returns Promise resolving to an array of {@link ProcessGetTopDurationResponse}
2605
+ * @example
2606
+ * ```typescript
2607
+ * import { MaestroProcesses } from '@uipath/uipath-typescript/maestro-processes';
2608
+ *
2609
+ * const maestroProcesses = new MaestroProcesses(sdk);
2610
+ *
2611
+ * // Get top processes by duration for the last 7 days
2612
+ * const topProcesses = await maestroProcesses.getTopExecutionDuration(
2613
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2614
+ * new Date()
2615
+ * );
2616
+ *
2617
+ * for (const process of topProcesses) {
2618
+ * console.log(`${process.packageId}: ${process.duration}ms total`);
2619
+ * }
2620
+ * ```
2621
+ *
2622
+ * @example
2623
+ * ```typescript
2624
+ * // Get top processes by duration for a specific package
2625
+ * const filtered = await maestroProcesses.getTopExecutionDuration(
2626
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2627
+ * new Date(),
2628
+ * { packageId: '<packageId>' }
2629
+ * );
2630
+ * ```
2631
+ */
2632
+ async getTopExecutionDuration(startTime, endTime, options) {
2633
+ const { data } = await this.post(MAESTRO_ENDPOINTS.INSIGHTS.TOP_PROCESSES_BY_DURATION, buildInsightsTopBody(startTime, endTime, false, options));
2634
+ return (data ?? []).map(process => ({ ...process, name: process.packageId }));
2635
+ }
2545
2636
  }
2546
2637
  __decorate([
2547
2638
  track('MaestroProcesses.GetAll')
@@ -2549,6 +2640,18 @@ __decorate([
2549
2640
  __decorate([
2550
2641
  track('MaestroProcesses.GetIncidents')
2551
2642
  ], MaestroProcessesService.prototype, "getIncidents", null);
2643
+ __decorate([
2644
+ track('MaestroProcesses.GetTopRunCount')
2645
+ ], MaestroProcessesService.prototype, "getTopRunCount", null);
2646
+ __decorate([
2647
+ track('MaestroProcesses.GetInstanceStatusTimeline')
2648
+ ], MaestroProcessesService.prototype, "getInstanceStatusTimeline", null);
2649
+ __decorate([
2650
+ track('MaestroProcesses.GetTopFaultedCount')
2651
+ ], MaestroProcessesService.prototype, "getTopFaultedCount", null);
2652
+ __decorate([
2653
+ track('MaestroProcesses.GetTopExecutionDuration')
2654
+ ], MaestroProcessesService.prototype, "getTopExecutionDuration", null);
2552
2655
 
2553
2656
  /**
2554
2657
  * Service class for Maestro Process Incidents