{"version":3,"file":"validate.mjs","names":[],"sources":["../../src/core/validate.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { AnyPadroneCommand, InterceptorValidateResult } from '../types/index.ts';\nimport { coerceArgs, detectUnknownArgs, extractSchemaMetadata, getJsonSchema, parsePositionalConfig, preprocessArgs } from './args.ts';\nimport { getCommandRuntime } from './commands.ts';\nimport { getNestedValue, parseCliInputToParts, setNestedValue } from './parse.ts';\nimport { thenMaybe } from './results.ts';\n\n/**\n * Parses CLI input to find the command and extract raw arguments without validation.\n */\nexport function parseCommand(input: string | undefined, rootCommand: AnyPadroneCommand, findCommandByName: FindCommandFn) {\n  input ??= getCommandRuntime(rootCommand).argv().join(' ') || undefined;\n  if (!input) {\n    const defaultCommand = findCommandByName('', rootCommand.commands);\n    if (defaultCommand) {\n      return { command: defaultCommand, rawArgs: {} as Record<string, unknown>, args: [] as string[], unmatchedTerms: [] as string[] };\n    }\n    return { command: rootCommand, rawArgs: {} as Record<string, unknown>, args: [] as string[], unmatchedTerms: [] as string[] };\n  }\n\n  const parts = parseCliInputToParts(input);\n\n  const terms = parts.filter((p) => p.type === 'term').map((p) => p.value);\n  const argTokens = parts.filter((p) => p.type === 'arg').map((p) => p.value);\n\n  let curCommand: AnyPadroneCommand | undefined = rootCommand;\n  let unmatchedTerms: string[] = [];\n\n  if (terms[0] === rootCommand.name) terms.shift();\n\n  for (let i = 0; i < terms.length; i++) {\n    const term = terms[i] || '';\n    const found = findCommandByName(term, curCommand.commands);\n\n    if (found) {\n      curCommand = found;\n    } else {\n      unmatchedTerms = terms.slice(i);\n      argTokens.unshift(...unmatchedTerms);\n      break;\n    }\n  }\n\n  if (unmatchedTerms.length === 0 && curCommand.commands?.length) {\n    const defaultCommand = findCommandByName('', curCommand.commands);\n    if (defaultCommand) curCommand = defaultCommand;\n  }\n\n  if (!curCommand) return { command: rootCommand, rawArgs: {} as Record<string, unknown>, args: argTokens, unmatchedTerms };\n\n  const argsMeta = curCommand.meta?.fields;\n  const schemaMetadata = curCommand.argsSchema\n    ? extractSchemaMetadata(curCommand.argsSchema, argsMeta, curCommand.meta?.autoAlias)\n    : { flags: {}, aliases: {}, negatives: {}, customNegation: new Set<string>() };\n  const { flags, aliases, negatives, customNegation } = schemaMetadata;\n\n  const arrayArguments = new Set<string>();\n  if (curCommand.argsSchema) {\n    try {\n      const jsonSchema = getJsonSchema(curCommand.argsSchema) as Record<string, any>;\n      if (jsonSchema.type === 'object' && jsonSchema.properties) {\n        for (const [key, prop] of Object.entries(jsonSchema.properties as Record<string, any>)) {\n          if (prop?.type === 'array') arrayArguments.add(key);\n        }\n      }\n    } catch {\n      // Ignore schema parsing errors\n    }\n  }\n\n  const argParts = parts.filter((p) => p.type === 'named' || p.type === 'alias');\n  const rawArgs: Record<string, unknown> = {};\n\n  for (const arg of argParts) {\n    let key: string[];\n    if (arg.type === 'alias' && arg.key.length === 1 && flags[arg.key[0]!]) {\n      key = [flags[arg.key[0]!]!];\n    } else if (arg.type === 'named' && arg.key.length === 1 && aliases[arg.key[0]!]) {\n      key = [aliases[arg.key[0]!]!];\n    } else if (arg.type === 'named' && arg.key.length === 1 && negatives[arg.key[0]!]) {\n      // Negative keyword: --remote sets local to false\n      setNestedValue(rawArgs, [negatives[arg.key[0]!]!], false);\n      continue;\n    } else {\n      key = arg.key;\n    }\n\n    const rootKey = key[0]!;\n\n    if (arg.type === 'named' && arg.negated) {\n      // Skip --no- prefix negation for args with custom negation\n      if (customNegation.has(rootKey)) {\n        // Treat as unknown: put it back as `no-<key>` so detectUnknownArgs catches it\n        setNestedValue(rawArgs, [`no-${key.join('.')}`], false);\n        continue;\n      }\n      setNestedValue(rawArgs, key, false);\n      continue;\n    }\n\n    const value = arg.value ?? true;\n\n    if (arrayArguments.has(rootKey)) {\n      const existing = getNestedValue(rawArgs, key);\n      if (existing !== undefined) {\n        if (Array.isArray(existing)) {\n          if (Array.isArray(value)) existing.push(...value);\n          else existing.push(value);\n        } else {\n          if (Array.isArray(value)) setNestedValue(rawArgs, key, [existing, ...value]);\n          else setNestedValue(rawArgs, key, [existing, value]);\n        }\n      } else {\n        setNestedValue(rawArgs, key, Array.isArray(value) ? value : [value]);\n      }\n    } else {\n      const existing = getNestedValue(rawArgs, key);\n      if (existing !== undefined) {\n        if (Array.isArray(existing)) {\n          if (Array.isArray(value)) existing.push(...value);\n          else existing.push(value);\n        } else {\n          if (Array.isArray(value)) setNestedValue(rawArgs, key, [existing, ...value]);\n          else setNestedValue(rawArgs, key, [existing, value]);\n        }\n      } else {\n        setNestedValue(rawArgs, key, value);\n      }\n    }\n  }\n\n  return { command: curCommand, rawArgs, args: argTokens, unmatchedTerms };\n}\n\ntype FindCommandFn = (name: string, commands?: AnyPadroneCommand[]) => AnyPadroneCommand | undefined;\n\n/**\n * Preprocesses raw arguments: maps positional arguments and performs auto-coercion.\n * External data sources (stdin, env, config) are handled by extensions before this runs.\n */\nexport function buildCommandArgs(\n  command: AnyPadroneCommand,\n  rawArgs: Record<string, unknown>,\n  positionalArgs: string[],\n): { args: Record<string, unknown>; issues?: StandardSchemaV1.Issue[] } {\n  let preprocessedArgs = preprocessArgs(rawArgs, { flags: {}, aliases: {} });\n  let issues: StandardSchemaV1.Issue[] | undefined;\n\n  const positionalConfig = command.meta?.positional ? parsePositionalConfig(command.meta.positional) : [];\n\n  if (positionalConfig.length > 0) {\n    let argIndex = 0;\n    for (let i = 0; i < positionalConfig.length; i++) {\n      const { name, variadic } = positionalConfig[i]!;\n      if (argIndex >= positionalArgs.length) break;\n\n      // Detect ambiguity: same arg provided both positionally and as a named option\n      if (name in preprocessedArgs) {\n        issues ??= [];\n        issues.push({ path: [name], message: `Ambiguous argument \"${name}\": provided both positionally and as a named option` });\n        continue;\n      }\n\n      if (variadic) {\n        const remainingPositionals = positionalConfig.slice(i + 1);\n        const nonVariadicAfter = remainingPositionals.filter((p) => !p.variadic).length;\n        const variadicEnd = positionalArgs.length - nonVariadicAfter;\n        preprocessedArgs[name] = positionalArgs.slice(argIndex, variadicEnd);\n        argIndex = variadicEnd;\n      } else if (i === positionalConfig.length - 1 && positionalArgs.length > argIndex + 1) {\n        preprocessedArgs[name] = positionalArgs.slice(argIndex).join(' ');\n        argIndex = positionalArgs.length;\n      } else {\n        preprocessedArgs[name] = positionalArgs[argIndex];\n        argIndex++;\n      }\n    }\n  }\n\n  if (command.argsSchema) {\n    preprocessedArgs = coerceArgs(preprocessedArgs, command.argsSchema);\n  }\n\n  return { args: preprocessedArgs, issues };\n}\n\n/**\n * Detects unknown options in args that aren't defined in the schema.\n * Returns unknown key info with suggestions, or empty array if schema is loose.\n */\nexport function checkUnknownArgs(command: AnyPadroneCommand, preprocessedArgs: Record<string, unknown>): { key: string }[] {\n  if (!command.argsSchema) {\n    const unknowns: { key: string }[] = [];\n    for (const key of Object.keys(preprocessedArgs)) {\n      unknowns.push({ key });\n    }\n    return unknowns;\n  }\n\n  const argsMeta = command.meta?.fields;\n  const { flags, aliases, negatives } = extractSchemaMetadata(command.argsSchema, argsMeta, command.meta?.autoAlias);\n\n  return detectUnknownArgs(preprocessedArgs, command.argsSchema, flags, aliases, negatives);\n}\n\n/**\n * Validates preprocessed arguments against the command's schema.\n * First checks for unknown args (strict by default), then runs schema validation.\n * Returns sync or async result depending on the schema's validate method.\n */\nexport function validateCommandArgs(command: AnyPadroneCommand, preprocessedArgs: Record<string, unknown>) {\n  const unknownArgs = checkUnknownArgs(command, preprocessedArgs);\n  if (unknownArgs.length > 0) {\n    const issues: StandardSchemaV1.Issue[] = unknownArgs.map(({ key }) => ({\n      path: [key],\n      message: `Unknown option: \"${key}\"`,\n    }));\n    return { args: undefined, argsResult: { issues } as any };\n  }\n\n  const argsParsed = command.argsSchema ? command.argsSchema['~standard'].validate(preprocessedArgs) : { value: {} };\n\n  const buildResult = (parsed: StandardSchemaV1.Result<unknown>) => ({\n    args: parsed.issues ? undefined : (parsed.value as any),\n    argsResult: parsed as any,\n  });\n\n  return thenMaybe(argsParsed, buildResult);\n}\n\n/**\n * Returns the list of known option names from a command's schema (for fuzzy suggestion).\n */\nexport function getKnownOptionNames(command: AnyPadroneCommand): string[] {\n  if (!command.argsSchema) return [];\n  try {\n    const js = getJsonSchema(command.argsSchema) as Record<string, any>;\n    if (js.type === 'object' && js.properties) return Object.keys(js.properties);\n  } catch {\n    /* ignore */\n  }\n  return [];\n}\n\n/**\n * Formats validation issue messages for display.\n */\nexport function formatIssueMessages(issues: readonly StandardSchemaV1.Issue[]): string {\n  return issues.map((i) => `  - ${i.path?.join('.') || 'root'}: ${i.message}`).join('\\n');\n}\n\n/**\n * Core validate function for parse() — preprocesses and validates CLI args.\n * Used by the parse program method (lighter weight than the full exec pipeline).\n * External data sources (stdin, env, config) are not resolved here — use eval() for that.\n */\nexport function coreValidateForParse(\n  command: AnyPadroneCommand,\n  rawArgs: Record<string, unknown>,\n  positionalArgs: string[],\n): InterceptorValidateResult | Promise<InterceptorValidateResult> {\n  const { args: preprocessedArgs, issues } = buildCommandArgs(command, rawArgs, positionalArgs);\n  if (issues) return { args: undefined, argsResult: { issues } as any };\n  const validated = validateCommandArgs(command, preprocessedArgs);\n  return thenMaybe(validated, (v) => v as InterceptorValidateResult);\n}\n"],"mappings":"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"}