@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/neatd.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req2, reply, done) => {
64
- const path76 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
64
+ const path78 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path78) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path78)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path51.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
- return import_node_path51.default.resolve(here, "..", "proto");
197
+ const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
+ return import_node_path52.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url, import_node_path51, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url = require("url");
272
- import_node_path51 = __toESM(require("path"), 1);
272
+ import_node_path52 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
415
415
  const v = attrs[key];
416
416
  if (typeof v === "string" && v.length > 0) {
417
417
  const q = v.indexOf("?");
418
- const path76 = q === -1 ? v : v.slice(0, q);
419
- if (path76.length > 0) return path76;
418
+ const path78 = q === -1 ? v : v.slice(0, q);
419
+ if (path78.length > 0) return path78;
420
420
  }
421
421
  }
422
422
  return void 0;
@@ -478,10 +478,10 @@ function parseOtlpRequest(body) {
478
478
  return out;
479
479
  }
480
480
  function loadProtoRoot() {
481
- const here = import_node_path52.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
- const protoRoot = import_node_path52.default.resolve(here, "..", "proto");
481
+ const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
+ const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
483
483
  const root = new import_protobufjs.default.Root();
484
- root.resolvePath = (_origin, target) => import_node_path52.default.resolve(protoRoot, target);
484
+ root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
485
485
  root.loadSync(
486
486
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
487
487
  { keepCase: true }
@@ -728,12 +728,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
728
728
  }
729
729
  }
730
730
  }
731
- 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;
731
+ 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;
732
732
  var init_otel = __esm({
733
733
  "src/otel.ts"() {
734
734
  "use strict";
735
735
  init_cjs_shims();
736
- import_node_path52 = __toESM(require("path"), 1);
736
+ import_node_path53 = __toESM(require("path"), 1);
737
737
  import_node_url2 = require("url");
738
738
  import_fastify = __toESM(require("fastify"), 1);
739
739
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -756,14 +756,14 @@ __export(neatd_exports, {
756
756
  });
757
757
  module.exports = __toCommonJS(neatd_exports);
758
758
  init_cjs_shims();
759
- var import_node_fs41 = require("fs");
760
- var import_node_path75 = __toESM(require("path"), 1);
759
+ var import_node_fs42 = require("fs");
760
+ var import_node_path77 = __toESM(require("path"), 1);
761
761
  var import_node_module2 = require("module");
762
762
 
763
763
  // src/daemon.ts
764
764
  init_cjs_shims();
765
- var import_node_fs39 = require("fs");
766
- var import_node_path73 = __toESM(require("path"), 1);
765
+ var import_node_fs40 = require("fs");
766
+ var import_node_path75 = __toESM(require("path"), 1);
767
767
  var import_node_module = require("module");
768
768
 
769
769
  // src/graph.ts
@@ -1296,19 +1296,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1296
1296
  function longestIncomingWalk(graph, start, maxDepth) {
1297
1297
  let best = { path: [start], edges: [] };
1298
1298
  const visited = /* @__PURE__ */ new Set([start]);
1299
- function step(node, path76, edges) {
1300
- if (path76.length > best.path.length) {
1301
- best = { path: [...path76], edges: [...edges] };
1299
+ function step(node, path78, edges) {
1300
+ if (path78.length > best.path.length) {
1301
+ best = { path: [...path78], edges: [...edges] };
1302
1302
  }
1303
- if (path76.length - 1 >= maxDepth) return;
1303
+ if (path78.length - 1 >= maxDepth) return;
1304
1304
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1305
1305
  for (const [srcId, edge] of incoming) {
1306
1306
  if (visited.has(srcId)) continue;
1307
1307
  visited.add(srcId);
1308
- path76.push(srcId);
1308
+ path78.push(srcId);
1309
1309
  edges.push(edge);
1310
- step(srcId, path76, edges);
1311
- path76.pop();
1310
+ step(srcId, path78, edges);
1311
+ path78.pop();
1312
1312
  edges.pop();
1313
1313
  visited.delete(srcId);
1314
1314
  }
@@ -1316,11 +1316,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1316
1316
  step(start, [start], []);
1317
1317
  return best;
1318
1318
  }
1319
- function databaseRootCauseShape(graph, origin, walk9) {
1319
+ function databaseRootCauseShape(graph, origin, walk10) {
1320
1320
  const targetDb = origin;
1321
1321
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1322
1322
  if (candidatePairs.length === 0) return null;
1323
- for (const id of walk9.path) {
1323
+ for (const id of walk10.path) {
1324
1324
  const owner = resolveOwningService(graph, id);
1325
1325
  if (!owner) continue;
1326
1326
  const { id: serviceId15, svc } = owner;
@@ -1347,8 +1347,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
1347
1347
  }
1348
1348
  return null;
1349
1349
  }
1350
- function serviceRootCauseShape(graph, _origin, walk9) {
1351
- for (const id of walk9.path) {
1350
+ function serviceRootCauseShape(graph, _origin, walk10) {
1351
+ for (const id of walk10.path) {
1352
1352
  const owner = resolveOwningService(graph, id);
1353
1353
  if (!owner) continue;
1354
1354
  const { id: serviceId15, svc } = owner;
@@ -1384,15 +1384,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
1384
1384
  }
1385
1385
  return null;
1386
1386
  }
1387
- function fileRootCauseShape(graph, origin, walk9) {
1387
+ function fileRootCauseShape(graph, origin, walk10) {
1388
1388
  const owner = resolveOwningService(graph, origin.id);
1389
1389
  if (!owner) return null;
1390
- return serviceRootCauseShape(graph, owner.svc, walk9);
1390
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1391
1391
  }
1392
- function symbolRootCauseShape(graph, origin, walk9) {
1392
+ function symbolRootCauseShape(graph, origin, walk10) {
1393
1393
  const owner = resolveOwningService(graph, origin.id);
1394
1394
  if (!owner) return null;
1395
- return serviceRootCauseShape(graph, owner.svc, walk9);
1395
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1396
1396
  }
1397
1397
  var rootCauseShapes = {
1398
1398
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1405,25 +1405,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1405
1405
  const origin = graph.getNodeAttributes(errorNodeId);
1406
1406
  const shape = rootCauseShapes[origin.type];
1407
1407
  if (shape) {
1408
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1409
- const match = shape(graph, origin, walk9);
1408
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1409
+ const match = shape(graph, origin, walk10);
1410
1410
  if (match) {
1411
1411
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1412
- return import_types.RootCauseResultSchema.parse({
1413
- rootCauseNode: match.rootCauseNode,
1414
- rootCauseReason: reason,
1415
- traversalPath: walk9.path,
1416
- edgeProvenances: walk9.edges.map((e) => e.provenance),
1417
- confidence: confidenceFromMix(walk9.edges),
1418
- fixRecommendation: match.fixRecommendation
1419
- });
1412
+ return {
1413
+ source: "compat",
1414
+ result: import_types.RootCauseResultSchema.parse({
1415
+ rootCauseNode: match.rootCauseNode,
1416
+ rootCauseReason: reason,
1417
+ traversalPath: walk10.path,
1418
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
1419
+ confidence: confidenceFromMix(walk10.edges),
1420
+ fixRecommendation: match.fixRecommendation
1421
+ })
1422
+ };
1420
1423
  }
1421
1424
  }
1422
1425
  if (origin.type === import_types.NodeType.ServiceNode) {
1423
1426
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
1424
- if (crossService) return crossService;
1427
+ if (crossService) return { result: crossService, source: "cross-service" };
1425
1428
  }
1426
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1429
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1430
+ return incident ? { result: incident, source: "incident" } : null;
1427
1431
  }
1428
1432
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
1429
1433
  function incidentMatchesNode(ev, nodeId) {
@@ -1519,26 +1523,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
1519
1523
  return best;
1520
1524
  }
1521
1525
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1522
- const path76 = [originServiceId];
1526
+ const path78 = [originServiceId];
1523
1527
  const edges = [];
1524
1528
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1525
1529
  let current = originServiceId;
1526
1530
  for (let depth = 0; depth < maxDepth; depth++) {
1527
1531
  const hop = dominantFailingCall(graph, current, visited);
1528
1532
  if (!hop) break;
1529
- path76.push(hop.nextService);
1533
+ path78.push(hop.nextService);
1530
1534
  edges.push(hop.edge);
1531
1535
  visited.add(hop.nextService);
1532
1536
  current = hop.nextService;
1533
1537
  }
1534
1538
  if (edges.length === 0) return null;
1535
- return { path: path76, edges, culprit: current };
1539
+ return { path: path78, edges, culprit: current };
1540
+ }
1541
+ function isStaleCallEdge(e) {
1542
+ return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
1543
+ }
1544
+ function staleCallDominates(e, id, curEdge, curId) {
1545
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
1546
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
1547
+ if (ev !== cv) return ev > cv;
1548
+ return id < curId;
1549
+ }
1550
+ function dominantStaleCall(graph, serviceId15, visited) {
1551
+ const bestByCallee = /* @__PURE__ */ new Map();
1552
+ for (const src of callSourcesForService(graph, serviceId15)) {
1553
+ for (const edgeId of graph.outboundEdges(src)) {
1554
+ const e = graph.getEdgeAttributes(edgeId);
1555
+ if (e.type !== import_types.EdgeType.CALLS) continue;
1556
+ if (isFrontierNode(graph, e.target)) continue;
1557
+ const owner = resolveOwningService(graph, e.target);
1558
+ if (!owner || visited.has(owner.id)) continue;
1559
+ const cur = bestByCallee.get(owner.id);
1560
+ if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
1561
+ bestByCallee.set(owner.id, e);
1562
+ }
1563
+ }
1564
+ }
1565
+ let best = null;
1566
+ for (const [id, edge] of bestByCallee) {
1567
+ if (!isStaleCallEdge(edge)) continue;
1568
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
1569
+ best = { nextService: id, edge };
1570
+ }
1571
+ }
1572
+ return best;
1573
+ }
1574
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
1575
+ const path78 = [originServiceId];
1576
+ const edges = [];
1577
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
1578
+ let current = originServiceId;
1579
+ for (let depth = 0; depth < maxDepth; depth++) {
1580
+ const hop = dominantStaleCall(graph, current, visited);
1581
+ if (!hop) break;
1582
+ path78.push(hop.nextService);
1583
+ edges.push(hop.edge);
1584
+ visited.add(hop.nextService);
1585
+ current = hop.nextService;
1586
+ }
1587
+ if (edges.length === 0) return null;
1588
+ return { path: path78, edges, culprit: current };
1536
1589
  }
1537
1590
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1538
1591
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1539
1592
  if (!chain) return null;
1540
1593
  const culprit = chain.culprit;
1541
- const path76 = [...chain.path];
1594
+ const path78 = [...chain.path];
1542
1595
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1543
1596
  const baseConfidence = confidenceFromMix(chain.edges);
1544
1597
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1546,14 +1599,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1546
1599
  if (loc) {
1547
1600
  let rootCauseNode = culprit;
1548
1601
  if (loc.fileNode) {
1549
- path76.push(loc.fileNode);
1602
+ path78.push(loc.fileNode);
1550
1603
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1551
1604
  rootCauseNode = loc.fileNode;
1552
1605
  }
1553
1606
  return import_types.RootCauseResultSchema.parse({
1554
1607
  rootCauseNode,
1555
1608
  rootCauseReason: loc.rootCauseReason,
1556
- traversalPath: path76,
1609
+ traversalPath: path78,
1557
1610
  edgeProvenances,
1558
1611
  confidence,
1559
1612
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1565,7 +1618,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1565
1618
  return import_types.RootCauseResultSchema.parse({
1566
1619
  rootCauseNode: culprit,
1567
1620
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1568
- traversalPath: path76,
1621
+ traversalPath: path78,
1569
1622
  edgeProvenances,
1570
1623
  confidence,
1571
1624
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -1952,17 +2005,20 @@ function displayNameOf(nodeId) {
1952
2005
  return nodeId.replace(/^[a-z]+:/, "");
1953
2006
  }
1954
2007
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
1955
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
1956
- if (!legacy) return null;
2008
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
2009
+ if (!tagged) return null;
1957
2010
  const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
1958
- if (!navigation) return legacy;
1959
- return enrichWithNavigation(graph, errorNodeId, legacy, incidents, opts?.now ?? Date.now());
2011
+ if (!navigation) return tagged.result;
2012
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
1960
2013
  }
1961
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2014
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2015
+ const legacy = tagged.result;
1962
2016
  const seedNode = legacy.rootCauseNode;
1963
2017
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
1964
2018
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
1965
2019
  const candidates = [];
2020
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
2021
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
1966
2022
  if (seedCtx && isVictimSeed(seedCtx)) {
1967
2023
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
1968
2024
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -1987,6 +2043,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
1987
2043
  confidence: Math.min(legacy.confidence, 0.4),
1988
2044
  ...lastProv ? { provenance: lastProv } : {}
1989
2045
  });
2046
+ } else if (staleChain) {
2047
+ const culprit = staleChain.culprit;
2048
+ const culpritName = displayNameOf(culprit);
2049
+ const seedName = displayNameOf(seedNode);
2050
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
2051
+ candidates.push({
2052
+ node: culprit,
2053
+ classification: "primary-failure",
2054
+ 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.`,
2055
+ context: nodeContext(graph, culprit, incidents, now),
2056
+ confidence: staleConfidence,
2057
+ provenance: import_types.Provenance.STALE
2058
+ });
2059
+ candidates.push({
2060
+ node: seedNode,
2061
+ classification: "symptom-only",
2062
+ 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.`,
2063
+ context: seedCtx ?? EMPTY_CONTEXT,
2064
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
2065
+ ...lastProv ? { provenance: lastProv } : {}
2066
+ });
1990
2067
  } else {
1991
2068
  candidates.push({
1992
2069
  node: seedNode,
@@ -2000,11 +2077,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2000
2077
  const top = candidates[0];
2001
2078
  let traversalPath = legacy.traversalPath;
2002
2079
  let edgeProvenances = legacy.edgeProvenances;
2003
- if (top.node !== seedNode) {
2004
- const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2005
- if (path76) {
2006
- traversalPath = path76.nodes;
2007
- edgeProvenances = path76.edges.map((e) => e.provenance);
2080
+ if (staleChain && top.node === staleChain.culprit) {
2081
+ traversalPath = staleChain.path;
2082
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
2083
+ } else if (top.node !== seedNode) {
2084
+ const path78 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2085
+ if (path78) {
2086
+ traversalPath = path78.nodes;
2087
+ edgeProvenances = path78.edges.map((e) => e.provenance);
2008
2088
  } else {
2009
2089
  traversalPath = [errorNodeId, top.node];
2010
2090
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2026,6 +2106,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
2026
2106
  return legacy.fixRecommendation;
2027
2107
  }
2028
2108
  const name = top.node.replace(/^service:/, "");
2109
+ if (top.provenance === import_types.Provenance.STALE) {
2110
+ 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}.`;
2111
+ }
2029
2112
  if (top.classification === "primary-failure") {
2030
2113
  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.`;
2031
2114
  }
@@ -2544,6 +2627,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2544
2627
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2545
2628
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2546
2629
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2630
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2547
2631
  var import_types6 = require("@neat.is/types");
2548
2632
 
2549
2633
  // src/extract/shared.ts
@@ -2824,7 +2908,7 @@ function buildServiceHostIndex(services) {
2824
2908
  async function walkSourceFiles(dir, excludeDirs = []) {
2825
2909
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
2826
2910
  const out = [];
2827
- async function walk9(current) {
2911
+ async function walk10(current) {
2828
2912
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2829
2913
  for (const entry2 of entries) {
2830
2914
  const full = import_node_path5.default.join(current, entry2.name);
@@ -2832,7 +2916,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2832
2916
  if (IGNORED_DIRS.has(entry2.name)) continue;
2833
2917
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
2834
2918
  if (await isPythonVenvDir(full)) continue;
2835
- await walk9(full);
2919
+ await walk10(full);
2836
2920
  } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2837
2921
  // would attribute our instrumentation imports to the user's service.
2838
2922
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -2840,7 +2924,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2840
2924
  }
2841
2925
  }
2842
2926
  }
2843
- await walk9(dir);
2927
+ await walk10(dir);
2844
2928
  return out;
2845
2929
  }
2846
2930
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3360,6 +3444,11 @@ function makePhpParser() {
3360
3444
  p.setLanguage(import_tree_sitter_php.default.php_only);
3361
3445
  return p;
3362
3446
  }
3447
+ function makeRustParser() {
3448
+ const p = new import_tree_sitter2.default();
3449
+ p.setLanguage(import_tree_sitter_rust.default);
3450
+ return p;
3451
+ }
3363
3452
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
3364
3453
  "get",
3365
3454
  "post",
@@ -3431,8 +3520,8 @@ function chiRoutesFromSource(source, parser) {
3431
3520
  chiWalk(tree.rootNode, "", out);
3432
3521
  return out;
3433
3522
  }
3434
- function stripChiRegex(path76) {
3435
- return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3523
+ function stripChiRegex(path78) {
3524
+ return path78.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3436
3525
  }
3437
3526
  function chiWalk(node, prefix, out) {
3438
3527
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4104,9 +4193,9 @@ function rubyRocketRoute(args) {
4104
4193
  if (!pair || pair.type !== "pair") continue;
4105
4194
  const k = pair.childForFieldName("key");
4106
4195
  if (k?.type !== "string") continue;
4107
- const path76 = rubyLiteral(k);
4108
- if (path76 === null) continue;
4109
- return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
4196
+ const path78 = rubyLiteral(k);
4197
+ if (path78 === null) continue;
4198
+ return { path: path78, target: rubyLiteral(pair.childForFieldName("value")) };
4110
4199
  }
4111
4200
  return null;
4112
4201
  }
@@ -4276,6 +4365,48 @@ function railsRoutesFromSource(source, parser) {
4276
4365
  });
4277
4366
  return out;
4278
4367
  }
4368
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4369
+ function sinatraRoutesFromSource(source, parser) {
4370
+ const tree = parseSource2(parser, source);
4371
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
4372
+ const out = [];
4373
+ walk(tree.rootNode, (node) => {
4374
+ if (node.type !== "call") return;
4375
+ if (node.childForFieldName("receiver")) return;
4376
+ const method = node.childForFieldName("method")?.text;
4377
+ if (!method || !SINATRA_VERBS.has(method)) return;
4378
+ if (!node.childForFieldName("block")) return;
4379
+ const first = node.childForFieldName("arguments")?.namedChild(0);
4380
+ if (first?.type !== "string") return;
4381
+ const p = rubyLiteral(first);
4382
+ if (p === null || !p.startsWith("/")) return;
4383
+ out.push({
4384
+ method: method.toUpperCase(),
4385
+ pathTemplate: canonicalizeTemplate(p),
4386
+ line: node.startPosition.row + 1,
4387
+ framework: "sinatra"
4388
+ });
4389
+ });
4390
+ return out;
4391
+ }
4392
+ function fileReferencesSinatra(root) {
4393
+ let found = false;
4394
+ walk(root, (node) => {
4395
+ if (found) return;
4396
+ if (node.type === "call") {
4397
+ const m = node.childForFieldName("method")?.text;
4398
+ if (m === "require" || m === "require_relative") {
4399
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
4400
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
4401
+ }
4402
+ return;
4403
+ }
4404
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
4405
+ found = true;
4406
+ }
4407
+ });
4408
+ return found;
4409
+ }
4279
4410
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
4280
4411
  var LARAVEL_RESOURCE_ROWS = [
4281
4412
  { action: "index", methods: ["GET"], suffix: "" },
@@ -4451,6 +4582,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
4451
4582
  }
4452
4583
  return out;
4453
4584
  }
4585
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4586
+ function isSlimAppCtor(node) {
4587
+ if (!node) return false;
4588
+ if (node.type === "scoped_call_expression") {
4589
+ const scope = node.childForFieldName("scope")?.text ?? "";
4590
+ const name = node.childForFieldName("name")?.text;
4591
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
4592
+ }
4593
+ if (node.type === "object_creation_expression") {
4594
+ const cls = node.namedChild(0)?.text ?? "";
4595
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
4596
+ }
4597
+ return false;
4598
+ }
4599
+ function isSlimAppType(node) {
4600
+ if (!node || node.type !== "named_type") return false;
4601
+ const text = node.text;
4602
+ return text === "App" || text.endsWith("\\App");
4603
+ }
4604
+ function collectSlimAppVars(root) {
4605
+ const vars = /* @__PURE__ */ new Set();
4606
+ walk(root, (node) => {
4607
+ if (node.type === "assignment_expression") {
4608
+ const left = node.childForFieldName("left");
4609
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
4610
+ vars.add(left.text);
4611
+ }
4612
+ return;
4613
+ }
4614
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
4615
+ const name = node.childForFieldName("name");
4616
+ if (name?.type === "variable_name") vars.add(name.text);
4617
+ }
4618
+ });
4619
+ return vars;
4620
+ }
4621
+ function slimClosureParamVars(closure) {
4622
+ const out = ["$this"];
4623
+ for (let i = 0; i < closure.namedChildCount; i++) {
4624
+ const params = closure.namedChild(i);
4625
+ if (params?.type !== "formal_parameters") continue;
4626
+ for (let j = 0; j < params.namedChildCount; j++) {
4627
+ const param = params.namedChild(j);
4628
+ if (param?.type !== "simple_parameter") continue;
4629
+ for (let k = 0; k < param.namedChildCount; k++) {
4630
+ const v = param.namedChild(k);
4631
+ if (v?.type === "variable_name") {
4632
+ out.push(v.text);
4633
+ break;
4634
+ }
4635
+ }
4636
+ }
4637
+ }
4638
+ return out;
4639
+ }
4640
+ function phpStringArray(node) {
4641
+ const out = [];
4642
+ if (node?.type !== "array_creation_expression") return out;
4643
+ for (let i = 0; i < node.namedChildCount; i++) {
4644
+ const el = node.namedChild(i);
4645
+ if (el?.type !== "array_element_initializer") continue;
4646
+ const s = phpStaticString(el.namedChild(0));
4647
+ if (s !== null) out.push(s);
4648
+ }
4649
+ return out;
4650
+ }
4651
+ function slimRoutesFromSource(source, parser) {
4652
+ const tree = parseSource2(parser, source);
4653
+ const appVars = collectSlimAppVars(tree.rootNode);
4654
+ if (appVars.size === 0) return [];
4655
+ const out = [];
4656
+ slimWalk(tree.rootNode, "", appVars, out);
4657
+ return out;
4658
+ }
4659
+ function slimWalk(node, prefix, appVars, out) {
4660
+ for (let i = 0; i < node.namedChildCount; i++) {
4661
+ const child = node.namedChild(i);
4662
+ if (child) slimHandle(child, prefix, appVars, out);
4663
+ }
4664
+ }
4665
+ function slimHandle(node, prefix, appVars, out) {
4666
+ if (node.type === "member_call_expression") {
4667
+ const obj = node.childForFieldName("object");
4668
+ const method = node.childForFieldName("name")?.text;
4669
+ const args = node.childForFieldName("arguments");
4670
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4671
+ const line = node.startPosition.row + 1;
4672
+ if (method === "group") {
4673
+ const groupPrefix = phpFirstString(args);
4674
+ const closure = laravelGroupClosure(args);
4675
+ if (groupPrefix !== null && closure) {
4676
+ const inner = new Set(appVars);
4677
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4678
+ const body = closure.childForFieldName("body");
4679
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4680
+ }
4681
+ return;
4682
+ }
4683
+ if (SLIM_VERBS.has(method)) {
4684
+ const p = phpFirstString(args);
4685
+ if (p !== null) {
4686
+ out.push({
4687
+ method: method.toUpperCase(),
4688
+ pathTemplate: laravelJoinPath(prefix, p),
4689
+ line,
4690
+ framework: "slim"
4691
+ });
4692
+ }
4693
+ return;
4694
+ }
4695
+ if (method === "any") {
4696
+ const p = phpFirstString(args);
4697
+ if (p !== null) {
4698
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4699
+ }
4700
+ return;
4701
+ }
4702
+ if (method === "map") {
4703
+ const vals = phpArgumentValues(args);
4704
+ const methods = phpStringArray(vals[0]);
4705
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4706
+ if (p !== null) {
4707
+ for (const m of methods) {
4708
+ out.push({
4709
+ method: m.toUpperCase(),
4710
+ pathTemplate: laravelJoinPath(prefix, p),
4711
+ line,
4712
+ framework: "slim"
4713
+ });
4714
+ }
4715
+ }
4716
+ return;
4717
+ }
4718
+ }
4719
+ }
4720
+ slimWalk(node, prefix, appVars, out);
4721
+ }
4722
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4723
+ function rustStringContent(node) {
4724
+ if (!node || node.type !== "string_literal") return null;
4725
+ for (let i = 0; i < node.namedChildCount; i++) {
4726
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4727
+ }
4728
+ return "";
4729
+ }
4730
+ function actixRoutesFromSource(source, parser) {
4731
+ const tree = parseSource2(parser, source);
4732
+ const out = [];
4733
+ walk(tree.rootNode, (node) => {
4734
+ if (node.type === "attribute_item") {
4735
+ actixAttributeRoute(node, out);
4736
+ return;
4737
+ }
4738
+ if (node.type === "call_expression") {
4739
+ actixBuilderRoute(node, out);
4740
+ }
4741
+ });
4742
+ return out;
4743
+ }
4744
+ function actixAttributeRoute(attrItem, out) {
4745
+ const attr = attrItem.namedChild(0);
4746
+ if (!attr || attr.type !== "attribute") return;
4747
+ const nameNode = attr.namedChild(0);
4748
+ if (!nameNode) return;
4749
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4750
+ if (!macro) return;
4751
+ const tokens = attr.childForFieldName("arguments");
4752
+ if (!tokens || tokens.type !== "token_tree") return;
4753
+ const strings = [];
4754
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4755
+ const s = rustStringContent(tokens.namedChild(i));
4756
+ if (s !== null) strings.push(s);
4757
+ }
4758
+ const pathStr = strings[0];
4759
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4760
+ const line = attrItem.startPosition.row + 1;
4761
+ const template = canonicalizeTemplate(pathStr);
4762
+ if (ACTIX_METHODS.has(macro)) {
4763
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4764
+ return;
4765
+ }
4766
+ if (macro === "route") {
4767
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4768
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4769
+ for (const m of list) {
4770
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4771
+ }
4772
+ }
4773
+ }
4774
+ function actixBuilderRoute(call, out) {
4775
+ const fn = call.childForFieldName("function");
4776
+ if (fn?.type !== "field_expression") return;
4777
+ if (fn.childForFieldName("field")?.text !== "route") return;
4778
+ const args = call.childForFieldName("arguments");
4779
+ const pathStr = rustStringContent(args?.namedChild(0));
4780
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4781
+ const second = args?.namedChild(1);
4782
+ if (!second) return;
4783
+ const method = actixBuilderMethod(second);
4784
+ if (!method) return;
4785
+ out.push({
4786
+ method,
4787
+ pathTemplate: canonicalizeTemplate(pathStr),
4788
+ line: call.startPosition.row + 1,
4789
+ framework: "actix-web"
4790
+ });
4791
+ }
4792
+ function actixBuilderMethod(node) {
4793
+ let method = null;
4794
+ walk(node, (n) => {
4795
+ if (method || n.type !== "scoped_identifier") return;
4796
+ const verb = n.childForFieldName("name")?.text;
4797
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4798
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4799
+ });
4800
+ return method;
4801
+ }
4454
4802
  function namedArgs(argsNode) {
4455
4803
  const out = [];
4456
4804
  if (!argsNode) return out;
@@ -4767,6 +5115,7 @@ async function addRoutes(graph, services) {
4767
5115
  const goParser = makeGoParser2();
4768
5116
  const rubyParser = makeRubyParser();
4769
5117
  const phpParser = makePhpParser();
5118
+ const rustParser = makeRustParser();
4770
5119
  let nodesAdded = 0;
4771
5120
  let edgesAdded = 0;
4772
5121
  for (const service of services) {
@@ -4789,7 +5138,10 @@ async function addRoutes(graph, services) {
4789
5138
  const isGoService = service.node.language === "go";
4790
5139
  const hasRails = deps["rails"] !== void 0;
4791
5140
  const hasLaravel = deps["laravel/framework"] !== void 0;
4792
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
5141
+ const hasSlim = deps["slim/slim"] !== void 0;
5142
+ const hasSinatra = deps["sinatra"] !== void 0;
5143
+ const hasActix = deps["actix-web"] !== void 0;
5144
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4793
5145
  continue;
4794
5146
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4795
5147
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4800,7 +5152,8 @@ async function addRoutes(graph, services) {
4800
5152
  const isGo = ext === ".go";
4801
5153
  const isRb = ext === ".rb";
4802
5154
  const isPhp = ext === ".php";
4803
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
5155
+ const isRs = ext === ".rs";
5156
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4804
5157
  const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
4805
5158
  let routes;
4806
5159
  try {
@@ -4810,8 +5163,12 @@ async function addRoutes(graph, services) {
4810
5163
  phpParser,
4811
5164
  relFile === "routes/api.php" ? "/api" : ""
4812
5165
  ) : [];
5166
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4813
5167
  } else if (isRb) {
4814
5168
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
5169
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
5170
+ } else if (isRs) {
5171
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4815
5172
  } else if (isGo) {
4816
5173
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4817
5174
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -5046,6 +5403,37 @@ function loadIncidentThresholdsFromEnv() {
5046
5403
  return DEFAULT_INCIDENT_THRESHOLDS;
5047
5404
  }
5048
5405
  }
5406
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
5407
+ function latencyStreamCeilingMs() {
5408
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
5409
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
5410
+ const n = Number(raw);
5411
+ if (Number.isFinite(n) && n > 0) return n;
5412
+ console.warn(
5413
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
5414
+ );
5415
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
5416
+ }
5417
+ function spanServesEventStream(attrs) {
5418
+ for (const key of [
5419
+ "http.response.header.content-type",
5420
+ "http.response.header.content_type"
5421
+ ]) {
5422
+ const v = attrs[key];
5423
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
5424
+ for (const item of values) {
5425
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
5426
+ return true;
5427
+ }
5428
+ }
5429
+ }
5430
+ return false;
5431
+ }
5432
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
5433
+ if (span.websocketChannel !== void 0) return true;
5434
+ if (spanServesEventStream(span.attributes)) return true;
5435
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
5436
+ }
5049
5437
  function httpResponseStatusFromAttrs(attrs) {
5050
5438
  for (const key of ["http.response.status_code", "http.status_code"]) {
5051
5439
  const v = attrs[key];
@@ -5938,6 +6326,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5938
6326
  );
5939
6327
  ctx.burstState.delete(key);
5940
6328
  }
6329
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
6330
+ function nextApiRouteTemplate(span) {
6331
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
6332
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
6333
+ return match ? match[1] : void 0;
6334
+ }
5941
6335
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5942
6336
  const target = normalizePathTemplate(httpRoute);
5943
6337
  const m = method?.toUpperCase();
@@ -5960,7 +6354,7 @@ async function handleSpan(ctx, span) {
5960
6354
  }
5961
6355
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5962
6356
  const isError = span.statusCode === 2;
5963
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
6357
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
5964
6358
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
5965
6359
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
5966
6360
  cacheSpanService(span, nowMs, callSite);
@@ -6157,12 +6551,13 @@ async function handleSpan(ctx, span) {
6157
6551
  }
6158
6552
  }
6159
6553
  }
6160
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6554
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
6555
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6161
6556
  const routeNodeId = findRouteNodeByHttpRoute(
6162
6557
  ctx.graph,
6163
6558
  span.service,
6164
6559
  span.httpMethod,
6165
- span.httpRoute
6560
+ fusionRoute
6166
6561
  );
6167
6562
  if (routeNodeId) {
6168
6563
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -7550,7 +7945,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
7550
7945
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
7551
7946
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
7552
7947
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
7553
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
7948
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
7554
7949
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
7555
7950
  var import_types20 = require("@neat.is/types");
7556
7951
  var PARSE_CHUNK3 = 16384;
@@ -7571,7 +7966,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
7571
7966
  ".cs": import_tree_sitter_c_sharp.default,
7572
7967
  ".java": import_tree_sitter_java.default,
7573
7968
  ".kt": import_tree_sitter_kotlin.default,
7574
- ".rs": import_tree_sitter_rust.default,
7969
+ ".rs": import_tree_sitter_rust2.default,
7575
7970
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
7576
7971
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
7577
7972
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8011,15 +8406,15 @@ function collectKotlinSymbolDefs(root) {
8011
8406
  });
8012
8407
  };
8013
8408
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8014
- const firstChildOfType = (node, types) => {
8409
+ const firstChildOfType2 = (node, types) => {
8015
8410
  for (let i = 0; i < node.namedChildCount; i++) {
8016
8411
  const child = node.namedChild(i);
8017
8412
  if (child && types.includes(child.type)) return child;
8018
8413
  }
8019
8414
  return void 0;
8020
8415
  };
8021
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8022
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
8416
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
8417
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8023
8418
  let pkg;
8024
8419
  for (let i = 0; i < root.namedChildCount; i++) {
8025
8420
  const child = root.namedChild(i);
@@ -8520,7 +8915,7 @@ async function addSymbolEdges(graph, services) {
8520
8915
  return best;
8521
8916
  };
8522
8917
  const requests = [];
8523
- const walk9 = (node) => {
8918
+ const walk10 = (node) => {
8524
8919
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
8525
8920
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
8526
8921
  if (self && self.kind === "class") {
@@ -8566,10 +8961,10 @@ async function addSymbolEdges(graph, services) {
8566
8961
  }
8567
8962
  for (let i = 0; i < node.namedChildCount; i++) {
8568
8963
  const child = node.namedChild(i);
8569
- if (child) walk9(child);
8964
+ if (child) walk10(child);
8570
8965
  }
8571
8966
  };
8572
- walk9(root);
8967
+ walk10(root);
8573
8968
  for (const req2 of requests) {
8574
8969
  const targetSid = resolveTarget(req2.targetName, req2.wantKind);
8575
8970
  if (!targetSid) continue;
@@ -8865,7 +9260,7 @@ async function addServerActions(graph, services) {
8865
9260
 
8866
9261
  // src/extract/databases/index.ts
8867
9262
  init_cjs_shims();
8868
- var import_node_path33 = __toESM(require("path"), 1);
9263
+ var import_node_path34 = __toESM(require("path"), 1);
8869
9264
  var import_types23 = require("@neat.is/types");
8870
9265
 
8871
9266
  // src/extract/databases/db-config-yaml.ts
@@ -9300,9 +9695,163 @@ async function parse8(serviceDir) {
9300
9695
  }
9301
9696
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9302
9697
 
9303
- // src/extract/databases/docker-compose.ts
9698
+ // src/extract/databases/csharp.ts
9304
9699
  init_cjs_shims();
9700
+ var import_node_fs22 = require("fs");
9305
9701
  var import_node_path32 = __toESM(require("path"), 1);
9702
+ var CS_EXT = ".cs";
9703
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
9704
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
9705
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
9706
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
9707
+ function hostIsUnresolved(host) {
9708
+ return host === "" || /[${}]/.test(host);
9709
+ }
9710
+ function looksLikePostgres(s) {
9711
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
9712
+ }
9713
+ function looksLikeRedis(s) {
9714
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
9715
+ }
9716
+ function parsePostgresConnection(raw) {
9717
+ const s = raw.trim();
9718
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
9719
+ const fields = /* @__PURE__ */ new Map();
9720
+ for (const part of s.split(";")) {
9721
+ const eq = part.indexOf("=");
9722
+ if (eq < 0) continue;
9723
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
9724
+ const value = part.slice(eq + 1).trim();
9725
+ if (value && !fields.has(key)) fields.set(key, value);
9726
+ }
9727
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
9728
+ if (!hostRaw) return null;
9729
+ const host = hostRaw.split(",")[0].trim();
9730
+ if (hostIsUnresolved(host)) return null;
9731
+ const portRaw = fields.get("port");
9732
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
9733
+ const database = fields.get("database") ?? fields.get("db") ?? "";
9734
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
9735
+ }
9736
+ function parseRedisEndpoint(raw) {
9737
+ const s = raw.trim();
9738
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
9739
+ const first = s.split(",")[0].trim();
9740
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
9741
+ if (!m) return null;
9742
+ const host = m[1];
9743
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
9744
+ const port = m[2] ? Number(m[2]) : void 0;
9745
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
9746
+ }
9747
+ async function resolveEnvUpTree(startDir, name) {
9748
+ let dir = import_node_path32.default.resolve(startDir);
9749
+ for (let depth = 0; depth < 12; depth++) {
9750
+ const value = await resolveEnvVar(dir, name);
9751
+ if (value !== null) return value;
9752
+ const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
9753
+ const parent = import_node_path32.default.dirname(dir);
9754
+ if (atRepoRoot || parent === dir) break;
9755
+ dir = parent;
9756
+ }
9757
+ return null;
9758
+ }
9759
+ async function interpolateEnvRefs(value, dir) {
9760
+ const refs = /* @__PURE__ */ new Set();
9761
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
9762
+ refs.add(m[1] ?? m[2]);
9763
+ }
9764
+ let out = value;
9765
+ for (const name of refs) {
9766
+ const resolved = await resolveEnvUpTree(dir, name);
9767
+ if (resolved === null) continue;
9768
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
9769
+ }
9770
+ return out;
9771
+ }
9772
+ function stringLiterals(masked) {
9773
+ const out = [];
9774
+ STRING_LITERAL_RE.lastIndex = 0;
9775
+ let m;
9776
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
9777
+ return out;
9778
+ }
9779
+ function envKeys(masked) {
9780
+ const out = [];
9781
+ ENV_READ_RE.lastIndex = 0;
9782
+ let m;
9783
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
9784
+ const key = m[1] ?? m[2];
9785
+ if (key) out.push(key);
9786
+ }
9787
+ return out;
9788
+ }
9789
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
9790
+ const out = [];
9791
+ for (const key of keys) {
9792
+ const raw = await resolveEnvUpTree(serviceDir, key);
9793
+ if (raw === null) continue;
9794
+ const value = await interpolateEnvRefs(raw, serviceDir);
9795
+ if (!looksLike(value)) continue;
9796
+ const parsed = parse11(value);
9797
+ if (parsed) out.push(parsed);
9798
+ }
9799
+ for (const lit of literals) {
9800
+ if (!looksLike(lit)) continue;
9801
+ const parsed = parse11(lit);
9802
+ if (parsed) out.push(parsed);
9803
+ }
9804
+ return out;
9805
+ }
9806
+ async function parse9(serviceDir) {
9807
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
9808
+ (f) => import_node_path32.default.extname(f) === CS_EXT
9809
+ );
9810
+ if (files.length === 0) return [];
9811
+ const sources = [];
9812
+ for (const file of files) {
9813
+ const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
9814
+ if (content !== null) sources.push({ file, content });
9815
+ }
9816
+ let pgGateFile = null;
9817
+ let redisGateFile = null;
9818
+ for (const { file, content } of sources) {
9819
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
9820
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
9821
+ }
9822
+ if (!pgGateFile && !redisGateFile) return [];
9823
+ const literals = [];
9824
+ const keys = [];
9825
+ for (const { content } of sources) {
9826
+ const masked = maskCommentsInSource(content);
9827
+ literals.push(...stringLiterals(masked));
9828
+ keys.push(...envKeys(masked));
9829
+ }
9830
+ const out = [];
9831
+ const seenHosts = /* @__PURE__ */ new Set();
9832
+ const push = (config, sourceFile) => {
9833
+ const dedupe = `${config.engine}:${config.host}`;
9834
+ if (seenHosts.has(dedupe)) return;
9835
+ seenHosts.add(dedupe);
9836
+ out.push({ ...config, sourceFile });
9837
+ };
9838
+ if (pgGateFile) {
9839
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
9840
+ push(pg3, pgGateFile);
9841
+ }
9842
+ }
9843
+ if (redisGateFile) {
9844
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
9845
+ push(redis, redisGateFile);
9846
+ }
9847
+ }
9848
+ return out;
9849
+ }
9850
+ var csharpParser = { name: "csharp", parse: parse9 };
9851
+
9852
+ // src/extract/databases/docker-compose.ts
9853
+ init_cjs_shims();
9854
+ var import_node_path33 = __toESM(require("path"), 1);
9306
9855
  function portFromService(svc) {
9307
9856
  for (const raw of svc.ports ?? []) {
9308
9857
  const str = String(raw);
@@ -9327,9 +9876,9 @@ function databaseFromEnv(svc) {
9327
9876
  };
9328
9877
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9329
9878
  }
9330
- async function parse9(serviceDir) {
9879
+ async function parse10(serviceDir) {
9331
9880
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9332
- const abs = import_node_path32.default.join(serviceDir, name);
9881
+ const abs = import_node_path33.default.join(serviceDir, name);
9333
9882
  if (!await exists(abs)) continue;
9334
9883
  const raw = await readYaml(abs);
9335
9884
  if (!raw?.services) return [];
@@ -9351,7 +9900,7 @@ async function parse9(serviceDir) {
9351
9900
  }
9352
9901
  return [];
9353
9902
  }
9354
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
9903
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9355
9904
 
9356
9905
  // src/extract/databases/index.ts
9357
9906
  var DB_PARSERS = [
@@ -9363,6 +9912,7 @@ var DB_PARSERS = [
9363
9912
  ormconfigParser,
9364
9913
  typeormParser,
9365
9914
  sequelizeParser,
9915
+ csharpParser,
9366
9916
  dockerComposeParser
9367
9917
  ];
9368
9918
  function compatibleDriversFor(engine) {
@@ -9501,7 +10051,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9501
10051
  discoveredVia: mergedDiscoveredVia
9502
10052
  });
9503
10053
  }
9504
- const relConfigFile = toPosix(import_node_path33.default.relative(service.dir, config.sourceFile));
10054
+ const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
9505
10055
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9506
10056
  graph,
9507
10057
  service.pkg.name,
@@ -9510,7 +10060,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9510
10060
  );
9511
10061
  nodesAdded += fn;
9512
10062
  edgesAdded += fe;
9513
- const evidenceFile = toPosix(import_node_path33.default.relative(scanPath, config.sourceFile));
10063
+ const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
9514
10064
  const edge = {
9515
10065
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
9516
10066
  source: fileNodeId,
@@ -9528,15 +10078,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9528
10078
  if (allConfigs.length === 1) {
9529
10079
  const primary = allConfigs[0];
9530
10080
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
9531
- const relPath = import_node_path33.default.relative(scanPath, primary.sourceFile);
10081
+ const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
9532
10082
  const cfgId = (0, import_types23.configId)(relPath);
9533
10083
  if (!graph.hasNode(cfgId)) {
9534
10084
  const cfgNode = {
9535
10085
  id: cfgId,
9536
10086
  type: import_types23.NodeType.ConfigNode,
9537
- name: import_node_path33.default.basename(primary.sourceFile),
10087
+ name: import_node_path34.default.basename(primary.sourceFile),
9538
10088
  path: relPath,
9539
- fileType: isConfigFile(import_node_path33.default.basename(primary.sourceFile)).fileType || "config"
10089
+ fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
9540
10090
  };
9541
10091
  graph.addNode(cfgId, cfgNode);
9542
10092
  nodesAdded++;
@@ -9577,27 +10127,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9577
10127
 
9578
10128
  // src/extract/configs.ts
9579
10129
  init_cjs_shims();
9580
- var import_node_fs22 = require("fs");
9581
- var import_node_path34 = __toESM(require("path"), 1);
10130
+ var import_node_fs23 = require("fs");
10131
+ var import_node_path35 = __toESM(require("path"), 1);
9582
10132
  var import_types24 = require("@neat.is/types");
9583
10133
  async function walkConfigFiles(dir, excludeDirs = []) {
9584
- const excluded = new Set(excludeDirs.map((d) => import_node_path34.default.resolve(d)));
10134
+ const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
9585
10135
  const out = [];
9586
- async function walk9(current) {
9587
- const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true });
10136
+ async function walk10(current) {
10137
+ const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
9588
10138
  for (const entry2 of entries) {
9589
- const full = import_node_path34.default.join(current, entry2.name);
10139
+ const full = import_node_path35.default.join(current, entry2.name);
9590
10140
  if (entry2.isDirectory()) {
9591
10141
  if (IGNORED_DIRS.has(entry2.name)) continue;
9592
- if (excluded.has(import_node_path34.default.resolve(full))) continue;
10142
+ if (excluded.has(import_node_path35.default.resolve(full))) continue;
9593
10143
  if (await isPythonVenvDir(full)) continue;
9594
- await walk9(full);
10144
+ await walk10(full);
9595
10145
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
9596
10146
  out.push(full);
9597
10147
  }
9598
10148
  }
9599
10149
  }
9600
- await walk9(dir);
10150
+ await walk10(dir);
9601
10151
  return out;
9602
10152
  }
9603
10153
  async function addConfigNodes(graph, services, scanPath) {
@@ -9606,19 +10156,19 @@ async function addConfigNodes(graph, services, scanPath) {
9606
10156
  for (const service of services) {
9607
10157
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
9608
10158
  for (const file of configFiles) {
9609
- const relPath = import_node_path34.default.relative(scanPath, file);
10159
+ const relPath = import_node_path35.default.relative(scanPath, file);
9610
10160
  const node = {
9611
10161
  id: (0, import_types24.configId)(relPath),
9612
10162
  type: import_types24.NodeType.ConfigNode,
9613
- name: import_node_path34.default.basename(file),
10163
+ name: import_node_path35.default.basename(file),
9614
10164
  path: relPath,
9615
- fileType: isConfigFile(import_node_path34.default.basename(file)).fileType
10165
+ fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
9616
10166
  };
9617
10167
  if (!graph.hasNode(node.id)) {
9618
10168
  graph.addNode(node.id, node);
9619
10169
  nodesAdded++;
9620
10170
  }
9621
- const relToService = toPosix(import_node_path34.default.relative(service.dir, file));
10171
+ const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
9622
10172
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9623
10173
  graph,
9624
10174
  service.pkg.name,
@@ -9634,7 +10184,7 @@ async function addConfigNodes(graph, services, scanPath) {
9634
10184
  type: import_types24.EdgeType.CONFIGURED_BY,
9635
10185
  provenance: import_types24.Provenance.EXTRACTED,
9636
10186
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
9637
- evidence: { file: relPath.split(import_node_path34.default.sep).join("/") }
10187
+ evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
9638
10188
  };
9639
10189
  if (!graph.hasEdge(edge.id)) {
9640
10190
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -9647,8 +10197,8 @@ async function addConfigNodes(graph, services, scanPath) {
9647
10197
 
9648
10198
  // src/extract/proto.ts
9649
10199
  init_cjs_shims();
9650
- var import_node_fs23 = require("fs");
9651
- var import_node_path35 = __toESM(require("path"), 1);
10200
+ var import_node_fs24 = require("fs");
10201
+ var import_node_path36 = __toESM(require("path"), 1);
9652
10202
  var import_types25 = require("@neat.is/types");
9653
10203
  var PROTO_EXTENSION = ".proto";
9654
10204
  function packageOf(content) {
@@ -9686,23 +10236,23 @@ function grpcMethodsFromProto(content, fqPackage) {
9686
10236
  return out;
9687
10237
  }
9688
10238
  async function walkProtoFiles(dir, excludeDirs = []) {
9689
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10239
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
9690
10240
  const out = [];
9691
- async function walk9(current) {
9692
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10241
+ async function walk10(current) {
10242
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9693
10243
  for (const entry2 of entries) {
9694
- const full = import_node_path35.default.join(current, entry2.name);
10244
+ const full = import_node_path36.default.join(current, entry2.name);
9695
10245
  if (entry2.isDirectory()) {
9696
10246
  if (IGNORED_DIRS.has(entry2.name)) continue;
9697
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10247
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
9698
10248
  if (await isPythonVenvDir(full)) continue;
9699
- await walk9(full);
9700
- } else if (entry2.isFile() && import_node_path35.default.extname(entry2.name) === PROTO_EXTENSION) {
10249
+ await walk10(full);
10250
+ } else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
9701
10251
  out.push(full);
9702
10252
  }
9703
10253
  }
9704
10254
  }
9705
- await walk9(dir);
10255
+ await walk10(dir);
9706
10256
  return out;
9707
10257
  }
9708
10258
  async function addGrpcMethods(graph, services) {
@@ -9712,10 +10262,10 @@ async function addGrpcMethods(graph, services) {
9712
10262
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
9713
10263
  for (const protoPath of protoPaths) {
9714
10264
  if (isTestPath(protoPath)) continue;
9715
- const relFile = toPosix(import_node_path35.default.relative(service.dir, protoPath));
10265
+ const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
9716
10266
  let content;
9717
10267
  try {
9718
- content = await import_node_fs23.promises.readFile(protoPath, "utf8");
10268
+ content = await import_node_fs24.promises.readFile(protoPath, "utf8");
9719
10269
  } catch (err) {
9720
10270
  recordExtractionError("proto extraction", protoPath, err);
9721
10271
  continue;
@@ -9770,11 +10320,11 @@ async function addGrpcMethods(graph, services) {
9770
10320
 
9771
10321
  // src/extract/calls/index.ts
9772
10322
  init_cjs_shims();
9773
- var import_types43 = require("@neat.is/types");
10323
+ var import_types44 = require("@neat.is/types");
9774
10324
 
9775
10325
  // src/extract/calls/http.ts
9776
10326
  init_cjs_shims();
9777
- var import_node_path36 = __toESM(require("path"), 1);
10327
+ var import_node_path37 = __toESM(require("path"), 1);
9778
10328
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
9779
10329
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
9780
10330
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -9857,7 +10407,7 @@ async function addHttpCallEdges(graph, services) {
9857
10407
  const seen = /* @__PURE__ */ new Set();
9858
10408
  for (const file of files) {
9859
10409
  if (isTestPath(file.path)) continue;
9860
- const parser = parserForExt(import_node_path36.default.extname(file.path), parserCache);
10410
+ const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
9861
10411
  let sites;
9862
10412
  try {
9863
10413
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9866,7 +10416,7 @@ async function addHttpCallEdges(graph, services) {
9866
10416
  continue;
9867
10417
  }
9868
10418
  if (sites.length === 0) continue;
9869
- const relFile = toPosix(import_node_path36.default.relative(service.dir, file.path));
10419
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
9870
10420
  for (const site of sites) {
9871
10421
  const targetId = hostToNodeId.get(site.host);
9872
10422
  if (!targetId || targetId === service.node.id) continue;
@@ -9920,7 +10470,7 @@ async function addHttpCallEdges(graph, services) {
9920
10470
 
9921
10471
  // src/extract/calls/route-match.ts
9922
10472
  init_cjs_shims();
9923
- var import_node_path37 = __toESM(require("path"), 1);
10473
+ var import_node_path38 = __toESM(require("path"), 1);
9924
10474
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
9925
10475
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
9926
10476
  var import_types27 = require("@neat.is/types");
@@ -10119,7 +10669,7 @@ async function addRouteCallEdges(graph, services) {
10119
10669
  const seen = /* @__PURE__ */ new Set();
10120
10670
  for (const file of files) {
10121
10671
  if (isTestPath(file.path)) continue;
10122
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path37.default.extname(file.path))) continue;
10672
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
10123
10673
  let sites;
10124
10674
  try {
10125
10675
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10128,7 +10678,7 @@ async function addRouteCallEdges(graph, services) {
10128
10678
  continue;
10129
10679
  }
10130
10680
  if (sites.length === 0) continue;
10131
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
10681
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10132
10682
  for (const site of sites) {
10133
10683
  const serverServiceId = hostToNodeId.get(site.host);
10134
10684
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10189,10 +10739,15 @@ async function addRouteCallEdges(graph, services) {
10189
10739
 
10190
10740
  // src/extract/calls/kafka.ts
10191
10741
  init_cjs_shims();
10192
- var import_node_path38 = __toESM(require("path"), 1);
10742
+ var import_node_path39 = __toESM(require("path"), 1);
10193
10743
  var import_types28 = require("@neat.is/types");
10194
10744
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10195
10745
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
10746
+ var SARAMA_IMPORT_RE = /"[^"]*\/sarama"/;
10747
+ var GO_PRODUCER_RE = /ProducerMessage\s*\{[\s\S]{0,300}?\bTopic\s*:\s*(?:"([^"]+)"|`([^`]+)`|([A-Za-z_]\w*))/g;
10748
+ var GO_CONSUMER_RE = /\.Consume\s*\([\s\S]{0,120}?\[\]string\s*\{([^}]*)\}/g;
10749
+ var GO_STRING_LITERAL_RE = /"([^"]+)"|`([^`]+)`/g;
10750
+ var GO_IDENT_RE = /[A-Za-z_]\w*/g;
10196
10751
  function findAll(re, text) {
10197
10752
  re.lastIndex = 0;
10198
10753
  const out = [];
@@ -10202,38 +10757,94 @@ function findAll(re, text) {
10202
10757
  }
10203
10758
  return out;
10204
10759
  }
10760
+ function resolveGoLiteral(ident, text) {
10761
+ const esc = ident.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10762
+ const re = new RegExp(
10763
+ `\\b${esc}(?:\\s+\\w[\\w.\\[\\]*]*)?\\s*(?::=|=)\\s*(?:"([^"]+)"|\`([^\`]+)\`)`
10764
+ );
10765
+ const m = re.exec(text);
10766
+ if (!m) return void 0;
10767
+ return m[1] ?? m[2];
10768
+ }
10769
+ function producerTopicAnchor(match) {
10770
+ const [full] = match;
10771
+ const tokenLen = match[1] ? match[1].length + 2 : match[2] ? match[2].length + 2 : match[3]?.length ?? 0;
10772
+ return match.index + full.length - tokenLen;
10773
+ }
10774
+ function goSaramaEndpoints(text, emit) {
10775
+ if (!SARAMA_IMPORT_RE.test(text)) return;
10776
+ GO_PRODUCER_RE.lastIndex = 0;
10777
+ let m;
10778
+ while ((m = GO_PRODUCER_RE.exec(text)) !== null) {
10779
+ const literal = m[1] ?? m[2];
10780
+ const anchor = producerTopicAnchor(m);
10781
+ if (literal) {
10782
+ emit(literal, "PUBLISHES_TO", anchor);
10783
+ } else if (m[3]) {
10784
+ const resolved = resolveGoLiteral(m[3], text);
10785
+ if (resolved) emit(resolved, "PUBLISHES_TO", anchor);
10786
+ }
10787
+ }
10788
+ GO_CONSUMER_RE.lastIndex = 0;
10789
+ while ((m = GO_CONSUMER_RE.exec(text)) !== null) {
10790
+ const inside = m[1] ?? "";
10791
+ const anchor = m.index + Math.max(0, m[0].indexOf("[]string"));
10792
+ let sawLiteral = false;
10793
+ GO_STRING_LITERAL_RE.lastIndex = 0;
10794
+ let s;
10795
+ while ((s = GO_STRING_LITERAL_RE.exec(inside)) !== null) {
10796
+ const topic = s[1] ?? s[2];
10797
+ if (topic) {
10798
+ emit(topic, "CONSUMES_FROM", anchor);
10799
+ sawLiteral = true;
10800
+ }
10801
+ }
10802
+ if (sawLiteral) continue;
10803
+ GO_IDENT_RE.lastIndex = 0;
10804
+ let id;
10805
+ while ((id = GO_IDENT_RE.exec(inside)) !== null) {
10806
+ const resolved = resolveGoLiteral(id[0], text);
10807
+ if (resolved) emit(resolved, "CONSUMES_FROM", anchor);
10808
+ }
10809
+ }
10810
+ }
10205
10811
  function kafkaEndpointsFromFile(file, serviceDir) {
10206
10812
  const out = [];
10207
10813
  const seen = /* @__PURE__ */ new Set();
10208
- const make = (topic, edgeType) => {
10814
+ const make = (topic, edgeType, anchor) => {
10209
10815
  const key = `${edgeType}|${topic}`;
10210
10816
  if (seen.has(key)) return;
10211
10817
  seen.add(key);
10212
- const line = lineOf(file.content, topic);
10818
+ const line = anchor !== void 0 ? file.content.slice(0, anchor).split("\n").length : lineOf(file.content, topic);
10213
10819
  out.push({
10214
10820
  infraId: (0, import_types28.infraId)("kafka-topic", topic),
10215
10821
  name: topic,
10216
10822
  kind: "kafka-topic",
10217
10823
  edgeType,
10218
- // `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
10219
- // framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
10220
- // tier (ADR-066).
10824
+ // JS: `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
10825
+ // (kafkajs / node-rdkafka). Go: `sarama.ProducerMessage{Topic: 'x'}` /
10826
+ // `consumerGroup.Consume(ctx, []string{'x'}, …)`. Both are framework-aware
10827
+ // call sites — verified-call-site tier (ADR-066).
10221
10828
  confidenceKind: "verified-call-site",
10222
10829
  evidence: {
10223
- file: import_node_path38.default.relative(serviceDir, file.path),
10830
+ file: import_node_path39.default.relative(serviceDir, file.path),
10224
10831
  line,
10225
10832
  snippet: snippet(file.content, line)
10226
10833
  }
10227
10834
  });
10228
10835
  };
10229
- for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
10230
- for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
10836
+ if (import_node_path39.default.extname(file.path) === ".go") {
10837
+ goSaramaEndpoints(file.content, make);
10838
+ } else {
10839
+ for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
10840
+ for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
10841
+ }
10231
10842
  return out;
10232
10843
  }
10233
10844
 
10234
10845
  // src/extract/calls/redis.ts
10235
10846
  init_cjs_shims();
10236
- var import_node_path39 = __toESM(require("path"), 1);
10847
+ var import_node_path40 = __toESM(require("path"), 1);
10237
10848
  var import_types29 = require("@neat.is/types");
10238
10849
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10239
10850
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10256,7 +10867,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10256
10867
  // support tier (ADR-066).
10257
10868
  confidenceKind: "url-with-structural-support",
10258
10869
  evidence: {
10259
- file: import_node_path39.default.relative(serviceDir, file.path),
10870
+ file: import_node_path40.default.relative(serviceDir, file.path),
10260
10871
  line,
10261
10872
  snippet: snippet(file.content, line)
10262
10873
  }
@@ -10267,7 +10878,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10267
10878
 
10268
10879
  // src/extract/calls/aws.ts
10269
10880
  init_cjs_shims();
10270
- var import_node_path40 = __toESM(require("path"), 1);
10881
+ var import_node_path41 = __toESM(require("path"), 1);
10271
10882
  var import_types30 = require("@neat.is/types");
10272
10883
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10273
10884
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10301,7 +10912,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10301
10912
  // (ADR-066).
10302
10913
  confidenceKind: "verified-call-site",
10303
10914
  evidence: {
10304
- file: import_node_path40.default.relative(serviceDir, file.path),
10915
+ file: import_node_path41.default.relative(serviceDir, file.path),
10305
10916
  line,
10306
10917
  snippet: snippet(file.content, line)
10307
10918
  }
@@ -10326,7 +10937,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10326
10937
 
10327
10938
  // src/extract/calls/grpc.ts
10328
10939
  init_cjs_shims();
10329
- var import_node_path41 = __toESM(require("path"), 1);
10940
+ var import_node_path42 = __toESM(require("path"), 1);
10330
10941
  var import_types31 = require("@neat.is/types");
10331
10942
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10332
10943
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -10385,7 +10996,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10385
10996
  // tier (ADR-066).
10386
10997
  confidenceKind: "verified-call-site",
10387
10998
  evidence: {
10388
- file: import_node_path41.default.relative(serviceDir, file.path),
10999
+ file: import_node_path42.default.relative(serviceDir, file.path),
10389
11000
  line,
10390
11001
  snippet: snippet(file.content, line)
10391
11002
  }
@@ -10396,7 +11007,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10396
11007
 
10397
11008
  // src/extract/calls/supabase.ts
10398
11009
  init_cjs_shims();
10399
- var import_node_path42 = __toESM(require("path"), 1);
11010
+ var import_node_path43 = __toESM(require("path"), 1);
10400
11011
  var import_types32 = require("@neat.is/types");
10401
11012
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
10402
11013
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -10455,7 +11066,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10455
11066
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10456
11067
  confidenceKind: "verified-call-site",
10457
11068
  evidence: {
10458
- file: import_node_path42.default.relative(serviceDir, file.path),
11069
+ file: import_node_path43.default.relative(serviceDir, file.path),
10459
11070
  line,
10460
11071
  snippet: snippet(file.content, line)
10461
11072
  }
@@ -10482,7 +11093,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10482
11093
  edgeType: "CALLS",
10483
11094
  confidenceKind: "verified-call-site",
10484
11095
  evidence: {
10485
- file: import_node_path42.default.relative(serviceDir, file.path),
11096
+ file: import_node_path43.default.relative(serviceDir, file.path),
10486
11097
  line,
10487
11098
  snippet: snippet(file.content, line)
10488
11099
  }
@@ -10494,7 +11105,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10494
11105
 
10495
11106
  // src/extract/calls/firestore.ts
10496
11107
  init_cjs_shims();
10497
- var import_node_path43 = __toESM(require("path"), 1);
11108
+ var import_node_path44 = __toESM(require("path"), 1);
10498
11109
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10499
11110
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10500
11111
  var import_types33 = require("@neat.is/types");
@@ -10533,7 +11144,7 @@ function isFirestoreClientFactory(node) {
10533
11144
  }
10534
11145
  function firestoreClientVars(root) {
10535
11146
  const vars = /* @__PURE__ */ new Set();
10536
- const walk9 = (node) => {
11147
+ const walk10 = (node) => {
10537
11148
  if (node.type === "variable_declarator") {
10538
11149
  const name = node.childForFieldName("name");
10539
11150
  let value = node.childForFieldName("value");
@@ -10542,9 +11153,9 @@ function firestoreClientVars(root) {
10542
11153
  vars.add(name.text);
10543
11154
  }
10544
11155
  }
10545
- for (const c of namedChildren(node)) walk9(c);
11156
+ for (const c of namedChildren(node)) walk10(c);
10546
11157
  };
10547
- walk9(root);
11158
+ walk10(root);
10548
11159
  return vars;
10549
11160
  }
10550
11161
  function isClientExpr(node, clientVars) {
@@ -10665,7 +11276,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10665
11276
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10666
11277
  if (!hasClient && !hasAdmin) return [];
10667
11278
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10668
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
11279
+ const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
10669
11280
  const clientVars = firestoreClientVars(tree.rootNode);
10670
11281
  const collLine = /* @__PURE__ */ new Map();
10671
11282
  const writes = /* @__PURE__ */ new Map();
@@ -10699,7 +11310,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10699
11310
  }
10700
11311
  s.add(field);
10701
11312
  };
10702
- const walk9 = (node) => {
11313
+ const walk10 = (node) => {
10703
11314
  if (node.type === "call_expression") {
10704
11315
  const fn = node.childForFieldName("function");
10705
11316
  const line = node.startPosition.row + 1;
@@ -10739,9 +11350,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10739
11350
  }
10740
11351
  }
10741
11352
  }
10742
- for (const c of namedChildren(node)) walk9(c);
11353
+ for (const c of namedChildren(node)) walk10(c);
10743
11354
  };
10744
- walk9(tree.rootNode);
11355
+ walk10(tree.rootNode);
10745
11356
  const out = [];
10746
11357
  for (const [collPath, line] of collLine) {
10747
11358
  const byField = writes.get(collPath);
@@ -10769,7 +11380,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10769
11380
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10770
11381
  ...sdkWrites ? { sdkWrites } : {},
10771
11382
  evidence: {
10772
- file: import_node_path43.default.relative(serviceDir, file.path),
11383
+ file: import_node_path44.default.relative(serviceDir, file.path),
10773
11384
  line,
10774
11385
  snippet: snippet(file.content, line)
10775
11386
  }
@@ -10780,7 +11391,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10780
11391
 
10781
11392
  // src/extract/calls/mongoose.ts
10782
11393
  init_cjs_shims();
10783
- var import_node_path44 = __toESM(require("path"), 1);
11394
+ var import_node_path45 = __toESM(require("path"), 1);
10784
11395
  var import_types34 = require("@neat.is/types");
10785
11396
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10786
11397
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -10931,7 +11542,7 @@ function endpoint(r, file, serviceDir, matchText) {
10931
11542
  kind: r.kind,
10932
11543
  edgeType: "CALLS",
10933
11544
  confidenceKind: "verified-call-site",
10934
- evidence: { file: import_node_path44.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11545
+ evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
10935
11546
  };
10936
11547
  }
10937
11548
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11021,7 +11632,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11021
11632
  const registry = /* @__PURE__ */ new Map();
11022
11633
  for (const f of mongooseFiles) {
11023
11634
  const fx = fileExportsOf(f.content, pluralizeOn);
11024
- if (fx) registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), fx);
11635
+ if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
11025
11636
  }
11026
11637
  if (registry.size === 0) return [];
11027
11638
  const out = [];
@@ -11032,7 +11643,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11032
11643
  const directColl = /* @__PURE__ */ new Map();
11033
11644
  const nsExports = /* @__PURE__ */ new Map();
11034
11645
  for (const b of bindings) {
11035
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
11646
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
11036
11647
  if (!resolvedRel) continue;
11037
11648
  const fx = registry.get(resolvedRel);
11038
11649
  if (!fx) continue;
@@ -11076,7 +11687,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11076
11687
 
11077
11688
  // src/extract/calls/sqlalchemy.ts
11078
11689
  init_cjs_shims();
11079
- var import_node_path45 = __toESM(require("path"), 1);
11690
+ var import_node_path46 = __toESM(require("path"), 1);
11080
11691
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11081
11692
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11082
11693
  var import_types35 = require("@neat.is/types");
@@ -11215,7 +11826,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11215
11826
  childTable,
11216
11827
  parentTable,
11217
11828
  evidence: {
11218
- file: import_node_path45.default.relative(serviceDir, file.path),
11829
+ file: import_node_path46.default.relative(serviceDir, file.path),
11219
11830
  line,
11220
11831
  snippet: snippet(file.content, line)
11221
11832
  }
@@ -11240,7 +11851,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11240
11851
  confidenceKind: "verified-call-site",
11241
11852
  ...columns && columns.length > 0 ? { columns } : {},
11242
11853
  evidence: {
11243
- file: import_node_path45.default.relative(serviceDir, file.path),
11854
+ file: import_node_path46.default.relative(serviceDir, file.path),
11244
11855
  line,
11245
11856
  snippet: snippet(file.content, line)
11246
11857
  }
@@ -11347,7 +11958,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11347
11958
  edgeType: "CALLS",
11348
11959
  confidenceKind: "verified-call-site",
11349
11960
  evidence: {
11350
- file: import_node_path45.default.relative(serviceDir, file.path),
11961
+ file: import_node_path46.default.relative(serviceDir, file.path),
11351
11962
  line,
11352
11963
  snippet: snippet(file.content, line)
11353
11964
  }
@@ -11359,7 +11970,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11359
11970
 
11360
11971
  // src/extract/calls/django-orm.ts
11361
11972
  init_cjs_shims();
11362
- var import_node_path46 = __toESM(require("path"), 1);
11973
+ var import_node_path47 = __toESM(require("path"), 1);
11363
11974
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11364
11975
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11365
11976
  var import_types36 = require("@neat.is/types");
@@ -11429,7 +12040,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11429
12040
  const tree = parseSource7(makePyParser4(), file.content);
11430
12041
  const out = [];
11431
12042
  const seen = /* @__PURE__ */ new Set();
11432
- const defaultAppLabel = import_node_path46.default.basename(import_node_path46.default.dirname(file.path));
12043
+ const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
11433
12044
  walk4(tree.rootNode, (node) => {
11434
12045
  if (node.type !== "class_definition") return;
11435
12046
  if (!extendsDjangoModel(node)) return;
@@ -11447,7 +12058,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11447
12058
  kind: "sql-table",
11448
12059
  edgeType: "CALLS",
11449
12060
  confidenceKind: "verified-call-site",
11450
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12061
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11451
12062
  });
11452
12063
  });
11453
12064
  return out;
@@ -11455,7 +12066,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11455
12066
 
11456
12067
  // src/extract/calls/drizzle.ts
11457
12068
  init_cjs_shims();
11458
- var import_node_path47 = __toESM(require("path"), 1);
12069
+ var import_node_path48 = __toESM(require("path"), 1);
11459
12070
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11460
12071
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11461
12072
  var import_types37 = require("@neat.is/types");
@@ -11533,10 +12144,10 @@ function columnsFromObject(obj) {
11533
12144
  }
11534
12145
  function drizzleEndpointsFromFile(file, serviceDir) {
11535
12146
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11536
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12147
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11537
12148
  const out = [];
11538
12149
  const seen = /* @__PURE__ */ new Set();
11539
- const walk9 = (node) => {
12150
+ const walk10 = (node) => {
11540
12151
  if (node.type === "call_expression") {
11541
12152
  const fn = node.childForFieldName("function");
11542
12153
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11556,7 +12167,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11556
12167
  confidenceKind: "structural",
11557
12168
  columns,
11558
12169
  evidence: {
11559
- file: import_node_path47.default.relative(serviceDir, file.path),
12170
+ file: import_node_path48.default.relative(serviceDir, file.path),
11560
12171
  line,
11561
12172
  snippet: snippet(file.content, line)
11562
12173
  }
@@ -11564,9 +12175,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11564
12175
  }
11565
12176
  }
11566
12177
  }
11567
- for (const c of namedChildren4(node)) walk9(c);
12178
+ for (const c of namedChildren4(node)) walk10(c);
11568
12179
  };
11569
- walk9(tree.rootNode);
12180
+ walk10(tree.rootNode);
11570
12181
  return out;
11571
12182
  }
11572
12183
  function enclosingVarName(call) {
@@ -11588,7 +12199,7 @@ function enclosingVarName(call) {
11588
12199
  function collectDrizzleTables(root) {
11589
12200
  const tables = [];
11590
12201
  const varToTable = /* @__PURE__ */ new Map();
11591
- const walk9 = (node) => {
12202
+ const walk10 = (node) => {
11592
12203
  if (node.type === "call_expression") {
11593
12204
  const fn = node.childForFieldName("function");
11594
12205
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11603,9 +12214,9 @@ function collectDrizzleTables(root) {
11603
12214
  }
11604
12215
  }
11605
12216
  }
11606
- for (const c of namedChildren4(node)) walk9(c);
12217
+ for (const c of namedChildren4(node)) walk10(c);
11607
12218
  };
11608
- walk9(root);
12219
+ walk10(root);
11609
12220
  return { tables, varToTable };
11610
12221
  }
11611
12222
  function referencesTargetVar(call) {
@@ -11622,13 +12233,13 @@ function referencesTargetVar(call) {
11622
12233
  }
11623
12234
  function drizzleForeignKeys(file, serviceDir) {
11624
12235
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11625
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12236
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11626
12237
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11627
12238
  const out = [];
11628
12239
  const seen = /* @__PURE__ */ new Set();
11629
12240
  for (const table of tables) {
11630
12241
  if (!table.object) continue;
11631
- const walk9 = (node) => {
12242
+ const walk10 = (node) => {
11632
12243
  if (node.type === "call_expression") {
11633
12244
  const targetVar = referencesTargetVar(node);
11634
12245
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -11641,7 +12252,7 @@ function drizzleForeignKeys(file, serviceDir) {
11641
12252
  childTable: table.tableName,
11642
12253
  parentTable,
11643
12254
  evidence: {
11644
- file: import_node_path47.default.relative(serviceDir, file.path),
12255
+ file: import_node_path48.default.relative(serviceDir, file.path),
11645
12256
  line,
11646
12257
  snippet: snippet(file.content, line)
11647
12258
  }
@@ -11649,16 +12260,16 @@ function drizzleForeignKeys(file, serviceDir) {
11649
12260
  }
11650
12261
  }
11651
12262
  }
11652
- for (const c of namedChildren4(node)) walk9(c);
12263
+ for (const c of namedChildren4(node)) walk10(c);
11653
12264
  };
11654
- walk9(table.object);
12265
+ walk10(table.object);
11655
12266
  }
11656
12267
  return out;
11657
12268
  }
11658
12269
 
11659
12270
  // src/extract/calls/prisma.ts
11660
12271
  init_cjs_shims();
11661
- var import_node_path48 = __toESM(require("path"), 1);
12272
+ var import_node_path49 = __toESM(require("path"), 1);
11662
12273
  var import_types38 = require("@neat.is/types");
11663
12274
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11664
12275
  "Int",
@@ -11714,7 +12325,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11714
12325
  confidenceKind: "structural",
11715
12326
  columns: b.columns,
11716
12327
  evidence: {
11717
- file: import_node_path48.default.relative(serviceDir, file.path),
12328
+ file: import_node_path49.default.relative(serviceDir, file.path),
11718
12329
  line: b.startLine,
11719
12330
  snippet: snippet(content, b.startLine)
11720
12331
  }
@@ -11775,7 +12386,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11775
12386
  }
11776
12387
  async function prismaColumnEndpoints(serviceDir) {
11777
12388
  const schemaPath = await findFirst(serviceDir, [
11778
- import_node_path48.default.join("prisma", "schema.prisma"),
12389
+ import_node_path49.default.join("prisma", "schema.prisma"),
11779
12390
  "schema.prisma"
11780
12391
  ]);
11781
12392
  if (!schemaPath) return [];
@@ -11850,7 +12461,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11850
12461
  childTable: current.table,
11851
12462
  parentTable,
11852
12463
  evidence: {
11853
- file: import_node_path48.default.relative(serviceDir, file.path),
12464
+ file: import_node_path49.default.relative(serviceDir, file.path),
11854
12465
  line: lineNo,
11855
12466
  snippet: snippet(content, lineNo)
11856
12467
  }
@@ -11865,7 +12476,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11865
12476
  }
11866
12477
  async function prismaForeignKeys(serviceDir) {
11867
12478
  const schemaPath = await findFirst(serviceDir, [
11868
- import_node_path48.default.join("prisma", "schema.prisma"),
12479
+ import_node_path49.default.join("prisma", "schema.prisma"),
11869
12480
  "schema.prisma"
11870
12481
  ]);
11871
12482
  if (!schemaPath) return [];
@@ -11876,7 +12487,7 @@ async function prismaForeignKeys(serviceDir) {
11876
12487
 
11877
12488
  // src/extract/calls/activerecord.ts
11878
12489
  init_cjs_shims();
11879
- var import_node_path49 = __toESM(require("path"), 1);
12490
+ var import_node_path50 = __toESM(require("path"), 1);
11880
12491
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11881
12492
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
11882
12493
  var import_types39 = require("@neat.is/types");
@@ -12084,7 +12695,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12084
12695
  confidenceKind: "structural",
12085
12696
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12086
12697
  evidence: {
12087
- file: import_node_path49.default.relative(serviceDir, file.path),
12698
+ file: import_node_path50.default.relative(serviceDir, file.path),
12088
12699
  line: table.line,
12089
12700
  snippet: snippet(file.content, table.line)
12090
12701
  }
@@ -12106,7 +12717,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12106
12717
  childTable,
12107
12718
  parentTable,
12108
12719
  evidence: {
12109
- file: import_node_path49.default.relative(serviceDir, file.path),
12720
+ file: import_node_path50.default.relative(serviceDir, file.path),
12110
12721
  line,
12111
12722
  snippet: snippet(file.content, line)
12112
12723
  }
@@ -12189,7 +12800,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12189
12800
  edgeType: "CALLS",
12190
12801
  confidenceKind: "verified-call-site",
12191
12802
  evidence: {
12192
- file: import_node_path49.default.relative(serviceDir, file.path),
12803
+ file: import_node_path50.default.relative(serviceDir, file.path),
12193
12804
  line,
12194
12805
  snippet: snippet(file.content, line)
12195
12806
  }
@@ -12226,7 +12837,7 @@ function railsModelForeignKeys(file, serviceDir) {
12226
12837
  childTable,
12227
12838
  parentTable,
12228
12839
  evidence: {
12229
- file: import_node_path49.default.relative(serviceDir, file.path),
12840
+ file: import_node_path50.default.relative(serviceDir, file.path),
12230
12841
  line,
12231
12842
  snippet: snippet(file.content, line)
12232
12843
  }
@@ -12238,7 +12849,7 @@ function railsModelForeignKeys(file, serviceDir) {
12238
12849
 
12239
12850
  // src/extract/calls/eloquent.ts
12240
12851
  init_cjs_shims();
12241
- var import_node_path50 = __toESM(require("path"), 1);
12852
+ var import_node_path51 = __toESM(require("path"), 1);
12242
12853
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12243
12854
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12244
12855
  var import_types40 = require("@neat.is/types");
@@ -12495,7 +13106,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12495
13106
  confidenceKind: "structural",
12496
13107
  ...columns.length > 0 ? { columns } : {},
12497
13108
  evidence: {
12498
- file: import_node_path50.default.relative(serviceDir, file.path),
13109
+ file: import_node_path51.default.relative(serviceDir, file.path),
12499
13110
  line: bp.line,
12500
13111
  snippet: snippet(file.content, bp.line)
12501
13112
  }
@@ -12517,7 +13128,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12517
13128
  childTable,
12518
13129
  parentTable,
12519
13130
  evidence: {
12520
- file: import_node_path50.default.relative(serviceDir, file.path),
13131
+ file: import_node_path51.default.relative(serviceDir, file.path),
12521
13132
  line,
12522
13133
  snippet: snippet(file.content, line)
12523
13134
  }
@@ -12637,7 +13248,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12637
13248
  edgeType: "CALLS",
12638
13249
  confidenceKind: "verified-call-site",
12639
13250
  evidence: {
12640
- file: import_node_path50.default.relative(serviceDir, file.path),
13251
+ file: import_node_path51.default.relative(serviceDir, file.path),
12641
13252
  line,
12642
13253
  snippet: snippet(file.content, line)
12643
13254
  }
@@ -12673,7 +13284,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12673
13284
  childTable,
12674
13285
  parentTable,
12675
13286
  evidence: {
12676
- file: import_node_path50.default.relative(serviceDir, file.path),
13287
+ file: import_node_path51.default.relative(serviceDir, file.path),
12677
13288
  line,
12678
13289
  snippet: snippet(file.content, line)
12679
13290
  }
@@ -12685,7 +13296,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12685
13296
 
12686
13297
  // src/extract/calls/go.ts
12687
13298
  init_cjs_shims();
12688
- var import_node_path53 = __toESM(require("path"), 1);
13299
+ var import_node_path54 = __toESM(require("path"), 1);
12689
13300
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12690
13301
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12691
13302
  var import_types41 = require("@neat.is/types");
@@ -12760,7 +13371,7 @@ function firstStringLiteralArg(argsNode) {
12760
13371
  return null;
12761
13372
  }
12762
13373
  function goSqlEndpointsFromFile(file, serviceDir) {
12763
- if (import_node_path53.default.extname(file.path) !== ".go") return [];
13374
+ if (import_node_path54.default.extname(file.path) !== ".go") return [];
12764
13375
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12765
13376
  const tree = parseSource10(makeGoParser3(), file.content);
12766
13377
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12789,7 +13400,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12789
13400
  confidenceKind: "verified-call-site",
12790
13401
  ...columns.length > 0 ? { columns } : {},
12791
13402
  evidence: {
12792
- file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
13403
+ file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
12793
13404
  line,
12794
13405
  snippet: snippet(file.content, line)
12795
13406
  }
@@ -12800,7 +13411,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12800
13411
 
12801
13412
  // src/extract/calls/gorm.ts
12802
13413
  init_cjs_shims();
12803
- var import_node_path54 = __toESM(require("path"), 1);
13414
+ var import_node_path55 = __toESM(require("path"), 1);
12804
13415
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12805
13416
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12806
13417
  var import_types42 = require("@neat.is/types");
@@ -13233,7 +13844,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13233
13844
  seen.delete(struct.name);
13234
13845
  }
13235
13846
  function gormEndpointsFromFile(file, serviceDir) {
13236
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13847
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13237
13848
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13238
13849
  const tree = parseSource11(makeGoParser4(), file.content);
13239
13850
  const { structs, models, tableFor } = analyze(tree);
@@ -13255,7 +13866,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13255
13866
  confidenceKind: "structural",
13256
13867
  ...columns.length > 0 ? { columns } : {},
13257
13868
  evidence: {
13258
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13869
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13259
13870
  line: struct.line,
13260
13871
  snippet: snippet(file.content, struct.line)
13261
13872
  }
@@ -13264,7 +13875,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13264
13875
  return out;
13265
13876
  }
13266
13877
  function gormForeignKeys(file, serviceDir) {
13267
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13878
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13268
13879
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13269
13880
  const tree = parseSource11(makeGoParser4(), file.content);
13270
13881
  const { structs, models, tableFor } = analyze(tree);
@@ -13279,7 +13890,7 @@ function gormForeignKeys(file, serviceDir) {
13279
13890
  childTable,
13280
13891
  parentTable,
13281
13892
  evidence: {
13282
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13893
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13283
13894
  line,
13284
13895
  snippet: snippet(file.content, line)
13285
13896
  }
@@ -13314,15 +13925,131 @@ function gormForeignKeys(file, serviceDir) {
13314
13925
  return out;
13315
13926
  }
13316
13927
 
13928
+ // src/extract/calls/efcore.ts
13929
+ init_cjs_shims();
13930
+ var import_node_path56 = __toESM(require("path"), 1);
13931
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13932
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
13933
+ var import_types43 = require("@neat.is/types");
13934
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
13935
+ var PARSE_CHUNK12 = 16384;
13936
+ function makeCsParser() {
13937
+ const p = new import_tree_sitter16.default();
13938
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
13939
+ return p;
13940
+ }
13941
+ function parseSource12(parser, source) {
13942
+ return parser.parse(
13943
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
13944
+ );
13945
+ }
13946
+ function walk9(node, visit) {
13947
+ visit(node);
13948
+ for (let i = 0; i < node.namedChildCount; i++) {
13949
+ const c = node.namedChild(i);
13950
+ if (c) walk9(c, visit);
13951
+ }
13952
+ }
13953
+ function firstChildOfType(node, type) {
13954
+ for (let i = 0; i < node.namedChildCount; i++) {
13955
+ const c = node.namedChild(i);
13956
+ if (c?.type === type) return c;
13957
+ }
13958
+ return null;
13959
+ }
13960
+ function csStringLiteral(node) {
13961
+ if (node.type === "string_literal") {
13962
+ let out = "";
13963
+ for (let i = 0; i < node.namedChildCount; i++) {
13964
+ const c = node.namedChild(i);
13965
+ if (c?.type === "string_literal_content") out += c.text;
13966
+ }
13967
+ return out;
13968
+ }
13969
+ if (node.type === "verbatim_string_literal") {
13970
+ const t = node.text;
13971
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
13972
+ }
13973
+ return null;
13974
+ }
13975
+ function attributeName(attr) {
13976
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
13977
+ if (!nameNode) return null;
13978
+ const text = nameNode.text;
13979
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
13980
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
13981
+ }
13982
+ function tableFromAttribute(attr) {
13983
+ if (attributeName(attr) !== "Table") return null;
13984
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
13985
+ if (!args) return null;
13986
+ for (let i = 0; i < args.namedChildCount; i++) {
13987
+ const arg = args.namedChild(i);
13988
+ if (arg?.type !== "attribute_argument") continue;
13989
+ const first = arg.namedChild(0);
13990
+ if (!first) continue;
13991
+ const value = csStringLiteral(first);
13992
+ if (value !== null) return value;
13993
+ return null;
13994
+ }
13995
+ return null;
13996
+ }
13997
+ function tableFromToTable(call) {
13998
+ const fn = call.childForFieldName("function");
13999
+ if (fn?.type !== "member_access_expression") return null;
14000
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14001
+ if (method?.text !== "ToTable") return null;
14002
+ const args = call.childForFieldName("arguments");
14003
+ const firstArg2 = args?.namedChild(0);
14004
+ if (firstArg2?.type !== "argument") return null;
14005
+ const value = firstArg2.namedChild(0);
14006
+ return value ? csStringLiteral(value) : null;
14007
+ }
14008
+ function efcoreEndpointsFromFile(file, serviceDir) {
14009
+ if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14010
+ if (!EFCORE_GATE.test(file.content)) return [];
14011
+ const tree = parseSource12(makeCsParser(), file.content);
14012
+ const out = [];
14013
+ const seen = /* @__PURE__ */ new Set();
14014
+ const push = (name, line) => {
14015
+ if (!name || seen.has(name)) return;
14016
+ seen.add(name);
14017
+ out.push({
14018
+ infraId: (0, import_types43.infraId)("sql-table", name),
14019
+ name,
14020
+ kind: "sql-table",
14021
+ edgeType: "CALLS",
14022
+ confidenceKind: "structural",
14023
+ evidence: {
14024
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14025
+ line,
14026
+ snippet: snippet(file.content, line)
14027
+ }
14028
+ });
14029
+ };
14030
+ walk9(tree.rootNode, (node) => {
14031
+ if (node.type === "attribute") {
14032
+ const table = tableFromAttribute(node);
14033
+ if (table) push(table, node.startPosition.row + 1);
14034
+ return;
14035
+ }
14036
+ if (node.type === "invocation_expression") {
14037
+ const table = tableFromToTable(node);
14038
+ if (table) push(table, node.startPosition.row + 1);
14039
+ }
14040
+ });
14041
+ return out;
14042
+ }
14043
+
13317
14044
  // src/extract/calls/index.ts
13318
14045
  function edgeTypeFromEndpoint(ep) {
13319
14046
  switch (ep.edgeType) {
13320
14047
  case "PUBLISHES_TO":
13321
- return import_types43.EdgeType.PUBLISHES_TO;
14048
+ return import_types44.EdgeType.PUBLISHES_TO;
13322
14049
  case "CONSUMES_FROM":
13323
- return import_types43.EdgeType.CONSUMES_FROM;
14050
+ return import_types44.EdgeType.CONSUMES_FROM;
13324
14051
  default:
13325
- return import_types43.EdgeType.CALLS;
14052
+ return import_types44.EdgeType.CALLS;
13326
14053
  }
13327
14054
  }
13328
14055
  function isAwsKind(kind) {
@@ -13372,6 +14099,11 @@ async function addExternalEndpointEdges(graph, services) {
13372
14099
  } catch (err) {
13373
14100
  recordExtractionError("laravel eloquent extraction", file.path, err);
13374
14101
  }
14102
+ try {
14103
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14104
+ } catch (err) {
14105
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14106
+ }
13375
14107
  }
13376
14108
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13377
14109
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -13382,7 +14114,7 @@ async function addExternalEndpointEdges(graph, services) {
13382
14114
  if (!graph.hasNode(ep.infraId)) {
13383
14115
  const node = {
13384
14116
  id: ep.infraId,
13385
- type: import_types43.NodeType.InfraNode,
14117
+ type: import_types44.NodeType.InfraNode,
13386
14118
  name: ep.name,
13387
14119
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
13388
14120
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -13395,21 +14127,21 @@ async function addExternalEndpointEdges(graph, services) {
13395
14127
  }
13396
14128
  if (ep.columns && ep.columns.length > 0) {
13397
14129
  const node = graph.getNodeAttributes(ep.infraId);
13398
- if (node.type === import_types43.NodeType.InfraNode) {
14130
+ if (node.type === import_types44.NodeType.InfraNode) {
13399
14131
  graph.replaceNodeAttributes(ep.infraId, {
13400
14132
  ...node,
13401
14133
  columns: foldColumns(
13402
14134
  node.columns,
13403
14135
  ep.columns,
13404
- import_types43.Provenance.EXTRACTED,
13405
- (0, import_types43.confidenceForExtracted)(ep.confidenceKind)
14136
+ import_types44.Provenance.EXTRACTED,
14137
+ (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
13406
14138
  )
13407
14139
  });
13408
14140
  }
13409
14141
  }
13410
14142
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
13411
14143
  const node = graph.getNodeAttributes(ep.infraId);
13412
- if (node.type === import_types43.NodeType.InfraNode) {
14144
+ if (node.type === import_types44.NodeType.InfraNode) {
13413
14145
  graph.replaceNodeAttributes(ep.infraId, {
13414
14146
  ...node,
13415
14147
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -13417,7 +14149,7 @@ async function addExternalEndpointEdges(graph, services) {
13417
14149
  }
13418
14150
  }
13419
14151
  const edgeType = edgeTypeFromEndpoint(ep);
13420
- const confidence = (0, import_types43.confidenceForExtracted)(ep.confidenceKind);
14152
+ const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
13421
14153
  const relFile = toPosix(ep.evidence.file);
13422
14154
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
13423
14155
  graph,
@@ -13427,7 +14159,7 @@ async function addExternalEndpointEdges(graph, services) {
13427
14159
  );
13428
14160
  nodesAdded += n;
13429
14161
  edgesAdded += e;
13430
- if (!(0, import_types43.passesExtractedFloor)(confidence)) {
14162
+ if (!(0, import_types44.passesExtractedFloor)(confidence)) {
13431
14163
  noteExtractedDropped({
13432
14164
  source: fileNodeId,
13433
14165
  target: ep.infraId,
@@ -13447,7 +14179,7 @@ async function addExternalEndpointEdges(graph, services) {
13447
14179
  source: fileNodeId,
13448
14180
  target: ep.infraId,
13449
14181
  type: edgeType,
13450
- provenance: import_types43.Provenance.EXTRACTED,
14182
+ provenance: import_types44.Provenance.EXTRACTED,
13451
14183
  confidence,
13452
14184
  evidence: ep.evidence
13453
14185
  };
@@ -13470,7 +14202,7 @@ async function addCallEdges(graph, services) {
13470
14202
 
13471
14203
  // src/extract/table-edges.ts
13472
14204
  init_cjs_shims();
13473
- var import_types44 = require("@neat.is/types");
14205
+ var import_types45 = require("@neat.is/types");
13474
14206
  async function addTableEdges(graph, services) {
13475
14207
  let nodesAdded = 0;
13476
14208
  let edgesAdded = 0;
@@ -13498,20 +14230,20 @@ async function addTableEdges(graph, services) {
13498
14230
  }
13499
14231
  refs.push(...modelRefs);
13500
14232
  for (const ref of refs) {
13501
- const childId = (0, import_types44.infraId)("sql-table", ref.childTable);
13502
- const parentId = (0, import_types44.infraId)("sql-table", ref.parentTable);
14233
+ const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14234
+ const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
13503
14235
  if (childId === parentId) continue;
13504
14236
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13505
14237
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13506
- const edgeId = (0, import_types44.extractedEdgeId)(childId, parentId, import_types44.EdgeType.REFERENCES);
14238
+ const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
13507
14239
  if (graph.hasEdge(edgeId)) continue;
13508
14240
  const edge = {
13509
14241
  id: edgeId,
13510
14242
  source: childId,
13511
14243
  target: parentId,
13512
- type: import_types44.EdgeType.REFERENCES,
13513
- provenance: import_types44.Provenance.EXTRACTED,
13514
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14244
+ type: import_types45.EdgeType.REFERENCES,
14245
+ provenance: import_types45.Provenance.EXTRACTED,
14246
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13515
14247
  evidence: ref.evidence
13516
14248
  };
13517
14249
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13524,7 +14256,7 @@ function ensureTableNode(graph, id, name) {
13524
14256
  if (graph.hasNode(id)) return 0;
13525
14257
  const node = {
13526
14258
  id,
13527
- type: import_types44.NodeType.InfraNode,
14259
+ type: import_types45.NodeType.InfraNode,
13528
14260
  name,
13529
14261
  provider: "self",
13530
14262
  kind: "sql-table"
@@ -13538,16 +14270,16 @@ init_cjs_shims();
13538
14270
 
13539
14271
  // src/extract/infra/docker-compose.ts
13540
14272
  init_cjs_shims();
13541
- var import_node_path55 = __toESM(require("path"), 1);
13542
- var import_types46 = require("@neat.is/types");
14273
+ var import_node_path57 = __toESM(require("path"), 1);
14274
+ var import_types47 = require("@neat.is/types");
13543
14275
 
13544
14276
  // src/extract/infra/shared.ts
13545
14277
  init_cjs_shims();
13546
- var import_types45 = require("@neat.is/types");
14278
+ var import_types46 = require("@neat.is/types");
13547
14279
  function makeInfraNode(kind, name, provider = "self", extras) {
13548
14280
  return {
13549
- id: (0, import_types45.infraId)(kind, name),
13550
- type: import_types45.NodeType.InfraNode,
14281
+ id: (0, import_types46.infraId)(kind, name),
14282
+ type: import_types46.NodeType.InfraNode,
13551
14283
  name,
13552
14284
  provider,
13553
14285
  kind,
@@ -13591,8 +14323,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13591
14323
  source: anchorId,
13592
14324
  target: node.id,
13593
14325
  type: edgeType,
13594
- provenance: import_types45.Provenance.EXTRACTED,
13595
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14326
+ provenance: import_types46.Provenance.EXTRACTED,
14327
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13596
14328
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13597
14329
  };
13598
14330
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13609,7 +14341,7 @@ function dependsOnList(value) {
13609
14341
  }
13610
14342
  function serviceNameToServiceNode(name, services) {
13611
14343
  for (const s of services) {
13612
- if (s.node.name === name || import_node_path55.default.basename(s.dir) === name) return s.node.id;
14344
+ if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
13613
14345
  }
13614
14346
  return null;
13615
14347
  }
@@ -13618,7 +14350,7 @@ async function addComposeInfra(graph, scanPath, services) {
13618
14350
  let edgesAdded = 0;
13619
14351
  let composePath = null;
13620
14352
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13621
- const abs = import_node_path55.default.join(scanPath, name);
14353
+ const abs = import_node_path57.default.join(scanPath, name);
13622
14354
  if (await exists(abs)) {
13623
14355
  composePath = abs;
13624
14356
  break;
@@ -13631,13 +14363,13 @@ async function addComposeInfra(graph, scanPath, services) {
13631
14363
  } catch (err) {
13632
14364
  recordExtractionError(
13633
14365
  "infra docker-compose",
13634
- import_node_path55.default.relative(scanPath, composePath),
14366
+ import_node_path57.default.relative(scanPath, composePath),
13635
14367
  err
13636
14368
  );
13637
14369
  return { nodesAdded, edgesAdded };
13638
14370
  }
13639
14371
  if (!compose?.services) return { nodesAdded, edgesAdded };
13640
- const evidenceFile = import_node_path55.default.relative(scanPath, composePath).split(import_node_path55.default.sep).join("/");
14372
+ const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
13641
14373
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13642
14374
  for (const [composeName, svc] of Object.entries(compose.services)) {
13643
14375
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13659,15 +14391,15 @@ async function addComposeInfra(graph, scanPath, services) {
13659
14391
  for (const dep of dependsOnList(svc.depends_on)) {
13660
14392
  const targetId = composeNameToNodeId.get(dep);
13661
14393
  if (!targetId) continue;
13662
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types46.EdgeType.DEPENDS_ON);
14394
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
13663
14395
  if (graph.hasEdge(edgeId)) continue;
13664
14396
  const edge = {
13665
14397
  id: edgeId,
13666
14398
  source: sourceId,
13667
14399
  target: targetId,
13668
- type: import_types46.EdgeType.DEPENDS_ON,
13669
- provenance: import_types46.Provenance.EXTRACTED,
13670
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14400
+ type: import_types47.EdgeType.DEPENDS_ON,
14401
+ provenance: import_types47.Provenance.EXTRACTED,
14402
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13671
14403
  evidence: { file: evidenceFile }
13672
14404
  };
13673
14405
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13679,9 +14411,9 @@ async function addComposeInfra(graph, scanPath, services) {
13679
14411
 
13680
14412
  // src/extract/infra/dockerfile.ts
13681
14413
  init_cjs_shims();
13682
- var import_node_path56 = __toESM(require("path"), 1);
13683
- var import_node_fs24 = require("fs");
13684
- var import_types47 = require("@neat.is/types");
14414
+ var import_node_path58 = __toESM(require("path"), 1);
14415
+ var import_node_fs25 = require("fs");
14416
+ var import_types48 = require("@neat.is/types");
13685
14417
  function readDockerfile(content) {
13686
14418
  let image = null;
13687
14419
  const ports = [];
@@ -13710,15 +14442,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13710
14442
  let nodesAdded = 0;
13711
14443
  let edgesAdded = 0;
13712
14444
  for (const service of services) {
13713
- const dockerfilePath = import_node_path56.default.join(service.dir, "Dockerfile");
14445
+ const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
13714
14446
  if (!await exists(dockerfilePath)) continue;
13715
14447
  let content;
13716
14448
  try {
13717
- content = await import_node_fs24.promises.readFile(dockerfilePath, "utf8");
14449
+ content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
13718
14450
  } catch (err) {
13719
14451
  recordExtractionError(
13720
14452
  "infra dockerfile",
13721
- import_node_path56.default.relative(scanPath, dockerfilePath),
14453
+ import_node_path58.default.relative(scanPath, dockerfilePath),
13722
14454
  err
13723
14455
  );
13724
14456
  continue;
@@ -13730,8 +14462,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13730
14462
  graph.addNode(node.id, node);
13731
14463
  nodesAdded++;
13732
14464
  }
13733
- const relDockerfile = toPosix(import_node_path56.default.relative(service.dir, dockerfilePath));
13734
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, dockerfilePath));
14465
+ const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14466
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
13735
14467
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13736
14468
  graph,
13737
14469
  service.pkg.name,
@@ -13740,15 +14472,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13740
14472
  );
13741
14473
  nodesAdded += fn;
13742
14474
  edgesAdded += fe;
13743
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types47.EdgeType.RUNS_ON);
14475
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
13744
14476
  if (!graph.hasEdge(edgeId)) {
13745
14477
  const edge = {
13746
14478
  id: edgeId,
13747
14479
  source: fileNodeId,
13748
14480
  target: node.id,
13749
- type: import_types47.EdgeType.RUNS_ON,
13750
- provenance: import_types47.Provenance.EXTRACTED,
13751
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14481
+ type: import_types48.EdgeType.RUNS_ON,
14482
+ provenance: import_types48.Provenance.EXTRACTED,
14483
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13752
14484
  evidence: {
13753
14485
  file: evidenceFile,
13754
14486
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13763,15 +14495,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13763
14495
  graph.addNode(portNode.id, portNode);
13764
14496
  nodesAdded++;
13765
14497
  }
13766
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types47.EdgeType.CONNECTS_TO);
14498
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
13767
14499
  if (graph.hasEdge(portEdgeId)) continue;
13768
14500
  const portEdge = {
13769
14501
  id: portEdgeId,
13770
14502
  source: fileNodeId,
13771
14503
  target: portNode.id,
13772
- type: import_types47.EdgeType.CONNECTS_TO,
13773
- provenance: import_types47.Provenance.EXTRACTED,
13774
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14504
+ type: import_types48.EdgeType.CONNECTS_TO,
14505
+ provenance: import_types48.Provenance.EXTRACTED,
14506
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13775
14507
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13776
14508
  };
13777
14509
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13783,23 +14515,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13783
14515
 
13784
14516
  // src/extract/infra/terraform.ts
13785
14517
  init_cjs_shims();
13786
- var import_node_fs25 = require("fs");
13787
- var import_node_path57 = __toESM(require("path"), 1);
13788
- var import_types48 = require("@neat.is/types");
14518
+ var import_node_fs26 = require("fs");
14519
+ var import_node_path59 = __toESM(require("path"), 1);
14520
+ var import_types49 = require("@neat.is/types");
13789
14521
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13790
14522
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13791
14523
  async function walkTfFiles(start, depth = 0, max = 5) {
13792
14524
  if (depth > max) return [];
13793
14525
  const out = [];
13794
- const entries = await import_node_fs25.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14526
+ const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13795
14527
  for (const entry2 of entries) {
13796
14528
  if (entry2.isDirectory()) {
13797
14529
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
13798
- const child = import_node_path57.default.join(start, entry2.name);
14530
+ const child = import_node_path59.default.join(start, entry2.name);
13799
14531
  if (await isPythonVenvDir(child)) continue;
13800
14532
  out.push(...await walkTfFiles(child, depth + 1, max));
13801
14533
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
13802
- out.push(import_node_path57.default.join(start, entry2.name));
14534
+ out.push(import_node_path59.default.join(start, entry2.name));
13803
14535
  }
13804
14536
  }
13805
14537
  return out;
@@ -13830,8 +14562,8 @@ async function addTerraformResources(graph, scanPath) {
13830
14562
  let edgesAdded = 0;
13831
14563
  const files = await walkTfFiles(scanPath);
13832
14564
  for (const file of files) {
13833
- const content = await import_node_fs25.promises.readFile(file, "utf8");
13834
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, file));
14565
+ const content = await import_node_fs26.promises.readFile(file, "utf8");
14566
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
13835
14567
  const resources = [];
13836
14568
  const byKey = /* @__PURE__ */ new Map();
13837
14569
  RESOURCE_RE.lastIndex = 0;
@@ -13866,16 +14598,16 @@ async function addTerraformResources(graph, scanPath) {
13866
14598
  if (!target) continue;
13867
14599
  if (seen.has(target.nodeId)) continue;
13868
14600
  seen.add(target.nodeId);
13869
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types48.EdgeType.DEPENDS_ON);
14601
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
13870
14602
  if (graph.hasEdge(edgeId)) continue;
13871
14603
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13872
14604
  const edge = {
13873
14605
  id: edgeId,
13874
14606
  source: resource.nodeId,
13875
14607
  target: target.nodeId,
13876
- type: import_types48.EdgeType.DEPENDS_ON,
13877
- provenance: import_types48.Provenance.EXTRACTED,
13878
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
14608
+ type: import_types49.EdgeType.DEPENDS_ON,
14609
+ provenance: import_types49.Provenance.EXTRACTED,
14610
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
13879
14611
  evidence: { file: evidenceFile, line, snippet: key }
13880
14612
  };
13881
14613
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13888,8 +14620,8 @@ async function addTerraformResources(graph, scanPath) {
13888
14620
 
13889
14621
  // src/extract/infra/k8s.ts
13890
14622
  init_cjs_shims();
13891
- var import_node_fs26 = require("fs");
13892
- var import_node_path58 = __toESM(require("path"), 1);
14623
+ var import_node_fs27 = require("fs");
14624
+ var import_node_path60 = __toESM(require("path"), 1);
13893
14625
  var import_yaml3 = require("yaml");
13894
14626
  var K8S_KIND_TO_INFRA_KIND = {
13895
14627
  Service: "k8s-service",
@@ -13903,15 +14635,15 @@ var K8S_KIND_TO_INFRA_KIND = {
13903
14635
  async function walkYamlFiles2(start, depth = 0, max = 5) {
13904
14636
  if (depth > max) return [];
13905
14637
  const out = [];
13906
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14638
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13907
14639
  for (const entry2 of entries) {
13908
14640
  if (entry2.isDirectory()) {
13909
14641
  if (IGNORED_DIRS.has(entry2.name)) continue;
13910
- const child = import_node_path58.default.join(start, entry2.name);
14642
+ const child = import_node_path60.default.join(start, entry2.name);
13911
14643
  if (await isPythonVenvDir(child)) continue;
13912
14644
  out.push(...await walkYamlFiles2(child, depth + 1, max));
13913
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path58.default.extname(entry2.name))) {
13914
- out.push(import_node_path58.default.join(start, entry2.name));
14645
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry2.name))) {
14646
+ out.push(import_node_path60.default.join(start, entry2.name));
13915
14647
  }
13916
14648
  }
13917
14649
  return out;
@@ -13920,7 +14652,7 @@ async function addK8sResources(graph, scanPath) {
13920
14652
  let nodesAdded = 0;
13921
14653
  const files = await walkYamlFiles2(scanPath);
13922
14654
  for (const file of files) {
13923
- const content = await import_node_fs26.promises.readFile(file, "utf8");
14655
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
13924
14656
  let docs;
13925
14657
  try {
13926
14658
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -13944,16 +14676,16 @@ async function addK8sResources(graph, scanPath) {
13944
14676
 
13945
14677
  // src/extract/infra/cloudflare.ts
13946
14678
  init_cjs_shims();
13947
- var import_node_fs27 = require("fs");
13948
- var import_node_path59 = __toESM(require("path"), 1);
14679
+ var import_node_fs28 = require("fs");
14680
+ var import_node_path61 = __toESM(require("path"), 1);
13949
14681
  var import_smol_toml3 = require("smol-toml");
13950
- var import_types49 = require("@neat.is/types");
14682
+ var import_types50 = require("@neat.is/types");
13951
14683
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
13952
14684
  async function readWranglerConfig(dir) {
13953
14685
  for (const filename of WRANGLER_FILENAMES) {
13954
- const abs = import_node_path59.default.join(dir, filename);
14686
+ const abs = import_node_path61.default.join(dir, filename);
13955
14687
  if (!await exists(abs)) continue;
13956
- const raw = await import_node_fs27.promises.readFile(abs, "utf8");
14688
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
13957
14689
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
13958
14690
  return { config, relFile: filename, raw };
13959
14691
  }
@@ -13995,8 +14727,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
13995
14727
  source: anchorId,
13996
14728
  target: node.id,
13997
14729
  type: edgeType,
13998
- provenance: import_types49.Provenance.EXTRACTED,
13999
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14730
+ provenance: import_types50.Provenance.EXTRACTED,
14731
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14000
14732
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14001
14733
  };
14002
14734
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14014,11 +14746,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14014
14746
  try {
14015
14747
  read = await readWranglerConfig(service.dir);
14016
14748
  } catch (err) {
14017
- recordExtractionError("infra cloudflare", import_node_path59.default.relative(scanPath, service.dir), err);
14749
+ recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
14018
14750
  continue;
14019
14751
  }
14020
14752
  if (!read || !read.config.name) continue;
14021
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, import_node_path59.default.join(service.dir, read.relFile)));
14753
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
14022
14754
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14023
14755
  }
14024
14756
  for (const worker of discovered) {
@@ -14030,7 +14762,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14030
14762
  }
14031
14763
  let anchorId = service.node.id;
14032
14764
  if (config.main) {
14033
- const entryRelPath = toPosix(import_node_path59.default.normalize(config.main));
14765
+ const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
14034
14766
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14035
14767
  graph,
14036
14768
  service.pkg.name,
@@ -14057,15 +14789,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14057
14789
  nodesAdded++;
14058
14790
  }
14059
14791
  if (runtimeNode.id !== anchorId) {
14060
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types49.EdgeType.RUNS_ON);
14792
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
14061
14793
  if (!graph.hasEdge(runsOnId)) {
14062
14794
  const edge = {
14063
14795
  id: runsOnId,
14064
14796
  source: anchorId,
14065
14797
  target: runtimeNode.id,
14066
- type: import_types49.EdgeType.RUNS_ON,
14067
- provenance: import_types49.Provenance.EXTRACTED,
14068
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14798
+ type: import_types50.EdgeType.RUNS_ON,
14799
+ provenance: import_types50.Provenance.EXTRACTED,
14800
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14069
14801
  evidence: {
14070
14802
  file: evidenceFile,
14071
14803
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14079,7 +14811,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14079
14811
  const result = addResourceEdge(
14080
14812
  graph,
14081
14813
  anchorId,
14082
- import_types49.EdgeType.CONNECTS_TO,
14814
+ import_types50.EdgeType.CONNECTS_TO,
14083
14815
  "cloudflare-route",
14084
14816
  route,
14085
14817
  evidenceFile,
@@ -14103,7 +14835,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14103
14835
  const result = addResourceEdge(
14104
14836
  graph,
14105
14837
  anchorId,
14106
- import_types49.EdgeType.DEPENDS_ON,
14838
+ import_types50.EdgeType.DEPENDS_ON,
14107
14839
  group.kind,
14108
14840
  name,
14109
14841
  evidenceFile,
@@ -14117,7 +14849,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14117
14849
  const result = addResourceEdge(
14118
14850
  graph,
14119
14851
  anchorId,
14120
- import_types49.EdgeType.DEPENDS_ON,
14852
+ import_types50.EdgeType.DEPENDS_ON,
14121
14853
  "cloudflare-cron",
14122
14854
  cron,
14123
14855
  evidenceFile,
@@ -14130,7 +14862,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14130
14862
  const result = addResourceEdge(
14131
14863
  graph,
14132
14864
  anchorId,
14133
- import_types49.EdgeType.DEPENDS_ON,
14865
+ import_types50.EdgeType.DEPENDS_ON,
14134
14866
  "cloudflare-env-var",
14135
14867
  varName,
14136
14868
  evidenceFile,
@@ -14143,15 +14875,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14143
14875
  if (!svc.service) continue;
14144
14876
  const target = workerIndex.get(svc.service);
14145
14877
  if (target && target.anchorId !== anchorId) {
14146
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types49.EdgeType.CALLS);
14878
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
14147
14879
  if (!graph.hasEdge(edgeId)) {
14148
14880
  const edge = {
14149
14881
  id: edgeId,
14150
14882
  source: anchorId,
14151
14883
  target: target.anchorId,
14152
- type: import_types49.EdgeType.CALLS,
14153
- provenance: import_types49.Provenance.EXTRACTED,
14154
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14884
+ type: import_types50.EdgeType.CALLS,
14885
+ provenance: import_types50.Provenance.EXTRACTED,
14886
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14155
14887
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14156
14888
  };
14157
14889
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14162,7 +14894,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14162
14894
  const result = addResourceEdge(
14163
14895
  graph,
14164
14896
  anchorId,
14165
- import_types49.EdgeType.DEPENDS_ON,
14897
+ import_types50.EdgeType.DEPENDS_ON,
14166
14898
  "cloudflare-service-binding",
14167
14899
  svc.service,
14168
14900
  evidenceFile,
@@ -14177,24 +14909,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14177
14909
 
14178
14910
  // src/extract/infra/vercel.ts
14179
14911
  init_cjs_shims();
14180
- var import_node_fs28 = require("fs");
14181
- var import_node_path60 = __toESM(require("path"), 1);
14182
- var import_types50 = require("@neat.is/types");
14912
+ var import_node_fs29 = require("fs");
14913
+ var import_node_path62 = __toESM(require("path"), 1);
14914
+ var import_types51 = require("@neat.is/types");
14183
14915
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14184
14916
  async function readVercelConfig(dir) {
14185
14917
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14186
- const abs = import_node_path60.default.join(dir, filename);
14918
+ const abs = import_node_path62.default.join(dir, filename);
14187
14919
  if (!await exists(abs)) continue;
14188
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14920
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14189
14921
  const config = JSON.parse(maskCommentsInSource(raw));
14190
14922
  return { config, relFile: filename, raw };
14191
14923
  }
14192
14924
  return null;
14193
14925
  }
14194
14926
  async function readLinkedProjectName(dir) {
14195
- const abs = import_node_path60.default.join(dir, ".vercel", "project.json");
14927
+ const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
14196
14928
  if (!await exists(abs)) return void 0;
14197
- const parsed = JSON.parse(await import_node_fs28.promises.readFile(abs, "utf8"));
14929
+ const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
14198
14930
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14199
14931
  }
14200
14932
  function routeSource(route) {
@@ -14210,7 +14942,7 @@ async function addVercelServices(graph, services, scanPath) {
14210
14942
  read = await readVercelConfig(service.dir);
14211
14943
  projectName = await readLinkedProjectName(service.dir);
14212
14944
  } catch (err) {
14213
- recordExtractionError("infra vercel", import_node_path60.default.relative(scanPath, service.dir), err);
14945
+ recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
14214
14946
  continue;
14215
14947
  }
14216
14948
  if (!read && !projectName) continue;
@@ -14226,7 +14958,7 @@ async function addVercelServices(graph, services, scanPath) {
14226
14958
  const anchorId = service.node.id;
14227
14959
  if (!read) continue;
14228
14960
  const { config, relFile, raw } = read;
14229
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, import_node_path60.default.join(service.dir, relFile)));
14961
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
14230
14962
  const add = (edgeType, kind, name) => {
14231
14963
  if (!name) return;
14232
14964
  const result = emitPlatformResourceEdge(
@@ -14242,12 +14974,12 @@ async function addVercelServices(graph, services, scanPath) {
14242
14974
  nodesAdded += result.nodesAdded;
14243
14975
  edgesAdded += result.edgesAdded;
14244
14976
  };
14245
- add(import_types50.EdgeType.RUNS_ON, "vercel", "vercel");
14246
- for (const cron of config.crons ?? []) add(import_types50.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14247
- for (const varName of Object.keys(config.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14248
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14977
+ add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
14978
+ for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14979
+ for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14980
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14249
14981
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14250
- add(import_types50.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14982
+ add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14251
14983
  }
14252
14984
  }
14253
14985
  return { nodesAdded, edgesAdded };
@@ -14255,16 +14987,16 @@ async function addVercelServices(graph, services, scanPath) {
14255
14987
 
14256
14988
  // src/extract/infra/railway.ts
14257
14989
  init_cjs_shims();
14258
- var import_node_fs29 = require("fs");
14259
- var import_node_path61 = __toESM(require("path"), 1);
14990
+ var import_node_fs30 = require("fs");
14991
+ var import_node_path63 = __toESM(require("path"), 1);
14260
14992
  var import_smol_toml4 = require("smol-toml");
14261
- var import_types51 = require("@neat.is/types");
14993
+ var import_types52 = require("@neat.is/types");
14262
14994
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14263
14995
  async function readRailwayConfig(dir) {
14264
14996
  for (const filename of RAILWAY_FILENAMES) {
14265
- const abs = import_node_path61.default.join(dir, filename);
14997
+ const abs = import_node_path63.default.join(dir, filename);
14266
14998
  if (!await exists(abs)) continue;
14267
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14999
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14268
15000
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14269
15001
  return { config, relFile: filename, raw };
14270
15002
  }
@@ -14278,7 +15010,7 @@ async function addRailwayServices(graph, services, scanPath) {
14278
15010
  try {
14279
15011
  read = await readRailwayConfig(service.dir);
14280
15012
  } catch (err) {
14281
- recordExtractionError("infra railway", import_node_path61.default.relative(scanPath, service.dir), err);
15013
+ recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
14282
15014
  continue;
14283
15015
  }
14284
15016
  if (!read) continue;
@@ -14288,7 +15020,7 @@ async function addRailwayServices(graph, services, scanPath) {
14288
15020
  }
14289
15021
  const anchorId = service.node.id;
14290
15022
  const { config, relFile, raw } = read;
14291
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, relFile)));
15023
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
14292
15024
  const add = (edgeType, kind, name) => {
14293
15025
  if (!name) return;
14294
15026
  const result = emitPlatformResourceEdge(
@@ -14304,24 +15036,24 @@ async function addRailwayServices(graph, services, scanPath) {
14304
15036
  nodesAdded += result.nodesAdded;
14305
15037
  edgesAdded += result.edgesAdded;
14306
15038
  };
14307
- add(import_types51.EdgeType.RUNS_ON, "railway", "railway");
14308
- add(import_types51.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14309
- add(import_types51.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15039
+ add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15040
+ add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15041
+ add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14310
15042
  }
14311
15043
  return { nodesAdded, edgesAdded };
14312
15044
  }
14313
15045
 
14314
15046
  // src/extract/infra/supabase.ts
14315
15047
  init_cjs_shims();
14316
- var import_node_fs30 = require("fs");
14317
- var import_node_path62 = __toESM(require("path"), 1);
15048
+ var import_node_fs31 = require("fs");
15049
+ var import_node_path64 = __toESM(require("path"), 1);
14318
15050
  var import_smol_toml5 = require("smol-toml");
14319
- var import_types52 = require("@neat.is/types");
15051
+ var import_types53 = require("@neat.is/types");
14320
15052
  async function readSupabaseConfig(dir) {
14321
- const relFile = import_node_path62.default.join("supabase", "config.toml");
14322
- const abs = import_node_path62.default.join(dir, relFile);
15053
+ const relFile = import_node_path64.default.join("supabase", "config.toml");
15054
+ const abs = import_node_path64.default.join(dir, relFile);
14323
15055
  if (!await exists(abs)) return null;
14324
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15056
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14325
15057
  const config = (0, import_smol_toml5.parse)(raw);
14326
15058
  return { config, relFile, raw };
14327
15059
  }
@@ -14333,7 +15065,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14333
15065
  try {
14334
15066
  read = await readSupabaseConfig(service.dir);
14335
15067
  } catch (err) {
14336
- recordExtractionError("infra supabase", import_node_path62.default.relative(scanPath, service.dir), err);
15068
+ recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
14337
15069
  continue;
14338
15070
  }
14339
15071
  if (!read) continue;
@@ -14348,7 +15080,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14348
15080
  });
14349
15081
  }
14350
15082
  const anchorId = service.node.id;
14351
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15083
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14352
15084
  const add = (edgeType, kind, name) => {
14353
15085
  if (!name) return;
14354
15086
  const result = emitPlatformResourceEdge(
@@ -14364,10 +15096,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14364
15096
  nodesAdded += result.nodesAdded;
14365
15097
  edgesAdded += result.edgesAdded;
14366
15098
  };
14367
- add(import_types52.EdgeType.RUNS_ON, "supabase", "supabase");
14368
- for (const fn of Object.keys(config.functions ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "supabase-function", fn);
14369
- if (config.storage) add(import_types52.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14370
- if (config.auth) add(import_types52.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15099
+ add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15100
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15101
+ if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15102
+ if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
14371
15103
  }
14372
15104
  return { nodesAdded, edgesAdded };
14373
15105
  }
@@ -14390,14 +15122,14 @@ async function addInfra(graph, scanPath, services) {
14390
15122
 
14391
15123
  // src/extract/zod-shapes.ts
14392
15124
  init_cjs_shims();
14393
- var import_node_path63 = __toESM(require("path"), 1);
14394
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15125
+ var import_node_path65 = __toESM(require("path"), 1);
15126
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14395
15127
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
14396
- var import_types53 = require("@neat.is/types");
15128
+ var import_types54 = require("@neat.is/types");
14397
15129
  var ZOD_IMPORT_RE = /\bzod\b/;
14398
15130
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
14399
15131
  function parserForExt4(ext) {
14400
- const p = new import_tree_sitter16.default();
15132
+ const p = new import_tree_sitter17.default();
14401
15133
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
14402
15134
  return p;
14403
15135
  }
@@ -14485,7 +15217,7 @@ function topLevelSchemas(root) {
14485
15217
  }
14486
15218
  function zodShapesFromFile(file, serviceDir) {
14487
15219
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14488
- const tree = parseSource3(parserForExt4(import_node_path63.default.extname(file.path)), file.content);
15220
+ const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
14489
15221
  const out = [];
14490
15222
  const seen = /* @__PURE__ */ new Set();
14491
15223
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14499,11 +15231,11 @@ function zodShapesFromFile(file, serviceDir) {
14499
15231
  seen.add(name);
14500
15232
  const line = call.startPosition.row + 1;
14501
15233
  out.push({
14502
- infraId: (0, import_types53.infraId)("zod-schema", name),
15234
+ infraId: (0, import_types54.infraId)("zod-schema", name),
14503
15235
  name,
14504
15236
  fields,
14505
15237
  evidence: {
14506
- file: import_node_path63.default.relative(serviceDir, file.path),
15238
+ file: import_node_path65.default.relative(serviceDir, file.path),
14507
15239
  line,
14508
15240
  snippet: snippet(file.content, line)
14509
15241
  }
@@ -14534,7 +15266,7 @@ async function addZodShapes(graph, services) {
14534
15266
  if (!graph.hasNode(shape.infraId)) {
14535
15267
  const node = {
14536
15268
  id: shape.infraId,
14537
- type: import_types53.NodeType.InfraNode,
15269
+ type: import_types54.NodeType.InfraNode,
14538
15270
  name: shape.name,
14539
15271
  provider: "self",
14540
15272
  kind: "zod-schema"
@@ -14544,14 +15276,14 @@ async function addZodShapes(graph, services) {
14544
15276
  }
14545
15277
  if (shape.fields.length > 0) {
14546
15278
  const node = graph.getNodeAttributes(shape.infraId);
14547
- if (node.type === import_types53.NodeType.InfraNode) {
15279
+ if (node.type === import_types54.NodeType.InfraNode) {
14548
15280
  graph.replaceNodeAttributes(shape.infraId, {
14549
15281
  ...node,
14550
15282
  columns: foldColumns(
14551
15283
  node.columns,
14552
15284
  shape.fields,
14553
- import_types53.Provenance.EXTRACTED,
14554
- (0, import_types53.confidenceForExtracted)("structural")
15285
+ import_types54.Provenance.EXTRACTED,
15286
+ (0, import_types54.confidenceForExtracted)("structural")
14555
15287
  )
14556
15288
  });
14557
15289
  }
@@ -14565,15 +15297,15 @@ async function addZodShapes(graph, services) {
14565
15297
  );
14566
15298
  nodesAdded += n;
14567
15299
  edgesAdded += e;
14568
- const edgeId = (0, import_types53.extractedEdgeId)(fileNodeId, shape.infraId, import_types53.EdgeType.CONTAINS);
15300
+ const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
14569
15301
  if (!graph.hasEdge(edgeId)) {
14570
15302
  const edge = {
14571
15303
  id: edgeId,
14572
15304
  source: fileNodeId,
14573
15305
  target: shape.infraId,
14574
- type: import_types53.EdgeType.CONTAINS,
14575
- provenance: import_types53.Provenance.EXTRACTED,
14576
- confidence: (0, import_types53.confidenceForExtracted)("structural"),
15306
+ type: import_types54.EdgeType.CONTAINS,
15307
+ provenance: import_types54.Provenance.EXTRACTED,
15308
+ confidence: (0, import_types54.confidenceForExtracted)("structural"),
14577
15309
  evidence: shape.evidence
14578
15310
  };
14579
15311
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14587,7 +15319,7 @@ async function addZodShapes(graph, services) {
14587
15319
 
14588
15320
  // src/extract/firestore-rules.ts
14589
15321
  init_cjs_shims();
14590
- var import_types54 = require("@neat.is/types");
15322
+ var import_types55 = require("@neat.is/types");
14591
15323
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14592
15324
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14593
15325
  function stripComments(src) {
@@ -14727,7 +15459,7 @@ async function addFirestoreRules(graph, services) {
14727
15459
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14728
15460
  graph.forEachNode((id, attrs) => {
14729
15461
  const node = attrs;
14730
- if (node.type !== import_types54.NodeType.InfraNode) return;
15462
+ if (node.type !== import_types55.NodeType.InfraNode) return;
14731
15463
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14732
15464
  const fields = guards.get(collectionKeyFromName(node.name));
14733
15465
  if (!fields || fields.size === 0) return;
@@ -14740,17 +15472,17 @@ async function addFirestoreRules(graph, services) {
14740
15472
  }
14741
15473
 
14742
15474
  // src/extract/index.ts
14743
- var import_node_path65 = __toESM(require("path"), 1);
15475
+ var import_node_path67 = __toESM(require("path"), 1);
14744
15476
 
14745
15477
  // src/extract/retire.ts
14746
15478
  init_cjs_shims();
14747
- var import_node_fs31 = require("fs");
14748
- var import_node_path64 = __toESM(require("path"), 1);
14749
- var import_types55 = require("@neat.is/types");
15479
+ var import_node_fs32 = require("fs");
15480
+ var import_node_path66 = __toESM(require("path"), 1);
15481
+ var import_types56 = require("@neat.is/types");
14750
15482
  function dropOrphanedFileNodes(graph) {
14751
15483
  const orphans = [];
14752
15484
  graph.forEachNode((id, attrs) => {
14753
- if (attrs.type !== import_types55.NodeType.FileNode) return;
15485
+ if (attrs.type !== import_types56.NodeType.FileNode) return;
14754
15486
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14755
15487
  orphans.push(id);
14756
15488
  }
@@ -14763,14 +15495,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14763
15495
  const bases = [scanPath, ...serviceDirs];
14764
15496
  graph.forEachEdge((id, attrs) => {
14765
15497
  const edge = attrs;
14766
- if (edge.provenance !== import_types55.Provenance.EXTRACTED) return;
15498
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
14767
15499
  const evidenceFile = edge.evidence?.file;
14768
15500
  if (!evidenceFile) return;
14769
- if (import_node_path64.default.isAbsolute(evidenceFile)) {
14770
- if (!(0, import_node_fs31.existsSync)(evidenceFile)) toDrop.push(id);
15501
+ if (import_node_path66.default.isAbsolute(evidenceFile)) {
15502
+ if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
14771
15503
  return;
14772
15504
  }
14773
- const found = bases.some((base) => (0, import_node_fs31.existsSync)(import_node_path64.default.join(base, evidenceFile)));
15505
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
14774
15506
  if (!found) toDrop.push(id);
14775
15507
  });
14776
15508
  for (const id of toDrop) graph.dropEdge(id);
@@ -14827,7 +15559,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14827
15559
  }
14828
15560
  const droppedEntries = drainDroppedExtracted();
14829
15561
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14830
- const rejectedPath = import_node_path65.default.join(import_node_path65.default.dirname(opts.errorsPath), "rejected.ndjson");
15562
+ const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
14831
15563
  try {
14832
15564
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14833
15565
  } catch (err) {
@@ -14861,9 +15593,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14861
15593
 
14862
15594
  // src/persist.ts
14863
15595
  init_cjs_shims();
14864
- var import_node_fs32 = require("fs");
14865
- var import_node_path66 = __toESM(require("path"), 1);
14866
- var import_types56 = require("@neat.is/types");
15596
+ var import_node_fs33 = require("fs");
15597
+ var import_node_path68 = __toESM(require("path"), 1);
15598
+ var import_types57 = require("@neat.is/types");
14867
15599
  var SCHEMA_VERSION = 7;
14868
15600
  function migrateV1ToV2(payload) {
14869
15601
  const nodes = payload.graph.nodes;
@@ -14887,7 +15619,7 @@ function migrateV5ToV6(payload) {
14887
15619
  if (Array.isArray(nodes)) {
14888
15620
  for (const node of nodes) {
14889
15621
  const attrs = node.attributes;
14890
- if (!attrs || attrs.type !== import_types56.NodeType.InfraNode) continue;
15622
+ if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
14891
15623
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14892
15624
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14893
15625
  }
@@ -14903,12 +15635,12 @@ function migrateV2ToV3(payload) {
14903
15635
  for (const edge of edges) {
14904
15636
  const attrs = edge.attributes;
14905
15637
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
14906
- attrs.provenance = import_types56.Provenance.OBSERVED;
15638
+ attrs.provenance = import_types57.Provenance.OBSERVED;
14907
15639
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
14908
15640
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
14909
15641
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
14910
15642
  if (type && source && target) {
14911
- const newId = (0, import_types56.observedEdgeId)(source, target, type);
15643
+ const newId = (0, import_types57.observedEdgeId)(source, target, type);
14912
15644
  attrs.id = newId;
14913
15645
  if (edge.key) edge.key = newId;
14914
15646
  }
@@ -14917,7 +15649,7 @@ function migrateV2ToV3(payload) {
14917
15649
  return { ...payload, schemaVersion: 3 };
14918
15650
  }
14919
15651
  async function ensureDir(filePath) {
14920
- await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(filePath), { recursive: true });
15652
+ await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
14921
15653
  }
14922
15654
  async function saveGraphToDisk(graph, outPath) {
14923
15655
  await ensureDir(outPath);
@@ -14927,13 +15659,13 @@ async function saveGraphToDisk(graph, outPath) {
14927
15659
  graph: graph.export()
14928
15660
  };
14929
15661
  const tmp = `${outPath}.tmp`;
14930
- await import_node_fs32.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
14931
- await import_node_fs32.promises.rename(tmp, outPath);
15662
+ await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15663
+ await import_node_fs33.promises.rename(tmp, outPath);
14932
15664
  }
14933
15665
  async function loadGraphFromDisk(graph, outPath) {
14934
15666
  let raw;
14935
15667
  try {
14936
- raw = await import_node_fs32.promises.readFile(outPath, "utf8");
15668
+ raw = await import_node_fs33.promises.readFile(outPath, "utf8");
14937
15669
  } catch (err) {
14938
15670
  if (err.code === "ENOENT") return;
14939
15671
  throw err;
@@ -15007,23 +15739,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
15007
15739
 
15008
15740
  // src/projects.ts
15009
15741
  init_cjs_shims();
15010
- var import_node_path67 = __toESM(require("path"), 1);
15742
+ var import_node_path69 = __toESM(require("path"), 1);
15011
15743
  function pathsForProject(project, baseDir) {
15012
15744
  if (project === DEFAULT_PROJECT) {
15013
15745
  return {
15014
- snapshotPath: import_node_path67.default.join(baseDir, "graph.json"),
15015
- errorsPath: import_node_path67.default.join(baseDir, "errors.ndjson"),
15016
- staleEventsPath: import_node_path67.default.join(baseDir, "stale-events.ndjson"),
15017
- embeddingsCachePath: import_node_path67.default.join(baseDir, "embeddings.json"),
15018
- policyViolationsPath: import_node_path67.default.join(baseDir, "policy-violations.ndjson")
15746
+ snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
15747
+ errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
15748
+ staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
15749
+ embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
15750
+ policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
15019
15751
  };
15020
15752
  }
15021
15753
  return {
15022
- snapshotPath: import_node_path67.default.join(baseDir, `${project}.json`),
15023
- errorsPath: import_node_path67.default.join(baseDir, `errors.${project}.ndjson`),
15024
- staleEventsPath: import_node_path67.default.join(baseDir, `stale-events.${project}.ndjson`),
15025
- embeddingsCachePath: import_node_path67.default.join(baseDir, `embeddings.${project}.json`),
15026
- policyViolationsPath: import_node_path67.default.join(baseDir, `policy-violations.${project}.ndjson`)
15754
+ snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
15755
+ errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
15756
+ staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
15757
+ embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
15758
+ policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
15027
15759
  };
15028
15760
  }
15029
15761
  var Projects = class {
@@ -15061,19 +15793,19 @@ var Projects = class {
15061
15793
  init_cjs_shims();
15062
15794
  var import_fastify2 = __toESM(require("fastify"), 1);
15063
15795
  var import_cors = __toESM(require("@fastify/cors"), 1);
15064
- var import_types91 = require("@neat.is/types");
15796
+ var import_types92 = require("@neat.is/types");
15065
15797
 
15066
15798
  // src/extend/index.ts
15067
15799
  init_cjs_shims();
15068
- var import_node_fs34 = require("fs");
15069
- var import_node_path69 = __toESM(require("path"), 1);
15800
+ var import_node_fs35 = require("fs");
15801
+ var import_node_path71 = __toESM(require("path"), 1);
15070
15802
  var import_node_os2 = __toESM(require("os"), 1);
15071
15803
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
15072
15804
 
15073
15805
  // src/installers/package-manager.ts
15074
15806
  init_cjs_shims();
15075
- var import_node_fs33 = require("fs");
15076
- var import_node_path68 = __toESM(require("path"), 1);
15807
+ var import_node_fs34 = require("fs");
15808
+ var import_node_path70 = __toESM(require("path"), 1);
15077
15809
  var import_node_child_process = require("child_process");
15078
15810
  var LOCKFILE_PRIORITY = [
15079
15811
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -15088,29 +15820,29 @@ var LOCKFILE_PRIORITY = [
15088
15820
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
15089
15821
  async function exists2(p) {
15090
15822
  try {
15091
- await import_node_fs33.promises.access(p);
15823
+ await import_node_fs34.promises.access(p);
15092
15824
  return true;
15093
15825
  } catch {
15094
15826
  return false;
15095
15827
  }
15096
15828
  }
15097
15829
  async function detectPackageManager(serviceDir) {
15098
- let dir = import_node_path68.default.resolve(serviceDir);
15830
+ let dir = import_node_path70.default.resolve(serviceDir);
15099
15831
  const stops = /* @__PURE__ */ new Set();
15100
15832
  for (let i = 0; i < 64; i++) {
15101
15833
  if (stops.has(dir)) break;
15102
15834
  stops.add(dir);
15103
15835
  for (const candidate of LOCKFILE_PRIORITY) {
15104
- const lockPath = import_node_path68.default.join(dir, candidate.lockfile);
15836
+ const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
15105
15837
  if (await exists2(lockPath)) {
15106
15838
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
15107
15839
  }
15108
15840
  }
15109
- const parent = import_node_path68.default.dirname(dir);
15841
+ const parent = import_node_path70.default.dirname(dir);
15110
15842
  if (parent === dir) break;
15111
15843
  dir = parent;
15112
15844
  }
15113
- return { pm: "npm", cwd: import_node_path68.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15845
+ return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15114
15846
  }
15115
15847
  async function runPackageManagerInstall(cmd) {
15116
15848
  return new Promise((resolve) => {
@@ -15152,15 +15884,15 @@ ${err.message}`
15152
15884
  // src/extend/index.ts
15153
15885
  async function fileExists2(p) {
15154
15886
  try {
15155
- await import_node_fs34.promises.access(p);
15887
+ await import_node_fs35.promises.access(p);
15156
15888
  return true;
15157
15889
  } catch {
15158
15890
  return false;
15159
15891
  }
15160
15892
  }
15161
15893
  async function readPackageJson(scanPath) {
15162
- const pkgPath = import_node_path69.default.join(scanPath, "package.json");
15163
- const raw = await import_node_fs34.promises.readFile(pkgPath, "utf8");
15894
+ const pkgPath = import_node_path71.default.join(scanPath, "package.json");
15895
+ const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
15164
15896
  return JSON.parse(raw);
15165
15897
  }
15166
15898
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -15173,27 +15905,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
15173
15905
  ]);
15174
15906
  async function findHookFiles(scanPath) {
15175
15907
  const found = [];
15176
- const walk9 = async (dir) => {
15177
- const entries = await import_node_fs34.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15908
+ const walk10 = async (dir) => {
15909
+ const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15178
15910
  for (const entry2 of entries) {
15179
15911
  if (entry2.isDirectory()) {
15180
15912
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
15181
- await walk9(import_node_path69.default.join(dir, entry2.name));
15913
+ await walk10(import_node_path71.default.join(dir, entry2.name));
15182
15914
  } else if (entry2.isFile()) {
15183
15915
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
15184
- const rel = import_node_path69.default.relative(scanPath, import_node_path69.default.join(dir, entry2.name));
15185
- found.push(rel.split(import_node_path69.default.sep).join("/"));
15916
+ const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry2.name));
15917
+ found.push(rel.split(import_node_path71.default.sep).join("/"));
15186
15918
  }
15187
15919
  }
15188
15920
  }
15189
15921
  };
15190
- await walk9(scanPath);
15922
+ await walk10(scanPath);
15191
15923
  return found.sort();
15192
15924
  }
15193
15925
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15194
15926
  let fallback = null;
15195
15927
  for (const file of hookFiles) {
15196
- const content = await import_node_fs34.promises.readFile(import_node_path69.default.join(scanPath, file), "utf8");
15928
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
15197
15929
  const patched = splicedContent(content, snippet2);
15198
15930
  if (patched !== null) return { file, content, patched };
15199
15931
  if (fallback === null) fallback = { file, content };
@@ -15201,12 +15933,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15201
15933
  return { file: fallback.file, content: fallback.content, patched: null };
15202
15934
  }
15203
15935
  function extendLogPath() {
15204
- return process.env.NEAT_EXTEND_LOG ?? import_node_path69.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15936
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15205
15937
  }
15206
15938
  async function appendExtendLog(entry2) {
15207
15939
  const logPath = extendLogPath();
15208
- await import_node_fs34.promises.mkdir(import_node_path69.default.dirname(logPath), { recursive: true });
15209
- await import_node_fs34.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
15940
+ await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
15941
+ await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
15210
15942
  }
15211
15943
  function splicedContent(fileContent, snippet2) {
15212
15944
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -15264,7 +15996,7 @@ function lookupInstrumentation(library, installedVersion) {
15264
15996
  }
15265
15997
  async function describeProjectInstrumentation(ctx) {
15266
15998
  const hookFiles = await findHookFiles(ctx.scanPath);
15267
- const envNeat = await fileExists2(import_node_path69.default.join(ctx.scanPath, ".env.neat"));
15999
+ const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
15268
16000
  const registryInstrPackages = new Set(
15269
16001
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
15270
16002
  );
@@ -15286,7 +16018,7 @@ async function applyExtension(ctx, args, options) {
15286
16018
  );
15287
16019
  }
15288
16020
  for (const file of hookFiles) {
15289
- const content = await import_node_fs34.promises.readFile(import_node_path69.default.join(ctx.scanPath, file), "utf8");
16021
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
15290
16022
  if (content.includes(args.registration_snippet)) {
15291
16023
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
15292
16024
  }
@@ -15298,18 +16030,18 @@ async function applyExtension(ctx, args, options) {
15298
16030
  );
15299
16031
  }
15300
16032
  const primaryFile = primary.file;
15301
- const primaryPath = import_node_path69.default.join(ctx.scanPath, primaryFile);
16033
+ const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
15302
16034
  const filesTouched = [];
15303
16035
  const depsAdded = [];
15304
- const pkgPath = import_node_path69.default.join(ctx.scanPath, "package.json");
16036
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
15305
16037
  const pkg = await readPackageJson(ctx.scanPath);
15306
16038
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
15307
16039
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
15308
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16040
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15309
16041
  filesTouched.push("package.json");
15310
16042
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
15311
16043
  }
15312
- await import_node_fs34.promises.writeFile(primaryPath, primary.patched, "utf8");
16044
+ await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
15313
16045
  filesTouched.push(primaryFile);
15314
16046
  const cmd = await detectPackageManager(ctx.scanPath);
15315
16047
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -15340,7 +16072,7 @@ async function dryRunExtension(ctx, args) {
15340
16072
  };
15341
16073
  }
15342
16074
  for (const file of hookFiles) {
15343
- const content = await import_node_fs34.promises.readFile(import_node_path69.default.join(ctx.scanPath, file), "utf8");
16075
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
15344
16076
  if (content.includes(args.registration_snippet)) {
15345
16077
  return {
15346
16078
  library: args.library,
@@ -15375,28 +16107,28 @@ async function rollbackExtension(ctx, args) {
15375
16107
  if (!await fileExists2(logPath)) {
15376
16108
  return { undone: false, message: "no apply found for library" };
15377
16109
  }
15378
- const raw = await import_node_fs34.promises.readFile(logPath, "utf8");
16110
+ const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
15379
16111
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
15380
16112
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
15381
16113
  if (!match) {
15382
16114
  return { undone: false, message: "no apply found for library" };
15383
16115
  }
15384
- const pkgPath = import_node_path69.default.join(ctx.scanPath, "package.json");
16116
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
15385
16117
  if (await fileExists2(pkgPath)) {
15386
16118
  const pkg = await readPackageJson(ctx.scanPath);
15387
16119
  if (pkg.dependencies?.[match.instrumentation_package]) {
15388
16120
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
15389
16121
  pkg.dependencies = rest;
15390
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16122
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15391
16123
  }
15392
16124
  }
15393
16125
  const hookFiles = await findHookFiles(ctx.scanPath);
15394
16126
  for (const file of hookFiles) {
15395
- const filePath = import_node_path69.default.join(ctx.scanPath, file);
15396
- const content = await import_node_fs34.promises.readFile(filePath, "utf8");
16127
+ const filePath = import_node_path71.default.join(ctx.scanPath, file);
16128
+ const content = await import_node_fs35.promises.readFile(filePath, "utf8");
15397
16129
  if (content.includes(match.registration_snippet)) {
15398
16130
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
15399
- await import_node_fs34.promises.writeFile(filePath, filtered, "utf8");
16131
+ await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
15400
16132
  break;
15401
16133
  }
15402
16134
  }
@@ -15408,39 +16140,39 @@ async function rollbackExtension(ctx, args) {
15408
16140
 
15409
16141
  // src/divergences.ts
15410
16142
  init_cjs_shims();
15411
- var import_types57 = require("@neat.is/types");
16143
+ var import_types58 = require("@neat.is/types");
15412
16144
  function bucketKey(source, target, type) {
15413
16145
  return `${type}|${source}|${target}`;
15414
16146
  }
15415
16147
  function bucketSourceFor(graph, edge) {
15416
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) return edge.source;
15417
- const parsed = (0, import_types57.parseFileId)(edge.source);
16148
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16149
+ const parsed = (0, import_types58.parseFileId)(edge.source);
15418
16150
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
15419
16151
  const target = graph.getNodeAttributes(edge.target);
15420
- if (target.type !== import_types57.NodeType.DatabaseNode) return edge.source;
15421
- return (0, import_types57.serviceId)(parsed.service);
16152
+ if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16153
+ return (0, import_types58.serviceId)(parsed.service);
15422
16154
  }
15423
16155
  function bucketEdges(graph) {
15424
16156
  const buckets2 = /* @__PURE__ */ new Map();
15425
16157
  graph.forEachEdge((id, attrs) => {
15426
16158
  const e = attrs;
15427
- const parsed = (0, import_types57.parseEdgeId)(id);
16159
+ const parsed = (0, import_types58.parseEdgeId)(id);
15428
16160
  const provenance = parsed?.provenance ?? e.provenance;
15429
16161
  const source = bucketSourceFor(graph, e);
15430
16162
  const key = bucketKey(source, e.target, e.type);
15431
16163
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
15432
16164
  switch (provenance) {
15433
- case import_types57.Provenance.EXTRACTED:
16165
+ case import_types58.Provenance.EXTRACTED:
15434
16166
  cur.extracted = e;
15435
16167
  break;
15436
- case import_types57.Provenance.OBSERVED:
16168
+ case import_types58.Provenance.OBSERVED:
15437
16169
  cur.observed = e;
15438
16170
  break;
15439
- case import_types57.Provenance.INFERRED:
16171
+ case import_types58.Provenance.INFERRED:
15440
16172
  cur.inferred = e;
15441
16173
  break;
15442
16174
  default:
15443
- if (e.provenance === import_types57.Provenance.STALE) cur.stale = e;
16175
+ if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
15444
16176
  }
15445
16177
  buckets2.set(key, cur);
15446
16178
  });
@@ -15449,22 +16181,22 @@ function bucketEdges(graph) {
15449
16181
  function nodeIsFrontier(graph, nodeId) {
15450
16182
  if (!graph.hasNode(nodeId)) return false;
15451
16183
  const attrs = graph.getNodeAttributes(nodeId);
15452
- return attrs.type === import_types57.NodeType.FrontierNode;
16184
+ return attrs.type === import_types58.NodeType.FrontierNode;
15453
16185
  }
15454
16186
  function nodeIsWebsocketChannel(graph, nodeId) {
15455
16187
  if (!graph.hasNode(nodeId)) return false;
15456
16188
  const attrs = graph.getNodeAttributes(nodeId);
15457
- return attrs.type === import_types57.NodeType.WebSocketChannelNode;
16189
+ return attrs.type === import_types58.NodeType.WebSocketChannelNode;
15458
16190
  }
15459
16191
  function nodeIsServerAction(graph, nodeId) {
15460
16192
  if (!graph.hasNode(nodeId)) return false;
15461
16193
  const attrs = graph.getNodeAttributes(nodeId);
15462
- return attrs.type === import_types57.NodeType.ServerActionNode;
16194
+ return attrs.type === import_types58.NodeType.ServerActionNode;
15463
16195
  }
15464
16196
  function nodeIsSymbol(graph, nodeId) {
15465
16197
  if (!graph.hasNode(nodeId)) return false;
15466
16198
  const attrs = graph.getNodeAttributes(nodeId);
15467
- return attrs.type === import_types57.NodeType.SymbolNode;
16199
+ return attrs.type === import_types58.NodeType.SymbolNode;
15468
16200
  }
15469
16201
  function clampConfidence(n) {
15470
16202
  if (!Number.isFinite(n)) return 0;
@@ -15484,14 +16216,14 @@ function gradedConfidence(edge) {
15484
16216
  return clampConfidence(confidenceForEdge(edge));
15485
16217
  }
15486
16218
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
15487
- import_types57.EdgeType.CALLS,
15488
- import_types57.EdgeType.CONNECTS_TO,
15489
- import_types57.EdgeType.PUBLISHES_TO,
15490
- import_types57.EdgeType.CONSUMES_FROM
16219
+ import_types58.EdgeType.CALLS,
16220
+ import_types58.EdgeType.CONNECTS_TO,
16221
+ import_types58.EdgeType.PUBLISHES_TO,
16222
+ import_types58.EdgeType.CONSUMES_FROM
15491
16223
  ]);
15492
16224
  function detectMissingDivergences(graph, bucket) {
15493
16225
  const out = [];
15494
- if (bucket.type === import_types57.EdgeType.CONTAINS) return out;
16226
+ if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
15495
16227
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
15496
16228
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
15497
16229
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -15533,7 +16265,7 @@ function declaredHostFor(svc) {
15533
16265
  function hasExtractedConfiguredBy(graph, svcId) {
15534
16266
  for (const edgeId of graph.outboundEdges(svcId)) {
15535
16267
  const e = graph.getEdgeAttributes(edgeId);
15536
- if (e.type === import_types57.EdgeType.CONFIGURED_BY && e.provenance === import_types57.Provenance.EXTRACTED) {
16268
+ if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
15537
16269
  return true;
15538
16270
  }
15539
16271
  }
@@ -15546,10 +16278,10 @@ function detectHostMismatch(graph, svcId, svc) {
15546
16278
  const out = [];
15547
16279
  for (const edgeId of graph.outboundEdges(svcId)) {
15548
16280
  const edge = graph.getEdgeAttributes(edgeId);
15549
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15550
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16281
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16282
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15551
16283
  const target = graph.getNodeAttributes(edge.target);
15552
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16284
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15553
16285
  const observedHost = target.host?.trim();
15554
16286
  if (!observedHost) continue;
15555
16287
  if (observedHost === declaredHost) continue;
@@ -15571,10 +16303,10 @@ function detectCompatDivergences(graph, svcId, svc) {
15571
16303
  const deps = svc.dependencies ?? {};
15572
16304
  for (const edgeId of graph.outboundEdges(svcId)) {
15573
16305
  const edge = graph.getEdgeAttributes(edgeId);
15574
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15575
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16306
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16307
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15576
16308
  const target = graph.getNodeAttributes(edge.target);
15577
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16309
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15578
16310
  for (const pair of compatPairs()) {
15579
16311
  if (pair.engine !== target.engine) continue;
15580
16312
  const declared = deps[pair.driver];
@@ -15671,7 +16403,7 @@ function suppressHostMismatchHalves(all) {
15671
16403
  for (const d of all) {
15672
16404
  if (d.type !== "host-mismatch") continue;
15673
16405
  observedHalf.add(`${d.source}->${d.target}`);
15674
- declaredHalf.add((0, import_types57.databaseId)(d.extractedHost));
16406
+ declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
15675
16407
  }
15676
16408
  if (observedHalf.size === 0) return all;
15677
16409
  return all.filter((d) => {
@@ -15690,13 +16422,13 @@ function computeDivergences(graph, opts = {}) {
15690
16422
  }
15691
16423
  graph.forEachNode((nodeId, attrs) => {
15692
16424
  const n = attrs;
15693
- if (n.type === import_types57.NodeType.ServiceNode) {
16425
+ if (n.type === import_types58.NodeType.ServiceNode) {
15694
16426
  const svc = n;
15695
16427
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
15696
16428
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
15697
16429
  return;
15698
16430
  }
15699
- if (n.type === import_types57.NodeType.InfraNode && n.kind === "sql-table") {
16431
+ if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
15700
16432
  for (const d of detectColumnDrift(n)) all.push(d);
15701
16433
  }
15702
16434
  });
@@ -15732,7 +16464,7 @@ function computeDivergences(graph, opts = {}) {
15732
16464
  const bc = "column" in b && b.column ? b.column : "";
15733
16465
  return ac.localeCompare(bc);
15734
16466
  });
15735
- return import_types57.DivergenceResultSchema.parse({
16467
+ return import_types58.DivergenceResultSchema.parse({
15736
16468
  divergences: filtered,
15737
16469
  totalAffected: filtered.length,
15738
16470
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -15786,7 +16518,7 @@ function queryLogEntries(opts) {
15786
16518
 
15787
16519
  // src/diff.ts
15788
16520
  init_cjs_shims();
15789
- var import_node_fs35 = require("fs");
16521
+ var import_node_fs36 = require("fs");
15790
16522
  async function loadSnapshotForDiff(target) {
15791
16523
  if (/^https?:\/\//i.test(target)) {
15792
16524
  const res = await fetch(target);
@@ -15795,7 +16527,7 @@ async function loadSnapshotForDiff(target) {
15795
16527
  }
15796
16528
  return await res.json();
15797
16529
  }
15798
- const raw = await import_node_fs35.promises.readFile(target, "utf8");
16530
+ const raw = await import_node_fs36.promises.readFile(target, "utf8");
15799
16531
  return JSON.parse(raw);
15800
16532
  }
15801
16533
  function indexEntries(entries) {
@@ -15863,28 +16595,28 @@ function canonicalJson(value) {
15863
16595
 
15864
16596
  // src/registry.ts
15865
16597
  init_cjs_shims();
15866
- var import_node_fs36 = require("fs");
16598
+ var import_node_fs37 = require("fs");
15867
16599
  var import_node_os3 = __toESM(require("os"), 1);
15868
- var import_node_path70 = __toESM(require("path"), 1);
15869
- var import_types58 = require("@neat.is/types");
16600
+ var import_node_path72 = __toESM(require("path"), 1);
16601
+ var import_types59 = require("@neat.is/types");
15870
16602
  var LOCK_TIMEOUT_MS = 5e3;
15871
16603
  var LOCK_RETRY_MS = 50;
15872
16604
  function neatHome() {
15873
16605
  const override = process.env.NEAT_HOME;
15874
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15875
- return import_node_path70.default.join(import_node_os3.default.homedir(), ".neat");
16606
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16607
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15876
16608
  }
15877
16609
  function registryPath() {
15878
- return import_node_path70.default.join(neatHome(), "projects.json");
16610
+ return import_node_path72.default.join(neatHome(), "projects.json");
15879
16611
  }
15880
16612
  function registryLockPath() {
15881
- return import_node_path70.default.join(neatHome(), "projects.json.lock");
16613
+ return import_node_path72.default.join(neatHome(), "projects.json.lock");
15882
16614
  }
15883
16615
  function daemonPidPath() {
15884
- return import_node_path70.default.join(neatHome(), "neatd.pid");
16616
+ return import_node_path72.default.join(neatHome(), "neatd.pid");
15885
16617
  }
15886
16618
  function daemonsDir() {
15887
- return import_node_path70.default.join(neatHome(), "daemons");
16619
+ return import_node_path72.default.join(neatHome(), "daemons");
15888
16620
  }
15889
16621
  function isFiniteInt(v) {
15890
16622
  return typeof v === "number" && Number.isFinite(v);
@@ -15917,7 +16649,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15917
16649
  const dir = daemonsDir();
15918
16650
  let names;
15919
16651
  try {
15920
- names = await import_node_fs36.promises.readdir(dir);
16652
+ names = await import_node_fs37.promises.readdir(dir);
15921
16653
  } catch (err) {
15922
16654
  if (err.code === "ENOENT") return [];
15923
16655
  throw err;
@@ -15925,10 +16657,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15925
16657
  const out = [];
15926
16658
  for (const name of names) {
15927
16659
  if (!name.endsWith(".json")) continue;
15928
- const file = import_node_path70.default.join(dir, name);
16660
+ const file = import_node_path72.default.join(dir, name);
15929
16661
  let raw;
15930
16662
  try {
15931
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16663
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15932
16664
  } catch {
15933
16665
  continue;
15934
16666
  }
@@ -15954,7 +16686,7 @@ function isPidAliveDefault(pid) {
15954
16686
  }
15955
16687
  async function readPidFile(file) {
15956
16688
  try {
15957
- const raw = await import_node_fs36.promises.readFile(file, "utf8");
16689
+ const raw = await import_node_fs37.promises.readFile(file, "utf8");
15958
16690
  const pid = Number.parseInt(raw.trim(), 10);
15959
16691
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
15960
16692
  } catch {
@@ -16002,24 +16734,24 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
16002
16734
  }
16003
16735
  }
16004
16736
  async function writeAtomically(target, contents) {
16005
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(target), { recursive: true });
16737
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
16006
16738
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
16007
- const fd = await import_node_fs36.promises.open(tmp, "w");
16739
+ const fd = await import_node_fs37.promises.open(tmp, "w");
16008
16740
  try {
16009
16741
  await fd.writeFile(contents, "utf8");
16010
16742
  await fd.sync();
16011
16743
  } finally {
16012
16744
  await fd.close();
16013
16745
  }
16014
- await import_node_fs36.promises.rename(tmp, target);
16746
+ await import_node_fs37.promises.rename(tmp, target);
16015
16747
  }
16016
16748
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
16017
16749
  const deadline = Date.now() + timeoutMs;
16018
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(lockPath), { recursive: true });
16750
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
16019
16751
  let probedHolder = false;
16020
16752
  while (true) {
16021
16753
  try {
16022
- const fd = await import_node_fs36.promises.open(lockPath, "wx");
16754
+ const fd = await import_node_fs37.promises.open(lockPath, "wx");
16023
16755
  try {
16024
16756
  await fd.writeFile(`${process.pid}
16025
16757
  `, "utf8");
@@ -16044,7 +16776,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
16044
16776
  }
16045
16777
  }
16046
16778
  async function releaseLock(lockPath) {
16047
- await import_node_fs36.promises.unlink(lockPath).catch(() => {
16779
+ await import_node_fs37.promises.unlink(lockPath).catch(() => {
16048
16780
  });
16049
16781
  }
16050
16782
  async function withLock(fn) {
@@ -16060,7 +16792,7 @@ async function readRegistry() {
16060
16792
  const file = registryPath();
16061
16793
  let raw;
16062
16794
  try {
16063
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16795
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16064
16796
  } catch (err) {
16065
16797
  if (err.code === "ENOENT") {
16066
16798
  return { version: 1, projects: [] };
@@ -16068,10 +16800,10 @@ async function readRegistry() {
16068
16800
  throw err;
16069
16801
  }
16070
16802
  const parsed = JSON.parse(raw);
16071
- return import_types58.RegistryFileSchema.parse(parsed);
16803
+ return import_types59.RegistryFileSchema.parse(parsed);
16072
16804
  }
16073
16805
  async function writeRegistry(reg) {
16074
- const validated = import_types58.RegistryFileSchema.parse(reg);
16806
+ const validated = import_types59.RegistryFileSchema.parse(reg);
16075
16807
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
16076
16808
  }
16077
16809
  async function getProject(name) {
@@ -16115,7 +16847,7 @@ function pruneTtlMs() {
16115
16847
  }
16116
16848
  async function statPathStatus(p) {
16117
16849
  try {
16118
- const stat = await import_node_fs36.promises.stat(p);
16850
+ const stat = await import_node_fs37.promises.stat(p);
16119
16851
  return stat.isDirectory() ? "present" : "unknown";
16120
16852
  } catch (err) {
16121
16853
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -16216,8 +16948,8 @@ init_auth();
16216
16948
  // src/connectors-config.ts
16217
16949
  init_cjs_shims();
16218
16950
  var import_node_os4 = __toESM(require("os"), 1);
16219
- var import_node_path71 = __toESM(require("path"), 1);
16220
- var import_node_fs37 = require("fs");
16951
+ var import_node_path73 = __toESM(require("path"), 1);
16952
+ var import_node_fs38 = require("fs");
16221
16953
  var CONNECTORS_CONFIG_VERSION = 1;
16222
16954
  var EnvRefUnsetError = class extends Error {
16223
16955
  ref;
@@ -16231,17 +16963,17 @@ var EnvRefUnsetError = class extends Error {
16231
16963
  };
16232
16964
  function neatHome2() {
16233
16965
  const override = process.env.NEAT_HOME;
16234
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
16235
- return import_node_path71.default.join(import_node_os4.default.homedir(), ".neat");
16966
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
16967
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16236
16968
  }
16237
16969
  function connectorsConfigPath(home = neatHome2()) {
16238
- return import_node_path71.default.join(home, "connectors.json");
16970
+ return import_node_path73.default.join(home, "connectors.json");
16239
16971
  }
16240
16972
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16241
16973
  async function warnIfModeLooserThan0600(file) {
16242
16974
  if (process.platform === "win32") return;
16243
16975
  try {
16244
- const stat = await import_node_fs37.promises.stat(file);
16976
+ const stat = await import_node_fs38.promises.stat(file);
16245
16977
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16246
16978
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16247
16979
  console.warn(
@@ -16255,7 +16987,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16255
16987
  const file = connectorsConfigPath(home);
16256
16988
  let raw;
16257
16989
  try {
16258
- raw = await import_node_fs37.promises.readFile(file, "utf8");
16990
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16259
16991
  } catch (err) {
16260
16992
  if (err.code === "ENOENT") {
16261
16993
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16422,15 +17154,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16422
17154
 
16423
17155
  // src/connectors/index.ts
16424
17156
  init_cjs_shims();
16425
- var import_types59 = require("@neat.is/types");
17157
+ var import_types60 = require("@neat.is/types");
16426
17158
  var NO_ENV = "unknown";
16427
17159
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16428
17160
  if (!graph.hasNode(targetNodeId)) return void 0;
16429
17161
  const sites = [];
16430
17162
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16431
17163
  const edge = graph.getEdgeAttributes(edgeId);
16432
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16433
- const parsed = (0, import_types59.parseFileId)(edge.source);
17164
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
17165
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16434
17166
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16435
17167
  const site = { relPath: edge.evidence.file };
16436
17168
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16441,7 +17173,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16441
17173
  function routeCallSiteFor(graph, targetNodeId) {
16442
17174
  if (!graph.hasNode(targetNodeId)) return void 0;
16443
17175
  const attrs = graph.getNodeAttributes(targetNodeId);
16444
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
17176
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16445
17177
  const site = { relPath: attrs.path };
16446
17178
  if (attrs.line !== void 0) site.line = attrs.line;
16447
17179
  return site;
@@ -16939,10 +17671,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
16939
17671
  // src/connectors/supabase/map.ts
16940
17672
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
16941
17673
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
16942
- function targetFromRestPath(path76) {
16943
- const rpcMatch = REST_RPC_PATH_RE.exec(path76);
17674
+ function targetFromRestPath(path78) {
17675
+ const rpcMatch = REST_RPC_PATH_RE.exec(path78);
16944
17676
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
16945
- const tableMatch = REST_TABLE_PATH_RE.exec(path76);
17677
+ const tableMatch = REST_TABLE_PATH_RE.exec(path78);
16946
17678
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
16947
17679
  return null;
16948
17680
  }
@@ -17053,23 +17785,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
17053
17785
 
17054
17786
  // src/connectors/supabase/resolve.ts
17055
17787
  init_cjs_shims();
17056
- var import_types61 = require("@neat.is/types");
17788
+ var import_types62 = require("@neat.is/types");
17057
17789
  function createSupabaseResolveTarget(graph, config) {
17058
17790
  return (signal, _ctx) => {
17059
17791
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
17060
17792
  return null;
17061
17793
  }
17062
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17794
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17063
17795
  if (graph.hasNode(subResourceId)) {
17064
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17796
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17065
17797
  }
17066
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17798
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
17067
17799
  if (graph.hasNode(bareResourceId)) {
17068
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17800
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17069
17801
  }
17070
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17802
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
17071
17803
  if (graph.hasNode(projectLevelId)) {
17072
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17804
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17073
17805
  }
17074
17806
  return null;
17075
17807
  };
@@ -17162,7 +17894,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
17162
17894
 
17163
17895
  // src/connectors/railway/index.ts
17164
17896
  init_cjs_shims();
17165
- var import_types65 = require("@neat.is/types");
17897
+ var import_types66 = require("@neat.is/types");
17166
17898
 
17167
17899
  // src/connectors/railway/client.ts
17168
17900
  init_cjs_shims();
@@ -17313,7 +18045,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17313
18045
  const out = [];
17314
18046
  graph.forEachNode((_id, attrs) => {
17315
18047
  const node = attrs;
17316
- if (node.type !== import_types65.NodeType.RouteNode) return;
18048
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17317
18049
  const route = attrs;
17318
18050
  if (route.service !== serviceName) return;
17319
18051
  out.push({
@@ -17417,12 +18149,12 @@ function createRailwayResolveTarget(config) {
17417
18149
  const serviceName = config.serviceNameById[config.serviceId];
17418
18150
  if (!serviceName) return null;
17419
18151
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17420
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
18152
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17421
18153
  }
17422
18154
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17423
18155
  const peerName = config.serviceNameById[signal.targetName];
17424
18156
  if (!peerName) return null;
17425
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
18157
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17426
18158
  }
17427
18159
  return null;
17428
18160
  };
@@ -17546,9 +18278,9 @@ function parseFirebaseTargetName(targetName) {
17546
18278
  const secondSep = rest.indexOf(FIELD_SEP);
17547
18279
  if (secondSep === -1) return null;
17548
18280
  const method = rest.slice(0, secondSep);
17549
- const path76 = rest.slice(secondSep + 1);
17550
- if (!resourceName || !method || !path76) return null;
17551
- return { resourceName, method, path: path76 };
18281
+ const path78 = rest.slice(secondSep + 1);
18282
+ if (!resourceName || !method || !path78) return null;
18283
+ return { resourceName, method, path: path78 };
17552
18284
  }
17553
18285
  function resourceNameFor(type, labels) {
17554
18286
  if (!labels) return null;
@@ -17586,14 +18318,14 @@ function mapLogEntryToSignal(entry2) {
17586
18318
  if (!req2) return null;
17587
18319
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
17588
18320
  const method = req2.requestMethod.toUpperCase();
17589
- const path76 = pathFromRequestUrl(req2.requestUrl);
17590
- if (path76 === null) return null;
18321
+ const path78 = pathFromRequestUrl(req2.requestUrl);
18322
+ if (path78 === null) return null;
17591
18323
  const timestamp = entry2.timestamp;
17592
18324
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17593
18325
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
17594
18326
  return {
17595
18327
  targetKind: resourceType,
17596
- targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
18328
+ targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
17597
18329
  callCount: 1,
17598
18330
  errorCount: isError ? 1 : 0,
17599
18331
  lastObservedIso: timestamp
@@ -17610,7 +18342,7 @@ function mapLogEntriesToSignals(entries) {
17610
18342
 
17611
18343
  // src/connectors/firebase/resolve.ts
17612
18344
  init_cjs_shims();
17613
- var import_types66 = require("@neat.is/types");
18345
+ var import_types67 = require("@neat.is/types");
17614
18346
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17615
18347
  switch (resourceType) {
17616
18348
  case "cloud_function":
@@ -17625,7 +18357,7 @@ function routeEntriesFor(graph, serviceName) {
17625
18357
  const entries = [];
17626
18358
  graph.forEachNode((_id, attrs) => {
17627
18359
  const node = attrs;
17628
- if (node.type !== import_types66.NodeType.RouteNode) return;
18360
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17629
18361
  const route = attrs;
17630
18362
  if (route.service !== serviceName) return;
17631
18363
  entries.push({
@@ -17657,7 +18389,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17657
18389
  return {
17658
18390
  targetNodeId: match.routeNodeId,
17659
18391
  serviceName,
17660
- edgeType: import_types66.EdgeType.CALLS
18392
+ edgeType: import_types67.EdgeType.CALLS
17661
18393
  };
17662
18394
  };
17663
18395
  }
@@ -17684,7 +18416,7 @@ init_cjs_shims();
17684
18416
 
17685
18417
  // src/connectors/cloudflare/connector.ts
17686
18418
  init_cjs_shims();
17687
- var import_types68 = require("@neat.is/types");
18419
+ var import_types69 = require("@neat.is/types");
17688
18420
 
17689
18421
  // src/connectors/cloudflare/client.ts
17690
18422
  init_cjs_shims();
@@ -17800,7 +18532,7 @@ function mapEventToSignal(event) {
17800
18532
  if (Number.isNaN(observedAt.getTime())) return null;
17801
18533
  const statusCode = metadata?.statusCode;
17802
18534
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17803
- const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18535
+ const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17804
18536
  return {
17805
18537
  targetKind: CLOUDFLARE_TARGET_KIND,
17806
18538
  targetName: scriptName,
@@ -17808,7 +18540,7 @@ function mapEventToSignal(event) {
17808
18540
  errorCount: isError ? 1 : 0,
17809
18541
  lastObservedIso: observedAt.toISOString(),
17810
18542
  method,
17811
- ...path76 ? { path: path76 } : {},
18543
+ ...path78 ? { path: path78 } : {},
17812
18544
  ...typeof statusCode === "number" ? { statusCode } : {},
17813
18545
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17814
18546
  };
@@ -17848,19 +18580,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17848
18580
  graph.forEachNode((id, attrs) => {
17849
18581
  if (found) return;
17850
18582
  const a = attrs;
17851
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18583
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
17852
18584
  found = id;
17853
18585
  }
17854
18586
  });
17855
18587
  return found;
17856
18588
  }
17857
- function findMatchingRouteNode(graph, serviceName, method, path76) {
17858
- const normalizedPath = normalizePathTemplate(path76);
18589
+ function findMatchingRouteNode(graph, serviceName, method, path78) {
18590
+ const normalizedPath = normalizePathTemplate(path78);
17859
18591
  let found = null;
17860
18592
  graph.forEachNode((id, attrs) => {
17861
18593
  if (found) return;
17862
18594
  const a = attrs;
17863
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18595
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17864
18596
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17865
18597
  const routeMethod = (a.method ?? "").toUpperCase();
17866
18598
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17872,18 +18604,18 @@ function createCloudflareResolveTarget(config, graph) {
17872
18604
  return (signal) => {
17873
18605
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17874
18606
  const scriptName = signal.targetName;
17875
- const { method, path: path76 } = signal;
18607
+ const { method, path: path78 } = signal;
17876
18608
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17877
- if (!method || !path76) return wholeFileId;
17878
- return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
18609
+ if (!method || !path78) return wholeFileId;
18610
+ return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
17879
18611
  };
17880
18612
  const mapping = config.workers?.[scriptName];
17881
18613
  if (mapping) {
17882
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18614
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17883
18615
  return {
17884
18616
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17885
18617
  serviceName: mapping.service,
17886
- edgeType: import_types68.EdgeType.CALLS
18618
+ edgeType: import_types69.EdgeType.CALLS
17887
18619
  };
17888
18620
  }
17889
18621
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17892,13 +18624,13 @@ function createCloudflareResolveTarget(config, graph) {
17892
18624
  return {
17893
18625
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17894
18626
  serviceName: fileNode.service,
17895
- edgeType: import_types68.EdgeType.CALLS
18627
+ edgeType: import_types69.EdgeType.CALLS
17896
18628
  };
17897
18629
  }
17898
18630
  return {
17899
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18631
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17900
18632
  serviceName: scriptName,
17901
- edgeType: import_types68.EdgeType.CALLS,
18633
+ edgeType: import_types69.EdgeType.CALLS,
17902
18634
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
17903
18635
  };
17904
18636
  };
@@ -18094,14 +18826,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
18094
18826
 
18095
18827
  // src/connectors/neon/resolve.ts
18096
18828
  init_cjs_shims();
18097
- var import_types72 = require("@neat.is/types");
18829
+ var import_types73 = require("@neat.is/types");
18098
18830
  function createNeonResolveTarget(config) {
18099
18831
  return (signal) => {
18100
18832
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18101
18833
  return {
18102
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18834
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
18103
18835
  serviceName: config.serviceName,
18104
- edgeType: import_types72.EdgeType.CALLS,
18836
+ edgeType: import_types73.EdgeType.CALLS,
18105
18837
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
18106
18838
  };
18107
18839
  };
@@ -18227,9 +18959,9 @@ function parseCloudRunTargetName(targetName) {
18227
18959
  const secondSep = rest.indexOf(FIELD_SEP2);
18228
18960
  if (secondSep === -1) return null;
18229
18961
  const method = rest.slice(0, secondSep);
18230
- const path76 = rest.slice(secondSep + 1);
18231
- if (!serviceName || !method || !path76) return null;
18232
- return { serviceName, method, path: path76 };
18962
+ const path78 = rest.slice(secondSep + 1);
18963
+ if (!serviceName || !method || !path78) return null;
18964
+ return { serviceName, method, path: path78 };
18233
18965
  }
18234
18966
 
18235
18967
  // src/connectors/cloud-run/map.ts
@@ -18258,14 +18990,14 @@ function mapLogEntryToSignal2(entry2) {
18258
18990
  if (!req2) return null;
18259
18991
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
18260
18992
  const method = req2.requestMethod.toUpperCase();
18261
- const path76 = pathFromRequestUrl2(req2.requestUrl);
18262
- if (path76 === null) return null;
18993
+ const path78 = pathFromRequestUrl2(req2.requestUrl);
18994
+ if (path78 === null) return null;
18263
18995
  const timestamp = entry2.timestamp;
18264
18996
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
18265
18997
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
18266
18998
  return {
18267
18999
  targetKind: CLOUD_RUN_TARGET_KIND,
18268
- targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
19000
+ targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
18269
19001
  callCount: 1,
18270
19002
  errorCount: isError ? 1 : 0,
18271
19003
  lastObservedIso: timestamp
@@ -18282,14 +19014,14 @@ function mapLogEntriesToSignals2(entries) {
18282
19014
 
18283
19015
  // src/connectors/cloud-run/resolve.ts
18284
19016
  init_cjs_shims();
18285
- var import_types76 = require("@neat.is/types");
19017
+ var import_types77 = require("@neat.is/types");
18286
19018
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18287
19019
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18288
19020
  let found = null;
18289
19021
  graph.forEachNode((_id, attrs) => {
18290
19022
  if (found) return;
18291
19023
  const node = attrs;
18292
- if (node.type !== import_types76.NodeType.RouteNode) return;
19024
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18293
19025
  const route = attrs;
18294
19026
  if (route.service !== serviceName || !route.pathTemplate) return;
18295
19027
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18304,23 +19036,23 @@ function createCloudRunResolveTarget(graph, config) {
18304
19036
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18305
19037
  const identity = parseCloudRunTargetName(signal.targetName);
18306
19038
  if (!identity) return null;
18307
- const { serviceName: gcpServiceName, method, path: path76 } = identity;
19039
+ const { serviceName: gcpServiceName, method, path: path78 } = identity;
18308
19040
  const mappedService = config.serviceMap?.[gcpServiceName];
18309
19041
  if (mappedService) {
18310
19042
  const routeNodeId = findMatchingRouteNode2(
18311
19043
  graph,
18312
19044
  mappedService,
18313
19045
  method,
18314
- normalizePathTemplate(path76)
19046
+ normalizePathTemplate(path78)
18315
19047
  );
18316
19048
  if (routeNodeId) {
18317
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
19049
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18318
19050
  }
18319
19051
  }
18320
19052
  return {
18321
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
19053
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18322
19054
  serviceName: mappedService ?? gcpServiceName,
18323
- edgeType: import_types76.EdgeType.CALLS,
19055
+ edgeType: import_types77.EdgeType.CALLS,
18324
19056
  ensureInfraNode: {
18325
19057
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18326
19058
  name: gcpServiceName,
@@ -18361,7 +19093,7 @@ function createCloudRunConnector(graph, config = {}) {
18361
19093
 
18362
19094
  // src/connectors/render/index.ts
18363
19095
  init_cjs_shims();
18364
- var import_types79 = require("@neat.is/types");
19096
+ var import_types80 = require("@neat.is/types");
18365
19097
 
18366
19098
  // src/connectors/render/types.ts
18367
19099
  init_cjs_shims();
@@ -18439,7 +19171,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18439
19171
  const out = [];
18440
19172
  graph.forEachNode((_id, attrs) => {
18441
19173
  const node = attrs;
18442
- if (node.type !== import_types79.NodeType.RouteNode) return;
19174
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18443
19175
  const route = attrs;
18444
19176
  if (route.service !== serviceName) return;
18445
19177
  out.push({
@@ -18524,7 +19256,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18524
19256
  function createRenderResolveTarget(config) {
18525
19257
  return (signal) => {
18526
19258
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18527
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
19259
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18528
19260
  }
18529
19261
  return null;
18530
19262
  };
@@ -18662,21 +19394,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18662
19394
 
18663
19395
  // src/connectors/planetscale/resolve.ts
18664
19396
  init_cjs_shims();
18665
- var import_types83 = require("@neat.is/types");
19397
+ var import_types84 = require("@neat.is/types");
18666
19398
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18667
19399
  function createPlanetscaleResolveTarget(graph, config) {
18668
19400
  const databaseName = `${config.organization}/${config.database}`;
18669
19401
  return (signal, _ctx) => {
18670
19402
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18671
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19403
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18672
19404
  if (graph.hasNode(tableId)) {
18673
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19405
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18674
19406
  }
18675
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19407
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18676
19408
  return {
18677
19409
  targetNodeId: providerId,
18678
19410
  serviceName: config.serviceName,
18679
- edgeType: import_types83.EdgeType.CALLS,
19411
+ edgeType: import_types84.EdgeType.CALLS,
18680
19412
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18681
19413
  };
18682
19414
  };
@@ -18941,7 +19673,7 @@ function mapBuildsToSignals(builds, serviceName) {
18941
19673
 
18942
19674
  // src/connectors/eas/resolve.ts
18943
19675
  init_cjs_shims();
18944
- var import_types88 = require("@neat.is/types");
19676
+ var import_types89 = require("@neat.is/types");
18945
19677
  var NO_ENV2 = "unknown";
18946
19678
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
18947
19679
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -18957,8 +19689,8 @@ function configBasenamesForPhase(phase) {
18957
19689
  function configNodeService(graph, configNodeId) {
18958
19690
  for (const edgeId of graph.inboundEdges(configNodeId)) {
18959
19691
  const edge = graph.getEdgeAttributes(edgeId);
18960
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
18961
- const parsed = (0, import_types88.parseFileId)(edge.source);
19692
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19693
+ const parsed = (0, import_types89.parseFileId)(edge.source);
18962
19694
  if (parsed) return parsed.service;
18963
19695
  }
18964
19696
  return null;
@@ -18969,7 +19701,7 @@ function findConfigNode(graph, basenames, serviceName) {
18969
19701
  graph.forEachNode((id, attrs) => {
18970
19702
  if (scoped) return;
18971
19703
  const node = attrs;
18972
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19704
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
18973
19705
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
18974
19706
  if (anyMatch === null) anyMatch = id;
18975
19707
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -18986,13 +19718,13 @@ function createEasResolveTarget(graph) {
18986
19718
  if (basenames.length > 0) {
18987
19719
  const configNodeId = findConfigNode(graph, basenames, serviceName);
18988
19720
  if (configNodeId) {
18989
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19721
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
18990
19722
  }
18991
19723
  }
18992
19724
  return {
18993
19725
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
18994
19726
  serviceName,
18995
- edgeType: import_types88.EdgeType.CALLS
19727
+ edgeType: import_types89.EdgeType.CALLS
18996
19728
  };
18997
19729
  };
18998
19730
  }
@@ -19717,11 +20449,11 @@ function registerRoutes(scope, ctx) {
19717
20449
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19718
20450
  const parsed = [];
19719
20451
  for (const c of candidates) {
19720
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20452
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19721
20453
  if (!r.success) {
19722
20454
  return reply.code(400).send({
19723
20455
  error: `unknown divergence type "${c}"`,
19724
- allowed: import_types91.DivergenceTypeSchema.options
20456
+ allowed: import_types92.DivergenceTypeSchema.options
19725
20457
  });
19726
20458
  }
19727
20459
  parsed.push(r.data);
@@ -20063,7 +20795,7 @@ function registerRoutes(scope, ctx) {
20063
20795
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
20064
20796
  let violations = await log.readAll();
20065
20797
  if (req2.query.severity) {
20066
- const sev = import_types91.PolicySeveritySchema.safeParse(req2.query.severity);
20798
+ const sev = import_types92.PolicySeveritySchema.safeParse(req2.query.severity);
20067
20799
  if (!sev.success) {
20068
20800
  return reply.code(400).send({
20069
20801
  error: "invalid severity",
@@ -20102,7 +20834,7 @@ function registerRoutes(scope, ctx) {
20102
20834
  scope.post("/policies/check", async (req2, reply) => {
20103
20835
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
20104
20836
  if (!proj) return;
20105
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
20837
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
20106
20838
  if (!parsed.success) {
20107
20839
  return reply.code(400).send({
20108
20840
  error: "invalid /policies/check body",
@@ -20423,8 +21155,8 @@ init_auth();
20423
21155
 
20424
21156
  // src/unrouted.ts
20425
21157
  init_cjs_shims();
20426
- var import_node_fs38 = require("fs");
20427
- var import_node_path72 = __toESM(require("path"), 1);
21158
+ var import_node_fs39 = require("fs");
21159
+ var import_node_path74 = __toESM(require("path"), 1);
20428
21160
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
20429
21161
  return {
20430
21162
  timestamp: now.toISOString(),
@@ -20434,34 +21166,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
20434
21166
  };
20435
21167
  }
20436
21168
  async function appendUnroutedSpan(neatHome4, record) {
20437
- const target = import_node_path72.default.join(neatHome4, "errors.ndjson");
20438
- await import_node_fs38.promises.mkdir(neatHome4, { recursive: true });
20439
- await import_node_fs38.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
21169
+ const target = import_node_path74.default.join(neatHome4, "errors.ndjson");
21170
+ await import_node_fs39.promises.mkdir(neatHome4, { recursive: true });
21171
+ await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
20440
21172
  }
20441
21173
  function unroutedErrorsPath(neatHome4) {
20442
- return import_node_path72.default.join(neatHome4, "errors.ndjson");
21174
+ return import_node_path74.default.join(neatHome4, "errors.ndjson");
20443
21175
  }
20444
21176
 
20445
21177
  // src/daemon.ts
20446
- var import_types92 = require("@neat.is/types");
21178
+ var import_types93 = require("@neat.is/types");
20447
21179
  function daemonJsonPath(scanPath) {
20448
- return import_node_path73.default.join(scanPath, "neat-out", "daemon.json");
21180
+ return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
20449
21181
  }
20450
21182
  function daemonsDiscoveryDir(home) {
20451
21183
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
20452
- return import_node_path73.default.join(base, "daemons");
21184
+ return import_node_path75.default.join(base, "daemons");
20453
21185
  }
20454
21186
  function daemonDiscoveryPath(project, home) {
20455
- return import_node_path73.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
21187
+ return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
20456
21188
  }
20457
21189
  function sanitizeDiscoveryName(project) {
20458
21190
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
20459
21191
  }
20460
21192
  function neatHomeFromEnv() {
20461
21193
  const env = process.env.NEAT_HOME;
20462
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21194
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20463
21195
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20464
- return import_node_path73.default.join(home, ".neat");
21196
+ return import_node_path75.default.join(home, ".neat");
20465
21197
  }
20466
21198
  function resolveNeatVersion() {
20467
21199
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -20493,7 +21225,7 @@ async function clearDaemonRecord(record, home) {
20493
21225
  } catch {
20494
21226
  }
20495
21227
  try {
20496
- await import_node_fs39.promises.unlink(daemonDiscoveryPath(record.project, home));
21228
+ await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
20497
21229
  } catch {
20498
21230
  }
20499
21231
  }
@@ -20502,12 +21234,12 @@ function reconcileDaemonRecordSync(record, home) {
20502
21234
  const stopped = { ...record, status: "stopped" };
20503
21235
  const target = daemonJsonPath(record.projectPath);
20504
21236
  const tmp = `${target}.${process.pid}.tmp`;
20505
- (0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
20506
- (0, import_node_fs39.renameSync)(tmp, target);
21237
+ (0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
21238
+ (0, import_node_fs40.renameSync)(tmp, target);
20507
21239
  } catch {
20508
21240
  }
20509
21241
  try {
20510
- (0, import_node_fs39.unlinkSync)(daemonDiscoveryPath(record.project, home));
21242
+ (0, import_node_fs40.unlinkSync)(daemonDiscoveryPath(record.project, home));
20511
21243
  } catch {
20512
21244
  }
20513
21245
  }
@@ -20530,11 +21262,11 @@ function teardownSlot(slot) {
20530
21262
  }
20531
21263
  }
20532
21264
  function neatHomeFor(opts) {
20533
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path73.default.resolve(opts.neatHome);
21265
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
20534
21266
  const env = process.env.NEAT_HOME;
20535
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21267
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20536
21268
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20537
- return import_node_path73.default.join(home, ".neat");
21269
+ return import_node_path75.default.join(home, ".neat");
20538
21270
  }
20539
21271
  function routeSpanToProject(serviceName, projects) {
20540
21272
  if (!serviceName) return DEFAULT_PROJECT;
@@ -20582,13 +21314,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
20582
21314
  if (!serviceName) return true;
20583
21315
  if (serviceNameMatchesProject(serviceName, project)) return true;
20584
21316
  return graph.someNode(
20585
- (_id, attrs) => attrs.type === import_types92.NodeType.ServiceNode && attrs.name === serviceName
21317
+ (_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
20586
21318
  );
20587
21319
  }
20588
21320
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
20589
- const paths = pathsForProject(entry2.name, import_node_path73.default.join(entry2.path, "neat-out"));
21321
+ const paths = pathsForProject(entry2.name, import_node_path75.default.join(entry2.path, "neat-out"));
20590
21322
  try {
20591
- const stat = await import_node_fs39.promises.stat(entry2.path);
21323
+ const stat = await import_node_fs40.promises.stat(entry2.path);
20592
21324
  if (!stat.isDirectory()) {
20593
21325
  throw new Error(`registered path ${entry2.path} is not a directory`);
20594
21326
  }
@@ -20706,7 +21438,7 @@ async function startDaemon(opts = {}) {
20706
21438
  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;
20707
21439
  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;
20708
21440
  const singleProject = projectArg;
20709
- const singleProjectPath = singleProject && projectPathArg ? import_node_path73.default.resolve(projectPathArg) : null;
21441
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
20710
21442
  if (singleProject && !singleProjectPath) {
20711
21443
  throw new Error(
20712
21444
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -20714,14 +21446,14 @@ async function startDaemon(opts = {}) {
20714
21446
  }
20715
21447
  if (!singleProject) {
20716
21448
  try {
20717
- await import_node_fs39.promises.access(regPath);
21449
+ await import_node_fs40.promises.access(regPath);
20718
21450
  } catch {
20719
21451
  throw new Error(
20720
21452
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
20721
21453
  );
20722
21454
  }
20723
21455
  }
20724
- const pidPath = import_node_path73.default.join(home, "neatd.pid");
21456
+ const pidPath = import_node_path75.default.join(home, "neatd.pid");
20725
21457
  await writeAtomically(pidPath, `${process.pid}
20726
21458
  `);
20727
21459
  const slots = /* @__PURE__ */ new Map();
@@ -20933,7 +21665,7 @@ async function startDaemon(opts = {}) {
20933
21665
  }
20934
21666
  if (restApp) await restApp.close().catch(() => {
20935
21667
  });
20936
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21668
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
20937
21669
  });
20938
21670
  throw new Error(
20939
21671
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -21101,7 +21833,7 @@ async function startDaemon(opts = {}) {
21101
21833
  });
21102
21834
  if (otlpApp) await otlpApp.close().catch(() => {
21103
21835
  });
21104
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21836
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21105
21837
  });
21106
21838
  throw new Error(
21107
21839
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -21136,7 +21868,7 @@ async function startDaemon(opts = {}) {
21136
21868
  });
21137
21869
  if (otlpApp) await otlpApp.close().catch(() => {
21138
21870
  });
21139
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21871
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21140
21872
  });
21141
21873
  throw new Error(
21142
21874
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -21183,9 +21915,9 @@ async function startDaemon(opts = {}) {
21183
21915
  let registryWatcher = null;
21184
21916
  let reloadTimer = null;
21185
21917
  if (!singleProject) try {
21186
- const regDir = import_node_path73.default.dirname(regPath);
21187
- const regBase = import_node_path73.default.basename(regPath);
21188
- registryWatcher = (0, import_node_fs39.watch)(regDir, (_eventType, filename) => {
21918
+ const regDir = import_node_path75.default.dirname(regPath);
21919
+ const regBase = import_node_path75.default.basename(regPath);
21920
+ registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
21189
21921
  if (filename !== null && filename !== regBase) return;
21190
21922
  if (reloadTimer) clearTimeout(reloadTimer);
21191
21923
  reloadTimer = setTimeout(() => {
@@ -21234,7 +21966,7 @@ async function startDaemon(opts = {}) {
21234
21966
  if (daemonRecord) {
21235
21967
  await clearDaemonRecord(daemonRecord, home);
21236
21968
  }
21237
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21969
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21238
21970
  });
21239
21971
  };
21240
21972
  return {
@@ -21257,9 +21989,9 @@ init_auth();
21257
21989
  // src/web-spawn.ts
21258
21990
  init_cjs_shims();
21259
21991
  var import_node_child_process2 = require("child_process");
21260
- var import_node_fs40 = require("fs");
21992
+ var import_node_fs41 = require("fs");
21261
21993
  var import_node_net = __toESM(require("net"), 1);
21262
- var import_node_path74 = __toESM(require("path"), 1);
21994
+ var import_node_path76 = __toESM(require("path"), 1);
21263
21995
  var DEFAULT_WEB_PORT = 6328;
21264
21996
  var DEFAULT_REST_PORT = 8080;
21265
21997
  function asValidPort(value) {
@@ -21268,11 +22000,11 @@ function asValidPort(value) {
21268
22000
  }
21269
22001
  function projectRoot() {
21270
22002
  const fromEnv = process.env.NEAT_SCAN_PATH;
21271
- return import_node_path74.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
22003
+ return import_node_path76.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
21272
22004
  }
21273
22005
  async function readDaemonPorts(root) {
21274
22006
  try {
21275
- const raw = await import_node_fs40.promises.readFile(import_node_path74.default.join(root, "neat-out", "daemon.json"), "utf8");
22007
+ const raw = await import_node_fs41.promises.readFile(import_node_path76.default.join(root, "neat-out", "daemon.json"), "utf8");
21276
22008
  const parsed = JSON.parse(raw);
21277
22009
  const ports = parsed?.ports ?? {};
21278
22010
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -21317,10 +22049,10 @@ function resolveWebPackageDir() {
21317
22049
  eval("require")
21318
22050
  );
21319
22051
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
21320
- return import_node_path74.default.dirname(pkgJsonPath);
22052
+ return import_node_path76.default.dirname(pkgJsonPath);
21321
22053
  }
21322
22054
  function resolveStandaloneServerEntry(webDir) {
21323
- return import_node_path74.default.join(webDir, ".next/standalone/packages/web/server.js");
22055
+ return import_node_path76.default.join(webDir, ".next/standalone/packages/web/server.js");
21324
22056
  }
21325
22057
  async function pickInternalPort() {
21326
22058
  return new Promise((resolve, reject) => {
@@ -21373,7 +22105,7 @@ async function spawnWebUI(restPort, opts = {}) {
21373
22105
  NEAT_API_URL: apiUrl
21374
22106
  };
21375
22107
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
21376
- cwd: import_node_path74.default.dirname(serverEntry),
22108
+ cwd: import_node_path76.default.dirname(serverEntry),
21377
22109
  env,
21378
22110
  stdio: ["ignore", "inherit", "inherit"],
21379
22111
  detached: false
@@ -21500,15 +22232,15 @@ async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
21500
22232
  }
21501
22233
  function isLocalBehind(local, remote) {
21502
22234
  if (local === remote) return false;
21503
- const parse10 = (v) => {
22235
+ const parse11 = (v) => {
21504
22236
  const core = v.split(/[-+]/)[0] ?? v;
21505
22237
  return core.split(".").map((s) => {
21506
22238
  const n = Number.parseInt(s, 10);
21507
22239
  return Number.isFinite(n) ? n : 0;
21508
22240
  });
21509
22241
  };
21510
- const a = parse10(local);
21511
- const b = parse10(remote);
22242
+ const a = parse11(local);
22243
+ const b = parse11(remote);
21512
22244
  const len = Math.max(a.length, b.length);
21513
22245
  for (let i = 0; i < len; i++) {
21514
22246
  const av = a[i] ?? 0;
@@ -21549,14 +22281,14 @@ function localVersion() {
21549
22281
  }
21550
22282
  function neatHome3() {
21551
22283
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
21552
- return import_node_path75.default.resolve(process.env.NEAT_HOME);
22284
+ return import_node_path77.default.resolve(process.env.NEAT_HOME);
21553
22285
  }
21554
22286
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21555
- return import_node_path75.default.join(home, ".neat");
22287
+ return import_node_path77.default.join(home, ".neat");
21556
22288
  }
21557
22289
  async function readPid() {
21558
22290
  try {
21559
- const raw = await import_node_fs41.promises.readFile(import_node_path75.default.join(neatHome3(), "neatd.pid"), "utf8");
22291
+ const raw = await import_node_fs42.promises.readFile(import_node_path77.default.join(neatHome3(), "neatd.pid"), "utf8");
21560
22292
  const n = Number.parseInt(raw.trim(), 10);
21561
22293
  return Number.isFinite(n) ? n : null;
21562
22294
  } catch {