@uipath/uipath-typescript 1.3.8 → 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 (39) hide show
  1. package/dist/assets/index.cjs +25 -270
  2. package/dist/assets/index.mjs +25 -270
  3. package/dist/attachments/index.cjs +23 -267
  4. package/dist/attachments/index.mjs +23 -267
  5. package/dist/buckets/index.cjs +54 -270
  6. package/dist/buckets/index.d.ts +50 -1
  7. package/dist/buckets/index.mjs +54 -270
  8. package/dist/cases/index.cjs +408 -337
  9. package/dist/cases/index.d.ts +534 -2
  10. package/dist/cases/index.mjs +409 -338
  11. package/dist/conversational-agent/index.cjs +71 -281
  12. package/dist/conversational-agent/index.d.ts +62 -12
  13. package/dist/conversational-agent/index.mjs +71 -282
  14. package/dist/core/index.cjs +39 -289
  15. package/dist/core/index.d.ts +9 -98
  16. package/dist/core/index.mjs +40 -275
  17. package/dist/document-understanding/index.cjs +18 -1
  18. package/dist/document-understanding/index.d.ts +636 -610
  19. package/dist/document-understanding/index.mjs +18 -1
  20. package/dist/entities/index.cjs +25 -270
  21. package/dist/entities/index.mjs +25 -270
  22. package/dist/feedback/index.cjs +23 -268
  23. package/dist/feedback/index.mjs +23 -268
  24. package/dist/index.cjs +600 -293
  25. package/dist/index.d.ts +1603 -722
  26. package/dist/index.mjs +600 -279
  27. package/dist/index.umd.js +789 -158
  28. package/dist/jobs/index.cjs +25 -270
  29. package/dist/jobs/index.mjs +25 -270
  30. package/dist/maestro-processes/index.cjs +1751 -1720
  31. package/dist/maestro-processes/index.d.ts +430 -2
  32. package/dist/maestro-processes/index.mjs +1752 -1721
  33. package/dist/processes/index.cjs +25 -270
  34. package/dist/processes/index.mjs +25 -270
  35. package/dist/queues/index.cjs +25 -270
  36. package/dist/queues/index.mjs +25 -270
  37. package/dist/tasks/index.cjs +25 -270
  38. package/dist/tasks/index.mjs +25 -270
  39. 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,466 +45,405 @@ 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';
57
- }
58
- function isEntityError(error) {
59
- return typeof error === 'object' &&
60
- error !== null &&
61
- 'error' in error &&
62
- typeof error.error === 'string';
63
- }
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';
73
- }
50
+ const PIMS_BASE = 'pims_';
51
+ const INSIGHTS_RTM_BASE = 'insightsrtm_';
74
52
 
75
53
  /**
76
- * HTTP status code constants for error handling
54
+ * Maestro Service Endpoints
77
55
  */
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
56
  /**
92
- * Error type constants for consistent error identification
57
+ * Maestro Process Service Endpoints
93
58
  */
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'
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
+ },
102
87
  };
88
+
103
89
  /**
104
- * HTTP header constants for error handling
90
+ * Maestro Process Models
91
+ * Model classes for Maestro processes
105
92
  */
106
- const HttpHeaders = {
107
- X_REQUEST_ID: 'x-request-id'
108
- };
109
93
  /**
110
- * Standard error message constants
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
111
99
  */
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',
129
- };
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
+ };
110
+ }
130
111
  /**
131
- * Error name constants for network error identification
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
132
117
  */
133
- const ErrorNames = {
134
- ABORT_ERROR: 'AbortError'};
118
+ function createProcessWithMethods(processData, service) {
119
+ const methods = createProcessMethods(processData, service);
120
+ return Object.assign({}, processData, methods);
121
+ }
135
122
 
136
123
  /**
137
- * Parser for Orchestrator/Task error format
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
138
132
  */
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
- }
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
+ };
156
144
  }
157
145
  /**
158
- * Parser for Entity (Data Fabric) error format
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
159
156
  */
160
- class EntityErrorParser {
161
- canParse(errorBody) {
162
- return isEntityError(errorBody);
163
- }
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
- };
176
- }
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 ?? [];
177
168
  }
169
+
178
170
  /**
179
- * Parser for PIMS error format
171
+ * Common constants used across the SDK
180
172
  */
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}`;
194
- }
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
- }
209
- }
210
173
  /**
211
- * Fallback parser for unrecognized formats
174
+ * Prefix used for OData query parameters
212
175
  */
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
- }
176
+ const ODATA_PREFIX = '$';
177
+ const UNKNOWN = 'Unknown';
178
+ const NO_INSTANCE = 'no-instance';
229
179
  /**
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
180
+ * HTTP methods
245
181
  */
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
- }
255
- /**
256
- * Parses error response body into standardized format
257
- * @param response - The HTTP response object
258
- * @returns Standardized error information
259
- */
260
- async parse(response) {
261
- 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);
267
- }
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
- };
280
- }
281
- }
282
- }
283
- // Export singleton instance
284
- const errorResponseParser = new ErrorResponseParser();
285
-
182
+ const HTTP_METHODS = {
183
+ GET: 'GET',
184
+ POST: 'POST'};
286
185
  /**
287
- * Base error class for all UiPath SDK errors
288
- * Extends Error for standard error handling compatibility
186
+ * Process Instance pagination constants for token-based pagination
289
187
  */
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);
301
- }
302
- }
303
- /**
304
- * Returns a clean JSON representation of the error
305
- */
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
- };
314
- }
315
- /**
316
- * Returns detailed debug information including stack trace
317
- */
318
- getDebugInfo() {
319
- return {
320
- ...this.toJSON(),
321
- stack: this.stack
322
- };
323
- }
324
- }
325
-
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'
193
+ };
326
194
  /**
327
- * Error thrown when authentication fails (401 errors)
328
- * Common scenarios:
329
- * - Invalid credentials
330
- * - Expired token
331
- * - Missing authentication
195
+ * OData OFFSET pagination parameter names (ODATA-style)
332
196
  */
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
- }
342
-
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'
204
+ };
343
205
  /**
344
- * Error thrown when authorization fails (403 errors)
345
- * Common scenarios:
346
- * - Insufficient permissions
347
- * - Access denied to resource
348
- * - Invalid scope
206
+ * Bucket TOKEN pagination parameter names
349
207
  */
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
-
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'
213
+ };
360
214
  /**
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
215
+ * Process Instance TOKEN pagination parameter names
366
216
  */
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
- }
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
+ };
376
223
 
377
224
  /**
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
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.
383
227
  */
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
228
  /**
395
- * Error thrown when rate limit is exceeded (429 errors)
396
- * Common scenarios:
397
- * - Too many requests in a time window
398
- * - API throttling
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
+ * ```
399
253
  */
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
- });
254
+ function transformData(data, fieldMapping) {
255
+ // Handle array of objects
256
+ if (Array.isArray(data)) {
257
+ return data.map(item => transformData(item, fieldMapping));
407
258
  }
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
+ }
267
+ }
268
+ return result;
408
269
  }
409
-
410
270
  /**
411
- * Error thrown when server encounters an error (5xx errors)
412
- * Common scenarios:
413
- * - Internal server error
414
- * - Service unavailable
415
- * - Gateway timeout
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 }
416
281
  */
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;
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;
290
+ }
432
291
  }
292
+ return result;
433
293
  }
434
294
 
435
295
  /**
436
- * Error thrown when network/connection issues occur
437
- * Common scenarios:
438
- * - Connection timeout
439
- * - DNS resolution failure
440
- * - Network unreachable
441
- * - Request aborted
296
+ * Maps fields for Incident entities
442
297
  */
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
-
298
+ const ProcessIncidentMap = {
299
+ errorTimeUtc: 'errorTime'
300
+ };
453
301
  /**
454
- * Factory for creating typed errors based on HTTP status codes
455
- * Follows the Factory pattern for clean error instantiation
302
+ * Maps fields for Incident Summary entities
456
303
  */
457
- class ErrorFactory {
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 {
458
312
  /**
459
- * Creates appropriate error instance based on HTTP status code
313
+ * Parse BPMN XML and extract element id {name,type} used for incidents
460
314
  */
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 });
315
+ static parseBpmnElementsForIncidents(bpmnXml) {
316
+ const elementInfo = {};
317
+ try {
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;
479
327
  }
480
- // For unknown client errors, treat as validation error
481
- return new ValidationError({
482
- message: `${message} (HTTP ${statusCode})`,
483
- statusCode,
484
- requestId
485
- });
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
+ }
486
339
  }
340
+ catch (error) {
341
+ console.warn('Failed to parse BPMN XML for incidents:', error);
342
+ }
343
+ return elementInfo;
487
344
  }
488
345
  /**
489
- * Creates a NetworkError from a fetch/network error
346
+ * Format BPMN element type to human-readable activity type for incidents
490
347
  */
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
- }
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));
503
366
  }
504
- return new NetworkError({ message });
367
+ else {
368
+ // Multiple instances optimization (in case of process incidents)
369
+ return this.enrichMultipleInstanceIncidents(incidents, folderKey, service);
370
+ }
371
+ }
372
+ /**
373
+ * When incidents span multiple instances, fetch BPMN per instance and annotate
374
+ */
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();
387
+ }
388
+ /**
389
+ * Retrieve BPMN XML for an instance and derive element id → {name,type}
390
+ */
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);
410
+ return {
411
+ ...transformed,
412
+ incidentElementActivityType: element?.type || UNKNOWN,
413
+ incidentElementActivityName: element?.name || UNKNOWN
414
+ };
505
415
  }
506
416
  }
507
417
 
418
+ /**
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';
430
+
431
+ /**
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);
446
+
508
447
  const FOLDER_KEY = 'X-UIPATH-FolderKey';
509
448
  const FOLDER_ID = 'X-UIPATH-OrganizationUnitId';
510
449
  const TRACEPARENT = 'traceparent';
@@ -527,1524 +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
- * Resolves a field value from an object, supporting both direct keys (e.g., '@odata.count')
657
- * and dot-separated nested paths (e.g., 'pagination.totalCount').
658
- * Direct key match takes priority over nested traversal.
552
+ * Error type constants for consistent error identification
659
553
  */
660
- function resolveNestedField(data, fieldPath) {
661
- if (!data) {
662
- return undefined;
663
- }
664
- if (fieldPath in data) {
665
- return data[fieldPath];
666
- }
667
- if (!fieldPath.includes('.')) {
668
- return undefined;
669
- }
670
- let value = data;
671
- for (const part of fieldPath.split('.')) {
672
- value = value?.[part];
673
- }
674
- return value;
675
- }
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
+ };
676
563
  /**
677
- * Filters out undefined values from an object
678
- * @param obj The source object
679
- * @returns A new object without undefined values
680
- *
681
- * @example
682
- * ```typescript
683
- * // Object with undefined values
684
- * const options = {
685
- * name: 'test',
686
- * count: 5,
687
- * prefix: undefined,
688
- * suffix: null
689
- * };
690
- * const result = filterUndefined(options);
691
- * // result = { name: 'test', count: 5, suffix: null }
692
- * ```
564
+ * HTTP header constants for error handling
693
565
  */
694
- function filterUndefined(obj) {
695
- const result = {};
696
- for (const [key, value] of Object.entries(obj)) {
697
- if (value !== undefined) {
698
- result[key] = value;
699
- }
700
- }
701
- return result;
702
- }
703
-
566
+ const HttpHeaders = {
567
+ X_REQUEST_ID: 'x-request-id'
568
+ };
704
569
  /**
705
- * Utility functions for platform detection
570
+ * Standard error message constants
706
571
  */
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
+ };
707
590
  /**
708
- * Checks if code is running in a browser environment
591
+ * Error name constants for network error identification
709
592
  */
710
- const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
711
- isBrowser && window.self != window.top && window.location.href.includes('source=ActionCenter');
593
+ const ErrorNames = {
594
+ ABORT_ERROR: 'AbortError'};
712
595
 
713
596
  /**
714
- * Base64 encoding/decoding
715
- */
716
- /**
717
- * Encodes a string to base64
718
- * @param str - The string to encode
719
- * @returns Base64 encoded string
597
+ * Parser for Orchestrator/Task error format
720
598
  */
721
- function encodeBase64(str) {
722
- // TextEncoder for UTF-8 encoding (works in both browser and Node.js)
723
- const encoder = new TextEncoder();
724
- const data = encoder.encode(str);
725
- // Convert Uint8Array to base64
726
- if (isBrowser) {
727
- // Browser environment
728
- // Convert Uint8Array to binary string then to base64
729
- const binaryString = Array.from(data, byte => String.fromCharCode(byte)).join('');
730
- return btoa(binaryString);
599
+ class OrchestratorErrorParser {
600
+ canParse(errorBody) {
601
+ return isOrchestratorError(errorBody);
731
602
  }
732
- else {
733
- // Node.js environment
734
- 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
+ };
735
615
  }
736
616
  }
737
617
  /**
738
- * Decodes a base64 string
739
- * @param base64 - The base64 string to decode
740
- * @returns Decoded string
618
+ * Parser for Entity (Data Fabric) error format
741
619
  */
742
- function decodeBase64(base64) {
743
- let bytes;
744
- if (isBrowser) {
745
- // Browser environment
746
- const binaryString = atob(base64);
747
- bytes = new Uint8Array(binaryString.length);
748
- for (let i = 0; i < binaryString.length; i++) {
749
- bytes[i] = binaryString.charCodeAt(i);
750
- }
620
+ class EntityErrorParser {
621
+ canParse(errorBody) {
622
+ return isEntityError(errorBody);
751
623
  }
752
- else {
753
- // Node.js environment
754
- 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
+ };
755
636
  }
756
- // TextDecoder for UTF-8 decoding (works in both browser and Node.js)
757
- const decoder = new TextDecoder();
758
- return decoder.decode(bytes);
759
637
  }
760
-
761
638
  /**
762
- * PaginationManager handles the conversion between uniform cursor-based pagination
763
- * and the specific pagination type for each service
639
+ * Parser for PIMS error format
764
640
  */
765
- class PaginationManager {
766
- /**
767
- * Create a pagination cursor for subsequent page requests
768
- */
769
- static createCursor({ pageInfo, type }) {
770
- if (!pageInfo.hasMore) {
771
- return undefined;
772
- }
773
- const cursorData = {
774
- type,
775
- pageSize: pageInfo.pageSize,
776
- };
777
- switch (type) {
778
- case PaginationType.OFFSET:
779
- if (pageInfo.currentPage) {
780
- cursorData.pageNumber = pageInfo.currentPage + 1;
781
- }
782
- break;
783
- case PaginationType.TOKEN:
784
- if (pageInfo.continuationToken) {
785
- cursorData.continuationToken = pageInfo.continuationToken;
786
- }
787
- else {
788
- return undefined; // No continuation token, can't continue
789
- }
790
- break;
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}`;
791
654
  }
792
655
  return {
793
- value: encodeBase64(JSON.stringify(cursorData))
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
667
+ };
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';
680
+ return {
681
+ message,
682
+ code: response?.status?.toString(),
683
+ details: {
684
+ originalResponse: errorBody
685
+ },
794
686
  };
795
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
+ }
796
715
  /**
797
- * 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
798
719
  */
799
- static createPaginatedResponse({ pageInfo, type }, items) {
800
- const nextCursor = PaginationManager.createCursor({ pageInfo, type });
801
- // Create previous page cursor if applicable
802
- let previousCursor = undefined;
803
- if (pageInfo.currentPage && pageInfo.currentPage > 1) {
804
- const prevCursorData = {
805
- type,
806
- pageNumber: pageInfo.currentPage - 1,
807
- pageSize: pageInfo.pageSize,
808
- };
809
- previousCursor = {
810
- value: encodeBase64(JSON.stringify(prevCursorData))
811
- };
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);
812
727
  }
813
- // Calculate total pages if we have totalCount and pageSize
814
- let totalPages = undefined;
815
- if (pageInfo.totalCount !== undefined && pageInfo.pageSize) {
816
- 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
+ };
817
740
  }
818
- // Determine if this pagination type supports page jumping
819
- const supportsPageJump = type === PaginationType.OFFSET;
820
- // Create the result object with all fields, then filter out undefined values
821
- const result = filterUndefined({
822
- items,
823
- totalCount: pageInfo.totalCount,
824
- hasNextPage: pageInfo.hasMore,
825
- nextCursor: nextCursor,
826
- previousCursor: previousCursor,
827
- currentPage: pageInfo.currentPage,
828
- totalPages,
829
- supportsPageJump
830
- });
831
- return result;
832
741
  }
833
742
  }
743
+ // Export singleton instance
744
+ const errorResponseParser = new ErrorResponseParser();
834
745
 
835
746
  /**
836
- * Creates headers object from key-value pairs
837
- * @param headersObj - Object containing header key-value pairs
838
- * @returns Headers object with all values converted to strings
839
- *
840
- * @example
841
- * ```typescript
842
- * // Single header
843
- * const headers = createHeaders({ 'X-UIPATH-FolderKey': '1234567890' });
844
- *
845
- * // Multiple headers
846
- * const headers = createHeaders({
847
- * 'X-UIPATH-FolderKey': '1234567890',
848
- * 'X-UIPATH-OrganizationUnitId': 123,
849
- * 'Accept': 'application/json'
850
- * });
851
- *
852
- * // Using constants
853
- * import { FOLDER_KEY, FOLDER_ID } from '../constants/headers';
854
- * const headers = createHeaders({
855
- * [FOLDER_KEY]: 'abc-123',
856
- * [FOLDER_ID]: 456
857
- * });
858
- *
859
- * // Empty headers
860
- * const headers = createHeaders();
861
- * ```
747
+ * Base error class for all UiPath SDK errors
748
+ * Extends Error for standard error handling compatibility
862
749
  */
863
- function createHeaders(headersObj) {
864
- const headers = {};
865
- for (const [key, value] of Object.entries(headersObj)) {
866
- if (value !== undefined && value !== null) {
867
- 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);
868
761
  }
869
762
  }
870
- 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
+ }
871
784
  }
872
785
 
873
786
  /**
874
- * 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
875
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
+
876
803
  /**
877
- * 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
878
809
  */
879
- const ODATA_PREFIX = '$';
880
- const UNKNOWN$1 = 'Unknown';
881
- 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
+
882
820
  /**
883
- * 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
884
826
  */
885
- const HTTP_METHODS = {
886
- GET: 'GET',
887
- POST: 'POST'};
888
- /**
889
- * Process Instance pagination constants for token-based pagination
890
- */
891
- const PROCESS_INSTANCE_PAGINATION = {
892
- /** Field name for items in process instance response */
893
- ITEMS_FIELD: 'instances',
894
- /** Field name for continuation token in process instance response */
895
- CONTINUATION_TOKEN_FIELD: 'nextPage'
896
- };
897
- /**
898
- * OData OFFSET pagination parameter names (ODATA-style)
899
- */
900
- const ODATA_OFFSET_PARAMS = {
901
- /** OData page size parameter name */
902
- PAGE_SIZE_PARAM: '$top',
903
- /** OData offset parameter name */
904
- OFFSET_PARAM: '$skip',
905
- /** OData count parameter name */
906
- COUNT_PARAM: '$count'
907
- };
908
- /**
909
- * Bucket TOKEN pagination parameter names
910
- */
911
- const BUCKET_TOKEN_PARAMS = {
912
- /** Bucket page size parameter name */
913
- PAGE_SIZE_PARAM: 'takeHint',
914
- /** Bucket token parameter name */
915
- TOKEN_PARAM: 'continuationToken'
916
- };
917
- /**
918
- * Process Instance TOKEN pagination parameter names
919
- */
920
- const PROCESS_INSTANCE_TOKEN_PARAMS = {
921
- /** Process instance page size parameter name */
922
- PAGE_SIZE_PARAM: 'pageSize',
923
- /** Process instance token parameter name */
924
- TOKEN_PARAM: 'nextPage'
925
- };
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
+ }
926
836
 
927
837
  /**
928
- * Converts a UTC timestamp string (e.g., "5/8/2026 11:20:17 AM") to ISO 8601 UTC format.
929
- * Returns the original value if parsing fails.
930
- */
931
- /**
932
- * Transforms data by mapping fields according to the provided field mapping
933
- * @param data The source data to transform
934
- * @param fieldMapping Object mapping source field names to target field names
935
- * @returns Transformed data with mapped field names
936
- *
937
- * @example
938
- * ```typescript
939
- * // Single object transformation
940
- * const data = { id: '123', userName: 'john' };
941
- * const mapping = { id: 'userId', userName: 'name' };
942
- * const result = transformData(data, mapping);
943
- * // result = { userId: '123', name: 'john' }
944
- *
945
- * // Array transformation
946
- * const dataArray = [
947
- * { id: '123', userName: 'john' },
948
- * { id: '456', userName: 'jane' }
949
- * ];
950
- * const result = transformData(dataArray, mapping);
951
- * // result = [
952
- * // { userId: '123', name: 'john' },
953
- * // { userId: '456', name: 'jane' }
954
- * // ]
955
- * ```
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
956
843
  */
957
- function transformData(data, fieldMapping) {
958
- // Handle array of objects
959
- if (Array.isArray(data)) {
960
- return data.map(item => transformData(item, fieldMapping));
961
- }
962
- // Handle single object
963
- const result = { ...data };
964
- for (const [sourceField, targetField] of Object.entries(fieldMapping)) {
965
- if (sourceField in result) {
966
- const value = result[sourceField];
967
- delete result[sourceField];
968
- result[targetField] = value;
969
- }
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
+ });
970
851
  }
971
- return result;
972
852
  }
853
+
973
854
  /**
974
- * Adds a prefix to specified keys in an object, returning a new object.
975
- * Only the provided keys are prefixed; all others are left unchanged.
976
- *
977
- * @param obj The source object
978
- * @param prefix The prefix to add (e.g., '$')
979
- * @param keys The keys to prefix (e.g., ['expand', 'filter'])
980
- * @returns A new object with specified keys prefixed
981
- *
982
- * @example
983
- * addPrefixToKeys({ expand: 'a', foo: 1 }, '$', ['expand']) // { $expand: 'a', foo: 1 }
855
+ * Error thrown when rate limit is exceeded (429 errors)
856
+ * Common scenarios:
857
+ * - Too many requests in a time window
858
+ * - API throttling
984
859
  */
985
- function addPrefixToKeys(obj, prefix, keys) {
986
- const result = {};
987
- for (const [key, value] of Object.entries(obj)) {
988
- if (keys.includes(key)) {
989
- result[`${prefix}${key}`] = value;
990
- }
991
- else {
992
- result[key] = value;
993
- }
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
+ });
994
867
  }
995
- return result;
996
868
  }
997
869
 
998
870
  /**
999
- * Constants used throughout the pagination system
871
+ * Error thrown when server encounters an error (5xx errors)
872
+ * Common scenarios:
873
+ * - Internal server error
874
+ * - Service unavailable
875
+ * - Gateway timeout
1000
876
  */
1001
- /** Maximum number of items that can be requested in a single page */
1002
- const MAX_PAGE_SIZE = 1000;
1003
- /** Default page size when jumpToPage is used without specifying pageSize */
1004
- const DEFAULT_PAGE_SIZE = 50;
1005
- /** Default field name for items in a paginated response */
1006
- const DEFAULT_ITEMS_FIELD = 'value';
1007
- /** Default field name for total count in a paginated response */
1008
- const DEFAULT_TOTAL_COUNT_FIELD = '@odata.count';
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;
892
+ }
893
+ }
894
+
1009
895
  /**
1010
- * Limits the page size to the maximum allowed value
1011
- * @param pageSize - Requested page size
1012
- * @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
1013
902
  */
1014
- function getLimitedPageSize(pageSize) {
1015
- if (pageSize === undefined || pageSize === null) {
1016
- 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
+ });
1017
910
  }
1018
- return Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
1019
911
  }
1020
912
 
1021
913
  /**
1022
- * 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
1023
916
  */
1024
- class PaginationHelpers {
917
+ class ErrorFactory {
1025
918
  /**
1026
- * Checks if any pagination parameters are provided
1027
- *
1028
- * @param options - The options object to check
1029
- * @returns True if any pagination parameter is defined, false otherwise
919
+ * Creates appropriate error instance based on HTTP status code
1030
920
  */
1031
- static hasPaginationParameters(options = {}) {
1032
- const { cursor, pageSize, jumpToPage } = options;
1033
- return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined;
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
+ });
946
+ }
1034
947
  }
1035
948
  /**
1036
- * Parse a pagination cursor string into cursor data
949
+ * Creates a NetworkError from a fetch/network error
1037
950
  */
1038
- static parseCursor(cursorString) {
1039
- try {
1040
- const cursorData = JSON.parse(decodeBase64(cursorString));
1041
- return cursorData;
1042
- }
1043
- catch {
1044
- throw new Error('Invalid pagination cursor');
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;
956
+ }
957
+ else if (error.message.includes('timeout')) {
958
+ message = ErrorMessages.REQUEST_TIMEOUT;
959
+ }
960
+ else {
961
+ message = error.message;
962
+ }
1045
963
  }
964
+ return new NetworkError({ message });
965
+ }
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;
1046
974
  }
1047
975
  /**
1048
- * Validates cursor format and structure
976
+ * Gets a valid authentication token, refreshing if necessary.
977
+ * Used internally for API requests and exposed for services that need manual auth headers.
1049
978
  *
1050
- * @param paginationOptions - The pagination options containing the cursor
1051
- * @param paginationType - Optional pagination type to validate against
979
+ * @returns The valid token
980
+ * @throws AuthenticationError if no token available or refresh fails
1052
981
  */
1053
- static validateCursor(paginationOptions, paginationType) {
1054
- if (paginationOptions.cursor !== undefined) {
1055
- if (!paginationOptions.cursor || typeof paginationOptions.cursor.value !== 'string' || !paginationOptions.cursor.value) {
1056
- throw new Error('cursor must contain a valid cursor string');
1057
- }
1058
- try {
1059
- // Try to parse the cursor to validate it
1060
- const cursorData = PaginationHelpers.parseCursor(paginationOptions.cursor.value);
1061
- // If type is provided, validate cursor contains expected type information
1062
- if (paginationType) {
1063
- if (!cursorData.type) {
1064
- throw new Error('Invalid cursor: missing pagination type');
1065
- }
1066
- // Check pagination type compatibility
1067
- if (cursorData.type !== paginationType) {
1068
- throw new Error(`Pagination type mismatch: cursor is for ${cursorData.type} but service uses ${paginationType}`);
1069
- }
1070
- }
1071
- }
1072
- catch (error) {
1073
- if (error instanceof Error) {
1074
- // If it's already our error with specific message, pass it through
1075
- if (error.message.startsWith('Invalid cursor') ||
1076
- error.message.startsWith('Pagination type mismatch')) {
1077
- throw error;
1078
- }
1079
- }
1080
- throw new Error('Invalid pagination cursor format');
1081
- }
1082
- }
982
+ async getValidToken() {
983
+ return this.tokenManager.getValidToken();
1083
984
  }
1084
- /**
1085
- * Comprehensive validation for pagination options
1086
- *
1087
- * @param options - The pagination options to validate
1088
- * @param paginationType - The pagination type these options will be used with
1089
- * @returns Processed pagination parameters ready for use
1090
- */
1091
- static validatePaginationOptions(options, paginationType) {
1092
- // Validate pageSize
1093
- if (options.pageSize !== undefined && options.pageSize <= 0) {
1094
- throw new Error('pageSize must be a positive number');
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'];
1095
1002
  }
1096
- // Validate jumpToPage
1097
- if (options.jumpToPage !== undefined && options.jumpToPage <= 0) {
1098
- 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
+ });
1099
1018
  }
1100
- // Validate cursor
1101
- PaginationHelpers.validateCursor(options, paginationType);
1102
- // Validate service compatibility
1103
- if (options.jumpToPage !== undefined && paginationType === PaginationType.TOKEN) {
1104
- 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);
1105
1023
  }
1106
- // Get processed parameters
1107
- return PaginationHelpers.getRequestParameters(options, paginationType);
1108
- }
1109
- /**
1110
- * Convert a unified pagination options to service-specific parameters
1111
- */
1112
- static getRequestParameters(options, paginationType) {
1113
- // Handle jumpToPage
1114
- if (options.jumpToPage !== undefined) {
1115
- const jumpToPageOptions = {
1116
- pageSize: options.pageSize,
1117
- pageNumber: options.jumpToPage
1118
- };
1119
- 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);
1120
1054
  }
1121
- // If no cursor is provided, it's a first page request
1122
- if (!options.cursor) {
1123
- const firstPageOptions = {
1124
- pageSize: options.pageSize,
1125
- // Only set pageNumber for OFFSET pagination
1126
- pageNumber: paginationType === PaginationType.OFFSET ? 1 : undefined
1127
- };
1128
- 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);
1129
1062
  }
1130
- // Parse the cursor
1131
- try {
1132
- const cursorData = PaginationHelpers.parseCursor(options.cursor.value);
1133
- const cursorBasedOptions = {
1134
- pageSize: cursorData.pageSize || options.pageSize,
1135
- pageNumber: cursorData.pageNumber,
1136
- continuationToken: cursorData.continuationToken,
1137
- type: cursorData.type,
1138
- };
1139
- 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;
1140
1137
  }
1141
- catch {
1142
- 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);
1143
1188
  }
1144
1189
  }
1145
- /**
1146
- * Helper method for paginated resource retrieval
1147
- *
1148
- * @param params - Parameters for pagination
1149
- * @returns Promise resolving to a paginated result
1150
- */
1151
- static async getAllPaginated(params) {
1152
- const { serviceAccess, getEndpoint, folderId, paginationParams, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1153
- const endpoint = getEndpoint(folderId);
1154
- const headers = folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {};
1155
- const paginatedResponse = await serviceAccess.requestWithPagination(method, endpoint, paginationParams, {
1156
- headers,
1157
- params: additionalParams,
1158
- pagination: {
1159
- paginationType: options.paginationType || PaginationType.OFFSET,
1160
- itemsField: options.itemsField || DEFAULT_ITEMS_FIELD,
1161
- totalCountField: options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD,
1162
- continuationTokenField: options.continuationTokenField,
1163
- paginationParams: options.paginationParams
1164
- }
1165
- });
1166
- // Parse items - automatically handle JSON string responses
1167
- const rawItems = paginatedResponse.items;
1168
- const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1169
- const transformedItems = transformFn ? parsedItems.map(transformFn) : parsedItems;
1170
- return {
1171
- ...paginatedResponse,
1172
- items: transformedItems
1173
- };
1190
+ else {
1191
+ // Node.js environment
1192
+ bytes = new Uint8Array(Buffer.from(base64, 'base64'));
1174
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 {
1175
1204
  /**
1176
- * Helper method for non-paginated resource retrieval
1177
- *
1178
- * @param params - Parameters for non-paginated resource retrieval
1179
- * @returns Promise resolving to an object with data and totalCount
1205
+ * Create a pagination cursor for subsequent page requests
1180
1206
  */
1181
- static async getAllNonPaginated(params) {
1182
- const { serviceAccess, getAllEndpoint, getByFolderEndpoint, folderId, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1183
- // Set default field names
1184
- const itemsField = options.itemsField || DEFAULT_ITEMS_FIELD;
1185
- const totalCountField = options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD;
1186
- // Determine endpoint and headers based on folderId
1187
- const endpoint = folderId ? getByFolderEndpoint : getAllEndpoint;
1188
- const headers = folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {};
1189
- // Make the API call based on method
1190
- let response;
1191
- if (method === HTTP_METHODS.POST) {
1192
- response = await serviceAccess.post(endpoint, additionalParams, { headers });
1207
+ static createCursor({ pageInfo, type }) {
1208
+ if (!pageInfo.hasMore) {
1209
+ return undefined;
1193
1210
  }
1194
- else {
1195
- response = await serviceAccess.get(endpoint, {
1196
- params: additionalParams,
1197
- headers
1198
- });
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;
1199
1229
  }
1200
- // Extract and transform items from response
1201
- // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1202
- const rawItems = Array.isArray(response.data) ? response.data : response.data?.[itemsField];
1203
- const rawTotalCount = Array.isArray(response.data) ? undefined : resolveNestedField(response.data, totalCountField);
1204
- const totalCount = typeof rawTotalCount === 'number' ? rawTotalCount : undefined;
1205
- // Parse items - automatically handle JSON string responses
1206
- const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1207
- const items = transformFn ? parsedItems.map(transformFn) : parsedItems;
1208
1230
  return {
1209
- items,
1210
- totalCount
1231
+ value: encodeBase64(JSON.stringify(cursorData))
1211
1232
  };
1212
1233
  }
1213
1234
  /**
1214
- * Centralized getAll implementation that handles both paginated and non-paginated requests
1215
- *
1216
- * @param config - Configuration for the getAll operation
1217
- * @param options - Request options including pagination parameters
1218
- * @returns Promise resolving to either paginated or non-paginated response based on options
1235
+ * Create a paginated response with navigation cursors
1219
1236
  */
1220
- static async getAll(config, options) {
1221
- const optionsWithDefaults = options || {};
1222
- const { folderId, pageSize, cursor, jumpToPage, ...restOptions } = optionsWithDefaults;
1223
- // Determine if pagination is requested
1224
- const isPaginationRequested = PaginationHelpers.hasPaginationParameters(options || {});
1225
- // Process parameters (custom processing if provided, otherwise default)
1226
- let processedOptions = restOptions;
1227
- if (config.processParametersFn) {
1228
- 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
+ };
1229
1250
  }
1230
- // Apply ODATA prefix to keys (excluding specified keys)
1231
- const excludeKeys = config.excludeFromPrefix || [];
1232
- const keysToPrefix = Object.keys(processedOptions).filter(k => !excludeKeys.includes(k));
1233
- const prefixedOptions = addPrefixToKeys(processedOptions, ODATA_PREFIX, keysToPrefix);
1234
- // Default pagination options
1235
- const paginationOptions = {
1236
- paginationType: PaginationType.OFFSET,
1237
- itemsField: DEFAULT_ITEMS_FIELD,
1238
- totalCountField: DEFAULT_TOTAL_COUNT_FIELD,
1239
- ...config.pagination
1240
- };
1241
- // Paginated flow
1242
- if (isPaginationRequested) {
1243
- return PaginationHelpers.getAllPaginated({
1244
- serviceAccess: config.serviceAccess,
1245
- getEndpoint: config.getEndpoint,
1246
- folderId,
1247
- paginationParams: cursor ? { cursor, pageSize } : jumpToPage ? { jumpToPage, pageSize } : { pageSize },
1248
- additionalParams: prefixedOptions,
1249
- transformFn: config.transformFn,
1250
- method: config.method,
1251
- options: {
1252
- ...paginationOptions,
1253
- paginationParams: config.pagination?.paginationParams
1254
- }
1255
- }); // 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);
1256
1255
  }
1257
- // Non-paginated flow
1258
- const byFolderEndpoint = config.getByFolderEndpoint || config.getEndpoint(folderId);
1259
- return PaginationHelpers.getAllNonPaginated({
1260
- serviceAccess: config.serviceAccess,
1261
- getAllEndpoint: config.getEndpoint(),
1262
- getByFolderEndpoint: byFolderEndpoint,
1263
- folderId,
1264
- additionalParams: prefixedOptions,
1265
- transformFn: config.transformFn,
1266
- method: config.method,
1267
- options: {
1268
- itemsField: paginationOptions.itemsField,
1269
- totalCountField: paginationOptions.totalCountField
1270
- }
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
1271
1268
  });
1269
+ return result;
1272
1270
  }
1273
1271
  }
1274
1272
 
1275
1273
  /**
1276
- * SDK Internals Registry - Internal registry for SDK instances
1277
- *
1278
- * This class is NOT exported in the public API.
1279
- * It provides a secure way to share SDK internals between
1280
- * the UiPath class and service classes without exposing them publicly.
1281
- *
1282
- * @internal
1283
- */
1284
- // Global symbol key to ensure WeakMap is shared across module instances
1285
- // This prevents issues when core and service modules are bundled separately
1286
- const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
1287
- // Get or create the global WeakMap store
1288
- const getGlobalStore = () => {
1289
- const globalObj = globalThis;
1290
- if (!globalObj[REGISTRY_KEY]) {
1291
- globalObj[REGISTRY_KEY] = new WeakMap();
1292
- }
1293
- return globalObj[REGISTRY_KEY];
1294
- };
1295
- /**
1296
- * Internal registry for SDK private components.
1297
- * Uses WeakMap to prevent memory leaks - entries are automatically
1298
- * garbage collected when the SDK instance is no longer referenced.
1299
- *
1300
- * Uses a global singleton pattern to ensure the same WeakMap is shared
1301
- * across separately bundled modules (core, entities, tasks, etc.).
1302
- *
1303
- * @internal - Not exported in public API
1274
+ * Constants used throughout the pagination system
1304
1275
  */
1305
- class SDKInternalsRegistry {
1306
- // Use global store to ensure sharing across module bundles
1307
- static get store() {
1308
- return getGlobalStore();
1309
- }
1310
- /**
1311
- * Register SDK instance internals
1312
- * Called by UiPath constructor
1313
- */
1314
- static set(instance, internals) {
1315
- this.store.set(instance, internals);
1316
- }
1317
- /**
1318
- * Retrieve SDK instance internals
1319
- * Called by BaseService constructor
1320
- */
1321
- static get(instance) {
1322
- const internals = this.store.get(instance);
1323
- if (!internals) {
1324
- throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
1325
- }
1326
- return internals;
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;
1327
1292
  }
1293
+ return Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
1328
1294
  }
1329
1295
 
1330
- var _BaseService_apiClient;
1331
1296
  /**
1332
- * Base class for all UiPath SDK services.
1333
- *
1334
- * Provides common functionality for authentication, configuration, and API communication.
1335
- * All service classes extend this base to inherit dependency injection and HTTP client access.
1336
- *
1337
- * This class implements the dependency injection pattern where services receive a configured
1338
- * UiPath instance. The ApiClient is created internally and handles all HTTP operations
1339
- * including authentication token management.
1340
- *
1341
- * @remarks
1342
- * Service classes should extend this base and call `super(uiPath)` in their constructor.
1343
- * Protected HTTP methods (get, post, put, patch, delete) are available to all subclasses.
1344
- *
1297
+ * Helper functions for pagination that can be used across services
1345
1298
  */
1346
- class BaseService {
1299
+ class PaginationHelpers {
1347
1300
  /**
1348
- * Creates a base service instance with dependency injection.
1349
- *
1350
- * Extracts configuration, execution context, and token manager from the UiPath instance
1351
- * to initialize an authenticated API client. The ApiClient handles all HTTP operations
1352
- * and token management internally.
1353
- *
1354
- * @param instance - UiPath SDK instance providing authentication and configuration.
1355
- * Services receive this via dependency injection in the modular pattern.
1356
- * @param headers - Optional default headers to include in every request (e.g. `x-uipath-external-user-id` for
1357
- * CAS external-app auth)
1358
- *
1359
- * @example
1360
- * ```typescript
1361
- * // Services automatically call this via super()
1362
- * export class EntityService extends BaseService {
1363
- * constructor(instance: IUiPath) {
1364
- * super(instance); // Initializes the internal ApiClient
1365
- * }
1366
- * }
1367
- *
1368
- * // Usage in modular pattern
1369
- * import { UiPath } from '@uipath/uipath-typescript/core';
1370
- * import { Entities } from '@uipath/uipath-typescript/entities';
1301
+ * Checks if any pagination parameters are provided
1371
1302
  *
1372
- * const sdk = new UiPath(config);
1373
- * await sdk.initialize();
1374
- * const entities = new Entities(sdk);
1375
- * ```
1303
+ * @param options - The options object to check
1304
+ * @returns True if any pagination parameter is defined, false otherwise
1376
1305
  */
1377
- constructor(instance, headers) {
1378
- // Private field - not visible via Object.keys() or any reflection
1379
- _BaseService_apiClient.set(this, void 0);
1380
- const { config, context, tokenManager, folderKey } = SDKInternalsRegistry.get(instance);
1381
- __classPrivateFieldSet(this, _BaseService_apiClient, new ApiClient(config, context, tokenManager, headers ? { headers } : {}), "f");
1382
- this.config = { folderKey };
1306
+ static hasPaginationParameters(options = {}) {
1307
+ const { cursor, pageSize, jumpToPage } = options;
1308
+ return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined;
1383
1309
  }
1384
1310
  /**
1385
- * Gets a valid authentication token, refreshing if necessary.
1386
- * Use this when you need to manually add Authorization headers (e.g., direct uploads).
1387
- *
1388
- * @returns Promise resolving to a valid access token string
1389
- * @throws AuthenticationError if no token is available or refresh fails
1311
+ * Parse a pagination cursor string into cursor data
1390
1312
  */
1391
- async getValidAuthToken() {
1392
- return __classPrivateFieldGet(this, _BaseService_apiClient, "f").getValidToken();
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');
1320
+ }
1393
1321
  }
1394
1322
  /**
1395
- * Creates a service accessor for pagination helpers
1396
- * This allows pagination helpers to access protected methods without making them public
1323
+ * Validates cursor format and structure
1324
+ *
1325
+ * @param paginationOptions - The pagination options containing the cursor
1326
+ * @param paginationType - Optional pagination type to validate against
1397
1327
  */
1398
- createPaginationServiceAccess() {
1399
- return {
1400
- get: (path, options) => this.get(path, options || {}),
1401
- post: (path, body, options) => this.post(path, body, options || {}),
1402
- requestWithPagination: (method, path, paginationOptions, options) => this.requestWithPagination(method, path, paginationOptions, options)
1403
- };
1404
- }
1405
- async request(method, path, options = {}) {
1406
- switch (method.toUpperCase()) {
1407
- case 'GET':
1408
- return this.get(path, options);
1409
- case 'POST':
1410
- return this.post(path, options.body, options);
1411
- case 'PUT':
1412
- return this.put(path, options.body, options);
1413
- case 'PATCH':
1414
- return this.patch(path, options.body, options);
1415
- case 'DELETE':
1416
- return this.delete(path, options);
1417
- default:
1418
- throw new Error(`Unsupported HTTP method: ${method}`);
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
+ }
1419
1357
  }
1420
1358
  }
1421
- async requestWithSpec(spec) {
1422
- if (!spec.method || !spec.url) {
1423
- throw new Error('Request spec must include method and url');
1359
+ /**
1360
+ * Comprehensive validation for pagination options
1361
+ *
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
1365
+ */
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');
1424
1370
  }
1425
- return this.request(spec.method, spec.url, spec);
1426
- }
1427
- async get(path, options = {}) {
1428
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").get(path, options);
1429
- return { data: response };
1430
- }
1431
- async post(path, data, options = {}) {
1432
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").post(path, data, options);
1433
- return { data: response };
1434
- }
1435
- async put(path, data, options = {}) {
1436
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").put(path, data, options);
1437
- return { data: response };
1438
- }
1439
- async patch(path, data, options = {}) {
1440
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").patch(path, data, options);
1441
- return { data: response };
1442
- }
1443
- async delete(path, options = {}) {
1444
- const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").delete(path, options);
1445
- return { data: response };
1371
+ // Validate jumpToPage
1372
+ if (options.jumpToPage !== undefined && options.jumpToPage <= 0) {
1373
+ throw new Error('jumpToPage must be a positive number');
1374
+ }
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);
1446
1383
  }
1447
1384
  /**
1448
- * Execute a request with cursor-based pagination
1385
+ * Convert a unified pagination options to service-specific parameters
1449
1386
  */
1450
- async requestWithPagination(method, path, paginationOptions, options) {
1451
- const paginationType = options.pagination.paginationType;
1452
- // Validate and prepare pagination parameters
1453
- const params = this.validateAndPreparePaginationParams(paginationType, paginationOptions);
1454
- // Prepare request parameters based on pagination type
1455
- const requestParams = this.preparePaginationRequestParams(paginationType, params, options.pagination);
1456
- // For POST requests, merge pagination params into body and set params to undefined; for GET, use query params
1457
- if (method.toUpperCase() === 'POST') {
1458
- const existingBody = (options.body && typeof options.body === 'object') ? options.body : {};
1459
- options.body = {
1460
- ...existingBody,
1461
- ...options.params,
1462
- ...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
1463
1393
  };
1464
- options.params = undefined;
1394
+ return filterUndefined(jumpToPageOptions);
1465
1395
  }
1466
- else {
1467
- // Merge pagination parameters with existing parameters
1468
- options.params = {
1469
- ...options.params,
1470
- ...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
1471
1402
  };
1403
+ return filterUndefined(firstPageOptions);
1472
1404
  }
1473
- // Make the request
1474
- const response = await this.request(method, path, options);
1475
- // Extract data from the response and create page result
1476
- return this.createPaginatedResponseFromResponse(response, params, paginationType, {
1477
- itemsField: options.pagination.itemsField,
1478
- totalCountField: options.pagination.totalCountField,
1479
- continuationTokenField: options.pagination.continuationTokenField
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,
1413
+ };
1414
+ return filterUndefined(cursorBasedOptions);
1415
+ }
1416
+ catch {
1417
+ throw new Error('Invalid pagination cursor');
1418
+ }
1419
+ }
1420
+ /**
1421
+ * Helper method for paginated resource retrieval
1422
+ *
1423
+ * @param params - Parameters for pagination
1424
+ * @returns Promise resolving to a paginated result
1425
+ */
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
+ }
1480
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
+ };
1481
1449
  }
1482
1450
  /**
1483
- * Validates and prepares pagination parameters from options
1484
- */
1485
- validateAndPreparePaginationParams(paginationType, paginationOptions) {
1486
- return PaginationHelpers.validatePaginationOptions(paginationOptions, paginationType);
1487
- }
1488
- /**
1489
- * 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
1490
1455
  */
1491
- preparePaginationRequestParams(paginationType, params, paginationConfig) {
1492
- const requestParams = {};
1493
- let limitedPageSize;
1494
- const paginationParams = paginationConfig?.paginationParams;
1495
- switch (paginationType) {
1496
- case PaginationType.OFFSET:
1497
- limitedPageSize = getLimitedPageSize(params.pageSize);
1498
- const pageSizeParam = paginationParams?.pageSizeParam || ODATA_OFFSET_PARAMS.PAGE_SIZE_PARAM;
1499
- const offsetParam = paginationParams?.offsetParam || ODATA_OFFSET_PARAMS.OFFSET_PARAM;
1500
- const countParam = paginationParams?.countParam || ODATA_OFFSET_PARAMS.COUNT_PARAM;
1501
- // When true (default), converts pageNumber to a skip/offset value (e.g., page 3 with pageSize 10 → skip 20).
1502
- // When false, passes pageNumber directly as the offset param — used by APIs that accept a page number instead of a record offset.
1503
- const convertToSkip = paginationParams?.convertToSkip ?? true;
1504
- requestParams[pageSizeParam] = limitedPageSize;
1505
- if (convertToSkip) {
1506
- if (params.pageNumber && params.pageNumber > 1) {
1507
- requestParams[offsetParam] = (params.pageNumber - 1) * limitedPageSize;
1508
- }
1509
- }
1510
- else {
1511
- requestParams[offsetParam] = params.pageNumber || 1;
1512
- }
1513
- {
1514
- requestParams[countParam] = true;
1515
- }
1516
- break;
1517
- case PaginationType.TOKEN:
1518
- const tokenPageSizeParam = paginationParams?.pageSizeParam || BUCKET_TOKEN_PARAMS.PAGE_SIZE_PARAM;
1519
- const tokenParam = paginationParams?.tokenParam || BUCKET_TOKEN_PARAMS.TOKEN_PARAM;
1520
- if (params.pageSize) {
1521
- requestParams[tokenPageSizeParam] = getLimitedPageSize(params.pageSize);
1522
- }
1523
- if (params.continuationToken) {
1524
- requestParams[tokenParam] = params.continuationToken;
1525
- }
1526
- 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 });
1527
1468
  }
1528
- return requestParams;
1529
- }
1530
- /**
1531
- * Creates a paginated response from API response
1532
- */
1533
- createPaginatedResponseFromResponse(response, params, paginationType, fields) {
1534
- // Extract fields from response
1535
- const itemsField = fields.itemsField ||
1536
- (paginationType === PaginationType.TOKEN ? 'items' : 'value');
1537
- const totalCountField = fields.totalCountField || 'totalRecordCount';
1538
- const continuationTokenField = fields.continuationTokenField || 'continuationToken';
1539
- // 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
1540
1476
  // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1541
- const items = Array.isArray(response.data) ? response.data : (response.data[itemsField] || []);
1477
+ const rawItems = Array.isArray(response.data) ? response.data : response.data?.[itemsField];
1542
1478
  const rawTotalCount = Array.isArray(response.data) ? undefined : resolveNestedField(response.data, totalCountField);
1543
1479
  const totalCount = typeof rawTotalCount === 'number' ? rawTotalCount : undefined;
1544
- const continuationToken = response.data[continuationTokenField];
1545
- // Determine if there are more pages
1546
- const hasMore = this.determineHasMorePages(paginationType, {
1547
- totalCount,
1548
- pageSize: params.pageSize,
1549
- currentPage: params.pageNumber || 1,
1550
- itemsCount: items.length,
1551
- continuationToken
1552
- });
1553
- // Create and return the page result
1554
- return PaginationManager.createPaginatedResponse({
1555
- pageInfo: {
1556
- hasMore,
1557
- totalCount,
1558
- currentPage: params.pageNumber,
1559
- pageSize: params.pageSize,
1560
- continuationToken
1561
- },
1562
- type: paginationType,
1563
- }, items);
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
+ };
1564
1487
  }
1565
1488
  /**
1566
- * 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
1567
1494
  */
1568
- determineHasMorePages(paginationType, info) {
1569
- switch (paginationType) {
1570
- case PaginationType.OFFSET:
1571
- const effectivePageSize = info.pageSize ?? DEFAULT_PAGE_SIZE;
1572
- // If totalCount is available, use it for precise calculation
1573
- if (info.totalCount !== undefined) {
1574
- return (info.currentPage * effectivePageSize) < info.totalCount;
1575
- }
1576
- // Fallback when totalCount is not available
1577
- // NOTE: This code path should rarely be executed as the APIs typically return totalCount
1578
- return info.itemsCount === effectivePageSize;
1579
- case PaginationType.TOKEN:
1580
- return !!info.continuationToken;
1581
- default:
1582
- 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);
1583
1504
  }
1584
- }
1585
- }
1586
- _BaseService_apiClient = new WeakMap();
1587
-
1588
- /**
1589
- * Base path constants for different services
1590
- */
1591
- const PIMS_BASE = 'pims_';
1592
-
1593
- /**
1594
- * Maestro Service Endpoints
1595
- */
1596
- /**
1597
- * Maestro Process Service Endpoints
1598
- */
1599
- const MAESTRO_ENDPOINTS = {
1600
- PROCESSES: {
1601
- GET_ALL: `${PIMS_BASE}/api/v1/processes/summary`},
1602
- INSTANCES: {
1603
- GET_ALL: `${PIMS_BASE}/api/v1/instances`,
1604
- GET_BY_ID: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}`,
1605
- GET_EXECUTION_HISTORY: (instanceId) => `${PIMS_BASE}/api/v1/spans/${instanceId}`,
1606
- GET_BPMN: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/bpmn`,
1607
- GET_VARIABLES: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/variables`,
1608
- CANCEL: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/cancel`,
1609
- PAUSE: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/pause`,
1610
- RESUME: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/resume`,
1611
- },
1612
- INCIDENTS: {
1613
- GET_ALL: `${PIMS_BASE}/api/v1/incidents/summary`,
1614
- GET_BY_PROCESS: (processKey) => `${PIMS_BASE}/api/v1/incidents/process/${processKey}`,
1615
- GET_BY_INSTANCE: (instanceId) => `${PIMS_BASE}/api/v1/instances/${instanceId}/incidents`,
1616
- }};
1617
-
1618
- /**
1619
- * Maestro Process Models
1620
- * Model classes for Maestro processes
1621
- */
1622
- /**
1623
- * Creates methods for a process object
1624
- *
1625
- * @param processData - The process data (response from API)
1626
- * @param service - The process service instance
1627
- * @returns Object containing process methods
1628
- */
1629
- function createProcessMethods(processData, service) {
1630
- return {
1631
- async getIncidents() {
1632
- if (!processData.processKey)
1633
- throw new Error('Process key is undefined');
1634
- if (!processData.folderKey)
1635
- throw new Error('Folder key is undefined');
1636
- 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
1637
1531
  }
1638
- };
1639
- }
1640
- /**
1641
- * Creates an actionable process by combining API process data with operational methods.
1642
- *
1643
- * @param processData - The process data from API
1644
- * @param service - The process service instance
1645
- * @returns A process object with added methods
1646
- */
1647
- function createProcessWithMethods(processData, service) {
1648
- const methods = createProcessMethods(processData, service);
1649
- 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
+ }
1650
1548
  }
1651
1549
 
1652
1550
  /**
1653
- * 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
1654
1558
  */
1655
- const ProcessIncidentMap = {
1656
- 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];
1657
1569
  };
1658
1570
  /**
1659
- * 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
1660
1579
  */
1661
- const ProcessIncidentSummaryMap = {
1662
- firstTimeUtc: 'firstOccuranceTime'
1663
- };
1664
-
1665
- /**
1666
- * Helpers for fetching BPMN XML and extracting element details used to annotate responses
1667
- */
1668
- class BpmnHelpers {
1669
- /**
1670
- * Parse BPMN XML and extract element id → {name,type} used for incidents
1671
- */
1672
- static parseBpmnElementsForIncidents(bpmnXml) {
1673
- const elementInfo = {};
1674
- try {
1675
- // Find <bpmn:...> start tags and capture the element type.
1676
- // Then read 'id' and 'name' attributes from each tag.
1677
- const bpmnOpenTagRegex = /<bpmn:([A-Za-z][\w.-]*)\b[^>]*>/g;
1678
- for (const tagMatch of bpmnXml.matchAll(bpmnOpenTagRegex)) {
1679
- const [fullTag, elementType] = tagMatch;
1680
- // Extract attributes from the current tag text.
1681
- const idMatch = /\bid\s*=\s*"([^"]*)"/.exec(fullTag);
1682
- if (!idMatch) {
1683
- continue;
1684
- }
1685
- const elementId = idMatch[1];
1686
- const nameMatch = /\bname\s*=\s*"([^"]*)"/.exec(fullTag);
1687
- const name = nameMatch ? nameMatch[1] : '';
1688
- // Convert BPMN element type to human-readable format
1689
- const activityType = this.formatActivityTypeForIncidents(elementType);
1690
- const activityName = name || elementId;
1691
- elementInfo[elementId] = {
1692
- type: activityType,
1693
- name: activityName
1694
- };
1695
- }
1696
- }
1697
- catch (error) {
1698
- console.warn('Failed to parse BPMN XML for incidents:', error);
1699
- }
1700
- return elementInfo;
1580
+ class SDKInternalsRegistry {
1581
+ // Use global store to ensure sharing across module bundles
1582
+ static get store() {
1583
+ return getGlobalStore();
1701
1584
  }
1702
1585
  /**
1703
- * Format BPMN element type to human-readable activity type for incidents
1586
+ * Register SDK instance internals
1587
+ * Called by UiPath constructor
1704
1588
  */
1705
- static formatActivityTypeForIncidents(elementType) {
1706
- // Convert camelCase BPMN element types to human-readable format
1707
- // e.g., "serviceTask" -> "Service Task", "exclusiveGateway" -> "Exclusive Gateway"
1708
- return elementType
1709
- .replace(/([A-Z])/g, ' $1') // Add space before uppercase letters
1710
- .replace(/^./, str => str.toUpperCase()) // Capitalize first letter
1711
- .trim(); // Remove any leading/trailing spaces
1589
+ static set(instance, internals) {
1590
+ this.store.set(instance, internals);
1712
1591
  }
1713
1592
  /**
1714
- * Fetch BPMN via getBpmn and add element name/type to each incident
1593
+ * Retrieve SDK instance internals
1594
+ * Called by BaseService constructor
1715
1595
  */
1716
- static async enrichIncidentsWithBpmnData(incidents, folderKey, service) {
1717
- // Check if all incidents have the same instanceId
1718
- const uniqueInstanceIds = [...new Set(incidents.map(i => i.instanceId))];
1719
- if (uniqueInstanceIds.length === 1) {
1720
- // Single instance optimization (in case of process instance incidents)
1721
- const elementInfo = await this.getBpmnElementInfo(uniqueInstanceIds[0], folderKey, service);
1722
- return incidents.map((incident) => this.transformIncidentWithBpmn(incident, elementInfo));
1723
- }
1724
- else {
1725
- // Multiple instances optimization (in case of process incidents)
1726
- 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.');
1727
1600
  }
1601
+ return internals;
1728
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 {
1729
1622
  /**
1730
- * 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
+ * ```
1731
1651
  */
1732
- static async enrichMultipleInstanceIncidents(incidents, folderKey, service) {
1733
- const groups = incidents.reduce((acc, incident) => {
1734
- const id = incident.instanceId || NO_INSTANCE;
1735
- (acc[id] = acc[id] || []).push(incident);
1736
- return acc;
1737
- }, {});
1738
- const results = await Promise.all(Object.entries(groups).map(async (entry) => {
1739
- const [instanceId, groupIncidents] = entry;
1740
- const elementInfo = await this.getBpmnElementInfo(instanceId, folderKey, service);
1741
- return groupIncidents.map((incident) => this.transformIncidentWithBpmn(incident, elementInfo));
1742
- }));
1743
- 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 };
1744
1658
  }
1745
1659
  /**
1746
- * 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
1747
1665
  */
1748
- static async getBpmnElementInfo(instanceId, folderKey, service) {
1749
- if (!instanceId || instanceId === NO_INSTANCE) {
1750
- return {};
1751
- }
1752
- try {
1753
- const bpmnXml = await service.getBpmn(instanceId, folderKey);
1754
- return this.parseBpmnElementsForIncidents(bpmnXml);
1755
- }
1756
- catch (error) {
1757
- console.warn(`Failed to get BPMN for instance ${instanceId}:`, error);
1758
- return {};
1759
- }
1666
+ async getValidAuthToken() {
1667
+ return __classPrivateFieldGet(this, _BaseService_apiClient, "f").getValidToken();
1760
1668
  }
1761
1669
  /**
1762
- * 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
1763
1672
  */
1764
- static transformIncidentWithBpmn(incident, elementInfo) {
1765
- const element = elementInfo[incident.elementId];
1766
- const transformed = transformData(incident, ProcessIncidentMap);
1673
+ createPaginationServiceAccess() {
1767
1674
  return {
1768
- ...transformed,
1769
- incidentElementActivityType: element?.type || UNKNOWN$1,
1770
- 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)
1771
1678
  };
1772
1679
  }
1773
- }
1774
-
1775
- /**
1776
- * SDK Telemetry constants
1777
- */
1778
- // Connection string placeholder that will be replaced during build
1779
- 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";
1780
- // SDK Version placeholder
1781
- const SDK_VERSION = "1.3.8";
1782
- const VERSION = "Version";
1783
- const SERVICE = "Service";
1784
- const CLOUD_ORGANIZATION_NAME = "CloudOrganizationName";
1785
- const CLOUD_TENANT_NAME = "CloudTenantName";
1786
- const CLOUD_URL = "CloudUrl";
1787
- const CLOUD_CLIENT_ID = "CloudClientId";
1788
- const CLOUD_REDIRECT_URI = "CloudRedirectUri";
1789
- const APP_NAME = "ApplicationName";
1790
- const CLOUD_ROLE_NAME = "uipath-ts-sdk";
1791
- // Service and logger names
1792
- const SDK_SERVICE_NAME = "UiPath.TypeScript.Sdk";
1793
- const SDK_LOGGER_NAME = "uipath-ts-sdk-telemetry";
1794
- // Event names
1795
- const SDK_RUN_EVENT = "Sdk.Run";
1796
- // Default value for unknown/empty attributes
1797
- const UNKNOWN = "";
1798
-
1799
- /**
1800
- * Log exporter that sends ALL logs as Application Insights custom events
1801
- */
1802
- class ApplicationInsightsEventExporter {
1803
- constructor(connectionString) {
1804
- this.connectionString = connectionString;
1805
- }
1806
- export(logs, resultCallback) {
1807
- try {
1808
- logs.forEach(logRecord => {
1809
- this.sendAsCustomEvent(logRecord);
1810
- });
1811
- resultCallback({ code: 0 });
1812
- }
1813
- catch (error) {
1814
- console.debug('Failed to export logs to Application Insights:', error);
1815
- 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}`);
1816
1694
  }
1817
1695
  }
1818
- shutdown() {
1819
- return Promise.resolve();
1820
- }
1821
- sendAsCustomEvent(logRecord) {
1822
- // Get event name from body or attributes
1823
- const eventName = logRecord.body || SDK_RUN_EVENT;
1824
- const payload = {
1825
- name: 'Microsoft.ApplicationInsights.Event',
1826
- time: new Date().toISOString(),
1827
- iKey: this.extractInstrumentationKey(),
1828
- data: {
1829
- baseType: 'EventData',
1830
- baseData: {
1831
- ver: 2,
1832
- name: eventName,
1833
- properties: this.convertAttributesToProperties(logRecord.attributes || {})
1834
- }
1835
- },
1836
- tags: {
1837
- 'ai.cloud.role': CLOUD_ROLE_NAME,
1838
- 'ai.cloud.roleInstance': SDK_VERSION
1839
- }
1840
- };
1841
- this.sendToApplicationInsights(payload);
1842
- }
1843
- extractInstrumentationKey() {
1844
- const match = this.connectionString.match(/InstrumentationKey=([^;]+)/);
1845
- return match ? match[1] : '';
1696
+ async requestWithSpec(spec) {
1697
+ if (!spec.method || !spec.url) {
1698
+ throw new Error('Request spec must include method and url');
1699
+ }
1700
+ return this.request(spec.method, spec.url, spec);
1846
1701
  }
1847
- convertAttributesToProperties(attributes) {
1848
- const properties = {};
1849
- Object.entries(attributes || {}).forEach(([key, value]) => {
1850
- properties[key] = String(value);
1851
- });
1852
- return properties;
1702
+ async get(path, options = {}) {
1703
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").get(path, options);
1704
+ return { data: response };
1853
1705
  }
1854
- async sendToApplicationInsights(payload) {
1855
- try {
1856
- const ingestionEndpoint = this.extractIngestionEndpoint();
1857
- if (!ingestionEndpoint) {
1858
- console.debug('No ingestion endpoint found in connection string');
1859
- return;
1860
- }
1861
- const url = `${ingestionEndpoint}/v2/track`;
1862
- const response = await fetch(url, {
1863
- method: 'POST',
1864
- headers: {
1865
- 'Content-Type': 'application/json',
1866
- },
1867
- body: JSON.stringify(payload)
1868
- });
1869
- if (!response.ok) {
1870
- console.debug(`Failed to send event telemetry: ${response.status} ${response.statusText}`);
1871
- }
1872
- }
1873
- catch (error) {
1874
- console.debug('Error sending event telemetry to Application Insights:', error);
1875
- }
1706
+ async post(path, data, options = {}) {
1707
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").post(path, data, options);
1708
+ return { data: response };
1876
1709
  }
1877
- extractIngestionEndpoint() {
1878
- const match = this.connectionString.match(/IngestionEndpoint=([^;]+)/);
1879
- return match ? match[1] : '';
1710
+ async put(path, data, options = {}) {
1711
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").put(path, data, options);
1712
+ return { data: response };
1880
1713
  }
1881
- }
1882
- /**
1883
- * Singleton telemetry client
1884
- */
1885
- class TelemetryClient {
1886
- constructor() {
1887
- this.isInitialized = false;
1714
+ async patch(path, data, options = {}) {
1715
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").patch(path, data, options);
1716
+ return { data: response };
1888
1717
  }
1889
- static getInstance() {
1890
- if (!TelemetryClient.instance) {
1891
- TelemetryClient.instance = new TelemetryClient();
1892
- }
1893
- return TelemetryClient.instance;
1718
+ async delete(path, options = {}) {
1719
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").delete(path, options);
1720
+ return { data: response };
1894
1721
  }
1895
1722
  /**
1896
- * Initialize telemetry
1723
+ * Execute a request with cursor-based pagination
1897
1724
  */
1898
- initialize(config) {
1899
- if (this.isInitialized) {
1900
- return;
1901
- }
1902
- this.isInitialized = true;
1903
- if (config) {
1904
- this.telemetryContext = config;
1905
- }
1906
- try {
1907
- const connectionString = this.getConnectionString();
1908
- if (!connectionString) {
1909
- return;
1910
- }
1911
- 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;
1912
1740
  }
1913
- catch (error) {
1914
- // Silent failure - telemetry errors shouldn't break functionality
1915
- 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
+ };
1916
1747
  }
1917
- }
1918
- getConnectionString() {
1919
- const connectionString = CONNECTION_STRING;
1920
- return connectionString;
1921
- }
1922
- setupTelemetryProvider(connectionString) {
1923
- const exporter = new ApplicationInsightsEventExporter(connectionString);
1924
- const processor = new sdkLogs.BatchLogRecordProcessor(exporter);
1925
- this.logProvider = new sdkLogs.LoggerProvider({
1926
- 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
1927
1755
  });
1928
- this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
1929
1756
  }
1930
1757
  /**
1931
- * Track a telemetry event
1758
+ * Validates and prepares pagination parameters from options
1932
1759
  */
1933
- track(eventName, name, extraAttributes = {}) {
1934
- try {
1935
- // Skip if logger not initialized
1936
- if (!this.logger) {
1937
- return;
1938
- }
1939
- const finalDisplayName = name || eventName;
1940
- const attributes = this.getEnrichedAttributes(extraAttributes, eventName);
1941
- // Emit as log
1942
- this.logger.emit({
1943
- body: finalDisplayName,
1944
- attributes: attributes,
1945
- timestamp: Date.now(),
1946
- });
1947
- }
1948
- catch (error) {
1949
- // Silent failure
1950
- 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;
1951
1802
  }
1803
+ return requestParams;
1952
1804
  }
1953
1805
  /**
1954
- * Get enriched attributes for telemetry events
1806
+ * Creates a paginated response from API response
1955
1807
  */
1956
- getEnrichedAttributes(extraAttributes, eventName) {
1957
- const attributes = {
1958
- [APP_NAME]: SDK_SERVICE_NAME,
1959
- [VERSION]: SDK_VERSION,
1960
- [SERVICE]: eventName,
1961
- [CLOUD_URL]: this.createCloudUrl(),
1962
- [CLOUD_ORGANIZATION_NAME]: this.telemetryContext?.orgName || UNKNOWN,
1963
- [CLOUD_TENANT_NAME]: this.telemetryContext?.tenantName || UNKNOWN,
1964
- [CLOUD_REDIRECT_URI]: this.telemetryContext?.redirectUri || UNKNOWN,
1965
- [CLOUD_CLIENT_ID]: this.telemetryContext?.clientId || UNKNOWN,
1966
- ...extraAttributes,
1967
- };
1968
- 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);
1969
1839
  }
1970
1840
  /**
1971
- * Create cloud URL from base URL, organization ID, and tenant ID
1841
+ * Determines if there are more pages based on pagination type and metadata
1972
1842
  */
1973
- createCloudUrl() {
1974
- const baseUrl = this.telemetryContext?.baseUrl;
1975
- const orgId = this.telemetryContext?.orgName;
1976
- const tenantId = this.telemetryContext?.tenantName;
1977
- if (!baseUrl || !orgId || !tenantId) {
1978
- 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;
1979
1858
  }
1980
- return `${baseUrl}/${orgId}/${tenantId}`;
1981
1859
  }
1982
1860
  }
1983
- // Export singleton instance
1984
- const telemetryClient = TelemetryClient.getInstance();
1985
-
1986
- /**
1987
- * SDK Track decorator and function for telemetry
1988
- */
1989
- /**
1990
- * Common tracking logic shared between method and function decorators
1991
- */
1992
- function createTrackedFunction(originalFunction, nameOrOptions, fallbackName, opts) {
1993
- return function (...args) {
1994
- // Determine if we should track this call
1995
- let shouldTrack = true;
1996
- if (opts.condition !== undefined) {
1997
- if (typeof opts.condition === 'function') {
1998
- shouldTrack = opts.condition.apply(this, args);
1999
- }
2000
- else {
2001
- shouldTrack = opts.condition;
2002
- }
2003
- }
2004
- // Track the event if enabled
2005
- if (shouldTrack) {
2006
- // Use the full name provided in the decorator (e.g., "Queue.GetAll")
2007
- const serviceMethod = typeof nameOrOptions === 'string'
2008
- ? nameOrOptions
2009
- : fallbackName;
2010
- // Use 'Sdk.Run' as the name and serviceMethod as the service
2011
- telemetryClient.track(serviceMethod, SDK_RUN_EVENT, opts.attributes);
2012
- }
2013
- // Execute the original function
2014
- return originalFunction.apply(this, args);
2015
- };
2016
- }
2017
- /**
2018
- * Track decorator that can be used to automatically track function calls
2019
- *
2020
- * Usage:
2021
- * @track("Service.Method")
2022
- * function myFunction() { ... }
2023
- *
2024
- * @track("Queue.GetAll")
2025
- * async getAll() { ... }
2026
- *
2027
- * @track("Tasks.Create")
2028
- * async create() { ... }
2029
- *
2030
- * @track("Assets.Update", { condition: false })
2031
- * function myFunction() { ... }
2032
- *
2033
- * @track("Processes.Start", { attributes: { customProp: "value" } })
2034
- * function myFunction() { ... }
2035
- */
2036
- function track(nameOrOptions, options) {
2037
- return function decorator(_target, propertyKey, descriptor) {
2038
- const opts = typeof nameOrOptions === 'object' ? nameOrOptions : {};
2039
- if (descriptor && typeof descriptor.value === 'function') {
2040
- // Method decorator
2041
- descriptor.value = createTrackedFunction(descriptor.value, nameOrOptions, propertyKey || 'unknown_method', opts);
2042
- return descriptor;
2043
- }
2044
- // Function decorator
2045
- return (originalFunction) => createTrackedFunction(originalFunction, nameOrOptions, originalFunction.name || 'unknown_function', opts);
2046
- };
2047
- }
1861
+ _BaseService_apiClient = new WeakMap();
2048
1862
 
2049
1863
  /**
2050
1864
  * Creates methods for a process instance
@@ -2239,6 +2053,40 @@ var SLADurationUnit;
2239
2053
  SLADurationUnit["MONTHS"] = "m";
2240
2054
  })(SLADurationUnit || (SLADurationUnit = {}));
2241
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
+
2242
2090
  /**
2243
2091
  * Maps fields for Process Instance entities to ensure consistent naming
2244
2092
  */
@@ -2614,6 +2462,177 @@ class MaestroProcessesService extends BaseService {
2614
2462
  // Fetch BPMN XML and add element name/type to each incident
2615
2463
  return BpmnHelpers.enrichIncidentsWithBpmnData(rawResponse.data || [], folderKey, this.processInstancesService);
2616
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
+ }
2617
2636
  }
2618
2637
  __decorate([
2619
2638
  track('MaestroProcesses.GetAll')
@@ -2621,6 +2640,18 @@ __decorate([
2621
2640
  __decorate([
2622
2641
  track('MaestroProcesses.GetIncidents')
2623
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);
2624
2655
 
2625
2656
  /**
2626
2657
  * Service class for Maestro Process Incidents