@bodhiapp/bodhi-js 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/bodhi-browser-ext/src/types/bodhiext.d.ts +202 -0
- package/dist/bodhi-browser-ext/src/types/common.d.ts +36 -0
- package/dist/bodhi-browser-ext/src/types/index.d.ts +6 -0
- package/dist/bodhi-browser-ext/src/types/protocol.d.ts +223 -0
- package/dist/bodhi-js-sdk/core/src/direct-client-base.d.ts +129 -0
- package/dist/bodhi-js-sdk/core/src/errors.d.ts +23 -0
- package/dist/bodhi-js-sdk/core/src/facade-client-base.d.ts +130 -0
- package/dist/bodhi-js-sdk/core/src/index.d.ts +19 -0
- package/dist/bodhi-js-sdk/core/src/interface.d.ts +228 -0
- package/dist/bodhi-js-sdk/core/src/logger.d.ts +13 -0
- package/dist/bodhi-js-sdk/core/src/oauth.d.ts +45 -0
- package/dist/bodhi-js-sdk/core/src/onboarding/config.d.ts +10 -0
- package/dist/bodhi-js-sdk/core/src/onboarding/index.d.ts +5 -0
- package/dist/bodhi-js-sdk/core/src/onboarding/modal.d.ts +80 -0
- package/dist/bodhi-js-sdk/core/src/onboarding/protocol-utils.d.ts +33 -0
- package/dist/bodhi-js-sdk/core/src/platform.d.ts +11 -0
- package/dist/bodhi-js-sdk/core/src/storage.d.ts +81 -0
- package/dist/bodhi-js-sdk/core/src/types/api.d.ts +34 -0
- package/dist/bodhi-js-sdk/core/src/types/callback.d.ts +23 -0
- package/dist/bodhi-js-sdk/core/src/types/client-state.d.ts +191 -0
- package/dist/bodhi-js-sdk/core/src/types/config.d.ts +26 -0
- package/dist/bodhi-js-sdk/core/src/types/html.d.ts +9 -0
- package/dist/bodhi-js-sdk/core/src/types/index.d.ts +15 -0
- package/dist/bodhi-js-sdk/core/src/types/platform.d.ts +16 -0
- package/dist/bodhi-js-sdk/core/src/types/user-info.d.ts +59 -0
- package/dist/bodhi-js-sdk/web/src/constants.d.ts +9 -0
- package/dist/bodhi-js-sdk/web/src/direct-client.d.ts +24 -0
- package/dist/bodhi-js-sdk/web/src/ext-client.d.ts +151 -0
- package/dist/bodhi-js-sdk/web/src/facade-client.d.ts +43 -0
- package/dist/bodhi-js-sdk/web/src/index.d.ts +5 -0
- package/dist/bodhi-js-sdk/web/src/interface.d.ts +4 -0
- package/dist/bodhi-web.cjs.js +749 -0
- package/dist/bodhi-web.cjs.js.map +1 -0
- package/dist/bodhi-web.esm.d.ts +1 -0
- package/dist/bodhi-web.esm.js +749 -0
- package/dist/bodhi-web.esm.js.map +1 -0
- package/dist/setup-modal/src/types/extension.d.ts +24 -0
- package/dist/setup-modal/src/types/index.d.ts +20 -0
- package/dist/setup-modal/src/types/lna.d.ts +56 -0
- package/dist/setup-modal/src/types/message-types.d.ts +169 -0
- package/dist/setup-modal/src/types/platform.d.ts +32 -0
- package/dist/setup-modal/src/types/protocol.d.ts +71 -0
- package/dist/setup-modal/src/types/server.d.ts +63 -0
- package/dist/setup-modal/src/types/state.d.ts +43 -0
- package/dist/setup-modal/src/types/type-guards.d.ts +27 -0
- package/package.json +54 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ApiResponse, OperationErrorResponse } from '../../../../bodhi-browser-ext/src/types';
|
|
2
|
+
import { OpenAiApiError } from '@bodhiapp/ts-client';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Public API result type - discriminated union without protocol fields
|
|
6
|
+
*
|
|
7
|
+
* This is the return type for sendApiRequest() and similar methods.
|
|
8
|
+
* Unlike ApiResponseMessage<T> (which includes type and requestId for internal routing),
|
|
9
|
+
* this is the clean public interface without protocol overhead.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* if ('error' in result) → { error: OperationErrorResponse } (network/extension error)
|
|
13
|
+
* if ('body' in result) → ApiResponse<T> (HTTP completed - check status for success/error)
|
|
14
|
+
*/
|
|
15
|
+
export type ApiResponseResult<T> = ApiResponse<T> | {
|
|
16
|
+
error: OperationErrorResponse;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Type guard for operation error response
|
|
20
|
+
*/
|
|
21
|
+
export declare function isApiResultOperationError<T>(result: ApiResponseResult<T>): result is {
|
|
22
|
+
error: OperationErrorResponse;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Type guard for API response (success or HTTP error)
|
|
26
|
+
*/
|
|
27
|
+
export declare function isApiResultSuccess<T>(result: ApiResponseResult<T>): result is ApiResponse<T> & {
|
|
28
|
+
body: T;
|
|
29
|
+
status: number;
|
|
30
|
+
};
|
|
31
|
+
export declare function isApiResultError<T>(result: ApiResponseResult<T>): result is ApiResponse<OpenAiApiError> & {
|
|
32
|
+
body: OpenAiApiError;
|
|
33
|
+
status: number;
|
|
34
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ClientState } from './client-state';
|
|
2
|
+
import { AuthState } from './user-info';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Discriminated union for state changes.
|
|
6
|
+
* Allows single callback to handle both client state and auth state changes.
|
|
7
|
+
*/
|
|
8
|
+
export type ClientStateChange = {
|
|
9
|
+
type: 'client-state';
|
|
10
|
+
state: ClientState;
|
|
11
|
+
};
|
|
12
|
+
export type AuthStateChange = {
|
|
13
|
+
type: 'auth-state';
|
|
14
|
+
state: AuthState;
|
|
15
|
+
};
|
|
16
|
+
export type StateChange = ClientStateChange | AuthStateChange;
|
|
17
|
+
/**
|
|
18
|
+
* Callback invoked when client state or auth state changes.
|
|
19
|
+
* Defaults to no-op if not provided.
|
|
20
|
+
*/
|
|
21
|
+
export type StateChangeCallback = (change: StateChange) => void;
|
|
22
|
+
/** No-op callback for clients created without listener */
|
|
23
|
+
export declare const NOOP_STATE_CALLBACK: StateChangeCallback;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { OperationErrorResponse } from '../../../../bodhi-browser-ext/src/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Serialized direct client state for persistence
|
|
5
|
+
* Stores minimal state needed to restore direct connection
|
|
6
|
+
*/
|
|
7
|
+
export type SerializedDirectState = {
|
|
8
|
+
url?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Connection mode - how the client communicates with the local server
|
|
12
|
+
*
|
|
13
|
+
* - 'direct': Direct HTTP fetch to local server (requires LNA permission in web context,
|
|
14
|
+
* but extension already has network access)
|
|
15
|
+
* - 'extension': Communication via extension (chrome.runtime or window.bodhiext)
|
|
16
|
+
*/
|
|
17
|
+
export type ConnectionMode = 'direct' | 'extension';
|
|
18
|
+
/**
|
|
19
|
+
* Hardcoded error messages for server states
|
|
20
|
+
*/
|
|
21
|
+
export declare const SERVER_ERROR_CODES: {
|
|
22
|
+
readonly NOT_REACHABLE: {
|
|
23
|
+
readonly message: "server is not reachable on given url";
|
|
24
|
+
readonly type: "network_error";
|
|
25
|
+
};
|
|
26
|
+
readonly SERVER_NOT_READY: {
|
|
27
|
+
readonly message: "server is not in ready state, configure to complete setup";
|
|
28
|
+
readonly type: "extension_error";
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export declare const BACKEND_SERVER_NOT_REACHABLE: BackendServerNotReachableState;
|
|
32
|
+
/**
|
|
33
|
+
* Server is ready - no error field
|
|
34
|
+
*/
|
|
35
|
+
export interface BackendServerReadyState {
|
|
36
|
+
status: 'ready';
|
|
37
|
+
version: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Server is reachable but needs configuration - has error details
|
|
41
|
+
* error code: 'server-not-ready'
|
|
42
|
+
*/
|
|
43
|
+
export interface BackendServerNotReadyState {
|
|
44
|
+
status: 'setup' | 'resource-admin' | 'error';
|
|
45
|
+
version: string;
|
|
46
|
+
error: OperationErrorResponse;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Server is not reachable - network/connection failure
|
|
50
|
+
* error code: 'not-reachable'
|
|
51
|
+
*/
|
|
52
|
+
export interface BackendServerNotReachableState {
|
|
53
|
+
status: 'not-reachable';
|
|
54
|
+
error: OperationErrorResponse;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Backend server state from /bodhi/v1/info endpoint
|
|
58
|
+
* Discriminated union with definite fields per state
|
|
59
|
+
* Different from setup-modal's ServerState which includes UI states
|
|
60
|
+
*/
|
|
61
|
+
export type BackendServerState = BackendServerReadyState | BackendServerNotReadyState | BackendServerNotReachableState;
|
|
62
|
+
/**
|
|
63
|
+
* State indicating extension is ready but server state is pending
|
|
64
|
+
*/
|
|
65
|
+
export interface PendingExtensionReadyState {
|
|
66
|
+
status: 'pending-extension-ready';
|
|
67
|
+
}
|
|
68
|
+
export declare const PENDING_EXTENSION_READY: PendingExtensionReadyState;
|
|
69
|
+
/**
|
|
70
|
+
* State indicating direct connection not yet established
|
|
71
|
+
*/
|
|
72
|
+
export interface BackendServerNotConnectedState {
|
|
73
|
+
status: 'not-connected';
|
|
74
|
+
}
|
|
75
|
+
export declare const BACKEND_SERVER_NOT_CONNECTED: BackendServerNotConnectedState;
|
|
76
|
+
/**
|
|
77
|
+
* Raw response from /bodhi/v1/info endpoint
|
|
78
|
+
*/
|
|
79
|
+
export interface ServerInfoResponse {
|
|
80
|
+
status: 'setup' | 'ready' | 'resource-admin' | 'error';
|
|
81
|
+
version?: string;
|
|
82
|
+
error?: OperationErrorResponse;
|
|
83
|
+
}
|
|
84
|
+
export declare function isServerReady(state: BackendServerState): state is BackendServerReadyState;
|
|
85
|
+
export declare function backendServerNotReady(status: 'setup' | 'resource-admin' | 'error', version?: string, error?: OperationErrorResponse): BackendServerNotReadyState;
|
|
86
|
+
/**
|
|
87
|
+
* ClientState - Unified state for extension or direct connectivity
|
|
88
|
+
* Discriminated union with type field: 'extension' | 'direct'
|
|
89
|
+
*/
|
|
90
|
+
export type ClientState = ExtensionState | DirectState;
|
|
91
|
+
/**
|
|
92
|
+
* Serialized client state for localStorage persistence
|
|
93
|
+
* Nested structure storing each client's state separately
|
|
94
|
+
* Server state is transient and not persisted
|
|
95
|
+
*/
|
|
96
|
+
export type SerializedExtensionState = Record<string, unknown>;
|
|
97
|
+
export interface SerializedClientState {
|
|
98
|
+
connectionMode: ConnectionMode | null;
|
|
99
|
+
direct: SerializedDirectState;
|
|
100
|
+
extension: SerializedExtensionState;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parameters for client initialization
|
|
104
|
+
* Unified interface with priority handling:
|
|
105
|
+
* - Explicit params (serverUrl, timeoutMs) take priority over savedState
|
|
106
|
+
* - selectedConnection: true → initialize client (get handle/set url), server stays 'not-initialized'
|
|
107
|
+
* - testConnection: true → also test backend server connectivity and update server state
|
|
108
|
+
*/
|
|
109
|
+
export interface InitParams {
|
|
110
|
+
savedState?: Record<string, unknown>;
|
|
111
|
+
selectedConnection?: boolean;
|
|
112
|
+
testConnection?: boolean;
|
|
113
|
+
serverUrl?: string;
|
|
114
|
+
timeoutMs?: number;
|
|
115
|
+
intervalMs?: number;
|
|
116
|
+
}
|
|
117
|
+
export declare function isExtensionState(state: ClientState): state is ExtensionState;
|
|
118
|
+
export declare function isDirectState(state: ClientState): state is DirectState;
|
|
119
|
+
export interface DirectStateNotInitialized {
|
|
120
|
+
type: 'direct';
|
|
121
|
+
server: BackendServerNotConnectedState;
|
|
122
|
+
}
|
|
123
|
+
export interface DirectStateInitialized {
|
|
124
|
+
type: 'direct';
|
|
125
|
+
url: string;
|
|
126
|
+
server: BackendServerNotConnectedState | BackendServerState;
|
|
127
|
+
}
|
|
128
|
+
export interface DirectStateReady {
|
|
129
|
+
type: 'direct';
|
|
130
|
+
url: string;
|
|
131
|
+
server: BackendServerReadyState;
|
|
132
|
+
}
|
|
133
|
+
export interface DirectStateNotReachable {
|
|
134
|
+
type: 'direct';
|
|
135
|
+
url: string;
|
|
136
|
+
server: BackendServerNotReachableState;
|
|
137
|
+
}
|
|
138
|
+
export interface DirectStateNotReady {
|
|
139
|
+
type: 'direct';
|
|
140
|
+
url: string;
|
|
141
|
+
server: BackendServerNotReadyState;
|
|
142
|
+
}
|
|
143
|
+
export type DirectState = DirectStateNotInitialized | DirectStateInitialized | DirectStateReady | DirectStateNotReachable | DirectStateNotReady;
|
|
144
|
+
export declare function isDirectServerReady(state: DirectState): state is DirectStateReady;
|
|
145
|
+
export declare function isDirectClientReady(state: DirectState): state is DirectStateInitialized | DirectStateReady | DirectStateNotReachable | DirectStateNotReady;
|
|
146
|
+
export declare const DIRECT_STATE_NOT_INITIALIZED: DirectStateNotInitialized;
|
|
147
|
+
export declare function createDirectStateReady(url: string, version?: string): DirectStateReady;
|
|
148
|
+
export declare function createDirectStateNotReachable(url: string): DirectStateNotReachable;
|
|
149
|
+
export declare function createDirectStateNotReady(url: string, server: BackendServerNotReadyState): DirectStateNotReady;
|
|
150
|
+
export declare const EXTENSION_STATE_NOT_INITIALIZED: ExtensionState;
|
|
151
|
+
export declare const EXTENSION_STATE_NOT_FOUND: ExtensionState;
|
|
152
|
+
export interface ExtensionStateNotInitialized {
|
|
153
|
+
type: 'extension';
|
|
154
|
+
extension: 'not-initialized';
|
|
155
|
+
server: PendingExtensionReadyState;
|
|
156
|
+
}
|
|
157
|
+
export interface ExtensionStateNotFound {
|
|
158
|
+
type: 'extension';
|
|
159
|
+
extension: 'not-found';
|
|
160
|
+
server: PendingExtensionReadyState;
|
|
161
|
+
}
|
|
162
|
+
export interface ExtensionStateReady {
|
|
163
|
+
type: 'extension';
|
|
164
|
+
extension: 'ready';
|
|
165
|
+
extensionId: string;
|
|
166
|
+
server: PendingExtensionReadyState | BackendServerState;
|
|
167
|
+
}
|
|
168
|
+
export type ExtensionState = ExtensionStateNotInitialized | ExtensionStateNotFound | ExtensionStateReady;
|
|
169
|
+
export declare function isExtensionServerReady(state: ExtensionState): state is ExtensionStateReady;
|
|
170
|
+
export declare function isExtensionClientReady(state: ExtensionState): state is ExtensionStateReady;
|
|
171
|
+
export declare function createExtensionStateNotInitialized(): ExtensionStateNotInitialized;
|
|
172
|
+
export declare function createExtensionStateNotFound(): ExtensionStateNotFound;
|
|
173
|
+
/**
|
|
174
|
+
* Check if client is ready (has handle/url) - does NOT require server ready
|
|
175
|
+
* Use this for auth checks and reload state checks
|
|
176
|
+
*/
|
|
177
|
+
export declare function isClientReady(state: ClientState): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Get backend server state from client state
|
|
180
|
+
*/
|
|
181
|
+
export declare function getBackendServerState(state: ClientState): BackendServerState | PendingExtensionReadyState | BackendServerNotConnectedState;
|
|
182
|
+
/**
|
|
183
|
+
* Safely get extension ID from client state
|
|
184
|
+
* @returns Extension ID if state is ExtensionStateReady, undefined otherwise
|
|
185
|
+
*/
|
|
186
|
+
export declare function getExtensionId(state: ClientState): string | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* Safely get server URL from client state
|
|
189
|
+
* @returns Server URL if state is DirectState with URL, undefined otherwise
|
|
190
|
+
*/
|
|
191
|
+
export declare function getServerUrl(state: ClientState): string | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { UserScope } from './user-info';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Log levels for client logging
|
|
5
|
+
*/
|
|
6
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
7
|
+
/**
|
|
8
|
+
* Extension discovery result
|
|
9
|
+
*/
|
|
10
|
+
export interface DiscoveryResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
extensionId?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Client configuration
|
|
17
|
+
*/
|
|
18
|
+
export interface ClientConfig {
|
|
19
|
+
authServerUrl?: string;
|
|
20
|
+
userScope?: UserScope;
|
|
21
|
+
extensionId?: string;
|
|
22
|
+
logLevel?: LogLevel;
|
|
23
|
+
discoveryAttempts?: number;
|
|
24
|
+
discoveryAttemptWaitMs?: number;
|
|
25
|
+
discoveryAttemptTimeout?: number;
|
|
26
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types used by both ext2ext and web2ext clients
|
|
3
|
+
*/
|
|
4
|
+
export { isApiResultError, isApiResultOperationError, isApiResultSuccess } from './api';
|
|
5
|
+
export type { ApiResponseResult } from './api';
|
|
6
|
+
export { createApiError, createOperationError } from '../errors';
|
|
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';
|
|
11
|
+
export type { ClientConfig, DiscoveryResult, LogLevel } from './config';
|
|
12
|
+
export type { BrowserInfo, OSInfo } from './platform';
|
|
13
|
+
export { NOOP_STATE_CALLBACK } from './callback';
|
|
14
|
+
export type { AuthStateChange, ClientStateChange, StateChange, StateChangeCallback, } from './callback';
|
|
15
|
+
export { buildError, buildEvent, buildResponse, handleRequest } from '../onboarding/protocol-utils';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BrowserType, OSType } from '../../../../setup-modal/src/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Browser detection result
|
|
5
|
+
*/
|
|
6
|
+
export interface BrowserInfo {
|
|
7
|
+
name: string;
|
|
8
|
+
type: BrowserType;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Operating system detection result
|
|
12
|
+
*/
|
|
13
|
+
export interface OSInfo {
|
|
14
|
+
name: string;
|
|
15
|
+
type: OSType;
|
|
16
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User and authentication related types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* User scope types for regular and power users
|
|
6
|
+
*/
|
|
7
|
+
export type UserScope = 'scope_user_user' | 'scope_user_power_user';
|
|
8
|
+
/**
|
|
9
|
+
* User information from OAuth
|
|
10
|
+
*/
|
|
11
|
+
export interface UserInfo {
|
|
12
|
+
sub: string;
|
|
13
|
+
email: string;
|
|
14
|
+
name: string;
|
|
15
|
+
given_name: string;
|
|
16
|
+
family_name: string;
|
|
17
|
+
preferred_username: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* OAuth tokens
|
|
21
|
+
*/
|
|
22
|
+
export interface Tokens {
|
|
23
|
+
accessToken: string;
|
|
24
|
+
refreshToken?: string;
|
|
25
|
+
idToken?: string;
|
|
26
|
+
expiresIn: number;
|
|
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;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for web2ext communication
|
|
3
|
+
*/
|
|
4
|
+
export declare const POLL_INTERVAL = 500;
|
|
5
|
+
export declare const POLL_TIMEOUT = 5000;
|
|
6
|
+
/**
|
|
7
|
+
* LocalStorage keys for OAuth tokens and PKCE flow (namespaced with 'bodhi:web' prefix)
|
|
8
|
+
*/
|
|
9
|
+
export declare const STORAGE_KEYS: import('../../core/src/index.ts').StorageKeys;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DirectClientBase, AuthLoggedIn, AuthLoggedOut, DirectClientBaseConfig, StateChangeCallback } from '../../core/src/index.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for DirectWebClient
|
|
5
|
+
*/
|
|
6
|
+
export interface DirectWebClientConfig extends DirectClientBaseConfig {
|
|
7
|
+
redirectUri: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* DirectWebClient - Web mode implementation using browser redirect OAuth
|
|
11
|
+
*/
|
|
12
|
+
export declare class DirectWebClient extends DirectClientBase {
|
|
13
|
+
private redirectUri;
|
|
14
|
+
constructor(config: DirectWebClientConfig, onStateChange?: StateChangeCallback);
|
|
15
|
+
login(): Promise<AuthLoggedIn>;
|
|
16
|
+
handleOAuthCallback(code: string, state: string): Promise<AuthLoggedIn>;
|
|
17
|
+
logout(): Promise<AuthLoggedOut>;
|
|
18
|
+
protected requestResourceAccess(): Promise<string>;
|
|
19
|
+
protected exchangeCodeForTokens(code: string): Promise<void>;
|
|
20
|
+
protected _storageGet(key: string): Promise<string | null>;
|
|
21
|
+
protected _storageSet(items: Record<string, string | number>): Promise<void>;
|
|
22
|
+
protected _storageRemove(keys: string[]): Promise<void>;
|
|
23
|
+
protected _getRedirectUri(): string;
|
|
24
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { ApiResponseResult, AuthLoggedIn, AuthLoggedOut, AuthState, BackendServerState, ClientState, ExtensionState, IExtensionClient, InitParams, StateChangeCallback } from '../../core/src/index.ts';
|
|
2
|
+
import { CreateChatCompletionStreamResponse } from '@bodhiapp/ts-client';
|
|
3
|
+
import { WebClientConfig } from './facade-client';
|
|
4
|
+
|
|
5
|
+
export type SerializedWebExtensionState = {
|
|
6
|
+
extensionId?: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* WindowBodhiextClient - web mode extension client using window.bodhiext
|
|
10
|
+
*
|
|
11
|
+
* Communicates with bodhi-browser-ext via window.bodhiext API
|
|
12
|
+
*
|
|
13
|
+
* Implements IExtensionClient interface with state callback for state changes
|
|
14
|
+
* Additionally provides handleOAuthCallback for web-specific OAuth flow
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
export declare class WindowBodhiextClient implements IExtensionClient {
|
|
18
|
+
private state;
|
|
19
|
+
private logger;
|
|
20
|
+
private bodhiext;
|
|
21
|
+
private authClientId;
|
|
22
|
+
private config;
|
|
23
|
+
private authEndpoints;
|
|
24
|
+
private onStateChange;
|
|
25
|
+
private refreshPromise;
|
|
26
|
+
constructor(authClientId: string, config: WebClientConfig, onStateChange?: StateChangeCallback);
|
|
27
|
+
/**
|
|
28
|
+
* Set client state and notify callback
|
|
29
|
+
*/
|
|
30
|
+
private setState;
|
|
31
|
+
/**
|
|
32
|
+
* Set auth state and notify callback
|
|
33
|
+
*/
|
|
34
|
+
private setAuthState;
|
|
35
|
+
/**
|
|
36
|
+
* Set or update the state change callback
|
|
37
|
+
*/
|
|
38
|
+
setStateCallback(callback: StateChangeCallback): void;
|
|
39
|
+
/**
|
|
40
|
+
* Ensure bodhiext is available, attempting to acquire it if not already set
|
|
41
|
+
* @throws Error if client not initialized
|
|
42
|
+
*/
|
|
43
|
+
private ensureBodhiext;
|
|
44
|
+
/**
|
|
45
|
+
* Send extension request via window.bodhiext.sendExtRequest
|
|
46
|
+
*/
|
|
47
|
+
sendExtRequest<TParams = void, TRes = unknown>(action: string, params?: TParams): Promise<TRes>;
|
|
48
|
+
/**
|
|
49
|
+
* Send API message via window.bodhiext.sendApiRequest
|
|
50
|
+
* Converts ApiResponse to ApiResponseResult
|
|
51
|
+
*/
|
|
52
|
+
sendApiRequest<TReq = void, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): Promise<ApiResponseResult<TRes>>;
|
|
53
|
+
/**
|
|
54
|
+
* Get current client state
|
|
55
|
+
*/
|
|
56
|
+
getState(): ClientState;
|
|
57
|
+
isClientInitialized(): boolean;
|
|
58
|
+
isServerReady(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Initialize extension discovery with optional timeout
|
|
61
|
+
* Returns ExtensionState with extension and server status
|
|
62
|
+
*
|
|
63
|
+
* Note: Web mode uses stateless discovery (always polls for window.bodhiext)
|
|
64
|
+
* No extensionId storage/restoration needed - window.bodhiext handle is ephemeral
|
|
65
|
+
*/
|
|
66
|
+
init(params?: InitParams): Promise<ExtensionState>;
|
|
67
|
+
/**
|
|
68
|
+
* Request resource access scope from backend
|
|
69
|
+
* Required for authenticated API access
|
|
70
|
+
*/
|
|
71
|
+
private requestResourceAccess;
|
|
72
|
+
/**
|
|
73
|
+
* Login via browser redirect OAuth2 + PKCE flow
|
|
74
|
+
* @returns AuthLoggedIn (though in practice, this redirects and never returns)
|
|
75
|
+
*/
|
|
76
|
+
login(): Promise<AuthLoggedIn>;
|
|
77
|
+
/**
|
|
78
|
+
* Handle OAuth callback with authorization code
|
|
79
|
+
* Should be called from callback page with extracted URL params
|
|
80
|
+
* @returns AuthLoggedIn with login state and user info
|
|
81
|
+
*/
|
|
82
|
+
handleOAuthCallback(code: string, state: string): Promise<AuthLoggedIn>;
|
|
83
|
+
/**
|
|
84
|
+
* Exchange authorization code for tokens
|
|
85
|
+
*/
|
|
86
|
+
private exchangeCodeForTokens;
|
|
87
|
+
/**
|
|
88
|
+
* Logout user and revoke tokens
|
|
89
|
+
* @returns AuthLoggedOut with logged out state
|
|
90
|
+
*/
|
|
91
|
+
logout(): Promise<AuthLoggedOut>;
|
|
92
|
+
/**
|
|
93
|
+
* Get current authentication state
|
|
94
|
+
*/
|
|
95
|
+
getAuthState(): Promise<AuthState>;
|
|
96
|
+
/**
|
|
97
|
+
* Get current access token
|
|
98
|
+
* Returns null if not logged in or token expired
|
|
99
|
+
*/
|
|
100
|
+
protected _getAccessTokenRaw(): Promise<string | null>;
|
|
101
|
+
/**
|
|
102
|
+
* Try to refresh access token using refresh token
|
|
103
|
+
* Race condition prevention: Returns existing promise if refresh already in progress
|
|
104
|
+
*/
|
|
105
|
+
private _tryRefreshToken;
|
|
106
|
+
/**
|
|
107
|
+
* Perform the actual token refresh
|
|
108
|
+
*/
|
|
109
|
+
private _doRefreshToken;
|
|
110
|
+
/**
|
|
111
|
+
* Store refreshed tokens
|
|
112
|
+
*/
|
|
113
|
+
private _storeRefreshedTokens;
|
|
114
|
+
/**
|
|
115
|
+
* Ping API
|
|
116
|
+
*/
|
|
117
|
+
pingApi(): Promise<ApiResponseResult<{
|
|
118
|
+
message: string;
|
|
119
|
+
}>>;
|
|
120
|
+
/**
|
|
121
|
+
* Fetch models
|
|
122
|
+
*/
|
|
123
|
+
fetchModels(): Promise<ApiResponseResult<{
|
|
124
|
+
data: Array<{
|
|
125
|
+
id: string;
|
|
126
|
+
object: string;
|
|
127
|
+
}>;
|
|
128
|
+
}>>;
|
|
129
|
+
/**
|
|
130
|
+
* Get backend server state
|
|
131
|
+
* Calls /bodhi/v1/info and returns structured server state
|
|
132
|
+
*/
|
|
133
|
+
getServerState(): Promise<BackendServerState>;
|
|
134
|
+
/**
|
|
135
|
+
* Generic streaming via window.bodhiext.sendStreamRequest
|
|
136
|
+
* Wraps ReadableStream as AsyncGenerator
|
|
137
|
+
*/
|
|
138
|
+
stream<TReq = unknown, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): AsyncGenerator<TRes>;
|
|
139
|
+
/**
|
|
140
|
+
* Chat streaming
|
|
141
|
+
*/
|
|
142
|
+
streamChat(model: string, prompt: string, authenticated?: boolean): AsyncGenerator<CreateChatCompletionStreamResponse>;
|
|
143
|
+
/**
|
|
144
|
+
* Serialize web extension client state (all transient, nothing to persist)
|
|
145
|
+
*/
|
|
146
|
+
serialize(): SerializedWebExtensionState;
|
|
147
|
+
/**
|
|
148
|
+
* Debug dump of WindowBodhiextClient internal state
|
|
149
|
+
*/
|
|
150
|
+
debug(): Promise<Record<string, unknown>>;
|
|
151
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BaseFacadeClient, Logger, AuthLoggedIn, IWebUIClient, LogLevel, StateChange, StateChangeCallback, UserScope } from '../../core/src/index.ts';
|
|
2
|
+
import { DirectWebClient } from './direct-client';
|
|
3
|
+
import { WindowBodhiextClient } from './ext-client';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for WebClient OAuth
|
|
7
|
+
*/
|
|
8
|
+
export interface WebClientConfig {
|
|
9
|
+
authServerUrl: string;
|
|
10
|
+
redirectUri: string;
|
|
11
|
+
userScope: UserScope;
|
|
12
|
+
logLevel: LogLevel;
|
|
13
|
+
initParams?: {
|
|
14
|
+
extension?: {
|
|
15
|
+
timeoutMs?: number;
|
|
16
|
+
intervalMs?: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* WebUIClient - Public facade for web mode
|
|
22
|
+
*
|
|
23
|
+
* Automatically switches between DirectClient and InternalWebUIClient
|
|
24
|
+
* based on stored user preferences.
|
|
25
|
+
*/
|
|
26
|
+
export declare class WebUIClient extends BaseFacadeClient<WebClientConfig, WindowBodhiextClient, DirectWebClient> implements IWebUIClient {
|
|
27
|
+
constructor(authClientId: string, config: {
|
|
28
|
+
redirectUri: string;
|
|
29
|
+
authServerUrl?: string;
|
|
30
|
+
userScope?: UserScope;
|
|
31
|
+
logLevel?: LogLevel;
|
|
32
|
+
initParams?: {
|
|
33
|
+
extension?: {
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
intervalMs?: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}, onStateChange?: StateChangeCallback, storagePrefix?: string);
|
|
39
|
+
protected createLogger(config: WebClientConfig): Logger;
|
|
40
|
+
protected createExtClient(config: WebClientConfig, onStateChange: (change: StateChange) => void): WindowBodhiextClient;
|
|
41
|
+
protected createDirectClient(authClientId: string, config: WebClientConfig, onStateChange: (change: StateChange) => void): DirectWebClient;
|
|
42
|
+
handleOAuthCallback(code: string, state: string): Promise<AuthLoggedIn>;
|
|
43
|
+
}
|