@bodhiapp/bodhi-js-react 0.0.5 → 0.0.6

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.
@@ -26,6 +26,7 @@ export declare const ERROR_TYPES: {
26
26
  readonly NETWORK_ERROR: "network_error";
27
27
  readonly EXTENSION_ERROR: "extension_error";
28
28
  readonly TIMEOUT_ERROR: "timeout_error";
29
+ readonly AUTH_ERROR: "auth_error";
29
30
  };
30
31
  export type ConnectionErrorType = (typeof ERROR_TYPES)[keyof typeof ERROR_TYPES];
31
32
  export declare const DOCUMENT_STATE_COMPLETE = "complete";
@@ -1,9 +1,9 @@
1
- import { CreateChatCompletionStreamResponse } from '@bodhiapp/ts-client';
1
+ import { AppAccessResponse, CreateChatCompletionStreamResponse } from '@bodhiapp/ts-client';
2
2
  import { IDirectClient } from './interface';
3
3
  import { Logger } from './logger';
4
4
  import { OAuthEndpoints, RefreshTokenResponse } from './oauth';
5
5
  import { StorageKeys } from './storage';
6
- import { ApiResponseResult, AuthLoggedIn, AuthLoggedOut, AuthState, BackendServerState, ClientState, DirectState, InitParams, LogLevel, SerializedDirectState, StateChangeCallback } from './types';
6
+ import { ApiResponseResult, AuthState, BackendServerState, ClientState, DirectState, InitParams, LogLevel, SerializedDirectState, StateChangeCallback } from './types';
7
7
  /**
8
8
  * DirectClientBase - Abstract base class for DirectClient implementations
9
9
  *
@@ -101,8 +101,8 @@ export declare abstract class DirectClientBase implements IDirectClient {
101
101
  * Debug dump of DirectClient internal state
102
102
  */
103
103
  debug(): Promise<Record<string, unknown>>;
104
- abstract login(): Promise<AuthLoggedIn>;
105
- abstract logout(): Promise<AuthLoggedOut>;
104
+ abstract login(): Promise<AuthState>;
105
+ abstract logout(): Promise<AuthState>;
106
106
  getAuthState(): Promise<AuthState>;
107
107
  protected _getAccessTokenRaw(): Promise<string | null>;
108
108
  /**
@@ -118,7 +118,7 @@ export declare abstract class DirectClientBase implements IDirectClient {
118
118
  * Store refreshed tokens
119
119
  */
120
120
  protected _storeRefreshedTokens(tokens: RefreshTokenResponse): Promise<void>;
121
- protected requestResourceAccess(): Promise<string>;
121
+ protected requestResourceAccess(): Promise<ApiResponseResult<AppAccessResponse>>;
122
122
  protected exchangeCodeForTokens(code: string): Promise<void>;
123
123
  protected revokeRefreshToken(): Promise<void>;
124
124
  protected clearAuthStorage(): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  import { CreateChatCompletionStreamResponse } from '@bodhiapp/ts-client';
2
- import { IConnectionClient } from './interface';
2
+ import { IConnectionClient, IExtensionClient } from './interface';
3
3
  import { Logger } from './logger';
4
4
  import { BodhiClientUserPrefsManager } from './storage';
5
- import { ApiResponseResult, AuthLoggedIn, AuthLoggedOut, AuthState, BackendServerState, ClientState, ConnectionMode, DirectState, ExtensionState, InitParams, SerializedClientState, SerializedDirectState, SerializedExtensionState, StateChange, StateChangeCallback } from './types';
5
+ import { ApiResponseResult, AuthState, BackendServerState, ClientState, ConnectionMode, DirectState, ExtensionState, InitParams, SerializedClientState, SerializedDirectState, SerializedExtensionState, StateChange, StateChangeCallback } from './types';
6
6
  /**
7
7
  * Base facade client with common delegation logic
8
8
  *
@@ -10,7 +10,7 @@ import { ApiResponseResult, AuthLoggedIn, AuthLoggedOut, AuthState, BackendServe
10
10
  * @template TExtClient - Extension client type (implements IConnectionClient with SerializedExtensionState)
11
11
  * @template TDirectClient - Direct client type (implements IConnectionClient with SerializedDirectState)
12
12
  */
13
- export declare abstract class BaseFacadeClient<TConfig, TExtClient extends IConnectionClient<unknown, SerializedExtensionState>, TDirectClient extends IConnectionClient<unknown, SerializedDirectState>> {
13
+ export declare abstract class BaseFacadeClient<TConfig, TExtClient extends IExtensionClient<unknown, SerializedExtensionState>, TDirectClient extends IConnectionClient<unknown, SerializedDirectState>> {
14
14
  protected logger: Logger;
15
15
  protected extClient: TExtClient;
16
16
  protected directClient: TDirectClient;
@@ -89,8 +89,8 @@ export declare abstract class BaseFacadeClient<TConfig, TExtClient extends IConn
89
89
  isServerReady(): boolean;
90
90
  sendExtRequest<TParams = void, TRes = unknown>(action: string, params?: TParams): Promise<TRes>;
91
91
  sendApiRequest<TReq = void, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): Promise<ApiResponseResult<TRes>>;
92
- login(): Promise<AuthLoggedIn>;
93
- logout(): Promise<AuthLoggedOut>;
92
+ login(): Promise<AuthState>;
93
+ logout(): Promise<AuthState>;
94
94
  getAuthState(): Promise<AuthState>;
95
95
  pingApi(): Promise<ApiResponseResult<{
96
96
  message: string;
@@ -1,5 +1,5 @@
1
1
  import { CreateChatCompletionStreamResponse } from '@bodhiapp/ts-client';
2
- import { ApiResponseResult, AuthLoggedIn, AuthLoggedOut, AuthState, BackendServerState, ClientState, ConnectionMode, DirectState, ExtensionState, InitParams, StateChangeCallback } from './types';
2
+ import { ApiResponseResult, AuthState, BackendServerState, ClientState, ConnectionMode, DirectState, ExtensionState, InitParams, StateChangeCallback } from './types';
3
3
  /**
4
4
  * ConnectionClient - Base interface for all client implementations
5
5
  *
@@ -85,14 +85,14 @@ export interface IConnectionClient<IParams = unknown, SerialState = unknown> {
85
85
  * Login via OAuth
86
86
  * - IExtensionClient: Delegates to extension (chrome.identity or browser redirect)
87
87
  * - DirectClient: Direct HTTP OAuth flow
88
- * @returns AuthLoggedIn with login state and user info
88
+ * @returns AuthState with login state and user info
89
89
  */
90
- login(): Promise<AuthLoggedIn>;
90
+ login(): Promise<AuthState>;
91
91
  /**
92
92
  * Logout and revoke tokens
93
- * @returns AuthLoggedOut with logged out state
93
+ * @returns AuthState with logged out state
94
94
  */
95
- logout(): Promise<AuthLoggedOut>;
95
+ logout(): Promise<AuthState>;
96
96
  /**
97
97
  * Get current authentication state
98
98
  * @returns AuthState (discriminated union: AuthLoggedIn | AuthLoggedOut)
@@ -205,6 +205,14 @@ export type UIClient = IConnectionClient<InitParams> & {
205
205
  * @returns DirectState
206
206
  */
207
207
  getDirectState(): Promise<DirectState>;
208
+ /**
209
+ * Send extension request (only available in extension mode)
210
+ * @param action - Extension action
211
+ * @param params - Action parameters
212
+ * @returns Response from extension
213
+ * @throws Error if connection mode is not 'extension'
214
+ */
215
+ sendExtRequest<TParams = void, TRes = unknown>(action: string, params?: TParams): Promise<TRes>;
208
216
  };
209
217
  /**
210
218
  * Web-specific UIClient interface with OAuth callback handling
@@ -217,7 +225,7 @@ export interface IWebUIClient extends UIClient {
217
225
  * @param state - State parameter for CSRF protection
218
226
  * @returns AuthLoggedIn with login state and user info
219
227
  */
220
- handleOAuthCallback(code: string, state: string): Promise<AuthLoggedIn>;
228
+ handleOAuthCallback(code: string, state: string): Promise<AuthState>;
221
229
  }
222
230
  /**
223
231
  * Type guard to check if client has OAuth callback handling (web mode)
@@ -31,7 +31,7 @@ export interface OnboardingModalConfig {
31
31
  /**
32
32
  * Path to modal HTML file relative to extension root
33
33
  * Used with chrome.runtime.getURL() in extension context
34
- * @default 'src/sdk/core/onboarding/modal.html'
34
+ * @default 'src/bodhi-js-core/setup-modal.html'
35
35
  */
36
36
  modalHtmlPath?: string;
37
37
  /**
@@ -0,0 +1,37 @@
1
+ import { UserInfo } from './user-info';
2
+ /**
3
+ * Authentication status enum
4
+ */
5
+ export type AuthStatus = 'idle' | 'loading' | 'authenticated' | 'unauthenticated' | 'error';
6
+ /**
7
+ * Authentication error
8
+ */
9
+ export interface AuthError {
10
+ code: string;
11
+ message: string;
12
+ }
13
+ /**
14
+ * Authentication state (flat interface)
15
+ */
16
+ export interface AuthState {
17
+ status: AuthStatus;
18
+ user: UserInfo | null;
19
+ accessToken: string | null;
20
+ error: AuthError | null;
21
+ }
22
+ /**
23
+ * Helper to check if authenticated
24
+ */
25
+ export declare function isAuthenticated(state: AuthState): boolean;
26
+ /**
27
+ * Helper to check if loading
28
+ */
29
+ export declare function isAuthLoading(state: AuthState): boolean;
30
+ /**
31
+ * Helper to check if error
32
+ */
33
+ export declare function isAuthError(state: AuthState): boolean;
34
+ /**
35
+ * Initial auth state
36
+ */
37
+ export declare const INITIAL_AUTH_STATE: AuthState;
@@ -1,5 +1,5 @@
1
1
  import { ClientState } from './client-state';
2
- import { AuthState } from './user-info';
2
+ import { AuthState } from './auth';
3
3
  /**
4
4
  * Discriminated union for state changes.
5
5
  * Allows single callback to handle both client state and auth state changes.
@@ -27,51 +27,24 @@ export declare const SERVER_ERROR_CODES: {
27
27
  readonly type: "extension_error";
28
28
  };
29
29
  };
30
- export declare const BACKEND_SERVER_NOT_REACHABLE: BackendServerNotReachableState;
31
30
  /**
32
- * Server is ready - no error field
31
+ * All possible server status values
32
+ * Unified across extension and direct modes
33
33
  */
34
- export interface BackendServerReadyState {
35
- status: 'ready';
36
- version: string;
37
- }
38
- /**
39
- * Server is reachable but needs configuration - has error details
40
- * error code: 'server-not-ready'
41
- */
42
- export interface BackendServerNotReadyState {
43
- status: 'setup' | 'resource-admin' | 'error';
44
- version: string;
45
- error: OperationErrorResponse;
46
- }
34
+ export type ServerStatus = 'not-connected' | 'pending-extension-ready' | 'ready' | 'setup' | 'resource-admin' | 'error' | 'not-reachable';
47
35
  /**
48
- * Server is not reachable - network/connection failure
49
- * error code: 'not-reachable'
50
- */
51
- export interface BackendServerNotReachableState {
52
- status: 'not-reachable';
53
- error: OperationErrorResponse;
54
- }
55
- /**
56
- * Backend server state from /bodhi/v1/info endpoint
57
- * Discriminated union with definite fields per state
36
+ * Backend server state - flat interface with nullable fields
37
+ * Replaces discriminated union of 5 separate interfaces
58
38
  * Different from setup-modal's ServerState which includes UI states
59
39
  */
60
- export type BackendServerState = BackendServerReadyState | BackendServerNotReadyState | BackendServerNotReachableState;
61
- /**
62
- * State indicating extension is ready but server state is pending
63
- */
64
- export interface PendingExtensionReadyState {
65
- status: 'pending-extension-ready';
66
- }
67
- export declare const PENDING_EXTENSION_READY: PendingExtensionReadyState;
68
- /**
69
- * State indicating direct connection not yet established
70
- */
71
- export interface BackendServerNotConnectedState {
72
- status: 'not-connected';
40
+ export interface BackendServerState {
41
+ status: ServerStatus;
42
+ version: string | null;
43
+ error: OperationErrorResponse | null;
73
44
  }
74
- export declare const BACKEND_SERVER_NOT_CONNECTED: BackendServerNotConnectedState;
45
+ export declare const BACKEND_SERVER_NOT_REACHABLE: BackendServerState;
46
+ export declare const PENDING_EXTENSION_READY: BackendServerState;
47
+ export declare const BACKEND_SERVER_NOT_CONNECTED: BackendServerState;
75
48
  /**
76
49
  * Raw response from /bodhi/v1/info endpoint
77
50
  */
@@ -80,8 +53,8 @@ export interface ServerInfoResponse {
80
53
  version?: string;
81
54
  error?: OperationErrorResponse;
82
55
  }
83
- export declare function isServerReady(state: BackendServerState): state is BackendServerReadyState;
84
- export declare function backendServerNotReady(status: 'setup' | 'resource-admin' | 'error', version?: string, error?: OperationErrorResponse): BackendServerNotReadyState;
56
+ export declare function isServerReady(state: BackendServerState): boolean;
57
+ export declare function backendServerNotReady(status: 'setup' | 'resource-admin' | 'error', version?: string, error?: OperationErrorResponse): BackendServerState;
85
58
  /**
86
59
  * ClientState - Unified state for extension or direct connectivity
87
60
  * Discriminated union with type field: 'extension' | 'direct'
@@ -115,60 +88,37 @@ export interface InitParams {
115
88
  }
116
89
  export declare function isExtensionState(state: ClientState): state is ExtensionState;
117
90
  export declare function isDirectState(state: ClientState): state is DirectState;
118
- export interface DirectStateNotInitialized {
119
- type: 'direct';
120
- server: BackendServerNotConnectedState;
121
- }
122
- export interface DirectStateInitialized {
123
- type: 'direct';
124
- url: string;
125
- server: BackendServerNotConnectedState | BackendServerState;
126
- }
127
- export interface DirectStateReady {
128
- type: 'direct';
129
- url: string;
130
- server: BackendServerReadyState;
131
- }
132
- export interface DirectStateNotReachable {
91
+ /**
92
+ * DirectState - Flat interface with nullable url
93
+ * url is null when not initialized, string when configured
94
+ */
95
+ export interface DirectState {
133
96
  type: 'direct';
134
- url: string;
135
- server: BackendServerNotReachableState;
97
+ url: string | null;
98
+ server: BackendServerState;
136
99
  }
137
- export interface DirectStateNotReady {
138
- type: 'direct';
139
- url: string;
140
- server: BackendServerNotReadyState;
100
+ export declare function isDirectServerReady(state: DirectState): boolean;
101
+ export declare function isDirectClientReady(state: DirectState): boolean;
102
+ export declare const DIRECT_STATE_NOT_INITIALIZED: DirectState;
103
+ export declare function createDirectStateReady(url: string, version?: string): DirectState;
104
+ export declare function createDirectStateNotReachable(url: string): DirectState;
105
+ export declare function createDirectStateNotReady(url: string, server: BackendServerState): DirectState;
106
+ /**
107
+ * ExtensionState - Flat interface with nullable extensionId
108
+ * extensionId is null when not ready, string when ready
109
+ */
110
+ export interface ExtensionState {
111
+ type: 'extension';
112
+ extension: 'not-initialized' | 'not-found' | 'ready';
113
+ extensionId: string | null;
114
+ server: BackendServerState;
141
115
  }
142
- export type DirectState = DirectStateNotInitialized | DirectStateInitialized | DirectStateReady | DirectStateNotReachable | DirectStateNotReady;
143
- export declare function isDirectServerReady(state: DirectState): state is DirectStateReady;
144
- export declare function isDirectClientReady(state: DirectState): state is DirectStateInitialized | DirectStateReady | DirectStateNotReachable | DirectStateNotReady;
145
- export declare const DIRECT_STATE_NOT_INITIALIZED: DirectStateNotInitialized;
146
- export declare function createDirectStateReady(url: string, version?: string): DirectStateReady;
147
- export declare function createDirectStateNotReachable(url: string): DirectStateNotReachable;
148
- export declare function createDirectStateNotReady(url: string, server: BackendServerNotReadyState): DirectStateNotReady;
149
116
  export declare const EXTENSION_STATE_NOT_INITIALIZED: ExtensionState;
150
117
  export declare const EXTENSION_STATE_NOT_FOUND: ExtensionState;
151
- export interface ExtensionStateNotInitialized {
152
- type: 'extension';
153
- extension: 'not-initialized';
154
- server: PendingExtensionReadyState;
155
- }
156
- export interface ExtensionStateNotFound {
157
- type: 'extension';
158
- extension: 'not-found';
159
- server: PendingExtensionReadyState;
160
- }
161
- export interface ExtensionStateReady {
162
- type: 'extension';
163
- extension: 'ready';
164
- extensionId: string;
165
- server: PendingExtensionReadyState | BackendServerState;
166
- }
167
- export type ExtensionState = ExtensionStateNotInitialized | ExtensionStateNotFound | ExtensionStateReady;
168
- export declare function isExtensionServerReady(state: ExtensionState): state is ExtensionStateReady;
169
- export declare function isExtensionClientReady(state: ExtensionState): state is ExtensionStateReady;
170
- export declare function createExtensionStateNotInitialized(): ExtensionStateNotInitialized;
171
- export declare function createExtensionStateNotFound(): ExtensionStateNotFound;
118
+ export declare function isExtensionServerReady(state: ExtensionState): boolean;
119
+ export declare function isExtensionClientReady(state: ExtensionState): boolean;
120
+ export declare function createExtensionStateNotInitialized(): ExtensionState;
121
+ export declare function createExtensionStateNotFound(): ExtensionState;
172
122
  /**
173
123
  * Check if client is ready (has handle/url) - does NOT require server ready
174
124
  * Use this for auth checks and reload state checks
@@ -177,14 +127,14 @@ export declare function isClientReady(state: ClientState): boolean;
177
127
  /**
178
128
  * Get backend server state from client state
179
129
  */
180
- export declare function getBackendServerState(state: ClientState): BackendServerState | PendingExtensionReadyState | BackendServerNotConnectedState;
130
+ export declare function getBackendServerState(state: ClientState): BackendServerState;
181
131
  /**
182
132
  * Safely get extension ID from client state
183
- * @returns Extension ID if state is ExtensionStateReady, undefined otherwise
133
+ * @returns Extension ID if present, undefined otherwise
184
134
  */
185
135
  export declare function getExtensionId(state: ClientState): string | undefined;
186
136
  /**
187
137
  * Safely get server URL from client state
188
- * @returns Server URL if state is DirectState with URL, undefined otherwise
138
+ * @returns Server URL if present, undefined otherwise
189
139
  */
190
140
  export declare function getServerUrl(state: ClientState): string | undefined;
@@ -5,9 +5,10 @@ export { isApiResultError, isApiResultOperationError, isApiResultSuccess } from
5
5
  export type { ApiResponseResult } from './api';
6
6
  export { createApiError, createOperationError } from '../errors';
7
7
  export { BACKEND_SERVER_NOT_CONNECTED, BACKEND_SERVER_NOT_REACHABLE, backendServerNotReady, createDirectStateNotReachable, createDirectStateNotReady, createDirectStateReady, createExtensionStateNotFound, createExtensionStateNotInitialized, DIRECT_STATE_NOT_INITIALIZED, EXTENSION_STATE_NOT_FOUND, EXTENSION_STATE_NOT_INITIALIZED, getBackendServerState, getExtensionId, getServerUrl, isClientReady, isDirectClientReady, isDirectServerReady, isDirectState, isExtensionClientReady, isExtensionServerReady, isExtensionState, isServerReady, PENDING_EXTENSION_READY, SERVER_ERROR_CODES, } from './client-state';
8
- export type { BackendServerNotConnectedState, BackendServerNotReachableState, BackendServerNotReadyState, BackendServerReadyState, BackendServerState, ClientState, ConnectionMode, DirectState, DirectStateNotInitialized, DirectStateNotReachable, DirectStateNotReady, DirectStateReady, ExtensionState, ExtensionStateNotFound, ExtensionStateNotInitialized, ExtensionStateReady, InitParams, PendingExtensionReadyState, SerializedClientState, SerializedDirectState, SerializedExtensionState, ServerInfoResponse, } from './client-state';
9
- export { AUTH_CLIENT_NOT_INITIALIZED as AUTH_EXT_NOT_INITIALIZED, isAuthError, isAuthLoggedIn, isAuthLoggedOut, } from './user-info';
10
- export type { AuthLoggedIn, AuthLoggedOut, AuthState, Tokens, UserInfo, UserScope, } from './user-info';
8
+ export type { BackendServerState, ClientState, ConnectionMode, DirectState, ExtensionState, InitParams, SerializedClientState, SerializedDirectState, SerializedExtensionState, ServerInfoResponse, ServerStatus, } from './client-state';
9
+ export { INITIAL_AUTH_STATE, isAuthError, isAuthLoading, isAuthenticated } from './auth';
10
+ export type { AuthError, AuthState, AuthStatus } from './auth';
11
+ export type { Tokens, UserInfo, UserScope } from './user-info';
11
12
  export type { ClientConfig, DiscoveryResult, LogLevel } from './config';
12
13
  export type { BrowserInfo, OSInfo } from './platform';
13
14
  export { NOOP_STATE_CALLBACK } from './callback';
@@ -25,35 +25,3 @@ export interface Tokens {
25
25
  idToken?: string;
26
26
  expiresIn: number;
27
27
  }
28
- /**
29
- * Authentication state when user is logged in
30
- */
31
- export interface AuthLoggedIn {
32
- isLoggedIn: true;
33
- userInfo: UserInfo;
34
- accessToken: string;
35
- }
36
- /**
37
- * Authentication state when user is logged out
38
- */
39
- export interface AuthLoggedOut {
40
- isLoggedIn: false;
41
- }
42
- /**
43
- * Authentication state when there is an error
44
- */
45
- export interface AuthError {
46
- isLoggedIn: false;
47
- error: {
48
- message: string;
49
- code: string;
50
- };
51
- }
52
- export declare const AUTH_CLIENT_NOT_INITIALIZED: AuthError;
53
- export declare function isAuthError(state: unknown): state is AuthError;
54
- export declare function isAuthLoggedOut(state: unknown): state is AuthLoggedOut;
55
- export declare function isAuthLoggedIn(state: unknown): state is AuthLoggedIn;
56
- /**
57
- * Authentication state (discriminated union)
58
- */
59
- export type AuthState = AuthLoggedIn | AuthLoggedOut | AuthError;
@@ -15,12 +15,20 @@ export interface BodhiContext {
15
15
  client: UIClient;
16
16
  clientState: ClientContextState;
17
17
  setupState: SetupState;
18
- auth: AuthState | null;
19
- authLoading: boolean;
18
+ auth: AuthState;
19
+ isAuthLoading: boolean;
20
20
  login: () => Promise<void>;
21
21
  logout: () => Promise<void>;
22
22
  showSetup: () => Promise<void>;
23
23
  hideSetup: () => void;
24
+ isAuthenticated: boolean;
25
+ canLogin: boolean;
26
+ isReady: boolean;
27
+ isServerReady: boolean;
28
+ isOverallReady: boolean;
29
+ isInitializing: boolean;
30
+ isExtension: boolean;
31
+ isDirect: boolean;
24
32
  }
25
33
  export declare const BodhiReactContext: import('react').Context<BodhiContext | null>;
26
34
  export declare function BodhiProvider({ children, client, modalHtmlPath, handleCallback, callbackPath, basePath, logLevel, }: BodhiProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -1,33 +1,26 @@
1
- import { ClientState } from '../../core/src/index.ts';
2
- export interface ClientContextNotInitialized {
3
- type: 'not-initialized';
1
+ import { BackendServerState, ClientState } from '../../core/src/index.ts';
2
+ import { OperationErrorResponse } from '../../../bodhi-browser-ext/src/types';
3
+ export type ClientContextStatus = 'not-initialized' | 'initializing' | 'extension-not-found' | 'direct-not-connected' | 'ready';
4
+ export interface ClientContextState {
5
+ status: ClientContextStatus;
6
+ mode: 'extension' | 'direct' | null;
7
+ extensionId: string | null;
8
+ url: string | null;
9
+ server: BackendServerState;
10
+ error: OperationErrorResponse | null;
4
11
  }
5
- export interface ClientContextInitializing {
6
- type: 'initializing';
7
- }
8
- export type ClientContextState = ClientContextNotInitialized | ClientContextInitializing | ClientState;
9
- export declare function isClientCtxNotInitialized(state: ClientContextState): state is ClientContextNotInitialized;
10
- export declare function isClientCtxInitializing(state: ClientContextState): state is ClientContextInitializing;
11
- export declare function isClientCtxInitialized(state: ClientContextState): state is ClientState;
12
+ export declare const INITIAL_CLIENT_CONTEXT_STATE: ClientContextState;
13
+ export declare const INITIALIZING_CLIENT_CONTEXT_STATE: ClientContextState;
14
+ export declare function isClientCtxNotInitialized(state: ClientContextState): boolean;
15
+ export declare function isClientCtxInitializing(state: ClientContextState): boolean;
16
+ export declare function isClientCtxInitialized(state: ClientContextState): boolean;
12
17
  export declare function isClientCtxReady(state: ClientContextState): boolean;
13
- export interface ClientInitState {
14
- ready: boolean;
15
- actualState: string;
16
- mode: string | null;
17
- }
18
- export interface ServerState {
19
- ready: boolean;
20
- actualState: string;
21
- }
22
- export declare function getClientInitState(state: ClientContextState): ClientInitState;
23
- export declare function getServerState(state: ClientContextState): ServerState;
24
18
  export declare function isOverallReady(state: ClientContextState): boolean;
19
+ export declare function clientStateToContextState(state: ClientState): ClientContextState;
25
20
  export declare const ClientCtxState: {
26
21
  readonly isNotInitialized: typeof isClientCtxNotInitialized;
27
22
  readonly isInitializing: typeof isClientCtxInitializing;
28
23
  readonly isInitialized: typeof isClientCtxInitialized;
29
24
  readonly isReady: typeof isClientCtxReady;
30
- readonly getClientInitState: typeof getClientInitState;
31
- readonly getServerState: typeof getServerState;
32
25
  readonly isOverallReady: typeof isOverallReady;
33
26
  };
@@ -4,12 +4,13 @@
4
4
  * Public API exports - minimal surface area following industry best practices
5
5
  */
6
6
  export { BodhiProvider, BodhiReactContext, useBodhi, type BodhiContext, type BodhiProviderProps, type SetupState, } from './BodhiProvider';
7
- export type { ClientContextState, ClientInitState, ServerState } from './client-ctx';
8
- export { ClientCtxState, getClientInitState, getServerState, isClientCtxInitialized, isClientCtxInitializing, isClientCtxNotInitialized, isClientCtxReady, isOverallReady, } from './client-ctx';
7
+ export type { ClientContextState, ClientContextStatus } from './client-ctx';
8
+ export { INITIAL_CLIENT_CONTEXT_STATE, INITIALIZING_CLIENT_CONTEXT_STATE } from './client-ctx';
9
+ export { ClientCtxState, clientStateToContextState, isClientCtxInitialized, isClientCtxInitializing, isClientCtxNotInitialized, isClientCtxReady, isOverallReady, } from './client-ctx';
9
10
  export type { ApiResponseResult, ClientState } from '../../core/src/index.ts';
10
- export { isApiResultError, isApiResultOperationError, isApiResultSuccess, isDirectState, isExtensionState, } from '../../core/src/index.ts';
11
+ export { isApiResultError, isApiResultOperationError, isApiResultSuccess, isDirectState, isExtensionState, isWebUIClient, } from '../../core/src/index.ts';
11
12
  export { createApiError, createOperationError } from '../../core/src/index.ts';
12
13
  export type { AuthState, UIClient } from '../../core/src/index.ts';
13
- export { isAuthError, isAuthLoggedIn, isClientReady } from '../../core/src/index.ts';
14
+ export { isAuthError, isAuthLoading, isAuthenticated, isClientReady, } from '../../core/src/index.ts';
14
15
  export { isOperationError, type OperationError } from '../../core/src/index.ts';
15
16
  export { BUILD_MODE as REACT_BUILD_MODE } from './build-info';
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=require("react/jsx-runtime"),s=require("@bodhiapp/bodhi-js-core"),n=require("react"),p={MODAL_READY:"modal:ready",MODAL_REFRESH:"modal:refresh",MODAL_CLOSE:"modal:close",MODAL_COMPLETE:"modal:complete",MODAL_LNA_CONNECT:"modal:lna:connect",MODAL_LNA_SKIP:"modal:lna:skip",MODAL_CONFIRM_SERVER_INSTALL:"modal:confirm-server-install",MODAL_SELECT_CONNECTION:"modal:select-connection"};function H({client:e,modalHtmlPath:o,hideSetup:y,onSetupReady:m,setupState:I,basePath:v="/",logLevel:D="warn"}){const b=I!=="ready",O=n.useMemo(()=>new s.Logger("SetupModalProcessor",D),[D]),c=n.useRef(null),l=n.useMemo(()=>new s.BodhiClientUserPrefsManager(s.createStoragePrefixWithBasePath(v,"bodhijs:")),[v]),S=n.useRef(null),C=n.useCallback(t=>t==="direct"?"lna":t==="extension"?"extension":null,[]),L=n.useCallback(t=>t.extension==="ready"?{status:"ready",version:"unknown",id:t.extensionId}:t.extension==="not-found"?{status:"not-installed",error:{message:"Extension not found",code:"ext-not-installed"}}:{status:"unreachable",error:{message:"Client not initialized",code:"ext-connection-failed"}},[]),g=n.useCallback(t=>{if(t.server.status==="pending-extension-ready")return{status:"pending-extension-ready",error:{message:"Extension not ready",code:"server-pending-ext-ready"}};const r=t.server;switch(r.status){case"ready":return{status:"ready",version:r.version};case"setup":return{status:"setup",version:r.version,error:{message:r.error.message,code:"server-in-setup-status"}};case"resource-admin":return{status:"resource-admin",version:r.version,error:{message:r.error.message,code:"server-in-admin-status"}};case"error":return{status:"error",error:{message:r.error.message,code:"server-unexpected-error"}};case"not-reachable":return{status:"unreachable",error:{message:r.error.message,code:"server-conn-refused"}}}},[]),E=n.useCallback(t=>{if(t.server.status==="not-connected")return{status:"pending-lna-ready"};const r=t.server;switch(r.status){case"ready":return{status:"ready",version:r.version};case"setup":return{status:"setup",version:r.version};case"resource-admin":return{status:"resource-admin",version:r.version};case"error":case"not-reachable":return{status:"error",error:{message:r.error.message}}}},[]),A=n.useCallback((t,r)=>{if(t.server.status!=="pending-extension-ready"){const i=t.server.status;if(i==="ready"||i==="setup"||i==="resource-admin")return!0}if(r.server.status!=="not-connected"){const i=r.server.status;if(i==="ready"||i==="setup"||i==="resource-admin")return!0}return!1},[]),R=n.useCallback((t,r,i)=>{if(r==="skipped")return{status:"skipped",serverUrl:i};if(t.server.status!=="not-connected"){const a=t.server;return a.status==="ready"||a.status==="setup"||a.status==="resource-admin"?{status:"granted",serverUrl:i}:{status:"unreachable",serverUrl:i,error:{message:a.error.message,code:"lna-unreachable"}}}return r==="granted"?{status:"granted",serverUrl:i}:{status:"prompt",serverUrl:i}},[]),d=n.useCallback(async(t=!1)=>{const r=s.detectBrowser().type,i=s.detectOS().type;let a=await e.getExtensionState();(a.extension==="not-initialized"||t)&&(a=await e.testExtensionConnectivity());let u=await e.getDirectState();const h=l.getDirectStatus();h==="granted"&&(u.server.status==="not-connected"||t)&&(u=await e.testDirectConnectivity());const M=s.getServerUrl(u)||"http://localhost:1135",U=R(u,h,M);return e.getConnectionMode()===null&&(s.isDirectServerReady(u)?await e.setConnectionMode("direct"):s.isExtensionServerReady(a)&&await e.setConnectionMode("extension")),l.isServerInstallConfirmed()||A(a,u)&&l.setServerInstallConfirmed(!0),{extension:L(a),server:g(a),lna:U,lnaServer:E(u),env:{browser:r,os:i},browsers:s.BROWSER_CONFIGS,os:s.OS_CONFIGS,userConfirmations:{serverInstall:l.isServerInstallConfirmed()},selectedConnection:C(e.getConnectionMode())}},[e,l,L,g,E,C,R,A]),f=n.useCallback(()=>{if(!S.current)throw new Error("Cannot get state: currentStateRef is null");return{...S.current,userConfirmations:{serverInstall:l.isServerInstallConfirmed()},selectedConnection:C(e.getConnectionMode())}},[l,e,C]),x=n.useMemo(()=>({[p.MODAL_READY]:async()=>(O.info("MODAL_READY: building initial state"),{setupState:f()}),[p.MODAL_REFRESH]:async()=>{O.info("MODAL_REFRESH: refreshing state");const t=await d(!0);S.current=t;const r=f();return O.info(`MODAL_REFRESH: state refreshed
2
- `,JSON.stringify(r,null,2)),c.current?.updateState(f()),{setupState:r}},[p.MODAL_LNA_CONNECT]:async t=>{const r=t.payload.serverUrl;console.log("[SetupModalProcessor] LNA connect:",r);const i=await e.testDirectConnectivity(r);if(i.server.status!=="not-connected"){const u=i.server.status;if(u==="ready"||u==="setup"||u==="resource-admin"){l.setDirectStatus("granted"),e.getConnectionMode()===null&&await e.setConnectionMode("direct");const h=await d();return S.current=h,c.current?.updateState(f()),{success:!0}}}const a=await d();return S.current=a,c.current?.updateState(f()),{success:!1}},[p.MODAL_LNA_SKIP]:async()=>{l.setDirectStatus("skipped");const t=await d();return S.current=t,c.current?.updateState(f()),{success:!0}},[p.MODAL_CLOSE]:()=>{y()},[p.MODAL_COMPLETE]:()=>{y()},[p.MODAL_CONFIRM_SERVER_INSTALL]:t=>(l.setServerInstallConfirmed(t.payload.confirmed),c.current?.updateState(f()),{success:!0}),[p.MODAL_SELECT_CONNECTION]:async t=>{const i=t.payload.connection==="lna"?"direct":"extension";return await e.setConnectionMode(i),c.current?.updateState(f()),{success:!0}}}),[O,e,l,d,f,E,y]);return n.useEffect(()=>(c.current=new s.OnboardingModal({modalHtmlPath:o,handlers:x}),()=>{c.current?.destroy(),c.current=null}),[o,x]),n.useEffect(()=>{b&&c.current?d(!0).then(t=>{S.current=t,c.current?.show(t),m?.()}).catch(t=>{console.error("[SetupModalProcessor] buildSetupState failed:",t)}):!b&&c.current&&c.current.destroy()},[b,d,m]),null}const _=n.createContext(null);_.displayName="BodhiContext";function G({children:e,client:o,modalHtmlPath:y,handleCallback:m=!0,callbackPath:I="/callback",basePath:v="/",logLevel:D="warn"}){const b=n.useRef(!1),O=n.useRef(!1),[c,l]=n.useState({type:"not-initialized"}),[S,C]=n.useState(null),[L,g]=n.useState(!1),[E,A]=n.useState("ready");n.useEffect(()=>{const a=u=>{switch(u.type){case"client-state":l(u.state);break;case"auth-state":C(u.state),g(!1);break}};return o.setStateCallback(a),()=>{o.setStateCallback(s.NOOP_STATE_CALLBACK)}},[o]);const R=n.useCallback(async()=>{A("loading")},[]),d=n.useCallback(()=>{A("ready")},[]),f=n.useCallback(()=>{A("loaded")},[]),x=n.useCallback(async a=>{l({type:"initializing"});try{await o.init(a||{})}catch(u){console.error("[BodhiProvider] Init failed:",u)}},[o]);n.useEffect(()=>{if(O.current)return;O.current=!0,(async()=>{if(await x(),!m)return;const u=new URL(window.location.href);if(u.pathname!==I)return;const h=u.searchParams.get("code"),P=u.searchParams.get("state");!h||!P||b.current||(b.current=!0,s.isWebUIClient(o)&&(g(!0),o.handleOAuthCallback(h,P).then(()=>{window.history.replaceState({},"",v)}).catch(M=>{console.error("OAuth callback failed:",M),C({isLoggedIn:!1,error:{message:M instanceof Error?M.message:"OAuth callback failed",code:"OAUTH_CALLBACK_FAILED"}}),g(!1),window.history.replaceState({},"",v)})))})()},[x,o,m,I,v]);const t=n.useCallback(async()=>{g(!0);try{await o.login()}catch(a){C({isLoggedIn:!1,error:{message:a instanceof Error?a.message:"Login failed",code:"LOGIN_FAILED"}}),g(!1)}},[o]),r=n.useCallback(async()=>{try{await o.logout()}catch(a){C({isLoggedIn:!1,error:{message:a instanceof Error?a.message:"Logout failed",code:"LOGOUT_FAILED"}})}},[o]),i=n.useMemo(()=>({client:o,clientState:c,auth:S,authLoading:L,login:t,logout:r,showSetup:R,hideSetup:d,setupState:E}),[o,c,S,L,E,t,r,R,d]);return B.jsxs(_.Provider,{value:i,children:[B.jsx(H,{client:o,modalHtmlPath:y,hideSetup:d,onSetupReady:f,setupState:E,basePath:v,logLevel:D}),e]})}function K(){const e=n.useContext(_);if(!e)throw new Error("useBodhi must be used within BodhiProvider");return{client:e.client,clientState:e.clientState,setupState:e.setupState,auth:e.auth,authLoading:e.authLoading,login:e.login,logout:e.logout,showSetup:e.showSetup,hideSetup:e.hideSetup}}function k(e){return e.type==="not-initialized"}function N(e){return e.type==="initializing"}function w(e){return!(k(e)||N(e))}function T(e){return w(e)?s.isClientReady(e):!1}function z(e){if(k(e))return{ready:!1,actualState:"not-initialized",mode:null};if(N(e))return{ready:!1,actualState:"initializing",mode:null};const o=s.isExtensionState(e)?"extension":"direct",y=s.isClientReady(e),m=s.isExtensionState(e)?e.extension:e.server.status==="not-connected"?"not-connected":"ready";return{ready:y,actualState:m,mode:o}}function j(e){if(!w(e))return{ready:!1,actualState:"n/a"};const o=e.server;return{ready:o.status==="ready",actualState:o.status}}function F(e){return w(e)?s.isClientReady(e)&&e.server.status==="ready":!1}const V={isNotInitialized:k,isInitializing:N,isInitialized:w,isReady:T,getClientInitState:z,getServerState:j,isOverallReady:F},W="production";Object.defineProperty(exports,"createApiError",{enumerable:!0,get:()=>s.createApiError});Object.defineProperty(exports,"createOperationError",{enumerable:!0,get:()=>s.createOperationError});Object.defineProperty(exports,"isApiResultError",{enumerable:!0,get:()=>s.isApiResultError});Object.defineProperty(exports,"isApiResultOperationError",{enumerable:!0,get:()=>s.isApiResultOperationError});Object.defineProperty(exports,"isApiResultSuccess",{enumerable:!0,get:()=>s.isApiResultSuccess});Object.defineProperty(exports,"isAuthError",{enumerable:!0,get:()=>s.isAuthError});Object.defineProperty(exports,"isAuthLoggedIn",{enumerable:!0,get:()=>s.isAuthLoggedIn});Object.defineProperty(exports,"isClientReady",{enumerable:!0,get:()=>s.isClientReady});Object.defineProperty(exports,"isDirectState",{enumerable:!0,get:()=>s.isDirectState});Object.defineProperty(exports,"isExtensionState",{enumerable:!0,get:()=>s.isExtensionState});Object.defineProperty(exports,"isOperationError",{enumerable:!0,get:()=>s.isOperationError});exports.BodhiProvider=G;exports.BodhiReactContext=_;exports.ClientCtxState=V;exports.REACT_BUILD_MODE=W;exports.getClientInitState=z;exports.getServerState=j;exports.isClientCtxInitialized=w;exports.isClientCtxInitializing=N;exports.isClientCtxNotInitialized=k;exports.isClientCtxReady=T;exports.isOverallReady=F;exports.useBodhi=K;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const k=require("react/jsx-runtime"),s=require("@bodhiapp/bodhi-js-core"),r=require("react"),p={MODAL_READY:"modal:ready",MODAL_REFRESH:"modal:refresh",MODAL_CLOSE:"modal:close",MODAL_COMPLETE:"modal:complete",MODAL_LNA_CONNECT:"modal:lna:connect",MODAL_LNA_SKIP:"modal:lna:skip",MODAL_CONFIRM_SERVER_INSTALL:"modal:confirm-server-install",MODAL_SELECT_CONNECTION:"modal:select-connection"};function H({client:e,modalHtmlPath:i,hideSetup:L,onSetupReady:x,setupState:R,basePath:m="/",logLevel:A="warn"}){const y=R!=="ready",O=r.useMemo(()=>new s.Logger("SetupModalProcessor",A),[A]),l=r.useRef(null),u=r.useMemo(()=>new s.BodhiClientUserPrefsManager(s.createStoragePrefixWithBasePath(m,"bodhijs:")),[m]),C=r.useRef(null),E=r.useCallback(t=>t==="direct"?"lna":t==="extension"?"extension":null,[]),v=r.useCallback(t=>t.extension==="ready"?{status:"ready",version:"unknown",id:t.extensionId}:t.extension==="not-found"?{status:"not-installed",error:{message:"Extension not found",code:"ext-not-installed"}}:{status:"unreachable",error:{message:"Client not initialized",code:"ext-connection-failed"}},[]),b=r.useCallback(t=>{if(t.server.status==="pending-extension-ready")return{status:"pending-extension-ready",error:{message:"Extension not ready",code:"server-pending-ext-ready"}};const n=t.server;switch(n.status){case"ready":return{status:"ready",version:n.version||"unknown"};case"setup":return{status:"setup",version:n.version||"unknown",error:{message:n.error?.message||"Setup required",code:"server-in-setup-status"}};case"resource-admin":return{status:"resource-admin",version:n.version||"unknown",error:{message:n.error?.message||"Resource admin required",code:"server-in-admin-status"}};case"error":return{status:"error",error:{message:n.error?.message||"Unknown error",code:"server-unexpected-error"}};case"not-reachable":return{status:"unreachable",error:{message:n.error?.message||"Server not reachable",code:"server-conn-refused"}};default:return{status:"unreachable",error:{message:"Unknown server status",code:"server-unexpected-error"}}}},[]),g=r.useCallback(t=>{if(t.server.status==="not-connected")return{status:"pending-lna-ready"};const n=t.server;switch(n.status){case"ready":return{status:"ready",version:n.version||"unknown"};case"setup":return{status:"setup",version:n.version||"unknown"};case"resource-admin":return{status:"resource-admin",version:n.version||"unknown"};case"error":case"not-reachable":return{status:"error",error:{message:n.error?.message||"Connection error"}};default:return{status:"error",error:{message:"Unknown server status"}}}},[]),I=r.useCallback((t,n)=>{if(t.server.status!=="pending-extension-ready"){const o=t.server.status;if(o==="ready"||o==="setup"||o==="resource-admin")return!0}if(n.server.status!=="not-connected"){const o=n.server.status;if(o==="ready"||o==="setup"||o==="resource-admin")return!0}return!1},[]),h=r.useCallback((t,n,o)=>{if(n==="skipped")return{status:"skipped",serverUrl:o};if(t.server.status!=="not-connected"){const d=t.server;return d.status==="ready"||d.status==="setup"||d.status==="resource-admin"?{status:"granted",serverUrl:o}:{status:"unreachable",serverUrl:o,error:{message:d.error?.message||"Server unreachable",code:"lna-unreachable"}}}return n==="granted"?{status:"granted",serverUrl:o}:{status:"prompt",serverUrl:o}},[]),S=r.useCallback(async(t=!1)=>{const n=s.detectBrowser().type,o=s.detectOS().type;let d=await e.getExtensionState();(d.extension==="not-initialized"||t)&&(d=await e.testExtensionConnectivity());let a=await e.getDirectState();const c=u.getDirectStatus();c==="granted"&&(a.server.status==="not-connected"||t)&&(a=await e.testDirectConnectivity());const w=s.getServerUrl(a)||"http://localhost:1135",_=h(a,c,w);return e.getConnectionMode()===null&&(s.isDirectServerReady(a)?await e.setConnectionMode("direct"):s.isExtensionServerReady(d)&&await e.setConnectionMode("extension")),u.isServerInstallConfirmed()||I(d,a)&&u.setServerInstallConfirmed(!0),{extension:v(d),server:b(d),lna:_,lnaServer:g(a),env:{browser:n,os:o},browsers:s.BROWSER_CONFIGS,os:s.OS_CONFIGS,userConfirmations:{serverInstall:u.isServerInstallConfirmed()},selectedConnection:E(e.getConnectionMode())}},[e,u,v,b,g,E,h,I]),f=r.useCallback(()=>{if(!C.current)throw new Error("Cannot get state: currentStateRef is null");return{...C.current,userConfirmations:{serverInstall:u.isServerInstallConfirmed()},selectedConnection:E(e.getConnectionMode())}},[u,e,E]),T=r.useMemo(()=>({[p.MODAL_READY]:async()=>(O.info("MODAL_READY: building initial state"),{setupState:f()}),[p.MODAL_REFRESH]:async()=>{O.info("MODAL_REFRESH: refreshing state");const t=await S(!0);C.current=t;const n=f();return O.info(`MODAL_REFRESH: state refreshed
2
+ `,JSON.stringify(n,null,2)),l.current?.updateState(f()),{setupState:n}},[p.MODAL_LNA_CONNECT]:async t=>{const n=t.payload.serverUrl;console.log("[SetupModalProcessor] LNA connect:",n);const o=await e.testDirectConnectivity(n);if(o.server.status!=="not-connected"){const a=o.server.status;if(a==="ready"||a==="setup"||a==="resource-admin"){u.setDirectStatus("granted"),e.getConnectionMode()===null&&await e.setConnectionMode("direct");const c=await S();return C.current=c,l.current?.updateState(f()),{success:!0}}}const d=await S();return C.current=d,l.current?.updateState(f()),{success:!1}},[p.MODAL_LNA_SKIP]:async()=>{u.setDirectStatus("skipped");const t=await S();return C.current=t,l.current?.updateState(f()),{success:!0}},[p.MODAL_CLOSE]:()=>{L()},[p.MODAL_COMPLETE]:()=>{L()},[p.MODAL_CONFIRM_SERVER_INSTALL]:t=>(u.setServerInstallConfirmed(t.payload.confirmed),l.current?.updateState(f()),{success:!0}),[p.MODAL_SELECT_CONNECTION]:async t=>{const o=t.payload.connection==="lna"?"direct":"extension";return await e.setConnectionMode(o),l.current?.updateState(f()),{success:!0}}}),[O,e,u,S,f,g,L]);return r.useEffect(()=>(l.current=new s.OnboardingModal({modalHtmlPath:i,handlers:T}),()=>{l.current?.destroy(),l.current=null}),[i,T]),r.useEffect(()=>{y&&l.current?S(!0).then(t=>{C.current=t,l.current?.show(t),x?.()}).catch(t=>{console.error("[SetupModalProcessor] buildSetupState failed:",t)}):!y&&l.current&&l.current.destroy()},[y,S,x]),null}const P={status:"not-initialized",mode:null,extensionId:null,url:null,server:s.BACKEND_SERVER_NOT_CONNECTED,error:null},z={status:"initializing",mode:null,extensionId:null,url:null,server:s.BACKEND_SERVER_NOT_CONNECTED,error:null};function B(e){return e.status==="not-initialized"}function j(e){return e.status==="initializing"}function U(e){return e.status!=="not-initialized"&&e.status!=="initializing"}function D(e){return e.status==="ready"}function F(e){return D(e)&&e.server.status==="ready"}function G(e){return s.isExtensionState(e)?{status:e.extension==="not-initialized"?"initializing":e.extension==="not-found"?"extension-not-found":"ready",mode:"extension",extensionId:e.extensionId,url:null,server:e.server,error:e.server.error}:{status:e.url===null?"direct-not-connected":"ready",mode:"direct",extensionId:null,url:e.url,server:e.server,error:e.server.error}}const K={isNotInitialized:B,isInitializing:j,isInitialized:U,isReady:D,isOverallReady:F},N=r.createContext(null);N.displayName="BodhiContext";function V({children:e,client:i,modalHtmlPath:L,handleCallback:x=!0,callbackPath:R="/callback",basePath:m="/",logLevel:A="warn"}){const y=r.useMemo(()=>new s.Logger("BodhiProvider",A),[A]),O=r.useRef(!1),l=r.useRef(!1),[u,C]=r.useState(P),[E,v]=r.useState(s.INITIAL_AUTH_STATE),[b,g]=r.useState(!1),[I,h]=r.useState("ready");r.useEffect(()=>{const a=c=>{switch(c.type){case"client-state":C(G(c.state));break;case"auth-state":v(c.state),g(!1);break}};return i.setStateCallback(a),()=>{i.setStateCallback(s.NOOP_STATE_CALLBACK)}},[i]);const S=r.useCallback(async()=>{h("loading")},[]),f=r.useCallback(()=>{h("ready")},[]),T=r.useCallback(()=>{h("loaded")},[]),t=r.useCallback(async a=>{C(z);try{await i.init(a||{})}catch(c){y.error("Init failed:",c)}},[i,y]);r.useEffect(()=>{if(l.current)return;l.current=!0,(async()=>{if(await t(),!x)return;const c=new URL(window.location.href);if(c.pathname!==R)return;const M=c.searchParams.get("code"),w=c.searchParams.get("state");!M||!w||O.current||(O.current=!0,s.isWebUIClient(i)&&(g(!0),i.handleOAuthCallback(M,w).then(()=>{window.history.replaceState({},"",m)}).catch(_=>{y.error("OAuth callback failed:",_),v({status:"error",user:null,accessToken:null,error:{message:_ instanceof Error?_.message:"OAuth callback failed",code:"OAUTH_CALLBACK_FAILED"}}),g(!1),window.history.replaceState({},"",m)})))})()},[t,i,x,R,m]);const n=r.useCallback(async()=>{g(!0);try{await i.login()}catch(a){v({status:"error",user:null,accessToken:null,error:{message:a instanceof Error?a.message:"Login failed",code:"LOGIN_FAILED"}}),g(!1)}},[i]),o=r.useCallback(async()=>{try{await i.logout()}catch(a){v({status:"error",user:null,accessToken:null,error:{message:a instanceof Error?a.message:"Logout failed",code:"LOGOUT_FAILED"}})}},[i]),d=r.useMemo(()=>{const a=u.status==="ready",c=u.server.status==="ready";return{client:i,clientState:u,auth:E,isAuthLoading:b,login:n,logout:o,showSetup:S,hideSetup:f,setupState:I,isAuthenticated:E.status==="authenticated",canLogin:a&&!b,isReady:a,isServerReady:c,isOverallReady:a&&c,isInitializing:u.status==="initializing",isExtension:u.mode==="extension",isDirect:u.mode==="direct"}},[i,u,E,b,I,n,o,S,f]);return k.jsxs(N.Provider,{value:d,children:[k.jsx(H,{client:i,modalHtmlPath:L,hideSetup:f,onSetupReady:T,setupState:I,basePath:m,logLevel:A}),e]})}function W(){const e=r.useContext(N);if(!e)throw new Error("useBodhi must be used within BodhiProvider");return e}const q="production";Object.defineProperty(exports,"createApiError",{enumerable:!0,get:()=>s.createApiError});Object.defineProperty(exports,"createOperationError",{enumerable:!0,get:()=>s.createOperationError});Object.defineProperty(exports,"isApiResultError",{enumerable:!0,get:()=>s.isApiResultError});Object.defineProperty(exports,"isApiResultOperationError",{enumerable:!0,get:()=>s.isApiResultOperationError});Object.defineProperty(exports,"isApiResultSuccess",{enumerable:!0,get:()=>s.isApiResultSuccess});Object.defineProperty(exports,"isAuthError",{enumerable:!0,get:()=>s.isAuthError});Object.defineProperty(exports,"isAuthLoading",{enumerable:!0,get:()=>s.isAuthLoading});Object.defineProperty(exports,"isAuthenticated",{enumerable:!0,get:()=>s.isAuthenticated});Object.defineProperty(exports,"isClientReady",{enumerable:!0,get:()=>s.isClientReady});Object.defineProperty(exports,"isDirectState",{enumerable:!0,get:()=>s.isDirectState});Object.defineProperty(exports,"isExtensionState",{enumerable:!0,get:()=>s.isExtensionState});Object.defineProperty(exports,"isOperationError",{enumerable:!0,get:()=>s.isOperationError});Object.defineProperty(exports,"isWebUIClient",{enumerable:!0,get:()=>s.isWebUIClient});exports.BodhiProvider=V;exports.BodhiReactContext=N;exports.ClientCtxState=K;exports.INITIALIZING_CLIENT_CONTEXT_STATE=z;exports.INITIAL_CLIENT_CONTEXT_STATE=P;exports.REACT_BUILD_MODE=q;exports.clientStateToContextState=G;exports.isClientCtxInitialized=U;exports.isClientCtxInitializing=j;exports.isClientCtxNotInitialized=B;exports.isClientCtxReady=D;exports.isOverallReady=F;exports.useBodhi=W;