@neat.is/core 0.9.11 → 0.9.12

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 path78 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path78) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path78)) {
64
+ const path80 = (req2.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path80) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path80)) {
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_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
- return import_node_path52.default.resolve(here, "..", "proto");
197
+ const here = import_node_path53.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
198
+ return import_node_path53.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_path52, import_node_crypto2, grpc, protoLoader;
266
+ var import_node_url, import_node_path53, 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_path52 = __toESM(require("path"), 1);
272
+ import_node_path53 = __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 path78 = q === -1 ? v : v.slice(0, q);
419
- if (path78.length > 0) return path78;
418
+ const path80 = q === -1 ? v : v.slice(0, q);
419
+ if (path80.length > 0) return path80;
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_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
- const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
481
+ const here = import_node_path54.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
482
+ const protoRoot = import_node_path54.default.resolve(here, "..", "proto");
483
483
  const root = new import_protobufjs.default.Root();
484
- root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
484
+ root.resolvePath = (_origin, target) => import_node_path54.default.resolve(protoRoot, target);
485
485
  root.loadSync(
486
486
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
487
487
  { keepCase: true }
@@ -759,12 +759,12 @@ async function listenSteppingOtlp(app, requestedPort, host) {
759
759
  }
760
760
  }
761
761
  }
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;
762
+ var import_node_path54, 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;
763
763
  var init_otel = __esm({
764
764
  "src/otel.ts"() {
765
765
  "use strict";
766
766
  init_cjs_shims();
767
- import_node_path53 = __toESM(require("path"), 1);
767
+ import_node_path54 = __toESM(require("path"), 1);
768
768
  import_node_url2 = require("url");
769
769
  import_node_zlib = __toESM(require("zlib"), 1);
770
770
  import_fastify = __toESM(require("fastify"), 1);
@@ -788,14 +788,14 @@ __export(neatd_exports, {
788
788
  });
789
789
  module.exports = __toCommonJS(neatd_exports);
790
790
  init_cjs_shims();
791
- var import_node_fs42 = require("fs");
792
- var import_node_path77 = __toESM(require("path"), 1);
791
+ var import_node_fs43 = require("fs");
792
+ var import_node_path79 = __toESM(require("path"), 1);
793
793
  var import_node_module2 = require("module");
794
794
 
795
795
  // src/daemon.ts
796
796
  init_cjs_shims();
797
- var import_node_fs40 = require("fs");
798
- var import_node_path75 = __toESM(require("path"), 1);
797
+ var import_node_fs41 = require("fs");
798
+ var import_node_path77 = __toESM(require("path"), 1);
799
799
  var import_node_module = require("module");
800
800
 
801
801
  // src/graph.ts
@@ -1328,19 +1328,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1328
1328
  function longestIncomingWalk(graph, start, maxDepth) {
1329
1329
  let best = { path: [start], edges: [] };
1330
1330
  const visited = /* @__PURE__ */ new Set([start]);
1331
- function step(node, path78, edges) {
1332
- if (path78.length > best.path.length) {
1333
- best = { path: [...path78], edges: [...edges] };
1331
+ function step(node, path80, edges) {
1332
+ if (path80.length > best.path.length) {
1333
+ best = { path: [...path80], edges: [...edges] };
1334
1334
  }
1335
- if (path78.length - 1 >= maxDepth) return;
1335
+ if (path80.length - 1 >= maxDepth) return;
1336
1336
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1337
1337
  for (const [srcId, edge] of incoming) {
1338
1338
  if (visited.has(srcId)) continue;
1339
1339
  visited.add(srcId);
1340
- path78.push(srcId);
1340
+ path80.push(srcId);
1341
1341
  edges.push(edge);
1342
- step(srcId, path78, edges);
1343
- path78.pop();
1342
+ step(srcId, path80, edges);
1343
+ path80.pop();
1344
1344
  edges.pop();
1345
1345
  visited.delete(srcId);
1346
1346
  }
@@ -1348,14 +1348,14 @@ function longestIncomingWalk(graph, start, maxDepth) {
1348
1348
  step(start, [start], []);
1349
1349
  return best;
1350
1350
  }
1351
- function databaseRootCauseShape(graph, origin, walk10) {
1351
+ function databaseRootCauseShape(graph, origin, walk11) {
1352
1352
  const targetDb = origin;
1353
1353
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1354
1354
  if (candidatePairs.length === 0) return null;
1355
- for (const id of walk10.path) {
1355
+ for (const id of walk11.path) {
1356
1356
  const owner = resolveOwningService(graph, id);
1357
1357
  if (!owner) continue;
1358
- const { id: serviceId16, svc } = owner;
1358
+ const { id: serviceId17, svc } = owner;
1359
1359
  const deps = svc.dependencies ?? {};
1360
1360
  for (const pair of candidatePairs) {
1361
1361
  const declared = deps[pair.driver];
@@ -1368,7 +1368,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
1368
1368
  );
1369
1369
  if (!result.compatible) {
1370
1370
  return {
1371
- rootCauseNode: serviceId16,
1371
+ rootCauseNode: serviceId17,
1372
1372
  rootCauseReason: result.reason ?? "incompatible driver",
1373
1373
  ...result.minDriverVersion ? {
1374
1374
  fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
@@ -1379,11 +1379,11 @@ function databaseRootCauseShape(graph, origin, walk10) {
1379
1379
  }
1380
1380
  return null;
1381
1381
  }
1382
- function serviceRootCauseShape(graph, _origin, walk10) {
1383
- for (const id of walk10.path) {
1382
+ function serviceRootCauseShape(graph, _origin, walk11) {
1383
+ for (const id of walk11.path) {
1384
1384
  const owner = resolveOwningService(graph, id);
1385
1385
  if (!owner) continue;
1386
- const { id: serviceId16, svc } = owner;
1386
+ const { id: serviceId17, svc } = owner;
1387
1387
  const deps = svc.dependencies ?? {};
1388
1388
  const serviceNodeEngine = svc.nodeEngine;
1389
1389
  for (const constraint of nodeEngineConstraints()) {
@@ -1392,7 +1392,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
1392
1392
  const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
1393
1393
  if (!result.compatible && result.reason) {
1394
1394
  return {
1395
- rootCauseNode: serviceId16,
1395
+ rootCauseNode: serviceId17,
1396
1396
  rootCauseReason: result.reason,
1397
1397
  ...result.requiredNodeVersion ? {
1398
1398
  fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
@@ -1407,7 +1407,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
1407
1407
  const result = checkPackageConflict(conflict, declared, requiredDeclared);
1408
1408
  if (!result.compatible && result.reason) {
1409
1409
  return {
1410
- rootCauseNode: serviceId16,
1410
+ rootCauseNode: serviceId17,
1411
1411
  rootCauseReason: result.reason,
1412
1412
  fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
1413
1413
  };
@@ -1416,15 +1416,15 @@ function serviceRootCauseShape(graph, _origin, walk10) {
1416
1416
  }
1417
1417
  return null;
1418
1418
  }
1419
- function fileRootCauseShape(graph, origin, walk10) {
1419
+ function fileRootCauseShape(graph, origin, walk11) {
1420
1420
  const owner = resolveOwningService(graph, origin.id);
1421
1421
  if (!owner) return null;
1422
- return serviceRootCauseShape(graph, owner.svc, walk10);
1422
+ return serviceRootCauseShape(graph, owner.svc, walk11);
1423
1423
  }
1424
- function symbolRootCauseShape(graph, origin, walk10) {
1424
+ function symbolRootCauseShape(graph, origin, walk11) {
1425
1425
  const owner = resolveOwningService(graph, origin.id);
1426
1426
  if (!owner) return null;
1427
- return serviceRootCauseShape(graph, owner.svc, walk10);
1427
+ return serviceRootCauseShape(graph, owner.svc, walk11);
1428
1428
  }
1429
1429
  var rootCauseShapes = {
1430
1430
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1437,8 +1437,8 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1437
1437
  const origin = graph.getNodeAttributes(errorNodeId);
1438
1438
  const shape = rootCauseShapes[origin.type];
1439
1439
  if (shape) {
1440
- const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1441
- const match = shape(graph, origin, walk10);
1440
+ const walk11 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1441
+ const match = shape(graph, origin, walk11);
1442
1442
  if (match) {
1443
1443
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1444
1444
  return {
@@ -1446,9 +1446,9 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1446
1446
  result: import_types.RootCauseResultSchema.parse({
1447
1447
  rootCauseNode: match.rootCauseNode,
1448
1448
  rootCauseReason: reason,
1449
- traversalPath: walk10.path,
1450
- edgeProvenances: walk10.edges.map((e) => e.provenance),
1451
- confidence: confidenceFromMix(walk10.edges),
1449
+ traversalPath: walk11.path,
1450
+ edgeProvenances: walk11.edges.map((e) => e.provenance),
1451
+ confidence: confidenceFromMix(walk11.edges),
1452
1452
  fixRecommendation: match.fixRecommendation
1453
1453
  })
1454
1454
  };
@@ -1512,9 +1512,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
1512
1512
  function isFailingCallEdge(e) {
1513
1513
  return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
1514
1514
  }
1515
- function callSourcesForService(graph, serviceId16) {
1516
- const ids = [serviceId16];
1517
- for (const edgeId of graph.outboundEdges(serviceId16)) {
1515
+ function callSourcesForService(graph, serviceId17) {
1516
+ const ids = [serviceId17];
1517
+ for (const edgeId of graph.outboundEdges(serviceId17)) {
1518
1518
  const e = graph.getEdgeAttributes(edgeId);
1519
1519
  if (e.type !== import_types.EdgeType.CONTAINS) continue;
1520
1520
  const tgt = graph.getNodeAttributes(e.target);
@@ -1538,9 +1538,9 @@ function failingCallDominates(e, id, curEdge, curId) {
1538
1538
  }
1539
1539
  return id < curId;
1540
1540
  }
1541
- function dominantFailingCall(graph, serviceId16, visited) {
1541
+ function dominantFailingCall(graph, serviceId17, visited) {
1542
1542
  let best = null;
1543
- for (const src of callSourcesForService(graph, serviceId16)) {
1543
+ for (const src of callSourcesForService(graph, serviceId17)) {
1544
1544
  for (const edgeId of graph.outboundEdges(src)) {
1545
1545
  const e = graph.getEdgeAttributes(edgeId);
1546
1546
  if (!isFailingCallEdge(e)) continue;
@@ -1555,20 +1555,20 @@ function dominantFailingCall(graph, serviceId16, visited) {
1555
1555
  return best;
1556
1556
  }
1557
1557
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1558
- const path78 = [originServiceId];
1558
+ const path80 = [originServiceId];
1559
1559
  const edges = [];
1560
1560
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1561
1561
  let current = originServiceId;
1562
1562
  for (let depth = 0; depth < maxDepth; depth++) {
1563
1563
  const hop = dominantFailingCall(graph, current, visited);
1564
1564
  if (!hop) break;
1565
- path78.push(hop.nextService);
1565
+ path80.push(hop.nextService);
1566
1566
  edges.push(hop.edge);
1567
1567
  visited.add(hop.nextService);
1568
1568
  current = hop.nextService;
1569
1569
  }
1570
1570
  if (edges.length === 0) return null;
1571
- return { path: path78, edges, culprit: current };
1571
+ return { path: path80, edges, culprit: current };
1572
1572
  }
1573
1573
  function isStaleCallEdge(e) {
1574
1574
  return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
@@ -1579,9 +1579,9 @@ function staleCallDominates(e, id, curEdge, curId) {
1579
1579
  if (ev !== cv) return ev > cv;
1580
1580
  return id < curId;
1581
1581
  }
1582
- function dominantStaleCall(graph, serviceId16, visited) {
1582
+ function dominantStaleCall(graph, serviceId17, visited) {
1583
1583
  const bestByCallee = /* @__PURE__ */ new Map();
1584
- for (const src of callSourcesForService(graph, serviceId16)) {
1584
+ for (const src of callSourcesForService(graph, serviceId17)) {
1585
1585
  for (const edgeId of graph.outboundEdges(src)) {
1586
1586
  const e = graph.getEdgeAttributes(edgeId);
1587
1587
  if (e.type !== import_types.EdgeType.CALLS) continue;
@@ -1604,26 +1604,26 @@ function dominantStaleCall(graph, serviceId16, visited) {
1604
1604
  return best;
1605
1605
  }
1606
1606
  function followStaleCallChain(graph, originServiceId, maxDepth) {
1607
- const path78 = [originServiceId];
1607
+ const path80 = [originServiceId];
1608
1608
  const edges = [];
1609
1609
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1610
1610
  let current = originServiceId;
1611
1611
  for (let depth = 0; depth < maxDepth; depth++) {
1612
1612
  const hop = dominantStaleCall(graph, current, visited);
1613
1613
  if (!hop) break;
1614
- path78.push(hop.nextService);
1614
+ path80.push(hop.nextService);
1615
1615
  edges.push(hop.edge);
1616
1616
  visited.add(hop.nextService);
1617
1617
  current = hop.nextService;
1618
1618
  }
1619
1619
  if (edges.length === 0) return null;
1620
- return { path: path78, edges, culprit: current };
1620
+ return { path: path80, edges, culprit: current };
1621
1621
  }
1622
1622
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1623
1623
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1624
1624
  if (!chain) return null;
1625
1625
  const culprit = chain.culprit;
1626
- const path78 = [...chain.path];
1626
+ const path80 = [...chain.path];
1627
1627
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1628
1628
  const baseConfidence = confidenceFromMix(chain.edges);
1629
1629
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1631,14 +1631,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1631
1631
  if (loc) {
1632
1632
  let rootCauseNode = culprit;
1633
1633
  if (loc.fileNode) {
1634
- path78.push(loc.fileNode);
1634
+ path80.push(loc.fileNode);
1635
1635
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1636
1636
  rootCauseNode = loc.fileNode;
1637
1637
  }
1638
1638
  return import_types.RootCauseResultSchema.parse({
1639
1639
  rootCauseNode,
1640
1640
  rootCauseReason: loc.rootCauseReason,
1641
- traversalPath: path78,
1641
+ traversalPath: path80,
1642
1642
  edgeProvenances,
1643
1643
  confidence,
1644
1644
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1650,7 +1650,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1650
1650
  return import_types.RootCauseResultSchema.parse({
1651
1651
  rootCauseNode: culprit,
1652
1652
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1653
- traversalPath: path78,
1653
+ traversalPath: path80,
1654
1654
  edgeProvenances,
1655
1655
  confidence,
1656
1656
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2263,10 +2263,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2263
2263
  traversalPath = staleChain.path;
2264
2264
  edgeProvenances = staleChain.edges.map((e) => e.provenance);
2265
2265
  } else if (top.node !== seedNode) {
2266
- const path78 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2267
- if (path78) {
2268
- traversalPath = path78.nodes;
2269
- edgeProvenances = path78.edges.map((e) => e.provenance);
2266
+ const path80 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2267
+ if (path80) {
2268
+ traversalPath = path80.nodes;
2269
+ edgeProvenances = path80.edges.map((e) => e.provenance);
2270
2270
  } else {
2271
2271
  traversalPath = [errorNodeId, top.node];
2272
2272
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -3090,7 +3090,7 @@ function buildServiceHostIndex(services) {
3090
3090
  async function walkSourceFiles(dir, excludeDirs = []) {
3091
3091
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
3092
3092
  const out = [];
3093
- async function walk10(current) {
3093
+ async function walk11(current) {
3094
3094
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3095
3095
  for (const entry2 of entries) {
3096
3096
  const full = import_node_path5.default.join(current, entry2.name);
@@ -3098,7 +3098,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
3098
3098
  if (IGNORED_DIRS.has(entry2.name)) continue;
3099
3099
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
3100
3100
  if (await isPythonVenvDir(full)) continue;
3101
- await walk10(full);
3101
+ await walk11(full);
3102
3102
  } 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
3103
3103
  // would attribute our instrumentation imports to the user's service.
3104
3104
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -3106,7 +3106,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
3106
3106
  }
3107
3107
  }
3108
3108
  }
3109
- await walk10(dir);
3109
+ await walk11(dir);
3110
3110
  return out;
3111
3111
  }
3112
3112
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3702,8 +3702,8 @@ function chiRoutesFromSource(source, parser) {
3702
3702
  chiWalk(tree.rootNode, "", out);
3703
3703
  return out;
3704
3704
  }
3705
- function stripChiRegex(path78) {
3706
- return path78.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3705
+ function stripChiRegex(path80) {
3706
+ return path80.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3707
3707
  }
3708
3708
  function chiWalk(node, prefix, out) {
3709
3709
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4375,9 +4375,9 @@ function rubyRocketRoute(args) {
4375
4375
  if (!pair || pair.type !== "pair") continue;
4376
4376
  const k = pair.childForFieldName("key");
4377
4377
  if (k?.type !== "string") continue;
4378
- const path78 = rubyLiteral(k);
4379
- if (path78 === null) continue;
4380
- return { path: path78, target: rubyLiteral(pair.childForFieldName("value")) };
4378
+ const path80 = rubyLiteral(k);
4379
+ if (path80 === null) continue;
4380
+ return { path: path80, target: rubyLiteral(pair.childForFieldName("value")) };
4381
4381
  }
4382
4382
  return null;
4383
4383
  }
@@ -5988,8 +5988,8 @@ function pickContainingSymbol(candidates, fn) {
5988
5988
  return b.symbol.span.startLine - a.symbol.span.startLine;
5989
5989
  };
5990
5990
  if (fn) {
5991
- const named = candidates.filter((c) => terminalName(c.symbol.qualname) === fn);
5992
- if (named.length > 0) return [...named].sort(bySpan)[0].id;
5991
+ const named2 = candidates.filter((c) => terminalName(c.symbol.qualname) === fn);
5992
+ if (named2.length > 0) return [...named2].sort(bySpan)[0].id;
5993
5993
  }
5994
5994
  return [...candidates].sort(bySpan)[0].id;
5995
5995
  }
@@ -6932,29 +6932,29 @@ function promoteFrontierNodes(graph, opts = {}) {
6932
6932
  toPromote.push({ frontierId: id, serviceId: target });
6933
6933
  });
6934
6934
  let promoted = 0;
6935
- for (const { frontierId: frontierId2, serviceId: serviceId16 } of toPromote) {
6935
+ for (const { frontierId: frontierId2, serviceId: serviceId17 } of toPromote) {
6936
6936
  if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
6937
6937
  const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
6938
6938
  if (!gate.allowed) {
6939
6939
  continue;
6940
6940
  }
6941
6941
  }
6942
- rewireFrontierEdges(graph, frontierId2, serviceId16);
6942
+ rewireFrontierEdges(graph, frontierId2, serviceId17);
6943
6943
  graph.dropNode(frontierId2);
6944
6944
  promoted++;
6945
6945
  }
6946
6946
  return promoted;
6947
6947
  }
6948
- function rewireFrontierEdges(graph, frontierId2, serviceId16) {
6948
+ function rewireFrontierEdges(graph, frontierId2, serviceId17) {
6949
6949
  const inbound = [...graph.inboundEdges(frontierId2)];
6950
6950
  const outbound = [...graph.outboundEdges(frontierId2)];
6951
6951
  for (const edgeId of inbound) {
6952
6952
  const edge = graph.getEdgeAttributes(edgeId);
6953
- rebuildEdge(graph, edge, edge.source, serviceId16, edgeId);
6953
+ rebuildEdge(graph, edge, edge.source, serviceId17, edgeId);
6954
6954
  }
6955
6955
  for (const edgeId of outbound) {
6956
6956
  const edge = graph.getEdgeAttributes(edgeId);
6957
- rebuildEdge(graph, edge, serviceId16, edge.target, edgeId);
6957
+ rebuildEdge(graph, edge, serviceId17, edge.target, edgeId);
6958
6958
  }
6959
6959
  }
6960
6960
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
@@ -8087,9 +8087,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
8087
8087
  "StatefulSet",
8088
8088
  "DaemonSet"
8089
8089
  ]);
8090
- function addAliases(graph, serviceId16, candidates) {
8091
- if (!graph.hasNode(serviceId16)) return;
8092
- const node = graph.getNodeAttributes(serviceId16);
8090
+ function addAliases(graph, serviceId17, candidates) {
8091
+ if (!graph.hasNode(serviceId17)) return;
8092
+ const node = graph.getNodeAttributes(serviceId17);
8093
8093
  if (node.type !== import_types19.NodeType.ServiceNode) return;
8094
8094
  const set = new Set(node.aliases ?? []);
8095
8095
  for (const c of candidates) {
@@ -8099,7 +8099,7 @@ function addAliases(graph, serviceId16, candidates) {
8099
8099
  }
8100
8100
  if (set.size === 0) return;
8101
8101
  const updated = { ...node, aliases: [...set].sort() };
8102
- graph.replaceNodeAttributes(serviceId16, updated);
8102
+ graph.replaceNodeAttributes(serviceId17, updated);
8103
8103
  }
8104
8104
  function indexServicesByName(services) {
8105
8105
  const map = /* @__PURE__ */ new Map();
@@ -8132,12 +8132,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
8132
8132
  }
8133
8133
  if (!compose?.services) return;
8134
8134
  for (const [composeName, svc] of Object.entries(compose.services)) {
8135
- const serviceId16 = serviceIndex.get(composeName);
8136
- if (!serviceId16) continue;
8135
+ const serviceId17 = serviceIndex.get(composeName);
8136
+ if (!serviceId17) continue;
8137
8137
  const aliases = /* @__PURE__ */ new Set([composeName]);
8138
8138
  if (svc.container_name) aliases.add(svc.container_name);
8139
8139
  if (svc.hostname) aliases.add(svc.hostname);
8140
- addAliases(graph, serviceId16, aliases);
8140
+ addAliases(graph, serviceId17, aliases);
8141
8141
  }
8142
8142
  }
8143
8143
  var LABEL_KEYS = /* @__PURE__ */ new Set([
@@ -9248,7 +9248,7 @@ async function addSymbolEdges(graph, services) {
9248
9248
  return best;
9249
9249
  };
9250
9250
  const requests = [];
9251
- const walk10 = (node) => {
9251
+ const walk11 = (node) => {
9252
9252
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
9253
9253
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
9254
9254
  if (self && self.kind === "class") {
@@ -9294,10 +9294,10 @@ async function addSymbolEdges(graph, services) {
9294
9294
  }
9295
9295
  for (let i = 0; i < node.namedChildCount; i++) {
9296
9296
  const child = node.namedChild(i);
9297
- if (child) walk10(child);
9297
+ if (child) walk11(child);
9298
9298
  }
9299
9299
  };
9300
- walk10(root);
9300
+ walk11(root);
9301
9301
  for (const req2 of requests) {
9302
9302
  const targetSid = resolveTarget(req2.targetName, req2.wantKind);
9303
9303
  if (!targetSid) continue;
@@ -9405,10 +9405,10 @@ function collectNamedImports(root) {
9405
9405
  const clause = node.namedChild(i);
9406
9406
  if (clause?.type !== "import_clause") continue;
9407
9407
  for (let j = 0; j < clause.namedChildCount; j++) {
9408
- const named = clause.namedChild(j);
9409
- if (named?.type !== "named_imports") continue;
9410
- for (let k = 0; k < named.namedChildCount; k++) {
9411
- const spec = named.namedChild(k);
9408
+ const named2 = clause.namedChild(j);
9409
+ if (named2?.type !== "named_imports") continue;
9410
+ for (let k = 0; k < named2.namedChildCount; k++) {
9411
+ const spec = named2.namedChild(k);
9412
9412
  if (spec?.type !== "import_specifier") continue;
9413
9413
  let isType = false;
9414
9414
  for (let t = 0; t < spec.childCount; t++) {
@@ -10469,7 +10469,7 @@ var import_types24 = require("@neat.is/types");
10469
10469
  async function walkConfigFiles(dir, excludeDirs = []) {
10470
10470
  const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10471
10471
  const out = [];
10472
- async function walk10(current) {
10472
+ async function walk11(current) {
10473
10473
  const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
10474
10474
  for (const entry2 of entries) {
10475
10475
  const full = import_node_path35.default.join(current, entry2.name);
@@ -10477,13 +10477,13 @@ async function walkConfigFiles(dir, excludeDirs = []) {
10477
10477
  if (IGNORED_DIRS.has(entry2.name)) continue;
10478
10478
  if (excluded.has(import_node_path35.default.resolve(full))) continue;
10479
10479
  if (await isPythonVenvDir(full)) continue;
10480
- await walk10(full);
10480
+ await walk11(full);
10481
10481
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
10482
10482
  out.push(full);
10483
10483
  }
10484
10484
  }
10485
10485
  }
10486
- await walk10(dir);
10486
+ await walk11(dir);
10487
10487
  return out;
10488
10488
  }
10489
10489
  async function addConfigNodes(graph, services, scanPath) {
@@ -10574,7 +10574,7 @@ function grpcMethodsFromProto(content, fqPackage) {
10574
10574
  async function walkProtoFiles(dir, excludeDirs = []) {
10575
10575
  const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10576
10576
  const out = [];
10577
- async function walk10(current) {
10577
+ async function walk11(current) {
10578
10578
  const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10579
10579
  for (const entry2 of entries) {
10580
10580
  const full = import_node_path36.default.join(current, entry2.name);
@@ -10582,13 +10582,13 @@ async function walkProtoFiles(dir, excludeDirs = []) {
10582
10582
  if (IGNORED_DIRS.has(entry2.name)) continue;
10583
10583
  if (excluded.has(import_node_path36.default.resolve(full))) continue;
10584
10584
  if (await isPythonVenvDir(full)) continue;
10585
- await walk10(full);
10585
+ await walk11(full);
10586
10586
  } else if (entry2.isFile() && import_node_path36.default.extname(entry2.name) === PROTO_EXTENSION) {
10587
10587
  out.push(full);
10588
10588
  }
10589
10589
  }
10590
10590
  }
10591
- await walk10(dir);
10591
+ await walk11(dir);
10592
10592
  return out;
10593
10593
  }
10594
10594
  async function addGrpcMethods(graph, services) {
@@ -10656,7 +10656,7 @@ async function addGrpcMethods(graph, services) {
10656
10656
 
10657
10657
  // src/extract/calls/index.ts
10658
10658
  init_cjs_shims();
10659
- var import_types44 = require("@neat.is/types");
10659
+ var import_types45 = require("@neat.is/types");
10660
10660
 
10661
10661
  // src/extract/calls/http.ts
10662
10662
  init_cjs_shims();
@@ -11591,7 +11591,7 @@ function isFirestoreClientFactory(node) {
11591
11591
  }
11592
11592
  function firestoreClientVars(root) {
11593
11593
  const vars = /* @__PURE__ */ new Set();
11594
- const walk10 = (node) => {
11594
+ const walk11 = (node) => {
11595
11595
  if (node.type === "variable_declarator") {
11596
11596
  const name = node.childForFieldName("name");
11597
11597
  let value = node.childForFieldName("value");
@@ -11600,9 +11600,9 @@ function firestoreClientVars(root) {
11600
11600
  vars.add(name.text);
11601
11601
  }
11602
11602
  }
11603
- for (const c of namedChildren(node)) walk10(c);
11603
+ for (const c of namedChildren(node)) walk11(c);
11604
11604
  };
11605
- walk10(root);
11605
+ walk11(root);
11606
11606
  return vars;
11607
11607
  }
11608
11608
  function isClientExpr(node, clientVars) {
@@ -11757,7 +11757,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11757
11757
  }
11758
11758
  s.add(field);
11759
11759
  };
11760
- const walk10 = (node) => {
11760
+ const walk11 = (node) => {
11761
11761
  if (node.type === "call_expression") {
11762
11762
  const fn = node.childForFieldName("function");
11763
11763
  const line = node.startPosition.row + 1;
@@ -11797,9 +11797,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11797
11797
  }
11798
11798
  }
11799
11799
  }
11800
- for (const c of namedChildren(node)) walk10(c);
11800
+ for (const c of namedChildren(node)) walk11(c);
11801
11801
  };
11802
- walk10(tree.rootNode);
11802
+ walk11(tree.rootNode);
11803
11803
  const out = [];
11804
11804
  for (const [collPath, line] of collLine) {
11805
11805
  const byField = writes.get(collPath);
@@ -12042,8 +12042,8 @@ function parseImportBindings(content) {
12042
12042
  out.push({ local: ns[1], kind: "namespace", specifier: spec });
12043
12043
  continue;
12044
12044
  }
12045
- const named = /\{([^}]*)\}/.exec(clause);
12046
- if (named) parseDestructure(named[1], spec, out);
12045
+ const named2 = /\{([^}]*)\}/.exec(clause);
12046
+ if (named2) parseDestructure(named2[1], spec, out);
12047
12047
  const def = /^(\w+)\s*(?:,|$)/.exec(clause);
12048
12048
  if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
12049
12049
  }
@@ -12063,8 +12063,8 @@ function fileExportsOf(content, pluralizeOn) {
12063
12063
  for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
12064
12064
  const byName = new Map(byVar);
12065
12065
  let def;
12066
- const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
12067
- if (named && byVar.has(named[1])) def = byVar.get(named[1]);
12066
+ const named2 = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
12067
+ if (named2 && byVar.has(named2[1])) def = byVar.get(named2[1]);
12068
12068
  if (!def) {
12069
12069
  const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
12070
12070
  if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
@@ -12415,13 +12415,13 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12415
12415
  return out;
12416
12416
  }
12417
12417
 
12418
- // src/extract/calls/django-orm.ts
12418
+ // src/extract/calls/socket.ts
12419
12419
  init_cjs_shims();
12420
12420
  var import_node_path47 = __toESM(require("path"), 1);
12421
12421
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
12422
12422
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
12423
12423
  var import_types36 = require("@neat.is/types");
12424
- var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
12424
+ var SOCKET_IMPORT_RE = /(?:^|\n)\s*(?:import\s+socket\b|from\s+socket\s+import\b)/;
12425
12425
  var PARSE_CHUNK7 = 16384;
12426
12426
  function makePyParser4() {
12427
12427
  const p = new import_tree_sitter10.default();
@@ -12441,22 +12441,109 @@ function namedChildren3(node) {
12441
12441
  }
12442
12442
  return out;
12443
12443
  }
12444
+ function walk4(node, visit) {
12445
+ visit(node);
12446
+ for (const c of namedChildren3(node)) walk4(c, visit);
12447
+ }
12444
12448
  function pyStaticStringText3(node) {
12445
12449
  if (node.type !== "string") return null;
12446
- for (const c of namedChildren3(node)) {
12450
+ for (const child of namedChildren3(node)) {
12451
+ if (child.type === "interpolation") return null;
12452
+ if (child.type === "string_content") return child.text;
12453
+ }
12454
+ return "";
12455
+ }
12456
+ function hasSocketConstructor(root) {
12457
+ let found = false;
12458
+ walk4(root, (n) => {
12459
+ if (found || n.type !== "call") return;
12460
+ const t = n.childForFieldName("function")?.text;
12461
+ if (t === "socket.socket" || t === "socket") found = true;
12462
+ });
12463
+ return found;
12464
+ }
12465
+ function socketEndpointsFromFile(file, serviceDir) {
12466
+ if (!SOCKET_IMPORT_RE.test(file.content)) return [];
12467
+ const tree = parseSource7(makePyParser4(), file.content);
12468
+ if (!hasSocketConstructor(tree.rootNode)) return [];
12469
+ const out = [];
12470
+ const seen = /* @__PURE__ */ new Set();
12471
+ walk4(tree.rootNode, (node) => {
12472
+ if (node.type !== "call") return;
12473
+ const fn = node.childForFieldName("function");
12474
+ if (fn?.type !== "attribute" || fn.childForFieldName("attribute")?.text !== "connect") return;
12475
+ const args = node.childForFieldName("arguments");
12476
+ const first = args ? namedChildren3(args).find((a) => a.type !== "keyword_argument") : void 0;
12477
+ if (!first || first.type !== "tuple") return;
12478
+ const elems = namedChildren3(first);
12479
+ if (elems.length < 2) return;
12480
+ const hostNode = elems[0];
12481
+ const host = hostNode.type === "string" ? pyStaticStringText3(hostNode) : null;
12482
+ const portNode = elems[1];
12483
+ const port = portNode.type === "integer" ? portNode.text : null;
12484
+ const name = host ? port ? `${host}:${port}` : host : "env";
12485
+ if (seen.has(name)) return;
12486
+ seen.add(name);
12487
+ const line = node.startPosition.row + 1;
12488
+ out.push({
12489
+ infraId: (0, import_types36.infraId)("socket", name),
12490
+ name,
12491
+ kind: "socket",
12492
+ edgeType: "CALLS",
12493
+ // A verified `socket.socket(...)` + `.connect((host, port))` call site.
12494
+ confidenceKind: "verified-call-site",
12495
+ evidence: {
12496
+ file: import_node_path47.default.relative(serviceDir, file.path),
12497
+ line,
12498
+ snippet: snippet(file.content, line)
12499
+ }
12500
+ });
12501
+ });
12502
+ return out;
12503
+ }
12504
+
12505
+ // src/extract/calls/django-orm.ts
12506
+ init_cjs_shims();
12507
+ var import_node_path48 = __toESM(require("path"), 1);
12508
+ var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12509
+ var import_tree_sitter_python7 = __toESM(require("tree-sitter-python"), 1);
12510
+ var import_types37 = require("@neat.is/types");
12511
+ var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
12512
+ var PARSE_CHUNK8 = 16384;
12513
+ function makePyParser5() {
12514
+ const p = new import_tree_sitter11.default();
12515
+ p.setLanguage(import_tree_sitter_python7.default);
12516
+ return p;
12517
+ }
12518
+ function parseSource8(parser, source) {
12519
+ return parser.parse(
12520
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK8)
12521
+ );
12522
+ }
12523
+ function namedChildren4(node) {
12524
+ const out = [];
12525
+ for (let i = 0; i < node.namedChildCount; i++) {
12526
+ const c = node.namedChild(i);
12527
+ if (c) out.push(c);
12528
+ }
12529
+ return out;
12530
+ }
12531
+ function pyStaticStringText4(node) {
12532
+ if (node.type !== "string") return null;
12533
+ for (const c of namedChildren4(node)) {
12447
12534
  if (c.type === "interpolation") return null;
12448
12535
  if (c.type === "string_content") return c.text;
12449
12536
  }
12450
12537
  return "";
12451
12538
  }
12452
- function walk4(node, visit) {
12539
+ function walk5(node, visit) {
12453
12540
  visit(node);
12454
- for (const c of namedChildren3(node)) walk4(c, visit);
12541
+ for (const c of namedChildren4(node)) walk5(c, visit);
12455
12542
  }
12456
12543
  function extendsDjangoModel(cls) {
12457
12544
  const supers = cls.childForFieldName("superclasses");
12458
12545
  if (!supers) return false;
12459
- for (const a of namedChildren3(supers)) {
12546
+ for (const a of namedChildren4(supers)) {
12460
12547
  const t = a.text;
12461
12548
  if (t === "models.Model" || t === "Model" || t.endsWith(".Model")) return true;
12462
12549
  }
@@ -12464,31 +12551,31 @@ function extendsDjangoModel(cls) {
12464
12551
  }
12465
12552
  function readMeta(body) {
12466
12553
  const out = {};
12467
- for (const stmt of namedChildren3(body)) {
12554
+ for (const stmt of namedChildren4(body)) {
12468
12555
  if (stmt.type !== "class_definition") continue;
12469
12556
  if (stmt.childForFieldName("name")?.text !== "Meta") continue;
12470
12557
  const metaBody = stmt.childForFieldName("body");
12471
12558
  if (!metaBody) continue;
12472
- for (const ms of namedChildren3(metaBody)) {
12559
+ for (const ms of namedChildren4(metaBody)) {
12473
12560
  if (ms.type !== "expression_statement") continue;
12474
12561
  const a = ms.namedChild(0);
12475
12562
  if (a?.type !== "assignment") continue;
12476
12563
  const l = a.childForFieldName("left")?.text;
12477
12564
  const r = a.childForFieldName("right");
12478
12565
  if (r?.type !== "string") continue;
12479
- if (l === "db_table") out.dbTable = pyStaticStringText3(r) ?? void 0;
12480
- if (l === "app_label") out.appLabel = pyStaticStringText3(r) ?? void 0;
12566
+ if (l === "db_table") out.dbTable = pyStaticStringText4(r) ?? void 0;
12567
+ if (l === "app_label") out.appLabel = pyStaticStringText4(r) ?? void 0;
12481
12568
  }
12482
12569
  }
12483
12570
  return out;
12484
12571
  }
12485
12572
  function djangoOrmEndpointsFromFile(file, serviceDir) {
12486
12573
  if (!DJANGO_IMPORT_RE.test(file.content)) return [];
12487
- const tree = parseSource7(makePyParser4(), file.content);
12574
+ const tree = parseSource8(makePyParser5(), file.content);
12488
12575
  const out = [];
12489
12576
  const seen = /* @__PURE__ */ new Set();
12490
- const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
12491
- walk4(tree.rootNode, (node) => {
12577
+ const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
12578
+ walk5(tree.rootNode, (node) => {
12492
12579
  if (node.type !== "class_definition") return;
12493
12580
  if (!extendsDjangoModel(node)) return;
12494
12581
  const nameNode = node.childForFieldName("name");
@@ -12500,12 +12587,12 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12500
12587
  seen.add(table);
12501
12588
  const line = node.startPosition.row + 1;
12502
12589
  out.push({
12503
- infraId: (0, import_types36.infraId)("sql-table", table),
12590
+ infraId: (0, import_types37.infraId)("sql-table", table),
12504
12591
  name: table,
12505
12592
  kind: "sql-table",
12506
12593
  edgeType: "CALLS",
12507
12594
  confidenceKind: "verified-call-site",
12508
- evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12595
+ evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12509
12596
  });
12510
12597
  });
12511
12598
  return out;
@@ -12513,18 +12600,18 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12513
12600
 
12514
12601
  // src/extract/calls/drizzle.ts
12515
12602
  init_cjs_shims();
12516
- var import_node_path48 = __toESM(require("path"), 1);
12517
- var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12603
+ var import_node_path49 = __toESM(require("path"), 1);
12604
+ var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12518
12605
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12519
- var import_types37 = require("@neat.is/types");
12606
+ var import_types38 = require("@neat.is/types");
12520
12607
  var DRIZZLE_IMPORT_RE = /drizzle-orm/;
12521
12608
  var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
12522
12609
  function parserForExt3(ext) {
12523
- const p = new import_tree_sitter11.default();
12610
+ const p = new import_tree_sitter12.default();
12524
12611
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
12525
12612
  return p;
12526
12613
  }
12527
- function namedChildren4(node) {
12614
+ function namedChildren5(node) {
12528
12615
  const out = [];
12529
12616
  for (let i = 0; i < node.namedChildCount; i++) {
12530
12617
  const c = node.namedChild(i);
@@ -12534,7 +12621,7 @@ function namedChildren4(node) {
12534
12621
  }
12535
12622
  function stringLiteralText4(node) {
12536
12623
  if (!node || node.type !== "string") return null;
12537
- for (const child of namedChildren4(node)) {
12624
+ for (const child of namedChildren5(node)) {
12538
12625
  if (child.type === "string_fragment") return child.text;
12539
12626
  }
12540
12627
  return "";
@@ -12542,7 +12629,7 @@ function stringLiteralText4(node) {
12542
12629
  function firstStringArg(call) {
12543
12630
  const args = call.childForFieldName("arguments");
12544
12631
  if (!args) return null;
12545
- for (const arg of namedChildren4(args)) {
12632
+ for (const arg of namedChildren5(args)) {
12546
12633
  if (arg.type === "string") return stringLiteralText4(arg);
12547
12634
  return null;
12548
12635
  }
@@ -12577,7 +12664,7 @@ function keyName2(key) {
12577
12664
  function columnsFromObject(obj) {
12578
12665
  const out = [];
12579
12666
  const seen = /* @__PURE__ */ new Set();
12580
- for (const child of namedChildren4(obj)) {
12667
+ for (const child of namedChildren5(obj)) {
12581
12668
  if (child.type !== "pair") continue;
12582
12669
  const value = child.childForFieldName("value");
12583
12670
  const key = keyName2(child.childForFieldName("key"));
@@ -12591,15 +12678,15 @@ function columnsFromObject(obj) {
12591
12678
  }
12592
12679
  function drizzleEndpointsFromFile(file, serviceDir) {
12593
12680
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12594
- const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
12681
+ const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12595
12682
  const out = [];
12596
12683
  const seen = /* @__PURE__ */ new Set();
12597
- const walk10 = (node) => {
12684
+ const walk11 = (node) => {
12598
12685
  if (node.type === "call_expression") {
12599
12686
  const fn = node.childForFieldName("function");
12600
12687
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
12601
12688
  const args = node.childForFieldName("arguments");
12602
- const argNodes = args ? namedChildren4(args) : [];
12689
+ const argNodes = args ? namedChildren5(args) : [];
12603
12690
  const tableName = stringLiteralText4(argNodes[0] ?? null);
12604
12691
  const obj = argNodes[1];
12605
12692
  if (tableName && obj?.type === "object" && !seen.has(tableName)) {
@@ -12607,14 +12694,14 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12607
12694
  const columns = columnsFromObject(obj);
12608
12695
  const line = node.startPosition.row + 1;
12609
12696
  out.push({
12610
- infraId: (0, import_types37.infraId)("sql-table", tableName),
12697
+ infraId: (0, import_types38.infraId)("sql-table", tableName),
12611
12698
  name: tableName,
12612
12699
  kind: "sql-table",
12613
12700
  edgeType: "CALLS",
12614
12701
  confidenceKind: "structural",
12615
12702
  columns,
12616
12703
  evidence: {
12617
- file: import_node_path48.default.relative(serviceDir, file.path),
12704
+ file: import_node_path49.default.relative(serviceDir, file.path),
12618
12705
  line,
12619
12706
  snippet: snippet(file.content, line)
12620
12707
  }
@@ -12622,9 +12709,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12622
12709
  }
12623
12710
  }
12624
12711
  }
12625
- for (const c of namedChildren4(node)) walk10(c);
12712
+ for (const c of namedChildren5(node)) walk11(c);
12626
12713
  };
12627
- walk10(tree.rootNode);
12714
+ walk11(tree.rootNode);
12628
12715
  return out;
12629
12716
  }
12630
12717
  function enclosingVarName(call) {
@@ -12646,12 +12733,12 @@ function enclosingVarName(call) {
12646
12733
  function collectDrizzleTables(root) {
12647
12734
  const tables = [];
12648
12735
  const varToTable = /* @__PURE__ */ new Map();
12649
- const walk10 = (node) => {
12736
+ const walk11 = (node) => {
12650
12737
  if (node.type === "call_expression") {
12651
12738
  const fn = node.childForFieldName("function");
12652
12739
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
12653
12740
  const args = node.childForFieldName("arguments");
12654
- const argNodes = args ? namedChildren4(args) : [];
12741
+ const argNodes = args ? namedChildren5(args) : [];
12655
12742
  const tableName = stringLiteralText4(argNodes[0] ?? null);
12656
12743
  const obj = argNodes[1]?.type === "object" ? argNodes[1] : null;
12657
12744
  if (tableName) {
@@ -12661,9 +12748,9 @@ function collectDrizzleTables(root) {
12661
12748
  }
12662
12749
  }
12663
12750
  }
12664
- for (const c of namedChildren4(node)) walk10(c);
12751
+ for (const c of namedChildren5(node)) walk11(c);
12665
12752
  };
12666
- walk10(root);
12753
+ walk11(root);
12667
12754
  return { tables, varToTable };
12668
12755
  }
12669
12756
  function referencesTargetVar(call) {
@@ -12671,7 +12758,7 @@ function referencesTargetVar(call) {
12671
12758
  if (fn?.type !== "member_expression") return null;
12672
12759
  if (fn.childForFieldName("property")?.text !== "references") return null;
12673
12760
  const args = call.childForFieldName("arguments");
12674
- const first = args ? namedChildren4(args)[0] ?? null : null;
12761
+ const first = args ? namedChildren5(args)[0] ?? null : null;
12675
12762
  if (first?.type !== "arrow_function") return null;
12676
12763
  const body = first.childForFieldName("body");
12677
12764
  if (body?.type !== "member_expression") return null;
@@ -12680,13 +12767,13 @@ function referencesTargetVar(call) {
12680
12767
  }
12681
12768
  function drizzleForeignKeys(file, serviceDir) {
12682
12769
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12683
- const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
12770
+ const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12684
12771
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
12685
12772
  const out = [];
12686
12773
  const seen = /* @__PURE__ */ new Set();
12687
12774
  for (const table of tables) {
12688
12775
  if (!table.object) continue;
12689
- const walk10 = (node) => {
12776
+ const walk11 = (node) => {
12690
12777
  if (node.type === "call_expression") {
12691
12778
  const targetVar = referencesTargetVar(node);
12692
12779
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -12699,7 +12786,7 @@ function drizzleForeignKeys(file, serviceDir) {
12699
12786
  childTable: table.tableName,
12700
12787
  parentTable,
12701
12788
  evidence: {
12702
- file: import_node_path48.default.relative(serviceDir, file.path),
12789
+ file: import_node_path49.default.relative(serviceDir, file.path),
12703
12790
  line,
12704
12791
  snippet: snippet(file.content, line)
12705
12792
  }
@@ -12707,17 +12794,17 @@ function drizzleForeignKeys(file, serviceDir) {
12707
12794
  }
12708
12795
  }
12709
12796
  }
12710
- for (const c of namedChildren4(node)) walk10(c);
12797
+ for (const c of namedChildren5(node)) walk11(c);
12711
12798
  };
12712
- walk10(table.object);
12799
+ walk11(table.object);
12713
12800
  }
12714
12801
  return out;
12715
12802
  }
12716
12803
 
12717
12804
  // src/extract/calls/prisma.ts
12718
12805
  init_cjs_shims();
12719
- var import_node_path49 = __toESM(require("path"), 1);
12720
- var import_types38 = require("@neat.is/types");
12806
+ var import_node_path50 = __toESM(require("path"), 1);
12807
+ var import_types39 = require("@neat.is/types");
12721
12808
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
12722
12809
  "Int",
12723
12810
  "String",
@@ -12765,14 +12852,14 @@ function prismaColumnsFromSchema(file, serviceDir) {
12765
12852
  if (seenTable.has(b.tableName)) return;
12766
12853
  seenTable.add(b.tableName);
12767
12854
  out.push({
12768
- infraId: (0, import_types38.infraId)("sql-table", b.tableName),
12855
+ infraId: (0, import_types39.infraId)("sql-table", b.tableName),
12769
12856
  name: b.tableName,
12770
12857
  kind: "sql-table",
12771
12858
  edgeType: "CALLS",
12772
12859
  confidenceKind: "structural",
12773
12860
  columns: b.columns,
12774
12861
  evidence: {
12775
- file: import_node_path49.default.relative(serviceDir, file.path),
12862
+ file: import_node_path50.default.relative(serviceDir, file.path),
12776
12863
  line: b.startLine,
12777
12864
  snippet: snippet(content, b.startLine)
12778
12865
  }
@@ -12833,7 +12920,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12833
12920
  }
12834
12921
  async function prismaColumnEndpoints(serviceDir) {
12835
12922
  const schemaPath = await findFirst(serviceDir, [
12836
- import_node_path49.default.join("prisma", "schema.prisma"),
12923
+ import_node_path50.default.join("prisma", "schema.prisma"),
12837
12924
  "schema.prisma"
12838
12925
  ]);
12839
12926
  if (!schemaPath) return [];
@@ -12908,7 +12995,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12908
12995
  childTable: current.table,
12909
12996
  parentTable,
12910
12997
  evidence: {
12911
- file: import_node_path49.default.relative(serviceDir, file.path),
12998
+ file: import_node_path50.default.relative(serviceDir, file.path),
12912
12999
  line: lineNo,
12913
13000
  snippet: snippet(content, lineNo)
12914
13001
  }
@@ -12923,7 +13010,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12923
13010
  }
12924
13011
  async function prismaForeignKeys(serviceDir) {
12925
13012
  const schemaPath = await findFirst(serviceDir, [
12926
- import_node_path49.default.join("prisma", "schema.prisma"),
13013
+ import_node_path50.default.join("prisma", "schema.prisma"),
12927
13014
  "schema.prisma"
12928
13015
  ]);
12929
13016
  if (!schemaPath) return [];
@@ -12934,27 +13021,27 @@ async function prismaForeignKeys(serviceDir) {
12934
13021
 
12935
13022
  // src/extract/calls/activerecord.ts
12936
13023
  init_cjs_shims();
12937
- var import_node_path50 = __toESM(require("path"), 1);
12938
- var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
13024
+ var import_node_path51 = __toESM(require("path"), 1);
13025
+ var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12939
13026
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
12940
- var import_types39 = require("@neat.is/types");
13027
+ var import_types40 = require("@neat.is/types");
12941
13028
  var AR_SCHEMA_RE = /ActiveRecord::Schema/;
12942
- var PARSE_CHUNK8 = 16384;
13029
+ var PARSE_CHUNK9 = 16384;
12943
13030
  function makeRubyParser2() {
12944
- const p = new import_tree_sitter12.default();
13031
+ const p = new import_tree_sitter13.default();
12945
13032
  p.setLanguage(import_tree_sitter_ruby3.default);
12946
13033
  return p;
12947
13034
  }
12948
- function parseSource8(parser, source) {
13035
+ function parseSource9(parser, source) {
12949
13036
  return parser.parse(
12950
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK8)
13037
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK9)
12951
13038
  );
12952
13039
  }
12953
- function walk5(node, visit) {
13040
+ function walk6(node, visit) {
12954
13041
  visit(node);
12955
13042
  for (let i = 0; i < node.namedChildCount; i++) {
12956
13043
  const c = node.namedChild(i);
12957
- if (c) walk5(c, visit);
13044
+ if (c) walk6(c, visit);
12958
13045
  }
12959
13046
  }
12960
13047
  function rubyLiteral2(node) {
@@ -13125,24 +13212,24 @@ function readCreateTable(call) {
13125
13212
  }
13126
13213
  function railsSchemaEndpointsFromFile(file, serviceDir) {
13127
13214
  if (!AR_SCHEMA_RE.test(file.content)) return [];
13128
- const tree = parseSource8(makeRubyParser2(), file.content);
13215
+ const tree = parseSource9(makeRubyParser2(), file.content);
13129
13216
  const out = [];
13130
13217
  const seen = /* @__PURE__ */ new Set();
13131
- walk5(tree.rootNode, (node) => {
13218
+ walk6(tree.rootNode, (node) => {
13132
13219
  if (node.type !== "call") return;
13133
13220
  if (blockCallMethod(node) !== "create_table") return;
13134
13221
  const table = readCreateTable(node);
13135
13222
  if (!table || seen.has(table.name)) return;
13136
13223
  seen.add(table.name);
13137
13224
  out.push({
13138
- infraId: (0, import_types39.infraId)("sql-table", table.name),
13225
+ infraId: (0, import_types40.infraId)("sql-table", table.name),
13139
13226
  name: table.name,
13140
13227
  kind: "sql-table",
13141
13228
  edgeType: "CALLS",
13142
13229
  confidenceKind: "structural",
13143
13230
  ...table.columns.length > 0 ? { columns: table.columns } : {},
13144
13231
  evidence: {
13145
- file: import_node_path50.default.relative(serviceDir, file.path),
13232
+ file: import_node_path51.default.relative(serviceDir, file.path),
13146
13233
  line: table.line,
13147
13234
  snippet: snippet(file.content, table.line)
13148
13235
  }
@@ -13152,7 +13239,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
13152
13239
  }
13153
13240
  function railsSchemaForeignKeys(file, serviceDir) {
13154
13241
  if (!AR_SCHEMA_RE.test(file.content)) return [];
13155
- const tree = parseSource8(makeRubyParser2(), file.content);
13242
+ const tree = parseSource9(makeRubyParser2(), file.content);
13156
13243
  const out = [];
13157
13244
  const seen = /* @__PURE__ */ new Set();
13158
13245
  const emit = (childTable, parentTable, line) => {
@@ -13164,13 +13251,13 @@ function railsSchemaForeignKeys(file, serviceDir) {
13164
13251
  childTable,
13165
13252
  parentTable,
13166
13253
  evidence: {
13167
- file: import_node_path50.default.relative(serviceDir, file.path),
13254
+ file: import_node_path51.default.relative(serviceDir, file.path),
13168
13255
  line,
13169
13256
  snippet: snippet(file.content, line)
13170
13257
  }
13171
13258
  });
13172
13259
  };
13173
- walk5(tree.rootNode, (node) => {
13260
+ walk6(tree.rootNode, (node) => {
13174
13261
  if (node.type !== "call") return;
13175
13262
  const method = blockCallMethod(node);
13176
13263
  if (method === "add_foreign_key") {
@@ -13231,23 +13318,23 @@ function modelTable(cls) {
13231
13318
  return tableize(nameNode.text);
13232
13319
  }
13233
13320
  function railsModelEndpointsFromFile(file, serviceDir) {
13234
- const tree = parseSource8(makeRubyParser2(), file.content);
13321
+ const tree = parseSource9(makeRubyParser2(), file.content);
13235
13322
  const out = [];
13236
13323
  const seen = /* @__PURE__ */ new Set();
13237
- walk5(tree.rootNode, (node) => {
13324
+ walk6(tree.rootNode, (node) => {
13238
13325
  if (node.type !== "class" || !isActiveRecordModel(node)) return;
13239
13326
  const table = modelTable(node);
13240
13327
  if (!table || seen.has(table)) return;
13241
13328
  seen.add(table);
13242
13329
  const line = node.startPosition.row + 1;
13243
13330
  out.push({
13244
- infraId: (0, import_types39.infraId)("sql-table", table),
13331
+ infraId: (0, import_types40.infraId)("sql-table", table),
13245
13332
  name: table,
13246
13333
  kind: "sql-table",
13247
13334
  edgeType: "CALLS",
13248
13335
  confidenceKind: "verified-call-site",
13249
13336
  evidence: {
13250
- file: import_node_path50.default.relative(serviceDir, file.path),
13337
+ file: import_node_path51.default.relative(serviceDir, file.path),
13251
13338
  line,
13252
13339
  snippet: snippet(file.content, line)
13253
13340
  }
@@ -13257,10 +13344,10 @@ function railsModelEndpointsFromFile(file, serviceDir) {
13257
13344
  }
13258
13345
  var AR_ASSOCIATIONS = /* @__PURE__ */ new Set(["belongs_to", "has_many", "has_one"]);
13259
13346
  function railsModelForeignKeys(file, serviceDir) {
13260
- const tree = parseSource8(makeRubyParser2(), file.content);
13347
+ const tree = parseSource9(makeRubyParser2(), file.content);
13261
13348
  const out = [];
13262
13349
  const seen = /* @__PURE__ */ new Set();
13263
- walk5(tree.rootNode, (node) => {
13350
+ walk6(tree.rootNode, (node) => {
13264
13351
  if (node.type !== "class" || !isActiveRecordModel(node)) return;
13265
13352
  const thisTable = modelTable(node);
13266
13353
  if (!thisTable) return;
@@ -13284,7 +13371,7 @@ function railsModelForeignKeys(file, serviceDir) {
13284
13371
  childTable,
13285
13372
  parentTable,
13286
13373
  evidence: {
13287
- file: import_node_path50.default.relative(serviceDir, file.path),
13374
+ file: import_node_path51.default.relative(serviceDir, file.path),
13288
13375
  line,
13289
13376
  snippet: snippet(file.content, line)
13290
13377
  }
@@ -13296,27 +13383,27 @@ function railsModelForeignKeys(file, serviceDir) {
13296
13383
 
13297
13384
  // src/extract/calls/eloquent.ts
13298
13385
  init_cjs_shims();
13299
- var import_node_path51 = __toESM(require("path"), 1);
13300
- var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
13386
+ var import_node_path52 = __toESM(require("path"), 1);
13387
+ var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13301
13388
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
13302
- var import_types40 = require("@neat.is/types");
13389
+ var import_types41 = require("@neat.is/types");
13303
13390
  var LARAVEL_SCHEMA_RE = /\bSchema::(create|createIfNotExists|table)\b/;
13304
- var PARSE_CHUNK9 = 16384;
13391
+ var PARSE_CHUNK10 = 16384;
13305
13392
  function makePhpParser2() {
13306
- const p = new import_tree_sitter13.default();
13393
+ const p = new import_tree_sitter14.default();
13307
13394
  p.setLanguage(import_tree_sitter_php3.default.php_only);
13308
13395
  return p;
13309
13396
  }
13310
- function parseSource9(parser, source) {
13397
+ function parseSource10(parser, source) {
13311
13398
  return parser.parse(
13312
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK9)
13399
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
13313
13400
  );
13314
13401
  }
13315
- function walk6(node, visit) {
13402
+ function walk7(node, visit) {
13316
13403
  visit(node);
13317
13404
  for (let i = 0; i < node.namedChildCount; i++) {
13318
13405
  const c = node.namedChild(i);
13319
- if (c) walk6(c, visit);
13406
+ if (c) walk7(c, visit);
13320
13407
  }
13321
13408
  }
13322
13409
  function phpStaticString2(node) {
@@ -13527,9 +13614,9 @@ function columnsFromCall(chain, blueprintVar) {
13527
13614
  }
13528
13615
  function laravelMigrationEndpointsFromFile(file, serviceDir) {
13529
13616
  if (!LARAVEL_SCHEMA_RE.test(file.content)) return [];
13530
- const tree = parseSource9(makePhpParser2(), file.content);
13617
+ const tree = parseSource10(makePhpParser2(), file.content);
13531
13618
  const out = [];
13532
- walk6(tree.rootNode, (node) => {
13619
+ walk7(tree.rootNode, (node) => {
13533
13620
  if (!isSchemaCall(node)) return;
13534
13621
  const bp = readBlueprint(node);
13535
13622
  if (!bp) return;
@@ -13546,14 +13633,14 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13546
13633
  }
13547
13634
  }
13548
13635
  out.push({
13549
- infraId: (0, import_types40.infraId)("sql-table", bp.table),
13636
+ infraId: (0, import_types41.infraId)("sql-table", bp.table),
13550
13637
  name: bp.table,
13551
13638
  kind: "sql-table",
13552
13639
  edgeType: "CALLS",
13553
13640
  confidenceKind: "structural",
13554
13641
  ...columns.length > 0 ? { columns } : {},
13555
13642
  evidence: {
13556
- file: import_node_path51.default.relative(serviceDir, file.path),
13643
+ file: import_node_path52.default.relative(serviceDir, file.path),
13557
13644
  line: bp.line,
13558
13645
  snippet: snippet(file.content, bp.line)
13559
13646
  }
@@ -13563,7 +13650,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13563
13650
  }
13564
13651
  function laravelMigrationForeignKeys(file, serviceDir) {
13565
13652
  if (!LARAVEL_SCHEMA_RE.test(file.content)) return [];
13566
- const tree = parseSource9(makePhpParser2(), file.content);
13653
+ const tree = parseSource10(makePhpParser2(), file.content);
13567
13654
  const out = [];
13568
13655
  const seen = /* @__PURE__ */ new Set();
13569
13656
  const emit = (childTable, parentTable, line) => {
@@ -13575,13 +13662,13 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13575
13662
  childTable,
13576
13663
  parentTable,
13577
13664
  evidence: {
13578
- file: import_node_path51.default.relative(serviceDir, file.path),
13665
+ file: import_node_path52.default.relative(serviceDir, file.path),
13579
13666
  line,
13580
13667
  snippet: snippet(file.content, line)
13581
13668
  }
13582
13669
  });
13583
13670
  };
13584
- walk6(tree.rootNode, (node) => {
13671
+ walk7(tree.rootNode, (node) => {
13585
13672
  if (!isSchemaCall(node)) return;
13586
13673
  const bp = readBlueprint(node);
13587
13674
  if (!bp || !bp.closureBody) return;
@@ -13679,23 +13766,23 @@ function walkWithinClass(cls, visit) {
13679
13766
  recur(body);
13680
13767
  }
13681
13768
  function laravelModelEndpointsFromFile(file, serviceDir) {
13682
- const tree = parseSource9(makePhpParser2(), file.content);
13769
+ const tree = parseSource10(makePhpParser2(), file.content);
13683
13770
  const out = [];
13684
13771
  const seen = /* @__PURE__ */ new Set();
13685
- walk6(tree.rootNode, (node) => {
13772
+ walk7(tree.rootNode, (node) => {
13686
13773
  if (node.type !== "class_declaration" || !isEloquentModel(node)) return;
13687
13774
  const table = modelTable2(node);
13688
13775
  if (!table || seen.has(table)) return;
13689
13776
  seen.add(table);
13690
13777
  const line = node.startPosition.row + 1;
13691
13778
  out.push({
13692
- infraId: (0, import_types40.infraId)("sql-table", table),
13779
+ infraId: (0, import_types41.infraId)("sql-table", table),
13693
13780
  name: table,
13694
13781
  kind: "sql-table",
13695
13782
  edgeType: "CALLS",
13696
13783
  confidenceKind: "verified-call-site",
13697
13784
  evidence: {
13698
- file: import_node_path51.default.relative(serviceDir, file.path),
13785
+ file: import_node_path52.default.relative(serviceDir, file.path),
13699
13786
  line,
13700
13787
  snippet: snippet(file.content, line)
13701
13788
  }
@@ -13705,10 +13792,10 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
13705
13792
  }
13706
13793
  var ELOQUENT_RELATIONS = /* @__PURE__ */ new Set(["belongsTo", "hasMany", "hasOne"]);
13707
13794
  function laravelModelForeignKeys(file, serviceDir) {
13708
- const tree = parseSource9(makePhpParser2(), file.content);
13795
+ const tree = parseSource10(makePhpParser2(), file.content);
13709
13796
  const out = [];
13710
13797
  const seen = /* @__PURE__ */ new Set();
13711
- walk6(tree.rootNode, (node) => {
13798
+ walk7(tree.rootNode, (node) => {
13712
13799
  if (node.type !== "class_declaration" || !isEloquentModel(node)) return;
13713
13800
  const thisTable = modelTable2(node);
13714
13801
  if (!thisTable) return;
@@ -13731,7 +13818,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13731
13818
  childTable,
13732
13819
  parentTable,
13733
13820
  evidence: {
13734
- file: import_node_path51.default.relative(serviceDir, file.path),
13821
+ file: import_node_path52.default.relative(serviceDir, file.path),
13735
13822
  line,
13736
13823
  snippet: snippet(file.content, line)
13737
13824
  }
@@ -13743,12 +13830,12 @@ function laravelModelForeignKeys(file, serviceDir) {
13743
13830
 
13744
13831
  // src/extract/calls/go.ts
13745
13832
  init_cjs_shims();
13746
- var import_node_path54 = __toESM(require("path"), 1);
13747
- var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13833
+ var import_node_path55 = __toESM(require("path"), 1);
13834
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13748
13835
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
13749
- var import_types41 = require("@neat.is/types");
13836
+ var import_types42 = require("@neat.is/types");
13750
13837
  init_otel();
13751
- var PARSE_CHUNK10 = 16384;
13838
+ var PARSE_CHUNK11 = 16384;
13752
13839
  var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
13753
13840
  "Query",
13754
13841
  "QueryContext",
@@ -13774,20 +13861,20 @@ var SQLX_METHODS = /* @__PURE__ */ new Set([
13774
13861
  var DATABASE_SQL_IMPORT = "database/sql";
13775
13862
  var SQLX_IMPORT = "github.com/jmoiron/sqlx";
13776
13863
  function makeGoParser3() {
13777
- const p = new import_tree_sitter14.default();
13864
+ const p = new import_tree_sitter15.default();
13778
13865
  p.setLanguage(import_tree_sitter_go4.default);
13779
13866
  return p;
13780
13867
  }
13781
- function parseSource10(parser, source) {
13868
+ function parseSource11(parser, source) {
13782
13869
  return parser.parse(
13783
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
13870
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
13784
13871
  );
13785
13872
  }
13786
- function walk7(node, visit) {
13873
+ function walk8(node, visit) {
13787
13874
  visit(node);
13788
13875
  for (let i = 0; i < node.namedChildCount; i++) {
13789
13876
  const child = node.namedChild(i);
13790
- if (child) walk7(child, visit);
13877
+ if (child) walk8(child, visit);
13791
13878
  }
13792
13879
  }
13793
13880
  function goStringLiteralValue(node) {
@@ -13800,7 +13887,7 @@ function goStringLiteralValue(node) {
13800
13887
  }
13801
13888
  function goImportsAny(root, names) {
13802
13889
  let found = false;
13803
- walk7(root, (node) => {
13890
+ walk8(root, (node) => {
13804
13891
  if (found || node.type !== "import_spec") return;
13805
13892
  for (let i = 0; i < node.namedChildCount; i++) {
13806
13893
  const value = goStringLiteralValue(node.namedChild(i));
@@ -13818,14 +13905,14 @@ function firstStringLiteralArg(argsNode) {
13818
13905
  return null;
13819
13906
  }
13820
13907
  function goSqlEndpointsFromFile(file, serviceDir) {
13821
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13908
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13822
13909
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
13823
- const tree = parseSource10(makeGoParser3(), file.content);
13910
+ const tree = parseSource11(makeGoParser3(), file.content);
13824
13911
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
13825
13912
  const importsSqlx = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([SQLX_IMPORT]));
13826
13913
  if (!importsDatabaseSql && !importsSqlx) return [];
13827
13914
  const out = [];
13828
- walk7(tree.rootNode, (node) => {
13915
+ walk8(tree.rootNode, (node) => {
13829
13916
  if (node.type !== "call_expression") return;
13830
13917
  const fn = node.childForFieldName("function");
13831
13918
  if (fn?.type !== "selector_expression") return;
@@ -13840,14 +13927,14 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13840
13927
  const columns = columnsFromSqlStatement(sql);
13841
13928
  const line = node.startPosition.row + 1;
13842
13929
  out.push({
13843
- infraId: (0, import_types41.infraId)("sql-table", table),
13930
+ infraId: (0, import_types42.infraId)("sql-table", table),
13844
13931
  name: table,
13845
13932
  kind: "sql-table",
13846
13933
  edgeType: "CALLS",
13847
13934
  confidenceKind: "verified-call-site",
13848
13935
  ...columns.length > 0 ? { columns } : {},
13849
13936
  evidence: {
13850
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13937
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13851
13938
  line,
13852
13939
  snippet: snippet(file.content, line)
13853
13940
  }
@@ -13858,27 +13945,27 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13858
13945
 
13859
13946
  // src/extract/calls/gorm.ts
13860
13947
  init_cjs_shims();
13861
- var import_node_path55 = __toESM(require("path"), 1);
13862
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13948
+ var import_node_path56 = __toESM(require("path"), 1);
13949
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13863
13950
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
13864
- var import_types42 = require("@neat.is/types");
13951
+ var import_types43 = require("@neat.is/types");
13865
13952
  var GORM_IMPORT_RE = /gorm\.io\/gorm/;
13866
- var PARSE_CHUNK11 = 16384;
13953
+ var PARSE_CHUNK12 = 16384;
13867
13954
  function makeGoParser4() {
13868
- const p = new import_tree_sitter15.default();
13955
+ const p = new import_tree_sitter16.default();
13869
13956
  p.setLanguage(import_tree_sitter_go5.default);
13870
13957
  return p;
13871
13958
  }
13872
- function parseSource11(parser, source) {
13959
+ function parseSource12(parser, source) {
13873
13960
  return parser.parse(
13874
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
13961
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
13875
13962
  );
13876
13963
  }
13877
- function walk8(node, visit) {
13964
+ function walk9(node, visit) {
13878
13965
  visit(node);
13879
13966
  for (let i = 0; i < node.namedChildCount; i++) {
13880
13967
  const c = node.namedChild(i);
13881
- if (c) walk8(c, visit);
13968
+ if (c) walk9(c, visit);
13882
13969
  }
13883
13970
  }
13884
13971
  var COMMON_INITIALISMS = [
@@ -14111,7 +14198,7 @@ function readField(fieldDecl) {
14111
14198
  }
14112
14199
  function collectStructs(tree) {
14113
14200
  const structs = /* @__PURE__ */ new Map();
14114
- walk8(tree.rootNode, (node) => {
14201
+ walk9(tree.rootNode, (node) => {
14115
14202
  if (node.type !== "type_spec") return;
14116
14203
  const nameNode = node.childForFieldName("name");
14117
14204
  const typeNode = node.childForFieldName("type");
@@ -14160,7 +14247,7 @@ function compositeStructName(arg) {
14160
14247
  }
14161
14248
  function collectCallModels(tree) {
14162
14249
  const models = /* @__PURE__ */ new Set();
14163
- walk8(tree.rootNode, (node) => {
14250
+ walk9(tree.rootNode, (node) => {
14164
14251
  if (node.type !== "call_expression") return;
14165
14252
  const fn = node.childForFieldName("function");
14166
14253
  if (fn?.type !== "selector_expression") return;
@@ -14180,7 +14267,7 @@ function collectCallModels(tree) {
14180
14267
  function collectTableNameOverrides(tree) {
14181
14268
  const overrides = /* @__PURE__ */ new Map();
14182
14269
  const declarers = /* @__PURE__ */ new Set();
14183
- walk8(tree.rootNode, (node) => {
14270
+ walk9(tree.rootNode, (node) => {
14184
14271
  if (node.type !== "method_declaration") return;
14185
14272
  if (node.childForFieldName("name")?.text !== "TableName") return;
14186
14273
  const receiver = node.childForFieldName("receiver");
@@ -14197,7 +14284,7 @@ function collectTableNameOverrides(tree) {
14197
14284
  const body = node.childForFieldName("body");
14198
14285
  if (!body) return;
14199
14286
  let literal = null;
14200
- walk8(body, (n) => {
14287
+ walk9(body, (n) => {
14201
14288
  if (literal !== null) return;
14202
14289
  if (n.type !== "return_statement") return;
14203
14290
  const exprList = n.namedChild(0);
@@ -14291,9 +14378,9 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
14291
14378
  seen.delete(struct.name);
14292
14379
  }
14293
14380
  function gormEndpointsFromFile(file, serviceDir) {
14294
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14381
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
14295
14382
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14296
- const tree = parseSource11(makeGoParser4(), file.content);
14383
+ const tree = parseSource12(makeGoParser4(), file.content);
14297
14384
  const { structs, models, tableFor } = analyze(tree);
14298
14385
  const out = [];
14299
14386
  const seenTables = /* @__PURE__ */ new Set();
@@ -14306,14 +14393,14 @@ function gormEndpointsFromFile(file, serviceDir) {
14306
14393
  const columns = [];
14307
14394
  collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
14308
14395
  out.push({
14309
- infraId: (0, import_types42.infraId)("sql-table", table),
14396
+ infraId: (0, import_types43.infraId)("sql-table", table),
14310
14397
  name: table,
14311
14398
  kind: "sql-table",
14312
14399
  edgeType: "CALLS",
14313
14400
  confidenceKind: "structural",
14314
14401
  ...columns.length > 0 ? { columns } : {},
14315
14402
  evidence: {
14316
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14403
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14317
14404
  line: struct.line,
14318
14405
  snippet: snippet(file.content, struct.line)
14319
14406
  }
@@ -14322,9 +14409,9 @@ function gormEndpointsFromFile(file, serviceDir) {
14322
14409
  return out;
14323
14410
  }
14324
14411
  function gormForeignKeys(file, serviceDir) {
14325
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14412
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
14326
14413
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14327
- const tree = parseSource11(makeGoParser4(), file.content);
14414
+ const tree = parseSource12(makeGoParser4(), file.content);
14328
14415
  const { structs, models, tableFor } = analyze(tree);
14329
14416
  const out = [];
14330
14417
  const seen = /* @__PURE__ */ new Set();
@@ -14337,7 +14424,7 @@ function gormForeignKeys(file, serviceDir) {
14337
14424
  childTable,
14338
14425
  parentTable,
14339
14426
  evidence: {
14340
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14427
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14341
14428
  line,
14342
14429
  snippet: snippet(file.content, line)
14343
14430
  }
@@ -14374,27 +14461,27 @@ function gormForeignKeys(file, serviceDir) {
14374
14461
 
14375
14462
  // src/extract/calls/efcore.ts
14376
14463
  init_cjs_shims();
14377
- var import_node_path56 = __toESM(require("path"), 1);
14378
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14464
+ var import_node_path57 = __toESM(require("path"), 1);
14465
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14379
14466
  var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14380
- var import_types43 = require("@neat.is/types");
14467
+ var import_types44 = require("@neat.is/types");
14381
14468
  var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
14382
- var PARSE_CHUNK12 = 16384;
14469
+ var PARSE_CHUNK13 = 16384;
14383
14470
  function makeCsParser() {
14384
- const p = new import_tree_sitter16.default();
14471
+ const p = new import_tree_sitter17.default();
14385
14472
  p.setLanguage(import_tree_sitter_c_sharp2.default);
14386
14473
  return p;
14387
14474
  }
14388
- function parseSource12(parser, source) {
14475
+ function parseSource13(parser, source) {
14389
14476
  return parser.parse(
14390
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
14477
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK13)
14391
14478
  );
14392
14479
  }
14393
- function walk9(node, visit) {
14480
+ function walk10(node, visit) {
14394
14481
  visit(node);
14395
14482
  for (let i = 0; i < node.namedChildCount; i++) {
14396
14483
  const c = node.namedChild(i);
14397
- if (c) walk9(c, visit);
14484
+ if (c) walk10(c, visit);
14398
14485
  }
14399
14486
  }
14400
14487
  function firstChildOfType(node, type) {
@@ -14453,28 +14540,28 @@ function tableFromToTable(call) {
14453
14540
  return value ? csStringLiteral(value) : null;
14454
14541
  }
14455
14542
  function efcoreEndpointsFromFile(file, serviceDir) {
14456
- if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14543
+ if (import_node_path57.default.extname(file.path) !== ".cs") return [];
14457
14544
  if (!EFCORE_GATE.test(file.content)) return [];
14458
- const tree = parseSource12(makeCsParser(), file.content);
14545
+ const tree = parseSource13(makeCsParser(), file.content);
14459
14546
  const out = [];
14460
14547
  const seen = /* @__PURE__ */ new Set();
14461
14548
  const push = (name, line) => {
14462
14549
  if (!name || seen.has(name)) return;
14463
14550
  seen.add(name);
14464
14551
  out.push({
14465
- infraId: (0, import_types43.infraId)("sql-table", name),
14552
+ infraId: (0, import_types44.infraId)("sql-table", name),
14466
14553
  name,
14467
14554
  kind: "sql-table",
14468
14555
  edgeType: "CALLS",
14469
14556
  confidenceKind: "structural",
14470
14557
  evidence: {
14471
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14558
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14472
14559
  line,
14473
14560
  snippet: snippet(file.content, line)
14474
14561
  }
14475
14562
  });
14476
14563
  };
14477
- walk9(tree.rootNode, (node) => {
14564
+ walk10(tree.rootNode, (node) => {
14478
14565
  if (node.type === "attribute") {
14479
14566
  const table = tableFromAttribute(node);
14480
14567
  if (table) push(table, node.startPosition.row + 1);
@@ -14492,11 +14579,11 @@ function efcoreEndpointsFromFile(file, serviceDir) {
14492
14579
  function edgeTypeFromEndpoint(ep) {
14493
14580
  switch (ep.edgeType) {
14494
14581
  case "PUBLISHES_TO":
14495
- return import_types44.EdgeType.PUBLISHES_TO;
14582
+ return import_types45.EdgeType.PUBLISHES_TO;
14496
14583
  case "CONSUMES_FROM":
14497
- return import_types44.EdgeType.CONSUMES_FROM;
14584
+ return import_types45.EdgeType.CONSUMES_FROM;
14498
14585
  default:
14499
- return import_types44.EdgeType.CALLS;
14586
+ return import_types45.EdgeType.CALLS;
14500
14587
  }
14501
14588
  }
14502
14589
  function isAwsKind(kind) {
@@ -14551,6 +14638,11 @@ async function addExternalEndpointEdges(graph, services) {
14551
14638
  } catch (err) {
14552
14639
  recordExtractionError("efcore data-axis extraction", file.path, err);
14553
14640
  }
14641
+ try {
14642
+ endpoints.push(...socketEndpointsFromFile(file, service.dir));
14643
+ } catch (err) {
14644
+ recordExtractionError("python socket call extraction", file.path, err);
14645
+ }
14554
14646
  }
14555
14647
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
14556
14648
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -14561,7 +14653,7 @@ async function addExternalEndpointEdges(graph, services) {
14561
14653
  if (!graph.hasNode(ep.infraId)) {
14562
14654
  const node = {
14563
14655
  id: ep.infraId,
14564
- type: import_types44.NodeType.InfraNode,
14656
+ type: import_types45.NodeType.InfraNode,
14565
14657
  name: ep.name,
14566
14658
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
14567
14659
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -14574,21 +14666,21 @@ async function addExternalEndpointEdges(graph, services) {
14574
14666
  }
14575
14667
  if (ep.columns && ep.columns.length > 0) {
14576
14668
  const node = graph.getNodeAttributes(ep.infraId);
14577
- if (node.type === import_types44.NodeType.InfraNode) {
14669
+ if (node.type === import_types45.NodeType.InfraNode) {
14578
14670
  graph.replaceNodeAttributes(ep.infraId, {
14579
14671
  ...node,
14580
14672
  columns: foldColumns(
14581
14673
  node.columns,
14582
14674
  ep.columns,
14583
- import_types44.Provenance.EXTRACTED,
14584
- (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
14675
+ import_types45.Provenance.EXTRACTED,
14676
+ (0, import_types45.confidenceForExtracted)(ep.confidenceKind)
14585
14677
  )
14586
14678
  });
14587
14679
  }
14588
14680
  }
14589
14681
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
14590
14682
  const node = graph.getNodeAttributes(ep.infraId);
14591
- if (node.type === import_types44.NodeType.InfraNode) {
14683
+ if (node.type === import_types45.NodeType.InfraNode) {
14592
14684
  graph.replaceNodeAttributes(ep.infraId, {
14593
14685
  ...node,
14594
14686
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -14596,7 +14688,7 @@ async function addExternalEndpointEdges(graph, services) {
14596
14688
  }
14597
14689
  }
14598
14690
  const edgeType = edgeTypeFromEndpoint(ep);
14599
- const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
14691
+ const confidence = (0, import_types45.confidenceForExtracted)(ep.confidenceKind);
14600
14692
  const relFile = toPosix(ep.evidence.file);
14601
14693
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
14602
14694
  graph,
@@ -14606,7 +14698,7 @@ async function addExternalEndpointEdges(graph, services) {
14606
14698
  );
14607
14699
  nodesAdded += n;
14608
14700
  edgesAdded += e;
14609
- if (!(0, import_types44.passesExtractedFloor)(confidence)) {
14701
+ if (!(0, import_types45.passesExtractedFloor)(confidence)) {
14610
14702
  noteExtractedDropped({
14611
14703
  source: fileNodeId,
14612
14704
  target: ep.infraId,
@@ -14626,7 +14718,7 @@ async function addExternalEndpointEdges(graph, services) {
14626
14718
  source: fileNodeId,
14627
14719
  target: ep.infraId,
14628
14720
  type: edgeType,
14629
- provenance: import_types44.Provenance.EXTRACTED,
14721
+ provenance: import_types45.Provenance.EXTRACTED,
14630
14722
  confidence,
14631
14723
  evidence: ep.evidence
14632
14724
  };
@@ -14649,7 +14741,7 @@ async function addCallEdges(graph, services) {
14649
14741
 
14650
14742
  // src/extract/table-edges.ts
14651
14743
  init_cjs_shims();
14652
- var import_types45 = require("@neat.is/types");
14744
+ var import_types46 = require("@neat.is/types");
14653
14745
  async function addTableEdges(graph, services) {
14654
14746
  let nodesAdded = 0;
14655
14747
  let edgesAdded = 0;
@@ -14677,20 +14769,20 @@ async function addTableEdges(graph, services) {
14677
14769
  }
14678
14770
  refs.push(...modelRefs);
14679
14771
  for (const ref of refs) {
14680
- const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14681
- const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
14772
+ const childId = (0, import_types46.infraId)("sql-table", ref.childTable);
14773
+ const parentId = (0, import_types46.infraId)("sql-table", ref.parentTable);
14682
14774
  if (childId === parentId) continue;
14683
14775
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
14684
14776
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
14685
- const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
14777
+ const edgeId = (0, import_types46.extractedEdgeId)(childId, parentId, import_types46.EdgeType.REFERENCES);
14686
14778
  if (graph.hasEdge(edgeId)) continue;
14687
14779
  const edge = {
14688
14780
  id: edgeId,
14689
14781
  source: childId,
14690
14782
  target: parentId,
14691
- type: import_types45.EdgeType.REFERENCES,
14692
- provenance: import_types45.Provenance.EXTRACTED,
14693
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14783
+ type: import_types46.EdgeType.REFERENCES,
14784
+ provenance: import_types46.Provenance.EXTRACTED,
14785
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
14694
14786
  evidence: ref.evidence
14695
14787
  };
14696
14788
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -14703,7 +14795,7 @@ function ensureTableNode(graph, id, name) {
14703
14795
  if (graph.hasNode(id)) return 0;
14704
14796
  const node = {
14705
14797
  id,
14706
- type: import_types45.NodeType.InfraNode,
14798
+ type: import_types46.NodeType.InfraNode,
14707
14799
  name,
14708
14800
  provider: "self",
14709
14801
  kind: "sql-table"
@@ -14717,16 +14809,16 @@ init_cjs_shims();
14717
14809
 
14718
14810
  // src/extract/infra/docker-compose.ts
14719
14811
  init_cjs_shims();
14720
- var import_node_path57 = __toESM(require("path"), 1);
14721
- var import_types47 = require("@neat.is/types");
14812
+ var import_node_path58 = __toESM(require("path"), 1);
14813
+ var import_types48 = require("@neat.is/types");
14722
14814
 
14723
14815
  // src/extract/infra/shared.ts
14724
14816
  init_cjs_shims();
14725
- var import_types46 = require("@neat.is/types");
14817
+ var import_types47 = require("@neat.is/types");
14726
14818
  function makeInfraNode(kind, name, provider = "self", extras) {
14727
14819
  return {
14728
- id: (0, import_types46.infraId)(kind, name),
14729
- type: import_types46.NodeType.InfraNode,
14820
+ id: (0, import_types47.infraId)(kind, name),
14821
+ type: import_types47.NodeType.InfraNode,
14730
14822
  name,
14731
14823
  provider,
14732
14824
  kind,
@@ -14770,8 +14862,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
14770
14862
  source: anchorId,
14771
14863
  target: node.id,
14772
14864
  type: edgeType,
14773
- provenance: import_types46.Provenance.EXTRACTED,
14774
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14865
+ provenance: import_types47.Provenance.EXTRACTED,
14866
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
14775
14867
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14776
14868
  };
14777
14869
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14788,7 +14880,7 @@ function dependsOnList(value) {
14788
14880
  }
14789
14881
  function serviceNameToServiceNode(name, services) {
14790
14882
  for (const s of services) {
14791
- if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
14883
+ if (s.node.name === name || import_node_path58.default.basename(s.dir) === name) return s.node.id;
14792
14884
  }
14793
14885
  return null;
14794
14886
  }
@@ -14797,7 +14889,7 @@ async function addComposeInfra(graph, scanPath, services) {
14797
14889
  let edgesAdded = 0;
14798
14890
  let composePath = null;
14799
14891
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
14800
- const abs = import_node_path57.default.join(scanPath, name);
14892
+ const abs = import_node_path58.default.join(scanPath, name);
14801
14893
  if (await exists(abs)) {
14802
14894
  composePath = abs;
14803
14895
  break;
@@ -14810,13 +14902,13 @@ async function addComposeInfra(graph, scanPath, services) {
14810
14902
  } catch (err) {
14811
14903
  recordExtractionError(
14812
14904
  "infra docker-compose",
14813
- import_node_path57.default.relative(scanPath, composePath),
14905
+ import_node_path58.default.relative(scanPath, composePath),
14814
14906
  err
14815
14907
  );
14816
14908
  return { nodesAdded, edgesAdded };
14817
14909
  }
14818
14910
  if (!compose?.services) return { nodesAdded, edgesAdded };
14819
- const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
14911
+ const evidenceFile = import_node_path58.default.relative(scanPath, composePath).split(import_node_path58.default.sep).join("/");
14820
14912
  const composeNameToNodeId = /* @__PURE__ */ new Map();
14821
14913
  for (const [composeName, svc] of Object.entries(compose.services)) {
14822
14914
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -14838,15 +14930,15 @@ async function addComposeInfra(graph, scanPath, services) {
14838
14930
  for (const dep of dependsOnList(svc.depends_on)) {
14839
14931
  const targetId = composeNameToNodeId.get(dep);
14840
14932
  if (!targetId) continue;
14841
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
14933
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types48.EdgeType.DEPENDS_ON);
14842
14934
  if (graph.hasEdge(edgeId)) continue;
14843
14935
  const edge = {
14844
14936
  id: edgeId,
14845
14937
  source: sourceId,
14846
14938
  target: targetId,
14847
- type: import_types47.EdgeType.DEPENDS_ON,
14848
- provenance: import_types47.Provenance.EXTRACTED,
14849
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14939
+ type: import_types48.EdgeType.DEPENDS_ON,
14940
+ provenance: import_types48.Provenance.EXTRACTED,
14941
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
14850
14942
  evidence: { file: evidenceFile }
14851
14943
  };
14852
14944
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14858,9 +14950,9 @@ async function addComposeInfra(graph, scanPath, services) {
14858
14950
 
14859
14951
  // src/extract/infra/dockerfile.ts
14860
14952
  init_cjs_shims();
14861
- var import_node_path58 = __toESM(require("path"), 1);
14953
+ var import_node_path59 = __toESM(require("path"), 1);
14862
14954
  var import_node_fs25 = require("fs");
14863
- var import_types48 = require("@neat.is/types");
14955
+ var import_types49 = require("@neat.is/types");
14864
14956
  function readDockerfile(content) {
14865
14957
  let image = null;
14866
14958
  const ports = [];
@@ -14889,7 +14981,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14889
14981
  let nodesAdded = 0;
14890
14982
  let edgesAdded = 0;
14891
14983
  for (const service of services) {
14892
- const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
14984
+ const dockerfilePath = import_node_path59.default.join(service.dir, "Dockerfile");
14893
14985
  if (!await exists(dockerfilePath)) continue;
14894
14986
  let content;
14895
14987
  try {
@@ -14897,7 +14989,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14897
14989
  } catch (err) {
14898
14990
  recordExtractionError(
14899
14991
  "infra dockerfile",
14900
- import_node_path58.default.relative(scanPath, dockerfilePath),
14992
+ import_node_path59.default.relative(scanPath, dockerfilePath),
14901
14993
  err
14902
14994
  );
14903
14995
  continue;
@@ -14909,8 +15001,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14909
15001
  graph.addNode(node.id, node);
14910
15002
  nodesAdded++;
14911
15003
  }
14912
- const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14913
- const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
15004
+ const relDockerfile = toPosix(import_node_path59.default.relative(service.dir, dockerfilePath));
15005
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, dockerfilePath));
14914
15006
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14915
15007
  graph,
14916
15008
  service.pkg.name,
@@ -14919,15 +15011,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14919
15011
  );
14920
15012
  nodesAdded += fn;
14921
15013
  edgesAdded += fe;
14922
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
15014
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types49.EdgeType.RUNS_ON);
14923
15015
  if (!graph.hasEdge(edgeId)) {
14924
15016
  const edge = {
14925
15017
  id: edgeId,
14926
15018
  source: fileNodeId,
14927
15019
  target: node.id,
14928
- type: import_types48.EdgeType.RUNS_ON,
14929
- provenance: import_types48.Provenance.EXTRACTED,
14930
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15020
+ type: import_types49.EdgeType.RUNS_ON,
15021
+ provenance: import_types49.Provenance.EXTRACTED,
15022
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14931
15023
  evidence: {
14932
15024
  file: evidenceFile,
14933
15025
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -14942,15 +15034,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14942
15034
  graph.addNode(portNode.id, portNode);
14943
15035
  nodesAdded++;
14944
15036
  }
14945
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
15037
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types49.EdgeType.CONNECTS_TO);
14946
15038
  if (graph.hasEdge(portEdgeId)) continue;
14947
15039
  const portEdge = {
14948
15040
  id: portEdgeId,
14949
15041
  source: fileNodeId,
14950
15042
  target: portNode.id,
14951
- type: import_types48.EdgeType.CONNECTS_TO,
14952
- provenance: import_types48.Provenance.EXTRACTED,
14953
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15043
+ type: import_types49.EdgeType.CONNECTS_TO,
15044
+ provenance: import_types49.Provenance.EXTRACTED,
15045
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14954
15046
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
14955
15047
  };
14956
15048
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -14963,8 +15055,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14963
15055
  // src/extract/infra/terraform.ts
14964
15056
  init_cjs_shims();
14965
15057
  var import_node_fs26 = require("fs");
14966
- var import_node_path59 = __toESM(require("path"), 1);
14967
- var import_types49 = require("@neat.is/types");
15058
+ var import_node_path60 = __toESM(require("path"), 1);
15059
+ var import_types50 = require("@neat.is/types");
14968
15060
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
14969
15061
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
14970
15062
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -14974,11 +15066,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
14974
15066
  for (const entry2 of entries) {
14975
15067
  if (entry2.isDirectory()) {
14976
15068
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
14977
- const child = import_node_path59.default.join(start, entry2.name);
15069
+ const child = import_node_path60.default.join(start, entry2.name);
14978
15070
  if (await isPythonVenvDir(child)) continue;
14979
15071
  out.push(...await walkTfFiles(child, depth + 1, max));
14980
15072
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
14981
- out.push(import_node_path59.default.join(start, entry2.name));
15073
+ out.push(import_node_path60.default.join(start, entry2.name));
14982
15074
  }
14983
15075
  }
14984
15076
  return out;
@@ -15010,7 +15102,7 @@ async function addTerraformResources(graph, scanPath) {
15010
15102
  const files = await walkTfFiles(scanPath);
15011
15103
  for (const file of files) {
15012
15104
  const content = await import_node_fs26.promises.readFile(file, "utf8");
15013
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
15105
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, file));
15014
15106
  const resources = [];
15015
15107
  const byKey = /* @__PURE__ */ new Map();
15016
15108
  RESOURCE_RE.lastIndex = 0;
@@ -15045,16 +15137,16 @@ async function addTerraformResources(graph, scanPath) {
15045
15137
  if (!target) continue;
15046
15138
  if (seen.has(target.nodeId)) continue;
15047
15139
  seen.add(target.nodeId);
15048
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
15140
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types50.EdgeType.DEPENDS_ON);
15049
15141
  if (graph.hasEdge(edgeId)) continue;
15050
15142
  const line = lineAt2(content, resource.bodyOffset + ref.index);
15051
15143
  const edge = {
15052
15144
  id: edgeId,
15053
15145
  source: resource.nodeId,
15054
15146
  target: target.nodeId,
15055
- type: import_types49.EdgeType.DEPENDS_ON,
15056
- provenance: import_types49.Provenance.EXTRACTED,
15057
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
15147
+ type: import_types50.EdgeType.DEPENDS_ON,
15148
+ provenance: import_types50.Provenance.EXTRACTED,
15149
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
15058
15150
  evidence: { file: evidenceFile, line, snippet: key }
15059
15151
  };
15060
15152
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -15068,8 +15160,9 @@ async function addTerraformResources(graph, scanPath) {
15068
15160
  // src/extract/infra/k8s.ts
15069
15161
  init_cjs_shims();
15070
15162
  var import_node_fs27 = require("fs");
15071
- var import_node_path60 = __toESM(require("path"), 1);
15163
+ var import_node_path61 = __toESM(require("path"), 1);
15072
15164
  var import_yaml3 = require("yaml");
15165
+ var import_types51 = require("@neat.is/types");
15073
15166
  var K8S_KIND_TO_INFRA_KIND = {
15074
15167
  Service: "k8s-service",
15075
15168
  Deployment: "k8s-deployment",
@@ -15079,6 +15172,7 @@ var K8S_KIND_TO_INFRA_KIND = {
15079
15172
  Job: "k8s-job",
15080
15173
  Ingress: "k8s-ingress"
15081
15174
  };
15175
+ var WORKLOAD_KINDS = /* @__PURE__ */ new Set(["Deployment", "StatefulSet", "DaemonSet"]);
15082
15176
  async function walkYamlFiles2(start, depth = 0, max = 5) {
15083
15177
  if (depth > max) return [];
15084
15178
  const out = [];
@@ -15086,17 +15180,18 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
15086
15180
  for (const entry2 of entries) {
15087
15181
  if (entry2.isDirectory()) {
15088
15182
  if (IGNORED_DIRS.has(entry2.name)) continue;
15089
- const child = import_node_path60.default.join(start, entry2.name);
15183
+ const child = import_node_path61.default.join(start, entry2.name);
15090
15184
  if (await isPythonVenvDir(child)) continue;
15091
15185
  out.push(...await walkYamlFiles2(child, depth + 1, max));
15092
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry2.name))) {
15093
- out.push(import_node_path60.default.join(start, entry2.name));
15186
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path61.default.extname(entry2.name))) {
15187
+ out.push(import_node_path61.default.join(start, entry2.name));
15094
15188
  }
15095
15189
  }
15096
15190
  return out;
15097
15191
  }
15098
15192
  async function addK8sResources(graph, scanPath) {
15099
15193
  let nodesAdded = 0;
15194
+ let edgesAdded = 0;
15100
15195
  const files = await walkYamlFiles2(scanPath);
15101
15196
  for (const file of files) {
15102
15197
  const content = await import_node_fs27.promises.readFile(file, "utf8");
@@ -15106,31 +15201,63 @@ async function addK8sResources(graph, scanPath) {
15106
15201
  } catch {
15107
15202
  continue;
15108
15203
  }
15204
+ const relFile = toPosix(import_node_path61.default.relative(scanPath, file));
15109
15205
  for (const doc of docs) {
15110
15206
  if (!doc?.kind || !doc.metadata?.name) continue;
15111
15207
  const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind];
15112
15208
  if (!infraKind) continue;
15113
- const namespaced = doc.metadata.namespace ? `${doc.metadata.namespace}/${doc.metadata.name}` : doc.metadata.name;
15114
- const node = makeInfraNode(infraKind, namespaced, "kubernetes");
15209
+ const name = doc.metadata.name;
15210
+ const isWorkload = WORKLOAD_KINDS.has(doc.kind);
15211
+ const image = isWorkload ? doc.spec?.template?.spec?.containers?.[0]?.image : void 0;
15212
+ const replicas = isWorkload && typeof doc.spec?.replicas === "number" ? doc.spec.replicas : void 0;
15213
+ const base = makeInfraNode(infraKind, name, "kubernetes");
15214
+ const node = {
15215
+ ...base,
15216
+ ...doc.metadata.namespace ? { namespace: doc.metadata.namespace } : {},
15217
+ ...image ? { image } : {},
15218
+ ...replicas !== void 0 ? { replicas } : {}
15219
+ };
15115
15220
  if (!graph.hasNode(node.id)) {
15116
15221
  graph.addNode(node.id, node);
15117
15222
  nodesAdded++;
15118
15223
  }
15224
+ if (isWorkload) {
15225
+ const svcId = (0, import_types51.serviceId)(name);
15226
+ if (graph.hasNode(svcId)) {
15227
+ const svc = graph.getNodeAttributes(svcId);
15228
+ graph.replaceNodeAttributes(svcId, {
15229
+ ...svc,
15230
+ platform: svc.platform ?? "kubernetes",
15231
+ ...image ? { declaredImage: image } : {},
15232
+ ...replicas !== void 0 ? { declaredReplicas: replicas } : {}
15233
+ });
15234
+ edgesAdded += emitPlatformResourceEdge(
15235
+ graph,
15236
+ svcId,
15237
+ import_types51.EdgeType.RUNS_ON,
15238
+ infraKind,
15239
+ name,
15240
+ "kubernetes",
15241
+ relFile,
15242
+ lineContaining(content, image)
15243
+ ).edgesAdded;
15244
+ }
15245
+ }
15119
15246
  }
15120
15247
  }
15121
- return { nodesAdded, edgesAdded: 0 };
15248
+ return { nodesAdded, edgesAdded };
15122
15249
  }
15123
15250
 
15124
15251
  // src/extract/infra/cloudflare.ts
15125
15252
  init_cjs_shims();
15126
15253
  var import_node_fs28 = require("fs");
15127
- var import_node_path61 = __toESM(require("path"), 1);
15254
+ var import_node_path62 = __toESM(require("path"), 1);
15128
15255
  var import_smol_toml3 = require("smol-toml");
15129
- var import_types50 = require("@neat.is/types");
15256
+ var import_types52 = require("@neat.is/types");
15130
15257
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
15131
15258
  async function readWranglerConfig(dir) {
15132
15259
  for (const filename of WRANGLER_FILENAMES) {
15133
- const abs = import_node_path61.default.join(dir, filename);
15260
+ const abs = import_node_path62.default.join(dir, filename);
15134
15261
  if (!await exists(abs)) continue;
15135
15262
  const raw = await import_node_fs28.promises.readFile(abs, "utf8");
15136
15263
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -15174,8 +15301,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
15174
15301
  source: anchorId,
15175
15302
  target: node.id,
15176
15303
  type: edgeType,
15177
- provenance: import_types50.Provenance.EXTRACTED,
15178
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15304
+ provenance: import_types52.Provenance.EXTRACTED,
15305
+ confidence: (0, import_types52.confidenceForExtracted)("structural"),
15179
15306
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
15180
15307
  };
15181
15308
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -15193,11 +15320,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15193
15320
  try {
15194
15321
  read = await readWranglerConfig(service.dir);
15195
15322
  } catch (err) {
15196
- recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
15323
+ recordExtractionError("infra cloudflare", import_node_path62.default.relative(scanPath, service.dir), err);
15197
15324
  continue;
15198
15325
  }
15199
15326
  if (!read || !read.config.name) continue;
15200
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
15327
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, read.relFile)));
15201
15328
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
15202
15329
  }
15203
15330
  for (const worker of discovered) {
@@ -15209,7 +15336,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15209
15336
  }
15210
15337
  let anchorId = service.node.id;
15211
15338
  if (config.main) {
15212
- const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
15339
+ const entryRelPath = toPosix(import_node_path62.default.normalize(config.main));
15213
15340
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
15214
15341
  graph,
15215
15342
  service.pkg.name,
@@ -15236,15 +15363,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15236
15363
  nodesAdded++;
15237
15364
  }
15238
15365
  if (runtimeNode.id !== anchorId) {
15239
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
15366
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types52.EdgeType.RUNS_ON);
15240
15367
  if (!graph.hasEdge(runsOnId)) {
15241
15368
  const edge = {
15242
15369
  id: runsOnId,
15243
15370
  source: anchorId,
15244
15371
  target: runtimeNode.id,
15245
- type: import_types50.EdgeType.RUNS_ON,
15246
- provenance: import_types50.Provenance.EXTRACTED,
15247
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15372
+ type: import_types52.EdgeType.RUNS_ON,
15373
+ provenance: import_types52.Provenance.EXTRACTED,
15374
+ confidence: (0, import_types52.confidenceForExtracted)("structural"),
15248
15375
  evidence: {
15249
15376
  file: evidenceFile,
15250
15377
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -15258,7 +15385,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15258
15385
  const result = addResourceEdge(
15259
15386
  graph,
15260
15387
  anchorId,
15261
- import_types50.EdgeType.CONNECTS_TO,
15388
+ import_types52.EdgeType.CONNECTS_TO,
15262
15389
  "cloudflare-route",
15263
15390
  route,
15264
15391
  evidenceFile,
@@ -15282,7 +15409,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15282
15409
  const result = addResourceEdge(
15283
15410
  graph,
15284
15411
  anchorId,
15285
- import_types50.EdgeType.DEPENDS_ON,
15412
+ import_types52.EdgeType.DEPENDS_ON,
15286
15413
  group.kind,
15287
15414
  name,
15288
15415
  evidenceFile,
@@ -15296,7 +15423,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15296
15423
  const result = addResourceEdge(
15297
15424
  graph,
15298
15425
  anchorId,
15299
- import_types50.EdgeType.DEPENDS_ON,
15426
+ import_types52.EdgeType.DEPENDS_ON,
15300
15427
  "cloudflare-cron",
15301
15428
  cron,
15302
15429
  evidenceFile,
@@ -15309,7 +15436,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15309
15436
  const result = addResourceEdge(
15310
15437
  graph,
15311
15438
  anchorId,
15312
- import_types50.EdgeType.DEPENDS_ON,
15439
+ import_types52.EdgeType.DEPENDS_ON,
15313
15440
  "cloudflare-env-var",
15314
15441
  varName,
15315
15442
  evidenceFile,
@@ -15322,15 +15449,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15322
15449
  if (!svc.service) continue;
15323
15450
  const target = workerIndex.get(svc.service);
15324
15451
  if (target && target.anchorId !== anchorId) {
15325
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
15452
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types52.EdgeType.CALLS);
15326
15453
  if (!graph.hasEdge(edgeId)) {
15327
15454
  const edge = {
15328
15455
  id: edgeId,
15329
15456
  source: anchorId,
15330
15457
  target: target.anchorId,
15331
- type: import_types50.EdgeType.CALLS,
15332
- provenance: import_types50.Provenance.EXTRACTED,
15333
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15458
+ type: import_types52.EdgeType.CALLS,
15459
+ provenance: import_types52.Provenance.EXTRACTED,
15460
+ confidence: (0, import_types52.confidenceForExtracted)("structural"),
15334
15461
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
15335
15462
  };
15336
15463
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -15341,7 +15468,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15341
15468
  const result = addResourceEdge(
15342
15469
  graph,
15343
15470
  anchorId,
15344
- import_types50.EdgeType.DEPENDS_ON,
15471
+ import_types52.EdgeType.DEPENDS_ON,
15345
15472
  "cloudflare-service-binding",
15346
15473
  svc.service,
15347
15474
  evidenceFile,
@@ -15357,12 +15484,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15357
15484
  // src/extract/infra/vercel.ts
15358
15485
  init_cjs_shims();
15359
15486
  var import_node_fs29 = require("fs");
15360
- var import_node_path62 = __toESM(require("path"), 1);
15361
- var import_types51 = require("@neat.is/types");
15487
+ var import_node_path63 = __toESM(require("path"), 1);
15488
+ var import_types53 = require("@neat.is/types");
15362
15489
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
15363
15490
  async function readVercelConfig(dir) {
15364
15491
  for (const filename of VERCEL_CONFIG_FILENAMES) {
15365
- const abs = import_node_path62.default.join(dir, filename);
15492
+ const abs = import_node_path63.default.join(dir, filename);
15366
15493
  if (!await exists(abs)) continue;
15367
15494
  const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15368
15495
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -15371,7 +15498,7 @@ async function readVercelConfig(dir) {
15371
15498
  return null;
15372
15499
  }
15373
15500
  async function readLinkedProjectName(dir) {
15374
- const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
15501
+ const abs = import_node_path63.default.join(dir, ".vercel", "project.json");
15375
15502
  if (!await exists(abs)) return void 0;
15376
15503
  const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
15377
15504
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -15389,7 +15516,7 @@ async function addVercelServices(graph, services, scanPath) {
15389
15516
  read = await readVercelConfig(service.dir);
15390
15517
  projectName = await readLinkedProjectName(service.dir);
15391
15518
  } catch (err) {
15392
- recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
15519
+ recordExtractionError("infra vercel", import_node_path63.default.relative(scanPath, service.dir), err);
15393
15520
  continue;
15394
15521
  }
15395
15522
  if (!read && !projectName) continue;
@@ -15405,7 +15532,7 @@ async function addVercelServices(graph, services, scanPath) {
15405
15532
  const anchorId = service.node.id;
15406
15533
  if (!read) continue;
15407
15534
  const { config, relFile, raw } = read;
15408
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15535
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15409
15536
  const add = (edgeType, kind, name) => {
15410
15537
  if (!name) return;
15411
15538
  const result = emitPlatformResourceEdge(
@@ -15421,12 +15548,12 @@ async function addVercelServices(graph, services, scanPath) {
15421
15548
  nodesAdded += result.nodesAdded;
15422
15549
  edgesAdded += result.edgesAdded;
15423
15550
  };
15424
- add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
15425
- for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15426
- for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15427
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15551
+ add(import_types53.EdgeType.RUNS_ON, "vercel", "vercel");
15552
+ for (const cron of config.crons ?? []) add(import_types53.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15553
+ for (const varName of Object.keys(config.env ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15554
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15428
15555
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
15429
- add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15556
+ add(import_types53.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15430
15557
  }
15431
15558
  }
15432
15559
  return { nodesAdded, edgesAdded };
@@ -15435,13 +15562,13 @@ async function addVercelServices(graph, services, scanPath) {
15435
15562
  // src/extract/infra/railway.ts
15436
15563
  init_cjs_shims();
15437
15564
  var import_node_fs30 = require("fs");
15438
- var import_node_path63 = __toESM(require("path"), 1);
15565
+ var import_node_path64 = __toESM(require("path"), 1);
15439
15566
  var import_smol_toml4 = require("smol-toml");
15440
- var import_types52 = require("@neat.is/types");
15567
+ var import_types54 = require("@neat.is/types");
15441
15568
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
15442
15569
  async function readRailwayConfig(dir) {
15443
15570
  for (const filename of RAILWAY_FILENAMES) {
15444
- const abs = import_node_path63.default.join(dir, filename);
15571
+ const abs = import_node_path64.default.join(dir, filename);
15445
15572
  if (!await exists(abs)) continue;
15446
15573
  const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15447
15574
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -15457,7 +15584,7 @@ async function addRailwayServices(graph, services, scanPath) {
15457
15584
  try {
15458
15585
  read = await readRailwayConfig(service.dir);
15459
15586
  } catch (err) {
15460
- recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
15587
+ recordExtractionError("infra railway", import_node_path64.default.relative(scanPath, service.dir), err);
15461
15588
  continue;
15462
15589
  }
15463
15590
  if (!read) continue;
@@ -15467,7 +15594,7 @@ async function addRailwayServices(graph, services, scanPath) {
15467
15594
  }
15468
15595
  const anchorId = service.node.id;
15469
15596
  const { config, relFile, raw } = read;
15470
- const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15597
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15471
15598
  const add = (edgeType, kind, name) => {
15472
15599
  if (!name) return;
15473
15600
  const result = emitPlatformResourceEdge(
@@ -15483,9 +15610,9 @@ async function addRailwayServices(graph, services, scanPath) {
15483
15610
  nodesAdded += result.nodesAdded;
15484
15611
  edgesAdded += result.edgesAdded;
15485
15612
  };
15486
- add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15487
- add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15488
- add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15613
+ add(import_types54.EdgeType.RUNS_ON, "railway", "railway");
15614
+ add(import_types54.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15615
+ add(import_types54.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15489
15616
  }
15490
15617
  return { nodesAdded, edgesAdded };
15491
15618
  }
@@ -15493,12 +15620,12 @@ async function addRailwayServices(graph, services, scanPath) {
15493
15620
  // src/extract/infra/supabase.ts
15494
15621
  init_cjs_shims();
15495
15622
  var import_node_fs31 = require("fs");
15496
- var import_node_path64 = __toESM(require("path"), 1);
15623
+ var import_node_path65 = __toESM(require("path"), 1);
15497
15624
  var import_smol_toml5 = require("smol-toml");
15498
- var import_types53 = require("@neat.is/types");
15625
+ var import_types55 = require("@neat.is/types");
15499
15626
  async function readSupabaseConfig(dir) {
15500
- const relFile = import_node_path64.default.join("supabase", "config.toml");
15501
- const abs = import_node_path64.default.join(dir, relFile);
15627
+ const relFile = import_node_path65.default.join("supabase", "config.toml");
15628
+ const abs = import_node_path65.default.join(dir, relFile);
15502
15629
  if (!await exists(abs)) return null;
15503
15630
  const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15504
15631
  const config = (0, import_smol_toml5.parse)(raw);
@@ -15512,7 +15639,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15512
15639
  try {
15513
15640
  read = await readSupabaseConfig(service.dir);
15514
15641
  } catch (err) {
15515
- recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
15642
+ recordExtractionError("infra supabase", import_node_path65.default.relative(scanPath, service.dir), err);
15516
15643
  continue;
15517
15644
  }
15518
15645
  if (!read) continue;
@@ -15527,7 +15654,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15527
15654
  });
15528
15655
  }
15529
15656
  const anchorId = service.node.id;
15530
- const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15657
+ const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
15531
15658
  const add = (edgeType, kind, name) => {
15532
15659
  if (!name) return;
15533
15660
  const result = emitPlatformResourceEdge(
@@ -15543,10 +15670,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
15543
15670
  nodesAdded += result.nodesAdded;
15544
15671
  edgesAdded += result.edgesAdded;
15545
15672
  };
15546
- add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15547
- for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15548
- if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15549
- if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15673
+ add(import_types55.EdgeType.RUNS_ON, "supabase", "supabase");
15674
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types55.EdgeType.DEPENDS_ON, "supabase-function", fn);
15675
+ if (config.storage) add(import_types55.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15676
+ if (config.auth) add(import_types55.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15550
15677
  }
15551
15678
  return { nodesAdded, edgesAdded };
15552
15679
  }
@@ -15569,18 +15696,18 @@ async function addInfra(graph, scanPath, services) {
15569
15696
 
15570
15697
  // src/extract/zod-shapes.ts
15571
15698
  init_cjs_shims();
15572
- var import_node_path65 = __toESM(require("path"), 1);
15573
- var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
15699
+ var import_node_path66 = __toESM(require("path"), 1);
15700
+ var import_tree_sitter18 = __toESM(require("tree-sitter"), 1);
15574
15701
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15575
- var import_types54 = require("@neat.is/types");
15702
+ var import_types56 = require("@neat.is/types");
15576
15703
  var ZOD_IMPORT_RE = /\bzod\b/;
15577
15704
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
15578
15705
  function parserForExt4(ext) {
15579
- const p = new import_tree_sitter17.default();
15706
+ const p = new import_tree_sitter18.default();
15580
15707
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
15581
15708
  return p;
15582
15709
  }
15583
- function namedChildren5(node) {
15710
+ function namedChildren6(node) {
15584
15711
  const out = [];
15585
15712
  for (let i = 0; i < node.namedChildCount; i++) {
15586
15713
  const c = node.namedChild(i);
@@ -15590,7 +15717,7 @@ function namedChildren5(node) {
15590
15717
  }
15591
15718
  function stringLiteralText5(node) {
15592
15719
  if (!node || node.type !== "string") return null;
15593
- for (const child of namedChildren5(node)) {
15720
+ for (const child of namedChildren6(node)) {
15594
15721
  if (child.type === "string_fragment") return child.text;
15595
15722
  }
15596
15723
  return "";
@@ -15615,12 +15742,12 @@ function zodCallKind(call) {
15615
15742
  function firstArg(call) {
15616
15743
  const args = call.childForFieldName("arguments");
15617
15744
  if (!args) return null;
15618
- return namedChildren5(args)[0] ?? null;
15745
+ return namedChildren6(args)[0] ?? null;
15619
15746
  }
15620
15747
  function fieldsFromObject(obj) {
15621
15748
  const out = [];
15622
15749
  const seen = /* @__PURE__ */ new Set();
15623
- for (const child of namedChildren5(obj)) {
15750
+ for (const child of namedChildren6(obj)) {
15624
15751
  if (child.type === "spread_element") return null;
15625
15752
  if (child.type !== "pair") continue;
15626
15753
  const key = child.childForFieldName("key");
@@ -15636,7 +15763,7 @@ function fieldsFromObject(obj) {
15636
15763
  function membersFromEnum(arr) {
15637
15764
  const out = [];
15638
15765
  const seen = /* @__PURE__ */ new Set();
15639
- for (const child of namedChildren5(arr)) {
15766
+ for (const child of namedChildren6(arr)) {
15640
15767
  if (child.type !== "string") return null;
15641
15768
  const value = stringLiteralText5(child);
15642
15769
  if (value !== null && value !== "" && !seen.has(value)) {
@@ -15648,10 +15775,10 @@ function membersFromEnum(arr) {
15648
15775
  }
15649
15776
  function topLevelSchemas(root) {
15650
15777
  const out = [];
15651
- for (const stmt of namedChildren5(root)) {
15778
+ for (const stmt of namedChildren6(root)) {
15652
15779
  const decl = stmt.type === "export_statement" ? stmt.childForFieldName("declaration") : stmt;
15653
15780
  if (decl?.type !== "lexical_declaration" && decl?.type !== "variable_declaration") continue;
15654
- for (const declarator of namedChildren5(decl)) {
15781
+ for (const declarator of namedChildren6(decl)) {
15655
15782
  if (declarator.type !== "variable_declarator") continue;
15656
15783
  const name = declarator.childForFieldName("name");
15657
15784
  const value = declarator.childForFieldName("value");
@@ -15664,7 +15791,7 @@ function topLevelSchemas(root) {
15664
15791
  }
15665
15792
  function zodShapesFromFile(file, serviceDir) {
15666
15793
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
15667
- const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
15794
+ const tree = parseSource3(parserForExt4(import_node_path66.default.extname(file.path)), file.content);
15668
15795
  const out = [];
15669
15796
  const seen = /* @__PURE__ */ new Set();
15670
15797
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -15678,11 +15805,11 @@ function zodShapesFromFile(file, serviceDir) {
15678
15805
  seen.add(name);
15679
15806
  const line = call.startPosition.row + 1;
15680
15807
  out.push({
15681
- infraId: (0, import_types54.infraId)("zod-schema", name),
15808
+ infraId: (0, import_types56.infraId)("zod-schema", name),
15682
15809
  name,
15683
15810
  fields,
15684
15811
  evidence: {
15685
- file: import_node_path65.default.relative(serviceDir, file.path),
15812
+ file: import_node_path66.default.relative(serviceDir, file.path),
15686
15813
  line,
15687
15814
  snippet: snippet(file.content, line)
15688
15815
  }
@@ -15713,7 +15840,7 @@ async function addZodShapes(graph, services) {
15713
15840
  if (!graph.hasNode(shape.infraId)) {
15714
15841
  const node = {
15715
15842
  id: shape.infraId,
15716
- type: import_types54.NodeType.InfraNode,
15843
+ type: import_types56.NodeType.InfraNode,
15717
15844
  name: shape.name,
15718
15845
  provider: "self",
15719
15846
  kind: "zod-schema"
@@ -15723,14 +15850,14 @@ async function addZodShapes(graph, services) {
15723
15850
  }
15724
15851
  if (shape.fields.length > 0) {
15725
15852
  const node = graph.getNodeAttributes(shape.infraId);
15726
- if (node.type === import_types54.NodeType.InfraNode) {
15853
+ if (node.type === import_types56.NodeType.InfraNode) {
15727
15854
  graph.replaceNodeAttributes(shape.infraId, {
15728
15855
  ...node,
15729
15856
  columns: foldColumns(
15730
15857
  node.columns,
15731
15858
  shape.fields,
15732
- import_types54.Provenance.EXTRACTED,
15733
- (0, import_types54.confidenceForExtracted)("structural")
15859
+ import_types56.Provenance.EXTRACTED,
15860
+ (0, import_types56.confidenceForExtracted)("structural")
15734
15861
  )
15735
15862
  });
15736
15863
  }
@@ -15744,15 +15871,15 @@ async function addZodShapes(graph, services) {
15744
15871
  );
15745
15872
  nodesAdded += n;
15746
15873
  edgesAdded += e;
15747
- const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
15874
+ const edgeId = (0, import_types56.extractedEdgeId)(fileNodeId, shape.infraId, import_types56.EdgeType.CONTAINS);
15748
15875
  if (!graph.hasEdge(edgeId)) {
15749
15876
  const edge = {
15750
15877
  id: edgeId,
15751
15878
  source: fileNodeId,
15752
15879
  target: shape.infraId,
15753
- type: import_types54.EdgeType.CONTAINS,
15754
- provenance: import_types54.Provenance.EXTRACTED,
15755
- confidence: (0, import_types54.confidenceForExtracted)("structural"),
15880
+ type: import_types56.EdgeType.CONTAINS,
15881
+ provenance: import_types56.Provenance.EXTRACTED,
15882
+ confidence: (0, import_types56.confidenceForExtracted)("structural"),
15756
15883
  evidence: shape.evidence
15757
15884
  };
15758
15885
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -15766,7 +15893,7 @@ async function addZodShapes(graph, services) {
15766
15893
 
15767
15894
  // src/extract/firestore-rules.ts
15768
15895
  init_cjs_shims();
15769
- var import_types55 = require("@neat.is/types");
15896
+ var import_types57 = require("@neat.is/types");
15770
15897
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
15771
15898
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
15772
15899
  function stripComments(src) {
@@ -15906,7 +16033,7 @@ async function addFirestoreRules(graph, services) {
15906
16033
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
15907
16034
  graph.forEachNode((id, attrs) => {
15908
16035
  const node = attrs;
15909
- if (node.type !== import_types55.NodeType.InfraNode) return;
16036
+ if (node.type !== import_types57.NodeType.InfraNode) return;
15910
16037
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
15911
16038
  const fields = guards.get(collectionKeyFromName(node.name));
15912
16039
  if (!fields || fields.size === 0) return;
@@ -15919,17 +16046,17 @@ async function addFirestoreRules(graph, services) {
15919
16046
  }
15920
16047
 
15921
16048
  // src/extract/index.ts
15922
- var import_node_path67 = __toESM(require("path"), 1);
16049
+ var import_node_path68 = __toESM(require("path"), 1);
15923
16050
 
15924
16051
  // src/extract/retire.ts
15925
16052
  init_cjs_shims();
15926
16053
  var import_node_fs32 = require("fs");
15927
- var import_node_path66 = __toESM(require("path"), 1);
15928
- var import_types56 = require("@neat.is/types");
16054
+ var import_node_path67 = __toESM(require("path"), 1);
16055
+ var import_types58 = require("@neat.is/types");
15929
16056
  function dropOrphanedFileNodes(graph) {
15930
16057
  const orphans = [];
15931
16058
  graph.forEachNode((id, attrs) => {
15932
- if (attrs.type !== import_types56.NodeType.FileNode) return;
16059
+ if (attrs.type !== import_types58.NodeType.FileNode) return;
15933
16060
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
15934
16061
  orphans.push(id);
15935
16062
  }
@@ -15942,14 +16069,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
15942
16069
  const bases = [scanPath, ...serviceDirs];
15943
16070
  graph.forEachEdge((id, attrs) => {
15944
16071
  const edge = attrs;
15945
- if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
16072
+ if (edge.provenance !== import_types58.Provenance.EXTRACTED) return;
15946
16073
  const evidenceFile = edge.evidence?.file;
15947
16074
  if (!evidenceFile) return;
15948
- if (import_node_path66.default.isAbsolute(evidenceFile)) {
16075
+ if (import_node_path67.default.isAbsolute(evidenceFile)) {
15949
16076
  if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
15950
16077
  return;
15951
16078
  }
15952
- const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
16079
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path67.default.join(base, evidenceFile)));
15953
16080
  if (!found) toDrop.push(id);
15954
16081
  });
15955
16082
  for (const id of toDrop) graph.dropEdge(id);
@@ -16006,7 +16133,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16006
16133
  }
16007
16134
  const droppedEntries = drainDroppedExtracted();
16008
16135
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
16009
- const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
16136
+ const rejectedPath = import_node_path68.default.join(import_node_path68.default.dirname(opts.errorsPath), "rejected.ndjson");
16010
16137
  try {
16011
16138
  await writeRejectedExtracted(droppedEntries, rejectedPath);
16012
16139
  } catch (err) {
@@ -16041,8 +16168,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16041
16168
  // src/persist.ts
16042
16169
  init_cjs_shims();
16043
16170
  var import_node_fs33 = require("fs");
16044
- var import_node_path68 = __toESM(require("path"), 1);
16045
- var import_types57 = require("@neat.is/types");
16171
+ var import_node_path69 = __toESM(require("path"), 1);
16172
+ var import_types59 = require("@neat.is/types");
16046
16173
  var SCHEMA_VERSION = 7;
16047
16174
  function migrateV1ToV2(payload) {
16048
16175
  const nodes = payload.graph.nodes;
@@ -16066,7 +16193,7 @@ function migrateV5ToV6(payload) {
16066
16193
  if (Array.isArray(nodes)) {
16067
16194
  for (const node of nodes) {
16068
16195
  const attrs = node.attributes;
16069
- if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
16196
+ if (!attrs || attrs.type !== import_types59.NodeType.InfraNode) continue;
16070
16197
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
16071
16198
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
16072
16199
  }
@@ -16082,12 +16209,12 @@ function migrateV2ToV3(payload) {
16082
16209
  for (const edge of edges) {
16083
16210
  const attrs = edge.attributes;
16084
16211
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
16085
- attrs.provenance = import_types57.Provenance.OBSERVED;
16212
+ attrs.provenance = import_types59.Provenance.OBSERVED;
16086
16213
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
16087
16214
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
16088
16215
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
16089
16216
  if (type && source && target) {
16090
- const newId = (0, import_types57.observedEdgeId)(source, target, type);
16217
+ const newId = (0, import_types59.observedEdgeId)(source, target, type);
16091
16218
  attrs.id = newId;
16092
16219
  if (edge.key) edge.key = newId;
16093
16220
  }
@@ -16096,7 +16223,7 @@ function migrateV2ToV3(payload) {
16096
16223
  return { ...payload, schemaVersion: 3 };
16097
16224
  }
16098
16225
  async function ensureDir(filePath) {
16099
- await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
16226
+ await import_node_fs33.promises.mkdir(import_node_path69.default.dirname(filePath), { recursive: true });
16100
16227
  }
16101
16228
  async function saveGraphToDisk(graph, outPath) {
16102
16229
  await ensureDir(outPath);
@@ -16186,23 +16313,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
16186
16313
 
16187
16314
  // src/projects.ts
16188
16315
  init_cjs_shims();
16189
- var import_node_path69 = __toESM(require("path"), 1);
16316
+ var import_node_path70 = __toESM(require("path"), 1);
16190
16317
  function pathsForProject(project, baseDir) {
16191
16318
  if (project === DEFAULT_PROJECT) {
16192
16319
  return {
16193
- snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
16194
- errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
16195
- staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
16196
- embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
16197
- policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
16320
+ snapshotPath: import_node_path70.default.join(baseDir, "graph.json"),
16321
+ errorsPath: import_node_path70.default.join(baseDir, "errors.ndjson"),
16322
+ staleEventsPath: import_node_path70.default.join(baseDir, "stale-events.ndjson"),
16323
+ embeddingsCachePath: import_node_path70.default.join(baseDir, "embeddings.json"),
16324
+ policyViolationsPath: import_node_path70.default.join(baseDir, "policy-violations.ndjson")
16198
16325
  };
16199
16326
  }
16200
16327
  return {
16201
- snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
16202
- errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
16203
- staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
16204
- embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
16205
- policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
16328
+ snapshotPath: import_node_path70.default.join(baseDir, `${project}.json`),
16329
+ errorsPath: import_node_path70.default.join(baseDir, `errors.${project}.ndjson`),
16330
+ staleEventsPath: import_node_path70.default.join(baseDir, `stale-events.${project}.ndjson`),
16331
+ embeddingsCachePath: import_node_path70.default.join(baseDir, `embeddings.${project}.json`),
16332
+ policyViolationsPath: import_node_path70.default.join(baseDir, `policy-violations.${project}.ndjson`)
16206
16333
  };
16207
16334
  }
16208
16335
  var Projects = class {
@@ -16240,19 +16367,19 @@ var Projects = class {
16240
16367
  init_cjs_shims();
16241
16368
  var import_fastify2 = __toESM(require("fastify"), 1);
16242
16369
  var import_cors = __toESM(require("@fastify/cors"), 1);
16243
- var import_types98 = require("@neat.is/types");
16370
+ var import_types100 = require("@neat.is/types");
16244
16371
 
16245
16372
  // src/extend/index.ts
16246
16373
  init_cjs_shims();
16247
16374
  var import_node_fs35 = require("fs");
16248
- var import_node_path71 = __toESM(require("path"), 1);
16375
+ var import_node_path72 = __toESM(require("path"), 1);
16249
16376
  var import_node_os2 = __toESM(require("os"), 1);
16250
16377
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
16251
16378
 
16252
16379
  // src/installers/package-manager.ts
16253
16380
  init_cjs_shims();
16254
16381
  var import_node_fs34 = require("fs");
16255
- var import_node_path70 = __toESM(require("path"), 1);
16382
+ var import_node_path71 = __toESM(require("path"), 1);
16256
16383
  var import_node_child_process = require("child_process");
16257
16384
  var LOCKFILE_PRIORITY = [
16258
16385
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -16274,22 +16401,22 @@ async function exists2(p) {
16274
16401
  }
16275
16402
  }
16276
16403
  async function detectPackageManager(serviceDir) {
16277
- let dir = import_node_path70.default.resolve(serviceDir);
16404
+ let dir = import_node_path71.default.resolve(serviceDir);
16278
16405
  const stops = /* @__PURE__ */ new Set();
16279
16406
  for (let i = 0; i < 64; i++) {
16280
16407
  if (stops.has(dir)) break;
16281
16408
  stops.add(dir);
16282
16409
  for (const candidate of LOCKFILE_PRIORITY) {
16283
- const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
16410
+ const lockPath = import_node_path71.default.join(dir, candidate.lockfile);
16284
16411
  if (await exists2(lockPath)) {
16285
16412
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
16286
16413
  }
16287
16414
  }
16288
- const parent = import_node_path70.default.dirname(dir);
16415
+ const parent = import_node_path71.default.dirname(dir);
16289
16416
  if (parent === dir) break;
16290
16417
  dir = parent;
16291
16418
  }
16292
- return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16419
+ return { pm: "npm", cwd: import_node_path71.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16293
16420
  }
16294
16421
  async function runPackageManagerInstall(cmd) {
16295
16422
  return new Promise((resolve) => {
@@ -16338,7 +16465,7 @@ async function fileExists2(p) {
16338
16465
  }
16339
16466
  }
16340
16467
  async function readPackageJson(scanPath) {
16341
- const pkgPath = import_node_path71.default.join(scanPath, "package.json");
16468
+ const pkgPath = import_node_path72.default.join(scanPath, "package.json");
16342
16469
  const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
16343
16470
  return JSON.parse(raw);
16344
16471
  }
@@ -16352,27 +16479,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
16352
16479
  ]);
16353
16480
  async function findHookFiles(scanPath) {
16354
16481
  const found = [];
16355
- const walk10 = async (dir) => {
16482
+ const walk11 = async (dir) => {
16356
16483
  const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
16357
16484
  for (const entry2 of entries) {
16358
16485
  if (entry2.isDirectory()) {
16359
16486
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
16360
- await walk10(import_node_path71.default.join(dir, entry2.name));
16487
+ await walk11(import_node_path72.default.join(dir, entry2.name));
16361
16488
  } else if (entry2.isFile()) {
16362
16489
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
16363
- const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry2.name));
16364
- found.push(rel.split(import_node_path71.default.sep).join("/"));
16490
+ const rel = import_node_path72.default.relative(scanPath, import_node_path72.default.join(dir, entry2.name));
16491
+ found.push(rel.split(import_node_path72.default.sep).join("/"));
16365
16492
  }
16366
16493
  }
16367
16494
  }
16368
16495
  };
16369
- await walk10(scanPath);
16496
+ await walk11(scanPath);
16370
16497
  return found.sort();
16371
16498
  }
16372
16499
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16373
16500
  let fallback = null;
16374
16501
  for (const file of hookFiles) {
16375
- const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
16502
+ const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(scanPath, file), "utf8");
16376
16503
  const patched = splicedContent(content, snippet2);
16377
16504
  if (patched !== null) return { file, content, patched };
16378
16505
  if (fallback === null) fallback = { file, content };
@@ -16380,11 +16507,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16380
16507
  return { file: fallback.file, content: fallback.content, patched: null };
16381
16508
  }
16382
16509
  function extendLogPath() {
16383
- return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16510
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path72.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16384
16511
  }
16385
16512
  async function appendExtendLog(entry2) {
16386
16513
  const logPath = extendLogPath();
16387
- await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
16514
+ await import_node_fs35.promises.mkdir(import_node_path72.default.dirname(logPath), { recursive: true });
16388
16515
  await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
16389
16516
  }
16390
16517
  function splicedContent(fileContent, snippet2) {
@@ -16443,7 +16570,7 @@ function lookupInstrumentation(library, installedVersion) {
16443
16570
  }
16444
16571
  async function describeProjectInstrumentation(ctx) {
16445
16572
  const hookFiles = await findHookFiles(ctx.scanPath);
16446
- const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
16573
+ const envNeat = await fileExists2(import_node_path72.default.join(ctx.scanPath, ".env.neat"));
16447
16574
  const registryInstrPackages = new Set(
16448
16575
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
16449
16576
  );
@@ -16465,7 +16592,7 @@ async function applyExtension(ctx, args, options) {
16465
16592
  );
16466
16593
  }
16467
16594
  for (const file of hookFiles) {
16468
- const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
16595
+ const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
16469
16596
  if (content.includes(args.registration_snippet)) {
16470
16597
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
16471
16598
  }
@@ -16477,10 +16604,10 @@ async function applyExtension(ctx, args, options) {
16477
16604
  );
16478
16605
  }
16479
16606
  const primaryFile = primary.file;
16480
- const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
16607
+ const primaryPath = import_node_path72.default.join(ctx.scanPath, primaryFile);
16481
16608
  const filesTouched = [];
16482
16609
  const depsAdded = [];
16483
- const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
16610
+ const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
16484
16611
  const pkg = await readPackageJson(ctx.scanPath);
16485
16612
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
16486
16613
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -16519,7 +16646,7 @@ async function dryRunExtension(ctx, args) {
16519
16646
  };
16520
16647
  }
16521
16648
  for (const file of hookFiles) {
16522
- const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
16649
+ const content = await import_node_fs35.promises.readFile(import_node_path72.default.join(ctx.scanPath, file), "utf8");
16523
16650
  if (content.includes(args.registration_snippet)) {
16524
16651
  return {
16525
16652
  library: args.library,
@@ -16560,7 +16687,7 @@ async function rollbackExtension(ctx, args) {
16560
16687
  if (!match) {
16561
16688
  return { undone: false, message: "no apply found for library" };
16562
16689
  }
16563
- const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
16690
+ const pkgPath = import_node_path72.default.join(ctx.scanPath, "package.json");
16564
16691
  if (await fileExists2(pkgPath)) {
16565
16692
  const pkg = await readPackageJson(ctx.scanPath);
16566
16693
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -16571,7 +16698,7 @@ async function rollbackExtension(ctx, args) {
16571
16698
  }
16572
16699
  const hookFiles = await findHookFiles(ctx.scanPath);
16573
16700
  for (const file of hookFiles) {
16574
- const filePath = import_node_path71.default.join(ctx.scanPath, file);
16701
+ const filePath = import_node_path72.default.join(ctx.scanPath, file);
16575
16702
  const content = await import_node_fs35.promises.readFile(filePath, "utf8");
16576
16703
  if (content.includes(match.registration_snippet)) {
16577
16704
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -16587,39 +16714,39 @@ async function rollbackExtension(ctx, args) {
16587
16714
 
16588
16715
  // src/divergences.ts
16589
16716
  init_cjs_shims();
16590
- var import_types58 = require("@neat.is/types");
16717
+ var import_types60 = require("@neat.is/types");
16591
16718
  function bucketKey(source, target, type) {
16592
16719
  return `${type}|${source}|${target}`;
16593
16720
  }
16594
16721
  function bucketSourceFor(graph, edge) {
16595
- if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16596
- const parsed = (0, import_types58.parseFileId)(edge.source);
16722
+ if (edge.type !== import_types60.EdgeType.CONNECTS_TO) return edge.source;
16723
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16597
16724
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
16598
16725
  const target = graph.getNodeAttributes(edge.target);
16599
- if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16600
- return (0, import_types58.serviceId)(parsed.service);
16726
+ if (target.type !== import_types60.NodeType.DatabaseNode) return edge.source;
16727
+ return (0, import_types60.serviceId)(parsed.service);
16601
16728
  }
16602
16729
  function bucketEdges(graph) {
16603
16730
  const buckets2 = /* @__PURE__ */ new Map();
16604
16731
  graph.forEachEdge((id, attrs) => {
16605
16732
  const e = attrs;
16606
- const parsed = (0, import_types58.parseEdgeId)(id);
16733
+ const parsed = (0, import_types60.parseEdgeId)(id);
16607
16734
  const provenance = parsed?.provenance ?? e.provenance;
16608
16735
  const source = bucketSourceFor(graph, e);
16609
16736
  const key = bucketKey(source, e.target, e.type);
16610
16737
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
16611
16738
  switch (provenance) {
16612
- case import_types58.Provenance.EXTRACTED:
16739
+ case import_types60.Provenance.EXTRACTED:
16613
16740
  cur.extracted = e;
16614
16741
  break;
16615
- case import_types58.Provenance.OBSERVED:
16742
+ case import_types60.Provenance.OBSERVED:
16616
16743
  cur.observed = e;
16617
16744
  break;
16618
- case import_types58.Provenance.INFERRED:
16745
+ case import_types60.Provenance.INFERRED:
16619
16746
  cur.inferred = e;
16620
16747
  break;
16621
16748
  default:
16622
- if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
16749
+ if (e.provenance === import_types60.Provenance.STALE) cur.stale = e;
16623
16750
  }
16624
16751
  buckets2.set(key, cur);
16625
16752
  });
@@ -16628,22 +16755,22 @@ function bucketEdges(graph) {
16628
16755
  function nodeIsFrontier(graph, nodeId) {
16629
16756
  if (!graph.hasNode(nodeId)) return false;
16630
16757
  const attrs = graph.getNodeAttributes(nodeId);
16631
- return attrs.type === import_types58.NodeType.FrontierNode;
16758
+ return attrs.type === import_types60.NodeType.FrontierNode;
16632
16759
  }
16633
16760
  function nodeIsWebsocketChannel(graph, nodeId) {
16634
16761
  if (!graph.hasNode(nodeId)) return false;
16635
16762
  const attrs = graph.getNodeAttributes(nodeId);
16636
- return attrs.type === import_types58.NodeType.WebSocketChannelNode;
16763
+ return attrs.type === import_types60.NodeType.WebSocketChannelNode;
16637
16764
  }
16638
16765
  function nodeIsServerAction(graph, nodeId) {
16639
16766
  if (!graph.hasNode(nodeId)) return false;
16640
16767
  const attrs = graph.getNodeAttributes(nodeId);
16641
- return attrs.type === import_types58.NodeType.ServerActionNode;
16768
+ return attrs.type === import_types60.NodeType.ServerActionNode;
16642
16769
  }
16643
16770
  function nodeIsSymbol(graph, nodeId) {
16644
16771
  if (!graph.hasNode(nodeId)) return false;
16645
16772
  const attrs = graph.getNodeAttributes(nodeId);
16646
- return attrs.type === import_types58.NodeType.SymbolNode;
16773
+ return attrs.type === import_types60.NodeType.SymbolNode;
16647
16774
  }
16648
16775
  function clampConfidence(n) {
16649
16776
  if (!Number.isFinite(n)) return 0;
@@ -16663,14 +16790,14 @@ function gradedConfidence(edge) {
16663
16790
  return clampConfidence(confidenceForEdge(edge));
16664
16791
  }
16665
16792
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
16666
- import_types58.EdgeType.CALLS,
16667
- import_types58.EdgeType.CONNECTS_TO,
16668
- import_types58.EdgeType.PUBLISHES_TO,
16669
- import_types58.EdgeType.CONSUMES_FROM
16793
+ import_types60.EdgeType.CALLS,
16794
+ import_types60.EdgeType.CONNECTS_TO,
16795
+ import_types60.EdgeType.PUBLISHES_TO,
16796
+ import_types60.EdgeType.CONSUMES_FROM
16670
16797
  ]);
16671
16798
  function detectMissingDivergences(graph, bucket) {
16672
16799
  const out = [];
16673
- if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
16800
+ if (bucket.type === import_types60.EdgeType.CONTAINS) return out;
16674
16801
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
16675
16802
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
16676
16803
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -16712,7 +16839,7 @@ function declaredHostFor(svc) {
16712
16839
  function hasExtractedConfiguredBy(graph, svcId) {
16713
16840
  for (const edgeId of graph.outboundEdges(svcId)) {
16714
16841
  const e = graph.getEdgeAttributes(edgeId);
16715
- if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
16842
+ if (e.type === import_types60.EdgeType.CONFIGURED_BY && e.provenance === import_types60.Provenance.EXTRACTED) {
16716
16843
  return true;
16717
16844
  }
16718
16845
  }
@@ -16725,10 +16852,10 @@ function detectHostMismatch(graph, svcId, svc) {
16725
16852
  const out = [];
16726
16853
  for (const edgeId of graph.outboundEdges(svcId)) {
16727
16854
  const edge = graph.getEdgeAttributes(edgeId);
16728
- if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16729
- if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
16855
+ if (edge.type !== import_types60.EdgeType.CONNECTS_TO) continue;
16856
+ if (edge.provenance !== import_types60.Provenance.OBSERVED) continue;
16730
16857
  const target = graph.getNodeAttributes(edge.target);
16731
- if (target.type !== import_types58.NodeType.DatabaseNode) continue;
16858
+ if (target.type !== import_types60.NodeType.DatabaseNode) continue;
16732
16859
  const observedHost = target.host?.trim();
16733
16860
  if (!observedHost) continue;
16734
16861
  if (observedHost === declaredHost) continue;
@@ -16750,10 +16877,10 @@ function detectCompatDivergences(graph, svcId, svc) {
16750
16877
  const deps = svc.dependencies ?? {};
16751
16878
  for (const edgeId of graph.outboundEdges(svcId)) {
16752
16879
  const edge = graph.getEdgeAttributes(edgeId);
16753
- if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16754
- if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
16880
+ if (edge.type !== import_types60.EdgeType.CONNECTS_TO) continue;
16881
+ if (edge.provenance !== import_types60.Provenance.OBSERVED) continue;
16755
16882
  const target = graph.getNodeAttributes(edge.target);
16756
- if (target.type !== import_types58.NodeType.DatabaseNode) continue;
16883
+ if (target.type !== import_types60.NodeType.DatabaseNode) continue;
16757
16884
  for (const pair of compatPairs()) {
16758
16885
  if (pair.engine !== target.engine) continue;
16759
16886
  const declared = deps[pair.driver];
@@ -16897,11 +17024,11 @@ function symbolLocus(graph, ev) {
16897
17024
  const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
16898
17025
  const affected = ev.affectedNode;
16899
17026
  const affectedInGraph = affected.length > 0 && graph.hasNode(affected);
16900
- const affectedIsCode = affectedInGraph && ((0, import_types58.parseSymbolId)(affected) !== null || (0, import_types58.parseFileId)(affected) !== null);
17027
+ const affectedIsCode = affectedInGraph && ((0, import_types60.parseSymbolId)(affected) !== null || (0, import_types60.parseFileId)(affected) !== null);
16901
17028
  if (affectedIsCode) return { node: affected, ...location ? { location } : {} };
16902
17029
  if (!location) return null;
16903
17030
  if (affectedInGraph) return { node: affected, location };
16904
- const svc = (0, import_types58.serviceId)(ev.service);
17031
+ const svc = (0, import_types60.serviceId)(ev.service);
16905
17032
  if (graph.hasNode(svc)) return { node: svc, location };
16906
17033
  return null;
16907
17034
  }
@@ -16944,7 +17071,7 @@ function detectSymbolMismatches(graph, incidents) {
16944
17071
  mismatchKind: g.kind,
16945
17072
  ...g.symbol ? { symbol: g.symbol } : {},
16946
17073
  ...g.location ? { location: g.location } : {},
16947
- provenance: import_types58.Provenance.INFERRED,
17074
+ provenance: import_types60.Provenance.INFERRED,
16948
17075
  incidentId: g.latest.id,
16949
17076
  errorMessage: g.latest.errorMessage,
16950
17077
  incidentCount: g.count,
@@ -16961,7 +17088,7 @@ var OBSERVED_FAILING_CONFIDENCE = 0.6;
16961
17088
  function detectObservedFailingEdge(graph, bucket) {
16962
17089
  if (!bucket.extracted || !bucket.observed) return [];
16963
17090
  if (!OBSERVABLE_EDGE_TYPES.has(bucket.type)) return [];
16964
- if (bucket.observed.provenance !== import_types58.Provenance.OBSERVED) return [];
17091
+ if (bucket.observed.provenance !== import_types60.Provenance.OBSERVED) return [];
16965
17092
  const signal = bucket.observed.signal;
16966
17093
  if (!signal) return [];
16967
17094
  const { spanCount, errorCount } = signal;
@@ -16975,7 +17102,7 @@ function detectObservedFailingEdge(graph, bucket) {
16975
17102
  source: bucket.source,
16976
17103
  target: bucket.target,
16977
17104
  failureKind: "error-rate",
16978
- provenance: import_types58.Provenance.INFERRED,
17105
+ provenance: import_types60.Provenance.INFERRED,
16979
17106
  edgeType: bucket.type,
16980
17107
  observed: bucket.observed,
16981
17108
  spanCount,
@@ -17075,7 +17202,7 @@ function detectObservedFailingIncidents(graph, incidents) {
17075
17202
  source: g.node,
17076
17203
  target: g.node,
17077
17204
  failureKind: g.kind,
17078
- provenance: import_types58.Provenance.INFERRED,
17205
+ provenance: import_types60.Provenance.INFERRED,
17079
17206
  ...g.location ? { location: g.location } : {},
17080
17207
  incidentId: g.latest.id,
17081
17208
  errorMessage: g.latest.errorMessage,
@@ -17094,24 +17221,24 @@ function involvesNode(d, nodeId) {
17094
17221
  function datastoreEverObserved(graph, nodeId) {
17095
17222
  if (!graph.hasNode(nodeId)) return false;
17096
17223
  const n = graph.getNodeAttributes(nodeId);
17097
- if (n.type === import_types58.NodeType.DatabaseNode) {
17224
+ if (n.type === import_types60.NodeType.DatabaseNode) {
17098
17225
  const via = n.discoveredVia;
17099
17226
  if (via === "otel" || via === "merged") return true;
17100
17227
  }
17101
17228
  for (const edgeId of graph.inboundEdges(nodeId)) {
17102
17229
  const e = graph.getEdgeAttributes(edgeId);
17103
- if (e.provenance === import_types58.Provenance.OBSERVED || e.provenance === import_types58.Provenance.STALE) return true;
17230
+ if (e.provenance === import_types60.Provenance.OBSERVED || e.provenance === import_types60.Provenance.STALE) return true;
17104
17231
  }
17105
17232
  return false;
17106
17233
  }
17107
- function serviceHasObservedSameEngineStore(graph, buckets2, serviceId16, engine, excludeTarget) {
17234
+ function serviceHasObservedSameEngineStore(graph, buckets2, serviceId17, engine, excludeTarget) {
17108
17235
  for (const bucket of buckets2.values()) {
17109
- if (bucket.type !== import_types58.EdgeType.CONNECTS_TO) continue;
17110
- if (bucket.source !== serviceId16) continue;
17236
+ if (bucket.type !== import_types60.EdgeType.CONNECTS_TO) continue;
17237
+ if (bucket.source !== serviceId17) continue;
17111
17238
  if (bucket.target === excludeTarget) continue;
17112
17239
  if (!graph.hasNode(bucket.target)) continue;
17113
17240
  const target = graph.getNodeAttributes(bucket.target);
17114
- if (target.type !== import_types58.NodeType.DatabaseNode) continue;
17241
+ if (target.type !== import_types60.NodeType.DatabaseNode) continue;
17115
17242
  if (target.engine !== engine) continue;
17116
17243
  if (bucket.observed || datastoreEverObserved(graph, bucket.target)) return true;
17117
17244
  }
@@ -17121,10 +17248,10 @@ var DEAD_CODE_PROBE_CONFIDENCE = 0.1;
17121
17248
  function dampenDeadCodeProbes(graph, buckets2, all) {
17122
17249
  return all.map((d) => {
17123
17250
  if (d.type !== "missing-observed") return d;
17124
- if (!d.extracted || d.edgeType !== import_types58.EdgeType.CONNECTS_TO) return d;
17251
+ if (!d.extracted || d.edgeType !== import_types60.EdgeType.CONNECTS_TO) return d;
17125
17252
  if (!graph.hasNode(d.target)) return d;
17126
17253
  const target = graph.getNodeAttributes(d.target);
17127
- if (target.type !== import_types58.NodeType.DatabaseNode) return d;
17254
+ if (target.type !== import_types60.NodeType.DatabaseNode) return d;
17128
17255
  if (d.extracted.evidence?.hostSource !== "literal") return d;
17129
17256
  if (datastoreEverObserved(graph, d.target)) return d;
17130
17257
  const engine = target.engine;
@@ -17144,7 +17271,7 @@ function suppressHostMismatchHalves(all) {
17144
17271
  for (const d of all) {
17145
17272
  if (d.type !== "host-mismatch") continue;
17146
17273
  observedHalf.add(`${d.source}->${d.target}`);
17147
- declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
17274
+ declaredHalf.add((0, import_types60.databaseId)(d.extractedHost));
17148
17275
  }
17149
17276
  if (observedHalf.size === 0) return all;
17150
17277
  return all.filter((d) => {
@@ -17164,13 +17291,13 @@ function computeDivergences(graph, opts = {}) {
17164
17291
  }
17165
17292
  graph.forEachNode((nodeId, attrs) => {
17166
17293
  const n = attrs;
17167
- if (n.type === import_types58.NodeType.ServiceNode) {
17294
+ if (n.type === import_types60.NodeType.ServiceNode) {
17168
17295
  const svc = n;
17169
17296
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
17170
17297
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
17171
17298
  return;
17172
17299
  }
17173
- if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
17300
+ if (n.type === import_types60.NodeType.InfraNode && n.kind === "sql-table") {
17174
17301
  for (const d of detectColumnDrift(n)) all.push(d);
17175
17302
  }
17176
17303
  });
@@ -17230,7 +17357,7 @@ function computeDivergences(graph, opts = {}) {
17230
17357
  const bloc = "location" in b && b.location ? b.location : "";
17231
17358
  return aloc.localeCompare(bloc);
17232
17359
  });
17233
- return import_types58.DivergenceResultSchema.parse({
17360
+ return import_types60.DivergenceResultSchema.parse({
17234
17361
  divergences: filtered,
17235
17362
  totalAffected: filtered.length,
17236
17363
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -17284,7 +17411,7 @@ function queryLogEntries(opts) {
17284
17411
 
17285
17412
  // src/goodybag.ts
17286
17413
  init_cjs_shims();
17287
- var import_types59 = require("@neat.is/types");
17414
+ var import_types61 = require("@neat.is/types");
17288
17415
  var CODE_FILEPATH_ATTR2 = "code.filepath";
17289
17416
  var CODE_LINENO_ATTR2 = "code.lineno";
17290
17417
  var BLAST_NEAREST_LIMIT = 5;
@@ -17309,7 +17436,7 @@ function locusOf(graph, ev) {
17309
17436
  ...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
17310
17437
  ...node?.name ? { symbol: node.name } : {},
17311
17438
  service: node?.service ?? ev.service,
17312
- provenance: import_types59.Provenance.OBSERVED
17439
+ provenance: import_types61.Provenance.OBSERVED
17313
17440
  };
17314
17441
  }
17315
17442
  function locusFromNode(graph, nodeId) {
@@ -17325,7 +17452,7 @@ function locusFromNode(graph, nodeId) {
17325
17452
  ...typeof end === "number" ? { lineEnd: end } : {},
17326
17453
  ...n.qualname ? { symbol: shortLabel(graph, nodeId) } : {},
17327
17454
  ...n.service ? { service: n.service } : {},
17328
- provenance: import_types59.Provenance.INFERRED
17455
+ provenance: import_types61.Provenance.INFERRED
17329
17456
  };
17330
17457
  }
17331
17458
  function promoteCauseLocus(graph, causeNode, incidents) {
@@ -17334,7 +17461,7 @@ function promoteCauseLocus(graph, causeNode, incidents) {
17334
17461
  );
17335
17462
  if (native) {
17336
17463
  const l = locusOf(graph, native);
17337
- if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types59.Provenance.INFERRED };
17464
+ if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types61.Provenance.INFERRED };
17338
17465
  }
17339
17466
  return locusFromNode(graph, causeNode);
17340
17467
  }
@@ -17383,7 +17510,7 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17383
17510
  const chain = (rc.traversalPath ?? []).map((node, i) => ({
17384
17511
  node,
17385
17512
  grain: grainOf2(graph, node),
17386
- provenance: provs[i] ?? provs[provs.length - 1] ?? import_types59.Provenance.INFERRED
17513
+ provenance: provs[i] ?? provs[provs.length - 1] ?? import_types61.Provenance.INFERRED
17387
17514
  }));
17388
17515
  rootCause = {
17389
17516
  node: rc.rootCauseNode,
@@ -17413,7 +17540,7 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17413
17540
  kind: "incident",
17414
17541
  id: errorEvent.id,
17415
17542
  at: errorEvent.timestamp,
17416
- incidentKind: (0, import_types59.incidentKindOf)(errorEvent),
17543
+ incidentKind: (0, import_types61.incidentKindOf)(errorEvent),
17417
17544
  service: errorEvent.service,
17418
17545
  affectedNode: affected,
17419
17546
  message: errorEvent.errorMessage,
@@ -17430,12 +17557,12 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17430
17557
  ...divergences.length > 0 ? { divergence: divergences } : {},
17431
17558
  headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
17432
17559
  };
17433
- return import_types59.IncidentCardSchema.parse(card);
17560
+ return import_types61.IncidentCardSchema.parse(card);
17434
17561
  }
17435
17562
 
17436
17563
  // src/ask.ts
17437
17564
  init_cjs_shims();
17438
- var import_types60 = require("@neat.is/types");
17565
+ var import_types62 = require("@neat.is/types");
17439
17566
  var DEFAULT_MAX_NODES = 3;
17440
17567
  var MAX_FACTS_PER_SECTION = 6;
17441
17568
  var INTENT_RULES = [
@@ -17663,18 +17790,18 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17663
17790
  };
17664
17791
  graph.forEachNode((id, attrs) => {
17665
17792
  const node = attrs;
17666
- if (node.type === import_types60.NodeType.FrontierNode) return;
17793
+ if (node.type === import_types62.NodeType.FrontierNode) return;
17667
17794
  const name = nodeName(node);
17668
17795
  const body = idBody(id);
17669
17796
  const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
17670
17797
  if (labelTokens.size === 0) return;
17671
17798
  let matched = 0;
17672
17799
  for (const t of qTokens) if (labelTokens.has(t)) matched += 1;
17673
- const named = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
17674
- if (matched === 0 && !named) return;
17800
+ const named2 = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
17801
+ if (matched === 0 && !named2) return;
17675
17802
  const coverage = qTokens.length > 0 ? matched / qTokens.length : 0;
17676
- const via = named ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
17677
- const score = named ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
17803
+ const via = named2 ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
17804
+ const score = named2 ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
17678
17805
  consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
17679
17806
  });
17680
17807
  if (searchIndex) {
@@ -17682,7 +17809,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17682
17809
  const res = await searchIndex.search(question, 10);
17683
17810
  if (res.provider !== "substring") {
17684
17811
  for (const m of res.matches) {
17685
- if (m.node.type === import_types60.NodeType.FrontierNode) continue;
17812
+ if (m.node.type === import_types62.NodeType.FrontierNode) continue;
17686
17813
  const already = best.get(m.node.id);
17687
17814
  if (!already && m.score < EMBED_MIN_SCORE) continue;
17688
17815
  consider({
@@ -17720,7 +17847,7 @@ function edgeSignalNote(e) {
17720
17847
  function buildRootCauseSection(graph, node, incidents, now) {
17721
17848
  const result = getRootCause(graph, node, void 0, incidents, { now });
17722
17849
  if (!result) return null;
17723
- const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types60.Provenance.OBSERVED;
17850
+ const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types62.Provenance.OBSERVED;
17724
17851
  const facts = [
17725
17852
  {
17726
17853
  text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
@@ -17770,7 +17897,7 @@ function buildObservedSection(graph, node) {
17770
17897
  if (result.observed && result.inboundObservedCount > 0) {
17771
17898
  facts.push({
17772
17899
  text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
17773
- provenance: import_types60.Provenance.OBSERVED
17900
+ provenance: import_types62.Provenance.OBSERVED
17774
17901
  });
17775
17902
  } else {
17776
17903
  return null;
@@ -17798,7 +17925,7 @@ function buildIncidentsSection(node, incidents) {
17798
17925
  const facts = ordered.map((ev) => ({
17799
17926
  text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
17800
17927
  // ErrorEvents are observation records — OBSERVED by definition.
17801
- provenance: import_types60.Provenance.OBSERVED
17928
+ provenance: import_types62.Provenance.OBSERVED
17802
17929
  }));
17803
17930
  return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
17804
17931
  }
@@ -17858,7 +17985,7 @@ function buildGlobalIncidentsSection(incidents) {
17858
17985
  }
17859
17986
  const byKey = /* @__PURE__ */ new Map();
17860
17987
  for (const ev of incidents) {
17861
- const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
17988
+ const key = ev.affectedNode || (0, import_types62.serviceId)(ev.service);
17862
17989
  const cur = byKey.get(key);
17863
17990
  if (!cur) {
17864
17991
  byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
@@ -17873,7 +18000,7 @@ function buildGlobalIncidentsSection(incidents) {
17873
18000
  const rows = [...byKey.values()].sort((a, b) => b.count - a.count || b.latest.localeCompare(a.latest) || a.key.localeCompare(b.key)).slice(0, MAX_FACTS_PER_SECTION);
17874
18001
  const facts = rows.map((r) => ({
17875
18002
  text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
17876
- provenance: import_types60.Provenance.OBSERVED
18003
+ provenance: import_types62.Provenance.OBSERVED
17877
18004
  }));
17878
18005
  return {
17879
18006
  heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
@@ -17886,7 +18013,7 @@ function buildOverviewSections(graph, incidents) {
17886
18013
  graph.forEachNode((_id, attrs) => {
17887
18014
  const node = attrs;
17888
18015
  nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
17889
- if (node.type === import_types60.NodeType.ServiceNode) services.push(node.id);
18016
+ if (node.type === import_types62.NodeType.ServiceNode) services.push(node.id);
17890
18017
  });
17891
18018
  const edgeByProv = /* @__PURE__ */ new Map();
17892
18019
  graph.forEachEdge((_id, attrs) => {
@@ -17898,10 +18025,10 @@ function buildOverviewSections(graph, incidents) {
17898
18025
  const shapeFacts = [
17899
18026
  { text: `${graph.order} nodes, ${graph.size} edges` },
17900
18027
  {
17901
- text: `${count(import_types60.NodeType.ServiceNode)} services, ${count(import_types60.NodeType.FileNode)} files, ${count(import_types60.NodeType.SymbolNode)} symbols, ${count(import_types60.NodeType.DatabaseNode)} databases`
18028
+ text: `${count(import_types62.NodeType.ServiceNode)} services, ${count(import_types62.NodeType.FileNode)} files, ${count(import_types62.NodeType.SymbolNode)} symbols, ${count(import_types62.NodeType.DatabaseNode)} databases`
17902
18029
  }
17903
18030
  ];
17904
- for (const p of [import_types60.Provenance.EXTRACTED, import_types60.Provenance.OBSERVED, import_types60.Provenance.INFERRED, import_types60.Provenance.STALE]) {
18031
+ for (const p of [import_types62.Provenance.EXTRACTED, import_types62.Provenance.OBSERVED, import_types62.Provenance.INFERRED, import_types62.Provenance.STALE]) {
17905
18032
  const n = edgeByProv.get(p) ?? 0;
17906
18033
  if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
17907
18034
  }
@@ -17918,7 +18045,7 @@ function buildOverviewSections(graph, incidents) {
17918
18045
  if (incidents && incidents.length > 0) {
17919
18046
  const incCount = /* @__PURE__ */ new Map();
17920
18047
  for (const ev of incidents) {
17921
- const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
18048
+ const key = ev.affectedNode || (0, import_types62.serviceId)(ev.service);
17922
18049
  incCount.set(key, (incCount.get(key) ?? 0) + 1);
17923
18050
  }
17924
18051
  const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
@@ -17926,7 +18053,7 @@ function buildOverviewSections(graph, incidents) {
17926
18053
  heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
17927
18054
  facts: top.map(([k, n]) => ({
17928
18055
  text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
17929
- provenance: import_types60.Provenance.OBSERVED
18056
+ provenance: import_types62.Provenance.OBSERVED
17930
18057
  }))
17931
18058
  });
17932
18059
  }
@@ -18062,7 +18189,7 @@ async function askGraph(graph, question, opts = {}) {
18062
18189
  const provSet = /* @__PURE__ */ new Set();
18063
18190
  for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
18064
18191
  const confidence = sections[0]?.facts[0]?.confidence;
18065
- return import_types60.AskResultSchema.parse({
18192
+ return import_types62.AskResultSchema.parse({
18066
18193
  question,
18067
18194
  intent,
18068
18195
  matched,
@@ -18156,26 +18283,26 @@ function canonicalJson(value) {
18156
18283
  init_cjs_shims();
18157
18284
  var import_node_fs37 = require("fs");
18158
18285
  var import_node_os3 = __toESM(require("os"), 1);
18159
- var import_node_path72 = __toESM(require("path"), 1);
18160
- var import_types61 = require("@neat.is/types");
18286
+ var import_node_path73 = __toESM(require("path"), 1);
18287
+ var import_types63 = require("@neat.is/types");
18161
18288
  var LOCK_TIMEOUT_MS = 5e3;
18162
18289
  var LOCK_RETRY_MS = 50;
18163
18290
  function neatHome() {
18164
18291
  const override = process.env.NEAT_HOME;
18165
- if (override && override.length > 0) return import_node_path72.default.resolve(override);
18166
- return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
18292
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
18293
+ return import_node_path73.default.join(import_node_os3.default.homedir(), ".neat");
18167
18294
  }
18168
18295
  function registryPath() {
18169
- return import_node_path72.default.join(neatHome(), "projects.json");
18296
+ return import_node_path73.default.join(neatHome(), "projects.json");
18170
18297
  }
18171
18298
  function registryLockPath() {
18172
- return import_node_path72.default.join(neatHome(), "projects.json.lock");
18299
+ return import_node_path73.default.join(neatHome(), "projects.json.lock");
18173
18300
  }
18174
18301
  function daemonPidPath() {
18175
- return import_node_path72.default.join(neatHome(), "neatd.pid");
18302
+ return import_node_path73.default.join(neatHome(), "neatd.pid");
18176
18303
  }
18177
18304
  function daemonsDir() {
18178
- return import_node_path72.default.join(neatHome(), "daemons");
18305
+ return import_node_path73.default.join(neatHome(), "daemons");
18179
18306
  }
18180
18307
  function isFiniteInt(v) {
18181
18308
  return typeof v === "number" && Number.isFinite(v);
@@ -18216,7 +18343,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
18216
18343
  const out = [];
18217
18344
  for (const name of names) {
18218
18345
  if (!name.endsWith(".json")) continue;
18219
- const file = import_node_path72.default.join(dir, name);
18346
+ const file = import_node_path73.default.join(dir, name);
18220
18347
  let raw;
18221
18348
  try {
18222
18349
  raw = await import_node_fs37.promises.readFile(file, "utf8");
@@ -18293,7 +18420,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
18293
18420
  }
18294
18421
  }
18295
18422
  async function writeAtomically(target, contents) {
18296
- await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
18423
+ await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(target), { recursive: true });
18297
18424
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
18298
18425
  const fd = await import_node_fs37.promises.open(tmp, "w");
18299
18426
  try {
@@ -18306,7 +18433,7 @@ async function writeAtomically(target, contents) {
18306
18433
  }
18307
18434
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
18308
18435
  const deadline = Date.now() + timeoutMs;
18309
- await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
18436
+ await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(lockPath), { recursive: true });
18310
18437
  let probedHolder = false;
18311
18438
  while (true) {
18312
18439
  try {
@@ -18359,10 +18486,10 @@ async function readRegistry() {
18359
18486
  throw err;
18360
18487
  }
18361
18488
  const parsed = JSON.parse(raw);
18362
- return import_types61.RegistryFileSchema.parse(parsed);
18489
+ return import_types63.RegistryFileSchema.parse(parsed);
18363
18490
  }
18364
18491
  async function writeRegistry(reg) {
18365
- const validated = import_types61.RegistryFileSchema.parse(reg);
18492
+ const validated = import_types63.RegistryFileSchema.parse(reg);
18366
18493
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
18367
18494
  }
18368
18495
  async function getProject(name) {
@@ -18507,7 +18634,7 @@ init_auth();
18507
18634
  // src/connectors-config.ts
18508
18635
  init_cjs_shims();
18509
18636
  var import_node_os4 = __toESM(require("os"), 1);
18510
- var import_node_path73 = __toESM(require("path"), 1);
18637
+ var import_node_path74 = __toESM(require("path"), 1);
18511
18638
  var import_node_fs38 = require("fs");
18512
18639
  var CONNECTORS_CONFIG_VERSION = 1;
18513
18640
  var EnvRefUnsetError = class extends Error {
@@ -18522,11 +18649,11 @@ var EnvRefUnsetError = class extends Error {
18522
18649
  };
18523
18650
  function neatHome2() {
18524
18651
  const override = process.env.NEAT_HOME;
18525
- if (override && override.length > 0) return import_node_path73.default.resolve(override);
18526
- return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
18652
+ if (override && override.length > 0) return import_node_path74.default.resolve(override);
18653
+ return import_node_path74.default.join(import_node_os4.default.homedir(), ".neat");
18527
18654
  }
18528
18655
  function connectorsConfigPath(home = neatHome2()) {
18529
- return import_node_path73.default.join(home, "connectors.json");
18656
+ return import_node_path74.default.join(home, "connectors.json");
18530
18657
  }
18531
18658
  var MODE_MASK_LOOSER_THAN_0600 = 63;
18532
18659
  async function warnIfModeLooserThan0600(file) {
@@ -18713,15 +18840,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
18713
18840
 
18714
18841
  // src/connectors/index.ts
18715
18842
  init_cjs_shims();
18716
- var import_types62 = require("@neat.is/types");
18843
+ var import_types64 = require("@neat.is/types");
18717
18844
  var NO_ENV = "unknown";
18718
18845
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
18719
18846
  if (!graph.hasNode(targetNodeId)) return void 0;
18720
18847
  const sites = [];
18721
18848
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
18722
18849
  const edge = graph.getEdgeAttributes(edgeId);
18723
- if (edge.provenance !== import_types62.Provenance.EXTRACTED) continue;
18724
- const parsed = (0, import_types62.parseFileId)(edge.source);
18850
+ if (edge.provenance !== import_types64.Provenance.EXTRACTED) continue;
18851
+ const parsed = (0, import_types64.parseFileId)(edge.source);
18725
18852
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
18726
18853
  const site = { relPath: edge.evidence.file };
18727
18854
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -18732,7 +18859,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
18732
18859
  function routeCallSiteFor(graph, targetNodeId) {
18733
18860
  if (!graph.hasNode(targetNodeId)) return void 0;
18734
18861
  const attrs = graph.getNodeAttributes(targetNodeId);
18735
- if (attrs.type !== import_types62.NodeType.RouteNode || !attrs.path) return void 0;
18862
+ if (attrs.type !== import_types64.NodeType.RouteNode || !attrs.path) return void 0;
18736
18863
  const site = { relPath: attrs.path };
18737
18864
  if (attrs.line !== void 0) site.line = attrs.line;
18738
18865
  return site;
@@ -19230,10 +19357,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
19230
19357
  // src/connectors/supabase/map.ts
19231
19358
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
19232
19359
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
19233
- function targetFromRestPath(path78) {
19234
- const rpcMatch = REST_RPC_PATH_RE.exec(path78);
19360
+ function targetFromRestPath(path80) {
19361
+ const rpcMatch = REST_RPC_PATH_RE.exec(path80);
19235
19362
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
19236
- const tableMatch = REST_TABLE_PATH_RE.exec(path78);
19363
+ const tableMatch = REST_TABLE_PATH_RE.exec(path80);
19237
19364
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
19238
19365
  return null;
19239
19366
  }
@@ -19281,7 +19408,7 @@ function tableNameFromQueryText(query) {
19281
19408
  if (SYSTEM_SCHEMA_PREFIXES.some((prefix) => lower.startsWith(prefix))) return null;
19282
19409
  return name;
19283
19410
  }
19284
- function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
19411
+ function diffPgStatStatementsToSignals(rows, previous, nowIso3) {
19285
19412
  const signals = [];
19286
19413
  const seen = /* @__PURE__ */ new Set();
19287
19414
  if (!Array.isArray(rows)) return signals;
@@ -19303,7 +19430,7 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
19303
19430
  targetName: table,
19304
19431
  callCount: delta,
19305
19432
  errorCount: 0,
19306
- lastObservedIso: nowIso2,
19433
+ lastObservedIso: nowIso3,
19307
19434
  ...columns.length > 0 ? { columns } : {}
19308
19435
  });
19309
19436
  }
@@ -19344,23 +19471,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
19344
19471
 
19345
19472
  // src/connectors/supabase/resolve.ts
19346
19473
  init_cjs_shims();
19347
- var import_types64 = require("@neat.is/types");
19474
+ var import_types66 = require("@neat.is/types");
19348
19475
  function createSupabaseResolveTarget(graph, config) {
19349
19476
  return (signal, _ctx) => {
19350
19477
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
19351
19478
  return null;
19352
19479
  }
19353
- const subResourceId = (0, import_types64.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19480
+ const subResourceId = (0, import_types66.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19354
19481
  if (graph.hasNode(subResourceId)) {
19355
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19482
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
19356
19483
  }
19357
- const bareResourceId = (0, import_types64.infraId)(signal.targetKind, signal.targetName);
19484
+ const bareResourceId = (0, import_types66.infraId)(signal.targetKind, signal.targetName);
19358
19485
  if (graph.hasNode(bareResourceId)) {
19359
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19486
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
19360
19487
  }
19361
- const projectLevelId = (0, import_types64.infraId)("supabase", config.nodeRef);
19488
+ const projectLevelId = (0, import_types66.infraId)("supabase", config.nodeRef);
19362
19489
  if (graph.hasNode(projectLevelId)) {
19363
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19490
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
19364
19491
  }
19365
19492
  return null;
19366
19493
  };
@@ -19453,7 +19580,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
19453
19580
 
19454
19581
  // src/connectors/railway/index.ts
19455
19582
  init_cjs_shims();
19456
- var import_types68 = require("@neat.is/types");
19583
+ var import_types70 = require("@neat.is/types");
19457
19584
 
19458
19585
  // src/connectors/railway/client.ts
19459
19586
  init_cjs_shims();
@@ -19604,7 +19731,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
19604
19731
  const out = [];
19605
19732
  graph.forEachNode((_id, attrs) => {
19606
19733
  const node = attrs;
19607
- if (node.type !== import_types68.NodeType.RouteNode) return;
19734
+ if (node.type !== import_types70.NodeType.RouteNode) return;
19608
19735
  const route = attrs;
19609
19736
  if (route.service !== serviceName) return;
19610
19737
  out.push({
@@ -19708,12 +19835,12 @@ function createRailwayResolveTarget(config) {
19708
19835
  const serviceName = config.serviceNameById[config.serviceId];
19709
19836
  if (!serviceName) return null;
19710
19837
  if (signal.targetKind === ROUTE_TARGET_KIND) {
19711
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types68.EdgeType.CALLS };
19838
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types70.EdgeType.CALLS };
19712
19839
  }
19713
19840
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
19714
19841
  const peerName = config.serviceNameById[signal.targetName];
19715
19842
  if (!peerName) return null;
19716
- return { targetNodeId: (0, import_types68.serviceId)(peerName), serviceName, edgeType: import_types68.EdgeType.CONNECTS_TO };
19843
+ return { targetNodeId: (0, import_types70.serviceId)(peerName), serviceName, edgeType: import_types70.EdgeType.CONNECTS_TO };
19717
19844
  }
19718
19845
  return null;
19719
19846
  };
@@ -19837,9 +19964,9 @@ function parseFirebaseTargetName(targetName) {
19837
19964
  const secondSep = rest.indexOf(FIELD_SEP);
19838
19965
  if (secondSep === -1) return null;
19839
19966
  const method = rest.slice(0, secondSep);
19840
- const path78 = rest.slice(secondSep + 1);
19841
- if (!resourceName || !method || !path78) return null;
19842
- return { resourceName, method, path: path78 };
19967
+ const path80 = rest.slice(secondSep + 1);
19968
+ if (!resourceName || !method || !path80) return null;
19969
+ return { resourceName, method, path: path80 };
19843
19970
  }
19844
19971
  function resourceNameFor(type, labels) {
19845
19972
  if (!labels) return null;
@@ -19877,14 +20004,14 @@ function mapLogEntryToSignal(entry2) {
19877
20004
  if (!req2) return null;
19878
20005
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
19879
20006
  const method = req2.requestMethod.toUpperCase();
19880
- const path78 = pathFromRequestUrl(req2.requestUrl);
19881
- if (path78 === null) return null;
20007
+ const path80 = pathFromRequestUrl(req2.requestUrl);
20008
+ if (path80 === null) return null;
19882
20009
  const timestamp = entry2.timestamp;
19883
20010
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
19884
20011
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD2;
19885
20012
  return {
19886
20013
  targetKind: resourceType,
19887
- targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
20014
+ targetName: packFirebaseTargetName({ resourceName, method, path: path80 }),
19888
20015
  callCount: 1,
19889
20016
  errorCount: isError ? 1 : 0,
19890
20017
  lastObservedIso: timestamp
@@ -19901,7 +20028,7 @@ function mapLogEntriesToSignals(entries) {
19901
20028
 
19902
20029
  // src/connectors/firebase/resolve.ts
19903
20030
  init_cjs_shims();
19904
- var import_types69 = require("@neat.is/types");
20031
+ var import_types71 = require("@neat.is/types");
19905
20032
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
19906
20033
  switch (resourceType) {
19907
20034
  case "cloud_function":
@@ -19916,7 +20043,7 @@ function routeEntriesFor(graph, serviceName) {
19916
20043
  const entries = [];
19917
20044
  graph.forEachNode((_id, attrs) => {
19918
20045
  const node = attrs;
19919
- if (node.type !== import_types69.NodeType.RouteNode) return;
20046
+ if (node.type !== import_types71.NodeType.RouteNode) return;
19920
20047
  const route = attrs;
19921
20048
  if (route.service !== serviceName) return;
19922
20049
  entries.push({
@@ -19948,7 +20075,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
19948
20075
  return {
19949
20076
  targetNodeId: match.routeNodeId,
19950
20077
  serviceName,
19951
- edgeType: import_types69.EdgeType.CALLS
20078
+ edgeType: import_types71.EdgeType.CALLS
19952
20079
  };
19953
20080
  };
19954
20081
  }
@@ -19975,7 +20102,7 @@ init_cjs_shims();
19975
20102
 
19976
20103
  // src/connectors/cloudflare/connector.ts
19977
20104
  init_cjs_shims();
19978
- var import_types71 = require("@neat.is/types");
20105
+ var import_types73 = require("@neat.is/types");
19979
20106
 
19980
20107
  // src/connectors/cloudflare/client.ts
19981
20108
  init_cjs_shims();
@@ -20091,7 +20218,7 @@ function mapEventToSignal(event) {
20091
20218
  if (Number.isNaN(observedAt.getTime())) return null;
20092
20219
  const statusCode = metadata?.statusCode;
20093
20220
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
20094
- const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20221
+ const path80 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20095
20222
  return {
20096
20223
  targetKind: CLOUDFLARE_TARGET_KIND,
20097
20224
  targetName: scriptName,
@@ -20099,7 +20226,7 @@ function mapEventToSignal(event) {
20099
20226
  errorCount: isError ? 1 : 0,
20100
20227
  lastObservedIso: observedAt.toISOString(),
20101
20228
  method,
20102
- ...path78 ? { path: path78 } : {},
20229
+ ...path80 ? { path: path80 } : {},
20103
20230
  ...typeof statusCode === "number" ? { statusCode } : {},
20104
20231
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
20105
20232
  };
@@ -20139,19 +20266,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
20139
20266
  graph.forEachNode((id, attrs) => {
20140
20267
  if (found) return;
20141
20268
  const a = attrs;
20142
- if (a.type === import_types71.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
20269
+ if (a.type === import_types73.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
20143
20270
  found = id;
20144
20271
  }
20145
20272
  });
20146
20273
  return found;
20147
20274
  }
20148
- function findMatchingRouteNode(graph, serviceName, method, path78) {
20149
- const normalizedPath = normalizePathTemplate(path78);
20275
+ function findMatchingRouteNode(graph, serviceName, method, path80) {
20276
+ const normalizedPath = normalizePathTemplate(path80);
20150
20277
  let found = null;
20151
20278
  graph.forEachNode((id, attrs) => {
20152
20279
  if (found) return;
20153
20280
  const a = attrs;
20154
- if (a.type !== import_types71.NodeType.RouteNode || a.service !== serviceName) return;
20281
+ if (a.type !== import_types73.NodeType.RouteNode || a.service !== serviceName) return;
20155
20282
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
20156
20283
  const routeMethod = (a.method ?? "").toUpperCase();
20157
20284
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -20163,18 +20290,18 @@ function createCloudflareResolveTarget(config, graph) {
20163
20290
  return (signal) => {
20164
20291
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
20165
20292
  const scriptName = signal.targetName;
20166
- const { method, path: path78 } = signal;
20293
+ const { method, path: path80 } = signal;
20167
20294
  const resolveRouteGrain = (serviceName, wholeFileId) => {
20168
- if (!method || !path78) return wholeFileId;
20169
- return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
20295
+ if (!method || !path80) return wholeFileId;
20296
+ return findMatchingRouteNode(graph, serviceName, method, path80) ?? wholeFileId;
20170
20297
  };
20171
20298
  const mapping = config.workers?.[scriptName];
20172
20299
  if (mapping) {
20173
- const wholeFileId = (0, import_types71.fileId)(mapping.service, mapping.entryFile);
20300
+ const wholeFileId = (0, import_types73.fileId)(mapping.service, mapping.entryFile);
20174
20301
  return {
20175
20302
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
20176
20303
  serviceName: mapping.service,
20177
- edgeType: import_types71.EdgeType.CALLS
20304
+ edgeType: import_types73.EdgeType.CALLS
20178
20305
  };
20179
20306
  }
20180
20307
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -20183,13 +20310,13 @@ function createCloudflareResolveTarget(config, graph) {
20183
20310
  return {
20184
20311
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
20185
20312
  serviceName: fileNode.service,
20186
- edgeType: import_types71.EdgeType.CALLS
20313
+ edgeType: import_types73.EdgeType.CALLS
20187
20314
  };
20188
20315
  }
20189
20316
  return {
20190
- targetNodeId: (0, import_types71.infraId)("cloudflare-worker", scriptName),
20317
+ targetNodeId: (0, import_types73.infraId)("cloudflare-worker", scriptName),
20191
20318
  serviceName: scriptName,
20192
- edgeType: import_types71.EdgeType.CALLS,
20319
+ edgeType: import_types73.EdgeType.CALLS,
20193
20320
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
20194
20321
  };
20195
20322
  };
@@ -20385,14 +20512,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
20385
20512
 
20386
20513
  // src/connectors/neon/resolve.ts
20387
20514
  init_cjs_shims();
20388
- var import_types75 = require("@neat.is/types");
20515
+ var import_types77 = require("@neat.is/types");
20389
20516
  function createNeonResolveTarget(config) {
20390
20517
  return (signal) => {
20391
20518
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
20392
20519
  return {
20393
- targetNodeId: (0, import_types75.infraId)("sql-table", signal.targetName),
20520
+ targetNodeId: (0, import_types77.infraId)("sql-table", signal.targetName),
20394
20521
  serviceName: config.serviceName,
20395
- edgeType: import_types75.EdgeType.CALLS,
20522
+ edgeType: import_types77.EdgeType.CALLS,
20396
20523
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
20397
20524
  };
20398
20525
  };
@@ -20518,9 +20645,9 @@ function parseCloudRunTargetName(targetName) {
20518
20645
  const secondSep = rest.indexOf(FIELD_SEP2);
20519
20646
  if (secondSep === -1) return null;
20520
20647
  const method = rest.slice(0, secondSep);
20521
- const path78 = rest.slice(secondSep + 1);
20522
- if (!serviceName || !method || !path78) return null;
20523
- return { serviceName, method, path: path78 };
20648
+ const path80 = rest.slice(secondSep + 1);
20649
+ if (!serviceName || !method || !path80) return null;
20650
+ return { serviceName, method, path: path80 };
20524
20651
  }
20525
20652
 
20526
20653
  // src/connectors/cloud-run/map.ts
@@ -20549,14 +20676,14 @@ function mapLogEntryToSignal2(entry2) {
20549
20676
  if (!req2) return null;
20550
20677
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
20551
20678
  const method = req2.requestMethod.toUpperCase();
20552
- const path78 = pathFromRequestUrl2(req2.requestUrl);
20553
- if (path78 === null) return null;
20679
+ const path80 = pathFromRequestUrl2(req2.requestUrl);
20680
+ if (path80 === null) return null;
20554
20681
  const timestamp = entry2.timestamp;
20555
20682
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
20556
20683
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD4;
20557
20684
  return {
20558
20685
  targetKind: CLOUD_RUN_TARGET_KIND,
20559
- targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
20686
+ targetName: packCloudRunTargetName({ serviceName, method, path: path80 }),
20560
20687
  callCount: 1,
20561
20688
  errorCount: isError ? 1 : 0,
20562
20689
  lastObservedIso: timestamp
@@ -20573,14 +20700,14 @@ function mapLogEntriesToSignals2(entries) {
20573
20700
 
20574
20701
  // src/connectors/cloud-run/resolve.ts
20575
20702
  init_cjs_shims();
20576
- var import_types79 = require("@neat.is/types");
20703
+ var import_types81 = require("@neat.is/types");
20577
20704
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
20578
20705
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
20579
20706
  let found = null;
20580
20707
  graph.forEachNode((_id, attrs) => {
20581
20708
  if (found) return;
20582
20709
  const node = attrs;
20583
- if (node.type !== import_types79.NodeType.RouteNode) return;
20710
+ if (node.type !== import_types81.NodeType.RouteNode) return;
20584
20711
  const route = attrs;
20585
20712
  if (route.service !== serviceName || !route.pathTemplate) return;
20586
20713
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20595,23 +20722,23 @@ function createCloudRunResolveTarget(graph, config) {
20595
20722
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
20596
20723
  const identity = parseCloudRunTargetName(signal.targetName);
20597
20724
  if (!identity) return null;
20598
- const { serviceName: gcpServiceName, method, path: path78 } = identity;
20725
+ const { serviceName: gcpServiceName, method, path: path80 } = identity;
20599
20726
  const mappedService = config.serviceMap?.[gcpServiceName];
20600
20727
  if (mappedService) {
20601
20728
  const routeNodeId = findMatchingRouteNode2(
20602
20729
  graph,
20603
20730
  mappedService,
20604
20731
  method,
20605
- normalizePathTemplate(path78)
20732
+ normalizePathTemplate(path80)
20606
20733
  );
20607
20734
  if (routeNodeId) {
20608
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types79.EdgeType.CALLS };
20735
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types81.EdgeType.CALLS };
20609
20736
  }
20610
20737
  }
20611
20738
  return {
20612
- targetNodeId: (0, import_types79.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20739
+ targetNodeId: (0, import_types81.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20613
20740
  serviceName: mappedService ?? gcpServiceName,
20614
- edgeType: import_types79.EdgeType.CALLS,
20741
+ edgeType: import_types81.EdgeType.CALLS,
20615
20742
  ensureInfraNode: {
20616
20743
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
20617
20744
  name: gcpServiceName,
@@ -20738,9 +20865,9 @@ function parseGcpLbTargetName(targetName) {
20738
20865
  const secondSep = rest.indexOf(FIELD_SEP3);
20739
20866
  if (secondSep === -1) return null;
20740
20867
  const method = rest.slice(0, secondSep);
20741
- const path78 = rest.slice(secondSep + 1);
20742
- if (!backendServiceName || !method || !path78) return null;
20743
- return { backendServiceName, method, path: path78 };
20868
+ const path80 = rest.slice(secondSep + 1);
20869
+ if (!backendServiceName || !method || !path80) return null;
20870
+ return { backendServiceName, method, path: path80 };
20744
20871
  }
20745
20872
 
20746
20873
  // src/connectors/gcp-lb/map.ts
@@ -20769,14 +20896,14 @@ function mapLogEntryToSignal3(entry2) {
20769
20896
  if (!req2) return null;
20770
20897
  if (typeof req2.requestMethod !== "string" || req2.requestMethod.length === 0) return null;
20771
20898
  const method = req2.requestMethod.toUpperCase();
20772
- const path78 = pathFromRequestUrl3(req2.requestUrl);
20773
- if (path78 === null) return null;
20899
+ const path80 = pathFromRequestUrl3(req2.requestUrl);
20900
+ if (path80 === null) return null;
20774
20901
  const timestamp = entry2.timestamp;
20775
20902
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
20776
20903
  const isError = typeof req2.status === "number" && req2.status >= ERROR_STATUS_THRESHOLD5;
20777
20904
  return {
20778
20905
  targetKind: GCP_LB_TARGET_KIND,
20779
- targetName: packGcpLbTargetName({ backendServiceName, method, path: path78 }),
20906
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path80 }),
20780
20907
  callCount: 1,
20781
20908
  errorCount: isError ? 1 : 0,
20782
20909
  lastObservedIso: timestamp
@@ -20793,14 +20920,14 @@ function mapLogEntriesToSignals3(entries) {
20793
20920
 
20794
20921
  // src/connectors/gcp-lb/resolve.ts
20795
20922
  init_cjs_shims();
20796
- var import_types83 = require("@neat.is/types");
20923
+ var import_types85 = require("@neat.is/types");
20797
20924
  var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
20798
20925
  function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
20799
20926
  let found = null;
20800
20927
  graph.forEachNode((_id, attrs) => {
20801
20928
  if (found) return;
20802
20929
  const node = attrs;
20803
- if (node.type !== import_types83.NodeType.RouteNode) return;
20930
+ if (node.type !== import_types85.NodeType.RouteNode) return;
20804
20931
  const route = attrs;
20805
20932
  if (route.service !== serviceName || !route.pathTemplate) return;
20806
20933
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20815,23 +20942,23 @@ function createGcpLbResolveTarget(graph, config) {
20815
20942
  if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
20816
20943
  const identity = parseGcpLbTargetName(signal.targetName);
20817
20944
  if (!identity) return null;
20818
- const { backendServiceName, method, path: path78 } = identity;
20945
+ const { backendServiceName, method, path: path80 } = identity;
20819
20946
  const mappedService = config.backendServiceMap?.[backendServiceName];
20820
20947
  if (mappedService) {
20821
20948
  const routeNodeId = findMatchingRouteNode3(
20822
20949
  graph,
20823
20950
  mappedService,
20824
20951
  method,
20825
- normalizePathTemplate(path78)
20952
+ normalizePathTemplate(path80)
20826
20953
  );
20827
20954
  if (routeNodeId) {
20828
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
20955
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types85.EdgeType.CALLS };
20829
20956
  }
20830
20957
  }
20831
20958
  return {
20832
- targetNodeId: (0, import_types83.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20959
+ targetNodeId: (0, import_types85.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20833
20960
  serviceName: mappedService ?? backendServiceName,
20834
- edgeType: import_types83.EdgeType.CALLS,
20961
+ edgeType: import_types85.EdgeType.CALLS,
20835
20962
  ensureInfraNode: {
20836
20963
  kind: GCP_LB_BACKEND_INFRA_KIND,
20837
20964
  name: backendServiceName,
@@ -20872,7 +20999,7 @@ function createGcpLbConnector(graph, config = {}) {
20872
20999
 
20873
21000
  // src/connectors/render/index.ts
20874
21001
  init_cjs_shims();
20875
- var import_types86 = require("@neat.is/types");
21002
+ var import_types88 = require("@neat.is/types");
20876
21003
 
20877
21004
  // src/connectors/render/types.ts
20878
21005
  init_cjs_shims();
@@ -20950,7 +21077,7 @@ function buildRenderRouteIndex(graph, serviceName) {
20950
21077
  const out = [];
20951
21078
  graph.forEachNode((_id, attrs) => {
20952
21079
  const node = attrs;
20953
- if (node.type !== import_types86.NodeType.RouteNode) return;
21080
+ if (node.type !== import_types88.NodeType.RouteNode) return;
20954
21081
  const route = attrs;
20955
21082
  if (route.service !== serviceName) return;
20956
21083
  out.push({
@@ -21035,7 +21162,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
21035
21162
  function createRenderResolveTarget(config) {
21036
21163
  return (signal) => {
21037
21164
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
21038
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types86.EdgeType.CALLS };
21165
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types88.EdgeType.CALLS };
21039
21166
  }
21040
21167
  return null;
21041
21168
  };
@@ -21173,21 +21300,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
21173
21300
 
21174
21301
  // src/connectors/planetscale/resolve.ts
21175
21302
  init_cjs_shims();
21176
- var import_types90 = require("@neat.is/types");
21303
+ var import_types92 = require("@neat.is/types");
21177
21304
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
21178
21305
  function createPlanetscaleResolveTarget(graph, config) {
21179
21306
  const databaseName = `${config.organization}/${config.database}`;
21180
21307
  return (signal, _ctx) => {
21181
21308
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
21182
- const tableId = (0, import_types90.infraId)("sql-table", signal.targetName);
21309
+ const tableId = (0, import_types92.infraId)("sql-table", signal.targetName);
21183
21310
  if (graph.hasNode(tableId)) {
21184
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
21311
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types92.EdgeType.CALLS };
21185
21312
  }
21186
- const providerId = (0, import_types90.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21313
+ const providerId = (0, import_types92.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21187
21314
  return {
21188
21315
  targetNodeId: providerId,
21189
21316
  serviceName: config.serviceName,
21190
- edgeType: import_types90.EdgeType.CALLS,
21317
+ edgeType: import_types92.EdgeType.CALLS,
21191
21318
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
21192
21319
  };
21193
21320
  };
@@ -21452,7 +21579,7 @@ function mapBuildsToSignals(builds, serviceName) {
21452
21579
 
21453
21580
  // src/connectors/eas/resolve.ts
21454
21581
  init_cjs_shims();
21455
- var import_types95 = require("@neat.is/types");
21582
+ var import_types97 = require("@neat.is/types");
21456
21583
  var NO_ENV2 = "unknown";
21457
21584
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
21458
21585
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -21468,8 +21595,8 @@ function configBasenamesForPhase(phase) {
21468
21595
  function configNodeService(graph, configNodeId) {
21469
21596
  for (const edgeId of graph.inboundEdges(configNodeId)) {
21470
21597
  const edge = graph.getEdgeAttributes(edgeId);
21471
- if (edge.type !== import_types95.EdgeType.CONFIGURED_BY) continue;
21472
- const parsed = (0, import_types95.parseFileId)(edge.source);
21598
+ if (edge.type !== import_types97.EdgeType.CONFIGURED_BY) continue;
21599
+ const parsed = (0, import_types97.parseFileId)(edge.source);
21473
21600
  if (parsed) return parsed.service;
21474
21601
  }
21475
21602
  return null;
@@ -21480,7 +21607,7 @@ function findConfigNode(graph, basenames, serviceName) {
21480
21607
  graph.forEachNode((id, attrs) => {
21481
21608
  if (scoped) return;
21482
21609
  const node = attrs;
21483
- if (node.type !== import_types95.NodeType.ConfigNode) return;
21610
+ if (node.type !== import_types97.NodeType.ConfigNode) return;
21484
21611
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
21485
21612
  if (anyMatch === null) anyMatch = id;
21486
21613
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -21497,13 +21624,13 @@ function createEasResolveTarget(graph) {
21497
21624
  if (basenames.length > 0) {
21498
21625
  const configNodeId = findConfigNode(graph, basenames, serviceName);
21499
21626
  if (configNodeId) {
21500
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types95.EdgeType.CALLS };
21627
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types97.EdgeType.CALLS };
21501
21628
  }
21502
21629
  }
21503
21630
  return {
21504
21631
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
21505
21632
  serviceName,
21506
- edgeType: import_types95.EdgeType.CALLS
21633
+ edgeType: import_types97.EdgeType.CALLS
21507
21634
  };
21508
21635
  };
21509
21636
  }
@@ -22120,11 +22247,11 @@ function serializeGraph(graph) {
22120
22247
  }
22121
22248
  function projectFromReq(req2, singleProject) {
22122
22249
  const params = req2.params;
22123
- const named = params.project;
22250
+ const named2 = params.project;
22124
22251
  if (singleProject) {
22125
- return named === void 0 || named === DEFAULT_PROJECT ? singleProject : named;
22252
+ return named2 === void 0 || named2 === DEFAULT_PROJECT ? singleProject : named2;
22126
22253
  }
22127
- return named ?? DEFAULT_PROJECT;
22254
+ return named2 ?? DEFAULT_PROJECT;
22128
22255
  }
22129
22256
  function resolveProject(registry, req2, reply, bootstrap, singleProject) {
22130
22257
  const name = projectFromReq(req2, singleProject);
@@ -22265,11 +22392,11 @@ function registerRoutes(scope, ctx) {
22265
22392
  const candidates = req2.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
22266
22393
  const parsed = [];
22267
22394
  for (const c of candidates) {
22268
- const r = import_types98.DivergenceTypeSchema.safeParse(c);
22395
+ const r = import_types100.DivergenceTypeSchema.safeParse(c);
22269
22396
  if (!r.success) {
22270
22397
  return reply.code(400).send({
22271
22398
  error: `unknown divergence type "${c}"`,
22272
- allowed: import_types98.DivergenceTypeSchema.options
22399
+ allowed: import_types100.DivergenceTypeSchema.options
22273
22400
  });
22274
22401
  }
22275
22402
  parsed.push(r.data);
@@ -22665,7 +22792,7 @@ function registerRoutes(scope, ctx) {
22665
22792
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
22666
22793
  let violations = await log.readAll();
22667
22794
  if (req2.query.severity) {
22668
- const sev = import_types98.PolicySeveritySchema.safeParse(req2.query.severity);
22795
+ const sev = import_types100.PolicySeveritySchema.safeParse(req2.query.severity);
22669
22796
  if (!sev.success) {
22670
22797
  return reply.code(400).send({
22671
22798
  error: "invalid severity",
@@ -22704,7 +22831,7 @@ function registerRoutes(scope, ctx) {
22704
22831
  scope.post("/policies/check", async (req2, reply) => {
22705
22832
  const proj = resolveProject(registry, req2, reply, ctx.bootstrap, ctx.singleProject);
22706
22833
  if (!proj) return;
22707
- const parsed = import_types98.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
22834
+ const parsed = import_types100.PoliciesCheckBodySchema.safeParse(req2.body ?? {});
22708
22835
  if (!parsed.success) {
22709
22836
  return reply.code(400).send({
22710
22837
  error: "invalid /policies/check body",
@@ -23021,12 +23148,454 @@ async function buildApi(opts) {
23021
23148
 
23022
23149
  // src/daemon.ts
23023
23150
  init_otel();
23151
+
23152
+ // src/connectors/kubernetes/index.ts
23153
+ init_cjs_shims();
23154
+
23155
+ // src/connectors/kubernetes/client.ts
23156
+ init_cjs_shims();
23157
+ var import_node_https = require("https");
23158
+ function deploymentsPath(namespace) {
23159
+ return `/apis/apps/v1/namespaces/${namespace}/deployments`;
23160
+ }
23161
+ function podsPath(namespace) {
23162
+ return `/api/v1/namespaces/${namespace}/pods`;
23163
+ }
23164
+ function makeK8sFetchImpl(transport) {
23165
+ const agent = new import_node_https.Agent({
23166
+ ...transport.ca ? { ca: transport.ca } : {},
23167
+ ...transport.clientCert ? { cert: transport.clientCert } : {},
23168
+ ...transport.clientKey ? { key: transport.clientKey } : {},
23169
+ rejectUnauthorized: !transport.insecureSkipTlsVerify
23170
+ });
23171
+ return ((url, init) => new Promise((resolve, reject) => {
23172
+ const u = new URL(String(url));
23173
+ const req2 = (0, import_node_https.request)(
23174
+ u,
23175
+ {
23176
+ method: (init?.method ?? "GET").toUpperCase(),
23177
+ headers: init?.headers ?? {},
23178
+ agent
23179
+ },
23180
+ (res) => {
23181
+ const chunks = [];
23182
+ res.on("data", (c) => chunks.push(c));
23183
+ res.on("end", () => {
23184
+ const body = Buffer.concat(chunks).toString("utf8");
23185
+ const status2 = res.statusCode ?? 0;
23186
+ resolve({
23187
+ ok: status2 >= 200 && status2 < 300,
23188
+ status: status2,
23189
+ statusText: res.statusMessage ?? "",
23190
+ json: async () => JSON.parse(body),
23191
+ text: async () => body
23192
+ });
23193
+ });
23194
+ }
23195
+ );
23196
+ req2.on("error", reject);
23197
+ const signal = init?.signal;
23198
+ if (signal) {
23199
+ if (signal.aborted) req2.destroy(new Error("aborted"));
23200
+ else signal.addEventListener("abort", () => req2.destroy(new Error("aborted")), { once: true });
23201
+ }
23202
+ req2.end();
23203
+ }));
23204
+ }
23205
+ async function listResource(transport, namespace, path80, opts = {}) {
23206
+ const base = opts.apiUrl ?? transport.server;
23207
+ const url = `${base.replace(/\/$/, "")}${path80}`;
23208
+ const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
23209
+ const res = await junctionFetch(
23210
+ url,
23211
+ { method: "GET", headers: { ...transport.token ? bearerAuthHeader(transport.token) : {}, Accept: "application/json" } },
23212
+ // accountKey: the (cluster, namespace) pair — an identifier, safe to log, the
23213
+ // rate-limit bucket for one namespace on one cluster (ADR-131).
23214
+ { provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
23215
+ );
23216
+ if (!res.ok) {
23217
+ throw new Error(`kubernetes ${path80} failed: ${res.status} ${res.statusText}`);
23218
+ }
23219
+ const json = await res.json();
23220
+ return Array.isArray(json.items) ? json.items : [];
23221
+ }
23222
+ function safeHost(server) {
23223
+ try {
23224
+ return new URL(server).host;
23225
+ } catch {
23226
+ return "cluster";
23227
+ }
23228
+ }
23229
+ async function fetchDeployments(transport, namespace, opts = {}) {
23230
+ return listResource(transport, namespace, deploymentsPath(namespace), opts);
23231
+ }
23232
+ async function fetchPods(transport, namespace, opts = {}) {
23233
+ return listResource(transport, namespace, podsPath(namespace), opts);
23234
+ }
23235
+
23236
+ // src/connectors/kubernetes/kubeconfig.ts
23237
+ init_cjs_shims();
23238
+ var import_node_fs39 = require("fs");
23239
+ var import_yaml4 = require("yaml");
23240
+ function pemFrom(dataField, pathField) {
23241
+ if (typeof dataField === "string" && dataField.length > 0) {
23242
+ return Buffer.from(dataField, "base64").toString("utf8");
23243
+ }
23244
+ if (typeof pathField === "string" && pathField.length > 0) {
23245
+ return (0, import_node_fs39.readFileSync)(pathField, "utf8");
23246
+ }
23247
+ return void 0;
23248
+ }
23249
+ function named(list, name) {
23250
+ if (!Array.isArray(list)) return void 0;
23251
+ const hit = list.find((e) => e && e.name === name);
23252
+ return hit;
23253
+ }
23254
+ function parseKubeconfig(kubeconfig) {
23255
+ const looksInline = /\n/.test(kubeconfig) || /(^|\s)clusters\s*:/.test(kubeconfig);
23256
+ const text = looksInline ? kubeconfig : (0, import_node_fs39.readFileSync)(kubeconfig, "utf8");
23257
+ let doc;
23258
+ try {
23259
+ doc = (0, import_yaml4.parse)(text);
23260
+ } catch {
23261
+ throw new Error("kubernetes connector: kubeconfig is not valid YAML");
23262
+ }
23263
+ if (!doc || typeof doc !== "object") {
23264
+ throw new Error("kubernetes connector: kubeconfig is empty or malformed");
23265
+ }
23266
+ const currentContext = doc["current-context"];
23267
+ if (typeof currentContext !== "string" || currentContext.length === 0) {
23268
+ throw new Error("kubernetes connector: kubeconfig has no current-context");
23269
+ }
23270
+ const ctxEntry = named(doc["contexts"], currentContext);
23271
+ const ctx = ctxEntry?.context;
23272
+ if (!ctx) {
23273
+ throw new Error(`kubernetes connector: kubeconfig context "${currentContext}" not found`);
23274
+ }
23275
+ const clusterEntry = named(doc["clusters"], String(ctx["cluster"] ?? ""));
23276
+ const cluster = clusterEntry?.cluster;
23277
+ if (!cluster || typeof cluster["server"] !== "string") {
23278
+ throw new Error("kubernetes connector: kubeconfig current context has no cluster server");
23279
+ }
23280
+ const userEntry = named(doc["users"], String(ctx["user"] ?? ""));
23281
+ const user = userEntry?.user ?? {};
23282
+ const transport = {
23283
+ server: cluster["server"],
23284
+ insecureSkipTlsVerify: cluster["insecure-skip-tls-verify"] === true
23285
+ };
23286
+ const ca = pemFrom(cluster["certificate-authority-data"], cluster["certificate-authority"]);
23287
+ if (ca) transport.ca = ca;
23288
+ if (typeof user["token"] === "string" && user["token"].length > 0) transport.token = user["token"];
23289
+ const clientCert = pemFrom(user["client-certificate-data"], user["client-certificate"]);
23290
+ const clientKey = pemFrom(user["client-key-data"], user["client-key"]);
23291
+ if (clientCert) transport.clientCert = clientCert;
23292
+ if (clientKey) transport.clientKey = clientKey;
23293
+ return transport;
23294
+ }
23295
+ function resolveK8sTransport(creds, config) {
23296
+ if (creds.kubeconfig) return parseKubeconfig(creds.kubeconfig);
23297
+ if (!config.apiServerUrl) {
23298
+ throw new Error("kubernetes connector: options.apiServerUrl is required with a token credential");
23299
+ }
23300
+ const transport = {
23301
+ server: config.apiServerUrl,
23302
+ insecureSkipTlsVerify: config.insecureSkipTlsVerify === true
23303
+ };
23304
+ if (creds.token) transport.token = creds.token;
23305
+ if (config.caCert) transport.ca = config.caCert;
23306
+ return transport;
23307
+ }
23308
+
23309
+ // src/connectors/kubernetes/map.ts
23310
+ init_cjs_shims();
23311
+
23312
+ // src/connectors/kubernetes/types.ts
23313
+ init_cjs_shims();
23314
+ function readK8sCredentials(raw) {
23315
+ const token = typeof raw["token"] === "string" && raw["token"].length > 0 ? raw["token"] : void 0;
23316
+ const kubeconfig = typeof raw["kubeconfig"] === "string" && raw["kubeconfig"].length > 0 ? raw["kubeconfig"] : void 0;
23317
+ if (!token && !kubeconfig) {
23318
+ throw new Error("kubernetes connector: credentials must carry a token or a kubeconfig");
23319
+ }
23320
+ const out = {};
23321
+ if (token) out.token = token;
23322
+ if (kubeconfig) out.kubeconfig = kubeconfig;
23323
+ return out;
23324
+ }
23325
+ var IMAGE_PULL_REASONS = /* @__PURE__ */ new Set(["ImagePullBackOff", "ErrImagePull", "InvalidImageName"]);
23326
+ var CRASH_LOOP_REASON = "CrashLoopBackOff";
23327
+ var FIELD_SEP5 = "\0";
23328
+ var K8S_TARGET_KIND = "k8s-workload";
23329
+ function packK8sTargetName(identity) {
23330
+ return [identity.serviceName, identity.fault].join(FIELD_SEP5);
23331
+ }
23332
+ function parseK8sTargetName(targetName) {
23333
+ const sep = targetName.indexOf(FIELD_SEP5);
23334
+ if (sep === -1) return null;
23335
+ const serviceName = targetName.slice(0, sep);
23336
+ const fault = targetName.slice(sep + 1);
23337
+ if (!serviceName || !fault) return null;
23338
+ return { serviceName, fault };
23339
+ }
23340
+
23341
+ // src/connectors/kubernetes/map.ts
23342
+ function serviceNameFor(deployment, config) {
23343
+ const name = deployment.metadata?.name ?? "";
23344
+ return config.serviceMap?.[name] ?? name;
23345
+ }
23346
+ function podMatchesSelector(pod, selector) {
23347
+ if (!selector || Object.keys(selector).length === 0) return false;
23348
+ const labels = pod.metadata?.labels ?? {};
23349
+ return Object.entries(selector).every(([k, v]) => labels[k] === v);
23350
+ }
23351
+ function podsForDeployment(deployment, pods) {
23352
+ const selector = deployment.spec?.selector?.matchLabels;
23353
+ return pods.filter((p) => podMatchesSelector(p, selector));
23354
+ }
23355
+ function nowIso2() {
23356
+ return (/* @__PURE__ */ new Date()).toISOString();
23357
+ }
23358
+ function podLevelFault(deployment, pods) {
23359
+ const name = deployment.metadata?.name ?? "";
23360
+ const owned = podsForDeployment(deployment, pods);
23361
+ let crash = null;
23362
+ for (const pod of owned) {
23363
+ for (const cs of pod.status?.containerStatuses ?? []) {
23364
+ const reason = cs.state?.waiting?.reason;
23365
+ if (typeof reason !== "string") continue;
23366
+ if (IMAGE_PULL_REASONS.has(reason)) {
23367
+ const image = typeof cs.image === "string" ? cs.image : "unknown image";
23368
+ const attrs = { "k8s.image": image, "k8s.waitingReason": reason };
23369
+ const wm = cs.state?.waiting?.message;
23370
+ if (typeof wm === "string" && wm.length > 0) attrs["k8s.waitingMessage"] = wm;
23371
+ return {
23372
+ fault: "image-pull",
23373
+ message: `Deployment ${name} cannot pull image ${image} (${reason})`,
23374
+ timestamp: pod.status?.startTime ?? nowIso2(),
23375
+ attributes: attrs
23376
+ };
23377
+ }
23378
+ if (reason === CRASH_LOOP_REASON && !crash) crash = { cs, pod };
23379
+ }
23380
+ }
23381
+ if (crash) {
23382
+ const { cs, pod } = crash;
23383
+ const term = cs.lastState?.terminated;
23384
+ const termReason = typeof term?.reason === "string" ? term.reason : void 0;
23385
+ const termMsg = typeof term?.message === "string" ? term.message.trim() : void 0;
23386
+ const restarts = typeof cs.restartCount === "number" ? cs.restartCount : 0;
23387
+ const detail = termReason ? `last terminated: ${termReason}${termMsg ? ` \u2014 ${termMsg}` : ""}${typeof term?.exitCode === "number" ? ` (exit ${term.exitCode})` : ""}` : "no last-termination detail reported";
23388
+ const attrs = { "k8s.waitingReason": CRASH_LOOP_REASON, "k8s.restartCount": restarts };
23389
+ if (termReason) attrs["k8s.terminatedReason"] = termReason;
23390
+ if (termMsg) attrs["k8s.terminatedMessage"] = termMsg;
23391
+ if (typeof term?.exitCode === "number") attrs["k8s.exitCode"] = term.exitCode;
23392
+ if (typeof cs.image === "string") attrs["k8s.image"] = cs.image;
23393
+ return {
23394
+ fault: "crash-loop",
23395
+ message: `Deployment ${name} is crashlooping (restarts: ${restarts}); ${detail}`,
23396
+ timestamp: term?.finishedAt ?? pod.status?.startTime ?? nowIso2(),
23397
+ attributes: attrs
23398
+ };
23399
+ }
23400
+ return null;
23401
+ }
23402
+ function classifyDeployment(deployment, pods, expectedZero) {
23403
+ const name = deployment.metadata?.name ?? "";
23404
+ const desired = typeof deployment.spec?.replicas === "number" ? deployment.spec.replicas : 1;
23405
+ const ready = typeof deployment.status?.readyReplicas === "number" ? deployment.status.readyReplicas : 0;
23406
+ if (desired === 0) {
23407
+ if (expectedZero?.has(name)) return null;
23408
+ return {
23409
+ fault: "scaled-to-zero",
23410
+ message: `Deployment ${name} is scaled to 0 \u2014 no running pods (desired 0)`,
23411
+ timestamp: nowIso2(),
23412
+ attributes: { "k8s.desiredReplicas": 0, "k8s.readyReplicas": ready }
23413
+ };
23414
+ }
23415
+ if (ready >= desired) return null;
23416
+ const podFault = podLevelFault(deployment, pods);
23417
+ if (podFault) {
23418
+ podFault.attributes["k8s.desiredReplicas"] = desired;
23419
+ podFault.attributes["k8s.readyReplicas"] = ready;
23420
+ return podFault;
23421
+ }
23422
+ return {
23423
+ fault: "no-ready-replicas",
23424
+ message: `Deployment ${name} has no ready replicas (desired ${desired}, ready ${ready})`,
23425
+ timestamp: nowIso2(),
23426
+ attributes: { "k8s.desiredReplicas": desired, "k8s.readyReplicas": ready }
23427
+ };
23428
+ }
23429
+ function mapDeploymentToSignal(deployment, pods, config) {
23430
+ const name = deployment.metadata?.name;
23431
+ if (typeof name !== "string" || name.length === 0) return null;
23432
+ const expectedZero = config.expectedZero ? new Set(config.expectedZero) : void 0;
23433
+ const finding = classifyDeployment(deployment, pods, expectedZero);
23434
+ if (!finding) return null;
23435
+ const serviceName = serviceNameFor(deployment, config);
23436
+ const namespace = deployment.metadata?.namespace ?? config.namespace;
23437
+ const attributes = {
23438
+ "k8s.namespace": namespace,
23439
+ "k8s.deployment": name,
23440
+ "k8s.fault": finding.fault,
23441
+ ...finding.attributes
23442
+ };
23443
+ return {
23444
+ targetKind: K8S_TARGET_KIND,
23445
+ targetName: packK8sTargetName({ serviceName, fault: finding.fault }),
23446
+ // Incident-only — no edge, so no call/error count to replay.
23447
+ callCount: 0,
23448
+ errorCount: 0,
23449
+ lastObservedIso: finding.timestamp,
23450
+ incident: {
23451
+ id: `k8s:deploy:${namespace}:${name}:${finding.fault}`,
23452
+ timestamp: finding.timestamp,
23453
+ service: serviceName,
23454
+ errorType: "k8s-deploy-failure",
23455
+ errorMessage: finding.message,
23456
+ attributes
23457
+ }
23458
+ };
23459
+ }
23460
+ function mapWorkloadsToSignals(deployments, pods, config) {
23461
+ const out = [];
23462
+ for (const deployment of deployments) {
23463
+ const signal = mapDeploymentToSignal(deployment, pods, config);
23464
+ if (signal) out.push(signal);
23465
+ }
23466
+ return out;
23467
+ }
23468
+
23469
+ // src/connectors/kubernetes/resolve.ts
23470
+ init_cjs_shims();
23471
+ var import_types102 = require("@neat.is/types");
23472
+ var NO_ENV3 = "unknown";
23473
+ function createK8sResolveTarget(graph) {
23474
+ return (signal) => {
23475
+ if (signal.targetKind !== K8S_TARGET_KIND) return null;
23476
+ const identity = parseK8sTargetName(signal.targetName);
23477
+ if (!identity) return null;
23478
+ const { serviceName } = identity;
23479
+ return {
23480
+ targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV3),
23481
+ serviceName,
23482
+ edgeType: import_types102.EdgeType.CALLS
23483
+ };
23484
+ };
23485
+ }
23486
+
23487
+ // src/connectors/kubernetes/substrate.ts
23488
+ init_cjs_shims();
23489
+ var import_promises = require("fs/promises");
23490
+ var import_node_os5 = __toESM(require("os"), 1);
23491
+ var import_node_path75 = __toESM(require("path"), 1);
23492
+ function defaultHome() {
23493
+ const override = process.env.NEAT_HOME;
23494
+ if (override && override.length > 0) return import_node_path75.default.resolve(override);
23495
+ return import_node_path75.default.join(import_node_os5.default.homedir(), ".neat");
23496
+ }
23497
+ function k8sSubstrateConfigPath(home = defaultHome()) {
23498
+ return import_node_path75.default.join(home, "k8s.json");
23499
+ }
23500
+ async function readK8sSubstrateConfig(home = defaultHome()) {
23501
+ let raw;
23502
+ try {
23503
+ raw = await (0, import_promises.readFile)(k8sSubstrateConfigPath(home), "utf8");
23504
+ } catch {
23505
+ return { version: 1, deployments: [] };
23506
+ }
23507
+ try {
23508
+ const parsed = JSON.parse(raw);
23509
+ if (!parsed || !Array.isArray(parsed.deployments)) return { version: 1, deployments: [] };
23510
+ return parsed;
23511
+ } catch {
23512
+ return { version: 1, deployments: [] };
23513
+ }
23514
+ }
23515
+ async function startK8sSubstratePolling(input) {
23516
+ const config = await readK8sSubstrateConfig(input.home);
23517
+ const env = input.env ?? process.env;
23518
+ const stops = [];
23519
+ for (const entry2 of config.deployments) {
23520
+ if (entry2.project !== void 0 && entry2.project !== input.project) continue;
23521
+ if (typeof entry2.namespace !== "string" || entry2.namespace.length === 0) {
23522
+ input.onSkip?.(entry2, "missing namespace");
23523
+ continue;
23524
+ }
23525
+ let credentials;
23526
+ try {
23527
+ const resolved = resolveCredential(entry2.credential, env);
23528
+ credentials = resolved.kind === "fields" ? { ...resolved.fields } : { token: resolved.value };
23529
+ } catch (err) {
23530
+ input.onSkip?.(entry2, err.message);
23531
+ continue;
23532
+ }
23533
+ const cfg = {
23534
+ namespace: entry2.namespace,
23535
+ ...entry2.apiServerUrl ? { apiServerUrl: entry2.apiServerUrl } : {},
23536
+ ...entry2.caCert ? { caCert: entry2.caCert } : {},
23537
+ ...entry2.insecureSkipTlsVerify ? { insecureSkipTlsVerify: true } : {},
23538
+ ...entry2.serviceMap ? { serviceMap: entry2.serviceMap } : {},
23539
+ ...entry2.expectedZero ? { expectedZero: entry2.expectedZero } : {}
23540
+ };
23541
+ const { connector, resolveTarget } = createKubernetesConnector(input.graph, cfg, input.fetchImpl);
23542
+ const stop = startConnectorPollLoop(
23543
+ connector,
23544
+ {
23545
+ projectDir: input.projectDir,
23546
+ project: input.project,
23547
+ credentials,
23548
+ ...input.errorsPath ? { errorsPath: input.errorsPath } : {}
23549
+ },
23550
+ input.graph,
23551
+ resolveTarget,
23552
+ { connectorId: `k8s:${entry2.id}`, ...entry2.intervalMs ? { intervalMs: entry2.intervalMs } : {} }
23553
+ );
23554
+ stops.push(stop);
23555
+ }
23556
+ return () => {
23557
+ for (const stop of stops) stop();
23558
+ };
23559
+ }
23560
+
23561
+ // src/connectors/kubernetes/index.ts
23562
+ var KubernetesConnector = class {
23563
+ constructor(config, fetchImpl) {
23564
+ this.config = config;
23565
+ this.fetchImpl = fetchImpl;
23566
+ }
23567
+ config;
23568
+ fetchImpl;
23569
+ provider = "kubernetes";
23570
+ async poll(ctx) {
23571
+ const creds = readK8sCredentials(ctx.credentials);
23572
+ const transport = resolveK8sTransport(creds, this.config);
23573
+ const namespace = this.config.namespace;
23574
+ const opts = {
23575
+ ...this.config.apiUrl ? { apiUrl: this.config.apiUrl } : {},
23576
+ ...this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}
23577
+ };
23578
+ const [deployments, pods] = await Promise.all([
23579
+ fetchDeployments(transport, namespace, opts),
23580
+ fetchPods(transport, namespace, opts)
23581
+ ]);
23582
+ return mapWorkloadsToSignals(deployments, pods, this.config);
23583
+ }
23584
+ };
23585
+ function createKubernetesConnector(graph, config, fetchImpl) {
23586
+ return {
23587
+ connector: new KubernetesConnector(config, fetchImpl),
23588
+ resolveTarget: createK8sResolveTarget(graph)
23589
+ };
23590
+ }
23591
+
23592
+ // src/daemon.ts
23024
23593
  init_auth();
23025
23594
 
23026
23595
  // src/unrouted.ts
23027
23596
  init_cjs_shims();
23028
- var import_node_fs39 = require("fs");
23029
- var import_node_path74 = __toESM(require("path"), 1);
23597
+ var import_node_fs40 = require("fs");
23598
+ var import_node_path76 = __toESM(require("path"), 1);
23030
23599
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
23031
23600
  return {
23032
23601
  timestamp: now.toISOString(),
@@ -23036,34 +23605,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
23036
23605
  };
23037
23606
  }
23038
23607
  async function appendUnroutedSpan(neatHome4, record) {
23039
- const target = import_node_path74.default.join(neatHome4, "errors.ndjson");
23040
- await import_node_fs39.promises.mkdir(neatHome4, { recursive: true });
23041
- await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
23608
+ const target = import_node_path76.default.join(neatHome4, "errors.ndjson");
23609
+ await import_node_fs40.promises.mkdir(neatHome4, { recursive: true });
23610
+ await import_node_fs40.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
23042
23611
  }
23043
23612
  function unroutedErrorsPath(neatHome4) {
23044
- return import_node_path74.default.join(neatHome4, "errors.ndjson");
23613
+ return import_node_path76.default.join(neatHome4, "errors.ndjson");
23045
23614
  }
23046
23615
 
23047
23616
  // src/daemon.ts
23048
- var import_types99 = require("@neat.is/types");
23617
+ var import_types105 = require("@neat.is/types");
23049
23618
  function daemonJsonPath(scanPath) {
23050
- return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
23619
+ return import_node_path77.default.join(scanPath, "neat-out", "daemon.json");
23051
23620
  }
23052
23621
  function daemonsDiscoveryDir(home) {
23053
23622
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
23054
- return import_node_path75.default.join(base, "daemons");
23623
+ return import_node_path77.default.join(base, "daemons");
23055
23624
  }
23056
23625
  function daemonDiscoveryPath(project, home) {
23057
- return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
23626
+ return import_node_path77.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
23058
23627
  }
23059
23628
  function sanitizeDiscoveryName(project) {
23060
23629
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
23061
23630
  }
23062
23631
  function neatHomeFromEnv() {
23063
23632
  const env = process.env.NEAT_HOME;
23064
- if (env && env.length > 0) return import_node_path75.default.resolve(env);
23633
+ if (env && env.length > 0) return import_node_path77.default.resolve(env);
23065
23634
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
23066
- return import_node_path75.default.join(home, ".neat");
23635
+ return import_node_path77.default.join(home, ".neat");
23067
23636
  }
23068
23637
  function resolveNeatVersion() {
23069
23638
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -23095,7 +23664,7 @@ async function clearDaemonRecord(record, home) {
23095
23664
  } catch {
23096
23665
  }
23097
23666
  try {
23098
- await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
23667
+ await import_node_fs41.promises.unlink(daemonDiscoveryPath(record.project, home));
23099
23668
  } catch {
23100
23669
  }
23101
23670
  }
@@ -23104,12 +23673,12 @@ function reconcileDaemonRecordSync(record, home) {
23104
23673
  const stopped = { ...record, status: "stopped" };
23105
23674
  const target = daemonJsonPath(record.projectPath);
23106
23675
  const tmp = `${target}.${process.pid}.tmp`;
23107
- (0, import_node_fs40.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
23108
- (0, import_node_fs40.renameSync)(tmp, target);
23676
+ (0, import_node_fs41.writeFileSync)(tmp, JSON.stringify(stopped, null, 2) + "\n");
23677
+ (0, import_node_fs41.renameSync)(tmp, target);
23109
23678
  } catch {
23110
23679
  }
23111
23680
  try {
23112
- (0, import_node_fs40.unlinkSync)(daemonDiscoveryPath(record.project, home));
23681
+ (0, import_node_fs41.unlinkSync)(daemonDiscoveryPath(record.project, home));
23113
23682
  } catch {
23114
23683
  }
23115
23684
  }
@@ -23126,17 +23695,21 @@ function teardownSlot(slot) {
23126
23695
  slot.stopConnectors();
23127
23696
  } catch {
23128
23697
  }
23698
+ try {
23699
+ slot.stopK8sSubstrate();
23700
+ } catch {
23701
+ }
23129
23702
  try {
23130
23703
  slot.detachEvents();
23131
23704
  } catch {
23132
23705
  }
23133
23706
  }
23134
23707
  function neatHomeFor(opts) {
23135
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
23708
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path77.default.resolve(opts.neatHome);
23136
23709
  const env = process.env.NEAT_HOME;
23137
- if (env && env.length > 0) return import_node_path75.default.resolve(env);
23710
+ if (env && env.length > 0) return import_node_path77.default.resolve(env);
23138
23711
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
23139
- return import_node_path75.default.join(home, ".neat");
23712
+ return import_node_path77.default.join(home, ".neat");
23140
23713
  }
23141
23714
  function routeSpanToProject(serviceName, projects) {
23142
23715
  if (!serviceName) return DEFAULT_PROJECT;
@@ -23184,13 +23757,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
23184
23757
  if (!serviceName) return true;
23185
23758
  if (serviceNameMatchesProject(serviceName, project)) return true;
23186
23759
  return graph.someNode(
23187
- (_id, attrs) => attrs.type === import_types99.NodeType.ServiceNode && attrs.name === serviceName
23760
+ (_id, attrs) => attrs.type === import_types105.NodeType.ServiceNode && attrs.name === serviceName
23188
23761
  );
23189
23762
  }
23190
23763
  async function bootstrapProject(entry2, connectors = [], neatHome4) {
23191
- const paths = pathsForProject(entry2.name, import_node_path75.default.join(entry2.path, "neat-out"));
23764
+ const paths = pathsForProject(entry2.name, import_node_path77.default.join(entry2.path, "neat-out"));
23192
23765
  try {
23193
- const stat = await import_node_fs40.promises.stat(entry2.path);
23766
+ const stat = await import_node_fs41.promises.stat(entry2.path);
23194
23767
  if (!stat.isDirectory()) {
23195
23768
  throw new Error(`registered path ${entry2.path} is not a directory`);
23196
23769
  }
@@ -23210,6 +23783,8 @@ async function bootstrapProject(entry2, connectors = [], neatHome4) {
23210
23783
  },
23211
23784
  stopConnectors: () => {
23212
23785
  },
23786
+ stopK8sSubstrate: () => {
23787
+ },
23213
23788
  detachEvents: () => {
23214
23789
  },
23215
23790
  status: "broken",
@@ -23242,6 +23817,14 @@ async function bootstrapProject(entry2, connectors = [], neatHome4) {
23242
23817
  `neatd: connector "${skipped.id}" (${skipped.provider}) skipped for project "${entry2.name}" \u2014 ${reason}`
23243
23818
  )
23244
23819
  });
23820
+ const stopK8sSubstrate = await startK8sSubstratePolling({
23821
+ project: entry2.name,
23822
+ graph,
23823
+ projectDir: entry2.path,
23824
+ errorsPath: paths.errorsPath,
23825
+ ...neatHome4 ? { home: neatHome4 } : {},
23826
+ onSkip: (skipped, reason) => console.warn(`neatd: k8s substrate "${skipped.id}" skipped for project "${entry2.name}" \u2014 ${reason}`)
23827
+ });
23245
23828
  await touchLastSeen(entry2.name).catch(() => {
23246
23829
  });
23247
23830
  return {
@@ -23252,6 +23835,7 @@ async function bootstrapProject(entry2, connectors = [], neatHome4) {
23252
23835
  stopPersist,
23253
23836
  stopStaleness,
23254
23837
  stopConnectors,
23838
+ stopK8sSubstrate,
23255
23839
  detachEvents,
23256
23840
  status: "active"
23257
23841
  };
@@ -23308,7 +23892,7 @@ async function startDaemon(opts = {}) {
23308
23892
  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;
23309
23893
  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;
23310
23894
  const singleProject = projectArg;
23311
- const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
23895
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path77.default.resolve(projectPathArg) : null;
23312
23896
  if (singleProject && !singleProjectPath) {
23313
23897
  throw new Error(
23314
23898
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -23316,14 +23900,14 @@ async function startDaemon(opts = {}) {
23316
23900
  }
23317
23901
  if (!singleProject) {
23318
23902
  try {
23319
- await import_node_fs40.promises.access(regPath);
23903
+ await import_node_fs41.promises.access(regPath);
23320
23904
  } catch {
23321
23905
  throw new Error(
23322
23906
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
23323
23907
  );
23324
23908
  }
23325
23909
  }
23326
- const pidPath = import_node_path75.default.join(home, "neatd.pid");
23910
+ const pidPath = import_node_path77.default.join(home, "neatd.pid");
23327
23911
  await writeAtomically(pidPath, `${process.pid}
23328
23912
  `);
23329
23913
  const slots = /* @__PURE__ */ new Map();
@@ -23535,7 +24119,7 @@ async function startDaemon(opts = {}) {
23535
24119
  }
23536
24120
  if (restApp) await restApp.close().catch(() => {
23537
24121
  });
23538
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24122
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23539
24123
  });
23540
24124
  throw new Error(
23541
24125
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -23713,7 +24297,7 @@ async function startDaemon(opts = {}) {
23713
24297
  });
23714
24298
  if (otlpApp) await otlpApp.close().catch(() => {
23715
24299
  });
23716
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24300
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23717
24301
  });
23718
24302
  throw new Error(
23719
24303
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -23748,7 +24332,7 @@ async function startDaemon(opts = {}) {
23748
24332
  });
23749
24333
  if (otlpApp) await otlpApp.close().catch(() => {
23750
24334
  });
23751
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24335
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23752
24336
  });
23753
24337
  throw new Error(
23754
24338
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -23795,9 +24379,9 @@ async function startDaemon(opts = {}) {
23795
24379
  let registryWatcher = null;
23796
24380
  let reloadTimer = null;
23797
24381
  if (!singleProject) try {
23798
- const regDir = import_node_path75.default.dirname(regPath);
23799
- const regBase = import_node_path75.default.basename(regPath);
23800
- registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
24382
+ const regDir = import_node_path77.default.dirname(regPath);
24383
+ const regBase = import_node_path77.default.basename(regPath);
24384
+ registryWatcher = (0, import_node_fs41.watch)(regDir, (_eventType, filename) => {
23801
24385
  if (filename !== null && filename !== regBase) return;
23802
24386
  if (reloadTimer) clearTimeout(reloadTimer);
23803
24387
  reloadTimer = setTimeout(() => {
@@ -23846,7 +24430,7 @@ async function startDaemon(opts = {}) {
23846
24430
  if (daemonRecord) {
23847
24431
  await clearDaemonRecord(daemonRecord, home);
23848
24432
  }
23849
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24433
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23850
24434
  });
23851
24435
  };
23852
24436
  return {
@@ -23869,9 +24453,9 @@ init_auth();
23869
24453
  // src/web-spawn.ts
23870
24454
  init_cjs_shims();
23871
24455
  var import_node_child_process2 = require("child_process");
23872
- var import_node_fs41 = require("fs");
24456
+ var import_node_fs42 = require("fs");
23873
24457
  var import_node_net = __toESM(require("net"), 1);
23874
- var import_node_path76 = __toESM(require("path"), 1);
24458
+ var import_node_path78 = __toESM(require("path"), 1);
23875
24459
  var DEFAULT_WEB_PORT = 6328;
23876
24460
  var DEFAULT_REST_PORT = 8080;
23877
24461
  function asValidPort(value) {
@@ -23880,11 +24464,11 @@ function asValidPort(value) {
23880
24464
  }
23881
24465
  function projectRoot() {
23882
24466
  const fromEnv = process.env.NEAT_SCAN_PATH;
23883
- return import_node_path76.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
24467
+ return import_node_path78.default.resolve(fromEnv && fromEnv.length > 0 ? fromEnv : process.cwd());
23884
24468
  }
23885
24469
  async function readDaemonPorts(root) {
23886
24470
  try {
23887
- const raw = await import_node_fs41.promises.readFile(import_node_path76.default.join(root, "neat-out", "daemon.json"), "utf8");
24471
+ const raw = await import_node_fs42.promises.readFile(import_node_path78.default.join(root, "neat-out", "daemon.json"), "utf8");
23888
24472
  const parsed = JSON.parse(raw);
23889
24473
  const ports = parsed?.ports ?? {};
23890
24474
  return { web: asValidPort(ports.web), rest: asValidPort(ports.rest) };
@@ -23929,10 +24513,10 @@ function resolveWebPackageDir() {
23929
24513
  eval("require")
23930
24514
  );
23931
24515
  const pkgJsonPath = req.resolve("@neat.is/web/package.json");
23932
- return import_node_path76.default.dirname(pkgJsonPath);
24516
+ return import_node_path78.default.dirname(pkgJsonPath);
23933
24517
  }
23934
24518
  function resolveStandaloneServerEntry(webDir) {
23935
- return import_node_path76.default.join(webDir, ".next/standalone/packages/web/server.js");
24519
+ return import_node_path78.default.join(webDir, ".next/standalone/packages/web/server.js");
23936
24520
  }
23937
24521
  async function pickInternalPort() {
23938
24522
  return new Promise((resolve, reject) => {
@@ -23985,7 +24569,7 @@ async function spawnWebUI(restPort, opts = {}) {
23985
24569
  NEAT_API_URL: apiUrl
23986
24570
  };
23987
24571
  child = (0, import_node_child_process2.spawn)(process.execPath, [serverEntry], {
23988
- cwd: import_node_path76.default.dirname(serverEntry),
24572
+ cwd: import_node_path78.default.dirname(serverEntry),
23989
24573
  env,
23990
24574
  stdio: ["ignore", "inherit", "inherit"],
23991
24575
  detached: false
@@ -24084,13 +24668,13 @@ async function waitForListening(port, timeoutMs = 15e3) {
24084
24668
 
24085
24669
  // src/version-skew.ts
24086
24670
  init_cjs_shims();
24087
- var import_promises = require("timers/promises");
24671
+ var import_promises2 = require("timers/promises");
24088
24672
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/neat.is/latest";
24089
24673
  var DEFAULT_TIMEOUT_MS = 2e3;
24090
24674
  async function fetchRegistryVersion(url, timeoutMs, fetchImpl) {
24091
24675
  const controller = new AbortController();
24092
24676
  let timedOut = false;
24093
- const timer = (0, import_promises.setTimeout)(timeoutMs).then(() => {
24677
+ const timer = (0, import_promises2.setTimeout)(timeoutMs).then(() => {
24094
24678
  timedOut = true;
24095
24679
  controller.abort();
24096
24680
  });
@@ -24161,14 +24745,14 @@ function localVersion() {
24161
24745
  }
24162
24746
  function neatHome3() {
24163
24747
  if (process.env.NEAT_HOME && process.env.NEAT_HOME.length > 0) {
24164
- return import_node_path77.default.resolve(process.env.NEAT_HOME);
24748
+ return import_node_path79.default.resolve(process.env.NEAT_HOME);
24165
24749
  }
24166
24750
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
24167
- return import_node_path77.default.join(home, ".neat");
24751
+ return import_node_path79.default.join(home, ".neat");
24168
24752
  }
24169
24753
  async function readPid() {
24170
24754
  try {
24171
- const raw = await import_node_fs42.promises.readFile(import_node_path77.default.join(neatHome3(), "neatd.pid"), "utf8");
24755
+ const raw = await import_node_fs43.promises.readFile(import_node_path79.default.join(neatHome3(), "neatd.pid"), "utf8");
24172
24756
  const n = Number.parseInt(raw.trim(), 10);
24173
24757
  return Number.isFinite(n) ? n : null;
24174
24758
  } catch {