@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.
- package/dist/bodhi-react.cjs.js +2 -583
- package/dist/bodhi-react.esm.js +302 -426
- package/package.json +2 -2
- package/dist/bodhi-react.cjs.js.map +0 -1
- package/dist/bodhi-react.esm.js.map +0 -1
package/dist/bodhi-react.cjs.js
CHANGED
|
@@ -1,583 +1,2 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
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;
|