{"version":3,"file":"default-runtime.mjs","names":[],"sources":["../../src/core/default-runtime.ts"],"sourcesContent":["import { readStreamAsText } from '../util/stream.ts';\nimport type {\n  InteractiveMode,\n  InteractivePromptConfig,\n  PadroneRuntime,\n  PadroneSignal,\n  ReplSessionConfig,\n  ResolvedPadroneRuntime,\n} from './runtime.ts';\nimport { REPL_SIGINT } from './runtime.ts';\n\n/**\n * Default terminal prompt implementation powered by Enquirer.\n * Lazily imported to avoid loading Enquirer when not needed.\n */\nasync function defaultTerminalPrompt(config: InteractivePromptConfig): Promise<unknown> {\n  const Enquirer = (await import('enquirer')).default;\n\n  const question: Record<string, unknown> = {\n    type: config.type,\n    name: config.name,\n    message: config.message,\n  };\n\n  if (config.default !== undefined) {\n    question.initial = config.default;\n  }\n\n  if (config.choices) {\n    question.choices = config.choices.map((c) => ({\n      name: String(c.value),\n      message: c.label,\n    }));\n  }\n\n  const response = (await Enquirer.prompt(question as any)) as Record<string, unknown>;\n  return response[config.name];\n}\n\nexport function createTerminalReplSession(config: ReplSessionConfig) {\n  // History accumulates across per-call interfaces, giving us\n  // up/down arrow navigation without a persistent stdin listener\n  // that would conflict with Enquirer or other stdin consumers.\n  let history: string[] = config.history ? [...config.history] : [];\n  let currentCompleter = config.completer;\n\n  return {\n    /** Update the tab completer (e.g. when REPL scope changes). Takes effect on the next question. */\n    set completer(fn: ((line: string) => [string[], string]) | undefined) {\n      currentCompleter = fn;\n    },\n    async question(prompt: string): Promise<string | typeof REPL_SIGINT | null> {\n      const { createInterface } = await import('node:readline');\n      const opts: Record<string, unknown> = {\n        input: process.stdin,\n        output: process.stdout,\n        terminal: true,\n        history: [...history],\n        historySize: Math.max(history.length, 1000),\n      };\n      if (currentCompleter) {\n        opts.completer = currentCompleter;\n      }\n      const rl = createInterface(opts as any);\n\n      return new Promise((resolve) => {\n        let resolved = false;\n        const settle = (value: string | typeof REPL_SIGINT | null) => {\n          if (resolved) return;\n          resolved = true;\n          rl.close();\n          resolve(value);\n        };\n\n        rl.question(prompt, (answer) => {\n          // Grab updated history (includes the new entry) before closing.\n          if (Array.isArray((rl as any).history)) history = [...(rl as any).history];\n          settle(answer);\n        });\n        // Ctrl+C: cancel current line, print newline, resolve SIGINT sentinel.\n        rl.once('SIGINT', () => {\n          process.stdout.write('\\n');\n          settle(REPL_SIGINT);\n        });\n        // EOF (Ctrl+D) fires close without the question callback.\n        rl.once('close', () => {\n          // Write newline so zsh doesn't show '%' (partial-line indicator).\n          process.stdout.write('\\n');\n          settle(null);\n        });\n      });\n    },\n    close() {\n      // No persistent interface to clean up.\n    },\n  };\n}\n\n/**\n * Auto-detect interactive mode when not explicitly set.\n * Returns 'disabled' in CI environments or non-TTY contexts, 'supported' otherwise.\n */\nfunction detectInteractiveMode(): InteractiveMode {\n  if (typeof process === 'undefined') return 'disabled';\n  if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return 'disabled';\n  if (!process.stdout?.isTTY) return 'disabled';\n  return 'supported';\n}\n\n/**\n * Creates a default stdin reader from `process.stdin`.\n * Only created when a command actually declares a `stdin` meta field.\n */\nfunction createDefaultStdin(): NonNullable<PadroneRuntime['stdin']> {\n  return {\n    get isTTY() {\n      // process.stdin.isTTY is `true` when interactive terminal, `undefined` when piped/redirected.\n      // Node.js never sets it to `false` — it's either `true` or absent.\n      if (typeof process === 'undefined') return true;\n      return process.stdin?.isTTY === true;\n    },\n    async text() {\n      if (typeof process === 'undefined') return '';\n      return readStreamAsText(process.stdin);\n    },\n    async *lines() {\n      if (typeof process === 'undefined') return;\n      const { createInterface } = await import('node:readline');\n      const rl = createInterface({ input: process.stdin });\n      try {\n        for await (const line of rl) {\n          yield line;\n        }\n      } finally {\n        rl.close();\n      }\n    },\n  };\n}\n\n/**\n * Default signal listener that wires to `process.on(signal)`.\n * Returns an unsubscribe function that removes all listeners.\n */\nfunction defaultOnSignal(callback: (signal: PadroneSignal) => void): () => void {\n  if (typeof process === 'undefined') return () => {};\n  const signals: PadroneSignal[] = ['SIGINT', 'SIGTERM', 'SIGHUP'];\n  const handlers = new Map<PadroneSignal, () => void>();\n  for (const sig of signals) {\n    const handler = () => callback(sig);\n    handlers.set(sig, handler);\n    process.on(sig, handler);\n  }\n  return () => {\n    for (const [sig, handler] of handlers) {\n      process.removeListener(sig, handler);\n    }\n  };\n}\n\n/**\n * Creates the default Node.js/Bun runtime.\n */\nfunction defaultExit(code: number): never {\n  if (typeof process !== 'undefined') process.exit(code);\n  throw new Error(`Exit with code ${code}`);\n}\n\nfunction getTerminalInfo(): PadroneRuntime['terminal'] {\n  if (typeof process === 'undefined') return undefined;\n  return {\n    get columns() {\n      return process.stdout?.columns;\n    },\n    get isTTY() {\n      return process.stdout?.isTTY === true;\n    },\n  };\n}\n\nexport function createDefaultRuntime(): ResolvedPadroneRuntime {\n  return {\n    output: (...args) => console.log(...args),\n    error: (text) => console.error(text),\n    argv: () => (typeof process !== 'undefined' ? process.argv.slice(2) : []),\n    env: () => (typeof process !== 'undefined' ? (process.env as Record<string, string | undefined>) : {}),\n    format: 'auto',\n    prompt: defaultTerminalPrompt,\n    interactive: detectInteractiveMode(),\n    onSignal: defaultOnSignal,\n    terminal: getTerminalInfo(),\n    exit: defaultExit,\n  };\n}\n\n/**\n * Returns the stdin abstraction: custom runtime stdin > default process.stdin.\n * Returns `undefined` when no custom stdin is provided and process.stdin is not piped.\n */\nexport function resolveStdin(partial?: PadroneRuntime): NonNullable<PadroneRuntime['stdin']> | undefined {\n  if (partial?.stdin) return partial.stdin;\n  const defaultStdin = createDefaultStdin();\n  // Only use default stdin if it's actually piped (isTTY === false).\n  // This avoids accidentally blocking on stdin in tests/CI.\n  if (defaultStdin.isTTY) return undefined;\n  return defaultStdin;\n}\n\n/**\n * Like `resolveStdin`, but always returns a stdin source even when it's a TTY.\n * Used for async streams which support interactive (non-piped) input.\n */\nexport function resolveStdinAlways(partial?: PadroneRuntime): NonNullable<PadroneRuntime['stdin']> {\n  if (partial?.stdin) return partial.stdin;\n  return createDefaultStdin();\n}\n\n/**\n * Merges a partial runtime with the default runtime.\n */\nexport function resolveRuntime(partial?: PadroneRuntime): ResolvedPadroneRuntime {\n  const defaults = createDefaultRuntime();\n  if (!partial) return defaults;\n  return {\n    output: partial.output ?? defaults.output,\n    error: partial.error ?? defaults.error,\n    argv: partial.argv ?? defaults.argv,\n    env: partial.env ?? defaults.env,\n    format: partial.format ?? defaults.format,\n    interactive: partial.interactive ?? defaults.interactive,\n    prompt: partial.prompt ?? defaults.prompt,\n    readLine: partial.readLine ?? defaults.readLine,\n    stdin: partial.stdin,\n    theme: partial.theme,\n    onSignal: partial.onSignal ?? defaults.onSignal,\n    terminal: partial.terminal ?? defaults.terminal,\n    exit: partial.exit ?? defaults.exit,\n  };\n}\n"],"mappings":"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"}