@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/neatd.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req2, reply, done) => {
64
- const path76 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
64
+ const path78 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path78) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path78)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -194,8 +194,8 @@ function reshapeGrpcRequest(req2) {
194
194
  };
195
195
  }
196
196
  function resolveProtoRoot() {
197
- const here = import_node_path51.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
- return import_node_path51.default.resolve(here, "..", "proto");
197
+ const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
+ return import_node_path52.default.resolve(here, "..", "proto");
199
199
  }
200
200
  function loadTraceService() {
201
201
  const protoRoot = resolveProtoRoot();
@@ -263,13 +263,13 @@ async function startOtelGrpcReceiver(opts) {
263
263
  })
264
264
  };
265
265
  }
266
- var import_node_url, import_node_path51, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
267
267
  var init_otel_grpc = __esm({
268
268
  "src/otel-grpc.ts"() {
269
269
  "use strict";
270
270
  init_cjs_shims();
271
271
  import_node_url = require("url");
272
- import_node_path51 = __toESM(require("path"), 1);
272
+ import_node_path52 = __toESM(require("path"), 1);
273
273
  import_node_crypto2 = require("crypto");
274
274
  grpc = __toESM(require("@grpc/grpc-js"), 1);
275
275
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
415
415
  const v = attrs[key];
416
416
  if (typeof v === "string" && v.length > 0) {
417
417
  const q = v.indexOf("?");
418
- const path76 = q === -1 ? v : v.slice(0, q);
419
- if (path76.length > 0) return path76;
418
+ const path78 = q === -1 ? v : v.slice(0, q);
419
+ if (path78.length > 0) return path78;
420
420
  }
421
421
  }
422
422
  return void 0;
@@ -478,10 +478,10 @@ function parseOtlpRequest(body) {
478
478
  return out;
479
479
  }
480
480
  function loadProtoRoot() {
481
- const here = import_node_path52.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
- const protoRoot = import_node_path52.default.resolve(here, "..", "proto");
481
+ const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
+ const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
483
483
  const root = new import_protobufjs.default.Root();
484
- root.resolvePath = (_origin, target) => import_node_path52.default.resolve(protoRoot, target);
484
+ root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
485
485
  root.loadSync(
486
486
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
487
487
  { keepCase: true }
@@ -526,11 +526,42 @@ async function decodeProtobufBody(buf) {
526
526
  const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
527
527
  return reshapeGrpcRequest2(decoded);
528
528
  }
529
+ function decompressorForEncoding(encoding) {
530
+ switch (encoding) {
531
+ case "gzip":
532
+ case "x-gzip":
533
+ return import_node_zlib.default.createGunzip();
534
+ case "deflate":
535
+ return import_node_zlib.default.createInflate();
536
+ default:
537
+ return null;
538
+ }
539
+ }
529
540
  async function buildOtelReceiver(opts) {
530
541
  const app = (0, import_fastify.default)({
531
542
  logger: false,
532
543
  bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
533
544
  });
545
+ app.addHook("preParsing", (req2, _reply, payload, done) => {
546
+ const encoding = (req2.headers["content-encoding"] ?? "").toString().trim().toLowerCase();
547
+ if (encoding === "" || encoding === "identity") {
548
+ done(null, payload);
549
+ return;
550
+ }
551
+ const decompressor = decompressorForEncoding(encoding);
552
+ if (!decompressor) {
553
+ done(null, payload);
554
+ return;
555
+ }
556
+ const tracked = decompressor;
557
+ tracked.receivedEncodedLength = 0;
558
+ payload.on("data", (chunk) => {
559
+ tracked.receivedEncodedLength = (tracked.receivedEncodedLength ?? 0) + chunk.length;
560
+ });
561
+ payload.on("error", (err) => decompressor.destroy(err));
562
+ payload.pipe(decompressor);
563
+ done(null, decompressor);
564
+ });
534
565
  const REJECT_WARN_INTERVAL_MS = 6e4;
535
566
  let lastRejectWarnAt = 0;
536
567
  const warnRejectedOtlp = () => {
@@ -728,13 +759,14 @@ async function listenSteppingOtlp(app, requestedPort, host) {
728
759
  }
729
760
  }
730
761
  }
731
- var import_node_path52, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
762
+ 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;
732
763
  var init_otel = __esm({
733
764
  "src/otel.ts"() {
734
765
  "use strict";
735
766
  init_cjs_shims();
736
- import_node_path52 = __toESM(require("path"), 1);
767
+ import_node_path53 = __toESM(require("path"), 1);
737
768
  import_node_url2 = require("url");
769
+ import_node_zlib = __toESM(require("zlib"), 1);
738
770
  import_fastify = __toESM(require("fastify"), 1);
739
771
  import_protobufjs = __toESM(require("protobufjs"), 1);
740
772
  init_auth();
@@ -756,14 +788,14 @@ __export(neatd_exports, {
756
788
  });
757
789
  module.exports = __toCommonJS(neatd_exports);
758
790
  init_cjs_shims();
759
- var import_node_fs41 = require("fs");
760
- var import_node_path75 = __toESM(require("path"), 1);
791
+ var import_node_fs42 = require("fs");
792
+ var import_node_path77 = __toESM(require("path"), 1);
761
793
  var import_node_module2 = require("module");
762
794
 
763
795
  // src/daemon.ts
764
796
  init_cjs_shims();
765
- var import_node_fs39 = require("fs");
766
- var import_node_path73 = __toESM(require("path"), 1);
797
+ var import_node_fs40 = require("fs");
798
+ var import_node_path75 = __toESM(require("path"), 1);
767
799
  var import_node_module = require("module");
768
800
 
769
801
  // src/graph.ts
@@ -1296,19 +1328,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1296
1328
  function longestIncomingWalk(graph, start, maxDepth) {
1297
1329
  let best = { path: [start], edges: [] };
1298
1330
  const visited = /* @__PURE__ */ new Set([start]);
1299
- function step(node, path76, edges) {
1300
- if (path76.length > best.path.length) {
1301
- best = { path: [...path76], edges: [...edges] };
1331
+ function step(node, path78, edges) {
1332
+ if (path78.length > best.path.length) {
1333
+ best = { path: [...path78], edges: [...edges] };
1302
1334
  }
1303
- if (path76.length - 1 >= maxDepth) return;
1335
+ if (path78.length - 1 >= maxDepth) return;
1304
1336
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1305
1337
  for (const [srcId, edge] of incoming) {
1306
1338
  if (visited.has(srcId)) continue;
1307
1339
  visited.add(srcId);
1308
- path76.push(srcId);
1340
+ path78.push(srcId);
1309
1341
  edges.push(edge);
1310
- step(srcId, path76, edges);
1311
- path76.pop();
1342
+ step(srcId, path78, edges);
1343
+ path78.pop();
1312
1344
  edges.pop();
1313
1345
  visited.delete(srcId);
1314
1346
  }
@@ -1316,11 +1348,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1316
1348
  step(start, [start], []);
1317
1349
  return best;
1318
1350
  }
1319
- function databaseRootCauseShape(graph, origin, walk9) {
1351
+ function databaseRootCauseShape(graph, origin, walk10) {
1320
1352
  const targetDb = origin;
1321
1353
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1322
1354
  if (candidatePairs.length === 0) return null;
1323
- for (const id of walk9.path) {
1355
+ for (const id of walk10.path) {
1324
1356
  const owner = resolveOwningService(graph, id);
1325
1357
  if (!owner) continue;
1326
1358
  const { id: serviceId15, svc } = owner;
@@ -1347,8 +1379,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
1347
1379
  }
1348
1380
  return null;
1349
1381
  }
1350
- function serviceRootCauseShape(graph, _origin, walk9) {
1351
- for (const id of walk9.path) {
1382
+ function serviceRootCauseShape(graph, _origin, walk10) {
1383
+ for (const id of walk10.path) {
1352
1384
  const owner = resolveOwningService(graph, id);
1353
1385
  if (!owner) continue;
1354
1386
  const { id: serviceId15, svc } = owner;
@@ -1384,15 +1416,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
1384
1416
  }
1385
1417
  return null;
1386
1418
  }
1387
- function fileRootCauseShape(graph, origin, walk9) {
1419
+ function fileRootCauseShape(graph, origin, walk10) {
1388
1420
  const owner = resolveOwningService(graph, origin.id);
1389
1421
  if (!owner) return null;
1390
- return serviceRootCauseShape(graph, owner.svc, walk9);
1422
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1391
1423
  }
1392
- function symbolRootCauseShape(graph, origin, walk9) {
1424
+ function symbolRootCauseShape(graph, origin, walk10) {
1393
1425
  const owner = resolveOwningService(graph, origin.id);
1394
1426
  if (!owner) return null;
1395
- return serviceRootCauseShape(graph, owner.svc, walk9);
1427
+ return serviceRootCauseShape(graph, owner.svc, walk10);
1396
1428
  }
1397
1429
  var rootCauseShapes = {
1398
1430
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1405,25 +1437,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1405
1437
  const origin = graph.getNodeAttributes(errorNodeId);
1406
1438
  const shape = rootCauseShapes[origin.type];
1407
1439
  if (shape) {
1408
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1409
- const match = shape(graph, origin, walk9);
1440
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1441
+ const match = shape(graph, origin, walk10);
1410
1442
  if (match) {
1411
1443
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1412
- return import_types.RootCauseResultSchema.parse({
1413
- rootCauseNode: match.rootCauseNode,
1414
- rootCauseReason: reason,
1415
- traversalPath: walk9.path,
1416
- edgeProvenances: walk9.edges.map((e) => e.provenance),
1417
- confidence: confidenceFromMix(walk9.edges),
1418
- fixRecommendation: match.fixRecommendation
1419
- });
1444
+ return {
1445
+ source: "compat",
1446
+ result: import_types.RootCauseResultSchema.parse({
1447
+ rootCauseNode: match.rootCauseNode,
1448
+ rootCauseReason: reason,
1449
+ traversalPath: walk10.path,
1450
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
1451
+ confidence: confidenceFromMix(walk10.edges),
1452
+ fixRecommendation: match.fixRecommendation
1453
+ })
1454
+ };
1420
1455
  }
1421
1456
  }
1422
1457
  if (origin.type === import_types.NodeType.ServiceNode) {
1423
1458
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
1424
- if (crossService) return crossService;
1459
+ if (crossService) return { result: crossService, source: "cross-service" };
1425
1460
  }
1426
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1461
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
1462
+ return incident ? { result: incident, source: "incident" } : null;
1427
1463
  }
1428
1464
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
1429
1465
  function incidentMatchesNode(ev, nodeId) {
@@ -1519,26 +1555,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
1519
1555
  return best;
1520
1556
  }
1521
1557
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1522
- const path76 = [originServiceId];
1558
+ const path78 = [originServiceId];
1523
1559
  const edges = [];
1524
1560
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1525
1561
  let current = originServiceId;
1526
1562
  for (let depth = 0; depth < maxDepth; depth++) {
1527
1563
  const hop = dominantFailingCall(graph, current, visited);
1528
1564
  if (!hop) break;
1529
- path76.push(hop.nextService);
1565
+ path78.push(hop.nextService);
1530
1566
  edges.push(hop.edge);
1531
1567
  visited.add(hop.nextService);
1532
1568
  current = hop.nextService;
1533
1569
  }
1534
1570
  if (edges.length === 0) return null;
1535
- return { path: path76, edges, culprit: current };
1571
+ return { path: path78, edges, culprit: current };
1572
+ }
1573
+ function isStaleCallEdge(e) {
1574
+ return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
1575
+ }
1576
+ function staleCallDominates(e, id, curEdge, curId) {
1577
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
1578
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
1579
+ if (ev !== cv) return ev > cv;
1580
+ return id < curId;
1581
+ }
1582
+ function dominantStaleCall(graph, serviceId15, visited) {
1583
+ const bestByCallee = /* @__PURE__ */ new Map();
1584
+ for (const src of callSourcesForService(graph, serviceId15)) {
1585
+ for (const edgeId of graph.outboundEdges(src)) {
1586
+ const e = graph.getEdgeAttributes(edgeId);
1587
+ if (e.type !== import_types.EdgeType.CALLS) continue;
1588
+ if (isFrontierNode(graph, e.target)) continue;
1589
+ const owner = resolveOwningService(graph, e.target);
1590
+ if (!owner || visited.has(owner.id)) continue;
1591
+ const cur = bestByCallee.get(owner.id);
1592
+ if (!cur || import_types.PROV_RANK[e.provenance] > import_types.PROV_RANK[cur.provenance]) {
1593
+ bestByCallee.set(owner.id, e);
1594
+ }
1595
+ }
1596
+ }
1597
+ let best = null;
1598
+ for (const [id, edge] of bestByCallee) {
1599
+ if (!isStaleCallEdge(edge)) continue;
1600
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
1601
+ best = { nextService: id, edge };
1602
+ }
1603
+ }
1604
+ return best;
1605
+ }
1606
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
1607
+ const path78 = [originServiceId];
1608
+ const edges = [];
1609
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
1610
+ let current = originServiceId;
1611
+ for (let depth = 0; depth < maxDepth; depth++) {
1612
+ const hop = dominantStaleCall(graph, current, visited);
1613
+ if (!hop) break;
1614
+ path78.push(hop.nextService);
1615
+ edges.push(hop.edge);
1616
+ visited.add(hop.nextService);
1617
+ current = hop.nextService;
1618
+ }
1619
+ if (edges.length === 0) return null;
1620
+ return { path: path78, edges, culprit: current };
1536
1621
  }
1537
1622
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1538
1623
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1539
1624
  if (!chain) return null;
1540
1625
  const culprit = chain.culprit;
1541
- const path76 = [...chain.path];
1626
+ const path78 = [...chain.path];
1542
1627
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1543
1628
  const baseConfidence = confidenceFromMix(chain.edges);
1544
1629
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1546,14 +1631,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1546
1631
  if (loc) {
1547
1632
  let rootCauseNode = culprit;
1548
1633
  if (loc.fileNode) {
1549
- path76.push(loc.fileNode);
1634
+ path78.push(loc.fileNode);
1550
1635
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1551
1636
  rootCauseNode = loc.fileNode;
1552
1637
  }
1553
1638
  return import_types.RootCauseResultSchema.parse({
1554
1639
  rootCauseNode,
1555
1640
  rootCauseReason: loc.rootCauseReason,
1556
- traversalPath: path76,
1641
+ traversalPath: path78,
1557
1642
  edgeProvenances,
1558
1643
  confidence,
1559
1644
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1565,7 +1650,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1565
1650
  return import_types.RootCauseResultSchema.parse({
1566
1651
  rootCauseNode: culprit,
1567
1652
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1568
- traversalPath: path76,
1653
+ traversalPath: path78,
1569
1654
  edgeProvenances,
1570
1655
  confidence,
1571
1656
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -1952,17 +2037,20 @@ function displayNameOf(nodeId) {
1952
2037
  return nodeId.replace(/^[a-z]+:/, "");
1953
2038
  }
1954
2039
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
1955
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
1956
- if (!legacy) return null;
2040
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
2041
+ if (!tagged) return null;
1957
2042
  const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
1958
- if (!navigation) return legacy;
1959
- return enrichWithNavigation(graph, errorNodeId, legacy, incidents, opts?.now ?? Date.now());
2043
+ if (!navigation) return tagged.result;
2044
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
1960
2045
  }
1961
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2046
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2047
+ const legacy = tagged.result;
1962
2048
  const seedNode = legacy.rootCauseNode;
1963
2049
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
1964
2050
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
1965
2051
  const candidates = [];
2052
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
2053
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
1966
2054
  if (seedCtx && isVictimSeed(seedCtx)) {
1967
2055
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
1968
2056
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -1987,6 +2075,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
1987
2075
  confidence: Math.min(legacy.confidence, 0.4),
1988
2076
  ...lastProv ? { provenance: lastProv } : {}
1989
2077
  });
2078
+ } else if (staleChain) {
2079
+ const culprit = staleChain.culprit;
2080
+ const culpritName = displayNameOf(culprit);
2081
+ const seedName = displayNameOf(seedNode);
2082
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
2083
+ candidates.push({
2084
+ node: culprit,
2085
+ classification: "primary-failure",
2086
+ 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.`,
2087
+ context: nodeContext(graph, culprit, incidents, now),
2088
+ confidence: staleConfidence,
2089
+ provenance: import_types.Provenance.STALE
2090
+ });
2091
+ candidates.push({
2092
+ node: seedNode,
2093
+ classification: "symptom-only",
2094
+ 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.`,
2095
+ context: seedCtx ?? EMPTY_CONTEXT,
2096
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
2097
+ ...lastProv ? { provenance: lastProv } : {}
2098
+ });
1990
2099
  } else {
1991
2100
  candidates.push({
1992
2101
  node: seedNode,
@@ -2000,11 +2109,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
2000
2109
  const top = candidates[0];
2001
2110
  let traversalPath = legacy.traversalPath;
2002
2111
  let edgeProvenances = legacy.edgeProvenances;
2003
- if (top.node !== seedNode) {
2004
- const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2005
- if (path76) {
2006
- traversalPath = path76.nodes;
2007
- edgeProvenances = path76.edges.map((e) => e.provenance);
2112
+ if (staleChain && top.node === staleChain.culprit) {
2113
+ traversalPath = staleChain.path;
2114
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
2115
+ } else if (top.node !== seedNode) {
2116
+ const path78 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2117
+ if (path78) {
2118
+ traversalPath = path78.nodes;
2119
+ edgeProvenances = path78.edges.map((e) => e.provenance);
2008
2120
  } else {
2009
2121
  traversalPath = [errorNodeId, top.node];
2010
2122
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -2026,6 +2138,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
2026
2138
  return legacy.fixRecommendation;
2027
2139
  }
2028
2140
  const name = top.node.replace(/^service:/, "");
2141
+ if (top.provenance === import_types.Provenance.STALE) {
2142
+ 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}.`;
2143
+ }
2029
2144
  if (top.classification === "primary-failure") {
2030
2145
  return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
2031
2146
  }
@@ -2544,6 +2659,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2544
2659
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2545
2660
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2546
2661
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2662
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2547
2663
  var import_types6 = require("@neat.is/types");
2548
2664
 
2549
2665
  // src/extract/shared.ts
@@ -2824,7 +2940,7 @@ function buildServiceHostIndex(services) {
2824
2940
  async function walkSourceFiles(dir, excludeDirs = []) {
2825
2941
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
2826
2942
  const out = [];
2827
- async function walk9(current) {
2943
+ async function walk10(current) {
2828
2944
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2829
2945
  for (const entry2 of entries) {
2830
2946
  const full = import_node_path5.default.join(current, entry2.name);
@@ -2832,7 +2948,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2832
2948
  if (IGNORED_DIRS.has(entry2.name)) continue;
2833
2949
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
2834
2950
  if (await isPythonVenvDir(full)) continue;
2835
- await walk9(full);
2951
+ await walk10(full);
2836
2952
  } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2837
2953
  // would attribute our instrumentation imports to the user's service.
2838
2954
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -2840,7 +2956,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2840
2956
  }
2841
2957
  }
2842
2958
  }
2843
- await walk9(dir);
2959
+ await walk10(dir);
2844
2960
  return out;
2845
2961
  }
2846
2962
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3360,6 +3476,11 @@ function makePhpParser() {
3360
3476
  p.setLanguage(import_tree_sitter_php.default.php_only);
3361
3477
  return p;
3362
3478
  }
3479
+ function makeRustParser() {
3480
+ const p = new import_tree_sitter2.default();
3481
+ p.setLanguage(import_tree_sitter_rust.default);
3482
+ return p;
3483
+ }
3363
3484
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
3364
3485
  "get",
3365
3486
  "post",
@@ -3431,8 +3552,8 @@ function chiRoutesFromSource(source, parser) {
3431
3552
  chiWalk(tree.rootNode, "", out);
3432
3553
  return out;
3433
3554
  }
3434
- function stripChiRegex(path76) {
3435
- return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3555
+ function stripChiRegex(path78) {
3556
+ return path78.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3436
3557
  }
3437
3558
  function chiWalk(node, prefix, out) {
3438
3559
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4104,9 +4225,9 @@ function rubyRocketRoute(args) {
4104
4225
  if (!pair || pair.type !== "pair") continue;
4105
4226
  const k = pair.childForFieldName("key");
4106
4227
  if (k?.type !== "string") continue;
4107
- const path76 = rubyLiteral(k);
4108
- if (path76 === null) continue;
4109
- return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
4228
+ const path78 = rubyLiteral(k);
4229
+ if (path78 === null) continue;
4230
+ return { path: path78, target: rubyLiteral(pair.childForFieldName("value")) };
4110
4231
  }
4111
4232
  return null;
4112
4233
  }
@@ -4276,6 +4397,48 @@ function railsRoutesFromSource(source, parser) {
4276
4397
  });
4277
4398
  return out;
4278
4399
  }
4400
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4401
+ function sinatraRoutesFromSource(source, parser) {
4402
+ const tree = parseSource2(parser, source);
4403
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
4404
+ const out = [];
4405
+ walk(tree.rootNode, (node) => {
4406
+ if (node.type !== "call") return;
4407
+ if (node.childForFieldName("receiver")) return;
4408
+ const method = node.childForFieldName("method")?.text;
4409
+ if (!method || !SINATRA_VERBS.has(method)) return;
4410
+ if (!node.childForFieldName("block")) return;
4411
+ const first = node.childForFieldName("arguments")?.namedChild(0);
4412
+ if (first?.type !== "string") return;
4413
+ const p = rubyLiteral(first);
4414
+ if (p === null || !p.startsWith("/")) return;
4415
+ out.push({
4416
+ method: method.toUpperCase(),
4417
+ pathTemplate: canonicalizeTemplate(p),
4418
+ line: node.startPosition.row + 1,
4419
+ framework: "sinatra"
4420
+ });
4421
+ });
4422
+ return out;
4423
+ }
4424
+ function fileReferencesSinatra(root) {
4425
+ let found = false;
4426
+ walk(root, (node) => {
4427
+ if (found) return;
4428
+ if (node.type === "call") {
4429
+ const m = node.childForFieldName("method")?.text;
4430
+ if (m === "require" || m === "require_relative") {
4431
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
4432
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
4433
+ }
4434
+ return;
4435
+ }
4436
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
4437
+ found = true;
4438
+ }
4439
+ });
4440
+ return found;
4441
+ }
4279
4442
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
4280
4443
  var LARAVEL_RESOURCE_ROWS = [
4281
4444
  { action: "index", methods: ["GET"], suffix: "" },
@@ -4451,6 +4614,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
4451
4614
  }
4452
4615
  return out;
4453
4616
  }
4617
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
4618
+ function isSlimAppCtor(node) {
4619
+ if (!node) return false;
4620
+ if (node.type === "scoped_call_expression") {
4621
+ const scope = node.childForFieldName("scope")?.text ?? "";
4622
+ const name = node.childForFieldName("name")?.text;
4623
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
4624
+ }
4625
+ if (node.type === "object_creation_expression") {
4626
+ const cls = node.namedChild(0)?.text ?? "";
4627
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
4628
+ }
4629
+ return false;
4630
+ }
4631
+ function isSlimAppType(node) {
4632
+ if (!node || node.type !== "named_type") return false;
4633
+ const text = node.text;
4634
+ return text === "App" || text.endsWith("\\App");
4635
+ }
4636
+ function collectSlimAppVars(root) {
4637
+ const vars = /* @__PURE__ */ new Set();
4638
+ walk(root, (node) => {
4639
+ if (node.type === "assignment_expression") {
4640
+ const left = node.childForFieldName("left");
4641
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
4642
+ vars.add(left.text);
4643
+ }
4644
+ return;
4645
+ }
4646
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
4647
+ const name = node.childForFieldName("name");
4648
+ if (name?.type === "variable_name") vars.add(name.text);
4649
+ }
4650
+ });
4651
+ return vars;
4652
+ }
4653
+ function slimClosureParamVars(closure) {
4654
+ const out = ["$this"];
4655
+ for (let i = 0; i < closure.namedChildCount; i++) {
4656
+ const params = closure.namedChild(i);
4657
+ if (params?.type !== "formal_parameters") continue;
4658
+ for (let j = 0; j < params.namedChildCount; j++) {
4659
+ const param = params.namedChild(j);
4660
+ if (param?.type !== "simple_parameter") continue;
4661
+ for (let k = 0; k < param.namedChildCount; k++) {
4662
+ const v = param.namedChild(k);
4663
+ if (v?.type === "variable_name") {
4664
+ out.push(v.text);
4665
+ break;
4666
+ }
4667
+ }
4668
+ }
4669
+ }
4670
+ return out;
4671
+ }
4672
+ function phpStringArray(node) {
4673
+ const out = [];
4674
+ if (node?.type !== "array_creation_expression") return out;
4675
+ for (let i = 0; i < node.namedChildCount; i++) {
4676
+ const el = node.namedChild(i);
4677
+ if (el?.type !== "array_element_initializer") continue;
4678
+ const s = phpStaticString(el.namedChild(0));
4679
+ if (s !== null) out.push(s);
4680
+ }
4681
+ return out;
4682
+ }
4683
+ function slimRoutesFromSource(source, parser) {
4684
+ const tree = parseSource2(parser, source);
4685
+ const appVars = collectSlimAppVars(tree.rootNode);
4686
+ if (appVars.size === 0) return [];
4687
+ const out = [];
4688
+ slimWalk(tree.rootNode, "", appVars, out);
4689
+ return out;
4690
+ }
4691
+ function slimWalk(node, prefix, appVars, out) {
4692
+ for (let i = 0; i < node.namedChildCount; i++) {
4693
+ const child = node.namedChild(i);
4694
+ if (child) slimHandle(child, prefix, appVars, out);
4695
+ }
4696
+ }
4697
+ function slimHandle(node, prefix, appVars, out) {
4698
+ if (node.type === "member_call_expression") {
4699
+ const obj = node.childForFieldName("object");
4700
+ const method = node.childForFieldName("name")?.text;
4701
+ const args = node.childForFieldName("arguments");
4702
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4703
+ const line = node.startPosition.row + 1;
4704
+ if (method === "group") {
4705
+ const groupPrefix = phpFirstString(args);
4706
+ const closure = laravelGroupClosure(args);
4707
+ if (groupPrefix !== null && closure) {
4708
+ const inner = new Set(appVars);
4709
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4710
+ const body = closure.childForFieldName("body");
4711
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4712
+ }
4713
+ return;
4714
+ }
4715
+ if (SLIM_VERBS.has(method)) {
4716
+ const p = phpFirstString(args);
4717
+ if (p !== null) {
4718
+ out.push({
4719
+ method: method.toUpperCase(),
4720
+ pathTemplate: laravelJoinPath(prefix, p),
4721
+ line,
4722
+ framework: "slim"
4723
+ });
4724
+ }
4725
+ return;
4726
+ }
4727
+ if (method === "any") {
4728
+ const p = phpFirstString(args);
4729
+ if (p !== null) {
4730
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4731
+ }
4732
+ return;
4733
+ }
4734
+ if (method === "map") {
4735
+ const vals = phpArgumentValues(args);
4736
+ const methods = phpStringArray(vals[0]);
4737
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4738
+ if (p !== null) {
4739
+ for (const m of methods) {
4740
+ out.push({
4741
+ method: m.toUpperCase(),
4742
+ pathTemplate: laravelJoinPath(prefix, p),
4743
+ line,
4744
+ framework: "slim"
4745
+ });
4746
+ }
4747
+ }
4748
+ return;
4749
+ }
4750
+ }
4751
+ }
4752
+ slimWalk(node, prefix, appVars, out);
4753
+ }
4754
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4755
+ function rustStringContent(node) {
4756
+ if (!node || node.type !== "string_literal") return null;
4757
+ for (let i = 0; i < node.namedChildCount; i++) {
4758
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4759
+ }
4760
+ return "";
4761
+ }
4762
+ function actixRoutesFromSource(source, parser) {
4763
+ const tree = parseSource2(parser, source);
4764
+ const out = [];
4765
+ walk(tree.rootNode, (node) => {
4766
+ if (node.type === "attribute_item") {
4767
+ actixAttributeRoute(node, out);
4768
+ return;
4769
+ }
4770
+ if (node.type === "call_expression") {
4771
+ actixBuilderRoute(node, out);
4772
+ }
4773
+ });
4774
+ return out;
4775
+ }
4776
+ function actixAttributeRoute(attrItem, out) {
4777
+ const attr = attrItem.namedChild(0);
4778
+ if (!attr || attr.type !== "attribute") return;
4779
+ const nameNode = attr.namedChild(0);
4780
+ if (!nameNode) return;
4781
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4782
+ if (!macro) return;
4783
+ const tokens = attr.childForFieldName("arguments");
4784
+ if (!tokens || tokens.type !== "token_tree") return;
4785
+ const strings = [];
4786
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4787
+ const s = rustStringContent(tokens.namedChild(i));
4788
+ if (s !== null) strings.push(s);
4789
+ }
4790
+ const pathStr = strings[0];
4791
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4792
+ const line = attrItem.startPosition.row + 1;
4793
+ const template = canonicalizeTemplate(pathStr);
4794
+ if (ACTIX_METHODS.has(macro)) {
4795
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4796
+ return;
4797
+ }
4798
+ if (macro === "route") {
4799
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4800
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4801
+ for (const m of list) {
4802
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4803
+ }
4804
+ }
4805
+ }
4806
+ function actixBuilderRoute(call, out) {
4807
+ const fn = call.childForFieldName("function");
4808
+ if (fn?.type !== "field_expression") return;
4809
+ if (fn.childForFieldName("field")?.text !== "route") return;
4810
+ const args = call.childForFieldName("arguments");
4811
+ const pathStr = rustStringContent(args?.namedChild(0));
4812
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4813
+ const second = args?.namedChild(1);
4814
+ if (!second) return;
4815
+ const method = actixBuilderMethod(second);
4816
+ if (!method) return;
4817
+ out.push({
4818
+ method,
4819
+ pathTemplate: canonicalizeTemplate(pathStr),
4820
+ line: call.startPosition.row + 1,
4821
+ framework: "actix-web"
4822
+ });
4823
+ }
4824
+ function actixBuilderMethod(node) {
4825
+ let method = null;
4826
+ walk(node, (n) => {
4827
+ if (method || n.type !== "scoped_identifier") return;
4828
+ const verb = n.childForFieldName("name")?.text;
4829
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4830
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4831
+ });
4832
+ return method;
4833
+ }
4454
4834
  function namedArgs(argsNode) {
4455
4835
  const out = [];
4456
4836
  if (!argsNode) return out;
@@ -4767,6 +5147,7 @@ async function addRoutes(graph, services) {
4767
5147
  const goParser = makeGoParser2();
4768
5148
  const rubyParser = makeRubyParser();
4769
5149
  const phpParser = makePhpParser();
5150
+ const rustParser = makeRustParser();
4770
5151
  let nodesAdded = 0;
4771
5152
  let edgesAdded = 0;
4772
5153
  for (const service of services) {
@@ -4789,7 +5170,10 @@ async function addRoutes(graph, services) {
4789
5170
  const isGoService = service.node.language === "go";
4790
5171
  const hasRails = deps["rails"] !== void 0;
4791
5172
  const hasLaravel = deps["laravel/framework"] !== void 0;
4792
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
5173
+ const hasSlim = deps["slim/slim"] !== void 0;
5174
+ const hasSinatra = deps["sinatra"] !== void 0;
5175
+ const hasActix = deps["actix-web"] !== void 0;
5176
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4793
5177
  continue;
4794
5178
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4795
5179
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4800,7 +5184,8 @@ async function addRoutes(graph, services) {
4800
5184
  const isGo = ext === ".go";
4801
5185
  const isRb = ext === ".rb";
4802
5186
  const isPhp = ext === ".php";
4803
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
5187
+ const isRs = ext === ".rs";
5188
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4804
5189
  const relFile = toPosix(import_node_path7.default.relative(service.dir, file.path));
4805
5190
  let routes;
4806
5191
  try {
@@ -4810,8 +5195,12 @@ async function addRoutes(graph, services) {
4810
5195
  phpParser,
4811
5196
  relFile === "routes/api.php" ? "/api" : ""
4812
5197
  ) : [];
5198
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4813
5199
  } else if (isRb) {
4814
5200
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
5201
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
5202
+ } else if (isRs) {
5203
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4815
5204
  } else if (isGo) {
4816
5205
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4817
5206
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -5046,6 +5435,37 @@ function loadIncidentThresholdsFromEnv() {
5046
5435
  return DEFAULT_INCIDENT_THRESHOLDS;
5047
5436
  }
5048
5437
  }
5438
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
5439
+ function latencyStreamCeilingMs() {
5440
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
5441
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
5442
+ const n = Number(raw);
5443
+ if (Number.isFinite(n) && n > 0) return n;
5444
+ console.warn(
5445
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
5446
+ );
5447
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
5448
+ }
5449
+ function spanServesEventStream(attrs) {
5450
+ for (const key of [
5451
+ "http.response.header.content-type",
5452
+ "http.response.header.content_type"
5453
+ ]) {
5454
+ const v = attrs[key];
5455
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
5456
+ for (const item of values) {
5457
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
5458
+ return true;
5459
+ }
5460
+ }
5461
+ }
5462
+ return false;
5463
+ }
5464
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
5465
+ if (span.websocketChannel !== void 0) return true;
5466
+ if (spanServesEventStream(span.attributes)) return true;
5467
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
5468
+ }
5049
5469
  function httpResponseStatusFromAttrs(attrs) {
5050
5470
  for (const key of ["http.response.status_code", "http.status_code"]) {
5051
5471
  const v = attrs[key];
@@ -5097,6 +5517,14 @@ function grpcStatusCodeFromAttrs(attrs) {
5097
5517
  }
5098
5518
  return void 0;
5099
5519
  }
5520
+ function spanRecordsError(span) {
5521
+ if (span.statusCode === 2) return true;
5522
+ const grpc2 = grpcStatusCodeFromAttrs(span.attributes);
5523
+ if (grpc2 !== void 0 && grpc2 !== 0) return true;
5524
+ const httpStatus = httpResponseStatusFromAttrs(span.attributes);
5525
+ if (httpStatus !== void 0 && httpStatus >= 500) return true;
5526
+ return false;
5527
+ }
5100
5528
  function nonHttpFailureMessageFromAttrs(attrs) {
5101
5529
  const grpc2 = grpcStatusCodeFromAttrs(attrs);
5102
5530
  if (grpc2 !== void 0 && grpc2 !== 0) {
@@ -5895,6 +6323,21 @@ async function recordExceptionIncident(ctx, span, ts) {
5895
6323
  };
5896
6324
  await appendErrorEvent(ctx, ev);
5897
6325
  }
6326
+ async function recordGrpcFailureIncident(ctx, span, ts) {
6327
+ const attrs = sanitizeAttributes(span.attributes);
6328
+ const ev = {
6329
+ id: `${span.traceId}:${span.spanId}`,
6330
+ timestamp: ts,
6331
+ service: span.service,
6332
+ traceId: span.traceId,
6333
+ spanId: span.spanId,
6334
+ errorType: "grpc-failure",
6335
+ errorMessage: incidentMessage(span),
6336
+ ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
6337
+ affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath)
6338
+ };
6339
+ await appendErrorEvent(ctx, ev);
6340
+ }
5898
6341
  async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5899
6342
  const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
5900
6343
  if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
@@ -5938,6 +6381,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5938
6381
  );
5939
6382
  ctx.burstState.delete(key);
5940
6383
  }
6384
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
6385
+ function nextApiRouteTemplate(span) {
6386
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
6387
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
6388
+ return match ? match[1] : void 0;
6389
+ }
5941
6390
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5942
6391
  const target = normalizePathTemplate(httpRoute);
5943
6392
  const m = method?.toUpperCase();
@@ -5959,8 +6408,8 @@ async function handleSpan(ctx, span) {
5959
6408
  warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT);
5960
6409
  }
5961
6410
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5962
- const isError = span.statusCode === 2;
5963
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
6411
+ const isError = spanRecordsError(span);
6412
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
5964
6413
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
5965
6414
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
5966
6415
  cacheSpanService(span, nowMs, callSite);
@@ -6157,12 +6606,13 @@ async function handleSpan(ctx, span) {
6157
6606
  }
6158
6607
  }
6159
6608
  }
6160
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6609
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
6610
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
6161
6611
  const routeNodeId = findRouteNodeByHttpRoute(
6162
6612
  ctx.graph,
6163
6613
  span.service,
6164
6614
  span.httpMethod,
6165
- span.httpRoute
6615
+ fusionRoute
6166
6616
  );
6167
6617
  if (routeNodeId) {
6168
6618
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -6194,10 +6644,13 @@ async function handleSpan(ctx, span) {
6194
6644
  }
6195
6645
  if (span.statusCode !== 2) {
6196
6646
  const status2 = httpResponseStatus(span);
6647
+ const grpcStatus = grpcStatusCodeFromAttrs(span.attributes);
6197
6648
  if (span.exception) {
6198
6649
  await recordExceptionIncident(ctx, span, ts);
6199
6650
  } else if (status2 !== void 0 && status2 >= 500) {
6200
6651
  await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
6652
+ } else if (grpcStatus !== void 0 && grpcStatus !== 0) {
6653
+ await recordGrpcFailureIncident(ctx, span, ts);
6201
6654
  } else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
6202
6655
  await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
6203
6656
  }
@@ -7550,7 +8003,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
7550
8003
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
7551
8004
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
7552
8005
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
7553
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
8006
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
7554
8007
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
7555
8008
  var import_types20 = require("@neat.is/types");
7556
8009
  var PARSE_CHUNK3 = 16384;
@@ -7571,7 +8024,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
7571
8024
  ".cs": import_tree_sitter_c_sharp.default,
7572
8025
  ".java": import_tree_sitter_java.default,
7573
8026
  ".kt": import_tree_sitter_kotlin.default,
7574
- ".rs": import_tree_sitter_rust.default,
8027
+ ".rs": import_tree_sitter_rust2.default,
7575
8028
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
7576
8029
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
7577
8030
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8011,15 +8464,15 @@ function collectKotlinSymbolDefs(root) {
8011
8464
  });
8012
8465
  };
8013
8466
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8014
- const firstChildOfType = (node, types) => {
8467
+ const firstChildOfType2 = (node, types) => {
8015
8468
  for (let i = 0; i < node.namedChildCount; i++) {
8016
8469
  const child = node.namedChild(i);
8017
8470
  if (child && types.includes(child.type)) return child;
8018
8471
  }
8019
8472
  return void 0;
8020
8473
  };
8021
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8022
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
8474
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
8475
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8023
8476
  let pkg;
8024
8477
  for (let i = 0; i < root.namedChildCount; i++) {
8025
8478
  const child = root.namedChild(i);
@@ -8520,7 +8973,7 @@ async function addSymbolEdges(graph, services) {
8520
8973
  return best;
8521
8974
  };
8522
8975
  const requests = [];
8523
- const walk9 = (node) => {
8976
+ const walk10 = (node) => {
8524
8977
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
8525
8978
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
8526
8979
  if (self && self.kind === "class") {
@@ -8566,10 +9019,10 @@ async function addSymbolEdges(graph, services) {
8566
9019
  }
8567
9020
  for (let i = 0; i < node.namedChildCount; i++) {
8568
9021
  const child = node.namedChild(i);
8569
- if (child) walk9(child);
9022
+ if (child) walk10(child);
8570
9023
  }
8571
9024
  };
8572
- walk9(root);
9025
+ walk10(root);
8573
9026
  for (const req2 of requests) {
8574
9027
  const targetSid = resolveTarget(req2.targetName, req2.wantKind);
8575
9028
  if (!targetSid) continue;
@@ -8865,7 +9318,7 @@ async function addServerActions(graph, services) {
8865
9318
 
8866
9319
  // src/extract/databases/index.ts
8867
9320
  init_cjs_shims();
8868
- var import_node_path33 = __toESM(require("path"), 1);
9321
+ var import_node_path34 = __toESM(require("path"), 1);
8869
9322
  var import_types23 = require("@neat.is/types");
8870
9323
 
8871
9324
  // src/extract/databases/db-config-yaml.ts
@@ -9300,9 +9753,163 @@ async function parse8(serviceDir) {
9300
9753
  }
9301
9754
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9302
9755
 
9303
- // src/extract/databases/docker-compose.ts
9756
+ // src/extract/databases/csharp.ts
9304
9757
  init_cjs_shims();
9758
+ var import_node_fs22 = require("fs");
9305
9759
  var import_node_path32 = __toESM(require("path"), 1);
9760
+ var CS_EXT = ".cs";
9761
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
9762
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
9763
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
9764
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
9765
+ function hostIsUnresolved(host) {
9766
+ return host === "" || /[${}]/.test(host);
9767
+ }
9768
+ function looksLikePostgres(s) {
9769
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
9770
+ }
9771
+ function looksLikeRedis(s) {
9772
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
9773
+ }
9774
+ function parsePostgresConnection(raw) {
9775
+ const s = raw.trim();
9776
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
9777
+ const fields = /* @__PURE__ */ new Map();
9778
+ for (const part of s.split(";")) {
9779
+ const eq = part.indexOf("=");
9780
+ if (eq < 0) continue;
9781
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
9782
+ const value = part.slice(eq + 1).trim();
9783
+ if (value && !fields.has(key)) fields.set(key, value);
9784
+ }
9785
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
9786
+ if (!hostRaw) return null;
9787
+ const host = hostRaw.split(",")[0].trim();
9788
+ if (hostIsUnresolved(host)) return null;
9789
+ const portRaw = fields.get("port");
9790
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
9791
+ const database = fields.get("database") ?? fields.get("db") ?? "";
9792
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
9793
+ }
9794
+ function parseRedisEndpoint(raw) {
9795
+ const s = raw.trim();
9796
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
9797
+ const first = s.split(",")[0].trim();
9798
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
9799
+ if (!m) return null;
9800
+ const host = m[1];
9801
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
9802
+ const port = m[2] ? Number(m[2]) : void 0;
9803
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
9804
+ }
9805
+ async function resolveEnvUpTree(startDir, name) {
9806
+ let dir = import_node_path32.default.resolve(startDir);
9807
+ for (let depth = 0; depth < 12; depth++) {
9808
+ const value = await resolveEnvVar(dir, name);
9809
+ if (value !== null) return value;
9810
+ const atRepoRoot = await import_node_fs22.promises.access(import_node_path32.default.join(dir, ".git")).then(() => true).catch(() => false);
9811
+ const parent = import_node_path32.default.dirname(dir);
9812
+ if (atRepoRoot || parent === dir) break;
9813
+ dir = parent;
9814
+ }
9815
+ return null;
9816
+ }
9817
+ async function interpolateEnvRefs(value, dir) {
9818
+ const refs = /* @__PURE__ */ new Set();
9819
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
9820
+ refs.add(m[1] ?? m[2]);
9821
+ }
9822
+ let out = value;
9823
+ for (const name of refs) {
9824
+ const resolved = await resolveEnvUpTree(dir, name);
9825
+ if (resolved === null) continue;
9826
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
9827
+ }
9828
+ return out;
9829
+ }
9830
+ function stringLiterals(masked) {
9831
+ const out = [];
9832
+ STRING_LITERAL_RE.lastIndex = 0;
9833
+ let m;
9834
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
9835
+ return out;
9836
+ }
9837
+ function envKeys(masked) {
9838
+ const out = [];
9839
+ ENV_READ_RE.lastIndex = 0;
9840
+ let m;
9841
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
9842
+ const key = m[1] ?? m[2];
9843
+ if (key) out.push(key);
9844
+ }
9845
+ return out;
9846
+ }
9847
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
9848
+ const out = [];
9849
+ for (const key of keys) {
9850
+ const raw = await resolveEnvUpTree(serviceDir, key);
9851
+ if (raw === null) continue;
9852
+ const value = await interpolateEnvRefs(raw, serviceDir);
9853
+ if (!looksLike(value)) continue;
9854
+ const parsed = parse11(value);
9855
+ if (parsed) out.push(parsed);
9856
+ }
9857
+ for (const lit of literals) {
9858
+ if (!looksLike(lit)) continue;
9859
+ const parsed = parse11(lit);
9860
+ if (parsed) out.push(parsed);
9861
+ }
9862
+ return out;
9863
+ }
9864
+ async function parse9(serviceDir) {
9865
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
9866
+ (f) => import_node_path32.default.extname(f) === CS_EXT
9867
+ );
9868
+ if (files.length === 0) return [];
9869
+ const sources = [];
9870
+ for (const file of files) {
9871
+ const content = await import_node_fs22.promises.readFile(file, "utf8").catch(() => null);
9872
+ if (content !== null) sources.push({ file, content });
9873
+ }
9874
+ let pgGateFile = null;
9875
+ let redisGateFile = null;
9876
+ for (const { file, content } of sources) {
9877
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
9878
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
9879
+ }
9880
+ if (!pgGateFile && !redisGateFile) return [];
9881
+ const literals = [];
9882
+ const keys = [];
9883
+ for (const { content } of sources) {
9884
+ const masked = maskCommentsInSource(content);
9885
+ literals.push(...stringLiterals(masked));
9886
+ keys.push(...envKeys(masked));
9887
+ }
9888
+ const out = [];
9889
+ const seenHosts = /* @__PURE__ */ new Set();
9890
+ const push = (config, sourceFile) => {
9891
+ const dedupe = `${config.engine}:${config.host}`;
9892
+ if (seenHosts.has(dedupe)) return;
9893
+ seenHosts.add(dedupe);
9894
+ out.push({ ...config, sourceFile });
9895
+ };
9896
+ if (pgGateFile) {
9897
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
9898
+ push(pg3, pgGateFile);
9899
+ }
9900
+ }
9901
+ if (redisGateFile) {
9902
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
9903
+ push(redis, redisGateFile);
9904
+ }
9905
+ }
9906
+ return out;
9907
+ }
9908
+ var csharpParser = { name: "csharp", parse: parse9 };
9909
+
9910
+ // src/extract/databases/docker-compose.ts
9911
+ init_cjs_shims();
9912
+ var import_node_path33 = __toESM(require("path"), 1);
9306
9913
  function portFromService(svc) {
9307
9914
  for (const raw of svc.ports ?? []) {
9308
9915
  const str = String(raw);
@@ -9327,9 +9934,9 @@ function databaseFromEnv(svc) {
9327
9934
  };
9328
9935
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9329
9936
  }
9330
- async function parse9(serviceDir) {
9937
+ async function parse10(serviceDir) {
9331
9938
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9332
- const abs = import_node_path32.default.join(serviceDir, name);
9939
+ const abs = import_node_path33.default.join(serviceDir, name);
9333
9940
  if (!await exists(abs)) continue;
9334
9941
  const raw = await readYaml(abs);
9335
9942
  if (!raw?.services) return [];
@@ -9351,7 +9958,7 @@ async function parse9(serviceDir) {
9351
9958
  }
9352
9959
  return [];
9353
9960
  }
9354
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
9961
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9355
9962
 
9356
9963
  // src/extract/databases/index.ts
9357
9964
  var DB_PARSERS = [
@@ -9363,6 +9970,7 @@ var DB_PARSERS = [
9363
9970
  ormconfigParser,
9364
9971
  typeormParser,
9365
9972
  sequelizeParser,
9973
+ csharpParser,
9366
9974
  dockerComposeParser
9367
9975
  ];
9368
9976
  function compatibleDriversFor(engine) {
@@ -9501,7 +10109,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9501
10109
  discoveredVia: mergedDiscoveredVia
9502
10110
  });
9503
10111
  }
9504
- const relConfigFile = toPosix(import_node_path33.default.relative(service.dir, config.sourceFile));
10112
+ const relConfigFile = toPosix(import_node_path34.default.relative(service.dir, config.sourceFile));
9505
10113
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9506
10114
  graph,
9507
10115
  service.pkg.name,
@@ -9510,7 +10118,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9510
10118
  );
9511
10119
  nodesAdded += fn;
9512
10120
  edgesAdded += fe;
9513
- const evidenceFile = toPosix(import_node_path33.default.relative(scanPath, config.sourceFile));
10121
+ const evidenceFile = toPosix(import_node_path34.default.relative(scanPath, config.sourceFile));
9514
10122
  const edge = {
9515
10123
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types23.EdgeType.CONNECTS_TO),
9516
10124
  source: fileNodeId,
@@ -9528,15 +10136,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9528
10136
  if (allConfigs.length === 1) {
9529
10137
  const primary = allConfigs[0];
9530
10138
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
9531
- const relPath = import_node_path33.default.relative(scanPath, primary.sourceFile);
10139
+ const relPath = import_node_path34.default.relative(scanPath, primary.sourceFile);
9532
10140
  const cfgId = (0, import_types23.configId)(relPath);
9533
10141
  if (!graph.hasNode(cfgId)) {
9534
10142
  const cfgNode = {
9535
10143
  id: cfgId,
9536
10144
  type: import_types23.NodeType.ConfigNode,
9537
- name: import_node_path33.default.basename(primary.sourceFile),
10145
+ name: import_node_path34.default.basename(primary.sourceFile),
9538
10146
  path: relPath,
9539
- fileType: isConfigFile(import_node_path33.default.basename(primary.sourceFile)).fileType || "config"
10147
+ fileType: isConfigFile(import_node_path34.default.basename(primary.sourceFile)).fileType || "config"
9540
10148
  };
9541
10149
  graph.addNode(cfgId, cfgNode);
9542
10150
  nodesAdded++;
@@ -9577,27 +10185,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9577
10185
 
9578
10186
  // src/extract/configs.ts
9579
10187
  init_cjs_shims();
9580
- var import_node_fs22 = require("fs");
9581
- var import_node_path34 = __toESM(require("path"), 1);
10188
+ var import_node_fs23 = require("fs");
10189
+ var import_node_path35 = __toESM(require("path"), 1);
9582
10190
  var import_types24 = require("@neat.is/types");
9583
10191
  async function walkConfigFiles(dir, excludeDirs = []) {
9584
- const excluded = new Set(excludeDirs.map((d) => import_node_path34.default.resolve(d)));
10192
+ const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
9585
10193
  const out = [];
9586
- async function walk9(current) {
9587
- const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true });
10194
+ async function walk10(current) {
10195
+ const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
9588
10196
  for (const entry2 of entries) {
9589
- const full = import_node_path34.default.join(current, entry2.name);
10197
+ const full = import_node_path35.default.join(current, entry2.name);
9590
10198
  if (entry2.isDirectory()) {
9591
10199
  if (IGNORED_DIRS.has(entry2.name)) continue;
9592
- if (excluded.has(import_node_path34.default.resolve(full))) continue;
10200
+ if (excluded.has(import_node_path35.default.resolve(full))) continue;
9593
10201
  if (await isPythonVenvDir(full)) continue;
9594
- await walk9(full);
10202
+ await walk10(full);
9595
10203
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
9596
10204
  out.push(full);
9597
10205
  }
9598
10206
  }
9599
10207
  }
9600
- await walk9(dir);
10208
+ await walk10(dir);
9601
10209
  return out;
9602
10210
  }
9603
10211
  async function addConfigNodes(graph, services, scanPath) {
@@ -9606,19 +10214,19 @@ async function addConfigNodes(graph, services, scanPath) {
9606
10214
  for (const service of services) {
9607
10215
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
9608
10216
  for (const file of configFiles) {
9609
- const relPath = import_node_path34.default.relative(scanPath, file);
10217
+ const relPath = import_node_path35.default.relative(scanPath, file);
9610
10218
  const node = {
9611
10219
  id: (0, import_types24.configId)(relPath),
9612
10220
  type: import_types24.NodeType.ConfigNode,
9613
- name: import_node_path34.default.basename(file),
10221
+ name: import_node_path35.default.basename(file),
9614
10222
  path: relPath,
9615
- fileType: isConfigFile(import_node_path34.default.basename(file)).fileType
10223
+ fileType: isConfigFile(import_node_path35.default.basename(file)).fileType
9616
10224
  };
9617
10225
  if (!graph.hasNode(node.id)) {
9618
10226
  graph.addNode(node.id, node);
9619
10227
  nodesAdded++;
9620
10228
  }
9621
- const relToService = toPosix(import_node_path34.default.relative(service.dir, file));
10229
+ const relToService = toPosix(import_node_path35.default.relative(service.dir, file));
9622
10230
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9623
10231
  graph,
9624
10232
  service.pkg.name,
@@ -9634,7 +10242,7 @@ async function addConfigNodes(graph, services, scanPath) {
9634
10242
  type: import_types24.EdgeType.CONFIGURED_BY,
9635
10243
  provenance: import_types24.Provenance.EXTRACTED,
9636
10244
  confidence: (0, import_types24.confidenceForExtracted)("structural"),
9637
- evidence: { file: relPath.split(import_node_path34.default.sep).join("/") }
10245
+ evidence: { file: relPath.split(import_node_path35.default.sep).join("/") }
9638
10246
  };
9639
10247
  if (!graph.hasEdge(edge.id)) {
9640
10248
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -9647,8 +10255,8 @@ async function addConfigNodes(graph, services, scanPath) {
9647
10255
 
9648
10256
  // src/extract/proto.ts
9649
10257
  init_cjs_shims();
9650
- var import_node_fs23 = require("fs");
9651
- var import_node_path35 = __toESM(require("path"), 1);
10258
+ var import_node_fs24 = require("fs");
10259
+ var import_node_path36 = __toESM(require("path"), 1);
9652
10260
  var import_types25 = require("@neat.is/types");
9653
10261
  var PROTO_EXTENSION = ".proto";
9654
10262
  function packageOf(content) {
@@ -9686,23 +10294,23 @@ function grpcMethodsFromProto(content, fqPackage) {
9686
10294
  return out;
9687
10295
  }
9688
10296
  async function walkProtoFiles(dir, excludeDirs = []) {
9689
- const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10297
+ const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
9690
10298
  const out = [];
9691
- async function walk9(current) {
9692
- const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10299
+ async function walk10(current) {
10300
+ const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9693
10301
  for (const entry2 of entries) {
9694
- const full = import_node_path35.default.join(current, entry2.name);
10302
+ const full = import_node_path36.default.join(current, entry2.name);
9695
10303
  if (entry2.isDirectory()) {
9696
10304
  if (IGNORED_DIRS.has(entry2.name)) continue;
9697
- if (excluded.has(import_node_path35.default.resolve(full))) continue;
10305
+ if (excluded.has(import_node_path36.default.resolve(full))) continue;
9698
10306
  if (await isPythonVenvDir(full)) continue;
9699
- await walk9(full);
9700
- } else if (entry2.isFile() && import_node_path35.default.extname(entry2.name) === PROTO_EXTENSION) {
10307
+ await walk10(full);
10308
+ } else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
9701
10309
  out.push(full);
9702
10310
  }
9703
10311
  }
9704
10312
  }
9705
- await walk9(dir);
10313
+ await walk10(dir);
9706
10314
  return out;
9707
10315
  }
9708
10316
  async function addGrpcMethods(graph, services) {
@@ -9712,10 +10320,10 @@ async function addGrpcMethods(graph, services) {
9712
10320
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
9713
10321
  for (const protoPath of protoPaths) {
9714
10322
  if (isTestPath(protoPath)) continue;
9715
- const relFile = toPosix(import_node_path35.default.relative(service.dir, protoPath));
10323
+ const relFile = toPosix(import_node_path36.default.relative(service.dir, protoPath));
9716
10324
  let content;
9717
10325
  try {
9718
- content = await import_node_fs23.promises.readFile(protoPath, "utf8");
10326
+ content = await import_node_fs24.promises.readFile(protoPath, "utf8");
9719
10327
  } catch (err) {
9720
10328
  recordExtractionError("proto extraction", protoPath, err);
9721
10329
  continue;
@@ -9770,11 +10378,11 @@ async function addGrpcMethods(graph, services) {
9770
10378
 
9771
10379
  // src/extract/calls/index.ts
9772
10380
  init_cjs_shims();
9773
- var import_types43 = require("@neat.is/types");
10381
+ var import_types44 = require("@neat.is/types");
9774
10382
 
9775
10383
  // src/extract/calls/http.ts
9776
10384
  init_cjs_shims();
9777
- var import_node_path36 = __toESM(require("path"), 1);
10385
+ var import_node_path37 = __toESM(require("path"), 1);
9778
10386
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
9779
10387
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
9780
10388
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -9857,7 +10465,7 @@ async function addHttpCallEdges(graph, services) {
9857
10465
  const seen = /* @__PURE__ */ new Set();
9858
10466
  for (const file of files) {
9859
10467
  if (isTestPath(file.path)) continue;
9860
- const parser = parserForExt(import_node_path36.default.extname(file.path), parserCache);
10468
+ const parser = parserForExt(import_node_path37.default.extname(file.path), parserCache);
9861
10469
  let sites;
9862
10470
  try {
9863
10471
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9866,7 +10474,7 @@ async function addHttpCallEdges(graph, services) {
9866
10474
  continue;
9867
10475
  }
9868
10476
  if (sites.length === 0) continue;
9869
- const relFile = toPosix(import_node_path36.default.relative(service.dir, file.path));
10477
+ const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
9870
10478
  for (const site of sites) {
9871
10479
  const targetId = hostToNodeId.get(site.host);
9872
10480
  if (!targetId || targetId === service.node.id) continue;
@@ -9920,7 +10528,7 @@ async function addHttpCallEdges(graph, services) {
9920
10528
 
9921
10529
  // src/extract/calls/route-match.ts
9922
10530
  init_cjs_shims();
9923
- var import_node_path37 = __toESM(require("path"), 1);
10531
+ var import_node_path38 = __toESM(require("path"), 1);
9924
10532
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
9925
10533
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
9926
10534
  var import_types27 = require("@neat.is/types");
@@ -10119,7 +10727,7 @@ async function addRouteCallEdges(graph, services) {
10119
10727
  const seen = /* @__PURE__ */ new Set();
10120
10728
  for (const file of files) {
10121
10729
  if (isTestPath(file.path)) continue;
10122
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path37.default.extname(file.path))) continue;
10730
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path38.default.extname(file.path))) continue;
10123
10731
  let sites;
10124
10732
  try {
10125
10733
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10128,7 +10736,7 @@ async function addRouteCallEdges(graph, services) {
10128
10736
  continue;
10129
10737
  }
10130
10738
  if (sites.length === 0) continue;
10131
- const relFile = toPosix(import_node_path37.default.relative(service.dir, file.path));
10739
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10132
10740
  for (const site of sites) {
10133
10741
  const serverServiceId = hostToNodeId.get(site.host);
10134
10742
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10189,7 +10797,7 @@ async function addRouteCallEdges(graph, services) {
10189
10797
 
10190
10798
  // src/extract/calls/kafka.ts
10191
10799
  init_cjs_shims();
10192
- var import_node_path38 = __toESM(require("path"), 1);
10800
+ var import_node_path39 = __toESM(require("path"), 1);
10193
10801
  var import_types28 = require("@neat.is/types");
10194
10802
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10195
10803
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
@@ -10277,13 +10885,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10277
10885
  // call sites — verified-call-site tier (ADR-066).
10278
10886
  confidenceKind: "verified-call-site",
10279
10887
  evidence: {
10280
- file: import_node_path38.default.relative(serviceDir, file.path),
10888
+ file: import_node_path39.default.relative(serviceDir, file.path),
10281
10889
  line,
10282
10890
  snippet: snippet(file.content, line)
10283
10891
  }
10284
10892
  });
10285
10893
  };
10286
- if (import_node_path38.default.extname(file.path) === ".go") {
10894
+ if (import_node_path39.default.extname(file.path) === ".go") {
10287
10895
  goSaramaEndpoints(file.content, make);
10288
10896
  } else {
10289
10897
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -10294,7 +10902,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10294
10902
 
10295
10903
  // src/extract/calls/redis.ts
10296
10904
  init_cjs_shims();
10297
- var import_node_path39 = __toESM(require("path"), 1);
10905
+ var import_node_path40 = __toESM(require("path"), 1);
10298
10906
  var import_types29 = require("@neat.is/types");
10299
10907
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10300
10908
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10317,7 +10925,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10317
10925
  // support tier (ADR-066).
10318
10926
  confidenceKind: "url-with-structural-support",
10319
10927
  evidence: {
10320
- file: import_node_path39.default.relative(serviceDir, file.path),
10928
+ file: import_node_path40.default.relative(serviceDir, file.path),
10321
10929
  line,
10322
10930
  snippet: snippet(file.content, line)
10323
10931
  }
@@ -10328,7 +10936,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10328
10936
 
10329
10937
  // src/extract/calls/aws.ts
10330
10938
  init_cjs_shims();
10331
- var import_node_path40 = __toESM(require("path"), 1);
10939
+ var import_node_path41 = __toESM(require("path"), 1);
10332
10940
  var import_types30 = require("@neat.is/types");
10333
10941
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10334
10942
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10362,7 +10970,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10362
10970
  // (ADR-066).
10363
10971
  confidenceKind: "verified-call-site",
10364
10972
  evidence: {
10365
- file: import_node_path40.default.relative(serviceDir, file.path),
10973
+ file: import_node_path41.default.relative(serviceDir, file.path),
10366
10974
  line,
10367
10975
  snippet: snippet(file.content, line)
10368
10976
  }
@@ -10387,7 +10995,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10387
10995
 
10388
10996
  // src/extract/calls/grpc.ts
10389
10997
  init_cjs_shims();
10390
- var import_node_path41 = __toESM(require("path"), 1);
10998
+ var import_node_path42 = __toESM(require("path"), 1);
10391
10999
  var import_types31 = require("@neat.is/types");
10392
11000
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10393
11001
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -10446,7 +11054,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10446
11054
  // tier (ADR-066).
10447
11055
  confidenceKind: "verified-call-site",
10448
11056
  evidence: {
10449
- file: import_node_path41.default.relative(serviceDir, file.path),
11057
+ file: import_node_path42.default.relative(serviceDir, file.path),
10450
11058
  line,
10451
11059
  snippet: snippet(file.content, line)
10452
11060
  }
@@ -10457,7 +11065,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
10457
11065
 
10458
11066
  // src/extract/calls/supabase.ts
10459
11067
  init_cjs_shims();
10460
- var import_node_path42 = __toESM(require("path"), 1);
11068
+ var import_node_path43 = __toESM(require("path"), 1);
10461
11069
  var import_types32 = require("@neat.is/types");
10462
11070
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
10463
11071
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -10516,7 +11124,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10516
11124
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10517
11125
  confidenceKind: "verified-call-site",
10518
11126
  evidence: {
10519
- file: import_node_path42.default.relative(serviceDir, file.path),
11127
+ file: import_node_path43.default.relative(serviceDir, file.path),
10520
11128
  line,
10521
11129
  snippet: snippet(file.content, line)
10522
11130
  }
@@ -10543,7 +11151,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10543
11151
  edgeType: "CALLS",
10544
11152
  confidenceKind: "verified-call-site",
10545
11153
  evidence: {
10546
- file: import_node_path42.default.relative(serviceDir, file.path),
11154
+ file: import_node_path43.default.relative(serviceDir, file.path),
10547
11155
  line,
10548
11156
  snippet: snippet(file.content, line)
10549
11157
  }
@@ -10555,7 +11163,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10555
11163
 
10556
11164
  // src/extract/calls/firestore.ts
10557
11165
  init_cjs_shims();
10558
- var import_node_path43 = __toESM(require("path"), 1);
11166
+ var import_node_path44 = __toESM(require("path"), 1);
10559
11167
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10560
11168
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10561
11169
  var import_types33 = require("@neat.is/types");
@@ -10594,7 +11202,7 @@ function isFirestoreClientFactory(node) {
10594
11202
  }
10595
11203
  function firestoreClientVars(root) {
10596
11204
  const vars = /* @__PURE__ */ new Set();
10597
- const walk9 = (node) => {
11205
+ const walk10 = (node) => {
10598
11206
  if (node.type === "variable_declarator") {
10599
11207
  const name = node.childForFieldName("name");
10600
11208
  let value = node.childForFieldName("value");
@@ -10603,9 +11211,9 @@ function firestoreClientVars(root) {
10603
11211
  vars.add(name.text);
10604
11212
  }
10605
11213
  }
10606
- for (const c of namedChildren(node)) walk9(c);
11214
+ for (const c of namedChildren(node)) walk10(c);
10607
11215
  };
10608
- walk9(root);
11216
+ walk10(root);
10609
11217
  return vars;
10610
11218
  }
10611
11219
  function isClientExpr(node, clientVars) {
@@ -10726,7 +11334,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10726
11334
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10727
11335
  if (!hasClient && !hasAdmin) return [];
10728
11336
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10729
- const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
11337
+ const tree = parseSource3(parserForExt2(import_node_path44.default.extname(file.path)), file.content);
10730
11338
  const clientVars = firestoreClientVars(tree.rootNode);
10731
11339
  const collLine = /* @__PURE__ */ new Map();
10732
11340
  const writes = /* @__PURE__ */ new Map();
@@ -10760,7 +11368,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10760
11368
  }
10761
11369
  s.add(field);
10762
11370
  };
10763
- const walk9 = (node) => {
11371
+ const walk10 = (node) => {
10764
11372
  if (node.type === "call_expression") {
10765
11373
  const fn = node.childForFieldName("function");
10766
11374
  const line = node.startPosition.row + 1;
@@ -10800,9 +11408,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10800
11408
  }
10801
11409
  }
10802
11410
  }
10803
- for (const c of namedChildren(node)) walk9(c);
11411
+ for (const c of namedChildren(node)) walk10(c);
10804
11412
  };
10805
- walk9(tree.rootNode);
11413
+ walk10(tree.rootNode);
10806
11414
  const out = [];
10807
11415
  for (const [collPath, line] of collLine) {
10808
11416
  const byField = writes.get(collPath);
@@ -10830,7 +11438,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10830
11438
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10831
11439
  ...sdkWrites ? { sdkWrites } : {},
10832
11440
  evidence: {
10833
- file: import_node_path43.default.relative(serviceDir, file.path),
11441
+ file: import_node_path44.default.relative(serviceDir, file.path),
10834
11442
  line,
10835
11443
  snippet: snippet(file.content, line)
10836
11444
  }
@@ -10841,7 +11449,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10841
11449
 
10842
11450
  // src/extract/calls/mongoose.ts
10843
11451
  init_cjs_shims();
10844
- var import_node_path44 = __toESM(require("path"), 1);
11452
+ var import_node_path45 = __toESM(require("path"), 1);
10845
11453
  var import_types34 = require("@neat.is/types");
10846
11454
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10847
11455
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -10992,7 +11600,7 @@ function endpoint(r, file, serviceDir, matchText) {
10992
11600
  kind: r.kind,
10993
11601
  edgeType: "CALLS",
10994
11602
  confidenceKind: "verified-call-site",
10995
- evidence: { file: import_node_path44.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11603
+ evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
10996
11604
  };
10997
11605
  }
10998
11606
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11082,7 +11690,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11082
11690
  const registry = /* @__PURE__ */ new Map();
11083
11691
  for (const f of mongooseFiles) {
11084
11692
  const fx = fileExportsOf(f.content, pluralizeOn);
11085
- if (fx) registry.set(toPosix(import_node_path44.default.relative(serviceDir, f.path)), fx);
11693
+ if (fx) registry.set(toPosix(import_node_path45.default.relative(serviceDir, f.path)), fx);
11086
11694
  }
11087
11695
  if (registry.size === 0) return [];
11088
11696
  const out = [];
@@ -11093,7 +11701,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11093
11701
  const directColl = /* @__PURE__ */ new Map();
11094
11702
  const nsExports = /* @__PURE__ */ new Map();
11095
11703
  for (const b of bindings) {
11096
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path44.default.dirname(f.path), serviceDir, null);
11704
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path45.default.dirname(f.path), serviceDir, null);
11097
11705
  if (!resolvedRel) continue;
11098
11706
  const fx = registry.get(resolvedRel);
11099
11707
  if (!fx) continue;
@@ -11137,7 +11745,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11137
11745
 
11138
11746
  // src/extract/calls/sqlalchemy.ts
11139
11747
  init_cjs_shims();
11140
- var import_node_path45 = __toESM(require("path"), 1);
11748
+ var import_node_path46 = __toESM(require("path"), 1);
11141
11749
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11142
11750
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11143
11751
  var import_types35 = require("@neat.is/types");
@@ -11276,7 +11884,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11276
11884
  childTable,
11277
11885
  parentTable,
11278
11886
  evidence: {
11279
- file: import_node_path45.default.relative(serviceDir, file.path),
11887
+ file: import_node_path46.default.relative(serviceDir, file.path),
11280
11888
  line,
11281
11889
  snippet: snippet(file.content, line)
11282
11890
  }
@@ -11301,7 +11909,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11301
11909
  confidenceKind: "verified-call-site",
11302
11910
  ...columns && columns.length > 0 ? { columns } : {},
11303
11911
  evidence: {
11304
- file: import_node_path45.default.relative(serviceDir, file.path),
11912
+ file: import_node_path46.default.relative(serviceDir, file.path),
11305
11913
  line,
11306
11914
  snippet: snippet(file.content, line)
11307
11915
  }
@@ -11408,7 +12016,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11408
12016
  edgeType: "CALLS",
11409
12017
  confidenceKind: "verified-call-site",
11410
12018
  evidence: {
11411
- file: import_node_path45.default.relative(serviceDir, file.path),
12019
+ file: import_node_path46.default.relative(serviceDir, file.path),
11412
12020
  line,
11413
12021
  snippet: snippet(file.content, line)
11414
12022
  }
@@ -11420,7 +12028,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11420
12028
 
11421
12029
  // src/extract/calls/django-orm.ts
11422
12030
  init_cjs_shims();
11423
- var import_node_path46 = __toESM(require("path"), 1);
12031
+ var import_node_path47 = __toESM(require("path"), 1);
11424
12032
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11425
12033
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11426
12034
  var import_types36 = require("@neat.is/types");
@@ -11490,7 +12098,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11490
12098
  const tree = parseSource7(makePyParser4(), file.content);
11491
12099
  const out = [];
11492
12100
  const seen = /* @__PURE__ */ new Set();
11493
- const defaultAppLabel = import_node_path46.default.basename(import_node_path46.default.dirname(file.path));
12101
+ const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
11494
12102
  walk4(tree.rootNode, (node) => {
11495
12103
  if (node.type !== "class_definition") return;
11496
12104
  if (!extendsDjangoModel(node)) return;
@@ -11508,7 +12116,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11508
12116
  kind: "sql-table",
11509
12117
  edgeType: "CALLS",
11510
12118
  confidenceKind: "verified-call-site",
11511
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12119
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11512
12120
  });
11513
12121
  });
11514
12122
  return out;
@@ -11516,7 +12124,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11516
12124
 
11517
12125
  // src/extract/calls/drizzle.ts
11518
12126
  init_cjs_shims();
11519
- var import_node_path47 = __toESM(require("path"), 1);
12127
+ var import_node_path48 = __toESM(require("path"), 1);
11520
12128
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11521
12129
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11522
12130
  var import_types37 = require("@neat.is/types");
@@ -11594,10 +12202,10 @@ function columnsFromObject(obj) {
11594
12202
  }
11595
12203
  function drizzleEndpointsFromFile(file, serviceDir) {
11596
12204
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11597
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12205
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11598
12206
  const out = [];
11599
12207
  const seen = /* @__PURE__ */ new Set();
11600
- const walk9 = (node) => {
12208
+ const walk10 = (node) => {
11601
12209
  if (node.type === "call_expression") {
11602
12210
  const fn = node.childForFieldName("function");
11603
12211
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11617,7 +12225,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11617
12225
  confidenceKind: "structural",
11618
12226
  columns,
11619
12227
  evidence: {
11620
- file: import_node_path47.default.relative(serviceDir, file.path),
12228
+ file: import_node_path48.default.relative(serviceDir, file.path),
11621
12229
  line,
11622
12230
  snippet: snippet(file.content, line)
11623
12231
  }
@@ -11625,9 +12233,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11625
12233
  }
11626
12234
  }
11627
12235
  }
11628
- for (const c of namedChildren4(node)) walk9(c);
12236
+ for (const c of namedChildren4(node)) walk10(c);
11629
12237
  };
11630
- walk9(tree.rootNode);
12238
+ walk10(tree.rootNode);
11631
12239
  return out;
11632
12240
  }
11633
12241
  function enclosingVarName(call) {
@@ -11649,7 +12257,7 @@ function enclosingVarName(call) {
11649
12257
  function collectDrizzleTables(root) {
11650
12258
  const tables = [];
11651
12259
  const varToTable = /* @__PURE__ */ new Map();
11652
- const walk9 = (node) => {
12260
+ const walk10 = (node) => {
11653
12261
  if (node.type === "call_expression") {
11654
12262
  const fn = node.childForFieldName("function");
11655
12263
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -11664,9 +12272,9 @@ function collectDrizzleTables(root) {
11664
12272
  }
11665
12273
  }
11666
12274
  }
11667
- for (const c of namedChildren4(node)) walk9(c);
12275
+ for (const c of namedChildren4(node)) walk10(c);
11668
12276
  };
11669
- walk9(root);
12277
+ walk10(root);
11670
12278
  return { tables, varToTable };
11671
12279
  }
11672
12280
  function referencesTargetVar(call) {
@@ -11683,13 +12291,13 @@ function referencesTargetVar(call) {
11683
12291
  }
11684
12292
  function drizzleForeignKeys(file, serviceDir) {
11685
12293
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11686
- const tree = parseSource3(parserForExt3(import_node_path47.default.extname(file.path)), file.content);
12294
+ const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
11687
12295
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11688
12296
  const out = [];
11689
12297
  const seen = /* @__PURE__ */ new Set();
11690
12298
  for (const table of tables) {
11691
12299
  if (!table.object) continue;
11692
- const walk9 = (node) => {
12300
+ const walk10 = (node) => {
11693
12301
  if (node.type === "call_expression") {
11694
12302
  const targetVar = referencesTargetVar(node);
11695
12303
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -11702,7 +12310,7 @@ function drizzleForeignKeys(file, serviceDir) {
11702
12310
  childTable: table.tableName,
11703
12311
  parentTable,
11704
12312
  evidence: {
11705
- file: import_node_path47.default.relative(serviceDir, file.path),
12313
+ file: import_node_path48.default.relative(serviceDir, file.path),
11706
12314
  line,
11707
12315
  snippet: snippet(file.content, line)
11708
12316
  }
@@ -11710,16 +12318,16 @@ function drizzleForeignKeys(file, serviceDir) {
11710
12318
  }
11711
12319
  }
11712
12320
  }
11713
- for (const c of namedChildren4(node)) walk9(c);
12321
+ for (const c of namedChildren4(node)) walk10(c);
11714
12322
  };
11715
- walk9(table.object);
12323
+ walk10(table.object);
11716
12324
  }
11717
12325
  return out;
11718
12326
  }
11719
12327
 
11720
12328
  // src/extract/calls/prisma.ts
11721
12329
  init_cjs_shims();
11722
- var import_node_path48 = __toESM(require("path"), 1);
12330
+ var import_node_path49 = __toESM(require("path"), 1);
11723
12331
  var import_types38 = require("@neat.is/types");
11724
12332
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11725
12333
  "Int",
@@ -11775,7 +12383,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11775
12383
  confidenceKind: "structural",
11776
12384
  columns: b.columns,
11777
12385
  evidence: {
11778
- file: import_node_path48.default.relative(serviceDir, file.path),
12386
+ file: import_node_path49.default.relative(serviceDir, file.path),
11779
12387
  line: b.startLine,
11780
12388
  snippet: snippet(content, b.startLine)
11781
12389
  }
@@ -11836,7 +12444,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11836
12444
  }
11837
12445
  async function prismaColumnEndpoints(serviceDir) {
11838
12446
  const schemaPath = await findFirst(serviceDir, [
11839
- import_node_path48.default.join("prisma", "schema.prisma"),
12447
+ import_node_path49.default.join("prisma", "schema.prisma"),
11840
12448
  "schema.prisma"
11841
12449
  ]);
11842
12450
  if (!schemaPath) return [];
@@ -11911,7 +12519,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11911
12519
  childTable: current.table,
11912
12520
  parentTable,
11913
12521
  evidence: {
11914
- file: import_node_path48.default.relative(serviceDir, file.path),
12522
+ file: import_node_path49.default.relative(serviceDir, file.path),
11915
12523
  line: lineNo,
11916
12524
  snippet: snippet(content, lineNo)
11917
12525
  }
@@ -11926,7 +12534,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11926
12534
  }
11927
12535
  async function prismaForeignKeys(serviceDir) {
11928
12536
  const schemaPath = await findFirst(serviceDir, [
11929
- import_node_path48.default.join("prisma", "schema.prisma"),
12537
+ import_node_path49.default.join("prisma", "schema.prisma"),
11930
12538
  "schema.prisma"
11931
12539
  ]);
11932
12540
  if (!schemaPath) return [];
@@ -11937,7 +12545,7 @@ async function prismaForeignKeys(serviceDir) {
11937
12545
 
11938
12546
  // src/extract/calls/activerecord.ts
11939
12547
  init_cjs_shims();
11940
- var import_node_path49 = __toESM(require("path"), 1);
12548
+ var import_node_path50 = __toESM(require("path"), 1);
11941
12549
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11942
12550
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
11943
12551
  var import_types39 = require("@neat.is/types");
@@ -12145,7 +12753,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12145
12753
  confidenceKind: "structural",
12146
12754
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12147
12755
  evidence: {
12148
- file: import_node_path49.default.relative(serviceDir, file.path),
12756
+ file: import_node_path50.default.relative(serviceDir, file.path),
12149
12757
  line: table.line,
12150
12758
  snippet: snippet(file.content, table.line)
12151
12759
  }
@@ -12167,7 +12775,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12167
12775
  childTable,
12168
12776
  parentTable,
12169
12777
  evidence: {
12170
- file: import_node_path49.default.relative(serviceDir, file.path),
12778
+ file: import_node_path50.default.relative(serviceDir, file.path),
12171
12779
  line,
12172
12780
  snippet: snippet(file.content, line)
12173
12781
  }
@@ -12250,7 +12858,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12250
12858
  edgeType: "CALLS",
12251
12859
  confidenceKind: "verified-call-site",
12252
12860
  evidence: {
12253
- file: import_node_path49.default.relative(serviceDir, file.path),
12861
+ file: import_node_path50.default.relative(serviceDir, file.path),
12254
12862
  line,
12255
12863
  snippet: snippet(file.content, line)
12256
12864
  }
@@ -12287,7 +12895,7 @@ function railsModelForeignKeys(file, serviceDir) {
12287
12895
  childTable,
12288
12896
  parentTable,
12289
12897
  evidence: {
12290
- file: import_node_path49.default.relative(serviceDir, file.path),
12898
+ file: import_node_path50.default.relative(serviceDir, file.path),
12291
12899
  line,
12292
12900
  snippet: snippet(file.content, line)
12293
12901
  }
@@ -12299,7 +12907,7 @@ function railsModelForeignKeys(file, serviceDir) {
12299
12907
 
12300
12908
  // src/extract/calls/eloquent.ts
12301
12909
  init_cjs_shims();
12302
- var import_node_path50 = __toESM(require("path"), 1);
12910
+ var import_node_path51 = __toESM(require("path"), 1);
12303
12911
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12304
12912
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12305
12913
  var import_types40 = require("@neat.is/types");
@@ -12556,7 +13164,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12556
13164
  confidenceKind: "structural",
12557
13165
  ...columns.length > 0 ? { columns } : {},
12558
13166
  evidence: {
12559
- file: import_node_path50.default.relative(serviceDir, file.path),
13167
+ file: import_node_path51.default.relative(serviceDir, file.path),
12560
13168
  line: bp.line,
12561
13169
  snippet: snippet(file.content, bp.line)
12562
13170
  }
@@ -12578,7 +13186,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12578
13186
  childTable,
12579
13187
  parentTable,
12580
13188
  evidence: {
12581
- file: import_node_path50.default.relative(serviceDir, file.path),
13189
+ file: import_node_path51.default.relative(serviceDir, file.path),
12582
13190
  line,
12583
13191
  snippet: snippet(file.content, line)
12584
13192
  }
@@ -12698,7 +13306,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12698
13306
  edgeType: "CALLS",
12699
13307
  confidenceKind: "verified-call-site",
12700
13308
  evidence: {
12701
- file: import_node_path50.default.relative(serviceDir, file.path),
13309
+ file: import_node_path51.default.relative(serviceDir, file.path),
12702
13310
  line,
12703
13311
  snippet: snippet(file.content, line)
12704
13312
  }
@@ -12734,7 +13342,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12734
13342
  childTable,
12735
13343
  parentTable,
12736
13344
  evidence: {
12737
- file: import_node_path50.default.relative(serviceDir, file.path),
13345
+ file: import_node_path51.default.relative(serviceDir, file.path),
12738
13346
  line,
12739
13347
  snippet: snippet(file.content, line)
12740
13348
  }
@@ -12746,7 +13354,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12746
13354
 
12747
13355
  // src/extract/calls/go.ts
12748
13356
  init_cjs_shims();
12749
- var import_node_path53 = __toESM(require("path"), 1);
13357
+ var import_node_path54 = __toESM(require("path"), 1);
12750
13358
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12751
13359
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12752
13360
  var import_types41 = require("@neat.is/types");
@@ -12821,7 +13429,7 @@ function firstStringLiteralArg(argsNode) {
12821
13429
  return null;
12822
13430
  }
12823
13431
  function goSqlEndpointsFromFile(file, serviceDir) {
12824
- if (import_node_path53.default.extname(file.path) !== ".go") return [];
13432
+ if (import_node_path54.default.extname(file.path) !== ".go") return [];
12825
13433
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12826
13434
  const tree = parseSource10(makeGoParser3(), file.content);
12827
13435
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12850,7 +13458,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12850
13458
  confidenceKind: "verified-call-site",
12851
13459
  ...columns.length > 0 ? { columns } : {},
12852
13460
  evidence: {
12853
- file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
13461
+ file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
12854
13462
  line,
12855
13463
  snippet: snippet(file.content, line)
12856
13464
  }
@@ -12861,7 +13469,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12861
13469
 
12862
13470
  // src/extract/calls/gorm.ts
12863
13471
  init_cjs_shims();
12864
- var import_node_path54 = __toESM(require("path"), 1);
13472
+ var import_node_path55 = __toESM(require("path"), 1);
12865
13473
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12866
13474
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12867
13475
  var import_types42 = require("@neat.is/types");
@@ -13294,7 +13902,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13294
13902
  seen.delete(struct.name);
13295
13903
  }
13296
13904
  function gormEndpointsFromFile(file, serviceDir) {
13297
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13905
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13298
13906
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13299
13907
  const tree = parseSource11(makeGoParser4(), file.content);
13300
13908
  const { structs, models, tableFor } = analyze(tree);
@@ -13316,7 +13924,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13316
13924
  confidenceKind: "structural",
13317
13925
  ...columns.length > 0 ? { columns } : {},
13318
13926
  evidence: {
13319
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13927
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13320
13928
  line: struct.line,
13321
13929
  snippet: snippet(file.content, struct.line)
13322
13930
  }
@@ -13325,7 +13933,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13325
13933
  return out;
13326
13934
  }
13327
13935
  function gormForeignKeys(file, serviceDir) {
13328
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13936
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13329
13937
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13330
13938
  const tree = parseSource11(makeGoParser4(), file.content);
13331
13939
  const { structs, models, tableFor } = analyze(tree);
@@ -13340,7 +13948,7 @@ function gormForeignKeys(file, serviceDir) {
13340
13948
  childTable,
13341
13949
  parentTable,
13342
13950
  evidence: {
13343
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13951
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13344
13952
  line,
13345
13953
  snippet: snippet(file.content, line)
13346
13954
  }
@@ -13375,15 +13983,131 @@ function gormForeignKeys(file, serviceDir) {
13375
13983
  return out;
13376
13984
  }
13377
13985
 
13986
+ // src/extract/calls/efcore.ts
13987
+ init_cjs_shims();
13988
+ var import_node_path56 = __toESM(require("path"), 1);
13989
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13990
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
13991
+ var import_types43 = require("@neat.is/types");
13992
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
13993
+ var PARSE_CHUNK12 = 16384;
13994
+ function makeCsParser() {
13995
+ const p = new import_tree_sitter16.default();
13996
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
13997
+ return p;
13998
+ }
13999
+ function parseSource12(parser, source) {
14000
+ return parser.parse(
14001
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
14002
+ );
14003
+ }
14004
+ function walk9(node, visit) {
14005
+ visit(node);
14006
+ for (let i = 0; i < node.namedChildCount; i++) {
14007
+ const c = node.namedChild(i);
14008
+ if (c) walk9(c, visit);
14009
+ }
14010
+ }
14011
+ function firstChildOfType(node, type) {
14012
+ for (let i = 0; i < node.namedChildCount; i++) {
14013
+ const c = node.namedChild(i);
14014
+ if (c?.type === type) return c;
14015
+ }
14016
+ return null;
14017
+ }
14018
+ function csStringLiteral(node) {
14019
+ if (node.type === "string_literal") {
14020
+ let out = "";
14021
+ for (let i = 0; i < node.namedChildCount; i++) {
14022
+ const c = node.namedChild(i);
14023
+ if (c?.type === "string_literal_content") out += c.text;
14024
+ }
14025
+ return out;
14026
+ }
14027
+ if (node.type === "verbatim_string_literal") {
14028
+ const t = node.text;
14029
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
14030
+ }
14031
+ return null;
14032
+ }
14033
+ function attributeName(attr) {
14034
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
14035
+ if (!nameNode) return null;
14036
+ const text = nameNode.text;
14037
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
14038
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
14039
+ }
14040
+ function tableFromAttribute(attr) {
14041
+ if (attributeName(attr) !== "Table") return null;
14042
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
14043
+ if (!args) return null;
14044
+ for (let i = 0; i < args.namedChildCount; i++) {
14045
+ const arg = args.namedChild(i);
14046
+ if (arg?.type !== "attribute_argument") continue;
14047
+ const first = arg.namedChild(0);
14048
+ if (!first) continue;
14049
+ const value = csStringLiteral(first);
14050
+ if (value !== null) return value;
14051
+ return null;
14052
+ }
14053
+ return null;
14054
+ }
14055
+ function tableFromToTable(call) {
14056
+ const fn = call.childForFieldName("function");
14057
+ if (fn?.type !== "member_access_expression") return null;
14058
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14059
+ if (method?.text !== "ToTable") return null;
14060
+ const args = call.childForFieldName("arguments");
14061
+ const firstArg2 = args?.namedChild(0);
14062
+ if (firstArg2?.type !== "argument") return null;
14063
+ const value = firstArg2.namedChild(0);
14064
+ return value ? csStringLiteral(value) : null;
14065
+ }
14066
+ function efcoreEndpointsFromFile(file, serviceDir) {
14067
+ if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14068
+ if (!EFCORE_GATE.test(file.content)) return [];
14069
+ const tree = parseSource12(makeCsParser(), file.content);
14070
+ const out = [];
14071
+ const seen = /* @__PURE__ */ new Set();
14072
+ const push = (name, line) => {
14073
+ if (!name || seen.has(name)) return;
14074
+ seen.add(name);
14075
+ out.push({
14076
+ infraId: (0, import_types43.infraId)("sql-table", name),
14077
+ name,
14078
+ kind: "sql-table",
14079
+ edgeType: "CALLS",
14080
+ confidenceKind: "structural",
14081
+ evidence: {
14082
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14083
+ line,
14084
+ snippet: snippet(file.content, line)
14085
+ }
14086
+ });
14087
+ };
14088
+ walk9(tree.rootNode, (node) => {
14089
+ if (node.type === "attribute") {
14090
+ const table = tableFromAttribute(node);
14091
+ if (table) push(table, node.startPosition.row + 1);
14092
+ return;
14093
+ }
14094
+ if (node.type === "invocation_expression") {
14095
+ const table = tableFromToTable(node);
14096
+ if (table) push(table, node.startPosition.row + 1);
14097
+ }
14098
+ });
14099
+ return out;
14100
+ }
14101
+
13378
14102
  // src/extract/calls/index.ts
13379
14103
  function edgeTypeFromEndpoint(ep) {
13380
14104
  switch (ep.edgeType) {
13381
14105
  case "PUBLISHES_TO":
13382
- return import_types43.EdgeType.PUBLISHES_TO;
14106
+ return import_types44.EdgeType.PUBLISHES_TO;
13383
14107
  case "CONSUMES_FROM":
13384
- return import_types43.EdgeType.CONSUMES_FROM;
14108
+ return import_types44.EdgeType.CONSUMES_FROM;
13385
14109
  default:
13386
- return import_types43.EdgeType.CALLS;
14110
+ return import_types44.EdgeType.CALLS;
13387
14111
  }
13388
14112
  }
13389
14113
  function isAwsKind(kind) {
@@ -13433,6 +14157,11 @@ async function addExternalEndpointEdges(graph, services) {
13433
14157
  } catch (err) {
13434
14158
  recordExtractionError("laravel eloquent extraction", file.path, err);
13435
14159
  }
14160
+ try {
14161
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14162
+ } catch (err) {
14163
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14164
+ }
13436
14165
  }
13437
14166
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13438
14167
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -13443,7 +14172,7 @@ async function addExternalEndpointEdges(graph, services) {
13443
14172
  if (!graph.hasNode(ep.infraId)) {
13444
14173
  const node = {
13445
14174
  id: ep.infraId,
13446
- type: import_types43.NodeType.InfraNode,
14175
+ type: import_types44.NodeType.InfraNode,
13447
14176
  name: ep.name,
13448
14177
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
13449
14178
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -13456,21 +14185,21 @@ async function addExternalEndpointEdges(graph, services) {
13456
14185
  }
13457
14186
  if (ep.columns && ep.columns.length > 0) {
13458
14187
  const node = graph.getNodeAttributes(ep.infraId);
13459
- if (node.type === import_types43.NodeType.InfraNode) {
14188
+ if (node.type === import_types44.NodeType.InfraNode) {
13460
14189
  graph.replaceNodeAttributes(ep.infraId, {
13461
14190
  ...node,
13462
14191
  columns: foldColumns(
13463
14192
  node.columns,
13464
14193
  ep.columns,
13465
- import_types43.Provenance.EXTRACTED,
13466
- (0, import_types43.confidenceForExtracted)(ep.confidenceKind)
14194
+ import_types44.Provenance.EXTRACTED,
14195
+ (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
13467
14196
  )
13468
14197
  });
13469
14198
  }
13470
14199
  }
13471
14200
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
13472
14201
  const node = graph.getNodeAttributes(ep.infraId);
13473
- if (node.type === import_types43.NodeType.InfraNode) {
14202
+ if (node.type === import_types44.NodeType.InfraNode) {
13474
14203
  graph.replaceNodeAttributes(ep.infraId, {
13475
14204
  ...node,
13476
14205
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -13478,7 +14207,7 @@ async function addExternalEndpointEdges(graph, services) {
13478
14207
  }
13479
14208
  }
13480
14209
  const edgeType = edgeTypeFromEndpoint(ep);
13481
- const confidence = (0, import_types43.confidenceForExtracted)(ep.confidenceKind);
14210
+ const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
13482
14211
  const relFile = toPosix(ep.evidence.file);
13483
14212
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
13484
14213
  graph,
@@ -13488,7 +14217,7 @@ async function addExternalEndpointEdges(graph, services) {
13488
14217
  );
13489
14218
  nodesAdded += n;
13490
14219
  edgesAdded += e;
13491
- if (!(0, import_types43.passesExtractedFloor)(confidence)) {
14220
+ if (!(0, import_types44.passesExtractedFloor)(confidence)) {
13492
14221
  noteExtractedDropped({
13493
14222
  source: fileNodeId,
13494
14223
  target: ep.infraId,
@@ -13508,7 +14237,7 @@ async function addExternalEndpointEdges(graph, services) {
13508
14237
  source: fileNodeId,
13509
14238
  target: ep.infraId,
13510
14239
  type: edgeType,
13511
- provenance: import_types43.Provenance.EXTRACTED,
14240
+ provenance: import_types44.Provenance.EXTRACTED,
13512
14241
  confidence,
13513
14242
  evidence: ep.evidence
13514
14243
  };
@@ -13531,7 +14260,7 @@ async function addCallEdges(graph, services) {
13531
14260
 
13532
14261
  // src/extract/table-edges.ts
13533
14262
  init_cjs_shims();
13534
- var import_types44 = require("@neat.is/types");
14263
+ var import_types45 = require("@neat.is/types");
13535
14264
  async function addTableEdges(graph, services) {
13536
14265
  let nodesAdded = 0;
13537
14266
  let edgesAdded = 0;
@@ -13559,20 +14288,20 @@ async function addTableEdges(graph, services) {
13559
14288
  }
13560
14289
  refs.push(...modelRefs);
13561
14290
  for (const ref of refs) {
13562
- const childId = (0, import_types44.infraId)("sql-table", ref.childTable);
13563
- const parentId = (0, import_types44.infraId)("sql-table", ref.parentTable);
14291
+ const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14292
+ const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
13564
14293
  if (childId === parentId) continue;
13565
14294
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13566
14295
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13567
- const edgeId = (0, import_types44.extractedEdgeId)(childId, parentId, import_types44.EdgeType.REFERENCES);
14296
+ const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
13568
14297
  if (graph.hasEdge(edgeId)) continue;
13569
14298
  const edge = {
13570
14299
  id: edgeId,
13571
14300
  source: childId,
13572
14301
  target: parentId,
13573
- type: import_types44.EdgeType.REFERENCES,
13574
- provenance: import_types44.Provenance.EXTRACTED,
13575
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14302
+ type: import_types45.EdgeType.REFERENCES,
14303
+ provenance: import_types45.Provenance.EXTRACTED,
14304
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13576
14305
  evidence: ref.evidence
13577
14306
  };
13578
14307
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13585,7 +14314,7 @@ function ensureTableNode(graph, id, name) {
13585
14314
  if (graph.hasNode(id)) return 0;
13586
14315
  const node = {
13587
14316
  id,
13588
- type: import_types44.NodeType.InfraNode,
14317
+ type: import_types45.NodeType.InfraNode,
13589
14318
  name,
13590
14319
  provider: "self",
13591
14320
  kind: "sql-table"
@@ -13599,16 +14328,16 @@ init_cjs_shims();
13599
14328
 
13600
14329
  // src/extract/infra/docker-compose.ts
13601
14330
  init_cjs_shims();
13602
- var import_node_path55 = __toESM(require("path"), 1);
13603
- var import_types46 = require("@neat.is/types");
14331
+ var import_node_path57 = __toESM(require("path"), 1);
14332
+ var import_types47 = require("@neat.is/types");
13604
14333
 
13605
14334
  // src/extract/infra/shared.ts
13606
14335
  init_cjs_shims();
13607
- var import_types45 = require("@neat.is/types");
14336
+ var import_types46 = require("@neat.is/types");
13608
14337
  function makeInfraNode(kind, name, provider = "self", extras) {
13609
14338
  return {
13610
- id: (0, import_types45.infraId)(kind, name),
13611
- type: import_types45.NodeType.InfraNode,
14339
+ id: (0, import_types46.infraId)(kind, name),
14340
+ type: import_types46.NodeType.InfraNode,
13612
14341
  name,
13613
14342
  provider,
13614
14343
  kind,
@@ -13652,8 +14381,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13652
14381
  source: anchorId,
13653
14382
  target: node.id,
13654
14383
  type: edgeType,
13655
- provenance: import_types45.Provenance.EXTRACTED,
13656
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14384
+ provenance: import_types46.Provenance.EXTRACTED,
14385
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13657
14386
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13658
14387
  };
13659
14388
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13670,7 +14399,7 @@ function dependsOnList(value) {
13670
14399
  }
13671
14400
  function serviceNameToServiceNode(name, services) {
13672
14401
  for (const s of services) {
13673
- if (s.node.name === name || import_node_path55.default.basename(s.dir) === name) return s.node.id;
14402
+ if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
13674
14403
  }
13675
14404
  return null;
13676
14405
  }
@@ -13679,7 +14408,7 @@ async function addComposeInfra(graph, scanPath, services) {
13679
14408
  let edgesAdded = 0;
13680
14409
  let composePath = null;
13681
14410
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13682
- const abs = import_node_path55.default.join(scanPath, name);
14411
+ const abs = import_node_path57.default.join(scanPath, name);
13683
14412
  if (await exists(abs)) {
13684
14413
  composePath = abs;
13685
14414
  break;
@@ -13692,13 +14421,13 @@ async function addComposeInfra(graph, scanPath, services) {
13692
14421
  } catch (err) {
13693
14422
  recordExtractionError(
13694
14423
  "infra docker-compose",
13695
- import_node_path55.default.relative(scanPath, composePath),
14424
+ import_node_path57.default.relative(scanPath, composePath),
13696
14425
  err
13697
14426
  );
13698
14427
  return { nodesAdded, edgesAdded };
13699
14428
  }
13700
14429
  if (!compose?.services) return { nodesAdded, edgesAdded };
13701
- const evidenceFile = import_node_path55.default.relative(scanPath, composePath).split(import_node_path55.default.sep).join("/");
14430
+ const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
13702
14431
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13703
14432
  for (const [composeName, svc] of Object.entries(compose.services)) {
13704
14433
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13720,15 +14449,15 @@ async function addComposeInfra(graph, scanPath, services) {
13720
14449
  for (const dep of dependsOnList(svc.depends_on)) {
13721
14450
  const targetId = composeNameToNodeId.get(dep);
13722
14451
  if (!targetId) continue;
13723
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types46.EdgeType.DEPENDS_ON);
14452
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
13724
14453
  if (graph.hasEdge(edgeId)) continue;
13725
14454
  const edge = {
13726
14455
  id: edgeId,
13727
14456
  source: sourceId,
13728
14457
  target: targetId,
13729
- type: import_types46.EdgeType.DEPENDS_ON,
13730
- provenance: import_types46.Provenance.EXTRACTED,
13731
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14458
+ type: import_types47.EdgeType.DEPENDS_ON,
14459
+ provenance: import_types47.Provenance.EXTRACTED,
14460
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13732
14461
  evidence: { file: evidenceFile }
13733
14462
  };
13734
14463
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13740,9 +14469,9 @@ async function addComposeInfra(graph, scanPath, services) {
13740
14469
 
13741
14470
  // src/extract/infra/dockerfile.ts
13742
14471
  init_cjs_shims();
13743
- var import_node_path56 = __toESM(require("path"), 1);
13744
- var import_node_fs24 = require("fs");
13745
- var import_types47 = require("@neat.is/types");
14472
+ var import_node_path58 = __toESM(require("path"), 1);
14473
+ var import_node_fs25 = require("fs");
14474
+ var import_types48 = require("@neat.is/types");
13746
14475
  function readDockerfile(content) {
13747
14476
  let image = null;
13748
14477
  const ports = [];
@@ -13771,15 +14500,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13771
14500
  let nodesAdded = 0;
13772
14501
  let edgesAdded = 0;
13773
14502
  for (const service of services) {
13774
- const dockerfilePath = import_node_path56.default.join(service.dir, "Dockerfile");
14503
+ const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
13775
14504
  if (!await exists(dockerfilePath)) continue;
13776
14505
  let content;
13777
14506
  try {
13778
- content = await import_node_fs24.promises.readFile(dockerfilePath, "utf8");
14507
+ content = await import_node_fs25.promises.readFile(dockerfilePath, "utf8");
13779
14508
  } catch (err) {
13780
14509
  recordExtractionError(
13781
14510
  "infra dockerfile",
13782
- import_node_path56.default.relative(scanPath, dockerfilePath),
14511
+ import_node_path58.default.relative(scanPath, dockerfilePath),
13783
14512
  err
13784
14513
  );
13785
14514
  continue;
@@ -13791,8 +14520,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13791
14520
  graph.addNode(node.id, node);
13792
14521
  nodesAdded++;
13793
14522
  }
13794
- const relDockerfile = toPosix(import_node_path56.default.relative(service.dir, dockerfilePath));
13795
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, dockerfilePath));
14523
+ const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14524
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
13796
14525
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13797
14526
  graph,
13798
14527
  service.pkg.name,
@@ -13801,15 +14530,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13801
14530
  );
13802
14531
  nodesAdded += fn;
13803
14532
  edgesAdded += fe;
13804
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types47.EdgeType.RUNS_ON);
14533
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
13805
14534
  if (!graph.hasEdge(edgeId)) {
13806
14535
  const edge = {
13807
14536
  id: edgeId,
13808
14537
  source: fileNodeId,
13809
14538
  target: node.id,
13810
- type: import_types47.EdgeType.RUNS_ON,
13811
- provenance: import_types47.Provenance.EXTRACTED,
13812
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14539
+ type: import_types48.EdgeType.RUNS_ON,
14540
+ provenance: import_types48.Provenance.EXTRACTED,
14541
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13813
14542
  evidence: {
13814
14543
  file: evidenceFile,
13815
14544
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13824,15 +14553,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13824
14553
  graph.addNode(portNode.id, portNode);
13825
14554
  nodesAdded++;
13826
14555
  }
13827
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types47.EdgeType.CONNECTS_TO);
14556
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
13828
14557
  if (graph.hasEdge(portEdgeId)) continue;
13829
14558
  const portEdge = {
13830
14559
  id: portEdgeId,
13831
14560
  source: fileNodeId,
13832
14561
  target: portNode.id,
13833
- type: import_types47.EdgeType.CONNECTS_TO,
13834
- provenance: import_types47.Provenance.EXTRACTED,
13835
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14562
+ type: import_types48.EdgeType.CONNECTS_TO,
14563
+ provenance: import_types48.Provenance.EXTRACTED,
14564
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
13836
14565
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13837
14566
  };
13838
14567
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13844,23 +14573,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13844
14573
 
13845
14574
  // src/extract/infra/terraform.ts
13846
14575
  init_cjs_shims();
13847
- var import_node_fs25 = require("fs");
13848
- var import_node_path57 = __toESM(require("path"), 1);
13849
- var import_types48 = require("@neat.is/types");
14576
+ var import_node_fs26 = require("fs");
14577
+ var import_node_path59 = __toESM(require("path"), 1);
14578
+ var import_types49 = require("@neat.is/types");
13850
14579
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13851
14580
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13852
14581
  async function walkTfFiles(start, depth = 0, max = 5) {
13853
14582
  if (depth > max) return [];
13854
14583
  const out = [];
13855
- const entries = await import_node_fs25.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14584
+ const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13856
14585
  for (const entry2 of entries) {
13857
14586
  if (entry2.isDirectory()) {
13858
14587
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
13859
- const child = import_node_path57.default.join(start, entry2.name);
14588
+ const child = import_node_path59.default.join(start, entry2.name);
13860
14589
  if (await isPythonVenvDir(child)) continue;
13861
14590
  out.push(...await walkTfFiles(child, depth + 1, max));
13862
14591
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
13863
- out.push(import_node_path57.default.join(start, entry2.name));
14592
+ out.push(import_node_path59.default.join(start, entry2.name));
13864
14593
  }
13865
14594
  }
13866
14595
  return out;
@@ -13891,8 +14620,8 @@ async function addTerraformResources(graph, scanPath) {
13891
14620
  let edgesAdded = 0;
13892
14621
  const files = await walkTfFiles(scanPath);
13893
14622
  for (const file of files) {
13894
- const content = await import_node_fs25.promises.readFile(file, "utf8");
13895
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, file));
14623
+ const content = await import_node_fs26.promises.readFile(file, "utf8");
14624
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
13896
14625
  const resources = [];
13897
14626
  const byKey = /* @__PURE__ */ new Map();
13898
14627
  RESOURCE_RE.lastIndex = 0;
@@ -13927,16 +14656,16 @@ async function addTerraformResources(graph, scanPath) {
13927
14656
  if (!target) continue;
13928
14657
  if (seen.has(target.nodeId)) continue;
13929
14658
  seen.add(target.nodeId);
13930
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types48.EdgeType.DEPENDS_ON);
14659
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
13931
14660
  if (graph.hasEdge(edgeId)) continue;
13932
14661
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13933
14662
  const edge = {
13934
14663
  id: edgeId,
13935
14664
  source: resource.nodeId,
13936
14665
  target: target.nodeId,
13937
- type: import_types48.EdgeType.DEPENDS_ON,
13938
- provenance: import_types48.Provenance.EXTRACTED,
13939
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
14666
+ type: import_types49.EdgeType.DEPENDS_ON,
14667
+ provenance: import_types49.Provenance.EXTRACTED,
14668
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
13940
14669
  evidence: { file: evidenceFile, line, snippet: key }
13941
14670
  };
13942
14671
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13949,8 +14678,8 @@ async function addTerraformResources(graph, scanPath) {
13949
14678
 
13950
14679
  // src/extract/infra/k8s.ts
13951
14680
  init_cjs_shims();
13952
- var import_node_fs26 = require("fs");
13953
- var import_node_path58 = __toESM(require("path"), 1);
14681
+ var import_node_fs27 = require("fs");
14682
+ var import_node_path60 = __toESM(require("path"), 1);
13954
14683
  var import_yaml3 = require("yaml");
13955
14684
  var K8S_KIND_TO_INFRA_KIND = {
13956
14685
  Service: "k8s-service",
@@ -13964,15 +14693,15 @@ var K8S_KIND_TO_INFRA_KIND = {
13964
14693
  async function walkYamlFiles2(start, depth = 0, max = 5) {
13965
14694
  if (depth > max) return [];
13966
14695
  const out = [];
13967
- const entries = await import_node_fs26.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14696
+ const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13968
14697
  for (const entry2 of entries) {
13969
14698
  if (entry2.isDirectory()) {
13970
14699
  if (IGNORED_DIRS.has(entry2.name)) continue;
13971
- const child = import_node_path58.default.join(start, entry2.name);
14700
+ const child = import_node_path60.default.join(start, entry2.name);
13972
14701
  if (await isPythonVenvDir(child)) continue;
13973
14702
  out.push(...await walkYamlFiles2(child, depth + 1, max));
13974
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path58.default.extname(entry2.name))) {
13975
- out.push(import_node_path58.default.join(start, entry2.name));
14703
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry2.name))) {
14704
+ out.push(import_node_path60.default.join(start, entry2.name));
13976
14705
  }
13977
14706
  }
13978
14707
  return out;
@@ -13981,7 +14710,7 @@ async function addK8sResources(graph, scanPath) {
13981
14710
  let nodesAdded = 0;
13982
14711
  const files = await walkYamlFiles2(scanPath);
13983
14712
  for (const file of files) {
13984
- const content = await import_node_fs26.promises.readFile(file, "utf8");
14713
+ const content = await import_node_fs27.promises.readFile(file, "utf8");
13985
14714
  let docs;
13986
14715
  try {
13987
14716
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14005,16 +14734,16 @@ async function addK8sResources(graph, scanPath) {
14005
14734
 
14006
14735
  // src/extract/infra/cloudflare.ts
14007
14736
  init_cjs_shims();
14008
- var import_node_fs27 = require("fs");
14009
- var import_node_path59 = __toESM(require("path"), 1);
14737
+ var import_node_fs28 = require("fs");
14738
+ var import_node_path61 = __toESM(require("path"), 1);
14010
14739
  var import_smol_toml3 = require("smol-toml");
14011
- var import_types49 = require("@neat.is/types");
14740
+ var import_types50 = require("@neat.is/types");
14012
14741
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14013
14742
  async function readWranglerConfig(dir) {
14014
14743
  for (const filename of WRANGLER_FILENAMES) {
14015
- const abs = import_node_path59.default.join(dir, filename);
14744
+ const abs = import_node_path61.default.join(dir, filename);
14016
14745
  if (!await exists(abs)) continue;
14017
- const raw = await import_node_fs27.promises.readFile(abs, "utf8");
14746
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14018
14747
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14019
14748
  return { config, relFile: filename, raw };
14020
14749
  }
@@ -14056,8 +14785,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14056
14785
  source: anchorId,
14057
14786
  target: node.id,
14058
14787
  type: edgeType,
14059
- provenance: import_types49.Provenance.EXTRACTED,
14060
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14788
+ provenance: import_types50.Provenance.EXTRACTED,
14789
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14061
14790
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14062
14791
  };
14063
14792
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14075,11 +14804,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14075
14804
  try {
14076
14805
  read = await readWranglerConfig(service.dir);
14077
14806
  } catch (err) {
14078
- recordExtractionError("infra cloudflare", import_node_path59.default.relative(scanPath, service.dir), err);
14807
+ recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
14079
14808
  continue;
14080
14809
  }
14081
14810
  if (!read || !read.config.name) continue;
14082
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, import_node_path59.default.join(service.dir, read.relFile)));
14811
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
14083
14812
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14084
14813
  }
14085
14814
  for (const worker of discovered) {
@@ -14091,7 +14820,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14091
14820
  }
14092
14821
  let anchorId = service.node.id;
14093
14822
  if (config.main) {
14094
- const entryRelPath = toPosix(import_node_path59.default.normalize(config.main));
14823
+ const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
14095
14824
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14096
14825
  graph,
14097
14826
  service.pkg.name,
@@ -14118,15 +14847,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14118
14847
  nodesAdded++;
14119
14848
  }
14120
14849
  if (runtimeNode.id !== anchorId) {
14121
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types49.EdgeType.RUNS_ON);
14850
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
14122
14851
  if (!graph.hasEdge(runsOnId)) {
14123
14852
  const edge = {
14124
14853
  id: runsOnId,
14125
14854
  source: anchorId,
14126
14855
  target: runtimeNode.id,
14127
- type: import_types49.EdgeType.RUNS_ON,
14128
- provenance: import_types49.Provenance.EXTRACTED,
14129
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14856
+ type: import_types50.EdgeType.RUNS_ON,
14857
+ provenance: import_types50.Provenance.EXTRACTED,
14858
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14130
14859
  evidence: {
14131
14860
  file: evidenceFile,
14132
14861
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14140,7 +14869,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14140
14869
  const result = addResourceEdge(
14141
14870
  graph,
14142
14871
  anchorId,
14143
- import_types49.EdgeType.CONNECTS_TO,
14872
+ import_types50.EdgeType.CONNECTS_TO,
14144
14873
  "cloudflare-route",
14145
14874
  route,
14146
14875
  evidenceFile,
@@ -14164,7 +14893,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14164
14893
  const result = addResourceEdge(
14165
14894
  graph,
14166
14895
  anchorId,
14167
- import_types49.EdgeType.DEPENDS_ON,
14896
+ import_types50.EdgeType.DEPENDS_ON,
14168
14897
  group.kind,
14169
14898
  name,
14170
14899
  evidenceFile,
@@ -14178,7 +14907,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14178
14907
  const result = addResourceEdge(
14179
14908
  graph,
14180
14909
  anchorId,
14181
- import_types49.EdgeType.DEPENDS_ON,
14910
+ import_types50.EdgeType.DEPENDS_ON,
14182
14911
  "cloudflare-cron",
14183
14912
  cron,
14184
14913
  evidenceFile,
@@ -14191,7 +14920,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14191
14920
  const result = addResourceEdge(
14192
14921
  graph,
14193
14922
  anchorId,
14194
- import_types49.EdgeType.DEPENDS_ON,
14923
+ import_types50.EdgeType.DEPENDS_ON,
14195
14924
  "cloudflare-env-var",
14196
14925
  varName,
14197
14926
  evidenceFile,
@@ -14204,15 +14933,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14204
14933
  if (!svc.service) continue;
14205
14934
  const target = workerIndex.get(svc.service);
14206
14935
  if (target && target.anchorId !== anchorId) {
14207
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types49.EdgeType.CALLS);
14936
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
14208
14937
  if (!graph.hasEdge(edgeId)) {
14209
14938
  const edge = {
14210
14939
  id: edgeId,
14211
14940
  source: anchorId,
14212
14941
  target: target.anchorId,
14213
- type: import_types49.EdgeType.CALLS,
14214
- provenance: import_types49.Provenance.EXTRACTED,
14215
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
14942
+ type: import_types50.EdgeType.CALLS,
14943
+ provenance: import_types50.Provenance.EXTRACTED,
14944
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14216
14945
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14217
14946
  };
14218
14947
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14223,7 +14952,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14223
14952
  const result = addResourceEdge(
14224
14953
  graph,
14225
14954
  anchorId,
14226
- import_types49.EdgeType.DEPENDS_ON,
14955
+ import_types50.EdgeType.DEPENDS_ON,
14227
14956
  "cloudflare-service-binding",
14228
14957
  svc.service,
14229
14958
  evidenceFile,
@@ -14238,24 +14967,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14238
14967
 
14239
14968
  // src/extract/infra/vercel.ts
14240
14969
  init_cjs_shims();
14241
- var import_node_fs28 = require("fs");
14242
- var import_node_path60 = __toESM(require("path"), 1);
14243
- var import_types50 = require("@neat.is/types");
14970
+ var import_node_fs29 = require("fs");
14971
+ var import_node_path62 = __toESM(require("path"), 1);
14972
+ var import_types51 = require("@neat.is/types");
14244
14973
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14245
14974
  async function readVercelConfig(dir) {
14246
14975
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14247
- const abs = import_node_path60.default.join(dir, filename);
14976
+ const abs = import_node_path62.default.join(dir, filename);
14248
14977
  if (!await exists(abs)) continue;
14249
- const raw = await import_node_fs28.promises.readFile(abs, "utf8");
14978
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
14250
14979
  const config = JSON.parse(maskCommentsInSource(raw));
14251
14980
  return { config, relFile: filename, raw };
14252
14981
  }
14253
14982
  return null;
14254
14983
  }
14255
14984
  async function readLinkedProjectName(dir) {
14256
- const abs = import_node_path60.default.join(dir, ".vercel", "project.json");
14985
+ const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
14257
14986
  if (!await exists(abs)) return void 0;
14258
- const parsed = JSON.parse(await import_node_fs28.promises.readFile(abs, "utf8"));
14987
+ const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
14259
14988
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14260
14989
  }
14261
14990
  function routeSource(route) {
@@ -14271,7 +15000,7 @@ async function addVercelServices(graph, services, scanPath) {
14271
15000
  read = await readVercelConfig(service.dir);
14272
15001
  projectName = await readLinkedProjectName(service.dir);
14273
15002
  } catch (err) {
14274
- recordExtractionError("infra vercel", import_node_path60.default.relative(scanPath, service.dir), err);
15003
+ recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
14275
15004
  continue;
14276
15005
  }
14277
15006
  if (!read && !projectName) continue;
@@ -14287,7 +15016,7 @@ async function addVercelServices(graph, services, scanPath) {
14287
15016
  const anchorId = service.node.id;
14288
15017
  if (!read) continue;
14289
15018
  const { config, relFile, raw } = read;
14290
- const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, import_node_path60.default.join(service.dir, relFile)));
15019
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
14291
15020
  const add = (edgeType, kind, name) => {
14292
15021
  if (!name) return;
14293
15022
  const result = emitPlatformResourceEdge(
@@ -14303,12 +15032,12 @@ async function addVercelServices(graph, services, scanPath) {
14303
15032
  nodesAdded += result.nodesAdded;
14304
15033
  edgesAdded += result.edgesAdded;
14305
15034
  };
14306
- add(import_types50.EdgeType.RUNS_ON, "vercel", "vercel");
14307
- for (const cron of config.crons ?? []) add(import_types50.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14308
- for (const varName of Object.keys(config.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14309
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15035
+ add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
15036
+ for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15037
+ for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15038
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14310
15039
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14311
- add(import_types50.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15040
+ add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14312
15041
  }
14313
15042
  }
14314
15043
  return { nodesAdded, edgesAdded };
@@ -14316,16 +15045,16 @@ async function addVercelServices(graph, services, scanPath) {
14316
15045
 
14317
15046
  // src/extract/infra/railway.ts
14318
15047
  init_cjs_shims();
14319
- var import_node_fs29 = require("fs");
14320
- var import_node_path61 = __toESM(require("path"), 1);
15048
+ var import_node_fs30 = require("fs");
15049
+ var import_node_path63 = __toESM(require("path"), 1);
14321
15050
  var import_smol_toml4 = require("smol-toml");
14322
- var import_types51 = require("@neat.is/types");
15051
+ var import_types52 = require("@neat.is/types");
14323
15052
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14324
15053
  async function readRailwayConfig(dir) {
14325
15054
  for (const filename of RAILWAY_FILENAMES) {
14326
- const abs = import_node_path61.default.join(dir, filename);
15055
+ const abs = import_node_path63.default.join(dir, filename);
14327
15056
  if (!await exists(abs)) continue;
14328
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15057
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14329
15058
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14330
15059
  return { config, relFile: filename, raw };
14331
15060
  }
@@ -14339,7 +15068,7 @@ async function addRailwayServices(graph, services, scanPath) {
14339
15068
  try {
14340
15069
  read = await readRailwayConfig(service.dir);
14341
15070
  } catch (err) {
14342
- recordExtractionError("infra railway", import_node_path61.default.relative(scanPath, service.dir), err);
15071
+ recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
14343
15072
  continue;
14344
15073
  }
14345
15074
  if (!read) continue;
@@ -14349,7 +15078,7 @@ async function addRailwayServices(graph, services, scanPath) {
14349
15078
  }
14350
15079
  const anchorId = service.node.id;
14351
15080
  const { config, relFile, raw } = read;
14352
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, relFile)));
15081
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
14353
15082
  const add = (edgeType, kind, name) => {
14354
15083
  if (!name) return;
14355
15084
  const result = emitPlatformResourceEdge(
@@ -14365,24 +15094,24 @@ async function addRailwayServices(graph, services, scanPath) {
14365
15094
  nodesAdded += result.nodesAdded;
14366
15095
  edgesAdded += result.edgesAdded;
14367
15096
  };
14368
- add(import_types51.EdgeType.RUNS_ON, "railway", "railway");
14369
- add(import_types51.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14370
- add(import_types51.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15097
+ add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15098
+ add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15099
+ add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14371
15100
  }
14372
15101
  return { nodesAdded, edgesAdded };
14373
15102
  }
14374
15103
 
14375
15104
  // src/extract/infra/supabase.ts
14376
15105
  init_cjs_shims();
14377
- var import_node_fs30 = require("fs");
14378
- var import_node_path62 = __toESM(require("path"), 1);
15106
+ var import_node_fs31 = require("fs");
15107
+ var import_node_path64 = __toESM(require("path"), 1);
14379
15108
  var import_smol_toml5 = require("smol-toml");
14380
- var import_types52 = require("@neat.is/types");
15109
+ var import_types53 = require("@neat.is/types");
14381
15110
  async function readSupabaseConfig(dir) {
14382
- const relFile = import_node_path62.default.join("supabase", "config.toml");
14383
- const abs = import_node_path62.default.join(dir, relFile);
15111
+ const relFile = import_node_path64.default.join("supabase", "config.toml");
15112
+ const abs = import_node_path64.default.join(dir, relFile);
14384
15113
  if (!await exists(abs)) return null;
14385
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15114
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14386
15115
  const config = (0, import_smol_toml5.parse)(raw);
14387
15116
  return { config, relFile, raw };
14388
15117
  }
@@ -14394,7 +15123,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14394
15123
  try {
14395
15124
  read = await readSupabaseConfig(service.dir);
14396
15125
  } catch (err) {
14397
- recordExtractionError("infra supabase", import_node_path62.default.relative(scanPath, service.dir), err);
15126
+ recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
14398
15127
  continue;
14399
15128
  }
14400
15129
  if (!read) continue;
@@ -14409,7 +15138,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14409
15138
  });
14410
15139
  }
14411
15140
  const anchorId = service.node.id;
14412
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15141
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14413
15142
  const add = (edgeType, kind, name) => {
14414
15143
  if (!name) return;
14415
15144
  const result = emitPlatformResourceEdge(
@@ -14425,10 +15154,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14425
15154
  nodesAdded += result.nodesAdded;
14426
15155
  edgesAdded += result.edgesAdded;
14427
15156
  };
14428
- add(import_types52.EdgeType.RUNS_ON, "supabase", "supabase");
14429
- for (const fn of Object.keys(config.functions ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "supabase-function", fn);
14430
- if (config.storage) add(import_types52.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14431
- if (config.auth) add(import_types52.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15157
+ add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15158
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15159
+ if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15160
+ if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
14432
15161
  }
14433
15162
  return { nodesAdded, edgesAdded };
14434
15163
  }
@@ -14451,14 +15180,14 @@ async function addInfra(graph, scanPath, services) {
14451
15180
 
14452
15181
  // src/extract/zod-shapes.ts
14453
15182
  init_cjs_shims();
14454
- var import_node_path63 = __toESM(require("path"), 1);
14455
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15183
+ var import_node_path65 = __toESM(require("path"), 1);
15184
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14456
15185
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
14457
- var import_types53 = require("@neat.is/types");
15186
+ var import_types54 = require("@neat.is/types");
14458
15187
  var ZOD_IMPORT_RE = /\bzod\b/;
14459
15188
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
14460
15189
  function parserForExt4(ext) {
14461
- const p = new import_tree_sitter16.default();
15190
+ const p = new import_tree_sitter17.default();
14462
15191
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
14463
15192
  return p;
14464
15193
  }
@@ -14546,7 +15275,7 @@ function topLevelSchemas(root) {
14546
15275
  }
14547
15276
  function zodShapesFromFile(file, serviceDir) {
14548
15277
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14549
- const tree = parseSource3(parserForExt4(import_node_path63.default.extname(file.path)), file.content);
15278
+ const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
14550
15279
  const out = [];
14551
15280
  const seen = /* @__PURE__ */ new Set();
14552
15281
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14560,11 +15289,11 @@ function zodShapesFromFile(file, serviceDir) {
14560
15289
  seen.add(name);
14561
15290
  const line = call.startPosition.row + 1;
14562
15291
  out.push({
14563
- infraId: (0, import_types53.infraId)("zod-schema", name),
15292
+ infraId: (0, import_types54.infraId)("zod-schema", name),
14564
15293
  name,
14565
15294
  fields,
14566
15295
  evidence: {
14567
- file: import_node_path63.default.relative(serviceDir, file.path),
15296
+ file: import_node_path65.default.relative(serviceDir, file.path),
14568
15297
  line,
14569
15298
  snippet: snippet(file.content, line)
14570
15299
  }
@@ -14595,7 +15324,7 @@ async function addZodShapes(graph, services) {
14595
15324
  if (!graph.hasNode(shape.infraId)) {
14596
15325
  const node = {
14597
15326
  id: shape.infraId,
14598
- type: import_types53.NodeType.InfraNode,
15327
+ type: import_types54.NodeType.InfraNode,
14599
15328
  name: shape.name,
14600
15329
  provider: "self",
14601
15330
  kind: "zod-schema"
@@ -14605,14 +15334,14 @@ async function addZodShapes(graph, services) {
14605
15334
  }
14606
15335
  if (shape.fields.length > 0) {
14607
15336
  const node = graph.getNodeAttributes(shape.infraId);
14608
- if (node.type === import_types53.NodeType.InfraNode) {
15337
+ if (node.type === import_types54.NodeType.InfraNode) {
14609
15338
  graph.replaceNodeAttributes(shape.infraId, {
14610
15339
  ...node,
14611
15340
  columns: foldColumns(
14612
15341
  node.columns,
14613
15342
  shape.fields,
14614
- import_types53.Provenance.EXTRACTED,
14615
- (0, import_types53.confidenceForExtracted)("structural")
15343
+ import_types54.Provenance.EXTRACTED,
15344
+ (0, import_types54.confidenceForExtracted)("structural")
14616
15345
  )
14617
15346
  });
14618
15347
  }
@@ -14626,15 +15355,15 @@ async function addZodShapes(graph, services) {
14626
15355
  );
14627
15356
  nodesAdded += n;
14628
15357
  edgesAdded += e;
14629
- const edgeId = (0, import_types53.extractedEdgeId)(fileNodeId, shape.infraId, import_types53.EdgeType.CONTAINS);
15358
+ const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
14630
15359
  if (!graph.hasEdge(edgeId)) {
14631
15360
  const edge = {
14632
15361
  id: edgeId,
14633
15362
  source: fileNodeId,
14634
15363
  target: shape.infraId,
14635
- type: import_types53.EdgeType.CONTAINS,
14636
- provenance: import_types53.Provenance.EXTRACTED,
14637
- confidence: (0, import_types53.confidenceForExtracted)("structural"),
15364
+ type: import_types54.EdgeType.CONTAINS,
15365
+ provenance: import_types54.Provenance.EXTRACTED,
15366
+ confidence: (0, import_types54.confidenceForExtracted)("structural"),
14638
15367
  evidence: shape.evidence
14639
15368
  };
14640
15369
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14648,7 +15377,7 @@ async function addZodShapes(graph, services) {
14648
15377
 
14649
15378
  // src/extract/firestore-rules.ts
14650
15379
  init_cjs_shims();
14651
- var import_types54 = require("@neat.is/types");
15380
+ var import_types55 = require("@neat.is/types");
14652
15381
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14653
15382
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14654
15383
  function stripComments(src) {
@@ -14788,7 +15517,7 @@ async function addFirestoreRules(graph, services) {
14788
15517
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14789
15518
  graph.forEachNode((id, attrs) => {
14790
15519
  const node = attrs;
14791
- if (node.type !== import_types54.NodeType.InfraNode) return;
15520
+ if (node.type !== import_types55.NodeType.InfraNode) return;
14792
15521
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14793
15522
  const fields = guards.get(collectionKeyFromName(node.name));
14794
15523
  if (!fields || fields.size === 0) return;
@@ -14801,17 +15530,17 @@ async function addFirestoreRules(graph, services) {
14801
15530
  }
14802
15531
 
14803
15532
  // src/extract/index.ts
14804
- var import_node_path65 = __toESM(require("path"), 1);
15533
+ var import_node_path67 = __toESM(require("path"), 1);
14805
15534
 
14806
15535
  // src/extract/retire.ts
14807
15536
  init_cjs_shims();
14808
- var import_node_fs31 = require("fs");
14809
- var import_node_path64 = __toESM(require("path"), 1);
14810
- var import_types55 = require("@neat.is/types");
15537
+ var import_node_fs32 = require("fs");
15538
+ var import_node_path66 = __toESM(require("path"), 1);
15539
+ var import_types56 = require("@neat.is/types");
14811
15540
  function dropOrphanedFileNodes(graph) {
14812
15541
  const orphans = [];
14813
15542
  graph.forEachNode((id, attrs) => {
14814
- if (attrs.type !== import_types55.NodeType.FileNode) return;
15543
+ if (attrs.type !== import_types56.NodeType.FileNode) return;
14815
15544
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14816
15545
  orphans.push(id);
14817
15546
  }
@@ -14824,14 +15553,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14824
15553
  const bases = [scanPath, ...serviceDirs];
14825
15554
  graph.forEachEdge((id, attrs) => {
14826
15555
  const edge = attrs;
14827
- if (edge.provenance !== import_types55.Provenance.EXTRACTED) return;
15556
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
14828
15557
  const evidenceFile = edge.evidence?.file;
14829
15558
  if (!evidenceFile) return;
14830
- if (import_node_path64.default.isAbsolute(evidenceFile)) {
14831
- if (!(0, import_node_fs31.existsSync)(evidenceFile)) toDrop.push(id);
15559
+ if (import_node_path66.default.isAbsolute(evidenceFile)) {
15560
+ if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
14832
15561
  return;
14833
15562
  }
14834
- const found = bases.some((base) => (0, import_node_fs31.existsSync)(import_node_path64.default.join(base, evidenceFile)));
15563
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
14835
15564
  if (!found) toDrop.push(id);
14836
15565
  });
14837
15566
  for (const id of toDrop) graph.dropEdge(id);
@@ -14888,7 +15617,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14888
15617
  }
14889
15618
  const droppedEntries = drainDroppedExtracted();
14890
15619
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14891
- const rejectedPath = import_node_path65.default.join(import_node_path65.default.dirname(opts.errorsPath), "rejected.ndjson");
15620
+ const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
14892
15621
  try {
14893
15622
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14894
15623
  } catch (err) {
@@ -14922,9 +15651,9 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14922
15651
 
14923
15652
  // src/persist.ts
14924
15653
  init_cjs_shims();
14925
- var import_node_fs32 = require("fs");
14926
- var import_node_path66 = __toESM(require("path"), 1);
14927
- var import_types56 = require("@neat.is/types");
15654
+ var import_node_fs33 = require("fs");
15655
+ var import_node_path68 = __toESM(require("path"), 1);
15656
+ var import_types57 = require("@neat.is/types");
14928
15657
  var SCHEMA_VERSION = 7;
14929
15658
  function migrateV1ToV2(payload) {
14930
15659
  const nodes = payload.graph.nodes;
@@ -14948,7 +15677,7 @@ function migrateV5ToV6(payload) {
14948
15677
  if (Array.isArray(nodes)) {
14949
15678
  for (const node of nodes) {
14950
15679
  const attrs = node.attributes;
14951
- if (!attrs || attrs.type !== import_types56.NodeType.InfraNode) continue;
15680
+ if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
14952
15681
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14953
15682
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14954
15683
  }
@@ -14964,12 +15693,12 @@ function migrateV2ToV3(payload) {
14964
15693
  for (const edge of edges) {
14965
15694
  const attrs = edge.attributes;
14966
15695
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
14967
- attrs.provenance = import_types56.Provenance.OBSERVED;
15696
+ attrs.provenance = import_types57.Provenance.OBSERVED;
14968
15697
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
14969
15698
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
14970
15699
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
14971
15700
  if (type && source && target) {
14972
- const newId = (0, import_types56.observedEdgeId)(source, target, type);
15701
+ const newId = (0, import_types57.observedEdgeId)(source, target, type);
14973
15702
  attrs.id = newId;
14974
15703
  if (edge.key) edge.key = newId;
14975
15704
  }
@@ -14978,7 +15707,7 @@ function migrateV2ToV3(payload) {
14978
15707
  return { ...payload, schemaVersion: 3 };
14979
15708
  }
14980
15709
  async function ensureDir(filePath) {
14981
- await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(filePath), { recursive: true });
15710
+ await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
14982
15711
  }
14983
15712
  async function saveGraphToDisk(graph, outPath) {
14984
15713
  await ensureDir(outPath);
@@ -14988,13 +15717,13 @@ async function saveGraphToDisk(graph, outPath) {
14988
15717
  graph: graph.export()
14989
15718
  };
14990
15719
  const tmp = `${outPath}.tmp`;
14991
- await import_node_fs32.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
14992
- await import_node_fs32.promises.rename(tmp, outPath);
15720
+ await import_node_fs33.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15721
+ await import_node_fs33.promises.rename(tmp, outPath);
14993
15722
  }
14994
15723
  async function loadGraphFromDisk(graph, outPath) {
14995
15724
  let raw;
14996
15725
  try {
14997
- raw = await import_node_fs32.promises.readFile(outPath, "utf8");
15726
+ raw = await import_node_fs33.promises.readFile(outPath, "utf8");
14998
15727
  } catch (err) {
14999
15728
  if (err.code === "ENOENT") return;
15000
15729
  throw err;
@@ -15068,23 +15797,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
15068
15797
 
15069
15798
  // src/projects.ts
15070
15799
  init_cjs_shims();
15071
- var import_node_path67 = __toESM(require("path"), 1);
15800
+ var import_node_path69 = __toESM(require("path"), 1);
15072
15801
  function pathsForProject(project, baseDir) {
15073
15802
  if (project === DEFAULT_PROJECT) {
15074
15803
  return {
15075
- snapshotPath: import_node_path67.default.join(baseDir, "graph.json"),
15076
- errorsPath: import_node_path67.default.join(baseDir, "errors.ndjson"),
15077
- staleEventsPath: import_node_path67.default.join(baseDir, "stale-events.ndjson"),
15078
- embeddingsCachePath: import_node_path67.default.join(baseDir, "embeddings.json"),
15079
- policyViolationsPath: import_node_path67.default.join(baseDir, "policy-violations.ndjson")
15804
+ snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
15805
+ errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
15806
+ staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
15807
+ embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
15808
+ policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
15080
15809
  };
15081
15810
  }
15082
15811
  return {
15083
- snapshotPath: import_node_path67.default.join(baseDir, `${project}.json`),
15084
- errorsPath: import_node_path67.default.join(baseDir, `errors.${project}.ndjson`),
15085
- staleEventsPath: import_node_path67.default.join(baseDir, `stale-events.${project}.ndjson`),
15086
- embeddingsCachePath: import_node_path67.default.join(baseDir, `embeddings.${project}.json`),
15087
- policyViolationsPath: import_node_path67.default.join(baseDir, `policy-violations.${project}.ndjson`)
15812
+ snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
15813
+ errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
15814
+ staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
15815
+ embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
15816
+ policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
15088
15817
  };
15089
15818
  }
15090
15819
  var Projects = class {
@@ -15122,19 +15851,19 @@ var Projects = class {
15122
15851
  init_cjs_shims();
15123
15852
  var import_fastify2 = __toESM(require("fastify"), 1);
15124
15853
  var import_cors = __toESM(require("@fastify/cors"), 1);
15125
- var import_types91 = require("@neat.is/types");
15854
+ var import_types92 = require("@neat.is/types");
15126
15855
 
15127
15856
  // src/extend/index.ts
15128
15857
  init_cjs_shims();
15129
- var import_node_fs34 = require("fs");
15130
- var import_node_path69 = __toESM(require("path"), 1);
15858
+ var import_node_fs35 = require("fs");
15859
+ var import_node_path71 = __toESM(require("path"), 1);
15131
15860
  var import_node_os2 = __toESM(require("os"), 1);
15132
15861
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
15133
15862
 
15134
15863
  // src/installers/package-manager.ts
15135
15864
  init_cjs_shims();
15136
- var import_node_fs33 = require("fs");
15137
- var import_node_path68 = __toESM(require("path"), 1);
15865
+ var import_node_fs34 = require("fs");
15866
+ var import_node_path70 = __toESM(require("path"), 1);
15138
15867
  var import_node_child_process = require("child_process");
15139
15868
  var LOCKFILE_PRIORITY = [
15140
15869
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -15149,29 +15878,29 @@ var LOCKFILE_PRIORITY = [
15149
15878
  var NPM_FALLBACK_ARGS = ["install", "--no-audit", "--no-fund", "--prefer-offline"];
15150
15879
  async function exists2(p) {
15151
15880
  try {
15152
- await import_node_fs33.promises.access(p);
15881
+ await import_node_fs34.promises.access(p);
15153
15882
  return true;
15154
15883
  } catch {
15155
15884
  return false;
15156
15885
  }
15157
15886
  }
15158
15887
  async function detectPackageManager(serviceDir) {
15159
- let dir = import_node_path68.default.resolve(serviceDir);
15888
+ let dir = import_node_path70.default.resolve(serviceDir);
15160
15889
  const stops = /* @__PURE__ */ new Set();
15161
15890
  for (let i = 0; i < 64; i++) {
15162
15891
  if (stops.has(dir)) break;
15163
15892
  stops.add(dir);
15164
15893
  for (const candidate of LOCKFILE_PRIORITY) {
15165
- const lockPath = import_node_path68.default.join(dir, candidate.lockfile);
15894
+ const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
15166
15895
  if (await exists2(lockPath)) {
15167
15896
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
15168
15897
  }
15169
15898
  }
15170
- const parent = import_node_path68.default.dirname(dir);
15899
+ const parent = import_node_path70.default.dirname(dir);
15171
15900
  if (parent === dir) break;
15172
15901
  dir = parent;
15173
15902
  }
15174
- return { pm: "npm", cwd: import_node_path68.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15903
+ return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
15175
15904
  }
15176
15905
  async function runPackageManagerInstall(cmd) {
15177
15906
  return new Promise((resolve) => {
@@ -15213,15 +15942,15 @@ ${err.message}`
15213
15942
  // src/extend/index.ts
15214
15943
  async function fileExists2(p) {
15215
15944
  try {
15216
- await import_node_fs34.promises.access(p);
15945
+ await import_node_fs35.promises.access(p);
15217
15946
  return true;
15218
15947
  } catch {
15219
15948
  return false;
15220
15949
  }
15221
15950
  }
15222
15951
  async function readPackageJson(scanPath) {
15223
- const pkgPath = import_node_path69.default.join(scanPath, "package.json");
15224
- const raw = await import_node_fs34.promises.readFile(pkgPath, "utf8");
15952
+ const pkgPath = import_node_path71.default.join(scanPath, "package.json");
15953
+ const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
15225
15954
  return JSON.parse(raw);
15226
15955
  }
15227
15956
  var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
@@ -15234,27 +15963,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
15234
15963
  ]);
15235
15964
  async function findHookFiles(scanPath) {
15236
15965
  const found = [];
15237
- const walk9 = async (dir) => {
15238
- const entries = await import_node_fs34.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15966
+ const walk10 = async (dir) => {
15967
+ const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
15239
15968
  for (const entry2 of entries) {
15240
15969
  if (entry2.isDirectory()) {
15241
15970
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
15242
- await walk9(import_node_path69.default.join(dir, entry2.name));
15971
+ await walk10(import_node_path71.default.join(dir, entry2.name));
15243
15972
  } else if (entry2.isFile()) {
15244
15973
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
15245
- const rel = import_node_path69.default.relative(scanPath, import_node_path69.default.join(dir, entry2.name));
15246
- found.push(rel.split(import_node_path69.default.sep).join("/"));
15974
+ const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry2.name));
15975
+ found.push(rel.split(import_node_path71.default.sep).join("/"));
15247
15976
  }
15248
15977
  }
15249
15978
  }
15250
15979
  };
15251
- await walk9(scanPath);
15980
+ await walk10(scanPath);
15252
15981
  return found.sort();
15253
15982
  }
15254
15983
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15255
15984
  let fallback = null;
15256
15985
  for (const file of hookFiles) {
15257
- const content = await import_node_fs34.promises.readFile(import_node_path69.default.join(scanPath, file), "utf8");
15986
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
15258
15987
  const patched = splicedContent(content, snippet2);
15259
15988
  if (patched !== null) return { file, content, patched };
15260
15989
  if (fallback === null) fallback = { file, content };
@@ -15262,12 +15991,12 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
15262
15991
  return { file: fallback.file, content: fallback.content, patched: null };
15263
15992
  }
15264
15993
  function extendLogPath() {
15265
- return process.env.NEAT_EXTEND_LOG ?? import_node_path69.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15994
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
15266
15995
  }
15267
15996
  async function appendExtendLog(entry2) {
15268
15997
  const logPath = extendLogPath();
15269
- await import_node_fs34.promises.mkdir(import_node_path69.default.dirname(logPath), { recursive: true });
15270
- await import_node_fs34.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
15998
+ await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
15999
+ await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
15271
16000
  }
15272
16001
  function splicedContent(fileContent, snippet2) {
15273
16002
  if (fileContent.includes("__INSTRUMENTATION_BLOCK__")) {
@@ -15325,7 +16054,7 @@ function lookupInstrumentation(library, installedVersion) {
15325
16054
  }
15326
16055
  async function describeProjectInstrumentation(ctx) {
15327
16056
  const hookFiles = await findHookFiles(ctx.scanPath);
15328
- const envNeat = await fileExists2(import_node_path69.default.join(ctx.scanPath, ".env.neat"));
16057
+ const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
15329
16058
  const registryInstrPackages = new Set(
15330
16059
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
15331
16060
  );
@@ -15347,7 +16076,7 @@ async function applyExtension(ctx, args, options) {
15347
16076
  );
15348
16077
  }
15349
16078
  for (const file of hookFiles) {
15350
- const content = await import_node_fs34.promises.readFile(import_node_path69.default.join(ctx.scanPath, file), "utf8");
16079
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
15351
16080
  if (content.includes(args.registration_snippet)) {
15352
16081
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
15353
16082
  }
@@ -15359,18 +16088,18 @@ async function applyExtension(ctx, args, options) {
15359
16088
  );
15360
16089
  }
15361
16090
  const primaryFile = primary.file;
15362
- const primaryPath = import_node_path69.default.join(ctx.scanPath, primaryFile);
16091
+ const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
15363
16092
  const filesTouched = [];
15364
16093
  const depsAdded = [];
15365
- const pkgPath = import_node_path69.default.join(ctx.scanPath, "package.json");
16094
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
15366
16095
  const pkg = await readPackageJson(ctx.scanPath);
15367
16096
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
15368
16097
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
15369
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16098
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15370
16099
  filesTouched.push("package.json");
15371
16100
  depsAdded.push(`${args.instrumentation_package}@${args.version}`);
15372
16101
  }
15373
- await import_node_fs34.promises.writeFile(primaryPath, primary.patched, "utf8");
16102
+ await import_node_fs35.promises.writeFile(primaryPath, primary.patched, "utf8");
15374
16103
  filesTouched.push(primaryFile);
15375
16104
  const cmd = await detectPackageManager(ctx.scanPath);
15376
16105
  const installer = options?.runInstall ?? runPackageManagerInstall;
@@ -15401,7 +16130,7 @@ async function dryRunExtension(ctx, args) {
15401
16130
  };
15402
16131
  }
15403
16132
  for (const file of hookFiles) {
15404
- const content = await import_node_fs34.promises.readFile(import_node_path69.default.join(ctx.scanPath, file), "utf8");
16133
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
15405
16134
  if (content.includes(args.registration_snippet)) {
15406
16135
  return {
15407
16136
  library: args.library,
@@ -15436,28 +16165,28 @@ async function rollbackExtension(ctx, args) {
15436
16165
  if (!await fileExists2(logPath)) {
15437
16166
  return { undone: false, message: "no apply found for library" };
15438
16167
  }
15439
- const raw = await import_node_fs34.promises.readFile(logPath, "utf8");
16168
+ const raw = await import_node_fs35.promises.readFile(logPath, "utf8");
15440
16169
  const entries = raw.trim().split("\n").filter(Boolean).map((line) => JSON.parse(line));
15441
16170
  const match = [...entries].reverse().find((e) => e.project === ctx.project && e.library === args.library);
15442
16171
  if (!match) {
15443
16172
  return { undone: false, message: "no apply found for library" };
15444
16173
  }
15445
- const pkgPath = import_node_path69.default.join(ctx.scanPath, "package.json");
16174
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
15446
16175
  if (await fileExists2(pkgPath)) {
15447
16176
  const pkg = await readPackageJson(ctx.scanPath);
15448
16177
  if (pkg.dependencies?.[match.instrumentation_package]) {
15449
16178
  const { [match.instrumentation_package]: _removed, ...rest } = pkg.dependencies;
15450
16179
  pkg.dependencies = rest;
15451
- await import_node_fs34.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
16180
+ await import_node_fs35.promises.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
15452
16181
  }
15453
16182
  }
15454
16183
  const hookFiles = await findHookFiles(ctx.scanPath);
15455
16184
  for (const file of hookFiles) {
15456
- const filePath = import_node_path69.default.join(ctx.scanPath, file);
15457
- const content = await import_node_fs34.promises.readFile(filePath, "utf8");
16185
+ const filePath = import_node_path71.default.join(ctx.scanPath, file);
16186
+ const content = await import_node_fs35.promises.readFile(filePath, "utf8");
15458
16187
  if (content.includes(match.registration_snippet)) {
15459
16188
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
15460
- await import_node_fs34.promises.writeFile(filePath, filtered, "utf8");
16189
+ await import_node_fs35.promises.writeFile(filePath, filtered, "utf8");
15461
16190
  break;
15462
16191
  }
15463
16192
  }
@@ -15469,39 +16198,39 @@ async function rollbackExtension(ctx, args) {
15469
16198
 
15470
16199
  // src/divergences.ts
15471
16200
  init_cjs_shims();
15472
- var import_types57 = require("@neat.is/types");
16201
+ var import_types58 = require("@neat.is/types");
15473
16202
  function bucketKey(source, target, type) {
15474
16203
  return `${type}|${source}|${target}`;
15475
16204
  }
15476
16205
  function bucketSourceFor(graph, edge) {
15477
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) return edge.source;
15478
- const parsed = (0, import_types57.parseFileId)(edge.source);
16206
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16207
+ const parsed = (0, import_types58.parseFileId)(edge.source);
15479
16208
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
15480
16209
  const target = graph.getNodeAttributes(edge.target);
15481
- if (target.type !== import_types57.NodeType.DatabaseNode) return edge.source;
15482
- return (0, import_types57.serviceId)(parsed.service);
16210
+ if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16211
+ return (0, import_types58.serviceId)(parsed.service);
15483
16212
  }
15484
16213
  function bucketEdges(graph) {
15485
16214
  const buckets2 = /* @__PURE__ */ new Map();
15486
16215
  graph.forEachEdge((id, attrs) => {
15487
16216
  const e = attrs;
15488
- const parsed = (0, import_types57.parseEdgeId)(id);
16217
+ const parsed = (0, import_types58.parseEdgeId)(id);
15489
16218
  const provenance = parsed?.provenance ?? e.provenance;
15490
16219
  const source = bucketSourceFor(graph, e);
15491
16220
  const key = bucketKey(source, e.target, e.type);
15492
16221
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
15493
16222
  switch (provenance) {
15494
- case import_types57.Provenance.EXTRACTED:
16223
+ case import_types58.Provenance.EXTRACTED:
15495
16224
  cur.extracted = e;
15496
16225
  break;
15497
- case import_types57.Provenance.OBSERVED:
16226
+ case import_types58.Provenance.OBSERVED:
15498
16227
  cur.observed = e;
15499
16228
  break;
15500
- case import_types57.Provenance.INFERRED:
16229
+ case import_types58.Provenance.INFERRED:
15501
16230
  cur.inferred = e;
15502
16231
  break;
15503
16232
  default:
15504
- if (e.provenance === import_types57.Provenance.STALE) cur.stale = e;
16233
+ if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
15505
16234
  }
15506
16235
  buckets2.set(key, cur);
15507
16236
  });
@@ -15510,22 +16239,22 @@ function bucketEdges(graph) {
15510
16239
  function nodeIsFrontier(graph, nodeId) {
15511
16240
  if (!graph.hasNode(nodeId)) return false;
15512
16241
  const attrs = graph.getNodeAttributes(nodeId);
15513
- return attrs.type === import_types57.NodeType.FrontierNode;
16242
+ return attrs.type === import_types58.NodeType.FrontierNode;
15514
16243
  }
15515
16244
  function nodeIsWebsocketChannel(graph, nodeId) {
15516
16245
  if (!graph.hasNode(nodeId)) return false;
15517
16246
  const attrs = graph.getNodeAttributes(nodeId);
15518
- return attrs.type === import_types57.NodeType.WebSocketChannelNode;
16247
+ return attrs.type === import_types58.NodeType.WebSocketChannelNode;
15519
16248
  }
15520
16249
  function nodeIsServerAction(graph, nodeId) {
15521
16250
  if (!graph.hasNode(nodeId)) return false;
15522
16251
  const attrs = graph.getNodeAttributes(nodeId);
15523
- return attrs.type === import_types57.NodeType.ServerActionNode;
16252
+ return attrs.type === import_types58.NodeType.ServerActionNode;
15524
16253
  }
15525
16254
  function nodeIsSymbol(graph, nodeId) {
15526
16255
  if (!graph.hasNode(nodeId)) return false;
15527
16256
  const attrs = graph.getNodeAttributes(nodeId);
15528
- return attrs.type === import_types57.NodeType.SymbolNode;
16257
+ return attrs.type === import_types58.NodeType.SymbolNode;
15529
16258
  }
15530
16259
  function clampConfidence(n) {
15531
16260
  if (!Number.isFinite(n)) return 0;
@@ -15545,14 +16274,14 @@ function gradedConfidence(edge) {
15545
16274
  return clampConfidence(confidenceForEdge(edge));
15546
16275
  }
15547
16276
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
15548
- import_types57.EdgeType.CALLS,
15549
- import_types57.EdgeType.CONNECTS_TO,
15550
- import_types57.EdgeType.PUBLISHES_TO,
15551
- import_types57.EdgeType.CONSUMES_FROM
16277
+ import_types58.EdgeType.CALLS,
16278
+ import_types58.EdgeType.CONNECTS_TO,
16279
+ import_types58.EdgeType.PUBLISHES_TO,
16280
+ import_types58.EdgeType.CONSUMES_FROM
15552
16281
  ]);
15553
16282
  function detectMissingDivergences(graph, bucket) {
15554
16283
  const out = [];
15555
- if (bucket.type === import_types57.EdgeType.CONTAINS) return out;
16284
+ if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
15556
16285
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
15557
16286
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
15558
16287
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -15594,7 +16323,7 @@ function declaredHostFor(svc) {
15594
16323
  function hasExtractedConfiguredBy(graph, svcId) {
15595
16324
  for (const edgeId of graph.outboundEdges(svcId)) {
15596
16325
  const e = graph.getEdgeAttributes(edgeId);
15597
- if (e.type === import_types57.EdgeType.CONFIGURED_BY && e.provenance === import_types57.Provenance.EXTRACTED) {
16326
+ if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
15598
16327
  return true;
15599
16328
  }
15600
16329
  }
@@ -15607,10 +16336,10 @@ function detectHostMismatch(graph, svcId, svc) {
15607
16336
  const out = [];
15608
16337
  for (const edgeId of graph.outboundEdges(svcId)) {
15609
16338
  const edge = graph.getEdgeAttributes(edgeId);
15610
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15611
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16339
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16340
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15612
16341
  const target = graph.getNodeAttributes(edge.target);
15613
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16342
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15614
16343
  const observedHost = target.host?.trim();
15615
16344
  if (!observedHost) continue;
15616
16345
  if (observedHost === declaredHost) continue;
@@ -15632,10 +16361,10 @@ function detectCompatDivergences(graph, svcId, svc) {
15632
16361
  const deps = svc.dependencies ?? {};
15633
16362
  for (const edgeId of graph.outboundEdges(svcId)) {
15634
16363
  const edge = graph.getEdgeAttributes(edgeId);
15635
- if (edge.type !== import_types57.EdgeType.CONNECTS_TO) continue;
15636
- if (edge.provenance !== import_types57.Provenance.OBSERVED) continue;
16364
+ if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16365
+ if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
15637
16366
  const target = graph.getNodeAttributes(edge.target);
15638
- if (target.type !== import_types57.NodeType.DatabaseNode) continue;
16367
+ if (target.type !== import_types58.NodeType.DatabaseNode) continue;
15639
16368
  for (const pair of compatPairs()) {
15640
16369
  if (pair.engine !== target.engine) continue;
15641
16370
  const declared = deps[pair.driver];
@@ -15732,7 +16461,7 @@ function suppressHostMismatchHalves(all) {
15732
16461
  for (const d of all) {
15733
16462
  if (d.type !== "host-mismatch") continue;
15734
16463
  observedHalf.add(`${d.source}->${d.target}`);
15735
- declaredHalf.add((0, import_types57.databaseId)(d.extractedHost));
16464
+ declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
15736
16465
  }
15737
16466
  if (observedHalf.size === 0) return all;
15738
16467
  return all.filter((d) => {
@@ -15751,13 +16480,13 @@ function computeDivergences(graph, opts = {}) {
15751
16480
  }
15752
16481
  graph.forEachNode((nodeId, attrs) => {
15753
16482
  const n = attrs;
15754
- if (n.type === import_types57.NodeType.ServiceNode) {
16483
+ if (n.type === import_types58.NodeType.ServiceNode) {
15755
16484
  const svc = n;
15756
16485
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
15757
16486
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
15758
16487
  return;
15759
16488
  }
15760
- if (n.type === import_types57.NodeType.InfraNode && n.kind === "sql-table") {
16489
+ if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
15761
16490
  for (const d of detectColumnDrift(n)) all.push(d);
15762
16491
  }
15763
16492
  });
@@ -15793,7 +16522,7 @@ function computeDivergences(graph, opts = {}) {
15793
16522
  const bc = "column" in b && b.column ? b.column : "";
15794
16523
  return ac.localeCompare(bc);
15795
16524
  });
15796
- return import_types57.DivergenceResultSchema.parse({
16525
+ return import_types58.DivergenceResultSchema.parse({
15797
16526
  divergences: filtered,
15798
16527
  totalAffected: filtered.length,
15799
16528
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -15847,7 +16576,7 @@ function queryLogEntries(opts) {
15847
16576
 
15848
16577
  // src/diff.ts
15849
16578
  init_cjs_shims();
15850
- var import_node_fs35 = require("fs");
16579
+ var import_node_fs36 = require("fs");
15851
16580
  async function loadSnapshotForDiff(target) {
15852
16581
  if (/^https?:\/\//i.test(target)) {
15853
16582
  const res = await fetch(target);
@@ -15856,7 +16585,7 @@ async function loadSnapshotForDiff(target) {
15856
16585
  }
15857
16586
  return await res.json();
15858
16587
  }
15859
- const raw = await import_node_fs35.promises.readFile(target, "utf8");
16588
+ const raw = await import_node_fs36.promises.readFile(target, "utf8");
15860
16589
  return JSON.parse(raw);
15861
16590
  }
15862
16591
  function indexEntries(entries) {
@@ -15924,28 +16653,28 @@ function canonicalJson(value) {
15924
16653
 
15925
16654
  // src/registry.ts
15926
16655
  init_cjs_shims();
15927
- var import_node_fs36 = require("fs");
16656
+ var import_node_fs37 = require("fs");
15928
16657
  var import_node_os3 = __toESM(require("os"), 1);
15929
- var import_node_path70 = __toESM(require("path"), 1);
15930
- var import_types58 = require("@neat.is/types");
16658
+ var import_node_path72 = __toESM(require("path"), 1);
16659
+ var import_types59 = require("@neat.is/types");
15931
16660
  var LOCK_TIMEOUT_MS = 5e3;
15932
16661
  var LOCK_RETRY_MS = 50;
15933
16662
  function neatHome() {
15934
16663
  const override = process.env.NEAT_HOME;
15935
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15936
- return import_node_path70.default.join(import_node_os3.default.homedir(), ".neat");
16664
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16665
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15937
16666
  }
15938
16667
  function registryPath() {
15939
- return import_node_path70.default.join(neatHome(), "projects.json");
16668
+ return import_node_path72.default.join(neatHome(), "projects.json");
15940
16669
  }
15941
16670
  function registryLockPath() {
15942
- return import_node_path70.default.join(neatHome(), "projects.json.lock");
16671
+ return import_node_path72.default.join(neatHome(), "projects.json.lock");
15943
16672
  }
15944
16673
  function daemonPidPath() {
15945
- return import_node_path70.default.join(neatHome(), "neatd.pid");
16674
+ return import_node_path72.default.join(neatHome(), "neatd.pid");
15946
16675
  }
15947
16676
  function daemonsDir() {
15948
- return import_node_path70.default.join(neatHome(), "daemons");
16677
+ return import_node_path72.default.join(neatHome(), "daemons");
15949
16678
  }
15950
16679
  function isFiniteInt(v) {
15951
16680
  return typeof v === "number" && Number.isFinite(v);
@@ -15978,7 +16707,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15978
16707
  const dir = daemonsDir();
15979
16708
  let names;
15980
16709
  try {
15981
- names = await import_node_fs36.promises.readdir(dir);
16710
+ names = await import_node_fs37.promises.readdir(dir);
15982
16711
  } catch (err) {
15983
16712
  if (err.code === "ENOENT") return [];
15984
16713
  throw err;
@@ -15986,10 +16715,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15986
16715
  const out = [];
15987
16716
  for (const name of names) {
15988
16717
  if (!name.endsWith(".json")) continue;
15989
- const file = import_node_path70.default.join(dir, name);
16718
+ const file = import_node_path72.default.join(dir, name);
15990
16719
  let raw;
15991
16720
  try {
15992
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16721
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15993
16722
  } catch {
15994
16723
  continue;
15995
16724
  }
@@ -16015,7 +16744,7 @@ function isPidAliveDefault(pid) {
16015
16744
  }
16016
16745
  async function readPidFile(file) {
16017
16746
  try {
16018
- const raw = await import_node_fs36.promises.readFile(file, "utf8");
16747
+ const raw = await import_node_fs37.promises.readFile(file, "utf8");
16019
16748
  const pid = Number.parseInt(raw.trim(), 10);
16020
16749
  return Number.isInteger(pid) && pid > 0 ? pid : void 0;
16021
16750
  } catch {
@@ -16063,24 +16792,24 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
16063
16792
  }
16064
16793
  }
16065
16794
  async function writeAtomically(target, contents) {
16066
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(target), { recursive: true });
16795
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
16067
16796
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
16068
- const fd = await import_node_fs36.promises.open(tmp, "w");
16797
+ const fd = await import_node_fs37.promises.open(tmp, "w");
16069
16798
  try {
16070
16799
  await fd.writeFile(contents, "utf8");
16071
16800
  await fd.sync();
16072
16801
  } finally {
16073
16802
  await fd.close();
16074
16803
  }
16075
- await import_node_fs36.promises.rename(tmp, target);
16804
+ await import_node_fs37.promises.rename(tmp, target);
16076
16805
  }
16077
16806
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
16078
16807
  const deadline = Date.now() + timeoutMs;
16079
- await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(lockPath), { recursive: true });
16808
+ await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
16080
16809
  let probedHolder = false;
16081
16810
  while (true) {
16082
16811
  try {
16083
- const fd = await import_node_fs36.promises.open(lockPath, "wx");
16812
+ const fd = await import_node_fs37.promises.open(lockPath, "wx");
16084
16813
  try {
16085
16814
  await fd.writeFile(`${process.pid}
16086
16815
  `, "utf8");
@@ -16105,7 +16834,7 @@ async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaul
16105
16834
  }
16106
16835
  }
16107
16836
  async function releaseLock(lockPath) {
16108
- await import_node_fs36.promises.unlink(lockPath).catch(() => {
16837
+ await import_node_fs37.promises.unlink(lockPath).catch(() => {
16109
16838
  });
16110
16839
  }
16111
16840
  async function withLock(fn) {
@@ -16121,7 +16850,7 @@ async function readRegistry() {
16121
16850
  const file = registryPath();
16122
16851
  let raw;
16123
16852
  try {
16124
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16853
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
16125
16854
  } catch (err) {
16126
16855
  if (err.code === "ENOENT") {
16127
16856
  return { version: 1, projects: [] };
@@ -16129,10 +16858,10 @@ async function readRegistry() {
16129
16858
  throw err;
16130
16859
  }
16131
16860
  const parsed = JSON.parse(raw);
16132
- return import_types58.RegistryFileSchema.parse(parsed);
16861
+ return import_types59.RegistryFileSchema.parse(parsed);
16133
16862
  }
16134
16863
  async function writeRegistry(reg) {
16135
- const validated = import_types58.RegistryFileSchema.parse(reg);
16864
+ const validated = import_types59.RegistryFileSchema.parse(reg);
16136
16865
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
16137
16866
  }
16138
16867
  async function getProject(name) {
@@ -16176,7 +16905,7 @@ function pruneTtlMs() {
16176
16905
  }
16177
16906
  async function statPathStatus(p) {
16178
16907
  try {
16179
- const stat = await import_node_fs36.promises.stat(p);
16908
+ const stat = await import_node_fs37.promises.stat(p);
16180
16909
  return stat.isDirectory() ? "present" : "unknown";
16181
16910
  } catch (err) {
16182
16911
  return err.code === "ENOENT" ? "gone" : "unknown";
@@ -16277,8 +17006,8 @@ init_auth();
16277
17006
  // src/connectors-config.ts
16278
17007
  init_cjs_shims();
16279
17008
  var import_node_os4 = __toESM(require("os"), 1);
16280
- var import_node_path71 = __toESM(require("path"), 1);
16281
- var import_node_fs37 = require("fs");
17009
+ var import_node_path73 = __toESM(require("path"), 1);
17010
+ var import_node_fs38 = require("fs");
16282
17011
  var CONNECTORS_CONFIG_VERSION = 1;
16283
17012
  var EnvRefUnsetError = class extends Error {
16284
17013
  ref;
@@ -16292,17 +17021,17 @@ var EnvRefUnsetError = class extends Error {
16292
17021
  };
16293
17022
  function neatHome2() {
16294
17023
  const override = process.env.NEAT_HOME;
16295
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
16296
- return import_node_path71.default.join(import_node_os4.default.homedir(), ".neat");
17024
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
17025
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16297
17026
  }
16298
17027
  function connectorsConfigPath(home = neatHome2()) {
16299
- return import_node_path71.default.join(home, "connectors.json");
17028
+ return import_node_path73.default.join(home, "connectors.json");
16300
17029
  }
16301
17030
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16302
17031
  async function warnIfModeLooserThan0600(file) {
16303
17032
  if (process.platform === "win32") return;
16304
17033
  try {
16305
- const stat = await import_node_fs37.promises.stat(file);
17034
+ const stat = await import_node_fs38.promises.stat(file);
16306
17035
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16307
17036
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16308
17037
  console.warn(
@@ -16316,7 +17045,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16316
17045
  const file = connectorsConfigPath(home);
16317
17046
  let raw;
16318
17047
  try {
16319
- raw = await import_node_fs37.promises.readFile(file, "utf8");
17048
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16320
17049
  } catch (err) {
16321
17050
  if (err.code === "ENOENT") {
16322
17051
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16483,15 +17212,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16483
17212
 
16484
17213
  // src/connectors/index.ts
16485
17214
  init_cjs_shims();
16486
- var import_types59 = require("@neat.is/types");
17215
+ var import_types60 = require("@neat.is/types");
16487
17216
  var NO_ENV = "unknown";
16488
17217
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16489
17218
  if (!graph.hasNode(targetNodeId)) return void 0;
16490
17219
  const sites = [];
16491
17220
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16492
17221
  const edge = graph.getEdgeAttributes(edgeId);
16493
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16494
- const parsed = (0, import_types59.parseFileId)(edge.source);
17222
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
17223
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16495
17224
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16496
17225
  const site = { relPath: edge.evidence.file };
16497
17226
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16502,7 +17231,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16502
17231
  function routeCallSiteFor(graph, targetNodeId) {
16503
17232
  if (!graph.hasNode(targetNodeId)) return void 0;
16504
17233
  const attrs = graph.getNodeAttributes(targetNodeId);
16505
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
17234
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16506
17235
  const site = { relPath: attrs.path };
16507
17236
  if (attrs.line !== void 0) site.line = attrs.line;
16508
17237
  return site;
@@ -17000,10 +17729,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
17000
17729
  // src/connectors/supabase/map.ts
17001
17730
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
17002
17731
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
17003
- function targetFromRestPath(path76) {
17004
- const rpcMatch = REST_RPC_PATH_RE.exec(path76);
17732
+ function targetFromRestPath(path78) {
17733
+ const rpcMatch = REST_RPC_PATH_RE.exec(path78);
17005
17734
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
17006
- const tableMatch = REST_TABLE_PATH_RE.exec(path76);
17735
+ const tableMatch = REST_TABLE_PATH_RE.exec(path78);
17007
17736
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
17008
17737
  return null;
17009
17738
  }
@@ -17114,23 +17843,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
17114
17843
 
17115
17844
  // src/connectors/supabase/resolve.ts
17116
17845
  init_cjs_shims();
17117
- var import_types61 = require("@neat.is/types");
17846
+ var import_types62 = require("@neat.is/types");
17118
17847
  function createSupabaseResolveTarget(graph, config) {
17119
17848
  return (signal, _ctx) => {
17120
17849
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
17121
17850
  return null;
17122
17851
  }
17123
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17852
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17124
17853
  if (graph.hasNode(subResourceId)) {
17125
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17854
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17126
17855
  }
17127
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17856
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
17128
17857
  if (graph.hasNode(bareResourceId)) {
17129
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17858
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17130
17859
  }
17131
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17860
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
17132
17861
  if (graph.hasNode(projectLevelId)) {
17133
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17862
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
17134
17863
  }
17135
17864
  return null;
17136
17865
  };
@@ -17223,7 +17952,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
17223
17952
 
17224
17953
  // src/connectors/railway/index.ts
17225
17954
  init_cjs_shims();
17226
- var import_types65 = require("@neat.is/types");
17955
+ var import_types66 = require("@neat.is/types");
17227
17956
 
17228
17957
  // src/connectors/railway/client.ts
17229
17958
  init_cjs_shims();
@@ -17374,7 +18103,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17374
18103
  const out = [];
17375
18104
  graph.forEachNode((_id, attrs) => {
17376
18105
  const node = attrs;
17377
- if (node.type !== import_types65.NodeType.RouteNode) return;
18106
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17378
18107
  const route = attrs;
17379
18108
  if (route.service !== serviceName) return;
17380
18109
  out.push({
@@ -17478,12 +18207,12 @@ function createRailwayResolveTarget(config) {
17478
18207
  const serviceName = config.serviceNameById[config.serviceId];
17479
18208
  if (!serviceName) return null;
17480
18209
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17481
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
18210
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17482
18211
  }
17483
18212
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17484
18213
  const peerName = config.serviceNameById[signal.targetName];
17485
18214
  if (!peerName) return null;
17486
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
18215
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17487
18216
  }
17488
18217
  return null;
17489
18218
  };
@@ -17607,9 +18336,9 @@ function parseFirebaseTargetName(targetName) {
17607
18336
  const secondSep = rest.indexOf(FIELD_SEP);
17608
18337
  if (secondSep === -1) return null;
17609
18338
  const method = rest.slice(0, secondSep);
17610
- const path76 = rest.slice(secondSep + 1);
17611
- if (!resourceName || !method || !path76) return null;
17612
- return { resourceName, method, path: path76 };
18339
+ const path78 = rest.slice(secondSep + 1);
18340
+ if (!resourceName || !method || !path78) return null;
18341
+ return { resourceName, method, path: path78 };
17613
18342
  }
17614
18343
  function resourceNameFor(type, labels) {
17615
18344
  if (!labels) return null;
@@ -17647,14 +18376,14 @@ function mapLogEntryToSignal(entry2) {
17647
18376
  if (!req2) return null;
17648
18377
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
17649
18378
  const method = req2.requestMethod.toUpperCase();
17650
- const path76 = pathFromRequestUrl(req2.requestUrl);
17651
- if (path76 === null) return null;
18379
+ const path78 = pathFromRequestUrl(req2.requestUrl);
18380
+ if (path78 === null) return null;
17652
18381
  const timestamp = entry2.timestamp;
17653
18382
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17654
18383
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
17655
18384
  return {
17656
18385
  targetKind: resourceType,
17657
- targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
18386
+ targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
17658
18387
  callCount: 1,
17659
18388
  errorCount: isError ? 1 : 0,
17660
18389
  lastObservedIso: timestamp
@@ -17671,7 +18400,7 @@ function mapLogEntriesToSignals(entries) {
17671
18400
 
17672
18401
  // src/connectors/firebase/resolve.ts
17673
18402
  init_cjs_shims();
17674
- var import_types66 = require("@neat.is/types");
18403
+ var import_types67 = require("@neat.is/types");
17675
18404
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17676
18405
  switch (resourceType) {
17677
18406
  case "cloud_function":
@@ -17686,7 +18415,7 @@ function routeEntriesFor(graph, serviceName) {
17686
18415
  const entries = [];
17687
18416
  graph.forEachNode((_id, attrs) => {
17688
18417
  const node = attrs;
17689
- if (node.type !== import_types66.NodeType.RouteNode) return;
18418
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17690
18419
  const route = attrs;
17691
18420
  if (route.service !== serviceName) return;
17692
18421
  entries.push({
@@ -17718,7 +18447,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17718
18447
  return {
17719
18448
  targetNodeId: match.routeNodeId,
17720
18449
  serviceName,
17721
- edgeType: import_types66.EdgeType.CALLS
18450
+ edgeType: import_types67.EdgeType.CALLS
17722
18451
  };
17723
18452
  };
17724
18453
  }
@@ -17745,7 +18474,7 @@ init_cjs_shims();
17745
18474
 
17746
18475
  // src/connectors/cloudflare/connector.ts
17747
18476
  init_cjs_shims();
17748
- var import_types68 = require("@neat.is/types");
18477
+ var import_types69 = require("@neat.is/types");
17749
18478
 
17750
18479
  // src/connectors/cloudflare/client.ts
17751
18480
  init_cjs_shims();
@@ -17861,7 +18590,7 @@ function mapEventToSignal(event) {
17861
18590
  if (Number.isNaN(observedAt.getTime())) return null;
17862
18591
  const statusCode = metadata?.statusCode;
17863
18592
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17864
- const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18593
+ const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17865
18594
  return {
17866
18595
  targetKind: CLOUDFLARE_TARGET_KIND,
17867
18596
  targetName: scriptName,
@@ -17869,7 +18598,7 @@ function mapEventToSignal(event) {
17869
18598
  errorCount: isError ? 1 : 0,
17870
18599
  lastObservedIso: observedAt.toISOString(),
17871
18600
  method,
17872
- ...path76 ? { path: path76 } : {},
18601
+ ...path78 ? { path: path78 } : {},
17873
18602
  ...typeof statusCode === "number" ? { statusCode } : {},
17874
18603
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17875
18604
  };
@@ -17909,19 +18638,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17909
18638
  graph.forEachNode((id, attrs) => {
17910
18639
  if (found) return;
17911
18640
  const a = attrs;
17912
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18641
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
17913
18642
  found = id;
17914
18643
  }
17915
18644
  });
17916
18645
  return found;
17917
18646
  }
17918
- function findMatchingRouteNode(graph, serviceName, method, path76) {
17919
- const normalizedPath = normalizePathTemplate(path76);
18647
+ function findMatchingRouteNode(graph, serviceName, method, path78) {
18648
+ const normalizedPath = normalizePathTemplate(path78);
17920
18649
  let found = null;
17921
18650
  graph.forEachNode((id, attrs) => {
17922
18651
  if (found) return;
17923
18652
  const a = attrs;
17924
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18653
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17925
18654
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17926
18655
  const routeMethod = (a.method ?? "").toUpperCase();
17927
18656
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17933,18 +18662,18 @@ function createCloudflareResolveTarget(config, graph) {
17933
18662
  return (signal) => {
17934
18663
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17935
18664
  const scriptName = signal.targetName;
17936
- const { method, path: path76 } = signal;
18665
+ const { method, path: path78 } = signal;
17937
18666
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17938
- if (!method || !path76) return wholeFileId;
17939
- return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
18667
+ if (!method || !path78) return wholeFileId;
18668
+ return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
17940
18669
  };
17941
18670
  const mapping = config.workers?.[scriptName];
17942
18671
  if (mapping) {
17943
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18672
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17944
18673
  return {
17945
18674
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17946
18675
  serviceName: mapping.service,
17947
- edgeType: import_types68.EdgeType.CALLS
18676
+ edgeType: import_types69.EdgeType.CALLS
17948
18677
  };
17949
18678
  }
17950
18679
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17953,13 +18682,13 @@ function createCloudflareResolveTarget(config, graph) {
17953
18682
  return {
17954
18683
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17955
18684
  serviceName: fileNode.service,
17956
- edgeType: import_types68.EdgeType.CALLS
18685
+ edgeType: import_types69.EdgeType.CALLS
17957
18686
  };
17958
18687
  }
17959
18688
  return {
17960
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18689
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17961
18690
  serviceName: scriptName,
17962
- edgeType: import_types68.EdgeType.CALLS,
18691
+ edgeType: import_types69.EdgeType.CALLS,
17963
18692
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
17964
18693
  };
17965
18694
  };
@@ -18155,14 +18884,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
18155
18884
 
18156
18885
  // src/connectors/neon/resolve.ts
18157
18886
  init_cjs_shims();
18158
- var import_types72 = require("@neat.is/types");
18887
+ var import_types73 = require("@neat.is/types");
18159
18888
  function createNeonResolveTarget(config) {
18160
18889
  return (signal) => {
18161
18890
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18162
18891
  return {
18163
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18892
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
18164
18893
  serviceName: config.serviceName,
18165
- edgeType: import_types72.EdgeType.CALLS,
18894
+ edgeType: import_types73.EdgeType.CALLS,
18166
18895
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
18167
18896
  };
18168
18897
  };
@@ -18288,9 +19017,9 @@ function parseCloudRunTargetName(targetName) {
18288
19017
  const secondSep = rest.indexOf(FIELD_SEP2);
18289
19018
  if (secondSep === -1) return null;
18290
19019
  const method = rest.slice(0, secondSep);
18291
- const path76 = rest.slice(secondSep + 1);
18292
- if (!serviceName || !method || !path76) return null;
18293
- return { serviceName, method, path: path76 };
19020
+ const path78 = rest.slice(secondSep + 1);
19021
+ if (!serviceName || !method || !path78) return null;
19022
+ return { serviceName, method, path: path78 };
18294
19023
  }
18295
19024
 
18296
19025
  // src/connectors/cloud-run/map.ts
@@ -18319,14 +19048,14 @@ function mapLogEntryToSignal2(entry2) {
18319
19048
  if (!req2) return null;
18320
19049
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
18321
19050
  const method = req2.requestMethod.toUpperCase();
18322
- const path76 = pathFromRequestUrl2(req2.requestUrl);
18323
- if (path76 === null) return null;
19051
+ const path78 = pathFromRequestUrl2(req2.requestUrl);
19052
+ if (path78 === null) return null;
18324
19053
  const timestamp = entry2.timestamp;
18325
19054
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
18326
19055
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
18327
19056
  return {
18328
19057
  targetKind: CLOUD_RUN_TARGET_KIND,
18329
- targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
19058
+ targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
18330
19059
  callCount: 1,
18331
19060
  errorCount: isError ? 1 : 0,
18332
19061
  lastObservedIso: timestamp
@@ -18343,14 +19072,14 @@ function mapLogEntriesToSignals2(entries) {
18343
19072
 
18344
19073
  // src/connectors/cloud-run/resolve.ts
18345
19074
  init_cjs_shims();
18346
- var import_types76 = require("@neat.is/types");
19075
+ var import_types77 = require("@neat.is/types");
18347
19076
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18348
19077
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18349
19078
  let found = null;
18350
19079
  graph.forEachNode((_id, attrs) => {
18351
19080
  if (found) return;
18352
19081
  const node = attrs;
18353
- if (node.type !== import_types76.NodeType.RouteNode) return;
19082
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18354
19083
  const route = attrs;
18355
19084
  if (route.service !== serviceName || !route.pathTemplate) return;
18356
19085
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18365,23 +19094,23 @@ function createCloudRunResolveTarget(graph, config) {
18365
19094
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18366
19095
  const identity = parseCloudRunTargetName(signal.targetName);
18367
19096
  if (!identity) return null;
18368
- const { serviceName: gcpServiceName, method, path: path76 } = identity;
19097
+ const { serviceName: gcpServiceName, method, path: path78 } = identity;
18369
19098
  const mappedService = config.serviceMap?.[gcpServiceName];
18370
19099
  if (mappedService) {
18371
19100
  const routeNodeId = findMatchingRouteNode2(
18372
19101
  graph,
18373
19102
  mappedService,
18374
19103
  method,
18375
- normalizePathTemplate(path76)
19104
+ normalizePathTemplate(path78)
18376
19105
  );
18377
19106
  if (routeNodeId) {
18378
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
19107
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18379
19108
  }
18380
19109
  }
18381
19110
  return {
18382
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
19111
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18383
19112
  serviceName: mappedService ?? gcpServiceName,
18384
- edgeType: import_types76.EdgeType.CALLS,
19113
+ edgeType: import_types77.EdgeType.CALLS,
18385
19114
  ensureInfraNode: {
18386
19115
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18387
19116
  name: gcpServiceName,
@@ -18422,7 +19151,7 @@ function createCloudRunConnector(graph, config = {}) {
18422
19151
 
18423
19152
  // src/connectors/render/index.ts
18424
19153
  init_cjs_shims();
18425
- var import_types79 = require("@neat.is/types");
19154
+ var import_types80 = require("@neat.is/types");
18426
19155
 
18427
19156
  // src/connectors/render/types.ts
18428
19157
  init_cjs_shims();
@@ -18500,7 +19229,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18500
19229
  const out = [];
18501
19230
  graph.forEachNode((_id, attrs) => {
18502
19231
  const node = attrs;
18503
- if (node.type !== import_types79.NodeType.RouteNode) return;
19232
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18504
19233
  const route = attrs;
18505
19234
  if (route.service !== serviceName) return;
18506
19235
  out.push({
@@ -18585,7 +19314,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18585
19314
  function createRenderResolveTarget(config) {
18586
19315
  return (signal) => {
18587
19316
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18588
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
19317
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18589
19318
  }
18590
19319
  return null;
18591
19320
  };
@@ -18723,21 +19452,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18723
19452
 
18724
19453
  // src/connectors/planetscale/resolve.ts
18725
19454
  init_cjs_shims();
18726
- var import_types83 = require("@neat.is/types");
19455
+ var import_types84 = require("@neat.is/types");
18727
19456
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18728
19457
  function createPlanetscaleResolveTarget(graph, config) {
18729
19458
  const databaseName = `${config.organization}/${config.database}`;
18730
19459
  return (signal, _ctx) => {
18731
19460
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18732
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19461
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18733
19462
  if (graph.hasNode(tableId)) {
18734
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19463
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18735
19464
  }
18736
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19465
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18737
19466
  return {
18738
19467
  targetNodeId: providerId,
18739
19468
  serviceName: config.serviceName,
18740
- edgeType: import_types83.EdgeType.CALLS,
19469
+ edgeType: import_types84.EdgeType.CALLS,
18741
19470
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18742
19471
  };
18743
19472
  };
@@ -19002,7 +19731,7 @@ function mapBuildsToSignals(builds, serviceName) {
19002
19731
 
19003
19732
  // src/connectors/eas/resolve.ts
19004
19733
  init_cjs_shims();
19005
- var import_types88 = require("@neat.is/types");
19734
+ var import_types89 = require("@neat.is/types");
19006
19735
  var NO_ENV2 = "unknown";
19007
19736
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
19008
19737
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -19018,8 +19747,8 @@ function configBasenamesForPhase(phase) {
19018
19747
  function configNodeService(graph, configNodeId) {
19019
19748
  for (const edgeId of graph.inboundEdges(configNodeId)) {
19020
19749
  const edge = graph.getEdgeAttributes(edgeId);
19021
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
19022
- const parsed = (0, import_types88.parseFileId)(edge.source);
19750
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19751
+ const parsed = (0, import_types89.parseFileId)(edge.source);
19023
19752
  if (parsed) return parsed.service;
19024
19753
  }
19025
19754
  return null;
@@ -19030,7 +19759,7 @@ function findConfigNode(graph, basenames, serviceName) {
19030
19759
  graph.forEachNode((id, attrs) => {
19031
19760
  if (scoped) return;
19032
19761
  const node = attrs;
19033
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19762
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
19034
19763
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
19035
19764
  if (anyMatch === null) anyMatch = id;
19036
19765
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -19047,13 +19776,13 @@ function createEasResolveTarget(graph) {
19047
19776
  if (basenames.length > 0) {
19048
19777
  const configNodeId = findConfigNode(graph, basenames, serviceName);
19049
19778
  if (configNodeId) {
19050
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19779
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
19051
19780
  }
19052
19781
  }
19053
19782
  return {
19054
19783
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
19055
19784
  serviceName,
19056
- edgeType: import_types88.EdgeType.CALLS
19785
+ edgeType: import_types89.EdgeType.CALLS
19057
19786
  };
19058
19787
  };
19059
19788
  }
@@ -19778,11 +20507,11 @@ function registerRoutes(scope, ctx) {
19778
20507
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19779
20508
  const parsed = [];
19780
20509
  for (const c of candidates) {
19781
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20510
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19782
20511
  if (!r.success) {
19783
20512
  return reply.code(400).send({
19784
20513
  error: `unknown divergence type "${c}"`,
19785
- allowed: import_types91.DivergenceTypeSchema.options
20514
+ allowed: import_types92.DivergenceTypeSchema.options
19786
20515
  });
19787
20516
  }
19788
20517
  parsed.push(r.data);
@@ -20124,7 +20853,7 @@ function registerRoutes(scope, ctx) {
20124
20853
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
20125
20854
  let violations = await log.readAll();
20126
20855
  if (req2.query.severity) {
20127
- const sev = import_types91.PolicySeveritySchema.safeParse(req2.query.severity);
20856
+ const sev = import_types92.PolicySeveritySchema.safeParse(req2.query.severity);
20128
20857
  if (!sev.success) {
20129
20858
  return reply.code(400).send({
20130
20859
  error: "invalid severity",
@@ -20163,7 +20892,7 @@ function registerRoutes(scope, ctx) {
20163
20892
  scope.post("/policies/check", async (req2, reply) => {
20164
20893
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
20165
20894
  if (!proj) return;
20166
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
20895
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
20167
20896
  if (!parsed.success) {
20168
20897
  return reply.code(400).send({
20169
20898
  error: "invalid /policies/check body",
@@ -20484,8 +21213,8 @@ init_auth();
20484
21213
 
20485
21214
  // src/unrouted.ts
20486
21215
  init_cjs_shims();
20487
- var import_node_fs38 = require("fs");
20488
- var import_node_path72 = __toESM(require("path"), 1);
21216
+ var import_node_fs39 = require("fs");
21217
+ var import_node_path74 = __toESM(require("path"), 1);
20489
21218
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
20490
21219
  return {
20491
21220
  timestamp: now.toISOString(),
@@ -20495,34 +21224,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
20495
21224
  };
20496
21225
  }
20497
21226
  async function appendUnroutedSpan(neatHome4, record) {
20498
- const target = import_node_path72.default.join(neatHome4, "errors.ndjson");
20499
- await import_node_fs38.promises.mkdir(neatHome4, { recursive: true });
20500
- await import_node_fs38.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
21227
+ const target = import_node_path74.default.join(neatHome4, "errors.ndjson");
21228
+ await import_node_fs39.promises.mkdir(neatHome4, { recursive: true });
21229
+ await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
20501
21230
  }
20502
21231
  function unroutedErrorsPath(neatHome4) {
20503
- return import_node_path72.default.join(neatHome4, "errors.ndjson");
21232
+ return import_node_path74.default.join(neatHome4, "errors.ndjson");
20504
21233
  }
20505
21234
 
20506
21235
  // src/daemon.ts
20507
- var import_types92 = require("@neat.is/types");
21236
+ var import_types93 = require("@neat.is/types");
20508
21237
  function daemonJsonPath(scanPath) {
20509
- return import_node_path73.default.join(scanPath, "neat-out", "daemon.json");
21238
+ return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
20510
21239
  }
20511
21240
  function daemonsDiscoveryDir(home) {
20512
21241
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
20513
- return import_node_path73.default.join(base, "daemons");
21242
+ return import_node_path75.default.join(base, "daemons");
20514
21243
  }
20515
21244
  function daemonDiscoveryPath(project, home) {
20516
- return import_node_path73.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
21245
+ return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
20517
21246
  }
20518
21247
  function sanitizeDiscoveryName(project) {
20519
21248
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
20520
21249
  }
20521
21250
  function neatHomeFromEnv() {
20522
21251
  const env = process.env.NEAT_HOME;
20523
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21252
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20524
21253
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20525
- return import_node_path73.default.join(home, ".neat");
21254
+ return import_node_path75.default.join(home, ".neat");
20526
21255
  }
20527
21256
  function resolveNeatVersion() {
20528
21257
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -20554,7 +21283,7 @@ async function clearDaemonRecord(record, home) {
20554
21283
  } catch {
20555
21284
  }
20556
21285
  try {
20557
- await import_node_fs39.promises.unlink(daemonDiscoveryPath(record.project, home));
21286
+ await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
20558
21287
  } catch {
20559
21288
  }
20560
21289
  }
@@ -20563,12 +21292,12 @@ function reconcileDaemonRecordSync(record, home) {
20563
21292
  const stopped = { ...record, status: "stopped" };
20564
21293
  const target = daemonJsonPath(record.projectPath);
20565
21294
  const tmp = `${target}.${process.pid}.tmp`;
20566
- (0, import_node_fs39.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
20567
- (0, import_node_fs39.renameSync)(tmp, target);
21295
+ (0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
21296
+ (0, import_node_fs40.renameSync)(tmp, target);
20568
21297
  } catch {
20569
21298
  }
20570
21299
  try {
20571
- (0, import_node_fs39.unlinkSync)(daemonDiscoveryPath(record.project, home));
21300
+ (0, import_node_fs40.unlinkSync)(daemonDiscoveryPath(record.project, home));
20572
21301
  } catch {
20573
21302
  }
20574
21303
  }
@@ -20591,11 +21320,11 @@ function teardownSlot(slot) {
20591
21320
  }
20592
21321
  }
20593
21322
  function neatHomeFor(opts) {
20594
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path73.default.resolve(opts.neatHome);
21323
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
20595
21324
  const env = process.env.NEAT_HOME;
20596
- if (env && env.length > 0) return import_node_path73.default.resolve(env);
21325
+ if (env && env.length > 0) return import_node_path75.default.resolve(env);
20597
21326
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
20598
- return import_node_path73.default.join(home, ".neat");
21327
+ return import_node_path75.default.join(home, ".neat");
20599
21328
  }
20600
21329
  function routeSpanToProject(serviceName, projects) {
20601
21330
  if (!serviceName) return DEFAULT_PROJECT;
@@ -20643,13 +21372,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
20643
21372
  if (!serviceName) return true;
20644
21373
  if (serviceNameMatchesProject(serviceName, project)) return true;
20645
21374
  return graph.someNode(
20646
- (_id, attrs) => attrs.type === import_types92.NodeType.ServiceNode && attrs.name === serviceName
21375
+ (_id, attrs) => attrs.type === import_types93.NodeType.ServiceNode && attrs.name === serviceName
20647
21376
  );
20648
21377
  }
20649
21378
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
20650
- const paths = pathsForProject(entry2.name, import_node_path73.default.join(entry2.path, "neat-out"));
21379
+ const paths = pathsForProject(entry2.name, import_node_path75.default.join(entry2.path, "neat-out"));
20651
21380
  try {
20652
- const stat = await import_node_fs39.promises.stat(entry2.path);
21381
+ const stat = await import_node_fs40.promises.stat(entry2.path);
20653
21382
  if (!stat.isDirectory()) {
20654
21383
  throw new Error(`registered path ${entry2.path} is not a directory`);
20655
21384
  }
@@ -20767,7 +21496,7 @@ async function startDaemon(opts = {}) {
20767
21496
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
20768
21497
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
20769
21498
  const singleProject = projectArg;
20770
- const singleProjectPath = singleProject && projectPathArg ? import_node_path73.default.resolve(projectPathArg) : null;
21499
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
20771
21500
  if (singleProject && !singleProjectPath) {
20772
21501
  throw new Error(
20773
21502
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -20775,14 +21504,14 @@ async function startDaemon(opts = {}) {
20775
21504
  }
20776
21505
  if (!singleProject) {
20777
21506
  try {
20778
- await import_node_fs39.promises.access(regPath);
21507
+ await import_node_fs40.promises.access(regPath);
20779
21508
  } catch {
20780
21509
  throw new Error(
20781
21510
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
20782
21511
  );
20783
21512
  }
20784
21513
  }
20785
- const pidPath = import_node_path73.default.join(home, "neatd.pid");
21514
+ const pidPath = import_node_path75.default.join(home, "neatd.pid");
20786
21515
  await writeAtomically(pidPath, `${process.pid}
20787
21516
  `);
20788
21517
  const slots = /* @__PURE__ */ new Map();
@@ -20994,7 +21723,7 @@ async function startDaemon(opts = {}) {
20994
21723
  }
20995
21724
  if (restApp) await restApp.close().catch(() => {
20996
21725
  });
20997
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21726
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
20998
21727
  });
20999
21728
  throw new Error(
21000
21729
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -21162,7 +21891,7 @@ async function startDaemon(opts = {}) {
21162
21891
  });
21163
21892
  if (otlpApp) await otlpApp.close().catch(() => {
21164
21893
  });
21165
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21894
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21166
21895
  });
21167
21896
  throw new Error(
21168
21897
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -21197,7 +21926,7 @@ async function startDaemon(opts = {}) {
21197
21926
  });
21198
21927
  if (otlpApp) await otlpApp.close().catch(() => {
21199
21928
  });
21200
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
21929
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21201
21930
  });
21202
21931
  throw new Error(
21203
21932
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -21244,9 +21973,9 @@ async function startDaemon(opts = {}) {
21244
21973
  let registryWatcher = null;
21245
21974
  let reloadTimer = null;
21246
21975
  if (!singleProject) try {
21247
- const regDir = import_node_path73.default.dirname(regPath);
21248
- const regBase = import_node_path73.default.basename(regPath);
21249
- registryWatcher = (0, import_node_fs39.watch)(regDir, (_eventType, filename) => {
21976
+ const regDir = import_node_path75.default.dirname(regPath);
21977
+ const regBase = import_node_path75.default.basename(regPath);
21978
+ registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
21250
21979
  if (filename !== null && filename !== regBase) return;
21251
21980
  if (reloadTimer) clearTimeout(reloadTimer);
21252
21981
  reloadTimer = setTimeout(() => {
@@ -21295,7 +22024,7 @@ async function startDaemon(opts = {}) {
21295
22024
  if (daemonRecord) {
21296
22025
  await clearDaemonRecord(daemonRecord, home);
21297
22026
  }
21298
- await import_node_fs39.promises.unlink(pidPath).catch(() => {
22027
+ await import_node_fs40.promises.unlink(pidPath).catch(() => {
21299
22028
  });
21300
22029
  };
21301
22030
  return {
@@ -21318,9 +22047,9 @@ init_auth();
21318
22047
  // src/web-spawn.ts
21319
22048
  init_cjs_shims();
21320
22049
  var import_node_child_process2 = require("child_process");
21321
- var import_node_fs40 = require("fs");
22050
+ var import_node_fs41 = require("fs");
21322
22051
  var import_node_net = __toESM(require("net"), 1);
21323
- var import_node_path74 = __toESM(require("path"), 1);
22052
+ var import_node_path76 = __toESM(require("path"), 1);
21324
22053
  var DEFAULT_WEB_PORT = 6328;
21325
22054
  var DEFAULT_REST_PORT = 8080;
21326
22055
  function asValidPort(value) {
@@ -21329,11 +22058,11 @@ function asValidPort(value) {
21329
22058
  }
21330
22059
  function projectRoot() {
21331
22060
  const fromEnv = process.env.NEAT_SCAN_PATH;
21332
- return import_node_path74.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
22061
+ return import_node_path76.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
21333
22062
  }
21334
22063
  async function readDaemonPorts(root) {
21335
22064
  try {
21336
- const raw = await import_node_fs40.promises.readFile(import_node_path74.default.join(root, "neat-out", "daemon.json"), "utf8");
22065
+ const raw = await import_node_fs41.promises.readFile(import_node_path76.default.join(root, "neat-out", "daemon.json"), "utf8");
21337
22066
  const parsed = JSON.parse(raw);
21338
22067
  const ports = parsed?.ports ?? {};
21339
22068
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -21378,10 +22107,10 @@ function resolveWebPackageDir() {
21378
22107
  eval("require")
21379
22108
  );
21380
22109
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
21381
- return import_node_path74.default.dirname(pkgJsonPath);
22110
+ return import_node_path76.default.dirname(pkgJsonPath);
21382
22111
  }
21383
22112
  function resolveStandaloneServerEntry(webDir) {
21384
- return import_node_path74.default.join(webDir, ".next/standalone/packages/web/server.js");
22113
+ return import_node_path76.default.join(webDir, ".next/standalone/packages/web/server.js");
21385
22114
  }
21386
22115
  async function pickInternalPort() {
21387
22116
  return new Promise((resolve, reject) => {
@@ -21434,7 +22163,7 @@ async function spawnWebUI(restPort, opts = {}) {
21434
22163
  NEAT_API_URL: apiUrl
21435
22164
  };
21436
22165
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
21437
- cwd: import_node_path74.default.dirname(serverEntry),
22166
+ cwd: import_node_path76.default.dirname(serverEntry),
21438
22167
  env,
21439
22168
  stdio: ["ignore", "inherit", "inherit"],
21440
22169
  detached: false
@@ -21561,15 +22290,15 @@ async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
21561
22290
  }
21562
22291
  function isLocalBehind(local, remote) {
21563
22292
  if (local === remote) return false;
21564
- const parse10 = (v) => {
22293
+ const parse11 = (v) => {
21565
22294
  const core = v.split(/[-+]/)[0] ?? v;
21566
22295
  return core.split(".").map((s) => {
21567
22296
  const n = Number.parseInt(s, 10);
21568
22297
  return Number.isFinite(n) ? n : 0;
21569
22298
  });
21570
22299
  };
21571
- const a = parse10(local);
21572
- const b = parse10(remote);
22300
+ const a = parse11(local);
22301
+ const b = parse11(remote);
21573
22302
  const len = Math.max(a.length, b.length);
21574
22303
  for (let i = 0; i < len; i++) {
21575
22304
  const av = a[i] ?? 0;
@@ -21610,14 +22339,14 @@ function localVersion() {
21610
22339
  }
21611
22340
  function neatHome3() {
21612
22341
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
21613
- return import_node_path75.default.resolve(process.env.NEAT_HOME);
22342
+ return import_node_path77.default.resolve(process.env.NEAT_HOME);
21614
22343
  }
21615
22344
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
21616
- return import_node_path75.default.join(home, ".neat");
22345
+ return import_node_path77.default.join(home, ".neat");
21617
22346
  }
21618
22347
  async function readPid() {
21619
22348
  try {
21620
- const raw = await import_node_fs41.promises.readFile(import_node_path75.default.join(neatHome3(), "neatd.pid"), "utf8");
22349
+ const raw = await import_node_fs42.promises.readFile(import_node_path77.default.join(neatHome3(), "neatd.pid"), "utf8");
21621
22350
  const n = Number.parseInt(raw.trim(), 10);
21622
22351
  return Number.isFinite(n) ? n : null;
21623
22352
  } catch {