@wolfstar/cli 0.7.0 → 1.0.0-next-20260919162943

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 (52) hide show
  1. package/README.md +5 -5
  2. package/dist/{_shared-Cxp93Wni.js → _shared-dVR2pKjb.js} +14 -1
  3. package/dist/_shared-dVR2pKjb.js.map +1 -0
  4. package/dist/{build-B7QQzVk8.js → build-BUhQQ1E5.js} +5 -9
  5. package/dist/build-BUhQQ1E5.js.map +1 -0
  6. package/dist/cli.js +1 -1
  7. package/dist/{codegen-Ba2qI4ji.js → codegen-hmnFzRjK.js} +12 -11
  8. package/dist/codegen-hmnFzRjK.js.map +1 -0
  9. package/dist/{commands-DOkMjyT7.js → commands-BApoAbpQ.js} +13 -25
  10. package/dist/commands-BApoAbpQ.js.map +1 -0
  11. package/dist/{dev-Q-FSs-mH.js → dev-B0RlN46N.js} +14 -13
  12. package/dist/dev-B0RlN46N.js.map +1 -0
  13. package/dist/diagnostics-C98hnJB5.js +59 -0
  14. package/dist/diagnostics-C98hnJB5.js.map +1 -0
  15. package/dist/errors-D1UU8atd.js +56 -0
  16. package/dist/errors-D1UU8atd.js.map +1 -0
  17. package/dist/index.d.ts +94 -22
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -4
  20. package/dist/{info-Dt6WW6Px.js → info-DN4vUx1S.js} +4 -3
  21. package/dist/info-DN4vUx1S.js.map +1 -0
  22. package/dist/{locales-CODosUiP.js → locales-khJEemUn.js} +9 -19
  23. package/dist/locales-khJEemUn.js.map +1 -0
  24. package/dist/nitro-Cki2gTpt.js +145 -0
  25. package/dist/nitro-Cki2gTpt.js.map +1 -0
  26. package/dist/{prepare-Dv_8yIbG.js → prepare-wGp-cAa9.js} +4 -7
  27. package/dist/prepare-wGp-cAa9.js.map +1 -0
  28. package/dist/{project-jVFFrLl9.js → project-DT3wg_lf.js} +5 -4
  29. package/dist/project-DT3wg_lf.js.map +1 -0
  30. package/dist/{run-DjjDIYTy.js → run-D_j2tqWF.js} +8 -8
  31. package/dist/{run-DjjDIYTy.js.map → run-D_j2tqWF.js.map} +1 -1
  32. package/dist/{tsc-DeEv6Ce0.js → tsc-Bex5dwzD.js} +6 -5
  33. package/dist/tsc-Bex5dwzD.js.map +1 -0
  34. package/dist/tsc-dzhKrr7T.js +3 -0
  35. package/dist/{tsdown-C9q3H3MK.js → tsdown-scDokwb0.js} +2 -2
  36. package/dist/{tsdown-C9q3H3MK.js.map → tsdown-scDokwb0.js.map} +1 -1
  37. package/dist/{vite-BVXBSWQb.js → vite-C-szLSGb.js} +2 -2
  38. package/dist/{vite-BVXBSWQb.js.map → vite-C-szLSGb.js.map} +1 -1
  39. package/package.json +8 -2
  40. package/dist/_shared-Cxp93Wni.js.map +0 -1
  41. package/dist/build-B7QQzVk8.js.map +0 -1
  42. package/dist/codegen-Ba2qI4ji.js.map +0 -1
  43. package/dist/commands-DOkMjyT7.js.map +0 -1
  44. package/dist/dev-Q-FSs-mH.js.map +0 -1
  45. package/dist/errors-DETRFkoF.js +0 -62
  46. package/dist/errors-DETRFkoF.js.map +0 -1
  47. package/dist/info-Dt6WW6Px.js.map +0 -1
  48. package/dist/locales-CODosUiP.js.map +0 -1
  49. package/dist/prepare-Dv_8yIbG.js.map +0 -1
  50. package/dist/project-jVFFrLl9.js.map +0 -1
  51. package/dist/tsc-DQq_UrBf.js +0 -3
  52. package/dist/tsc-DeEv6Ce0.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"run-DjjDIYTy.js","names":["_runCommand"],"sources":["../src/commands/index.ts","../src/main.ts","../src/run.ts"],"sourcesContent":["import type { CommandDef } from 'citty';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nexport const commands: Record<string, () => Promise<AnyCommand>> = {\n\tdev: () => import('./dev.js').then((module) => module.default),\n\tbuild: () => import('./build.js').then((module) => module.default),\n\tinfo: () => import('./info.js').then((module) => module.default),\n\tcodegen: () => import('./codegen.js').then((module) => module.default),\n\tprepare: () => import('./prepare.js').then((module) => module.default),\n\tcommands: () => import('./commands.js').then((module) => module.default)\n};\n","import { defineCommand, type CommandDef } from 'citty';\nimport { commands } from './commands/index.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nconst packageJson = readOwnPackageJson();\n\nexport const main = defineCommand({\n\tmeta: {\n\t\tname: 'stars',\n\t\tversion: packageJson.version,\n\t\tdescription: packageJson.description\n\t},\n\tsubCommands: commands\n}) as AnyCommand;\n","import { renderUsage, runCommand as _runCommand, type CommandDef } from 'citty';\nimport process from 'node:process';\nimport { commands } from './commands/index.js';\nimport { main } from './main.js';\nimport { exitCodeOf, formatError } from './utils/errors.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\n/**\n * Walks the command names of `stars <command> [<subcommand>] --help` so nested commands (`stars commands clean`)\n * render their own usage instead of their parent's.\n */\nasync function resolveHelpTarget(names: string[]): Promise<AnyCommand | null> {\n\tconst [first, ...rest] = names;\n\tif (!first || !Object.hasOwn(commands, first)) return null;\n\n\tlet command = await commands[first]!();\n\tfor (const name of rest) {\n\t\tconst resolved = typeof command.subCommands === 'function' ? await command.subCommands() : await command.subCommands;\n\t\tconst child: unknown = (resolved as Record<string, unknown> | undefined)?.[name];\n\t\tif (!child) break;\n\t\tcommand = (typeof child === 'function' ? await child() : child) as AnyCommand;\n\t}\n\n\treturn command;\n}\n\nexport async function runMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n\tif (argv.includes('--version') || argv.includes('-V')) {\n\t\tconsole.log(readOwnPackageJson().version);\n\t\tprocess.exit(0);\n\t}\n\n\tif (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {\n\t\t// citty's showUsage logs through consola, which is muted in some environments; render and print directly.\n\t\tconst command = await resolveHelpTarget(argv.filter((argument) => !argument.startsWith('-')));\n\t\tconsole.log(await (command ? renderUsage(command, main) : renderUsage(main)));\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tawait _runCommand(main, { rawArgs: argv });\n\t} catch (error) {\n\t\tconsole.error(await formatError(error));\n\t\tprocess.exit(exitCodeOf(error));\n\t}\n}\n"],"mappings":";;;;;;AAMA,MAAa,WAAsD;CAClE,WAAW,OAAO,oBAAW,CAAC,MAAM,WAAW,OAAO,OAAO;CAC7D,aAAa,OAAO,sBAAa,CAAC,MAAM,WAAW,OAAO,OAAO;CACjE,YAAY,OAAO,qBAAY,CAAC,MAAM,WAAW,OAAO,OAAO;CAC/D,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,gBAAgB,OAAO,yBAAgB,CAAC,MAAM,WAAW,OAAO,OAAO;AACxE;;;;ACLA,MAAM,cAAc,mBAAmB;AAEvC,MAAa,OAAO,cAAc;CACjC,MAAM;EACL,MAAM;EACN,SAAS,YAAY;EACrB,aAAa,YAAY;CAC1B;CACA,aAAa;AACd,CAAC;;;;;;;;ACFD,eAAe,kBAAkB,OAA6C;CAC7E,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,UAAU,KAAK,GAAG,OAAO;CAEtD,IAAI,UAAU,MAAM,SAAS,MAAM,CAAE;CACrC,KAAK,MAAM,QAAQ,MAAM;EAExB,MAAM,SADW,OAAO,QAAQ,gBAAgB,aAAa,MAAM,QAAQ,YAAY,IAAI,MAAM,QAAQ,YACzE,GAA2C;EAC3E,IAAI,CAAC,OAAO;EACZ,UAAW,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI;CAC1D;CAEA,OAAO;AACR;AAEA,eAAsB,QAAQ,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAkB;CACpF,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;EACtD,QAAQ,IAAI,mBAAmB,CAAC,CAAC,OAAO;EACxC,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;EAExE,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,aAAa,CAAC,SAAS,WAAW,GAAG,CAAC,CAAC;EAC5F,QAAQ,IAAI,OAAO,UAAU,YAAY,SAAS,IAAI,IAAI,YAAY,IAAI,EAAE;EAC5E,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI;EACH,MAAMA,WAAY,MAAM,EAAE,SAAS,KAAK,CAAC;CAC1C,SAAS,OAAO;EACf,QAAQ,MAAM,MAAM,YAAY,KAAK,CAAC;EACtC,QAAQ,KAAK,WAAW,KAAK,CAAC;CAC/B;AACD"}
1
+ {"version":3,"file":"run-D_j2tqWF.js","names":["_runCommand"],"sources":["../src/commands/index.ts","../src/main.ts","../src/run.ts"],"sourcesContent":["import type { CommandDef } from 'citty';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nexport const commands: Record<string, () => Promise<AnyCommand>> = {\n\tdev: () => import('./dev.js').then((module) => module.default),\n\tbuild: () => import('./build.js').then((module) => module.default),\n\tinfo: () => import('./info.js').then((module) => module.default),\n\tcodegen: () => import('./codegen.js').then((module) => module.default),\n\tprepare: () => import('./prepare.js').then((module) => module.default),\n\tcommands: () => import('./commands.js').then((module) => module.default)\n};\n","import { defineCommand, type CommandDef } from 'citty';\nimport { commands } from './commands/index.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nconst packageJson = readOwnPackageJson();\n\nexport const main = defineCommand({\n\tmeta: {\n\t\tname: 'stars',\n\t\tversion: packageJson.version,\n\t\tdescription: packageJson.description\n\t},\n\tsubCommands: commands\n}) as AnyCommand;\n","import { renderUsage, runCommand as _runCommand, type CommandDef } from 'citty';\nimport process from 'node:process';\nimport { commands } from './commands/index.js';\nimport { main } from './main.js';\nimport { exitCodeOf, formatError } from './utils/errors.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\n/**\n * Walks the command names of `stars <command> [<subcommand>] --help` so nested commands (`stars commands clean`)\n * render their own usage instead of their parent's.\n */\nasync function resolveHelpTarget(names: string[]): Promise<AnyCommand | null> {\n\tconst [first, ...rest] = names;\n\tif (!first || !Object.hasOwn(commands, first)) return null;\n\n\tlet command = await commands[first]!();\n\tfor (const name of rest) {\n\t\tconst resolved = typeof command.subCommands === 'function' ? await command.subCommands() : await command.subCommands;\n\t\tconst child: unknown = (resolved as Record<string, unknown> | undefined)?.[name];\n\t\tif (!child) break;\n\t\tcommand = (typeof child === 'function' ? await child() : child) as AnyCommand;\n\t}\n\n\treturn command;\n}\n\nexport async function runMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n\tif (argv.includes('--version') || argv.includes('-V')) {\n\t\tconsole.log(readOwnPackageJson().version);\n\t\tprocess.exit(0);\n\t}\n\n\tif (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {\n\t\t// citty's showUsage logs through consola, which is muted in some environments; render and print directly.\n\t\tconst command = await resolveHelpTarget(argv.filter((argument) => !argument.startsWith('-')));\n\t\tconsole.log(await (command ? renderUsage(command, main) : renderUsage(main)));\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tawait _runCommand(main, { rawArgs: argv });\n\t} catch (error) {\n\t\tconsole.error(await formatError(error));\n\t\tprocess.exit(exitCodeOf(error));\n\t}\n}\n"],"mappings":";;;;;;AAMA,MAAa,WAAsD;CAClE,WAAW,OAAO,oBAAW,CAAC,MAAM,WAAW,OAAO,OAAO;CAC7D,aAAa,OAAO,sBAAa,CAAC,MAAM,WAAW,OAAO,OAAO;CACjE,YAAY,OAAO,qBAAY,CAAC,MAAM,WAAW,OAAO,OAAO;CAC/D,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,eAAe,OAAO,wBAAe,CAAC,MAAM,WAAW,OAAO,OAAO;CACrE,gBAAgB,OAAO,yBAAgB,CAAC,MAAM,WAAW,OAAO,OAAO;AACxE;;;;ACLA,MAAM,cAAc,mBAAmB;AAEvC,MAAa,OAAO,cAAc;CACjC,MAAM;EACL,MAAM;EACN,SAAS,YAAY;EACrB,aAAa,YAAY;CAC1B;CACA,aAAa;AACd,CAAC;;;;;;;;ACFD,eAAe,kBAAkB,OAA6C;CAC7E,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,UAAU,KAAK,GAAG,OAAO;CAEtD,IAAI,UAAU,MAAM,SAAS,MAAM,CAAE;CACrC,KAAK,MAAM,QAAQ,MAAM;EAExB,MAAM,SADW,OAAO,QAAQ,gBAAgB,aAAa,MAAM,QAAQ,YAAY,IAAI,MAAM,QAAQ,YACzE,GAA2C;EAC3E,IAAI,CAAC,OAAO;EACZ,UAAW,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI;CAC1D;CAEA,OAAO;AACR;AAEA,eAAsB,QAAQ,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAkB;CACpF,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;EACtD,QAAQ,IAAI,mBAAmB,CAAC,CAAC,OAAO;EACxC,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;EAExE,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,aAAa,CAAC,SAAS,WAAW,GAAG,CAAC,CAAC;EAC5F,QAAQ,IAAI,OAAO,UAAU,YAAY,SAAS,IAAI,IAAI,YAAY,IAAI,EAAE;EAC5E,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI;EACH,MAAMA,WAAY,MAAM,EAAE,SAAS,KAAK,CAAC;CAC1C,SAAS,OAAO;EACf,QAAQ,MAAM,MAAM,YAAY,KAAK,CAAC;EACtC,QAAQ,KAAK,WAAW,KAAK,CAAC;CAC/B;AACD"}
@@ -1,5 +1,5 @@
1
- import { t as CliError } from "./errors-DETRFkoF.js";
2
- import { r as resolveBinary } from "./project-jVFFrLl9.js";
1
+ import { t as cliDiagnostics } from "./diagnostics-C98hnJB5.js";
2
+ import { r as resolveBinary } from "./project-DT3wg_lf.js";
3
3
  import { EventEmitter } from "node:events";
4
4
  import { spawn } from "node:child_process";
5
5
 
@@ -177,8 +177,9 @@ var TscBuilder = class extends EventEmitter {
177
177
  }
178
178
  #spawn(extraArgs) {
179
179
  const tsc = resolveTscBinary(this.config.root);
180
- if (!tsc) throw new CliError(`"typescript" is not installed in ${this.config.root}`, {
181
- code: "DEPENDENCY_MISSING",
180
+ if (!tsc) throw cliDiagnostics.DEPENDENCY_MISSING({
181
+ name: "typescript",
182
+ root: this.config.root,
182
183
  hint: INSTALL_HINT
183
184
  });
184
185
  const child = spawn(process.execPath, [
@@ -259,4 +260,4 @@ function resolveTscBinary(root) {
259
260
 
260
261
  //#endregion
261
262
  export { createLineSplitter as i, resolveTscBinary as n, ProcessSupervisor as r, TscBuilder as t };
262
- //# sourceMappingURL=tsc-DeEv6Ce0.js.map
263
+ //# sourceMappingURL=tsc-Bex5dwzD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsc-Bex5dwzD.js","names":[],"sources":["../src/utils/process-supervisor.ts","../src/builders/tsc.ts"],"sourcesContent":["import { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\n\nexport type ProcessState = 'idle' | 'starting' | 'running' | 'stopping' | 'stopped' | 'crashed';\n\nexport interface ProcessExit {\n\tcode: number | null;\n\tsignal: NodeJS.Signals | null;\n\t/** Whether the exit was requested through {@link ProcessSupervisor.stop}. */\n\trequested: boolean;\n}\n\nexport interface ProcessSupervisorOptions {\n\tcommand: string;\n\targs: readonly string[];\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n\t/** Milliseconds to wait after `SIGTERM` before sending `SIGKILL`. */\n\tkillTimeout: number;\n}\n\nexport interface ProcessSupervisorEvents {\n\tstate: [state: ProcessState];\n\tstdout: [line: string];\n\tstderr: [line: string];\n\texit: [exit: ProcessExit];\n\terror: [error: Error];\n}\n\n/**\n * Owns a single child process: starts it, streams its output line by line and stops it gracefully.\n */\nexport class ProcessSupervisor extends EventEmitter<ProcessSupervisorEvents> {\n\t#child: ChildProcess | null = null;\n\t#state: ProcessState = 'idle';\n\t#startedAt: number | null = null;\n\t#stopping: Promise<void> | null = null;\n\n\tpublic constructor(private readonly options: ProcessSupervisorOptions) {\n\t\tsuper();\n\t}\n\n\tpublic get state(): ProcessState {\n\t\treturn this.#state;\n\t}\n\n\tpublic get pid(): number | null {\n\t\treturn this.#child?.pid ?? null;\n\t}\n\n\tpublic get startedAt(): number | null {\n\t\treturn this.#startedAt;\n\t}\n\n\tpublic get running(): boolean {\n\t\treturn this.#state === 'starting' || this.#state === 'running';\n\t}\n\n\tpublic start(): void {\n\t\tif (this.#child) throw new Error('The process is already running, call stop() or restart() first.');\n\n\t\tconst { command, args, cwd, env } = this.options;\n\t\tthis.#setState('starting');\n\n\t\tconst child = spawn(command, args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true });\n\t\tthis.#child = child;\n\t\tthis.#startedAt = Date.now();\n\n\t\tlet requested = false;\n\t\tconst stdout = createLineSplitter((line) => this.emit('stdout', line));\n\t\tconst stderr = createLineSplitter((line) => this.emit('stderr', line));\n\t\tchild.stdout?.on('data', stdout.push);\n\t\tchild.stderr?.on('data', stderr.push);\n\n\t\tchild.once('spawn', () => {\n\t\t\tif (this.#child === child) this.#setState('running');\n\t\t});\n\n\t\tchild.once('error', (error) => {\n\t\t\tthis.emit('error', error);\n\t\t});\n\n\t\tchild.once('exit', (code, signal) => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t\trequested = this.#state === 'stopping';\n\t\t\tthis.#child = null;\n\t\t\tthis.#startedAt = null;\n\t\t\tthis.#setState(requested || code === 0 ? 'stopped' : 'crashed');\n\t\t\tthis.emit('exit', { code, signal, requested });\n\t\t});\n\t}\n\n\t/**\n\t * Sends `SIGTERM`, escalates to `SIGKILL` after `killTimeout` and resolves once the process exited.\n\t */\n\tpublic stop(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tif (!child) return Promise.resolve();\n\t\tif (this.#stopping) return this.#stopping;\n\n\t\tthis.#setState('stopping');\n\t\tthis.#stopping = new Promise<void>((resolve) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tif (child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');\n\t\t\t}, this.options.killTimeout);\n\t\t\ttimer.unref();\n\n\t\t\tchild.once('exit', () => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tthis.#stopping = null;\n\t\t\t\tresolve();\n\t\t\t});\n\n\t\t\tif (!child.kill('SIGTERM')) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tthis.#stopping = null;\n\t\t\t\tresolve();\n\t\t\t}\n\t\t});\n\n\t\treturn this.#stopping;\n\t}\n\n\t/**\n\t * Sends `SIGKILL` right away, without waiting for the process to exit. Used to force a shutdown that is taking too long.\n\t */\n\tpublic kill(): void {\n\t\tconst child = this.#child;\n\t\tif (child && child.exitCode === null && child.signalCode === null) child.kill('SIGKILL');\n\t}\n\n\tpublic async restart(): Promise<void> {\n\t\tawait this.stop();\n\t\tthis.start();\n\t}\n\n\t#setState(state: ProcessState): void {\n\t\tif (this.#state === state) return;\n\t\tthis.#state = state;\n\t\tthis.emit('state', state);\n\t}\n}\n\n/**\n * Splits a byte stream into complete lines, keeping the trailing partial line until the next chunk.\n */\nexport function createLineSplitter(onLine: (line: string) => void) {\n\tlet remainder = '';\n\treturn {\n\t\tpush(chunk: Buffer | string) {\n\t\t\tremainder += chunk.toString();\n\t\t\tconst lines = remainder.split(/\\r?\\n/);\n\t\t\tremainder = lines.pop() ?? '';\n\t\t\tfor (const line of lines) onLine(line);\n\t\t},\n\t\tflush() {\n\t\t\tif (remainder.length > 0) onLine(remainder);\n\t\t\tremainder = '';\n\t\t}\n\t};\n}\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { createLineSplitter } from '../utils/process-supervisor.js';\nimport { resolveBinary } from '../utils/project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D typescript`, or set `build.tool` to 'tsdown' or 'none'.\";\nconst WATCH_START = /Starting (incremental )?compilation/;\nconst WATCH_DONE = /Found (\\d+) errors?\\. Watching for file changes/;\nconst ERROR_LINE = /error TS\\d+:/;\n\n/**\n * Builds through the project's `tsc -b`, parsing its output to report progress.\n */\nexport class TscBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'tsc' as const;\n\t#child: ChildProcess | null = null;\n\t#startedAt = 0;\n\t#firstError: string | null = null;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic build(): Promise<BuildOutcome> {\n\t\tconst child = this.#spawn([]);\n\t\tthis.#begin();\n\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once('exit', (code) => {\n\t\t\t\tthis.#child = null;\n\t\t\t\tresolve(this.#finish(code === 0 ? null : (this.#firstError ?? `tsc exited with code ${code}`)));\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic watch(): Promise<void> {\n\t\tthis.#spawn(['--watch', '--preserveWatchOutput']);\n\t\tthis.#begin();\n\t\treturn Promise.resolve();\n\t}\n\n\tpublic close(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tthis.#child = null;\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#spawn(extraArgs: string[]): ChildProcess {\n\t\tconst tsc = resolveTscBinary(this.config.root);\n\t\tif (!tsc) throw cliDiagnostics.DEPENDENCY_MISSING({ name: 'typescript', root: this.config.root, hint: INSTALL_HINT });\n\n\t\tconst child = spawn(process.execPath, [tsc, '-b', this.config.build.tsconfig!, '--pretty', 'false', ...extraArgs], {\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\n\t\tconst handleLine = (line: string) => this.#handleLine(line);\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\t\tchild.once('exit', () => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t});\n\t\tchild.once('error', (error) => this.emit('log', 'error', error.message));\n\n\t\treturn child;\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\tif (this.#startedAt === 0) this.#begin();\n\t\t\treturn;\n\t\t}\n\n\t\tif (ERROR_LINE.test(text)) {\n\t\t\tthis.#firstError ??= 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);\n\t\tif (done) {\n\t\t\tconst errors = Number(done[1]);\n\t\t\tthis.emit('log', errors === 0 ? 'success' : 'error', text);\n\t\t\tthis.#finish(errors === 0 ? null : (this.#firstError ?? text));\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit('log', 'info', text);\n\t}\n\n\t#begin(): void {\n\t\tthis.#firstError = null;\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = { ok: message === null, durationMs: Math.round(performance.now() - this.#startedAt), message };\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n}\n\n/**\n * Locates the `tsc` script of the project's TypeScript through its `package.json#bin`, which works for both the\n * classic `lib/tsc.js` layout (TypeScript ≤ 6) and the native `bin/tsc` launcher (TypeScript 7, whose `exports` map\n * hides `lib/`).\n */\nexport function resolveTscBinary(root: string): string | null {\n\treturn resolveBinary(root, 'typescript', 'tsc');\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { n as resolveTscBinary, t as TscBuilder } from "./tsc-Bex5dwzD.js";
2
+
3
+ export { TscBuilder };
@@ -1,5 +1,5 @@
1
- import { n as importFromProject } from "./project-jVFFrLl9.js";
2
1
  import { t as pluginRegistrations } from "./plugin-registrations-4PA_qWXF.js";
2
+ import { n as importFromProject } from "./project-DT3wg_lf.js";
3
3
  import { dirname, extname, relative, resolve } from "node:path";
4
4
  import { EventEmitter } from "node:events";
5
5
 
@@ -244,4 +244,4 @@ function errorSummary(args) {
244
244
 
245
245
  //#endregion
246
246
  export { TsdownBuilder };
247
- //# sourceMappingURL=tsdown-C9q3H3MK.js.map
247
+ //# sourceMappingURL=tsdown-scDokwb0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tsdown-C9q3H3MK.js","names":[],"sources":["../src/builders/tsdown.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { dirname, extname, relative, resolve } from 'node:path';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { pluginRegistrations } from '../utils/plugin-registrations.js';\nimport { importFromProject } from '../utils/project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype TsdownModule = typeof import('tsdown');\ntype TsdownLogger = import('tsdown').Logger;\ntype TsdownHandle = import('tsdown').TsdownHandle;\ntype TsdownInlineConfig = NonNullable<Parameters<TsdownModule['build']>[0]>;\ntype TsdownOptions = Record<string, unknown>;\ntype AnyFunction = (...args: never[]) => unknown;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D tsdown`, or set `build.tool` to 'tsc' or 'none'.\";\nconst TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.mts', '.cts']);\n/** `./src/lib` and `../shared` are paths; `preact/compat` is a module id and must be left alone. */\nconst RELATIVE_PATH = /^\\.\\.?[/\\\\]/;\n\n/**\n * Builds through the project's own `tsdown`, configured from `stars.config`.\n *\n * From `future.compatibilityVersion` 4 on this is the whole configuration: the entry's directory, `build.outDir` and\n * `build.tsconfig` produce the defaults a bot needs, the project's `tsdown` block is layered on top, and the auto\n * imports plugin is wired in — one configuration file instead of two. At 3 the project's own `tsdown.config.*` is\n * still loaded and everything here is merged over it by `tsdown` itself: scalars from `stars.config` win, and\n * `plugins` are appended rather than replaced.\n */\nexport class TsdownBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'tsdown' as const;\n\t#handle: TsdownHandle | null = null;\n\t#hadError = false;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst tsdown = await this.#load();\n\t\tconst options = await this.#options();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tthis.#handle = await tsdown.build(options as TsdownInlineConfig);\n\t\t\treturn this.#finish(this.#hadError ? 'The build reported errors' : null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst tsdown = await this.#load();\n\t\tconst options = await this.#options();\n\n\t\tthis.#begin();\n\t\tthis.#handle = await tsdown.build(this.#watchOptions(options) as TsdownInlineConfig);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst handle = this.#handle;\n\t\tthis.#handle = null;\n\t\tawait handle?.watch.close();\n\t}\n\n\t#load(): Promise<TsdownModule> {\n\t\treturn importFromProject<TsdownModule>(this.config.root, 'tsdown', INSTALL_HINT);\n\t}\n\n\t/**\n\t * The options handed to `tsdown.build()`, with the project's own `tsdown` block already layered on top of the\n\t * defaults (and of the plugins `stars` contributes).\n\t */\n\tasync #options(): Promise<TsdownOptions> {\n\t\tconst user = this.config.tsdown as TsdownOptions;\n\t\t// A `tsdown.config.*` (or `package.json#tsdown`) is the project's own; `build.configFile` is `null` from\n\t\t// compatibility version 4 on, where loading one is a configuration error rather than a fallback.\n\t\tconst defaults = this.config.build.configFile === null ? this.#defaults() : {};\n\t\tconst plugins = [pluginRegistrations(this.config), ...(await this.#plugins()), ...toArray(user.plugins)];\n\t\t// `plugins` and `alias` are added to rather than replaced: a project declaring one of its own would\n\t\t// otherwise silently drop the auto imports transform, or every `~`/`@` import in its sources.\n\t\tconst alias = { ...(defaults.alias as object), ...this.#alias(user.alias) };\n\n\t\treturn {\n\t\t\t...defaults,\n\t\t\t...user,\n\t\t\t...(plugins.length > 0 ? { plugins } : {}),\n\t\t\t...(Object.keys(alias).length > 0 ? { alias } : {}),\n\t\t\t// Last, and so not overridable: these are how the CLI talks to `tsdown` rather than project build options —\n\t\t\t// the project root it resolves from, its own compact progress UI, and the logger that feeds diagnostics to\n\t\t\t// the dev panel. `logLevel` also covers the few lifecycle messages tsdown writes through its global logger.\n\t\t\tcwd: this.config.root,\n\t\t\tlogLevel: 'warn',\n\t\t\tcustomLogger: this.#logger()\n\t\t};\n\t}\n\n\t/**\n\t * What `stars.config` alone says the build is — the configuration a bot would otherwise write out by hand, taken\n\t * from the ones the WolfStar bots already keep next to theirs:\n\t *\n\t * - every source file next to the entry (minus tests), emitted one-to-one (`unbundle`) so the stores keep\n\t * loading pieces from `dist/commands` and friends at runtime;\n\t * - ESM on `platform: 'node'`, with the extension `build.output` — and so `stars dev`, `package.json#main` and\n\t * `node dist/…` — expects;\n\t * - sourcemaps and treeshaking on, minification off: a bot is deployed, not shipped, so readable stack traces\n\t * are worth more than bytes;\n\t * - dependencies left in `node_modules` rather than bundled into the output;\n\t * - Nuxt's own alias prefixes: `~` and `@` for the entry's directory, `~~` and `@@` for the project root;\n\t * - no declaration files: nothing consumes a bot's `dist/`, and emitting them roughly doubles the build. A\n\t * project that wants them sets `tsdown: { dts: true }`.\n\t *\n\t * Every one of these is overridable through the project's `tsdown` block.\n\t */\n\t#defaults(): TsdownOptions {\n\t\tconst { root, entry, build } = this.config;\n\t\tconst source = dirname(entry);\n\t\tconst sourceDir = relative(root, source) || '.';\n\t\tconst extensions = TYPESCRIPT_EXTENSIONS.has(extname(entry)) ? '{ts,mts,cts}' : '{js,mjs,cjs}';\n\n\t\treturn {\n\t\t\tconfig: false,\n\t\t\tentry: [`${sourceDir}/**/*.${extensions}`, `!${sourceDir}/**/*.{test,spec}.${extensions}`],\n\t\t\t// The prefixes Nuxt gives every project, pointing at the same two places its own do: the source\n\t\t\t// directory and the project root. A project's tsconfig needs the matching `paths` for them to type.\n\t\t\talias: { '~': source, '@': source, '~~': root, '@@': root },\n\t\t\tformat: 'esm',\n\t\t\tplatform: 'node',\n\t\t\tunbundle: true,\n\t\t\toutDir: build.outDir,\n\t\t\toutExtensions: () => ({ js: extname(build.output) }),\n\t\t\tsourcemap: true,\n\t\t\tclean: true,\n\t\t\ttreeshake: true,\n\t\t\tminify: false,\n\t\t\tdts: false,\n\t\t\tdeps: { neverBundle: true },\n\t\t\t...(build.tsconfig === null ? {} : { tsconfig: build.tsconfig })\n\t\t};\n\t}\n\n\t/**\n\t * The project's own aliases, with relative targets resolved against the project root the way every other path in\n\t * `stars.config` is. Module ids (`preact/compat`) and absolute paths are passed through untouched.\n\t */\n\t#alias(alias: unknown): Record<string, unknown> {\n\t\tif (alias === null || typeof alias !== 'object' || Array.isArray(alias)) return {};\n\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(alias as Record<string, unknown>).map(([key, value]) => [\n\t\t\t\tkey,\n\t\t\t\ttypeof value === 'string' && RELATIVE_PATH.test(value) ? resolve(this.config.root, value) : value\n\t\t\t])\n\t\t);\n\t}\n\n\t/**\n\t * The auto imports transform, the way `nuxt dev` wires its own into the build rather than asking the project to.\n\t * `stars dev`/`stars build` regenerate its declaration file (`stars prepare`) before the first build.\n\t */\n\tasync #plugins(): Promise<unknown[]> {\n\t\tif (!this.config.imports.enabled) return [];\n\n\t\tconst { autoImports } = await import('@wolfstar/http-framework/auto-imports');\n\t\tconst { dirs, presets, exclude, dts } = this.config.imports;\n\t\treturn [await autoImports({ root: this.config.root, dirs, presets, exclude, dts })];\n\t}\n\n\t/**\n\t * Watch mode reports through the same events a single build does. The project's own `hooks`/`onSuccess` still\n\t * run: they are the reason a build is watched at all in some projects.\n\t */\n\t#watchOptions(options: TsdownOptions): TsdownOptions {\n\t\tconst hooks = (options.hooks ?? {}) as Record<string, unknown>;\n\t\tconst prepare = hooks['build:prepare'];\n\t\tconst before = hooks['build:before'];\n\t\tconst done = hooks['build:done'];\n\t\tconst success = options.onSuccess;\n\n\t\treturn {\n\t\t\t...options,\n\t\t\twatch: true,\n\t\t\t// tsdown's build:before runs once when the watcher is configured, not on every rebuild. Rolldown's\n\t\t\t// buildStart is the actual per-build boundary, including recovery after a failed compilation.\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'stars:dev-progress',\n\t\t\t\t\tbuildStart: () => {\n\t\t\t\t\t\tif (this.#startedAt === 0) this.#begin();\n\t\t\t\t\t\tthis.emit('progress', 0.25, 'bundling app');\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t...toArray(options.plugins)\n\t\t\t],\n\t\t\thooks: {\n\t\t\t\t...hooks,\n\t\t\t\t'build:prepare': async (...args: never[]) => {\n\t\t\t\t\tif (this.#startedAt === 0) this.#begin();\n\t\t\t\t\tif (typeof prepare === 'function') await (prepare as AnyFunction)(...args);\n\t\t\t\t},\n\t\t\t\t'build:before': async (...args: never[]) => {\n\t\t\t\t\tif (typeof before === 'function') await (before as AnyFunction)(...args);\n\t\t\t\t},\n\t\t\t\t'build:done': async (...args: never[]) => {\n\t\t\t\t\tthis.emit('progress', 0.5, 'finishing build');\n\t\t\t\t\tif (typeof done === 'function') await (done as AnyFunction)(...args);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonSuccess: async (...args: never[]) => {\n\t\t\t\tif (typeof success === 'function') await (success as AnyFunction)(...args);\n\t\t\t\tthis.#finish(null);\n\t\t\t}\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#hadError = false;\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = {\n\t\t\tok: message === null && !this.#hadError,\n\t\t\tdurationMs: Math.round(performance.now() - this.#startedAt),\n\t\t\tmessage\n\t\t};\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n\n\t#logger(): TsdownLogger {\n\t\tconst log = (level: 'info' | 'warn' | 'error' | 'success', args: unknown[]) => {\n\t\t\t// tsdown passes its (possibly undefined) name label and other blanks as separate arguments.\n\t\t\tconst text = args\n\t\t\t\t.filter((argument) => argument !== undefined && argument !== null && argument !== false && argument !== '')\n\t\t\t\t.map((argument) => (argument instanceof Error ? (argument.stack ?? argument.message) : String(argument)))\n\t\t\t\t.join(' ');\n\t\t\tthis.emit('log', level, text);\n\t\t};\n\n\t\tconst warned = new Set<string>();\n\t\treturn {\n\t\t\t// `stars` owns the build progress and completion messages. Keeping tsdown at `warn` avoids copying its\n\t\t\t// entry list, target, output table and completion line into the dev session while preserving diagnostics\n\t\t\t// that need action from the project.\n\t\t\tlevel: 'warn',\n\t\t\tinfo: () => {},\n\t\t\twarn: (...args) => log('warn', args),\n\t\t\twarnOnce: (...args) => {\n\t\t\t\tconst key = args.map(String).join(' ');\n\t\t\t\tif (warned.has(key)) return;\n\t\t\t\twarned.add(key);\n\t\t\t\tlog('warn', args);\n\t\t\t},\n\t\t\terror: (...args) => {\n\t\t\t\tthis.#hadError = true;\n\t\t\t\tlog('error', args);\n\t\t\t\t// In watch mode tsdown never calls `onSuccess` after an error, report the failure now.\n\t\t\t\tif (this.#startedAt !== 0) this.#finish(errorSummary(args));\n\t\t\t},\n\t\t\tsuccess: () => {},\n\t\t\tclearScreen: () => {}\n\t\t};\n\t}\n}\n\nfunction toArray(value: unknown): unknown[] {\n\tif (value === undefined || value === null || value === false) return [];\n\treturn Array.isArray(value) ? value : [value];\n}\n\nfunction errorSummary(args: unknown[]): string {\n\tconst first = args[0];\n\tif (first instanceof Error) return first.message.split('\\n')[0] ?? 'Build failed';\n\treturn String(first ?? 'Build failed').split('\\n')[0] ?? 'Build failed';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tsdown-scDokwb0.js","names":[],"sources":["../src/builders/tsdown.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { dirname, extname, relative, resolve } from 'node:path';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { pluginRegistrations } from '../utils/plugin-registrations.js';\nimport { importFromProject } from '../utils/project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype TsdownModule = typeof import('tsdown');\ntype TsdownLogger = import('tsdown').Logger;\ntype TsdownHandle = import('tsdown').TsdownHandle;\ntype TsdownInlineConfig = NonNullable<Parameters<TsdownModule['build']>[0]>;\ntype TsdownOptions = Record<string, unknown>;\ntype AnyFunction = (...args: never[]) => unknown;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D tsdown`, or set `build.tool` to 'tsc' or 'none'.\";\nconst TYPESCRIPT_EXTENSIONS = new Set(['.ts', '.mts', '.cts']);\n/** `./src/lib` and `../shared` are paths; `preact/compat` is a module id and must be left alone. */\nconst RELATIVE_PATH = /^\\.\\.?[/\\\\]/;\n\n/**\n * Builds through the project's own `tsdown`, configured from `stars.config`.\n *\n * From `future.compatibilityVersion` 4 on this is the whole configuration: the entry's directory, `build.outDir` and\n * `build.tsconfig` produce the defaults a bot needs, the project's `tsdown` block is layered on top, and the auto\n * imports plugin is wired in — one configuration file instead of two. At 3 the project's own `tsdown.config.*` is\n * still loaded and everything here is merged over it by `tsdown` itself: scalars from `stars.config` win, and\n * `plugins` are appended rather than replaced.\n */\nexport class TsdownBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'tsdown' as const;\n\t#handle: TsdownHandle | null = null;\n\t#hadError = false;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst tsdown = await this.#load();\n\t\tconst options = await this.#options();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tthis.#handle = await tsdown.build(options as TsdownInlineConfig);\n\t\t\treturn this.#finish(this.#hadError ? 'The build reported errors' : null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst tsdown = await this.#load();\n\t\tconst options = await this.#options();\n\n\t\tthis.#begin();\n\t\tthis.#handle = await tsdown.build(this.#watchOptions(options) as TsdownInlineConfig);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst handle = this.#handle;\n\t\tthis.#handle = null;\n\t\tawait handle?.watch.close();\n\t}\n\n\t#load(): Promise<TsdownModule> {\n\t\treturn importFromProject<TsdownModule>(this.config.root, 'tsdown', INSTALL_HINT);\n\t}\n\n\t/**\n\t * The options handed to `tsdown.build()`, with the project's own `tsdown` block already layered on top of the\n\t * defaults (and of the plugins `stars` contributes).\n\t */\n\tasync #options(): Promise<TsdownOptions> {\n\t\tconst user = this.config.tsdown as TsdownOptions;\n\t\t// A `tsdown.config.*` (or `package.json#tsdown`) is the project's own; `build.configFile` is `null` from\n\t\t// compatibility version 4 on, where loading one is a configuration error rather than a fallback.\n\t\tconst defaults = this.config.build.configFile === null ? this.#defaults() : {};\n\t\tconst plugins = [pluginRegistrations(this.config), ...(await this.#plugins()), ...toArray(user.plugins)];\n\t\t// `plugins` and `alias` are added to rather than replaced: a project declaring one of its own would\n\t\t// otherwise silently drop the auto imports transform, or every `~`/`@` import in its sources.\n\t\tconst alias = { ...(defaults.alias as object), ...this.#alias(user.alias) };\n\n\t\treturn {\n\t\t\t...defaults,\n\t\t\t...user,\n\t\t\t...(plugins.length > 0 ? { plugins } : {}),\n\t\t\t...(Object.keys(alias).length > 0 ? { alias } : {}),\n\t\t\t// Last, and so not overridable: these are how the CLI talks to `tsdown` rather than project build options —\n\t\t\t// the project root it resolves from, its own compact progress UI, and the logger that feeds diagnostics to\n\t\t\t// the dev panel. `logLevel` also covers the few lifecycle messages tsdown writes through its global logger.\n\t\t\tcwd: this.config.root,\n\t\t\tlogLevel: 'warn',\n\t\t\tcustomLogger: this.#logger()\n\t\t};\n\t}\n\n\t/**\n\t * What `stars.config` alone says the build is — the configuration a bot would otherwise write out by hand, taken\n\t * from the ones the WolfStar bots already keep next to theirs:\n\t *\n\t * - every source file next to the entry (minus tests), emitted one-to-one (`unbundle`) so the stores keep\n\t * loading pieces from `dist/commands` and friends at runtime;\n\t * - ESM on `platform: 'node'`, with the extension `build.output` — and so `stars dev`, `package.json#main` and\n\t * `node dist/…` — expects;\n\t * - sourcemaps and treeshaking on, minification off: a bot is deployed, not shipped, so readable stack traces\n\t * are worth more than bytes;\n\t * - dependencies left in `node_modules` rather than bundled into the output;\n\t * - Nuxt's own alias prefixes: `~` and `@` for the entry's directory, `~~` and `@@` for the project root;\n\t * - no declaration files: nothing consumes a bot's `dist/`, and emitting them roughly doubles the build. A\n\t * project that wants them sets `tsdown: { dts: true }`.\n\t *\n\t * Every one of these is overridable through the project's `tsdown` block.\n\t */\n\t#defaults(): TsdownOptions {\n\t\tconst { root, entry, build } = this.config;\n\t\tconst source = dirname(entry);\n\t\tconst sourceDir = relative(root, source) || '.';\n\t\tconst extensions = TYPESCRIPT_EXTENSIONS.has(extname(entry)) ? '{ts,mts,cts}' : '{js,mjs,cjs}';\n\n\t\treturn {\n\t\t\tconfig: false,\n\t\t\tentry: [`${sourceDir}/**/*.${extensions}`, `!${sourceDir}/**/*.{test,spec}.${extensions}`],\n\t\t\t// The prefixes Nuxt gives every project, pointing at the same two places its own do: the source\n\t\t\t// directory and the project root. A project's tsconfig needs the matching `paths` for them to type.\n\t\t\talias: { '~': source, '@': source, '~~': root, '@@': root },\n\t\t\tformat: 'esm',\n\t\t\tplatform: 'node',\n\t\t\tunbundle: true,\n\t\t\toutDir: build.outDir,\n\t\t\toutExtensions: () => ({ js: extname(build.output) }),\n\t\t\tsourcemap: true,\n\t\t\tclean: true,\n\t\t\ttreeshake: true,\n\t\t\tminify: false,\n\t\t\tdts: false,\n\t\t\tdeps: { neverBundle: true },\n\t\t\t...(build.tsconfig === null ? {} : { tsconfig: build.tsconfig })\n\t\t};\n\t}\n\n\t/**\n\t * The project's own aliases, with relative targets resolved against the project root the way every other path in\n\t * `stars.config` is. Module ids (`preact/compat`) and absolute paths are passed through untouched.\n\t */\n\t#alias(alias: unknown): Record<string, unknown> {\n\t\tif (alias === null || typeof alias !== 'object' || Array.isArray(alias)) return {};\n\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(alias as Record<string, unknown>).map(([key, value]) => [\n\t\t\t\tkey,\n\t\t\t\ttypeof value === 'string' && RELATIVE_PATH.test(value) ? resolve(this.config.root, value) : value\n\t\t\t])\n\t\t);\n\t}\n\n\t/**\n\t * The auto imports transform, the way `nuxt dev` wires its own into the build rather than asking the project to.\n\t * `stars dev`/`stars build` regenerate its declaration file (`stars prepare`) before the first build.\n\t */\n\tasync #plugins(): Promise<unknown[]> {\n\t\tif (!this.config.imports.enabled) return [];\n\n\t\tconst { autoImports } = await import('@wolfstar/http-framework/auto-imports');\n\t\tconst { dirs, presets, exclude, dts } = this.config.imports;\n\t\treturn [await autoImports({ root: this.config.root, dirs, presets, exclude, dts })];\n\t}\n\n\t/**\n\t * Watch mode reports through the same events a single build does. The project's own `hooks`/`onSuccess` still\n\t * run: they are the reason a build is watched at all in some projects.\n\t */\n\t#watchOptions(options: TsdownOptions): TsdownOptions {\n\t\tconst hooks = (options.hooks ?? {}) as Record<string, unknown>;\n\t\tconst prepare = hooks['build:prepare'];\n\t\tconst before = hooks['build:before'];\n\t\tconst done = hooks['build:done'];\n\t\tconst success = options.onSuccess;\n\n\t\treturn {\n\t\t\t...options,\n\t\t\twatch: true,\n\t\t\t// tsdown's build:before runs once when the watcher is configured, not on every rebuild. Rolldown's\n\t\t\t// buildStart is the actual per-build boundary, including recovery after a failed compilation.\n\t\t\tplugins: [\n\t\t\t\t{\n\t\t\t\t\tname: 'stars:dev-progress',\n\t\t\t\t\tbuildStart: () => {\n\t\t\t\t\t\tif (this.#startedAt === 0) this.#begin();\n\t\t\t\t\t\tthis.emit('progress', 0.25, 'bundling app');\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t...toArray(options.plugins)\n\t\t\t],\n\t\t\thooks: {\n\t\t\t\t...hooks,\n\t\t\t\t'build:prepare': async (...args: never[]) => {\n\t\t\t\t\tif (this.#startedAt === 0) this.#begin();\n\t\t\t\t\tif (typeof prepare === 'function') await (prepare as AnyFunction)(...args);\n\t\t\t\t},\n\t\t\t\t'build:before': async (...args: never[]) => {\n\t\t\t\t\tif (typeof before === 'function') await (before as AnyFunction)(...args);\n\t\t\t\t},\n\t\t\t\t'build:done': async (...args: never[]) => {\n\t\t\t\t\tthis.emit('progress', 0.5, 'finishing build');\n\t\t\t\t\tif (typeof done === 'function') await (done as AnyFunction)(...args);\n\t\t\t\t}\n\t\t\t},\n\t\t\tonSuccess: async (...args: never[]) => {\n\t\t\t\tif (typeof success === 'function') await (success as AnyFunction)(...args);\n\t\t\t\tthis.#finish(null);\n\t\t\t}\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#hadError = false;\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = {\n\t\t\tok: message === null && !this.#hadError,\n\t\t\tdurationMs: Math.round(performance.now() - this.#startedAt),\n\t\t\tmessage\n\t\t};\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n\n\t#logger(): TsdownLogger {\n\t\tconst log = (level: 'info' | 'warn' | 'error' | 'success', args: unknown[]) => {\n\t\t\t// tsdown passes its (possibly undefined) name label and other blanks as separate arguments.\n\t\t\tconst text = args\n\t\t\t\t.filter((argument) => argument !== undefined && argument !== null && argument !== false && argument !== '')\n\t\t\t\t.map((argument) => (argument instanceof Error ? (argument.stack ?? argument.message) : String(argument)))\n\t\t\t\t.join(' ');\n\t\t\tthis.emit('log', level, text);\n\t\t};\n\n\t\tconst warned = new Set<string>();\n\t\treturn {\n\t\t\t// `stars` owns the build progress and completion messages. Keeping tsdown at `warn` avoids copying its\n\t\t\t// entry list, target, output table and completion line into the dev session while preserving diagnostics\n\t\t\t// that need action from the project.\n\t\t\tlevel: 'warn',\n\t\t\tinfo: () => {},\n\t\t\twarn: (...args) => log('warn', args),\n\t\t\twarnOnce: (...args) => {\n\t\t\t\tconst key = args.map(String).join(' ');\n\t\t\t\tif (warned.has(key)) return;\n\t\t\t\twarned.add(key);\n\t\t\t\tlog('warn', args);\n\t\t\t},\n\t\t\terror: (...args) => {\n\t\t\t\tthis.#hadError = true;\n\t\t\t\tlog('error', args);\n\t\t\t\t// In watch mode tsdown never calls `onSuccess` after an error, report the failure now.\n\t\t\t\tif (this.#startedAt !== 0) this.#finish(errorSummary(args));\n\t\t\t},\n\t\t\tsuccess: () => {},\n\t\t\tclearScreen: () => {}\n\t\t};\n\t}\n}\n\nfunction toArray(value: unknown): unknown[] {\n\tif (value === undefined || value === null || value === false) return [];\n\treturn Array.isArray(value) ? value : [value];\n}\n\nfunction errorSummary(args: unknown[]): string {\n\tconst first = args[0];\n\tif (first instanceof Error) return first.message.split('\\n')[0] ?? 'Build failed';\n\treturn String(first ?? 'Build failed').split('\\n')[0] ?? 'Build failed';\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { n as importFromProject } from "./project-jVFFrLl9.js";
2
1
  import { t as pluginRegistrations } from "./plugin-registrations-4PA_qWXF.js";
2
+ import { n as importFromProject } from "./project-DT3wg_lf.js";
3
3
  import { EventEmitter } from "node:events";
4
4
 
5
5
  //#region src/builders/vite.ts
@@ -106,4 +106,4 @@ function toArray(value) {
106
106
 
107
107
  //#endregion
108
108
  export { ViteBuilder };
109
- //# sourceMappingURL=vite-BVXBSWQb.js.map
109
+ //# sourceMappingURL=vite-C-szLSGb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-BVXBSWQb.js","names":[],"sources":["../src/builders/vite.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { EventEmitter } from 'node:events';\nimport { pluginRegistrations } from '../utils/plugin-registrations.js';\nimport { importFromProject } from '../utils/project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype ViteModule = typeof import('vite');\ntype RollupWatcher =\n\tAwaited<ReturnType<ViteModule['build']>> extends infer Result\n\t\t? Result extends { close: () => Promise<void>; on: (...args: never[]) => unknown }\n\t\t\t? Result\n\t\t\t: never\n\t\t: never;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D vite`, or set `build.tool` to 'tsdown', 'tsc' or 'none'.\";\n\n/**\n * Builds through the project's own `vite`, honouring its `vite.config.*`.\n *\n * Guarded by `experimental.enableVite`: Vite replaces `tsdown` as the bundler and, with the framework's Fetch\n * adapter, the `node:http` listener too, so a project can serve its interactions endpoint the same way it serves\n * everything else.\n */\nexport class ViteBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'vite' as const;\n\t#watcher: RollupWatcher | null = null;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tawait vite.build(this.#options(vite));\n\t\t\treturn this.#finish(null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\t// `build.watch` makes Vite return a rollup watcher instead of resolving once, which is what keeps the bot\n\t\t// rebuilding on change; every rebuild reports through the same events the other builders use.\n\t\tconst options = this.#options(vite);\n\t\tconst result = (await vite.build({ ...options, build: { ...options?.build, watch: {} } })) as unknown as RollupWatcher;\n\n\t\tthis.#watcher = result;\n\t\tresult.on(\n\t\t\t'event' as never,\n\t\t\t((event: { code: string; error?: Error }) => {\n\t\t\t\tswitch (event.code) {\n\t\t\t\t\tcase 'BUNDLE_START':\n\t\t\t\t\t\treturn this.#begin();\n\t\t\t\t\tcase 'BUNDLE_END':\n\t\t\t\t\t\treturn void this.#finish(null);\n\t\t\t\t\tcase 'ERROR':\n\t\t\t\t\t\treturn void this.#finish(event.error?.message ?? 'The build reported errors');\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}) as never\n\t\t);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst watcher = this.#watcher;\n\t\tthis.#watcher = null;\n\t\tawait watcher?.close();\n\t}\n\n\tasync #load(): Promise<ViteModule> {\n\t\treturn importFromProject<ViteModule>(this.config.root, 'vite', INSTALL_HINT);\n\t}\n\n\t/**\n\t * The inline config Vite merges into the project's own `vite.config.*`, with the `vite` block of `stars.config`\n\t * layered on top — the way `vite: {}` in a Nuxt config is merged into Nuxt's own.\n\t */\n\t#options(vite: ViteModule): Parameters<ViteModule['build']>[0] {\n\t\treturn {\n\t\t\troot: this.config.root,\n\t\t\tlogLevel: 'warn',\n\t\t\tcustomLogger: this.#logger(vite),\n\t\t\t...this.config.vite,\n\t\t\tplugins: [pluginRegistrations(this.config), ...toArray(this.config.vite.plugins)]\n\t\t} as Parameters<ViteModule['build']>[0];\n\t}\n\n\t#logger(vite: ViteModule) {\n\t\tconst logger = vite.createLogger('warn', { allowClearScreen: false });\n\t\treturn {\n\t\t\t...logger,\n\t\t\tinfo: (message: string) => this.emit('log', 'info', message),\n\t\t\twarn: (message: string) => this.emit('log', 'warn', message),\n\t\t\twarnOnce: (message: string) => this.emit('log', 'warn', message),\n\t\t\terror: (message: string) => this.emit('log', 'error', message)\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = { ok: message === null, durationMs: Math.round(performance.now() - this.#startedAt), message };\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n}\n\nfunction toArray(value: unknown): unknown[] {\n\tif (value === undefined || value === null || value === false) return [];\n\treturn Array.isArray(value) ? value : [value];\n}\n"],"mappings":";;;;;AAcA,MAAM,eAAe;;;;;;;;AASrB,IAAa,cAAb,cAAiC,aAA+C;CAK3C;CAJpC,AAAgB,OAAO;CACvB,WAAiC;CACjC,aAAa;CAEb,AAAO,YAAY,AAAiB,QAA6B;EAChE,MAAM;EAD6B;CAEpC;CAEA,MAAa,QAA+B;EAC3C,MAAM,OAAO,MAAM,KAAK,MAAM;EAC9B,KAAK,OAAO;EAEZ,IAAI;GACH,MAAM,KAAK,MAAM,KAAK,SAAS,IAAI,CAAC;GACpC,OAAO,KAAK,QAAQ,IAAI;EACzB,SAAS,OAAO;GACf,OAAO,KAAK,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC3E;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,OAAO,MAAM,KAAK,MAAM;EAC9B,KAAK,OAAO;EAIZ,MAAM,UAAU,KAAK,SAAS,IAAI;EAClC,MAAM,SAAU,MAAM,KAAK,MAAM;GAAE,GAAG;GAAS,OAAO;IAAE,GAAG,SAAS;IAAO,OAAO,CAAC;GAAE;EAAE,CAAC;EAExF,KAAK,WAAW;EAChB,OAAO,GACN,WACE,UAA2C;GAC5C,QAAQ,MAAM,MAAd;IACC,KAAK,gBACJ,OAAO,KAAK,OAAO;IACpB,KAAK;KACG,AAAK,KAAK,QAAQ,IAAI;KAA7B;IACD,KAAK;KACG,AAAK,KAAK,QAAQ,MAAM,OAAO,WAAW,2BAA2B;KAA5E;GAGF;EACD,EACD;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,UAAU,KAAK;EACrB,KAAK,WAAW;EAChB,MAAM,SAAS,MAAM;CACtB;CAEA,MAAM,QAA6B;EAClC,OAAO,kBAA8B,KAAK,OAAO,MAAM,QAAQ,YAAY;CAC5E;;;;;CAMA,SAAS,MAAsD;EAC9D,OAAO;GACN,MAAM,KAAK,OAAO;GAClB,UAAU;GACV,cAAc,KAAK,QAAQ,IAAI;GAC/B,GAAG,KAAK,OAAO;GACf,SAAS,CAAC,oBAAoB,KAAK,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,KAAK,OAAO,CAAC;EACjF;CACD;CAEA,QAAQ,MAAkB;EAEzB,OAAO;GACN,GAFc,KAAK,aAAa,QAAQ,EAAE,kBAAkB,MAAM,CAE1D;GACR,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,WAAW,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC/D,QAAQ,YAAoB,KAAK,KAAK,OAAO,SAAS,OAAO;EAC9D;CACD;CAEA,SAAe;EACd,KAAK,aAAa,YAAY,IAAI;EAClC,KAAK,KAAK,OAAO;CAClB;CAEA,QAAQ,SAAsC;EAC7C,MAAM,UAAwB;GAAE,IAAI,YAAY;GAAM,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,KAAK,UAAU;GAAG;EAAQ;EAC3H,KAAK,aAAa;EAClB,KAAK,KAAK,QAAQ,KAAK,YAAY,WAAW,OAAO;EACrD,OAAO;CACR;AACD;AAEA,SAAS,QAAQ,OAA2B;CAC3C,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,OAAO,OAAO,CAAC;CACtE,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC7C"}
1
+ {"version":3,"file":"vite-C-szLSGb.js","names":[],"sources":["../src/builders/vite.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { EventEmitter } from 'node:events';\nimport { pluginRegistrations } from '../utils/plugin-registrations.js';\nimport { importFromProject } from '../utils/project.js';\nimport type { Builder, BuilderEvents, BuildOutcome } from './types.js';\n\ntype ViteModule = typeof import('vite');\ntype RollupWatcher =\n\tAwaited<ReturnType<ViteModule['build']>> extends infer Result\n\t\t? Result extends { close: () => Promise<void>; on: (...args: never[]) => unknown }\n\t\t\t? Result\n\t\t\t: never\n\t\t: never;\n\nconst INSTALL_HINT = \"Install it with `pnpm add -D vite`, or set `build.tool` to 'tsdown', 'tsc' or 'none'.\";\n\n/**\n * Builds through the project's own `vite`, honouring its `vite.config.*`.\n *\n * Guarded by `experimental.enableVite`: Vite replaces `tsdown` as the bundler and, with the framework's Fetch\n * adapter, the `node:http` listener too, so a project can serve its interactions endpoint the same way it serves\n * everything else.\n */\nexport class ViteBuilder extends EventEmitter<BuilderEvents> implements Builder {\n\tpublic readonly tool = 'vite' as const;\n\t#watcher: RollupWatcher | null = null;\n\t#startedAt = 0;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic async build(): Promise<BuildOutcome> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\ttry {\n\t\t\tawait vite.build(this.#options(vite));\n\t\t\treturn this.#finish(null);\n\t\t} catch (error) {\n\t\t\treturn this.#finish(error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\tpublic async watch(): Promise<void> {\n\t\tconst vite = await this.#load();\n\t\tthis.#begin();\n\n\t\t// `build.watch` makes Vite return a rollup watcher instead of resolving once, which is what keeps the bot\n\t\t// rebuilding on change; every rebuild reports through the same events the other builders use.\n\t\tconst options = this.#options(vite);\n\t\tconst result = (await vite.build({ ...options, build: { ...options?.build, watch: {} } })) as unknown as RollupWatcher;\n\n\t\tthis.#watcher = result;\n\t\tresult.on(\n\t\t\t'event' as never,\n\t\t\t((event: { code: string; error?: Error }) => {\n\t\t\t\tswitch (event.code) {\n\t\t\t\t\tcase 'BUNDLE_START':\n\t\t\t\t\t\treturn this.#begin();\n\t\t\t\t\tcase 'BUNDLE_END':\n\t\t\t\t\t\treturn void this.#finish(null);\n\t\t\t\t\tcase 'ERROR':\n\t\t\t\t\t\treturn void this.#finish(event.error?.message ?? 'The build reported errors');\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}) as never\n\t\t);\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tconst watcher = this.#watcher;\n\t\tthis.#watcher = null;\n\t\tawait watcher?.close();\n\t}\n\n\tasync #load(): Promise<ViteModule> {\n\t\treturn importFromProject<ViteModule>(this.config.root, 'vite', INSTALL_HINT);\n\t}\n\n\t/**\n\t * The inline config Vite merges into the project's own `vite.config.*`, with the `vite` block of `stars.config`\n\t * layered on top — the way `vite: {}` in a Nuxt config is merged into Nuxt's own.\n\t */\n\t#options(vite: ViteModule): Parameters<ViteModule['build']>[0] {\n\t\treturn {\n\t\t\troot: this.config.root,\n\t\t\tlogLevel: 'warn',\n\t\t\tcustomLogger: this.#logger(vite),\n\t\t\t...this.config.vite,\n\t\t\tplugins: [pluginRegistrations(this.config), ...toArray(this.config.vite.plugins)]\n\t\t} as Parameters<ViteModule['build']>[0];\n\t}\n\n\t#logger(vite: ViteModule) {\n\t\tconst logger = vite.createLogger('warn', { allowClearScreen: false });\n\t\treturn {\n\t\t\t...logger,\n\t\t\tinfo: (message: string) => this.emit('log', 'info', message),\n\t\t\twarn: (message: string) => this.emit('log', 'warn', message),\n\t\t\twarnOnce: (message: string) => this.emit('log', 'warn', message),\n\t\t\terror: (message: string) => this.emit('log', 'error', message)\n\t\t};\n\t}\n\n\t#begin(): void {\n\t\tthis.#startedAt = performance.now();\n\t\tthis.emit('start');\n\t}\n\n\t#finish(message: string | null): BuildOutcome {\n\t\tconst outcome: BuildOutcome = { ok: message === null, durationMs: Math.round(performance.now() - this.#startedAt), message };\n\t\tthis.#startedAt = 0;\n\t\tthis.emit(outcome.ok ? 'success' : 'failure', outcome);\n\t\treturn outcome;\n\t}\n}\n\nfunction toArray(value: unknown): unknown[] {\n\tif (value === undefined || value === null || value === false) return [];\n\treturn Array.isArray(value) ? value : [value];\n}\n"],"mappings":";;;;;AAcA,MAAM,eAAe;;;;;;;;AASrB,IAAa,cAAb,cAAiC,aAA+C;CAK3C;CAJpC,AAAgB,OAAO;CACvB,WAAiC;CACjC,aAAa;CAEb,AAAO,YAAY,AAAiB,QAA6B;EAChE,MAAM;EAD6B;CAEpC;CAEA,MAAa,QAA+B;EAC3C,MAAM,OAAO,MAAM,KAAK,MAAM;EAC9B,KAAK,OAAO;EAEZ,IAAI;GACH,MAAM,KAAK,MAAM,KAAK,SAAS,IAAI,CAAC;GACpC,OAAO,KAAK,QAAQ,IAAI;EACzB,SAAS,OAAO;GACf,OAAO,KAAK,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;EAC3E;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,OAAO,MAAM,KAAK,MAAM;EAC9B,KAAK,OAAO;EAIZ,MAAM,UAAU,KAAK,SAAS,IAAI;EAClC,MAAM,SAAU,MAAM,KAAK,MAAM;GAAE,GAAG;GAAS,OAAO;IAAE,GAAG,SAAS;IAAO,OAAO,CAAC;GAAE;EAAE,CAAC;EAExF,KAAK,WAAW;EAChB,OAAO,GACN,WACE,UAA2C;GAC5C,QAAQ,MAAM,MAAd;IACC,KAAK,gBACJ,OAAO,KAAK,OAAO;IACpB,KAAK;KACG,AAAK,KAAK,QAAQ,IAAI;KAA7B;IACD,KAAK;KACG,AAAK,KAAK,QAAQ,MAAM,OAAO,WAAW,2BAA2B;KAA5E;GAGF;EACD,EACD;CACD;CAEA,MAAa,QAAuB;EACnC,MAAM,UAAU,KAAK;EACrB,KAAK,WAAW;EAChB,MAAM,SAAS,MAAM;CACtB;CAEA,MAAM,QAA6B;EAClC,OAAO,kBAA8B,KAAK,OAAO,MAAM,QAAQ,YAAY;CAC5E;;;;;CAMA,SAAS,MAAsD;EAC9D,OAAO;GACN,MAAM,KAAK,OAAO;GAClB,UAAU;GACV,cAAc,KAAK,QAAQ,IAAI;GAC/B,GAAG,KAAK,OAAO;GACf,SAAS,CAAC,oBAAoB,KAAK,MAAM,GAAG,GAAG,QAAQ,KAAK,OAAO,KAAK,OAAO,CAAC;EACjF;CACD;CAEA,QAAQ,MAAkB;EAEzB,OAAO;GACN,GAFc,KAAK,aAAa,QAAQ,EAAE,kBAAkB,MAAM,CAE1D;GACR,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,OAAO,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC3D,WAAW,YAAoB,KAAK,KAAK,OAAO,QAAQ,OAAO;GAC/D,QAAQ,YAAoB,KAAK,KAAK,OAAO,SAAS,OAAO;EAC9D;CACD;CAEA,SAAe;EACd,KAAK,aAAa,YAAY,IAAI;EAClC,KAAK,KAAK,OAAO;CAClB;CAEA,QAAQ,SAAsC;EAC7C,MAAM,UAAwB;GAAE,IAAI,YAAY;GAAM,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,KAAK,UAAU;GAAG;EAAQ;EAC3H,KAAK,aAAa;EAClB,KAAK,KAAK,QAAQ,KAAK,YAAY,WAAW,OAAO;EACrD,OAAO;CACR;AACD;AAEA,SAAS,QAAQ,OAA2B;CAC3C,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,OAAO,OAAO,CAAC;CACtE,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC7C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfstar/cli",
3
- "version": "0.7.0",
3
+ "version": "1.0.0-next-20260919162943",
4
4
  "description": "The stars command line interface for @wolfstar/http-framework projects: typed configuration, dev server, build and project info",
5
5
  "keywords": [
6
6
  "api",
@@ -51,12 +51,13 @@
51
51
  "dependencies": {
52
52
  "@clack/prompts": "^1.8.0",
53
53
  "@sapphire/ts-config": "^5.0.3",
54
- "@wolfstar/http-framework": "^4.0.2",
54
+ "@wolfstar/http-framework": "^5.0.0-next-20260919162943",
55
55
  "chokidar": "^5.0.0",
56
56
  "citty": "^0.2.2",
57
57
  "colorette": "^2.0.20",
58
58
  "ink": "^7.1.1",
59
59
  "my-bad": "^0.2.2",
60
+ "nostics": "^1.2.0",
60
61
  "react": "^19.3.0",
61
62
  "untun": "^0.2.2"
62
63
  },
@@ -65,17 +66,22 @@
65
66
  "@vitest/coverage-v8": "^4.1.11",
66
67
  "@xterm/headless": "^6.0.0",
67
68
  "golar": "^0.1.10",
69
+ "nitro": "^3.0.260903-beta",
68
70
  "tsdown": "^0.23.0",
69
71
  "typescript": "~7.0.2",
70
72
  "vite": "^8.2.2",
71
73
  "vitest": "^4.1.11"
72
74
  },
73
75
  "peerDependencies": {
76
+ "nitro": ">=3.0.0-0",
74
77
  "tsdown": ">=0.15.0",
75
78
  "typescript": ">=5.0.0",
76
79
  "vite": ">=6.0.0"
77
80
  },
78
81
  "peerDependenciesMeta": {
82
+ "nitro": {
83
+ "optional": true
84
+ },
79
85
  "tsdown": {
80
86
  "optional": true
81
87
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"_shared-Cxp93Wni.js","names":[],"sources":["../src/utils/tsconfig.ts","../src/commands/_shared.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { createRequire } from 'node:module';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path';\n\nconst require = createRequire(import.meta.url);\nconst sapphireOptions = Object.assign(\n\t{},\n\t...['@sapphire/ts-config', '@sapphire/ts-config/extra-strict', '@sapphire/ts-config/decorators'].map(\n\t\t(id) => (require(id) as { compilerOptions: Record<string, unknown> }).compilerOptions\n\t)\n) as Record<string, unknown>;\n\n/** Generates an extendable config without changing the project's own tsconfig. */\nexport async function prepareTsconfig(config: ResolvedStarsConfig, check = false) {\n\tconst path = join(config.root, '.stars', 'tsconfig.json');\n\tconst fromGenerated = (target: string) => `./${relative(dirname(path), target).replaceAll('\\\\', '/')}`;\n\tconst paths: Record<string, string[]> = {};\n\tif (config.build.tool === 'tsdown') {\n\t\tconst source = dirname(config.entry);\n\t\tconst defaults = config.build.configFile === null ? { '~': source, '@': source, '~~': config.root, '@@': config.root } : {};\n\t\tconst aliases = { ...defaults, ...(config.tsdown.alias as Record<string, unknown> | undefined) };\n\t\tfor (const [alias, target] of Object.entries(aliases)) {\n\t\t\t// Module redirects belong to the bundler; only filesystem targets become TypeScript paths.\n\t\t\tif (typeof target !== 'string' || (!isAbsolute(target) && !/^\\.\\.?[/\\\\]/.test(target))) continue;\n\t\t\tconst resolved = fromGenerated(resolve(config.root, target));\n\t\t\tpaths[alias] = [resolved];\n\t\t\tpaths[`${alias}/*`] = [`${resolved}/*`];\n\t\t}\n\t}\n\tconst content = `${JSON.stringify(\n\t\t{\n\t\t\tcompilerOptions: {\n\t\t\t\t...sapphireOptions,\n\t\t\t\t// Bundlers emit the application; TypeScript only checks it. Keep Node16 emit for tsc.\n\t\t\t\t...(config.build.tool === 'tsdown' || config.build.tool === 'vite'\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tmodule: 'ESNext',\n\t\t\t\t\t\t\tmoduleResolution: 'Bundler',\n\t\t\t\t\t\t\tmoduleDetection: 'force',\n\t\t\t\t\t\t\tisolatedModules: true,\n\t\t\t\t\t\t\tverbatimModuleSyntax: true,\n\t\t\t\t\t\t\tallowJs: true,\n\t\t\t\t\t\t\tallowImportingTsExtensions: true,\n\t\t\t\t\t\t\tresolvePackageJsonImports: true,\n\t\t\t\t\t\t\tlib: ['ESNext', 'DOM'],\n\t\t\t\t\t\t\tnoEmit: true\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\n\t\t\t\ttarget: 'ES2022',\n\t\t\t\tforceConsistentCasingInFileNames: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\ttsBuildInfoFile: './tsconfig.tsbuildinfo',\n\t\t\t\tpaths\n\t\t\t},\n\t\t\tinclude: [`${fromGenerated(dirname(config.entry))}/**/*`, ...(config.imports.enabled ? [fromGenerated(config.imports.dts)] : [])],\n\t\t\texclude: [fromGenerated(join(config.root, 'node_modules')), fromGenerated(config.build.outDir)]\n\t\t},\n\t\tnull,\n\t\t2\n\t)}\\n`;\n\tif (check) {\n\t\tconst existing = await readFile(path, 'utf-8').catch(() => null);\n\t\treturn { path, status: existing === content ? ('up-to-date' as const) : ('outdated' as const) };\n\t}\n\tawait mkdir(dirname(path), { recursive: true });\n\tawait writeFile(path, content);\n\treturn { path, status: 'written' as const };\n}\n","import { generateAutoImportsDts } from '@wolfstar/http-framework/auto-imports';\nimport type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { prepareTsconfig } from '../utils/tsconfig.js';\n\nexport type PrepareResult =\n\t| { enabled: false; dts: null; status: null }\n\t| { enabled: true; dts: string; status: 'written' | 'up-to-date' | 'outdated' };\n\n/**\n * Regenerates `imports.dts` unconditionally, used by `stars dev`/`stars build` before the first build so the\n * declaration file exists (and is current) even when the user never ran `stars prepare` themselves.\n */\nexport async function prepareAutoImports(config: ResolvedStarsConfig, check = false): Promise<PrepareResult> {\n\tif (!config.imports.enabled) return { enabled: false, dts: null, status: null };\n\n\tconst { dirs, presets, exclude, dts } = config.imports;\n\tconst content = await generateAutoImportsDts({ root: config.root, dirs, presets, exclude });\n\n\tif (check) {\n\t\tconst existing = await readFile(dts, 'utf-8').catch(() => null);\n\t\treturn { enabled: true, dts, status: existing === content ? 'up-to-date' : 'outdated' };\n\t}\n\n\tawait mkdir(dirname(dts), { recursive: true });\n\tawait writeFile(dts, content);\n\treturn { enabled: true, dts, status: 'written' };\n}\n\n/** Prepares TypeScript configuration and auto import declarations before building. */\nexport async function prepareProject(config: ResolvedStarsConfig, check = false) {\n\tconst tsconfig = await prepareTsconfig(config, check);\n\treturn { ...(await prepareAutoImports(config, check)), tsconfig };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"build-B7QQzVk8.js","names":[],"sources":["../src/commands/build.ts"],"sourcesContent":["import { displayPath, loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { defineCommand } from 'citty';\nimport { createColors } from 'colorette';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { CliError, ExitCode } from '../utils/errors.js';\nimport { Locales } from '../utils/locales.js';\nimport { shouldUseColor } from '../utils/output-mode.js';\nimport { prepareProject } from './_shared.js';\n\nexport interface BuildTaskOptions extends ProjectArgs {\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport async function runBuild(options: BuildTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() });\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tassertSupportedExperiments(config);\n\tawait prepareProject(config);\n\n\tif (config.build.tool === 'none') {\n\t\tstdout.write(`${colors.dim('stars')} nothing to build, ${displayPath(config.root, config.entry)} runs as-is (build.tool is 'none')\\n`);\n\t\treturn;\n\t}\n\n\tconst builder = await createBuilder(config);\n\tbuilder.on('log', (level, text) => {\n\t\tconst paint = level === 'error' ? colors.red : level === 'warn' ? colors.yellow : (value: string) => value;\n\t\tstdout.write(`${paint(text)}\\n`);\n\t});\n\n\tstdout.write(`${colors.dim('stars')} building with ${colors.bold(config.build.tool)}…\\n`);\n\tconst outcome = await builder.build();\n\tif (!outcome.ok) {\n\t\tthrow new CliError(`Build failed${outcome.message ? `: ${outcome.message}` : ''}`, { code: 'BUILD_FAILED', exitCode: ExitCode.BuildFailed });\n\t}\n\tnew Locales(config).copy();\n\n\tstdout.write(`${colors.dim('stars')} ${colors.green(`built in ${outcome.durationMs}ms`)} → ${displayPath(config.root, config.build.output)}\\n`);\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'build',\n\t\tdescription: 'Build the project once with the configured build tool'\n\t},\n\targs: { ...projectArgs },\n\tasync run({ args }) {\n\t\tawait runBuild({ config: args.config, cwd: args.cwd });\n\t}\n});\n"],"mappings":";;;;;;;;;;AAcA,eAAsB,SAAS,SAA0C;CACxE,MAAM,SAAS,QAAQ,UAAU,QAAQ;CACzC,MAAM,SAAS,aAAa,EAAE,UAAU,eAAe,EAAE,CAAC;CAC1D,MAAM,SAAS,MAAM,gBAAgB;EAAE,KAAK,WAAW,OAAO;EAAG,YAAY,QAAQ;CAAO,CAAC;CAC7F,2BAA2B,MAAM;CACjC,MAAM,eAAe,MAAM;CAE3B,IAAI,OAAO,MAAM,SAAS,QAAQ;EACjC,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,qBAAqB,YAAY,OAAO,MAAM,OAAO,KAAK,EAAE,qCAAqC;EACrI;CACD;CAEA,MAAM,UAAU,MAAM,cAAc,MAAM;CAC1C,QAAQ,GAAG,QAAQ,OAAO,SAAS;EAClC,MAAM,QAAQ,UAAU,UAAU,OAAO,MAAM,UAAU,SAAS,OAAO,UAAU,UAAkB;EACrG,OAAO,MAAM,GAAG,MAAM,IAAI,EAAE,GAAG;CAChC,CAAC;CAED,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,iBAAiB,OAAO,KAAK,OAAO,MAAM,IAAI,EAAE,IAAI;CACxF,MAAM,UAAU,MAAM,QAAQ,MAAM;CACpC,IAAI,CAAC,QAAQ,IACZ,MAAM,IAAI,SAAS,eAAe,QAAQ,UAAU,KAAK,QAAQ,YAAY,MAAM;EAAE,MAAM;EAAgB,UAAU,SAAS;CAAY,CAAC;CAE5I,IAAI,QAAQ,MAAM,CAAC,CAAC,KAAK;CAEzB,OAAO,MAAM,GAAG,OAAO,IAAI,OAAO,EAAE,GAAG,OAAO,MAAM,YAAY,QAAQ,WAAW,GAAG,EAAE,KAAK,YAAY,OAAO,MAAM,OAAO,MAAM,MAAM,EAAE,GAAG;AAC/I;AAEA,oBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM,EAAE,GAAG,YAAY;CACvB,MAAM,IAAI,EAAE,QAAQ;EACnB,MAAM,SAAS;GAAE,QAAQ,KAAK;GAAQ,KAAK,KAAK;EAAI,CAAC;CACtD;AACD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"codegen-Ba2qI4ji.js","names":[],"sources":["../src/commands/codegen.ts"],"sourcesContent":["import { displayPath, loadStarsConfig, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { defineCommand } from 'citty';\nimport { createColors } from 'colorette';\nimport { spawn } from 'node:child_process';\nimport { mkdtemp, readFile, rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { CliError, ExitCode } from '../utils/errors.js';\nimport { shouldUseColor } from '../utils/output-mode.js';\nimport { resolveFromProject } from '../utils/project.js';\n\nexport interface CodegenTaskOptions extends ProjectArgs {\n\tcheck?: boolean;\n\tjson?: boolean;\n\tstdout?: NodeJS.WritableStream;\n}\n\nexport interface CodegenResult {\n\tgenerator: 'i18n';\n\toutput: string;\n\tstatus: 'written' | 'up-to-date' | 'outdated';\n}\n\nexport async function runCodegen(options: CodegenTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() && !options.json });\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\tconst results: CodegenResult[] = [];\n\n\tif (config.codegen.i18n) results.push(await runI18n(config, Boolean(options.check)));\n\n\tif (options.json) {\n\t\tstdout.write(`${JSON.stringify({ check: Boolean(options.check), results }, null, 2)}\\n`);\n\t} else if (results.length === 0) {\n\t\tstdout.write(`${colors.dim('stars')} nothing to generate (no code generators are configured)\\n`);\n\t} else {\n\t\tfor (const result of results) {\n\t\t\tconst paint = result.status === 'outdated' ? colors.red : colors.green;\n\t\t\tstdout.write(`${colors.dim('stars')} ${result.generator}: ${paint(result.status)} ${displayPath(config.root, result.output)}\\n`);\n\t\t}\n\t}\n\n\tif (results.some((result) => result.status === 'outdated')) {\n\t\tthrow new CliError('Generated files are out of date, run `stars codegen` to update them.', {\n\t\t\tcode: 'CODEGEN_OUTDATED',\n\t\t\texitCode: ExitCode.Error\n\t\t});\n\t}\n}\n\nasync function runI18n(config: ResolvedStarsConfig, check: boolean): Promise<CodegenResult> {\n\tconst { locales, output } = config.codegen.i18n!;\n\tconst cli = resolveFromProject(config.root, '@wolfstar/i18next-type-generator');\n\tif (!cli) {\n\t\tthrow new CliError(`\"@wolfstar/i18next-type-generator\" is not installed in ${config.root}`, {\n\t\t\tcode: 'DEPENDENCY_MISSING',\n\t\t\thint: 'Install it with `pnpm add -D @wolfstar/i18next-type-generator`, or set `codegen.i18n` to false.'\n\t\t});\n\t}\n\n\tif (!check) {\n\t\tawait generate(config.root, cli, locales, output);\n\t\treturn { generator: 'i18n', output, status: 'written' };\n\t}\n\n\tconst directory = await mkdtemp(join(tmpdir(), 'stars-codegen-'));\n\ttry {\n\t\tconst candidate = join(directory, 'i18next.d.ts');\n\t\tawait generate(config.root, cli, locales, candidate);\n\t\tconst [expected, actual] = await Promise.all([readFile(candidate, 'utf-8'), readFile(output, 'utf-8').catch(() => null)]);\n\t\treturn { generator: 'i18n', output, status: expected === actual ? 'up-to-date' : 'outdated' };\n\t} finally {\n\t\tawait rm(directory, { recursive: true, force: true });\n\t}\n}\n\nfunction generate(root: string, cli: string, locales: string, output: string): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst child = spawn(process.execPath, [cli, locales, output], { cwd: root, stdio: ['ignore', 'ignore', 'pipe'], windowsHide: true });\n\t\tlet stderr = '';\n\t\tchild.stderr?.on('data', (chunk: Buffer) => (stderr += chunk.toString()));\n\t\tchild.once('error', reject);\n\t\tchild.once('exit', (code) => {\n\t\t\tif (code === 0) resolve();\n\t\t\telse\n\t\t\t\treject(\n\t\t\t\t\tnew CliError(`i18next-type-generator exited with code ${code}${stderr ? `: ${stderr.trim()}` : ''}`, { code: 'CODEGEN_FAILED' })\n\t\t\t\t);\n\t\t});\n\t});\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'codegen',\n\t\tdescription: 'Run the configured code generators (i18next types)'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\tcheck: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Fail when the generated files are out of date instead of writing them',\n\t\t\tdefault: false\n\t\t},\n\t\tjson: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Print machine-readable JSON',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tawait runCodegen({ config: args.config, cwd: args.cwd, check: args.check, json: args.json });\n\t}\n});\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"commands-DOkMjyT7.js","names":[],"sources":["../src/utils/discord.ts","../src/utils/prompts.ts","../src/commands/commands.ts"],"sourcesContent":["import type { ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { CliError } from './errors.js';\nimport { readDiscordCredentials } from '../dev/tunnel.js';\n\nconst API = 'https://discord.com/api/v10';\n\nexport interface ApplicationCommand {\n\tid: string;\n\tapplication_id: string;\n\tname: string;\n\tdescription?: string;\n\ttype?: number;\n\tguild_id?: string;\n\tversion?: string;\n}\n\nexport interface DiscordClient {\n\tapplicationId: string;\n\tlistCommands(guildId: string | null): Promise<ApplicationCommand[]>;\n\tdeleteCommand(guildId: string | null, commandId: string): Promise<void>;\n}\n\nexport const COMMAND_TYPE_NAMES: Record<number, string> = { 1: 'chat input', 2: 'user', 3: 'message' };\n\n/**\n * A minimal Discord REST client for what `stars commands` manages. The credentials come from the environment or the\n * project's `.env`, the same place the bot reads them from once it starts.\n */\nexport function createDiscordClient(config: ResolvedStarsConfig, env: NodeJS.ProcessEnv = process.env): DiscordClient {\n\tconst credentials = readDiscordCredentials(config, env);\n\tif (!credentials) {\n\t\tthrow new CliError('DISCORD_TOKEN is not set', {\n\t\t\tcode: 'DISCORD_TOKEN_MISSING',\n\t\t\thint: 'Set DISCORD_TOKEN in the environment or in the project .env file.'\n\t\t});\n\t}\n\n\tif (!credentials.applicationId) {\n\t\tthrow new CliError('The Discord application id is not set', {\n\t\t\tcode: 'DISCORD_APPLICATION_ID_MISSING',\n\t\t\thint: 'Set DISCORD_APPLICATION_ID (or APPLICATION_ID) in the environment or in the project .env file.'\n\t\t});\n\t}\n\n\tconst applicationId = credentials.applicationId;\n\tconst scope = (guildId: string | null) =>\n\t\tguildId ? `/applications/${applicationId}/guilds/${guildId}/commands` : `/applications/${applicationId}/commands`;\n\n\tconst request = async (method: string, path: string): Promise<unknown> => {\n\t\tconst response = await fetch(`${API}${path}`, {\n\t\t\tmethod,\n\t\t\theaders: { authorization: `Bot ${credentials.token}` },\n\t\t\tsignal: AbortSignal.timeout(15_000)\n\t\t});\n\n\t\tif (response.status === 204) return null;\n\n\t\tconst body: unknown = await response.json().catch(() => null);\n\t\tif (!response.ok) {\n\t\t\tconst detail = typeof body === 'object' && body !== null && 'message' in body ? String((body as { message: unknown }).message) : '';\n\t\t\tthrow new CliError(`Discord answered ${response.status}${detail ? `: ${detail}` : ''}`, {\n\t\t\t\tcode: 'DISCORD_REQUEST_FAILED',\n\t\t\t\thint: response.status === 401 ? 'Check DISCORD_TOKEN.' : 'Check the application id and the bot permissions.'\n\t\t\t});\n\t\t}\n\n\t\treturn body;\n\t};\n\n\treturn {\n\t\tapplicationId,\n\t\tasync listCommands(guildId) {\n\t\t\tconst body = await request('GET', scope(guildId));\n\t\t\treturn Array.isArray(body) ? (body as ApplicationCommand[]) : [];\n\t\t},\n\t\tasync deleteCommand(guildId, commandId) {\n\t\t\tawait request('DELETE', `${scope(guildId)}/${commandId}`);\n\t\t}\n\t};\n}\n","import type { ApplicationCommand } from './discord.js';\nimport { CliError } from './errors.js';\n\nexport interface CommandsPrompt {\n\t/** Asks which of the deployed commands to delete. Returns the chosen ids. */\n\tpick(commands: readonly ApplicationCommand[], scope: string): Promise<string[]>;\n\t/** Final go/no-go before anything is deleted. */\n\tconfirm(message: string): Promise<boolean>;\n}\n\n/**\n * The interactive wizard behind `stars commands clean`: a checklist of what Discord has deployed, then a\n * confirmation, so a destructive cleanup is never one keystroke away. `@clack/prompts` is loaded lazily — the\n * non-interactive paths (`--yes`, `--json`, CI) never pay for it.\n */\nexport function createClackPrompt(): CommandsPrompt {\n\treturn {\n\t\tasync pick(commands, scope) {\n\t\t\tconst { isCancel, multiselect } = await import('@clack/prompts');\n\t\t\tconst answer = await multiselect({\n\t\t\t\tmessage: `Which commands should be deleted from ${scope}?`,\n\t\t\t\toptions: commands.map((command) => ({\n\t\t\t\t\tvalue: command.id,\n\t\t\t\t\tlabel: command.name,\n\t\t\t\t\thint: [describeType(command), command.id].filter(Boolean).join(' · ')\n\t\t\t\t})),\n\t\t\t\trequired: false\n\t\t\t});\n\n\t\t\tif (isCancel(answer)) throw aborted();\n\t\t\treturn answer as string[];\n\t\t},\n\t\tasync confirm(message) {\n\t\t\tconst { confirm, isCancel } = await import('@clack/prompts');\n\t\t\tconst answer = await confirm({ message, initialValue: false });\n\n\t\t\tif (isCancel(answer)) throw aborted();\n\t\t\treturn Boolean(answer);\n\t\t}\n\t};\n}\n\nfunction describeType(command: ApplicationCommand): string {\n\tswitch (command.type) {\n\t\tcase 2:\n\t\t\treturn 'user';\n\t\tcase 3:\n\t\t\treturn 'message';\n\t\tdefault:\n\t\t\treturn 'chat input';\n\t}\n}\n\nfunction aborted(): CliError {\n\treturn new CliError('Aborted', { code: 'ABORTED' });\n}\n","import { loadStarsConfig } from '@wolfstar/http-framework/config';\nimport { defineCommand } from 'citty';\nimport { createColors } from 'colorette';\nimport { COMMAND_TYPE_NAMES, createDiscordClient, type ApplicationCommand, type DiscordClient } from '../utils/discord.js';\nimport { CliError } from '../utils/errors.js';\nimport { shouldUseColor } from '../utils/output-mode.js';\nimport { createClackPrompt, type CommandsPrompt } from '../utils/prompts.js';\nimport { projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\n\nexport interface CommandsTaskOptions extends ProjectArgs {\n\t/** A guild id to work on the guild-scoped commands instead of the global ones. */\n\tguild?: string;\n\tjson?: boolean;\n\tstdout?: NodeJS.WritableStream;\n\t/** Overrides the client, for tests. */\n\tclient?: DiscordClient;\n}\n\nexport interface CommandsCleanOptions extends CommandsTaskOptions {\n\t/** Names to delete; without any, the wizard asks which of the deployed commands to remove. */\n\tnames?: string[];\n\t/** Deletes without asking. Required in a non-interactive terminal. */\n\tyes?: boolean;\n\tstdin?: NodeJS.ReadableStream & { isTTY?: boolean };\n\t/** Overrides the interactive wizard, for tests. */\n\tprompt?: CommandsPrompt;\n}\n\n/**\n * Lists the application commands Discord currently has deployed, which is not necessarily what the project would\n * register today: renamed and removed commands stay until something deletes them.\n */\nexport async function runCommandsList(options: CommandsTaskOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() && !options.json });\n\tconst { client, guild } = await connect(options);\n\tconst commands = await client.listCommands(guild);\n\n\tif (options.json) {\n\t\tstdout.write(`${JSON.stringify({ applicationId: client.applicationId, guildId: guild, commands }, null, 2)}\\n`);\n\t\treturn;\n\t}\n\n\tif (commands.length === 0) {\n\t\tstdout.write(`${colors.dim('stars')} no ${guild ? `commands in guild ${guild}` : 'global commands'} are deployed\\n`);\n\t\treturn;\n\t}\n\n\tstdout.write(`${colors.dim('stars')} ${commands.length} ${guild ? `command(s) in guild ${guild}` : 'global command(s)'}\\n`);\n\tfor (const command of commands) stdout.write(` ${colors.bold(describeName(command))} ${colors.dim(command.id)}\\n`);\n}\n\n/**\n * Deletes deployed application commands. Discord keeps whatever was registered last, so this is the way to clear\n * commands a project no longer defines (or a whole guild's test deployment).\n */\nexport async function runCommandsClean(options: CommandsCleanOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst colors = createColors({ useColor: shouldUseColor() && !options.json });\n\tconst { client, guild } = await connect(options);\n\tconst deployed = await client.listCommands(guild);\n\tconst wanted = options.names?.filter((name) => name.length > 0) ?? [];\n\n\tconst missing = wanted.filter((name) => !deployed.some((command) => command.name === name));\n\tif (missing.length > 0) {\n\t\tthrow new CliError(`No deployed command is named ${missing.join(', ')}`, {\n\t\t\tcode: 'COMMAND_NOT_FOUND',\n\t\t\thint: 'Run `stars commands list` to see what is deployed.'\n\t\t});\n\t}\n\n\tif (deployed.length === 0) {\n\t\tif (options.json) stdout.write(`${JSON.stringify({ deleted: [] }, null, 2)}\\n`);\n\t\telse stdout.write(`${colors.dim('stars')} nothing to delete\\n`);\n\t\treturn;\n\t}\n\n\tconst scope = guild ? `guild ${guild}` : 'the global scope';\n\tconst targets = await selectTargets(options, deployed, wanted, scope, stdout, colors);\n\tif (targets.length === 0) {\n\t\tif (options.json) stdout.write(`${JSON.stringify({ deleted: [] }, null, 2)}\\n`);\n\t\telse stdout.write(`${colors.dim('stars')} nothing to delete\\n`);\n\t\treturn;\n\t}\n\n\tfor (const command of targets) {\n\t\tawait client.deleteCommand(guild, command.id);\n\t\tif (!options.json) stdout.write(`${colors.dim('stars')} ${colors.red('deleted')} ${describeName(command)}\\n`);\n\t}\n\n\tif (options.json) stdout.write(`${JSON.stringify({ deleted: targets.map((command) => ({ id: command.id, name: command.name })) }, null, 2)}\\n`);\n}\n\n/**\n * Works out what to delete: `--name` (and `--yes`) keep the command scriptable, while a bare `stars commands clean`\n * on a terminal runs the wizard — a checklist of what Discord has deployed, then a confirmation.\n */\nasync function selectTargets(\n\toptions: CommandsCleanOptions,\n\tdeployed: ApplicationCommand[],\n\twanted: string[],\n\tscope: string,\n\tstdout: NodeJS.WritableStream,\n\tcolors: ReturnType<typeof createColors>\n): Promise<ApplicationCommand[]> {\n\tif (wanted.length > 0) {\n\t\tconst named = deployed.filter((command) => wanted.includes(command.name));\n\t\tif (options.yes) return named;\n\n\t\tawait confirmOrThrow(options, stdout, colors, `delete ${colors.bold(String(named.length))} command(s) from ${scope}?`);\n\t\treturn named;\n\t}\n\n\tif (options.yes) return deployed;\n\n\tconst stdin = options.stdin ?? process.stdin;\n\tif (!options.prompt && !stdin.isTTY) throw confirmationRequired();\n\n\tconst prompt = options.prompt ?? createClackPrompt();\n\tconst chosen = new Set(await prompt.pick(deployed, scope));\n\tconst targets = deployed.filter((command) => chosen.has(command.id));\n\tif (targets.length === 0) return [];\n\n\tconst names = targets.map((command) => command.name).join(', ');\n\tif (!(await prompt.confirm(`Delete ${targets.length} command(s) from ${scope}: ${names}?`))) {\n\t\tthrow new CliError('Aborted', { code: 'ABORTED' });\n\t}\n\n\treturn targets;\n}\n\nasync function connect(options: CommandsTaskOptions): Promise<{ client: DiscordClient; guild: string | null }> {\n\tconst config = await loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config });\n\treturn { client: options.client ?? createDiscordClient(config), guild: options.guild ?? null };\n}\n\nfunction describeName(command: ApplicationCommand): string {\n\tconst type = command.type && command.type !== 1 ? ` (${COMMAND_TYPE_NAMES[command.type] ?? `type ${command.type}`})` : '';\n\treturn `${command.name}${type}`;\n}\n\n/**\n * Deleting deployed commands is not reversible, so a terminal run asks first; scripts pass `--yes`.\n */\nasync function confirmOrThrow(\n\toptions: CommandsCleanOptions,\n\tstdout: NodeJS.WritableStream,\n\tcolors: ReturnType<typeof createColors>,\n\tquestion: string\n): Promise<void> {\n\tif (options.prompt) {\n\t\tif (await options.prompt.confirm(question)) return;\n\t\tthrow new CliError('Aborted', { code: 'ABORTED' });\n\t}\n\n\tconst stdin = options.stdin ?? process.stdin;\n\tif (!stdin.isTTY) throw confirmationRequired();\n\n\tstdout.write(`${colors.dim('stars')} ${question} [y/N] `);\n\treturn new Promise((resolve, reject) => {\n\t\tstdin.setEncoding?.('utf-8');\n\t\tstdin.once('data', (chunk: string) => {\n\t\t\tstdin.pause?.();\n\t\t\tconst answer = chunk.trim().toLowerCase();\n\t\t\tif (answer === 'y' || answer === 'yes') resolve();\n\t\t\telse reject(new CliError('Aborted', { code: 'ABORTED' }));\n\t\t});\n\t\tstdin.resume?.();\n\t});\n}\n\nfunction confirmationRequired(): CliError {\n\treturn new CliError('Refusing to delete commands without a confirmation', {\n\t\tcode: 'CONFIRMATION_REQUIRED',\n\t\thint: 'Pass --yes to delete them, or --name to pick one, from a script.'\n\t});\n}\n\nconst scopeArgs = {\n\t...projectArgs,\n\tguild: {\n\t\ttype: 'string',\n\t\tdescription: 'Work on a guild’s commands instead of the global ones'\n\t},\n\tjson: {\n\t\ttype: 'boolean',\n\t\tdescription: 'Print machine-readable JSON',\n\t\tdefault: false\n\t}\n} as const;\n\nconst list = defineCommand({\n\tmeta: { name: 'list', description: 'List the application commands Discord has deployed' },\n\targs: scopeArgs,\n\tasync run({ args }) {\n\t\tawait runCommandsList({ config: args.config, cwd: args.cwd, guild: args.guild, json: args.json });\n\t}\n});\n\nconst clean = defineCommand({\n\tmeta: { name: 'clean', description: 'Delete deployed application commands (all of them, or the named ones)' },\n\targs: {\n\t\t...scopeArgs,\n\t\tname: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Only delete the command with this name (repeatable)'\n\t\t},\n\t\tyes: {\n\t\t\ttype: 'boolean',\n\t\t\talias: 'y',\n\t\t\tdescription: 'Delete without asking for a confirmation',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tconst names = Array.isArray(args.name) ? args.name : args.name ? [args.name] : [];\n\t\tawait runCommandsClean({ config: args.config, cwd: args.cwd, guild: args.guild, json: args.json, names, yes: args.yes });\n\t}\n});\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'commands',\n\t\tdescription: 'Inspect and clean the application commands deployed to Discord'\n\t},\n\tsubCommands: { list, clean },\n\targs: scopeArgs,\n\tasync run({ args, rawArgs }) {\n\t\t// `stars commands` with no subcommand lists, the way `git branch` does.\n\t\tif (rawArgs.some((argument) => argument === 'list' || argument === 'clean')) return;\n\n\t\tawait runCommandsList({ config: args.config, cwd: args.cwd, guild: args.guild, json: args.json });\n\t}\n});\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev-Q-FSs-mH.js","names":[],"sources":["../src/dev/typechecker.ts","../src/dev/dev-service.ts","../src/dev/host.ts","../src/dev/log-file.ts","../src/commands/dev.ts"],"sourcesContent":["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 '../utils/errors.js';\nimport type { LogLevel } from '../utils/log-buffer.js';\nimport { createLineSplitter } from '../utils/process-supervisor.js';\nimport { resolveBinary } from '../utils/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 { displayPath, type ResolvedStarsConfig } from '@wolfstar/http-framework/config';\nimport { classifyAppLine, LogBuffer, type LogLevel, type LogSource } from '../utils/log-buffer.js';\nimport { Locales } from '../utils/locales.js';\nimport { ProcessSupervisor, type ProcessExit, type ProcessState } from '../utils/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 progress: { readonly fraction: number; readonly message: string; readonly startedAt: number; readonly readyMs: number | null };\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\tpublic readonly locales: Locales;\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\t#progress = { fraction: 0, message: 'preparing your app', startedAt: Date.now(), readyMs: null as number | null };\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\t\tthis.locales = new Locales(config);\n\n\t\tthis.builder.on('start', () => {\n\t\t\tthis.#progress = { fraction: 0, message: 'preparing build', startedAt: Date.now(), readyMs: null };\n\t\t\tthis.#setBuild('building');\n\t\t});\n\t\tthis.builder.on('progress', (fraction, message) => {\n\t\t\tthis.#progress = { ...this.#progress, fraction: Math.max(this.#progress.fraction, Math.min(0.75, fraction)), message };\n\t\t\tthis.#emitStatus();\n\t\t});\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', () => {\n\t\t\tif (this.supervisor.state === 'running') {\n\t\t\t\tthis.#settleProgress();\n\t\t\t\tvoid this.#checkHealth();\n\t\t\t}\n\t\t\tthis.#emitStatus();\n\t\t});\n\t\tthis.supervisor.on('stdout', (line) => this.log('app', classifyAppLine(line, 'info'), line));\n\t\tthis.supervisor.on('stderr', (line) => this.log('app', classifyAppLine(line, '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\t\tthis.locales.on('log', (level, text) => this.log('build', level, text));\n\t\tthis.locales.on('change', () => {\n\t\t\tif (this.#build === 'ok') this.#scheduleRestart('build');\n\t\t});\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\tprogress: this.#progress,\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\tconst entry = displayPath(this.config.root, this.config.entry);\n\t\tthis.log('stars', 'info', this.config.build.tool === 'none' ? `Watching ${entry}` : `Watching ${entry} with ${this.config.build.tool}`);\n\t\tif (this.config.dev.typecheck.enabled) this.typechecker.start();\n\t\tawait this.locales.watch();\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\tif (reason === 'manual' || reason === 'crash') {\n\t\t\t\tthis.#progress = { fraction: 0, message: 'restarting the bot', startedAt: Date.now(), readyMs: null };\n\t\t\t}\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.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/** Opens or closes the public tunnel (used by the `t` key). */\n\tpublic toggleTunnel(): Promise<void> {\n\t\treturn this.tunnel.toggle();\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([\n\t\t\t\tthis.builder.close(),\n\t\t\t\tthis.supervisor.stop(),\n\t\t\t\tthis.typechecker.close(),\n\t\t\t\tthis.tunnel.close(),\n\t\t\t\tthis.locales.close()\n\t\t\t]);\n\t\t});\n\t}\n\n\t/**\n\t * Kills the bot and the helper processes without waiting for them. Only for a forced shutdown, {@link stop} is the graceful path.\n\t */\n\tpublic kill(): void {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tthis.supervisor.kill();\n\t\tvoid this.typechecker.close();\n\t\tvoid this.tunnel.close();\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\ttry {\n\t\t\tthis.locales.copy();\n\t\t} catch (error) {\n\t\t\tthis.#onBuildFailure({\n\t\t\t\t...outcome,\n\t\t\t\tok: false,\n\t\t\t\tmessage: `Failed to copy locales: ${error instanceof Error ? error.message : String(error)}`\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.#lastBuild = outcome;\n\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\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\t\tconst pid = this.supervisor.pid;\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 (this.#stopped || pid !== this.supervisor.pid) return;\n\t\tif (next !== this.#health) {\n\t\t\tthis.#health = next;\n\t\t\tif (next === 'ok') this.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t}\n\t}\n\n\t#settleProgress(): void {\n\t\tif (this.config.dev.health && this.#health !== 'ok') return;\n\t\tif (this.#build !== 'ok') return;\n\t\tthis.#progress = {\n\t\t\t...this.#progress,\n\t\t\tfraction: 1,\n\t\t\tmessage: 'watching for changes',\n\t\t\treadyMs: this.#progress.readyMs ?? Date.now() - this.#progress.startedAt\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: {\n\t\t\t...process.env,\n\t\t\t...config.dev.env,\n\t\t\tSTARS_DEV: '1',\n\t\t\tNODE_ENV: 'development',\n\t\t\tFORCE_COLOR: process.env.NO_COLOR !== undefined ? undefined : (process.env.FORCE_COLOR ?? (process.stdout.isTTY ? '1' : undefined))\n\t\t},\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 '../utils/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 { defineCommand } from 'citty';\nimport { assertSupportedExperiments, createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev/dev-service.js';\nimport { withResolvedLocalhost } from '../dev/host.js';\nimport { LogFileWriter } from '../dev/log-file.js';\nimport { projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { CliError, ExitCode, renderCrashReport } from '../utils/errors.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../utils/output-mode.js';\nimport { THEME_SETTINGS, isThemeSetting, readSavedTheme, resolveThemeSetting, saveTheme } from '../utils/theme.js';\nimport { prepareProject } from './_shared.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n\t/** `--theme`. */\n\ttheme?: string;\n}\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tif (options.theme !== undefined && !isThemeSetting(options.theme)) {\n\t\tthrow new CliError(`Unknown theme \\`${options.theme}\\`.`, { code: 'INVALID_THEME', hint: `Use one of: ${THEME_SETTINGS.join(', ')}.` });\n\t}\n\n\t// Dev mode applies to config evaluation, build plugins, and the supervised application — not only the child.\n\tprocess.env.NODE_ENV = 'development';\n\tconst config = await resolveDevConfig(\n\t\tawait loadStarsConfig({ cwd: resolveCwd(options), configFile: options.config, env: { ...process.env, NODE_ENV: 'development' } })\n\t);\n\tassertSupportedExperiments(config);\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\tconst theme = resolveThemeSetting({ flag: options.theme, saved: readSavedTheme() });\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('../dev/tui/tui.js')).createTuiRenderer(service, {\n\t\t\t\t\tcolor,\n\t\t\t\t\ttheme,\n\t\t\t\t\treducedMotion: prefersReducedMotion(),\n\t\t\t\t\tonThemeSave: (setting) => {\n\t\t\t\t\t\tif (!saveTheme(setting)) service.log('stars', 'warn', 'Could not save the theme preference.');\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t: (await import('../dev/tui/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\t// Like Turborepo: the first signal shuts down gracefully, a second one gives up waiting and kills everything.\n\tlet signalled = false;\n\tconst onSignal = (code: ExitCode) => () => {\n\t\tif (signalled) {\n\t\t\tprocess.stderr.write('\\nForcing shutdown.\\n');\n\t\t\tservice.kill();\n\t\t\tprocess.exit(code);\n\t\t}\n\n\t\tsignalled = true;\n\t\tvoid shutdown(code);\n\t\tprocess.stderr.write('\\nShutting down gracefully, press Ctrl+C again to force quit.\\n');\n\t};\n\n\tprocess.on('SIGINT', onSignal(ExitCode.Interrupted));\n\tprocess.on('SIGTERM', onSignal(ExitCode.Terminated));\n\tprocess.on('SIGHUP', onSignal(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\ttry {\n\t\tawait prepareProject(config);\n\t\tawait service.start();\n\t} catch (error) {\n\t\tservice.log('stars', 'error', error instanceof Error ? await renderCrashReport(error, config.root) : String(error));\n\t\tawait shutdown(ExitCode.Error);\n\t}\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\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'dev',\n\t\tdescription: 'Build, run and restart the bot on changes'\n\t},\n\targs: {\n\t\t...projectArgs,\n\t\ttui: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Interactive terminal UI; use --no-tui (or STARS_TUI=plain) for plain line output',\n\t\t\tdefault: true\n\t\t},\n\t\ttheme: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Colour theme of the interactive UI: ${THEME_SETTINGS.join(', ')} (or STARS_THEME; press T in the UI to pick one)`\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tawait runDev({ config: args.config, cwd: args.cwd, tui: args.tui ? undefined : false, theme: args.theme });\n\t}\n});\n"],"mappings":"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"}
@@ -1,62 +0,0 @@
1
- import { ConfigError } from "@wolfstar/http-framework/config";
2
-
3
- //#region src/utils/errors.ts
4
- const ExitCode = {
5
- Ok: 0,
6
- Error: 1,
7
- InvalidConfig: 2,
8
- BuildFailed: 3,
9
- Interrupted: 130,
10
- Terminated: 143
11
- };
12
- /**
13
- * An error the CLI reports to the user without a stack trace.
14
- */
15
- var CliError = class extends Error {
16
- code;
17
- hint;
18
- exitCode;
19
- constructor(message, options) {
20
- super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
21
- this.name = "CliError";
22
- this.code = options.code;
23
- this.hint = options.hint ?? null;
24
- this.exitCode = options.exitCode ?? ExitCode.Error;
25
- }
26
- };
27
- /**
28
- * Formats an error for the terminal: message, optional location and hint.
29
- *
30
- * `ConfigError` comes from `@wolfstar/http-framework/config`: the framework validates `stars.config.*` and throws a
31
- * plain data error, the CLI decides how it looks on a terminal and which exit code it gets ({@link exitCodeOf}).
32
- */
33
- async function formatError(error) {
34
- if (error instanceof ConfigError) {
35
- const location = [error.file, error.path ? `option \`${error.path}\`` : null].filter(Boolean).join(" › ");
36
- return [`${error.message}${location ? ` (${location})` : ""}`, error.hint ? ` hint: ${error.hint}` : null].filter(Boolean).join("\n");
37
- }
38
- if (error instanceof CliError) return [error.message, error.hint ? ` hint: ${error.hint}` : null].filter(Boolean).join("\n");
39
- if (error instanceof Error && error.name === "CLIError") return error.message;
40
- if (error instanceof Error) return renderCrashReport(error);
41
- return String(error);
42
- }
43
- /**
44
- * Renders an unexpected error (a bug, not something the CLI already explains through {@link CliError} or
45
- * `ConfigError`) as a sourcemapped, syntax-highlighted report — frames and a snippet of the original source instead
46
- * of a stack trace pointing into the bundled `dist/cli.js`.
47
- */
48
- async function renderCrashReport(error, cwd = process.cwd()) {
49
- const { createReport, fsLoader, renderAnsi } = await import("my-bad");
50
- return renderAnsi(await createReport(error, {
51
- cwd,
52
- loaders: [fsLoader()]
53
- }), { cwd });
54
- }
55
- function exitCodeOf(error) {
56
- if (error instanceof ConfigError) return ExitCode.InvalidConfig;
57
- return error instanceof CliError ? error.exitCode : ExitCode.Error;
58
- }
59
-
60
- //#endregion
61
- export { renderCrashReport as a, formatError as i, ExitCode as n, exitCodeOf as r, CliError as t };
62
- //# sourceMappingURL=errors-DETRFkoF.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors-DETRFkoF.js","names":[],"sources":["../src/utils/errors.ts"],"sourcesContent":["import { ConfigError } from '@wolfstar/http-framework/config';\n\nexport const ExitCode = {\n\tOk: 0,\n\tError: 1,\n\tInvalidConfig: 2,\n\tBuildFailed: 3,\n\tInterrupted: 130,\n\tTerminated: 143\n} as const;\n\nexport type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];\n\nexport interface CliErrorOptions {\n\t/** A stable, machine readable code. */\n\tcode: string;\n\t/** A short, actionable suggestion. */\n\thint?: string;\n\texitCode?: ExitCode;\n\tcause?: unknown;\n}\n\n/**\n * An error the CLI reports to the user without a stack trace.\n */\nexport class CliError extends Error {\n\tpublic readonly code: string;\n\tpublic readonly hint: string | null;\n\tpublic readonly exitCode: ExitCode;\n\n\tpublic constructor(message: string, options: CliErrorOptions) {\n\t\tsuper(message, options.cause === undefined ? undefined : { cause: options.cause });\n\t\tthis.name = 'CliError';\n\t\tthis.code = options.code;\n\t\tthis.hint = options.hint ?? null;\n\t\tthis.exitCode = options.exitCode ?? ExitCode.Error;\n\t}\n}\n\n/**\n * Formats an error for the terminal: message, optional location and hint.\n *\n * `ConfigError` comes from `@wolfstar/http-framework/config`: the framework validates `stars.config.*` and throws a\n * plain data error, the CLI decides how it looks on a terminal and which exit code it gets ({@link exitCodeOf}).\n */\nexport async function formatError(error: unknown): Promise<string> {\n\tif (error instanceof ConfigError) {\n\t\tconst location = [error.file, error.path ? `option \\`${error.path}\\`` : null].filter(Boolean).join(' › ');\n\t\treturn [`${error.message}${location ? ` (${location})` : ''}`, error.hint ? ` hint: ${error.hint}` : null].filter(Boolean).join('\\n');\n\t}\n\n\tif (error instanceof CliError) {\n\t\treturn [error.message, error.hint ? ` hint: ${error.hint}` : null].filter(Boolean).join('\\n');\n\t}\n\n\t// citty's CLIError (unknown command, missing argument): the message is enough.\n\tif (error instanceof Error && error.name === 'CLIError') return error.message;\n\tif (error instanceof Error) return renderCrashReport(error);\n\treturn String(error);\n}\n\n/**\n * Renders an unexpected error (a bug, not something the CLI already explains through {@link CliError} or\n * `ConfigError`) as a sourcemapped, syntax-highlighted report — frames and a snippet of the original source instead\n * of a stack trace pointing into the bundled `dist/cli.js`.\n */\nexport async function renderCrashReport(error: unknown, cwd: string = process.cwd()): Promise<string> {\n\tconst { createReport, fsLoader, renderAnsi } = await import('my-bad');\n\tconst report = await createReport(error, { cwd, loaders: [fsLoader()] });\n\treturn renderAnsi(report, { cwd });\n}\n\nexport function exitCodeOf(error: unknown): ExitCode {\n\tif (error instanceof ConfigError) return ExitCode.InvalidConfig;\n\treturn error instanceof CliError ? error.exitCode : ExitCode.Error;\n}\n"],"mappings":";;;AAEA,MAAa,WAAW;CACvB,IAAI;CACJ,OAAO;CACP,eAAe;CACf,aAAa;CACb,aAAa;CACb,YAAY;AACb;;;;AAgBA,IAAa,WAAb,cAA8B,MAAM;CACnC,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAEhB,AAAO,YAAY,SAAiB,SAA0B;EAC7D,MAAM,SAAS,QAAQ,UAAU,SAAY,SAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EACjF,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,OAAO,QAAQ,QAAQ;EAC5B,KAAK,WAAW,QAAQ,YAAY,SAAS;CAC9C;AACD;;;;;;;AAQA,eAAsB,YAAY,OAAiC;CAClE,IAAI,iBAAiB,aAAa;EACjC,MAAM,WAAW,CAAC,MAAM,MAAM,MAAM,OAAO,YAAY,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;EACxG,OAAO,CAAC,GAAG,MAAM,UAAU,WAAW,KAAK,SAAS,KAAK,MAAM,MAAM,OAAO,WAAW,MAAM,SAAS,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;CACtI;CAEA,IAAI,iBAAiB,UACpB,OAAO,CAAC,MAAM,SAAS,MAAM,OAAO,WAAW,MAAM,SAAS,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;CAI9F,IAAI,iBAAiB,SAAS,MAAM,SAAS,YAAY,OAAO,MAAM;CACtE,IAAI,iBAAiB,OAAO,OAAO,kBAAkB,KAAK;CAC1D,OAAO,OAAO,KAAK;AACpB;;;;;;AAOA,eAAsB,kBAAkB,OAAgB,MAAc,QAAQ,IAAI,GAAoB;CACrG,MAAM,EAAE,cAAc,UAAU,eAAe,MAAM,OAAO;CAE5D,OAAO,WAAW,MADG,aAAa,OAAO;EAAE;EAAK,SAAS,CAAC,SAAS,CAAC;CAAE,CAAC,GAC7C,EAAE,IAAI,CAAC;AAClC;AAEA,SAAgB,WAAW,OAA0B;CACpD,IAAI,iBAAiB,aAAa,OAAO,SAAS;CAClD,OAAO,iBAAiB,WAAW,MAAM,WAAW,SAAS;AAC9D"}