@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/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path74 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path74) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path74)) {
63
+ const path76 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
193
193
  };
194
194
  }
195
195
  function resolveProtoRoot() {
196
- const here = import_node_path51.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
- return import_node_path51.default.resolve(here, "..", "proto");
196
+ const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
+ return import_node_path52.default.resolve(here, "..", "proto");
198
198
  }
199
199
  function loadTraceService() {
200
200
  const protoRoot = resolveProtoRoot();
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
262
262
  })
263
263
  };
264
264
  }
265
- var import_node_url, import_node_path51, import_node_crypto2, grpc, protoLoader;
265
+ var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
266
266
  var init_otel_grpc = __esm({
267
267
  "src/otel-grpc.ts"() {
268
268
  "use strict";
269
269
  init_cjs_shims();
270
270
  import_node_url = require("url");
271
- import_node_path51 = __toESM(require("path"), 1);
271
+ import_node_path52 = __toESM(require("path"), 1);
272
272
  import_node_crypto2 = require("crypto");
273
273
  grpc = __toESM(require("@grpc/grpc-js"), 1);
274
274
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
414
414
  const v = attrs[key];
415
415
  if (typeof v === "string" && v.length > 0) {
416
416
  const q = v.indexOf("?");
417
- const path74 = q === -1 ? v : v.slice(0, q);
418
- if (path74.length > 0) return path74;
417
+ const path76 = q === -1 ? v : v.slice(0, q);
418
+ if (path76.length > 0) return path76;
419
419
  }
420
420
  }
421
421
  return void 0;
@@ -477,10 +477,10 @@ function parseOtlpRequest(body) {
477
477
  return out;
478
478
  }
479
479
  function loadProtoRoot() {
480
- const here = import_node_path52.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
- const protoRoot = import_node_path52.default.resolve(here, "..", "proto");
480
+ const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
+ const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
482
482
  const root = new import_protobufjs.default.Root();
483
- root.resolvePath = (_origin, target) => import_node_path52.default.resolve(protoRoot, target);
483
+ root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
484
484
  root.loadSync(
485
485
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
486
486
  { keepCase: true }
@@ -735,12 +735,12 @@ function logSpanHandler(span) {
735
735
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
736
736
  );
737
737
  }
738
- var import_node_path52, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
738
+ var import_node_path53, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
739
739
  var init_otel = __esm({
740
740
  "src/otel.ts"() {
741
741
  "use strict";
742
742
  init_cjs_shims();
743
- import_node_path52 = __toESM(require("path"), 1);
743
+ import_node_path53 = __toESM(require("path"), 1);
744
744
  import_node_url2 = require("url");
745
745
  import_fastify = __toESM(require("fastify"), 1);
746
746
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -1333,19 +1333,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1333
1333
  function longestIncomingWalk(graph, start, maxDepth) {
1334
1334
  let best = { path: [start], edges: [] };
1335
1335
  const visited = /* @__PURE__ */ new Set([start]);
1336
- function step(node, path74, edges) {
1337
- if (path74.length > best.path.length) {
1338
- best = { path: [...path74], edges: [...edges] };
1336
+ function step(node, path76, edges) {
1337
+ if (path76.length > best.path.length) {
1338
+ best = { path: [...path76], edges: [...edges] };
1339
1339
  }
1340
- if (path74.length - 1 >= maxDepth) return;
1340
+ if (path76.length - 1 >= maxDepth) return;
1341
1341
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1342
1342
  for (const [srcId, edge] of incoming) {
1343
1343
  if (visited.has(srcId)) continue;
1344
1344
  visited.add(srcId);
1345
- path74.push(srcId);
1345
+ path76.push(srcId);
1346
1346
  edges.push(edge);
1347
- step(srcId, path74, edges);
1348
- path74.pop();
1347
+ step(srcId, path76, edges);
1348
+ path76.pop();
1349
1349
  edges.pop();
1350
1350
  visited.delete(srcId);
1351
1351
  }
@@ -1353,11 +1353,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1353
1353
  step(start, [start], []);
1354
1354
  return best;
1355
1355
  }
1356
- function databaseRootCauseShape(graph, origin, walk9) {
1356
+ function databaseRootCauseShape(graph, origin, walk10) {
1357
1357
  const targetDb = origin;
1358
1358
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1359
1359
  if (candidatePairs.length === 0) return null;
1360
- for (const id of walk9.path) {
1360
+ for (const id of walk10.path) {
1361
1361
  const owner = resolveOwningService(graph, id);
1362
1362
  if (!owner) continue;
1363
1363
  const { id: serviceId15, svc } = owner;
@@ -1384,8 +1384,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
1384
1384
  }
1385
1385
  return null;
1386
1386
  }
1387
- function serviceRootCauseShape(graph, _origin, walk9) {
1388
- for (const id of walk9.path) {
1387
+ function serviceRootCauseShape(graph, _origin, walk10) {
1388
+ for (const id of walk10.path) {
1389
1389
  const owner = resolveOwningService(graph, id);
1390
1390
  if (!owner) continue;
1391
1391
  const { id: serviceId15, svc } = owner;
@@ -1421,15 +1421,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
1421
1421
  }
1422
1422
  return null;
1423
1423
  }
1424
- function fileRootCauseShape(graph, origin, walk9) {
1424
+ function fileRootCauseShape(graph, origin, walk10) {
1425
1425
  const owner = resolveOwningService(graph, origin.id);
1426
1426
  if (!owner) return null;
1427
- return serviceRootCauseShape(graph, owner.svc, walk9);
1427
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1428
1428
  }
1429
- function symbolRootCauseShape(graph, origin, walk9) {
1429
+ function symbolRootCauseShape(graph, origin, walk10) {
1430
1430
  const owner = resolveOwningService(graph, origin.id);
1431
1431
  if (!owner) return null;
1432
- return serviceRootCauseShape(graph, owner.svc, walk9);
1432
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1433
1433
  }
1434
1434
  var rootCauseShapes = {
1435
1435
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1442,25 +1442,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1442
1442
  const origin = graph.getNodeAttributes(errorNodeId);
1443
1443
  const shape = rootCauseShapes[origin.type];
1444
1444
  if (shape) {
1445
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1446
- const match = shape(graph, origin, walk9);
1445
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1446
+ const match = shape(graph, origin, walk10);
1447
1447
  if (match) {
1448
1448
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1449
- return import_types.RootCauseResultSchema.parse({
1450
- rootCauseNode: match.rootCauseNode,
1451
- rootCauseReason: reason,
1452
- traversalPath: walk9.path,
1453
- edgeProvenances: walk9.edges.map((e) => e.provenance),
1454
- confidence: confidenceFromMix(walk9.edges),
1455
- fixRecommendation: match.fixRecommendation
1456
- });
1449
+ return {
1450
+ source: "compat",
1451
+ result: import_types.RootCauseResultSchema.parse({
1452
+ rootCauseNode: match.rootCauseNode,
1453
+ rootCauseReason: reason,
1454
+ traversalPath: walk10.path,
1455
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
1456
+ confidence: confidenceFromMix(walk10.edges),
1457
+ fixRecommendation: match.fixRecommendation
1458
+ })
1459
+ };
1457
1460
  }
1458
1461
  }
1459
1462
  if (origin.type === import_types.NodeType.ServiceNode) {
1460
1463
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
1461
- if (crossService) return crossService;
1464
+ if (crossService) return { result: crossService, source: "cross-service" };
1462
1465
  }
1463
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1466
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1467
+ return incident ? { result: incident, source: "incident" } : null;
1464
1468
  }
1465
1469
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
1466
1470
  function incidentMatchesNode(ev, nodeId) {
@@ -1556,26 +1560,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
1556
1560
  return best;
1557
1561
  }
1558
1562
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1559
- const path74 = [originServiceId];
1563
+ const path76 = [originServiceId];
1560
1564
  const edges = [];
1561
1565
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1562
1566
  let current = originServiceId;
1563
1567
  for (let depth = 0; depth < maxDepth; depth++) {
1564
1568
  const hop = dominantFailingCall(graph, current, visited);
1565
1569
  if (!hop) break;
1566
- path74.push(hop.nextService);
1570
+ path76.push(hop.nextService);
1567
1571
  edges.push(hop.edge);
1568
1572
  visited.add(hop.nextService);
1569
1573
  current = hop.nextService;
1570
1574
  }
1571
1575
  if (edges.length === 0) return null;
1572
- return { path: path74, edges, culprit: current };
1576
+ return { path: path76, edges, culprit: current };
1577
+ }
1578
+ function isStaleCallEdge(e) {
1579
+ return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
1580
+ }
1581
+ function staleCallDominates(e, id, curEdge, curId) {
1582
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
1583
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
1584
+ if (ev !== cv) return ev > cv;
1585
+ return id < curId;
1586
+ }
1587
+ function dominantStaleCall(graph, serviceId15, visited) {
1588
+ const bestByCallee = /* @__PURE__ */ new Map();
1589
+ for (const src of callSourcesForService(graph, serviceId15)) {
1590
+ for (const edgeId of graph.outboundEdges(src)) {
1591
+ const e = graph.getEdgeAttributes(edgeId);
1592
+ if (e.type !== import_types.EdgeType.CALLS) continue;
1593
+ if (isFrontierNode(graph, e.target)) continue;
1594
+ const owner = resolveOwningService(graph, e.target);
1595
+ if (!owner || visited.has(owner.id)) continue;
1596
+ const cur = bestByCallee.get(owner.id);
1597
+ if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
1598
+ bestByCallee.set(owner.id, e);
1599
+ }
1600
+ }
1601
+ }
1602
+ let best = null;
1603
+ for (const [id, edge] of bestByCallee) {
1604
+ if (!isStaleCallEdge(edge)) continue;
1605
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
1606
+ best = { nextService: id, edge };
1607
+ }
1608
+ }
1609
+ return best;
1610
+ }
1611
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
1612
+ const path76 = [originServiceId];
1613
+ const edges = [];
1614
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
1615
+ let current = originServiceId;
1616
+ for (let depth = 0; depth < maxDepth; depth++) {
1617
+ const hop = dominantStaleCall(graph, current, visited);
1618
+ if (!hop) break;
1619
+ path76.push(hop.nextService);
1620
+ edges.push(hop.edge);
1621
+ visited.add(hop.nextService);
1622
+ current = hop.nextService;
1623
+ }
1624
+ if (edges.length === 0) return null;
1625
+ return { path: path76, edges, culprit: current };
1573
1626
  }
1574
1627
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1575
1628
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1576
1629
  if (!chain) return null;
1577
1630
  const culprit = chain.culprit;
1578
- const path74 = [...chain.path];
1631
+ const path76 = [...chain.path];
1579
1632
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1580
1633
  const baseConfidence = confidenceFromMix(chain.edges);
1581
1634
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1583,14 +1636,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1583
1636
  if (loc) {
1584
1637
  let rootCauseNode = culprit;
1585
1638
  if (loc.fileNode) {
1586
- path74.push(loc.fileNode);
1639
+ path76.push(loc.fileNode);
1587
1640
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1588
1641
  rootCauseNode = loc.fileNode;
1589
1642
  }
1590
1643
  return import_types.RootCauseResultSchema.parse({
1591
1644
  rootCauseNode,
1592
1645
  rootCauseReason: loc.rootCauseReason,
1593
- traversalPath: path74,
1646
+ traversalPath: path76,
1594
1647
  edgeProvenances,
1595
1648
  confidence,
1596
1649
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1602,7 +1655,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1602
1655
  return import_types.RootCauseResultSchema.parse({
1603
1656
  rootCauseNode: culprit,
1604
1657
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1605
- traversalPath: path74,
1658
+ traversalPath: path76,
1606
1659
  edgeProvenances,
1607
1660
  confidence,
1608
1661
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -1989,17 +2042,20 @@ function displayNameOf(nodeId) {
1989
2042
  return nodeId.replace(/^[a-z]+:/, "");
1990
2043
  }
1991
2044
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
1992
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
1993
- if (!legacy) return null;
2045
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
2046
+ if (!tagged) return null;
1994
2047
  const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
1995
- if (!navigation) return legacy;
1996
- return enrichWithNavigation(graph, errorNodeId, legacy, incidents, opts?.now ?? Date.now());
2048
+ if (!navigation) return tagged.result;
2049
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
1997
2050
  }
1998
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2051
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2052
+ const legacy = tagged.result;
1999
2053
  const seedNode = legacy.rootCauseNode;
2000
2054
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
2001
2055
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
2002
2056
  const candidates = [];
2057
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
2058
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
2003
2059
  if (seedCtx && isVictimSeed(seedCtx)) {
2004
2060
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
2005
2061
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -2024,6 +2080,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2024
2080
  confidence: Math.min(legacy.confidence, 0.4),
2025
2081
  ...lastProv ? { provenance: lastProv } : {}
2026
2082
  });
2083
+ } else if (staleChain) {
2084
+ const culprit = staleChain.culprit;
2085
+ const culpritName = displayNameOf(culprit);
2086
+ const seedName = displayNameOf(seedNode);
2087
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
2088
+ candidates.push({
2089
+ node: culprit,
2090
+ classification: "primary-failure",
2091
+ reason: `${culpritName} is the stale-derived root cause (low confidence): live telemetry for this subgraph has gone quiet, but the last-observed topology traces the failure surfacing at ${seedName} downstream through a STALE call chain to ${culpritName}. Provenance is STALE, so confidence is capped low \u2014 restore instrumentation and re-run to confirm before acting.`,
2092
+ context: nodeContext(graph, culprit, incidents, now),
2093
+ confidence: staleConfidence,
2094
+ provenance: import_types.Provenance.STALE
2095
+ });
2096
+ candidates.push({
2097
+ node: seedNode,
2098
+ classification: "symptom-only",
2099
+ reason: `The failure surfaced here, but the only causal chain the graph still holds is STALE and runs downstream \u2014 ${seedName} is the surface of a stale-traced failure, not a proven origin.`,
2100
+ context: seedCtx ?? EMPTY_CONTEXT,
2101
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
2102
+ ...lastProv ? { provenance: lastProv } : {}
2103
+ });
2027
2104
  } else {
2028
2105
  candidates.push({
2029
2106
  node: seedNode,
@@ -2037,11 +2114,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2037
2114
  const top = candidates[0];
2038
2115
  let traversalPath = legacy.traversalPath;
2039
2116
  let edgeProvenances = legacy.edgeProvenances;
2040
- if (top.node !== seedNode) {
2041
- const path74 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2042
- if (path74) {
2043
- traversalPath = path74.nodes;
2044
- edgeProvenances = path74.edges.map((e) => e.provenance);
2117
+ if (staleChain && top.node === staleChain.culprit) {
2118
+ traversalPath = staleChain.path;
2119
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
2120
+ } else if (top.node !== seedNode) {
2121
+ const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2122
+ if (path76) {
2123
+ traversalPath = path76.nodes;
2124
+ edgeProvenances = path76.edges.map((e) => e.provenance);
2045
2125
  } else {
2046
2126
  traversalPath = [errorNodeId, top.node];
2047
2127
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2063,6 +2143,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
2063
2143
  return legacy.fixRecommendation;
2064
2144
  }
2065
2145
  const name = top.node.replace(/^service:/, "");
2146
+ if (top.provenance === import_types.Provenance.STALE) {
2147
+ return `Live telemetry for this path has gone quiet; the last-observed topology traces the failure downstream to ${name}. Restore instrumentation (or re-run with live traces) to confirm, then inspect ${name}.`;
2148
+ }
2066
2149
  if (top.classification === "primary-failure") {
2067
2150
  return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
2068
2151
  }
@@ -2581,6 +2664,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2581
2664
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2582
2665
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2583
2666
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2667
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2584
2668
  var import_types6 = require("@neat.is/types");
2585
2669
 
2586
2670
  // src/extract/shared.ts
@@ -2861,7 +2945,7 @@ function buildServiceHostIndex(services) {
2861
2945
  async function walkSourceFiles(dir, excludeDirs = []) {
2862
2946
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
2863
2947
  const out = [];
2864
- async function walk9(current) {
2948
+ async function walk10(current) {
2865
2949
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2866
2950
  for (const entry of entries) {
2867
2951
  const full = import_node_path5.default.join(current, entry.name);
@@ -2869,7 +2953,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2869
2953
  if (IGNORED_DIRS.has(entry.name)) continue;
2870
2954
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
2871
2955
  if (await isPythonVenvDir(full)) continue;
2872
- await walk9(full);
2956
+ await walk10(full);
2873
2957
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2874
2958
  // would attribute our instrumentation imports to the user's service.
2875
2959
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2877,7 +2961,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2877
2961
  }
2878
2962
  }
2879
2963
  }
2880
- await walk9(dir);
2964
+ await walk10(dir);
2881
2965
  return out;
2882
2966
  }
2883
2967
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3397,6 +3481,11 @@ function makePhpParser() {
3397
3481
  p.setLanguage(import_tree_sitter_php.default.php_only);
3398
3482
  return p;
3399
3483
  }
3484
+ function makeRustParser() {
3485
+ const p = new import_tree_sitter2.default();
3486
+ p.setLanguage(import_tree_sitter_rust.default);
3487
+ return p;
3488
+ }
3400
3489
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
3401
3490
  "get",
3402
3491
  "post",
@@ -3468,8 +3557,8 @@ function chiRoutesFromSource(source, parser) {
3468
3557
  chiWalk(tree.rootNode, "", out);
3469
3558
  return out;
3470
3559
  }
3471
- function stripChiRegex(path74) {
3472
- return path74.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3560
+ function stripChiRegex(path76) {
3561
+ return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3473
3562
  }
3474
3563
  function chiWalk(node, prefix, out) {
3475
3564
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4141,9 +4230,9 @@ function rubyRocketRoute(args) {
4141
4230
  if (!pair || pair.type !== "pair") continue;
4142
4231
  const k = pair.childForFieldName("key");
4143
4232
  if (k?.type !== "string") continue;
4144
- const path74 = rubyLiteral(k);
4145
- if (path74 === null) continue;
4146
- return { path: path74, target: rubyLiteral(pair.childForFieldName("value")) };
4233
+ const path76 = rubyLiteral(k);
4234
+ if (path76 === null) continue;
4235
+ return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
4147
4236
  }
4148
4237
  return null;
4149
4238
  }
@@ -4313,6 +4402,48 @@ function railsRoutesFromSource(source, parser) {
4313
4402
  });
4314
4403
  return out;
4315
4404
  }
4405
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4406
+ function sinatraRoutesFromSource(source, parser) {
4407
+ const tree = parseSource2(parser, source);
4408
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
4409
+ const out = [];
4410
+ walk(tree.rootNode, (node) => {
4411
+ if (node.type !== "call") return;
4412
+ if (node.childForFieldName("receiver")) return;
4413
+ const method = node.childForFieldName("method")?.text;
4414
+ if (!method || !SINATRA_VERBS.has(method)) return;
4415
+ if (!node.childForFieldName("block")) return;
4416
+ const first = node.childForFieldName("arguments")?.namedChild(0);
4417
+ if (first?.type !== "string") return;
4418
+ const p = rubyLiteral(first);
4419
+ if (p === null || !p.startsWith("/")) return;
4420
+ out.push({
4421
+ method: method.toUpperCase(),
4422
+ pathTemplate: canonicalizeTemplate(p),
4423
+ line: node.startPosition.row + 1,
4424
+ framework: "sinatra"
4425
+ });
4426
+ });
4427
+ return out;
4428
+ }
4429
+ function fileReferencesSinatra(root) {
4430
+ let found = false;
4431
+ walk(root, (node) => {
4432
+ if (found) return;
4433
+ if (node.type === "call") {
4434
+ const m = node.childForFieldName("method")?.text;
4435
+ if (m === "require" || m === "require_relative") {
4436
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
4437
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
4438
+ }
4439
+ return;
4440
+ }
4441
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
4442
+ found = true;
4443
+ }
4444
+ });
4445
+ return found;
4446
+ }
4316
4447
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
4317
4448
  var LARAVEL_RESOURCE_ROWS = [
4318
4449
  { action: "index", methods: ["GET"], suffix: "" },
@@ -4488,6 +4619,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
4488
4619
  }
4489
4620
  return out;
4490
4621
  }
4622
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4623
+ function isSlimAppCtor(node) {
4624
+ if (!node) return false;
4625
+ if (node.type === "scoped_call_expression") {
4626
+ const scope = node.childForFieldName("scope")?.text ?? "";
4627
+ const name = node.childForFieldName("name")?.text;
4628
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
4629
+ }
4630
+ if (node.type === "object_creation_expression") {
4631
+ const cls = node.namedChild(0)?.text ?? "";
4632
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
4633
+ }
4634
+ return false;
4635
+ }
4636
+ function isSlimAppType(node) {
4637
+ if (!node || node.type !== "named_type") return false;
4638
+ const text = node.text;
4639
+ return text === "App" || text.endsWith("\\App");
4640
+ }
4641
+ function collectSlimAppVars(root) {
4642
+ const vars = /* @__PURE__ */ new Set();
4643
+ walk(root, (node) => {
4644
+ if (node.type === "assignment_expression") {
4645
+ const left = node.childForFieldName("left");
4646
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
4647
+ vars.add(left.text);
4648
+ }
4649
+ return;
4650
+ }
4651
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
4652
+ const name = node.childForFieldName("name");
4653
+ if (name?.type === "variable_name") vars.add(name.text);
4654
+ }
4655
+ });
4656
+ return vars;
4657
+ }
4658
+ function slimClosureParamVars(closure) {
4659
+ const out = ["$this"];
4660
+ for (let i = 0; i < closure.namedChildCount; i++) {
4661
+ const params = closure.namedChild(i);
4662
+ if (params?.type !== "formal_parameters") continue;
4663
+ for (let j = 0; j < params.namedChildCount; j++) {
4664
+ const param = params.namedChild(j);
4665
+ if (param?.type !== "simple_parameter") continue;
4666
+ for (let k = 0; k < param.namedChildCount; k++) {
4667
+ const v = param.namedChild(k);
4668
+ if (v?.type === "variable_name") {
4669
+ out.push(v.text);
4670
+ break;
4671
+ }
4672
+ }
4673
+ }
4674
+ }
4675
+ return out;
4676
+ }
4677
+ function phpStringArray(node) {
4678
+ const out = [];
4679
+ if (node?.type !== "array_creation_expression") return out;
4680
+ for (let i = 0; i < node.namedChildCount; i++) {
4681
+ const el = node.namedChild(i);
4682
+ if (el?.type !== "array_element_initializer") continue;
4683
+ const s = phpStaticString(el.namedChild(0));
4684
+ if (s !== null) out.push(s);
4685
+ }
4686
+ return out;
4687
+ }
4688
+ function slimRoutesFromSource(source, parser) {
4689
+ const tree = parseSource2(parser, source);
4690
+ const appVars = collectSlimAppVars(tree.rootNode);
4691
+ if (appVars.size === 0) return [];
4692
+ const out = [];
4693
+ slimWalk(tree.rootNode, "", appVars, out);
4694
+ return out;
4695
+ }
4696
+ function slimWalk(node, prefix, appVars, out) {
4697
+ for (let i = 0; i < node.namedChildCount; i++) {
4698
+ const child = node.namedChild(i);
4699
+ if (child) slimHandle(child, prefix, appVars, out);
4700
+ }
4701
+ }
4702
+ function slimHandle(node, prefix, appVars, out) {
4703
+ if (node.type === "member_call_expression") {
4704
+ const obj = node.childForFieldName("object");
4705
+ const method = node.childForFieldName("name")?.text;
4706
+ const args = node.childForFieldName("arguments");
4707
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4708
+ const line = node.startPosition.row + 1;
4709
+ if (method === "group") {
4710
+ const groupPrefix = phpFirstString(args);
4711
+ const closure = laravelGroupClosure(args);
4712
+ if (groupPrefix !== null && closure) {
4713
+ const inner = new Set(appVars);
4714
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4715
+ const body = closure.childForFieldName("body");
4716
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4717
+ }
4718
+ return;
4719
+ }
4720
+ if (SLIM_VERBS.has(method)) {
4721
+ const p = phpFirstString(args);
4722
+ if (p !== null) {
4723
+ out.push({
4724
+ method: method.toUpperCase(),
4725
+ pathTemplate: laravelJoinPath(prefix, p),
4726
+ line,
4727
+ framework: "slim"
4728
+ });
4729
+ }
4730
+ return;
4731
+ }
4732
+ if (method === "any") {
4733
+ const p = phpFirstString(args);
4734
+ if (p !== null) {
4735
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4736
+ }
4737
+ return;
4738
+ }
4739
+ if (method === "map") {
4740
+ const vals = phpArgumentValues(args);
4741
+ const methods = phpStringArray(vals[0]);
4742
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4743
+ if (p !== null) {
4744
+ for (const m of methods) {
4745
+ out.push({
4746
+ method: m.toUpperCase(),
4747
+ pathTemplate: laravelJoinPath(prefix, p),
4748
+ line,
4749
+ framework: "slim"
4750
+ });
4751
+ }
4752
+ }
4753
+ return;
4754
+ }
4755
+ }
4756
+ }
4757
+ slimWalk(node, prefix, appVars, out);
4758
+ }
4759
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4760
+ function rustStringContent(node) {
4761
+ if (!node || node.type !== "string_literal") return null;
4762
+ for (let i = 0; i < node.namedChildCount; i++) {
4763
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4764
+ }
4765
+ return "";
4766
+ }
4767
+ function actixRoutesFromSource(source, parser) {
4768
+ const tree = parseSource2(parser, source);
4769
+ const out = [];
4770
+ walk(tree.rootNode, (node) => {
4771
+ if (node.type === "attribute_item") {
4772
+ actixAttributeRoute(node, out);
4773
+ return;
4774
+ }
4775
+ if (node.type === "call_expression") {
4776
+ actixBuilderRoute(node, out);
4777
+ }
4778
+ });
4779
+ return out;
4780
+ }
4781
+ function actixAttributeRoute(attrItem, out) {
4782
+ const attr = attrItem.namedChild(0);
4783
+ if (!attr || attr.type !== "attribute") return;
4784
+ const nameNode = attr.namedChild(0);
4785
+ if (!nameNode) return;
4786
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4787
+ if (!macro) return;
4788
+ const tokens = attr.childForFieldName("arguments");
4789
+ if (!tokens || tokens.type !== "token_tree") return;
4790
+ const strings = [];
4791
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4792
+ const s = rustStringContent(tokens.namedChild(i));
4793
+ if (s !== null) strings.push(s);
4794
+ }
4795
+ const pathStr = strings[0];
4796
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4797
+ const line = attrItem.startPosition.row + 1;
4798
+ const template = canonicalizeTemplate(pathStr);
4799
+ if (ACTIX_METHODS.has(macro)) {
4800
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4801
+ return;
4802
+ }
4803
+ if (macro === "route") {
4804
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4805
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4806
+ for (const m of list) {
4807
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4808
+ }
4809
+ }
4810
+ }
4811
+ function actixBuilderRoute(call, out) {
4812
+ const fn = call.childForFieldName("function");
4813
+ if (fn?.type !== "field_expression") return;
4814
+ if (fn.childForFieldName("field")?.text !== "route") return;
4815
+ const args = call.childForFieldName("arguments");
4816
+ const pathStr = rustStringContent(args?.namedChild(0));
4817
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4818
+ const second = args?.namedChild(1);
4819
+ if (!second) return;
4820
+ const method = actixBuilderMethod(second);
4821
+ if (!method) return;
4822
+ out.push({
4823
+ method,
4824
+ pathTemplate: canonicalizeTemplate(pathStr),
4825
+ line: call.startPosition.row + 1,
4826
+ framework: "actix-web"
4827
+ });
4828
+ }
4829
+ function actixBuilderMethod(node) {
4830
+ let method = null;
4831
+ walk(node, (n) => {
4832
+ if (method || n.type !== "scoped_identifier") return;
4833
+ const verb = n.childForFieldName("name")?.text;
4834
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4835
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4836
+ });
4837
+ return method;
4838
+ }
4491
4839
  function namedArgs(argsNode) {
4492
4840
  const out = [];
4493
4841
  if (!argsNode) return out;
@@ -4804,6 +5152,7 @@ async function addRoutes(graph, services) {
4804
5152
  const goParser = makeGoParser2();
4805
5153
  const rubyParser = makeRubyParser();
4806
5154
  const phpParser = makePhpParser();
5155
+ const rustParser = makeRustParser();
4807
5156
  let nodesAdded = 0;
4808
5157
  let edgesAdded = 0;
4809
5158
  for (const service of services) {
@@ -4826,7 +5175,10 @@ async function addRoutes(graph, services) {
4826
5175
  const isGoService = service.node.language === "go";
4827
5176
  const hasRails = deps["rails"] !== void 0;
4828
5177
  const hasLaravel = deps["laravel/framework"] !== void 0;
4829
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
5178
+ const hasSlim = deps["slim/slim"] !== void 0;
5179
+ const hasSinatra = deps["sinatra"] !== void 0;
5180
+ const hasActix = deps["actix-web"] !== void 0;
5181
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4830
5182
  continue;
4831
5183
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4832
5184
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4837,7 +5189,8 @@ async function addRoutes(graph, services) {
4837
5189
  const isGo = ext === ".go";
4838
5190
  const isRb = ext === ".rb";
4839
5191
  const isPhp = ext === ".php";
4840
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
5192
+ const isRs = ext === ".rs";
5193
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4841
5194
  const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
4842
5195
  let routes;
4843
5196
  try {
@@ -4847,8 +5200,12 @@ async function addRoutes(graph, services) {
4847
5200
  phpParser,
4848
5201
  relFile === "routes/api.php" ? "/api" : ""
4849
5202
  ) : [];
5203
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4850
5204
  } else if (isRb) {
4851
5205
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
5206
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
5207
+ } else if (isRs) {
5208
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4852
5209
  } else if (isGo) {
4853
5210
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4854
5211
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -5083,6 +5440,37 @@ function loadIncidentThresholdsFromEnv() {
5083
5440
  return DEFAULT_INCIDENT_THRESHOLDS;
5084
5441
  }
5085
5442
  }
5443
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
5444
+ function latencyStreamCeilingMs() {
5445
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
5446
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
5447
+ const n = Number(raw);
5448
+ if (Number.isFinite(n) && n > 0) return n;
5449
+ console.warn(
5450
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
5451
+ );
5452
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
5453
+ }
5454
+ function spanServesEventStream(attrs) {
5455
+ for (const key of [
5456
+ "http.response.header.content-type",
5457
+ "http.response.header.content_type"
5458
+ ]) {
5459
+ const v = attrs[key];
5460
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
5461
+ for (const item of values) {
5462
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
5463
+ return true;
5464
+ }
5465
+ }
5466
+ }
5467
+ return false;
5468
+ }
5469
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
5470
+ if (span.websocketChannel !== void 0) return true;
5471
+ if (spanServesEventStream(span.attributes)) return true;
5472
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
5473
+ }
5086
5474
  function httpResponseStatusFromAttrs(attrs) {
5087
5475
  for (const key of ["http.response.status_code", "http.status_code"]) {
5088
5476
  const v = attrs[key];
@@ -5975,6 +6363,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5975
6363
  );
5976
6364
  ctx.burstState.delete(key);
5977
6365
  }
6366
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
6367
+ function nextApiRouteTemplate(span) {
6368
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
6369
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
6370
+ return match ? match[1] : void 0;
6371
+ }
5978
6372
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5979
6373
  const target = normalizePathTemplate(httpRoute);
5980
6374
  const m = method?.toUpperCase();
@@ -5997,7 +6391,7 @@ async function handleSpan(ctx, span) {
5997
6391
  }
5998
6392
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5999
6393
  const isError = span.statusCode === 2;
6000
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
6394
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
6001
6395
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
6002
6396
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
6003
6397
  cacheSpanService(span, nowMs, callSite);
@@ -6194,12 +6588,13 @@ async function handleSpan(ctx, span) {
6194
6588
  }
6195
6589
  }
6196
6590
  }
6197
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6591
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
6592
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6198
6593
  const routeNodeId = findRouteNodeByHttpRoute(
6199
6594
  ctx.graph,
6200
6595
  span.service,
6201
6596
  span.httpMethod,
6202
- span.httpRoute
6597
+ fusionRoute
6203
6598
  );
6204
6599
  if (routeNodeId) {
6205
6600
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -7590,7 +7985,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
7590
7985
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
7591
7986
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
7592
7987
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
7593
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
7988
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
7594
7989
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
7595
7990
  var import_types20 = require("@neat.is/types");
7596
7991
  var PARSE_CHUNK3 = 16384;
@@ -7611,7 +8006,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
7611
8006
  ".cs": import_tree_sitter_c_sharp.default,
7612
8007
  ".java": import_tree_sitter_java.default,
7613
8008
  ".kt": import_tree_sitter_kotlin.default,
7614
- ".rs": import_tree_sitter_rust.default,
8009
+ ".rs": import_tree_sitter_rust2.default,
7615
8010
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
7616
8011
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
7617
8012
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8051,15 +8446,15 @@ function collectKotlinSymbolDefs(root) {
8051
8446
  });
8052
8447
  };
8053
8448
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8054
- const firstChildOfType = (node, types) => {
8449
+ const firstChildOfType2 = (node, types) => {
8055
8450
  for (let i = 0; i < node.namedChildCount; i++) {
8056
8451
  const child = node.namedChild(i);
8057
8452
  if (child && types.includes(child.type)) return child;
8058
8453
  }
8059
8454
  return void 0;
8060
8455
  };
8061
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8062
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
8456
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
8457
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8063
8458
  let pkg;
8064
8459
  for (let i = 0; i < root.namedChildCount; i++) {
8065
8460
  const child = root.namedChild(i);
@@ -8560,7 +8955,7 @@ async function addSymbolEdges(graph, services) {
8560
8955
  return best;
8561
8956
  };
8562
8957
  const requests = [];
8563
- const walk9 = (node) => {
8958
+ const walk10 = (node) => {
8564
8959
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
8565
8960
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
8566
8961
  if (self && self.kind === "class") {
@@ -8606,10 +9001,10 @@ async function addSymbolEdges(graph, services) {
8606
9001
  }
8607
9002
  for (let i = 0; i < node.namedChildCount; i++) {
8608
9003
  const child = node.namedChild(i);
8609
- if (child) walk9(child);
9004
+ if (child) walk10(child);
8610
9005
  }
8611
9006
  };
8612
- walk9(root);
9007
+ walk10(root);
8613
9008
  for (const req of requests) {
8614
9009
  const targetSid = resolveTarget(req.targetName, req.wantKind);
8615
9010
  if (!targetSid) continue;
@@ -8905,7 +9300,7 @@ async function addServerActions(graph, services) {
8905
9300
 
8906
9301
  // src/extract/databases/index.ts
8907
9302
  init_cjs_shims();
8908
- var import_node_path33 = __toESM(require("path"), 1);
9303
+ var import_node_path34 = __toESM(require("path"), 1);
8909
9304
  var import_types23 = require("@neat.is/types");
8910
9305
 
8911
9306
  // src/extract/databases/db-config-yaml.ts
@@ -9340,9 +9735,163 @@ async function parse8(serviceDir) {
9340
9735
  }
9341
9736
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9342
9737
 
9343
- // src/extract/databases/docker-compose.ts
9738
+ // src/extract/databases/csharp.ts
9344
9739
  init_cjs_shims();
9740
+ var import_node_fs22 = require("fs");
9345
9741
  var import_node_path32 = __toESM(require("path"), 1);
9742
+ var CS_EXT = ".cs";
9743
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
9744
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
9745
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
9746
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
9747
+ function hostIsUnresolved(host) {
9748
+ return host === "" || /[${}]/.test(host);
9749
+ }
9750
+ function looksLikePostgres(s) {
9751
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
9752
+ }
9753
+ function looksLikeRedis(s) {
9754
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
9755
+ }
9756
+ function parsePostgresConnection(raw) {
9757
+ const s = raw.trim();
9758
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
9759
+ const fields = /* @__PURE__ */ new Map();
9760
+ for (const part of s.split(";")) {
9761
+ const eq = part.indexOf("=");
9762
+ if (eq < 0) continue;
9763
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
9764
+ const value = part.slice(eq + 1).trim();
9765
+ if (value && !fields.has(key)) fields.set(key, value);
9766
+ }
9767
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
9768
+ if (!hostRaw) return null;
9769
+ const host = hostRaw.split(",")[0].trim();
9770
+ if (hostIsUnresolved(host)) return null;
9771
+ const portRaw = fields.get("port");
9772
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
9773
+ const database = fields.get("database") ?? fields.get("db") ?? "";
9774
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
9775
+ }
9776
+ function parseRedisEndpoint(raw) {
9777
+ const s = raw.trim();
9778
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
9779
+ const first = s.split(",")[0].trim();
9780
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
9781
+ if (!m) return null;
9782
+ const host = m[1];
9783
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
9784
+ const port = m[2] ? Number(m[2]) : void 0;
9785
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
9786
+ }
9787
+ async function resolveEnvUpTree(startDir, name) {
9788
+ let dir = import_node_path32.default.resolve(startDir);
9789
+ for (let depth = 0; depth < 12; depth++) {
9790
+ const value = await resolveEnvVar(dir, name);
9791
+ if (value !== null) return value;
9792
+ const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
9793
+ const parent = import_node_path32.default.dirname(dir);
9794
+ if (atRepoRoot || parent === dir) break;
9795
+ dir = parent;
9796
+ }
9797
+ return null;
9798
+ }
9799
+ async function interpolateEnvRefs(value, dir) {
9800
+ const refs = /* @__PURE__ */ new Set();
9801
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
9802
+ refs.add(m[1] ?? m[2]);
9803
+ }
9804
+ let out = value;
9805
+ for (const name of refs) {
9806
+ const resolved = await resolveEnvUpTree(dir, name);
9807
+ if (resolved === null) continue;
9808
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
9809
+ }
9810
+ return out;
9811
+ }
9812
+ function stringLiterals(masked) {
9813
+ const out = [];
9814
+ STRING_LITERAL_RE.lastIndex = 0;
9815
+ let m;
9816
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
9817
+ return out;
9818
+ }
9819
+ function envKeys(masked) {
9820
+ const out = [];
9821
+ ENV_READ_RE.lastIndex = 0;
9822
+ let m;
9823
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
9824
+ const key = m[1] ?? m[2];
9825
+ if (key) out.push(key);
9826
+ }
9827
+ return out;
9828
+ }
9829
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
9830
+ const out = [];
9831
+ for (const key of keys) {
9832
+ const raw = await resolveEnvUpTree(serviceDir, key);
9833
+ if (raw === null) continue;
9834
+ const value = await interpolateEnvRefs(raw, serviceDir);
9835
+ if (!looksLike(value)) continue;
9836
+ const parsed = parse11(value);
9837
+ if (parsed) out.push(parsed);
9838
+ }
9839
+ for (const lit of literals) {
9840
+ if (!looksLike(lit)) continue;
9841
+ const parsed = parse11(lit);
9842
+ if (parsed) out.push(parsed);
9843
+ }
9844
+ return out;
9845
+ }
9846
+ async function parse9(serviceDir) {
9847
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
9848
+ (f) => import_node_path32.default.extname(f) === CS_EXT
9849
+ );
9850
+ if (files.length === 0) return [];
9851
+ const sources = [];
9852
+ for (const file of files) {
9853
+ const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
9854
+ if (content !== null) sources.push({ file, content });
9855
+ }
9856
+ let pgGateFile = null;
9857
+ let redisGateFile = null;
9858
+ for (const { file, content } of sources) {
9859
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
9860
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
9861
+ }
9862
+ if (!pgGateFile && !redisGateFile) return [];
9863
+ const literals = [];
9864
+ const keys = [];
9865
+ for (const { content } of sources) {
9866
+ const masked = maskCommentsInSource(content);
9867
+ literals.push(...stringLiterals(masked));
9868
+ keys.push(...envKeys(masked));
9869
+ }
9870
+ const out = [];
9871
+ const seenHosts = /* @__PURE__ */ new Set();
9872
+ const push = (config, sourceFile) => {
9873
+ const dedupe = `${config.engine}:${config.host}`;
9874
+ if (seenHosts.has(dedupe)) return;
9875
+ seenHosts.add(dedupe);
9876
+ out.push({ ...config, sourceFile });
9877
+ };
9878
+ if (pgGateFile) {
9879
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
9880
+ push(pg3, pgGateFile);
9881
+ }
9882
+ }
9883
+ if (redisGateFile) {
9884
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
9885
+ push(redis, redisGateFile);
9886
+ }
9887
+ }
9888
+ return out;
9889
+ }
9890
+ var csharpParser = { name: "csharp", parse: parse9 };
9891
+
9892
+ // src/extract/databases/docker-compose.ts
9893
+ init_cjs_shims();
9894
+ var import_node_path33 = __toESM(require("path"), 1);
9346
9895
  function portFromService(svc) {
9347
9896
  for (const raw of svc.ports ?? []) {
9348
9897
  const str = String(raw);
@@ -9367,9 +9916,9 @@ function databaseFromEnv(svc) {
9367
9916
  };
9368
9917
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9369
9918
  }
9370
- async function parse9(serviceDir) {
9919
+ async function parse10(serviceDir) {
9371
9920
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9372
- const abs = import_node_path32.default.join(serviceDir, name);
9921
+ const abs = import_node_path33.default.join(serviceDir, name);
9373
9922
  if (!await exists(abs)) continue;
9374
9923
  const raw = await readYaml(abs);
9375
9924
  if (!raw?.services) return [];
@@ -9391,7 +9940,7 @@ async function parse9(serviceDir) {
9391
9940
  }
9392
9941
  return [];
9393
9942
  }
9394
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
9943
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9395
9944
 
9396
9945
  // src/extract/databases/index.ts
9397
9946
  var DB_PARSERS = [
@@ -9403,6 +9952,7 @@ var DB_PARSERS = [
9403
9952
  ormconfigParser,
9404
9953
  typeormParser,
9405
9954
  sequelizeParser,
9955
+ csharpParser,
9406
9956
  dockerComposeParser
9407
9957
  ];
9408
9958
  function compatibleDriversFor(engine) {
@@ -9541,7 +10091,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9541
10091
  discoveredVia: mergedDiscoveredVia
9542
10092
  });
9543
10093
  }
9544
- const relConfigFile = toPosix(import_node_path33.default.relative(service.dir, config.sourceFile));
10094
+ const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
9545
10095
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9546
10096
  graph,
9547
10097
  service.pkg.name,
@@ -9550,7 +10100,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9550
10100
  );
9551
10101
  nodesAdded += fn;
9552
10102
  edgesAdded += fe;
9553
- const evidenceFile = toPosix(import_node_path33.default.relative(scanPath, config.sourceFile));
10103
+ const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
9554
10104
  const edge = {
9555
10105
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
9556
10106
  source: fileNodeId,
@@ -9568,15 +10118,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9568
10118
  if (allConfigs.length === 1) {
9569
10119
  const primary = allConfigs[0];
9570
10120
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
9571
- const relPath = import_node_path33.default.relative(scanPath, primary.sourceFile);
10121
+ const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
9572
10122
  const cfgId = (0, import_types23.configId)(relPath);
9573
10123
  if (!graph.hasNode(cfgId)) {
9574
10124
  const cfgNode = {
9575
10125
  id: cfgId,
9576
10126
  type: import_types23.NodeType.ConfigNode,
9577
- name: import_node_path33.default.basename(primary.sourceFile),
10127
+ name: import_node_path34.default.basename(primary.sourceFile),
9578
10128
  path: relPath,
9579
- fileType: isConfigFile(import_node_path33.default.basename(primary.sourceFile)).fileType || "config"
10129
+ fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
9580
10130
  };
9581
10131
  graph.addNode(cfgId, cfgNode);
9582
10132
  nodesAdded++;
@@ -9617,27 +10167,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9617
10167
 
9618
10168
  // src/extract/configs.ts
9619
10169
  init_cjs_shims();
9620
- var import_node_fs22 = require("fs");
9621
- var import_node_path34 = __toESM(require("path"), 1);
10170
+ var import_node_fs23 = require("fs");
10171
+ var import_node_path35 = __toESM(require("path"), 1);
9622
10172
  var import_types24 = require("@neat.is/types");
9623
10173
  async function walkConfigFiles(dir, excludeDirs = []) {
9624
- const excluded = new Set(excludeDirs.map((d) => import_node_path34.default.resolve(d)));
10174
+ const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
9625
10175
  const out = [];
9626
- async function walk9(current) {
9627
- const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true });
10176
+ async function walk10(current) {
10177
+ const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
9628
10178
  for (const entry of entries) {
9629
- const full = import_node_path34.default.join(current, entry.name);
10179
+ const full = import_node_path35.default.join(current, entry.name);
9630
10180
  if (entry.isDirectory()) {
9631
10181
  if (IGNORED_DIRS.has(entry.name)) continue;
9632
- if (excluded.has(import_node_path34.default.resolve(full))) continue;
10182
+ if (excluded.has(import_node_path35.default.resolve(full))) continue;
9633
10183
  if (await isPythonVenvDir(full)) continue;
9634
- await walk9(full);
10184
+ await walk10(full);
9635
10185
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
9636
10186
  out.push(full);
9637
10187
  }
9638
10188
  }
9639
10189
  }
9640
- await walk9(dir);
10190
+ await walk10(dir);
9641
10191
  return out;
9642
10192
  }
9643
10193
  async function addConfigNodes(graph, services, scanPath) {
@@ -9646,19 +10196,19 @@ async function addConfigNodes(graph, services, scanPath) {
9646
10196
  for (const service of services) {
9647
10197
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
9648
10198
  for (const file of configFiles) {
9649
- const relPath = import_node_path34.default.relative(scanPath, file);
10199
+ const relPath = import_node_path35.default.relative(scanPath, file);
9650
10200
  const node = {
9651
10201
  id: (0, import_types24.configId)(relPath),
9652
10202
  type: import_types24.NodeType.ConfigNode,
9653
- name: import_node_path34.default.basename(file),
10203
+ name: import_node_path35.default.basename(file),
9654
10204
  path: relPath,
9655
- fileType: isConfigFile(import_node_path34.default.basename(file)).fileType
10205
+ fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
9656
10206
  };
9657
10207
  if (!graph.hasNode(node.id)) {
9658
10208
  graph.addNode(node.id, node);
9659
10209
  nodesAdded++;
9660
10210
  }
9661
- const relToService = toPosix(import_node_path34.default.relative(service.dir, file));
10211
+ const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
9662
10212
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9663
10213
  graph,
9664
10214
  service.pkg.name,
@@ -9674,7 +10224,7 @@ async function addConfigNodes(graph, services, scanPath) {
9674
10224
  type: import_types24.EdgeType.CONFIGURED_BY,
9675
10225
  provenance: import_types24.Provenance.EXTRACTED,
9676
10226
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
9677
- evidence: { file: relPath.split(import_node_path34.default.sep).join("/") }
10227
+ evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
9678
10228
  };
9679
10229
  if (!graph.hasEdge(edge.id)) {
9680
10230
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -9687,8 +10237,8 @@ async function addConfigNodes(graph, services, scanPath) {
9687
10237
 
9688
10238
  // src/extract/proto.ts
9689
10239
  init_cjs_shims();
9690
- var import_node_fs23 = require("fs");
9691
- var import_node_path35 = __toESM(require("path"), 1);
10240
+ var import_node_fs24 = require("fs");
10241
+ var import_node_path36 = __toESM(require("path"), 1);
9692
10242
  var import_types25 = require("@neat.is/types");
9693
10243
  var PROTO_EXTENSION = ".proto";
9694
10244
  function packageOf(content) {
@@ -9726,23 +10276,23 @@ function grpcMethodsFromProto(content, fqPackage) {
9726
10276
  return out;
9727
10277
  }
9728
10278
  async function walkProtoFiles(dir, excludeDirs = []) {
9729
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10279
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
9730
10280
  const out = [];
9731
- async function walk9(current) {
9732
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10281
+ async function walk10(current) {
10282
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9733
10283
  for (const entry of entries) {
9734
- const full = import_node_path35.default.join(current, entry.name);
10284
+ const full = import_node_path36.default.join(current, entry.name);
9735
10285
  if (entry.isDirectory()) {
9736
10286
  if (IGNORED_DIRS.has(entry.name)) continue;
9737
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10287
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
9738
10288
  if (await isPythonVenvDir(full)) continue;
9739
- await walk9(full);
9740
- } else if (entry.isFile() && import_node_path35.default.extname(entry.name) === PROTO_EXTENSION) {
10289
+ await walk10(full);
10290
+ } else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
9741
10291
  out.push(full);
9742
10292
  }
9743
10293
  }
9744
10294
  }
9745
- await walk9(dir);
10295
+ await walk10(dir);
9746
10296
  return out;
9747
10297
  }
9748
10298
  async function addGrpcMethods(graph, services) {
@@ -9752,10 +10302,10 @@ async function addGrpcMethods(graph, services) {
9752
10302
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
9753
10303
  for (const protoPath of protoPaths) {
9754
10304
  if (isTestPath(protoPath)) continue;
9755
- const relFile = toPosix(import_node_path35.default.relative(service.dir, protoPath));
10305
+ const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
9756
10306
  let content;
9757
10307
  try {
9758
- content = await import_node_fs23.promises.readFile(protoPath, "utf8");
10308
+ content = await import_node_fs24.promises.readFile(protoPath, "utf8");
9759
10309
  } catch (err) {
9760
10310
  recordExtractionError("proto extraction", protoPath, err);
9761
10311
  continue;
@@ -9810,11 +10360,11 @@ async function addGrpcMethods(graph, services) {
9810
10360
 
9811
10361
  // src/extract/calls/index.ts
9812
10362
  init_cjs_shims();
9813
- var import_types43 = require("@neat.is/types");
10363
+ var import_types44 = require("@neat.is/types");
9814
10364
 
9815
10365
  // src/extract/calls/http.ts
9816
10366
  init_cjs_shims();
9817
- var import_node_path36 = __toESM(require("path"), 1);
10367
+ var import_node_path37 = __toESM(require("path"), 1);
9818
10368
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
9819
10369
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
9820
10370
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -9897,7 +10447,7 @@ async function addHttpCallEdges(graph, services) {
9897
10447
  const seen = /* @__PURE__ */ new Set();
9898
10448
  for (const file of files) {
9899
10449
  if (isTestPath(file.path)) continue;
9900
- const parser = parserForExt(import_node_path36.default.extname(file.path), parserCache);
10450
+ const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
9901
10451
  let sites;
9902
10452
  try {
9903
10453
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9906,7 +10456,7 @@ async function addHttpCallEdges(graph, services) {
9906
10456
  continue;
9907
10457
  }
9908
10458
  if (sites.length === 0) continue;
9909
- const relFile = toPosix(import_node_path36.default.relative(service.dir, file.path));
10459
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
9910
10460
  for (const site of sites) {
9911
10461
  const targetId = hostToNodeId.get(site.host);
9912
10462
  if (!targetId || targetId === service.node.id) continue;
@@ -9960,7 +10510,7 @@ async function addHttpCallEdges(graph, services) {
9960
10510
 
9961
10511
  // src/extract/calls/route-match.ts
9962
10512
  init_cjs_shims();
9963
- var import_node_path37 = __toESM(require("path"), 1);
10513
+ var import_node_path38 = __toESM(require("path"), 1);
9964
10514
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
9965
10515
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
9966
10516
  var import_types27 = require("@neat.is/types");
@@ -10159,7 +10709,7 @@ async function addRouteCallEdges(graph, services) {
10159
10709
  const seen = /* @__PURE__ */ new Set();
10160
10710
  for (const file of files) {
10161
10711
  if (isTestPath(file.path)) continue;
10162
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path37.default.extname(file.path))) continue;
10712
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
10163
10713
  let sites;
10164
10714
  try {
10165
10715
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10168,7 +10718,7 @@ async function addRouteCallEdges(graph, services) {
10168
10718
  continue;
10169
10719
  }
10170
10720
  if (sites.length === 0) continue;
10171
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
10721
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10172
10722
  for (const site of sites) {
10173
10723
  const serverServiceId = hostToNodeId.get(site.host);
10174
10724
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10229,7 +10779,7 @@ async function addRouteCallEdges(graph, services) {
10229
10779
 
10230
10780
  // src/extract/calls/kafka.ts
10231
10781
  init_cjs_shims();
10232
- var import_node_path38 = __toESM(require("path"), 1);
10782
+ var import_node_path39 = __toESM(require("path"), 1);
10233
10783
  var import_types28 = require("@neat.is/types");
10234
10784
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10235
10785
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
@@ -10317,13 +10867,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10317
10867
  // call sites — verified-call-site tier (ADR-066).
10318
10868
  confidenceKind: "verified-call-site",
10319
10869
  evidence: {
10320
- file: import_node_path38.default.relative(serviceDir, file.path),
10870
+ file: import_node_path39.default.relative(serviceDir, file.path),
10321
10871
  line,
10322
10872
  snippet: snippet(file.content, line)
10323
10873
  }
10324
10874
  });
10325
10875
  };
10326
- if (import_node_path38.default.extname(file.path) === ".go") {
10876
+ if (import_node_path39.default.extname(file.path) === ".go") {
10327
10877
  goSaramaEndpoints(file.content, make);
10328
10878
  } else {
10329
10879
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -10334,7 +10884,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10334
10884
 
10335
10885
  // src/extract/calls/redis.ts
10336
10886
  init_cjs_shims();
10337
- var import_node_path39 = __toESM(require("path"), 1);
10887
+ var import_node_path40 = __toESM(require("path"), 1);
10338
10888
  var import_types29 = require("@neat.is/types");
10339
10889
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10340
10890
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10357,7 +10907,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10357
10907
  // support tier (ADR-066).
10358
10908
  confidenceKind: "url-with-structural-support",
10359
10909
  evidence: {
10360
- file: import_node_path39.default.relative(serviceDir, file.path),
10910
+ file: import_node_path40.default.relative(serviceDir, file.path),
10361
10911
  line,
10362
10912
  snippet: snippet(file.content, line)
10363
10913
  }
@@ -10368,7 +10918,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10368
10918
 
10369
10919
  // src/extract/calls/aws.ts
10370
10920
  init_cjs_shims();
10371
- var import_node_path40 = __toESM(require("path"), 1);
10921
+ var import_node_path41 = __toESM(require("path"), 1);
10372
10922
  var import_types30 = require("@neat.is/types");
10373
10923
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10374
10924
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10402,7 +10952,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10402
10952
  // (ADR-066).
10403
10953
  confidenceKind: "verified-call-site",
10404
10954
  evidence: {
10405
- file: import_node_path40.default.relative(serviceDir, file.path),
10955
+ file: import_node_path41.default.relative(serviceDir, file.path),
10406
10956
  line,
10407
10957
  snippet: snippet(file.content, line)
10408
10958
  }
@@ -10427,7 +10977,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10427
10977
 
10428
10978
  // src/extract/calls/grpc.ts
10429
10979
  init_cjs_shims();
10430
- var import_node_path41 = __toESM(require("path"), 1);
10980
+ var import_node_path42 = __toESM(require("path"), 1);
10431
10981
  var import_types31 = require("@neat.is/types");
10432
10982
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10433
10983
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -10486,7 +11036,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10486
11036
  // tier (ADR-066).
10487
11037
  confidenceKind: "verified-call-site",
10488
11038
  evidence: {
10489
- file: import_node_path41.default.relative(serviceDir, file.path),
11039
+ file: import_node_path42.default.relative(serviceDir, file.path),
10490
11040
  line,
10491
11041
  snippet: snippet(file.content, line)
10492
11042
  }
@@ -10497,7 +11047,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10497
11047
 
10498
11048
  // src/extract/calls/supabase.ts
10499
11049
  init_cjs_shims();
10500
- var import_node_path42 = __toESM(require("path"), 1);
11050
+ var import_node_path43 = __toESM(require("path"), 1);
10501
11051
  var import_types32 = require("@neat.is/types");
10502
11052
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
10503
11053
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -10556,7 +11106,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10556
11106
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10557
11107
  confidenceKind: "verified-call-site",
10558
11108
  evidence: {
10559
- file: import_node_path42.default.relative(serviceDir, file.path),
11109
+ file: import_node_path43.default.relative(serviceDir, file.path),
10560
11110
  line,
10561
11111
  snippet: snippet(file.content, line)
10562
11112
  }
@@ -10583,7 +11133,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10583
11133
  edgeType: "CALLS",
10584
11134
  confidenceKind: "verified-call-site",
10585
11135
  evidence: {
10586
- file: import_node_path42.default.relative(serviceDir, file.path),
11136
+ file: import_node_path43.default.relative(serviceDir, file.path),
10587
11137
  line,
10588
11138
  snippet: snippet(file.content, line)
10589
11139
  }
@@ -10595,7 +11145,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10595
11145
 
10596
11146
  // src/extract/calls/firestore.ts
10597
11147
  init_cjs_shims();
10598
- var import_node_path43 = __toESM(require("path"), 1);
11148
+ var import_node_path44 = __toESM(require("path"), 1);
10599
11149
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10600
11150
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10601
11151
  var import_types33 = require("@neat.is/types");
@@ -10634,7 +11184,7 @@ function isFirestoreClientFactory(node) {
10634
11184
  }
10635
11185
  function firestoreClientVars(root) {
10636
11186
  const vars = /* @__PURE__ */ new Set();
10637
- const walk9 = (node) => {
11187
+ const walk10 = (node) => {
10638
11188
  if (node.type === "variable_declarator") {
10639
11189
  const name = node.childForFieldName("name");
10640
11190
  let value = node.childForFieldName("value");
@@ -10643,9 +11193,9 @@ function firestoreClientVars(root) {
10643
11193
  vars.add(name.text);
10644
11194
  }
10645
11195
  }
10646
- for (const c of namedChildren(node)) walk9(c);
11196
+ for (const c of namedChildren(node)) walk10(c);
10647
11197
  };
10648
- walk9(root);
11198
+ walk10(root);
10649
11199
  return vars;
10650
11200
  }
10651
11201
  function isClientExpr(node, clientVars) {
@@ -10766,7 +11316,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10766
11316
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10767
11317
  if (!hasClient && !hasAdmin) return [];
10768
11318
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10769
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
11319
+ const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
10770
11320
  const clientVars = firestoreClientVars(tree.rootNode);
10771
11321
  const collLine = /* @__PURE__ */ new Map();
10772
11322
  const writes = /* @__PURE__ */ new Map();
@@ -10800,7 +11350,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10800
11350
  }
10801
11351
  s.add(field);
10802
11352
  };
10803
- const walk9 = (node) => {
11353
+ const walk10 = (node) => {
10804
11354
  if (node.type === "call_expression") {
10805
11355
  const fn = node.childForFieldName("function");
10806
11356
  const line = node.startPosition.row + 1;
@@ -10840,9 +11390,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10840
11390
  }
10841
11391
  }
10842
11392
  }
10843
- for (const c of namedChildren(node)) walk9(c);
11393
+ for (const c of namedChildren(node)) walk10(c);
10844
11394
  };
10845
- walk9(tree.rootNode);
11395
+ walk10(tree.rootNode);
10846
11396
  const out = [];
10847
11397
  for (const [collPath, line] of collLine) {
10848
11398
  const byField = writes.get(collPath);
@@ -10870,7 +11420,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10870
11420
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10871
11421
  ...sdkWrites ? { sdkWrites } : {},
10872
11422
  evidence: {
10873
- file: import_node_path43.default.relative(serviceDir, file.path),
11423
+ file: import_node_path44.default.relative(serviceDir, file.path),
10874
11424
  line,
10875
11425
  snippet: snippet(file.content, line)
10876
11426
  }
@@ -10881,7 +11431,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10881
11431
 
10882
11432
  // src/extract/calls/mongoose.ts
10883
11433
  init_cjs_shims();
10884
- var import_node_path44 = __toESM(require("path"), 1);
11434
+ var import_node_path45 = __toESM(require("path"), 1);
10885
11435
  var import_types34 = require("@neat.is/types");
10886
11436
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10887
11437
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -11032,7 +11582,7 @@ function endpoint(r, file, serviceDir, matchText) {
11032
11582
  kind: r.kind,
11033
11583
  edgeType: "CALLS",
11034
11584
  confidenceKind: "verified-call-site",
11035
- evidence: { file: import_node_path44.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11585
+ evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11036
11586
  };
11037
11587
  }
11038
11588
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11122,7 +11672,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11122
11672
  const registry = /* @__PURE__ */ new Map();
11123
11673
  for (const f of mongooseFiles) {
11124
11674
  const fx = fileExportsOf(f.content, pluralizeOn);
11125
- if (fx) registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), fx);
11675
+ if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
11126
11676
  }
11127
11677
  if (registry.size === 0) return [];
11128
11678
  const out = [];
@@ -11133,7 +11683,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11133
11683
  const directColl = /* @__PURE__ */ new Map();
11134
11684
  const nsExports = /* @__PURE__ */ new Map();
11135
11685
  for (const b of bindings) {
11136
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
11686
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
11137
11687
  if (!resolvedRel) continue;
11138
11688
  const fx = registry.get(resolvedRel);
11139
11689
  if (!fx) continue;
@@ -11177,7 +11727,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11177
11727
 
11178
11728
  // src/extract/calls/sqlalchemy.ts
11179
11729
  init_cjs_shims();
11180
- var import_node_path45 = __toESM(require("path"), 1);
11730
+ var import_node_path46 = __toESM(require("path"), 1);
11181
11731
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11182
11732
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11183
11733
  var import_types35 = require("@neat.is/types");
@@ -11316,7 +11866,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11316
11866
  childTable,
11317
11867
  parentTable,
11318
11868
  evidence: {
11319
- file: import_node_path45.default.relative(serviceDir, file.path),
11869
+ file: import_node_path46.default.relative(serviceDir, file.path),
11320
11870
  line,
11321
11871
  snippet: snippet(file.content, line)
11322
11872
  }
@@ -11341,7 +11891,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11341
11891
  confidenceKind: "verified-call-site",
11342
11892
  ...columns && columns.length > 0 ? { columns } : {},
11343
11893
  evidence: {
11344
- file: import_node_path45.default.relative(serviceDir, file.path),
11894
+ file: import_node_path46.default.relative(serviceDir, file.path),
11345
11895
  line,
11346
11896
  snippet: snippet(file.content, line)
11347
11897
  }
@@ -11448,7 +11998,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11448
11998
  edgeType: "CALLS",
11449
11999
  confidenceKind: "verified-call-site",
11450
12000
  evidence: {
11451
- file: import_node_path45.default.relative(serviceDir, file.path),
12001
+ file: import_node_path46.default.relative(serviceDir, file.path),
11452
12002
  line,
11453
12003
  snippet: snippet(file.content, line)
11454
12004
  }
@@ -11460,7 +12010,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11460
12010
 
11461
12011
  // src/extract/calls/django-orm.ts
11462
12012
  init_cjs_shims();
11463
- var import_node_path46 = __toESM(require("path"), 1);
12013
+ var import_node_path47 = __toESM(require("path"), 1);
11464
12014
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11465
12015
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11466
12016
  var import_types36 = require("@neat.is/types");
@@ -11530,7 +12080,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11530
12080
  const tree = parseSource7(makePyParser4(), file.content);
11531
12081
  const out = [];
11532
12082
  const seen = /* @__PURE__ */ new Set();
11533
- const defaultAppLabel = import_node_path46.default.basename(import_node_path46.default.dirname(file.path));
12083
+ const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
11534
12084
  walk4(tree.rootNode, (node) => {
11535
12085
  if (node.type !== "class_definition") return;
11536
12086
  if (!extendsDjangoModel(node)) return;
@@ -11548,7 +12098,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11548
12098
  kind: "sql-table",
11549
12099
  edgeType: "CALLS",
11550
12100
  confidenceKind: "verified-call-site",
11551
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12101
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11552
12102
  });
11553
12103
  });
11554
12104
  return out;
@@ -11556,7 +12106,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11556
12106
 
11557
12107
  // src/extract/calls/drizzle.ts
11558
12108
  init_cjs_shims();
11559
- var import_node_path47 = __toESM(require("path"), 1);
12109
+ var import_node_path48 = __toESM(require("path"), 1);
11560
12110
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11561
12111
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11562
12112
  var import_types37 = require("@neat.is/types");
@@ -11634,10 +12184,10 @@ function columnsFromObject(obj) {
11634
12184
  }
11635
12185
  function drizzleEndpointsFromFile(file, serviceDir) {
11636
12186
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11637
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12187
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11638
12188
  const out = [];
11639
12189
  const seen = /* @__PURE__ */ new Set();
11640
- const walk9 = (node) => {
12190
+ const walk10 = (node) => {
11641
12191
  if (node.type === "call_expression") {
11642
12192
  const fn = node.childForFieldName("function");
11643
12193
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11657,7 +12207,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11657
12207
  confidenceKind: "structural",
11658
12208
  columns,
11659
12209
  evidence: {
11660
- file: import_node_path47.default.relative(serviceDir, file.path),
12210
+ file: import_node_path48.default.relative(serviceDir, file.path),
11661
12211
  line,
11662
12212
  snippet: snippet(file.content, line)
11663
12213
  }
@@ -11665,9 +12215,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11665
12215
  }
11666
12216
  }
11667
12217
  }
11668
- for (const c of namedChildren4(node)) walk9(c);
12218
+ for (const c of namedChildren4(node)) walk10(c);
11669
12219
  };
11670
- walk9(tree.rootNode);
12220
+ walk10(tree.rootNode);
11671
12221
  return out;
11672
12222
  }
11673
12223
  function enclosingVarName(call) {
@@ -11689,7 +12239,7 @@ function enclosingVarName(call) {
11689
12239
  function collectDrizzleTables(root) {
11690
12240
  const tables = [];
11691
12241
  const varToTable = /* @__PURE__ */ new Map();
11692
- const walk9 = (node) => {
12242
+ const walk10 = (node) => {
11693
12243
  if (node.type === "call_expression") {
11694
12244
  const fn = node.childForFieldName("function");
11695
12245
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11704,9 +12254,9 @@ function collectDrizzleTables(root) {
11704
12254
  }
11705
12255
  }
11706
12256
  }
11707
- for (const c of namedChildren4(node)) walk9(c);
12257
+ for (const c of namedChildren4(node)) walk10(c);
11708
12258
  };
11709
- walk9(root);
12259
+ walk10(root);
11710
12260
  return { tables, varToTable };
11711
12261
  }
11712
12262
  function referencesTargetVar(call) {
@@ -11723,13 +12273,13 @@ function referencesTargetVar(call) {
11723
12273
  }
11724
12274
  function drizzleForeignKeys(file, serviceDir) {
11725
12275
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11726
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12276
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11727
12277
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11728
12278
  const out = [];
11729
12279
  const seen = /* @__PURE__ */ new Set();
11730
12280
  for (const table of tables) {
11731
12281
  if (!table.object) continue;
11732
- const walk9 = (node) => {
12282
+ const walk10 = (node) => {
11733
12283
  if (node.type === "call_expression") {
11734
12284
  const targetVar = referencesTargetVar(node);
11735
12285
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -11742,7 +12292,7 @@ function drizzleForeignKeys(file, serviceDir) {
11742
12292
  childTable: table.tableName,
11743
12293
  parentTable,
11744
12294
  evidence: {
11745
- file: import_node_path47.default.relative(serviceDir, file.path),
12295
+ file: import_node_path48.default.relative(serviceDir, file.path),
11746
12296
  line,
11747
12297
  snippet: snippet(file.content, line)
11748
12298
  }
@@ -11750,16 +12300,16 @@ function drizzleForeignKeys(file, serviceDir) {
11750
12300
  }
11751
12301
  }
11752
12302
  }
11753
- for (const c of namedChildren4(node)) walk9(c);
12303
+ for (const c of namedChildren4(node)) walk10(c);
11754
12304
  };
11755
- walk9(table.object);
12305
+ walk10(table.object);
11756
12306
  }
11757
12307
  return out;
11758
12308
  }
11759
12309
 
11760
12310
  // src/extract/calls/prisma.ts
11761
12311
  init_cjs_shims();
11762
- var import_node_path48 = __toESM(require("path"), 1);
12312
+ var import_node_path49 = __toESM(require("path"), 1);
11763
12313
  var import_types38 = require("@neat.is/types");
11764
12314
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11765
12315
  "Int",
@@ -11815,7 +12365,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11815
12365
  confidenceKind: "structural",
11816
12366
  columns: b.columns,
11817
12367
  evidence: {
11818
- file: import_node_path48.default.relative(serviceDir, file.path),
12368
+ file: import_node_path49.default.relative(serviceDir, file.path),
11819
12369
  line: b.startLine,
11820
12370
  snippet: snippet(content, b.startLine)
11821
12371
  }
@@ -11876,7 +12426,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11876
12426
  }
11877
12427
  async function prismaColumnEndpoints(serviceDir) {
11878
12428
  const schemaPath = await findFirst(serviceDir, [
11879
- import_node_path48.default.join("prisma", "schema.prisma"),
12429
+ import_node_path49.default.join("prisma", "schema.prisma"),
11880
12430
  "schema.prisma"
11881
12431
  ]);
11882
12432
  if (!schemaPath) return [];
@@ -11951,7 +12501,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11951
12501
  childTable: current.table,
11952
12502
  parentTable,
11953
12503
  evidence: {
11954
- file: import_node_path48.default.relative(serviceDir, file.path),
12504
+ file: import_node_path49.default.relative(serviceDir, file.path),
11955
12505
  line: lineNo,
11956
12506
  snippet: snippet(content, lineNo)
11957
12507
  }
@@ -11966,7 +12516,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11966
12516
  }
11967
12517
  async function prismaForeignKeys(serviceDir) {
11968
12518
  const schemaPath = await findFirst(serviceDir, [
11969
- import_node_path48.default.join("prisma", "schema.prisma"),
12519
+ import_node_path49.default.join("prisma", "schema.prisma"),
11970
12520
  "schema.prisma"
11971
12521
  ]);
11972
12522
  if (!schemaPath) return [];
@@ -11977,7 +12527,7 @@ async function prismaForeignKeys(serviceDir) {
11977
12527
 
11978
12528
  // src/extract/calls/activerecord.ts
11979
12529
  init_cjs_shims();
11980
- var import_node_path49 = __toESM(require("path"), 1);
12530
+ var import_node_path50 = __toESM(require("path"), 1);
11981
12531
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11982
12532
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
11983
12533
  var import_types39 = require("@neat.is/types");
@@ -12185,7 +12735,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12185
12735
  confidenceKind: "structural",
12186
12736
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12187
12737
  evidence: {
12188
- file: import_node_path49.default.relative(serviceDir, file.path),
12738
+ file: import_node_path50.default.relative(serviceDir, file.path),
12189
12739
  line: table.line,
12190
12740
  snippet: snippet(file.content, table.line)
12191
12741
  }
@@ -12207,7 +12757,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12207
12757
  childTable,
12208
12758
  parentTable,
12209
12759
  evidence: {
12210
- file: import_node_path49.default.relative(serviceDir, file.path),
12760
+ file: import_node_path50.default.relative(serviceDir, file.path),
12211
12761
  line,
12212
12762
  snippet: snippet(file.content, line)
12213
12763
  }
@@ -12290,7 +12840,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12290
12840
  edgeType: "CALLS",
12291
12841
  confidenceKind: "verified-call-site",
12292
12842
  evidence: {
12293
- file: import_node_path49.default.relative(serviceDir, file.path),
12843
+ file: import_node_path50.default.relative(serviceDir, file.path),
12294
12844
  line,
12295
12845
  snippet: snippet(file.content, line)
12296
12846
  }
@@ -12327,7 +12877,7 @@ function railsModelForeignKeys(file, serviceDir) {
12327
12877
  childTable,
12328
12878
  parentTable,
12329
12879
  evidence: {
12330
- file: import_node_path49.default.relative(serviceDir, file.path),
12880
+ file: import_node_path50.default.relative(serviceDir, file.path),
12331
12881
  line,
12332
12882
  snippet: snippet(file.content, line)
12333
12883
  }
@@ -12339,7 +12889,7 @@ function railsModelForeignKeys(file, serviceDir) {
12339
12889
 
12340
12890
  // src/extract/calls/eloquent.ts
12341
12891
  init_cjs_shims();
12342
- var import_node_path50 = __toESM(require("path"), 1);
12892
+ var import_node_path51 = __toESM(require("path"), 1);
12343
12893
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12344
12894
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12345
12895
  var import_types40 = require("@neat.is/types");
@@ -12596,7 +13146,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12596
13146
  confidenceKind: "structural",
12597
13147
  ...columns.length > 0 ? { columns } : {},
12598
13148
  evidence: {
12599
- file: import_node_path50.default.relative(serviceDir, file.path),
13149
+ file: import_node_path51.default.relative(serviceDir, file.path),
12600
13150
  line: bp.line,
12601
13151
  snippet: snippet(file.content, bp.line)
12602
13152
  }
@@ -12618,7 +13168,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12618
13168
  childTable,
12619
13169
  parentTable,
12620
13170
  evidence: {
12621
- file: import_node_path50.default.relative(serviceDir, file.path),
13171
+ file: import_node_path51.default.relative(serviceDir, file.path),
12622
13172
  line,
12623
13173
  snippet: snippet(file.content, line)
12624
13174
  }
@@ -12738,7 +13288,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12738
13288
  edgeType: "CALLS",
12739
13289
  confidenceKind: "verified-call-site",
12740
13290
  evidence: {
12741
- file: import_node_path50.default.relative(serviceDir, file.path),
13291
+ file: import_node_path51.default.relative(serviceDir, file.path),
12742
13292
  line,
12743
13293
  snippet: snippet(file.content, line)
12744
13294
  }
@@ -12774,7 +13324,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12774
13324
  childTable,
12775
13325
  parentTable,
12776
13326
  evidence: {
12777
- file: import_node_path50.default.relative(serviceDir, file.path),
13327
+ file: import_node_path51.default.relative(serviceDir, file.path),
12778
13328
  line,
12779
13329
  snippet: snippet(file.content, line)
12780
13330
  }
@@ -12786,7 +13336,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12786
13336
 
12787
13337
  // src/extract/calls/go.ts
12788
13338
  init_cjs_shims();
12789
- var import_node_path53 = __toESM(require("path"), 1);
13339
+ var import_node_path54 = __toESM(require("path"), 1);
12790
13340
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12791
13341
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12792
13342
  var import_types41 = require("@neat.is/types");
@@ -12861,7 +13411,7 @@ function firstStringLiteralArg(argsNode) {
12861
13411
  return null;
12862
13412
  }
12863
13413
  function goSqlEndpointsFromFile(file, serviceDir) {
12864
- if (import_node_path53.default.extname(file.path) !== ".go") return [];
13414
+ if (import_node_path54.default.extname(file.path) !== ".go") return [];
12865
13415
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12866
13416
  const tree = parseSource10(makeGoParser3(), file.content);
12867
13417
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12890,7 +13440,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12890
13440
  confidenceKind: "verified-call-site",
12891
13441
  ...columns.length > 0 ? { columns } : {},
12892
13442
  evidence: {
12893
- file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
13443
+ file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
12894
13444
  line,
12895
13445
  snippet: snippet(file.content, line)
12896
13446
  }
@@ -12901,7 +13451,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12901
13451
 
12902
13452
  // src/extract/calls/gorm.ts
12903
13453
  init_cjs_shims();
12904
- var import_node_path54 = __toESM(require("path"), 1);
13454
+ var import_node_path55 = __toESM(require("path"), 1);
12905
13455
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12906
13456
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12907
13457
  var import_types42 = require("@neat.is/types");
@@ -13334,7 +13884,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13334
13884
  seen.delete(struct.name);
13335
13885
  }
13336
13886
  function gormEndpointsFromFile(file, serviceDir) {
13337
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13887
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13338
13888
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13339
13889
  const tree = parseSource11(makeGoParser4(), file.content);
13340
13890
  const { structs, models, tableFor } = analyze(tree);
@@ -13356,7 +13906,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13356
13906
  confidenceKind: "structural",
13357
13907
  ...columns.length > 0 ? { columns } : {},
13358
13908
  evidence: {
13359
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13909
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13360
13910
  line: struct.line,
13361
13911
  snippet: snippet(file.content, struct.line)
13362
13912
  }
@@ -13365,7 +13915,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13365
13915
  return out;
13366
13916
  }
13367
13917
  function gormForeignKeys(file, serviceDir) {
13368
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13918
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13369
13919
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13370
13920
  const tree = parseSource11(makeGoParser4(), file.content);
13371
13921
  const { structs, models, tableFor } = analyze(tree);
@@ -13380,7 +13930,7 @@ function gormForeignKeys(file, serviceDir) {
13380
13930
  childTable,
13381
13931
  parentTable,
13382
13932
  evidence: {
13383
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13933
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13384
13934
  line,
13385
13935
  snippet: snippet(file.content, line)
13386
13936
  }
@@ -13415,15 +13965,131 @@ function gormForeignKeys(file, serviceDir) {
13415
13965
  return out;
13416
13966
  }
13417
13967
 
13968
+ // src/extract/calls/efcore.ts
13969
+ init_cjs_shims();
13970
+ var import_node_path56 = __toESM(require("path"), 1);
13971
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13972
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
13973
+ var import_types43 = require("@neat.is/types");
13974
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
13975
+ var PARSE_CHUNK12 = 16384;
13976
+ function makeCsParser() {
13977
+ const p = new import_tree_sitter16.default();
13978
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
13979
+ return p;
13980
+ }
13981
+ function parseSource12(parser, source) {
13982
+ return parser.parse(
13983
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
13984
+ );
13985
+ }
13986
+ function walk9(node, visit) {
13987
+ visit(node);
13988
+ for (let i = 0; i < node.namedChildCount; i++) {
13989
+ const c = node.namedChild(i);
13990
+ if (c) walk9(c, visit);
13991
+ }
13992
+ }
13993
+ function firstChildOfType(node, type) {
13994
+ for (let i = 0; i < node.namedChildCount; i++) {
13995
+ const c = node.namedChild(i);
13996
+ if (c?.type === type) return c;
13997
+ }
13998
+ return null;
13999
+ }
14000
+ function csStringLiteral(node) {
14001
+ if (node.type === "string_literal") {
14002
+ let out = "";
14003
+ for (let i = 0; i < node.namedChildCount; i++) {
14004
+ const c = node.namedChild(i);
14005
+ if (c?.type === "string_literal_content") out += c.text;
14006
+ }
14007
+ return out;
14008
+ }
14009
+ if (node.type === "verbatim_string_literal") {
14010
+ const t = node.text;
14011
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
14012
+ }
14013
+ return null;
14014
+ }
14015
+ function attributeName(attr) {
14016
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
14017
+ if (!nameNode) return null;
14018
+ const text = nameNode.text;
14019
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
14020
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
14021
+ }
14022
+ function tableFromAttribute(attr) {
14023
+ if (attributeName(attr) !== "Table") return null;
14024
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
14025
+ if (!args) return null;
14026
+ for (let i = 0; i < args.namedChildCount; i++) {
14027
+ const arg = args.namedChild(i);
14028
+ if (arg?.type !== "attribute_argument") continue;
14029
+ const first = arg.namedChild(0);
14030
+ if (!first) continue;
14031
+ const value = csStringLiteral(first);
14032
+ if (value !== null) return value;
14033
+ return null;
14034
+ }
14035
+ return null;
14036
+ }
14037
+ function tableFromToTable(call) {
14038
+ const fn = call.childForFieldName("function");
14039
+ if (fn?.type !== "member_access_expression") return null;
14040
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14041
+ if (method?.text !== "ToTable") return null;
14042
+ const args = call.childForFieldName("arguments");
14043
+ const firstArg2 = args?.namedChild(0);
14044
+ if (firstArg2?.type !== "argument") return null;
14045
+ const value = firstArg2.namedChild(0);
14046
+ return value ? csStringLiteral(value) : null;
14047
+ }
14048
+ function efcoreEndpointsFromFile(file, serviceDir) {
14049
+ if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14050
+ if (!EFCORE_GATE.test(file.content)) return [];
14051
+ const tree = parseSource12(makeCsParser(), file.content);
14052
+ const out = [];
14053
+ const seen = /* @__PURE__ */ new Set();
14054
+ const push = (name, line) => {
14055
+ if (!name || seen.has(name)) return;
14056
+ seen.add(name);
14057
+ out.push({
14058
+ infraId: (0, import_types43.infraId)("sql-table", name),
14059
+ name,
14060
+ kind: "sql-table",
14061
+ edgeType: "CALLS",
14062
+ confidenceKind: "structural",
14063
+ evidence: {
14064
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14065
+ line,
14066
+ snippet: snippet(file.content, line)
14067
+ }
14068
+ });
14069
+ };
14070
+ walk9(tree.rootNode, (node) => {
14071
+ if (node.type === "attribute") {
14072
+ const table = tableFromAttribute(node);
14073
+ if (table) push(table, node.startPosition.row + 1);
14074
+ return;
14075
+ }
14076
+ if (node.type === "invocation_expression") {
14077
+ const table = tableFromToTable(node);
14078
+ if (table) push(table, node.startPosition.row + 1);
14079
+ }
14080
+ });
14081
+ return out;
14082
+ }
14083
+
13418
14084
  // src/extract/calls/index.ts
13419
14085
  function edgeTypeFromEndpoint(ep) {
13420
14086
  switch (ep.edgeType) {
13421
14087
  case "PUBLISHES_TO":
13422
- return import_types43.EdgeType.PUBLISHES_TO;
14088
+ return import_types44.EdgeType.PUBLISHES_TO;
13423
14089
  case "CONSUMES_FROM":
13424
- return import_types43.EdgeType.CONSUMES_FROM;
14090
+ return import_types44.EdgeType.CONSUMES_FROM;
13425
14091
  default:
13426
- return import_types43.EdgeType.CALLS;
14092
+ return import_types44.EdgeType.CALLS;
13427
14093
  }
13428
14094
  }
13429
14095
  function isAwsKind(kind) {
@@ -13473,6 +14139,11 @@ async function addExternalEndpointEdges(graph, services) {
13473
14139
  } catch (err) {
13474
14140
  recordExtractionError("laravel eloquent extraction", file.path, err);
13475
14141
  }
14142
+ try {
14143
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14144
+ } catch (err) {
14145
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14146
+ }
13476
14147
  }
13477
14148
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13478
14149
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -13483,7 +14154,7 @@ async function addExternalEndpointEdges(graph, services) {
13483
14154
  if (!graph.hasNode(ep.infraId)) {
13484
14155
  const node = {
13485
14156
  id: ep.infraId,
13486
- type: import_types43.NodeType.InfraNode,
14157
+ type: import_types44.NodeType.InfraNode,
13487
14158
  name: ep.name,
13488
14159
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
13489
14160
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -13496,21 +14167,21 @@ async function addExternalEndpointEdges(graph, services) {
13496
14167
  }
13497
14168
  if (ep.columns && ep.columns.length > 0) {
13498
14169
  const node = graph.getNodeAttributes(ep.infraId);
13499
- if (node.type === import_types43.NodeType.InfraNode) {
14170
+ if (node.type === import_types44.NodeType.InfraNode) {
13500
14171
  graph.replaceNodeAttributes(ep.infraId, {
13501
14172
  ...node,
13502
14173
  columns: foldColumns(
13503
14174
  node.columns,
13504
14175
  ep.columns,
13505
- import_types43.Provenance.EXTRACTED,
13506
- (0, import_types43.confidenceForExtracted)(ep.confidenceKind)
14176
+ import_types44.Provenance.EXTRACTED,
14177
+ (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
13507
14178
  )
13508
14179
  });
13509
14180
  }
13510
14181
  }
13511
14182
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
13512
14183
  const node = graph.getNodeAttributes(ep.infraId);
13513
- if (node.type === import_types43.NodeType.InfraNode) {
14184
+ if (node.type === import_types44.NodeType.InfraNode) {
13514
14185
  graph.replaceNodeAttributes(ep.infraId, {
13515
14186
  ...node,
13516
14187
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -13518,7 +14189,7 @@ async function addExternalEndpointEdges(graph, services) {
13518
14189
  }
13519
14190
  }
13520
14191
  const edgeType = edgeTypeFromEndpoint(ep);
13521
- const confidence = (0, import_types43.confidenceForExtracted)(ep.confidenceKind);
14192
+ const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
13522
14193
  const relFile = toPosix(ep.evidence.file);
13523
14194
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
13524
14195
  graph,
@@ -13528,7 +14199,7 @@ async function addExternalEndpointEdges(graph, services) {
13528
14199
  );
13529
14200
  nodesAdded += n;
13530
14201
  edgesAdded += e;
13531
- if (!(0, import_types43.passesExtractedFloor)(confidence)) {
14202
+ if (!(0, import_types44.passesExtractedFloor)(confidence)) {
13532
14203
  noteExtractedDropped({
13533
14204
  source: fileNodeId,
13534
14205
  target: ep.infraId,
@@ -13548,7 +14219,7 @@ async function addExternalEndpointEdges(graph, services) {
13548
14219
  source: fileNodeId,
13549
14220
  target: ep.infraId,
13550
14221
  type: edgeType,
13551
- provenance: import_types43.Provenance.EXTRACTED,
14222
+ provenance: import_types44.Provenance.EXTRACTED,
13552
14223
  confidence,
13553
14224
  evidence: ep.evidence
13554
14225
  };
@@ -13571,7 +14242,7 @@ async function addCallEdges(graph, services) {
13571
14242
 
13572
14243
  // src/extract/table-edges.ts
13573
14244
  init_cjs_shims();
13574
- var import_types44 = require("@neat.is/types");
14245
+ var import_types45 = require("@neat.is/types");
13575
14246
  async function addTableEdges(graph, services) {
13576
14247
  let nodesAdded = 0;
13577
14248
  let edgesAdded = 0;
@@ -13599,20 +14270,20 @@ async function addTableEdges(graph, services) {
13599
14270
  }
13600
14271
  refs.push(...modelRefs);
13601
14272
  for (const ref of refs) {
13602
- const childId = (0, import_types44.infraId)("sql-table", ref.childTable);
13603
- const parentId = (0, import_types44.infraId)("sql-table", ref.parentTable);
14273
+ const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14274
+ const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
13604
14275
  if (childId === parentId) continue;
13605
14276
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13606
14277
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13607
- const edgeId = (0, import_types44.extractedEdgeId)(childId, parentId, import_types44.EdgeType.REFERENCES);
14278
+ const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
13608
14279
  if (graph.hasEdge(edgeId)) continue;
13609
14280
  const edge = {
13610
14281
  id: edgeId,
13611
14282
  source: childId,
13612
14283
  target: parentId,
13613
- type: import_types44.EdgeType.REFERENCES,
13614
- provenance: import_types44.Provenance.EXTRACTED,
13615
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14284
+ type: import_types45.EdgeType.REFERENCES,
14285
+ provenance: import_types45.Provenance.EXTRACTED,
14286
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13616
14287
  evidence: ref.evidence
13617
14288
  };
13618
14289
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13625,7 +14296,7 @@ function ensureTableNode(graph, id, name) {
13625
14296
  if (graph.hasNode(id)) return 0;
13626
14297
  const node = {
13627
14298
  id,
13628
- type: import_types44.NodeType.InfraNode,
14299
+ type: import_types45.NodeType.InfraNode,
13629
14300
  name,
13630
14301
  provider: "self",
13631
14302
  kind: "sql-table"
@@ -13639,16 +14310,16 @@ init_cjs_shims();
13639
14310
 
13640
14311
  // src/extract/infra/docker-compose.ts
13641
14312
  init_cjs_shims();
13642
- var import_node_path55 = __toESM(require("path"), 1);
13643
- var import_types46 = require("@neat.is/types");
14313
+ var import_node_path57 = __toESM(require("path"), 1);
14314
+ var import_types47 = require("@neat.is/types");
13644
14315
 
13645
14316
  // src/extract/infra/shared.ts
13646
14317
  init_cjs_shims();
13647
- var import_types45 = require("@neat.is/types");
14318
+ var import_types46 = require("@neat.is/types");
13648
14319
  function makeInfraNode(kind, name, provider = "self", extras) {
13649
14320
  return {
13650
- id: (0, import_types45.infraId)(kind, name),
13651
- type: import_types45.NodeType.InfraNode,
14321
+ id: (0, import_types46.infraId)(kind, name),
14322
+ type: import_types46.NodeType.InfraNode,
13652
14323
  name,
13653
14324
  provider,
13654
14325
  kind,
@@ -13692,8 +14363,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13692
14363
  source: anchorId,
13693
14364
  target: node.id,
13694
14365
  type: edgeType,
13695
- provenance: import_types45.Provenance.EXTRACTED,
13696
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14366
+ provenance: import_types46.Provenance.EXTRACTED,
14367
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13697
14368
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13698
14369
  };
13699
14370
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13710,7 +14381,7 @@ function dependsOnList(value) {
13710
14381
  }
13711
14382
  function serviceNameToServiceNode(name, services) {
13712
14383
  for (const s of services) {
13713
- if (s.node.name === name || import_node_path55.default.basename(s.dir) === name) return s.node.id;
14384
+ if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
13714
14385
  }
13715
14386
  return null;
13716
14387
  }
@@ -13719,7 +14390,7 @@ async function addComposeInfra(graph, scanPath, services) {
13719
14390
  let edgesAdded = 0;
13720
14391
  let composePath = null;
13721
14392
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13722
- const abs = import_node_path55.default.join(scanPath, name);
14393
+ const abs = import_node_path57.default.join(scanPath, name);
13723
14394
  if (await exists(abs)) {
13724
14395
  composePath = abs;
13725
14396
  break;
@@ -13732,13 +14403,13 @@ async function addComposeInfra(graph, scanPath, services) {
13732
14403
  } catch (err) {
13733
14404
  recordExtractionError(
13734
14405
  "infra docker-compose",
13735
- import_node_path55.default.relative(scanPath, composePath),
14406
+ import_node_path57.default.relative(scanPath, composePath),
13736
14407
  err
13737
14408
  );
13738
14409
  return { nodesAdded, edgesAdded };
13739
14410
  }
13740
14411
  if (!compose?.services) return { nodesAdded, edgesAdded };
13741
- const evidenceFile = import_node_path55.default.relative(scanPath, composePath).split(import_node_path55.default.sep).join("/");
14412
+ const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
13742
14413
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13743
14414
  for (const [composeName, svc] of Object.entries(compose.services)) {
13744
14415
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13760,15 +14431,15 @@ async function addComposeInfra(graph, scanPath, services) {
13760
14431
  for (const dep of dependsOnList(svc.depends_on)) {
13761
14432
  const targetId = composeNameToNodeId.get(dep);
13762
14433
  if (!targetId) continue;
13763
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types46.EdgeType.DEPENDS_ON);
14434
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
13764
14435
  if (graph.hasEdge(edgeId)) continue;
13765
14436
  const edge = {
13766
14437
  id: edgeId,
13767
14438
  source: sourceId,
13768
14439
  target: targetId,
13769
- type: import_types46.EdgeType.DEPENDS_ON,
13770
- provenance: import_types46.Provenance.EXTRACTED,
13771
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14440
+ type: import_types47.EdgeType.DEPENDS_ON,
14441
+ provenance: import_types47.Provenance.EXTRACTED,
14442
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13772
14443
  evidence: { file: evidenceFile }
13773
14444
  };
13774
14445
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13780,9 +14451,9 @@ async function addComposeInfra(graph, scanPath, services) {
13780
14451
 
13781
14452
  // src/extract/infra/dockerfile.ts
13782
14453
  init_cjs_shims();
13783
- var import_node_path56 = __toESM(require("path"), 1);
13784
- var import_node_fs24 = require("fs");
13785
- var import_types47 = require("@neat.is/types");
14454
+ var import_node_path58 = __toESM(require("path"), 1);
14455
+ var import_node_fs25 = require("fs");
14456
+ var import_types48 = require("@neat.is/types");
13786
14457
  function readDockerfile(content) {
13787
14458
  let image = null;
13788
14459
  const ports = [];
@@ -13811,15 +14482,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13811
14482
  let nodesAdded = 0;
13812
14483
  let edgesAdded = 0;
13813
14484
  for (const service of services) {
13814
- const dockerfilePath = import_node_path56.default.join(service.dir, "Dockerfile");
14485
+ const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
13815
14486
  if (!await exists(dockerfilePath)) continue;
13816
14487
  let content;
13817
14488
  try {
13818
- content = await import_node_fs24.promises.readFile(dockerfilePath, "utf8");
14489
+ content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
13819
14490
  } catch (err) {
13820
14491
  recordExtractionError(
13821
14492
  "infra dockerfile",
13822
- import_node_path56.default.relative(scanPath, dockerfilePath),
14493
+ import_node_path58.default.relative(scanPath, dockerfilePath),
13823
14494
  err
13824
14495
  );
13825
14496
  continue;
@@ -13831,8 +14502,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13831
14502
  graph.addNode(node.id, node);
13832
14503
  nodesAdded++;
13833
14504
  }
13834
- const relDockerfile = toPosix(import_node_path56.default.relative(service.dir, dockerfilePath));
13835
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, dockerfilePath));
14505
+ const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14506
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
13836
14507
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13837
14508
  graph,
13838
14509
  service.pkg.name,
@@ -13841,15 +14512,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13841
14512
  );
13842
14513
  nodesAdded += fn;
13843
14514
  edgesAdded += fe;
13844
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types47.EdgeType.RUNS_ON);
14515
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
13845
14516
  if (!graph.hasEdge(edgeId)) {
13846
14517
  const edge = {
13847
14518
  id: edgeId,
13848
14519
  source: fileNodeId,
13849
14520
  target: node.id,
13850
- type: import_types47.EdgeType.RUNS_ON,
13851
- provenance: import_types47.Provenance.EXTRACTED,
13852
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14521
+ type: import_types48.EdgeType.RUNS_ON,
14522
+ provenance: import_types48.Provenance.EXTRACTED,
14523
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13853
14524
  evidence: {
13854
14525
  file: evidenceFile,
13855
14526
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13864,15 +14535,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13864
14535
  graph.addNode(portNode.id, portNode);
13865
14536
  nodesAdded++;
13866
14537
  }
13867
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types47.EdgeType.CONNECTS_TO);
14538
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
13868
14539
  if (graph.hasEdge(portEdgeId)) continue;
13869
14540
  const portEdge = {
13870
14541
  id: portEdgeId,
13871
14542
  source: fileNodeId,
13872
14543
  target: portNode.id,
13873
- type: import_types47.EdgeType.CONNECTS_TO,
13874
- provenance: import_types47.Provenance.EXTRACTED,
13875
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14544
+ type: import_types48.EdgeType.CONNECTS_TO,
14545
+ provenance: import_types48.Provenance.EXTRACTED,
14546
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13876
14547
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13877
14548
  };
13878
14549
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13884,23 +14555,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13884
14555
 
13885
14556
  // src/extract/infra/terraform.ts
13886
14557
  init_cjs_shims();
13887
- var import_node_fs25 = require("fs");
13888
- var import_node_path57 = __toESM(require("path"), 1);
13889
- var import_types48 = require("@neat.is/types");
14558
+ var import_node_fs26 = require("fs");
14559
+ var import_node_path59 = __toESM(require("path"), 1);
14560
+ var import_types49 = require("@neat.is/types");
13890
14561
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13891
14562
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13892
14563
  async function walkTfFiles(start, depth = 0, max = 5) {
13893
14564
  if (depth > max) return [];
13894
14565
  const out = [];
13895
- const entries = await import_node_fs25.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14566
+ const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13896
14567
  for (const entry of entries) {
13897
14568
  if (entry.isDirectory()) {
13898
14569
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
13899
- const child = import_node_path57.default.join(start, entry.name);
14570
+ const child = import_node_path59.default.join(start, entry.name);
13900
14571
  if (await isPythonVenvDir(child)) continue;
13901
14572
  out.push(...await walkTfFiles(child, depth + 1, max));
13902
14573
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
13903
- out.push(import_node_path57.default.join(start, entry.name));
14574
+ out.push(import_node_path59.default.join(start, entry.name));
13904
14575
  }
13905
14576
  }
13906
14577
  return out;
@@ -13931,8 +14602,8 @@ async function addTerraformResources(graph, scanPath) {
13931
14602
  let edgesAdded = 0;
13932
14603
  const files = await walkTfFiles(scanPath);
13933
14604
  for (const file of files) {
13934
- const content = await import_node_fs25.promises.readFile(file, "utf8");
13935
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, file));
14605
+ const content = await import_node_fs26.promises.readFile(file, "utf8");
14606
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
13936
14607
  const resources = [];
13937
14608
  const byKey = /* @__PURE__ */ new Map();
13938
14609
  RESOURCE_RE.lastIndex = 0;
@@ -13967,16 +14638,16 @@ async function addTerraformResources(graph, scanPath) {
13967
14638
  if (!target) continue;
13968
14639
  if (seen.has(target.nodeId)) continue;
13969
14640
  seen.add(target.nodeId);
13970
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types48.EdgeType.DEPENDS_ON);
14641
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
13971
14642
  if (graph.hasEdge(edgeId)) continue;
13972
14643
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13973
14644
  const edge = {
13974
14645
  id: edgeId,
13975
14646
  source: resource.nodeId,
13976
14647
  target: target.nodeId,
13977
- type: import_types48.EdgeType.DEPENDS_ON,
13978
- provenance: import_types48.Provenance.EXTRACTED,
13979
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
14648
+ type: import_types49.EdgeType.DEPENDS_ON,
14649
+ provenance: import_types49.Provenance.EXTRACTED,
14650
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
13980
14651
  evidence: { file: evidenceFile, line, snippet: key }
13981
14652
  };
13982
14653
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13989,8 +14660,8 @@ async function addTerraformResources(graph, scanPath) {
13989
14660
 
13990
14661
  // src/extract/infra/k8s.ts
13991
14662
  init_cjs_shims();
13992
- var import_node_fs26 = require("fs");
13993
- var import_node_path58 = __toESM(require("path"), 1);
14663
+ var import_node_fs27 = require("fs");
14664
+ var import_node_path60 = __toESM(require("path"), 1);
13994
14665
  var import_yaml3 = require("yaml");
13995
14666
  var K8S_KIND_TO_INFRA_KIND = {
13996
14667
  Service: "k8s-service",
@@ -14004,15 +14675,15 @@ var K8S_KIND_TO_INFRA_KIND = {
14004
14675
  async function walkYamlFiles2(start, depth = 0, max = 5) {
14005
14676
  if (depth > max) return [];
14006
14677
  const out = [];
14007
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14678
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14008
14679
  for (const entry of entries) {
14009
14680
  if (entry.isDirectory()) {
14010
14681
  if (IGNORED_DIRS.has(entry.name)) continue;
14011
- const child = import_node_path58.default.join(start, entry.name);
14682
+ const child = import_node_path60.default.join(start, entry.name);
14012
14683
  if (await isPythonVenvDir(child)) continue;
14013
14684
  out.push(...await walkYamlFiles2(child, depth + 1, max));
14014
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path58.default.extname(entry.name))) {
14015
- out.push(import_node_path58.default.join(start, entry.name));
14685
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
14686
+ out.push(import_node_path60.default.join(start, entry.name));
14016
14687
  }
14017
14688
  }
14018
14689
  return out;
@@ -14021,7 +14692,7 @@ async function addK8sResources(graph, scanPath) {
14021
14692
  let nodesAdded = 0;
14022
14693
  const files = await walkYamlFiles2(scanPath);
14023
14694
  for (const file of files) {
14024
- const content = await import_node_fs26.promises.readFile(file, "utf8");
14695
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
14025
14696
  let docs;
14026
14697
  try {
14027
14698
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14045,16 +14716,16 @@ async function addK8sResources(graph, scanPath) {
14045
14716
 
14046
14717
  // src/extract/infra/cloudflare.ts
14047
14718
  init_cjs_shims();
14048
- var import_node_fs27 = require("fs");
14049
- var import_node_path59 = __toESM(require("path"), 1);
14719
+ var import_node_fs28 = require("fs");
14720
+ var import_node_path61 = __toESM(require("path"), 1);
14050
14721
  var import_smol_toml3 = require("smol-toml");
14051
- var import_types49 = require("@neat.is/types");
14722
+ var import_types50 = require("@neat.is/types");
14052
14723
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14053
14724
  async function readWranglerConfig(dir) {
14054
14725
  for (const filename of WRANGLER_FILENAMES) {
14055
- const abs = import_node_path59.default.join(dir, filename);
14726
+ const abs = import_node_path61.default.join(dir, filename);
14056
14727
  if (!await exists(abs)) continue;
14057
- const raw = await import_node_fs27.promises.readFile(abs, "utf8");
14728
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14058
14729
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14059
14730
  return { config, relFile: filename, raw };
14060
14731
  }
@@ -14096,8 +14767,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14096
14767
  source: anchorId,
14097
14768
  target: node.id,
14098
14769
  type: edgeType,
14099
- provenance: import_types49.Provenance.EXTRACTED,
14100
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14770
+ provenance: import_types50.Provenance.EXTRACTED,
14771
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14101
14772
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14102
14773
  };
14103
14774
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14115,11 +14786,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14115
14786
  try {
14116
14787
  read = await readWranglerConfig(service.dir);
14117
14788
  } catch (err) {
14118
- recordExtractionError("infra cloudflare", import_node_path59.default.relative(scanPath, service.dir), err);
14789
+ recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
14119
14790
  continue;
14120
14791
  }
14121
14792
  if (!read || !read.config.name) continue;
14122
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, import_node_path59.default.join(service.dir, read.relFile)));
14793
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
14123
14794
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14124
14795
  }
14125
14796
  for (const worker of discovered) {
@@ -14131,7 +14802,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14131
14802
  }
14132
14803
  let anchorId = service.node.id;
14133
14804
  if (config.main) {
14134
- const entryRelPath = toPosix(import_node_path59.default.normalize(config.main));
14805
+ const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
14135
14806
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14136
14807
  graph,
14137
14808
  service.pkg.name,
@@ -14158,15 +14829,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14158
14829
  nodesAdded++;
14159
14830
  }
14160
14831
  if (runtimeNode.id !== anchorId) {
14161
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types49.EdgeType.RUNS_ON);
14832
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
14162
14833
  if (!graph.hasEdge(runsOnId)) {
14163
14834
  const edge = {
14164
14835
  id: runsOnId,
14165
14836
  source: anchorId,
14166
14837
  target: runtimeNode.id,
14167
- type: import_types49.EdgeType.RUNS_ON,
14168
- provenance: import_types49.Provenance.EXTRACTED,
14169
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14838
+ type: import_types50.EdgeType.RUNS_ON,
14839
+ provenance: import_types50.Provenance.EXTRACTED,
14840
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14170
14841
  evidence: {
14171
14842
  file: evidenceFile,
14172
14843
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14180,7 +14851,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14180
14851
  const result = addResourceEdge(
14181
14852
  graph,
14182
14853
  anchorId,
14183
- import_types49.EdgeType.CONNECTS_TO,
14854
+ import_types50.EdgeType.CONNECTS_TO,
14184
14855
  "cloudflare-route",
14185
14856
  route,
14186
14857
  evidenceFile,
@@ -14204,7 +14875,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14204
14875
  const result = addResourceEdge(
14205
14876
  graph,
14206
14877
  anchorId,
14207
- import_types49.EdgeType.DEPENDS_ON,
14878
+ import_types50.EdgeType.DEPENDS_ON,
14208
14879
  group.kind,
14209
14880
  name,
14210
14881
  evidenceFile,
@@ -14218,7 +14889,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14218
14889
  const result = addResourceEdge(
14219
14890
  graph,
14220
14891
  anchorId,
14221
- import_types49.EdgeType.DEPENDS_ON,
14892
+ import_types50.EdgeType.DEPENDS_ON,
14222
14893
  "cloudflare-cron",
14223
14894
  cron,
14224
14895
  evidenceFile,
@@ -14231,7 +14902,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14231
14902
  const result = addResourceEdge(
14232
14903
  graph,
14233
14904
  anchorId,
14234
- import_types49.EdgeType.DEPENDS_ON,
14905
+ import_types50.EdgeType.DEPENDS_ON,
14235
14906
  "cloudflare-env-var",
14236
14907
  varName,
14237
14908
  evidenceFile,
@@ -14244,15 +14915,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14244
14915
  if (!svc.service) continue;
14245
14916
  const target = workerIndex.get(svc.service);
14246
14917
  if (target && target.anchorId !== anchorId) {
14247
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types49.EdgeType.CALLS);
14918
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
14248
14919
  if (!graph.hasEdge(edgeId)) {
14249
14920
  const edge = {
14250
14921
  id: edgeId,
14251
14922
  source: anchorId,
14252
14923
  target: target.anchorId,
14253
- type: import_types49.EdgeType.CALLS,
14254
- provenance: import_types49.Provenance.EXTRACTED,
14255
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14924
+ type: import_types50.EdgeType.CALLS,
14925
+ provenance: import_types50.Provenance.EXTRACTED,
14926
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14256
14927
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14257
14928
  };
14258
14929
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14263,7 +14934,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14263
14934
  const result = addResourceEdge(
14264
14935
  graph,
14265
14936
  anchorId,
14266
- import_types49.EdgeType.DEPENDS_ON,
14937
+ import_types50.EdgeType.DEPENDS_ON,
14267
14938
  "cloudflare-service-binding",
14268
14939
  svc.service,
14269
14940
  evidenceFile,
@@ -14278,24 +14949,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14278
14949
 
14279
14950
  // src/extract/infra/vercel.ts
14280
14951
  init_cjs_shims();
14281
- var import_node_fs28 = require("fs");
14282
- var import_node_path60 = __toESM(require("path"), 1);
14283
- var import_types50 = require("@neat.is/types");
14952
+ var import_node_fs29 = require("fs");
14953
+ var import_node_path62 = __toESM(require("path"), 1);
14954
+ var import_types51 = require("@neat.is/types");
14284
14955
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14285
14956
  async function readVercelConfig(dir) {
14286
14957
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14287
- const abs = import_node_path60.default.join(dir, filename);
14958
+ const abs = import_node_path62.default.join(dir, filename);
14288
14959
  if (!await exists(abs)) continue;
14289
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14960
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14290
14961
  const config = JSON.parse(maskCommentsInSource(raw));
14291
14962
  return { config, relFile: filename, raw };
14292
14963
  }
14293
14964
  return null;
14294
14965
  }
14295
14966
  async function readLinkedProjectName(dir) {
14296
- const abs = import_node_path60.default.join(dir, ".vercel", "project.json");
14967
+ const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
14297
14968
  if (!await exists(abs)) return void 0;
14298
- const parsed = JSON.parse(await import_node_fs28.promises.readFile(abs, "utf8"));
14969
+ const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
14299
14970
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14300
14971
  }
14301
14972
  function routeSource(route) {
@@ -14311,7 +14982,7 @@ async function addVercelServices(graph, services, scanPath) {
14311
14982
  read = await readVercelConfig(service.dir);
14312
14983
  projectName = await readLinkedProjectName(service.dir);
14313
14984
  } catch (err) {
14314
- recordExtractionError("infra vercel", import_node_path60.default.relative(scanPath, service.dir), err);
14985
+ recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
14315
14986
  continue;
14316
14987
  }
14317
14988
  if (!read && !projectName) continue;
@@ -14327,7 +14998,7 @@ async function addVercelServices(graph, services, scanPath) {
14327
14998
  const anchorId = service.node.id;
14328
14999
  if (!read) continue;
14329
15000
  const { config, relFile, raw } = read;
14330
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, import_node_path60.default.join(service.dir, relFile)));
15001
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
14331
15002
  const add = (edgeType, kind, name) => {
14332
15003
  if (!name) return;
14333
15004
  const result = emitPlatformResourceEdge(
@@ -14343,12 +15014,12 @@ async function addVercelServices(graph, services, scanPath) {
14343
15014
  nodesAdded += result.nodesAdded;
14344
15015
  edgesAdded += result.edgesAdded;
14345
15016
  };
14346
- add(import_types50.EdgeType.RUNS_ON, "vercel", "vercel");
14347
- for (const cron of config.crons ?? []) add(import_types50.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14348
- for (const varName of Object.keys(config.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14349
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15017
+ add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
15018
+ for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15019
+ for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15020
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14350
15021
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14351
- add(import_types50.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15022
+ add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14352
15023
  }
14353
15024
  }
14354
15025
  return { nodesAdded, edgesAdded };
@@ -14356,16 +15027,16 @@ async function addVercelServices(graph, services, scanPath) {
14356
15027
 
14357
15028
  // src/extract/infra/railway.ts
14358
15029
  init_cjs_shims();
14359
- var import_node_fs29 = require("fs");
14360
- var import_node_path61 = __toESM(require("path"), 1);
15030
+ var import_node_fs30 = require("fs");
15031
+ var import_node_path63 = __toESM(require("path"), 1);
14361
15032
  var import_smol_toml4 = require("smol-toml");
14362
- var import_types51 = require("@neat.is/types");
15033
+ var import_types52 = require("@neat.is/types");
14363
15034
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14364
15035
  async function readRailwayConfig(dir) {
14365
15036
  for (const filename of RAILWAY_FILENAMES) {
14366
- const abs = import_node_path61.default.join(dir, filename);
15037
+ const abs = import_node_path63.default.join(dir, filename);
14367
15038
  if (!await exists(abs)) continue;
14368
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15039
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14369
15040
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14370
15041
  return { config, relFile: filename, raw };
14371
15042
  }
@@ -14379,7 +15050,7 @@ async function addRailwayServices(graph, services, scanPath) {
14379
15050
  try {
14380
15051
  read = await readRailwayConfig(service.dir);
14381
15052
  } catch (err) {
14382
- recordExtractionError("infra railway", import_node_path61.default.relative(scanPath, service.dir), err);
15053
+ recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
14383
15054
  continue;
14384
15055
  }
14385
15056
  if (!read) continue;
@@ -14389,7 +15060,7 @@ async function addRailwayServices(graph, services, scanPath) {
14389
15060
  }
14390
15061
  const anchorId = service.node.id;
14391
15062
  const { config, relFile, raw } = read;
14392
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, relFile)));
15063
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
14393
15064
  const add = (edgeType, kind, name) => {
14394
15065
  if (!name) return;
14395
15066
  const result = emitPlatformResourceEdge(
@@ -14405,24 +15076,24 @@ async function addRailwayServices(graph, services, scanPath) {
14405
15076
  nodesAdded += result.nodesAdded;
14406
15077
  edgesAdded += result.edgesAdded;
14407
15078
  };
14408
- add(import_types51.EdgeType.RUNS_ON, "railway", "railway");
14409
- add(import_types51.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14410
- add(import_types51.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15079
+ add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15080
+ add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15081
+ add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14411
15082
  }
14412
15083
  return { nodesAdded, edgesAdded };
14413
15084
  }
14414
15085
 
14415
15086
  // src/extract/infra/supabase.ts
14416
15087
  init_cjs_shims();
14417
- var import_node_fs30 = require("fs");
14418
- var import_node_path62 = __toESM(require("path"), 1);
15088
+ var import_node_fs31 = require("fs");
15089
+ var import_node_path64 = __toESM(require("path"), 1);
14419
15090
  var import_smol_toml5 = require("smol-toml");
14420
- var import_types52 = require("@neat.is/types");
15091
+ var import_types53 = require("@neat.is/types");
14421
15092
  async function readSupabaseConfig(dir) {
14422
- const relFile = import_node_path62.default.join("supabase", "config.toml");
14423
- const abs = import_node_path62.default.join(dir, relFile);
15093
+ const relFile = import_node_path64.default.join("supabase", "config.toml");
15094
+ const abs = import_node_path64.default.join(dir, relFile);
14424
15095
  if (!await exists(abs)) return null;
14425
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15096
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14426
15097
  const config = (0, import_smol_toml5.parse)(raw);
14427
15098
  return { config, relFile, raw };
14428
15099
  }
@@ -14434,7 +15105,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14434
15105
  try {
14435
15106
  read = await readSupabaseConfig(service.dir);
14436
15107
  } catch (err) {
14437
- recordExtractionError("infra supabase", import_node_path62.default.relative(scanPath, service.dir), err);
15108
+ recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
14438
15109
  continue;
14439
15110
  }
14440
15111
  if (!read) continue;
@@ -14449,7 +15120,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14449
15120
  });
14450
15121
  }
14451
15122
  const anchorId = service.node.id;
14452
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15123
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14453
15124
  const add = (edgeType, kind, name) => {
14454
15125
  if (!name) return;
14455
15126
  const result = emitPlatformResourceEdge(
@@ -14465,10 +15136,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14465
15136
  nodesAdded += result.nodesAdded;
14466
15137
  edgesAdded += result.edgesAdded;
14467
15138
  };
14468
- add(import_types52.EdgeType.RUNS_ON, "supabase", "supabase");
14469
- for (const fn of Object.keys(config.functions ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "supabase-function", fn);
14470
- if (config.storage) add(import_types52.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14471
- if (config.auth) add(import_types52.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15139
+ add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15140
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15141
+ if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15142
+ if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
14472
15143
  }
14473
15144
  return { nodesAdded, edgesAdded };
14474
15145
  }
@@ -14491,14 +15162,14 @@ async function addInfra(graph, scanPath, services) {
14491
15162
 
14492
15163
  // src/extract/zod-shapes.ts
14493
15164
  init_cjs_shims();
14494
- var import_node_path63 = __toESM(require("path"), 1);
14495
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15165
+ var import_node_path65 = __toESM(require("path"), 1);
15166
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14496
15167
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
14497
- var import_types53 = require("@neat.is/types");
15168
+ var import_types54 = require("@neat.is/types");
14498
15169
  var ZOD_IMPORT_RE = /\bzod\b/;
14499
15170
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
14500
15171
  function parserForExt4(ext) {
14501
- const p = new import_tree_sitter16.default();
15172
+ const p = new import_tree_sitter17.default();
14502
15173
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
14503
15174
  return p;
14504
15175
  }
@@ -14586,7 +15257,7 @@ function topLevelSchemas(root) {
14586
15257
  }
14587
15258
  function zodShapesFromFile(file, serviceDir) {
14588
15259
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14589
- const tree = parseSource3(parserForExt4(import_node_path63.default.extname(file.path)), file.content);
15260
+ const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
14590
15261
  const out = [];
14591
15262
  const seen = /* @__PURE__ */ new Set();
14592
15263
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14600,11 +15271,11 @@ function zodShapesFromFile(file, serviceDir) {
14600
15271
  seen.add(name);
14601
15272
  const line = call.startPosition.row + 1;
14602
15273
  out.push({
14603
- infraId: (0, import_types53.infraId)("zod-schema", name),
15274
+ infraId: (0, import_types54.infraId)("zod-schema", name),
14604
15275
  name,
14605
15276
  fields,
14606
15277
  evidence: {
14607
- file: import_node_path63.default.relative(serviceDir, file.path),
15278
+ file: import_node_path65.default.relative(serviceDir, file.path),
14608
15279
  line,
14609
15280
  snippet: snippet(file.content, line)
14610
15281
  }
@@ -14635,7 +15306,7 @@ async function addZodShapes(graph, services) {
14635
15306
  if (!graph.hasNode(shape.infraId)) {
14636
15307
  const node = {
14637
15308
  id: shape.infraId,
14638
- type: import_types53.NodeType.InfraNode,
15309
+ type: import_types54.NodeType.InfraNode,
14639
15310
  name: shape.name,
14640
15311
  provider: "self",
14641
15312
  kind: "zod-schema"
@@ -14645,14 +15316,14 @@ async function addZodShapes(graph, services) {
14645
15316
  }
14646
15317
  if (shape.fields.length > 0) {
14647
15318
  const node = graph.getNodeAttributes(shape.infraId);
14648
- if (node.type === import_types53.NodeType.InfraNode) {
15319
+ if (node.type === import_types54.NodeType.InfraNode) {
14649
15320
  graph.replaceNodeAttributes(shape.infraId, {
14650
15321
  ...node,
14651
15322
  columns: foldColumns(
14652
15323
  node.columns,
14653
15324
  shape.fields,
14654
- import_types53.Provenance.EXTRACTED,
14655
- (0, import_types53.confidenceForExtracted)("structural")
15325
+ import_types54.Provenance.EXTRACTED,
15326
+ (0, import_types54.confidenceForExtracted)("structural")
14656
15327
  )
14657
15328
  });
14658
15329
  }
@@ -14666,15 +15337,15 @@ async function addZodShapes(graph, services) {
14666
15337
  );
14667
15338
  nodesAdded += n;
14668
15339
  edgesAdded += e;
14669
- const edgeId = (0, import_types53.extractedEdgeId)(fileNodeId, shape.infraId, import_types53.EdgeType.CONTAINS);
15340
+ const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
14670
15341
  if (!graph.hasEdge(edgeId)) {
14671
15342
  const edge = {
14672
15343
  id: edgeId,
14673
15344
  source: fileNodeId,
14674
15345
  target: shape.infraId,
14675
- type: import_types53.EdgeType.CONTAINS,
14676
- provenance: import_types53.Provenance.EXTRACTED,
14677
- confidence: (0, import_types53.confidenceForExtracted)("structural"),
15346
+ type: import_types54.EdgeType.CONTAINS,
15347
+ provenance: import_types54.Provenance.EXTRACTED,
15348
+ confidence: (0, import_types54.confidenceForExtracted)("structural"),
14678
15349
  evidence: shape.evidence
14679
15350
  };
14680
15351
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14688,7 +15359,7 @@ async function addZodShapes(graph, services) {
14688
15359
 
14689
15360
  // src/extract/firestore-rules.ts
14690
15361
  init_cjs_shims();
14691
- var import_types54 = require("@neat.is/types");
15362
+ var import_types55 = require("@neat.is/types");
14692
15363
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14693
15364
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14694
15365
  function stripComments(src) {
@@ -14828,7 +15499,7 @@ async function addFirestoreRules(graph, services) {
14828
15499
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14829
15500
  graph.forEachNode((id, attrs) => {
14830
15501
  const node = attrs;
14831
- if (node.type !== import_types54.NodeType.InfraNode) return;
15502
+ if (node.type !== import_types55.NodeType.InfraNode) return;
14832
15503
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14833
15504
  const fields = guards.get(collectionKeyFromName(node.name));
14834
15505
  if (!fields || fields.size === 0) return;
@@ -14841,17 +15512,17 @@ async function addFirestoreRules(graph, services) {
14841
15512
  }
14842
15513
 
14843
15514
  // src/extract/index.ts
14844
- var import_node_path65 = __toESM(require("path"), 1);
15515
+ var import_node_path67 = __toESM(require("path"), 1);
14845
15516
 
14846
15517
  // src/extract/retire.ts
14847
15518
  init_cjs_shims();
14848
- var import_node_fs31 = require("fs");
14849
- var import_node_path64 = __toESM(require("path"), 1);
14850
- var import_types55 = require("@neat.is/types");
15519
+ var import_node_fs32 = require("fs");
15520
+ var import_node_path66 = __toESM(require("path"), 1);
15521
+ var import_types56 = require("@neat.is/types");
14851
15522
  function dropOrphanedFileNodes(graph) {
14852
15523
  const orphans = [];
14853
15524
  graph.forEachNode((id, attrs) => {
14854
- if (attrs.type !== import_types55.NodeType.FileNode) return;
15525
+ if (attrs.type !== import_types56.NodeType.FileNode) return;
14855
15526
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14856
15527
  orphans.push(id);
14857
15528
  }
@@ -14864,14 +15535,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14864
15535
  const bases = [scanPath, ...serviceDirs];
14865
15536
  graph.forEachEdge((id, attrs) => {
14866
15537
  const edge = attrs;
14867
- if (edge.provenance !== import_types55.Provenance.EXTRACTED) return;
15538
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
14868
15539
  const evidenceFile = edge.evidence?.file;
14869
15540
  if (!evidenceFile) return;
14870
- if (import_node_path64.default.isAbsolute(evidenceFile)) {
14871
- if (!(0, import_node_fs31.existsSync)(evidenceFile)) toDrop.push(id);
15541
+ if (import_node_path66.default.isAbsolute(evidenceFile)) {
15542
+ if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
14872
15543
  return;
14873
15544
  }
14874
- const found = bases.some((base) => (0, import_node_fs31.existsSync)(import_node_path64.default.join(base, evidenceFile)));
15545
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
14875
15546
  if (!found) toDrop.push(id);
14876
15547
  });
14877
15548
  for (const id of toDrop) graph.dropEdge(id);
@@ -14928,7 +15599,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14928
15599
  }
14929
15600
  const droppedEntries = drainDroppedExtracted();
14930
15601
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14931
- const rejectedPath = import_node_path65.default.join(import_node_path65.default.dirname(opts.errorsPath), "rejected.ndjson");
15602
+ const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
14932
15603
  try {
14933
15604
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14934
15605
  } catch (err) {
@@ -14962,9 +15633,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14962
15633
 
14963
15634
  // src/persist.ts
14964
15635
  init_cjs_shims();
14965
- var import_node_fs32 = require("fs");
14966
- var import_node_path66 = __toESM(require("path"), 1);
14967
- var import_types56 = require("@neat.is/types");
15636
+ var import_node_fs33 = require("fs");
15637
+ var import_node_path68 = __toESM(require("path"), 1);
15638
+ var import_types57 = require("@neat.is/types");
14968
15639
  var SCHEMA_VERSION = 7;
14969
15640
  function migrateV1ToV2(payload) {
14970
15641
  const nodes = payload.graph.nodes;
@@ -14988,7 +15659,7 @@ function migrateV5ToV6(payload) {
14988
15659
  if (Array.isArray(nodes)) {
14989
15660
  for (const node of nodes) {
14990
15661
  const attrs = node.attributes;
14991
- if (!attrs || attrs.type !== import_types56.NodeType.InfraNode) continue;
15662
+ if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
14992
15663
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14993
15664
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14994
15665
  }
@@ -15004,12 +15675,12 @@ function migrateV2ToV3(payload) {
15004
15675
  for (const edge of edges) {
15005
15676
  const attrs = edge.attributes;
15006
15677
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
15007
- attrs.provenance = import_types56.Provenance.OBSERVED;
15678
+ attrs.provenance = import_types57.Provenance.OBSERVED;
15008
15679
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
15009
15680
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
15010
15681
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
15011
15682
  if (type && source && target) {
15012
- const newId = (0, import_types56.observedEdgeId)(source, target, type);
15683
+ const newId = (0, import_types57.observedEdgeId)(source, target, type);
15013
15684
  attrs.id = newId;
15014
15685
  if (edge.key) edge.key = newId;
15015
15686
  }
@@ -15018,7 +15689,7 @@ function migrateV2ToV3(payload) {
15018
15689
  return { ...payload, schemaVersion: 3 };
15019
15690
  }
15020
15691
  async function ensureDir(filePath) {
15021
- await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(filePath), { recursive: true });
15692
+ await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
15022
15693
  }
15023
15694
  async function saveGraphToDisk(graph, outPath) {
15024
15695
  await ensureDir(outPath);
@@ -15028,13 +15699,13 @@ async function saveGraphToDisk(graph, outPath) {
15028
15699
  graph: graph.export()
15029
15700
  };
15030
15701
  const tmp = `${outPath}.tmp`;
15031
- await import_node_fs32.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15032
- await import_node_fs32.promises.rename(tmp, outPath);
15702
+ await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15703
+ await import_node_fs33.promises.rename(tmp, outPath);
15033
15704
  }
15034
15705
  async function loadGraphFromDisk(graph, outPath) {
15035
15706
  let raw;
15036
15707
  try {
15037
- raw = await import_node_fs32.promises.readFile(outPath, "utf8");
15708
+ raw = await import_node_fs33.promises.readFile(outPath, "utf8");
15038
15709
  } catch (err) {
15039
15710
  if (err.code === "ENOENT") return;
15040
15711
  throw err;
@@ -15110,19 +15781,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
15110
15781
  init_cjs_shims();
15111
15782
  var import_fastify2 = __toESM(require("fastify"), 1);
15112
15783
  var import_cors = __toESM(require("@fastify/cors"), 1);
15113
- var import_types91 = require("@neat.is/types");
15784
+ var import_types92 = require("@neat.is/types");
15114
15785
 
15115
15786
  // src/extend/index.ts
15116
15787
  init_cjs_shims();
15117
- var import_node_fs34 = require("fs");
15118
- var import_node_path68 = __toESM(require("path"), 1);
15788
+ var import_node_fs35 = require("fs");
15789
+ var import_node_path70 = __toESM(require("path"), 1);
15119
15790
  var import_node_os2 = __toESM(require("os"), 1);
15120
15791
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
15121
15792
 
15122
15793
  // src/installers/package-manager.ts
15123
15794
  init_cjs_shims();
15124
- var import_node_fs33 = require("fs");
15125
- var import_node_path67 = __toESM(require("path"), 1);
15795
+ var import_node_fs34 = require("fs");
15796
+ var import_node_path69 = __toESM(require("path"), 1);
15126
15797
  var import_node_child_process = require("child_process");
15127
15798
  var LOCKFILE_PRIORITY = [
15128
15799
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -15137,29 +15808,29 @@ var LOCKFILE_PRIORITY = [
15137
15808
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
15138
15809
  async function exists2(p) {
15139
15810
  try {
15140
- await import_node_fs33.promises.access(p);
15811
+ await import_node_fs34.promises.access(p);
15141
15812
  return true;
15142
15813
  } catch {
15143
15814
  return false;
15144
15815
  }
15145
15816
  }
15146
15817
  async function detectPackageManager(serviceDir) {
15147
- let dir = import_node_path67.default.resolve(serviceDir);
15818
+ let dir = import_node_path69.default.resolve(serviceDir);
15148
15819
  const stops = /* @__PURE__ */ new Set();
15149
15820
  for (let i = 0; i < 64; i++) {
15150
15821
  if (stops.has(dir)) break;
15151
15822
  stops.add(dir);
15152
15823
  for (const candidate of LOCKFILE_PRIORITY) {
15153
- const lockPath = import_node_path67.default.join(dir, candidate.lockfile);
15824
+ const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
15154
15825
  if (await exists2(lockPath)) {
15155
15826
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
15156
15827
  }
15157
15828
  }
15158
- const parent = import_node_path67.default.dirname(dir);
15829
+ const parent = import_node_path69.default.dirname(dir);
15159
15830
  if (parent === dir) break;
15160
15831
  dir = parent;
15161
15832
  }
15162
- return { pm: "npm", cwd: import_node_path67.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15833
+ return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15163
15834
  }
15164
15835
  async function runPackageManagerInstall(cmd) {
15165
15836
  return new Promise((resolve) => {
@@ -15201,15 +15872,15 @@ ${err.message}`
15201
15872
  // src/extend/index.ts
15202
15873
  async function fileExists2(p) {
15203
15874
  try {
15204
- await import_node_fs34.promises.access(p);
15875
+ await import_node_fs35.promises.access(p);
15205
15876
  return true;
15206
15877
  } catch {
15207
15878
  return false;
15208
15879
  }
15209
15880
  }
15210
15881
  async function readPackageJson(scanPath) {
15211
- const pkgPath = import_node_path68.default.join(scanPath, "package.json");
15212
- const raw = await import_node_fs34.promises.readFile(pkgPath, "utf8");
15882
+ const pkgPath = import_node_path70.default.join(scanPath, "package.json");
15883
+ const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
15213
15884
  return JSON.parse(raw);
15214
15885
  }
15215
15886
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -15222,27 +15893,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
15222
15893
  ]);
15223
15894
  async function findHookFiles(scanPath) {
15224
15895
  const found = [];
15225
- const walk9 = async (dir) => {
15226
- const entries = await import_node_fs34.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15896
+ const walk10 = async (dir) => {
15897
+ const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15227
15898
  for (const entry of entries) {
15228
15899
  if (entry.isDirectory()) {
15229
15900
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
15230
- await walk9(import_node_path68.default.join(dir, entry.name));
15901
+ await walk10(import_node_path70.default.join(dir, entry.name));
15231
15902
  } else if (entry.isFile()) {
15232
15903
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
15233
- const rel = import_node_path68.default.relative(scanPath, import_node_path68.default.join(dir, entry.name));
15234
- found.push(rel.split(import_node_path68.default.sep).join("/"));
15904
+ const rel = import_node_path70.default.relative(scanPath, import_node_path70.default.join(dir, entry.name));
15905
+ found.push(rel.split(import_node_path70.default.sep).join("/"));
15235
15906
  }
15236
15907
  }
15237
15908
  }
15238
15909
  };
15239
- await walk9(scanPath);
15910
+ await walk10(scanPath);
15240
15911
  return found.sort();
15241
15912
  }
15242
15913
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15243
15914
  let fallback = null;
15244
15915
  for (const file of hookFiles) {
15245
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(scanPath, file), "utf8");
15916
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
15246
15917
  const patched = splicedContent(content, snippet2);
15247
15918
  if (patched !== null) return { file, content, patched };
15248
15919
  if (fallback === null) fallback = { file, content };
@@ -15250,12 +15921,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15250
15921
  return { file: fallback.file, content: fallback.content, patched: null };
15251
15922
  }
15252
15923
  function extendLogPath() {
15253
- return process.env.NEAT_EXTEND_LOG ?? import_node_path68.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15924
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path70.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15254
15925
  }
15255
15926
  async function appendExtendLog(entry) {
15256
15927
  const logPath = extendLogPath();
15257
- await import_node_fs34.promises.mkdir(import_node_path68.default.dirname(logPath), { recursive: true });
15258
- await import_node_fs34.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
15928
+ await import_node_fs35.promises.mkdir(import_node_path70.default.dirname(logPath), { recursive: true });
15929
+ await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
15259
15930
  }
15260
15931
  function splicedContent(fileContent, snippet2) {
15261
15932
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -15313,7 +15984,7 @@ function lookupInstrumentation(library, installedVersion) {
15313
15984
  }
15314
15985
  async function describeProjectInstrumentation(ctx) {
15315
15986
  const hookFiles = await findHookFiles(ctx.scanPath);
15316
- const envNeat = await fileExists2(import_node_path68.default.join(ctx.scanPath, ".env.neat"));
15987
+ const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
15317
15988
  const registryInstrPackages = new Set(
15318
15989
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
15319
15990
  );
@@ -15335,7 +16006,7 @@ async function applyExtension(ctx, args, options) {
15335
16006
  );
15336
16007
  }
15337
16008
  for (const file of hookFiles) {
15338
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(ctx.scanPath, file), "utf8");
16009
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
15339
16010
  if (content.includes(args.registration_snippet)) {
15340
16011
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
15341
16012
  }
@@ -15347,18 +16018,18 @@ async function applyExtension(ctx, args, options) {
15347
16018
  );
15348
16019
  }
15349
16020
  const primaryFile = primary.file;
15350
- const primaryPath = import_node_path68.default.join(ctx.scanPath, primaryFile);
16021
+ const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
15351
16022
  const filesTouched = [];
15352
16023
  const depsAdded = [];
15353
- const pkgPath = import_node_path68.default.join(ctx.scanPath, "package.json");
16024
+ const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
15354
16025
  const pkg = await readPackageJson(ctx.scanPath);
15355
16026
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
15356
16027
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
15357
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16028
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15358
16029
  filesTouched.push("package.json");
15359
16030
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
15360
16031
  }
15361
- await import_node_fs34.promises.writeFile(primaryPath, primary.patched, "utf8");
16032
+ await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
15362
16033
  filesTouched.push(primaryFile);
15363
16034
  const cmd = await detectPackageManager(ctx.scanPath);
15364
16035
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -15389,7 +16060,7 @@ async function dryRunExtension(ctx, args) {
15389
16060
  };
15390
16061
  }
15391
16062
  for (const file of hookFiles) {
15392
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(ctx.scanPath, file), "utf8");
16063
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
15393
16064
  if (content.includes(args.registration_snippet)) {
15394
16065
  return {
15395
16066
  library: args.library,
@@ -15424,28 +16095,28 @@ async function rollbackExtension(ctx, args) {
15424
16095
  if (!await fileExists2(logPath)) {
15425
16096
  return { undone: false, message: "no apply found for library" };
15426
16097
  }
15427
- const raw = await import_node_fs34.promises.readFile(logPath, "utf8");
16098
+ const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
15428
16099
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
15429
16100
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
15430
16101
  if (!match) {
15431
16102
  return { undone: false, message: "no apply found for library" };
15432
16103
  }
15433
- const pkgPath = import_node_path68.default.join(ctx.scanPath, "package.json");
16104
+ const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
15434
16105
  if (await fileExists2(pkgPath)) {
15435
16106
  const pkg = await readPackageJson(ctx.scanPath);
15436
16107
  if (pkg.dependencies?.[match.instrumentation_package]) {
15437
16108
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
15438
16109
  pkg.dependencies = rest;
15439
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16110
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15440
16111
  }
15441
16112
  }
15442
16113
  const hookFiles = await findHookFiles(ctx.scanPath);
15443
16114
  for (const file of hookFiles) {
15444
- const filePath = import_node_path68.default.join(ctx.scanPath, file);
15445
- const content = await import_node_fs34.promises.readFile(filePath, "utf8");
16115
+ const filePath = import_node_path70.default.join(ctx.scanPath, file);
16116
+ const content = await import_node_fs35.promises.readFile(filePath, "utf8");
15446
16117
  if (content.includes(match.registration_snippet)) {
15447
16118
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
15448
- await import_node_fs34.promises.writeFile(filePath, filtered, "utf8");
16119
+ await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
15449
16120
  break;
15450
16121
  }
15451
16122
  }
@@ -15457,39 +16128,39 @@ async function rollbackExtension(ctx, args) {
15457
16128
 
15458
16129
  // src/divergences.ts
15459
16130
  init_cjs_shims();
15460
- var import_types57 = require("@neat.is/types");
16131
+ var import_types58 = require("@neat.is/types");
15461
16132
  function bucketKey(source, target, type) {
15462
16133
  return `${type}|${source}|${target}`;
15463
16134
  }
15464
16135
  function bucketSourceFor(graph, edge) {
15465
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) return edge.source;
15466
- const parsed = (0, import_types57.parseFileId)(edge.source);
16136
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16137
+ const parsed = (0, import_types58.parseFileId)(edge.source);
15467
16138
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
15468
16139
  const target = graph.getNodeAttributes(edge.target);
15469
- if (target.type !== import_types57.NodeType.DatabaseNode) return edge.source;
15470
- return (0, import_types57.serviceId)(parsed.service);
16140
+ if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16141
+ return (0, import_types58.serviceId)(parsed.service);
15471
16142
  }
15472
16143
  function bucketEdges(graph) {
15473
16144
  const buckets2 = /* @__PURE__ */ new Map();
15474
16145
  graph.forEachEdge((id, attrs) => {
15475
16146
  const e = attrs;
15476
- const parsed = (0, import_types57.parseEdgeId)(id);
16147
+ const parsed = (0, import_types58.parseEdgeId)(id);
15477
16148
  const provenance = parsed?.provenance ?? e.provenance;
15478
16149
  const source = bucketSourceFor(graph, e);
15479
16150
  const key = bucketKey(source, e.target, e.type);
15480
16151
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
15481
16152
  switch (provenance) {
15482
- case import_types57.Provenance.EXTRACTED:
16153
+ case import_types58.Provenance.EXTRACTED:
15483
16154
  cur.extracted = e;
15484
16155
  break;
15485
- case import_types57.Provenance.OBSERVED:
16156
+ case import_types58.Provenance.OBSERVED:
15486
16157
  cur.observed = e;
15487
16158
  break;
15488
- case import_types57.Provenance.INFERRED:
16159
+ case import_types58.Provenance.INFERRED:
15489
16160
  cur.inferred = e;
15490
16161
  break;
15491
16162
  default:
15492
- if (e.provenance === import_types57.Provenance.STALE) cur.stale = e;
16163
+ if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
15493
16164
  }
15494
16165
  buckets2.set(key, cur);
15495
16166
  });
@@ -15498,22 +16169,22 @@ function bucketEdges(graph) {
15498
16169
  function nodeIsFrontier(graph, nodeId) {
15499
16170
  if (!graph.hasNode(nodeId)) return false;
15500
16171
  const attrs = graph.getNodeAttributes(nodeId);
15501
- return attrs.type === import_types57.NodeType.FrontierNode;
16172
+ return attrs.type === import_types58.NodeType.FrontierNode;
15502
16173
  }
15503
16174
  function nodeIsWebsocketChannel(graph, nodeId) {
15504
16175
  if (!graph.hasNode(nodeId)) return false;
15505
16176
  const attrs = graph.getNodeAttributes(nodeId);
15506
- return attrs.type === import_types57.NodeType.WebSocketChannelNode;
16177
+ return attrs.type === import_types58.NodeType.WebSocketChannelNode;
15507
16178
  }
15508
16179
  function nodeIsServerAction(graph, nodeId) {
15509
16180
  if (!graph.hasNode(nodeId)) return false;
15510
16181
  const attrs = graph.getNodeAttributes(nodeId);
15511
- return attrs.type === import_types57.NodeType.ServerActionNode;
16182
+ return attrs.type === import_types58.NodeType.ServerActionNode;
15512
16183
  }
15513
16184
  function nodeIsSymbol(graph, nodeId) {
15514
16185
  if (!graph.hasNode(nodeId)) return false;
15515
16186
  const attrs = graph.getNodeAttributes(nodeId);
15516
- return attrs.type === import_types57.NodeType.SymbolNode;
16187
+ return attrs.type === import_types58.NodeType.SymbolNode;
15517
16188
  }
15518
16189
  function clampConfidence(n) {
15519
16190
  if (!Number.isFinite(n)) return 0;
@@ -15533,14 +16204,14 @@ function gradedConfidence(edge) {
15533
16204
  return clampConfidence(confidenceForEdge(edge));
15534
16205
  }
15535
16206
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
15536
- import_types57.EdgeType.CALLS,
15537
- import_types57.EdgeType.CONNECTS_TO,
15538
- import_types57.EdgeType.PUBLISHES_TO,
15539
- import_types57.EdgeType.CONSUMES_FROM
16207
+ import_types58.EdgeType.CALLS,
16208
+ import_types58.EdgeType.CONNECTS_TO,
16209
+ import_types58.EdgeType.PUBLISHES_TO,
16210
+ import_types58.EdgeType.CONSUMES_FROM
15540
16211
  ]);
15541
16212
  function detectMissingDivergences(graph, bucket) {
15542
16213
  const out = [];
15543
- if (bucket.type === import_types57.EdgeType.CONTAINS) return out;
16214
+ if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
15544
16215
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
15545
16216
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
15546
16217
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -15582,7 +16253,7 @@ function declaredHostFor(svc) {
15582
16253
  function hasExtractedConfiguredBy(graph, svcId) {
15583
16254
  for (const edgeId of graph.outboundEdges(svcId)) {
15584
16255
  const e = graph.getEdgeAttributes(edgeId);
15585
- if (e.type === import_types57.EdgeType.CONFIGURED_BY && e.provenance === import_types57.Provenance.EXTRACTED) {
16256
+ if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
15586
16257
  return true;
15587
16258
  }
15588
16259
  }
@@ -15595,10 +16266,10 @@ function detectHostMismatch(graph, svcId, svc) {
15595
16266
  const out = [];
15596
16267
  for (const edgeId of graph.outboundEdges(svcId)) {
15597
16268
  const edge = graph.getEdgeAttributes(edgeId);
15598
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15599
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16269
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16270
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15600
16271
  const target = graph.getNodeAttributes(edge.target);
15601
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16272
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15602
16273
  const observedHost = target.host?.trim();
15603
16274
  if (!observedHost) continue;
15604
16275
  if (observedHost === declaredHost) continue;
@@ -15620,10 +16291,10 @@ function detectCompatDivergences(graph, svcId, svc) {
15620
16291
  const deps = svc.dependencies ?? {};
15621
16292
  for (const edgeId of graph.outboundEdges(svcId)) {
15622
16293
  const edge = graph.getEdgeAttributes(edgeId);
15623
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15624
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16294
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16295
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15625
16296
  const target = graph.getNodeAttributes(edge.target);
15626
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16297
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15627
16298
  for (const pair of compatPairs()) {
15628
16299
  if (pair.engine !== target.engine) continue;
15629
16300
  const declared = deps[pair.driver];
@@ -15720,7 +16391,7 @@ function suppressHostMismatchHalves(all) {
15720
16391
  for (const d of all) {
15721
16392
  if (d.type !== "host-mismatch") continue;
15722
16393
  observedHalf.add(`${d.source}->${d.target}`);
15723
- declaredHalf.add((0, import_types57.databaseId)(d.extractedHost));
16394
+ declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
15724
16395
  }
15725
16396
  if (observedHalf.size === 0) return all;
15726
16397
  return all.filter((d) => {
@@ -15739,13 +16410,13 @@ function computeDivergences(graph, opts = {}) {
15739
16410
  }
15740
16411
  graph.forEachNode((nodeId, attrs) => {
15741
16412
  const n = attrs;
15742
- if (n.type === import_types57.NodeType.ServiceNode) {
16413
+ if (n.type === import_types58.NodeType.ServiceNode) {
15743
16414
  const svc = n;
15744
16415
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
15745
16416
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
15746
16417
  return;
15747
16418
  }
15748
- if (n.type === import_types57.NodeType.InfraNode && n.kind === "sql-table") {
16419
+ if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
15749
16420
  for (const d of detectColumnDrift(n)) all.push(d);
15750
16421
  }
15751
16422
  });
@@ -15781,7 +16452,7 @@ function computeDivergences(graph, opts = {}) {
15781
16452
  const bc = "column" in b && b.column ? b.column : "";
15782
16453
  return ac.localeCompare(bc);
15783
16454
  });
15784
- return import_types57.DivergenceResultSchema.parse({
16455
+ return import_types58.DivergenceResultSchema.parse({
15785
16456
  divergences: filtered,
15786
16457
  totalAffected: filtered.length,
15787
16458
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -15835,7 +16506,7 @@ function queryLogEntries(opts) {
15835
16506
 
15836
16507
  // src/diff.ts
15837
16508
  init_cjs_shims();
15838
- var import_node_fs35 = require("fs");
16509
+ var import_node_fs36 = require("fs");
15839
16510
  async function loadSnapshotForDiff(target) {
15840
16511
  if (/^https?:\/\//i.test(target)) {
15841
16512
  const res = await fetch(target);
@@ -15844,7 +16515,7 @@ async function loadSnapshotForDiff(target) {
15844
16515
  }
15845
16516
  return await res.json();
15846
16517
  }
15847
- const raw = await import_node_fs35.promises.readFile(target, "utf8");
16518
+ const raw = await import_node_fs36.promises.readFile(target, "utf8");
15848
16519
  return JSON.parse(raw);
15849
16520
  }
15850
16521
  function indexEntries(entries) {
@@ -15912,23 +16583,23 @@ function canonicalJson(value) {
15912
16583
 
15913
16584
  // src/projects.ts
15914
16585
  init_cjs_shims();
15915
- var import_node_path69 = __toESM(require("path"), 1);
16586
+ var import_node_path71 = __toESM(require("path"), 1);
15916
16587
  function pathsForProject(project, baseDir) {
15917
16588
  if (project === DEFAULT_PROJECT) {
15918
16589
  return {
15919
- snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
15920
- errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
15921
- staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
15922
- embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
15923
- policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
16590
+ snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
16591
+ errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
16592
+ staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
16593
+ embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
16594
+ policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
15924
16595
  };
15925
16596
  }
15926
16597
  return {
15927
- snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
15928
- errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
15929
- staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
15930
- embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
15931
- policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
16598
+ snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
16599
+ errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
16600
+ staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
16601
+ embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
16602
+ policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
15932
16603
  };
15933
16604
  }
15934
16605
  var Projects = class {
@@ -15964,28 +16635,28 @@ var Projects = class {
15964
16635
 
15965
16636
  // src/registry.ts
15966
16637
  init_cjs_shims();
15967
- var import_node_fs36 = require("fs");
16638
+ var import_node_fs37 = require("fs");
15968
16639
  var import_node_os3 = __toESM(require("os"), 1);
15969
- var import_node_path70 = __toESM(require("path"), 1);
15970
- var import_types58 = require("@neat.is/types");
16640
+ var import_node_path72 = __toESM(require("path"), 1);
16641
+ var import_types59 = require("@neat.is/types");
15971
16642
  var LOCK_TIMEOUT_MS = 5e3;
15972
16643
  var LOCK_RETRY_MS = 50;
15973
16644
  function neatHome() {
15974
16645
  const override = process.env.NEAT_HOME;
15975
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15976
- return import_node_path70.default.join(import_node_os3.default.homedir(), ".neat");
16646
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16647
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15977
16648
  }
15978
16649
  function registryPath() {
15979
- return import_node_path70.default.join(neatHome(), "projects.json");
16650
+ return import_node_path72.default.join(neatHome(), "projects.json");
15980
16651
  }
15981
16652
  function registryLockPath() {
15982
- return import_node_path70.default.join(neatHome(), "projects.json.lock");
16653
+ return import_node_path72.default.join(neatHome(), "projects.json.lock");
15983
16654
  }
15984
16655
  function daemonPidPath() {
15985
- return import_node_path70.default.join(neatHome(), "neatd.pid");
16656
+ return import_node_path72.default.join(neatHome(), "neatd.pid");
15986
16657
  }
15987
16658
  function daemonsDir() {
15988
- return import_node_path70.default.join(neatHome(), "daemons");
16659
+ return import_node_path72.default.join(neatHome(), "daemons");
15989
16660
  }
15990
16661
  function isFiniteInt(v) {
15991
16662
  return typeof v === "number" && Number.isFinite(v);
@@ -16018,7 +16689,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
16018
16689
  const dir = daemonsDir();
16019
16690
  let names;
16020
16691
  try {
16021
- names = await import_node_fs36.promises.readdir(dir);
16692
+ names = await import_node_fs37.promises.readdir(dir);
16022
16693
  } catch (err) {
16023
16694
  if (err.code === "ENOENT") return [];
16024
16695
  throw err;
@@ -16026,10 +16697,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
16026
16697
  const out = [];
16027
16698
  for (const name of names) {
16028
16699
  if (!name.endsWith(".json")) continue;
16029
- const file = import_node_path70.default.join(dir, name);
16700
+ const file = import_node_path72.default.join(dir, name);
16030
16701
  let raw;
16031
16702
  try {
16032
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16703
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16033
16704
  } catch {
16034
16705
  continue;
16035
16706
  }
@@ -16055,7 +16726,7 @@ function isPidAliveDefault(pid) {
16055
16726
  }
16056
16727
  async function readPidFile(file) {
16057
16728
  try {
16058
- const raw = await import_node_fs36.promises.readFile(file, "utf8");
16729
+ const raw = await import_node_fs37.promises.readFile(file, "utf8");
16059
16730
  const pid = Number.parseInt(raw.trim(), 10);
16060
16731
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
16061
16732
  } catch {
@@ -16103,32 +16774,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
16103
16774
  }
16104
16775
  }
16105
16776
  async function normalizeProjectPath(input) {
16106
- const resolved = import_node_path70.default.resolve(input);
16777
+ const resolved = import_node_path72.default.resolve(input);
16107
16778
  try {
16108
- return await import_node_fs36.promises.realpath(resolved);
16779
+ return await import_node_fs37.promises.realpath(resolved);
16109
16780
  } catch {
16110
16781
  return resolved;
16111
16782
  }
16112
16783
  }
16113
16784
  async function writeAtomically(target, contents) {
16114
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(target), { recursive: true });
16785
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
16115
16786
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
16116
- const fd = await import_node_fs36.promises.open(tmp, "w");
16787
+ const fd = await import_node_fs37.promises.open(tmp, "w");
16117
16788
  try {
16118
16789
  await fd.writeFile(contents, "utf8");
16119
16790
  await fd.sync();
16120
16791
  } finally {
16121
16792
  await fd.close();
16122
16793
  }
16123
- await import_node_fs36.promises.rename(tmp, target);
16794
+ await import_node_fs37.promises.rename(tmp, target);
16124
16795
  }
16125
16796
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
16126
16797
  const deadline = Date.now() + timeoutMs;
16127
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(lockPath), { recursive: true });
16798
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
16128
16799
  let probedHolder = false;
16129
16800
  while (true) {
16130
16801
  try {
16131
- const fd = await import_node_fs36.promises.open(lockPath, "wx");
16802
+ const fd = await import_node_fs37.promises.open(lockPath, "wx");
16132
16803
  try {
16133
16804
  await fd.writeFile(`${process.pid}
16134
16805
  `, "utf8");
@@ -16153,7 +16824,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
16153
16824
  }
16154
16825
  }
16155
16826
  async function releaseLock(lockPath) {
16156
- await import_node_fs36.promises.unlink(lockPath).catch(() => {
16827
+ await import_node_fs37.promises.unlink(lockPath).catch(() => {
16157
16828
  });
16158
16829
  }
16159
16830
  async function withLock(fn) {
@@ -16169,7 +16840,7 @@ async function readRegistry() {
16169
16840
  const file = registryPath();
16170
16841
  let raw;
16171
16842
  try {
16172
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16843
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16173
16844
  } catch (err) {
16174
16845
  if (err.code === "ENOENT") {
16175
16846
  return { version: 1, projects: [] };
@@ -16177,10 +16848,10 @@ async function readRegistry() {
16177
16848
  throw err;
16178
16849
  }
16179
16850
  const parsed = JSON.parse(raw);
16180
- return import_types58.RegistryFileSchema.parse(parsed);
16851
+ return import_types59.RegistryFileSchema.parse(parsed);
16181
16852
  }
16182
16853
  async function writeRegistry(reg) {
16183
- const validated = import_types58.RegistryFileSchema.parse(reg);
16854
+ const validated = import_types59.RegistryFileSchema.parse(reg);
16184
16855
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
16185
16856
  }
16186
16857
  var ProjectNameCollisionError = class extends Error {
@@ -16274,7 +16945,7 @@ function pruneTtlMs() {
16274
16945
  }
16275
16946
  async function statPathStatus(p) {
16276
16947
  try {
16277
- const stat = await import_node_fs36.promises.stat(p);
16948
+ const stat = await import_node_fs37.promises.stat(p);
16278
16949
  return stat.isDirectory() ? "present" : "unknown";
16279
16950
  } catch (err) {
16280
16951
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -16375,8 +17046,8 @@ init_auth();
16375
17046
  // src/connectors-config.ts
16376
17047
  init_cjs_shims();
16377
17048
  var import_node_os4 = __toESM(require("os"), 1);
16378
- var import_node_path71 = __toESM(require("path"), 1);
16379
- var import_node_fs37 = require("fs");
17049
+ var import_node_path73 = __toESM(require("path"), 1);
17050
+ var import_node_fs38 = require("fs");
16380
17051
  var CONNECTORS_CONFIG_VERSION = 1;
16381
17052
  var EnvRefUnsetError = class extends Error {
16382
17053
  ref;
@@ -16390,17 +17061,17 @@ var EnvRefUnsetError = class extends Error {
16390
17061
  };
16391
17062
  function neatHome2() {
16392
17063
  const override = process.env.NEAT_HOME;
16393
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
16394
- return import_node_path71.default.join(import_node_os4.default.homedir(), ".neat");
17064
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
17065
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16395
17066
  }
16396
17067
  function connectorsConfigPath(home = neatHome2()) {
16397
- return import_node_path71.default.join(home, "connectors.json");
17068
+ return import_node_path73.default.join(home, "connectors.json");
16398
17069
  }
16399
17070
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16400
17071
  async function warnIfModeLooserThan0600(file) {
16401
17072
  if (process.platform === "win32") return;
16402
17073
  try {
16403
- const stat = await import_node_fs37.promises.stat(file);
17074
+ const stat = await import_node_fs38.promises.stat(file);
16404
17075
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16405
17076
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16406
17077
  console.warn(
@@ -16414,7 +17085,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16414
17085
  const file = connectorsConfigPath(home);
16415
17086
  let raw;
16416
17087
  try {
16417
- raw = await import_node_fs37.promises.readFile(file, "utf8");
17088
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16418
17089
  } catch (err) {
16419
17090
  if (err.code === "ENOENT") {
16420
17091
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16581,15 +17252,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16581
17252
 
16582
17253
  // src/connectors/index.ts
16583
17254
  init_cjs_shims();
16584
- var import_types59 = require("@neat.is/types");
17255
+ var import_types60 = require("@neat.is/types");
16585
17256
  var NO_ENV = "unknown";
16586
17257
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16587
17258
  if (!graph.hasNode(targetNodeId)) return void 0;
16588
17259
  const sites = [];
16589
17260
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16590
17261
  const edge = graph.getEdgeAttributes(edgeId);
16591
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16592
- const parsed = (0, import_types59.parseFileId)(edge.source);
17262
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
17263
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16593
17264
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16594
17265
  const site = { relPath: edge.evidence.file };
16595
17266
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16600,7 +17271,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16600
17271
  function routeCallSiteFor(graph, targetNodeId) {
16601
17272
  if (!graph.hasNode(targetNodeId)) return void 0;
16602
17273
  const attrs = graph.getNodeAttributes(targetNodeId);
16603
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
17274
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16604
17275
  const site = { relPath: attrs.path };
16605
17276
  if (attrs.line !== void 0) site.line = attrs.line;
16606
17277
  return site;
@@ -17098,10 +17769,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
17098
17769
  // src/connectors/supabase/map.ts
17099
17770
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
17100
17771
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
17101
- function targetFromRestPath(path74) {
17102
- const rpcMatch = REST_RPC_PATH_RE.exec(path74);
17772
+ function targetFromRestPath(path76) {
17773
+ const rpcMatch = REST_RPC_PATH_RE.exec(path76);
17103
17774
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
17104
- const tableMatch = REST_TABLE_PATH_RE.exec(path74);
17775
+ const tableMatch = REST_TABLE_PATH_RE.exec(path76);
17105
17776
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
17106
17777
  return null;
17107
17778
  }
@@ -17212,23 +17883,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
17212
17883
 
17213
17884
  // src/connectors/supabase/resolve.ts
17214
17885
  init_cjs_shims();
17215
- var import_types61 = require("@neat.is/types");
17886
+ var import_types62 = require("@neat.is/types");
17216
17887
  function createSupabaseResolveTarget(graph, config) {
17217
17888
  return (signal, _ctx) => {
17218
17889
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
17219
17890
  return null;
17220
17891
  }
17221
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17892
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17222
17893
  if (graph.hasNode(subResourceId)) {
17223
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17894
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17224
17895
  }
17225
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17896
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
17226
17897
  if (graph.hasNode(bareResourceId)) {
17227
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17898
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17228
17899
  }
17229
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17900
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
17230
17901
  if (graph.hasNode(projectLevelId)) {
17231
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17902
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17232
17903
  }
17233
17904
  return null;
17234
17905
  };
@@ -17321,7 +17992,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
17321
17992
 
17322
17993
  // src/connectors/railway/index.ts
17323
17994
  init_cjs_shims();
17324
- var import_types65 = require("@neat.is/types");
17995
+ var import_types66 = require("@neat.is/types");
17325
17996
 
17326
17997
  // src/connectors/railway/client.ts
17327
17998
  init_cjs_shims();
@@ -17472,7 +18143,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17472
18143
  const out = [];
17473
18144
  graph.forEachNode((_id, attrs) => {
17474
18145
  const node = attrs;
17475
- if (node.type !== import_types65.NodeType.RouteNode) return;
18146
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17476
18147
  const route = attrs;
17477
18148
  if (route.service !== serviceName) return;
17478
18149
  out.push({
@@ -17576,12 +18247,12 @@ function createRailwayResolveTarget(config) {
17576
18247
  const serviceName = config.serviceNameById[config.serviceId];
17577
18248
  if (!serviceName) return null;
17578
18249
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17579
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
18250
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17580
18251
  }
17581
18252
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17582
18253
  const peerName = config.serviceNameById[signal.targetName];
17583
18254
  if (!peerName) return null;
17584
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
18255
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17585
18256
  }
17586
18257
  return null;
17587
18258
  };
@@ -17705,9 +18376,9 @@ function parseFirebaseTargetName(targetName) {
17705
18376
  const secondSep = rest.indexOf(FIELD_SEP);
17706
18377
  if (secondSep === -1) return null;
17707
18378
  const method = rest.slice(0, secondSep);
17708
- const path74 = rest.slice(secondSep + 1);
17709
- if (!resourceName || !method || !path74) return null;
17710
- return { resourceName, method, path: path74 };
18379
+ const path76 = rest.slice(secondSep + 1);
18380
+ if (!resourceName || !method || !path76) return null;
18381
+ return { resourceName, method, path: path76 };
17711
18382
  }
17712
18383
  function resourceNameFor(type, labels) {
17713
18384
  if (!labels) return null;
@@ -17745,14 +18416,14 @@ function mapLogEntryToSignal(entry) {
17745
18416
  if (!req) return null;
17746
18417
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17747
18418
  const method = req.requestMethod.toUpperCase();
17748
- const path74 = pathFromRequestUrl(req.requestUrl);
17749
- if (path74 === null) return null;
18419
+ const path76 = pathFromRequestUrl(req.requestUrl);
18420
+ if (path76 === null) return null;
17750
18421
  const timestamp = entry.timestamp;
17751
18422
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17752
18423
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
17753
18424
  return {
17754
18425
  targetKind: resourceType,
17755
- targetName: packFirebaseTargetName({ resourceName, method, path: path74 }),
18426
+ targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
17756
18427
  callCount: 1,
17757
18428
  errorCount: isError ? 1 : 0,
17758
18429
  lastObservedIso: timestamp
@@ -17769,7 +18440,7 @@ function mapLogEntriesToSignals(entries) {
17769
18440
 
17770
18441
  // src/connectors/firebase/resolve.ts
17771
18442
  init_cjs_shims();
17772
- var import_types66 = require("@neat.is/types");
18443
+ var import_types67 = require("@neat.is/types");
17773
18444
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17774
18445
  switch (resourceType) {
17775
18446
  case "cloud_function":
@@ -17784,7 +18455,7 @@ function routeEntriesFor(graph, serviceName) {
17784
18455
  const entries = [];
17785
18456
  graph.forEachNode((_id, attrs) => {
17786
18457
  const node = attrs;
17787
- if (node.type !== import_types66.NodeType.RouteNode) return;
18458
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17788
18459
  const route = attrs;
17789
18460
  if (route.service !== serviceName) return;
17790
18461
  entries.push({
@@ -17816,7 +18487,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17816
18487
  return {
17817
18488
  targetNodeId: match.routeNodeId,
17818
18489
  serviceName,
17819
- edgeType: import_types66.EdgeType.CALLS
18490
+ edgeType: import_types67.EdgeType.CALLS
17820
18491
  };
17821
18492
  };
17822
18493
  }
@@ -17843,7 +18514,7 @@ init_cjs_shims();
17843
18514
 
17844
18515
  // src/connectors/cloudflare/connector.ts
17845
18516
  init_cjs_shims();
17846
- var import_types68 = require("@neat.is/types");
18517
+ var import_types69 = require("@neat.is/types");
17847
18518
 
17848
18519
  // src/connectors/cloudflare/client.ts
17849
18520
  init_cjs_shims();
@@ -17959,7 +18630,7 @@ function mapEventToSignal(event) {
17959
18630
  if (Number.isNaN(observedAt.getTime())) return null;
17960
18631
  const statusCode = metadata?.statusCode;
17961
18632
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17962
- const path74 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18633
+ const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17963
18634
  return {
17964
18635
  targetKind: CLOUDFLARE_TARGET_KIND,
17965
18636
  targetName: scriptName,
@@ -17967,7 +18638,7 @@ function mapEventToSignal(event) {
17967
18638
  errorCount: isError ? 1 : 0,
17968
18639
  lastObservedIso: observedAt.toISOString(),
17969
18640
  method,
17970
- ...path74 ? { path: path74 } : {},
18641
+ ...path76 ? { path: path76 } : {},
17971
18642
  ...typeof statusCode === "number" ? { statusCode } : {},
17972
18643
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17973
18644
  };
@@ -18007,19 +18678,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
18007
18678
  graph.forEachNode((id, attrs) => {
18008
18679
  if (found) return;
18009
18680
  const a = attrs;
18010
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18681
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18011
18682
  found = id;
18012
18683
  }
18013
18684
  });
18014
18685
  return found;
18015
18686
  }
18016
- function findMatchingRouteNode(graph, serviceName, method, path74) {
18017
- const normalizedPath = normalizePathTemplate(path74);
18687
+ function findMatchingRouteNode(graph, serviceName, method, path76) {
18688
+ const normalizedPath = normalizePathTemplate(path76);
18018
18689
  let found = null;
18019
18690
  graph.forEachNode((id, attrs) => {
18020
18691
  if (found) return;
18021
18692
  const a = attrs;
18022
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18693
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
18023
18694
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
18024
18695
  const routeMethod = (a.method ?? "").toUpperCase();
18025
18696
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -18031,18 +18702,18 @@ function createCloudflareResolveTarget(config, graph) {
18031
18702
  return (signal) => {
18032
18703
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
18033
18704
  const scriptName = signal.targetName;
18034
- const { method, path: path74 } = signal;
18705
+ const { method, path: path76 } = signal;
18035
18706
  const resolveRouteGrain = (serviceName, wholeFileId) => {
18036
- if (!method || !path74) return wholeFileId;
18037
- return findMatchingRouteNode(graph, serviceName, method, path74) ?? wholeFileId;
18707
+ if (!method || !path76) return wholeFileId;
18708
+ return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
18038
18709
  };
18039
18710
  const mapping = config.workers?.[scriptName];
18040
18711
  if (mapping) {
18041
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18712
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
18042
18713
  return {
18043
18714
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
18044
18715
  serviceName: mapping.service,
18045
- edgeType: import_types68.EdgeType.CALLS
18716
+ edgeType: import_types69.EdgeType.CALLS
18046
18717
  };
18047
18718
  }
18048
18719
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -18051,13 +18722,13 @@ function createCloudflareResolveTarget(config, graph) {
18051
18722
  return {
18052
18723
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
18053
18724
  serviceName: fileNode.service,
18054
- edgeType: import_types68.EdgeType.CALLS
18725
+ edgeType: import_types69.EdgeType.CALLS
18055
18726
  };
18056
18727
  }
18057
18728
  return {
18058
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18729
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
18059
18730
  serviceName: scriptName,
18060
- edgeType: import_types68.EdgeType.CALLS,
18731
+ edgeType: import_types69.EdgeType.CALLS,
18061
18732
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
18062
18733
  };
18063
18734
  };
@@ -18253,14 +18924,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
18253
18924
 
18254
18925
  // src/connectors/neon/resolve.ts
18255
18926
  init_cjs_shims();
18256
- var import_types72 = require("@neat.is/types");
18927
+ var import_types73 = require("@neat.is/types");
18257
18928
  function createNeonResolveTarget(config) {
18258
18929
  return (signal) => {
18259
18930
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18260
18931
  return {
18261
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18932
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
18262
18933
  serviceName: config.serviceName,
18263
- edgeType: import_types72.EdgeType.CALLS,
18934
+ edgeType: import_types73.EdgeType.CALLS,
18264
18935
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
18265
18936
  };
18266
18937
  };
@@ -18386,9 +19057,9 @@ function parseCloudRunTargetName(targetName) {
18386
19057
  const secondSep = rest.indexOf(FIELD_SEP2);
18387
19058
  if (secondSep === -1) return null;
18388
19059
  const method = rest.slice(0, secondSep);
18389
- const path74 = rest.slice(secondSep + 1);
18390
- if (!serviceName || !method || !path74) return null;
18391
- return { serviceName, method, path: path74 };
19060
+ const path76 = rest.slice(secondSep + 1);
19061
+ if (!serviceName || !method || !path76) return null;
19062
+ return { serviceName, method, path: path76 };
18392
19063
  }
18393
19064
 
18394
19065
  // src/connectors/cloud-run/map.ts
@@ -18417,14 +19088,14 @@ function mapLogEntryToSignal2(entry) {
18417
19088
  if (!req) return null;
18418
19089
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
18419
19090
  const method = req.requestMethod.toUpperCase();
18420
- const path74 = pathFromRequestUrl2(req.requestUrl);
18421
- if (path74 === null) return null;
19091
+ const path76 = pathFromRequestUrl2(req.requestUrl);
19092
+ if (path76 === null) return null;
18422
19093
  const timestamp = entry.timestamp;
18423
19094
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
18424
19095
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
18425
19096
  return {
18426
19097
  targetKind: CLOUD_RUN_TARGET_KIND,
18427
- targetName: packCloudRunTargetName({ serviceName, method, path: path74 }),
19098
+ targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
18428
19099
  callCount: 1,
18429
19100
  errorCount: isError ? 1 : 0,
18430
19101
  lastObservedIso: timestamp
@@ -18441,14 +19112,14 @@ function mapLogEntriesToSignals2(entries) {
18441
19112
 
18442
19113
  // src/connectors/cloud-run/resolve.ts
18443
19114
  init_cjs_shims();
18444
- var import_types76 = require("@neat.is/types");
19115
+ var import_types77 = require("@neat.is/types");
18445
19116
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18446
19117
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18447
19118
  let found = null;
18448
19119
  graph.forEachNode((_id, attrs) => {
18449
19120
  if (found) return;
18450
19121
  const node = attrs;
18451
- if (node.type !== import_types76.NodeType.RouteNode) return;
19122
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18452
19123
  const route = attrs;
18453
19124
  if (route.service !== serviceName || !route.pathTemplate) return;
18454
19125
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18463,23 +19134,23 @@ function createCloudRunResolveTarget(graph, config) {
18463
19134
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18464
19135
  const identity = parseCloudRunTargetName(signal.targetName);
18465
19136
  if (!identity) return null;
18466
- const { serviceName: gcpServiceName, method, path: path74 } = identity;
19137
+ const { serviceName: gcpServiceName, method, path: path76 } = identity;
18467
19138
  const mappedService = config.serviceMap?.[gcpServiceName];
18468
19139
  if (mappedService) {
18469
19140
  const routeNodeId = findMatchingRouteNode2(
18470
19141
  graph,
18471
19142
  mappedService,
18472
19143
  method,
18473
- normalizePathTemplate(path74)
19144
+ normalizePathTemplate(path76)
18474
19145
  );
18475
19146
  if (routeNodeId) {
18476
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
19147
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18477
19148
  }
18478
19149
  }
18479
19150
  return {
18480
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
19151
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18481
19152
  serviceName: mappedService ?? gcpServiceName,
18482
- edgeType: import_types76.EdgeType.CALLS,
19153
+ edgeType: import_types77.EdgeType.CALLS,
18483
19154
  ensureInfraNode: {
18484
19155
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18485
19156
  name: gcpServiceName,
@@ -18520,7 +19191,7 @@ function createCloudRunConnector(graph, config = {}) {
18520
19191
 
18521
19192
  // src/connectors/render/index.ts
18522
19193
  init_cjs_shims();
18523
- var import_types79 = require("@neat.is/types");
19194
+ var import_types80 = require("@neat.is/types");
18524
19195
 
18525
19196
  // src/connectors/render/types.ts
18526
19197
  init_cjs_shims();
@@ -18598,7 +19269,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18598
19269
  const out = [];
18599
19270
  graph.forEachNode((_id, attrs) => {
18600
19271
  const node = attrs;
18601
- if (node.type !== import_types79.NodeType.RouteNode) return;
19272
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18602
19273
  const route = attrs;
18603
19274
  if (route.service !== serviceName) return;
18604
19275
  out.push({
@@ -18683,7 +19354,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18683
19354
  function createRenderResolveTarget(config) {
18684
19355
  return (signal) => {
18685
19356
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18686
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
19357
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18687
19358
  }
18688
19359
  return null;
18689
19360
  };
@@ -18821,21 +19492,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18821
19492
 
18822
19493
  // src/connectors/planetscale/resolve.ts
18823
19494
  init_cjs_shims();
18824
- var import_types83 = require("@neat.is/types");
19495
+ var import_types84 = require("@neat.is/types");
18825
19496
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18826
19497
  function createPlanetscaleResolveTarget(graph, config) {
18827
19498
  const databaseName = `${config.organization}/${config.database}`;
18828
19499
  return (signal, _ctx) => {
18829
19500
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18830
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19501
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18831
19502
  if (graph.hasNode(tableId)) {
18832
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19503
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18833
19504
  }
18834
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19505
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18835
19506
  return {
18836
19507
  targetNodeId: providerId,
18837
19508
  serviceName: config.serviceName,
18838
- edgeType: import_types83.EdgeType.CALLS,
19509
+ edgeType: import_types84.EdgeType.CALLS,
18839
19510
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18840
19511
  };
18841
19512
  };
@@ -19100,7 +19771,7 @@ function mapBuildsToSignals(builds, serviceName) {
19100
19771
 
19101
19772
  // src/connectors/eas/resolve.ts
19102
19773
  init_cjs_shims();
19103
- var import_types88 = require("@neat.is/types");
19774
+ var import_types89 = require("@neat.is/types");
19104
19775
  var NO_ENV2 = "unknown";
19105
19776
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
19106
19777
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -19116,8 +19787,8 @@ function configBasenamesForPhase(phase) {
19116
19787
  function configNodeService(graph, configNodeId) {
19117
19788
  for (const edgeId of graph.inboundEdges(configNodeId)) {
19118
19789
  const edge = graph.getEdgeAttributes(edgeId);
19119
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
19120
- const parsed = (0, import_types88.parseFileId)(edge.source);
19790
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19791
+ const parsed = (0, import_types89.parseFileId)(edge.source);
19121
19792
  if (parsed) return parsed.service;
19122
19793
  }
19123
19794
  return null;
@@ -19128,7 +19799,7 @@ function findConfigNode(graph, basenames, serviceName) {
19128
19799
  graph.forEachNode((id, attrs) => {
19129
19800
  if (scoped) return;
19130
19801
  const node = attrs;
19131
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19802
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
19132
19803
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
19133
19804
  if (anyMatch === null) anyMatch = id;
19134
19805
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -19145,13 +19816,13 @@ function createEasResolveTarget(graph) {
19145
19816
  if (basenames.length > 0) {
19146
19817
  const configNodeId = findConfigNode(graph, basenames, serviceName);
19147
19818
  if (configNodeId) {
19148
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19819
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
19149
19820
  }
19150
19821
  }
19151
19822
  return {
19152
19823
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
19153
19824
  serviceName,
19154
- edgeType: import_types88.EdgeType.CALLS
19825
+ edgeType: import_types89.EdgeType.CALLS
19155
19826
  };
19156
19827
  };
19157
19828
  }
@@ -19876,11 +20547,11 @@ function registerRoutes(scope, ctx) {
19876
20547
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19877
20548
  const parsed = [];
19878
20549
  for (const c of candidates) {
19879
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20550
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19880
20551
  if (!r.success) {
19881
20552
  return reply.code(400).send({
19882
20553
  error: `unknown divergence type "${c}"`,
19883
- allowed: import_types91.DivergenceTypeSchema.options
20554
+ allowed: import_types92.DivergenceTypeSchema.options
19884
20555
  });
19885
20556
  }
19886
20557
  parsed.push(r.data);
@@ -20222,7 +20893,7 @@ function registerRoutes(scope, ctx) {
20222
20893
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
20223
20894
  let violations = await log.readAll();
20224
20895
  if (req.query.severity) {
20225
- const sev = import_types91.PolicySeveritySchema.safeParse(req.query.severity);
20896
+ const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
20226
20897
  if (!sev.success) {
20227
20898
  return reply.code(400).send({
20228
20899
  error: "invalid severity",
@@ -20261,7 +20932,7 @@ function registerRoutes(scope, ctx) {
20261
20932
  scope.post("/policies/check", async (req, reply) => {
20262
20933
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
20263
20934
  if (!proj) return;
20264
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20935
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20265
20936
  if (!parsed.success) {
20266
20937
  return reply.code(400).send({
20267
20938
  error: "invalid /policies/check body",
@@ -20582,16 +21253,16 @@ init_otel_grpc();
20582
21253
 
20583
21254
  // src/daemon.ts
20584
21255
  init_cjs_shims();
20585
- var import_node_fs39 = require("fs");
20586
- var import_node_path73 = __toESM(require("path"), 1);
21256
+ var import_node_fs40 = require("fs");
21257
+ var import_node_path75 = __toESM(require("path"), 1);
20587
21258
  var import_node_module = require("module");
20588
21259
  init_otel();
20589
21260
  init_auth();
20590
21261
 
20591
21262
  // src/unrouted.ts
20592
21263
  init_cjs_shims();
20593
- var import_node_fs38 = require("fs");
20594
- var import_node_path72 = __toESM(require("path"), 1);
21264
+ var import_node_fs39 = require("fs");
21265
+ var import_node_path74 = __toESM(require("path"), 1);
20595
21266
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
20596
21267
  return {
20597
21268
  timestamp: now.toISOString(),
@@ -20601,34 +21272,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
20601
21272
  };
20602
21273
  }
20603
21274
  async function appendUnroutedSpan(neatHome3, record) {
20604
- const target = import_node_path72.default.join(neatHome3, "errors.ndjson");
20605
- await import_node_fs38.promises.mkdir(neatHome3, { recursive: true });
20606
- await import_node_fs38.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
21275
+ const target = import_node_path74.default.join(neatHome3, "errors.ndjson");
21276
+ await import_node_fs39.promises.mkdir(neatHome3, { recursive: true });
21277
+ await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
20607
21278
  }
20608
21279
  function unroutedErrorsPath(neatHome3) {
20609
- return import_node_path72.default.join(neatHome3, "errors.ndjson");
21280
+ return import_node_path74.default.join(neatHome3, "errors.ndjson");
20610
21281
  }
20611
21282
 
20612
21283
  // src/daemon.ts
20613
- var import_types92 = require("@neat.is/types");
21284
+ var import_types93 = require("@neat.is/types");
20614
21285
  function daemonJsonPath(scanPath) {
20615
- return import_node_path73.default.join(scanPath, "neat-out", "daemon.json");
21286
+ return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
20616
21287
  }
20617
21288
  function daemonsDiscoveryDir(home) {
20618
21289
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
20619
- return import_node_path73.default.join(base, "daemons");
21290
+ return import_node_path75.default.join(base, "daemons");
20620
21291
  }
20621
21292
  function daemonDiscoveryPath(project, home) {
20622
- return import_node_path73.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
21293
+ return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
20623
21294
  }
20624
21295
  function sanitizeDiscoveryName(project) {
20625
21296
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
20626
21297
  }
20627
21298
  function neatHomeFromEnv() {
20628
21299
  const env = process.env.NEAT_HOME;
20629
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21300
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20630
21301
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20631
- return import_node_path73.default.join(home, ".neat");
21302
+ return import_node_path75.default.join(home, ".neat");
20632
21303
  }
20633
21304
  function resolveNeatVersion() {
20634
21305
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -20660,7 +21331,7 @@ async function clearDaemonRecord(record, home) {
20660
21331
  } catch {
20661
21332
  }
20662
21333
  try {
20663
- await import_node_fs39.promises.unlink(daemonDiscoveryPath(record.project, home));
21334
+ await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
20664
21335
  } catch {
20665
21336
  }
20666
21337
  }
@@ -20683,11 +21354,11 @@ function teardownSlot(slot) {
20683
21354
  }
20684
21355
  }
20685
21356
  function neatHomeFor(opts) {
20686
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path73.default.resolve(opts.neatHome);
21357
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
20687
21358
  const env = process.env.NEAT_HOME;
20688
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21359
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20689
21360
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20690
- return import_node_path73.default.join(home, ".neat");
21361
+ return import_node_path75.default.join(home, ".neat");
20691
21362
  }
20692
21363
  function routeSpanToProject(serviceName, projects) {
20693
21364
  if (!serviceName) return DEFAULT_PROJECT;
@@ -20735,13 +21406,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
20735
21406
  if (!serviceName) return true;
20736
21407
  if (serviceNameMatchesProject(serviceName, project)) return true;
20737
21408
  return graph.someNode(
20738
- (_id, attrs) => attrs.type === import_types92.NodeType.ServiceNode && attrs.name === serviceName
21409
+ (_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
20739
21410
  );
20740
21411
  }
20741
21412
  async function bootstrapProject(entry, connectors = [], neatHome3) {
20742
- const paths = pathsForProject(entry.name, import_node_path73.default.join(entry.path, "neat-out"));
21413
+ const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
20743
21414
  try {
20744
- const stat = await import_node_fs39.promises.stat(entry.path);
21415
+ const stat = await import_node_fs40.promises.stat(entry.path);
20745
21416
  if (!stat.isDirectory()) {
20746
21417
  throw new Error(`registered path ${entry.path} is not a directory`);
20747
21418
  }
@@ -20859,7 +21530,7 @@ async function startDaemon(opts = {}) {
20859
21530
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
20860
21531
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
20861
21532
  const singleProject = projectArg;
20862
- const singleProjectPath = singleProject && projectPathArg ? import_node_path73.default.resolve(projectPathArg) : null;
21533
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
20863
21534
  if (singleProject && !singleProjectPath) {
20864
21535
  throw new Error(
20865
21536
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -20867,14 +21538,14 @@ async function startDaemon(opts = {}) {
20867
21538
  }
20868
21539
  if (!singleProject) {
20869
21540
  try {
20870
- await import_node_fs39.promises.access(regPath);
21541
+ await import_node_fs40.promises.access(regPath);
20871
21542
  } catch {
20872
21543
  throw new Error(
20873
21544
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
20874
21545
  );
20875
21546
  }
20876
21547
  }
20877
- const pidPath = import_node_path73.default.join(home, "neatd.pid");
21548
+ const pidPath = import_node_path75.default.join(home, "neatd.pid");
20878
21549
  await writeAtomically(pidPath, `${process.pid}
20879
21550
  `);
20880
21551
  const slots = /* @__PURE__ */ new Map();
@@ -21086,7 +21757,7 @@ async function startDaemon(opts = {}) {
21086
21757
  }
21087
21758
  if (restApp) await restApp.close().catch(() => {
21088
21759
  });
21089
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21760
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21090
21761
  });
21091
21762
  throw new Error(
21092
21763
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -21254,7 +21925,7 @@ async function startDaemon(opts = {}) {
21254
21925
  });
21255
21926
  if (otlpApp) await otlpApp.close().catch(() => {
21256
21927
  });
21257
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21928
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21258
21929
  });
21259
21930
  throw new Error(
21260
21931
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -21289,7 +21960,7 @@ async function startDaemon(opts = {}) {
21289
21960
  });
21290
21961
  if (otlpApp) await otlpApp.close().catch(() => {
21291
21962
  });
21292
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21963
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21293
21964
  });
21294
21965
  throw new Error(
21295
21966
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -21336,9 +22007,9 @@ async function startDaemon(opts = {}) {
21336
22007
  let registryWatcher = null;
21337
22008
  let reloadTimer = null;
21338
22009
  if (!singleProject) try {
21339
- const regDir = import_node_path73.default.dirname(regPath);
21340
- const regBase = import_node_path73.default.basename(regPath);
21341
- registryWatcher = (0, import_node_fs39.watch)(regDir, (_eventType, filename) => {
22010
+ const regDir = import_node_path75.default.dirname(regPath);
22011
+ const regBase = import_node_path75.default.basename(regPath);
22012
+ registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
21342
22013
  if (filename !== null && filename !== regBase) return;
21343
22014
  if (reloadTimer) clearTimeout(reloadTimer);
21344
22015
  reloadTimer = setTimeout(() => {
@@ -21387,7 +22058,7 @@ async function startDaemon(opts = {}) {
21387
22058
  if (daemonRecord) {
21388
22059
  await clearDaemonRecord(daemonRecord, home);
21389
22060
  }
21390
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
22061
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21391
22062
  });
21392
22063
  };
21393
22064
  return {