@wolfstar/cli 0.1.0-next-20260904111115

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.
Files changed (65) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +151 -0
  3. package/dist/args-Ch1KXUMn.js +24 -0
  4. package/dist/args-Ch1KXUMn.js.map +1 -0
  5. package/dist/build-Ct6zabKg.js +22 -0
  6. package/dist/build-Ct6zabKg.js.map +1 -0
  7. package/dist/build-DU76s-Ec.js +39 -0
  8. package/dist/build-DU76s-Ec.js.map +1 -0
  9. package/dist/builders-BzLMIm9Y.js +44 -0
  10. package/dist/builders-BzLMIm9Y.js.map +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +58 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/codegen-DjPOn7cp.js +95 -0
  15. package/dist/codegen-DjPOn7cp.js.map +1 -0
  16. package/dist/codegen-mYkL-GBf.js +36 -0
  17. package/dist/codegen-mYkL-GBf.js.map +1 -0
  18. package/dist/commands-BkGKs_Bg.js +88 -0
  19. package/dist/commands-BkGKs_Bg.js.map +1 -0
  20. package/dist/commands-Dw2LFWV-.js +233 -0
  21. package/dist/commands-Dw2LFWV-.js.map +1 -0
  22. package/dist/dev-DWg8l23a.js +604 -0
  23. package/dist/dev-DWg8l23a.js.map +1 -0
  24. package/dist/dev-QUxuLcXj.js +30 -0
  25. package/dist/dev-QUxuLcXj.js.map +1 -0
  26. package/dist/errors-Bket6XFn.js +50 -0
  27. package/dist/errors-Bket6XFn.js.map +1 -0
  28. package/dist/external-Bk4EESdT.js +59 -0
  29. package/dist/external-Bk4EESdT.js.map +1 -0
  30. package/dist/index.d.ts +66 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +5 -0
  33. package/dist/info-B-8rAIn6.js +106 -0
  34. package/dist/info-B-8rAIn6.js.map +1 -0
  35. package/dist/info-DRTYYvVB.js +30 -0
  36. package/dist/info-DRTYYvVB.js.map +1 -0
  37. package/dist/none-DEo7zEr9.js +50 -0
  38. package/dist/none-DEo7zEr9.js.map +1 -0
  39. package/dist/output-mode-ChrOf0LO.js +46 -0
  40. package/dist/output-mode-ChrOf0LO.js.map +1 -0
  41. package/dist/plain-CEe4X_Cz.js +43 -0
  42. package/dist/plain-CEe4X_Cz.js.map +1 -0
  43. package/dist/prepare-BXcm2Ukp.js +3 -0
  44. package/dist/prepare-CVf88_Or.js +36 -0
  45. package/dist/prepare-CVf88_Or.js.map +1 -0
  46. package/dist/prepare-D5NZ0w4t.js +68 -0
  47. package/dist/prepare-D5NZ0w4t.js.map +1 -0
  48. package/dist/process-supervisor-CJOdmp0W.js +128 -0
  49. package/dist/process-supervisor-CJOdmp0W.js.map +1 -0
  50. package/dist/project-DHF3qdm-.js +66 -0
  51. package/dist/project-DHF3qdm-.js.map +1 -0
  52. package/dist/tsc-BqZfDTD0.js +133 -0
  53. package/dist/tsc-BqZfDTD0.js.map +1 -0
  54. package/dist/tsc-CovVAtfo.js +3 -0
  55. package/dist/tsdown-DlAs0rSI.js +104 -0
  56. package/dist/tsdown-DlAs0rSI.js.map +1 -0
  57. package/dist/tui-BTRFsLQR.js +770 -0
  58. package/dist/tui-BTRFsLQR.js.map +1 -0
  59. package/dist/tunnel-B8Id4-lR.js +205 -0
  60. package/dist/tunnel-B8Id4-lR.js.map +1 -0
  61. package/dist/version-DN6Uho-Y.js +30 -0
  62. package/dist/version-DN6Uho-Y.js.map +1 -0
  63. package/dist/vite-D3vYFsqa.js +93 -0
  64. package/dist/vite-D3vYFsqa.js.map +1 -0
  65. package/package.json +95 -0
@@ -0,0 +1,604 @@
1
+ import { n as ExitCode, t as CliError } from "./errors-Bket6XFn.js";
2
+ import { i as shouldUseColor, n as prefersReducedMotion, r as resolveOutputMode } from "./output-mode-ChrOf0LO.js";
3
+ import { n as resolveCwd } from "./args-Ch1KXUMn.js";
4
+ import { n as createLineSplitter, t as ProcessSupervisor } from "./process-supervisor-CJOdmp0W.js";
5
+ import { r as resolveBinary } from "./project-DHF3qdm-.js";
6
+ import { n as resolveTscBinary } from "./tsc-BqZfDTD0.js";
7
+ import { n as createBuilder, t as assertSupportedExperiments } from "./builders-BzLMIm9Y.js";
8
+ import { t as prepareAutoImports } from "./prepare-D5NZ0w4t.js";
9
+ import { t as Tunnel } from "./tunnel-B8Id4-lR.js";
10
+ import { loadStarsConfig } from "@wolfstar/http-framework/config";
11
+ import { createWriteStream, mkdirSync } from "node:fs";
12
+ import { dirname } from "node:path";
13
+ import { EventEmitter } from "node:events";
14
+ import { spawn } from "node:child_process";
15
+ import { lookup } from "node:dns/promises";
16
+
17
+ //#region src/lib/log-buffer.ts
18
+ /**
19
+ * A bounded, in-memory log store shared by the dev service and its renderers.
20
+ */
21
+ var LogBuffer = class extends EventEmitter {
22
+ capacity;
23
+ #entries = [];
24
+ #nextId = 1;
25
+ constructor(capacity = 2e3) {
26
+ super();
27
+ this.capacity = capacity;
28
+ }
29
+ push(input) {
30
+ const entry = {
31
+ id: this.#nextId++,
32
+ time: Date.now(),
33
+ ...input
34
+ };
35
+ this.#entries.push(entry);
36
+ if (this.#entries.length > this.capacity) this.#entries.splice(0, this.#entries.length - this.capacity);
37
+ this.emit("entry", entry);
38
+ return entry;
39
+ }
40
+ entries() {
41
+ return this.#entries;
42
+ }
43
+ filter(filter) {
44
+ const query = filter.query?.toLowerCase() ?? null;
45
+ return this.#entries.filter((entry) => (!filter.source || entry.source === filter.source) && (!filter.level || matchesLevel(entry.level, filter.level)) && (!query || entry.text.toLowerCase().includes(query)));
46
+ }
47
+ clear() {
48
+ this.#entries = [];
49
+ this.emit("clear");
50
+ }
51
+ };
52
+ const LEVEL_ORDER = {
53
+ debug: 0,
54
+ info: 1,
55
+ success: 1,
56
+ warn: 2,
57
+ error: 3
58
+ };
59
+ /**
60
+ * Whether `level` is at least as severe as `minimum`.
61
+ */
62
+ function matchesLevel(level, minimum) {
63
+ return LEVEL_ORDER[level] >= LEVEL_ORDER[minimum];
64
+ }
65
+
66
+ //#endregion
67
+ //#region src/lib/typechecker.ts
68
+ const WATCH_START = /Starting (incremental )?compilation/;
69
+ const WATCH_DONE = /Found (\d+) errors?\. Watching for file changes/;
70
+ const ONE_SHOT_DONE = /Found (\d+) errors?/;
71
+ const ERROR_LINE = /error TS\d+:/;
72
+ /**
73
+ * Runs a type checker next to the bot and reports type errors on their own channel, without ever blocking builds or
74
+ * restarts: `stars dev` keeps running whatever the build tool produced, the way Seedcord's `hmr.typecheck` and
75
+ * Vite's `checker` plugin do.
76
+ *
77
+ * `tsc` and `golar` (which forwards to TypeScript) watch the project themselves; `tsz` has no watch mode, so
78
+ * {@link Typechecker.check} re-runs it after every build instead.
79
+ */
80
+ var Typechecker = class extends EventEmitter {
81
+ config;
82
+ #child = null;
83
+ #state = "idle";
84
+ #errors = 0;
85
+ #pending = false;
86
+ constructor(config) {
87
+ super();
88
+ this.config = config;
89
+ }
90
+ get state() {
91
+ return this.#state;
92
+ }
93
+ get errors() {
94
+ return this.#errors;
95
+ }
96
+ /** Whether the checker re-runs per build instead of watching on its own. */
97
+ get oneShot() {
98
+ return this.config.dev.typecheck.enabled && !resolveTypecheckCommand(this.config).watch;
99
+ }
100
+ start() {
101
+ if (!this.config.dev.typecheck.enabled) return;
102
+ this.#run();
103
+ }
104
+ /**
105
+ * Re-runs a checker that cannot watch. Called after every successful build; a run already in flight is replaced,
106
+ * so a fast series of builds only leaves the last check standing.
107
+ */
108
+ check() {
109
+ if (!this.oneShot) return;
110
+ if (this.#child) {
111
+ this.#pending = true;
112
+ this.#child.kill();
113
+ return;
114
+ }
115
+ this.#run();
116
+ }
117
+ close() {
118
+ const child = this.#child;
119
+ this.#child = null;
120
+ this.#pending = false;
121
+ if (!child || child.exitCode !== null) return Promise.resolve();
122
+ return new Promise((resolve) => {
123
+ child.once("exit", () => resolve());
124
+ child.kill();
125
+ });
126
+ }
127
+ #run() {
128
+ const { command, args, node, watch } = resolveTypecheckCommand(this.config);
129
+ const child = spawn(node ? process.execPath : command, node ? [command, ...args] : [...args], {
130
+ cwd: this.config.root,
131
+ env: process.env,
132
+ stdio: [
133
+ "ignore",
134
+ "pipe",
135
+ "pipe"
136
+ ],
137
+ windowsHide: true
138
+ });
139
+ this.#child = child;
140
+ this.#setState("checking", 0);
141
+ let errors = 0;
142
+ const handleLine = (line) => {
143
+ if (ERROR_LINE.test(line)) errors++;
144
+ this.#handleLine(line);
145
+ };
146
+ const stdout = createLineSplitter(handleLine);
147
+ const stderr = createLineSplitter(handleLine);
148
+ child.stdout?.on("data", stdout.push);
149
+ child.stderr?.on("data", stderr.push);
150
+ child.once("exit", (code) => {
151
+ stdout.flush();
152
+ stderr.flush();
153
+ this.#child = null;
154
+ if (!watch && this.#state === "checking") this.#setState(code === 0 && errors === 0 ? "ok" : "failed", errors);
155
+ if (this.#pending) {
156
+ this.#pending = false;
157
+ this.#run();
158
+ }
159
+ });
160
+ child.once("error", (error) => {
161
+ this.#child = null;
162
+ this.emit("log", "error", error.code === "ENOENT" ? `${command} is not installed` : error.message);
163
+ this.#setState("failed", this.#errors);
164
+ });
165
+ }
166
+ #handleLine(line) {
167
+ const text = line.trim();
168
+ if (text.length === 0) return;
169
+ if (WATCH_START.test(text)) {
170
+ this.#setState("checking", 0);
171
+ return;
172
+ }
173
+ if (ERROR_LINE.test(text)) {
174
+ this.emit("log", "error", text);
175
+ return;
176
+ }
177
+ const done = WATCH_DONE.exec(text) ?? ONE_SHOT_DONE.exec(text);
178
+ if (done) {
179
+ const errors = Number(done[1]);
180
+ this.#setState(errors === 0 ? "ok" : "failed", errors);
181
+ this.emit("log", errors === 0 ? "success" : "error", errors === 0 ? "No type errors" : `${errors} type error${errors === 1 ? "" : "s"}`);
182
+ return;
183
+ }
184
+ this.emit("log", "info", text);
185
+ }
186
+ #setState(state, errors) {
187
+ this.#state = state;
188
+ this.#errors = errors;
189
+ this.emit("state", state, errors);
190
+ }
191
+ };
192
+ /**
193
+ * Builds the command line of the configured type checker, resolved from the project's own `node_modules`.
194
+ *
195
+ * @throws {CliError} when the checker is not installed in the project.
196
+ */
197
+ function resolveTypecheckCommand(config) {
198
+ const { checker, tsconfig } = config.dev.typecheck;
199
+ const project = tsconfig;
200
+ switch (checker) {
201
+ case "golar": {
202
+ const golar = resolveBinary(config.root, "golar", "golar");
203
+ if (!golar) throw missing(config, "golar", "Install it with `pnpm add -D golar`");
204
+ return {
205
+ command: golar,
206
+ args: [
207
+ "tsc",
208
+ ...WATCH_ARGS,
209
+ "-p",
210
+ project
211
+ ],
212
+ node: true,
213
+ watch: true
214
+ };
215
+ }
216
+ case "tsz": {
217
+ const tsz = resolveBinary(config.root, "@mohsen-azimi/tsz-dev", "tsz");
218
+ if (tsz) return {
219
+ command: tsz,
220
+ args: [
221
+ "--noEmit",
222
+ "-p",
223
+ project
224
+ ],
225
+ node: true,
226
+ watch: false
227
+ };
228
+ const tryTsz = resolveBinary(config.root, "try-tsz", "try-tsz");
229
+ if (tryTsz) return {
230
+ command: tryTsz,
231
+ args: ["-p", project],
232
+ node: true,
233
+ watch: false
234
+ };
235
+ throw missing(config, "tsz", "Install it with `pnpm add -D @mohsen-azimi/tsz-dev` (or `try-tsz`)");
236
+ }
237
+ default: {
238
+ const tsc = resolveTscBinary(config.root);
239
+ if (!tsc) throw missing(config, "typescript", "Install it with `pnpm add -D typescript`");
240
+ return {
241
+ command: tsc,
242
+ args: [
243
+ ...WATCH_ARGS,
244
+ "-p",
245
+ project
246
+ ],
247
+ node: true,
248
+ watch: true
249
+ };
250
+ }
251
+ }
252
+ }
253
+ const WATCH_ARGS = [
254
+ "--noEmit",
255
+ "--watch",
256
+ "--preserveWatchOutput",
257
+ "--pretty",
258
+ "false"
259
+ ];
260
+ function missing(config, name, install) {
261
+ return new CliError(`"${name}" is not installed in ${config.root}`, {
262
+ code: "DEPENDENCY_MISSING",
263
+ hint: `${install}, pick another \`dev.typecheck.checker\`, or set \`dev.typecheck\` to false.`
264
+ });
265
+ }
266
+
267
+ //#endregion
268
+ //#region src/lib/dev-service.ts
269
+ /**
270
+ * The headless heart of `stars dev`: builds, (re)starts the bot and exposes state and logs.
271
+ * Renderers (plain or TUI) only subscribe to it, they never own behaviour.
272
+ */
273
+ var DevService = class extends EventEmitter {
274
+ config;
275
+ logs;
276
+ builder;
277
+ supervisor;
278
+ typechecker;
279
+ tunnel;
280
+ #build = "idle";
281
+ #health = "unknown";
282
+ #restarts = 0;
283
+ #lastRestartReason = null;
284
+ #lastBuild = null;
285
+ #lastExit = null;
286
+ #restartTimer = null;
287
+ #healthTimer = null;
288
+ #pendingReason = null;
289
+ #stopped = false;
290
+ #queue = Promise.resolve();
291
+ constructor(config, options) {
292
+ super();
293
+ this.config = config;
294
+ this.logs = options.logs ?? new LogBuffer();
295
+ this.builder = options.builder;
296
+ this.supervisor = options.supervisor ?? createSupervisor(config);
297
+ this.typechecker = options.typechecker ?? new Typechecker(config);
298
+ this.tunnel = options.tunnel ?? new Tunnel(config);
299
+ this.builder.on("start", () => this.#setBuild("building"));
300
+ this.builder.on("success", (outcome) => this.#onBuildSuccess(outcome));
301
+ this.builder.on("failure", (outcome) => this.#onBuildFailure(outcome));
302
+ this.builder.on("log", (level, text) => this.log("build", level, text));
303
+ this.supervisor.on("state", () => this.#emitStatus());
304
+ this.supervisor.on("stdout", (line) => this.log("app", "info", line));
305
+ this.supervisor.on("stderr", (line) => this.log("app", "error", line));
306
+ this.supervisor.on("error", (error) => this.log("stars", "error", `Failed to start the bot: ${error.message}`));
307
+ this.supervisor.on("exit", (exit) => this.#onExit(exit));
308
+ this.typechecker.on("log", (level, text) => this.log("tsc", level, text));
309
+ this.typechecker.on("state", () => this.#emitStatus());
310
+ this.tunnel.on("log", (level, text) => this.log("tunnel", level, text));
311
+ this.tunnel.on("state", () => this.#emitStatus());
312
+ if (config.dev.url && config.dev.health) {
313
+ const interval = options.healthInterval ?? 5e3;
314
+ this.#healthTimer = setInterval(() => void this.#checkHealth(), interval);
315
+ this.#healthTimer.unref();
316
+ }
317
+ }
318
+ get status() {
319
+ return {
320
+ process: this.supervisor.state,
321
+ build: this.#build,
322
+ health: this.#health,
323
+ pid: this.supervisor.pid,
324
+ startedAt: this.supervisor.startedAt,
325
+ restarts: this.#restarts,
326
+ lastRestartReason: this.#lastRestartReason,
327
+ lastBuild: this.#lastBuild,
328
+ lastExit: this.#lastExit,
329
+ url: this.config.dev.url,
330
+ typecheck: this.typechecker.state,
331
+ typeErrors: this.typechecker.errors,
332
+ tunnel: this.tunnel.state,
333
+ tunnelUrl: this.tunnel.url
334
+ };
335
+ }
336
+ log(source, level, text) {
337
+ this.logs.push({
338
+ source,
339
+ level,
340
+ text
341
+ });
342
+ }
343
+ /**
344
+ * Starts watching; the bot starts after the first successful build.
345
+ */
346
+ async start() {
347
+ this.#stopped = false;
348
+ this.log("stars", "info", `Watching ${this.config.build.tool === "none" ? "sources" : `with ${this.config.build.tool}`}, entry ${this.config.entry}`);
349
+ if (this.config.dev.typecheck.enabled) this.typechecker.start();
350
+ this.tunnel.start();
351
+ await this.builder.watch();
352
+ }
353
+ /**
354
+ * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).
355
+ */
356
+ restart(reason = "manual") {
357
+ this.#clearRestartTimer();
358
+ return this.#enqueue(async () => {
359
+ if (this.#stopped) return;
360
+ this.#lastRestartReason = reason;
361
+ if (this.supervisor.running) {
362
+ this.#restarts++;
363
+ this.log("stars", "info", `Restarting (${describeReason(reason)})`);
364
+ await this.supervisor.stop();
365
+ } else this.log("stars", "info", `Starting (${describeReason(reason)})`);
366
+ if (this.#stopped) return;
367
+ this.supervisor.start();
368
+ this.#health = "unknown";
369
+ this.#emitStatus();
370
+ });
371
+ }
372
+ /**
373
+ * Stops the watcher and the bot. Safe to call more than once.
374
+ */
375
+ async stop() {
376
+ this.#stopped = true;
377
+ this.#clearRestartTimer();
378
+ if (this.#healthTimer) clearInterval(this.#healthTimer);
379
+ this.#healthTimer = null;
380
+ await this.#enqueue(async () => {
381
+ await Promise.allSettled([
382
+ this.builder.close(),
383
+ this.supervisor.stop(),
384
+ this.typechecker.close(),
385
+ this.tunnel.close()
386
+ ]);
387
+ });
388
+ }
389
+ clearLogs() {
390
+ this.logs.clear();
391
+ }
392
+ #onBuildSuccess(outcome) {
393
+ this.#lastBuild = outcome;
394
+ this.#setBuild("ok");
395
+ if (this.#stopped) return;
396
+ this.typechecker.check();
397
+ this.log("stars", "success", this.builder.tool === "none" ? "Sources changed" : `Build succeeded in ${outcome.durationMs}ms`);
398
+ this.#scheduleRestart(this.supervisor.state === "idle" ? "initial" : "build");
399
+ }
400
+ #onBuildFailure(outcome) {
401
+ this.#lastBuild = outcome;
402
+ this.#setBuild("failed");
403
+ this.#clearRestartTimer();
404
+ this.log("stars", "error", `Build failed${outcome.message ? `: ${outcome.message}` : ""}, waiting for changes`);
405
+ }
406
+ #onExit(exit) {
407
+ this.#lastExit = exit;
408
+ this.#health = "unknown";
409
+ if (!exit.requested) {
410
+ const how = exit.signal ? `signal ${exit.signal}` : `code ${exit.code}`;
411
+ this.log("stars", exit.code === 0 ? "warn" : "error", `The bot exited with ${how}, waiting for changes (press r to restart)`);
412
+ }
413
+ this.#emitStatus();
414
+ }
415
+ #scheduleRestart(reason) {
416
+ this.#pendingReason = reason;
417
+ this.#clearRestartTimer();
418
+ this.#restartTimer = setTimeout(() => {
419
+ this.#restartTimer = null;
420
+ const pending = this.#pendingReason ?? reason;
421
+ this.#pendingReason = null;
422
+ this.restart(pending);
423
+ }, this.config.dev.debounce);
424
+ }
425
+ #clearRestartTimer() {
426
+ if (this.#restartTimer) clearTimeout(this.#restartTimer);
427
+ this.#restartTimer = null;
428
+ }
429
+ #enqueue(task) {
430
+ this.#queue = this.#queue.then(task, task);
431
+ return this.#queue;
432
+ }
433
+ #setBuild(state) {
434
+ if (this.#build === state) return;
435
+ this.#build = state;
436
+ this.#emitStatus();
437
+ }
438
+ async #checkHealth() {
439
+ if (!this.supervisor.running || !this.config.dev.url || !this.config.dev.health) return;
440
+ let next;
441
+ try {
442
+ next = (await fetch(new URL(this.config.dev.health, this.config.dev.url), { signal: AbortSignal.timeout(2e3) })).status < 500 ? "ok" : "down";
443
+ } catch {
444
+ next = "down";
445
+ }
446
+ if (next !== this.#health) {
447
+ this.#health = next;
448
+ this.#emitStatus();
449
+ }
450
+ }
451
+ #emitStatus() {
452
+ this.emit("status", this.status);
453
+ }
454
+ };
455
+ function createSupervisor(config) {
456
+ return new ProcessSupervisor({
457
+ command: process.execPath,
458
+ args: [
459
+ ...config.dev.nodeArgs,
460
+ config.build.output,
461
+ ...config.dev.args
462
+ ],
463
+ cwd: config.root,
464
+ env: {
465
+ ...process.env,
466
+ ...config.dev.env,
467
+ STARS_DEV: "1",
468
+ FORCE_COLOR: process.env.FORCE_COLOR ?? (process.stdout.isTTY ? "1" : void 0)
469
+ },
470
+ killTimeout: config.dev.killTimeout
471
+ });
472
+ }
473
+ function describeReason(reason) {
474
+ switch (reason) {
475
+ case "initial": return "first build";
476
+ case "build": return "sources changed";
477
+ case "manual": return "manual restart";
478
+ case "crash": return "after crash";
479
+ }
480
+ }
481
+
482
+ //#endregion
483
+ //#region src/lib/host.ts
484
+ /**
485
+ * Resolves `localhost` the way Vite's dev server does: Node's default DNS result order (`dns.lookup('localhost')`)
486
+ * can disagree with the OS's own, "verbatim" resolution order — a well known source of a dev URL that prints fine
487
+ * but is not the address the bot actually bound to (e.g. an IPv6-first system where the server listens on IPv4
488
+ * only). When the two disagree this returns the OS's own address instead of the literal `localhost` hostname.
489
+ */
490
+ async function resolveLocalhost() {
491
+ try {
492
+ const [ordered, verbatim] = await Promise.all([lookup("localhost"), lookup("localhost", { verbatim: true })]);
493
+ return ordered.family === verbatim.family && ordered.address === verbatim.address ? "localhost" : verbatim.address;
494
+ } catch {
495
+ return "localhost";
496
+ }
497
+ }
498
+ /**
499
+ * Replaces a `localhost` hostname in `url` with {@link resolveLocalhost}'s result. Any other hostname (an explicit
500
+ * `dev.url` override, a LAN address, …) is returned untouched.
501
+ */
502
+ async function withResolvedLocalhost(url) {
503
+ const parsed = new URL(url);
504
+ if (parsed.hostname !== "localhost") return url;
505
+ const host = await resolveLocalhost();
506
+ if (host === "localhost") return url;
507
+ parsed.hostname = host.includes(":") ? `[${host}]` : host;
508
+ return parsed.href;
509
+ }
510
+
511
+ //#endregion
512
+ //#region src/lib/log-file.ts
513
+ /**
514
+ * Mirrors a dev session's logs into a file, so a run can be read back after the terminal UI is gone (the TUI takes
515
+ * over the alternate screen and its scrollback disappears on exit). Seedcord keeps a `logs/` directory for the same
516
+ * reason; this writes a single, truncated-per-run file instead.
517
+ */
518
+ var LogFileWriter = class {
519
+ file;
520
+ logs;
521
+ #stream = null;
522
+ constructor(file, logs) {
523
+ this.file = file;
524
+ this.logs = logs;
525
+ }
526
+ open() {
527
+ if (this.#stream) return;
528
+ mkdirSync(dirname(this.file), { recursive: true });
529
+ this.#stream = createWriteStream(this.file, { flags: "w" });
530
+ this.#stream.on("error", () => this.close());
531
+ for (const entry of this.logs.entries()) this.#write(entry);
532
+ this.logs.on("entry", this.#write);
533
+ }
534
+ close() {
535
+ const stream = this.#stream;
536
+ this.#stream = null;
537
+ if (!stream) return;
538
+ this.logs.off("entry", this.#write);
539
+ stream.end();
540
+ }
541
+ #write = (entry) => {
542
+ this.#stream?.write(`${format(entry)}\n`);
543
+ };
544
+ };
545
+ function format(entry) {
546
+ return `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.source.padEnd(6)} ${entry.text}`;
547
+ }
548
+
549
+ //#endregion
550
+ //#region src/lib/tasks/dev.ts
551
+ async function runDev(options) {
552
+ const config = await resolveDevConfig(await loadStarsConfig({
553
+ cwd: resolveCwd(options),
554
+ configFile: options.config
555
+ }));
556
+ assertSupportedExperiments(config);
557
+ await prepareAutoImports(config);
558
+ const mode = resolveOutputMode({ tui: options.tui });
559
+ const color = shouldUseColor();
560
+ const service = new DevService(config, { builder: await createBuilder(config) });
561
+ const logFile = config.dev.logFile ? new LogFileWriter(config.dev.logFile, service.logs) : null;
562
+ logFile?.open();
563
+ const renderer = mode === "tui" ? (await import("./tui-BTRFsLQR.js")).createTuiRenderer(service, {
564
+ color,
565
+ reducedMotion: prefersReducedMotion()
566
+ }) : (await import("./plain-CEe4X_Cz.js")).createPlainRenderer(service, { color });
567
+ let exiting = null;
568
+ const shutdown = (code) => {
569
+ exiting ??= (async () => {
570
+ renderer.stop();
571
+ await service.stop();
572
+ logFile?.close();
573
+ process.exit(code);
574
+ })();
575
+ return exiting;
576
+ };
577
+ process.once("SIGINT", () => void shutdown(ExitCode.Interrupted));
578
+ process.once("SIGTERM", () => void shutdown(ExitCode.Terminated));
579
+ process.once("SIGHUP", () => void shutdown(ExitCode.Terminated));
580
+ if (process.platform !== "win32") process.on("SIGUSR2", () => void service.restart("manual"));
581
+ const finished = renderer.start().then(() => shutdown(ExitCode.Ok));
582
+ await service.start();
583
+ await finished;
584
+ }
585
+ /**
586
+ * Resolves `dev.url`'s `localhost` to the address that is actually reachable (see {@link withResolvedLocalhost}),
587
+ * the way Vite's dev server does when it starts. Only `stars dev` pays this DNS lookup; `stars info`/`stars build`
588
+ * show or use the unresolved config as-is, since they never talk to the bot.
589
+ */
590
+ async function resolveDevConfig(config) {
591
+ if (!config.dev.url) return config;
592
+ const url = await withResolvedLocalhost(config.dev.url);
593
+ return url === config.dev.url ? config : {
594
+ ...config,
595
+ dev: {
596
+ ...config.dev,
597
+ url
598
+ }
599
+ };
600
+ }
601
+
602
+ //#endregion
603
+ export { runDev };
604
+ //# sourceMappingURL=dev-DWg8l23a.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-DWg8l23a.js","names":["#nextId","#entries","#state","#errors","#run","#child","#pending","#setState","#handleLine","#setBuild","#onBuildSuccess","#onBuildFailure","#emitStatus","#onExit","#healthTimer","#checkHealth","#build","#health","#restarts","#lastRestartReason","#lastBuild","#lastExit","#stopped","#clearRestartTimer","#enqueue","#scheduleRestart","#pendingReason","#restartTimer","#queue","#stream","#write"],"sources":["../src/lib/log-buffer.ts","../src/lib/typechecker.ts","../src/lib/dev-service.ts","../src/lib/host.ts","../src/lib/log-file.ts","../src/lib/tasks/dev.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\n\nexport type LogSource = 'stars' | 'build' | 'app' | 'tsc' | 'tunnel';\nexport type LogLevel = 'debug' | 'info' | 'success' | 'warn' | 'error';\n\nexport interface LogEntry {\n\treadonly id: number;\n\treadonly time: number;\n\treadonly source: LogSource;\n\treadonly level: LogLevel;\n\treadonly text: string;\n}\n\nexport interface LogFilter {\n\tsource?: LogSource | null;\n\tlevel?: LogLevel | null;\n\tquery?: string | null;\n}\n\nexport type LogInput = Omit<LogEntry, 'id' | 'time'>;\n\n/**\n * A bounded, in-memory log store shared by the dev service and its renderers.\n */\nexport class LogBuffer extends EventEmitter<{ entry: [LogEntry]; clear: [] }> {\n\t#entries: LogEntry[] = [];\n\t#nextId = 1;\n\n\tpublic constructor(public readonly capacity = 2000) {\n\t\tsuper();\n\t}\n\n\tpublic push(input: LogInput): LogEntry {\n\t\tconst entry: LogEntry = { id: this.#nextId++, time: Date.now(), ...input };\n\t\tthis.#entries.push(entry);\n\t\tif (this.#entries.length > this.capacity) this.#entries.splice(0, this.#entries.length - this.capacity);\n\t\tthis.emit('entry', entry);\n\t\treturn entry;\n\t}\n\n\tpublic entries(): readonly LogEntry[] {\n\t\treturn this.#entries;\n\t}\n\n\tpublic filter(filter: LogFilter): LogEntry[] {\n\t\tconst query = filter.query?.toLowerCase() ?? null;\n\t\treturn this.#entries.filter(\n\t\t\t(entry) =>\n\t\t\t\t(!filter.source || entry.source === filter.source) &&\n\t\t\t\t(!filter.level || matchesLevel(entry.level, filter.level)) &&\n\t\t\t\t(!query || entry.text.toLowerCase().includes(query))\n\t\t);\n\t}\n\n\tpublic clear(): void {\n\t\tthis.#entries = [];\n\t\tthis.emit('clear');\n\t}\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, success: 1, warn: 2, error: 3 };\n\n/**\n * Whether `level` is at least as severe as `minimum`.\n */\nexport function matchesLevel(level: LogLevel, minimum: LogLevel): boolean {\n\treturn LEVEL_ORDER[level] >= LEVEL_ORDER[minimum];\n}\n","import type { ResolvedStarsConfig, StarsTypechecker } from '@wolfstar/http-framework/config';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport { resolveTscBinary } from './builders/tsc.js';\nimport { CliError } from './errors.js';\nimport type { LogLevel } from './log-buffer.js';\nimport { createLineSplitter } from './process-supervisor.js';\nimport { resolveBinary } from './project.js';\n\nconst WATCH_START = /Starting (incremental )?compilation/;\nconst WATCH_DONE = /Found (\\d+) errors?\\. Watching for file changes/;\nconst ONE_SHOT_DONE = /Found (\\d+) errors?/;\nconst ERROR_LINE = /error TS\\d+:/;\n\nexport type TypecheckState = 'idle' | 'checking' | 'ok' | 'failed';\n\nexport interface TypecheckerEvents {\n\tlog: [level: LogLevel, text: string];\n\tstate: [state: TypecheckState, errors: number];\n}\n\nexport interface TypecheckCommand {\n\t/** The executable, and whether it is a script `node` has to run. */\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly node: boolean;\n\t/** Whether the checker watches the project itself, or has to be re-run after every build. */\n\treadonly watch: boolean;\n}\n\n/**\n * Runs a type checker next to the bot and reports type errors on their own channel, without ever blocking builds or\n * restarts: `stars dev` keeps running whatever the build tool produced, the way Seedcord's `hmr.typecheck` and\n * Vite's `checker` plugin do.\n *\n * `tsc` and `golar` (which forwards to TypeScript) watch the project themselves; `tsz` has no watch mode, so\n * {@link Typechecker.check} re-runs it after every build instead.\n */\nexport class Typechecker extends EventEmitter<TypecheckerEvents> {\n\t#child: ChildProcess | null = null;\n\t#state: TypecheckState = 'idle';\n\t#errors = 0;\n\t#pending = false;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic get state(): TypecheckState {\n\t\treturn this.#state;\n\t}\n\n\tpublic get errors(): number {\n\t\treturn this.#errors;\n\t}\n\n\t/** Whether the checker re-runs per build instead of watching on its own. */\n\tpublic get oneShot(): boolean {\n\t\treturn this.config.dev.typecheck.enabled && !resolveTypecheckCommand(this.config).watch;\n\t}\n\n\tpublic start(): void {\n\t\tif (!this.config.dev.typecheck.enabled) return;\n\t\tthis.#run();\n\t}\n\n\t/**\n\t * Re-runs a checker that cannot watch. Called after every successful build; a run already in flight is replaced,\n\t * so a fast series of builds only leaves the last check standing.\n\t */\n\tpublic check(): void {\n\t\tif (!this.oneShot) return;\n\t\tif (this.#child) {\n\t\t\tthis.#pending = true;\n\t\t\tthis.#child.kill();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#run();\n\t}\n\n\tpublic close(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tthis.#child = null;\n\t\tthis.#pending = false;\n\t\tif (!child || child.exitCode !== null) return Promise.resolve();\n\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once('exit', () => resolve());\n\t\t\tchild.kill();\n\t\t});\n\t}\n\n\t#run(): void {\n\t\tconst { command, args, node, watch } = resolveTypecheckCommand(this.config);\n\t\tconst child = spawn(node ? process.execPath : command, node ? [command, ...args] : [...args], {\n\t\t\tcwd: this.config.root,\n\t\t\tenv: process.env,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\twindowsHide: true\n\t\t});\n\t\tthis.#child = child;\n\t\tthis.#setState('checking', 0);\n\n\t\tlet errors = 0;\n\t\tconst handleLine = (line: string) => {\n\t\t\tif (ERROR_LINE.test(line)) errors++;\n\t\t\tthis.#handleLine(line);\n\t\t};\n\n\t\tconst stdout = createLineSplitter(handleLine);\n\t\tconst stderr = createLineSplitter(handleLine);\n\t\tchild.stdout?.on('data', stdout.push);\n\t\tchild.stderr?.on('data', stderr.push);\n\n\t\tchild.once('exit', (code) => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t\tthis.#child = null;\n\n\t\t\tif (!watch && this.#state === 'checking') {\n\t\t\t\t// A one-shot checker only reports through its exit code when it prints no summary line.\n\t\t\t\tthis.#setState(code === 0 && errors === 0 ? 'ok' : 'failed', errors);\n\t\t\t}\n\n\t\t\tif (this.#pending) {\n\t\t\t\tthis.#pending = false;\n\t\t\t\tthis.#run();\n\t\t\t}\n\t\t});\n\t\tchild.once('error', (error: NodeJS.ErrnoException) => {\n\t\t\tthis.#child = null;\n\t\t\tthis.emit('log', 'error', error.code === 'ENOENT' ? `${command} is not installed` : error.message);\n\t\t\tthis.#setState('failed', this.#errors);\n\t\t});\n\t}\n\n\t#handleLine(line: string): void {\n\t\tconst text = line.trim();\n\t\tif (text.length === 0) return;\n\n\t\tif (WATCH_START.test(text)) {\n\t\t\tthis.#setState('checking', 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (ERROR_LINE.test(text)) {\n\t\t\tthis.emit('log', 'error', text);\n\t\t\treturn;\n\t\t}\n\n\t\tconst done = WATCH_DONE.exec(text) ?? ONE_SHOT_DONE.exec(text);\n\t\tif (done) {\n\t\t\tconst errors = Number(done[1]);\n\t\t\tthis.#setState(errors === 0 ? 'ok' : 'failed', errors);\n\t\t\tthis.emit('log', errors === 0 ? 'success' : 'error', errors === 0 ? 'No type errors' : `${errors} type error${errors === 1 ? '' : 's'}`);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit('log', 'info', text);\n\t}\n\n\t#setState(state: TypecheckState, errors: number): void {\n\t\tthis.#state = state;\n\t\tthis.#errors = errors;\n\t\tthis.emit('state', state, errors);\n\t}\n}\n\n/**\n * Builds the command line of the configured type checker, resolved from the project's own `node_modules`.\n *\n * @throws {CliError} when the checker is not installed in the project.\n */\nexport function resolveTypecheckCommand(config: ResolvedStarsConfig): TypecheckCommand {\n\tconst { checker, tsconfig } = config.dev.typecheck;\n\tconst project = tsconfig!;\n\n\tswitch (checker) {\n\t\tcase 'golar': {\n\t\t\t// `golar tsc` forwards everything after it to the TypeScript CLI, watch mode included.\n\t\t\tconst golar = resolveBinary(config.root, 'golar', 'golar');\n\t\t\tif (!golar) throw missing(config, 'golar', 'Install it with `pnpm add -D golar`');\n\n\t\t\treturn { command: golar, args: ['tsc', ...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\n\t\tcase 'tsz': {\n\t\t\t// `tsz` is an early, tsc-compatible checker with no watch mode; `try-tsz` is its comparison harness.\n\t\t\tconst tsz = resolveBinary(config.root, '@mohsen-azimi/tsz-dev', 'tsz');\n\t\t\tif (tsz) return { command: tsz, args: ['--noEmit', '-p', project], node: true, watch: false };\n\n\t\t\tconst tryTsz = resolveBinary(config.root, 'try-tsz', 'try-tsz');\n\t\t\tif (tryTsz) return { command: tryTsz, args: ['-p', project], node: true, watch: false };\n\n\t\t\tthrow missing(config, 'tsz', 'Install it with `pnpm add -D @mohsen-azimi/tsz-dev` (or `try-tsz`)');\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst tsc = resolveTscBinary(config.root);\n\t\t\tif (!tsc) throw missing(config, 'typescript', 'Install it with `pnpm add -D typescript`');\n\n\t\t\treturn { command: tsc, args: [...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\t}\n}\n\nconst WATCH_ARGS = ['--noEmit', '--watch', '--preserveWatchOutput', '--pretty', 'false'] as const;\n\nfunction missing(config: ResolvedStarsConfig, name: string, install: string): CliError {\n\treturn new CliError(`\"${name}\" is not installed in ${config.root}`, {\n\t\tcode: 'DEPENDENCY_MISSING',\n\t\thint: `${install}, pick another \\`dev.typecheck.checker\\`, or set \\`dev.typecheck\\` to false.`\n\t});\n}\n\nexport type { StarsTypechecker };\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from './builders/types.js';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { LogBuffer, type LogLevel, type LogSource } from './log-buffer.js';\nimport { ProcessSupervisor, type ProcessExit, type ProcessState } from './process-supervisor.js';\nimport { Tunnel, type TunnelState } from './tunnel.js';\nimport { Typechecker, type TypecheckState } from './typechecker.js';\n\nexport type BuildState = 'idle' | 'building' | 'ok' | 'failed';\nexport type HealthState = 'unknown' | 'ok' | 'down';\nexport type RestartReason = 'initial' | 'build' | 'manual' | 'crash';\n\nexport interface DevStatus {\n\treadonly process: ProcessState;\n\treadonly build: BuildState;\n\treadonly health: HealthState;\n\treadonly pid: number | null;\n\treadonly startedAt: number | null;\n\treadonly restarts: number;\n\treadonly lastRestartReason: RestartReason | null;\n\treadonly lastBuild: BuildOutcome | null;\n\treadonly lastExit: ProcessExit | null;\n\treadonly url: string | null;\n\treadonly typecheck: TypecheckState;\n\treadonly typeErrors: number;\n\treadonly tunnel: TunnelState;\n\treadonly tunnelUrl: string | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n}\n\n/**\n * The headless heart of `stars dev`: builds, (re)starts the bot and exposes state and logs.\n * Renderers (plain or TUI) only subscribe to it, they never own behaviour.\n */\nexport class DevService extends EventEmitter<DevServiceEvents> {\n\tpublic readonly logs: LogBuffer;\n\tpublic readonly builder: Builder;\n\tpublic readonly supervisor: ProcessSupervisor;\n\tpublic readonly typechecker: Typechecker;\n\tpublic readonly tunnel: Tunnel;\n\n\t#build: BuildState = 'idle';\n\t#health: HealthState = 'unknown';\n\t#restarts = 0;\n\t#lastRestartReason: RestartReason | null = null;\n\t#lastBuild: BuildOutcome | null = null;\n\t#lastExit: ProcessExit | null = null;\n\t#restartTimer: NodeJS.Timeout | null = null;\n\t#healthTimer: NodeJS.Timeout | null = null;\n\t#pendingReason: RestartReason | null = null;\n\t#stopped = false;\n\t#queue: Promise<void> = Promise.resolve();\n\n\tpublic constructor(\n\t\tpublic readonly config: ResolvedStarsConfig,\n\t\toptions: DevServiceOptions\n\t) {\n\t\tsuper();\n\t\tthis.logs = options.logs ?? new LogBuffer();\n\t\tthis.builder = options.builder;\n\t\tthis.supervisor = options.supervisor ?? createSupervisor(config);\n\t\tthis.typechecker = options.typechecker ?? new Typechecker(config);\n\t\tthis.tunnel = options.tunnel ?? new Tunnel(config);\n\n\t\tthis.builder.on('start', () => this.#setBuild('building'));\n\t\tthis.builder.on('success', (outcome) => this.#onBuildSuccess(outcome));\n\t\tthis.builder.on('failure', (outcome) => this.#onBuildFailure(outcome));\n\t\tthis.builder.on('log', (level, text) => this.log('build', level, text));\n\n\t\tthis.supervisor.on('state', () => this.#emitStatus());\n\t\tthis.supervisor.on('stdout', (line) => this.log('app', 'info', line));\n\t\tthis.supervisor.on('stderr', (line) => this.log('app', 'error', line));\n\t\tthis.supervisor.on('error', (error) => this.log('stars', 'error', `Failed to start the bot: ${error.message}`));\n\t\tthis.supervisor.on('exit', (exit) => this.#onExit(exit));\n\n\t\tthis.typechecker.on('log', (level, text) => this.log('tsc', level, text));\n\t\tthis.typechecker.on('state', () => this.#emitStatus());\n\t\tthis.tunnel.on('log', (level, text) => this.log('tunnel', level, text));\n\t\tthis.tunnel.on('state', () => this.#emitStatus());\n\n\t\tif (config.dev.url && config.dev.health) {\n\t\t\tconst interval = options.healthInterval ?? 5000;\n\t\t\tthis.#healthTimer = setInterval(() => void this.#checkHealth(), interval);\n\t\t\tthis.#healthTimer.unref();\n\t\t}\n\t}\n\n\tpublic get status(): DevStatus {\n\t\treturn {\n\t\t\tprocess: this.supervisor.state,\n\t\t\tbuild: this.#build,\n\t\t\thealth: this.#health,\n\t\t\tpid: this.supervisor.pid,\n\t\t\tstartedAt: this.supervisor.startedAt,\n\t\t\trestarts: this.#restarts,\n\t\t\tlastRestartReason: this.#lastRestartReason,\n\t\t\tlastBuild: this.#lastBuild,\n\t\t\tlastExit: this.#lastExit,\n\t\t\turl: this.config.dev.url,\n\t\t\ttypecheck: this.typechecker.state,\n\t\t\ttypeErrors: this.typechecker.errors,\n\t\t\ttunnel: this.tunnel.state,\n\t\t\ttunnelUrl: this.tunnel.url\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string): void {\n\t\tthis.logs.push({ source, level, text });\n\t}\n\n\t/**\n\t * Starts watching; the bot starts after the first successful build.\n\t */\n\tpublic async start(): Promise<void> {\n\t\tthis.#stopped = false;\n\t\tthis.log(\n\t\t\t'stars',\n\t\t\t'info',\n\t\t\t`Watching ${this.config.build.tool === 'none' ? 'sources' : `with ${this.config.build.tool}`}, entry ${this.config.entry}`\n\t\t);\n\t\tif (this.config.dev.typecheck.enabled) this.typechecker.start();\n\t\t// The tunnel comes up next to the build: neither waits for the other, and a failed tunnel never stops the bot.\n\t\tvoid this.tunnel.start();\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the watcher and the bot. Safe to call more than once.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([this.builder.close(), this.supervisor.stop(), this.typechecker.close(), this.tunnel.close()]);\n\t\t});\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#setBuild('ok');\n\t\tif (this.#stopped) return;\n\t\t// A checker without a watch mode (`tsz`) only knows about the change once the build is through.\n\t\tthis.typechecker.check();\n\t\tthis.log('stars', 'success', this.builder.tool === 'none' ? 'Sources changed' : `Build succeeded in ${outcome.durationMs}ms`);\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#setBuild('failed');\n\t\tthis.#clearRestartTimer();\n\t\tthis.log('stars', 'error', `Build failed${outcome.message ? `: ${outcome.message}` : ''}, waiting for changes`);\n\t}\n\n\t#onExit(exit: ProcessExit): void {\n\t\tthis.#lastExit = exit;\n\t\tthis.#health = 'unknown';\n\t\tif (!exit.requested) {\n\t\t\tconst how = exit.signal ? `signal ${exit.signal}` : `code ${exit.code}`;\n\t\t\tthis.log('stars', exit.code === 0 ? 'warn' : 'error', `The bot exited with ${how}, waiting for changes (press r to restart)`);\n\t\t}\n\t\tthis.#emitStatus();\n\t}\n\n\t#scheduleRestart(reason: RestartReason): void {\n\t\tthis.#pendingReason = reason;\n\t\tthis.#clearRestartTimer();\n\t\tthis.#restartTimer = setTimeout(() => {\n\t\t\tthis.#restartTimer = null;\n\t\t\tconst pending = this.#pendingReason ?? reason;\n\t\t\tthis.#pendingReason = null;\n\t\t\tvoid this.restart(pending);\n\t\t}, this.config.dev.debounce);\n\t}\n\n\t#clearRestartTimer(): void {\n\t\tif (this.#restartTimer) clearTimeout(this.#restartTimer);\n\t\tthis.#restartTimer = null;\n\t}\n\n\t#enqueue(task: () => Promise<void>): Promise<void> {\n\t\tthis.#queue = this.#queue.then(task, task);\n\t\treturn this.#queue;\n\t}\n\n\t#setBuild(state: BuildState): void {\n\t\tif (this.#build === state) return;\n\t\tthis.#build = state;\n\t\tthis.#emitStatus();\n\t}\n\n\tasync #checkHealth(): Promise<void> {\n\t\tif (!this.supervisor.running || !this.config.dev.url || !this.config.dev.health) return;\n\n\t\tlet next: HealthState;\n\t\ttry {\n\t\t\tconst response = await fetch(new URL(this.config.dev.health, this.config.dev.url), { signal: AbortSignal.timeout(2000) });\n\t\t\tnext = response.status < 500 ? 'ok' : 'down';\n\t\t} catch {\n\t\t\tnext = 'down';\n\t\t}\n\n\t\tif (next !== this.#health) {\n\t\t\tthis.#health = next;\n\t\t\tthis.#emitStatus();\n\t\t}\n\t}\n\n\t#emitStatus(): void {\n\t\tthis.emit('status', this.status);\n\t}\n}\n\nexport function createSupervisor(config: ResolvedStarsConfig): ProcessSupervisor {\n\treturn new ProcessSupervisor({\n\t\tcommand: process.execPath,\n\t\targs: [...config.dev.nodeArgs, config.build.output, ...config.dev.args],\n\t\tcwd: config.root,\n\t\tenv: { ...process.env, ...config.dev.env, STARS_DEV: '1', FORCE_COLOR: process.env.FORCE_COLOR ?? (process.stdout.isTTY ? '1' : undefined) },\n\t\tkillTimeout: config.dev.killTimeout\n\t});\n}\n\nexport function describeReason(reason: RestartReason): string {\n\tswitch (reason) {\n\t\tcase 'initial':\n\t\t\treturn 'first build';\n\t\tcase 'build':\n\t\t\treturn 'sources changed';\n\t\tcase 'manual':\n\t\t\treturn 'manual restart';\n\t\tcase 'crash':\n\t\t\treturn 'after crash';\n\t}\n}\n","import { lookup } from 'node:dns/promises';\n\n/**\n * Resolves `localhost` the way Vite's dev server does: Node's default DNS result order (`dns.lookup('localhost')`)\n * can disagree with the OS's own, \"verbatim\" resolution order — a well known source of a dev URL that prints fine\n * but is not the address the bot actually bound to (e.g. an IPv6-first system where the server listens on IPv4\n * only). When the two disagree this returns the OS's own address instead of the literal `localhost` hostname.\n */\nexport async function resolveLocalhost(): Promise<string> {\n\ttry {\n\t\tconst [ordered, verbatim] = await Promise.all([lookup('localhost'), lookup('localhost', { verbatim: true })]);\n\t\treturn ordered.family === verbatim.family && ordered.address === verbatim.address ? 'localhost' : verbatim.address;\n\t} catch {\n\t\treturn 'localhost';\n\t}\n}\n\n/**\n * Replaces a `localhost` hostname in `url` with {@link resolveLocalhost}'s result. Any other hostname (an explicit\n * `dev.url` override, a LAN address, …) is returned untouched.\n */\nexport async function withResolvedLocalhost(url: string): Promise<string> {\n\tconst parsed = new URL(url);\n\tif (parsed.hostname !== 'localhost') return url;\n\n\tconst host = await resolveLocalhost();\n\tif (host === 'localhost') return url;\n\n\tparsed.hostname = host.includes(':') ? `[${host}]` : host;\n\treturn parsed.href;\n}\n","import { createWriteStream, mkdirSync, type WriteStream } from 'node:fs';\nimport { dirname } from 'node:path';\nimport type { LogBuffer, LogEntry } from './log-buffer.js';\n\n/**\n * Mirrors a dev session's logs into a file, so a run can be read back after the terminal UI is gone (the TUI takes\n * over the alternate screen and its scrollback disappears on exit). Seedcord keeps a `logs/` directory for the same\n * reason; this writes a single, truncated-per-run file instead.\n */\nexport class LogFileWriter {\n\t#stream: WriteStream | null = null;\n\n\tpublic constructor(\n\t\tpublic readonly file: string,\n\t\tprivate readonly logs: LogBuffer\n\t) {}\n\n\tpublic open(): void {\n\t\tif (this.#stream) return;\n\n\t\tmkdirSync(dirname(this.file), { recursive: true });\n\t\tthis.#stream = createWriteStream(this.file, { flags: 'w' });\n\t\t// A broken pipe or a read-only directory must never take the dev session down with it.\n\t\tthis.#stream.on('error', () => this.close());\n\n\t\tfor (const entry of this.logs.entries()) this.#write(entry);\n\t\tthis.logs.on('entry', this.#write);\n\t}\n\n\tpublic close(): void {\n\t\tconst stream = this.#stream;\n\t\tthis.#stream = null;\n\t\tif (!stream) return;\n\n\t\tthis.logs.off('entry', this.#write);\n\t\tstream.end();\n\t}\n\n\t#write = (entry: LogEntry): void => {\n\t\tthis.#stream?.write(`${format(entry)}\\n`);\n\t};\n}\n\nfunction format(entry: LogEntry): string {\n\treturn `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.source.padEnd(6)} ${entry.text}`;\n}\n","import { loadStarsConfig, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport type { ProjectArgs } from '../args.js';\nimport { resolveCwd } from '../args.js';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev-service.js';\nimport { ExitCode } from '../errors.js';\nimport { withResolvedLocalhost } from '../host.js';\nimport { LogFileWriter } from '../log-file.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../output-mode.js';\nimport { prepareAutoImports } from './prepare.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n}\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tconst config = await resolveDevConfig(await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config }));\n\tassertSupportedExperiments(config);\n\tawait prepareAutoImports(config);\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\n\tconst service = new DevService(config, { builder: await createBuilder(config) });\n\tconst logFile = config.dev.logFile ? new LogFileWriter(config.dev.logFile, service.logs) : null;\n\tlogFile?.open();\n\tconst renderer =\n\t\tmode === 'tui'\n\t\t\t? (await import('../../renderers/tui.js')).createTuiRenderer(service, { color, reducedMotion: prefersReducedMotion() })\n\t\t\t: (await import('../../renderers/plain.js')).createPlainRenderer(service, { color });\n\n\tlet exiting: Promise<never> | null = null;\n\tconst shutdown = (code: ExitCode): Promise<never> => {\n\t\texiting ??= (async () => {\n\t\t\trenderer.stop();\n\t\t\tawait service.stop();\n\t\t\tlogFile?.close();\n\t\t\tprocess.exit(code);\n\t\t})();\n\t\treturn exiting;\n\t};\n\n\tprocess.once('SIGINT', () => void shutdown(ExitCode.Interrupted));\n\tprocess.once('SIGTERM', () => void shutdown(ExitCode.Terminated));\n\tprocess.once('SIGHUP', () => void shutdown(ExitCode.Terminated));\n\tif (process.platform !== 'win32') process.on('SIGUSR2', () => void service.restart('manual'));\n\n\tconst finished = renderer.start().then(() => shutdown(ExitCode.Ok));\n\tawait service.start();\n\tawait finished;\n}\n\n/**\n * Resolves `dev.url`'s `localhost` to the address that is actually reachable (see {@link withResolvedLocalhost}),\n * the way Vite's dev server does when it starts. Only `stars dev` pays this DNS lookup; `stars info`/`stars build`\n * show or use the unresolved config as-is, since they never talk to the bot.\n */\nasync function resolveDevConfig(config: ResolvedStarsConfig): Promise<ResolvedStarsConfig> {\n\tif (!config.dev.url) return config;\n\n\tconst url = await withResolvedLocalhost(config.dev.url);\n\treturn url === config.dev.url ? config : { ...config, dev: { ...config.dev, url } };\n}\n"],"mappings":"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"}