@uipath/uipath-typescript 1.0.0 → 1.1.0

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