{"version":3,"file":"html-generator-DjWagEB5.mjs","names":[],"sources":["../src/viz/html-generator.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { writeFile, mkdir } from 'node:fs/promises'\nimport { fileURLToPath } from 'node:url'\nimport path from 'node:path'\nimport type { GraphData } from './graph-model.js'\nimport { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\nconst templatePath = path.resolve(__dirname, 'templates', 'graph.html')\nconst template = readFileSync(templatePath, 'utf-8')\n\ninterface GraphHtmlNode {\n  address: string\n  node_kind: string\n  labels: string[]\n  flow_in_usd: number\n  flow_out_usd: number\n  role: string | null\n  risk_level: string | null\n  pattern_flags: string[]\n}\n\ninterface GraphHtmlEdge {\n  source: string\n  target: string\n  usd_amount: number\n  tx_count: number\n  edge_type: string\n}\n\ninterface GraphHtmlData {\n  nodes: GraphHtmlNode[]\n  edges: GraphHtmlEdge[]\n  metadata?: { seed_address?: string; title?: string }\n}\n\nconst ENTITY_TO_ROLE: Record<string, string | null> = {\n  eoa: 'search',\n  contract: 'intermediary',\n  exchange: 'exchange',\n  mixer: 'intermediary',\n  unknown: null,\n}\n\nexport function transformToGraphHtml(data: GraphData): GraphHtmlData {\n  const nodes: GraphHtmlNode[] = data.nodes.map((n) => ({\n    address: n.id,\n    node_kind: n.entityType === 'exchange' ? 'exchange' : 'wallet',\n    labels: n.label ? [n.label] : [],\n    flow_in_usd: n.totalIn,\n    flow_out_usd: n.totalOut,\n    role: ENTITY_TO_ROLE[n.entityType] ?? null,\n    risk_level: n.riskLevel === 'unknown' ? null : n.riskLevel,\n    pattern_flags: [],\n  }))\n\n  const edges: GraphHtmlEdge[] = data.edges.map((e) => ({\n    source: e.source,\n    target: e.target,\n    usd_amount: e.value,\n    tx_count: 1,\n    edge_type: 'flows_to',\n  }))\n\n  return {\n    nodes,\n    edges,\n    metadata: {\n      title: data.metadata.title,\n    },\n  }\n}\n\nexport function generateHtml(data: GraphData, _title: string): string {\n  const graphHtmlData = transformToGraphHtml(data)\n  return generateInlineGraphHtml(graphHtmlData)\n}\n\nexport function generateInlineGraphHtml(data: unknown): string {\n  const dataJson = JSON.stringify(data).replaceAll('</script>', '<\\\\/script>')\n  const inlineScript = `<script>var INLINE_DATA = ${dataJson};</script>`\n\n  return template.replace('</body>', `${inlineScript}\\n</body>`)\n}\n\nfunction sanitizePathSegment(segment: string): string {\n  if (/[/\\\\]|^\\.\\.?$/.test(segment)) throw new Error(`Invalid path segment: ${segment}`)\n  return segment\n}\n\nexport async function writeVizHtml(vizId: string, html: string): Promise<string> {\n  const paths = workspaceOutputPaths()\n  const vizDir = path.join(paths.publishedRoot, 'viz')\n  sanitizePathSegment(vizId)\n  await mkdir(vizDir, { recursive: true })\n  const filePath = path.join(vizDir, `${vizId}.html`)\n  await writeFile(filePath, html, { mode: 0o600 })\n  return filePath\n}\n"],"mappings":";;;;;;;;;;;;;AAOA,MAAM,YAAY,KAAK,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;AAE7D,MAAM,WAAW,aADI,KAAK,QAAQ,WAAW,aAAa,YACjB,GAAG,OAAO;AA2BnD,MAAM,iBAAgD;CACpD,KAAK;CACL,UAAU;CACV,UAAU;CACV,OAAO;CACP,SAAS;AACX;AAEA,SAAgB,qBAAqB,MAAgC;CAoBnE,OAAO;EACL,OApB6B,KAAK,MAAM,KAAK,OAAO;GACpD,SAAS,EAAE;GACX,WAAW,EAAE,eAAe,aAAa,aAAa;GACtD,QAAQ,EAAE,QAAQ,CAAC,EAAE,KAAK,IAAI,CAAC;GAC/B,aAAa,EAAE;GACf,cAAc,EAAE;GAChB,MAAM,eAAe,EAAE,eAAe;GACtC,YAAY,EAAE,cAAc,YAAY,OAAO,EAAE;GACjD,eAAe,CAAC;EAClB,EAWM;EACJ,OAV6B,KAAK,MAAM,KAAK,OAAO;GACpD,QAAQ,EAAE;GACV,QAAQ,EAAE;GACV,YAAY,EAAE;GACd,UAAU;GACV,WAAW;EACb,EAIM;EACJ,UAAU,EACR,OAAO,KAAK,SAAS,MACvB;CACF;AACF;AAEA,SAAgB,aAAa,MAAiB,QAAwB;CAEpE,OAAO,wBADe,qBAAqB,IACA,CAAC;AAC9C;AAEA,SAAgB,wBAAwB,MAAuB;CAE7D,MAAM,eAAe,6BADJ,KAAK,UAAU,IAAI,CAAC,CAAC,WAAW,cAAa,aACL,EAAE;CAE3D,OAAO,SAAS,QAAQ,WAAW,GAAG,aAAa,UAAU;AAC/D;AAEA,SAAS,oBAAoB,SAAyB;CACpD,IAAI,gBAAgB,KAAK,OAAO,GAAG,MAAM,IAAI,MAAM,yBAAyB,SAAS;CACrF,OAAO;AACT;AAEA,eAAsB,aAAa,OAAe,MAA+B;CAC/E,MAAM,QAAQ,qBAAqB;CACnC,MAAM,SAAS,KAAK,KAAK,MAAM,eAAe,KAAK;CACnD,oBAAoB,KAAK;CACzB,MAAM,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;CACvC,MAAM,WAAW,KAAK,KAAK,QAAQ,GAAG,MAAM,MAAM;CAClD,MAAM,UAAU,UAAU,MAAM,EAAE,MAAM,IAAM,CAAC;CAC/C,OAAO;AACT"}