@neat.is/core 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/compat.json +120 -0
  2. package/dist/chunk-6JT6L2OV.js +164 -0
  3. package/dist/chunk-6JT6L2OV.js.map +1 -0
  4. package/dist/chunk-6SFEITLJ.js +3371 -0
  5. package/dist/chunk-6SFEITLJ.js.map +1 -0
  6. package/dist/chunk-I5IMCXRO.js +325 -0
  7. package/dist/chunk-I5IMCXRO.js.map +1 -0
  8. package/dist/chunk-T2U4U256.js +462 -0
  9. package/dist/chunk-T2U4U256.js.map +1 -0
  10. package/dist/chunk-WX55TLUT.js +184 -0
  11. package/dist/chunk-WX55TLUT.js.map +1 -0
  12. package/dist/chunk-XOOCA5T7.js +290 -0
  13. package/dist/chunk-XOOCA5T7.js.map +1 -0
  14. package/dist/cli.cjs +5754 -0
  15. package/dist/cli.cjs.map +1 -0
  16. package/dist/cli.d.cts +36 -0
  17. package/dist/cli.d.ts +36 -0
  18. package/dist/cli.js +1175 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/index.cjs +4552 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +408 -0
  23. package/dist/index.d.ts +408 -0
  24. package/dist/index.js +93 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/neatd.cjs +3070 -0
  27. package/dist/neatd.cjs.map +1 -0
  28. package/dist/neatd.d.cts +1 -0
  29. package/dist/neatd.d.ts +1 -0
  30. package/dist/neatd.js +114 -0
  31. package/dist/neatd.js.map +1 -0
  32. package/dist/otel-grpc-B4XBSI4W.js +9 -0
  33. package/dist/otel-grpc-B4XBSI4W.js.map +1 -0
  34. package/dist/server.cjs +4499 -0
  35. package/dist/server.cjs.map +1 -0
  36. package/dist/server.d.cts +2 -0
  37. package/dist/server.d.ts +2 -0
  38. package/dist/server.js +97 -0
  39. package/dist/server.js.map +1 -0
  40. package/package.json +77 -0
  41. package/proto/opentelemetry/proto/collector/trace/v1/trace_service.proto +31 -0
  42. package/proto/opentelemetry/proto/common/v1/common.proto +46 -0
  43. package/proto/opentelemetry/proto/resource/v1/resource.proto +19 -0
  44. package/proto/opentelemetry/proto/trace/v1/trace.proto +93 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/graph.ts","../src/compat.ts","../compat.json","../src/traverse.ts","../src/ingest.ts","../src/policy.ts","../src/extract/services.ts","../src/extract/shared.ts","../src/extract/python.ts","../src/extract/aliases.ts","../src/extract/databases/index.ts","../src/extract/databases/db-config-yaml.ts","../src/extract/databases/dotenv.ts","../src/extract/databases/shared.ts","../src/extract/databases/prisma.ts","../src/extract/databases/drizzle.ts","../src/extract/databases/knex.ts","../src/extract/databases/ormconfig.ts","../src/extract/databases/typeorm.ts","../src/extract/databases/sequelize.ts","../src/extract/databases/docker-compose.ts","../src/extract/configs.ts","../src/extract/calls/index.ts","../src/extract/calls/http.ts","../src/extract/calls/shared.ts","../src/extract/calls/kafka.ts","../src/extract/calls/redis.ts","../src/extract/calls/aws.ts","../src/extract/calls/grpc.ts","../src/extract/infra/docker-compose.ts","../src/extract/infra/shared.ts","../src/extract/infra/dockerfile.ts","../src/extract/infra/terraform.ts","../src/extract/infra/k8s.ts","../src/extract/infra/index.ts","../src/extract/index.ts","../src/persist.ts","../src/projects.ts"],"sourcesContent":["import GraphDefault from 'graphology'\nimport type { MultiDirectedGraph as MDGType } from 'graphology'\nimport type { GraphEdge, GraphNode } from '@neat.is/types'\n\n// graphology ships as a CJS bundle that does `module.exports = Graph` with\n// the other constructors attached as properties (`Graph.MultiDirectedGraph =\n// ...`). cjs-module-lexer can't see through that attachment, so a named\n// import like `import { MultiDirectedGraph } from 'graphology'` fails under\n// strict Node ESM (Node 22+ via tsx in particular). Pull the constructor off\n// the default export instead — same shape under tsx and tsup-bundled output.\ntype MultiDirectedGraphCtor = typeof MDGType\nconst MultiDirectedGraph: MultiDirectedGraphCtor = (\n GraphDefault as unknown as { MultiDirectedGraph: MultiDirectedGraphCtor }\n).MultiDirectedGraph\n\n// Multi because two nodes can have edges of different types simultaneously\n// (e.g. CALLS and DEPENDS_ON between the same pair of services).\nexport type NeatGraph = MDGType<GraphNode, GraphEdge>\n\nexport const DEFAULT_PROJECT = 'default'\n\n// One graph per project. The map is the source of truth; getGraph() with no\n// arg or with 'default' hits the legacy single-project path so existing\n// callers keep working byte-for-byte (ADR-026).\nconst graphs = new Map<string, NeatGraph>()\n\nfunction makeGraph(): NeatGraph {\n return new MultiDirectedGraph<GraphNode, GraphEdge>({ allowSelfLoops: false })\n}\n\nexport function getGraph(project: string = DEFAULT_PROJECT): NeatGraph {\n let g = graphs.get(project)\n if (!g) {\n g = makeGraph()\n graphs.set(project, g)\n }\n return g\n}\n\nexport function hasProject(project: string): boolean {\n return graphs.has(project)\n}\n\nexport function listProjects(): string[] {\n return [...graphs.keys()].sort()\n}\n\n// Reset a single project, or all of them when the arg is omitted. Tests use\n// the no-arg form between cases; runtime never calls it.\nexport function resetGraph(project?: string): void {\n if (project === undefined) {\n graphs.clear()\n return\n }\n graphs.delete(project)\n}\n","import { promises as fs } from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport semver from 'semver'\nimport compatData from '../compat.json' with { type: 'json' }\n\nexport interface CompatibilityResult {\n compatible: boolean\n reason?: string\n minDriverVersion?: string\n}\n\nexport interface CompatPair {\n kind?: 'driver-engine'\n driver: string\n engine: string\n minDriverVersion: string\n // The driver constraint only kicks in once the engine is at this major or higher.\n // Older engines (e.g. PostgreSQL 13) accept the older driver fine.\n minEngineVersion?: string\n reason: string\n}\n\nexport interface NodeEngineConstraint {\n kind?: 'node-engine'\n package: string\n packageMinVersion?: string\n minNodeVersion: string\n reason: string\n}\n\nexport interface PackageConflict {\n kind?: 'package-conflict'\n package: string\n packageMinVersion?: string\n requires: { name: string; minVersion: string }\n reason: string\n}\n\nexport interface DeprecatedApi {\n kind?: 'deprecated-api'\n package: string\n packageMaxVersion?: string\n reason: string\n}\n\nexport interface CompatMatrix {\n pairs: CompatPair[]\n nodeEngineConstraints?: NodeEngineConstraint[]\n packageConflicts?: PackageConflict[]\n deprecatedApis?: DeprecatedApi[]\n}\n\nconst bundledMatrix = compatData as CompatMatrix\nlet mergedMatrix: CompatMatrix | null = null\nlet remoteLoadAttempted = false\n\nconst REMOTE_CACHE_DIR = path.join(os.homedir(), '.neat')\nconst REMOTE_CACHE_PATH = path.join(REMOTE_CACHE_DIR, 'compat-cache.json')\nconst REMOTE_TTL_MS = 24 * 60 * 60 * 1000\n\ninterface RemoteCacheFile {\n fetchedAt: string\n url: string\n matrix: CompatMatrix\n}\n\n// Engines like Postgres/MySQL only carry a major in the version field, so semver\n// won't always parse them cleanly. Compare as integers when both sides look like\n// majors; otherwise fall back to semver.coerce.\nfunction engineMeetsThreshold(engineVersion: string, threshold: string): boolean {\n const e = parseInt(engineVersion, 10)\n const t = parseInt(threshold, 10)\n if (Number.isFinite(e) && Number.isFinite(t)) return e >= t\n\n const ec = semver.coerce(engineVersion)\n const tc = semver.coerce(threshold)\n if (ec && tc) return semver.gte(ec, tc)\n\n return false\n}\n\nexport function checkCompatibility(\n driver: string,\n driverVersion: string,\n engine: string,\n engineVersion: string,\n): CompatibilityResult {\n const matrix = currentMatrix()\n const pair = matrix.pairs.find((p) => p.driver === driver && p.engine === engine)\n if (!pair) return { compatible: true }\n\n if (pair.minEngineVersion && !engineMeetsThreshold(engineVersion, pair.minEngineVersion)) {\n return { compatible: true }\n }\n\n const driverCoerced = semver.coerce(driverVersion)\n if (!driverCoerced) return { compatible: true }\n\n if (semver.lt(driverCoerced, pair.minDriverVersion)) {\n return {\n compatible: false,\n reason: pair.reason,\n minDriverVersion: pair.minDriverVersion,\n }\n }\n\n return { compatible: true }\n}\n\nexport interface NodeEngineCheck {\n compatible: boolean\n reason?: string\n requiredNodeVersion?: string\n}\n\n// True when `serviceNodeRange` (a service's `engines.node`) is guaranteed to\n// admit `requiredNodeVersion`. We use a permissive semver compare via `coerce`\n// — exact ranges like \">=20\" parse fine, exotic ones like \"^20 || ^22\" pass as\n// long as semver can resolve them. If the range can't be parsed at all, we\n// don't claim a conflict — under-flag rather than over-flag.\nfunction rangeAdmitsVersion(serviceNodeRange: string, requiredNodeVersion: string): boolean {\n try {\n const required = semver.coerce(requiredNodeVersion)\n if (!required) return true\n // Is every version that satisfies the service's range >= required? If yes,\n // the service guarantees the requirement; if not, there's at least one\n // admissible Node version that won't satisfy the dep — that's the\n // conflict.\n return semver.subset(serviceNodeRange, `>=${required.version}`, {\n includePrerelease: false,\n })\n } catch {\n return true\n }\n}\n\nexport function checkNodeEngineConstraint(\n constraint: NodeEngineConstraint,\n declaredPackageVersion: string | undefined,\n serviceNodeRange: string | undefined,\n): NodeEngineCheck {\n if (constraint.packageMinVersion && declaredPackageVersion) {\n const v = semver.coerce(declaredPackageVersion)\n if (v && semver.lt(v, constraint.packageMinVersion)) {\n return { compatible: true }\n }\n }\n if (!serviceNodeRange) {\n return { compatible: true }\n }\n if (rangeAdmitsVersion(serviceNodeRange, constraint.minNodeVersion)) {\n return { compatible: true }\n }\n return {\n compatible: false,\n reason: constraint.reason,\n requiredNodeVersion: constraint.minNodeVersion,\n }\n}\n\nexport interface PackageConflictCheck {\n compatible: boolean\n reason?: string\n requires?: { name: string; minVersion: string }\n foundVersion?: string\n}\n\nexport function checkPackageConflict(\n conflict: PackageConflict,\n declaredPackageVersion: string | undefined,\n declaredRequiredVersion: string | undefined,\n): PackageConflictCheck {\n if (!declaredPackageVersion) return { compatible: true }\n if (conflict.packageMinVersion) {\n const v = semver.coerce(declaredPackageVersion)\n if (v && semver.lt(v, conflict.packageMinVersion)) {\n return { compatible: true }\n }\n }\n if (!declaredRequiredVersion) {\n return {\n compatible: false,\n reason: conflict.reason,\n requires: conflict.requires,\n }\n }\n const requiredCoerced = semver.coerce(declaredRequiredVersion)\n if (!requiredCoerced) return { compatible: true }\n if (semver.lt(requiredCoerced, conflict.requires.minVersion)) {\n return {\n compatible: false,\n reason: conflict.reason,\n requires: conflict.requires,\n foundVersion: declaredRequiredVersion,\n }\n }\n return { compatible: true }\n}\n\nexport function checkDeprecatedApi(\n rule: DeprecatedApi,\n declaredVersion: string | undefined,\n): { compatible: boolean; reason?: string } {\n if (declaredVersion === undefined) return { compatible: true }\n if (rule.packageMaxVersion) {\n const v = semver.coerce(declaredVersion)\n const max = semver.coerce(rule.packageMaxVersion)\n if (v && max && semver.gt(v, max)) return { compatible: true }\n }\n return { compatible: false, reason: rule.reason }\n}\n\nfunction currentMatrix(): CompatMatrix {\n return mergedMatrix ?? bundledMatrix\n}\n\nfunction mergeMatrices(a: CompatMatrix, b: CompatMatrix): CompatMatrix {\n return {\n pairs: [...a.pairs, ...(b.pairs ?? [])],\n nodeEngineConstraints: [\n ...(a.nodeEngineConstraints ?? []),\n ...(b.nodeEngineConstraints ?? []),\n ],\n packageConflicts: [...(a.packageConflicts ?? []), ...(b.packageConflicts ?? [])],\n deprecatedApis: [...(a.deprecatedApis ?? []), ...(b.deprecatedApis ?? [])],\n }\n}\n\nasync function readRemoteCache(url: string): Promise<CompatMatrix | null> {\n try {\n const raw = await fs.readFile(REMOTE_CACHE_PATH, 'utf8')\n const parsed = JSON.parse(raw) as RemoteCacheFile\n if (parsed.url !== url) return null\n const age = Date.now() - new Date(parsed.fetchedAt).getTime()\n if (age > REMOTE_TTL_MS) return null\n return parsed.matrix\n } catch {\n return null\n }\n}\n\nasync function writeRemoteCache(url: string, matrix: CompatMatrix): Promise<void> {\n const file: RemoteCacheFile = {\n fetchedAt: new Date().toISOString(),\n url,\n matrix,\n }\n try {\n await fs.mkdir(REMOTE_CACHE_DIR, { recursive: true })\n await fs.writeFile(REMOTE_CACHE_PATH, JSON.stringify(file), 'utf8')\n } catch (err) {\n console.warn(`[neat] failed to cache compat matrix: ${(err as Error).message}`)\n }\n}\n\n// Loads the bundled matrix and, if `NEAT_COMPAT_URL` is set, merges in a\n// remote extension. Falls back to a fresh fetch when the on-disk cache is\n// stale (24h TTL) or missing. Returns the merged matrix; subsequent calls are\n// memoised.\n//\n// Async because the fetch happens lazily on first use. Extract phase 2 awaits\n// this before iterating pairs; everything else goes through the sync\n// `currentMatrix()` view, which is fine because by the time CLI / traversal\n// runs, extraction has already loaded.\nexport async function ensureCompatLoaded(): Promise<CompatMatrix> {\n if (mergedMatrix) return mergedMatrix\n if (remoteLoadAttempted) {\n mergedMatrix = bundledMatrix\n return mergedMatrix\n }\n remoteLoadAttempted = true\n\n const url = process.env.NEAT_COMPAT_URL\n if (!url) {\n mergedMatrix = bundledMatrix\n return mergedMatrix\n }\n\n const cached = await readRemoteCache(url)\n if (cached) {\n mergedMatrix = mergeMatrices(bundledMatrix, cached)\n return mergedMatrix\n }\n\n try {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)\n const remote = (await res.json()) as CompatMatrix\n await writeRemoteCache(url, remote)\n mergedMatrix = mergeMatrices(bundledMatrix, remote)\n return mergedMatrix\n } catch (err) {\n console.warn(\n `[neat] NEAT_COMPAT_URL fetch failed (${(err as Error).message}); using bundled matrix only`,\n )\n mergedMatrix = bundledMatrix\n return mergedMatrix\n }\n}\n\n// Reset the merged-matrix memo. Intended for tests so each test starts with a\n// freshly loaded matrix.\nexport function resetCompatMatrix(): void {\n mergedMatrix = null\n remoteLoadAttempted = false\n}\n\nexport function compatPairs(): readonly CompatPair[] {\n return currentMatrix().pairs\n}\n\nexport function nodeEngineConstraints(): readonly NodeEngineConstraint[] {\n return currentMatrix().nodeEngineConstraints ?? []\n}\n\nexport function packageConflicts(): readonly PackageConflict[] {\n return currentMatrix().packageConflicts ?? []\n}\n\nexport function deprecatedApis(): readonly DeprecatedApi[] {\n return currentMatrix().deprecatedApis ?? []\n}\n","{\n \"pairs\": [\n {\n \"kind\": \"driver-engine\",\n \"driver\": \"pg\",\n \"engine\": \"postgresql\",\n \"minDriverVersion\": \"8.0.0\",\n \"minEngineVersion\": \"14\",\n \"reason\": \"PostgreSQL 14+ requires scram-sha-256 auth by default; pg < 8.0.0 only speaks md5.\"\n },\n {\n \"kind\": \"driver-engine\",\n \"driver\": \"mysql2\",\n \"engine\": \"mysql\",\n \"minDriverVersion\": \"3.0.0\",\n \"minEngineVersion\": \"8\",\n \"reason\": \"MySQL 8 defaults to caching_sha2_password; mysql2 < 3.0.0 doesn't negotiate it.\"\n },\n {\n \"kind\": \"driver-engine\",\n \"driver\": \"mongoose\",\n \"engine\": \"mongodb\",\n \"minDriverVersion\": \"7.0.0\",\n \"minEngineVersion\": \"7\",\n \"reason\": \"MongoDB 7 drops legacy wire-protocol opcodes that mongoose < 7.0.0 still emits.\"\n },\n {\n \"kind\": \"driver-engine\",\n \"driver\": \"psycopg2\",\n \"engine\": \"postgresql\",\n \"minDriverVersion\": \"2.9.0\",\n \"minEngineVersion\": \"14\",\n \"reason\": \"PostgreSQL 14+ requires scram-sha-256 auth by default; psycopg2 < 2.9.0 only speaks md5.\"\n },\n {\n \"kind\": \"driver-engine\",\n \"driver\": \"pymongo\",\n \"engine\": \"mongodb\",\n \"minDriverVersion\": \"4.0.0\",\n \"minEngineVersion\": \"7\",\n \"reason\": \"MongoDB 7 drops legacy wire-protocol opcodes that pymongo < 4.0.0 still emits.\"\n },\n {\n \"kind\": \"driver-engine\",\n \"driver\": \"mysql-connector-python\",\n \"engine\": \"mysql\",\n \"minDriverVersion\": \"8.0.0\",\n \"minEngineVersion\": \"8\",\n \"reason\": \"MySQL 8 defaults to caching_sha2_password; mysql-connector-python < 8.0.0 doesn't negotiate it.\"\n }\n ],\n \"nodeEngineConstraints\": [\n {\n \"kind\": \"node-engine\",\n \"package\": \"vitest\",\n \"packageMinVersion\": \"2.0.0\",\n \"minNodeVersion\": \"18.0.0\",\n \"reason\": \"vitest >= 2.0 drops Node 16 support; requires Node 18+.\"\n },\n {\n \"kind\": \"node-engine\",\n \"package\": \"next\",\n \"packageMinVersion\": \"14.0.0\",\n \"minNodeVersion\": \"18.17.0\",\n \"reason\": \"Next 14+ requires Node 18.17+ (uses APIs introduced in that minor).\"\n },\n {\n \"kind\": \"node-engine\",\n \"package\": \"@modelcontextprotocol/sdk\",\n \"packageMinVersion\": \"1.0.0\",\n \"minNodeVersion\": \"18.0.0\",\n \"reason\": \"@modelcontextprotocol/sdk >= 1 requires Node 18+ (web-streams polyfill removed).\"\n }\n ],\n \"packageConflicts\": [\n {\n \"kind\": \"package-conflict\",\n \"package\": \"@tanstack/react-query\",\n \"packageMinVersion\": \"5.0.0\",\n \"requires\": {\n \"name\": \"react\",\n \"minVersion\": \"18.0.0\"\n },\n \"reason\": \"@tanstack/react-query 5+ uses useSyncExternalStore — only available in React 18+.\"\n },\n {\n \"kind\": \"package-conflict\",\n \"package\": \"react-router-dom\",\n \"packageMinVersion\": \"7.0.0\",\n \"requires\": {\n \"name\": \"react\",\n \"minVersion\": \"18.0.0\"\n },\n \"reason\": \"react-router-dom 7+ requires React 18+.\"\n },\n {\n \"kind\": \"package-conflict\",\n \"package\": \"next\",\n \"packageMinVersion\": \"14.0.0\",\n \"requires\": {\n \"name\": \"react\",\n \"minVersion\": \"18.2.0\"\n },\n \"reason\": \"Next.js 14+ requires React 18.2+.\"\n }\n ],\n \"deprecatedApis\": [\n {\n \"kind\": \"deprecated-api\",\n \"package\": \"request\",\n \"packageMaxVersion\": \"2.88.2\",\n \"reason\": \"request is deprecated; use undici, node-fetch, or axios instead.\"\n },\n {\n \"kind\": \"deprecated-api\",\n \"package\": \"node-uuid\",\n \"reason\": \"node-uuid is deprecated; use the `uuid` package.\"\n }\n ]\n}\n","import type {\n BlastRadiusAffectedNode,\n BlastRadiusResult,\n DatabaseNode,\n ErrorEvent,\n GraphEdge,\n GraphNode,\n RootCauseResult,\n ServiceNode,\n TransitiveDependenciesResult,\n TransitiveDependency,\n} from '@neat.is/types'\nimport {\n BlastRadiusResultSchema,\n NodeType,\n PROV_RANK,\n Provenance,\n RootCauseResultSchema,\n TransitiveDependenciesResultSchema,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport {\n checkCompatibility,\n checkNodeEngineConstraint,\n checkPackageConflict,\n compatPairs,\n nodeEngineConstraints,\n packageConflicts,\n} from './compat.js'\n\n// Contract anchors (see /docs/contracts.md + docs/contracts/provenance.md):\n// * Rule 2 — Coexistence: walk by provenance priority, never collapse edges.\n// * Rule 3 — FRONTIER edges must be skipped, not merely deprioritized.\n// If a node's only edges are FRONTIER, traversal stops there.\n// * Rule 5 — Validate results against RootCauseResultSchema /\n// BlastRadiusResultSchema before returning.\n// * Rule 8 — No demo-name hardcoding: driver/engine identifiers come from\n// node properties + compatPairs(), never literals.\n// * ADR-029 — PROV_RANK is the canonical provenance ranking, imported\n// from @neat.is/types so consumers (traversal, MCP, policies) all agree.\n\nconst ROOT_CAUSE_MAX_DEPTH = 5\nconst BLAST_RADIUS_DEFAULT_DEPTH = 10\n\n// Multiple edges between the same pair coexist by provenance (EXTRACTED next to\n// OBSERVED next to INFERRED). Traversal walks the system as the graph \"sees it\n// best\", so for any neighbour pair we pick the highest-provenance edge.\n// FRONTIER means unknown territory — ADR-036 / Rule 3 require these edges to\n// be skipped, not merely deprioritized. Filtering happens here so the rest of\n// traversal can stay generic.\nfunction bestEdgeBySource(graph: NeatGraph, edgeIds: string[]): Map<string, GraphEdge> {\n const best = new Map<string, GraphEdge>()\n for (const id of edgeIds) {\n const e = graph.getEdgeAttributes(id) as GraphEdge\n if (e.provenance === Provenance.FRONTIER) continue\n const cur = best.get(e.source)\n if (!cur || PROV_RANK[e.provenance] > PROV_RANK[cur.provenance]) {\n best.set(e.source, e)\n }\n }\n return best\n}\n\nfunction bestEdgeByTarget(graph: NeatGraph, edgeIds: string[]): Map<string, GraphEdge> {\n const best = new Map<string, GraphEdge>()\n for (const id of edgeIds) {\n const e = graph.getEdgeAttributes(id) as GraphEdge\n if (e.provenance === Provenance.FRONTIER) continue\n const cur = best.get(e.target)\n if (!cur || PROV_RANK[e.provenance] > PROV_RANK[cur.provenance]) {\n best.set(e.target, e)\n }\n }\n return best\n}\n\n// Per-edge confidence is provenance × volume × recency × cleanliness.\n// * provenance gives a ceiling: OBSERVED 1.0, INFERRED 0.7, EXTRACTED 0.5,\n// STALE/FRONTIER 0.3.\n// * volume: log-scaled span count, saturating quickly so 1 span ≈ 0.55 and\n// ~1k spans ≈ 1.0.\n// * recency: 1.0 within an hour; decays toward 0.5 by 24h, toward 0.3 past.\n// * cleanliness: error rate above ~10% pulls the score down — a flapping\n// edge with thousands of spans shouldn't outrank a clean low-traffic one.\n// Bounded to [0, 1]. Walks of multiple edges multiply per-edge confidences.\nconst PROVENANCE_CEILING: Record<string, number> = {\n OBSERVED: 1.0,\n INFERRED: 0.7,\n EXTRACTED: 0.5,\n STALE: 0.3,\n FRONTIER: 0.3,\n}\n\nfunction volumeWeight(spanCount: number | undefined): number {\n if (!spanCount || spanCount <= 0) return 0.5\n // log10 saturating around ~1000 spans → ~1.0.\n const w = 0.5 + Math.log10(spanCount + 1) / 3\n return Math.min(1, w)\n}\n\nfunction recencyWeight(ageMs: number | undefined): number {\n if (ageMs === undefined) return 0.8\n const hour = 60 * 60 * 1000\n if (ageMs <= hour) return 1.0\n if (ageMs <= 24 * hour) {\n const t = (ageMs - hour) / (23 * hour)\n return 1.0 - 0.5 * t\n }\n return 0.3\n}\n\nfunction cleanlinessWeight(spanCount: number | undefined, errorCount: number | undefined): number {\n if (!spanCount || spanCount <= 0) return 1\n const rate = (errorCount ?? 0) / spanCount\n if (rate <= 0.01) return 1\n if (rate >= 0.5) return 0.3\n return 1 - rate * 1.4\n}\n\nexport function confidenceForEdge(edge: GraphEdge, now = Date.now()): number {\n const ceiling = PROVENANCE_CEILING[edge.provenance] ?? 0.5\n\n // No runtime signal yet → the provenance ceiling is all we have. This keeps\n // EXTRACTED-only graphs returning the same coarse 0.3/0.5/0.7/1.0 ladder\n // they always have, while letting OBSERVED edges with real OTel data move\n // off the ceiling once ingest starts populating signal counters.\n const spanCount = edge.signal?.spanCount ?? edge.callCount\n const ageMs = edge.signal?.lastObservedAgeMs ?? lastObservedAge(edge, now)\n if (spanCount === undefined && ageMs === undefined && edge.signal === undefined) {\n return ceiling\n }\n\n const v = volumeWeight(spanCount)\n const r = recencyWeight(ageMs)\n const c = cleanlinessWeight(spanCount, edge.signal?.errorCount)\n return Math.max(0, Math.min(1, ceiling * v * r * c))\n}\n\nfunction lastObservedAge(edge: GraphEdge, now: number): number | undefined {\n if (!edge.lastObserved) return undefined\n const t = Date.parse(edge.lastObserved)\n if (!Number.isFinite(t)) return undefined\n return Math.max(0, now - t)\n}\n\n// Path-level confidence is the *product* of per-edge confidences (ADR-036).\n// Each hop is independent evidence and uncertainty compounds — a 3-hop path\n// of edges at confidence 0.8 each gives 0.512, not 0.8. Multiplying punishes\n// long walks accordingly, which is the contract's intent: traversal should\n// surface the cumulative trust the graph actually has, not the weakest link\n// alone.\nfunction confidenceFromMix(edges: GraphEdge[], now = Date.now()): number {\n if (edges.length === 0) return 1.0\n let product = 1\n for (const e of edges) {\n product *= confidenceForEdge(e, now)\n }\n return Math.max(0, Math.min(1, product))\n}\n\ninterface Walk {\n path: string[]\n edges: GraphEdge[]\n}\n\n// DFS along incoming edges from start, depth-bounded. Returns the longest path\n// reachable, picking best-provenance edges per neighbour pair so the walk\n// reflects the system as the graph knows it most reliably.\nfunction longestIncomingWalk(graph: NeatGraph, start: string, maxDepth: number): Walk {\n let best: Walk = { path: [start], edges: [] }\n const visited = new Set<string>([start])\n\n function step(node: string, path: string[], edges: GraphEdge[]): void {\n if (path.length > best.path.length) {\n best = { path: [...path], edges: [...edges] }\n }\n if (path.length - 1 >= maxDepth) return\n\n const incoming = bestEdgeBySource(graph, graph.inboundEdges(node))\n for (const [srcId, edge] of incoming) {\n if (visited.has(srcId)) continue\n visited.add(srcId)\n path.push(srcId)\n edges.push(edge)\n step(srcId, path, edges)\n path.pop()\n edges.pop()\n visited.delete(srcId)\n }\n }\n\n step(start, [start], [])\n return best\n}\n\n// Per-shape match result. Each shape walks the same incoming `walk.path` but\n// looks for a different class of incompatibility. Adding a new shape (e.g. a\n// future ConfigNode \"missing required env var\" rule) is one entry in\n// `rootCauseShapes` plus its match function — no restructure to getRootCause.\ninterface RootCauseMatch {\n rootCauseNode: string\n rootCauseReason: string\n fixRecommendation?: string\n}\n\ntype RootCauseShape = (\n graph: NeatGraph,\n origin: GraphNode,\n walk: Walk,\n) => RootCauseMatch | null\n\n// DatabaseNode origin → driver/engine compat (the original v0.1.x behavior,\n// preserved verbatim). The walk ignores non-ServiceNodes; the first upstream\n// service whose declared driver fails compat against the origin DB's\n// (engine, engineVersion) wins.\nfunction databaseRootCauseShape(\n graph: NeatGraph,\n origin: GraphNode,\n walk: Walk,\n): RootCauseMatch | null {\n const targetDb = origin as DatabaseNode\n // Pairs that could possibly hit on this engine — narrowed once outside the\n // walk so we don't re-scan the matrix for every service we visit.\n const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine)\n if (candidatePairs.length === 0) return null\n\n for (const id of walk.path) {\n const attrs = graph.getNodeAttributes(id) as GraphNode\n if (attrs.type !== NodeType.ServiceNode) continue\n const svc = attrs as ServiceNode\n const deps = svc.dependencies ?? {}\n for (const pair of candidatePairs) {\n const declared = deps[pair.driver]\n if (!declared) continue\n const result = checkCompatibility(\n pair.driver,\n declared,\n targetDb.engine,\n targetDb.engineVersion,\n )\n if (!result.compatible) {\n return {\n rootCauseNode: id,\n rootCauseReason: result.reason ?? 'incompatible driver',\n ...(result.minDriverVersion\n ? {\n fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`,\n }\n : {}),\n }\n }\n }\n }\n return null\n}\n\n// ServiceNode origin → node-engine + package-conflict shapes from compat.ts.\n// The check is over each ServiceNode along the incoming walk (the origin\n// itself + any upstream callers): a node-engine constraint failing against\n// the service's `engines.node`, or a package-conflict where a declared dep\n// requires a peer at a higher version than the service has.\nfunction serviceRootCauseShape(\n graph: NeatGraph,\n _origin: GraphNode,\n walk: Walk,\n): RootCauseMatch | null {\n for (const id of walk.path) {\n const attrs = graph.getNodeAttributes(id) as GraphNode\n if (attrs.type !== NodeType.ServiceNode) continue\n const svc = attrs as ServiceNode\n const deps = svc.dependencies ?? {}\n const serviceNodeEngine = svc.nodeEngine\n\n for (const constraint of nodeEngineConstraints()) {\n const declared = deps[constraint.package]\n if (!declared) continue\n const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine)\n if (!result.compatible && result.reason) {\n return {\n rootCauseNode: id,\n rootCauseReason: result.reason,\n ...(result.requiredNodeVersion\n ? {\n fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`,\n }\n : {}),\n }\n }\n }\n\n for (const conflict of packageConflicts()) {\n const declared = deps[conflict.package]\n if (!declared) continue\n const requiredDeclared = deps[conflict.requires.name]\n const result = checkPackageConflict(conflict, declared, requiredDeclared)\n if (!result.compatible && result.reason) {\n return {\n rootCauseNode: id,\n rootCauseReason: result.reason,\n fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`,\n }\n }\n }\n }\n return null\n}\n\n// Dispatch by origin node type per ADR-037. Origin types not present here\n// (InfraNode, ConfigNode, FrontierNode) cleanly return null — getRootCause\n// needs an explicit shape to know what an \"incompatibility\" looks like for\n// that origin, and those types don't have one yet.\nconst rootCauseShapes: Partial<Record<GraphNode['type'], RootCauseShape>> = {\n [NodeType.DatabaseNode]: databaseRootCauseShape,\n [NodeType.ServiceNode]: serviceRootCauseShape,\n}\n\nexport function getRootCause(\n graph: NeatGraph,\n errorNodeId: string,\n errorEvent?: ErrorEvent,\n): RootCauseResult | null {\n if (!graph.hasNode(errorNodeId)) return null\n const origin = graph.getNodeAttributes(errorNodeId) as GraphNode\n const shape = rootCauseShapes[origin.type]\n if (!shape) return null\n\n const walk = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH)\n const match = shape(graph, origin, walk)\n if (!match) return null\n\n const reason = errorEvent\n ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})`\n : match.rootCauseReason\n\n // Schema-validate before return (ADR-036, #139). A drift in the result\n // shape becomes a runtime throw at the call site rather than a silently\n // malformed payload reaching MCP / REST consumers.\n return RootCauseResultSchema.parse({\n rootCauseNode: match.rootCauseNode,\n rootCauseReason: reason,\n traversalPath: walk.path,\n edgeProvenances: walk.edges.map((e) => e.provenance),\n confidence: confidenceFromMix(walk.edges),\n fixRecommendation: match.fixRecommendation,\n })\n}\n\n// BFS along outgoing edges from origin. Records each reachable node with the\n// shortest distance back to origin and the provenance of the edge that brought\n// us to it. Best-provenance edge selection per pair mirrors getRootCause.\nexport function getBlastRadius(\n graph: NeatGraph,\n nodeId: string,\n maxDepth = BLAST_RADIUS_DEFAULT_DEPTH,\n): BlastRadiusResult {\n if (!graph.hasNode(nodeId)) {\n return BlastRadiusResultSchema.parse({ origin: nodeId, affectedNodes: [], totalAffected: 0 })\n }\n\n // Each frame carries its full predecessor chain so the affected-node payload\n // can surface `path` (origin → ... → nodeId) and `confidence` (cascaded over\n // every edge along that path). The BFS visits each reachable node once on\n // its shortest-distance path; later frames at greater distance are dropped.\n interface Frame {\n nodeId: string\n distance: number\n path: string[]\n pathEdges: GraphEdge[]\n }\n\n const seen = new Map<string, BlastRadiusAffectedNode>()\n const queue: Frame[] = [{ nodeId, distance: 0, path: [nodeId], pathEdges: [] }]\n const enqueued = new Set<string>([nodeId])\n\n while (queue.length > 0) {\n const frame = queue.shift()!\n if (frame.distance > 0 && frame.pathEdges.length > 0) {\n const lastEdge = frame.pathEdges[frame.pathEdges.length - 1]!\n seen.set(frame.nodeId, {\n nodeId: frame.nodeId,\n distance: frame.distance,\n edgeProvenance: lastEdge.provenance,\n path: frame.path,\n confidence: confidenceFromMix(frame.pathEdges),\n })\n }\n if (frame.distance >= maxDepth) continue\n\n const outgoing = bestEdgeByTarget(graph, graph.outboundEdges(frame.nodeId))\n for (const [tgtId, edge] of outgoing) {\n if (enqueued.has(tgtId)) continue\n enqueued.add(tgtId)\n queue.push({\n nodeId: tgtId,\n distance: frame.distance + 1,\n path: [...frame.path, tgtId],\n pathEdges: [...frame.pathEdges, edge],\n })\n }\n }\n\n const affectedNodes = [...seen.values()].sort(\n (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n )\n return BlastRadiusResultSchema.parse({\n origin: nodeId,\n affectedNodes,\n totalAffected: affectedNodes.length,\n })\n}\n\n// Default + max depth for transitive get_dependencies (issue #144). Default\n// 3 keeps the output legible at the agent layer; the contract caps the\n// caller-supplied value at 10 to prevent BFS blow-up on dense graphs.\nexport const TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH = 3\nexport const TRANSITIVE_DEPENDENCIES_MAX_DEPTH = 10\n\n// Transitive get_dependencies (ADR-039 / #144). BFS outbound from origin to\n// `depth` hops, returning a flat list with distance, edgeType, and provenance\n// per dependency. Origin is never in the list. Direct-only consumers pass\n// depth=1; the MCP get_dependencies tool defaults to 3.\n//\n// Reuses bestEdgeByTarget (FRONTIER filtered, PROV_RANK-best per pair) so\n// dedup behavior matches the rest of traversal. Result is schema-validated\n// before return per ADR-036 §Result schema validation.\nexport function getTransitiveDependencies(\n graph: NeatGraph,\n nodeId: string,\n depth: number = TRANSITIVE_DEPENDENCIES_DEFAULT_DEPTH,\n): TransitiveDependenciesResult {\n if (!graph.hasNode(nodeId)) {\n return TransitiveDependenciesResultSchema.parse({\n origin: nodeId,\n depth,\n dependencies: [],\n total: 0,\n })\n }\n\n interface Frame {\n nodeId: string\n distance: number\n edge: GraphEdge | null\n }\n\n const seen = new Map<string, TransitiveDependency>()\n const queue: Frame[] = [{ nodeId, distance: 0, edge: null }]\n const enqueued = new Set<string>([nodeId])\n\n while (queue.length > 0) {\n const frame = queue.shift()!\n if (frame.distance > 0 && frame.edge) {\n seen.set(frame.nodeId, {\n nodeId: frame.nodeId,\n distance: frame.distance,\n edgeType: frame.edge.type,\n provenance: frame.edge.provenance,\n })\n }\n if (frame.distance >= depth) continue\n\n const outgoing = bestEdgeByTarget(graph, graph.outboundEdges(frame.nodeId))\n for (const [tgtId, edge] of outgoing) {\n if (enqueued.has(tgtId)) continue\n enqueued.add(tgtId)\n queue.push({ nodeId: tgtId, distance: frame.distance + 1, edge })\n }\n }\n\n const dependencies = [...seen.values()].sort(\n (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n )\n return TransitiveDependenciesResultSchema.parse({\n origin: nodeId,\n depth,\n dependencies,\n total: dependencies.length,\n })\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type {\n DatabaseNode,\n ErrorEvent,\n FrontierNode,\n GraphEdge,\n Policy,\n ServiceNode,\n} from '@neat.is/types'\nimport type { EvaluationContext as PolicyEvaluationContext } from './policy.js'\nimport { canPromoteFrontier } from './policy.js'\nimport {\n EdgeType,\n NodeType,\n Provenance,\n databaseId,\n extractedEdgeId,\n frontierEdgeId,\n frontierId,\n inferredEdgeId,\n observedEdgeId,\n serviceId,\n type EdgeTypeValue,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport type { ParsedSpan } from './otel.js'\n\n// Maps OTel spans to graph signal:\n// * Cross-service span → upsert CALLS edge.\n// * Database span (db.system attr present) → upsert CONNECTS_TO edge to a\n// DatabaseNode resolved by host.\n// * Span with status.code === 2 → ErrorEvent appended to errors.ndjson.\n//\n// Contract anchors (see /docs/contracts.md):\n// * Rule 1 — Provenance: every edge here carries Provenance.X from @neat.is/types.\n// * Rule 2 — Coexistence: OBSERVED edges live alongside EXTRACTED ones with a\n// distinct id pattern (`${type}:OBSERVED:src->tgt`). Never write OBSERVED\n// under the EXTRACTED id; that erases the gap NEAT exists to surface.\n// * Rule 4 — Per-edge-type staleness (ADR-024): STALE_THRESHOLDS_BY_EDGE_TYPE\n// governs decay; never hardcode a flat 24h threshold.\n// * Rule 8 — No demo names: derive driver/engine identifiers from node\n// properties, not literals.\n\nexport interface IngestContext {\n graph: NeatGraph\n errorsPath: string\n now?: () => number\n // Set to false when the receiver already wrote the ErrorEvent synchronously\n // (production daemons via watch.ts wire this). When true or omitted, handleSpan\n // appends the ErrorEvent itself — the path used by ad-hoc scripts and tests\n // that don't go through buildOtelReceiver. ADR-033 §Error events.\n writeErrorEventInline?: boolean\n // Post-mutation policy trigger (ADR-043). Fires after handleSpan finishes\n // and the queue is drained. Daemons wire this to evaluateAllPolicies +\n // PolicyViolationsLog.append. Ad-hoc callers leave it undefined; their tests\n // don't need policy side effects.\n onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void\n}\n\nconst HOUR_MS = 60 * 60 * 1000\nconst DAY_MS = 24 * HOUR_MS\n\n// Per-edge-type stale thresholds. HTTP CALLS at 24h is meaningless because\n// healthy traffic recurs in seconds; infra DEPENDS_ON is the opposite — a\n// docker-compose service can sit idle overnight without anything being wrong.\n// Override via NEAT_STALE_THRESHOLDS (JSON, ms-per-edge-type).\nconst DEFAULT_STALE_THRESHOLDS: Record<string, number> = {\n CALLS: HOUR_MS,\n CONNECTS_TO: 4 * HOUR_MS,\n PUBLISHES_TO: 4 * HOUR_MS,\n CONSUMES_FROM: 4 * HOUR_MS,\n DEPENDS_ON: DAY_MS,\n CONFIGURED_BY: DAY_MS,\n RUNS_ON: DAY_MS,\n}\n// Fallback for any edge type not in the map (forward compat — adding a new\n// EdgeType shouldn't break staleness sweeps).\nconst FALLBACK_STALE_THRESHOLD_MS = DAY_MS\n\nfunction loadStaleThresholdsFromEnv(): Record<string, number> {\n const raw = process.env.NEAT_STALE_THRESHOLDS\n if (!raw) return DEFAULT_STALE_THRESHOLDS\n try {\n const overrides = JSON.parse(raw) as Record<string, unknown>\n const merged = { ...DEFAULT_STALE_THRESHOLDS }\n for (const [k, v] of Object.entries(overrides)) {\n if (typeof v === 'number' && Number.isFinite(v) && v >= 0) merged[k] = v\n }\n return merged\n } catch (err) {\n console.warn(\n `[neat] NEAT_STALE_THRESHOLDS could not be parsed (${(err as Error).message}); using defaults`,\n )\n return DEFAULT_STALE_THRESHOLDS\n }\n}\n\nexport function thresholdForEdgeType(\n edgeType: string,\n overrides?: Record<string, number>,\n): number {\n const map = overrides ?? loadStaleThresholdsFromEnv()\n return map[edgeType] ?? FALLBACK_STALE_THRESHOLD_MS\n}\n\nfunction nowIso(ctx: IngestContext): string {\n return new Date(ctx.now ? ctx.now() : Date.now()).toISOString()\n}\n\nfunction pickAttr(span: ParsedSpan, ...keys: string[]): string | undefined {\n for (const k of keys) {\n const v = span.attributes[k]\n if (typeof v === 'string' && v.length > 0) return v\n }\n return undefined\n}\n\nfunction hostFromUrl(u: string | undefined): string | undefined {\n if (!u) return undefined\n try {\n return new URL(u).hostname\n } catch {\n return undefined\n }\n}\n\n// OTel HTTP/db semconv has gone through several names for \"the host on the\n// other end of this call.\" Try the modern ones first, fall back to the legacy\n// ones, then last resort parse out of a full URL.\nfunction pickAddress(span: ParsedSpan): string | undefined {\n return (\n pickAttr(span, 'server.address', 'net.peer.name', 'net.host.name') ??\n hostFromUrl(pickAttr(span, 'url.full', 'http.url'))\n )\n}\n\n// Edge id helpers live in @neat.is/types/identity.ts (ADR-029). The local\n// signatures below preserve the (type, source, target) argument order ingest.ts\n// has used historically while delegating to the canonical wire-format helpers.\nfunction makeObservedEdgeId(type: EdgeTypeValue, source: string, target: string): string {\n return observedEdgeId(source, target, type)\n}\n\nfunction makeInferredEdgeId(type: EdgeTypeValue, source: string, target: string): string {\n return inferredEdgeId(source, target, type)\n}\n\nconst INFERRED_CONFIDENCE = 0.6\nconst STITCH_MAX_DEPTH = 2\n\n// Parent-span TTL cache (ADR-033). Address-based peer resolution (server.address /\n// net.peer.name / url.full) misses non-HTTP RPCs and any span with an opaque\n// peer. The cache stores each span's service keyed by `${traceId}:${spanId}` so\n// a child span whose address resolution fails can fall back to its parent's\n// service, identifying a cross-service CALLS edge from parent → current.\n//\n// Bounded size + TTL — out-of-order arrival (child before parent) drops the\n// child rather than buffering. We accept that loss because the cache is best-\n// effort: for every cross-service call, the CLIENT span on the caller side\n// covers the same edge via address-based resolution, so missing one direction\n// is recoverable.\nconst PARENT_SPAN_CACHE_SIZE = 10_000\nconst PARENT_SPAN_CACHE_TTL_MS = 5 * 60 * 1000\n\ninterface ParentSpanCacheEntry {\n service: string\n expiresAt: number\n}\n\nconst parentSpanCache = new Map<string, ParentSpanCacheEntry>()\n\nfunction parentSpanKey(traceId: string, spanId: string): string {\n return `${traceId}:${spanId}`\n}\n\nfunction cacheSpanService(span: ParsedSpan, now: number): void {\n if (!span.traceId || !span.spanId) return\n const key = parentSpanKey(span.traceId, span.spanId)\n // Map preserves insertion order, so deleting + re-inserting bumps an entry to\n // the back. Eviction is \"drop oldest\" once size exceeds the cap.\n parentSpanCache.delete(key)\n parentSpanCache.set(key, { service: span.service, expiresAt: now + PARENT_SPAN_CACHE_TTL_MS })\n while (parentSpanCache.size > PARENT_SPAN_CACHE_SIZE) {\n const oldest = parentSpanCache.keys().next().value\n if (!oldest) break\n parentSpanCache.delete(oldest)\n }\n}\n\nfunction lookupParentSpanService(\n traceId: string,\n parentSpanId: string,\n now: number,\n): string | null {\n const entry = parentSpanCache.get(parentSpanKey(traceId, parentSpanId))\n if (!entry) return null\n if (entry.expiresAt <= now) {\n parentSpanCache.delete(parentSpanKey(traceId, parentSpanId))\n return null\n }\n return entry.service\n}\n\n// Test seam: lets unit tests start from a clean slate.\nexport function resetParentSpanCache(): void {\n parentSpanCache.clear()\n}\n\nfunction resolveServiceId(graph: NeatGraph, host: string): string | null {\n const direct = serviceId(host)\n if (graph.hasNode(direct)) return direct\n\n // Service hostnames in the demo can match either the package name (which the\n // node id is built from) or the directory basename — handled by the name\n // check below. Beyond that, anything in `aliases` (compose service names,\n // k8s metadata.name + cluster-DNS variants, Dockerfile labels) should\n // resolve too. Population happens in the extract phases; consumption is\n // here.\n let found: string | null = null\n graph.forEachNode((id, attrs) => {\n if (found) return\n const a = attrs as ServiceNode & { type?: string }\n if (a.type !== NodeType.ServiceNode) return\n if (a.name === host) {\n found = id\n return\n }\n if (a.aliases && a.aliases.includes(host)) {\n found = id\n }\n })\n return found\n}\n\nexport function frontierIdFor(host: string): string {\n return frontierId(host)\n}\n\n// Auto-create a minimal ServiceNode for span.service when no such node exists.\n// Used at the top of handleSpan so subsequent edge upserts always have endpoints\n// — without it, OBSERVED edges silently drop for any service the static\n// extractor hasn't reached yet (and never reaches at all in OTel-only setups).\n// `language: 'unknown'` is the contract's specified placeholder (ADR-033). When\n// static extraction later produces a ServiceNode at the same id, addServiceNodes\n// merges and flips discoveredVia to 'merged' rather than overwriting.\nfunction ensureServiceNode(graph: NeatGraph, serviceName: string): string {\n const id = serviceId(serviceName)\n if (graph.hasNode(id)) return id\n const node: ServiceNode = {\n id,\n type: NodeType.ServiceNode,\n name: serviceName,\n language: 'unknown',\n discoveredVia: 'otel',\n }\n graph.addNode(id, node)\n return id\n}\n\n// Same shape for unseen db.system + host pairs. Engine comes off the OTel\n// attribute as a string per Rule 8 — no hardcoded engine list. compatibleDrivers\n// is empty until static extraction merges in the matrix-derived drivers.\nfunction ensureDatabaseNode(graph: NeatGraph, host: string, engine: string): string {\n const id = databaseId(host)\n if (graph.hasNode(id)) return id\n const node: DatabaseNode = {\n id,\n type: NodeType.DatabaseNode,\n name: host,\n engine,\n engineVersion: 'unknown',\n compatibleDrivers: [],\n host,\n discoveredVia: 'otel',\n }\n graph.addNode(id, node)\n return id\n}\n\nfunction ensureFrontierNode(graph: NeatGraph, host: string, ts: string): string {\n const id = frontierIdFor(host)\n if (graph.hasNode(id)) {\n const existing = graph.getNodeAttributes(id) as FrontierNode\n graph.replaceNodeAttributes(id, { ...existing, lastObserved: ts })\n return id\n }\n const node: FrontierNode = {\n id,\n type: NodeType.FrontierNode,\n name: host,\n host,\n firstObserved: ts,\n lastObserved: ts,\n }\n graph.addNode(id, node)\n return id\n}\n\nfunction upsertFrontierEdge(\n graph: NeatGraph,\n type: EdgeTypeValue,\n source: string,\n target: string,\n ts: string,\n): void {\n const id = frontierEdgeId(source, target, type)\n if (graph.hasEdge(id)) {\n const existing = graph.getEdgeAttributes(id) as GraphEdge\n const updated: GraphEdge = {\n ...existing,\n provenance: Provenance.FRONTIER,\n lastObserved: ts,\n callCount: (existing.callCount ?? 0) + 1,\n }\n graph.replaceEdgeAttributes(id, updated)\n return\n }\n const edge: GraphEdge = {\n id,\n source,\n target,\n type,\n provenance: Provenance.FRONTIER,\n confidence: 1.0,\n lastObserved: ts,\n callCount: 1,\n }\n graph.addEdgeWithKey(id, source, target, edge)\n}\n\ninterface UpsertResult {\n edge: GraphEdge\n created: boolean\n}\n\nfunction upsertObservedEdge(\n graph: NeatGraph,\n type: EdgeTypeValue,\n source: string,\n target: string,\n ts: string,\n isError = false,\n): UpsertResult | null {\n if (!graph.hasNode(source) || !graph.hasNode(target)) return null\n\n const id = makeObservedEdgeId(type, source, target)\n if (graph.hasEdge(id)) {\n const existing = graph.getEdgeAttributes(id) as GraphEdge\n const newSpanCount = (existing.signal?.spanCount ?? existing.callCount ?? 0) + 1\n const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0)\n const updated: GraphEdge = {\n ...existing,\n provenance: Provenance.OBSERVED,\n lastObserved: ts,\n callCount: newSpanCount,\n signal: {\n spanCount: newSpanCount,\n errorCount: newErrorCount,\n lastObservedAgeMs: 0,\n },\n confidence: 1.0,\n }\n graph.replaceEdgeAttributes(id, updated)\n return { edge: updated, created: false }\n }\n\n const edge: GraphEdge = {\n id,\n source,\n target,\n type,\n provenance: Provenance.OBSERVED,\n confidence: 1.0,\n lastObserved: ts,\n callCount: 1,\n signal: {\n spanCount: 1,\n errorCount: isError ? 1 : 0,\n lastObservedAgeMs: 0,\n },\n }\n graph.addEdgeWithKey(id, source, target, edge)\n return { edge, created: true }\n}\n\n// When a span errors, the system is exercising its dependencies right now even\n// if some of them aren't auto-instrumented (pg 7.4.0 in the demo, see ADR-014).\n// Walk EXTRACTED edges out from the erroring service for a couple of hops and\n// promote them to INFERRED twins so traversal can prefer them over the bare\n// static edges without claiming OBSERVED-grade certainty.\nfunction stitchTrace(graph: NeatGraph, sourceServiceId: string, ts: string): void {\n if (!graph.hasNode(sourceServiceId)) return\n\n const visited = new Set<string>([sourceServiceId])\n const queue: { nodeId: string; depth: number }[] = [{ nodeId: sourceServiceId, depth: 0 }]\n\n while (queue.length > 0) {\n const { nodeId, depth } = queue.shift()!\n if (depth >= STITCH_MAX_DEPTH) continue\n\n const outbound = graph.outboundEdges(nodeId)\n for (const edgeId of outbound) {\n const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (edge.provenance !== Provenance.EXTRACTED) continue\n\n // OBSERVED twin already covers this hop with ground truth — no inference\n // needed (ADR-034). Stomping it with INFERRED erases the gap NEAT exists\n // to surface; skipping it keeps the OBSERVED edge as the authoritative\n // record and avoids cluttering the graph with a redundant INFERRED twin.\n if (graph.hasEdge(observedEdgeId(edge.source, edge.target, edge.type))) continue\n\n upsertInferredEdge(graph, edge.type, edge.source, edge.target, ts)\n\n if (!visited.has(edge.target)) {\n visited.add(edge.target)\n queue.push({ nodeId: edge.target, depth: depth + 1 })\n }\n }\n }\n}\n\nfunction upsertInferredEdge(\n graph: NeatGraph,\n type: EdgeTypeValue,\n source: string,\n target: string,\n ts: string,\n): void {\n const id = makeInferredEdgeId(type, source, target)\n if (graph.hasEdge(id)) {\n const existing = graph.getEdgeAttributes(id) as GraphEdge\n const updated: GraphEdge = { ...existing, lastObserved: ts }\n graph.replaceEdgeAttributes(id, updated)\n return\n }\n\n const edge: GraphEdge = {\n id,\n source,\n target,\n type,\n provenance: Provenance.INFERRED,\n confidence: INFERRED_CONFIDENCE,\n lastObserved: ts,\n }\n graph.addEdgeWithKey(id, source, target, edge)\n}\n\nasync function appendErrorEvent(ctx: IngestContext, ev: ErrorEvent): Promise<void> {\n await fs.mkdir(path.dirname(ctx.errorsPath), { recursive: true })\n await fs.appendFile(ctx.errorsPath, JSON.stringify(ev) + '\\n', 'utf8')\n}\n\n// Build the minimal ErrorEvent the receiver writes synchronously before\n// replying (ADR-033 §Error events, amended). affectedNode resolves to the\n// originating service because graph state isn't available at this point —\n// the queued handleSpan path may reach a more precise target later, but the\n// durable record is what the receiver writes here.\nexport function buildErrorEventForReceiver(span: ParsedSpan): ErrorEvent | null {\n if (span.statusCode !== 2) return null\n const ts = span.startTimeIso ?? new Date().toISOString()\n return {\n id: `${span.traceId}:${span.spanId}`,\n timestamp: ts,\n service: span.service,\n traceId: span.traceId,\n spanId: span.spanId,\n errorMessage:\n span.exception?.message ?? span.errorMessage ?? span.name ?? 'unknown error',\n ...(span.exception?.type ? { exceptionType: span.exception.type } : {}),\n ...(span.exception?.stacktrace\n ? { exceptionStacktrace: span.exception.stacktrace }\n : {}),\n affectedNode: serviceId(span.service),\n }\n}\n\n// Synchronous file-write helper bound to a receiver. The receiver awaits this\n// before replying, so a write failure surfaces as 500 → OTel SDK retries.\nexport function makeErrorSpanWriter(\n errorsPath: string,\n): (span: ParsedSpan) => Promise<void> {\n return async (span) => {\n const ev = buildErrorEventForReceiver(span)\n if (!ev) return\n await fs.mkdir(path.dirname(errorsPath), { recursive: true })\n await fs.appendFile(errorsPath, JSON.stringify(ev) + '\\n', 'utf8')\n }\n}\n\nexport async function handleSpan(ctx: IngestContext, span: ParsedSpan): Promise<void> {\n // lastObserved derives from the span's own startTime per ADR-033 — replayed\n // traces and out-of-order spans get a timestamp that reflects when the call\n // actually fired, not when the receiver received it. Wall-clock is only the\n // fallback for spans whose startTimeUnixNano is missing or unparseable.\n const ts = span.startTimeIso ?? nowIso(ctx)\n const nowMs = ctx.now ? ctx.now() : Date.now()\n // Auto-create a minimal ServiceNode for unseen span.service so OBSERVED\n // edges land instead of silently dropping. Static extraction merges richer\n // fields when it later finds the same id (ADR-033).\n const sourceId = ensureServiceNode(ctx.graph, span.service)\n const isError = span.statusCode === 2\n // Stash this span in the parent-span cache so any later child whose address\n // resolution misses can still resolve the cross-service edge via parentSpanId.\n cacheSpanService(span, nowMs)\n\n let affectedNode = sourceId\n\n if (span.dbSystem) {\n // Database span — try to resolve the DatabaseNode by host.\n const host = pickAddress(span)\n if (host) {\n // Auto-create a minimal DatabaseNode when this host hasn't been seen.\n // Engine comes off the OTel attribute as a string per Rule 8.\n ensureDatabaseNode(ctx.graph, host, span.dbSystem)\n const targetId = databaseId(host)\n const result = upsertObservedEdge(\n ctx.graph,\n EdgeType.CONNECTS_TO,\n sourceId,\n targetId,\n ts,\n isError,\n )\n if (result) affectedNode = targetId\n }\n } else {\n // Possibly a cross-service call. Resolve the peer; if it matches a known\n // ServiceNode, record an OBSERVED CALLS edge. If it matches nothing — pod\n // IP, ingress hostname, AWS PrivateLink endpoint — drop a FRONTIER\n // placeholder so the call isn't lost. promoteFrontierNodes (run by the\n // extract orchestrator) replaces it once a later round records the host\n // as an alias on a real service.\n const host = pickAddress(span)\n let resolvedViaAddress = false\n if (host && host !== span.service) {\n const targetId = resolveServiceId(ctx.graph, host)\n if (targetId && targetId !== sourceId) {\n upsertObservedEdge(\n ctx.graph,\n EdgeType.CALLS,\n sourceId,\n targetId,\n ts,\n isError,\n )\n affectedNode = targetId\n resolvedViaAddress = true\n } else if (!targetId) {\n const frontierId = ensureFrontierNode(ctx.graph, host, ts)\n if (ctx.graph.hasNode(sourceId)) {\n upsertFrontierEdge(ctx.graph, EdgeType.CALLS, sourceId, frontierId, ts)\n }\n affectedNode = frontierId\n resolvedViaAddress = true\n }\n }\n\n // Parent-span fallback (ADR-033): when address-based resolution didn't\n // produce an edge and the span has a parentSpanId we've cached, the\n // parent's service identifies the caller. The current span is the server\n // side of the call, so the edge direction is parent.service → current.\n if (!resolvedViaAddress && span.parentSpanId) {\n const parentService = lookupParentSpanService(span.traceId, span.parentSpanId, nowMs)\n if (parentService && parentService !== span.service) {\n const parentId = ensureServiceNode(ctx.graph, parentService)\n upsertObservedEdge(\n ctx.graph,\n EdgeType.CALLS,\n parentId,\n sourceId,\n ts,\n isError,\n )\n }\n }\n }\n\n if (span.statusCode === 2) {\n stitchTrace(ctx.graph, sourceId, ts)\n // The durable ErrorEvent write moved to the receiver so the file write\n // happens synchronously before the 200 reply (ADR-033 §Error events,\n // amended). watch.ts wires makeErrorSpanWriter into onErrorSpanSync.\n // handleSpan still runs the in-graph error effects (stitchTrace above);\n // it just doesn't append to errors.ndjson anymore. ctx.errorsPath stays\n // for the optional opt-in path below — daemon-less callers (CLI tests,\n // ad-hoc scripts) that skip the receiver hook still get a write here.\n if (ctx.writeErrorEventInline !== false) {\n const ev: ErrorEvent = {\n id: `${span.traceId}:${span.spanId}`,\n timestamp: ts,\n service: span.service,\n traceId: span.traceId,\n spanId: span.spanId,\n errorMessage:\n span.exception?.message ?? span.errorMessage ?? span.name ?? 'unknown error',\n ...(span.exception?.type ? { exceptionType: span.exception.type } : {}),\n ...(span.exception?.stacktrace\n ? { exceptionStacktrace: span.exception.stacktrace }\n : {}),\n affectedNode,\n }\n await appendErrorEvent(ctx, ev)\n }\n }\n void affectedNode\n\n // Post-ingest policy trigger (ADR-043). The hook is awaited so failures\n // surface; daemons wrap it in a try/catch that logs without throwing.\n if (ctx.onPolicyTrigger) await ctx.onPolicyTrigger(ctx.graph)\n}\n\nexport { stitchTrace }\n\n// Promote any frontier:<host> placeholder whose host matches an alias on a\n// real ServiceNode: re-link inbound/outbound edges to the service, then drop\n// the placeholder. Returns the count of nodes promoted, for tests + logs.\n//\n// Called at the end of every extraction round. Static rounds are when new\n// aliases land (compose names, k8s metadata.name, Dockerfile labels), so\n// running it there picks up the case the issue describes: ingest fills in a\n// frontier when traffic arrives for an unknown host, and the next extraction\n// round resolves it.\n// Optional gate for block-action policies (ADR-044). When `policies` is\n// non-empty, each candidate FrontierNode runs through `canPromoteFrontier`\n// before its incident edges are rewired. Block-action policies that fire on\n// the frontier veto the promotion — the FrontierNode persists; the next\n// extract pass tries again.\nexport interface PromoteFrontierOptions {\n policies?: Policy[]\n policyCtx?: PolicyEvaluationContext\n}\n\nexport function promoteFrontierNodes(\n graph: NeatGraph,\n opts: PromoteFrontierOptions = {},\n): number {\n const aliasIndex = new Map<string, string>()\n graph.forEachNode((id, attrs) => {\n const a = attrs as ServiceNode & { type?: string }\n if (a.type !== NodeType.ServiceNode) return\n aliasIndex.set(a.name, id)\n if (a.aliases) {\n for (const alias of a.aliases) aliasIndex.set(alias, id)\n }\n })\n\n const toPromote: { frontierId: string; serviceId: string }[] = []\n graph.forEachNode((id, attrs) => {\n const a = attrs as FrontierNode & { type?: string }\n if (a.type !== NodeType.FrontierNode) return\n const target = aliasIndex.get(a.host)\n if (!target) return\n if (target === id) return\n toPromote.push({ frontierId: id, serviceId: target })\n })\n\n let promoted = 0\n for (const { frontierId, serviceId } of toPromote) {\n if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {\n const gate = canPromoteFrontier(graph, frontierId, opts.policies, opts.policyCtx)\n if (!gate.allowed) {\n // Block-action policy fired on this frontier — skip the rewire and\n // leave the FrontierNode in place. Violations already surfaced via\n // the policy log on the same evaluation pass.\n continue\n }\n }\n rewireFrontierEdges(graph, frontierId, serviceId)\n graph.dropNode(frontierId)\n promoted++\n }\n return promoted\n}\n\nfunction rewireFrontierEdges(graph: NeatGraph, frontierId: string, serviceId: string): void {\n const inbound = [...graph.inboundEdges(frontierId)]\n const outbound = [...graph.outboundEdges(frontierId)]\n\n for (const edgeId of inbound) {\n const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n rebuildEdge(graph, edge, edge.source, serviceId, edgeId)\n }\n for (const edgeId of outbound) {\n const edge = graph.getEdgeAttributes(edgeId) as GraphEdge\n rebuildEdge(graph, edge, serviceId, edge.target, edgeId)\n }\n}\n\nfunction rebuildEdge(\n graph: NeatGraph,\n edge: GraphEdge,\n newSource: string,\n newTarget: string,\n oldEdgeId: string,\n): void {\n graph.dropEdge(oldEdgeId)\n // FRONTIER provenance gets upgraded to OBSERVED on promotion: the call\n // certainty was always there; only the target identity was unknown, and now\n // it isn't.\n const promotedProvenance =\n edge.provenance === Provenance.FRONTIER ? Provenance.OBSERVED : edge.provenance\n const newId =\n promotedProvenance === Provenance.OBSERVED\n ? observedEdgeId(newSource, newTarget, edge.type)\n : promotedProvenance === Provenance.INFERRED\n ? inferredEdgeId(newSource, newTarget, edge.type)\n : promotedProvenance === Provenance.EXTRACTED\n ? extractedEdgeId(newSource, newTarget, edge.type)\n : frontierEdgeId(newSource, newTarget, edge.type)\n\n if (graph.hasEdge(newId)) {\n const existing = graph.getEdgeAttributes(newId) as GraphEdge\n const merged: GraphEdge = {\n ...existing,\n callCount: (existing.callCount ?? 0) + (edge.callCount ?? 0),\n lastObserved: pickLater(existing.lastObserved, edge.lastObserved),\n }\n graph.replaceEdgeAttributes(newId, merged)\n return\n }\n\n const rebuilt: GraphEdge = {\n ...edge,\n id: newId,\n source: newSource,\n target: newTarget,\n provenance: promotedProvenance,\n }\n graph.addEdgeWithKey(newId, newSource, newTarget, rebuilt)\n}\n\nfunction pickLater(a: string | undefined, b: string | undefined): string | undefined {\n if (!a) return b\n if (!b) return a\n return new Date(a).getTime() >= new Date(b).getTime() ? a : b\n}\n\nexport function makeSpanHandler(ctx: IngestContext): (span: ParsedSpan) => Promise<void> {\n return (span) => handleSpan(ctx, span)\n}\n\nexport interface StaleEvent {\n edgeId: string\n source: string\n target: string\n edgeType: string\n thresholdMs: number\n ageMs: number\n lastObserved: string\n transitionedAt: string\n}\n\nexport interface MarkStaleOptions {\n // Per-edge-type override map. Defaults to DEFAULT_STALE_THRESHOLDS, merged\n // with NEAT_STALE_THRESHOLDS if the env var is set.\n thresholds?: Record<string, number>\n now?: number\n // ndjson path. When set, every OBSERVED → STALE transition appends one\n // line. Skipped if undefined — tests and embedded use cases don't need a\n // log.\n staleEventsPath?: string\n}\n\n// Demote OBSERVED edges that haven't been seen in a while. Per-edge-type\n// thresholds: HTTP CALLS go stale fast; infra DEPENDS_ON is patient. Returns\n// the count of demotions and the events appended to the log.\nexport async function markStaleEdges(\n graph: NeatGraph,\n options: MarkStaleOptions = {},\n): Promise<{ count: number; events: StaleEvent[] }> {\n const thresholds = options.thresholds ?? loadStaleThresholdsFromEnv()\n const now = options.now ?? Date.now()\n const events: StaleEvent[] = []\n\n graph.forEachEdge((id, attrs) => {\n const e = attrs as GraphEdge\n if (e.provenance !== Provenance.OBSERVED) return\n if (!e.lastObserved) return\n const threshold = thresholdForEdgeType(e.type, thresholds)\n const age = now - new Date(e.lastObserved).getTime()\n if (age > threshold) {\n const updated: GraphEdge = { ...e, provenance: Provenance.STALE, confidence: 0.3 }\n graph.replaceEdgeAttributes(id, updated)\n events.push({\n edgeId: id,\n source: e.source,\n target: e.target,\n edgeType: e.type,\n thresholdMs: threshold,\n ageMs: age,\n lastObserved: e.lastObserved,\n transitionedAt: new Date(now).toISOString(),\n })\n }\n })\n\n if (options.staleEventsPath && events.length > 0) {\n await appendStaleEvents(options.staleEventsPath, events)\n }\n\n return { count: events.length, events }\n}\n\nasync function appendStaleEvents(staleEventsPath: string, events: StaleEvent[]): Promise<void> {\n await fs.mkdir(path.dirname(staleEventsPath), { recursive: true })\n const lines = events.map((e) => JSON.stringify(e)).join('\\n') + '\\n'\n await fs.appendFile(staleEventsPath, lines, 'utf8')\n}\n\nexport async function readStaleEvents(staleEventsPath: string): Promise<StaleEvent[]> {\n try {\n const raw = await fs.readFile(staleEventsPath, 'utf8')\n return raw\n .split('\\n')\n .filter((line) => line.length > 0)\n .map((line) => JSON.parse(line) as StaleEvent)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw err\n }\n}\n\nexport interface StalenessLoopOptions {\n thresholds?: Record<string, number>\n intervalMs?: number\n staleEventsPath?: string\n // Post-stale-transition policy trigger (ADR-043). Fires after each tick of\n // markStaleEdges so policies see the new STALE state. Daemons wire this to\n // evaluateAllPolicies + PolicyViolationsLog.append.\n onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void\n}\n\nexport function startStalenessLoop(\n graph: NeatGraph,\n options: StalenessLoopOptions = {},\n): () => void {\n let stopped = false\n const intervalMs = options.intervalMs ?? 60_000\n const tick = (): void => {\n if (stopped) return\n void (async () => {\n try {\n await markStaleEdges(graph, {\n thresholds: options.thresholds,\n staleEventsPath: options.staleEventsPath,\n })\n if (options.onPolicyTrigger) await options.onPolicyTrigger(graph)\n } catch (err) {\n console.error('staleness tick failed', err)\n }\n })()\n }\n const interval = setInterval(tick, intervalMs)\n if (typeof interval.unref === 'function') interval.unref()\n return () => {\n stopped = true\n clearInterval(interval)\n }\n}\n\nexport async function readErrorEvents(errorsPath: string): Promise<ErrorEvent[]> {\n try {\n const raw = await fs.readFile(errorsPath, 'utf8')\n return raw\n .split('\\n')\n .filter((line) => line.length > 0)\n .map((line) => JSON.parse(line) as ErrorEvent)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw err\n }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type {\n GraphEdge,\n GraphNode,\n Policy,\n PolicyAction,\n PolicyFile,\n PolicyRule,\n PolicySeverity,\n PolicyViolation,\n ServiceNode,\n} from '@neat.is/types'\nimport {\n EdgeType,\n NodeType,\n PolicyFileSchema,\n Provenance,\n} from '@neat.is/types'\nimport type { NeatGraph } from './graph.js'\nimport {\n checkCompatibility,\n checkDeprecatedApi,\n checkNodeEngineConstraint,\n checkPackageConflict,\n compatPairs,\n deprecatedApis,\n nodeEngineConstraints,\n packageConflicts,\n} from './compat.js'\nimport { getBlastRadius } from './traverse.js'\n\n// Policy evaluation engine (ADR-043). The entry point evaluateAllPolicies is\n// pure: same graph + same policies → same violations. Per-rule-type dispatch\n// via the policyEvaluators table. Adding a new rule type means one new\n// evaluator entry plus the schema entry in @neat.is/types/policy.ts.\n//\n// Deterministic violation ids per ADR-043: ${policy.id}:${context}. The\n// context is shape-specific (nodeId, edgeId, or composite). The\n// policy-violations.ndjson writer skips on duplicate ids.\n\nexport interface EvaluationContext {\n // Wall-clock provider. Tests pin this; production uses Date.now.\n now: () => number\n}\n\ninterface RuleEvaluatorArgs<T extends PolicyRule = PolicyRule> {\n graph: NeatGraph\n policy: Policy\n rule: T\n ctx: EvaluationContext\n}\n\ntype RuleEvaluator<T extends PolicyRule = PolicyRule> = (\n args: RuleEvaluatorArgs<T>,\n) => PolicyViolation[]\n\n// Severity-driven default action per ADR-044.\nconst DEFAULT_ACTION_BY_SEVERITY: Record<PolicySeverity, PolicyAction> = {\n info: 'log',\n warning: 'alert',\n error: 'alert',\n critical: 'block',\n}\n\nexport function resolveOnViolation(policy: Policy): PolicyAction {\n return policy.onViolation ?? DEFAULT_ACTION_BY_SEVERITY[policy.severity]\n}\n\nfunction makeViolation(\n policy: Policy,\n rule: PolicyRule,\n contextSuffix: string,\n message: string,\n subject: PolicyViolation['subject'],\n ctx: EvaluationContext,\n): PolicyViolation {\n return {\n id: `${policy.id}:${contextSuffix}`,\n policyId: policy.id,\n policyName: policy.name,\n severity: policy.severity,\n onViolation: resolveOnViolation(policy),\n ruleType: rule.type,\n subject,\n message,\n observedAt: new Date(ctx.now()).toISOString(),\n }\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Per-rule-type evaluators\n// ──────────────────────────────────────────────────────────────────────────\n\nconst evaluateStructural: RuleEvaluator<Extract<PolicyRule, { type: 'structural' }>> = ({\n graph,\n policy,\n rule,\n ctx,\n}) => {\n const violations: PolicyViolation[] = []\n graph.forEachNode((id, attrs) => {\n const a = attrs as GraphNode\n if (a.type !== rule.fromNodeType) return\n let satisfied = false\n for (const edgeId of graph.outboundEdges(id)) {\n const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (e.type !== rule.edgeType) continue\n if (e.provenance === Provenance.FRONTIER) continue\n const target = graph.getNodeAttributes(e.target) as GraphNode\n if (target.type === rule.toNodeType) {\n satisfied = true\n break\n }\n }\n if (!satisfied) {\n violations.push(\n makeViolation(\n policy,\n rule,\n id,\n `${rule.fromNodeType} ${id} has no ${rule.edgeType} edge to a ${rule.toNodeType}`,\n { nodeId: id },\n ctx,\n ),\n )\n }\n })\n return violations\n}\n\nconst evaluateOwnership: RuleEvaluator<Extract<PolicyRule, { type: 'ownership' }>> = ({\n graph,\n policy,\n rule,\n ctx,\n}) => {\n const violations: PolicyViolation[] = []\n graph.forEachNode((id, attrs) => {\n const a = attrs as GraphNode & Record<string, unknown>\n if (a.type !== rule.nodeType) return\n const value = a[rule.field]\n if (typeof value !== 'string' || value.length === 0) {\n violations.push(\n makeViolation(\n policy,\n rule,\n id,\n `${rule.nodeType} ${id} is missing required field \"${rule.field}\"`,\n { nodeId: id },\n ctx,\n ),\n )\n }\n })\n return violations\n}\n\nconst evaluateProvenance: RuleEvaluator<Extract<PolicyRule, { type: 'provenance' }>> = ({\n graph,\n policy,\n rule,\n ctx,\n}) => {\n const required = Array.isArray(rule.required) ? new Set(rule.required) : new Set([rule.required])\n const violations: PolicyViolation[] = []\n graph.forEachEdge((edgeId, attrs) => {\n const e = attrs as GraphEdge\n if (e.type !== rule.edgeType) return\n if (rule.targetNodeId && e.target !== rule.targetNodeId) return\n if (!required.has(e.provenance)) {\n const requiredList = [...required].join(' | ')\n violations.push(\n makeViolation(\n policy,\n rule,\n edgeId,\n `${rule.edgeType} edge ${edgeId} has provenance ${e.provenance}; required ${requiredList}`,\n { edgeId },\n ctx,\n ),\n )\n }\n })\n return violations\n}\n\nconst evaluateBlastRadius: RuleEvaluator<Extract<PolicyRule, { type: 'blast-radius' }>> = ({\n graph,\n policy,\n rule,\n ctx,\n}) => {\n const violations: PolicyViolation[] = []\n const depth = rule.depth\n graph.forEachNode((id, attrs) => {\n const a = attrs as GraphNode\n if (a.type !== rule.nodeType) return\n const result = depth !== undefined ? getBlastRadius(graph, id, depth) : getBlastRadius(graph, id)\n if (result.totalAffected > rule.maxAffected) {\n violations.push(\n makeViolation(\n policy,\n rule,\n id,\n `${rule.nodeType} ${id} has blast radius ${result.totalAffected} > ${rule.maxAffected}`,\n { nodeId: id, path: [id] },\n ctx,\n ),\n )\n }\n })\n return violations\n}\n\nconst evaluateCompatibility: RuleEvaluator<Extract<PolicyRule, { type: 'compatibility' }>> = ({\n graph,\n policy,\n rule,\n ctx,\n}) => {\n const violations: PolicyViolation[] = []\n // Iterate every ServiceNode and re-run the compat shapes the static\n // extractor runs at extract time. Catches OBSERVED-vs-EXTRACTED divergence:\n // a service whose dep manifest changed since the last extract gets re-flagged\n // here on every evaluation cycle.\n const wantsKind = (kind: NonNullable<typeof rule.kind>): boolean =>\n rule.kind === undefined || rule.kind === kind\n\n graph.forEachNode((svcId, attrs) => {\n const a = attrs as GraphNode\n if (a.type !== NodeType.ServiceNode) return\n const svc = a as ServiceNode\n const deps = svc.dependencies ?? {}\n\n if (wantsKind('driver-engine')) {\n // Walk every CONNECTS_TO edge from this service to a DatabaseNode,\n // then run the driver-engine compat for each (driver, declared, engine,\n // engineVersion) tuple.\n for (const edgeId of graph.outboundEdges(svcId)) {\n const e = graph.getEdgeAttributes(edgeId) as GraphEdge\n if (e.type !== EdgeType.CONNECTS_TO) continue\n if (e.provenance === Provenance.FRONTIER) continue\n const dbAttrs = graph.getNodeAttributes(e.target) as GraphNode\n if (dbAttrs.type !== NodeType.DatabaseNode) continue\n const db = dbAttrs as { engine: string; engineVersion: string }\n for (const pair of compatPairs()) {\n if (pair.engine !== db.engine) continue\n const declared = deps[pair.driver]\n if (!declared) continue\n const result = checkCompatibility(pair.driver, declared, db.engine, db.engineVersion)\n if (!result.compatible && result.reason) {\n violations.push(\n makeViolation(\n policy,\n rule,\n `${svcId}:driver-engine:${pair.driver}@${declared}:${db.engine}@${db.engineVersion}`,\n result.reason,\n { nodeId: svcId, edgeId },\n ctx,\n ),\n )\n }\n }\n }\n }\n\n if (wantsKind('node-engine')) {\n const serviceNodeRange = svc.nodeEngine\n for (const constraint of nodeEngineConstraints()) {\n const declared = deps[constraint.package]\n if (!declared) continue\n const result = checkNodeEngineConstraint(constraint, declared, serviceNodeRange)\n if (!result.compatible && result.reason) {\n violations.push(\n makeViolation(\n policy,\n rule,\n `${svcId}:node-engine:${constraint.package}@${declared}`,\n result.reason,\n { nodeId: svcId },\n ctx,\n ),\n )\n }\n }\n }\n\n if (wantsKind('package-conflict')) {\n for (const conflict of packageConflicts()) {\n const declared = deps[conflict.package]\n if (!declared) continue\n const requiredDeclared = deps[conflict.requires.name]\n const result = checkPackageConflict(conflict, declared, requiredDeclared)\n if (!result.compatible && result.reason) {\n violations.push(\n makeViolation(\n policy,\n rule,\n `${svcId}:package-conflict:${conflict.package}@${declared}`,\n result.reason,\n { nodeId: svcId },\n ctx,\n ),\n )\n }\n }\n }\n\n if (wantsKind('deprecated-api')) {\n for (const dep of deprecatedApis()) {\n const declared = deps[dep.package]\n if (!declared) continue\n const result = checkDeprecatedApi(dep, declared)\n if (!result.compatible && result.reason) {\n violations.push(\n makeViolation(\n policy,\n rule,\n `${svcId}:deprecated-api:${dep.package}@${declared}`,\n result.reason,\n { nodeId: svcId },\n ctx,\n ),\n )\n }\n }\n }\n })\n\n return violations\n}\n\nconst policyEvaluators: { [K in PolicyRule['type']]: RuleEvaluator<Extract<PolicyRule, { type: K }>> } = {\n structural: evaluateStructural,\n ownership: evaluateOwnership,\n provenance: evaluateProvenance,\n 'blast-radius': evaluateBlastRadius,\n compatibility: evaluateCompatibility,\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Public entry point\n// ──────────────────────────────────────────────────────────────────────────\n\n// Block-action gating for FrontierNode promotion (ADR-044 §block, MVP scope).\n// Runs the policy evaluator and returns the subset of block-action violations\n// that mention the candidate FrontierNode. Callers (ingest.ts\n// promoteFrontierNodes) check `allowed` before rewiring; when false, the\n// promotion is skipped and the violations surface through the standard\n// policy-violations.ndjson channel.\n//\n// Block scope is tightly bounded per the contract: FrontierNode promotion\n// only. Other gating points (deploy, codemod, OTel auto-create) need their\n// own ADRs before this function expands.\nexport function canPromoteFrontier(\n graph: NeatGraph,\n frontierId: string,\n policies: Policy[],\n ctx: EvaluationContext,\n): { allowed: boolean; violations: PolicyViolation[] } {\n if (policies.length === 0) return { allowed: true, violations: [] }\n const all = evaluateAllPolicies(graph, policies, ctx)\n const blocking = all.filter((v) => {\n if (v.onViolation !== 'block') return false\n return (\n v.subject.nodeId === frontierId ||\n v.subject.path?.includes(frontierId) === true\n )\n })\n return { allowed: blocking.length === 0, violations: blocking }\n}\n\nexport function evaluateAllPolicies(\n graph: NeatGraph,\n policies: Policy[],\n ctx: EvaluationContext,\n): PolicyViolation[] {\n const out: PolicyViolation[] = []\n for (const policy of policies) {\n const evaluator = policyEvaluators[policy.rule.type] as RuleEvaluator\n const violations = evaluator({ graph, policy, rule: policy.rule, ctx })\n for (const v of violations) out.push(v)\n }\n return out\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Loader\n// ──────────────────────────────────────────────────────────────────────────\n\n// Reads <projectRoot>/policy.json. Returns [] when the file doesn't exist —\n// a project without policies is a perfectly fine state. Failures to parse\n// throw with the Zod error so the daemon surfaces malformed files loudly\n// instead of silently dropping rules.\nexport async function loadPolicyFile(policyPath: string): Promise<Policy[]> {\n let raw: string\n try {\n raw = await fs.readFile(policyPath, 'utf8')\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw err\n }\n const json = JSON.parse(raw) as unknown\n const file: PolicyFile = PolicyFileSchema.parse(json)\n return file.policies\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Append-only ndjson writer with id-based dedup\n// ──────────────────────────────────────────────────────────────────────────\n\n// Keeps an in-memory Set of seen violation ids so re-evaluation cycles don't\n// produce duplicate ndjson lines. The set hydrates from disk on first append\n// — startups that load an existing log don't lose dedup state.\nexport class PolicyViolationsLog {\n private readonly path: string\n private seen: Set<string> | null = null\n\n constructor(logPath: string) {\n this.path = logPath\n }\n\n async append(v: PolicyViolation): Promise<boolean> {\n if (!this.seen) await this.hydrate()\n if (this.seen!.has(v.id)) return false\n this.seen!.add(v.id)\n await fs.mkdir(path.dirname(this.path), { recursive: true })\n await fs.appendFile(this.path, JSON.stringify(v) + '\\n', 'utf8')\n return true\n }\n\n async readAll(): Promise<PolicyViolation[]> {\n try {\n const raw = await fs.readFile(this.path, 'utf8')\n return raw\n .split('\\n')\n .filter(Boolean)\n .map((line) => JSON.parse(line) as PolicyViolation)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return []\n throw err\n }\n }\n\n private async hydrate(): Promise<void> {\n this.seen = new Set()\n const existing = await this.readAll()\n for (const v of existing) this.seen.add(v.id)\n }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport ignore, { type Ignore } from 'ignore'\nimport { minimatch } from 'minimatch'\nimport type { ServiceNode } from '@neat.is/types'\nimport { NodeType, serviceId } from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport {\n IGNORED_DIRS,\n exists,\n readJson,\n type DiscoveredService,\n type PackageJson,\n} from './shared.js'\nimport { discoverPythonService, pythonToPackage } from './python.js'\n\nconst DEFAULT_SCAN_DEPTH = 5\n\ninterface RootPackageJson extends PackageJson {\n workspaces?: string[] | { packages?: string[] }\n}\n\nfunction parseScanDepth(): number {\n const raw = process.env.NEAT_SCAN_DEPTH\n if (!raw) return DEFAULT_SCAN_DEPTH\n const n = Number.parseInt(raw, 10)\n return Number.isFinite(n) && n >= 0 ? n : DEFAULT_SCAN_DEPTH\n}\n\nfunction workspaceGlobs(pkg: RootPackageJson): string[] | null {\n const ws = pkg.workspaces\n if (!ws) return null\n if (Array.isArray(ws)) return ws.length > 0 ? ws : null\n if (Array.isArray(ws.packages)) return ws.packages.length > 0 ? ws.packages : null\n return null\n}\n\nasync function loadGitignore(scanPath: string): Promise<Ignore | null> {\n const gitignorePath = path.join(scanPath, '.gitignore')\n if (!(await exists(gitignorePath))) return null\n const raw = await fs.readFile(gitignorePath, 'utf8')\n return ignore().add(raw)\n}\n\ninterface WalkOptions {\n maxDepth: number\n ig: Ignore | null\n}\n\nasync function walkDirs(\n start: string,\n scanPath: string,\n options: WalkOptions,\n visit: (dir: string) => Promise<void> | void,\n): Promise<void> {\n async function recurse(current: string, depth: number): Promise<void> {\n if (depth > options.maxDepth) return\n const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => [])\n for (const entry of entries) {\n if (!entry.isDirectory()) continue\n if (IGNORED_DIRS.has(entry.name)) continue\n const child = path.join(current, entry.name)\n if (options.ig) {\n const rel = path.relative(scanPath, child).split(path.sep).join('/')\n // Trailing slash so `ignore` evaluates the entry as a directory; without\n // it, gitignore patterns like `dist/` won't match because the lib\n // distinguishes file vs. directory tests.\n if (rel && options.ig.ignores(rel + '/')) continue\n }\n await visit(child)\n await recurse(child, depth + 1)\n }\n }\n await recurse(start, 0)\n}\n\nasync function expandWorkspaceGlobs(\n scanPath: string,\n globs: string[],\n): Promise<string[]> {\n const found = new Set<string>()\n const scanDepth = parseScanDepth()\n\n for (const raw of globs) {\n const pattern = raw.replace(/^\\.\\//, '')\n\n if (!pattern.includes('*')) {\n const candidate = path.join(scanPath, pattern)\n if (await exists(path.join(candidate, 'package.json'))) found.add(candidate)\n continue\n }\n\n const segments = pattern.split('/')\n const staticSegments: string[] = []\n for (const seg of segments) {\n if (seg.includes('*')) break\n staticSegments.push(seg)\n }\n const start = path.join(scanPath, ...staticSegments)\n if (!(await exists(start))) continue\n\n const hasDoubleStar = pattern.includes('**')\n const walkDepth = hasDoubleStar\n ? scanDepth\n : Math.max(0, segments.length - staticSegments.length - 1)\n\n await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {\n const rel = path.relative(scanPath, dir).split(path.sep).join('/')\n if (minimatch(rel, pattern) && (await exists(path.join(dir, 'package.json')))) {\n found.add(dir)\n }\n })\n }\n\n return [...found]\n}\n\nasync function discoverNodeService(\n scanPath: string,\n dir: string,\n): Promise<DiscoveredService | null> {\n const pkgPath = path.join(dir, 'package.json')\n if (!(await exists(pkgPath))) return null\n const pkg = await readJson<PackageJson>(pkgPath)\n if (!pkg.name) return null\n const node: ServiceNode = {\n id: serviceId(pkg.name),\n type: NodeType.ServiceNode,\n name: pkg.name,\n language: 'javascript',\n version: pkg.version,\n dependencies: pkg.dependencies ?? {},\n repoPath: path.relative(scanPath, dir),\n ...(pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {}),\n }\n return { pkg, dir, node }\n}\n\nasync function discoverPyService(\n scanPath: string,\n dir: string,\n): Promise<DiscoveredService | null> {\n const py = await discoverPythonService(dir)\n if (!py) return null\n const pkg = pythonToPackage(py)\n const node: ServiceNode = {\n id: serviceId(py.name),\n type: NodeType.ServiceNode,\n name: py.name,\n language: 'python',\n version: py.version,\n dependencies: py.dependencies,\n repoPath: path.relative(scanPath, dir),\n }\n return { pkg, dir, node }\n}\n\n// Phase 1 — discover service directories under scanPath. A service is any\n// directory containing a JS/TS manifest (`package.json`) or a Python manifest\n// (`pyproject.toml` / `requirements.txt` / `setup.py`). JS wins on tie.\n//\n// If the root `package.json` declares `workspaces`, those globs are\n// authoritative — we don't fall back to a free recursive walk. Otherwise we\n// walk recursively, depth-bounded by `NEAT_SCAN_DEPTH` (default 5), skipping\n// `IGNORED_DIRS` and anything matched by the root `.gitignore`.\n//\n// Two manifests sharing a `name` collapse to one node per ADR-010; the\n// duplicate logs a warning naming both paths.\nexport async function discoverServices(scanPath: string): Promise<DiscoveredService[]> {\n const rootPkgPath = path.join(scanPath, 'package.json')\n const rootPkg = (await exists(rootPkgPath))\n ? await readJson<RootPackageJson>(rootPkgPath)\n : null\n const wsGlobs = rootPkg ? workspaceGlobs(rootPkg) : null\n\n const candidateDirs: string[] = []\n if (wsGlobs) {\n candidateDirs.push(...(await expandWorkspaceGlobs(scanPath, wsGlobs)))\n } else {\n if (rootPkg && rootPkg.name) candidateDirs.push(scanPath)\n const ig = await loadGitignore(scanPath)\n await walkDirs(\n scanPath,\n scanPath,\n { maxDepth: parseScanDepth(), ig },\n async (dir) => {\n if (await exists(path.join(dir, 'package.json'))) {\n candidateDirs.push(dir)\n } else if (\n (await exists(path.join(dir, 'pyproject.toml'))) ||\n (await exists(path.join(dir, 'requirements.txt'))) ||\n (await exists(path.join(dir, 'setup.py')))\n ) {\n candidateDirs.push(dir)\n }\n },\n )\n }\n\n candidateDirs.sort()\n\n const seen = new Map<string, string>()\n const out: DiscoveredService[] = []\n for (const dir of candidateDirs) {\n const service =\n (await discoverNodeService(scanPath, dir)) ??\n (await discoverPyService(scanPath, dir))\n if (!service) continue\n\n const existingDir = seen.get(service.node.name)\n if (existingDir !== undefined) {\n const a = path.relative(scanPath, existingDir) || '.'\n const b = path.relative(scanPath, dir) || '.'\n console.warn(\n `[neat] duplicate package name \"${service.node.name}\" — keeping ${a}, ignoring ${b}`,\n )\n continue\n }\n seen.set(service.node.name, dir)\n out.push(service)\n }\n return out\n}\n\nexport function addServiceNodes(graph: NeatGraph, services: DiscoveredService[]): number {\n let nodesAdded = 0\n for (const service of services) {\n if (!graph.hasNode(service.node.id)) {\n graph.addNode(service.node.id, { ...service.node, discoveredVia: 'static' })\n nodesAdded++\n continue\n }\n // OTel ingest may have auto-created a minimal node at this id. Merge per\n // ADR-033 / identity contract: static fields override OTel-derived fields,\n // and discoveredVia flips to 'merged' when both layers contributed.\n const existing = graph.getNodeAttributes(service.node.id) as ServiceNode\n const mergedDiscoveredVia: 'static' | 'otel' | 'merged' =\n existing.discoveredVia === 'otel' ? 'merged' : 'static'\n graph.replaceNodeAttributes(service.node.id, {\n ...existing,\n ...service.node,\n discoveredVia: mergedDiscoveredVia,\n })\n }\n return nodesAdded\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { parse as parseYaml } from 'yaml'\nimport type { ServiceNode } from '@neat.is/types'\n\nexport interface PackageJson {\n name: string\n version?: string\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n engines?: { node?: string }\n}\n\nexport interface DiscoveredService {\n pkg: PackageJson\n dir: string\n node: ServiceNode\n}\n\nexport const SERVICE_FILE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.py'])\nexport const CONFIG_FILE_EXTENSIONS = new Set(['.yaml', '.yml'])\nexport const IGNORED_DIRS = new Set([\n 'node_modules',\n '.git',\n '.turbo',\n 'dist',\n 'build',\n '.next',\n])\n\nexport function isConfigFile(name: string): { match: boolean; fileType: string } {\n const ext = path.extname(name)\n if (CONFIG_FILE_EXTENSIONS.has(ext)) return { match: true, fileType: ext.slice(1) }\n // .env, .env.local, .env.production. Bare filename or any dotted-suffix\n // variant; folder names get filtered upstream by walking files only.\n if (name === '.env' || name.startsWith('.env.')) return { match: true, fileType: 'env' }\n return { match: false, fileType: '' }\n}\n\n// Strip semver range prefixes (^, ~, >=, etc.) and bare \"v\" so the extracted\n// version is usable for compat checks. We don't try to resolve ranges to actual\n// installed versions — that's a published-lockfile concern, not extraction's job.\nexport function cleanVersion(raw: string | undefined): string | undefined {\n if (!raw) return undefined\n return raw.replace(/^[\\^~><=v\\s]+/, '').trim() || undefined\n}\n\nexport async function readJson<T>(filePath: string): Promise<T> {\n const raw = await fs.readFile(filePath, 'utf8')\n return JSON.parse(raw) as T\n}\n\nexport async function readYaml<T>(filePath: string): Promise<T> {\n const raw = await fs.readFile(filePath, 'utf8')\n return parseYaml(raw) as T\n}\n\nexport async function exists(p: string): Promise<boolean> {\n try {\n await fs.access(p)\n return true\n } catch {\n return false\n }\n}\n\n// Thin re-export so existing callers (calls/, configs.ts, databases/, infra/)\n// keep their import path. Wire format lives in @neat.is/types/identity.ts per\n// ADR-029.\nexport { extractedEdgeId as makeEdgeId } from '@neat.is/types'\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { parse as parseToml } from 'smol-toml'\nimport { exists, type PackageJson } from './shared.js'\n\n// Lines like `psycopg2==2.7.0`, `psycopg2 == 2.7.0`, `psycopg2[extras]==2.7`,\n// or `psycopg2~=2.7,<3`. We capture the package name and the first version\n// that follows an `==` operator. Anything else (range, no pin, no version) is\n// recorded with an empty version — the compat matrix's semver coercer treats\n// those as \"can't reason\" and under-flags rather than over-flags.\nconst REQUIREMENT_LINE = /^\\s*([A-Za-z0-9_.-]+)(?:\\[[^\\]]*\\])?\\s*(?:(==)\\s*([A-Za-z0-9_.+-]+))?/\n\nfunction parseRequirementsTxt(content: string): Record<string, string> {\n const out: Record<string, string> = {}\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.split('#')[0]?.trim()\n if (!line) continue\n if (line.startsWith('-')) continue // -r requirements-dev.txt etc.\n const match = REQUIREMENT_LINE.exec(line)\n if (!match) continue\n const name = match[1]!.toLowerCase()\n const version = match[3] ?? ''\n out[name] = version\n }\n return out\n}\n\ninterface PyProjectFile {\n project?: {\n name?: string\n version?: string\n dependencies?: string[]\n }\n tool?: {\n poetry?: {\n name?: string\n version?: string\n dependencies?: Record<string, string | { version?: string }>\n }\n }\n}\n\nfunction depsFromPyProject(pyproject: PyProjectFile): Record<string, string> {\n const out: Record<string, string> = {}\n\n // PEP 621 — [project] dependencies = [\"psycopg2==2.7.0\", \"requests\"]\n for (const entry of pyproject.project?.dependencies ?? []) {\n const match = REQUIREMENT_LINE.exec(entry)\n if (!match) continue\n out[match[1]!.toLowerCase()] = match[3] ?? ''\n }\n\n // Poetry — [tool.poetry.dependencies] psycopg2 = \"2.7.0\"\n const poetryDeps = pyproject.tool?.poetry?.dependencies ?? {}\n for (const [name, value] of Object.entries(poetryDeps)) {\n if (name.toLowerCase() === 'python') continue\n const raw = typeof value === 'string' ? value : (value?.version ?? '')\n out[name.toLowerCase()] = raw.replace(/^[\\^~><=v\\s]+/, '')\n }\n return out\n}\n\nexport interface PythonService {\n name: string\n version?: string\n dependencies: Record<string, string>\n}\n\n// Detect a Python service by the conventional manifest files. We try\n// pyproject.toml first because it can name the package; fallback to the\n// directory name when only requirements.txt or setup.py is present.\nexport async function discoverPythonService(serviceDir: string): Promise<PythonService | null> {\n const pyprojectPath = path.join(serviceDir, 'pyproject.toml')\n const requirementsPath = path.join(serviceDir, 'requirements.txt')\n const setupPath = path.join(serviceDir, 'setup.py')\n\n const hasPyproject = await exists(pyprojectPath)\n const hasRequirements = await exists(requirementsPath)\n const hasSetup = await exists(setupPath)\n if (!hasPyproject && !hasRequirements && !hasSetup) return null\n\n let name = path.basename(serviceDir)\n let version: string | undefined\n const dependencies: Record<string, string> = {}\n\n if (hasPyproject) {\n const raw = await fs.readFile(pyprojectPath, 'utf8')\n const pyproject = parseToml(raw) as PyProjectFile\n name = pyproject.project?.name ?? pyproject.tool?.poetry?.name ?? name\n version = pyproject.project?.version ?? pyproject.tool?.poetry?.version ?? undefined\n Object.assign(dependencies, depsFromPyProject(pyproject))\n }\n\n if (hasRequirements) {\n const raw = await fs.readFile(requirementsPath, 'utf8')\n Object.assign(dependencies, parseRequirementsTxt(raw))\n }\n\n return { name, version, dependencies }\n}\n\n// Build the same `pkg`-shaped shim the JS path uses so downstream phases\n// (databases, calls, etc.) can keep reading service.pkg.dependencies and\n// service.pkg.name without caring which language produced the service.\nexport function pythonToPackage(service: PythonService): PackageJson {\n return {\n name: service.name,\n version: service.version,\n dependencies: service.dependencies,\n }\n}\n","import path from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport { parseAllDocuments } from 'yaml'\nimport type { ServiceNode } from '@neat.is/types'\nimport { NodeType } from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport {\n CONFIG_FILE_EXTENSIONS,\n IGNORED_DIRS,\n exists,\n readYaml,\n type DiscoveredService,\n} from './shared.js'\n\n// Populate ServiceNode.aliases from sources that the OTel layer is likely to\n// see in span attributes:\n//\n// - docker-compose service names (compose-DNS).\n// - Dockerfile LABEL values that name the service.\n// - k8s metadata.name (and the cluster-DNS variants) for Service /\n// Deployment / StatefulSet whose name matches a known service.\n//\n// resolveServiceId in ingest.ts checks aliases before falling back to a\n// FRONTIER placeholder; promoteFrontierNodes uses them to retire stale\n// placeholders once they map to a real service.\n\ninterface ComposeService {\n container_name?: string\n hostname?: string\n networks?: string[] | Record<string, unknown>\n}\n\ninterface ComposeFile {\n services?: Record<string, ComposeService>\n}\n\ninterface K8sDoc {\n kind?: string\n metadata?: {\n name?: string\n namespace?: string\n labels?: Record<string, string>\n }\n spec?: {\n selector?: {\n app?: string\n matchLabels?: Record<string, string>\n }\n }\n}\n\nconst K8S_KINDS_WITH_HOSTNAMES = new Set([\n 'Service',\n 'Deployment',\n 'StatefulSet',\n 'DaemonSet',\n])\n\nfunction addAliases(graph: NeatGraph, serviceId: string, candidates: Iterable<string>): void {\n if (!graph.hasNode(serviceId)) return\n const node = graph.getNodeAttributes(serviceId) as ServiceNode & { type?: string }\n if (node.type !== NodeType.ServiceNode) return\n const set = new Set(node.aliases ?? [])\n for (const c of candidates) {\n if (!c) continue\n if (c === node.name) continue\n set.add(c)\n }\n if (set.size === 0) return\n const updated: ServiceNode = { ...node, aliases: [...set].sort() }\n graph.replaceNodeAttributes(serviceId, updated)\n}\n\nfunction indexServicesByName(services: DiscoveredService[]): Map<string, string> {\n const map = new Map<string, string>()\n for (const s of services) {\n map.set(s.node.name, s.node.id)\n map.set(path.basename(s.dir), s.node.id)\n }\n return map\n}\n\nasync function collectComposeAliases(\n graph: NeatGraph,\n scanPath: string,\n serviceIndex: Map<string, string>,\n): Promise<void> {\n let composePath: string | null = null\n for (const name of ['docker-compose.yml', 'docker-compose.yaml']) {\n const abs = path.join(scanPath, name)\n if (await exists(abs)) {\n composePath = abs\n break\n }\n }\n if (!composePath) return\n\n const compose = await readYaml<ComposeFile>(composePath)\n if (!compose?.services) return\n\n for (const [composeName, svc] of Object.entries(compose.services)) {\n const serviceId = serviceIndex.get(composeName)\n if (!serviceId) continue\n const aliases = new Set<string>([composeName])\n if (svc.container_name) aliases.add(svc.container_name)\n if (svc.hostname) aliases.add(svc.hostname)\n addAliases(graph, serviceId, aliases)\n }\n}\n\nconst LABEL_KEYS = new Set([\n 'service',\n 'service.name',\n 'app',\n 'app.name',\n 'com.docker.compose.service',\n 'org.opencontainers.image.title',\n])\n\nfunction parseDockerfileLabels(content: string): string[] {\n const out: string[] = []\n // Support `LABEL key=value`, `LABEL key=\"value with spaces\"`, and the\n // multi-pair form `LABEL k1=v1 k2=v2`. We don't try to honour line\n // continuations — the common single-line form is enough.\n const lineRegex = /^\\s*label\\s+(.+)$/i\n for (const raw of content.split('\\n')) {\n const m = lineRegex.exec(raw)\n if (!m) continue\n const rest = m[1]!\n const pairRegex = /([\\w.-]+)\\s*=\\s*(\"([^\"]*)\"|'([^']*)'|([^\\s]+))/g\n let pair: RegExpExecArray | null\n while ((pair = pairRegex.exec(rest)) !== null) {\n const key = pair[1]!.toLowerCase()\n if (!LABEL_KEYS.has(key)) continue\n const value = pair[3] ?? pair[4] ?? pair[5] ?? ''\n if (value) out.push(value)\n }\n }\n return out\n}\n\nasync function collectDockerfileAliases(\n graph: NeatGraph,\n services: DiscoveredService[],\n): Promise<void> {\n for (const service of services) {\n const dockerfilePath = path.join(service.dir, 'Dockerfile')\n if (!(await exists(dockerfilePath))) continue\n const content = await fs.readFile(dockerfilePath, 'utf8')\n const aliases = parseDockerfileLabels(content)\n if (aliases.length > 0) addAliases(graph, service.node.id, aliases)\n }\n}\n\nasync function walkYamlFiles(start: string, depth = 0, max = 5): Promise<string[]> {\n if (depth > max) return []\n const out: string[] = []\n const entries = await fs.readdir(start, { withFileTypes: true }).catch(() => [])\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (IGNORED_DIRS.has(entry.name)) continue\n out.push(...(await walkYamlFiles(path.join(start, entry.name), depth + 1, max)))\n } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path.extname(entry.name))) {\n out.push(path.join(start, entry.name))\n }\n }\n return out\n}\n\nfunction k8sHostnames(name: string, namespace: string | undefined): string[] {\n const ns = namespace ?? 'default'\n return [\n name,\n `${name}.${ns}`,\n `${name}.${ns}.svc`,\n `${name}.${ns}.svc.cluster.local`,\n ]\n}\n\nfunction k8sServiceTarget(\n doc: K8sDoc,\n byName: Map<string, string>,\n): string | null {\n // For `Service` resources, the target is whatever app the spec selects, not\n // necessarily the Service's own metadata.name. We prefer that mapping; if no\n // selector, fall back to the metadata.name match.\n const selector = doc.spec?.selector\n const selectorApp = selector?.app ?? selector?.matchLabels?.app\n if (selectorApp && byName.has(selectorApp)) return byName.get(selectorApp)!\n\n const labelApp = doc.metadata?.labels?.app\n if (labelApp && byName.has(labelApp)) return byName.get(labelApp)!\n\n const metaName = doc.metadata?.name\n if (metaName && byName.has(metaName)) return byName.get(metaName)!\n\n return null\n}\n\nasync function collectK8sAliases(\n graph: NeatGraph,\n scanPath: string,\n serviceIndex: Map<string, string>,\n): Promise<void> {\n const files = await walkYamlFiles(scanPath)\n for (const file of files) {\n const content = await fs.readFile(file, 'utf8')\n let docs: K8sDoc[]\n try {\n docs = parseAllDocuments(content).map((d) => d.toJSON() as K8sDoc)\n } catch {\n continue\n }\n for (const doc of docs) {\n if (!doc?.kind || !doc.metadata?.name) continue\n if (!K8S_KINDS_WITH_HOSTNAMES.has(doc.kind)) continue\n const target = k8sServiceTarget(doc, serviceIndex)\n if (!target) continue\n addAliases(graph, target, k8sHostnames(doc.metadata.name, doc.metadata.namespace))\n }\n }\n}\n\nexport async function addServiceAliases(\n graph: NeatGraph,\n scanPath: string,\n services: DiscoveredService[],\n): Promise<void> {\n const byName = indexServicesByName(services)\n await collectComposeAliases(graph, scanPath, byName)\n await collectDockerfileAliases(graph, services)\n await collectK8sAliases(graph, scanPath, byName)\n}\n","import path from 'node:path'\nimport type {\n CompatibleDriver,\n DatabaseNode,\n GraphEdge,\n GraphNode,\n ServiceNode,\n} from '@neat.is/types'\nimport { EdgeType, NodeType, Provenance, databaseId } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport {\n checkCompatibility,\n checkDeprecatedApi,\n checkNodeEngineConstraint,\n checkPackageConflict,\n compatPairs,\n deprecatedApis,\n nodeEngineConstraints,\n packageConflicts,\n} from '../../compat.js'\nimport { cleanVersion, makeEdgeId, type DiscoveredService } from '../shared.js'\nimport { dbConfigYamlParser } from './db-config-yaml.js'\nimport { dotenvParser } from './dotenv.js'\nimport { prismaParser } from './prisma.js'\nimport { drizzleParser } from './drizzle.js'\nimport { knexParser } from './knex.js'\nimport { ormconfigParser } from './ormconfig.js'\nimport { typeormParser } from './typeorm.js'\nimport { sequelizeParser } from './sequelize.js'\nimport { dockerComposeParser } from './docker-compose.js'\nimport type { DbConfig } from './shared.js'\n\nexport type { DbConfig } from './shared.js'\n\nexport interface DbParser {\n name: string\n parse(serviceDir: string): Promise<DbConfig[]>\n}\n\n// Registry — order is for tie-breaking only (first wins on identical host).\n// db-config.yaml stays first so the canonical demo behaviour matches today's\n// extraction byte-for-byte.\nexport const DB_PARSERS: DbParser[] = [\n dbConfigYamlParser,\n dotenvParser,\n prismaParser,\n drizzleParser,\n knexParser,\n ormconfigParser,\n typeormParser,\n sequelizeParser,\n dockerComposeParser,\n]\n\nfunction compatibleDriversFor(engine: string): CompatibleDriver[] {\n return compatPairs()\n .filter((p) => p.engine === engine)\n .map((p) => ({ name: p.driver, minVersion: p.minDriverVersion }))\n}\n\nfunction toDatabaseNode(config: DbConfig): DatabaseNode {\n return {\n id: databaseId(config.host),\n type: NodeType.DatabaseNode,\n name: config.database || config.host,\n engine: config.engine,\n engineVersion: config.engineVersion,\n compatibleDrivers: compatibleDriversFor(config.engine),\n host: config.host,\n port: config.port,\n }\n}\n\nexport function attachIncompatibilities(\n service: DiscoveredService,\n configs: DbConfig[],\n): void {\n const deps = { ...(service.pkg.dependencies ?? {}), ...(service.pkg.devDependencies ?? {}) }\n const incompatibilities: NonNullable<ServiceNode['incompatibilities']> = []\n const seen = new Set<string>()\n\n // 1. driver-engine — original behaviour. Per (db config, configured driver\n // pair) check that the declared driver version meets the engine threshold.\n for (const config of configs) {\n for (const pair of compatPairs()) {\n if (pair.engine !== config.engine) continue\n const declaredVersion = cleanVersion(deps[pair.driver])\n if (!declaredVersion) continue\n const result = checkCompatibility(\n pair.driver,\n declaredVersion,\n config.engine,\n config.engineVersion,\n )\n if (!result.compatible && result.reason) {\n const key = `driver-engine|${pair.driver}@${declaredVersion}|${config.engine}@${config.engineVersion}`\n if (seen.has(key)) continue\n seen.add(key)\n incompatibilities.push({\n kind: 'driver-engine',\n driver: pair.driver,\n driverVersion: declaredVersion,\n engine: config.engine,\n engineVersion: config.engineVersion,\n reason: result.reason,\n })\n }\n }\n }\n\n // 2. node-engine — service's `engines.node` vs each declared dep that has a\n // matrix-recorded minimum.\n const serviceNodeEngine = service.node.nodeEngine ?? service.pkg.engines?.node\n for (const constraint of nodeEngineConstraints()) {\n const declared = cleanVersion(deps[constraint.package])\n if (!declared) continue\n const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine)\n if (!result.compatible && result.reason) {\n const key = `node-engine|${constraint.package}@${declared}|${serviceNodeEngine ?? ''}`\n if (seen.has(key)) continue\n seen.add(key)\n incompatibilities.push({\n kind: 'node-engine',\n package: constraint.package,\n packageVersion: declared,\n requiredNodeVersion: result.requiredNodeVersion ?? constraint.minNodeVersion,\n ...(serviceNodeEngine ? { declaredNodeEngine: serviceNodeEngine } : {}),\n reason: result.reason,\n })\n }\n }\n\n // 3. package-conflict — pair like react-query 5+ requiring react 18+.\n for (const conflict of packageConflicts()) {\n const declared = cleanVersion(deps[conflict.package])\n if (!declared) continue\n const requiredVersion = cleanVersion(deps[conflict.requires.name])\n const result = checkPackageConflict(conflict, declared, requiredVersion)\n if (!result.compatible && result.reason) {\n const key = `package-conflict|${conflict.package}@${declared}|${conflict.requires.name}@${requiredVersion ?? 'missing'}`\n if (seen.has(key)) continue\n seen.add(key)\n incompatibilities.push({\n kind: 'package-conflict',\n package: conflict.package,\n packageVersion: declared,\n requires: conflict.requires,\n ...(requiredVersion ? { foundVersion: requiredVersion } : {}),\n reason: result.reason,\n })\n }\n }\n\n // 4. deprecated-api — flag presence of a known-deprecated package.\n for (const rule of deprecatedApis()) {\n const declared = cleanVersion(deps[rule.package])\n if (declared === undefined) continue\n const result = checkDeprecatedApi(rule, declared)\n if (!result.compatible && result.reason) {\n const key = `deprecated-api|${rule.package}@${declared}`\n if (seen.has(key)) continue\n seen.add(key)\n incompatibilities.push({\n kind: 'deprecated-api',\n package: rule.package,\n packageVersion: declared,\n reason: result.reason,\n })\n }\n }\n\n if (incompatibilities.length > 0) service.node.incompatibilities = incompatibilities\n}\n\n// Phase 2 — for each service, run every parser and merge their DbConfigs by\n// host. Each unique host produces one DatabaseNode + CONNECTS_TO edge from the\n// service. The parser registry decides priority on tie; the demo's\n// db-config.yaml stays first so its `engineVersion: 15` continues to win.\nexport async function addDatabasesAndCompat(\n graph: NeatGraph,\n services: DiscoveredService[],\n scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n let nodesAdded = 0\n let edgesAdded = 0\n\n for (const service of services) {\n const merged = new Map<string, DbConfig>()\n for (const parser of DB_PARSERS) {\n let configs: DbConfig[]\n try {\n configs = await parser.parse(service.dir)\n } catch (err) {\n console.warn(\n `[neat] ${parser.name} parser failed on ${service.node.name}: ${(err as Error).message}`,\n )\n continue\n }\n for (const config of configs) {\n if (!config.host) continue\n if (!merged.has(config.host)) merged.set(config.host, config)\n }\n }\n\n const allConfigs = [...merged.values()]\n for (const config of allConfigs) {\n const dbNode = toDatabaseNode(config)\n if (!graph.hasNode(dbNode.id)) {\n graph.addNode(dbNode.id, { ...dbNode, discoveredVia: 'static' })\n nodesAdded++\n } else {\n // OTel ingest may have auto-created a minimal node at this id. Merge\n // per ADR-033: static fields override OTel-derived fields, discoveredVia\n // flips to 'merged' when both layers contributed.\n const existing = graph.getNodeAttributes(dbNode.id) as DatabaseNode\n const mergedDiscoveredVia: 'static' | 'otel' | 'merged' =\n existing.discoveredVia === 'otel' ? 'merged' : 'static'\n graph.replaceNodeAttributes(dbNode.id, {\n ...existing,\n ...dbNode,\n discoveredVia: mergedDiscoveredVia,\n })\n }\n const edge: GraphEdge = {\n id: makeEdgeId(service.node.id, dbNode.id, EdgeType.CONNECTS_TO),\n source: service.node.id,\n target: dbNode.id,\n type: EdgeType.CONNECTS_TO,\n provenance: Provenance.EXTRACTED,\n ...(config.sourceFile\n ? {\n evidence: {\n file: path.relative(scanPath, config.sourceFile).split(path.sep).join('/'),\n },\n }\n : {}),\n }\n if (!graph.hasEdge(edge.id)) {\n graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge)\n edgesAdded++\n }\n }\n\n // Run all kinds of incompat checks even for services with no db connection\n // — node-engine / package-conflict / deprecated-api don't depend on db.\n attachIncompatibilities(service, allConfigs)\n if (graph.hasNode(service.node.id)) {\n // Merge with whatever's on the node already (aliases from γ #75 land\n // before this phase), so the writeback doesn't drop fields populated by\n // earlier passes.\n const current = graph.getNodeAttributes(service.node.id) as ServiceNode\n const updated: ServiceNode = {\n ...current,\n ...(service.node as ServiceNode),\n ...(current.aliases ? { aliases: current.aliases } : {}),\n }\n // attachIncompatibilities only sets the field when there's something to\n // flag. On a re-extract (`neat watch`, `POST /graph/scan`), a stale\n // entry on `current` would otherwise survive the spread and leave the\n // graph reporting a problem the new manifest no longer has.\n if (!service.node.incompatibilities || service.node.incompatibilities.length === 0) {\n delete (updated as { incompatibilities?: unknown }).incompatibilities\n }\n graph.replaceNodeAttributes(service.node.id, updated as unknown as GraphNode)\n }\n }\n\n return { nodesAdded, edgesAdded }\n}\n","import path from 'node:path'\nimport { exists, readYaml } from '../shared.js'\nimport type { DbConfig } from './shared.js'\n\ninterface DbConfigYaml {\n host: string\n port?: number\n database: string\n engine: string\n engineVersion?: string | number\n}\n\n// The original db-config.yaml format, kept as a parser so the demo continues\n// to work. Engine + version are explicit here, so this is the one source that\n// can produce a real engineVersion without inference.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const yamlPath = path.join(serviceDir, 'db-config.yaml')\n if (!(await exists(yamlPath))) return []\n const raw = await readYaml<DbConfigYaml>(yamlPath)\n return [\n {\n host: raw.host,\n port: raw.port,\n database: raw.database,\n engine: raw.engine,\n engineVersion: raw.engineVersion !== undefined ? String(raw.engineVersion) : 'unknown',\n sourceFile: yamlPath,\n },\n ]\n}\n\nexport const dbConfigYamlParser = { name: 'db-config.yaml', parse }\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { isConfigFile } from '../shared.js'\nimport { parseConnectionString, type DbConfig } from './shared.js'\n\nconst CONNECTION_KEYS = new Set([\n 'DATABASE_URL',\n 'DB_URL',\n 'POSTGRES_URL',\n 'POSTGRESQL_URL',\n 'MYSQL_URL',\n 'MONGODB_URI',\n 'MONGO_URL',\n 'MONGO_URI',\n 'REDIS_URL',\n])\n\n// Per ADR-016, .env contents do not land in any snapshot. We read them here\n// only to derive a transient DbConfig — the value never reaches a ConfigNode.\nfunction parseDotenvLine(line: string): { key: string; value: string } | null {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#')) return null\n const eq = trimmed.indexOf('=')\n if (eq < 0) return null\n const key = trimmed.slice(0, eq).trim()\n let value = trimmed.slice(eq + 1).trim()\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1)\n }\n return { key, value }\n}\n\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const entries = await fs.readdir(serviceDir, { withFileTypes: true }).catch(() => [])\n const configs: DbConfig[] = []\n const seen = new Set<string>()\n\n for (const entry of entries) {\n if (!entry.isFile()) continue\n const match = isConfigFile(entry.name)\n if (!match.match || match.fileType !== 'env') continue\n\n const filePath = path.join(serviceDir, entry.name)\n const content = await fs.readFile(filePath, 'utf8')\n for (const line of content.split('\\n')) {\n const parsed = parseDotenvLine(line)\n if (!parsed) continue\n if (!CONNECTION_KEYS.has(parsed.key.toUpperCase())) continue\n const config = parseConnectionString(parsed.value)\n if (!config) continue\n const key = `${config.engine}://${config.host}:${config.port ?? ''}/${config.database}`\n if (seen.has(key)) continue\n seen.add(key)\n configs.push({ ...config, sourceFile: filePath })\n }\n }\n return configs\n}\n\nexport const dotenvParser = { name: '.env', parse }\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface DbConfig {\n host: string\n port?: number\n database: string\n engine: string\n engineVersion: string // \"unknown\" when not statically determinable\n // Absolute path to the file the parser read this config from. Used to\n // populate evidence.file on the resulting CONNECTS_TO edge. Optional so\n // synthesized configs (e.g. prisma's env() fallback) can omit it; the\n // CONNECTS_TO writer emits evidence only when present.\n sourceFile?: string\n}\n\n// Map a connection-string scheme to the engine name our compat matrix uses.\n// Schemes like \"postgres+asyncpg\" are normalised by stripping the dialect\n// suffix; anything we don't recognise returns null so the parser can decline.\nexport function schemeToEngine(scheme: string): string | null {\n const s = scheme.toLowerCase().split('+')[0]\n switch (s) {\n case 'postgres':\n case 'postgresql':\n return 'postgresql'\n case 'mysql':\n case 'mariadb':\n return 'mysql'\n case 'mongodb':\n case 'mongodb+srv':\n return 'mongodb'\n case 'redis':\n case 'rediss':\n return 'redis'\n case 'sqlite':\n return 'sqlite'\n default:\n return null\n }\n}\n\nexport function parseConnectionString(url: string): DbConfig | null {\n const m = url.match(\n /^(?<scheme>[a-z][a-z+]*):\\/\\/(?:[^@/]+(?::[^@]*)?@)?(?<host>[^:/?]+)(?::(?<port>\\d+))?(?:\\/(?<db>[^?#]*))?/i,\n )\n if (!m || !m.groups) return null\n const engine = schemeToEngine(m.groups.scheme!)\n if (!engine) return null\n return {\n host: m.groups.host!,\n port: m.groups.port ? Number(m.groups.port) : undefined,\n database: m.groups.db ?? '',\n engine,\n engineVersion: 'unknown',\n }\n}\n\nexport async function readIfExists(filePath: string): Promise<string | null> {\n try {\n return await fs.readFile(filePath, 'utf8')\n } catch {\n return null\n }\n}\n\nexport async function findFirst(\n serviceDir: string,\n candidates: string[],\n): Promise<string | null> {\n for (const rel of candidates) {\n const abs = path.join(serviceDir, rel)\n const content = await readIfExists(abs)\n if (content !== null) return abs\n }\n return null\n}\n\n// Engine name from a docker-compose `image:` value like \"postgres:15-alpine\"\n// or \"mysql/mysql-server:8.0\". Returns the engine + version when both are\n// resolvable, or null if the image isn't one we recognise.\nexport function engineFromImage(\n image: string,\n): { engine: string; engineVersion: string } | null {\n const lower = image.toLowerCase()\n const colon = lower.lastIndexOf(':')\n const repo = colon >= 0 ? lower.slice(0, colon) : lower\n const tag = colon >= 0 ? lower.slice(colon + 1) : 'latest'\n const last = repo.split('/').pop() ?? repo\n let engine: string | null = null\n if (last.startsWith('postgres')) engine = 'postgresql'\n else if (last.startsWith('mysql') || last.startsWith('mariadb')) engine = 'mysql'\n else if (last.startsWith('mongo')) engine = 'mongodb'\n else if (last.startsWith('redis')) engine = 'redis'\n else if (last.startsWith('sqlite')) engine = 'sqlite'\n if (!engine) return null\n // Strip everything after the major version digit run; \"15-alpine\" -> \"15\".\n const versionMatch = tag.match(/^(\\d+(?:\\.\\d+){0,2})/)\n return {\n engine,\n engineVersion: versionMatch ? versionMatch[1]! : 'unknown',\n }\n}\n","import path from 'node:path'\nimport { readIfExists, parseConnectionString, schemeToEngine, type DbConfig } from './shared.js'\n\n// Prisma's schema file declares datasources of the form:\n//\n// datasource db {\n// provider = \"postgresql\"\n// url = env(\"DATABASE_URL\")\n// }\n//\n// We match the provider directly. URLs come in via env() so we can't resolve\n// them statically; host/database fall back to placeholders so the DatabaseNode\n// id remains deterministic per service.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const schemaPath = path.join(serviceDir, 'prisma', 'schema.prisma')\n const content = await readIfExists(schemaPath)\n if (!content) return []\n\n const block = content.match(/datasource\\s+\\w+\\s*\\{([^}]*)\\}/s)\n if (!block) return []\n const body = block[1] ?? ''\n\n const providerMatch = body.match(/provider\\s*=\\s*\"([^\"]+)\"/)\n if (!providerMatch) return []\n const engine = schemeToEngine(providerMatch[1]!)\n if (!engine) return []\n\n const urlMatch = body.match(/url\\s*=\\s*\"([^\"]+)\"/)\n if (urlMatch) {\n const config = parseConnectionString(urlMatch[1]!)\n if (config) return [{ ...config, sourceFile: schemaPath }]\n }\n\n return [\n {\n host: `${engine}-prisma`,\n database: '',\n engine,\n engineVersion: 'unknown',\n sourceFile: schemaPath,\n },\n ]\n}\n\nexport const prismaParser = { name: 'prisma', parse }\n","import { findFirst, readIfExists, parseConnectionString, schemeToEngine, type DbConfig } from './shared.js'\n\nconst DIALECT_TO_ENGINE: Record<string, string> = {\n postgresql: 'postgresql',\n postgres: 'postgresql',\n pg: 'postgresql',\n mysql: 'mysql',\n mysql2: 'mysql',\n sqlite: 'sqlite',\n 'better-sqlite': 'sqlite',\n}\n\n// Drizzle's drizzle.config.{ts,js,mjs} declares a `dialect` plus credentials.\n// We can't safely eval the file, but the relevant bits are simple key/value\n// expressions — regex-extracted to stay sandbox-clean and dep-free.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const filePath = await findFirst(serviceDir, [\n 'drizzle.config.ts',\n 'drizzle.config.js',\n 'drizzle.config.mjs',\n ])\n if (!filePath) return []\n const content = await readIfExists(filePath)\n if (!content) return []\n\n const dialectMatch = content.match(/dialect\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n if (!dialectMatch) return []\n const engine =\n DIALECT_TO_ENGINE[dialectMatch[1]!.toLowerCase()] ?? schemeToEngine(dialectMatch[1]!)\n if (!engine) return []\n\n const urlMatch = content.match(\n /(?:url|connectionString)\\s*:\\s*['\"`]([a-z][a-z+]*:\\/\\/[^'\"`]+)['\"`]/i,\n )\n if (urlMatch) {\n const config = parseConnectionString(urlMatch[1]!)\n if (config) return [{ ...config, sourceFile: filePath }]\n }\n const hostMatch = content.match(/host\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n if (hostMatch) {\n const portMatch = content.match(/port\\s*:\\s*(\\d+)/)\n const dbMatch = content.match(/database\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n return [\n {\n host: hostMatch[1]!,\n port: portMatch ? Number(portMatch[1]) : undefined,\n database: dbMatch?.[1] ?? '',\n engine,\n engineVersion: 'unknown',\n sourceFile: filePath,\n },\n ]\n }\n return [\n { host: `${engine}-drizzle`, database: '', engine, engineVersion: 'unknown', sourceFile: filePath },\n ]\n}\n\nexport const drizzleParser = { name: 'drizzle', parse }\n","import { findFirst, readIfExists, parseConnectionString, type DbConfig } from './shared.js'\n\nconst CLIENT_TO_ENGINE: Record<string, string> = {\n pg: 'postgresql',\n postgres: 'postgresql',\n postgresql: 'postgresql',\n mysql: 'mysql',\n mysql2: 'mysql',\n sqlite3: 'sqlite',\n 'better-sqlite3': 'sqlite',\n}\n\n// knexfile.{js,ts} declares a client (one of pg/mysql/sqlite/...) plus a\n// connection string or host/port object. We pick whichever shape is in the\n// file and ignore environment-driven values we can't resolve statically.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const filePath = await findFirst(serviceDir, [\n 'knexfile.js',\n 'knexfile.ts',\n 'knexfile.cjs',\n 'knexfile.mjs',\n ])\n if (!filePath) return []\n const content = await readIfExists(filePath)\n if (!content) return []\n\n const clientMatch = content.match(/client\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n if (!clientMatch) return []\n const engine = CLIENT_TO_ENGINE[clientMatch[1]!.toLowerCase()]\n if (!engine) return []\n\n const urlMatch = content.match(\n /connection\\s*:\\s*['\"`]([a-z][a-z+]*:\\/\\/[^'\"`]+)['\"`]/i,\n )\n if (urlMatch) {\n const config = parseConnectionString(urlMatch[1]!)\n if (config) return [{ ...config, sourceFile: filePath }]\n }\n\n const host = content.match(/host\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1]\n if (host) {\n const port = content.match(/port\\s*:\\s*(\\d+)/)?.[1]\n const database = content.match(/database\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1] ?? ''\n return [\n {\n host,\n port: port ? Number(port) : undefined,\n database,\n engine,\n engineVersion: 'unknown',\n sourceFile: filePath,\n },\n ]\n }\n\n return [{ host: `${engine}-knex`, database: '', engine, engineVersion: 'unknown', sourceFile: filePath }]\n}\n\nexport const knexParser = { name: 'knex', parse }\n","import path from 'node:path'\nimport { exists, readJson, readYaml } from '../shared.js'\nimport { schemeToEngine, type DbConfig } from './shared.js'\n\ninterface OrmConfigEntry {\n type?: string\n host?: string\n port?: number\n database?: string\n}\n\n// ormconfig.{json,yaml,yml} — TypeORM's legacy config file. Single object or\n// an array of named connections; we walk both.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n for (const candidate of ['ormconfig.json', 'ormconfig.yaml', 'ormconfig.yml']) {\n const abs = path.join(serviceDir, candidate)\n if (!(await exists(abs))) continue\n const raw = candidate.endsWith('.json')\n ? await readJson<OrmConfigEntry | OrmConfigEntry[]>(abs)\n : await readYaml<OrmConfigEntry | OrmConfigEntry[]>(abs)\n const entries = Array.isArray(raw) ? raw : [raw]\n\n const out: DbConfig[] = []\n for (const entry of entries) {\n if (!entry?.type || !entry.host) continue\n const engine = schemeToEngine(entry.type)\n if (!engine) continue\n out.push({\n host: entry.host,\n port: entry.port,\n database: entry.database ?? '',\n engine,\n engineVersion: 'unknown',\n sourceFile: abs,\n })\n }\n if (out.length > 0) return out\n }\n return []\n}\n\nexport const ormconfigParser = { name: 'ormconfig', parse }\n","import { findFirst, readIfExists, schemeToEngine, type DbConfig } from './shared.js'\n\n// TypeORM's modern shape: `new DataSource({ type: 'postgres', host, port, ... })`\n// in a data-source.ts (or .js). We regex for the type/host/port/database keys\n// in the first DataSource literal we find.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const filePath = await findFirst(serviceDir, [\n 'data-source.ts',\n 'data-source.js',\n 'src/data-source.ts',\n 'src/data-source.js',\n ])\n if (!filePath) return []\n const content = await readIfExists(filePath)\n if (!content) return []\n\n const block = content.match(/new\\s+DataSource\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)/)\n const body = block ? block[1]! : content\n\n const typeMatch = body.match(/type\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)\n const host = body.match(/host\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1]\n if (!typeMatch || !host) return []\n\n const engine = schemeToEngine(typeMatch[1]!)\n if (!engine) return []\n\n const port = body.match(/port\\s*:\\s*(\\d+)/)?.[1]\n const database = body.match(/database\\s*:\\s*['\"`]([^'\"`]+)['\"`]/)?.[1] ?? ''\n\n return [\n {\n host,\n port: port ? Number(port) : undefined,\n database,\n engine,\n engineVersion: 'unknown',\n sourceFile: filePath,\n },\n ]\n}\n\nexport const typeormParser = { name: 'typeorm', parse }\n","import path from 'node:path'\nimport { exists, readJson } from '../shared.js'\nimport { schemeToEngine, type DbConfig } from './shared.js'\n\ninterface SequelizeConfigEntry {\n dialect?: string\n host?: string\n port?: number\n database?: string\n}\n\ntype SequelizeConfig = Record<string, SequelizeConfigEntry>\n\n// Sequelize stores per-environment configs under config/config.json. We read\n// every named environment so a service that declares production + staging\n// surfaces both DB targets.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n const configPath = path.join(serviceDir, 'config', 'config.json')\n if (!(await exists(configPath))) return []\n const raw = await readJson<SequelizeConfig>(configPath)\n\n const out: DbConfig[] = []\n const seen = new Set<string>()\n for (const entry of Object.values(raw)) {\n if (!entry?.dialect || !entry.host) continue\n const engine = schemeToEngine(entry.dialect)\n if (!engine) continue\n const key = `${engine}://${entry.host}:${entry.port ?? ''}/${entry.database ?? ''}`\n if (seen.has(key)) continue\n seen.add(key)\n out.push({\n host: entry.host,\n port: entry.port,\n database: entry.database ?? '',\n engine,\n engineVersion: 'unknown',\n sourceFile: configPath,\n })\n }\n return out\n}\n\nexport const sequelizeParser = { name: 'sequelize', parse }\n","import path from 'node:path'\nimport { exists, readYaml } from '../shared.js'\nimport { engineFromImage, type DbConfig } from './shared.js'\n\ninterface ComposeService {\n image?: string\n ports?: (string | number)[]\n environment?: Record<string, string> | string[]\n}\n\ninterface ComposeFile {\n services?: Record<string, ComposeService>\n}\n\nfunction portFromService(svc: ComposeService): number | undefined {\n for (const raw of svc.ports ?? []) {\n const str = String(raw)\n // \"5432:5432\", \"5432\", or \"host:5432\" → take the trailing port.\n const last = str.split(':').pop()\n const n = Number(last)\n if (Number.isFinite(n) && n > 0) return n\n }\n return undefined\n}\n\nfunction databaseFromEnv(svc: ComposeService): string {\n const env = svc.environment\n const get = (key: string): string | undefined => {\n if (!env) return undefined\n if (Array.isArray(env)) {\n for (const line of env) {\n const [k, v] = line.split('=')\n if (k === key) return v\n }\n return undefined\n }\n return env[key]\n }\n return get('POSTGRES_DB') ?? get('MYSQL_DATABASE') ?? get('MONGO_INITDB_DATABASE') ?? ''\n}\n\n// Service-local docker-compose.yml — every service whose image we recognise\n// becomes a candidate DB. The compose service name doubles as the host since\n// that's how peer services on the same compose network reach it.\nexport async function parse(serviceDir: string): Promise<DbConfig[]> {\n for (const name of ['docker-compose.yml', 'docker-compose.yaml']) {\n const abs = path.join(serviceDir, name)\n if (!(await exists(abs))) continue\n const raw = await readYaml<ComposeFile>(abs)\n if (!raw?.services) return []\n\n const out: DbConfig[] = []\n for (const [serviceName, svc] of Object.entries(raw.services)) {\n if (!svc.image) continue\n const meta = engineFromImage(svc.image)\n if (!meta) continue\n out.push({\n host: serviceName,\n port: portFromService(svc),\n database: databaseFromEnv(svc),\n engine: meta.engine,\n engineVersion: meta.engineVersion,\n sourceFile: abs,\n })\n }\n return out\n }\n return []\n}\n\nexport const dockerComposeParser = { name: 'docker-compose', parse }\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { ConfigNode, GraphEdge } from '@neat.is/types'\nimport { EdgeType, NodeType, Provenance, configId } from '@neat.is/types'\nimport type { NeatGraph } from '../graph.js'\nimport { IGNORED_DIRS, isConfigFile, makeEdgeId, type DiscoveredService } from './shared.js'\n\n// Walk a service directory and collect every config file path\n// (yaml/yml + .env-shaped). We deliberately stop at file paths here so nothing\n// in this module reads file contents — .env files routinely carry secrets\n// (ADR-016).\nexport async function walkConfigFiles(dir: string): Promise<string[]> {\n const out: string[] = []\n async function walk(current: string): Promise<void> {\n const entries = await fs.readdir(current, { withFileTypes: true })\n for (const entry of entries) {\n const full = path.join(current, entry.name)\n if (entry.isDirectory()) {\n if (!IGNORED_DIRS.has(entry.name)) await walk(full)\n } else if (entry.isFile() && isConfigFile(entry.name).match) {\n out.push(full)\n }\n }\n }\n await walk(dir)\n return out\n}\n\n// Phase 3 — turn each config file into a ConfigNode with a CONFIGURED_BY edge\n// from its owning service.\nexport async function addConfigNodes(\n graph: NeatGraph,\n services: DiscoveredService[],\n scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n let nodesAdded = 0\n let edgesAdded = 0\n for (const service of services) {\n const configFiles = await walkConfigFiles(service.dir)\n for (const file of configFiles) {\n const relPath = path.relative(scanPath, file)\n const node: ConfigNode = {\n id: configId(relPath),\n type: NodeType.ConfigNode,\n name: path.basename(file),\n path: relPath,\n fileType: isConfigFile(path.basename(file)).fileType,\n }\n if (!graph.hasNode(node.id)) {\n graph.addNode(node.id, node)\n nodesAdded++\n }\n const edge: GraphEdge = {\n id: makeEdgeId(service.node.id, node.id, EdgeType.CONFIGURED_BY),\n source: service.node.id,\n target: node.id,\n type: EdgeType.CONFIGURED_BY,\n provenance: Provenance.EXTRACTED,\n evidence: { file: relPath.split(path.sep).join('/') },\n }\n if (!graph.hasEdge(edge.id)) {\n graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge)\n edgesAdded++\n }\n }\n }\n return { nodesAdded, edgesAdded }\n}\n","import type { GraphEdge, InfraNode } from '@neat.is/types'\nimport { EdgeType, NodeType, Provenance } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { makeEdgeId, type DiscoveredService } from '../shared.js'\nimport { addHttpCallEdges } from './http.js'\nimport { loadSourceFiles, type ExternalEndpoint } from './shared.js'\nimport { kafkaEndpointsFromFile } from './kafka.js'\nimport { redisEndpointsFromFile } from './redis.js'\nimport { awsEndpointsFromFile } from './aws.js'\nimport { grpcEndpointsFromFile } from './grpc.js'\n\nexport interface CallExtractResult {\n nodesAdded: number\n edgesAdded: number\n}\n\nfunction edgeTypeFromEndpoint(ep: ExternalEndpoint): (typeof EdgeType)[keyof typeof EdgeType] {\n switch (ep.edgeType) {\n case 'PUBLISHES_TO':\n return EdgeType.PUBLISHES_TO\n case 'CONSUMES_FROM':\n return EdgeType.CONSUMES_FROM\n default:\n return EdgeType.CALLS\n }\n}\n\nasync function addExternalEndpointEdges(\n graph: NeatGraph,\n services: DiscoveredService[],\n): Promise<CallExtractResult> {\n let nodesAdded = 0\n let edgesAdded = 0\n\n for (const service of services) {\n const files = await loadSourceFiles(service.dir)\n const endpoints: ExternalEndpoint[] = []\n for (const file of files) {\n endpoints.push(...kafkaEndpointsFromFile(file, service.dir))\n endpoints.push(...redisEndpointsFromFile(file, service.dir))\n endpoints.push(...awsEndpointsFromFile(file, service.dir))\n endpoints.push(...grpcEndpointsFromFile(file, service.dir))\n }\n if (endpoints.length === 0) continue\n\n const seenEdges = new Set<string>()\n for (const ep of endpoints) {\n if (!graph.hasNode(ep.infraId)) {\n const node: InfraNode = {\n id: ep.infraId,\n type: NodeType.InfraNode,\n name: ep.name,\n provider: ep.kind.startsWith('s3') || ep.kind.startsWith('dynamodb') ? 'aws' : 'self',\n kind: ep.kind,\n }\n graph.addNode(node.id, node)\n nodesAdded++\n }\n\n const edgeType = edgeTypeFromEndpoint(ep)\n const edgeId = makeEdgeId(service.node.id, ep.infraId, edgeType)\n if (seenEdges.has(edgeId)) continue\n seenEdges.add(edgeId)\n if (!graph.hasEdge(edgeId)) {\n const edge: GraphEdge = {\n id: edgeId,\n source: service.node.id,\n target: ep.infraId,\n type: edgeType,\n provenance: Provenance.EXTRACTED,\n evidence: ep.evidence,\n }\n graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n edgesAdded++\n }\n }\n }\n return { nodesAdded, edgesAdded }\n}\n\nexport async function addCallEdges(\n graph: NeatGraph,\n services: DiscoveredService[],\n): Promise<CallExtractResult> {\n const httpEdges = await addHttpCallEdges(graph, services)\n const ext = await addExternalEndpointEdges(graph, services)\n return {\n nodesAdded: ext.nodesAdded,\n edgesAdded: httpEdges + ext.edgesAdded,\n }\n}\n","import path from 'node:path'\nimport Parser from 'tree-sitter'\nimport JavaScript from 'tree-sitter-javascript'\nimport Python from 'tree-sitter-python'\nimport type { GraphEdge } from '@neat.is/types'\nimport { EdgeType, Provenance } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { makeEdgeId, type DiscoveredService } from '../shared.js'\nimport { loadSourceFiles, lineOf, snippet } from './shared.js'\n\n// JS uses `string_fragment` for the textual interior of a template/string;\n// Python uses `string_content` inside a `string` node. Either way we want the\n// raw textual content (no quotes), so we accept both.\nconst STRING_LITERAL_NODE_TYPES = new Set(['string_fragment', 'string_content'])\n\nfunction collectStringLiterals(node: Parser.SyntaxNode, out: string[]): void {\n if (STRING_LITERAL_NODE_TYPES.has(node.type)) out.push(node.text)\n for (let i = 0; i < node.namedChildCount; i++) {\n const child = node.namedChild(i)\n if (child) collectStringLiterals(child, out)\n }\n}\n\nexport function callsFromSource(\n source: string,\n parser: Parser,\n knownHosts: Set<string>,\n): Set<string> {\n const tree = parser.parse(source)\n const literals: string[] = []\n collectStringLiterals(tree.rootNode, literals)\n const targets = new Set<string>()\n for (const lit of literals) {\n for (const host of knownHosts) {\n if (lit.includes(`//${host}`) || lit.includes(`//${host}:`)) {\n targets.add(host)\n }\n }\n }\n return targets\n}\n\nfunction makeJsParser(): Parser {\n const p = new Parser()\n p.setLanguage(JavaScript)\n return p\n}\n\nfunction makePyParser(): Parser {\n const p = new Parser()\n p.setLanguage(Python)\n return p\n}\n\n// HTTP CALLS via URL substring match. Parser is picked per file extension:\n// .py uses tree-sitter-python; everything else uses tree-sitter-javascript.\n// The demo's CALLS edges stay byte-for-byte identical to the M1 baseline.\nexport async function addHttpCallEdges(\n graph: NeatGraph,\n services: DiscoveredService[],\n): Promise<number> {\n const jsParser = makeJsParser()\n const pyParser = makePyParser()\n\n const knownHosts = new Set<string>()\n const hostToNodeId = new Map<string, string>()\n for (const service of services) {\n knownHosts.add(path.basename(service.dir))\n knownHosts.add(service.pkg.name)\n hostToNodeId.set(path.basename(service.dir), service.node.id)\n hostToNodeId.set(service.pkg.name, service.node.id)\n }\n\n let edgesAdded = 0\n for (const service of services) {\n const files = await loadSourceFiles(service.dir)\n const seenTargets = new Map<string, { file: string; host: string }>()\n for (const file of files) {\n const parser = path.extname(file.path) === '.py' ? pyParser : jsParser\n const targets = callsFromSource(file.content, parser, knownHosts)\n for (const t of targets) {\n const targetId = hostToNodeId.get(t)\n if (!targetId || targetId === service.node.id) continue\n if (!seenTargets.has(targetId)) {\n seenTargets.set(targetId, { file: file.path, host: t })\n }\n }\n }\n for (const [targetId, evidenceFile] of seenTargets) {\n const fileContent = files.find((f) => f.path === evidenceFile.file)?.content ?? ''\n const line = lineOf(fileContent, `//${evidenceFile.host}`)\n const edge: GraphEdge = {\n id: makeEdgeId(service.node.id, targetId, EdgeType.CALLS),\n source: service.node.id,\n target: targetId,\n type: EdgeType.CALLS,\n provenance: Provenance.EXTRACTED,\n evidence: {\n file: path.relative(service.dir, evidenceFile.file),\n line,\n snippet: snippet(fileContent, line),\n },\n }\n if (!graph.hasEdge(edge.id)) {\n graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge)\n edgesAdded++\n }\n }\n }\n return edgesAdded\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { EdgeEvidence } from '@neat.is/types'\nimport { IGNORED_DIRS, SERVICE_FILE_EXTENSIONS } from '../shared.js'\n\nexport interface SourceFile {\n path: string\n content: string\n}\n\nexport interface ExternalEndpoint {\n // Stable id of the InfraNode this evidence implies. Format\n // `infra:<kind>:<name>` so the orchestrator can dedupe across services.\n infraId: string\n // Display name on the InfraNode (e.g., \"orders\" for kafka-topic:orders).\n name: string\n kind: string\n edgeType: 'CALLS' | 'PUBLISHES_TO' | 'CONSUMES_FROM'\n evidence: EdgeEvidence\n}\n\nexport async function walkSourceFiles(dir: string): Promise<string[]> {\n const out: string[] = []\n async function walk(current: string): Promise<void> {\n const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => [])\n for (const entry of entries) {\n const full = path.join(current, entry.name)\n if (entry.isDirectory()) {\n if (!IGNORED_DIRS.has(entry.name)) await walk(full)\n } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(path.extname(entry.name))) {\n out.push(full)\n }\n }\n }\n await walk(dir)\n return out\n}\n\nexport async function loadSourceFiles(dir: string): Promise<SourceFile[]> {\n const paths = await walkSourceFiles(dir)\n const out: SourceFile[] = []\n for (const p of paths) {\n try {\n const content = await fs.readFile(p, 'utf8')\n out.push({ path: p, content })\n } catch {\n // unreadable, skip\n }\n }\n return out\n}\n\n// Locate the line of the first occurrence of `needle` in `text`, 1-indexed.\n// Falls back to line 1 if the needle isn't found verbatim — better to point at\n// the file than to drop the evidence entirely.\nexport function lineOf(text: string, needle: string): number {\n const idx = text.indexOf(needle)\n if (idx < 0) return 1\n return text.slice(0, idx).split('\\n').length\n}\n\nexport function snippet(text: string, line: number): string {\n const lines = text.split('\\n')\n return (lines[line - 1] ?? '').trim()\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// Match `producer.send({ topic: \"orders\" ... })` and `producer.send({\n// topic: 'orders' })` plus the two-arg form `producer.send(\"orders\", ...)`.\n// Kafka client libraries vary; these two forms cover kafkajs + node-rdkafka\n// well enough for static extraction. Same shape covers consumer.subscribe.\nconst PRODUCER_TOPIC_RE =\n /(?:producer|kafkaProducer)[\\s\\S]{0,40}?\\.send\\s*\\(\\s*\\{[\\s\\S]{0,200}?topic\\s*:\\s*['\"`]([^'\"`]+)['\"`]/g\nconst CONSUMER_TOPIC_RE =\n /(?:consumer|kafkaConsumer)[\\s\\S]{0,40}?\\.(?:subscribe|run)\\s*\\(\\s*\\{[\\s\\S]{0,200}?topic[s]?\\s*:\\s*(?:\\[\\s*)?['\"`]([^'\"`]+)['\"`]/g\n\nfunction findAll(re: RegExp, text: string): { topic: string; index: number }[] {\n re.lastIndex = 0\n const out: { topic: string; index: number }[] = []\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n out.push({ topic: m[1]!, index: m.index })\n }\n return out\n}\n\nexport function kafkaEndpointsFromFile(\n file: SourceFile,\n serviceDir: string,\n): ExternalEndpoint[] {\n const out: ExternalEndpoint[] = []\n const seen = new Set<string>()\n const make = (topic: string, edgeType: 'PUBLISHES_TO' | 'CONSUMES_FROM'): void => {\n const key = `${edgeType}|${topic}`\n if (seen.has(key)) return\n seen.add(key)\n const line = lineOf(file.content, topic)\n out.push({\n infraId: infraId('kafka-topic', topic),\n name: topic,\n kind: 'kafka-topic',\n edgeType,\n evidence: {\n file: path.relative(serviceDir, file.path),\n line,\n snippet: snippet(file.content, line),\n },\n })\n }\n\n for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, 'PUBLISHES_TO')\n for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, 'CONSUMES_FROM')\n return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// Redis URLs in source — `redis://host[:port]` or `rediss://...`. We only\n// catch literal strings; env-driven URLs go through the database parsers\n// (.env, ormconfig, etc.) and don't need a CALLS edge.\nconst REDIS_URL_RE = /redis(?:s)?:\\/\\/(?:[^@'\"`\\s]+@)?([^:/'\"`\\s]+)(?::(\\d+))?/g\n\nexport function redisEndpointsFromFile(\n file: SourceFile,\n serviceDir: string,\n): ExternalEndpoint[] {\n const out: ExternalEndpoint[] = []\n const seen = new Set<string>()\n REDIS_URL_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = REDIS_URL_RE.exec(file.content)) !== null) {\n const host = m[1]!\n if (seen.has(host)) continue\n seen.add(host)\n const line = lineOf(file.content, host)\n out.push({\n infraId: infraId('redis', host),\n name: host,\n kind: 'redis',\n edgeType: 'CALLS',\n evidence: {\n file: path.relative(serviceDir, file.path),\n line,\n snippet: snippet(file.content, line),\n },\n })\n }\n return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// AWS SDK v3 calls. We catch S3 (`Bucket: \"x\"` near a `S3Client`-using\n// PutObjectCommand / GetObjectCommand / DeleteObjectCommand) and DynamoDB\n// (`TableName: \"x\"` near GetCommand / PutCommand / DynamoDBClient). The\n// pattern is intentionally permissive: a literal Bucket/TableName near an\n// SDK constant is good enough evidence; misses are fine because non-static\n// resources can't be catalogued anyway.\nconst S3_BUCKET_RE = /Bucket\\s*:\\s*['\"`]([^'\"`]+)['\"`]/g\nconst DYNAMO_TABLE_RE = /TableName\\s*:\\s*['\"`]([^'\"`]+)['\"`]/g\n\nfunction hasMarker(text: string, markers: string[]): boolean {\n return markers.some((m) => text.includes(m))\n}\n\nfunction findAll(re: RegExp, text: string): { name: string; index: number }[] {\n re.lastIndex = 0\n const out: { name: string; index: number }[] = []\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n out.push({ name: m[1]!, index: m.index })\n }\n return out\n}\n\nexport function awsEndpointsFromFile(\n file: SourceFile,\n serviceDir: string,\n): ExternalEndpoint[] {\n const out: ExternalEndpoint[] = []\n const seen = new Set<string>()\n const make = (kind: string, name: string): void => {\n const key = `${kind}|${name}`\n if (seen.has(key)) return\n seen.add(key)\n const line = lineOf(file.content, name)\n out.push({\n infraId: infraId(kind, name),\n name,\n kind,\n edgeType: 'CALLS',\n evidence: {\n file: path.relative(serviceDir, file.path),\n line,\n snippet: snippet(file.content, line),\n },\n })\n }\n\n if (hasMarker(file.content, ['S3Client', 'PutObjectCommand', 'GetObjectCommand', 'DeleteObjectCommand'])) {\n for (const { name } of findAll(S3_BUCKET_RE, file.content)) make('s3-bucket', name)\n }\n if (\n hasMarker(file.content, [\n 'DynamoDBClient',\n 'DynamoDBDocumentClient',\n 'GetCommand',\n 'PutCommand',\n 'QueryCommand',\n 'UpdateCommand',\n 'DeleteCommand',\n ])\n ) {\n for (const { name } of findAll(DYNAMO_TABLE_RE, file.content)) make('dynamodb-table', name)\n }\n return out\n}\n","import path from 'node:path'\nimport { infraId } from '@neat.is/types'\nimport { lineOf, snippet, type ExternalEndpoint, type SourceFile } from './shared.js'\n\n// gRPC client construction in JS/TS:\n//\n// const client = new orders_proto.OrderService(...)\n// const client = new OrdersClient('orders.internal:50051', ...)\n//\n// We catch `new <Name>Client(...)` and `new <namespace>.<Name>Service(...)`\n// patterns and use the symbol name as the inferred service id. The address\n// argument, when statically resolvable, becomes the host hint on the\n// resulting CALLS edge — but resolution is best-effort.\nconst GRPC_CLIENT_RE = /new\\s+([A-Z][A-Za-z0-9_]*)Client\\s*\\(\\s*['\"`]?([^,'\"`)]+)?/g\n\nfunction isLikelyAddress(value: string | undefined): boolean {\n if (!value) return false\n return /:\\d{2,5}$/.test(value) || value.includes('.')\n}\n\nexport function grpcEndpointsFromFile(\n file: SourceFile,\n serviceDir: string,\n): ExternalEndpoint[] {\n const out: ExternalEndpoint[] = []\n const seen = new Set<string>()\n GRPC_CLIENT_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = GRPC_CLIENT_RE.exec(file.content)) !== null) {\n const symbol = m[1]!\n const addr = m[2]?.trim()\n const name = isLikelyAddress(addr) ? addr! : symbol\n if (seen.has(name)) continue\n seen.add(name)\n const line = lineOf(file.content, m[0])\n out.push({\n infraId: infraId('grpc-service', name),\n name,\n kind: 'grpc-service',\n edgeType: 'CALLS',\n evidence: {\n file: path.relative(serviceDir, file.path),\n line,\n snippet: snippet(file.content, line),\n },\n })\n }\n return out\n}\n","import path from 'node:path'\nimport type { GraphEdge } from '@neat.is/types'\nimport { EdgeType, Provenance } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { exists, makeEdgeId, readYaml, type DiscoveredService } from '../shared.js'\nimport { classifyImage, makeInfraNode } from './shared.js'\n\ninterface ComposeService {\n image?: string\n build?: string | { context?: string }\n depends_on?: string[] | Record<string, unknown>\n}\n\ninterface ComposeFile {\n services?: Record<string, ComposeService>\n}\n\nfunction dependsOnList(value: ComposeService['depends_on']): string[] {\n if (!value) return []\n if (Array.isArray(value)) return value\n return Object.keys(value)\n}\n\nfunction serviceNameToServiceNode(\n name: string,\n services: DiscoveredService[],\n): string | null {\n for (const s of services) {\n if (s.node.name === name || path.basename(s.dir) === name) return s.node.id\n }\n return null\n}\n\n// Project-level docker-compose.yml describes deployment topology. Each compose\n// service that is *not* one of the discovered ServiceNodes becomes an\n// InfraNode (databases, brokers, caches). depends_on lists become DEPENDS_ON\n// edges from the dependent to its dependency, regardless of whether the\n// endpoint is a ServiceNode or InfraNode — the edge itself is the deployment\n// fact, not the role.\nexport async function addComposeInfra(\n graph: NeatGraph,\n scanPath: string,\n services: DiscoveredService[],\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n let nodesAdded = 0\n let edgesAdded = 0\n\n let composePath: string | null = null\n for (const name of ['docker-compose.yml', 'docker-compose.yaml']) {\n const abs = path.join(scanPath, name)\n if (await exists(abs)) {\n composePath = abs\n break\n }\n }\n if (!composePath) return { nodesAdded, edgesAdded }\n\n const compose = await readYaml<ComposeFile>(composePath)\n if (!compose?.services) return { nodesAdded, edgesAdded }\n const evidenceFile = path.relative(scanPath, composePath).split(path.sep).join('/')\n\n const composeNameToNodeId = new Map<string, string>()\n for (const [composeName, svc] of Object.entries(compose.services)) {\n const matchedServiceId = serviceNameToServiceNode(composeName, services)\n if (matchedServiceId) {\n composeNameToNodeId.set(composeName, matchedServiceId)\n continue\n }\n const kind = svc.image ? classifyImage(svc.image) : 'container'\n const node = makeInfraNode(kind, composeName)\n if (!graph.hasNode(node.id)) {\n graph.addNode(node.id, node)\n nodesAdded++\n }\n composeNameToNodeId.set(composeName, node.id)\n }\n\n for (const [composeName, svc] of Object.entries(compose.services)) {\n const sourceId = composeNameToNodeId.get(composeName)\n if (!sourceId) continue\n for (const dep of dependsOnList(svc.depends_on)) {\n const targetId = composeNameToNodeId.get(dep)\n if (!targetId) continue\n const edgeId = makeEdgeId(sourceId, targetId, EdgeType.DEPENDS_ON)\n if (graph.hasEdge(edgeId)) continue\n const edge: GraphEdge = {\n id: edgeId,\n source: sourceId,\n target: targetId,\n type: EdgeType.DEPENDS_ON,\n provenance: Provenance.EXTRACTED,\n evidence: { file: evidenceFile },\n }\n graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n edgesAdded++\n }\n }\n\n return { nodesAdded, edgesAdded }\n}\n","import type { InfraNode } from '@neat.is/types'\nimport { NodeType, infraId } from '@neat.is/types'\n\n// ADR-010 reserves the `infra:` prefix; the kind segment lets traversal and\n// MCP tools sub-type without inventing a new top-level NodeType per source.\nexport function makeInfraNode(\n kind: string,\n name: string,\n provider = 'self',\n extras?: { region?: string },\n): InfraNode {\n return {\n id: infraId(kind, name),\n type: NodeType.InfraNode,\n name,\n provider,\n kind,\n ...(extras?.region ? { region: extras.region } : {}),\n }\n}\n\n// Stable kind for an image string like \"postgres:15-alpine\" or \"mysql:8\".\n// The image name itself ends up in the InfraNode `name` field; this function\n// only classifies what the image *is*, so callers can group similar runtimes.\nexport function classifyImage(image: string): string {\n const lower = image.toLowerCase()\n const repo = lower.split(':')[0]!\n const last = repo.split('/').pop() ?? repo\n if (last.startsWith('postgres')) return 'postgres'\n if (last.startsWith('mysql') || last.startsWith('mariadb')) return 'mysql'\n if (last.startsWith('mongo')) return 'mongodb'\n if (last.startsWith('redis')) return 'redis'\n if (last.startsWith('rabbitmq')) return 'rabbitmq'\n if (last.startsWith('kafka') || last.includes('kafka')) return 'kafka'\n if (last.startsWith('memcached')) return 'memcached'\n return 'container'\n}\n","import path from 'node:path'\nimport { promises as fs } from 'node:fs'\nimport type { GraphEdge } from '@neat.is/types'\nimport { EdgeType, Provenance } from '@neat.is/types'\nimport type { NeatGraph } from '../../graph.js'\nimport { exists, makeEdgeId, type DiscoveredService } from '../shared.js'\nimport { makeInfraNode } from './shared.js'\n\n// Pull the first non-`scratch` `FROM` line out of a Dockerfile, ignoring\n// multi-stage `as` aliases. Returns the image including tag (e.g. `node:20`,\n// `python:3.11-slim`). Multi-stage builds report the *runtime* image — the\n// last FROM that isn't aliasing a previous stage.\nfunction runtimeImage(content: string): string | null {\n const lines = content.split('\\n')\n let last: string | null = null\n for (const raw of lines) {\n const line = raw.trim()\n if (!line || line.startsWith('#')) continue\n if (!/^from\\s+/i.test(line)) continue\n const tokens = line.split(/\\s+/)\n const image = tokens[1]\n if (!image || image.toLowerCase() === 'scratch') continue\n last = image\n }\n return last\n}\n\n// For each ServiceNode that has a Dockerfile in its dir, emit a\n// `infra:container-image:<image>` InfraNode and a RUNS_ON edge from the\n// service to the image.\nexport async function addDockerfileRuntimes(\n graph: NeatGraph,\n services: DiscoveredService[],\n scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n let nodesAdded = 0\n let edgesAdded = 0\n\n for (const service of services) {\n const dockerfilePath = path.join(service.dir, 'Dockerfile')\n if (!(await exists(dockerfilePath))) continue\n const content = await fs.readFile(dockerfilePath, 'utf8')\n const image = runtimeImage(content)\n if (!image) continue\n\n const node = makeInfraNode('container-image', image)\n if (!graph.hasNode(node.id)) {\n graph.addNode(node.id, node)\n nodesAdded++\n }\n\n const edgeId = makeEdgeId(service.node.id, node.id, EdgeType.RUNS_ON)\n if (!graph.hasEdge(edgeId)) {\n const edge: GraphEdge = {\n id: edgeId,\n source: service.node.id,\n target: node.id,\n type: EdgeType.RUNS_ON,\n provenance: Provenance.EXTRACTED,\n evidence: {\n file: path.relative(scanPath, dockerfilePath).split(path.sep).join('/'),\n },\n }\n graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge)\n edgesAdded++\n }\n }\n\n return { nodesAdded, edgesAdded }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { NeatGraph } from '../../graph.js'\nimport { IGNORED_DIRS } from '../shared.js'\nimport { makeInfraNode } from './shared.js'\n\n// Light pass: catalogue `resource \"aws_*\" \"name\"` blocks in any *.tf file.\n// We don't interpret references — a real Terraform backend would resolve\n// those — but the resource-type/name pair is enough to register the node so\n// later cross-references can hang off it.\nconst RESOURCE_RE = /resource\\s+\"(aws_[A-Za-z0-9_]+)\"\\s+\"([A-Za-z0-9_-]+)\"/g\n\nasync function walkTfFiles(start: string, depth = 0, max = 5): Promise<string[]> {\n if (depth > max) return []\n const out: string[] = []\n const entries = await fs.readdir(start, { withFileTypes: true }).catch(() => [])\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (IGNORED_DIRS.has(entry.name) || entry.name === '.terraform') continue\n out.push(...(await walkTfFiles(path.join(start, entry.name), depth + 1, max)))\n } else if (entry.isFile() && entry.name.endsWith('.tf')) {\n out.push(path.join(start, entry.name))\n }\n }\n return out\n}\n\nexport async function addTerraformResources(\n graph: NeatGraph,\n scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n let nodesAdded = 0\n const files = await walkTfFiles(scanPath)\n for (const file of files) {\n const content = await fs.readFile(file, 'utf8')\n RESOURCE_RE.lastIndex = 0\n let m: RegExpExecArray | null\n while ((m = RESOURCE_RE.exec(content)) !== null) {\n const kind = m[1]!\n const name = m[2]!\n const node = makeInfraNode(kind, name, 'aws')\n if (!graph.hasNode(node.id)) {\n graph.addNode(node.id, node)\n nodesAdded++\n }\n }\n }\n return { nodesAdded, edgesAdded: 0 }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { parseAllDocuments } from 'yaml'\nimport type { NeatGraph } from '../../graph.js'\nimport { CONFIG_FILE_EXTENSIONS, IGNORED_DIRS } from '../shared.js'\nimport { makeInfraNode } from './shared.js'\n\ninterface K8sDoc {\n kind?: string\n metadata?: { name?: string; namespace?: string }\n}\n\nconst K8S_KIND_TO_INFRA_KIND: Record<string, string> = {\n Service: 'k8s-service',\n Deployment: 'k8s-deployment',\n StatefulSet: 'k8s-statefulset',\n DaemonSet: 'k8s-daemonset',\n CronJob: 'k8s-cronjob',\n Job: 'k8s-job',\n Ingress: 'k8s-ingress',\n}\n\nasync function walkYamlFiles(start: string, depth = 0, max = 5): Promise<string[]> {\n if (depth > max) return []\n const out: string[] = []\n const entries = await fs.readdir(start, { withFileTypes: true }).catch(() => [])\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (IGNORED_DIRS.has(entry.name)) continue\n out.push(...(await walkYamlFiles(path.join(start, entry.name), depth + 1, max)))\n } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(path.extname(entry.name))) {\n out.push(path.join(start, entry.name))\n }\n }\n return out\n}\n\n// Multi-document YAML with kind/metadata.name. We keep the matching simple:\n// any file whose first doc looks k8s-shaped. The match is on `kind` only —\n// random YAML configs (db-config.yaml, etc.) are usually flat objects with no\n// `kind` field, so they're ignored without false positives.\nexport async function addK8sResources(\n graph: NeatGraph,\n scanPath: string,\n): Promise<{ nodesAdded: number; edgesAdded: number }> {\n let nodesAdded = 0\n const files = await walkYamlFiles(scanPath)\n for (const file of files) {\n const content = await fs.readFile(file, 'utf8')\n let docs: K8sDoc[]\n try {\n docs = parseAllDocuments(content).map((d) => d.toJSON() as K8sDoc)\n } catch {\n continue\n }\n for (const doc of docs) {\n if (!doc?.kind || !doc.metadata?.name) continue\n const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind]\n if (!infraKind) continue\n const namespaced = doc.metadata.namespace\n ? `${doc.metadata.namespace}/${doc.metadata.name}`\n : doc.metadata.name\n const node = makeInfraNode(infraKind, namespaced, 'kubernetes')\n if (!graph.hasNode(node.id)) {\n graph.addNode(node.id, node)\n nodesAdded++\n }\n }\n }\n return { nodesAdded, edgesAdded: 0 }\n}\n","import type { NeatGraph } from '../../graph.js'\nimport { type DiscoveredService } from '../shared.js'\nimport { addComposeInfra } from './docker-compose.js'\nimport { addDockerfileRuntimes } from './dockerfile.js'\nimport { addTerraformResources } from './terraform.js'\nimport { addK8sResources } from './k8s.js'\n\nexport interface InfraExtractResult {\n nodesAdded: number\n edgesAdded: number\n}\n\n// Phase 5 — infrastructure. Runs after services so RUNS_ON edges have a\n// ServiceNode to anchor on. Each sub-source contributes its own nodes/edges\n// independently; nothing here mutates ServiceNodes themselves.\nexport async function addInfra(\n graph: NeatGraph,\n scanPath: string,\n services: DiscoveredService[],\n): Promise<InfraExtractResult> {\n const compose = await addComposeInfra(graph, scanPath, services)\n const dockerfile = await addDockerfileRuntimes(graph, services, scanPath)\n const terraform = await addTerraformResources(graph, scanPath)\n const k8s = await addK8sResources(graph, scanPath)\n\n return {\n nodesAdded:\n compose.nodesAdded + dockerfile.nodesAdded + terraform.nodesAdded + k8s.nodesAdded,\n edgesAdded:\n compose.edgesAdded + dockerfile.edgesAdded + terraform.edgesAdded + k8s.edgesAdded,\n }\n}\n","// Static-extraction pipeline. Phase order is load-bearing:\n// services → aliases → databases (+ compat) → configs → calls → infra → frontier promotion.\n//\n// Contract anchors (see /docs/contracts.md):\n// * Rule 1 — Every emitted edge carries Provenance.EXTRACTED from @neat.is/types.\n// * Rule 2 — EXTRACTED edges use the plain `${type}:src->tgt` id pattern.\n// Never write under the OBSERVED id pattern; that's ingest.ts's territory.\n// * Rule 5 — Nodes/edges constructed against schemas in @neat.is/types; no\n// local interface redefinitions in this tree.\n// * Rule 8 — No demo-name hardcoding. Driver names come from package.json\n// dependencies; engine names from compat.json via compatPairs().\n// * Rule 14 — ConfigNodes record file existence only; never the contents.\nimport type { NeatGraph } from '../graph.js'\nimport { promoteFrontierNodes } from '../ingest.js'\nimport { ensureCompatLoaded } from '../compat.js'\nimport { addServiceNodes, discoverServices } from './services.js'\nimport { addServiceAliases } from './aliases.js'\nimport { addDatabasesAndCompat } from './databases/index.js'\nimport { addConfigNodes } from './configs.js'\nimport { addCallEdges } from './calls/index.js'\nimport { addInfra } from './infra/index.js'\n\nexport interface ExtractResult {\n nodesAdded: number\n edgesAdded: number\n frontiersPromoted: number\n}\n\nexport interface ExtractOptions {\n // Post-extract policy trigger (ADR-043). Awaited after frontier promotion\n // so policies see the final post-pass graph state. Daemons wire this to\n // evaluateAllPolicies + PolicyViolationsLog.append.\n onPolicyTrigger?: (graph: NeatGraph) => Promise<void> | void\n}\n\nexport async function extractFromDirectory(\n graph: NeatGraph,\n scanPath: string,\n opts: ExtractOptions = {},\n): Promise<ExtractResult> {\n await ensureCompatLoaded()\n const services = await discoverServices(scanPath)\n\n const phase1Nodes = addServiceNodes(graph, services)\n await addServiceAliases(graph, scanPath, services)\n const phase2 = await addDatabasesAndCompat(graph, services, scanPath)\n const phase3 = await addConfigNodes(graph, services, scanPath)\n const phase4 = await addCallEdges(graph, services)\n const phase5 = await addInfra(graph, scanPath, services)\n const frontiersPromoted = promoteFrontierNodes(graph)\n\n // Post-extract policy trigger (ADR-043). Fires after frontier promotion so\n // policies see the post-pass graph (including any FRONTIER → OBSERVED edge\n // upgrades that just landed).\n if (opts.onPolicyTrigger) await opts.onPolicyTrigger(graph)\n\n return {\n nodesAdded:\n phase1Nodes +\n phase2.nodesAdded +\n phase3.nodesAdded +\n phase4.nodesAdded +\n phase5.nodesAdded,\n edgesAdded:\n phase2.edgesAdded + phase3.edgesAdded + phase4.edgesAdded + phase5.edgesAdded,\n frontiersPromoted,\n }\n}\n","import { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport type { NeatGraph } from './graph.js'\n\nconst SCHEMA_VERSION = 2\n\ninterface PersistedGraph {\n schemaVersion: number\n exportedAt: string\n graph: ReturnType<NeatGraph['export']>\n}\n\n// v1 → v2: ServiceNode shed `pgDriverVersion` (ADR-019). Compat traversal reads\n// `dependencies[driver]` instead. Strip the field from any v1 snapshot rather\n// than hard-failing — a stale snapshot on disk shouldn't cost a re-extract.\nfunction migrateV1ToV2(payload: PersistedGraph): PersistedGraph {\n const nodes = (payload.graph as { nodes?: Array<{ attributes?: Record<string, unknown> }> })\n .nodes\n if (Array.isArray(nodes)) {\n for (const node of nodes) {\n if (node.attributes && 'pgDriverVersion' in node.attributes) {\n delete node.attributes.pgDriverVersion\n }\n }\n }\n return { ...payload, schemaVersion: 2 }\n}\n\nasync function ensureDir(filePath: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true })\n}\n\nexport async function saveGraphToDisk(graph: NeatGraph, outPath: string): Promise<void> {\n await ensureDir(outPath)\n const payload: PersistedGraph = {\n schemaVersion: SCHEMA_VERSION,\n exportedAt: new Date().toISOString(),\n graph: graph.export(),\n }\n // Atomic write: drop into <name>.tmp first, then rename. A crash mid-write\n // leaves the previous snapshot intact instead of a half-truncated file.\n const tmp = `${outPath}.tmp`\n await fs.writeFile(tmp, JSON.stringify(payload), 'utf8')\n await fs.rename(tmp, outPath)\n}\n\nexport async function loadGraphFromDisk(graph: NeatGraph, outPath: string): Promise<void> {\n let raw: string\n try {\n raw = await fs.readFile(outPath, 'utf8')\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return\n throw err\n }\n let payload = JSON.parse(raw) as PersistedGraph\n if (payload.schemaVersion === 1) {\n payload = migrateV1ToV2(payload)\n }\n if (payload.schemaVersion !== SCHEMA_VERSION) {\n throw new Error(\n `persist: unsupported snapshot schemaVersion ${payload.schemaVersion} (expected ${SCHEMA_VERSION})`,\n )\n }\n graph.clear()\n graph.import(payload.graph)\n}\n\n// Periodic save + best-effort save on SIGTERM/SIGINT. Returns a cleanup that\n// clears the interval and unhooks the signal handlers — important for tests so\n// they don't keep the process alive.\nexport function startPersistLoop(\n graph: NeatGraph,\n outPath: string,\n intervalMs = 60_000,\n): () => void {\n let stopped = false\n\n const tick = async (): Promise<void> => {\n if (stopped) return\n try {\n await saveGraphToDisk(graph, outPath)\n } catch (err) {\n console.error('persist: periodic save failed', err)\n }\n }\n\n const interval = setInterval(() => {\n void tick()\n }, intervalMs)\n\n const onSignal = (signal: NodeJS.Signals): void => {\n void (async () => {\n try {\n await saveGraphToDisk(graph, outPath)\n } catch (err) {\n console.error(`persist: ${signal} save failed`, err)\n } finally {\n process.exit(0)\n }\n })()\n }\n\n process.on('SIGTERM', onSignal)\n process.on('SIGINT', onSignal)\n\n return () => {\n stopped = true\n clearInterval(interval)\n process.off('SIGTERM', onSignal)\n process.off('SIGINT', onSignal)\n }\n}\n","// Project registry — owns the per-project state that lives alongside the\n// graph map: snapshot/error/stale paths, scan path, optional search index.\n//\n// Routes use it via `resolve(project)`. Server / watch construct it once at\n// boot and pass it to buildApi. Tests can mock it with a small literal.\n\nimport path from 'node:path'\nimport type { NeatGraph } from './graph.js'\nimport { DEFAULT_PROJECT, getGraph } from './graph.js'\nimport type { SearchIndex } from './search.js'\n\nexport interface ProjectPaths {\n snapshotPath: string\n errorsPath: string\n staleEventsPath: string\n embeddingsCachePath: string\n // Policy-violations log per ADR-041 § Append-only ndjson sidecars. Lives\n // in the same neat-out directory as the other ndjson sidecars; daemons\n // wire PolicyViolationsLog to it.\n policyViolationsPath: string\n}\n\n// Default project keeps the legacy filenames so existing M5 / β / γ users\n// see no behaviour change. Named projects fan out by name (ADR-026).\nexport function pathsForProject(project: string, baseDir: string): ProjectPaths {\n if (project === DEFAULT_PROJECT) {\n return {\n snapshotPath: path.join(baseDir, 'graph.json'),\n errorsPath: path.join(baseDir, 'errors.ndjson'),\n staleEventsPath: path.join(baseDir, 'stale-events.ndjson'),\n embeddingsCachePath: path.join(baseDir, 'embeddings.json'),\n policyViolationsPath: path.join(baseDir, 'policy-violations.ndjson'),\n }\n }\n return {\n snapshotPath: path.join(baseDir, `${project}.json`),\n errorsPath: path.join(baseDir, `errors.${project}.ndjson`),\n staleEventsPath: path.join(baseDir, `stale-events.${project}.ndjson`),\n embeddingsCachePath: path.join(baseDir, `embeddings.${project}.json`),\n policyViolationsPath: path.join(baseDir, `policy-violations.${project}.ndjson`),\n }\n}\n\nexport interface ProjectContext {\n name: string\n graph: NeatGraph\n scanPath?: string\n paths: ProjectPaths\n searchIndex?: SearchIndex\n}\n\nexport class Projects {\n private contexts = new Map<string, ProjectContext>()\n\n upsert(ctx: ProjectContext): void {\n this.contexts.set(ctx.name, ctx)\n }\n\n set(\n name: string,\n init: Omit<ProjectContext, 'name' | 'graph'> & { graph?: NeatGraph },\n ): ProjectContext {\n const ctx: ProjectContext = {\n name,\n graph: init.graph ?? getGraph(name),\n scanPath: init.scanPath,\n paths: init.paths,\n searchIndex: init.searchIndex,\n }\n this.contexts.set(name, ctx)\n return ctx\n }\n\n get(name: string): ProjectContext | undefined {\n return this.contexts.get(name)\n }\n\n has(name: string): boolean {\n return this.contexts.has(name)\n }\n\n list(): string[] {\n return [...this.contexts.keys()].sort()\n }\n\n attachSearchIndex(name: string, index: SearchIndex | undefined): void {\n const ctx = this.contexts.get(name)\n if (ctx) ctx.searchIndex = index\n }\n}\n\n// Parses NEAT_PROJECTS=a,b,c; trims whitespace, drops empty entries.\n// `default` is implicit (always loaded), so callers usually filter it out\n// before iterating extra projects.\nexport function parseExtraProjects(raw: string | undefined): string[] {\n if (!raw) return []\n return raw\n .split(',')\n .map((p) => p.trim())\n .filter((p) => p.length > 0 && p !== DEFAULT_PROJECT)\n}\n"],"mappings":"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","names":["path","fs","path","fs","path","NodeType","Provenance","Provenance","NodeType","frontierId","fs","path","EdgeType","NodeType","Provenance","NodeType","Provenance","fs","path","EdgeType","frontierId","NodeType","frontierId","serviceId","Provenance","fs","path","fs","path","NodeType","serviceId","fs","path","extractedEdgeId","fs","path","path","fs","path","fs","serviceId","NodeType","path","fs","NodeType","serviceId","NodeType","path","fs","path","EdgeType","NodeType","Provenance","databaseId","path","path","fs","path","fs","path","parse","fs","path","path","parse","path","parse","parse","path","parse","path","parse","path","parse","path","path","parse","path","databaseId","NodeType","extractedEdgeId","EdgeType","Provenance","path","fs","path","EdgeType","NodeType","Provenance","fs","path","NodeType","extractedEdgeId","EdgeType","Provenance","EdgeType","NodeType","Provenance","path","EdgeType","Provenance","fs","path","fs","path","path","extractedEdgeId","EdgeType","Provenance","path","path","path","infraId","infraId","path","path","infraId","findAll","infraId","path","path","infraId","infraId","path","EdgeType","NodeType","extractedEdgeId","Provenance","path","EdgeType","Provenance","NodeType","infraId","path","extractedEdgeId","EdgeType","Provenance","path","fs","EdgeType","Provenance","path","fs","extractedEdgeId","EdgeType","Provenance","fs","path","fs","path","fs","path","parseAllDocuments","walkYamlFiles","fs","path","parseAllDocuments","fs","path","path","path"]}