{"version":3,"file":"wrap.mjs","names":[],"sources":["../../src/feature/wrap.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport { ValidationError } from '../core/errors.ts';\nimport type { PadroneSchema } from '../types/index.ts';\nimport { concatBytes } from '../util/stream.ts';\n\n/**\n * Configuration for wrapping an external CLI tool.\n */\nexport type WrapConfig<TCommandArgs extends PadroneSchema = PadroneSchema, TWrapArgs extends PadroneSchema = TCommandArgs> = {\n  /**\n   * The command to execute (e.g., 'git', 'docker', 'npm').\n   */\n  command: string;\n  /**\n   * Optional fixed arguments that always precede the arguments (e.g., ['commit'] for 'git commit').\n   */\n  args?: string[];\n  /**\n   * Positional argument configuration for the external command.\n   * If not provided, defaults to the wrapping command's positional configuration.\n   */\n  positional?: string[];\n  /**\n   * Whether to inherit stdio streams (stdin, stdout, stderr) from the parent process.\n   * Default: true\n   */\n  inheritStdio?: boolean;\n  /**\n   * Optional schema that transforms command arguments to external CLI arguments.\n   * The schema's input type should match the command arguments, and its output type defines\n   * the arguments expected by the external command.\n   * If not provided, command arguments are passed through as-is.\n   */\n  schema?: TWrapArgs | ((commandArguments: TCommandArgs) => TWrapArgs);\n};\n\n/**\n * Result from executing a wrapped CLI tool.\n */\nexport type WrapResult = {\n  /**\n   * The exit code of the process.\n   */\n  exitCode: number;\n  /**\n   * Standard output from the process (only if inheritStdio is false).\n   */\n  stdout?: string;\n  /**\n   * Standard error from the process (only if inheritStdio is false).\n   */\n  stderr?: string;\n  /**\n   * Whether the process exited successfully (exit code 0).\n   */\n  success: boolean;\n};\n\n/**\n * Converts parsed arguments to CLI arguments for an external command.\n */\nfunction argsToCliArgs(input: Record<string, unknown> | undefined, positional: readonly string[] = []): string[] {\n  const args: string[] = [];\n\n  // Handle undefined or null input\n  if (!input) return args;\n\n  const positionalValues: Record<string, unknown> = {};\n  const regularArguments: Record<string, unknown> = {};\n\n  // Separate positional and regular arguments\n  for (const [key, value] of Object.entries(input)) {\n    if (positional.includes(key) || positional.includes(`...${key}`)) {\n      positionalValues[key] = value;\n    } else {\n      regularArguments[key] = value;\n    }\n  }\n\n  // Add regular arguments first\n  for (const [key, value] of Object.entries(regularArguments)) {\n    if (value === undefined || value === null) continue;\n\n    // Use the key as-is with -- prefix\n    const flag = `--${key}`;\n\n    if (typeof value === 'boolean') {\n      if (value) args.push(flag);\n    } else if (Array.isArray(value)) {\n      // For arrays, add the flag multiple times\n      for (const item of value) {\n        args.push(flag, String(item));\n      }\n    } else {\n      args.push(flag, String(value));\n    }\n  }\n\n  // Add positional arguments in the specified order\n  for (const posKey of positional) {\n    const isVariadic = posKey.startsWith('...');\n    const key = isVariadic ? posKey.slice(3) : posKey;\n    const value = positionalValues[key];\n\n    if (value === undefined || value === null) continue;\n\n    if (isVariadic && Array.isArray(value)) {\n      args.push(...value.map(String));\n    } else {\n      args.push(String(value));\n    }\n  }\n\n  return args;\n}\n\n/**\n * Creates an action handler that wraps an external CLI tool.\n * @param config - Configuration for wrapping the external command (includes optional schema)\n * @param commandArguments - The command's arguments schema\n * @param commandPositional - Default positional config from the wrapping command\n */\nexport function createWrapHandler<TCommandArgs extends PadroneSchema, TWrapArgs extends PadroneSchema>(\n  config: WrapConfig<TCommandArgs, TWrapArgs>,\n  commandArguments: TCommandArgs,\n  commandPositional?: readonly string[],\n): (args: StandardSchemaV1.InferOutput<TCommandArgs>) => Promise<WrapResult> {\n  return async (args: StandardSchemaV1.InferOutput<TCommandArgs>): Promise<WrapResult> => {\n    const { command, args: fixedArgs = [], inheritStdio = true, positional = commandPositional, schema: wrapSchema } = config;\n\n    // Get the wrap schema (handle function or direct schema)\n    const schema = wrapSchema ? (typeof wrapSchema === 'function' ? wrapSchema(commandArguments) : wrapSchema) : commandArguments;\n\n    // Transform command arguments to external CLI arguments using the wrap schema\n    const validationResult = schema['~standard'].validate(args);\n\n    const processResult = (result: StandardSchemaV1.Result<unknown>) => {\n      if (result.issues) {\n        const issueMessages = result.issues\n          .map((i: StandardSchemaV1.Issue) => `  - ${(i.path as (string | number)[] | undefined)?.join('.') || 'root'}: ${i.message}`)\n          .join('\\n');\n        throw new ValidationError(`Wrap schema validation failed:\\n${issueMessages}`, result.issues as any);\n      }\n      return result.value;\n    };\n\n    const externalArguments =\n      validationResult instanceof Promise ? await validationResult.then(processResult) : processResult(validationResult);\n\n    // Convert arguments to CLI arguments\n    const regularArgs = argsToCliArgs(externalArguments as Record<string, unknown>, positional);\n\n    // Combine fixed args and regular args\n    const allArgs = [...fixedArgs, ...regularArgs];\n\n    // Execute the external command\n    const { spawn } = await import('node:child_process');\n\n    return new Promise<WrapResult>((resolve, reject) => {\n      const proc = spawn(command, allArgs, {\n        stdio: inheritStdio ? 'inherit' : ['ignore', 'pipe', 'pipe'],\n      });\n\n      const stdoutChunks: Uint8Array[] = [];\n      const stderrChunks: Uint8Array[] = [];\n\n      if (!inheritStdio) {\n        proc.stdout!.on('data', (chunk: Uint8Array) => stdoutChunks.push(chunk));\n        proc.stderr!.on('data', (chunk: Uint8Array) => stderrChunks.push(chunk));\n      }\n\n      const decoder = new TextDecoder();\n      proc.on('error', reject);\n      proc.on('close', (code) => {\n        const exitCode = code ?? 1;\n        resolve({\n          exitCode,\n          stdout: inheritStdio ? undefined : decoder.decode(concatBytes(stdoutChunks)),\n          stderr: inheritStdio ? undefined : decoder.decode(concatBytes(stderrChunks)),\n          success: exitCode === 0,\n        });\n      });\n    });\n  };\n}\n"],"mappings":";;;;;;AA6DA,SAAS,cAAc,OAA4C,aAAgC,CAAC,GAAa;CAC/G,MAAM,OAAiB,CAAC;CAGxB,IAAI,CAAC,OAAO,OAAO;CAEnB,MAAM,mBAA4C,CAAC;CACnD,MAAM,mBAA4C,CAAC;CAGnD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAC7C,IAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,MAAM,KAAK,GAC7D,iBAAiB,OAAO;MAExB,iBAAiB,OAAO;CAK5B,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,gBAAgB,GAAG;EAC3D,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAG3C,MAAM,OAAO,KAAK;EAElB,IAAI,OAAO,UAAU;OACf,OAAO,KAAK,KAAK,IAAI;EAAA,OACpB,IAAI,MAAM,QAAQ,KAAK,GAE5B,KAAK,MAAM,QAAQ,OACjB,KAAK,KAAK,MAAM,OAAO,IAAI,CAAC;OAG9B,KAAK,KAAK,MAAM,OAAO,KAAK,CAAC;CAEjC;CAGA,KAAK,MAAM,UAAU,YAAY;EAC/B,MAAM,aAAa,OAAO,WAAW,KAAK;EAE1C,MAAM,QAAQ,iBADF,aAAa,OAAO,MAAM,CAAC,IAAI;EAG3C,IAAI,UAAU,KAAA,KAAa,UAAU,MAAM;EAE3C,IAAI,cAAc,MAAM,QAAQ,KAAK,GACnC,KAAK,KAAK,GAAG,MAAM,IAAI,MAAM,CAAC;OAE9B,KAAK,KAAK,OAAO,KAAK,CAAC;CAE3B;CAEA,OAAO;AACT;;;;;;;AAQA,SAAgB,kBACd,QACA,kBACA,mBAC2E;CAC3E,OAAO,OAAO,SAA0E;EACtF,MAAM,EAAE,SAAS,MAAM,YAAY,CAAC,GAAG,eAAe,MAAM,aAAa,mBAAmB,QAAQ,eAAe;EAMnH,MAAM,oBAHS,aAAc,OAAO,eAAe,aAAa,WAAW,gBAAgB,IAAI,aAAc,iBAAA,CAG7E,YAAY,CAAC,SAAS,IAAI;EAE1D,MAAM,iBAAiB,WAA6C;GAClE,IAAI,OAAO,QAIT,MAAM,IAAI,gBAAgB,mCAHJ,OAAO,OAC1B,KAAK,MAA8B,OAAQ,EAAE,MAA0C,KAAK,GAAG,KAAK,OAAO,IAAI,EAAE,SAAS,CAAC,CAC3H,KAAK,IACiE,KAAK,OAAO,MAAa;GAEpG,OAAO,OAAO;EAChB;EAMA,MAAM,cAAc,cAHlB,4BAA4B,UAAU,MAAM,iBAAiB,KAAK,aAAa,IAAI,cAAc,gBAAgB,GAGnC,UAAU;EAG1F,MAAM,UAAU,CAAC,GAAG,WAAW,GAAG,WAAW;EAG7C,MAAM,EAAE,UAAU,MAAM,OAAO;EAE/B,OAAO,IAAI,SAAqB,SAAS,WAAW;GAClD,MAAM,OAAO,MAAM,SAAS,SAAS,EACnC,OAAO,eAAe,YAAY;IAAC;IAAU;IAAQ;GAAM,EAC7D,CAAC;GAED,MAAM,eAA6B,CAAC;GACpC,MAAM,eAA6B,CAAC;GAEpC,IAAI,CAAC,cAAc;IACjB,KAAK,OAAQ,GAAG,SAAS,UAAsB,aAAa,KAAK,KAAK,CAAC;IACvE,KAAK,OAAQ,GAAG,SAAS,UAAsB,aAAa,KAAK,KAAK,CAAC;GACzE;GAEA,MAAM,UAAU,IAAI,YAAY;GAChC,KAAK,GAAG,SAAS,MAAM;GACvB,KAAK,GAAG,UAAU,SAAS;IACzB,MAAM,WAAW,QAAQ;IACzB,QAAQ;KACN;KACA,QAAQ,eAAe,KAAA,IAAY,QAAQ,OAAO,YAAY,YAAY,CAAC;KAC3E,QAAQ,eAAe,KAAA,IAAY,QAAQ,OAAO,YAAY,YAAY,CAAC;KAC3E,SAAS,aAAa;IACxB,CAAC;GACH,CAAC;EACH,CAAC;CACH;AACF"}