@wolfstar/cli 2.7.0-next-20261009115919 → 2.7.0

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 (37) hide show
  1. package/dist/{_shared-Bqw7h4O0.js → _shared-BLXTNq89.js} +2 -2
  2. package/dist/{_shared-Bqw7h4O0.js.map → _shared-BLXTNq89.js.map} +1 -1
  3. package/dist/{build-BYlgPxAE.js → build-DVF5vcSS.js} +3 -3
  4. package/dist/{build-BYlgPxAE.js.map → build-DVF5vcSS.js.map} +1 -1
  5. package/dist/cli.js +1 -1
  6. package/dist/{codegen-CiDXuBHu.js → codegen-DI263aSx.js} +3 -3
  7. package/dist/{codegen-CiDXuBHu.js.map → codegen-DI263aSx.js.map} +1 -1
  8. package/dist/commands-Bd6Dp5Y0.js +3 -0
  9. package/dist/{commands-CURkZI0f.js → commands-CtucWY2d.js} +4 -4
  10. package/dist/{commands-CURkZI0f.js.map → commands-CtucWY2d.js.map} +1 -1
  11. package/dist/commands-D7SFy2_3.js +16 -0
  12. package/dist/{commands-Chpk8BR8.js.map → commands-D7SFy2_3.js.map} +1 -1
  13. package/dist/{completions-DBipETDu.js → completions-D_scEKH1.js} +2 -2
  14. package/dist/{completions-DBipETDu.js.map → completions-D_scEKH1.js.map} +1 -1
  15. package/dist/{dev-GvuLQzWu.js → dev-CnXgPQ8F.js} +4 -4
  16. package/dist/{dev-GvuLQzWu.js.map → dev-CnXgPQ8F.js.map} +1 -1
  17. package/dist/{doctor-C_lTMEjG.js → doctor-BYRkkc5l.js} +4 -4
  18. package/dist/{doctor-C_lTMEjG.js.map → doctor-BYRkkc5l.js.map} +1 -1
  19. package/dist/{hooks-DqlScRZA.js → hooks-CzV2YRoJ.js} +4 -3
  20. package/dist/hooks-CzV2YRoJ.js.map +1 -0
  21. package/dist/index.js +1 -1
  22. package/dist/{info-BX-As3qN.js → info-DLo6tZT5.js} +3 -3
  23. package/dist/{info-BX-As3qN.js.map → info-DLo6tZT5.js.map} +1 -1
  24. package/dist/{local-commands-DkWl97zR.js → local-commands-DKnabUnX.js} +2 -2
  25. package/dist/{local-commands-DkWl97zR.js.map → local-commands-DKnabUnX.js.map} +1 -1
  26. package/dist/{prepare-6abxyJeD.js → prepare-DnNsv3TU.js} +3 -3
  27. package/dist/{prepare-6abxyJeD.js.map → prepare-DnNsv3TU.js.map} +1 -1
  28. package/dist/{run-DlctD5rb.js → run-BVBqR6hV.js} +2 -2
  29. package/dist/{run-DlctD5rb.js.map → run-BVBqR6hV.js.map} +1 -1
  30. package/dist/{tunnel-D3WxypOt.js → tunnel-DlFjqUE7.js} +2 -2
  31. package/dist/{tunnel-D3WxypOt.js.map → tunnel-DlFjqUE7.js.map} +1 -1
  32. package/dist/{typecheck-BIscCUNQ.js → typecheck-B2aRagKp.js} +3 -3
  33. package/dist/{typecheck-BIscCUNQ.js.map → typecheck-B2aRagKp.js.map} +1 -1
  34. package/package.json +5 -5
  35. package/dist/commands-Chpk8BR8.js +0 -16
  36. package/dist/commands-R2m3xweq.js +0 -3
  37. package/dist/hooks-DqlScRZA.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"dev-GvuLQzWu.js","names":[],"sources":["../src/dev/changed-files.ts","../src/dev/dev-service.ts","../src/dev/host.ts","../src/dev/log-file.ts","../src/commands/dev.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { basename, extname, join, sep } from 'node:path';\n\nconst SKIPPED_DIRECTORIES = new Set(['node_modules', '.git']);\n/**\n * `tsc -b` rewrites its build info on every build, whatever changed; a source map only ever changes with the file it\n * maps, and is most of what there would be to read.\n */\nconst SKIPPED_FILES = /\\.(?:tsbuildinfo|map)$/;\n\n/** The content hash of every file under the roots, by absolute path. */\nexport type FileSnapshot = ReadonlyMap<string, string>;\n\n/** What was last read of each file, so one that was not touched since is not read again. */\nexport type HashCache = Map<string, { size: number; mtimeMs: number; hash: string }>;\n\n/**\n * Hashes every file under `roots`, skipping `node_modules`, `.git` and everything under `exclude`.\n *\n * The builders only report that a build finished, and a bundler rewrites files it did not change (`tsdown` writes\n * its whole output on every rebuild), so neither an event nor a modification time says what a build changed: two\n * snapshots do (see {@link changedFiles}).\n */\nexport function snapshotFiles(roots: readonly string[], exclude: readonly string[] = [], cache: HashCache = new Map()): FileSnapshot {\n\tconst snapshot = new Map<string, string>();\n\tconst visit = (directory: string) => {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(directory, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst path = join(directory, entry.name);\n\t\t\tif (exclude.some((excluded) => isInside(path, excluded))) continue;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (!SKIPPED_DIRECTORIES.has(entry.name)) visit(path);\n\t\t\t} else if (entry.isFile() && !SKIPPED_FILES.test(entry.name)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { size, mtimeMs } = statSync(path);\n\t\t\t\t\tconst known = cache.get(path);\n\t\t\t\t\tconst hash =\n\t\t\t\t\t\tknown?.size === size && known.mtimeMs === mtimeMs\n\t\t\t\t\t\t\t? known.hash\n\t\t\t\t\t\t\t: createHash('sha1').update(readFileSync(path)).digest('base64');\n\t\t\t\t\tcache.set(path, { size, mtimeMs, hash });\n\t\t\t\t\tsnapshot.set(path, hash);\n\t\t\t\t} catch {\n\t\t\t\t\t// Removed between the listing and the read: the next build reports it.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tfor (const root of new Set(roots)) visit(root);\n\treturn snapshot;\n}\n\n/** The files that were added, removed, or whose content differs between two snapshots. */\nexport function changedFiles(previous: FileSnapshot, next: FileSnapshot): string[] {\n\tconst changed = [...next].filter(([path, hash]) => previous.get(path) !== hash).map(([path]) => path);\n\tfor (const path of previous.keys()) {\n\t\tif (!next.has(path)) changed.push(path);\n\t}\n\n\treturn changed;\n}\n\nconst PIECE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts']);\n\n/**\n * Whether a file a store has not loaded yet could be a piece, by the rule the stores filter files with: a script\n * that is not a declaration file and whose name does not start with `_`.\n */\nexport function couldBePiece(path: string): boolean {\n\tconst extension = extname(path);\n\treturn PIECE_EXTENSIONS.has(extension) && !path.endsWith('.d.ts') && !basename(path, extension).startsWith('_');\n}\n\n/** Whether `path` is `directory` itself or lies under it. */\nexport function isInside(path: string, directory: string): boolean {\n\treturn path === directory || path.startsWith(directory.endsWith(sep) ? directory : `${directory}${sep}`);\n}\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from '../builders/types.js';\nimport { displayPath, type ResolvedStarsConfig, type StarsHooks, type StarsRestartReason } from '@wolfstar/schema';\nimport { describeCommand, diffSnapshots, type CommandChange } from '../utils/command-diff.js';\nimport { envImportArgs, moduleImportArgs } from '../utils/env-import.js';\nimport type { StarsHookable } from '../utils/hooks.js';\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 { bridgeImportArgs, CLI_SOURCE, parseBridgeMessage, type BridgeMessage, type CommandSnapshot, type RefreshRequest } from './bridge.js';\nimport { changedFiles, couldBePiece, isInside, snapshotFiles, type FileSnapshot, type HashCache } from './changed-files.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 = StarsRestartReason;\n\n/** A question the dev UI asks the user; {@link DevService.answerPrompt} settles it. */\nexport interface DevPrompt {\n\treadonly kind: 'commands';\n\treadonly changes: readonly CommandChange[];\n}\n\nexport interface LogExtra {\n\t/** The channel to log on instead of the source's own. */\n\tchannel?: string;\n\tdetail?: readonly string[];\n}\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\t/** Whether the running bot reported that it is listening (see the bridge); always `false` without the bridge. */\n\treadonly ready: boolean;\n\t/** The port the bot reported it listens on. */\n\treadonly port: number | null;\n\t/** Whether the running bot reported that it hot reloads its pieces. */\n\treadonly hmr: boolean;\n\t/** Whether the bot was stopped on purpose (the `d` key) and waits for a restart. */\n\treadonly paused: boolean;\n\treadonly prompt: DevPrompt | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\t/** The `stars.config` hooks; `build:*` and `dev:restart` run from here. */\n\thooks?: StarsHookable;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n\t/**\n\t * Whether the tunnel opens at start: `true` opens it whatever `dev.tunnel` says (a quick tunnel when it is off),\n\t * `false` keeps it closed, and `undefined` leaves it to `dev.tunnel`. The `--tunnel`/`--no-tunnel` flags.\n\t */\n\topenTunnel?: boolean;\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\t/** Whether something can answer a {@link DevPrompt}: the interactive UI sets it, the plain renderer cannot. */\n\tpublic promptable = false;\n\treadonly #hooks: StarsHookable | null;\n\treadonly #openTunnel: boolean | undefined;\n\t/** Runs hooks one at a time, so an async `build:done` settles before the next build's hooks or a restart. */\n\t#hookChain: Promise<void> = Promise.resolve();\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/** The restart a build asked for that has not happened yet: it outlives the timer, which the next build start clears. */\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\t#ready = false;\n\t#port: number | null = null;\n\t/** The store paths the running bot watches for hot reloads, empty when it does not. */\n\t#hmrPaths: string[] = [];\n\t#paused = false;\n\t#prompt: DevPrompt | null = null;\n\t/** The commands the bot last reported, kept across restarts: a restart is how most changes arrive. */\n\t#commands: CommandSnapshot | null = null;\n\t/** What the build output held after the previous build, `null` before the first one. */\n\t#files: FileSnapshot | null = null;\n\treadonly #hashes: HashCache = new Map();\n\t/** The files the running bot loaded pieces from: what its hot reload can replace. */\n\t#pieces = new Set<string>();\n\t/** The commands Discord is assumed to have: the first report of the session, then whatever was last deployed. */\n\t#deployed: CommandSnapshot | null = null;\n\t/** A refresh of the commands asked for while the bot was not listening; sent once it is. */\n\t#refreshPending = false;\n\t/** Restarts waiting for the running build to end, see {@link DevService.#buildSettled}. */\n\t#buildWaiters: Array<() => void> = [];\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\t\tthis.#hooks = options.hooks ?? null;\n\t\tthis.#openTunnel = options.openTunnel;\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\t// A restart armed by the previous build must not fire while this one rewrites the output; it stays owed.\n\t\t\tthis.#clearRestartTimer();\n\t\t\tthis.#setBuild('building');\n\t\t\tvoid this.#startHook('build:before', this.config);\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('message', (message) => {\n\t\t\tconst parsed = parseBridgeMessage(message);\n\t\t\tif (parsed === null) return;\n\t\t\ttry {\n\t\t\t\tthis.#onBridge(parsed);\n\t\t\t} catch (error) {\n\t\t\t\t// Whatever the bot sends, it must not take the session down.\n\t\t\t\tthis.log('stars', 'warn', `Ignored a ${parsed.type} message from the bot: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t}\n\t\t});\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\tready: this.#ready,\n\t\t\tport: this.#port,\n\t\t\thmr: this.#hmrPaths.length > 0,\n\t\t\tpaused: this.#paused,\n\t\t\tprompt: this.#prompt\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string, extra: LogExtra = {}): void {\n\t\tthis.logs.push({ source, level, text, ...extra });\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\t// `--no-tunnel` only keeps it closed at start: the `t` key still opens one.\n\t\tif (this.#openTunnel !== false) void this.tunnel.start(this.#openTunnel === true);\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\tawait this.#queueHook('dev:restart', reason, this.config);\n\t\t\tif (this.#stopped) return;\n\t\t\tif (!(await this.#buildSettled(reason))) 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\t// Stopping the bot takes a while, and a build may have begun meanwhile.\n\t\t\tif (this.#stopped || !(await this.#buildSettled(reason))) return;\n\t\t\t// The bot that starts now runs the latest output, so no restart is owed any more.\n\t\t\tthis.#pendingReason = null;\n\t\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#paused = false;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the bot and leaves it stopped (the `d` key): builds keep running, but nothing starts the bot again until\n\t * {@link restart}. For when the bot must be off without ending the session.\n\t */\n\tpublic disconnect(): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped || this.#paused) return;\n\t\t\tthis.#paused = true;\n\t\t\tthis.log('stars', 'info', 'Stopped the bot, press r to start it again');\n\t\t\tawait this.supervisor.stop();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/** Settles the pending {@link DevPrompt}: `true` does what it asks, `false` dismisses it. */\n\tpublic answerPrompt(accept: boolean): void {\n\t\tconst prompt = this.#prompt;\n\t\tif (prompt === null) return;\n\t\t// The question stays open: the answer is not given up, only the redeploy is not safe yet.\n\t\tif (accept && this.#refreshBlocked()) return;\n\t\tthis.#prompt = null;\n\t\tif (accept) this.refreshCommands();\n\t\telse this.log('stars', 'info', 'Kept the deployed commands as they are', { channel: 'commands' });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Whether a refresh has to wait, saying why. While a build rewrites the output a bot that starts loads a part of the\n\t * commands, and registering that would unregister the rest from Discord.\n\t */\n\t#refreshBlocked(): boolean {\n\t\tif (this.#build !== 'building') return false;\n\t\tthis.log('stars', 'warn', 'A build is running, the commands of the bot may be incomplete: answer again once it has finished', {\n\t\t\tchannel: 'commands'\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Resolves once no build is running, `false` when the restart is not worth doing any more: the session stopped, or\n\t * an automatic restart (`build`, `initial`) had to wait for a build, whose own end already decides what happens next\n\t * (a restart if it succeeded, nothing if it failed: the output would be the previous, possibly half written one).\n\t * The dropped restart stays owed in {@link DevService.#pendingReason}, so that build's end restarts the bot even when\n\t * it could be hot reloaded: the change the dropped restart was for is not part of what that build rewrote.\n\t * A manual restart or one after a crash is still what was asked for.\n\t */\n\tasync #buildSettled(reason: RestartReason): Promise<boolean> {\n\t\tconst automatic = reason === 'build' || reason === 'initial';\n\t\tlet waited = false;\n\t\twhile (this.#build === 'building' && !this.#stopped) {\n\t\t\twaited = true;\n\t\t\tawait new Promise<void>((resolve) => this.#buildWaiters.push(resolve));\n\t\t}\n\n\t\tif (this.#stopped) return false;\n\t\tif (waited && automatic) {\n\t\t\tthis.#pendingReason ??= reason;\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t#releaseBuildWaiters(): void {\n\t\tconst waiters = this.#buildWaiters;\n\t\tthis.#buildWaiters = [];\n\t\tfor (const resolve of waiters) resolve();\n\t}\n\n\t/** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */\n\tpublic refreshCommands(): void {\n\t\tif (this.#refreshBlocked()) return;\n\t\t// A bot that is stopped or restarting has no client to register anything with: the refresh waits for it to listen.\n\t\tif (!this.#ready) {\n\t\t\tthis.#refreshPending = true;\n\t\t\tconst when = this.supervisor.running ? 'once the bot is ready' : 'when the bot starts again';\n\t\t\tthis.log('stars', 'info', `Refreshing commands ${when}…`, { channel: 'commands' });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#refreshPending = false;\n\t\tconst current = this.#commands?.guilds ?? {};\n\t\tconst request: RefreshRequest = {\n\t\t\tsource: CLI_SOURCE,\n\t\t\ttype: 'commands:refresh',\n\t\t\tclearGuilds: Object.keys(this.#deployed?.guilds ?? {}).filter((guild) => !Object.hasOwn(current, guild))\n\t\t};\n\t\tif (this.supervisor.send(request)) {\n\t\t\tthis.log('stars', 'info', 'Refreshing commands…', { channel: 'commands' });\n\t\t} else {\n\t\t\tthis.log('stars', 'warn', 'Could not reach the bot to refresh its commands', { channel: 'commands' });\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\t// A restart waiting for a build must not hold the queue that closes the session.\n\t\tthis.#releaseBuildWaiters();\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.#releaseBuildWaiters();\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\t// Before the locales are copied: the copy rewrites every file, which is not what the build changed.\n\t\tconst hot = this.#hotReloadable();\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\t// Only once locales are copied: until then the build is not done, and may still fail.\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\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\tif (this.#paused) return;\n\t\t// A restart an earlier build asked for (and a build start cleared) is still owed: its change is not in `hot`.\n\t\tif (hot !== null && this.#pendingReason === null) {\n\t\t\tfor (const file of hot) this.log('stars', 'trace', `UPDATE ${displayPath(this.config.root, file)}`, { channel: 'hmr' });\n\t\t\tthis.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t/**\n\t * The files the build rewrote when all of them are pieces the running bot hot reloads, `null` when the bot has to\n\t * be restarted instead: it is not running, it does not hot reload, `dev.hmr` is off, nothing changed (a rebuild for\n\t * a reason that cannot be seen from the output), or a file changed that the bot cannot replace. What a build changed\n\t * is the difference in content with the previous output, since a bundler rewrites files it did not change.\n\t *\n\t * The bot replaces the files it loaded pieces from, and loads a new file that could be one. A helper module next to\n\t * the pieces (`_shared.js`, or any file no piece came from) is imported once and stays as it was: changing it\n\t * needs a restart, even though it lies in a store path.\n\t */\n\t#hotReloadable(): string[] | null {\n\t\tif (this.config.dev.hmr === false) return null;\n\n\t\tconst roots = this.config.build.tool === 'none' ? this.config.dev.watch : [this.config.build.outDir];\n\t\tconst previous = this.#files;\n\t\tthis.#files = snapshotFiles(roots, [this.locales.destination], this.#hashes);\n\t\tif (previous === null || this.#hmrPaths.length === 0 || this.supervisor.state !== 'running') return null;\n\n\t\tconst changed = changedFiles(previous, this.#files);\n\t\tconst replaceable = (file: string) =>\n\t\t\tthis.#hmrPaths.some((path) => isInside(file, path)) && (this.#pieces.has(file) || (!previous.has(file) && couldBePiece(file)));\n\t\treturn changed.length > 0 && changed.every(replaceable) ? changed : null;\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\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\tthis.#ready = false;\n\t\tthis.#port = null;\n\t\tthis.#hmrPaths = [];\n\t\tthis.#pieces.clear();\n\t\t// A pending prompt stays: the commands still differ from what Discord has, and the next process can deploy them.\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\tif (this.#paused) return;\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\tif (state !== 'building') this.#releaseBuildWaiters();\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/** Turns what the bot reports over the bridge into log entries and status. */\n\t#onBridge(message: BridgeMessage): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tswitch (message.type) {\n\t\t\tcase 'ready':\n\t\t\t\tthis.#ready = true;\n\t\t\t\tthis.#port = message.port;\n\t\t\t\tthis.log('stars', 'success', message.port === null ? 'The bot is ready' : `Listening on port ${message.port}`, {\n\t\t\t\t\tchannel: 'lifecycle'\n\t\t\t\t});\n\t\t\t\tif (this.#refreshPending) this.refreshCommands();\n\t\t\t\tthis.#emitStatus();\n\t\t\t\treturn;\n\t\t\tcase 'pieces':\n\t\t\t\tfor (const piece of message.pieces) this.#pieces.add(piece.path);\n\t\t\t\tif (message.pieces.length === 0) return;\n\t\t\t\tthis.log('stars', 'debug', `Loaded ${message.store}: ${message.pieces.length} ${message.pieces.length === 1 ? 'piece' : 'pieces'}`, {\n\t\t\t\t\tchannel: message.store === 'commands' ? 'commands' : 'lifecycle',\n\t\t\t\t\tdetail: message.pieces.map((piece) => `${piece.name} ${relative(piece.path)}`)\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'hmr':\n\t\t\t\tthis.#onHmr(message);\n\t\t\t\treturn;\n\t\t\tcase 'commands':\n\t\t\t\tthis.#onCommands({ global: message.global, guilds: message.guilds });\n\t\t\t\treturn;\n\t\t\tcase 'dispatch':\n\t\t\t\tif (message.phase === 'run') {\n\t\t\t\t\tthis.log('stars', 'debug', `Processing ${message.route} with ${message.piece}`, { channel: 'interactions' });\n\t\t\t\t} else if (message.phase === 'success') {\n\t\t\t\t\tthis.log('stars', 'trace', `${message.route} handled${formatMs(message.ms)}`, { channel: 'interactions' });\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `${message.route} failed${formatMs(message.ms)}: ${message.error.message}`, {\n\t\t\t\t\t\tchannel: 'interactions',\n\t\t\t\t\t\tdetail: message.error.stack\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'request':\n\t\t\t\tthis.log('stars', message.status >= 500 ? 'error' : message.status >= 400 ? 'warn' : 'trace', formatRequest(message), {\n\t\t\t\t\tchannel: 'http'\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'refreshed':\n\t\t\t\tif (message.ok) {\n\t\t\t\t\tthis.#deployed = this.#commands;\n\t\t\t\t\tconst guilds =\n\t\t\t\t\t\t(message.guilds > 0 ? `, ${message.guilds} guild ${message.guilds === 1 ? 'group' : 'groups'}` : '') +\n\t\t\t\t\t\t(message.cleared > 0 ? `, cleared ${message.cleared} ${message.cleared === 1 ? 'guild' : 'guilds'}` : '');\n\t\t\t\t\tthis.log('stars', 'success', `Deployed ${message.global} global ${message.global === 1 ? 'command' : 'commands'}${guilds}`, {\n\t\t\t\t\t\tchannel: 'commands'\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `Could not refresh the commands: ${message.message}`, { channel: 'commands', detail: message.stack });\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'log':\n\t\t\t\tthis.log('app', toLogLevel(message.level), message.text, { channel: message.channel, detail: message.detail });\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t#onHmr(message: Extract<BridgeMessage, { type: 'hmr' }>): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tif (message.event === 'start') {\n\t\t\tthis.#hmrPaths = message.paths;\n\t\t\tthis.log('stars', 'debug', `Hot reload on, watching ${message.paths.length} ${message.paths.length === 1 ? 'path' : 'paths'}`, {\n\t\t\t\tchannel: 'hmr',\n\t\t\t\tdetail: message.paths.map(relative)\n\t\t\t});\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'stop') {\n\t\t\t// Nothing in the bot watches any more: the next change needs a restart again.\n\t\t\tthis.#hmrPaths = [];\n\t\t\tthis.log('stars', 'debug', 'Hot reload off', { channel: 'hmr' });\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'error') {\n\t\t\tthis.log('stars', 'error', `Could not reload ${relative(message.path)}: ${message.message}`, { channel: 'hmr', detail: message.stack });\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'unloaded') this.#pieces.delete(message.path);\n\t\telse this.#pieces.add(message.path);\n\t\tconst verb = message.event === 'reloaded' ? 'Reloaded' : message.event === 'loaded' ? 'Loaded' : 'Unloaded';\n\t\tthis.log('stars', 'debug', `${verb} ${message.names.join(', ')} from ${relative(message.path)}`, { channel: 'hmr' });\n\t}\n\n\t/**\n\t * Compares the commands the bot registers with the ones Discord is assumed to have. The first report of a session is\n\t * the baseline; a later one that differs from the report before is acted on as `dev.commands.refresh` says, with\n\t * what differs from the deployed commands rather than from that report: a restart that caught the build half written\n\t * reports a part of the commands, and the next one reports them all again, which is no change at all.\n\t */\n\t#onCommands(snapshot: CommandSnapshot): void {\n\t\tconst previous = this.#commands;\n\t\tthis.#commands = snapshot;\n\t\tif (previous === null) {\n\t\t\tthis.#deployed = snapshot;\n\t\t\tconst guilds = Object.keys(snapshot.guilds).length;\n\t\t\tthis.log('stars', 'debug', `Loaded commands: ${snapshot.global.length} global, ${guilds} guild ${guilds === 1 ? 'group' : 'groups'}`, {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: snapshot.global.map((command) => describeCommand({ name: command.name, type: command.type ?? 1, guild: null }))\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// The same report again (a restart for a file that is not a command) has nothing new to say.\n\t\tif (diffSnapshots(previous, snapshot).length === 0) return;\n\n\t\tconst changes = diffSnapshots(this.#deployed ?? previous, snapshot);\n\t\tif (changes.length === 0) {\n\t\t\tif (this.#prompt !== null) {\n\t\t\t\tthis.#prompt = null;\n\t\t\t\tthis.log('stars', 'info', 'The commands match the deployed ones again', { channel: 'commands' });\n\t\t\t\tthis.#emitStatus();\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst detail = changes.map((change) => `${change.kind} ${describeCommand(change)}`);\n\t\tconst mode = this.config.dev.commands?.refresh ?? 'prompt';\n\t\tif (mode === 'auto') {\n\t\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\t\tthis.refreshCommands();\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode === 'off' || !this.promptable) {\n\t\t\tthis.log('stars', 'warn', 'Commands updated, Discord still has the previous ones', {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: [...detail, mode === 'off' ? 'dev.commands.refresh is off' : 'Set dev.commands.refresh to \"auto\" to redeploy without asking']\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// A second change before the first was answered asks once, about all of it: `changes` already holds both.\n\t\tthis.#prompt = { kind: 'commands', changes };\n\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Runs a hook behind the ones already running (see {@link DevService.#hookChain}), e.g. `dev:close` so shutdown\n\t * waits for an unfinished `build:done`.\n\t */\n\tpublic runHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\treturn this.#queueHook(name, ...args);\n\t}\n\n\t/** Queues a hook behind the ones already running; see {@link DevService.#hookChain}. */\n\t#queueHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tthis.#hookChain = this.#hookChain.then(() => this.#callHook(name, ...args));\n\t\treturn this.#hookChain;\n\t}\n\n\t/**\n\t * Starts a hook right away, at the moment it describes, instead of queueing it: `build:before` belongs to the\n\t * builder's `start` boundary, and a builder such as `none` finishes in the same call stack. Its synchronous part\n\t * runs here; later hooks still wait for the rest.\n\t */\n\t#startHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tconst running = this.#callHook(name, ...args);\n\t\tthis.#hookChain = Promise.all([this.#hookChain, running]).then(() => undefined);\n\t\treturn running;\n\t}\n\n\t/**\n\t * Runs a `stars.config` hook. A failing hook is logged rather than thrown: a broken hook must never stop the\n\t * watcher or keep the bot from restarting.\n\t */\n\tasync #callHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tif (this.#hooks === null) return;\n\t\ttry {\n\t\t\tawait this.#hooks.callHook(name, ...args);\n\t\t} catch (error) {\n\t\t\tthis.log('stars', 'error', `Hook ${name} failed: ${error instanceof Error ? error.message : String(error)}`);\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: [\n\t\t\t...envImportArgs(config),\n\t\t\t...moduleImportArgs(config),\n\t\t\t...bridgeImportArgs(config),\n\t\t\t...config.dev.nodeArgs,\n\t\t\tconfig.build.output,\n\t\t\t...config.dev.args\n\t\t],\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\tipc: true\n\t});\n}\n\nfunction formatMs(ms: number | null): string {\n\treturn ms === null ? '' : ` in ${ms}ms`;\n}\n\nfunction formatRequest(message: Extract<BridgeMessage, { type: 'request' }>): string {\n\treturn `${message.method} ${message.path} ${message.status} in ${message.ms}ms`;\n}\n\nconst BRIDGE_LEVELS = new Set<string>(['trace', 'debug', 'info', 'success', 'warn', 'error']);\n\nfunction toLogLevel(level: string): LogLevel {\n\treturn BRIDGE_LEVELS.has(level) ? (level as LogLevel) : 'info';\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, readdirSync, rmSync, type WriteStream } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { stripVTControlCharacters } from 'node:util';\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). `dev.logFile` is a single file truncated on every\n * run; `dev.logs.dir` adds one file per run (see {@link runLogFile}).\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(`${formatLogLine(entry)}\\n`);\n\t};\n}\n\n/** One line per entry, then its detail lines indented under it. Never filtered: the file is the whole session. */\nexport function formatLogLine(entry: LogEntry): string {\n\tconst head = `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.channel.padEnd(12)} ${stripVTControlCharacters(entry.text)}`;\n\treturn [head, ...(entry.detail ?? []).map((line) => ` ${stripVTControlCharacters(line)}`)].join('\\n');\n}\n\nconst RUN_FILE = /^dev-.+\\.log$/;\n\n/**\n * The file of this run inside `dev.logs.dir` (`dev-<timestamp>.log`), after deleting the oldest ones so the directory\n * holds at most `keep` files with the new one. The names sort by time, so no file needs to be opened to order them.\n */\nexport function runLogFile(directory: string, keep: number, now: Date = new Date()): string {\n\tlet existing: string[] = [];\n\ttry {\n\t\texisting = readdirSync(directory)\n\t\t\t.filter((name) => RUN_FILE.test(name))\n\t\t\t.sort();\n\t} catch {\n\t\t// The directory does not exist yet: nothing to prune.\n\t}\n\n\tfor (const name of existing.slice(0, Math.max(0, existing.length - Math.max(0, keep - 1)))) {\n\t\trmSync(join(directory, name), { force: true });\n\t}\n\n\treturn join(directory, `dev-${now.toISOString().replaceAll(':', '-')}.log`);\n}\n","import type { ResolvedStarsConfig } from '@wolfstar/schema';\nimport { defineCommand } from 'citty';\nimport { createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev/dev-service.js';\nimport { withResolvedLocalhost } from '../dev/host.js';\nimport { LogFileWriter, runLogFile } from '../dev/log-file.js';\nimport { initialLogView, isLogLevel } from '../dev/tui/log-view.js';\nimport { collectFlag, projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { ExitCode, renderCrashReport } from '../utils/errors.js';\nimport { applyEnvOptions, loadProject, withProjectEnv } from '../utils/hooks.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../utils/output-mode.js';\nimport { THEME_SETTINGS, isThemeSetting, readSavedTheme, resolveThemeSetting, saveTheme } from '../utils/theme.js';\nimport { prepareProject, reportWarnings } from './_shared.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n\t/** `--theme`. */\n\ttheme?: string;\n\t/** `--layout`. */\n\tlayout?: string;\n\t/** `--channel`, each value possibly a comma-separated list. */\n\tchannel?: string[];\n\t/** `--level`. */\n\tlevel?: string;\n\t/** `--tunnel` opens the tunnel at start whatever `dev.tunnel` says, `--no-tunnel` keeps it closed. */\n\ttunnel?: boolean;\n\t/** `--no-mouse` leaves the mouse to the terminal whatever `dev.mouse` says. */\n\tmouse?: boolean;\n}\n\nconst LAYOUTS = ['auto', 'dashboard', 'panel'] as const;\nconst LEVELS = ['trace', 'debug', 'info', 'warn', 'error'] as const;\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tif (options.theme !== undefined && !isThemeSetting(options.theme)) {\n\t\tthrow cliDiagnostics.INVALID_THEME({ theme: options.theme, themes: THEME_SETTINGS.join(', ') });\n\t}\n\n\tconst layout = options.layout as (typeof LAYOUTS)[number] | undefined;\n\tif (layout !== undefined && !LAYOUTS.includes(layout)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--layout', value: options.layout!, allowed: LAYOUTS.join(', ') });\n\t}\n\n\tif (options.level !== undefined && !isLogLevel(options.level)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--level', value: options.level, allowed: LEVELS.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 project = await loadProject({ cwd: resolveCwd(options), configFile: options.config, env: { ...process.env, NODE_ENV: 'development' } });\n\tconst hooks = project.hooks;\n\t// `env:options` first: it may pick env files with another `HTTP_PORT`, which `dev.url` is derived from.\n\tconst config = await resolveDevConfig(withProjectEnv(await applyEnvOptions(project.config, hooks)));\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\tconst theme = resolveThemeSetting({ flag: options.theme, saved: readSavedTheme() });\n\n\tconst builder = await createBuilder(config, hooks);\n\tawait hooks.callHook('builder:created', builder, config);\n\tconst service = new DevService(config, { builder, hooks, openTunnel: options.tunnel });\n\tawait reportWarnings(config, (text) => service.log('stars', 'warn', text), { production: false });\n\t// The files receive every entry: the filter below only decides what the terminal shows.\n\tconst logFiles = [\n\t\t...(config.dev.logFile ? [config.dev.logFile] : []),\n\t\t...(config.dev.logs?.dir ? [runLogFile(config.dev.logs.dir, config.dev.logs.keep)] : [])\n\t].map((file) => new LogFileWriter(file, service.logs));\n\tfor (const file of logFiles) file.open();\n\tconst filter = initialLogView(config, { channels: options.channel, level: options.level });\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\tlayout,\n\t\t\t\t\tfilter,\n\t\t\t\t\tmouse: options.mouse,\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, filter });\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\t// A failing hook must not keep the bot running.\n\t\t\t// Behind any hook still running (an async `build:done`), and logged rather than thrown if it fails.\n\t\t\tawait service.runHook('dev:close', config);\n\t\t\tawait service.stop();\n\t\t\tfor (const file of logFiles) file.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, hooks);\n\t\tawait service.start();\n\t\tawait hooks.callHook('dev:start', config);\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\tlayout: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Layout of the interactive UI: ${LAYOUTS.join(', ')} (defaults to dev.layout; press v in the UI to switch)`\n\t\t},\n\t\tchannel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Only show these log channels at start (repeatable, or comma-separated; defaults to dev.logs.channels)'\n\t\t},\n\t\tlevel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Only show this log level and the more severe ones at start: ${LEVELS.join(', ')} (defaults to dev.logs.levels)`\n\t\t},\n\t\t// No default: left out, `dev.tunnel` decides.\n\t\ttunnel: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Open the public tunnel at start, whatever dev.tunnel says',\n\t\t\tnegativeDescription: 'Keep the public tunnel closed at start even when dev.tunnel enables it'\n\t\t},\n\t\t// No default: left out, `dev.mouse` decides.\n\t\tmouse: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Clickable dashboard and wheel scrolling (defaults to dev.mouse)',\n\t\t\tnegativeDescription: 'Leave the mouse to the terminal even when dev.mouse is on'\n\t\t}\n\t},\n\tasync run({ args, rawArgs }) {\n\t\tconst channel = collectFlag(rawArgs, 'channel');\n\t\tawait runDev({\n\t\t\tconfig: args.config,\n\t\t\tcwd: args.cwd,\n\t\t\ttui: args.tui ? undefined : false,\n\t\t\ttheme: args.theme,\n\t\t\tlayout: args.layout,\n\t\t\tchannel: channel.length > 0 ? channel : undefined,\n\t\t\tlevel: args.level,\n\t\t\ttunnel: args.tunnel,\n\t\t\tmouse: args.mouse\n\t\t});\n\t}\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev-CnXgPQ8F.js","names":[],"sources":["../src/dev/changed-files.ts","../src/dev/dev-service.ts","../src/dev/host.ts","../src/dev/log-file.ts","../src/commands/dev.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { basename, extname, join, sep } from 'node:path';\n\nconst SKIPPED_DIRECTORIES = new Set(['node_modules', '.git']);\n/**\n * `tsc -b` rewrites its build info on every build, whatever changed; a source map only ever changes with the file it\n * maps, and is most of what there would be to read.\n */\nconst SKIPPED_FILES = /\\.(?:tsbuildinfo|map)$/;\n\n/** The content hash of every file under the roots, by absolute path. */\nexport type FileSnapshot = ReadonlyMap<string, string>;\n\n/** What was last read of each file, so one that was not touched since is not read again. */\nexport type HashCache = Map<string, { size: number; mtimeMs: number; hash: string }>;\n\n/**\n * Hashes every file under `roots`, skipping `node_modules`, `.git` and everything under `exclude`.\n *\n * The builders only report that a build finished, and a bundler rewrites files it did not change (`tsdown` writes\n * its whole output on every rebuild), so neither an event nor a modification time says what a build changed: two\n * snapshots do (see {@link changedFiles}).\n */\nexport function snapshotFiles(roots: readonly string[], exclude: readonly string[] = [], cache: HashCache = new Map()): FileSnapshot {\n\tconst snapshot = new Map<string, string>();\n\tconst visit = (directory: string) => {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = readdirSync(directory, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const entry of entries) {\n\t\t\tconst path = join(directory, entry.name);\n\t\t\tif (exclude.some((excluded) => isInside(path, excluded))) continue;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (!SKIPPED_DIRECTORIES.has(entry.name)) visit(path);\n\t\t\t} else if (entry.isFile() && !SKIPPED_FILES.test(entry.name)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst { size, mtimeMs } = statSync(path);\n\t\t\t\t\tconst known = cache.get(path);\n\t\t\t\t\tconst hash =\n\t\t\t\t\t\tknown?.size === size && known.mtimeMs === mtimeMs\n\t\t\t\t\t\t\t? known.hash\n\t\t\t\t\t\t\t: createHash('sha1').update(readFileSync(path)).digest('base64');\n\t\t\t\t\tcache.set(path, { size, mtimeMs, hash });\n\t\t\t\t\tsnapshot.set(path, hash);\n\t\t\t\t} catch {\n\t\t\t\t\t// Removed between the listing and the read: the next build reports it.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\tfor (const root of new Set(roots)) visit(root);\n\treturn snapshot;\n}\n\n/** The files that were added, removed, or whose content differs between two snapshots. */\nexport function changedFiles(previous: FileSnapshot, next: FileSnapshot): string[] {\n\tconst changed = [...next].filter(([path, hash]) => previous.get(path) !== hash).map(([path]) => path);\n\tfor (const path of previous.keys()) {\n\t\tif (!next.has(path)) changed.push(path);\n\t}\n\n\treturn changed;\n}\n\nconst PIECE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts']);\n\n/**\n * Whether a file a store has not loaded yet could be a piece, by the rule the stores filter files with: a script\n * that is not a declaration file and whose name does not start with `_`.\n */\nexport function couldBePiece(path: string): boolean {\n\tconst extension = extname(path);\n\treturn PIECE_EXTENSIONS.has(extension) && !path.endsWith('.d.ts') && !basename(path, extension).startsWith('_');\n}\n\n/** Whether `path` is `directory` itself or lies under it. */\nexport function isInside(path: string, directory: string): boolean {\n\treturn path === directory || path.startsWith(directory.endsWith(sep) ? directory : `${directory}${sep}`);\n}\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from '../builders/types.js';\nimport { displayPath, type ResolvedStarsConfig, type StarsHooks, type StarsRestartReason } from '@wolfstar/schema';\nimport { describeCommand, diffSnapshots, type CommandChange } from '../utils/command-diff.js';\nimport { envImportArgs, moduleImportArgs } from '../utils/env-import.js';\nimport type { StarsHookable } from '../utils/hooks.js';\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 { bridgeImportArgs, CLI_SOURCE, parseBridgeMessage, type BridgeMessage, type CommandSnapshot, type RefreshRequest } from './bridge.js';\nimport { changedFiles, couldBePiece, isInside, snapshotFiles, type FileSnapshot, type HashCache } from './changed-files.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 = StarsRestartReason;\n\n/** A question the dev UI asks the user; {@link DevService.answerPrompt} settles it. */\nexport interface DevPrompt {\n\treadonly kind: 'commands';\n\treadonly changes: readonly CommandChange[];\n}\n\nexport interface LogExtra {\n\t/** The channel to log on instead of the source's own. */\n\tchannel?: string;\n\tdetail?: readonly string[];\n}\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\t/** Whether the running bot reported that it is listening (see the bridge); always `false` without the bridge. */\n\treadonly ready: boolean;\n\t/** The port the bot reported it listens on. */\n\treadonly port: number | null;\n\t/** Whether the running bot reported that it hot reloads its pieces. */\n\treadonly hmr: boolean;\n\t/** Whether the bot was stopped on purpose (the `d` key) and waits for a restart. */\n\treadonly paused: boolean;\n\treadonly prompt: DevPrompt | null;\n}\n\nexport interface DevServiceEvents {\n\tstatus: [status: DevStatus];\n}\n\nexport interface DevServiceOptions {\n\tbuilder: Builder;\n\t/** The `stars.config` hooks; `build:*` and `dev:restart` run from here. */\n\thooks?: StarsHookable;\n\tlogs?: LogBuffer;\n\t/** Overrides for tests. */\n\tsupervisor?: ProcessSupervisor;\n\ttypechecker?: Typechecker;\n\ttunnel?: Tunnel;\n\thealthInterval?: number;\n\t/**\n\t * Whether the tunnel opens at start: `true` opens it whatever `dev.tunnel` says (a quick tunnel when it is off),\n\t * `false` keeps it closed, and `undefined` leaves it to `dev.tunnel`. The `--tunnel`/`--no-tunnel` flags.\n\t */\n\topenTunnel?: boolean;\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\t/** Whether something can answer a {@link DevPrompt}: the interactive UI sets it, the plain renderer cannot. */\n\tpublic promptable = false;\n\treadonly #hooks: StarsHookable | null;\n\treadonly #openTunnel: boolean | undefined;\n\t/** Runs hooks one at a time, so an async `build:done` settles before the next build's hooks or a restart. */\n\t#hookChain: Promise<void> = Promise.resolve();\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/** The restart a build asked for that has not happened yet: it outlives the timer, which the next build start clears. */\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\t#ready = false;\n\t#port: number | null = null;\n\t/** The store paths the running bot watches for hot reloads, empty when it does not. */\n\t#hmrPaths: string[] = [];\n\t#paused = false;\n\t#prompt: DevPrompt | null = null;\n\t/** The commands the bot last reported, kept across restarts: a restart is how most changes arrive. */\n\t#commands: CommandSnapshot | null = null;\n\t/** What the build output held after the previous build, `null` before the first one. */\n\t#files: FileSnapshot | null = null;\n\treadonly #hashes: HashCache = new Map();\n\t/** The files the running bot loaded pieces from: what its hot reload can replace. */\n\t#pieces = new Set<string>();\n\t/** The commands Discord is assumed to have: the first report of the session, then whatever was last deployed. */\n\t#deployed: CommandSnapshot | null = null;\n\t/** A refresh of the commands asked for while the bot was not listening; sent once it is. */\n\t#refreshPending = false;\n\t/** Restarts waiting for the running build to end, see {@link DevService.#buildSettled}. */\n\t#buildWaiters: Array<() => void> = [];\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\t\tthis.#hooks = options.hooks ?? null;\n\t\tthis.#openTunnel = options.openTunnel;\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\t// A restart armed by the previous build must not fire while this one rewrites the output; it stays owed.\n\t\t\tthis.#clearRestartTimer();\n\t\t\tthis.#setBuild('building');\n\t\t\tvoid this.#startHook('build:before', this.config);\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('message', (message) => {\n\t\t\tconst parsed = parseBridgeMessage(message);\n\t\t\tif (parsed === null) return;\n\t\t\ttry {\n\t\t\t\tthis.#onBridge(parsed);\n\t\t\t} catch (error) {\n\t\t\t\t// Whatever the bot sends, it must not take the session down.\n\t\t\t\tthis.log('stars', 'warn', `Ignored a ${parsed.type} message from the bot: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t}\n\t\t});\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\tready: this.#ready,\n\t\t\tport: this.#port,\n\t\t\thmr: this.#hmrPaths.length > 0,\n\t\t\tpaused: this.#paused,\n\t\t\tprompt: this.#prompt\n\t\t};\n\t}\n\n\tpublic log(source: LogSource, level: LogLevel, text: string, extra: LogExtra = {}): void {\n\t\tthis.logs.push({ source, level, text, ...extra });\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\t// `--no-tunnel` only keeps it closed at start: the `t` key still opens one.\n\t\tif (this.#openTunnel !== false) void this.tunnel.start(this.#openTunnel === true);\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\tawait this.#queueHook('dev:restart', reason, this.config);\n\t\t\tif (this.#stopped) return;\n\t\t\tif (!(await this.#buildSettled(reason))) 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\t// Stopping the bot takes a while, and a build may have begun meanwhile.\n\t\t\tif (this.#stopped || !(await this.#buildSettled(reason))) return;\n\t\t\t// The bot that starts now runs the latest output, so no restart is owed any more.\n\t\t\tthis.#pendingReason = null;\n\t\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#paused = false;\n\t\t\tthis.supervisor.start();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/**\n\t * Stops the bot and leaves it stopped (the `d` key): builds keep running, but nothing starts the bot again until\n\t * {@link restart}. For when the bot must be off without ending the session.\n\t */\n\tpublic disconnect(): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped || this.#paused) return;\n\t\t\tthis.#paused = true;\n\t\t\tthis.log('stars', 'info', 'Stopped the bot, press r to start it again');\n\t\t\tawait this.supervisor.stop();\n\t\t\tthis.#emitStatus();\n\t\t});\n\t}\n\n\t/** Settles the pending {@link DevPrompt}: `true` does what it asks, `false` dismisses it. */\n\tpublic answerPrompt(accept: boolean): void {\n\t\tconst prompt = this.#prompt;\n\t\tif (prompt === null) return;\n\t\t// The question stays open: the answer is not given up, only the redeploy is not safe yet.\n\t\tif (accept && this.#refreshBlocked()) return;\n\t\tthis.#prompt = null;\n\t\tif (accept) this.refreshCommands();\n\t\telse this.log('stars', 'info', 'Kept the deployed commands as they are', { channel: 'commands' });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Whether a refresh has to wait, saying why. While a build rewrites the output a bot that starts loads a part of the\n\t * commands, and registering that would unregister the rest from Discord.\n\t */\n\t#refreshBlocked(): boolean {\n\t\tif (this.#build !== 'building') return false;\n\t\tthis.log('stars', 'warn', 'A build is running, the commands of the bot may be incomplete: answer again once it has finished', {\n\t\t\tchannel: 'commands'\n\t\t});\n\t\treturn true;\n\t}\n\n\t/**\n\t * Resolves once no build is running, `false` when the restart is not worth doing any more: the session stopped, or\n\t * an automatic restart (`build`, `initial`) had to wait for a build, whose own end already decides what happens next\n\t * (a restart if it succeeded, nothing if it failed: the output would be the previous, possibly half written one).\n\t * The dropped restart stays owed in {@link DevService.#pendingReason}, so that build's end restarts the bot even when\n\t * it could be hot reloaded: the change the dropped restart was for is not part of what that build rewrote.\n\t * A manual restart or one after a crash is still what was asked for.\n\t */\n\tasync #buildSettled(reason: RestartReason): Promise<boolean> {\n\t\tconst automatic = reason === 'build' || reason === 'initial';\n\t\tlet waited = false;\n\t\twhile (this.#build === 'building' && !this.#stopped) {\n\t\t\twaited = true;\n\t\t\tawait new Promise<void>((resolve) => this.#buildWaiters.push(resolve));\n\t\t}\n\n\t\tif (this.#stopped) return false;\n\t\tif (waited && automatic) {\n\t\t\tthis.#pendingReason ??= reason;\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t#releaseBuildWaiters(): void {\n\t\tconst waiters = this.#buildWaiters;\n\t\tthis.#buildWaiters = [];\n\t\tfor (const resolve of waiters) resolve();\n\t}\n\n\t/** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */\n\tpublic refreshCommands(): void {\n\t\tif (this.#refreshBlocked()) return;\n\t\t// A bot that is stopped or restarting has no client to register anything with: the refresh waits for it to listen.\n\t\tif (!this.#ready) {\n\t\t\tthis.#refreshPending = true;\n\t\t\tconst when = this.supervisor.running ? 'once the bot is ready' : 'when the bot starts again';\n\t\t\tthis.log('stars', 'info', `Refreshing commands ${when}…`, { channel: 'commands' });\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#refreshPending = false;\n\t\tconst current = this.#commands?.guilds ?? {};\n\t\tconst request: RefreshRequest = {\n\t\t\tsource: CLI_SOURCE,\n\t\t\ttype: 'commands:refresh',\n\t\t\tclearGuilds: Object.keys(this.#deployed?.guilds ?? {}).filter((guild) => !Object.hasOwn(current, guild))\n\t\t};\n\t\tif (this.supervisor.send(request)) {\n\t\t\tthis.log('stars', 'info', 'Refreshing commands…', { channel: 'commands' });\n\t\t} else {\n\t\t\tthis.log('stars', 'warn', 'Could not reach the bot to refresh its commands', { channel: 'commands' });\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\t// A restart waiting for a build must not hold the queue that closes the session.\n\t\tthis.#releaseBuildWaiters();\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.#releaseBuildWaiters();\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\t// Before the locales are copied: the copy rewrites every file, which is not what the build changed.\n\t\tconst hot = this.#hotReloadable();\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\t// Only once locales are copied: until then the build is not done, and may still fail.\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\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\tif (this.#paused) return;\n\t\t// A restart an earlier build asked for (and a build start cleared) is still owed: its change is not in `hot`.\n\t\tif (hot !== null && this.#pendingReason === null) {\n\t\t\tfor (const file of hot) this.log('stars', 'trace', `UPDATE ${displayPath(this.config.root, file)}`, { channel: 'hmr' });\n\t\t\tthis.#settleProgress();\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\t\tthis.#scheduleRestart(this.supervisor.state === 'idle' ? 'initial' : 'build');\n\t}\n\n\t/**\n\t * The files the build rewrote when all of them are pieces the running bot hot reloads, `null` when the bot has to\n\t * be restarted instead: it is not running, it does not hot reload, `dev.hmr` is off, nothing changed (a rebuild for\n\t * a reason that cannot be seen from the output), or a file changed that the bot cannot replace. What a build changed\n\t * is the difference in content with the previous output, since a bundler rewrites files it did not change.\n\t *\n\t * The bot replaces the files it loaded pieces from, and loads a new file that could be one. A helper module next to\n\t * the pieces (`_shared.js`, or any file no piece came from) is imported once and stays as it was: changing it\n\t * needs a restart, even though it lies in a store path.\n\t */\n\t#hotReloadable(): string[] | null {\n\t\tif (this.config.dev.hmr === false) return null;\n\n\t\tconst roots = this.config.build.tool === 'none' ? this.config.dev.watch : [this.config.build.outDir];\n\t\tconst previous = this.#files;\n\t\tthis.#files = snapshotFiles(roots, [this.locales.destination], this.#hashes);\n\t\tif (previous === null || this.#hmrPaths.length === 0 || this.supervisor.state !== 'running') return null;\n\n\t\tconst changed = changedFiles(previous, this.#files);\n\t\tconst replaceable = (file: string) =>\n\t\t\tthis.#hmrPaths.some((path) => isInside(file, path)) && (this.#pieces.has(file) || (!previous.has(file) && couldBePiece(file)));\n\t\treturn changed.length > 0 && changed.every(replaceable) ? changed : null;\n\t}\n\n\t#onBuildFailure(outcome: BuildOutcome): void {\n\t\tthis.#lastBuild = outcome;\n\t\tvoid this.#queueHook('build:done', outcome, this.config);\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\tthis.#ready = false;\n\t\tthis.#port = null;\n\t\tthis.#hmrPaths = [];\n\t\tthis.#pieces.clear();\n\t\t// A pending prompt stays: the commands still differ from what Discord has, and the next process can deploy them.\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\tif (this.#paused) return;\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\tif (state !== 'building') this.#releaseBuildWaiters();\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/** Turns what the bot reports over the bridge into log entries and status. */\n\t#onBridge(message: BridgeMessage): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tswitch (message.type) {\n\t\t\tcase 'ready':\n\t\t\t\tthis.#ready = true;\n\t\t\t\tthis.#port = message.port;\n\t\t\t\tthis.log('stars', 'success', message.port === null ? 'The bot is ready' : `Listening on port ${message.port}`, {\n\t\t\t\t\tchannel: 'lifecycle'\n\t\t\t\t});\n\t\t\t\tif (this.#refreshPending) this.refreshCommands();\n\t\t\t\tthis.#emitStatus();\n\t\t\t\treturn;\n\t\t\tcase 'pieces':\n\t\t\t\tfor (const piece of message.pieces) this.#pieces.add(piece.path);\n\t\t\t\tif (message.pieces.length === 0) return;\n\t\t\t\tthis.log('stars', 'debug', `Loaded ${message.store}: ${message.pieces.length} ${message.pieces.length === 1 ? 'piece' : 'pieces'}`, {\n\t\t\t\t\tchannel: message.store === 'commands' ? 'commands' : 'lifecycle',\n\t\t\t\t\tdetail: message.pieces.map((piece) => `${piece.name} ${relative(piece.path)}`)\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'hmr':\n\t\t\t\tthis.#onHmr(message);\n\t\t\t\treturn;\n\t\t\tcase 'commands':\n\t\t\t\tthis.#onCommands({ global: message.global, guilds: message.guilds });\n\t\t\t\treturn;\n\t\t\tcase 'dispatch':\n\t\t\t\tif (message.phase === 'run') {\n\t\t\t\t\tthis.log('stars', 'debug', `Processing ${message.route} with ${message.piece}`, { channel: 'interactions' });\n\t\t\t\t} else if (message.phase === 'success') {\n\t\t\t\t\tthis.log('stars', 'trace', `${message.route} handled${formatMs(message.ms)}`, { channel: 'interactions' });\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `${message.route} failed${formatMs(message.ms)}: ${message.error.message}`, {\n\t\t\t\t\t\tchannel: 'interactions',\n\t\t\t\t\t\tdetail: message.error.stack\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'request':\n\t\t\t\tthis.log('stars', message.status >= 500 ? 'error' : message.status >= 400 ? 'warn' : 'trace', formatRequest(message), {\n\t\t\t\t\tchannel: 'http'\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\tcase 'refreshed':\n\t\t\t\tif (message.ok) {\n\t\t\t\t\tthis.#deployed = this.#commands;\n\t\t\t\t\tconst guilds =\n\t\t\t\t\t\t(message.guilds > 0 ? `, ${message.guilds} guild ${message.guilds === 1 ? 'group' : 'groups'}` : '') +\n\t\t\t\t\t\t(message.cleared > 0 ? `, cleared ${message.cleared} ${message.cleared === 1 ? 'guild' : 'guilds'}` : '');\n\t\t\t\t\tthis.log('stars', 'success', `Deployed ${message.global} global ${message.global === 1 ? 'command' : 'commands'}${guilds}`, {\n\t\t\t\t\t\tchannel: 'commands'\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('stars', 'error', `Could not refresh the commands: ${message.message}`, { channel: 'commands', detail: message.stack });\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tcase 'log':\n\t\t\t\tthis.log('app', toLogLevel(message.level), message.text, { channel: message.channel, detail: message.detail });\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t#onHmr(message: Extract<BridgeMessage, { type: 'hmr' }>): void {\n\t\tconst relative = (path: string) => displayPath(this.config.root, path);\n\t\tif (message.event === 'start') {\n\t\t\tthis.#hmrPaths = message.paths;\n\t\t\tthis.log('stars', 'debug', `Hot reload on, watching ${message.paths.length} ${message.paths.length === 1 ? 'path' : 'paths'}`, {\n\t\t\t\tchannel: 'hmr',\n\t\t\t\tdetail: message.paths.map(relative)\n\t\t\t});\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'stop') {\n\t\t\t// Nothing in the bot watches any more: the next change needs a restart again.\n\t\t\tthis.#hmrPaths = [];\n\t\t\tthis.log('stars', 'debug', 'Hot reload off', { channel: 'hmr' });\n\t\t\tthis.#emitStatus();\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'error') {\n\t\t\tthis.log('stars', 'error', `Could not reload ${relative(message.path)}: ${message.message}`, { channel: 'hmr', detail: message.stack });\n\t\t\treturn;\n\t\t}\n\n\t\tif (message.event === 'unloaded') this.#pieces.delete(message.path);\n\t\telse this.#pieces.add(message.path);\n\t\tconst verb = message.event === 'reloaded' ? 'Reloaded' : message.event === 'loaded' ? 'Loaded' : 'Unloaded';\n\t\tthis.log('stars', 'debug', `${verb} ${message.names.join(', ')} from ${relative(message.path)}`, { channel: 'hmr' });\n\t}\n\n\t/**\n\t * Compares the commands the bot registers with the ones Discord is assumed to have. The first report of a session is\n\t * the baseline; a later one that differs from the report before is acted on as `dev.commands.refresh` says, with\n\t * what differs from the deployed commands rather than from that report: a restart that caught the build half written\n\t * reports a part of the commands, and the next one reports them all again, which is no change at all.\n\t */\n\t#onCommands(snapshot: CommandSnapshot): void {\n\t\tconst previous = this.#commands;\n\t\tthis.#commands = snapshot;\n\t\tif (previous === null) {\n\t\t\tthis.#deployed = snapshot;\n\t\t\tconst guilds = Object.keys(snapshot.guilds).length;\n\t\t\tthis.log('stars', 'debug', `Loaded commands: ${snapshot.global.length} global, ${guilds} guild ${guilds === 1 ? 'group' : 'groups'}`, {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: snapshot.global.map((command) => describeCommand({ name: command.name, type: command.type ?? 1, guild: null }))\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// The same report again (a restart for a file that is not a command) has nothing new to say.\n\t\tif (diffSnapshots(previous, snapshot).length === 0) return;\n\n\t\tconst changes = diffSnapshots(this.#deployed ?? previous, snapshot);\n\t\tif (changes.length === 0) {\n\t\t\tif (this.#prompt !== null) {\n\t\t\t\tthis.#prompt = null;\n\t\t\t\tthis.log('stars', 'info', 'The commands match the deployed ones again', { channel: 'commands' });\n\t\t\t\tthis.#emitStatus();\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst detail = changes.map((change) => `${change.kind} ${describeCommand(change)}`);\n\t\tconst mode = this.config.dev.commands?.refresh ?? 'prompt';\n\t\tif (mode === 'auto') {\n\t\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\t\tthis.refreshCommands();\n\t\t\treturn;\n\t\t}\n\n\t\tif (mode === 'off' || !this.promptable) {\n\t\t\tthis.log('stars', 'warn', 'Commands updated, Discord still has the previous ones', {\n\t\t\t\tchannel: 'commands',\n\t\t\t\tdetail: [...detail, mode === 'off' ? 'dev.commands.refresh is off' : 'Set dev.commands.refresh to \"auto\" to redeploy without asking']\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// A second change before the first was answered asks once, about all of it: `changes` already holds both.\n\t\tthis.#prompt = { kind: 'commands', changes };\n\t\tthis.log('stars', 'info', 'Commands updated', { channel: 'commands', detail });\n\t\tthis.#emitStatus();\n\t}\n\n\t/**\n\t * Runs a hook behind the ones already running (see {@link DevService.#hookChain}), e.g. `dev:close` so shutdown\n\t * waits for an unfinished `build:done`.\n\t */\n\tpublic runHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\treturn this.#queueHook(name, ...args);\n\t}\n\n\t/** Queues a hook behind the ones already running; see {@link DevService.#hookChain}. */\n\t#queueHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tthis.#hookChain = this.#hookChain.then(() => this.#callHook(name, ...args));\n\t\treturn this.#hookChain;\n\t}\n\n\t/**\n\t * Starts a hook right away, at the moment it describes, instead of queueing it: `build:before` belongs to the\n\t * builder's `start` boundary, and a builder such as `none` finishes in the same call stack. Its synchronous part\n\t * runs here; later hooks still wait for the rest.\n\t */\n\t#startHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tconst running = this.#callHook(name, ...args);\n\t\tthis.#hookChain = Promise.all([this.#hookChain, running]).then(() => undefined);\n\t\treturn running;\n\t}\n\n\t/**\n\t * Runs a `stars.config` hook. A failing hook is logged rather than thrown: a broken hook must never stop the\n\t * watcher or keep the bot from restarting.\n\t */\n\tasync #callHook<Name extends keyof StarsHooks>(name: Name, ...args: Parameters<StarsHooks[Name]>): Promise<void> {\n\t\tif (this.#hooks === null) return;\n\t\ttry {\n\t\t\tawait this.#hooks.callHook(name, ...args);\n\t\t} catch (error) {\n\t\t\tthis.log('stars', 'error', `Hook ${name} failed: ${error instanceof Error ? error.message : String(error)}`);\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: [\n\t\t\t...envImportArgs(config),\n\t\t\t...moduleImportArgs(config),\n\t\t\t...bridgeImportArgs(config),\n\t\t\t...config.dev.nodeArgs,\n\t\t\tconfig.build.output,\n\t\t\t...config.dev.args\n\t\t],\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\tipc: true\n\t});\n}\n\nfunction formatMs(ms: number | null): string {\n\treturn ms === null ? '' : ` in ${ms}ms`;\n}\n\nfunction formatRequest(message: Extract<BridgeMessage, { type: 'request' }>): string {\n\treturn `${message.method} ${message.path} ${message.status} in ${message.ms}ms`;\n}\n\nconst BRIDGE_LEVELS = new Set<string>(['trace', 'debug', 'info', 'success', 'warn', 'error']);\n\nfunction toLogLevel(level: string): LogLevel {\n\treturn BRIDGE_LEVELS.has(level) ? (level as LogLevel) : 'info';\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, readdirSync, rmSync, type WriteStream } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { stripVTControlCharacters } from 'node:util';\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). `dev.logFile` is a single file truncated on every\n * run; `dev.logs.dir` adds one file per run (see {@link runLogFile}).\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(`${formatLogLine(entry)}\\n`);\n\t};\n}\n\n/** One line per entry, then its detail lines indented under it. Never filtered: the file is the whole session. */\nexport function formatLogLine(entry: LogEntry): string {\n\tconst head = `${new Date(entry.time).toISOString()} ${entry.level.padEnd(7)} ${entry.channel.padEnd(12)} ${stripVTControlCharacters(entry.text)}`;\n\treturn [head, ...(entry.detail ?? []).map((line) => ` ${stripVTControlCharacters(line)}`)].join('\\n');\n}\n\nconst RUN_FILE = /^dev-.+\\.log$/;\n\n/**\n * The file of this run inside `dev.logs.dir` (`dev-<timestamp>.log`), after deleting the oldest ones so the directory\n * holds at most `keep` files with the new one. The names sort by time, so no file needs to be opened to order them.\n */\nexport function runLogFile(directory: string, keep: number, now: Date = new Date()): string {\n\tlet existing: string[] = [];\n\ttry {\n\t\texisting = readdirSync(directory)\n\t\t\t.filter((name) => RUN_FILE.test(name))\n\t\t\t.sort();\n\t} catch {\n\t\t// The directory does not exist yet: nothing to prune.\n\t}\n\n\tfor (const name of existing.slice(0, Math.max(0, existing.length - Math.max(0, keep - 1)))) {\n\t\trmSync(join(directory, name), { force: true });\n\t}\n\n\treturn join(directory, `dev-${now.toISOString().replaceAll(':', '-')}.log`);\n}\n","import type { ResolvedStarsConfig } from '@wolfstar/schema';\nimport { defineCommand } from 'citty';\nimport { createBuilder } from '../builders/index.js';\nimport { DevService } from '../dev/dev-service.js';\nimport { withResolvedLocalhost } from '../dev/host.js';\nimport { LogFileWriter, runLogFile } from '../dev/log-file.js';\nimport { initialLogView, isLogLevel } from '../dev/tui/log-view.js';\nimport { collectFlag, projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { ExitCode, renderCrashReport } from '../utils/errors.js';\nimport { applyEnvOptions, loadProject, withProjectEnv } from '../utils/hooks.js';\nimport { prefersReducedMotion, resolveOutputMode, shouldUseColor } from '../utils/output-mode.js';\nimport { THEME_SETTINGS, isThemeSetting, readSavedTheme, resolveThemeSetting, saveTheme } from '../utils/theme.js';\nimport { prepareProject, reportWarnings } from './_shared.js';\n\nexport interface DevTaskOptions extends ProjectArgs {\n\ttui?: boolean;\n\t/** `--theme`. */\n\ttheme?: string;\n\t/** `--layout`. */\n\tlayout?: string;\n\t/** `--channel`, each value possibly a comma-separated list. */\n\tchannel?: string[];\n\t/** `--level`. */\n\tlevel?: string;\n\t/** `--tunnel` opens the tunnel at start whatever `dev.tunnel` says, `--no-tunnel` keeps it closed. */\n\ttunnel?: boolean;\n\t/** `--no-mouse` leaves the mouse to the terminal whatever `dev.mouse` says. */\n\tmouse?: boolean;\n}\n\nconst LAYOUTS = ['auto', 'dashboard', 'panel'] as const;\nconst LEVELS = ['trace', 'debug', 'info', 'warn', 'error'] as const;\n\nexport async function runDev(options: DevTaskOptions): Promise<void> {\n\tif (options.theme !== undefined && !isThemeSetting(options.theme)) {\n\t\tthrow cliDiagnostics.INVALID_THEME({ theme: options.theme, themes: THEME_SETTINGS.join(', ') });\n\t}\n\n\tconst layout = options.layout as (typeof LAYOUTS)[number] | undefined;\n\tif (layout !== undefined && !LAYOUTS.includes(layout)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--layout', value: options.layout!, allowed: LAYOUTS.join(', ') });\n\t}\n\n\tif (options.level !== undefined && !isLogLevel(options.level)) {\n\t\tthrow cliDiagnostics.INVALID_OPTION({ option: '--level', value: options.level, allowed: LEVELS.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 project = await loadProject({ cwd: resolveCwd(options), configFile: options.config, env: { ...process.env, NODE_ENV: 'development' } });\n\tconst hooks = project.hooks;\n\t// `env:options` first: it may pick env files with another `HTTP_PORT`, which `dev.url` is derived from.\n\tconst config = await resolveDevConfig(withProjectEnv(await applyEnvOptions(project.config, hooks)));\n\tconst mode = resolveOutputMode({ tui: options.tui });\n\tconst color = shouldUseColor();\n\tconst theme = resolveThemeSetting({ flag: options.theme, saved: readSavedTheme() });\n\n\tconst builder = await createBuilder(config, hooks);\n\tawait hooks.callHook('builder:created', builder, config);\n\tconst service = new DevService(config, { builder, hooks, openTunnel: options.tunnel });\n\tawait reportWarnings(config, (text) => service.log('stars', 'warn', text), { production: false });\n\t// The files receive every entry: the filter below only decides what the terminal shows.\n\tconst logFiles = [\n\t\t...(config.dev.logFile ? [config.dev.logFile] : []),\n\t\t...(config.dev.logs?.dir ? [runLogFile(config.dev.logs.dir, config.dev.logs.keep)] : [])\n\t].map((file) => new LogFileWriter(file, service.logs));\n\tfor (const file of logFiles) file.open();\n\tconst filter = initialLogView(config, { channels: options.channel, level: options.level });\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\tlayout,\n\t\t\t\t\tfilter,\n\t\t\t\t\tmouse: options.mouse,\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, filter });\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\t// A failing hook must not keep the bot running.\n\t\t\t// Behind any hook still running (an async `build:done`), and logged rather than thrown if it fails.\n\t\t\tawait service.runHook('dev:close', config);\n\t\t\tawait service.stop();\n\t\t\tfor (const file of logFiles) file.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, hooks);\n\t\tawait service.start();\n\t\tawait hooks.callHook('dev:start', config);\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\tlayout: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Layout of the interactive UI: ${LAYOUTS.join(', ')} (defaults to dev.layout; press v in the UI to switch)`\n\t\t},\n\t\tchannel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Only show these log channels at start (repeatable, or comma-separated; defaults to dev.logs.channels)'\n\t\t},\n\t\tlevel: {\n\t\t\ttype: 'string',\n\t\t\tdescription: `Only show this log level and the more severe ones at start: ${LEVELS.join(', ')} (defaults to dev.logs.levels)`\n\t\t},\n\t\t// No default: left out, `dev.tunnel` decides.\n\t\ttunnel: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Open the public tunnel at start, whatever dev.tunnel says',\n\t\t\tnegativeDescription: 'Keep the public tunnel closed at start even when dev.tunnel enables it'\n\t\t},\n\t\t// No default: left out, `dev.mouse` decides.\n\t\tmouse: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Clickable dashboard and wheel scrolling (defaults to dev.mouse)',\n\t\t\tnegativeDescription: 'Leave the mouse to the terminal even when dev.mouse is on'\n\t\t}\n\t},\n\tasync run({ args, rawArgs }) {\n\t\tconst channel = collectFlag(rawArgs, 'channel');\n\t\tawait runDev({\n\t\t\tconfig: args.config,\n\t\t\tcwd: args.cwd,\n\t\t\ttui: args.tui ? undefined : false,\n\t\t\ttheme: args.theme,\n\t\t\tlayout: args.layout,\n\t\t\tchannel: channel.length > 0 ? channel : undefined,\n\t\t\tlevel: args.level,\n\t\t\ttunnel: args.tunnel,\n\t\t\tmouse: args.mouse\n\t\t});\n\t}\n});\n"],"mappings":"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"}
@@ -2,10 +2,10 @@ import { t as readOwnPackageJson } from "./version-BzclCE19.js";
2
2
  import { i as shouldUseColor } from "./output-mode-Bp7Da8vJ.js";
3
3
  import { t as cliDiagnostics } from "./diagnostics-BKc--u_3.js";
4
4
  import { t as findInstalledVersion } from "./project-EJyKro-M.js";
5
- import { d as projectArgs, f as resolveCwd, i as readProjectEnv, n as loadProject, o as modulesPreloadWarning, r as withProjectEnv, t as applyEnvOptions } from "./hooks-DqlScRZA.js";
6
- import { r as findLegacyRootTsconfig, t as prepareProject } from "./_shared-Bqw7h4O0.js";
5
+ import { d as projectArgs, f as resolveCwd, i as readProjectEnv, n as loadProject, o as modulesPreloadWarning, r as withProjectEnv, t as applyEnvOptions } from "./hooks-CzV2YRoJ.js";
6
+ import { r as findLegacyRootTsconfig, t as prepareProject } from "./_shared-BLXTNq89.js";
7
7
  import { a as isAtLeast, t as BRIDGE_FRAMEWORK_VERSION } from "./bridge-DQ3dZ9pm.js";
8
- import { a as NGROK_AUTHTOKEN_VARIABLE, i as readDiscordCredentials, n as describeQuickTunnel, o as NGROK_INSTALL_HINT, r as endpointUrl, s as NGROK_PACKAGE } from "./tunnel-D3WxypOt.js";
8
+ import { a as NGROK_AUTHTOKEN_VARIABLE, i as readDiscordCredentials, n as describeQuickTunnel, o as NGROK_INSTALL_HINT, r as endpointUrl, s as NGROK_PACKAGE } from "./tunnel-DlFjqUE7.js";
9
9
  import { defineCommand } from "citty";
10
10
  import { existsSync, readFileSync } from "node:fs";
11
11
  import { delimiter, join } from "node:path";
@@ -357,4 +357,4 @@ var doctor_default = defineCommand({
357
357
 
358
358
  //#endregion
359
359
  export { doctor_default as default };
360
- //# sourceMappingURL=doctor-C_lTMEjG.js.map
360
+ //# sourceMappingURL=doctor-BYRkkc5l.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor-C_lTMEjG.js","names":[],"sources":["../src/commands/doctor.ts"],"sourcesContent":["import { displayPath, type ResolvedStarsConfig } from '@wolfstar/schema';\nimport { defineCommand } from 'citty';\nimport { createColors } from 'colorette';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createServer } from 'node:net';\nimport { delimiter, join } from 'node:path';\nimport { BRIDGE_FRAMEWORK_VERSION, isAtLeast } from '../dev/bridge.js';\nimport { endpointUrl, describeQuickTunnel, readDiscordCredentials } from '../dev/tunnel.js';\nimport { NGROK_AUTHTOKEN_VARIABLE, NGROK_INSTALL_HINT, NGROK_PACKAGE } from '../dev/tunnel-providers.js';\nimport { projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { loadProject, withProjectEnv, applyEnvOptions, type StarsHookable } from '../utils/hooks.js';\nimport { modulesPreloadWarning } from '../utils/modules.js';\nimport { shouldUseColor } from '../utils/output-mode.js';\nimport { findLegacyRootTsconfig } from '../utils/tsconfig.js';\nimport { readProjectEnv } from '../utils/project-env.js';\nimport { findInstalledVersion } from '../utils/project.js';\nimport { readOwnPackageJson } from '../utils/version.js';\nimport { prepareProject } from './_shared.js';\n\nexport type CheckStatus = 'ok' | 'info' | 'warn' | 'error';\n\nexport interface Check {\n\tname: string;\n\tstatus: CheckStatus;\n\tmessage: string;\n\t/** What to do about it. */\n\tfix?: string;\n}\n\nexport interface DoctorOptions extends ProjectArgs {\n\tjson?: boolean;\n\t/** Also asks Discord whether the token works and where the application's interactions go. */\n\tonline?: boolean;\n\tstdout?: NodeJS.WritableStream;\n\t/** Overrides for tests. */\n\tenv?: NodeJS.ProcessEnv;\n\tnodeVersion?: string;\n\tfetch?: typeof fetch;\n\tisPortFree?: (port: number, host: string) => Promise<boolean>;\n}\n\n/** The Node.js versions `@wolfstar/cli` runs on (`engines.node` of its `package.json`). */\nconst MINIMUM_NODE = 22;\n\n/**\n * Checks what a project needs before `stars dev` can do its job: the runtime, the framework, the credentials, the\n * interactions endpoint and the generated files. Each check says what is wrong and what to do about it; nothing is\n * changed.\n */\nexport async function collectChecks(config: ResolvedStarsConfig, hooks: StarsHookable, options: DoctorOptions = {}): Promise<Check[]> {\n\tconst env = options.env ?? process.env;\n\tconst checks: Check[] = [];\n\tconst show = (path: string) => displayPath(config.root, path);\n\n\tconst node = options.nodeVersion ?? process.version;\n\tconst major = Number.parseInt(node.replace(/^v/, ''), 10);\n\tchecks.push(\n\t\tmajor >= MINIMUM_NODE\n\t\t\t? { name: 'node', status: 'ok', message: `Node.js ${node}` }\n\t\t\t: { name: 'node', status: 'error', message: `Node.js ${node} is too old`, fix: `Use Node.js ${MINIMUM_NODE} or later.` }\n\t);\n\tconst wanted = readEnginesNode(config.root);\n\tconst minimum = wanted === null ? null : /^\\s*>=\\s*(\\d+)/.exec(wanted);\n\tif (minimum && major < Number(minimum[1])) {\n\t\tchecks.push({\n\t\t\tname: 'engines',\n\t\t\tstatus: 'warn',\n\t\t\tmessage: `The project asks for Node.js ${wanted}, this is ${node}`,\n\t\t\tfix: `Use Node.js ${wanted}.`\n\t\t});\n\t}\n\n\tchecks.push({ name: 'config', status: 'ok', message: config.configFile ? show(config.configFile) : 'no stars.config, running on defaults' });\n\tfor (const warning of [...config.warnings, modulesPreloadWarning(config)]) {\n\t\tif (warning) checks.push({ name: 'config', status: 'warn', message: warning.message, ...(warning.fix ? { fix: warning.fix } : {}) });\n\t}\n\n\tconst framework = findInstalledVersion(config.root, '@wolfstar/http-framework');\n\tif (framework === null) {\n\t\tchecks.push({\n\t\t\tname: 'framework',\n\t\t\tstatus: 'error',\n\t\t\tmessage: '@wolfstar/http-framework is not installed',\n\t\t\tfix: 'Install @wolfstar/http-framework in the project.'\n\t\t});\n\t} else if (isAtLeast(framework, BRIDGE_FRAMEWORK_VERSION)) {\n\t\tchecks.push({ name: 'framework', status: 'ok', message: `@wolfstar/http-framework v${framework}` });\n\t} else {\n\t\tchecks.push({\n\t\t\tname: 'framework',\n\t\t\tstatus: 'warn',\n\t\t\tmessage: `@wolfstar/http-framework v${framework} cannot report to the dev UI`,\n\t\t\tfix: `Update to ${BRIDGE_FRAMEWORK_VERSION} or later for the hmr, commands, interactions and http channels.`\n\t\t});\n\t}\n\n\tchecks.push({ name: 'entry', status: 'ok', message: `${show(config.entry)} (${config.build.tool})` });\n\tif (config.build.tool !== 'none' && !existsSync(config.build.output)) {\n\t\tchecks.push({\n\t\t\tname: 'build',\n\t\t\tstatus: 'info',\n\t\t\tmessage: `${show(config.build.output)} is not built yet`,\n\t\t\tfix: 'Run `stars build` (or `stars dev`).'\n\t\t});\n\t}\n\n\tconst values = { ...readProjectEnv(config), ...definedOnly(env), ...config.dev.env };\n\tconst credentials = readDiscordCredentials(config, env);\n\tchecks.push(\n\t\tcredentials\n\t\t\t? { name: 'token', status: 'ok', message: 'DISCORD_TOKEN is set' }\n\t\t\t: {\n\t\t\t\t\tname: 'token',\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tmessage: 'DISCORD_TOKEN is not set',\n\t\t\t\t\tfix: 'Set DISCORD_TOKEN in the environment or in the project .env file.'\n\t\t\t\t}\n\t);\n\tchecks.push(\n\t\tvalues.DISCORD_PUBLIC_KEY\n\t\t\t? { name: 'public key', status: 'ok', message: 'DISCORD_PUBLIC_KEY is set' }\n\t\t\t: {\n\t\t\t\t\tname: 'public key',\n\t\t\t\t\tstatus: 'warn',\n\t\t\t\t\tmessage: 'DISCORD_PUBLIC_KEY is not set',\n\t\t\t\t\tfix: 'Set it in the project .env file, unless the bot passes `discordPublicKey` to its Client.'\n\t\t\t\t}\n\t);\n\tif (credentials && !credentials.applicationId) {\n\t\tchecks.push({\n\t\t\tname: 'application',\n\t\t\tstatus: 'warn',\n\t\t\tmessage: 'The Discord application id is not set',\n\t\t\tfix: 'Set DISCORD_APPLICATION_ID for `stars commands`.'\n\t\t});\n\t}\n\n\tif (config.dev.url) {\n\t\tconst url = new URL(config.dev.url);\n\t\tconst port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));\n\t\tconst free = await (options.isPortFree ?? isPortFree)(port, url.hostname);\n\t\tchecks.push(\n\t\t\tfree\n\t\t\t\t? { name: 'port', status: 'ok', message: `${config.dev.url} is free` }\n\t\t\t\t: {\n\t\t\t\t\t\tname: 'port',\n\t\t\t\t\t\tstatus: 'warn',\n\t\t\t\t\t\tmessage: `Something already listens on ${config.dev.url}`,\n\t\t\t\t\t\tfix: 'Stop it, or set another HTTP_PORT, unless it is this bot running.'\n\t\t\t\t\t}\n\t\t);\n\t}\n\n\tconst { tunnel } = config.dev;\n\tif (tunnel.mode === 'off') {\n\t\tchecks.push({\n\t\t\tname: 'tunnel',\n\t\t\tstatus: 'info',\n\t\t\tmessage: 'No tunnel: Discord cannot reach a bot on localhost',\n\t\t\tfix: \"Set `dev.tunnel: true` for a cloudflared quick tunnel (`{ provider: 'ngrok' }` for ngrok), or press t in `stars dev`.\"\n\t\t});\n\t} else if (tunnel.mode === 'quick' && tunnel.provider === 'ngrok') {\n\t\tif (findInstalledVersion(config.root, NGROK_PACKAGE) === null) {\n\t\t\tchecks.push({ name: 'tunnel', status: 'warn', message: `${NGROK_PACKAGE} is not installed`, fix: NGROK_INSTALL_HINT });\n\t\t} else if (!values[NGROK_AUTHTOKEN_VARIABLE]) {\n\t\t\tchecks.push({\n\t\t\t\tname: 'tunnel',\n\t\t\t\tstatus: 'warn',\n\t\t\t\tmessage: `${NGROK_AUTHTOKEN_VARIABLE} is not set`,\n\t\t\t\tfix: `Set ${NGROK_AUTHTOKEN_VARIABLE} in the environment or in the project .env file.`\n\t\t\t});\n\t\t} else {\n\t\t\tchecks.push({ name: 'tunnel', status: 'ok', message: describeQuickTunnel(tunnel) });\n\t\t}\n\t} else if (tunnel.mode === 'quick') {\n\t\tchecks.push(\n\t\t\tfindOnPath('cloudflared', env)\n\t\t\t\t? { name: 'tunnel', status: 'ok', message: describeQuickTunnel(tunnel) }\n\t\t\t\t: { name: 'tunnel', status: 'info', message: 'cloudflared is not on the PATH: it is downloaded the first time the tunnel opens' }\n\t\t);\n\t} else {\n\t\tchecks.push({ name: 'tunnel', status: 'ok', message: tunnel.url });\n\t}\n\n\ttry {\n\t\tconst prepared = await prepareProject(config, hooks, true);\n\t\tconst stale = [\n\t\t\t...prepared.tsconfigs.filter((file) => file.status === 'outdated').map((file) => show(file.path)),\n\t\t\tprepared.status === 'outdated' ? show(prepared.dts) : null,\n\t\t\tprepared.modules?.status === 'outdated' ? show(prepared.modules.path) : null\n\t\t].filter((path) => path !== null);\n\t\tchecks.push(\n\t\t\tstale.length === 0\n\t\t\t\t? { name: 'prepare', status: 'ok', message: 'The generated files are up to date' }\n\t\t\t\t: { name: 'prepare', status: 'warn', message: `Out of date: ${stale.join(', ')}`, fix: 'Run `stars prepare`.' }\n\t\t);\n\t\tconst legacy = await findLegacyRootTsconfig(config);\n\t\tif (legacy) checks.push({ name: 'tsconfig', status: 'warn', message: legacy.message, ...(legacy.fix ? { fix: legacy.fix } : {}) });\n\t} catch (error) {\n\t\tchecks.push({ name: 'prepare', status: 'error', message: error instanceof Error ? error.message : String(error) });\n\t}\n\n\tif (options.online && credentials) checks.push(await checkApplication(credentials.token, config, options.fetch ?? fetch));\n\treturn checks;\n}\n\n/** Asks Discord who the token belongs to and where it sends interactions: the one thing only Discord knows. */\nasync function checkApplication(token: string, config: ResolvedStarsConfig, request: typeof fetch): Promise<Check> {\n\ttry {\n\t\tconst response = await request('https://discord.com/api/v10/applications/@me', {\n\t\t\theaders: { authorization: `Bot ${token}` },\n\t\t\tsignal: AbortSignal.timeout(10_000)\n\t\t});\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tname: 'discord',\n\t\t\t\tstatus: 'error',\n\t\t\t\tmessage: `Discord answered ${response.status}`,\n\t\t\t\tfix: response.status === 401 ? 'Check DISCORD_TOKEN.' : 'Try again later.'\n\t\t\t};\n\t\t}\n\n\t\tconst application = (await response.json()) as { name?: string; interactions_endpoint_url?: string | null };\n\t\tconst endpoint = application.interactions_endpoint_url;\n\t\tconst name = application.name ?? 'the application';\n\t\tif (!endpoint) {\n\t\t\treturn {\n\t\t\t\tname: 'discord',\n\t\t\t\tstatus: 'warn',\n\t\t\t\tmessage: `${name} has no interactions endpoint URL`,\n\t\t\t\tfix: 'Set `dev.tunnel.updateEndpoint: true`, or set it in the Discord developer portal.'\n\t\t\t};\n\t\t}\n\n\t\tconst tunnel = config.dev.tunnel;\n\t\t// The whole endpoint: the same origin with another path, or a host that merely starts the same, is elsewhere.\n\t\tconst expected = tunnel.mode === 'url' ? endpointUrl(tunnel.url, tunnel.path) : null;\n\t\tif (expected !== null && normalizeUrl(endpoint) !== normalizeUrl(expected)) {\n\t\t\treturn {\n\t\t\t\tname: 'discord',\n\t\t\t\tstatus: 'warn',\n\t\t\t\tmessage: `${name} sends interactions to ${endpoint}, not to ${expected}`,\n\t\t\t\tfix: 'Set `dev.tunnel.updateEndpoint: true`, or update it in the Discord developer portal.'\n\t\t\t};\n\t\t}\n\n\t\treturn { name: 'discord', status: 'ok', message: `${name} sends interactions to ${endpoint}` };\n\t} catch (error) {\n\t\treturn { name: 'discord', status: 'warn', message: `Could not reach Discord: ${error instanceof Error ? error.message : String(error)}` };\n\t}\n}\n\n/** A URL without the difference a trailing slash makes, or the text itself when it is not one. */\nfunction normalizeUrl(url: string): string {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn `${parsed.origin}${parsed.pathname.replace(/\\/+$/, '')}${parsed.search}`;\n\t} catch {\n\t\treturn url;\n\t}\n}\n\nfunction readEnginesNode(root: string): string | null {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')) as { engines?: { node?: unknown } };\n\t\treturn typeof parsed.engines?.node === 'string' ? parsed.engines.node : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction definedOnly(env: NodeJS.ProcessEnv): Record<string, string> {\n\treturn Object.fromEntries(Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined));\n}\n\nfunction findOnPath(binary: string, env: NodeJS.ProcessEnv): boolean {\n\tconst extensions = process.platform === 'win32' ? ['.exe', '.cmd', ''] : [''];\n\treturn (env.PATH ?? '')\n\t\t.split(delimiter)\n\t\t.filter(Boolean)\n\t\t.some((directory) => extensions.some((extension) => existsSync(join(directory, `${binary}${extension}`))));\n}\n\n/** Whether nothing listens on `host:port`: the port is bound for an instant and released. */\nfunction isPortFree(port: number, host: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst server = createServer();\n\t\tserver.once('error', () => resolve(false));\n\t\tserver.listen({ port, host, exclusive: true }, () => server.close(() => resolve(true)));\n\t});\n}\n\nconst SYMBOLS: Record<CheckStatus, string> = { ok: '✔', info: 'ℹ', warn: '⚠', error: '✖' };\n\nexport function formatChecks(checks: readonly Check[], useColor: boolean): string {\n\tconst colors = createColors({ useColor });\n\tconst paint = { ok: colors.green, info: colors.cyan, warn: colors.yellow, error: colors.red };\n\tconst width = Math.max(...checks.map((check) => check.name.length));\n\tconst lines = checks.flatMap((check) => [\n\t\t` ${paint[check.status](SYMBOLS[check.status])} ${colors.dim(check.name.padEnd(width))} ${check.message}`,\n\t\t...(check.fix ? [` ${' '.repeat(width)} ${colors.dim(`→ ${check.fix}`)}`] : [])\n\t]);\n\tconst count = (status: CheckStatus) => checks.filter((check) => check.status === status).length;\n\tconst summary = count('error') > 0 ? colors.red(`${count('error')} error(s)`) : colors.green('no errors');\n\treturn [\n\t\tcolors.bold(`stars doctor ${colors.dim(`v${readOwnPackageJson().version}`)}`),\n\t\t...lines,\n\t\t'',\n\t\t` ${summary}, ${count('warn')} warning(s)`\n\t].join('\\n');\n}\n\nexport async function runDoctor(options: DoctorOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst project = await loadProject({ cwd: resolveCwd(options), configFile: options.config });\n\t// What `stars dev` would run with: `env:options` and varlock may change the port and the credentials.\n\tconst config = withProjectEnv(await applyEnvOptions(project.config, project.hooks));\n\tconst checks = await collectChecks(config, project.hooks, options);\n\tconst errors = checks.filter((check) => check.status === 'error').length;\n\n\tstdout.write(`${options.json ? JSON.stringify({ ok: errors === 0, checks }, null, 2) : formatChecks(checks, shouldUseColor())}\\n`);\n\tif (errors > 0) throw cliDiagnostics.DOCTOR_FAILED({ count: errors });\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'doctor',\n\t\tdescription: 'Check that the project is ready for `stars dev`: runtime, framework, credentials, endpoint, generated files'\n\t},\n\targs: {\n\t\t...projectArgs,\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\tonline: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Also ask Discord whether the token works and where the application sends interactions',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tawait runDoctor({ config: args.config, cwd: args.cwd, json: args.json, online: args.online });\n\t}\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"doctor-BYRkkc5l.js","names":[],"sources":["../src/commands/doctor.ts"],"sourcesContent":["import { displayPath, type ResolvedStarsConfig } from '@wolfstar/schema';\nimport { defineCommand } from 'citty';\nimport { createColors } from 'colorette';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createServer } from 'node:net';\nimport { delimiter, join } from 'node:path';\nimport { BRIDGE_FRAMEWORK_VERSION, isAtLeast } from '../dev/bridge.js';\nimport { endpointUrl, describeQuickTunnel, readDiscordCredentials } from '../dev/tunnel.js';\nimport { NGROK_AUTHTOKEN_VARIABLE, NGROK_INSTALL_HINT, NGROK_PACKAGE } from '../dev/tunnel-providers.js';\nimport { projectArgs, resolveCwd, type ProjectArgs } from '../utils/args.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport { loadProject, withProjectEnv, applyEnvOptions, type StarsHookable } from '../utils/hooks.js';\nimport { modulesPreloadWarning } from '../utils/modules.js';\nimport { shouldUseColor } from '../utils/output-mode.js';\nimport { findLegacyRootTsconfig } from '../utils/tsconfig.js';\nimport { readProjectEnv } from '../utils/project-env.js';\nimport { findInstalledVersion } from '../utils/project.js';\nimport { readOwnPackageJson } from '../utils/version.js';\nimport { prepareProject } from './_shared.js';\n\nexport type CheckStatus = 'ok' | 'info' | 'warn' | 'error';\n\nexport interface Check {\n\tname: string;\n\tstatus: CheckStatus;\n\tmessage: string;\n\t/** What to do about it. */\n\tfix?: string;\n}\n\nexport interface DoctorOptions extends ProjectArgs {\n\tjson?: boolean;\n\t/** Also asks Discord whether the token works and where the application's interactions go. */\n\tonline?: boolean;\n\tstdout?: NodeJS.WritableStream;\n\t/** Overrides for tests. */\n\tenv?: NodeJS.ProcessEnv;\n\tnodeVersion?: string;\n\tfetch?: typeof fetch;\n\tisPortFree?: (port: number, host: string) => Promise<boolean>;\n}\n\n/** The Node.js versions `@wolfstar/cli` runs on (`engines.node` of its `package.json`). */\nconst MINIMUM_NODE = 22;\n\n/**\n * Checks what a project needs before `stars dev` can do its job: the runtime, the framework, the credentials, the\n * interactions endpoint and the generated files. Each check says what is wrong and what to do about it; nothing is\n * changed.\n */\nexport async function collectChecks(config: ResolvedStarsConfig, hooks: StarsHookable, options: DoctorOptions = {}): Promise<Check[]> {\n\tconst env = options.env ?? process.env;\n\tconst checks: Check[] = [];\n\tconst show = (path: string) => displayPath(config.root, path);\n\n\tconst node = options.nodeVersion ?? process.version;\n\tconst major = Number.parseInt(node.replace(/^v/, ''), 10);\n\tchecks.push(\n\t\tmajor >= MINIMUM_NODE\n\t\t\t? { name: 'node', status: 'ok', message: `Node.js ${node}` }\n\t\t\t: { name: 'node', status: 'error', message: `Node.js ${node} is too old`, fix: `Use Node.js ${MINIMUM_NODE} or later.` }\n\t);\n\tconst wanted = readEnginesNode(config.root);\n\tconst minimum = wanted === null ? null : /^\\s*>=\\s*(\\d+)/.exec(wanted);\n\tif (minimum && major < Number(minimum[1])) {\n\t\tchecks.push({\n\t\t\tname: 'engines',\n\t\t\tstatus: 'warn',\n\t\t\tmessage: `The project asks for Node.js ${wanted}, this is ${node}`,\n\t\t\tfix: `Use Node.js ${wanted}.`\n\t\t});\n\t}\n\n\tchecks.push({ name: 'config', status: 'ok', message: config.configFile ? show(config.configFile) : 'no stars.config, running on defaults' });\n\tfor (const warning of [...config.warnings, modulesPreloadWarning(config)]) {\n\t\tif (warning) checks.push({ name: 'config', status: 'warn', message: warning.message, ...(warning.fix ? { fix: warning.fix } : {}) });\n\t}\n\n\tconst framework = findInstalledVersion(config.root, '@wolfstar/http-framework');\n\tif (framework === null) {\n\t\tchecks.push({\n\t\t\tname: 'framework',\n\t\t\tstatus: 'error',\n\t\t\tmessage: '@wolfstar/http-framework is not installed',\n\t\t\tfix: 'Install @wolfstar/http-framework in the project.'\n\t\t});\n\t} else if (isAtLeast(framework, BRIDGE_FRAMEWORK_VERSION)) {\n\t\tchecks.push({ name: 'framework', status: 'ok', message: `@wolfstar/http-framework v${framework}` });\n\t} else {\n\t\tchecks.push({\n\t\t\tname: 'framework',\n\t\t\tstatus: 'warn',\n\t\t\tmessage: `@wolfstar/http-framework v${framework} cannot report to the dev UI`,\n\t\t\tfix: `Update to ${BRIDGE_FRAMEWORK_VERSION} or later for the hmr, commands, interactions and http channels.`\n\t\t});\n\t}\n\n\tchecks.push({ name: 'entry', status: 'ok', message: `${show(config.entry)} (${config.build.tool})` });\n\tif (config.build.tool !== 'none' && !existsSync(config.build.output)) {\n\t\tchecks.push({\n\t\t\tname: 'build',\n\t\t\tstatus: 'info',\n\t\t\tmessage: `${show(config.build.output)} is not built yet`,\n\t\t\tfix: 'Run `stars build` (or `stars dev`).'\n\t\t});\n\t}\n\n\tconst values = { ...readProjectEnv(config), ...definedOnly(env), ...config.dev.env };\n\tconst credentials = readDiscordCredentials(config, env);\n\tchecks.push(\n\t\tcredentials\n\t\t\t? { name: 'token', status: 'ok', message: 'DISCORD_TOKEN is set' }\n\t\t\t: {\n\t\t\t\t\tname: 'token',\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\tmessage: 'DISCORD_TOKEN is not set',\n\t\t\t\t\tfix: 'Set DISCORD_TOKEN in the environment or in the project .env file.'\n\t\t\t\t}\n\t);\n\tchecks.push(\n\t\tvalues.DISCORD_PUBLIC_KEY\n\t\t\t? { name: 'public key', status: 'ok', message: 'DISCORD_PUBLIC_KEY is set' }\n\t\t\t: {\n\t\t\t\t\tname: 'public key',\n\t\t\t\t\tstatus: 'warn',\n\t\t\t\t\tmessage: 'DISCORD_PUBLIC_KEY is not set',\n\t\t\t\t\tfix: 'Set it in the project .env file, unless the bot passes `discordPublicKey` to its Client.'\n\t\t\t\t}\n\t);\n\tif (credentials && !credentials.applicationId) {\n\t\tchecks.push({\n\t\t\tname: 'application',\n\t\t\tstatus: 'warn',\n\t\t\tmessage: 'The Discord application id is not set',\n\t\t\tfix: 'Set DISCORD_APPLICATION_ID for `stars commands`.'\n\t\t});\n\t}\n\n\tif (config.dev.url) {\n\t\tconst url = new URL(config.dev.url);\n\t\tconst port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));\n\t\tconst free = await (options.isPortFree ?? isPortFree)(port, url.hostname);\n\t\tchecks.push(\n\t\t\tfree\n\t\t\t\t? { name: 'port', status: 'ok', message: `${config.dev.url} is free` }\n\t\t\t\t: {\n\t\t\t\t\t\tname: 'port',\n\t\t\t\t\t\tstatus: 'warn',\n\t\t\t\t\t\tmessage: `Something already listens on ${config.dev.url}`,\n\t\t\t\t\t\tfix: 'Stop it, or set another HTTP_PORT, unless it is this bot running.'\n\t\t\t\t\t}\n\t\t);\n\t}\n\n\tconst { tunnel } = config.dev;\n\tif (tunnel.mode === 'off') {\n\t\tchecks.push({\n\t\t\tname: 'tunnel',\n\t\t\tstatus: 'info',\n\t\t\tmessage: 'No tunnel: Discord cannot reach a bot on localhost',\n\t\t\tfix: \"Set `dev.tunnel: true` for a cloudflared quick tunnel (`{ provider: 'ngrok' }` for ngrok), or press t in `stars dev`.\"\n\t\t});\n\t} else if (tunnel.mode === 'quick' && tunnel.provider === 'ngrok') {\n\t\tif (findInstalledVersion(config.root, NGROK_PACKAGE) === null) {\n\t\t\tchecks.push({ name: 'tunnel', status: 'warn', message: `${NGROK_PACKAGE} is not installed`, fix: NGROK_INSTALL_HINT });\n\t\t} else if (!values[NGROK_AUTHTOKEN_VARIABLE]) {\n\t\t\tchecks.push({\n\t\t\t\tname: 'tunnel',\n\t\t\t\tstatus: 'warn',\n\t\t\t\tmessage: `${NGROK_AUTHTOKEN_VARIABLE} is not set`,\n\t\t\t\tfix: `Set ${NGROK_AUTHTOKEN_VARIABLE} in the environment or in the project .env file.`\n\t\t\t});\n\t\t} else {\n\t\t\tchecks.push({ name: 'tunnel', status: 'ok', message: describeQuickTunnel(tunnel) });\n\t\t}\n\t} else if (tunnel.mode === 'quick') {\n\t\tchecks.push(\n\t\t\tfindOnPath('cloudflared', env)\n\t\t\t\t? { name: 'tunnel', status: 'ok', message: describeQuickTunnel(tunnel) }\n\t\t\t\t: { name: 'tunnel', status: 'info', message: 'cloudflared is not on the PATH: it is downloaded the first time the tunnel opens' }\n\t\t);\n\t} else {\n\t\tchecks.push({ name: 'tunnel', status: 'ok', message: tunnel.url });\n\t}\n\n\ttry {\n\t\tconst prepared = await prepareProject(config, hooks, true);\n\t\tconst stale = [\n\t\t\t...prepared.tsconfigs.filter((file) => file.status === 'outdated').map((file) => show(file.path)),\n\t\t\tprepared.status === 'outdated' ? show(prepared.dts) : null,\n\t\t\tprepared.modules?.status === 'outdated' ? show(prepared.modules.path) : null\n\t\t].filter((path) => path !== null);\n\t\tchecks.push(\n\t\t\tstale.length === 0\n\t\t\t\t? { name: 'prepare', status: 'ok', message: 'The generated files are up to date' }\n\t\t\t\t: { name: 'prepare', status: 'warn', message: `Out of date: ${stale.join(', ')}`, fix: 'Run `stars prepare`.' }\n\t\t);\n\t\tconst legacy = await findLegacyRootTsconfig(config);\n\t\tif (legacy) checks.push({ name: 'tsconfig', status: 'warn', message: legacy.message, ...(legacy.fix ? { fix: legacy.fix } : {}) });\n\t} catch (error) {\n\t\tchecks.push({ name: 'prepare', status: 'error', message: error instanceof Error ? error.message : String(error) });\n\t}\n\n\tif (options.online && credentials) checks.push(await checkApplication(credentials.token, config, options.fetch ?? fetch));\n\treturn checks;\n}\n\n/** Asks Discord who the token belongs to and where it sends interactions: the one thing only Discord knows. */\nasync function checkApplication(token: string, config: ResolvedStarsConfig, request: typeof fetch): Promise<Check> {\n\ttry {\n\t\tconst response = await request('https://discord.com/api/v10/applications/@me', {\n\t\t\theaders: { authorization: `Bot ${token}` },\n\t\t\tsignal: AbortSignal.timeout(10_000)\n\t\t});\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tname: 'discord',\n\t\t\t\tstatus: 'error',\n\t\t\t\tmessage: `Discord answered ${response.status}`,\n\t\t\t\tfix: response.status === 401 ? 'Check DISCORD_TOKEN.' : 'Try again later.'\n\t\t\t};\n\t\t}\n\n\t\tconst application = (await response.json()) as { name?: string; interactions_endpoint_url?: string | null };\n\t\tconst endpoint = application.interactions_endpoint_url;\n\t\tconst name = application.name ?? 'the application';\n\t\tif (!endpoint) {\n\t\t\treturn {\n\t\t\t\tname: 'discord',\n\t\t\t\tstatus: 'warn',\n\t\t\t\tmessage: `${name} has no interactions endpoint URL`,\n\t\t\t\tfix: 'Set `dev.tunnel.updateEndpoint: true`, or set it in the Discord developer portal.'\n\t\t\t};\n\t\t}\n\n\t\tconst tunnel = config.dev.tunnel;\n\t\t// The whole endpoint: the same origin with another path, or a host that merely starts the same, is elsewhere.\n\t\tconst expected = tunnel.mode === 'url' ? endpointUrl(tunnel.url, tunnel.path) : null;\n\t\tif (expected !== null && normalizeUrl(endpoint) !== normalizeUrl(expected)) {\n\t\t\treturn {\n\t\t\t\tname: 'discord',\n\t\t\t\tstatus: 'warn',\n\t\t\t\tmessage: `${name} sends interactions to ${endpoint}, not to ${expected}`,\n\t\t\t\tfix: 'Set `dev.tunnel.updateEndpoint: true`, or update it in the Discord developer portal.'\n\t\t\t};\n\t\t}\n\n\t\treturn { name: 'discord', status: 'ok', message: `${name} sends interactions to ${endpoint}` };\n\t} catch (error) {\n\t\treturn { name: 'discord', status: 'warn', message: `Could not reach Discord: ${error instanceof Error ? error.message : String(error)}` };\n\t}\n}\n\n/** A URL without the difference a trailing slash makes, or the text itself when it is not one. */\nfunction normalizeUrl(url: string): string {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn `${parsed.origin}${parsed.pathname.replace(/\\/+$/, '')}${parsed.search}`;\n\t} catch {\n\t\treturn url;\n\t}\n}\n\nfunction readEnginesNode(root: string): string | null {\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')) as { engines?: { node?: unknown } };\n\t\treturn typeof parsed.engines?.node === 'string' ? parsed.engines.node : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction definedOnly(env: NodeJS.ProcessEnv): Record<string, string> {\n\treturn Object.fromEntries(Object.entries(env).filter((entry): entry is [string, string] => entry[1] !== undefined));\n}\n\nfunction findOnPath(binary: string, env: NodeJS.ProcessEnv): boolean {\n\tconst extensions = process.platform === 'win32' ? ['.exe', '.cmd', ''] : [''];\n\treturn (env.PATH ?? '')\n\t\t.split(delimiter)\n\t\t.filter(Boolean)\n\t\t.some((directory) => extensions.some((extension) => existsSync(join(directory, `${binary}${extension}`))));\n}\n\n/** Whether nothing listens on `host:port`: the port is bound for an instant and released. */\nfunction isPortFree(port: number, host: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst server = createServer();\n\t\tserver.once('error', () => resolve(false));\n\t\tserver.listen({ port, host, exclusive: true }, () => server.close(() => resolve(true)));\n\t});\n}\n\nconst SYMBOLS: Record<CheckStatus, string> = { ok: '✔', info: 'ℹ', warn: '⚠', error: '✖' };\n\nexport function formatChecks(checks: readonly Check[], useColor: boolean): string {\n\tconst colors = createColors({ useColor });\n\tconst paint = { ok: colors.green, info: colors.cyan, warn: colors.yellow, error: colors.red };\n\tconst width = Math.max(...checks.map((check) => check.name.length));\n\tconst lines = checks.flatMap((check) => [\n\t\t` ${paint[check.status](SYMBOLS[check.status])} ${colors.dim(check.name.padEnd(width))} ${check.message}`,\n\t\t...(check.fix ? [` ${' '.repeat(width)} ${colors.dim(`→ ${check.fix}`)}`] : [])\n\t]);\n\tconst count = (status: CheckStatus) => checks.filter((check) => check.status === status).length;\n\tconst summary = count('error') > 0 ? colors.red(`${count('error')} error(s)`) : colors.green('no errors');\n\treturn [\n\t\tcolors.bold(`stars doctor ${colors.dim(`v${readOwnPackageJson().version}`)}`),\n\t\t...lines,\n\t\t'',\n\t\t` ${summary}, ${count('warn')} warning(s)`\n\t].join('\\n');\n}\n\nexport async function runDoctor(options: DoctorOptions): Promise<void> {\n\tconst stdout = options.stdout ?? process.stdout;\n\tconst project = await loadProject({ cwd: resolveCwd(options), configFile: options.config });\n\t// What `stars dev` would run with: `env:options` and varlock may change the port and the credentials.\n\tconst config = withProjectEnv(await applyEnvOptions(project.config, project.hooks));\n\tconst checks = await collectChecks(config, project.hooks, options);\n\tconst errors = checks.filter((check) => check.status === 'error').length;\n\n\tstdout.write(`${options.json ? JSON.stringify({ ok: errors === 0, checks }, null, 2) : formatChecks(checks, shouldUseColor())}\\n`);\n\tif (errors > 0) throw cliDiagnostics.DOCTOR_FAILED({ count: errors });\n}\n\nexport default defineCommand({\n\tmeta: {\n\t\tname: 'doctor',\n\t\tdescription: 'Check that the project is ready for `stars dev`: runtime, framework, credentials, endpoint, generated files'\n\t},\n\targs: {\n\t\t...projectArgs,\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\tonline: {\n\t\t\ttype: 'boolean',\n\t\t\tdescription: 'Also ask Discord whether the token works and where the application sends interactions',\n\t\t\tdefault: false\n\t\t}\n\t},\n\tasync run({ args }) {\n\t\tawait runDoctor({ config: args.config, cwd: args.cwd, json: args.json, online: args.online });\n\t}\n});\n"],"mappings":"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"}
@@ -215,7 +215,8 @@ async function prepareModulesPreload(config, check = false) {
215
215
  * when varlock cannot be run, rather than values the bot never sees.
216
216
  */
217
217
  function readProjectEnv(config) {
218
- const { path, env = "development", loader } = config.env.options;
218
+ const { path, env = "development" } = config.env.options;
219
+ const { loader } = config.env;
219
220
  if (loader !== "varlock") return readProjectEnvFiles(config.root, env, { path });
220
221
  const binary = resolveBinary(config.root, "varlock", "varlock");
221
222
  if (binary === null) return {};
@@ -339,7 +340,7 @@ async function applyEnvOptions(config, hooks) {
339
340
  */
340
341
  function withProjectEnv(config) {
341
342
  const source = sources.get(config);
342
- if (source === void 0 || !config.env.enabled || config.env.options.loader !== "varlock") return config;
343
+ if (source === void 0 || !config.env.enabled || config.env.loader !== "varlock") return config;
343
344
  return reresolve(config, source, { projectEnv: readProjectEnv(config) });
344
345
  }
345
346
  function reresolve(previous, source, change) {
@@ -362,4 +363,4 @@ function reresolve(previous, source, change) {
362
363
 
363
364
  //#endregion
364
365
  export { readProjectVariable as a, envImportArgs as c, projectArgs as d, resolveCwd as f, readProjectEnv as i, moduleImportArgs as l, loadProject as n, modulesPreloadWarning as o, withProjectEnv as r, prepareModulesPreload as s, applyEnvOptions as t, collectFlag as u };
365
- //# sourceMappingURL=hooks-DqlScRZA.js.map
366
+ //# sourceMappingURL=hooks-CzV2YRoJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-CzV2YRoJ.js","names":["resolveModule"],"sources":["../src/utils/args.ts","../src/utils/env-import.ts","../src/utils/modules.ts","../src/utils/project-env.ts","../src/utils/hooks.ts"],"sourcesContent":["import type { ArgsDef } from 'citty';\nimport { resolve } from 'node:path';\n\n/**\n * Arguments shared by every command that reads the project configuration.\n */\nexport const projectArgs = {\n\tconfig: {\n\t\ttype: 'string',\n\t\talias: 'c',\n\t\tdescription: 'Path to the configuration file (defaults to stars.config.* in the working directory)'\n\t},\n\tcwd: {\n\t\ttype: 'string',\n\t\tdescription: 'Working directory to run from (defaults to the current directory)'\n\t}\n} as const satisfies ArgsDef;\n\nexport interface ProjectArgs {\n\tconfig?: string;\n\tcwd?: string;\n}\n\n/**\n * Every value of a flag given more than once (`--channel a --channel=b`). citty keeps only the last one of a\n * repeated string flag, so a repeatable flag reads the raw arguments.\n */\nexport function collectFlag(rawArgs: readonly string[], name: string): string[] {\n\tconst values: string[] = [];\n\tfor (const [index, argument] of rawArgs.entries()) {\n\t\tif (argument === '--') break;\n\t\tif (argument.startsWith(`--${name}=`)) values.push(argument.slice(name.length + 3));\n\t\telse if (argument === `--${name}`) {\n\t\t\tconst next = rawArgs[index + 1];\n\t\t\tif (next !== undefined && !next.startsWith('-')) values.push(next);\n\t\t}\n\t}\n\n\treturn values;\n}\n\nexport function resolveCwd(args: ProjectArgs): string {\n\treturn args.cwd ? resolve(process.cwd(), args.cwd) : process.cwd();\n}\n","import type { ResolvedStarsConfig } from '@wolfstar/schema';\nimport { envModuleSource, modulesModuleSource } from '@wolfstar/vite-server/internal';\nimport { resolveSync } from 'mlly';\nimport { isAbsolute } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { cliDiagnostics } from './diagnostics.js';\nimport { resolveFromProject } from './project.js';\n\n/** Build tools whose output never passes through the entry transform that registers `env`. */\nexport const UNTRANSFORMED_TOOLS = new Set<string>(['none', 'tsc']);\n\n/**\n * `node` arguments that register `env` before the entry runs, for the build tools the entry transform cannot reach.\n * The preload is a `data:` URL, so `.stars/` gets no generated runtime file; a `data:` URL cannot resolve bare\n * specifiers, so `@wolfstar/env-utilities` is resolved from the project first. When it cannot be resolved nothing is\n * preloaded, and the bot's own import of it reports the missing install.\n */\nexport function envImportArgs(config: ResolvedStarsConfig): string[] {\n\tif (!config.env.enabled || !UNTRANSFORMED_TOOLS.has(config.build.tool)) return [];\n\n\tconst resolved = resolveFromProject(config.root, '@wolfstar/env-utilities');\n\tif (resolved === null) return [];\n\n\tconst source = envModuleSource(config.env.options, pathToFileURL(resolved).href);\n\treturn ['--import', `data:text/javascript,${encodeURIComponent(source)}`];\n}\n\n/**\n * `node` arguments that register the runtime plugins of the installed modules (`ctx.addPlugin`) before the entry\n * runs, for the build tools the entry transform cannot reach. `@wolfstar/http-framework` and every plugin are resolved\n * from the project first, with the `import` condition (both are ESM), because a `data:` URL cannot resolve bare\n * specifiers; the framework is the bot's own copy, so `Client.use` reaches the `Client` the bot constructs. When the\n * framework is not installed nothing is preloaded, and the bot's own import of it reports the missing install.\n */\nexport function moduleImportArgs(config: ResolvedStarsConfig): string[] {\n\tconst plugins = config.runtime?.plugins ?? [];\n\tif (plugins.length === 0 || !UNTRANSFORMED_TOOLS.has(config.build.tool)) return [];\n\n\tconst url = pathToFileURL(`${config.root}/package.json`).href;\n\tlet framework: string;\n\ttry {\n\t\tframework = resolveSync('@wolfstar/http-framework', { url });\n\t} catch {\n\t\treturn [];\n\t}\n\n\tconst source = modulesModuleSource(plugins, {\n\t\tframework,\n\t\tresolve: (from) => {\n\t\t\tif (from.startsWith('file:')) return from;\n\t\t\tif (isAbsolute(from)) return pathToFileURL(from).href;\n\t\t\ttry {\n\t\t\t\treturn resolveSync(from, { url });\n\t\t\t} catch {\n\t\t\t\tthrow cliDiagnostics.DEPENDENCY_MISSING({\n\t\t\t\t\tname: from,\n\t\t\t\t\troot: config.root,\n\t\t\t\t\thint: 'Install the package of the module that registers this plugin.'\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\treturn ['--import', `data:text/javascript,${encodeURIComponent(source)}`];\n}\n","import { ModuleError, setupModules } from '@wolfstar/kit';\nimport { assertModuleOptionsClaimed, type ResolvedStarsConfig } from '@wolfstar/schema';\nimport { envModuleSource, modulesModuleSource } from '@wolfstar/vite-server/internal';\nimport { resolve as resolveModule } from 'mlly';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport type { Diagnostic } from 'nostics';\nimport { isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { cliDiagnostics } from './diagnostics.js';\nimport { UNTRANSFORMED_TOOLS } from './env-import.js';\nimport type { StarsHookable } from './hooks.js';\nimport { findInstalledVersion } from './project.js';\nimport { readOwnPackageJson } from './version.js';\n\n/**\n * Where a module specifier is imported from: a relative or absolute path is a file of the project, a `file:` URL is\n * itself, anything else is a package resolved from the project root — with `mlly`, honouring the `import` condition,\n * because installed modules are ESM-only packages a CommonJS `require.resolve` cannot see.\n */\nasync function resolveSpecifier(root: string, specifier: string): Promise<string> {\n\tif (specifier.startsWith('file:')) return specifier;\n\tif (specifier.startsWith('.') || isAbsolute(specifier)) return pathToFileURL(resolve(root, specifier)).href;\n\treturn resolveModule(specifier, { url: pathToFileURL(`${root}/package.json`).href });\n}\n\n/**\n * Runs `setup` of every module in `modules` (see `@wolfstar/kit`) and returns the configuration carrying what they\n * contributed: `runtime`, and `imports.presets` extended by `ctx.addImports`. Without modules the configuration is\n * returned as it is.\n *\n * Afterwards, a top-level `stars.config` key that is not built in and that no installed module claimed with\n * `meta.configKey` is an `UNKNOWN_OPTION` error: the schema keeps such keys aside (`moduleOptions`) because only the\n * modules know which ones are theirs. That check runs without modules too, where every extra key is unknown.\n */\nexport async function installModules(config: ResolvedStarsConfig, hooks: StarsHookable): Promise<ResolvedStarsConfig> {\n\tif (config.modules.length === 0) {\n\t\tassertModuleOptionsClaimed(config, []);\n\t\treturn config;\n\t}\n\n\ttry {\n\t\tconst runtime = await setupModules({\n\t\t\tconfig,\n\t\t\thooks,\n\t\t\tversions: { framework: findInstalledVersion(config.root, '@wolfstar/http-framework'), stars: readOwnPackageJson().version },\n\t\t\tload: async (specifier) => import(await resolveSpecifier(config.root, specifier))\n\t\t});\n\n\t\tassertModuleOptionsClaimed(config, runtime.configKeys);\n\n\t\tconst presets = [...config.imports.presets, ...runtime.imports.filter((preset) => !config.imports.presets.includes(preset))];\n\t\treturn { ...config, runtime, imports: { ...config.imports, presets } };\n\t} catch (error) {\n\t\tif (error instanceof ModuleError) {\n\t\t\tthrow cliDiagnostics.MODULE_FAILED({ module: error.moduleName, reason: error.code, message: error.message, cause: error });\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n\n/**\n * The warning for a project whose runtime plugins reach production only through a `node --import` preload, `null`\n * otherwise. It is not one of `config.warnings`: `stars dev` preloads the plugins itself, so there it would be noise\n * on every start. `stars build` and `stars prepare` report it.\n */\nexport function modulesPreloadWarning(config: ResolvedStarsConfig): Diagnostic | null {\n\tif (!needsPreload(config)) return null;\n\treturn cliDiagnostics.MODULES_PRELOAD_REQUIRED({\n\t\ttool: config.build.tool,\n\t\tplugins: [...new Set(config.runtime.plugins.map((plugin) => plugin.module))].join(', ')\n\t});\n}\n\n/** A build tool the entry transform cannot reach, with runtime plugins to register: they need a `node --import` preload. */\nfunction needsPreload(config: ResolvedStarsConfig): boolean {\n\treturn UNTRANSFORMED_TOOLS.has(config.build.tool) && (config.runtime?.plugins.length ?? 0) > 0;\n}\n\nexport interface ModulesPreloadResult {\n\t/** Absolute path of the preload file. */\n\tpath: string;\n\tstatus: 'written' | 'up-to-date' | 'outdated';\n}\n\n/**\n * Writes `.stars/modules.mjs`, the production counterpart of the preload `stars dev` passes to `node`: `node --import\n * ./.stars/modules.mjs <entry>` registers the runtime plugins of the installed modules for the build tools whose entry\n * cannot be transformed (`tsc`, `none`). Its specifiers are bare, so they resolve from the project's `node_modules`\n * like the bot's own imports. `null` when there is nothing to register.\n *\n * With `env` enabled it is preceded by `.stars/env.mjs`, a separate module imported first: imports of one module are\n * all evaluated before its body runs, so the environment could not be loaded ahead of the plugins from the same file,\n * and a plugin reading `process.env` while it is imported would see nothing.\n */\nexport async function prepareModulesPreload(config: ResolvedStarsConfig, check = false): Promise<ModulesPreloadResult | null> {\n\tif (!needsPreload(config)) return null;\n\n\tconst directory = join(config.root, '.stars');\n\tconst path = join(directory, 'modules.mjs');\n\tconst withEnv = config.env?.enabled === true;\n\t// An absolute path is a `file:` URL in the import, like in `moduleImportArgs`: Node rejects a Windows path there.\n\tconst plugins = modulesModuleSource(config.runtime.plugins, { resolve: (from) => (isAbsolute(from) ? pathToFileURL(from).href : from) });\n\tconst files = [\n\t\t{ path, content: withEnv ? `import \"./env.mjs\";\\n${plugins}` : plugins },\n\t\t...(withEnv ? [{ path: join(directory, 'env.mjs'), content: envModuleSource(config.env.options) }] : [])\n\t];\n\n\tif (check) {\n\t\tconst current = await Promise.all(files.map(async (file) => (await readFile(file.path, 'utf-8').catch(() => null)) === file.content));\n\t\treturn { path, status: current.every(Boolean) ? 'up-to-date' : 'outdated' };\n\t}\n\n\tawait mkdir(directory, { recursive: true });\n\tawait Promise.all(files.map((file) => writeFile(file.path, file.content)));\n\treturn { path, status: 'written' };\n}\n","import { readProjectEnvFiles, type ResolvedStarsConfig } from '@wolfstar/schema';\nimport { execFileSync } from 'node:child_process';\nimport { resolveBinary } from './project.js';\n\n/**\n * The variables the bot will load, for the CLI's own lookups (Discord credentials for the tunnel). With the default\n * loader these are the project's env files, read from `env.path`/`env.env`. With `loader: 'varlock'` they are what\n * `varlock load` resolves from the project's `.env.schema` — a dotenv file may be stale or absent there — and nothing\n * when varlock cannot be run, rather than values the bot never sees.\n */\nexport function readProjectEnv(config: ResolvedStarsConfig): Record<string, string> {\n\tconst { path, env = 'development' } = config.env.options;\n\tconst { loader } = config.env;\n\tif (loader !== 'varlock') return readProjectEnvFiles(config.root, env, { path });\n\n\tconst binary = resolveBinary(config.root, 'varlock', 'varlock');\n\tif (binary === null) return {};\n\n\ttry {\n\t\t// The way the bot loads it: `@wolfstar/env-utilities` runs `varlock/auto-load` with no `--env` (it ignores\n\t\t// `env.env` with varlock), in the environment `stars dev` gives the bot.\n\t\tconst output = execFileSync(process.execPath, [binary, 'load', '--format', 'json'], {\n\t\t\tcwd: config.root,\n\t\t\t// Same order as `createSupervisor`: `NODE_ENV` is forced to development after `dev.env`, so it can't differ.\n\t\t\tenv: { ...process.env, ...config.dev.env, NODE_ENV: 'development' },\n\t\t\tencoding: 'utf8',\n\t\t\tstdio: ['ignore', 'pipe', 'ignore'],\n\t\t\ttimeout: 30_000\n\t\t});\n\t\tconst values = JSON.parse(output) as Record<string, unknown>;\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(values)\n\t\t\t\t.filter(([, value]) => value !== undefined && value !== null)\n\t\t\t\t.map(([key, value]) => [key, String(value)])\n\t\t);\n\t} catch {\n\t\treturn {};\n\t}\n}\n\nlet envFileCache: { key: string; values: Record<string, string> } | null = null;\n\n/** The variables the bot loads (see {@link readProjectEnv}), once per project and `env` options. */\nfunction readProjectEnvCached(config: ResolvedStarsConfig, fresh: boolean): Record<string, string> {\n\tconst key = JSON.stringify([config.root, config.env.options]);\n\tif (fresh || envFileCache?.key !== key) envFileCache = { key, values: readProjectEnv(config) };\n\treturn envFileCache.values;\n}\n\n/**\n * The first of `keys` that is set, looking at `dev.env`, then the environment, then the project's env files the way the\n * bot itself does once it starts. `null` when none is.\n *\n * The env files are read once per project; `fresh` reads them again, for a lookup that follows the user editing them\n * (pressing `t` after adding a token), which would otherwise keep failing until `stars dev` restarts.\n */\nexport function readProjectVariable(\n\tconfig: ResolvedStarsConfig,\n\tenv: NodeJS.ProcessEnv,\n\tkeys: readonly string[],\n\t{ fresh = false }: { fresh?: boolean } = {}\n): string | null {\n\tconst fromFiles = readProjectEnvCached(config, fresh);\n\tfor (const key of keys) {\n\t\tconst value = config.dev.env[key] ?? env[key] ?? fromFiles[key];\n\t\tif (value) return value;\n\t}\n\n\treturn null;\n}\n","import {\n\tloadConfigFile,\n\tresolveStarsConfig,\n\ttype LoadStarsConfigOptions,\n\ttype ResolveConfigOptions,\n\ttype ResolvedStarsConfig,\n\ttype StarsHookName,\n\ttype StarsHooks\n} from '@wolfstar/schema';\nimport { createHooks, type Hookable } from 'hookable';\nimport { installModules } from './modules.js';\nimport { readProjectEnv } from './project-env.js';\n\nexport type StarsHookable = Hookable<StarsHooks>;\n\n/** What each configuration loaded by {@link loadProject} was resolved from, so `env:options` can re-resolve it. */\nconst sources = new WeakMap<ResolvedStarsConfig, ResolveConfigOptions>();\n\n/** Registers the `hooks` of `stars.config` on a fresh `hookable` instance. */\nexport function createStarsHooks(config: ResolvedStarsConfig): StarsHookable {\n\tconst hooks = createHooks<StarsHooks>();\n\tfor (const [name, callbacks] of Object.entries(config.hooks) as [StarsHookName, readonly StarsHooks[StarsHookName][]][]) {\n\t\tfor (const callback of callbacks) hooks.hook(name, callback);\n\t}\n\n\treturn hooks;\n}\n\n/** Loads `stars.config`, registers its hooks and runs `config:resolved`. */\nexport async function loadProject(options: LoadStarsConfigOptions): Promise<{ config: ResolvedStarsConfig; hooks: StarsHookable }> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst loaded = await loadConfigFile({ cwd, configFile: options.configFile });\n\tconst source: ResolveConfigOptions = { cwd, configFile: loaded.configFile, config: loaded.config, env: options.env };\n\tconst resolved = resolveStarsConfig(source);\n\tconst hooks = createStarsHooks(resolved);\n\t// Modules install before `config:resolved`, so that hook already sees what they contributed.\n\tconst config = await installModules(resolved, hooks);\n\tsources.set(config, source);\n\n\tawait hooks.callHook('config:resolved', config);\n\treturn { config, hooks };\n}\n\n/**\n * Runs `env:options` on a copy of the resolved `env` options and returns a configuration carrying the result, which\n * is what the builders write into the entry. The resolved configuration itself is left untouched.\n *\n * When the hook changes the options, the configuration is resolved again with them, so everything read from the env\n * files — the default `dev.url` port above all — matches what the bot will load.\n */\nexport async function applyEnvOptions(config: ResolvedStarsConfig, hooks: StarsHookable): Promise<ResolvedStarsConfig> {\n\tif (!config.env.enabled) return config;\n\n\tconst options = { ...config.env.options };\n\tawait hooks.callHook('env:options', options, config);\n\n\tconst source = sources.get(config);\n\tif (source === undefined || JSON.stringify(options) === JSON.stringify(config.env.options)) {\n\t\treturn { ...config, env: { ...config.env, options } };\n\t}\n\n\treturn reresolve(config, source, { config: { ...source.config, env: { ...options, enabled: true } } });\n}\n\n/**\n * With `loader: 'varlock'`, resolves the configuration again with the variables `varlock load` returns, so the\n * defaults `stars.config` derives from the environment — the `dev.url` port — match what the bot loads. Every other\n * configuration is returned as-is: the schema reads its env files directly.\n */\nexport function withProjectEnv(config: ResolvedStarsConfig): ResolvedStarsConfig {\n\tconst source = sources.get(config);\n\tif (source === undefined || !config.env.enabled || config.env.loader !== 'varlock') return config;\n\treturn reresolve(config, source, { projectEnv: readProjectEnv(config) });\n}\n\nfunction reresolve(previous: ResolvedStarsConfig, source: ResolveConfigOptions, change: Partial<ResolveConfigOptions>): ResolvedStarsConfig {\n\tconst next = { ...source, ...change };\n\tconst resolved = resolveStarsConfig(next);\n\t// Modules ran once, against the first resolution: what they contributed carries over to the new one.\n\tconst config = { ...resolved, runtime: previous.runtime, imports: { ...resolved.imports, presets: previous.imports.presets } };\n\tsources.set(config, next);\n\treturn config;\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as runMain } from "./run-DlctD5rb.js";
1
+ import { t as runMain } from "./run-BVBqR6hV.js";
2
2
  import { i as shouldUseColor, n as prefersReducedMotion, r as resolveOutputMode, t as isCIEnvironment } from "./output-mode-Bp7Da8vJ.js";
3
3
  import { n as exitCodeOf, r as formatError, t as ExitCode } from "./errors-COFWKKKw.js";
4
4
  import { t as cliDiagnostics } from "./diagnostics-BKc--u_3.js";