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