@bodhiapp/bodhi-js-react 0.0.1 → 0.0.3

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.
@@ -1,570 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const jsxRuntime = require("react/jsx-runtime");
4
- const bodhiJsCore = require("@bodhiapp/bodhi-js-core");
5
- const react = require("react");
6
- const types = require("@bodhiapp/setup-modal/types");
7
- function SetupModalProcessor({
8
- client,
9
- modalHtmlPath,
10
- hideSetup,
11
- onSetupReady,
12
- setupState,
13
- logLevel = "warn"
14
- }) {
15
- const isVisible = setupState !== "ready";
16
- const logger = react.useMemo(() => new bodhiJsCore.Logger("SetupModalProcessor", logLevel), [logLevel]);
17
- const modalRef = react.useRef(null);
18
- const prefs = react.useMemo(() => new bodhiJsCore.BodhiClientUserPrefsManager(), []);
19
- const currentStateRef = react.useRef(null);
20
- const mapConnectionMode = react.useCallback(
21
- (mode) => {
22
- if (mode === "direct") return "lna";
23
- if (mode === "extension") return "extension";
24
- return null;
25
- },
26
- []
27
- );
28
- const mapExtension = react.useCallback((state) => {
29
- if (state.extension === "ready") {
30
- return {
31
- status: "ready",
32
- version: "unknown",
33
- // TODO: have ExtensionState also get extension version
34
- id: state.extensionId
35
- };
36
- } else if (state.extension === "not-found") {
37
- return {
38
- status: "not-installed",
39
- error: { message: "Extension not found", code: "ext-not-installed" }
40
- };
41
- } else {
42
- return {
43
- status: "unreachable",
44
- error: { message: "Client not initialized", code: "ext-connection-failed" }
45
- };
46
- }
47
- }, []);
48
- const mapServer = react.useCallback((state) => {
49
- if (state.server.status === "pending-extension-ready") {
50
- return {
51
- status: "pending-extension-ready",
52
- error: { message: "Extension not ready", code: "server-pending-ext-ready" }
53
- };
54
- }
55
- const serverState = state.server;
56
- switch (serverState.status) {
57
- case "ready":
58
- return { status: "ready", version: serverState.version };
59
- case "setup":
60
- return {
61
- status: "setup",
62
- version: serverState.version,
63
- error: { message: serverState.error.message, code: "server-in-setup-status" }
64
- };
65
- case "resource-admin":
66
- return {
67
- status: "resource-admin",
68
- version: serverState.version,
69
- error: { message: serverState.error.message, code: "server-in-admin-status" }
70
- };
71
- case "error":
72
- return {
73
- status: "error",
74
- error: { message: serverState.error.message, code: "server-unexpected-error" }
75
- };
76
- case "not-reachable":
77
- return {
78
- status: "unreachable",
79
- error: { message: serverState.error.message, code: "server-conn-refused" }
80
- };
81
- }
82
- }, []);
83
- const mapLnaServer = react.useCallback((state) => {
84
- if (state.server.status === "not-connected") {
85
- return { status: "pending-lna-ready" };
86
- }
87
- const serverState = state.server;
88
- switch (serverState.status) {
89
- case "ready":
90
- return { status: "ready", version: serverState.version };
91
- case "setup":
92
- return { status: "setup", version: serverState.version };
93
- case "resource-admin":
94
- return { status: "resource-admin", version: serverState.version };
95
- case "error":
96
- case "not-reachable":
97
- return { status: "error", error: { message: serverState.error.message } };
98
- }
99
- }, []);
100
- const isServerReachable = react.useCallback(
101
- (extensionState, directState) => {
102
- if (extensionState.server.status !== "pending-extension-ready") {
103
- const status = extensionState.server.status;
104
- if (status === "ready" || status === "setup" || status === "resource-admin") {
105
- return true;
106
- }
107
- }
108
- if (directState.server.status !== "not-connected") {
109
- const status = directState.server.status;
110
- if (status === "ready" || status === "setup" || status === "resource-admin") {
111
- return true;
112
- }
113
- }
114
- return false;
115
- },
116
- []
117
- );
118
- const buildLnaState = react.useCallback(
119
- (directState, directStatus, serverUrl) => {
120
- if (directStatus === "skipped") {
121
- return { status: "skipped", serverUrl };
122
- }
123
- if (directState.server.status !== "not-connected") {
124
- const serverState = directState.server;
125
- if (serverState.status === "ready" || serverState.status === "setup" || serverState.status === "resource-admin") {
126
- return { status: "granted", serverUrl };
127
- } else {
128
- return {
129
- status: "unreachable",
130
- serverUrl,
131
- error: { message: serverState.error.message, code: "lna-unreachable" }
132
- };
133
- }
134
- }
135
- if (directStatus === "granted") {
136
- return { status: "granted", serverUrl };
137
- }
138
- return { status: "prompt", serverUrl };
139
- },
140
- []
141
- );
142
- const buildSetupState = react.useCallback(
143
- async (forceRefresh = false) => {
144
- const browser = bodhiJsCore.detectBrowser().type;
145
- const os = bodhiJsCore.detectOS().type;
146
- let extensionState = await client.getExtensionState();
147
- if (extensionState.extension === "not-initialized" || forceRefresh) {
148
- extensionState = await client.testExtensionConnectivity();
149
- }
150
- let directState = await client.getDirectState();
151
- const directStatus = prefs.getDirectStatus();
152
- const shouldTestDirect = directStatus === "granted" && (directState.server.status === "not-connected" || forceRefresh);
153
- if (shouldTestDirect) {
154
- directState = await client.testDirectConnectivity();
155
- }
156
- const defaultServerUrl = bodhiJsCore.getServerUrl(directState) || "http://localhost:1135";
157
- const lna = buildLnaState(directState, directStatus, defaultServerUrl);
158
- if (client.getConnectionMode() === null) {
159
- if (bodhiJsCore.isDirectServerReady(directState)) {
160
- await client.setConnectionMode("direct");
161
- } else if (bodhiJsCore.isExtensionServerReady(extensionState)) {
162
- await client.setConnectionMode("extension");
163
- }
164
- }
165
- if (!prefs.isServerInstallConfirmed()) {
166
- if (isServerReachable(extensionState, directState)) {
167
- prefs.setServerInstallConfirmed(true);
168
- }
169
- }
170
- return {
171
- extension: mapExtension(extensionState),
172
- server: mapServer(extensionState),
173
- lna,
174
- lnaServer: mapLnaServer(directState),
175
- env: { browser, os },
176
- browsers: bodhiJsCore.BROWSER_CONFIGS,
177
- os: bodhiJsCore.OS_CONFIGS,
178
- userConfirmations: { serverInstall: prefs.isServerInstallConfirmed() },
179
- selectedConnection: mapConnectionMode(client.getConnectionMode())
180
- };
181
- },
182
- [
183
- client,
184
- prefs,
185
- mapExtension,
186
- mapServer,
187
- mapLnaServer,
188
- mapConnectionMode,
189
- buildLnaState,
190
- isServerReachable
191
- ]
192
- );
193
- const getStateWithOverrides = react.useCallback(() => {
194
- if (!currentStateRef.current) {
195
- throw new Error("Cannot get state: currentStateRef is null");
196
- }
197
- return {
198
- ...currentStateRef.current,
199
- // Override userConfirmations from storage (in case updated outside buildSetupState)
200
- userConfirmations: {
201
- serverInstall: prefs.isServerInstallConfirmed()
202
- },
203
- // Override selectedConnection from client (in case changed via handler)
204
- selectedConnection: mapConnectionMode(client.getConnectionMode())
205
- };
206
- }, [prefs, client, mapConnectionMode]);
207
- const handlers = react.useMemo(
208
- () => ({
209
- [types.MSG.MODAL_READY]: async () => {
210
- logger.info("MODAL_READY: building initial state");
211
- const finalState = getStateWithOverrides();
212
- return { setupState: finalState };
213
- },
214
- [types.MSG.MODAL_REFRESH]: async () => {
215
- var _a;
216
- logger.info("MODAL_REFRESH: refreshing state");
217
- const state = await buildSetupState(true);
218
- currentStateRef.current = state;
219
- const finalState = getStateWithOverrides();
220
- logger.info("MODAL_REFRESH: state refreshed\n", JSON.stringify(finalState, null, 2));
221
- (_a = modalRef.current) == null ? void 0 : _a.updateState(getStateWithOverrides());
222
- return { setupState: finalState };
223
- },
224
- [types.MSG.MODAL_LNA_CONNECT]: async (msg) => {
225
- var _a, _b;
226
- const serverUrl = msg.payload.serverUrl;
227
- console.log("[SetupModalProcessor] LNA connect:", serverUrl);
228
- const directState = await client.testDirectConnectivity(serverUrl);
229
- if (directState.server.status !== "not-connected") {
230
- const serverStatus = directState.server.status;
231
- if (serverStatus === "ready" || serverStatus === "setup" || serverStatus === "resource-admin") {
232
- prefs.setDirectStatus("granted");
233
- if (client.getConnectionMode() === null) {
234
- await client.setConnectionMode("direct");
235
- }
236
- const state2 = await buildSetupState();
237
- currentStateRef.current = state2;
238
- (_a = modalRef.current) == null ? void 0 : _a.updateState(getStateWithOverrides());
239
- return { success: true };
240
- }
241
- }
242
- const state = await buildSetupState();
243
- currentStateRef.current = state;
244
- (_b = modalRef.current) == null ? void 0 : _b.updateState(getStateWithOverrides());
245
- return { success: false };
246
- },
247
- [types.MSG.MODAL_LNA_SKIP]: async () => {
248
- var _a;
249
- prefs.setDirectStatus("skipped");
250
- const state = await buildSetupState();
251
- currentStateRef.current = state;
252
- (_a = modalRef.current) == null ? void 0 : _a.updateState(getStateWithOverrides());
253
- return { success: true };
254
- },
255
- [types.MSG.MODAL_CLOSE]: () => {
256
- hideSetup();
257
- return void 0;
258
- },
259
- [types.MSG.MODAL_COMPLETE]: () => {
260
- hideSetup();
261
- return void 0;
262
- },
263
- [types.MSG.MODAL_CONFIRM_SERVER_INSTALL]: (msg) => {
264
- var _a;
265
- prefs.setServerInstallConfirmed(msg.payload.confirmed);
266
- (_a = modalRef.current) == null ? void 0 : _a.updateState(getStateWithOverrides());
267
- return { success: true };
268
- },
269
- [types.MSG.MODAL_SELECT_CONNECTION]: async (msg) => {
270
- var _a;
271
- const connection = msg.payload.connection;
272
- const mode = connection === "lna" ? "direct" : "extension";
273
- await client.setConnectionMode(mode);
274
- (_a = modalRef.current) == null ? void 0 : _a.updateState(getStateWithOverrides());
275
- return { success: true };
276
- }
277
- }),
278
- [logger, client, prefs, buildSetupState, getStateWithOverrides, mapLnaServer, hideSetup]
279
- );
280
- react.useEffect(() => {
281
- modalRef.current = new bodhiJsCore.OnboardingModal({ modalHtmlPath, handlers });
282
- return () => {
283
- var _a;
284
- (_a = modalRef.current) == null ? void 0 : _a.destroy();
285
- modalRef.current = null;
286
- };
287
- }, [modalHtmlPath, handlers]);
288
- react.useEffect(() => {
289
- if (isVisible && modalRef.current) {
290
- buildSetupState(true).then((state) => {
291
- var _a;
292
- currentStateRef.current = state;
293
- (_a = modalRef.current) == null ? void 0 : _a.show(state);
294
- onSetupReady == null ? void 0 : onSetupReady();
295
- }).catch((error) => {
296
- console.error("[SetupModalProcessor] buildSetupState failed:", error);
297
- });
298
- } else if (!isVisible && modalRef.current) {
299
- modalRef.current.destroy();
300
- }
301
- }, [isVisible, buildSetupState, onSetupReady]);
302
- return null;
303
- }
304
- const BodhiReactContext = react.createContext(null);
305
- BodhiReactContext.displayName = "BodhiContext";
306
- function BodhiProvider({
307
- children,
308
- client,
309
- modalHtmlPath,
310
- handleCallback = true,
311
- callbackUrl = "/callback",
312
- logLevel = "warn"
313
- }) {
314
- const callbackProcessedRef = react.useRef(false);
315
- const initAttemptedRef = react.useRef(false);
316
- const [clientState, setClientState] = react.useState(
317
- { type: "not-initialized" }
318
- // Start with UI state, client.init() will update
319
- );
320
- const [auth, setAuth] = react.useState(null);
321
- const [authLoading, setAuthLoading] = react.useState(false);
322
- const [setupState, setSetupState] = react.useState("ready");
323
- react.useEffect(() => {
324
- const onStateChange = (change) => {
325
- switch (change.type) {
326
- case "client-state":
327
- setClientState(change.state);
328
- break;
329
- case "auth-state":
330
- setAuth(change.state);
331
- setAuthLoading(false);
332
- break;
333
- }
334
- };
335
- client.setStateCallback(onStateChange);
336
- return () => {
337
- client.setStateCallback(bodhiJsCore.NOOP_STATE_CALLBACK);
338
- };
339
- }, [client]);
340
- const showSetup = react.useCallback(async () => {
341
- setSetupState("loading");
342
- }, []);
343
- const hideSetup = react.useCallback(() => {
344
- setSetupState("ready");
345
- }, []);
346
- const onSetupReady = react.useCallback(() => {
347
- setSetupState("loaded");
348
- }, []);
349
- const init = react.useCallback(
350
- async (params) => {
351
- setClientState({ type: "initializing" });
352
- try {
353
- await client.init(params || {});
354
- } catch (err) {
355
- console.error("[BodhiProvider] Init failed:", err);
356
- }
357
- },
358
- [client]
359
- );
360
- react.useEffect(() => {
361
- if (initAttemptedRef.current) return;
362
- initAttemptedRef.current = true;
363
- const initAndHandleCallback = async () => {
364
- await init();
365
- if (!handleCallback) return;
366
- const url = new URL(window.location.href);
367
- if (url.pathname !== callbackUrl) return;
368
- const code = url.searchParams.get("code");
369
- const state = url.searchParams.get("state");
370
- if (!code || !state) return;
371
- if (callbackProcessedRef.current) return;
372
- callbackProcessedRef.current = true;
373
- if (bodhiJsCore.isWebUIClient(client)) {
374
- setAuthLoading(true);
375
- client.handleOAuthCallback(code, state).then(() => {
376
- window.history.replaceState({}, "", "/");
377
- }).catch((error) => {
378
- console.error("OAuth callback failed:", error);
379
- setAuth({
380
- isLoggedIn: false,
381
- error: {
382
- message: error instanceof Error ? error.message : "OAuth callback failed",
383
- code: "OAUTH_CALLBACK_FAILED"
384
- }
385
- });
386
- setAuthLoading(false);
387
- window.history.replaceState({}, "", "/");
388
- });
389
- }
390
- };
391
- initAndHandleCallback();
392
- }, [init, client, handleCallback, callbackUrl]);
393
- const login = react.useCallback(async () => {
394
- setAuthLoading(true);
395
- try {
396
- await client.login();
397
- } catch (err) {
398
- setAuth({
399
- isLoggedIn: false,
400
- error: {
401
- message: err instanceof Error ? err.message : "Login failed",
402
- code: "LOGIN_FAILED"
403
- }
404
- });
405
- setAuthLoading(false);
406
- }
407
- }, [client]);
408
- const logout = react.useCallback(async () => {
409
- try {
410
- await client.logout();
411
- } catch (err) {
412
- setAuth({
413
- isLoggedIn: false,
414
- error: {
415
- message: err instanceof Error ? err.message : "Logout failed",
416
- code: "LOGOUT_FAILED"
417
- }
418
- });
419
- }
420
- }, [client]);
421
- const contextValue = react.useMemo(
422
- () => ({
423
- client,
424
- clientState,
425
- auth,
426
- authLoading,
427
- login,
428
- logout,
429
- showSetup,
430
- hideSetup,
431
- setupState
432
- }),
433
- [client, clientState, auth, authLoading, setupState, login, logout, showSetup, hideSetup]
434
- );
435
- return /* @__PURE__ */ jsxRuntime.jsxs(BodhiReactContext.Provider, { value: contextValue, children: [
436
- /* @__PURE__ */ jsxRuntime.jsx(
437
- SetupModalProcessor,
438
- {
439
- client,
440
- modalHtmlPath,
441
- hideSetup,
442
- onSetupReady,
443
- setupState,
444
- logLevel
445
- }
446
- ),
447
- children
448
- ] });
449
- }
450
- function useBodhi() {
451
- const context = react.useContext(BodhiReactContext);
452
- if (!context) throw new Error("useBodhi must be used within BodhiProvider");
453
- return {
454
- client: context.client,
455
- clientState: context.clientState,
456
- setupState: context.setupState,
457
- auth: context.auth,
458
- authLoading: context.authLoading,
459
- login: context.login,
460
- logout: context.logout,
461
- showSetup: context.showSetup,
462
- hideSetup: context.hideSetup
463
- };
464
- }
465
- function isClientCtxNotInitialized(state) {
466
- return state.type === "not-initialized";
467
- }
468
- function isClientCtxInitializing(state) {
469
- return state.type === "initializing";
470
- }
471
- function isClientCtxInitialized(state) {
472
- return !(isClientCtxNotInitialized(state) || isClientCtxInitializing(state));
473
- }
474
- function isClientCtxReady(state) {
475
- if (!isClientCtxInitialized(state)) return false;
476
- return bodhiJsCore.isClientReady(state);
477
- }
478
- function getClientInitState(state) {
479
- if (isClientCtxNotInitialized(state)) {
480
- return { ready: false, actualState: "not-initialized", mode: null };
481
- }
482
- if (isClientCtxInitializing(state)) {
483
- return { ready: false, actualState: "initializing", mode: null };
484
- }
485
- const mode = bodhiJsCore.isExtensionState(state) ? "extension" : "direct";
486
- const ready = bodhiJsCore.isClientReady(state);
487
- const actualState = bodhiJsCore.isExtensionState(state) ? state.extension : state.server.status === "not-connected" ? "not-connected" : "ready";
488
- return { ready, actualState, mode };
489
- }
490
- function getServerState(state) {
491
- if (!isClientCtxInitialized(state)) {
492
- return { ready: false, actualState: "n/a" };
493
- }
494
- const serverState = state.server;
495
- return {
496
- ready: serverState.status === "ready",
497
- actualState: serverState.status
498
- };
499
- }
500
- function isOverallReady(state) {
501
- if (!isClientCtxInitialized(state)) return false;
502
- return bodhiJsCore.isClientReady(state) && state.server.status === "ready";
503
- }
504
- const ClientCtxState = {
505
- // Type guards
506
- isNotInitialized: isClientCtxNotInitialized,
507
- isInitializing: isClientCtxInitializing,
508
- isInitialized: isClientCtxInitialized,
509
- isReady: isClientCtxReady,
510
- // Derived state
511
- getClientInitState,
512
- getServerState,
513
- isOverallReady
514
- };
515
- Object.defineProperty(exports, "createApiError", {
516
- enumerable: true,
517
- get: () => bodhiJsCore.createApiError
518
- });
519
- Object.defineProperty(exports, "createOperationError", {
520
- enumerable: true,
521
- get: () => bodhiJsCore.createOperationError
522
- });
523
- Object.defineProperty(exports, "isApiResultError", {
524
- enumerable: true,
525
- get: () => bodhiJsCore.isApiResultError
526
- });
527
- Object.defineProperty(exports, "isApiResultOperationError", {
528
- enumerable: true,
529
- get: () => bodhiJsCore.isApiResultOperationError
530
- });
531
- Object.defineProperty(exports, "isApiResultSuccess", {
532
- enumerable: true,
533
- get: () => bodhiJsCore.isApiResultSuccess
534
- });
535
- Object.defineProperty(exports, "isAuthError", {
536
- enumerable: true,
537
- get: () => bodhiJsCore.isAuthError
538
- });
539
- Object.defineProperty(exports, "isAuthLoggedIn", {
540
- enumerable: true,
541
- get: () => bodhiJsCore.isAuthLoggedIn
542
- });
543
- Object.defineProperty(exports, "isClientReady", {
544
- enumerable: true,
545
- get: () => bodhiJsCore.isClientReady
546
- });
547
- Object.defineProperty(exports, "isDirectState", {
548
- enumerable: true,
549
- get: () => bodhiJsCore.isDirectState
550
- });
551
- Object.defineProperty(exports, "isExtensionState", {
552
- enumerable: true,
553
- get: () => bodhiJsCore.isExtensionState
554
- });
555
- Object.defineProperty(exports, "isOperationError", {
556
- enumerable: true,
557
- get: () => bodhiJsCore.isOperationError
558
- });
559
- exports.BodhiProvider = BodhiProvider;
560
- exports.BodhiReactContext = BodhiReactContext;
561
- exports.ClientCtxState = ClientCtxState;
562
- exports.getClientInitState = getClientInitState;
563
- exports.getServerState = getServerState;
564
- exports.isClientCtxInitialized = isClientCtxInitialized;
565
- exports.isClientCtxInitializing = isClientCtxInitializing;
566
- exports.isClientCtxNotInitialized = isClientCtxNotInitialized;
567
- exports.isClientCtxReady = isClientCtxReady;
568
- exports.isOverallReady = isOverallReady;
569
- exports.useBodhi = useBodhi;
570
- //# sourceMappingURL=bodhi-react.cjs.js.map
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("react/jsx-runtime"),s=require("@bodhiapp/bodhi-js-core"),n=require("react"),m={MODAL_READY:"modal:ready",MODAL_REFRESH:"modal:refresh",MODAL_CLOSE:"modal:close",MODAL_COMPLETE:"modal:complete",MODAL_LNA_CONNECT:"modal:lna:connect",MODAL_LNA_SKIP:"modal:lna:skip",MODAL_CONFIRM_SERVER_INSTALL:"modal:confirm-server-install",MODAL_SELECT_CONNECTION:"modal:select-connection"};function H({client:e,modalHtmlPath:i,hideSetup:v,onSetupReady:C,setupState:I,logLevel:_="warn"}){const A=I!=="ready",O=n.useMemo(()=>new s.Logger("SetupModalProcessor",_),[_]),c=n.useRef(null),l=n.useMemo(()=>new s.BodhiClientUserPrefsManager,[]),S=n.useRef(null),g=n.useCallback(t=>t==="direct"?"lna":t==="extension"?"extension":null,[]),x=n.useCallback(t=>t.extension==="ready"?{status:"ready",version:"unknown",id:t.extensionId}:t.extension==="not-found"?{status:"not-installed",error:{message:"Extension not found",code:"ext-not-installed"}}:{status:"unreachable",error:{message:"Client not initialized",code:"ext-connection-failed"}},[]),p=n.useCallback(t=>{if(t.server.status==="pending-extension-ready")return{status:"pending-extension-ready",error:{message:"Extension not ready",code:"server-pending-ext-ready"}};const r=t.server;switch(r.status){case"ready":return{status:"ready",version:r.version};case"setup":return{status:"setup",version:r.version,error:{message:r.error.message,code:"server-in-setup-status"}};case"resource-admin":return{status:"resource-admin",version:r.version,error:{message:r.error.message,code:"server-in-admin-status"}};case"error":return{status:"error",error:{message:r.error.message,code:"server-unexpected-error"}};case"not-reachable":return{status:"unreachable",error:{message:r.error.message,code:"server-conn-refused"}}}},[]),h=n.useCallback(t=>{if(t.server.status==="not-connected")return{status:"pending-lna-ready"};const r=t.server;switch(r.status){case"ready":return{status:"ready",version:r.version};case"setup":return{status:"setup",version:r.version};case"resource-admin":return{status:"resource-admin",version:r.version};case"error":case"not-reachable":return{status:"error",error:{message:r.error.message}}}},[]),L=n.useCallback((t,r)=>{if(t.server.status!=="pending-extension-ready"){const o=t.server.status;if(o==="ready"||o==="setup"||o==="resource-admin")return!0}if(r.server.status!=="not-connected"){const o=r.server.status;if(o==="ready"||o==="setup"||o==="resource-admin")return!0}return!1},[]),R=n.useCallback((t,r,o)=>{if(r==="skipped")return{status:"skipped",serverUrl:o};if(t.server.status!=="not-connected"){const a=t.server;return a.status==="ready"||a.status==="setup"||a.status==="resource-admin"?{status:"granted",serverUrl:o}:{status:"unreachable",serverUrl:o,error:{message:a.error.message,code:"lna-unreachable"}}}return r==="granted"?{status:"granted",serverUrl:o}:{status:"prompt",serverUrl:o}},[]),d=n.useCallback(async(t=!1)=>{const r=s.detectBrowser().type,o=s.detectOS().type;let a=await e.getExtensionState();(a.extension==="not-initialized"||t)&&(a=await e.testExtensionConnectivity());let u=await e.getDirectState();const y=l.getDirectStatus();y==="granted"&&(u.server.status==="not-connected"||t)&&(u=await e.testDirectConnectivity());const E=s.getServerUrl(u)||"http://localhost:1135",F=R(u,y,E);return e.getConnectionMode()===null&&(s.isDirectServerReady(u)?await e.setConnectionMode("direct"):s.isExtensionServerReady(a)&&await e.setConnectionMode("extension")),l.isServerInstallConfirmed()||L(a,u)&&l.setServerInstallConfirmed(!0),{extension:x(a),server:p(a),lna:F,lnaServer:h(u),env:{browser:r,os:o},browsers:s.BROWSER_CONFIGS,os:s.OS_CONFIGS,userConfirmations:{serverInstall:l.isServerInstallConfirmed()},selectedConnection:g(e.getConnectionMode())}},[e,l,x,p,h,g,R,L]),f=n.useCallback(()=>{if(!S.current)throw new Error("Cannot get state: currentStateRef is null");return{...S.current,userConfirmations:{serverInstall:l.isServerInstallConfirmed()},selectedConnection:g(e.getConnectionMode())}},[l,e,g]),w=n.useMemo(()=>({[m.MODAL_READY]:async()=>(O.info("MODAL_READY: building initial state"),{setupState:f()}),[m.MODAL_REFRESH]:async()=>{var o;O.info("MODAL_REFRESH: refreshing state");const t=await d(!0);S.current=t;const r=f();return O.info(`MODAL_REFRESH: state refreshed
2
+ `,JSON.stringify(r,null,2)),(o=c.current)==null||o.updateState(f()),{setupState:r}},[m.MODAL_LNA_CONNECT]:async t=>{var u,y;const r=t.payload.serverUrl;console.log("[SetupModalProcessor] LNA connect:",r);const o=await e.testDirectConnectivity(r);if(o.server.status!=="not-connected"){const b=o.server.status;if(b==="ready"||b==="setup"||b==="resource-admin"){l.setDirectStatus("granted"),e.getConnectionMode()===null&&await e.setConnectionMode("direct");const E=await d();return S.current=E,(u=c.current)==null||u.updateState(f()),{success:!0}}}const a=await d();return S.current=a,(y=c.current)==null||y.updateState(f()),{success:!1}},[m.MODAL_LNA_SKIP]:async()=>{var r;l.setDirectStatus("skipped");const t=await d();return S.current=t,(r=c.current)==null||r.updateState(f()),{success:!0}},[m.MODAL_CLOSE]:()=>{v()},[m.MODAL_COMPLETE]:()=>{v()},[m.MODAL_CONFIRM_SERVER_INSTALL]:t=>{var r;return l.setServerInstallConfirmed(t.payload.confirmed),(r=c.current)==null||r.updateState(f()),{success:!0}},[m.MODAL_SELECT_CONNECTION]:async t=>{var a;const o=t.payload.connection==="lna"?"direct":"extension";return await e.setConnectionMode(o),(a=c.current)==null||a.updateState(f()),{success:!0}}}),[O,e,l,d,f,h,v]);return n.useEffect(()=>(c.current=new s.OnboardingModal({modalHtmlPath:i,handlers:w}),()=>{var t;(t=c.current)==null||t.destroy(),c.current=null}),[i,w]),n.useEffect(()=>{A&&c.current?d(!0).then(t=>{var r;S.current=t,(r=c.current)==null||r.show(t),C==null||C()}).catch(t=>{console.error("[SetupModalProcessor] buildSetupState failed:",t)}):!A&&c.current&&c.current.destroy()},[A,d,C]),null}const D=n.createContext(null);D.displayName="BodhiContext";function G({children:e,client:i,modalHtmlPath:v,handleCallback:C=!0,callbackUrl:I="/callback",logLevel:_="warn"}){const A=n.useRef(!1),O=n.useRef(!1),[c,l]=n.useState({type:"not-initialized"}),[S,g]=n.useState(null),[x,p]=n.useState(!1),[h,L]=n.useState("ready");n.useEffect(()=>{const a=u=>{switch(u.type){case"client-state":l(u.state);break;case"auth-state":g(u.state),p(!1);break}};return i.setStateCallback(a),()=>{i.setStateCallback(s.NOOP_STATE_CALLBACK)}},[i]);const R=n.useCallback(async()=>{L("loading")},[]),d=n.useCallback(()=>{L("ready")},[]),f=n.useCallback(()=>{L("loaded")},[]),w=n.useCallback(async a=>{l({type:"initializing"});try{await i.init(a||{})}catch(u){console.error("[BodhiProvider] Init failed:",u)}},[i]);n.useEffect(()=>{if(O.current)return;O.current=!0,(async()=>{if(await w(),!C)return;const u=new URL(window.location.href);if(u.pathname!==I)return;const y=u.searchParams.get("code"),b=u.searchParams.get("state");!y||!b||A.current||(A.current=!0,s.isWebUIClient(i)&&(p(!0),i.handleOAuthCallback(y,b).then(()=>{window.history.replaceState({},"","/")}).catch(E=>{console.error("OAuth callback failed:",E),g({isLoggedIn:!1,error:{message:E instanceof Error?E.message:"OAuth callback failed",code:"OAUTH_CALLBACK_FAILED"}}),p(!1),window.history.replaceState({},"","/")})))})()},[w,i,C,I]);const t=n.useCallback(async()=>{p(!0);try{await i.login()}catch(a){g({isLoggedIn:!1,error:{message:a instanceof Error?a.message:"Login failed",code:"LOGIN_FAILED"}}),p(!1)}},[i]),r=n.useCallback(async()=>{try{await i.logout()}catch(a){g({isLoggedIn:!1,error:{message:a instanceof Error?a.message:"Logout failed",code:"LOGOUT_FAILED"}})}},[i]),o=n.useMemo(()=>({client:i,clientState:c,auth:S,authLoading:x,login:t,logout:r,showSetup:R,hideSetup:d,setupState:h}),[i,c,S,x,h,t,r,R,d]);return N.jsxs(D.Provider,{value:o,children:[N.jsx(H,{client:i,modalHtmlPath:v,hideSetup:d,onSetupReady:f,setupState:h,logLevel:_}),e]})}function K(){const e=n.useContext(D);if(!e)throw new Error("useBodhi must be used within BodhiProvider");return{client:e.client,clientState:e.clientState,setupState:e.setupState,auth:e.auth,authLoading:e.authLoading,login:e.login,logout:e.logout,showSetup:e.showSetup,hideSetup:e.hideSetup}}function P(e){return e.type==="not-initialized"}function k(e){return e.type==="initializing"}function M(e){return!(P(e)||k(e))}function z(e){return M(e)?s.isClientReady(e):!1}function T(e){if(P(e))return{ready:!1,actualState:"not-initialized",mode:null};if(k(e))return{ready:!1,actualState:"initializing",mode:null};const i=s.isExtensionState(e)?"extension":"direct",v=s.isClientReady(e),C=s.isExtensionState(e)?e.extension:e.server.status==="not-connected"?"not-connected":"ready";return{ready:v,actualState:C,mode:i}}function j(e){if(!M(e))return{ready:!1,actualState:"n/a"};const i=e.server;return{ready:i.status==="ready",actualState:i.status}}function B(e){return M(e)?s.isClientReady(e)&&e.server.status==="ready":!1}const U={isNotInitialized:P,isInitializing:k,isInitialized:M,isReady:z,getClientInitState:T,getServerState:j,isOverallReady:B};Object.defineProperty(exports,"createApiError",{enumerable:!0,get:()=>s.createApiError});Object.defineProperty(exports,"createOperationError",{enumerable:!0,get:()=>s.createOperationError});Object.defineProperty(exports,"isApiResultError",{enumerable:!0,get:()=>s.isApiResultError});Object.defineProperty(exports,"isApiResultOperationError",{enumerable:!0,get:()=>s.isApiResultOperationError});Object.defineProperty(exports,"isApiResultSuccess",{enumerable:!0,get:()=>s.isApiResultSuccess});Object.defineProperty(exports,"isAuthError",{enumerable:!0,get:()=>s.isAuthError});Object.defineProperty(exports,"isAuthLoggedIn",{enumerable:!0,get:()=>s.isAuthLoggedIn});Object.defineProperty(exports,"isClientReady",{enumerable:!0,get:()=>s.isClientReady});Object.defineProperty(exports,"isDirectState",{enumerable:!0,get:()=>s.isDirectState});Object.defineProperty(exports,"isExtensionState",{enumerable:!0,get:()=>s.isExtensionState});Object.defineProperty(exports,"isOperationError",{enumerable:!0,get:()=>s.isOperationError});exports.BodhiProvider=G;exports.BodhiReactContext=D;exports.ClientCtxState=U;exports.getClientInitState=T;exports.getServerState=j;exports.isClientCtxInitialized=M;exports.isClientCtxInitializing=k;exports.isClientCtxNotInitialized=P;exports.isClientCtxReady=z;exports.isOverallReady=B;exports.useBodhi=K;