@devframes/hub-ui-onboard 1.3.1 → 1.3.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/client/embedded.js +5 -20
- package/dist/index.d.mts +6 -4
- package/dist/index.mjs +48 -12
- package/package.json +6 -6
package/dist/client/embedded.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/client/.generated/css.ts
|
|
2
2
|
var e = import.meta.url, t = new URL("./", e), n = 1e3, r = "devframes-onboard-hidden", i = (e, n) => fetch(new URL(`__onboard/${e}`, t), n);
|
|
3
3
|
async function a() {
|
|
4
4
|
return (await i("status")).json();
|
|
@@ -112,21 +112,13 @@ function p(e) {
|
|
|
112
112
|
}
|
|
113
113
|
async function W() {
|
|
114
114
|
let e = await a();
|
|
115
|
-
|
|
116
|
-
t.remove(), await o();
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
U(e), e.state === "installing" && setTimeout(() => void W(), n);
|
|
115
|
+
e.state === "ready" ? (t.remove(), await o()) : (U(e), e.state === "installing" && setTimeout(() => void W(), n));
|
|
120
116
|
}
|
|
121
117
|
async function G() {
|
|
122
|
-
|
|
123
|
-
await W(), N.hidden = !1, N.textContent = h.refused;
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
U({
|
|
118
|
+
P.disabled = !0, I.textContent = h.confirming, (await i("install", { method: "POST" })).ok ? (U({
|
|
127
119
|
...e,
|
|
128
120
|
state: "installing"
|
|
129
|
-
}), await W();
|
|
121
|
+
}), await W()) : (await W(), N.hidden = !1, N.textContent = h.refused);
|
|
130
122
|
}
|
|
131
123
|
async function K() {
|
|
132
124
|
(await i("disable", { method: "POST" })).ok && t.remove();
|
|
@@ -136,13 +128,6 @@ function p(e) {
|
|
|
136
128
|
async function m() {
|
|
137
129
|
if (window.parent !== window || sessionStorage.getItem(r)) return;
|
|
138
130
|
let e = await a();
|
|
139
|
-
|
|
140
|
-
if (e.state === "ready") {
|
|
141
|
-
await o();
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
p(e);
|
|
145
|
-
}
|
|
131
|
+
e.state !== "disabled" && e.state !== "installed" && (e.state === "ready" ? await o() : p(e));
|
|
146
132
|
}
|
|
147
133
|
m();
|
|
148
|
-
//#endregion
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
1
|
/** A logo URL, or one per color scheme. Same shape as hub-ui's `BrandingLogo`. */
|
|
3
2
|
type OnboardingLogo = string | {
|
|
4
3
|
light: string;
|
|
@@ -54,6 +53,12 @@ interface CreateOnboardingOptions {
|
|
|
54
53
|
stateDir?: string;
|
|
55
54
|
/** Install as a devDependency. Default: `true`. */
|
|
56
55
|
dev?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Stop the install when it runs longer than this many milliseconds, and
|
|
58
|
+
* show an error that asks the user to run the command in a terminal.
|
|
59
|
+
* Default: `300_000` (5 minutes).
|
|
60
|
+
*/
|
|
61
|
+
installTimeout?: number;
|
|
57
62
|
branding?: OnboardingBranding;
|
|
58
63
|
messages?: Partial<OnboardingMessages>;
|
|
59
64
|
/**
|
|
@@ -102,8 +107,6 @@ interface Onboarding {
|
|
|
102
107
|
/** `<base>embedded.js`: the `<script type="module">` URL to inject. */
|
|
103
108
|
scriptSrc: string;
|
|
104
109
|
}
|
|
105
|
-
//#endregion
|
|
106
|
-
//#region src/index.d.ts
|
|
107
110
|
export declare function defaultMessages(productName: string): OnboardingMessages;
|
|
108
111
|
/**
|
|
109
112
|
* Create the stand-in for a hub UI that is not installed yet: a request
|
|
@@ -111,5 +114,4 @@ export declare function defaultMessages(productName: string): OnboardingMessages
|
|
|
111
114
|
* `__onboard/*` routes it talks to.
|
|
112
115
|
*/
|
|
113
116
|
export declare function createOnboarding(options: CreateOnboardingOptions): Onboarding;
|
|
114
|
-
//#endregion
|
|
115
117
|
export type { CreateOnboardingOptions, Onboarding, OnboardingBranding, OnboardingConfirmation, OnboardingHandler, OnboardingLogo, OnboardingMessages, OnboardingState, OnboardingStatus };
|
package/dist/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
import { Diagnostic, createConsoleReporter, defineDiagnostics } from "nostics";
|
|
10
10
|
import { detect, resolveCommand } from "package-manager-detector";
|
|
11
11
|
import { exec } from "tinyexec";
|
|
12
|
-
|
|
12
|
+
// src/diagnostics.ts
|
|
13
13
|
/**
|
|
14
14
|
* `DF90xx` is this package's range (see `.agents/08-diagnostics.md`). It
|
|
15
15
|
* uses `nostics` directly: the package must stay free of `devframe` so a
|
|
@@ -54,11 +54,14 @@ const diagnostics = defineDiagnostics({
|
|
|
54
54
|
DF9008: {
|
|
55
55
|
why: (p) => `Nobody allowed \`${p.command}\` within ${p.minutes} minutes. The install was refused.`,
|
|
56
56
|
fix: "Click Install again and answer in the terminal that runs the dev server. If the answer does not arrive, another program reads that terminal: pass an `onConfirm` function to `createOnboarding()`."
|
|
57
|
+
},
|
|
58
|
+
DF9009: {
|
|
59
|
+
why: (p) => `\`${p.command}\` did not finish within ${p.seconds} seconds, so it was stopped.`,
|
|
60
|
+
fix: "Run the command in a terminal to see where the package manager stops, then click Install again. If the install is only slow, raise `installTimeout`."
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
});
|
|
60
|
-
|
|
61
|
-
//#region src/install.ts
|
|
64
|
+
// src/install.ts
|
|
62
65
|
/** The `add` command for the project's package manager, `npm` when none is detected. */
|
|
63
66
|
async function resolveInstallCommand(plan) {
|
|
64
67
|
const agent = (await detect({ cwd: plan.cwd }))?.agent ?? "npm";
|
|
@@ -67,18 +70,35 @@ async function resolveInstallCommand(plan) {
|
|
|
67
70
|
}
|
|
68
71
|
/**
|
|
69
72
|
* Run the install and make sure that each named package landed in
|
|
70
|
-
* `<cwd>/node_modules`. Throws a `DF9001` / `DF9002` diagnostic on failure.
|
|
73
|
+
* `<cwd>/node_modules`. Throws a `DF9001` / `DF9002` / `DF9009` diagnostic on failure.
|
|
71
74
|
*/
|
|
72
75
|
async function runInstall(plan, command) {
|
|
73
|
-
const
|
|
76
|
+
const commandLine = [command.command, ...command.args].join(" ");
|
|
77
|
+
const child = exec(command.command, command.args, { nodeOptions: {
|
|
74
78
|
cwd: plan.cwd,
|
|
75
79
|
env: {
|
|
76
80
|
...process.env,
|
|
77
81
|
CI: "1"
|
|
78
82
|
}
|
|
79
83
|
} });
|
|
84
|
+
let timer;
|
|
85
|
+
let killed;
|
|
86
|
+
const timedOut = new Promise((resolve) => {
|
|
87
|
+
timer = setTimeout(() => {
|
|
88
|
+
killed = killTree(child);
|
|
89
|
+
killed.then(() => resolve(void 0));
|
|
90
|
+
}, plan.timeout);
|
|
91
|
+
});
|
|
92
|
+
const result = await Promise.race([child, timedOut]).finally(() => clearTimeout(timer));
|
|
93
|
+
if (killed || !result) {
|
|
94
|
+
await killed;
|
|
95
|
+
throw diagnostics.DF9009({
|
|
96
|
+
command: commandLine,
|
|
97
|
+
seconds: Math.round(plan.timeout / 1e3)
|
|
98
|
+
});
|
|
99
|
+
}
|
|
80
100
|
if (result.exitCode !== 0) throw diagnostics.DF9001({
|
|
81
|
-
command:
|
|
101
|
+
command: commandLine,
|
|
82
102
|
exitCode: result.exitCode,
|
|
83
103
|
stderr: result.stderr.trim().slice(-2048)
|
|
84
104
|
});
|
|
@@ -88,6 +108,23 @@ async function runInstall(plan, command) {
|
|
|
88
108
|
});
|
|
89
109
|
}
|
|
90
110
|
/**
|
|
111
|
+
* Kill the install with everything it started. On Windows, tinyexec runs a
|
|
112
|
+
* package manager's `.cmd` shim through `cmd.exe`, and a kill does not reach
|
|
113
|
+
* child processes, so `taskkill /T /F` ends the whole tree.
|
|
114
|
+
*/
|
|
115
|
+
async function killTree(child) {
|
|
116
|
+
const pid = child.process?.pid;
|
|
117
|
+
if (process.platform === "win32" && pid !== void 0) {
|
|
118
|
+
if ((await exec("taskkill", [
|
|
119
|
+
"/pid",
|
|
120
|
+
String(pid),
|
|
121
|
+
"/T",
|
|
122
|
+
"/F"
|
|
123
|
+
]).then((r) => r, () => void 0))?.exitCode === 0) return;
|
|
124
|
+
}
|
|
125
|
+
child.kill();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
91
128
|
* `true` when every named package is already in `<cwd>/node_modules`, so the
|
|
92
129
|
* onboarding has nothing to install. Path, URL and alias specs cannot be
|
|
93
130
|
* checked by name, so a plan made only of those is never "already installed".
|
|
@@ -109,8 +146,7 @@ function namedPackages(plan) {
|
|
|
109
146
|
function packageName(spec) {
|
|
110
147
|
return /^((?:@[\w.~-]+\/)?[\w.~-]+)(?:@[^/]*)?$/.exec(spec)?.[1];
|
|
111
148
|
}
|
|
112
|
-
|
|
113
|
-
//#region src/terminal-confirm.ts
|
|
149
|
+
// src/terminal-confirm.ts
|
|
114
150
|
const ANSWER_MINUTES = 5;
|
|
115
151
|
/**
|
|
116
152
|
* The default `onConfirm`: a `y/N` question in the terminal that runs the dev
|
|
@@ -149,9 +185,9 @@ function confirmInTerminal(command, input = process.stdin) {
|
|
|
149
185
|
input.on("data", onData);
|
|
150
186
|
});
|
|
151
187
|
}
|
|
152
|
-
|
|
153
|
-
//#region src/index.ts
|
|
188
|
+
// src/index.ts
|
|
154
189
|
const STATE_FILE = "hub-ui-onboard.json";
|
|
190
|
+
const DEFAULT_INSTALL_TIMEOUT = 3e5;
|
|
155
191
|
/** `devframes` → `/devframes/`, same rule as `normalizeHubBase` in `@devframes/hub`. */
|
|
156
192
|
function normalizeBase(base) {
|
|
157
193
|
return `/${base}/`.replace(/\/{2,}/g, "/");
|
|
@@ -245,7 +281,8 @@ function createOnboarding(options) {
|
|
|
245
281
|
const plan = {
|
|
246
282
|
cwd,
|
|
247
283
|
packages: options.packages,
|
|
248
|
-
dev: options.dev ?? true
|
|
284
|
+
dev: options.dev ?? true,
|
|
285
|
+
timeout: options.installTimeout ?? DEFAULT_INSTALL_TIMEOUT
|
|
249
286
|
};
|
|
250
287
|
const branding = options.branding ?? {};
|
|
251
288
|
const messages = {
|
|
@@ -375,5 +412,4 @@ function createOnboarding(options) {
|
|
|
375
412
|
scriptSrc: `${base}embedded.js`
|
|
376
413
|
};
|
|
377
414
|
}
|
|
378
|
-
//#endregion
|
|
379
415
|
export { createOnboarding, defaultMessages };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devframes/hub-ui-onboard",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.3",
|
|
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",
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"tinyexec": "^1.3.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^26.6.
|
|
37
|
-
"tsdown": "0.23.
|
|
36
|
+
"@types/node": "^26.6.6",
|
|
37
|
+
"tsdown": "^0.23.7",
|
|
38
38
|
"tsx": "^4.23.15",
|
|
39
|
-
"unocss": "^66.
|
|
40
|
-
"vite": "^8.3.
|
|
41
|
-
"vitest": "^5.0
|
|
39
|
+
"unocss": "^66.12.0",
|
|
40
|
+
"vite": "^8.3.6",
|
|
41
|
+
"vitest": "^5.1.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "pnpm run build:css && tsdown && vite build --config vite.client.config.ts",
|