@devframes/hub-ui-onboard 1.2.4 → 1.2.5
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/client/embedded.js +5 -1
- package/dist/index.d.mts +22 -1
- package/dist/index.mjs +51 -5
- package/package.json +1 -1
package/dist/client/embedded.js
CHANGED
|
@@ -119,10 +119,14 @@ function p(e) {
|
|
|
119
119
|
U(e), e.state === "installing" && setTimeout(() => void W(), n);
|
|
120
120
|
}
|
|
121
121
|
async function G() {
|
|
122
|
+
if (P.disabled = !0, I.textContent = h.confirming, !(await i("install", { method: "POST" })).ok) {
|
|
123
|
+
await W(), N.hidden = !1, N.textContent = h.refused;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
122
126
|
U({
|
|
123
127
|
...e,
|
|
124
128
|
state: "installing"
|
|
125
|
-
}), await
|
|
129
|
+
}), await W();
|
|
126
130
|
}
|
|
127
131
|
async function K() {
|
|
128
132
|
(await i("disable", { method: "POST" })).ok && t.remove();
|
package/dist/index.d.mts
CHANGED
|
@@ -19,6 +19,10 @@ interface OnboardingMessages {
|
|
|
19
19
|
/** Short sentence under the heading. */
|
|
20
20
|
description: string;
|
|
21
21
|
install: string;
|
|
22
|
+
/** Install button label while `onConfirm` waits for the developer. */
|
|
23
|
+
confirming: string;
|
|
24
|
+
/** Shown when `onConfirm` refused the install. */
|
|
25
|
+
refused: string;
|
|
22
26
|
/** Hides the button for this browser tab (`sessionStorage`). */
|
|
23
27
|
hide: string;
|
|
24
28
|
/** Writes the state file; the host stops injecting the button. */
|
|
@@ -30,6 +34,15 @@ interface OnboardingMessages {
|
|
|
30
34
|
}
|
|
31
35
|
/** A web-standard request handler the hub exposes once installed. */
|
|
32
36
|
type OnboardingHandler = (request: Request) => Response | Promise<Response>;
|
|
37
|
+
/** What `onConfirm` receives when the panel asks to install. */
|
|
38
|
+
interface OnboardingConfirmation {
|
|
39
|
+
/** The packages Install adds, as given in `packages`. */
|
|
40
|
+
packages: string[];
|
|
41
|
+
/** The exact command Install runs. */
|
|
42
|
+
command: string[];
|
|
43
|
+
/** The panel's request, for example to read its `Origin` or `User-Agent`. */
|
|
44
|
+
request: Request;
|
|
45
|
+
}
|
|
33
46
|
interface CreateOnboardingOptions {
|
|
34
47
|
/** Packages to install when the user clicks Install. Package specs as the package manager accepts them. */
|
|
35
48
|
packages: string[];
|
|
@@ -50,6 +63,14 @@ interface CreateOnboardingOptions {
|
|
|
50
63
|
* user to restart the dev server.
|
|
51
64
|
*/
|
|
52
65
|
onInstalled?: () => void | OnboardingHandler | Promise<void | OnboardingHandler>;
|
|
66
|
+
/**
|
|
67
|
+
* Ask the developer to allow an install that the panel requested; the
|
|
68
|
+
* dev server can be reachable from other devices. Return `true` to run
|
|
69
|
+
* it. Only one call is open at a time: clicks meanwhile share its answer.
|
|
70
|
+
* Default: a `y/N` question in the terminal, or a refusal when stdin is
|
|
71
|
+
* not a TTY.
|
|
72
|
+
*/
|
|
73
|
+
onConfirm?: (confirmation: OnboardingConfirmation) => boolean | Promise<boolean>;
|
|
53
74
|
}
|
|
54
75
|
type OnboardingState = 'idle' | 'installing' | 'installed' | 'ready' | 'error' | 'disabled';
|
|
55
76
|
/** The `GET <base>__onboard/status` body. */
|
|
@@ -90,4 +111,4 @@ export declare function defaultMessages(productName: string): OnboardingMessages
|
|
|
90
111
|
*/
|
|
91
112
|
export declare function createOnboarding(options: CreateOnboardingOptions): Onboarding;
|
|
92
113
|
//#endregion
|
|
93
|
-
export type { CreateOnboardingOptions, Onboarding, OnboardingBranding, OnboardingHandler, OnboardingLogo, OnboardingMessages, OnboardingState, OnboardingStatus };
|
|
114
|
+
export type { CreateOnboardingOptions, Onboarding, OnboardingBranding, OnboardingConfirmation, OnboardingHandler, OnboardingLogo, OnboardingMessages, OnboardingState, OnboardingStatus };
|
package/dist/index.mjs
CHANGED
|
@@ -38,6 +38,14 @@ const diagnostics = defineDiagnostics({
|
|
|
38
38
|
DF9004: {
|
|
39
39
|
why: (p) => `The onboarding state file ${p.file} could not be read or written.`,
|
|
40
40
|
fix: "Make sure that `stateDir` is writable, or point it at another directory."
|
|
41
|
+
},
|
|
42
|
+
DF9005: {
|
|
43
|
+
why: (p) => `A browser asked to run \`${p.command}\`. Type y and press Enter to allow it.`,
|
|
44
|
+
fix: "If you did not click Install, press Enter to refuse. Another device can reach this dev server, so bind it to localhost."
|
|
45
|
+
},
|
|
46
|
+
DF9006: {
|
|
47
|
+
why: (p) => `A browser asked to run \`${p.command}\`, but no interactive terminal can confirm it. The install was refused.`,
|
|
48
|
+
fix: "Run the command yourself, or pass an `onConfirm` function to `createOnboarding()`."
|
|
41
49
|
}
|
|
42
50
|
}
|
|
43
51
|
});
|
|
@@ -94,6 +102,26 @@ function packageName(spec) {
|
|
|
94
102
|
return /^((?:@[\w.~-]+\/)?[\w.~-]+)(?:@[^/]*)?$/.exec(spec)?.[1];
|
|
95
103
|
}
|
|
96
104
|
//#endregion
|
|
105
|
+
//#region src/terminal-confirm.ts
|
|
106
|
+
/**
|
|
107
|
+
* The default `onConfirm`: a `y/N` question in the terminal that runs the dev
|
|
108
|
+
* server. Without an interactive terminal nobody can answer, so it refuses.
|
|
109
|
+
*/
|
|
110
|
+
function confirmInTerminal(command, input = process.stdin) {
|
|
111
|
+
if (!input.isTTY) {
|
|
112
|
+
diagnostics.DF9006({ command: command.join(" ") });
|
|
113
|
+
return Promise.resolve(false);
|
|
114
|
+
}
|
|
115
|
+
diagnostics.DF9005({ command: command.join(" ") });
|
|
116
|
+
const flowing = input.readableFlowing;
|
|
117
|
+
return new Promise((resolve) => {
|
|
118
|
+
input.once("data", (chunk) => {
|
|
119
|
+
if (!flowing) input.pause();
|
|
120
|
+
resolve(/^y(?:es)?$/i.test(String(chunk).trim()));
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
97
125
|
//#region src/index.ts
|
|
98
126
|
const STATE_FILE = "hub-ui-onboard.json";
|
|
99
127
|
/** `devframes` → `/devframes/`, same rule as `normalizeHubBase` in `@devframes/hub`. */
|
|
@@ -115,6 +143,8 @@ function defaultMessages(productName) {
|
|
|
115
143
|
title: productName,
|
|
116
144
|
description: `${productName} enables development features in your project. Install it with:`,
|
|
117
145
|
install: `Install ${productName}`,
|
|
146
|
+
confirming: "Confirm in your terminal...",
|
|
147
|
+
refused: "The dev server did not allow the install. Check its terminal, or run the command above yourself.",
|
|
118
148
|
hide: "Hide in this session",
|
|
119
149
|
disable: "Disable entirely",
|
|
120
150
|
installing: "Installing...",
|
|
@@ -258,11 +288,29 @@ function createOnboarding(options) {
|
|
|
258
288
|
state = "disabled";
|
|
259
289
|
return new Response(null, { status: 204 });
|
|
260
290
|
}
|
|
291
|
+
const commandLine = async () => {
|
|
292
|
+
const resolved = await getCommand();
|
|
293
|
+
return [resolved.command, ...resolved.args];
|
|
294
|
+
};
|
|
295
|
+
const onConfirm = options.onConfirm ?? (({ command }) => confirmInTerminal(command));
|
|
296
|
+
let asking;
|
|
297
|
+
async function startInstall(request) {
|
|
298
|
+
if (state !== "idle" && state !== "error") return new Response(null, { status: 202 });
|
|
299
|
+
asking ??= commandLine().then((command) => onConfirm({
|
|
300
|
+
packages: plan.packages,
|
|
301
|
+
command,
|
|
302
|
+
request
|
|
303
|
+
})).finally(() => {
|
|
304
|
+
asking = void 0;
|
|
305
|
+
});
|
|
306
|
+
if (!await asking) return new Response(null, { status: 403 });
|
|
307
|
+
if (state === "idle" || state === "error") install();
|
|
308
|
+
return new Response(null, { status: 202 });
|
|
309
|
+
}
|
|
261
310
|
async function status() {
|
|
262
|
-
const command = await getCommand();
|
|
263
311
|
return {
|
|
264
312
|
state,
|
|
265
|
-
command:
|
|
313
|
+
command: await commandLine(),
|
|
266
314
|
branding,
|
|
267
315
|
messages,
|
|
268
316
|
error
|
|
@@ -281,9 +329,7 @@ function createOnboarding(options) {
|
|
|
281
329
|
if (route === "GET __onboard/status") return json(await status());
|
|
282
330
|
if (route === "POST __onboard/install" || route === "POST __onboard/disable") {
|
|
283
331
|
if (!isSameOrigin(request)) return new Response(null, { status: 403 });
|
|
284
|
-
|
|
285
|
-
if (state === "idle" || state === "error") install();
|
|
286
|
-
return new Response(null, { status: 202 });
|
|
332
|
+
return route.endsWith("disable") ? disable() : startInstall(request);
|
|
287
333
|
}
|
|
288
334
|
return new Response(null, { status: 404 });
|
|
289
335
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devframes/hub-ui-onboard",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.5",
|
|
5
5
|
"description": "A tiny stand-in for the devframe hub UI: a floating button that installs it on demand.",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|