@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/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path76 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
63
+ const path78 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path78) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path78)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -193,8 +193,8 @@ function reshapeGrpcRequest(req) {
193
193
  };
194
194
  }
195
195
  function resolveProtoRoot() {
196
- const here = import_node_path52.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
- return import_node_path52.default.resolve(here, "..", "proto");
196
+ const here = import_node_path53.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
197
+ return import_node_path53.default.resolve(here, "..", "proto");
198
198
  }
199
199
  function loadTraceService() {
200
200
  const protoRoot = resolveProtoRoot();
@@ -262,13 +262,13 @@ async function startOtelGrpcReceiver(opts) {
262
262
  })
263
263
  };
264
264
  }
265
- var import_node_url, import_node_path52, import_node_crypto2, grpc, protoLoader;
265
+ var import_node_url, import_node_path53, import_node_crypto2, grpc, protoLoader;
266
266
  var init_otel_grpc = __esm({
267
267
  "src/otel-grpc.ts"() {
268
268
  "use strict";
269
269
  init_cjs_shims();
270
270
  import_node_url = require("url");
271
- import_node_path52 = __toESM(require("path"), 1);
271
+ import_node_path53 = __toESM(require("path"), 1);
272
272
  import_node_crypto2 = require("crypto");
273
273
  grpc = __toESM(require("@grpc/grpc-js"), 1);
274
274
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
414
414
  const v = attrs[key];
415
415
  if (typeof v === "string" && v.length > 0) {
416
416
  const q = v.indexOf("?");
417
- const path76 = q === -1 ? v : v.slice(0, q);
418
- if (path76.length > 0) return path76;
417
+ const path78 = q === -1 ? v : v.slice(0, q);
418
+ if (path78.length > 0) return path78;
419
419
  }
420
420
  }
421
421
  return void 0;
@@ -477,10 +477,10 @@ function parseOtlpRequest(body) {
477
477
  return out;
478
478
  }
479
479
  function loadProtoRoot() {
480
- const here = import_node_path53.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
- const protoRoot = import_node_path53.default.resolve(here, "..", "proto");
480
+ const here = import_node_path54.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
481
+ const protoRoot = import_node_path54.default.resolve(here, "..", "proto");
482
482
  const root = new import_protobufjs.default.Root();
483
- root.resolvePath = (_origin, target) => import_node_path53.default.resolve(protoRoot, target);
483
+ root.resolvePath = (_origin, target) => import_node_path54.default.resolve(protoRoot, target);
484
484
  root.loadSync(
485
485
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
486
486
  { keepCase: true }
@@ -766,12 +766,12 @@ function logSpanHandler(span) {
766
766
  `otel: ${span.service} ${span.name} parent=${parent} status=${status2}${db}`
767
767
  );
768
768
  }
769
- var import_node_path53, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody, OTLP_STEP_ATTEMPTS, OTLP_STEP_STRIDE;
769
+ 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;
770
770
  var init_otel = __esm({
771
771
  "src/otel.ts"() {
772
772
  "use strict";
773
773
  init_cjs_shims();
774
- import_node_path53 = __toESM(require("path"), 1);
774
+ import_node_path54 = __toESM(require("path"), 1);
775
775
  import_node_url2 = require("url");
776
776
  import_node_zlib = __toESM(require("zlib"), 1);
777
777
  import_fastify = __toESM(require("fastify"), 1);
@@ -1365,19 +1365,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1365
1365
  function longestIncomingWalk(graph, start, maxDepth) {
1366
1366
  let best = { path: [start], edges: [] };
1367
1367
  const visited = /* @__PURE__ */ new Set([start]);
1368
- function step(node, path76, edges) {
1369
- if (path76.length > best.path.length) {
1370
- best = { path: [...path76], edges: [...edges] };
1368
+ function step(node, path78, edges) {
1369
+ if (path78.length > best.path.length) {
1370
+ best = { path: [...path78], edges: [...edges] };
1371
1371
  }
1372
- if (path76.length - 1 >= maxDepth) return;
1372
+ if (path78.length - 1 >= maxDepth) return;
1373
1373
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1374
1374
  for (const [srcId, edge] of incoming) {
1375
1375
  if (visited.has(srcId)) continue;
1376
1376
  visited.add(srcId);
1377
- path76.push(srcId);
1377
+ path78.push(srcId);
1378
1378
  edges.push(edge);
1379
- step(srcId, path76, edges);
1380
- path76.pop();
1379
+ step(srcId, path78, edges);
1380
+ path78.pop();
1381
1381
  edges.pop();
1382
1382
  visited.delete(srcId);
1383
1383
  }
@@ -1385,14 +1385,14 @@ function longestIncomingWalk(graph, start, maxDepth) {
1385
1385
  step(start, [start], []);
1386
1386
  return best;
1387
1387
  }
1388
- function databaseRootCauseShape(graph, origin, walk10) {
1388
+ function databaseRootCauseShape(graph, origin, walk11) {
1389
1389
  const targetDb = origin;
1390
1390
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1391
1391
  if (candidatePairs.length === 0) return null;
1392
- for (const id of walk10.path) {
1392
+ for (const id of walk11.path) {
1393
1393
  const owner = resolveOwningService(graph, id);
1394
1394
  if (!owner) continue;
1395
- const { id: serviceId16, svc } = owner;
1395
+ const { id: serviceId17, svc } = owner;
1396
1396
  const deps = svc.dependencies ?? {};
1397
1397
  for (const pair of candidatePairs) {
1398
1398
  const declared = deps[pair.driver];
@@ -1405,7 +1405,7 @@ function databaseRootCauseShape(graph, origin, walk10) {
1405
1405
  );
1406
1406
  if (!result.compatible) {
1407
1407
  return {
1408
- rootCauseNode: serviceId16,
1408
+ rootCauseNode: serviceId17,
1409
1409
  rootCauseReason: result.reason ?? "incompatible driver",
1410
1410
  ...result.minDriverVersion ? {
1411
1411
  fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
@@ -1416,11 +1416,11 @@ function databaseRootCauseShape(graph, origin, walk10) {
1416
1416
  }
1417
1417
  return null;
1418
1418
  }
1419
- function serviceRootCauseShape(graph, _origin, walk10) {
1420
- for (const id of walk10.path) {
1419
+ function serviceRootCauseShape(graph, _origin, walk11) {
1420
+ for (const id of walk11.path) {
1421
1421
  const owner = resolveOwningService(graph, id);
1422
1422
  if (!owner) continue;
1423
- const { id: serviceId16, svc } = owner;
1423
+ const { id: serviceId17, svc } = owner;
1424
1424
  const deps = svc.dependencies ?? {};
1425
1425
  const serviceNodeEngine = svc.nodeEngine;
1426
1426
  for (const constraint of nodeEngineConstraints()) {
@@ -1429,7 +1429,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
1429
1429
  const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
1430
1430
  if (!result.compatible && result.reason) {
1431
1431
  return {
1432
- rootCauseNode: serviceId16,
1432
+ rootCauseNode: serviceId17,
1433
1433
  rootCauseReason: result.reason,
1434
1434
  ...result.requiredNodeVersion ? {
1435
1435
  fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
@@ -1444,7 +1444,7 @@ function serviceRootCauseShape(graph, _origin, walk10) {
1444
1444
  const result = checkPackageConflict(conflict, declared, requiredDeclared);
1445
1445
  if (!result.compatible && result.reason) {
1446
1446
  return {
1447
- rootCauseNode: serviceId16,
1447
+ rootCauseNode: serviceId17,
1448
1448
  rootCauseReason: result.reason,
1449
1449
  fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
1450
1450
  };
@@ -1453,15 +1453,15 @@ function serviceRootCauseShape(graph, _origin, walk10) {
1453
1453
  }
1454
1454
  return null;
1455
1455
  }
1456
- function fileRootCauseShape(graph, origin, walk10) {
1456
+ function fileRootCauseShape(graph, origin, walk11) {
1457
1457
  const owner = resolveOwningService(graph, origin.id);
1458
1458
  if (!owner) return null;
1459
- return serviceRootCauseShape(graph, owner.svc, walk10);
1459
+ return serviceRootCauseShape(graph, owner.svc, walk11);
1460
1460
  }
1461
- function symbolRootCauseShape(graph, origin, walk10) {
1461
+ function symbolRootCauseShape(graph, origin, walk11) {
1462
1462
  const owner = resolveOwningService(graph, origin.id);
1463
1463
  if (!owner) return null;
1464
- return serviceRootCauseShape(graph, owner.svc, walk10);
1464
+ return serviceRootCauseShape(graph, owner.svc, walk11);
1465
1465
  }
1466
1466
  var rootCauseShapes = {
1467
1467
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1474,8 +1474,8 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1474
1474
  const origin = graph.getNodeAttributes(errorNodeId);
1475
1475
  const shape = rootCauseShapes[origin.type];
1476
1476
  if (shape) {
1477
- const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1478
- const match = shape(graph, origin, walk10);
1477
+ const walk11 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1478
+ const match = shape(graph, origin, walk11);
1479
1479
  if (match) {
1480
1480
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1481
1481
  return {
@@ -1483,9 +1483,9 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
1483
1483
  result: import_types.RootCauseResultSchema.parse({
1484
1484
  rootCauseNode: match.rootCauseNode,
1485
1485
  rootCauseReason: reason,
1486
- traversalPath: walk10.path,
1487
- edgeProvenances: walk10.edges.map((e) => e.provenance),
1488
- confidence: confidenceFromMix(walk10.edges),
1486
+ traversalPath: walk11.path,
1487
+ edgeProvenances: walk11.edges.map((e) => e.provenance),
1488
+ confidence: confidenceFromMix(walk11.edges),
1489
1489
  fixRecommendation: match.fixRecommendation
1490
1490
  })
1491
1491
  };
@@ -1549,9 +1549,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
1549
1549
  function isFailingCallEdge(e) {
1550
1550
  return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
1551
1551
  }
1552
- function callSourcesForService(graph, serviceId16) {
1553
- const ids = [serviceId16];
1554
- for (const edgeId of graph.outboundEdges(serviceId16)) {
1552
+ function callSourcesForService(graph, serviceId17) {
1553
+ const ids = [serviceId17];
1554
+ for (const edgeId of graph.outboundEdges(serviceId17)) {
1555
1555
  const e = graph.getEdgeAttributes(edgeId);
1556
1556
  if (e.type !== import_types.EdgeType.CONTAINS) continue;
1557
1557
  const tgt = graph.getNodeAttributes(e.target);
@@ -1575,9 +1575,9 @@ function failingCallDominates(e, id, curEdge, curId) {
1575
1575
  }
1576
1576
  return id < curId;
1577
1577
  }
1578
- function dominantFailingCall(graph, serviceId16, visited) {
1578
+ function dominantFailingCall(graph, serviceId17, visited) {
1579
1579
  let best = null;
1580
- for (const src of callSourcesForService(graph, serviceId16)) {
1580
+ for (const src of callSourcesForService(graph, serviceId17)) {
1581
1581
  for (const edgeId of graph.outboundEdges(src)) {
1582
1582
  const e = graph.getEdgeAttributes(edgeId);
1583
1583
  if (!isFailingCallEdge(e)) continue;
@@ -1592,20 +1592,20 @@ function dominantFailingCall(graph, serviceId16, visited) {
1592
1592
  return best;
1593
1593
  }
1594
1594
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1595
- const path76 = [originServiceId];
1595
+ const path78 = [originServiceId];
1596
1596
  const edges = [];
1597
1597
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1598
1598
  let current = originServiceId;
1599
1599
  for (let depth = 0; depth < maxDepth; depth++) {
1600
1600
  const hop = dominantFailingCall(graph, current, visited);
1601
1601
  if (!hop) break;
1602
- path76.push(hop.nextService);
1602
+ path78.push(hop.nextService);
1603
1603
  edges.push(hop.edge);
1604
1604
  visited.add(hop.nextService);
1605
1605
  current = hop.nextService;
1606
1606
  }
1607
1607
  if (edges.length === 0) return null;
1608
- return { path: path76, edges, culprit: current };
1608
+ return { path: path78, edges, culprit: current };
1609
1609
  }
1610
1610
  function isStaleCallEdge(e) {
1611
1611
  return e.type === import_types.EdgeType.CALLS && e.provenance === import_types.Provenance.STALE;
@@ -1616,9 +1616,9 @@ function staleCallDominates(e, id, curEdge, curId) {
1616
1616
  if (ev !== cv) return ev > cv;
1617
1617
  return id < curId;
1618
1618
  }
1619
- function dominantStaleCall(graph, serviceId16, visited) {
1619
+ function dominantStaleCall(graph, serviceId17, visited) {
1620
1620
  const bestByCallee = /* @__PURE__ */ new Map();
1621
- for (const src of callSourcesForService(graph, serviceId16)) {
1621
+ for (const src of callSourcesForService(graph, serviceId17)) {
1622
1622
  for (const edgeId of graph.outboundEdges(src)) {
1623
1623
  const e = graph.getEdgeAttributes(edgeId);
1624
1624
  if (e.type !== import_types.EdgeType.CALLS) continue;
@@ -1641,26 +1641,26 @@ function dominantStaleCall(graph, serviceId16, visited) {
1641
1641
  return best;
1642
1642
  }
1643
1643
  function followStaleCallChain(graph, originServiceId, maxDepth) {
1644
- const path76 = [originServiceId];
1644
+ const path78 = [originServiceId];
1645
1645
  const edges = [];
1646
1646
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1647
1647
  let current = originServiceId;
1648
1648
  for (let depth = 0; depth < maxDepth; depth++) {
1649
1649
  const hop = dominantStaleCall(graph, current, visited);
1650
1650
  if (!hop) break;
1651
- path76.push(hop.nextService);
1651
+ path78.push(hop.nextService);
1652
1652
  edges.push(hop.edge);
1653
1653
  visited.add(hop.nextService);
1654
1654
  current = hop.nextService;
1655
1655
  }
1656
1656
  if (edges.length === 0) return null;
1657
- return { path: path76, edges, culprit: current };
1657
+ return { path: path78, edges, culprit: current };
1658
1658
  }
1659
1659
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1660
1660
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1661
1661
  if (!chain) return null;
1662
1662
  const culprit = chain.culprit;
1663
- const path76 = [...chain.path];
1663
+ const path78 = [...chain.path];
1664
1664
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1665
1665
  const baseConfidence = confidenceFromMix(chain.edges);
1666
1666
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1668,14 +1668,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1668
1668
  if (loc) {
1669
1669
  let rootCauseNode = culprit;
1670
1670
  if (loc.fileNode) {
1671
- path76.push(loc.fileNode);
1671
+ path78.push(loc.fileNode);
1672
1672
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1673
1673
  rootCauseNode = loc.fileNode;
1674
1674
  }
1675
1675
  return import_types.RootCauseResultSchema.parse({
1676
1676
  rootCauseNode,
1677
1677
  rootCauseReason: loc.rootCauseReason,
1678
- traversalPath: path76,
1678
+ traversalPath: path78,
1679
1679
  edgeProvenances,
1680
1680
  confidence,
1681
1681
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1687,7 +1687,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1687
1687
  return import_types.RootCauseResultSchema.parse({
1688
1688
  rootCauseNode: culprit,
1689
1689
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1690
- traversalPath: path76,
1690
+ traversalPath: path78,
1691
1691
  edgeProvenances,
1692
1692
  confidence,
1693
1693
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2300,10 +2300,10 @@ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
2300
2300
  traversalPath = staleChain.path;
2301
2301
  edgeProvenances = staleChain.edges.map((e) => e.provenance);
2302
2302
  } else if (top.node !== seedNode) {
2303
- const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2304
- if (path76) {
2305
- traversalPath = path76.nodes;
2306
- edgeProvenances = path76.edges.map((e) => e.provenance);
2303
+ const path78 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
2304
+ if (path78) {
2305
+ traversalPath = path78.nodes;
2306
+ edgeProvenances = path78.edges.map((e) => e.provenance);
2307
2307
  } else {
2308
2308
  traversalPath = [errorNodeId, top.node];
2309
2309
  edgeProvenances = [top.provenance ?? import_types.Provenance.OBSERVED];
@@ -3127,7 +3127,7 @@ function buildServiceHostIndex(services) {
3127
3127
  async function walkSourceFiles(dir, excludeDirs = []) {
3128
3128
  const excluded = new Set(excludeDirs.map((d) => import_node_path5.default.resolve(d)));
3129
3129
  const out = [];
3130
- async function walk10(current) {
3130
+ async function walk11(current) {
3131
3131
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
3132
3132
  for (const entry of entries) {
3133
3133
  const full = import_node_path5.default.join(current, entry.name);
@@ -3135,7 +3135,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
3135
3135
  if (IGNORED_DIRS.has(entry.name)) continue;
3136
3136
  if (excluded.has(import_node_path5.default.resolve(full))) continue;
3137
3137
  if (await isPythonVenvDir(full)) continue;
3138
- await walk10(full);
3138
+ await walk11(full);
3139
3139
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
3140
3140
  // would attribute our instrumentation imports to the user's service.
3141
3141
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -3143,7 +3143,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
3143
3143
  }
3144
3144
  }
3145
3145
  }
3146
- await walk10(dir);
3146
+ await walk11(dir);
3147
3147
  return out;
3148
3148
  }
3149
3149
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -3739,8 +3739,8 @@ function chiRoutesFromSource(source, parser) {
3739
3739
  chiWalk(tree.rootNode, "", out);
3740
3740
  return out;
3741
3741
  }
3742
- function stripChiRegex(path76) {
3743
- return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3742
+ function stripChiRegex(path78) {
3743
+ return path78.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
3744
3744
  }
3745
3745
  function chiWalk(node, prefix, out) {
3746
3746
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -4412,9 +4412,9 @@ function rubyRocketRoute(args) {
4412
4412
  if (!pair || pair.type !== "pair") continue;
4413
4413
  const k = pair.childForFieldName("key");
4414
4414
  if (k?.type !== "string") continue;
4415
- const path76 = rubyLiteral(k);
4416
- if (path76 === null) continue;
4417
- return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
4415
+ const path78 = rubyLiteral(k);
4416
+ if (path78 === null) continue;
4417
+ return { path: path78, target: rubyLiteral(pair.childForFieldName("value")) };
4418
4418
  }
4419
4419
  return null;
4420
4420
  }
@@ -6025,8 +6025,8 @@ function pickContainingSymbol(candidates, fn) {
6025
6025
  return b.symbol.span.startLine - a.symbol.span.startLine;
6026
6026
  };
6027
6027
  if (fn) {
6028
- const named = candidates.filter((c) => terminalName(c.symbol.qualname) === fn);
6029
- if (named.length > 0) return [...named].sort(bySpan)[0].id;
6028
+ const named2 = candidates.filter((c) => terminalName(c.symbol.qualname) === fn);
6029
+ if (named2.length > 0) return [...named2].sort(bySpan)[0].id;
6030
6030
  }
6031
6031
  return [...candidates].sort(bySpan)[0].id;
6032
6032
  }
@@ -6969,29 +6969,29 @@ function promoteFrontierNodes(graph, opts = {}) {
6969
6969
  toPromote.push({ frontierId: id, serviceId: target });
6970
6970
  });
6971
6971
  let promoted = 0;
6972
- for (const { frontierId: frontierId2, serviceId: serviceId16 } of toPromote) {
6972
+ for (const { frontierId: frontierId2, serviceId: serviceId17 } of toPromote) {
6973
6973
  if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
6974
6974
  const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
6975
6975
  if (!gate.allowed) {
6976
6976
  continue;
6977
6977
  }
6978
6978
  }
6979
- rewireFrontierEdges(graph, frontierId2, serviceId16);
6979
+ rewireFrontierEdges(graph, frontierId2, serviceId17);
6980
6980
  graph.dropNode(frontierId2);
6981
6981
  promoted++;
6982
6982
  }
6983
6983
  return promoted;
6984
6984
  }
6985
- function rewireFrontierEdges(graph, frontierId2, serviceId16) {
6985
+ function rewireFrontierEdges(graph, frontierId2, serviceId17) {
6986
6986
  const inbound = [...graph.inboundEdges(frontierId2)];
6987
6987
  const outbound = [...graph.outboundEdges(frontierId2)];
6988
6988
  for (const edgeId of inbound) {
6989
6989
  const edge = graph.getEdgeAttributes(edgeId);
6990
- rebuildEdge(graph, edge, edge.source, serviceId16, edgeId);
6990
+ rebuildEdge(graph, edge, edge.source, serviceId17, edgeId);
6991
6991
  }
6992
6992
  for (const edgeId of outbound) {
6993
6993
  const edge = graph.getEdgeAttributes(edgeId);
6994
- rebuildEdge(graph, edge, serviceId16, edge.target, edgeId);
6994
+ rebuildEdge(graph, edge, serviceId17, edge.target, edgeId);
6995
6995
  }
6996
6996
  }
6997
6997
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
@@ -8127,9 +8127,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
8127
8127
  "StatefulSet",
8128
8128
  "DaemonSet"
8129
8129
  ]);
8130
- function addAliases(graph, serviceId16, candidates) {
8131
- if (!graph.hasNode(serviceId16)) return;
8132
- const node = graph.getNodeAttributes(serviceId16);
8130
+ function addAliases(graph, serviceId17, candidates) {
8131
+ if (!graph.hasNode(serviceId17)) return;
8132
+ const node = graph.getNodeAttributes(serviceId17);
8133
8133
  if (node.type !== import_types19.NodeType.ServiceNode) return;
8134
8134
  const set = new Set(node.aliases ?? []);
8135
8135
  for (const c of candidates) {
@@ -8139,7 +8139,7 @@ function addAliases(graph, serviceId16, candidates) {
8139
8139
  }
8140
8140
  if (set.size === 0) return;
8141
8141
  const updated = { ...node, aliases: [...set].sort() };
8142
- graph.replaceNodeAttributes(serviceId16, updated);
8142
+ graph.replaceNodeAttributes(serviceId17, updated);
8143
8143
  }
8144
8144
  function indexServicesByName(services) {
8145
8145
  const map = /* @__PURE__ */ new Map();
@@ -8172,12 +8172,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
8172
8172
  }
8173
8173
  if (!compose?.services) return;
8174
8174
  for (const [composeName, svc] of Object.entries(compose.services)) {
8175
- const serviceId16 = serviceIndex.get(composeName);
8176
- if (!serviceId16) continue;
8175
+ const serviceId17 = serviceIndex.get(composeName);
8176
+ if (!serviceId17) continue;
8177
8177
  const aliases = /* @__PURE__ */ new Set([composeName]);
8178
8178
  if (svc.container_name) aliases.add(svc.container_name);
8179
8179
  if (svc.hostname) aliases.add(svc.hostname);
8180
- addAliases(graph, serviceId16, aliases);
8180
+ addAliases(graph, serviceId17, aliases);
8181
8181
  }
8182
8182
  }
8183
8183
  var LABEL_KEYS = /* @__PURE__ */ new Set([
@@ -9288,7 +9288,7 @@ async function addSymbolEdges(graph, services) {
9288
9288
  return best;
9289
9289
  };
9290
9290
  const requests = [];
9291
- const walk10 = (node) => {
9291
+ const walk11 = (node) => {
9292
9292
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
9293
9293
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
9294
9294
  if (self && self.kind === "class") {
@@ -9334,10 +9334,10 @@ async function addSymbolEdges(graph, services) {
9334
9334
  }
9335
9335
  for (let i = 0; i < node.namedChildCount; i++) {
9336
9336
  const child = node.namedChild(i);
9337
- if (child) walk10(child);
9337
+ if (child) walk11(child);
9338
9338
  }
9339
9339
  };
9340
- walk10(root);
9340
+ walk11(root);
9341
9341
  for (const req of requests) {
9342
9342
  const targetSid = resolveTarget(req.targetName, req.wantKind);
9343
9343
  if (!targetSid) continue;
@@ -9445,10 +9445,10 @@ function collectNamedImports(root) {
9445
9445
  const clause = node.namedChild(i);
9446
9446
  if (clause?.type !== "import_clause") continue;
9447
9447
  for (let j = 0; j < clause.namedChildCount; j++) {
9448
- const named = clause.namedChild(j);
9449
- if (named?.type !== "named_imports") continue;
9450
- for (let k = 0; k < named.namedChildCount; k++) {
9451
- const spec = named.namedChild(k);
9448
+ const named2 = clause.namedChild(j);
9449
+ if (named2?.type !== "named_imports") continue;
9450
+ for (let k = 0; k < named2.namedChildCount; k++) {
9451
+ const spec = named2.namedChild(k);
9452
9452
  if (spec?.type !== "import_specifier") continue;
9453
9453
  let isType = false;
9454
9454
  for (let t = 0; t < spec.childCount; t++) {
@@ -10509,7 +10509,7 @@ var import_types24 = require("@neat.is/types");
10509
10509
  async function walkConfigFiles(dir, excludeDirs = []) {
10510
10510
  const excluded = new Set(excludeDirs.map((d) => import_node_path35.default.resolve(d)));
10511
10511
  const out = [];
10512
- async function walk10(current) {
10512
+ async function walk11(current) {
10513
10513
  const entries = await import_node_fs23.promises.readdir(current, { withFileTypes: true });
10514
10514
  for (const entry of entries) {
10515
10515
  const full = import_node_path35.default.join(current, entry.name);
@@ -10517,13 +10517,13 @@ async function walkConfigFiles(dir, excludeDirs = []) {
10517
10517
  if (IGNORED_DIRS.has(entry.name)) continue;
10518
10518
  if (excluded.has(import_node_path35.default.resolve(full))) continue;
10519
10519
  if (await isPythonVenvDir(full)) continue;
10520
- await walk10(full);
10520
+ await walk11(full);
10521
10521
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
10522
10522
  out.push(full);
10523
10523
  }
10524
10524
  }
10525
10525
  }
10526
- await walk10(dir);
10526
+ await walk11(dir);
10527
10527
  return out;
10528
10528
  }
10529
10529
  async function addConfigNodes(graph, services, scanPath) {
@@ -10614,7 +10614,7 @@ function grpcMethodsFromProto(content, fqPackage) {
10614
10614
  async function walkProtoFiles(dir, excludeDirs = []) {
10615
10615
  const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10616
10616
  const out = [];
10617
- async function walk10(current) {
10617
+ async function walk11(current) {
10618
10618
  const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10619
10619
  for (const entry of entries) {
10620
10620
  const full = import_node_path36.default.join(current, entry.name);
@@ -10622,13 +10622,13 @@ async function walkProtoFiles(dir, excludeDirs = []) {
10622
10622
  if (IGNORED_DIRS.has(entry.name)) continue;
10623
10623
  if (excluded.has(import_node_path36.default.resolve(full))) continue;
10624
10624
  if (await isPythonVenvDir(full)) continue;
10625
- await walk10(full);
10625
+ await walk11(full);
10626
10626
  } else if (entry.isFile() && import_node_path36.default.extname(entry.name) === PROTO_EXTENSION) {
10627
10627
  out.push(full);
10628
10628
  }
10629
10629
  }
10630
10630
  }
10631
- await walk10(dir);
10631
+ await walk11(dir);
10632
10632
  return out;
10633
10633
  }
10634
10634
  async function addGrpcMethods(graph, services) {
@@ -10696,7 +10696,7 @@ async function addGrpcMethods(graph, services) {
10696
10696
 
10697
10697
  // src/extract/calls/index.ts
10698
10698
  init_cjs_shims();
10699
- var import_types44 = require("@neat.is/types");
10699
+ var import_types45 = require("@neat.is/types");
10700
10700
 
10701
10701
  // src/extract/calls/http.ts
10702
10702
  init_cjs_shims();
@@ -11631,7 +11631,7 @@ function isFirestoreClientFactory(node) {
11631
11631
  }
11632
11632
  function firestoreClientVars(root) {
11633
11633
  const vars = /* @__PURE__ */ new Set();
11634
- const walk10 = (node) => {
11634
+ const walk11 = (node) => {
11635
11635
  if (node.type === "variable_declarator") {
11636
11636
  const name = node.childForFieldName("name");
11637
11637
  let value = node.childForFieldName("value");
@@ -11640,9 +11640,9 @@ function firestoreClientVars(root) {
11640
11640
  vars.add(name.text);
11641
11641
  }
11642
11642
  }
11643
- for (const c of namedChildren(node)) walk10(c);
11643
+ for (const c of namedChildren(node)) walk11(c);
11644
11644
  };
11645
- walk10(root);
11645
+ walk11(root);
11646
11646
  return vars;
11647
11647
  }
11648
11648
  function isClientExpr(node, clientVars) {
@@ -11797,7 +11797,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11797
11797
  }
11798
11798
  s.add(field);
11799
11799
  };
11800
- const walk10 = (node) => {
11800
+ const walk11 = (node) => {
11801
11801
  if (node.type === "call_expression") {
11802
11802
  const fn = node.childForFieldName("function");
11803
11803
  const line = node.startPosition.row + 1;
@@ -11837,9 +11837,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11837
11837
  }
11838
11838
  }
11839
11839
  }
11840
- for (const c of namedChildren(node)) walk10(c);
11840
+ for (const c of namedChildren(node)) walk11(c);
11841
11841
  };
11842
- walk10(tree.rootNode);
11842
+ walk11(tree.rootNode);
11843
11843
  const out = [];
11844
11844
  for (const [collPath, line] of collLine) {
11845
11845
  const byField = writes.get(collPath);
@@ -12082,8 +12082,8 @@ function parseImportBindings(content) {
12082
12082
  out.push({ local: ns[1], kind: "namespace", specifier: spec });
12083
12083
  continue;
12084
12084
  }
12085
- const named = /\{([^}]*)\}/.exec(clause);
12086
- if (named) parseDestructure(named[1], spec, out);
12085
+ const named2 = /\{([^}]*)\}/.exec(clause);
12086
+ if (named2) parseDestructure(named2[1], spec, out);
12087
12087
  const def = /^(\w+)\s*(?:,|$)/.exec(clause);
12088
12088
  if (def && !clause.startsWith("{")) out.push({ local: def[1], kind: "default", specifier: spec });
12089
12089
  }
@@ -12103,8 +12103,8 @@ function fileExportsOf(content, pluralizeOn) {
12103
12103
  for (const d of defs) if (d.varName) byVar.set(d.varName, d.resolved.name);
12104
12104
  const byName = new Map(byVar);
12105
12105
  let def;
12106
- const named = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
12107
- if (named && byVar.has(named[1])) def = byVar.get(named[1]);
12106
+ const named2 = /(?:module\.exports|export\s+default)\s*=\s*(\w+)\b/.exec(content);
12107
+ if (named2 && byVar.has(named2[1])) def = byVar.get(named2[1]);
12108
12108
  if (!def) {
12109
12109
  const inline = /(?:module\.exports|export\s+default)\s*=\s*(?:await\s+)?(?:\w+\s*\.\s*)?model\s*\(\s*['"`]([\w$]+)['"`]/.exec(content);
12110
12110
  if (inline) def = pluralizeOn ? pluralizeCollection(inline[1]) : inline[1];
@@ -12455,13 +12455,13 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12455
12455
  return out;
12456
12456
  }
12457
12457
 
12458
- // src/extract/calls/django-orm.ts
12458
+ // src/extract/calls/socket.ts
12459
12459
  init_cjs_shims();
12460
12460
  var import_node_path47 = __toESM(require("path"), 1);
12461
12461
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
12462
12462
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
12463
12463
  var import_types36 = require("@neat.is/types");
12464
- var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
12464
+ var SOCKET_IMPORT_RE = /(?:^|\n)\s*(?:import\s+socket\b|from\s+socket\s+import\b)/;
12465
12465
  var PARSE_CHUNK7 = 16384;
12466
12466
  function makePyParser4() {
12467
12467
  const p = new import_tree_sitter10.default();
@@ -12481,22 +12481,109 @@ function namedChildren3(node) {
12481
12481
  }
12482
12482
  return out;
12483
12483
  }
12484
+ function walk4(node, visit) {
12485
+ visit(node);
12486
+ for (const c of namedChildren3(node)) walk4(c, visit);
12487
+ }
12484
12488
  function pyStaticStringText3(node) {
12485
12489
  if (node.type !== "string") return null;
12486
- for (const c of namedChildren3(node)) {
12490
+ for (const child of namedChildren3(node)) {
12491
+ if (child.type === "interpolation") return null;
12492
+ if (child.type === "string_content") return child.text;
12493
+ }
12494
+ return "";
12495
+ }
12496
+ function hasSocketConstructor(root) {
12497
+ let found = false;
12498
+ walk4(root, (n) => {
12499
+ if (found || n.type !== "call") return;
12500
+ const t = n.childForFieldName("function")?.text;
12501
+ if (t === "socket.socket" || t === "socket") found = true;
12502
+ });
12503
+ return found;
12504
+ }
12505
+ function socketEndpointsFromFile(file, serviceDir) {
12506
+ if (!SOCKET_IMPORT_RE.test(file.content)) return [];
12507
+ const tree = parseSource7(makePyParser4(), file.content);
12508
+ if (!hasSocketConstructor(tree.rootNode)) return [];
12509
+ const out = [];
12510
+ const seen = /* @__PURE__ */ new Set();
12511
+ walk4(tree.rootNode, (node) => {
12512
+ if (node.type !== "call") return;
12513
+ const fn = node.childForFieldName("function");
12514
+ if (fn?.type !== "attribute" || fn.childForFieldName("attribute")?.text !== "connect") return;
12515
+ const args = node.childForFieldName("arguments");
12516
+ const first = args ? namedChildren3(args).find((a) => a.type !== "keyword_argument") : void 0;
12517
+ if (!first || first.type !== "tuple") return;
12518
+ const elems = namedChildren3(first);
12519
+ if (elems.length < 2) return;
12520
+ const hostNode = elems[0];
12521
+ const host = hostNode.type === "string" ? pyStaticStringText3(hostNode) : null;
12522
+ const portNode = elems[1];
12523
+ const port = portNode.type === "integer" ? portNode.text : null;
12524
+ const name = host ? port ? `${host}:${port}` : host : "env";
12525
+ if (seen.has(name)) return;
12526
+ seen.add(name);
12527
+ const line = node.startPosition.row + 1;
12528
+ out.push({
12529
+ infraId: (0, import_types36.infraId)("socket", name),
12530
+ name,
12531
+ kind: "socket",
12532
+ edgeType: "CALLS",
12533
+ // A verified `socket.socket(...)` + `.connect((host, port))` call site.
12534
+ confidenceKind: "verified-call-site",
12535
+ evidence: {
12536
+ file: import_node_path47.default.relative(serviceDir, file.path),
12537
+ line,
12538
+ snippet: snippet(file.content, line)
12539
+ }
12540
+ });
12541
+ });
12542
+ return out;
12543
+ }
12544
+
12545
+ // src/extract/calls/django-orm.ts
12546
+ init_cjs_shims();
12547
+ var import_node_path48 = __toESM(require("path"), 1);
12548
+ var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12549
+ var import_tree_sitter_python7 = __toESM(require("tree-sitter-python"), 1);
12550
+ var import_types37 = require("@neat.is/types");
12551
+ var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
12552
+ var PARSE_CHUNK8 = 16384;
12553
+ function makePyParser5() {
12554
+ const p = new import_tree_sitter11.default();
12555
+ p.setLanguage(import_tree_sitter_python7.default);
12556
+ return p;
12557
+ }
12558
+ function parseSource8(parser, source) {
12559
+ return parser.parse(
12560
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK8)
12561
+ );
12562
+ }
12563
+ function namedChildren4(node) {
12564
+ const out = [];
12565
+ for (let i = 0; i < node.namedChildCount; i++) {
12566
+ const c = node.namedChild(i);
12567
+ if (c) out.push(c);
12568
+ }
12569
+ return out;
12570
+ }
12571
+ function pyStaticStringText4(node) {
12572
+ if (node.type !== "string") return null;
12573
+ for (const c of namedChildren4(node)) {
12487
12574
  if (c.type === "interpolation") return null;
12488
12575
  if (c.type === "string_content") return c.text;
12489
12576
  }
12490
12577
  return "";
12491
12578
  }
12492
- function walk4(node, visit) {
12579
+ function walk5(node, visit) {
12493
12580
  visit(node);
12494
- for (const c of namedChildren3(node)) walk4(c, visit);
12581
+ for (const c of namedChildren4(node)) walk5(c, visit);
12495
12582
  }
12496
12583
  function extendsDjangoModel(cls) {
12497
12584
  const supers = cls.childForFieldName("superclasses");
12498
12585
  if (!supers) return false;
12499
- for (const a of namedChildren3(supers)) {
12586
+ for (const a of namedChildren4(supers)) {
12500
12587
  const t = a.text;
12501
12588
  if (t === "models.Model" || t === "Model" || t.endsWith(".Model")) return true;
12502
12589
  }
@@ -12504,31 +12591,31 @@ function extendsDjangoModel(cls) {
12504
12591
  }
12505
12592
  function readMeta(body) {
12506
12593
  const out = {};
12507
- for (const stmt of namedChildren3(body)) {
12594
+ for (const stmt of namedChildren4(body)) {
12508
12595
  if (stmt.type !== "class_definition") continue;
12509
12596
  if (stmt.childForFieldName("name")?.text !== "Meta") continue;
12510
12597
  const metaBody = stmt.childForFieldName("body");
12511
12598
  if (!metaBody) continue;
12512
- for (const ms of namedChildren3(metaBody)) {
12599
+ for (const ms of namedChildren4(metaBody)) {
12513
12600
  if (ms.type !== "expression_statement") continue;
12514
12601
  const a = ms.namedChild(0);
12515
12602
  if (a?.type !== "assignment") continue;
12516
12603
  const l = a.childForFieldName("left")?.text;
12517
12604
  const r = a.childForFieldName("right");
12518
12605
  if (r?.type !== "string") continue;
12519
- if (l === "db_table") out.dbTable = pyStaticStringText3(r) ?? void 0;
12520
- if (l === "app_label") out.appLabel = pyStaticStringText3(r) ?? void 0;
12606
+ if (l === "db_table") out.dbTable = pyStaticStringText4(r) ?? void 0;
12607
+ if (l === "app_label") out.appLabel = pyStaticStringText4(r) ?? void 0;
12521
12608
  }
12522
12609
  }
12523
12610
  return out;
12524
12611
  }
12525
12612
  function djangoOrmEndpointsFromFile(file, serviceDir) {
12526
12613
  if (!DJANGO_IMPORT_RE.test(file.content)) return [];
12527
- const tree = parseSource7(makePyParser4(), file.content);
12614
+ const tree = parseSource8(makePyParser5(), file.content);
12528
12615
  const out = [];
12529
12616
  const seen = /* @__PURE__ */ new Set();
12530
- const defaultAppLabel = import_node_path47.default.basename(import_node_path47.default.dirname(file.path));
12531
- walk4(tree.rootNode, (node) => {
12617
+ const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
12618
+ walk5(tree.rootNode, (node) => {
12532
12619
  if (node.type !== "class_definition") return;
12533
12620
  if (!extendsDjangoModel(node)) return;
12534
12621
  const nameNode = node.childForFieldName("name");
@@ -12540,12 +12627,12 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12540
12627
  seen.add(table);
12541
12628
  const line = node.startPosition.row + 1;
12542
12629
  out.push({
12543
- infraId: (0, import_types36.infraId)("sql-table", table),
12630
+ infraId: (0, import_types37.infraId)("sql-table", table),
12544
12631
  name: table,
12545
12632
  kind: "sql-table",
12546
12633
  edgeType: "CALLS",
12547
12634
  confidenceKind: "verified-call-site",
12548
- evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12635
+ evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12549
12636
  });
12550
12637
  });
12551
12638
  return out;
@@ -12553,18 +12640,18 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12553
12640
 
12554
12641
  // src/extract/calls/drizzle.ts
12555
12642
  init_cjs_shims();
12556
- var import_node_path48 = __toESM(require("path"), 1);
12557
- var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12643
+ var import_node_path49 = __toESM(require("path"), 1);
12644
+ var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12558
12645
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12559
- var import_types37 = require("@neat.is/types");
12646
+ var import_types38 = require("@neat.is/types");
12560
12647
  var DRIZZLE_IMPORT_RE = /drizzle-orm/;
12561
12648
  var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
12562
12649
  function parserForExt3(ext) {
12563
- const p = new import_tree_sitter11.default();
12650
+ const p = new import_tree_sitter12.default();
12564
12651
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript7.default);
12565
12652
  return p;
12566
12653
  }
12567
- function namedChildren4(node) {
12654
+ function namedChildren5(node) {
12568
12655
  const out = [];
12569
12656
  for (let i = 0; i < node.namedChildCount; i++) {
12570
12657
  const c = node.namedChild(i);
@@ -12574,7 +12661,7 @@ function namedChildren4(node) {
12574
12661
  }
12575
12662
  function stringLiteralText4(node) {
12576
12663
  if (!node || node.type !== "string") return null;
12577
- for (const child of namedChildren4(node)) {
12664
+ for (const child of namedChildren5(node)) {
12578
12665
  if (child.type === "string_fragment") return child.text;
12579
12666
  }
12580
12667
  return "";
@@ -12582,7 +12669,7 @@ function stringLiteralText4(node) {
12582
12669
  function firstStringArg(call) {
12583
12670
  const args = call.childForFieldName("arguments");
12584
12671
  if (!args) return null;
12585
- for (const arg of namedChildren4(args)) {
12672
+ for (const arg of namedChildren5(args)) {
12586
12673
  if (arg.type === "string") return stringLiteralText4(arg);
12587
12674
  return null;
12588
12675
  }
@@ -12617,7 +12704,7 @@ function keyName2(key) {
12617
12704
  function columnsFromObject(obj) {
12618
12705
  const out = [];
12619
12706
  const seen = /* @__PURE__ */ new Set();
12620
- for (const child of namedChildren4(obj)) {
12707
+ for (const child of namedChildren5(obj)) {
12621
12708
  if (child.type !== "pair") continue;
12622
12709
  const value = child.childForFieldName("value");
12623
12710
  const key = keyName2(child.childForFieldName("key"));
@@ -12631,15 +12718,15 @@ function columnsFromObject(obj) {
12631
12718
  }
12632
12719
  function drizzleEndpointsFromFile(file, serviceDir) {
12633
12720
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12634
- const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
12721
+ const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12635
12722
  const out = [];
12636
12723
  const seen = /* @__PURE__ */ new Set();
12637
- const walk10 = (node) => {
12724
+ const walk11 = (node) => {
12638
12725
  if (node.type === "call_expression") {
12639
12726
  const fn = node.childForFieldName("function");
12640
12727
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
12641
12728
  const args = node.childForFieldName("arguments");
12642
- const argNodes = args ? namedChildren4(args) : [];
12729
+ const argNodes = args ? namedChildren5(args) : [];
12643
12730
  const tableName = stringLiteralText4(argNodes[0] ?? null);
12644
12731
  const obj = argNodes[1];
12645
12732
  if (tableName && obj?.type === "object" && !seen.has(tableName)) {
@@ -12647,14 +12734,14 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12647
12734
  const columns = columnsFromObject(obj);
12648
12735
  const line = node.startPosition.row + 1;
12649
12736
  out.push({
12650
- infraId: (0, import_types37.infraId)("sql-table", tableName),
12737
+ infraId: (0, import_types38.infraId)("sql-table", tableName),
12651
12738
  name: tableName,
12652
12739
  kind: "sql-table",
12653
12740
  edgeType: "CALLS",
12654
12741
  confidenceKind: "structural",
12655
12742
  columns,
12656
12743
  evidence: {
12657
- file: import_node_path48.default.relative(serviceDir, file.path),
12744
+ file: import_node_path49.default.relative(serviceDir, file.path),
12658
12745
  line,
12659
12746
  snippet: snippet(file.content, line)
12660
12747
  }
@@ -12662,9 +12749,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12662
12749
  }
12663
12750
  }
12664
12751
  }
12665
- for (const c of namedChildren4(node)) walk10(c);
12752
+ for (const c of namedChildren5(node)) walk11(c);
12666
12753
  };
12667
- walk10(tree.rootNode);
12754
+ walk11(tree.rootNode);
12668
12755
  return out;
12669
12756
  }
12670
12757
  function enclosingVarName(call) {
@@ -12686,12 +12773,12 @@ function enclosingVarName(call) {
12686
12773
  function collectDrizzleTables(root) {
12687
12774
  const tables = [];
12688
12775
  const varToTable = /* @__PURE__ */ new Map();
12689
- const walk10 = (node) => {
12776
+ const walk11 = (node) => {
12690
12777
  if (node.type === "call_expression") {
12691
12778
  const fn = node.childForFieldName("function");
12692
12779
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
12693
12780
  const args = node.childForFieldName("arguments");
12694
- const argNodes = args ? namedChildren4(args) : [];
12781
+ const argNodes = args ? namedChildren5(args) : [];
12695
12782
  const tableName = stringLiteralText4(argNodes[0] ?? null);
12696
12783
  const obj = argNodes[1]?.type === "object" ? argNodes[1] : null;
12697
12784
  if (tableName) {
@@ -12701,9 +12788,9 @@ function collectDrizzleTables(root) {
12701
12788
  }
12702
12789
  }
12703
12790
  }
12704
- for (const c of namedChildren4(node)) walk10(c);
12791
+ for (const c of namedChildren5(node)) walk11(c);
12705
12792
  };
12706
- walk10(root);
12793
+ walk11(root);
12707
12794
  return { tables, varToTable };
12708
12795
  }
12709
12796
  function referencesTargetVar(call) {
@@ -12711,7 +12798,7 @@ function referencesTargetVar(call) {
12711
12798
  if (fn?.type !== "member_expression") return null;
12712
12799
  if (fn.childForFieldName("property")?.text !== "references") return null;
12713
12800
  const args = call.childForFieldName("arguments");
12714
- const first = args ? namedChildren4(args)[0] ?? null : null;
12801
+ const first = args ? namedChildren5(args)[0] ?? null : null;
12715
12802
  if (first?.type !== "arrow_function") return null;
12716
12803
  const body = first.childForFieldName("body");
12717
12804
  if (body?.type !== "member_expression") return null;
@@ -12720,13 +12807,13 @@ function referencesTargetVar(call) {
12720
12807
  }
12721
12808
  function drizzleForeignKeys(file, serviceDir) {
12722
12809
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12723
- const tree = parseSource3(parserForExt3(import_node_path48.default.extname(file.path)), file.content);
12810
+ const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12724
12811
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
12725
12812
  const out = [];
12726
12813
  const seen = /* @__PURE__ */ new Set();
12727
12814
  for (const table of tables) {
12728
12815
  if (!table.object) continue;
12729
- const walk10 = (node) => {
12816
+ const walk11 = (node) => {
12730
12817
  if (node.type === "call_expression") {
12731
12818
  const targetVar = referencesTargetVar(node);
12732
12819
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -12739,7 +12826,7 @@ function drizzleForeignKeys(file, serviceDir) {
12739
12826
  childTable: table.tableName,
12740
12827
  parentTable,
12741
12828
  evidence: {
12742
- file: import_node_path48.default.relative(serviceDir, file.path),
12829
+ file: import_node_path49.default.relative(serviceDir, file.path),
12743
12830
  line,
12744
12831
  snippet: snippet(file.content, line)
12745
12832
  }
@@ -12747,17 +12834,17 @@ function drizzleForeignKeys(file, serviceDir) {
12747
12834
  }
12748
12835
  }
12749
12836
  }
12750
- for (const c of namedChildren4(node)) walk10(c);
12837
+ for (const c of namedChildren5(node)) walk11(c);
12751
12838
  };
12752
- walk10(table.object);
12839
+ walk11(table.object);
12753
12840
  }
12754
12841
  return out;
12755
12842
  }
12756
12843
 
12757
12844
  // src/extract/calls/prisma.ts
12758
12845
  init_cjs_shims();
12759
- var import_node_path49 = __toESM(require("path"), 1);
12760
- var import_types38 = require("@neat.is/types");
12846
+ var import_node_path50 = __toESM(require("path"), 1);
12847
+ var import_types39 = require("@neat.is/types");
12761
12848
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
12762
12849
  "Int",
12763
12850
  "String",
@@ -12805,14 +12892,14 @@ function prismaColumnsFromSchema(file, serviceDir) {
12805
12892
  if (seenTable.has(b.tableName)) return;
12806
12893
  seenTable.add(b.tableName);
12807
12894
  out.push({
12808
- infraId: (0, import_types38.infraId)("sql-table", b.tableName),
12895
+ infraId: (0, import_types39.infraId)("sql-table", b.tableName),
12809
12896
  name: b.tableName,
12810
12897
  kind: "sql-table",
12811
12898
  edgeType: "CALLS",
12812
12899
  confidenceKind: "structural",
12813
12900
  columns: b.columns,
12814
12901
  evidence: {
12815
- file: import_node_path49.default.relative(serviceDir, file.path),
12902
+ file: import_node_path50.default.relative(serviceDir, file.path),
12816
12903
  line: b.startLine,
12817
12904
  snippet: snippet(content, b.startLine)
12818
12905
  }
@@ -12873,7 +12960,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12873
12960
  }
12874
12961
  async function prismaColumnEndpoints(serviceDir) {
12875
12962
  const schemaPath = await findFirst(serviceDir, [
12876
- import_node_path49.default.join("prisma", "schema.prisma"),
12963
+ import_node_path50.default.join("prisma", "schema.prisma"),
12877
12964
  "schema.prisma"
12878
12965
  ]);
12879
12966
  if (!schemaPath) return [];
@@ -12948,7 +13035,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12948
13035
  childTable: current.table,
12949
13036
  parentTable,
12950
13037
  evidence: {
12951
- file: import_node_path49.default.relative(serviceDir, file.path),
13038
+ file: import_node_path50.default.relative(serviceDir, file.path),
12952
13039
  line: lineNo,
12953
13040
  snippet: snippet(content, lineNo)
12954
13041
  }
@@ -12963,7 +13050,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12963
13050
  }
12964
13051
  async function prismaForeignKeys(serviceDir) {
12965
13052
  const schemaPath = await findFirst(serviceDir, [
12966
- import_node_path49.default.join("prisma", "schema.prisma"),
13053
+ import_node_path50.default.join("prisma", "schema.prisma"),
12967
13054
  "schema.prisma"
12968
13055
  ]);
12969
13056
  if (!schemaPath) return [];
@@ -12974,27 +13061,27 @@ async function prismaForeignKeys(serviceDir) {
12974
13061
 
12975
13062
  // src/extract/calls/activerecord.ts
12976
13063
  init_cjs_shims();
12977
- var import_node_path50 = __toESM(require("path"), 1);
12978
- var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
13064
+ var import_node_path51 = __toESM(require("path"), 1);
13065
+ var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12979
13066
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
12980
- var import_types39 = require("@neat.is/types");
13067
+ var import_types40 = require("@neat.is/types");
12981
13068
  var AR_SCHEMA_RE = /ActiveRecord::Schema/;
12982
- var PARSE_CHUNK8 = 16384;
13069
+ var PARSE_CHUNK9 = 16384;
12983
13070
  function makeRubyParser2() {
12984
- const p = new import_tree_sitter12.default();
13071
+ const p = new import_tree_sitter13.default();
12985
13072
  p.setLanguage(import_tree_sitter_ruby3.default);
12986
13073
  return p;
12987
13074
  }
12988
- function parseSource8(parser, source) {
13075
+ function parseSource9(parser, source) {
12989
13076
  return parser.parse(
12990
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK8)
13077
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK9)
12991
13078
  );
12992
13079
  }
12993
- function walk5(node, visit) {
13080
+ function walk6(node, visit) {
12994
13081
  visit(node);
12995
13082
  for (let i = 0; i < node.namedChildCount; i++) {
12996
13083
  const c = node.namedChild(i);
12997
- if (c) walk5(c, visit);
13084
+ if (c) walk6(c, visit);
12998
13085
  }
12999
13086
  }
13000
13087
  function rubyLiteral2(node) {
@@ -13165,24 +13252,24 @@ function readCreateTable(call) {
13165
13252
  }
13166
13253
  function railsSchemaEndpointsFromFile(file, serviceDir) {
13167
13254
  if (!AR_SCHEMA_RE.test(file.content)) return [];
13168
- const tree = parseSource8(makeRubyParser2(), file.content);
13255
+ const tree = parseSource9(makeRubyParser2(), file.content);
13169
13256
  const out = [];
13170
13257
  const seen = /* @__PURE__ */ new Set();
13171
- walk5(tree.rootNode, (node) => {
13258
+ walk6(tree.rootNode, (node) => {
13172
13259
  if (node.type !== "call") return;
13173
13260
  if (blockCallMethod(node) !== "create_table") return;
13174
13261
  const table = readCreateTable(node);
13175
13262
  if (!table || seen.has(table.name)) return;
13176
13263
  seen.add(table.name);
13177
13264
  out.push({
13178
- infraId: (0, import_types39.infraId)("sql-table", table.name),
13265
+ infraId: (0, import_types40.infraId)("sql-table", table.name),
13179
13266
  name: table.name,
13180
13267
  kind: "sql-table",
13181
13268
  edgeType: "CALLS",
13182
13269
  confidenceKind: "structural",
13183
13270
  ...table.columns.length > 0 ? { columns: table.columns } : {},
13184
13271
  evidence: {
13185
- file: import_node_path50.default.relative(serviceDir, file.path),
13272
+ file: import_node_path51.default.relative(serviceDir, file.path),
13186
13273
  line: table.line,
13187
13274
  snippet: snippet(file.content, table.line)
13188
13275
  }
@@ -13192,7 +13279,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
13192
13279
  }
13193
13280
  function railsSchemaForeignKeys(file, serviceDir) {
13194
13281
  if (!AR_SCHEMA_RE.test(file.content)) return [];
13195
- const tree = parseSource8(makeRubyParser2(), file.content);
13282
+ const tree = parseSource9(makeRubyParser2(), file.content);
13196
13283
  const out = [];
13197
13284
  const seen = /* @__PURE__ */ new Set();
13198
13285
  const emit = (childTable, parentTable, line) => {
@@ -13204,13 +13291,13 @@ function railsSchemaForeignKeys(file, serviceDir) {
13204
13291
  childTable,
13205
13292
  parentTable,
13206
13293
  evidence: {
13207
- file: import_node_path50.default.relative(serviceDir, file.path),
13294
+ file: import_node_path51.default.relative(serviceDir, file.path),
13208
13295
  line,
13209
13296
  snippet: snippet(file.content, line)
13210
13297
  }
13211
13298
  });
13212
13299
  };
13213
- walk5(tree.rootNode, (node) => {
13300
+ walk6(tree.rootNode, (node) => {
13214
13301
  if (node.type !== "call") return;
13215
13302
  const method = blockCallMethod(node);
13216
13303
  if (method === "add_foreign_key") {
@@ -13271,23 +13358,23 @@ function modelTable(cls) {
13271
13358
  return tableize(nameNode.text);
13272
13359
  }
13273
13360
  function railsModelEndpointsFromFile(file, serviceDir) {
13274
- const tree = parseSource8(makeRubyParser2(), file.content);
13361
+ const tree = parseSource9(makeRubyParser2(), file.content);
13275
13362
  const out = [];
13276
13363
  const seen = /* @__PURE__ */ new Set();
13277
- walk5(tree.rootNode, (node) => {
13364
+ walk6(tree.rootNode, (node) => {
13278
13365
  if (node.type !== "class" || !isActiveRecordModel(node)) return;
13279
13366
  const table = modelTable(node);
13280
13367
  if (!table || seen.has(table)) return;
13281
13368
  seen.add(table);
13282
13369
  const line = node.startPosition.row + 1;
13283
13370
  out.push({
13284
- infraId: (0, import_types39.infraId)("sql-table", table),
13371
+ infraId: (0, import_types40.infraId)("sql-table", table),
13285
13372
  name: table,
13286
13373
  kind: "sql-table",
13287
13374
  edgeType: "CALLS",
13288
13375
  confidenceKind: "verified-call-site",
13289
13376
  evidence: {
13290
- file: import_node_path50.default.relative(serviceDir, file.path),
13377
+ file: import_node_path51.default.relative(serviceDir, file.path),
13291
13378
  line,
13292
13379
  snippet: snippet(file.content, line)
13293
13380
  }
@@ -13297,10 +13384,10 @@ function railsModelEndpointsFromFile(file, serviceDir) {
13297
13384
  }
13298
13385
  var AR_ASSOCIATIONS = /* @__PURE__ */ new Set(["belongs_to", "has_many", "has_one"]);
13299
13386
  function railsModelForeignKeys(file, serviceDir) {
13300
- const tree = parseSource8(makeRubyParser2(), file.content);
13387
+ const tree = parseSource9(makeRubyParser2(), file.content);
13301
13388
  const out = [];
13302
13389
  const seen = /* @__PURE__ */ new Set();
13303
- walk5(tree.rootNode, (node) => {
13390
+ walk6(tree.rootNode, (node) => {
13304
13391
  if (node.type !== "class" || !isActiveRecordModel(node)) return;
13305
13392
  const thisTable = modelTable(node);
13306
13393
  if (!thisTable) return;
@@ -13324,7 +13411,7 @@ function railsModelForeignKeys(file, serviceDir) {
13324
13411
  childTable,
13325
13412
  parentTable,
13326
13413
  evidence: {
13327
- file: import_node_path50.default.relative(serviceDir, file.path),
13414
+ file: import_node_path51.default.relative(serviceDir, file.path),
13328
13415
  line,
13329
13416
  snippet: snippet(file.content, line)
13330
13417
  }
@@ -13336,27 +13423,27 @@ function railsModelForeignKeys(file, serviceDir) {
13336
13423
 
13337
13424
  // src/extract/calls/eloquent.ts
13338
13425
  init_cjs_shims();
13339
- var import_node_path51 = __toESM(require("path"), 1);
13340
- var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
13426
+ var import_node_path52 = __toESM(require("path"), 1);
13427
+ var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13341
13428
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
13342
- var import_types40 = require("@neat.is/types");
13429
+ var import_types41 = require("@neat.is/types");
13343
13430
  var LARAVEL_SCHEMA_RE = /\bSchema::(create|createIfNotExists|table)\b/;
13344
- var PARSE_CHUNK9 = 16384;
13431
+ var PARSE_CHUNK10 = 16384;
13345
13432
  function makePhpParser2() {
13346
- const p = new import_tree_sitter13.default();
13433
+ const p = new import_tree_sitter14.default();
13347
13434
  p.setLanguage(import_tree_sitter_php3.default.php_only);
13348
13435
  return p;
13349
13436
  }
13350
- function parseSource9(parser, source) {
13437
+ function parseSource10(parser, source) {
13351
13438
  return parser.parse(
13352
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK9)
13439
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
13353
13440
  );
13354
13441
  }
13355
- function walk6(node, visit) {
13442
+ function walk7(node, visit) {
13356
13443
  visit(node);
13357
13444
  for (let i = 0; i < node.namedChildCount; i++) {
13358
13445
  const c = node.namedChild(i);
13359
- if (c) walk6(c, visit);
13446
+ if (c) walk7(c, visit);
13360
13447
  }
13361
13448
  }
13362
13449
  function phpStaticString2(node) {
@@ -13567,9 +13654,9 @@ function columnsFromCall(chain, blueprintVar) {
13567
13654
  }
13568
13655
  function laravelMigrationEndpointsFromFile(file, serviceDir) {
13569
13656
  if (!LARAVEL_SCHEMA_RE.test(file.content)) return [];
13570
- const tree = parseSource9(makePhpParser2(), file.content);
13657
+ const tree = parseSource10(makePhpParser2(), file.content);
13571
13658
  const out = [];
13572
- walk6(tree.rootNode, (node) => {
13659
+ walk7(tree.rootNode, (node) => {
13573
13660
  if (!isSchemaCall(node)) return;
13574
13661
  const bp = readBlueprint(node);
13575
13662
  if (!bp) return;
@@ -13586,14 +13673,14 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13586
13673
  }
13587
13674
  }
13588
13675
  out.push({
13589
- infraId: (0, import_types40.infraId)("sql-table", bp.table),
13676
+ infraId: (0, import_types41.infraId)("sql-table", bp.table),
13590
13677
  name: bp.table,
13591
13678
  kind: "sql-table",
13592
13679
  edgeType: "CALLS",
13593
13680
  confidenceKind: "structural",
13594
13681
  ...columns.length > 0 ? { columns } : {},
13595
13682
  evidence: {
13596
- file: import_node_path51.default.relative(serviceDir, file.path),
13683
+ file: import_node_path52.default.relative(serviceDir, file.path),
13597
13684
  line: bp.line,
13598
13685
  snippet: snippet(file.content, bp.line)
13599
13686
  }
@@ -13603,7 +13690,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13603
13690
  }
13604
13691
  function laravelMigrationForeignKeys(file, serviceDir) {
13605
13692
  if (!LARAVEL_SCHEMA_RE.test(file.content)) return [];
13606
- const tree = parseSource9(makePhpParser2(), file.content);
13693
+ const tree = parseSource10(makePhpParser2(), file.content);
13607
13694
  const out = [];
13608
13695
  const seen = /* @__PURE__ */ new Set();
13609
13696
  const emit = (childTable, parentTable, line) => {
@@ -13615,13 +13702,13 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13615
13702
  childTable,
13616
13703
  parentTable,
13617
13704
  evidence: {
13618
- file: import_node_path51.default.relative(serviceDir, file.path),
13705
+ file: import_node_path52.default.relative(serviceDir, file.path),
13619
13706
  line,
13620
13707
  snippet: snippet(file.content, line)
13621
13708
  }
13622
13709
  });
13623
13710
  };
13624
- walk6(tree.rootNode, (node) => {
13711
+ walk7(tree.rootNode, (node) => {
13625
13712
  if (!isSchemaCall(node)) return;
13626
13713
  const bp = readBlueprint(node);
13627
13714
  if (!bp || !bp.closureBody) return;
@@ -13719,23 +13806,23 @@ function walkWithinClass(cls, visit) {
13719
13806
  recur(body);
13720
13807
  }
13721
13808
  function laravelModelEndpointsFromFile(file, serviceDir) {
13722
- const tree = parseSource9(makePhpParser2(), file.content);
13809
+ const tree = parseSource10(makePhpParser2(), file.content);
13723
13810
  const out = [];
13724
13811
  const seen = /* @__PURE__ */ new Set();
13725
- walk6(tree.rootNode, (node) => {
13812
+ walk7(tree.rootNode, (node) => {
13726
13813
  if (node.type !== "class_declaration" || !isEloquentModel(node)) return;
13727
13814
  const table = modelTable2(node);
13728
13815
  if (!table || seen.has(table)) return;
13729
13816
  seen.add(table);
13730
13817
  const line = node.startPosition.row + 1;
13731
13818
  out.push({
13732
- infraId: (0, import_types40.infraId)("sql-table", table),
13819
+ infraId: (0, import_types41.infraId)("sql-table", table),
13733
13820
  name: table,
13734
13821
  kind: "sql-table",
13735
13822
  edgeType: "CALLS",
13736
13823
  confidenceKind: "verified-call-site",
13737
13824
  evidence: {
13738
- file: import_node_path51.default.relative(serviceDir, file.path),
13825
+ file: import_node_path52.default.relative(serviceDir, file.path),
13739
13826
  line,
13740
13827
  snippet: snippet(file.content, line)
13741
13828
  }
@@ -13745,10 +13832,10 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
13745
13832
  }
13746
13833
  var ELOQUENT_RELATIONS = /* @__PURE__ */ new Set(["belongsTo", "hasMany", "hasOne"]);
13747
13834
  function laravelModelForeignKeys(file, serviceDir) {
13748
- const tree = parseSource9(makePhpParser2(), file.content);
13835
+ const tree = parseSource10(makePhpParser2(), file.content);
13749
13836
  const out = [];
13750
13837
  const seen = /* @__PURE__ */ new Set();
13751
- walk6(tree.rootNode, (node) => {
13838
+ walk7(tree.rootNode, (node) => {
13752
13839
  if (node.type !== "class_declaration" || !isEloquentModel(node)) return;
13753
13840
  const thisTable = modelTable2(node);
13754
13841
  if (!thisTable) return;
@@ -13771,7 +13858,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13771
13858
  childTable,
13772
13859
  parentTable,
13773
13860
  evidence: {
13774
- file: import_node_path51.default.relative(serviceDir, file.path),
13861
+ file: import_node_path52.default.relative(serviceDir, file.path),
13775
13862
  line,
13776
13863
  snippet: snippet(file.content, line)
13777
13864
  }
@@ -13783,12 +13870,12 @@ function laravelModelForeignKeys(file, serviceDir) {
13783
13870
 
13784
13871
  // src/extract/calls/go.ts
13785
13872
  init_cjs_shims();
13786
- var import_node_path54 = __toESM(require("path"), 1);
13787
- var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13873
+ var import_node_path55 = __toESM(require("path"), 1);
13874
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13788
13875
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
13789
- var import_types41 = require("@neat.is/types");
13876
+ var import_types42 = require("@neat.is/types");
13790
13877
  init_otel();
13791
- var PARSE_CHUNK10 = 16384;
13878
+ var PARSE_CHUNK11 = 16384;
13792
13879
  var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
13793
13880
  "Query",
13794
13881
  "QueryContext",
@@ -13814,20 +13901,20 @@ var SQLX_METHODS = /* @__PURE__ */ new Set([
13814
13901
  var DATABASE_SQL_IMPORT = "database/sql";
13815
13902
  var SQLX_IMPORT = "github.com/jmoiron/sqlx";
13816
13903
  function makeGoParser3() {
13817
- const p = new import_tree_sitter14.default();
13904
+ const p = new import_tree_sitter15.default();
13818
13905
  p.setLanguage(import_tree_sitter_go4.default);
13819
13906
  return p;
13820
13907
  }
13821
- function parseSource10(parser, source) {
13908
+ function parseSource11(parser, source) {
13822
13909
  return parser.parse(
13823
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK10)
13910
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
13824
13911
  );
13825
13912
  }
13826
- function walk7(node, visit) {
13913
+ function walk8(node, visit) {
13827
13914
  visit(node);
13828
13915
  for (let i = 0; i < node.namedChildCount; i++) {
13829
13916
  const child = node.namedChild(i);
13830
- if (child) walk7(child, visit);
13917
+ if (child) walk8(child, visit);
13831
13918
  }
13832
13919
  }
13833
13920
  function goStringLiteralValue(node) {
@@ -13840,7 +13927,7 @@ function goStringLiteralValue(node) {
13840
13927
  }
13841
13928
  function goImportsAny(root, names) {
13842
13929
  let found = false;
13843
- walk7(root, (node) => {
13930
+ walk8(root, (node) => {
13844
13931
  if (found || node.type !== "import_spec") return;
13845
13932
  for (let i = 0; i < node.namedChildCount; i++) {
13846
13933
  const value = goStringLiteralValue(node.namedChild(i));
@@ -13858,14 +13945,14 @@ function firstStringLiteralArg(argsNode) {
13858
13945
  return null;
13859
13946
  }
13860
13947
  function goSqlEndpointsFromFile(file, serviceDir) {
13861
- if (import_node_path54.default.extname(file.path) !== ".go") return [];
13948
+ if (import_node_path55.default.extname(file.path) !== ".go") return [];
13862
13949
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
13863
- const tree = parseSource10(makeGoParser3(), file.content);
13950
+ const tree = parseSource11(makeGoParser3(), file.content);
13864
13951
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
13865
13952
  const importsSqlx = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([SQLX_IMPORT]));
13866
13953
  if (!importsDatabaseSql && !importsSqlx) return [];
13867
13954
  const out = [];
13868
- walk7(tree.rootNode, (node) => {
13955
+ walk8(tree.rootNode, (node) => {
13869
13956
  if (node.type !== "call_expression") return;
13870
13957
  const fn = node.childForFieldName("function");
13871
13958
  if (fn?.type !== "selector_expression") return;
@@ -13880,14 +13967,14 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13880
13967
  const columns = columnsFromSqlStatement(sql);
13881
13968
  const line = node.startPosition.row + 1;
13882
13969
  out.push({
13883
- infraId: (0, import_types41.infraId)("sql-table", table),
13970
+ infraId: (0, import_types42.infraId)("sql-table", table),
13884
13971
  name: table,
13885
13972
  kind: "sql-table",
13886
13973
  edgeType: "CALLS",
13887
13974
  confidenceKind: "verified-call-site",
13888
13975
  ...columns.length > 0 ? { columns } : {},
13889
13976
  evidence: {
13890
- file: toPosix(import_node_path54.default.relative(serviceDir, file.path)),
13977
+ file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
13891
13978
  line,
13892
13979
  snippet: snippet(file.content, line)
13893
13980
  }
@@ -13898,27 +13985,27 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13898
13985
 
13899
13986
  // src/extract/calls/gorm.ts
13900
13987
  init_cjs_shims();
13901
- var import_node_path55 = __toESM(require("path"), 1);
13902
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13988
+ var import_node_path56 = __toESM(require("path"), 1);
13989
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13903
13990
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
13904
- var import_types42 = require("@neat.is/types");
13991
+ var import_types43 = require("@neat.is/types");
13905
13992
  var GORM_IMPORT_RE = /gorm\.io\/gorm/;
13906
- var PARSE_CHUNK11 = 16384;
13993
+ var PARSE_CHUNK12 = 16384;
13907
13994
  function makeGoParser4() {
13908
- const p = new import_tree_sitter15.default();
13995
+ const p = new import_tree_sitter16.default();
13909
13996
  p.setLanguage(import_tree_sitter_go5.default);
13910
13997
  return p;
13911
13998
  }
13912
- function parseSource11(parser, source) {
13999
+ function parseSource12(parser, source) {
13913
14000
  return parser.parse(
13914
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
14001
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
13915
14002
  );
13916
14003
  }
13917
- function walk8(node, visit) {
14004
+ function walk9(node, visit) {
13918
14005
  visit(node);
13919
14006
  for (let i = 0; i < node.namedChildCount; i++) {
13920
14007
  const c = node.namedChild(i);
13921
- if (c) walk8(c, visit);
14008
+ if (c) walk9(c, visit);
13922
14009
  }
13923
14010
  }
13924
14011
  var COMMON_INITIALISMS = [
@@ -14151,7 +14238,7 @@ function readField(fieldDecl) {
14151
14238
  }
14152
14239
  function collectStructs(tree) {
14153
14240
  const structs = /* @__PURE__ */ new Map();
14154
- walk8(tree.rootNode, (node) => {
14241
+ walk9(tree.rootNode, (node) => {
14155
14242
  if (node.type !== "type_spec") return;
14156
14243
  const nameNode = node.childForFieldName("name");
14157
14244
  const typeNode = node.childForFieldName("type");
@@ -14200,7 +14287,7 @@ function compositeStructName(arg) {
14200
14287
  }
14201
14288
  function collectCallModels(tree) {
14202
14289
  const models = /* @__PURE__ */ new Set();
14203
- walk8(tree.rootNode, (node) => {
14290
+ walk9(tree.rootNode, (node) => {
14204
14291
  if (node.type !== "call_expression") return;
14205
14292
  const fn = node.childForFieldName("function");
14206
14293
  if (fn?.type !== "selector_expression") return;
@@ -14220,7 +14307,7 @@ function collectCallModels(tree) {
14220
14307
  function collectTableNameOverrides(tree) {
14221
14308
  const overrides = /* @__PURE__ */ new Map();
14222
14309
  const declarers = /* @__PURE__ */ new Set();
14223
- walk8(tree.rootNode, (node) => {
14310
+ walk9(tree.rootNode, (node) => {
14224
14311
  if (node.type !== "method_declaration") return;
14225
14312
  if (node.childForFieldName("name")?.text !== "TableName") return;
14226
14313
  const receiver = node.childForFieldName("receiver");
@@ -14237,7 +14324,7 @@ function collectTableNameOverrides(tree) {
14237
14324
  const body = node.childForFieldName("body");
14238
14325
  if (!body) return;
14239
14326
  let literal = null;
14240
- walk8(body, (n) => {
14327
+ walk9(body, (n) => {
14241
14328
  if (literal !== null) return;
14242
14329
  if (n.type !== "return_statement") return;
14243
14330
  const exprList = n.namedChild(0);
@@ -14331,9 +14418,9 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
14331
14418
  seen.delete(struct.name);
14332
14419
  }
14333
14420
  function gormEndpointsFromFile(file, serviceDir) {
14334
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14421
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
14335
14422
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14336
- const tree = parseSource11(makeGoParser4(), file.content);
14423
+ const tree = parseSource12(makeGoParser4(), file.content);
14337
14424
  const { structs, models, tableFor } = analyze(tree);
14338
14425
  const out = [];
14339
14426
  const seenTables = /* @__PURE__ */ new Set();
@@ -14346,14 +14433,14 @@ function gormEndpointsFromFile(file, serviceDir) {
14346
14433
  const columns = [];
14347
14434
  collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
14348
14435
  out.push({
14349
- infraId: (0, import_types42.infraId)("sql-table", table),
14436
+ infraId: (0, import_types43.infraId)("sql-table", table),
14350
14437
  name: table,
14351
14438
  kind: "sql-table",
14352
14439
  edgeType: "CALLS",
14353
14440
  confidenceKind: "structural",
14354
14441
  ...columns.length > 0 ? { columns } : {},
14355
14442
  evidence: {
14356
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14443
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14357
14444
  line: struct.line,
14358
14445
  snippet: snippet(file.content, struct.line)
14359
14446
  }
@@ -14362,9 +14449,9 @@ function gormEndpointsFromFile(file, serviceDir) {
14362
14449
  return out;
14363
14450
  }
14364
14451
  function gormForeignKeys(file, serviceDir) {
14365
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14452
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
14366
14453
  if (!GORM_IMPORT_RE.test(file.content)) return [];
14367
- const tree = parseSource11(makeGoParser4(), file.content);
14454
+ const tree = parseSource12(makeGoParser4(), file.content);
14368
14455
  const { structs, models, tableFor } = analyze(tree);
14369
14456
  const out = [];
14370
14457
  const seen = /* @__PURE__ */ new Set();
@@ -14377,7 +14464,7 @@ function gormForeignKeys(file, serviceDir) {
14377
14464
  childTable,
14378
14465
  parentTable,
14379
14466
  evidence: {
14380
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14467
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14381
14468
  line,
14382
14469
  snippet: snippet(file.content, line)
14383
14470
  }
@@ -14414,27 +14501,27 @@ function gormForeignKeys(file, serviceDir) {
14414
14501
 
14415
14502
  // src/extract/calls/efcore.ts
14416
14503
  init_cjs_shims();
14417
- var import_node_path56 = __toESM(require("path"), 1);
14418
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14504
+ var import_node_path57 = __toESM(require("path"), 1);
14505
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
14419
14506
  var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14420
- var import_types43 = require("@neat.is/types");
14507
+ var import_types44 = require("@neat.is/types");
14421
14508
  var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
14422
- var PARSE_CHUNK12 = 16384;
14509
+ var PARSE_CHUNK13 = 16384;
14423
14510
  function makeCsParser() {
14424
- const p = new import_tree_sitter16.default();
14511
+ const p = new import_tree_sitter17.default();
14425
14512
  p.setLanguage(import_tree_sitter_c_sharp2.default);
14426
14513
  return p;
14427
14514
  }
14428
- function parseSource12(parser, source) {
14515
+ function parseSource13(parser, source) {
14429
14516
  return parser.parse(
14430
- (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
14517
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK13)
14431
14518
  );
14432
14519
  }
14433
- function walk9(node, visit) {
14520
+ function walk10(node, visit) {
14434
14521
  visit(node);
14435
14522
  for (let i = 0; i < node.namedChildCount; i++) {
14436
14523
  const c = node.namedChild(i);
14437
- if (c) walk9(c, visit);
14524
+ if (c) walk10(c, visit);
14438
14525
  }
14439
14526
  }
14440
14527
  function firstChildOfType(node, type) {
@@ -14493,28 +14580,28 @@ function tableFromToTable(call) {
14493
14580
  return value ? csStringLiteral(value) : null;
14494
14581
  }
14495
14582
  function efcoreEndpointsFromFile(file, serviceDir) {
14496
- if (import_node_path56.default.extname(file.path) !== ".cs") return [];
14583
+ if (import_node_path57.default.extname(file.path) !== ".cs") return [];
14497
14584
  if (!EFCORE_GATE.test(file.content)) return [];
14498
- const tree = parseSource12(makeCsParser(), file.content);
14585
+ const tree = parseSource13(makeCsParser(), file.content);
14499
14586
  const out = [];
14500
14587
  const seen = /* @__PURE__ */ new Set();
14501
14588
  const push = (name, line) => {
14502
14589
  if (!name || seen.has(name)) return;
14503
14590
  seen.add(name);
14504
14591
  out.push({
14505
- infraId: (0, import_types43.infraId)("sql-table", name),
14592
+ infraId: (0, import_types44.infraId)("sql-table", name),
14506
14593
  name,
14507
14594
  kind: "sql-table",
14508
14595
  edgeType: "CALLS",
14509
14596
  confidenceKind: "structural",
14510
14597
  evidence: {
14511
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14598
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
14512
14599
  line,
14513
14600
  snippet: snippet(file.content, line)
14514
14601
  }
14515
14602
  });
14516
14603
  };
14517
- walk9(tree.rootNode, (node) => {
14604
+ walk10(tree.rootNode, (node) => {
14518
14605
  if (node.type === "attribute") {
14519
14606
  const table = tableFromAttribute(node);
14520
14607
  if (table) push(table, node.startPosition.row + 1);
@@ -14532,11 +14619,11 @@ function efcoreEndpointsFromFile(file, serviceDir) {
14532
14619
  function edgeTypeFromEndpoint(ep) {
14533
14620
  switch (ep.edgeType) {
14534
14621
  case "PUBLISHES_TO":
14535
- return import_types44.EdgeType.PUBLISHES_TO;
14622
+ return import_types45.EdgeType.PUBLISHES_TO;
14536
14623
  case "CONSUMES_FROM":
14537
- return import_types44.EdgeType.CONSUMES_FROM;
14624
+ return import_types45.EdgeType.CONSUMES_FROM;
14538
14625
  default:
14539
- return import_types44.EdgeType.CALLS;
14626
+ return import_types45.EdgeType.CALLS;
14540
14627
  }
14541
14628
  }
14542
14629
  function isAwsKind(kind) {
@@ -14591,6 +14678,11 @@ async function addExternalEndpointEdges(graph, services) {
14591
14678
  } catch (err) {
14592
14679
  recordExtractionError("efcore data-axis extraction", file.path, err);
14593
14680
  }
14681
+ try {
14682
+ endpoints.push(...socketEndpointsFromFile(file, service.dir));
14683
+ } catch (err) {
14684
+ recordExtractionError("python socket call extraction", file.path, err);
14685
+ }
14594
14686
  }
14595
14687
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
14596
14688
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -14601,7 +14693,7 @@ async function addExternalEndpointEdges(graph, services) {
14601
14693
  if (!graph.hasNode(ep.infraId)) {
14602
14694
  const node = {
14603
14695
  id: ep.infraId,
14604
- type: import_types44.NodeType.InfraNode,
14696
+ type: import_types45.NodeType.InfraNode,
14605
14697
  name: ep.name,
14606
14698
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
14607
14699
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -14614,21 +14706,21 @@ async function addExternalEndpointEdges(graph, services) {
14614
14706
  }
14615
14707
  if (ep.columns && ep.columns.length > 0) {
14616
14708
  const node = graph.getNodeAttributes(ep.infraId);
14617
- if (node.type === import_types44.NodeType.InfraNode) {
14709
+ if (node.type === import_types45.NodeType.InfraNode) {
14618
14710
  graph.replaceNodeAttributes(ep.infraId, {
14619
14711
  ...node,
14620
14712
  columns: foldColumns(
14621
14713
  node.columns,
14622
14714
  ep.columns,
14623
- import_types44.Provenance.EXTRACTED,
14624
- (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
14715
+ import_types45.Provenance.EXTRACTED,
14716
+ (0, import_types45.confidenceForExtracted)(ep.confidenceKind)
14625
14717
  )
14626
14718
  });
14627
14719
  }
14628
14720
  }
14629
14721
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
14630
14722
  const node = graph.getNodeAttributes(ep.infraId);
14631
- if (node.type === import_types44.NodeType.InfraNode) {
14723
+ if (node.type === import_types45.NodeType.InfraNode) {
14632
14724
  graph.replaceNodeAttributes(ep.infraId, {
14633
14725
  ...node,
14634
14726
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -14636,7 +14728,7 @@ async function addExternalEndpointEdges(graph, services) {
14636
14728
  }
14637
14729
  }
14638
14730
  const edgeType = edgeTypeFromEndpoint(ep);
14639
- const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
14731
+ const confidence = (0, import_types45.confidenceForExtracted)(ep.confidenceKind);
14640
14732
  const relFile = toPosix(ep.evidence.file);
14641
14733
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
14642
14734
  graph,
@@ -14646,7 +14738,7 @@ async function addExternalEndpointEdges(graph, services) {
14646
14738
  );
14647
14739
  nodesAdded += n;
14648
14740
  edgesAdded += e;
14649
- if (!(0, import_types44.passesExtractedFloor)(confidence)) {
14741
+ if (!(0, import_types45.passesExtractedFloor)(confidence)) {
14650
14742
  noteExtractedDropped({
14651
14743
  source: fileNodeId,
14652
14744
  target: ep.infraId,
@@ -14666,7 +14758,7 @@ async function addExternalEndpointEdges(graph, services) {
14666
14758
  source: fileNodeId,
14667
14759
  target: ep.infraId,
14668
14760
  type: edgeType,
14669
- provenance: import_types44.Provenance.EXTRACTED,
14761
+ provenance: import_types45.Provenance.EXTRACTED,
14670
14762
  confidence,
14671
14763
  evidence: ep.evidence
14672
14764
  };
@@ -14689,7 +14781,7 @@ async function addCallEdges(graph, services) {
14689
14781
 
14690
14782
  // src/extract/table-edges.ts
14691
14783
  init_cjs_shims();
14692
- var import_types45 = require("@neat.is/types");
14784
+ var import_types46 = require("@neat.is/types");
14693
14785
  async function addTableEdges(graph, services) {
14694
14786
  let nodesAdded = 0;
14695
14787
  let edgesAdded = 0;
@@ -14717,20 +14809,20 @@ async function addTableEdges(graph, services) {
14717
14809
  }
14718
14810
  refs.push(...modelRefs);
14719
14811
  for (const ref of refs) {
14720
- const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14721
- const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
14812
+ const childId = (0, import_types46.infraId)("sql-table", ref.childTable);
14813
+ const parentId = (0, import_types46.infraId)("sql-table", ref.parentTable);
14722
14814
  if (childId === parentId) continue;
14723
14815
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
14724
14816
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
14725
- const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
14817
+ const edgeId = (0, import_types46.extractedEdgeId)(childId, parentId, import_types46.EdgeType.REFERENCES);
14726
14818
  if (graph.hasEdge(edgeId)) continue;
14727
14819
  const edge = {
14728
14820
  id: edgeId,
14729
14821
  source: childId,
14730
14822
  target: parentId,
14731
- type: import_types45.EdgeType.REFERENCES,
14732
- provenance: import_types45.Provenance.EXTRACTED,
14733
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14823
+ type: import_types46.EdgeType.REFERENCES,
14824
+ provenance: import_types46.Provenance.EXTRACTED,
14825
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
14734
14826
  evidence: ref.evidence
14735
14827
  };
14736
14828
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -14743,7 +14835,7 @@ function ensureTableNode(graph, id, name) {
14743
14835
  if (graph.hasNode(id)) return 0;
14744
14836
  const node = {
14745
14837
  id,
14746
- type: import_types45.NodeType.InfraNode,
14838
+ type: import_types46.NodeType.InfraNode,
14747
14839
  name,
14748
14840
  provider: "self",
14749
14841
  kind: "sql-table"
@@ -14757,16 +14849,16 @@ init_cjs_shims();
14757
14849
 
14758
14850
  // src/extract/infra/docker-compose.ts
14759
14851
  init_cjs_shims();
14760
- var import_node_path57 = __toESM(require("path"), 1);
14761
- var import_types47 = require("@neat.is/types");
14852
+ var import_node_path58 = __toESM(require("path"), 1);
14853
+ var import_types48 = require("@neat.is/types");
14762
14854
 
14763
14855
  // src/extract/infra/shared.ts
14764
14856
  init_cjs_shims();
14765
- var import_types46 = require("@neat.is/types");
14857
+ var import_types47 = require("@neat.is/types");
14766
14858
  function makeInfraNode(kind, name, provider = "self", extras) {
14767
14859
  return {
14768
- id: (0, import_types46.infraId)(kind, name),
14769
- type: import_types46.NodeType.InfraNode,
14860
+ id: (0, import_types47.infraId)(kind, name),
14861
+ type: import_types47.NodeType.InfraNode,
14770
14862
  name,
14771
14863
  provider,
14772
14864
  kind,
@@ -14810,8 +14902,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
14810
14902
  source: anchorId,
14811
14903
  target: node.id,
14812
14904
  type: edgeType,
14813
- provenance: import_types46.Provenance.EXTRACTED,
14814
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14905
+ provenance: import_types47.Provenance.EXTRACTED,
14906
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
14815
14907
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14816
14908
  };
14817
14909
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14828,7 +14920,7 @@ function dependsOnList(value) {
14828
14920
  }
14829
14921
  function serviceNameToServiceNode(name, services) {
14830
14922
  for (const s of services) {
14831
- if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
14923
+ if (s.node.name === name || import_node_path58.default.basename(s.dir) === name) return s.node.id;
14832
14924
  }
14833
14925
  return null;
14834
14926
  }
@@ -14837,7 +14929,7 @@ async function addComposeInfra(graph, scanPath, services) {
14837
14929
  let edgesAdded = 0;
14838
14930
  let composePath = null;
14839
14931
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
14840
- const abs = import_node_path57.default.join(scanPath, name);
14932
+ const abs = import_node_path58.default.join(scanPath, name);
14841
14933
  if (await exists(abs)) {
14842
14934
  composePath = abs;
14843
14935
  break;
@@ -14850,13 +14942,13 @@ async function addComposeInfra(graph, scanPath, services) {
14850
14942
  } catch (err) {
14851
14943
  recordExtractionError(
14852
14944
  "infra docker-compose",
14853
- import_node_path57.default.relative(scanPath, composePath),
14945
+ import_node_path58.default.relative(scanPath, composePath),
14854
14946
  err
14855
14947
  );
14856
14948
  return { nodesAdded, edgesAdded };
14857
14949
  }
14858
14950
  if (!compose?.services) return { nodesAdded, edgesAdded };
14859
- const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
14951
+ const evidenceFile = import_node_path58.default.relative(scanPath, composePath).split(import_node_path58.default.sep).join("/");
14860
14952
  const composeNameToNodeId = /* @__PURE__ */ new Map();
14861
14953
  for (const [composeName, svc] of Object.entries(compose.services)) {
14862
14954
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -14878,15 +14970,15 @@ async function addComposeInfra(graph, scanPath, services) {
14878
14970
  for (const dep of dependsOnList(svc.depends_on)) {
14879
14971
  const targetId = composeNameToNodeId.get(dep);
14880
14972
  if (!targetId) continue;
14881
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
14973
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types48.EdgeType.DEPENDS_ON);
14882
14974
  if (graph.hasEdge(edgeId)) continue;
14883
14975
  const edge = {
14884
14976
  id: edgeId,
14885
14977
  source: sourceId,
14886
14978
  target: targetId,
14887
- type: import_types47.EdgeType.DEPENDS_ON,
14888
- provenance: import_types47.Provenance.EXTRACTED,
14889
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
14979
+ type: import_types48.EdgeType.DEPENDS_ON,
14980
+ provenance: import_types48.Provenance.EXTRACTED,
14981
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
14890
14982
  evidence: { file: evidenceFile }
14891
14983
  };
14892
14984
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14898,9 +14990,9 @@ async function addComposeInfra(graph, scanPath, services) {
14898
14990
 
14899
14991
  // src/extract/infra/dockerfile.ts
14900
14992
  init_cjs_shims();
14901
- var import_node_path58 = __toESM(require("path"), 1);
14993
+ var import_node_path59 = __toESM(require("path"), 1);
14902
14994
  var import_node_fs25 = require("fs");
14903
- var import_types48 = require("@neat.is/types");
14995
+ var import_types49 = require("@neat.is/types");
14904
14996
  function readDockerfile(content) {
14905
14997
  let image = null;
14906
14998
  const ports = [];
@@ -14929,7 +15021,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14929
15021
  let nodesAdded = 0;
14930
15022
  let edgesAdded = 0;
14931
15023
  for (const service of services) {
14932
- const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
15024
+ const dockerfilePath = import_node_path59.default.join(service.dir, "Dockerfile");
14933
15025
  if (!await exists(dockerfilePath)) continue;
14934
15026
  let content;
14935
15027
  try {
@@ -14937,7 +15029,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14937
15029
  } catch (err) {
14938
15030
  recordExtractionError(
14939
15031
  "infra dockerfile",
14940
- import_node_path58.default.relative(scanPath, dockerfilePath),
15032
+ import_node_path59.default.relative(scanPath, dockerfilePath),
14941
15033
  err
14942
15034
  );
14943
15035
  continue;
@@ -14949,8 +15041,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14949
15041
  graph.addNode(node.id, node);
14950
15042
  nodesAdded++;
14951
15043
  }
14952
- const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14953
- const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
15044
+ const relDockerfile = toPosix(import_node_path59.default.relative(service.dir, dockerfilePath));
15045
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, dockerfilePath));
14954
15046
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14955
15047
  graph,
14956
15048
  service.pkg.name,
@@ -14959,15 +15051,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14959
15051
  );
14960
15052
  nodesAdded += fn;
14961
15053
  edgesAdded += fe;
14962
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
15054
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types49.EdgeType.RUNS_ON);
14963
15055
  if (!graph.hasEdge(edgeId)) {
14964
15056
  const edge = {
14965
15057
  id: edgeId,
14966
15058
  source: fileNodeId,
14967
15059
  target: node.id,
14968
- type: import_types48.EdgeType.RUNS_ON,
14969
- provenance: import_types48.Provenance.EXTRACTED,
14970
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15060
+ type: import_types49.EdgeType.RUNS_ON,
15061
+ provenance: import_types49.Provenance.EXTRACTED,
15062
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14971
15063
  evidence: {
14972
15064
  file: evidenceFile,
14973
15065
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -14982,15 +15074,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14982
15074
  graph.addNode(portNode.id, portNode);
14983
15075
  nodesAdded++;
14984
15076
  }
14985
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
15077
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types49.EdgeType.CONNECTS_TO);
14986
15078
  if (graph.hasEdge(portEdgeId)) continue;
14987
15079
  const portEdge = {
14988
15080
  id: portEdgeId,
14989
15081
  source: fileNodeId,
14990
15082
  target: portNode.id,
14991
- type: import_types48.EdgeType.CONNECTS_TO,
14992
- provenance: import_types48.Provenance.EXTRACTED,
14993
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15083
+ type: import_types49.EdgeType.CONNECTS_TO,
15084
+ provenance: import_types49.Provenance.EXTRACTED,
15085
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14994
15086
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
14995
15087
  };
14996
15088
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -15003,8 +15095,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
15003
15095
  // src/extract/infra/terraform.ts
15004
15096
  init_cjs_shims();
15005
15097
  var import_node_fs26 = require("fs");
15006
- var import_node_path59 = __toESM(require("path"), 1);
15007
- var import_types49 = require("@neat.is/types");
15098
+ var import_node_path60 = __toESM(require("path"), 1);
15099
+ var import_types50 = require("@neat.is/types");
15008
15100
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
15009
15101
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
15010
15102
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -15014,11 +15106,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
15014
15106
  for (const entry of entries) {
15015
15107
  if (entry.isDirectory()) {
15016
15108
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
15017
- const child = import_node_path59.default.join(start, entry.name);
15109
+ const child = import_node_path60.default.join(start, entry.name);
15018
15110
  if (await isPythonVenvDir(child)) continue;
15019
15111
  out.push(...await walkTfFiles(child, depth + 1, max));
15020
15112
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
15021
- out.push(import_node_path59.default.join(start, entry.name));
15113
+ out.push(import_node_path60.default.join(start, entry.name));
15022
15114
  }
15023
15115
  }
15024
15116
  return out;
@@ -15050,7 +15142,7 @@ async function addTerraformResources(graph, scanPath) {
15050
15142
  const files = await walkTfFiles(scanPath);
15051
15143
  for (const file of files) {
15052
15144
  const content = await import_node_fs26.promises.readFile(file, "utf8");
15053
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
15145
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, file));
15054
15146
  const resources = [];
15055
15147
  const byKey = /* @__PURE__ */ new Map();
15056
15148
  RESOURCE_RE.lastIndex = 0;
@@ -15085,16 +15177,16 @@ async function addTerraformResources(graph, scanPath) {
15085
15177
  if (!target) continue;
15086
15178
  if (seen.has(target.nodeId)) continue;
15087
15179
  seen.add(target.nodeId);
15088
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
15180
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types50.EdgeType.DEPENDS_ON);
15089
15181
  if (graph.hasEdge(edgeId)) continue;
15090
15182
  const line = lineAt2(content, resource.bodyOffset + ref.index);
15091
15183
  const edge = {
15092
15184
  id: edgeId,
15093
15185
  source: resource.nodeId,
15094
15186
  target: target.nodeId,
15095
- type: import_types49.EdgeType.DEPENDS_ON,
15096
- provenance: import_types49.Provenance.EXTRACTED,
15097
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
15187
+ type: import_types50.EdgeType.DEPENDS_ON,
15188
+ provenance: import_types50.Provenance.EXTRACTED,
15189
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
15098
15190
  evidence: { file: evidenceFile, line, snippet: key }
15099
15191
  };
15100
15192
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -15108,8 +15200,9 @@ async function addTerraformResources(graph, scanPath) {
15108
15200
  // src/extract/infra/k8s.ts
15109
15201
  init_cjs_shims();
15110
15202
  var import_node_fs27 = require("fs");
15111
- var import_node_path60 = __toESM(require("path"), 1);
15203
+ var import_node_path61 = __toESM(require("path"), 1);
15112
15204
  var import_yaml3 = require("yaml");
15205
+ var import_types51 = require("@neat.is/types");
15113
15206
  var K8S_KIND_TO_INFRA_KIND = {
15114
15207
  Service: "k8s-service",
15115
15208
  Deployment: "k8s-deployment",
@@ -15119,6 +15212,7 @@ var K8S_KIND_TO_INFRA_KIND = {
15119
15212
  Job: "k8s-job",
15120
15213
  Ingress: "k8s-ingress"
15121
15214
  };
15215
+ var WORKLOAD_KINDS = /* @__PURE__ */ new Set(["Deployment", "StatefulSet", "DaemonSet"]);
15122
15216
  async function walkYamlFiles2(start, depth = 0, max = 5) {
15123
15217
  if (depth > max) return [];
15124
15218
  const out = [];
@@ -15126,17 +15220,18 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
15126
15220
  for (const entry of entries) {
15127
15221
  if (entry.isDirectory()) {
15128
15222
  if (IGNORED_DIRS.has(entry.name)) continue;
15129
- const child = import_node_path60.default.join(start, entry.name);
15223
+ const child = import_node_path61.default.join(start, entry.name);
15130
15224
  if (await isPythonVenvDir(child)) continue;
15131
15225
  out.push(...await walkYamlFiles2(child, depth + 1, max));
15132
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
15133
- out.push(import_node_path60.default.join(start, entry.name));
15226
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path61.default.extname(entry.name))) {
15227
+ out.push(import_node_path61.default.join(start, entry.name));
15134
15228
  }
15135
15229
  }
15136
15230
  return out;
15137
15231
  }
15138
15232
  async function addK8sResources(graph, scanPath) {
15139
15233
  let nodesAdded = 0;
15234
+ let edgesAdded = 0;
15140
15235
  const files = await walkYamlFiles2(scanPath);
15141
15236
  for (const file of files) {
15142
15237
  const content = await import_node_fs27.promises.readFile(file, "utf8");
@@ -15146,31 +15241,63 @@ async function addK8sResources(graph, scanPath) {
15146
15241
  } catch {
15147
15242
  continue;
15148
15243
  }
15244
+ const relFile = toPosix(import_node_path61.default.relative(scanPath, file));
15149
15245
  for (const doc of docs) {
15150
15246
  if (!doc?.kind || !doc.metadata?.name) continue;
15151
15247
  const infraKind = K8S_KIND_TO_INFRA_KIND[doc.kind];
15152
15248
  if (!infraKind) continue;
15153
- const namespaced = doc.metadata.namespace ? `${doc.metadata.namespace}/${doc.metadata.name}` : doc.metadata.name;
15154
- const node = makeInfraNode(infraKind, namespaced, "kubernetes");
15249
+ const name = doc.metadata.name;
15250
+ const isWorkload = WORKLOAD_KINDS.has(doc.kind);
15251
+ const image = isWorkload ? doc.spec?.template?.spec?.containers?.[0]?.image : void 0;
15252
+ const replicas = isWorkload && typeof doc.spec?.replicas === "number" ? doc.spec.replicas : void 0;
15253
+ const base = makeInfraNode(infraKind, name, "kubernetes");
15254
+ const node = {
15255
+ ...base,
15256
+ ...doc.metadata.namespace ? { namespace: doc.metadata.namespace } : {},
15257
+ ...image ? { image } : {},
15258
+ ...replicas !== void 0 ? { replicas } : {}
15259
+ };
15155
15260
  if (!graph.hasNode(node.id)) {
15156
15261
  graph.addNode(node.id, node);
15157
15262
  nodesAdded++;
15158
15263
  }
15264
+ if (isWorkload) {
15265
+ const svcId = (0, import_types51.serviceId)(name);
15266
+ if (graph.hasNode(svcId)) {
15267
+ const svc = graph.getNodeAttributes(svcId);
15268
+ graph.replaceNodeAttributes(svcId, {
15269
+ ...svc,
15270
+ platform: svc.platform ?? "kubernetes",
15271
+ ...image ? { declaredImage: image } : {},
15272
+ ...replicas !== void 0 ? { declaredReplicas: replicas } : {}
15273
+ });
15274
+ edgesAdded += emitPlatformResourceEdge(
15275
+ graph,
15276
+ svcId,
15277
+ import_types51.EdgeType.RUNS_ON,
15278
+ infraKind,
15279
+ name,
15280
+ "kubernetes",
15281
+ relFile,
15282
+ lineContaining(content, image)
15283
+ ).edgesAdded;
15284
+ }
15285
+ }
15159
15286
  }
15160
15287
  }
15161
- return { nodesAdded, edgesAdded: 0 };
15288
+ return { nodesAdded, edgesAdded };
15162
15289
  }
15163
15290
 
15164
15291
  // src/extract/infra/cloudflare.ts
15165
15292
  init_cjs_shims();
15166
15293
  var import_node_fs28 = require("fs");
15167
- var import_node_path61 = __toESM(require("path"), 1);
15294
+ var import_node_path62 = __toESM(require("path"), 1);
15168
15295
  var import_smol_toml3 = require("smol-toml");
15169
- var import_types50 = require("@neat.is/types");
15296
+ var import_types52 = require("@neat.is/types");
15170
15297
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
15171
15298
  async function readWranglerConfig(dir) {
15172
15299
  for (const filename of WRANGLER_FILENAMES) {
15173
- const abs = import_node_path61.default.join(dir, filename);
15300
+ const abs = import_node_path62.default.join(dir, filename);
15174
15301
  if (!await exists(abs)) continue;
15175
15302
  const raw = await import_node_fs28.promises.readFile(abs, "utf8");
15176
15303
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -15214,8 +15341,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
15214
15341
  source: anchorId,
15215
15342
  target: node.id,
15216
15343
  type: edgeType,
15217
- provenance: import_types50.Provenance.EXTRACTED,
15218
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15344
+ provenance: import_types52.Provenance.EXTRACTED,
15345
+ confidence: (0, import_types52.confidenceForExtracted)("structural"),
15219
15346
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
15220
15347
  };
15221
15348
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -15233,11 +15360,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15233
15360
  try {
15234
15361
  read = await readWranglerConfig(service.dir);
15235
15362
  } catch (err) {
15236
- recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
15363
+ recordExtractionError("infra cloudflare", import_node_path62.default.relative(scanPath, service.dir), err);
15237
15364
  continue;
15238
15365
  }
15239
15366
  if (!read || !read.config.name) continue;
15240
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
15367
+ const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, read.relFile)));
15241
15368
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
15242
15369
  }
15243
15370
  for (const worker of discovered) {
@@ -15249,7 +15376,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15249
15376
  }
15250
15377
  let anchorId = service.node.id;
15251
15378
  if (config.main) {
15252
- const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
15379
+ const entryRelPath = toPosix(import_node_path62.default.normalize(config.main));
15253
15380
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
15254
15381
  graph,
15255
15382
  service.pkg.name,
@@ -15276,15 +15403,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15276
15403
  nodesAdded++;
15277
15404
  }
15278
15405
  if (runtimeNode.id !== anchorId) {
15279
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
15406
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types52.EdgeType.RUNS_ON);
15280
15407
  if (!graph.hasEdge(runsOnId)) {
15281
15408
  const edge = {
15282
15409
  id: runsOnId,
15283
15410
  source: anchorId,
15284
15411
  target: runtimeNode.id,
15285
- type: import_types50.EdgeType.RUNS_ON,
15286
- provenance: import_types50.Provenance.EXTRACTED,
15287
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15412
+ type: import_types52.EdgeType.RUNS_ON,
15413
+ provenance: import_types52.Provenance.EXTRACTED,
15414
+ confidence: (0, import_types52.confidenceForExtracted)("structural"),
15288
15415
  evidence: {
15289
15416
  file: evidenceFile,
15290
15417
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -15298,7 +15425,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15298
15425
  const result = addResourceEdge(
15299
15426
  graph,
15300
15427
  anchorId,
15301
- import_types50.EdgeType.CONNECTS_TO,
15428
+ import_types52.EdgeType.CONNECTS_TO,
15302
15429
  "cloudflare-route",
15303
15430
  route,
15304
15431
  evidenceFile,
@@ -15322,7 +15449,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15322
15449
  const result = addResourceEdge(
15323
15450
  graph,
15324
15451
  anchorId,
15325
- import_types50.EdgeType.DEPENDS_ON,
15452
+ import_types52.EdgeType.DEPENDS_ON,
15326
15453
  group.kind,
15327
15454
  name,
15328
15455
  evidenceFile,
@@ -15336,7 +15463,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15336
15463
  const result = addResourceEdge(
15337
15464
  graph,
15338
15465
  anchorId,
15339
- import_types50.EdgeType.DEPENDS_ON,
15466
+ import_types52.EdgeType.DEPENDS_ON,
15340
15467
  "cloudflare-cron",
15341
15468
  cron,
15342
15469
  evidenceFile,
@@ -15349,7 +15476,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15349
15476
  const result = addResourceEdge(
15350
15477
  graph,
15351
15478
  anchorId,
15352
- import_types50.EdgeType.DEPENDS_ON,
15479
+ import_types52.EdgeType.DEPENDS_ON,
15353
15480
  "cloudflare-env-var",
15354
15481
  varName,
15355
15482
  evidenceFile,
@@ -15362,15 +15489,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15362
15489
  if (!svc.service) continue;
15363
15490
  const target = workerIndex.get(svc.service);
15364
15491
  if (target && target.anchorId !== anchorId) {
15365
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
15492
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types52.EdgeType.CALLS);
15366
15493
  if (!graph.hasEdge(edgeId)) {
15367
15494
  const edge = {
15368
15495
  id: edgeId,
15369
15496
  source: anchorId,
15370
15497
  target: target.anchorId,
15371
- type: import_types50.EdgeType.CALLS,
15372
- provenance: import_types50.Provenance.EXTRACTED,
15373
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15498
+ type: import_types52.EdgeType.CALLS,
15499
+ provenance: import_types52.Provenance.EXTRACTED,
15500
+ confidence: (0, import_types52.confidenceForExtracted)("structural"),
15374
15501
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
15375
15502
  };
15376
15503
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -15381,7 +15508,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15381
15508
  const result = addResourceEdge(
15382
15509
  graph,
15383
15510
  anchorId,
15384
- import_types50.EdgeType.DEPENDS_ON,
15511
+ import_types52.EdgeType.DEPENDS_ON,
15385
15512
  "cloudflare-service-binding",
15386
15513
  svc.service,
15387
15514
  evidenceFile,
@@ -15397,12 +15524,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
15397
15524
  // src/extract/infra/vercel.ts
15398
15525
  init_cjs_shims();
15399
15526
  var import_node_fs29 = require("fs");
15400
- var import_node_path62 = __toESM(require("path"), 1);
15401
- var import_types51 = require("@neat.is/types");
15527
+ var import_node_path63 = __toESM(require("path"), 1);
15528
+ var import_types53 = require("@neat.is/types");
15402
15529
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
15403
15530
  async function readVercelConfig(dir) {
15404
15531
  for (const filename of VERCEL_CONFIG_FILENAMES) {
15405
- const abs = import_node_path62.default.join(dir, filename);
15532
+ const abs = import_node_path63.default.join(dir, filename);
15406
15533
  if (!await exists(abs)) continue;
15407
15534
  const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15408
15535
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -15411,7 +15538,7 @@ async function readVercelConfig(dir) {
15411
15538
  return null;
15412
15539
  }
15413
15540
  async function readLinkedProjectName(dir) {
15414
- const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
15541
+ const abs = import_node_path63.default.join(dir, ".vercel", "project.json");
15415
15542
  if (!await exists(abs)) return void 0;
15416
15543
  const parsed = JSON.parse(await import_node_fs29.promises.readFile(abs, "utf8"));
15417
15544
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -15429,7 +15556,7 @@ async function addVercelServices(graph, services, scanPath) {
15429
15556
  read = await readVercelConfig(service.dir);
15430
15557
  projectName = await readLinkedProjectName(service.dir);
15431
15558
  } catch (err) {
15432
- recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
15559
+ recordExtractionError("infra vercel", import_node_path63.default.relative(scanPath, service.dir), err);
15433
15560
  continue;
15434
15561
  }
15435
15562
  if (!read && !projectName) continue;
@@ -15445,7 +15572,7 @@ async function addVercelServices(graph, services, scanPath) {
15445
15572
  const anchorId = service.node.id;
15446
15573
  if (!read) continue;
15447
15574
  const { config, relFile, raw } = read;
15448
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15575
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15449
15576
  const add = (edgeType, kind, name) => {
15450
15577
  if (!name) return;
15451
15578
  const result = emitPlatformResourceEdge(
@@ -15461,12 +15588,12 @@ async function addVercelServices(graph, services, scanPath) {
15461
15588
  nodesAdded += result.nodesAdded;
15462
15589
  edgesAdded += result.edgesAdded;
15463
15590
  };
15464
- add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
15465
- for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15466
- for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15467
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15591
+ add(import_types53.EdgeType.RUNS_ON, "vercel", "vercel");
15592
+ for (const cron of config.crons ?? []) add(import_types53.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15593
+ for (const varName of Object.keys(config.env ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15594
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15468
15595
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
15469
- add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15596
+ add(import_types53.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15470
15597
  }
15471
15598
  }
15472
15599
  return { nodesAdded, edgesAdded };
@@ -15475,13 +15602,13 @@ async function addVercelServices(graph, services, scanPath) {
15475
15602
  // src/extract/infra/railway.ts
15476
15603
  init_cjs_shims();
15477
15604
  var import_node_fs30 = require("fs");
15478
- var import_node_path63 = __toESM(require("path"), 1);
15605
+ var import_node_path64 = __toESM(require("path"), 1);
15479
15606
  var import_smol_toml4 = require("smol-toml");
15480
- var import_types52 = require("@neat.is/types");
15607
+ var import_types54 = require("@neat.is/types");
15481
15608
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
15482
15609
  async function readRailwayConfig(dir) {
15483
15610
  for (const filename of RAILWAY_FILENAMES) {
15484
- const abs = import_node_path63.default.join(dir, filename);
15611
+ const abs = import_node_path64.default.join(dir, filename);
15485
15612
  if (!await exists(abs)) continue;
15486
15613
  const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15487
15614
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -15497,7 +15624,7 @@ async function addRailwayServices(graph, services, scanPath) {
15497
15624
  try {
15498
15625
  read = await readRailwayConfig(service.dir);
15499
15626
  } catch (err) {
15500
- recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
15627
+ recordExtractionError("infra railway", import_node_path64.default.relative(scanPath, service.dir), err);
15501
15628
  continue;
15502
15629
  }
15503
15630
  if (!read) continue;
@@ -15507,7 +15634,7 @@ async function addRailwayServices(graph, services, scanPath) {
15507
15634
  }
15508
15635
  const anchorId = service.node.id;
15509
15636
  const { config, relFile, raw } = read;
15510
- const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15637
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15511
15638
  const add = (edgeType, kind, name) => {
15512
15639
  if (!name) return;
15513
15640
  const result = emitPlatformResourceEdge(
@@ -15523,9 +15650,9 @@ async function addRailwayServices(graph, services, scanPath) {
15523
15650
  nodesAdded += result.nodesAdded;
15524
15651
  edgesAdded += result.edgesAdded;
15525
15652
  };
15526
- add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
15527
- add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15528
- add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15653
+ add(import_types54.EdgeType.RUNS_ON, "railway", "railway");
15654
+ add(import_types54.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15655
+ add(import_types54.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15529
15656
  }
15530
15657
  return { nodesAdded, edgesAdded };
15531
15658
  }
@@ -15533,12 +15660,12 @@ async function addRailwayServices(graph, services, scanPath) {
15533
15660
  // src/extract/infra/supabase.ts
15534
15661
  init_cjs_shims();
15535
15662
  var import_node_fs31 = require("fs");
15536
- var import_node_path64 = __toESM(require("path"), 1);
15663
+ var import_node_path65 = __toESM(require("path"), 1);
15537
15664
  var import_smol_toml5 = require("smol-toml");
15538
- var import_types53 = require("@neat.is/types");
15665
+ var import_types55 = require("@neat.is/types");
15539
15666
  async function readSupabaseConfig(dir) {
15540
- const relFile = import_node_path64.default.join("supabase", "config.toml");
15541
- const abs = import_node_path64.default.join(dir, relFile);
15667
+ const relFile = import_node_path65.default.join("supabase", "config.toml");
15668
+ const abs = import_node_path65.default.join(dir, relFile);
15542
15669
  if (!await exists(abs)) return null;
15543
15670
  const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15544
15671
  const config = (0, import_smol_toml5.parse)(raw);
@@ -15552,7 +15679,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15552
15679
  try {
15553
15680
  read = await readSupabaseConfig(service.dir);
15554
15681
  } catch (err) {
15555
- recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
15682
+ recordExtractionError("infra supabase", import_node_path65.default.relative(scanPath, service.dir), err);
15556
15683
  continue;
15557
15684
  }
15558
15685
  if (!read) continue;
@@ -15567,7 +15694,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15567
15694
  });
15568
15695
  }
15569
15696
  const anchorId = service.node.id;
15570
- const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15697
+ const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
15571
15698
  const add = (edgeType, kind, name) => {
15572
15699
  if (!name) return;
15573
15700
  const result = emitPlatformResourceEdge(
@@ -15583,10 +15710,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
15583
15710
  nodesAdded += result.nodesAdded;
15584
15711
  edgesAdded += result.edgesAdded;
15585
15712
  };
15586
- add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15587
- for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15588
- if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15589
- if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15713
+ add(import_types55.EdgeType.RUNS_ON, "supabase", "supabase");
15714
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types55.EdgeType.DEPENDS_ON, "supabase-function", fn);
15715
+ if (config.storage) add(import_types55.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15716
+ if (config.auth) add(import_types55.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15590
15717
  }
15591
15718
  return { nodesAdded, edgesAdded };
15592
15719
  }
@@ -15609,18 +15736,18 @@ async function addInfra(graph, scanPath, services) {
15609
15736
 
15610
15737
  // src/extract/zod-shapes.ts
15611
15738
  init_cjs_shims();
15612
- var import_node_path65 = __toESM(require("path"), 1);
15613
- var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
15739
+ var import_node_path66 = __toESM(require("path"), 1);
15740
+ var import_tree_sitter18 = __toESM(require("tree-sitter"), 1);
15614
15741
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15615
- var import_types54 = require("@neat.is/types");
15742
+ var import_types56 = require("@neat.is/types");
15616
15743
  var ZOD_IMPORT_RE = /\bzod\b/;
15617
15744
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
15618
15745
  function parserForExt4(ext) {
15619
- const p = new import_tree_sitter17.default();
15746
+ const p = new import_tree_sitter18.default();
15620
15747
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
15621
15748
  return p;
15622
15749
  }
15623
- function namedChildren5(node) {
15750
+ function namedChildren6(node) {
15624
15751
  const out = [];
15625
15752
  for (let i = 0; i < node.namedChildCount; i++) {
15626
15753
  const c = node.namedChild(i);
@@ -15630,7 +15757,7 @@ function namedChildren5(node) {
15630
15757
  }
15631
15758
  function stringLiteralText5(node) {
15632
15759
  if (!node || node.type !== "string") return null;
15633
- for (const child of namedChildren5(node)) {
15760
+ for (const child of namedChildren6(node)) {
15634
15761
  if (child.type === "string_fragment") return child.text;
15635
15762
  }
15636
15763
  return "";
@@ -15655,12 +15782,12 @@ function zodCallKind(call) {
15655
15782
  function firstArg(call) {
15656
15783
  const args = call.childForFieldName("arguments");
15657
15784
  if (!args) return null;
15658
- return namedChildren5(args)[0] ?? null;
15785
+ return namedChildren6(args)[0] ?? null;
15659
15786
  }
15660
15787
  function fieldsFromObject(obj) {
15661
15788
  const out = [];
15662
15789
  const seen = /* @__PURE__ */ new Set();
15663
- for (const child of namedChildren5(obj)) {
15790
+ for (const child of namedChildren6(obj)) {
15664
15791
  if (child.type === "spread_element") return null;
15665
15792
  if (child.type !== "pair") continue;
15666
15793
  const key = child.childForFieldName("key");
@@ -15676,7 +15803,7 @@ function fieldsFromObject(obj) {
15676
15803
  function membersFromEnum(arr) {
15677
15804
  const out = [];
15678
15805
  const seen = /* @__PURE__ */ new Set();
15679
- for (const child of namedChildren5(arr)) {
15806
+ for (const child of namedChildren6(arr)) {
15680
15807
  if (child.type !== "string") return null;
15681
15808
  const value = stringLiteralText5(child);
15682
15809
  if (value !== null && value !== "" && !seen.has(value)) {
@@ -15688,10 +15815,10 @@ function membersFromEnum(arr) {
15688
15815
  }
15689
15816
  function topLevelSchemas(root) {
15690
15817
  const out = [];
15691
- for (const stmt of namedChildren5(root)) {
15818
+ for (const stmt of namedChildren6(root)) {
15692
15819
  const decl = stmt.type === "export_statement" ? stmt.childForFieldName("declaration") : stmt;
15693
15820
  if (decl?.type !== "lexical_declaration" && decl?.type !== "variable_declaration") continue;
15694
- for (const declarator of namedChildren5(decl)) {
15821
+ for (const declarator of namedChildren6(decl)) {
15695
15822
  if (declarator.type !== "variable_declarator") continue;
15696
15823
  const name = declarator.childForFieldName("name");
15697
15824
  const value = declarator.childForFieldName("value");
@@ -15704,7 +15831,7 @@ function topLevelSchemas(root) {
15704
15831
  }
15705
15832
  function zodShapesFromFile(file, serviceDir) {
15706
15833
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
15707
- const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
15834
+ const tree = parseSource3(parserForExt4(import_node_path66.default.extname(file.path)), file.content);
15708
15835
  const out = [];
15709
15836
  const seen = /* @__PURE__ */ new Set();
15710
15837
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -15718,11 +15845,11 @@ function zodShapesFromFile(file, serviceDir) {
15718
15845
  seen.add(name);
15719
15846
  const line = call.startPosition.row + 1;
15720
15847
  out.push({
15721
- infraId: (0, import_types54.infraId)("zod-schema", name),
15848
+ infraId: (0, import_types56.infraId)("zod-schema", name),
15722
15849
  name,
15723
15850
  fields,
15724
15851
  evidence: {
15725
- file: import_node_path65.default.relative(serviceDir, file.path),
15852
+ file: import_node_path66.default.relative(serviceDir, file.path),
15726
15853
  line,
15727
15854
  snippet: snippet(file.content, line)
15728
15855
  }
@@ -15753,7 +15880,7 @@ async function addZodShapes(graph, services) {
15753
15880
  if (!graph.hasNode(shape.infraId)) {
15754
15881
  const node = {
15755
15882
  id: shape.infraId,
15756
- type: import_types54.NodeType.InfraNode,
15883
+ type: import_types56.NodeType.InfraNode,
15757
15884
  name: shape.name,
15758
15885
  provider: "self",
15759
15886
  kind: "zod-schema"
@@ -15763,14 +15890,14 @@ async function addZodShapes(graph, services) {
15763
15890
  }
15764
15891
  if (shape.fields.length > 0) {
15765
15892
  const node = graph.getNodeAttributes(shape.infraId);
15766
- if (node.type === import_types54.NodeType.InfraNode) {
15893
+ if (node.type === import_types56.NodeType.InfraNode) {
15767
15894
  graph.replaceNodeAttributes(shape.infraId, {
15768
15895
  ...node,
15769
15896
  columns: foldColumns(
15770
15897
  node.columns,
15771
15898
  shape.fields,
15772
- import_types54.Provenance.EXTRACTED,
15773
- (0, import_types54.confidenceForExtracted)("structural")
15899
+ import_types56.Provenance.EXTRACTED,
15900
+ (0, import_types56.confidenceForExtracted)("structural")
15774
15901
  )
15775
15902
  });
15776
15903
  }
@@ -15784,15 +15911,15 @@ async function addZodShapes(graph, services) {
15784
15911
  );
15785
15912
  nodesAdded += n;
15786
15913
  edgesAdded += e;
15787
- const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
15914
+ const edgeId = (0, import_types56.extractedEdgeId)(fileNodeId, shape.infraId, import_types56.EdgeType.CONTAINS);
15788
15915
  if (!graph.hasEdge(edgeId)) {
15789
15916
  const edge = {
15790
15917
  id: edgeId,
15791
15918
  source: fileNodeId,
15792
15919
  target: shape.infraId,
15793
- type: import_types54.EdgeType.CONTAINS,
15794
- provenance: import_types54.Provenance.EXTRACTED,
15795
- confidence: (0, import_types54.confidenceForExtracted)("structural"),
15920
+ type: import_types56.EdgeType.CONTAINS,
15921
+ provenance: import_types56.Provenance.EXTRACTED,
15922
+ confidence: (0, import_types56.confidenceForExtracted)("structural"),
15796
15923
  evidence: shape.evidence
15797
15924
  };
15798
15925
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -15806,7 +15933,7 @@ async function addZodShapes(graph, services) {
15806
15933
 
15807
15934
  // src/extract/firestore-rules.ts
15808
15935
  init_cjs_shims();
15809
- var import_types55 = require("@neat.is/types");
15936
+ var import_types57 = require("@neat.is/types");
15810
15937
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
15811
15938
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
15812
15939
  function stripComments(src) {
@@ -15946,7 +16073,7 @@ async function addFirestoreRules(graph, services) {
15946
16073
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
15947
16074
  graph.forEachNode((id, attrs) => {
15948
16075
  const node = attrs;
15949
- if (node.type !== import_types55.NodeType.InfraNode) return;
16076
+ if (node.type !== import_types57.NodeType.InfraNode) return;
15950
16077
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
15951
16078
  const fields = guards.get(collectionKeyFromName(node.name));
15952
16079
  if (!fields || fields.size === 0) return;
@@ -15959,17 +16086,17 @@ async function addFirestoreRules(graph, services) {
15959
16086
  }
15960
16087
 
15961
16088
  // src/extract/index.ts
15962
- var import_node_path67 = __toESM(require("path"), 1);
16089
+ var import_node_path68 = __toESM(require("path"), 1);
15963
16090
 
15964
16091
  // src/extract/retire.ts
15965
16092
  init_cjs_shims();
15966
16093
  var import_node_fs32 = require("fs");
15967
- var import_node_path66 = __toESM(require("path"), 1);
15968
- var import_types56 = require("@neat.is/types");
16094
+ var import_node_path67 = __toESM(require("path"), 1);
16095
+ var import_types58 = require("@neat.is/types");
15969
16096
  function dropOrphanedFileNodes(graph) {
15970
16097
  const orphans = [];
15971
16098
  graph.forEachNode((id, attrs) => {
15972
- if (attrs.type !== import_types56.NodeType.FileNode) return;
16099
+ if (attrs.type !== import_types58.NodeType.FileNode) return;
15973
16100
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
15974
16101
  orphans.push(id);
15975
16102
  }
@@ -15982,14 +16109,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
15982
16109
  const bases = [scanPath, ...serviceDirs];
15983
16110
  graph.forEachEdge((id, attrs) => {
15984
16111
  const edge = attrs;
15985
- if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
16112
+ if (edge.provenance !== import_types58.Provenance.EXTRACTED) return;
15986
16113
  const evidenceFile = edge.evidence?.file;
15987
16114
  if (!evidenceFile) return;
15988
- if (import_node_path66.default.isAbsolute(evidenceFile)) {
16115
+ if (import_node_path67.default.isAbsolute(evidenceFile)) {
15989
16116
  if (!(0, import_node_fs32.existsSync)(evidenceFile)) toDrop.push(id);
15990
16117
  return;
15991
16118
  }
15992
- const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path66.default.join(base, evidenceFile)));
16119
+ const found = bases.some((base) => (0, import_node_fs32.existsSync)(import_node_path67.default.join(base, evidenceFile)));
15993
16120
  if (!found) toDrop.push(id);
15994
16121
  });
15995
16122
  for (const id of toDrop) graph.dropEdge(id);
@@ -16046,7 +16173,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16046
16173
  }
16047
16174
  const droppedEntries = drainDroppedExtracted();
16048
16175
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
16049
- const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
16176
+ const rejectedPath = import_node_path68.default.join(import_node_path68.default.dirname(opts.errorsPath), "rejected.ndjson");
16050
16177
  try {
16051
16178
  await writeRejectedExtracted(droppedEntries, rejectedPath);
16052
16179
  } catch (err) {
@@ -16081,8 +16208,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
16081
16208
  // src/persist.ts
16082
16209
  init_cjs_shims();
16083
16210
  var import_node_fs33 = require("fs");
16084
- var import_node_path68 = __toESM(require("path"), 1);
16085
- var import_types57 = require("@neat.is/types");
16211
+ var import_node_path69 = __toESM(require("path"), 1);
16212
+ var import_types59 = require("@neat.is/types");
16086
16213
  var SCHEMA_VERSION = 7;
16087
16214
  function migrateV1ToV2(payload) {
16088
16215
  const nodes = payload.graph.nodes;
@@ -16106,7 +16233,7 @@ function migrateV5ToV6(payload) {
16106
16233
  if (Array.isArray(nodes)) {
16107
16234
  for (const node of nodes) {
16108
16235
  const attrs = node.attributes;
16109
- if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
16236
+ if (!attrs || attrs.type !== import_types59.NodeType.InfraNode) continue;
16110
16237
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
16111
16238
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
16112
16239
  }
@@ -16122,12 +16249,12 @@ function migrateV2ToV3(payload) {
16122
16249
  for (const edge of edges) {
16123
16250
  const attrs = edge.attributes;
16124
16251
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
16125
- attrs.provenance = import_types57.Provenance.OBSERVED;
16252
+ attrs.provenance = import_types59.Provenance.OBSERVED;
16126
16253
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
16127
16254
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
16128
16255
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
16129
16256
  if (type && source && target) {
16130
- const newId = (0, import_types57.observedEdgeId)(source, target, type);
16257
+ const newId = (0, import_types59.observedEdgeId)(source, target, type);
16131
16258
  attrs.id = newId;
16132
16259
  if (edge.key) edge.key = newId;
16133
16260
  }
@@ -16136,7 +16263,7 @@ function migrateV2ToV3(payload) {
16136
16263
  return { ...payload, schemaVersion: 3 };
16137
16264
  }
16138
16265
  async function ensureDir(filePath) {
16139
- await import_node_fs33.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
16266
+ await import_node_fs33.promises.mkdir(import_node_path69.default.dirname(filePath), { recursive: true });
16140
16267
  }
16141
16268
  async function saveGraphToDisk(graph, outPath) {
16142
16269
  await ensureDir(outPath);
@@ -16228,19 +16355,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
16228
16355
  init_cjs_shims();
16229
16356
  var import_fastify2 = __toESM(require("fastify"), 1);
16230
16357
  var import_cors = __toESM(require("@fastify/cors"), 1);
16231
- var import_types98 = require("@neat.is/types");
16358
+ var import_types100 = require("@neat.is/types");
16232
16359
 
16233
16360
  // src/extend/index.ts
16234
16361
  init_cjs_shims();
16235
16362
  var import_node_fs35 = require("fs");
16236
- var import_node_path70 = __toESM(require("path"), 1);
16363
+ var import_node_path71 = __toESM(require("path"), 1);
16237
16364
  var import_node_os2 = __toESM(require("os"), 1);
16238
16365
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
16239
16366
 
16240
16367
  // src/installers/package-manager.ts
16241
16368
  init_cjs_shims();
16242
16369
  var import_node_fs34 = require("fs");
16243
- var import_node_path69 = __toESM(require("path"), 1);
16370
+ var import_node_path70 = __toESM(require("path"), 1);
16244
16371
  var import_node_child_process = require("child_process");
16245
16372
  var LOCKFILE_PRIORITY = [
16246
16373
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -16262,22 +16389,22 @@ async function exists2(p) {
16262
16389
  }
16263
16390
  }
16264
16391
  async function detectPackageManager(serviceDir) {
16265
- let dir = import_node_path69.default.resolve(serviceDir);
16392
+ let dir = import_node_path70.default.resolve(serviceDir);
16266
16393
  const stops = /* @__PURE__ */ new Set();
16267
16394
  for (let i = 0; i < 64; i++) {
16268
16395
  if (stops.has(dir)) break;
16269
16396
  stops.add(dir);
16270
16397
  for (const candidate of LOCKFILE_PRIORITY) {
16271
- const lockPath = import_node_path69.default.join(dir, candidate.lockfile);
16398
+ const lockPath = import_node_path70.default.join(dir, candidate.lockfile);
16272
16399
  if (await exists2(lockPath)) {
16273
16400
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
16274
16401
  }
16275
16402
  }
16276
- const parent = import_node_path69.default.dirname(dir);
16403
+ const parent = import_node_path70.default.dirname(dir);
16277
16404
  if (parent === dir) break;
16278
16405
  dir = parent;
16279
16406
  }
16280
- return { pm: "npm", cwd: import_node_path69.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16407
+ return { pm: "npm", cwd: import_node_path70.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
16281
16408
  }
16282
16409
  async function runPackageManagerInstall(cmd) {
16283
16410
  return new Promise((resolve) => {
@@ -16326,7 +16453,7 @@ async function fileExists2(p) {
16326
16453
  }
16327
16454
  }
16328
16455
  async function readPackageJson(scanPath) {
16329
- const pkgPath = import_node_path70.default.join(scanPath, "package.json");
16456
+ const pkgPath = import_node_path71.default.join(scanPath, "package.json");
16330
16457
  const raw = await import_node_fs35.promises.readFile(pkgPath, "utf8");
16331
16458
  return JSON.parse(raw);
16332
16459
  }
@@ -16340,27 +16467,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
16340
16467
  ]);
16341
16468
  async function findHookFiles(scanPath) {
16342
16469
  const found = [];
16343
- const walk10 = async (dir) => {
16470
+ const walk11 = async (dir) => {
16344
16471
  const entries = await import_node_fs35.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
16345
16472
  for (const entry of entries) {
16346
16473
  if (entry.isDirectory()) {
16347
16474
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
16348
- await walk10(import_node_path70.default.join(dir, entry.name));
16475
+ await walk11(import_node_path71.default.join(dir, entry.name));
16349
16476
  } else if (entry.isFile()) {
16350
16477
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
16351
- const rel = import_node_path70.default.relative(scanPath, import_node_path70.default.join(dir, entry.name));
16352
- found.push(rel.split(import_node_path70.default.sep).join("/"));
16478
+ const rel = import_node_path71.default.relative(scanPath, import_node_path71.default.join(dir, entry.name));
16479
+ found.push(rel.split(import_node_path71.default.sep).join("/"));
16353
16480
  }
16354
16481
  }
16355
16482
  }
16356
16483
  };
16357
- await walk10(scanPath);
16484
+ await walk11(scanPath);
16358
16485
  return found.sort();
16359
16486
  }
16360
16487
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16361
16488
  let fallback = null;
16362
16489
  for (const file of hookFiles) {
16363
- const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(scanPath, file), "utf8");
16490
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(scanPath, file), "utf8");
16364
16491
  const patched = splicedContent(content, snippet2);
16365
16492
  if (patched !== null) return { file, content, patched };
16366
16493
  if (fallback === null) fallback = { file, content };
@@ -16368,11 +16495,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
16368
16495
  return { file: fallback.file, content: fallback.content, patched: null };
16369
16496
  }
16370
16497
  function extendLogPath() {
16371
- return process.env.NEAT_EXTEND_LOG ?? import_node_path70.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16498
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path71.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
16372
16499
  }
16373
16500
  async function appendExtendLog(entry) {
16374
16501
  const logPath = extendLogPath();
16375
- await import_node_fs35.promises.mkdir(import_node_path70.default.dirname(logPath), { recursive: true });
16502
+ await import_node_fs35.promises.mkdir(import_node_path71.default.dirname(logPath), { recursive: true });
16376
16503
  await import_node_fs35.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
16377
16504
  }
16378
16505
  function splicedContent(fileContent, snippet2) {
@@ -16431,7 +16558,7 @@ function lookupInstrumentation(library, installedVersion) {
16431
16558
  }
16432
16559
  async function describeProjectInstrumentation(ctx) {
16433
16560
  const hookFiles = await findHookFiles(ctx.scanPath);
16434
- const envNeat = await fileExists2(import_node_path70.default.join(ctx.scanPath, ".env.neat"));
16561
+ const envNeat = await fileExists2(import_node_path71.default.join(ctx.scanPath, ".env.neat"));
16435
16562
  const registryInstrPackages = new Set(
16436
16563
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
16437
16564
  );
@@ -16453,7 +16580,7 @@ async function applyExtension(ctx, args, options) {
16453
16580
  );
16454
16581
  }
16455
16582
  for (const file of hookFiles) {
16456
- const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
16583
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
16457
16584
  if (content.includes(args.registration_snippet)) {
16458
16585
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
16459
16586
  }
@@ -16465,10 +16592,10 @@ async function applyExtension(ctx, args, options) {
16465
16592
  );
16466
16593
  }
16467
16594
  const primaryFile = primary.file;
16468
- const primaryPath = import_node_path70.default.join(ctx.scanPath, primaryFile);
16595
+ const primaryPath = import_node_path71.default.join(ctx.scanPath, primaryFile);
16469
16596
  const filesTouched = [];
16470
16597
  const depsAdded = [];
16471
- const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
16598
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
16472
16599
  const pkg = await readPackageJson(ctx.scanPath);
16473
16600
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
16474
16601
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -16507,7 +16634,7 @@ async function dryRunExtension(ctx, args) {
16507
16634
  };
16508
16635
  }
16509
16636
  for (const file of hookFiles) {
16510
- const content = await import_node_fs35.promises.readFile(import_node_path70.default.join(ctx.scanPath, file), "utf8");
16637
+ const content = await import_node_fs35.promises.readFile(import_node_path71.default.join(ctx.scanPath, file), "utf8");
16511
16638
  if (content.includes(args.registration_snippet)) {
16512
16639
  return {
16513
16640
  library: args.library,
@@ -16548,7 +16675,7 @@ async function rollbackExtension(ctx, args) {
16548
16675
  if (!match) {
16549
16676
  return { undone: false, message: "no apply found for library" };
16550
16677
  }
16551
- const pkgPath = import_node_path70.default.join(ctx.scanPath, "package.json");
16678
+ const pkgPath = import_node_path71.default.join(ctx.scanPath, "package.json");
16552
16679
  if (await fileExists2(pkgPath)) {
16553
16680
  const pkg = await readPackageJson(ctx.scanPath);
16554
16681
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -16559,7 +16686,7 @@ async function rollbackExtension(ctx, args) {
16559
16686
  }
16560
16687
  const hookFiles = await findHookFiles(ctx.scanPath);
16561
16688
  for (const file of hookFiles) {
16562
- const filePath = import_node_path70.default.join(ctx.scanPath, file);
16689
+ const filePath = import_node_path71.default.join(ctx.scanPath, file);
16563
16690
  const content = await import_node_fs35.promises.readFile(filePath, "utf8");
16564
16691
  if (content.includes(match.registration_snippet)) {
16565
16692
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -16575,39 +16702,39 @@ async function rollbackExtension(ctx, args) {
16575
16702
 
16576
16703
  // src/divergences.ts
16577
16704
  init_cjs_shims();
16578
- var import_types58 = require("@neat.is/types");
16705
+ var import_types60 = require("@neat.is/types");
16579
16706
  function bucketKey(source, target, type) {
16580
16707
  return `${type}|${source}|${target}`;
16581
16708
  }
16582
16709
  function bucketSourceFor(graph, edge) {
16583
- if (edge.type !== import_types58.EdgeType.CONNECTS_TO) return edge.source;
16584
- const parsed = (0, import_types58.parseFileId)(edge.source);
16710
+ if (edge.type !== import_types60.EdgeType.CONNECTS_TO) return edge.source;
16711
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16585
16712
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
16586
16713
  const target = graph.getNodeAttributes(edge.target);
16587
- if (target.type !== import_types58.NodeType.DatabaseNode) return edge.source;
16588
- return (0, import_types58.serviceId)(parsed.service);
16714
+ if (target.type !== import_types60.NodeType.DatabaseNode) return edge.source;
16715
+ return (0, import_types60.serviceId)(parsed.service);
16589
16716
  }
16590
16717
  function bucketEdges(graph) {
16591
16718
  const buckets2 = /* @__PURE__ */ new Map();
16592
16719
  graph.forEachEdge((id, attrs) => {
16593
16720
  const e = attrs;
16594
- const parsed = (0, import_types58.parseEdgeId)(id);
16721
+ const parsed = (0, import_types60.parseEdgeId)(id);
16595
16722
  const provenance = parsed?.provenance ?? e.provenance;
16596
16723
  const source = bucketSourceFor(graph, e);
16597
16724
  const key = bucketKey(source, e.target, e.type);
16598
16725
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
16599
16726
  switch (provenance) {
16600
- case import_types58.Provenance.EXTRACTED:
16727
+ case import_types60.Provenance.EXTRACTED:
16601
16728
  cur.extracted = e;
16602
16729
  break;
16603
- case import_types58.Provenance.OBSERVED:
16730
+ case import_types60.Provenance.OBSERVED:
16604
16731
  cur.observed = e;
16605
16732
  break;
16606
- case import_types58.Provenance.INFERRED:
16733
+ case import_types60.Provenance.INFERRED:
16607
16734
  cur.inferred = e;
16608
16735
  break;
16609
16736
  default:
16610
- if (e.provenance === import_types58.Provenance.STALE) cur.stale = e;
16737
+ if (e.provenance === import_types60.Provenance.STALE) cur.stale = e;
16611
16738
  }
16612
16739
  buckets2.set(key, cur);
16613
16740
  });
@@ -16616,22 +16743,22 @@ function bucketEdges(graph) {
16616
16743
  function nodeIsFrontier(graph, nodeId) {
16617
16744
  if (!graph.hasNode(nodeId)) return false;
16618
16745
  const attrs = graph.getNodeAttributes(nodeId);
16619
- return attrs.type === import_types58.NodeType.FrontierNode;
16746
+ return attrs.type === import_types60.NodeType.FrontierNode;
16620
16747
  }
16621
16748
  function nodeIsWebsocketChannel(graph, nodeId) {
16622
16749
  if (!graph.hasNode(nodeId)) return false;
16623
16750
  const attrs = graph.getNodeAttributes(nodeId);
16624
- return attrs.type === import_types58.NodeType.WebSocketChannelNode;
16751
+ return attrs.type === import_types60.NodeType.WebSocketChannelNode;
16625
16752
  }
16626
16753
  function nodeIsServerAction(graph, nodeId) {
16627
16754
  if (!graph.hasNode(nodeId)) return false;
16628
16755
  const attrs = graph.getNodeAttributes(nodeId);
16629
- return attrs.type === import_types58.NodeType.ServerActionNode;
16756
+ return attrs.type === import_types60.NodeType.ServerActionNode;
16630
16757
  }
16631
16758
  function nodeIsSymbol(graph, nodeId) {
16632
16759
  if (!graph.hasNode(nodeId)) return false;
16633
16760
  const attrs = graph.getNodeAttributes(nodeId);
16634
- return attrs.type === import_types58.NodeType.SymbolNode;
16761
+ return attrs.type === import_types60.NodeType.SymbolNode;
16635
16762
  }
16636
16763
  function clampConfidence(n) {
16637
16764
  if (!Number.isFinite(n)) return 0;
@@ -16651,14 +16778,14 @@ function gradedConfidence(edge) {
16651
16778
  return clampConfidence(confidenceForEdge(edge));
16652
16779
  }
16653
16780
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
16654
- import_types58.EdgeType.CALLS,
16655
- import_types58.EdgeType.CONNECTS_TO,
16656
- import_types58.EdgeType.PUBLISHES_TO,
16657
- import_types58.EdgeType.CONSUMES_FROM
16781
+ import_types60.EdgeType.CALLS,
16782
+ import_types60.EdgeType.CONNECTS_TO,
16783
+ import_types60.EdgeType.PUBLISHES_TO,
16784
+ import_types60.EdgeType.CONSUMES_FROM
16658
16785
  ]);
16659
16786
  function detectMissingDivergences(graph, bucket) {
16660
16787
  const out = [];
16661
- if (bucket.type === import_types58.EdgeType.CONTAINS) return out;
16788
+ if (bucket.type === import_types60.EdgeType.CONTAINS) return out;
16662
16789
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
16663
16790
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
16664
16791
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -16700,7 +16827,7 @@ function declaredHostFor(svc) {
16700
16827
  function hasExtractedConfiguredBy(graph, svcId) {
16701
16828
  for (const edgeId of graph.outboundEdges(svcId)) {
16702
16829
  const e = graph.getEdgeAttributes(edgeId);
16703
- if (e.type === import_types58.EdgeType.CONFIGURED_BY && e.provenance === import_types58.Provenance.EXTRACTED) {
16830
+ if (e.type === import_types60.EdgeType.CONFIGURED_BY && e.provenance === import_types60.Provenance.EXTRACTED) {
16704
16831
  return true;
16705
16832
  }
16706
16833
  }
@@ -16713,10 +16840,10 @@ function detectHostMismatch(graph, svcId, svc) {
16713
16840
  const out = [];
16714
16841
  for (const edgeId of graph.outboundEdges(svcId)) {
16715
16842
  const edge = graph.getEdgeAttributes(edgeId);
16716
- if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16717
- if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
16843
+ if (edge.type !== import_types60.EdgeType.CONNECTS_TO) continue;
16844
+ if (edge.provenance !== import_types60.Provenance.OBSERVED) continue;
16718
16845
  const target = graph.getNodeAttributes(edge.target);
16719
- if (target.type !== import_types58.NodeType.DatabaseNode) continue;
16846
+ if (target.type !== import_types60.NodeType.DatabaseNode) continue;
16720
16847
  const observedHost = target.host?.trim();
16721
16848
  if (!observedHost) continue;
16722
16849
  if (observedHost === declaredHost) continue;
@@ -16738,10 +16865,10 @@ function detectCompatDivergences(graph, svcId, svc) {
16738
16865
  const deps = svc.dependencies ?? {};
16739
16866
  for (const edgeId of graph.outboundEdges(svcId)) {
16740
16867
  const edge = graph.getEdgeAttributes(edgeId);
16741
- if (edge.type !== import_types58.EdgeType.CONNECTS_TO) continue;
16742
- if (edge.provenance !== import_types58.Provenance.OBSERVED) continue;
16868
+ if (edge.type !== import_types60.EdgeType.CONNECTS_TO) continue;
16869
+ if (edge.provenance !== import_types60.Provenance.OBSERVED) continue;
16743
16870
  const target = graph.getNodeAttributes(edge.target);
16744
- if (target.type !== import_types58.NodeType.DatabaseNode) continue;
16871
+ if (target.type !== import_types60.NodeType.DatabaseNode) continue;
16745
16872
  for (const pair of compatPairs()) {
16746
16873
  if (pair.engine !== target.engine) continue;
16747
16874
  const declared = deps[pair.driver];
@@ -16885,11 +17012,11 @@ function symbolLocus(graph, ev) {
16885
17012
  const location = filepath ? `${filepath}${lineno !== void 0 ? `:${lineno}` : ""}` : void 0;
16886
17013
  const affected = ev.affectedNode;
16887
17014
  const affectedInGraph = affected.length > 0 && graph.hasNode(affected);
16888
- const affectedIsCode = affectedInGraph && ((0, import_types58.parseSymbolId)(affected) !== null || (0, import_types58.parseFileId)(affected) !== null);
17015
+ const affectedIsCode = affectedInGraph && ((0, import_types60.parseSymbolId)(affected) !== null || (0, import_types60.parseFileId)(affected) !== null);
16889
17016
  if (affectedIsCode) return { node: affected, ...location ? { location } : {} };
16890
17017
  if (!location) return null;
16891
17018
  if (affectedInGraph) return { node: affected, location };
16892
- const svc = (0, import_types58.serviceId)(ev.service);
17019
+ const svc = (0, import_types60.serviceId)(ev.service);
16893
17020
  if (graph.hasNode(svc)) return { node: svc, location };
16894
17021
  return null;
16895
17022
  }
@@ -16932,7 +17059,7 @@ function detectSymbolMismatches(graph, incidents) {
16932
17059
  mismatchKind: g.kind,
16933
17060
  ...g.symbol ? { symbol: g.symbol } : {},
16934
17061
  ...g.location ? { location: g.location } : {},
16935
- provenance: import_types58.Provenance.INFERRED,
17062
+ provenance: import_types60.Provenance.INFERRED,
16936
17063
  incidentId: g.latest.id,
16937
17064
  errorMessage: g.latest.errorMessage,
16938
17065
  incidentCount: g.count,
@@ -16949,7 +17076,7 @@ var OBSERVED_FAILING_CONFIDENCE = 0.6;
16949
17076
  function detectObservedFailingEdge(graph, bucket) {
16950
17077
  if (!bucket.extracted || !bucket.observed) return [];
16951
17078
  if (!OBSERVABLE_EDGE_TYPES.has(bucket.type)) return [];
16952
- if (bucket.observed.provenance !== import_types58.Provenance.OBSERVED) return [];
17079
+ if (bucket.observed.provenance !== import_types60.Provenance.OBSERVED) return [];
16953
17080
  const signal = bucket.observed.signal;
16954
17081
  if (!signal) return [];
16955
17082
  const { spanCount, errorCount } = signal;
@@ -16963,7 +17090,7 @@ function detectObservedFailingEdge(graph, bucket) {
16963
17090
  source: bucket.source,
16964
17091
  target: bucket.target,
16965
17092
  failureKind: "error-rate",
16966
- provenance: import_types58.Provenance.INFERRED,
17093
+ provenance: import_types60.Provenance.INFERRED,
16967
17094
  edgeType: bucket.type,
16968
17095
  observed: bucket.observed,
16969
17096
  spanCount,
@@ -17063,7 +17190,7 @@ function detectObservedFailingIncidents(graph, incidents) {
17063
17190
  source: g.node,
17064
17191
  target: g.node,
17065
17192
  failureKind: g.kind,
17066
- provenance: import_types58.Provenance.INFERRED,
17193
+ provenance: import_types60.Provenance.INFERRED,
17067
17194
  ...g.location ? { location: g.location } : {},
17068
17195
  incidentId: g.latest.id,
17069
17196
  errorMessage: g.latest.errorMessage,
@@ -17082,24 +17209,24 @@ function involvesNode(d, nodeId) {
17082
17209
  function datastoreEverObserved(graph, nodeId) {
17083
17210
  if (!graph.hasNode(nodeId)) return false;
17084
17211
  const n = graph.getNodeAttributes(nodeId);
17085
- if (n.type === import_types58.NodeType.DatabaseNode) {
17212
+ if (n.type === import_types60.NodeType.DatabaseNode) {
17086
17213
  const via = n.discoveredVia;
17087
17214
  if (via === "otel" || via === "merged") return true;
17088
17215
  }
17089
17216
  for (const edgeId of graph.inboundEdges(nodeId)) {
17090
17217
  const e = graph.getEdgeAttributes(edgeId);
17091
- if (e.provenance === import_types58.Provenance.OBSERVED || e.provenance === import_types58.Provenance.STALE) return true;
17218
+ if (e.provenance === import_types60.Provenance.OBSERVED || e.provenance === import_types60.Provenance.STALE) return true;
17092
17219
  }
17093
17220
  return false;
17094
17221
  }
17095
- function serviceHasObservedSameEngineStore(graph, buckets2, serviceId16, engine, excludeTarget) {
17222
+ function serviceHasObservedSameEngineStore(graph, buckets2, serviceId17, engine, excludeTarget) {
17096
17223
  for (const bucket of buckets2.values()) {
17097
- if (bucket.type !== import_types58.EdgeType.CONNECTS_TO) continue;
17098
- if (bucket.source !== serviceId16) continue;
17224
+ if (bucket.type !== import_types60.EdgeType.CONNECTS_TO) continue;
17225
+ if (bucket.source !== serviceId17) continue;
17099
17226
  if (bucket.target === excludeTarget) continue;
17100
17227
  if (!graph.hasNode(bucket.target)) continue;
17101
17228
  const target = graph.getNodeAttributes(bucket.target);
17102
- if (target.type !== import_types58.NodeType.DatabaseNode) continue;
17229
+ if (target.type !== import_types60.NodeType.DatabaseNode) continue;
17103
17230
  if (target.engine !== engine) continue;
17104
17231
  if (bucket.observed || datastoreEverObserved(graph, bucket.target)) return true;
17105
17232
  }
@@ -17109,10 +17236,10 @@ var DEAD_CODE_PROBE_CONFIDENCE = 0.1;
17109
17236
  function dampenDeadCodeProbes(graph, buckets2, all) {
17110
17237
  return all.map((d) => {
17111
17238
  if (d.type !== "missing-observed") return d;
17112
- if (!d.extracted || d.edgeType !== import_types58.EdgeType.CONNECTS_TO) return d;
17239
+ if (!d.extracted || d.edgeType !== import_types60.EdgeType.CONNECTS_TO) return d;
17113
17240
  if (!graph.hasNode(d.target)) return d;
17114
17241
  const target = graph.getNodeAttributes(d.target);
17115
- if (target.type !== import_types58.NodeType.DatabaseNode) return d;
17242
+ if (target.type !== import_types60.NodeType.DatabaseNode) return d;
17116
17243
  if (d.extracted.evidence?.hostSource !== "literal") return d;
17117
17244
  if (datastoreEverObserved(graph, d.target)) return d;
17118
17245
  const engine = target.engine;
@@ -17132,7 +17259,7 @@ function suppressHostMismatchHalves(all) {
17132
17259
  for (const d of all) {
17133
17260
  if (d.type !== "host-mismatch") continue;
17134
17261
  observedHalf.add(`${d.source}->${d.target}`);
17135
- declaredHalf.add((0, import_types58.databaseId)(d.extractedHost));
17262
+ declaredHalf.add((0, import_types60.databaseId)(d.extractedHost));
17136
17263
  }
17137
17264
  if (observedHalf.size === 0) return all;
17138
17265
  return all.filter((d) => {
@@ -17152,13 +17279,13 @@ function computeDivergences(graph, opts = {}) {
17152
17279
  }
17153
17280
  graph.forEachNode((nodeId, attrs) => {
17154
17281
  const n = attrs;
17155
- if (n.type === import_types58.NodeType.ServiceNode) {
17282
+ if (n.type === import_types60.NodeType.ServiceNode) {
17156
17283
  const svc = n;
17157
17284
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
17158
17285
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
17159
17286
  return;
17160
17287
  }
17161
- if (n.type === import_types58.NodeType.InfraNode && n.kind === "sql-table") {
17288
+ if (n.type === import_types60.NodeType.InfraNode && n.kind === "sql-table") {
17162
17289
  for (const d of detectColumnDrift(n)) all.push(d);
17163
17290
  }
17164
17291
  });
@@ -17218,7 +17345,7 @@ function computeDivergences(graph, opts = {}) {
17218
17345
  const bloc = "location" in b && b.location ? b.location : "";
17219
17346
  return aloc.localeCompare(bloc);
17220
17347
  });
17221
- return import_types58.DivergenceResultSchema.parse({
17348
+ return import_types60.DivergenceResultSchema.parse({
17222
17349
  divergences: filtered,
17223
17350
  totalAffected: filtered.length,
17224
17351
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -17272,7 +17399,7 @@ function queryLogEntries(opts) {
17272
17399
 
17273
17400
  // src/goodybag.ts
17274
17401
  init_cjs_shims();
17275
- var import_types59 = require("@neat.is/types");
17402
+ var import_types61 = require("@neat.is/types");
17276
17403
  var CODE_FILEPATH_ATTR2 = "code.filepath";
17277
17404
  var CODE_LINENO_ATTR2 = "code.lineno";
17278
17405
  var BLAST_NEAREST_LIMIT = 5;
@@ -17297,7 +17424,7 @@ function locusOf(graph, ev) {
17297
17424
  ...Number.isFinite(line) ? { lineStart: line, lineEnd: line } : {},
17298
17425
  ...node?.name ? { symbol: node.name } : {},
17299
17426
  service: node?.service ?? ev.service,
17300
- provenance: import_types59.Provenance.OBSERVED
17427
+ provenance: import_types61.Provenance.OBSERVED
17301
17428
  };
17302
17429
  }
17303
17430
  function locusFromNode(graph, nodeId) {
@@ -17313,7 +17440,7 @@ function locusFromNode(graph, nodeId) {
17313
17440
  ...typeof end === "number" ? { lineEnd: end } : {},
17314
17441
  ...n.qualname ? { symbol: shortLabel(graph, nodeId) } : {},
17315
17442
  ...n.service ? { service: n.service } : {},
17316
- provenance: import_types59.Provenance.INFERRED
17443
+ provenance: import_types61.Provenance.INFERRED
17317
17444
  };
17318
17445
  }
17319
17446
  function promoteCauseLocus(graph, causeNode, incidents) {
@@ -17322,7 +17449,7 @@ function promoteCauseLocus(graph, causeNode, incidents) {
17322
17449
  );
17323
17450
  if (native) {
17324
17451
  const l = locusOf(graph, native);
17325
- if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types59.Provenance.INFERRED };
17452
+ if (l) return { ...l, symbol: shortLabel(graph, causeNode), provenance: import_types61.Provenance.INFERRED };
17326
17453
  }
17327
17454
  return locusFromNode(graph, causeNode);
17328
17455
  }
@@ -17371,7 +17498,7 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17371
17498
  const chain = (rc.traversalPath ?? []).map((node, i) => ({
17372
17499
  node,
17373
17500
  grain: grainOf2(graph, node),
17374
- provenance: provs[i] ?? provs[provs.length - 1] ?? import_types59.Provenance.INFERRED
17501
+ provenance: provs[i] ?? provs[provs.length - 1] ?? import_types61.Provenance.INFERRED
17375
17502
  }));
17376
17503
  rootCause = {
17377
17504
  node: rc.rootCauseNode,
@@ -17401,7 +17528,7 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17401
17528
  kind: "incident",
17402
17529
  id: errorEvent.id,
17403
17530
  at: errorEvent.timestamp,
17404
- incidentKind: (0, import_types59.incidentKindOf)(errorEvent),
17531
+ incidentKind: (0, import_types61.incidentKindOf)(errorEvent),
17405
17532
  service: errorEvent.service,
17406
17533
  affectedNode: affected,
17407
17534
  message: errorEvent.errorMessage,
@@ -17418,12 +17545,12 @@ function buildIncidentCard(graph, errorEvent, incidents, policies) {
17418
17545
  ...divergences.length > 0 ? { divergence: divergences } : {},
17419
17546
  headline: renderHeadline(graph, errorEvent, locus, rootCause?.node ?? null)
17420
17547
  };
17421
- return import_types59.IncidentCardSchema.parse(card);
17548
+ return import_types61.IncidentCardSchema.parse(card);
17422
17549
  }
17423
17550
 
17424
17551
  // src/ask.ts
17425
17552
  init_cjs_shims();
17426
- var import_types60 = require("@neat.is/types");
17553
+ var import_types62 = require("@neat.is/types");
17427
17554
  var DEFAULT_MAX_NODES = 3;
17428
17555
  var MAX_FACTS_PER_SECTION = 6;
17429
17556
  var INTENT_RULES = [
@@ -17651,18 +17778,18 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17651
17778
  };
17652
17779
  graph.forEachNode((id, attrs) => {
17653
17780
  const node = attrs;
17654
- if (node.type === import_types60.NodeType.FrontierNode) return;
17781
+ if (node.type === import_types62.NodeType.FrontierNode) return;
17655
17782
  const name = nodeName(node);
17656
17783
  const body = idBody(id);
17657
17784
  const labelTokens = /* @__PURE__ */ new Set([...tokens(body), ...tokens(name)]);
17658
17785
  if (labelTokens.size === 0) return;
17659
17786
  let matched = 0;
17660
17787
  for (const t of qTokens) if (labelTokens.has(t)) matched += 1;
17661
- const named = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
17662
- if (matched === 0 && !named) return;
17788
+ const named2 = body.length >= 2 && normalized.includes(body.toLowerCase()) || name.length >= 2 && normalized.includes(name.toLowerCase());
17789
+ if (matched === 0 && !named2) return;
17663
17790
  const coverage = qTokens.length > 0 ? matched / qTokens.length : 0;
17664
- const via = named ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
17665
- const score = named ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
17791
+ const via = named2 ? "id" : matched > 0 && tokens(name).some((t) => qTokens.includes(t)) ? "label" : "token";
17792
+ const score = named2 ? Math.max(0.9, coverage) : Math.min(0.85, 0.3 + 0.7 * coverage);
17666
17793
  consider({ nodeId: id, label: name, via, score: Math.min(1, score) });
17667
17794
  });
17668
17795
  if (searchIndex) {
@@ -17670,7 +17797,7 @@ async function resolveEntities(graph, question, searchIndex, maxNodes) {
17670
17797
  const res = await searchIndex.search(question, 10);
17671
17798
  if (res.provider !== "substring") {
17672
17799
  for (const m of res.matches) {
17673
- if (m.node.type === import_types60.NodeType.FrontierNode) continue;
17800
+ if (m.node.type === import_types62.NodeType.FrontierNode) continue;
17674
17801
  const already = best.get(m.node.id);
17675
17802
  if (!already && m.score < EMBED_MIN_SCORE) continue;
17676
17803
  consider({
@@ -17708,7 +17835,7 @@ function edgeSignalNote(e) {
17708
17835
  function buildRootCauseSection(graph, node, incidents, now) {
17709
17836
  const result = getRootCause(graph, node, void 0, incidents, { now });
17710
17837
  if (!result) return null;
17711
- const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types60.Provenance.OBSERVED;
17838
+ const lastProv = result.edgeProvenances[result.edgeProvenances.length - 1] ?? import_types62.Provenance.OBSERVED;
17712
17839
  const facts = [
17713
17840
  {
17714
17841
  text: `Root cause: ${result.rootCauseNode} \u2014 ${result.rootCauseReason}`,
@@ -17758,7 +17885,7 @@ function buildObservedSection(graph, node) {
17758
17885
  if (result.observed && result.inboundObservedCount > 0) {
17759
17886
  facts.push({
17760
17887
  text: `no outbound runtime calls, but OTel observed ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 a pure receiver`,
17761
- provenance: import_types60.Provenance.OBSERVED
17888
+ provenance: import_types62.Provenance.OBSERVED
17762
17889
  });
17763
17890
  } else {
17764
17891
  return null;
@@ -17786,7 +17913,7 @@ function buildIncidentsSection(node, incidents) {
17786
17913
  const facts = ordered.map((ev) => ({
17787
17914
  text: `${ev.timestamp} \u2014 ${ev.service}: ${ev.errorMessage}`,
17788
17915
  // ErrorEvents are observation records — OBSERVED by definition.
17789
- provenance: import_types60.Provenance.OBSERVED
17916
+ provenance: import_types62.Provenance.OBSERVED
17790
17917
  }));
17791
17918
  return { heading: `Recent incidents (OBSERVED) \u2014 ${relevant.length} recorded`, facts };
17792
17919
  }
@@ -17846,7 +17973,7 @@ function buildGlobalIncidentsSection(incidents) {
17846
17973
  }
17847
17974
  const byKey = /* @__PURE__ */ new Map();
17848
17975
  for (const ev of incidents) {
17849
- const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
17976
+ const key = ev.affectedNode || (0, import_types62.serviceId)(ev.service);
17850
17977
  const cur = byKey.get(key);
17851
17978
  if (!cur) {
17852
17979
  byKey.set(key, { key, count: 1, latest: ev.timestamp, sampleMsg: ev.errorMessage });
@@ -17861,7 +17988,7 @@ function buildGlobalIncidentsSection(incidents) {
17861
17988
  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);
17862
17989
  const facts = rows.map((r) => ({
17863
17990
  text: `${r.key} \u2014 ${r.count} incident${r.count === 1 ? "" : "s"}, latest ${r.latest}: ${r.sampleMsg}`,
17864
- provenance: import_types60.Provenance.OBSERVED
17991
+ provenance: import_types62.Provenance.OBSERVED
17865
17992
  }));
17866
17993
  return {
17867
17994
  heading: `Incidents across the system \u2014 ${incidents.length} recorded`,
@@ -17874,7 +18001,7 @@ function buildOverviewSections(graph, incidents) {
17874
18001
  graph.forEachNode((_id, attrs) => {
17875
18002
  const node = attrs;
17876
18003
  nodeByType.set(node.type, (nodeByType.get(node.type) ?? 0) + 1);
17877
- if (node.type === import_types60.NodeType.ServiceNode) services.push(node.id);
18004
+ if (node.type === import_types62.NodeType.ServiceNode) services.push(node.id);
17878
18005
  });
17879
18006
  const edgeByProv = /* @__PURE__ */ new Map();
17880
18007
  graph.forEachEdge((_id, attrs) => {
@@ -17886,10 +18013,10 @@ function buildOverviewSections(graph, incidents) {
17886
18013
  const shapeFacts = [
17887
18014
  { text: `${graph.order} nodes, ${graph.size} edges` },
17888
18015
  {
17889
- 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`
18016
+ 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`
17890
18017
  }
17891
18018
  ];
17892
- for (const p of [import_types60.Provenance.EXTRACTED, import_types60.Provenance.OBSERVED, import_types60.Provenance.INFERRED, import_types60.Provenance.STALE]) {
18019
+ for (const p of [import_types62.Provenance.EXTRACTED, import_types62.Provenance.OBSERVED, import_types62.Provenance.INFERRED, import_types62.Provenance.STALE]) {
17893
18020
  const n = edgeByProv.get(p) ?? 0;
17894
18021
  if (n > 0) shapeFacts.push({ text: `${n} ${p} edge${n === 1 ? "" : "s"}`, provenance: p });
17895
18022
  }
@@ -17906,7 +18033,7 @@ function buildOverviewSections(graph, incidents) {
17906
18033
  if (incidents && incidents.length > 0) {
17907
18034
  const incCount = /* @__PURE__ */ new Map();
17908
18035
  for (const ev of incidents) {
17909
- const key = ev.affectedNode || (0, import_types60.serviceId)(ev.service);
18036
+ const key = ev.affectedNode || (0, import_types62.serviceId)(ev.service);
17910
18037
  incCount.set(key, (incCount.get(key) ?? 0) + 1);
17911
18038
  }
17912
18039
  const top = [...incCount.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_FACTS_PER_SECTION);
@@ -17914,7 +18041,7 @@ function buildOverviewSections(graph, incidents) {
17914
18041
  heading: `Nodes with incidents \u2014 ${incidents.length} recorded`,
17915
18042
  facts: top.map(([k, n]) => ({
17916
18043
  text: `${k} \u2014 ${n} incident${n === 1 ? "" : "s"}`,
17917
- provenance: import_types60.Provenance.OBSERVED
18044
+ provenance: import_types62.Provenance.OBSERVED
17918
18045
  }))
17919
18046
  });
17920
18047
  }
@@ -18050,7 +18177,7 @@ async function askGraph(graph, question, opts = {}) {
18050
18177
  const provSet = /* @__PURE__ */ new Set();
18051
18178
  for (const s of sections) for (const f of s.facts) if (f.provenance) provSet.add(f.provenance);
18052
18179
  const confidence = sections[0]?.facts[0]?.confidence;
18053
- return import_types60.AskResultSchema.parse({
18180
+ return import_types62.AskResultSchema.parse({
18054
18181
  question,
18055
18182
  intent,
18056
18183
  matched,
@@ -18142,23 +18269,23 @@ function canonicalJson(value) {
18142
18269
 
18143
18270
  // src/projects.ts
18144
18271
  init_cjs_shims();
18145
- var import_node_path71 = __toESM(require("path"), 1);
18272
+ var import_node_path72 = __toESM(require("path"), 1);
18146
18273
  function pathsForProject(project, baseDir) {
18147
18274
  if (project === DEFAULT_PROJECT) {
18148
18275
  return {
18149
- snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
18150
- errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
18151
- staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
18152
- embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
18153
- policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
18276
+ snapshotPath: import_node_path72.default.join(baseDir, "graph.json"),
18277
+ errorsPath: import_node_path72.default.join(baseDir, "errors.ndjson"),
18278
+ staleEventsPath: import_node_path72.default.join(baseDir, "stale-events.ndjson"),
18279
+ embeddingsCachePath: import_node_path72.default.join(baseDir, "embeddings.json"),
18280
+ policyViolationsPath: import_node_path72.default.join(baseDir, "policy-violations.ndjson")
18154
18281
  };
18155
18282
  }
18156
18283
  return {
18157
- snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
18158
- errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
18159
- staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
18160
- embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
18161
- policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
18284
+ snapshotPath: import_node_path72.default.join(baseDir, `${project}.json`),
18285
+ errorsPath: import_node_path72.default.join(baseDir, `errors.${project}.ndjson`),
18286
+ staleEventsPath: import_node_path72.default.join(baseDir, `stale-events.${project}.ndjson`),
18287
+ embeddingsCachePath: import_node_path72.default.join(baseDir, `embeddings.${project}.json`),
18288
+ policyViolationsPath: import_node_path72.default.join(baseDir, `policy-violations.${project}.ndjson`)
18162
18289
  };
18163
18290
  }
18164
18291
  var Projects = class {
@@ -18196,26 +18323,26 @@ var Projects = class {
18196
18323
  init_cjs_shims();
18197
18324
  var import_node_fs37 = require("fs");
18198
18325
  var import_node_os3 = __toESM(require("os"), 1);
18199
- var import_node_path72 = __toESM(require("path"), 1);
18200
- var import_types61 = require("@neat.is/types");
18326
+ var import_node_path73 = __toESM(require("path"), 1);
18327
+ var import_types63 = require("@neat.is/types");
18201
18328
  var LOCK_TIMEOUT_MS = 5e3;
18202
18329
  var LOCK_RETRY_MS = 50;
18203
18330
  function neatHome() {
18204
18331
  const override = process.env.NEAT_HOME;
18205
- if (override && override.length > 0) return import_node_path72.default.resolve(override);
18206
- return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
18332
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
18333
+ return import_node_path73.default.join(import_node_os3.default.homedir(), ".neat");
18207
18334
  }
18208
18335
  function registryPath() {
18209
- return import_node_path72.default.join(neatHome(), "projects.json");
18336
+ return import_node_path73.default.join(neatHome(), "projects.json");
18210
18337
  }
18211
18338
  function registryLockPath() {
18212
- return import_node_path72.default.join(neatHome(), "projects.json.lock");
18339
+ return import_node_path73.default.join(neatHome(), "projects.json.lock");
18213
18340
  }
18214
18341
  function daemonPidPath() {
18215
- return import_node_path72.default.join(neatHome(), "neatd.pid");
18342
+ return import_node_path73.default.join(neatHome(), "neatd.pid");
18216
18343
  }
18217
18344
  function daemonsDir() {
18218
- return import_node_path72.default.join(neatHome(), "daemons");
18345
+ return import_node_path73.default.join(neatHome(), "daemons");
18219
18346
  }
18220
18347
  function isFiniteInt(v) {
18221
18348
  return typeof v === "number" && Number.isFinite(v);
@@ -18256,7 +18383,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
18256
18383
  const out = [];
18257
18384
  for (const name of names) {
18258
18385
  if (!name.endsWith(".json")) continue;
18259
- const file = import_node_path72.default.join(dir, name);
18386
+ const file = import_node_path73.default.join(dir, name);
18260
18387
  let raw;
18261
18388
  try {
18262
18389
  raw = await import_node_fs37.promises.readFile(file, "utf8");
@@ -18333,7 +18460,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
18333
18460
  }
18334
18461
  }
18335
18462
  async function normalizeProjectPath(input) {
18336
- const resolved = import_node_path72.default.resolve(input);
18463
+ const resolved = import_node_path73.default.resolve(input);
18337
18464
  try {
18338
18465
  return await import_node_fs37.promises.realpath(resolved);
18339
18466
  } catch {
@@ -18341,7 +18468,7 @@ async function normalizeProjectPath(input) {
18341
18468
  }
18342
18469
  }
18343
18470
  async function writeAtomically(target, contents) {
18344
- await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(target), { recursive: true });
18471
+ await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(target), { recursive: true });
18345
18472
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
18346
18473
  const fd = await import_node_fs37.promises.open(tmp, "w");
18347
18474
  try {
@@ -18354,7 +18481,7 @@ async function writeAtomically(target, contents) {
18354
18481
  }
18355
18482
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
18356
18483
  const deadline = Date.now() + timeoutMs;
18357
- await import_node_fs37.promises.mkdir(import_node_path72.default.dirname(lockPath), { recursive: true });
18484
+ await import_node_fs37.promises.mkdir(import_node_path73.default.dirname(lockPath), { recursive: true });
18358
18485
  let probedHolder = false;
18359
18486
  while (true) {
18360
18487
  try {
@@ -18407,10 +18534,10 @@ async function readRegistry() {
18407
18534
  throw err;
18408
18535
  }
18409
18536
  const parsed = JSON.parse(raw);
18410
- return import_types61.RegistryFileSchema.parse(parsed);
18537
+ return import_types63.RegistryFileSchema.parse(parsed);
18411
18538
  }
18412
18539
  async function writeRegistry(reg) {
18413
- const validated = import_types61.RegistryFileSchema.parse(reg);
18540
+ const validated = import_types63.RegistryFileSchema.parse(reg);
18414
18541
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
18415
18542
  }
18416
18543
  var ProjectNameCollisionError = class extends Error {
@@ -18605,7 +18732,7 @@ init_auth();
18605
18732
  // src/connectors-config.ts
18606
18733
  init_cjs_shims();
18607
18734
  var import_node_os4 = __toESM(require("os"), 1);
18608
- var import_node_path73 = __toESM(require("path"), 1);
18735
+ var import_node_path74 = __toESM(require("path"), 1);
18609
18736
  var import_node_fs38 = require("fs");
18610
18737
  var CONNECTORS_CONFIG_VERSION = 1;
18611
18738
  var EnvRefUnsetError = class extends Error {
@@ -18620,11 +18747,11 @@ var EnvRefUnsetError = class extends Error {
18620
18747
  };
18621
18748
  function neatHome2() {
18622
18749
  const override = process.env.NEAT_HOME;
18623
- if (override && override.length > 0) return import_node_path73.default.resolve(override);
18624
- return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
18750
+ if (override && override.length > 0) return import_node_path74.default.resolve(override);
18751
+ return import_node_path74.default.join(import_node_os4.default.homedir(), ".neat");
18625
18752
  }
18626
18753
  function connectorsConfigPath(home = neatHome2()) {
18627
- return import_node_path73.default.join(home, "connectors.json");
18754
+ return import_node_path74.default.join(home, "connectors.json");
18628
18755
  }
18629
18756
  var MODE_MASK_LOOSER_THAN_0600 = 63;
18630
18757
  async function warnIfModeLooserThan0600(file) {
@@ -18811,15 +18938,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
18811
18938
 
18812
18939
  // src/connectors/index.ts
18813
18940
  init_cjs_shims();
18814
- var import_types62 = require("@neat.is/types");
18941
+ var import_types64 = require("@neat.is/types");
18815
18942
  var NO_ENV = "unknown";
18816
18943
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
18817
18944
  if (!graph.hasNode(targetNodeId)) return void 0;
18818
18945
  const sites = [];
18819
18946
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
18820
18947
  const edge = graph.getEdgeAttributes(edgeId);
18821
- if (edge.provenance !== import_types62.Provenance.EXTRACTED) continue;
18822
- const parsed = (0, import_types62.parseFileId)(edge.source);
18948
+ if (edge.provenance !== import_types64.Provenance.EXTRACTED) continue;
18949
+ const parsed = (0, import_types64.parseFileId)(edge.source);
18823
18950
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
18824
18951
  const site = { relPath: edge.evidence.file };
18825
18952
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -18830,7 +18957,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
18830
18957
  function routeCallSiteFor(graph, targetNodeId) {
18831
18958
  if (!graph.hasNode(targetNodeId)) return void 0;
18832
18959
  const attrs = graph.getNodeAttributes(targetNodeId);
18833
- if (attrs.type !== import_types62.NodeType.RouteNode || !attrs.path) return void 0;
18960
+ if (attrs.type !== import_types64.NodeType.RouteNode || !attrs.path) return void 0;
18834
18961
  const site = { relPath: attrs.path };
18835
18962
  if (attrs.line !== void 0) site.line = attrs.line;
18836
18963
  return site;
@@ -19328,10 +19455,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
19328
19455
  // src/connectors/supabase/map.ts
19329
19456
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
19330
19457
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
19331
- function targetFromRestPath(path76) {
19332
- const rpcMatch = REST_RPC_PATH_RE.exec(path76);
19458
+ function targetFromRestPath(path78) {
19459
+ const rpcMatch = REST_RPC_PATH_RE.exec(path78);
19333
19460
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
19334
- const tableMatch = REST_TABLE_PATH_RE.exec(path76);
19461
+ const tableMatch = REST_TABLE_PATH_RE.exec(path78);
19335
19462
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
19336
19463
  return null;
19337
19464
  }
@@ -19379,7 +19506,7 @@ function tableNameFromQueryText(query) {
19379
19506
  if (SYSTEM_SCHEMA_PREFIXES.some((prefix) => lower.startsWith(prefix))) return null;
19380
19507
  return name;
19381
19508
  }
19382
- function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
19509
+ function diffPgStatStatementsToSignals(rows, previous, nowIso3) {
19383
19510
  const signals = [];
19384
19511
  const seen = /* @__PURE__ */ new Set();
19385
19512
  if (!Array.isArray(rows)) return signals;
@@ -19401,7 +19528,7 @@ function diffPgStatStatementsToSignals(rows, previous, nowIso2) {
19401
19528
  targetName: table,
19402
19529
  callCount: delta,
19403
19530
  errorCount: 0,
19404
- lastObservedIso: nowIso2,
19531
+ lastObservedIso: nowIso3,
19405
19532
  ...columns.length > 0 ? { columns } : {}
19406
19533
  });
19407
19534
  }
@@ -19442,23 +19569,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
19442
19569
 
19443
19570
  // src/connectors/supabase/resolve.ts
19444
19571
  init_cjs_shims();
19445
- var import_types64 = require("@neat.is/types");
19572
+ var import_types66 = require("@neat.is/types");
19446
19573
  function createSupabaseResolveTarget(graph, config) {
19447
19574
  return (signal, _ctx) => {
19448
19575
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
19449
19576
  return null;
19450
19577
  }
19451
- const subResourceId = (0, import_types64.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19578
+ const subResourceId = (0, import_types66.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
19452
19579
  if (graph.hasNode(subResourceId)) {
19453
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19580
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
19454
19581
  }
19455
- const bareResourceId = (0, import_types64.infraId)(signal.targetKind, signal.targetName);
19582
+ const bareResourceId = (0, import_types66.infraId)(signal.targetKind, signal.targetName);
19456
19583
  if (graph.hasNode(bareResourceId)) {
19457
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19584
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
19458
19585
  }
19459
- const projectLevelId = (0, import_types64.infraId)("supabase", config.nodeRef);
19586
+ const projectLevelId = (0, import_types66.infraId)("supabase", config.nodeRef);
19460
19587
  if (graph.hasNode(projectLevelId)) {
19461
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types64.EdgeType.CALLS };
19588
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types66.EdgeType.CALLS };
19462
19589
  }
19463
19590
  return null;
19464
19591
  };
@@ -19551,7 +19678,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
19551
19678
 
19552
19679
  // src/connectors/railway/index.ts
19553
19680
  init_cjs_shims();
19554
- var import_types68 = require("@neat.is/types");
19681
+ var import_types70 = require("@neat.is/types");
19555
19682
 
19556
19683
  // src/connectors/railway/client.ts
19557
19684
  init_cjs_shims();
@@ -19702,7 +19829,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
19702
19829
  const out = [];
19703
19830
  graph.forEachNode((_id, attrs) => {
19704
19831
  const node = attrs;
19705
- if (node.type !== import_types68.NodeType.RouteNode) return;
19832
+ if (node.type !== import_types70.NodeType.RouteNode) return;
19706
19833
  const route = attrs;
19707
19834
  if (route.service !== serviceName) return;
19708
19835
  out.push({
@@ -19806,12 +19933,12 @@ function createRailwayResolveTarget(config) {
19806
19933
  const serviceName = config.serviceNameById[config.serviceId];
19807
19934
  if (!serviceName) return null;
19808
19935
  if (signal.targetKind === ROUTE_TARGET_KIND) {
19809
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types68.EdgeType.CALLS };
19936
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types70.EdgeType.CALLS };
19810
19937
  }
19811
19938
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
19812
19939
  const peerName = config.serviceNameById[signal.targetName];
19813
19940
  if (!peerName) return null;
19814
- return { targetNodeId: (0, import_types68.serviceId)(peerName), serviceName, edgeType: import_types68.EdgeType.CONNECTS_TO };
19941
+ return { targetNodeId: (0, import_types70.serviceId)(peerName), serviceName, edgeType: import_types70.EdgeType.CONNECTS_TO };
19815
19942
  }
19816
19943
  return null;
19817
19944
  };
@@ -19935,9 +20062,9 @@ function parseFirebaseTargetName(targetName) {
19935
20062
  const secondSep = rest.indexOf(FIELD_SEP);
19936
20063
  if (secondSep === -1) return null;
19937
20064
  const method = rest.slice(0, secondSep);
19938
- const path76 = rest.slice(secondSep + 1);
19939
- if (!resourceName || !method || !path76) return null;
19940
- return { resourceName, method, path: path76 };
20065
+ const path78 = rest.slice(secondSep + 1);
20066
+ if (!resourceName || !method || !path78) return null;
20067
+ return { resourceName, method, path: path78 };
19941
20068
  }
19942
20069
  function resourceNameFor(type, labels) {
19943
20070
  if (!labels) return null;
@@ -19975,14 +20102,14 @@ function mapLogEntryToSignal(entry) {
19975
20102
  if (!req) return null;
19976
20103
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
19977
20104
  const method = req.requestMethod.toUpperCase();
19978
- const path76 = pathFromRequestUrl(req.requestUrl);
19979
- if (path76 === null) return null;
20105
+ const path78 = pathFromRequestUrl(req.requestUrl);
20106
+ if (path78 === null) return null;
19980
20107
  const timestamp = entry.timestamp;
19981
20108
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
19982
20109
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
19983
20110
  return {
19984
20111
  targetKind: resourceType,
19985
- targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
20112
+ targetName: packFirebaseTargetName({ resourceName, method, path: path78 }),
19986
20113
  callCount: 1,
19987
20114
  errorCount: isError ? 1 : 0,
19988
20115
  lastObservedIso: timestamp
@@ -19999,7 +20126,7 @@ function mapLogEntriesToSignals(entries) {
19999
20126
 
20000
20127
  // src/connectors/firebase/resolve.ts
20001
20128
  init_cjs_shims();
20002
- var import_types69 = require("@neat.is/types");
20129
+ var import_types71 = require("@neat.is/types");
20003
20130
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
20004
20131
  switch (resourceType) {
20005
20132
  case "cloud_function":
@@ -20014,7 +20141,7 @@ function routeEntriesFor(graph, serviceName) {
20014
20141
  const entries = [];
20015
20142
  graph.forEachNode((_id, attrs) => {
20016
20143
  const node = attrs;
20017
- if (node.type !== import_types69.NodeType.RouteNode) return;
20144
+ if (node.type !== import_types71.NodeType.RouteNode) return;
20018
20145
  const route = attrs;
20019
20146
  if (route.service !== serviceName) return;
20020
20147
  entries.push({
@@ -20046,7 +20173,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
20046
20173
  return {
20047
20174
  targetNodeId: match.routeNodeId,
20048
20175
  serviceName,
20049
- edgeType: import_types69.EdgeType.CALLS
20176
+ edgeType: import_types71.EdgeType.CALLS
20050
20177
  };
20051
20178
  };
20052
20179
  }
@@ -20073,7 +20200,7 @@ init_cjs_shims();
20073
20200
 
20074
20201
  // src/connectors/cloudflare/connector.ts
20075
20202
  init_cjs_shims();
20076
- var import_types71 = require("@neat.is/types");
20203
+ var import_types73 = require("@neat.is/types");
20077
20204
 
20078
20205
  // src/connectors/cloudflare/client.ts
20079
20206
  init_cjs_shims();
@@ -20189,7 +20316,7 @@ function mapEventToSignal(event) {
20189
20316
  if (Number.isNaN(observedAt.getTime())) return null;
20190
20317
  const statusCode = metadata?.statusCode;
20191
20318
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
20192
- const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20319
+ const path78 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
20193
20320
  return {
20194
20321
  targetKind: CLOUDFLARE_TARGET_KIND,
20195
20322
  targetName: scriptName,
@@ -20197,7 +20324,7 @@ function mapEventToSignal(event) {
20197
20324
  errorCount: isError ? 1 : 0,
20198
20325
  lastObservedIso: observedAt.toISOString(),
20199
20326
  method,
20200
- ...path76 ? { path: path76 } : {},
20327
+ ...path78 ? { path: path78 } : {},
20201
20328
  ...typeof statusCode === "number" ? { statusCode } : {},
20202
20329
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
20203
20330
  };
@@ -20237,19 +20364,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
20237
20364
  graph.forEachNode((id, attrs) => {
20238
20365
  if (found) return;
20239
20366
  const a = attrs;
20240
- if (a.type === import_types71.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
20367
+ if (a.type === import_types73.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
20241
20368
  found = id;
20242
20369
  }
20243
20370
  });
20244
20371
  return found;
20245
20372
  }
20246
- function findMatchingRouteNode(graph, serviceName, method, path76) {
20247
- const normalizedPath = normalizePathTemplate(path76);
20373
+ function findMatchingRouteNode(graph, serviceName, method, path78) {
20374
+ const normalizedPath = normalizePathTemplate(path78);
20248
20375
  let found = null;
20249
20376
  graph.forEachNode((id, attrs) => {
20250
20377
  if (found) return;
20251
20378
  const a = attrs;
20252
- if (a.type !== import_types71.NodeType.RouteNode || a.service !== serviceName) return;
20379
+ if (a.type !== import_types73.NodeType.RouteNode || a.service !== serviceName) return;
20253
20380
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
20254
20381
  const routeMethod = (a.method ?? "").toUpperCase();
20255
20382
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -20261,18 +20388,18 @@ function createCloudflareResolveTarget(config, graph) {
20261
20388
  return (signal) => {
20262
20389
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
20263
20390
  const scriptName = signal.targetName;
20264
- const { method, path: path76 } = signal;
20391
+ const { method, path: path78 } = signal;
20265
20392
  const resolveRouteGrain = (serviceName, wholeFileId) => {
20266
- if (!method || !path76) return wholeFileId;
20267
- return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
20393
+ if (!method || !path78) return wholeFileId;
20394
+ return findMatchingRouteNode(graph, serviceName, method, path78) ?? wholeFileId;
20268
20395
  };
20269
20396
  const mapping = config.workers?.[scriptName];
20270
20397
  if (mapping) {
20271
- const wholeFileId = (0, import_types71.fileId)(mapping.service, mapping.entryFile);
20398
+ const wholeFileId = (0, import_types73.fileId)(mapping.service, mapping.entryFile);
20272
20399
  return {
20273
20400
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
20274
20401
  serviceName: mapping.service,
20275
- edgeType: import_types71.EdgeType.CALLS
20402
+ edgeType: import_types73.EdgeType.CALLS
20276
20403
  };
20277
20404
  }
20278
20405
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -20281,13 +20408,13 @@ function createCloudflareResolveTarget(config, graph) {
20281
20408
  return {
20282
20409
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
20283
20410
  serviceName: fileNode.service,
20284
- edgeType: import_types71.EdgeType.CALLS
20411
+ edgeType: import_types73.EdgeType.CALLS
20285
20412
  };
20286
20413
  }
20287
20414
  return {
20288
- targetNodeId: (0, import_types71.infraId)("cloudflare-worker", scriptName),
20415
+ targetNodeId: (0, import_types73.infraId)("cloudflare-worker", scriptName),
20289
20416
  serviceName: scriptName,
20290
- edgeType: import_types71.EdgeType.CALLS,
20417
+ edgeType: import_types73.EdgeType.CALLS,
20291
20418
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
20292
20419
  };
20293
20420
  };
@@ -20483,14 +20610,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
20483
20610
 
20484
20611
  // src/connectors/neon/resolve.ts
20485
20612
  init_cjs_shims();
20486
- var import_types75 = require("@neat.is/types");
20613
+ var import_types77 = require("@neat.is/types");
20487
20614
  function createNeonResolveTarget(config) {
20488
20615
  return (signal) => {
20489
20616
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
20490
20617
  return {
20491
- targetNodeId: (0, import_types75.infraId)("sql-table", signal.targetName),
20618
+ targetNodeId: (0, import_types77.infraId)("sql-table", signal.targetName),
20492
20619
  serviceName: config.serviceName,
20493
- edgeType: import_types75.EdgeType.CALLS,
20620
+ edgeType: import_types77.EdgeType.CALLS,
20494
20621
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
20495
20622
  };
20496
20623
  };
@@ -20616,9 +20743,9 @@ function parseCloudRunTargetName(targetName) {
20616
20743
  const secondSep = rest.indexOf(FIELD_SEP2);
20617
20744
  if (secondSep === -1) return null;
20618
20745
  const method = rest.slice(0, secondSep);
20619
- const path76 = rest.slice(secondSep + 1);
20620
- if (!serviceName || !method || !path76) return null;
20621
- return { serviceName, method, path: path76 };
20746
+ const path78 = rest.slice(secondSep + 1);
20747
+ if (!serviceName || !method || !path78) return null;
20748
+ return { serviceName, method, path: path78 };
20622
20749
  }
20623
20750
 
20624
20751
  // src/connectors/cloud-run/map.ts
@@ -20647,14 +20774,14 @@ function mapLogEntryToSignal2(entry) {
20647
20774
  if (!req) return null;
20648
20775
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
20649
20776
  const method = req.requestMethod.toUpperCase();
20650
- const path76 = pathFromRequestUrl2(req.requestUrl);
20651
- if (path76 === null) return null;
20777
+ const path78 = pathFromRequestUrl2(req.requestUrl);
20778
+ if (path78 === null) return null;
20652
20779
  const timestamp = entry.timestamp;
20653
20780
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
20654
20781
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
20655
20782
  return {
20656
20783
  targetKind: CLOUD_RUN_TARGET_KIND,
20657
- targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
20784
+ targetName: packCloudRunTargetName({ serviceName, method, path: path78 }),
20658
20785
  callCount: 1,
20659
20786
  errorCount: isError ? 1 : 0,
20660
20787
  lastObservedIso: timestamp
@@ -20671,14 +20798,14 @@ function mapLogEntriesToSignals2(entries) {
20671
20798
 
20672
20799
  // src/connectors/cloud-run/resolve.ts
20673
20800
  init_cjs_shims();
20674
- var import_types79 = require("@neat.is/types");
20801
+ var import_types81 = require("@neat.is/types");
20675
20802
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
20676
20803
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
20677
20804
  let found = null;
20678
20805
  graph.forEachNode((_id, attrs) => {
20679
20806
  if (found) return;
20680
20807
  const node = attrs;
20681
- if (node.type !== import_types79.NodeType.RouteNode) return;
20808
+ if (node.type !== import_types81.NodeType.RouteNode) return;
20682
20809
  const route = attrs;
20683
20810
  if (route.service !== serviceName || !route.pathTemplate) return;
20684
20811
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20693,23 +20820,23 @@ function createCloudRunResolveTarget(graph, config) {
20693
20820
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
20694
20821
  const identity = parseCloudRunTargetName(signal.targetName);
20695
20822
  if (!identity) return null;
20696
- const { serviceName: gcpServiceName, method, path: path76 } = identity;
20823
+ const { serviceName: gcpServiceName, method, path: path78 } = identity;
20697
20824
  const mappedService = config.serviceMap?.[gcpServiceName];
20698
20825
  if (mappedService) {
20699
20826
  const routeNodeId = findMatchingRouteNode2(
20700
20827
  graph,
20701
20828
  mappedService,
20702
20829
  method,
20703
- normalizePathTemplate(path76)
20830
+ normalizePathTemplate(path78)
20704
20831
  );
20705
20832
  if (routeNodeId) {
20706
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types79.EdgeType.CALLS };
20833
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types81.EdgeType.CALLS };
20707
20834
  }
20708
20835
  }
20709
20836
  return {
20710
- targetNodeId: (0, import_types79.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20837
+ targetNodeId: (0, import_types81.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
20711
20838
  serviceName: mappedService ?? gcpServiceName,
20712
- edgeType: import_types79.EdgeType.CALLS,
20839
+ edgeType: import_types81.EdgeType.CALLS,
20713
20840
  ensureInfraNode: {
20714
20841
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
20715
20842
  name: gcpServiceName,
@@ -20836,9 +20963,9 @@ function parseGcpLbTargetName(targetName) {
20836
20963
  const secondSep = rest.indexOf(FIELD_SEP3);
20837
20964
  if (secondSep === -1) return null;
20838
20965
  const method = rest.slice(0, secondSep);
20839
- const path76 = rest.slice(secondSep + 1);
20840
- if (!backendServiceName || !method || !path76) return null;
20841
- return { backendServiceName, method, path: path76 };
20966
+ const path78 = rest.slice(secondSep + 1);
20967
+ if (!backendServiceName || !method || !path78) return null;
20968
+ return { backendServiceName, method, path: path78 };
20842
20969
  }
20843
20970
 
20844
20971
  // src/connectors/gcp-lb/map.ts
@@ -20867,14 +20994,14 @@ function mapLogEntryToSignal3(entry) {
20867
20994
  if (!req) return null;
20868
20995
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
20869
20996
  const method = req.requestMethod.toUpperCase();
20870
- const path76 = pathFromRequestUrl3(req.requestUrl);
20871
- if (path76 === null) return null;
20997
+ const path78 = pathFromRequestUrl3(req.requestUrl);
20998
+ if (path78 === null) return null;
20872
20999
  const timestamp = entry.timestamp;
20873
21000
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
20874
21001
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD5;
20875
21002
  return {
20876
21003
  targetKind: GCP_LB_TARGET_KIND,
20877
- targetName: packGcpLbTargetName({ backendServiceName, method, path: path76 }),
21004
+ targetName: packGcpLbTargetName({ backendServiceName, method, path: path78 }),
20878
21005
  callCount: 1,
20879
21006
  errorCount: isError ? 1 : 0,
20880
21007
  lastObservedIso: timestamp
@@ -20891,14 +21018,14 @@ function mapLogEntriesToSignals3(entries) {
20891
21018
 
20892
21019
  // src/connectors/gcp-lb/resolve.ts
20893
21020
  init_cjs_shims();
20894
- var import_types83 = require("@neat.is/types");
21021
+ var import_types85 = require("@neat.is/types");
20895
21022
  var GCP_LB_BACKEND_INFRA_KIND = "gcp-lb-backend";
20896
21023
  function findMatchingRouteNode3(graph, serviceName, method, normalizedPath) {
20897
21024
  let found = null;
20898
21025
  graph.forEachNode((_id, attrs) => {
20899
21026
  if (found) return;
20900
21027
  const node = attrs;
20901
- if (node.type !== import_types83.NodeType.RouteNode) return;
21028
+ if (node.type !== import_types85.NodeType.RouteNode) return;
20902
21029
  const route = attrs;
20903
21030
  if (route.service !== serviceName || !route.pathTemplate) return;
20904
21031
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -20913,23 +21040,23 @@ function createGcpLbResolveTarget(graph, config) {
20913
21040
  if (signal.targetKind !== GCP_LB_TARGET_KIND) return null;
20914
21041
  const identity = parseGcpLbTargetName(signal.targetName);
20915
21042
  if (!identity) return null;
20916
- const { backendServiceName, method, path: path76 } = identity;
21043
+ const { backendServiceName, method, path: path78 } = identity;
20917
21044
  const mappedService = config.backendServiceMap?.[backendServiceName];
20918
21045
  if (mappedService) {
20919
21046
  const routeNodeId = findMatchingRouteNode3(
20920
21047
  graph,
20921
21048
  mappedService,
20922
21049
  method,
20923
- normalizePathTemplate(path76)
21050
+ normalizePathTemplate(path78)
20924
21051
  );
20925
21052
  if (routeNodeId) {
20926
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types83.EdgeType.CALLS };
21053
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types85.EdgeType.CALLS };
20927
21054
  }
20928
21055
  }
20929
21056
  return {
20930
- targetNodeId: (0, import_types83.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
21057
+ targetNodeId: (0, import_types85.infraId)(GCP_LB_BACKEND_INFRA_KIND, backendServiceName),
20931
21058
  serviceName: mappedService ?? backendServiceName,
20932
- edgeType: import_types83.EdgeType.CALLS,
21059
+ edgeType: import_types85.EdgeType.CALLS,
20933
21060
  ensureInfraNode: {
20934
21061
  kind: GCP_LB_BACKEND_INFRA_KIND,
20935
21062
  name: backendServiceName,
@@ -20970,7 +21097,7 @@ function createGcpLbConnector(graph, config = {}) {
20970
21097
 
20971
21098
  // src/connectors/render/index.ts
20972
21099
  init_cjs_shims();
20973
- var import_types86 = require("@neat.is/types");
21100
+ var import_types88 = require("@neat.is/types");
20974
21101
 
20975
21102
  // src/connectors/render/types.ts
20976
21103
  init_cjs_shims();
@@ -21048,7 +21175,7 @@ function buildRenderRouteIndex(graph, serviceName) {
21048
21175
  const out = [];
21049
21176
  graph.forEachNode((_id, attrs) => {
21050
21177
  const node = attrs;
21051
- if (node.type !== import_types86.NodeType.RouteNode) return;
21178
+ if (node.type !== import_types88.NodeType.RouteNode) return;
21052
21179
  const route = attrs;
21053
21180
  if (route.service !== serviceName) return;
21054
21181
  out.push({
@@ -21133,7 +21260,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
21133
21260
  function createRenderResolveTarget(config) {
21134
21261
  return (signal) => {
21135
21262
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
21136
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types86.EdgeType.CALLS };
21263
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types88.EdgeType.CALLS };
21137
21264
  }
21138
21265
  return null;
21139
21266
  };
@@ -21271,21 +21398,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
21271
21398
 
21272
21399
  // src/connectors/planetscale/resolve.ts
21273
21400
  init_cjs_shims();
21274
- var import_types90 = require("@neat.is/types");
21401
+ var import_types92 = require("@neat.is/types");
21275
21402
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
21276
21403
  function createPlanetscaleResolveTarget(graph, config) {
21277
21404
  const databaseName = `${config.organization}/${config.database}`;
21278
21405
  return (signal, _ctx) => {
21279
21406
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
21280
- const tableId = (0, import_types90.infraId)("sql-table", signal.targetName);
21407
+ const tableId = (0, import_types92.infraId)("sql-table", signal.targetName);
21281
21408
  if (graph.hasNode(tableId)) {
21282
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types90.EdgeType.CALLS };
21409
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types92.EdgeType.CALLS };
21283
21410
  }
21284
- const providerId = (0, import_types90.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21411
+ const providerId = (0, import_types92.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
21285
21412
  return {
21286
21413
  targetNodeId: providerId,
21287
21414
  serviceName: config.serviceName,
21288
- edgeType: import_types90.EdgeType.CALLS,
21415
+ edgeType: import_types92.EdgeType.CALLS,
21289
21416
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
21290
21417
  };
21291
21418
  };
@@ -21550,7 +21677,7 @@ function mapBuildsToSignals(builds, serviceName) {
21550
21677
 
21551
21678
  // src/connectors/eas/resolve.ts
21552
21679
  init_cjs_shims();
21553
- var import_types95 = require("@neat.is/types");
21680
+ var import_types97 = require("@neat.is/types");
21554
21681
  var NO_ENV2 = "unknown";
21555
21682
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
21556
21683
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -21566,8 +21693,8 @@ function configBasenamesForPhase(phase) {
21566
21693
  function configNodeService(graph, configNodeId) {
21567
21694
  for (const edgeId of graph.inboundEdges(configNodeId)) {
21568
21695
  const edge = graph.getEdgeAttributes(edgeId);
21569
- if (edge.type !== import_types95.EdgeType.CONFIGURED_BY) continue;
21570
- const parsed = (0, import_types95.parseFileId)(edge.source);
21696
+ if (edge.type !== import_types97.EdgeType.CONFIGURED_BY) continue;
21697
+ const parsed = (0, import_types97.parseFileId)(edge.source);
21571
21698
  if (parsed) return parsed.service;
21572
21699
  }
21573
21700
  return null;
@@ -21578,7 +21705,7 @@ function findConfigNode(graph, basenames, serviceName) {
21578
21705
  graph.forEachNode((id, attrs) => {
21579
21706
  if (scoped) return;
21580
21707
  const node = attrs;
21581
- if (node.type !== import_types95.NodeType.ConfigNode) return;
21708
+ if (node.type !== import_types97.NodeType.ConfigNode) return;
21582
21709
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
21583
21710
  if (anyMatch === null) anyMatch = id;
21584
21711
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -21595,13 +21722,13 @@ function createEasResolveTarget(graph) {
21595
21722
  if (basenames.length > 0) {
21596
21723
  const configNodeId = findConfigNode(graph, basenames, serviceName);
21597
21724
  if (configNodeId) {
21598
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types95.EdgeType.CALLS };
21725
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types97.EdgeType.CALLS };
21599
21726
  }
21600
21727
  }
21601
21728
  return {
21602
21729
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
21603
21730
  serviceName,
21604
- edgeType: import_types95.EdgeType.CALLS
21731
+ edgeType: import_types97.EdgeType.CALLS
21605
21732
  };
21606
21733
  };
21607
21734
  }
@@ -22218,11 +22345,11 @@ function serializeGraph(graph) {
22218
22345
  }
22219
22346
  function projectFromReq(req, singleProject) {
22220
22347
  const params = req.params;
22221
- const named = params.project;
22348
+ const named2 = params.project;
22222
22349
  if (singleProject) {
22223
- return named === void 0 || named === DEFAULT_PROJECT ? singleProject : named;
22350
+ return named2 === void 0 || named2 === DEFAULT_PROJECT ? singleProject : named2;
22224
22351
  }
22225
- return named ?? DEFAULT_PROJECT;
22352
+ return named2 ?? DEFAULT_PROJECT;
22226
22353
  }
22227
22354
  function resolveProject(registry, req, reply, bootstrap, singleProject) {
22228
22355
  const name = projectFromReq(req, singleProject);
@@ -22363,11 +22490,11 @@ function registerRoutes(scope, ctx) {
22363
22490
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
22364
22491
  const parsed = [];
22365
22492
  for (const c of candidates) {
22366
- const r = import_types98.DivergenceTypeSchema.safeParse(c);
22493
+ const r = import_types100.DivergenceTypeSchema.safeParse(c);
22367
22494
  if (!r.success) {
22368
22495
  return reply.code(400).send({
22369
22496
  error: `unknown divergence type "${c}"`,
22370
- allowed: import_types98.DivergenceTypeSchema.options
22497
+ allowed: import_types100.DivergenceTypeSchema.options
22371
22498
  });
22372
22499
  }
22373
22500
  parsed.push(r.data);
@@ -22763,7 +22890,7 @@ function registerRoutes(scope, ctx) {
22763
22890
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
22764
22891
  let violations = await log.readAll();
22765
22892
  if (req.query.severity) {
22766
- const sev = import_types98.PolicySeveritySchema.safeParse(req.query.severity);
22893
+ const sev = import_types100.PolicySeveritySchema.safeParse(req.query.severity);
22767
22894
  if (!sev.success) {
22768
22895
  return reply.code(400).send({
22769
22896
  error: "invalid severity",
@@ -22802,7 +22929,7 @@ function registerRoutes(scope, ctx) {
22802
22929
  scope.post("/policies/check", async (req, reply) => {
22803
22930
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
22804
22931
  if (!proj) return;
22805
- const parsed = import_types98.PoliciesCheckBodySchema.safeParse(req.body ?? {});
22932
+ const parsed = import_types100.PoliciesCheckBodySchema.safeParse(req.body ?? {});
22806
22933
  if (!parsed.success) {
22807
22934
  return reply.code(400).send({
22808
22935
  error: "invalid /policies/check body",
@@ -23123,16 +23250,458 @@ init_otel_grpc();
23123
23250
 
23124
23251
  // src/daemon.ts
23125
23252
  init_cjs_shims();
23126
- var import_node_fs40 = require("fs");
23127
- var import_node_path75 = __toESM(require("path"), 1);
23253
+ var import_node_fs41 = require("fs");
23254
+ var import_node_path77 = __toESM(require("path"), 1);
23128
23255
  var import_node_module = require("module");
23129
23256
  init_otel();
23257
+
23258
+ // src/connectors/kubernetes/index.ts
23259
+ init_cjs_shims();
23260
+
23261
+ // src/connectors/kubernetes/client.ts
23262
+ init_cjs_shims();
23263
+ var import_node_https = require("https");
23264
+ function deploymentsPath(namespace) {
23265
+ return `/apis/apps/v1/namespaces/${namespace}/deployments`;
23266
+ }
23267
+ function podsPath(namespace) {
23268
+ return `/api/v1/namespaces/${namespace}/pods`;
23269
+ }
23270
+ function makeK8sFetchImpl(transport) {
23271
+ const agent = new import_node_https.Agent({
23272
+ ...transport.ca ? { ca: transport.ca } : {},
23273
+ ...transport.clientCert ? { cert: transport.clientCert } : {},
23274
+ ...transport.clientKey ? { key: transport.clientKey } : {},
23275
+ rejectUnauthorized: !transport.insecureSkipTlsVerify
23276
+ });
23277
+ return ((url, init) => new Promise((resolve, reject) => {
23278
+ const u = new URL(String(url));
23279
+ const req = (0, import_node_https.request)(
23280
+ u,
23281
+ {
23282
+ method: (init?.method ?? "GET").toUpperCase(),
23283
+ headers: init?.headers ?? {},
23284
+ agent
23285
+ },
23286
+ (res) => {
23287
+ const chunks = [];
23288
+ res.on("data", (c) => chunks.push(c));
23289
+ res.on("end", () => {
23290
+ const body = Buffer.concat(chunks).toString("utf8");
23291
+ const status2 = res.statusCode ?? 0;
23292
+ resolve({
23293
+ ok: status2 >= 200 && status2 < 300,
23294
+ status: status2,
23295
+ statusText: res.statusMessage ?? "",
23296
+ json: async () => JSON.parse(body),
23297
+ text: async () => body
23298
+ });
23299
+ });
23300
+ }
23301
+ );
23302
+ req.on("error", reject);
23303
+ const signal = init?.signal;
23304
+ if (signal) {
23305
+ if (signal.aborted) req.destroy(new Error("aborted"));
23306
+ else signal.addEventListener("abort", () => req.destroy(new Error("aborted")), { once: true });
23307
+ }
23308
+ req.end();
23309
+ }));
23310
+ }
23311
+ async function listResource(transport, namespace, path78, opts = {}) {
23312
+ const base = opts.apiUrl ?? transport.server;
23313
+ const url = `${base.replace(/\/$/, "")}${path78}`;
23314
+ const fetchImpl = opts.fetchImpl ?? makeK8sFetchImpl(transport);
23315
+ const res = await junctionFetch(
23316
+ url,
23317
+ { method: "GET", headers: { ...transport.token ? bearerAuthHeader(transport.token) : {}, Accept: "application/json" } },
23318
+ // accountKey: the (cluster, namespace) pair — an identifier, safe to log, the
23319
+ // rate-limit bucket for one namespace on one cluster (ADR-131).
23320
+ { provider: "kubernetes", accountKey: `${safeHost(transport.server)}/${namespace}`, fetchImpl }
23321
+ );
23322
+ if (!res.ok) {
23323
+ throw new Error(`kubernetes ${path78} failed: ${res.status} ${res.statusText}`);
23324
+ }
23325
+ const json = await res.json();
23326
+ return Array.isArray(json.items) ? json.items : [];
23327
+ }
23328
+ function safeHost(server) {
23329
+ try {
23330
+ return new URL(server).host;
23331
+ } catch {
23332
+ return "cluster";
23333
+ }
23334
+ }
23335
+ async function fetchDeployments(transport, namespace, opts = {}) {
23336
+ return listResource(transport, namespace, deploymentsPath(namespace), opts);
23337
+ }
23338
+ async function fetchPods(transport, namespace, opts = {}) {
23339
+ return listResource(transport, namespace, podsPath(namespace), opts);
23340
+ }
23341
+
23342
+ // src/connectors/kubernetes/kubeconfig.ts
23343
+ init_cjs_shims();
23344
+ var import_node_fs39 = require("fs");
23345
+ var import_yaml4 = require("yaml");
23346
+ function pemFrom(dataField, pathField) {
23347
+ if (typeof dataField === "string" && dataField.length > 0) {
23348
+ return Buffer.from(dataField, "base64").toString("utf8");
23349
+ }
23350
+ if (typeof pathField === "string" && pathField.length > 0) {
23351
+ return (0, import_node_fs39.readFileSync)(pathField, "utf8");
23352
+ }
23353
+ return void 0;
23354
+ }
23355
+ function named(list, name) {
23356
+ if (!Array.isArray(list)) return void 0;
23357
+ const hit = list.find((e) => e && e.name === name);
23358
+ return hit;
23359
+ }
23360
+ function parseKubeconfig(kubeconfig) {
23361
+ const looksInline = /\n/.test(kubeconfig) || /(^|\s)clusters\s*:/.test(kubeconfig);
23362
+ const text = looksInline ? kubeconfig : (0, import_node_fs39.readFileSync)(kubeconfig, "utf8");
23363
+ let doc;
23364
+ try {
23365
+ doc = (0, import_yaml4.parse)(text);
23366
+ } catch {
23367
+ throw new Error("kubernetes connector: kubeconfig is not valid YAML");
23368
+ }
23369
+ if (!doc || typeof doc !== "object") {
23370
+ throw new Error("kubernetes connector: kubeconfig is empty or malformed");
23371
+ }
23372
+ const currentContext = doc["current-context"];
23373
+ if (typeof currentContext !== "string" || currentContext.length === 0) {
23374
+ throw new Error("kubernetes connector: kubeconfig has no current-context");
23375
+ }
23376
+ const ctxEntry = named(doc["contexts"], currentContext);
23377
+ const ctx = ctxEntry?.context;
23378
+ if (!ctx) {
23379
+ throw new Error(`kubernetes connector: kubeconfig context "${currentContext}" not found`);
23380
+ }
23381
+ const clusterEntry = named(doc["clusters"], String(ctx["cluster"] ?? ""));
23382
+ const cluster = clusterEntry?.cluster;
23383
+ if (!cluster || typeof cluster["server"] !== "string") {
23384
+ throw new Error("kubernetes connector: kubeconfig current context has no cluster server");
23385
+ }
23386
+ const userEntry = named(doc["users"], String(ctx["user"] ?? ""));
23387
+ const user = userEntry?.user ?? {};
23388
+ const transport = {
23389
+ server: cluster["server"],
23390
+ insecureSkipTlsVerify: cluster["insecure-skip-tls-verify"] === true
23391
+ };
23392
+ const ca = pemFrom(cluster["certificate-authority-data"], cluster["certificate-authority"]);
23393
+ if (ca) transport.ca = ca;
23394
+ if (typeof user["token"] === "string" && user["token"].length > 0) transport.token = user["token"];
23395
+ const clientCert = pemFrom(user["client-certificate-data"], user["client-certificate"]);
23396
+ const clientKey = pemFrom(user["client-key-data"], user["client-key"]);
23397
+ if (clientCert) transport.clientCert = clientCert;
23398
+ if (clientKey) transport.clientKey = clientKey;
23399
+ return transport;
23400
+ }
23401
+ function resolveK8sTransport(creds, config) {
23402
+ if (creds.kubeconfig) return parseKubeconfig(creds.kubeconfig);
23403
+ if (!config.apiServerUrl) {
23404
+ throw new Error("kubernetes connector: options.apiServerUrl is required with a token credential");
23405
+ }
23406
+ const transport = {
23407
+ server: config.apiServerUrl,
23408
+ insecureSkipTlsVerify: config.insecureSkipTlsVerify === true
23409
+ };
23410
+ if (creds.token) transport.token = creds.token;
23411
+ if (config.caCert) transport.ca = config.caCert;
23412
+ return transport;
23413
+ }
23414
+
23415
+ // src/connectors/kubernetes/map.ts
23416
+ init_cjs_shims();
23417
+
23418
+ // src/connectors/kubernetes/types.ts
23419
+ init_cjs_shims();
23420
+ function readK8sCredentials(raw) {
23421
+ const token = typeof raw["token"] === "string" && raw["token"].length > 0 ? raw["token"] : void 0;
23422
+ const kubeconfig = typeof raw["kubeconfig"] === "string" && raw["kubeconfig"].length > 0 ? raw["kubeconfig"] : void 0;
23423
+ if (!token && !kubeconfig) {
23424
+ throw new Error("kubernetes connector: credentials must carry a token or a kubeconfig");
23425
+ }
23426
+ const out = {};
23427
+ if (token) out.token = token;
23428
+ if (kubeconfig) out.kubeconfig = kubeconfig;
23429
+ return out;
23430
+ }
23431
+ var IMAGE_PULL_REASONS = /* @__PURE__ */ new Set(["ImagePullBackOff", "ErrImagePull", "InvalidImageName"]);
23432
+ var CRASH_LOOP_REASON = "CrashLoopBackOff";
23433
+ var FIELD_SEP5 = "\0";
23434
+ var K8S_TARGET_KIND = "k8s-workload";
23435
+ function packK8sTargetName(identity) {
23436
+ return [identity.serviceName, identity.fault].join(FIELD_SEP5);
23437
+ }
23438
+ function parseK8sTargetName(targetName) {
23439
+ const sep = targetName.indexOf(FIELD_SEP5);
23440
+ if (sep === -1) return null;
23441
+ const serviceName = targetName.slice(0, sep);
23442
+ const fault = targetName.slice(sep + 1);
23443
+ if (!serviceName || !fault) return null;
23444
+ return { serviceName, fault };
23445
+ }
23446
+
23447
+ // src/connectors/kubernetes/map.ts
23448
+ function serviceNameFor(deployment, config) {
23449
+ const name = deployment.metadata?.name ?? "";
23450
+ return config.serviceMap?.[name] ?? name;
23451
+ }
23452
+ function podMatchesSelector(pod, selector) {
23453
+ if (!selector || Object.keys(selector).length === 0) return false;
23454
+ const labels = pod.metadata?.labels ?? {};
23455
+ return Object.entries(selector).every(([k, v]) => labels[k] === v);
23456
+ }
23457
+ function podsForDeployment(deployment, pods) {
23458
+ const selector = deployment.spec?.selector?.matchLabels;
23459
+ return pods.filter((p) => podMatchesSelector(p, selector));
23460
+ }
23461
+ function nowIso2() {
23462
+ return (/* @__PURE__ */ new Date()).toISOString();
23463
+ }
23464
+ function podLevelFault(deployment, pods) {
23465
+ const name = deployment.metadata?.name ?? "";
23466
+ const owned = podsForDeployment(deployment, pods);
23467
+ let crash = null;
23468
+ for (const pod of owned) {
23469
+ for (const cs of pod.status?.containerStatuses ?? []) {
23470
+ const reason = cs.state?.waiting?.reason;
23471
+ if (typeof reason !== "string") continue;
23472
+ if (IMAGE_PULL_REASONS.has(reason)) {
23473
+ const image = typeof cs.image === "string" ? cs.image : "unknown image";
23474
+ const attrs = { "k8s.image": image, "k8s.waitingReason": reason };
23475
+ const wm = cs.state?.waiting?.message;
23476
+ if (typeof wm === "string" && wm.length > 0) attrs["k8s.waitingMessage"] = wm;
23477
+ return {
23478
+ fault: "image-pull",
23479
+ message: `Deployment ${name} cannot pull image ${image} (${reason})`,
23480
+ timestamp: pod.status?.startTime ?? nowIso2(),
23481
+ attributes: attrs
23482
+ };
23483
+ }
23484
+ if (reason === CRASH_LOOP_REASON && !crash) crash = { cs, pod };
23485
+ }
23486
+ }
23487
+ if (crash) {
23488
+ const { cs, pod } = crash;
23489
+ const term = cs.lastState?.terminated;
23490
+ const termReason = typeof term?.reason === "string" ? term.reason : void 0;
23491
+ const termMsg = typeof term?.message === "string" ? term.message.trim() : void 0;
23492
+ const restarts = typeof cs.restartCount === "number" ? cs.restartCount : 0;
23493
+ const detail = termReason ? `last terminated: ${termReason}${termMsg ? ` \u2014 ${termMsg}` : ""}${typeof term?.exitCode === "number" ? ` (exit ${term.exitCode})` : ""}` : "no last-termination detail reported";
23494
+ const attrs = { "k8s.waitingReason": CRASH_LOOP_REASON, "k8s.restartCount": restarts };
23495
+ if (termReason) attrs["k8s.terminatedReason"] = termReason;
23496
+ if (termMsg) attrs["k8s.terminatedMessage"] = termMsg;
23497
+ if (typeof term?.exitCode === "number") attrs["k8s.exitCode"] = term.exitCode;
23498
+ if (typeof cs.image === "string") attrs["k8s.image"] = cs.image;
23499
+ return {
23500
+ fault: "crash-loop",
23501
+ message: `Deployment ${name} is crashlooping (restarts: ${restarts}); ${detail}`,
23502
+ timestamp: term?.finishedAt ?? pod.status?.startTime ?? nowIso2(),
23503
+ attributes: attrs
23504
+ };
23505
+ }
23506
+ return null;
23507
+ }
23508
+ function classifyDeployment(deployment, pods, expectedZero) {
23509
+ const name = deployment.metadata?.name ?? "";
23510
+ const desired = typeof deployment.spec?.replicas === "number" ? deployment.spec.replicas : 1;
23511
+ const ready = typeof deployment.status?.readyReplicas === "number" ? deployment.status.readyReplicas : 0;
23512
+ if (desired === 0) {
23513
+ if (expectedZero?.has(name)) return null;
23514
+ return {
23515
+ fault: "scaled-to-zero",
23516
+ message: `Deployment ${name} is scaled to 0 \u2014 no running pods (desired 0)`,
23517
+ timestamp: nowIso2(),
23518
+ attributes: { "k8s.desiredReplicas": 0, "k8s.readyReplicas": ready }
23519
+ };
23520
+ }
23521
+ if (ready >= desired) return null;
23522
+ const podFault = podLevelFault(deployment, pods);
23523
+ if (podFault) {
23524
+ podFault.attributes["k8s.desiredReplicas"] = desired;
23525
+ podFault.attributes["k8s.readyReplicas"] = ready;
23526
+ return podFault;
23527
+ }
23528
+ return {
23529
+ fault: "no-ready-replicas",
23530
+ message: `Deployment ${name} has no ready replicas (desired ${desired}, ready ${ready})`,
23531
+ timestamp: nowIso2(),
23532
+ attributes: { "k8s.desiredReplicas": desired, "k8s.readyReplicas": ready }
23533
+ };
23534
+ }
23535
+ function mapDeploymentToSignal(deployment, pods, config) {
23536
+ const name = deployment.metadata?.name;
23537
+ if (typeof name !== "string" || name.length === 0) return null;
23538
+ const expectedZero = config.expectedZero ? new Set(config.expectedZero) : void 0;
23539
+ const finding = classifyDeployment(deployment, pods, expectedZero);
23540
+ if (!finding) return null;
23541
+ const serviceName = serviceNameFor(deployment, config);
23542
+ const namespace = deployment.metadata?.namespace ?? config.namespace;
23543
+ const attributes = {
23544
+ "k8s.namespace": namespace,
23545
+ "k8s.deployment": name,
23546
+ "k8s.fault": finding.fault,
23547
+ ...finding.attributes
23548
+ };
23549
+ return {
23550
+ targetKind: K8S_TARGET_KIND,
23551
+ targetName: packK8sTargetName({ serviceName, fault: finding.fault }),
23552
+ // Incident-only — no edge, so no call/error count to replay.
23553
+ callCount: 0,
23554
+ errorCount: 0,
23555
+ lastObservedIso: finding.timestamp,
23556
+ incident: {
23557
+ id: `k8s:deploy:${namespace}:${name}:${finding.fault}`,
23558
+ timestamp: finding.timestamp,
23559
+ service: serviceName,
23560
+ errorType: "k8s-deploy-failure",
23561
+ errorMessage: finding.message,
23562
+ attributes
23563
+ }
23564
+ };
23565
+ }
23566
+ function mapWorkloadsToSignals(deployments, pods, config) {
23567
+ const out = [];
23568
+ for (const deployment of deployments) {
23569
+ const signal = mapDeploymentToSignal(deployment, pods, config);
23570
+ if (signal) out.push(signal);
23571
+ }
23572
+ return out;
23573
+ }
23574
+
23575
+ // src/connectors/kubernetes/resolve.ts
23576
+ init_cjs_shims();
23577
+ var import_types102 = require("@neat.is/types");
23578
+ var NO_ENV3 = "unknown";
23579
+ function createK8sResolveTarget(graph) {
23580
+ return (signal) => {
23581
+ if (signal.targetKind !== K8S_TARGET_KIND) return null;
23582
+ const identity = parseK8sTargetName(signal.targetName);
23583
+ if (!identity) return null;
23584
+ const { serviceName } = identity;
23585
+ return {
23586
+ targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV3),
23587
+ serviceName,
23588
+ edgeType: import_types102.EdgeType.CALLS
23589
+ };
23590
+ };
23591
+ }
23592
+
23593
+ // src/connectors/kubernetes/substrate.ts
23594
+ init_cjs_shims();
23595
+ var import_promises = require("fs/promises");
23596
+ var import_node_os5 = __toESM(require("os"), 1);
23597
+ var import_node_path75 = __toESM(require("path"), 1);
23598
+ function defaultHome() {
23599
+ const override = process.env.NEAT_HOME;
23600
+ if (override && override.length > 0) return import_node_path75.default.resolve(override);
23601
+ return import_node_path75.default.join(import_node_os5.default.homedir(), ".neat");
23602
+ }
23603
+ function k8sSubstrateConfigPath(home = defaultHome()) {
23604
+ return import_node_path75.default.join(home, "k8s.json");
23605
+ }
23606
+ async function readK8sSubstrateConfig(home = defaultHome()) {
23607
+ let raw;
23608
+ try {
23609
+ raw = await (0, import_promises.readFile)(k8sSubstrateConfigPath(home), "utf8");
23610
+ } catch {
23611
+ return { version: 1, deployments: [] };
23612
+ }
23613
+ try {
23614
+ const parsed = JSON.parse(raw);
23615
+ if (!parsed || !Array.isArray(parsed.deployments)) return { version: 1, deployments: [] };
23616
+ return parsed;
23617
+ } catch {
23618
+ return { version: 1, deployments: [] };
23619
+ }
23620
+ }
23621
+ async function startK8sSubstratePolling(input) {
23622
+ const config = await readK8sSubstrateConfig(input.home);
23623
+ const env = input.env ?? process.env;
23624
+ const stops = [];
23625
+ for (const entry of config.deployments) {
23626
+ if (entry.project !== void 0 && entry.project !== input.project) continue;
23627
+ if (typeof entry.namespace !== "string" || entry.namespace.length === 0) {
23628
+ input.onSkip?.(entry, "missing namespace");
23629
+ continue;
23630
+ }
23631
+ let credentials;
23632
+ try {
23633
+ const resolved = resolveCredential(entry.credential, env);
23634
+ credentials = resolved.kind === "fields" ? { ...resolved.fields } : { token: resolved.value };
23635
+ } catch (err) {
23636
+ input.onSkip?.(entry, err.message);
23637
+ continue;
23638
+ }
23639
+ const cfg = {
23640
+ namespace: entry.namespace,
23641
+ ...entry.apiServerUrl ? { apiServerUrl: entry.apiServerUrl } : {},
23642
+ ...entry.caCert ? { caCert: entry.caCert } : {},
23643
+ ...entry.insecureSkipTlsVerify ? { insecureSkipTlsVerify: true } : {},
23644
+ ...entry.serviceMap ? { serviceMap: entry.serviceMap } : {},
23645
+ ...entry.expectedZero ? { expectedZero: entry.expectedZero } : {}
23646
+ };
23647
+ const { connector, resolveTarget } = createKubernetesConnector(input.graph, cfg, input.fetchImpl);
23648
+ const stop = startConnectorPollLoop(
23649
+ connector,
23650
+ {
23651
+ projectDir: input.projectDir,
23652
+ project: input.project,
23653
+ credentials,
23654
+ ...input.errorsPath ? { errorsPath: input.errorsPath } : {}
23655
+ },
23656
+ input.graph,
23657
+ resolveTarget,
23658
+ { connectorId: `k8s:${entry.id}`, ...entry.intervalMs ? { intervalMs: entry.intervalMs } : {} }
23659
+ );
23660
+ stops.push(stop);
23661
+ }
23662
+ return () => {
23663
+ for (const stop of stops) stop();
23664
+ };
23665
+ }
23666
+
23667
+ // src/connectors/kubernetes/index.ts
23668
+ var KubernetesConnector = class {
23669
+ constructor(config, fetchImpl) {
23670
+ this.config = config;
23671
+ this.fetchImpl = fetchImpl;
23672
+ }
23673
+ config;
23674
+ fetchImpl;
23675
+ provider = "kubernetes";
23676
+ async poll(ctx) {
23677
+ const creds = readK8sCredentials(ctx.credentials);
23678
+ const transport = resolveK8sTransport(creds, this.config);
23679
+ const namespace = this.config.namespace;
23680
+ const opts = {
23681
+ ...this.config.apiUrl ? { apiUrl: this.config.apiUrl } : {},
23682
+ ...this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}
23683
+ };
23684
+ const [deployments, pods] = await Promise.all([
23685
+ fetchDeployments(transport, namespace, opts),
23686
+ fetchPods(transport, namespace, opts)
23687
+ ]);
23688
+ return mapWorkloadsToSignals(deployments, pods, this.config);
23689
+ }
23690
+ };
23691
+ function createKubernetesConnector(graph, config, fetchImpl) {
23692
+ return {
23693
+ connector: new KubernetesConnector(config, fetchImpl),
23694
+ resolveTarget: createK8sResolveTarget(graph)
23695
+ };
23696
+ }
23697
+
23698
+ // src/daemon.ts
23130
23699
  init_auth();
23131
23700
 
23132
23701
  // src/unrouted.ts
23133
23702
  init_cjs_shims();
23134
- var import_node_fs39 = require("fs");
23135
- var import_node_path74 = __toESM(require("path"), 1);
23703
+ var import_node_fs40 = require("fs");
23704
+ var import_node_path76 = __toESM(require("path"), 1);
23136
23705
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
23137
23706
  return {
23138
23707
  timestamp: now.toISOString(),
@@ -23142,34 +23711,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
23142
23711
  };
23143
23712
  }
23144
23713
  async function appendUnroutedSpan(neatHome3, record) {
23145
- const target = import_node_path74.default.join(neatHome3, "errors.ndjson");
23146
- await import_node_fs39.promises.mkdir(neatHome3, { recursive: true });
23147
- await import_node_fs39.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
23714
+ const target = import_node_path76.default.join(neatHome3, "errors.ndjson");
23715
+ await import_node_fs40.promises.mkdir(neatHome3, { recursive: true });
23716
+ await import_node_fs40.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
23148
23717
  }
23149
23718
  function unroutedErrorsPath(neatHome3) {
23150
- return import_node_path74.default.join(neatHome3, "errors.ndjson");
23719
+ return import_node_path76.default.join(neatHome3, "errors.ndjson");
23151
23720
  }
23152
23721
 
23153
23722
  // src/daemon.ts
23154
- var import_types99 = require("@neat.is/types");
23723
+ var import_types105 = require("@neat.is/types");
23155
23724
  function daemonJsonPath(scanPath) {
23156
- return import_node_path75.default.join(scanPath, "neat-out", "daemon.json");
23725
+ return import_node_path77.default.join(scanPath, "neat-out", "daemon.json");
23157
23726
  }
23158
23727
  function daemonsDiscoveryDir(home) {
23159
23728
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
23160
- return import_node_path75.default.join(base, "daemons");
23729
+ return import_node_path77.default.join(base, "daemons");
23161
23730
  }
23162
23731
  function daemonDiscoveryPath(project, home) {
23163
- return import_node_path75.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
23732
+ return import_node_path77.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
23164
23733
  }
23165
23734
  function sanitizeDiscoveryName(project) {
23166
23735
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
23167
23736
  }
23168
23737
  function neatHomeFromEnv() {
23169
23738
  const env = process.env.NEAT_HOME;
23170
- if (env && env.length > 0) return import_node_path75.default.resolve(env);
23739
+ if (env && env.length > 0) return import_node_path77.default.resolve(env);
23171
23740
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
23172
- return import_node_path75.default.join(home, ".neat");
23741
+ return import_node_path77.default.join(home, ".neat");
23173
23742
  }
23174
23743
  function resolveNeatVersion() {
23175
23744
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -23201,7 +23770,7 @@ async function clearDaemonRecord(record, home) {
23201
23770
  } catch {
23202
23771
  }
23203
23772
  try {
23204
- await import_node_fs40.promises.unlink(daemonDiscoveryPath(record.project, home));
23773
+ await import_node_fs41.promises.unlink(daemonDiscoveryPath(record.project, home));
23205
23774
  } catch {
23206
23775
  }
23207
23776
  }
@@ -23218,17 +23787,21 @@ function teardownSlot(slot) {
23218
23787
  slot.stopConnectors();
23219
23788
  } catch {
23220
23789
  }
23790
+ try {
23791
+ slot.stopK8sSubstrate();
23792
+ } catch {
23793
+ }
23221
23794
  try {
23222
23795
  slot.detachEvents();
23223
23796
  } catch {
23224
23797
  }
23225
23798
  }
23226
23799
  function neatHomeFor(opts) {
23227
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path75.default.resolve(opts.neatHome);
23800
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path77.default.resolve(opts.neatHome);
23228
23801
  const env = process.env.NEAT_HOME;
23229
- if (env && env.length > 0) return import_node_path75.default.resolve(env);
23802
+ if (env && env.length > 0) return import_node_path77.default.resolve(env);
23230
23803
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
23231
- return import_node_path75.default.join(home, ".neat");
23804
+ return import_node_path77.default.join(home, ".neat");
23232
23805
  }
23233
23806
  function routeSpanToProject(serviceName, projects) {
23234
23807
  if (!serviceName) return DEFAULT_PROJECT;
@@ -23276,13 +23849,13 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
23276
23849
  if (!serviceName) return true;
23277
23850
  if (serviceNameMatchesProject(serviceName, project)) return true;
23278
23851
  return graph.someNode(
23279
- (_id, attrs) => attrs.type === import_types99.NodeType.ServiceNode && attrs.name === serviceName
23852
+ (_id, attrs) => attrs.type === import_types105.NodeType.ServiceNode && attrs.name === serviceName
23280
23853
  );
23281
23854
  }
23282
23855
  async function bootstrapProject(entry, connectors = [], neatHome3) {
23283
- const paths = pathsForProject(entry.name, import_node_path75.default.join(entry.path, "neat-out"));
23856
+ const paths = pathsForProject(entry.name, import_node_path77.default.join(entry.path, "neat-out"));
23284
23857
  try {
23285
- const stat = await import_node_fs40.promises.stat(entry.path);
23858
+ const stat = await import_node_fs41.promises.stat(entry.path);
23286
23859
  if (!stat.isDirectory()) {
23287
23860
  throw new Error(`registered path ${entry.path} is not a directory`);
23288
23861
  }
@@ -23302,6 +23875,8 @@ async function bootstrapProject(entry, connectors = [], neatHome3) {
23302
23875
  },
23303
23876
  stopConnectors: () => {
23304
23877
  },
23878
+ stopK8sSubstrate: () => {
23879
+ },
23305
23880
  detachEvents: () => {
23306
23881
  },
23307
23882
  status: "broken",
@@ -23334,6 +23909,14 @@ async function bootstrapProject(entry, connectors = [], neatHome3) {
23334
23909
  `neatd: connector "${skipped.id}" (${skipped.provider}) skipped for project "${entry.name}" \u2014 ${reason}`
23335
23910
  )
23336
23911
  });
23912
+ const stopK8sSubstrate = await startK8sSubstratePolling({
23913
+ project: entry.name,
23914
+ graph,
23915
+ projectDir: entry.path,
23916
+ errorsPath: paths.errorsPath,
23917
+ ...neatHome3 ? { home: neatHome3 } : {},
23918
+ onSkip: (skipped, reason) => console.warn(`neatd: k8s substrate "${skipped.id}" skipped for project "${entry.name}" \u2014 ${reason}`)
23919
+ });
23337
23920
  await touchLastSeen(entry.name).catch(() => {
23338
23921
  });
23339
23922
  return {
@@ -23344,6 +23927,7 @@ async function bootstrapProject(entry, connectors = [], neatHome3) {
23344
23927
  stopPersist,
23345
23928
  stopStaleness,
23346
23929
  stopConnectors,
23930
+ stopK8sSubstrate,
23347
23931
  detachEvents,
23348
23932
  status: "active"
23349
23933
  };
@@ -23400,7 +23984,7 @@ async function startDaemon(opts = {}) {
23400
23984
  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;
23401
23985
  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;
23402
23986
  const singleProject = projectArg;
23403
- const singleProjectPath = singleProject && projectPathArg ? import_node_path75.default.resolve(projectPathArg) : null;
23987
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path77.default.resolve(projectPathArg) : null;
23404
23988
  if (singleProject && !singleProjectPath) {
23405
23989
  throw new Error(
23406
23990
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -23408,14 +23992,14 @@ async function startDaemon(opts = {}) {
23408
23992
  }
23409
23993
  if (!singleProject) {
23410
23994
  try {
23411
- await import_node_fs40.promises.access(regPath);
23995
+ await import_node_fs41.promises.access(regPath);
23412
23996
  } catch {
23413
23997
  throw new Error(
23414
23998
  `neatd: registry not found at ${regPath}. Run \`neat init <path>\` to register a project before starting the daemon.`
23415
23999
  );
23416
24000
  }
23417
24001
  }
23418
- const pidPath = import_node_path75.default.join(home, "neatd.pid");
24002
+ const pidPath = import_node_path77.default.join(home, "neatd.pid");
23419
24003
  await writeAtomically(pidPath, `${process.pid}
23420
24004
  `);
23421
24005
  const slots = /* @__PURE__ */ new Map();
@@ -23627,7 +24211,7 @@ async function startDaemon(opts = {}) {
23627
24211
  }
23628
24212
  if (restApp) await restApp.close().catch(() => {
23629
24213
  });
23630
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24214
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23631
24215
  });
23632
24216
  throw new Error(
23633
24217
  `neatd: failed to bind REST on port ${restPort} \u2014 ${err.message}`
@@ -23805,7 +24389,7 @@ async function startDaemon(opts = {}) {
23805
24389
  });
23806
24390
  if (otlpApp) await otlpApp.close().catch(() => {
23807
24391
  });
23808
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24392
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23809
24393
  });
23810
24394
  throw new Error(
23811
24395
  `neatd: failed to bind OTLP on port ${otlpPort} \u2014 ${err.message}`
@@ -23840,7 +24424,7 @@ async function startDaemon(opts = {}) {
23840
24424
  });
23841
24425
  if (otlpApp) await otlpApp.close().catch(() => {
23842
24426
  });
23843
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24427
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23844
24428
  });
23845
24429
  throw new Error(
23846
24430
  `neatd: failed to write daemon.json for "${singleProject}" \u2014 ${err.message}`
@@ -23887,9 +24471,9 @@ async function startDaemon(opts = {}) {
23887
24471
  let registryWatcher = null;
23888
24472
  let reloadTimer = null;
23889
24473
  if (!singleProject) try {
23890
- const regDir = import_node_path75.default.dirname(regPath);
23891
- const regBase = import_node_path75.default.basename(regPath);
23892
- registryWatcher = (0, import_node_fs40.watch)(regDir, (_eventType, filename) => {
24474
+ const regDir = import_node_path77.default.dirname(regPath);
24475
+ const regBase = import_node_path77.default.basename(regPath);
24476
+ registryWatcher = (0, import_node_fs41.watch)(regDir, (_eventType, filename) => {
23893
24477
  if (filename !== null && filename !== regBase) return;
23894
24478
  if (reloadTimer) clearTimeout(reloadTimer);
23895
24479
  reloadTimer = setTimeout(() => {
@@ -23938,7 +24522,7 @@ async function startDaemon(opts = {}) {
23938
24522
  if (daemonRecord) {
23939
24523
  await clearDaemonRecord(daemonRecord, home);
23940
24524
  }
23941
- await import_node_fs40.promises.unlink(pidPath).catch(() => {
24525
+ await import_node_fs41.promises.unlink(pidPath).catch(() => {
23942
24526
  });
23943
24527
  };
23944
24528
  return {