{"version":3,"sources":["../src/index.ts","../src/FileDataSource.ts","../src/parsers/csv.ts","../src/parsers/markdown.ts"],"sourcesContent":["import { FileDataSource } from './FileDataSource.js';\nimport type { FileDataSourceConfig } from './types.js';\n\nexport { FileDataSource } from './FileDataSource.js';\nexport type {\n  FileType,\n  FileDataSourceConfig,\n  FileDataSourceConfigBase,\n  CsvLikeConfig,\n  MarkdownConfig,\n} from './types.js';\n\n/**\n * Factory for {@link FileDataSource}. Preferred entry point for hosts; the\n * class export remains available as an escape hatch for advanced subclassing.\n */\nexport function fileDataSource(config: FileDataSourceConfig): FileDataSource {\n  return new FileDataSource(config);\n}\n","import { DataSource, StaticDataAdapter } from '@inferagraph/core/data';\nimport type {\n  DataAdapterConfig,\n  GraphData,\n  NodeId,\n  NodeData,\n  ContentData,\n  PaginationOptions,\n  PaginatedResult,\n  DataFilter,\n} from '@inferagraph/core/data';\nimport type { FileDataSourceConfig, CsvLikeConfig, MarkdownConfig } from './types.js';\nimport { parseCsvFile } from './parsers/csv.js';\nimport { parseMarkdownFolder } from './parsers/markdown.js';\n\n/**\n * DataSource that loads nodes from delimited files (CSV/TSV) or a folder\n * of Markdown files with frontmatter. Edges are host-supplied via config.\n *\n * After parsing, an internal {@link StaticDataAdapter} handles all graph\n * traversal/search/filter operations. `getContent` is handled directly:\n *\n * - markdown: returns the body of the source .md file with `contentType: 'markdown'`\n * - csv/tsv with `contentFields`: returns selected columns as `metadata` with\n *   `content: ''` and `contentType: 'fields'`\n * - csv/tsv without `contentFields`: returns `undefined`\n */\nexport class FileDataSource extends DataSource {\n  readonly name = 'file';\n\n  private readonly config: FileDataSourceConfig;\n  private readonly idField: string;\n\n  private adapter: StaticDataAdapter | null = null;\n  private connected = false;\n\n  // Markdown body lookup\n  private contentByNodeId: Map<string, string> = new Map();\n  // CSV/TSV row lookup for getContent metadata\n  private csvRowsById: Map<string, Record<string, string>> = new Map();\n\n  constructor(config: FileDataSourceConfig) {\n    super();\n    this.config = config;\n    this.idField = config.idField ?? 'id';\n  }\n\n  // --- Lifecycle ---\n\n  async connect(): Promise<void> {\n    let graphData: GraphData;\n\n    if (this.config.type === 'markdown') {\n      graphData = await this.loadMarkdown(this.config);\n    } else {\n      graphData = await this.loadCsvLike(this.config);\n    }\n\n    this.adapter = new StaticDataAdapter(graphData);\n    this.connected = true;\n  }\n\n  async disconnect(): Promise<void> {\n    this.adapter = null;\n    this.connected = false;\n    this.contentByNodeId = new Map();\n    this.csvRowsById = new Map();\n  }\n\n  isConnected(): boolean {\n    return this.connected;\n  }\n\n  // --- DataAdapter delegation ---\n\n  async getInitialView(config?: DataAdapterConfig): Promise<GraphData> {\n    return this.requireAdapter().getInitialView(config);\n  }\n\n  async getNode(id: NodeId): Promise<NodeData | undefined> {\n    return this.requireAdapter().getNode(id);\n  }\n\n  async getNeighbors(nodeId: NodeId, depth: number = 1): Promise<GraphData> {\n    return this.requireAdapter().getNeighbors(nodeId, depth);\n  }\n\n  async findPath(fromId: NodeId, toId: NodeId): Promise<GraphData> {\n    return this.requireAdapter().findPath(fromId, toId);\n  }\n\n  async search(\n    query: string,\n    pagination?: PaginationOptions,\n  ): Promise<PaginatedResult<NodeData>> {\n    return this.requireAdapter().search(query, pagination);\n  }\n\n  async filter(\n    filter: DataFilter,\n    pagination?: PaginationOptions,\n  ): Promise<PaginatedResult<NodeData>> {\n    return this.requireAdapter().filter(filter, pagination);\n  }\n\n  async getContent(nodeId: NodeId): Promise<ContentData | undefined> {\n    this.ensureConnected();\n\n    if (this.config.type === 'markdown') {\n      const body = this.contentByNodeId.get(nodeId);\n      if (body === undefined) return undefined;\n      return {\n        nodeId,\n        content: body,\n        contentType: 'markdown',\n      };\n    }\n\n    // csv/tsv\n    const fields = this.config.contentFields;\n    if (!fields || fields.length === 0) return undefined;\n\n    const row = this.csvRowsById.get(nodeId);\n    if (!row) return undefined;\n\n    const metadata: Record<string, unknown> = {};\n    for (const f of fields) {\n      metadata[f] = row[f];\n    }\n    return {\n      nodeId,\n      content: '',\n      contentType: 'fields',\n      metadata,\n    };\n  }\n\n  // --- Loaders ---\n\n  private async loadCsvLike(config: CsvLikeConfig): Promise<GraphData> {\n    const delimiter: ',' | '\\t' = config.type === 'tsv' ? '\\t' : ',';\n    const hasHeader = config.hasHeader ?? true;\n\n    const parsed = await parseCsvFile(config.path, {\n      delimiter,\n      hasHeader,\n      columns: config.columns,\n    });\n\n    // Validate contentFields against parsed columns up-front\n    if (config.contentFields && config.contentFields.length > 0) {\n      const colSet = new Set(parsed.columns);\n      const missing = config.contentFields.filter((c) => !colSet.has(c));\n      if (missing.length > 0) {\n        throw new Error(\n          `FileDataSource: \\`contentFields\\` references unknown column(s): [${missing.join(', ')}]. Available columns: [${parsed.columns.join(', ')}].`,\n        );\n      }\n    }\n\n    const nodes: NodeData[] = parsed.rows.map((row) => {\n      const idValue = row[this.idField];\n      if (idValue === undefined || idValue === '') {\n        throw new Error(\n          `FileDataSource: row is missing a value for idField '${this.idField}'.`,\n        );\n      }\n      const id = String(idValue);\n      this.csvRowsById.set(id, row);\n      return { id, attributes: { ...row } };\n    });\n\n    return { nodes, edges: config.edges };\n  }\n\n  private async loadMarkdown(config: MarkdownConfig): Promise<GraphData> {\n    const parsed = await parseMarkdownFolder(config.path, {\n      frontmatter: config.frontmatter,\n      idField: this.idField,\n    });\n\n    this.contentByNodeId = parsed.contentByNodeId;\n\n    return { nodes: parsed.nodes, edges: config.edges };\n  }\n\n  // --- Helpers ---\n\n  private requireAdapter(): StaticDataAdapter {\n    this.ensureConnected();\n    return this.adapter!;\n  }\n\n  private ensureConnected(): void {\n    if (!this.connected || !this.adapter) {\n      throw new Error('FileDataSource is not connected. Call connect() first.');\n    }\n  }\n}\n","import { readFile } from 'node:fs/promises';\nimport { parse } from 'csv-parse/sync';\n\nexport interface ParseCsvOptions {\n  /** ',' for csv, '\\\\t' for tsv */\n  delimiter: ',' | '\\t';\n  /** Whether the first parsed row is a header */\n  hasHeader: boolean;\n  /** When hasHeader=false, the host-supplied column names. */\n  columns?: string[];\n}\n\nexport interface ParsedCsv {\n  /** Resolved column names in order */\n  columns: string[];\n  /** Each row as a column-keyed record */\n  rows: Record<string, string>[];\n}\n\n/**\n * Read and parse a delimited file (CSV/TSV) into columns + rows.\n * Throws if columns are required but missing or mismatched.\n */\nexport async function parseCsvFile(\n  path: string,\n  options: ParseCsvOptions,\n): Promise<ParsedCsv> {\n  const raw = await readFile(path, 'utf8');\n\n  // First parse as raw arrays so we can validate column counts before naming.\n  const records: string[][] = parse(raw, {\n    delimiter: options.delimiter,\n    skip_empty_lines: true,\n    relax_quotes: true,\n  });\n\n  if (records.length === 0) {\n    if (!options.hasHeader && (!options.columns || options.columns.length === 0)) {\n      throw new Error(\n        'FileDataSource: hasHeader=false requires non-empty `columns` configuration.',\n      );\n    }\n    return { columns: options.hasHeader ? [] : (options.columns ?? []), rows: [] };\n  }\n\n  let columns: string[];\n  let dataRows: string[][];\n\n  if (options.hasHeader) {\n    columns = records[0];\n    dataRows = records.slice(1);\n  } else {\n    if (!options.columns || options.columns.length === 0) {\n      throw new Error(\n        'FileDataSource: hasHeader=false requires non-empty `columns` configuration.',\n      );\n    }\n    if (options.columns.length !== records[0].length) {\n      throw new Error(\n        `FileDataSource: \\`columns\\` length (${options.columns.length}) does not match the parsed first row's field count (${records[0].length}).`,\n      );\n    }\n    columns = options.columns;\n    dataRows = records;\n  }\n\n  const rows: Record<string, string>[] = dataRows.map((row) => {\n    const obj: Record<string, string> = {};\n    for (let i = 0; i < columns.length; i++) {\n      obj[columns[i]] = row[i] ?? '';\n    }\n    return obj;\n  });\n\n  return { columns, rows };\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport matter from 'gray-matter';\nimport type { NodeData } from '@inferagraph/core/data';\n\nexport interface ParseMarkdownOptions {\n  /** Required frontmatter keys; each file must contain EXACTLY this set */\n  frontmatter: string[];\n  /** Frontmatter key that holds the node id (default 'id') */\n  idField: string;\n}\n\nexport interface ParsedMarkdown {\n  nodes: NodeData[];\n  /** Map from node id to body content (everything after frontmatter) */\n  contentByNodeId: Map<string, string>;\n}\n\n/**\n * Read every .md file in a folder (flat, no recursion), validate frontmatter,\n * and return nodes plus a body lookup map.\n */\nexport async function parseMarkdownFolder(\n  folderPath: string,\n  options: ParseMarkdownOptions,\n): Promise<ParsedMarkdown> {\n  const entries = await readdir(folderPath, { withFileTypes: true });\n  const mdFiles = entries\n    .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.md'))\n    .map((entry) => entry.name)\n    .sort();\n\n  const requiredKeys = new Set(options.frontmatter);\n  const nodes: NodeData[] = [];\n  const contentByNodeId = new Map<string, string>();\n\n  for (const fileName of mdFiles) {\n    const fullPath = join(folderPath, fileName);\n    const raw = await readFile(fullPath, 'utf8');\n    const parsed = matter(raw);\n    const fmRecord = (parsed.data ?? {}) as Record<string, unknown>;\n\n    const fileKeys = Object.keys(fmRecord);\n    const fileKeySet = new Set(fileKeys);\n\n    // Strict equality of key sets\n    const missing = options.frontmatter.filter((k) => !fileKeySet.has(k));\n    const extra = fileKeys.filter((k) => !requiredKeys.has(k));\n\n    if (missing.length > 0 || extra.length > 0) {\n      const parts: string[] = [];\n      if (missing.length > 0) parts.push(`missing: [${missing.join(', ')}]`);\n      if (extra.length > 0) parts.push(`extra: [${extra.join(', ')}]`);\n      throw new Error(\n        `FileDataSource: frontmatter mismatch in ${fileName} — ${parts.join('; ')}.`,\n      );\n    }\n\n    const idValue = fmRecord[options.idField];\n    if (idValue === undefined || idValue === null || idValue === '') {\n      throw new Error(\n        `FileDataSource: file ${fileName} has no value for idField '${options.idField}'.`,\n      );\n    }\n\n    const id = String(idValue);\n    nodes.push({ id, attributes: { ...fmRecord } });\n    contentByNodeId.set(id, parsed.content);\n  }\n\n  return { nodes, contentByNodeId };\n}\n"],"mappings":"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","names":["import_promises","matter"]}