@bodhiapp/bodhi-js-react-core 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.
@@ -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-core",
3
+ "version": "0.0.11",
4
+ "description": "Core React bindings for Bodhi Browser SDK (dependency injection)",
5
+ "type": "module",
6
+ "main": "dist/bodhi-react-core.cjs.js",
7
+ "module": "dist/bodhi-react-core.esm.js",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/bodhi-react-core.esm.d.ts",
11
+ "import": "./dist/bodhi-react-core.esm.js",
12
+ "require": "./dist/bodhi-react-core.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-core"
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-core '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-core": "0.0.11",
46
+ "@bodhiapp/ts-client": "0.1.9"
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
+ }