@wolfstar/cli 2.3.0 → 2.3.1-next-20261005082549
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{bridge-BvkmlAOZ.js → bridge-BegCt_n2.js} +67 -6
- package/dist/bridge-BegCt_n2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{commands-DVC7ky6f.js → commands-Bh0K9iWX.js} +2 -2
- package/dist/{commands-DVC7ky6f.js.map → commands-Bh0K9iWX.js.map} +1 -1
- package/dist/commands-BlDTDvFO.js +3 -0
- package/dist/{commands-Clpbqq3K.js → commands-CSB-pjgs.js} +5 -5
- package/dist/{commands-Clpbqq3K.js.map → commands-CSB-pjgs.js.map} +1 -1
- package/dist/{completions-D2KYtrrv.js → completions-DMPlZ0S1.js} +2 -2
- package/dist/{completions-D2KYtrrv.js.map → completions-DMPlZ0S1.js.map} +1 -1
- package/dist/{dev-oPPVR7d_.js → dev-Owu3e3AO.js} +2 -2
- package/dist/{dev-oPPVR7d_.js.map → dev-Owu3e3AO.js.map} +1 -1
- package/dist/{doctor-DkVUwgDd.js → doctor-DEtAAknK.js} +2 -2
- package/dist/{doctor-DkVUwgDd.js.map → doctor-DEtAAknK.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/{run-DMM2YlP3.js → run-CHqwvZXf.js} +2 -2
- package/dist/{run-DMM2YlP3.js.map → run-CHqwvZXf.js.map} +1 -1
- package/package.json +1 -1
- package/dist/bridge-BvkmlAOZ.js.map +0 -1
- package/dist/commands-CIPWQtF0.js +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-oPPVR7d_.js","names":[],"sources":["../src/dev/changed-files.ts","../src/dev/typechecker.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 type { ResolvedStarsConfig, StarsTypechecker } from '@wolfstar/schema';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport type { Diagnostic } from 'nostics';\nimport { resolveTscBinary } from '../builders/tsc.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport type { LogLevel } from '../utils/log-buffer.js';\nimport { createLineSplitter } from '../utils/process-supervisor.js';\nimport { resolveBinary } from '../utils/project.js';\n\nconst WATCH_START = /Starting (incremental )?compilation/;\nconst WATCH_DONE = /Found (\\d+) errors?\\. Watching for file changes/;\nconst ONE_SHOT_DONE = /Found (\\d+) errors?/;\nconst ERROR_LINE = /error TS\\d+:/;\n\nexport type TypecheckState = 'idle' | 'checking' | 'ok' | 'failed';\n\nexport interface TypecheckerEvents {\n\tlog: [level: LogLevel, text: string];\n\tstate: [state: TypecheckState, errors: number];\n}\n\nexport interface TypecheckCommand {\n\t/** The executable, and whether it is a script `node` has to run. */\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly node: boolean;\n\t/** Whether the checker watches the project itself, or has to be re-run after every build. */\n\treadonly watch: boolean;\n}\n\n/**\n * Runs a type checker next to the bot and reports type errors on their own channel, without ever blocking builds or\n * restarts: `stars dev` keeps running whatever the build tool produced, the way Seedcord's `hmr.typecheck` and\n * Vite's `checker` plugin do.\n *\n * `tsc` and `golar` (which forwards to TypeScript) watch the project themselves; `tsz` has no watch mode, so\n * {@link Typechecker.check} re-runs it after every build instead.\n */\nexport class Typechecker extends EventEmitter<TypecheckerEvents> {\n\t#child: ChildProcess | null = null;\n\t#state: TypecheckState = 'idle';\n\t#errors = 0;\n\t#pending = false;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic get state(): TypecheckState {\n\t\treturn this.#state;\n\t}\n\n\tpublic get errors(): number {\n\t\treturn this.#errors;\n\t}\n\n\t/** Whether the checker re-runs per build instead of watching on its own. */\n\tpublic get oneShot(): boolean {\n\t\treturn this.config.dev.typecheck.enabled && !resolveTypecheckCommand(this.config).watch;\n\t}\n\n\tpublic start(): void {\n\t\tif (!this.config.dev.typecheck.enabled) return;\n\t\tthis.#run();\n\t}\n\n\t/**\n\t * Re-runs a checker that cannot watch. Called after every successful build; a run already in flight is replaced,\n\t * so a fast series of builds only leaves the last check standing.\n\t */\n\tpublic check(): void {\n\t\tif (!this.oneShot) return;\n\t\tif (this.#child) {\n\t\t\tthis.#pending = true;\n\t\t\tthis.#child.kill();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#run();\n\t}\n\n\tpublic close(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tthis.#child = null;\n\t\tthis.#pending = false;\n\t\tif (!child || child.exitCode !== null) return Promise.resolve();\n\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once('exit', () => resolve());\n\t\t\tchild.kill();\n\t\t});\n\t}\n\n\t#run(): void {\n\t\tconst { command, args, node, watch } = resolveTypecheckCommand(this.config);\n\t\tconst child = spawn(node ? process.execPath : command, node ? [command, ...args] : [...args], {\n\t\t\tcwd: this.config.root,\n\t\t\tenv: process.env,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\twindowsHide: true\n\t\t});\n\t\tthis.#child = child;\n\t\tthis.#setState('checking', 0);\n\n\t\tlet errors = 0;\n\t\tconst handleLine = (line: string) => {\n\t\t\tif (ERROR_LINE.test(line)) errors++;\n\t\t\tthis.#handleLine(line);\n\t\t};\n\n\t\tconst stdout = createLineSplitter(handleLine);\n\t\tconst stderr = createLineSplitter(handleLine);\n\t\tchild.stdout?.on('data', stdout.push);\n\t\tchild.stderr?.on('data', stderr.push);\n\n\t\tchild.once('exit', (code) => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t\tthis.#child = null;\n\n\t\t\tif (!watch && this.#state === 'checking') {\n\t\t\t\t// A one-shot checker only reports through its exit code when it prints no summary line.\n\t\t\t\tthis.#setState(code === 0 && errors === 0 ? 'ok' : 'failed', errors);\n\t\t\t}\n\n\t\t\tif (this.#pending) {\n\t\t\t\tthis.#pending = false;\n\t\t\t\tthis.#run();\n\t\t\t}\n\t\t});\n\t\tchild.once('error', (error: NodeJS.ErrnoException) => {\n\t\t\tthis.#child = null;\n\t\t\tthis.emit('log', 'error', error.code === 'ENOENT' ? `${command} is not installed` : error.message);\n\t\t\tthis.#setState('failed', this.#errors);\n\t\t});\n\t}\n\n\t#handleLine(line: string): void {\n\t\tconst text = line.trim();\n\t\tif (text.length === 0) return;\n\n\t\tif (WATCH_START.test(text)) {\n\t\t\tthis.#setState('checking', 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (ERROR_LINE.test(text)) {\n\t\t\tthis.emit('log', 'error', text);\n\t\t\treturn;\n\t\t}\n\n\t\tconst done = WATCH_DONE.exec(text) ?? ONE_SHOT_DONE.exec(text);\n\t\tif (done) {\n\t\t\tconst errors = Number(done[1]);\n\t\t\tthis.#setState(errors === 0 ? 'ok' : 'failed', errors);\n\t\t\tthis.emit('log', errors === 0 ? 'success' : 'error', errors === 0 ? 'No type errors' : `${errors} type error${errors === 1 ? '' : 's'}`);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit('log', 'info', text);\n\t}\n\n\t#setState(state: TypecheckState, errors: number): void {\n\t\tthis.#state = state;\n\t\tthis.#errors = errors;\n\t\tthis.emit('state', state, errors);\n\t}\n}\n\n/**\n * Builds the command line of the configured type checker, resolved from the project's own `node_modules`.\n *\n * @throws {Diagnostic} when the checker is not installed in the project.\n */\nexport function resolveTypecheckCommand(config: ResolvedStarsConfig): TypecheckCommand {\n\tconst { checker, tsconfig } = config.dev.typecheck;\n\tconst project = tsconfig!;\n\n\tswitch (checker) {\n\t\tcase 'golar': {\n\t\t\t// `golar tsc` forwards everything after it to the TypeScript CLI, watch mode included.\n\t\t\tconst golar = resolveBinary(config.root, 'golar', 'golar');\n\t\t\tif (!golar) throw missing(config, 'golar', 'Install it with `pnpm add -D golar`');\n\n\t\t\treturn { command: golar, args: ['tsc', ...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\n\t\tcase 'tsz': {\n\t\t\t// `tsz` is an early, tsc-compatible checker with no watch mode; `try-tsz` is its comparison harness.\n\t\t\tconst tsz = resolveBinary(config.root, '@mohsen-azimi/tsz-dev', 'tsz');\n\t\t\tif (tsz) return { command: tsz, args: ['--noEmit', '-p', project], node: true, watch: false };\n\n\t\t\tconst tryTsz = resolveBinary(config.root, 'try-tsz', 'try-tsz');\n\t\t\tif (tryTsz) return { command: tryTsz, args: ['-p', project], node: true, watch: false };\n\n\t\t\tthrow missing(config, 'tsz', 'Install it with `pnpm add -D @mohsen-azimi/tsz-dev` (or `try-tsz`)');\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst tsc = resolveTscBinary(config.root);\n\t\t\tif (!tsc) throw missing(config, 'typescript', 'Install it with `pnpm add -D typescript`');\n\n\t\t\treturn { command: tsc, args: [...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\t}\n}\n\nconst WATCH_ARGS = ['--noEmit', '--watch', '--preserveWatchOutput', '--pretty', 'false'] as const;\n\nfunction missing(config: ResolvedStarsConfig, name: string, install: string): Diagnostic {\n\treturn cliDiagnostics.DEPENDENCY_MISSING({\n\t\tname,\n\t\troot: config.root,\n\t\thint: `${install}, pick another \\`dev.typecheck.checker\\`, or set \\`dev.typecheck\\` to false.`\n\t});\n}\n\nexport type { StarsTypechecker };\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from '../builders/types.js';\nimport { displayPath, type ResolvedStarsConfig, 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}\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\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#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\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\n\t\tthis.builder.on('start', () => {\n\t\t\tthis.#progress = { fraction: 0, message: 'preparing build', startedAt: Date.now(), readyMs: null };\n\t\t\tthis.#setBuild('building');\n\t\t\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\tvoid this.tunnel.start();\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tawait this.#queueHook('dev:restart', reason, this.config);\n\t\t\tif (this.#stopped) return;\n\t\t\tif (reason === 'manual' || reason === 'crash') {\n\t\t\t\tthis.#progress = { fraction: 0, message: 'restarting the bot', startedAt: Date.now(), readyMs: null };\n\t\t\t}\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#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\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/** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */\n\tpublic refreshCommands(): void {\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\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([\n\t\t\t\tthis.builder.close(),\n\t\t\t\tthis.supervisor.stop(),\n\t\t\t\tthis.typechecker.close(),\n\t\t\t\tthis.tunnel.close(),\n\t\t\t\tthis.locales.close()\n\t\t\t]);\n\t\t});\n\t}\n\n\t/**\n\t * Kills the bot and the helper processes without waiting for them. Only for a forced shutdown, {@link stop} is the graceful path.\n\t */\n\tpublic kill(): void {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tthis.supervisor.kill();\n\t\tvoid this.typechecker.close();\n\t\tvoid this.tunnel.close();\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\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\tif (hot !== 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\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 it reported before. The first report of a session is the\n\t * baseline; a later one that differs is acted on as `dev.commands.refresh` says.\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\tconst changes = diffSnapshots(previous, snapshot);\n\t\tif (changes.length === 0) return;\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.\n\t\tthis.#prompt = { kind: 'commands', changes: mergeChanges(this.#prompt?.changes ?? [], 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\n/** The latest change of each command wins: `added` then `changed` is still one line of the prompt. */\nfunction mergeChanges(previous: readonly CommandChange[], next: readonly CommandChange[]): CommandChange[] {\n\tconst key = (change: CommandChange) => `${change.guild ?? ''}:${change.type}:${change.name}`;\n\tconst merged = new Map(previous.map((change) => [key(change), change]));\n\tfor (const change of next) merged.set(key(change), change);\n\treturn [...merged.values()];\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}\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 });\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\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},\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});\n\t}\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev-Owu3e3AO.js","names":[],"sources":["../src/dev/changed-files.ts","../src/dev/typechecker.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 type { ResolvedStarsConfig, StarsTypechecker } from '@wolfstar/schema';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { EventEmitter } from 'node:events';\nimport type { Diagnostic } from 'nostics';\nimport { resolveTscBinary } from '../builders/tsc.js';\nimport { cliDiagnostics } from '../utils/diagnostics.js';\nimport type { LogLevel } from '../utils/log-buffer.js';\nimport { createLineSplitter } from '../utils/process-supervisor.js';\nimport { resolveBinary } from '../utils/project.js';\n\nconst WATCH_START = /Starting (incremental )?compilation/;\nconst WATCH_DONE = /Found (\\d+) errors?\\. Watching for file changes/;\nconst ONE_SHOT_DONE = /Found (\\d+) errors?/;\nconst ERROR_LINE = /error TS\\d+:/;\n\nexport type TypecheckState = 'idle' | 'checking' | 'ok' | 'failed';\n\nexport interface TypecheckerEvents {\n\tlog: [level: LogLevel, text: string];\n\tstate: [state: TypecheckState, errors: number];\n}\n\nexport interface TypecheckCommand {\n\t/** The executable, and whether it is a script `node` has to run. */\n\treadonly command: string;\n\treadonly args: readonly string[];\n\treadonly node: boolean;\n\t/** Whether the checker watches the project itself, or has to be re-run after every build. */\n\treadonly watch: boolean;\n}\n\n/**\n * Runs a type checker next to the bot and reports type errors on their own channel, without ever blocking builds or\n * restarts: `stars dev` keeps running whatever the build tool produced, the way Seedcord's `hmr.typecheck` and\n * Vite's `checker` plugin do.\n *\n * `tsc` and `golar` (which forwards to TypeScript) watch the project themselves; `tsz` has no watch mode, so\n * {@link Typechecker.check} re-runs it after every build instead.\n */\nexport class Typechecker extends EventEmitter<TypecheckerEvents> {\n\t#child: ChildProcess | null = null;\n\t#state: TypecheckState = 'idle';\n\t#errors = 0;\n\t#pending = false;\n\n\tpublic constructor(private readonly config: ResolvedStarsConfig) {\n\t\tsuper();\n\t}\n\n\tpublic get state(): TypecheckState {\n\t\treturn this.#state;\n\t}\n\n\tpublic get errors(): number {\n\t\treturn this.#errors;\n\t}\n\n\t/** Whether the checker re-runs per build instead of watching on its own. */\n\tpublic get oneShot(): boolean {\n\t\treturn this.config.dev.typecheck.enabled && !resolveTypecheckCommand(this.config).watch;\n\t}\n\n\tpublic start(): void {\n\t\tif (!this.config.dev.typecheck.enabled) return;\n\t\tthis.#run();\n\t}\n\n\t/**\n\t * Re-runs a checker that cannot watch. Called after every successful build; a run already in flight is replaced,\n\t * so a fast series of builds only leaves the last check standing.\n\t */\n\tpublic check(): void {\n\t\tif (!this.oneShot) return;\n\t\tif (this.#child) {\n\t\t\tthis.#pending = true;\n\t\t\tthis.#child.kill();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#run();\n\t}\n\n\tpublic close(): Promise<void> {\n\t\tconst child = this.#child;\n\t\tthis.#child = null;\n\t\tthis.#pending = false;\n\t\tif (!child || child.exitCode !== null) return Promise.resolve();\n\n\t\treturn new Promise((resolve) => {\n\t\t\tchild.once('exit', () => resolve());\n\t\t\tchild.kill();\n\t\t});\n\t}\n\n\t#run(): void {\n\t\tconst { command, args, node, watch } = resolveTypecheckCommand(this.config);\n\t\tconst child = spawn(node ? process.execPath : command, node ? [command, ...args] : [...args], {\n\t\t\tcwd: this.config.root,\n\t\t\tenv: process.env,\n\t\t\tstdio: ['ignore', 'pipe', 'pipe'],\n\t\t\twindowsHide: true\n\t\t});\n\t\tthis.#child = child;\n\t\tthis.#setState('checking', 0);\n\n\t\tlet errors = 0;\n\t\tconst handleLine = (line: string) => {\n\t\t\tif (ERROR_LINE.test(line)) errors++;\n\t\t\tthis.#handleLine(line);\n\t\t};\n\n\t\tconst stdout = createLineSplitter(handleLine);\n\t\tconst stderr = createLineSplitter(handleLine);\n\t\tchild.stdout?.on('data', stdout.push);\n\t\tchild.stderr?.on('data', stderr.push);\n\n\t\tchild.once('exit', (code) => {\n\t\t\tstdout.flush();\n\t\t\tstderr.flush();\n\t\t\tthis.#child = null;\n\n\t\t\tif (!watch && this.#state === 'checking') {\n\t\t\t\t// A one-shot checker only reports through its exit code when it prints no summary line.\n\t\t\t\tthis.#setState(code === 0 && errors === 0 ? 'ok' : 'failed', errors);\n\t\t\t}\n\n\t\t\tif (this.#pending) {\n\t\t\t\tthis.#pending = false;\n\t\t\t\tthis.#run();\n\t\t\t}\n\t\t});\n\t\tchild.once('error', (error: NodeJS.ErrnoException) => {\n\t\t\tthis.#child = null;\n\t\t\tthis.emit('log', 'error', error.code === 'ENOENT' ? `${command} is not installed` : error.message);\n\t\t\tthis.#setState('failed', this.#errors);\n\t\t});\n\t}\n\n\t#handleLine(line: string): void {\n\t\tconst text = line.trim();\n\t\tif (text.length === 0) return;\n\n\t\tif (WATCH_START.test(text)) {\n\t\t\tthis.#setState('checking', 0);\n\t\t\treturn;\n\t\t}\n\n\t\tif (ERROR_LINE.test(text)) {\n\t\t\tthis.emit('log', 'error', text);\n\t\t\treturn;\n\t\t}\n\n\t\tconst done = WATCH_DONE.exec(text) ?? ONE_SHOT_DONE.exec(text);\n\t\tif (done) {\n\t\t\tconst errors = Number(done[1]);\n\t\t\tthis.#setState(errors === 0 ? 'ok' : 'failed', errors);\n\t\t\tthis.emit('log', errors === 0 ? 'success' : 'error', errors === 0 ? 'No type errors' : `${errors} type error${errors === 1 ? '' : 's'}`);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.emit('log', 'info', text);\n\t}\n\n\t#setState(state: TypecheckState, errors: number): void {\n\t\tthis.#state = state;\n\t\tthis.#errors = errors;\n\t\tthis.emit('state', state, errors);\n\t}\n}\n\n/**\n * Builds the command line of the configured type checker, resolved from the project's own `node_modules`.\n *\n * @throws {Diagnostic} when the checker is not installed in the project.\n */\nexport function resolveTypecheckCommand(config: ResolvedStarsConfig): TypecheckCommand {\n\tconst { checker, tsconfig } = config.dev.typecheck;\n\tconst project = tsconfig!;\n\n\tswitch (checker) {\n\t\tcase 'golar': {\n\t\t\t// `golar tsc` forwards everything after it to the TypeScript CLI, watch mode included.\n\t\t\tconst golar = resolveBinary(config.root, 'golar', 'golar');\n\t\t\tif (!golar) throw missing(config, 'golar', 'Install it with `pnpm add -D golar`');\n\n\t\t\treturn { command: golar, args: ['tsc', ...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\n\t\tcase 'tsz': {\n\t\t\t// `tsz` is an early, tsc-compatible checker with no watch mode; `try-tsz` is its comparison harness.\n\t\t\tconst tsz = resolveBinary(config.root, '@mohsen-azimi/tsz-dev', 'tsz');\n\t\t\tif (tsz) return { command: tsz, args: ['--noEmit', '-p', project], node: true, watch: false };\n\n\t\t\tconst tryTsz = resolveBinary(config.root, 'try-tsz', 'try-tsz');\n\t\t\tif (tryTsz) return { command: tryTsz, args: ['-p', project], node: true, watch: false };\n\n\t\t\tthrow missing(config, 'tsz', 'Install it with `pnpm add -D @mohsen-azimi/tsz-dev` (or `try-tsz`)');\n\t\t}\n\n\t\tdefault: {\n\t\t\tconst tsc = resolveTscBinary(config.root);\n\t\t\tif (!tsc) throw missing(config, 'typescript', 'Install it with `pnpm add -D typescript`');\n\n\t\t\treturn { command: tsc, args: [...WATCH_ARGS, '-p', project], node: true, watch: true };\n\t\t}\n\t}\n}\n\nconst WATCH_ARGS = ['--noEmit', '--watch', '--preserveWatchOutput', '--pretty', 'false'] as const;\n\nfunction missing(config: ResolvedStarsConfig, name: string, install: string): Diagnostic {\n\treturn cliDiagnostics.DEPENDENCY_MISSING({\n\t\tname,\n\t\troot: config.root,\n\t\thint: `${install}, pick another \\`dev.typecheck.checker\\`, or set \\`dev.typecheck\\` to false.`\n\t});\n}\n\nexport type { StarsTypechecker };\n","import { EventEmitter } from 'node:events';\nimport type { Builder, BuildOutcome } from '../builders/types.js';\nimport { displayPath, type ResolvedStarsConfig, 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}\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\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#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\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\n\t\tthis.builder.on('start', () => {\n\t\t\tthis.#progress = { fraction: 0, message: 'preparing build', startedAt: Date.now(), readyMs: null };\n\t\t\tthis.#setBuild('building');\n\t\t\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\tvoid this.tunnel.start();\n\t\tawait this.builder.watch();\n\t}\n\n\t/**\n\t * Restarts the bot immediately (used by the `r` key and by `SIGUSR2`).\n\t */\n\tpublic restart(reason: RestartReason = 'manual'): Promise<void> {\n\t\tthis.#clearRestartTimer();\n\t\treturn this.#enqueue(async () => {\n\t\t\tif (this.#stopped) return;\n\t\t\tawait this.#queueHook('dev:restart', reason, this.config);\n\t\t\tif (this.#stopped) return;\n\t\t\tif (reason === 'manual' || reason === 'crash') {\n\t\t\t\tthis.#progress = { fraction: 0, message: 'restarting the bot', startedAt: Date.now(), readyMs: null };\n\t\t\t}\n\t\t\tthis.#lastRestartReason = reason;\n\t\t\tif (this.supervisor.running) {\n\t\t\t\tthis.#restarts++;\n\t\t\t\tthis.log('stars', 'info', `Restarting (${describeReason(reason)})`);\n\t\t\t\tawait this.supervisor.stop();\n\t\t\t} else {\n\t\t\t\tthis.log('stars', 'info', `Starting (${describeReason(reason)})`);\n\t\t\t}\n\n\t\t\tif (this.#stopped) return;\n\t\t\tthis.#progress = { ...this.#progress, fraction: 0.75, message: 'starting the bot' };\n\t\t\tthis.#health = 'unknown';\n\t\t\tthis.#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\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/** Asks the bot to register its commands with Discord again. The bot answers on the `commands` channel. */\n\tpublic refreshCommands(): void {\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\tif (this.#healthTimer) clearInterval(this.#healthTimer);\n\t\tthis.#healthTimer = null;\n\t\tawait this.#enqueue(async () => {\n\t\t\tawait Promise.allSettled([\n\t\t\t\tthis.builder.close(),\n\t\t\t\tthis.supervisor.stop(),\n\t\t\t\tthis.typechecker.close(),\n\t\t\t\tthis.tunnel.close(),\n\t\t\t\tthis.locales.close()\n\t\t\t]);\n\t\t});\n\t}\n\n\t/**\n\t * Kills the bot and the helper processes without waiting for them. Only for a forced shutdown, {@link stop} is the graceful path.\n\t */\n\tpublic kill(): void {\n\t\tthis.#stopped = true;\n\t\tthis.#clearRestartTimer();\n\t\tthis.supervisor.kill();\n\t\tvoid this.typechecker.close();\n\t\tvoid this.tunnel.close();\n\t}\n\n\tpublic clearLogs(): void {\n\t\tthis.logs.clear();\n\t}\n\n\t#onBuildSuccess(outcome: BuildOutcome): void {\n\t\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\tif (hot !== 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\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 it reported before. The first report of a session is the\n\t * baseline; a later one that differs is acted on as `dev.commands.refresh` says.\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\tconst changes = diffSnapshots(previous, snapshot);\n\t\tif (changes.length === 0) return;\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.\n\t\tthis.#prompt = { kind: 'commands', changes: mergeChanges(this.#prompt?.changes ?? [], 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\n/** The latest change of each command wins: `added` then `changed` is still one line of the prompt. */\nfunction mergeChanges(previous: readonly CommandChange[], next: readonly CommandChange[]): CommandChange[] {\n\tconst key = (change: CommandChange) => `${change.guild ?? ''}:${change.type}:${change.name}`;\n\tconst merged = new Map(previous.map((change) => [key(change), change]));\n\tfor (const change of next) merged.set(key(change), change);\n\treturn [...merged.values()];\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}\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 });\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\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},\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});\n\t}\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,sCAAsB,IAAI,IAAI,CAAC,gBAAgB,MAAM,CAAC;;;;;AAK5D,MAAM,gBAAgB;;;;;;;;AAetB,SAAgB,cAAc,OAA0B,UAA6B,CAAC,GAAG,wBAAmB,IAAI,IAAI,GAAiB;CACpI,MAAM,2BAAW,IAAI,IAAoB;CACzC,MAAM,SAAS,cAAsB;EACpC,IAAI;EACJ,IAAI;GACH,UAAU,YAAY,WAAW,EAAE,eAAe,KAAK,CAAC;EACzD,QAAQ;GACP;EACD;EAEA,KAAK,MAAM,SAAS,SAAS;GAC5B,MAAM,OAAO,KAAK,WAAW,MAAM,IAAI;GACvC,IAAI,QAAQ,MAAM,aAAa,SAAS,MAAM,QAAQ,CAAC,GAAG;GAC1D,IAAI,MAAM,YAAY,GACrB;QAAI,CAAC,oBAAoB,IAAI,MAAM,IAAI,GAAG,MAAM,IAAI;GAAC,OAC/C,IAAI,MAAM,OAAO,KAAK,CAAC,cAAc,KAAK,MAAM,IAAI,GAC1D,IAAI;IACH,MAAM,EAAE,MAAM,YAAY,SAAS,IAAI;IACvC,MAAM,QAAQ,MAAM,IAAI,IAAI;IAC5B,MAAM,OACL,OAAO,SAAS,QAAQ,MAAM,YAAY,UACvC,MAAM,OACN,WAAW,MAAM,CAAC,CAAC,OAAO,aAAa,IAAI,CAAC,CAAC,CAAC,OAAO,QAAQ;IACjE,MAAM,IAAI,MAAM;KAAE;KAAM;KAAS;IAAK,CAAC;IACvC,SAAS,IAAI,MAAM,IAAI;GACxB,QAAQ,CAER;EAEF;CACD;CAEA,KAAK,MAAM,QAAQ,IAAI,IAAI,KAAK,GAAG,MAAM,IAAI;CAC7C,OAAO;AACR;;AAGA,SAAgB,aAAa,UAAwB,MAA8B;CAClF,MAAM,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,UAAU,SAAS,IAAI,IAAI,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI;CACpG,KAAK,MAAM,QAAQ,SAAS,KAAK,GAChC,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG,QAAQ,KAAK,IAAI;CAGvC,OAAO;AACR;AAEA,MAAM,mCAAmB,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAQ;CAAO;CAAQ;AAAM,CAAC;;;;;AAM/E,SAAgB,aAAa,MAAuB;CACnD,MAAM,YAAY,QAAQ,IAAI;CAC9B,OAAO,iBAAiB,IAAI,SAAS,KAAK,CAAC,KAAK,SAAS,OAAO,KAAK,CAAC,SAAS,MAAM,SAAS,CAAC,CAAC,WAAW,GAAG;AAC/G;;AAGA,SAAgB,SAAS,MAAc,WAA4B;CAClE,OAAO,SAAS,aAAa,KAAK,WAAW,UAAU,SAAS,GAAG,IAAI,YAAY,GAAG,YAAY,KAAK;AACxG;;;;AC1EA,MAAM,cAAc;AACpB,MAAM,aAAa;AACnB,MAAM,gBAAgB;AACtB,MAAM,aAAa;;;;;;;;;AA0BnB,IAAa,cAAb,cAAiC,aAAgC;CAM5B;CALpC,SAA8B;CAC9B,SAAyB;CACzB,UAAU;CACV,WAAW;CAEX,AAAO,YAAY,AAAiB,QAA6B;EAChE,MAAM;EAD6B;CAEpC;CAEA,IAAW,QAAwB;EAClC,OAAO,KAAK;CACb;CAEA,IAAW,SAAiB;EAC3B,OAAO,KAAK;CACb;;CAGA,IAAW,UAAmB;EAC7B,OAAO,KAAK,OAAO,IAAI,UAAU,WAAW,CAAC,wBAAwB,KAAK,MAAM,CAAC,CAAC;CACnF;CAEA,AAAO,QAAc;EACpB,IAAI,CAAC,KAAK,OAAO,IAAI,UAAU,SAAS;EACxC,KAAK,KAAK;CACX;;;;;CAMA,AAAO,QAAc;EACpB,IAAI,CAAC,KAAK,SAAS;EACnB,IAAI,KAAK,QAAQ;GAChB,KAAK,WAAW;GAChB,KAAK,OAAO,KAAK;GACjB;EACD;EAEA,KAAK,KAAK;CACX;CAEA,AAAO,QAAuB;EAC7B,MAAM,QAAQ,KAAK;EACnB,KAAK,SAAS;EACd,KAAK,WAAW;EAChB,IAAI,CAAC,SAAS,MAAM,aAAa,MAAM,OAAO,QAAQ,QAAQ;EAE9D,OAAO,IAAI,SAAS,YAAY;GAC/B,MAAM,KAAK,cAAc,QAAQ,CAAC;GAClC,MAAM,KAAK;EACZ,CAAC;CACF;CAEA,OAAa;EACZ,MAAM,EAAE,SAAS,MAAM,MAAM,UAAU,wBAAwB,KAAK,MAAM;EAC1E,MAAM,QAAQ,MAAM,OAAO,QAAQ,WAAW,SAAS,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,GAAG;GAC7F,KAAK,KAAK,OAAO;GACjB,KAAK,QAAQ;GACb,OAAO;IAAC;IAAU;IAAQ;GAAM;GAChC,aAAa;EACd,CAAC;EACD,KAAK,SAAS;EACd,KAAK,UAAU,YAAY,CAAC;EAE5B,IAAI,SAAS;EACb,MAAM,cAAc,SAAiB;GACpC,IAAI,WAAW,KAAK,IAAI,GAAG;GAC3B,KAAK,YAAY,IAAI;EACtB;EAEA,MAAM,SAAS,mBAAmB,UAAU;EAC5C,MAAM,SAAS,mBAAmB,UAAU;EAC5C,MAAM,QAAQ,GAAG,QAAQ,OAAO,IAAI;EACpC,MAAM,QAAQ,GAAG,QAAQ,OAAO,IAAI;EAEpC,MAAM,KAAK,SAAS,SAAS;GAC5B,OAAO,MAAM;GACb,OAAO,MAAM;GACb,KAAK,SAAS;GAEd,IAAI,CAAC,SAAS,KAAK,WAAW,YAE7B,KAAK,UAAU,SAAS,KAAK,WAAW,IAAI,OAAO,UAAU,MAAM;GAGpE,IAAI,KAAK,UAAU;IAClB,KAAK,WAAW;IAChB,KAAK,KAAK;GACX;EACD,CAAC;EACD,MAAM,KAAK,UAAU,UAAiC;GACrD,KAAK,SAAS;GACd,KAAK,KAAK,OAAO,SAAS,MAAM,SAAS,WAAW,GAAG,QAAQ,qBAAqB,MAAM,OAAO;GACjG,KAAK,UAAU,UAAU,KAAK,OAAO;EACtC,CAAC;CACF;CAEA,YAAY,MAAoB;EAC/B,MAAM,OAAO,KAAK,KAAK;EACvB,IAAI,KAAK,WAAW,GAAG;EAEvB,IAAI,YAAY,KAAK,IAAI,GAAG;GAC3B,KAAK,UAAU,YAAY,CAAC;GAC5B;EACD;EAEA,IAAI,WAAW,KAAK,IAAI,GAAG;GAC1B,KAAK,KAAK,OAAO,SAAS,IAAI;GAC9B;EACD;EAEA,MAAM,OAAO,WAAW,KAAK,IAAI,KAAK,cAAc,KAAK,IAAI;EAC7D,IAAI,MAAM;GACT,MAAM,SAAS,OAAO,KAAK,EAAE;GAC7B,KAAK,UAAU,WAAW,IAAI,OAAO,UAAU,MAAM;GACrD,KAAK,KAAK,OAAO,WAAW,IAAI,YAAY,SAAS,WAAW,IAAI,mBAAmB,GAAG,OAAO,aAAa,WAAW,IAAI,KAAK,KAAK;GACvI;EACD;EAEA,KAAK,KAAK,OAAO,QAAQ,IAAI;CAC9B;CAEA,UAAU,OAAuB,QAAsB;EACtD,KAAK,SAAS;EACd,KAAK,UAAU;EACf,KAAK,KAAK,SAAS,OAAO,MAAM;CACjC;AACD;;;;;;AAOA,SAAgB,wBAAwB,QAA+C;CACtF,MAAM,EAAE,SAAS,aAAa,OAAO,IAAI;CACzC,MAAM,UAAU;CAEhB,QAAQ,SAAR;EACC,KAAK,SAAS;GAEb,MAAM,QAAQ,cAAc,OAAO,MAAM,SAAS,OAAO;GACzD,IAAI,CAAC,OAAO,MAAM,QAAQ,QAAQ,SAAS,qCAAqC;GAEhF,OAAO;IAAE,SAAS;IAAO,MAAM;KAAC;KAAO,GAAG;KAAY;KAAM;IAAO;IAAG,MAAM;IAAM,OAAO;GAAK;EAC/F;EAEA,KAAK,OAAO;GAEX,MAAM,MAAM,cAAc,OAAO,MAAM,yBAAyB,KAAK;GACrE,IAAI,KAAK,OAAO;IAAE,SAAS;IAAK,MAAM;KAAC;KAAY;KAAM;IAAO;IAAG,MAAM;IAAM,OAAO;GAAM;GAE5F,MAAM,SAAS,cAAc,OAAO,MAAM,WAAW,SAAS;GAC9D,IAAI,QAAQ,OAAO;IAAE,SAAS;IAAQ,MAAM,CAAC,MAAM,OAAO;IAAG,MAAM;IAAM,OAAO;GAAM;GAEtF,MAAM,QAAQ,QAAQ,OAAO,oEAAoE;EAClG;EAEA,SAAS;GACR,MAAM,MAAM,iBAAiB,OAAO,IAAI;GACxC,IAAI,CAAC,KAAK,MAAM,QAAQ,QAAQ,cAAc,0CAA0C;GAExF,OAAO;IAAE,SAAS;IAAK,MAAM;KAAC,GAAG;KAAY;KAAM;IAAO;IAAG,MAAM;IAAM,OAAO;GAAK;EACtF;CACD;AACD;AAEA,MAAM,aAAa;CAAC;CAAY;CAAW;CAAyB;CAAY;AAAO;AAEvF,SAAS,QAAQ,QAA6B,MAAc,SAA6B;CACxF,OAAO,eAAe,mBAAmB;EACxC;EACA,MAAM,OAAO;EACb,MAAM,GAAG,QAAQ;CAClB,CAAC;AACF;;;;;;;;AC3IA,IAAa,aAAb,cAAgC,aAA+B;CA4C7C;CA3CjB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;CAChB,AAAgB;;CAEhB,AAAO,aAAa;CACpB,AAAS;;CAET,aAA4B,QAAQ,QAAQ;CAE5C,SAAqB;CACrB,UAAuB;CACvB,YAAY;CACZ,qBAA2C;CAC3C,aAAkC;CAClC,YAAgC;CAChC,gBAAuC;CACvC,eAAsC;CACtC,iBAAuC;CACvC,WAAW;CACX,SAAwB,QAAQ,QAAQ;CACxC,YAAY;EAAE,UAAU;EAAG,SAAS;EAAsB,WAAW,KAAK,IAAI;EAAG,SAAS;CAAsB;CAChH,SAAS;CACT,QAAuB;;CAEvB,YAAsB,CAAC;CACvB,UAAU;CACV,UAA4B;;CAE5B,YAAoC;;CAEpC,SAA8B;CAC9B,AAAS,0BAAqB,IAAI,IAAI;;CAEtC,0BAAU,IAAI,IAAY;;CAE1B,YAAoC;;CAEpC,kBAAkB;CAElB,AAAO,YACN,AAAgB,QAChB,SACC;EACD,MAAM;EAHU;EAIhB,KAAK,OAAO,QAAQ,QAAQ,IAAI,UAAU;EAC1C,KAAK,UAAU,QAAQ;EACvB,KAAK,aAAa,QAAQ,cAAc,iBAAiB,MAAM;EAC/D,KAAK,cAAc,QAAQ,eAAe,IAAI,YAAY,MAAM;EAChE,KAAK,SAAS,QAAQ,UAAU,IAAI,OAAO,MAAM;EACjD,KAAK,UAAU,IAAI,QAAQ,MAAM;EACjC,KAAK,SAAS,QAAQ,SAAS;EAE/B,KAAK,QAAQ,GAAG,eAAe;GAC9B,KAAK,YAAY;IAAE,UAAU;IAAG,SAAS;IAAmB,WAAW,KAAK,IAAI;IAAG,SAAS;GAAK;GACjG,KAAK,UAAU,UAAU;GACzB,AAAK,KAAK,WAAW,gBAAgB,KAAK,MAAM;EACjD,CAAC;EACD,KAAK,QAAQ,GAAG,aAAa,UAAU,YAAY;GAClD,KAAK,YAAY;IAAE,GAAG,KAAK;IAAW,UAAU,KAAK,IAAI,KAAK,UAAU,UAAU,KAAK,IAAI,KAAM,QAAQ,CAAC;IAAG;GAAQ;GACrH,KAAK,YAAY;EAClB,CAAC;EACD,KAAK,QAAQ,GAAG,YAAY,YAAY,KAAK,gBAAgB,OAAO,CAAC;EACrE,KAAK,QAAQ,GAAG,YAAY,YAAY,KAAK,gBAAgB,OAAO,CAAC;EACrE,KAAK,QAAQ,GAAG,QAAQ,OAAO,SAAS,KAAK,IAAI,SAAS,OAAO,IAAI,CAAC;EAEtE,KAAK,WAAW,GAAG,eAAe;GACjC,IAAI,KAAK,WAAW,UAAU,WAAW;IACxC,KAAK,gBAAgB;IACrB,AAAK,KAAK,aAAa;GACxB;GACA,KAAK,YAAY;EAClB,CAAC;EACD,KAAK,WAAW,GAAG,WAAW,SAAS,KAAK,IAAI,OAAO,gBAAgB,MAAM,MAAM,GAAG,IAAI,CAAC;EAC3F,KAAK,WAAW,GAAG,WAAW,SAAS,KAAK,IAAI,OAAO,gBAAgB,MAAM,OAAO,GAAG,IAAI,CAAC;EAC5F,KAAK,WAAW,GAAG,YAAY,YAAY;GAC1C,MAAM,SAAS,mBAAmB,OAAO;GACzC,IAAI,WAAW,MAAM;GACrB,IAAI;IACH,KAAK,UAAU,MAAM;GACtB,SAAS,OAAO;IAEf,KAAK,IAAI,SAAS,QAAQ,aAAa,OAAO,KAAK,yBAAyB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;GACrI;EACD,CAAC;EACD,KAAK,WAAW,GAAG,UAAU,UAAU,KAAK,IAAI,SAAS,SAAS,4BAA4B,MAAM,SAAS,CAAC;EAC9G,KAAK,WAAW,GAAG,SAAS,SAAS,KAAK,QAAQ,IAAI,CAAC;EAEvD,KAAK,YAAY,GAAG,QAAQ,OAAO,SAAS,KAAK,IAAI,OAAO,OAAO,IAAI,CAAC;EACxE,KAAK,YAAY,GAAG,eAAe,KAAK,YAAY,CAAC;EACrD,KAAK,OAAO,GAAG,QAAQ,OAAO,SAAS,KAAK,IAAI,UAAU,OAAO,IAAI,CAAC;EACtE,KAAK,OAAO,GAAG,eAAe,KAAK,YAAY,CAAC;EAChD,KAAK,QAAQ,GAAG,QAAQ,OAAO,SAAS,KAAK,IAAI,SAAS,OAAO,IAAI,CAAC;EACtE,KAAK,QAAQ,GAAG,gBAAgB;GAC/B,IAAI,KAAK,WAAW,MAAM,KAAK,iBAAiB,OAAO;EACxD,CAAC;EAED,IAAI,OAAO,IAAI,OAAO,OAAO,IAAI,QAAQ;GACxC,MAAM,WAAW,QAAQ,kBAAkB;GAC3C,KAAK,eAAe,kBAAkB,KAAK,KAAK,aAAa,GAAG,QAAQ;GACxE,KAAK,aAAa,MAAM;EACzB;CACD;CAEA,IAAW,SAAoB;EAC9B,OAAO;GACN,UAAU,KAAK;GACf,SAAS,KAAK,WAAW;GACzB,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,KAAK,KAAK,WAAW;GACrB,WAAW,KAAK,WAAW;GAC3B,UAAU,KAAK;GACf,mBAAmB,KAAK;GACxB,WAAW,KAAK;GAChB,UAAU,KAAK;GACf,KAAK,KAAK,OAAO,IAAI;GACrB,WAAW,KAAK,YAAY;GAC5B,YAAY,KAAK,YAAY;GAC7B,QAAQ,KAAK,OAAO;GACpB,WAAW,KAAK,OAAO;GACvB,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,KAAK,KAAK,UAAU,SAAS;GAC7B,QAAQ,KAAK;GACb,QAAQ,KAAK;EACd;CACD;CAEA,AAAO,IAAI,QAAmB,OAAiB,MAAc,QAAkB,CAAC,GAAS;EACxF,KAAK,KAAK,KAAK;GAAE;GAAQ;GAAO;GAAM,GAAG;EAAM,CAAC;CACjD;;;;CAKA,MAAa,QAAuB;EACnC,KAAK,WAAW;EAChB,MAAM,QAAQ,YAAY,KAAK,OAAO,MAAM,KAAK,OAAO,KAAK;EAC7D,KAAK,IAAI,SAAS,QAAQ,KAAK,OAAO,MAAM,SAAS,SAAS,YAAY,UAAU,YAAY,MAAM,QAAQ,KAAK,OAAO,MAAM,MAAM;EACtI,IAAI,KAAK,OAAO,IAAI,UAAU,SAAS,KAAK,YAAY,MAAM;EAC9D,MAAM,KAAK,QAAQ,MAAM;EAEzB,AAAK,KAAK,OAAO,MAAM;EACvB,MAAM,KAAK,QAAQ,MAAM;CAC1B;;;;CAKA,AAAO,QAAQ,SAAwB,UAAyB;EAC/D,KAAK,mBAAmB;EACxB,OAAO,KAAK,SAAS,YAAY;GAChC,IAAI,KAAK,UAAU;GACnB,MAAM,KAAK,WAAW,eAAe,QAAQ,KAAK,MAAM;GACxD,IAAI,KAAK,UAAU;GACnB,IAAI,WAAW,YAAY,WAAW,SACrC,KAAK,YAAY;IAAE,UAAU;IAAG,SAAS;IAAsB,WAAW,KAAK,IAAI;IAAG,SAAS;GAAK;GAErG,KAAK,qBAAqB;GAC1B,IAAI,KAAK,WAAW,SAAS;IAC5B,KAAK;IACL,KAAK,IAAI,SAAS,QAAQ,eAAe,eAAe,MAAM,EAAE,EAAE;IAClE,MAAM,KAAK,WAAW,KAAK;GAC5B,OACC,KAAK,IAAI,SAAS,QAAQ,aAAa,eAAe,MAAM,EAAE,EAAE;GAGjE,IAAI,KAAK,UAAU;GACnB,KAAK,YAAY;IAAE,GAAG,KAAK;IAAW,UAAU;IAAM,SAAS;GAAmB;GAClF,KAAK,UAAU;GACf,KAAK,UAAU;GACf,KAAK,WAAW,MAAM;GACtB,KAAK,YAAY;EAClB,CAAC;CACF;;;;;CAMA,AAAO,aAA4B;EAClC,KAAK,mBAAmB;EACxB,OAAO,KAAK,SAAS,YAAY;GAChC,IAAI,KAAK,YAAY,KAAK,SAAS;GACnC,KAAK,UAAU;GACf,KAAK,IAAI,SAAS,QAAQ,4CAA4C;GACtE,MAAM,KAAK,WAAW,KAAK;GAC3B,KAAK,YAAY;EAClB,CAAC;CACF;;CAGA,AAAO,aAAa,QAAuB;EAE1C,IADe,KAAK,YACL,MAAM;EACrB,KAAK,UAAU;EACf,IAAI,QAAQ,KAAK,gBAAgB;OAC5B,KAAK,IAAI,SAAS,QAAQ,0CAA0C,EAAE,SAAS,WAAW,CAAC;EAChG,KAAK,YAAY;CAClB;;CAGA,AAAO,kBAAwB;EAE9B,IAAI,CAAC,KAAK,QAAQ;GACjB,KAAK,kBAAkB;GACvB,MAAM,OAAO,KAAK,WAAW,UAAU,0BAA0B;GACjE,KAAK,IAAI,SAAS,QAAQ,uBAAuB,KAAK,IAAI,EAAE,SAAS,WAAW,CAAC;GACjF;EACD;EAEA,KAAK,kBAAkB;EACvB,MAAM,UAAU,KAAK,WAAW,UAAU,CAAC;EAC3C,MAAM,UAA0B;GAC/B,QAAQ;GACR,MAAM;GACN,aAAa,OAAO,KAAK,KAAK,WAAW,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,UAAU,CAAC,OAAO,OAAO,SAAS,KAAK,CAAC;EACxG;EACA,IAAI,KAAK,WAAW,KAAK,OAAO,GAC/B,KAAK,IAAI,SAAS,QAAQ,wBAAwB,EAAE,SAAS,WAAW,CAAC;OAEzE,KAAK,IAAI,SAAS,QAAQ,mDAAmD,EAAE,SAAS,WAAW,CAAC;CAEtG;;CAGA,AAAO,eAA8B;EACpC,OAAO,KAAK,OAAO,OAAO;CAC3B;;;;CAKA,MAAa,OAAsB;EAClC,KAAK,WAAW;EAChB,KAAK,mBAAmB;EACxB,IAAI,KAAK,cAAc,cAAc,KAAK,YAAY;EACtD,KAAK,eAAe;EACpB,MAAM,KAAK,SAAS,YAAY;GAC/B,MAAM,QAAQ,WAAW;IACxB,KAAK,QAAQ,MAAM;IACnB,KAAK,WAAW,KAAK;IACrB,KAAK,YAAY,MAAM;IACvB,KAAK,OAAO,MAAM;IAClB,KAAK,QAAQ,MAAM;GACpB,CAAC;EACF,CAAC;CACF;;;;CAKA,AAAO,OAAa;EACnB,KAAK,WAAW;EAChB,KAAK,mBAAmB;EACxB,KAAK,WAAW,KAAK;EACrB,AAAK,KAAK,YAAY,MAAM;EAC5B,AAAK,KAAK,OAAO,MAAM;CACxB;CAEA,AAAO,YAAkB;EACxB,KAAK,KAAK,MAAM;CACjB;CAEA,gBAAgB,SAA6B;EAE5C,MAAM,MAAM,KAAK,eAAe;EAChC,IAAI;GACH,KAAK,QAAQ,KAAK;EACnB,SAAS,OAAO;GACf,KAAK,gBAAgB;IACpB,GAAG;IACH,IAAI;IACJ,SAAS,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC1F,CAAC;GACD;EACD;EACA,KAAK,aAAa;EAClB,KAAK,YAAY;GAAE,GAAG,KAAK;GAAW,UAAU;GAAM,SAAS;EAAmB;EAClF,KAAK,UAAU,IAAI;EAEnB,AAAK,KAAK,WAAW,cAAc,SAAS,KAAK,MAAM;EACvD,IAAI,KAAK,UAAU;EAEnB,KAAK,YAAY,MAAM;EACvB,KAAK,IAAI,SAAS,WAAW,KAAK,QAAQ,SAAS,SAAS,oBAAoB,sBAAsB,QAAQ,WAAW,GAAG;EAC5H,IAAI,KAAK,SAAS;EAClB,IAAI,QAAQ,MAAM;GACjB,KAAK,MAAM,QAAQ,KAAK,KAAK,IAAI,SAAS,SAAS,UAAU,YAAY,KAAK,OAAO,MAAM,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;GACtH,KAAK,gBAAgB;GACrB,KAAK,YAAY;GACjB;EACD;EACA,KAAK,iBAAiB,KAAK,WAAW,UAAU,SAAS,YAAY,OAAO;CAC7E;;;;;;;;;;;CAYA,iBAAkC;EACjC,IAAI,KAAK,OAAO,IAAI,QAAQ,OAAO,OAAO;EAE1C,MAAM,QAAQ,KAAK,OAAO,MAAM,SAAS,SAAS,KAAK,OAAO,IAAI,QAAQ,CAAC,KAAK,OAAO,MAAM,MAAM;EACnG,MAAM,WAAW,KAAK;EACtB,KAAK,SAAS,cAAc,OAAO,CAAC,KAAK,QAAQ,WAAW,GAAG,KAAK,OAAO;EAC3E,IAAI,aAAa,QAAQ,KAAK,UAAU,WAAW,KAAK,KAAK,WAAW,UAAU,WAAW,OAAO;EAEpG,MAAM,UAAU,aAAa,UAAU,KAAK,MAAM;EAClD,MAAM,eAAe,SACpB,KAAK,UAAU,MAAM,SAAS,SAAS,MAAM,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,KAAM,CAAC,SAAS,IAAI,IAAI,KAAK,aAAa,IAAI;EAC5H,OAAO,QAAQ,SAAS,KAAK,QAAQ,MAAM,WAAW,IAAI,UAAU;CACrE;CAEA,gBAAgB,SAA6B;EAC5C,KAAK,aAAa;EAClB,AAAK,KAAK,WAAW,cAAc,SAAS,KAAK,MAAM;EACvD,KAAK,UAAU,QAAQ;EACvB,KAAK,mBAAmB;EACxB,KAAK,IAAI,SAAS,SAAS,eAAe,QAAQ,UAAU,KAAK,QAAQ,YAAY,GAAG,sBAAsB;CAC/G;CAEA,QAAQ,MAAyB;EAChC,KAAK,YAAY;EACjB,KAAK,UAAU;EACf,KAAK,SAAS;EACd,KAAK,QAAQ;EACb,KAAK,YAAY,CAAC;EAClB,KAAK,QAAQ,MAAM;EAEnB,IAAI,CAAC,KAAK,WAAW;GACpB,MAAM,MAAM,KAAK,SAAS,UAAU,KAAK,WAAW,QAAQ,KAAK;GACjE,KAAK,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,SAAS,uBAAuB,IAAI,2CAA2C;EAC7H;EACA,KAAK,YAAY;CAClB;CAEA,iBAAiB,QAA6B;EAC7C,IAAI,KAAK,SAAS;EAClB,KAAK,iBAAiB;EACtB,KAAK,mBAAmB;EACxB,KAAK,gBAAgB,iBAAiB;GACrC,KAAK,gBAAgB;GACrB,MAAM,UAAU,KAAK,kBAAkB;GACvC,KAAK,iBAAiB;GACtB,AAAK,KAAK,QAAQ,OAAO;EAC1B,GAAG,KAAK,OAAO,IAAI,QAAQ;CAC5B;CAEA,qBAA2B;EAC1B,IAAI,KAAK,eAAe,aAAa,KAAK,aAAa;EACvD,KAAK,gBAAgB;CACtB;CAEA,SAAS,MAA0C;EAClD,KAAK,SAAS,KAAK,OAAO,KAAK,MAAM,IAAI;EACzC,OAAO,KAAK;CACb;CAEA,UAAU,OAAyB;EAClC,IAAI,KAAK,WAAW,OAAO;EAC3B,KAAK,SAAS;EACd,KAAK,YAAY;CAClB;CAEA,MAAM,eAA8B;EACnC,IAAI,CAAC,KAAK,WAAW,WAAW,CAAC,KAAK,OAAO,IAAI,OAAO,CAAC,KAAK,OAAO,IAAI,QAAQ;EACjF,MAAM,MAAM,KAAK,WAAW;EAE5B,IAAI;EACJ,IAAI;GAEH,QAAO,MADgB,MAAM,IAAI,IAAI,KAAK,OAAO,IAAI,QAAQ,KAAK,OAAO,IAAI,GAAG,GAAG,EAAE,QAAQ,YAAY,QAAQ,GAAI,EAAE,CAAC,EACzG,CAAC,SAAS,MAAM,OAAO;EACvC,QAAQ;GACP,OAAO;EACR;EAEA,IAAI,KAAK,YAAY,QAAQ,KAAK,WAAW,KAAK;EAClD,IAAI,SAAS,KAAK,SAAS;GAC1B,KAAK,UAAU;GACf,IAAI,SAAS,MAAM,KAAK,gBAAgB;GACxC,KAAK,YAAY;EAClB;CACD;CAEA,kBAAwB;EACvB,IAAI,KAAK,OAAO,IAAI,UAAU,KAAK,YAAY,MAAM;EACrD,IAAI,KAAK,WAAW,MAAM;EAC1B,KAAK,YAAY;GAChB,GAAG,KAAK;GACR,UAAU;GACV,SAAS;GACT,SAAS,KAAK,UAAU,WAAW,KAAK,IAAI,IAAI,KAAK,UAAU;EAChE;CACD;;CAGA,UAAU,SAA8B;EACvC,MAAM,YAAY,SAAiB,YAAY,KAAK,OAAO,MAAM,IAAI;EACrE,QAAQ,QAAQ,MAAhB;GACC,KAAK;IACJ,KAAK,SAAS;IACd,KAAK,QAAQ,QAAQ;IACrB,KAAK,IAAI,SAAS,WAAW,QAAQ,SAAS,OAAO,qBAAqB,qBAAqB,QAAQ,QAAQ,EAC9G,SAAS,YACV,CAAC;IACD,IAAI,KAAK,iBAAiB,KAAK,gBAAgB;IAC/C,KAAK,YAAY;IACjB;GACD,KAAK;IACJ,KAAK,MAAM,SAAS,QAAQ,QAAQ,KAAK,QAAQ,IAAI,MAAM,IAAI;IAC/D,IAAI,QAAQ,OAAO,WAAW,GAAG;IACjC,KAAK,IAAI,SAAS,SAAS,UAAU,QAAQ,MAAM,IAAI,QAAQ,OAAO,OAAO,GAAG,QAAQ,OAAO,WAAW,IAAI,UAAU,YAAY;KACnI,SAAS,QAAQ,UAAU,aAAa,aAAa;KACrD,QAAQ,QAAQ,OAAO,KAAK,UAAU,GAAG,MAAM,KAAK,GAAG,SAAS,MAAM,IAAI,GAAG;IAC9E,CAAC;IACD;GACD,KAAK;IACJ,KAAK,OAAO,OAAO;IACnB;GACD,KAAK;IACJ,KAAK,YAAY;KAAE,QAAQ,QAAQ;KAAQ,QAAQ,QAAQ;IAAO,CAAC;IACnE;GACD,KAAK;IACJ,IAAI,QAAQ,UAAU,OACrB,KAAK,IAAI,SAAS,SAAS,cAAc,QAAQ,MAAM,QAAQ,QAAQ,SAAS,EAAE,SAAS,eAAe,CAAC;SACrG,IAAI,QAAQ,UAAU,WAC5B,KAAK,IAAI,SAAS,SAAS,GAAG,QAAQ,MAAM,UAAU,SAAS,QAAQ,EAAE,KAAK,EAAE,SAAS,eAAe,CAAC;SAEzG,KAAK,IAAI,SAAS,SAAS,GAAG,QAAQ,MAAM,SAAS,SAAS,QAAQ,EAAE,EAAE,IAAI,QAAQ,MAAM,WAAW;KACtG,SAAS;KACT,QAAQ,QAAQ,MAAM;IACvB,CAAC;IAEF;GACD,KAAK;IACJ,KAAK,IAAI,SAAS,QAAQ,UAAU,MAAM,UAAU,QAAQ,UAAU,MAAM,SAAS,SAAS,cAAc,OAAO,GAAG,EACrH,SAAS,OACV,CAAC;IACD;GACD,KAAK;IACJ,IAAI,QAAQ,IAAI;KACf,KAAK,YAAY,KAAK;KACtB,MAAM,UACJ,QAAQ,SAAS,IAAI,KAAK,QAAQ,OAAO,SAAS,QAAQ,WAAW,IAAI,UAAU,aAAa,OAChG,QAAQ,UAAU,IAAI,aAAa,QAAQ,QAAQ,GAAG,QAAQ,YAAY,IAAI,UAAU,aAAa;KACvG,KAAK,IAAI,SAAS,WAAW,YAAY,QAAQ,OAAO,UAAU,QAAQ,WAAW,IAAI,YAAY,aAAa,UAAU,EAC3H,SAAS,WACV,CAAC;IACF,OACC,KAAK,IAAI,SAAS,SAAS,mCAAmC,QAAQ,WAAW;KAAE,SAAS;KAAY,QAAQ,QAAQ;IAAM,CAAC;IAEhI;GACD,KAAK;IACJ,KAAK,IAAI,OAAO,WAAW,QAAQ,KAAK,GAAG,QAAQ,MAAM;KAAE,SAAS,QAAQ;KAAS,QAAQ,QAAQ;IAAO,CAAC;IAC7G;EACF;CACD;CAEA,OAAO,SAAwD;EAC9D,MAAM,YAAY,SAAiB,YAAY,KAAK,OAAO,MAAM,IAAI;EACrE,IAAI,QAAQ,UAAU,SAAS;GAC9B,KAAK,YAAY,QAAQ;GACzB,KAAK,IAAI,SAAS,SAAS,2BAA2B,QAAQ,MAAM,OAAO,GAAG,QAAQ,MAAM,WAAW,IAAI,SAAS,WAAW;IAC9H,SAAS;IACT,QAAQ,QAAQ,MAAM,IAAI,QAAQ;GACnC,CAAC;GACD,KAAK,YAAY;GACjB;EACD;EAEA,IAAI,QAAQ,UAAU,QAAQ;GAE7B,KAAK,YAAY,CAAC;GAClB,KAAK,IAAI,SAAS,SAAS,kBAAkB,EAAE,SAAS,MAAM,CAAC;GAC/D,KAAK,YAAY;GACjB;EACD;EAEA,IAAI,QAAQ,UAAU,SAAS;GAC9B,KAAK,IAAI,SAAS,SAAS,oBAAoB,SAAS,QAAQ,IAAI,EAAE,IAAI,QAAQ,WAAW;IAAE,SAAS;IAAO,QAAQ,QAAQ;GAAM,CAAC;GACtI;EACD;EAEA,IAAI,QAAQ,UAAU,YAAY,KAAK,QAAQ,OAAO,QAAQ,IAAI;OAC7D,KAAK,QAAQ,IAAI,QAAQ,IAAI;EAClC,MAAM,OAAO,QAAQ,UAAU,aAAa,aAAa,QAAQ,UAAU,WAAW,WAAW;EACjG,KAAK,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,QAAQ,MAAM,KAAK,IAAI,EAAE,QAAQ,SAAS,QAAQ,IAAI,KAAK,EAAE,SAAS,MAAM,CAAC;CACpH;;;;;CAMA,YAAY,UAAiC;EAC5C,MAAM,WAAW,KAAK;EACtB,KAAK,YAAY;EACjB,IAAI,aAAa,MAAM;GACtB,KAAK,YAAY;GACjB,MAAM,SAAS,OAAO,KAAK,SAAS,MAAM,CAAC,CAAC;GAC5C,KAAK,IAAI,SAAS,SAAS,oBAAoB,SAAS,OAAO,OAAO,WAAW,OAAO,SAAS,WAAW,IAAI,UAAU,YAAY;IACrI,SAAS;IACT,QAAQ,SAAS,OAAO,KAAK,YAAY,gBAAgB;KAAE,MAAM,QAAQ;KAAM,MAAM,QAAQ,QAAQ;KAAG,OAAO;IAAK,CAAC,CAAC;GACvH,CAAC;GACD;EACD;EAEA,MAAM,UAAU,cAAc,UAAU,QAAQ;EAChD,IAAI,QAAQ,WAAW,GAAG;EAE1B,MAAM,SAAS,QAAQ,KAAK,WAAW,GAAG,OAAO,KAAK,GAAG,gBAAgB,MAAM,GAAG;EAClF,MAAM,OAAO,KAAK,OAAO,IAAI,UAAU,WAAW;EAClD,IAAI,SAAS,QAAQ;GACpB,KAAK,IAAI,SAAS,QAAQ,oBAAoB;IAAE,SAAS;IAAY;GAAO,CAAC;GAC7E,KAAK,gBAAgB;GACrB;EACD;EAEA,IAAI,SAAS,SAAS,CAAC,KAAK,YAAY;GACvC,KAAK,IAAI,SAAS,QAAQ,yDAAyD;IAClF,SAAS;IACT,QAAQ,CAAC,GAAG,QAAQ,SAAS,QAAQ,gCAAgC,iEAA+D;GACrI,CAAC;GACD;EACD;EAGA,KAAK,UAAU;GAAE,MAAM;GAAY,SAAS,aAAa,KAAK,SAAS,WAAW,CAAC,GAAG,OAAO;EAAE;EAC/F,KAAK,IAAI,SAAS,QAAQ,oBAAoB;GAAE,SAAS;GAAY;EAAO,CAAC;EAC7E,KAAK,YAAY;CAClB;;;;;CAMA,AAAO,QAAuC,MAAY,GAAG,MAAmD;EAC/G,OAAO,KAAK,WAAW,MAAM,GAAG,IAAI;CACrC;;CAGA,WAA0C,MAAY,GAAG,MAAmD;EAC3G,KAAK,aAAa,KAAK,WAAW,WAAW,KAAK,UAAU,MAAM,GAAG,IAAI,CAAC;EAC1E,OAAO,KAAK;CACb;;;;;;CAOA,WAA0C,MAAY,GAAG,MAAmD;EAC3G,MAAM,UAAU,KAAK,UAAU,MAAM,GAAG,IAAI;EAC5C,KAAK,aAAa,QAAQ,IAAI,CAAC,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,WAAW,MAAS;EAC9E,OAAO;CACR;;;;;CAMA,MAAM,UAAyC,MAAY,GAAG,MAAmD;EAChH,IAAI,KAAK,WAAW,MAAM;EAC1B,IAAI;GACH,MAAM,KAAK,OAAO,SAAS,MAAM,GAAG,IAAI;EACzC,SAAS,OAAO;GACf,KAAK,IAAI,SAAS,SAAS,QAAQ,KAAK,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;EAC5G;CACD;CAEA,cAAoB;EACnB,KAAK,KAAK,UAAU,KAAK,MAAM;CAChC;AACD;AAEA,SAAgB,iBAAiB,QAAgD;CAChF,OAAO,IAAI,kBAAkB;EAC5B,SAAS,QAAQ;EACjB,MAAM;GACL,GAAG,cAAc,MAAM;GACvB,GAAG,iBAAiB,MAAM;GAC1B,GAAG,iBAAiB,MAAM;GAC1B,GAAG,OAAO,IAAI;GACd,OAAO,MAAM;GACb,GAAG,OAAO,IAAI;EACf;EACA,KAAK,OAAO;EACZ,KAAK;GACJ,GAAG,QAAQ;GACX,GAAG,OAAO,IAAI;GACd,WAAW;GACX,UAAU;GACV,aAAa,QAAQ,IAAI,aAAa,SAAY,SAAa,QAAQ,IAAI,gBAAgB,QAAQ,OAAO,QAAQ,MAAM;EACzH;EACA,aAAa,OAAO,IAAI;EACxB,KAAK;CACN,CAAC;AACF;AAEA,SAAS,SAAS,IAA2B;CAC5C,OAAO,OAAO,OAAO,KAAK,OAAO,GAAG;AACrC;AAEA,SAAS,cAAc,SAA8D;CACpF,OAAO,GAAG,QAAQ,OAAO,GAAG,QAAQ,KAAK,GAAG,QAAQ,OAAO,MAAM,QAAQ,GAAG;AAC7E;AAEA,MAAM,gCAAgB,IAAI,IAAY;CAAC;CAAS;CAAS;CAAQ;CAAW;CAAQ;AAAO,CAAC;AAE5F,SAAS,WAAW,OAAyB;CAC5C,OAAO,cAAc,IAAI,KAAK,IAAK,QAAqB;AACzD;;AAGA,SAAS,aAAa,UAAoC,MAAiD;CAC1G,MAAM,OAAO,WAA0B,GAAG,OAAO,SAAS,GAAG,GAAG,OAAO,KAAK,GAAG,OAAO;CACtF,MAAM,SAAS,IAAI,IAAI,SAAS,KAAK,WAAW,CAAC,IAAI,MAAM,GAAG,MAAM,CAAC,CAAC;CACtE,KAAK,MAAM,UAAU,MAAM,OAAO,IAAI,IAAI,MAAM,GAAG,MAAM;CACzD,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC;AAC3B;AAEA,SAAgB,eAAe,QAA+B;CAC7D,QAAQ,QAAR;EACC,KAAK,WACJ,OAAO;EACR,KAAK,SACJ,OAAO;EACR,KAAK,UACJ,OAAO;EACR,KAAK,SACJ,OAAO;CACT;AACD;;;;;;;;;;ACvsBA,eAAsB,mBAAoC;CACzD,IAAI;EACH,MAAM,CAAC,SAAS,YAAY,MAAM,QAAQ,IAAI,CAAC,OAAO,WAAW,GAAG,OAAO,aAAa,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC;EAC5G,OAAO,QAAQ,WAAW,SAAS,UAAU,QAAQ,YAAY,SAAS,UAAU,cAAc,SAAS;CAC5G,QAAQ;EACP,OAAO;CACR;AACD;;;;;AAMA,eAAsB,sBAAsB,KAA8B;CACzE,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,IAAI,OAAO,aAAa,aAAa,OAAO;CAE5C,MAAM,OAAO,MAAM,iBAAiB;CACpC,IAAI,SAAS,aAAa,OAAO;CAEjC,OAAO,WAAW,KAAK,SAAS,GAAG,IAAI,IAAI,KAAK,KAAK;CACrD,OAAO,OAAO;AACf;;;;;;;;;ACpBA,IAAa,gBAAb,MAA2B;CAIT;CACC;CAJlB,UAA8B;CAE9B,AAAO,YACN,AAAgB,MAChB,AAAiB,MAChB;EAFe;EACC;CACf;CAEH,AAAO,OAAa;EACnB,IAAI,KAAK,SAAS;EAElB,UAAU,QAAQ,KAAK,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;EACjD,KAAK,UAAU,kBAAkB,KAAK,MAAM,EAAE,OAAO,IAAI,CAAC;EAE1D,KAAK,QAAQ,GAAG,eAAe,KAAK,MAAM,CAAC;EAE3C,KAAK,MAAM,SAAS,KAAK,KAAK,QAAQ,GAAG,KAAK,OAAO,KAAK;EAC1D,KAAK,KAAK,GAAG,SAAS,KAAK,MAAM;CAClC;CAEA,AAAO,QAAc;EACpB,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU;EACf,IAAI,CAAC,QAAQ;EAEb,KAAK,KAAK,IAAI,SAAS,KAAK,MAAM;EAClC,OAAO,IAAI;CACZ;CAEA,UAAU,UAA0B;EACnC,KAAK,SAAS,MAAM,GAAG,cAAc,KAAK,EAAE,GAAG;CAChD;AACD;;AAGA,SAAgB,cAAc,OAAyB;CAEtD,OAAO,CAAC,GADQ,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,YAAY,EAAE,GAAG,MAAM,MAAM,OAAO,CAAC,EAAE,GAAG,MAAM,QAAQ,OAAO,EAAE,EAAE,GAAG,yBAAyB,MAAM,IAAI,KAChI,IAAI,MAAM,UAAU,CAAC,EAAC,CAAE,KAAK,SAAS,KAAK,yBAAyB,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI;AACtG;AAEA,MAAM,WAAW;;;;;AAMjB,SAAgB,WAAW,WAAmB,MAAc,sBAAY,IAAI,KAAK,GAAW;CAC3F,IAAI,WAAqB,CAAC;CAC1B,IAAI;EACH,WAAW,YAAY,SAAS,CAAC,CAC/B,QAAQ,SAAS,SAAS,KAAK,IAAI,CAAC,CAAC,CACrC,KAAK;CACR,QAAQ,CAER;CAEA,KAAK,MAAM,QAAQ,SAAS,MAAM,GAAG,KAAK,IAAI,GAAG,SAAS,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GACxF,OAAO,KAAK,WAAW,IAAI,GAAG,EAAE,OAAO,KAAK,CAAC;CAG9C,OAAO,KAAK,WAAW,OAAO,IAAI,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG,EAAE,KAAK;AAC3E;;;;AC5CA,MAAM,UAAU;CAAC;CAAQ;CAAa;AAAO;AAC7C,MAAM,SAAS;CAAC;CAAS;CAAS;CAAQ;CAAQ;AAAO;AAEzD,eAAsB,OAAO,SAAwC;CACpE,IAAI,QAAQ,UAAU,UAAa,CAAC,eAAe,QAAQ,KAAK,GAC/D,MAAM,eAAe,cAAc;EAAE,OAAO,QAAQ;EAAO,QAAQ,eAAe,KAAK,IAAI;CAAE,CAAC;CAG/F,MAAM,SAAS,QAAQ;CACvB,IAAI,WAAW,UAAa,CAAC,QAAQ,SAAS,MAAM,GACnD,MAAM,eAAe,eAAe;EAAE,QAAQ;EAAY,OAAO,QAAQ;EAAS,SAAS,QAAQ,KAAK,IAAI;CAAE,CAAC;CAGhH,IAAI,QAAQ,UAAU,UAAa,CAAC,WAAW,QAAQ,KAAK,GAC3D,MAAM,eAAe,eAAe;EAAE,QAAQ;EAAW,OAAO,QAAQ;EAAO,SAAS,OAAO,KAAK,IAAI;CAAE,CAAC;CAI5G,QAAQ,IAAI,WAAW;CACvB,MAAM,UAAU,MAAM,YAAY;EAAE,KAAK,WAAW,OAAO;EAAG,YAAY,QAAQ;EAAQ,KAAK;GAAE,GAAG,QAAQ;GAAK,UAAU;EAAc;CAAE,CAAC;CAC5I,MAAM,QAAQ,QAAQ;CAEtB,MAAM,SAAS,MAAM,iBAAiB,eAAe,MAAM,gBAAgB,QAAQ,QAAQ,KAAK,CAAC,CAAC;CAClG,MAAM,OAAO,kBAAkB,EAAE,KAAK,QAAQ,IAAI,CAAC;CACnD,MAAM,QAAQ,eAAe;CAC7B,MAAM,QAAQ,oBAAoB;EAAE,MAAM,QAAQ;EAAO,OAAO,eAAe;CAAE,CAAC;CAElF,MAAM,UAAU,MAAM,cAAc,QAAQ,KAAK;CACjD,MAAM,MAAM,SAAS,mBAAmB,SAAS,MAAM;CACvD,MAAM,UAAU,IAAI,WAAW,QAAQ;EAAE;EAAS;CAAM,CAAC;CACzD,MAAM,eAAe,SAAS,SAAS,QAAQ,IAAI,SAAS,QAAQ,IAAI,GAAG,EAAE,YAAY,MAAM,CAAC;CAEhG,MAAM,WAAW,CAChB,GAAI,OAAO,IAAI,UAAU,CAAC,OAAO,IAAI,OAAO,IAAI,CAAC,GACjD,GAAI,OAAO,IAAI,MAAM,MAAM,CAAC,WAAW,OAAO,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CACvF,CAAC,CAAC,KAAK,SAAS,IAAI,cAAc,MAAM,QAAQ,IAAI,CAAC;CACrD,KAAK,MAAM,QAAQ,UAAU,KAAK,KAAK;CACvC,MAAM,SAAS,eAAe,QAAQ;EAAE,UAAU,QAAQ;EAAS,OAAO,QAAQ;CAAM,CAAC;CACzF,MAAM,WACL,SAAS,SACL,MAAM,OAAO,qBAAoB,CAAE,kBAAkB,SAAS;EAC/D;EACA;EACA;EACA;EACA,eAAe,qBAAqB;EACpC,cAAc,YAAY;GACzB,IAAI,CAAC,UAAU,OAAO,GAAG,QAAQ,IAAI,SAAS,QAAQ,sCAAsC;EAC7F;CACD,CAAC,KACC,MAAM,OAAO,uBAAsB,CAAE,oBAAoB,SAAS;EAAE;EAAO;CAAO,CAAC;CAExF,IAAI,UAAiC;CACrC,MAAM,YAAY,SAAmC;EACpD,aAAa,YAAY;GACxB,SAAS,KAAK;GAGd,MAAM,QAAQ,QAAQ,aAAa,MAAM;GACzC,MAAM,QAAQ,KAAK;GACnB,KAAK,MAAM,QAAQ,UAAU,KAAK,MAAM;GACxC,QAAQ,KAAK,IAAI;EAClB,EAAC,CAAE;EACH,OAAO;CACR;CAGA,IAAI,YAAY;CAChB,MAAM,YAAY,eAAyB;EAC1C,IAAI,WAAW;GACd,QAAQ,OAAO,MAAM,uBAAuB;GAC5C,QAAQ,KAAK;GACb,QAAQ,KAAK,IAAI;EAClB;EAEA,YAAY;EACZ,AAAK,SAAS,IAAI;EAClB,QAAQ,OAAO,MAAM,iEAAiE;CACvF;CAEA,QAAQ,GAAG,UAAU,SAAS,SAAS,WAAW,CAAC;CACnD,QAAQ,GAAG,WAAW,SAAS,SAAS,UAAU,CAAC;CACnD,QAAQ,GAAG,UAAU,SAAS,SAAS,UAAU,CAAC;CAClD,IAAI,QAAQ,aAAa,SAAS,QAAQ,GAAG,iBAAiB,KAAK,QAAQ,QAAQ,QAAQ,CAAC;CAE5F,MAAM,WAAW,SAAS,MAAM,CAAC,CAAC,WAAW,SAAS,SAAS,EAAE,CAAC;CAClE,IAAI;EACH,MAAM,eAAe,QAAQ,KAAK;EAClC,MAAM,QAAQ,MAAM;EACpB,MAAM,MAAM,SAAS,aAAa,MAAM;CACzC,SAAS,OAAO;EACf,QAAQ,IAAI,SAAS,SAAS,iBAAiB,QAAQ,MAAM,kBAAkB,OAAO,OAAO,IAAI,IAAI,OAAO,KAAK,CAAC;EAClH,MAAM,SAAS,SAAS,KAAK;CAC9B;CACA,MAAM;AACP;;;;;;AAOA,eAAe,iBAAiB,QAA2D;CAC1F,IAAI,CAAC,OAAO,IAAI,KAAK,OAAO;CAE5B,MAAM,MAAM,MAAM,sBAAsB,OAAO,IAAI,GAAG;CACtD,OAAO,QAAQ,OAAO,IAAI,MAAM,SAAS;EAAE,GAAG;EAAQ,KAAK;GAAE,GAAG,OAAO;GAAK;EAAI;CAAE;AACnF;AAEA,kBAAe,cAAc;CAC5B,MAAM;EACL,MAAM;EACN,aAAa;CACd;CACA,MAAM;EACL,GAAG;EACH,KAAK;GACJ,MAAM;GACN,aAAa;GACb,SAAS;EACV;EACA,OAAO;GACN,MAAM;GACN,aAAa,uCAAuC,eAAe,KAAK,IAAI,EAAE;EAC/E;EACA,QAAQ;GACP,MAAM;GACN,aAAa,iCAAiC,QAAQ,KAAK,IAAI,EAAE;EAClE;EACA,SAAS;GACR,MAAM;GACN,aAAa;EACd;EACA,OAAO;GACN,MAAM;GACN,aAAa,+DAA+D,OAAO,KAAK,IAAI,EAAE;EAC/F;CACD;CACA,MAAM,IAAI,EAAE,MAAM,WAAW;EAC5B,MAAM,UAAU,YAAY,SAAS,SAAS;EAC9C,MAAM,OAAO;GACZ,QAAQ,KAAK;GACb,KAAK,KAAK;GACV,KAAK,KAAK,MAAM,SAAY;GAC5B,OAAO,KAAK;GACZ,QAAQ,KAAK;GACb,SAAS,QAAQ,SAAS,IAAI,UAAU;GACxC,OAAO,KAAK;EACb,CAAC;CACF;AACD,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { t as cliDiagnostics } from "./diagnostics-BysbNnPD.js";
|
|
|
4
4
|
import { t as findInstalledVersion } from "./project-zlQStwsf.js";
|
|
5
5
|
import { a as modulesPreloadWarning, d as resolveCwd, i as readProjectEnv, n as loadProject, r as withProjectEnv, t as applyEnvOptions, u as projectArgs } from "./hooks-Cy6F2bhH.js";
|
|
6
6
|
import { t as prepareProject } from "./_shared-BIQK0Bhm.js";
|
|
7
|
-
import { a as isAtLeast, c as endpointUrl, l as readDiscordCredentials, t as BRIDGE_FRAMEWORK_VERSION } from "./bridge-
|
|
7
|
+
import { a as isAtLeast, c as endpointUrl, l as readDiscordCredentials, t as BRIDGE_FRAMEWORK_VERSION } from "./bridge-BegCt_n2.js";
|
|
8
8
|
import { defineCommand } from "citty";
|
|
9
9
|
import { existsSync, readFileSync } from "node:fs";
|
|
10
10
|
import { delimiter, join } from "node:path";
|
|
@@ -331,4 +331,4 @@ var doctor_default = defineCommand({
|
|
|
331
331
|
|
|
332
332
|
//#endregion
|
|
333
333
|
export { doctor_default as default };
|
|
334
|
-
//# sourceMappingURL=doctor-
|
|
334
|
+
//# sourceMappingURL=doctor-DEtAAknK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-DkVUwgDd.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, readDiscordCredentials } from '../dev/tunnel.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 { 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, or press t in `stars dev`.'\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: 'cloudflared quick 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\tprepared.tsconfig.status === 'outdated' ? show(prepared.tsconfig.path) : null,\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} 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-DEtAAknK.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, readDiscordCredentials } from '../dev/tunnel.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 { 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, or press t in `stars dev`.'\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: 'cloudflared quick 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\tprepared.tsconfig.status === 'outdated' ? show(prepared.tsconfig.path) : null,\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} 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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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as runMain } from "./run-
|
|
1
|
+
import { t as runMain } from "./run-CHqwvZXf.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-BysbNnPD.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as commands } from "./commands-
|
|
1
|
+
import { t as commands } from "./commands-CSB-pjgs.js";
|
|
2
2
|
import { t as readOwnPackageJson } from "./version-BzclCE19.js";
|
|
3
3
|
import { n as exitCodeOf, r as formatError } from "./errors-COFWKKKw.js";
|
|
4
4
|
import { defineCommand, renderUsage, runCommand } from "citty";
|
|
@@ -52,4 +52,4 @@ async function runMain(argv = process.argv.slice(2)) {
|
|
|
52
52
|
|
|
53
53
|
//#endregion
|
|
54
54
|
export { runMain as t };
|
|
55
|
-
//# sourceMappingURL=run-
|
|
55
|
+
//# sourceMappingURL=run-CHqwvZXf.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-
|
|
1
|
+
{"version":3,"file":"run-CHqwvZXf.js","names":["_runCommand"],"sources":["../src/main.ts","../src/run.ts"],"sourcesContent":["import { defineCommand, type CommandDef } from 'citty';\nimport { commands } from './commands/index.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\nconst packageJson = readOwnPackageJson();\n\nexport const main = defineCommand({\n\tmeta: {\n\t\tname: 'stars',\n\t\tversion: packageJson.version,\n\t\tdescription: packageJson.description\n\t},\n\tsubCommands: commands\n}) as AnyCommand;\n","import { renderUsage, runCommand as _runCommand, type CommandDef } from 'citty';\nimport process from 'node:process';\nimport { commands } from './commands/index.js';\nimport { main } from './main.js';\nimport { exitCodeOf, formatError } from './utils/errors.js';\nimport { readOwnPackageJson } from './utils/version.js';\n\n// citty types each command by its own arguments; the registry needs the erased form.\n// oxlint-disable-next-line typescript/no-explicit-any\ntype AnyCommand = CommandDef<any>;\n\n/**\n * Walks the command names of `stars <command> [<subcommand>] --help` so nested commands (`stars commands clean`)\n * render their own usage instead of their parent's.\n */\nasync function resolveHelpTarget(names: string[]): Promise<AnyCommand | null> {\n\tconst [first, ...rest] = names;\n\tif (!first || !Object.hasOwn(commands, first)) return null;\n\n\tlet command = await commands[first]!();\n\tfor (const name of rest) {\n\t\tconst resolved = typeof command.subCommands === 'function' ? await command.subCommands() : await command.subCommands;\n\t\tconst child: unknown = (resolved as Record<string, unknown> | undefined)?.[name];\n\t\tif (!child) break;\n\t\tcommand = (typeof child === 'function' ? await child() : child) as AnyCommand;\n\t}\n\n\treturn command;\n}\n\nexport async function runMain(argv: string[] = process.argv.slice(2)): Promise<void> {\n\tif (argv.includes('--version') || argv.includes('-V')) {\n\t\tconsole.log(readOwnPackageJson().version);\n\t\tprocess.exit(0);\n\t}\n\n\tif (argv.length === 0 || argv.includes('--help') || argv.includes('-h')) {\n\t\t// citty's showUsage logs through consola, which is muted in some environments; render and print directly.\n\t\tconst command = await resolveHelpTarget(argv.filter((argument) => !argument.startsWith('-')));\n\t\tconsole.log(await (command ? renderUsage(command, main) : renderUsage(main)));\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tawait _runCommand(main, { rawArgs: argv });\n\t} catch (error) {\n\t\tconsole.error(await formatError(error));\n\t\tprocess.exit(exitCodeOf(error));\n\t}\n}\n"],"mappings":";;;;;;;AAQA,MAAM,cAAc,mBAAmB;AAEvC,MAAa,OAAO,cAAc;CACjC,MAAM;EACL,MAAM;EACN,SAAS,YAAY;EACrB,aAAa,YAAY;CAC1B;CACA,aAAa;AACd,CAAC;;;;;;;;ACFD,eAAe,kBAAkB,OAA6C;CAC7E,MAAM,CAAC,OAAO,GAAG,QAAQ;CACzB,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,UAAU,KAAK,GAAG,OAAO;CAEtD,IAAI,UAAU,MAAM,SAAS,MAAM,CAAE;CACrC,KAAK,MAAM,QAAQ,MAAM;EAExB,MAAM,SADW,OAAO,QAAQ,gBAAgB,aAAa,MAAM,QAAQ,YAAY,IAAI,MAAM,QAAQ,YACzE,GAA2C;EAC3E,IAAI,CAAC,OAAO;EACZ,UAAW,OAAO,UAAU,aAAa,MAAM,MAAM,IAAI;CAC1D;CAEA,OAAO;AACR;AAEA,eAAsB,QAAQ,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAkB;CACpF,IAAI,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;EACtD,QAAQ,IAAI,mBAAmB,CAAC,CAAC,OAAO;EACxC,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;EAExE,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,aAAa,CAAC,SAAS,WAAW,GAAG,CAAC,CAAC;EAC5F,QAAQ,IAAI,OAAO,UAAU,YAAY,SAAS,IAAI,IAAI,YAAY,IAAI,EAAE;EAC5E,QAAQ,KAAK,CAAC;CACf;CAEA,IAAI;EACH,MAAMA,WAAY,MAAM,EAAE,SAAS,KAAK,CAAC;CAC1C,SAAS,OAAO;EACf,QAAQ,MAAM,MAAM,YAAY,KAAK,CAAC;EACtC,QAAQ,KAAK,WAAW,KAAK,CAAC;CAC/B;AACD"}
|
package/package.json
CHANGED