electrobun 2.0.1-beta.7 → 2.0.2-beta.1
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/README.md +29 -14
- package/bin/electrobun.cjs +13 -210
- package/bin/resolve-hutch.cjs +1363 -0
- package/lib/moved.cjs +12 -0
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Electrobun npm bootstrap
|
|
2
2
|
|
|
3
|
-
The `electrobun` npm package is a small
|
|
4
|
-
pnpm, Yarn, and Bun users. It contains no Electrobun runtime
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
The dependency-free `electrobun` npm package is a small Hutch entry point for
|
|
4
|
+
npm, pnpm, Yarn, and Bun users. It contains no Electrobun runtime, SDK, or
|
|
5
|
+
platform-specific npm dependency. On first use, the `electrobun` command reads
|
|
6
|
+
the `hutch-artifacts.json` index for its exact version, downloads the host's
|
|
7
|
+
matching Hutch archive from the indexed immutable Electrobun GitHub Release
|
|
8
|
+
URL, checks its byte length and SHA-256 digest against that index entry, caches
|
|
9
|
+
the extracted launcher and engine safely, and delegates all commands to `hutch
|
|
10
|
+
electrobun`. `electrobun init` additionally ensures a compatible global
|
|
11
|
+
Hutch launcher is present for the generated project's `hutch run ...` tasks;
|
|
12
|
+
init itself still runs through the exact private cache.
|
|
8
13
|
|
|
9
14
|
Create a project from the current stable template catalog:
|
|
10
15
|
|
|
@@ -18,17 +23,27 @@ Use the beta catalog only when you ask for it:
|
|
|
18
23
|
npx electrobun init --beta
|
|
19
24
|
```
|
|
20
25
|
|
|
21
|
-
The npm bootstrap
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
The npm bootstrap supplies the exact Hutch and Electrobun defaults paired with
|
|
27
|
+
its own release version. Those defaults are not overrides: published templates
|
|
28
|
+
include an exact `electrobun.version`, and an exact `// @hutch` pragma or
|
|
29
|
+
product pin in `hutch.config.ts` always wins. In an unpinned hand-written
|
|
30
|
+
project, the installed package version selects the toolchain for npm-launched
|
|
31
|
+
commands, so the single `electrobun` dependency and its immutable GitHub Release
|
|
32
|
+
assets ride the project lockfile together. Hutch
|
|
33
|
+
downloads the matching runtime and SDK into the shared
|
|
34
|
+
`~/.hutch/releases/electrobun` store and projects the SDKs into the project's
|
|
35
|
+
`.hutch/devkit` sysroot. Hutch's paired Cottontail is the build-time runtime;
|
|
36
|
+
the Cottontail bundled into an app is separately pinned by that resolved
|
|
37
|
+
Electrobun devkit. Installing a beta bootstrap still uses stable templates
|
|
38
|
+
unless `init` receives `--beta`.
|
|
27
39
|
|
|
28
40
|
`DASH_RELEASE_OFFLINE=1` prevents this bootstrap from downloading a missing
|
|
29
|
-
Hutch
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
Hutch archive. A previously verified cached copy can still run, but
|
|
42
|
+
`electrobun init` requires network access for the current template catalog and
|
|
43
|
+
selected template. Builds can run without network access only when every exact
|
|
44
|
+
release and managed toolchain they require is already installed. The flag does
|
|
45
|
+
not put Hutch's built-in dependency resolver or an external project package
|
|
46
|
+
manager into offline mode.
|
|
32
47
|
|
|
33
48
|
You can install Hutch directly from <https://hutch.blackboard.sh> and run the
|
|
34
49
|
same commands without npm:
|
package/bin/electrobun.cjs
CHANGED
|
@@ -1,58 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const { homedir, tmpdir } = require("node:os");
|
|
8
|
-
const path = require("node:path");
|
|
4
|
+
// The electrobun front door: resolves the vendored (or installed) Hutch and
|
|
5
|
+
// forwards every argument to `hutch electrobun`, supplying this release's
|
|
6
|
+
// paired toolchain versions as defaults for unpinned projects.
|
|
9
7
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function normalizeChannel(value) {
|
|
15
|
-
if (value === "stable") return "production";
|
|
16
|
-
if (value === "production" || value === "canary") return value;
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function hutchChannel(environment) {
|
|
21
|
-
for (const key of ["ELECTROBUN_HUTCH_CHANNEL", "HUTCH_ACTIVE_CHANNEL"]) {
|
|
22
|
-
const selected = normalizeChannel(environment[key]);
|
|
23
|
-
if (selected) return selected;
|
|
24
|
-
}
|
|
25
|
-
return "production";
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function environmentFlagEnabled(environment, name) {
|
|
29
|
-
const value = environment[name];
|
|
30
|
-
return (
|
|
31
|
-
value === "1" ||
|
|
32
|
-
(typeof value === "string" &&
|
|
33
|
-
["true", "yes"].includes(value.toLowerCase()))
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function hutchBinaryPath(channel, environment, platform, userHome) {
|
|
38
|
-
if (environment.ELECTROBUN_HUTCH_BINARY) {
|
|
39
|
-
return environment.ELECTROBUN_HUTCH_BINARY;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// HUTCH_HOME is Hutch's own home; DASH_HOME stays honored as a deprecated
|
|
43
|
-
// fallback for Dash Desktop and older setups.
|
|
44
|
-
const hutchHome =
|
|
45
|
-
environment.HUTCH_HOME ||
|
|
46
|
-
environment.DASH_HOME ||
|
|
47
|
-
path.join(userHome, ".hutch");
|
|
48
|
-
const command = channel === "canary" ? "hutch-canary" : "hutch";
|
|
49
|
-
const pathApi = platform === "win32" ? path.win32 : path.posix;
|
|
50
|
-
return pathApi.join(
|
|
51
|
-
hutchHome,
|
|
52
|
-
"bin",
|
|
53
|
-
`${command}${platform === "win32" ? ".exe" : ""}`,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
8
|
+
const {
|
|
9
|
+
resolveHutchBinary,
|
|
10
|
+
runHutch,
|
|
11
|
+
} = require("./resolve-hutch.cjs");
|
|
56
12
|
|
|
57
13
|
function hutchArguments(args) {
|
|
58
14
|
const forwarded = Array.from(args);
|
|
@@ -68,163 +24,15 @@ function hutchArguments(args) {
|
|
|
68
24
|
: [...forwarded, "--channel=stable"];
|
|
69
25
|
}
|
|
70
26
|
|
|
71
|
-
function download(url, redirects = 0) {
|
|
72
|
-
if (redirects > maxInstallerRedirects) {
|
|
73
|
-
return Promise.reject(new Error("too many installer redirects"));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let target;
|
|
77
|
-
try {
|
|
78
|
-
target = new URL(url);
|
|
79
|
-
} catch {
|
|
80
|
-
return Promise.reject(new Error("invalid Hutch installer URL"));
|
|
81
|
-
}
|
|
82
|
-
if (target.protocol !== "https:") {
|
|
83
|
-
return Promise.reject(
|
|
84
|
-
new Error("the Hutch installer must be downloaded over HTTPS"),
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return new Promise((resolve, reject) => {
|
|
89
|
-
const request = get(target, (response) => {
|
|
90
|
-
if (
|
|
91
|
-
response.statusCode >= 300 &&
|
|
92
|
-
response.statusCode < 400 &&
|
|
93
|
-
response.headers.location
|
|
94
|
-
) {
|
|
95
|
-
response.resume();
|
|
96
|
-
let redirected;
|
|
97
|
-
try {
|
|
98
|
-
redirected = new URL(response.headers.location, target);
|
|
99
|
-
} catch {
|
|
100
|
-
reject(new Error("invalid Hutch installer redirect URL"));
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
resolve(download(redirected.href, redirects + 1));
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (response.statusCode !== 200) {
|
|
108
|
-
response.resume();
|
|
109
|
-
reject(
|
|
110
|
-
new Error(
|
|
111
|
-
`installer download returned HTTP ${response.statusCode ?? "unknown"}`,
|
|
112
|
-
),
|
|
113
|
-
);
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const chunks = [];
|
|
118
|
-
let size = 0;
|
|
119
|
-
response.on("data", (chunk) => {
|
|
120
|
-
size += chunk.length;
|
|
121
|
-
if (size > maxInstallerBytes) {
|
|
122
|
-
request.destroy(
|
|
123
|
-
new Error("installer download exceeded the 1 MiB limit"),
|
|
124
|
-
);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
chunks.push(chunk);
|
|
128
|
-
});
|
|
129
|
-
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
130
|
-
response.on("error", reject);
|
|
131
|
-
});
|
|
132
|
-
request.on("error", reject);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function checkedSpawn(command, args, options) {
|
|
137
|
-
const result = spawnSync(command, args, options);
|
|
138
|
-
if (result.error) throw result.error;
|
|
139
|
-
if (result.status !== 0) {
|
|
140
|
-
throw new Error(
|
|
141
|
-
`${command} exited with status ${result.status ?? "unknown"}`,
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function installHutch({ channel, environment, platform }) {
|
|
147
|
-
const temporary = mkdtempSync(path.join(tmpdir(), "electrobun-hutch-"));
|
|
148
|
-
try {
|
|
149
|
-
if (platform === "win32") {
|
|
150
|
-
const installer = path.join(temporary, "install.ps1");
|
|
151
|
-
writeFileSync(
|
|
152
|
-
installer,
|
|
153
|
-
await download(`${installerBaseUrl}/install.ps1`),
|
|
154
|
-
);
|
|
155
|
-
checkedSpawn(
|
|
156
|
-
"powershell.exe",
|
|
157
|
-
[
|
|
158
|
-
"-NoProfile",
|
|
159
|
-
"-NonInteractive",
|
|
160
|
-
"-ExecutionPolicy",
|
|
161
|
-
"Bypass",
|
|
162
|
-
"-File",
|
|
163
|
-
installer,
|
|
164
|
-
"-Channel",
|
|
165
|
-
channel,
|
|
166
|
-
],
|
|
167
|
-
{ env: environment, stdio: "inherit" },
|
|
168
|
-
);
|
|
169
|
-
} else {
|
|
170
|
-
const installer = path.join(temporary, "install.sh");
|
|
171
|
-
writeFileSync(
|
|
172
|
-
installer,
|
|
173
|
-
await download(`${installerBaseUrl}/install.sh`),
|
|
174
|
-
{ mode: 0o700 },
|
|
175
|
-
);
|
|
176
|
-
checkedSpawn("sh", [installer, "--channel", channel], {
|
|
177
|
-
env: environment,
|
|
178
|
-
stdio: "inherit",
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
} finally {
|
|
182
|
-
rmSync(temporary, { force: true, recursive: true });
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function runHutch({ binary, args, environment }) {
|
|
187
|
-
const result = spawnSync(binary, ["electrobun", ...args], {
|
|
188
|
-
env: environment,
|
|
189
|
-
stdio: "inherit",
|
|
190
|
-
});
|
|
191
|
-
if (result.error) throw result.error;
|
|
192
|
-
if (result.status !== null) return result.status;
|
|
193
|
-
if (result.signal === "SIGINT") return 130;
|
|
194
|
-
if (result.signal === "SIGTERM") return 143;
|
|
195
|
-
return 1;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
27
|
async function main(options = {}) {
|
|
199
28
|
const args = hutchArguments(options.args ?? process.argv.slice(2));
|
|
200
29
|
const environment = options.environment ?? process.env;
|
|
201
|
-
const platform = options.platform ?? process.platform;
|
|
202
|
-
const userHome = options.userHome ?? homedir();
|
|
203
|
-
const fileExists = options.existsSync ?? existsSync;
|
|
204
|
-
const install = options.installHutch ?? installHutch;
|
|
205
30
|
const run = options.runHutch ?? runHutch;
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
throw new Error(`ELECTROBUN_HUTCH_BINARY does not exist: ${binary}`);
|
|
212
|
-
}
|
|
213
|
-
if (environmentFlagEnabled(environment, "DASH_RELEASE_OFFLINE")) {
|
|
214
|
-
throw new Error(
|
|
215
|
-
`Hutch is not installed at ${binary}; DASH_RELEASE_OFFLINE prevents downloading it`,
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
console.error(
|
|
219
|
-
`Electrobun requires Hutch; installing the latest ${channel} release...`,
|
|
220
|
-
);
|
|
221
|
-
await install({ channel, environment, platform });
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (!fileExists(binary)) {
|
|
225
|
-
throw new Error(`Hutch was not installed at ${binary}`);
|
|
226
|
-
}
|
|
227
|
-
return run({ binary, args, environment });
|
|
31
|
+
const binary = await resolveHutchBinary({
|
|
32
|
+
...options,
|
|
33
|
+
ensureGlobalHutch: args[0] === "init",
|
|
34
|
+
});
|
|
35
|
+
return run({ binary, args: ["electrobun", ...args], environment });
|
|
228
36
|
}
|
|
229
37
|
|
|
230
38
|
if (require.main === module) {
|
|
@@ -238,9 +46,4 @@ if (require.main === module) {
|
|
|
238
46
|
});
|
|
239
47
|
}
|
|
240
48
|
|
|
241
|
-
module.exports = {
|
|
242
|
-
hutchArguments,
|
|
243
|
-
hutchBinaryPath,
|
|
244
|
-
hutchChannel,
|
|
245
|
-
main,
|
|
246
|
-
};
|
|
49
|
+
module.exports = { hutchArguments, main };
|