{"version":3,"file":"app-CCXmYEV1.mjs","names":[],"sources":["../src/server/app.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { lstat, readFile, readdir, realpath } from 'node:fs/promises'\nimport path from 'node:path'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst WORKSPACE_TREE_ROOTS = ['artifacts', 'entities', 'reports', 'sessions', '.chain-insights/schema']\nconst WORKSPACE_TREE_MAX_DEPTH = 4\n\ninterface WorkspaceTreeEntry {\n  path: string\n  type: 'file' | 'directory' | 'symlink'\n  size?: number\n}\n\nfunction withinRoot(root: string, target: string): boolean {\n  const relative = path.relative(path.resolve(root), path.resolve(target))\n  return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))\n}\n\nasync function realPathWithinRoot(root: string, target: string): Promise<boolean> {\n  try {\n    const [realRoot, realTarget] = await Promise.all([realpath(root), realpath(target)])\n    return withinRoot(realRoot, realTarget)\n  } catch {\n    return false\n  }\n}\n\nfunction toWorkspaceRelative(root: string, target: string): string {\n  return path.relative(root, target).split(path.sep).join('/')\n}\n\nasync function listWorkspaceEntries(\n  workspaceRoot: string,\n  roots = WORKSPACE_TREE_ROOTS,\n  maxDepth = WORKSPACE_TREE_MAX_DEPTH\n): Promise<WorkspaceTreeEntry[]> {\n  const entries: WorkspaceTreeEntry[] = []\n  const root = path.resolve(workspaceRoot)\n\n  async function visit(target: string, depth: number): Promise<void> {\n    const resolved = path.resolve(target)\n    if (!withinRoot(root, resolved)) return\n\n    let info: Awaited<ReturnType<typeof lstat>>\n    try {\n      info = await lstat(resolved)\n    } catch {\n      return\n    }\n\n    const type = info.isSymbolicLink() ? 'symlink' : info.isDirectory() ? 'directory' : info.isFile() ? 'file' : null\n    if (!type) return\n\n    const entry: WorkspaceTreeEntry = {\n      path: toWorkspaceRelative(root, resolved),\n      type,\n    }\n    if (type === 'file') entry.size = info.size\n    entries.push(entry)\n\n    if (type !== 'directory' || depth >= maxDepth) return\n    if (!await realPathWithinRoot(root, resolved)) return\n\n    let children: string[]\n    try {\n      children = await readdir(resolved)\n    } catch {\n      return\n    }\n\n    for (const child of children.sort()) {\n      await visit(path.join(resolved, child), depth + 1)\n    }\n  }\n\n  for (const rootName of roots) {\n    const target = path.resolve(root, rootName)\n    if (withinRoot(root, target)) await visit(target, 0)\n  }\n\n  return entries\n}\n\nasync function findVizHtml(vizId: string): Promise<string | null> {\n  const filename = `${vizId}.html`\n  const paths = workspaceOutputPaths()\n  const vizPath = path.join(paths.publishedRoot, 'viz', filename)\n  try {\n    return await readFile(vizPath, 'utf-8')\n  } catch {\n    return null\n  }\n}\n\nfunction isSafeGraphReportFilename(filename: string): boolean {\n  return (\n    filename.endsWith('.graph.json') &&\n    /^[A-Za-z0-9._-]+$/.test(filename) &&\n    !filename.includes('..') &&\n    !filename.includes('/') &&\n    !filename.includes('\\\\')\n  )\n}\n\nexport function createApp(): Hono {\n  const app = new Hono()\n\n  app.get('/health', (c) => c.json({ ok: true, ts: Date.now() }))\n\n  app.get('/status', async (c) => {\n    const { loadConfig } = await import('../config/index.js')\n    const config = await loadConfig()\n    return c.json({\n      dataDir: config.dataDir,\n      graphMcpMode: config.graphMcpMode,\n      server: 'running',\n    })\n  })\n\n  app.get('/viz/:id', async (c) => {\n    const id = c.req.param('id')\n    if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n      return c.json({ error: 'Invalid visualization ID' }, 400)\n    }\n    const html = await findVizHtml(id)\n    if (!html) {\n      return c.json({ error: 'Visualization not found' }, 404)\n    }\n    return c.html(html)\n  })\n\n  app.get('/graph-reports/:filename', async (c) => {\n    const filename = c.req.param('filename')\n    if (!isSafeGraphReportFilename(filename)) {\n      return c.json({ error: 'Invalid graph report filename' }, 400)\n    }\n\n    const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n    const paths = workspaceOutputPaths()\n    const graphPath = path.resolve(paths.reportGraphsRoot, filename)\n    if (!withinRoot(paths.reportGraphsRoot, graphPath)) {\n      return c.json({ error: 'Invalid graph report filename' }, 400)\n    }\n    if (!await realPathWithinRoot(paths.reportGraphsRoot, graphPath)) {\n      return c.json({ error: 'Graph report not found' }, 404)\n    }\n\n    try {\n      const graph = await readFile(graphPath, 'utf-8')\n      return c.body(graph, 200, {\n        'Content-Type': 'application/json',\n        'Access-Control-Allow-Origin': '*',\n      })\n    } catch {\n      return c.json({ error: 'Graph report not found' }, 404)\n    }\n  })\n\n  app.get('/graph-reports/*', (c) => {\n    return c.json({ error: 'Invalid graph report filename' }, 400)\n  })\n\n  app.get('/workspace/tree', async (c) => {\n    const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n    const paths = workspaceOutputPaths()\n    const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS)\n    return c.json({\n      schema: 'chain-insights.workspace-tree.v1',\n      root: paths.root,\n      entries,\n    })\n  })\n\n  app.onError((err, c) => {\n    console.error(err)\n    return c.json({ error: 'Internal server error' }, 500)\n  })\n\n  return app\n}\n"],"mappings":"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"}