@bodhiapp/bodhi-js-react-ext 0.0.11
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/dist/bodhi-browser-ext/src/types/bodhiext.d.ts +201 -0
- package/dist/bodhi-browser-ext/src/types/common.d.ts +37 -0
- package/dist/bodhi-browser-ext/src/types/index.d.ts +6 -0
- package/dist/bodhi-browser-ext/src/types/protocol.d.ts +222 -0
- package/dist/bodhi-js-sdk/react-ext/src/BodhiProvider.d.ts +8 -0
- package/dist/bodhi-js-sdk/react-ext/src/api/index.d.ts +7 -0
- package/dist/bodhi-js-sdk/react-ext/src/build-info.d.ts +1 -0
- package/dist/bodhi-js-sdk/react-ext/src/index.d.ts +10 -0
- package/dist/bodhi-react-ext.cjs.js +1 -0
- package/dist/bodhi-react-ext.esm.d.ts +2 -0
- package/dist/bodhi-react-ext.esm.js +50 -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 +168 -0
- package/dist/setup-modal/src/types/platform.d.ts +32 -0
- package/dist/setup-modal/src/types/protocol.d.ts +70 -0
- package/dist/setup-modal/src/types/server.d.ts +63 -0
- package/dist/setup-modal/src/types/state.d.ts +42 -0
- package/dist/setup-modal/src/types/type-guards.d.ts +26 -0
- package/package.json +67 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { OpenAiApiError, PingResponse, CreateChatCompletionRequest, CreateChatCompletionResponse, CreateChatCompletionStreamResponse } from '@bodhiapp/ts-client';
|
|
2
|
+
/**
|
|
3
|
+
* HTTP response wrapper - body can be success type OR error type
|
|
4
|
+
* Use isApiErrorResponse() to narrow the type based on status
|
|
5
|
+
*/
|
|
6
|
+
export interface ApiResponse<T = unknown> {
|
|
7
|
+
body: T | OpenAiApiError;
|
|
8
|
+
status: number;
|
|
9
|
+
headers: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Stream chunk returned by sendStreamRequest
|
|
13
|
+
*
|
|
14
|
+
* This is the chunk structure yielded by the ReadableStream from sendStreamRequest.
|
|
15
|
+
* Different from StreamChunkMessage which is the internal message wrapper.
|
|
16
|
+
*/
|
|
17
|
+
export interface StreamChunk {
|
|
18
|
+
body: unknown;
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
status?: number;
|
|
21
|
+
done?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Server state information returned by /bodhi/v1/info endpoint
|
|
25
|
+
*/
|
|
26
|
+
export interface ServerStateInfo {
|
|
27
|
+
/** Current application status */
|
|
28
|
+
status: 'setup' | 'ready' | 'resource-admin' | 'error' | 'unreachable';
|
|
29
|
+
/** Application version */
|
|
30
|
+
version?: string;
|
|
31
|
+
/** Server URL (added by extension) */
|
|
32
|
+
url?: string;
|
|
33
|
+
/** Error details if status is 'error' or 'unreachable' */
|
|
34
|
+
error?: {
|
|
35
|
+
message: string;
|
|
36
|
+
type?: string;
|
|
37
|
+
code?: string;
|
|
38
|
+
param?: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* API error thrown when server returns HTTP 4xx/5xx
|
|
43
|
+
* Used for streaming responses (non-streaming returns ApiResponse)
|
|
44
|
+
*/
|
|
45
|
+
export interface ApiError extends Error {
|
|
46
|
+
response: {
|
|
47
|
+
status: number;
|
|
48
|
+
body: OpenAiApiError;
|
|
49
|
+
headers?: Record<string, string>;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Operation error thrown when HTTP request couldn't complete
|
|
54
|
+
* (network unreachable, timeout, extension error)
|
|
55
|
+
*/
|
|
56
|
+
export interface OperationError extends Error {
|
|
57
|
+
error: {
|
|
58
|
+
message: string;
|
|
59
|
+
type: string;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Union of all extension-thrown errors
|
|
64
|
+
*/
|
|
65
|
+
export type ExtensionError = ApiError | OperationError;
|
|
66
|
+
/**
|
|
67
|
+
* Type guard: API error (has response field)
|
|
68
|
+
*/
|
|
69
|
+
export declare function isApiError(err: unknown): err is ApiError;
|
|
70
|
+
/**
|
|
71
|
+
* Type guard: Operation error (has error field, no response)
|
|
72
|
+
*/
|
|
73
|
+
export declare function isOperationError(err: unknown): err is OperationError;
|
|
74
|
+
/**
|
|
75
|
+
* Chat API uses types from @bodhiapp/ts-client.
|
|
76
|
+
* Consumers should import these types directly from @bodhiapp/ts-client:
|
|
77
|
+
* - ChatCompletionRequestMessage
|
|
78
|
+
* - ChatCompletionResponseMessage
|
|
79
|
+
* - CreateChatCompletionRequest
|
|
80
|
+
* - CreateChatCompletionResponse
|
|
81
|
+
* - CreateChatCompletionStreamResponse
|
|
82
|
+
* - ChatChoice
|
|
83
|
+
* - ChatChoiceStream
|
|
84
|
+
* - ChatCompletionStreamResponseDelta
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* Chat completions API interface
|
|
88
|
+
*/
|
|
89
|
+
export interface ChatCompletionsApi {
|
|
90
|
+
/**
|
|
91
|
+
* Create a chat completion
|
|
92
|
+
*
|
|
93
|
+
* Non-streaming: Returns ApiResponse - caller checks status for success/error
|
|
94
|
+
* Streaming: Yields chunks via AsyncIterable - throws ApiError or OperationError on error
|
|
95
|
+
*
|
|
96
|
+
* @param params - Chat completion parameters
|
|
97
|
+
* @returns Non-streaming: Promise<ApiResponse<CreateChatCompletionResponse>>, Streaming: AsyncIterable<CreateChatCompletionStreamResponse>
|
|
98
|
+
*/
|
|
99
|
+
create(params: CreateChatCompletionRequest & {
|
|
100
|
+
stream?: false;
|
|
101
|
+
}): Promise<ApiResponse<CreateChatCompletionResponse>>;
|
|
102
|
+
create(params: CreateChatCompletionRequest & {
|
|
103
|
+
stream: true;
|
|
104
|
+
}): AsyncIterable<CreateChatCompletionStreamResponse>;
|
|
105
|
+
create(params: CreateChatCompletionRequest): Promise<ApiResponse<CreateChatCompletionResponse>> | AsyncIterable<CreateChatCompletionStreamResponse>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Chat API namespace
|
|
109
|
+
*/
|
|
110
|
+
export interface ChatApi {
|
|
111
|
+
completions: ChatCompletionsApi;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Public window.bodhiext interface
|
|
115
|
+
*
|
|
116
|
+
* This interface defines all methods available to web pages through window.bodhiext.
|
|
117
|
+
* The extension creates this interface in inject.ts and attaches it to the window object.
|
|
118
|
+
*/
|
|
119
|
+
export interface BodhiExtPublicApi {
|
|
120
|
+
/**
|
|
121
|
+
* Send a generic API request through the extension to the backend server.
|
|
122
|
+
*
|
|
123
|
+
* @template TReq - Request body type (inferred from body parameter)
|
|
124
|
+
* @template TRes - Response body type (must be specified explicitly)
|
|
125
|
+
* @param method - HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
126
|
+
* @param endpoint - API endpoint path (e.g., '/v1/chat/completions')
|
|
127
|
+
* @param body - Optional request body (will be JSON stringified)
|
|
128
|
+
* @param headers - Optional additional headers
|
|
129
|
+
* @returns Promise resolving to ApiResponse with body, headers, and status
|
|
130
|
+
*/
|
|
131
|
+
sendApiRequest<TReq = unknown, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>): Promise<ApiResponse<TRes>>;
|
|
132
|
+
/**
|
|
133
|
+
* Send a streaming API request through the extension.
|
|
134
|
+
*
|
|
135
|
+
* Used for SSE (Server-Sent Events) endpoints like streaming chat completions.
|
|
136
|
+
* Returns a ReadableStream that yields StreamChunk objects.
|
|
137
|
+
*
|
|
138
|
+
* @template TReq - Request body type (inferred from body parameter)
|
|
139
|
+
* @param method - HTTP method (typically POST for streaming)
|
|
140
|
+
* @param endpoint - API endpoint path (e.g., '/v1/chat/completions')
|
|
141
|
+
* @param body - Optional request body
|
|
142
|
+
* @param headers - Optional additional headers
|
|
143
|
+
* @returns ReadableStream yielding StreamChunk objects
|
|
144
|
+
*/
|
|
145
|
+
sendStreamRequest<TReq = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>): ReadableStream<StreamChunk>;
|
|
146
|
+
/**
|
|
147
|
+
* Send a generic extension request.
|
|
148
|
+
*
|
|
149
|
+
* Used for extension-specific operations like test_connection, get_extension_id, etc.
|
|
150
|
+
* This is a low-level API for extension capabilities.
|
|
151
|
+
*
|
|
152
|
+
* @param action - Extension action name (e.g., 'test_connection')
|
|
153
|
+
* @param params - Optional action parameters
|
|
154
|
+
* @returns Promise resolving to action-specific response
|
|
155
|
+
*/
|
|
156
|
+
sendExtRequest(action: string, params?: any): Promise<any>;
|
|
157
|
+
/**
|
|
158
|
+
* Simple health check to verify extension connectivity.
|
|
159
|
+
*
|
|
160
|
+
* Returns ApiResponse - caller should check status to determine success/error.
|
|
161
|
+
* On success (2xx), body is PingResponse. On error (4xx/5xx), body is OpenAiApiError.
|
|
162
|
+
*
|
|
163
|
+
* @returns Promise resolving to ApiResponse<PingResponse>
|
|
164
|
+
*/
|
|
165
|
+
ping(): Promise<ApiResponse<PingResponse>>;
|
|
166
|
+
/**
|
|
167
|
+
* Get server state information from /bodhi/v1/info endpoint.
|
|
168
|
+
*
|
|
169
|
+
* Returns the current status of the backend server including
|
|
170
|
+
* whether it's in setup mode, ready, or has errors.
|
|
171
|
+
*
|
|
172
|
+
* @returns Promise resolving to ServerStateInfo
|
|
173
|
+
*/
|
|
174
|
+
serverState(): Promise<ServerStateInfo>;
|
|
175
|
+
/**
|
|
176
|
+
* OpenAI-compatible chat API.
|
|
177
|
+
*
|
|
178
|
+
* Provides chat completion functionality compatible with OpenAI's API structure.
|
|
179
|
+
*/
|
|
180
|
+
chat: ChatApi;
|
|
181
|
+
/**
|
|
182
|
+
* Get the extension ID.
|
|
183
|
+
*
|
|
184
|
+
* The extension ID is fetched asynchronously during initialization.
|
|
185
|
+
* This method returns a promise that resolves to the extension ID once available.
|
|
186
|
+
*
|
|
187
|
+
* @returns Promise resolving to the extension ID string
|
|
188
|
+
*/
|
|
189
|
+
getExtensionId(): Promise<string>;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Window augmentation for TypeScript
|
|
193
|
+
*
|
|
194
|
+
* Declares the optional bodhiext property on the Window interface.
|
|
195
|
+
* This allows TypeScript code to access window.bodhiext with proper typing.
|
|
196
|
+
*/
|
|
197
|
+
declare global {
|
|
198
|
+
interface Window {
|
|
199
|
+
bodhiext?: BodhiExtPublicApi;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared constants for Bodhi Browser Extension
|
|
3
|
+
*
|
|
4
|
+
* Constants used by both inject.ts and background/content.ts scripts,
|
|
5
|
+
* or common to the entire extension.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CONTENT_TYPE_JSON = "application/json";
|
|
8
|
+
export declare const CONTENT_TYPE_EVENT_STREAM = "text/event-stream";
|
|
9
|
+
export declare const CONTENT_TYPE_HEADER = "Content-Type";
|
|
10
|
+
export declare const HTTP_METHOD_GET = "GET";
|
|
11
|
+
export declare const HTTP_METHOD_POST = "POST";
|
|
12
|
+
export declare const ENDPOINT_PING = "/ping";
|
|
13
|
+
export declare const ENDPOINT_CHAT_COMPLETIONS = "/v1/chat/completions";
|
|
14
|
+
export declare const DEFAULT_API_BASE_URL = "http://localhost:1135";
|
|
15
|
+
export declare const DEFAULT_API_TIMEOUT = 10000;
|
|
16
|
+
export declare const DEFAULT_STREAM_TIMEOUT = 60000;
|
|
17
|
+
export declare const STORAGE_KEY_BACKEND_URL = "backendUrl";
|
|
18
|
+
export declare const SSE_DONE_MARKER = "[DONE]";
|
|
19
|
+
export declare const SSE_DATA_PREFIX = "data: ";
|
|
20
|
+
export declare const SSE_CHUNK_DELIMITER = "\n\n";
|
|
21
|
+
export declare const EXT_ACTIONS: {
|
|
22
|
+
readonly GET_EXTENSION_ID: "get_extension_id";
|
|
23
|
+
readonly TEST_CONNECTION: "test_connection";
|
|
24
|
+
};
|
|
25
|
+
export declare const ERROR_TYPES: {
|
|
26
|
+
readonly NETWORK_ERROR: "network_error";
|
|
27
|
+
readonly EXTENSION_ERROR: "extension_error";
|
|
28
|
+
readonly TIMEOUT_ERROR: "timeout_error";
|
|
29
|
+
readonly AUTH_ERROR: "auth_error";
|
|
30
|
+
};
|
|
31
|
+
export type ConnectionErrorType = (typeof ERROR_TYPES)[keyof typeof ERROR_TYPES];
|
|
32
|
+
export declare const DOCUMENT_STATE_COMPLETE = "complete";
|
|
33
|
+
export declare const EVENT_INITIALIZED = "bodhiext:initialized";
|
|
34
|
+
export declare const BODHI_STREAM_PORT = "BODHI_STREAM_PORT";
|
|
35
|
+
export declare const ORIGIN_WILDCARD = "*";
|
|
36
|
+
export declare const ERROR_MISSING_REQUEST_ID = "Invalid message format: missing requestId or request";
|
|
37
|
+
export declare const ERROR_CONNECTION_CLOSED = "Connection closed unexpectedly";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { ApiResponse, StreamChunk, ServerStateInfo, ApiError, OperationError, ExtensionError, ChatCompletionsApi, ChatApi, BodhiExtPublicApi } from './bodhiext';
|
|
2
|
+
export { isApiError, isOperationError } from './bodhiext';
|
|
3
|
+
export type { ApiRequest, ApiRequestMessage, OperationErrorResponse, ApiResponseSuccessMessage, OperationErrorResponseMessage, ApiResponseMessage, ErrorMessage, StreamChunkMessage, StreamApiErrorMessage, StreamErrorMessage, StreamMessage, StreamController, SSEChunk, ExtRequest, ExtRequestMessage, ExtError, ExtErrorResponse, ExtResponse, ExtResponseMessage, GetExtensionIdRequest, GetExtensionIdResponse, TestConnectionRequest, TestConnectionResponse, } from './protocol';
|
|
4
|
+
export { MESSAGE_TYPES, isOperationErrorResponse, isApiErrorResponse, isApiSuccessResponse, isStreamChunk, isStreamApiError, isStreamError, isExtError, isOpenAiApiErrorBody, isOperationErrorStructure, } from './protocol';
|
|
5
|
+
export { CONTENT_TYPE_JSON, CONTENT_TYPE_EVENT_STREAM, CONTENT_TYPE_HEADER, HTTP_METHOD_GET, HTTP_METHOD_POST, ENDPOINT_PING, ENDPOINT_CHAT_COMPLETIONS, DEFAULT_API_BASE_URL, DEFAULT_API_TIMEOUT, DEFAULT_STREAM_TIMEOUT, STORAGE_KEY_BACKEND_URL, SSE_DONE_MARKER, SSE_DATA_PREFIX, SSE_CHUNK_DELIMITER, EXT_ACTIONS, ERROR_TYPES, DOCUMENT_STATE_COMPLETE, EVENT_INITIALIZED, BODHI_STREAM_PORT, ORIGIN_WILDCARD, ERROR_MISSING_REQUEST_ID, ERROR_CONNECTION_CLOSED, } from './common';
|
|
6
|
+
export type { ConnectionErrorType } from './common';
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { OpenAiApiError, ErrorBody } from '@bodhiapp/ts-client';
|
|
2
|
+
import { ApiResponse, ServerStateInfo } from './bodhiext';
|
|
3
|
+
/**
|
|
4
|
+
* Validate OpenAI API error body structure
|
|
5
|
+
* { error: { message: string, type: string } }
|
|
6
|
+
*/
|
|
7
|
+
export declare function isOpenAiApiErrorBody(body: unknown): body is OpenAiApiError;
|
|
8
|
+
/**
|
|
9
|
+
* Validate OperationErrorResponse structure
|
|
10
|
+
* { message: string, type: string }
|
|
11
|
+
*/
|
|
12
|
+
export declare function isOperationErrorStructure(obj: unknown): obj is OperationErrorResponse;
|
|
13
|
+
export declare const MESSAGE_TYPES: {
|
|
14
|
+
readonly API_REQUEST: "BODHI_API_REQUEST";
|
|
15
|
+
readonly API_RESPONSE: "BODHI_API_RESPONSE";
|
|
16
|
+
readonly STREAM_REQUEST: "BODHI_STREAM_REQUEST";
|
|
17
|
+
readonly STREAM_CHUNK: "BODHI_STREAM_CHUNK";
|
|
18
|
+
readonly STREAM_ERROR: "BODHI_STREAM_ERROR";
|
|
19
|
+
readonly STREAM_API_ERROR: "BODHI_STREAM_API_ERROR";
|
|
20
|
+
readonly ERROR: "BODHI_ERROR";
|
|
21
|
+
readonly EXT_REQUEST: "BODHI_EXT_REQUEST";
|
|
22
|
+
readonly EXT_RESPONSE: "BODHI_EXT_RESPONSE";
|
|
23
|
+
};
|
|
24
|
+
export interface ApiRequest<T = unknown> {
|
|
25
|
+
method: string;
|
|
26
|
+
endpoint: string;
|
|
27
|
+
body?: T;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
}
|
|
30
|
+
export interface ApiRequestMessage<TReq = unknown> {
|
|
31
|
+
type: string;
|
|
32
|
+
requestId: string;
|
|
33
|
+
request: ApiRequest<TReq>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Operation-level error response (network unreachable, timeout, extension error)
|
|
37
|
+
* NOT an API error (those come through ApiResponse with OpenAiApiError body)
|
|
38
|
+
* This is a response type, not a thrown error
|
|
39
|
+
*/
|
|
40
|
+
export interface OperationErrorResponse {
|
|
41
|
+
message: string;
|
|
42
|
+
type: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Success API response message (HTTP request completed, regardless of status code)
|
|
46
|
+
*/
|
|
47
|
+
export interface ApiResponseSuccessMessage<T = unknown> {
|
|
48
|
+
type: string;
|
|
49
|
+
requestId: string;
|
|
50
|
+
response: ApiResponse<T>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Operation error response message - HTTP request couldn't complete
|
|
54
|
+
*/
|
|
55
|
+
export interface OperationErrorResponseMessage {
|
|
56
|
+
type: string;
|
|
57
|
+
requestId: string;
|
|
58
|
+
error: OperationErrorResponse;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* API response message - discriminated union
|
|
62
|
+
*/
|
|
63
|
+
export type ApiResponseMessage<T = unknown> = ApiResponseSuccessMessage<T> | OperationErrorResponseMessage;
|
|
64
|
+
/**
|
|
65
|
+
* Type guard for operation error response
|
|
66
|
+
*/
|
|
67
|
+
export declare function isOperationErrorResponse(msg: ApiResponseMessage): msg is OperationErrorResponseMessage;
|
|
68
|
+
/**
|
|
69
|
+
* Type guard to check if response is an API error (4xx/5xx)
|
|
70
|
+
* Narrows body type to OpenAiApiError
|
|
71
|
+
*/
|
|
72
|
+
export declare function isApiErrorResponse<T>(response: ApiResponse<T>): response is ApiResponse<T> & {
|
|
73
|
+
body: OpenAiApiError;
|
|
74
|
+
status: number;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Type guard to check if response is successful (2xx)
|
|
78
|
+
* Narrows body type to T
|
|
79
|
+
*/
|
|
80
|
+
export declare function isApiSuccessResponse<T>(response: ApiResponse<T>): response is ApiResponse<T> & {
|
|
81
|
+
body: T;
|
|
82
|
+
status: number;
|
|
83
|
+
};
|
|
84
|
+
export interface ErrorMessage {
|
|
85
|
+
type: string;
|
|
86
|
+
requestId: string;
|
|
87
|
+
response: {
|
|
88
|
+
body: ErrorBody;
|
|
89
|
+
status: number;
|
|
90
|
+
headers: Record<string, string>;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Stream chunk message - successful SSE chunk received
|
|
95
|
+
* Uses ApiResponse<T> wrapper for consistency with non-streaming pattern
|
|
96
|
+
*/
|
|
97
|
+
export interface StreamChunkMessage<T = unknown> {
|
|
98
|
+
type: typeof MESSAGE_TYPES.STREAM_CHUNK;
|
|
99
|
+
requestId: string;
|
|
100
|
+
response: ApiResponse<T>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Stream API error message - server returned error response (not SSE)
|
|
104
|
+
* E.g., 400/401/500 JSON error instead of SSE stream
|
|
105
|
+
*/
|
|
106
|
+
export interface StreamApiErrorMessage {
|
|
107
|
+
type: typeof MESSAGE_TYPES.STREAM_API_ERROR;
|
|
108
|
+
requestId: string;
|
|
109
|
+
response: ApiResponse<OpenAiApiError>;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Stream error message - network/extension level error
|
|
113
|
+
* E.g., connection refused, timeout, extension error
|
|
114
|
+
*/
|
|
115
|
+
export interface StreamErrorMessage {
|
|
116
|
+
type: typeof MESSAGE_TYPES.STREAM_ERROR;
|
|
117
|
+
requestId: string;
|
|
118
|
+
error: OperationErrorResponse;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Union type for all streaming messages
|
|
122
|
+
*/
|
|
123
|
+
export type StreamMessage<T = unknown> = StreamChunkMessage<T> | StreamApiErrorMessage | StreamErrorMessage;
|
|
124
|
+
/**
|
|
125
|
+
* Type guard for stream chunk message
|
|
126
|
+
*/
|
|
127
|
+
export declare function isStreamChunk<T>(msg: StreamMessage<T>): msg is StreamChunkMessage<T>;
|
|
128
|
+
/**
|
|
129
|
+
* Type guard for stream API error
|
|
130
|
+
*/
|
|
131
|
+
export declare function isStreamApiError(msg: StreamMessage): msg is StreamApiErrorMessage;
|
|
132
|
+
/**
|
|
133
|
+
* Type guard for stream error
|
|
134
|
+
*/
|
|
135
|
+
export declare function isStreamError(msg: StreamMessage): msg is StreamErrorMessage;
|
|
136
|
+
/**
|
|
137
|
+
* Interface for stream controller to handle SSE responses
|
|
138
|
+
*/
|
|
139
|
+
export interface StreamController {
|
|
140
|
+
enqueue: (chunk: any) => void;
|
|
141
|
+
error: (err: Error) => void;
|
|
142
|
+
complete: () => void;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Interface for SSE data chunk
|
|
146
|
+
*/
|
|
147
|
+
export interface SSEChunk {
|
|
148
|
+
done?: boolean;
|
|
149
|
+
[key: string]: any;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Generic extension request interface
|
|
153
|
+
*/
|
|
154
|
+
export interface ExtRequest {
|
|
155
|
+
action: string;
|
|
156
|
+
params?: any;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Generic extension request message
|
|
160
|
+
*/
|
|
161
|
+
export interface ExtRequestMessage {
|
|
162
|
+
type: 'BODHI_EXT_REQUEST';
|
|
163
|
+
requestId: string;
|
|
164
|
+
request: ExtRequest;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Extension-level error structure
|
|
168
|
+
*/
|
|
169
|
+
export interface ExtError {
|
|
170
|
+
message: string;
|
|
171
|
+
type?: string;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Error response for extension operations
|
|
175
|
+
*/
|
|
176
|
+
export interface ExtErrorResponse {
|
|
177
|
+
error: ExtError;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Union type for extension response (flattened - no wrapper for success)
|
|
181
|
+
* Success: T (the actual response data)
|
|
182
|
+
* Error: { error: ExtError }
|
|
183
|
+
*/
|
|
184
|
+
export type ExtResponse<T = unknown> = T | ExtErrorResponse;
|
|
185
|
+
/**
|
|
186
|
+
* Type guard to check if extension response is an error
|
|
187
|
+
*/
|
|
188
|
+
export declare function isExtError<T>(res: ExtResponse<T>): res is ExtErrorResponse;
|
|
189
|
+
/**
|
|
190
|
+
* Generic extension response message
|
|
191
|
+
*/
|
|
192
|
+
export interface ExtResponseMessage<T = unknown> {
|
|
193
|
+
type: 'BODHI_EXT_RESPONSE';
|
|
194
|
+
requestId: string;
|
|
195
|
+
response: ExtResponse<T>;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Get Extension ID request (no params needed)
|
|
199
|
+
*/
|
|
200
|
+
export interface GetExtensionIdRequest extends ExtRequest {
|
|
201
|
+
action: 'get_extension_id';
|
|
202
|
+
params?: undefined;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Get Extension ID response body
|
|
206
|
+
*/
|
|
207
|
+
export interface GetExtensionIdResponse {
|
|
208
|
+
extension_id: string;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Test Connection request
|
|
212
|
+
*/
|
|
213
|
+
export interface TestConnectionRequest extends ExtRequest {
|
|
214
|
+
action: 'test_connection';
|
|
215
|
+
params: {
|
|
216
|
+
url: string;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Test Connection response body (reuses ServerStateInfo)
|
|
221
|
+
*/
|
|
222
|
+
export type TestConnectionResponse = ServerStateInfo;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ExtUIClientParams } from '../../ext/src/index.ts';
|
|
2
|
+
import { BodhiProviderProps as CoreBodhiProviderProps, UIClient } from '../../react-core/src/index.ts';
|
|
3
|
+
export interface BodhiProviderProps extends Omit<CoreBodhiProviderProps, 'client'> {
|
|
4
|
+
authClientId?: string;
|
|
5
|
+
clientConfig?: ExtUIClientParams;
|
|
6
|
+
client?: UIClient;
|
|
7
|
+
}
|
|
8
|
+
export declare function BodhiProvider({ children, authClientId, clientConfig, client: providedClient, basePath, ...restProps }: BodhiProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const BUILD_MODE: string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bodhiapp/bodhi-js-react-ext - React bindings for Bodhi Browser SDK (Extension preset)
|
|
3
|
+
*
|
|
4
|
+
* Preset package that auto-creates ExtUIClient for simplified developer experience in Chrome extensions.
|
|
5
|
+
* For advanced usage with custom client configuration, use @bodhiapp/bodhi-js-react-core.
|
|
6
|
+
*/
|
|
7
|
+
export { BodhiProvider, type BodhiProviderProps } from './BodhiProvider';
|
|
8
|
+
export { ExtUIClient, type ExtUIClientParams } from '../../ext/src/index.ts';
|
|
9
|
+
export { BodhiReactContext, useBodhi, type BodhiContext, type SetupState, type ClientContextState, type ClientContextStatus, INITIAL_CLIENT_CONTEXT_STATE, INITIALIZING_CLIENT_CONTEXT_STATE, ClientCtxState, clientStateToContextState, isClientCtxInitialized, isClientCtxInitializing, isClientCtxNotInitialized, isClientCtxReady, isOverallReady, type ApiResponseResult, type ClientState, type AuthState, type UIClient, type OperationError, isApiResultError, isApiResultOperationError, isApiResultSuccess, isDirectState, isExtensionState, isWebUIClient, isAuthError, isAuthLoading, isAuthenticated, isClientReady, isOperationError, createApiError, createOperationError, } from '../../react-core/src/index.ts';
|
|
10
|
+
export { BUILD_MODE as REACT_EXT_BUILD_MODE } from './build-info';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("react/jsx-runtime"),o=require("react"),a=require("@bodhiapp/bodhi-js-ext"),e=require("@bodhiapp/bodhi-js-react-core");function p({children:l,authClientId:i,clientConfig:n,client:t,basePath:u,...s}){if(!t&&!i)throw new Error('BodhiProvider requires either "client" or "authClientId" prop');const r=o.useRef(null),c=o.useMemo(()=>{if(t)return t;if(r.current)return r.current;const b={...n,basePath:n?.basePath??u};return r.current=new a.ExtUIClient(i,b),r.current},[t,i,n,u]);return d.jsx(e.BodhiProvider,{client:c,basePath:u,...s,children:l})}const O="production";Object.defineProperty(exports,"ExtUIClient",{enumerable:!0,get:()=>a.ExtUIClient});Object.defineProperty(exports,"BodhiReactContext",{enumerable:!0,get:()=>e.BodhiReactContext});Object.defineProperty(exports,"ClientCtxState",{enumerable:!0,get:()=>e.ClientCtxState});Object.defineProperty(exports,"INITIALIZING_CLIENT_CONTEXT_STATE",{enumerable:!0,get:()=>e.INITIALIZING_CLIENT_CONTEXT_STATE});Object.defineProperty(exports,"INITIAL_CLIENT_CONTEXT_STATE",{enumerable:!0,get:()=>e.INITIAL_CLIENT_CONTEXT_STATE});Object.defineProperty(exports,"clientStateToContextState",{enumerable:!0,get:()=>e.clientStateToContextState});Object.defineProperty(exports,"createApiError",{enumerable:!0,get:()=>e.createApiError});Object.defineProperty(exports,"createOperationError",{enumerable:!0,get:()=>e.createOperationError});Object.defineProperty(exports,"isApiResultError",{enumerable:!0,get:()=>e.isApiResultError});Object.defineProperty(exports,"isApiResultOperationError",{enumerable:!0,get:()=>e.isApiResultOperationError});Object.defineProperty(exports,"isApiResultSuccess",{enumerable:!0,get:()=>e.isApiResultSuccess});Object.defineProperty(exports,"isAuthError",{enumerable:!0,get:()=>e.isAuthError});Object.defineProperty(exports,"isAuthLoading",{enumerable:!0,get:()=>e.isAuthLoading});Object.defineProperty(exports,"isAuthenticated",{enumerable:!0,get:()=>e.isAuthenticated});Object.defineProperty(exports,"isClientCtxInitialized",{enumerable:!0,get:()=>e.isClientCtxInitialized});Object.defineProperty(exports,"isClientCtxInitializing",{enumerable:!0,get:()=>e.isClientCtxInitializing});Object.defineProperty(exports,"isClientCtxNotInitialized",{enumerable:!0,get:()=>e.isClientCtxNotInitialized});Object.defineProperty(exports,"isClientCtxReady",{enumerable:!0,get:()=>e.isClientCtxReady});Object.defineProperty(exports,"isClientReady",{enumerable:!0,get:()=>e.isClientReady});Object.defineProperty(exports,"isDirectState",{enumerable:!0,get:()=>e.isDirectState});Object.defineProperty(exports,"isExtensionState",{enumerable:!0,get:()=>e.isExtensionState});Object.defineProperty(exports,"isOperationError",{enumerable:!0,get:()=>e.isOperationError});Object.defineProperty(exports,"isOverallReady",{enumerable:!0,get:()=>e.isOverallReady});Object.defineProperty(exports,"isWebUIClient",{enumerable:!0,get:()=>e.isWebUIClient});Object.defineProperty(exports,"useBodhi",{enumerable:!0,get:()=>e.useBodhi});exports.BodhiProvider=p;exports.REACT_EXT_BUILD_MODE=O;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { jsx as c } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as E, useMemo as l } from "react";
|
|
3
|
+
import { ExtUIClient as I } from "@bodhiapp/bodhi-js-ext";
|
|
4
|
+
import { ExtUIClient as R } from "@bodhiapp/bodhi-js-ext";
|
|
5
|
+
import { BodhiProvider as C } from "@bodhiapp/bodhi-js-react-core";
|
|
6
|
+
import { BodhiReactContext as B, ClientCtxState as N, INITIALIZING_CLIENT_CONTEXT_STATE as O, INITIAL_CLIENT_CONTEXT_STATE as S, clientStateToContextState as L, createApiError as D, createOperationError as U, isApiResultError as P, isApiResultOperationError as w, isApiResultSuccess as y, isAuthError as z, isAuthLoading as M, isAuthenticated as X, isClientCtxInitialized as g, isClientCtxInitializing as v, isClientCtxNotInitialized as b, isClientCtxReady as j, isClientReady as q, isDirectState as G, isExtensionState as W, isOperationError as Z, isOverallReady as $, isWebUIClient as k, useBodhi as F } from "@bodhiapp/bodhi-js-react-core";
|
|
7
|
+
function m({ children: n, authClientId: e, clientConfig: i, client: t, basePath: o, ...s }) {
|
|
8
|
+
if (!t && !e)
|
|
9
|
+
throw new Error('BodhiProvider requires either "client" or "authClientId" prop');
|
|
10
|
+
const r = E(null), a = l(() => {
|
|
11
|
+
if (t) return t;
|
|
12
|
+
if (r.current) return r.current;
|
|
13
|
+
const u = {
|
|
14
|
+
...i,
|
|
15
|
+
basePath: i?.basePath ?? o
|
|
16
|
+
};
|
|
17
|
+
return r.current = new I(e, u), r.current;
|
|
18
|
+
}, [t, e, i, o]);
|
|
19
|
+
return /* @__PURE__ */ c(C, { client: a, basePath: o, ...s, children: n });
|
|
20
|
+
}
|
|
21
|
+
const A = "production";
|
|
22
|
+
export {
|
|
23
|
+
m as BodhiProvider,
|
|
24
|
+
B as BodhiReactContext,
|
|
25
|
+
N as ClientCtxState,
|
|
26
|
+
R as ExtUIClient,
|
|
27
|
+
O as INITIALIZING_CLIENT_CONTEXT_STATE,
|
|
28
|
+
S as INITIAL_CLIENT_CONTEXT_STATE,
|
|
29
|
+
A as REACT_EXT_BUILD_MODE,
|
|
30
|
+
L as clientStateToContextState,
|
|
31
|
+
D as createApiError,
|
|
32
|
+
U as createOperationError,
|
|
33
|
+
P as isApiResultError,
|
|
34
|
+
w as isApiResultOperationError,
|
|
35
|
+
y as isApiResultSuccess,
|
|
36
|
+
z as isAuthError,
|
|
37
|
+
M as isAuthLoading,
|
|
38
|
+
X as isAuthenticated,
|
|
39
|
+
g as isClientCtxInitialized,
|
|
40
|
+
v as isClientCtxInitializing,
|
|
41
|
+
b as isClientCtxNotInitialized,
|
|
42
|
+
j as isClientCtxReady,
|
|
43
|
+
q as isClientReady,
|
|
44
|
+
G as isDirectState,
|
|
45
|
+
W as isExtensionState,
|
|
46
|
+
Z as isOperationError,
|
|
47
|
+
$ as isOverallReady,
|
|
48
|
+
k as isWebUIClient,
|
|
49
|
+
F as useBodhi
|
|
50
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ExtensionErrorCode = 'ext-not-installed' | 'ext-connection-failed' | 'ext-unsupported-version';
|
|
2
|
+
export declare const EXT_NOT_INSTALLED: ExtensionErrorCode;
|
|
3
|
+
export declare const EXT_CONNECTION_FAILED: ExtensionErrorCode;
|
|
4
|
+
export declare const EXT_UNSUPPORTED_VERSION: ExtensionErrorCode;
|
|
5
|
+
export interface ExtensionStateReady {
|
|
6
|
+
/** Current extension status */
|
|
7
|
+
status: 'ready';
|
|
8
|
+
/** Extension version */
|
|
9
|
+
version: string;
|
|
10
|
+
/** Extension ID (always present when ready) */
|
|
11
|
+
id: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ExtensionStateNotReady {
|
|
14
|
+
/** Current extension status */
|
|
15
|
+
status: 'unreachable' | 'not-installed' | 'unsupported';
|
|
16
|
+
/** Error details */
|
|
17
|
+
error: {
|
|
18
|
+
/** Error message */
|
|
19
|
+
message: string;
|
|
20
|
+
/** Error code */
|
|
21
|
+
code: ExtensionErrorCode;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export type ExtensionState = ExtensionStateReady | ExtensionStateNotReady;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consolidated types for setup-modal
|
|
3
|
+
*
|
|
4
|
+
* This folder contains all domain and protocol types organized by domain.
|
|
5
|
+
* It is designed to be independent and copyable to other packages.
|
|
6
|
+
*/
|
|
7
|
+
export type { BrowserType, OSType, EnvState, SupportedBrowser, NotSupportedBrowser, Browser, SupportedOS, NotSupportedOS, OS } from './platform';
|
|
8
|
+
export type { ExtensionErrorCode, ExtensionStateReady, ExtensionStateNotReady, ExtensionState } from './extension';
|
|
9
|
+
export { EXT_NOT_INSTALLED, EXT_CONNECTION_FAILED, EXT_UNSUPPORTED_VERSION } from './extension';
|
|
10
|
+
export type { ServerErrorCode, ServerStateReady, ServerStateReachable, ServerStatePending, ServerStateUnreachable, ServerStateError, ServerState } from './server';
|
|
11
|
+
export { SERVER_PENDING_EXT_READY, SERVER_CONN_REFUSED, SERVER_CONN_TIMEOUT, SERVER_NOT_FOUND, SERVER_NETWORK_UNREACHABLE, SERVER_SERVICE_UNAVAILABLE, SERVER_UNEXPECTED_ERROR, SERVER_IN_SETUP_STATUS, SERVER_IN_ADMIN_STATUS, } from './server';
|
|
12
|
+
export type { LnaErrorCode, LnaStatePrompt, LnaStateSkipped, LnaStateGranted, LnaStateUnreachable, LnaStateDenied, LnaStateUnsupported, LnaState, LnaServerStatePending, LnaServerStateReady, LnaServerStateSetup, LnaServerStateResourceAdmin, LnaServerStateError, LnaServerState, } from './lna';
|
|
13
|
+
export { LNA_UNREACHABLE, LNA_PERMISSION_DENIED } from './lna';
|
|
14
|
+
export { SetupStep, DEFAULT_USER_CONFIRMATIONS, DEFAULT_SETUP_STATE } from './state';
|
|
15
|
+
export type { SelectedConnection, UserConfirmations, SetupState } from './state';
|
|
16
|
+
export type { RequestId, MessageKind, RequestMessage, ResponseMessage, ErrorMessage, EventMessage, ProtocolMessage } from './protocol';
|
|
17
|
+
export { isRequestMessage, isResponseMessage, isErrorMessage, isEventMessage } from './protocol';
|
|
18
|
+
export type { MessageTypeRegistry, MessageType, RequestPayload, ResponsePayload, RequestHandlers } from './message-types';
|
|
19
|
+
export { MSG, isMessageType } from './message-types';
|
|
20
|
+
export { isExtensionStateReady, isExtensionStateNotReady, isServerStateReady, isServerStateReachable, isServerStatePending, isServerStateUnreachable, isServerStateError, isLnaStatePrompt, isLnaStateSkipped, isLnaStateGranted, isLnaStateUnreachable, isLnaStateDenied, isLnaStateUnsupported, isLnaServerStatePending, isLnaServerStateReady, isLnaServerStateSetup, isLnaServerStateResourceAdmin, isLnaServerStateError, isSupportedBrowser, isNotSupportedBrowser, isSupportedOS, isNotSupportedOS, } from './type-guards';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type LnaErrorCode = 'lna-unreachable' | 'lna-permission-denied';
|
|
2
|
+
export declare const LNA_UNREACHABLE: LnaErrorCode;
|
|
3
|
+
export declare const LNA_PERMISSION_DENIED: LnaErrorCode;
|
|
4
|
+
export interface LnaStatePrompt {
|
|
5
|
+
status: 'prompt';
|
|
6
|
+
serverUrl?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LnaStateSkipped {
|
|
9
|
+
status: 'skipped';
|
|
10
|
+
serverUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface LnaStateGranted {
|
|
13
|
+
status: 'granted';
|
|
14
|
+
serverUrl: string;
|
|
15
|
+
}
|
|
16
|
+
export interface LnaStateUnreachable {
|
|
17
|
+
status: 'unreachable';
|
|
18
|
+
serverUrl: string;
|
|
19
|
+
error: {
|
|
20
|
+
message: string;
|
|
21
|
+
code: LnaErrorCode;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface LnaStateDenied {
|
|
25
|
+
status: 'denied';
|
|
26
|
+
error: {
|
|
27
|
+
message: string;
|
|
28
|
+
code: LnaErrorCode;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface LnaStateUnsupported {
|
|
32
|
+
status: 'unsupported';
|
|
33
|
+
}
|
|
34
|
+
export type LnaState = LnaStatePrompt | LnaStateSkipped | LnaStateGranted | LnaStateUnreachable | LnaStateDenied | LnaStateUnsupported;
|
|
35
|
+
export interface LnaServerStatePending {
|
|
36
|
+
status: 'pending-lna-ready';
|
|
37
|
+
}
|
|
38
|
+
export interface LnaServerStateReady {
|
|
39
|
+
status: 'ready';
|
|
40
|
+
version: string;
|
|
41
|
+
}
|
|
42
|
+
export interface LnaServerStateSetup {
|
|
43
|
+
status: 'setup';
|
|
44
|
+
version: string;
|
|
45
|
+
}
|
|
46
|
+
export interface LnaServerStateResourceAdmin {
|
|
47
|
+
status: 'resource-admin';
|
|
48
|
+
version: string;
|
|
49
|
+
}
|
|
50
|
+
export interface LnaServerStateError {
|
|
51
|
+
status: 'error';
|
|
52
|
+
error: {
|
|
53
|
+
message: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export type LnaServerState = LnaServerStatePending | LnaServerStateReady | LnaServerStateSetup | LnaServerStateResourceAdmin | LnaServerStateError;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { SetupState } from './state';
|
|
2
|
+
/**
|
|
3
|
+
* Central registry mapping message types to their payload/response shapes
|
|
4
|
+
* Single source of truth - everything else is inferred!
|
|
5
|
+
*/
|
|
6
|
+
export interface MessageTypeRegistry {
|
|
7
|
+
/**
|
|
8
|
+
* Modal is ready and requesting initial state
|
|
9
|
+
* Sent on modal mount
|
|
10
|
+
*/
|
|
11
|
+
'modal:ready': {
|
|
12
|
+
request: void;
|
|
13
|
+
response: {
|
|
14
|
+
setupState: SetupState;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* User requested to refresh platform detection
|
|
19
|
+
* Triggers re-detection of browser, OS, extension, server
|
|
20
|
+
*/
|
|
21
|
+
'modal:refresh': {
|
|
22
|
+
request: void;
|
|
23
|
+
response: {
|
|
24
|
+
setupState: SetupState;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* User requested to close the modal
|
|
29
|
+
* Parent should hide/destroy modal
|
|
30
|
+
*/
|
|
31
|
+
'modal:close': {
|
|
32
|
+
request: void;
|
|
33
|
+
response: void;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Setup completed successfully
|
|
37
|
+
* Signals parent that user has finished setup
|
|
38
|
+
*/
|
|
39
|
+
'modal:complete': {
|
|
40
|
+
request: void;
|
|
41
|
+
response: void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* User requested LNA connection to specific server URL
|
|
45
|
+
* Triggers LNA permission request and connection attempt
|
|
46
|
+
*/
|
|
47
|
+
'modal:lna:connect': {
|
|
48
|
+
request: {
|
|
49
|
+
serverUrl: string;
|
|
50
|
+
};
|
|
51
|
+
response: {
|
|
52
|
+
success: boolean;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* User chose to skip LNA setup
|
|
57
|
+
* Falls back to extension-only mode
|
|
58
|
+
*/
|
|
59
|
+
'modal:lna:skip': {
|
|
60
|
+
request: void;
|
|
61
|
+
response: {
|
|
62
|
+
success: boolean;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* User confirmed server installation status
|
|
67
|
+
* Used when server needs to be installed
|
|
68
|
+
*/
|
|
69
|
+
'modal:confirm-server-install': {
|
|
70
|
+
request: {
|
|
71
|
+
confirmed: boolean;
|
|
72
|
+
};
|
|
73
|
+
response: {
|
|
74
|
+
success: boolean;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* User selected preferred connection method
|
|
79
|
+
* Allows choosing between LNA and Extension paths
|
|
80
|
+
*/
|
|
81
|
+
'modal:select-connection': {
|
|
82
|
+
request: {
|
|
83
|
+
connection: 'lna' | 'extension';
|
|
84
|
+
};
|
|
85
|
+
response: {
|
|
86
|
+
success: boolean;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Parent sending updated state to modal
|
|
91
|
+
* Fired when platform detection results change
|
|
92
|
+
*/
|
|
93
|
+
'parent:state-update': {
|
|
94
|
+
request: {
|
|
95
|
+
setupState: SetupState;
|
|
96
|
+
};
|
|
97
|
+
response: void;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Union of all valid message type strings
|
|
102
|
+
* Derived from MessageTypeRegistry keys
|
|
103
|
+
*/
|
|
104
|
+
export type MessageType = keyof MessageTypeRegistry;
|
|
105
|
+
/**
|
|
106
|
+
* Extract request payload type for a given message type
|
|
107
|
+
* Returns the 'request' field from the registry entry
|
|
108
|
+
*/
|
|
109
|
+
export type RequestPayload<T extends MessageType> = MessageTypeRegistry[T]['request'];
|
|
110
|
+
/**
|
|
111
|
+
* Extract response payload type for a given message type
|
|
112
|
+
* Returns the 'response' field from the registry entry
|
|
113
|
+
*/
|
|
114
|
+
export type ResponsePayload<T extends MessageType> = MessageTypeRegistry[T]['response'];
|
|
115
|
+
/**
|
|
116
|
+
* Message type constants - Type-safe identifiers
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* switch (message.type) {
|
|
120
|
+
* case MSG.MODAL_READY:
|
|
121
|
+
* // Type-safe constant, no typos possible
|
|
122
|
+
* break;
|
|
123
|
+
* }
|
|
124
|
+
*/
|
|
125
|
+
export declare const MSG: {
|
|
126
|
+
readonly MODAL_READY: "modal:ready";
|
|
127
|
+
readonly MODAL_REFRESH: "modal:refresh";
|
|
128
|
+
readonly MODAL_CLOSE: "modal:close";
|
|
129
|
+
readonly MODAL_COMPLETE: "modal:complete";
|
|
130
|
+
readonly MODAL_LNA_CONNECT: "modal:lna:connect";
|
|
131
|
+
readonly MODAL_LNA_SKIP: "modal:lna:skip";
|
|
132
|
+
readonly MODAL_CONFIRM_SERVER_INSTALL: "modal:confirm-server-install";
|
|
133
|
+
readonly MODAL_SELECT_CONNECTION: "modal:select-connection";
|
|
134
|
+
readonly PARENT_STATE_UPDATE: "parent:state-update";
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Type guard that narrows RequestMessage to specific type WITH payload typing
|
|
138
|
+
*
|
|
139
|
+
* Use this for full type safety when you need access to typed payload fields.
|
|
140
|
+
* The guard narrows the generic RequestMessage to RequestMessage<T> where T
|
|
141
|
+
* determines the payload type.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* if (isMessageType(msg, MSG.MODAL_LNA_CONNECT)) {
|
|
145
|
+
* // msg.payload is { serverUrl: string } - fully typed!
|
|
146
|
+
* console.log(msg.payload.serverUrl); // Autocomplete works!
|
|
147
|
+
* }
|
|
148
|
+
*/
|
|
149
|
+
export declare function isMessageType<T extends MessageType>(msg: {
|
|
150
|
+
type: string;
|
|
151
|
+
}, type: T): msg is {
|
|
152
|
+
type: T;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Type-safe request handler map
|
|
156
|
+
* Enforces correct payload access AND correct return type for each message
|
|
157
|
+
*
|
|
158
|
+
* Each handler receives a RequestMessage<K> where K is the specific message type,
|
|
159
|
+
* providing full type safety for payload access. The handler must return the
|
|
160
|
+
* correct ResponsePayload<K> type.
|
|
161
|
+
*/
|
|
162
|
+
export type RequestHandlers = {
|
|
163
|
+
[K in MessageType]?: (msg: {
|
|
164
|
+
type: K;
|
|
165
|
+
requestId: string;
|
|
166
|
+
payload: RequestPayload<K>;
|
|
167
|
+
}) => ResponsePayload<K>;
|
|
168
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type BrowserType = 'chrome' | 'edge' | 'firefox' | 'safari' | 'unknown';
|
|
2
|
+
export type OSType = 'macos' | 'windows' | 'linux' | 'unknown';
|
|
3
|
+
export interface EnvState {
|
|
4
|
+
os: OSType;
|
|
5
|
+
browser: BrowserType;
|
|
6
|
+
}
|
|
7
|
+
export interface SupportedBrowser {
|
|
8
|
+
id: BrowserType;
|
|
9
|
+
status: 'supported';
|
|
10
|
+
name: string;
|
|
11
|
+
extension_url: string;
|
|
12
|
+
}
|
|
13
|
+
export interface NotSupportedBrowser {
|
|
14
|
+
id: BrowserType;
|
|
15
|
+
status: 'not-supported';
|
|
16
|
+
name: string;
|
|
17
|
+
github_issue_url?: string;
|
|
18
|
+
}
|
|
19
|
+
export type Browser = SupportedBrowser | NotSupportedBrowser;
|
|
20
|
+
export interface SupportedOS {
|
|
21
|
+
id: OSType;
|
|
22
|
+
status: 'supported';
|
|
23
|
+
name: string;
|
|
24
|
+
download_url: string;
|
|
25
|
+
}
|
|
26
|
+
export interface NotSupportedOS {
|
|
27
|
+
id: OSType;
|
|
28
|
+
status: 'not-supported';
|
|
29
|
+
name: string;
|
|
30
|
+
github_issue_url?: string;
|
|
31
|
+
}
|
|
32
|
+
export type OS = SupportedOS | NotSupportedOS;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { MessageType, RequestPayload, ResponsePayload } from './message-types';
|
|
2
|
+
/** Branded type for type-safe request IDs */
|
|
3
|
+
export type RequestId = string & {
|
|
4
|
+
readonly __brand: 'RequestId';
|
|
5
|
+
};
|
|
6
|
+
/** Message kind discriminator */
|
|
7
|
+
export type MessageKind = 'request' | 'response' | 'error' | 'event';
|
|
8
|
+
/**
|
|
9
|
+
* Request message - expects a response
|
|
10
|
+
* Sent by either modal or parent to request an action
|
|
11
|
+
*/
|
|
12
|
+
export interface RequestMessage<T extends MessageType = MessageType> {
|
|
13
|
+
readonly kind: 'request';
|
|
14
|
+
readonly type: T;
|
|
15
|
+
readonly requestId: RequestId;
|
|
16
|
+
readonly payload: RequestPayload<T>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Response message - correlates to a request
|
|
20
|
+
* Sent in response to a RequestMessage with matching requestId
|
|
21
|
+
*/
|
|
22
|
+
export interface ResponseMessage<T extends MessageType = MessageType> {
|
|
23
|
+
readonly kind: 'response';
|
|
24
|
+
readonly type: T;
|
|
25
|
+
readonly requestId: RequestId;
|
|
26
|
+
readonly payload: ResponsePayload<T>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Error response - indicates request failure
|
|
30
|
+
* Sent instead of ResponseMessage when request cannot be fulfilled
|
|
31
|
+
*/
|
|
32
|
+
export interface ErrorMessage {
|
|
33
|
+
readonly kind: 'error';
|
|
34
|
+
readonly requestId: RequestId;
|
|
35
|
+
readonly error: {
|
|
36
|
+
code: string;
|
|
37
|
+
message: string;
|
|
38
|
+
details?: unknown;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Event message - fire-and-forget notification
|
|
43
|
+
* No response expected, used for one-way state updates
|
|
44
|
+
*/
|
|
45
|
+
export interface EventMessage<T extends MessageType = MessageType> {
|
|
46
|
+
readonly kind: 'event';
|
|
47
|
+
readonly type: T;
|
|
48
|
+
readonly payload: RequestPayload<T>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Union of all protocol messages
|
|
52
|
+
* Discriminated by 'kind' field for type narrowing
|
|
53
|
+
*/
|
|
54
|
+
export type ProtocolMessage = RequestMessage | ResponseMessage | ErrorMessage | EventMessage;
|
|
55
|
+
/**
|
|
56
|
+
* Type guard to check if message is a request
|
|
57
|
+
*/
|
|
58
|
+
export declare function isRequestMessage(msg: ProtocolMessage): msg is RequestMessage;
|
|
59
|
+
/**
|
|
60
|
+
* Type guard to check if message is a response
|
|
61
|
+
*/
|
|
62
|
+
export declare function isResponseMessage(msg: ProtocolMessage): msg is ResponseMessage;
|
|
63
|
+
/**
|
|
64
|
+
* Type guard to check if message is an error
|
|
65
|
+
*/
|
|
66
|
+
export declare function isErrorMessage(msg: ProtocolMessage): msg is ErrorMessage;
|
|
67
|
+
/**
|
|
68
|
+
* Type guard to check if message is an event
|
|
69
|
+
*/
|
|
70
|
+
export declare function isEventMessage(msg: ProtocolMessage): msg is EventMessage;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export type ServerErrorCode = 'server-pending-ext-ready' | 'server-conn-refused' | 'server-conn-timeout' | 'server-not-found' | 'server-network-unreachable' | 'server-service-unavailable' | 'server-unexpected-error' | 'server-in-setup-status' | 'server-in-admin-status';
|
|
2
|
+
export declare const SERVER_PENDING_EXT_READY: ServerErrorCode;
|
|
3
|
+
export declare const SERVER_CONN_REFUSED: ServerErrorCode;
|
|
4
|
+
export declare const SERVER_CONN_TIMEOUT: ServerErrorCode;
|
|
5
|
+
export declare const SERVER_NOT_FOUND: ServerErrorCode;
|
|
6
|
+
export declare const SERVER_NETWORK_UNREACHABLE: ServerErrorCode;
|
|
7
|
+
export declare const SERVER_SERVICE_UNAVAILABLE: ServerErrorCode;
|
|
8
|
+
export declare const SERVER_UNEXPECTED_ERROR: ServerErrorCode;
|
|
9
|
+
export declare const SERVER_IN_SETUP_STATUS: ServerErrorCode;
|
|
10
|
+
export declare const SERVER_IN_ADMIN_STATUS: ServerErrorCode;
|
|
11
|
+
export interface ServerStateReady {
|
|
12
|
+
/** Current server status */
|
|
13
|
+
status: 'ready';
|
|
14
|
+
/** Server version */
|
|
15
|
+
version: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ServerStateReachable {
|
|
18
|
+
/** Current server status */
|
|
19
|
+
status: 'setup' | 'resource-admin';
|
|
20
|
+
/** Server version */
|
|
21
|
+
version: string;
|
|
22
|
+
/** Error details */
|
|
23
|
+
error: {
|
|
24
|
+
/** Error message */
|
|
25
|
+
message: string;
|
|
26
|
+
/** Error code */
|
|
27
|
+
code: ServerErrorCode;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface ServerStatePending {
|
|
31
|
+
/** Current server status */
|
|
32
|
+
status: 'pending-extension-ready';
|
|
33
|
+
/** Error details */
|
|
34
|
+
error: {
|
|
35
|
+
/** Error message */
|
|
36
|
+
message: string;
|
|
37
|
+
/** Error code */
|
|
38
|
+
code: ServerErrorCode;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export interface ServerStateUnreachable {
|
|
42
|
+
/** Current server status */
|
|
43
|
+
status: 'unreachable';
|
|
44
|
+
/** Error details */
|
|
45
|
+
error: {
|
|
46
|
+
/** Error message */
|
|
47
|
+
message: string;
|
|
48
|
+
/** Error code */
|
|
49
|
+
code: ServerErrorCode;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export interface ServerStateError {
|
|
53
|
+
/** Current server status */
|
|
54
|
+
status: 'error';
|
|
55
|
+
/** Error details */
|
|
56
|
+
error: {
|
|
57
|
+
/** Error message */
|
|
58
|
+
message: string;
|
|
59
|
+
/** Error code */
|
|
60
|
+
code: ServerErrorCode;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export type ServerState = ServerStateReady | ServerStateReachable | ServerStatePending | ServerStateUnreachable | ServerStateError;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Browser, EnvState, OS } from './platform';
|
|
2
|
+
import { ExtensionState } from './extension';
|
|
3
|
+
import { ServerState } from './server';
|
|
4
|
+
import { LnaServerState, LnaState } from './lna';
|
|
5
|
+
export declare enum SetupStep {
|
|
6
|
+
PLATFORM_CHECK = "platform-check",
|
|
7
|
+
SERVER_SETUP = "server-setup",
|
|
8
|
+
LNA_SETUP = "lna-setup",
|
|
9
|
+
EXTENSION_SETUP = "extension-setup",
|
|
10
|
+
COMPLETE = "complete"
|
|
11
|
+
}
|
|
12
|
+
export type SelectedConnection = 'lna' | 'extension' | null;
|
|
13
|
+
export interface UserConfirmations {
|
|
14
|
+
/** Whether user has confirmed server installation */
|
|
15
|
+
serverInstall: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_USER_CONFIRMATIONS: UserConfirmations;
|
|
18
|
+
export interface SetupState {
|
|
19
|
+
/** Extension state details */
|
|
20
|
+
extension: ExtensionState;
|
|
21
|
+
/** Server state details (via extension) */
|
|
22
|
+
server: ServerState;
|
|
23
|
+
/** LNA connection state */
|
|
24
|
+
lna: LnaState;
|
|
25
|
+
/** Server state details (via LNA) */
|
|
26
|
+
lnaServer: LnaServerState;
|
|
27
|
+
/** Environment detection */
|
|
28
|
+
env: EnvState;
|
|
29
|
+
/** Browser platforms list */
|
|
30
|
+
browsers: Browser[];
|
|
31
|
+
/** Operating systems list */
|
|
32
|
+
os: OS[];
|
|
33
|
+
/** User confirmations for manual steps */
|
|
34
|
+
userConfirmations: UserConfirmations;
|
|
35
|
+
/** User's preferred connection method (null = auto-select based on priority) */
|
|
36
|
+
selectedConnection: SelectedConnection;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Default setup state used during initialization before parent sends real state
|
|
40
|
+
* Represents "loading" state - unknown platform, no extension, no server
|
|
41
|
+
*/
|
|
42
|
+
export declare const DEFAULT_SETUP_STATE: SetupState;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ExtensionState, ExtensionStateNotReady, ExtensionStateReady } from './extension';
|
|
2
|
+
import { ServerState, ServerStateError, ServerStatePending, ServerStateReachable, ServerStateReady, ServerStateUnreachable } from './server';
|
|
3
|
+
import { LnaServerState, LnaServerStateError, LnaServerStatePending, LnaServerStateReady, LnaServerStateResourceAdmin, LnaServerStateSetup, LnaState, LnaStateDenied, LnaStateGranted, LnaStatePrompt, LnaStateSkipped, LnaStateUnreachable, LnaStateUnsupported } from './lna';
|
|
4
|
+
import { Browser, NotSupportedBrowser, NotSupportedOS, OS, SupportedBrowser, SupportedOS } from './platform';
|
|
5
|
+
export declare function isExtensionStateReady(ext: ExtensionState): ext is ExtensionStateReady;
|
|
6
|
+
export declare function isExtensionStateNotReady(ext: ExtensionState): ext is ExtensionStateNotReady;
|
|
7
|
+
export declare function isServerStateReady(server: ServerState): server is ServerStateReady;
|
|
8
|
+
export declare function isServerStateReachable(server: ServerState): server is ServerStateReachable;
|
|
9
|
+
export declare function isServerStatePending(server: ServerState): server is ServerStatePending;
|
|
10
|
+
export declare function isServerStateUnreachable(server: ServerState): server is ServerStateUnreachable;
|
|
11
|
+
export declare function isServerStateError(server: ServerState): server is ServerStateError;
|
|
12
|
+
export declare function isLnaStatePrompt(lna: LnaState): lna is LnaStatePrompt;
|
|
13
|
+
export declare function isLnaStateSkipped(lna: LnaState): lna is LnaStateSkipped;
|
|
14
|
+
export declare function isLnaStateGranted(lna: LnaState): lna is LnaStateGranted;
|
|
15
|
+
export declare function isLnaStateUnreachable(lna: LnaState): lna is LnaStateUnreachable;
|
|
16
|
+
export declare function isLnaStateDenied(lna: LnaState): lna is LnaStateDenied;
|
|
17
|
+
export declare function isLnaStateUnsupported(lna: LnaState): lna is LnaStateUnsupported;
|
|
18
|
+
export declare function isLnaServerStatePending(server: LnaServerState): server is LnaServerStatePending;
|
|
19
|
+
export declare function isLnaServerStateReady(server: LnaServerState): server is LnaServerStateReady;
|
|
20
|
+
export declare function isLnaServerStateSetup(server: LnaServerState): server is LnaServerStateSetup;
|
|
21
|
+
export declare function isLnaServerStateResourceAdmin(server: LnaServerState): server is LnaServerStateResourceAdmin;
|
|
22
|
+
export declare function isLnaServerStateError(server: LnaServerState): server is LnaServerStateError;
|
|
23
|
+
export declare function isSupportedBrowser(browser: Browser): browser is SupportedBrowser;
|
|
24
|
+
export declare function isNotSupportedBrowser(browser: Browser): browser is NotSupportedBrowser;
|
|
25
|
+
export declare function isSupportedOS(os: OS): os is SupportedOS;
|
|
26
|
+
export declare function isNotSupportedOS(os: OS): os is NotSupportedOS;
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bodhiapp/bodhi-js-react-ext",
|
|
3
|
+
"version": "0.0.11",
|
|
4
|
+
"description": "React bindings for Bodhi Browser SDK with ExtUIClient preset (Chrome Extension)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/bodhi-react-ext.cjs.js",
|
|
7
|
+
"module": "dist/bodhi-react-ext.esm.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/bodhi-react-ext.esm.d.ts",
|
|
11
|
+
"import": "./dist/bodhi-react-ext.esm.js",
|
|
12
|
+
"require": "./dist/bodhi-react-ext.cjs.js"
|
|
13
|
+
},
|
|
14
|
+
"./api": {
|
|
15
|
+
"types": "./src/api/index.ts",
|
|
16
|
+
"import": "./src/api/index.ts",
|
|
17
|
+
"default": "./src/api/index.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/BodhiSearch/bodhi-js.git",
|
|
27
|
+
"directory": "bodhi-js-sdk/react-ext"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"author": "BodhiSearch",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"clean": "rimraf dist",
|
|
36
|
+
"build": "vite build --mode development",
|
|
37
|
+
"ci:build": "vite build --mode development",
|
|
38
|
+
"dev:build": "node ../../scripts/build-fast.mjs bodhi-js-sdk/react-ext 'npm run build' src package.json vite.config.ts",
|
|
39
|
+
"build:prod": "vite build --mode production",
|
|
40
|
+
"lint": "eslint . --ext .js,.jsx,.ts,.tsx && prettier --check .",
|
|
41
|
+
"lint:fix": "prettier --write . && eslint . --ext .js,.jsx,.ts,.tsx --fix",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@bodhiapp/bodhi-js-react-core": "0.0.11",
|
|
46
|
+
"@bodhiapp/bodhi-js-ext": "0.0.11"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": "^18.3.0 || ^19.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@eslint/js": "^9.23.0",
|
|
53
|
+
"@types/node": "^20.19.10",
|
|
54
|
+
"@types/react": "^19.0.0",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "8.28.0",
|
|
56
|
+
"@typescript-eslint/parser": "8.28.0",
|
|
57
|
+
"eslint": "9.32.0",
|
|
58
|
+
"eslint-config-prettier": "10.1.8",
|
|
59
|
+
"globals": "^16.0.0",
|
|
60
|
+
"prettier": "3.6.2",
|
|
61
|
+
"rimraf": "^6.0.1",
|
|
62
|
+
"tslib": "^2.6.2",
|
|
63
|
+
"typescript": "^5.8.3",
|
|
64
|
+
"vite": "^7.1.12",
|
|
65
|
+
"vite-plugin-dts": "^4.5.4"
|
|
66
|
+
}
|
|
67
|
+
}
|