@neat.is/core 0.9.1-dev.20260819 → 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,7 +10739,7 @@ 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;
@@ -10277,13 +10827,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10277
10827
  // call sites — verified-call-site tier (ADR-066).
10278
10828
  confidenceKind: "verified-call-site",
10279
10829
  evidence: {
10280
- file: import_node_path38.default.relative(serviceDir, file.path),
10830
+ file: import_node_path39.default.relative(serviceDir, file.path),
10281
10831
  line,
10282
10832
  snippet: snippet(file.content, line)
10283
10833
  }
10284
10834
  });
10285
10835
  };
10286
- if (import_node_path38.default.extname(file.path) === ".go") {
10836
+ if (import_node_path39.default.extname(file.path) === ".go") {
10287
10837
  goSaramaEndpoints(file.content, make);
10288
10838
  } else {
10289
10839
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -10294,7 +10844,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10294
10844
 
10295
10845
  // src/extract/calls/redis.ts
10296
10846
  init_cjs_shims();
10297
- var import_node_path39 = __toESM(require("path"), 1);
10847
+ var import_node_path40 = __toESM(require("path"), 1);
10298
10848
  var import_types29 = require("@neat.is/types");
10299
10849
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10300
10850
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10317,7 +10867,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10317
10867
  // support tier (ADR-066).
10318
10868
  confidenceKind: "url-with-structural-support",
10319
10869
  evidence: {
10320
- file: import_node_path39.default.relative(serviceDir, file.path),
10870
+ file: import_node_path40.default.relative(serviceDir, file.path),
10321
10871
  line,
10322
10872
  snippet: snippet(file.content, line)
10323
10873
  }
@@ -10328,7 +10878,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10328
10878
 
10329
10879
  // src/extract/calls/aws.ts
10330
10880
  init_cjs_shims();
10331
- var import_node_path40 = __toESM(require("path"), 1);
10881
+ var import_node_path41 = __toESM(require("path"), 1);
10332
10882
  var import_types30 = require("@neat.is/types");
10333
10883
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10334
10884
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10362,7 +10912,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10362
10912
  // (ADR-066).
10363
10913
  confidenceKind: "verified-call-site",
10364
10914
  evidence: {
10365
- file: import_node_path40.default.relative(serviceDir, file.path),
10915
+ file: import_node_path41.default.relative(serviceDir, file.path),
10366
10916
  line,
10367
10917
  snippet: snippet(file.content, line)
10368
10918
  }
@@ -10387,7 +10937,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10387
10937
 
10388
10938
  // src/extract/calls/grpc.ts
10389
10939
  init_cjs_shims();
10390
- var import_node_path41 = __toESM(require("path"), 1);
10940
+ var import_node_path42 = __toESM(require("path"), 1);
10391
10941
  var import_types31 = require("@neat.is/types");
10392
10942
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10393
10943
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -10446,7 +10996,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10446
10996
  // tier (ADR-066).
10447
10997
  confidenceKind: "verified-call-site",
10448
10998
  evidence: {
10449
- file: import_node_path41.default.relative(serviceDir, file.path),
10999
+ file: import_node_path42.default.relative(serviceDir, file.path),
10450
11000
  line,
10451
11001
  snippet: snippet(file.content, line)
10452
11002
  }
@@ -10457,7 +11007,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10457
11007
 
10458
11008
  // src/extract/calls/supabase.ts
10459
11009
  init_cjs_shims();
10460
- var import_node_path42 = __toESM(require("path"), 1);
11010
+ var import_node_path43 = __toESM(require("path"), 1);
10461
11011
  var import_types32 = require("@neat.is/types");
10462
11012
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
10463
11013
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -10516,7 +11066,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10516
11066
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10517
11067
  confidenceKind: "verified-call-site",
10518
11068
  evidence: {
10519
- file: import_node_path42.default.relative(serviceDir, file.path),
11069
+ file: import_node_path43.default.relative(serviceDir, file.path),
10520
11070
  line,
10521
11071
  snippet: snippet(file.content, line)
10522
11072
  }
@@ -10543,7 +11093,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10543
11093
  edgeType: "CALLS",
10544
11094
  confidenceKind: "verified-call-site",
10545
11095
  evidence: {
10546
- file: import_node_path42.default.relative(serviceDir, file.path),
11096
+ file: import_node_path43.default.relative(serviceDir, file.path),
10547
11097
  line,
10548
11098
  snippet: snippet(file.content, line)
10549
11099
  }
@@ -10555,7 +11105,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10555
11105
 
10556
11106
  // src/extract/calls/firestore.ts
10557
11107
  init_cjs_shims();
10558
- var import_node_path43 = __toESM(require("path"), 1);
11108
+ var import_node_path44 = __toESM(require("path"), 1);
10559
11109
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10560
11110
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10561
11111
  var import_types33 = require("@neat.is/types");
@@ -10594,7 +11144,7 @@ function isFirestoreClientFactory(node) {
10594
11144
  }
10595
11145
  function firestoreClientVars(root) {
10596
11146
  const vars = /* @__PURE__ */ new Set();
10597
- const walk9 = (node) => {
11147
+ const walk10 = (node) => {
10598
11148
  if (node.type === "variable_declarator") {
10599
11149
  const name = node.childForFieldName("name");
10600
11150
  let value = node.childForFieldName("value");
@@ -10603,9 +11153,9 @@ function firestoreClientVars(root) {
10603
11153
  vars.add(name.text);
10604
11154
  }
10605
11155
  }
10606
- for (const c of namedChildren(node)) walk9(c);
11156
+ for (const c of namedChildren(node)) walk10(c);
10607
11157
  };
10608
- walk9(root);
11158
+ walk10(root);
10609
11159
  return vars;
10610
11160
  }
10611
11161
  function isClientExpr(node, clientVars) {
@@ -10726,7 +11276,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10726
11276
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10727
11277
  if (!hasClient && !hasAdmin) return [];
10728
11278
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10729
- 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);
10730
11280
  const clientVars = firestoreClientVars(tree.rootNode);
10731
11281
  const collLine = /* @__PURE__ */ new Map();
10732
11282
  const writes = /* @__PURE__ */ new Map();
@@ -10760,7 +11310,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10760
11310
  }
10761
11311
  s.add(field);
10762
11312
  };
10763
- const walk9 = (node) => {
11313
+ const walk10 = (node) => {
10764
11314
  if (node.type === "call_expression") {
10765
11315
  const fn = node.childForFieldName("function");
10766
11316
  const line = node.startPosition.row + 1;
@@ -10800,9 +11350,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10800
11350
  }
10801
11351
  }
10802
11352
  }
10803
- for (const c of namedChildren(node)) walk9(c);
11353
+ for (const c of namedChildren(node)) walk10(c);
10804
11354
  };
10805
- walk9(tree.rootNode);
11355
+ walk10(tree.rootNode);
10806
11356
  const out = [];
10807
11357
  for (const [collPath, line] of collLine) {
10808
11358
  const byField = writes.get(collPath);
@@ -10830,7 +11380,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10830
11380
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10831
11381
  ...sdkWrites ? { sdkWrites } : {},
10832
11382
  evidence: {
10833
- file: import_node_path43.default.relative(serviceDir, file.path),
11383
+ file: import_node_path44.default.relative(serviceDir, file.path),
10834
11384
  line,
10835
11385
  snippet: snippet(file.content, line)
10836
11386
  }
@@ -10841,7 +11391,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10841
11391
 
10842
11392
  // src/extract/calls/mongoose.ts
10843
11393
  init_cjs_shims();
10844
- var import_node_path44 = __toESM(require("path"), 1);
11394
+ var import_node_path45 = __toESM(require("path"), 1);
10845
11395
  var import_types34 = require("@neat.is/types");
10846
11396
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10847
11397
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -10992,7 +11542,7 @@ function endpoint(r, file, serviceDir, matchText) {
10992
11542
  kind: r.kind,
10993
11543
  edgeType: "CALLS",
10994
11544
  confidenceKind: "verified-call-site",
10995
- 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) }
10996
11546
  };
10997
11547
  }
10998
11548
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11082,7 +11632,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11082
11632
  const registry = /* @__PURE__ */ new Map();
11083
11633
  for (const f of mongooseFiles) {
11084
11634
  const fx = fileExportsOf(f.content, pluralizeOn);
11085
- 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);
11086
11636
  }
11087
11637
  if (registry.size === 0) return [];
11088
11638
  const out = [];
@@ -11093,7 +11643,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11093
11643
  const directColl = /* @__PURE__ */ new Map();
11094
11644
  const nsExports = /* @__PURE__ */ new Map();
11095
11645
  for (const b of bindings) {
11096
- 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);
11097
11647
  if (!resolvedRel) continue;
11098
11648
  const fx = registry.get(resolvedRel);
11099
11649
  if (!fx) continue;
@@ -11137,7 +11687,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11137
11687
 
11138
11688
  // src/extract/calls/sqlalchemy.ts
11139
11689
  init_cjs_shims();
11140
- var import_node_path45 = __toESM(require("path"), 1);
11690
+ var import_node_path46 = __toESM(require("path"), 1);
11141
11691
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11142
11692
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11143
11693
  var import_types35 = require("@neat.is/types");
@@ -11276,7 +11826,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11276
11826
  childTable,
11277
11827
  parentTable,
11278
11828
  evidence: {
11279
- file: import_node_path45.default.relative(serviceDir, file.path),
11829
+ file: import_node_path46.default.relative(serviceDir, file.path),
11280
11830
  line,
11281
11831
  snippet: snippet(file.content, line)
11282
11832
  }
@@ -11301,7 +11851,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11301
11851
  confidenceKind: "verified-call-site",
11302
11852
  ...columns && columns.length > 0 ? { columns } : {},
11303
11853
  evidence: {
11304
- file: import_node_path45.default.relative(serviceDir, file.path),
11854
+ file: import_node_path46.default.relative(serviceDir, file.path),
11305
11855
  line,
11306
11856
  snippet: snippet(file.content, line)
11307
11857
  }
@@ -11408,7 +11958,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11408
11958
  edgeType: "CALLS",
11409
11959
  confidenceKind: "verified-call-site",
11410
11960
  evidence: {
11411
- file: import_node_path45.default.relative(serviceDir, file.path),
11961
+ file: import_node_path46.default.relative(serviceDir, file.path),
11412
11962
  line,
11413
11963
  snippet: snippet(file.content, line)
11414
11964
  }
@@ -11420,7 +11970,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11420
11970
 
11421
11971
  // src/extract/calls/django-orm.ts
11422
11972
  init_cjs_shims();
11423
- var import_node_path46 = __toESM(require("path"), 1);
11973
+ var import_node_path47 = __toESM(require("path"), 1);
11424
11974
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11425
11975
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11426
11976
  var import_types36 = require("@neat.is/types");
@@ -11490,7 +12040,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11490
12040
  const tree = parseSource7(makePyParser4(), file.content);
11491
12041
  const out = [];
11492
12042
  const seen = /* @__PURE__ */ new Set();
11493
- 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));
11494
12044
  walk4(tree.rootNode, (node) => {
11495
12045
  if (node.type !== "class_definition") return;
11496
12046
  if (!extendsDjangoModel(node)) return;
@@ -11508,7 +12058,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11508
12058
  kind: "sql-table",
11509
12059
  edgeType: "CALLS",
11510
12060
  confidenceKind: "verified-call-site",
11511
- 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) }
11512
12062
  });
11513
12063
  });
11514
12064
  return out;
@@ -11516,7 +12066,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11516
12066
 
11517
12067
  // src/extract/calls/drizzle.ts
11518
12068
  init_cjs_shims();
11519
- var import_node_path47 = __toESM(require("path"), 1);
12069
+ var import_node_path48 = __toESM(require("path"), 1);
11520
12070
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11521
12071
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11522
12072
  var import_types37 = require("@neat.is/types");
@@ -11594,10 +12144,10 @@ function columnsFromObject(obj) {
11594
12144
  }
11595
12145
  function drizzleEndpointsFromFile(file, serviceDir) {
11596
12146
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11597
- 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);
11598
12148
  const out = [];
11599
12149
  const seen = /* @__PURE__ */ new Set();
11600
- const walk9 = (node) => {
12150
+ const walk10 = (node) => {
11601
12151
  if (node.type === "call_expression") {
11602
12152
  const fn = node.childForFieldName("function");
11603
12153
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11617,7 +12167,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11617
12167
  confidenceKind: "structural",
11618
12168
  columns,
11619
12169
  evidence: {
11620
- file: import_node_path47.default.relative(serviceDir, file.path),
12170
+ file: import_node_path48.default.relative(serviceDir, file.path),
11621
12171
  line,
11622
12172
  snippet: snippet(file.content, line)
11623
12173
  }
@@ -11625,9 +12175,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11625
12175
  }
11626
12176
  }
11627
12177
  }
11628
- for (const c of namedChildren4(node)) walk9(c);
12178
+ for (const c of namedChildren4(node)) walk10(c);
11629
12179
  };
11630
- walk9(tree.rootNode);
12180
+ walk10(tree.rootNode);
11631
12181
  return out;
11632
12182
  }
11633
12183
  function enclosingVarName(call) {
@@ -11649,7 +12199,7 @@ function enclosingVarName(call) {
11649
12199
  function collectDrizzleTables(root) {
11650
12200
  const tables = [];
11651
12201
  const varToTable = /* @__PURE__ */ new Map();
11652
- const walk9 = (node) => {
12202
+ const walk10 = (node) => {
11653
12203
  if (node.type === "call_expression") {
11654
12204
  const fn = node.childForFieldName("function");
11655
12205
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11664,9 +12214,9 @@ function collectDrizzleTables(root) {
11664
12214
  }
11665
12215
  }
11666
12216
  }
11667
- for (const c of namedChildren4(node)) walk9(c);
12217
+ for (const c of namedChildren4(node)) walk10(c);
11668
12218
  };
11669
- walk9(root);
12219
+ walk10(root);
11670
12220
  return { tables, varToTable };
11671
12221
  }
11672
12222
  function referencesTargetVar(call) {
@@ -11683,13 +12233,13 @@ function referencesTargetVar(call) {
11683
12233
  }
11684
12234
  function drizzleForeignKeys(file, serviceDir) {
11685
12235
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11686
- 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);
11687
12237
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11688
12238
  const out = [];
11689
12239
  const seen = /* @__PURE__ */ new Set();
11690
12240
  for (const table of tables) {
11691
12241
  if (!table.object) continue;
11692
- const walk9 = (node) => {
12242
+ const walk10 = (node) => {
11693
12243
  if (node.type === "call_expression") {
11694
12244
  const targetVar = referencesTargetVar(node);
11695
12245
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -11702,7 +12252,7 @@ function drizzleForeignKeys(file, serviceDir) {
11702
12252
  childTable: table.tableName,
11703
12253
  parentTable,
11704
12254
  evidence: {
11705
- file: import_node_path47.default.relative(serviceDir, file.path),
12255
+ file: import_node_path48.default.relative(serviceDir, file.path),
11706
12256
  line,
11707
12257
  snippet: snippet(file.content, line)
11708
12258
  }
@@ -11710,16 +12260,16 @@ function drizzleForeignKeys(file, serviceDir) {
11710
12260
  }
11711
12261
  }
11712
12262
  }
11713
- for (const c of namedChildren4(node)) walk9(c);
12263
+ for (const c of namedChildren4(node)) walk10(c);
11714
12264
  };
11715
- walk9(table.object);
12265
+ walk10(table.object);
11716
12266
  }
11717
12267
  return out;
11718
12268
  }
11719
12269
 
11720
12270
  // src/extract/calls/prisma.ts
11721
12271
  init_cjs_shims();
11722
- var import_node_path48 = __toESM(require("path"), 1);
12272
+ var import_node_path49 = __toESM(require("path"), 1);
11723
12273
  var import_types38 = require("@neat.is/types");
11724
12274
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11725
12275
  "Int",
@@ -11775,7 +12325,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11775
12325
  confidenceKind: "structural",
11776
12326
  columns: b.columns,
11777
12327
  evidence: {
11778
- file: import_node_path48.default.relative(serviceDir, file.path),
12328
+ file: import_node_path49.default.relative(serviceDir, file.path),
11779
12329
  line: b.startLine,
11780
12330
  snippet: snippet(content, b.startLine)
11781
12331
  }
@@ -11836,7 +12386,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11836
12386
  }
11837
12387
  async function prismaColumnEndpoints(serviceDir) {
11838
12388
  const schemaPath = await findFirst(serviceDir, [
11839
- import_node_path48.default.join("prisma", "schema.prisma"),
12389
+ import_node_path49.default.join("prisma", "schema.prisma"),
11840
12390
  "schema.prisma"
11841
12391
  ]);
11842
12392
  if (!schemaPath) return [];
@@ -11911,7 +12461,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11911
12461
  childTable: current.table,
11912
12462
  parentTable,
11913
12463
  evidence: {
11914
- file: import_node_path48.default.relative(serviceDir, file.path),
12464
+ file: import_node_path49.default.relative(serviceDir, file.path),
11915
12465
  line: lineNo,
11916
12466
  snippet: snippet(content, lineNo)
11917
12467
  }
@@ -11926,7 +12476,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11926
12476
  }
11927
12477
  async function prismaForeignKeys(serviceDir) {
11928
12478
  const schemaPath = await findFirst(serviceDir, [
11929
- import_node_path48.default.join("prisma", "schema.prisma"),
12479
+ import_node_path49.default.join("prisma", "schema.prisma"),
11930
12480
  "schema.prisma"
11931
12481
  ]);
11932
12482
  if (!schemaPath) return [];
@@ -11937,7 +12487,7 @@ async function prismaForeignKeys(serviceDir) {
11937
12487
 
11938
12488
  // src/extract/calls/activerecord.ts
11939
12489
  init_cjs_shims();
11940
- var import_node_path49 = __toESM(require("path"), 1);
12490
+ var import_node_path50 = __toESM(require("path"), 1);
11941
12491
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11942
12492
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
11943
12493
  var import_types39 = require("@neat.is/types");
@@ -12145,7 +12695,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12145
12695
  confidenceKind: "structural",
12146
12696
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12147
12697
  evidence: {
12148
- file: import_node_path49.default.relative(serviceDir, file.path),
12698
+ file: import_node_path50.default.relative(serviceDir, file.path),
12149
12699
  line: table.line,
12150
12700
  snippet: snippet(file.content, table.line)
12151
12701
  }
@@ -12167,7 +12717,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12167
12717
  childTable,
12168
12718
  parentTable,
12169
12719
  evidence: {
12170
- file: import_node_path49.default.relative(serviceDir, file.path),
12720
+ file: import_node_path50.default.relative(serviceDir, file.path),
12171
12721
  line,
12172
12722
  snippet: snippet(file.content, line)
12173
12723
  }
@@ -12250,7 +12800,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12250
12800
  edgeType: "CALLS",
12251
12801
  confidenceKind: "verified-call-site",
12252
12802
  evidence: {
12253
- file: import_node_path49.default.relative(serviceDir, file.path),
12803
+ file: import_node_path50.default.relative(serviceDir, file.path),
12254
12804
  line,
12255
12805
  snippet: snippet(file.content, line)
12256
12806
  }
@@ -12287,7 +12837,7 @@ function railsModelForeignKeys(file, serviceDir) {
12287
12837
  childTable,
12288
12838
  parentTable,
12289
12839
  evidence: {
12290
- file: import_node_path49.default.relative(serviceDir, file.path),
12840
+ file: import_node_path50.default.relative(serviceDir, file.path),
12291
12841
  line,
12292
12842
  snippet: snippet(file.content, line)
12293
12843
  }
@@ -12299,7 +12849,7 @@ function railsModelForeignKeys(file, serviceDir) {
12299
12849
 
12300
12850
  // src/extract/calls/eloquent.ts
12301
12851
  init_cjs_shims();
12302
- var import_node_path50 = __toESM(require("path"), 1);
12852
+ var import_node_path51 = __toESM(require("path"), 1);
12303
12853
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12304
12854
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12305
12855
  var import_types40 = require("@neat.is/types");
@@ -12556,7 +13106,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12556
13106
  confidenceKind: "structural",
12557
13107
  ...columns.length > 0 ? { columns } : {},
12558
13108
  evidence: {
12559
- file: import_node_path50.default.relative(serviceDir, file.path),
13109
+ file: import_node_path51.default.relative(serviceDir, file.path),
12560
13110
  line: bp.line,
12561
13111
  snippet: snippet(file.content, bp.line)
12562
13112
  }
@@ -12578,7 +13128,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12578
13128
  childTable,
12579
13129
  parentTable,
12580
13130
  evidence: {
12581
- file: import_node_path50.default.relative(serviceDir, file.path),
13131
+ file: import_node_path51.default.relative(serviceDir, file.path),
12582
13132
  line,
12583
13133
  snippet: snippet(file.content, line)
12584
13134
  }
@@ -12698,7 +13248,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12698
13248
  edgeType: "CALLS",
12699
13249
  confidenceKind: "verified-call-site",
12700
13250
  evidence: {
12701
- file: import_node_path50.default.relative(serviceDir, file.path),
13251
+ file: import_node_path51.default.relative(serviceDir, file.path),
12702
13252
  line,
12703
13253
  snippet: snippet(file.content, line)
12704
13254
  }
@@ -12734,7 +13284,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12734
13284
  childTable,
12735
13285
  parentTable,
12736
13286
  evidence: {
12737
- file: import_node_path50.default.relative(serviceDir, file.path),
13287
+ file: import_node_path51.default.relative(serviceDir, file.path),
12738
13288
  line,
12739
13289
  snippet: snippet(file.content, line)
12740
13290
  }
@@ -12746,7 +13296,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12746
13296
 
12747
13297
  // src/extract/calls/go.ts
12748
13298
  init_cjs_shims();
12749
- var import_node_path53 = __toESM(require("path"), 1);
13299
+ var import_node_path54 = __toESM(require("path"), 1);
12750
13300
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12751
13301
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12752
13302
  var import_types41 = require("@neat.is/types");
@@ -12821,7 +13371,7 @@ function firstStringLiteralArg(argsNode) {
12821
13371
  return null;
12822
13372
  }
12823
13373
  function goSqlEndpointsFromFile(file, serviceDir) {
12824
- if (import_node_path53.default.extname(file.path) !== ".go") return [];
13374
+ if (import_node_path54.default.extname(file.path) !== ".go") return [];
12825
13375
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12826
13376
  const tree = parseSource10(makeGoParser3(), file.content);
12827
13377
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12850,7 +13400,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12850
13400
  confidenceKind: "verified-call-site",
12851
13401
  ...columns.length > 0 ? { columns } : {},
12852
13402
  evidence: {
12853
- file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
13403
+ file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
12854
13404
  line,
12855
13405
  snippet: snippet(file.content, line)
12856
13406
  }
@@ -12861,7 +13411,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12861
13411
 
12862
13412
  // src/extract/calls/gorm.ts
12863
13413
  init_cjs_shims();
12864
- var import_node_path54 = __toESM(require("path"), 1);
13414
+ var import_node_path55 = __toESM(require("path"), 1);
12865
13415
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12866
13416
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12867
13417
  var import_types42 = require("@neat.is/types");
@@ -13294,7 +13844,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13294
13844
  seen.delete(struct.name);
13295
13845
  }
13296
13846
  function gormEndpointsFromFile(file, serviceDir) {
13297
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13847
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13298
13848
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13299
13849
  const tree = parseSource11(makeGoParser4(), file.content);
13300
13850
  const { structs, models, tableFor } = analyze(tree);
@@ -13316,7 +13866,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13316
13866
  confidenceKind: "structural",
13317
13867
  ...columns.length > 0 ? { columns } : {},
13318
13868
  evidence: {
13319
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13869
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13320
13870
  line: struct.line,
13321
13871
  snippet: snippet(file.content, struct.line)
13322
13872
  }
@@ -13325,7 +13875,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13325
13875
  return out;
13326
13876
  }
13327
13877
  function gormForeignKeys(file, serviceDir) {
13328
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13878
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13329
13879
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13330
13880
  const tree = parseSource11(makeGoParser4(), file.content);
13331
13881
  const { structs, models, tableFor } = analyze(tree);
@@ -13340,7 +13890,7 @@ function gormForeignKeys(file, serviceDir) {
13340
13890
  childTable,
13341
13891
  parentTable,
13342
13892
  evidence: {
13343
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13893
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13344
13894
  line,
13345
13895
  snippet: snippet(file.content, line)
13346
13896
  }
@@ -13375,15 +13925,131 @@ function gormForeignKeys(file, serviceDir) {
13375
13925
  return out;
13376
13926
  }
13377
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
+
13378
14044
  // src/extract/calls/index.ts
13379
14045
  function edgeTypeFromEndpoint(ep) {
13380
14046
  switch (ep.edgeType) {
13381
14047
  case "PUBLISHES_TO":
13382
- return import_types43.EdgeType.PUBLISHES_TO;
14048
+ return import_types44.EdgeType.PUBLISHES_TO;
13383
14049
  case "CONSUMES_FROM":
13384
- return import_types43.EdgeType.CONSUMES_FROM;
14050
+ return import_types44.EdgeType.CONSUMES_FROM;
13385
14051
  default:
13386
- return import_types43.EdgeType.CALLS;
14052
+ return import_types44.EdgeType.CALLS;
13387
14053
  }
13388
14054
  }
13389
14055
  function isAwsKind(kind) {
@@ -13433,6 +14099,11 @@ async function addExternalEndpointEdges(graph, services) {
13433
14099
  } catch (err) {
13434
14100
  recordExtractionError("laravel eloquent extraction", file.path, err);
13435
14101
  }
14102
+ try {
14103
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14104
+ } catch (err) {
14105
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14106
+ }
13436
14107
  }
13437
14108
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13438
14109
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -13443,7 +14114,7 @@ async function addExternalEndpointEdges(graph, services) {
13443
14114
  if (!graph.hasNode(ep.infraId)) {
13444
14115
  const node = {
13445
14116
  id: ep.infraId,
13446
- type: import_types43.NodeType.InfraNode,
14117
+ type: import_types44.NodeType.InfraNode,
13447
14118
  name: ep.name,
13448
14119
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
13449
14120
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -13456,21 +14127,21 @@ async function addExternalEndpointEdges(graph, services) {
13456
14127
  }
13457
14128
  if (ep.columns && ep.columns.length > 0) {
13458
14129
  const node = graph.getNodeAttributes(ep.infraId);
13459
- if (node.type === import_types43.NodeType.InfraNode) {
14130
+ if (node.type === import_types44.NodeType.InfraNode) {
13460
14131
  graph.replaceNodeAttributes(ep.infraId, {
13461
14132
  ...node,
13462
14133
  columns: foldColumns(
13463
14134
  node.columns,
13464
14135
  ep.columns,
13465
- import_types43.Provenance.EXTRACTED,
13466
- (0, import_types43.confidenceForExtracted)(ep.confidenceKind)
14136
+ import_types44.Provenance.EXTRACTED,
14137
+ (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
13467
14138
  )
13468
14139
  });
13469
14140
  }
13470
14141
  }
13471
14142
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
13472
14143
  const node = graph.getNodeAttributes(ep.infraId);
13473
- if (node.type === import_types43.NodeType.InfraNode) {
14144
+ if (node.type === import_types44.NodeType.InfraNode) {
13474
14145
  graph.replaceNodeAttributes(ep.infraId, {
13475
14146
  ...node,
13476
14147
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -13478,7 +14149,7 @@ async function addExternalEndpointEdges(graph, services) {
13478
14149
  }
13479
14150
  }
13480
14151
  const edgeType = edgeTypeFromEndpoint(ep);
13481
- const confidence = (0, import_types43.confidenceForExtracted)(ep.confidenceKind);
14152
+ const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
13482
14153
  const relFile = toPosix(ep.evidence.file);
13483
14154
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
13484
14155
  graph,
@@ -13488,7 +14159,7 @@ async function addExternalEndpointEdges(graph, services) {
13488
14159
  );
13489
14160
  nodesAdded += n;
13490
14161
  edgesAdded += e;
13491
- if (!(0, import_types43.passesExtractedFloor)(confidence)) {
14162
+ if (!(0, import_types44.passesExtractedFloor)(confidence)) {
13492
14163
  noteExtractedDropped({
13493
14164
  source: fileNodeId,
13494
14165
  target: ep.infraId,
@@ -13508,7 +14179,7 @@ async function addExternalEndpointEdges(graph, services) {
13508
14179
  source: fileNodeId,
13509
14180
  target: ep.infraId,
13510
14181
  type: edgeType,
13511
- provenance: import_types43.Provenance.EXTRACTED,
14182
+ provenance: import_types44.Provenance.EXTRACTED,
13512
14183
  confidence,
13513
14184
  evidence: ep.evidence
13514
14185
  };
@@ -13531,7 +14202,7 @@ async function addCallEdges(graph, services) {
13531
14202
 
13532
14203
  // src/extract/table-edges.ts
13533
14204
  init_cjs_shims();
13534
- var import_types44 = require("@neat.is/types");
14205
+ var import_types45 = require("@neat.is/types");
13535
14206
  async function addTableEdges(graph, services) {
13536
14207
  let nodesAdded = 0;
13537
14208
  let edgesAdded = 0;
@@ -13559,20 +14230,20 @@ async function addTableEdges(graph, services) {
13559
14230
  }
13560
14231
  refs.push(...modelRefs);
13561
14232
  for (const ref of refs) {
13562
- const childId = (0, import_types44.infraId)("sql-table", ref.childTable);
13563
- 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);
13564
14235
  if (childId === parentId) continue;
13565
14236
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13566
14237
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13567
- 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);
13568
14239
  if (graph.hasEdge(edgeId)) continue;
13569
14240
  const edge = {
13570
14241
  id: edgeId,
13571
14242
  source: childId,
13572
14243
  target: parentId,
13573
- type: import_types44.EdgeType.REFERENCES,
13574
- provenance: import_types44.Provenance.EXTRACTED,
13575
- 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"),
13576
14247
  evidence: ref.evidence
13577
14248
  };
13578
14249
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13585,7 +14256,7 @@ function ensureTableNode(graph, id, name) {
13585
14256
  if (graph.hasNode(id)) return 0;
13586
14257
  const node = {
13587
14258
  id,
13588
- type: import_types44.NodeType.InfraNode,
14259
+ type: import_types45.NodeType.InfraNode,
13589
14260
  name,
13590
14261
  provider: "self",
13591
14262
  kind: "sql-table"
@@ -13599,16 +14270,16 @@ init_cjs_shims();
13599
14270
 
13600
14271
  // src/extract/infra/docker-compose.ts
13601
14272
  init_cjs_shims();
13602
- var import_node_path55 = __toESM(require("path"), 1);
13603
- var import_types46 = require("@neat.is/types");
14273
+ var import_node_path57 = __toESM(require("path"), 1);
14274
+ var import_types47 = require("@neat.is/types");
13604
14275
 
13605
14276
  // src/extract/infra/shared.ts
13606
14277
  init_cjs_shims();
13607
- var import_types45 = require("@neat.is/types");
14278
+ var import_types46 = require("@neat.is/types");
13608
14279
  function makeInfraNode(kind, name, provider = "self", extras) {
13609
14280
  return {
13610
- id: (0, import_types45.infraId)(kind, name),
13611
- type: import_types45.NodeType.InfraNode,
14281
+ id: (0, import_types46.infraId)(kind, name),
14282
+ type: import_types46.NodeType.InfraNode,
13612
14283
  name,
13613
14284
  provider,
13614
14285
  kind,
@@ -13652,8 +14323,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13652
14323
  source: anchorId,
13653
14324
  target: node.id,
13654
14325
  type: edgeType,
13655
- provenance: import_types45.Provenance.EXTRACTED,
13656
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14326
+ provenance: import_types46.Provenance.EXTRACTED,
14327
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13657
14328
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13658
14329
  };
13659
14330
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13670,7 +14341,7 @@ function dependsOnList(value) {
13670
14341
  }
13671
14342
  function serviceNameToServiceNode(name, services) {
13672
14343
  for (const s of services) {
13673
- 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;
13674
14345
  }
13675
14346
  return null;
13676
14347
  }
@@ -13679,7 +14350,7 @@ async function addComposeInfra(graph, scanPath, services) {
13679
14350
  let edgesAdded = 0;
13680
14351
  let composePath = null;
13681
14352
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13682
- const abs = import_node_path55.default.join(scanPath, name);
14353
+ const abs = import_node_path57.default.join(scanPath, name);
13683
14354
  if (await exists(abs)) {
13684
14355
  composePath = abs;
13685
14356
  break;
@@ -13692,13 +14363,13 @@ async function addComposeInfra(graph, scanPath, services) {
13692
14363
  } catch (err) {
13693
14364
  recordExtractionError(
13694
14365
  "infra docker-compose",
13695
- import_node_path55.default.relative(scanPath, composePath),
14366
+ import_node_path57.default.relative(scanPath, composePath),
13696
14367
  err
13697
14368
  );
13698
14369
  return { nodesAdded, edgesAdded };
13699
14370
  }
13700
14371
  if (!compose?.services) return { nodesAdded, edgesAdded };
13701
- 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("/");
13702
14373
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13703
14374
  for (const [composeName, svc] of Object.entries(compose.services)) {
13704
14375
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13720,15 +14391,15 @@ async function addComposeInfra(graph, scanPath, services) {
13720
14391
  for (const dep of dependsOnList(svc.depends_on)) {
13721
14392
  const targetId = composeNameToNodeId.get(dep);
13722
14393
  if (!targetId) continue;
13723
- 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);
13724
14395
  if (graph.hasEdge(edgeId)) continue;
13725
14396
  const edge = {
13726
14397
  id: edgeId,
13727
14398
  source: sourceId,
13728
14399
  target: targetId,
13729
- type: import_types46.EdgeType.DEPENDS_ON,
13730
- provenance: import_types46.Provenance.EXTRACTED,
13731
- 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"),
13732
14403
  evidence: { file: evidenceFile }
13733
14404
  };
13734
14405
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13740,9 +14411,9 @@ async function addComposeInfra(graph, scanPath, services) {
13740
14411
 
13741
14412
  // src/extract/infra/dockerfile.ts
13742
14413
  init_cjs_shims();
13743
- var import_node_path56 = __toESM(require("path"), 1);
13744
- var import_node_fs24 = require("fs");
13745
- 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");
13746
14417
  function readDockerfile(content) {
13747
14418
  let image = null;
13748
14419
  const ports = [];
@@ -13771,15 +14442,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13771
14442
  let nodesAdded = 0;
13772
14443
  let edgesAdded = 0;
13773
14444
  for (const service of services) {
13774
- const dockerfilePath = import_node_path56.default.join(service.dir, "Dockerfile");
14445
+ const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
13775
14446
  if (!await exists(dockerfilePath)) continue;
13776
14447
  let content;
13777
14448
  try {
13778
- content = await import_node_fs24.promises.readFile(dockerfilePath, "utf8");
14449
+ content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
13779
14450
  } catch (err) {
13780
14451
  recordExtractionError(
13781
14452
  "infra dockerfile",
13782
- import_node_path56.default.relative(scanPath, dockerfilePath),
14453
+ import_node_path58.default.relative(scanPath, dockerfilePath),
13783
14454
  err
13784
14455
  );
13785
14456
  continue;
@@ -13791,8 +14462,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13791
14462
  graph.addNode(node.id, node);
13792
14463
  nodesAdded++;
13793
14464
  }
13794
- const relDockerfile = toPosix(import_node_path56.default.relative(service.dir, dockerfilePath));
13795
- 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));
13796
14467
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13797
14468
  graph,
13798
14469
  service.pkg.name,
@@ -13801,15 +14472,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13801
14472
  );
13802
14473
  nodesAdded += fn;
13803
14474
  edgesAdded += fe;
13804
- 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);
13805
14476
  if (!graph.hasEdge(edgeId)) {
13806
14477
  const edge = {
13807
14478
  id: edgeId,
13808
14479
  source: fileNodeId,
13809
14480
  target: node.id,
13810
- type: import_types47.EdgeType.RUNS_ON,
13811
- provenance: import_types47.Provenance.EXTRACTED,
13812
- 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"),
13813
14484
  evidence: {
13814
14485
  file: evidenceFile,
13815
14486
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13824,15 +14495,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13824
14495
  graph.addNode(portNode.id, portNode);
13825
14496
  nodesAdded++;
13826
14497
  }
13827
- 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);
13828
14499
  if (graph.hasEdge(portEdgeId)) continue;
13829
14500
  const portEdge = {
13830
14501
  id: portEdgeId,
13831
14502
  source: fileNodeId,
13832
14503
  target: portNode.id,
13833
- type: import_types47.EdgeType.CONNECTS_TO,
13834
- provenance: import_types47.Provenance.EXTRACTED,
13835
- 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"),
13836
14507
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13837
14508
  };
13838
14509
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13844,23 +14515,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13844
14515
 
13845
14516
  // src/extract/infra/terraform.ts
13846
14517
  init_cjs_shims();
13847
- var import_node_fs25 = require("fs");
13848
- var import_node_path57 = __toESM(require("path"), 1);
13849
- 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");
13850
14521
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13851
14522
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13852
14523
  async function walkTfFiles(start, depth = 0, max = 5) {
13853
14524
  if (depth > max) return [];
13854
14525
  const out = [];
13855
- 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(() => []);
13856
14527
  for (const entry2 of entries) {
13857
14528
  if (entry2.isDirectory()) {
13858
14529
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
13859
- const child = import_node_path57.default.join(start, entry2.name);
14530
+ const child = import_node_path59.default.join(start, entry2.name);
13860
14531
  if (await isPythonVenvDir(child)) continue;
13861
14532
  out.push(...await walkTfFiles(child, depth + 1, max));
13862
14533
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
13863
- out.push(import_node_path57.default.join(start, entry2.name));
14534
+ out.push(import_node_path59.default.join(start, entry2.name));
13864
14535
  }
13865
14536
  }
13866
14537
  return out;
@@ -13891,8 +14562,8 @@ async function addTerraformResources(graph, scanPath) {
13891
14562
  let edgesAdded = 0;
13892
14563
  const files = await walkTfFiles(scanPath);
13893
14564
  for (const file of files) {
13894
- const content = await import_node_fs25.promises.readFile(file, "utf8");
13895
- 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));
13896
14567
  const resources = [];
13897
14568
  const byKey = /* @__PURE__ */ new Map();
13898
14569
  RESOURCE_RE.lastIndex = 0;
@@ -13927,16 +14598,16 @@ async function addTerraformResources(graph, scanPath) {
13927
14598
  if (!target) continue;
13928
14599
  if (seen.has(target.nodeId)) continue;
13929
14600
  seen.add(target.nodeId);
13930
- 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);
13931
14602
  if (graph.hasEdge(edgeId)) continue;
13932
14603
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13933
14604
  const edge = {
13934
14605
  id: edgeId,
13935
14606
  source: resource.nodeId,
13936
14607
  target: target.nodeId,
13937
- type: import_types48.EdgeType.DEPENDS_ON,
13938
- provenance: import_types48.Provenance.EXTRACTED,
13939
- 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"),
13940
14611
  evidence: { file: evidenceFile, line, snippet: key }
13941
14612
  };
13942
14613
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13949,8 +14620,8 @@ async function addTerraformResources(graph, scanPath) {
13949
14620
 
13950
14621
  // src/extract/infra/k8s.ts
13951
14622
  init_cjs_shims();
13952
- var import_node_fs26 = require("fs");
13953
- var import_node_path58 = __toESM(require("path"), 1);
14623
+ var import_node_fs27 = require("fs");
14624
+ var import_node_path60 = __toESM(require("path"), 1);
13954
14625
  var import_yaml3 = require("yaml");
13955
14626
  var K8S_KIND_TO_INFRA_KIND = {
13956
14627
  Service: "k8s-service",
@@ -13964,15 +14635,15 @@ var K8S_KIND_TO_INFRA_KIND = {
13964
14635
  async function walkYamlFiles2(start, depth = 0, max = 5) {
13965
14636
  if (depth > max) return [];
13966
14637
  const out = [];
13967
- 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(() => []);
13968
14639
  for (const entry2 of entries) {
13969
14640
  if (entry2.isDirectory()) {
13970
14641
  if (IGNORED_DIRS.has(entry2.name)) continue;
13971
- const child = import_node_path58.default.join(start, entry2.name);
14642
+ const child = import_node_path60.default.join(start, entry2.name);
13972
14643
  if (await isPythonVenvDir(child)) continue;
13973
14644
  out.push(...await walkYamlFiles2(child, depth + 1, max));
13974
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path58.default.extname(entry2.name))) {
13975
- 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));
13976
14647
  }
13977
14648
  }
13978
14649
  return out;
@@ -13981,7 +14652,7 @@ async function addK8sResources(graph, scanPath) {
13981
14652
  let nodesAdded = 0;
13982
14653
  const files = await walkYamlFiles2(scanPath);
13983
14654
  for (const file of files) {
13984
- const content = await import_node_fs26.promises.readFile(file, "utf8");
14655
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
13985
14656
  let docs;
13986
14657
  try {
13987
14658
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14005,16 +14676,16 @@ async function addK8sResources(graph, scanPath) {
14005
14676
 
14006
14677
  // src/extract/infra/cloudflare.ts
14007
14678
  init_cjs_shims();
14008
- var import_node_fs27 = require("fs");
14009
- var import_node_path59 = __toESM(require("path"), 1);
14679
+ var import_node_fs28 = require("fs");
14680
+ var import_node_path61 = __toESM(require("path"), 1);
14010
14681
  var import_smol_toml3 = require("smol-toml");
14011
- var import_types49 = require("@neat.is/types");
14682
+ var import_types50 = require("@neat.is/types");
14012
14683
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14013
14684
  async function readWranglerConfig(dir) {
14014
14685
  for (const filename of WRANGLER_FILENAMES) {
14015
- const abs = import_node_path59.default.join(dir, filename);
14686
+ const abs = import_node_path61.default.join(dir, filename);
14016
14687
  if (!await exists(abs)) continue;
14017
- const raw = await import_node_fs27.promises.readFile(abs, "utf8");
14688
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14018
14689
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14019
14690
  return { config, relFile: filename, raw };
14020
14691
  }
@@ -14056,8 +14727,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14056
14727
  source: anchorId,
14057
14728
  target: node.id,
14058
14729
  type: edgeType,
14059
- provenance: import_types49.Provenance.EXTRACTED,
14060
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14730
+ provenance: import_types50.Provenance.EXTRACTED,
14731
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14061
14732
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14062
14733
  };
14063
14734
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14075,11 +14746,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14075
14746
  try {
14076
14747
  read = await readWranglerConfig(service.dir);
14077
14748
  } catch (err) {
14078
- 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);
14079
14750
  continue;
14080
14751
  }
14081
14752
  if (!read || !read.config.name) continue;
14082
- 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)));
14083
14754
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14084
14755
  }
14085
14756
  for (const worker of discovered) {
@@ -14091,7 +14762,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14091
14762
  }
14092
14763
  let anchorId = service.node.id;
14093
14764
  if (config.main) {
14094
- const entryRelPath = toPosix(import_node_path59.default.normalize(config.main));
14765
+ const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
14095
14766
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14096
14767
  graph,
14097
14768
  service.pkg.name,
@@ -14118,15 +14789,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14118
14789
  nodesAdded++;
14119
14790
  }
14120
14791
  if (runtimeNode.id !== anchorId) {
14121
- 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);
14122
14793
  if (!graph.hasEdge(runsOnId)) {
14123
14794
  const edge = {
14124
14795
  id: runsOnId,
14125
14796
  source: anchorId,
14126
14797
  target: runtimeNode.id,
14127
- type: import_types49.EdgeType.RUNS_ON,
14128
- provenance: import_types49.Provenance.EXTRACTED,
14129
- 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"),
14130
14801
  evidence: {
14131
14802
  file: evidenceFile,
14132
14803
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14140,7 +14811,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14140
14811
  const result = addResourceEdge(
14141
14812
  graph,
14142
14813
  anchorId,
14143
- import_types49.EdgeType.CONNECTS_TO,
14814
+ import_types50.EdgeType.CONNECTS_TO,
14144
14815
  "cloudflare-route",
14145
14816
  route,
14146
14817
  evidenceFile,
@@ -14164,7 +14835,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14164
14835
  const result = addResourceEdge(
14165
14836
  graph,
14166
14837
  anchorId,
14167
- import_types49.EdgeType.DEPENDS_ON,
14838
+ import_types50.EdgeType.DEPENDS_ON,
14168
14839
  group.kind,
14169
14840
  name,
14170
14841
  evidenceFile,
@@ -14178,7 +14849,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14178
14849
  const result = addResourceEdge(
14179
14850
  graph,
14180
14851
  anchorId,
14181
- import_types49.EdgeType.DEPENDS_ON,
14852
+ import_types50.EdgeType.DEPENDS_ON,
14182
14853
  "cloudflare-cron",
14183
14854
  cron,
14184
14855
  evidenceFile,
@@ -14191,7 +14862,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14191
14862
  const result = addResourceEdge(
14192
14863
  graph,
14193
14864
  anchorId,
14194
- import_types49.EdgeType.DEPENDS_ON,
14865
+ import_types50.EdgeType.DEPENDS_ON,
14195
14866
  "cloudflare-env-var",
14196
14867
  varName,
14197
14868
  evidenceFile,
@@ -14204,15 +14875,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14204
14875
  if (!svc.service) continue;
14205
14876
  const target = workerIndex.get(svc.service);
14206
14877
  if (target && target.anchorId !== anchorId) {
14207
- 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);
14208
14879
  if (!graph.hasEdge(edgeId)) {
14209
14880
  const edge = {
14210
14881
  id: edgeId,
14211
14882
  source: anchorId,
14212
14883
  target: target.anchorId,
14213
- type: import_types49.EdgeType.CALLS,
14214
- provenance: import_types49.Provenance.EXTRACTED,
14215
- 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"),
14216
14887
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14217
14888
  };
14218
14889
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14223,7 +14894,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14223
14894
  const result = addResourceEdge(
14224
14895
  graph,
14225
14896
  anchorId,
14226
- import_types49.EdgeType.DEPENDS_ON,
14897
+ import_types50.EdgeType.DEPENDS_ON,
14227
14898
  "cloudflare-service-binding",
14228
14899
  svc.service,
14229
14900
  evidenceFile,
@@ -14238,24 +14909,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14238
14909
 
14239
14910
  // src/extract/infra/vercel.ts
14240
14911
  init_cjs_shims();
14241
- var import_node_fs28 = require("fs");
14242
- var import_node_path60 = __toESM(require("path"), 1);
14243
- 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");
14244
14915
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14245
14916
  async function readVercelConfig(dir) {
14246
14917
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14247
- const abs = import_node_path60.default.join(dir, filename);
14918
+ const abs = import_node_path62.default.join(dir, filename);
14248
14919
  if (!await exists(abs)) continue;
14249
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14920
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14250
14921
  const config = JSON.parse(maskCommentsInSource(raw));
14251
14922
  return { config, relFile: filename, raw };
14252
14923
  }
14253
14924
  return null;
14254
14925
  }
14255
14926
  async function readLinkedProjectName(dir) {
14256
- const abs = import_node_path60.default.join(dir, ".vercel", "project.json");
14927
+ const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
14257
14928
  if (!await exists(abs)) return void 0;
14258
- 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"));
14259
14930
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14260
14931
  }
14261
14932
  function routeSource(route) {
@@ -14271,7 +14942,7 @@ async function addVercelServices(graph, services, scanPath) {
14271
14942
  read = await readVercelConfig(service.dir);
14272
14943
  projectName = await readLinkedProjectName(service.dir);
14273
14944
  } catch (err) {
14274
- 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);
14275
14946
  continue;
14276
14947
  }
14277
14948
  if (!read && !projectName) continue;
@@ -14287,7 +14958,7 @@ async function addVercelServices(graph, services, scanPath) {
14287
14958
  const anchorId = service.node.id;
14288
14959
  if (!read) continue;
14289
14960
  const { config, relFile, raw } = read;
14290
- 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)));
14291
14962
  const add = (edgeType, kind, name) => {
14292
14963
  if (!name) return;
14293
14964
  const result = emitPlatformResourceEdge(
@@ -14303,12 +14974,12 @@ async function addVercelServices(graph, services, scanPath) {
14303
14974
  nodesAdded += result.nodesAdded;
14304
14975
  edgesAdded += result.edgesAdded;
14305
14976
  };
14306
- add(import_types50.EdgeType.RUNS_ON, "vercel", "vercel");
14307
- for (const cron of config.crons ?? []) add(import_types50.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14308
- for (const varName of Object.keys(config.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14309
- 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);
14310
14981
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14311
- add(import_types50.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14982
+ add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14312
14983
  }
14313
14984
  }
14314
14985
  return { nodesAdded, edgesAdded };
@@ -14316,16 +14987,16 @@ async function addVercelServices(graph, services, scanPath) {
14316
14987
 
14317
14988
  // src/extract/infra/railway.ts
14318
14989
  init_cjs_shims();
14319
- var import_node_fs29 = require("fs");
14320
- var import_node_path61 = __toESM(require("path"), 1);
14990
+ var import_node_fs30 = require("fs");
14991
+ var import_node_path63 = __toESM(require("path"), 1);
14321
14992
  var import_smol_toml4 = require("smol-toml");
14322
- var import_types51 = require("@neat.is/types");
14993
+ var import_types52 = require("@neat.is/types");
14323
14994
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14324
14995
  async function readRailwayConfig(dir) {
14325
14996
  for (const filename of RAILWAY_FILENAMES) {
14326
- const abs = import_node_path61.default.join(dir, filename);
14997
+ const abs = import_node_path63.default.join(dir, filename);
14327
14998
  if (!await exists(abs)) continue;
14328
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14999
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14329
15000
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14330
15001
  return { config, relFile: filename, raw };
14331
15002
  }
@@ -14339,7 +15010,7 @@ async function addRailwayServices(graph, services, scanPath) {
14339
15010
  try {
14340
15011
  read = await readRailwayConfig(service.dir);
14341
15012
  } catch (err) {
14342
- 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);
14343
15014
  continue;
14344
15015
  }
14345
15016
  if (!read) continue;
@@ -14349,7 +15020,7 @@ async function addRailwayServices(graph, services, scanPath) {
14349
15020
  }
14350
15021
  const anchorId = service.node.id;
14351
15022
  const { config, relFile, raw } = read;
14352
- 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)));
14353
15024
  const add = (edgeType, kind, name) => {
14354
15025
  if (!name) return;
14355
15026
  const result = emitPlatformResourceEdge(
@@ -14365,24 +15036,24 @@ async function addRailwayServices(graph, services, scanPath) {
14365
15036
  nodesAdded += result.nodesAdded;
14366
15037
  edgesAdded += result.edgesAdded;
14367
15038
  };
14368
- add(import_types51.EdgeType.RUNS_ON, "railway", "railway");
14369
- add(import_types51.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14370
- 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);
14371
15042
  }
14372
15043
  return { nodesAdded, edgesAdded };
14373
15044
  }
14374
15045
 
14375
15046
  // src/extract/infra/supabase.ts
14376
15047
  init_cjs_shims();
14377
- var import_node_fs30 = require("fs");
14378
- var import_node_path62 = __toESM(require("path"), 1);
15048
+ var import_node_fs31 = require("fs");
15049
+ var import_node_path64 = __toESM(require("path"), 1);
14379
15050
  var import_smol_toml5 = require("smol-toml");
14380
- var import_types52 = require("@neat.is/types");
15051
+ var import_types53 = require("@neat.is/types");
14381
15052
  async function readSupabaseConfig(dir) {
14382
- const relFile = import_node_path62.default.join("supabase", "config.toml");
14383
- 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);
14384
15055
  if (!await exists(abs)) return null;
14385
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15056
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14386
15057
  const config = (0, import_smol_toml5.parse)(raw);
14387
15058
  return { config, relFile, raw };
14388
15059
  }
@@ -14394,7 +15065,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14394
15065
  try {
14395
15066
  read = await readSupabaseConfig(service.dir);
14396
15067
  } catch (err) {
14397
- 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);
14398
15069
  continue;
14399
15070
  }
14400
15071
  if (!read) continue;
@@ -14409,7 +15080,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14409
15080
  });
14410
15081
  }
14411
15082
  const anchorId = service.node.id;
14412
- 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)));
14413
15084
  const add = (edgeType, kind, name) => {
14414
15085
  if (!name) return;
14415
15086
  const result = emitPlatformResourceEdge(
@@ -14425,10 +15096,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14425
15096
  nodesAdded += result.nodesAdded;
14426
15097
  edgesAdded += result.edgesAdded;
14427
15098
  };
14428
- add(import_types52.EdgeType.RUNS_ON, "supabase", "supabase");
14429
- for (const fn of Object.keys(config.functions ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "supabase-function", fn);
14430
- if (config.storage) add(import_types52.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14431
- 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");
14432
15103
  }
14433
15104
  return { nodesAdded, edgesAdded };
14434
15105
  }
@@ -14451,14 +15122,14 @@ async function addInfra(graph, scanPath, services) {
14451
15122
 
14452
15123
  // src/extract/zod-shapes.ts
14453
15124
  init_cjs_shims();
14454
- var import_node_path63 = __toESM(require("path"), 1);
14455
- 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);
14456
15127
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
14457
- var import_types53 = require("@neat.is/types");
15128
+ var import_types54 = require("@neat.is/types");
14458
15129
  var ZOD_IMPORT_RE = /\bzod\b/;
14459
15130
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
14460
15131
  function parserForExt4(ext) {
14461
- const p = new import_tree_sitter16.default();
15132
+ const p = new import_tree_sitter17.default();
14462
15133
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
14463
15134
  return p;
14464
15135
  }
@@ -14546,7 +15217,7 @@ function topLevelSchemas(root) {
14546
15217
  }
14547
15218
  function zodShapesFromFile(file, serviceDir) {
14548
15219
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14549
- 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);
14550
15221
  const out = [];
14551
15222
  const seen = /* @__PURE__ */ new Set();
14552
15223
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14560,11 +15231,11 @@ function zodShapesFromFile(file, serviceDir) {
14560
15231
  seen.add(name);
14561
15232
  const line = call.startPosition.row + 1;
14562
15233
  out.push({
14563
- infraId: (0, import_types53.infraId)("zod-schema", name),
15234
+ infraId: (0, import_types54.infraId)("zod-schema", name),
14564
15235
  name,
14565
15236
  fields,
14566
15237
  evidence: {
14567
- file: import_node_path63.default.relative(serviceDir, file.path),
15238
+ file: import_node_path65.default.relative(serviceDir, file.path),
14568
15239
  line,
14569
15240
  snippet: snippet(file.content, line)
14570
15241
  }
@@ -14595,7 +15266,7 @@ async function addZodShapes(graph, services) {
14595
15266
  if (!graph.hasNode(shape.infraId)) {
14596
15267
  const node = {
14597
15268
  id: shape.infraId,
14598
- type: import_types53.NodeType.InfraNode,
15269
+ type: import_types54.NodeType.InfraNode,
14599
15270
  name: shape.name,
14600
15271
  provider: "self",
14601
15272
  kind: "zod-schema"
@@ -14605,14 +15276,14 @@ async function addZodShapes(graph, services) {
14605
15276
  }
14606
15277
  if (shape.fields.length > 0) {
14607
15278
  const node = graph.getNodeAttributes(shape.infraId);
14608
- if (node.type === import_types53.NodeType.InfraNode) {
15279
+ if (node.type === import_types54.NodeType.InfraNode) {
14609
15280
  graph.replaceNodeAttributes(shape.infraId, {
14610
15281
  ...node,
14611
15282
  columns: foldColumns(
14612
15283
  node.columns,
14613
15284
  shape.fields,
14614
- import_types53.Provenance.EXTRACTED,
14615
- (0, import_types53.confidenceForExtracted)("structural")
15285
+ import_types54.Provenance.EXTRACTED,
15286
+ (0, import_types54.confidenceForExtracted)("structural")
14616
15287
  )
14617
15288
  });
14618
15289
  }
@@ -14626,15 +15297,15 @@ async function addZodShapes(graph, services) {
14626
15297
  );
14627
15298
  nodesAdded += n;
14628
15299
  edgesAdded += e;
14629
- 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);
14630
15301
  if (!graph.hasEdge(edgeId)) {
14631
15302
  const edge = {
14632
15303
  id: edgeId,
14633
15304
  source: fileNodeId,
14634
15305
  target: shape.infraId,
14635
- type: import_types53.EdgeType.CONTAINS,
14636
- provenance: import_types53.Provenance.EXTRACTED,
14637
- 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"),
14638
15309
  evidence: shape.evidence
14639
15310
  };
14640
15311
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14648,7 +15319,7 @@ async function addZodShapes(graph, services) {
14648
15319
 
14649
15320
  // src/extract/firestore-rules.ts
14650
15321
  init_cjs_shims();
14651
- var import_types54 = require("@neat.is/types");
15322
+ var import_types55 = require("@neat.is/types");
14652
15323
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14653
15324
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14654
15325
  function stripComments(src) {
@@ -14788,7 +15459,7 @@ async function addFirestoreRules(graph, services) {
14788
15459
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14789
15460
  graph.forEachNode((id, attrs) => {
14790
15461
  const node = attrs;
14791
- if (node.type !== import_types54.NodeType.InfraNode) return;
15462
+ if (node.type !== import_types55.NodeType.InfraNode) return;
14792
15463
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14793
15464
  const fields = guards.get(collectionKeyFromName(node.name));
14794
15465
  if (!fields || fields.size === 0) return;
@@ -14801,17 +15472,17 @@ async function addFirestoreRules(graph, services) {
14801
15472
  }
14802
15473
 
14803
15474
  // src/extract/index.ts
14804
- var import_node_path65 = __toESM(require("path"), 1);
15475
+ var import_node_path67 = __toESM(require("path"), 1);
14805
15476
 
14806
15477
  // src/extract/retire.ts
14807
15478
  init_cjs_shims();
14808
- var import_node_fs31 = require("fs");
14809
- var import_node_path64 = __toESM(require("path"), 1);
14810
- 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");
14811
15482
  function dropOrphanedFileNodes(graph) {
14812
15483
  const orphans = [];
14813
15484
  graph.forEachNode((id, attrs) => {
14814
- if (attrs.type !== import_types55.NodeType.FileNode) return;
15485
+ if (attrs.type !== import_types56.NodeType.FileNode) return;
14815
15486
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14816
15487
  orphans.push(id);
14817
15488
  }
@@ -14824,14 +15495,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14824
15495
  const bases = [scanPath, ...serviceDirs];
14825
15496
  graph.forEachEdge((id, attrs) => {
14826
15497
  const edge = attrs;
14827
- if (edge.provenance !== import_types55.Provenance.EXTRACTED) return;
15498
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
14828
15499
  const evidenceFile = edge.evidence?.file;
14829
15500
  if (!evidenceFile) return;
14830
- if (import_node_path64.default.isAbsolute(evidenceFile)) {
14831
- 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);
14832
15503
  return;
14833
15504
  }
14834
- 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)));
14835
15506
  if (!found) toDrop.push(id);
14836
15507
  });
14837
15508
  for (const id of toDrop) graph.dropEdge(id);
@@ -14888,7 +15559,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14888
15559
  }
14889
15560
  const droppedEntries = drainDroppedExtracted();
14890
15561
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14891
- 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");
14892
15563
  try {
14893
15564
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14894
15565
  } catch (err) {
@@ -14922,9 +15593,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14922
15593
 
14923
15594
  // src/persist.ts
14924
15595
  init_cjs_shims();
14925
- var import_node_fs32 = require("fs");
14926
- var import_node_path66 = __toESM(require("path"), 1);
14927
- 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");
14928
15599
  var SCHEMA_VERSION = 7;
14929
15600
  function migrateV1ToV2(payload) {
14930
15601
  const nodes = payload.graph.nodes;
@@ -14948,7 +15619,7 @@ function migrateV5ToV6(payload) {
14948
15619
  if (Array.isArray(nodes)) {
14949
15620
  for (const node of nodes) {
14950
15621
  const attrs = node.attributes;
14951
- if (!attrs || attrs.type !== import_types56.NodeType.InfraNode) continue;
15622
+ if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
14952
15623
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14953
15624
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14954
15625
  }
@@ -14964,12 +15635,12 @@ function migrateV2ToV3(payload) {
14964
15635
  for (const edge of edges) {
14965
15636
  const attrs = edge.attributes;
14966
15637
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
14967
- attrs.provenance = import_types56.Provenance.OBSERVED;
15638
+ attrs.provenance = import_types57.Provenance.OBSERVED;
14968
15639
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
14969
15640
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
14970
15641
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
14971
15642
  if (type && source && target) {
14972
- const newId = (0, import_types56.observedEdgeId)(source, target, type);
15643
+ const newId = (0, import_types57.observedEdgeId)(source, target, type);
14973
15644
  attrs.id = newId;
14974
15645
  if (edge.key) edge.key = newId;
14975
15646
  }
@@ -14978,7 +15649,7 @@ function migrateV2ToV3(payload) {
14978
15649
  return { ...payload, schemaVersion: 3 };
14979
15650
  }
14980
15651
  async function ensureDir(filePath) {
14981
- 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 });
14982
15653
  }
14983
15654
  async function saveGraphToDisk(graph, outPath) {
14984
15655
  await ensureDir(outPath);
@@ -14988,13 +15659,13 @@ async function saveGraphToDisk(graph, outPath) {
14988
15659
  graph: graph.export()
14989
15660
  };
14990
15661
  const tmp = `${outPath}.tmp`;
14991
- await import_node_fs32.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
14992
- 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);
14993
15664
  }
14994
15665
  async function loadGraphFromDisk(graph, outPath) {
14995
15666
  let raw;
14996
15667
  try {
14997
- raw = await import_node_fs32.promises.readFile(outPath, "utf8");
15668
+ raw = await import_node_fs33.promises.readFile(outPath, "utf8");
14998
15669
  } catch (err) {
14999
15670
  if (err.code === "ENOENT") return;
15000
15671
  throw err;
@@ -15068,23 +15739,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
15068
15739
 
15069
15740
  // src/projects.ts
15070
15741
  init_cjs_shims();
15071
- var import_node_path67 = __toESM(require("path"), 1);
15742
+ var import_node_path69 = __toESM(require("path"), 1);
15072
15743
  function pathsForProject(project, baseDir) {
15073
15744
  if (project === DEFAULT_PROJECT) {
15074
15745
  return {
15075
- snapshotPath: import_node_path67.default.join(baseDir, "graph.json"),
15076
- errorsPath: import_node_path67.default.join(baseDir, "errors.ndjson"),
15077
- staleEventsPath: import_node_path67.default.join(baseDir, "stale-events.ndjson"),
15078
- embeddingsCachePath: import_node_path67.default.join(baseDir, "embeddings.json"),
15079
- 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")
15080
15751
  };
15081
15752
  }
15082
15753
  return {
15083
- snapshotPath: import_node_path67.default.join(baseDir, `${project}.json`),
15084
- errorsPath: import_node_path67.default.join(baseDir, `errors.${project}.ndjson`),
15085
- staleEventsPath: import_node_path67.default.join(baseDir, `stale-events.${project}.ndjson`),
15086
- embeddingsCachePath: import_node_path67.default.join(baseDir, `embeddings.${project}.json`),
15087
- 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`)
15088
15759
  };
15089
15760
  }
15090
15761
  var Projects = class {
@@ -15122,19 +15793,19 @@ var Projects = class {
15122
15793
  init_cjs_shims();
15123
15794
  var import_fastify2 = __toESM(require("fastify"), 1);
15124
15795
  var import_cors = __toESM(require("@fastify/cors"), 1);
15125
- var import_types91 = require("@neat.is/types");
15796
+ var import_types92 = require("@neat.is/types");
15126
15797
 
15127
15798
  // src/extend/index.ts
15128
15799
  init_cjs_shims();
15129
- var import_node_fs34 = require("fs");
15130
- var import_node_path69 = __toESM(require("path"), 1);
15800
+ var import_node_fs35 = require("fs");
15801
+ var import_node_path71 = __toESM(require("path"), 1);
15131
15802
  var import_node_os2 = __toESM(require("os"), 1);
15132
15803
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
15133
15804
 
15134
15805
  // src/installers/package-manager.ts
15135
15806
  init_cjs_shims();
15136
- var import_node_fs33 = require("fs");
15137
- var import_node_path68 = __toESM(require("path"), 1);
15807
+ var import_node_fs34 = require("fs");
15808
+ var import_node_path70 = __toESM(require("path"), 1);
15138
15809
  var import_node_child_process = require("child_process");
15139
15810
  var LOCKFILE_PRIORITY = [
15140
15811
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -15149,29 +15820,29 @@ var LOCKFILE_PRIORITY = [
15149
15820
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
15150
15821
  async function exists2(p) {
15151
15822
  try {
15152
- await import_node_fs33.promises.access(p);
15823
+ await import_node_fs34.promises.access(p);
15153
15824
  return true;
15154
15825
  } catch {
15155
15826
  return false;
15156
15827
  }
15157
15828
  }
15158
15829
  async function detectPackageManager(serviceDir) {
15159
- let dir = import_node_path68.default.resolve(serviceDir);
15830
+ let dir = import_node_path70.default.resolve(serviceDir);
15160
15831
  const stops = /* @__PURE__ */ new Set();
15161
15832
  for (let i = 0; i < 64; i++) {
15162
15833
  if (stops.has(dir)) break;
15163
15834
  stops.add(dir);
15164
15835
  for (const candidate of LOCKFILE_PRIORITY) {
15165
- const lockPath = import_node_path68.default.join(dir, candidate.lockfile);
15836
+ const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
15166
15837
  if (await exists2(lockPath)) {
15167
15838
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
15168
15839
  }
15169
15840
  }
15170
- const parent = import_node_path68.default.dirname(dir);
15841
+ const parent = import_node_path70.default.dirname(dir);
15171
15842
  if (parent === dir) break;
15172
15843
  dir = parent;
15173
15844
  }
15174
- 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] };
15175
15846
  }
15176
15847
  async function runPackageManagerInstall(cmd) {
15177
15848
  return new Promise((resolve) => {
@@ -15213,15 +15884,15 @@ ${err.message}`
15213
15884
  // src/extend/index.ts
15214
15885
  async function fileExists2(p) {
15215
15886
  try {
15216
- await import_node_fs34.promises.access(p);
15887
+ await import_node_fs35.promises.access(p);
15217
15888
  return true;
15218
15889
  } catch {
15219
15890
  return false;
15220
15891
  }
15221
15892
  }
15222
15893
  async function readPackageJson(scanPath) {
15223
- const pkgPath = import_node_path69.default.join(scanPath, "package.json");
15224
- 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");
15225
15896
  return JSON.parse(raw);
15226
15897
  }
15227
15898
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -15234,27 +15905,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
15234
15905
  ]);
15235
15906
  async function findHookFiles(scanPath) {
15236
15907
  const found = [];
15237
- const walk9 = async (dir) => {
15238
- 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(() => []);
15239
15910
  for (const entry2 of entries) {
15240
15911
  if (entry2.isDirectory()) {
15241
15912
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
15242
- await walk9(import_node_path69.default.join(dir, entry2.name));
15913
+ await walk10(import_node_path71.default.join(dir, entry2.name));
15243
15914
  } else if (entry2.isFile()) {
15244
15915
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
15245
- const rel = import_node_path69.default.relative(scanPath, import_node_path69.default.join(dir, entry2.name));
15246
- 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("/"));
15247
15918
  }
15248
15919
  }
15249
15920
  }
15250
15921
  };
15251
- await walk9(scanPath);
15922
+ await walk10(scanPath);
15252
15923
  return found.sort();
15253
15924
  }
15254
15925
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15255
15926
  let fallback = null;
15256
15927
  for (const file of hookFiles) {
15257
- 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");
15258
15929
  const patched = splicedContent(content, snippet2);
15259
15930
  if (patched !== null) return { file, content, patched };
15260
15931
  if (fallback === null) fallback = { file, content };
@@ -15262,12 +15933,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15262
15933
  return { file: fallback.file, content: fallback.content, patched: null };
15263
15934
  }
15264
15935
  function extendLogPath() {
15265
- 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");
15266
15937
  }
15267
15938
  async function appendExtendLog(entry2) {
15268
15939
  const logPath = extendLogPath();
15269
- await import_node_fs34.promises.mkdir(import_node_path69.default.dirname(logPath), { recursive: true });
15270
- 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");
15271
15942
  }
15272
15943
  function splicedContent(fileContent, snippet2) {
15273
15944
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -15325,7 +15996,7 @@ function lookupInstrumentation(library, installedVersion) {
15325
15996
  }
15326
15997
  async function describeProjectInstrumentation(ctx) {
15327
15998
  const hookFiles = await findHookFiles(ctx.scanPath);
15328
- 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"));
15329
16000
  const registryInstrPackages = new Set(
15330
16001
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
15331
16002
  );
@@ -15347,7 +16018,7 @@ async function applyExtension(ctx, args, options) {
15347
16018
  );
15348
16019
  }
15349
16020
  for (const file of hookFiles) {
15350
- 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");
15351
16022
  if (content.includes(args.registration_snippet)) {
15352
16023
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
15353
16024
  }
@@ -15359,18 +16030,18 @@ async function applyExtension(ctx, args, options) {
15359
16030
  );
15360
16031
  }
15361
16032
  const primaryFile = primary.file;
15362
- const primaryPath = import_node_path69.default.join(ctx.scanPath, primaryFile);
16033
+ const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
15363
16034
  const filesTouched = [];
15364
16035
  const depsAdded = [];
15365
- const pkgPath = import_node_path69.default.join(ctx.scanPath, "package.json");
16036
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
15366
16037
  const pkg = await readPackageJson(ctx.scanPath);
15367
16038
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
15368
16039
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
15369
- 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");
15370
16041
  filesTouched.push("package.json");
15371
16042
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
15372
16043
  }
15373
- await import_node_fs34.promises.writeFile(primaryPath, primary.patched, "utf8");
16044
+ await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
15374
16045
  filesTouched.push(primaryFile);
15375
16046
  const cmd = await detectPackageManager(ctx.scanPath);
15376
16047
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -15401,7 +16072,7 @@ async function dryRunExtension(ctx, args) {
15401
16072
  };
15402
16073
  }
15403
16074
  for (const file of hookFiles) {
15404
- 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");
15405
16076
  if (content.includes(args.registration_snippet)) {
15406
16077
  return {
15407
16078
  library: args.library,
@@ -15436,28 +16107,28 @@ async function rollbackExtension(ctx, args) {
15436
16107
  if (!await fileExists2(logPath)) {
15437
16108
  return { undone: false, message: "no apply found for library" };
15438
16109
  }
15439
- const raw = await import_node_fs34.promises.readFile(logPath, "utf8");
16110
+ const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
15440
16111
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
15441
16112
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
15442
16113
  if (!match) {
15443
16114
  return { undone: false, message: "no apply found for library" };
15444
16115
  }
15445
- const pkgPath = import_node_path69.default.join(ctx.scanPath, "package.json");
16116
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
15446
16117
  if (await fileExists2(pkgPath)) {
15447
16118
  const pkg = await readPackageJson(ctx.scanPath);
15448
16119
  if (pkg.dependencies?.[match.instrumentation_package]) {
15449
16120
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
15450
16121
  pkg.dependencies = rest;
15451
- 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");
15452
16123
  }
15453
16124
  }
15454
16125
  const hookFiles = await findHookFiles(ctx.scanPath);
15455
16126
  for (const file of hookFiles) {
15456
- const filePath = import_node_path69.default.join(ctx.scanPath, file);
15457
- 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");
15458
16129
  if (content.includes(match.registration_snippet)) {
15459
16130
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
15460
- await import_node_fs34.promises.writeFile(filePath, filtered, "utf8");
16131
+ await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
15461
16132
  break;
15462
16133
  }
15463
16134
  }
@@ -15469,39 +16140,39 @@ async function rollbackExtension(ctx, args) {
15469
16140
 
15470
16141
  // src/divergences.ts
15471
16142
  init_cjs_shims();
15472
- var import_types57 = require("@neat.is/types");
16143
+ var import_types58 = require("@neat.is/types");
15473
16144
  function bucketKey(source, target, type) {
15474
16145
  return `${type}|${source}|${target}`;
15475
16146
  }
15476
16147
  function bucketSourceFor(graph, edge) {
15477
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) return edge.source;
15478
- 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);
15479
16150
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
15480
16151
  const target = graph.getNodeAttributes(edge.target);
15481
- if (target.type !== import_types57.NodeType.DatabaseNode) return edge.source;
15482
- 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);
15483
16154
  }
15484
16155
  function bucketEdges(graph) {
15485
16156
  const buckets2 = /* @__PURE__ */ new Map();
15486
16157
  graph.forEachEdge((id, attrs) => {
15487
16158
  const e = attrs;
15488
- const parsed = (0, import_types57.parseEdgeId)(id);
16159
+ const parsed = (0, import_types58.parseEdgeId)(id);
15489
16160
  const provenance = parsed?.provenance ?? e.provenance;
15490
16161
  const source = bucketSourceFor(graph, e);
15491
16162
  const key = bucketKey(source, e.target, e.type);
15492
16163
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
15493
16164
  switch (provenance) {
15494
- case import_types57.Provenance.EXTRACTED:
16165
+ case import_types58.Provenance.EXTRACTED:
15495
16166
  cur.extracted = e;
15496
16167
  break;
15497
- case import_types57.Provenance.OBSERVED:
16168
+ case import_types58.Provenance.OBSERVED:
15498
16169
  cur.observed = e;
15499
16170
  break;
15500
- case import_types57.Provenance.INFERRED:
16171
+ case import_types58.Provenance.INFERRED:
15501
16172
  cur.inferred = e;
15502
16173
  break;
15503
16174
  default:
15504
- if (e.provenance === import_types57.Provenance.STALE) cur.stale = e;
16175
+ if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
15505
16176
  }
15506
16177
  buckets2.set(key, cur);
15507
16178
  });
@@ -15510,22 +16181,22 @@ function bucketEdges(graph) {
15510
16181
  function nodeIsFrontier(graph, nodeId) {
15511
16182
  if (!graph.hasNode(nodeId)) return false;
15512
16183
  const attrs = graph.getNodeAttributes(nodeId);
15513
- return attrs.type === import_types57.NodeType.FrontierNode;
16184
+ return attrs.type === import_types58.NodeType.FrontierNode;
15514
16185
  }
15515
16186
  function nodeIsWebsocketChannel(graph, nodeId) {
15516
16187
  if (!graph.hasNode(nodeId)) return false;
15517
16188
  const attrs = graph.getNodeAttributes(nodeId);
15518
- return attrs.type === import_types57.NodeType.WebSocketChannelNode;
16189
+ return attrs.type === import_types58.NodeType.WebSocketChannelNode;
15519
16190
  }
15520
16191
  function nodeIsServerAction(graph, nodeId) {
15521
16192
  if (!graph.hasNode(nodeId)) return false;
15522
16193
  const attrs = graph.getNodeAttributes(nodeId);
15523
- return attrs.type === import_types57.NodeType.ServerActionNode;
16194
+ return attrs.type === import_types58.NodeType.ServerActionNode;
15524
16195
  }
15525
16196
  function nodeIsSymbol(graph, nodeId) {
15526
16197
  if (!graph.hasNode(nodeId)) return false;
15527
16198
  const attrs = graph.getNodeAttributes(nodeId);
15528
- return attrs.type === import_types57.NodeType.SymbolNode;
16199
+ return attrs.type === import_types58.NodeType.SymbolNode;
15529
16200
  }
15530
16201
  function clampConfidence(n) {
15531
16202
  if (!Number.isFinite(n)) return 0;
@@ -15545,14 +16216,14 @@ function gradedConfidence(edge) {
15545
16216
  return clampConfidence(confidenceForEdge(edge));
15546
16217
  }
15547
16218
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
15548
- import_types57.EdgeType.CALLS,
15549
- import_types57.EdgeType.CONNECTS_TO,
15550
- import_types57.EdgeType.PUBLISHES_TO,
15551
- 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
15552
16223
  ]);
15553
16224
  function detectMissingDivergences(graph, bucket) {
15554
16225
  const out = [];
15555
- if (bucket.type === import_types57.EdgeType.CONTAINS) return out;
16226
+ if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
15556
16227
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
15557
16228
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
15558
16229
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -15594,7 +16265,7 @@ function declaredHostFor(svc) {
15594
16265
  function hasExtractedConfiguredBy(graph, svcId) {
15595
16266
  for (const edgeId of graph.outboundEdges(svcId)) {
15596
16267
  const e = graph.getEdgeAttributes(edgeId);
15597
- 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) {
15598
16269
  return true;
15599
16270
  }
15600
16271
  }
@@ -15607,10 +16278,10 @@ function detectHostMismatch(graph, svcId, svc) {
15607
16278
  const out = [];
15608
16279
  for (const edgeId of graph.outboundEdges(svcId)) {
15609
16280
  const edge = graph.getEdgeAttributes(edgeId);
15610
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15611
- 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;
15612
16283
  const target = graph.getNodeAttributes(edge.target);
15613
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16284
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15614
16285
  const observedHost = target.host?.trim();
15615
16286
  if (!observedHost) continue;
15616
16287
  if (observedHost === declaredHost) continue;
@@ -15632,10 +16303,10 @@ function detectCompatDivergences(graph, svcId, svc) {
15632
16303
  const deps = svc.dependencies ?? {};
15633
16304
  for (const edgeId of graph.outboundEdges(svcId)) {
15634
16305
  const edge = graph.getEdgeAttributes(edgeId);
15635
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15636
- 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;
15637
16308
  const target = graph.getNodeAttributes(edge.target);
15638
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16309
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15639
16310
  for (const pair of compatPairs()) {
15640
16311
  if (pair.engine !== target.engine) continue;
15641
16312
  const declared = deps[pair.driver];
@@ -15732,7 +16403,7 @@ function suppressHostMismatchHalves(all) {
15732
16403
  for (const d of all) {
15733
16404
  if (d.type !== "host-mismatch") continue;
15734
16405
  observedHalf.add(`${d.source}->${d.target}`);
15735
- declaredHalf.add((0, import_types57.databaseId)(d.extractedHost));
16406
+ declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
15736
16407
  }
15737
16408
  if (observedHalf.size === 0) return all;
15738
16409
  return all.filter((d) => {
@@ -15751,13 +16422,13 @@ function computeDivergences(graph, opts = {}) {
15751
16422
  }
15752
16423
  graph.forEachNode((nodeId, attrs) => {
15753
16424
  const n = attrs;
15754
- if (n.type === import_types57.NodeType.ServiceNode) {
16425
+ if (n.type === import_types58.NodeType.ServiceNode) {
15755
16426
  const svc = n;
15756
16427
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
15757
16428
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
15758
16429
  return;
15759
16430
  }
15760
- if (n.type === import_types57.NodeType.InfraNode && n.kind === "sql-table") {
16431
+ if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
15761
16432
  for (const d of detectColumnDrift(n)) all.push(d);
15762
16433
  }
15763
16434
  });
@@ -15793,7 +16464,7 @@ function computeDivergences(graph, opts = {}) {
15793
16464
  const bc = "column" in b && b.column ? b.column : "";
15794
16465
  return ac.localeCompare(bc);
15795
16466
  });
15796
- return import_types57.DivergenceResultSchema.parse({
16467
+ return import_types58.DivergenceResultSchema.parse({
15797
16468
  divergences: filtered,
15798
16469
  totalAffected: filtered.length,
15799
16470
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -15847,7 +16518,7 @@ function queryLogEntries(opts) {
15847
16518
 
15848
16519
  // src/diff.ts
15849
16520
  init_cjs_shims();
15850
- var import_node_fs35 = require("fs");
16521
+ var import_node_fs36 = require("fs");
15851
16522
  async function loadSnapshotForDiff(target) {
15852
16523
  if (/^https?:\/\//i.test(target)) {
15853
16524
  const res = await fetch(target);
@@ -15856,7 +16527,7 @@ async function loadSnapshotForDiff(target) {
15856
16527
  }
15857
16528
  return await res.json();
15858
16529
  }
15859
- const raw = await import_node_fs35.promises.readFile(target, "utf8");
16530
+ const raw = await import_node_fs36.promises.readFile(target, "utf8");
15860
16531
  return JSON.parse(raw);
15861
16532
  }
15862
16533
  function indexEntries(entries) {
@@ -15924,28 +16595,28 @@ function canonicalJson(value) {
15924
16595
 
15925
16596
  // src/registry.ts
15926
16597
  init_cjs_shims();
15927
- var import_node_fs36 = require("fs");
16598
+ var import_node_fs37 = require("fs");
15928
16599
  var import_node_os3 = __toESM(require("os"), 1);
15929
- var import_node_path70 = __toESM(require("path"), 1);
15930
- var import_types58 = require("@neat.is/types");
16600
+ var import_node_path72 = __toESM(require("path"), 1);
16601
+ var import_types59 = require("@neat.is/types");
15931
16602
  var LOCK_TIMEOUT_MS = 5e3;
15932
16603
  var LOCK_RETRY_MS = 50;
15933
16604
  function neatHome() {
15934
16605
  const override = process.env.NEAT_HOME;
15935
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15936
- 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");
15937
16608
  }
15938
16609
  function registryPath() {
15939
- return import_node_path70.default.join(neatHome(), "projects.json");
16610
+ return import_node_path72.default.join(neatHome(), "projects.json");
15940
16611
  }
15941
16612
  function registryLockPath() {
15942
- return import_node_path70.default.join(neatHome(), "projects.json.lock");
16613
+ return import_node_path72.default.join(neatHome(), "projects.json.lock");
15943
16614
  }
15944
16615
  function daemonPidPath() {
15945
- return import_node_path70.default.join(neatHome(), "neatd.pid");
16616
+ return import_node_path72.default.join(neatHome(), "neatd.pid");
15946
16617
  }
15947
16618
  function daemonsDir() {
15948
- return import_node_path70.default.join(neatHome(), "daemons");
16619
+ return import_node_path72.default.join(neatHome(), "daemons");
15949
16620
  }
15950
16621
  function isFiniteInt(v) {
15951
16622
  return typeof v === "number" && Number.isFinite(v);
@@ -15978,7 +16649,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15978
16649
  const dir = daemonsDir();
15979
16650
  let names;
15980
16651
  try {
15981
- names = await import_node_fs36.promises.readdir(dir);
16652
+ names = await import_node_fs37.promises.readdir(dir);
15982
16653
  } catch (err) {
15983
16654
  if (err.code === "ENOENT") return [];
15984
16655
  throw err;
@@ -15986,10 +16657,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15986
16657
  const out = [];
15987
16658
  for (const name of names) {
15988
16659
  if (!name.endsWith(".json")) continue;
15989
- const file = import_node_path70.default.join(dir, name);
16660
+ const file = import_node_path72.default.join(dir, name);
15990
16661
  let raw;
15991
16662
  try {
15992
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16663
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15993
16664
  } catch {
15994
16665
  continue;
15995
16666
  }
@@ -16015,7 +16686,7 @@ function isPidAliveDefault(pid) {
16015
16686
  }
16016
16687
  async function readPidFile(file) {
16017
16688
  try {
16018
- const raw = await import_node_fs36.promises.readFile(file, "utf8");
16689
+ const raw = await import_node_fs37.promises.readFile(file, "utf8");
16019
16690
  const pid = Number.parseInt(raw.trim(), 10);
16020
16691
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
16021
16692
  } catch {
@@ -16063,24 +16734,24 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
16063
16734
  }
16064
16735
  }
16065
16736
  async function writeAtomically(target, contents) {
16066
- 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 });
16067
16738
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
16068
- const fd = await import_node_fs36.promises.open(tmp, "w");
16739
+ const fd = await import_node_fs37.promises.open(tmp, "w");
16069
16740
  try {
16070
16741
  await fd.writeFile(contents, "utf8");
16071
16742
  await fd.sync();
16072
16743
  } finally {
16073
16744
  await fd.close();
16074
16745
  }
16075
- await import_node_fs36.promises.rename(tmp, target);
16746
+ await import_node_fs37.promises.rename(tmp, target);
16076
16747
  }
16077
16748
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
16078
16749
  const deadline = Date.now() + timeoutMs;
16079
- 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 });
16080
16751
  let probedHolder = false;
16081
16752
  while (true) {
16082
16753
  try {
16083
- const fd = await import_node_fs36.promises.open(lockPath, "wx");
16754
+ const fd = await import_node_fs37.promises.open(lockPath, "wx");
16084
16755
  try {
16085
16756
  await fd.writeFile(`${process.pid}
16086
16757
  `, "utf8");
@@ -16105,7 +16776,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
16105
16776
  }
16106
16777
  }
16107
16778
  async function releaseLock(lockPath) {
16108
- await import_node_fs36.promises.unlink(lockPath).catch(() => {
16779
+ await import_node_fs37.promises.unlink(lockPath).catch(() => {
16109
16780
  });
16110
16781
  }
16111
16782
  async function withLock(fn) {
@@ -16121,7 +16792,7 @@ async function readRegistry() {
16121
16792
  const file = registryPath();
16122
16793
  let raw;
16123
16794
  try {
16124
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16795
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16125
16796
  } catch (err) {
16126
16797
  if (err.code === "ENOENT") {
16127
16798
  return { version: 1, projects: [] };
@@ -16129,10 +16800,10 @@ async function readRegistry() {
16129
16800
  throw err;
16130
16801
  }
16131
16802
  const parsed = JSON.parse(raw);
16132
- return import_types58.RegistryFileSchema.parse(parsed);
16803
+ return import_types59.RegistryFileSchema.parse(parsed);
16133
16804
  }
16134
16805
  async function writeRegistry(reg) {
16135
- const validated = import_types58.RegistryFileSchema.parse(reg);
16806
+ const validated = import_types59.RegistryFileSchema.parse(reg);
16136
16807
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
16137
16808
  }
16138
16809
  async function getProject(name) {
@@ -16176,7 +16847,7 @@ function pruneTtlMs() {
16176
16847
  }
16177
16848
  async function statPathStatus(p) {
16178
16849
  try {
16179
- const stat = await import_node_fs36.promises.stat(p);
16850
+ const stat = await import_node_fs37.promises.stat(p);
16180
16851
  return stat.isDirectory() ? "present" : "unknown";
16181
16852
  } catch (err) {
16182
16853
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -16277,8 +16948,8 @@ init_auth();
16277
16948
  // src/connectors-config.ts
16278
16949
  init_cjs_shims();
16279
16950
  var import_node_os4 = __toESM(require("os"), 1);
16280
- var import_node_path71 = __toESM(require("path"), 1);
16281
- var import_node_fs37 = require("fs");
16951
+ var import_node_path73 = __toESM(require("path"), 1);
16952
+ var import_node_fs38 = require("fs");
16282
16953
  var CONNECTORS_CONFIG_VERSION = 1;
16283
16954
  var EnvRefUnsetError = class extends Error {
16284
16955
  ref;
@@ -16292,17 +16963,17 @@ var EnvRefUnsetError = class extends Error {
16292
16963
  };
16293
16964
  function neatHome2() {
16294
16965
  const override = process.env.NEAT_HOME;
16295
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
16296
- 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");
16297
16968
  }
16298
16969
  function connectorsConfigPath(home = neatHome2()) {
16299
- return import_node_path71.default.join(home, "connectors.json");
16970
+ return import_node_path73.default.join(home, "connectors.json");
16300
16971
  }
16301
16972
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16302
16973
  async function warnIfModeLooserThan0600(file) {
16303
16974
  if (process.platform === "win32") return;
16304
16975
  try {
16305
- const stat = await import_node_fs37.promises.stat(file);
16976
+ const stat = await import_node_fs38.promises.stat(file);
16306
16977
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16307
16978
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16308
16979
  console.warn(
@@ -16316,7 +16987,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16316
16987
  const file = connectorsConfigPath(home);
16317
16988
  let raw;
16318
16989
  try {
16319
- raw = await import_node_fs37.promises.readFile(file, "utf8");
16990
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16320
16991
  } catch (err) {
16321
16992
  if (err.code === "ENOENT") {
16322
16993
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16483,15 +17154,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16483
17154
 
16484
17155
  // src/connectors/index.ts
16485
17156
  init_cjs_shims();
16486
- var import_types59 = require("@neat.is/types");
17157
+ var import_types60 = require("@neat.is/types");
16487
17158
  var NO_ENV = "unknown";
16488
17159
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16489
17160
  if (!graph.hasNode(targetNodeId)) return void 0;
16490
17161
  const sites = [];
16491
17162
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16492
17163
  const edge = graph.getEdgeAttributes(edgeId);
16493
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16494
- 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);
16495
17166
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16496
17167
  const site = { relPath: edge.evidence.file };
16497
17168
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16502,7 +17173,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16502
17173
  function routeCallSiteFor(graph, targetNodeId) {
16503
17174
  if (!graph.hasNode(targetNodeId)) return void 0;
16504
17175
  const attrs = graph.getNodeAttributes(targetNodeId);
16505
- 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;
16506
17177
  const site = { relPath: attrs.path };
16507
17178
  if (attrs.line !== void 0) site.line = attrs.line;
16508
17179
  return site;
@@ -17000,10 +17671,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
17000
17671
  // src/connectors/supabase/map.ts
17001
17672
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
17002
17673
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
17003
- function targetFromRestPath(path76) {
17004
- const rpcMatch = REST_RPC_PATH_RE.exec(path76);
17674
+ function targetFromRestPath(path78) {
17675
+ const rpcMatch = REST_RPC_PATH_RE.exec(path78);
17005
17676
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
17006
- const tableMatch = REST_TABLE_PATH_RE.exec(path76);
17677
+ const tableMatch = REST_TABLE_PATH_RE.exec(path78);
17007
17678
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
17008
17679
  return null;
17009
17680
  }
@@ -17114,23 +17785,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
17114
17785
 
17115
17786
  // src/connectors/supabase/resolve.ts
17116
17787
  init_cjs_shims();
17117
- var import_types61 = require("@neat.is/types");
17788
+ var import_types62 = require("@neat.is/types");
17118
17789
  function createSupabaseResolveTarget(graph, config) {
17119
17790
  return (signal, _ctx) => {
17120
17791
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
17121
17792
  return null;
17122
17793
  }
17123
- 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}`);
17124
17795
  if (graph.hasNode(subResourceId)) {
17125
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17796
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17126
17797
  }
17127
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17798
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
17128
17799
  if (graph.hasNode(bareResourceId)) {
17129
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17800
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17130
17801
  }
17131
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17802
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
17132
17803
  if (graph.hasNode(projectLevelId)) {
17133
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17804
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17134
17805
  }
17135
17806
  return null;
17136
17807
  };
@@ -17223,7 +17894,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
17223
17894
 
17224
17895
  // src/connectors/railway/index.ts
17225
17896
  init_cjs_shims();
17226
- var import_types65 = require("@neat.is/types");
17897
+ var import_types66 = require("@neat.is/types");
17227
17898
 
17228
17899
  // src/connectors/railway/client.ts
17229
17900
  init_cjs_shims();
@@ -17374,7 +18045,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17374
18045
  const out = [];
17375
18046
  graph.forEachNode((_id, attrs) => {
17376
18047
  const node = attrs;
17377
- if (node.type !== import_types65.NodeType.RouteNode) return;
18048
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17378
18049
  const route = attrs;
17379
18050
  if (route.service !== serviceName) return;
17380
18051
  out.push({
@@ -17478,12 +18149,12 @@ function createRailwayResolveTarget(config) {
17478
18149
  const serviceName = config.serviceNameById[config.serviceId];
17479
18150
  if (!serviceName) return null;
17480
18151
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17481
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
18152
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17482
18153
  }
17483
18154
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17484
18155
  const peerName = config.serviceNameById[signal.targetName];
17485
18156
  if (!peerName) return null;
17486
- 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 };
17487
18158
  }
17488
18159
  return null;
17489
18160
  };
@@ -17607,9 +18278,9 @@ function parseFirebaseTargetName(targetName) {
17607
18278
  const secondSep = rest.indexOf(FIELD_SEP);
17608
18279
  if (secondSep === -1) return null;
17609
18280
  const method = rest.slice(0, secondSep);
17610
- const path76 = rest.slice(secondSep + 1);
17611
- if (!resourceName || !method || !path76) return null;
17612
- 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 };
17613
18284
  }
17614
18285
  function resourceNameFor(type, labels) {
17615
18286
  if (!labels) return null;
@@ -17647,14 +18318,14 @@ function mapLogEntryToSignal(entry2) {
17647
18318
  if (!req2) return null;
17648
18319
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
17649
18320
  const method = req2.requestMethod.toUpperCase();
17650
- const path76 = pathFromRequestUrl(req2.requestUrl);
17651
- if (path76 === null) return null;
18321
+ const path78 = pathFromRequestUrl(req2.requestUrl);
18322
+ if (path78 === null) return null;
17652
18323
  const timestamp = entry2.timestamp;
17653
18324
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17654
18325
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
17655
18326
  return {
17656
18327
  targetKind: resourceType,
17657
- targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
18328
+ targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
17658
18329
  callCount: 1,
17659
18330
  errorCount: isError ? 1 : 0,
17660
18331
  lastObservedIso: timestamp
@@ -17671,7 +18342,7 @@ function mapLogEntriesToSignals(entries) {
17671
18342
 
17672
18343
  // src/connectors/firebase/resolve.ts
17673
18344
  init_cjs_shims();
17674
- var import_types66 = require("@neat.is/types");
18345
+ var import_types67 = require("@neat.is/types");
17675
18346
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17676
18347
  switch (resourceType) {
17677
18348
  case "cloud_function":
@@ -17686,7 +18357,7 @@ function routeEntriesFor(graph, serviceName) {
17686
18357
  const entries = [];
17687
18358
  graph.forEachNode((_id, attrs) => {
17688
18359
  const node = attrs;
17689
- if (node.type !== import_types66.NodeType.RouteNode) return;
18360
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17690
18361
  const route = attrs;
17691
18362
  if (route.service !== serviceName) return;
17692
18363
  entries.push({
@@ -17718,7 +18389,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17718
18389
  return {
17719
18390
  targetNodeId: match.routeNodeId,
17720
18391
  serviceName,
17721
- edgeType: import_types66.EdgeType.CALLS
18392
+ edgeType: import_types67.EdgeType.CALLS
17722
18393
  };
17723
18394
  };
17724
18395
  }
@@ -17745,7 +18416,7 @@ init_cjs_shims();
17745
18416
 
17746
18417
  // src/connectors/cloudflare/connector.ts
17747
18418
  init_cjs_shims();
17748
- var import_types68 = require("@neat.is/types");
18419
+ var import_types69 = require("@neat.is/types");
17749
18420
 
17750
18421
  // src/connectors/cloudflare/client.ts
17751
18422
  init_cjs_shims();
@@ -17861,7 +18532,7 @@ function mapEventToSignal(event) {
17861
18532
  if (Number.isNaN(observedAt.getTime())) return null;
17862
18533
  const statusCode = metadata?.statusCode;
17863
18534
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17864
- const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18535
+ const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17865
18536
  return {
17866
18537
  targetKind: CLOUDFLARE_TARGET_KIND,
17867
18538
  targetName: scriptName,
@@ -17869,7 +18540,7 @@ function mapEventToSignal(event) {
17869
18540
  errorCount: isError ? 1 : 0,
17870
18541
  lastObservedIso: observedAt.toISOString(),
17871
18542
  method,
17872
- ...path76 ? { path: path76 } : {},
18543
+ ...path78 ? { path: path78 } : {},
17873
18544
  ...typeof statusCode === "number" ? { statusCode } : {},
17874
18545
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17875
18546
  };
@@ -17909,19 +18580,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17909
18580
  graph.forEachNode((id, attrs) => {
17910
18581
  if (found) return;
17911
18582
  const a = attrs;
17912
- 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) {
17913
18584
  found = id;
17914
18585
  }
17915
18586
  });
17916
18587
  return found;
17917
18588
  }
17918
- function findMatchingRouteNode(graph, serviceName, method, path76) {
17919
- const normalizedPath = normalizePathTemplate(path76);
18589
+ function findMatchingRouteNode(graph, serviceName, method, path78) {
18590
+ const normalizedPath = normalizePathTemplate(path78);
17920
18591
  let found = null;
17921
18592
  graph.forEachNode((id, attrs) => {
17922
18593
  if (found) return;
17923
18594
  const a = attrs;
17924
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18595
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17925
18596
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17926
18597
  const routeMethod = (a.method ?? "").toUpperCase();
17927
18598
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17933,18 +18604,18 @@ function createCloudflareResolveTarget(config, graph) {
17933
18604
  return (signal) => {
17934
18605
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17935
18606
  const scriptName = signal.targetName;
17936
- const { method, path: path76 } = signal;
18607
+ const { method, path: path78 } = signal;
17937
18608
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17938
- if (!method || !path76) return wholeFileId;
17939
- return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
18609
+ if (!method || !path78) return wholeFileId;
18610
+ return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
17940
18611
  };
17941
18612
  const mapping = config.workers?.[scriptName];
17942
18613
  if (mapping) {
17943
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18614
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17944
18615
  return {
17945
18616
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17946
18617
  serviceName: mapping.service,
17947
- edgeType: import_types68.EdgeType.CALLS
18618
+ edgeType: import_types69.EdgeType.CALLS
17948
18619
  };
17949
18620
  }
17950
18621
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17953,13 +18624,13 @@ function createCloudflareResolveTarget(config, graph) {
17953
18624
  return {
17954
18625
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17955
18626
  serviceName: fileNode.service,
17956
- edgeType: import_types68.EdgeType.CALLS
18627
+ edgeType: import_types69.EdgeType.CALLS
17957
18628
  };
17958
18629
  }
17959
18630
  return {
17960
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18631
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17961
18632
  serviceName: scriptName,
17962
- edgeType: import_types68.EdgeType.CALLS,
18633
+ edgeType: import_types69.EdgeType.CALLS,
17963
18634
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
17964
18635
  };
17965
18636
  };
@@ -18155,14 +18826,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
18155
18826
 
18156
18827
  // src/connectors/neon/resolve.ts
18157
18828
  init_cjs_shims();
18158
- var import_types72 = require("@neat.is/types");
18829
+ var import_types73 = require("@neat.is/types");
18159
18830
  function createNeonResolveTarget(config) {
18160
18831
  return (signal) => {
18161
18832
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18162
18833
  return {
18163
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18834
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
18164
18835
  serviceName: config.serviceName,
18165
- edgeType: import_types72.EdgeType.CALLS,
18836
+ edgeType: import_types73.EdgeType.CALLS,
18166
18837
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
18167
18838
  };
18168
18839
  };
@@ -18288,9 +18959,9 @@ function parseCloudRunTargetName(targetName) {
18288
18959
  const secondSep = rest.indexOf(FIELD_SEP2);
18289
18960
  if (secondSep === -1) return null;
18290
18961
  const method = rest.slice(0, secondSep);
18291
- const path76 = rest.slice(secondSep + 1);
18292
- if (!serviceName || !method || !path76) return null;
18293
- 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 };
18294
18965
  }
18295
18966
 
18296
18967
  // src/connectors/cloud-run/map.ts
@@ -18319,14 +18990,14 @@ function mapLogEntryToSignal2(entry2) {
18319
18990
  if (!req2) return null;
18320
18991
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
18321
18992
  const method = req2.requestMethod.toUpperCase();
18322
- const path76 = pathFromRequestUrl2(req2.requestUrl);
18323
- if (path76 === null) return null;
18993
+ const path78 = pathFromRequestUrl2(req2.requestUrl);
18994
+ if (path78 === null) return null;
18324
18995
  const timestamp = entry2.timestamp;
18325
18996
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
18326
18997
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
18327
18998
  return {
18328
18999
  targetKind: CLOUD_RUN_TARGET_KIND,
18329
- targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
19000
+ targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
18330
19001
  callCount: 1,
18331
19002
  errorCount: isError ? 1 : 0,
18332
19003
  lastObservedIso: timestamp
@@ -18343,14 +19014,14 @@ function mapLogEntriesToSignals2(entries) {
18343
19014
 
18344
19015
  // src/connectors/cloud-run/resolve.ts
18345
19016
  init_cjs_shims();
18346
- var import_types76 = require("@neat.is/types");
19017
+ var import_types77 = require("@neat.is/types");
18347
19018
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18348
19019
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18349
19020
  let found = null;
18350
19021
  graph.forEachNode((_id, attrs) => {
18351
19022
  if (found) return;
18352
19023
  const node = attrs;
18353
- if (node.type !== import_types76.NodeType.RouteNode) return;
19024
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18354
19025
  const route = attrs;
18355
19026
  if (route.service !== serviceName || !route.pathTemplate) return;
18356
19027
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18365,23 +19036,23 @@ function createCloudRunResolveTarget(graph, config) {
18365
19036
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18366
19037
  const identity = parseCloudRunTargetName(signal.targetName);
18367
19038
  if (!identity) return null;
18368
- const { serviceName: gcpServiceName, method, path: path76 } = identity;
19039
+ const { serviceName: gcpServiceName, method, path: path78 } = identity;
18369
19040
  const mappedService = config.serviceMap?.[gcpServiceName];
18370
19041
  if (mappedService) {
18371
19042
  const routeNodeId = findMatchingRouteNode2(
18372
19043
  graph,
18373
19044
  mappedService,
18374
19045
  method,
18375
- normalizePathTemplate(path76)
19046
+ normalizePathTemplate(path78)
18376
19047
  );
18377
19048
  if (routeNodeId) {
18378
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
19049
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18379
19050
  }
18380
19051
  }
18381
19052
  return {
18382
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
19053
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18383
19054
  serviceName: mappedService ?? gcpServiceName,
18384
- edgeType: import_types76.EdgeType.CALLS,
19055
+ edgeType: import_types77.EdgeType.CALLS,
18385
19056
  ensureInfraNode: {
18386
19057
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18387
19058
  name: gcpServiceName,
@@ -18422,7 +19093,7 @@ function createCloudRunConnector(graph, config = {}) {
18422
19093
 
18423
19094
  // src/connectors/render/index.ts
18424
19095
  init_cjs_shims();
18425
- var import_types79 = require("@neat.is/types");
19096
+ var import_types80 = require("@neat.is/types");
18426
19097
 
18427
19098
  // src/connectors/render/types.ts
18428
19099
  init_cjs_shims();
@@ -18500,7 +19171,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18500
19171
  const out = [];
18501
19172
  graph.forEachNode((_id, attrs) => {
18502
19173
  const node = attrs;
18503
- if (node.type !== import_types79.NodeType.RouteNode) return;
19174
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18504
19175
  const route = attrs;
18505
19176
  if (route.service !== serviceName) return;
18506
19177
  out.push({
@@ -18585,7 +19256,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18585
19256
  function createRenderResolveTarget(config) {
18586
19257
  return (signal) => {
18587
19258
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18588
- 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 };
18589
19260
  }
18590
19261
  return null;
18591
19262
  };
@@ -18723,21 +19394,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18723
19394
 
18724
19395
  // src/connectors/planetscale/resolve.ts
18725
19396
  init_cjs_shims();
18726
- var import_types83 = require("@neat.is/types");
19397
+ var import_types84 = require("@neat.is/types");
18727
19398
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18728
19399
  function createPlanetscaleResolveTarget(graph, config) {
18729
19400
  const databaseName = `${config.organization}/${config.database}`;
18730
19401
  return (signal, _ctx) => {
18731
19402
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18732
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19403
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18733
19404
  if (graph.hasNode(tableId)) {
18734
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19405
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18735
19406
  }
18736
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19407
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18737
19408
  return {
18738
19409
  targetNodeId: providerId,
18739
19410
  serviceName: config.serviceName,
18740
- edgeType: import_types83.EdgeType.CALLS,
19411
+ edgeType: import_types84.EdgeType.CALLS,
18741
19412
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18742
19413
  };
18743
19414
  };
@@ -19002,7 +19673,7 @@ function mapBuildsToSignals(builds, serviceName) {
19002
19673
 
19003
19674
  // src/connectors/eas/resolve.ts
19004
19675
  init_cjs_shims();
19005
- var import_types88 = require("@neat.is/types");
19676
+ var import_types89 = require("@neat.is/types");
19006
19677
  var NO_ENV2 = "unknown";
19007
19678
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
19008
19679
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -19018,8 +19689,8 @@ function configBasenamesForPhase(phase) {
19018
19689
  function configNodeService(graph, configNodeId) {
19019
19690
  for (const edgeId of graph.inboundEdges(configNodeId)) {
19020
19691
  const edge = graph.getEdgeAttributes(edgeId);
19021
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
19022
- 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);
19023
19694
  if (parsed) return parsed.service;
19024
19695
  }
19025
19696
  return null;
@@ -19030,7 +19701,7 @@ function findConfigNode(graph, basenames, serviceName) {
19030
19701
  graph.forEachNode((id, attrs) => {
19031
19702
  if (scoped) return;
19032
19703
  const node = attrs;
19033
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19704
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
19034
19705
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
19035
19706
  if (anyMatch === null) anyMatch = id;
19036
19707
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -19047,13 +19718,13 @@ function createEasResolveTarget(graph) {
19047
19718
  if (basenames.length > 0) {
19048
19719
  const configNodeId = findConfigNode(graph, basenames, serviceName);
19049
19720
  if (configNodeId) {
19050
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19721
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
19051
19722
  }
19052
19723
  }
19053
19724
  return {
19054
19725
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
19055
19726
  serviceName,
19056
- edgeType: import_types88.EdgeType.CALLS
19727
+ edgeType: import_types89.EdgeType.CALLS
19057
19728
  };
19058
19729
  };
19059
19730
  }
@@ -19778,11 +20449,11 @@ function registerRoutes(scope, ctx) {
19778
20449
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19779
20450
  const parsed = [];
19780
20451
  for (const c of candidates) {
19781
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20452
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19782
20453
  if (!r.success) {
19783
20454
  return reply.code(400).send({
19784
20455
  error: `unknown divergence type "${c}"`,
19785
- allowed: import_types91.DivergenceTypeSchema.options
20456
+ allowed: import_types92.DivergenceTypeSchema.options
19786
20457
  });
19787
20458
  }
19788
20459
  parsed.push(r.data);
@@ -20124,7 +20795,7 @@ function registerRoutes(scope, ctx) {
20124
20795
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
20125
20796
  let violations = await log.readAll();
20126
20797
  if (req2.query.severity) {
20127
- const sev = import_types91.PolicySeveritySchema.safeParse(req2.query.severity);
20798
+ const sev = import_types92.PolicySeveritySchema.safeParse(req2.query.severity);
20128
20799
  if (!sev.success) {
20129
20800
  return reply.code(400).send({
20130
20801
  error: "invalid severity",
@@ -20163,7 +20834,7 @@ function registerRoutes(scope, ctx) {
20163
20834
  scope.post("/policies/check", async (req2, reply) => {
20164
20835
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
20165
20836
  if (!proj) return;
20166
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
20837
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
20167
20838
  if (!parsed.success) {
20168
20839
  return reply.code(400).send({
20169
20840
  error: "invalid /policies/check body",
@@ -20484,8 +21155,8 @@ init_auth();
20484
21155
 
20485
21156
  // src/unrouted.ts
20486
21157
  init_cjs_shims();
20487
- var import_node_fs38 = require("fs");
20488
- var import_node_path72 = __toESM(require("path"), 1);
21158
+ var import_node_fs39 = require("fs");
21159
+ var import_node_path74 = __toESM(require("path"), 1);
20489
21160
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
20490
21161
  return {
20491
21162
  timestamp: now.toISOString(),
@@ -20495,34 +21166,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
20495
21166
  };
20496
21167
  }
20497
21168
  async function appendUnroutedSpan(neatHome4, record) {
20498
- const target = import_node_path72.default.join(neatHome4, "errors.ndjson");
20499
- await import_node_fs38.promises.mkdir(neatHome4, { recursive: true });
20500
- 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");
20501
21172
  }
20502
21173
  function unroutedErrorsPath(neatHome4) {
20503
- return import_node_path72.default.join(neatHome4, "errors.ndjson");
21174
+ return import_node_path74.default.join(neatHome4, "errors.ndjson");
20504
21175
  }
20505
21176
 
20506
21177
  // src/daemon.ts
20507
- var import_types92 = require("@neat.is/types");
21178
+ var import_types93 = require("@neat.is/types");
20508
21179
  function daemonJsonPath(scanPath) {
20509
- return import_node_path73.default.join(scanPath, "neat-out", "daemon.json");
21180
+ return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
20510
21181
  }
20511
21182
  function daemonsDiscoveryDir(home) {
20512
21183
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
20513
- return import_node_path73.default.join(base, "daemons");
21184
+ return import_node_path75.default.join(base, "daemons");
20514
21185
  }
20515
21186
  function daemonDiscoveryPath(project, home) {
20516
- return import_node_path73.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
21187
+ return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
20517
21188
  }
20518
21189
  function sanitizeDiscoveryName(project) {
20519
21190
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
20520
21191
  }
20521
21192
  function neatHomeFromEnv() {
20522
21193
  const env = process.env.NEAT_HOME;
20523
- 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);
20524
21195
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20525
- return import_node_path73.default.join(home, ".neat");
21196
+ return import_node_path75.default.join(home, ".neat");
20526
21197
  }
20527
21198
  function resolveNeatVersion() {
20528
21199
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -20554,7 +21225,7 @@ async function clearDaemonRecord(record, home) {
20554
21225
  } catch {
20555
21226
  }
20556
21227
  try {
20557
- await import_node_fs39.promises.unlink(daemonDiscoveryPath(record.project, home));
21228
+ await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
20558
21229
  } catch {
20559
21230
  }
20560
21231
  }
@@ -20563,12 +21234,12 @@ function reconcileDaemonRecordSync(record, home) {
20563
21234
  const stopped = { ...record, status: "stopped" };
20564
21235
  const target = daemonJsonPath(record.projectPath);
20565
21236
  const tmp = `${target}.${process.pid}.tmp`;
20566
- (0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
20567
- (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);
20568
21239
  } catch {
20569
21240
  }
20570
21241
  try {
20571
- (0, import_node_fs39.unlinkSync)(daemonDiscoveryPath(record.project, home));
21242
+ (0, import_node_fs40.unlinkSync)(daemonDiscoveryPath(record.project, home));
20572
21243
  } catch {
20573
21244
  }
20574
21245
  }
@@ -20591,11 +21262,11 @@ function teardownSlot(slot) {
20591
21262
  }
20592
21263
  }
20593
21264
  function neatHomeFor(opts) {
20594
- 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);
20595
21266
  const env = process.env.NEAT_HOME;
20596
- 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);
20597
21268
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20598
- return import_node_path73.default.join(home, ".neat");
21269
+ return import_node_path75.default.join(home, ".neat");
20599
21270
  }
20600
21271
  function routeSpanToProject(serviceName, projects) {
20601
21272
  if (!serviceName) return DEFAULT_PROJECT;
@@ -20643,13 +21314,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
20643
21314
  if (!serviceName) return true;
20644
21315
  if (serviceNameMatchesProject(serviceName, project)) return true;
20645
21316
  return graph.someNode(
20646
- (_id, attrs) => attrs.type === import_types92.NodeType.ServiceNode && attrs.name === serviceName
21317
+ (_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
20647
21318
  );
20648
21319
  }
20649
21320
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
20650
- 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"));
20651
21322
  try {
20652
- const stat = await import_node_fs39.promises.stat(entry2.path);
21323
+ const stat = await import_node_fs40.promises.stat(entry2.path);
20653
21324
  if (!stat.isDirectory()) {
20654
21325
  throw new Error(`registered path ${entry2.path} is not a directory`);
20655
21326
  }
@@ -20767,7 +21438,7 @@ async function startDaemon(opts = {}) {
20767
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;
20768
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;
20769
21440
  const singleProject = projectArg;
20770
- const singleProjectPath = singleProject && projectPathArg ? import_node_path73.default.resolve(projectPathArg) : null;
21441
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
20771
21442
  if (singleProject && !singleProjectPath) {
20772
21443
  throw new Error(
20773
21444
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -20775,14 +21446,14 @@ async function startDaemon(opts = {}) {
20775
21446
  }
20776
21447
  if (!singleProject) {
20777
21448
  try {
20778
- await import_node_fs39.promises.access(regPath);
21449
+ await import_node_fs40.promises.access(regPath);
20779
21450
  } catch {
20780
21451
  throw new Error(
20781
21452
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
20782
21453
  );
20783
21454
  }
20784
21455
  }
20785
- const pidPath = import_node_path73.default.join(home, "neatd.pid");
21456
+ const pidPath = import_node_path75.default.join(home, "neatd.pid");
20786
21457
  await writeAtomically(pidPath, `${process.pid}
20787
21458
  `);
20788
21459
  const slots = /* @__PURE__ */ new Map();
@@ -20994,7 +21665,7 @@ async function startDaemon(opts = {}) {
20994
21665
  }
20995
21666
  if (restApp) await restApp.close().catch(() => {
20996
21667
  });
20997
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21668
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
20998
21669
  });
20999
21670
  throw new Error(
21000
21671
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -21162,7 +21833,7 @@ async function startDaemon(opts = {}) {
21162
21833
  });
21163
21834
  if (otlpApp) await otlpApp.close().catch(() => {
21164
21835
  });
21165
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21836
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21166
21837
  });
21167
21838
  throw new Error(
21168
21839
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -21197,7 +21868,7 @@ async function startDaemon(opts = {}) {
21197
21868
  });
21198
21869
  if (otlpApp) await otlpApp.close().catch(() => {
21199
21870
  });
21200
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21871
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21201
21872
  });
21202
21873
  throw new Error(
21203
21874
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -21244,9 +21915,9 @@ async function startDaemon(opts = {}) {
21244
21915
  let registryWatcher = null;
21245
21916
  let reloadTimer = null;
21246
21917
  if (!singleProject) try {
21247
- const regDir = import_node_path73.default.dirname(regPath);
21248
- const regBase = import_node_path73.default.basename(regPath);
21249
- 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) => {
21250
21921
  if (filename !== null && filename !== regBase) return;
21251
21922
  if (reloadTimer) clearTimeout(reloadTimer);
21252
21923
  reloadTimer = setTimeout(() => {
@@ -21295,7 +21966,7 @@ async function startDaemon(opts = {}) {
21295
21966
  if (daemonRecord) {
21296
21967
  await clearDaemonRecord(daemonRecord, home);
21297
21968
  }
21298
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21969
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21299
21970
  });
21300
21971
  };
21301
21972
  return {
@@ -21318,9 +21989,9 @@ init_auth();
21318
21989
  // src/web-spawn.ts
21319
21990
  init_cjs_shims();
21320
21991
  var import_node_child_process2 = require("child_process");
21321
- var import_node_fs40 = require("fs");
21992
+ var import_node_fs41 = require("fs");
21322
21993
  var import_node_net = __toESM(require("net"), 1);
21323
- var import_node_path74 = __toESM(require("path"), 1);
21994
+ var import_node_path76 = __toESM(require("path"), 1);
21324
21995
  var DEFAULT_WEB_PORT = 6328;
21325
21996
  var DEFAULT_REST_PORT = 8080;
21326
21997
  function asValidPort(value) {
@@ -21329,11 +22000,11 @@ function asValidPort(value) {
21329
22000
  }
21330
22001
  function projectRoot() {
21331
22002
  const fromEnv = process.env.NEAT_SCAN_PATH;
21332
- 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());
21333
22004
  }
21334
22005
  async function readDaemonPorts(root) {
21335
22006
  try {
21336
- 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");
21337
22008
  const parsed = JSON.parse(raw);
21338
22009
  const ports = parsed?.ports ?? {};
21339
22010
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -21378,10 +22049,10 @@ function resolveWebPackageDir() {
21378
22049
  eval("require")
21379
22050
  );
21380
22051
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
21381
- return import_node_path74.default.dirname(pkgJsonPath);
22052
+ return import_node_path76.default.dirname(pkgJsonPath);
21382
22053
  }
21383
22054
  function resolveStandaloneServerEntry(webDir) {
21384
- 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");
21385
22056
  }
21386
22057
  async function pickInternalPort() {
21387
22058
  return new Promise((resolve, reject) => {
@@ -21434,7 +22105,7 @@ async function spawnWebUI(restPort, opts = {}) {
21434
22105
  NEAT_API_URL: apiUrl
21435
22106
  };
21436
22107
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
21437
- cwd: import_node_path74.default.dirname(serverEntry),
22108
+ cwd: import_node_path76.default.dirname(serverEntry),
21438
22109
  env,
21439
22110
  stdio: ["ignore", "inherit", "inherit"],
21440
22111
  detached: false
@@ -21561,15 +22232,15 @@ async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
21561
22232
  }
21562
22233
  function isLocalBehind(local, remote) {
21563
22234
  if (local === remote) return false;
21564
- const parse10 = (v) => {
22235
+ const parse11 = (v) => {
21565
22236
  const core = v.split(/[-+]/)[0] ?? v;
21566
22237
  return core.split(".").map((s) => {
21567
22238
  const n = Number.parseInt(s, 10);
21568
22239
  return Number.isFinite(n) ? n : 0;
21569
22240
  });
21570
22241
  };
21571
- const a = parse10(local);
21572
- const b = parse10(remote);
22242
+ const a = parse11(local);
22243
+ const b = parse11(remote);
21573
22244
  const len = Math.max(a.length, b.length);
21574
22245
  for (let i = 0; i < len; i++) {
21575
22246
  const av = a[i] ?? 0;
@@ -21610,14 +22281,14 @@ function localVersion() {
21610
22281
  }
21611
22282
  function neatHome3() {
21612
22283
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
21613
- return import_node_path75.default.resolve(process.env.NEAT_HOME);
22284
+ return import_node_path77.default.resolve(process.env.NEAT_HOME);
21614
22285
  }
21615
22286
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21616
- return import_node_path75.default.join(home, ".neat");
22287
+ return import_node_path77.default.join(home, ".neat");
21617
22288
  }
21618
22289
  async function readPid() {
21619
22290
  try {
21620
- 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");
21621
22292
  const n = Number.parseInt(raw.trim(), 10);
21622
22293
  return Number.isFinite(n) ? n : null;
21623
22294
  } catch {