@neat.is/core 0.9.1-dev.20260819 → 0.9.2-dev.20260821

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 }
@@ -525,11 +525,42 @@ async function decodeProtobufBody(buf) {
525
525
  const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
526
526
  return reshapeGrpcRequest2(decoded);
527
527
  }
528
+ function decompressorForEncoding(encoding) {
529
+ switch (encoding) {
530
+ case "gzip":
531
+ case "x-gzip":
532
+ return import_node_zlib.default.createGunzip();
533
+ case "deflate":
534
+ return import_node_zlib.default.createInflate();
535
+ default:
536
+ return null;
537
+ }
538
+ }
528
539
  async function buildOtelReceiver(opts) {
529
540
  const app = (0, import_fastify.default)({
530
541
  logger: false,
531
542
  bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
532
543
  });
544
+ app.addHook("preParsing", (req, _reply, payload, done) => {
545
+ const encoding = (req.headers["content-encoding"] ?? "").toString().trim().toLowerCase();
546
+ if (encoding === "" || encoding === "identity") {
547
+ done(null, payload);
548
+ return;
549
+ }
550
+ const decompressor = decompressorForEncoding(encoding);
551
+ if (!decompressor) {
552
+ done(null, payload);
553
+ return;
554
+ }
555
+ const tracked = decompressor;
556
+ tracked.receivedEncodedLength = 0;
557
+ payload.on("data", (chunk) => {
558
+ tracked.receivedEncodedLength = (tracked.receivedEncodedLength ?? 0) + chunk.length;
559
+ });
560
+ payload.on("error", (err) => decompressor.destroy(err));
561
+ payload.pipe(decompressor);
562
+ done(null, decompressor);
563
+ });
533
564
  const REJECT_WARN_INTERVAL_MS = 6e4;
534
565
  let lastRejectWarnAt = 0;
535
566
  const warnRejectedOtlp = () => {
@@ -735,13 +766,14 @@ function logSpanHandler(span) {
735
766
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
736
767
  );
737
768
  }
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;
769
+ var import_node_path53, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
739
770
  var init_otel = __esm({
740
771
  "src/otel.ts"() {
741
772
  "use strict";
742
773
  init_cjs_shims();
743
- import_node_path52 = __toESM(require("path"), 1);
774
+ import_node_path53 = __toESM(require("path"), 1);
744
775
  import_node_url2 = require("url");
776
+ import_node_zlib = __toESM(require("zlib"), 1);
745
777
  import_fastify = __toESM(require("fastify"), 1);
746
778
  import_protobufjs = __toESM(require("protobufjs"), 1);
747
779
  init_auth();
@@ -1333,19 +1365,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1333
1365
  function longestIncomingWalk(graph, start, maxDepth) {
1334
1366
  let best = { path: [start], edges: [] };
1335
1367
  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] };
1368
+ function step(node, path76, edges) {
1369
+ if (path76.length > best.path.length) {
1370
+ best = { path: [...path76], edges: [...edges] };
1339
1371
  }
1340
- if (path74.length - 1 >= maxDepth) return;
1372
+ if (path76.length - 1 >= maxDepth) return;
1341
1373
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1342
1374
  for (const [srcId, edge] of incoming) {
1343
1375
  if (visited.has(srcId)) continue;
1344
1376
  visited.add(srcId);
1345
- path74.push(srcId);
1377
+ path76.push(srcId);
1346
1378
  edges.push(edge);
1347
- step(srcId, path74, edges);
1348
- path74.pop();
1379
+ step(srcId, path76, edges);
1380
+ path76.pop();
1349
1381
  edges.pop();
1350
1382
  visited.delete(srcId);
1351
1383
  }
@@ -1353,11 +1385,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1353
1385
  step(start, [start], []);
1354
1386
  return best;
1355
1387
  }
1356
- function databaseRootCauseShape(graph, origin, walk9) {
1388
+ function databaseRootCauseShape(graph, origin, walk10) {
1357
1389
  const targetDb = origin;
1358
1390
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1359
1391
  if (candidatePairs.length === 0) return null;
1360
- for (const id of walk9.path) {
1392
+ for (const id of walk10.path) {
1361
1393
  const owner = resolveOwningService(graph, id);
1362
1394
  if (!owner) continue;
1363
1395
  const { id: serviceId15, svc } = owner;
@@ -1384,8 +1416,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
1384
1416
  }
1385
1417
  return null;
1386
1418
  }
1387
- function serviceRootCauseShape(graph, _origin, walk9) {
1388
- for (const id of walk9.path) {
1419
+ function serviceRootCauseShape(graph, _origin, walk10) {
1420
+ for (const id of walk10.path) {
1389
1421
  const owner = resolveOwningService(graph, id);
1390
1422
  if (!owner) continue;
1391
1423
  const { id: serviceId15, svc } = owner;
@@ -1421,15 +1453,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
1421
1453
  }
1422
1454
  return null;
1423
1455
  }
1424
- function fileRootCauseShape(graph, origin, walk9) {
1456
+ function fileRootCauseShape(graph, origin, walk10) {
1425
1457
  const owner = resolveOwningService(graph, origin.id);
1426
1458
  if (!owner) return null;
1427
- return serviceRootCauseShape(graph, owner.svc, walk9);
1459
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1428
1460
  }
1429
- function symbolRootCauseShape(graph, origin, walk9) {
1461
+ function symbolRootCauseShape(graph, origin, walk10) {
1430
1462
  const owner = resolveOwningService(graph, origin.id);
1431
1463
  if (!owner) return null;
1432
- return serviceRootCauseShape(graph, owner.svc, walk9);
1464
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1433
1465
  }
1434
1466
  var rootCauseShapes = {
1435
1467
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1442,25 +1474,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1442
1474
  const origin = graph.getNodeAttributes(errorNodeId);
1443
1475
  const shape = rootCauseShapes[origin.type];
1444
1476
  if (shape) {
1445
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1446
- const match = shape(graph, origin, walk9);
1477
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1478
+ const match = shape(graph, origin, walk10);
1447
1479
  if (match) {
1448
1480
  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
- });
1481
+ return {
1482
+ source: "compat",
1483
+ result: import_types.RootCauseResultSchema.parse({
1484
+ rootCauseNode: match.rootCauseNode,
1485
+ rootCauseReason: reason,
1486
+ traversalPath: walk10.path,
1487
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
1488
+ confidence: confidenceFromMix(walk10.edges),
1489
+ fixRecommendation: match.fixRecommendation
1490
+ })
1491
+ };
1457
1492
  }
1458
1493
  }
1459
1494
  if (origin.type === import_types.NodeType.ServiceNode) {
1460
1495
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
1461
- if (crossService) return crossService;
1496
+ if (crossService) return { result: crossService, source: "cross-service" };
1462
1497
  }
1463
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1498
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1499
+ return incident ? { result: incident, source: "incident" } : null;
1464
1500
  }
1465
1501
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
1466
1502
  function incidentMatchesNode(ev, nodeId) {
@@ -1556,26 +1592,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
1556
1592
  return best;
1557
1593
  }
1558
1594
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1559
- const path74 = [originServiceId];
1595
+ const path76 = [originServiceId];
1560
1596
  const edges = [];
1561
1597
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1562
1598
  let current = originServiceId;
1563
1599
  for (let depth = 0; depth < maxDepth; depth++) {
1564
1600
  const hop = dominantFailingCall(graph, current, visited);
1565
1601
  if (!hop) break;
1566
- path74.push(hop.nextService);
1602
+ path76.push(hop.nextService);
1603
+ edges.push(hop.edge);
1604
+ visited.add(hop.nextService);
1605
+ current = hop.nextService;
1606
+ }
1607
+ if (edges.length === 0) return null;
1608
+ return { path: path76, edges, culprit: current };
1609
+ }
1610
+ function isStaleCallEdge(e) {
1611
+ return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
1612
+ }
1613
+ function staleCallDominates(e, id, curEdge, curId) {
1614
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
1615
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
1616
+ if (ev !== cv) return ev > cv;
1617
+ return id < curId;
1618
+ }
1619
+ function dominantStaleCall(graph, serviceId15, visited) {
1620
+ const bestByCallee = /* @__PURE__ */ new Map();
1621
+ for (const src of callSourcesForService(graph, serviceId15)) {
1622
+ for (const edgeId of graph.outboundEdges(src)) {
1623
+ const e = graph.getEdgeAttributes(edgeId);
1624
+ if (e.type !== import_types.EdgeType.CALLS) continue;
1625
+ if (isFrontierNode(graph, e.target)) continue;
1626
+ const owner = resolveOwningService(graph, e.target);
1627
+ if (!owner || visited.has(owner.id)) continue;
1628
+ const cur = bestByCallee.get(owner.id);
1629
+ if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
1630
+ bestByCallee.set(owner.id, e);
1631
+ }
1632
+ }
1633
+ }
1634
+ let best = null;
1635
+ for (const [id, edge] of bestByCallee) {
1636
+ if (!isStaleCallEdge(edge)) continue;
1637
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
1638
+ best = { nextService: id, edge };
1639
+ }
1640
+ }
1641
+ return best;
1642
+ }
1643
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
1644
+ const path76 = [originServiceId];
1645
+ const edges = [];
1646
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
1647
+ let current = originServiceId;
1648
+ for (let depth = 0; depth < maxDepth; depth++) {
1649
+ const hop = dominantStaleCall(graph, current, visited);
1650
+ if (!hop) break;
1651
+ path76.push(hop.nextService);
1567
1652
  edges.push(hop.edge);
1568
1653
  visited.add(hop.nextService);
1569
1654
  current = hop.nextService;
1570
1655
  }
1571
1656
  if (edges.length === 0) return null;
1572
- return { path: path74, edges, culprit: current };
1657
+ return { path: path76, edges, culprit: current };
1573
1658
  }
1574
1659
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1575
1660
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1576
1661
  if (!chain) return null;
1577
1662
  const culprit = chain.culprit;
1578
- const path74 = [...chain.path];
1663
+ const path76 = [...chain.path];
1579
1664
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1580
1665
  const baseConfidence = confidenceFromMix(chain.edges);
1581
1666
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1583,14 +1668,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1583
1668
  if (loc) {
1584
1669
  let rootCauseNode = culprit;
1585
1670
  if (loc.fileNode) {
1586
- path74.push(loc.fileNode);
1671
+ path76.push(loc.fileNode);
1587
1672
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1588
1673
  rootCauseNode = loc.fileNode;
1589
1674
  }
1590
1675
  return import_types.RootCauseResultSchema.parse({
1591
1676
  rootCauseNode,
1592
1677
  rootCauseReason: loc.rootCauseReason,
1593
- traversalPath: path74,
1678
+ traversalPath: path76,
1594
1679
  edgeProvenances,
1595
1680
  confidence,
1596
1681
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1602,7 +1687,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1602
1687
  return import_types.RootCauseResultSchema.parse({
1603
1688
  rootCauseNode: culprit,
1604
1689
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1605
- traversalPath: path74,
1690
+ traversalPath: path76,
1606
1691
  edgeProvenances,
1607
1692
  confidence,
1608
1693
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -1989,17 +2074,20 @@ function displayNameOf(nodeId) {
1989
2074
  return nodeId.replace(/^[a-z]+:/, "");
1990
2075
  }
1991
2076
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
1992
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
1993
- if (!legacy) return null;
2077
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
2078
+ if (!tagged) return null;
1994
2079
  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());
2080
+ if (!navigation) return tagged.result;
2081
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
1997
2082
  }
1998
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2083
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2084
+ const legacy = tagged.result;
1999
2085
  const seedNode = legacy.rootCauseNode;
2000
2086
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
2001
2087
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
2002
2088
  const candidates = [];
2089
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
2090
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
2003
2091
  if (seedCtx && isVictimSeed(seedCtx)) {
2004
2092
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
2005
2093
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -2024,6 +2112,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2024
2112
  confidence: Math.min(legacy.confidence, 0.4),
2025
2113
  ...lastProv ? { provenance: lastProv } : {}
2026
2114
  });
2115
+ } else if (staleChain) {
2116
+ const culprit = staleChain.culprit;
2117
+ const culpritName = displayNameOf(culprit);
2118
+ const seedName = displayNameOf(seedNode);
2119
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
2120
+ candidates.push({
2121
+ node: culprit,
2122
+ classification: "primary-failure",
2123
+ 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.`,
2124
+ context: nodeContext(graph, culprit, incidents, now),
2125
+ confidence: staleConfidence,
2126
+ provenance: import_types.Provenance.STALE
2127
+ });
2128
+ candidates.push({
2129
+ node: seedNode,
2130
+ classification: "symptom-only",
2131
+ 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.`,
2132
+ context: seedCtx ?? EMPTY_CONTEXT,
2133
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
2134
+ ...lastProv ? { provenance: lastProv } : {}
2135
+ });
2027
2136
  } else {
2028
2137
  candidates.push({
2029
2138
  node: seedNode,
@@ -2037,11 +2146,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2037
2146
  const top = candidates[0];
2038
2147
  let traversalPath = legacy.traversalPath;
2039
2148
  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);
2149
+ if (staleChain && top.node === staleChain.culprit) {
2150
+ traversalPath = staleChain.path;
2151
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
2152
+ } else if (top.node !== seedNode) {
2153
+ const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2154
+ if (path76) {
2155
+ traversalPath = path76.nodes;
2156
+ edgeProvenances = path76.edges.map((e) => e.provenance);
2045
2157
  } else {
2046
2158
  traversalPath = [errorNodeId, top.node];
2047
2159
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2063,6 +2175,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
2063
2175
  return legacy.fixRecommendation;
2064
2176
  }
2065
2177
  const name = top.node.replace(/^service:/, "");
2178
+ if (top.provenance === import_types.Provenance.STALE) {
2179
+ 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}.`;
2180
+ }
2066
2181
  if (top.classification === "primary-failure") {
2067
2182
  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
2183
  }
@@ -2581,6 +2696,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2581
2696
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2582
2697
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2583
2698
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2699
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2584
2700
  var import_types6 = require("@neat.is/types");
2585
2701
 
2586
2702
  // src/extract/shared.ts
@@ -2861,7 +2977,7 @@ function buildServiceHostIndex(services) {
2861
2977
  async function walkSourceFiles(dir, excludeDirs = []) {
2862
2978
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
2863
2979
  const out = [];
2864
- async function walk9(current) {
2980
+ async function walk10(current) {
2865
2981
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2866
2982
  for (const entry of entries) {
2867
2983
  const full = import_node_path5.default.join(current, entry.name);
@@ -2869,7 +2985,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2869
2985
  if (IGNORED_DIRS.has(entry.name)) continue;
2870
2986
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
2871
2987
  if (await isPythonVenvDir(full)) continue;
2872
- await walk9(full);
2988
+ await walk10(full);
2873
2989
  } 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
2990
  // would attribute our instrumentation imports to the user's service.
2875
2991
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2877,7 +2993,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2877
2993
  }
2878
2994
  }
2879
2995
  }
2880
- await walk9(dir);
2996
+ await walk10(dir);
2881
2997
  return out;
2882
2998
  }
2883
2999
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3397,6 +3513,11 @@ function makePhpParser() {
3397
3513
  p.setLanguage(import_tree_sitter_php.default.php_only);
3398
3514
  return p;
3399
3515
  }
3516
+ function makeRustParser() {
3517
+ const p = new import_tree_sitter2.default();
3518
+ p.setLanguage(import_tree_sitter_rust.default);
3519
+ return p;
3520
+ }
3400
3521
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
3401
3522
  "get",
3402
3523
  "post",
@@ -3468,8 +3589,8 @@ function chiRoutesFromSource(source, parser) {
3468
3589
  chiWalk(tree.rootNode, "", out);
3469
3590
  return out;
3470
3591
  }
3471
- function stripChiRegex(path74) {
3472
- return path74.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3592
+ function stripChiRegex(path76) {
3593
+ return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3473
3594
  }
3474
3595
  function chiWalk(node, prefix, out) {
3475
3596
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4141,9 +4262,9 @@ function rubyRocketRoute(args) {
4141
4262
  if (!pair || pair.type !== "pair") continue;
4142
4263
  const k = pair.childForFieldName("key");
4143
4264
  if (k?.type !== "string") continue;
4144
- const path74 = rubyLiteral(k);
4145
- if (path74 === null) continue;
4146
- return { path: path74, target: rubyLiteral(pair.childForFieldName("value")) };
4265
+ const path76 = rubyLiteral(k);
4266
+ if (path76 === null) continue;
4267
+ return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
4147
4268
  }
4148
4269
  return null;
4149
4270
  }
@@ -4313,6 +4434,48 @@ function railsRoutesFromSource(source, parser) {
4313
4434
  });
4314
4435
  return out;
4315
4436
  }
4437
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4438
+ function sinatraRoutesFromSource(source, parser) {
4439
+ const tree = parseSource2(parser, source);
4440
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
4441
+ const out = [];
4442
+ walk(tree.rootNode, (node) => {
4443
+ if (node.type !== "call") return;
4444
+ if (node.childForFieldName("receiver")) return;
4445
+ const method = node.childForFieldName("method")?.text;
4446
+ if (!method || !SINATRA_VERBS.has(method)) return;
4447
+ if (!node.childForFieldName("block")) return;
4448
+ const first = node.childForFieldName("arguments")?.namedChild(0);
4449
+ if (first?.type !== "string") return;
4450
+ const p = rubyLiteral(first);
4451
+ if (p === null || !p.startsWith("/")) return;
4452
+ out.push({
4453
+ method: method.toUpperCase(),
4454
+ pathTemplate: canonicalizeTemplate(p),
4455
+ line: node.startPosition.row + 1,
4456
+ framework: "sinatra"
4457
+ });
4458
+ });
4459
+ return out;
4460
+ }
4461
+ function fileReferencesSinatra(root) {
4462
+ let found = false;
4463
+ walk(root, (node) => {
4464
+ if (found) return;
4465
+ if (node.type === "call") {
4466
+ const m = node.childForFieldName("method")?.text;
4467
+ if (m === "require" || m === "require_relative") {
4468
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
4469
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
4470
+ }
4471
+ return;
4472
+ }
4473
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
4474
+ found = true;
4475
+ }
4476
+ });
4477
+ return found;
4478
+ }
4316
4479
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
4317
4480
  var LARAVEL_RESOURCE_ROWS = [
4318
4481
  { action: "index", methods: ["GET"], suffix: "" },
@@ -4488,6 +4651,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
4488
4651
  }
4489
4652
  return out;
4490
4653
  }
4654
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4655
+ function isSlimAppCtor(node) {
4656
+ if (!node) return false;
4657
+ if (node.type === "scoped_call_expression") {
4658
+ const scope = node.childForFieldName("scope")?.text ?? "";
4659
+ const name = node.childForFieldName("name")?.text;
4660
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
4661
+ }
4662
+ if (node.type === "object_creation_expression") {
4663
+ const cls = node.namedChild(0)?.text ?? "";
4664
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
4665
+ }
4666
+ return false;
4667
+ }
4668
+ function isSlimAppType(node) {
4669
+ if (!node || node.type !== "named_type") return false;
4670
+ const text = node.text;
4671
+ return text === "App" || text.endsWith("\\App");
4672
+ }
4673
+ function collectSlimAppVars(root) {
4674
+ const vars = /* @__PURE__ */ new Set();
4675
+ walk(root, (node) => {
4676
+ if (node.type === "assignment_expression") {
4677
+ const left = node.childForFieldName("left");
4678
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
4679
+ vars.add(left.text);
4680
+ }
4681
+ return;
4682
+ }
4683
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
4684
+ const name = node.childForFieldName("name");
4685
+ if (name?.type === "variable_name") vars.add(name.text);
4686
+ }
4687
+ });
4688
+ return vars;
4689
+ }
4690
+ function slimClosureParamVars(closure) {
4691
+ const out = ["$this"];
4692
+ for (let i = 0; i < closure.namedChildCount; i++) {
4693
+ const params = closure.namedChild(i);
4694
+ if (params?.type !== "formal_parameters") continue;
4695
+ for (let j = 0; j < params.namedChildCount; j++) {
4696
+ const param = params.namedChild(j);
4697
+ if (param?.type !== "simple_parameter") continue;
4698
+ for (let k = 0; k < param.namedChildCount; k++) {
4699
+ const v = param.namedChild(k);
4700
+ if (v?.type === "variable_name") {
4701
+ out.push(v.text);
4702
+ break;
4703
+ }
4704
+ }
4705
+ }
4706
+ }
4707
+ return out;
4708
+ }
4709
+ function phpStringArray(node) {
4710
+ const out = [];
4711
+ if (node?.type !== "array_creation_expression") return out;
4712
+ for (let i = 0; i < node.namedChildCount; i++) {
4713
+ const el = node.namedChild(i);
4714
+ if (el?.type !== "array_element_initializer") continue;
4715
+ const s = phpStaticString(el.namedChild(0));
4716
+ if (s !== null) out.push(s);
4717
+ }
4718
+ return out;
4719
+ }
4720
+ function slimRoutesFromSource(source, parser) {
4721
+ const tree = parseSource2(parser, source);
4722
+ const appVars = collectSlimAppVars(tree.rootNode);
4723
+ if (appVars.size === 0) return [];
4724
+ const out = [];
4725
+ slimWalk(tree.rootNode, "", appVars, out);
4726
+ return out;
4727
+ }
4728
+ function slimWalk(node, prefix, appVars, out) {
4729
+ for (let i = 0; i < node.namedChildCount; i++) {
4730
+ const child = node.namedChild(i);
4731
+ if (child) slimHandle(child, prefix, appVars, out);
4732
+ }
4733
+ }
4734
+ function slimHandle(node, prefix, appVars, out) {
4735
+ if (node.type === "member_call_expression") {
4736
+ const obj = node.childForFieldName("object");
4737
+ const method = node.childForFieldName("name")?.text;
4738
+ const args = node.childForFieldName("arguments");
4739
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4740
+ const line = node.startPosition.row + 1;
4741
+ if (method === "group") {
4742
+ const groupPrefix = phpFirstString(args);
4743
+ const closure = laravelGroupClosure(args);
4744
+ if (groupPrefix !== null && closure) {
4745
+ const inner = new Set(appVars);
4746
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4747
+ const body = closure.childForFieldName("body");
4748
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4749
+ }
4750
+ return;
4751
+ }
4752
+ if (SLIM_VERBS.has(method)) {
4753
+ const p = phpFirstString(args);
4754
+ if (p !== null) {
4755
+ out.push({
4756
+ method: method.toUpperCase(),
4757
+ pathTemplate: laravelJoinPath(prefix, p),
4758
+ line,
4759
+ framework: "slim"
4760
+ });
4761
+ }
4762
+ return;
4763
+ }
4764
+ if (method === "any") {
4765
+ const p = phpFirstString(args);
4766
+ if (p !== null) {
4767
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4768
+ }
4769
+ return;
4770
+ }
4771
+ if (method === "map") {
4772
+ const vals = phpArgumentValues(args);
4773
+ const methods = phpStringArray(vals[0]);
4774
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4775
+ if (p !== null) {
4776
+ for (const m of methods) {
4777
+ out.push({
4778
+ method: m.toUpperCase(),
4779
+ pathTemplate: laravelJoinPath(prefix, p),
4780
+ line,
4781
+ framework: "slim"
4782
+ });
4783
+ }
4784
+ }
4785
+ return;
4786
+ }
4787
+ }
4788
+ }
4789
+ slimWalk(node, prefix, appVars, out);
4790
+ }
4791
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4792
+ function rustStringContent(node) {
4793
+ if (!node || node.type !== "string_literal") return null;
4794
+ for (let i = 0; i < node.namedChildCount; i++) {
4795
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4796
+ }
4797
+ return "";
4798
+ }
4799
+ function actixRoutesFromSource(source, parser) {
4800
+ const tree = parseSource2(parser, source);
4801
+ const out = [];
4802
+ walk(tree.rootNode, (node) => {
4803
+ if (node.type === "attribute_item") {
4804
+ actixAttributeRoute(node, out);
4805
+ return;
4806
+ }
4807
+ if (node.type === "call_expression") {
4808
+ actixBuilderRoute(node, out);
4809
+ }
4810
+ });
4811
+ return out;
4812
+ }
4813
+ function actixAttributeRoute(attrItem, out) {
4814
+ const attr = attrItem.namedChild(0);
4815
+ if (!attr || attr.type !== "attribute") return;
4816
+ const nameNode = attr.namedChild(0);
4817
+ if (!nameNode) return;
4818
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4819
+ if (!macro) return;
4820
+ const tokens = attr.childForFieldName("arguments");
4821
+ if (!tokens || tokens.type !== "token_tree") return;
4822
+ const strings = [];
4823
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4824
+ const s = rustStringContent(tokens.namedChild(i));
4825
+ if (s !== null) strings.push(s);
4826
+ }
4827
+ const pathStr = strings[0];
4828
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4829
+ const line = attrItem.startPosition.row + 1;
4830
+ const template = canonicalizeTemplate(pathStr);
4831
+ if (ACTIX_METHODS.has(macro)) {
4832
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4833
+ return;
4834
+ }
4835
+ if (macro === "route") {
4836
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4837
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4838
+ for (const m of list) {
4839
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4840
+ }
4841
+ }
4842
+ }
4843
+ function actixBuilderRoute(call, out) {
4844
+ const fn = call.childForFieldName("function");
4845
+ if (fn?.type !== "field_expression") return;
4846
+ if (fn.childForFieldName("field")?.text !== "route") return;
4847
+ const args = call.childForFieldName("arguments");
4848
+ const pathStr = rustStringContent(args?.namedChild(0));
4849
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4850
+ const second = args?.namedChild(1);
4851
+ if (!second) return;
4852
+ const method = actixBuilderMethod(second);
4853
+ if (!method) return;
4854
+ out.push({
4855
+ method,
4856
+ pathTemplate: canonicalizeTemplate(pathStr),
4857
+ line: call.startPosition.row + 1,
4858
+ framework: "actix-web"
4859
+ });
4860
+ }
4861
+ function actixBuilderMethod(node) {
4862
+ let method = null;
4863
+ walk(node, (n) => {
4864
+ if (method || n.type !== "scoped_identifier") return;
4865
+ const verb = n.childForFieldName("name")?.text;
4866
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4867
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4868
+ });
4869
+ return method;
4870
+ }
4491
4871
  function namedArgs(argsNode) {
4492
4872
  const out = [];
4493
4873
  if (!argsNode) return out;
@@ -4804,6 +5184,7 @@ async function addRoutes(graph, services) {
4804
5184
  const goParser = makeGoParser2();
4805
5185
  const rubyParser = makeRubyParser();
4806
5186
  const phpParser = makePhpParser();
5187
+ const rustParser = makeRustParser();
4807
5188
  let nodesAdded = 0;
4808
5189
  let edgesAdded = 0;
4809
5190
  for (const service of services) {
@@ -4826,7 +5207,10 @@ async function addRoutes(graph, services) {
4826
5207
  const isGoService = service.node.language === "go";
4827
5208
  const hasRails = deps["rails"] !== void 0;
4828
5209
  const hasLaravel = deps["laravel/framework"] !== void 0;
4829
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
5210
+ const hasSlim = deps["slim/slim"] !== void 0;
5211
+ const hasSinatra = deps["sinatra"] !== void 0;
5212
+ const hasActix = deps["actix-web"] !== void 0;
5213
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4830
5214
  continue;
4831
5215
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4832
5216
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4837,7 +5221,8 @@ async function addRoutes(graph, services) {
4837
5221
  const isGo = ext === ".go";
4838
5222
  const isRb = ext === ".rb";
4839
5223
  const isPhp = ext === ".php";
4840
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
5224
+ const isRs = ext === ".rs";
5225
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4841
5226
  const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
4842
5227
  let routes;
4843
5228
  try {
@@ -4847,8 +5232,12 @@ async function addRoutes(graph, services) {
4847
5232
  phpParser,
4848
5233
  relFile === "routes/api.php" ? "/api" : ""
4849
5234
  ) : [];
5235
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4850
5236
  } else if (isRb) {
4851
5237
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
5238
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
5239
+ } else if (isRs) {
5240
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4852
5241
  } else if (isGo) {
4853
5242
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4854
5243
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -5083,6 +5472,37 @@ function loadIncidentThresholdsFromEnv() {
5083
5472
  return DEFAULT_INCIDENT_THRESHOLDS;
5084
5473
  }
5085
5474
  }
5475
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
5476
+ function latencyStreamCeilingMs() {
5477
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
5478
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
5479
+ const n = Number(raw);
5480
+ if (Number.isFinite(n) && n > 0) return n;
5481
+ console.warn(
5482
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
5483
+ );
5484
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
5485
+ }
5486
+ function spanServesEventStream(attrs) {
5487
+ for (const key of [
5488
+ "http.response.header.content-type",
5489
+ "http.response.header.content_type"
5490
+ ]) {
5491
+ const v = attrs[key];
5492
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
5493
+ for (const item of values) {
5494
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
5495
+ return true;
5496
+ }
5497
+ }
5498
+ }
5499
+ return false;
5500
+ }
5501
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
5502
+ if (span.websocketChannel !== void 0) return true;
5503
+ if (spanServesEventStream(span.attributes)) return true;
5504
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
5505
+ }
5086
5506
  function httpResponseStatusFromAttrs(attrs) {
5087
5507
  for (const key of ["http.response.status_code", "http.status_code"]) {
5088
5508
  const v = attrs[key];
@@ -5134,6 +5554,14 @@ function grpcStatusCodeFromAttrs(attrs) {
5134
5554
  }
5135
5555
  return void 0;
5136
5556
  }
5557
+ function spanRecordsError(span) {
5558
+ if (span.statusCode === 2) return true;
5559
+ const grpc2 = grpcStatusCodeFromAttrs(span.attributes);
5560
+ if (grpc2 !== void 0 && grpc2 !== 0) return true;
5561
+ const httpStatus = httpResponseStatusFromAttrs(span.attributes);
5562
+ if (httpStatus !== void 0 && httpStatus >= 500) return true;
5563
+ return false;
5564
+ }
5137
5565
  function nonHttpFailureMessageFromAttrs(attrs) {
5138
5566
  const grpc2 = grpcStatusCodeFromAttrs(attrs);
5139
5567
  if (grpc2 !== void 0 && grpc2 !== 0) {
@@ -5932,6 +6360,21 @@ async function recordExceptionIncident(ctx, span, ts) {
5932
6360
  };
5933
6361
  await appendErrorEvent(ctx, ev);
5934
6362
  }
6363
+ async function recordGrpcFailureIncident(ctx, span, ts) {
6364
+ const attrs = sanitizeAttributes(span.attributes);
6365
+ const ev = {
6366
+ id: `${span.traceId}:${span.spanId}`,
6367
+ timestamp: ts,
6368
+ service: span.service,
6369
+ traceId: span.traceId,
6370
+ spanId: span.spanId,
6371
+ errorType: "grpc-failure",
6372
+ errorMessage: incidentMessage(span),
6373
+ ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
6374
+ affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath)
6375
+ };
6376
+ await appendErrorEvent(ctx, ev);
6377
+ }
5935
6378
  async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5936
6379
  const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
5937
6380
  if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
@@ -5975,6 +6418,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5975
6418
  );
5976
6419
  ctx.burstState.delete(key);
5977
6420
  }
6421
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
6422
+ function nextApiRouteTemplate(span) {
6423
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
6424
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
6425
+ return match ? match[1] : void 0;
6426
+ }
5978
6427
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5979
6428
  const target = normalizePathTemplate(httpRoute);
5980
6429
  const m = method?.toUpperCase();
@@ -5996,8 +6445,8 @@ async function handleSpan(ctx, span) {
5996
6445
  warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT);
5997
6446
  }
5998
6447
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5999
- const isError = span.statusCode === 2;
6000
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
6448
+ const isError = spanRecordsError(span);
6449
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
6001
6450
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
6002
6451
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
6003
6452
  cacheSpanService(span, nowMs, callSite);
@@ -6194,12 +6643,13 @@ async function handleSpan(ctx, span) {
6194
6643
  }
6195
6644
  }
6196
6645
  }
6197
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6646
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
6647
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6198
6648
  const routeNodeId = findRouteNodeByHttpRoute(
6199
6649
  ctx.graph,
6200
6650
  span.service,
6201
6651
  span.httpMethod,
6202
- span.httpRoute
6652
+ fusionRoute
6203
6653
  );
6204
6654
  if (routeNodeId) {
6205
6655
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -6231,10 +6681,13 @@ async function handleSpan(ctx, span) {
6231
6681
  }
6232
6682
  if (span.statusCode !== 2) {
6233
6683
  const status2 = httpResponseStatus(span);
6684
+ const grpcStatus = grpcStatusCodeFromAttrs(span.attributes);
6234
6685
  if (span.exception) {
6235
6686
  await recordExceptionIncident(ctx, span, ts);
6236
6687
  } else if (status2 !== void 0 && status2 >= 500) {
6237
6688
  await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
6689
+ } else if (grpcStatus !== void 0 && grpcStatus !== 0) {
6690
+ await recordGrpcFailureIncident(ctx, span, ts);
6238
6691
  } else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
6239
6692
  await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
6240
6693
  }
@@ -7590,7 +8043,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
7590
8043
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
7591
8044
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
7592
8045
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
7593
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
8046
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
7594
8047
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
7595
8048
  var import_types20 = require("@neat.is/types");
7596
8049
  var PARSE_CHUNK3 = 16384;
@@ -7611,7 +8064,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
7611
8064
  ".cs": import_tree_sitter_c_sharp.default,
7612
8065
  ".java": import_tree_sitter_java.default,
7613
8066
  ".kt": import_tree_sitter_kotlin.default,
7614
- ".rs": import_tree_sitter_rust.default,
8067
+ ".rs": import_tree_sitter_rust2.default,
7615
8068
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
7616
8069
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
7617
8070
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8051,15 +8504,15 @@ function collectKotlinSymbolDefs(root) {
8051
8504
  });
8052
8505
  };
8053
8506
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8054
- const firstChildOfType = (node, types) => {
8507
+ const firstChildOfType2 = (node, types) => {
8055
8508
  for (let i = 0; i < node.namedChildCount; i++) {
8056
8509
  const child = node.namedChild(i);
8057
8510
  if (child && types.includes(child.type)) return child;
8058
8511
  }
8059
8512
  return void 0;
8060
8513
  };
8061
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8062
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
8514
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
8515
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8063
8516
  let pkg;
8064
8517
  for (let i = 0; i < root.namedChildCount; i++) {
8065
8518
  const child = root.namedChild(i);
@@ -8560,7 +9013,7 @@ async function addSymbolEdges(graph, services) {
8560
9013
  return best;
8561
9014
  };
8562
9015
  const requests = [];
8563
- const walk9 = (node) => {
9016
+ const walk10 = (node) => {
8564
9017
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
8565
9018
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
8566
9019
  if (self && self.kind === "class") {
@@ -8606,10 +9059,10 @@ async function addSymbolEdges(graph, services) {
8606
9059
  }
8607
9060
  for (let i = 0; i < node.namedChildCount; i++) {
8608
9061
  const child = node.namedChild(i);
8609
- if (child) walk9(child);
9062
+ if (child) walk10(child);
8610
9063
  }
8611
9064
  };
8612
- walk9(root);
9065
+ walk10(root);
8613
9066
  for (const req of requests) {
8614
9067
  const targetSid = resolveTarget(req.targetName, req.wantKind);
8615
9068
  if (!targetSid) continue;
@@ -8905,7 +9358,7 @@ async function addServerActions(graph, services) {
8905
9358
 
8906
9359
  // src/extract/databases/index.ts
8907
9360
  init_cjs_shims();
8908
- var import_node_path33 = __toESM(require("path"), 1);
9361
+ var import_node_path34 = __toESM(require("path"), 1);
8909
9362
  var import_types23 = require("@neat.is/types");
8910
9363
 
8911
9364
  // src/extract/databases/db-config-yaml.ts
@@ -9340,9 +9793,163 @@ async function parse8(serviceDir) {
9340
9793
  }
9341
9794
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9342
9795
 
9343
- // src/extract/databases/docker-compose.ts
9796
+ // src/extract/databases/csharp.ts
9344
9797
  init_cjs_shims();
9798
+ var import_node_fs22 = require("fs");
9345
9799
  var import_node_path32 = __toESM(require("path"), 1);
9800
+ var CS_EXT = ".cs";
9801
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
9802
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
9803
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
9804
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
9805
+ function hostIsUnresolved(host) {
9806
+ return host === "" || /[${}]/.test(host);
9807
+ }
9808
+ function looksLikePostgres(s) {
9809
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
9810
+ }
9811
+ function looksLikeRedis(s) {
9812
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
9813
+ }
9814
+ function parsePostgresConnection(raw) {
9815
+ const s = raw.trim();
9816
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
9817
+ const fields = /* @__PURE__ */ new Map();
9818
+ for (const part of s.split(";")) {
9819
+ const eq = part.indexOf("=");
9820
+ if (eq < 0) continue;
9821
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
9822
+ const value = part.slice(eq + 1).trim();
9823
+ if (value && !fields.has(key)) fields.set(key, value);
9824
+ }
9825
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
9826
+ if (!hostRaw) return null;
9827
+ const host = hostRaw.split(",")[0].trim();
9828
+ if (hostIsUnresolved(host)) return null;
9829
+ const portRaw = fields.get("port");
9830
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
9831
+ const database = fields.get("database") ?? fields.get("db") ?? "";
9832
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
9833
+ }
9834
+ function parseRedisEndpoint(raw) {
9835
+ const s = raw.trim();
9836
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
9837
+ const first = s.split(",")[0].trim();
9838
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
9839
+ if (!m) return null;
9840
+ const host = m[1];
9841
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
9842
+ const port = m[2] ? Number(m[2]) : void 0;
9843
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
9844
+ }
9845
+ async function resolveEnvUpTree(startDir, name) {
9846
+ let dir = import_node_path32.default.resolve(startDir);
9847
+ for (let depth = 0; depth < 12; depth++) {
9848
+ const value = await resolveEnvVar(dir, name);
9849
+ if (value !== null) return value;
9850
+ const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
9851
+ const parent = import_node_path32.default.dirname(dir);
9852
+ if (atRepoRoot || parent === dir) break;
9853
+ dir = parent;
9854
+ }
9855
+ return null;
9856
+ }
9857
+ async function interpolateEnvRefs(value, dir) {
9858
+ const refs = /* @__PURE__ */ new Set();
9859
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
9860
+ refs.add(m[1] ?? m[2]);
9861
+ }
9862
+ let out = value;
9863
+ for (const name of refs) {
9864
+ const resolved = await resolveEnvUpTree(dir, name);
9865
+ if (resolved === null) continue;
9866
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
9867
+ }
9868
+ return out;
9869
+ }
9870
+ function stringLiterals(masked) {
9871
+ const out = [];
9872
+ STRING_LITERAL_RE.lastIndex = 0;
9873
+ let m;
9874
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
9875
+ return out;
9876
+ }
9877
+ function envKeys(masked) {
9878
+ const out = [];
9879
+ ENV_READ_RE.lastIndex = 0;
9880
+ let m;
9881
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
9882
+ const key = m[1] ?? m[2];
9883
+ if (key) out.push(key);
9884
+ }
9885
+ return out;
9886
+ }
9887
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
9888
+ const out = [];
9889
+ for (const key of keys) {
9890
+ const raw = await resolveEnvUpTree(serviceDir, key);
9891
+ if (raw === null) continue;
9892
+ const value = await interpolateEnvRefs(raw, serviceDir);
9893
+ if (!looksLike(value)) continue;
9894
+ const parsed = parse11(value);
9895
+ if (parsed) out.push(parsed);
9896
+ }
9897
+ for (const lit of literals) {
9898
+ if (!looksLike(lit)) continue;
9899
+ const parsed = parse11(lit);
9900
+ if (parsed) out.push(parsed);
9901
+ }
9902
+ return out;
9903
+ }
9904
+ async function parse9(serviceDir) {
9905
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
9906
+ (f) => import_node_path32.default.extname(f) === CS_EXT
9907
+ );
9908
+ if (files.length === 0) return [];
9909
+ const sources = [];
9910
+ for (const file of files) {
9911
+ const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
9912
+ if (content !== null) sources.push({ file, content });
9913
+ }
9914
+ let pgGateFile = null;
9915
+ let redisGateFile = null;
9916
+ for (const { file, content } of sources) {
9917
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
9918
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
9919
+ }
9920
+ if (!pgGateFile && !redisGateFile) return [];
9921
+ const literals = [];
9922
+ const keys = [];
9923
+ for (const { content } of sources) {
9924
+ const masked = maskCommentsInSource(content);
9925
+ literals.push(...stringLiterals(masked));
9926
+ keys.push(...envKeys(masked));
9927
+ }
9928
+ const out = [];
9929
+ const seenHosts = /* @__PURE__ */ new Set();
9930
+ const push = (config, sourceFile) => {
9931
+ const dedupe = `${config.engine}:${config.host}`;
9932
+ if (seenHosts.has(dedupe)) return;
9933
+ seenHosts.add(dedupe);
9934
+ out.push({ ...config, sourceFile });
9935
+ };
9936
+ if (pgGateFile) {
9937
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
9938
+ push(pg3, pgGateFile);
9939
+ }
9940
+ }
9941
+ if (redisGateFile) {
9942
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
9943
+ push(redis, redisGateFile);
9944
+ }
9945
+ }
9946
+ return out;
9947
+ }
9948
+ var csharpParser = { name: "csharp", parse: parse9 };
9949
+
9950
+ // src/extract/databases/docker-compose.ts
9951
+ init_cjs_shims();
9952
+ var import_node_path33 = __toESM(require("path"), 1);
9346
9953
  function portFromService(svc) {
9347
9954
  for (const raw of svc.ports ?? []) {
9348
9955
  const str = String(raw);
@@ -9367,9 +9974,9 @@ function databaseFromEnv(svc) {
9367
9974
  };
9368
9975
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9369
9976
  }
9370
- async function parse9(serviceDir) {
9977
+ async function parse10(serviceDir) {
9371
9978
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9372
- const abs = import_node_path32.default.join(serviceDir, name);
9979
+ const abs = import_node_path33.default.join(serviceDir, name);
9373
9980
  if (!await exists(abs)) continue;
9374
9981
  const raw = await readYaml(abs);
9375
9982
  if (!raw?.services) return [];
@@ -9391,7 +9998,7 @@ async function parse9(serviceDir) {
9391
9998
  }
9392
9999
  return [];
9393
10000
  }
9394
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
10001
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9395
10002
 
9396
10003
  // src/extract/databases/index.ts
9397
10004
  var DB_PARSERS = [
@@ -9403,6 +10010,7 @@ var DB_PARSERS = [
9403
10010
  ormconfigParser,
9404
10011
  typeormParser,
9405
10012
  sequelizeParser,
10013
+ csharpParser,
9406
10014
  dockerComposeParser
9407
10015
  ];
9408
10016
  function compatibleDriversFor(engine) {
@@ -9541,7 +10149,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9541
10149
  discoveredVia: mergedDiscoveredVia
9542
10150
  });
9543
10151
  }
9544
- const relConfigFile = toPosix(import_node_path33.default.relative(service.dir, config.sourceFile));
10152
+ const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
9545
10153
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9546
10154
  graph,
9547
10155
  service.pkg.name,
@@ -9550,7 +10158,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9550
10158
  );
9551
10159
  nodesAdded += fn;
9552
10160
  edgesAdded += fe;
9553
- const evidenceFile = toPosix(import_node_path33.default.relative(scanPath, config.sourceFile));
10161
+ const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
9554
10162
  const edge = {
9555
10163
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
9556
10164
  source: fileNodeId,
@@ -9568,15 +10176,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9568
10176
  if (allConfigs.length === 1) {
9569
10177
  const primary = allConfigs[0];
9570
10178
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
9571
- const relPath = import_node_path33.default.relative(scanPath, primary.sourceFile);
10179
+ const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
9572
10180
  const cfgId = (0, import_types23.configId)(relPath);
9573
10181
  if (!graph.hasNode(cfgId)) {
9574
10182
  const cfgNode = {
9575
10183
  id: cfgId,
9576
10184
  type: import_types23.NodeType.ConfigNode,
9577
- name: import_node_path33.default.basename(primary.sourceFile),
10185
+ name: import_node_path34.default.basename(primary.sourceFile),
9578
10186
  path: relPath,
9579
- fileType: isConfigFile(import_node_path33.default.basename(primary.sourceFile)).fileType || "config"
10187
+ fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
9580
10188
  };
9581
10189
  graph.addNode(cfgId, cfgNode);
9582
10190
  nodesAdded++;
@@ -9617,27 +10225,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9617
10225
 
9618
10226
  // src/extract/configs.ts
9619
10227
  init_cjs_shims();
9620
- var import_node_fs22 = require("fs");
9621
- var import_node_path34 = __toESM(require("path"), 1);
10228
+ var import_node_fs23 = require("fs");
10229
+ var import_node_path35 = __toESM(require("path"), 1);
9622
10230
  var import_types24 = require("@neat.is/types");
9623
10231
  async function walkConfigFiles(dir, excludeDirs = []) {
9624
- const excluded = new Set(excludeDirs.map((d) => import_node_path34.default.resolve(d)));
10232
+ const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
9625
10233
  const out = [];
9626
- async function walk9(current) {
9627
- const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true });
10234
+ async function walk10(current) {
10235
+ const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
9628
10236
  for (const entry of entries) {
9629
- const full = import_node_path34.default.join(current, entry.name);
10237
+ const full = import_node_path35.default.join(current, entry.name);
9630
10238
  if (entry.isDirectory()) {
9631
10239
  if (IGNORED_DIRS.has(entry.name)) continue;
9632
- if (excluded.has(import_node_path34.default.resolve(full))) continue;
10240
+ if (excluded.has(import_node_path35.default.resolve(full))) continue;
9633
10241
  if (await isPythonVenvDir(full)) continue;
9634
- await walk9(full);
10242
+ await walk10(full);
9635
10243
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
9636
10244
  out.push(full);
9637
10245
  }
9638
10246
  }
9639
10247
  }
9640
- await walk9(dir);
10248
+ await walk10(dir);
9641
10249
  return out;
9642
10250
  }
9643
10251
  async function addConfigNodes(graph, services, scanPath) {
@@ -9646,19 +10254,19 @@ async function addConfigNodes(graph, services, scanPath) {
9646
10254
  for (const service of services) {
9647
10255
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
9648
10256
  for (const file of configFiles) {
9649
- const relPath = import_node_path34.default.relative(scanPath, file);
10257
+ const relPath = import_node_path35.default.relative(scanPath, file);
9650
10258
  const node = {
9651
10259
  id: (0, import_types24.configId)(relPath),
9652
10260
  type: import_types24.NodeType.ConfigNode,
9653
- name: import_node_path34.default.basename(file),
10261
+ name: import_node_path35.default.basename(file),
9654
10262
  path: relPath,
9655
- fileType: isConfigFile(import_node_path34.default.basename(file)).fileType
10263
+ fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
9656
10264
  };
9657
10265
  if (!graph.hasNode(node.id)) {
9658
10266
  graph.addNode(node.id, node);
9659
10267
  nodesAdded++;
9660
10268
  }
9661
- const relToService = toPosix(import_node_path34.default.relative(service.dir, file));
10269
+ const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
9662
10270
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9663
10271
  graph,
9664
10272
  service.pkg.name,
@@ -9674,7 +10282,7 @@ async function addConfigNodes(graph, services, scanPath) {
9674
10282
  type: import_types24.EdgeType.CONFIGURED_BY,
9675
10283
  provenance: import_types24.Provenance.EXTRACTED,
9676
10284
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
9677
- evidence: { file: relPath.split(import_node_path34.default.sep).join("/") }
10285
+ evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
9678
10286
  };
9679
10287
  if (!graph.hasEdge(edge.id)) {
9680
10288
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -9687,8 +10295,8 @@ async function addConfigNodes(graph, services, scanPath) {
9687
10295
 
9688
10296
  // src/extract/proto.ts
9689
10297
  init_cjs_shims();
9690
- var import_node_fs23 = require("fs");
9691
- var import_node_path35 = __toESM(require("path"), 1);
10298
+ var import_node_fs24 = require("fs");
10299
+ var import_node_path36 = __toESM(require("path"), 1);
9692
10300
  var import_types25 = require("@neat.is/types");
9693
10301
  var PROTO_EXTENSION = ".proto";
9694
10302
  function packageOf(content) {
@@ -9726,23 +10334,23 @@ function grpcMethodsFromProto(content, fqPackage) {
9726
10334
  return out;
9727
10335
  }
9728
10336
  async function walkProtoFiles(dir, excludeDirs = []) {
9729
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10337
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
9730
10338
  const out = [];
9731
- async function walk9(current) {
9732
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10339
+ async function walk10(current) {
10340
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9733
10341
  for (const entry of entries) {
9734
- const full = import_node_path35.default.join(current, entry.name);
10342
+ const full = import_node_path36.default.join(current, entry.name);
9735
10343
  if (entry.isDirectory()) {
9736
10344
  if (IGNORED_DIRS.has(entry.name)) continue;
9737
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10345
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
9738
10346
  if (await isPythonVenvDir(full)) continue;
9739
- await walk9(full);
9740
- } else if (entry.isFile() && import_node_path35.default.extname(entry.name) === PROTO_EXTENSION) {
10347
+ await walk10(full);
10348
+ } else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
9741
10349
  out.push(full);
9742
10350
  }
9743
10351
  }
9744
10352
  }
9745
- await walk9(dir);
10353
+ await walk10(dir);
9746
10354
  return out;
9747
10355
  }
9748
10356
  async function addGrpcMethods(graph, services) {
@@ -9752,10 +10360,10 @@ async function addGrpcMethods(graph, services) {
9752
10360
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
9753
10361
  for (const protoPath of protoPaths) {
9754
10362
  if (isTestPath(protoPath)) continue;
9755
- const relFile = toPosix(import_node_path35.default.relative(service.dir, protoPath));
10363
+ const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
9756
10364
  let content;
9757
10365
  try {
9758
- content = await import_node_fs23.promises.readFile(protoPath, "utf8");
10366
+ content = await import_node_fs24.promises.readFile(protoPath, "utf8");
9759
10367
  } catch (err) {
9760
10368
  recordExtractionError("proto extraction", protoPath, err);
9761
10369
  continue;
@@ -9810,11 +10418,11 @@ async function addGrpcMethods(graph, services) {
9810
10418
 
9811
10419
  // src/extract/calls/index.ts
9812
10420
  init_cjs_shims();
9813
- var import_types43 = require("@neat.is/types");
10421
+ var import_types44 = require("@neat.is/types");
9814
10422
 
9815
10423
  // src/extract/calls/http.ts
9816
10424
  init_cjs_shims();
9817
- var import_node_path36 = __toESM(require("path"), 1);
10425
+ var import_node_path37 = __toESM(require("path"), 1);
9818
10426
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
9819
10427
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
9820
10428
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -9897,7 +10505,7 @@ async function addHttpCallEdges(graph, services) {
9897
10505
  const seen = /* @__PURE__ */ new Set();
9898
10506
  for (const file of files) {
9899
10507
  if (isTestPath(file.path)) continue;
9900
- const parser = parserForExt(import_node_path36.default.extname(file.path), parserCache);
10508
+ const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
9901
10509
  let sites;
9902
10510
  try {
9903
10511
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9906,7 +10514,7 @@ async function addHttpCallEdges(graph, services) {
9906
10514
  continue;
9907
10515
  }
9908
10516
  if (sites.length === 0) continue;
9909
- const relFile = toPosix(import_node_path36.default.relative(service.dir, file.path));
10517
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
9910
10518
  for (const site of sites) {
9911
10519
  const targetId = hostToNodeId.get(site.host);
9912
10520
  if (!targetId || targetId === service.node.id) continue;
@@ -9960,7 +10568,7 @@ async function addHttpCallEdges(graph, services) {
9960
10568
 
9961
10569
  // src/extract/calls/route-match.ts
9962
10570
  init_cjs_shims();
9963
- var import_node_path37 = __toESM(require("path"), 1);
10571
+ var import_node_path38 = __toESM(require("path"), 1);
9964
10572
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
9965
10573
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
9966
10574
  var import_types27 = require("@neat.is/types");
@@ -10159,7 +10767,7 @@ async function addRouteCallEdges(graph, services) {
10159
10767
  const seen = /* @__PURE__ */ new Set();
10160
10768
  for (const file of files) {
10161
10769
  if (isTestPath(file.path)) continue;
10162
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path37.default.extname(file.path))) continue;
10770
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
10163
10771
  let sites;
10164
10772
  try {
10165
10773
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10168,7 +10776,7 @@ async function addRouteCallEdges(graph, services) {
10168
10776
  continue;
10169
10777
  }
10170
10778
  if (sites.length === 0) continue;
10171
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
10779
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10172
10780
  for (const site of sites) {
10173
10781
  const serverServiceId = hostToNodeId.get(site.host);
10174
10782
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10229,7 +10837,7 @@ async function addRouteCallEdges(graph, services) {
10229
10837
 
10230
10838
  // src/extract/calls/kafka.ts
10231
10839
  init_cjs_shims();
10232
- var import_node_path38 = __toESM(require("path"), 1);
10840
+ var import_node_path39 = __toESM(require("path"), 1);
10233
10841
  var import_types28 = require("@neat.is/types");
10234
10842
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10235
10843
  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 +10925,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10317
10925
  // call sites — verified-call-site tier (ADR-066).
10318
10926
  confidenceKind: "verified-call-site",
10319
10927
  evidence: {
10320
- file: import_node_path38.default.relative(serviceDir, file.path),
10928
+ file: import_node_path39.default.relative(serviceDir, file.path),
10321
10929
  line,
10322
10930
  snippet: snippet(file.content, line)
10323
10931
  }
10324
10932
  });
10325
10933
  };
10326
- if (import_node_path38.default.extname(file.path) === ".go") {
10934
+ if (import_node_path39.default.extname(file.path) === ".go") {
10327
10935
  goSaramaEndpoints(file.content, make);
10328
10936
  } else {
10329
10937
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -10334,7 +10942,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10334
10942
 
10335
10943
  // src/extract/calls/redis.ts
10336
10944
  init_cjs_shims();
10337
- var import_node_path39 = __toESM(require("path"), 1);
10945
+ var import_node_path40 = __toESM(require("path"), 1);
10338
10946
  var import_types29 = require("@neat.is/types");
10339
10947
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10340
10948
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10357,7 +10965,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10357
10965
  // support tier (ADR-066).
10358
10966
  confidenceKind: "url-with-structural-support",
10359
10967
  evidence: {
10360
- file: import_node_path39.default.relative(serviceDir, file.path),
10968
+ file: import_node_path40.default.relative(serviceDir, file.path),
10361
10969
  line,
10362
10970
  snippet: snippet(file.content, line)
10363
10971
  }
@@ -10368,7 +10976,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10368
10976
 
10369
10977
  // src/extract/calls/aws.ts
10370
10978
  init_cjs_shims();
10371
- var import_node_path40 = __toESM(require("path"), 1);
10979
+ var import_node_path41 = __toESM(require("path"), 1);
10372
10980
  var import_types30 = require("@neat.is/types");
10373
10981
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10374
10982
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10402,7 +11010,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10402
11010
  // (ADR-066).
10403
11011
  confidenceKind: "verified-call-site",
10404
11012
  evidence: {
10405
- file: import_node_path40.default.relative(serviceDir, file.path),
11013
+ file: import_node_path41.default.relative(serviceDir, file.path),
10406
11014
  line,
10407
11015
  snippet: snippet(file.content, line)
10408
11016
  }
@@ -10427,7 +11035,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10427
11035
 
10428
11036
  // src/extract/calls/grpc.ts
10429
11037
  init_cjs_shims();
10430
- var import_node_path41 = __toESM(require("path"), 1);
11038
+ var import_node_path42 = __toESM(require("path"), 1);
10431
11039
  var import_types31 = require("@neat.is/types");
10432
11040
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10433
11041
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -10486,7 +11094,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10486
11094
  // tier (ADR-066).
10487
11095
  confidenceKind: "verified-call-site",
10488
11096
  evidence: {
10489
- file: import_node_path41.default.relative(serviceDir, file.path),
11097
+ file: import_node_path42.default.relative(serviceDir, file.path),
10490
11098
  line,
10491
11099
  snippet: snippet(file.content, line)
10492
11100
  }
@@ -10497,7 +11105,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10497
11105
 
10498
11106
  // src/extract/calls/supabase.ts
10499
11107
  init_cjs_shims();
10500
- var import_node_path42 = __toESM(require("path"), 1);
11108
+ var import_node_path43 = __toESM(require("path"), 1);
10501
11109
  var import_types32 = require("@neat.is/types");
10502
11110
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
10503
11111
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -10556,7 +11164,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10556
11164
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10557
11165
  confidenceKind: "verified-call-site",
10558
11166
  evidence: {
10559
- file: import_node_path42.default.relative(serviceDir, file.path),
11167
+ file: import_node_path43.default.relative(serviceDir, file.path),
10560
11168
  line,
10561
11169
  snippet: snippet(file.content, line)
10562
11170
  }
@@ -10583,7 +11191,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10583
11191
  edgeType: "CALLS",
10584
11192
  confidenceKind: "verified-call-site",
10585
11193
  evidence: {
10586
- file: import_node_path42.default.relative(serviceDir, file.path),
11194
+ file: import_node_path43.default.relative(serviceDir, file.path),
10587
11195
  line,
10588
11196
  snippet: snippet(file.content, line)
10589
11197
  }
@@ -10595,7 +11203,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10595
11203
 
10596
11204
  // src/extract/calls/firestore.ts
10597
11205
  init_cjs_shims();
10598
- var import_node_path43 = __toESM(require("path"), 1);
11206
+ var import_node_path44 = __toESM(require("path"), 1);
10599
11207
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10600
11208
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10601
11209
  var import_types33 = require("@neat.is/types");
@@ -10634,7 +11242,7 @@ function isFirestoreClientFactory(node) {
10634
11242
  }
10635
11243
  function firestoreClientVars(root) {
10636
11244
  const vars = /* @__PURE__ */ new Set();
10637
- const walk9 = (node) => {
11245
+ const walk10 = (node) => {
10638
11246
  if (node.type === "variable_declarator") {
10639
11247
  const name = node.childForFieldName("name");
10640
11248
  let value = node.childForFieldName("value");
@@ -10643,9 +11251,9 @@ function firestoreClientVars(root) {
10643
11251
  vars.add(name.text);
10644
11252
  }
10645
11253
  }
10646
- for (const c of namedChildren(node)) walk9(c);
11254
+ for (const c of namedChildren(node)) walk10(c);
10647
11255
  };
10648
- walk9(root);
11256
+ walk10(root);
10649
11257
  return vars;
10650
11258
  }
10651
11259
  function isClientExpr(node, clientVars) {
@@ -10766,7 +11374,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10766
11374
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10767
11375
  if (!hasClient && !hasAdmin) return [];
10768
11376
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10769
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
11377
+ const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
10770
11378
  const clientVars = firestoreClientVars(tree.rootNode);
10771
11379
  const collLine = /* @__PURE__ */ new Map();
10772
11380
  const writes = /* @__PURE__ */ new Map();
@@ -10800,7 +11408,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10800
11408
  }
10801
11409
  s.add(field);
10802
11410
  };
10803
- const walk9 = (node) => {
11411
+ const walk10 = (node) => {
10804
11412
  if (node.type === "call_expression") {
10805
11413
  const fn = node.childForFieldName("function");
10806
11414
  const line = node.startPosition.row + 1;
@@ -10840,9 +11448,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10840
11448
  }
10841
11449
  }
10842
11450
  }
10843
- for (const c of namedChildren(node)) walk9(c);
11451
+ for (const c of namedChildren(node)) walk10(c);
10844
11452
  };
10845
- walk9(tree.rootNode);
11453
+ walk10(tree.rootNode);
10846
11454
  const out = [];
10847
11455
  for (const [collPath, line] of collLine) {
10848
11456
  const byField = writes.get(collPath);
@@ -10870,7 +11478,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10870
11478
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10871
11479
  ...sdkWrites ? { sdkWrites } : {},
10872
11480
  evidence: {
10873
- file: import_node_path43.default.relative(serviceDir, file.path),
11481
+ file: import_node_path44.default.relative(serviceDir, file.path),
10874
11482
  line,
10875
11483
  snippet: snippet(file.content, line)
10876
11484
  }
@@ -10881,7 +11489,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10881
11489
 
10882
11490
  // src/extract/calls/mongoose.ts
10883
11491
  init_cjs_shims();
10884
- var import_node_path44 = __toESM(require("path"), 1);
11492
+ var import_node_path45 = __toESM(require("path"), 1);
10885
11493
  var import_types34 = require("@neat.is/types");
10886
11494
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10887
11495
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -11032,7 +11640,7 @@ function endpoint(r, file, serviceDir, matchText) {
11032
11640
  kind: r.kind,
11033
11641
  edgeType: "CALLS",
11034
11642
  confidenceKind: "verified-call-site",
11035
- evidence: { file: import_node_path44.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11643
+ evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11036
11644
  };
11037
11645
  }
11038
11646
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11122,7 +11730,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11122
11730
  const registry = /* @__PURE__ */ new Map();
11123
11731
  for (const f of mongooseFiles) {
11124
11732
  const fx = fileExportsOf(f.content, pluralizeOn);
11125
- if (fx) registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), fx);
11733
+ if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
11126
11734
  }
11127
11735
  if (registry.size === 0) return [];
11128
11736
  const out = [];
@@ -11133,7 +11741,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11133
11741
  const directColl = /* @__PURE__ */ new Map();
11134
11742
  const nsExports = /* @__PURE__ */ new Map();
11135
11743
  for (const b of bindings) {
11136
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
11744
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
11137
11745
  if (!resolvedRel) continue;
11138
11746
  const fx = registry.get(resolvedRel);
11139
11747
  if (!fx) continue;
@@ -11177,7 +11785,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11177
11785
 
11178
11786
  // src/extract/calls/sqlalchemy.ts
11179
11787
  init_cjs_shims();
11180
- var import_node_path45 = __toESM(require("path"), 1);
11788
+ var import_node_path46 = __toESM(require("path"), 1);
11181
11789
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11182
11790
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11183
11791
  var import_types35 = require("@neat.is/types");
@@ -11316,7 +11924,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11316
11924
  childTable,
11317
11925
  parentTable,
11318
11926
  evidence: {
11319
- file: import_node_path45.default.relative(serviceDir, file.path),
11927
+ file: import_node_path46.default.relative(serviceDir, file.path),
11320
11928
  line,
11321
11929
  snippet: snippet(file.content, line)
11322
11930
  }
@@ -11341,7 +11949,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11341
11949
  confidenceKind: "verified-call-site",
11342
11950
  ...columns && columns.length > 0 ? { columns } : {},
11343
11951
  evidence: {
11344
- file: import_node_path45.default.relative(serviceDir, file.path),
11952
+ file: import_node_path46.default.relative(serviceDir, file.path),
11345
11953
  line,
11346
11954
  snippet: snippet(file.content, line)
11347
11955
  }
@@ -11448,7 +12056,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11448
12056
  edgeType: "CALLS",
11449
12057
  confidenceKind: "verified-call-site",
11450
12058
  evidence: {
11451
- file: import_node_path45.default.relative(serviceDir, file.path),
12059
+ file: import_node_path46.default.relative(serviceDir, file.path),
11452
12060
  line,
11453
12061
  snippet: snippet(file.content, line)
11454
12062
  }
@@ -11460,7 +12068,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11460
12068
 
11461
12069
  // src/extract/calls/django-orm.ts
11462
12070
  init_cjs_shims();
11463
- var import_node_path46 = __toESM(require("path"), 1);
12071
+ var import_node_path47 = __toESM(require("path"), 1);
11464
12072
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11465
12073
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11466
12074
  var import_types36 = require("@neat.is/types");
@@ -11530,7 +12138,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11530
12138
  const tree = parseSource7(makePyParser4(), file.content);
11531
12139
  const out = [];
11532
12140
  const seen = /* @__PURE__ */ new Set();
11533
- const defaultAppLabel = import_node_path46.default.basename(import_node_path46.default.dirname(file.path));
12141
+ const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
11534
12142
  walk4(tree.rootNode, (node) => {
11535
12143
  if (node.type !== "class_definition") return;
11536
12144
  if (!extendsDjangoModel(node)) return;
@@ -11548,7 +12156,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11548
12156
  kind: "sql-table",
11549
12157
  edgeType: "CALLS",
11550
12158
  confidenceKind: "verified-call-site",
11551
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12159
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11552
12160
  });
11553
12161
  });
11554
12162
  return out;
@@ -11556,7 +12164,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11556
12164
 
11557
12165
  // src/extract/calls/drizzle.ts
11558
12166
  init_cjs_shims();
11559
- var import_node_path47 = __toESM(require("path"), 1);
12167
+ var import_node_path48 = __toESM(require("path"), 1);
11560
12168
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11561
12169
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11562
12170
  var import_types37 = require("@neat.is/types");
@@ -11634,10 +12242,10 @@ function columnsFromObject(obj) {
11634
12242
  }
11635
12243
  function drizzleEndpointsFromFile(file, serviceDir) {
11636
12244
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11637
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12245
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11638
12246
  const out = [];
11639
12247
  const seen = /* @__PURE__ */ new Set();
11640
- const walk9 = (node) => {
12248
+ const walk10 = (node) => {
11641
12249
  if (node.type === "call_expression") {
11642
12250
  const fn = node.childForFieldName("function");
11643
12251
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11657,7 +12265,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11657
12265
  confidenceKind: "structural",
11658
12266
  columns,
11659
12267
  evidence: {
11660
- file: import_node_path47.default.relative(serviceDir, file.path),
12268
+ file: import_node_path48.default.relative(serviceDir, file.path),
11661
12269
  line,
11662
12270
  snippet: snippet(file.content, line)
11663
12271
  }
@@ -11665,9 +12273,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11665
12273
  }
11666
12274
  }
11667
12275
  }
11668
- for (const c of namedChildren4(node)) walk9(c);
12276
+ for (const c of namedChildren4(node)) walk10(c);
11669
12277
  };
11670
- walk9(tree.rootNode);
12278
+ walk10(tree.rootNode);
11671
12279
  return out;
11672
12280
  }
11673
12281
  function enclosingVarName(call) {
@@ -11689,7 +12297,7 @@ function enclosingVarName(call) {
11689
12297
  function collectDrizzleTables(root) {
11690
12298
  const tables = [];
11691
12299
  const varToTable = /* @__PURE__ */ new Map();
11692
- const walk9 = (node) => {
12300
+ const walk10 = (node) => {
11693
12301
  if (node.type === "call_expression") {
11694
12302
  const fn = node.childForFieldName("function");
11695
12303
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11704,9 +12312,9 @@ function collectDrizzleTables(root) {
11704
12312
  }
11705
12313
  }
11706
12314
  }
11707
- for (const c of namedChildren4(node)) walk9(c);
12315
+ for (const c of namedChildren4(node)) walk10(c);
11708
12316
  };
11709
- walk9(root);
12317
+ walk10(root);
11710
12318
  return { tables, varToTable };
11711
12319
  }
11712
12320
  function referencesTargetVar(call) {
@@ -11723,13 +12331,13 @@ function referencesTargetVar(call) {
11723
12331
  }
11724
12332
  function drizzleForeignKeys(file, serviceDir) {
11725
12333
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11726
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12334
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11727
12335
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11728
12336
  const out = [];
11729
12337
  const seen = /* @__PURE__ */ new Set();
11730
12338
  for (const table of tables) {
11731
12339
  if (!table.object) continue;
11732
- const walk9 = (node) => {
12340
+ const walk10 = (node) => {
11733
12341
  if (node.type === "call_expression") {
11734
12342
  const targetVar = referencesTargetVar(node);
11735
12343
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -11742,7 +12350,7 @@ function drizzleForeignKeys(file, serviceDir) {
11742
12350
  childTable: table.tableName,
11743
12351
  parentTable,
11744
12352
  evidence: {
11745
- file: import_node_path47.default.relative(serviceDir, file.path),
12353
+ file: import_node_path48.default.relative(serviceDir, file.path),
11746
12354
  line,
11747
12355
  snippet: snippet(file.content, line)
11748
12356
  }
@@ -11750,16 +12358,16 @@ function drizzleForeignKeys(file, serviceDir) {
11750
12358
  }
11751
12359
  }
11752
12360
  }
11753
- for (const c of namedChildren4(node)) walk9(c);
12361
+ for (const c of namedChildren4(node)) walk10(c);
11754
12362
  };
11755
- walk9(table.object);
12363
+ walk10(table.object);
11756
12364
  }
11757
12365
  return out;
11758
12366
  }
11759
12367
 
11760
12368
  // src/extract/calls/prisma.ts
11761
12369
  init_cjs_shims();
11762
- var import_node_path48 = __toESM(require("path"), 1);
12370
+ var import_node_path49 = __toESM(require("path"), 1);
11763
12371
  var import_types38 = require("@neat.is/types");
11764
12372
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11765
12373
  "Int",
@@ -11815,7 +12423,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11815
12423
  confidenceKind: "structural",
11816
12424
  columns: b.columns,
11817
12425
  evidence: {
11818
- file: import_node_path48.default.relative(serviceDir, file.path),
12426
+ file: import_node_path49.default.relative(serviceDir, file.path),
11819
12427
  line: b.startLine,
11820
12428
  snippet: snippet(content, b.startLine)
11821
12429
  }
@@ -11876,7 +12484,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11876
12484
  }
11877
12485
  async function prismaColumnEndpoints(serviceDir) {
11878
12486
  const schemaPath = await findFirst(serviceDir, [
11879
- import_node_path48.default.join("prisma", "schema.prisma"),
12487
+ import_node_path49.default.join("prisma", "schema.prisma"),
11880
12488
  "schema.prisma"
11881
12489
  ]);
11882
12490
  if (!schemaPath) return [];
@@ -11951,7 +12559,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11951
12559
  childTable: current.table,
11952
12560
  parentTable,
11953
12561
  evidence: {
11954
- file: import_node_path48.default.relative(serviceDir, file.path),
12562
+ file: import_node_path49.default.relative(serviceDir, file.path),
11955
12563
  line: lineNo,
11956
12564
  snippet: snippet(content, lineNo)
11957
12565
  }
@@ -11966,7 +12574,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11966
12574
  }
11967
12575
  async function prismaForeignKeys(serviceDir) {
11968
12576
  const schemaPath = await findFirst(serviceDir, [
11969
- import_node_path48.default.join("prisma", "schema.prisma"),
12577
+ import_node_path49.default.join("prisma", "schema.prisma"),
11970
12578
  "schema.prisma"
11971
12579
  ]);
11972
12580
  if (!schemaPath) return [];
@@ -11977,7 +12585,7 @@ async function prismaForeignKeys(serviceDir) {
11977
12585
 
11978
12586
  // src/extract/calls/activerecord.ts
11979
12587
  init_cjs_shims();
11980
- var import_node_path49 = __toESM(require("path"), 1);
12588
+ var import_node_path50 = __toESM(require("path"), 1);
11981
12589
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11982
12590
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
11983
12591
  var import_types39 = require("@neat.is/types");
@@ -12185,7 +12793,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12185
12793
  confidenceKind: "structural",
12186
12794
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12187
12795
  evidence: {
12188
- file: import_node_path49.default.relative(serviceDir, file.path),
12796
+ file: import_node_path50.default.relative(serviceDir, file.path),
12189
12797
  line: table.line,
12190
12798
  snippet: snippet(file.content, table.line)
12191
12799
  }
@@ -12207,7 +12815,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12207
12815
  childTable,
12208
12816
  parentTable,
12209
12817
  evidence: {
12210
- file: import_node_path49.default.relative(serviceDir, file.path),
12818
+ file: import_node_path50.default.relative(serviceDir, file.path),
12211
12819
  line,
12212
12820
  snippet: snippet(file.content, line)
12213
12821
  }
@@ -12290,7 +12898,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12290
12898
  edgeType: "CALLS",
12291
12899
  confidenceKind: "verified-call-site",
12292
12900
  evidence: {
12293
- file: import_node_path49.default.relative(serviceDir, file.path),
12901
+ file: import_node_path50.default.relative(serviceDir, file.path),
12294
12902
  line,
12295
12903
  snippet: snippet(file.content, line)
12296
12904
  }
@@ -12327,7 +12935,7 @@ function railsModelForeignKeys(file, serviceDir) {
12327
12935
  childTable,
12328
12936
  parentTable,
12329
12937
  evidence: {
12330
- file: import_node_path49.default.relative(serviceDir, file.path),
12938
+ file: import_node_path50.default.relative(serviceDir, file.path),
12331
12939
  line,
12332
12940
  snippet: snippet(file.content, line)
12333
12941
  }
@@ -12339,7 +12947,7 @@ function railsModelForeignKeys(file, serviceDir) {
12339
12947
 
12340
12948
  // src/extract/calls/eloquent.ts
12341
12949
  init_cjs_shims();
12342
- var import_node_path50 = __toESM(require("path"), 1);
12950
+ var import_node_path51 = __toESM(require("path"), 1);
12343
12951
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12344
12952
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12345
12953
  var import_types40 = require("@neat.is/types");
@@ -12596,7 +13204,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12596
13204
  confidenceKind: "structural",
12597
13205
  ...columns.length > 0 ? { columns } : {},
12598
13206
  evidence: {
12599
- file: import_node_path50.default.relative(serviceDir, file.path),
13207
+ file: import_node_path51.default.relative(serviceDir, file.path),
12600
13208
  line: bp.line,
12601
13209
  snippet: snippet(file.content, bp.line)
12602
13210
  }
@@ -12618,7 +13226,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12618
13226
  childTable,
12619
13227
  parentTable,
12620
13228
  evidence: {
12621
- file: import_node_path50.default.relative(serviceDir, file.path),
13229
+ file: import_node_path51.default.relative(serviceDir, file.path),
12622
13230
  line,
12623
13231
  snippet: snippet(file.content, line)
12624
13232
  }
@@ -12738,7 +13346,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12738
13346
  edgeType: "CALLS",
12739
13347
  confidenceKind: "verified-call-site",
12740
13348
  evidence: {
12741
- file: import_node_path50.default.relative(serviceDir, file.path),
13349
+ file: import_node_path51.default.relative(serviceDir, file.path),
12742
13350
  line,
12743
13351
  snippet: snippet(file.content, line)
12744
13352
  }
@@ -12774,7 +13382,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12774
13382
  childTable,
12775
13383
  parentTable,
12776
13384
  evidence: {
12777
- file: import_node_path50.default.relative(serviceDir, file.path),
13385
+ file: import_node_path51.default.relative(serviceDir, file.path),
12778
13386
  line,
12779
13387
  snippet: snippet(file.content, line)
12780
13388
  }
@@ -12786,7 +13394,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12786
13394
 
12787
13395
  // src/extract/calls/go.ts
12788
13396
  init_cjs_shims();
12789
- var import_node_path53 = __toESM(require("path"), 1);
13397
+ var import_node_path54 = __toESM(require("path"), 1);
12790
13398
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12791
13399
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12792
13400
  var import_types41 = require("@neat.is/types");
@@ -12861,7 +13469,7 @@ function firstStringLiteralArg(argsNode) {
12861
13469
  return null;
12862
13470
  }
12863
13471
  function goSqlEndpointsFromFile(file, serviceDir) {
12864
- if (import_node_path53.default.extname(file.path) !== ".go") return [];
13472
+ if (import_node_path54.default.extname(file.path) !== ".go") return [];
12865
13473
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12866
13474
  const tree = parseSource10(makeGoParser3(), file.content);
12867
13475
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12890,7 +13498,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12890
13498
  confidenceKind: "verified-call-site",
12891
13499
  ...columns.length > 0 ? { columns } : {},
12892
13500
  evidence: {
12893
- file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
13501
+ file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
12894
13502
  line,
12895
13503
  snippet: snippet(file.content, line)
12896
13504
  }
@@ -12901,7 +13509,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12901
13509
 
12902
13510
  // src/extract/calls/gorm.ts
12903
13511
  init_cjs_shims();
12904
- var import_node_path54 = __toESM(require("path"), 1);
13512
+ var import_node_path55 = __toESM(require("path"), 1);
12905
13513
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12906
13514
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12907
13515
  var import_types42 = require("@neat.is/types");
@@ -13334,7 +13942,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13334
13942
  seen.delete(struct.name);
13335
13943
  }
13336
13944
  function gormEndpointsFromFile(file, serviceDir) {
13337
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13945
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13338
13946
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13339
13947
  const tree = parseSource11(makeGoParser4(), file.content);
13340
13948
  const { structs, models, tableFor } = analyze(tree);
@@ -13356,7 +13964,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13356
13964
  confidenceKind: "structural",
13357
13965
  ...columns.length > 0 ? { columns } : {},
13358
13966
  evidence: {
13359
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13967
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13360
13968
  line: struct.line,
13361
13969
  snippet: snippet(file.content, struct.line)
13362
13970
  }
@@ -13365,7 +13973,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13365
13973
  return out;
13366
13974
  }
13367
13975
  function gormForeignKeys(file, serviceDir) {
13368
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13976
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13369
13977
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13370
13978
  const tree = parseSource11(makeGoParser4(), file.content);
13371
13979
  const { structs, models, tableFor } = analyze(tree);
@@ -13380,7 +13988,7 @@ function gormForeignKeys(file, serviceDir) {
13380
13988
  childTable,
13381
13989
  parentTable,
13382
13990
  evidence: {
13383
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13991
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13384
13992
  line,
13385
13993
  snippet: snippet(file.content, line)
13386
13994
  }
@@ -13415,15 +14023,131 @@ function gormForeignKeys(file, serviceDir) {
13415
14023
  return out;
13416
14024
  }
13417
14025
 
14026
+ // src/extract/calls/efcore.ts
14027
+ init_cjs_shims();
14028
+ var import_node_path56 = __toESM(require("path"), 1);
14029
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14030
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14031
+ var import_types43 = require("@neat.is/types");
14032
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
14033
+ var PARSE_CHUNK12 = 16384;
14034
+ function makeCsParser() {
14035
+ const p = new import_tree_sitter16.default();
14036
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
14037
+ return p;
14038
+ }
14039
+ function parseSource12(parser, source) {
14040
+ return parser.parse(
14041
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
14042
+ );
14043
+ }
14044
+ function walk9(node, visit) {
14045
+ visit(node);
14046
+ for (let i = 0; i < node.namedChildCount; i++) {
14047
+ const c = node.namedChild(i);
14048
+ if (c) walk9(c, visit);
14049
+ }
14050
+ }
14051
+ function firstChildOfType(node, type) {
14052
+ for (let i = 0; i < node.namedChildCount; i++) {
14053
+ const c = node.namedChild(i);
14054
+ if (c?.type === type) return c;
14055
+ }
14056
+ return null;
14057
+ }
14058
+ function csStringLiteral(node) {
14059
+ if (node.type === "string_literal") {
14060
+ let out = "";
14061
+ for (let i = 0; i < node.namedChildCount; i++) {
14062
+ const c = node.namedChild(i);
14063
+ if (c?.type === "string_literal_content") out += c.text;
14064
+ }
14065
+ return out;
14066
+ }
14067
+ if (node.type === "verbatim_string_literal") {
14068
+ const t = node.text;
14069
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
14070
+ }
14071
+ return null;
14072
+ }
14073
+ function attributeName(attr) {
14074
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
14075
+ if (!nameNode) return null;
14076
+ const text = nameNode.text;
14077
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
14078
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
14079
+ }
14080
+ function tableFromAttribute(attr) {
14081
+ if (attributeName(attr) !== "Table") return null;
14082
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
14083
+ if (!args) return null;
14084
+ for (let i = 0; i < args.namedChildCount; i++) {
14085
+ const arg = args.namedChild(i);
14086
+ if (arg?.type !== "attribute_argument") continue;
14087
+ const first = arg.namedChild(0);
14088
+ if (!first) continue;
14089
+ const value = csStringLiteral(first);
14090
+ if (value !== null) return value;
14091
+ return null;
14092
+ }
14093
+ return null;
14094
+ }
14095
+ function tableFromToTable(call) {
14096
+ const fn = call.childForFieldName("function");
14097
+ if (fn?.type !== "member_access_expression") return null;
14098
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14099
+ if (method?.text !== "ToTable") return null;
14100
+ const args = call.childForFieldName("arguments");
14101
+ const firstArg2 = args?.namedChild(0);
14102
+ if (firstArg2?.type !== "argument") return null;
14103
+ const value = firstArg2.namedChild(0);
14104
+ return value ? csStringLiteral(value) : null;
14105
+ }
14106
+ function efcoreEndpointsFromFile(file, serviceDir) {
14107
+ if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14108
+ if (!EFCORE_GATE.test(file.content)) return [];
14109
+ const tree = parseSource12(makeCsParser(), file.content);
14110
+ const out = [];
14111
+ const seen = /* @__PURE__ */ new Set();
14112
+ const push = (name, line) => {
14113
+ if (!name || seen.has(name)) return;
14114
+ seen.add(name);
14115
+ out.push({
14116
+ infraId: (0, import_types43.infraId)("sql-table", name),
14117
+ name,
14118
+ kind: "sql-table",
14119
+ edgeType: "CALLS",
14120
+ confidenceKind: "structural",
14121
+ evidence: {
14122
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14123
+ line,
14124
+ snippet: snippet(file.content, line)
14125
+ }
14126
+ });
14127
+ };
14128
+ walk9(tree.rootNode, (node) => {
14129
+ if (node.type === "attribute") {
14130
+ const table = tableFromAttribute(node);
14131
+ if (table) push(table, node.startPosition.row + 1);
14132
+ return;
14133
+ }
14134
+ if (node.type === "invocation_expression") {
14135
+ const table = tableFromToTable(node);
14136
+ if (table) push(table, node.startPosition.row + 1);
14137
+ }
14138
+ });
14139
+ return out;
14140
+ }
14141
+
13418
14142
  // src/extract/calls/index.ts
13419
14143
  function edgeTypeFromEndpoint(ep) {
13420
14144
  switch (ep.edgeType) {
13421
14145
  case "PUBLISHES_TO":
13422
- return import_types43.EdgeType.PUBLISHES_TO;
14146
+ return import_types44.EdgeType.PUBLISHES_TO;
13423
14147
  case "CONSUMES_FROM":
13424
- return import_types43.EdgeType.CONSUMES_FROM;
14148
+ return import_types44.EdgeType.CONSUMES_FROM;
13425
14149
  default:
13426
- return import_types43.EdgeType.CALLS;
14150
+ return import_types44.EdgeType.CALLS;
13427
14151
  }
13428
14152
  }
13429
14153
  function isAwsKind(kind) {
@@ -13473,6 +14197,11 @@ async function addExternalEndpointEdges(graph, services) {
13473
14197
  } catch (err) {
13474
14198
  recordExtractionError("laravel eloquent extraction", file.path, err);
13475
14199
  }
14200
+ try {
14201
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14202
+ } catch (err) {
14203
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14204
+ }
13476
14205
  }
13477
14206
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13478
14207
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -13483,7 +14212,7 @@ async function addExternalEndpointEdges(graph, services) {
13483
14212
  if (!graph.hasNode(ep.infraId)) {
13484
14213
  const node = {
13485
14214
  id: ep.infraId,
13486
- type: import_types43.NodeType.InfraNode,
14215
+ type: import_types44.NodeType.InfraNode,
13487
14216
  name: ep.name,
13488
14217
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
13489
14218
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -13496,21 +14225,21 @@ async function addExternalEndpointEdges(graph, services) {
13496
14225
  }
13497
14226
  if (ep.columns && ep.columns.length > 0) {
13498
14227
  const node = graph.getNodeAttributes(ep.infraId);
13499
- if (node.type === import_types43.NodeType.InfraNode) {
14228
+ if (node.type === import_types44.NodeType.InfraNode) {
13500
14229
  graph.replaceNodeAttributes(ep.infraId, {
13501
14230
  ...node,
13502
14231
  columns: foldColumns(
13503
14232
  node.columns,
13504
14233
  ep.columns,
13505
- import_types43.Provenance.EXTRACTED,
13506
- (0, import_types43.confidenceForExtracted)(ep.confidenceKind)
14234
+ import_types44.Provenance.EXTRACTED,
14235
+ (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
13507
14236
  )
13508
14237
  });
13509
14238
  }
13510
14239
  }
13511
14240
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
13512
14241
  const node = graph.getNodeAttributes(ep.infraId);
13513
- if (node.type === import_types43.NodeType.InfraNode) {
14242
+ if (node.type === import_types44.NodeType.InfraNode) {
13514
14243
  graph.replaceNodeAttributes(ep.infraId, {
13515
14244
  ...node,
13516
14245
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -13518,7 +14247,7 @@ async function addExternalEndpointEdges(graph, services) {
13518
14247
  }
13519
14248
  }
13520
14249
  const edgeType = edgeTypeFromEndpoint(ep);
13521
- const confidence = (0, import_types43.confidenceForExtracted)(ep.confidenceKind);
14250
+ const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
13522
14251
  const relFile = toPosix(ep.evidence.file);
13523
14252
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
13524
14253
  graph,
@@ -13528,7 +14257,7 @@ async function addExternalEndpointEdges(graph, services) {
13528
14257
  );
13529
14258
  nodesAdded += n;
13530
14259
  edgesAdded += e;
13531
- if (!(0, import_types43.passesExtractedFloor)(confidence)) {
14260
+ if (!(0, import_types44.passesExtractedFloor)(confidence)) {
13532
14261
  noteExtractedDropped({
13533
14262
  source: fileNodeId,
13534
14263
  target: ep.infraId,
@@ -13548,7 +14277,7 @@ async function addExternalEndpointEdges(graph, services) {
13548
14277
  source: fileNodeId,
13549
14278
  target: ep.infraId,
13550
14279
  type: edgeType,
13551
- provenance: import_types43.Provenance.EXTRACTED,
14280
+ provenance: import_types44.Provenance.EXTRACTED,
13552
14281
  confidence,
13553
14282
  evidence: ep.evidence
13554
14283
  };
@@ -13571,7 +14300,7 @@ async function addCallEdges(graph, services) {
13571
14300
 
13572
14301
  // src/extract/table-edges.ts
13573
14302
  init_cjs_shims();
13574
- var import_types44 = require("@neat.is/types");
14303
+ var import_types45 = require("@neat.is/types");
13575
14304
  async function addTableEdges(graph, services) {
13576
14305
  let nodesAdded = 0;
13577
14306
  let edgesAdded = 0;
@@ -13599,20 +14328,20 @@ async function addTableEdges(graph, services) {
13599
14328
  }
13600
14329
  refs.push(...modelRefs);
13601
14330
  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);
14331
+ const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14332
+ const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
13604
14333
  if (childId === parentId) continue;
13605
14334
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13606
14335
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13607
- const edgeId = (0, import_types44.extractedEdgeId)(childId, parentId, import_types44.EdgeType.REFERENCES);
14336
+ const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
13608
14337
  if (graph.hasEdge(edgeId)) continue;
13609
14338
  const edge = {
13610
14339
  id: edgeId,
13611
14340
  source: childId,
13612
14341
  target: parentId,
13613
- type: import_types44.EdgeType.REFERENCES,
13614
- provenance: import_types44.Provenance.EXTRACTED,
13615
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14342
+ type: import_types45.EdgeType.REFERENCES,
14343
+ provenance: import_types45.Provenance.EXTRACTED,
14344
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13616
14345
  evidence: ref.evidence
13617
14346
  };
13618
14347
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13625,7 +14354,7 @@ function ensureTableNode(graph, id, name) {
13625
14354
  if (graph.hasNode(id)) return 0;
13626
14355
  const node = {
13627
14356
  id,
13628
- type: import_types44.NodeType.InfraNode,
14357
+ type: import_types45.NodeType.InfraNode,
13629
14358
  name,
13630
14359
  provider: "self",
13631
14360
  kind: "sql-table"
@@ -13639,16 +14368,16 @@ init_cjs_shims();
13639
14368
 
13640
14369
  // src/extract/infra/docker-compose.ts
13641
14370
  init_cjs_shims();
13642
- var import_node_path55 = __toESM(require("path"), 1);
13643
- var import_types46 = require("@neat.is/types");
14371
+ var import_node_path57 = __toESM(require("path"), 1);
14372
+ var import_types47 = require("@neat.is/types");
13644
14373
 
13645
14374
  // src/extract/infra/shared.ts
13646
14375
  init_cjs_shims();
13647
- var import_types45 = require("@neat.is/types");
14376
+ var import_types46 = require("@neat.is/types");
13648
14377
  function makeInfraNode(kind, name, provider = "self", extras) {
13649
14378
  return {
13650
- id: (0, import_types45.infraId)(kind, name),
13651
- type: import_types45.NodeType.InfraNode,
14379
+ id: (0, import_types46.infraId)(kind, name),
14380
+ type: import_types46.NodeType.InfraNode,
13652
14381
  name,
13653
14382
  provider,
13654
14383
  kind,
@@ -13692,8 +14421,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13692
14421
  source: anchorId,
13693
14422
  target: node.id,
13694
14423
  type: edgeType,
13695
- provenance: import_types45.Provenance.EXTRACTED,
13696
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14424
+ provenance: import_types46.Provenance.EXTRACTED,
14425
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13697
14426
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13698
14427
  };
13699
14428
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13710,7 +14439,7 @@ function dependsOnList(value) {
13710
14439
  }
13711
14440
  function serviceNameToServiceNode(name, services) {
13712
14441
  for (const s of services) {
13713
- if (s.node.name === name || import_node_path55.default.basename(s.dir) === name) return s.node.id;
14442
+ if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
13714
14443
  }
13715
14444
  return null;
13716
14445
  }
@@ -13719,7 +14448,7 @@ async function addComposeInfra(graph, scanPath, services) {
13719
14448
  let edgesAdded = 0;
13720
14449
  let composePath = null;
13721
14450
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13722
- const abs = import_node_path55.default.join(scanPath, name);
14451
+ const abs = import_node_path57.default.join(scanPath, name);
13723
14452
  if (await exists(abs)) {
13724
14453
  composePath = abs;
13725
14454
  break;
@@ -13732,13 +14461,13 @@ async function addComposeInfra(graph, scanPath, services) {
13732
14461
  } catch (err) {
13733
14462
  recordExtractionError(
13734
14463
  "infra docker-compose",
13735
- import_node_path55.default.relative(scanPath, composePath),
14464
+ import_node_path57.default.relative(scanPath, composePath),
13736
14465
  err
13737
14466
  );
13738
14467
  return { nodesAdded, edgesAdded };
13739
14468
  }
13740
14469
  if (!compose?.services) return { nodesAdded, edgesAdded };
13741
- const evidenceFile = import_node_path55.default.relative(scanPath, composePath).split(import_node_path55.default.sep).join("/");
14470
+ const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
13742
14471
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13743
14472
  for (const [composeName, svc] of Object.entries(compose.services)) {
13744
14473
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13760,15 +14489,15 @@ async function addComposeInfra(graph, scanPath, services) {
13760
14489
  for (const dep of dependsOnList(svc.depends_on)) {
13761
14490
  const targetId = composeNameToNodeId.get(dep);
13762
14491
  if (!targetId) continue;
13763
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types46.EdgeType.DEPENDS_ON);
14492
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
13764
14493
  if (graph.hasEdge(edgeId)) continue;
13765
14494
  const edge = {
13766
14495
  id: edgeId,
13767
14496
  source: sourceId,
13768
14497
  target: targetId,
13769
- type: import_types46.EdgeType.DEPENDS_ON,
13770
- provenance: import_types46.Provenance.EXTRACTED,
13771
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14498
+ type: import_types47.EdgeType.DEPENDS_ON,
14499
+ provenance: import_types47.Provenance.EXTRACTED,
14500
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13772
14501
  evidence: { file: evidenceFile }
13773
14502
  };
13774
14503
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13780,9 +14509,9 @@ async function addComposeInfra(graph, scanPath, services) {
13780
14509
 
13781
14510
  // src/extract/infra/dockerfile.ts
13782
14511
  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");
14512
+ var import_node_path58 = __toESM(require("path"), 1);
14513
+ var import_node_fs25 = require("fs");
14514
+ var import_types48 = require("@neat.is/types");
13786
14515
  function readDockerfile(content) {
13787
14516
  let image = null;
13788
14517
  const ports = [];
@@ -13811,15 +14540,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13811
14540
  let nodesAdded = 0;
13812
14541
  let edgesAdded = 0;
13813
14542
  for (const service of services) {
13814
- const dockerfilePath = import_node_path56.default.join(service.dir, "Dockerfile");
14543
+ const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
13815
14544
  if (!await exists(dockerfilePath)) continue;
13816
14545
  let content;
13817
14546
  try {
13818
- content = await import_node_fs24.promises.readFile(dockerfilePath, "utf8");
14547
+ content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
13819
14548
  } catch (err) {
13820
14549
  recordExtractionError(
13821
14550
  "infra dockerfile",
13822
- import_node_path56.default.relative(scanPath, dockerfilePath),
14551
+ import_node_path58.default.relative(scanPath, dockerfilePath),
13823
14552
  err
13824
14553
  );
13825
14554
  continue;
@@ -13831,8 +14560,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13831
14560
  graph.addNode(node.id, node);
13832
14561
  nodesAdded++;
13833
14562
  }
13834
- const relDockerfile = toPosix(import_node_path56.default.relative(service.dir, dockerfilePath));
13835
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, dockerfilePath));
14563
+ const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14564
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
13836
14565
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13837
14566
  graph,
13838
14567
  service.pkg.name,
@@ -13841,15 +14570,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13841
14570
  );
13842
14571
  nodesAdded += fn;
13843
14572
  edgesAdded += fe;
13844
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types47.EdgeType.RUNS_ON);
14573
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
13845
14574
  if (!graph.hasEdge(edgeId)) {
13846
14575
  const edge = {
13847
14576
  id: edgeId,
13848
14577
  source: fileNodeId,
13849
14578
  target: node.id,
13850
- type: import_types47.EdgeType.RUNS_ON,
13851
- provenance: import_types47.Provenance.EXTRACTED,
13852
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14579
+ type: import_types48.EdgeType.RUNS_ON,
14580
+ provenance: import_types48.Provenance.EXTRACTED,
14581
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13853
14582
  evidence: {
13854
14583
  file: evidenceFile,
13855
14584
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13864,15 +14593,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13864
14593
  graph.addNode(portNode.id, portNode);
13865
14594
  nodesAdded++;
13866
14595
  }
13867
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types47.EdgeType.CONNECTS_TO);
14596
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
13868
14597
  if (graph.hasEdge(portEdgeId)) continue;
13869
14598
  const portEdge = {
13870
14599
  id: portEdgeId,
13871
14600
  source: fileNodeId,
13872
14601
  target: portNode.id,
13873
- type: import_types47.EdgeType.CONNECTS_TO,
13874
- provenance: import_types47.Provenance.EXTRACTED,
13875
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14602
+ type: import_types48.EdgeType.CONNECTS_TO,
14603
+ provenance: import_types48.Provenance.EXTRACTED,
14604
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13876
14605
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13877
14606
  };
13878
14607
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13884,23 +14613,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13884
14613
 
13885
14614
  // src/extract/infra/terraform.ts
13886
14615
  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");
14616
+ var import_node_fs26 = require("fs");
14617
+ var import_node_path59 = __toESM(require("path"), 1);
14618
+ var import_types49 = require("@neat.is/types");
13890
14619
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13891
14620
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13892
14621
  async function walkTfFiles(start, depth = 0, max = 5) {
13893
14622
  if (depth > max) return [];
13894
14623
  const out = [];
13895
- const entries = await import_node_fs25.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14624
+ const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13896
14625
  for (const entry of entries) {
13897
14626
  if (entry.isDirectory()) {
13898
14627
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
13899
- const child = import_node_path57.default.join(start, entry.name);
14628
+ const child = import_node_path59.default.join(start, entry.name);
13900
14629
  if (await isPythonVenvDir(child)) continue;
13901
14630
  out.push(...await walkTfFiles(child, depth + 1, max));
13902
14631
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
13903
- out.push(import_node_path57.default.join(start, entry.name));
14632
+ out.push(import_node_path59.default.join(start, entry.name));
13904
14633
  }
13905
14634
  }
13906
14635
  return out;
@@ -13931,8 +14660,8 @@ async function addTerraformResources(graph, scanPath) {
13931
14660
  let edgesAdded = 0;
13932
14661
  const files = await walkTfFiles(scanPath);
13933
14662
  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));
14663
+ const content = await import_node_fs26.promises.readFile(file, "utf8");
14664
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
13936
14665
  const resources = [];
13937
14666
  const byKey = /* @__PURE__ */ new Map();
13938
14667
  RESOURCE_RE.lastIndex = 0;
@@ -13967,16 +14696,16 @@ async function addTerraformResources(graph, scanPath) {
13967
14696
  if (!target) continue;
13968
14697
  if (seen.has(target.nodeId)) continue;
13969
14698
  seen.add(target.nodeId);
13970
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types48.EdgeType.DEPENDS_ON);
14699
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
13971
14700
  if (graph.hasEdge(edgeId)) continue;
13972
14701
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13973
14702
  const edge = {
13974
14703
  id: edgeId,
13975
14704
  source: resource.nodeId,
13976
14705
  target: target.nodeId,
13977
- type: import_types48.EdgeType.DEPENDS_ON,
13978
- provenance: import_types48.Provenance.EXTRACTED,
13979
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
14706
+ type: import_types49.EdgeType.DEPENDS_ON,
14707
+ provenance: import_types49.Provenance.EXTRACTED,
14708
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
13980
14709
  evidence: { file: evidenceFile, line, snippet: key }
13981
14710
  };
13982
14711
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13989,8 +14718,8 @@ async function addTerraformResources(graph, scanPath) {
13989
14718
 
13990
14719
  // src/extract/infra/k8s.ts
13991
14720
  init_cjs_shims();
13992
- var import_node_fs26 = require("fs");
13993
- var import_node_path58 = __toESM(require("path"), 1);
14721
+ var import_node_fs27 = require("fs");
14722
+ var import_node_path60 = __toESM(require("path"), 1);
13994
14723
  var import_yaml3 = require("yaml");
13995
14724
  var K8S_KIND_TO_INFRA_KIND = {
13996
14725
  Service: "k8s-service",
@@ -14004,15 +14733,15 @@ var K8S_KIND_TO_INFRA_KIND = {
14004
14733
  async function walkYamlFiles2(start, depth = 0, max = 5) {
14005
14734
  if (depth > max) return [];
14006
14735
  const out = [];
14007
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14736
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14008
14737
  for (const entry of entries) {
14009
14738
  if (entry.isDirectory()) {
14010
14739
  if (IGNORED_DIRS.has(entry.name)) continue;
14011
- const child = import_node_path58.default.join(start, entry.name);
14740
+ const child = import_node_path60.default.join(start, entry.name);
14012
14741
  if (await isPythonVenvDir(child)) continue;
14013
14742
  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));
14743
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
14744
+ out.push(import_node_path60.default.join(start, entry.name));
14016
14745
  }
14017
14746
  }
14018
14747
  return out;
@@ -14021,7 +14750,7 @@ async function addK8sResources(graph, scanPath) {
14021
14750
  let nodesAdded = 0;
14022
14751
  const files = await walkYamlFiles2(scanPath);
14023
14752
  for (const file of files) {
14024
- const content = await import_node_fs26.promises.readFile(file, "utf8");
14753
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
14025
14754
  let docs;
14026
14755
  try {
14027
14756
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14045,16 +14774,16 @@ async function addK8sResources(graph, scanPath) {
14045
14774
 
14046
14775
  // src/extract/infra/cloudflare.ts
14047
14776
  init_cjs_shims();
14048
- var import_node_fs27 = require("fs");
14049
- var import_node_path59 = __toESM(require("path"), 1);
14777
+ var import_node_fs28 = require("fs");
14778
+ var import_node_path61 = __toESM(require("path"), 1);
14050
14779
  var import_smol_toml3 = require("smol-toml");
14051
- var import_types49 = require("@neat.is/types");
14780
+ var import_types50 = require("@neat.is/types");
14052
14781
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14053
14782
  async function readWranglerConfig(dir) {
14054
14783
  for (const filename of WRANGLER_FILENAMES) {
14055
- const abs = import_node_path59.default.join(dir, filename);
14784
+ const abs = import_node_path61.default.join(dir, filename);
14056
14785
  if (!await exists(abs)) continue;
14057
- const raw = await import_node_fs27.promises.readFile(abs, "utf8");
14786
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14058
14787
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14059
14788
  return { config, relFile: filename, raw };
14060
14789
  }
@@ -14096,8 +14825,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14096
14825
  source: anchorId,
14097
14826
  target: node.id,
14098
14827
  type: edgeType,
14099
- provenance: import_types49.Provenance.EXTRACTED,
14100
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14828
+ provenance: import_types50.Provenance.EXTRACTED,
14829
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14101
14830
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14102
14831
  };
14103
14832
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14115,11 +14844,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14115
14844
  try {
14116
14845
  read = await readWranglerConfig(service.dir);
14117
14846
  } catch (err) {
14118
- recordExtractionError("infra cloudflare", import_node_path59.default.relative(scanPath, service.dir), err);
14847
+ recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
14119
14848
  continue;
14120
14849
  }
14121
14850
  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)));
14851
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
14123
14852
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14124
14853
  }
14125
14854
  for (const worker of discovered) {
@@ -14131,7 +14860,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14131
14860
  }
14132
14861
  let anchorId = service.node.id;
14133
14862
  if (config.main) {
14134
- const entryRelPath = toPosix(import_node_path59.default.normalize(config.main));
14863
+ const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
14135
14864
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14136
14865
  graph,
14137
14866
  service.pkg.name,
@@ -14158,15 +14887,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14158
14887
  nodesAdded++;
14159
14888
  }
14160
14889
  if (runtimeNode.id !== anchorId) {
14161
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types49.EdgeType.RUNS_ON);
14890
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
14162
14891
  if (!graph.hasEdge(runsOnId)) {
14163
14892
  const edge = {
14164
14893
  id: runsOnId,
14165
14894
  source: anchorId,
14166
14895
  target: runtimeNode.id,
14167
- type: import_types49.EdgeType.RUNS_ON,
14168
- provenance: import_types49.Provenance.EXTRACTED,
14169
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14896
+ type: import_types50.EdgeType.RUNS_ON,
14897
+ provenance: import_types50.Provenance.EXTRACTED,
14898
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14170
14899
  evidence: {
14171
14900
  file: evidenceFile,
14172
14901
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14180,7 +14909,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14180
14909
  const result = addResourceEdge(
14181
14910
  graph,
14182
14911
  anchorId,
14183
- import_types49.EdgeType.CONNECTS_TO,
14912
+ import_types50.EdgeType.CONNECTS_TO,
14184
14913
  "cloudflare-route",
14185
14914
  route,
14186
14915
  evidenceFile,
@@ -14204,7 +14933,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14204
14933
  const result = addResourceEdge(
14205
14934
  graph,
14206
14935
  anchorId,
14207
- import_types49.EdgeType.DEPENDS_ON,
14936
+ import_types50.EdgeType.DEPENDS_ON,
14208
14937
  group.kind,
14209
14938
  name,
14210
14939
  evidenceFile,
@@ -14218,7 +14947,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14218
14947
  const result = addResourceEdge(
14219
14948
  graph,
14220
14949
  anchorId,
14221
- import_types49.EdgeType.DEPENDS_ON,
14950
+ import_types50.EdgeType.DEPENDS_ON,
14222
14951
  "cloudflare-cron",
14223
14952
  cron,
14224
14953
  evidenceFile,
@@ -14231,7 +14960,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14231
14960
  const result = addResourceEdge(
14232
14961
  graph,
14233
14962
  anchorId,
14234
- import_types49.EdgeType.DEPENDS_ON,
14963
+ import_types50.EdgeType.DEPENDS_ON,
14235
14964
  "cloudflare-env-var",
14236
14965
  varName,
14237
14966
  evidenceFile,
@@ -14244,15 +14973,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14244
14973
  if (!svc.service) continue;
14245
14974
  const target = workerIndex.get(svc.service);
14246
14975
  if (target && target.anchorId !== anchorId) {
14247
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types49.EdgeType.CALLS);
14976
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
14248
14977
  if (!graph.hasEdge(edgeId)) {
14249
14978
  const edge = {
14250
14979
  id: edgeId,
14251
14980
  source: anchorId,
14252
14981
  target: target.anchorId,
14253
- type: import_types49.EdgeType.CALLS,
14254
- provenance: import_types49.Provenance.EXTRACTED,
14255
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14982
+ type: import_types50.EdgeType.CALLS,
14983
+ provenance: import_types50.Provenance.EXTRACTED,
14984
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14256
14985
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14257
14986
  };
14258
14987
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14263,7 +14992,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14263
14992
  const result = addResourceEdge(
14264
14993
  graph,
14265
14994
  anchorId,
14266
- import_types49.EdgeType.DEPENDS_ON,
14995
+ import_types50.EdgeType.DEPENDS_ON,
14267
14996
  "cloudflare-service-binding",
14268
14997
  svc.service,
14269
14998
  evidenceFile,
@@ -14278,24 +15007,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14278
15007
 
14279
15008
  // src/extract/infra/vercel.ts
14280
15009
  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");
15010
+ var import_node_fs29 = require("fs");
15011
+ var import_node_path62 = __toESM(require("path"), 1);
15012
+ var import_types51 = require("@neat.is/types");
14284
15013
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14285
15014
  async function readVercelConfig(dir) {
14286
15015
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14287
- const abs = import_node_path60.default.join(dir, filename);
15016
+ const abs = import_node_path62.default.join(dir, filename);
14288
15017
  if (!await exists(abs)) continue;
14289
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
15018
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14290
15019
  const config = JSON.parse(maskCommentsInSource(raw));
14291
15020
  return { config, relFile: filename, raw };
14292
15021
  }
14293
15022
  return null;
14294
15023
  }
14295
15024
  async function readLinkedProjectName(dir) {
14296
- const abs = import_node_path60.default.join(dir, ".vercel", "project.json");
15025
+ const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
14297
15026
  if (!await exists(abs)) return void 0;
14298
- const parsed = JSON.parse(await import_node_fs28.promises.readFile(abs, "utf8"));
15027
+ const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
14299
15028
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14300
15029
  }
14301
15030
  function routeSource(route) {
@@ -14311,7 +15040,7 @@ async function addVercelServices(graph, services, scanPath) {
14311
15040
  read = await readVercelConfig(service.dir);
14312
15041
  projectName = await readLinkedProjectName(service.dir);
14313
15042
  } catch (err) {
14314
- recordExtractionError("infra vercel", import_node_path60.default.relative(scanPath, service.dir), err);
15043
+ recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
14315
15044
  continue;
14316
15045
  }
14317
15046
  if (!read && !projectName) continue;
@@ -14327,7 +15056,7 @@ async function addVercelServices(graph, services, scanPath) {
14327
15056
  const anchorId = service.node.id;
14328
15057
  if (!read) continue;
14329
15058
  const { config, relFile, raw } = read;
14330
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, import_node_path60.default.join(service.dir, relFile)));
15059
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
14331
15060
  const add = (edgeType, kind, name) => {
14332
15061
  if (!name) return;
14333
15062
  const result = emitPlatformResourceEdge(
@@ -14343,12 +15072,12 @@ async function addVercelServices(graph, services, scanPath) {
14343
15072
  nodesAdded += result.nodesAdded;
14344
15073
  edgesAdded += result.edgesAdded;
14345
15074
  };
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);
15075
+ add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
15076
+ for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15077
+ for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15078
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14350
15079
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14351
- add(import_types50.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15080
+ add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14352
15081
  }
14353
15082
  }
14354
15083
  return { nodesAdded, edgesAdded };
@@ -14356,16 +15085,16 @@ async function addVercelServices(graph, services, scanPath) {
14356
15085
 
14357
15086
  // src/extract/infra/railway.ts
14358
15087
  init_cjs_shims();
14359
- var import_node_fs29 = require("fs");
14360
- var import_node_path61 = __toESM(require("path"), 1);
15088
+ var import_node_fs30 = require("fs");
15089
+ var import_node_path63 = __toESM(require("path"), 1);
14361
15090
  var import_smol_toml4 = require("smol-toml");
14362
- var import_types51 = require("@neat.is/types");
15091
+ var import_types52 = require("@neat.is/types");
14363
15092
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14364
15093
  async function readRailwayConfig(dir) {
14365
15094
  for (const filename of RAILWAY_FILENAMES) {
14366
- const abs = import_node_path61.default.join(dir, filename);
15095
+ const abs = import_node_path63.default.join(dir, filename);
14367
15096
  if (!await exists(abs)) continue;
14368
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15097
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14369
15098
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14370
15099
  return { config, relFile: filename, raw };
14371
15100
  }
@@ -14379,7 +15108,7 @@ async function addRailwayServices(graph, services, scanPath) {
14379
15108
  try {
14380
15109
  read = await readRailwayConfig(service.dir);
14381
15110
  } catch (err) {
14382
- recordExtractionError("infra railway", import_node_path61.default.relative(scanPath, service.dir), err);
15111
+ recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
14383
15112
  continue;
14384
15113
  }
14385
15114
  if (!read) continue;
@@ -14389,7 +15118,7 @@ async function addRailwayServices(graph, services, scanPath) {
14389
15118
  }
14390
15119
  const anchorId = service.node.id;
14391
15120
  const { config, relFile, raw } = read;
14392
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, relFile)));
15121
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
14393
15122
  const add = (edgeType, kind, name) => {
14394
15123
  if (!name) return;
14395
15124
  const result = emitPlatformResourceEdge(
@@ -14405,24 +15134,24 @@ async function addRailwayServices(graph, services, scanPath) {
14405
15134
  nodesAdded += result.nodesAdded;
14406
15135
  edgesAdded += result.edgesAdded;
14407
15136
  };
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);
15137
+ add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15138
+ add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15139
+ add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14411
15140
  }
14412
15141
  return { nodesAdded, edgesAdded };
14413
15142
  }
14414
15143
 
14415
15144
  // src/extract/infra/supabase.ts
14416
15145
  init_cjs_shims();
14417
- var import_node_fs30 = require("fs");
14418
- var import_node_path62 = __toESM(require("path"), 1);
15146
+ var import_node_fs31 = require("fs");
15147
+ var import_node_path64 = __toESM(require("path"), 1);
14419
15148
  var import_smol_toml5 = require("smol-toml");
14420
- var import_types52 = require("@neat.is/types");
15149
+ var import_types53 = require("@neat.is/types");
14421
15150
  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);
15151
+ const relFile = import_node_path64.default.join("supabase", "config.toml");
15152
+ const abs = import_node_path64.default.join(dir, relFile);
14424
15153
  if (!await exists(abs)) return null;
14425
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15154
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14426
15155
  const config = (0, import_smol_toml5.parse)(raw);
14427
15156
  return { config, relFile, raw };
14428
15157
  }
@@ -14434,7 +15163,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14434
15163
  try {
14435
15164
  read = await readSupabaseConfig(service.dir);
14436
15165
  } catch (err) {
14437
- recordExtractionError("infra supabase", import_node_path62.default.relative(scanPath, service.dir), err);
15166
+ recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
14438
15167
  continue;
14439
15168
  }
14440
15169
  if (!read) continue;
@@ -14449,7 +15178,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14449
15178
  });
14450
15179
  }
14451
15180
  const anchorId = service.node.id;
14452
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15181
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14453
15182
  const add = (edgeType, kind, name) => {
14454
15183
  if (!name) return;
14455
15184
  const result = emitPlatformResourceEdge(
@@ -14465,10 +15194,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14465
15194
  nodesAdded += result.nodesAdded;
14466
15195
  edgesAdded += result.edgesAdded;
14467
15196
  };
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");
15197
+ add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15198
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15199
+ if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15200
+ if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
14472
15201
  }
14473
15202
  return { nodesAdded, edgesAdded };
14474
15203
  }
@@ -14491,14 +15220,14 @@ async function addInfra(graph, scanPath, services) {
14491
15220
 
14492
15221
  // src/extract/zod-shapes.ts
14493
15222
  init_cjs_shims();
14494
- var import_node_path63 = __toESM(require("path"), 1);
14495
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15223
+ var import_node_path65 = __toESM(require("path"), 1);
15224
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14496
15225
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
14497
- var import_types53 = require("@neat.is/types");
15226
+ var import_types54 = require("@neat.is/types");
14498
15227
  var ZOD_IMPORT_RE = /\bzod\b/;
14499
15228
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
14500
15229
  function parserForExt4(ext) {
14501
- const p = new import_tree_sitter16.default();
15230
+ const p = new import_tree_sitter17.default();
14502
15231
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
14503
15232
  return p;
14504
15233
  }
@@ -14586,7 +15315,7 @@ function topLevelSchemas(root) {
14586
15315
  }
14587
15316
  function zodShapesFromFile(file, serviceDir) {
14588
15317
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14589
- const tree = parseSource3(parserForExt4(import_node_path63.default.extname(file.path)), file.content);
15318
+ const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
14590
15319
  const out = [];
14591
15320
  const seen = /* @__PURE__ */ new Set();
14592
15321
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14600,11 +15329,11 @@ function zodShapesFromFile(file, serviceDir) {
14600
15329
  seen.add(name);
14601
15330
  const line = call.startPosition.row + 1;
14602
15331
  out.push({
14603
- infraId: (0, import_types53.infraId)("zod-schema", name),
15332
+ infraId: (0, import_types54.infraId)("zod-schema", name),
14604
15333
  name,
14605
15334
  fields,
14606
15335
  evidence: {
14607
- file: import_node_path63.default.relative(serviceDir, file.path),
15336
+ file: import_node_path65.default.relative(serviceDir, file.path),
14608
15337
  line,
14609
15338
  snippet: snippet(file.content, line)
14610
15339
  }
@@ -14635,7 +15364,7 @@ async function addZodShapes(graph, services) {
14635
15364
  if (!graph.hasNode(shape.infraId)) {
14636
15365
  const node = {
14637
15366
  id: shape.infraId,
14638
- type: import_types53.NodeType.InfraNode,
15367
+ type: import_types54.NodeType.InfraNode,
14639
15368
  name: shape.name,
14640
15369
  provider: "self",
14641
15370
  kind: "zod-schema"
@@ -14645,14 +15374,14 @@ async function addZodShapes(graph, services) {
14645
15374
  }
14646
15375
  if (shape.fields.length > 0) {
14647
15376
  const node = graph.getNodeAttributes(shape.infraId);
14648
- if (node.type === import_types53.NodeType.InfraNode) {
15377
+ if (node.type === import_types54.NodeType.InfraNode) {
14649
15378
  graph.replaceNodeAttributes(shape.infraId, {
14650
15379
  ...node,
14651
15380
  columns: foldColumns(
14652
15381
  node.columns,
14653
15382
  shape.fields,
14654
- import_types53.Provenance.EXTRACTED,
14655
- (0, import_types53.confidenceForExtracted)("structural")
15383
+ import_types54.Provenance.EXTRACTED,
15384
+ (0, import_types54.confidenceForExtracted)("structural")
14656
15385
  )
14657
15386
  });
14658
15387
  }
@@ -14666,15 +15395,15 @@ async function addZodShapes(graph, services) {
14666
15395
  );
14667
15396
  nodesAdded += n;
14668
15397
  edgesAdded += e;
14669
- const edgeId = (0, import_types53.extractedEdgeId)(fileNodeId, shape.infraId, import_types53.EdgeType.CONTAINS);
15398
+ const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
14670
15399
  if (!graph.hasEdge(edgeId)) {
14671
15400
  const edge = {
14672
15401
  id: edgeId,
14673
15402
  source: fileNodeId,
14674
15403
  target: shape.infraId,
14675
- type: import_types53.EdgeType.CONTAINS,
14676
- provenance: import_types53.Provenance.EXTRACTED,
14677
- confidence: (0, import_types53.confidenceForExtracted)("structural"),
15404
+ type: import_types54.EdgeType.CONTAINS,
15405
+ provenance: import_types54.Provenance.EXTRACTED,
15406
+ confidence: (0, import_types54.confidenceForExtracted)("structural"),
14678
15407
  evidence: shape.evidence
14679
15408
  };
14680
15409
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14688,7 +15417,7 @@ async function addZodShapes(graph, services) {
14688
15417
 
14689
15418
  // src/extract/firestore-rules.ts
14690
15419
  init_cjs_shims();
14691
- var import_types54 = require("@neat.is/types");
15420
+ var import_types55 = require("@neat.is/types");
14692
15421
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14693
15422
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14694
15423
  function stripComments(src) {
@@ -14828,7 +15557,7 @@ async function addFirestoreRules(graph, services) {
14828
15557
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14829
15558
  graph.forEachNode((id, attrs) => {
14830
15559
  const node = attrs;
14831
- if (node.type !== import_types54.NodeType.InfraNode) return;
15560
+ if (node.type !== import_types55.NodeType.InfraNode) return;
14832
15561
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14833
15562
  const fields = guards.get(collectionKeyFromName(node.name));
14834
15563
  if (!fields || fields.size === 0) return;
@@ -14841,17 +15570,17 @@ async function addFirestoreRules(graph, services) {
14841
15570
  }
14842
15571
 
14843
15572
  // src/extract/index.ts
14844
- var import_node_path65 = __toESM(require("path"), 1);
15573
+ var import_node_path67 = __toESM(require("path"), 1);
14845
15574
 
14846
15575
  // src/extract/retire.ts
14847
15576
  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");
15577
+ var import_node_fs32 = require("fs");
15578
+ var import_node_path66 = __toESM(require("path"), 1);
15579
+ var import_types56 = require("@neat.is/types");
14851
15580
  function dropOrphanedFileNodes(graph) {
14852
15581
  const orphans = [];
14853
15582
  graph.forEachNode((id, attrs) => {
14854
- if (attrs.type !== import_types55.NodeType.FileNode) return;
15583
+ if (attrs.type !== import_types56.NodeType.FileNode) return;
14855
15584
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14856
15585
  orphans.push(id);
14857
15586
  }
@@ -14864,14 +15593,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14864
15593
  const bases = [scanPath, ...serviceDirs];
14865
15594
  graph.forEachEdge((id, attrs) => {
14866
15595
  const edge = attrs;
14867
- if (edge.provenance !== import_types55.Provenance.EXTRACTED) return;
15596
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
14868
15597
  const evidenceFile = edge.evidence?.file;
14869
15598
  if (!evidenceFile) return;
14870
- if (import_node_path64.default.isAbsolute(evidenceFile)) {
14871
- if (!(0, import_node_fs31.existsSync)(evidenceFile)) toDrop.push(id);
15599
+ if (import_node_path66.default.isAbsolute(evidenceFile)) {
15600
+ if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
14872
15601
  return;
14873
15602
  }
14874
- const found = bases.some((base) => (0, import_node_fs31.existsSync)(import_node_path64.default.join(base, evidenceFile)));
15603
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
14875
15604
  if (!found) toDrop.push(id);
14876
15605
  });
14877
15606
  for (const id of toDrop) graph.dropEdge(id);
@@ -14928,7 +15657,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14928
15657
  }
14929
15658
  const droppedEntries = drainDroppedExtracted();
14930
15659
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14931
- const rejectedPath = import_node_path65.default.join(import_node_path65.default.dirname(opts.errorsPath), "rejected.ndjson");
15660
+ const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
14932
15661
  try {
14933
15662
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14934
15663
  } catch (err) {
@@ -14962,9 +15691,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14962
15691
 
14963
15692
  // src/persist.ts
14964
15693
  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");
15694
+ var import_node_fs33 = require("fs");
15695
+ var import_node_path68 = __toESM(require("path"), 1);
15696
+ var import_types57 = require("@neat.is/types");
14968
15697
  var SCHEMA_VERSION = 7;
14969
15698
  function migrateV1ToV2(payload) {
14970
15699
  const nodes = payload.graph.nodes;
@@ -14988,7 +15717,7 @@ function migrateV5ToV6(payload) {
14988
15717
  if (Array.isArray(nodes)) {
14989
15718
  for (const node of nodes) {
14990
15719
  const attrs = node.attributes;
14991
- if (!attrs || attrs.type !== import_types56.NodeType.InfraNode) continue;
15720
+ if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
14992
15721
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14993
15722
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14994
15723
  }
@@ -15004,12 +15733,12 @@ function migrateV2ToV3(payload) {
15004
15733
  for (const edge of edges) {
15005
15734
  const attrs = edge.attributes;
15006
15735
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
15007
- attrs.provenance = import_types56.Provenance.OBSERVED;
15736
+ attrs.provenance = import_types57.Provenance.OBSERVED;
15008
15737
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
15009
15738
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
15010
15739
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
15011
15740
  if (type && source && target) {
15012
- const newId = (0, import_types56.observedEdgeId)(source, target, type);
15741
+ const newId = (0, import_types57.observedEdgeId)(source, target, type);
15013
15742
  attrs.id = newId;
15014
15743
  if (edge.key) edge.key = newId;
15015
15744
  }
@@ -15018,7 +15747,7 @@ function migrateV2ToV3(payload) {
15018
15747
  return { ...payload, schemaVersion: 3 };
15019
15748
  }
15020
15749
  async function ensureDir(filePath) {
15021
- await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(filePath), { recursive: true });
15750
+ await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
15022
15751
  }
15023
15752
  async function saveGraphToDisk(graph, outPath) {
15024
15753
  await ensureDir(outPath);
@@ -15028,13 +15757,13 @@ async function saveGraphToDisk(graph, outPath) {
15028
15757
  graph: graph.export()
15029
15758
  };
15030
15759
  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);
15760
+ await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15761
+ await import_node_fs33.promises.rename(tmp, outPath);
15033
15762
  }
15034
15763
  async function loadGraphFromDisk(graph, outPath) {
15035
15764
  let raw;
15036
15765
  try {
15037
- raw = await import_node_fs32.promises.readFile(outPath, "utf8");
15766
+ raw = await import_node_fs33.promises.readFile(outPath, "utf8");
15038
15767
  } catch (err) {
15039
15768
  if (err.code === "ENOENT") return;
15040
15769
  throw err;
@@ -15110,19 +15839,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
15110
15839
  init_cjs_shims();
15111
15840
  var import_fastify2 = __toESM(require("fastify"), 1);
15112
15841
  var import_cors = __toESM(require("@fastify/cors"), 1);
15113
- var import_types91 = require("@neat.is/types");
15842
+ var import_types92 = require("@neat.is/types");
15114
15843
 
15115
15844
  // src/extend/index.ts
15116
15845
  init_cjs_shims();
15117
- var import_node_fs34 = require("fs");
15118
- var import_node_path68 = __toESM(require("path"), 1);
15846
+ var import_node_fs35 = require("fs");
15847
+ var import_node_path70 = __toESM(require("path"), 1);
15119
15848
  var import_node_os2 = __toESM(require("os"), 1);
15120
15849
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
15121
15850
 
15122
15851
  // src/installers/package-manager.ts
15123
15852
  init_cjs_shims();
15124
- var import_node_fs33 = require("fs");
15125
- var import_node_path67 = __toESM(require("path"), 1);
15853
+ var import_node_fs34 = require("fs");
15854
+ var import_node_path69 = __toESM(require("path"), 1);
15126
15855
  var import_node_child_process = require("child_process");
15127
15856
  var LOCKFILE_PRIORITY = [
15128
15857
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -15137,29 +15866,29 @@ var LOCKFILE_PRIORITY = [
15137
15866
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
15138
15867
  async function exists2(p) {
15139
15868
  try {
15140
- await import_node_fs33.promises.access(p);
15869
+ await import_node_fs34.promises.access(p);
15141
15870
  return true;
15142
15871
  } catch {
15143
15872
  return false;
15144
15873
  }
15145
15874
  }
15146
15875
  async function detectPackageManager(serviceDir) {
15147
- let dir = import_node_path67.default.resolve(serviceDir);
15876
+ let dir = import_node_path69.default.resolve(serviceDir);
15148
15877
  const stops = /* @__PURE__ */ new Set();
15149
15878
  for (let i = 0; i < 64; i++) {
15150
15879
  if (stops.has(dir)) break;
15151
15880
  stops.add(dir);
15152
15881
  for (const candidate of LOCKFILE_PRIORITY) {
15153
- const lockPath = import_node_path67.default.join(dir, candidate.lockfile);
15882
+ const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
15154
15883
  if (await exists2(lockPath)) {
15155
15884
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
15156
15885
  }
15157
15886
  }
15158
- const parent = import_node_path67.default.dirname(dir);
15887
+ const parent = import_node_path69.default.dirname(dir);
15159
15888
  if (parent === dir) break;
15160
15889
  dir = parent;
15161
15890
  }
15162
- return { pm: "npm", cwd: import_node_path67.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15891
+ return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15163
15892
  }
15164
15893
  async function runPackageManagerInstall(cmd) {
15165
15894
  return new Promise((resolve) => {
@@ -15201,15 +15930,15 @@ ${err.message}`
15201
15930
  // src/extend/index.ts
15202
15931
  async function fileExists2(p) {
15203
15932
  try {
15204
- await import_node_fs34.promises.access(p);
15933
+ await import_node_fs35.promises.access(p);
15205
15934
  return true;
15206
15935
  } catch {
15207
15936
  return false;
15208
15937
  }
15209
15938
  }
15210
15939
  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");
15940
+ const pkgPath = import_node_path70.default.join(scanPath, "package.json");
15941
+ const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
15213
15942
  return JSON.parse(raw);
15214
15943
  }
15215
15944
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -15222,27 +15951,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
15222
15951
  ]);
15223
15952
  async function findHookFiles(scanPath) {
15224
15953
  const found = [];
15225
- const walk9 = async (dir) => {
15226
- const entries = await import_node_fs34.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15954
+ const walk10 = async (dir) => {
15955
+ const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15227
15956
  for (const entry of entries) {
15228
15957
  if (entry.isDirectory()) {
15229
15958
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
15230
- await walk9(import_node_path68.default.join(dir, entry.name));
15959
+ await walk10(import_node_path70.default.join(dir, entry.name));
15231
15960
  } else if (entry.isFile()) {
15232
15961
  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("/"));
15962
+ const rel = import_node_path70.default.relative(scanPath, import_node_path70.default.join(dir, entry.name));
15963
+ found.push(rel.split(import_node_path70.default.sep).join("/"));
15235
15964
  }
15236
15965
  }
15237
15966
  }
15238
15967
  };
15239
- await walk9(scanPath);
15968
+ await walk10(scanPath);
15240
15969
  return found.sort();
15241
15970
  }
15242
15971
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15243
15972
  let fallback = null;
15244
15973
  for (const file of hookFiles) {
15245
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(scanPath, file), "utf8");
15974
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
15246
15975
  const patched = splicedContent(content, snippet2);
15247
15976
  if (patched !== null) return { file, content, patched };
15248
15977
  if (fallback === null) fallback = { file, content };
@@ -15250,12 +15979,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15250
15979
  return { file: fallback.file, content: fallback.content, patched: null };
15251
15980
  }
15252
15981
  function extendLogPath() {
15253
- return process.env.NEAT_EXTEND_LOG ?? import_node_path68.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15982
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path70.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15254
15983
  }
15255
15984
  async function appendExtendLog(entry) {
15256
15985
  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");
15986
+ await import_node_fs35.promises.mkdir(import_node_path70.default.dirname(logPath), { recursive: true });
15987
+ await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
15259
15988
  }
15260
15989
  function splicedContent(fileContent, snippet2) {
15261
15990
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -15313,7 +16042,7 @@ function lookupInstrumentation(library, installedVersion) {
15313
16042
  }
15314
16043
  async function describeProjectInstrumentation(ctx) {
15315
16044
  const hookFiles = await findHookFiles(ctx.scanPath);
15316
- const envNeat = await fileExists2(import_node_path68.default.join(ctx.scanPath, ".env.neat"));
16045
+ const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
15317
16046
  const registryInstrPackages = new Set(
15318
16047
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
15319
16048
  );
@@ -15335,7 +16064,7 @@ async function applyExtension(ctx, args, options) {
15335
16064
  );
15336
16065
  }
15337
16066
  for (const file of hookFiles) {
15338
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(ctx.scanPath, file), "utf8");
16067
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
15339
16068
  if (content.includes(args.registration_snippet)) {
15340
16069
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
15341
16070
  }
@@ -15347,18 +16076,18 @@ async function applyExtension(ctx, args, options) {
15347
16076
  );
15348
16077
  }
15349
16078
  const primaryFile = primary.file;
15350
- const primaryPath = import_node_path68.default.join(ctx.scanPath, primaryFile);
16079
+ const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
15351
16080
  const filesTouched = [];
15352
16081
  const depsAdded = [];
15353
- const pkgPath = import_node_path68.default.join(ctx.scanPath, "package.json");
16082
+ const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
15354
16083
  const pkg = await readPackageJson(ctx.scanPath);
15355
16084
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
15356
16085
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
15357
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16086
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15358
16087
  filesTouched.push("package.json");
15359
16088
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
15360
16089
  }
15361
- await import_node_fs34.promises.writeFile(primaryPath, primary.patched, "utf8");
16090
+ await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
15362
16091
  filesTouched.push(primaryFile);
15363
16092
  const cmd = await detectPackageManager(ctx.scanPath);
15364
16093
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -15389,7 +16118,7 @@ async function dryRunExtension(ctx, args) {
15389
16118
  };
15390
16119
  }
15391
16120
  for (const file of hookFiles) {
15392
- const content = await import_node_fs34.promises.readFile(import_node_path68.default.join(ctx.scanPath, file), "utf8");
16121
+ const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
15393
16122
  if (content.includes(args.registration_snippet)) {
15394
16123
  return {
15395
16124
  library: args.library,
@@ -15424,28 +16153,28 @@ async function rollbackExtension(ctx, args) {
15424
16153
  if (!await fileExists2(logPath)) {
15425
16154
  return { undone: false, message: "no apply found for library" };
15426
16155
  }
15427
- const raw = await import_node_fs34.promises.readFile(logPath, "utf8");
16156
+ const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
15428
16157
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
15429
16158
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
15430
16159
  if (!match) {
15431
16160
  return { undone: false, message: "no apply found for library" };
15432
16161
  }
15433
- const pkgPath = import_node_path68.default.join(ctx.scanPath, "package.json");
16162
+ const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
15434
16163
  if (await fileExists2(pkgPath)) {
15435
16164
  const pkg = await readPackageJson(ctx.scanPath);
15436
16165
  if (pkg.dependencies?.[match.instrumentation_package]) {
15437
16166
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
15438
16167
  pkg.dependencies = rest;
15439
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16168
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15440
16169
  }
15441
16170
  }
15442
16171
  const hookFiles = await findHookFiles(ctx.scanPath);
15443
16172
  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");
16173
+ const filePath = import_node_path70.default.join(ctx.scanPath, file);
16174
+ const content = await import_node_fs35.promises.readFile(filePath, "utf8");
15446
16175
  if (content.includes(match.registration_snippet)) {
15447
16176
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
15448
- await import_node_fs34.promises.writeFile(filePath, filtered, "utf8");
16177
+ await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
15449
16178
  break;
15450
16179
  }
15451
16180
  }
@@ -15457,39 +16186,39 @@ async function rollbackExtension(ctx, args) {
15457
16186
 
15458
16187
  // src/divergences.ts
15459
16188
  init_cjs_shims();
15460
- var import_types57 = require("@neat.is/types");
16189
+ var import_types58 = require("@neat.is/types");
15461
16190
  function bucketKey(source, target, type) {
15462
16191
  return `${type}|${source}|${target}`;
15463
16192
  }
15464
16193
  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);
16194
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16195
+ const parsed = (0, import_types58.parseFileId)(edge.source);
15467
16196
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
15468
16197
  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);
16198
+ if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16199
+ return (0, import_types58.serviceId)(parsed.service);
15471
16200
  }
15472
16201
  function bucketEdges(graph) {
15473
16202
  const buckets2 = /* @__PURE__ */ new Map();
15474
16203
  graph.forEachEdge((id, attrs) => {
15475
16204
  const e = attrs;
15476
- const parsed = (0, import_types57.parseEdgeId)(id);
16205
+ const parsed = (0, import_types58.parseEdgeId)(id);
15477
16206
  const provenance = parsed?.provenance ?? e.provenance;
15478
16207
  const source = bucketSourceFor(graph, e);
15479
16208
  const key = bucketKey(source, e.target, e.type);
15480
16209
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
15481
16210
  switch (provenance) {
15482
- case import_types57.Provenance.EXTRACTED:
16211
+ case import_types58.Provenance.EXTRACTED:
15483
16212
  cur.extracted = e;
15484
16213
  break;
15485
- case import_types57.Provenance.OBSERVED:
16214
+ case import_types58.Provenance.OBSERVED:
15486
16215
  cur.observed = e;
15487
16216
  break;
15488
- case import_types57.Provenance.INFERRED:
16217
+ case import_types58.Provenance.INFERRED:
15489
16218
  cur.inferred = e;
15490
16219
  break;
15491
16220
  default:
15492
- if (e.provenance === import_types57.Provenance.STALE) cur.stale = e;
16221
+ if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
15493
16222
  }
15494
16223
  buckets2.set(key, cur);
15495
16224
  });
@@ -15498,22 +16227,22 @@ function bucketEdges(graph) {
15498
16227
  function nodeIsFrontier(graph, nodeId) {
15499
16228
  if (!graph.hasNode(nodeId)) return false;
15500
16229
  const attrs = graph.getNodeAttributes(nodeId);
15501
- return attrs.type === import_types57.NodeType.FrontierNode;
16230
+ return attrs.type === import_types58.NodeType.FrontierNode;
15502
16231
  }
15503
16232
  function nodeIsWebsocketChannel(graph, nodeId) {
15504
16233
  if (!graph.hasNode(nodeId)) return false;
15505
16234
  const attrs = graph.getNodeAttributes(nodeId);
15506
- return attrs.type === import_types57.NodeType.WebSocketChannelNode;
16235
+ return attrs.type === import_types58.NodeType.WebSocketChannelNode;
15507
16236
  }
15508
16237
  function nodeIsServerAction(graph, nodeId) {
15509
16238
  if (!graph.hasNode(nodeId)) return false;
15510
16239
  const attrs = graph.getNodeAttributes(nodeId);
15511
- return attrs.type === import_types57.NodeType.ServerActionNode;
16240
+ return attrs.type === import_types58.NodeType.ServerActionNode;
15512
16241
  }
15513
16242
  function nodeIsSymbol(graph, nodeId) {
15514
16243
  if (!graph.hasNode(nodeId)) return false;
15515
16244
  const attrs = graph.getNodeAttributes(nodeId);
15516
- return attrs.type === import_types57.NodeType.SymbolNode;
16245
+ return attrs.type === import_types58.NodeType.SymbolNode;
15517
16246
  }
15518
16247
  function clampConfidence(n) {
15519
16248
  if (!Number.isFinite(n)) return 0;
@@ -15533,14 +16262,14 @@ function gradedConfidence(edge) {
15533
16262
  return clampConfidence(confidenceForEdge(edge));
15534
16263
  }
15535
16264
  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
16265
+ import_types58.EdgeType.CALLS,
16266
+ import_types58.EdgeType.CONNECTS_TO,
16267
+ import_types58.EdgeType.PUBLISHES_TO,
16268
+ import_types58.EdgeType.CONSUMES_FROM
15540
16269
  ]);
15541
16270
  function detectMissingDivergences(graph, bucket) {
15542
16271
  const out = [];
15543
- if (bucket.type === import_types57.EdgeType.CONTAINS) return out;
16272
+ if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
15544
16273
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
15545
16274
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
15546
16275
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -15582,7 +16311,7 @@ function declaredHostFor(svc) {
15582
16311
  function hasExtractedConfiguredBy(graph, svcId) {
15583
16312
  for (const edgeId of graph.outboundEdges(svcId)) {
15584
16313
  const e = graph.getEdgeAttributes(edgeId);
15585
- if (e.type === import_types57.EdgeType.CONFIGURED_BY && e.provenance === import_types57.Provenance.EXTRACTED) {
16314
+ if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
15586
16315
  return true;
15587
16316
  }
15588
16317
  }
@@ -15595,10 +16324,10 @@ function detectHostMismatch(graph, svcId, svc) {
15595
16324
  const out = [];
15596
16325
  for (const edgeId of graph.outboundEdges(svcId)) {
15597
16326
  const edge = graph.getEdgeAttributes(edgeId);
15598
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15599
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16327
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16328
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15600
16329
  const target = graph.getNodeAttributes(edge.target);
15601
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16330
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15602
16331
  const observedHost = target.host?.trim();
15603
16332
  if (!observedHost) continue;
15604
16333
  if (observedHost === declaredHost) continue;
@@ -15620,10 +16349,10 @@ function detectCompatDivergences(graph, svcId, svc) {
15620
16349
  const deps = svc.dependencies ?? {};
15621
16350
  for (const edgeId of graph.outboundEdges(svcId)) {
15622
16351
  const edge = graph.getEdgeAttributes(edgeId);
15623
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15624
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16352
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16353
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15625
16354
  const target = graph.getNodeAttributes(edge.target);
15626
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16355
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15627
16356
  for (const pair of compatPairs()) {
15628
16357
  if (pair.engine !== target.engine) continue;
15629
16358
  const declared = deps[pair.driver];
@@ -15720,7 +16449,7 @@ function suppressHostMismatchHalves(all) {
15720
16449
  for (const d of all) {
15721
16450
  if (d.type !== "host-mismatch") continue;
15722
16451
  observedHalf.add(`${d.source}->${d.target}`);
15723
- declaredHalf.add((0, import_types57.databaseId)(d.extractedHost));
16452
+ declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
15724
16453
  }
15725
16454
  if (observedHalf.size === 0) return all;
15726
16455
  return all.filter((d) => {
@@ -15739,13 +16468,13 @@ function computeDivergences(graph, opts = {}) {
15739
16468
  }
15740
16469
  graph.forEachNode((nodeId, attrs) => {
15741
16470
  const n = attrs;
15742
- if (n.type === import_types57.NodeType.ServiceNode) {
16471
+ if (n.type === import_types58.NodeType.ServiceNode) {
15743
16472
  const svc = n;
15744
16473
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
15745
16474
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
15746
16475
  return;
15747
16476
  }
15748
- if (n.type === import_types57.NodeType.InfraNode && n.kind === "sql-table") {
16477
+ if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
15749
16478
  for (const d of detectColumnDrift(n)) all.push(d);
15750
16479
  }
15751
16480
  });
@@ -15781,7 +16510,7 @@ function computeDivergences(graph, opts = {}) {
15781
16510
  const bc = "column" in b && b.column ? b.column : "";
15782
16511
  return ac.localeCompare(bc);
15783
16512
  });
15784
- return import_types57.DivergenceResultSchema.parse({
16513
+ return import_types58.DivergenceResultSchema.parse({
15785
16514
  divergences: filtered,
15786
16515
  totalAffected: filtered.length,
15787
16516
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -15835,7 +16564,7 @@ function queryLogEntries(opts) {
15835
16564
 
15836
16565
  // src/diff.ts
15837
16566
  init_cjs_shims();
15838
- var import_node_fs35 = require("fs");
16567
+ var import_node_fs36 = require("fs");
15839
16568
  async function loadSnapshotForDiff(target) {
15840
16569
  if (/^https?:\/\//i.test(target)) {
15841
16570
  const res = await fetch(target);
@@ -15844,7 +16573,7 @@ async function loadSnapshotForDiff(target) {
15844
16573
  }
15845
16574
  return await res.json();
15846
16575
  }
15847
- const raw = await import_node_fs35.promises.readFile(target, "utf8");
16576
+ const raw = await import_node_fs36.promises.readFile(target, "utf8");
15848
16577
  return JSON.parse(raw);
15849
16578
  }
15850
16579
  function indexEntries(entries) {
@@ -15912,23 +16641,23 @@ function canonicalJson(value) {
15912
16641
 
15913
16642
  // src/projects.ts
15914
16643
  init_cjs_shims();
15915
- var import_node_path69 = __toESM(require("path"), 1);
16644
+ var import_node_path71 = __toESM(require("path"), 1);
15916
16645
  function pathsForProject(project, baseDir) {
15917
16646
  if (project === DEFAULT_PROJECT) {
15918
16647
  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")
16648
+ snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
16649
+ errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
16650
+ staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
16651
+ embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
16652
+ policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
15924
16653
  };
15925
16654
  }
15926
16655
  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`)
16656
+ snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
16657
+ errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
16658
+ staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
16659
+ embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
16660
+ policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
15932
16661
  };
15933
16662
  }
15934
16663
  var Projects = class {
@@ -15964,28 +16693,28 @@ var Projects = class {
15964
16693
 
15965
16694
  // src/registry.ts
15966
16695
  init_cjs_shims();
15967
- var import_node_fs36 = require("fs");
16696
+ var import_node_fs37 = require("fs");
15968
16697
  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");
16698
+ var import_node_path72 = __toESM(require("path"), 1);
16699
+ var import_types59 = require("@neat.is/types");
15971
16700
  var LOCK_TIMEOUT_MS = 5e3;
15972
16701
  var LOCK_RETRY_MS = 50;
15973
16702
  function neatHome() {
15974
16703
  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");
16704
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16705
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15977
16706
  }
15978
16707
  function registryPath() {
15979
- return import_node_path70.default.join(neatHome(), "projects.json");
16708
+ return import_node_path72.default.join(neatHome(), "projects.json");
15980
16709
  }
15981
16710
  function registryLockPath() {
15982
- return import_node_path70.default.join(neatHome(), "projects.json.lock");
16711
+ return import_node_path72.default.join(neatHome(), "projects.json.lock");
15983
16712
  }
15984
16713
  function daemonPidPath() {
15985
- return import_node_path70.default.join(neatHome(), "neatd.pid");
16714
+ return import_node_path72.default.join(neatHome(), "neatd.pid");
15986
16715
  }
15987
16716
  function daemonsDir() {
15988
- return import_node_path70.default.join(neatHome(), "daemons");
16717
+ return import_node_path72.default.join(neatHome(), "daemons");
15989
16718
  }
15990
16719
  function isFiniteInt(v) {
15991
16720
  return typeof v === "number" && Number.isFinite(v);
@@ -16018,7 +16747,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
16018
16747
  const dir = daemonsDir();
16019
16748
  let names;
16020
16749
  try {
16021
- names = await import_node_fs36.promises.readdir(dir);
16750
+ names = await import_node_fs37.promises.readdir(dir);
16022
16751
  } catch (err) {
16023
16752
  if (err.code === "ENOENT") return [];
16024
16753
  throw err;
@@ -16026,10 +16755,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
16026
16755
  const out = [];
16027
16756
  for (const name of names) {
16028
16757
  if (!name.endsWith(".json")) continue;
16029
- const file = import_node_path70.default.join(dir, name);
16758
+ const file = import_node_path72.default.join(dir, name);
16030
16759
  let raw;
16031
16760
  try {
16032
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16761
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16033
16762
  } catch {
16034
16763
  continue;
16035
16764
  }
@@ -16055,7 +16784,7 @@ function isPidAliveDefault(pid) {
16055
16784
  }
16056
16785
  async function readPidFile(file) {
16057
16786
  try {
16058
- const raw = await import_node_fs36.promises.readFile(file, "utf8");
16787
+ const raw = await import_node_fs37.promises.readFile(file, "utf8");
16059
16788
  const pid = Number.parseInt(raw.trim(), 10);
16060
16789
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
16061
16790
  } catch {
@@ -16103,32 +16832,32 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
16103
16832
  }
16104
16833
  }
16105
16834
  async function normalizeProjectPath(input) {
16106
- const resolved = import_node_path70.default.resolve(input);
16835
+ const resolved = import_node_path72.default.resolve(input);
16107
16836
  try {
16108
- return await import_node_fs36.promises.realpath(resolved);
16837
+ return await import_node_fs37.promises.realpath(resolved);
16109
16838
  } catch {
16110
16839
  return resolved;
16111
16840
  }
16112
16841
  }
16113
16842
  async function writeAtomically(target, contents) {
16114
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(target), { recursive: true });
16843
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
16115
16844
  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");
16845
+ const fd = await import_node_fs37.promises.open(tmp, "w");
16117
16846
  try {
16118
16847
  await fd.writeFile(contents, "utf8");
16119
16848
  await fd.sync();
16120
16849
  } finally {
16121
16850
  await fd.close();
16122
16851
  }
16123
- await import_node_fs36.promises.rename(tmp, target);
16852
+ await import_node_fs37.promises.rename(tmp, target);
16124
16853
  }
16125
16854
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
16126
16855
  const deadline = Date.now() + timeoutMs;
16127
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(lockPath), { recursive: true });
16856
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
16128
16857
  let probedHolder = false;
16129
16858
  while (true) {
16130
16859
  try {
16131
- const fd = await import_node_fs36.promises.open(lockPath, "wx");
16860
+ const fd = await import_node_fs37.promises.open(lockPath, "wx");
16132
16861
  try {
16133
16862
  await fd.writeFile(`${process.pid}
16134
16863
  `, "utf8");
@@ -16153,7 +16882,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
16153
16882
  }
16154
16883
  }
16155
16884
  async function releaseLock(lockPath) {
16156
- await import_node_fs36.promises.unlink(lockPath).catch(() => {
16885
+ await import_node_fs37.promises.unlink(lockPath).catch(() => {
16157
16886
  });
16158
16887
  }
16159
16888
  async function withLock(fn) {
@@ -16169,7 +16898,7 @@ async function readRegistry() {
16169
16898
  const file = registryPath();
16170
16899
  let raw;
16171
16900
  try {
16172
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16901
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16173
16902
  } catch (err) {
16174
16903
  if (err.code === "ENOENT") {
16175
16904
  return { version: 1, projects: [] };
@@ -16177,10 +16906,10 @@ async function readRegistry() {
16177
16906
  throw err;
16178
16907
  }
16179
16908
  const parsed = JSON.parse(raw);
16180
- return import_types58.RegistryFileSchema.parse(parsed);
16909
+ return import_types59.RegistryFileSchema.parse(parsed);
16181
16910
  }
16182
16911
  async function writeRegistry(reg) {
16183
- const validated = import_types58.RegistryFileSchema.parse(reg);
16912
+ const validated = import_types59.RegistryFileSchema.parse(reg);
16184
16913
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
16185
16914
  }
16186
16915
  var ProjectNameCollisionError = class extends Error {
@@ -16274,7 +17003,7 @@ function pruneTtlMs() {
16274
17003
  }
16275
17004
  async function statPathStatus(p) {
16276
17005
  try {
16277
- const stat = await import_node_fs36.promises.stat(p);
17006
+ const stat = await import_node_fs37.promises.stat(p);
16278
17007
  return stat.isDirectory() ? "present" : "unknown";
16279
17008
  } catch (err) {
16280
17009
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -16375,8 +17104,8 @@ init_auth();
16375
17104
  // src/connectors-config.ts
16376
17105
  init_cjs_shims();
16377
17106
  var import_node_os4 = __toESM(require("os"), 1);
16378
- var import_node_path71 = __toESM(require("path"), 1);
16379
- var import_node_fs37 = require("fs");
17107
+ var import_node_path73 = __toESM(require("path"), 1);
17108
+ var import_node_fs38 = require("fs");
16380
17109
  var CONNECTORS_CONFIG_VERSION = 1;
16381
17110
  var EnvRefUnsetError = class extends Error {
16382
17111
  ref;
@@ -16390,17 +17119,17 @@ var EnvRefUnsetError = class extends Error {
16390
17119
  };
16391
17120
  function neatHome2() {
16392
17121
  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");
17122
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
17123
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16395
17124
  }
16396
17125
  function connectorsConfigPath(home = neatHome2()) {
16397
- return import_node_path71.default.join(home, "connectors.json");
17126
+ return import_node_path73.default.join(home, "connectors.json");
16398
17127
  }
16399
17128
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16400
17129
  async function warnIfModeLooserThan0600(file) {
16401
17130
  if (process.platform === "win32") return;
16402
17131
  try {
16403
- const stat = await import_node_fs37.promises.stat(file);
17132
+ const stat = await import_node_fs38.promises.stat(file);
16404
17133
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16405
17134
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16406
17135
  console.warn(
@@ -16414,7 +17143,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16414
17143
  const file = connectorsConfigPath(home);
16415
17144
  let raw;
16416
17145
  try {
16417
- raw = await import_node_fs37.promises.readFile(file, "utf8");
17146
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16418
17147
  } catch (err) {
16419
17148
  if (err.code === "ENOENT") {
16420
17149
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16581,15 +17310,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16581
17310
 
16582
17311
  // src/connectors/index.ts
16583
17312
  init_cjs_shims();
16584
- var import_types59 = require("@neat.is/types");
17313
+ var import_types60 = require("@neat.is/types");
16585
17314
  var NO_ENV = "unknown";
16586
17315
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16587
17316
  if (!graph.hasNode(targetNodeId)) return void 0;
16588
17317
  const sites = [];
16589
17318
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16590
17319
  const edge = graph.getEdgeAttributes(edgeId);
16591
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16592
- const parsed = (0, import_types59.parseFileId)(edge.source);
17320
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
17321
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16593
17322
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16594
17323
  const site = { relPath: edge.evidence.file };
16595
17324
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16600,7 +17329,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16600
17329
  function routeCallSiteFor(graph, targetNodeId) {
16601
17330
  if (!graph.hasNode(targetNodeId)) return void 0;
16602
17331
  const attrs = graph.getNodeAttributes(targetNodeId);
16603
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
17332
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16604
17333
  const site = { relPath: attrs.path };
16605
17334
  if (attrs.line !== void 0) site.line = attrs.line;
16606
17335
  return site;
@@ -17098,10 +17827,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
17098
17827
  // src/connectors/supabase/map.ts
17099
17828
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
17100
17829
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
17101
- function targetFromRestPath(path74) {
17102
- const rpcMatch = REST_RPC_PATH_RE.exec(path74);
17830
+ function targetFromRestPath(path76) {
17831
+ const rpcMatch = REST_RPC_PATH_RE.exec(path76);
17103
17832
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
17104
- const tableMatch = REST_TABLE_PATH_RE.exec(path74);
17833
+ const tableMatch = REST_TABLE_PATH_RE.exec(path76);
17105
17834
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
17106
17835
  return null;
17107
17836
  }
@@ -17212,23 +17941,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
17212
17941
 
17213
17942
  // src/connectors/supabase/resolve.ts
17214
17943
  init_cjs_shims();
17215
- var import_types61 = require("@neat.is/types");
17944
+ var import_types62 = require("@neat.is/types");
17216
17945
  function createSupabaseResolveTarget(graph, config) {
17217
17946
  return (signal, _ctx) => {
17218
17947
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
17219
17948
  return null;
17220
17949
  }
17221
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17950
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17222
17951
  if (graph.hasNode(subResourceId)) {
17223
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17952
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17224
17953
  }
17225
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17954
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
17226
17955
  if (graph.hasNode(bareResourceId)) {
17227
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17956
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17228
17957
  }
17229
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17958
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
17230
17959
  if (graph.hasNode(projectLevelId)) {
17231
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17960
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17232
17961
  }
17233
17962
  return null;
17234
17963
  };
@@ -17321,7 +18050,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
17321
18050
 
17322
18051
  // src/connectors/railway/index.ts
17323
18052
  init_cjs_shims();
17324
- var import_types65 = require("@neat.is/types");
18053
+ var import_types66 = require("@neat.is/types");
17325
18054
 
17326
18055
  // src/connectors/railway/client.ts
17327
18056
  init_cjs_shims();
@@ -17472,7 +18201,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17472
18201
  const out = [];
17473
18202
  graph.forEachNode((_id, attrs) => {
17474
18203
  const node = attrs;
17475
- if (node.type !== import_types65.NodeType.RouteNode) return;
18204
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17476
18205
  const route = attrs;
17477
18206
  if (route.service !== serviceName) return;
17478
18207
  out.push({
@@ -17576,12 +18305,12 @@ function createRailwayResolveTarget(config) {
17576
18305
  const serviceName = config.serviceNameById[config.serviceId];
17577
18306
  if (!serviceName) return null;
17578
18307
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17579
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
18308
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17580
18309
  }
17581
18310
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17582
18311
  const peerName = config.serviceNameById[signal.targetName];
17583
18312
  if (!peerName) return null;
17584
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
18313
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17585
18314
  }
17586
18315
  return null;
17587
18316
  };
@@ -17705,9 +18434,9 @@ function parseFirebaseTargetName(targetName) {
17705
18434
  const secondSep = rest.indexOf(FIELD_SEP);
17706
18435
  if (secondSep === -1) return null;
17707
18436
  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 };
18437
+ const path76 = rest.slice(secondSep + 1);
18438
+ if (!resourceName || !method || !path76) return null;
18439
+ return { resourceName, method, path: path76 };
17711
18440
  }
17712
18441
  function resourceNameFor(type, labels) {
17713
18442
  if (!labels) return null;
@@ -17745,14 +18474,14 @@ function mapLogEntryToSignal(entry) {
17745
18474
  if (!req) return null;
17746
18475
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17747
18476
  const method = req.requestMethod.toUpperCase();
17748
- const path74 = pathFromRequestUrl(req.requestUrl);
17749
- if (path74 === null) return null;
18477
+ const path76 = pathFromRequestUrl(req.requestUrl);
18478
+ if (path76 === null) return null;
17750
18479
  const timestamp = entry.timestamp;
17751
18480
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17752
18481
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
17753
18482
  return {
17754
18483
  targetKind: resourceType,
17755
- targetName: packFirebaseTargetName({ resourceName, method, path: path74 }),
18484
+ targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
17756
18485
  callCount: 1,
17757
18486
  errorCount: isError ? 1 : 0,
17758
18487
  lastObservedIso: timestamp
@@ -17769,7 +18498,7 @@ function mapLogEntriesToSignals(entries) {
17769
18498
 
17770
18499
  // src/connectors/firebase/resolve.ts
17771
18500
  init_cjs_shims();
17772
- var import_types66 = require("@neat.is/types");
18501
+ var import_types67 = require("@neat.is/types");
17773
18502
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17774
18503
  switch (resourceType) {
17775
18504
  case "cloud_function":
@@ -17784,7 +18513,7 @@ function routeEntriesFor(graph, serviceName) {
17784
18513
  const entries = [];
17785
18514
  graph.forEachNode((_id, attrs) => {
17786
18515
  const node = attrs;
17787
- if (node.type !== import_types66.NodeType.RouteNode) return;
18516
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17788
18517
  const route = attrs;
17789
18518
  if (route.service !== serviceName) return;
17790
18519
  entries.push({
@@ -17816,7 +18545,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17816
18545
  return {
17817
18546
  targetNodeId: match.routeNodeId,
17818
18547
  serviceName,
17819
- edgeType: import_types66.EdgeType.CALLS
18548
+ edgeType: import_types67.EdgeType.CALLS
17820
18549
  };
17821
18550
  };
17822
18551
  }
@@ -17843,7 +18572,7 @@ init_cjs_shims();
17843
18572
 
17844
18573
  // src/connectors/cloudflare/connector.ts
17845
18574
  init_cjs_shims();
17846
- var import_types68 = require("@neat.is/types");
18575
+ var import_types69 = require("@neat.is/types");
17847
18576
 
17848
18577
  // src/connectors/cloudflare/client.ts
17849
18578
  init_cjs_shims();
@@ -17959,7 +18688,7 @@ function mapEventToSignal(event) {
17959
18688
  if (Number.isNaN(observedAt.getTime())) return null;
17960
18689
  const statusCode = metadata?.statusCode;
17961
18690
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17962
- const path74 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18691
+ const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17963
18692
  return {
17964
18693
  targetKind: CLOUDFLARE_TARGET_KIND,
17965
18694
  targetName: scriptName,
@@ -17967,7 +18696,7 @@ function mapEventToSignal(event) {
17967
18696
  errorCount: isError ? 1 : 0,
17968
18697
  lastObservedIso: observedAt.toISOString(),
17969
18698
  method,
17970
- ...path74 ? { path: path74 } : {},
18699
+ ...path76 ? { path: path76 } : {},
17971
18700
  ...typeof statusCode === "number" ? { statusCode } : {},
17972
18701
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17973
18702
  };
@@ -18007,19 +18736,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
18007
18736
  graph.forEachNode((id, attrs) => {
18008
18737
  if (found) return;
18009
18738
  const a = attrs;
18010
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18739
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18011
18740
  found = id;
18012
18741
  }
18013
18742
  });
18014
18743
  return found;
18015
18744
  }
18016
- function findMatchingRouteNode(graph, serviceName, method, path74) {
18017
- const normalizedPath = normalizePathTemplate(path74);
18745
+ function findMatchingRouteNode(graph, serviceName, method, path76) {
18746
+ const normalizedPath = normalizePathTemplate(path76);
18018
18747
  let found = null;
18019
18748
  graph.forEachNode((id, attrs) => {
18020
18749
  if (found) return;
18021
18750
  const a = attrs;
18022
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18751
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
18023
18752
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
18024
18753
  const routeMethod = (a.method ?? "").toUpperCase();
18025
18754
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -18031,18 +18760,18 @@ function createCloudflareResolveTarget(config, graph) {
18031
18760
  return (signal) => {
18032
18761
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
18033
18762
  const scriptName = signal.targetName;
18034
- const { method, path: path74 } = signal;
18763
+ const { method, path: path76 } = signal;
18035
18764
  const resolveRouteGrain = (serviceName, wholeFileId) => {
18036
- if (!method || !path74) return wholeFileId;
18037
- return findMatchingRouteNode(graph, serviceName, method, path74) ?? wholeFileId;
18765
+ if (!method || !path76) return wholeFileId;
18766
+ return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
18038
18767
  };
18039
18768
  const mapping = config.workers?.[scriptName];
18040
18769
  if (mapping) {
18041
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18770
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
18042
18771
  return {
18043
18772
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
18044
18773
  serviceName: mapping.service,
18045
- edgeType: import_types68.EdgeType.CALLS
18774
+ edgeType: import_types69.EdgeType.CALLS
18046
18775
  };
18047
18776
  }
18048
18777
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -18051,13 +18780,13 @@ function createCloudflareResolveTarget(config, graph) {
18051
18780
  return {
18052
18781
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
18053
18782
  serviceName: fileNode.service,
18054
- edgeType: import_types68.EdgeType.CALLS
18783
+ edgeType: import_types69.EdgeType.CALLS
18055
18784
  };
18056
18785
  }
18057
18786
  return {
18058
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18787
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
18059
18788
  serviceName: scriptName,
18060
- edgeType: import_types68.EdgeType.CALLS,
18789
+ edgeType: import_types69.EdgeType.CALLS,
18061
18790
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
18062
18791
  };
18063
18792
  };
@@ -18253,14 +18982,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
18253
18982
 
18254
18983
  // src/connectors/neon/resolve.ts
18255
18984
  init_cjs_shims();
18256
- var import_types72 = require("@neat.is/types");
18985
+ var import_types73 = require("@neat.is/types");
18257
18986
  function createNeonResolveTarget(config) {
18258
18987
  return (signal) => {
18259
18988
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18260
18989
  return {
18261
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18990
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
18262
18991
  serviceName: config.serviceName,
18263
- edgeType: import_types72.EdgeType.CALLS,
18992
+ edgeType: import_types73.EdgeType.CALLS,
18264
18993
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
18265
18994
  };
18266
18995
  };
@@ -18386,9 +19115,9 @@ function parseCloudRunTargetName(targetName) {
18386
19115
  const secondSep = rest.indexOf(FIELD_SEP2);
18387
19116
  if (secondSep === -1) return null;
18388
19117
  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 };
19118
+ const path76 = rest.slice(secondSep + 1);
19119
+ if (!serviceName || !method || !path76) return null;
19120
+ return { serviceName, method, path: path76 };
18392
19121
  }
18393
19122
 
18394
19123
  // src/connectors/cloud-run/map.ts
@@ -18417,14 +19146,14 @@ function mapLogEntryToSignal2(entry) {
18417
19146
  if (!req) return null;
18418
19147
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
18419
19148
  const method = req.requestMethod.toUpperCase();
18420
- const path74 = pathFromRequestUrl2(req.requestUrl);
18421
- if (path74 === null) return null;
19149
+ const path76 = pathFromRequestUrl2(req.requestUrl);
19150
+ if (path76 === null) return null;
18422
19151
  const timestamp = entry.timestamp;
18423
19152
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
18424
19153
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
18425
19154
  return {
18426
19155
  targetKind: CLOUD_RUN_TARGET_KIND,
18427
- targetName: packCloudRunTargetName({ serviceName, method, path: path74 }),
19156
+ targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
18428
19157
  callCount: 1,
18429
19158
  errorCount: isError ? 1 : 0,
18430
19159
  lastObservedIso: timestamp
@@ -18441,14 +19170,14 @@ function mapLogEntriesToSignals2(entries) {
18441
19170
 
18442
19171
  // src/connectors/cloud-run/resolve.ts
18443
19172
  init_cjs_shims();
18444
- var import_types76 = require("@neat.is/types");
19173
+ var import_types77 = require("@neat.is/types");
18445
19174
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18446
19175
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18447
19176
  let found = null;
18448
19177
  graph.forEachNode((_id, attrs) => {
18449
19178
  if (found) return;
18450
19179
  const node = attrs;
18451
- if (node.type !== import_types76.NodeType.RouteNode) return;
19180
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18452
19181
  const route = attrs;
18453
19182
  if (route.service !== serviceName || !route.pathTemplate) return;
18454
19183
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18463,23 +19192,23 @@ function createCloudRunResolveTarget(graph, config) {
18463
19192
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18464
19193
  const identity = parseCloudRunTargetName(signal.targetName);
18465
19194
  if (!identity) return null;
18466
- const { serviceName: gcpServiceName, method, path: path74 } = identity;
19195
+ const { serviceName: gcpServiceName, method, path: path76 } = identity;
18467
19196
  const mappedService = config.serviceMap?.[gcpServiceName];
18468
19197
  if (mappedService) {
18469
19198
  const routeNodeId = findMatchingRouteNode2(
18470
19199
  graph,
18471
19200
  mappedService,
18472
19201
  method,
18473
- normalizePathTemplate(path74)
19202
+ normalizePathTemplate(path76)
18474
19203
  );
18475
19204
  if (routeNodeId) {
18476
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
19205
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18477
19206
  }
18478
19207
  }
18479
19208
  return {
18480
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
19209
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18481
19210
  serviceName: mappedService ?? gcpServiceName,
18482
- edgeType: import_types76.EdgeType.CALLS,
19211
+ edgeType: import_types77.EdgeType.CALLS,
18483
19212
  ensureInfraNode: {
18484
19213
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18485
19214
  name: gcpServiceName,
@@ -18520,7 +19249,7 @@ function createCloudRunConnector(graph, config = {}) {
18520
19249
 
18521
19250
  // src/connectors/render/index.ts
18522
19251
  init_cjs_shims();
18523
- var import_types79 = require("@neat.is/types");
19252
+ var import_types80 = require("@neat.is/types");
18524
19253
 
18525
19254
  // src/connectors/render/types.ts
18526
19255
  init_cjs_shims();
@@ -18598,7 +19327,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18598
19327
  const out = [];
18599
19328
  graph.forEachNode((_id, attrs) => {
18600
19329
  const node = attrs;
18601
- if (node.type !== import_types79.NodeType.RouteNode) return;
19330
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18602
19331
  const route = attrs;
18603
19332
  if (route.service !== serviceName) return;
18604
19333
  out.push({
@@ -18683,7 +19412,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18683
19412
  function createRenderResolveTarget(config) {
18684
19413
  return (signal) => {
18685
19414
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18686
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
19415
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18687
19416
  }
18688
19417
  return null;
18689
19418
  };
@@ -18821,21 +19550,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18821
19550
 
18822
19551
  // src/connectors/planetscale/resolve.ts
18823
19552
  init_cjs_shims();
18824
- var import_types83 = require("@neat.is/types");
19553
+ var import_types84 = require("@neat.is/types");
18825
19554
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18826
19555
  function createPlanetscaleResolveTarget(graph, config) {
18827
19556
  const databaseName = `${config.organization}/${config.database}`;
18828
19557
  return (signal, _ctx) => {
18829
19558
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18830
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19559
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18831
19560
  if (graph.hasNode(tableId)) {
18832
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19561
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18833
19562
  }
18834
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19563
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18835
19564
  return {
18836
19565
  targetNodeId: providerId,
18837
19566
  serviceName: config.serviceName,
18838
- edgeType: import_types83.EdgeType.CALLS,
19567
+ edgeType: import_types84.EdgeType.CALLS,
18839
19568
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18840
19569
  };
18841
19570
  };
@@ -19100,7 +19829,7 @@ function mapBuildsToSignals(builds, serviceName) {
19100
19829
 
19101
19830
  // src/connectors/eas/resolve.ts
19102
19831
  init_cjs_shims();
19103
- var import_types88 = require("@neat.is/types");
19832
+ var import_types89 = require("@neat.is/types");
19104
19833
  var NO_ENV2 = "unknown";
19105
19834
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
19106
19835
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -19116,8 +19845,8 @@ function configBasenamesForPhase(phase) {
19116
19845
  function configNodeService(graph, configNodeId) {
19117
19846
  for (const edgeId of graph.inboundEdges(configNodeId)) {
19118
19847
  const edge = graph.getEdgeAttributes(edgeId);
19119
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
19120
- const parsed = (0, import_types88.parseFileId)(edge.source);
19848
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19849
+ const parsed = (0, import_types89.parseFileId)(edge.source);
19121
19850
  if (parsed) return parsed.service;
19122
19851
  }
19123
19852
  return null;
@@ -19128,7 +19857,7 @@ function findConfigNode(graph, basenames, serviceName) {
19128
19857
  graph.forEachNode((id, attrs) => {
19129
19858
  if (scoped) return;
19130
19859
  const node = attrs;
19131
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19860
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
19132
19861
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
19133
19862
  if (anyMatch === null) anyMatch = id;
19134
19863
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -19145,13 +19874,13 @@ function createEasResolveTarget(graph) {
19145
19874
  if (basenames.length > 0) {
19146
19875
  const configNodeId = findConfigNode(graph, basenames, serviceName);
19147
19876
  if (configNodeId) {
19148
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19877
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
19149
19878
  }
19150
19879
  }
19151
19880
  return {
19152
19881
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
19153
19882
  serviceName,
19154
- edgeType: import_types88.EdgeType.CALLS
19883
+ edgeType: import_types89.EdgeType.CALLS
19155
19884
  };
19156
19885
  };
19157
19886
  }
@@ -19876,11 +20605,11 @@ function registerRoutes(scope, ctx) {
19876
20605
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19877
20606
  const parsed = [];
19878
20607
  for (const c of candidates) {
19879
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20608
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19880
20609
  if (!r.success) {
19881
20610
  return reply.code(400).send({
19882
20611
  error: `unknown divergence type "${c}"`,
19883
- allowed: import_types91.DivergenceTypeSchema.options
20612
+ allowed: import_types92.DivergenceTypeSchema.options
19884
20613
  });
19885
20614
  }
19886
20615
  parsed.push(r.data);
@@ -20222,7 +20951,7 @@ function registerRoutes(scope, ctx) {
20222
20951
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
20223
20952
  let violations = await log.readAll();
20224
20953
  if (req.query.severity) {
20225
- const sev = import_types91.PolicySeveritySchema.safeParse(req.query.severity);
20954
+ const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
20226
20955
  if (!sev.success) {
20227
20956
  return reply.code(400).send({
20228
20957
  error: "invalid severity",
@@ -20261,7 +20990,7 @@ function registerRoutes(scope, ctx) {
20261
20990
  scope.post("/policies/check", async (req, reply) => {
20262
20991
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
20263
20992
  if (!proj) return;
20264
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20993
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20265
20994
  if (!parsed.success) {
20266
20995
  return reply.code(400).send({
20267
20996
  error: "invalid /policies/check body",
@@ -20582,16 +21311,16 @@ init_otel_grpc();
20582
21311
 
20583
21312
  // src/daemon.ts
20584
21313
  init_cjs_shims();
20585
- var import_node_fs39 = require("fs");
20586
- var import_node_path73 = __toESM(require("path"), 1);
21314
+ var import_node_fs40 = require("fs");
21315
+ var import_node_path75 = __toESM(require("path"), 1);
20587
21316
  var import_node_module = require("module");
20588
21317
  init_otel();
20589
21318
  init_auth();
20590
21319
 
20591
21320
  // src/unrouted.ts
20592
21321
  init_cjs_shims();
20593
- var import_node_fs38 = require("fs");
20594
- var import_node_path72 = __toESM(require("path"), 1);
21322
+ var import_node_fs39 = require("fs");
21323
+ var import_node_path74 = __toESM(require("path"), 1);
20595
21324
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
20596
21325
  return {
20597
21326
  timestamp: now.toISOString(),
@@ -20601,34 +21330,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
20601
21330
  };
20602
21331
  }
20603
21332
  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");
21333
+ const target = import_node_path74.default.join(neatHome3, "errors.ndjson");
21334
+ await import_node_fs39.promises.mkdir(neatHome3, { recursive: true });
21335
+ await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
20607
21336
  }
20608
21337
  function unroutedErrorsPath(neatHome3) {
20609
- return import_node_path72.default.join(neatHome3, "errors.ndjson");
21338
+ return import_node_path74.default.join(neatHome3, "errors.ndjson");
20610
21339
  }
20611
21340
 
20612
21341
  // src/daemon.ts
20613
- var import_types92 = require("@neat.is/types");
21342
+ var import_types93 = require("@neat.is/types");
20614
21343
  function daemonJsonPath(scanPath) {
20615
- return import_node_path73.default.join(scanPath, "neat-out", "daemon.json");
21344
+ return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
20616
21345
  }
20617
21346
  function daemonsDiscoveryDir(home) {
20618
21347
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
20619
- return import_node_path73.default.join(base, "daemons");
21348
+ return import_node_path75.default.join(base, "daemons");
20620
21349
  }
20621
21350
  function daemonDiscoveryPath(project, home) {
20622
- return import_node_path73.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
21351
+ return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
20623
21352
  }
20624
21353
  function sanitizeDiscoveryName(project) {
20625
21354
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
20626
21355
  }
20627
21356
  function neatHomeFromEnv() {
20628
21357
  const env = process.env.NEAT_HOME;
20629
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21358
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20630
21359
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20631
- return import_node_path73.default.join(home, ".neat");
21360
+ return import_node_path75.default.join(home, ".neat");
20632
21361
  }
20633
21362
  function resolveNeatVersion() {
20634
21363
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -20660,7 +21389,7 @@ async function clearDaemonRecord(record, home) {
20660
21389
  } catch {
20661
21390
  }
20662
21391
  try {
20663
- await import_node_fs39.promises.unlink(daemonDiscoveryPath(record.project, home));
21392
+ await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
20664
21393
  } catch {
20665
21394
  }
20666
21395
  }
@@ -20683,11 +21412,11 @@ function teardownSlot(slot) {
20683
21412
  }
20684
21413
  }
20685
21414
  function neatHomeFor(opts) {
20686
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path73.default.resolve(opts.neatHome);
21415
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
20687
21416
  const env = process.env.NEAT_HOME;
20688
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21417
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20689
21418
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20690
- return import_node_path73.default.join(home, ".neat");
21419
+ return import_node_path75.default.join(home, ".neat");
20691
21420
  }
20692
21421
  function routeSpanToProject(serviceName, projects) {
20693
21422
  if (!serviceName) return DEFAULT_PROJECT;
@@ -20735,13 +21464,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
20735
21464
  if (!serviceName) return true;
20736
21465
  if (serviceNameMatchesProject(serviceName, project)) return true;
20737
21466
  return graph.someNode(
20738
- (_id, attrs) => attrs.type === import_types92.NodeType.ServiceNode && attrs.name === serviceName
21467
+ (_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
20739
21468
  );
20740
21469
  }
20741
21470
  async function bootstrapProject(entry, connectors = [], neatHome3) {
20742
- const paths = pathsForProject(entry.name, import_node_path73.default.join(entry.path, "neat-out"));
21471
+ const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
20743
21472
  try {
20744
- const stat = await import_node_fs39.promises.stat(entry.path);
21473
+ const stat = await import_node_fs40.promises.stat(entry.path);
20745
21474
  if (!stat.isDirectory()) {
20746
21475
  throw new Error(`registered path ${entry.path} is not a directory`);
20747
21476
  }
@@ -20859,7 +21588,7 @@ async function startDaemon(opts = {}) {
20859
21588
  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
21589
  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
21590
  const singleProject = projectArg;
20862
- const singleProjectPath = singleProject && projectPathArg ? import_node_path73.default.resolve(projectPathArg) : null;
21591
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
20863
21592
  if (singleProject && !singleProjectPath) {
20864
21593
  throw new Error(
20865
21594
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -20867,14 +21596,14 @@ async function startDaemon(opts = {}) {
20867
21596
  }
20868
21597
  if (!singleProject) {
20869
21598
  try {
20870
- await import_node_fs39.promises.access(regPath);
21599
+ await import_node_fs40.promises.access(regPath);
20871
21600
  } catch {
20872
21601
  throw new Error(
20873
21602
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
20874
21603
  );
20875
21604
  }
20876
21605
  }
20877
- const pidPath = import_node_path73.default.join(home, "neatd.pid");
21606
+ const pidPath = import_node_path75.default.join(home, "neatd.pid");
20878
21607
  await writeAtomically(pidPath, `${process.pid}
20879
21608
  `);
20880
21609
  const slots = /* @__PURE__ */ new Map();
@@ -21086,7 +21815,7 @@ async function startDaemon(opts = {}) {
21086
21815
  }
21087
21816
  if (restApp) await restApp.close().catch(() => {
21088
21817
  });
21089
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21818
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21090
21819
  });
21091
21820
  throw new Error(
21092
21821
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -21254,7 +21983,7 @@ async function startDaemon(opts = {}) {
21254
21983
  });
21255
21984
  if (otlpApp) await otlpApp.close().catch(() => {
21256
21985
  });
21257
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21986
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21258
21987
  });
21259
21988
  throw new Error(
21260
21989
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -21289,7 +22018,7 @@ async function startDaemon(opts = {}) {
21289
22018
  });
21290
22019
  if (otlpApp) await otlpApp.close().catch(() => {
21291
22020
  });
21292
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
22021
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21293
22022
  });
21294
22023
  throw new Error(
21295
22024
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -21336,9 +22065,9 @@ async function startDaemon(opts = {}) {
21336
22065
  let registryWatcher = null;
21337
22066
  let reloadTimer = null;
21338
22067
  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) => {
22068
+ const regDir = import_node_path75.default.dirname(regPath);
22069
+ const regBase = import_node_path75.default.basename(regPath);
22070
+ registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
21342
22071
  if (filename !== null && filename !== regBase) return;
21343
22072
  if (reloadTimer) clearTimeout(reloadTimer);
21344
22073
  reloadTimer = setTimeout(() => {
@@ -21387,7 +22116,7 @@ async function startDaemon(opts = {}) {
21387
22116
  if (daemonRecord) {
21388
22117
  await clearDaemonRecord(daemonRecord, home);
21389
22118
  }
21390
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
22119
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21391
22120
  });
21392
22121
  };
21393
22122
  return {