@uipath/uipath-typescript 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -2
- package/README.md +2 -0
- package/dist/assets/index.cjs +199 -221
- package/dist/assets/index.mjs +199 -221
- package/dist/buckets/index.cjs +199 -221
- package/dist/buckets/index.mjs +199 -221
- package/dist/cases/index.cjs +220 -238
- package/dist/cases/index.mjs +220 -238
- package/dist/conversational-agent/index.cjs +6622 -0
- package/dist/conversational-agent/index.d.ts +6579 -0
- package/dist/conversational-agent/index.mjs +6575 -0
- package/dist/core/index.cjs +1264 -1111
- package/dist/core/index.d.ts +111 -11
- package/dist/core/index.mjs +1247 -1112
- package/dist/entities/index.cjs +199 -221
- package/dist/entities/index.mjs +199 -221
- package/dist/index.cjs +799 -435
- package/dist/index.d.ts +4403 -64
- package/dist/index.mjs +793 -436
- package/dist/index.umd.js +799 -435
- package/dist/maestro-processes/index.cjs +199 -221
- package/dist/maestro-processes/index.mjs +199 -221
- package/dist/processes/index.cjs +246 -232
- package/dist/processes/index.d.ts +1 -1
- package/dist/processes/index.mjs +246 -232
- package/dist/queues/index.cjs +199 -221
- package/dist/queues/index.mjs +199 -221
- package/dist/tasks/index.cjs +199 -221
- package/dist/tasks/index.mjs +199 -221
- package/package.json +22 -4
package/dist/core/index.cjs
CHANGED
|
@@ -3879,6 +3879,15 @@ class ExecutionContext {
|
|
|
3879
3879
|
*/
|
|
3880
3880
|
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
3881
3881
|
|
|
3882
|
+
/**
|
|
3883
|
+
* Session storage keys used by the auth module
|
|
3884
|
+
*/
|
|
3885
|
+
const AUTH_STORAGE_KEYS = {
|
|
3886
|
+
TOKEN_PREFIX: 'uipath_sdk_user_token-',
|
|
3887
|
+
OAUTH_CONTEXT: 'uipath_sdk_oauth_context',
|
|
3888
|
+
CODE_VERIFIER: 'uipath_sdk_code_verifier',
|
|
3889
|
+
};
|
|
3890
|
+
|
|
3882
3891
|
// Type guard to check if config has OAuth credentials
|
|
3883
3892
|
function hasOAuthConfig(config) {
|
|
3884
3893
|
return Boolean(config.clientId && config.redirectUri && config.scope);
|
|
@@ -3889,533 +3898,544 @@ function hasSecretConfig(config) {
|
|
|
3889
3898
|
}
|
|
3890
3899
|
|
|
3891
3900
|
/**
|
|
3892
|
-
*
|
|
3893
|
-
*
|
|
3894
|
-
* - For OAuth tokens: Uses session storage with client ID-based keys
|
|
3895
|
-
* - For Secret tokens: Stores only in memory, allowing multiple instances
|
|
3901
|
+
* Base error class for all UiPath SDK errors
|
|
3902
|
+
* Extends Error for standard error handling compatibility
|
|
3896
3903
|
*/
|
|
3897
|
-
class
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
this.STORAGE_KEY_PREFIX = 'uipath_sdk_user_token-';
|
|
3909
|
-
this.refreshPromise = null;
|
|
3910
|
-
}
|
|
3911
|
-
/**
|
|
3912
|
-
* Checks if a token is expired
|
|
3913
|
-
* @param tokenInfo The token info to check
|
|
3914
|
-
* @returns true if the token is expired, false otherwise
|
|
3915
|
-
*/
|
|
3916
|
-
isTokenExpired(tokenInfo) {
|
|
3917
|
-
// If no token info or no expiration date, token is not expired
|
|
3918
|
-
if (!tokenInfo?.expiresAt) {
|
|
3919
|
-
return false;
|
|
3920
|
-
}
|
|
3921
|
-
return new Date() >= tokenInfo.expiresAt;
|
|
3922
|
-
}
|
|
3923
|
-
/**
|
|
3924
|
-
* Gets the storage key for this TokenManager instance
|
|
3925
|
-
*/
|
|
3926
|
-
_getStorageKey() {
|
|
3927
|
-
return `${this.STORAGE_KEY_PREFIX}${this.config.clientId}`;
|
|
3928
|
-
}
|
|
3929
|
-
/**
|
|
3930
|
-
* Loads token from session storage if available
|
|
3931
|
-
* @returns true if a valid token was loaded, false otherwise
|
|
3932
|
-
*/
|
|
3933
|
-
loadFromStorage() {
|
|
3934
|
-
// Only OAuth tokens are stored in session storage
|
|
3935
|
-
if (!isBrowser || !this.isOAuth) {
|
|
3936
|
-
return false;
|
|
3937
|
-
}
|
|
3938
|
-
try {
|
|
3939
|
-
const storedToken = sessionStorage.getItem(this._getStorageKey());
|
|
3940
|
-
if (!storedToken) {
|
|
3941
|
-
return false;
|
|
3942
|
-
}
|
|
3943
|
-
const tokenInfo = this._parseTokenInfo(storedToken);
|
|
3944
|
-
if (!tokenInfo) {
|
|
3945
|
-
// Invalid token format, clear it
|
|
3946
|
-
sessionStorage.removeItem(this._getStorageKey());
|
|
3947
|
-
return false;
|
|
3948
|
-
}
|
|
3949
|
-
// Check if token is expired
|
|
3950
|
-
if (this.isTokenExpired(tokenInfo)) {
|
|
3951
|
-
// Token expired, clear it
|
|
3952
|
-
sessionStorage.removeItem(this._getStorageKey());
|
|
3953
|
-
return false;
|
|
3954
|
-
}
|
|
3955
|
-
// Valid token found, use it
|
|
3956
|
-
this.currentToken = tokenInfo;
|
|
3957
|
-
this._updateExecutionContext(tokenInfo);
|
|
3958
|
-
return true;
|
|
3959
|
-
}
|
|
3960
|
-
catch (error) {
|
|
3961
|
-
console.warn('Failed to load token from session storage', error);
|
|
3962
|
-
return false;
|
|
3904
|
+
class UiPathError extends Error {
|
|
3905
|
+
constructor(type, params) {
|
|
3906
|
+
super(params.message);
|
|
3907
|
+
this.name = type;
|
|
3908
|
+
this.type = type;
|
|
3909
|
+
this.statusCode = params.statusCode;
|
|
3910
|
+
this.requestId = params.requestId;
|
|
3911
|
+
this.timestamp = new Date();
|
|
3912
|
+
// Maintains proper stack trace for where our error was thrown
|
|
3913
|
+
if (Error.captureStackTrace) {
|
|
3914
|
+
Error.captureStackTrace(this, this.constructor);
|
|
3963
3915
|
}
|
|
3964
3916
|
}
|
|
3965
3917
|
/**
|
|
3966
|
-
*
|
|
3967
|
-
* @param storedToken JSON string from storage
|
|
3968
|
-
* @returns Valid TokenInfo or undefined if invalid
|
|
3918
|
+
* Returns a clean JSON representation of the error
|
|
3969
3919
|
*/
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
return undefined;
|
|
3979
|
-
}
|
|
3980
|
-
if (parsed.type !== 'secret' && parsed.type !== 'oauth') {
|
|
3981
|
-
return undefined;
|
|
3982
|
-
}
|
|
3983
|
-
const tokenInfo = parsed;
|
|
3984
|
-
// Convert string date back to Date object
|
|
3985
|
-
if (tokenInfo.expiresAt) {
|
|
3986
|
-
tokenInfo.expiresAt = new Date(tokenInfo.expiresAt);
|
|
3987
|
-
// Verify it's a valid date
|
|
3988
|
-
if (isNaN(tokenInfo.expiresAt.getTime())) {
|
|
3989
|
-
return undefined;
|
|
3990
|
-
}
|
|
3991
|
-
}
|
|
3992
|
-
return tokenInfo;
|
|
3993
|
-
}
|
|
3994
|
-
catch (error) {
|
|
3995
|
-
console.warn('Failed to parse token info', error);
|
|
3996
|
-
return undefined;
|
|
3997
|
-
}
|
|
3920
|
+
toJSON() {
|
|
3921
|
+
return {
|
|
3922
|
+
type: this.type,
|
|
3923
|
+
message: this.message,
|
|
3924
|
+
statusCode: this.statusCode,
|
|
3925
|
+
requestId: this.requestId,
|
|
3926
|
+
timestamp: this.timestamp
|
|
3927
|
+
};
|
|
3998
3928
|
}
|
|
3999
3929
|
/**
|
|
4000
|
-
*
|
|
3930
|
+
* Returns detailed debug information including stack trace
|
|
4001
3931
|
*/
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
if (isBrowser && this.isOAuth) {
|
|
4008
|
-
try {
|
|
4009
|
-
sessionStorage.setItem(this._getStorageKey(), JSON.stringify(tokenInfo));
|
|
4010
|
-
}
|
|
4011
|
-
catch (error) {
|
|
4012
|
-
console.warn('Failed to store token in session storage', error);
|
|
4013
|
-
}
|
|
4014
|
-
}
|
|
3932
|
+
getDebugInfo() {
|
|
3933
|
+
return {
|
|
3934
|
+
...this.toJSON(),
|
|
3935
|
+
stack: this.stack
|
|
3936
|
+
};
|
|
4015
3937
|
}
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
3938
|
+
}
|
|
3939
|
+
|
|
3940
|
+
/**
|
|
3941
|
+
* HTTP status code constants for error handling
|
|
3942
|
+
*/
|
|
3943
|
+
const HttpStatus = {
|
|
3944
|
+
// Client errors (4xx)
|
|
3945
|
+
BAD_REQUEST: 400,
|
|
3946
|
+
UNAUTHORIZED: 401,
|
|
3947
|
+
FORBIDDEN: 403,
|
|
3948
|
+
NOT_FOUND: 404,
|
|
3949
|
+
TOO_MANY_REQUESTS: 429,
|
|
3950
|
+
// Server errors (5xx)
|
|
3951
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
3952
|
+
NOT_IMPLEMENTED: 501,
|
|
3953
|
+
BAD_GATEWAY: 502,
|
|
3954
|
+
SERVICE_UNAVAILABLE: 503,
|
|
3955
|
+
GATEWAY_TIMEOUT: 504
|
|
3956
|
+
};
|
|
3957
|
+
/**
|
|
3958
|
+
* Error type constants for consistent error identification
|
|
3959
|
+
*/
|
|
3960
|
+
const ErrorType = {
|
|
3961
|
+
AUTHENTICATION: 'AuthenticationError',
|
|
3962
|
+
AUTHORIZATION: 'AuthorizationError',
|
|
3963
|
+
VALIDATION: 'ValidationError',
|
|
3964
|
+
NOT_FOUND: 'NotFoundError',
|
|
3965
|
+
RATE_LIMIT: 'RateLimitError',
|
|
3966
|
+
SERVER: 'ServerError',
|
|
3967
|
+
NETWORK: 'NetworkError'
|
|
3968
|
+
};
|
|
3969
|
+
/**
|
|
3970
|
+
* Standard error message constants
|
|
3971
|
+
*/
|
|
3972
|
+
const ErrorMessages = {
|
|
3973
|
+
// Authentication errors
|
|
3974
|
+
AUTHENTICATION_FAILED: 'Authentication failed',
|
|
3975
|
+
// Authorization errors
|
|
3976
|
+
ACCESS_DENIED: 'Access denied',
|
|
3977
|
+
// Validation errors
|
|
3978
|
+
VALIDATION_FAILED: 'Validation failed',
|
|
3979
|
+
// Not found errors
|
|
3980
|
+
RESOURCE_NOT_FOUND: 'Resource not found',
|
|
3981
|
+
// Rate limit errors
|
|
3982
|
+
RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
|
|
3983
|
+
// Server errors
|
|
3984
|
+
INTERNAL_SERVER_ERROR: 'Internal Server error occurred',
|
|
3985
|
+
// Network errors
|
|
3986
|
+
NETWORK_ERROR: 'Network error occurred'};
|
|
3987
|
+
|
|
3988
|
+
/**
|
|
3989
|
+
* Error thrown when authentication fails (401 errors)
|
|
3990
|
+
* Common scenarios:
|
|
3991
|
+
* - Invalid credentials
|
|
3992
|
+
* - Expired token
|
|
3993
|
+
* - Missing authentication
|
|
3994
|
+
*/
|
|
3995
|
+
class AuthenticationError extends UiPathError {
|
|
3996
|
+
constructor(params = {}) {
|
|
3997
|
+
super(ErrorType.AUTHENTICATION, {
|
|
3998
|
+
message: params.message || ErrorMessages.AUTHENTICATION_FAILED,
|
|
3999
|
+
statusCode: params.statusCode ?? HttpStatus.UNAUTHORIZED,
|
|
4000
|
+
requestId: params.requestId
|
|
4001
|
+
});
|
|
4021
4002
|
}
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4003
|
+
}
|
|
4004
|
+
|
|
4005
|
+
/**
|
|
4006
|
+
* Error thrown when authorization fails (403 errors)
|
|
4007
|
+
* Common scenarios:
|
|
4008
|
+
* - Insufficient permissions
|
|
4009
|
+
* - Access denied to resource
|
|
4010
|
+
* - Invalid scope
|
|
4011
|
+
*/
|
|
4012
|
+
class AuthorizationError extends UiPathError {
|
|
4013
|
+
constructor(params = {}) {
|
|
4014
|
+
super(ErrorType.AUTHORIZATION, {
|
|
4015
|
+
message: params.message || ErrorMessages.ACCESS_DENIED,
|
|
4016
|
+
statusCode: params.statusCode ?? HttpStatus.FORBIDDEN,
|
|
4017
|
+
requestId: params.requestId
|
|
4018
|
+
});
|
|
4027
4019
|
}
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
/**
|
|
4023
|
+
* Error thrown when validation fails (400 errors or client-side validation)
|
|
4024
|
+
* Common scenarios:
|
|
4025
|
+
* - Invalid input parameters
|
|
4026
|
+
* - Missing required fields
|
|
4027
|
+
* - Invalid data format
|
|
4028
|
+
*/
|
|
4029
|
+
class ValidationError extends UiPathError {
|
|
4030
|
+
constructor(params = {}) {
|
|
4031
|
+
super(ErrorType.VALIDATION, {
|
|
4032
|
+
message: params.message || ErrorMessages.VALIDATION_FAILED,
|
|
4033
|
+
statusCode: params.statusCode ?? HttpStatus.BAD_REQUEST,
|
|
4034
|
+
requestId: params.requestId
|
|
4035
|
+
});
|
|
4039
4036
|
}
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
}
|
|
4057
|
-
}
|
|
4037
|
+
}
|
|
4038
|
+
|
|
4039
|
+
/**
|
|
4040
|
+
* Error thrown when a resource is not found (404 errors)
|
|
4041
|
+
* Common scenarios:
|
|
4042
|
+
* - Resource doesn't exist
|
|
4043
|
+
* - Invalid ID provided
|
|
4044
|
+
* - Resource deleted
|
|
4045
|
+
*/
|
|
4046
|
+
class NotFoundError extends UiPathError {
|
|
4047
|
+
constructor(params = {}) {
|
|
4048
|
+
super(ErrorType.NOT_FOUND, {
|
|
4049
|
+
message: params.message || ErrorMessages.RESOURCE_NOT_FOUND,
|
|
4050
|
+
statusCode: params.statusCode ?? HttpStatus.NOT_FOUND,
|
|
4051
|
+
requestId: params.requestId
|
|
4052
|
+
});
|
|
4058
4053
|
}
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4054
|
+
}
|
|
4055
|
+
|
|
4056
|
+
/**
|
|
4057
|
+
* Error thrown when rate limit is exceeded (429 errors)
|
|
4058
|
+
* Common scenarios:
|
|
4059
|
+
* - Too many requests in a time window
|
|
4060
|
+
* - API throttling
|
|
4061
|
+
*/
|
|
4062
|
+
class RateLimitError extends UiPathError {
|
|
4063
|
+
constructor(params = {}) {
|
|
4064
|
+
super(ErrorType.RATE_LIMIT, {
|
|
4065
|
+
message: params.message || ErrorMessages.RATE_LIMIT_EXCEEDED,
|
|
4066
|
+
statusCode: params.statusCode ?? HttpStatus.TOO_MANY_REQUESTS,
|
|
4067
|
+
requestId: params.requestId
|
|
4067
4068
|
});
|
|
4068
4069
|
}
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
const result = await this.refreshPromise;
|
|
4086
|
-
return result;
|
|
4087
|
-
}
|
|
4088
|
-
finally {
|
|
4089
|
-
// Clear the refresh promise when done (success or failure)
|
|
4090
|
-
this.refreshPromise = null;
|
|
4091
|
-
}
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
/**
|
|
4073
|
+
* Error thrown when server encounters an error (5xx errors)
|
|
4074
|
+
* Common scenarios:
|
|
4075
|
+
* - Internal server error
|
|
4076
|
+
* - Service unavailable
|
|
4077
|
+
* - Gateway timeout
|
|
4078
|
+
*/
|
|
4079
|
+
class ServerError extends UiPathError {
|
|
4080
|
+
constructor(params = {}) {
|
|
4081
|
+
super(ErrorType.SERVER, {
|
|
4082
|
+
message: params.message || ErrorMessages.INTERNAL_SERVER_ERROR,
|
|
4083
|
+
statusCode: params.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR,
|
|
4084
|
+
requestId: params.requestId
|
|
4085
|
+
});
|
|
4092
4086
|
}
|
|
4093
4087
|
/**
|
|
4094
|
-
*
|
|
4088
|
+
* Checks if this is a temporary error that might succeed on retry
|
|
4095
4089
|
*/
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
body: body.toString()
|
|
4118
|
-
});
|
|
4119
|
-
if (!response.ok) {
|
|
4120
|
-
const errorData = await response.json().catch(() => ({ message: response.statusText }));
|
|
4121
|
-
console.error("Token refresh error:", errorData);
|
|
4122
|
-
// Clear the invalid token to prevent further failed requests
|
|
4123
|
-
this.clearToken();
|
|
4124
|
-
throw new Error(`Failed to refresh access token: ${JSON.stringify(errorData)}`);
|
|
4125
|
-
}
|
|
4126
|
-
const token = await response.json();
|
|
4127
|
-
this.setToken({
|
|
4128
|
-
token: token.access_token,
|
|
4129
|
-
type: 'oauth',
|
|
4130
|
-
expiresAt: new Date(Date.now() + token.expires_in * 1000),
|
|
4131
|
-
refreshToken: token.refresh_token
|
|
4090
|
+
get isRetryable() {
|
|
4091
|
+
return this.statusCode === HttpStatus.BAD_GATEWAY ||
|
|
4092
|
+
this.statusCode === HttpStatus.SERVICE_UNAVAILABLE ||
|
|
4093
|
+
this.statusCode === HttpStatus.GATEWAY_TIMEOUT;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
/**
|
|
4098
|
+
* Error thrown when network/connection issues occur
|
|
4099
|
+
* Common scenarios:
|
|
4100
|
+
* - Connection timeout
|
|
4101
|
+
* - DNS resolution failure
|
|
4102
|
+
* - Network unreachable
|
|
4103
|
+
* - Request aborted
|
|
4104
|
+
*/
|
|
4105
|
+
class NetworkError extends UiPathError {
|
|
4106
|
+
constructor(params = {}) {
|
|
4107
|
+
super(ErrorType.NETWORK, {
|
|
4108
|
+
message: params.message || ErrorMessages.NETWORK_ERROR,
|
|
4109
|
+
statusCode: params.statusCode, // Network errors typically don't have HTTP status codes
|
|
4110
|
+
requestId: params.requestId
|
|
4132
4111
|
});
|
|
4133
|
-
return token;
|
|
4134
4112
|
}
|
|
4135
4113
|
}
|
|
4136
4114
|
|
|
4137
4115
|
/**
|
|
4138
|
-
*
|
|
4139
|
-
* Centralized location for all API endpoints used throughout the SDK
|
|
4116
|
+
* Type guard to check if an error is a UiPathError
|
|
4140
4117
|
*/
|
|
4118
|
+
function isUiPathError(error) {
|
|
4119
|
+
return error instanceof UiPathError;
|
|
4120
|
+
}
|
|
4141
4121
|
/**
|
|
4142
|
-
*
|
|
4122
|
+
* Type guard to check if an error is an AuthenticationError
|
|
4143
4123
|
*/
|
|
4144
|
-
|
|
4124
|
+
function isAuthenticationError(error) {
|
|
4125
|
+
return error instanceof AuthenticationError;
|
|
4126
|
+
}
|
|
4145
4127
|
/**
|
|
4146
|
-
*
|
|
4128
|
+
* Type guard to check if an error is an AuthorizationError
|
|
4147
4129
|
*/
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4130
|
+
function isAuthorizationError(error) {
|
|
4131
|
+
return error instanceof AuthorizationError;
|
|
4132
|
+
}
|
|
4133
|
+
/**
|
|
4134
|
+
* Type guard to check if an error is a ValidationError
|
|
4135
|
+
*/
|
|
4136
|
+
function isValidationError(error) {
|
|
4137
|
+
return error instanceof ValidationError;
|
|
4138
|
+
}
|
|
4139
|
+
/**
|
|
4140
|
+
* Type guard to check if an error is a NotFoundError
|
|
4141
|
+
*/
|
|
4142
|
+
function isNotFoundError(error) {
|
|
4143
|
+
return error instanceof NotFoundError;
|
|
4144
|
+
}
|
|
4145
|
+
/**
|
|
4146
|
+
* Type guard to check if an error is a RateLimitError
|
|
4147
|
+
*/
|
|
4148
|
+
function isRateLimitError(error) {
|
|
4149
|
+
return error instanceof RateLimitError;
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Type guard to check if an error is a ServerError
|
|
4153
|
+
*/
|
|
4154
|
+
function isServerError(error) {
|
|
4155
|
+
return error instanceof ServerError;
|
|
4156
|
+
}
|
|
4157
|
+
/**
|
|
4158
|
+
* Type guard to check if an error is a NetworkError
|
|
4159
|
+
*/
|
|
4160
|
+
function isNetworkError(error) {
|
|
4161
|
+
return error instanceof NetworkError;
|
|
4162
|
+
}
|
|
4163
|
+
/**
|
|
4164
|
+
* Helper to get error details in a safe way
|
|
4165
|
+
*/
|
|
4166
|
+
function getErrorDetails(error) {
|
|
4167
|
+
if (isUiPathError(error)) {
|
|
4168
|
+
return {
|
|
4169
|
+
message: error.message,
|
|
4170
|
+
statusCode: error.statusCode
|
|
4171
|
+
};
|
|
4164
4172
|
}
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
if (!isBrowser)
|
|
4170
|
-
return false;
|
|
4171
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
4172
|
-
const code = urlParams.get('code');
|
|
4173
|
-
const hasCodeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4174
|
-
return !!(code && hasCodeVerifier);
|
|
4173
|
+
if (error instanceof Error) {
|
|
4174
|
+
return {
|
|
4175
|
+
message: error.message
|
|
4176
|
+
};
|
|
4175
4177
|
}
|
|
4178
|
+
return {
|
|
4179
|
+
message: String(error)
|
|
4180
|
+
};
|
|
4181
|
+
}
|
|
4182
|
+
|
|
4183
|
+
/**
|
|
4184
|
+
* TokenManager is responsible for managing authentication tokens.
|
|
4185
|
+
* It provides token operations for a specific client ID.
|
|
4186
|
+
* - For OAuth tokens: Uses session storage with client ID-based keys
|
|
4187
|
+
* - For Secret tokens: Stores only in memory, allowing multiple instances
|
|
4188
|
+
*/
|
|
4189
|
+
class TokenManager {
|
|
4176
4190
|
/**
|
|
4177
|
-
*
|
|
4191
|
+
* Creates a new TokenManager instance
|
|
4192
|
+
* @param executionContext The execution context
|
|
4193
|
+
* @param config The SDK configuration
|
|
4194
|
+
* @param isOAuth Whether this is an OAuth-based authentication
|
|
4178
4195
|
*/
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4196
|
+
constructor(executionContext, config, isOAuth = false) {
|
|
4197
|
+
this.executionContext = executionContext;
|
|
4198
|
+
this.config = config;
|
|
4199
|
+
this.isOAuth = isOAuth;
|
|
4200
|
+
this.refreshPromise = null;
|
|
4201
|
+
}
|
|
4202
|
+
/**
|
|
4203
|
+
* Checks if a token is expired
|
|
4204
|
+
* @param tokenInfo The token info to check
|
|
4205
|
+
* @returns true if the token is expired, false otherwise
|
|
4206
|
+
*/
|
|
4207
|
+
isTokenExpired(tokenInfo) {
|
|
4208
|
+
// If no token info or no expiration date, token is not expired
|
|
4209
|
+
if (!tokenInfo?.expiresAt) {
|
|
4210
|
+
return false;
|
|
4182
4211
|
}
|
|
4212
|
+
return new Date() >= tokenInfo.expiresAt;
|
|
4213
|
+
}
|
|
4214
|
+
/**
|
|
4215
|
+
* Gets a valid authentication token, refreshing if necessary.
|
|
4216
|
+
* This is the single source of truth for token validation and refresh logic.
|
|
4217
|
+
*
|
|
4218
|
+
* @returns The valid token string
|
|
4219
|
+
* @throws AuthenticationError if no token available or refresh fails
|
|
4220
|
+
*/
|
|
4221
|
+
async getValidToken() {
|
|
4222
|
+
const tokenInfo = this.executionContext.get('tokenInfo');
|
|
4223
|
+
if (!tokenInfo) {
|
|
4224
|
+
throw new AuthenticationError({
|
|
4225
|
+
message: 'No authentication token available. Make sure to initialize the SDK first.'
|
|
4226
|
+
});
|
|
4227
|
+
}
|
|
4228
|
+
// For secret-based tokens, they never expire
|
|
4229
|
+
if (tokenInfo.type === 'secret') {
|
|
4230
|
+
return tokenInfo.token;
|
|
4231
|
+
}
|
|
4232
|
+
// If token is not expired, return it
|
|
4233
|
+
if (!this.isTokenExpired(tokenInfo)) {
|
|
4234
|
+
return tokenInfo.token;
|
|
4235
|
+
}
|
|
4236
|
+
// Token is expired, refresh it
|
|
4183
4237
|
try {
|
|
4184
|
-
const
|
|
4185
|
-
|
|
4186
|
-
return null;
|
|
4187
|
-
}
|
|
4188
|
-
const context = JSON.parse(stored);
|
|
4189
|
-
// Validate required fields
|
|
4190
|
-
if (!context.codeVerifier || !context.clientId || !context.redirectUri ||
|
|
4191
|
-
!context.baseUrl || !context.orgName) {
|
|
4192
|
-
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4193
|
-
return null;
|
|
4194
|
-
}
|
|
4195
|
-
return context;
|
|
4238
|
+
const newToken = await this.refreshAccessToken();
|
|
4239
|
+
return newToken.access_token;
|
|
4196
4240
|
}
|
|
4197
4241
|
catch (error) {
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4242
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
4243
|
+
throw new AuthenticationError({
|
|
4244
|
+
message: `Token refresh failed: ${message}. Please re-authenticate.`,
|
|
4245
|
+
statusCode: HttpStatus.UNAUTHORIZED
|
|
4246
|
+
});
|
|
4201
4247
|
}
|
|
4202
4248
|
}
|
|
4203
4249
|
/**
|
|
4204
|
-
*
|
|
4205
|
-
*/
|
|
4206
|
-
static _mergeConfigWithContext(config, context) {
|
|
4207
|
-
return {
|
|
4208
|
-
...config,
|
|
4209
|
-
baseUrl: context.baseUrl,
|
|
4210
|
-
orgName: context.orgName,
|
|
4211
|
-
tenantName: context.tenantName,
|
|
4212
|
-
clientId: context.clientId,
|
|
4213
|
-
redirectUri: context.redirectUri,
|
|
4214
|
-
scope: context.scope
|
|
4215
|
-
};
|
|
4216
|
-
}
|
|
4217
|
-
/**
|
|
4218
|
-
* Get the token manager instance
|
|
4250
|
+
* Gets the storage key for this TokenManager instance
|
|
4219
4251
|
*/
|
|
4220
|
-
|
|
4221
|
-
return this.
|
|
4252
|
+
_getStorageKey() {
|
|
4253
|
+
return `${AUTH_STORAGE_KEYS.TOKEN_PREFIX}${this.config.clientId}`;
|
|
4222
4254
|
}
|
|
4223
4255
|
/**
|
|
4224
|
-
*
|
|
4225
|
-
*
|
|
4226
|
-
* For secret-based authentication, see authenticateWithSecret().
|
|
4227
|
-
* @param config The SDK configuration object.
|
|
4228
|
-
* @returns A promise that resolves to true if authentication is successful, otherwise false.
|
|
4229
|
-
* In an OAuth flow, this method will trigger a page redirect and the promise will not resolve.
|
|
4256
|
+
* Loads token from session storage if available
|
|
4257
|
+
* @returns true if a valid token was loaded, false otherwise
|
|
4230
4258
|
*/
|
|
4231
|
-
|
|
4232
|
-
|
|
4259
|
+
loadFromStorage() {
|
|
4260
|
+
// Only OAuth tokens are stored in session storage
|
|
4261
|
+
if (!isBrowser || !this.isOAuth) {
|
|
4233
4262
|
return false;
|
|
4234
4263
|
}
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
if (!code) {
|
|
4240
|
-
throw new Error('Authorization code missing in OAuth callback');
|
|
4264
|
+
try {
|
|
4265
|
+
const storedToken = sessionStorage.getItem(this._getStorageKey());
|
|
4266
|
+
if (!storedToken) {
|
|
4267
|
+
return false;
|
|
4241
4268
|
}
|
|
4242
|
-
|
|
4243
|
-
if (
|
|
4244
|
-
|
|
4269
|
+
const tokenInfo = this._parseTokenInfo(storedToken);
|
|
4270
|
+
if (!tokenInfo) {
|
|
4271
|
+
// Invalid token format, clear it
|
|
4272
|
+
sessionStorage.removeItem(this._getStorageKey());
|
|
4273
|
+
return false;
|
|
4245
4274
|
}
|
|
4246
|
-
//
|
|
4247
|
-
if (
|
|
4248
|
-
|
|
4275
|
+
// Check if token is expired
|
|
4276
|
+
if (this.isTokenExpired(tokenInfo)) {
|
|
4277
|
+
// Token expired, clear it
|
|
4278
|
+
sessionStorage.removeItem(this._getStorageKey());
|
|
4279
|
+
return false;
|
|
4249
4280
|
}
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
// Secondly: Try to load existing valid token from storage
|
|
4254
|
-
const loadedFromStorage = this.tokenManager.loadFromStorage();
|
|
4255
|
-
// If we have a valid token from storage, return true
|
|
4256
|
-
if (loadedFromStorage && this.tokenManager.hasValidToken()) {
|
|
4281
|
+
// Valid token found, use it
|
|
4282
|
+
this.currentToken = tokenInfo;
|
|
4283
|
+
this._updateExecutionContext(tokenInfo);
|
|
4257
4284
|
return true;
|
|
4258
4285
|
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
return
|
|
4286
|
+
catch (error) {
|
|
4287
|
+
console.warn('Failed to load token from session storage', error);
|
|
4288
|
+
return false;
|
|
4262
4289
|
}
|
|
4263
|
-
return false;
|
|
4264
4290
|
}
|
|
4265
4291
|
/**
|
|
4266
|
-
*
|
|
4292
|
+
* Parse and validate token info from storage
|
|
4293
|
+
* @param storedToken JSON string from storage
|
|
4294
|
+
* @returns Valid TokenInfo or undefined if invalid
|
|
4267
4295
|
*/
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
const isInOAuthFlow = codeVerifier !== null;
|
|
4275
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
4276
|
-
const code = urlParams.get('code');
|
|
4277
|
-
// If we're in an OAuth flow (server-controlled code verifier exists), handle the callback
|
|
4278
|
-
if (isInOAuthFlow) {
|
|
4279
|
-
// We're expecting a callback - validate parameters
|
|
4280
|
-
if (!code) {
|
|
4281
|
-
// Clear stored state on error
|
|
4282
|
-
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4283
|
-
throw new Error('Authorization code missing in OAuth callback');
|
|
4296
|
+
_parseTokenInfo(storedToken) {
|
|
4297
|
+
try {
|
|
4298
|
+
const parsed = JSON.parse(storedToken);
|
|
4299
|
+
// Basic validation
|
|
4300
|
+
if (typeof parsed !== 'object' || !parsed) {
|
|
4301
|
+
return undefined;
|
|
4284
4302
|
}
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
const codePattern = /^[A-Za-z0-9\-._~+/]+=*$/;
|
|
4288
|
-
if (!codePattern.test(code)) {
|
|
4289
|
-
// Clear stored state on error
|
|
4290
|
-
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4291
|
-
throw new Error('Invalid authorization code format');
|
|
4303
|
+
if (typeof parsed.token !== 'string' || !parsed.token) {
|
|
4304
|
+
return undefined;
|
|
4292
4305
|
}
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4306
|
+
if (parsed.type !== 'secret' && parsed.type !== 'oauth') {
|
|
4307
|
+
return undefined;
|
|
4308
|
+
}
|
|
4309
|
+
const tokenInfo = parsed;
|
|
4310
|
+
// Convert string date back to Date object
|
|
4311
|
+
if (tokenInfo.expiresAt) {
|
|
4312
|
+
tokenInfo.expiresAt = new Date(tokenInfo.expiresAt);
|
|
4313
|
+
// Verify it's a valid date
|
|
4314
|
+
if (isNaN(tokenInfo.expiresAt.getTime())) {
|
|
4315
|
+
return undefined;
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
return tokenInfo;
|
|
4296
4319
|
}
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
await this._initiateOAuthFlow(clientId, redirectUri, scope);
|
|
4301
|
-
// This line is not expected to be reached due to redirect
|
|
4302
|
-
return false;
|
|
4320
|
+
catch (error) {
|
|
4321
|
+
console.warn('Failed to parse token info', error);
|
|
4322
|
+
return undefined;
|
|
4303
4323
|
}
|
|
4304
4324
|
}
|
|
4305
4325
|
/**
|
|
4306
|
-
*
|
|
4326
|
+
* Sets a new token and updates all necessary contexts
|
|
4307
4327
|
*/
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4328
|
+
setToken(tokenInfo) {
|
|
4329
|
+
this.currentToken = tokenInfo;
|
|
4330
|
+
// Store token in execution context
|
|
4331
|
+
this._updateExecutionContext(tokenInfo);
|
|
4332
|
+
// Store in session storage if in browser and this is an OAuth token
|
|
4333
|
+
if (isBrowser && this.isOAuth) {
|
|
4334
|
+
try {
|
|
4335
|
+
sessionStorage.setItem(this._getStorageKey(), JSON.stringify(tokenInfo));
|
|
4336
|
+
}
|
|
4337
|
+
catch (error) {
|
|
4338
|
+
console.warn('Failed to store token in session storage', error);
|
|
4339
|
+
}
|
|
4316
4340
|
}
|
|
4317
4341
|
}
|
|
4318
4342
|
/**
|
|
4319
|
-
*
|
|
4320
|
-
* @param tokenInfo The token information containing the access token, type, expiration, and refresh token
|
|
4343
|
+
* Gets the current token information
|
|
4321
4344
|
*/
|
|
4322
|
-
|
|
4323
|
-
this.
|
|
4345
|
+
getTokenInfo() {
|
|
4346
|
+
return this.currentToken;
|
|
4324
4347
|
}
|
|
4325
4348
|
/**
|
|
4326
|
-
*
|
|
4349
|
+
* Gets just the token string
|
|
4327
4350
|
*/
|
|
4328
|
-
|
|
4329
|
-
return this.
|
|
4351
|
+
getToken() {
|
|
4352
|
+
return this.currentToken?.token;
|
|
4330
4353
|
}
|
|
4331
4354
|
/**
|
|
4332
|
-
*
|
|
4355
|
+
* Checks if we have a valid token
|
|
4333
4356
|
*/
|
|
4334
|
-
|
|
4335
|
-
if (!this.
|
|
4336
|
-
return
|
|
4357
|
+
hasValidToken() {
|
|
4358
|
+
if (!this.currentToken) {
|
|
4359
|
+
return false;
|
|
4337
4360
|
}
|
|
4338
|
-
|
|
4361
|
+
if (this.isTokenExpired(this.currentToken)) {
|
|
4362
|
+
return false;
|
|
4363
|
+
}
|
|
4364
|
+
return true;
|
|
4339
4365
|
}
|
|
4340
4366
|
/**
|
|
4341
|
-
*
|
|
4367
|
+
* Clears the current token
|
|
4342
4368
|
*/
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4369
|
+
clearToken() {
|
|
4370
|
+
this.currentToken = undefined;
|
|
4371
|
+
this.executionContext.set('tokenInfo', undefined);
|
|
4372
|
+
const headers = this.executionContext.getHeaders();
|
|
4373
|
+
delete headers['Authorization'];
|
|
4374
|
+
this.executionContext.setHeaders(headers);
|
|
4375
|
+
// Remove from session storage if this is an OAuth token
|
|
4376
|
+
if (isBrowser && this.isOAuth) {
|
|
4351
4377
|
try {
|
|
4352
|
-
|
|
4353
|
-
return crypto.randomBytes(32)
|
|
4354
|
-
.toString('base64')
|
|
4355
|
-
.replace(/\+/g, '-')
|
|
4356
|
-
.replace(/\//g, '_')
|
|
4357
|
-
.replace(/=/g, '');
|
|
4378
|
+
sessionStorage.removeItem(this._getStorageKey());
|
|
4358
4379
|
}
|
|
4359
|
-
catch {
|
|
4360
|
-
|
|
4380
|
+
catch (error) {
|
|
4381
|
+
console.warn('Failed to remove token from session storage', error);
|
|
4361
4382
|
}
|
|
4362
4383
|
}
|
|
4363
4384
|
}
|
|
4364
4385
|
/**
|
|
4365
|
-
*
|
|
4386
|
+
* Updates execution context with token information
|
|
4366
4387
|
*/
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
}
|
|
4374
|
-
else {
|
|
4375
|
-
// In Node.js environment
|
|
4376
|
-
try {
|
|
4377
|
-
const crypto = require('crypto');
|
|
4378
|
-
return crypto.createHash('sha256')
|
|
4379
|
-
.update(codeVerifier)
|
|
4380
|
-
.digest('base64')
|
|
4381
|
-
.replace(/\+/g, '-')
|
|
4382
|
-
.replace(/\//g, '_')
|
|
4383
|
-
.replace(/=/g, '');
|
|
4384
|
-
}
|
|
4385
|
-
catch {
|
|
4386
|
-
throw new Error("crypto not available in browser");
|
|
4387
|
-
}
|
|
4388
|
-
}
|
|
4388
|
+
_updateExecutionContext(tokenInfo) {
|
|
4389
|
+
this.executionContext.set('tokenInfo', tokenInfo);
|
|
4390
|
+
// Update authorization header
|
|
4391
|
+
this.executionContext.setHeaders({
|
|
4392
|
+
'Authorization': `Bearer ${tokenInfo.token}`
|
|
4393
|
+
});
|
|
4389
4394
|
}
|
|
4390
4395
|
/**
|
|
4391
|
-
*
|
|
4396
|
+
* Refreshes the access token using the stored refresh token.
|
|
4397
|
+
* This method only works for OAuth flow.
|
|
4398
|
+
* Uses a lock mechanism to prevent multiple simultaneous refreshes.
|
|
4399
|
+
* @returns A promise that resolves to the new AuthToken
|
|
4400
|
+
* @throws Error if not in OAuth flow, refresh token is missing, or the request fails
|
|
4392
4401
|
*/
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4402
|
+
async refreshAccessToken() {
|
|
4403
|
+
// If there's already a refresh in progress, return that promise
|
|
4404
|
+
if (this.refreshPromise) {
|
|
4405
|
+
return this.refreshPromise;
|
|
4406
|
+
}
|
|
4407
|
+
try {
|
|
4408
|
+
// Create new refresh promise
|
|
4409
|
+
this.refreshPromise = this._doRefreshToken();
|
|
4410
|
+
// Wait for refresh to complete
|
|
4411
|
+
const result = await this.refreshPromise;
|
|
4412
|
+
return result;
|
|
4413
|
+
}
|
|
4414
|
+
finally {
|
|
4415
|
+
// Clear the refresh promise when done (success or failure)
|
|
4416
|
+
this.refreshPromise = null;
|
|
4417
|
+
}
|
|
4405
4418
|
}
|
|
4406
4419
|
/**
|
|
4407
|
-
*
|
|
4420
|
+
* Internal method to perform the actual token refresh
|
|
4408
4421
|
*/
|
|
4409
|
-
async
|
|
4422
|
+
async _doRefreshToken() {
|
|
4423
|
+
// Check if we're in OAuth flow
|
|
4424
|
+
if (!hasOAuthConfig(this.config)) {
|
|
4425
|
+
throw new Error('refreshAccessToken is only available in OAuth flow');
|
|
4426
|
+
}
|
|
4427
|
+
// Get current token info from token manager
|
|
4428
|
+
const tokenInfo = this.getTokenInfo();
|
|
4429
|
+
if (!tokenInfo?.refreshToken) {
|
|
4430
|
+
throw new Error('No refresh token available. User may need to re-authenticate.');
|
|
4431
|
+
}
|
|
4410
4432
|
const orgName = this.config.orgName;
|
|
4411
4433
|
const body = new URLSearchParams({
|
|
4412
|
-
grant_type: '
|
|
4413
|
-
client_id:
|
|
4414
|
-
|
|
4415
|
-
redirect_uri: params.redirectUri,
|
|
4416
|
-
code_verifier: params.codeVerifier
|
|
4434
|
+
grant_type: 'refresh_token',
|
|
4435
|
+
client_id: this.config.clientId,
|
|
4436
|
+
refresh_token: tokenInfo.refreshToken
|
|
4417
4437
|
});
|
|
4418
|
-
const response = await fetch(`${this.config.baseUrl}/${orgName}
|
|
4438
|
+
const response = await fetch(`${this.config.baseUrl}/${orgName}/identity_/connect/token`, {
|
|
4419
4439
|
method: 'POST',
|
|
4420
4440
|
headers: {
|
|
4421
4441
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
@@ -4424,798 +4444,913 @@ class AuthService {
|
|
|
4424
4444
|
});
|
|
4425
4445
|
if (!response.ok) {
|
|
4426
4446
|
const errorData = await response.json().catch(() => ({ message: response.statusText }));
|
|
4427
|
-
console.error("
|
|
4428
|
-
|
|
4447
|
+
console.error("Token refresh error:", errorData);
|
|
4448
|
+
// Clear the invalid token to prevent further failed requests
|
|
4449
|
+
this.clearToken();
|
|
4450
|
+
throw new Error(`Failed to refresh access token: ${JSON.stringify(errorData)}`);
|
|
4429
4451
|
}
|
|
4430
4452
|
const token = await response.json();
|
|
4431
|
-
this.
|
|
4453
|
+
this.setToken({
|
|
4432
4454
|
token: token.access_token,
|
|
4433
4455
|
type: 'oauth',
|
|
4434
|
-
expiresAt:
|
|
4456
|
+
expiresAt: new Date(Date.now() + token.expires_in * 1000),
|
|
4435
4457
|
refreshToken: token.refresh_token
|
|
4436
4458
|
});
|
|
4437
4459
|
return token;
|
|
4438
4460
|
}
|
|
4439
|
-
/**
|
|
4440
|
-
* Base64URL encodes an array buffer
|
|
4441
|
-
*/
|
|
4442
|
-
_base64URLEncode(buffer) {
|
|
4443
|
-
const base64 = btoa(String.fromCharCode(...buffer));
|
|
4444
|
-
return base64
|
|
4445
|
-
.replace(/\+/g, '-')
|
|
4446
|
-
.replace(/\//g, '_')
|
|
4447
|
-
.replace(/=/g, '');
|
|
4448
|
-
}
|
|
4449
|
-
async _initiateOAuthFlow(clientId, redirectUri, scope) {
|
|
4450
|
-
const codeVerifier = this.generateCodeVerifier();
|
|
4451
|
-
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
4452
|
-
// Store complete OAuth context for callback completion
|
|
4453
|
-
const oauthContext = {
|
|
4454
|
-
codeVerifier,
|
|
4455
|
-
clientId,
|
|
4456
|
-
redirectUri,
|
|
4457
|
-
baseUrl: this.config.baseUrl,
|
|
4458
|
-
orgName: this.config.orgName,
|
|
4459
|
-
tenantName: this.config.tenantName,
|
|
4460
|
-
scope
|
|
4461
|
-
};
|
|
4462
|
-
sessionStorage.setItem('uipath_sdk_oauth_context', JSON.stringify(oauthContext));
|
|
4463
|
-
sessionStorage.setItem('uipath_sdk_code_verifier', codeVerifier);
|
|
4464
|
-
const authUrl = this.getAuthorizationUrl({
|
|
4465
|
-
clientId,
|
|
4466
|
-
redirectUri,
|
|
4467
|
-
codeChallenge,
|
|
4468
|
-
scope
|
|
4469
|
-
});
|
|
4470
|
-
window.location.href = authUrl;
|
|
4471
|
-
}
|
|
4472
|
-
async _handleOAuthCallback(code, clientId, redirectUri) {
|
|
4473
|
-
const codeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4474
|
-
if (!codeVerifier) {
|
|
4475
|
-
throw new Error('Code verifier not found in session storage. Authentication may have been interrupted.');
|
|
4476
|
-
}
|
|
4477
|
-
await this._getAccessToken({
|
|
4478
|
-
clientId,
|
|
4479
|
-
redirectUri,
|
|
4480
|
-
code,
|
|
4481
|
-
codeVerifier
|
|
4482
|
-
});
|
|
4483
|
-
// Clear OAuth context and code verifier after successful token exchange
|
|
4484
|
-
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4485
|
-
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4486
|
-
const url = new URL(window.location.href);
|
|
4487
|
-
url.searchParams.delete('code');
|
|
4488
|
-
url.searchParams.delete('state');
|
|
4489
|
-
window.history.replaceState({}, '', url.toString());
|
|
4490
|
-
}
|
|
4491
|
-
}
|
|
4492
|
-
|
|
4493
|
-
function validateConfig(config) {
|
|
4494
|
-
if (!config.baseUrl || !config.orgName || !config.tenantName) {
|
|
4495
|
-
throw new Error('Missing required configuration: baseUrl, orgName, and tenantName are required');
|
|
4496
|
-
}
|
|
4497
|
-
if (!hasSecretConfig(config) && !hasOAuthConfig(config)) {
|
|
4498
|
-
throw new Error('Invalid configuration: must provide either secret or (clientId, redirectUri, and scope)');
|
|
4499
|
-
}
|
|
4500
|
-
}
|
|
4501
|
-
function normalizeBaseUrl(url) {
|
|
4502
|
-
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
4503
4461
|
}
|
|
4504
4462
|
|
|
4505
4463
|
/**
|
|
4506
|
-
*
|
|
4464
|
+
* Base path constants for different services
|
|
4507
4465
|
*/
|
|
4508
|
-
|
|
4509
|
-
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";
|
|
4510
|
-
// SDK Version placeholder
|
|
4511
|
-
const SDK_VERSION = "1.0.0";
|
|
4512
|
-
const VERSION = "Version";
|
|
4513
|
-
const SERVICE = "Service";
|
|
4514
|
-
const CLOUD_ORGANIZATION_NAME = "CloudOrganizationName";
|
|
4515
|
-
const CLOUD_TENANT_NAME = "CloudTenantName";
|
|
4516
|
-
const CLOUD_URL = "CloudUrl";
|
|
4517
|
-
const CLOUD_CLIENT_ID = "CloudClientId";
|
|
4518
|
-
const CLOUD_REDIRECT_URI = "CloudRedirectUri";
|
|
4519
|
-
const APP_NAME = "ApplicationName";
|
|
4520
|
-
const CLOUD_ROLE_NAME = "uipath-ts-sdk";
|
|
4521
|
-
// Service and logger names
|
|
4522
|
-
const SDK_SERVICE_NAME = "UiPath.TypeScript.Sdk";
|
|
4523
|
-
const SDK_LOGGER_NAME = "uipath-ts-sdk-telemetry";
|
|
4524
|
-
// Event names
|
|
4525
|
-
const SDK_RUN_EVENT = "Sdk.Run";
|
|
4526
|
-
// Default value for unknown/empty attributes
|
|
4527
|
-
const UNKNOWN = "";
|
|
4466
|
+
const IDENTITY_BASE = 'identity_';
|
|
4528
4467
|
|
|
4529
4468
|
/**
|
|
4530
|
-
*
|
|
4469
|
+
* Identity/Authentication Endpoints
|
|
4531
4470
|
*/
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4471
|
+
/**
|
|
4472
|
+
* Identity Service Endpoints
|
|
4473
|
+
*/
|
|
4474
|
+
const IDENTITY_ENDPOINTS = {
|
|
4475
|
+
TOKEN: `${IDENTITY_BASE}/connect/token`,
|
|
4476
|
+
AUTHORIZE: `${IDENTITY_BASE}/connect/authorize`,
|
|
4477
|
+
};
|
|
4478
|
+
|
|
4479
|
+
class AuthService {
|
|
4480
|
+
constructor(config, executionContext) {
|
|
4481
|
+
// Check if we should use stored OAuth context instead of provided config
|
|
4482
|
+
const storedContext = AuthService.getStoredOAuthContext();
|
|
4483
|
+
const effectiveConfig = storedContext ? AuthService._mergeConfigWithContext(config, storedContext) : config;
|
|
4484
|
+
this.config = effectiveConfig;
|
|
4485
|
+
const isOAuth = hasOAuthConfig(effectiveConfig);
|
|
4486
|
+
this.tokenManager = new TokenManager(executionContext, effectiveConfig, isOAuth);
|
|
4487
|
+
// Auto-load token from storage on initialization
|
|
4488
|
+
// This ensures isAuthenticated() returns true after page refresh if a valid token exists
|
|
4489
|
+
this.tokenManager.loadFromStorage();
|
|
4535
4490
|
}
|
|
4536
|
-
|
|
4491
|
+
/**
|
|
4492
|
+
* Check if we're in an OAuth callback state
|
|
4493
|
+
*/
|
|
4494
|
+
static isInOAuthCallback() {
|
|
4495
|
+
if (!isBrowser)
|
|
4496
|
+
return false;
|
|
4497
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
4498
|
+
const code = urlParams.get('code');
|
|
4499
|
+
const hasCodeVerifier = sessionStorage.getItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4500
|
+
return !!(code && hasCodeVerifier);
|
|
4501
|
+
}
|
|
4502
|
+
/**
|
|
4503
|
+
* Get stored OAuth context
|
|
4504
|
+
*/
|
|
4505
|
+
static getStoredOAuthContext() {
|
|
4506
|
+
if (!isBrowser) {
|
|
4507
|
+
return null;
|
|
4508
|
+
}
|
|
4537
4509
|
try {
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4510
|
+
const stored = sessionStorage.getItem(AUTH_STORAGE_KEYS.OAUTH_CONTEXT);
|
|
4511
|
+
if (!stored) {
|
|
4512
|
+
return null;
|
|
4513
|
+
}
|
|
4514
|
+
const context = JSON.parse(stored);
|
|
4515
|
+
// Validate required fields
|
|
4516
|
+
if (!context.codeVerifier || !context.clientId || !context.redirectUri ||
|
|
4517
|
+
!context.baseUrl || !context.orgName) {
|
|
4518
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.OAUTH_CONTEXT);
|
|
4519
|
+
return null;
|
|
4520
|
+
}
|
|
4521
|
+
return context;
|
|
4542
4522
|
}
|
|
4543
4523
|
catch (error) {
|
|
4544
|
-
|
|
4545
|
-
|
|
4524
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.OAUTH_CONTEXT);
|
|
4525
|
+
console.warn('Failed to parse stored OAuth context from session storage', error);
|
|
4526
|
+
return null;
|
|
4546
4527
|
}
|
|
4547
4528
|
}
|
|
4548
|
-
|
|
4549
|
-
|
|
4529
|
+
/**
|
|
4530
|
+
* Merges provided config with stored OAuth context, prioritizing stored values
|
|
4531
|
+
*/
|
|
4532
|
+
static _mergeConfigWithContext(config, context) {
|
|
4533
|
+
return {
|
|
4534
|
+
...config,
|
|
4535
|
+
baseUrl: context.baseUrl,
|
|
4536
|
+
orgName: context.orgName,
|
|
4537
|
+
tenantName: context.tenantName,
|
|
4538
|
+
clientId: context.clientId,
|
|
4539
|
+
redirectUri: context.redirectUri,
|
|
4540
|
+
scope: context.scope
|
|
4541
|
+
};
|
|
4550
4542
|
}
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
time: new Date().toISOString(),
|
|
4557
|
-
iKey: this.extractInstrumentationKey(),
|
|
4558
|
-
data: {
|
|
4559
|
-
baseType: 'EventData',
|
|
4560
|
-
baseData: {
|
|
4561
|
-
ver: 2,
|
|
4562
|
-
name: eventName,
|
|
4563
|
-
properties: this.convertAttributesToProperties(logRecord.attributes || {})
|
|
4564
|
-
}
|
|
4565
|
-
},
|
|
4566
|
-
tags: {
|
|
4567
|
-
'ai.cloud.role': CLOUD_ROLE_NAME,
|
|
4568
|
-
'ai.cloud.roleInstance': SDK_VERSION
|
|
4569
|
-
}
|
|
4570
|
-
};
|
|
4571
|
-
this.sendToApplicationInsights(payload);
|
|
4572
|
-
}
|
|
4573
|
-
extractInstrumentationKey() {
|
|
4574
|
-
const match = this.connectionString.match(/InstrumentationKey=([^;]+)/);
|
|
4575
|
-
return match ? match[1] : '';
|
|
4576
|
-
}
|
|
4577
|
-
convertAttributesToProperties(attributes) {
|
|
4578
|
-
const properties = {};
|
|
4579
|
-
Object.entries(attributes || {}).forEach(([key, value]) => {
|
|
4580
|
-
properties[key] = String(value);
|
|
4581
|
-
});
|
|
4582
|
-
return properties;
|
|
4543
|
+
/**
|
|
4544
|
+
* Get the token manager instance
|
|
4545
|
+
*/
|
|
4546
|
+
getTokenManager() {
|
|
4547
|
+
return this.tokenManager;
|
|
4583
4548
|
}
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4549
|
+
/**
|
|
4550
|
+
* Authenticates the user based on the provided SDK configuration.
|
|
4551
|
+
* This method handles OAuth 2.0 authentication flow only.
|
|
4552
|
+
* For secret-based authentication, see authenticateWithSecret().
|
|
4553
|
+
* @param config The SDK configuration object.
|
|
4554
|
+
* @returns A promise that resolves to true if authentication is successful, otherwise false.
|
|
4555
|
+
* In an OAuth flow, this method will trigger a page redirect and the promise will not resolve.
|
|
4556
|
+
*/
|
|
4557
|
+
async authenticate(config) {
|
|
4558
|
+
if (!isBrowser) {
|
|
4559
|
+
return false;
|
|
4560
|
+
}
|
|
4561
|
+
// First priority: Complete OAuth callback if we detect it
|
|
4562
|
+
if (AuthService.isInOAuthCallback()) {
|
|
4563
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
4564
|
+
const code = urlParams.get('code');
|
|
4565
|
+
if (!code) {
|
|
4566
|
+
throw new Error('Authorization code missing in OAuth callback');
|
|
4590
4567
|
}
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
});
|
|
4599
|
-
if (!response.ok) {
|
|
4600
|
-
console.debug(`Failed to send event telemetry: ${response.status} ${response.statusText}`);
|
|
4568
|
+
// Check if token already exists (prevents duplicate processing)
|
|
4569
|
+
if (this.tokenManager.hasValidToken()) {
|
|
4570
|
+
return true;
|
|
4571
|
+
}
|
|
4572
|
+
// Ensure we have OAuth config for callback completion
|
|
4573
|
+
if (!hasOAuthConfig(config)) {
|
|
4574
|
+
throw new Error('OAuth configuration incomplete: clientId, redirectUri, and scope are required for OAuth callback');
|
|
4601
4575
|
}
|
|
4576
|
+
const result = await this._authenticateWithOAuth(config.clientId, config.redirectUri, config.scope);
|
|
4577
|
+
return result;
|
|
4602
4578
|
}
|
|
4603
|
-
|
|
4604
|
-
|
|
4579
|
+
// Secondly: Try to load existing valid token from storage
|
|
4580
|
+
const loadedFromStorage = this.tokenManager.loadFromStorage();
|
|
4581
|
+
// If we have a valid token from storage, return true
|
|
4582
|
+
if (loadedFromStorage && this.tokenManager.hasValidToken()) {
|
|
4583
|
+
return true;
|
|
4605
4584
|
}
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
return match ? match[1] : '';
|
|
4610
|
-
}
|
|
4611
|
-
}
|
|
4612
|
-
/**
|
|
4613
|
-
* Singleton telemetry client
|
|
4614
|
-
*/
|
|
4615
|
-
class TelemetryClient {
|
|
4616
|
-
constructor() {
|
|
4617
|
-
this.isInitialized = false;
|
|
4618
|
-
}
|
|
4619
|
-
static getInstance() {
|
|
4620
|
-
if (!TelemetryClient.instance) {
|
|
4621
|
-
TelemetryClient.instance = new TelemetryClient();
|
|
4585
|
+
// Start new OAuth flow if config has OAuth fields
|
|
4586
|
+
if (hasOAuthConfig(config)) {
|
|
4587
|
+
return await this._authenticateWithOAuth(config.clientId, config.redirectUri, config.scope);
|
|
4622
4588
|
}
|
|
4623
|
-
return
|
|
4589
|
+
return false;
|
|
4624
4590
|
}
|
|
4625
4591
|
/**
|
|
4626
|
-
*
|
|
4592
|
+
* Authenticate using OAuth flow
|
|
4627
4593
|
*/
|
|
4628
|
-
|
|
4629
|
-
if (
|
|
4630
|
-
|
|
4594
|
+
async _authenticateWithOAuth(clientId, redirectUri, scope) {
|
|
4595
|
+
if (!isBrowser) {
|
|
4596
|
+
throw new Error('OAuth flow is only supported in browser environments');
|
|
4631
4597
|
}
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4598
|
+
// Check if we have a stored code verifier indicating we're in an OAuth flow
|
|
4599
|
+
const codeVerifier = sessionStorage.getItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4600
|
+
const isInOAuthFlow = codeVerifier !== null;
|
|
4601
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
4602
|
+
const code = urlParams.get('code');
|
|
4603
|
+
// If we're in an OAuth flow (server-controlled code verifier exists), handle the callback
|
|
4604
|
+
if (isInOAuthFlow) {
|
|
4605
|
+
// We're expecting a callback - validate parameters
|
|
4606
|
+
if (!code) {
|
|
4607
|
+
// Clear stored state on error
|
|
4608
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4609
|
+
throw new Error('Authorization code missing in OAuth callback');
|
|
4610
|
+
}
|
|
4611
|
+
// Validate the authorization code format before using it
|
|
4612
|
+
// OAuth authorization codes should be alphanumeric with some special characters
|
|
4613
|
+
const codePattern = /^[A-Za-z0-9\-._~+/]+=*$/;
|
|
4614
|
+
if (!codePattern.test(code)) {
|
|
4615
|
+
// Clear stored state on error
|
|
4616
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4617
|
+
throw new Error('Invalid authorization code format');
|
|
4618
|
+
}
|
|
4619
|
+
// Authorization code is present and validated, so we can exchange it for a token.
|
|
4620
|
+
await this._handleOAuthCallback(code, clientId, redirectUri);
|
|
4621
|
+
return this.hasValidToken();
|
|
4622
|
+
}
|
|
4623
|
+
else {
|
|
4624
|
+
// Not in an OAuth flow - initiate one
|
|
4625
|
+
// Ignore any URL parameters that might be present
|
|
4626
|
+
await this._initiateOAuthFlow(clientId, redirectUri, scope);
|
|
4627
|
+
// This line is not expected to be reached due to redirect
|
|
4628
|
+
return false;
|
|
4635
4629
|
}
|
|
4630
|
+
}
|
|
4631
|
+
/**
|
|
4632
|
+
* Authenticate using API secret
|
|
4633
|
+
*/
|
|
4634
|
+
authenticateWithSecret(secret) {
|
|
4636
4635
|
try {
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
return;
|
|
4640
|
-
}
|
|
4641
|
-
this.setupTelemetryProvider(connectionString);
|
|
4636
|
+
this.updateToken({ token: secret, type: 'secret' });
|
|
4637
|
+
return true;
|
|
4642
4638
|
}
|
|
4643
4639
|
catch (error) {
|
|
4644
|
-
|
|
4645
|
-
|
|
4640
|
+
console.error('Failed to authenticate with secret', error);
|
|
4641
|
+
return false;
|
|
4646
4642
|
}
|
|
4647
4643
|
}
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4644
|
+
/**
|
|
4645
|
+
* Updates the access token used for API requests
|
|
4646
|
+
* @param tokenInfo The token information containing the access token, type, expiration, and refresh token
|
|
4647
|
+
*/
|
|
4648
|
+
updateToken(tokenInfo) {
|
|
4649
|
+
this.tokenManager.setToken(tokenInfo);
|
|
4651
4650
|
}
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
});
|
|
4658
|
-
this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
|
|
4651
|
+
/**
|
|
4652
|
+
* Checks if the current token is valid
|
|
4653
|
+
*/
|
|
4654
|
+
hasValidToken() {
|
|
4655
|
+
return this.tokenManager.hasValidToken();
|
|
4659
4656
|
}
|
|
4660
4657
|
/**
|
|
4661
|
-
*
|
|
4658
|
+
* Clears all authentication state including tokens and stored OAuth context.
|
|
4662
4659
|
*/
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4660
|
+
logout() {
|
|
4661
|
+
this.tokenManager.clearToken();
|
|
4662
|
+
// Clear OAuth context from session storage. These are normally cleaned up in _handleOAuthCallback after a successful
|
|
4663
|
+
// token exchange, but if a user calls logout() while an OAuth flow is
|
|
4664
|
+
// mid-redirect (before callback completes), they'd be left behind.
|
|
4665
|
+
if (isBrowser) {
|
|
4666
|
+
try {
|
|
4667
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.OAUTH_CONTEXT);
|
|
4668
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4669
|
+
}
|
|
4670
|
+
catch (error) {
|
|
4671
|
+
console.warn('Failed to clear OAuth context from session storage', error);
|
|
4668
4672
|
}
|
|
4669
|
-
const finalDisplayName = name || eventName;
|
|
4670
|
-
const attributes = this.getEnrichedAttributes(extraAttributes, eventName);
|
|
4671
|
-
// Emit as log
|
|
4672
|
-
this.logger.emit({
|
|
4673
|
-
body: finalDisplayName,
|
|
4674
|
-
attributes: attributes,
|
|
4675
|
-
timestamp: Date.now(),
|
|
4676
|
-
});
|
|
4677
|
-
}
|
|
4678
|
-
catch (error) {
|
|
4679
|
-
// Silent failure
|
|
4680
|
-
console.debug('Failed to track telemetry event:', error);
|
|
4681
4673
|
}
|
|
4682
4674
|
}
|
|
4683
4675
|
/**
|
|
4684
|
-
* Get
|
|
4676
|
+
* Get the current token
|
|
4685
4677
|
*/
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
[SERVICE]: eventName,
|
|
4692
|
-
[CLOUD_URL]: this.createCloudUrl(),
|
|
4693
|
-
[CLOUD_ORGANIZATION_NAME]: this.telemetryContext?.orgName || UNKNOWN,
|
|
4694
|
-
[CLOUD_TENANT_NAME]: this.telemetryContext?.tenantName || UNKNOWN,
|
|
4695
|
-
[CLOUD_REDIRECT_URI]: this.telemetryContext?.redirectUri || UNKNOWN,
|
|
4696
|
-
[CLOUD_CLIENT_ID]: this.telemetryContext?.clientId || UNKNOWN,
|
|
4697
|
-
};
|
|
4698
|
-
return attributes;
|
|
4678
|
+
getToken() {
|
|
4679
|
+
if (!this.tokenManager.hasValidToken()) {
|
|
4680
|
+
return undefined;
|
|
4681
|
+
}
|
|
4682
|
+
return this.tokenManager.getToken();
|
|
4699
4683
|
}
|
|
4700
4684
|
/**
|
|
4701
|
-
*
|
|
4685
|
+
* Generates a random code verifier for PKCE
|
|
4702
4686
|
*/
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
function trackEvent(eventName, name, attributes) {
|
|
4723
|
-
telemetryClient.track(eventName, name, attributes);
|
|
4724
|
-
}
|
|
4725
|
-
|
|
4726
|
-
/**
|
|
4727
|
-
* SDK Internals Registry - Internal registry for SDK instances
|
|
4728
|
-
*
|
|
4729
|
-
* This class is NOT exported in the public API.
|
|
4730
|
-
* It provides a secure way to share SDK internals between
|
|
4731
|
-
* the UiPath class and service classes without exposing them publicly.
|
|
4732
|
-
*
|
|
4733
|
-
* @internal
|
|
4734
|
-
*/
|
|
4735
|
-
// Global symbol key to ensure WeakMap is shared across module instances
|
|
4736
|
-
// This prevents issues when core and service modules are bundled separately
|
|
4737
|
-
const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
|
|
4738
|
-
// Get or create the global WeakMap store
|
|
4739
|
-
const getGlobalStore = () => {
|
|
4740
|
-
const globalObj = globalThis;
|
|
4741
|
-
if (!globalObj[REGISTRY_KEY]) {
|
|
4742
|
-
globalObj[REGISTRY_KEY] = new WeakMap();
|
|
4743
|
-
}
|
|
4744
|
-
return globalObj[REGISTRY_KEY];
|
|
4745
|
-
};
|
|
4746
|
-
/**
|
|
4747
|
-
* Internal registry for SDK private components.
|
|
4748
|
-
* Uses WeakMap to prevent memory leaks - entries are automatically
|
|
4749
|
-
* garbage collected when the SDK instance is no longer referenced.
|
|
4750
|
-
*
|
|
4751
|
-
* Uses a global singleton pattern to ensure the same WeakMap is shared
|
|
4752
|
-
* across separately bundled modules (core, entities, tasks, etc.).
|
|
4753
|
-
*
|
|
4754
|
-
* @internal - Not exported in public API
|
|
4755
|
-
*/
|
|
4756
|
-
class SDKInternalsRegistry {
|
|
4757
|
-
// Use global store to ensure sharing across module bundles
|
|
4758
|
-
static get store() {
|
|
4759
|
-
return getGlobalStore();
|
|
4760
|
-
}
|
|
4761
|
-
/**
|
|
4762
|
-
* Register SDK instance internals
|
|
4763
|
-
* Called by UiPath constructor
|
|
4764
|
-
*/
|
|
4765
|
-
static set(instance, internals) {
|
|
4766
|
-
this.store.set(instance, internals);
|
|
4767
|
-
}
|
|
4768
|
-
/**
|
|
4769
|
-
* Retrieve SDK instance internals
|
|
4770
|
-
* Called by BaseService constructor
|
|
4771
|
-
*/
|
|
4772
|
-
static get(instance) {
|
|
4773
|
-
const internals = this.store.get(instance);
|
|
4774
|
-
if (!internals) {
|
|
4775
|
-
throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
|
|
4776
|
-
}
|
|
4777
|
-
return internals;
|
|
4778
|
-
}
|
|
4779
|
-
}
|
|
4780
|
-
|
|
4781
|
-
var _UiPath_config, _UiPath_authService, _UiPath_initialized;
|
|
4782
|
-
/**
|
|
4783
|
-
* UiPath - Core SDK class for authentication and configuration management.
|
|
4784
|
-
*
|
|
4785
|
-
* Handles authentication, configuration, and provides access to SDK internals
|
|
4786
|
-
* for service instantiation in the modular pattern.
|
|
4787
|
-
*
|
|
4788
|
-
* @example
|
|
4789
|
-
* ```typescript
|
|
4790
|
-
* // Modular pattern
|
|
4791
|
-
* import { UiPath } from '@uipath/uipath-typescript/core';
|
|
4792
|
-
* import { Entities } from '@uipath/uipath-typescript/entities';
|
|
4793
|
-
*
|
|
4794
|
-
* const sdk = new UiPath({
|
|
4795
|
-
* baseUrl: 'https://cloud.uipath.com',
|
|
4796
|
-
* orgName: 'myorg',
|
|
4797
|
-
* tenantName: 'mytenant',
|
|
4798
|
-
* clientId: 'xxx',
|
|
4799
|
-
* redirectUri: 'http://localhost:3000/callback',
|
|
4800
|
-
* scope: 'OR.Users OR.Robots'
|
|
4801
|
-
* });
|
|
4802
|
-
*
|
|
4803
|
-
* await sdk.initialize();
|
|
4804
|
-
*
|
|
4805
|
-
* const entitiesService = new Entities(sdk);
|
|
4806
|
-
* const allEntities = await entitiesService.getAll();
|
|
4807
|
-
* ```
|
|
4808
|
-
*/
|
|
4809
|
-
class UiPath {
|
|
4810
|
-
constructor(config) {
|
|
4811
|
-
// Private fields - true runtime privacy, not visible via Object.keys()
|
|
4812
|
-
_UiPath_config.set(this, void 0);
|
|
4813
|
-
_UiPath_authService.set(this, void 0);
|
|
4814
|
-
_UiPath_initialized.set(this, false);
|
|
4815
|
-
// Validate and normalize the configuration
|
|
4816
|
-
validateConfig(config);
|
|
4817
|
-
const hasSecretAuth = hasSecretConfig(config);
|
|
4818
|
-
const hasOAuthAuth = hasOAuthConfig(config);
|
|
4819
|
-
// Initialize core components
|
|
4820
|
-
const internalConfig = new UiPathConfig({
|
|
4821
|
-
baseUrl: normalizeBaseUrl(config.baseUrl),
|
|
4822
|
-
orgName: config.orgName,
|
|
4823
|
-
tenantName: config.tenantName,
|
|
4824
|
-
secret: hasSecretAuth ? config.secret : undefined,
|
|
4825
|
-
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
4826
|
-
redirectUri: hasOAuthAuth ? config.redirectUri : undefined,
|
|
4827
|
-
scope: hasOAuthAuth ? config.scope : undefined
|
|
4828
|
-
});
|
|
4829
|
-
const executionContext = new ExecutionContext();
|
|
4830
|
-
__classPrivateFieldSet(this, _UiPath_authService, new AuthService(internalConfig, executionContext), "f");
|
|
4831
|
-
__classPrivateFieldSet(this, _UiPath_config, internalConfig, "f");
|
|
4832
|
-
// Store internals in SDKInternalsRegistry (not visible on instance)
|
|
4833
|
-
SDKInternalsRegistry.set(this, {
|
|
4834
|
-
config: internalConfig,
|
|
4835
|
-
context: executionContext,
|
|
4836
|
-
tokenManager: __classPrivateFieldGet(this, _UiPath_authService, "f").getTokenManager()
|
|
4837
|
-
});
|
|
4838
|
-
// Expose read-only config for user convenience
|
|
4839
|
-
this.config = {
|
|
4840
|
-
baseUrl: internalConfig.baseUrl,
|
|
4841
|
-
orgName: internalConfig.orgName,
|
|
4842
|
-
tenantName: internalConfig.tenantName
|
|
4843
|
-
};
|
|
4844
|
-
// Initialize telemetry with SDK configuration
|
|
4845
|
-
telemetryClient.initialize({
|
|
4846
|
-
baseUrl: config.baseUrl,
|
|
4847
|
-
orgName: config.orgName,
|
|
4848
|
-
tenantName: config.tenantName,
|
|
4849
|
-
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
4850
|
-
redirectUri: hasOAuthAuth ? config.redirectUri : undefined
|
|
4851
|
-
});
|
|
4852
|
-
// Track SDK initialization
|
|
4853
|
-
trackEvent('Sdk.Auth');
|
|
4854
|
-
// Auto-initialize for secret-based auth
|
|
4855
|
-
if (hasSecretAuth) {
|
|
4856
|
-
__classPrivateFieldGet(this, _UiPath_authService, "f").authenticateWithSecret(config.secret);
|
|
4857
|
-
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
4687
|
+
generateCodeVerifier() {
|
|
4688
|
+
if (isBrowser) {
|
|
4689
|
+
const array = new Uint8Array(32);
|
|
4690
|
+
crypto.getRandomValues(array);
|
|
4691
|
+
return this._base64URLEncode(array);
|
|
4692
|
+
}
|
|
4693
|
+
else {
|
|
4694
|
+
// In Node.js environment
|
|
4695
|
+
try {
|
|
4696
|
+
const crypto = require('crypto');
|
|
4697
|
+
return crypto.randomBytes(32)
|
|
4698
|
+
.toString('base64')
|
|
4699
|
+
.replace(/\+/g, '-')
|
|
4700
|
+
.replace(/\//g, '_')
|
|
4701
|
+
.replace(/=/g, '');
|
|
4702
|
+
}
|
|
4703
|
+
catch {
|
|
4704
|
+
throw new Error("crypto not available in browser");
|
|
4705
|
+
}
|
|
4858
4706
|
}
|
|
4859
4707
|
}
|
|
4860
4708
|
/**
|
|
4861
|
-
*
|
|
4862
|
-
* This method handles both OAuth flow initiation and completion automatically.
|
|
4863
|
-
* For secret-based authentication, initialization is automatic and this returns immediately.
|
|
4709
|
+
* Generates a code challenge from the code verifier
|
|
4864
4710
|
*/
|
|
4865
|
-
async
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4711
|
+
async generateCodeChallenge(codeVerifier) {
|
|
4712
|
+
if (isBrowser) {
|
|
4713
|
+
const encoder = new TextEncoder();
|
|
4714
|
+
const data = encoder.encode(codeVerifier);
|
|
4715
|
+
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
4716
|
+
return this._base64URLEncode(new Uint8Array(hash));
|
|
4869
4717
|
}
|
|
4870
|
-
|
|
4871
|
-
//
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
return;
|
|
4718
|
+
else {
|
|
4719
|
+
// In Node.js environment
|
|
4720
|
+
try {
|
|
4721
|
+
const crypto = require('crypto');
|
|
4722
|
+
return crypto.createHash('sha256')
|
|
4723
|
+
.update(codeVerifier)
|
|
4724
|
+
.digest('base64')
|
|
4725
|
+
.replace(/\+/g, '-')
|
|
4726
|
+
.replace(/\//g, '_')
|
|
4727
|
+
.replace(/=/g, '');
|
|
4881
4728
|
}
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
if (this.isAuthenticated()) {
|
|
4885
|
-
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
4729
|
+
catch {
|
|
4730
|
+
throw new Error("crypto not available in browser");
|
|
4886
4731
|
}
|
|
4887
4732
|
}
|
|
4888
|
-
catch (error) {
|
|
4889
|
-
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
4890
|
-
throw new Error(`Failed to initialize UiPath SDK: ${errorMessage}`);
|
|
4891
|
-
}
|
|
4892
4733
|
}
|
|
4893
4734
|
/**
|
|
4894
|
-
*
|
|
4895
|
-
*/
|
|
4896
|
-
isInitialized() {
|
|
4897
|
-
return __classPrivateFieldGet(this, _UiPath_initialized, "f");
|
|
4898
|
-
}
|
|
4899
|
-
/**
|
|
4900
|
-
* Check if we're in an OAuth callback state
|
|
4735
|
+
* Gets the authorization URL for the OAuth flow
|
|
4901
4736
|
*/
|
|
4902
|
-
|
|
4903
|
-
|
|
4737
|
+
getAuthorizationUrl(params) {
|
|
4738
|
+
const orgName = this.config.orgName;
|
|
4739
|
+
const queryParams = new URLSearchParams({
|
|
4740
|
+
response_type: 'code',
|
|
4741
|
+
client_id: params.clientId,
|
|
4742
|
+
redirect_uri: params.redirectUri,
|
|
4743
|
+
code_challenge: params.codeChallenge,
|
|
4744
|
+
code_challenge_method: 'S256',
|
|
4745
|
+
scope: params.scope + ' offline_access',
|
|
4746
|
+
state: params.state || this.generateCodeVerifier().slice(0, 16)
|
|
4747
|
+
});
|
|
4748
|
+
return `${this.config.baseUrl}/${orgName}/${IDENTITY_ENDPOINTS.AUTHORIZE}?${queryParams.toString()}`;
|
|
4904
4749
|
}
|
|
4905
4750
|
/**
|
|
4906
|
-
*
|
|
4751
|
+
* Exchanges the authorization code for an access token and automatically updates the current token
|
|
4907
4752
|
*/
|
|
4908
|
-
async
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4753
|
+
async _getAccessToken(params) {
|
|
4754
|
+
const orgName = this.config.orgName;
|
|
4755
|
+
const body = new URLSearchParams({
|
|
4756
|
+
grant_type: 'authorization_code',
|
|
4757
|
+
client_id: params.clientId,
|
|
4758
|
+
code: params.code,
|
|
4759
|
+
redirect_uri: params.redirectUri,
|
|
4760
|
+
code_verifier: params.codeVerifier
|
|
4761
|
+
});
|
|
4762
|
+
const response = await fetch(`${this.config.baseUrl}/${orgName}/${IDENTITY_ENDPOINTS.TOKEN}`, {
|
|
4763
|
+
method: 'POST',
|
|
4764
|
+
headers: {
|
|
4765
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
4766
|
+
},
|
|
4767
|
+
body: body.toString()
|
|
4768
|
+
});
|
|
4769
|
+
if (!response.ok) {
|
|
4770
|
+
const errorData = await response.json().catch(() => ({ message: response.statusText }));
|
|
4771
|
+
console.error("OAuth error:", errorData);
|
|
4772
|
+
throw new Error(`Failed to get access token: ${JSON.stringify(errorData)}`);
|
|
4923
4773
|
}
|
|
4774
|
+
const token = await response.json();
|
|
4775
|
+
this.updateToken({
|
|
4776
|
+
token: token.access_token,
|
|
4777
|
+
type: 'oauth',
|
|
4778
|
+
expiresAt: token.expires_in ? new Date(Date.now() + token.expires_in * 1000) : undefined,
|
|
4779
|
+
refreshToken: token.refresh_token
|
|
4780
|
+
});
|
|
4781
|
+
return token;
|
|
4924
4782
|
}
|
|
4925
4783
|
/**
|
|
4926
|
-
*
|
|
4927
|
-
*/
|
|
4928
|
-
isAuthenticated() {
|
|
4929
|
-
return __classPrivateFieldGet(this, _UiPath_authService, "f").hasValidToken();
|
|
4930
|
-
}
|
|
4931
|
-
/**
|
|
4932
|
-
* Get the current authentication token
|
|
4933
|
-
*/
|
|
4934
|
-
getToken() {
|
|
4935
|
-
return __classPrivateFieldGet(this, _UiPath_authService, "f").getToken();
|
|
4936
|
-
}
|
|
4937
|
-
}
|
|
4938
|
-
_UiPath_config = new WeakMap(), _UiPath_authService = new WeakMap(), _UiPath_initialized = new WeakMap();
|
|
4939
|
-
|
|
4940
|
-
/**
|
|
4941
|
-
* Base error class for all UiPath SDK errors
|
|
4942
|
-
* Pure TypeScript class with clean interface
|
|
4943
|
-
*/
|
|
4944
|
-
class UiPathError {
|
|
4945
|
-
constructor(type, params) {
|
|
4946
|
-
this.type = type;
|
|
4947
|
-
this.message = params.message;
|
|
4948
|
-
this.statusCode = params.statusCode;
|
|
4949
|
-
this.requestId = params.requestId;
|
|
4950
|
-
this.timestamp = new Date();
|
|
4951
|
-
// Capture stack trace for debugging
|
|
4952
|
-
this.stack = (new Error()).stack;
|
|
4953
|
-
}
|
|
4954
|
-
/**
|
|
4955
|
-
* Returns a clean JSON representation of the error
|
|
4784
|
+
* Base64URL encodes an array buffer
|
|
4956
4785
|
*/
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
timestamp: this.timestamp
|
|
4964
|
-
};
|
|
4786
|
+
_base64URLEncode(buffer) {
|
|
4787
|
+
const base64 = btoa(String.fromCharCode(...buffer));
|
|
4788
|
+
return base64
|
|
4789
|
+
.replace(/\+/g, '-')
|
|
4790
|
+
.replace(/\//g, '_')
|
|
4791
|
+
.replace(/=/g, '');
|
|
4965
4792
|
}
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4793
|
+
async _initiateOAuthFlow(clientId, redirectUri, scope) {
|
|
4794
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
4795
|
+
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
4796
|
+
// Store complete OAuth context for callback completion
|
|
4797
|
+
const oauthContext = {
|
|
4798
|
+
codeVerifier,
|
|
4799
|
+
clientId,
|
|
4800
|
+
redirectUri,
|
|
4801
|
+
baseUrl: this.config.baseUrl,
|
|
4802
|
+
orgName: this.config.orgName,
|
|
4803
|
+
tenantName: this.config.tenantName,
|
|
4804
|
+
scope
|
|
4973
4805
|
};
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
// Client errors (4xx)
|
|
4982
|
-
BAD_REQUEST: 400,
|
|
4983
|
-
UNAUTHORIZED: 401,
|
|
4984
|
-
FORBIDDEN: 403,
|
|
4985
|
-
NOT_FOUND: 404,
|
|
4986
|
-
TOO_MANY_REQUESTS: 429,
|
|
4987
|
-
// Server errors (5xx)
|
|
4988
|
-
INTERNAL_SERVER_ERROR: 500,
|
|
4989
|
-
NOT_IMPLEMENTED: 501,
|
|
4990
|
-
BAD_GATEWAY: 502,
|
|
4991
|
-
SERVICE_UNAVAILABLE: 503,
|
|
4992
|
-
GATEWAY_TIMEOUT: 504
|
|
4993
|
-
};
|
|
4994
|
-
/**
|
|
4995
|
-
* Error type constants for consistent error identification
|
|
4996
|
-
*/
|
|
4997
|
-
const ErrorType = {
|
|
4998
|
-
AUTHENTICATION: 'AuthenticationError',
|
|
4999
|
-
AUTHORIZATION: 'AuthorizationError',
|
|
5000
|
-
VALIDATION: 'ValidationError',
|
|
5001
|
-
NOT_FOUND: 'NotFoundError',
|
|
5002
|
-
RATE_LIMIT: 'RateLimitError',
|
|
5003
|
-
SERVER: 'ServerError',
|
|
5004
|
-
NETWORK: 'NetworkError'
|
|
5005
|
-
};
|
|
5006
|
-
/**
|
|
5007
|
-
* Standard error message constants
|
|
5008
|
-
*/
|
|
5009
|
-
const ErrorMessages = {
|
|
5010
|
-
// Authentication errors
|
|
5011
|
-
AUTHENTICATION_FAILED: 'Authentication failed',
|
|
5012
|
-
// Authorization errors
|
|
5013
|
-
ACCESS_DENIED: 'Access denied',
|
|
5014
|
-
// Validation errors
|
|
5015
|
-
VALIDATION_FAILED: 'Validation failed',
|
|
5016
|
-
// Not found errors
|
|
5017
|
-
RESOURCE_NOT_FOUND: 'Resource not found',
|
|
5018
|
-
// Rate limit errors
|
|
5019
|
-
RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
|
|
5020
|
-
// Server errors
|
|
5021
|
-
INTERNAL_SERVER_ERROR: 'Internal Server error occurred',
|
|
5022
|
-
// Network errors
|
|
5023
|
-
NETWORK_ERROR: 'Network error occurred'};
|
|
5024
|
-
|
|
5025
|
-
/**
|
|
5026
|
-
* Error thrown when authentication fails (401 errors)
|
|
5027
|
-
* Common scenarios:
|
|
5028
|
-
* - Invalid credentials
|
|
5029
|
-
* - Expired token
|
|
5030
|
-
* - Missing authentication
|
|
5031
|
-
*/
|
|
5032
|
-
class AuthenticationError extends UiPathError {
|
|
5033
|
-
constructor(params = {}) {
|
|
5034
|
-
super(ErrorType.AUTHENTICATION, {
|
|
5035
|
-
message: params.message || ErrorMessages.AUTHENTICATION_FAILED,
|
|
5036
|
-
statusCode: params.statusCode ?? HttpStatus.UNAUTHORIZED,
|
|
5037
|
-
requestId: params.requestId
|
|
4806
|
+
sessionStorage.setItem(AUTH_STORAGE_KEYS.OAUTH_CONTEXT, JSON.stringify(oauthContext));
|
|
4807
|
+
sessionStorage.setItem(AUTH_STORAGE_KEYS.CODE_VERIFIER, codeVerifier);
|
|
4808
|
+
const authUrl = this.getAuthorizationUrl({
|
|
4809
|
+
clientId,
|
|
4810
|
+
redirectUri,
|
|
4811
|
+
codeChallenge,
|
|
4812
|
+
scope
|
|
5038
4813
|
});
|
|
4814
|
+
window.location.href = authUrl;
|
|
5039
4815
|
}
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
constructor(params = {}) {
|
|
5051
|
-
super(ErrorType.AUTHORIZATION, {
|
|
5052
|
-
message: params.message || ErrorMessages.ACCESS_DENIED,
|
|
5053
|
-
statusCode: params.statusCode ?? HttpStatus.FORBIDDEN,
|
|
5054
|
-
requestId: params.requestId
|
|
4816
|
+
async _handleOAuthCallback(code, clientId, redirectUri) {
|
|
4817
|
+
const codeVerifier = sessionStorage.getItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4818
|
+
if (!codeVerifier) {
|
|
4819
|
+
throw new Error('Code verifier not found in session storage. Authentication may have been interrupted.');
|
|
4820
|
+
}
|
|
4821
|
+
await this._getAccessToken({
|
|
4822
|
+
clientId,
|
|
4823
|
+
redirectUri,
|
|
4824
|
+
code,
|
|
4825
|
+
codeVerifier
|
|
5055
4826
|
});
|
|
4827
|
+
// Clear OAuth context and code verifier after successful token exchange
|
|
4828
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.OAUTH_CONTEXT);
|
|
4829
|
+
sessionStorage.removeItem(AUTH_STORAGE_KEYS.CODE_VERIFIER);
|
|
4830
|
+
const url = new URL(window.location.href);
|
|
4831
|
+
url.searchParams.delete('code');
|
|
4832
|
+
url.searchParams.delete('state');
|
|
4833
|
+
window.history.replaceState({}, '', url.toString());
|
|
5056
4834
|
}
|
|
5057
4835
|
}
|
|
5058
4836
|
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
* - Invalid input parameters
|
|
5063
|
-
* - Missing required fields
|
|
5064
|
-
* - Invalid data format
|
|
5065
|
-
*/
|
|
5066
|
-
class ValidationError extends UiPathError {
|
|
5067
|
-
constructor(params = {}) {
|
|
5068
|
-
super(ErrorType.VALIDATION, {
|
|
5069
|
-
message: params.message || ErrorMessages.VALIDATION_FAILED,
|
|
5070
|
-
statusCode: params.statusCode ?? HttpStatus.BAD_REQUEST,
|
|
5071
|
-
requestId: params.requestId
|
|
5072
|
-
});
|
|
4837
|
+
function validateConfig(config) {
|
|
4838
|
+
if (!config.baseUrl || !config.orgName || !config.tenantName) {
|
|
4839
|
+
throw new Error('Missing required configuration: baseUrl, orgName, and tenantName are required');
|
|
5073
4840
|
}
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
/**
|
|
5077
|
-
* Error thrown when a resource is not found (404 errors)
|
|
5078
|
-
* Common scenarios:
|
|
5079
|
-
* - Resource doesn't exist
|
|
5080
|
-
* - Invalid ID provided
|
|
5081
|
-
* - Resource deleted
|
|
5082
|
-
*/
|
|
5083
|
-
class NotFoundError extends UiPathError {
|
|
5084
|
-
constructor(params = {}) {
|
|
5085
|
-
super(ErrorType.NOT_FOUND, {
|
|
5086
|
-
message: params.message || ErrorMessages.RESOURCE_NOT_FOUND,
|
|
5087
|
-
statusCode: params.statusCode ?? HttpStatus.NOT_FOUND,
|
|
5088
|
-
requestId: params.requestId
|
|
5089
|
-
});
|
|
4841
|
+
if (!hasSecretConfig(config) && !hasOAuthConfig(config)) {
|
|
4842
|
+
throw new Error('Invalid configuration: must provide either secret or (clientId, redirectUri, and scope)');
|
|
5090
4843
|
}
|
|
5091
4844
|
}
|
|
4845
|
+
function normalizeBaseUrl(url) {
|
|
4846
|
+
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
4847
|
+
}
|
|
5092
4848
|
|
|
5093
4849
|
/**
|
|
5094
|
-
*
|
|
5095
|
-
* Common scenarios:
|
|
5096
|
-
* - Too many requests in a time window
|
|
5097
|
-
* - API throttling
|
|
4850
|
+
* SDK Telemetry constants
|
|
5098
4851
|
*/
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
4852
|
+
// Connection string placeholder that will be replaced during build
|
|
4853
|
+
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";
|
|
4854
|
+
// SDK Version placeholder
|
|
4855
|
+
const SDK_VERSION = "1.1.1";
|
|
4856
|
+
const VERSION = "Version";
|
|
4857
|
+
const SERVICE = "Service";
|
|
4858
|
+
const CLOUD_ORGANIZATION_NAME = "CloudOrganizationName";
|
|
4859
|
+
const CLOUD_TENANT_NAME = "CloudTenantName";
|
|
4860
|
+
const CLOUD_URL = "CloudUrl";
|
|
4861
|
+
const CLOUD_CLIENT_ID = "CloudClientId";
|
|
4862
|
+
const CLOUD_REDIRECT_URI = "CloudRedirectUri";
|
|
4863
|
+
const APP_NAME = "ApplicationName";
|
|
4864
|
+
const CLOUD_ROLE_NAME = "uipath-ts-sdk";
|
|
4865
|
+
// Service and logger names
|
|
4866
|
+
const SDK_SERVICE_NAME = "UiPath.TypeScript.Sdk";
|
|
4867
|
+
const SDK_LOGGER_NAME = "uipath-ts-sdk-telemetry";
|
|
4868
|
+
// Event names
|
|
4869
|
+
const SDK_RUN_EVENT = "Sdk.Run";
|
|
4870
|
+
// Default value for unknown/empty attributes
|
|
4871
|
+
const UNKNOWN = "";
|
|
5108
4872
|
|
|
5109
4873
|
/**
|
|
5110
|
-
*
|
|
5111
|
-
* Common scenarios:
|
|
5112
|
-
* - Internal server error
|
|
5113
|
-
* - Service unavailable
|
|
5114
|
-
* - Gateway timeout
|
|
4874
|
+
* Log exporter that sends ALL logs as Application Insights custom events
|
|
5115
4875
|
*/
|
|
5116
|
-
class
|
|
5117
|
-
constructor(
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
4876
|
+
class ApplicationInsightsEventExporter {
|
|
4877
|
+
constructor(connectionString) {
|
|
4878
|
+
this.connectionString = connectionString;
|
|
4879
|
+
}
|
|
4880
|
+
export(logs, resultCallback) {
|
|
4881
|
+
try {
|
|
4882
|
+
logs.forEach(logRecord => {
|
|
4883
|
+
this.sendAsCustomEvent(logRecord);
|
|
4884
|
+
});
|
|
4885
|
+
resultCallback({ code: 0 });
|
|
4886
|
+
}
|
|
4887
|
+
catch (error) {
|
|
4888
|
+
console.debug('Failed to export logs to Application Insights:', error);
|
|
4889
|
+
resultCallback({ code: 2, error });
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
shutdown() {
|
|
4893
|
+
return Promise.resolve();
|
|
4894
|
+
}
|
|
4895
|
+
sendAsCustomEvent(logRecord) {
|
|
4896
|
+
// Get event name from body or attributes
|
|
4897
|
+
const eventName = logRecord.body || SDK_RUN_EVENT;
|
|
4898
|
+
const payload = {
|
|
4899
|
+
name: 'Microsoft.ApplicationInsights.Event',
|
|
4900
|
+
time: new Date().toISOString(),
|
|
4901
|
+
iKey: this.extractInstrumentationKey(),
|
|
4902
|
+
data: {
|
|
4903
|
+
baseType: 'EventData',
|
|
4904
|
+
baseData: {
|
|
4905
|
+
ver: 2,
|
|
4906
|
+
name: eventName,
|
|
4907
|
+
properties: this.convertAttributesToProperties(logRecord.attributes || {})
|
|
4908
|
+
}
|
|
4909
|
+
},
|
|
4910
|
+
tags: {
|
|
4911
|
+
'ai.cloud.role': CLOUD_ROLE_NAME,
|
|
4912
|
+
'ai.cloud.roleInstance': SDK_VERSION
|
|
4913
|
+
}
|
|
4914
|
+
};
|
|
4915
|
+
this.sendToApplicationInsights(payload);
|
|
4916
|
+
}
|
|
4917
|
+
extractInstrumentationKey() {
|
|
4918
|
+
const match = this.connectionString.match(/InstrumentationKey=([^;]+)/);
|
|
4919
|
+
return match ? match[1] : '';
|
|
4920
|
+
}
|
|
4921
|
+
convertAttributesToProperties(attributes) {
|
|
4922
|
+
const properties = {};
|
|
4923
|
+
Object.entries(attributes || {}).forEach(([key, value]) => {
|
|
4924
|
+
properties[key] = String(value);
|
|
5122
4925
|
});
|
|
4926
|
+
return properties;
|
|
5123
4927
|
}
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
4928
|
+
async sendToApplicationInsights(payload) {
|
|
4929
|
+
try {
|
|
4930
|
+
const ingestionEndpoint = this.extractIngestionEndpoint();
|
|
4931
|
+
if (!ingestionEndpoint) {
|
|
4932
|
+
console.debug('No ingestion endpoint found in connection string');
|
|
4933
|
+
return;
|
|
4934
|
+
}
|
|
4935
|
+
const url = `${ingestionEndpoint}/v2/track`;
|
|
4936
|
+
const response = await fetch(url, {
|
|
4937
|
+
method: 'POST',
|
|
4938
|
+
headers: {
|
|
4939
|
+
'Content-Type': 'application/json',
|
|
4940
|
+
},
|
|
4941
|
+
body: JSON.stringify(payload)
|
|
4942
|
+
});
|
|
4943
|
+
if (!response.ok) {
|
|
4944
|
+
console.debug(`Failed to send event telemetry: ${response.status} ${response.statusText}`);
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
catch (error) {
|
|
4948
|
+
console.debug('Error sending event telemetry to Application Insights:', error);
|
|
4949
|
+
}
|
|
4950
|
+
}
|
|
4951
|
+
extractIngestionEndpoint() {
|
|
4952
|
+
const match = this.connectionString.match(/IngestionEndpoint=([^;]+)/);
|
|
4953
|
+
return match ? match[1] : '';
|
|
5131
4954
|
}
|
|
5132
4955
|
}
|
|
5133
|
-
|
|
5134
4956
|
/**
|
|
5135
|
-
*
|
|
5136
|
-
* Common scenarios:
|
|
5137
|
-
* - Connection timeout
|
|
5138
|
-
* - DNS resolution failure
|
|
5139
|
-
* - Network unreachable
|
|
5140
|
-
* - Request aborted
|
|
4957
|
+
* Singleton telemetry client
|
|
5141
4958
|
*/
|
|
5142
|
-
class
|
|
5143
|
-
constructor(
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
4959
|
+
class TelemetryClient {
|
|
4960
|
+
constructor() {
|
|
4961
|
+
this.isInitialized = false;
|
|
4962
|
+
}
|
|
4963
|
+
static getInstance() {
|
|
4964
|
+
if (!TelemetryClient.instance) {
|
|
4965
|
+
TelemetryClient.instance = new TelemetryClient();
|
|
4966
|
+
}
|
|
4967
|
+
return TelemetryClient.instance;
|
|
4968
|
+
}
|
|
4969
|
+
/**
|
|
4970
|
+
* Initialize telemetry
|
|
4971
|
+
*/
|
|
4972
|
+
initialize(config) {
|
|
4973
|
+
if (this.isInitialized) {
|
|
4974
|
+
return;
|
|
4975
|
+
}
|
|
4976
|
+
this.isInitialized = true;
|
|
4977
|
+
if (config) {
|
|
4978
|
+
this.telemetryContext = config;
|
|
4979
|
+
}
|
|
4980
|
+
try {
|
|
4981
|
+
const connectionString = this.getConnectionString();
|
|
4982
|
+
if (!connectionString) {
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
this.setupTelemetryProvider(connectionString);
|
|
4986
|
+
}
|
|
4987
|
+
catch (error) {
|
|
4988
|
+
// Silent failure - telemetry errors shouldn't break functionality
|
|
4989
|
+
console.debug('Failed to initialize OpenTelemetry:', error);
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
getConnectionString() {
|
|
4993
|
+
const connectionString = CONNECTION_STRING;
|
|
4994
|
+
return connectionString;
|
|
4995
|
+
}
|
|
4996
|
+
setupTelemetryProvider(connectionString) {
|
|
4997
|
+
const exporter = new ApplicationInsightsEventExporter(connectionString);
|
|
4998
|
+
const processor = new sdkLogs.BatchLogRecordProcessor(exporter);
|
|
4999
|
+
this.logProvider = new sdkLogs.LoggerProvider({
|
|
5000
|
+
processors: [processor]
|
|
5148
5001
|
});
|
|
5002
|
+
this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
|
|
5003
|
+
}
|
|
5004
|
+
/**
|
|
5005
|
+
* Track a telemetry event
|
|
5006
|
+
*/
|
|
5007
|
+
track(eventName, name, extraAttributes = {}) {
|
|
5008
|
+
try {
|
|
5009
|
+
// Skip if logger not initialized
|
|
5010
|
+
if (!this.logger) {
|
|
5011
|
+
return;
|
|
5012
|
+
}
|
|
5013
|
+
const finalDisplayName = name || eventName;
|
|
5014
|
+
const attributes = this.getEnrichedAttributes(extraAttributes, eventName);
|
|
5015
|
+
// Emit as log
|
|
5016
|
+
this.logger.emit({
|
|
5017
|
+
body: finalDisplayName,
|
|
5018
|
+
attributes: attributes,
|
|
5019
|
+
timestamp: Date.now(),
|
|
5020
|
+
});
|
|
5021
|
+
}
|
|
5022
|
+
catch (error) {
|
|
5023
|
+
// Silent failure
|
|
5024
|
+
console.debug('Failed to track telemetry event:', error);
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
/**
|
|
5028
|
+
* Get enriched attributes for telemetry events
|
|
5029
|
+
*/
|
|
5030
|
+
getEnrichedAttributes(extraAttributes, eventName) {
|
|
5031
|
+
const attributes = {
|
|
5032
|
+
[APP_NAME]: SDK_SERVICE_NAME,
|
|
5033
|
+
[VERSION]: SDK_VERSION,
|
|
5034
|
+
[SERVICE]: eventName,
|
|
5035
|
+
[CLOUD_URL]: this.createCloudUrl(),
|
|
5036
|
+
[CLOUD_ORGANIZATION_NAME]: this.telemetryContext?.orgName || UNKNOWN,
|
|
5037
|
+
[CLOUD_TENANT_NAME]: this.telemetryContext?.tenantName || UNKNOWN,
|
|
5038
|
+
[CLOUD_REDIRECT_URI]: this.telemetryContext?.redirectUri || UNKNOWN,
|
|
5039
|
+
[CLOUD_CLIENT_ID]: this.telemetryContext?.clientId || UNKNOWN,
|
|
5040
|
+
...extraAttributes,
|
|
5041
|
+
};
|
|
5042
|
+
return attributes;
|
|
5043
|
+
}
|
|
5044
|
+
/**
|
|
5045
|
+
* Create cloud URL from base URL, organization ID, and tenant ID
|
|
5046
|
+
*/
|
|
5047
|
+
createCloudUrl() {
|
|
5048
|
+
const baseUrl = this.telemetryContext?.baseUrl;
|
|
5049
|
+
const orgId = this.telemetryContext?.orgName;
|
|
5050
|
+
const tenantId = this.telemetryContext?.tenantName;
|
|
5051
|
+
if (!baseUrl || !orgId || !tenantId) {
|
|
5052
|
+
return UNKNOWN;
|
|
5053
|
+
}
|
|
5054
|
+
return `${baseUrl}/${orgId}/${tenantId}`;
|
|
5149
5055
|
}
|
|
5150
5056
|
}
|
|
5057
|
+
// Export singleton instance
|
|
5058
|
+
const telemetryClient = TelemetryClient.getInstance();
|
|
5151
5059
|
|
|
5152
5060
|
/**
|
|
5153
|
-
*
|
|
5154
|
-
*/
|
|
5155
|
-
function isUiPathError(error) {
|
|
5156
|
-
return error instanceof UiPathError;
|
|
5157
|
-
}
|
|
5158
|
-
/**
|
|
5159
|
-
* Type guard to check if an error is an AuthenticationError
|
|
5160
|
-
*/
|
|
5161
|
-
function isAuthenticationError(error) {
|
|
5162
|
-
return error instanceof AuthenticationError;
|
|
5163
|
-
}
|
|
5164
|
-
/**
|
|
5165
|
-
* Type guard to check if an error is an AuthorizationError
|
|
5061
|
+
* SDK Track decorator and function for telemetry
|
|
5166
5062
|
*/
|
|
5167
|
-
function isAuthorizationError(error) {
|
|
5168
|
-
return error instanceof AuthorizationError;
|
|
5169
|
-
}
|
|
5170
5063
|
/**
|
|
5171
|
-
*
|
|
5064
|
+
* Common tracking logic shared between method and function decorators
|
|
5172
5065
|
*/
|
|
5173
|
-
function
|
|
5174
|
-
return
|
|
5066
|
+
function createTrackedFunction(originalFunction, nameOrOptions, fallbackName, opts) {
|
|
5067
|
+
return function (...args) {
|
|
5068
|
+
// Determine if we should track this call
|
|
5069
|
+
let shouldTrack = true;
|
|
5070
|
+
if (opts.condition !== undefined) {
|
|
5071
|
+
if (typeof opts.condition === 'function') {
|
|
5072
|
+
shouldTrack = opts.condition.apply(this, args);
|
|
5073
|
+
}
|
|
5074
|
+
else {
|
|
5075
|
+
shouldTrack = opts.condition;
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
// Track the event if enabled
|
|
5079
|
+
if (shouldTrack) {
|
|
5080
|
+
// Use the full name provided in the decorator (e.g., "Queue.GetAll")
|
|
5081
|
+
const serviceMethod = typeof nameOrOptions === 'string'
|
|
5082
|
+
? nameOrOptions
|
|
5083
|
+
: fallbackName;
|
|
5084
|
+
// Use 'Sdk.Run' as the name and serviceMethod as the service
|
|
5085
|
+
telemetryClient.track(serviceMethod, SDK_RUN_EVENT, opts.attributes);
|
|
5086
|
+
}
|
|
5087
|
+
// Execute the original function
|
|
5088
|
+
return originalFunction.apply(this, args);
|
|
5089
|
+
};
|
|
5175
5090
|
}
|
|
5176
5091
|
/**
|
|
5177
|
-
*
|
|
5092
|
+
* Track decorator that can be used to automatically track function calls
|
|
5093
|
+
*
|
|
5094
|
+
* Usage:
|
|
5095
|
+
* @track("Service.Method")
|
|
5096
|
+
* function myFunction() { ... }
|
|
5097
|
+
*
|
|
5098
|
+
* @track("Queue.GetAll")
|
|
5099
|
+
* async getAll() { ... }
|
|
5100
|
+
*
|
|
5101
|
+
* @track("Tasks.Create")
|
|
5102
|
+
* async create() { ... }
|
|
5103
|
+
*
|
|
5104
|
+
* @track("Assets.Update", { condition: false })
|
|
5105
|
+
* function myFunction() { ... }
|
|
5106
|
+
*
|
|
5107
|
+
* @track("Processes.Start", { attributes: { customProp: "value" } })
|
|
5108
|
+
* function myFunction() { ... }
|
|
5178
5109
|
*/
|
|
5179
|
-
function
|
|
5180
|
-
return
|
|
5110
|
+
function track(nameOrOptions, options) {
|
|
5111
|
+
return function decorator(_target, propertyKey, descriptor) {
|
|
5112
|
+
const opts = typeof nameOrOptions === 'object' ? nameOrOptions : options || {};
|
|
5113
|
+
if (descriptor && typeof descriptor.value === 'function') {
|
|
5114
|
+
// Method decorator
|
|
5115
|
+
descriptor.value = createTrackedFunction(descriptor.value, nameOrOptions, propertyKey || 'unknown_method', opts);
|
|
5116
|
+
return descriptor;
|
|
5117
|
+
}
|
|
5118
|
+
// Function decorator
|
|
5119
|
+
return (originalFunction) => createTrackedFunction(originalFunction, nameOrOptions, originalFunction.name || 'unknown_function', opts);
|
|
5120
|
+
};
|
|
5181
5121
|
}
|
|
5182
5122
|
/**
|
|
5183
|
-
*
|
|
5123
|
+
* Direct tracking function
|
|
5184
5124
|
*/
|
|
5185
|
-
function
|
|
5186
|
-
|
|
5125
|
+
function trackEvent(eventName, name, attributes) {
|
|
5126
|
+
telemetryClient.track(eventName, name, attributes);
|
|
5187
5127
|
}
|
|
5128
|
+
|
|
5188
5129
|
/**
|
|
5189
|
-
*
|
|
5130
|
+
* SDK Internals Registry - Internal registry for SDK instances
|
|
5131
|
+
*
|
|
5132
|
+
* This class is NOT exported in the public API.
|
|
5133
|
+
* It provides a secure way to share SDK internals between
|
|
5134
|
+
* the UiPath class and service classes without exposing them publicly.
|
|
5135
|
+
*
|
|
5136
|
+
* @internal
|
|
5190
5137
|
*/
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5138
|
+
// Global symbol key to ensure WeakMap is shared across module instances
|
|
5139
|
+
// This prevents issues when core and service modules are bundled separately
|
|
5140
|
+
const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
|
|
5141
|
+
// Get or create the global WeakMap store
|
|
5142
|
+
const getGlobalStore = () => {
|
|
5143
|
+
const globalObj = globalThis;
|
|
5144
|
+
if (!globalObj[REGISTRY_KEY]) {
|
|
5145
|
+
globalObj[REGISTRY_KEY] = new WeakMap();
|
|
5146
|
+
}
|
|
5147
|
+
return globalObj[REGISTRY_KEY];
|
|
5148
|
+
};
|
|
5194
5149
|
/**
|
|
5195
|
-
*
|
|
5150
|
+
* Internal registry for SDK private components.
|
|
5151
|
+
* Uses WeakMap to prevent memory leaks - entries are automatically
|
|
5152
|
+
* garbage collected when the SDK instance is no longer referenced.
|
|
5153
|
+
*
|
|
5154
|
+
* Uses a global singleton pattern to ensure the same WeakMap is shared
|
|
5155
|
+
* across separately bundled modules (core, entities, tasks, etc.).
|
|
5156
|
+
*
|
|
5157
|
+
* @internal - Not exported in public API
|
|
5196
5158
|
*/
|
|
5197
|
-
|
|
5198
|
-
|
|
5159
|
+
class SDKInternalsRegistry {
|
|
5160
|
+
// Use global store to ensure sharing across module bundles
|
|
5161
|
+
static get store() {
|
|
5162
|
+
return getGlobalStore();
|
|
5163
|
+
}
|
|
5164
|
+
/**
|
|
5165
|
+
* Register SDK instance internals
|
|
5166
|
+
* Called by UiPath constructor
|
|
5167
|
+
*/
|
|
5168
|
+
static set(instance, internals) {
|
|
5169
|
+
this.store.set(instance, internals);
|
|
5170
|
+
}
|
|
5171
|
+
/**
|
|
5172
|
+
* Retrieve SDK instance internals
|
|
5173
|
+
* Called by BaseService constructor
|
|
5174
|
+
*/
|
|
5175
|
+
static get(instance) {
|
|
5176
|
+
const internals = this.store.get(instance);
|
|
5177
|
+
if (!internals) {
|
|
5178
|
+
throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
|
|
5179
|
+
}
|
|
5180
|
+
return internals;
|
|
5181
|
+
}
|
|
5199
5182
|
}
|
|
5183
|
+
|
|
5184
|
+
var _UiPath_config, _UiPath_authService, _UiPath_initialized;
|
|
5200
5185
|
/**
|
|
5201
|
-
*
|
|
5186
|
+
* UiPath - Core SDK class for authentication and configuration management.
|
|
5187
|
+
*
|
|
5188
|
+
* Handles authentication, configuration, and provides access to SDK internals
|
|
5189
|
+
* for service instantiation in the modular pattern.
|
|
5190
|
+
*
|
|
5191
|
+
* @example
|
|
5192
|
+
* ```typescript
|
|
5193
|
+
* // Modular pattern
|
|
5194
|
+
* import { UiPath } from '@uipath/uipath-typescript/core';
|
|
5195
|
+
* import { Entities } from '@uipath/uipath-typescript/entities';
|
|
5196
|
+
*
|
|
5197
|
+
* const sdk = new UiPath({
|
|
5198
|
+
* baseUrl: 'https://cloud.uipath.com',
|
|
5199
|
+
* orgName: 'myorg',
|
|
5200
|
+
* tenantName: 'mytenant',
|
|
5201
|
+
* clientId: 'xxx',
|
|
5202
|
+
* redirectUri: 'http://localhost:3000/callback',
|
|
5203
|
+
* scope: 'OR.Users OR.Robots'
|
|
5204
|
+
* });
|
|
5205
|
+
*
|
|
5206
|
+
* await sdk.initialize();
|
|
5207
|
+
*
|
|
5208
|
+
* const entitiesService = new Entities(sdk);
|
|
5209
|
+
* const allEntities = await entitiesService.getAll();
|
|
5210
|
+
* ```
|
|
5202
5211
|
*/
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5212
|
+
class UiPath {
|
|
5213
|
+
constructor(config) {
|
|
5214
|
+
// Private fields - true runtime privacy, not visible via Object.keys()
|
|
5215
|
+
_UiPath_config.set(this, void 0);
|
|
5216
|
+
_UiPath_authService.set(this, void 0);
|
|
5217
|
+
_UiPath_initialized.set(this, false);
|
|
5218
|
+
// Validate and normalize the configuration
|
|
5219
|
+
validateConfig(config);
|
|
5220
|
+
const hasSecretAuth = hasSecretConfig(config);
|
|
5221
|
+
const hasOAuthAuth = hasOAuthConfig(config);
|
|
5222
|
+
// Initialize core components
|
|
5223
|
+
const internalConfig = new UiPathConfig({
|
|
5224
|
+
baseUrl: normalizeBaseUrl(config.baseUrl),
|
|
5225
|
+
orgName: config.orgName,
|
|
5226
|
+
tenantName: config.tenantName,
|
|
5227
|
+
secret: hasSecretAuth ? config.secret : undefined,
|
|
5228
|
+
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
5229
|
+
redirectUri: hasOAuthAuth ? config.redirectUri : undefined,
|
|
5230
|
+
scope: hasOAuthAuth ? config.scope : undefined
|
|
5231
|
+
});
|
|
5232
|
+
const executionContext = new ExecutionContext();
|
|
5233
|
+
__classPrivateFieldSet(this, _UiPath_authService, new AuthService(internalConfig, executionContext), "f");
|
|
5234
|
+
__classPrivateFieldSet(this, _UiPath_config, internalConfig, "f");
|
|
5235
|
+
// Store internals in SDKInternalsRegistry (not visible on instance)
|
|
5236
|
+
SDKInternalsRegistry.set(this, {
|
|
5237
|
+
config: internalConfig,
|
|
5238
|
+
context: executionContext,
|
|
5239
|
+
tokenManager: __classPrivateFieldGet(this, _UiPath_authService, "f").getTokenManager()
|
|
5240
|
+
});
|
|
5241
|
+
// Expose read-only config for user convenience
|
|
5242
|
+
this.config = {
|
|
5243
|
+
baseUrl: internalConfig.baseUrl,
|
|
5244
|
+
orgName: internalConfig.orgName,
|
|
5245
|
+
tenantName: internalConfig.tenantName
|
|
5208
5246
|
};
|
|
5247
|
+
// Initialize telemetry with SDK configuration
|
|
5248
|
+
telemetryClient.initialize({
|
|
5249
|
+
baseUrl: config.baseUrl,
|
|
5250
|
+
orgName: config.orgName,
|
|
5251
|
+
tenantName: config.tenantName,
|
|
5252
|
+
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
5253
|
+
redirectUri: hasOAuthAuth ? config.redirectUri : undefined
|
|
5254
|
+
});
|
|
5255
|
+
// Track SDK initialization
|
|
5256
|
+
trackEvent('Sdk.Auth');
|
|
5257
|
+
// Auto-initialize for secret-based auth
|
|
5258
|
+
if (hasSecretAuth) {
|
|
5259
|
+
__classPrivateFieldGet(this, _UiPath_authService, "f").authenticateWithSecret(config.secret);
|
|
5260
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5261
|
+
}
|
|
5209
5262
|
}
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5263
|
+
/**
|
|
5264
|
+
* Initialize the SDK based on the provided configuration.
|
|
5265
|
+
* This method handles both OAuth flow initiation and completion automatically.
|
|
5266
|
+
* For secret-based authentication, initialization is automatic and this returns immediately.
|
|
5267
|
+
*/
|
|
5268
|
+
async initialize() {
|
|
5269
|
+
// For secret-based auth, it's already initialized in constructor
|
|
5270
|
+
if (hasSecretConfig(__classPrivateFieldGet(this, _UiPath_config, "f"))) {
|
|
5271
|
+
return;
|
|
5272
|
+
}
|
|
5273
|
+
try {
|
|
5274
|
+
// Check for OAuth callback first
|
|
5275
|
+
if (AuthService.isInOAuthCallback()) {
|
|
5276
|
+
if (await this.completeOAuth()) {
|
|
5277
|
+
return;
|
|
5278
|
+
}
|
|
5279
|
+
}
|
|
5280
|
+
// Check if already authenticated
|
|
5281
|
+
if (this.isAuthenticated()) {
|
|
5282
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5283
|
+
return;
|
|
5284
|
+
}
|
|
5285
|
+
// Start new OAuth flow
|
|
5286
|
+
await __classPrivateFieldGet(this, _UiPath_authService, "f").authenticate(__classPrivateFieldGet(this, _UiPath_config, "f"));
|
|
5287
|
+
if (this.isAuthenticated()) {
|
|
5288
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5289
|
+
}
|
|
5290
|
+
}
|
|
5291
|
+
catch (error) {
|
|
5292
|
+
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
5293
|
+
throw new Error(`Failed to initialize UiPath SDK: ${errorMessage}`);
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
/**
|
|
5297
|
+
* Check if the SDK has been initialized
|
|
5298
|
+
*/
|
|
5299
|
+
isInitialized() {
|
|
5300
|
+
return __classPrivateFieldGet(this, _UiPath_initialized, "f");
|
|
5301
|
+
}
|
|
5302
|
+
/**
|
|
5303
|
+
* Check if we're in an OAuth callback state
|
|
5304
|
+
*/
|
|
5305
|
+
isInOAuthCallback() {
|
|
5306
|
+
return AuthService.isInOAuthCallback();
|
|
5307
|
+
}
|
|
5308
|
+
/**
|
|
5309
|
+
* Complete OAuth authentication flow (only call if isInOAuthCallback() is true)
|
|
5310
|
+
*/
|
|
5311
|
+
async completeOAuth() {
|
|
5312
|
+
if (!AuthService.isInOAuthCallback()) {
|
|
5313
|
+
throw new Error('Not in OAuth callback state. Call initialize() first to start OAuth flow.');
|
|
5314
|
+
}
|
|
5315
|
+
try {
|
|
5316
|
+
const success = await __classPrivateFieldGet(this, _UiPath_authService, "f").authenticate(__classPrivateFieldGet(this, _UiPath_config, "f"));
|
|
5317
|
+
if (success && this.isAuthenticated()) {
|
|
5318
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5319
|
+
return true;
|
|
5320
|
+
}
|
|
5321
|
+
return false;
|
|
5322
|
+
}
|
|
5323
|
+
catch (error) {
|
|
5324
|
+
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
5325
|
+
throw new Error(`Failed to complete OAuth: ${errorMessage}`);
|
|
5326
|
+
}
|
|
5327
|
+
}
|
|
5328
|
+
/**
|
|
5329
|
+
* Check if the user is authenticated (has valid token)
|
|
5330
|
+
*/
|
|
5331
|
+
isAuthenticated() {
|
|
5332
|
+
return __classPrivateFieldGet(this, _UiPath_authService, "f").hasValidToken();
|
|
5333
|
+
}
|
|
5334
|
+
/**
|
|
5335
|
+
* Get the current authentication token
|
|
5336
|
+
*/
|
|
5337
|
+
getToken() {
|
|
5338
|
+
return __classPrivateFieldGet(this, _UiPath_authService, "f").getToken();
|
|
5339
|
+
}
|
|
5340
|
+
/**
|
|
5341
|
+
* Logout from the SDK, clearing all authentication state.
|
|
5342
|
+
* After calling this method, the user will need to re-initialize to authenticate again.
|
|
5343
|
+
*/
|
|
5344
|
+
logout() {
|
|
5345
|
+
// Secret-based auth has no session to end — skip silently
|
|
5346
|
+
if (hasSecretConfig(__classPrivateFieldGet(this, _UiPath_config, "f"))) {
|
|
5347
|
+
return;
|
|
5348
|
+
}
|
|
5349
|
+
__classPrivateFieldGet(this, _UiPath_authService, "f").logout();
|
|
5350
|
+
__classPrivateFieldSet(this, _UiPath_initialized, false, "f");
|
|
5214
5351
|
}
|
|
5215
|
-
return {
|
|
5216
|
-
message: String(error)
|
|
5217
|
-
};
|
|
5218
5352
|
}
|
|
5353
|
+
_UiPath_config = new WeakMap(), _UiPath_authService = new WeakMap(), _UiPath_initialized = new WeakMap();
|
|
5219
5354
|
|
|
5220
5355
|
/**
|
|
5221
5356
|
* Constants used throughout the pagination system
|
|
@@ -5240,8 +5375,16 @@ function getLimitedPageSize(pageSize) {
|
|
|
5240
5375
|
return Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
|
|
5241
5376
|
}
|
|
5242
5377
|
|
|
5378
|
+
exports.APP_NAME = APP_NAME;
|
|
5243
5379
|
exports.AuthenticationError = AuthenticationError;
|
|
5244
5380
|
exports.AuthorizationError = AuthorizationError;
|
|
5381
|
+
exports.CLOUD_CLIENT_ID = CLOUD_CLIENT_ID;
|
|
5382
|
+
exports.CLOUD_ORGANIZATION_NAME = CLOUD_ORGANIZATION_NAME;
|
|
5383
|
+
exports.CLOUD_REDIRECT_URI = CLOUD_REDIRECT_URI;
|
|
5384
|
+
exports.CLOUD_ROLE_NAME = CLOUD_ROLE_NAME;
|
|
5385
|
+
exports.CLOUD_TENANT_NAME = CLOUD_TENANT_NAME;
|
|
5386
|
+
exports.CLOUD_URL = CLOUD_URL;
|
|
5387
|
+
exports.CONNECTION_STRING = CONNECTION_STRING;
|
|
5245
5388
|
exports.DEFAULT_ITEMS_FIELD = DEFAULT_ITEMS_FIELD;
|
|
5246
5389
|
exports.DEFAULT_PAGE_SIZE = DEFAULT_PAGE_SIZE;
|
|
5247
5390
|
exports.DEFAULT_TOTAL_COUNT_FIELD = DEFAULT_TOTAL_COUNT_FIELD;
|
|
@@ -5251,9 +5394,16 @@ exports.MAX_PAGE_SIZE = MAX_PAGE_SIZE;
|
|
|
5251
5394
|
exports.NetworkError = NetworkError;
|
|
5252
5395
|
exports.NotFoundError = NotFoundError;
|
|
5253
5396
|
exports.RateLimitError = RateLimitError;
|
|
5397
|
+
exports.SDK_LOGGER_NAME = SDK_LOGGER_NAME;
|
|
5398
|
+
exports.SDK_RUN_EVENT = SDK_RUN_EVENT;
|
|
5399
|
+
exports.SDK_SERVICE_NAME = SDK_SERVICE_NAME;
|
|
5400
|
+
exports.SDK_VERSION = SDK_VERSION;
|
|
5401
|
+
exports.SERVICE = SERVICE;
|
|
5254
5402
|
exports.ServerError = ServerError;
|
|
5403
|
+
exports.UNKNOWN = UNKNOWN;
|
|
5255
5404
|
exports.UiPath = UiPath;
|
|
5256
5405
|
exports.UiPathError = UiPathError;
|
|
5406
|
+
exports.VERSION = VERSION;
|
|
5257
5407
|
exports.ValidationError = ValidationError;
|
|
5258
5408
|
exports.getErrorDetails = getErrorDetails;
|
|
5259
5409
|
exports.getLimitedPageSize = getLimitedPageSize;
|
|
@@ -5265,3 +5415,6 @@ exports.isRateLimitError = isRateLimitError;
|
|
|
5265
5415
|
exports.isServerError = isServerError;
|
|
5266
5416
|
exports.isUiPathError = isUiPathError;
|
|
5267
5417
|
exports.isValidationError = isValidationError;
|
|
5418
|
+
exports.telemetryClient = telemetryClient;
|
|
5419
|
+
exports.track = track;
|
|
5420
|
+
exports.trackEvent = trackEvent;
|