@dthreads/atlas
Advanced tools
| import { | ||
| ATLAS_VERSION | ||
| } from "./chunk-DWXUUWMF.js"; | ||
| // src/core/types.ts | ||
| var graphNodeTypes = [ | ||
| "project", | ||
| "folder", | ||
| "file", | ||
| "package", | ||
| "module", | ||
| "controller", | ||
| "service", | ||
| "provider", | ||
| "repository", | ||
| "use_case", | ||
| "port", | ||
| "adapter", | ||
| "entity", | ||
| "dto", | ||
| "method", | ||
| "function", | ||
| "route", | ||
| "guard", | ||
| "pipe", | ||
| "interceptor", | ||
| "middleware", | ||
| "decorator", | ||
| "database", | ||
| "table", | ||
| "column", | ||
| "model", | ||
| "environment_variable", | ||
| "external_api", | ||
| "message_broker", | ||
| "message_topic", | ||
| "queue", | ||
| "processor", | ||
| "schema", | ||
| "index", | ||
| "constraint", | ||
| "migration", | ||
| "materialized_view", | ||
| "scheduled_job", | ||
| "workflow", | ||
| "pipeline_job", | ||
| "build_stage", | ||
| "container_image", | ||
| "container", | ||
| "deployment", | ||
| "infrastructure_service", | ||
| "ingress", | ||
| "config_map", | ||
| "secret", | ||
| "environment", | ||
| "config", | ||
| "test", | ||
| "library", | ||
| "risk" | ||
| ]; | ||
| var graphEdgeTypes = [ | ||
| "contains", | ||
| "imports", | ||
| "exports", | ||
| "declares", | ||
| "provides", | ||
| "injects", | ||
| "implements", | ||
| "calls", | ||
| "uses", | ||
| "reads", | ||
| "writes", | ||
| "handles", | ||
| "depends_on", | ||
| "decorates", | ||
| "validates", | ||
| "returns", | ||
| "references", | ||
| "connects_to", | ||
| "tests", | ||
| "has_method", | ||
| "has_column", | ||
| "publishes_to", | ||
| "delivers_to", | ||
| "enqueues", | ||
| "processes", | ||
| "creates", | ||
| "alters", | ||
| "drops", | ||
| "indexes", | ||
| "schedules", | ||
| "triggers", | ||
| "builds", | ||
| "publishes", | ||
| "deploys", | ||
| "exposes", | ||
| "configures", | ||
| "runs_in", | ||
| "targets" | ||
| ]; | ||
| // src/core/graph.ts | ||
| var validNodeTypes = new Set(graphNodeTypes); | ||
| var validEdgeTypes = new Set(graphEdgeTypes); | ||
| var GraphBuilder = class { | ||
| nodes = /* @__PURE__ */ new Map(); | ||
| edges = /* @__PURE__ */ new Map(); | ||
| addNode(node) { | ||
| const current = this.nodes.get(node.id); | ||
| if (!current) { | ||
| this.nodes.set(node.id, node); | ||
| return node; | ||
| } | ||
| const merged = { | ||
| ...current, | ||
| ...node, | ||
| metadata: { ...current.metadata, ...node.metadata } | ||
| }; | ||
| this.nodes.set(node.id, merged); | ||
| return merged; | ||
| } | ||
| addEdge(edge) { | ||
| if (!this.nodes.has(edge.from) || !this.nodes.has(edge.to)) return null; | ||
| const key = `${edge.from}|${edge.type}|${edge.to}|${edge.label ?? ""}`; | ||
| const id = edge.id ?? `edge:${encodeURIComponent(key)}`; | ||
| const result = { ...edge, id }; | ||
| this.edges.set(key, result); | ||
| return result; | ||
| } | ||
| hasNode(id) { | ||
| return this.nodes.has(id); | ||
| } | ||
| validate() { | ||
| const errors = []; | ||
| for (const node of this.nodes.values()) { | ||
| if (!validNodeTypes.has(String(node.type))) errors.push(`${node.id}: invalid node type ${String(node.type)}`); | ||
| } | ||
| for (const edge of this.edges.values()) { | ||
| if (!validEdgeTypes.has(String(edge.type))) errors.push(`${edge.id}: invalid edge type ${String(edge.type)}`); | ||
| if (!this.nodes.has(edge.from)) errors.push(`${edge.id}: missing source ${edge.from}`); | ||
| if (!this.nodes.has(edge.to)) errors.push(`${edge.id}: missing target ${edge.to}`); | ||
| } | ||
| return errors; | ||
| } | ||
| toGraph(project) { | ||
| const nodes = [...this.nodes.values()].sort((a, b) => a.id.localeCompare(b.id)); | ||
| const edges = [...this.edges.values()].sort((a, b) => a.id.localeCompare(b.id)); | ||
| return { version: ATLAS_VERSION, project, nodes, edges, stats: buildStats(nodes, edges) }; | ||
| } | ||
| }; | ||
| function increment(target, key) { | ||
| target[key] = (target[key] ?? 0) + 1; | ||
| } | ||
| function buildStats(nodes, edges) { | ||
| const byNodeType = /* @__PURE__ */ Object.create(null); | ||
| const byEdgeType = /* @__PURE__ */ Object.create(null); | ||
| for (const node of nodes) increment(byNodeType, node.type); | ||
| for (const edge of edges) increment(byEdgeType, edge.type); | ||
| return { totalNodes: nodes.length, totalEdges: edges.length, byNodeType, byEdgeType }; | ||
| } | ||
| var GraphQuery = class { | ||
| constructor(graph) { | ||
| this.graph = graph; | ||
| this.nodeMap = new Map(graph.nodes.map((node) => [node.id, node])); | ||
| for (const node of graph.nodes) { | ||
| const typed = this.nodesByType.get(node.type) ?? []; | ||
| typed.push(node); | ||
| this.nodesByType.set(node.type, typed); | ||
| } | ||
| for (const edge of graph.edges) { | ||
| this.edgeMap.set(edge.id, edge); | ||
| const incoming = this.incomingMap.get(edge.to) ?? []; | ||
| incoming.push(edge); | ||
| this.incomingMap.set(edge.to, incoming); | ||
| const outgoing = this.outgoingMap.get(edge.from) ?? []; | ||
| outgoing.push(edge); | ||
| this.outgoingMap.set(edge.from, outgoing); | ||
| } | ||
| } | ||
| graph; | ||
| nodeMap; | ||
| edgeMap = /* @__PURE__ */ new Map(); | ||
| incomingMap = /* @__PURE__ */ new Map(); | ||
| outgoingMap = /* @__PURE__ */ new Map(); | ||
| nodesByType = /* @__PURE__ */ new Map(); | ||
| findNode(query) { | ||
| const needle = query.trim().toLowerCase(); | ||
| if (!needle) return []; | ||
| return this.graph.nodes.filter((node) => searchableNode(node).includes(needle)).sort((a, b) => scoreNode(b, needle) - scoreNode(a, needle)).slice(0, 100); | ||
| } | ||
| search(query) { | ||
| const needle = query.trim().toLowerCase(); | ||
| if (!needle) return []; | ||
| return this.findNode(query).map((node) => ({ | ||
| node, | ||
| score: scoreNode(node, needle), | ||
| matches: matchingFields(node, needle) | ||
| })); | ||
| } | ||
| getNode(id) { | ||
| return this.nodeMap.get(id) ?? null; | ||
| } | ||
| getIncoming(id) { | ||
| return this.incomingMap.get(id) ?? []; | ||
| } | ||
| getOutgoing(id) { | ||
| return this.outgoingMap.get(id) ?? []; | ||
| } | ||
| findRoutes() { | ||
| return this.byType("route"); | ||
| } | ||
| findServices() { | ||
| return this.byType("service"); | ||
| } | ||
| findControllers() { | ||
| return this.byType("controller"); | ||
| } | ||
| findTables() { | ||
| return this.byType("table"); | ||
| } | ||
| findSchemas() { | ||
| return this.byType("schema"); | ||
| } | ||
| findIndexes() { | ||
| return this.byType("index"); | ||
| } | ||
| findConstraints() { | ||
| return this.byType("constraint"); | ||
| } | ||
| findMigrations() { | ||
| return this.byType("migration"); | ||
| } | ||
| findScheduledJobs() { | ||
| return this.byType("scheduled_job"); | ||
| } | ||
| findWorkflows() { | ||
| return this.byType("workflow"); | ||
| } | ||
| findDeployments() { | ||
| return this.byType("deployment"); | ||
| } | ||
| findEnvironments() { | ||
| return this.byType("environment"); | ||
| } | ||
| findExternalApis() { | ||
| return this.byType("external_api"); | ||
| } | ||
| findMessageTopics() { | ||
| return this.byType("message_topic"); | ||
| } | ||
| findQueues() { | ||
| return this.byType("queue"); | ||
| } | ||
| findProcessors() { | ||
| return this.byType("processor"); | ||
| } | ||
| findTableProfile(tableId) { | ||
| const table = this.nodeMap.get(tableId); | ||
| if (!table || table.type !== "table") return { nodes: [], edges: [] }; | ||
| const edgeTypes = /* @__PURE__ */ new Set(["has_column", "indexes", "contains", "references", "reads", "writes", "creates", "alters", "drops"]); | ||
| const edges = [...this.getIncoming(tableId), ...this.getOutgoing(tableId)].filter((edge) => edgeTypes.has(edge.type)); | ||
| const ids = /* @__PURE__ */ new Set([tableId, ...edges.flatMap((edge) => [edge.from, edge.to])]); | ||
| return { nodes: [...ids].map((id) => this.nodeMap.get(id)).filter(Boolean), edges }; | ||
| } | ||
| getNeighbors(nodeId, depth = 1) { | ||
| if (!this.nodeMap.has(nodeId)) return { nodes: [], edges: [] }; | ||
| const nodeIds = /* @__PURE__ */ new Set([nodeId]); | ||
| const edgeIds = /* @__PURE__ */ new Set(); | ||
| let frontier = [nodeId]; | ||
| for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) { | ||
| const next = []; | ||
| for (const id of frontier) { | ||
| for (const edge of [...this.getIncoming(id), ...this.getOutgoing(id)]) { | ||
| edgeIds.add(edge.id); | ||
| const neighbor = edge.from === id ? edge.to : edge.from; | ||
| if (!nodeIds.has(neighbor)) { | ||
| nodeIds.add(neighbor); | ||
| next.push(neighbor); | ||
| } | ||
| } | ||
| } | ||
| frontier = next; | ||
| } | ||
| return { | ||
| nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean), | ||
| edges: [...edgeIds].map((id) => this.edgeMap.get(id)).filter(Boolean) | ||
| }; | ||
| } | ||
| findFlowFromRoute(routeId) { | ||
| return this.walk(routeId, "outgoing", 12, /* @__PURE__ */ new Set([ | ||
| "handles", | ||
| "calls", | ||
| "reads", | ||
| "writes", | ||
| "uses", | ||
| "connects_to", | ||
| "validates", | ||
| "returns", | ||
| "publishes_to", | ||
| "delivers_to", | ||
| "enqueues", | ||
| "processes" | ||
| ])); | ||
| } | ||
| findAsyncFlow(rootId) { | ||
| const root = this.nodeMap.get(rootId); | ||
| if (!root || !["message_topic", "queue"].includes(root.type)) return { nodes: [], edges: [] }; | ||
| const flow = this.walk(rootId, "outgoing", 12, /* @__PURE__ */ new Set([ | ||
| "delivers_to", | ||
| "calls", | ||
| "reads", | ||
| "writes", | ||
| "uses", | ||
| "connects_to", | ||
| "publishes_to", | ||
| "enqueues", | ||
| "processes" | ||
| ])); | ||
| const nodeIds = new Set(flow.nodes.map((node) => node.id)); | ||
| const edgeIds = new Set(flow.edges.map((edge) => edge.id)); | ||
| for (const edge of this.getIncoming(rootId).filter((item) => ["publishes_to", "enqueues"].includes(item.type))) { | ||
| nodeIds.add(edge.from); | ||
| edgeIds.add(edge.id); | ||
| } | ||
| return { | ||
| nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean), | ||
| edges: [...edgeIds].map((id) => this.edgeMap.get(id)).filter(Boolean) | ||
| }; | ||
| } | ||
| findDependencies(nodeId, depth = 2) { | ||
| return this.walk(nodeId, "outgoing", depth); | ||
| } | ||
| findDependents(nodeId, depth = 2) { | ||
| return this.walk(nodeId, "incoming", depth); | ||
| } | ||
| byType(type) { | ||
| return this.nodesByType.get(type) ?? []; | ||
| } | ||
| walk(startId, direction, depth, allowedTypes) { | ||
| if (!this.nodeMap.has(startId)) return { nodes: [], edges: [] }; | ||
| const nodeIds = /* @__PURE__ */ new Set([startId]); | ||
| const edgeIds = /* @__PURE__ */ new Set(); | ||
| let frontier = [startId]; | ||
| for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) { | ||
| const next = []; | ||
| for (const id of frontier) { | ||
| const edges = direction === "outgoing" ? this.getOutgoing(id) : this.getIncoming(id); | ||
| for (const edge of edges) { | ||
| if (allowedTypes && !allowedTypes.has(edge.type)) continue; | ||
| edgeIds.add(edge.id); | ||
| const neighbor = direction === "outgoing" ? edge.to : edge.from; | ||
| if (!nodeIds.has(neighbor)) { | ||
| nodeIds.add(neighbor); | ||
| next.push(neighbor); | ||
| } | ||
| } | ||
| } | ||
| frontier = next; | ||
| } | ||
| return { | ||
| nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean), | ||
| edges: [...edgeIds].map((id) => this.edgeMap.get(id)).filter(Boolean) | ||
| }; | ||
| } | ||
| }; | ||
| function searchableNode(node) { | ||
| return [node.id, node.type, node.label, node.name, node.file, JSON.stringify(node.metadata ?? {})].filter(Boolean).join(" ").toLowerCase(); | ||
| } | ||
| function scoreNode(node, needle) { | ||
| const label = node.label.toLowerCase(); | ||
| if (label === needle) return 100; | ||
| if (label.startsWith(needle)) return 80; | ||
| if (node.id.toLowerCase().includes(needle)) return 60; | ||
| return 20; | ||
| } | ||
| function matchingFields(node, needle) { | ||
| const fields = { | ||
| id: node.id, | ||
| label: node.label, | ||
| name: node.name ?? "", | ||
| type: node.type, | ||
| file: node.file ?? "", | ||
| metadata: JSON.stringify(node.metadata ?? {}) | ||
| }; | ||
| return Object.entries(fields).filter(([, value]) => value.toLowerCase().includes(needle)).map(([key]) => key); | ||
| } | ||
| export { | ||
| graphNodeTypes, | ||
| graphEdgeTypes, | ||
| GraphBuilder, | ||
| buildStats, | ||
| GraphQuery | ||
| }; | ||
| //# sourceMappingURL=chunk-4WKRCGRX.js.map |
| {"version":3,"sources":["../src/core/types.ts","../src/core/graph.ts"],"sourcesContent":["export const graphNodeTypes = [\n \"project\", \"folder\", \"file\", \"package\", \"module\", \"controller\", \"service\",\n \"provider\", \"repository\", \"use_case\", \"port\", \"adapter\", \"entity\", \"dto\", \"method\", \"function\", \"route\",\n \"guard\", \"pipe\", \"interceptor\", \"middleware\", \"decorator\", \"database\",\n \"table\", \"column\", \"model\", \"environment_variable\", \"external_api\",\n \"message_broker\", \"message_topic\", \"queue\", \"processor\",\n \"schema\", \"index\", \"constraint\", \"migration\", \"materialized_view\",\n \"scheduled_job\", \"workflow\", \"pipeline_job\", \"build_stage\", \"container_image\",\n \"container\", \"deployment\", \"infrastructure_service\", \"ingress\", \"config_map\",\n \"secret\", \"environment\", \"config\", \"test\", \"library\", \"risk\",\n] as const;\n\nexport const graphEdgeTypes = [\n \"contains\", \"imports\", \"exports\", \"declares\", \"provides\", \"injects\", \"implements\", \"calls\",\n \"uses\", \"reads\", \"writes\", \"handles\", \"depends_on\", \"decorates\", \"validates\",\n \"returns\", \"references\", \"connects_to\", \"tests\", \"has_method\", \"has_column\",\n \"publishes_to\", \"delivers_to\", \"enqueues\", \"processes\",\n \"creates\", \"alters\", \"drops\", \"indexes\", \"schedules\", \"triggers\", \"builds\",\n \"publishes\", \"deploys\", \"exposes\", \"configures\", \"runs_in\", \"targets\",\n] as const;\n\nexport type GraphNodeType = (typeof graphNodeTypes)[number];\nexport type GraphEdgeType = (typeof graphEdgeTypes)[number];\nexport type GraphSourceType =\n | \"static_analysis\"\n | \"ast\"\n | \"config\"\n | \"package_json\"\n | \"heuristic\"\n | \"runtime\"\n | \"manual\";\n\nexport interface SourceLocation {\n file: string;\n startLine?: number;\n endLine?: number;\n}\n\nexport interface GraphNode {\n id: string;\n type: GraphNodeType;\n label: string;\n name?: string;\n file?: string;\n language?: string;\n framework?: string;\n sourceLocation?: SourceLocation;\n confidence?: number;\n source?: GraphSourceType;\n metadata?: Record<string, unknown>;\n}\n\nexport interface GraphEdge {\n id: string;\n from: string;\n to: string;\n type: GraphEdgeType;\n label?: string;\n confidence?: number;\n source?: GraphSourceType;\n metadata?: Record<string, unknown>;\n}\n\nexport interface DetectedStack {\n name: string;\n confidence: number;\n evidence: string[];\n}\n\nexport interface GraphStats {\n totalNodes: number;\n totalEdges: number;\n byNodeType: Partial<Record<GraphNodeType, number>>;\n byEdgeType: Partial<Record<GraphEdgeType, number>>;\n}\n\nexport interface ArchitectureGraph {\n version: string;\n project: {\n name: string;\n root: string;\n detectedStacks: string[];\n createdAt: string;\n };\n nodes: GraphNode[];\n edges: GraphEdge[];\n stats: GraphStats;\n}\n\nexport interface ScannedFile {\n absolutePath: string;\n path: string;\n extension: string;\n size: number;\n hash?: string;\n lastModified: string;\n}\n\nexport interface ScanMetadata {\n version: string;\n projectName: string;\n projectRoot: string;\n scanStartedAt: string;\n scanFinishedAt: string;\n durationMs: number;\n filesScanned: number;\n filesIgnored: number;\n filesHashed?: number;\n filesReused?: number;\n cacheHit?: boolean;\n inputFingerprint?: string;\n analysisCacheVersion?: number;\n viewerFingerprint?: string;\n runtimeEvents?: number;\n runtimeMergedAt?: string;\n runtimeFingerprint?: string;\n detectedStacks: DetectedStack[];\n}\n\nexport type RiskSeverity = \"low\" | \"medium\" | \"high\" | \"critical\";\n\nexport interface ArchitectureRisk {\n id: string;\n type: string;\n severity: RiskSeverity;\n title: string;\n description: string;\n recommendation: string;\n nodeId?: string;\n file?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface GraphSubgraph {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\nexport interface GraphSearchResult {\n node: GraphNode;\n score: number;\n matches: string[];\n}\n\nexport type ScanProgressStage =\n | \"scan_files\"\n | \"detect_stack\"\n | \"parse_architecture\"\n | \"build_graph\"\n | \"detect_risks\"\n | \"write_outputs\";\n\nexport interface ScanProgress {\n stage: ScanProgressStage;\n message: string;\n}\n\nexport interface ScanOptions {\n projectPath: string;\n outputPath?: string;\n incremental?: boolean;\n debug?: boolean;\n onProgress?: (progress: ScanProgress) => void;\n}\n\nexport interface ScanResult {\n graph: ArchitectureGraph;\n metadata: ScanMetadata;\n risks: ArchitectureRisk[];\n outputPath: string;\n}\n\nexport interface RuntimeTraceNode {\n id: string;\n type: GraphNodeType;\n label?: string;\n file?: string;\n}\n\nexport interface RuntimeTraceEvent {\n from: string;\n to: string;\n type: GraphEdgeType;\n timestamp?: string;\n count?: number;\n durationMs?: number;\n fromNode?: RuntimeTraceNode;\n toNode?: RuntimeTraceNode;\n metadata?: Record<string, unknown>;\n}\n","import type {\n ArchitectureGraph,\n GraphEdge,\n GraphEdgeType,\n GraphNode,\n GraphSearchResult,\n GraphStats,\n GraphSubgraph,\n} from \"./types.js\";\nimport { graphEdgeTypes, graphNodeTypes } from \"./types.js\";\nimport { ATLAS_VERSION } from \"../version.js\";\n\nconst validNodeTypes = new Set<string>(graphNodeTypes);\nconst validEdgeTypes = new Set<string>(graphEdgeTypes);\n\nexport class GraphBuilder {\n readonly nodes = new Map<string, GraphNode>();\n readonly edges = new Map<string, GraphEdge>();\n\n addNode(node: GraphNode): GraphNode {\n const current = this.nodes.get(node.id);\n if (!current) {\n this.nodes.set(node.id, node);\n return node;\n }\n const merged = {\n ...current,\n ...node,\n metadata: { ...current.metadata, ...node.metadata },\n };\n this.nodes.set(node.id, merged);\n return merged;\n }\n\n addEdge(edge: Omit<GraphEdge, \"id\"> & { id?: string }): GraphEdge | null {\n if (!this.nodes.has(edge.from) || !this.nodes.has(edge.to)) return null;\n const key = `${edge.from}|${edge.type}|${edge.to}|${edge.label ?? \"\"}`;\n const id = edge.id ?? `edge:${encodeURIComponent(key)}`;\n const result: GraphEdge = { ...edge, id };\n this.edges.set(key, result);\n return result;\n }\n\n hasNode(id: string): boolean {\n return this.nodes.has(id);\n }\n\n validate(): string[] {\n const errors: string[] = [];\n for (const node of this.nodes.values()) {\n if (!validNodeTypes.has(String(node.type))) errors.push(`${node.id}: invalid node type ${String(node.type)}`);\n }\n for (const edge of this.edges.values()) {\n if (!validEdgeTypes.has(String(edge.type))) errors.push(`${edge.id}: invalid edge type ${String(edge.type)}`);\n if (!this.nodes.has(edge.from)) errors.push(`${edge.id}: missing source ${edge.from}`);\n if (!this.nodes.has(edge.to)) errors.push(`${edge.id}: missing target ${edge.to}`);\n }\n return errors;\n }\n\n toGraph(project: ArchitectureGraph[\"project\"]): ArchitectureGraph {\n const nodes = [...this.nodes.values()].sort((a, b) => a.id.localeCompare(b.id));\n const edges = [...this.edges.values()].sort((a, b) => a.id.localeCompare(b.id));\n return { version: ATLAS_VERSION, project, nodes, edges, stats: buildStats(nodes, edges) };\n }\n}\n\nfunction increment<T extends string>(target: Partial<Record<T, number>>, key: T) {\n target[key] = (target[key] ?? 0) + 1;\n}\n\nexport function buildStats(nodes: GraphNode[], edges: GraphEdge[]): GraphStats {\n const byNodeType: GraphStats[\"byNodeType\"] = Object.create(null) as GraphStats[\"byNodeType\"];\n const byEdgeType: GraphStats[\"byEdgeType\"] = Object.create(null) as GraphStats[\"byEdgeType\"];\n for (const node of nodes) increment(byNodeType, node.type);\n for (const edge of edges) increment(byEdgeType, edge.type);\n return { totalNodes: nodes.length, totalEdges: edges.length, byNodeType, byEdgeType };\n}\n\nexport class GraphQuery {\n private readonly nodeMap: Map<string, GraphNode>;\n private readonly edgeMap = new Map<string, GraphEdge>();\n private readonly incomingMap = new Map<string, GraphEdge[]>();\n private readonly outgoingMap = new Map<string, GraphEdge[]>();\n private readonly nodesByType = new Map<GraphNode[\"type\"], GraphNode[]>();\n\n constructor(readonly graph: ArchitectureGraph) {\n this.nodeMap = new Map(graph.nodes.map((node) => [node.id, node]));\n for (const node of graph.nodes) {\n const typed = this.nodesByType.get(node.type) ?? [];\n typed.push(node);\n this.nodesByType.set(node.type, typed);\n }\n for (const edge of graph.edges) {\n this.edgeMap.set(edge.id, edge);\n const incoming = this.incomingMap.get(edge.to) ?? [];\n incoming.push(edge);\n this.incomingMap.set(edge.to, incoming);\n const outgoing = this.outgoingMap.get(edge.from) ?? [];\n outgoing.push(edge);\n this.outgoingMap.set(edge.from, outgoing);\n }\n }\n\n findNode(query: string): GraphNode[] {\n const needle = query.trim().toLowerCase();\n if (!needle) return [];\n return this.graph.nodes\n .filter((node) => searchableNode(node).includes(needle))\n .sort((a, b) => scoreNode(b, needle) - scoreNode(a, needle))\n .slice(0, 100);\n }\n\n search(query: string): GraphSearchResult[] {\n const needle = query.trim().toLowerCase();\n if (!needle) return [];\n return this.findNode(query).map((node) => ({\n node,\n score: scoreNode(node, needle),\n matches: matchingFields(node, needle),\n }));\n }\n\n getNode(id: string): GraphNode | null {\n return this.nodeMap.get(id) ?? null;\n }\n\n getIncoming(id: string): GraphEdge[] {\n return this.incomingMap.get(id) ?? [];\n }\n\n getOutgoing(id: string): GraphEdge[] {\n return this.outgoingMap.get(id) ?? [];\n }\n\n findRoutes(): GraphNode[] { return this.byType(\"route\"); }\n findServices(): GraphNode[] { return this.byType(\"service\"); }\n findControllers(): GraphNode[] { return this.byType(\"controller\"); }\n findTables(): GraphNode[] { return this.byType(\"table\"); }\n findSchemas(): GraphNode[] { return this.byType(\"schema\"); }\n findIndexes(): GraphNode[] { return this.byType(\"index\"); }\n findConstraints(): GraphNode[] { return this.byType(\"constraint\"); }\n findMigrations(): GraphNode[] { return this.byType(\"migration\"); }\n findScheduledJobs(): GraphNode[] { return this.byType(\"scheduled_job\"); }\n findWorkflows(): GraphNode[] { return this.byType(\"workflow\"); }\n findDeployments(): GraphNode[] { return this.byType(\"deployment\"); }\n findEnvironments(): GraphNode[] { return this.byType(\"environment\"); }\n findExternalApis(): GraphNode[] { return this.byType(\"external_api\"); }\n findMessageTopics(): GraphNode[] { return this.byType(\"message_topic\"); }\n findQueues(): GraphNode[] { return this.byType(\"queue\"); }\n findProcessors(): GraphNode[] { return this.byType(\"processor\"); }\n\n findTableProfile(tableId: string): GraphSubgraph {\n const table = this.nodeMap.get(tableId);\n if (!table || table.type !== \"table\") return { nodes: [], edges: [] };\n const edgeTypes = new Set<GraphEdgeType>([\"has_column\", \"indexes\", \"contains\", \"references\", \"reads\", \"writes\", \"creates\", \"alters\", \"drops\"]);\n const edges = [...this.getIncoming(tableId), ...this.getOutgoing(tableId)]\n .filter((edge) => edgeTypes.has(edge.type));\n const ids = new Set([tableId, ...edges.flatMap((edge) => [edge.from, edge.to])]);\n return { nodes: [...ids].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[], edges };\n }\n\n getNeighbors(nodeId: string, depth = 1): GraphSubgraph {\n if (!this.nodeMap.has(nodeId)) return { nodes: [], edges: [] };\n const nodeIds = new Set([nodeId]);\n const edgeIds = new Set<string>();\n let frontier = [nodeId];\n for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) {\n const next: string[] = [];\n for (const id of frontier) {\n for (const edge of [...this.getIncoming(id), ...this.getOutgoing(id)]) {\n edgeIds.add(edge.id);\n const neighbor = edge.from === id ? edge.to : edge.from;\n if (!nodeIds.has(neighbor)) { nodeIds.add(neighbor); next.push(neighbor); }\n }\n }\n frontier = next;\n }\n return {\n nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[],\n edges: [...edgeIds].map((id) => this.edgeMap.get(id)).filter(Boolean) as GraphEdge[],\n };\n }\n\n findFlowFromRoute(routeId: string): GraphSubgraph {\n return this.walk(routeId, \"outgoing\", 12, new Set([\n \"handles\", \"calls\", \"reads\", \"writes\", \"uses\", \"connects_to\", \"validates\", \"returns\",\n \"publishes_to\", \"delivers_to\", \"enqueues\", \"processes\",\n ]));\n }\n\n findAsyncFlow(rootId: string): GraphSubgraph {\n const root = this.nodeMap.get(rootId);\n if (!root || ![\"message_topic\", \"queue\"].includes(root.type)) return { nodes: [], edges: [] };\n const flow = this.walk(rootId, \"outgoing\", 12, new Set([\n \"delivers_to\", \"calls\", \"reads\", \"writes\", \"uses\", \"connects_to\", \"publishes_to\", \"enqueues\", \"processes\",\n ]));\n const nodeIds = new Set(flow.nodes.map((node) => node.id));\n const edgeIds = new Set(flow.edges.map((edge) => edge.id));\n for (const edge of this.getIncoming(rootId).filter((item) => [\"publishes_to\", \"enqueues\"].includes(item.type))) {\n nodeIds.add(edge.from);\n edgeIds.add(edge.id);\n }\n return {\n nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[],\n edges: [...edgeIds].map((id) => this.edgeMap.get(id)).filter(Boolean) as GraphEdge[],\n };\n }\n\n findDependencies(nodeId: string, depth = 2): GraphSubgraph {\n return this.walk(nodeId, \"outgoing\", depth);\n }\n\n findDependents(nodeId: string, depth = 2): GraphSubgraph {\n return this.walk(nodeId, \"incoming\", depth);\n }\n\n private byType(type: GraphNode[\"type\"]): GraphNode[] {\n return this.nodesByType.get(type) ?? [];\n }\n\n private walk(\n startId: string,\n direction: \"incoming\" | \"outgoing\",\n depth: number,\n allowedTypes?: Set<GraphEdgeType>,\n ): GraphSubgraph {\n if (!this.nodeMap.has(startId)) return { nodes: [], edges: [] };\n const nodeIds = new Set([startId]);\n const edgeIds = new Set<string>();\n let frontier = [startId];\n for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) {\n const next: string[] = [];\n for (const id of frontier) {\n const edges = direction === \"outgoing\" ? this.getOutgoing(id) : this.getIncoming(id);\n for (const edge of edges) {\n if (allowedTypes && !allowedTypes.has(edge.type)) continue;\n edgeIds.add(edge.id);\n const neighbor = direction === \"outgoing\" ? edge.to : edge.from;\n if (!nodeIds.has(neighbor)) {\n nodeIds.add(neighbor);\n next.push(neighbor);\n }\n }\n }\n frontier = next;\n }\n return {\n nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[],\n edges: [...edgeIds].map((id) => this.edgeMap.get(id)).filter(Boolean) as GraphEdge[],\n };\n }\n}\n\nfunction searchableNode(node: GraphNode): string {\n return [node.id, node.type, node.label, node.name, node.file, JSON.stringify(node.metadata ?? {})]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n}\n\nfunction scoreNode(node: GraphNode, needle: string): number {\n const label = node.label.toLowerCase();\n if (label === needle) return 100;\n if (label.startsWith(needle)) return 80;\n if (node.id.toLowerCase().includes(needle)) return 60;\n return 20;\n}\n\nfunction matchingFields(node: GraphNode, needle: string): string[] {\n const fields = {\n id: node.id,\n label: node.label,\n name: node.name ?? \"\",\n type: node.type,\n file: node.file ?? \"\",\n metadata: JSON.stringify(node.metadata ?? {}),\n };\n return Object.entries(fields).filter(([, value]) => value.toLowerCase().includes(needle)).map(([key]) => key);\n}\n"],"mappings":"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","names":[]} |
| // src/version.ts | ||
| var ATLAS_VERSION = "0.3.0"; | ||
| export { | ||
| ATLAS_VERSION | ||
| }; | ||
| //# sourceMappingURL=chunk-DWXUUWMF.js.map |
| {"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const ATLAS_VERSION = \"0.3.0\";\n"],"mappings":";AAAO,IAAM,gBAAgB;","names":[]} |
| import { | ||
| GraphQuery | ||
| } from "./chunk-4WKRCGRX.js"; | ||
| import "./chunk-DWXUUWMF.js"; | ||
| // src/mcp/server.ts | ||
| import { readFile } from "fs/promises"; | ||
| import { resolve } from "path"; | ||
| import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | ||
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | ||
| import * as z from "zod/v4"; | ||
| async function startMcpServer(projectPath, outputPath = ".atlas") { | ||
| const graphPath = resolve(projectPath, outputPath, "graph.json"); | ||
| const graph = JSON.parse(await readFile(graphPath, "utf8")); | ||
| const query = new GraphQuery(graph); | ||
| const server = new McpServer({ name: "atlas", version: graph.version }); | ||
| const result = (data) => ({ | ||
| content: [{ type: "text", text: JSON.stringify(data, null, 2) }], | ||
| structuredContent: data | ||
| }); | ||
| server.registerTool("atlas_find_node", { | ||
| description: "Find architecture nodes by name, label, type, file, route, or metadata.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => result({ results: query.findNode(value) })); | ||
| server.registerTool("atlas_get_node", { | ||
| description: "Get one node and its incoming, outgoing, and method relationships.", | ||
| inputSchema: { id: z.string().min(1) } | ||
| }, async ({ id }) => { | ||
| const node = query.getNode(id); | ||
| const outgoing = query.getOutgoing(id); | ||
| const methods = outgoing.filter((edge) => edge.type === "has_method").map((edge) => query.getNode(edge.to)).filter(Boolean); | ||
| return result({ node, incoming: query.getIncoming(id), outgoing, methods }); | ||
| }); | ||
| server.registerTool("atlas_get_dependencies", { | ||
| description: "Traverse outgoing architecture dependencies from a node.", | ||
| inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) } | ||
| }, async ({ id, depth }) => result({ graph: query.findDependencies(id, depth) })); | ||
| server.registerTool("atlas_get_dependents", { | ||
| description: "Traverse incoming architecture dependents of a node.", | ||
| inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) } | ||
| }, async ({ id, depth }) => result({ graph: query.findDependents(id, depth) })); | ||
| server.registerTool("atlas_find_routes", { description: "List all detected HTTP routes." }, async () => result({ routes: query.findRoutes() })); | ||
| server.registerTool("atlas_find_flow", { | ||
| description: "Find a route and return its route-to-controller-to-service-to-data flow.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => { | ||
| const route = query.findNode(value).find((node) => node.type === "route"); | ||
| return result({ route: route ?? null, flow: route ? query.findFlowFromRoute(route.id) : { nodes: [], edges: [] } }); | ||
| }); | ||
| server.registerTool("atlas_find_tables", { description: "List detected database tables." }, async () => result({ tables: query.findTables() })); | ||
| server.registerTool("atlas_find_data_model", { description: "List schemas, tables, indexes, constraints, migrations, and ClickHouse structures." }, async () => result({ | ||
| schemas: query.findSchemas(), | ||
| tables: query.findTables(), | ||
| indexes: query.findIndexes(), | ||
| constraints: query.findConstraints(), | ||
| migrations: query.findMigrations() | ||
| })); | ||
| server.registerTool("atlas_get_table_profile", { | ||
| description: "Return a table with its columns, indexes, constraints, relations, migrations, readers, and writers.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => { | ||
| const table = query.findNode(value).find((node) => node.type === "table"); | ||
| return result({ table: table ?? null, profile: table ? query.findTableProfile(table.id) : { nodes: [], edges: [] } }); | ||
| }); | ||
| server.registerTool("atlas_find_migrations", { description: "List migrations and the structures they create, alter, or drop." }, async () => result({ migrations: query.findMigrations() })); | ||
| server.registerTool("atlas_find_external_apis", { description: "List detected external API hosts." }, async () => result({ externalApis: query.findExternalApis() })); | ||
| server.registerTool("atlas_find_async_flows", { | ||
| description: "List detected Kafka or RabbitMQ topics, Bull/BullMQ queues, and background processors." | ||
| }, async () => result({ topics: query.findMessageTopics(), queues: query.findQueues(), processors: query.findProcessors() })); | ||
| server.registerTool("atlas_find_async_flow", { | ||
| description: "Find a message topic or queue and return publishers, consumers, processors, and downstream calls.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => { | ||
| const root = query.findNode(value).find((node) => ["message_topic", "queue"].includes(node.type)); | ||
| return result({ root: root ?? null, flow: root ? query.findAsyncFlow(root.id) : { nodes: [], edges: [] } }); | ||
| }); | ||
| server.registerTool("atlas_find_schedules", { description: "List cron, interval, timeout, repeatable queue, and Kubernetes scheduled jobs." }, async () => result({ schedules: query.findScheduledJobs() })); | ||
| server.registerTool("atlas_find_delivery", { description: "List CI/CD workflows and runtime deployments." }, async () => result({ workflows: query.findWorkflows(), deployments: query.findDeployments() })); | ||
| server.registerTool("atlas_find_environments", { description: "List detected development, staging, production, and custom runtime environments." }, async () => result({ environments: query.findEnvironments() })); | ||
| server.registerTool("atlas_search", { | ||
| description: "Search the complete architecture graph.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => result({ results: query.search(value) })); | ||
| server.registerTool("atlas_project_summary", { description: "Return project identity and graph statistics." }, async () => result({ project: graph.project, stats: graph.stats })); | ||
| await server.connect(new StdioServerTransport()); | ||
| console.error(`Atlas MCP server ready: ${graphPath}`); | ||
| } | ||
| export { | ||
| startMcpServer | ||
| }; | ||
| //# sourceMappingURL=server-WM7LIGZ5.js.map |
| {"version":3,"sources":["../src/mcp/server.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport * as z from \"zod/v4\";\nimport { GraphQuery } from \"../core/graph.js\";\nimport type { ArchitectureGraph } from \"../core/types.js\";\n\nexport async function startMcpServer(projectPath: string, outputPath = \".atlas\"): Promise<void> {\n const graphPath = resolve(projectPath, outputPath, \"graph.json\");\n const graph = JSON.parse(await readFile(graphPath, \"utf8\")) as ArchitectureGraph;\n const query = new GraphQuery(graph);\n const server = new McpServer({ name: \"atlas\", version: graph.version });\n const result = (data: Record<string, unknown>) => ({\n content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }],\n structuredContent: data,\n });\n\n server.registerTool(\"atlas_find_node\", {\n description: \"Find architecture nodes by name, label, type, file, route, or metadata.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => result({ results: query.findNode(value) }));\n\n server.registerTool(\"atlas_get_node\", {\n description: \"Get one node and its incoming, outgoing, and method relationships.\",\n inputSchema: { id: z.string().min(1) },\n }, async ({ id }) => {\n const node = query.getNode(id);\n const outgoing = query.getOutgoing(id);\n const methods = outgoing.filter((edge) => edge.type === \"has_method\").map((edge) => query.getNode(edge.to)).filter(Boolean);\n return result({ node, incoming: query.getIncoming(id), outgoing, methods });\n });\n\n server.registerTool(\"atlas_get_dependencies\", {\n description: \"Traverse outgoing architecture dependencies from a node.\",\n inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) },\n }, async ({ id, depth }) => result({ graph: query.findDependencies(id, depth) }));\n\n server.registerTool(\"atlas_get_dependents\", {\n description: \"Traverse incoming architecture dependents of a node.\",\n inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) },\n }, async ({ id, depth }) => result({ graph: query.findDependents(id, depth) }));\n\n server.registerTool(\"atlas_find_routes\", { description: \"List all detected HTTP routes.\" }, async () => result({ routes: query.findRoutes() }));\n\n server.registerTool(\"atlas_find_flow\", {\n description: \"Find a route and return its route-to-controller-to-service-to-data flow.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => {\n const route = query.findNode(value).find((node) => node.type === \"route\");\n return result({ route: route ?? null, flow: route ? query.findFlowFromRoute(route.id) : { nodes: [], edges: [] } });\n });\n\n server.registerTool(\"atlas_find_tables\", { description: \"List detected database tables.\" }, async () => result({ tables: query.findTables() }));\n server.registerTool(\"atlas_find_data_model\", { description: \"List schemas, tables, indexes, constraints, migrations, and ClickHouse structures.\" }, async () => result({\n schemas: query.findSchemas(), tables: query.findTables(), indexes: query.findIndexes(), constraints: query.findConstraints(), migrations: query.findMigrations(),\n }));\n server.registerTool(\"atlas_get_table_profile\", {\n description: \"Return a table with its columns, indexes, constraints, relations, migrations, readers, and writers.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => {\n const table = query.findNode(value).find((node) => node.type === \"table\");\n return result({ table: table ?? null, profile: table ? query.findTableProfile(table.id) : { nodes: [], edges: [] } });\n });\n server.registerTool(\"atlas_find_migrations\", { description: \"List migrations and the structures they create, alter, or drop.\" }, async () => result({ migrations: query.findMigrations() }));\n server.registerTool(\"atlas_find_external_apis\", { description: \"List detected external API hosts.\" }, async () => result({ externalApis: query.findExternalApis() }));\n\n server.registerTool(\"atlas_find_async_flows\", {\n description: \"List detected Kafka or RabbitMQ topics, Bull/BullMQ queues, and background processors.\",\n }, async () => result({ topics: query.findMessageTopics(), queues: query.findQueues(), processors: query.findProcessors() }));\n\n server.registerTool(\"atlas_find_async_flow\", {\n description: \"Find a message topic or queue and return publishers, consumers, processors, and downstream calls.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => {\n const root = query.findNode(value).find((node) => [\"message_topic\", \"queue\"].includes(node.type));\n return result({ root: root ?? null, flow: root ? query.findAsyncFlow(root.id) : { nodes: [], edges: [] } });\n });\n\n server.registerTool(\"atlas_find_schedules\", { description: \"List cron, interval, timeout, repeatable queue, and Kubernetes scheduled jobs.\" }, async () => result({ schedules: query.findScheduledJobs() }));\n server.registerTool(\"atlas_find_delivery\", { description: \"List CI/CD workflows and runtime deployments.\" }, async () => result({ workflows: query.findWorkflows(), deployments: query.findDeployments() }));\n server.registerTool(\"atlas_find_environments\", { description: \"List detected development, staging, production, and custom runtime environments.\" }, async () => result({ environments: query.findEnvironments() }));\n\n server.registerTool(\"atlas_search\", {\n description: \"Search the complete architecture graph.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => result({ results: query.search(value) }));\n\n server.registerTool(\"atlas_project_summary\", { description: \"Return project identity and graph statistics.\" }, async () => result({ project: graph.project, stats: graph.stats }));\n\n await server.connect(new StdioServerTransport());\n console.error(`Atlas MCP server ready: ${graphPath}`);\n}\n"],"mappings":"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","names":[]} |
+13
-3
| #!/usr/bin/env node | ||
| import { | ||
| ATLAS_VERSION | ||
| } from "../chunk-DWXUUWMF.js"; | ||
@@ -8,4 +11,4 @@ // src/cli/index.ts | ||
| var program = new Command(); | ||
| program.name("atlas").description("Local architecture intelligence for NestJS projects").version("0.1.0"); | ||
| program.command("scan").description("Scan a local NestJS project and generate its architecture graph").option("-p, --path <path>", "project root", ".").option("-o, --output <path>", "output directory relative to the project", ".atlas").option("--format <format>", "output format", "json").option("--debug", "show diagnostic details", false).action(async (options) => { | ||
| program.name("atlas").description("Local architecture intelligence for NestJS projects").version(ATLAS_VERSION); | ||
| program.command("scan").description("Scan a local NestJS project and generate its architecture graph").option("-p, --path <path>", "project root", ".").option("-o, --output <path>", "output directory relative to the project", ".atlas").option("--format <format>", "output format", "json").option("--no-cache", "ignore the previous Atlas analysis and scan everything again").option("--debug", "show diagnostic details", false).action(async (options) => { | ||
| if (options.format !== "json") throw new Error(`Unsupported format: ${options.format}. Use json.`); | ||
@@ -17,2 +20,3 @@ const { scanProject } = await import("../index.js"); | ||
| outputPath: options.output, | ||
| incremental: options.cache, | ||
| debug: options.debug, | ||
@@ -47,4 +51,10 @@ onProgress: ({ message }) => console.log(message) | ||
| }); | ||
| program.command("merge-runtime").description("Merge locally observed runtime links into the generated architecture graph").option("-p, --path <path>", "project root", ".").option("-o, --output <path>", "Atlas output directory relative to the project", ".atlas").option("-i, --input <path>", "runtime JSONL file relative to the project").action(async ({ path, output, input }) => { | ||
| const { mergeRuntimeTrace } = await import("../index.js"); | ||
| const result = await mergeRuntimeTrace(path, output, input); | ||
| console.log(`Runtime evidence merged: ${result.metadata.runtimeEvents ?? 0} observations`); | ||
| console.log(`Graph updated: ${result.graph.stats.totalNodes} nodes, ${result.graph.stats.totalEdges} edges`); | ||
| }); | ||
| program.command("mcp").description("Start the Atlas MCP server over stdio").option("-p, --path <path>", "project root", ".").option("-o, --output <path>", "Atlas output directory relative to the project", ".atlas").action(async ({ path, output }) => { | ||
| const { startMcpServer } = await import("../server-MDSHY22G.js"); | ||
| const { startMcpServer } = await import("../server-WM7LIGZ5.js"); | ||
| await startMcpServer(resolve(path), output); | ||
@@ -51,0 +61,0 @@ }); |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { access } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { Command } from \"commander\";\n\nconst program = new Command();\nprogram.name(\"atlas\").description(\"Local architecture intelligence for NestJS projects\").version(\"0.1.0\");\n\nprogram.command(\"scan\")\n .description(\"Scan a local NestJS project and generate its architecture graph\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"output directory relative to the project\", \".atlas\")\n .option(\"--format <format>\", \"output format\", \"json\")\n .option(\"--debug\", \"show diagnostic details\", false)\n .action(async (options) => {\n if (options.format !== \"json\") throw new Error(`Unsupported format: ${options.format}. Use json.`);\n const { scanProject } = await import(\"../index.js\");\n console.log(\"Atlas scan started\");\n const result = await scanProject({\n projectPath: options.path,\n outputPath: options.output,\n debug: options.debug,\n onProgress: ({ message }) => console.log(message),\n });\n console.log(`Graph created: ${result.graph.stats.totalNodes} nodes, ${result.graph.stats.totalEdges} edges`);\n console.log(`Risks detected: ${result.risks.length}`);\n console.log(`Viewer created: ${resolve(result.outputPath, \"viewer\", \"index.html\")}`);\n console.log(\"Done\");\n });\n\nprogram.command(\"open\")\n .description(\"Open the generated static viewer\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .action(async ({ path, output }) => {\n const { openBrowser } = await import(\"../server/open-browser.js\");\n const file = resolve(path, output, \"viewer\", \"index.html\");\n await access(file);\n await openBrowser(file);\n console.log(`Opened ${file}`);\n });\n\nprogram.command(\"serve\")\n .description(\"Serve the generated viewer on localhost\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .option(\"--port <port>\", \"local port\", \"4317\")\n .option(\"--open\", \"open the viewer in a browser\", false)\n .action(async ({ path, output, port, open }) => {\n const numericPort = Number.parseInt(port, 10);\n if (!Number.isInteger(numericPort) || numericPort < 1 || numericPort > 65535) throw new Error(`Invalid port: ${port}`);\n const { serveViewer } = await import(\"../server/viewer-server.js\");\n await serveViewer(resolve(path, output, \"viewer\"), numericPort);\n if (open) {\n const { openBrowser } = await import(\"../server/open-browser.js\");\n await openBrowser(`http://localhost:${numericPort}`);\n }\n });\n\nprogram.command(\"report\")\n .description(\"Regenerate report.md from graph.json and risks.json\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .action(async ({ path, output }) => {\n const { regenerateReport } = await import(\"../index.js\");\n console.log(`Report created: ${await regenerateReport(resolve(path), output)}`);\n });\n\nprogram.command(\"mcp\")\n .description(\"Start the Atlas MCP server over stdio\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .action(async ({ path, output }) => {\n const { startMcpServer } = await import(\"../mcp/server.js\");\n await startMcpServer(resolve(path), output);\n });\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(`Atlas error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { access } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { Command } from \"commander\";\nimport { ATLAS_VERSION } from \"../version.js\";\n\nconst program = new Command();\nprogram.name(\"atlas\").description(\"Local architecture intelligence for NestJS projects\").version(ATLAS_VERSION);\n\nprogram.command(\"scan\")\n .description(\"Scan a local NestJS project and generate its architecture graph\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"output directory relative to the project\", \".atlas\")\n .option(\"--format <format>\", \"output format\", \"json\")\n .option(\"--no-cache\", \"ignore the previous Atlas analysis and scan everything again\")\n .option(\"--debug\", \"show diagnostic details\", false)\n .action(async (options) => {\n if (options.format !== \"json\") throw new Error(`Unsupported format: ${options.format}. Use json.`);\n const { scanProject } = await import(\"../index.js\");\n console.log(\"Atlas scan started\");\n const result = await scanProject({\n projectPath: options.path,\n outputPath: options.output,\n incremental: options.cache,\n debug: options.debug,\n onProgress: ({ message }) => console.log(message),\n });\n console.log(`Graph created: ${result.graph.stats.totalNodes} nodes, ${result.graph.stats.totalEdges} edges`);\n console.log(`Risks detected: ${result.risks.length}`);\n console.log(`Viewer created: ${resolve(result.outputPath, \"viewer\", \"index.html\")}`);\n console.log(\"Done\");\n });\n\nprogram.command(\"open\")\n .description(\"Open the generated static viewer\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .action(async ({ path, output }) => {\n const { openBrowser } = await import(\"../server/open-browser.js\");\n const file = resolve(path, output, \"viewer\", \"index.html\");\n await access(file);\n await openBrowser(file);\n console.log(`Opened ${file}`);\n });\n\nprogram.command(\"serve\")\n .description(\"Serve the generated viewer on localhost\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .option(\"--port <port>\", \"local port\", \"4317\")\n .option(\"--open\", \"open the viewer in a browser\", false)\n .action(async ({ path, output, port, open }) => {\n const numericPort = Number.parseInt(port, 10);\n if (!Number.isInteger(numericPort) || numericPort < 1 || numericPort > 65535) throw new Error(`Invalid port: ${port}`);\n const { serveViewer } = await import(\"../server/viewer-server.js\");\n await serveViewer(resolve(path, output, \"viewer\"), numericPort);\n if (open) {\n const { openBrowser } = await import(\"../server/open-browser.js\");\n await openBrowser(`http://localhost:${numericPort}`);\n }\n });\n\nprogram.command(\"report\")\n .description(\"Regenerate report.md from graph.json and risks.json\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .action(async ({ path, output }) => {\n const { regenerateReport } = await import(\"../index.js\");\n console.log(`Report created: ${await regenerateReport(resolve(path), output)}`);\n });\n\nprogram.command(\"merge-runtime\")\n .description(\"Merge locally observed runtime links into the generated architecture graph\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .option(\"-i, --input <path>\", \"runtime JSONL file relative to the project\")\n .action(async ({ path, output, input }) => {\n const { mergeRuntimeTrace } = await import(\"../index.js\");\n const result = await mergeRuntimeTrace(path, output, input);\n console.log(`Runtime evidence merged: ${result.metadata.runtimeEvents ?? 0} observations`);\n console.log(`Graph updated: ${result.graph.stats.totalNodes} nodes, ${result.graph.stats.totalEdges} edges`);\n });\n\nprogram.command(\"mcp\")\n .description(\"Start the Atlas MCP server over stdio\")\n .option(\"-p, --path <path>\", \"project root\", \".\")\n .option(\"-o, --output <path>\", \"Atlas output directory relative to the project\", \".atlas\")\n .action(async ({ path, output }) => {\n const { startMcpServer } = await import(\"../mcp/server.js\");\n await startMcpServer(resolve(path), output);\n });\n\nprogram.parseAsync(process.argv).catch((error: unknown) => {\n console.error(`Atlas error: ${error instanceof Error ? error.message : String(error)}`);\n process.exitCode = 1;\n});\n"],"mappings":";;;;;;AACA,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,eAAe;AAGxB,IAAM,UAAU,IAAI,QAAQ;AAC5B,QAAQ,KAAK,OAAO,EAAE,YAAY,qDAAqD,EAAE,QAAQ,aAAa;AAE9G,QAAQ,QAAQ,MAAM,EACnB,YAAY,iEAAiE,EAC7E,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,uBAAuB,4CAA4C,QAAQ,EAClF,OAAO,qBAAqB,iBAAiB,MAAM,EACnD,OAAO,cAAc,8DAA8D,EACnF,OAAO,WAAW,2BAA2B,KAAK,EAClD,OAAO,OAAO,YAAY;AACzB,MAAI,QAAQ,WAAW,OAAQ,OAAM,IAAI,MAAM,uBAAuB,QAAQ,MAAM,aAAa;AACjG,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,aAAa;AAClD,UAAQ,IAAI,oBAAoB;AAChC,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B,aAAa,QAAQ;AAAA,IACrB,YAAY,QAAQ;AAAA,IACpB,aAAa,QAAQ;AAAA,IACrB,OAAO,QAAQ;AAAA,IACf,YAAY,CAAC,EAAE,QAAQ,MAAM,QAAQ,IAAI,OAAO;AAAA,EAClD,CAAC;AACD,UAAQ,IAAI,kBAAkB,OAAO,MAAM,MAAM,UAAU,WAAW,OAAO,MAAM,MAAM,UAAU,QAAQ;AAC3G,UAAQ,IAAI,mBAAmB,OAAO,MAAM,MAAM,EAAE;AACpD,UAAQ,IAAI,mBAAmB,QAAQ,OAAO,YAAY,UAAU,YAAY,CAAC,EAAE;AACnF,UAAQ,IAAI,MAAM;AACpB,CAAC;AAEH,QAAQ,QAAQ,MAAM,EACnB,YAAY,kCAAkC,EAC9C,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,uBAAuB,kDAAkD,QAAQ,EACxF,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM;AAClC,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,6BAA2B;AAChE,QAAM,OAAO,QAAQ,MAAM,QAAQ,UAAU,YAAY;AACzD,QAAM,OAAO,IAAI;AACjB,QAAM,YAAY,IAAI;AACtB,UAAQ,IAAI,UAAU,IAAI,EAAE;AAC9B,CAAC;AAEH,QAAQ,QAAQ,OAAO,EACpB,YAAY,yCAAyC,EACrD,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,uBAAuB,kDAAkD,QAAQ,EACxF,OAAO,iBAAiB,cAAc,MAAM,EAC5C,OAAO,UAAU,gCAAgC,KAAK,EACtD,OAAO,OAAO,EAAE,MAAM,QAAQ,MAAM,KAAK,MAAM;AAC9C,QAAM,cAAc,OAAO,SAAS,MAAM,EAAE;AAC5C,MAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,KAAK,cAAc,MAAO,OAAM,IAAI,MAAM,iBAAiB,IAAI,EAAE;AACrH,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,8BAA4B;AACjE,QAAM,YAAY,QAAQ,MAAM,QAAQ,QAAQ,GAAG,WAAW;AAC9D,MAAI,MAAM;AACR,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,6BAA2B;AAChE,UAAM,YAAY,oBAAoB,WAAW,EAAE;AAAA,EACrD;AACF,CAAC;AAEH,QAAQ,QAAQ,QAAQ,EACrB,YAAY,qDAAqD,EACjE,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,uBAAuB,kDAAkD,QAAQ,EACxF,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM;AAClC,QAAM,EAAE,iBAAiB,IAAI,MAAM,OAAO,aAAa;AACvD,UAAQ,IAAI,mBAAmB,MAAM,iBAAiB,QAAQ,IAAI,GAAG,MAAM,CAAC,EAAE;AAChF,CAAC;AAEH,QAAQ,QAAQ,eAAe,EAC5B,YAAY,4EAA4E,EACxF,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,uBAAuB,kDAAkD,QAAQ,EACxF,OAAO,sBAAsB,4CAA4C,EACzE,OAAO,OAAO,EAAE,MAAM,QAAQ,MAAM,MAAM;AACzC,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,aAAa;AACxD,QAAM,SAAS,MAAM,kBAAkB,MAAM,QAAQ,KAAK;AAC1D,UAAQ,IAAI,4BAA4B,OAAO,SAAS,iBAAiB,CAAC,eAAe;AACzF,UAAQ,IAAI,kBAAkB,OAAO,MAAM,MAAM,UAAU,WAAW,OAAO,MAAM,MAAM,UAAU,QAAQ;AAC7G,CAAC;AAEH,QAAQ,QAAQ,KAAK,EAClB,YAAY,uCAAuC,EACnD,OAAO,qBAAqB,gBAAgB,GAAG,EAC/C,OAAO,uBAAuB,kDAAkD,QAAQ,EACxF,OAAO,OAAO,EAAE,MAAM,OAAO,MAAM;AAClC,QAAM,EAAE,eAAe,IAAI,MAAM,OAAO,uBAAkB;AAC1D,QAAM,eAAe,QAAQ,IAAI,GAAG,MAAM;AAC5C,CAAC;AAEH,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,UAAmB;AACzD,UAAQ,MAAM,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AACtF,UAAQ,WAAW;AACrB,CAAC;","names":[]} |
+89
-2
@@ -7,3 +7,3 @@ import { Server } from 'node:http'; | ||
| type GraphEdgeType = (typeof graphEdgeTypes)[number]; | ||
| type GraphSourceType = "static_analysis" | "ast" | "config" | "package_json" | "heuristic" | "manual"; | ||
| type GraphSourceType = "static_analysis" | "ast" | "config" | "package_json" | "heuristic" | "runtime" | "manual"; | ||
| interface SourceLocation { | ||
@@ -77,2 +77,11 @@ file: string; | ||
| filesIgnored: number; | ||
| filesHashed?: number; | ||
| filesReused?: number; | ||
| cacheHit?: boolean; | ||
| inputFingerprint?: string; | ||
| analysisCacheVersion?: number; | ||
| viewerFingerprint?: string; | ||
| runtimeEvents?: number; | ||
| runtimeMergedAt?: string; | ||
| runtimeFingerprint?: string; | ||
| detectedStacks: DetectedStack[]; | ||
@@ -109,2 +118,3 @@ } | ||
| outputPath?: string; | ||
| incremental?: boolean; | ||
| debug?: boolean; | ||
@@ -119,2 +129,19 @@ onProgress?: (progress: ScanProgress) => void; | ||
| } | ||
| interface RuntimeTraceNode { | ||
| id: string; | ||
| type: GraphNodeType; | ||
| label?: string; | ||
| file?: string; | ||
| } | ||
| interface RuntimeTraceEvent { | ||
| from: string; | ||
| to: string; | ||
| type: GraphEdgeType; | ||
| timestamp?: string; | ||
| count?: number; | ||
| durationMs?: number; | ||
| fromNode?: RuntimeTraceNode; | ||
| toNode?: RuntimeTraceNode; | ||
| metadata?: Record<string, unknown>; | ||
| } | ||
@@ -137,2 +164,6 @@ declare function enrichGraphDescriptions(graph: ArchitectureGraph): ArchitectureGraph; | ||
| private readonly nodeMap; | ||
| private readonly edgeMap; | ||
| private readonly incomingMap; | ||
| private readonly outgoingMap; | ||
| private readonly nodesByType; | ||
| constructor(graph: ArchitectureGraph); | ||
@@ -186,12 +217,68 @@ findNode(query: string): GraphNode[]; | ||
| ignored: number; | ||
| hashed: number; | ||
| reused: number; | ||
| } | ||
| interface FileScanOptions { | ||
| ignoredPaths?: string[]; | ||
| cachePath?: string; | ||
| useCache?: boolean; | ||
| concurrency?: number; | ||
| } | ||
| declare function scanFiles(projectRoot: string, options?: FileScanOptions): Promise<FileScanResult>; | ||
| declare function readRuntimeEvents(path: string): Promise<RuntimeTraceEvent[]>; | ||
| declare function mergeRuntimeEvidence(graph: ArchitectureGraph, events: RuntimeTraceEvent[]): ArchitectureGraph; | ||
| interface RuntimeTracerOptions { | ||
| outputPath?: string; | ||
| flushIntervalMs?: number; | ||
| } | ||
| declare class RuntimeTracer { | ||
| private readonly outputPath; | ||
| private readonly flushIntervalMs; | ||
| private readonly pending; | ||
| private flushTimer; | ||
| constructor(options?: RuntimeTracerOptions); | ||
| record(event: RuntimeTraceEvent): void; | ||
| edge(from: string, to: string, type: GraphEdgeType, metadata?: Record<string, unknown>): void; | ||
| flush(): Promise<void>; | ||
| private scheduleFlush; | ||
| } | ||
| interface NestExecutionContextLike { | ||
| getClass(): { | ||
| name?: string; | ||
| }; | ||
| getHandler(): { | ||
| name?: string; | ||
| }; | ||
| getType?(): string; | ||
| switchToHttp?(): { | ||
| getRequest(): { | ||
| method?: string; | ||
| baseUrl?: string; | ||
| route?: { | ||
| path?: string; | ||
| }; | ||
| url?: string; | ||
| }; | ||
| }; | ||
| switchToRpc?(): { | ||
| getContext(): { | ||
| getPattern?(): unknown; | ||
| pattern?: unknown; | ||
| }; | ||
| }; | ||
| } | ||
| interface NestCallHandlerLike { | ||
| handle(): unknown; | ||
| } | ||
| declare function createNestRuntimeInterceptor(tracer: RuntimeTracer): { | ||
| intercept(context: NestExecutionContextLike, next: NestCallHandlerLike): unknown; | ||
| }; | ||
| declare function scanProject(options: ScanOptions): Promise<ScanResult>; | ||
| declare function loadGraph(projectPath: string, outputPath?: string): Promise<ArchitectureGraph>; | ||
| declare function regenerateReport(projectPath: string, outputPath?: string): Promise<string>; | ||
| declare function mergeRuntimeTrace(projectPath: string, outputPath?: string, inputPath?: string): Promise<ScanResult>; | ||
| export { type ArchitectureGraph, type ArchitectureRisk, type DetectedStack, type FileScanOptions, type FileScanResult, GraphBuilder, type GraphEdge, type GraphEdgeType, type GraphNode, type GraphNodeType, GraphQuery, type GraphSearchResult, type GraphSourceType, type GraphStats, type GraphSubgraph, type RiskSeverity, type ScanMetadata, type ScanOptions, type ScanProgress, type ScanProgressStage, type ScanResult, type ScannedFile, type SourceLocation, detectRisks, enrichGraphDescriptions, generateReport, getBrowserLaunch, graphEdgeTypes, graphNodeTypes, loadGraph, openBrowser, regenerateReport, scanFiles, scanProject, serveViewer }; | ||
| export { type ArchitectureGraph, type ArchitectureRisk, type DetectedStack, type FileScanOptions, type FileScanResult, GraphBuilder, type GraphEdge, type GraphEdgeType, type GraphNode, type GraphNodeType, GraphQuery, type GraphSearchResult, type GraphSourceType, type GraphStats, type GraphSubgraph, type RiskSeverity, type RuntimeTraceEvent, type RuntimeTraceNode, RuntimeTracer, type RuntimeTracerOptions, type ScanMetadata, type ScanOptions, type ScanProgress, type ScanProgressStage, type ScanResult, type ScannedFile, type SourceLocation, createNestRuntimeInterceptor, detectRisks, enrichGraphDescriptions, generateReport, getBrowserLaunch, graphEdgeTypes, graphNodeTypes, loadGraph, mergeRuntimeEvidence, mergeRuntimeTrace, openBrowser, readRuntimeEvents, regenerateReport, scanFiles, scanProject, serveViewer }; |
+1
-1
| { | ||
| "name": "@dthreads/atlas", | ||
| "version": "0.2.0", | ||
| "version": "0.3.0", | ||
| "description": "Architecture intelligence for NestJS codebases.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+85
-8
@@ -90,3 +90,3 @@ # Atlas | ||
| ```bash | ||
| atlas scan [--path <project>] [--output <directory>] [--debug] | ||
| atlas scan [--path <project>] [--output <directory>] [--no-cache] [--debug] | ||
| ``` | ||
@@ -99,2 +99,11 @@ | ||
| Atlas keeps a local file manifest and reuses the generated architecture graph when | ||
| the supported source and configuration files have not changed. A warm scan avoids | ||
| parsing the project again. Use `--no-cache` after changing analyzer configuration or | ||
| whenever you explicitly want a complete scan: | ||
| ```bash | ||
| atlas scan --path ../my-nest-app --no-cache | ||
| ``` | ||
| The scanner respects rules from the project's root `.gitignore`. It also skips | ||
@@ -105,2 +114,15 @@ dependencies, generated output, caches, temporary directories, Git worktrees, | ||
| ### `atlas merge-runtime` | ||
| Merges locally observed runtime links with the static graph and regenerates the | ||
| viewer. Existing static links are marked as runtime-confirmed; newly observed links | ||
| remain clearly identified as runtime evidence. | ||
| ```bash | ||
| atlas merge-runtime [--path <project>] [--output <directory>] [--input <runtime.jsonl>] | ||
| ``` | ||
| The default input is `<project>/.atlas/runtime.jsonl`. Payloads, request bodies, | ||
| headers, message contents, and environment values are not recorded. | ||
| ### `atlas open` | ||
@@ -144,4 +166,8 @@ | ||
| - modules, controllers, services, providers, and dependency injection; | ||
| - DI token bindings through `@Inject`, `useClass`, `useExisting`, and `useFactory`, including factory dependencies; | ||
| - `forwardRef`, `forRoot`, `forRootAsync`, `register`, and `registerAsync` module wiring; | ||
| - routes, controller methods, service methods, and method calls; | ||
| - Kafka publishers and consumers declared with `ClientKafka`, `@MessagePattern`, and `@EventPattern`; | ||
| - Kafka publishers and consumers declared with `ClientKafka`, KafkaJS, `@MessagePattern`, and `@EventPattern`; | ||
| - NestJS CQRS command, query, and event bus calls linked to their handlers; | ||
| - in-process events declared with `EventEmitter2` and `@OnEvent`; | ||
| - RabbitMQ handlers declared with `@RabbitSubscribe` and `@RabbitRPC`; | ||
@@ -164,6 +190,48 @@ - Bull and BullMQ queues, producers, processors, and jobs; | ||
| Static analysis has limits. Dynamic modules, runtime-generated providers, reflection, | ||
| and indirect calls may not always be resolved. Every inferred graph item includes its | ||
| source and confidence so consumers can distinguish evidence from inference. | ||
| Static analysis has limits. Runtime-generated providers, reflection, and indirect | ||
| calls may not always be resolved from source alone. Every inferred graph item | ||
| includes its source and confidence. Optional runtime evidence can confirm important | ||
| paths without replacing or hiding the static evidence. | ||
| ## Optional runtime evidence | ||
| Static analysis should remain the default. For local development or integration | ||
| tests, Atlas also exports a small NestJS interceptor that records route/RPC handler | ||
| transitions. It only records graph identifiers, timestamps, and counters. | ||
| ```ts | ||
| import { | ||
| createNestRuntimeInterceptor, | ||
| RuntimeTracer, | ||
| } from "@dthreads/atlas"; | ||
| const tracer = new RuntimeTracer({ | ||
| outputPath: ".atlas/runtime.jsonl", | ||
| }); | ||
| app.useGlobalInterceptors(createNestRuntimeInterceptor(tracer)); | ||
| ``` | ||
| Call `await tracer.flush()` from the application's normal shutdown hook. Internal | ||
| transitions that cannot be observed by a NestJS interceptor can be recorded | ||
| explicitly: | ||
| ```ts | ||
| tracer.edge( | ||
| "method:CheckoutService.checkout", | ||
| "message_topic:orders.created", | ||
| "publishes_to", | ||
| ); | ||
| ``` | ||
| Run the application or its integration tests, then merge the observations: | ||
| ```bash | ||
| atlas merge-runtime | ||
| atlas serve | ||
| ``` | ||
| Runtime tracing is opt-in and local. Do not commit `runtime.jsonl` when architecture | ||
| names are confidential. | ||
| ## Generated files | ||
@@ -173,2 +241,4 @@ | ||
| .atlas/ | ||
| cache/ | ||
| files.json Local incremental-scan manifest | ||
| graph.json Typed nodes and relationships | ||
@@ -178,2 +248,3 @@ metadata.json Scan time, file counts, stack evidence | ||
| report.md Human-readable architecture summary | ||
| runtime.jsonl Optional local runtime observations | ||
| viewer/ | ||
@@ -194,2 +265,7 @@ index.html Offline architecture application | ||
| Large scenes use adaptive detail: off-screen elements are not rendered, distant | ||
| cards switch to a lightweight form, edge labels appear when useful, and animation | ||
| is bounded. Catalogs load in pages and remain fully searchable, so a large project | ||
| does not need thousands of DOM elements just to open one focused flow. | ||
| The interactive viewer UX reference is stored in | ||
@@ -277,6 +353,7 @@ [`docs/design/atlas-viewer-prototype.html`](docs/design/atlas-viewer-prototype.html). | ||
| The tests scan a representative NestJS fixture, validate route-to-database, | ||
| publisher-to-consumer, migration, schedule, and delivery flows, exercise all 18 MCP | ||
| publisher-to-consumer, DI-token, CQRS, runtime, migration, schedule, and delivery flows, exercise all 18 MCP | ||
| tools, verify architecture and deployment risks, and confirm that real secret values | ||
| never enter generated artifacts. The performance test generates 1,000 | ||
| TypeScript files, 100 controllers, 300 services, and 1,000 routes. | ||
| never enter generated artifacts. The performance suite generates 1,000 TypeScript | ||
| files, 100 controllers, 300 services, and 1,000 routes; it also checks warm-scan | ||
| reuse, indexed graph queries, viewport culling, and bounded animation. | ||
@@ -283,0 +360,0 @@ The detailed MVP requirements and their automated evidence are listed in |
| // src/core/types.ts | ||
| var graphNodeTypes = [ | ||
| "project", | ||
| "folder", | ||
| "file", | ||
| "package", | ||
| "module", | ||
| "controller", | ||
| "service", | ||
| "provider", | ||
| "repository", | ||
| "use_case", | ||
| "port", | ||
| "adapter", | ||
| "entity", | ||
| "dto", | ||
| "method", | ||
| "function", | ||
| "route", | ||
| "guard", | ||
| "pipe", | ||
| "interceptor", | ||
| "middleware", | ||
| "decorator", | ||
| "database", | ||
| "table", | ||
| "column", | ||
| "model", | ||
| "environment_variable", | ||
| "external_api", | ||
| "message_broker", | ||
| "message_topic", | ||
| "queue", | ||
| "processor", | ||
| "schema", | ||
| "index", | ||
| "constraint", | ||
| "migration", | ||
| "materialized_view", | ||
| "scheduled_job", | ||
| "workflow", | ||
| "pipeline_job", | ||
| "build_stage", | ||
| "container_image", | ||
| "container", | ||
| "deployment", | ||
| "infrastructure_service", | ||
| "ingress", | ||
| "config_map", | ||
| "secret", | ||
| "environment", | ||
| "config", | ||
| "test", | ||
| "library", | ||
| "risk" | ||
| ]; | ||
| var graphEdgeTypes = [ | ||
| "contains", | ||
| "imports", | ||
| "exports", | ||
| "declares", | ||
| "provides", | ||
| "injects", | ||
| "implements", | ||
| "calls", | ||
| "uses", | ||
| "reads", | ||
| "writes", | ||
| "handles", | ||
| "depends_on", | ||
| "decorates", | ||
| "validates", | ||
| "returns", | ||
| "references", | ||
| "connects_to", | ||
| "tests", | ||
| "has_method", | ||
| "has_column", | ||
| "publishes_to", | ||
| "delivers_to", | ||
| "enqueues", | ||
| "processes", | ||
| "creates", | ||
| "alters", | ||
| "drops", | ||
| "indexes", | ||
| "schedules", | ||
| "triggers", | ||
| "builds", | ||
| "publishes", | ||
| "deploys", | ||
| "exposes", | ||
| "configures", | ||
| "runs_in", | ||
| "targets" | ||
| ]; | ||
| // src/core/graph.ts | ||
| var validNodeTypes = new Set(graphNodeTypes); | ||
| var validEdgeTypes = new Set(graphEdgeTypes); | ||
| var GraphBuilder = class { | ||
| nodes = /* @__PURE__ */ new Map(); | ||
| edges = /* @__PURE__ */ new Map(); | ||
| addNode(node) { | ||
| const current = this.nodes.get(node.id); | ||
| if (!current) { | ||
| this.nodes.set(node.id, node); | ||
| return node; | ||
| } | ||
| const merged = { | ||
| ...current, | ||
| ...node, | ||
| metadata: { ...current.metadata, ...node.metadata } | ||
| }; | ||
| this.nodes.set(node.id, merged); | ||
| return merged; | ||
| } | ||
| addEdge(edge) { | ||
| if (!this.nodes.has(edge.from) || !this.nodes.has(edge.to)) return null; | ||
| const key = `${edge.from}|${edge.type}|${edge.to}|${edge.label ?? ""}`; | ||
| const id = edge.id ?? `edge:${encodeURIComponent(key)}`; | ||
| const result = { ...edge, id }; | ||
| this.edges.set(key, result); | ||
| return result; | ||
| } | ||
| hasNode(id) { | ||
| return this.nodes.has(id); | ||
| } | ||
| validate() { | ||
| const errors = []; | ||
| for (const node of this.nodes.values()) { | ||
| if (!validNodeTypes.has(String(node.type))) errors.push(`${node.id}: invalid node type ${String(node.type)}`); | ||
| } | ||
| for (const edge of this.edges.values()) { | ||
| if (!validEdgeTypes.has(String(edge.type))) errors.push(`${edge.id}: invalid edge type ${String(edge.type)}`); | ||
| if (!this.nodes.has(edge.from)) errors.push(`${edge.id}: missing source ${edge.from}`); | ||
| if (!this.nodes.has(edge.to)) errors.push(`${edge.id}: missing target ${edge.to}`); | ||
| } | ||
| return errors; | ||
| } | ||
| toGraph(project) { | ||
| const nodes = [...this.nodes.values()].sort((a, b) => a.id.localeCompare(b.id)); | ||
| const edges = [...this.edges.values()].sort((a, b) => a.id.localeCompare(b.id)); | ||
| return { version: "0.1.0", project, nodes, edges, stats: buildStats(nodes, edges) }; | ||
| } | ||
| }; | ||
| function increment(target, key) { | ||
| target[key] = (target[key] ?? 0) + 1; | ||
| } | ||
| function buildStats(nodes, edges) { | ||
| const byNodeType = /* @__PURE__ */ Object.create(null); | ||
| const byEdgeType = /* @__PURE__ */ Object.create(null); | ||
| for (const node of nodes) increment(byNodeType, node.type); | ||
| for (const edge of edges) increment(byEdgeType, edge.type); | ||
| return { totalNodes: nodes.length, totalEdges: edges.length, byNodeType, byEdgeType }; | ||
| } | ||
| var GraphQuery = class { | ||
| constructor(graph) { | ||
| this.graph = graph; | ||
| this.nodeMap = new Map(graph.nodes.map((node) => [node.id, node])); | ||
| } | ||
| graph; | ||
| nodeMap; | ||
| findNode(query) { | ||
| const needle = query.trim().toLowerCase(); | ||
| if (!needle) return []; | ||
| return this.graph.nodes.filter((node) => searchableNode(node).includes(needle)).sort((a, b) => scoreNode(b, needle) - scoreNode(a, needle)).slice(0, 100); | ||
| } | ||
| search(query) { | ||
| const needle = query.trim().toLowerCase(); | ||
| if (!needle) return []; | ||
| return this.findNode(query).map((node) => ({ | ||
| node, | ||
| score: scoreNode(node, needle), | ||
| matches: matchingFields(node, needle) | ||
| })); | ||
| } | ||
| getNode(id) { | ||
| return this.nodeMap.get(id) ?? null; | ||
| } | ||
| getIncoming(id) { | ||
| return this.graph.edges.filter((edge) => edge.to === id); | ||
| } | ||
| getOutgoing(id) { | ||
| return this.graph.edges.filter((edge) => edge.from === id); | ||
| } | ||
| findRoutes() { | ||
| return this.byType("route"); | ||
| } | ||
| findServices() { | ||
| return this.byType("service"); | ||
| } | ||
| findControllers() { | ||
| return this.byType("controller"); | ||
| } | ||
| findTables() { | ||
| return this.byType("table"); | ||
| } | ||
| findSchemas() { | ||
| return this.byType("schema"); | ||
| } | ||
| findIndexes() { | ||
| return this.byType("index"); | ||
| } | ||
| findConstraints() { | ||
| return this.byType("constraint"); | ||
| } | ||
| findMigrations() { | ||
| return this.byType("migration"); | ||
| } | ||
| findScheduledJobs() { | ||
| return this.byType("scheduled_job"); | ||
| } | ||
| findWorkflows() { | ||
| return this.byType("workflow"); | ||
| } | ||
| findDeployments() { | ||
| return this.byType("deployment"); | ||
| } | ||
| findEnvironments() { | ||
| return this.byType("environment"); | ||
| } | ||
| findExternalApis() { | ||
| return this.byType("external_api"); | ||
| } | ||
| findMessageTopics() { | ||
| return this.byType("message_topic"); | ||
| } | ||
| findQueues() { | ||
| return this.byType("queue"); | ||
| } | ||
| findProcessors() { | ||
| return this.byType("processor"); | ||
| } | ||
| findTableProfile(tableId) { | ||
| const table = this.nodeMap.get(tableId); | ||
| if (!table || table.type !== "table") return { nodes: [], edges: [] }; | ||
| const edgeTypes = /* @__PURE__ */ new Set(["has_column", "indexes", "contains", "references", "reads", "writes", "creates", "alters", "drops"]); | ||
| const edges = this.graph.edges.filter((edge) => edgeTypes.has(edge.type) && (edge.from === tableId || edge.to === tableId)); | ||
| const ids = /* @__PURE__ */ new Set([tableId, ...edges.flatMap((edge) => [edge.from, edge.to])]); | ||
| return { nodes: [...ids].map((id) => this.nodeMap.get(id)).filter(Boolean), edges }; | ||
| } | ||
| getNeighbors(nodeId, depth = 1) { | ||
| if (!this.nodeMap.has(nodeId)) return { nodes: [], edges: [] }; | ||
| const nodeIds = /* @__PURE__ */ new Set([nodeId]); | ||
| const edgeIds = /* @__PURE__ */ new Set(); | ||
| let frontier = [nodeId]; | ||
| for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) { | ||
| const next = []; | ||
| for (const id of frontier) { | ||
| for (const edge of [...this.getIncoming(id), ...this.getOutgoing(id)]) { | ||
| edgeIds.add(edge.id); | ||
| const neighbor = edge.from === id ? edge.to : edge.from; | ||
| if (!nodeIds.has(neighbor)) { | ||
| nodeIds.add(neighbor); | ||
| next.push(neighbor); | ||
| } | ||
| } | ||
| } | ||
| frontier = next; | ||
| } | ||
| return { | ||
| nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean), | ||
| edges: this.graph.edges.filter((edge) => edgeIds.has(edge.id)) | ||
| }; | ||
| } | ||
| findFlowFromRoute(routeId) { | ||
| return this.walk(routeId, "outgoing", 12, /* @__PURE__ */ new Set([ | ||
| "handles", | ||
| "calls", | ||
| "reads", | ||
| "writes", | ||
| "uses", | ||
| "connects_to", | ||
| "validates", | ||
| "returns", | ||
| "publishes_to", | ||
| "delivers_to", | ||
| "enqueues", | ||
| "processes" | ||
| ])); | ||
| } | ||
| findAsyncFlow(rootId) { | ||
| const root = this.nodeMap.get(rootId); | ||
| if (!root || !["message_topic", "queue"].includes(root.type)) return { nodes: [], edges: [] }; | ||
| const flow = this.walk(rootId, "outgoing", 12, /* @__PURE__ */ new Set([ | ||
| "delivers_to", | ||
| "calls", | ||
| "reads", | ||
| "writes", | ||
| "uses", | ||
| "connects_to", | ||
| "publishes_to", | ||
| "enqueues", | ||
| "processes" | ||
| ])); | ||
| const nodeIds = new Set(flow.nodes.map((node) => node.id)); | ||
| const edgeIds = new Set(flow.edges.map((edge) => edge.id)); | ||
| for (const edge of this.getIncoming(rootId).filter((item) => ["publishes_to", "enqueues"].includes(item.type))) { | ||
| nodeIds.add(edge.from); | ||
| edgeIds.add(edge.id); | ||
| } | ||
| return { | ||
| nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean), | ||
| edges: this.graph.edges.filter((edge) => edgeIds.has(edge.id)) | ||
| }; | ||
| } | ||
| findDependencies(nodeId, depth = 2) { | ||
| return this.walk(nodeId, "outgoing", depth); | ||
| } | ||
| findDependents(nodeId, depth = 2) { | ||
| return this.walk(nodeId, "incoming", depth); | ||
| } | ||
| byType(type) { | ||
| return this.graph.nodes.filter((node) => node.type === type); | ||
| } | ||
| walk(startId, direction, depth, allowedTypes) { | ||
| if (!this.nodeMap.has(startId)) return { nodes: [], edges: [] }; | ||
| const nodeIds = /* @__PURE__ */ new Set([startId]); | ||
| const edgeIds = /* @__PURE__ */ new Set(); | ||
| let frontier = [startId]; | ||
| for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) { | ||
| const next = []; | ||
| for (const id of frontier) { | ||
| const edges = direction === "outgoing" ? this.getOutgoing(id) : this.getIncoming(id); | ||
| for (const edge of edges) { | ||
| if (allowedTypes && !allowedTypes.has(edge.type)) continue; | ||
| edgeIds.add(edge.id); | ||
| const neighbor = direction === "outgoing" ? edge.to : edge.from; | ||
| if (!nodeIds.has(neighbor)) { | ||
| nodeIds.add(neighbor); | ||
| next.push(neighbor); | ||
| } | ||
| } | ||
| } | ||
| frontier = next; | ||
| } | ||
| return { | ||
| nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean), | ||
| edges: this.graph.edges.filter((edge) => edgeIds.has(edge.id)) | ||
| }; | ||
| } | ||
| }; | ||
| function searchableNode(node) { | ||
| return [node.id, node.type, node.label, node.name, node.file, JSON.stringify(node.metadata ?? {})].filter(Boolean).join(" ").toLowerCase(); | ||
| } | ||
| function scoreNode(node, needle) { | ||
| const label = node.label.toLowerCase(); | ||
| if (label === needle) return 100; | ||
| if (label.startsWith(needle)) return 80; | ||
| if (node.id.toLowerCase().includes(needle)) return 60; | ||
| return 20; | ||
| } | ||
| function matchingFields(node, needle) { | ||
| const fields = { | ||
| id: node.id, | ||
| label: node.label, | ||
| name: node.name ?? "", | ||
| type: node.type, | ||
| file: node.file ?? "", | ||
| metadata: JSON.stringify(node.metadata ?? {}) | ||
| }; | ||
| return Object.entries(fields).filter(([, value]) => value.toLowerCase().includes(needle)).map(([key]) => key); | ||
| } | ||
| export { | ||
| graphNodeTypes, | ||
| graphEdgeTypes, | ||
| GraphBuilder, | ||
| GraphQuery | ||
| }; | ||
| //# sourceMappingURL=chunk-M2XP6F7I.js.map |
| {"version":3,"sources":["../src/core/types.ts","../src/core/graph.ts"],"sourcesContent":["export const graphNodeTypes = [\n \"project\", \"folder\", \"file\", \"package\", \"module\", \"controller\", \"service\",\n \"provider\", \"repository\", \"use_case\", \"port\", \"adapter\", \"entity\", \"dto\", \"method\", \"function\", \"route\",\n \"guard\", \"pipe\", \"interceptor\", \"middleware\", \"decorator\", \"database\",\n \"table\", \"column\", \"model\", \"environment_variable\", \"external_api\",\n \"message_broker\", \"message_topic\", \"queue\", \"processor\",\n \"schema\", \"index\", \"constraint\", \"migration\", \"materialized_view\",\n \"scheduled_job\", \"workflow\", \"pipeline_job\", \"build_stage\", \"container_image\",\n \"container\", \"deployment\", \"infrastructure_service\", \"ingress\", \"config_map\",\n \"secret\", \"environment\", \"config\", \"test\", \"library\", \"risk\",\n] as const;\n\nexport const graphEdgeTypes = [\n \"contains\", \"imports\", \"exports\", \"declares\", \"provides\", \"injects\", \"implements\", \"calls\",\n \"uses\", \"reads\", \"writes\", \"handles\", \"depends_on\", \"decorates\", \"validates\",\n \"returns\", \"references\", \"connects_to\", \"tests\", \"has_method\", \"has_column\",\n \"publishes_to\", \"delivers_to\", \"enqueues\", \"processes\",\n \"creates\", \"alters\", \"drops\", \"indexes\", \"schedules\", \"triggers\", \"builds\",\n \"publishes\", \"deploys\", \"exposes\", \"configures\", \"runs_in\", \"targets\",\n] as const;\n\nexport type GraphNodeType = (typeof graphNodeTypes)[number];\nexport type GraphEdgeType = (typeof graphEdgeTypes)[number];\nexport type GraphSourceType =\n | \"static_analysis\"\n | \"ast\"\n | \"config\"\n | \"package_json\"\n | \"heuristic\"\n | \"manual\";\n\nexport interface SourceLocation {\n file: string;\n startLine?: number;\n endLine?: number;\n}\n\nexport interface GraphNode {\n id: string;\n type: GraphNodeType;\n label: string;\n name?: string;\n file?: string;\n language?: string;\n framework?: string;\n sourceLocation?: SourceLocation;\n confidence?: number;\n source?: GraphSourceType;\n metadata?: Record<string, unknown>;\n}\n\nexport interface GraphEdge {\n id: string;\n from: string;\n to: string;\n type: GraphEdgeType;\n label?: string;\n confidence?: number;\n source?: GraphSourceType;\n metadata?: Record<string, unknown>;\n}\n\nexport interface DetectedStack {\n name: string;\n confidence: number;\n evidence: string[];\n}\n\nexport interface GraphStats {\n totalNodes: number;\n totalEdges: number;\n byNodeType: Partial<Record<GraphNodeType, number>>;\n byEdgeType: Partial<Record<GraphEdgeType, number>>;\n}\n\nexport interface ArchitectureGraph {\n version: string;\n project: {\n name: string;\n root: string;\n detectedStacks: string[];\n createdAt: string;\n };\n nodes: GraphNode[];\n edges: GraphEdge[];\n stats: GraphStats;\n}\n\nexport interface ScannedFile {\n absolutePath: string;\n path: string;\n extension: string;\n size: number;\n hash?: string;\n lastModified: string;\n}\n\nexport interface ScanMetadata {\n version: string;\n projectName: string;\n projectRoot: string;\n scanStartedAt: string;\n scanFinishedAt: string;\n durationMs: number;\n filesScanned: number;\n filesIgnored: number;\n detectedStacks: DetectedStack[];\n}\n\nexport type RiskSeverity = \"low\" | \"medium\" | \"high\" | \"critical\";\n\nexport interface ArchitectureRisk {\n id: string;\n type: string;\n severity: RiskSeverity;\n title: string;\n description: string;\n recommendation: string;\n nodeId?: string;\n file?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface GraphSubgraph {\n nodes: GraphNode[];\n edges: GraphEdge[];\n}\n\nexport interface GraphSearchResult {\n node: GraphNode;\n score: number;\n matches: string[];\n}\n\nexport type ScanProgressStage =\n | \"scan_files\"\n | \"detect_stack\"\n | \"parse_architecture\"\n | \"build_graph\"\n | \"detect_risks\"\n | \"write_outputs\";\n\nexport interface ScanProgress {\n stage: ScanProgressStage;\n message: string;\n}\n\nexport interface ScanOptions {\n projectPath: string;\n outputPath?: string;\n debug?: boolean;\n onProgress?: (progress: ScanProgress) => void;\n}\n\nexport interface ScanResult {\n graph: ArchitectureGraph;\n metadata: ScanMetadata;\n risks: ArchitectureRisk[];\n outputPath: string;\n}\n","import type {\n ArchitectureGraph,\n GraphEdge,\n GraphEdgeType,\n GraphNode,\n GraphSearchResult,\n GraphStats,\n GraphSubgraph,\n} from \"./types.js\";\nimport { graphEdgeTypes, graphNodeTypes } from \"./types.js\";\n\nconst validNodeTypes = new Set<string>(graphNodeTypes);\nconst validEdgeTypes = new Set<string>(graphEdgeTypes);\n\nexport class GraphBuilder {\n readonly nodes = new Map<string, GraphNode>();\n readonly edges = new Map<string, GraphEdge>();\n\n addNode(node: GraphNode): GraphNode {\n const current = this.nodes.get(node.id);\n if (!current) {\n this.nodes.set(node.id, node);\n return node;\n }\n const merged = {\n ...current,\n ...node,\n metadata: { ...current.metadata, ...node.metadata },\n };\n this.nodes.set(node.id, merged);\n return merged;\n }\n\n addEdge(edge: Omit<GraphEdge, \"id\"> & { id?: string }): GraphEdge | null {\n if (!this.nodes.has(edge.from) || !this.nodes.has(edge.to)) return null;\n const key = `${edge.from}|${edge.type}|${edge.to}|${edge.label ?? \"\"}`;\n const id = edge.id ?? `edge:${encodeURIComponent(key)}`;\n const result: GraphEdge = { ...edge, id };\n this.edges.set(key, result);\n return result;\n }\n\n hasNode(id: string): boolean {\n return this.nodes.has(id);\n }\n\n validate(): string[] {\n const errors: string[] = [];\n for (const node of this.nodes.values()) {\n if (!validNodeTypes.has(String(node.type))) errors.push(`${node.id}: invalid node type ${String(node.type)}`);\n }\n for (const edge of this.edges.values()) {\n if (!validEdgeTypes.has(String(edge.type))) errors.push(`${edge.id}: invalid edge type ${String(edge.type)}`);\n if (!this.nodes.has(edge.from)) errors.push(`${edge.id}: missing source ${edge.from}`);\n if (!this.nodes.has(edge.to)) errors.push(`${edge.id}: missing target ${edge.to}`);\n }\n return errors;\n }\n\n toGraph(project: ArchitectureGraph[\"project\"]): ArchitectureGraph {\n const nodes = [...this.nodes.values()].sort((a, b) => a.id.localeCompare(b.id));\n const edges = [...this.edges.values()].sort((a, b) => a.id.localeCompare(b.id));\n return { version: \"0.1.0\", project, nodes, edges, stats: buildStats(nodes, edges) };\n }\n}\n\nfunction increment<T extends string>(target: Partial<Record<T, number>>, key: T) {\n target[key] = (target[key] ?? 0) + 1;\n}\n\nexport function buildStats(nodes: GraphNode[], edges: GraphEdge[]): GraphStats {\n const byNodeType: GraphStats[\"byNodeType\"] = Object.create(null) as GraphStats[\"byNodeType\"];\n const byEdgeType: GraphStats[\"byEdgeType\"] = Object.create(null) as GraphStats[\"byEdgeType\"];\n for (const node of nodes) increment(byNodeType, node.type);\n for (const edge of edges) increment(byEdgeType, edge.type);\n return { totalNodes: nodes.length, totalEdges: edges.length, byNodeType, byEdgeType };\n}\n\nexport class GraphQuery {\n private readonly nodeMap: Map<string, GraphNode>;\n\n constructor(readonly graph: ArchitectureGraph) {\n this.nodeMap = new Map(graph.nodes.map((node) => [node.id, node]));\n }\n\n findNode(query: string): GraphNode[] {\n const needle = query.trim().toLowerCase();\n if (!needle) return [];\n return this.graph.nodes\n .filter((node) => searchableNode(node).includes(needle))\n .sort((a, b) => scoreNode(b, needle) - scoreNode(a, needle))\n .slice(0, 100);\n }\n\n search(query: string): GraphSearchResult[] {\n const needle = query.trim().toLowerCase();\n if (!needle) return [];\n return this.findNode(query).map((node) => ({\n node,\n score: scoreNode(node, needle),\n matches: matchingFields(node, needle),\n }));\n }\n\n getNode(id: string): GraphNode | null {\n return this.nodeMap.get(id) ?? null;\n }\n\n getIncoming(id: string): GraphEdge[] {\n return this.graph.edges.filter((edge) => edge.to === id);\n }\n\n getOutgoing(id: string): GraphEdge[] {\n return this.graph.edges.filter((edge) => edge.from === id);\n }\n\n findRoutes(): GraphNode[] { return this.byType(\"route\"); }\n findServices(): GraphNode[] { return this.byType(\"service\"); }\n findControllers(): GraphNode[] { return this.byType(\"controller\"); }\n findTables(): GraphNode[] { return this.byType(\"table\"); }\n findSchemas(): GraphNode[] { return this.byType(\"schema\"); }\n findIndexes(): GraphNode[] { return this.byType(\"index\"); }\n findConstraints(): GraphNode[] { return this.byType(\"constraint\"); }\n findMigrations(): GraphNode[] { return this.byType(\"migration\"); }\n findScheduledJobs(): GraphNode[] { return this.byType(\"scheduled_job\"); }\n findWorkflows(): GraphNode[] { return this.byType(\"workflow\"); }\n findDeployments(): GraphNode[] { return this.byType(\"deployment\"); }\n findEnvironments(): GraphNode[] { return this.byType(\"environment\"); }\n findExternalApis(): GraphNode[] { return this.byType(\"external_api\"); }\n findMessageTopics(): GraphNode[] { return this.byType(\"message_topic\"); }\n findQueues(): GraphNode[] { return this.byType(\"queue\"); }\n findProcessors(): GraphNode[] { return this.byType(\"processor\"); }\n\n findTableProfile(tableId: string): GraphSubgraph {\n const table = this.nodeMap.get(tableId);\n if (!table || table.type !== \"table\") return { nodes: [], edges: [] };\n const edgeTypes = new Set<GraphEdgeType>([\"has_column\", \"indexes\", \"contains\", \"references\", \"reads\", \"writes\", \"creates\", \"alters\", \"drops\"]);\n const edges = this.graph.edges.filter((edge) => edgeTypes.has(edge.type) && (edge.from === tableId || edge.to === tableId));\n const ids = new Set([tableId, ...edges.flatMap((edge) => [edge.from, edge.to])]);\n return { nodes: [...ids].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[], edges };\n }\n\n getNeighbors(nodeId: string, depth = 1): GraphSubgraph {\n if (!this.nodeMap.has(nodeId)) return { nodes: [], edges: [] };\n const nodeIds = new Set([nodeId]);\n const edgeIds = new Set<string>();\n let frontier = [nodeId];\n for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) {\n const next: string[] = [];\n for (const id of frontier) {\n for (const edge of [...this.getIncoming(id), ...this.getOutgoing(id)]) {\n edgeIds.add(edge.id);\n const neighbor = edge.from === id ? edge.to : edge.from;\n if (!nodeIds.has(neighbor)) { nodeIds.add(neighbor); next.push(neighbor); }\n }\n }\n frontier = next;\n }\n return {\n nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[],\n edges: this.graph.edges.filter((edge) => edgeIds.has(edge.id)),\n };\n }\n\n findFlowFromRoute(routeId: string): GraphSubgraph {\n return this.walk(routeId, \"outgoing\", 12, new Set([\n \"handles\", \"calls\", \"reads\", \"writes\", \"uses\", \"connects_to\", \"validates\", \"returns\",\n \"publishes_to\", \"delivers_to\", \"enqueues\", \"processes\",\n ]));\n }\n\n findAsyncFlow(rootId: string): GraphSubgraph {\n const root = this.nodeMap.get(rootId);\n if (!root || ![\"message_topic\", \"queue\"].includes(root.type)) return { nodes: [], edges: [] };\n const flow = this.walk(rootId, \"outgoing\", 12, new Set([\n \"delivers_to\", \"calls\", \"reads\", \"writes\", \"uses\", \"connects_to\", \"publishes_to\", \"enqueues\", \"processes\",\n ]));\n const nodeIds = new Set(flow.nodes.map((node) => node.id));\n const edgeIds = new Set(flow.edges.map((edge) => edge.id));\n for (const edge of this.getIncoming(rootId).filter((item) => [\"publishes_to\", \"enqueues\"].includes(item.type))) {\n nodeIds.add(edge.from);\n edgeIds.add(edge.id);\n }\n return {\n nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[],\n edges: this.graph.edges.filter((edge) => edgeIds.has(edge.id)),\n };\n }\n\n findDependencies(nodeId: string, depth = 2): GraphSubgraph {\n return this.walk(nodeId, \"outgoing\", depth);\n }\n\n findDependents(nodeId: string, depth = 2): GraphSubgraph {\n return this.walk(nodeId, \"incoming\", depth);\n }\n\n private byType(type: GraphNode[\"type\"]): GraphNode[] {\n return this.graph.nodes.filter((node) => node.type === type);\n }\n\n private walk(\n startId: string,\n direction: \"incoming\" | \"outgoing\",\n depth: number,\n allowedTypes?: Set<GraphEdgeType>,\n ): GraphSubgraph {\n if (!this.nodeMap.has(startId)) return { nodes: [], edges: [] };\n const nodeIds = new Set([startId]);\n const edgeIds = new Set<string>();\n let frontier = [startId];\n for (let level = 0; level < Math.max(0, depth) && frontier.length; level += 1) {\n const next: string[] = [];\n for (const id of frontier) {\n const edges = direction === \"outgoing\" ? this.getOutgoing(id) : this.getIncoming(id);\n for (const edge of edges) {\n if (allowedTypes && !allowedTypes.has(edge.type)) continue;\n edgeIds.add(edge.id);\n const neighbor = direction === \"outgoing\" ? edge.to : edge.from;\n if (!nodeIds.has(neighbor)) {\n nodeIds.add(neighbor);\n next.push(neighbor);\n }\n }\n }\n frontier = next;\n }\n return {\n nodes: [...nodeIds].map((id) => this.nodeMap.get(id)).filter(Boolean) as GraphNode[],\n edges: this.graph.edges.filter((edge) => edgeIds.has(edge.id)),\n };\n }\n}\n\nfunction searchableNode(node: GraphNode): string {\n return [node.id, node.type, node.label, node.name, node.file, JSON.stringify(node.metadata ?? {})]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n}\n\nfunction scoreNode(node: GraphNode, needle: string): number {\n const label = node.label.toLowerCase();\n if (label === needle) return 100;\n if (label.startsWith(needle)) return 80;\n if (node.id.toLowerCase().includes(needle)) return 60;\n return 20;\n}\n\nfunction matchingFields(node: GraphNode, needle: string): string[] {\n const fields = {\n id: node.id,\n label: node.label,\n name: node.name ?? \"\",\n type: node.type,\n file: node.file ?? \"\",\n metadata: JSON.stringify(node.metadata ?? {}),\n };\n return Object.entries(fields).filter(([, value]) => value.toLowerCase().includes(needle)).map(([key]) => key);\n}\n"],"mappings":";AAAO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EAAW;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAc;AAAA,EAChE;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAO;AAAA,EAAU;AAAA,EAAY;AAAA,EAChG;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAe;AAAA,EAAc;AAAA,EAAa;AAAA,EAC3D;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAwB;AAAA,EACpD;AAAA,EAAkB;AAAA,EAAiB;AAAA,EAAS;AAAA,EAC5C;AAAA,EAAU;AAAA,EAAS;AAAA,EAAc;AAAA,EAAa;AAAA,EAC9C;AAAA,EAAiB;AAAA,EAAY;AAAA,EAAgB;AAAA,EAAe;AAAA,EAC5D;AAAA,EAAa;AAAA,EAAc;AAAA,EAA0B;AAAA,EAAW;AAAA,EAChE;AAAA,EAAU;AAAA,EAAe;AAAA,EAAU;AAAA,EAAQ;AAAA,EAAW;AACxD;AAEO,IAAM,iBAAiB;AAAA,EAC5B;AAAA,EAAY;AAAA,EAAW;AAAA,EAAW;AAAA,EAAY;AAAA,EAAY;AAAA,EAAW;AAAA,EAAc;AAAA,EACnF;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAU;AAAA,EAAW;AAAA,EAAc;AAAA,EAAa;AAAA,EACjE;AAAA,EAAW;AAAA,EAAc;AAAA,EAAe;AAAA,EAAS;AAAA,EAAc;AAAA,EAC/D;AAAA,EAAgB;AAAA,EAAe;AAAA,EAAY;AAAA,EAC3C;AAAA,EAAW;AAAA,EAAU;AAAA,EAAS;AAAA,EAAW;AAAA,EAAa;AAAA,EAAY;AAAA,EAClE;AAAA,EAAa;AAAA,EAAW;AAAA,EAAW;AAAA,EAAc;AAAA,EAAW;AAC9D;;;ACRA,IAAM,iBAAiB,IAAI,IAAY,cAAc;AACrD,IAAM,iBAAiB,IAAI,IAAY,cAAc;AAE9C,IAAM,eAAN,MAAmB;AAAA,EACf,QAAQ,oBAAI,IAAuB;AAAA,EACnC,QAAQ,oBAAI,IAAuB;AAAA,EAE5C,QAAQ,MAA4B;AAClC,UAAM,UAAU,KAAK,MAAM,IAAI,KAAK,EAAE;AACtC,QAAI,CAAC,SAAS;AACZ,WAAK,MAAM,IAAI,KAAK,IAAI,IAAI;AAC5B,aAAO;AAAA,IACT;AACA,UAAM,SAAS;AAAA,MACb,GAAG;AAAA,MACH,GAAG;AAAA,MACH,UAAU,EAAE,GAAG,QAAQ,UAAU,GAAG,KAAK,SAAS;AAAA,IACpD;AACA,SAAK,MAAM,IAAI,KAAK,IAAI,MAAM;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,MAAiE;AACvE,QAAI,CAAC,KAAK,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,MAAM,IAAI,KAAK,EAAE,EAAG,QAAO;AACnE,UAAM,MAAM,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,EAAE,IAAI,KAAK,SAAS,EAAE;AACpE,UAAM,KAAK,KAAK,MAAM,QAAQ,mBAAmB,GAAG,CAAC;AACrD,UAAM,SAAoB,EAAE,GAAG,MAAM,GAAG;AACxC,SAAK,MAAM,IAAI,KAAK,MAAM;AAC1B,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,IAAqB;AAC3B,WAAO,KAAK,MAAM,IAAI,EAAE;AAAA,EAC1B;AAAA,EAEA,WAAqB;AACnB,UAAM,SAAmB,CAAC;AAC1B,eAAW,QAAQ,KAAK,MAAM,OAAO,GAAG;AACtC,UAAI,CAAC,eAAe,IAAI,OAAO,KAAK,IAAI,CAAC,EAAG,QAAO,KAAK,GAAG,KAAK,EAAE,uBAAuB,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,IAC9G;AACA,eAAW,QAAQ,KAAK,MAAM,OAAO,GAAG;AACtC,UAAI,CAAC,eAAe,IAAI,OAAO,KAAK,IAAI,CAAC,EAAG,QAAO,KAAK,GAAG,KAAK,EAAE,uBAAuB,OAAO,KAAK,IAAI,CAAC,EAAE;AAC5G,UAAI,CAAC,KAAK,MAAM,IAAI,KAAK,IAAI,EAAG,QAAO,KAAK,GAAG,KAAK,EAAE,oBAAoB,KAAK,IAAI,EAAE;AACrF,UAAI,CAAC,KAAK,MAAM,IAAI,KAAK,EAAE,EAAG,QAAO,KAAK,GAAG,KAAK,EAAE,oBAAoB,KAAK,EAAE,EAAE;AAAA,IACnF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,SAA0D;AAChE,UAAM,QAAQ,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC9E,UAAM,QAAQ,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC9E,WAAO,EAAE,SAAS,SAAS,SAAS,OAAO,OAAO,OAAO,WAAW,OAAO,KAAK,EAAE;AAAA,EACpF;AACF;AAEA,SAAS,UAA4B,QAAoC,KAAQ;AAC/E,SAAO,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK;AACrC;AAEO,SAAS,WAAW,OAAoB,OAAgC;AAC7E,QAAM,aAAuC,uBAAO,OAAO,IAAI;AAC/D,QAAM,aAAuC,uBAAO,OAAO,IAAI;AAC/D,aAAW,QAAQ,MAAO,WAAU,YAAY,KAAK,IAAI;AACzD,aAAW,QAAQ,MAAO,WAAU,YAAY,KAAK,IAAI;AACzD,SAAO,EAAE,YAAY,MAAM,QAAQ,YAAY,MAAM,QAAQ,YAAY,WAAW;AACtF;AAEO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YAAqB,OAA0B;AAA1B;AACnB,SAAK,UAAU,IAAI,IAAI,MAAM,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAAA,EACnE;AAAA,EAFqB;AAAA,EAFJ;AAAA,EAMjB,SAAS,OAA4B;AACnC,UAAM,SAAS,MAAM,KAAK,EAAE,YAAY;AACxC,QAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,WAAO,KAAK,MAAM,MACf,OAAO,CAAC,SAAS,eAAe,IAAI,EAAE,SAAS,MAAM,CAAC,EACtD,KAAK,CAAC,GAAG,MAAM,UAAU,GAAG,MAAM,IAAI,UAAU,GAAG,MAAM,CAAC,EAC1D,MAAM,GAAG,GAAG;AAAA,EACjB;AAAA,EAEA,OAAO,OAAoC;AACzC,UAAM,SAAS,MAAM,KAAK,EAAE,YAAY;AACxC,QAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,WAAO,KAAK,SAAS,KAAK,EAAE,IAAI,CAAC,UAAU;AAAA,MACzC;AAAA,MACA,OAAO,UAAU,MAAM,MAAM;AAAA,MAC7B,SAAS,eAAe,MAAM,MAAM;AAAA,IACtC,EAAE;AAAA,EACJ;AAAA,EAEA,QAAQ,IAA8B;AACpC,WAAO,KAAK,QAAQ,IAAI,EAAE,KAAK;AAAA,EACjC;AAAA,EAEA,YAAY,IAAyB;AACnC,WAAO,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,KAAK,OAAO,EAAE;AAAA,EACzD;AAAA,EAEA,YAAY,IAAyB;AACnC,WAAO,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE;AAAA,EAC3D;AAAA,EAEA,aAA0B;AAAE,WAAO,KAAK,OAAO,OAAO;AAAA,EAAG;AAAA,EACzD,eAA4B;AAAE,WAAO,KAAK,OAAO,SAAS;AAAA,EAAG;AAAA,EAC7D,kBAA+B;AAAE,WAAO,KAAK,OAAO,YAAY;AAAA,EAAG;AAAA,EACnE,aAA0B;AAAE,WAAO,KAAK,OAAO,OAAO;AAAA,EAAG;AAAA,EACzD,cAA2B;AAAE,WAAO,KAAK,OAAO,QAAQ;AAAA,EAAG;AAAA,EAC3D,cAA2B;AAAE,WAAO,KAAK,OAAO,OAAO;AAAA,EAAG;AAAA,EAC1D,kBAA+B;AAAE,WAAO,KAAK,OAAO,YAAY;AAAA,EAAG;AAAA,EACnE,iBAA8B;AAAE,WAAO,KAAK,OAAO,WAAW;AAAA,EAAG;AAAA,EACjE,oBAAiC;AAAE,WAAO,KAAK,OAAO,eAAe;AAAA,EAAG;AAAA,EACxE,gBAA6B;AAAE,WAAO,KAAK,OAAO,UAAU;AAAA,EAAG;AAAA,EAC/D,kBAA+B;AAAE,WAAO,KAAK,OAAO,YAAY;AAAA,EAAG;AAAA,EACnE,mBAAgC;AAAE,WAAO,KAAK,OAAO,aAAa;AAAA,EAAG;AAAA,EACrE,mBAAgC;AAAE,WAAO,KAAK,OAAO,cAAc;AAAA,EAAG;AAAA,EACtE,oBAAiC;AAAE,WAAO,KAAK,OAAO,eAAe;AAAA,EAAG;AAAA,EACxE,aAA0B;AAAE,WAAO,KAAK,OAAO,OAAO;AAAA,EAAG;AAAA,EACzD,iBAA8B;AAAE,WAAO,KAAK,OAAO,WAAW;AAAA,EAAG;AAAA,EAEjE,iBAAiB,SAAgC;AAC/C,UAAM,QAAQ,KAAK,QAAQ,IAAI,OAAO;AACtC,QAAI,CAAC,SAAS,MAAM,SAAS,QAAS,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AACpE,UAAM,YAAY,oBAAI,IAAmB,CAAC,cAAc,WAAW,YAAY,cAAc,SAAS,UAAU,WAAW,UAAU,OAAO,CAAC;AAC7I,UAAM,QAAQ,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,UAAU,IAAI,KAAK,IAAI,MAAM,KAAK,SAAS,WAAW,KAAK,OAAO,QAAQ;AAC1H,UAAM,MAAM,oBAAI,IAAI,CAAC,SAAS,GAAG,MAAM,QAAQ,CAAC,SAAS,CAAC,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC;AAC/E,WAAO,EAAE,OAAO,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,OAAO,GAAkB,MAAM;AAAA,EACnG;AAAA,EAEA,aAAa,QAAgB,QAAQ,GAAkB;AACrD,QAAI,CAAC,KAAK,QAAQ,IAAI,MAAM,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAC7D,UAAM,UAAU,oBAAI,IAAI,CAAC,MAAM,CAAC;AAChC,UAAM,UAAU,oBAAI,IAAY;AAChC,QAAI,WAAW,CAAC,MAAM;AACtB,aAAS,QAAQ,GAAG,QAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,QAAQ,SAAS,GAAG;AAC7E,YAAM,OAAiB,CAAC;AACxB,iBAAW,MAAM,UAAU;AACzB,mBAAW,QAAQ,CAAC,GAAG,KAAK,YAAY,EAAE,GAAG,GAAG,KAAK,YAAY,EAAE,CAAC,GAAG;AACrE,kBAAQ,IAAI,KAAK,EAAE;AACnB,gBAAM,WAAW,KAAK,SAAS,KAAK,KAAK,KAAK,KAAK;AACnD,cAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG;AAAE,oBAAQ,IAAI,QAAQ;AAAG,iBAAK,KAAK,QAAQ;AAAA,UAAG;AAAA,QAC5E;AAAA,MACF;AACA,iBAAW;AAAA,IACb;AACA,WAAO;AAAA,MACL,OAAO,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,OAAO;AAAA,MACpE,OAAO,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,QAAQ,IAAI,KAAK,EAAE,CAAC;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,kBAAkB,SAAgC;AAChD,WAAO,KAAK,KAAK,SAAS,YAAY,IAAI,oBAAI,IAAI;AAAA,MAChD;AAAA,MAAW;AAAA,MAAS;AAAA,MAAS;AAAA,MAAU;AAAA,MAAQ;AAAA,MAAe;AAAA,MAAa;AAAA,MAC3E;AAAA,MAAgB;AAAA,MAAe;AAAA,MAAY;AAAA,IAC7C,CAAC,CAAC;AAAA,EACJ;AAAA,EAEA,cAAc,QAA+B;AAC3C,UAAM,OAAO,KAAK,QAAQ,IAAI,MAAM;AACpC,QAAI,CAAC,QAAQ,CAAC,CAAC,iBAAiB,OAAO,EAAE,SAAS,KAAK,IAAI,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAC5F,UAAM,OAAO,KAAK,KAAK,QAAQ,YAAY,IAAI,oBAAI,IAAI;AAAA,MACrD;AAAA,MAAe;AAAA,MAAS;AAAA,MAAS;AAAA,MAAU;AAAA,MAAQ;AAAA,MAAe;AAAA,MAAgB;AAAA,MAAY;AAAA,IAChG,CAAC,CAAC;AACF,UAAM,UAAU,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC;AACzD,UAAM,UAAU,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC;AACzD,eAAW,QAAQ,KAAK,YAAY,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,gBAAgB,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC,GAAG;AAC9G,cAAQ,IAAI,KAAK,IAAI;AACrB,cAAQ,IAAI,KAAK,EAAE;AAAA,IACrB;AACA,WAAO;AAAA,MACL,OAAO,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,OAAO;AAAA,MACpE,OAAO,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,QAAQ,IAAI,KAAK,EAAE,CAAC;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,iBAAiB,QAAgB,QAAQ,GAAkB;AACzD,WAAO,KAAK,KAAK,QAAQ,YAAY,KAAK;AAAA,EAC5C;AAAA,EAEA,eAAe,QAAgB,QAAQ,GAAkB;AACvD,WAAO,KAAK,KAAK,QAAQ,YAAY,KAAK;AAAA,EAC5C;AAAA,EAEQ,OAAO,MAAsC;AACnD,WAAO,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI;AAAA,EAC7D;AAAA,EAEQ,KACN,SACA,WACA,OACA,cACe;AACf,QAAI,CAAC,KAAK,QAAQ,IAAI,OAAO,EAAG,QAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAC9D,UAAM,UAAU,oBAAI,IAAI,CAAC,OAAO,CAAC;AACjC,UAAM,UAAU,oBAAI,IAAY;AAChC,QAAI,WAAW,CAAC,OAAO;AACvB,aAAS,QAAQ,GAAG,QAAQ,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,QAAQ,SAAS,GAAG;AAC7E,YAAM,OAAiB,CAAC;AACxB,iBAAW,MAAM,UAAU;AACzB,cAAM,QAAQ,cAAc,aAAa,KAAK,YAAY,EAAE,IAAI,KAAK,YAAY,EAAE;AACnF,mBAAW,QAAQ,OAAO;AACxB,cAAI,gBAAgB,CAAC,aAAa,IAAI,KAAK,IAAI,EAAG;AAClD,kBAAQ,IAAI,KAAK,EAAE;AACnB,gBAAM,WAAW,cAAc,aAAa,KAAK,KAAK,KAAK;AAC3D,cAAI,CAAC,QAAQ,IAAI,QAAQ,GAAG;AAC1B,oBAAQ,IAAI,QAAQ;AACpB,iBAAK,KAAK,QAAQ;AAAA,UACpB;AAAA,QACF;AAAA,MACF;AACA,iBAAW;AAAA,IACb;AACA,WAAO;AAAA,MACL,OAAO,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,OAAO;AAAA,MACpE,OAAO,KAAK,MAAM,MAAM,OAAO,CAAC,SAAS,QAAQ,IAAI,KAAK,EAAE,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;AAEA,SAAS,eAAe,MAAyB;AAC/C,SAAO,CAAC,KAAK,IAAI,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,KAAK,MAAM,KAAK,UAAU,KAAK,YAAY,CAAC,CAAC,CAAC,EAC9F,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAY;AACjB;AAEA,SAAS,UAAU,MAAiB,QAAwB;AAC1D,QAAM,QAAQ,KAAK,MAAM,YAAY;AACrC,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,MAAM,WAAW,MAAM,EAAG,QAAO;AACrC,MAAI,KAAK,GAAG,YAAY,EAAE,SAAS,MAAM,EAAG,QAAO;AACnD,SAAO;AACT;AAEA,SAAS,eAAe,MAAiB,QAA0B;AACjE,QAAM,SAAS;AAAA,IACb,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,MAAM,KAAK,QAAQ;AAAA,IACnB,MAAM,KAAK;AAAA,IACX,MAAM,KAAK,QAAQ;AAAA,IACnB,UAAU,KAAK,UAAU,KAAK,YAAY,CAAC,CAAC;AAAA,EAC9C;AACA,SAAO,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,MAAM,YAAY,EAAE,SAAS,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,MAAM,GAAG;AAC9G;","names":[]} |
| import { | ||
| GraphQuery | ||
| } from "./chunk-M2XP6F7I.js"; | ||
| // src/mcp/server.ts | ||
| import { readFile } from "fs/promises"; | ||
| import { resolve } from "path"; | ||
| import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | ||
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | ||
| import * as z from "zod/v4"; | ||
| async function startMcpServer(projectPath, outputPath = ".atlas") { | ||
| const graphPath = resolve(projectPath, outputPath, "graph.json"); | ||
| const graph = JSON.parse(await readFile(graphPath, "utf8")); | ||
| const query = new GraphQuery(graph); | ||
| const server = new McpServer({ name: "atlas", version: graph.version }); | ||
| const result = (data) => ({ | ||
| content: [{ type: "text", text: JSON.stringify(data, null, 2) }], | ||
| structuredContent: data | ||
| }); | ||
| server.registerTool("atlas_find_node", { | ||
| description: "Find architecture nodes by name, label, type, file, route, or metadata.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => result({ results: query.findNode(value) })); | ||
| server.registerTool("atlas_get_node", { | ||
| description: "Get one node and its incoming, outgoing, and method relationships.", | ||
| inputSchema: { id: z.string().min(1) } | ||
| }, async ({ id }) => { | ||
| const node = query.getNode(id); | ||
| const outgoing = query.getOutgoing(id); | ||
| const methods = outgoing.filter((edge) => edge.type === "has_method").map((edge) => query.getNode(edge.to)).filter(Boolean); | ||
| return result({ node, incoming: query.getIncoming(id), outgoing, methods }); | ||
| }); | ||
| server.registerTool("atlas_get_dependencies", { | ||
| description: "Traverse outgoing architecture dependencies from a node.", | ||
| inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) } | ||
| }, async ({ id, depth }) => result({ graph: query.findDependencies(id, depth) })); | ||
| server.registerTool("atlas_get_dependents", { | ||
| description: "Traverse incoming architecture dependents of a node.", | ||
| inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) } | ||
| }, async ({ id, depth }) => result({ graph: query.findDependents(id, depth) })); | ||
| server.registerTool("atlas_find_routes", { description: "List all detected HTTP routes." }, async () => result({ routes: query.findRoutes() })); | ||
| server.registerTool("atlas_find_flow", { | ||
| description: "Find a route and return its route-to-controller-to-service-to-data flow.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => { | ||
| const route = query.findNode(value).find((node) => node.type === "route"); | ||
| return result({ route: route ?? null, flow: route ? query.findFlowFromRoute(route.id) : { nodes: [], edges: [] } }); | ||
| }); | ||
| server.registerTool("atlas_find_tables", { description: "List detected database tables." }, async () => result({ tables: query.findTables() })); | ||
| server.registerTool("atlas_find_data_model", { description: "List schemas, tables, indexes, constraints, migrations, and ClickHouse structures." }, async () => result({ | ||
| schemas: query.findSchemas(), | ||
| tables: query.findTables(), | ||
| indexes: query.findIndexes(), | ||
| constraints: query.findConstraints(), | ||
| migrations: query.findMigrations() | ||
| })); | ||
| server.registerTool("atlas_get_table_profile", { | ||
| description: "Return a table with its columns, indexes, constraints, relations, migrations, readers, and writers.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => { | ||
| const table = query.findNode(value).find((node) => node.type === "table"); | ||
| return result({ table: table ?? null, profile: table ? query.findTableProfile(table.id) : { nodes: [], edges: [] } }); | ||
| }); | ||
| server.registerTool("atlas_find_migrations", { description: "List migrations and the structures they create, alter, or drop." }, async () => result({ migrations: query.findMigrations() })); | ||
| server.registerTool("atlas_find_external_apis", { description: "List detected external API hosts." }, async () => result({ externalApis: query.findExternalApis() })); | ||
| server.registerTool("atlas_find_async_flows", { | ||
| description: "List detected Kafka or RabbitMQ topics, Bull/BullMQ queues, and background processors." | ||
| }, async () => result({ topics: query.findMessageTopics(), queues: query.findQueues(), processors: query.findProcessors() })); | ||
| server.registerTool("atlas_find_async_flow", { | ||
| description: "Find a message topic or queue and return publishers, consumers, processors, and downstream calls.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => { | ||
| const root = query.findNode(value).find((node) => ["message_topic", "queue"].includes(node.type)); | ||
| return result({ root: root ?? null, flow: root ? query.findAsyncFlow(root.id) : { nodes: [], edges: [] } }); | ||
| }); | ||
| server.registerTool("atlas_find_schedules", { description: "List cron, interval, timeout, repeatable queue, and Kubernetes scheduled jobs." }, async () => result({ schedules: query.findScheduledJobs() })); | ||
| server.registerTool("atlas_find_delivery", { description: "List CI/CD workflows and runtime deployments." }, async () => result({ workflows: query.findWorkflows(), deployments: query.findDeployments() })); | ||
| server.registerTool("atlas_find_environments", { description: "List detected development, staging, production, and custom runtime environments." }, async () => result({ environments: query.findEnvironments() })); | ||
| server.registerTool("atlas_search", { | ||
| description: "Search the complete architecture graph.", | ||
| inputSchema: { query: z.string().min(1) } | ||
| }, async ({ query: value }) => result({ results: query.search(value) })); | ||
| server.registerTool("atlas_project_summary", { description: "Return project identity and graph statistics." }, async () => result({ project: graph.project, stats: graph.stats })); | ||
| await server.connect(new StdioServerTransport()); | ||
| console.error(`Atlas MCP server ready: ${graphPath}`); | ||
| } | ||
| export { | ||
| startMcpServer | ||
| }; | ||
| //# sourceMappingURL=server-MDSHY22G.js.map |
| {"version":3,"sources":["../src/mcp/server.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport * as z from \"zod/v4\";\nimport { GraphQuery } from \"../core/graph.js\";\nimport type { ArchitectureGraph } from \"../core/types.js\";\n\nexport async function startMcpServer(projectPath: string, outputPath = \".atlas\"): Promise<void> {\n const graphPath = resolve(projectPath, outputPath, \"graph.json\");\n const graph = JSON.parse(await readFile(graphPath, \"utf8\")) as ArchitectureGraph;\n const query = new GraphQuery(graph);\n const server = new McpServer({ name: \"atlas\", version: graph.version });\n const result = (data: Record<string, unknown>) => ({\n content: [{ type: \"text\" as const, text: JSON.stringify(data, null, 2) }],\n structuredContent: data,\n });\n\n server.registerTool(\"atlas_find_node\", {\n description: \"Find architecture nodes by name, label, type, file, route, or metadata.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => result({ results: query.findNode(value) }));\n\n server.registerTool(\"atlas_get_node\", {\n description: \"Get one node and its incoming, outgoing, and method relationships.\",\n inputSchema: { id: z.string().min(1) },\n }, async ({ id }) => {\n const node = query.getNode(id);\n const outgoing = query.getOutgoing(id);\n const methods = outgoing.filter((edge) => edge.type === \"has_method\").map((edge) => query.getNode(edge.to)).filter(Boolean);\n return result({ node, incoming: query.getIncoming(id), outgoing, methods });\n });\n\n server.registerTool(\"atlas_get_dependencies\", {\n description: \"Traverse outgoing architecture dependencies from a node.\",\n inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) },\n }, async ({ id, depth }) => result({ graph: query.findDependencies(id, depth) }));\n\n server.registerTool(\"atlas_get_dependents\", {\n description: \"Traverse incoming architecture dependents of a node.\",\n inputSchema: { id: z.string().min(1), depth: z.number().int().min(1).max(10).default(2) },\n }, async ({ id, depth }) => result({ graph: query.findDependents(id, depth) }));\n\n server.registerTool(\"atlas_find_routes\", { description: \"List all detected HTTP routes.\" }, async () => result({ routes: query.findRoutes() }));\n\n server.registerTool(\"atlas_find_flow\", {\n description: \"Find a route and return its route-to-controller-to-service-to-data flow.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => {\n const route = query.findNode(value).find((node) => node.type === \"route\");\n return result({ route: route ?? null, flow: route ? query.findFlowFromRoute(route.id) : { nodes: [], edges: [] } });\n });\n\n server.registerTool(\"atlas_find_tables\", { description: \"List detected database tables.\" }, async () => result({ tables: query.findTables() }));\n server.registerTool(\"atlas_find_data_model\", { description: \"List schemas, tables, indexes, constraints, migrations, and ClickHouse structures.\" }, async () => result({\n schemas: query.findSchemas(), tables: query.findTables(), indexes: query.findIndexes(), constraints: query.findConstraints(), migrations: query.findMigrations(),\n }));\n server.registerTool(\"atlas_get_table_profile\", {\n description: \"Return a table with its columns, indexes, constraints, relations, migrations, readers, and writers.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => {\n const table = query.findNode(value).find((node) => node.type === \"table\");\n return result({ table: table ?? null, profile: table ? query.findTableProfile(table.id) : { nodes: [], edges: [] } });\n });\n server.registerTool(\"atlas_find_migrations\", { description: \"List migrations and the structures they create, alter, or drop.\" }, async () => result({ migrations: query.findMigrations() }));\n server.registerTool(\"atlas_find_external_apis\", { description: \"List detected external API hosts.\" }, async () => result({ externalApis: query.findExternalApis() }));\n\n server.registerTool(\"atlas_find_async_flows\", {\n description: \"List detected Kafka or RabbitMQ topics, Bull/BullMQ queues, and background processors.\",\n }, async () => result({ topics: query.findMessageTopics(), queues: query.findQueues(), processors: query.findProcessors() }));\n\n server.registerTool(\"atlas_find_async_flow\", {\n description: \"Find a message topic or queue and return publishers, consumers, processors, and downstream calls.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => {\n const root = query.findNode(value).find((node) => [\"message_topic\", \"queue\"].includes(node.type));\n return result({ root: root ?? null, flow: root ? query.findAsyncFlow(root.id) : { nodes: [], edges: [] } });\n });\n\n server.registerTool(\"atlas_find_schedules\", { description: \"List cron, interval, timeout, repeatable queue, and Kubernetes scheduled jobs.\" }, async () => result({ schedules: query.findScheduledJobs() }));\n server.registerTool(\"atlas_find_delivery\", { description: \"List CI/CD workflows and runtime deployments.\" }, async () => result({ workflows: query.findWorkflows(), deployments: query.findDeployments() }));\n server.registerTool(\"atlas_find_environments\", { description: \"List detected development, staging, production, and custom runtime environments.\" }, async () => result({ environments: query.findEnvironments() }));\n\n server.registerTool(\"atlas_search\", {\n description: \"Search the complete architecture graph.\",\n inputSchema: { query: z.string().min(1) },\n }, async ({ query: value }) => result({ results: query.search(value) }));\n\n server.registerTool(\"atlas_project_summary\", { description: \"Return project identity and graph statistics.\" }, async () => result({ project: graph.project, stats: graph.stats }));\n\n await server.connect(new StdioServerTransport());\n console.error(`Atlas MCP server ready: ${graphPath}`);\n}\n"],"mappings":"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","names":[]} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1362553
7.96%27
8%7779
10.76%375
25.84%