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