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