{"version":3,"file":"exec.mjs","names":[],"sources":["../../src/core/exec.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type {\n  AnyPadroneCommand,\n  AnyPadroneProgram,\n  InterceptorExecuteContext,\n  InterceptorExecuteResult,\n  InterceptorParseContext,\n  InterceptorParseResult,\n  InterceptorPipelinePhase,\n  InterceptorRouteContext,\n  InterceptorValidateContext,\n  InterceptorValidateResult,\n  PadroneActionContext,\n  PadroneEvalPreferences,\n  RegisteredInterceptor,\n  ResolvedInterceptor,\n} from '../types/index.ts';\nimport { getCommandRuntime } from './commands.ts';\nimport { RoutingError, SignalError, ValidationError } from './errors.ts';\nimport { resolveRegisteredInterceptors, runInterceptorChain, wrapWithCommandLifecycle, wrapWithLifecycle } from './interceptors.ts';\nimport { errorResult, noop, thenMaybe, warnIfUnexpectedAsync, withDrain } from './results.ts';\nimport { buildCommandArgs, formatIssueMessages, validateCommandArgs } from './validate.ts';\n\nexport type ExecContext = {\n  rootCommand: AnyPadroneCommand;\n  builder: AnyPadroneProgram;\n  parseCommandFn: (input: string | undefined) => {\n    command: AnyPadroneCommand;\n    rawArgs: Record<string, unknown>;\n    args: string[];\n    unmatchedTerms: string[];\n  };\n  collectInterceptorsFn: (cmd: AnyPadroneCommand) => RegisteredInterceptor[];\n};\n\n/**\n * Collects registered interceptors from the command's parent chain (root → ... → target).\n * Root/program interceptors come first (outermost), target command's interceptors last (innermost).\n */\nexport function collectInterceptors(cmd: AnyPadroneCommand, rootCommand: AnyPadroneCommand): RegisteredInterceptor[] {\n  const chain: RegisteredInterceptor[][] = [];\n  let current: AnyPadroneCommand | undefined = cmd;\n  while (current) {\n    const isTarget = current === cmd;\n    if (!current.parent) {\n      if (rootCommand.interceptors?.length) {\n        const isRootTarget = cmd === rootCommand || !cmd.parent;\n        chain.unshift(isRootTarget ? rootCommand.interceptors : rootCommand.interceptors.filter((i) => i.meta.inherit !== false));\n      }\n    } else {\n      if (current.interceptors?.length) {\n        chain.unshift(isTarget ? current.interceptors : current.interceptors.filter((i) => i.meta.inherit !== false));\n      }\n    }\n    current = current.parent;\n  }\n  return chain.flat();\n}\n\n/** Wraps an error into a result, preserving any signal info from the pipeline. */\nexport function errorResultWithSignal(err: unknown) {\n  const result = errorResult(err);\n  if (err instanceof SignalError) {\n    (result as any).signal = err.signal;\n    (result as any).exitCode = err.exitCode;\n  }\n  return result;\n}\n\n/** Resolve context by walking the command parent chain and applying transforms from root to target. */\nfunction resolveContext(command: AnyPadroneCommand, initialContext: unknown): unknown {\n  const chain: AnyPadroneCommand[] = [];\n  let current: AnyPadroneCommand | undefined = command;\n  while (current) {\n    chain.unshift(current);\n    current = current.parent;\n  }\n  let resolved = initialContext;\n  for (const cmd of chain) {\n    if (cmd.contextTransform) resolved = cmd.contextTransform(resolved);\n  }\n  return resolved;\n}\n\n/** Validate parse result — reject unmatched terms when the command doesn't accept positional args. */\nfunction validateParseResult(\n  parseResult: { command: AnyPadroneCommand; rawArgs: Record<string, unknown>; args: string[]; unmatchedTerms: string[] },\n  rootCommand: AnyPadroneCommand,\n): InterceptorParseResult {\n  const { command, rawArgs, args, unmatchedTerms } = parseResult;\n\n  if (unmatchedTerms.length > 0) {\n    const hasPositionalConfig = command.meta?.positional && command.meta.positional.length > 0;\n    if (!hasPositionalConfig) {\n      const isRootCommand = command === rootCommand;\n      const commandDisplayName = command.name || command.aliases?.[0] || command.path || '(default)';\n      const errorMsg = isRootCommand\n        ? `Unknown command: ${unmatchedTerms[0]}`\n        : `Unexpected arguments for '${commandDisplayName}': ${unmatchedTerms.join(' ')}`;\n\n      throw new RoutingError(errorMsg, { command: command.path || command.name });\n    }\n  }\n\n  return { command, rawArgs, positionalArgs: args };\n}\n\n/** Handle validation issues based on error mode: throw (hard) or return result with issues (soft). */\nfunction handleValidationIssues(argsResult: StandardSchemaV1.FailureResult, command: AnyPadroneCommand, errorMode: 'soft' | 'hard') {\n  if (errorMode === 'hard') {\n    const issueMessages = formatIssueMessages(argsResult.issues);\n    throw new ValidationError(`Validation error:\\n${issueMessages}`, argsResult.issues as any, {\n      command: command.path || command.name,\n    });\n  }\n\n  return withDrain({\n    command: command as any,\n    args: undefined,\n    argsResult,\n    result: undefined,\n  });\n}\n\n/**\n * Core execution logic shared by eval() and cli().\n * errorMode controls validation error behavior:\n * - 'soft': return result with issues (eval behavior)\n * - 'hard': print error + help and throw (cli-without-input behavior)\n */\nexport function execCommand(\n  resolvedInput: string | undefined,\n  ctx: ExecContext,\n  evalOptions?: PadroneEvalPreferences,\n  errorMode: 'soft' | 'hard' = 'soft',\n  caller: PadroneActionContext['caller'] = 'eval',\n) {\n  const { rootCommand, parseCommandFn, collectInterceptorsFn } = ctx;\n  const baseRuntime = getCommandRuntime(rootCommand);\n  const runtime = evalOptions?.runtime\n    ? Object.assign({}, baseRuntime, Object.fromEntries(Object.entries(evalOptions.runtime).filter(([, v]) => v !== undefined)))\n    : baseRuntime;\n\n  // Inert signal — the signal extension overrides this via next({ signal }) in the start phase.\n  const inertSignal = new AbortController().signal;\n\n  // Pipeline state accumulated as phases complete — propagated to error/shutdown contexts.\n  const pipelineState: { phase: InterceptorPipelinePhase; rawArgs?: Record<string, unknown>; positionalArgs?: string[]; args?: unknown } = {\n    phase: 'start',\n  };\n\n  const initialContext = evalOptions?.context;\n\n  // Factory resolution cache — ensures each factory is called at most once per execution,\n  // so root interceptor closures are shared when they appear in both root and command chains.\n  const factoryCache = new Map<RegisteredInterceptor, ResolvedInterceptor>();\n  const rootRegistered = rootCommand.interceptors ?? [];\n  const rootInterceptors = resolveRegisteredInterceptors(rootRegistered, factoryCache);\n  const rootInterceptorSet = new Set(rootInterceptors);\n\n  const runPipeline = (signal: AbortSignal, pipelineContext: unknown) => {\n    // ── Phase 1: Parse ──────────────────────────────────────────────────\n    const parseCtx: InterceptorParseContext = {\n      input: resolvedInput,\n      command: rootCommand,\n      signal,\n      context: pipelineContext as object,\n      runtime,\n      program: ctx.builder,\n      caller,\n    };\n\n    const coreParse = (parseCtx: InterceptorParseContext): InterceptorParseResult =>\n      validateParseResult(parseCommandFn(parseCtx.input), rootCommand);\n\n    const parsedOrPromise = runInterceptorChain('parse', rootInterceptors, parseCtx, coreParse);\n\n    // ── Phases 2 & 3 chained after parse ────────────────────────────────\n    const continueAfterParse = (parsed: InterceptorParseResult) => {\n      const { command } = parsed;\n      pipelineState.phase = 'parse';\n      pipelineState.rawArgs = parsed.rawArgs;\n      pipelineState.positionalArgs = parsed.positionalArgs;\n      const commandInterceptors = resolveRegisteredInterceptors(collectInterceptorsFn(command), factoryCache);\n      const commandOnlyInterceptors = commandInterceptors.filter((i) => !rootInterceptorSet.has(i));\n      const context = resolveContext(command, pipelineContext);\n\n      // ── Phase 2: Route ──────────────────────────────────────────────\n      const routeCtx: InterceptorRouteContext = {\n        ...parseCtx,\n        command,\n        rawArgs: parsed.rawArgs,\n        positionalArgs: parsed.positionalArgs,\n        context: context as object,\n      };\n\n      const routedOrPromise = runInterceptorChain('route', commandInterceptors, routeCtx, () => {});\n\n      const continueAfterRoute = () => {\n        pipelineState.phase = 'route';\n        const runValidateAndExecute = () => {\n          // ── Phase 3: Validate ───────────────────────────────────────────\n          const validateCtx: InterceptorValidateContext = {\n            ...parseCtx,\n            command,\n            rawArgs: parsed.rawArgs,\n            positionalArgs: parsed.positionalArgs,\n            context: context as object,\n            evalInteractive: evalOptions?.interactive,\n          };\n\n          const coreValidate = (\n            validateCtx: InterceptorValidateContext,\n          ): InterceptorValidateResult | Promise<InterceptorValidateResult> => {\n            const { args: preprocessedArgs, issues } = buildCommandArgs(\n              validateCtx.command,\n              validateCtx.rawArgs,\n              validateCtx.positionalArgs,\n            );\n            if (issues) return { args: undefined, argsResult: { issues } as any };\n            const validated = validateCommandArgs(validateCtx.command, preprocessedArgs);\n            return thenMaybe(validated, (v) => v as InterceptorValidateResult);\n          };\n\n          const validatedOrPromise = runInterceptorChain('validate', commandInterceptors, validateCtx, coreValidate);\n\n          // ── Phase 3: Execute (or handle validation errors) ──────────────\n          const continueAfterValidate = (v: InterceptorValidateResult) => {\n            pipelineState.phase = 'validate';\n            pipelineState.args = v.args;\n            if (v.argsResult?.issues) return handleValidationIssues(v.argsResult as StandardSchemaV1.FailureResult, command, errorMode);\n\n            const executeCtx: InterceptorExecuteContext = {\n              ...validateCtx,\n              args: v.args,\n            };\n\n            const coreExecute = (executeCtx: InterceptorExecuteContext): InterceptorExecuteResult => {\n              const handler = command.action ?? noop;\n              const effectiveRuntime = executeCtx.runtime;\n              const actionCtx: PadroneActionContext = {\n                runtime: effectiveRuntime,\n                command: executeCtx.command,\n                program: ctx.builder as any,\n                signal: executeCtx.signal,\n                context: executeCtx.context,\n                caller,\n              };\n              const result = handler(executeCtx.args as any, actionCtx);\n              return { result };\n            };\n\n            pipelineState.phase = 'execute';\n            const executedOrPromise = runInterceptorChain('execute', commandInterceptors, executeCtx, coreExecute);\n\n            return thenMaybe(executedOrPromise, (e) => {\n              const finalize = (result: unknown) =>\n                withDrain({\n                  command: command as any,\n                  args: v.args,\n                  argsResult: v.argsResult,\n                  result,\n                });\n\n              if (e.result instanceof Promise) return e.result.then(finalize);\n              return finalize(e.result);\n            });\n          };\n\n          return thenMaybe(warnIfUnexpectedAsync(validatedOrPromise, command), continueAfterValidate) as any;\n        };\n\n        return wrapWithCommandLifecycle(\n          commandOnlyInterceptors,\n          command,\n          resolvedInput,\n          runValidateAndExecute,\n          (result) => withDrain({ command: command as any, args: undefined, argsResult: undefined, result }),\n          signal,\n          context,\n          runtime,\n          ctx.builder,\n          caller,\n          pipelineState,\n        );\n      };\n\n      return thenMaybe(routedOrPromise, continueAfterRoute) as any;\n    };\n\n    return thenMaybe(parsedOrPromise, continueAfterParse) as any;\n  };\n\n  return wrapWithLifecycle(\n    rootInterceptors,\n    rootCommand,\n    resolvedInput,\n    runPipeline,\n    (result) => withDrain({ command: rootCommand, args: undefined, argsResult: undefined, result }),\n    inertSignal,\n    initialContext,\n    runtime,\n    ctx.builder,\n    caller,\n    pipelineState,\n  ) as any;\n}\n"],"mappings":"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"}