@neat.is/core 0.9.0 → 0.9.1

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.
package/dist/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path74 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path74) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path74)) {
63
+ const path76 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
193
193
  };
194
194
  }
195
195
  function resolveProtoRoot() {
196
- const here = import_node_path51.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
- return import_node_path51.default.resolve(here, "..", "proto");
196
+ const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
+ return import_node_path52.default.resolve(here, "..", "proto");
198
198
  }
199
199
  function loadTraceService() {
200
200
  const protoRoot = resolveProtoRoot();
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
262
262
  })
263
263
  };
264
264
  }
265
- var import_node_url, import_node_path51, import_node_crypto2, grpc, protoLoader;
265
+ var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
266
266
  var init_otel_grpc = __esm({
267
267
  "src/otel-grpc.ts"() {
268
268
  "use strict";
269
269
  init_cjs_shims();
270
270
  import_node_url = require("url");
271
- import_node_path51 = __toESM(require("path"), 1);
271
+ import_node_path52 = __toESM(require("path"), 1);
272
272
  import_node_crypto2 = require("crypto");
273
273
  grpc = __toESM(require("@grpc/grpc-js"), 1);
274
274
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
414
414
  const v = attrs[key];
415
415
  if (typeof v === "string" && v.length > 0) {
416
416
  const q = v.indexOf("?");
417
- const path74 = q === -1 ? v : v.slice(0, q);
418
- if (path74.length > 0) return path74;
417
+ const path76 = q === -1 ? v : v.slice(0, q);
418
+ if (path76.length > 0) return path76;
419
419
  }
420
420
  }
421
421
  return void 0;
@@ -477,10 +477,10 @@ function parseOtlpRequest(body) {
477
477
  return out;
478
478
  }
479
479
  function loadProtoRoot() {
480
- const here = import_node_path52.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
- const protoRoot = import_node_path52.default.resolve(here, "..", "proto");
480
+ const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
+ const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
482
482
  const root = new import_protobufjs.default.Root();
483
- root.resolvePath = (_origin, target) => import_node_path52.default.resolve(protoRoot, target);
483
+ root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
484
484
  root.loadSync(
485
485
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
486
486
  { keepCase: true }
@@ -735,12 +735,12 @@ function logSpanHandler(span) {
735
735
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
736
736
  );
737
737
  }
738
- var import_node_path52, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
738
+ var import_node_path53, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
739
739
  var init_otel = __esm({
740
740
  "src/otel.ts"() {
741
741
  "use strict";
742
742
  init_cjs_shims();
743
- import_node_path52 = __toESM(require("path"), 1);
743
+ import_node_path53 = __toESM(require("path"), 1);
744
744
  import_node_url2 = require("url");
745
745
  import_fastify = __toESM(require("fastify"), 1);
746
746
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -1333,19 +1333,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1333
1333
  function longestIncomingWalk(graph, start, maxDepth) {
1334
1334
  let best = { path: [start], edges: [] };
1335
1335
  const visited = /* @__PURE__ */ new Set([start]);
1336
- function step(node, path74, edges) {
1337
- if (path74.length > best.path.length) {
1338
- best = { path: [...path74], edges: [...edges] };
1336
+ function step(node, path76, edges) {
1337
+ if (path76.length > best.path.length) {
1338
+ best = { path: [...path76], edges: [...edges] };
1339
1339
  }
1340
- if (path74.length - 1 >= maxDepth) return;
1340
+ if (path76.length - 1 >= maxDepth) return;
1341
1341
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1342
1342
  for (const [srcId, edge] of incoming) {
1343
1343
  if (visited.has(srcId)) continue;
1344
1344
  visited.add(srcId);
1345
- path74.push(srcId);
1345
+ path76.push(srcId);
1346
1346
  edges.push(edge);
1347
- step(srcId, path74, edges);
1348
- path74.pop();
1347
+ step(srcId, path76, edges);
1348
+ path76.pop();
1349
1349
  edges.pop();
1350
1350
  visited.delete(srcId);
1351
1351
  }
@@ -1353,11 +1353,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1353
1353
  step(start, [start], []);
1354
1354
  return best;
1355
1355
  }
1356
- function databaseRootCauseShape(graph, origin, walk9) {
1356
+ function databaseRootCauseShape(graph, origin, walk10) {
1357
1357
  const targetDb = origin;
1358
1358
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1359
1359
  if (candidatePairs.length === 0) return null;
1360
- for (const id of walk9.path) {
1360
+ for (const id of walk10.path) {
1361
1361
  const owner = resolveOwningService(graph, id);
1362
1362
  if (!owner) continue;
1363
1363
  const { id: serviceId15, svc } = owner;
@@ -1384,8 +1384,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
1384
1384
  }
1385
1385
  return null;
1386
1386
  }
1387
- function serviceRootCauseShape(graph, _origin, walk9) {
1388
- for (const id of walk9.path) {
1387
+ function serviceRootCauseShape(graph, _origin, walk10) {
1388
+ for (const id of walk10.path) {
1389
1389
  const owner = resolveOwningService(graph, id);
1390
1390
  if (!owner) continue;
1391
1391
  const { id: serviceId15, svc } = owner;
@@ -1421,15 +1421,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
1421
1421
  }
1422
1422
  return null;
1423
1423
  }
1424
- function fileRootCauseShape(graph, origin, walk9) {
1424
+ function fileRootCauseShape(graph, origin, walk10) {
1425
1425
  const owner = resolveOwningService(graph, origin.id);
1426
1426
  if (!owner) return null;
1427
- return serviceRootCauseShape(graph, owner.svc, walk9);
1427
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1428
1428
  }
1429
- function symbolRootCauseShape(graph, origin, walk9) {
1429
+ function symbolRootCauseShape(graph, origin, walk10) {
1430
1430
  const owner = resolveOwningService(graph, origin.id);
1431
1431
  if (!owner) return null;
1432
- return serviceRootCauseShape(graph, owner.svc, walk9);
1432
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1433
1433
  }
1434
1434
  var rootCauseShapes = {
1435
1435
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1442,25 +1442,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1442
1442
  const origin = graph.getNodeAttributes(errorNodeId);
1443
1443
  const shape = rootCauseShapes[origin.type];
1444
1444
  if (shape) {
1445
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1446
- const match = shape(graph, origin, walk9);
1445
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1446
+ const match = shape(graph, origin, walk10);
1447
1447
  if (match) {
1448
1448
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1449
- return import_types.RootCauseResultSchema.parse({
1450
- rootCauseNode: match.rootCauseNode,
1451
- rootCauseReason: reason,
1452
- traversalPath: walk9.path,
1453
- edgeProvenances: walk9.edges.map((e) => e.provenance),
1454
- confidence: confidenceFromMix(walk9.edges),
1455
- fixRecommendation: match.fixRecommendation
1456
- });
1449
+ return {
1450
+ source: "compat",
1451
+ result: import_types.RootCauseResultSchema.parse({
1452
+ rootCauseNode: match.rootCauseNode,
1453
+ rootCauseReason: reason,
1454
+ traversalPath: walk10.path,
1455
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
1456
+ confidence: confidenceFromMix(walk10.edges),
1457
+ fixRecommendation: match.fixRecommendation
1458
+ })
1459
+ };
1457
1460
  }
1458
1461
  }
1459
1462
  if (origin.type === import_types.NodeType.ServiceNode) {
1460
1463
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
1461
- if (crossService) return crossService;
1464
+ if (crossService) return { result: crossService, source: "cross-service" };
1462
1465
  }
1463
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1466
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1467
+ return incident ? { result: incident, source: "incident" } : null;
1464
1468
  }
1465
1469
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
1466
1470
  function incidentMatchesNode(ev, nodeId) {
@@ -1556,26 +1560,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
1556
1560
  return best;
1557
1561
  }
1558
1562
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1559
- const path74 = [originServiceId];
1563
+ const path76 = [originServiceId];
1560
1564
  const edges = [];
1561
1565
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1562
1566
  let current = originServiceId;
1563
1567
  for (let depth = 0; depth < maxDepth; depth++) {
1564
1568
  const hop = dominantFailingCall(graph, current, visited);
1565
1569
  if (!hop) break;
1566
- path74.push(hop.nextService);
1570
+ path76.push(hop.nextService);
1571
+ edges.push(hop.edge);
1572
+ visited.add(hop.nextService);
1573
+ current = hop.nextService;
1574
+ }
1575
+ if (edges.length === 0) return null;
1576
+ return { path: path76, edges, culprit: current };
1577
+ }
1578
+ function isStaleCallEdge(e) {
1579
+ return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
1580
+ }
1581
+ function staleCallDominates(e, id, curEdge, curId) {
1582
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
1583
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
1584
+ if (ev !== cv) return ev > cv;
1585
+ return id < curId;
1586
+ }
1587
+ function dominantStaleCall(graph, serviceId15, visited) {
1588
+ const bestByCallee = /* @__PURE__ */ new Map();
1589
+ for (const src of callSourcesForService(graph, serviceId15)) {
1590
+ for (const edgeId of graph.outboundEdges(src)) {
1591
+ const e = graph.getEdgeAttributes(edgeId);
1592
+ if (e.type !== import_types.EdgeType.CALLS) continue;
1593
+ if (isFrontierNode(graph, e.target)) continue;
1594
+ const owner = resolveOwningService(graph, e.target);
1595
+ if (!owner || visited.has(owner.id)) continue;
1596
+ const cur = bestByCallee.get(owner.id);
1597
+ if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
1598
+ bestByCallee.set(owner.id, e);
1599
+ }
1600
+ }
1601
+ }
1602
+ let best = null;
1603
+ for (const [id, edge] of bestByCallee) {
1604
+ if (!isStaleCallEdge(edge)) continue;
1605
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
1606
+ best = { nextService: id, edge };
1607
+ }
1608
+ }
1609
+ return best;
1610
+ }
1611
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
1612
+ const path76 = [originServiceId];
1613
+ const edges = [];
1614
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
1615
+ let current = originServiceId;
1616
+ for (let depth = 0; depth < maxDepth; depth++) {
1617
+ const hop = dominantStaleCall(graph, current, visited);
1618
+ if (!hop) break;
1619
+ path76.push(hop.nextService);
1567
1620
  edges.push(hop.edge);
1568
1621
  visited.add(hop.nextService);
1569
1622
  current = hop.nextService;
1570
1623
  }
1571
1624
  if (edges.length === 0) return null;
1572
- return { path: path74, edges, culprit: current };
1625
+ return { path: path76, edges, culprit: current };
1573
1626
  }
1574
1627
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1575
1628
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1576
1629
  if (!chain) return null;
1577
1630
  const culprit = chain.culprit;
1578
- const path74 = [...chain.path];
1631
+ const path76 = [...chain.path];
1579
1632
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1580
1633
  const baseConfidence = confidenceFromMix(chain.edges);
1581
1634
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1583,14 +1636,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1583
1636
  if (loc) {
1584
1637
  let rootCauseNode = culprit;
1585
1638
  if (loc.fileNode) {
1586
- path74.push(loc.fileNode);
1639
+ path76.push(loc.fileNode);
1587
1640
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1588
1641
  rootCauseNode = loc.fileNode;
1589
1642
  }
1590
1643
  return import_types.RootCauseResultSchema.parse({
1591
1644
  rootCauseNode,
1592
1645
  rootCauseReason: loc.rootCauseReason,
1593
- traversalPath: path74,
1646
+ traversalPath: path76,
1594
1647
  edgeProvenances,
1595
1648
  confidence,
1596
1649
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1602,7 +1655,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1602
1655
  return import_types.RootCauseResultSchema.parse({
1603
1656
  rootCauseNode: culprit,
1604
1657
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1605
- traversalPath: path74,
1658
+ traversalPath: path76,
1606
1659
  edgeProvenances,
1607
1660
  confidence,
1608
1661
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -1989,17 +2042,20 @@ function displayNameOf(nodeId) {
1989
2042
  return nodeId.replace(/^[a-z]+:/, "");
1990
2043
  }
1991
2044
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
1992
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
1993
- if (!legacy) return null;
2045
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
2046
+ if (!tagged) return null;
1994
2047
  const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
1995
- if (!navigation) return legacy;
1996
- return enrichWithNavigation(graph, errorNodeId, legacy, incidents, opts?.now ?? Date.now());
2048
+ if (!navigation) return tagged.result;
2049
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
1997
2050
  }
1998
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2051
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2052
+ const legacy = tagged.result;
1999
2053
  const seedNode = legacy.rootCauseNode;
2000
2054
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
2001
2055
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
2002
2056
  const candidates = [];
2057
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
2058
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
2003
2059
  if (seedCtx && isVictimSeed(seedCtx)) {
2004
2060
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
2005
2061
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -2024,6 +2080,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2024
2080
  confidence: Math.min(legacy.confidence, 0.4),
2025
2081
  ...lastProv ? { provenance: lastProv } : {}
2026
2082
  });
2083
+ } else if (staleChain) {
2084
+ const culprit = staleChain.culprit;
2085
+ const culpritName = displayNameOf(culprit);
2086
+ const seedName = displayNameOf(seedNode);
2087
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
2088
+ candidates.push({
2089
+ node: culprit,
2090
+ classification: "primary-failure",
2091
+ reason: `${culpritName} is the stale-derived root cause (low confidence): live telemetry for this subgraph has gone quiet, but the last-observed topology traces the failure surfacing at ${seedName} downstream through a STALE call chain to ${culpritName}. Provenance is STALE, so confidence is capped low \u2014 restore instrumentation and re-run to confirm before acting.`,
2092
+ context: nodeContext(graph, culprit, incidents, now),
2093
+ confidence: staleConfidence,
2094
+ provenance: import_types.Provenance.STALE
2095
+ });
2096
+ candidates.push({
2097
+ node: seedNode,
2098
+ classification: "symptom-only",
2099
+ reason: `The failure surfaced here, but the only causal chain the graph still holds is STALE and runs downstream \u2014 ${seedName} is the surface of a stale-traced failure, not a proven origin.`,
2100
+ context: seedCtx ?? EMPTY_CONTEXT,
2101
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
2102
+ ...lastProv ? { provenance: lastProv } : {}
2103
+ });
2027
2104
  } else {
2028
2105
  candidates.push({
2029
2106
  node: seedNode,
@@ -2037,11 +2114,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2037
2114
  const top = candidates[0];
2038
2115
  let traversalPath = legacy.traversalPath;
2039
2116
  let edgeProvenances = legacy.edgeProvenances;
2040
- if (top.node !== seedNode) {
2041
- const path74 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2042
- if (path74) {
2043
- traversalPath = path74.nodes;
2044
- edgeProvenances = path74.edges.map((e) => e.provenance);
2117
+ if (staleChain && top.node === staleChain.culprit) {
2118
+ traversalPath = staleChain.path;
2119
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
2120
+ } else if (top.node !== seedNode) {
2121
+ const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2122
+ if (path76) {
2123
+ traversalPath = path76.nodes;
2124
+ edgeProvenances = path76.edges.map((e) => e.provenance);
2045
2125
  } else {
2046
2126
  traversalPath = [errorNodeId, top.node];
2047
2127
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2063,6 +2143,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
2063
2143
  return legacy.fixRecommendation;
2064
2144
  }
2065
2145
  const name = top.node.replace(/^service:/, "");
2146
+ if (top.provenance === import_types.Provenance.STALE) {
2147
+ return `Live telemetry for this path has gone quiet; the last-observed topology traces the failure downstream to ${name}. Restore instrumentation (or re-run with live traces) to confirm, then inspect ${name}.`;
2148
+ }
2066
2149
  if (top.classification === "primary-failure") {
2067
2150
  return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
2068
2151
  }
@@ -2581,6 +2664,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2581
2664
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2582
2665
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2583
2666
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2667
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2584
2668
  var import_types6 = require("@neat.is/types");
2585
2669
 
2586
2670
  // src/extract/shared.ts
@@ -2861,7 +2945,7 @@ function buildServiceHostIndex(services) {
2861
2945
  async function walkSourceFiles(dir, excludeDirs = []) {
2862
2946
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
2863
2947
  const out = [];
2864
- async function walk9(current) {
2948
+ async function walk10(current) {
2865
2949
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2866
2950
  for (const entry of entries) {
2867
2951
  const full = import_node_path5.default.join(current, entry.name);
@@ -2869,7 +2953,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2869
2953
  if (IGNORED_DIRS.has(entry.name)) continue;
2870
2954
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
2871
2955
  if (await isPythonVenvDir(full)) continue;
2872
- await walk9(full);
2956
+ await walk10(full);
2873
2957
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2874
2958
  // would attribute our instrumentation imports to the user's service.
2875
2959
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2877,7 +2961,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2877
2961
  }
2878
2962
  }
2879
2963
  }
2880
- await walk9(dir);
2964
+ await walk10(dir);
2881
2965
  return out;
2882
2966
  }
2883
2967
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3397,6 +3481,11 @@ function makePhpParser() {
3397
3481
  p.setLanguage(import_tree_sitter_php.default.php_only);
3398
3482
  return p;
3399
3483
  }
3484
+ function makeRustParser() {
3485
+ const p = new import_tree_sitter2.default();
3486
+ p.setLanguage(import_tree_sitter_rust.default);
3487
+ return p;
3488
+ }
3400
3489
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
3401
3490
  "get",
3402
3491
  "post",
@@ -3468,8 +3557,8 @@ function chiRoutesFromSource(source, parser) {
3468
3557
  chiWalk(tree.rootNode, "", out);
3469
3558
  return out;
3470
3559
  }
3471
- function stripChiRegex(path74) {
3472
- return path74.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3560
+ function stripChiRegex(path76) {
3561
+ return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3473
3562
  }
3474
3563
  function chiWalk(node, prefix, out) {
3475
3564
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4141,9 +4230,9 @@ function rubyRocketRoute(args) {
4141
4230
  if (!pair || pair.type !== "pair") continue;
4142
4231
  const k = pair.childForFieldName("key");
4143
4232
  if (k?.type !== "string") continue;
4144
- const path74 = rubyLiteral(k);
4145
- if (path74 === null) continue;
4146
- return { path: path74, target: rubyLiteral(pair.childForFieldName("value")) };
4233
+ const path76 = rubyLiteral(k);
4234
+ if (path76 === null) continue;
4235
+ return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
4147
4236
  }
4148
4237
  return null;
4149
4238
  }
@@ -4313,6 +4402,48 @@ function railsRoutesFromSource(source, parser) {
4313
4402
  });
4314
4403
  return out;
4315
4404
  }
4405
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4406
+ function sinatraRoutesFromSource(source, parser) {
4407
+ const tree = parseSource2(parser, source);
4408
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
4409
+ const out = [];
4410
+ walk(tree.rootNode, (node) => {
4411
+ if (node.type !== "call") return;
4412
+ if (node.childForFieldName("receiver")) return;
4413
+ const method = node.childForFieldName("method")?.text;
4414
+ if (!method || !SINATRA_VERBS.has(method)) return;
4415
+ if (!node.childForFieldName("block")) return;
4416
+ const first = node.childForFieldName("arguments")?.namedChild(0);
4417
+ if (first?.type !== "string") return;
4418
+ const p = rubyLiteral(first);
4419
+ if (p === null || !p.startsWith("/")) return;
4420
+ out.push({
4421
+ method: method.toUpperCase(),
4422
+ pathTemplate: canonicalizeTemplate(p),
4423
+ line: node.startPosition.row + 1,
4424
+ framework: "sinatra"
4425
+ });
4426
+ });
4427
+ return out;
4428
+ }
4429
+ function fileReferencesSinatra(root) {
4430
+ let found = false;
4431
+ walk(root, (node) => {
4432
+ if (found) return;
4433
+ if (node.type === "call") {
4434
+ const m = node.childForFieldName("method")?.text;
4435
+ if (m === "require" || m === "require_relative") {
4436
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
4437
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
4438
+ }
4439
+ return;
4440
+ }
4441
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
4442
+ found = true;
4443
+ }
4444
+ });
4445
+ return found;
4446
+ }
4316
4447
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
4317
4448
  var LARAVEL_RESOURCE_ROWS = [
4318
4449
  { action: "index", methods: ["GET"], suffix: "" },
@@ -4488,6 +4619,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
4488
4619
  }
4489
4620
  return out;
4490
4621
  }
4622
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4623
+ function isSlimAppCtor(node) {
4624
+ if (!node) return false;
4625
+ if (node.type === "scoped_call_expression") {
4626
+ const scope = node.childForFieldName("scope")?.text ?? "";
4627
+ const name = node.childForFieldName("name")?.text;
4628
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
4629
+ }
4630
+ if (node.type === "object_creation_expression") {
4631
+ const cls = node.namedChild(0)?.text ?? "";
4632
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
4633
+ }
4634
+ return false;
4635
+ }
4636
+ function isSlimAppType(node) {
4637
+ if (!node || node.type !== "named_type") return false;
4638
+ const text = node.text;
4639
+ return text === "App" || text.endsWith("\\App");
4640
+ }
4641
+ function collectSlimAppVars(root) {
4642
+ const vars = /* @__PURE__ */ new Set();
4643
+ walk(root, (node) => {
4644
+ if (node.type === "assignment_expression") {
4645
+ const left = node.childForFieldName("left");
4646
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
4647
+ vars.add(left.text);
4648
+ }
4649
+ return;
4650
+ }
4651
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
4652
+ const name = node.childForFieldName("name");
4653
+ if (name?.type === "variable_name") vars.add(name.text);
4654
+ }
4655
+ });
4656
+ return vars;
4657
+ }
4658
+ function slimClosureParamVars(closure) {
4659
+ const out = ["$this"];
4660
+ for (let i = 0; i < closure.namedChildCount; i++) {
4661
+ const params = closure.namedChild(i);
4662
+ if (params?.type !== "formal_parameters") continue;
4663
+ for (let j = 0; j < params.namedChildCount; j++) {
4664
+ const param = params.namedChild(j);
4665
+ if (param?.type !== "simple_parameter") continue;
4666
+ for (let k = 0; k < param.namedChildCount; k++) {
4667
+ const v = param.namedChild(k);
4668
+ if (v?.type === "variable_name") {
4669
+ out.push(v.text);
4670
+ break;
4671
+ }
4672
+ }
4673
+ }
4674
+ }
4675
+ return out;
4676
+ }
4677
+ function phpStringArray(node) {
4678
+ const out = [];
4679
+ if (node?.type !== "array_creation_expression") return out;
4680
+ for (let i = 0; i < node.namedChildCount; i++) {
4681
+ const el = node.namedChild(i);
4682
+ if (el?.type !== "array_element_initializer") continue;
4683
+ const s = phpStaticString(el.namedChild(0));
4684
+ if (s !== null) out.push(s);
4685
+ }
4686
+ return out;
4687
+ }
4688
+ function slimRoutesFromSource(source, parser) {
4689
+ const tree = parseSource2(parser, source);
4690
+ const appVars = collectSlimAppVars(tree.rootNode);
4691
+ if (appVars.size === 0) return [];
4692
+ const out = [];
4693
+ slimWalk(tree.rootNode, "", appVars, out);
4694
+ return out;
4695
+ }
4696
+ function slimWalk(node, prefix, appVars, out) {
4697
+ for (let i = 0; i < node.namedChildCount; i++) {
4698
+ const child = node.namedChild(i);
4699
+ if (child) slimHandle(child, prefix, appVars, out);
4700
+ }
4701
+ }
4702
+ function slimHandle(node, prefix, appVars, out) {
4703
+ if (node.type === "member_call_expression") {
4704
+ const obj = node.childForFieldName("object");
4705
+ const method = node.childForFieldName("name")?.text;
4706
+ const args = node.childForFieldName("arguments");
4707
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4708
+ const line = node.startPosition.row + 1;
4709
+ if (method === "group") {
4710
+ const groupPrefix = phpFirstString(args);
4711
+ const closure = laravelGroupClosure(args);
4712
+ if (groupPrefix !== null && closure) {
4713
+ const inner = new Set(appVars);
4714
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4715
+ const body = closure.childForFieldName("body");
4716
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4717
+ }
4718
+ return;
4719
+ }
4720
+ if (SLIM_VERBS.has(method)) {
4721
+ const p = phpFirstString(args);
4722
+ if (p !== null) {
4723
+ out.push({
4724
+ method: method.toUpperCase(),
4725
+ pathTemplate: laravelJoinPath(prefix, p),
4726
+ line,
4727
+ framework: "slim"
4728
+ });
4729
+ }
4730
+ return;
4731
+ }
4732
+ if (method === "any") {
4733
+ const p = phpFirstString(args);
4734
+ if (p !== null) {
4735
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4736
+ }
4737
+ return;
4738
+ }
4739
+ if (method === "map") {
4740
+ const vals = phpArgumentValues(args);
4741
+ const methods = phpStringArray(vals[0]);
4742
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4743
+ if (p !== null) {
4744
+ for (const m of methods) {
4745
+ out.push({
4746
+ method: m.toUpperCase(),
4747
+ pathTemplate: laravelJoinPath(prefix, p),
4748
+ line,
4749
+ framework: "slim"
4750
+ });
4751
+ }
4752
+ }
4753
+ return;
4754
+ }
4755
+ }
4756
+ }
4757
+ slimWalk(node, prefix, appVars, out);
4758
+ }
4759
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4760
+ function rustStringContent(node) {
4761
+ if (!node || node.type !== "string_literal") return null;
4762
+ for (let i = 0; i < node.namedChildCount; i++) {
4763
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4764
+ }
4765
+ return "";
4766
+ }
4767
+ function actixRoutesFromSource(source, parser) {
4768
+ const tree = parseSource2(parser, source);
4769
+ const out = [];
4770
+ walk(tree.rootNode, (node) => {
4771
+ if (node.type === "attribute_item") {
4772
+ actixAttributeRoute(node, out);
4773
+ return;
4774
+ }
4775
+ if (node.type === "call_expression") {
4776
+ actixBuilderRoute(node, out);
4777
+ }
4778
+ });
4779
+ return out;
4780
+ }
4781
+ function actixAttributeRoute(attrItem, out) {
4782
+ const attr = attrItem.namedChild(0);
4783
+ if (!attr || attr.type !== "attribute") return;
4784
+ const nameNode = attr.namedChild(0);
4785
+ if (!nameNode) return;
4786
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4787
+ if (!macro) return;
4788
+ const tokens = attr.childForFieldName("arguments");
4789
+ if (!tokens || tokens.type !== "token_tree") return;
4790
+ const strings = [];
4791
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4792
+ const s = rustStringContent(tokens.namedChild(i));
4793
+ if (s !== null) strings.push(s);
4794
+ }
4795
+ const pathStr = strings[0];
4796
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4797
+ const line = attrItem.startPosition.row + 1;
4798
+ const template = canonicalizeTemplate(pathStr);
4799
+ if (ACTIX_METHODS.has(macro)) {
4800
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4801
+ return;
4802
+ }
4803
+ if (macro === "route") {
4804
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4805
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4806
+ for (const m of list) {
4807
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4808
+ }
4809
+ }
4810
+ }
4811
+ function actixBuilderRoute(call, out) {
4812
+ const fn = call.childForFieldName("function");
4813
+ if (fn?.type !== "field_expression") return;
4814
+ if (fn.childForFieldName("field")?.text !== "route") return;
4815
+ const args = call.childForFieldName("arguments");
4816
+ const pathStr = rustStringContent(args?.namedChild(0));
4817
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4818
+ const second = args?.namedChild(1);
4819
+ if (!second) return;
4820
+ const method = actixBuilderMethod(second);
4821
+ if (!method) return;
4822
+ out.push({
4823
+ method,
4824
+ pathTemplate: canonicalizeTemplate(pathStr),
4825
+ line: call.startPosition.row + 1,
4826
+ framework: "actix-web"
4827
+ });
4828
+ }
4829
+ function actixBuilderMethod(node) {
4830
+ let method = null;
4831
+ walk(node, (n) => {
4832
+ if (method || n.type !== "scoped_identifier") return;
4833
+ const verb = n.childForFieldName("name")?.text;
4834
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4835
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4836
+ });
4837
+ return method;
4838
+ }
4491
4839
  function namedArgs(argsNode) {
4492
4840
  const out = [];
4493
4841
  if (!argsNode) return out;
@@ -4804,6 +5152,7 @@ async function addRoutes(graph, services) {
4804
5152
  const goParser = makeGoParser2();
4805
5153
  const rubyParser = makeRubyParser();
4806
5154
  const phpParser = makePhpParser();
5155
+ const rustParser = makeRustParser();
4807
5156
  let nodesAdded = 0;
4808
5157
  let edgesAdded = 0;
4809
5158
  for (const service of services) {
@@ -4826,7 +5175,10 @@ async function addRoutes(graph, services) {
4826
5175
  const isGoService = service.node.language === "go";
4827
5176
  const hasRails = deps["rails"] !== void 0;
4828
5177
  const hasLaravel = deps["laravel/framework"] !== void 0;
4829
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
5178
+ const hasSlim = deps["slim/slim"] !== void 0;
5179
+ const hasSinatra = deps["sinatra"] !== void 0;
5180
+ const hasActix = deps["actix-web"] !== void 0;
5181
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4830
5182
  continue;
4831
5183
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4832
5184
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4837,7 +5189,8 @@ async function addRoutes(graph, services) {
4837
5189
  const isGo = ext === ".go";
4838
5190
  const isRb = ext === ".rb";
4839
5191
  const isPhp = ext === ".php";
4840
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
5192
+ const isRs = ext === ".rs";
5193
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4841
5194
  const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
4842
5195
  let routes;
4843
5196
  try {
@@ -4847,8 +5200,12 @@ async function addRoutes(graph, services) {
4847
5200
  phpParser,
4848
5201
  relFile === "routes/api.php" ? "/api" : ""
4849
5202
  ) : [];
5203
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4850
5204
  } else if (isRb) {
4851
5205
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
5206
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
5207
+ } else if (isRs) {
5208
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4852
5209
  } else if (isGo) {
4853
5210
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4854
5211
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -5083,6 +5440,37 @@ function loadIncidentThresholdsFromEnv() {
5083
5440
  return DEFAULT_INCIDENT_THRESHOLDS;
5084
5441
  }
5085
5442
  }
5443
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
5444
+ function latencyStreamCeilingMs() {
5445
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
5446
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
5447
+ const n = Number(raw);
5448
+ if (Number.isFinite(n) && n > 0) return n;
5449
+ console.warn(
5450
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
5451
+ );
5452
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
5453
+ }
5454
+ function spanServesEventStream(attrs) {
5455
+ for (const key of [
5456
+ "http.response.header.content-type",
5457
+ "http.response.header.content_type"
5458
+ ]) {
5459
+ const v = attrs[key];
5460
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
5461
+ for (const item of values) {
5462
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
5463
+ return true;
5464
+ }
5465
+ }
5466
+ }
5467
+ return false;
5468
+ }
5469
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
5470
+ if (span.websocketChannel !== void 0) return true;
5471
+ if (spanServesEventStream(span.attributes)) return true;
5472
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
5473
+ }
5086
5474
  function httpResponseStatusFromAttrs(attrs) {
5087
5475
  for (const key of ["http.response.status_code", "http.status_code"]) {
5088
5476
  const v = attrs[key];
@@ -5975,6 +6363,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5975
6363
  );
5976
6364
  ctx.burstState.delete(key);
5977
6365
  }
6366
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
6367
+ function nextApiRouteTemplate(span) {
6368
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
6369
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
6370
+ return match ? match[1] : void 0;
6371
+ }
5978
6372
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5979
6373
  const target = normalizePathTemplate(httpRoute);
5980
6374
  const m = method?.toUpperCase();
@@ -5997,7 +6391,7 @@ async function handleSpan(ctx, span) {
5997
6391
  }
5998
6392
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5999
6393
  const isError = span.statusCode === 2;
6000
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
6394
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
6001
6395
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
6002
6396
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
6003
6397
  cacheSpanService(span, nowMs, callSite);
@@ -6194,12 +6588,13 @@ async function handleSpan(ctx, span) {
6194
6588
  }
6195
6589
  }
6196
6590
  }
6197
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6591
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
6592
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6198
6593
  const routeNodeId = findRouteNodeByHttpRoute(
6199
6594
  ctx.graph,
6200
6595
  span.service,
6201
6596
  span.httpMethod,
6202
- span.httpRoute
6597
+ fusionRoute
6203
6598
  );
6204
6599
  if (routeNodeId) {
6205
6600
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -7590,7 +7985,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
7590
7985
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
7591
7986
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
7592
7987
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
7593
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
7988
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
7594
7989
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
7595
7990
  var import_types20 = require("@neat.is/types");
7596
7991
  var PARSE_CHUNK3 = 16384;
@@ -7611,7 +8006,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
7611
8006
  ".cs": import_tree_sitter_c_sharp.default,
7612
8007
  ".java": import_tree_sitter_java.default,
7613
8008
  ".kt": import_tree_sitter_kotlin.default,
7614
- ".rs": import_tree_sitter_rust.default,
8009
+ ".rs": import_tree_sitter_rust2.default,
7615
8010
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
7616
8011
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
7617
8012
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8051,15 +8446,15 @@ function collectKotlinSymbolDefs(root) {
8051
8446
  });
8052
8447
  };
8053
8448
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8054
- const firstChildOfType = (node, types) => {
8449
+ const firstChildOfType2 = (node, types) => {
8055
8450
  for (let i = 0; i < node.namedChildCount; i++) {
8056
8451
  const child = node.namedChild(i);
8057
8452
  if (child && types.includes(child.type)) return child;
8058
8453
  }
8059
8454
  return void 0;
8060
8455
  };
8061
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8062
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
8456
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
8457
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8063
8458
  let pkg;
8064
8459
  for (let i = 0; i < root.namedChildCount; i++) {
8065
8460
  const child = root.namedChild(i);
@@ -8560,7 +8955,7 @@ async function addSymbolEdges(graph, services) {
8560
8955
  return best;
8561
8956
  };
8562
8957
  const requests = [];
8563
- const walk9 = (node) => {
8958
+ const walk10 = (node) => {
8564
8959
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
8565
8960
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
8566
8961
  if (self && self.kind === "class") {
@@ -8606,10 +9001,10 @@ async function addSymbolEdges(graph, services) {
8606
9001
  }
8607
9002
  for (let i = 0; i < node.namedChildCount; i++) {
8608
9003
  const child = node.namedChild(i);
8609
- if (child) walk9(child);
9004
+ if (child) walk10(child);
8610
9005
  }
8611
9006
  };
8612
- walk9(root);
9007
+ walk10(root);
8613
9008
  for (const req of requests) {
8614
9009
  const targetSid = resolveTarget(req.targetName, req.wantKind);
8615
9010
  if (!targetSid) continue;
@@ -8905,7 +9300,7 @@ async function addServerActions(graph, services) {
8905
9300
 
8906
9301
  // src/extract/databases/index.ts
8907
9302
  init_cjs_shims();
8908
- var import_node_path33 = __toESM(require("path"), 1);
9303
+ var import_node_path34 = __toESM(require("path"), 1);
8909
9304
  var import_types23 = require("@neat.is/types");
8910
9305
 
8911
9306
  // src/extract/databases/db-config-yaml.ts
@@ -9340,9 +9735,163 @@ async function parse8(serviceDir) {
9340
9735
  }
9341
9736
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9342
9737
 
9343
- // src/extract/databases/docker-compose.ts
9738
+ // src/extract/databases/csharp.ts
9344
9739
  init_cjs_shims();
9740
+ var import_node_fs22 = require("fs");
9345
9741
  var import_node_path32 = __toESM(require("path"), 1);
9742
+ var CS_EXT = ".cs";
9743
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
9744
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
9745
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
9746
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
9747
+ function hostIsUnresolved(host) {
9748
+ return host === "" || /[${}]/.test(host);
9749
+ }
9750
+ function looksLikePostgres(s) {
9751
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
9752
+ }
9753
+ function looksLikeRedis(s) {
9754
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
9755
+ }
9756
+ function parsePostgresConnection(raw) {
9757
+ const s = raw.trim();
9758
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
9759
+ const fields = /* @__PURE__ */ new Map();
9760
+ for (const part of s.split(";")) {
9761
+ const eq = part.indexOf("=");
9762
+ if (eq < 0) continue;
9763
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
9764
+ const value = part.slice(eq + 1).trim();
9765
+ if (value && !fields.has(key)) fields.set(key, value);
9766
+ }
9767
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
9768
+ if (!hostRaw) return null;
9769
+ const host = hostRaw.split(",")[0].trim();
9770
+ if (hostIsUnresolved(host)) return null;
9771
+ const portRaw = fields.get("port");
9772
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
9773
+ const database = fields.get("database") ?? fields.get("db") ?? "";
9774
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
9775
+ }
9776
+ function parseRedisEndpoint(raw) {
9777
+ const s = raw.trim();
9778
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
9779
+ const first = s.split(",")[0].trim();
9780
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
9781
+ if (!m) return null;
9782
+ const host = m[1];
9783
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
9784
+ const port = m[2] ? Number(m[2]) : void 0;
9785
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
9786
+ }
9787
+ async function resolveEnvUpTree(startDir, name) {
9788
+ let dir = import_node_path32.default.resolve(startDir);
9789
+ for (let depth = 0; depth < 12; depth++) {
9790
+ const value = await resolveEnvVar(dir, name);
9791
+ if (value !== null) return value;
9792
+ const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
9793
+ const parent = import_node_path32.default.dirname(dir);
9794
+ if (atRepoRoot || parent === dir) break;
9795
+ dir = parent;
9796
+ }
9797
+ return null;
9798
+ }
9799
+ async function interpolateEnvRefs(value, dir) {
9800
+ const refs = /* @__PURE__ */ new Set();
9801
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
9802
+ refs.add(m[1] ?? m[2]);
9803
+ }
9804
+ let out = value;
9805
+ for (const name of refs) {
9806
+ const resolved = await resolveEnvUpTree(dir, name);
9807
+ if (resolved === null) continue;
9808
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
9809
+ }
9810
+ return out;
9811
+ }
9812
+ function stringLiterals(masked) {
9813
+ const out = [];
9814
+ STRING_LITERAL_RE.lastIndex = 0;
9815
+ let m;
9816
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
9817
+ return out;
9818
+ }
9819
+ function envKeys(masked) {
9820
+ const out = [];
9821
+ ENV_READ_RE.lastIndex = 0;
9822
+ let m;
9823
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
9824
+ const key = m[1] ?? m[2];
9825
+ if (key) out.push(key);
9826
+ }
9827
+ return out;
9828
+ }
9829
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
9830
+ const out = [];
9831
+ for (const key of keys) {
9832
+ const raw = await resolveEnvUpTree(serviceDir, key);
9833
+ if (raw === null) continue;
9834
+ const value = await interpolateEnvRefs(raw, serviceDir);
9835
+ if (!looksLike(value)) continue;
9836
+ const parsed = parse11(value);
9837
+ if (parsed) out.push(parsed);
9838
+ }
9839
+ for (const lit of literals) {
9840
+ if (!looksLike(lit)) continue;
9841
+ const parsed = parse11(lit);
9842
+ if (parsed) out.push(parsed);
9843
+ }
9844
+ return out;
9845
+ }
9846
+ async function parse9(serviceDir) {
9847
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
9848
+ (f) => import_node_path32.default.extname(f) === CS_EXT
9849
+ );
9850
+ if (files.length === 0) return [];
9851
+ const sources = [];
9852
+ for (const file of files) {
9853
+ const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
9854
+ if (content !== null) sources.push({ file, content });
9855
+ }
9856
+ let pgGateFile = null;
9857
+ let redisGateFile = null;
9858
+ for (const { file, content } of sources) {
9859
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
9860
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
9861
+ }
9862
+ if (!pgGateFile && !redisGateFile) return [];
9863
+ const literals = [];
9864
+ const keys = [];
9865
+ for (const { content } of sources) {
9866
+ const masked = maskCommentsInSource(content);
9867
+ literals.push(...stringLiterals(masked));
9868
+ keys.push(...envKeys(masked));
9869
+ }
9870
+ const out = [];
9871
+ const seenHosts = /* @__PURE__ */ new Set();
9872
+ const push = (config, sourceFile) => {
9873
+ const dedupe = `${config.engine}:${config.host}`;
9874
+ if (seenHosts.has(dedupe)) return;
9875
+ seenHosts.add(dedupe);
9876
+ out.push({ ...config, sourceFile });
9877
+ };
9878
+ if (pgGateFile) {
9879
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
9880
+ push(pg3, pgGateFile);
9881
+ }
9882
+ }
9883
+ if (redisGateFile) {
9884
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
9885
+ push(redis, redisGateFile);
9886
+ }
9887
+ }
9888
+ return out;
9889
+ }
9890
+ var csharpParser = { name: "csharp", parse: parse9 };
9891
+
9892
+ // src/extract/databases/docker-compose.ts
9893
+ init_cjs_shims();
9894
+ var import_node_path33 = __toESM(require("path"), 1);
9346
9895
  function portFromService(svc) {
9347
9896
  for (const raw of svc.ports ?? []) {
9348
9897
  const str = String(raw);
@@ -9367,9 +9916,9 @@ function databaseFromEnv(svc) {
9367
9916
  };
9368
9917
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9369
9918
  }
9370
- async function parse9(serviceDir) {
9919
+ async function parse10(serviceDir) {
9371
9920
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9372
- const abs = import_node_path32.default.join(serviceDir, name);
9921
+ const abs = import_node_path33.default.join(serviceDir, name);
9373
9922
  if (!await exists(abs)) continue;
9374
9923
  const raw = await readYaml(abs);
9375
9924
  if (!raw?.services) return [];
@@ -9391,7 +9940,7 @@ async function parse9(serviceDir) {
9391
9940
  }
9392
9941
  return [];
9393
9942
  }
9394
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
9943
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9395
9944
 
9396
9945
  // src/extract/databases/index.ts
9397
9946
  var DB_PARSERS = [
@@ -9403,6 +9952,7 @@ var DB_PARSERS = [
9403
9952
  ormconfigParser,
9404
9953
  typeormParser,
9405
9954
  sequelizeParser,
9955
+ csharpParser,
9406
9956
  dockerComposeParser
9407
9957
  ];
9408
9958
  function compatibleDriversFor(engine) {
@@ -9541,7 +10091,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9541
10091
  discoveredVia: mergedDiscoveredVia
9542
10092
  });
9543
10093
  }
9544
- const relConfigFile = toPosix(import_node_path33.default.relative(service.dir, config.sourceFile));
10094
+ const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
9545
10095
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9546
10096
  graph,
9547
10097
  service.pkg.name,
@@ -9550,7 +10100,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9550
10100
  );
9551
10101
  nodesAdded += fn;
9552
10102
  edgesAdded += fe;
9553
- const evidenceFile = toPosix(import_node_path33.default.relative(scanPath, config.sourceFile));
10103
+ const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
9554
10104
  const edge = {
9555
10105
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
9556
10106
  source: fileNodeId,
@@ -9568,15 +10118,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9568
10118
  if (allConfigs.length === 1) {
9569
10119
  const primary = allConfigs[0];
9570
10120
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
9571
- const relPath = import_node_path33.default.relative(scanPath, primary.sourceFile);
10121
+ const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
9572
10122
  const cfgId = (0, import_types23.configId)(relPath);
9573
10123
  if (!graph.hasNode(cfgId)) {
9574
10124
  const cfgNode = {
9575
10125
  id: cfgId,
9576
10126
  type: import_types23.NodeType.ConfigNode,
9577
- name: import_node_path33.default.basename(primary.sourceFile),
10127
+ name: import_node_path34.default.basename(primary.sourceFile),
9578
10128
  path: relPath,
9579
- fileType: isConfigFile(import_node_path33.default.basename(primary.sourceFile)).fileType || "config"
10129
+ fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
9580
10130
  };
9581
10131
  graph.addNode(cfgId, cfgNode);
9582
10132
  nodesAdded++;
@@ -9617,27 +10167,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9617
10167
 
9618
10168
  // src/extract/configs.ts
9619
10169
  init_cjs_shims();
9620
- var import_node_fs22 = require("fs");
9621
- var import_node_path34 = __toESM(require("path"), 1);
10170
+ var import_node_fs23 = require("fs");
10171
+ var import_node_path35 = __toESM(require("path"), 1);
9622
10172
  var import_types24 = require("@neat.is/types");
9623
10173
  async function walkConfigFiles(dir, excludeDirs = []) {
9624
- const excluded = new Set(excludeDirs.map((d) => import_node_path34.default.resolve(d)));
10174
+ const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
9625
10175
  const out = [];
9626
- async function walk9(current) {
9627
- const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true });
10176
+ async function walk10(current) {
10177
+ const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
9628
10178
  for (const entry of entries) {
9629
- const full = import_node_path34.default.join(current, entry.name);
10179
+ const full = import_node_path35.default.join(current, entry.name);
9630
10180
  if (entry.isDirectory()) {
9631
10181
  if (IGNORED_DIRS.has(entry.name)) continue;
9632
- if (excluded.has(import_node_path34.default.resolve(full))) continue;
10182
+ if (excluded.has(import_node_path35.default.resolve(full))) continue;
9633
10183
  if (await isPythonVenvDir(full)) continue;
9634
- await walk9(full);
10184
+ await walk10(full);
9635
10185
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
9636
10186
  out.push(full);
9637
10187
  }
9638
10188
  }
9639
10189
  }
9640
- await walk9(dir);
10190
+ await walk10(dir);
9641
10191
  return out;
9642
10192
  }
9643
10193
  async function addConfigNodes(graph, services, scanPath) {
@@ -9646,19 +10196,19 @@ async function addConfigNodes(graph, services, scanPath) {
9646
10196
  for (const service of services) {
9647
10197
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
9648
10198
  for (const file of configFiles) {
9649
- const relPath = import_node_path34.default.relative(scanPath, file);
10199
+ const relPath = import_node_path35.default.relative(scanPath, file);
9650
10200
  const node = {
9651
10201
  id: (0, import_types24.configId)(relPath),
9652
10202
  type: import_types24.NodeType.ConfigNode,
9653
- name: import_node_path34.default.basename(file),
10203
+ name: import_node_path35.default.basename(file),
9654
10204
  path: relPath,
9655
- fileType: isConfigFile(import_node_path34.default.basename(file)).fileType
10205
+ fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
9656
10206
  };
9657
10207
  if (!graph.hasNode(node.id)) {
9658
10208
  graph.addNode(node.id, node);
9659
10209
  nodesAdded++;
9660
10210
  }
9661
- const relToService = toPosix(import_node_path34.default.relative(service.dir, file));
10211
+ const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
9662
10212
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9663
10213
  graph,
9664
10214
  service.pkg.name,
@@ -9674,7 +10224,7 @@ async function addConfigNodes(graph, services, scanPath) {
9674
10224
  type: import_types24.EdgeType.CONFIGURED_BY,
9675
10225
  provenance: import_types24.Provenance.EXTRACTED,
9676
10226
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
9677
- evidence: { file: relPath.split(import_node_path34.default.sep).join("/") }
10227
+ evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
9678
10228
  };
9679
10229
  if (!graph.hasEdge(edge.id)) {
9680
10230
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -9687,8 +10237,8 @@ async function addConfigNodes(graph, services, scanPath) {
9687
10237
 
9688
10238
  // src/extract/proto.ts
9689
10239
  init_cjs_shims();
9690
- var import_node_fs23 = require("fs");
9691
- var import_node_path35 = __toESM(require("path"), 1);
10240
+ var import_node_fs24 = require("fs");
10241
+ var import_node_path36 = __toESM(require("path"), 1);
9692
10242
  var import_types25 = require("@neat.is/types");
9693
10243
  var PROTO_EXTENSION = ".proto";
9694
10244
  function packageOf(content) {
@@ -9726,23 +10276,23 @@ function grpcMethodsFromProto(content, fqPackage) {
9726
10276
  return out;
9727
10277
  }
9728
10278
  async function walkProtoFiles(dir, excludeDirs = []) {
9729
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10279
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
9730
10280
  const out = [];
9731
- async function walk9(current) {
9732
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10281
+ async function walk10(current) {
10282
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9733
10283
  for (const entry of entries) {
9734
- const full = import_node_path35.default.join(current, entry.name);
10284
+ const full = import_node_path36.default.join(current, entry.name);
9735
10285
  if (entry.isDirectory()) {
9736
10286
  if (IGNORED_DIRS.has(entry.name)) continue;
9737
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10287
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
9738
10288
  if (await isPythonVenvDir(full)) continue;
9739
- await walk9(full);
9740
- } else if (entry.isFile() && import_node_path35.default.extname(entry.name) === PROTO_EXTENSION) {
10289
+ await walk10(full);
10290
+ } else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
9741
10291
  out.push(full);
9742
10292
  }
9743
10293
  }
9744
10294
  }
9745
- await walk9(dir);
10295
+ await walk10(dir);
9746
10296
  return out;
9747
10297
  }
9748
10298
  async function addGrpcMethods(graph, services) {
@@ -9752,10 +10302,10 @@ async function addGrpcMethods(graph, services) {
9752
10302
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
9753
10303
  for (const protoPath of protoPaths) {
9754
10304
  if (isTestPath(protoPath)) continue;
9755
- const relFile = toPosix(import_node_path35.default.relative(service.dir, protoPath));
10305
+ const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
9756
10306
  let content;
9757
10307
  try {
9758
- content = await import_node_fs23.promises.readFile(protoPath, "utf8");
10308
+ content = await import_node_fs24.promises.readFile(protoPath, "utf8");
9759
10309
  } catch (err) {
9760
10310
  recordExtractionError("proto extraction", protoPath, err);
9761
10311
  continue;
@@ -9810,11 +10360,11 @@ async function addGrpcMethods(graph, services) {
9810
10360
 
9811
10361
  // src/extract/calls/index.ts
9812
10362
  init_cjs_shims();
9813
- var import_types43 = require("@neat.is/types");
10363
+ var import_types44 = require("@neat.is/types");
9814
10364
 
9815
10365
  // src/extract/calls/http.ts
9816
10366
  init_cjs_shims();
9817
- var import_node_path36 = __toESM(require("path"), 1);
10367
+ var import_node_path37 = __toESM(require("path"), 1);
9818
10368
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
9819
10369
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
9820
10370
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -9897,7 +10447,7 @@ async function addHttpCallEdges(graph, services) {
9897
10447
  const seen = /* @__PURE__ */ new Set();
9898
10448
  for (const file of files) {
9899
10449
  if (isTestPath(file.path)) continue;
9900
- const parser = parserForExt(import_node_path36.default.extname(file.path), parserCache);
10450
+ const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
9901
10451
  let sites;
9902
10452
  try {
9903
10453
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9906,7 +10456,7 @@ async function addHttpCallEdges(graph, services) {
9906
10456
  continue;
9907
10457
  }
9908
10458
  if (sites.length === 0) continue;
9909
- const relFile = toPosix(import_node_path36.default.relative(service.dir, file.path));
10459
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
9910
10460
  for (const site of sites) {
9911
10461
  const targetId = hostToNodeId.get(site.host);
9912
10462
  if (!targetId || targetId === service.node.id) continue;
@@ -9960,7 +10510,7 @@ async function addHttpCallEdges(graph, services) {
9960
10510
 
9961
10511
  // src/extract/calls/route-match.ts
9962
10512
  init_cjs_shims();
9963
- var import_node_path37 = __toESM(require("path"), 1);
10513
+ var import_node_path38 = __toESM(require("path"), 1);
9964
10514
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
9965
10515
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
9966
10516
  var import_types27 = require("@neat.is/types");
@@ -10159,7 +10709,7 @@ async function addRouteCallEdges(graph, services) {
10159
10709
  const seen = /* @__PURE__ */ new Set();
10160
10710
  for (const file of files) {
10161
10711
  if (isTestPath(file.path)) continue;
10162
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path37.default.extname(file.path))) continue;
10712
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
10163
10713
  let sites;
10164
10714
  try {
10165
10715
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10168,7 +10718,7 @@ async function addRouteCallEdges(graph, services) {
10168
10718
  continue;
10169
10719
  }
10170
10720
  if (sites.length === 0) continue;
10171
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
10721
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10172
10722
  for (const site of sites) {
10173
10723
  const serverServiceId = hostToNodeId.get(site.host);
10174
10724
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10229,10 +10779,15 @@ async function addRouteCallEdges(graph, services) {
10229
10779
 
10230
10780
  // src/extract/calls/kafka.ts
10231
10781
  init_cjs_shims();
10232
- var import_node_path38 = __toESM(require("path"), 1);
10782
+ var import_node_path39 = __toESM(require("path"), 1);
10233
10783
  var import_types28 = require("@neat.is/types");
10234
10784
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10235
10785
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
10786
+ var SARAMA_IMPORT_RE = /"[^"]*\/sarama"/;
10787
+ var GO_PRODUCER_RE = /ProducerMessage\s*\{[\s\S]{0,300}?\bTopic\s*:\s*(?:"([^"]+)"|`([^`]+)`|([A-Za-z_]\w*))/g;
10788
+ var GO_CONSUMER_RE = /\.Consume\s*\([\s\S]{0,120}?\[\]string\s*\{([^}]*)\}/g;
10789
+ var GO_STRING_LITERAL_RE = /"([^"]+)"|`([^`]+)`/g;
10790
+ var GO_IDENT_RE = /[A-Za-z_]\w*/g;
10236
10791
  function findAll(re, text) {
10237
10792
  re.lastIndex = 0;
10238
10793
  const out = [];
@@ -10242,38 +10797,94 @@ function findAll(re, text) {
10242
10797
  }
10243
10798
  return out;
10244
10799
  }
10800
+ function resolveGoLiteral(ident, text) {
10801
+ const esc = ident.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10802
+ const re = new RegExp(
10803
+ `\\b${esc}(?:\\s+\\w[\\w.\\[\\]*]*)?\\s*(?::=|=)\\s*(?:"([^"]+)"|\`([^\`]+)\`)`
10804
+ );
10805
+ const m = re.exec(text);
10806
+ if (!m) return void 0;
10807
+ return m[1] ?? m[2];
10808
+ }
10809
+ function producerTopicAnchor(match) {
10810
+ const [full] = match;
10811
+ const tokenLen = match[1] ? match[1].length + 2 : match[2] ? match[2].length + 2 : match[3]?.length ?? 0;
10812
+ return match.index + full.length - tokenLen;
10813
+ }
10814
+ function goSaramaEndpoints(text, emit) {
10815
+ if (!SARAMA_IMPORT_RE.test(text)) return;
10816
+ GO_PRODUCER_RE.lastIndex = 0;
10817
+ let m;
10818
+ while ((m = GO_PRODUCER_RE.exec(text)) !== null) {
10819
+ const literal = m[1] ?? m[2];
10820
+ const anchor = producerTopicAnchor(m);
10821
+ if (literal) {
10822
+ emit(literal, "PUBLISHES_TO", anchor);
10823
+ } else if (m[3]) {
10824
+ const resolved = resolveGoLiteral(m[3], text);
10825
+ if (resolved) emit(resolved, "PUBLISHES_TO", anchor);
10826
+ }
10827
+ }
10828
+ GO_CONSUMER_RE.lastIndex = 0;
10829
+ while ((m = GO_CONSUMER_RE.exec(text)) !== null) {
10830
+ const inside = m[1] ?? "";
10831
+ const anchor = m.index + Math.max(0, m[0].indexOf("[]string"));
10832
+ let sawLiteral = false;
10833
+ GO_STRING_LITERAL_RE.lastIndex = 0;
10834
+ let s;
10835
+ while ((s = GO_STRING_LITERAL_RE.exec(inside)) !== null) {
10836
+ const topic = s[1] ?? s[2];
10837
+ if (topic) {
10838
+ emit(topic, "CONSUMES_FROM", anchor);
10839
+ sawLiteral = true;
10840
+ }
10841
+ }
10842
+ if (sawLiteral) continue;
10843
+ GO_IDENT_RE.lastIndex = 0;
10844
+ let id;
10845
+ while ((id = GO_IDENT_RE.exec(inside)) !== null) {
10846
+ const resolved = resolveGoLiteral(id[0], text);
10847
+ if (resolved) emit(resolved, "CONSUMES_FROM", anchor);
10848
+ }
10849
+ }
10850
+ }
10245
10851
  function kafkaEndpointsFromFile(file, serviceDir) {
10246
10852
  const out = [];
10247
10853
  const seen = /* @__PURE__ */ new Set();
10248
- const make = (topic, edgeType) => {
10854
+ const make = (topic, edgeType, anchor) => {
10249
10855
  const key = `${edgeType}|${topic}`;
10250
10856
  if (seen.has(key)) return;
10251
10857
  seen.add(key);
10252
- const line = lineOf(file.content, topic);
10858
+ const line = anchor !== void 0 ? file.content.slice(0, anchor).split("\n").length : lineOf(file.content, topic);
10253
10859
  out.push({
10254
10860
  infraId: (0, import_types28.infraId)("kafka-topic", topic),
10255
10861
  name: topic,
10256
10862
  kind: "kafka-topic",
10257
10863
  edgeType,
10258
- // `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
10259
- // framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
10260
- // tier (ADR-066).
10864
+ // JS: `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
10865
+ // (kafkajs / node-rdkafka). Go: `sarama.ProducerMessage{Topic: 'x'}` /
10866
+ // `consumerGroup.Consume(ctx, []string{'x'}, …)`. Both are framework-aware
10867
+ // call sites — verified-call-site tier (ADR-066).
10261
10868
  confidenceKind: "verified-call-site",
10262
10869
  evidence: {
10263
- file: import_node_path38.default.relative(serviceDir, file.path),
10870
+ file: import_node_path39.default.relative(serviceDir, file.path),
10264
10871
  line,
10265
10872
  snippet: snippet(file.content, line)
10266
10873
  }
10267
10874
  });
10268
10875
  };
10269
- for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
10270
- for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
10876
+ if (import_node_path39.default.extname(file.path) === ".go") {
10877
+ goSaramaEndpoints(file.content, make);
10878
+ } else {
10879
+ for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
10880
+ for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
10881
+ }
10271
10882
  return out;
10272
10883
  }
10273
10884
 
10274
10885
  // src/extract/calls/redis.ts
10275
10886
  init_cjs_shims();
10276
- var import_node_path39 = __toESM(require("path"), 1);
10887
+ var import_node_path40 = __toESM(require("path"), 1);
10277
10888
  var import_types29 = require("@neat.is/types");
10278
10889
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10279
10890
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10296,7 +10907,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10296
10907
  // support tier (ADR-066).
10297
10908
  confidenceKind: "url-with-structural-support",
10298
10909
  evidence: {
10299
- file: import_node_path39.default.relative(serviceDir, file.path),
10910
+ file: import_node_path40.default.relative(serviceDir, file.path),
10300
10911
  line,
10301
10912
  snippet: snippet(file.content, line)
10302
10913
  }
@@ -10307,7 +10918,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10307
10918
 
10308
10919
  // src/extract/calls/aws.ts
10309
10920
  init_cjs_shims();
10310
- var import_node_path40 = __toESM(require("path"), 1);
10921
+ var import_node_path41 = __toESM(require("path"), 1);
10311
10922
  var import_types30 = require("@neat.is/types");
10312
10923
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10313
10924
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10341,7 +10952,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10341
10952
  // (ADR-066).
10342
10953
  confidenceKind: "verified-call-site",
10343
10954
  evidence: {
10344
- file: import_node_path40.default.relative(serviceDir, file.path),
10955
+ file: import_node_path41.default.relative(serviceDir, file.path),
10345
10956
  line,
10346
10957
  snippet: snippet(file.content, line)
10347
10958
  }
@@ -10366,7 +10977,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10366
10977
 
10367
10978
  // src/extract/calls/grpc.ts
10368
10979
  init_cjs_shims();
10369
- var import_node_path41 = __toESM(require("path"), 1);
10980
+ var import_node_path42 = __toESM(require("path"), 1);
10370
10981
  var import_types31 = require("@neat.is/types");
10371
10982
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10372
10983
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -10425,7 +11036,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10425
11036
  // tier (ADR-066).
10426
11037
  confidenceKind: "verified-call-site",
10427
11038
  evidence: {
10428
- file: import_node_path41.default.relative(serviceDir, file.path),
11039
+ file: import_node_path42.default.relative(serviceDir, file.path),
10429
11040
  line,
10430
11041
  snippet: snippet(file.content, line)
10431
11042
  }
@@ -10436,7 +11047,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10436
11047
 
10437
11048
  // src/extract/calls/supabase.ts
10438
11049
  init_cjs_shims();
10439
- var import_node_path42 = __toESM(require("path"), 1);
11050
+ var import_node_path43 = __toESM(require("path"), 1);
10440
11051
  var import_types32 = require("@neat.is/types");
10441
11052
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
10442
11053
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -10495,7 +11106,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10495
11106
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10496
11107
  confidenceKind: "verified-call-site",
10497
11108
  evidence: {
10498
- file: import_node_path42.default.relative(serviceDir, file.path),
11109
+ file: import_node_path43.default.relative(serviceDir, file.path),
10499
11110
  line,
10500
11111
  snippet: snippet(file.content, line)
10501
11112
  }
@@ -10522,7 +11133,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10522
11133
  edgeType: "CALLS",
10523
11134
  confidenceKind: "verified-call-site",
10524
11135
  evidence: {
10525
- file: import_node_path42.default.relative(serviceDir, file.path),
11136
+ file: import_node_path43.default.relative(serviceDir, file.path),
10526
11137
  line,
10527
11138
  snippet: snippet(file.content, line)
10528
11139
  }
@@ -10534,7 +11145,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10534
11145
 
10535
11146
  // src/extract/calls/firestore.ts
10536
11147
  init_cjs_shims();
10537
- var import_node_path43 = __toESM(require("path"), 1);
11148
+ var import_node_path44 = __toESM(require("path"), 1);
10538
11149
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10539
11150
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10540
11151
  var import_types33 = require("@neat.is/types");
@@ -10573,7 +11184,7 @@ function isFirestoreClientFactory(node) {
10573
11184
  }
10574
11185
  function firestoreClientVars(root) {
10575
11186
  const vars = /* @__PURE__ */ new Set();
10576
- const walk9 = (node) => {
11187
+ const walk10 = (node) => {
10577
11188
  if (node.type === "variable_declarator") {
10578
11189
  const name = node.childForFieldName("name");
10579
11190
  let value = node.childForFieldName("value");
@@ -10582,9 +11193,9 @@ function firestoreClientVars(root) {
10582
11193
  vars.add(name.text);
10583
11194
  }
10584
11195
  }
10585
- for (const c of namedChildren(node)) walk9(c);
11196
+ for (const c of namedChildren(node)) walk10(c);
10586
11197
  };
10587
- walk9(root);
11198
+ walk10(root);
10588
11199
  return vars;
10589
11200
  }
10590
11201
  function isClientExpr(node, clientVars) {
@@ -10705,7 +11316,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10705
11316
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10706
11317
  if (!hasClient && !hasAdmin) return [];
10707
11318
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10708
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
11319
+ const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
10709
11320
  const clientVars = firestoreClientVars(tree.rootNode);
10710
11321
  const collLine = /* @__PURE__ */ new Map();
10711
11322
  const writes = /* @__PURE__ */ new Map();
@@ -10739,7 +11350,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10739
11350
  }
10740
11351
  s.add(field);
10741
11352
  };
10742
- const walk9 = (node) => {
11353
+ const walk10 = (node) => {
10743
11354
  if (node.type === "call_expression") {
10744
11355
  const fn = node.childForFieldName("function");
10745
11356
  const line = node.startPosition.row + 1;
@@ -10779,9 +11390,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10779
11390
  }
10780
11391
  }
10781
11392
  }
10782
- for (const c of namedChildren(node)) walk9(c);
11393
+ for (const c of namedChildren(node)) walk10(c);
10783
11394
  };
10784
- walk9(tree.rootNode);
11395
+ walk10(tree.rootNode);
10785
11396
  const out = [];
10786
11397
  for (const [collPath, line] of collLine) {
10787
11398
  const byField = writes.get(collPath);
@@ -10809,7 +11420,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10809
11420
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10810
11421
  ...sdkWrites ? { sdkWrites } : {},
10811
11422
  evidence: {
10812
- file: import_node_path43.default.relative(serviceDir, file.path),
11423
+ file: import_node_path44.default.relative(serviceDir, file.path),
10813
11424
  line,
10814
11425
  snippet: snippet(file.content, line)
10815
11426
  }
@@ -10820,7 +11431,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10820
11431
 
10821
11432
  // src/extract/calls/mongoose.ts
10822
11433
  init_cjs_shims();
10823
- var import_node_path44 = __toESM(require("path"), 1);
11434
+ var import_node_path45 = __toESM(require("path"), 1);
10824
11435
  var import_types34 = require("@neat.is/types");
10825
11436
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10826
11437
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -10971,7 +11582,7 @@ function endpoint(r, file, serviceDir, matchText) {
10971
11582
  kind: r.kind,
10972
11583
  edgeType: "CALLS",
10973
11584
  confidenceKind: "verified-call-site",
10974
- evidence: { file: import_node_path44.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11585
+ evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
10975
11586
  };
10976
11587
  }
10977
11588
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11061,7 +11672,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11061
11672
  const registry = /* @__PURE__ */ new Map();
11062
11673
  for (const f of mongooseFiles) {
11063
11674
  const fx = fileExportsOf(f.content, pluralizeOn);
11064
- if (fx) registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), fx);
11675
+ if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
11065
11676
  }
11066
11677
  if (registry.size === 0) return [];
11067
11678
  const out = [];
@@ -11072,7 +11683,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11072
11683
  const directColl = /* @__PURE__ */ new Map();
11073
11684
  const nsExports = /* @__PURE__ */ new Map();
11074
11685
  for (const b of bindings) {
11075
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
11686
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
11076
11687
  if (!resolvedRel) continue;
11077
11688
  const fx = registry.get(resolvedRel);
11078
11689
  if (!fx) continue;
@@ -11116,7 +11727,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11116
11727
 
11117
11728
  // src/extract/calls/sqlalchemy.ts
11118
11729
  init_cjs_shims();
11119
- var import_node_path45 = __toESM(require("path"), 1);
11730
+ var import_node_path46 = __toESM(require("path"), 1);
11120
11731
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11121
11732
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11122
11733
  var import_types35 = require("@neat.is/types");
@@ -11255,7 +11866,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11255
11866
  childTable,
11256
11867
  parentTable,
11257
11868
  evidence: {
11258
- file: import_node_path45.default.relative(serviceDir, file.path),
11869
+ file: import_node_path46.default.relative(serviceDir, file.path),
11259
11870
  line,
11260
11871
  snippet: snippet(file.content, line)
11261
11872
  }
@@ -11280,7 +11891,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11280
11891
  confidenceKind: "verified-call-site",
11281
11892
  ...columns && columns.length > 0 ? { columns } : {},
11282
11893
  evidence: {
11283
- file: import_node_path45.default.relative(serviceDir, file.path),
11894
+ file: import_node_path46.default.relative(serviceDir, file.path),
11284
11895
  line,
11285
11896
  snippet: snippet(file.content, line)
11286
11897
  }
@@ -11387,7 +11998,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11387
11998
  edgeType: "CALLS",
11388
11999
  confidenceKind: "verified-call-site",
11389
12000
  evidence: {
11390
- file: import_node_path45.default.relative(serviceDir, file.path),
12001
+ file: import_node_path46.default.relative(serviceDir, file.path),
11391
12002
  line,
11392
12003
  snippet: snippet(file.content, line)
11393
12004
  }
@@ -11399,7 +12010,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11399
12010
 
11400
12011
  // src/extract/calls/django-orm.ts
11401
12012
  init_cjs_shims();
11402
- var import_node_path46 = __toESM(require("path"), 1);
12013
+ var import_node_path47 = __toESM(require("path"), 1);
11403
12014
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11404
12015
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11405
12016
  var import_types36 = require("@neat.is/types");
@@ -11469,7 +12080,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11469
12080
  const tree = parseSource7(makePyParser4(), file.content);
11470
12081
  const out = [];
11471
12082
  const seen = /* @__PURE__ */ new Set();
11472
- const defaultAppLabel = import_node_path46.default.basename(import_node_path46.default.dirname(file.path));
12083
+ const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
11473
12084
  walk4(tree.rootNode, (node) => {
11474
12085
  if (node.type !== "class_definition") return;
11475
12086
  if (!extendsDjangoModel(node)) return;
@@ -11487,7 +12098,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11487
12098
  kind: "sql-table",
11488
12099
  edgeType: "CALLS",
11489
12100
  confidenceKind: "verified-call-site",
11490
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12101
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11491
12102
  });
11492
12103
  });
11493
12104
  return out;
@@ -11495,7 +12106,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11495
12106
 
11496
12107
  // src/extract/calls/drizzle.ts
11497
12108
  init_cjs_shims();
11498
- var import_node_path47 = __toESM(require("path"), 1);
12109
+ var import_node_path48 = __toESM(require("path"), 1);
11499
12110
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11500
12111
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11501
12112
  var import_types37 = require("@neat.is/types");
@@ -11573,10 +12184,10 @@ function columnsFromObject(obj) {
11573
12184
  }
11574
12185
  function drizzleEndpointsFromFile(file, serviceDir) {
11575
12186
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11576
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12187
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11577
12188
  const out = [];
11578
12189
  const seen = /* @__PURE__ */ new Set();
11579
- const walk9 = (node) => {
12190
+ const walk10 = (node) => {
11580
12191
  if (node.type === "call_expression") {
11581
12192
  const fn = node.childForFieldName("function");
11582
12193
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11596,7 +12207,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11596
12207
  confidenceKind: "structural",
11597
12208
  columns,
11598
12209
  evidence: {
11599
- file: import_node_path47.default.relative(serviceDir, file.path),
12210
+ file: import_node_path48.default.relative(serviceDir, file.path),
11600
12211
  line,
11601
12212
  snippet: snippet(file.content, line)
11602
12213
  }
@@ -11604,9 +12215,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11604
12215
  }
11605
12216
  }
11606
12217
  }
11607
- for (const c of namedChildren4(node)) walk9(c);
12218
+ for (const c of namedChildren4(node)) walk10(c);
11608
12219
  };
11609
- walk9(tree.rootNode);
12220
+ walk10(tree.rootNode);
11610
12221
  return out;
11611
12222
  }
11612
12223
  function enclosingVarName(call) {
@@ -11628,7 +12239,7 @@ function enclosingVarName(call) {
11628
12239
  function collectDrizzleTables(root) {
11629
12240
  const tables = [];
11630
12241
  const varToTable = /* @__PURE__ */ new Map();
11631
- const walk9 = (node) => {
12242
+ const walk10 = (node) => {
11632
12243
  if (node.type === "call_expression") {
11633
12244
  const fn = node.childForFieldName("function");
11634
12245
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11643,9 +12254,9 @@ function collectDrizzleTables(root) {
11643
12254
  }
11644
12255
  }
11645
12256
  }
11646
- for (const c of namedChildren4(node)) walk9(c);
12257
+ for (const c of namedChildren4(node)) walk10(c);
11647
12258
  };
11648
- walk9(root);
12259
+ walk10(root);
11649
12260
  return { tables, varToTable };
11650
12261
  }
11651
12262
  function referencesTargetVar(call) {
@@ -11662,13 +12273,13 @@ function referencesTargetVar(call) {
11662
12273
  }
11663
12274
  function drizzleForeignKeys(file, serviceDir) {
11664
12275
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11665
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12276
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11666
12277
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11667
12278
  const out = [];
11668
12279
  const seen = /* @__PURE__ */ new Set();
11669
12280
  for (const table of tables) {
11670
12281
  if (!table.object) continue;
11671
- const walk9 = (node) => {
12282
+ const walk10 = (node) => {
11672
12283
  if (node.type === "call_expression") {
11673
12284
  const targetVar = referencesTargetVar(node);
11674
12285
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -11681,7 +12292,7 @@ function drizzleForeignKeys(file, serviceDir) {
11681
12292
  childTable: table.tableName,
11682
12293
  parentTable,
11683
12294
  evidence: {
11684
- file: import_node_path47.default.relative(serviceDir, file.path),
12295
+ file: import_node_path48.default.relative(serviceDir, file.path),
11685
12296
  line,
11686
12297
  snippet: snippet(file.content, line)
11687
12298
  }
@@ -11689,16 +12300,16 @@ function drizzleForeignKeys(file, serviceDir) {
11689
12300
  }
11690
12301
  }
11691
12302
  }
11692
- for (const c of namedChildren4(node)) walk9(c);
12303
+ for (const c of namedChildren4(node)) walk10(c);
11693
12304
  };
11694
- walk9(table.object);
12305
+ walk10(table.object);
11695
12306
  }
11696
12307
  return out;
11697
12308
  }
11698
12309
 
11699
12310
  // src/extract/calls/prisma.ts
11700
12311
  init_cjs_shims();
11701
- var import_node_path48 = __toESM(require("path"), 1);
12312
+ var import_node_path49 = __toESM(require("path"), 1);
11702
12313
  var import_types38 = require("@neat.is/types");
11703
12314
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11704
12315
  "Int",
@@ -11754,7 +12365,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11754
12365
  confidenceKind: "structural",
11755
12366
  columns: b.columns,
11756
12367
  evidence: {
11757
- file: import_node_path48.default.relative(serviceDir, file.path),
12368
+ file: import_node_path49.default.relative(serviceDir, file.path),
11758
12369
  line: b.startLine,
11759
12370
  snippet: snippet(content, b.startLine)
11760
12371
  }
@@ -11815,7 +12426,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11815
12426
  }
11816
12427
  async function prismaColumnEndpoints(serviceDir) {
11817
12428
  const schemaPath = await findFirst(serviceDir, [
11818
- import_node_path48.default.join("prisma", "schema.prisma"),
12429
+ import_node_path49.default.join("prisma", "schema.prisma"),
11819
12430
  "schema.prisma"
11820
12431
  ]);
11821
12432
  if (!schemaPath) return [];
@@ -11890,7 +12501,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11890
12501
  childTable: current.table,
11891
12502
  parentTable,
11892
12503
  evidence: {
11893
- file: import_node_path48.default.relative(serviceDir, file.path),
12504
+ file: import_node_path49.default.relative(serviceDir, file.path),
11894
12505
  line: lineNo,
11895
12506
  snippet: snippet(content, lineNo)
11896
12507
  }
@@ -11905,7 +12516,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11905
12516
  }
11906
12517
  async function prismaForeignKeys(serviceDir) {
11907
12518
  const schemaPath = await findFirst(serviceDir, [
11908
- import_node_path48.default.join("prisma", "schema.prisma"),
12519
+ import_node_path49.default.join("prisma", "schema.prisma"),
11909
12520
  "schema.prisma"
11910
12521
  ]);
11911
12522
  if (!schemaPath) return [];
@@ -11916,7 +12527,7 @@ async function prismaForeignKeys(serviceDir) {
11916
12527
 
11917
12528
  // src/extract/calls/activerecord.ts
11918
12529
  init_cjs_shims();
11919
- var import_node_path49 = __toESM(require("path"), 1);
12530
+ var import_node_path50 = __toESM(require("path"), 1);
11920
12531
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11921
12532
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
11922
12533
  var import_types39 = require("@neat.is/types");
@@ -12124,7 +12735,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12124
12735
  confidenceKind: "structural",
12125
12736
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12126
12737
  evidence: {
12127
- file: import_node_path49.default.relative(serviceDir, file.path),
12738
+ file: import_node_path50.default.relative(serviceDir, file.path),
12128
12739
  line: table.line,
12129
12740
  snippet: snippet(file.content, table.line)
12130
12741
  }
@@ -12146,7 +12757,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12146
12757
  childTable,
12147
12758
  parentTable,
12148
12759
  evidence: {
12149
- file: import_node_path49.default.relative(serviceDir, file.path),
12760
+ file: import_node_path50.default.relative(serviceDir, file.path),
12150
12761
  line,
12151
12762
  snippet: snippet(file.content, line)
12152
12763
  }
@@ -12229,7 +12840,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12229
12840
  edgeType: "CALLS",
12230
12841
  confidenceKind: "verified-call-site",
12231
12842
  evidence: {
12232
- file: import_node_path49.default.relative(serviceDir, file.path),
12843
+ file: import_node_path50.default.relative(serviceDir, file.path),
12233
12844
  line,
12234
12845
  snippet: snippet(file.content, line)
12235
12846
  }
@@ -12266,7 +12877,7 @@ function railsModelForeignKeys(file, serviceDir) {
12266
12877
  childTable,
12267
12878
  parentTable,
12268
12879
  evidence: {
12269
- file: import_node_path49.default.relative(serviceDir, file.path),
12880
+ file: import_node_path50.default.relative(serviceDir, file.path),
12270
12881
  line,
12271
12882
  snippet: snippet(file.content, line)
12272
12883
  }
@@ -12278,7 +12889,7 @@ function railsModelForeignKeys(file, serviceDir) {
12278
12889
 
12279
12890
  // src/extract/calls/eloquent.ts
12280
12891
  init_cjs_shims();
12281
- var import_node_path50 = __toESM(require("path"), 1);
12892
+ var import_node_path51 = __toESM(require("path"), 1);
12282
12893
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12283
12894
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12284
12895
  var import_types40 = require("@neat.is/types");
@@ -12535,7 +13146,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12535
13146
  confidenceKind: "structural",
12536
13147
  ...columns.length > 0 ? { columns } : {},
12537
13148
  evidence: {
12538
- file: import_node_path50.default.relative(serviceDir, file.path),
13149
+ file: import_node_path51.default.relative(serviceDir, file.path),
12539
13150
  line: bp.line,
12540
13151
  snippet: snippet(file.content, bp.line)
12541
13152
  }
@@ -12557,7 +13168,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12557
13168
  childTable,
12558
13169
  parentTable,
12559
13170
  evidence: {
12560
- file: import_node_path50.default.relative(serviceDir, file.path),
13171
+ file: import_node_path51.default.relative(serviceDir, file.path),
12561
13172
  line,
12562
13173
  snippet: snippet(file.content, line)
12563
13174
  }
@@ -12677,7 +13288,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12677
13288
  edgeType: "CALLS",
12678
13289
  confidenceKind: "verified-call-site",
12679
13290
  evidence: {
12680
- file: import_node_path50.default.relative(serviceDir, file.path),
13291
+ file: import_node_path51.default.relative(serviceDir, file.path),
12681
13292
  line,
12682
13293
  snippet: snippet(file.content, line)
12683
13294
  }
@@ -12713,7 +13324,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12713
13324
  childTable,
12714
13325
  parentTable,
12715
13326
  evidence: {
12716
- file: import_node_path50.default.relative(serviceDir, file.path),
13327
+ file: import_node_path51.default.relative(serviceDir, file.path),
12717
13328
  line,
12718
13329
  snippet: snippet(file.content, line)
12719
13330
  }
@@ -12725,7 +13336,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12725
13336
 
12726
13337
  // src/extract/calls/go.ts
12727
13338
  init_cjs_shims();
12728
- var import_node_path53 = __toESM(require("path"), 1);
13339
+ var import_node_path54 = __toESM(require("path"), 1);
12729
13340
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12730
13341
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12731
13342
  var import_types41 = require("@neat.is/types");
@@ -12800,7 +13411,7 @@ function firstStringLiteralArg(argsNode) {
12800
13411
  return null;
12801
13412
  }
12802
13413
  function goSqlEndpointsFromFile(file, serviceDir) {
12803
- if (import_node_path53.default.extname(file.path) !== ".go") return [];
13414
+ if (import_node_path54.default.extname(file.path) !== ".go") return [];
12804
13415
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12805
13416
  const tree = parseSource10(makeGoParser3(), file.content);
12806
13417
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12829,7 +13440,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12829
13440
  confidenceKind: "verified-call-site",
12830
13441
  ...columns.length > 0 ? { columns } : {},
12831
13442
  evidence: {
12832
- file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
13443
+ file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
12833
13444
  line,
12834
13445
  snippet: snippet(file.content, line)
12835
13446
  }
@@ -12840,7 +13451,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12840
13451
 
12841
13452
  // src/extract/calls/gorm.ts
12842
13453
  init_cjs_shims();
12843
- var import_node_path54 = __toESM(require("path"), 1);
13454
+ var import_node_path55 = __toESM(require("path"), 1);
12844
13455
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12845
13456
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12846
13457
  var import_types42 = require("@neat.is/types");
@@ -13273,7 +13884,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13273
13884
  seen.delete(struct.name);
13274
13885
  }
13275
13886
  function gormEndpointsFromFile(file, serviceDir) {
13276
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13887
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13277
13888
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13278
13889
  const tree = parseSource11(makeGoParser4(), file.content);
13279
13890
  const { structs, models, tableFor } = analyze(tree);
@@ -13295,7 +13906,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13295
13906
  confidenceKind: "structural",
13296
13907
  ...columns.length > 0 ? { columns } : {},
13297
13908
  evidence: {
13298
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13909
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13299
13910
  line: struct.line,
13300
13911
  snippet: snippet(file.content, struct.line)
13301
13912
  }
@@ -13304,7 +13915,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13304
13915
  return out;
13305
13916
  }
13306
13917
  function gormForeignKeys(file, serviceDir) {
13307
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13918
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13308
13919
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13309
13920
  const tree = parseSource11(makeGoParser4(), file.content);
13310
13921
  const { structs, models, tableFor } = analyze(tree);
@@ -13319,7 +13930,7 @@ function gormForeignKeys(file, serviceDir) {
13319
13930
  childTable,
13320
13931
  parentTable,
13321
13932
  evidence: {
13322
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13933
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13323
13934
  line,
13324
13935
  snippet: snippet(file.content, line)
13325
13936
  }
@@ -13354,15 +13965,131 @@ function gormForeignKeys(file, serviceDir) {
13354
13965
  return out;
13355
13966
  }
13356
13967
 
13968
+ // src/extract/calls/efcore.ts
13969
+ init_cjs_shims();
13970
+ var import_node_path56 = __toESM(require("path"), 1);
13971
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13972
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
13973
+ var import_types43 = require("@neat.is/types");
13974
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
13975
+ var PARSE_CHUNK12 = 16384;
13976
+ function makeCsParser() {
13977
+ const p = new import_tree_sitter16.default();
13978
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
13979
+ return p;
13980
+ }
13981
+ function parseSource12(parser, source) {
13982
+ return parser.parse(
13983
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
13984
+ );
13985
+ }
13986
+ function walk9(node, visit) {
13987
+ visit(node);
13988
+ for (let i = 0; i < node.namedChildCount; i++) {
13989
+ const c = node.namedChild(i);
13990
+ if (c) walk9(c, visit);
13991
+ }
13992
+ }
13993
+ function firstChildOfType(node, type) {
13994
+ for (let i = 0; i < node.namedChildCount; i++) {
13995
+ const c = node.namedChild(i);
13996
+ if (c?.type === type) return c;
13997
+ }
13998
+ return null;
13999
+ }
14000
+ function csStringLiteral(node) {
14001
+ if (node.type === "string_literal") {
14002
+ let out = "";
14003
+ for (let i = 0; i < node.namedChildCount; i++) {
14004
+ const c = node.namedChild(i);
14005
+ if (c?.type === "string_literal_content") out += c.text;
14006
+ }
14007
+ return out;
14008
+ }
14009
+ if (node.type === "verbatim_string_literal") {
14010
+ const t = node.text;
14011
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
14012
+ }
14013
+ return null;
14014
+ }
14015
+ function attributeName(attr) {
14016
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
14017
+ if (!nameNode) return null;
14018
+ const text = nameNode.text;
14019
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
14020
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
14021
+ }
14022
+ function tableFromAttribute(attr) {
14023
+ if (attributeName(attr) !== "Table") return null;
14024
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
14025
+ if (!args) return null;
14026
+ for (let i = 0; i < args.namedChildCount; i++) {
14027
+ const arg = args.namedChild(i);
14028
+ if (arg?.type !== "attribute_argument") continue;
14029
+ const first = arg.namedChild(0);
14030
+ if (!first) continue;
14031
+ const value = csStringLiteral(first);
14032
+ if (value !== null) return value;
14033
+ return null;
14034
+ }
14035
+ return null;
14036
+ }
14037
+ function tableFromToTable(call) {
14038
+ const fn = call.childForFieldName("function");
14039
+ if (fn?.type !== "member_access_expression") return null;
14040
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14041
+ if (method?.text !== "ToTable") return null;
14042
+ const args = call.childForFieldName("arguments");
14043
+ const firstArg2 = args?.namedChild(0);
14044
+ if (firstArg2?.type !== "argument") return null;
14045
+ const value = firstArg2.namedChild(0);
14046
+ return value ? csStringLiteral(value) : null;
14047
+ }
14048
+ function efcoreEndpointsFromFile(file, serviceDir) {
14049
+ if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14050
+ if (!EFCORE_GATE.test(file.content)) return [];
14051
+ const tree = parseSource12(makeCsParser(), file.content);
14052
+ const out = [];
14053
+ const seen = /* @__PURE__ */ new Set();
14054
+ const push = (name, line) => {
14055
+ if (!name || seen.has(name)) return;
14056
+ seen.add(name);
14057
+ out.push({
14058
+ infraId: (0, import_types43.infraId)("sql-table", name),
14059
+ name,
14060
+ kind: "sql-table",
14061
+ edgeType: "CALLS",
14062
+ confidenceKind: "structural",
14063
+ evidence: {
14064
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14065
+ line,
14066
+ snippet: snippet(file.content, line)
14067
+ }
14068
+ });
14069
+ };
14070
+ walk9(tree.rootNode, (node) => {
14071
+ if (node.type === "attribute") {
14072
+ const table = tableFromAttribute(node);
14073
+ if (table) push(table, node.startPosition.row + 1);
14074
+ return;
14075
+ }
14076
+ if (node.type === "invocation_expression") {
14077
+ const table = tableFromToTable(node);
14078
+ if (table) push(table, node.startPosition.row + 1);
14079
+ }
14080
+ });
14081
+ return out;
14082
+ }
14083
+
13357
14084
  // src/extract/calls/index.ts
13358
14085
  function edgeTypeFromEndpoint(ep) {
13359
14086
  switch (ep.edgeType) {
13360
14087
  case "PUBLISHES_TO":
13361
- return import_types43.EdgeType.PUBLISHES_TO;
14088
+ return import_types44.EdgeType.PUBLISHES_TO;
13362
14089
  case "CONSUMES_FROM":
13363
- return import_types43.EdgeType.CONSUMES_FROM;
14090
+ return import_types44.EdgeType.CONSUMES_FROM;
13364
14091
  default:
13365
- return import_types43.EdgeType.CALLS;
14092
+ return import_types44.EdgeType.CALLS;
13366
14093
  }
13367
14094
  }
13368
14095
  function isAwsKind(kind) {
@@ -13412,6 +14139,11 @@ async function addExternalEndpointEdges(graph, services) {
13412
14139
  } catch (err) {
13413
14140
  recordExtractionError("laravel eloquent extraction", file.path, err);
13414
14141
  }
14142
+ try {
14143
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14144
+ } catch (err) {
14145
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14146
+ }
13415
14147
  }
13416
14148
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13417
14149
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -13422,7 +14154,7 @@ async function addExternalEndpointEdges(graph, services) {
13422
14154
  if (!graph.hasNode(ep.infraId)) {
13423
14155
  const node = {
13424
14156
  id: ep.infraId,
13425
- type: import_types43.NodeType.InfraNode,
14157
+ type: import_types44.NodeType.InfraNode,
13426
14158
  name: ep.name,
13427
14159
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
13428
14160
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -13435,21 +14167,21 @@ async function addExternalEndpointEdges(graph, services) {
13435
14167
  }
13436
14168
  if (ep.columns && ep.columns.length > 0) {
13437
14169
  const node = graph.getNodeAttributes(ep.infraId);
13438
- if (node.type === import_types43.NodeType.InfraNode) {
14170
+ if (node.type === import_types44.NodeType.InfraNode) {
13439
14171
  graph.replaceNodeAttributes(ep.infraId, {
13440
14172
  ...node,
13441
14173
  columns: foldColumns(
13442
14174
  node.columns,
13443
14175
  ep.columns,
13444
- import_types43.Provenance.EXTRACTED,
13445
- (0, import_types43.confidenceForExtracted)(ep.confidenceKind)
14176
+ import_types44.Provenance.EXTRACTED,
14177
+ (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
13446
14178
  )
13447
14179
  });
13448
14180
  }
13449
14181
  }
13450
14182
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
13451
14183
  const node = graph.getNodeAttributes(ep.infraId);
13452
- if (node.type === import_types43.NodeType.InfraNode) {
14184
+ if (node.type === import_types44.NodeType.InfraNode) {
13453
14185
  graph.replaceNodeAttributes(ep.infraId, {
13454
14186
  ...node,
13455
14187
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -13457,7 +14189,7 @@ async function addExternalEndpointEdges(graph, services) {
13457
14189
  }
13458
14190
  }
13459
14191
  const edgeType = edgeTypeFromEndpoint(ep);
13460
- const confidence = (0, import_types43.confidenceForExtracted)(ep.confidenceKind);
14192
+ const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
13461
14193
  const relFile = toPosix(ep.evidence.file);
13462
14194
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
13463
14195
  graph,
@@ -13467,7 +14199,7 @@ async function addExternalEndpointEdges(graph, services) {
13467
14199
  );
13468
14200
  nodesAdded += n;
13469
14201
  edgesAdded += e;
13470
- if (!(0, import_types43.passesExtractedFloor)(confidence)) {
14202
+ if (!(0, import_types44.passesExtractedFloor)(confidence)) {
13471
14203
  noteExtractedDropped({
13472
14204
  source: fileNodeId,
13473
14205
  target: ep.infraId,
@@ -13487,7 +14219,7 @@ async function addExternalEndpointEdges(graph, services) {
13487
14219
  source: fileNodeId,
13488
14220
  target: ep.infraId,
13489
14221
  type: edgeType,
13490
- provenance: import_types43.Provenance.EXTRACTED,
14222
+ provenance: import_types44.Provenance.EXTRACTED,
13491
14223
  confidence,
13492
14224
  evidence: ep.evidence
13493
14225
  };
@@ -13510,7 +14242,7 @@ async function addCallEdges(graph, services) {
13510
14242
 
13511
14243
  // src/extract/table-edges.ts
13512
14244
  init_cjs_shims();
13513
- var import_types44 = require("@neat.is/types");
14245
+ var import_types45 = require("@neat.is/types");
13514
14246
  async function addTableEdges(graph, services) {
13515
14247
  let nodesAdded = 0;
13516
14248
  let edgesAdded = 0;
@@ -13538,20 +14270,20 @@ async function addTableEdges(graph, services) {
13538
14270
  }
13539
14271
  refs.push(...modelRefs);
13540
14272
  for (const ref of refs) {
13541
- const childId = (0, import_types44.infraId)("sql-table", ref.childTable);
13542
- const parentId = (0, import_types44.infraId)("sql-table", ref.parentTable);
14273
+ const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14274
+ const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
13543
14275
  if (childId === parentId) continue;
13544
14276
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13545
14277
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13546
- const edgeId = (0, import_types44.extractedEdgeId)(childId, parentId, import_types44.EdgeType.REFERENCES);
14278
+ const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
13547
14279
  if (graph.hasEdge(edgeId)) continue;
13548
14280
  const edge = {
13549
14281
  id: edgeId,
13550
14282
  source: childId,
13551
14283
  target: parentId,
13552
- type: import_types44.EdgeType.REFERENCES,
13553
- provenance: import_types44.Provenance.EXTRACTED,
13554
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14284
+ type: import_types45.EdgeType.REFERENCES,
14285
+ provenance: import_types45.Provenance.EXTRACTED,
14286
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13555
14287
  evidence: ref.evidence
13556
14288
  };
13557
14289
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13564,7 +14296,7 @@ function ensureTableNode(graph, id, name) {
13564
14296
  if (graph.hasNode(id)) return 0;
13565
14297
  const node = {
13566
14298
  id,
13567
- type: import_types44.NodeType.InfraNode,
14299
+ type: import_types45.NodeType.InfraNode,
13568
14300
  name,
13569
14301
  provider: "self",
13570
14302
  kind: "sql-table"
@@ -13578,16 +14310,16 @@ init_cjs_shims();
13578
14310
 
13579
14311
  // src/extract/infra/docker-compose.ts
13580
14312
  init_cjs_shims();
13581
- var import_node_path55 = __toESM(require("path"), 1);
13582
- var import_types46 = require("@neat.is/types");
14313
+ var import_node_path57 = __toESM(require("path"), 1);
14314
+ var import_types47 = require("@neat.is/types");
13583
14315
 
13584
14316
  // src/extract/infra/shared.ts
13585
14317
  init_cjs_shims();
13586
- var import_types45 = require("@neat.is/types");
14318
+ var import_types46 = require("@neat.is/types");
13587
14319
  function makeInfraNode(kind, name, provider = "self", extras) {
13588
14320
  return {
13589
- id: (0, import_types45.infraId)(kind, name),
13590
- type: import_types45.NodeType.InfraNode,
14321
+ id: (0, import_types46.infraId)(kind, name),
14322
+ type: import_types46.NodeType.InfraNode,
13591
14323
  name,
13592
14324
  provider,
13593
14325
  kind,
@@ -13631,8 +14363,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13631
14363
  source: anchorId,
13632
14364
  target: node.id,
13633
14365
  type: edgeType,
13634
- provenance: import_types45.Provenance.EXTRACTED,
13635
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14366
+ provenance: import_types46.Provenance.EXTRACTED,
14367
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13636
14368
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13637
14369
  };
13638
14370
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13649,7 +14381,7 @@ function dependsOnList(value) {
13649
14381
  }
13650
14382
  function serviceNameToServiceNode(name, services) {
13651
14383
  for (const s of services) {
13652
- if (s.node.name === name || import_node_path55.default.basename(s.dir) === name) return s.node.id;
14384
+ if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
13653
14385
  }
13654
14386
  return null;
13655
14387
  }
@@ -13658,7 +14390,7 @@ async function addComposeInfra(graph, scanPath, services) {
13658
14390
  let edgesAdded = 0;
13659
14391
  let composePath = null;
13660
14392
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13661
- const abs = import_node_path55.default.join(scanPath, name);
14393
+ const abs = import_node_path57.default.join(scanPath, name);
13662
14394
  if (await exists(abs)) {
13663
14395
  composePath = abs;
13664
14396
  break;
@@ -13671,13 +14403,13 @@ async function addComposeInfra(graph, scanPath, services) {
13671
14403
  } catch (err) {
13672
14404
  recordExtractionError(
13673
14405
  "infra docker-compose",
13674
- import_node_path55.default.relative(scanPath, composePath),
14406
+ import_node_path57.default.relative(scanPath, composePath),
13675
14407
  err
13676
14408
  );
13677
14409
  return { nodesAdded, edgesAdded };
13678
14410
  }
13679
14411
  if (!compose?.services) return { nodesAdded, edgesAdded };
13680
- const evidenceFile = import_node_path55.default.relative(scanPath, composePath).split(import_node_path55.default.sep).join("/");
14412
+ const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
13681
14413
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13682
14414
  for (const [composeName, svc] of Object.entries(compose.services)) {
13683
14415
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13699,15 +14431,15 @@ async function addComposeInfra(graph, scanPath, services) {
13699
14431
  for (const dep of dependsOnList(svc.depends_on)) {
13700
14432
  const targetId = composeNameToNodeId.get(dep);
13701
14433
  if (!targetId) continue;
13702
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types46.EdgeType.DEPENDS_ON);
14434
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
13703
14435
  if (graph.hasEdge(edgeId)) continue;
13704
14436
  const edge = {
13705
14437
  id: edgeId,
13706
14438
  source: sourceId,
13707
14439
  target: targetId,
13708
- type: import_types46.EdgeType.DEPENDS_ON,
13709
- provenance: import_types46.Provenance.EXTRACTED,
13710
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14440
+ type: import_types47.EdgeType.DEPENDS_ON,
14441
+ provenance: import_types47.Provenance.EXTRACTED,
14442
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13711
14443
  evidence: { file: evidenceFile }
13712
14444
  };
13713
14445
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13719,9 +14451,9 @@ async function addComposeInfra(graph, scanPath, services) {
13719
14451
 
13720
14452
  // src/extract/infra/dockerfile.ts
13721
14453
  init_cjs_shims();
13722
- var import_node_path56 = __toESM(require("path"), 1);
13723
- var import_node_fs24 = require("fs");
13724
- var import_types47 = require("@neat.is/types");
14454
+ var import_node_path58 = __toESM(require("path"), 1);
14455
+ var import_node_fs25 = require("fs");
14456
+ var import_types48 = require("@neat.is/types");
13725
14457
  function readDockerfile(content) {
13726
14458
  let image = null;
13727
14459
  const ports = [];
@@ -13750,15 +14482,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13750
14482
  let nodesAdded = 0;
13751
14483
  let edgesAdded = 0;
13752
14484
  for (const service of services) {
13753
- const dockerfilePath = import_node_path56.default.join(service.dir, "Dockerfile");
14485
+ const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
13754
14486
  if (!await exists(dockerfilePath)) continue;
13755
14487
  let content;
13756
14488
  try {
13757
- content = await import_node_fs24.promises.readFile(dockerfilePath, "utf8");
14489
+ content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
13758
14490
  } catch (err) {
13759
14491
  recordExtractionError(
13760
14492
  "infra dockerfile",
13761
- import_node_path56.default.relative(scanPath, dockerfilePath),
14493
+ import_node_path58.default.relative(scanPath, dockerfilePath),
13762
14494
  err
13763
14495
  );
13764
14496
  continue;
@@ -13770,8 +14502,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13770
14502
  graph.addNode(node.id, node);
13771
14503
  nodesAdded++;
13772
14504
  }
13773
- const relDockerfile = toPosix(import_node_path56.default.relative(service.dir, dockerfilePath));
13774
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, dockerfilePath));
14505
+ const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14506
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
13775
14507
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13776
14508
  graph,
13777
14509
  service.pkg.name,
@@ -13780,15 +14512,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13780
14512
  );
13781
14513
  nodesAdded += fn;
13782
14514
  edgesAdded += fe;
13783
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types47.EdgeType.RUNS_ON);
14515
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
13784
14516
  if (!graph.hasEdge(edgeId)) {
13785
14517
  const edge = {
13786
14518
  id: edgeId,
13787
14519
  source: fileNodeId,
13788
14520
  target: node.id,
13789
- type: import_types47.EdgeType.RUNS_ON,
13790
- provenance: import_types47.Provenance.EXTRACTED,
13791
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14521
+ type: import_types48.EdgeType.RUNS_ON,
14522
+ provenance: import_types48.Provenance.EXTRACTED,
14523
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13792
14524
  evidence: {
13793
14525
  file: evidenceFile,
13794
14526
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13803,15 +14535,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13803
14535
  graph.addNode(portNode.id, portNode);
13804
14536
  nodesAdded++;
13805
14537
  }
13806
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types47.EdgeType.CONNECTS_TO);
14538
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
13807
14539
  if (graph.hasEdge(portEdgeId)) continue;
13808
14540
  const portEdge = {
13809
14541
  id: portEdgeId,
13810
14542
  source: fileNodeId,
13811
14543
  target: portNode.id,
13812
- type: import_types47.EdgeType.CONNECTS_TO,
13813
- provenance: import_types47.Provenance.EXTRACTED,
13814
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14544
+ type: import_types48.EdgeType.CONNECTS_TO,
14545
+ provenance: import_types48.Provenance.EXTRACTED,
14546
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13815
14547
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13816
14548
  };
13817
14549
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13823,23 +14555,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13823
14555
 
13824
14556
  // src/extract/infra/terraform.ts
13825
14557
  init_cjs_shims();
13826
- var import_node_fs25 = require("fs");
13827
- var import_node_path57 = __toESM(require("path"), 1);
13828
- var import_types48 = require("@neat.is/types");
14558
+ var import_node_fs26 = require("fs");
14559
+ var import_node_path59 = __toESM(require("path"), 1);
14560
+ var import_types49 = require("@neat.is/types");
13829
14561
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13830
14562
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13831
14563
  async function walkTfFiles(start, depth = 0, max = 5) {
13832
14564
  if (depth > max) return [];
13833
14565
  const out = [];
13834
- const entries = await import_node_fs25.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14566
+ const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13835
14567
  for (const entry of entries) {
13836
14568
  if (entry.isDirectory()) {
13837
14569
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
13838
- const child = import_node_path57.default.join(start, entry.name);
14570
+ const child = import_node_path59.default.join(start, entry.name);
13839
14571
  if (await isPythonVenvDir(child)) continue;
13840
14572
  out.push(...await walkTfFiles(child, depth + 1, max));
13841
14573
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
13842
- out.push(import_node_path57.default.join(start, entry.name));
14574
+ out.push(import_node_path59.default.join(start, entry.name));
13843
14575
  }
13844
14576
  }
13845
14577
  return out;
@@ -13870,8 +14602,8 @@ async function addTerraformResources(graph, scanPath) {
13870
14602
  let edgesAdded = 0;
13871
14603
  const files = await walkTfFiles(scanPath);
13872
14604
  for (const file of files) {
13873
- const content = await import_node_fs25.promises.readFile(file, "utf8");
13874
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, file));
14605
+ const content = await import_node_fs26.promises.readFile(file, "utf8");
14606
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
13875
14607
  const resources = [];
13876
14608
  const byKey = /* @__PURE__ */ new Map();
13877
14609
  RESOURCE_RE.lastIndex = 0;
@@ -13906,16 +14638,16 @@ async function addTerraformResources(graph, scanPath) {
13906
14638
  if (!target) continue;
13907
14639
  if (seen.has(target.nodeId)) continue;
13908
14640
  seen.add(target.nodeId);
13909
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types48.EdgeType.DEPENDS_ON);
14641
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
13910
14642
  if (graph.hasEdge(edgeId)) continue;
13911
14643
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13912
14644
  const edge = {
13913
14645
  id: edgeId,
13914
14646
  source: resource.nodeId,
13915
14647
  target: target.nodeId,
13916
- type: import_types48.EdgeType.DEPENDS_ON,
13917
- provenance: import_types48.Provenance.EXTRACTED,
13918
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
14648
+ type: import_types49.EdgeType.DEPENDS_ON,
14649
+ provenance: import_types49.Provenance.EXTRACTED,
14650
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
13919
14651
  evidence: { file: evidenceFile, line, snippet: key }
13920
14652
  };
13921
14653
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13928,8 +14660,8 @@ async function addTerraformResources(graph, scanPath) {
13928
14660
 
13929
14661
  // src/extract/infra/k8s.ts
13930
14662
  init_cjs_shims();
13931
- var import_node_fs26 = require("fs");
13932
- var import_node_path58 = __toESM(require("path"), 1);
14663
+ var import_node_fs27 = require("fs");
14664
+ var import_node_path60 = __toESM(require("path"), 1);
13933
14665
  var import_yaml3 = require("yaml");
13934
14666
  var K8S_KIND_TO_INFRA_KIND = {
13935
14667
  Service: "k8s-service",
@@ -13943,15 +14675,15 @@ var K8S_KIND_TO_INFRA_KIND = {
13943
14675
  async function walkYamlFiles2(start, depth = 0, max = 5) {
13944
14676
  if (depth > max) return [];
13945
14677
  const out = [];
13946
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14678
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13947
14679
  for (const entry of entries) {
13948
14680
  if (entry.isDirectory()) {
13949
14681
  if (IGNORED_DIRS.has(entry.name)) continue;
13950
- const child = import_node_path58.default.join(start, entry.name);
14682
+ const child = import_node_path60.default.join(start, entry.name);
13951
14683
  if (await isPythonVenvDir(child)) continue;
13952
14684
  out.push(...await walkYamlFiles2(child, depth + 1, max));
13953
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path58.default.extname(entry.name))) {
13954
- out.push(import_node_path58.default.join(start, entry.name));
14685
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
14686
+ out.push(import_node_path60.default.join(start, entry.name));
13955
14687
  }
13956
14688
  }
13957
14689
  return out;
@@ -13960,7 +14692,7 @@ async function addK8sResources(graph, scanPath) {
13960
14692
  let nodesAdded = 0;
13961
14693
  const files = await walkYamlFiles2(scanPath);
13962
14694
  for (const file of files) {
13963
- const content = await import_node_fs26.promises.readFile(file, "utf8");
14695
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
13964
14696
  let docs;
13965
14697
  try {
13966
14698
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -13984,16 +14716,16 @@ async function addK8sResources(graph, scanPath) {
13984
14716
 
13985
14717
  // src/extract/infra/cloudflare.ts
13986
14718
  init_cjs_shims();
13987
- var import_node_fs27 = require("fs");
13988
- var import_node_path59 = __toESM(require("path"), 1);
14719
+ var import_node_fs28 = require("fs");
14720
+ var import_node_path61 = __toESM(require("path"), 1);
13989
14721
  var import_smol_toml3 = require("smol-toml");
13990
- var import_types49 = require("@neat.is/types");
14722
+ var import_types50 = require("@neat.is/types");
13991
14723
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
13992
14724
  async function readWranglerConfig(dir) {
13993
14725
  for (const filename of WRANGLER_FILENAMES) {
13994
- const abs = import_node_path59.default.join(dir, filename);
14726
+ const abs = import_node_path61.default.join(dir, filename);
13995
14727
  if (!await exists(abs)) continue;
13996
- const raw = await import_node_fs27.promises.readFile(abs, "utf8");
14728
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
13997
14729
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
13998
14730
  return { config, relFile: filename, raw };
13999
14731
  }
@@ -14035,8 +14767,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14035
14767
  source: anchorId,
14036
14768
  target: node.id,
14037
14769
  type: edgeType,
14038
- provenance: import_types49.Provenance.EXTRACTED,
14039
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14770
+ provenance: import_types50.Provenance.EXTRACTED,
14771
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14040
14772
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14041
14773
  };
14042
14774
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14054,11 +14786,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14054
14786
  try {
14055
14787
  read = await readWranglerConfig(service.dir);
14056
14788
  } catch (err) {
14057
- recordExtractionError("infra cloudflare", import_node_path59.default.relative(scanPath, service.dir), err);
14789
+ recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
14058
14790
  continue;
14059
14791
  }
14060
14792
  if (!read || !read.config.name) continue;
14061
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, import_node_path59.default.join(service.dir, read.relFile)));
14793
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
14062
14794
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14063
14795
  }
14064
14796
  for (const worker of discovered) {
@@ -14070,7 +14802,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14070
14802
  }
14071
14803
  let anchorId = service.node.id;
14072
14804
  if (config.main) {
14073
- const entryRelPath = toPosix(import_node_path59.default.normalize(config.main));
14805
+ const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
14074
14806
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14075
14807
  graph,
14076
14808
  service.pkg.name,
@@ -14097,15 +14829,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14097
14829
  nodesAdded++;
14098
14830
  }
14099
14831
  if (runtimeNode.id !== anchorId) {
14100
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types49.EdgeType.RUNS_ON);
14832
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
14101
14833
  if (!graph.hasEdge(runsOnId)) {
14102
14834
  const edge = {
14103
14835
  id: runsOnId,
14104
14836
  source: anchorId,
14105
14837
  target: runtimeNode.id,
14106
- type: import_types49.EdgeType.RUNS_ON,
14107
- provenance: import_types49.Provenance.EXTRACTED,
14108
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14838
+ type: import_types50.EdgeType.RUNS_ON,
14839
+ provenance: import_types50.Provenance.EXTRACTED,
14840
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14109
14841
  evidence: {
14110
14842
  file: evidenceFile,
14111
14843
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14119,7 +14851,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14119
14851
  const result = addResourceEdge(
14120
14852
  graph,
14121
14853
  anchorId,
14122
- import_types49.EdgeType.CONNECTS_TO,
14854
+ import_types50.EdgeType.CONNECTS_TO,
14123
14855
  "cloudflare-route",
14124
14856
  route,
14125
14857
  evidenceFile,
@@ -14143,7 +14875,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14143
14875
  const result = addResourceEdge(
14144
14876
  graph,
14145
14877
  anchorId,
14146
- import_types49.EdgeType.DEPENDS_ON,
14878
+ import_types50.EdgeType.DEPENDS_ON,
14147
14879
  group.kind,
14148
14880
  name,
14149
14881
  evidenceFile,
@@ -14157,7 +14889,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14157
14889
  const result = addResourceEdge(
14158
14890
  graph,
14159
14891
  anchorId,
14160
- import_types49.EdgeType.DEPENDS_ON,
14892
+ import_types50.EdgeType.DEPENDS_ON,
14161
14893
  "cloudflare-cron",
14162
14894
  cron,
14163
14895
  evidenceFile,
@@ -14170,7 +14902,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14170
14902
  const result = addResourceEdge(
14171
14903
  graph,
14172
14904
  anchorId,
14173
- import_types49.EdgeType.DEPENDS_ON,
14905
+ import_types50.EdgeType.DEPENDS_ON,
14174
14906
  "cloudflare-env-var",
14175
14907
  varName,
14176
14908
  evidenceFile,
@@ -14183,15 +14915,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14183
14915
  if (!svc.service) continue;
14184
14916
  const target = workerIndex.get(svc.service);
14185
14917
  if (target && target.anchorId !== anchorId) {
14186
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types49.EdgeType.CALLS);
14918
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
14187
14919
  if (!graph.hasEdge(edgeId)) {
14188
14920
  const edge = {
14189
14921
  id: edgeId,
14190
14922
  source: anchorId,
14191
14923
  target: target.anchorId,
14192
- type: import_types49.EdgeType.CALLS,
14193
- provenance: import_types49.Provenance.EXTRACTED,
14194
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14924
+ type: import_types50.EdgeType.CALLS,
14925
+ provenance: import_types50.Provenance.EXTRACTED,
14926
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14195
14927
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14196
14928
  };
14197
14929
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14202,7 +14934,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14202
14934
  const result = addResourceEdge(
14203
14935
  graph,
14204
14936
  anchorId,
14205
- import_types49.EdgeType.DEPENDS_ON,
14937
+ import_types50.EdgeType.DEPENDS_ON,
14206
14938
  "cloudflare-service-binding",
14207
14939
  svc.service,
14208
14940
  evidenceFile,
@@ -14217,24 +14949,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14217
14949
 
14218
14950
  // src/extract/infra/vercel.ts
14219
14951
  init_cjs_shims();
14220
- var import_node_fs28 = require("fs");
14221
- var import_node_path60 = __toESM(require("path"), 1);
14222
- var import_types50 = require("@neat.is/types");
14952
+ var import_node_fs29 = require("fs");
14953
+ var import_node_path62 = __toESM(require("path"), 1);
14954
+ var import_types51 = require("@neat.is/types");
14223
14955
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14224
14956
  async function readVercelConfig(dir) {
14225
14957
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14226
- const abs = import_node_path60.default.join(dir, filename);
14958
+ const abs = import_node_path62.default.join(dir, filename);
14227
14959
  if (!await exists(abs)) continue;
14228
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14960
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14229
14961
  const config = JSON.parse(maskCommentsInSource(raw));
14230
14962
  return { config, relFile: filename, raw };
14231
14963
  }
14232
14964
  return null;
14233
14965
  }
14234
14966
  async function readLinkedProjectName(dir) {
14235
- const abs = import_node_path60.default.join(dir, ".vercel", "project.json");
14967
+ const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
14236
14968
  if (!await exists(abs)) return void 0;
14237
- const parsed = JSON.parse(await import_node_fs28.promises.readFile(abs, "utf8"));
14969
+ const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
14238
14970
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14239
14971
  }
14240
14972
  function routeSource(route) {
@@ -14250,7 +14982,7 @@ async function addVercelServices(graph, services, scanPath) {
14250
14982
  read = await readVercelConfig(service.dir);
14251
14983
  projectName = await readLinkedProjectName(service.dir);
14252
14984
  } catch (err) {
14253
- recordExtractionError("infra vercel", import_node_path60.default.relative(scanPath, service.dir), err);
14985
+ recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
14254
14986
  continue;
14255
14987
  }
14256
14988
  if (!read && !projectName) continue;
@@ -14266,7 +14998,7 @@ async function addVercelServices(graph, services, scanPath) {
14266
14998
  const anchorId = service.node.id;
14267
14999
  if (!read) continue;
14268
15000
  const { config, relFile, raw } = read;
14269
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, import_node_path60.default.join(service.dir, relFile)));
15001
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
14270
15002
  const add = (edgeType, kind, name) => {
14271
15003
  if (!name) return;
14272
15004
  const result = emitPlatformResourceEdge(
@@ -14282,12 +15014,12 @@ async function addVercelServices(graph, services, scanPath) {
14282
15014
  nodesAdded += result.nodesAdded;
14283
15015
  edgesAdded += result.edgesAdded;
14284
15016
  };
14285
- add(import_types50.EdgeType.RUNS_ON, "vercel", "vercel");
14286
- for (const cron of config.crons ?? []) add(import_types50.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14287
- for (const varName of Object.keys(config.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14288
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15017
+ add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
15018
+ for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15019
+ for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15020
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14289
15021
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14290
- add(import_types50.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15022
+ add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14291
15023
  }
14292
15024
  }
14293
15025
  return { nodesAdded, edgesAdded };
@@ -14295,16 +15027,16 @@ async function addVercelServices(graph, services, scanPath) {
14295
15027
 
14296
15028
  // src/extract/infra/railway.ts
14297
15029
  init_cjs_shims();
14298
- var import_node_fs29 = require("fs");
14299
- var import_node_path61 = __toESM(require("path"), 1);
15030
+ var import_node_fs30 = require("fs");
15031
+ var import_node_path63 = __toESM(require("path"), 1);
14300
15032
  var import_smol_toml4 = require("smol-toml");
14301
- var import_types51 = require("@neat.is/types");
15033
+ var import_types52 = require("@neat.is/types");
14302
15034
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14303
15035
  async function readRailwayConfig(dir) {
14304
15036
  for (const filename of RAILWAY_FILENAMES) {
14305
- const abs = import_node_path61.default.join(dir, filename);
15037
+ const abs = import_node_path63.default.join(dir, filename);
14306
15038
  if (!await exists(abs)) continue;
14307
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15039
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14308
15040
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14309
15041
  return { config, relFile: filename, raw };
14310
15042
  }
@@ -14318,7 +15050,7 @@ async function addRailwayServices(graph, services, scanPath) {
14318
15050
  try {
14319
15051
  read = await readRailwayConfig(service.dir);
14320
15052
  } catch (err) {
14321
- recordExtractionError("infra railway", import_node_path61.default.relative(scanPath, service.dir), err);
15053
+ recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
14322
15054
  continue;
14323
15055
  }
14324
15056
  if (!read) continue;
@@ -14328,7 +15060,7 @@ async function addRailwayServices(graph, services, scanPath) {
14328
15060
  }
14329
15061
  const anchorId = service.node.id;
14330
15062
  const { config, relFile, raw } = read;
14331
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, relFile)));
15063
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
14332
15064
  const add = (edgeType, kind, name) => {
14333
15065
  if (!name) return;
14334
15066
  const result = emitPlatformResourceEdge(
@@ -14344,24 +15076,24 @@ async function addRailwayServices(graph, services, scanPath) {
14344
15076
  nodesAdded += result.nodesAdded;
14345
15077
  edgesAdded += result.edgesAdded;
14346
15078
  };
14347
- add(import_types51.EdgeType.RUNS_ON, "railway", "railway");
14348
- add(import_types51.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14349
- add(import_types51.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15079
+ add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15080
+ add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15081
+ add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14350
15082
  }
14351
15083
  return { nodesAdded, edgesAdded };
14352
15084
  }
14353
15085
 
14354
15086
  // src/extract/infra/supabase.ts
14355
15087
  init_cjs_shims();
14356
- var import_node_fs30 = require("fs");
14357
- var import_node_path62 = __toESM(require("path"), 1);
15088
+ var import_node_fs31 = require("fs");
15089
+ var import_node_path64 = __toESM(require("path"), 1);
14358
15090
  var import_smol_toml5 = require("smol-toml");
14359
- var import_types52 = require("@neat.is/types");
15091
+ var import_types53 = require("@neat.is/types");
14360
15092
  async function readSupabaseConfig(dir) {
14361
- const relFile = import_node_path62.default.join("supabase", "config.toml");
14362
- const abs = import_node_path62.default.join(dir, relFile);
15093
+ const relFile = import_node_path64.default.join("supabase", "config.toml");
15094
+ const abs = import_node_path64.default.join(dir, relFile);
14363
15095
  if (!await exists(abs)) return null;
14364
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15096
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14365
15097
  const config = (0, import_smol_toml5.parse)(raw);
14366
15098
  return { config, relFile, raw };
14367
15099
  }
@@ -14373,7 +15105,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14373
15105
  try {
14374
15106
  read = await readSupabaseConfig(service.dir);
14375
15107
  } catch (err) {
14376
- recordExtractionError("infra supabase", import_node_path62.default.relative(scanPath, service.dir), err);
15108
+ recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
14377
15109
  continue;
14378
15110
  }
14379
15111
  if (!read) continue;
@@ -14388,7 +15120,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14388
15120
  });
14389
15121
  }
14390
15122
  const anchorId = service.node.id;
14391
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15123
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14392
15124
  const add = (edgeType, kind, name) => {
14393
15125
  if (!name) return;
14394
15126
  const result = emitPlatformResourceEdge(
@@ -14404,10 +15136,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14404
15136
  nodesAdded += result.nodesAdded;
14405
15137
  edgesAdded += result.edgesAdded;
14406
15138
  };
14407
- add(import_types52.EdgeType.RUNS_ON, "supabase", "supabase");
14408
- for (const fn of Object.keys(config.functions ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "supabase-function", fn);
14409
- if (config.storage) add(import_types52.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14410
- if (config.auth) add(import_types52.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15139
+ add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15140
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15141
+ if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15142
+ if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
14411
15143
  }
14412
15144
  return { nodesAdded, edgesAdded };
14413
15145
  }
@@ -14430,14 +15162,14 @@ async function addInfra(graph, scanPath, services) {
14430
15162
 
14431
15163
  // src/extract/zod-shapes.ts
14432
15164
  init_cjs_shims();
14433
- var import_node_path63 = __toESM(require("path"), 1);
14434
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15165
+ var import_node_path65 = __toESM(require("path"), 1);
15166
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14435
15167
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
14436
- var import_types53 = require("@neat.is/types");
15168
+ var import_types54 = require("@neat.is/types");
14437
15169
  var ZOD_IMPORT_RE = /\bzod\b/;
14438
15170
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
14439
15171
  function parserForExt4(ext) {
14440
- const p = new import_tree_sitter16.default();
15172
+ const p = new import_tree_sitter17.default();
14441
15173
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
14442
15174
  return p;
14443
15175
  }
@@ -14525,7 +15257,7 @@ function topLevelSchemas(root) {
14525
15257
  }
14526
15258
  function zodShapesFromFile(file, serviceDir) {
14527
15259
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14528
- const tree = parseSource3(parserForExt4(import_node_path63.default.extname(file.path)), file.content);
15260
+ const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
14529
15261
  const out = [];
14530
15262
  const seen = /* @__PURE__ */ new Set();
14531
15263
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14539,11 +15271,11 @@ function zodShapesFromFile(file, serviceDir) {
14539
15271
  seen.add(name);
14540
15272
  const line = call.startPosition.row + 1;
14541
15273
  out.push({
14542
- infraId: (0, import_types53.infraId)("zod-schema", name),
15274
+ infraId: (0, import_types54.infraId)("zod-schema", name),
14543
15275
  name,
14544
15276
  fields,
14545
15277
  evidence: {
14546
- file: import_node_path63.default.relative(serviceDir, file.path),
15278
+ file: import_node_path65.default.relative(serviceDir, file.path),
14547
15279
  line,
14548
15280
  snippet: snippet(file.content, line)
14549
15281
  }
@@ -14574,7 +15306,7 @@ async function addZodShapes(graph, services) {
14574
15306
  if (!graph.hasNode(shape.infraId)) {
14575
15307
  const node = {
14576
15308
  id: shape.infraId,
14577
- type: import_types53.NodeType.InfraNode,
15309
+ type: import_types54.NodeType.InfraNode,
14578
15310
  name: shape.name,
14579
15311
  provider: "self",
14580
15312
  kind: "zod-schema"
@@ -14584,14 +15316,14 @@ async function addZodShapes(graph, services) {
14584
15316
  }
14585
15317
  if (shape.fields.length > 0) {
14586
15318
  const node = graph.getNodeAttributes(shape.infraId);
14587
- if (node.type === import_types53.NodeType.InfraNode) {
15319
+ if (node.type === import_types54.NodeType.InfraNode) {
14588
15320
  graph.replaceNodeAttributes(shape.infraId, {
14589
15321
  ...node,
14590
15322
  columns: foldColumns(
14591
15323
  node.columns,
14592
15324
  shape.fields,
14593
- import_types53.Provenance.EXTRACTED,
14594
- (0, import_types53.confidenceForExtracted)("structural")
15325
+ import_types54.Provenance.EXTRACTED,
15326
+ (0, import_types54.confidenceForExtracted)("structural")
14595
15327
  )
14596
15328
  });
14597
15329
  }
@@ -14605,15 +15337,15 @@ async function addZodShapes(graph, services) {
14605
15337
  );
14606
15338
  nodesAdded += n;
14607
15339
  edgesAdded += e;
14608
- const edgeId = (0, import_types53.extractedEdgeId)(fileNodeId, shape.infraId, import_types53.EdgeType.CONTAINS);
15340
+ const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
14609
15341
  if (!graph.hasEdge(edgeId)) {
14610
15342
  const edge = {
14611
15343
  id: edgeId,
14612
15344
  source: fileNodeId,
14613
15345
  target: shape.infraId,
14614
- type: import_types53.EdgeType.CONTAINS,
14615
- provenance: import_types53.Provenance.EXTRACTED,
14616
- confidence: (0, import_types53.confidenceForExtracted)("structural"),
15346
+ type: import_types54.EdgeType.CONTAINS,
15347
+ provenance: import_types54.Provenance.EXTRACTED,
15348
+ confidence: (0, import_types54.confidenceForExtracted)("structural"),
14617
15349
  evidence: shape.evidence
14618
15350
  };
14619
15351
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14627,7 +15359,7 @@ async function addZodShapes(graph, services) {
14627
15359
 
14628
15360
  // src/extract/firestore-rules.ts
14629
15361
  init_cjs_shims();
14630
- var import_types54 = require("@neat.is/types");
15362
+ var import_types55 = require("@neat.is/types");
14631
15363
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14632
15364
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14633
15365
  function stripComments(src) {
@@ -14767,7 +15499,7 @@ async function addFirestoreRules(graph, services) {
14767
15499
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14768
15500
  graph.forEachNode((id, attrs) => {
14769
15501
  const node = attrs;
14770
- if (node.type !== import_types54.NodeType.InfraNode) return;
15502
+ if (node.type !== import_types55.NodeType.InfraNode) return;
14771
15503
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14772
15504
  const fields = guards.get(collectionKeyFromName(node.name));
14773
15505
  if (!fields || fields.size === 0) return;
@@ -14780,17 +15512,17 @@ async function addFirestoreRules(graph, services) {
14780
15512
  }
14781
15513
 
14782
15514
  // src/extract/index.ts
14783
- var import_node_path65 = __toESM(require("path"), 1);
15515
+ var import_node_path67 = __toESM(require("path"), 1);
14784
15516
 
14785
15517
  // src/extract/retire.ts
14786
15518
  init_cjs_shims();
14787
- var import_node_fs31 = require("fs");
14788
- var import_node_path64 = __toESM(require("path"), 1);
14789
- var import_types55 = require("@neat.is/types");
15519
+ var import_node_fs32 = require("fs");
15520
+ var import_node_path66 = __toESM(require("path"), 1);
15521
+ var import_types56 = require("@neat.is/types");
14790
15522
  function dropOrphanedFileNodes(graph) {
14791
15523
  const orphans = [];
14792
15524
  graph.forEachNode((id, attrs) => {
14793
- if (attrs.type !== import_types55.NodeType.FileNode) return;
15525
+ if (attrs.type !== import_types56.NodeType.FileNode) return;
14794
15526
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14795
15527
  orphans.push(id);
14796
15528
  }
@@ -14803,14 +15535,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14803
15535
  const bases = [scanPath, ...serviceDirs];
14804
15536
  graph.forEachEdge((id, attrs) => {
14805
15537
  const edge = attrs;
14806
- if (edge.provenance !== import_types55.Provenance.EXTRACTED) return;
15538
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
14807
15539
  const evidenceFile = edge.evidence?.file;
14808
15540
  if (!evidenceFile) return;
14809
- if (import_node_path64.default.isAbsolute(evidenceFile)) {
14810
- if (!(0, import_node_fs31.existsSync)(evidenceFile)) toDrop.push(id);
15541
+ if (import_node_path66.default.isAbsolute(evidenceFile)) {
15542
+ if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
14811
15543
  return;
14812
15544
  }
14813
- const found = bases.some((base) => (0, import_node_fs31.existsSync)(import_node_path64.default.join(base, evidenceFile)));
15545
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
14814
15546
  if (!found) toDrop.push(id);
14815
15547
  });
14816
15548
  for (const id of toDrop) graph.dropEdge(id);
@@ -14867,7 +15599,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14867
15599
  }
14868
15600
  const droppedEntries = drainDroppedExtracted();
14869
15601
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14870
- const rejectedPath = import_node_path65.default.join(import_node_path65.default.dirname(opts.errorsPath), "rejected.ndjson");
15602
+ const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
14871
15603
  try {
14872
15604
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14873
15605
  } catch (err) {
@@ -14901,9 +15633,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14901
15633
 
14902
15634
  // src/persist.ts
14903
15635
  init_cjs_shims();
14904
- var import_node_fs32 = require("fs");
14905
- var import_node_path66 = __toESM(require("path"), 1);
14906
- var import_types56 = require("@neat.is/types");
15636
+ var import_node_fs33 = require("fs");
15637
+ var import_node_path68 = __toESM(require("path"), 1);
15638
+ var import_types57 = require("@neat.is/types");
14907
15639
  var SCHEMA_VERSION = 7;
14908
15640
  function migrateV1ToV2(payload) {
14909
15641
  const nodes = payload.graph.nodes;
@@ -14927,7 +15659,7 @@ function migrateV5ToV6(payload) {
14927
15659
  if (Array.isArray(nodes)) {
14928
15660
  for (const node of nodes) {
14929
15661
  const attrs = node.attributes;
14930
- if (!attrs || attrs.type !== import_types56.NodeType.InfraNode) continue;
15662
+ if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
14931
15663
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14932
15664
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14933
15665
  }
@@ -14943,12 +15675,12 @@ function migrateV2ToV3(payload) {
14943
15675
  for (const edge of edges) {
14944
15676
  const attrs = edge.attributes;
14945
15677
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
14946
- attrs.provenance = import_types56.Provenance.OBSERVED;
15678
+ attrs.provenance = import_types57.Provenance.OBSERVED;
14947
15679
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
14948
15680
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
14949
15681
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
14950
15682
  if (type && source && target) {
14951
- const newId = (0, import_types56.observedEdgeId)(source, target, type);
15683
+ const newId = (0, import_types57.observedEdgeId)(source, target, type);
14952
15684
  attrs.id = newId;
14953
15685
  if (edge.key) edge.key = newId;
14954
15686
  }
@@ -14957,7 +15689,7 @@ function migrateV2ToV3(payload) {
14957
15689
  return { ...payload, schemaVersion: 3 };
14958
15690
  }
14959
15691
  async function ensureDir(filePath) {
14960
- await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(filePath), { recursive: true });
15692
+ await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
14961
15693
  }
14962
15694
  async function saveGraphToDisk(graph, outPath) {
14963
15695
  await ensureDir(outPath);
@@ -14967,13 +15699,13 @@ async function saveGraphToDisk(graph, outPath) {
14967
15699
  graph: graph.export()
14968
15700
  };
14969
15701
  const tmp = `${outPath}.tmp`;
14970
- await import_node_fs32.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
14971
- await import_node_fs32.promises.rename(tmp, outPath);
15702
+ await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15703
+ await import_node_fs33.promises.rename(tmp, outPath);
14972
15704
  }
14973
15705
  async function loadGraphFromDisk(graph, outPath) {
14974
15706
  let raw;
14975
15707
  try {
14976
- raw = await import_node_fs32.promises.readFile(outPath, "utf8");
15708
+ raw = await import_node_fs33.promises.readFile(outPath, "utf8");
14977
15709
  } catch (err) {
14978
15710
  if (err.code === "ENOENT") return;
14979
15711
  throw err;
@@ -15049,19 +15781,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
15049
15781
  init_cjs_shims();
15050
15782
  var import_fastify2 = __toESM(require("fastify"), 1);
15051
15783
  var import_cors = __toESM(require("@fastify/cors"), 1);
15052
- var import_types91 = require("@neat.is/types");
15784
+ var import_types92 = require("@neat.is/types");
15053
15785
 
15054
15786
  // src/extend/index.ts
15055
15787
  init_cjs_shims();
15056
- var import_node_fs34 = require("fs");
15057
- var import_node_path68 = __toESM(require("path"), 1);
15788
+ var import_node_fs35 = require("fs");
15789
+ var import_node_path70 = __toESM(require("path"), 1);
15058
15790
  var import_node_os2 = __toESM(require("os"), 1);
15059
15791
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
15060
15792
 
15061
15793
  // src/installers/package-manager.ts
15062
15794
  init_cjs_shims();
15063
- var import_node_fs33 = require("fs");
15064
- var import_node_path67 = __toESM(require("path"), 1);
15795
+ var import_node_fs34 = require("fs");
15796
+ var import_node_path69 = __toESM(require("path"), 1);
15065
15797
  var import_node_child_process = require("child_process");
15066
15798
  var LOCKFILE_PRIORITY = [
15067
15799
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -15076,29 +15808,29 @@ var LOCKFILE_PRIORITY = [
15076
15808
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
15077
15809
  async function exists2(p) {
15078
15810
  try {
15079
- await import_node_fs33.promises.access(p);
15811
+ await import_node_fs34.promises.access(p);
15080
15812
  return true;
15081
15813
  } catch {
15082
15814
  return false;
15083
15815
  }
15084
15816
  }
15085
15817
  async function detectPackageManager(serviceDir) {
15086
- let dir = import_node_path67.default.resolve(serviceDir);
15818
+ let dir = import_node_path69.default.resolve(serviceDir);
15087
15819
  const stops = /* @__PURE__ */ new Set();
15088
15820
  for (let i = 0; i < 64; i++) {
15089
15821
  if (stops.has(dir)) break;
15090
15822
  stops.add(dir);
15091
15823
  for (const candidate of LOCKFILE_PRIORITY) {
15092
- const lockPath = import_node_path67.default.join(dir, candidate.lockfile);
15824
+ const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
15093
15825
  if (await exists2(lockPath)) {
15094
15826
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
15095
15827
  }
15096
15828
  }
15097
- const parent = import_node_path67.default.dirname(dir);
15829
+ const parent = import_node_path69.default.dirname(dir);
15098
15830
  if (parent === dir) break;
15099
15831
  dir = parent;
15100
15832
  }
15101
- return { pm: "npm", cwd: import_node_path67.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15833
+ return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15102
15834
  }
15103
15835
  async function runPackageManagerInstall(cmd) {
15104
15836
  return new Promise((resolve) => {
@@ -15140,15 +15872,15 @@ ${err.message}`
15140
15872
  // src/extend/index.ts
15141
15873
  async function fileExists2(p) {
15142
15874
  try {
15143
- await import_node_fs34.promises.access(p);
15875
+ await import_node_fs35.promises.access(p);
15144
15876
  return true;
15145
15877
  } catch {
15146
15878
  return false;
15147
15879
  }
15148
15880
  }
15149
15881
  async function readPackageJson(scanPath) {
15150
- const pkgPath = import_node_path68.default.join(scanPath, "package.json");
15151
- const raw = await import_node_fs34.promises.readFile(pkgPath, "utf8");
15882
+ const pkgPath = import_node_path70.default.join(scanPath, "package.json");
15883
+ const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
15152
15884
  return JSON.parse(raw);
15153
15885
  }
15154
15886
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -15161,27 +15893,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
15161
15893
  ]);
15162
15894
  async function findHookFiles(scanPath) {
15163
15895
  const found = [];
15164
- const walk9 = async (dir) => {
15165
- const entries = await import_node_fs34.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15896
+ const walk10 = async (dir) => {
15897
+ const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15166
15898
  for (const entry of entries) {
15167
15899
  if (entry.isDirectory()) {
15168
15900
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
15169
- await walk9(import_node_path68.default.join(dir, entry.name));
15901
+ await walk10(import_node_path70.default.join(dir, entry.name));
15170
15902
  } else if (entry.isFile()) {
15171
15903
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
15172
- const rel = import_node_path68.default.relative(scanPath, import_node_path68.default.join(dir, entry.name));
15173
- found.push(rel.split(import_node_path68.default.sep).join("/"));
15904
+ const rel = import_node_path70.default.relative(scanPath, import_node_path70.default.join(dir, entry.name));
15905
+ found.push(rel.split(import_node_path70.default.sep).join("/"));
15174
15906
  }
15175
15907
  }
15176
15908
  }
15177
15909
  };
15178
- await walk9(scanPath);
15910
+ await walk10(scanPath);
15179
15911
  return found.sort();
15180
15912
  }
15181
15913
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15182
15914
  let fallback = null;
15183
15915
  for (const file of hookFiles) {
15184
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(scanPath, file), "utf8");
15916
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
15185
15917
  const patched = splicedContent(content, snippet2);
15186
15918
  if (patched !== null) return { file, content, patched };
15187
15919
  if (fallback === null) fallback = { file, content };
@@ -15189,12 +15921,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15189
15921
  return { file: fallback.file, content: fallback.content, patched: null };
15190
15922
  }
15191
15923
  function extendLogPath() {
15192
- return process.env.NEAT_EXTEND_LOG ?? import_node_path68.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15924
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path70.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15193
15925
  }
15194
15926
  async function appendExtendLog(entry) {
15195
15927
  const logPath = extendLogPath();
15196
- await import_node_fs34.promises.mkdir(import_node_path68.default.dirname(logPath), { recursive: true });
15197
- await import_node_fs34.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
15928
+ await import_node_fs35.promises.mkdir(import_node_path70.default.dirname(logPath), { recursive: true });
15929
+ await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
15198
15930
  }
15199
15931
  function splicedContent(fileContent, snippet2) {
15200
15932
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -15252,7 +15984,7 @@ function lookupInstrumentation(library, installedVersion) {
15252
15984
  }
15253
15985
  async function describeProjectInstrumentation(ctx) {
15254
15986
  const hookFiles = await findHookFiles(ctx.scanPath);
15255
- const envNeat = await fileExists2(import_node_path68.default.join(ctx.scanPath, ".env.neat"));
15987
+ const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
15256
15988
  const registryInstrPackages = new Set(
15257
15989
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
15258
15990
  );
@@ -15274,7 +16006,7 @@ async function applyExtension(ctx, args, options) {
15274
16006
  );
15275
16007
  }
15276
16008
  for (const file of hookFiles) {
15277
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(ctx.scanPath, file), "utf8");
16009
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
15278
16010
  if (content.includes(args.registration_snippet)) {
15279
16011
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
15280
16012
  }
@@ -15286,18 +16018,18 @@ async function applyExtension(ctx, args, options) {
15286
16018
  );
15287
16019
  }
15288
16020
  const primaryFile = primary.file;
15289
- const primaryPath = import_node_path68.default.join(ctx.scanPath, primaryFile);
16021
+ const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
15290
16022
  const filesTouched = [];
15291
16023
  const depsAdded = [];
15292
- const pkgPath = import_node_path68.default.join(ctx.scanPath, "package.json");
16024
+ const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
15293
16025
  const pkg = await readPackageJson(ctx.scanPath);
15294
16026
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
15295
16027
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
15296
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16028
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15297
16029
  filesTouched.push("package.json");
15298
16030
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
15299
16031
  }
15300
- await import_node_fs34.promises.writeFile(primaryPath, primary.patched, "utf8");
16032
+ await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
15301
16033
  filesTouched.push(primaryFile);
15302
16034
  const cmd = await detectPackageManager(ctx.scanPath);
15303
16035
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -15328,7 +16060,7 @@ async function dryRunExtension(ctx, args) {
15328
16060
  };
15329
16061
  }
15330
16062
  for (const file of hookFiles) {
15331
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(ctx.scanPath, file), "utf8");
16063
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
15332
16064
  if (content.includes(args.registration_snippet)) {
15333
16065
  return {
15334
16066
  library: args.library,
@@ -15363,28 +16095,28 @@ async function rollbackExtension(ctx, args) {
15363
16095
  if (!await fileExists2(logPath)) {
15364
16096
  return { undone: false, message: "no apply found for library" };
15365
16097
  }
15366
- const raw = await import_node_fs34.promises.readFile(logPath, "utf8");
16098
+ const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
15367
16099
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
15368
16100
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
15369
16101
  if (!match) {
15370
16102
  return { undone: false, message: "no apply found for library" };
15371
16103
  }
15372
- const pkgPath = import_node_path68.default.join(ctx.scanPath, "package.json");
16104
+ const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
15373
16105
  if (await fileExists2(pkgPath)) {
15374
16106
  const pkg = await readPackageJson(ctx.scanPath);
15375
16107
  if (pkg.dependencies?.[match.instrumentation_package]) {
15376
16108
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
15377
16109
  pkg.dependencies = rest;
15378
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16110
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15379
16111
  }
15380
16112
  }
15381
16113
  const hookFiles = await findHookFiles(ctx.scanPath);
15382
16114
  for (const file of hookFiles) {
15383
- const filePath = import_node_path68.default.join(ctx.scanPath, file);
15384
- const content = await import_node_fs34.promises.readFile(filePath, "utf8");
16115
+ const filePath = import_node_path70.default.join(ctx.scanPath, file);
16116
+ const content = await import_node_fs35.promises.readFile(filePath, "utf8");
15385
16117
  if (content.includes(match.registration_snippet)) {
15386
16118
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
15387
- await import_node_fs34.promises.writeFile(filePath, filtered, "utf8");
16119
+ await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
15388
16120
  break;
15389
16121
  }
15390
16122
  }
@@ -15396,39 +16128,39 @@ async function rollbackExtension(ctx, args) {
15396
16128
 
15397
16129
  // src/divergences.ts
15398
16130
  init_cjs_shims();
15399
- var import_types57 = require("@neat.is/types");
16131
+ var import_types58 = require("@neat.is/types");
15400
16132
  function bucketKey(source, target, type) {
15401
16133
  return `${type}|${source}|${target}`;
15402
16134
  }
15403
16135
  function bucketSourceFor(graph, edge) {
15404
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) return edge.source;
15405
- const parsed = (0, import_types57.parseFileId)(edge.source);
16136
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16137
+ const parsed = (0, import_types58.parseFileId)(edge.source);
15406
16138
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
15407
16139
  const target = graph.getNodeAttributes(edge.target);
15408
- if (target.type !== import_types57.NodeType.DatabaseNode) return edge.source;
15409
- return (0, import_types57.serviceId)(parsed.service);
16140
+ if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16141
+ return (0, import_types58.serviceId)(parsed.service);
15410
16142
  }
15411
16143
  function bucketEdges(graph) {
15412
16144
  const buckets2 = /* @__PURE__ */ new Map();
15413
16145
  graph.forEachEdge((id, attrs) => {
15414
16146
  const e = attrs;
15415
- const parsed = (0, import_types57.parseEdgeId)(id);
16147
+ const parsed = (0, import_types58.parseEdgeId)(id);
15416
16148
  const provenance = parsed?.provenance ?? e.provenance;
15417
16149
  const source = bucketSourceFor(graph, e);
15418
16150
  const key = bucketKey(source, e.target, e.type);
15419
16151
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
15420
16152
  switch (provenance) {
15421
- case import_types57.Provenance.EXTRACTED:
16153
+ case import_types58.Provenance.EXTRACTED:
15422
16154
  cur.extracted = e;
15423
16155
  break;
15424
- case import_types57.Provenance.OBSERVED:
16156
+ case import_types58.Provenance.OBSERVED:
15425
16157
  cur.observed = e;
15426
16158
  break;
15427
- case import_types57.Provenance.INFERRED:
16159
+ case import_types58.Provenance.INFERRED:
15428
16160
  cur.inferred = e;
15429
16161
  break;
15430
16162
  default:
15431
- if (e.provenance === import_types57.Provenance.STALE) cur.stale = e;
16163
+ if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
15432
16164
  }
15433
16165
  buckets2.set(key, cur);
15434
16166
  });
@@ -15437,22 +16169,22 @@ function bucketEdges(graph) {
15437
16169
  function nodeIsFrontier(graph, nodeId) {
15438
16170
  if (!graph.hasNode(nodeId)) return false;
15439
16171
  const attrs = graph.getNodeAttributes(nodeId);
15440
- return attrs.type === import_types57.NodeType.FrontierNode;
16172
+ return attrs.type === import_types58.NodeType.FrontierNode;
15441
16173
  }
15442
16174
  function nodeIsWebsocketChannel(graph, nodeId) {
15443
16175
  if (!graph.hasNode(nodeId)) return false;
15444
16176
  const attrs = graph.getNodeAttributes(nodeId);
15445
- return attrs.type === import_types57.NodeType.WebSocketChannelNode;
16177
+ return attrs.type === import_types58.NodeType.WebSocketChannelNode;
15446
16178
  }
15447
16179
  function nodeIsServerAction(graph, nodeId) {
15448
16180
  if (!graph.hasNode(nodeId)) return false;
15449
16181
  const attrs = graph.getNodeAttributes(nodeId);
15450
- return attrs.type === import_types57.NodeType.ServerActionNode;
16182
+ return attrs.type === import_types58.NodeType.ServerActionNode;
15451
16183
  }
15452
16184
  function nodeIsSymbol(graph, nodeId) {
15453
16185
  if (!graph.hasNode(nodeId)) return false;
15454
16186
  const attrs = graph.getNodeAttributes(nodeId);
15455
- return attrs.type === import_types57.NodeType.SymbolNode;
16187
+ return attrs.type === import_types58.NodeType.SymbolNode;
15456
16188
  }
15457
16189
  function clampConfidence(n) {
15458
16190
  if (!Number.isFinite(n)) return 0;
@@ -15472,14 +16204,14 @@ function gradedConfidence(edge) {
15472
16204
  return clampConfidence(confidenceForEdge(edge));
15473
16205
  }
15474
16206
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
15475
- import_types57.EdgeType.CALLS,
15476
- import_types57.EdgeType.CONNECTS_TO,
15477
- import_types57.EdgeType.PUBLISHES_TO,
15478
- import_types57.EdgeType.CONSUMES_FROM
16207
+ import_types58.EdgeType.CALLS,
16208
+ import_types58.EdgeType.CONNECTS_TO,
16209
+ import_types58.EdgeType.PUBLISHES_TO,
16210
+ import_types58.EdgeType.CONSUMES_FROM
15479
16211
  ]);
15480
16212
  function detectMissingDivergences(graph, bucket) {
15481
16213
  const out = [];
15482
- if (bucket.type === import_types57.EdgeType.CONTAINS) return out;
16214
+ if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
15483
16215
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
15484
16216
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
15485
16217
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -15521,7 +16253,7 @@ function declaredHostFor(svc) {
15521
16253
  function hasExtractedConfiguredBy(graph, svcId) {
15522
16254
  for (const edgeId of graph.outboundEdges(svcId)) {
15523
16255
  const e = graph.getEdgeAttributes(edgeId);
15524
- if (e.type === import_types57.EdgeType.CONFIGURED_BY && e.provenance === import_types57.Provenance.EXTRACTED) {
16256
+ if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
15525
16257
  return true;
15526
16258
  }
15527
16259
  }
@@ -15534,10 +16266,10 @@ function detectHostMismatch(graph, svcId, svc) {
15534
16266
  const out = [];
15535
16267
  for (const edgeId of graph.outboundEdges(svcId)) {
15536
16268
  const edge = graph.getEdgeAttributes(edgeId);
15537
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15538
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16269
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16270
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15539
16271
  const target = graph.getNodeAttributes(edge.target);
15540
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16272
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15541
16273
  const observedHost = target.host?.trim();
15542
16274
  if (!observedHost) continue;
15543
16275
  if (observedHost === declaredHost) continue;
@@ -15559,10 +16291,10 @@ function detectCompatDivergences(graph, svcId, svc) {
15559
16291
  const deps = svc.dependencies ?? {};
15560
16292
  for (const edgeId of graph.outboundEdges(svcId)) {
15561
16293
  const edge = graph.getEdgeAttributes(edgeId);
15562
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15563
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16294
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16295
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15564
16296
  const target = graph.getNodeAttributes(edge.target);
15565
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16297
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15566
16298
  for (const pair of compatPairs()) {
15567
16299
  if (pair.engine !== target.engine) continue;
15568
16300
  const declared = deps[pair.driver];
@@ -15659,7 +16391,7 @@ function suppressHostMismatchHalves(all) {
15659
16391
  for (const d of all) {
15660
16392
  if (d.type !== "host-mismatch") continue;
15661
16393
  observedHalf.add(`${d.source}->${d.target}`);
15662
- declaredHalf.add((0, import_types57.databaseId)(d.extractedHost));
16394
+ declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
15663
16395
  }
15664
16396
  if (observedHalf.size === 0) return all;
15665
16397
  return all.filter((d) => {
@@ -15678,13 +16410,13 @@ function computeDivergences(graph, opts = {}) {
15678
16410
  }
15679
16411
  graph.forEachNode((nodeId, attrs) => {
15680
16412
  const n = attrs;
15681
- if (n.type === import_types57.NodeType.ServiceNode) {
16413
+ if (n.type === import_types58.NodeType.ServiceNode) {
15682
16414
  const svc = n;
15683
16415
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
15684
16416
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
15685
16417
  return;
15686
16418
  }
15687
- if (n.type === import_types57.NodeType.InfraNode && n.kind === "sql-table") {
16419
+ if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
15688
16420
  for (const d of detectColumnDrift(n)) all.push(d);
15689
16421
  }
15690
16422
  });
@@ -15720,7 +16452,7 @@ function computeDivergences(graph, opts = {}) {
15720
16452
  const bc = "column" in b && b.column ? b.column : "";
15721
16453
  return ac.localeCompare(bc);
15722
16454
  });
15723
- return import_types57.DivergenceResultSchema.parse({
16455
+ return import_types58.DivergenceResultSchema.parse({
15724
16456
  divergences: filtered,
15725
16457
  totalAffected: filtered.length,
15726
16458
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -15774,7 +16506,7 @@ function queryLogEntries(opts) {
15774
16506
 
15775
16507
  // src/diff.ts
15776
16508
  init_cjs_shims();
15777
- var import_node_fs35 = require("fs");
16509
+ var import_node_fs36 = require("fs");
15778
16510
  async function loadSnapshotForDiff(target) {
15779
16511
  if (/^https?:\/\//i.test(target)) {
15780
16512
  const res = await fetch(target);
@@ -15783,7 +16515,7 @@ async function loadSnapshotForDiff(target) {
15783
16515
  }
15784
16516
  return await res.json();
15785
16517
  }
15786
- const raw = await import_node_fs35.promises.readFile(target, "utf8");
16518
+ const raw = await import_node_fs36.promises.readFile(target, "utf8");
15787
16519
  return JSON.parse(raw);
15788
16520
  }
15789
16521
  function indexEntries(entries) {
@@ -15851,23 +16583,23 @@ function canonicalJson(value) {
15851
16583
 
15852
16584
  // src/projects.ts
15853
16585
  init_cjs_shims();
15854
- var import_node_path69 = __toESM(require("path"), 1);
16586
+ var import_node_path71 = __toESM(require("path"), 1);
15855
16587
  function pathsForProject(project, baseDir) {
15856
16588
  if (project === DEFAULT_PROJECT) {
15857
16589
  return {
15858
- snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
15859
- errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
15860
- staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
15861
- embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
15862
- policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
16590
+ snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
16591
+ errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
16592
+ staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
16593
+ embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
16594
+ policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
15863
16595
  };
15864
16596
  }
15865
16597
  return {
15866
- snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
15867
- errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
15868
- staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
15869
- embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
15870
- policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
16598
+ snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
16599
+ errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
16600
+ staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
16601
+ embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
16602
+ policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
15871
16603
  };
15872
16604
  }
15873
16605
  var Projects = class {
@@ -15903,28 +16635,28 @@ var Projects = class {
15903
16635
 
15904
16636
  // src/registry.ts
15905
16637
  init_cjs_shims();
15906
- var import_node_fs36 = require("fs");
16638
+ var import_node_fs37 = require("fs");
15907
16639
  var import_node_os3 = __toESM(require("os"), 1);
15908
- var import_node_path70 = __toESM(require("path"), 1);
15909
- var import_types58 = require("@neat.is/types");
16640
+ var import_node_path72 = __toESM(require("path"), 1);
16641
+ var import_types59 = require("@neat.is/types");
15910
16642
  var LOCK_TIMEOUT_MS = 5e3;
15911
16643
  var LOCK_RETRY_MS = 50;
15912
16644
  function neatHome() {
15913
16645
  const override = process.env.NEAT_HOME;
15914
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15915
- return import_node_path70.default.join(import_node_os3.default.homedir(), ".neat");
16646
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16647
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15916
16648
  }
15917
16649
  function registryPath() {
15918
- return import_node_path70.default.join(neatHome(), "projects.json");
16650
+ return import_node_path72.default.join(neatHome(), "projects.json");
15919
16651
  }
15920
16652
  function registryLockPath() {
15921
- return import_node_path70.default.join(neatHome(), "projects.json.lock");
16653
+ return import_node_path72.default.join(neatHome(), "projects.json.lock");
15922
16654
  }
15923
16655
  function daemonPidPath() {
15924
- return import_node_path70.default.join(neatHome(), "neatd.pid");
16656
+ return import_node_path72.default.join(neatHome(), "neatd.pid");
15925
16657
  }
15926
16658
  function daemonsDir() {
15927
- return import_node_path70.default.join(neatHome(), "daemons");
16659
+ return import_node_path72.default.join(neatHome(), "daemons");
15928
16660
  }
15929
16661
  function isFiniteInt(v) {
15930
16662
  return typeof v === "number" && Number.isFinite(v);
@@ -15957,7 +16689,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15957
16689
  const dir = daemonsDir();
15958
16690
  let names;
15959
16691
  try {
15960
- names = await import_node_fs36.promises.readdir(dir);
16692
+ names = await import_node_fs37.promises.readdir(dir);
15961
16693
  } catch (err) {
15962
16694
  if (err.code === "ENOENT") return [];
15963
16695
  throw err;
@@ -15965,10 +16697,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15965
16697
  const out = [];
15966
16698
  for (const name of names) {
15967
16699
  if (!name.endsWith(".json")) continue;
15968
- const file = import_node_path70.default.join(dir, name);
16700
+ const file = import_node_path72.default.join(dir, name);
15969
16701
  let raw;
15970
16702
  try {
15971
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16703
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15972
16704
  } catch {
15973
16705
  continue;
15974
16706
  }
@@ -15994,7 +16726,7 @@ function isPidAliveDefault(pid) {
15994
16726
  }
15995
16727
  async function readPidFile(file) {
15996
16728
  try {
15997
- const raw = await import_node_fs36.promises.readFile(file, "utf8");
16729
+ const raw = await import_node_fs37.promises.readFile(file, "utf8");
15998
16730
  const pid = Number.parseInt(raw.trim(), 10);
15999
16731
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
16000
16732
  } catch {
@@ -16042,32 +16774,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
16042
16774
  }
16043
16775
  }
16044
16776
  async function normalizeProjectPath(input) {
16045
- const resolved = import_node_path70.default.resolve(input);
16777
+ const resolved = import_node_path72.default.resolve(input);
16046
16778
  try {
16047
- return await import_node_fs36.promises.realpath(resolved);
16779
+ return await import_node_fs37.promises.realpath(resolved);
16048
16780
  } catch {
16049
16781
  return resolved;
16050
16782
  }
16051
16783
  }
16052
16784
  async function writeAtomically(target, contents) {
16053
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(target), { recursive: true });
16785
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
16054
16786
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
16055
- const fd = await import_node_fs36.promises.open(tmp, "w");
16787
+ const fd = await import_node_fs37.promises.open(tmp, "w");
16056
16788
  try {
16057
16789
  await fd.writeFile(contents, "utf8");
16058
16790
  await fd.sync();
16059
16791
  } finally {
16060
16792
  await fd.close();
16061
16793
  }
16062
- await import_node_fs36.promises.rename(tmp, target);
16794
+ await import_node_fs37.promises.rename(tmp, target);
16063
16795
  }
16064
16796
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
16065
16797
  const deadline = Date.now() + timeoutMs;
16066
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(lockPath), { recursive: true });
16798
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
16067
16799
  let probedHolder = false;
16068
16800
  while (true) {
16069
16801
  try {
16070
- const fd = await import_node_fs36.promises.open(lockPath, "wx");
16802
+ const fd = await import_node_fs37.promises.open(lockPath, "wx");
16071
16803
  try {
16072
16804
  await fd.writeFile(`${process.pid}
16073
16805
  `, "utf8");
@@ -16092,7 +16824,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
16092
16824
  }
16093
16825
  }
16094
16826
  async function releaseLock(lockPath) {
16095
- await import_node_fs36.promises.unlink(lockPath).catch(() => {
16827
+ await import_node_fs37.promises.unlink(lockPath).catch(() => {
16096
16828
  });
16097
16829
  }
16098
16830
  async function withLock(fn) {
@@ -16108,7 +16840,7 @@ async function readRegistry() {
16108
16840
  const file = registryPath();
16109
16841
  let raw;
16110
16842
  try {
16111
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16843
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16112
16844
  } catch (err) {
16113
16845
  if (err.code === "ENOENT") {
16114
16846
  return { version: 1, projects: [] };
@@ -16116,10 +16848,10 @@ async function readRegistry() {
16116
16848
  throw err;
16117
16849
  }
16118
16850
  const parsed = JSON.parse(raw);
16119
- return import_types58.RegistryFileSchema.parse(parsed);
16851
+ return import_types59.RegistryFileSchema.parse(parsed);
16120
16852
  }
16121
16853
  async function writeRegistry(reg) {
16122
- const validated = import_types58.RegistryFileSchema.parse(reg);
16854
+ const validated = import_types59.RegistryFileSchema.parse(reg);
16123
16855
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
16124
16856
  }
16125
16857
  var ProjectNameCollisionError = class extends Error {
@@ -16213,7 +16945,7 @@ function pruneTtlMs() {
16213
16945
  }
16214
16946
  async function statPathStatus(p) {
16215
16947
  try {
16216
- const stat = await import_node_fs36.promises.stat(p);
16948
+ const stat = await import_node_fs37.promises.stat(p);
16217
16949
  return stat.isDirectory() ? "present" : "unknown";
16218
16950
  } catch (err) {
16219
16951
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -16314,8 +17046,8 @@ init_auth();
16314
17046
  // src/connectors-config.ts
16315
17047
  init_cjs_shims();
16316
17048
  var import_node_os4 = __toESM(require("os"), 1);
16317
- var import_node_path71 = __toESM(require("path"), 1);
16318
- var import_node_fs37 = require("fs");
17049
+ var import_node_path73 = __toESM(require("path"), 1);
17050
+ var import_node_fs38 = require("fs");
16319
17051
  var CONNECTORS_CONFIG_VERSION = 1;
16320
17052
  var EnvRefUnsetError = class extends Error {
16321
17053
  ref;
@@ -16329,17 +17061,17 @@ var EnvRefUnsetError = class extends Error {
16329
17061
  };
16330
17062
  function neatHome2() {
16331
17063
  const override = process.env.NEAT_HOME;
16332
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
16333
- return import_node_path71.default.join(import_node_os4.default.homedir(), ".neat");
17064
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
17065
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16334
17066
  }
16335
17067
  function connectorsConfigPath(home = neatHome2()) {
16336
- return import_node_path71.default.join(home, "connectors.json");
17068
+ return import_node_path73.default.join(home, "connectors.json");
16337
17069
  }
16338
17070
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16339
17071
  async function warnIfModeLooserThan0600(file) {
16340
17072
  if (process.platform === "win32") return;
16341
17073
  try {
16342
- const stat = await import_node_fs37.promises.stat(file);
17074
+ const stat = await import_node_fs38.promises.stat(file);
16343
17075
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16344
17076
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16345
17077
  console.warn(
@@ -16353,7 +17085,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16353
17085
  const file = connectorsConfigPath(home);
16354
17086
  let raw;
16355
17087
  try {
16356
- raw = await import_node_fs37.promises.readFile(file, "utf8");
17088
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16357
17089
  } catch (err) {
16358
17090
  if (err.code === "ENOENT") {
16359
17091
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16520,15 +17252,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16520
17252
 
16521
17253
  // src/connectors/index.ts
16522
17254
  init_cjs_shims();
16523
- var import_types59 = require("@neat.is/types");
17255
+ var import_types60 = require("@neat.is/types");
16524
17256
  var NO_ENV = "unknown";
16525
17257
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16526
17258
  if (!graph.hasNode(targetNodeId)) return void 0;
16527
17259
  const sites = [];
16528
17260
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16529
17261
  const edge = graph.getEdgeAttributes(edgeId);
16530
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16531
- const parsed = (0, import_types59.parseFileId)(edge.source);
17262
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
17263
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16532
17264
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16533
17265
  const site = { relPath: edge.evidence.file };
16534
17266
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16539,7 +17271,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16539
17271
  function routeCallSiteFor(graph, targetNodeId) {
16540
17272
  if (!graph.hasNode(targetNodeId)) return void 0;
16541
17273
  const attrs = graph.getNodeAttributes(targetNodeId);
16542
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
17274
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16543
17275
  const site = { relPath: attrs.path };
16544
17276
  if (attrs.line !== void 0) site.line = attrs.line;
16545
17277
  return site;
@@ -17037,10 +17769,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
17037
17769
  // src/connectors/supabase/map.ts
17038
17770
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
17039
17771
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
17040
- function targetFromRestPath(path74) {
17041
- const rpcMatch = REST_RPC_PATH_RE.exec(path74);
17772
+ function targetFromRestPath(path76) {
17773
+ const rpcMatch = REST_RPC_PATH_RE.exec(path76);
17042
17774
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
17043
- const tableMatch = REST_TABLE_PATH_RE.exec(path74);
17775
+ const tableMatch = REST_TABLE_PATH_RE.exec(path76);
17044
17776
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
17045
17777
  return null;
17046
17778
  }
@@ -17151,23 +17883,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
17151
17883
 
17152
17884
  // src/connectors/supabase/resolve.ts
17153
17885
  init_cjs_shims();
17154
- var import_types61 = require("@neat.is/types");
17886
+ var import_types62 = require("@neat.is/types");
17155
17887
  function createSupabaseResolveTarget(graph, config) {
17156
17888
  return (signal, _ctx) => {
17157
17889
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
17158
17890
  return null;
17159
17891
  }
17160
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17892
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17161
17893
  if (graph.hasNode(subResourceId)) {
17162
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17894
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17163
17895
  }
17164
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17896
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
17165
17897
  if (graph.hasNode(bareResourceId)) {
17166
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17898
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17167
17899
  }
17168
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17900
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
17169
17901
  if (graph.hasNode(projectLevelId)) {
17170
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17902
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17171
17903
  }
17172
17904
  return null;
17173
17905
  };
@@ -17260,7 +17992,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
17260
17992
 
17261
17993
  // src/connectors/railway/index.ts
17262
17994
  init_cjs_shims();
17263
- var import_types65 = require("@neat.is/types");
17995
+ var import_types66 = require("@neat.is/types");
17264
17996
 
17265
17997
  // src/connectors/railway/client.ts
17266
17998
  init_cjs_shims();
@@ -17411,7 +18143,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17411
18143
  const out = [];
17412
18144
  graph.forEachNode((_id, attrs) => {
17413
18145
  const node = attrs;
17414
- if (node.type !== import_types65.NodeType.RouteNode) return;
18146
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17415
18147
  const route = attrs;
17416
18148
  if (route.service !== serviceName) return;
17417
18149
  out.push({
@@ -17515,12 +18247,12 @@ function createRailwayResolveTarget(config) {
17515
18247
  const serviceName = config.serviceNameById[config.serviceId];
17516
18248
  if (!serviceName) return null;
17517
18249
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17518
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
18250
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17519
18251
  }
17520
18252
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17521
18253
  const peerName = config.serviceNameById[signal.targetName];
17522
18254
  if (!peerName) return null;
17523
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
18255
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17524
18256
  }
17525
18257
  return null;
17526
18258
  };
@@ -17644,9 +18376,9 @@ function parseFirebaseTargetName(targetName) {
17644
18376
  const secondSep = rest.indexOf(FIELD_SEP);
17645
18377
  if (secondSep === -1) return null;
17646
18378
  const method = rest.slice(0, secondSep);
17647
- const path74 = rest.slice(secondSep + 1);
17648
- if (!resourceName || !method || !path74) return null;
17649
- return { resourceName, method, path: path74 };
18379
+ const path76 = rest.slice(secondSep + 1);
18380
+ if (!resourceName || !method || !path76) return null;
18381
+ return { resourceName, method, path: path76 };
17650
18382
  }
17651
18383
  function resourceNameFor(type, labels) {
17652
18384
  if (!labels) return null;
@@ -17684,14 +18416,14 @@ function mapLogEntryToSignal(entry) {
17684
18416
  if (!req) return null;
17685
18417
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17686
18418
  const method = req.requestMethod.toUpperCase();
17687
- const path74 = pathFromRequestUrl(req.requestUrl);
17688
- if (path74 === null) return null;
18419
+ const path76 = pathFromRequestUrl(req.requestUrl);
18420
+ if (path76 === null) return null;
17689
18421
  const timestamp = entry.timestamp;
17690
18422
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17691
18423
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
17692
18424
  return {
17693
18425
  targetKind: resourceType,
17694
- targetName: packFirebaseTargetName({ resourceName, method, path: path74 }),
18426
+ targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
17695
18427
  callCount: 1,
17696
18428
  errorCount: isError ? 1 : 0,
17697
18429
  lastObservedIso: timestamp
@@ -17708,7 +18440,7 @@ function mapLogEntriesToSignals(entries) {
17708
18440
 
17709
18441
  // src/connectors/firebase/resolve.ts
17710
18442
  init_cjs_shims();
17711
- var import_types66 = require("@neat.is/types");
18443
+ var import_types67 = require("@neat.is/types");
17712
18444
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17713
18445
  switch (resourceType) {
17714
18446
  case "cloud_function":
@@ -17723,7 +18455,7 @@ function routeEntriesFor(graph, serviceName) {
17723
18455
  const entries = [];
17724
18456
  graph.forEachNode((_id, attrs) => {
17725
18457
  const node = attrs;
17726
- if (node.type !== import_types66.NodeType.RouteNode) return;
18458
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17727
18459
  const route = attrs;
17728
18460
  if (route.service !== serviceName) return;
17729
18461
  entries.push({
@@ -17755,7 +18487,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17755
18487
  return {
17756
18488
  targetNodeId: match.routeNodeId,
17757
18489
  serviceName,
17758
- edgeType: import_types66.EdgeType.CALLS
18490
+ edgeType: import_types67.EdgeType.CALLS
17759
18491
  };
17760
18492
  };
17761
18493
  }
@@ -17782,7 +18514,7 @@ init_cjs_shims();
17782
18514
 
17783
18515
  // src/connectors/cloudflare/connector.ts
17784
18516
  init_cjs_shims();
17785
- var import_types68 = require("@neat.is/types");
18517
+ var import_types69 = require("@neat.is/types");
17786
18518
 
17787
18519
  // src/connectors/cloudflare/client.ts
17788
18520
  init_cjs_shims();
@@ -17898,7 +18630,7 @@ function mapEventToSignal(event) {
17898
18630
  if (Number.isNaN(observedAt.getTime())) return null;
17899
18631
  const statusCode = metadata?.statusCode;
17900
18632
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17901
- const path74 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18633
+ const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17902
18634
  return {
17903
18635
  targetKind: CLOUDFLARE_TARGET_KIND,
17904
18636
  targetName: scriptName,
@@ -17906,7 +18638,7 @@ function mapEventToSignal(event) {
17906
18638
  errorCount: isError ? 1 : 0,
17907
18639
  lastObservedIso: observedAt.toISOString(),
17908
18640
  method,
17909
- ...path74 ? { path: path74 } : {},
18641
+ ...path76 ? { path: path76 } : {},
17910
18642
  ...typeof statusCode === "number" ? { statusCode } : {},
17911
18643
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17912
18644
  };
@@ -17946,19 +18678,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17946
18678
  graph.forEachNode((id, attrs) => {
17947
18679
  if (found) return;
17948
18680
  const a = attrs;
17949
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18681
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
17950
18682
  found = id;
17951
18683
  }
17952
18684
  });
17953
18685
  return found;
17954
18686
  }
17955
- function findMatchingRouteNode(graph, serviceName, method, path74) {
17956
- const normalizedPath = normalizePathTemplate(path74);
18687
+ function findMatchingRouteNode(graph, serviceName, method, path76) {
18688
+ const normalizedPath = normalizePathTemplate(path76);
17957
18689
  let found = null;
17958
18690
  graph.forEachNode((id, attrs) => {
17959
18691
  if (found) return;
17960
18692
  const a = attrs;
17961
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18693
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17962
18694
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17963
18695
  const routeMethod = (a.method ?? "").toUpperCase();
17964
18696
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17970,18 +18702,18 @@ function createCloudflareResolveTarget(config, graph) {
17970
18702
  return (signal) => {
17971
18703
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17972
18704
  const scriptName = signal.targetName;
17973
- const { method, path: path74 } = signal;
18705
+ const { method, path: path76 } = signal;
17974
18706
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17975
- if (!method || !path74) return wholeFileId;
17976
- return findMatchingRouteNode(graph, serviceName, method, path74) ?? wholeFileId;
18707
+ if (!method || !path76) return wholeFileId;
18708
+ return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
17977
18709
  };
17978
18710
  const mapping = config.workers?.[scriptName];
17979
18711
  if (mapping) {
17980
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18712
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17981
18713
  return {
17982
18714
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17983
18715
  serviceName: mapping.service,
17984
- edgeType: import_types68.EdgeType.CALLS
18716
+ edgeType: import_types69.EdgeType.CALLS
17985
18717
  };
17986
18718
  }
17987
18719
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17990,13 +18722,13 @@ function createCloudflareResolveTarget(config, graph) {
17990
18722
  return {
17991
18723
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17992
18724
  serviceName: fileNode.service,
17993
- edgeType: import_types68.EdgeType.CALLS
18725
+ edgeType: import_types69.EdgeType.CALLS
17994
18726
  };
17995
18727
  }
17996
18728
  return {
17997
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18729
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17998
18730
  serviceName: scriptName,
17999
- edgeType: import_types68.EdgeType.CALLS,
18731
+ edgeType: import_types69.EdgeType.CALLS,
18000
18732
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
18001
18733
  };
18002
18734
  };
@@ -18192,14 +18924,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
18192
18924
 
18193
18925
  // src/connectors/neon/resolve.ts
18194
18926
  init_cjs_shims();
18195
- var import_types72 = require("@neat.is/types");
18927
+ var import_types73 = require("@neat.is/types");
18196
18928
  function createNeonResolveTarget(config) {
18197
18929
  return (signal) => {
18198
18930
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18199
18931
  return {
18200
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18932
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
18201
18933
  serviceName: config.serviceName,
18202
- edgeType: import_types72.EdgeType.CALLS,
18934
+ edgeType: import_types73.EdgeType.CALLS,
18203
18935
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
18204
18936
  };
18205
18937
  };
@@ -18325,9 +19057,9 @@ function parseCloudRunTargetName(targetName) {
18325
19057
  const secondSep = rest.indexOf(FIELD_SEP2);
18326
19058
  if (secondSep === -1) return null;
18327
19059
  const method = rest.slice(0, secondSep);
18328
- const path74 = rest.slice(secondSep + 1);
18329
- if (!serviceName || !method || !path74) return null;
18330
- return { serviceName, method, path: path74 };
19060
+ const path76 = rest.slice(secondSep + 1);
19061
+ if (!serviceName || !method || !path76) return null;
19062
+ return { serviceName, method, path: path76 };
18331
19063
  }
18332
19064
 
18333
19065
  // src/connectors/cloud-run/map.ts
@@ -18356,14 +19088,14 @@ function mapLogEntryToSignal2(entry) {
18356
19088
  if (!req) return null;
18357
19089
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
18358
19090
  const method = req.requestMethod.toUpperCase();
18359
- const path74 = pathFromRequestUrl2(req.requestUrl);
18360
- if (path74 === null) return null;
19091
+ const path76 = pathFromRequestUrl2(req.requestUrl);
19092
+ if (path76 === null) return null;
18361
19093
  const timestamp = entry.timestamp;
18362
19094
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
18363
19095
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
18364
19096
  return {
18365
19097
  targetKind: CLOUD_RUN_TARGET_KIND,
18366
- targetName: packCloudRunTargetName({ serviceName, method, path: path74 }),
19098
+ targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
18367
19099
  callCount: 1,
18368
19100
  errorCount: isError ? 1 : 0,
18369
19101
  lastObservedIso: timestamp
@@ -18380,14 +19112,14 @@ function mapLogEntriesToSignals2(entries) {
18380
19112
 
18381
19113
  // src/connectors/cloud-run/resolve.ts
18382
19114
  init_cjs_shims();
18383
- var import_types76 = require("@neat.is/types");
19115
+ var import_types77 = require("@neat.is/types");
18384
19116
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18385
19117
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18386
19118
  let found = null;
18387
19119
  graph.forEachNode((_id, attrs) => {
18388
19120
  if (found) return;
18389
19121
  const node = attrs;
18390
- if (node.type !== import_types76.NodeType.RouteNode) return;
19122
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18391
19123
  const route = attrs;
18392
19124
  if (route.service !== serviceName || !route.pathTemplate) return;
18393
19125
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18402,23 +19134,23 @@ function createCloudRunResolveTarget(graph, config) {
18402
19134
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18403
19135
  const identity = parseCloudRunTargetName(signal.targetName);
18404
19136
  if (!identity) return null;
18405
- const { serviceName: gcpServiceName, method, path: path74 } = identity;
19137
+ const { serviceName: gcpServiceName, method, path: path76 } = identity;
18406
19138
  const mappedService = config.serviceMap?.[gcpServiceName];
18407
19139
  if (mappedService) {
18408
19140
  const routeNodeId = findMatchingRouteNode2(
18409
19141
  graph,
18410
19142
  mappedService,
18411
19143
  method,
18412
- normalizePathTemplate(path74)
19144
+ normalizePathTemplate(path76)
18413
19145
  );
18414
19146
  if (routeNodeId) {
18415
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
19147
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18416
19148
  }
18417
19149
  }
18418
19150
  return {
18419
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
19151
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18420
19152
  serviceName: mappedService ?? gcpServiceName,
18421
- edgeType: import_types76.EdgeType.CALLS,
19153
+ edgeType: import_types77.EdgeType.CALLS,
18422
19154
  ensureInfraNode: {
18423
19155
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18424
19156
  name: gcpServiceName,
@@ -18459,7 +19191,7 @@ function createCloudRunConnector(graph, config = {}) {
18459
19191
 
18460
19192
  // src/connectors/render/index.ts
18461
19193
  init_cjs_shims();
18462
- var import_types79 = require("@neat.is/types");
19194
+ var import_types80 = require("@neat.is/types");
18463
19195
 
18464
19196
  // src/connectors/render/types.ts
18465
19197
  init_cjs_shims();
@@ -18537,7 +19269,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18537
19269
  const out = [];
18538
19270
  graph.forEachNode((_id, attrs) => {
18539
19271
  const node = attrs;
18540
- if (node.type !== import_types79.NodeType.RouteNode) return;
19272
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18541
19273
  const route = attrs;
18542
19274
  if (route.service !== serviceName) return;
18543
19275
  out.push({
@@ -18622,7 +19354,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18622
19354
  function createRenderResolveTarget(config) {
18623
19355
  return (signal) => {
18624
19356
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18625
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
19357
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18626
19358
  }
18627
19359
  return null;
18628
19360
  };
@@ -18760,21 +19492,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18760
19492
 
18761
19493
  // src/connectors/planetscale/resolve.ts
18762
19494
  init_cjs_shims();
18763
- var import_types83 = require("@neat.is/types");
19495
+ var import_types84 = require("@neat.is/types");
18764
19496
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18765
19497
  function createPlanetscaleResolveTarget(graph, config) {
18766
19498
  const databaseName = `${config.organization}/${config.database}`;
18767
19499
  return (signal, _ctx) => {
18768
19500
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18769
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19501
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18770
19502
  if (graph.hasNode(tableId)) {
18771
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19503
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18772
19504
  }
18773
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19505
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18774
19506
  return {
18775
19507
  targetNodeId: providerId,
18776
19508
  serviceName: config.serviceName,
18777
- edgeType: import_types83.EdgeType.CALLS,
19509
+ edgeType: import_types84.EdgeType.CALLS,
18778
19510
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18779
19511
  };
18780
19512
  };
@@ -19039,7 +19771,7 @@ function mapBuildsToSignals(builds, serviceName) {
19039
19771
 
19040
19772
  // src/connectors/eas/resolve.ts
19041
19773
  init_cjs_shims();
19042
- var import_types88 = require("@neat.is/types");
19774
+ var import_types89 = require("@neat.is/types");
19043
19775
  var NO_ENV2 = "unknown";
19044
19776
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
19045
19777
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -19055,8 +19787,8 @@ function configBasenamesForPhase(phase) {
19055
19787
  function configNodeService(graph, configNodeId) {
19056
19788
  for (const edgeId of graph.inboundEdges(configNodeId)) {
19057
19789
  const edge = graph.getEdgeAttributes(edgeId);
19058
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
19059
- const parsed = (0, import_types88.parseFileId)(edge.source);
19790
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19791
+ const parsed = (0, import_types89.parseFileId)(edge.source);
19060
19792
  if (parsed) return parsed.service;
19061
19793
  }
19062
19794
  return null;
@@ -19067,7 +19799,7 @@ function findConfigNode(graph, basenames, serviceName) {
19067
19799
  graph.forEachNode((id, attrs) => {
19068
19800
  if (scoped) return;
19069
19801
  const node = attrs;
19070
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19802
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
19071
19803
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
19072
19804
  if (anyMatch === null) anyMatch = id;
19073
19805
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -19084,13 +19816,13 @@ function createEasResolveTarget(graph) {
19084
19816
  if (basenames.length > 0) {
19085
19817
  const configNodeId = findConfigNode(graph, basenames, serviceName);
19086
19818
  if (configNodeId) {
19087
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19819
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
19088
19820
  }
19089
19821
  }
19090
19822
  return {
19091
19823
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
19092
19824
  serviceName,
19093
- edgeType: import_types88.EdgeType.CALLS
19825
+ edgeType: import_types89.EdgeType.CALLS
19094
19826
  };
19095
19827
  };
19096
19828
  }
@@ -19815,11 +20547,11 @@ function registerRoutes(scope, ctx) {
19815
20547
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19816
20548
  const parsed = [];
19817
20549
  for (const c of candidates) {
19818
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20550
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19819
20551
  if (!r.success) {
19820
20552
  return reply.code(400).send({
19821
20553
  error: `unknown divergence type "${c}"`,
19822
- allowed: import_types91.DivergenceTypeSchema.options
20554
+ allowed: import_types92.DivergenceTypeSchema.options
19823
20555
  });
19824
20556
  }
19825
20557
  parsed.push(r.data);
@@ -20161,7 +20893,7 @@ function registerRoutes(scope, ctx) {
20161
20893
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
20162
20894
  let violations = await log.readAll();
20163
20895
  if (req.query.severity) {
20164
- const sev = import_types91.PolicySeveritySchema.safeParse(req.query.severity);
20896
+ const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
20165
20897
  if (!sev.success) {
20166
20898
  return reply.code(400).send({
20167
20899
  error: "invalid severity",
@@ -20200,7 +20932,7 @@ function registerRoutes(scope, ctx) {
20200
20932
  scope.post("/policies/check", async (req, reply) => {
20201
20933
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
20202
20934
  if (!proj) return;
20203
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20935
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20204
20936
  if (!parsed.success) {
20205
20937
  return reply.code(400).send({
20206
20938
  error: "invalid /policies/check body",
@@ -20521,16 +21253,16 @@ init_otel_grpc();
20521
21253
 
20522
21254
  // src/daemon.ts
20523
21255
  init_cjs_shims();
20524
- var import_node_fs39 = require("fs");
20525
- var import_node_path73 = __toESM(require("path"), 1);
21256
+ var import_node_fs40 = require("fs");
21257
+ var import_node_path75 = __toESM(require("path"), 1);
20526
21258
  var import_node_module = require("module");
20527
21259
  init_otel();
20528
21260
  init_auth();
20529
21261
 
20530
21262
  // src/unrouted.ts
20531
21263
  init_cjs_shims();
20532
- var import_node_fs38 = require("fs");
20533
- var import_node_path72 = __toESM(require("path"), 1);
21264
+ var import_node_fs39 = require("fs");
21265
+ var import_node_path74 = __toESM(require("path"), 1);
20534
21266
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
20535
21267
  return {
20536
21268
  timestamp: now.toISOString(),
@@ -20540,34 +21272,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
20540
21272
  };
20541
21273
  }
20542
21274
  async function appendUnroutedSpan(neatHome3, record) {
20543
- const target = import_node_path72.default.join(neatHome3, "errors.ndjson");
20544
- await import_node_fs38.promises.mkdir(neatHome3, { recursive: true });
20545
- await import_node_fs38.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
21275
+ const target = import_node_path74.default.join(neatHome3, "errors.ndjson");
21276
+ await import_node_fs39.promises.mkdir(neatHome3, { recursive: true });
21277
+ await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
20546
21278
  }
20547
21279
  function unroutedErrorsPath(neatHome3) {
20548
- return import_node_path72.default.join(neatHome3, "errors.ndjson");
21280
+ return import_node_path74.default.join(neatHome3, "errors.ndjson");
20549
21281
  }
20550
21282
 
20551
21283
  // src/daemon.ts
20552
- var import_types92 = require("@neat.is/types");
21284
+ var import_types93 = require("@neat.is/types");
20553
21285
  function daemonJsonPath(scanPath) {
20554
- return import_node_path73.default.join(scanPath, "neat-out", "daemon.json");
21286
+ return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
20555
21287
  }
20556
21288
  function daemonsDiscoveryDir(home) {
20557
21289
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
20558
- return import_node_path73.default.join(base, "daemons");
21290
+ return import_node_path75.default.join(base, "daemons");
20559
21291
  }
20560
21292
  function daemonDiscoveryPath(project, home) {
20561
- return import_node_path73.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
21293
+ return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
20562
21294
  }
20563
21295
  function sanitizeDiscoveryName(project) {
20564
21296
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
20565
21297
  }
20566
21298
  function neatHomeFromEnv() {
20567
21299
  const env = process.env.NEAT_HOME;
20568
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21300
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20569
21301
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20570
- return import_node_path73.default.join(home, ".neat");
21302
+ return import_node_path75.default.join(home, ".neat");
20571
21303
  }
20572
21304
  function resolveNeatVersion() {
20573
21305
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -20599,7 +21331,7 @@ async function clearDaemonRecord(record, home) {
20599
21331
  } catch {
20600
21332
  }
20601
21333
  try {
20602
- await import_node_fs39.promises.unlink(daemonDiscoveryPath(record.project, home));
21334
+ await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
20603
21335
  } catch {
20604
21336
  }
20605
21337
  }
@@ -20622,11 +21354,11 @@ function teardownSlot(slot) {
20622
21354
  }
20623
21355
  }
20624
21356
  function neatHomeFor(opts) {
20625
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path73.default.resolve(opts.neatHome);
21357
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
20626
21358
  const env = process.env.NEAT_HOME;
20627
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21359
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20628
21360
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20629
- return import_node_path73.default.join(home, ".neat");
21361
+ return import_node_path75.default.join(home, ".neat");
20630
21362
  }
20631
21363
  function routeSpanToProject(serviceName, projects) {
20632
21364
  if (!serviceName) return DEFAULT_PROJECT;
@@ -20674,13 +21406,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
20674
21406
  if (!serviceName) return true;
20675
21407
  if (serviceNameMatchesProject(serviceName, project)) return true;
20676
21408
  return graph.someNode(
20677
- (_id, attrs) => attrs.type === import_types92.NodeType.ServiceNode && attrs.name === serviceName
21409
+ (_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
20678
21410
  );
20679
21411
  }
20680
21412
  async function bootstrapProject(entry, connectors = [], neatHome3) {
20681
- const paths = pathsForProject(entry.name, import_node_path73.default.join(entry.path, "neat-out"));
21413
+ const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
20682
21414
  try {
20683
- const stat = await import_node_fs39.promises.stat(entry.path);
21415
+ const stat = await import_node_fs40.promises.stat(entry.path);
20684
21416
  if (!stat.isDirectory()) {
20685
21417
  throw new Error(`registered path ${entry.path} is not a directory`);
20686
21418
  }
@@ -20798,7 +21530,7 @@ async function startDaemon(opts = {}) {
20798
21530
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
20799
21531
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
20800
21532
  const singleProject = projectArg;
20801
- const singleProjectPath = singleProject && projectPathArg ? import_node_path73.default.resolve(projectPathArg) : null;
21533
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
20802
21534
  if (singleProject && !singleProjectPath) {
20803
21535
  throw new Error(
20804
21536
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -20806,14 +21538,14 @@ async function startDaemon(opts = {}) {
20806
21538
  }
20807
21539
  if (!singleProject) {
20808
21540
  try {
20809
- await import_node_fs39.promises.access(regPath);
21541
+ await import_node_fs40.promises.access(regPath);
20810
21542
  } catch {
20811
21543
  throw new Error(
20812
21544
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
20813
21545
  );
20814
21546
  }
20815
21547
  }
20816
- const pidPath = import_node_path73.default.join(home, "neatd.pid");
21548
+ const pidPath = import_node_path75.default.join(home, "neatd.pid");
20817
21549
  await writeAtomically(pidPath, `${process.pid}
20818
21550
  `);
20819
21551
  const slots = /* @__PURE__ */ new Map();
@@ -21025,7 +21757,7 @@ async function startDaemon(opts = {}) {
21025
21757
  }
21026
21758
  if (restApp) await restApp.close().catch(() => {
21027
21759
  });
21028
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21760
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21029
21761
  });
21030
21762
  throw new Error(
21031
21763
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -21193,7 +21925,7 @@ async function startDaemon(opts = {}) {
21193
21925
  });
21194
21926
  if (otlpApp) await otlpApp.close().catch(() => {
21195
21927
  });
21196
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21928
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21197
21929
  });
21198
21930
  throw new Error(
21199
21931
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -21228,7 +21960,7 @@ async function startDaemon(opts = {}) {
21228
21960
  });
21229
21961
  if (otlpApp) await otlpApp.close().catch(() => {
21230
21962
  });
21231
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21963
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21232
21964
  });
21233
21965
  throw new Error(
21234
21966
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -21275,9 +22007,9 @@ async function startDaemon(opts = {}) {
21275
22007
  let registryWatcher = null;
21276
22008
  let reloadTimer = null;
21277
22009
  if (!singleProject) try {
21278
- const regDir = import_node_path73.default.dirname(regPath);
21279
- const regBase = import_node_path73.default.basename(regPath);
21280
- registryWatcher = (0, import_node_fs39.watch)(regDir, (_eventType, filename) => {
22010
+ const regDir = import_node_path75.default.dirname(regPath);
22011
+ const regBase = import_node_path75.default.basename(regPath);
22012
+ registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
21281
22013
  if (filename !== null && filename !== regBase) return;
21282
22014
  if (reloadTimer) clearTimeout(reloadTimer);
21283
22015
  reloadTimer = setTimeout(() => {
@@ -21326,7 +22058,7 @@ async function startDaemon(opts = {}) {
21326
22058
  if (daemonRecord) {
21327
22059
  await clearDaemonRecord(daemonRecord, home);
21328
22060
  }
21329
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
22061
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21330
22062
  });
21331
22063
  };
21332
22064
  return {