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