{"version":3,"sources":["../src/index.ts","../src/parser.ts","../src/pool.ts"],"sourcesContent":["export type {\n  TestLayer,\n  TestMode,\n  TestEnvBase,\n  SpecCase,\n  SpecDoc,\n  Lease,\n  Pool,\n} from './types.js';\nexport { parseSpec, type ParseOptions } from './parser.js';\nexport { createPool, type PoolFactoryOptions } from './pool.js';\n","import type { SpecCase, SpecDoc, TestLayer, TestMode } from './types.js';\n\nconst HEADER_KEYS = [\n  'id',\n  'observation',\n  'given',\n  'when',\n  'then',\n  'priority',\n  'automation',\n  'mode',\n  'route',\n];\n\nconst VALID_LAYERS: ReadonlySet<TestLayer> = new Set<TestLayer>([\n  'contract',\n  'unit',\n  'integration',\n  'e2e',\n  'api',\n  'ui',\n  'data',\n  'cli',\n]);\n\nconst VALID_MODES: ReadonlySet<TestMode> = new Set<TestMode>(['mock', 'live', 'hybrid']);\n\nfunction normalize(value: string): string {\n  return value.trim().toLowerCase();\n}\n\nfunction parseMetaLine(line: string): [string, string] | null {\n  const match = line.match(/^[-*]\\s+([A-Za-z][\\w-]*)\\s*[::]\\s*(.+)$/);\n  if (!match) return null;\n  return [normalize(match[1] ?? ''), (match[2] ?? '').trim()];\n}\n\nfunction findTable(lines: string[]): { headerIdx: number; rows: string[] } | null {\n  for (let i = 0; i < lines.length - 1; i += 1) {\n    const header = lines[i];\n    const divider = lines[i + 1];\n    if (header && divider && header.includes('|') && /^\\s*\\|?\\s*[-:]+/.test(divider)) {\n      const rows: string[] = [];\n      for (let j = i + 2; j < lines.length; j += 1) {\n        const row = lines[j];\n        if (!row || !row.includes('|')) break;\n        if (row.trim().length === 0) break;\n        rows.push(row);\n      }\n      return { headerIdx: i, rows };\n    }\n  }\n  return null;\n}\n\nfunction splitRow(row: string): string[] {\n  const trimmed = row.trim().replace(/^\\|/, '').replace(/\\|$/, '');\n  return trimmed.split('|').map((cell) => cell.trim());\n}\n\nfunction isValidLayer(value: string): value is TestLayer {\n  return (VALID_LAYERS as ReadonlySet<string>).has(value);\n}\n\nfunction isValidMode(value: string): value is TestMode {\n  return (VALID_MODES as ReadonlySet<string>).has(value);\n}\n\nexport interface ParseOptions {\n  module?: string;\n  defaultLayer?: TestLayer;\n}\n\nexport function parseSpec(markdown: string, opts: ParseOptions = {}): SpecDoc {\n  const lines = markdown.split(/\\r?\\n/);\n  const warnings: string[] = [];\n  let module = opts.module ?? '';\n  let layer: TestLayer = opts.defaultLayer ?? 'unit';\n\n  for (const line of lines) {\n    const meta = parseMetaLine(line);\n    if (!meta) continue;\n    const [key, value] = meta;\n    if (key === 'module' && !opts.module) {\n      module = value;\n    } else if (key === 'layer') {\n      const lower = normalize(value);\n      if (isValidLayer(lower)) {\n        layer = lower;\n      } else {\n        warnings.push(`unknown layer \"${value}\"`);\n      }\n    }\n  }\n\n  const tableInfo = findTable(lines);\n  const cases: SpecCase[] = [];\n  if (!tableInfo) {\n    warnings.push('no test case table found');\n    return { module, layer, cases, raw: markdown, warnings };\n  }\n\n  const headerRow = lines[tableInfo.headerIdx];\n  if (!headerRow) {\n    warnings.push('header row missing');\n    return { module, layer, cases, raw: markdown, warnings };\n  }\n  const headers = splitRow(headerRow).map(normalize);\n  const indices: Partial<Record<string, number>> = {};\n  for (const key of HEADER_KEYS) {\n    const idx = headers.indexOf(key);\n    if (idx >= 0) indices[key] = idx;\n  }\n\n  const requiredMissing = ['id', 'observation', 'given', 'when', 'then'].filter(\n    (key) => indices[key] === undefined,\n  );\n  if (requiredMissing.length > 0) {\n    warnings.push(`required columns missing: ${requiredMissing.join(', ')}`);\n    return { module, layer, cases, raw: markdown, warnings };\n  }\n\n  for (const row of tableInfo.rows) {\n    const cells = splitRow(row);\n    const get = (key: string): string => {\n      const idx = indices[key];\n      if (idx === undefined) return '';\n      return cells[idx] ?? '';\n    };\n\n    const id = get('id');\n    if (!id || id.startsWith('-')) continue;\n\n    const priorityRaw = get('priority').toUpperCase();\n    const priority: SpecCase['priority'] =\n      priorityRaw === 'P0' || priorityRaw === 'P1' || priorityRaw === 'P2' || priorityRaw === 'P3'\n        ? priorityRaw\n        : 'P2';\n\n    const automationRaw = normalize(get('automation'));\n    const automation: SpecCase['automation'] =\n      automationRaw === 'yes' || automationRaw === 'manual' ? automationRaw : 'no';\n\n    const modeRaw = normalize(get('mode'));\n    const route = get('route');\n    const notes = get('notes');\n\n    const baseCase: SpecCase = {\n      id,\n      observation: get('observation'),\n      given: get('given'),\n      when: get('when'),\n      then: get('then'),\n      priority,\n      automation,\n    };\n\n    if (isValidMode(modeRaw)) {\n      baseCase.mode = modeRaw;\n    } else if (modeRaw) {\n      warnings.push(`row ${id}: unknown mode \"${modeRaw}\"`);\n    }\n    if (route) baseCase.route = route;\n    if (notes) baseCase.notes = notes;\n\n    cases.push(baseCase);\n  }\n\n  return { module, layer, cases, raw: markdown, warnings };\n}\n","import type { Lease, Pool } from './types.js';\n\nexport interface PoolFactoryOptions<T> {\n  size: number;\n  acquire: () => Promise<T>;\n  reset?: (value: T) => Promise<void>;\n  release?: (value: T) => Promise<void>;\n}\n\ninterface Slot<T> {\n  value: T;\n  inUse: boolean;\n}\n\nexport async function createPool<T>(opts: PoolFactoryOptions<T>): Promise<Pool<T>> {\n  if (!Number.isInteger(opts.size) || opts.size < 1) {\n    throw new Error(`createPool: size must be a positive integer, got ${opts.size}`);\n  }\n  const values = await Promise.all(\n    Array.from({ length: opts.size }, () => opts.acquire()),\n  );\n  const slots: Slot<T>[] = values.map((value) => ({ value, inUse: false }));\n  const waiters: Array<(slot: Slot<T>) => void> = [];\n\n  function takeFree(): Slot<T> | null {\n    for (const slot of slots) {\n      if (!slot.inUse) {\n        slot.inUse = true;\n        return slot;\n      }\n    }\n    return null;\n  }\n\n  async function borrow(): Promise<Lease<T>> {\n    const immediate = takeFree();\n    const slot = immediate ?? (await new Promise<Slot<T>>((resolve) => waiters.push(resolve)));\n    return {\n      value: slot.value,\n      release: async () => {\n        try {\n          if (opts.reset) await opts.reset(slot.value);\n        } finally {\n          slot.inUse = false;\n          const next = waiters.shift();\n          if (next) {\n            slot.inUse = true;\n            next(slot);\n          }\n        }\n      },\n    };\n  }\n\n  async function stopAll(): Promise<void> {\n    while (waiters.length) waiters.shift();\n    await Promise.all(\n      slots.map(async (slot) => {\n        if (opts.release) await opts.release(slot.value);\n      }),\n    );\n    slots.length = 0;\n  }\n\n  return { size: opts.size, borrow, stopAll };\n}\n"],"mappings":"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","names":["module"]}