@uipath/uipath-typescript 1.3.8 → 1.3.10

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 (41) hide show
  1. package/dist/assets/index.cjs +44 -276
  2. package/dist/assets/index.mjs +44 -276
  3. package/dist/attachments/index.cjs +42 -273
  4. package/dist/attachments/index.mjs +42 -273
  5. package/dist/buckets/index.cjs +195 -276
  6. package/dist/buckets/index.d.ts +213 -1
  7. package/dist/buckets/index.mjs +195 -276
  8. package/dist/cases/index.cjs +427 -343
  9. package/dist/cases/index.d.ts +534 -2
  10. package/dist/cases/index.mjs +428 -344
  11. package/dist/conversational-agent/index.cjs +90 -287
  12. package/dist/conversational-agent/index.d.ts +62 -12
  13. package/dist/conversational-agent/index.mjs +90 -288
  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 +251 -277
  21. package/dist/entities/index.d.ts +305 -2
  22. package/dist/entities/index.mjs +251 -277
  23. package/dist/feedback/index.cjs +42 -274
  24. package/dist/feedback/index.mjs +42 -274
  25. package/dist/index.cjs +998 -351
  26. package/dist/index.d.ts +2159 -762
  27. package/dist/index.mjs +998 -337
  28. package/dist/index.umd.js +1208 -237
  29. package/dist/jobs/index.cjs +44 -276
  30. package/dist/jobs/index.mjs +44 -276
  31. package/dist/maestro-processes/index.cjs +1761 -1717
  32. package/dist/maestro-processes/index.d.ts +430 -2
  33. package/dist/maestro-processes/index.mjs +1762 -1718
  34. package/dist/processes/index.cjs +72 -305
  35. package/dist/processes/index.d.ts +76 -26
  36. package/dist/processes/index.mjs +72 -305
  37. package/dist/queues/index.cjs +44 -276
  38. package/dist/queues/index.mjs +44 -276
  39. package/dist/tasks/index.cjs +44 -276
  40. package/dist/tasks/index.mjs +44 -276
  41. 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,1412 @@ 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
794
667
  };
795
668
  }
796
- /**
797
- * Create a paginated response with navigation cursors
798
- */
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
- };
812
- }
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);
817
- }
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;
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
+ },
686
+ };
832
687
  }
833
688
  }
834
-
835
689
  /**
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' });
690
+ * Main error response parser using Chain of Responsibility pattern
844
691
  *
845
- * // Multiple headers
846
- * const headers = createHeaders({
847
- * 'X-UIPATH-FolderKey': '1234567890',
848
- * 'X-UIPATH-OrganizationUnitId': 123,
849
- * 'Accept': 'application/json'
850
- * });
692
+ * This parser standardizes error responses from different UiPath services into a
693
+ * consistent format, regardless of the original error structure.
851
694
  *
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
- * });
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
858
700
  *
859
- * // Empty headers
860
- * const headers = createHeaders();
861
- * ```
701
+ * @example
702
+ * const parser = new ErrorResponseParser();
703
+ * const errorInfo = await parser.parse(response);
704
+ * // errorInfo will have consistent structure regardless of service
862
705
  */
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();
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
+ }
715
+ /**
716
+ * Parses error response body into standardized format
717
+ * @param response - The HTTP response object
718
+ * @returns Standardized error information
719
+ */
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);
727
+ }
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
+ };
868
740
  }
869
741
  }
870
- return headers;
871
742
  }
743
+ // Export singleton instance
744
+ const errorResponseParser = new ErrorResponseParser();
872
745
 
873
746
  /**
874
- * Common constants used across the SDK
747
+ * Base error class for all UiPath SDK errors
748
+ * Extends Error for standard error handling compatibility
875
749
  */
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);
761
+ }
762
+ }
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
+ }
784
+ }
785
+
876
786
  /**
877
- * Prefix used for OData query parameters
787
+ * Error thrown when authentication fails (401 errors)
788
+ * Common scenarios:
789
+ * - Invalid credentials
790
+ * - Expired token
791
+ * - Missing authentication
878
792
  */
879
- const ODATA_PREFIX = '$';
880
- const UNKNOWN$1 = 'Unknown';
881
- const NO_INSTANCE = 'no-instance';
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
+
882
803
  /**
883
- * HTTP methods
804
+ * Error thrown when authorization fails (403 errors)
805
+ * Common scenarios:
806
+ * - Insufficient permissions
807
+ * - Access denied to resource
808
+ * - Invalid scope
884
809
  */
885
- const HTTP_METHODS = {
886
- GET: 'GET',
887
- POST: 'POST'};
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
+
888
820
  /**
889
- * Process Instance pagination constants for token-based pagination
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
890
826
  */
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');
982
+ async getValidToken() {
983
+ return this.tokenManager.getValidToken();
984
+ }
985
+ async getDefaultHeaders() {
986
+ const token = await this.getValidToken();
987
+ return {
988
+ 'Authorization': `Bearer ${token}`,
989
+ 'Content-Type': CONTENT_TYPES.JSON,
990
+ ...this.clientConfig.headers
991
+ };
992
+ }
993
+ async request(method, path, options = {}) {
994
+ // Ensure path starts with a forward slash
995
+ const normalizedPath = path.startsWith('/') ? path.substring(1) : path;
996
+ // Construct URL with org and tenant names
997
+ const url = new URL(`${this.config.orgName}/${this.config.tenantName}/${normalizedPath}`, this.config.baseUrl).toString();
998
+ const isFormData = options.body instanceof FormData;
999
+ const defaultHeaders = await this.getDefaultHeaders();
1000
+ if (isFormData) {
1001
+ delete defaultHeaders['Content-Type'];
1002
+ }
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
+ });
1018
+ }
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);
1023
+ }
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;
1057
1052
  }
1058
1053
  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
- }
1054
+ return JSON.parse(text);
1071
1055
  }
1072
1056
  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
- }
1057
+ if (error instanceof SyntaxError) {
1058
+ throw new ServerError({
1059
+ message: `Server returned non-JSON response (${response.status} ${response.url}): ${error.message}`,
1060
+ statusCode: response.status,
1061
+ });
1079
1062
  }
1080
- throw new Error('Invalid pagination cursor format');
1063
+ throw error;
1081
1064
  }
1082
1065
  }
1083
- }
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');
1095
- }
1096
- // Validate jumpToPage
1097
- if (options.jumpToPage !== undefined && options.jumpToPage <= 0) {
1098
- throw new Error('jumpToPage must be a positive number');
1099
- }
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.');
1066
+ catch (error) {
1067
+ // If it's already one of our errors, re-throw it
1068
+ if (error.type && error.type.includes('Error')) {
1069
+ throw error;
1070
+ }
1071
+ // Otherwise, it's a genuine network/fetch failure
1072
+ throw ErrorFactory.createNetworkError(error);
1105
1073
  }
1106
- // Get processed parameters
1107
- return PaginationHelpers.getRequestParameters(options, paginationType);
1108
1074
  }
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);
1120
- }
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);
1129
- }
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);
1075
+ async get(path, options = {}) {
1076
+ return this.request('GET', path, options);
1077
+ }
1078
+ async post(path, data, options = {}) {
1079
+ return this.request('POST', path, { ...options, body: data });
1080
+ }
1081
+ async put(path, data, options = {}) {
1082
+ return this.request('PUT', path, { ...options, body: data });
1083
+ }
1084
+ async patch(path, data, options = {}) {
1085
+ return this.request('PATCH', path, { ...options, body: data });
1086
+ }
1087
+ async delete(path, options = {}) {
1088
+ return this.request('DELETE', path, options);
1089
+ }
1090
+ }
1091
+
1092
+ /**
1093
+ * Pagination types supported by the SDK
1094
+ */
1095
+ var PaginationType;
1096
+ (function (PaginationType) {
1097
+ PaginationType["OFFSET"] = "offset";
1098
+ PaginationType["TOKEN"] = "token";
1099
+ })(PaginationType || (PaginationType = {}));
1100
+
1101
+ /**
1102
+ * Collection of utility functions for working with objects
1103
+ */
1104
+ /**
1105
+ * Resolves a field value from an object, supporting both direct keys (e.g., '@odata.count')
1106
+ * and dot-separated nested paths (e.g., 'pagination.totalCount').
1107
+ * Direct key match takes priority over nested traversal.
1108
+ */
1109
+ function resolveNestedField(data, fieldPath) {
1110
+ if (!data) {
1111
+ return undefined;
1112
+ }
1113
+ if (fieldPath in data) {
1114
+ return data[fieldPath];
1115
+ }
1116
+ if (!fieldPath.includes('.')) {
1117
+ return undefined;
1118
+ }
1119
+ let value = data;
1120
+ for (const part of fieldPath.split('.')) {
1121
+ value = value?.[part];
1122
+ }
1123
+ return value;
1124
+ }
1125
+ /**
1126
+ * Filters out undefined values from an object
1127
+ * @param obj The source object
1128
+ * @returns A new object without undefined values
1129
+ *
1130
+ * @example
1131
+ * ```typescript
1132
+ * // Object with undefined values
1133
+ * const options = {
1134
+ * name: 'test',
1135
+ * count: 5,
1136
+ * prefix: undefined,
1137
+ * suffix: null
1138
+ * };
1139
+ * const result = filterUndefined(options);
1140
+ * // result = { name: 'test', count: 5, suffix: null }
1141
+ * ```
1142
+ */
1143
+ function filterUndefined(obj) {
1144
+ const result = {};
1145
+ for (const [key, value] of Object.entries(obj)) {
1146
+ if (value !== undefined) {
1147
+ result[key] = value;
1140
1148
  }
1141
- catch {
1142
- throw new Error('Invalid pagination cursor');
1149
+ }
1150
+ return result;
1151
+ }
1152
+
1153
+ /**
1154
+ * Utility functions for platform detection
1155
+ */
1156
+ /**
1157
+ * Checks if code is running in a browser environment
1158
+ */
1159
+ const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
1160
+ isBrowser && window.self != window.top && window.location.href.includes('source=ActionCenter');
1161
+
1162
+ /**
1163
+ * Base64 encoding/decoding
1164
+ */
1165
+ /**
1166
+ * Encodes a string to base64
1167
+ * @param str - The string to encode
1168
+ * @returns Base64 encoded string
1169
+ */
1170
+ function encodeBase64(str) {
1171
+ // TextEncoder for UTF-8 encoding (works in both browser and Node.js)
1172
+ const encoder = new TextEncoder();
1173
+ const data = encoder.encode(str);
1174
+ // Convert Uint8Array to base64
1175
+ if (isBrowser) {
1176
+ // Browser environment
1177
+ // Convert Uint8Array to binary string then to base64
1178
+ const binaryString = Array.from(data, byte => String.fromCharCode(byte)).join('');
1179
+ return btoa(binaryString);
1180
+ }
1181
+ else {
1182
+ // Node.js environment
1183
+ return Buffer.from(data).toString('base64');
1184
+ }
1185
+ }
1186
+ /**
1187
+ * Decodes a base64 string
1188
+ * @param base64 - The base64 string to decode
1189
+ * @returns Decoded string
1190
+ */
1191
+ function decodeBase64(base64) {
1192
+ let bytes;
1193
+ if (isBrowser) {
1194
+ // Browser environment
1195
+ const binaryString = atob(base64);
1196
+ bytes = new Uint8Array(binaryString.length);
1197
+ for (let i = 0; i < binaryString.length; i++) {
1198
+ bytes[i] = binaryString.charCodeAt(i);
1143
1199
  }
1144
1200
  }
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
- };
1201
+ else {
1202
+ // Node.js environment
1203
+ bytes = new Uint8Array(Buffer.from(base64, 'base64'));
1174
1204
  }
1205
+ // TextDecoder for UTF-8 decoding (works in both browser and Node.js)
1206
+ const decoder = new TextDecoder();
1207
+ return decoder.decode(bytes);
1208
+ }
1209
+
1210
+ /**
1211
+ * PaginationManager handles the conversion between uniform cursor-based pagination
1212
+ * and the specific pagination type for each service
1213
+ */
1214
+ class PaginationManager {
1175
1215
  /**
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
1216
+ * Create a pagination cursor for subsequent page requests
1180
1217
  */
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 });
1218
+ static createCursor({ pageInfo, type }) {
1219
+ if (!pageInfo.hasMore) {
1220
+ return undefined;
1193
1221
  }
1194
- else {
1195
- response = await serviceAccess.get(endpoint, {
1196
- params: additionalParams,
1197
- headers
1198
- });
1222
+ const cursorData = {
1223
+ type,
1224
+ pageSize: pageInfo.pageSize,
1225
+ };
1226
+ switch (type) {
1227
+ case PaginationType.OFFSET:
1228
+ if (pageInfo.currentPage) {
1229
+ cursorData.pageNumber = pageInfo.currentPage + 1;
1230
+ }
1231
+ break;
1232
+ case PaginationType.TOKEN:
1233
+ if (pageInfo.continuationToken) {
1234
+ cursorData.continuationToken = pageInfo.continuationToken;
1235
+ }
1236
+ else {
1237
+ return undefined; // No continuation token, can't continue
1238
+ }
1239
+ break;
1199
1240
  }
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
1241
  return {
1209
- items,
1210
- totalCount
1242
+ value: encodeBase64(JSON.stringify(cursorData))
1211
1243
  };
1212
1244
  }
1213
1245
  /**
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
1246
+ * Create a paginated response with navigation cursors
1219
1247
  */
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);
1248
+ static createPaginatedResponse({ pageInfo, type }, items) {
1249
+ const nextCursor = PaginationManager.createCursor({ pageInfo, type });
1250
+ // Create previous page cursor if applicable
1251
+ let previousCursor = undefined;
1252
+ if (pageInfo.currentPage && pageInfo.currentPage > 1) {
1253
+ const prevCursorData = {
1254
+ type,
1255
+ pageNumber: pageInfo.currentPage - 1,
1256
+ pageSize: pageInfo.pageSize,
1257
+ };
1258
+ previousCursor = {
1259
+ value: encodeBase64(JSON.stringify(prevCursorData))
1260
+ };
1229
1261
  }
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
1262
+ // Calculate total pages if we have totalCount and pageSize
1263
+ let totalPages = undefined;
1264
+ if (pageInfo.totalCount !== undefined && pageInfo.pageSize) {
1265
+ totalPages = Math.ceil(pageInfo.totalCount / pageInfo.pageSize);
1256
1266
  }
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
- }
1267
+ // Determine if this pagination type supports page jumping
1268
+ const supportsPageJump = type === PaginationType.OFFSET;
1269
+ // Create the result object with all fields, then filter out undefined values
1270
+ const result = filterUndefined({
1271
+ items,
1272
+ totalCount: pageInfo.totalCount,
1273
+ hasNextPage: pageInfo.hasMore,
1274
+ nextCursor: nextCursor,
1275
+ previousCursor: previousCursor,
1276
+ currentPage: pageInfo.currentPage,
1277
+ totalPages,
1278
+ supportsPageJump
1271
1279
  });
1280
+ return result;
1272
1281
  }
1273
1282
  }
1274
1283
 
1275
1284
  /**
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
1304
- */
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;
1285
+ * Constants used throughout the pagination system
1286
+ */
1287
+ /** Maximum number of items that can be requested in a single page */
1288
+ const MAX_PAGE_SIZE = 1000;
1289
+ /** Default page size when jumpToPage is used without specifying pageSize */
1290
+ const DEFAULT_PAGE_SIZE = 50;
1291
+ /** Default field name for items in a paginated response */
1292
+ const DEFAULT_ITEMS_FIELD = 'value';
1293
+ /** Default field name for total count in a paginated response */
1294
+ const DEFAULT_TOTAL_COUNT_FIELD = '@odata.count';
1295
+ /**
1296
+ * Limits the page size to the maximum allowed value
1297
+ * @param pageSize - Requested page size
1298
+ * @returns Limited page size value
1299
+ */
1300
+ function getLimitedPageSize(pageSize) {
1301
+ if (pageSize === undefined || pageSize === null) {
1302
+ return DEFAULT_PAGE_SIZE;
1327
1303
  }
1304
+ return Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
1328
1305
  }
1329
1306
 
1330
- var _BaseService_apiClient;
1331
1307
  /**
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
- *
1308
+ * Helper functions for pagination that can be used across services
1345
1309
  */
1346
- class BaseService {
1310
+ class PaginationHelpers {
1347
1311
  /**
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';
1312
+ * Checks if any pagination parameters are provided
1371
1313
  *
1372
- * const sdk = new UiPath(config);
1373
- * await sdk.initialize();
1374
- * const entities = new Entities(sdk);
1375
- * ```
1314
+ * @param options - The options object to check
1315
+ * @returns True if any pagination parameter is defined, false otherwise
1376
1316
  */
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 };
1317
+ static hasPaginationParameters(options = {}) {
1318
+ const { cursor, pageSize, jumpToPage } = options;
1319
+ return cursor !== undefined || pageSize !== undefined || jumpToPage !== undefined;
1383
1320
  }
1384
1321
  /**
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
1322
+ * Parse a pagination cursor string into cursor data
1390
1323
  */
1391
- async getValidAuthToken() {
1392
- return __classPrivateFieldGet(this, _BaseService_apiClient, "f").getValidToken();
1324
+ static parseCursor(cursorString) {
1325
+ try {
1326
+ const cursorData = JSON.parse(decodeBase64(cursorString));
1327
+ return cursorData;
1328
+ }
1329
+ catch {
1330
+ throw new Error('Invalid pagination cursor');
1331
+ }
1393
1332
  }
1394
1333
  /**
1395
- * Creates a service accessor for pagination helpers
1396
- * This allows pagination helpers to access protected methods without making them public
1334
+ * Validates cursor format and structure
1335
+ *
1336
+ * @param paginationOptions - The pagination options containing the cursor
1337
+ * @param paginationType - Optional pagination type to validate against
1397
1338
  */
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}`);
1339
+ static validateCursor(paginationOptions, paginationType) {
1340
+ if (paginationOptions.cursor !== undefined) {
1341
+ if (!paginationOptions.cursor || typeof paginationOptions.cursor.value !== 'string' || !paginationOptions.cursor.value) {
1342
+ throw new Error('cursor must contain a valid cursor string');
1343
+ }
1344
+ try {
1345
+ // Try to parse the cursor to validate it
1346
+ const cursorData = PaginationHelpers.parseCursor(paginationOptions.cursor.value);
1347
+ // If type is provided, validate cursor contains expected type information
1348
+ if (paginationType) {
1349
+ if (!cursorData.type) {
1350
+ throw new Error('Invalid cursor: missing pagination type');
1351
+ }
1352
+ // Check pagination type compatibility
1353
+ if (cursorData.type !== paginationType) {
1354
+ throw new Error(`Pagination type mismatch: cursor is for ${cursorData.type} but service uses ${paginationType}`);
1355
+ }
1356
+ }
1357
+ }
1358
+ catch (error) {
1359
+ if (error instanceof Error) {
1360
+ // If it's already our error with specific message, pass it through
1361
+ if (error.message.startsWith('Invalid cursor') ||
1362
+ error.message.startsWith('Pagination type mismatch')) {
1363
+ throw error;
1364
+ }
1365
+ }
1366
+ throw new Error('Invalid pagination cursor format');
1367
+ }
1419
1368
  }
1420
1369
  }
1421
- async requestWithSpec(spec) {
1422
- if (!spec.method || !spec.url) {
1423
- throw new Error('Request spec must include method and url');
1370
+ /**
1371
+ * Comprehensive validation for pagination options
1372
+ *
1373
+ * @param options - The pagination options to validate
1374
+ * @param paginationType - The pagination type these options will be used with
1375
+ * @returns Processed pagination parameters ready for use
1376
+ */
1377
+ static validatePaginationOptions(options, paginationType) {
1378
+ // Validate pageSize
1379
+ if (options.pageSize !== undefined && options.pageSize <= 0) {
1380
+ throw new Error('pageSize must be a positive number');
1424
1381
  }
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 };
1382
+ // Validate jumpToPage
1383
+ if (options.jumpToPage !== undefined && options.jumpToPage <= 0) {
1384
+ throw new Error('jumpToPage must be a positive number');
1385
+ }
1386
+ // Validate cursor
1387
+ PaginationHelpers.validateCursor(options, paginationType);
1388
+ // Validate service compatibility
1389
+ if (options.jumpToPage !== undefined && paginationType === PaginationType.TOKEN) {
1390
+ throw new Error('jumpToPage is not supported for token-based pagination. Use cursor-based navigation instead.');
1391
+ }
1392
+ // Get processed parameters
1393
+ return PaginationHelpers.getRequestParameters(options, paginationType);
1446
1394
  }
1447
1395
  /**
1448
- * Execute a request with cursor-based pagination
1396
+ * Convert a unified pagination options to service-specific parameters
1449
1397
  */
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
1398
+ static getRequestParameters(options, paginationType) {
1399
+ // Handle jumpToPage
1400
+ if (options.jumpToPage !== undefined) {
1401
+ const jumpToPageOptions = {
1402
+ pageSize: options.pageSize,
1403
+ pageNumber: options.jumpToPage
1463
1404
  };
1464
- options.params = undefined;
1405
+ return filterUndefined(jumpToPageOptions);
1465
1406
  }
1466
- else {
1467
- // Merge pagination parameters with existing parameters
1468
- options.params = {
1469
- ...options.params,
1470
- ...requestParams
1407
+ // If no cursor is provided, it's a first page request
1408
+ if (!options.cursor) {
1409
+ const firstPageOptions = {
1410
+ pageSize: options.pageSize,
1411
+ // Only set pageNumber for OFFSET pagination
1412
+ pageNumber: paginationType === PaginationType.OFFSET ? 1 : undefined
1471
1413
  };
1414
+ return filterUndefined(firstPageOptions);
1415
+ }
1416
+ // Parse the cursor
1417
+ try {
1418
+ const cursorData = PaginationHelpers.parseCursor(options.cursor.value);
1419
+ const cursorBasedOptions = {
1420
+ pageSize: cursorData.pageSize || options.pageSize,
1421
+ pageNumber: cursorData.pageNumber,
1422
+ continuationToken: cursorData.continuationToken,
1423
+ type: cursorData.type,
1424
+ };
1425
+ return filterUndefined(cursorBasedOptions);
1426
+ }
1427
+ catch {
1428
+ throw new Error('Invalid pagination cursor');
1472
1429
  }
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
1480
- });
1481
- }
1482
- /**
1483
- * Validates and prepares pagination parameters from options
1484
- */
1485
- validateAndPreparePaginationParams(paginationType, paginationOptions) {
1486
- return PaginationHelpers.validatePaginationOptions(paginationOptions, paginationType);
1487
1430
  }
1488
1431
  /**
1489
- * Prepares request parameters for pagination based on pagination type
1432
+ * Helper method for paginated resource retrieval
1433
+ *
1434
+ * @param params - Parameters for pagination
1435
+ * @returns Promise resolving to a paginated result
1490
1436
  */
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;
1527
- }
1528
- return requestParams;
1437
+ static async getAllPaginated(params) {
1438
+ const { serviceAccess, getEndpoint, folderId, headers: providedHeaders, paginationParams, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1439
+ const endpoint = getEndpoint(folderId);
1440
+ const headers = providedHeaders ?? (folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {});
1441
+ const paginatedResponse = await serviceAccess.requestWithPagination(method, endpoint, paginationParams, {
1442
+ headers,
1443
+ params: additionalParams,
1444
+ pagination: {
1445
+ paginationType: options.paginationType || PaginationType.OFFSET,
1446
+ itemsField: options.itemsField || DEFAULT_ITEMS_FIELD,
1447
+ totalCountField: options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD,
1448
+ continuationTokenField: options.continuationTokenField,
1449
+ paginationParams: options.paginationParams
1450
+ }
1451
+ });
1452
+ // Parse items - automatically handle JSON string responses
1453
+ const rawItems = paginatedResponse.items;
1454
+ const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1455
+ const transformedItems = transformFn ? parsedItems.map(transformFn) : parsedItems;
1456
+ return {
1457
+ ...paginatedResponse,
1458
+ items: transformedItems
1459
+ };
1529
1460
  }
1530
1461
  /**
1531
- * Creates a paginated response from API response
1462
+ * Helper method for non-paginated resource retrieval
1463
+ *
1464
+ * @param params - Parameters for non-paginated resource retrieval
1465
+ * @returns Promise resolving to an object with data and totalCount
1532
1466
  */
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
1467
+ static async getAllNonPaginated(params) {
1468
+ const { serviceAccess, getAllEndpoint, getByFolderEndpoint, folderId, headers: providedHeaders, additionalParams, transformFn, method = HTTP_METHODS.GET, options = {} } = params;
1469
+ // Set default field names
1470
+ const itemsField = options.itemsField || DEFAULT_ITEMS_FIELD;
1471
+ const totalCountField = options.totalCountField || DEFAULT_TOTAL_COUNT_FIELD;
1472
+ // Determine endpoint and headers based on folderId
1473
+ const endpoint = folderId ? getByFolderEndpoint : getAllEndpoint;
1474
+ const headers = providedHeaders ?? (folderId ? createHeaders({ [FOLDER_ID]: folderId }) : {});
1475
+ // Make the API call based on method
1476
+ let response;
1477
+ if (method === HTTP_METHODS.POST) {
1478
+ response = await serviceAccess.post(endpoint, additionalParams, { headers });
1479
+ }
1480
+ else {
1481
+ response = await serviceAccess.get(endpoint, {
1482
+ params: additionalParams,
1483
+ headers
1484
+ });
1485
+ }
1486
+ // Extract and transform items from response
1540
1487
  // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1541
- const items = Array.isArray(response.data) ? response.data : (response.data[itemsField] || []);
1488
+ const rawItems = Array.isArray(response.data) ? response.data : response.data?.[itemsField];
1542
1489
  const rawTotalCount = Array.isArray(response.data) ? undefined : resolveNestedField(response.data, totalCountField);
1543
1490
  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);
1491
+ // Parse items - automatically handle JSON string responses
1492
+ const parsedItems = typeof rawItems === 'string' ? JSON.parse(rawItems) : (rawItems || []);
1493
+ const items = transformFn ? parsedItems.map(transformFn) : parsedItems;
1494
+ return {
1495
+ items,
1496
+ totalCount
1497
+ };
1564
1498
  }
1565
1499
  /**
1566
- * Determines if there are more pages based on pagination type and metadata
1500
+ * Centralized getAll implementation that handles both paginated and non-paginated requests
1501
+ *
1502
+ * @param config - Configuration for the getAll operation
1503
+ * @param options - Request options including pagination parameters
1504
+ * @returns Promise resolving to either paginated or non-paginated response based on options
1567
1505
  */
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;
1506
+ static async getAll(config, options) {
1507
+ const optionsWithDefaults = options || {};
1508
+ const { folderId, pageSize, cursor, jumpToPage, ...restOptions } = optionsWithDefaults;
1509
+ // Determine if pagination is requested
1510
+ const isPaginationRequested = PaginationHelpers.hasPaginationParameters(options || {});
1511
+ // Process parameters (custom processing if provided, otherwise default)
1512
+ let processedOptions = restOptions;
1513
+ if (config.processParametersFn) {
1514
+ processedOptions = config.processParametersFn(restOptions, folderId);
1583
1515
  }
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);
1516
+ // Apply ODATA prefix to keys (excluding specified keys)
1517
+ const excludeKeys = config.excludeFromPrefix || [];
1518
+ const keysToPrefix = Object.keys(processedOptions).filter(k => !excludeKeys.includes(k));
1519
+ const prefixedOptions = addPrefixToKeys(processedOptions, ODATA_PREFIX, keysToPrefix);
1520
+ // Default pagination options
1521
+ const paginationOptions = {
1522
+ paginationType: PaginationType.OFFSET,
1523
+ itemsField: DEFAULT_ITEMS_FIELD,
1524
+ totalCountField: DEFAULT_TOTAL_COUNT_FIELD,
1525
+ ...config.pagination
1526
+ };
1527
+ // Paginated flow
1528
+ if (isPaginationRequested) {
1529
+ return PaginationHelpers.getAllPaginated({
1530
+ serviceAccess: config.serviceAccess,
1531
+ getEndpoint: config.getEndpoint,
1532
+ folderId,
1533
+ headers: config.headers,
1534
+ paginationParams: cursor ? { cursor, pageSize } : jumpToPage ? { jumpToPage, pageSize } : { pageSize },
1535
+ additionalParams: prefixedOptions,
1536
+ transformFn: config.transformFn,
1537
+ method: config.method,
1538
+ options: {
1539
+ ...paginationOptions,
1540
+ paginationParams: config.pagination?.paginationParams
1541
+ }
1542
+ }); // Type assertion needed due to conditional return
1637
1543
  }
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);
1544
+ // Non-paginated flow
1545
+ const byFolderEndpoint = config.getByFolderEndpoint || config.getEndpoint(folderId);
1546
+ return PaginationHelpers.getAllNonPaginated({
1547
+ serviceAccess: config.serviceAccess,
1548
+ getAllEndpoint: config.getEndpoint(),
1549
+ getByFolderEndpoint: byFolderEndpoint,
1550
+ folderId,
1551
+ headers: config.headers,
1552
+ additionalParams: prefixedOptions,
1553
+ transformFn: config.transformFn,
1554
+ method: config.method,
1555
+ options: {
1556
+ itemsField: paginationOptions.itemsField,
1557
+ totalCountField: paginationOptions.totalCountField
1558
+ }
1559
+ });
1560
+ }
1650
1561
  }
1651
1562
 
1652
1563
  /**
1653
- * Maps fields for Incident entities
1564
+ * SDK Internals Registry - Internal registry for SDK instances
1565
+ *
1566
+ * This class is NOT exported in the public API.
1567
+ * It provides a secure way to share SDK internals between
1568
+ * the UiPath class and service classes without exposing them publicly.
1569
+ *
1570
+ * @internal
1654
1571
  */
1655
- const ProcessIncidentMap = {
1656
- errorTimeUtc: 'errorTime'
1572
+ // Global symbol key to ensure WeakMap is shared across module instances
1573
+ // This prevents issues when core and service modules are bundled separately
1574
+ const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
1575
+ // Get or create the global WeakMap store
1576
+ const getGlobalStore = () => {
1577
+ const globalObj = globalThis;
1578
+ if (!globalObj[REGISTRY_KEY]) {
1579
+ globalObj[REGISTRY_KEY] = new WeakMap();
1580
+ }
1581
+ return globalObj[REGISTRY_KEY];
1657
1582
  };
1658
1583
  /**
1659
- * Maps fields for Incident Summary entities
1584
+ * Internal registry for SDK private components.
1585
+ * Uses WeakMap to prevent memory leaks - entries are automatically
1586
+ * garbage collected when the SDK instance is no longer referenced.
1587
+ *
1588
+ * Uses a global singleton pattern to ensure the same WeakMap is shared
1589
+ * across separately bundled modules (core, entities, tasks, etc.).
1590
+ *
1591
+ * @internal - Not exported in public API
1660
1592
  */
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;
1593
+ class SDKInternalsRegistry {
1594
+ // Use global store to ensure sharing across module bundles
1595
+ static get store() {
1596
+ return getGlobalStore();
1701
1597
  }
1702
1598
  /**
1703
- * Format BPMN element type to human-readable activity type for incidents
1599
+ * Register SDK instance internals
1600
+ * Called by UiPath constructor
1704
1601
  */
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
1602
+ static set(instance, internals) {
1603
+ this.store.set(instance, internals);
1712
1604
  }
1713
1605
  /**
1714
- * Fetch BPMN via getBpmn and add element name/type to each incident
1606
+ * Retrieve SDK instance internals
1607
+ * Called by BaseService constructor
1715
1608
  */
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);
1609
+ static get(instance) {
1610
+ const internals = this.store.get(instance);
1611
+ if (!internals) {
1612
+ throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
1727
1613
  }
1614
+ return internals;
1728
1615
  }
1616
+ }
1617
+
1618
+ var _BaseService_apiClient;
1619
+ /**
1620
+ * Base class for all UiPath SDK services.
1621
+ *
1622
+ * Provides common functionality for authentication, configuration, and API communication.
1623
+ * All service classes extend this base to inherit dependency injection and HTTP client access.
1624
+ *
1625
+ * This class implements the dependency injection pattern where services receive a configured
1626
+ * UiPath instance. The ApiClient is created internally and handles all HTTP operations
1627
+ * including authentication token management.
1628
+ *
1629
+ * @remarks
1630
+ * Service classes should extend this base and call `super(uiPath)` in their constructor.
1631
+ * Protected HTTP methods (get, post, put, patch, delete) are available to all subclasses.
1632
+ *
1633
+ */
1634
+ class BaseService {
1729
1635
  /**
1730
- * When incidents span multiple instances, fetch BPMN per instance and annotate
1636
+ * Creates a base service instance with dependency injection.
1637
+ *
1638
+ * Extracts configuration, execution context, and token manager from the UiPath instance
1639
+ * to initialize an authenticated API client. The ApiClient handles all HTTP operations
1640
+ * and token management internally.
1641
+ *
1642
+ * @param instance - UiPath SDK instance providing authentication and configuration.
1643
+ * Services receive this via dependency injection in the modular pattern.
1644
+ * @param headers - Optional default headers to include in every request (e.g. `x-uipath-external-user-id` for
1645
+ * CAS external-app auth)
1646
+ *
1647
+ * @example
1648
+ * ```typescript
1649
+ * // Services automatically call this via super()
1650
+ * export class EntityService extends BaseService {
1651
+ * constructor(instance: IUiPath) {
1652
+ * super(instance); // Initializes the internal ApiClient
1653
+ * }
1654
+ * }
1655
+ *
1656
+ * // Usage in modular pattern
1657
+ * import { UiPath } from '@uipath/uipath-typescript/core';
1658
+ * import { Entities } from '@uipath/uipath-typescript/entities';
1659
+ *
1660
+ * const sdk = new UiPath(config);
1661
+ * await sdk.initialize();
1662
+ * const entities = new Entities(sdk);
1663
+ * ```
1731
1664
  */
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();
1665
+ constructor(instance, headers) {
1666
+ // Private field - not visible via Object.keys() or any reflection
1667
+ _BaseService_apiClient.set(this, void 0);
1668
+ const { config, context, tokenManager, folderKey } = SDKInternalsRegistry.get(instance);
1669
+ __classPrivateFieldSet(this, _BaseService_apiClient, new ApiClient(config, context, tokenManager, headers ? { headers } : {}), "f");
1670
+ this.config = { folderKey };
1744
1671
  }
1745
1672
  /**
1746
- * Retrieve BPMN XML for an instance and derive element id → {name,type}
1673
+ * Gets a valid authentication token, refreshing if necessary.
1674
+ * Use this when you need to manually add Authorization headers (e.g., direct uploads).
1675
+ *
1676
+ * @returns Promise resolving to a valid access token string
1677
+ * @throws AuthenticationError if no token is available or refresh fails
1747
1678
  */
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
- }
1679
+ async getValidAuthToken() {
1680
+ return __classPrivateFieldGet(this, _BaseService_apiClient, "f").getValidToken();
1760
1681
  }
1761
1682
  /**
1762
- * Transform a raw incident by attaching element name/type from BPMN
1683
+ * Creates a service accessor for pagination helpers
1684
+ * This allows pagination helpers to access protected methods without making them public
1763
1685
  */
1764
- static transformIncidentWithBpmn(incident, elementInfo) {
1765
- const element = elementInfo[incident.elementId];
1766
- const transformed = transformData(incident, ProcessIncidentMap);
1686
+ createPaginationServiceAccess() {
1767
1687
  return {
1768
- ...transformed,
1769
- incidentElementActivityType: element?.type || UNKNOWN$1,
1770
- incidentElementActivityName: element?.name || UNKNOWN$1
1688
+ get: (path, options) => this.get(path, options || {}),
1689
+ post: (path, body, options) => this.post(path, body, options || {}),
1690
+ requestWithPagination: (method, path, paginationOptions, options) => this.requestWithPagination(method, path, paginationOptions, options)
1771
1691
  };
1772
1692
  }
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 });
1693
+ async request(method, path, options = {}) {
1694
+ switch (method.toUpperCase()) {
1695
+ case 'GET':
1696
+ return this.get(path, options);
1697
+ case 'POST':
1698
+ return this.post(path, options.body, options);
1699
+ case 'PUT':
1700
+ return this.put(path, options.body, options);
1701
+ case 'PATCH':
1702
+ return this.patch(path, options.body, options);
1703
+ case 'DELETE':
1704
+ return this.delete(path, options);
1705
+ default:
1706
+ throw new Error(`Unsupported HTTP method: ${method}`);
1816
1707
  }
1817
1708
  }
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] : '';
1709
+ async requestWithSpec(spec) {
1710
+ if (!spec.method || !spec.url) {
1711
+ throw new Error('Request spec must include method and url');
1712
+ }
1713
+ return this.request(spec.method, spec.url, spec);
1846
1714
  }
1847
- convertAttributesToProperties(attributes) {
1848
- const properties = {};
1849
- Object.entries(attributes || {}).forEach(([key, value]) => {
1850
- properties[key] = String(value);
1851
- });
1852
- return properties;
1715
+ async get(path, options = {}) {
1716
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").get(path, options);
1717
+ return { data: response };
1853
1718
  }
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
- }
1719
+ async post(path, data, options = {}) {
1720
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").post(path, data, options);
1721
+ return { data: response };
1876
1722
  }
1877
- extractIngestionEndpoint() {
1878
- const match = this.connectionString.match(/IngestionEndpoint=([^;]+)/);
1879
- return match ? match[1] : '';
1723
+ async put(path, data, options = {}) {
1724
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").put(path, data, options);
1725
+ return { data: response };
1880
1726
  }
1881
- }
1882
- /**
1883
- * Singleton telemetry client
1884
- */
1885
- class TelemetryClient {
1886
- constructor() {
1887
- this.isInitialized = false;
1727
+ async patch(path, data, options = {}) {
1728
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").patch(path, data, options);
1729
+ return { data: response };
1888
1730
  }
1889
- static getInstance() {
1890
- if (!TelemetryClient.instance) {
1891
- TelemetryClient.instance = new TelemetryClient();
1892
- }
1893
- return TelemetryClient.instance;
1731
+ async delete(path, options = {}) {
1732
+ const response = await __classPrivateFieldGet(this, _BaseService_apiClient, "f").delete(path, options);
1733
+ return { data: response };
1894
1734
  }
1895
1735
  /**
1896
- * Initialize telemetry
1736
+ * Execute a request with cursor-based pagination
1897
1737
  */
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);
1738
+ async requestWithPagination(method, path, paginationOptions, options) {
1739
+ const paginationType = options.pagination.paginationType;
1740
+ // Validate and prepare pagination parameters
1741
+ const params = this.validateAndPreparePaginationParams(paginationType, paginationOptions);
1742
+ // Prepare request parameters based on pagination type
1743
+ const requestParams = this.preparePaginationRequestParams(paginationType, params, options.pagination);
1744
+ // For POST requests, merge pagination params into body and set params to undefined; for GET, use query params
1745
+ if (method.toUpperCase() === 'POST') {
1746
+ const existingBody = (options.body && typeof options.body === 'object') ? options.body : {};
1747
+ options.body = {
1748
+ ...existingBody,
1749
+ ...options.params,
1750
+ ...requestParams
1751
+ };
1752
+ options.params = undefined;
1912
1753
  }
1913
- catch (error) {
1914
- // Silent failure - telemetry errors shouldn't break functionality
1915
- console.debug('Failed to initialize OpenTelemetry:', error);
1754
+ else {
1755
+ // Merge pagination parameters with existing parameters
1756
+ options.params = {
1757
+ ...options.params,
1758
+ ...requestParams
1759
+ };
1916
1760
  }
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]
1761
+ // Make the request
1762
+ const response = await this.request(method, path, options);
1763
+ // Extract data from the response and create page result
1764
+ return this.createPaginatedResponseFromResponse(response, params, paginationType, {
1765
+ itemsField: options.pagination.itemsField,
1766
+ totalCountField: options.pagination.totalCountField,
1767
+ continuationTokenField: options.pagination.continuationTokenField
1927
1768
  });
1928
- this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
1929
1769
  }
1930
1770
  /**
1931
- * Track a telemetry event
1771
+ * Validates and prepares pagination parameters from options
1932
1772
  */
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);
1773
+ validateAndPreparePaginationParams(paginationType, paginationOptions) {
1774
+ return PaginationHelpers.validatePaginationOptions(paginationOptions, paginationType);
1775
+ }
1776
+ /**
1777
+ * Prepares request parameters for pagination based on pagination type
1778
+ */
1779
+ preparePaginationRequestParams(paginationType, params, paginationConfig) {
1780
+ const requestParams = {};
1781
+ let limitedPageSize;
1782
+ const paginationParams = paginationConfig?.paginationParams;
1783
+ switch (paginationType) {
1784
+ case PaginationType.OFFSET:
1785
+ limitedPageSize = getLimitedPageSize(params.pageSize);
1786
+ const pageSizeParam = paginationParams?.pageSizeParam || ODATA_OFFSET_PARAMS.PAGE_SIZE_PARAM;
1787
+ const offsetParam = paginationParams?.offsetParam || ODATA_OFFSET_PARAMS.OFFSET_PARAM;
1788
+ const countParam = paginationParams?.countParam || ODATA_OFFSET_PARAMS.COUNT_PARAM;
1789
+ // When true (default), converts pageNumber to a skip/offset value (e.g., page 3 with pageSize 10 → skip 20).
1790
+ // When false, passes pageNumber directly as the offset param — used by APIs that accept a page number instead of a record offset.
1791
+ const convertToSkip = paginationParams?.convertToSkip ?? true;
1792
+ requestParams[pageSizeParam] = limitedPageSize;
1793
+ if (convertToSkip) {
1794
+ if (params.pageNumber && params.pageNumber > 1) {
1795
+ requestParams[offsetParam] = (params.pageNumber - 1) * limitedPageSize;
1796
+ }
1797
+ }
1798
+ else {
1799
+ requestParams[offsetParam] = params.pageNumber || 1;
1800
+ }
1801
+ {
1802
+ requestParams[countParam] = true;
1803
+ }
1804
+ break;
1805
+ case PaginationType.TOKEN:
1806
+ const tokenPageSizeParam = paginationParams?.pageSizeParam || BUCKET_TOKEN_PARAMS.PAGE_SIZE_PARAM;
1807
+ const tokenParam = paginationParams?.tokenParam || BUCKET_TOKEN_PARAMS.TOKEN_PARAM;
1808
+ if (params.pageSize) {
1809
+ requestParams[tokenPageSizeParam] = getLimitedPageSize(params.pageSize);
1810
+ }
1811
+ if (params.continuationToken) {
1812
+ requestParams[tokenParam] = params.continuationToken;
1813
+ }
1814
+ break;
1951
1815
  }
1816
+ return requestParams;
1952
1817
  }
1953
1818
  /**
1954
- * Get enriched attributes for telemetry events
1819
+ * Creates a paginated response from API response
1955
1820
  */
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;
1821
+ createPaginatedResponseFromResponse(response, params, paginationType, fields) {
1822
+ // Extract fields from response
1823
+ const itemsField = fields.itemsField ||
1824
+ (paginationType === PaginationType.TOKEN ? 'items' : 'value');
1825
+ const totalCountField = fields.totalCountField || 'totalRecordCount';
1826
+ const continuationTokenField = fields.continuationTokenField || 'continuationToken';
1827
+ // Extract items and metadata
1828
+ // Handle both plain array responses and envelope responses ({ value: [...], totalRecordCount: N })
1829
+ const items = Array.isArray(response.data) ? response.data : (response.data[itemsField] || []);
1830
+ const rawTotalCount = Array.isArray(response.data) ? undefined : resolveNestedField(response.data, totalCountField);
1831
+ const totalCount = typeof rawTotalCount === 'number' ? rawTotalCount : undefined;
1832
+ const continuationToken = response.data[continuationTokenField];
1833
+ // Determine if there are more pages
1834
+ const hasMore = this.determineHasMorePages(paginationType, {
1835
+ totalCount,
1836
+ pageSize: params.pageSize,
1837
+ currentPage: params.pageNumber || 1,
1838
+ itemsCount: items.length,
1839
+ continuationToken
1840
+ });
1841
+ // Create and return the page result
1842
+ return PaginationManager.createPaginatedResponse({
1843
+ pageInfo: {
1844
+ hasMore,
1845
+ totalCount,
1846
+ currentPage: params.pageNumber,
1847
+ pageSize: params.pageSize,
1848
+ continuationToken
1849
+ },
1850
+ type: paginationType,
1851
+ }, items);
1969
1852
  }
1970
1853
  /**
1971
- * Create cloud URL from base URL, organization ID, and tenant ID
1854
+ * Determines if there are more pages based on pagination type and metadata
1972
1855
  */
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;
1856
+ determineHasMorePages(paginationType, info) {
1857
+ switch (paginationType) {
1858
+ case PaginationType.OFFSET:
1859
+ const effectivePageSize = info.pageSize ?? DEFAULT_PAGE_SIZE;
1860
+ // If totalCount is available, use it for precise calculation
1861
+ if (info.totalCount !== undefined) {
1862
+ return (info.currentPage * effectivePageSize) < info.totalCount;
1863
+ }
1864
+ // Fallback when totalCount is not available
1865
+ // NOTE: This code path should rarely be executed as the APIs typically return totalCount
1866
+ return info.itemsCount === effectivePageSize;
1867
+ case PaginationType.TOKEN:
1868
+ return !!info.continuationToken;
1869
+ default:
1870
+ return false;
1979
1871
  }
1980
- return `${baseUrl}/${orgId}/${tenantId}`;
1981
1872
  }
1982
1873
  }
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
- }
1874
+ _BaseService_apiClient = new WeakMap();
2048
1875
 
2049
1876
  /**
2050
1877
  * Creates methods for a process instance
@@ -2239,6 +2066,40 @@ var SLADurationUnit;
2239
2066
  SLADurationUnit["MONTHS"] = "m";
2240
2067
  })(SLADurationUnit || (SLADurationUnit = {}));
2241
2068
 
2069
+ /**
2070
+ * Insights Types
2071
+ * Shared types for Maestro insights analytics endpoints
2072
+ */
2073
+ /**
2074
+ * Time bucketing granularity for insights time-series queries.
2075
+ *
2076
+ * Controls how data points are grouped on the time axis.
2077
+ */
2078
+ exports.TimeInterval = void 0;
2079
+ (function (TimeInterval) {
2080
+ /** Group data points by hour */
2081
+ TimeInterval["Hour"] = "HOUR";
2082
+ /** Group data points by day */
2083
+ TimeInterval["Day"] = "DAY";
2084
+ /** Group data points by week */
2085
+ TimeInterval["Week"] = "WEEK";
2086
+ })(exports.TimeInterval || (exports.TimeInterval = {}));
2087
+ /**
2088
+ * Final instance statuses returned by the instance status timeline endpoint.
2089
+ *
2090
+ * Only includes statuses where the instance has finished execution — Completed, Faulted, or Cancelled.
2091
+ * Active statuses like Running or Paused are not included.
2092
+ */
2093
+ exports.InstanceFinalStatus = void 0;
2094
+ (function (InstanceFinalStatus) {
2095
+ /** Instance completed successfully */
2096
+ InstanceFinalStatus["Completed"] = "Completed";
2097
+ /** Instance encountered an error */
2098
+ InstanceFinalStatus["Faulted"] = "Faulted";
2099
+ /** Instance was cancelled */
2100
+ InstanceFinalStatus["Cancelled"] = "Cancelled";
2101
+ })(exports.InstanceFinalStatus || (exports.InstanceFinalStatus = {}));
2102
+
2242
2103
  /**
2243
2104
  * Maps fields for Process Instance entities to ensure consistent naming
2244
2105
  */
@@ -2614,6 +2475,177 @@ class MaestroProcessesService extends BaseService {
2614
2475
  // Fetch BPMN XML and add element name/type to each incident
2615
2476
  return BpmnHelpers.enrichIncidentsWithBpmnData(rawResponse.data || [], folderKey, this.processInstancesService);
2616
2477
  }
2478
+ /**
2479
+ * Get the top 5 processes ranked by run count within a time range.
2480
+ *
2481
+ * Returns an array of up to 5 processes sorted by how many times they were executed,
2482
+ * useful for identifying the most active processes in a given period.
2483
+ *
2484
+ * @param startTime - Start of the time range to query
2485
+ * @param endTime - End of the time range to query
2486
+ * @param options - Optional filters (packageId, processKey, version)
2487
+ * @returns Promise resolving to an array of {@link ProcessGetTopRunCountResponse}
2488
+ * @example
2489
+ * ```typescript
2490
+ * import { MaestroProcesses } from '@uipath/uipath-typescript/maestro-processes';
2491
+ *
2492
+ * const maestroProcesses = new MaestroProcesses(sdk);
2493
+ *
2494
+ * // Get top processes by run count for the last 7 days
2495
+ * const topProcesses = await maestroProcesses.getTopRunCount(
2496
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2497
+ * new Date()
2498
+ * );
2499
+ *
2500
+ * for (const process of topProcesses) {
2501
+ * console.log(`${process.packageId}: ${process.runCount} runs`);
2502
+ * }
2503
+ * ```
2504
+ *
2505
+ * @example
2506
+ * ```typescript
2507
+ * // Get top processes by run count for a specific package
2508
+ * const filtered = await maestroProcesses.getTopRunCount(
2509
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2510
+ * new Date(),
2511
+ * { packageId: '<packageId>' }
2512
+ * );
2513
+ * ```
2514
+ */
2515
+ async getTopRunCount(startTime, endTime, options) {
2516
+ const { data } = await this.post(MAESTRO_ENDPOINTS.INSIGHTS.TOP_PROCESSES_BY_RUN_COUNT, buildInsightsTopBody(startTime, endTime, false, options));
2517
+ return (data ?? []).map(process => ({ ...process, name: process.packageId }));
2518
+ }
2519
+ /**
2520
+ * Get all instances status counts aggregated by date for maestro processes.
2521
+ *
2522
+ * Returns time-grouped counts of instances grouped by status (Completed, Faulted, Cancelled),
2523
+ * useful for rendering time-series charts. Use `groupBy` to control the time bucket size
2524
+ * (hour, day, or week) — defaults to day if not provided.
2525
+ *
2526
+ * @param startTime - Start of the time range to query
2527
+ * @param endTime - End of the time range to query
2528
+ * @param options - Optional settings for time bucketing granularity
2529
+ * @returns Promise resolving to an array of {@link InstanceStatusTimelineResponse}
2530
+ *
2531
+ * @example
2532
+ * ```typescript
2533
+ * // Get daily instance status for the last 7 days
2534
+ * const now = new Date();
2535
+ * const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
2536
+ * const statuses = await maestroProcesses.getInstanceStatusTimeline(sevenDaysAgo, now);
2537
+ *
2538
+ * for (const entry of statuses) {
2539
+ * console.log(`${entry.startTime} — ${entry.status}: ${entry.count}`);
2540
+ * }
2541
+ * ```
2542
+ *
2543
+ * @example
2544
+ * ```typescript
2545
+ * import { TimeInterval } from '@uipath/uipath-typescript/maestro-processes';
2546
+ *
2547
+ * // Get hourly breakdown
2548
+ * const statuses = await maestroProcesses.getInstanceStatusTimeline(startTime, endTime, {
2549
+ * groupBy: TimeInterval.Hour,
2550
+ * });
2551
+ * ```
2552
+ *
2553
+ * @example
2554
+ * ```typescript
2555
+ * // Get all-time data (from Unix epoch to now)
2556
+ * const allTime = await maestroProcesses.getInstanceStatusTimeline(new Date(0), new Date());
2557
+ * ```
2558
+ */
2559
+ async getInstanceStatusTimeline(startTime, endTime, options) {
2560
+ return fetchInstanceStatusTimeline(this.post.bind(this), startTime, endTime, false, options);
2561
+ }
2562
+ /**
2563
+ * Get the top 10 processes ranked by failure count within a time range.
2564
+ *
2565
+ * Returns an array of up to 10 processes sorted by how many instances faulted,
2566
+ * useful for identifying the most error-prone processes in a given period.
2567
+ *
2568
+ * @param startTime - Start of the time range to query
2569
+ * @param endTime - End of the time range to query
2570
+ * @param options - Optional filters (packageId, processKey, version)
2571
+ * @returns Promise resolving to an array of {@link ProcessGetTopFaultedCountResponse}
2572
+ * @example
2573
+ * ```typescript
2574
+ * import { MaestroProcesses } from '@uipath/uipath-typescript/maestro-processes';
2575
+ *
2576
+ * const maestroProcesses = new MaestroProcesses(sdk);
2577
+ *
2578
+ * // Get top processes by faulted count for the last 7 days
2579
+ * const topFailing = await maestroProcesses.getTopFaultedCount(
2580
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2581
+ * new Date()
2582
+ * );
2583
+ *
2584
+ * for (const process of topFailing) {
2585
+ * console.log(`${process.packageId}: ${process.faultedCount} failures`);
2586
+ * }
2587
+ * ```
2588
+ *
2589
+ * @example
2590
+ * ```typescript
2591
+ * // Get top processes by faulted count for a specific package
2592
+ * const filtered = await maestroProcesses.getTopFaultedCount(
2593
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2594
+ * new Date(),
2595
+ * { packageId: '<packageId>' }
2596
+ * );
2597
+ * ```
2598
+ */
2599
+ async getTopFaultedCount(startTime, endTime, options) {
2600
+ const { data } = await this.post(MAESTRO_ENDPOINTS.INSIGHTS.TOP_PROCESSES_WITH_FAILURE, buildInsightsTopBody(startTime, endTime, false, options));
2601
+ return (data ?? []).map(item => ({
2602
+ packageId: item.packageId,
2603
+ processKey: item.processKey,
2604
+ faultedCount: item.runCount,
2605
+ name: item.packageId,
2606
+ }));
2607
+ }
2608
+ /**
2609
+ * Get the top 5 processes ranked by total duration within a time range.
2610
+ *
2611
+ * Returns an array of up to 5 processes sorted by their total execution time,
2612
+ * useful for identifying the longest-running processes in a given period.
2613
+ *
2614
+ * @param startTime - Start of the time range to query
2615
+ * @param endTime - End of the time range to query
2616
+ * @param options - Optional filters (packageId, processKey, version)
2617
+ * @returns Promise resolving to an array of {@link ProcessGetTopDurationResponse}
2618
+ * @example
2619
+ * ```typescript
2620
+ * import { MaestroProcesses } from '@uipath/uipath-typescript/maestro-processes';
2621
+ *
2622
+ * const maestroProcesses = new MaestroProcesses(sdk);
2623
+ *
2624
+ * // Get top processes by duration for the last 7 days
2625
+ * const topProcesses = await maestroProcesses.getTopExecutionDuration(
2626
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2627
+ * new Date()
2628
+ * );
2629
+ *
2630
+ * for (const process of topProcesses) {
2631
+ * console.log(`${process.packageId}: ${process.duration}ms total`);
2632
+ * }
2633
+ * ```
2634
+ *
2635
+ * @example
2636
+ * ```typescript
2637
+ * // Get top processes by duration for a specific package
2638
+ * const filtered = await maestroProcesses.getTopExecutionDuration(
2639
+ * new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
2640
+ * new Date(),
2641
+ * { packageId: '<packageId>' }
2642
+ * );
2643
+ * ```
2644
+ */
2645
+ async getTopExecutionDuration(startTime, endTime, options) {
2646
+ const { data } = await this.post(MAESTRO_ENDPOINTS.INSIGHTS.TOP_PROCESSES_BY_DURATION, buildInsightsTopBody(startTime, endTime, false, options));
2647
+ return (data ?? []).map(process => ({ ...process, name: process.packageId }));
2648
+ }
2617
2649
  }
2618
2650
  __decorate([
2619
2651
  track('MaestroProcesses.GetAll')
@@ -2621,6 +2653,18 @@ __decorate([
2621
2653
  __decorate([
2622
2654
  track('MaestroProcesses.GetIncidents')
2623
2655
  ], MaestroProcessesService.prototype, "getIncidents", null);
2656
+ __decorate([
2657
+ track('MaestroProcesses.GetTopRunCount')
2658
+ ], MaestroProcessesService.prototype, "getTopRunCount", null);
2659
+ __decorate([
2660
+ track('MaestroProcesses.GetInstanceStatusTimeline')
2661
+ ], MaestroProcessesService.prototype, "getInstanceStatusTimeline", null);
2662
+ __decorate([
2663
+ track('MaestroProcesses.GetTopFaultedCount')
2664
+ ], MaestroProcessesService.prototype, "getTopFaultedCount", null);
2665
+ __decorate([
2666
+ track('MaestroProcesses.GetTopExecutionDuration')
2667
+ ], MaestroProcessesService.prototype, "getTopExecutionDuration", null);
2624
2668
 
2625
2669
  /**
2626
2670
  * Service class for Maestro Process Incidents