{"version":3,"file":"convert-mockllm.cjs","names":[],"sources":["../src/convert-mockllm.ts"],"sourcesContent":["/**\n * mock-llm (dwmkerr) -> aimock fixture converter\n *\n * Core conversion logic. Used by both the CLI (`aimock convert mockllm`)\n * and the standalone script (`scripts/convert-mockllm.ts`).\n */\n\n// ---------------------------------------------------------------------------\n// Minimal YAML parser\n// ---------------------------------------------------------------------------\n// Handles the subset used by mock-llm configs: indented maps, arrays with\n// `-` prefix, quoted/unquoted strings, numbers, booleans, and null.\n// Does NOT handle: anchors, aliases, multi-line scalars, flow collections,\n// tags, or other advanced YAML features.\n\ninterface YamlLine {\n  indent: number;\n  raw: string;\n  content: string; // trimmed, without trailing comment\n  isArrayItem: boolean;\n  arrayItemContent: string; // content after \"- \"\n}\n\nfunction tokenizeYamlLines(input: string): YamlLine[] {\n  const lines: YamlLine[] = [];\n  for (const raw of input.split(\"\\n\")) {\n    // Skip blank lines and full-line comments\n    const trimmed = raw.trimStart();\n    if (trimmed === \"\" || trimmed.startsWith(\"#\")) continue;\n\n    const indent = raw.length - raw.trimStart().length;\n    // Strip trailing comments (but not inside quoted strings)\n    const content = stripTrailingComment(trimmed);\n    const isArrayItem = content.startsWith(\"- \");\n    const arrayItemContent = isArrayItem ? content.slice(2).trim() : \"\";\n\n    lines.push({ indent, raw, content, isArrayItem, arrayItemContent });\n  }\n  return lines;\n}\n\nfunction stripTrailingComment(s: string): string {\n  // Naive: find # not inside quotes\n  let inSingle = false;\n  let inDouble = false;\n  for (let i = 0; i < s.length; i++) {\n    const ch = s[i];\n    if (ch === \"'\" && !inDouble) inSingle = !inSingle;\n    if (ch === '\"' && !inSingle) inDouble = !inDouble;\n    if (ch === \"#\" && !inSingle && !inDouble && i > 0 && s[i - 1] === \" \") {\n      return s.slice(0, i).trimEnd();\n    }\n  }\n  return s;\n}\n\nfunction parseScalar(value: string): unknown {\n  if (value === \"\" || value === \"~\" || value === \"null\") return null;\n  if (value === \"true\") return true;\n  if (value === \"false\") return false;\n\n  // Quoted string\n  if (\n    (value.startsWith('\"') && value.endsWith('\"')) ||\n    (value.startsWith(\"'\") && value.endsWith(\"'\"))\n  ) {\n    return value.slice(1, -1);\n  }\n\n  // Number\n  const num = Number(value);\n  if (!Number.isNaN(num) && value !== \"\") return num;\n\n  // Unquoted string\n  return value;\n}\n\nexport function parseSimpleYaml(input: string): unknown {\n  const lines = tokenizeYamlLines(input);\n  if (lines.length === 0) return null;\n\n  const result = parseBlock(lines, 0, 0);\n  return result.value;\n}\n\ninterface ParseResult {\n  value: unknown;\n  nextIndex: number;\n}\n\nfunction parseBlock(lines: YamlLine[], startIndex: number, minIndent: number): ParseResult {\n  if (startIndex >= lines.length) {\n    return { value: null, nextIndex: startIndex };\n  }\n\n  const line = lines[startIndex];\n\n  // Determine if this block is an array or a map\n  if (line.isArrayItem && line.indent >= minIndent) {\n    return parseArray(lines, startIndex, line.indent);\n  }\n\n  // Map\n  if (line.content.includes(\":\")) {\n    return parseMap(lines, startIndex, line.indent);\n  }\n\n  // Single scalar\n  return { value: parseScalar(line.content), nextIndex: startIndex + 1 };\n}\n\nfunction parseArray(lines: YamlLine[], startIndex: number, baseIndent: number): ParseResult {\n  const arr: unknown[] = [];\n  let i = startIndex;\n\n  while (i < lines.length) {\n    const line = lines[i];\n    if (line.indent < baseIndent) break;\n    if (line.indent > baseIndent) break; // shouldn't happen at array level\n    if (!line.isArrayItem) break;\n\n    const itemContent = line.arrayItemContent;\n\n    if (itemContent === \"\") {\n      // Array item with nested block on next lines\n      const nested = parseBlock(lines, i + 1, baseIndent + 1);\n      arr.push(nested.value);\n      i = nested.nextIndex;\n    } else if (itemContent.includes(\":\")) {\n      // Inline map start: \"- key: value\" possibly with more keys below\n      // Parse as a map, treating the \"- \" offset as extra indent\n      const inlineMap = parseArrayItemMap(lines, i, baseIndent);\n      arr.push(inlineMap.value);\n      i = inlineMap.nextIndex;\n    } else {\n      // Simple scalar array item\n      arr.push(parseScalar(itemContent));\n      i++;\n    }\n  }\n\n  return { value: arr, nextIndex: i };\n}\n\nfunction parseArrayItemMap(\n  lines: YamlLine[],\n  startIndex: number,\n  arrayIndent: number,\n): ParseResult {\n  // First line is \"- key: value\", subsequent lines at indent > arrayIndent are part of this map\n  const map: Record<string, unknown> = {};\n  const firstLine = lines[startIndex];\n  const firstContent = firstLine.arrayItemContent;\n\n  // Parse the first key: value from the array item line\n  const colonIdx = findColon(firstContent);\n  if (colonIdx === -1) {\n    return { value: parseScalar(firstContent), nextIndex: startIndex + 1 };\n  }\n\n  const key = firstContent.slice(0, colonIdx).trim();\n  const valueStr = firstContent.slice(colonIdx + 1).trim();\n\n  if (valueStr === \"\") {\n    // Value is a nested block\n    const nested = parseBlock(lines, startIndex + 1, arrayIndent + 2);\n    map[key] = nested.value;\n    let i = nested.nextIndex;\n\n    // Continue reading sibling keys at the array-item's content indent\n    const siblingIndent = arrayIndent + 2;\n    while (i < lines.length && lines[i].indent >= siblingIndent && !lines[i].isArrayItem) {\n      if (lines[i].indent === siblingIndent || lines[i].indent > siblingIndent) {\n        // Only parse if at exactly sibling indent and is a map key\n        if (lines[i].indent === siblingIndent && lines[i].content.includes(\":\")) {\n          const mapResult = parseMapEntries(lines, i, siblingIndent, map);\n          i = mapResult.nextIndex;\n        } else {\n          break;\n        }\n      }\n    }\n\n    return { value: map, nextIndex: i };\n  } else {\n    map[key] = parseScalar(valueStr);\n  }\n\n  // Read additional keys at indent > arrayIndent (the \"  key: value\" lines after \"- first: val\")\n  let i = startIndex + 1;\n  const contentIndent = arrayIndent + 2; // \"- \" adds 2 to effective indent\n\n  while (i < lines.length) {\n    const line = lines[i];\n    if (line.indent < contentIndent) break;\n    if (line.isArrayItem && line.indent <= arrayIndent) break;\n\n    if (line.indent === contentIndent && !line.isArrayItem && line.content.includes(\":\")) {\n      const colonPos = findColon(line.content);\n      if (colonPos === -1) break;\n      const k = line.content.slice(0, colonPos).trim();\n      const v = line.content.slice(colonPos + 1).trim();\n\n      if (v === \"\") {\n        const nested = parseBlock(lines, i + 1, contentIndent + 1);\n        map[k] = nested.value;\n        i = nested.nextIndex;\n      } else {\n        map[k] = parseScalar(v);\n        i++;\n      }\n    } else if (line.indent === contentIndent && line.isArrayItem) {\n      // This is a new array item at the same level -- not part of this map\n      break;\n    } else if (line.indent > contentIndent) {\n      // Skip nested content already consumed\n      i++;\n    } else {\n      break;\n    }\n  }\n\n  return { value: map, nextIndex: i };\n}\n\nfunction parseMap(lines: YamlLine[], startIndex: number, baseIndent: number): ParseResult {\n  const map: Record<string, unknown> = {};\n  const result = parseMapEntries(lines, startIndex, baseIndent, map);\n  return { value: map, nextIndex: result.nextIndex };\n}\n\nfunction parseMapEntries(\n  lines: YamlLine[],\n  startIndex: number,\n  baseIndent: number,\n  map: Record<string, unknown>,\n): ParseResult {\n  let i = startIndex;\n\n  while (i < lines.length) {\n    const line = lines[i];\n    if (line.indent < baseIndent) break;\n    if (line.indent > baseIndent) {\n      // Shouldn't happen at map level if properly structured -- skip\n      i++;\n      continue;\n    }\n    if (line.isArrayItem) break;\n\n    const colonIdx = findColon(line.content);\n    if (colonIdx === -1) {\n      // Not a map entry\n      break;\n    }\n\n    const key = line.content.slice(0, colonIdx).trim();\n    const valueStr = line.content.slice(colonIdx + 1).trim();\n\n    if (valueStr === \"\") {\n      // Value is a nested block on subsequent lines\n      const nested = parseBlock(lines, i + 1, baseIndent + 1);\n      map[key] = nested.value;\n      i = nested.nextIndex;\n    } else {\n      map[key] = parseScalar(valueStr);\n      i++;\n    }\n  }\n\n  return { value: map, nextIndex: i };\n}\n\nfunction findColon(s: string): number {\n  let inSingle = false;\n  let inDouble = false;\n  for (let i = 0; i < s.length; i++) {\n    const ch = s[i];\n    if (ch === \"'\" && !inDouble) inSingle = !inSingle;\n    if (ch === '\"' && !inSingle) inDouble = !inDouble;\n    if (ch === \":\" && !inSingle && !inDouble) {\n      // Must be followed by space, end of line, or nothing\n      if (i === s.length - 1 || s[i + 1] === \" \") {\n        return i;\n      }\n    }\n  }\n  return -1;\n}\n\n// ---------------------------------------------------------------------------\n// mock-llm config types\n// ---------------------------------------------------------------------------\n\nexport interface MockLLMRoute {\n  path: string;\n  method?: string;\n  match?: {\n    body?: {\n      messages?: Array<{ role: string; content: string }>;\n    };\n  };\n  response: Record<string, unknown>;\n}\n\nexport interface MockLLMTool {\n  name: string;\n  description?: string;\n  parameters?: Record<string, unknown>;\n}\n\nexport interface MockLLMConfig {\n  routes?: MockLLMRoute[];\n  mcp?: {\n    tools?: MockLLMTool[];\n  };\n}\n\n// ---------------------------------------------------------------------------\n// aimock output types\n// ---------------------------------------------------------------------------\n\nexport interface AimockFixture {\n  match?: { userMessage?: string };\n  response: { content?: string; toolCalls?: Array<{ name: string; arguments: string }> };\n  _comment?: string;\n}\n\nexport interface AimockMCPTool {\n  name: string;\n  description?: string;\n  inputSchema?: Record<string, unknown>;\n}\n\nexport interface ConvertResult {\n  fixtures: AimockFixture[];\n  mcpTools?: AimockMCPTool[];\n}\n\n// ---------------------------------------------------------------------------\n// Converter\n// ---------------------------------------------------------------------------\n\nexport function convertConfig(config: MockLLMConfig): ConvertResult {\n  const fixtures: AimockFixture[] = [];\n\n  if (config.routes) {\n    for (const route of config.routes) {\n      const fixture = convertRoute(route);\n      if (fixture) {\n        fixtures.push(fixture);\n      }\n    }\n  }\n\n  const result: ConvertResult = { fixtures };\n\n  if (config.mcp?.tools && config.mcp.tools.length > 0) {\n    result.mcpTools = config.mcp.tools.map(convertMCPTool);\n  }\n\n  return result;\n}\n\nfunction convertRoute(route: MockLLMRoute): AimockFixture | null {\n  // Extract content from response.choices[0].message.content\n  const content = extractResponseContent(route.response);\n  if (content === null) return null;\n\n  const fixture: AimockFixture = {\n    match: {},\n    response: { content },\n  };\n\n  // Extract match criteria from match.body.messages\n  const userMessage = extractUserMessage(route);\n  if (userMessage) {\n    fixture.match = { userMessage };\n  } else {\n    // Use path as a comment/identifier when no match criteria\n    fixture._comment = `${route.method ?? \"POST\"} ${route.path}`;\n  }\n\n  return fixture;\n}\n\nfunction extractResponseContent(response: Record<string, unknown>): string | null {\n  const choices = response.choices as Array<Record<string, unknown>> | undefined;\n  if (!Array.isArray(choices) || choices.length === 0) return null;\n\n  const firstChoice = choices[0];\n  const message = firstChoice.message as Record<string, unknown> | undefined;\n  if (!message) return null;\n\n  const content = message.content;\n  if (typeof content !== \"string\") return null;\n\n  return content;\n}\n\nfunction extractUserMessage(route: MockLLMRoute): string | null {\n  const messages = route.match?.body?.messages;\n  if (!Array.isArray(messages) || messages.length === 0) return null;\n\n  // Find the last user message\n  for (let i = messages.length - 1; i >= 0; i--) {\n    if (messages[i].role === \"user\") {\n      return messages[i].content;\n    }\n  }\n\n  // Fall back to last message content regardless of role\n  return messages[messages.length - 1].content ?? null;\n}\n\nfunction convertMCPTool(tool: MockLLMTool): AimockMCPTool {\n  const result: AimockMCPTool = { name: tool.name };\n  if (tool.description) result.description = tool.description;\n  if (tool.parameters) result.inputSchema = tool.parameters;\n  return result;\n}\n"],"mappings":"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"}