@neat.is/core 0.7.6 → 0.7.7

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/cli.cjs CHANGED
@@ -61,8 +61,8 @@ function mountBearerAuth(app, opts) {
61
61
  ]);
62
62
  const publicRead = opts.publicRead === true;
63
63
  app.addHook("preHandler", (req, reply, done) => {
64
- const path81 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
- if (exactUnauthPaths.has(path81) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path81)) {
64
+ const path82 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
65
+ if (exactUnauthPaths.has(path82) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path82)) {
66
66
  done();
67
67
  return;
68
68
  }
@@ -343,7 +343,7 @@ function pickEnv(spanAttrs, resourceAttrs) {
343
343
  return ENV_FALLBACK;
344
344
  }
345
345
  function normalizeDbSystem(attrs) {
346
- const raw = attrs["db.system"];
346
+ const raw = attrs["db.system"] ?? attrs["db.system.name"];
347
347
  if (typeof raw !== "string") return void 0;
348
348
  return raw === "mongoose" ? "mongodb" : raw;
349
349
  }
@@ -415,8 +415,8 @@ function websocketChannelPathOf(attrs) {
415
415
  const v = attrs[key];
416
416
  if (typeof v === "string" && v.length > 0) {
417
417
  const q = v.indexOf("?");
418
- const path81 = q === -1 ? v : v.slice(0, q);
419
- if (path81.length > 0) return path81;
418
+ const path82 = q === -1 ? v : v.slice(0, q);
419
+ if (path82.length > 0) return path82;
420
420
  }
421
421
  }
422
422
  return void 0;
@@ -435,6 +435,9 @@ function parseOtlpRequest(body) {
435
435
  for (const ss of rs.scopeSpans ?? []) {
436
436
  for (const span of ss.spans ?? []) {
437
437
  const attrs = attrsToRecord(span.attributes);
438
+ const dbSqlText = typeof attrs["db.statement"] === "string" ? attrs["db.statement"] : typeof attrs["db.query.text"] === "string" ? attrs["db.query.text"] : void 0;
439
+ const dbSystemName = normalizeDbSystem(attrs);
440
+ const directDbTable = typeof attrs["db.sql.table"] === "string" ? attrs["db.sql.table"] : dbSystemName !== "mongodb" && typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : void 0;
438
441
  const parsed = {
439
442
  service,
440
443
  resourceServiceNamePresent,
@@ -449,11 +452,11 @@ function parseOtlpRequest(body) {
449
452
  durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
450
453
  env: pickEnv(attrs, resourceAttrs),
451
454
  attributes: attrs,
452
- dbSystem: normalizeDbSystem(attrs),
455
+ dbSystem: dbSystemName,
453
456
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
454
457
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
455
- dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
456
- dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
458
+ dbTable: directDbTable ?? (dbSqlText ? tableFromSqlStatement(dbSqlText) ?? void 0 : void 0),
459
+ dbColumns: dbSqlText ? columnsFromSqlStatement(dbSqlText) : void 0,
457
460
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
458
461
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
459
462
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -752,7 +755,7 @@ __export(cli_exports, {
752
755
  });
753
756
  module.exports = __toCommonJS(cli_exports);
754
757
  init_cjs_shims();
755
- var import_node_path80 = __toESM(require("path"), 1);
758
+ var import_node_path81 = __toESM(require("path"), 1);
756
759
  var import_node_os8 = __toESM(require("os"), 1);
757
760
  var import_node_fs46 = require("fs");
758
761
 
@@ -1322,19 +1325,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1322
1325
  function longestIncomingWalk(graph, start, maxDepth) {
1323
1326
  let best = { path: [start], edges: [] };
1324
1327
  const visited = /* @__PURE__ */ new Set([start]);
1325
- function step(node, path81, edges) {
1326
- if (path81.length > best.path.length) {
1327
- best = { path: [...path81], edges: [...edges] };
1328
+ function step(node, path82, edges) {
1329
+ if (path82.length > best.path.length) {
1330
+ best = { path: [...path82], edges: [...edges] };
1328
1331
  }
1329
- if (path81.length - 1 >= maxDepth) return;
1332
+ if (path82.length - 1 >= maxDepth) return;
1330
1333
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1331
1334
  for (const [srcId, edge] of incoming) {
1332
1335
  if (visited.has(srcId)) continue;
1333
1336
  visited.add(srcId);
1334
- path81.push(srcId);
1337
+ path82.push(srcId);
1335
1338
  edges.push(edge);
1336
- step(srcId, path81, edges);
1337
- path81.pop();
1339
+ step(srcId, path82, edges);
1340
+ path82.pop();
1338
1341
  edges.pop();
1339
1342
  visited.delete(srcId);
1340
1343
  }
@@ -1342,11 +1345,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1342
1345
  step(start, [start], []);
1343
1346
  return best;
1344
1347
  }
1345
- function databaseRootCauseShape(graph, origin, walk8) {
1348
+ function databaseRootCauseShape(graph, origin, walk9) {
1346
1349
  const targetDb = origin;
1347
1350
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1348
1351
  if (candidatePairs.length === 0) return null;
1349
- for (const id of walk8.path) {
1352
+ for (const id of walk9.path) {
1350
1353
  const owner = resolveOwningService(graph, id);
1351
1354
  if (!owner) continue;
1352
1355
  const { id: serviceId9, svc } = owner;
@@ -1373,8 +1376,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
1373
1376
  }
1374
1377
  return null;
1375
1378
  }
1376
- function serviceRootCauseShape(graph, _origin, walk8) {
1377
- for (const id of walk8.path) {
1379
+ function serviceRootCauseShape(graph, _origin, walk9) {
1380
+ for (const id of walk9.path) {
1378
1381
  const owner = resolveOwningService(graph, id);
1379
1382
  if (!owner) continue;
1380
1383
  const { id: serviceId9, svc } = owner;
@@ -1410,15 +1413,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
1410
1413
  }
1411
1414
  return null;
1412
1415
  }
1413
- function fileRootCauseShape(graph, origin, walk8) {
1416
+ function fileRootCauseShape(graph, origin, walk9) {
1414
1417
  const owner = resolveOwningService(graph, origin.id);
1415
1418
  if (!owner) return null;
1416
- return serviceRootCauseShape(graph, owner.svc, walk8);
1419
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1417
1420
  }
1418
- function symbolRootCauseShape(graph, origin, walk8) {
1421
+ function symbolRootCauseShape(graph, origin, walk9) {
1419
1422
  const owner = resolveOwningService(graph, origin.id);
1420
1423
  if (!owner) return null;
1421
- return serviceRootCauseShape(graph, owner.svc, walk8);
1424
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1422
1425
  }
1423
1426
  var rootCauseShapes = {
1424
1427
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1431,16 +1434,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1431
1434
  const origin = graph.getNodeAttributes(errorNodeId);
1432
1435
  const shape = rootCauseShapes[origin.type];
1433
1436
  if (shape) {
1434
- const walk8 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1435
- const match = shape(graph, origin, walk8);
1437
+ const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1438
+ const match = shape(graph, origin, walk9);
1436
1439
  if (match) {
1437
1440
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1438
1441
  return import_types.RootCauseResultSchema.parse({
1439
1442
  rootCauseNode: match.rootCauseNode,
1440
1443
  rootCauseReason: reason,
1441
- traversalPath: walk8.path,
1442
- edgeProvenances: walk8.edges.map((e) => e.provenance),
1443
- confidence: confidenceFromMix(walk8.edges),
1444
+ traversalPath: walk9.path,
1445
+ edgeProvenances: walk9.edges.map((e) => e.provenance),
1446
+ confidence: confidenceFromMix(walk9.edges),
1444
1447
  fixRecommendation: match.fixRecommendation
1445
1448
  });
1446
1449
  }
@@ -1541,26 +1544,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
1541
1544
  return best;
1542
1545
  }
1543
1546
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1544
- const path81 = [originServiceId];
1547
+ const path82 = [originServiceId];
1545
1548
  const edges = [];
1546
1549
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1547
1550
  let current = originServiceId;
1548
1551
  for (let depth = 0; depth < maxDepth; depth++) {
1549
1552
  const hop = dominantFailingCall(graph, current, visited);
1550
1553
  if (!hop) break;
1551
- path81.push(hop.nextService);
1554
+ path82.push(hop.nextService);
1552
1555
  edges.push(hop.edge);
1553
1556
  visited.add(hop.nextService);
1554
1557
  current = hop.nextService;
1555
1558
  }
1556
1559
  if (edges.length === 0) return null;
1557
- return { path: path81, edges, culprit: current };
1560
+ return { path: path82, edges, culprit: current };
1558
1561
  }
1559
1562
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1560
1563
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1561
1564
  if (!chain) return null;
1562
1565
  const culprit = chain.culprit;
1563
- const path81 = [...chain.path];
1566
+ const path82 = [...chain.path];
1564
1567
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1565
1568
  const baseConfidence = confidenceFromMix(chain.edges);
1566
1569
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1568,14 +1571,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1568
1571
  if (loc) {
1569
1572
  let rootCauseNode = culprit;
1570
1573
  if (loc.fileNode) {
1571
- path81.push(loc.fileNode);
1574
+ path82.push(loc.fileNode);
1572
1575
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1573
1576
  rootCauseNode = loc.fileNode;
1574
1577
  }
1575
1578
  return import_types.RootCauseResultSchema.parse({
1576
1579
  rootCauseNode,
1577
1580
  rootCauseReason: loc.rootCauseReason,
1578
- traversalPath: path81,
1581
+ traversalPath: path82,
1579
1582
  edgeProvenances,
1580
1583
  confidence,
1581
1584
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1587,7 +1590,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1587
1590
  return import_types.RootCauseResultSchema.parse({
1588
1591
  rootCauseNode: culprit,
1589
1592
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1590
- traversalPath: path81,
1593
+ traversalPath: path82,
1591
1594
  edgeProvenances,
1592
1595
  confidence,
1593
1596
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2534,14 +2537,14 @@ function buildServiceHostIndex(services) {
2534
2537
  }
2535
2538
  async function walkSourceFiles(dir) {
2536
2539
  const out = [];
2537
- async function walk8(current) {
2540
+ async function walk9(current) {
2538
2541
  const entries = await import_node_fs6.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2539
2542
  for (const entry2 of entries) {
2540
2543
  const full = import_node_path6.default.join(current, entry2.name);
2541
2544
  if (entry2.isDirectory()) {
2542
2545
  if (IGNORED_DIRS.has(entry2.name)) continue;
2543
2546
  if (await isPythonVenvDir(full)) continue;
2544
- await walk8(full);
2547
+ await walk9(full);
2545
2548
  } else if (entry2.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path6.default.extname(entry2.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2546
2549
  // would attribute our instrumentation imports to the user's service.
2547
2550
  !isNeatAuthoredSourceFile(entry2.name)) {
@@ -2549,7 +2552,7 @@ async function walkSourceFiles(dir) {
2549
2552
  }
2550
2553
  }
2551
2554
  }
2552
- await walk8(dir);
2555
+ await walk9(dir);
2553
2556
  return out;
2554
2557
  }
2555
2558
  async function loadSourceFiles(dir) {
@@ -3063,7 +3066,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
3063
3066
  ]);
3064
3067
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
3065
3068
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
3066
- function ginRoutesFromSource(source, parser) {
3069
+ function goRouterRoutesFromSource(source, parser, framework) {
3067
3070
  const tree = parseSource2(parser, source);
3068
3071
  const prefixes = /* @__PURE__ */ new Map();
3069
3072
  const out = [];
@@ -3073,10 +3076,12 @@ function ginRoutesFromSource(source, parser) {
3073
3076
  const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
3074
3077
  if (name && value?.type === "call_expression") {
3075
3078
  const fn2 = value.childForFieldName("function");
3076
- const field = fn2?.childForFieldName("field")?.text;
3077
- const first2 = value.childForFieldName("arguments")?.namedChild(0);
3078
- if (field === "Group" && first2?.type === "interpreted_string_literal") {
3079
- prefixes.set(name, first2.text.slice(1, -1));
3079
+ if (fn2?.childForFieldName("field")?.text === "Group") {
3080
+ const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
3081
+ if (leaf2 !== null) {
3082
+ const parent = fn2.childForFieldName("operand")?.text ?? "";
3083
+ prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
3084
+ }
3080
3085
  }
3081
3086
  }
3082
3087
  return;
@@ -3087,18 +3092,32 @@ function ginRoutesFromSource(source, parser) {
3087
3092
  const method = fn.childForFieldName("field")?.text?.toUpperCase();
3088
3093
  if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
3089
3094
  const receiver = fn.childForFieldName("operand")?.text ?? "";
3090
- const first = node.childForFieldName("arguments")?.namedChild(0);
3091
- if (first?.type !== "interpreted_string_literal") return;
3092
- const leaf = first.text.slice(1, -1);
3095
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
3096
+ if (leaf === null) return;
3093
3097
  out.push({
3094
- method: method === "ALL" ? "ALL" : method,
3098
+ method,
3095
3099
  pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
3096
3100
  line: node.startPosition.row + 1,
3097
- framework: "gin"
3101
+ framework
3098
3102
  });
3099
3103
  });
3100
3104
  return out;
3101
3105
  }
3106
+ function goStringLiteral(node) {
3107
+ if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
3108
+ return node.text.slice(1, -1);
3109
+ }
3110
+ return null;
3111
+ }
3112
+ function ginRoutesFromSource(source, parser) {
3113
+ return goRouterRoutesFromSource(source, parser, "gin");
3114
+ }
3115
+ function echoRoutesFromSource(source, parser) {
3116
+ return goRouterRoutesFromSource(source, parser, "echo");
3117
+ }
3118
+ function fiberRoutesFromSource(source, parser) {
3119
+ return goRouterRoutesFromSource(source, parser, "fiber");
3120
+ }
3102
3121
  var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
3103
3122
  var NESTJS_METHODS = /* @__PURE__ */ new Map([
3104
3123
  ["Get", "GET"],
@@ -3683,9 +3702,9 @@ function rubyRocketRoute(args) {
3683
3702
  if (!pair || pair.type !== "pair") continue;
3684
3703
  const k = pair.childForFieldName("key");
3685
3704
  if (k?.type !== "string") continue;
3686
- const path81 = rubyLiteral(k);
3687
- if (path81 === null) continue;
3688
- return { path: path81, target: rubyLiteral(pair.childForFieldName("value")) };
3705
+ const path82 = rubyLiteral(k);
3706
+ if (path82 === null) continue;
3707
+ return { path: path82, target: rubyLiteral(pair.childForFieldName("value")) };
3689
3708
  }
3690
3709
  return null;
3691
3710
  }
@@ -4362,9 +4381,11 @@ async function addRoutes(graph, services) {
4362
4381
  const hasFlask = deps["flask"] !== void 0;
4363
4382
  const hasDjango = deps["django"] !== void 0;
4364
4383
  const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
4384
+ const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
4385
+ const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
4365
4386
  const hasRails = deps["rails"] !== void 0;
4366
4387
  const hasLaravel = deps["laravel/framework"] !== void 0;
4367
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
4388
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
4368
4389
  continue;
4369
4390
  const files = await loadSourceFiles(service.dir);
4370
4391
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4388,7 +4409,10 @@ async function addRoutes(graph, services) {
4388
4409
  } else if (isRb) {
4389
4410
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4390
4411
  } else if (isGo) {
4391
- routes = hasGin ? ginRoutesFromSource(file.content, goParser) : [];
4412
+ if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4413
+ else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
4414
+ else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
4415
+ else routes = [];
4392
4416
  } else if (isPy) {
4393
4417
  routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
4394
4418
  if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
@@ -5998,6 +6022,12 @@ function parseGoMod(source) {
5998
6022
  }
5999
6023
  return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
6000
6024
  }
6025
+ function goFramework(deps) {
6026
+ if (deps["github.com/gin-gonic/gin"]) return "gin";
6027
+ if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
6028
+ if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
6029
+ return void 0;
6030
+ }
6001
6031
  async function discoverGoService(scanPath, dir) {
6002
6032
  let raw;
6003
6033
  try {
@@ -6009,6 +6039,7 @@ async function discoverGoService(scanPath, dir) {
6009
6039
  if (!mod) return null;
6010
6040
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
6011
6041
  const pkg = { name, dependencies: mod.dependencies };
6042
+ const framework = goFramework(mod.dependencies);
6012
6043
  const node = {
6013
6044
  id: (0, import_types9.serviceId)(name),
6014
6045
  type: import_types9.NodeType.ServiceNode,
@@ -6016,7 +6047,7 @@ async function discoverGoService(scanPath, dir) {
6016
6047
  language: "go",
6017
6048
  dependencies: mod.dependencies,
6018
6049
  repoPath: import_node_path11.default.relative(scanPath, dir),
6019
- ...mod.dependencies["github.com/gin-gonic/gin"] ? { framework: "gin" } : {}
6050
+ ...framework ? { framework } : {}
6020
6051
  };
6021
6052
  return { pkg, dir, node };
6022
6053
  }
@@ -6916,7 +6947,7 @@ async function addSymbolEdges(graph, services) {
6916
6947
  return best;
6917
6948
  };
6918
6949
  const requests = [];
6919
- const walk8 = (node) => {
6950
+ const walk9 = (node) => {
6920
6951
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
6921
6952
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
6922
6953
  if (self && self.kind === "class") {
@@ -6962,10 +6993,10 @@ async function addSymbolEdges(graph, services) {
6962
6993
  }
6963
6994
  for (let i = 0; i < node.namedChildCount; i++) {
6964
6995
  const child = node.namedChild(i);
6965
- if (child) walk8(child);
6996
+ if (child) walk9(child);
6966
6997
  }
6967
6998
  };
6968
- walk8(root);
6999
+ walk9(root);
6969
7000
  for (const req of requests) {
6970
7001
  const targetSid = resolveTarget(req.targetName, req.wantKind);
6971
7002
  if (!targetSid) continue;
@@ -7978,20 +8009,20 @@ var import_node_path29 = __toESM(require("path"), 1);
7978
8009
  var import_types18 = require("@neat.is/types");
7979
8010
  async function walkConfigFiles(dir) {
7980
8011
  const out = [];
7981
- async function walk8(current) {
8012
+ async function walk9(current) {
7982
8013
  const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true });
7983
8014
  for (const entry2 of entries) {
7984
8015
  const full = import_node_path29.default.join(current, entry2.name);
7985
8016
  if (entry2.isDirectory()) {
7986
8017
  if (IGNORED_DIRS.has(entry2.name)) continue;
7987
8018
  if (await isPythonVenvDir(full)) continue;
7988
- await walk8(full);
8019
+ await walk9(full);
7989
8020
  } else if (entry2.isFile() && isConfigFile(entry2.name).match) {
7990
8021
  out.push(full);
7991
8022
  }
7992
8023
  }
7993
8024
  }
7994
- await walk8(dir);
8025
+ await walk9(dir);
7995
8026
  return out;
7996
8027
  }
7997
8028
  async function addConfigNodes(graph, services, scanPath) {
@@ -8081,20 +8112,20 @@ function grpcMethodsFromProto(content, fqPackage) {
8081
8112
  }
8082
8113
  async function walkProtoFiles(dir) {
8083
8114
  const out = [];
8084
- async function walk8(current) {
8115
+ async function walk9(current) {
8085
8116
  const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8086
8117
  for (const entry2 of entries) {
8087
8118
  const full = import_node_path30.default.join(current, entry2.name);
8088
8119
  if (entry2.isDirectory()) {
8089
8120
  if (IGNORED_DIRS.has(entry2.name)) continue;
8090
8121
  if (await isPythonVenvDir(full)) continue;
8091
- await walk8(full);
8122
+ await walk9(full);
8092
8123
  } else if (entry2.isFile() && import_node_path30.default.extname(entry2.name) === PROTO_EXTENSION) {
8093
8124
  out.push(full);
8094
8125
  }
8095
8126
  }
8096
8127
  }
8097
- await walk8(dir);
8128
+ await walk9(dir);
8098
8129
  return out;
8099
8130
  }
8100
8131
  async function addGrpcMethods(graph, services) {
@@ -8162,7 +8193,7 @@ async function addGrpcMethods(graph, services) {
8162
8193
 
8163
8194
  // src/extract/calls/index.ts
8164
8195
  init_cjs_shims();
8165
- var import_types36 = require("@neat.is/types");
8196
+ var import_types37 = require("@neat.is/types");
8166
8197
 
8167
8198
  // src/extract/calls/http.ts
8168
8199
  init_cjs_shims();
@@ -8916,7 +8947,7 @@ function isFirestoreClientFactory(node) {
8916
8947
  }
8917
8948
  function firestoreClientVars(root) {
8918
8949
  const vars = /* @__PURE__ */ new Set();
8919
- const walk8 = (node) => {
8950
+ const walk9 = (node) => {
8920
8951
  if (node.type === "variable_declarator") {
8921
8952
  const name = node.childForFieldName("name");
8922
8953
  let value = node.childForFieldName("value");
@@ -8925,9 +8956,9 @@ function firestoreClientVars(root) {
8925
8956
  vars.add(name.text);
8926
8957
  }
8927
8958
  }
8928
- for (const c of namedChildren(node)) walk8(c);
8959
+ for (const c of namedChildren(node)) walk9(c);
8929
8960
  };
8930
- walk8(root);
8961
+ walk9(root);
8931
8962
  return vars;
8932
8963
  }
8933
8964
  function isClientExpr(node, clientVars) {
@@ -9082,7 +9113,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9082
9113
  }
9083
9114
  s.add(field);
9084
9115
  };
9085
- const walk8 = (node) => {
9116
+ const walk9 = (node) => {
9086
9117
  if (node.type === "call_expression") {
9087
9118
  const fn = node.childForFieldName("function");
9088
9119
  const line = node.startPosition.row + 1;
@@ -9122,9 +9153,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9122
9153
  }
9123
9154
  }
9124
9155
  }
9125
- for (const c of namedChildren(node)) walk8(c);
9156
+ for (const c of namedChildren(node)) walk9(c);
9126
9157
  };
9127
- walk8(tree.rootNode);
9158
+ walk9(tree.rootNode);
9128
9159
  const out = [];
9129
9160
  for (const [collPath, line] of collLine) {
9130
9161
  const byField = writes.get(collPath);
@@ -9919,7 +9950,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9919
9950
  const tree = parseSource3(parserForExt2(import_node_path42.default.extname(file.path)), file.content);
9920
9951
  const out = [];
9921
9952
  const seen = /* @__PURE__ */ new Set();
9922
- const walk8 = (node) => {
9953
+ const walk9 = (node) => {
9923
9954
  if (node.type === "call_expression") {
9924
9955
  const fn = node.childForFieldName("function");
9925
9956
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9947,9 +9978,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9947
9978
  }
9948
9979
  }
9949
9980
  }
9950
- for (const c of namedChildren4(node)) walk8(c);
9981
+ for (const c of namedChildren4(node)) walk9(c);
9951
9982
  };
9952
- walk8(tree.rootNode);
9983
+ walk9(tree.rootNode);
9953
9984
  return out;
9954
9985
  }
9955
9986
  function enclosingVarName(call) {
@@ -9971,7 +10002,7 @@ function enclosingVarName(call) {
9971
10002
  function collectDrizzleTables(root) {
9972
10003
  const tables = [];
9973
10004
  const varToTable = /* @__PURE__ */ new Map();
9974
- const walk8 = (node) => {
10005
+ const walk9 = (node) => {
9975
10006
  if (node.type === "call_expression") {
9976
10007
  const fn = node.childForFieldName("function");
9977
10008
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9986,9 +10017,9 @@ function collectDrizzleTables(root) {
9986
10017
  }
9987
10018
  }
9988
10019
  }
9989
- for (const c of namedChildren4(node)) walk8(c);
10020
+ for (const c of namedChildren4(node)) walk9(c);
9990
10021
  };
9991
- walk8(root);
10022
+ walk9(root);
9992
10023
  return { tables, varToTable };
9993
10024
  }
9994
10025
  function referencesTargetVar(call) {
@@ -10011,7 +10042,7 @@ function drizzleForeignKeys(file, serviceDir) {
10011
10042
  const seen = /* @__PURE__ */ new Set();
10012
10043
  for (const table of tables) {
10013
10044
  if (!table.object) continue;
10014
- const walk8 = (node) => {
10045
+ const walk9 = (node) => {
10015
10046
  if (node.type === "call_expression") {
10016
10047
  const targetVar = referencesTargetVar(node);
10017
10048
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -10032,9 +10063,9 @@ function drizzleForeignKeys(file, serviceDir) {
10032
10063
  }
10033
10064
  }
10034
10065
  }
10035
- for (const c of namedChildren4(node)) walk8(c);
10066
+ for (const c of namedChildren4(node)) walk9(c);
10036
10067
  };
10037
- walk8(table.object);
10068
+ walk9(table.object);
10038
10069
  }
10039
10070
  return out;
10040
10071
  }
@@ -11114,15 +11145,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
11114
11145
  return out;
11115
11146
  }
11116
11147
 
11148
+ // src/extract/calls/gorm.ts
11149
+ init_cjs_shims();
11150
+ var import_node_path49 = __toESM(require("path"), 1);
11151
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
11152
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
11153
+ var import_types36 = require("@neat.is/types");
11154
+ var GORM_IMPORT_RE = /gorm\.io\/gorm/;
11155
+ var PARSE_CHUNK11 = 16384;
11156
+ function makeGoParser3() {
11157
+ const p = new import_tree_sitter15.default();
11158
+ p.setLanguage(import_tree_sitter_go4.default);
11159
+ return p;
11160
+ }
11161
+ function parseSource10(parser, source) {
11162
+ return parser.parse(
11163
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
11164
+ );
11165
+ }
11166
+ function walk8(node, visit) {
11167
+ visit(node);
11168
+ for (let i = 0; i < node.namedChildCount; i++) {
11169
+ const c = node.namedChild(i);
11170
+ if (c) walk8(c, visit);
11171
+ }
11172
+ }
11173
+ var COMMON_INITIALISMS = [
11174
+ "ASCII",
11175
+ "HTTPS",
11176
+ "UTF8",
11177
+ "XSRF",
11178
+ "HTML",
11179
+ "HTTP",
11180
+ "JSON",
11181
+ "UUID",
11182
+ "XMPP",
11183
+ "ACL",
11184
+ "API",
11185
+ "CPU",
11186
+ "CSS",
11187
+ "DNS",
11188
+ "EOF",
11189
+ "GUID",
11190
+ "LHS",
11191
+ "QPS",
11192
+ "RAM",
11193
+ "RHS",
11194
+ "RPC",
11195
+ "SLA",
11196
+ "SQL",
11197
+ "SSH",
11198
+ "TCP",
11199
+ "TLS",
11200
+ "TTL",
11201
+ "UDP",
11202
+ "UID",
11203
+ "URI",
11204
+ "URL",
11205
+ "UID",
11206
+ "XSS",
11207
+ "ID",
11208
+ "IP",
11209
+ "UI",
11210
+ "VM",
11211
+ "XML"
11212
+ ].sort((a, b) => b.length - a.length);
11213
+ function titleCase(word) {
11214
+ return word.charAt(0) + word.slice(1).toLowerCase();
11215
+ }
11216
+ function replaceInitialisms(name) {
11217
+ let out = "";
11218
+ let i = 0;
11219
+ while (i < name.length) {
11220
+ let matched = false;
11221
+ for (const init of COMMON_INITIALISMS) {
11222
+ if (name.startsWith(init, i)) {
11223
+ out += titleCase(init);
11224
+ i += init.length;
11225
+ matched = true;
11226
+ break;
11227
+ }
11228
+ }
11229
+ if (!matched) {
11230
+ out += name[i];
11231
+ i++;
11232
+ }
11233
+ }
11234
+ return out;
11235
+ }
11236
+ var isUpper = (c) => c >= "A" && c <= "Z";
11237
+ var isDigit = (c) => c >= "0" && c <= "9";
11238
+ function toDBName(name) {
11239
+ if (name === "") return "";
11240
+ const value = replaceInitialisms(name);
11241
+ if (value.length === 1) return value.toLowerCase();
11242
+ let buf = "";
11243
+ let lastCase = false;
11244
+ let curCase = isUpper(value[0]);
11245
+ for (let i = 0; i < value.length - 1; i++) {
11246
+ const v = value[i];
11247
+ const nextCase = isUpper(value[i + 1]);
11248
+ const nextNumber = isDigit(value[i + 1]);
11249
+ if (curCase) {
11250
+ if (lastCase && (nextCase || nextNumber)) {
11251
+ buf += v.toLowerCase();
11252
+ } else {
11253
+ if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
11254
+ buf += v.toLowerCase();
11255
+ }
11256
+ } else {
11257
+ buf += v;
11258
+ }
11259
+ lastCase = curCase;
11260
+ curCase = nextCase;
11261
+ }
11262
+ const last = value[value.length - 1];
11263
+ if (curCase) {
11264
+ if (!lastCase && value.length > 1) buf += "_";
11265
+ buf += last.toLowerCase();
11266
+ } else {
11267
+ buf += last;
11268
+ }
11269
+ return buf;
11270
+ }
11271
+ var UNCOUNTABLE = /* @__PURE__ */ new Set([
11272
+ "equipment",
11273
+ "information",
11274
+ "rice",
11275
+ "money",
11276
+ "species",
11277
+ "series",
11278
+ "fish",
11279
+ "sheep",
11280
+ "jeans",
11281
+ "police"
11282
+ ]);
11283
+ var IRREGULAR = [
11284
+ ["person", "people"],
11285
+ ["man", "men"],
11286
+ ["child", "children"],
11287
+ ["sex", "sexes"],
11288
+ ["move", "moves"]
11289
+ ];
11290
+ var PLURAL_RULES = [
11291
+ [/(quiz)$/i, "$1zes"],
11292
+ [/^(ox)$/i, "$1en"],
11293
+ [/([ml])ouse$/i, "$1ice"],
11294
+ [/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
11295
+ [/(x|ch|ss|sh)$/i, "$1es"],
11296
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
11297
+ [/(hive)$/i, "$1s"],
11298
+ [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
11299
+ [/sis$/i, "ses"],
11300
+ [/([ti])um$/i, "$1a"],
11301
+ [/([ti])a$/i, "$1a"],
11302
+ [/(buffal|tomat)o$/i, "$1oes"],
11303
+ [/(bu)s$/i, "$1ses"],
11304
+ [/(alias|status)$/i, "$1es"],
11305
+ [/(octop|vir)i$/i, "$1i"],
11306
+ [/(octop|vir)us$/i, "$1i"],
11307
+ [/(ax|test)is$/i, "$1es"],
11308
+ [/s$/i, "s"]
11309
+ ];
11310
+ function pluralize3(word) {
11311
+ if (word === "") return word;
11312
+ const lower = word.toLowerCase();
11313
+ for (const u of UNCOUNTABLE) {
11314
+ if (lower === u || lower.endsWith("_" + u)) return word;
11315
+ }
11316
+ for (const [sing, plur] of IRREGULAR) {
11317
+ const re = new RegExp(sing + "$", "i");
11318
+ if (re.test(word)) return word.replace(re, plur);
11319
+ }
11320
+ for (const [re, rep] of PLURAL_RULES) {
11321
+ if (re.test(word)) return word.replace(re, rep);
11322
+ }
11323
+ return word + "s";
11324
+ }
11325
+ function deriveTableName(structName) {
11326
+ return pluralize3(toDBName(structName));
11327
+ }
11328
+ function stringLiteralValue(node) {
11329
+ if (!node) return null;
11330
+ if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
11331
+ const t = node.text;
11332
+ return t.length >= 2 ? t.slice(1, -1) : "";
11333
+ }
11334
+ return null;
11335
+ }
11336
+ function parseGormTag(tagNode) {
11337
+ const tag = {};
11338
+ if (!tagNode) return tag;
11339
+ let inner = tagNode.text;
11340
+ if (inner.length >= 2) inner = inner.slice(1, -1);
11341
+ if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
11342
+ const m = inner.match(/gorm:"([^"]*)"/);
11343
+ if (!m) return tag;
11344
+ for (const part of m[1].split(";")) {
11345
+ if (part === "") continue;
11346
+ const idx = part.indexOf(":");
11347
+ const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
11348
+ const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
11349
+ if (key === "-") tag.skip = true;
11350
+ else if (key === "column") tag.column = value;
11351
+ else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
11352
+ else if (key === "foreignkey") tag.foreignKey = value;
11353
+ else if (key === "many2many") tag.many2many = value;
11354
+ else if (key === "embedded") tag.embedded = true;
11355
+ else if (key === "embeddedprefix") tag.embeddedPrefix = value;
11356
+ }
11357
+ return tag;
11358
+ }
11359
+ function unwrapType(typeNode) {
11360
+ let isSlice = false;
11361
+ let isPointer = false;
11362
+ let n = typeNode;
11363
+ while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
11364
+ if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
11365
+ if (n.type === "pointer_type") isPointer = true;
11366
+ n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
11367
+ }
11368
+ if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11369
+ if (n.type === "type_identifier") {
11370
+ return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
11371
+ }
11372
+ if (n.type === "qualified_type") {
11373
+ const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
11374
+ const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
11375
+ return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
11376
+ }
11377
+ return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11378
+ }
11379
+ function readField(fieldDecl) {
11380
+ const names = [];
11381
+ let tagNode = null;
11382
+ for (let i = 0; i < fieldDecl.namedChildCount; i++) {
11383
+ const c = fieldDecl.namedChild(i);
11384
+ if (!c) continue;
11385
+ if (c.type === "field_identifier") names.push(c.text);
11386
+ else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
11387
+ }
11388
+ const typeNode = fieldDecl.childForFieldName("type");
11389
+ const t = unwrapType(typeNode);
11390
+ return {
11391
+ names,
11392
+ typeName: t.name,
11393
+ qualifier: t.qualifier,
11394
+ isSlice: t.isSlice,
11395
+ isPointer: t.isPointer,
11396
+ isQualified: t.isQualified,
11397
+ tag: parseGormTag(tagNode),
11398
+ line: fieldDecl.startPosition.row + 1
11399
+ };
11400
+ }
11401
+ function collectStructs(tree) {
11402
+ const structs = /* @__PURE__ */ new Map();
11403
+ walk8(tree.rootNode, (node) => {
11404
+ if (node.type !== "type_spec") return;
11405
+ const nameNode = node.childForFieldName("name");
11406
+ const typeNode = node.childForFieldName("type");
11407
+ if (!nameNode || typeNode?.type !== "struct_type") return;
11408
+ const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
11409
+ const fields = [];
11410
+ if (list && list.type === "field_declaration_list") {
11411
+ for (let i = 0; i < list.namedChildCount; i++) {
11412
+ const fd = list.namedChild(i);
11413
+ if (fd?.type === "field_declaration") fields.push(readField(fd));
11414
+ }
11415
+ }
11416
+ structs.set(nameNode.text, {
11417
+ name: nameNode.text,
11418
+ fields,
11419
+ line: node.startPosition.row + 1
11420
+ });
11421
+ });
11422
+ return structs;
11423
+ }
11424
+ var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
11425
+ "AutoMigrate",
11426
+ "Model",
11427
+ "Create",
11428
+ "Find",
11429
+ "First",
11430
+ "Take",
11431
+ "Last",
11432
+ "Save",
11433
+ "Delete",
11434
+ "Where",
11435
+ "FirstOrCreate",
11436
+ "FirstOrInit"
11437
+ ]);
11438
+ function compositeStructName(arg) {
11439
+ let n = arg;
11440
+ if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
11441
+ if (!n || n.type !== "composite_literal") return null;
11442
+ const typeNode = n.childForFieldName("type");
11443
+ if (!typeNode) return null;
11444
+ if (typeNode.type === "type_identifier") return typeNode.text;
11445
+ if (typeNode.type === "qualified_type") {
11446
+ return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
11447
+ }
11448
+ return null;
11449
+ }
11450
+ function collectCallModels(tree) {
11451
+ const models = /* @__PURE__ */ new Set();
11452
+ walk8(tree.rootNode, (node) => {
11453
+ if (node.type !== "call_expression") return;
11454
+ const fn = node.childForFieldName("function");
11455
+ if (fn?.type !== "selector_expression") return;
11456
+ const method = fn.childForFieldName("field")?.text;
11457
+ if (!method || !GORM_MODEL_METHODS.has(method)) return;
11458
+ const args = node.childForFieldName("arguments");
11459
+ if (!args) return;
11460
+ for (let i = 0; i < args.namedChildCount; i++) {
11461
+ const arg = args.namedChild(i);
11462
+ if (!arg) continue;
11463
+ const name = compositeStructName(arg);
11464
+ if (name) models.add(name);
11465
+ }
11466
+ });
11467
+ return models;
11468
+ }
11469
+ function collectTableNameOverrides(tree) {
11470
+ const overrides = /* @__PURE__ */ new Map();
11471
+ const declarers = /* @__PURE__ */ new Set();
11472
+ walk8(tree.rootNode, (node) => {
11473
+ if (node.type !== "method_declaration") return;
11474
+ if (node.childForFieldName("name")?.text !== "TableName") return;
11475
+ const receiver = node.childForFieldName("receiver");
11476
+ if (!receiver) return;
11477
+ let recvType = null;
11478
+ for (let i = 0; i < receiver.namedChildCount; i++) {
11479
+ const pd = receiver.namedChild(i);
11480
+ if (pd?.type !== "parameter_declaration") continue;
11481
+ const t = unwrapType(pd.childForFieldName("type"));
11482
+ recvType = t.name;
11483
+ }
11484
+ if (!recvType) return;
11485
+ declarers.add(recvType);
11486
+ const body = node.childForFieldName("body");
11487
+ if (!body) return;
11488
+ let literal = null;
11489
+ walk8(body, (n) => {
11490
+ if (literal !== null) return;
11491
+ if (n.type !== "return_statement") return;
11492
+ const exprList = n.namedChild(0);
11493
+ const first = exprList?.namedChild(0) ?? exprList;
11494
+ const v = stringLiteralValue(first);
11495
+ if (v) literal = v;
11496
+ });
11497
+ if (literal !== null) overrides.set(recvType, literal);
11498
+ });
11499
+ return { overrides, declarers };
11500
+ }
11501
+ function isRelationField(field, structs) {
11502
+ if (field.names.length === 0) return false;
11503
+ if (field.isQualified) return false;
11504
+ if (!field.typeName) return false;
11505
+ return structs.has(field.typeName);
11506
+ }
11507
+ function isGormModelEmbed(field) {
11508
+ return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
11509
+ }
11510
+ function analyze(tree) {
11511
+ const structs = collectStructs(tree);
11512
+ const { overrides, declarers } = collectTableNameOverrides(tree);
11513
+ const callModels = collectCallModels(tree);
11514
+ const models = /* @__PURE__ */ new Set();
11515
+ for (const [name, info] of structs) {
11516
+ if (info.fields.some(isGormModelEmbed)) models.add(name);
11517
+ }
11518
+ for (const name of callModels) if (structs.has(name)) models.add(name);
11519
+ for (const name of declarers) if (structs.has(name)) models.add(name);
11520
+ let grew = true;
11521
+ while (grew) {
11522
+ grew = false;
11523
+ for (const name of Array.from(models)) {
11524
+ const info = structs.get(name);
11525
+ if (!info) continue;
11526
+ for (const field of info.fields) {
11527
+ if (!isRelationField(field, structs)) continue;
11528
+ const target = field.typeName;
11529
+ if (!models.has(target) && structs.has(target)) {
11530
+ models.add(target);
11531
+ grew = true;
11532
+ }
11533
+ }
11534
+ }
11535
+ }
11536
+ const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
11537
+ return { structs, models, tableFor };
11538
+ }
11539
+ function collectColumns(struct, structs, seen, prefix, out, emitted) {
11540
+ if (seen.has(struct.name)) return;
11541
+ seen.add(struct.name);
11542
+ const add = (col) => {
11543
+ const full = prefix + col;
11544
+ if (!emitted.has(full)) {
11545
+ emitted.add(full);
11546
+ out.push(full);
11547
+ }
11548
+ };
11549
+ for (const field of struct.fields) {
11550
+ if (field.tag.skip) continue;
11551
+ if (field.names.length === 0) {
11552
+ if (isGormModelEmbed(field)) {
11553
+ add("id");
11554
+ add("created_at");
11555
+ add("updated_at");
11556
+ add("deleted_at");
11557
+ } else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
11558
+ collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
11559
+ }
11560
+ continue;
11561
+ }
11562
+ if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
11563
+ collectColumns(
11564
+ structs.get(field.typeName),
11565
+ structs,
11566
+ seen,
11567
+ prefix + (field.tag.embeddedPrefix ?? ""),
11568
+ out,
11569
+ emitted
11570
+ );
11571
+ continue;
11572
+ }
11573
+ if (isRelationField(field, structs)) continue;
11574
+ if (field.names.length === 1 && field.tag.column) {
11575
+ add(field.tag.column);
11576
+ } else {
11577
+ for (const n of field.names) add(toDBName(n));
11578
+ }
11579
+ }
11580
+ seen.delete(struct.name);
11581
+ }
11582
+ function gormEndpointsFromFile(file, serviceDir) {
11583
+ if (import_node_path49.default.extname(file.path) !== ".go") return [];
11584
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11585
+ const tree = parseSource10(makeGoParser3(), file.content);
11586
+ const { structs, models, tableFor } = analyze(tree);
11587
+ const out = [];
11588
+ const seenTables = /* @__PURE__ */ new Set();
11589
+ for (const name of models) {
11590
+ const struct = structs.get(name);
11591
+ if (!struct) continue;
11592
+ const table = tableFor(name);
11593
+ if (seenTables.has(table)) continue;
11594
+ seenTables.add(table);
11595
+ const columns = [];
11596
+ collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
11597
+ out.push({
11598
+ infraId: (0, import_types36.infraId)("sql-table", table),
11599
+ name: table,
11600
+ kind: "sql-table",
11601
+ edgeType: "CALLS",
11602
+ confidenceKind: "structural",
11603
+ ...columns.length > 0 ? { columns } : {},
11604
+ evidence: {
11605
+ file: toPosix(import_node_path49.default.relative(serviceDir, file.path)),
11606
+ line: struct.line,
11607
+ snippet: snippet(file.content, struct.line)
11608
+ }
11609
+ });
11610
+ }
11611
+ return out;
11612
+ }
11613
+ function gormForeignKeys(file, serviceDir) {
11614
+ if (import_node_path49.default.extname(file.path) !== ".go") return [];
11615
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11616
+ const tree = parseSource10(makeGoParser3(), file.content);
11617
+ const { structs, models, tableFor } = analyze(tree);
11618
+ const out = [];
11619
+ const seen = /* @__PURE__ */ new Set();
11620
+ const emit = (childTable, parentTable, line) => {
11621
+ if (!childTable || !parentTable || childTable === parentTable) return;
11622
+ const key = `${childTable}->${parentTable}`;
11623
+ if (seen.has(key)) return;
11624
+ seen.add(key);
11625
+ out.push({
11626
+ childTable,
11627
+ parentTable,
11628
+ evidence: {
11629
+ file: toPosix(import_node_path49.default.relative(serviceDir, file.path)),
11630
+ line,
11631
+ snippet: snippet(file.content, line)
11632
+ }
11633
+ });
11634
+ };
11635
+ for (const name of models) {
11636
+ const struct = structs.get(name);
11637
+ if (!struct) continue;
11638
+ const thisTable = tableFor(name);
11639
+ const scalarNames = new Set(
11640
+ struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
11641
+ );
11642
+ for (const field of struct.fields) {
11643
+ if (field.tag.skip) continue;
11644
+ if (!isRelationField(field, structs)) continue;
11645
+ const relTable = tableFor(field.typeName);
11646
+ if (field.tag.many2many) {
11647
+ emit(field.tag.many2many, thisTable, field.line);
11648
+ emit(field.tag.many2many, relTable, field.line);
11649
+ continue;
11650
+ }
11651
+ if (field.isSlice) {
11652
+ emit(relTable, thisTable, field.line);
11653
+ continue;
11654
+ }
11655
+ const convFk = field.names[0] + "ID";
11656
+ const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
11657
+ if (belongsTo) emit(thisTable, relTable, field.line);
11658
+ else emit(relTable, thisTable, field.line);
11659
+ }
11660
+ }
11661
+ return out;
11662
+ }
11663
+
11117
11664
  // src/extract/calls/index.ts
11118
11665
  function edgeTypeFromEndpoint(ep) {
11119
11666
  switch (ep.edgeType) {
11120
11667
  case "PUBLISHES_TO":
11121
- return import_types36.EdgeType.PUBLISHES_TO;
11668
+ return import_types37.EdgeType.PUBLISHES_TO;
11122
11669
  case "CONSUMES_FROM":
11123
- return import_types36.EdgeType.CONSUMES_FROM;
11670
+ return import_types37.EdgeType.CONSUMES_FROM;
11124
11671
  default:
11125
- return import_types36.EdgeType.CALLS;
11672
+ return import_types37.EdgeType.CALLS;
11126
11673
  }
11127
11674
  }
11128
11675
  function isAwsKind(kind) {
@@ -11155,6 +11702,11 @@ async function addExternalEndpointEdges(graph, services) {
11155
11702
  } catch (err) {
11156
11703
  recordExtractionError("go SQL call extraction", file.path, err);
11157
11704
  }
11705
+ try {
11706
+ endpoints.push(...gormEndpointsFromFile(file, service.dir));
11707
+ } catch (err) {
11708
+ recordExtractionError("gorm data-axis extraction", file.path, err);
11709
+ }
11158
11710
  try {
11159
11711
  endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
11160
11712
  endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
@@ -11177,7 +11729,7 @@ async function addExternalEndpointEdges(graph, services) {
11177
11729
  if (!graph.hasNode(ep.infraId)) {
11178
11730
  const node = {
11179
11731
  id: ep.infraId,
11180
- type: import_types36.NodeType.InfraNode,
11732
+ type: import_types37.NodeType.InfraNode,
11181
11733
  name: ep.name,
11182
11734
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
11183
11735
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -11190,21 +11742,21 @@ async function addExternalEndpointEdges(graph, services) {
11190
11742
  }
11191
11743
  if (ep.columns && ep.columns.length > 0) {
11192
11744
  const node = graph.getNodeAttributes(ep.infraId);
11193
- if (node.type === import_types36.NodeType.InfraNode) {
11745
+ if (node.type === import_types37.NodeType.InfraNode) {
11194
11746
  graph.replaceNodeAttributes(ep.infraId, {
11195
11747
  ...node,
11196
11748
  columns: foldColumns(
11197
11749
  node.columns,
11198
11750
  ep.columns,
11199
- import_types36.Provenance.EXTRACTED,
11200
- (0, import_types36.confidenceForExtracted)(ep.confidenceKind)
11751
+ import_types37.Provenance.EXTRACTED,
11752
+ (0, import_types37.confidenceForExtracted)(ep.confidenceKind)
11201
11753
  )
11202
11754
  });
11203
11755
  }
11204
11756
  }
11205
11757
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
11206
11758
  const node = graph.getNodeAttributes(ep.infraId);
11207
- if (node.type === import_types36.NodeType.InfraNode) {
11759
+ if (node.type === import_types37.NodeType.InfraNode) {
11208
11760
  graph.replaceNodeAttributes(ep.infraId, {
11209
11761
  ...node,
11210
11762
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -11212,7 +11764,7 @@ async function addExternalEndpointEdges(graph, services) {
11212
11764
  }
11213
11765
  }
11214
11766
  const edgeType = edgeTypeFromEndpoint(ep);
11215
- const confidence = (0, import_types36.confidenceForExtracted)(ep.confidenceKind);
11767
+ const confidence = (0, import_types37.confidenceForExtracted)(ep.confidenceKind);
11216
11768
  const relFile = toPosix(ep.evidence.file);
11217
11769
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
11218
11770
  graph,
@@ -11222,7 +11774,7 @@ async function addExternalEndpointEdges(graph, services) {
11222
11774
  );
11223
11775
  nodesAdded += n;
11224
11776
  edgesAdded += e;
11225
- if (!(0, import_types36.passesExtractedFloor)(confidence)) {
11777
+ if (!(0, import_types37.passesExtractedFloor)(confidence)) {
11226
11778
  noteExtractedDropped({
11227
11779
  source: fileNodeId,
11228
11780
  target: ep.infraId,
@@ -11242,7 +11794,7 @@ async function addExternalEndpointEdges(graph, services) {
11242
11794
  source: fileNodeId,
11243
11795
  target: ep.infraId,
11244
11796
  type: edgeType,
11245
- provenance: import_types36.Provenance.EXTRACTED,
11797
+ provenance: import_types37.Provenance.EXTRACTED,
11246
11798
  confidence,
11247
11799
  evidence: ep.evidence
11248
11800
  };
@@ -11265,7 +11817,7 @@ async function addCallEdges(graph, services) {
11265
11817
 
11266
11818
  // src/extract/table-edges.ts
11267
11819
  init_cjs_shims();
11268
- var import_types37 = require("@neat.is/types");
11820
+ var import_types38 = require("@neat.is/types");
11269
11821
  async function addTableEdges(graph, services) {
11270
11822
  let nodesAdded = 0;
11271
11823
  let edgesAdded = 0;
@@ -11279,6 +11831,7 @@ async function addTableEdges(graph, services) {
11279
11831
  refs.push(...sqlalchemyForeignKeys(file, service.dir));
11280
11832
  refs.push(...railsSchemaForeignKeys(file, service.dir));
11281
11833
  refs.push(...laravelMigrationForeignKeys(file, service.dir));
11834
+ refs.push(...gormForeignKeys(file, service.dir));
11282
11835
  modelRefs.push(...railsModelForeignKeys(file, service.dir));
11283
11836
  modelRefs.push(...laravelModelForeignKeys(file, service.dir));
11284
11837
  } catch (err) {
@@ -11292,20 +11845,20 @@ async function addTableEdges(graph, services) {
11292
11845
  }
11293
11846
  refs.push(...modelRefs);
11294
11847
  for (const ref of refs) {
11295
- const childId = (0, import_types37.infraId)("sql-table", ref.childTable);
11296
- const parentId = (0, import_types37.infraId)("sql-table", ref.parentTable);
11848
+ const childId = (0, import_types38.infraId)("sql-table", ref.childTable);
11849
+ const parentId = (0, import_types38.infraId)("sql-table", ref.parentTable);
11297
11850
  if (childId === parentId) continue;
11298
11851
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
11299
11852
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
11300
- const edgeId = (0, import_types37.extractedEdgeId)(childId, parentId, import_types37.EdgeType.REFERENCES);
11853
+ const edgeId = (0, import_types38.extractedEdgeId)(childId, parentId, import_types38.EdgeType.REFERENCES);
11301
11854
  if (graph.hasEdge(edgeId)) continue;
11302
11855
  const edge = {
11303
11856
  id: edgeId,
11304
11857
  source: childId,
11305
11858
  target: parentId,
11306
- type: import_types37.EdgeType.REFERENCES,
11307
- provenance: import_types37.Provenance.EXTRACTED,
11308
- confidence: (0, import_types37.confidenceForExtracted)("structural"),
11859
+ type: import_types38.EdgeType.REFERENCES,
11860
+ provenance: import_types38.Provenance.EXTRACTED,
11861
+ confidence: (0, import_types38.confidenceForExtracted)("structural"),
11309
11862
  evidence: ref.evidence
11310
11863
  };
11311
11864
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -11318,7 +11871,7 @@ function ensureTableNode(graph, id, name) {
11318
11871
  if (graph.hasNode(id)) return 0;
11319
11872
  const node = {
11320
11873
  id,
11321
- type: import_types37.NodeType.InfraNode,
11874
+ type: import_types38.NodeType.InfraNode,
11322
11875
  name,
11323
11876
  provider: "self",
11324
11877
  kind: "sql-table"
@@ -11332,16 +11885,16 @@ init_cjs_shims();
11332
11885
 
11333
11886
  // src/extract/infra/docker-compose.ts
11334
11887
  init_cjs_shims();
11335
- var import_node_path49 = __toESM(require("path"), 1);
11336
- var import_types39 = require("@neat.is/types");
11888
+ var import_node_path50 = __toESM(require("path"), 1);
11889
+ var import_types40 = require("@neat.is/types");
11337
11890
 
11338
11891
  // src/extract/infra/shared.ts
11339
11892
  init_cjs_shims();
11340
- var import_types38 = require("@neat.is/types");
11893
+ var import_types39 = require("@neat.is/types");
11341
11894
  function makeInfraNode(kind, name, provider = "self", extras) {
11342
11895
  return {
11343
- id: (0, import_types38.infraId)(kind, name),
11344
- type: import_types38.NodeType.InfraNode,
11896
+ id: (0, import_types39.infraId)(kind, name),
11897
+ type: import_types39.NodeType.InfraNode,
11345
11898
  name,
11346
11899
  provider,
11347
11900
  kind,
@@ -11385,8 +11938,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
11385
11938
  source: anchorId,
11386
11939
  target: node.id,
11387
11940
  type: edgeType,
11388
- provenance: import_types38.Provenance.EXTRACTED,
11389
- confidence: (0, import_types38.confidenceForExtracted)("structural"),
11941
+ provenance: import_types39.Provenance.EXTRACTED,
11942
+ confidence: (0, import_types39.confidenceForExtracted)("structural"),
11390
11943
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11391
11944
  };
11392
11945
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11403,7 +11956,7 @@ function dependsOnList(value) {
11403
11956
  }
11404
11957
  function serviceNameToServiceNode(name, services) {
11405
11958
  for (const s of services) {
11406
- if (s.node.name === name || import_node_path49.default.basename(s.dir) === name) return s.node.id;
11959
+ if (s.node.name === name || import_node_path50.default.basename(s.dir) === name) return s.node.id;
11407
11960
  }
11408
11961
  return null;
11409
11962
  }
@@ -11412,7 +11965,7 @@ async function addComposeInfra(graph, scanPath, services) {
11412
11965
  let edgesAdded = 0;
11413
11966
  let composePath = null;
11414
11967
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
11415
- const abs = import_node_path49.default.join(scanPath, name);
11968
+ const abs = import_node_path50.default.join(scanPath, name);
11416
11969
  if (await exists(abs)) {
11417
11970
  composePath = abs;
11418
11971
  break;
@@ -11425,13 +11978,13 @@ async function addComposeInfra(graph, scanPath, services) {
11425
11978
  } catch (err) {
11426
11979
  recordExtractionError(
11427
11980
  "infra docker-compose",
11428
- import_node_path49.default.relative(scanPath, composePath),
11981
+ import_node_path50.default.relative(scanPath, composePath),
11429
11982
  err
11430
11983
  );
11431
11984
  return { nodesAdded, edgesAdded };
11432
11985
  }
11433
11986
  if (!compose?.services) return { nodesAdded, edgesAdded };
11434
- const evidenceFile = import_node_path49.default.relative(scanPath, composePath).split(import_node_path49.default.sep).join("/");
11987
+ const evidenceFile = import_node_path50.default.relative(scanPath, composePath).split(import_node_path50.default.sep).join("/");
11435
11988
  const composeNameToNodeId = /* @__PURE__ */ new Map();
11436
11989
  for (const [composeName, svc] of Object.entries(compose.services)) {
11437
11990
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -11453,15 +12006,15 @@ async function addComposeInfra(graph, scanPath, services) {
11453
12006
  for (const dep of dependsOnList(svc.depends_on)) {
11454
12007
  const targetId = composeNameToNodeId.get(dep);
11455
12008
  if (!targetId) continue;
11456
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types39.EdgeType.DEPENDS_ON);
12009
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types40.EdgeType.DEPENDS_ON);
11457
12010
  if (graph.hasEdge(edgeId)) continue;
11458
12011
  const edge = {
11459
12012
  id: edgeId,
11460
12013
  source: sourceId,
11461
12014
  target: targetId,
11462
- type: import_types39.EdgeType.DEPENDS_ON,
11463
- provenance: import_types39.Provenance.EXTRACTED,
11464
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
12015
+ type: import_types40.EdgeType.DEPENDS_ON,
12016
+ provenance: import_types40.Provenance.EXTRACTED,
12017
+ confidence: (0, import_types40.confidenceForExtracted)("structural"),
11465
12018
  evidence: { file: evidenceFile }
11466
12019
  };
11467
12020
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11473,9 +12026,9 @@ async function addComposeInfra(graph, scanPath, services) {
11473
12026
 
11474
12027
  // src/extract/infra/dockerfile.ts
11475
12028
  init_cjs_shims();
11476
- var import_node_path50 = __toESM(require("path"), 1);
12029
+ var import_node_path51 = __toESM(require("path"), 1);
11477
12030
  var import_node_fs19 = require("fs");
11478
- var import_types40 = require("@neat.is/types");
12031
+ var import_types41 = require("@neat.is/types");
11479
12032
  function readDockerfile(content) {
11480
12033
  let image = null;
11481
12034
  const ports = [];
@@ -11504,7 +12057,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11504
12057
  let nodesAdded = 0;
11505
12058
  let edgesAdded = 0;
11506
12059
  for (const service of services) {
11507
- const dockerfilePath = import_node_path50.default.join(service.dir, "Dockerfile");
12060
+ const dockerfilePath = import_node_path51.default.join(service.dir, "Dockerfile");
11508
12061
  if (!await exists(dockerfilePath)) continue;
11509
12062
  let content;
11510
12063
  try {
@@ -11512,7 +12065,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11512
12065
  } catch (err) {
11513
12066
  recordExtractionError(
11514
12067
  "infra dockerfile",
11515
- import_node_path50.default.relative(scanPath, dockerfilePath),
12068
+ import_node_path51.default.relative(scanPath, dockerfilePath),
11516
12069
  err
11517
12070
  );
11518
12071
  continue;
@@ -11524,8 +12077,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11524
12077
  graph.addNode(node.id, node);
11525
12078
  nodesAdded++;
11526
12079
  }
11527
- const relDockerfile = toPosix(import_node_path50.default.relative(service.dir, dockerfilePath));
11528
- const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, dockerfilePath));
12080
+ const relDockerfile = toPosix(import_node_path51.default.relative(service.dir, dockerfilePath));
12081
+ const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, dockerfilePath));
11529
12082
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11530
12083
  graph,
11531
12084
  service.pkg.name,
@@ -11534,15 +12087,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11534
12087
  );
11535
12088
  nodesAdded += fn;
11536
12089
  edgesAdded += fe;
11537
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types40.EdgeType.RUNS_ON);
12090
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types41.EdgeType.RUNS_ON);
11538
12091
  if (!graph.hasEdge(edgeId)) {
11539
12092
  const edge = {
11540
12093
  id: edgeId,
11541
12094
  source: fileNodeId,
11542
12095
  target: node.id,
11543
- type: import_types40.EdgeType.RUNS_ON,
11544
- provenance: import_types40.Provenance.EXTRACTED,
11545
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12096
+ type: import_types41.EdgeType.RUNS_ON,
12097
+ provenance: import_types41.Provenance.EXTRACTED,
12098
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11546
12099
  evidence: {
11547
12100
  file: evidenceFile,
11548
12101
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -11557,15 +12110,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11557
12110
  graph.addNode(portNode.id, portNode);
11558
12111
  nodesAdded++;
11559
12112
  }
11560
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types40.EdgeType.CONNECTS_TO);
12113
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types41.EdgeType.CONNECTS_TO);
11561
12114
  if (graph.hasEdge(portEdgeId)) continue;
11562
12115
  const portEdge = {
11563
12116
  id: portEdgeId,
11564
12117
  source: fileNodeId,
11565
12118
  target: portNode.id,
11566
- type: import_types40.EdgeType.CONNECTS_TO,
11567
- provenance: import_types40.Provenance.EXTRACTED,
11568
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12119
+ type: import_types41.EdgeType.CONNECTS_TO,
12120
+ provenance: import_types41.Provenance.EXTRACTED,
12121
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11569
12122
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
11570
12123
  };
11571
12124
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -11578,8 +12131,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11578
12131
  // src/extract/infra/terraform.ts
11579
12132
  init_cjs_shims();
11580
12133
  var import_node_fs20 = require("fs");
11581
- var import_node_path51 = __toESM(require("path"), 1);
11582
- var import_types41 = require("@neat.is/types");
12134
+ var import_node_path52 = __toESM(require("path"), 1);
12135
+ var import_types42 = require("@neat.is/types");
11583
12136
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
11584
12137
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
11585
12138
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -11589,11 +12142,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
11589
12142
  for (const entry2 of entries) {
11590
12143
  if (entry2.isDirectory()) {
11591
12144
  if (IGNORED_DIRS.has(entry2.name) || entry2.name === ".terraform") continue;
11592
- const child = import_node_path51.default.join(start, entry2.name);
12145
+ const child = import_node_path52.default.join(start, entry2.name);
11593
12146
  if (await isPythonVenvDir(child)) continue;
11594
12147
  out.push(...await walkTfFiles(child, depth + 1, max));
11595
12148
  } else if (entry2.isFile() && entry2.name.endsWith(".tf")) {
11596
- out.push(import_node_path51.default.join(start, entry2.name));
12149
+ out.push(import_node_path52.default.join(start, entry2.name));
11597
12150
  }
11598
12151
  }
11599
12152
  return out;
@@ -11625,7 +12178,7 @@ async function addTerraformResources(graph, scanPath) {
11625
12178
  const files = await walkTfFiles(scanPath);
11626
12179
  for (const file of files) {
11627
12180
  const content = await import_node_fs20.promises.readFile(file, "utf8");
11628
- const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, file));
12181
+ const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, file));
11629
12182
  const resources = [];
11630
12183
  const byKey = /* @__PURE__ */ new Map();
11631
12184
  RESOURCE_RE.lastIndex = 0;
@@ -11660,16 +12213,16 @@ async function addTerraformResources(graph, scanPath) {
11660
12213
  if (!target) continue;
11661
12214
  if (seen.has(target.nodeId)) continue;
11662
12215
  seen.add(target.nodeId);
11663
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types41.EdgeType.DEPENDS_ON);
12216
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types42.EdgeType.DEPENDS_ON);
11664
12217
  if (graph.hasEdge(edgeId)) continue;
11665
12218
  const line = lineAt2(content, resource.bodyOffset + ref.index);
11666
12219
  const edge = {
11667
12220
  id: edgeId,
11668
12221
  source: resource.nodeId,
11669
12222
  target: target.nodeId,
11670
- type: import_types41.EdgeType.DEPENDS_ON,
11671
- provenance: import_types41.Provenance.EXTRACTED,
11672
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12223
+ type: import_types42.EdgeType.DEPENDS_ON,
12224
+ provenance: import_types42.Provenance.EXTRACTED,
12225
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
11673
12226
  evidence: { file: evidenceFile, line, snippet: key }
11674
12227
  };
11675
12228
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11683,7 +12236,7 @@ async function addTerraformResources(graph, scanPath) {
11683
12236
  // src/extract/infra/k8s.ts
11684
12237
  init_cjs_shims();
11685
12238
  var import_node_fs21 = require("fs");
11686
- var import_node_path52 = __toESM(require("path"), 1);
12239
+ var import_node_path53 = __toESM(require("path"), 1);
11687
12240
  var import_yaml3 = require("yaml");
11688
12241
  var K8S_KIND_TO_INFRA_KIND = {
11689
12242
  Service: "k8s-service",
@@ -11701,11 +12254,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
11701
12254
  for (const entry2 of entries) {
11702
12255
  if (entry2.isDirectory()) {
11703
12256
  if (IGNORED_DIRS.has(entry2.name)) continue;
11704
- const child = import_node_path52.default.join(start, entry2.name);
12257
+ const child = import_node_path53.default.join(start, entry2.name);
11705
12258
  if (await isPythonVenvDir(child)) continue;
11706
12259
  out.push(...await walkYamlFiles2(child, depth + 1, max));
11707
- } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path52.default.extname(entry2.name))) {
11708
- out.push(import_node_path52.default.join(start, entry2.name));
12260
+ } else if (entry2.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path53.default.extname(entry2.name))) {
12261
+ out.push(import_node_path53.default.join(start, entry2.name));
11709
12262
  }
11710
12263
  }
11711
12264
  return out;
@@ -11739,13 +12292,13 @@ async function addK8sResources(graph, scanPath) {
11739
12292
  // src/extract/infra/cloudflare.ts
11740
12293
  init_cjs_shims();
11741
12294
  var import_node_fs22 = require("fs");
11742
- var import_node_path53 = __toESM(require("path"), 1);
12295
+ var import_node_path54 = __toESM(require("path"), 1);
11743
12296
  var import_smol_toml2 = require("smol-toml");
11744
- var import_types42 = require("@neat.is/types");
12297
+ var import_types43 = require("@neat.is/types");
11745
12298
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
11746
12299
  async function readWranglerConfig(dir) {
11747
12300
  for (const filename of WRANGLER_FILENAMES) {
11748
- const abs = import_node_path53.default.join(dir, filename);
12301
+ const abs = import_node_path54.default.join(dir, filename);
11749
12302
  if (!await exists(abs)) continue;
11750
12303
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
11751
12304
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -11789,8 +12342,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
11789
12342
  source: anchorId,
11790
12343
  target: node.id,
11791
12344
  type: edgeType,
11792
- provenance: import_types42.Provenance.EXTRACTED,
11793
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12345
+ provenance: import_types43.Provenance.EXTRACTED,
12346
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11794
12347
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11795
12348
  };
11796
12349
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11808,11 +12361,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11808
12361
  try {
11809
12362
  read = await readWranglerConfig(service.dir);
11810
12363
  } catch (err) {
11811
- recordExtractionError("infra cloudflare", import_node_path53.default.relative(scanPath, service.dir), err);
12364
+ recordExtractionError("infra cloudflare", import_node_path54.default.relative(scanPath, service.dir), err);
11812
12365
  continue;
11813
12366
  }
11814
12367
  if (!read || !read.config.name) continue;
11815
- const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, read.relFile)));
12368
+ const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, read.relFile)));
11816
12369
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
11817
12370
  }
11818
12371
  for (const worker of discovered) {
@@ -11824,7 +12377,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11824
12377
  }
11825
12378
  let anchorId = service.node.id;
11826
12379
  if (config.main) {
11827
- const entryRelPath = toPosix(import_node_path53.default.normalize(config.main));
12380
+ const entryRelPath = toPosix(import_node_path54.default.normalize(config.main));
11828
12381
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11829
12382
  graph,
11830
12383
  service.pkg.name,
@@ -11851,15 +12404,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11851
12404
  nodesAdded++;
11852
12405
  }
11853
12406
  if (runtimeNode.id !== anchorId) {
11854
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types42.EdgeType.RUNS_ON);
12407
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types43.EdgeType.RUNS_ON);
11855
12408
  if (!graph.hasEdge(runsOnId)) {
11856
12409
  const edge = {
11857
12410
  id: runsOnId,
11858
12411
  source: anchorId,
11859
12412
  target: runtimeNode.id,
11860
- type: import_types42.EdgeType.RUNS_ON,
11861
- provenance: import_types42.Provenance.EXTRACTED,
11862
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12413
+ type: import_types43.EdgeType.RUNS_ON,
12414
+ provenance: import_types43.Provenance.EXTRACTED,
12415
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11863
12416
  evidence: {
11864
12417
  file: evidenceFile,
11865
12418
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -11873,7 +12426,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11873
12426
  const result = addResourceEdge(
11874
12427
  graph,
11875
12428
  anchorId,
11876
- import_types42.EdgeType.CONNECTS_TO,
12429
+ import_types43.EdgeType.CONNECTS_TO,
11877
12430
  "cloudflare-route",
11878
12431
  route,
11879
12432
  evidenceFile,
@@ -11897,7 +12450,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11897
12450
  const result = addResourceEdge(
11898
12451
  graph,
11899
12452
  anchorId,
11900
- import_types42.EdgeType.DEPENDS_ON,
12453
+ import_types43.EdgeType.DEPENDS_ON,
11901
12454
  group.kind,
11902
12455
  name,
11903
12456
  evidenceFile,
@@ -11911,7 +12464,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11911
12464
  const result = addResourceEdge(
11912
12465
  graph,
11913
12466
  anchorId,
11914
- import_types42.EdgeType.DEPENDS_ON,
12467
+ import_types43.EdgeType.DEPENDS_ON,
11915
12468
  "cloudflare-cron",
11916
12469
  cron,
11917
12470
  evidenceFile,
@@ -11924,7 +12477,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11924
12477
  const result = addResourceEdge(
11925
12478
  graph,
11926
12479
  anchorId,
11927
- import_types42.EdgeType.DEPENDS_ON,
12480
+ import_types43.EdgeType.DEPENDS_ON,
11928
12481
  "cloudflare-env-var",
11929
12482
  varName,
11930
12483
  evidenceFile,
@@ -11937,15 +12490,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11937
12490
  if (!svc.service) continue;
11938
12491
  const target = workerIndex.get(svc.service);
11939
12492
  if (target && target.anchorId !== anchorId) {
11940
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types42.EdgeType.CALLS);
12493
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types43.EdgeType.CALLS);
11941
12494
  if (!graph.hasEdge(edgeId)) {
11942
12495
  const edge = {
11943
12496
  id: edgeId,
11944
12497
  source: anchorId,
11945
12498
  target: target.anchorId,
11946
- type: import_types42.EdgeType.CALLS,
11947
- provenance: import_types42.Provenance.EXTRACTED,
11948
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12499
+ type: import_types43.EdgeType.CALLS,
12500
+ provenance: import_types43.Provenance.EXTRACTED,
12501
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11949
12502
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
11950
12503
  };
11951
12504
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11956,7 +12509,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11956
12509
  const result = addResourceEdge(
11957
12510
  graph,
11958
12511
  anchorId,
11959
- import_types42.EdgeType.DEPENDS_ON,
12512
+ import_types43.EdgeType.DEPENDS_ON,
11960
12513
  "cloudflare-service-binding",
11961
12514
  svc.service,
11962
12515
  evidenceFile,
@@ -11972,12 +12525,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11972
12525
  // src/extract/infra/vercel.ts
11973
12526
  init_cjs_shims();
11974
12527
  var import_node_fs23 = require("fs");
11975
- var import_node_path54 = __toESM(require("path"), 1);
11976
- var import_types43 = require("@neat.is/types");
12528
+ var import_node_path55 = __toESM(require("path"), 1);
12529
+ var import_types44 = require("@neat.is/types");
11977
12530
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
11978
12531
  async function readVercelConfig(dir) {
11979
12532
  for (const filename of VERCEL_CONFIG_FILENAMES) {
11980
- const abs = import_node_path54.default.join(dir, filename);
12533
+ const abs = import_node_path55.default.join(dir, filename);
11981
12534
  if (!await exists(abs)) continue;
11982
12535
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
11983
12536
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -11986,7 +12539,7 @@ async function readVercelConfig(dir) {
11986
12539
  return null;
11987
12540
  }
11988
12541
  async function readLinkedProjectName(dir) {
11989
- const abs = import_node_path54.default.join(dir, ".vercel", "project.json");
12542
+ const abs = import_node_path55.default.join(dir, ".vercel", "project.json");
11990
12543
  if (!await exists(abs)) return void 0;
11991
12544
  const parsed = JSON.parse(await import_node_fs23.promises.readFile(abs, "utf8"));
11992
12545
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -12004,7 +12557,7 @@ async function addVercelServices(graph, services, scanPath) {
12004
12557
  read = await readVercelConfig(service.dir);
12005
12558
  projectName = await readLinkedProjectName(service.dir);
12006
12559
  } catch (err) {
12007
- recordExtractionError("infra vercel", import_node_path54.default.relative(scanPath, service.dir), err);
12560
+ recordExtractionError("infra vercel", import_node_path55.default.relative(scanPath, service.dir), err);
12008
12561
  continue;
12009
12562
  }
12010
12563
  if (!read && !projectName) continue;
@@ -12020,7 +12573,7 @@ async function addVercelServices(graph, services, scanPath) {
12020
12573
  const anchorId = service.node.id;
12021
12574
  if (!read) continue;
12022
12575
  const { config, relFile, raw } = read;
12023
- const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
12576
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12024
12577
  const add = (edgeType, kind, name) => {
12025
12578
  if (!name) return;
12026
12579
  const result = emitPlatformResourceEdge(
@@ -12036,12 +12589,12 @@ async function addVercelServices(graph, services, scanPath) {
12036
12589
  nodesAdded += result.nodesAdded;
12037
12590
  edgesAdded += result.edgesAdded;
12038
12591
  };
12039
- add(import_types43.EdgeType.RUNS_ON, "vercel", "vercel");
12040
- for (const cron of config.crons ?? []) add(import_types43.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12041
- for (const varName of Object.keys(config.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12042
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12592
+ add(import_types44.EdgeType.RUNS_ON, "vercel", "vercel");
12593
+ for (const cron of config.crons ?? []) add(import_types44.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12594
+ for (const varName of Object.keys(config.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12595
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12043
12596
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
12044
- add(import_types43.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12597
+ add(import_types44.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12045
12598
  }
12046
12599
  }
12047
12600
  return { nodesAdded, edgesAdded };
@@ -12050,13 +12603,13 @@ async function addVercelServices(graph, services, scanPath) {
12050
12603
  // src/extract/infra/railway.ts
12051
12604
  init_cjs_shims();
12052
12605
  var import_node_fs24 = require("fs");
12053
- var import_node_path55 = __toESM(require("path"), 1);
12606
+ var import_node_path56 = __toESM(require("path"), 1);
12054
12607
  var import_smol_toml3 = require("smol-toml");
12055
- var import_types44 = require("@neat.is/types");
12608
+ var import_types45 = require("@neat.is/types");
12056
12609
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
12057
12610
  async function readRailwayConfig(dir) {
12058
12611
  for (const filename of RAILWAY_FILENAMES) {
12059
- const abs = import_node_path55.default.join(dir, filename);
12612
+ const abs = import_node_path56.default.join(dir, filename);
12060
12613
  if (!await exists(abs)) continue;
12061
12614
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
12062
12615
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12072,7 +12625,7 @@ async function addRailwayServices(graph, services, scanPath) {
12072
12625
  try {
12073
12626
  read = await readRailwayConfig(service.dir);
12074
12627
  } catch (err) {
12075
- recordExtractionError("infra railway", import_node_path55.default.relative(scanPath, service.dir), err);
12628
+ recordExtractionError("infra railway", import_node_path56.default.relative(scanPath, service.dir), err);
12076
12629
  continue;
12077
12630
  }
12078
12631
  if (!read) continue;
@@ -12082,7 +12635,7 @@ async function addRailwayServices(graph, services, scanPath) {
12082
12635
  }
12083
12636
  const anchorId = service.node.id;
12084
12637
  const { config, relFile, raw } = read;
12085
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12638
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12086
12639
  const add = (edgeType, kind, name) => {
12087
12640
  if (!name) return;
12088
12641
  const result = emitPlatformResourceEdge(
@@ -12098,9 +12651,9 @@ async function addRailwayServices(graph, services, scanPath) {
12098
12651
  nodesAdded += result.nodesAdded;
12099
12652
  edgesAdded += result.edgesAdded;
12100
12653
  };
12101
- add(import_types44.EdgeType.RUNS_ON, "railway", "railway");
12102
- add(import_types44.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12103
- add(import_types44.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12654
+ add(import_types45.EdgeType.RUNS_ON, "railway", "railway");
12655
+ add(import_types45.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12656
+ add(import_types45.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12104
12657
  }
12105
12658
  return { nodesAdded, edgesAdded };
12106
12659
  }
@@ -12108,12 +12661,12 @@ async function addRailwayServices(graph, services, scanPath) {
12108
12661
  // src/extract/infra/supabase.ts
12109
12662
  init_cjs_shims();
12110
12663
  var import_node_fs25 = require("fs");
12111
- var import_node_path56 = __toESM(require("path"), 1);
12664
+ var import_node_path57 = __toESM(require("path"), 1);
12112
12665
  var import_smol_toml4 = require("smol-toml");
12113
- var import_types45 = require("@neat.is/types");
12666
+ var import_types46 = require("@neat.is/types");
12114
12667
  async function readSupabaseConfig(dir) {
12115
- const relFile = import_node_path56.default.join("supabase", "config.toml");
12116
- const abs = import_node_path56.default.join(dir, relFile);
12668
+ const relFile = import_node_path57.default.join("supabase", "config.toml");
12669
+ const abs = import_node_path57.default.join(dir, relFile);
12117
12670
  if (!await exists(abs)) return null;
12118
12671
  const raw = await import_node_fs25.promises.readFile(abs, "utf8");
12119
12672
  const config = (0, import_smol_toml4.parse)(raw);
@@ -12127,7 +12680,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12127
12680
  try {
12128
12681
  read = await readSupabaseConfig(service.dir);
12129
12682
  } catch (err) {
12130
- recordExtractionError("infra supabase", import_node_path56.default.relative(scanPath, service.dir), err);
12683
+ recordExtractionError("infra supabase", import_node_path57.default.relative(scanPath, service.dir), err);
12131
12684
  continue;
12132
12685
  }
12133
12686
  if (!read) continue;
@@ -12142,7 +12695,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12142
12695
  });
12143
12696
  }
12144
12697
  const anchorId = service.node.id;
12145
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12698
+ const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
12146
12699
  const add = (edgeType, kind, name) => {
12147
12700
  if (!name) return;
12148
12701
  const result = emitPlatformResourceEdge(
@@ -12158,10 +12711,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
12158
12711
  nodesAdded += result.nodesAdded;
12159
12712
  edgesAdded += result.edgesAdded;
12160
12713
  };
12161
- add(import_types45.EdgeType.RUNS_ON, "supabase", "supabase");
12162
- for (const fn of Object.keys(config.functions ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "supabase-function", fn);
12163
- if (config.storage) add(import_types45.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12164
- if (config.auth) add(import_types45.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12714
+ add(import_types46.EdgeType.RUNS_ON, "supabase", "supabase");
12715
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types46.EdgeType.DEPENDS_ON, "supabase-function", fn);
12716
+ if (config.storage) add(import_types46.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12717
+ if (config.auth) add(import_types46.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12165
12718
  }
12166
12719
  return { nodesAdded, edgesAdded };
12167
12720
  }
@@ -12184,14 +12737,14 @@ async function addInfra(graph, scanPath, services) {
12184
12737
 
12185
12738
  // src/extract/zod-shapes.ts
12186
12739
  init_cjs_shims();
12187
- var import_node_path57 = __toESM(require("path"), 1);
12188
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12740
+ var import_node_path58 = __toESM(require("path"), 1);
12741
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
12189
12742
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
12190
- var import_types46 = require("@neat.is/types");
12743
+ var import_types47 = require("@neat.is/types");
12191
12744
  var ZOD_IMPORT_RE = /\bzod\b/;
12192
12745
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12193
12746
  function parserForExt3(ext) {
12194
- const p = new import_tree_sitter15.default();
12747
+ const p = new import_tree_sitter16.default();
12195
12748
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12196
12749
  return p;
12197
12750
  }
@@ -12279,7 +12832,7 @@ function topLevelSchemas(root) {
12279
12832
  }
12280
12833
  function zodShapesFromFile(file, serviceDir) {
12281
12834
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12282
- const tree = parseSource3(parserForExt3(import_node_path57.default.extname(file.path)), file.content);
12835
+ const tree = parseSource3(parserForExt3(import_node_path58.default.extname(file.path)), file.content);
12283
12836
  const out = [];
12284
12837
  const seen = /* @__PURE__ */ new Set();
12285
12838
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -12293,11 +12846,11 @@ function zodShapesFromFile(file, serviceDir) {
12293
12846
  seen.add(name);
12294
12847
  const line = call.startPosition.row + 1;
12295
12848
  out.push({
12296
- infraId: (0, import_types46.infraId)("zod-schema", name),
12849
+ infraId: (0, import_types47.infraId)("zod-schema", name),
12297
12850
  name,
12298
12851
  fields,
12299
12852
  evidence: {
12300
- file: import_node_path57.default.relative(serviceDir, file.path),
12853
+ file: import_node_path58.default.relative(serviceDir, file.path),
12301
12854
  line,
12302
12855
  snippet: snippet(file.content, line)
12303
12856
  }
@@ -12328,7 +12881,7 @@ async function addZodShapes(graph, services) {
12328
12881
  if (!graph.hasNode(shape.infraId)) {
12329
12882
  const node = {
12330
12883
  id: shape.infraId,
12331
- type: import_types46.NodeType.InfraNode,
12884
+ type: import_types47.NodeType.InfraNode,
12332
12885
  name: shape.name,
12333
12886
  provider: "self",
12334
12887
  kind: "zod-schema"
@@ -12338,14 +12891,14 @@ async function addZodShapes(graph, services) {
12338
12891
  }
12339
12892
  if (shape.fields.length > 0) {
12340
12893
  const node = graph.getNodeAttributes(shape.infraId);
12341
- if (node.type === import_types46.NodeType.InfraNode) {
12894
+ if (node.type === import_types47.NodeType.InfraNode) {
12342
12895
  graph.replaceNodeAttributes(shape.infraId, {
12343
12896
  ...node,
12344
12897
  columns: foldColumns(
12345
12898
  node.columns,
12346
12899
  shape.fields,
12347
- import_types46.Provenance.EXTRACTED,
12348
- (0, import_types46.confidenceForExtracted)("structural")
12900
+ import_types47.Provenance.EXTRACTED,
12901
+ (0, import_types47.confidenceForExtracted)("structural")
12349
12902
  )
12350
12903
  });
12351
12904
  }
@@ -12359,15 +12912,15 @@ async function addZodShapes(graph, services) {
12359
12912
  );
12360
12913
  nodesAdded += n;
12361
12914
  edgesAdded += e;
12362
- const edgeId = (0, import_types46.extractedEdgeId)(fileNodeId, shape.infraId, import_types46.EdgeType.CONTAINS);
12915
+ const edgeId = (0, import_types47.extractedEdgeId)(fileNodeId, shape.infraId, import_types47.EdgeType.CONTAINS);
12363
12916
  if (!graph.hasEdge(edgeId)) {
12364
12917
  const edge = {
12365
12918
  id: edgeId,
12366
12919
  source: fileNodeId,
12367
12920
  target: shape.infraId,
12368
- type: import_types46.EdgeType.CONTAINS,
12369
- provenance: import_types46.Provenance.EXTRACTED,
12370
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
12921
+ type: import_types47.EdgeType.CONTAINS,
12922
+ provenance: import_types47.Provenance.EXTRACTED,
12923
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
12371
12924
  evidence: shape.evidence
12372
12925
  };
12373
12926
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -12381,7 +12934,7 @@ async function addZodShapes(graph, services) {
12381
12934
 
12382
12935
  // src/extract/firestore-rules.ts
12383
12936
  init_cjs_shims();
12384
- var import_types47 = require("@neat.is/types");
12937
+ var import_types48 = require("@neat.is/types");
12385
12938
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
12386
12939
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
12387
12940
  function stripComments(src) {
@@ -12521,7 +13074,7 @@ async function addFirestoreRules(graph, services) {
12521
13074
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
12522
13075
  graph.forEachNode((id, attrs) => {
12523
13076
  const node = attrs;
12524
- if (node.type !== import_types47.NodeType.InfraNode) return;
13077
+ if (node.type !== import_types48.NodeType.InfraNode) return;
12525
13078
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
12526
13079
  const fields = guards.get(collectionKeyFromName(node.name));
12527
13080
  if (!fields || fields.size === 0) return;
@@ -12534,17 +13087,17 @@ async function addFirestoreRules(graph, services) {
12534
13087
  }
12535
13088
 
12536
13089
  // src/extract/index.ts
12537
- var import_node_path59 = __toESM(require("path"), 1);
13090
+ var import_node_path60 = __toESM(require("path"), 1);
12538
13091
 
12539
13092
  // src/extract/retire.ts
12540
13093
  init_cjs_shims();
12541
13094
  var import_node_fs26 = require("fs");
12542
- var import_node_path58 = __toESM(require("path"), 1);
12543
- var import_types48 = require("@neat.is/types");
13095
+ var import_node_path59 = __toESM(require("path"), 1);
13096
+ var import_types49 = require("@neat.is/types");
12544
13097
  function dropOrphanedFileNodes(graph) {
12545
13098
  const orphans = [];
12546
13099
  graph.forEachNode((id, attrs) => {
12547
- if (attrs.type !== import_types48.NodeType.FileNode) return;
13100
+ if (attrs.type !== import_types49.NodeType.FileNode) return;
12548
13101
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
12549
13102
  orphans.push(id);
12550
13103
  }
@@ -12557,7 +13110,7 @@ function retireEdgesByFile(graph, file) {
12557
13110
  const toDrop = [];
12558
13111
  graph.forEachEdge((id, attrs) => {
12559
13112
  const edge = attrs;
12560
- if (edge.provenance !== import_types48.Provenance.EXTRACTED) return;
13113
+ if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
12561
13114
  if (!edge.evidence?.file) return;
12562
13115
  if (edge.evidence.file === normalized) toDrop.push(id);
12563
13116
  });
@@ -12570,14 +13123,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
12570
13123
  const bases = [scanPath, ...serviceDirs];
12571
13124
  graph.forEachEdge((id, attrs) => {
12572
13125
  const edge = attrs;
12573
- if (edge.provenance !== import_types48.Provenance.EXTRACTED) return;
13126
+ if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
12574
13127
  const evidenceFile = edge.evidence?.file;
12575
13128
  if (!evidenceFile) return;
12576
- if (import_node_path58.default.isAbsolute(evidenceFile)) {
13129
+ if (import_node_path59.default.isAbsolute(evidenceFile)) {
12577
13130
  if (!(0, import_node_fs26.existsSync)(evidenceFile)) toDrop.push(id);
12578
13131
  return;
12579
13132
  }
12580
- const found = bases.some((base) => (0, import_node_fs26.existsSync)(import_node_path58.default.join(base, evidenceFile)));
13133
+ const found = bases.some((base) => (0, import_node_fs26.existsSync)(import_node_path59.default.join(base, evidenceFile)));
12581
13134
  if (!found) toDrop.push(id);
12582
13135
  });
12583
13136
  for (const id of toDrop) graph.dropEdge(id);
@@ -12634,7 +13187,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12634
13187
  }
12635
13188
  const droppedEntries = drainDroppedExtracted();
12636
13189
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
12637
- const rejectedPath = import_node_path59.default.join(import_node_path59.default.dirname(opts.errorsPath), "rejected.ndjson");
13190
+ const rejectedPath = import_node_path60.default.join(import_node_path60.default.dirname(opts.errorsPath), "rejected.ndjson");
12638
13191
  try {
12639
13192
  await writeRejectedExtracted(droppedEntries, rejectedPath);
12640
13193
  } catch (err) {
@@ -12668,39 +13221,39 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12668
13221
 
12669
13222
  // src/divergences.ts
12670
13223
  init_cjs_shims();
12671
- var import_types49 = require("@neat.is/types");
13224
+ var import_types50 = require("@neat.is/types");
12672
13225
  function bucketKey(source, target, type) {
12673
13226
  return `${type}|${source}|${target}`;
12674
13227
  }
12675
13228
  function bucketSourceFor(graph, edge) {
12676
- if (edge.type !== import_types49.EdgeType.CONNECTS_TO) return edge.source;
12677
- const parsed = (0, import_types49.parseFileId)(edge.source);
13229
+ if (edge.type !== import_types50.EdgeType.CONNECTS_TO) return edge.source;
13230
+ const parsed = (0, import_types50.parseFileId)(edge.source);
12678
13231
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
12679
13232
  const target = graph.getNodeAttributes(edge.target);
12680
- if (target.type !== import_types49.NodeType.DatabaseNode) return edge.source;
12681
- return (0, import_types49.serviceId)(parsed.service);
13233
+ if (target.type !== import_types50.NodeType.DatabaseNode) return edge.source;
13234
+ return (0, import_types50.serviceId)(parsed.service);
12682
13235
  }
12683
13236
  function bucketEdges(graph) {
12684
13237
  const buckets2 = /* @__PURE__ */ new Map();
12685
13238
  graph.forEachEdge((id, attrs) => {
12686
13239
  const e = attrs;
12687
- const parsed = (0, import_types49.parseEdgeId)(id);
13240
+ const parsed = (0, import_types50.parseEdgeId)(id);
12688
13241
  const provenance = parsed?.provenance ?? e.provenance;
12689
13242
  const source = bucketSourceFor(graph, e);
12690
13243
  const key = bucketKey(source, e.target, e.type);
12691
13244
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
12692
13245
  switch (provenance) {
12693
- case import_types49.Provenance.EXTRACTED:
13246
+ case import_types50.Provenance.EXTRACTED:
12694
13247
  cur.extracted = e;
12695
13248
  break;
12696
- case import_types49.Provenance.OBSERVED:
13249
+ case import_types50.Provenance.OBSERVED:
12697
13250
  cur.observed = e;
12698
13251
  break;
12699
- case import_types49.Provenance.INFERRED:
13252
+ case import_types50.Provenance.INFERRED:
12700
13253
  cur.inferred = e;
12701
13254
  break;
12702
13255
  default:
12703
- if (e.provenance === import_types49.Provenance.STALE) cur.stale = e;
13256
+ if (e.provenance === import_types50.Provenance.STALE) cur.stale = e;
12704
13257
  }
12705
13258
  buckets2.set(key, cur);
12706
13259
  });
@@ -12709,22 +13262,22 @@ function bucketEdges(graph) {
12709
13262
  function nodeIsFrontier(graph, nodeId) {
12710
13263
  if (!graph.hasNode(nodeId)) return false;
12711
13264
  const attrs = graph.getNodeAttributes(nodeId);
12712
- return attrs.type === import_types49.NodeType.FrontierNode;
13265
+ return attrs.type === import_types50.NodeType.FrontierNode;
12713
13266
  }
12714
13267
  function nodeIsWebsocketChannel(graph, nodeId) {
12715
13268
  if (!graph.hasNode(nodeId)) return false;
12716
13269
  const attrs = graph.getNodeAttributes(nodeId);
12717
- return attrs.type === import_types49.NodeType.WebSocketChannelNode;
13270
+ return attrs.type === import_types50.NodeType.WebSocketChannelNode;
12718
13271
  }
12719
13272
  function nodeIsServerAction(graph, nodeId) {
12720
13273
  if (!graph.hasNode(nodeId)) return false;
12721
13274
  const attrs = graph.getNodeAttributes(nodeId);
12722
- return attrs.type === import_types49.NodeType.ServerActionNode;
13275
+ return attrs.type === import_types50.NodeType.ServerActionNode;
12723
13276
  }
12724
13277
  function nodeIsSymbol(graph, nodeId) {
12725
13278
  if (!graph.hasNode(nodeId)) return false;
12726
13279
  const attrs = graph.getNodeAttributes(nodeId);
12727
- return attrs.type === import_types49.NodeType.SymbolNode;
13280
+ return attrs.type === import_types50.NodeType.SymbolNode;
12728
13281
  }
12729
13282
  function clampConfidence(n) {
12730
13283
  if (!Number.isFinite(n)) return 0;
@@ -12744,14 +13297,14 @@ function gradedConfidence(edge) {
12744
13297
  return clampConfidence(confidenceForEdge(edge));
12745
13298
  }
12746
13299
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
12747
- import_types49.EdgeType.CALLS,
12748
- import_types49.EdgeType.CONNECTS_TO,
12749
- import_types49.EdgeType.PUBLISHES_TO,
12750
- import_types49.EdgeType.CONSUMES_FROM
13300
+ import_types50.EdgeType.CALLS,
13301
+ import_types50.EdgeType.CONNECTS_TO,
13302
+ import_types50.EdgeType.PUBLISHES_TO,
13303
+ import_types50.EdgeType.CONSUMES_FROM
12751
13304
  ]);
12752
13305
  function detectMissingDivergences(graph, bucket) {
12753
13306
  const out = [];
12754
- if (bucket.type === import_types49.EdgeType.CONTAINS) return out;
13307
+ if (bucket.type === import_types50.EdgeType.CONTAINS) return out;
12755
13308
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
12756
13309
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
12757
13310
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -12793,7 +13346,7 @@ function declaredHostFor(svc) {
12793
13346
  function hasExtractedConfiguredBy(graph, svcId) {
12794
13347
  for (const edgeId of graph.outboundEdges(svcId)) {
12795
13348
  const e = graph.getEdgeAttributes(edgeId);
12796
- if (e.type === import_types49.EdgeType.CONFIGURED_BY && e.provenance === import_types49.Provenance.EXTRACTED) {
13349
+ if (e.type === import_types50.EdgeType.CONFIGURED_BY && e.provenance === import_types50.Provenance.EXTRACTED) {
12797
13350
  return true;
12798
13351
  }
12799
13352
  }
@@ -12806,10 +13359,10 @@ function detectHostMismatch(graph, svcId, svc) {
12806
13359
  const out = [];
12807
13360
  for (const edgeId of graph.outboundEdges(svcId)) {
12808
13361
  const edge = graph.getEdgeAttributes(edgeId);
12809
- if (edge.type !== import_types49.EdgeType.CONNECTS_TO) continue;
12810
- if (edge.provenance !== import_types49.Provenance.OBSERVED) continue;
13362
+ if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13363
+ if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
12811
13364
  const target = graph.getNodeAttributes(edge.target);
12812
- if (target.type !== import_types49.NodeType.DatabaseNode) continue;
13365
+ if (target.type !== import_types50.NodeType.DatabaseNode) continue;
12813
13366
  const observedHost = target.host?.trim();
12814
13367
  if (!observedHost) continue;
12815
13368
  if (observedHost === declaredHost) continue;
@@ -12831,10 +13384,10 @@ function detectCompatDivergences(graph, svcId, svc) {
12831
13384
  const deps = svc.dependencies ?? {};
12832
13385
  for (const edgeId of graph.outboundEdges(svcId)) {
12833
13386
  const edge = graph.getEdgeAttributes(edgeId);
12834
- if (edge.type !== import_types49.EdgeType.CONNECTS_TO) continue;
12835
- if (edge.provenance !== import_types49.Provenance.OBSERVED) continue;
13387
+ if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13388
+ if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
12836
13389
  const target = graph.getNodeAttributes(edge.target);
12837
- if (target.type !== import_types49.NodeType.DatabaseNode) continue;
13390
+ if (target.type !== import_types50.NodeType.DatabaseNode) continue;
12838
13391
  for (const pair of compatPairs()) {
12839
13392
  if (pair.engine !== target.engine) continue;
12840
13393
  const declared = deps[pair.driver];
@@ -12931,7 +13484,7 @@ function suppressHostMismatchHalves(all) {
12931
13484
  for (const d of all) {
12932
13485
  if (d.type !== "host-mismatch") continue;
12933
13486
  observedHalf.add(`${d.source}->${d.target}`);
12934
- declaredHalf.add((0, import_types49.databaseId)(d.extractedHost));
13487
+ declaredHalf.add((0, import_types50.databaseId)(d.extractedHost));
12935
13488
  }
12936
13489
  if (observedHalf.size === 0) return all;
12937
13490
  return all.filter((d) => {
@@ -12950,13 +13503,13 @@ function computeDivergences(graph, opts = {}) {
12950
13503
  }
12951
13504
  graph.forEachNode((nodeId, attrs) => {
12952
13505
  const n = attrs;
12953
- if (n.type === import_types49.NodeType.ServiceNode) {
13506
+ if (n.type === import_types50.NodeType.ServiceNode) {
12954
13507
  const svc = n;
12955
13508
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
12956
13509
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
12957
13510
  return;
12958
13511
  }
12959
- if (n.type === import_types49.NodeType.InfraNode && n.kind === "sql-table") {
13512
+ if (n.type === import_types50.NodeType.InfraNode && n.kind === "sql-table") {
12960
13513
  for (const d of detectColumnDrift(n)) all.push(d);
12961
13514
  }
12962
13515
  });
@@ -12992,7 +13545,7 @@ function computeDivergences(graph, opts = {}) {
12992
13545
  const bc = "column" in b && b.column ? b.column : "";
12993
13546
  return ac.localeCompare(bc);
12994
13547
  });
12995
- return import_types49.DivergenceResultSchema.parse({
13548
+ return import_types50.DivergenceResultSchema.parse({
12996
13549
  divergences: filtered,
12997
13550
  totalAffected: filtered.length,
12998
13551
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13002,8 +13555,8 @@ function computeDivergences(graph, opts = {}) {
13002
13555
  // src/persist.ts
13003
13556
  init_cjs_shims();
13004
13557
  var import_node_fs27 = require("fs");
13005
- var import_node_path60 = __toESM(require("path"), 1);
13006
- var import_types50 = require("@neat.is/types");
13558
+ var import_node_path61 = __toESM(require("path"), 1);
13559
+ var import_types51 = require("@neat.is/types");
13007
13560
  var SCHEMA_VERSION = 7;
13008
13561
  function migrateV1ToV2(payload) {
13009
13562
  const nodes = payload.graph.nodes;
@@ -13027,7 +13580,7 @@ function migrateV5ToV6(payload) {
13027
13580
  if (Array.isArray(nodes)) {
13028
13581
  for (const node of nodes) {
13029
13582
  const attrs = node.attributes;
13030
- if (!attrs || attrs.type !== import_types50.NodeType.InfraNode) continue;
13583
+ if (!attrs || attrs.type !== import_types51.NodeType.InfraNode) continue;
13031
13584
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
13032
13585
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
13033
13586
  }
@@ -13043,12 +13596,12 @@ function migrateV2ToV3(payload) {
13043
13596
  for (const edge of edges) {
13044
13597
  const attrs = edge.attributes;
13045
13598
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
13046
- attrs.provenance = import_types50.Provenance.OBSERVED;
13599
+ attrs.provenance = import_types51.Provenance.OBSERVED;
13047
13600
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
13048
13601
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
13049
13602
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
13050
13603
  if (type && source && target) {
13051
- const newId = (0, import_types50.observedEdgeId)(source, target, type);
13604
+ const newId = (0, import_types51.observedEdgeId)(source, target, type);
13052
13605
  attrs.id = newId;
13053
13606
  if (edge.key) edge.key = newId;
13054
13607
  }
@@ -13057,7 +13610,7 @@ function migrateV2ToV3(payload) {
13057
13610
  return { ...payload, schemaVersion: 3 };
13058
13611
  }
13059
13612
  async function ensureDir(filePath) {
13060
- await import_node_fs27.promises.mkdir(import_node_path60.default.dirname(filePath), { recursive: true });
13613
+ await import_node_fs27.promises.mkdir(import_node_path61.default.dirname(filePath), { recursive: true });
13061
13614
  }
13062
13615
  async function saveGraphToDisk(graph, outPath) {
13063
13616
  await ensureDir(outPath);
@@ -13148,7 +13701,7 @@ function startPersistLoop(graph, outPath, opts = {}) {
13148
13701
  // src/gitignore.ts
13149
13702
  init_cjs_shims();
13150
13703
  var import_node_fs28 = require("fs");
13151
- var import_node_path61 = __toESM(require("path"), 1);
13704
+ var import_node_path62 = __toESM(require("path"), 1);
13152
13705
  var NEAT_OUT_LINE = "neat-out/";
13153
13706
  var NEAT_HEADER = "# NEAT \u2014 machine-local snapshots and events";
13154
13707
  function isNeatOutLine(line) {
@@ -13156,7 +13709,7 @@ function isNeatOutLine(line) {
13156
13709
  return trimmed === "neat-out/" || trimmed === "neat-out";
13157
13710
  }
13158
13711
  async function ensureNeatOutIgnored(projectDir) {
13159
- const file = import_node_path61.default.join(projectDir, ".gitignore");
13712
+ const file = import_node_path62.default.join(projectDir, ".gitignore");
13160
13713
  let existing = null;
13161
13714
  try {
13162
13715
  existing = await import_node_fs28.promises.readFile(file, "utf8");
@@ -13183,7 +13736,7 @@ ${NEAT_OUT_LINE}
13183
13736
 
13184
13737
  // src/summary.ts
13185
13738
  init_cjs_shims();
13186
- var import_types51 = require("@neat.is/types");
13739
+ var import_types52 = require("@neat.is/types");
13187
13740
  function renderOtelEnvBlock() {
13188
13741
  return [
13189
13742
  "for prod OTel routing, set these in your deploy platform's env:",
@@ -13193,19 +13746,19 @@ function renderOtelEnvBlock() {
13193
13746
  }
13194
13747
  function findIncompatServices(nodes) {
13195
13748
  return nodes.filter(
13196
- (n) => n.type === import_types51.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
13749
+ (n) => n.type === import_types52.NodeType.ServiceNode && Array.isArray(n.incompatibilities) && (n.incompatibilities ?? []).length > 0
13197
13750
  );
13198
13751
  }
13199
13752
  function servicesWithoutObserved(nodes, edges) {
13200
13753
  const seen = /* @__PURE__ */ new Set();
13201
13754
  for (const e of edges) {
13202
- if (e.provenance === import_types51.Provenance.OBSERVED) {
13755
+ if (e.provenance === import_types52.Provenance.OBSERVED) {
13203
13756
  seen.add(e.source);
13204
13757
  seen.add(e.target);
13205
13758
  }
13206
13759
  }
13207
13760
  return nodes.filter(
13208
- (n) => n.type === import_types51.NodeType.ServiceNode && !seen.has(n.id)
13761
+ (n) => n.type === import_types52.NodeType.ServiceNode && !seen.has(n.id)
13209
13762
  );
13210
13763
  }
13211
13764
  function formatDivergence(d) {
@@ -13280,26 +13833,26 @@ function formatIncompat(inc) {
13280
13833
  // src/watch.ts
13281
13834
  init_cjs_shims();
13282
13835
  var import_node_fs37 = __toESM(require("fs"), 1);
13283
- var import_node_path70 = __toESM(require("path"), 1);
13836
+ var import_node_path71 = __toESM(require("path"), 1);
13284
13837
  var import_chokidar = __toESM(require("chokidar"), 1);
13285
13838
 
13286
13839
  // src/api.ts
13287
13840
  init_cjs_shims();
13288
13841
  var import_fastify2 = __toESM(require("fastify"), 1);
13289
13842
  var import_cors = __toESM(require("@fastify/cors"), 1);
13290
- var import_types80 = require("@neat.is/types");
13843
+ var import_types81 = require("@neat.is/types");
13291
13844
 
13292
13845
  // src/extend/index.ts
13293
13846
  init_cjs_shims();
13294
13847
  var import_node_fs30 = require("fs");
13295
- var import_node_path63 = __toESM(require("path"), 1);
13848
+ var import_node_path64 = __toESM(require("path"), 1);
13296
13849
  var import_node_os2 = __toESM(require("os"), 1);
13297
13850
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
13298
13851
 
13299
13852
  // src/installers/package-manager.ts
13300
13853
  init_cjs_shims();
13301
13854
  var import_node_fs29 = require("fs");
13302
- var import_node_path62 = __toESM(require("path"), 1);
13855
+ var import_node_path63 = __toESM(require("path"), 1);
13303
13856
  var import_node_child_process = require("child_process");
13304
13857
  var LOCKFILE_PRIORITY = [
13305
13858
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -13321,22 +13874,22 @@ async function exists2(p) {
13321
13874
  }
13322
13875
  }
13323
13876
  async function detectPackageManager(serviceDir) {
13324
- let dir = import_node_path62.default.resolve(serviceDir);
13877
+ let dir = import_node_path63.default.resolve(serviceDir);
13325
13878
  const stops = /* @__PURE__ */ new Set();
13326
13879
  for (let i = 0; i < 64; i++) {
13327
13880
  if (stops.has(dir)) break;
13328
13881
  stops.add(dir);
13329
13882
  for (const candidate of LOCKFILE_PRIORITY) {
13330
- const lockPath = import_node_path62.default.join(dir, candidate.lockfile);
13883
+ const lockPath = import_node_path63.default.join(dir, candidate.lockfile);
13331
13884
  if (await exists2(lockPath)) {
13332
13885
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
13333
13886
  }
13334
13887
  }
13335
- const parent = import_node_path62.default.dirname(dir);
13888
+ const parent = import_node_path63.default.dirname(dir);
13336
13889
  if (parent === dir) break;
13337
13890
  dir = parent;
13338
13891
  }
13339
- return { pm: "npm", cwd: import_node_path62.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
13892
+ return { pm: "npm", cwd: import_node_path63.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
13340
13893
  }
13341
13894
  async function runPackageManagerInstall(cmd) {
13342
13895
  return new Promise((resolve) => {
@@ -13385,7 +13938,7 @@ async function fileExists2(p) {
13385
13938
  }
13386
13939
  }
13387
13940
  async function readPackageJson(scanPath) {
13388
- const pkgPath = import_node_path63.default.join(scanPath, "package.json");
13941
+ const pkgPath = import_node_path64.default.join(scanPath, "package.json");
13389
13942
  const raw = await import_node_fs30.promises.readFile(pkgPath, "utf8");
13390
13943
  return JSON.parse(raw);
13391
13944
  }
@@ -13399,27 +13952,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
13399
13952
  ]);
13400
13953
  async function findHookFiles(scanPath) {
13401
13954
  const found = [];
13402
- const walk8 = async (dir) => {
13955
+ const walk9 = async (dir) => {
13403
13956
  const entries = await import_node_fs30.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
13404
13957
  for (const entry2 of entries) {
13405
13958
  if (entry2.isDirectory()) {
13406
13959
  if (entry2.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry2.name)) continue;
13407
- await walk8(import_node_path63.default.join(dir, entry2.name));
13960
+ await walk9(import_node_path64.default.join(dir, entry2.name));
13408
13961
  } else if (entry2.isFile()) {
13409
13962
  if ((entry2.name.startsWith("instrumentation") || entry2.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry2.name)) {
13410
- const rel = import_node_path63.default.relative(scanPath, import_node_path63.default.join(dir, entry2.name));
13411
- found.push(rel.split(import_node_path63.default.sep).join("/"));
13963
+ const rel = import_node_path64.default.relative(scanPath, import_node_path64.default.join(dir, entry2.name));
13964
+ found.push(rel.split(import_node_path64.default.sep).join("/"));
13412
13965
  }
13413
13966
  }
13414
13967
  }
13415
13968
  };
13416
- await walk8(scanPath);
13969
+ await walk9(scanPath);
13417
13970
  return found.sort();
13418
13971
  }
13419
13972
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
13420
13973
  let fallback = null;
13421
13974
  for (const file of hookFiles) {
13422
- const content = await import_node_fs30.promises.readFile(import_node_path63.default.join(scanPath, file), "utf8");
13975
+ const content = await import_node_fs30.promises.readFile(import_node_path64.default.join(scanPath, file), "utf8");
13423
13976
  const patched = splicedContent(content, snippet2);
13424
13977
  if (patched !== null) return { file, content, patched };
13425
13978
  if (fallback === null) fallback = { file, content };
@@ -13427,11 +13980,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
13427
13980
  return { file: fallback.file, content: fallback.content, patched: null };
13428
13981
  }
13429
13982
  function extendLogPath() {
13430
- return process.env.NEAT_EXTEND_LOG ?? import_node_path63.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
13983
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path64.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
13431
13984
  }
13432
13985
  async function appendExtendLog(entry2) {
13433
13986
  const logPath = extendLogPath();
13434
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(logPath), { recursive: true });
13987
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(logPath), { recursive: true });
13435
13988
  await import_node_fs30.promises.appendFile(logPath, JSON.stringify(entry2) + "\n", "utf8");
13436
13989
  }
13437
13990
  function splicedContent(fileContent, snippet2) {
@@ -13490,7 +14043,7 @@ function lookupInstrumentation(library, installedVersion) {
13490
14043
  }
13491
14044
  async function describeProjectInstrumentation(ctx) {
13492
14045
  const hookFiles = await findHookFiles(ctx.scanPath);
13493
- const envNeat = await fileExists2(import_node_path63.default.join(ctx.scanPath, ".env.neat"));
14046
+ const envNeat = await fileExists2(import_node_path64.default.join(ctx.scanPath, ".env.neat"));
13494
14047
  const registryInstrPackages = new Set(
13495
14048
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
13496
14049
  );
@@ -13512,7 +14065,7 @@ async function applyExtension(ctx, args, options) {
13512
14065
  );
13513
14066
  }
13514
14067
  for (const file of hookFiles) {
13515
- const content = await import_node_fs30.promises.readFile(import_node_path63.default.join(ctx.scanPath, file), "utf8");
14068
+ const content = await import_node_fs30.promises.readFile(import_node_path64.default.join(ctx.scanPath, file), "utf8");
13516
14069
  if (content.includes(args.registration_snippet)) {
13517
14070
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
13518
14071
  }
@@ -13524,10 +14077,10 @@ async function applyExtension(ctx, args, options) {
13524
14077
  );
13525
14078
  }
13526
14079
  const primaryFile = primary.file;
13527
- const primaryPath = import_node_path63.default.join(ctx.scanPath, primaryFile);
14080
+ const primaryPath = import_node_path64.default.join(ctx.scanPath, primaryFile);
13528
14081
  const filesTouched = [];
13529
14082
  const depsAdded = [];
13530
- const pkgPath = import_node_path63.default.join(ctx.scanPath, "package.json");
14083
+ const pkgPath = import_node_path64.default.join(ctx.scanPath, "package.json");
13531
14084
  const pkg = await readPackageJson(ctx.scanPath);
13532
14085
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
13533
14086
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -13566,7 +14119,7 @@ async function dryRunExtension(ctx, args) {
13566
14119
  };
13567
14120
  }
13568
14121
  for (const file of hookFiles) {
13569
- const content = await import_node_fs30.promises.readFile(import_node_path63.default.join(ctx.scanPath, file), "utf8");
14122
+ const content = await import_node_fs30.promises.readFile(import_node_path64.default.join(ctx.scanPath, file), "utf8");
13570
14123
  if (content.includes(args.registration_snippet)) {
13571
14124
  return {
13572
14125
  library: args.library,
@@ -13607,7 +14160,7 @@ async function rollbackExtension(ctx, args) {
13607
14160
  if (!match) {
13608
14161
  return { undone: false, message: "no apply found for library" };
13609
14162
  }
13610
- const pkgPath = import_node_path63.default.join(ctx.scanPath, "package.json");
14163
+ const pkgPath = import_node_path64.default.join(ctx.scanPath, "package.json");
13611
14164
  if (await fileExists2(pkgPath)) {
13612
14165
  const pkg = await readPackageJson(ctx.scanPath);
13613
14166
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -13618,7 +14171,7 @@ async function rollbackExtension(ctx, args) {
13618
14171
  }
13619
14172
  const hookFiles = await findHookFiles(ctx.scanPath);
13620
14173
  for (const file of hookFiles) {
13621
- const filePath = import_node_path63.default.join(ctx.scanPath, file);
14174
+ const filePath = import_node_path64.default.join(ctx.scanPath, file);
13622
14175
  const content = await import_node_fs30.promises.readFile(filePath, "utf8");
13623
14176
  if (content.includes(match.registration_snippet)) {
13624
14177
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -13756,23 +14309,23 @@ function canonicalJson(value) {
13756
14309
 
13757
14310
  // src/projects.ts
13758
14311
  init_cjs_shims();
13759
- var import_node_path64 = __toESM(require("path"), 1);
14312
+ var import_node_path65 = __toESM(require("path"), 1);
13760
14313
  function pathsForProject(project, baseDir) {
13761
14314
  if (project === DEFAULT_PROJECT) {
13762
14315
  return {
13763
- snapshotPath: import_node_path64.default.join(baseDir, "graph.json"),
13764
- errorsPath: import_node_path64.default.join(baseDir, "errors.ndjson"),
13765
- staleEventsPath: import_node_path64.default.join(baseDir, "stale-events.ndjson"),
13766
- embeddingsCachePath: import_node_path64.default.join(baseDir, "embeddings.json"),
13767
- policyViolationsPath: import_node_path64.default.join(baseDir, "policy-violations.ndjson")
14316
+ snapshotPath: import_node_path65.default.join(baseDir, "graph.json"),
14317
+ errorsPath: import_node_path65.default.join(baseDir, "errors.ndjson"),
14318
+ staleEventsPath: import_node_path65.default.join(baseDir, "stale-events.ndjson"),
14319
+ embeddingsCachePath: import_node_path65.default.join(baseDir, "embeddings.json"),
14320
+ policyViolationsPath: import_node_path65.default.join(baseDir, "policy-violations.ndjson")
13768
14321
  };
13769
14322
  }
13770
14323
  return {
13771
- snapshotPath: import_node_path64.default.join(baseDir, `${project}.json`),
13772
- errorsPath: import_node_path64.default.join(baseDir, `errors.${project}.ndjson`),
13773
- staleEventsPath: import_node_path64.default.join(baseDir, `stale-events.${project}.ndjson`),
13774
- embeddingsCachePath: import_node_path64.default.join(baseDir, `embeddings.${project}.json`),
13775
- policyViolationsPath: import_node_path64.default.join(baseDir, `policy-violations.${project}.ndjson`)
14324
+ snapshotPath: import_node_path65.default.join(baseDir, `${project}.json`),
14325
+ errorsPath: import_node_path65.default.join(baseDir, `errors.${project}.ndjson`),
14326
+ staleEventsPath: import_node_path65.default.join(baseDir, `stale-events.${project}.ndjson`),
14327
+ embeddingsCachePath: import_node_path65.default.join(baseDir, `embeddings.${project}.json`),
14328
+ policyViolationsPath: import_node_path65.default.join(baseDir, `policy-violations.${project}.ndjson`)
13776
14329
  };
13777
14330
  }
13778
14331
  var Projects = class {
@@ -13810,26 +14363,26 @@ var Projects = class {
13810
14363
  init_cjs_shims();
13811
14364
  var import_node_fs32 = require("fs");
13812
14365
  var import_node_os3 = __toESM(require("os"), 1);
13813
- var import_node_path65 = __toESM(require("path"), 1);
13814
- var import_types52 = require("@neat.is/types");
14366
+ var import_node_path66 = __toESM(require("path"), 1);
14367
+ var import_types53 = require("@neat.is/types");
13815
14368
  var LOCK_TIMEOUT_MS = 5e3;
13816
14369
  var LOCK_RETRY_MS = 50;
13817
14370
  function neatHome() {
13818
14371
  const override = process.env.NEAT_HOME;
13819
- if (override && override.length > 0) return import_node_path65.default.resolve(override);
13820
- return import_node_path65.default.join(import_node_os3.default.homedir(), ".neat");
14372
+ if (override && override.length > 0) return import_node_path66.default.resolve(override);
14373
+ return import_node_path66.default.join(import_node_os3.default.homedir(), ".neat");
13821
14374
  }
13822
14375
  function registryPath() {
13823
- return import_node_path65.default.join(neatHome(), "projects.json");
14376
+ return import_node_path66.default.join(neatHome(), "projects.json");
13824
14377
  }
13825
14378
  function registryLockPath() {
13826
- return import_node_path65.default.join(neatHome(), "projects.json.lock");
14379
+ return import_node_path66.default.join(neatHome(), "projects.json.lock");
13827
14380
  }
13828
14381
  function daemonPidPath() {
13829
- return import_node_path65.default.join(neatHome(), "neatd.pid");
14382
+ return import_node_path66.default.join(neatHome(), "neatd.pid");
13830
14383
  }
13831
14384
  function daemonsDir() {
13832
- return import_node_path65.default.join(neatHome(), "daemons");
14385
+ return import_node_path66.default.join(neatHome(), "daemons");
13833
14386
  }
13834
14387
  function isFiniteInt(v) {
13835
14388
  return typeof v === "number" && Number.isFinite(v);
@@ -13870,7 +14423,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
13870
14423
  const out = [];
13871
14424
  for (const name of names) {
13872
14425
  if (!name.endsWith(".json")) continue;
13873
- const file = import_node_path65.default.join(dir, name);
14426
+ const file = import_node_path66.default.join(dir, name);
13874
14427
  let raw;
13875
14428
  try {
13876
14429
  raw = await import_node_fs32.promises.readFile(file, "utf8");
@@ -13991,7 +14544,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
13991
14544
  }
13992
14545
  }
13993
14546
  async function normalizeProjectPath(input) {
13994
- const resolved = import_node_path65.default.resolve(input);
14547
+ const resolved = import_node_path66.default.resolve(input);
13995
14548
  try {
13996
14549
  return await import_node_fs32.promises.realpath(resolved);
13997
14550
  } catch {
@@ -13999,7 +14552,7 @@ async function normalizeProjectPath(input) {
13999
14552
  }
14000
14553
  }
14001
14554
  async function writeAtomically(target, contents) {
14002
- await import_node_fs32.promises.mkdir(import_node_path65.default.dirname(target), { recursive: true });
14555
+ await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(target), { recursive: true });
14003
14556
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
14004
14557
  const fd = await import_node_fs32.promises.open(tmp, "w");
14005
14558
  try {
@@ -14012,7 +14565,7 @@ async function writeAtomically(target, contents) {
14012
14565
  }
14013
14566
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
14014
14567
  const deadline = Date.now() + timeoutMs;
14015
- await import_node_fs32.promises.mkdir(import_node_path65.default.dirname(lockPath), { recursive: true });
14568
+ await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(lockPath), { recursive: true });
14016
14569
  let probedHolder = false;
14017
14570
  while (true) {
14018
14571
  try {
@@ -14065,10 +14618,10 @@ async function readRegistry() {
14065
14618
  throw err;
14066
14619
  }
14067
14620
  const parsed = JSON.parse(raw);
14068
- return import_types52.RegistryFileSchema.parse(parsed);
14621
+ return import_types53.RegistryFileSchema.parse(parsed);
14069
14622
  }
14070
14623
  async function writeRegistry(reg) {
14071
- const validated = import_types52.RegistryFileSchema.parse(reg);
14624
+ const validated = import_types53.RegistryFileSchema.parse(reg);
14072
14625
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
14073
14626
  }
14074
14627
  var ProjectNameCollisionError = class extends Error {
@@ -14254,7 +14807,7 @@ init_auth();
14254
14807
  // src/connectors-config.ts
14255
14808
  init_cjs_shims();
14256
14809
  var import_node_os4 = __toESM(require("os"), 1);
14257
- var import_node_path66 = __toESM(require("path"), 1);
14810
+ var import_node_path67 = __toESM(require("path"), 1);
14258
14811
  var import_node_fs33 = require("fs");
14259
14812
  var CONNECTORS_CONFIG_VERSION = 1;
14260
14813
  var EnvRefUnsetError = class extends Error {
@@ -14269,11 +14822,11 @@ var EnvRefUnsetError = class extends Error {
14269
14822
  };
14270
14823
  function neatHome2() {
14271
14824
  const override = process.env.NEAT_HOME;
14272
- if (override && override.length > 0) return import_node_path66.default.resolve(override);
14273
- return import_node_path66.default.join(import_node_os4.default.homedir(), ".neat");
14825
+ if (override && override.length > 0) return import_node_path67.default.resolve(override);
14826
+ return import_node_path67.default.join(import_node_os4.default.homedir(), ".neat");
14274
14827
  }
14275
14828
  function connectorsConfigPath(home = neatHome2()) {
14276
- return import_node_path66.default.join(home, "connectors.json");
14829
+ return import_node_path67.default.join(home, "connectors.json");
14277
14830
  }
14278
14831
  var MODE_MASK_LOOSER_THAN_0600 = 63;
14279
14832
  async function warnIfModeLooserThan0600(file) {
@@ -14404,7 +14957,7 @@ function connectorMatchesProject(entry2, project) {
14404
14957
  var CONNECTORS_LOCK_TIMEOUT_MS = 5e3;
14405
14958
  var CONNECTORS_LOCK_RETRY_MS = 50;
14406
14959
  function connectorsConfigLockPath(home = neatHome2()) {
14407
- return import_node_path66.default.join(home, "connectors.json.lock");
14960
+ return import_node_path67.default.join(home, "connectors.json.lock");
14408
14961
  }
14409
14962
  function isEnvRef(value) {
14410
14963
  return value.length > 1 && value.startsWith("$");
@@ -14417,7 +14970,7 @@ function redactCredentialRef(ref) {
14417
14970
  return out;
14418
14971
  }
14419
14972
  async function writeConfigAtomically0600(file, contents) {
14420
- await import_node_fs33.promises.mkdir(import_node_path66.default.dirname(file), { recursive: true });
14973
+ await import_node_fs33.promises.mkdir(import_node_path67.default.dirname(file), { recursive: true });
14421
14974
  const tmp = `${file}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
14422
14975
  const fd = await import_node_fs33.promises.open(tmp, "w", 384);
14423
14976
  try {
@@ -14431,7 +14984,7 @@ async function writeConfigAtomically0600(file, contents) {
14431
14984
  }
14432
14985
  async function acquireConnectorsLock(lockPath, timeoutMs = CONNECTORS_LOCK_TIMEOUT_MS) {
14433
14986
  const deadline = Date.now() + timeoutMs;
14434
- await import_node_fs33.promises.mkdir(import_node_path66.default.dirname(lockPath), { recursive: true });
14987
+ await import_node_fs33.promises.mkdir(import_node_path67.default.dirname(lockPath), { recursive: true });
14435
14988
  for (; ; ) {
14436
14989
  try {
14437
14990
  const fd = await import_node_fs33.promises.open(lockPath, "wx");
@@ -14581,15 +15134,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
14581
15134
 
14582
15135
  // src/connectors/index.ts
14583
15136
  init_cjs_shims();
14584
- var import_types53 = require("@neat.is/types");
15137
+ var import_types54 = require("@neat.is/types");
14585
15138
  var NO_ENV = "unknown";
14586
15139
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
14587
15140
  if (!graph.hasNode(targetNodeId)) return void 0;
14588
15141
  const sites = [];
14589
15142
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
14590
15143
  const edge = graph.getEdgeAttributes(edgeId);
14591
- if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
14592
- const parsed = (0, import_types53.parseFileId)(edge.source);
15144
+ if (edge.provenance !== import_types54.Provenance.EXTRACTED) continue;
15145
+ const parsed = (0, import_types54.parseFileId)(edge.source);
14593
15146
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
14594
15147
  const site = { relPath: edge.evidence.file };
14595
15148
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -14600,7 +15153,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
14600
15153
  function routeCallSiteFor(graph, targetNodeId) {
14601
15154
  if (!graph.hasNode(targetNodeId)) return void 0;
14602
15155
  const attrs = graph.getNodeAttributes(targetNodeId);
14603
- if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
15156
+ if (attrs.type !== import_types54.NodeType.RouteNode || !attrs.path) return void 0;
14604
15157
  const site = { relPath: attrs.path };
14605
15158
  if (attrs.line !== void 0) site.line = attrs.line;
14606
15159
  return site;
@@ -15081,10 +15634,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
15081
15634
  // src/connectors/supabase/map.ts
15082
15635
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
15083
15636
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
15084
- function targetFromRestPath(path81) {
15085
- const rpcMatch = REST_RPC_PATH_RE.exec(path81);
15637
+ function targetFromRestPath(path82) {
15638
+ const rpcMatch = REST_RPC_PATH_RE.exec(path82);
15086
15639
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
15087
- const tableMatch = REST_TABLE_PATH_RE.exec(path81);
15640
+ const tableMatch = REST_TABLE_PATH_RE.exec(path82);
15088
15641
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
15089
15642
  return null;
15090
15643
  }
@@ -15195,23 +15748,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
15195
15748
 
15196
15749
  // src/connectors/supabase/resolve.ts
15197
15750
  init_cjs_shims();
15198
- var import_types55 = require("@neat.is/types");
15751
+ var import_types56 = require("@neat.is/types");
15199
15752
  function createSupabaseResolveTarget(graph, config) {
15200
15753
  return (signal, _ctx) => {
15201
15754
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
15202
15755
  return null;
15203
15756
  }
15204
- const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15757
+ const subResourceId = (0, import_types56.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15205
15758
  if (graph.hasNode(subResourceId)) {
15206
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
15759
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types56.EdgeType.CALLS };
15207
15760
  }
15208
- const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
15761
+ const bareResourceId = (0, import_types56.infraId)(signal.targetKind, signal.targetName);
15209
15762
  if (graph.hasNode(bareResourceId)) {
15210
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
15763
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types56.EdgeType.CALLS };
15211
15764
  }
15212
- const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
15765
+ const projectLevelId = (0, import_types56.infraId)("supabase", config.nodeRef);
15213
15766
  if (graph.hasNode(projectLevelId)) {
15214
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
15767
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types56.EdgeType.CALLS };
15215
15768
  }
15216
15769
  return null;
15217
15770
  };
@@ -15304,7 +15857,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
15304
15857
 
15305
15858
  // src/connectors/railway/index.ts
15306
15859
  init_cjs_shims();
15307
- var import_types59 = require("@neat.is/types");
15860
+ var import_types60 = require("@neat.is/types");
15308
15861
 
15309
15862
  // src/connectors/railway/client.ts
15310
15863
  init_cjs_shims();
@@ -15455,7 +16008,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
15455
16008
  const out = [];
15456
16009
  graph.forEachNode((_id, attrs) => {
15457
16010
  const node = attrs;
15458
- if (node.type !== import_types59.NodeType.RouteNode) return;
16011
+ if (node.type !== import_types60.NodeType.RouteNode) return;
15459
16012
  const route = attrs;
15460
16013
  if (route.service !== serviceName) return;
15461
16014
  out.push({
@@ -15559,12 +16112,12 @@ function createRailwayResolveTarget(config) {
15559
16112
  const serviceName = config.serviceNameById[config.serviceId];
15560
16113
  if (!serviceName) return null;
15561
16114
  if (signal.targetKind === ROUTE_TARGET_KIND) {
15562
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
16115
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types60.EdgeType.CALLS };
15563
16116
  }
15564
16117
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
15565
16118
  const peerName = config.serviceNameById[signal.targetName];
15566
16119
  if (!peerName) return null;
15567
- return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
16120
+ return { targetNodeId: (0, import_types60.serviceId)(peerName), serviceName, edgeType: import_types60.EdgeType.CONNECTS_TO };
15568
16121
  }
15569
16122
  return null;
15570
16123
  };
@@ -15688,9 +16241,9 @@ function parseFirebaseTargetName(targetName) {
15688
16241
  const secondSep = rest.indexOf(FIELD_SEP);
15689
16242
  if (secondSep === -1) return null;
15690
16243
  const method = rest.slice(0, secondSep);
15691
- const path81 = rest.slice(secondSep + 1);
15692
- if (!resourceName || !method || !path81) return null;
15693
- return { resourceName, method, path: path81 };
16244
+ const path82 = rest.slice(secondSep + 1);
16245
+ if (!resourceName || !method || !path82) return null;
16246
+ return { resourceName, method, path: path82 };
15694
16247
  }
15695
16248
  function resourceNameFor(type, labels) {
15696
16249
  if (!labels) return null;
@@ -15728,14 +16281,14 @@ function mapLogEntryToSignal(entry2) {
15728
16281
  if (!req) return null;
15729
16282
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
15730
16283
  const method = req.requestMethod.toUpperCase();
15731
- const path81 = pathFromRequestUrl(req.requestUrl);
15732
- if (path81 === null) return null;
16284
+ const path82 = pathFromRequestUrl(req.requestUrl);
16285
+ if (path82 === null) return null;
15733
16286
  const timestamp = entry2.timestamp;
15734
16287
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15735
16288
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
15736
16289
  return {
15737
16290
  targetKind: resourceType,
15738
- targetName: packFirebaseTargetName({ resourceName, method, path: path81 }),
16291
+ targetName: packFirebaseTargetName({ resourceName, method, path: path82 }),
15739
16292
  callCount: 1,
15740
16293
  errorCount: isError ? 1 : 0,
15741
16294
  lastObservedIso: timestamp
@@ -15752,7 +16305,7 @@ function mapLogEntriesToSignals(entries) {
15752
16305
 
15753
16306
  // src/connectors/firebase/resolve.ts
15754
16307
  init_cjs_shims();
15755
- var import_types60 = require("@neat.is/types");
16308
+ var import_types61 = require("@neat.is/types");
15756
16309
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
15757
16310
  switch (resourceType) {
15758
16311
  case "cloud_function":
@@ -15767,7 +16320,7 @@ function routeEntriesFor(graph, serviceName) {
15767
16320
  const entries = [];
15768
16321
  graph.forEachNode((_id, attrs) => {
15769
16322
  const node = attrs;
15770
- if (node.type !== import_types60.NodeType.RouteNode) return;
16323
+ if (node.type !== import_types61.NodeType.RouteNode) return;
15771
16324
  const route = attrs;
15772
16325
  if (route.service !== serviceName) return;
15773
16326
  entries.push({
@@ -15799,7 +16352,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
15799
16352
  return {
15800
16353
  targetNodeId: match.routeNodeId,
15801
16354
  serviceName,
15802
- edgeType: import_types60.EdgeType.CALLS
16355
+ edgeType: import_types61.EdgeType.CALLS
15803
16356
  };
15804
16357
  };
15805
16358
  }
@@ -15826,7 +16379,7 @@ init_cjs_shims();
15826
16379
 
15827
16380
  // src/connectors/cloudflare/connector.ts
15828
16381
  init_cjs_shims();
15829
- var import_types62 = require("@neat.is/types");
16382
+ var import_types63 = require("@neat.is/types");
15830
16383
 
15831
16384
  // src/connectors/cloudflare/client.ts
15832
16385
  init_cjs_shims();
@@ -15942,7 +16495,7 @@ function mapEventToSignal(event) {
15942
16495
  if (Number.isNaN(observedAt.getTime())) return null;
15943
16496
  const statusCode = metadata?.statusCode;
15944
16497
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
15945
- const path81 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
16498
+ const path82 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15946
16499
  return {
15947
16500
  targetKind: CLOUDFLARE_TARGET_KIND,
15948
16501
  targetName: scriptName,
@@ -15950,7 +16503,7 @@ function mapEventToSignal(event) {
15950
16503
  errorCount: isError ? 1 : 0,
15951
16504
  lastObservedIso: observedAt.toISOString(),
15952
16505
  method,
15953
- ...path81 ? { path: path81 } : {},
16506
+ ...path82 ? { path: path82 } : {},
15954
16507
  ...typeof statusCode === "number" ? { statusCode } : {},
15955
16508
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
15956
16509
  };
@@ -15990,19 +16543,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
15990
16543
  graph.forEachNode((id, attrs) => {
15991
16544
  if (found) return;
15992
16545
  const a = attrs;
15993
- if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
16546
+ if (a.type === import_types63.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15994
16547
  found = id;
15995
16548
  }
15996
16549
  });
15997
16550
  return found;
15998
16551
  }
15999
- function findMatchingRouteNode(graph, serviceName, method, path81) {
16000
- const normalizedPath = normalizePathTemplate(path81);
16552
+ function findMatchingRouteNode(graph, serviceName, method, path82) {
16553
+ const normalizedPath = normalizePathTemplate(path82);
16001
16554
  let found = null;
16002
16555
  graph.forEachNode((id, attrs) => {
16003
16556
  if (found) return;
16004
16557
  const a = attrs;
16005
- if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
16558
+ if (a.type !== import_types63.NodeType.RouteNode || a.service !== serviceName) return;
16006
16559
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
16007
16560
  const routeMethod = (a.method ?? "").toUpperCase();
16008
16561
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -16014,18 +16567,18 @@ function createCloudflareResolveTarget(config, graph) {
16014
16567
  return (signal) => {
16015
16568
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
16016
16569
  const scriptName = signal.targetName;
16017
- const { method, path: path81 } = signal;
16570
+ const { method, path: path82 } = signal;
16018
16571
  const resolveRouteGrain = (serviceName, wholeFileId) => {
16019
- if (!method || !path81) return wholeFileId;
16020
- return findMatchingRouteNode(graph, serviceName, method, path81) ?? wholeFileId;
16572
+ if (!method || !path82) return wholeFileId;
16573
+ return findMatchingRouteNode(graph, serviceName, method, path82) ?? wholeFileId;
16021
16574
  };
16022
16575
  const mapping = config.workers?.[scriptName];
16023
16576
  if (mapping) {
16024
- const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
16577
+ const wholeFileId = (0, import_types63.fileId)(mapping.service, mapping.entryFile);
16025
16578
  return {
16026
16579
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
16027
16580
  serviceName: mapping.service,
16028
- edgeType: import_types62.EdgeType.CALLS
16581
+ edgeType: import_types63.EdgeType.CALLS
16029
16582
  };
16030
16583
  }
16031
16584
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -16034,13 +16587,13 @@ function createCloudflareResolveTarget(config, graph) {
16034
16587
  return {
16035
16588
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
16036
16589
  serviceName: fileNode.service,
16037
- edgeType: import_types62.EdgeType.CALLS
16590
+ edgeType: import_types63.EdgeType.CALLS
16038
16591
  };
16039
16592
  }
16040
16593
  return {
16041
- targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
16594
+ targetNodeId: (0, import_types63.infraId)("cloudflare-worker", scriptName),
16042
16595
  serviceName: scriptName,
16043
- edgeType: import_types62.EdgeType.CALLS,
16596
+ edgeType: import_types63.EdgeType.CALLS,
16044
16597
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
16045
16598
  };
16046
16599
  };
@@ -16236,14 +16789,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
16236
16789
 
16237
16790
  // src/connectors/neon/resolve.ts
16238
16791
  init_cjs_shims();
16239
- var import_types66 = require("@neat.is/types");
16792
+ var import_types67 = require("@neat.is/types");
16240
16793
  function createNeonResolveTarget(config) {
16241
16794
  return (signal) => {
16242
16795
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16243
16796
  return {
16244
- targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
16797
+ targetNodeId: (0, import_types67.infraId)("sql-table", signal.targetName),
16245
16798
  serviceName: config.serviceName,
16246
- edgeType: import_types66.EdgeType.CALLS,
16799
+ edgeType: import_types67.EdgeType.CALLS,
16247
16800
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
16248
16801
  };
16249
16802
  };
@@ -16369,9 +16922,9 @@ function parseCloudRunTargetName(targetName) {
16369
16922
  const secondSep = rest.indexOf(FIELD_SEP2);
16370
16923
  if (secondSep === -1) return null;
16371
16924
  const method = rest.slice(0, secondSep);
16372
- const path81 = rest.slice(secondSep + 1);
16373
- if (!serviceName || !method || !path81) return null;
16374
- return { serviceName, method, path: path81 };
16925
+ const path82 = rest.slice(secondSep + 1);
16926
+ if (!serviceName || !method || !path82) return null;
16927
+ return { serviceName, method, path: path82 };
16375
16928
  }
16376
16929
 
16377
16930
  // src/connectors/cloud-run/map.ts
@@ -16400,14 +16953,14 @@ function mapLogEntryToSignal2(entry2) {
16400
16953
  if (!req) return null;
16401
16954
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
16402
16955
  const method = req.requestMethod.toUpperCase();
16403
- const path81 = pathFromRequestUrl2(req.requestUrl);
16404
- if (path81 === null) return null;
16956
+ const path82 = pathFromRequestUrl2(req.requestUrl);
16957
+ if (path82 === null) return null;
16405
16958
  const timestamp = entry2.timestamp;
16406
16959
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
16407
16960
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
16408
16961
  return {
16409
16962
  targetKind: CLOUD_RUN_TARGET_KIND,
16410
- targetName: packCloudRunTargetName({ serviceName, method, path: path81 }),
16963
+ targetName: packCloudRunTargetName({ serviceName, method, path: path82 }),
16411
16964
  callCount: 1,
16412
16965
  errorCount: isError ? 1 : 0,
16413
16966
  lastObservedIso: timestamp
@@ -16424,14 +16977,14 @@ function mapLogEntriesToSignals2(entries) {
16424
16977
 
16425
16978
  // src/connectors/cloud-run/resolve.ts
16426
16979
  init_cjs_shims();
16427
- var import_types70 = require("@neat.is/types");
16980
+ var import_types71 = require("@neat.is/types");
16428
16981
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
16429
16982
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
16430
16983
  let found = null;
16431
16984
  graph.forEachNode((_id, attrs) => {
16432
16985
  if (found) return;
16433
16986
  const node = attrs;
16434
- if (node.type !== import_types70.NodeType.RouteNode) return;
16987
+ if (node.type !== import_types71.NodeType.RouteNode) return;
16435
16988
  const route = attrs;
16436
16989
  if (route.service !== serviceName || !route.pathTemplate) return;
16437
16990
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -16446,23 +16999,23 @@ function createCloudRunResolveTarget(graph, config) {
16446
16999
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
16447
17000
  const identity = parseCloudRunTargetName(signal.targetName);
16448
17001
  if (!identity) return null;
16449
- const { serviceName: gcpServiceName, method, path: path81 } = identity;
17002
+ const { serviceName: gcpServiceName, method, path: path82 } = identity;
16450
17003
  const mappedService = config.serviceMap?.[gcpServiceName];
16451
17004
  if (mappedService) {
16452
17005
  const routeNodeId = findMatchingRouteNode2(
16453
17006
  graph,
16454
17007
  mappedService,
16455
17008
  method,
16456
- normalizePathTemplate(path81)
17009
+ normalizePathTemplate(path82)
16457
17010
  );
16458
17011
  if (routeNodeId) {
16459
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
17012
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types71.EdgeType.CALLS };
16460
17013
  }
16461
17014
  }
16462
17015
  return {
16463
- targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
17016
+ targetNodeId: (0, import_types71.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16464
17017
  serviceName: mappedService ?? gcpServiceName,
16465
- edgeType: import_types70.EdgeType.CALLS,
17018
+ edgeType: import_types71.EdgeType.CALLS,
16466
17019
  ensureInfraNode: {
16467
17020
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
16468
17021
  name: gcpServiceName,
@@ -16503,7 +17056,7 @@ function createCloudRunConnector(graph, config = {}) {
16503
17056
 
16504
17057
  // src/connectors/render/index.ts
16505
17058
  init_cjs_shims();
16506
- var import_types73 = require("@neat.is/types");
17059
+ var import_types74 = require("@neat.is/types");
16507
17060
 
16508
17061
  // src/connectors/render/types.ts
16509
17062
  init_cjs_shims();
@@ -16581,7 +17134,7 @@ function buildRenderRouteIndex(graph, serviceName) {
16581
17134
  const out = [];
16582
17135
  graph.forEachNode((_id, attrs) => {
16583
17136
  const node = attrs;
16584
- if (node.type !== import_types73.NodeType.RouteNode) return;
17137
+ if (node.type !== import_types74.NodeType.RouteNode) return;
16585
17138
  const route = attrs;
16586
17139
  if (route.service !== serviceName) return;
16587
17140
  out.push({
@@ -16666,7 +17219,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
16666
17219
  function createRenderResolveTarget(config) {
16667
17220
  return (signal) => {
16668
17221
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
16669
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
17222
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types74.EdgeType.CALLS };
16670
17223
  }
16671
17224
  return null;
16672
17225
  };
@@ -16804,21 +17357,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
16804
17357
 
16805
17358
  // src/connectors/planetscale/resolve.ts
16806
17359
  init_cjs_shims();
16807
- var import_types77 = require("@neat.is/types");
17360
+ var import_types78 = require("@neat.is/types");
16808
17361
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
16809
17362
  function createPlanetscaleResolveTarget(graph, config) {
16810
17363
  const databaseName = `${config.organization}/${config.database}`;
16811
17364
  return (signal, _ctx) => {
16812
17365
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16813
- const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
17366
+ const tableId = (0, import_types78.infraId)("sql-table", signal.targetName);
16814
17367
  if (graph.hasNode(tableId)) {
16815
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
17368
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types78.EdgeType.CALLS };
16816
17369
  }
16817
- const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
17370
+ const providerId = (0, import_types78.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16818
17371
  return {
16819
17372
  targetNodeId: providerId,
16820
17373
  serviceName: config.serviceName,
16821
- edgeType: import_types77.EdgeType.CALLS,
17374
+ edgeType: import_types78.EdgeType.CALLS,
16822
17375
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
16823
17376
  };
16824
17377
  };
@@ -17567,11 +18120,11 @@ function registerRoutes(scope, ctx) {
17567
18120
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
17568
18121
  const parsed = [];
17569
18122
  for (const c of candidates) {
17570
- const r = import_types80.DivergenceTypeSchema.safeParse(c);
18123
+ const r = import_types81.DivergenceTypeSchema.safeParse(c);
17571
18124
  if (!r.success) {
17572
18125
  return reply.code(400).send({
17573
18126
  error: `unknown divergence type "${c}"`,
17574
- allowed: import_types80.DivergenceTypeSchema.options
18127
+ allowed: import_types81.DivergenceTypeSchema.options
17575
18128
  });
17576
18129
  }
17577
18130
  parsed.push(r.data);
@@ -17880,7 +18433,7 @@ function registerRoutes(scope, ctx) {
17880
18433
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
17881
18434
  let violations = await log.readAll();
17882
18435
  if (req.query.severity) {
17883
- const sev = import_types80.PolicySeveritySchema.safeParse(req.query.severity);
18436
+ const sev = import_types81.PolicySeveritySchema.safeParse(req.query.severity);
17884
18437
  if (!sev.success) {
17885
18438
  return reply.code(400).send({
17886
18439
  error: "invalid severity",
@@ -17919,7 +18472,7 @@ function registerRoutes(scope, ctx) {
17919
18472
  scope.post("/policies/check", async (req, reply) => {
17920
18473
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
17921
18474
  if (!proj) return;
17922
- const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req.body ?? {});
18475
+ const parsed = import_types81.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17923
18476
  if (!parsed.success) {
17924
18477
  return reply.code(400).send({
17925
18478
  error: "invalid /policies/check body",
@@ -18241,7 +18794,7 @@ init_otel();
18241
18794
  // src/daemon.ts
18242
18795
  init_cjs_shims();
18243
18796
  var import_node_fs35 = require("fs");
18244
- var import_node_path68 = __toESM(require("path"), 1);
18797
+ var import_node_path69 = __toESM(require("path"), 1);
18245
18798
  var import_node_module = require("module");
18246
18799
  init_otel();
18247
18800
  init_auth();
@@ -18249,28 +18802,28 @@ init_auth();
18249
18802
  // src/unrouted.ts
18250
18803
  init_cjs_shims();
18251
18804
  var import_node_fs34 = require("fs");
18252
- var import_node_path67 = __toESM(require("path"), 1);
18805
+ var import_node_path68 = __toESM(require("path"), 1);
18253
18806
 
18254
18807
  // src/daemon.ts
18255
- var import_types81 = require("@neat.is/types");
18808
+ var import_types82 = require("@neat.is/types");
18256
18809
  function daemonJsonPath(scanPath) {
18257
- return import_node_path68.default.join(scanPath, "neat-out", "daemon.json");
18810
+ return import_node_path69.default.join(scanPath, "neat-out", "daemon.json");
18258
18811
  }
18259
18812
  function daemonsDiscoveryDir(home) {
18260
18813
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
18261
- return import_node_path68.default.join(base, "daemons");
18814
+ return import_node_path69.default.join(base, "daemons");
18262
18815
  }
18263
18816
  function daemonDiscoveryPath(project, home) {
18264
- return import_node_path68.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
18817
+ return import_node_path69.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
18265
18818
  }
18266
18819
  function sanitizeDiscoveryName(project) {
18267
18820
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
18268
18821
  }
18269
18822
  function neatHomeFromEnv() {
18270
18823
  const env = process.env.NEAT_HOME;
18271
- if (env && env.length > 0) return import_node_path68.default.resolve(env);
18824
+ if (env && env.length > 0) return import_node_path69.default.resolve(env);
18272
18825
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
18273
- return import_node_path68.default.join(home, ".neat");
18826
+ return import_node_path69.default.join(home, ".neat");
18274
18827
  }
18275
18828
  async function readDaemonRecord(scanPath) {
18276
18829
  try {
@@ -18341,7 +18894,7 @@ init_otel_grpc();
18341
18894
  // src/search.ts
18342
18895
  init_cjs_shims();
18343
18896
  var import_node_fs36 = require("fs");
18344
- var import_node_path69 = __toESM(require("path"), 1);
18897
+ var import_node_path70 = __toESM(require("path"), 1);
18345
18898
  var import_node_crypto4 = require("crypto");
18346
18899
  var DEFAULT_LIMIT = 10;
18347
18900
  var NOMIC_DIM = 768;
@@ -18504,7 +19057,7 @@ async function readCache(cachePath) {
18504
19057
  }
18505
19058
  }
18506
19059
  async function writeCache(cachePath, cache) {
18507
- await import_node_fs36.promises.mkdir(import_node_path69.default.dirname(cachePath), { recursive: true });
19060
+ await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(cachePath), { recursive: true });
18508
19061
  await import_node_fs36.promises.writeFile(cachePath, JSON.stringify(cache));
18509
19062
  }
18510
19063
  var VectorIndex = class {
@@ -18664,8 +19217,8 @@ var ALL_PHASES = [
18664
19217
  ];
18665
19218
  function classifyChange(relPath) {
18666
19219
  const phases = /* @__PURE__ */ new Set();
18667
- const base = import_node_path70.default.basename(relPath).toLowerCase();
18668
- const segments = relPath.split(import_node_path70.default.sep).map((s) => s.toLowerCase());
19220
+ const base = import_node_path71.default.basename(relPath).toLowerCase();
19221
+ const segments = relPath.split(import_node_path71.default.sep).map((s) => s.toLowerCase());
18669
19222
  if (base === "package.json" || base === "requirements.txt" || base === "pyproject.toml" || base === "setup.py") {
18670
19223
  phases.add("services");
18671
19224
  phases.add("aliases");
@@ -18805,9 +19358,9 @@ function countWatchableDirs(scanPath, limit) {
18805
19358
  for (const e of entries) {
18806
19359
  if (count >= limit) return;
18807
19360
  if (!e.isDirectory()) continue;
18808
- if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path70.default.join(dir, e.name) + import_node_path70.default.sep))) continue;
19361
+ if (IGNORED_WATCH_PATHS.some((re) => re.test(import_node_path71.default.join(dir, e.name) + import_node_path71.default.sep))) continue;
18809
19362
  count++;
18810
- if (depth < 2) visit(import_node_path70.default.join(dir, e.name), depth + 1);
19363
+ if (depth < 2) visit(import_node_path71.default.join(dir, e.name), depth + 1);
18811
19364
  }
18812
19365
  };
18813
19366
  visit(scanPath, 0);
@@ -18825,8 +19378,8 @@ async function startWatch(graph, opts) {
18825
19378
  const projectName = opts.project ?? DEFAULT_PROJECT;
18826
19379
  await loadGraphFromDisk(graph, opts.outPath);
18827
19380
  const detachEventBus = attachGraphToEventBus(graph, { project: projectName });
18828
- const policyFilePath = import_node_path70.default.join(opts.scanPath, "policy.json");
18829
- const policyViolationsPath = import_node_path70.default.join(import_node_path70.default.dirname(opts.outPath), "policy-violations.ndjson");
19381
+ const policyFilePath = import_node_path71.default.join(opts.scanPath, "policy.json");
19382
+ const policyViolationsPath = import_node_path71.default.join(import_node_path71.default.dirname(opts.outPath), "policy-violations.ndjson");
18830
19383
  let policies = [];
18831
19384
  try {
18832
19385
  policies = await loadPolicyFile(policyFilePath);
@@ -18886,7 +19439,7 @@ async function startWatch(graph, opts) {
18886
19439
  assertBindAuthority(host, auth.authToken);
18887
19440
  const port = opts.port ?? 8080;
18888
19441
  const otelPort = opts.otelPort ?? 4318;
18889
- const cachePath = opts.embeddingsCachePath ?? import_node_path70.default.join(import_node_path70.default.dirname(opts.outPath), "embeddings.json");
19442
+ const cachePath = opts.embeddingsCachePath ?? import_node_path71.default.join(import_node_path71.default.dirname(opts.outPath), "embeddings.json");
18890
19443
  let searchIndex;
18891
19444
  try {
18892
19445
  searchIndex = await buildSearchIndex(graph, { cachePath });
@@ -18904,7 +19457,7 @@ async function startWatch(graph, opts) {
18904
19457
  // Paths are derived from the explicit options the watch caller passes
18905
19458
  // — pathsForProject is only used to fill in the embeddings/snapshot
18906
19459
  // fields so the registry shape is complete.
18907
- ...pathsForProject(projectName, import_node_path70.default.dirname(opts.outPath)),
19460
+ ...pathsForProject(projectName, import_node_path71.default.dirname(opts.outPath)),
18908
19461
  snapshotPath: opts.outPath,
18909
19462
  errorsPath: opts.errorsPath,
18910
19463
  staleEventsPath: opts.staleEventsPath
@@ -19022,9 +19575,9 @@ async function startWatch(graph, opts) {
19022
19575
  };
19023
19576
  const onPath = (absPath) => {
19024
19577
  if (shouldIgnore(absPath)) return;
19025
- const rel = import_node_path70.default.relative(opts.scanPath, absPath);
19578
+ const rel = import_node_path71.default.relative(opts.scanPath, absPath);
19026
19579
  if (!rel || rel.startsWith("..")) return;
19027
- pendingPaths.add(rel.split(import_node_path70.default.sep).join("/"));
19580
+ pendingPaths.add(rel.split(import_node_path71.default.sep).join("/"));
19028
19581
  const phases = classifyChange(rel);
19029
19582
  if (phases.size === 0) {
19030
19583
  for (const p of ALL_PHASES) pending.add(p);
@@ -19082,7 +19635,7 @@ async function startWatch(graph, opts) {
19082
19635
  // src/deploy/detect.ts
19083
19636
  init_cjs_shims();
19084
19637
  var import_node_fs38 = require("fs");
19085
- var import_node_path71 = __toESM(require("path"), 1);
19638
+ var import_node_path72 = __toESM(require("path"), 1);
19086
19639
  var import_node_child_process2 = require("child_process");
19087
19640
  var import_node_crypto5 = require("crypto");
19088
19641
  function generateToken() {
@@ -19182,7 +19735,7 @@ async function runDeploy(opts = {}) {
19182
19735
  const token = generateToken();
19183
19736
  switch (substrate) {
19184
19737
  case "docker-compose": {
19185
- const artifactPath = import_node_path71.default.join(cwd, "docker-compose.neat.yml");
19738
+ const artifactPath = import_node_path72.default.join(cwd, "docker-compose.neat.yml");
19186
19739
  const contents = emitDockerCompose(cwd);
19187
19740
  await import_node_fs38.promises.writeFile(artifactPath, contents, "utf8");
19188
19741
  return {
@@ -19190,11 +19743,11 @@ async function runDeploy(opts = {}) {
19190
19743
  artifactPath,
19191
19744
  token,
19192
19745
  contents,
19193
- startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path71.default.basename(artifactPath)} up -d`
19746
+ startCommand: `NEAT_AUTH_TOKEN=${token} docker compose -f ${import_node_path72.default.basename(artifactPath)} up -d`
19194
19747
  };
19195
19748
  }
19196
19749
  case "systemd": {
19197
- const artifactPath = import_node_path71.default.join(cwd, "neat.service");
19750
+ const artifactPath = import_node_path72.default.join(cwd, "neat.service");
19198
19751
  const contents = emitSystemdUnit(cwd);
19199
19752
  await import_node_fs38.promises.writeFile(artifactPath, contents, "utf8");
19200
19753
  return {
@@ -19230,7 +19783,7 @@ init_cjs_shims();
19230
19783
  // src/installers/javascript.ts
19231
19784
  init_cjs_shims();
19232
19785
  var import_node_fs39 = require("fs");
19233
- var import_node_path72 = __toESM(require("path"), 1);
19786
+ var import_node_path73 = __toESM(require("path"), 1);
19234
19787
  var import_semver2 = __toESM(require("semver"), 1);
19235
19788
 
19236
19789
  // src/installers/templates.ts
@@ -19897,11 +20450,11 @@ var OTEL_ENV = {
19897
20450
  value: "http://localhost:4318/projects/<project>/v1/traces"
19898
20451
  };
19899
20452
  function serviceNodeName(pkg, serviceDir) {
19900
- return pkg.name ?? import_node_path72.default.basename(serviceDir);
20453
+ return pkg.name ?? import_node_path73.default.basename(serviceDir);
19901
20454
  }
19902
20455
  function projectToken(pkg, serviceDir, project) {
19903
20456
  if (project && project.length > 0) return project;
19904
- return pkg.name ?? import_node_path72.default.basename(serviceDir);
20457
+ return pkg.name ?? import_node_path73.default.basename(serviceDir);
19905
20458
  }
19906
20459
  async function readJsonFile(p) {
19907
20460
  try {
@@ -19914,16 +20467,16 @@ async function readJsonFile(p) {
19914
20467
  async function detectRuntimeKind(pkgRoot, pkg) {
19915
20468
  const deps = allDeps(pkg);
19916
20469
  if ("react-native" in deps || "expo" in deps) return "react-native";
19917
- const appJson = await readJsonFile(import_node_path72.default.join(pkgRoot, "app.json"));
20470
+ const appJson = await readJsonFile(import_node_path73.default.join(pkgRoot, "app.json"));
19918
20471
  if (appJson && typeof appJson === "object" && "expo" in appJson) {
19919
20472
  return "react-native";
19920
20473
  }
19921
- if (await exists3(import_node_path72.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path72.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path72.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
20474
+ if (await exists3(import_node_path73.default.join(pkgRoot, "vite.config.js")) || await exists3(import_node_path73.default.join(pkgRoot, "vite.config.ts")) || await exists3(import_node_path73.default.join(pkgRoot, "vite.config.mjs")) || "vite" in deps) {
19922
20475
  return "browser-bundle";
19923
20476
  }
19924
- if (await exists3(import_node_path72.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
19925
- if (await exists3(import_node_path72.default.join(pkgRoot, "bun.lockb"))) return "bun";
19926
- if (await exists3(import_node_path72.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path72.default.join(pkgRoot, "deno.lock"))) {
20477
+ if (await exists3(import_node_path73.default.join(pkgRoot, "wrangler.toml"))) return "cloudflare-workers";
20478
+ if (await exists3(import_node_path73.default.join(pkgRoot, "bun.lockb"))) return "bun";
20479
+ if (await exists3(import_node_path73.default.join(pkgRoot, "deno.json")) || await exists3(import_node_path73.default.join(pkgRoot, "deno.lock"))) {
19927
20480
  return "deno";
19928
20481
  }
19929
20482
  const engines = pkg.engines ?? {};
@@ -19932,7 +20485,7 @@ async function detectRuntimeKind(pkgRoot, pkg) {
19932
20485
  }
19933
20486
  async function readPackageJson2(serviceDir) {
19934
20487
  try {
19935
- const raw = await import_node_fs39.promises.readFile(import_node_path72.default.join(serviceDir, "package.json"), "utf8");
20488
+ const raw = await import_node_fs39.promises.readFile(import_node_path73.default.join(serviceDir, "package.json"), "utf8");
19936
20489
  return JSON.parse(raw);
19937
20490
  } catch {
19938
20491
  return null;
@@ -19976,7 +20529,7 @@ function needsVersionUpgrade(installed, expected) {
19976
20529
  var NEXT_CONFIG_CANDIDATES = ["next.config.js", "next.config.ts", "next.config.mjs"];
19977
20530
  async function findNextConfig(serviceDir) {
19978
20531
  for (const name of NEXT_CONFIG_CANDIDATES) {
19979
- const candidate = import_node_path72.default.join(serviceDir, name);
20532
+ const candidate = import_node_path73.default.join(serviceDir, name);
19980
20533
  if (await exists3(candidate)) return candidate;
19981
20534
  }
19982
20535
  return null;
@@ -20045,7 +20598,7 @@ function hasRemixDependency(pkg) {
20045
20598
  }
20046
20599
  async function findRemixEntry(serviceDir) {
20047
20600
  for (const rel of REMIX_ENTRY_CANDIDATES) {
20048
- const candidate = import_node_path72.default.join(serviceDir, rel);
20601
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20049
20602
  if (await exists3(candidate)) return candidate;
20050
20603
  }
20051
20604
  return null;
@@ -20057,14 +20610,14 @@ function hasSvelteKitDependency(pkg) {
20057
20610
  }
20058
20611
  async function findSvelteKitHooks(serviceDir) {
20059
20612
  for (const rel of SVELTEKIT_HOOKS_CANDIDATES) {
20060
- const candidate = import_node_path72.default.join(serviceDir, rel);
20613
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20061
20614
  if (await exists3(candidate)) return candidate;
20062
20615
  }
20063
20616
  return null;
20064
20617
  }
20065
20618
  async function findSvelteKitConfig(serviceDir) {
20066
20619
  for (const rel of SVELTEKIT_CONFIG_CANDIDATES) {
20067
- const candidate = import_node_path72.default.join(serviceDir, rel);
20620
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20068
20621
  if (await exists3(candidate)) return candidate;
20069
20622
  }
20070
20623
  return null;
@@ -20075,7 +20628,7 @@ function hasNuxtDependency(pkg) {
20075
20628
  }
20076
20629
  async function findNuxtConfig(serviceDir) {
20077
20630
  for (const name of NUXT_CONFIG_CANDIDATES) {
20078
- const candidate = import_node_path72.default.join(serviceDir, name);
20631
+ const candidate = import_node_path73.default.join(serviceDir, name);
20079
20632
  if (await exists3(candidate)) return candidate;
20080
20633
  }
20081
20634
  return null;
@@ -20086,7 +20639,7 @@ function hasAstroDependency(pkg) {
20086
20639
  }
20087
20640
  async function findAstroConfig(serviceDir) {
20088
20641
  for (const name of ASTRO_CONFIG_CANDIDATES) {
20089
- const candidate = import_node_path72.default.join(serviceDir, name);
20642
+ const candidate = import_node_path73.default.join(serviceDir, name);
20090
20643
  if (await exists3(candidate)) return candidate;
20091
20644
  }
20092
20645
  return null;
@@ -20100,7 +20653,7 @@ function parseNextMajor(range) {
20100
20653
  return Number.isFinite(n) ? n : null;
20101
20654
  }
20102
20655
  async function isTypeScriptProject(serviceDir) {
20103
- return exists3(import_node_path72.default.join(serviceDir, "tsconfig.json"));
20656
+ return exists3(import_node_path73.default.join(serviceDir, "tsconfig.json"));
20104
20657
  }
20105
20658
  var INDEX_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs", ".cjs"];
20106
20659
  var INDEX_CANDIDATES = INDEX_EXTENSIONS.map((ext) => `index${ext}`);
@@ -20149,7 +20702,7 @@ function entryFromScript(script) {
20149
20702
  }
20150
20703
  async function resolveEntry(serviceDir, pkg) {
20151
20704
  if (typeof pkg.main === "string" && pkg.main.length > 0) {
20152
- const candidate = import_node_path72.default.resolve(serviceDir, pkg.main);
20705
+ const candidate = import_node_path73.default.resolve(serviceDir, pkg.main);
20153
20706
  if (await exists3(candidate)) return candidate;
20154
20707
  }
20155
20708
  if (pkg.bin) {
@@ -20163,40 +20716,40 @@ async function resolveEntry(serviceDir, pkg) {
20163
20716
  if (typeof first === "string") binEntry = first;
20164
20717
  }
20165
20718
  if (binEntry) {
20166
- const candidate = import_node_path72.default.resolve(serviceDir, binEntry);
20719
+ const candidate = import_node_path73.default.resolve(serviceDir, binEntry);
20167
20720
  if (await exists3(candidate)) return candidate;
20168
20721
  }
20169
20722
  }
20170
20723
  const startEntry = entryFromScript(pkg.scripts?.start);
20171
20724
  if (startEntry) {
20172
- const candidate = import_node_path72.default.resolve(serviceDir, startEntry);
20725
+ const candidate = import_node_path73.default.resolve(serviceDir, startEntry);
20173
20726
  if (await exists3(candidate)) return candidate;
20174
20727
  }
20175
20728
  const devEntry = entryFromScript(pkg.scripts?.dev);
20176
20729
  if (devEntry) {
20177
- const candidate = import_node_path72.default.resolve(serviceDir, devEntry);
20730
+ const candidate = import_node_path73.default.resolve(serviceDir, devEntry);
20178
20731
  if (await exists3(candidate)) return candidate;
20179
20732
  }
20180
20733
  for (const rel of SRC_INDEX_CANDIDATES) {
20181
- const candidate = import_node_path72.default.join(serviceDir, rel);
20734
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20182
20735
  if (await exists3(candidate)) return candidate;
20183
20736
  }
20184
20737
  for (const rel of SRC_NAMED_CANDIDATES) {
20185
- const candidate = import_node_path72.default.join(serviceDir, rel);
20738
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20186
20739
  if (await exists3(candidate)) return candidate;
20187
20740
  }
20188
20741
  for (const rel of ROOT_NAMED_CANDIDATES) {
20189
- const candidate = import_node_path72.default.join(serviceDir, rel);
20742
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20190
20743
  if (await exists3(candidate)) return candidate;
20191
20744
  }
20192
20745
  for (const name of INDEX_CANDIDATES) {
20193
- const candidate = import_node_path72.default.join(serviceDir, name);
20746
+ const candidate = import_node_path73.default.join(serviceDir, name);
20194
20747
  if (await exists3(candidate)) return candidate;
20195
20748
  }
20196
20749
  return null;
20197
20750
  }
20198
20751
  function dispatchEntry(entryFile, pkg) {
20199
- const ext = import_node_path72.default.extname(entryFile).toLowerCase();
20752
+ const ext = import_node_path73.default.extname(entryFile).toLowerCase();
20200
20753
  if (ext === ".ts" || ext === ".tsx") return pkg.type === "module" ? "ts" : "ts-cjs";
20201
20754
  if (ext === ".mjs") return "esm";
20202
20755
  if (ext === ".cjs") return "cjs";
@@ -20214,9 +20767,9 @@ function otelInitContents(flavor) {
20214
20767
  return OTEL_INIT_CJS;
20215
20768
  }
20216
20769
  function injectionLine(flavor, entryFile, otelInitFile) {
20217
- let rel = import_node_path72.default.relative(import_node_path72.default.dirname(entryFile), otelInitFile);
20770
+ let rel = import_node_path73.default.relative(import_node_path73.default.dirname(entryFile), otelInitFile);
20218
20771
  if (!rel.startsWith(".")) rel = `./${rel}`;
20219
- rel = rel.split(import_node_path72.default.sep).join("/");
20772
+ rel = rel.split(import_node_path73.default.sep).join("/");
20220
20773
  if (flavor === "cjs") return `require('${rel}')`;
20221
20774
  if (flavor === "esm") return `import '${rel}'`;
20222
20775
  const tsRel = rel.replace(/\.ts$/, "");
@@ -20229,27 +20782,27 @@ function lineIsOtelInjection(line) {
20229
20782
  }
20230
20783
  async function detectsSrcLayout(serviceDir) {
20231
20784
  const [hasSrcApp, hasSrcPages, hasRootApp, hasRootPages] = await Promise.all([
20232
- exists3(import_node_path72.default.join(serviceDir, "src", "app")),
20233
- exists3(import_node_path72.default.join(serviceDir, "src", "pages")),
20234
- exists3(import_node_path72.default.join(serviceDir, "app")),
20235
- exists3(import_node_path72.default.join(serviceDir, "pages"))
20785
+ exists3(import_node_path73.default.join(serviceDir, "src", "app")),
20786
+ exists3(import_node_path73.default.join(serviceDir, "src", "pages")),
20787
+ exists3(import_node_path73.default.join(serviceDir, "app")),
20788
+ exists3(import_node_path73.default.join(serviceDir, "pages"))
20236
20789
  ]);
20237
20790
  return (hasSrcApp || hasSrcPages) && !hasRootApp && !hasRootPages;
20238
20791
  }
20239
20792
  async function planNext(serviceDir, pkg, manifestPath, nextConfigPath, project) {
20240
20793
  const useTs = await isTypeScriptProject(serviceDir);
20241
20794
  const srcLayout = await detectsSrcLayout(serviceDir);
20242
- const baseDir = srcLayout ? import_node_path72.default.join(serviceDir, "src") : serviceDir;
20243
- const instrumentationFile = import_node_path72.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
20244
- const instrumentationNodeFile = import_node_path72.default.join(
20795
+ const baseDir = srcLayout ? import_node_path73.default.join(serviceDir, "src") : serviceDir;
20796
+ const instrumentationFile = import_node_path73.default.join(baseDir, useTs ? "instrumentation.ts" : "instrumentation.js");
20797
+ const instrumentationNodeFile = import_node_path73.default.join(
20245
20798
  baseDir,
20246
20799
  useTs ? "instrumentation.node.ts" : "instrumentation.node.js"
20247
20800
  );
20248
- const instrumentationEdgeFile = import_node_path72.default.join(
20801
+ const instrumentationEdgeFile = import_node_path73.default.join(
20249
20802
  baseDir,
20250
20803
  useTs ? "instrumentation.edge.ts" : "instrumentation.edge.js"
20251
20804
  );
20252
- const envNeatFile = import_node_path72.default.join(baseDir, ".env.neat");
20805
+ const envNeatFile = import_node_path73.default.join(baseDir, ".env.neat");
20253
20806
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
20254
20807
  const dependencyEdits = [];
20255
20808
  for (const sdk of SDK_PACKAGES) {
@@ -20374,7 +20927,7 @@ function buildDependencyEdits(pkg, manifestPath) {
20374
20927
  return edits;
20375
20928
  }
20376
20929
  async function queueEnvNeat(serviceDir, pkg, project, generatedFiles) {
20377
- const envNeatFile = import_node_path72.default.join(serviceDir, ".env.neat");
20930
+ const envNeatFile = import_node_path73.default.join(serviceDir, ".env.neat");
20378
20931
  if (!await exists3(envNeatFile)) {
20379
20932
  generatedFiles.push({
20380
20933
  file: envNeatFile,
@@ -20409,7 +20962,7 @@ function fileImportsOtelHook(raw, specifiers) {
20409
20962
  }
20410
20963
  async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
20411
20964
  const useTs = await isTypeScriptProject(serviceDir);
20412
- const otelServerFile = import_node_path72.default.join(
20965
+ const otelServerFile = import_node_path73.default.join(
20413
20966
  serviceDir,
20414
20967
  useTs ? "app/otel.server.ts" : "app/otel.server.js"
20415
20968
  );
@@ -20466,11 +21019,11 @@ async function planRemix(serviceDir, pkg, manifestPath, entryFile, project) {
20466
21019
  }
20467
21020
  async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project) {
20468
21021
  const useTs = await isTypeScriptProject(serviceDir);
20469
- const otelInitFile = import_node_path72.default.join(
21022
+ const otelInitFile = import_node_path73.default.join(
20470
21023
  serviceDir,
20471
21024
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
20472
21025
  );
20473
- const resolvedHooksFile = hooksFile ?? import_node_path72.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
21026
+ const resolvedHooksFile = hooksFile ?? import_node_path73.default.join(serviceDir, useTs ? "src/hooks.server.ts" : "src/hooks.server.js");
20474
21027
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
20475
21028
  const generatedFiles = [];
20476
21029
  const entrypointEdits = [];
@@ -20532,11 +21085,11 @@ async function planSvelteKit(serviceDir, pkg, manifestPath, hooksFile, project)
20532
21085
  }
20533
21086
  async function planNuxt(serviceDir, pkg, manifestPath, project) {
20534
21087
  const useTs = await isTypeScriptProject(serviceDir);
20535
- const otelPluginFile = import_node_path72.default.join(
21088
+ const otelPluginFile = import_node_path73.default.join(
20536
21089
  serviceDir,
20537
21090
  useTs ? "server/plugins/otel.ts" : "server/plugins/otel.js"
20538
21091
  );
20539
- const otelInitFile = import_node_path72.default.join(
21092
+ const otelInitFile = import_node_path73.default.join(
20540
21093
  serviceDir,
20541
21094
  useTs ? "server/plugins/otel-init.ts" : "server/plugins/otel-init.js"
20542
21095
  );
@@ -20587,19 +21140,19 @@ async function planNuxt(serviceDir, pkg, manifestPath, project) {
20587
21140
  var ASTRO_MIDDLEWARE_CANDIDATES = ["src/middleware.ts", "src/middleware.js"];
20588
21141
  async function findAstroMiddleware(serviceDir) {
20589
21142
  for (const rel of ASTRO_MIDDLEWARE_CANDIDATES) {
20590
- const candidate = import_node_path72.default.join(serviceDir, rel);
21143
+ const candidate = import_node_path73.default.join(serviceDir, rel);
20591
21144
  if (await exists3(candidate)) return candidate;
20592
21145
  }
20593
21146
  return null;
20594
21147
  }
20595
21148
  async function planAstro(serviceDir, pkg, manifestPath, project) {
20596
21149
  const useTs = await isTypeScriptProject(serviceDir);
20597
- const otelInitFile = import_node_path72.default.join(
21150
+ const otelInitFile = import_node_path73.default.join(
20598
21151
  serviceDir,
20599
21152
  useTs ? "src/otel-init.ts" : "src/otel-init.js"
20600
21153
  );
20601
21154
  const existingMiddleware = await findAstroMiddleware(serviceDir);
20602
- const middlewareFile = existingMiddleware ?? import_node_path72.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
21155
+ const middlewareFile = existingMiddleware ?? import_node_path73.default.join(serviceDir, useTs ? "src/middleware.ts" : "src/middleware.js");
20603
21156
  const dependencyEdits = buildDependencyEdits(pkg, manifestPath);
20604
21157
  const generatedFiles = [];
20605
21158
  const entrypointEdits = [];
@@ -20695,7 +21248,7 @@ async function findFrameworkDispatch(serviceDir, pkg, manifestPath, project) {
20695
21248
  }
20696
21249
  async function plan(serviceDir, opts) {
20697
21250
  const pkg = await readPackageJson2(serviceDir);
20698
- const manifestPath = import_node_path72.default.join(serviceDir, "package.json");
21251
+ const manifestPath = import_node_path73.default.join(serviceDir, "package.json");
20699
21252
  const project = opts?.project;
20700
21253
  const empty = {
20701
21254
  language: "javascript",
@@ -20730,8 +21283,8 @@ async function plan(serviceDir, opts) {
20730
21283
  return { ...empty, libOnly: true };
20731
21284
  }
20732
21285
  const flavor = dispatchEntry(entryFile, pkg);
20733
- const otelInitFile = import_node_path72.default.join(import_node_path72.default.dirname(entryFile), otelInitFilename(flavor));
20734
- const envNeatFile = import_node_path72.default.join(serviceDir, ".env.neat");
21286
+ const otelInitFile = import_node_path73.default.join(import_node_path73.default.dirname(entryFile), otelInitFilename(flavor));
21287
+ const envNeatFile = import_node_path73.default.join(serviceDir, ".env.neat");
20735
21288
  const existingDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
20736
21289
  const dependencyEdits = [];
20737
21290
  for (const sdk of SDK_PACKAGES) {
@@ -20800,13 +21353,13 @@ async function plan(serviceDir, opts) {
20800
21353
  };
20801
21354
  }
20802
21355
  function isAllowedWritePath(serviceDir, target) {
20803
- const rel = import_node_path72.default.relative(serviceDir, target);
21356
+ const rel = import_node_path73.default.relative(serviceDir, target);
20804
21357
  if (rel.startsWith("..")) return false;
20805
- const base = import_node_path72.default.basename(target);
21358
+ const base = import_node_path73.default.basename(target);
20806
21359
  if (base === "package.json") return true;
20807
21360
  if (base === ".env.neat") return true;
20808
21361
  if (/^otel-init\.(?:js|cjs|mjs|ts)$/.test(base)) return true;
20809
- const relPosix = rel.split(import_node_path72.default.sep).join("/");
21362
+ const relPosix = rel.split(import_node_path73.default.sep).join("/");
20810
21363
  if (/^instrumentation(?:\.(?:node|edge))?\.(?:js|cjs|mjs|ts)$/.test(base)) {
20811
21364
  if (relPosix === base) return true;
20812
21365
  if (relPosix === `src/${base}`) return true;
@@ -20823,7 +21376,7 @@ function isAllowedWritePath(serviceDir, target) {
20823
21376
  return false;
20824
21377
  }
20825
21378
  async function writeAtomic(file, contents) {
20826
- await import_node_fs39.promises.mkdir(import_node_path72.default.dirname(file), { recursive: true });
21379
+ await import_node_fs39.promises.mkdir(import_node_path73.default.dirname(file), { recursive: true });
20827
21380
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
20828
21381
  await import_node_fs39.promises.writeFile(tmp, contents, "utf8");
20829
21382
  await import_node_fs39.promises.rename(tmp, file);
@@ -21007,7 +21560,7 @@ async function rollback(installPlan, originals, createdFiles) {
21007
21560
  ...removed.map((f) => `removed: ${f}`),
21008
21561
  ""
21009
21562
  ];
21010
- const rollbackPath = import_node_path72.default.join(installPlan.serviceDir, "neat-rollback.patch");
21563
+ const rollbackPath = import_node_path73.default.join(installPlan.serviceDir, "neat-rollback.patch");
21011
21564
  await import_node_fs39.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
21012
21565
  }
21013
21566
  function injectInstrumentationHook(raw) {
@@ -21038,7 +21591,7 @@ var javascriptInstaller = {
21038
21591
  // src/installers/python.ts
21039
21592
  init_cjs_shims();
21040
21593
  var import_node_fs40 = require("fs");
21041
- var import_node_path73 = __toESM(require("path"), 1);
21594
+ var import_node_path74 = __toESM(require("path"), 1);
21042
21595
  var SDK_PACKAGES2 = [
21043
21596
  { name: "opentelemetry-distro", version: ">=0.49b0" },
21044
21597
  { name: "opentelemetry-exporter-otlp", version: ">=1.28.0" }
@@ -21136,7 +21689,7 @@ async function writeFileAtomic(file, contents) {
21136
21689
  await import_node_fs40.promises.rename(tmp, file);
21137
21690
  }
21138
21691
  async function resolvePyEntrypoint(serviceDir) {
21139
- const procfile = import_node_path73.default.join(serviceDir, "Procfile");
21692
+ const procfile = import_node_path74.default.join(serviceDir, "Procfile");
21140
21693
  if (await exists4(procfile)) {
21141
21694
  const raw = await import_node_fs40.promises.readFile(procfile, "utf8");
21142
21695
  for (const line of raw.split(/\r?\n/)) {
@@ -21146,20 +21699,20 @@ async function resolvePyEntrypoint(serviceDir) {
21146
21699
  const asgi = cmd.match(/\b(?:uvicorn|gunicorn|hypercorn|daphne)\s+([\w.]+):/);
21147
21700
  if (asgi) {
21148
21701
  const modPath = asgi[1].replace(/\./g, "/");
21149
- const asFile = import_node_path73.default.join(serviceDir, `${modPath}.py`);
21702
+ const asFile = import_node_path74.default.join(serviceDir, `${modPath}.py`);
21150
21703
  if (await exists4(asFile)) return asFile;
21151
- const asPkg = import_node_path73.default.join(serviceDir, modPath, "__init__.py");
21704
+ const asPkg = import_node_path74.default.join(serviceDir, modPath, "__init__.py");
21152
21705
  if (await exists4(asPkg)) return asPkg;
21153
21706
  }
21154
21707
  const runFile = cmd.match(/\b(?:python3?|fastapi\s+(?:run|dev))\s+([\w./-]+\.py)\b/);
21155
21708
  if (runFile) {
21156
- const p = import_node_path73.default.join(serviceDir, runFile[1]);
21709
+ const p = import_node_path74.default.join(serviceDir, runFile[1]);
21157
21710
  if (await exists4(p)) return p;
21158
21711
  }
21159
21712
  }
21160
21713
  }
21161
21714
  for (const name of ["main.py", "app.py", "asgi.py", "wsgi.py", "manage.py", "server.py"]) {
21162
- const p = import_node_path73.default.join(serviceDir, name);
21715
+ const p = import_node_path74.default.join(serviceDir, name);
21163
21716
  if (await exists4(p)) return p;
21164
21717
  }
21165
21718
  return null;
@@ -21185,7 +21738,7 @@ async function exists4(p) {
21185
21738
  async function detect2(serviceDir) {
21186
21739
  const markers = ["requirements.txt", "pyproject.toml", "setup.py"];
21187
21740
  for (const m of markers) {
21188
- if (await exists4(import_node_path73.default.join(serviceDir, m))) return true;
21741
+ if (await exists4(import_node_path74.default.join(serviceDir, m))) return true;
21189
21742
  }
21190
21743
  return false;
21191
21744
  }
@@ -21195,7 +21748,7 @@ function reqPackageName(line) {
21195
21748
  return head.replace(/[<>=!~].*$/, "").toLowerCase();
21196
21749
  }
21197
21750
  async function planRequirementsTxtEdits(serviceDir) {
21198
- const file = import_node_path73.default.join(serviceDir, "requirements.txt");
21751
+ const file = import_node_path74.default.join(serviceDir, "requirements.txt");
21199
21752
  if (!await exists4(file)) return null;
21200
21753
  const raw = await import_node_fs40.promises.readFile(file, "utf8");
21201
21754
  const presentNames = new Set(
@@ -21205,7 +21758,7 @@ async function planRequirementsTxtEdits(serviceDir) {
21205
21758
  return { manifest: file, missing: [...missing] };
21206
21759
  }
21207
21760
  async function planProcfileEdits(serviceDir) {
21208
- const procfile = import_node_path73.default.join(serviceDir, "Procfile");
21761
+ const procfile = import_node_path74.default.join(serviceDir, "Procfile");
21209
21762
  if (!await exists4(procfile)) return [];
21210
21763
  const raw = await import_node_fs40.promises.readFile(procfile, "utf8");
21211
21764
  const edits = [];
@@ -21243,7 +21796,7 @@ async function plan2(serviceDir) {
21243
21796
  }
21244
21797
  const entrypointEdits = await planProcfileEdits(serviceDir);
21245
21798
  const entryFile = await resolvePyEntrypoint(serviceDir);
21246
- const generatedFiles = entryFile ? [{ file: import_node_path73.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
21799
+ const generatedFiles = entryFile ? [{ file: import_node_path74.default.join(serviceDir, NEAT_OTEL_FILENAME), contents: neatOtelPy() }] : [];
21247
21800
  if (dependencyEdits.length === 0 && entrypointEdits.length === 0 && !entryFile) {
21248
21801
  return empty;
21249
21802
  }
@@ -21311,7 +21864,7 @@ async function apply2(installPlan) {
21311
21864
  if (raw === void 0) {
21312
21865
  throw new Error(`python installer: cannot read ${file} during apply`);
21313
21866
  }
21314
- const base = import_node_path73.default.basename(file);
21867
+ const base = import_node_path74.default.basename(file);
21315
21868
  if (base === "requirements.txt") {
21316
21869
  const edits = installPlan.dependencyEdits.filter((e) => e.file === file);
21317
21870
  if (edits.length > 0) {
@@ -21366,7 +21919,7 @@ async function rollback2(installPlan, originals, createdFiles = []) {
21366
21919
  ...removed.map((f) => `removed: ${f}`),
21367
21920
  ""
21368
21921
  ];
21369
- const rollbackPath = import_node_path73.default.join(installPlan.serviceDir, "neat-rollback.patch");
21922
+ const rollbackPath = import_node_path74.default.join(installPlan.serviceDir, "neat-rollback.patch");
21370
21923
  await import_node_fs40.promises.writeFile(rollbackPath, lines.join("\n"), "utf8");
21371
21924
  }
21372
21925
  var pythonInstaller = {
@@ -21379,7 +21932,7 @@ var pythonInstaller = {
21379
21932
  // src/installers/go.ts
21380
21933
  init_cjs_shims();
21381
21934
  var import_node_fs41 = require("fs");
21382
- var import_node_path74 = __toESM(require("path"), 1);
21935
+ var import_node_path75 = __toESM(require("path"), 1);
21383
21936
  var GO_DEPS = [
21384
21937
  { name: "go.opentelemetry.io/otel", version: "v1.38.0" },
21385
21938
  { name: "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp", version: "v1.38.0" },
@@ -21389,13 +21942,13 @@ async function exists5(file) {
21389
21942
  return import_node_fs41.promises.stat(file).then(() => true, () => false);
21390
21943
  }
21391
21944
  async function findMain(serviceDir) {
21392
- const root = import_node_path74.default.join(serviceDir, "main.go");
21945
+ const root = import_node_path75.default.join(serviceDir, "main.go");
21393
21946
  if (await exists5(root)) return root;
21394
- const cmd = import_node_path74.default.join(serviceDir, "cmd");
21947
+ const cmd = import_node_path75.default.join(serviceDir, "cmd");
21395
21948
  const entries = await import_node_fs41.promises.readdir(cmd, { withFileTypes: true }).catch(() => []);
21396
21949
  for (const entry2 of entries.sort((a, b) => a.name.localeCompare(b.name))) {
21397
21950
  if (!entry2.isDirectory()) continue;
21398
- const candidate = import_node_path74.default.join(cmd, entry2.name, "main.go");
21951
+ const candidate = import_node_path75.default.join(cmd, entry2.name, "main.go");
21399
21952
  if (await exists5(candidate)) return candidate;
21400
21953
  }
21401
21954
  return null;
@@ -21470,17 +22023,17 @@ func init() {
21470
22023
  `;
21471
22024
  }
21472
22025
  async function detect3(serviceDir) {
21473
- return exists5(import_node_path74.default.join(serviceDir, "go.mod"));
22026
+ return exists5(import_node_path75.default.join(serviceDir, "go.mod"));
21474
22027
  }
21475
22028
  async function plan3(serviceDir) {
21476
- const manifest = import_node_path74.default.join(serviceDir, "go.mod");
22029
+ const manifest = import_node_path75.default.join(serviceDir, "go.mod");
21477
22030
  const raw = await import_node_fs41.promises.readFile(manifest, "utf8");
21478
22031
  const main2 = await findMain(serviceDir);
21479
22032
  const dependencyEdits = GO_DEPS.filter((dep) => !raw.includes(dep.name)).map((dep) => ({ file: manifest, kind: "add", ...dep }));
21480
22033
  if (!main2) return { language: "go", serviceDir, dependencyEdits: [], entrypointEdits: [], envEdits: [], libOnly: true };
21481
22034
  const source = await import_node_fs41.promises.readFile(main2, "utf8");
21482
22035
  const packageName = source.match(/^\s*package\s+(\w+)\s*$/m)?.[1] ?? "main";
21483
- const generated = import_node_path74.default.join(import_node_path74.default.dirname(main2), "neat_otel.go");
22036
+ const generated = import_node_path75.default.join(import_node_path75.default.dirname(main2), "neat_otel.go");
21484
22037
  const generatedFiles = await exists5(generated) ? [] : [{ file: generated, contents: neatOtelGo(packageName) }];
21485
22038
  return { language: "go", serviceDir, dependencyEdits, entrypointEdits: [], envEdits: [], generatedFiles, entryFile: main2 };
21486
22039
  }
@@ -21622,7 +22175,7 @@ init_cjs_shims();
21622
22175
  var import_node_fs42 = require("fs");
21623
22176
  var import_node_http = __toESM(require("http"), 1);
21624
22177
  var import_node_net = __toESM(require("net"), 1);
21625
- var import_node_path75 = __toESM(require("path"), 1);
22178
+ var import_node_path76 = __toESM(require("path"), 1);
21626
22179
  var import_node_url4 = require("url");
21627
22180
  var import_node_child_process3 = require("child_process");
21628
22181
  var import_node_readline = __toESM(require("readline"), 1);
@@ -21632,7 +22185,7 @@ async function extractAndPersist(opts) {
21632
22185
  const graphKey = opts.projectExplicit ? opts.project : DEFAULT_PROJECT;
21633
22186
  resetGraph(graphKey);
21634
22187
  const graph = getGraph(graphKey);
21635
- const projectPaths = pathsForProject(graphKey, import_node_path75.default.join(opts.scanPath, "neat-out"));
22188
+ const projectPaths = pathsForProject(graphKey, import_node_path76.default.join(opts.scanPath, "neat-out"));
21636
22189
  const extraction = await extractFromDirectory(graph, opts.scanPath, {
21637
22190
  errorsPath: projectPaths.errorsPath
21638
22191
  });
@@ -21684,7 +22237,7 @@ async function applyInstallersOver(services, project, options = {}) {
21684
22237
  libOnly++;
21685
22238
  const appDeps = svc.pkg ? appFrameworkDependencies(svc.pkg) : [];
21686
22239
  if (appDeps.length > 0) {
21687
- const svcName = import_node_path75.default.basename(svc.dir);
22240
+ const svcName = import_node_path76.default.basename(svc.dir);
21688
22241
  const list = appDeps.join(", ");
21689
22242
  console.warn(
21690
22243
  `neat: runtime layer won't engage for ${svcName}: no entry point found.
@@ -21697,7 +22250,7 @@ async function applyInstallersOver(services, project, options = {}) {
21697
22250
  console.log(`skipping ${svc.dir}: browser bundle; browser-OTel support lands in a future release.`);
21698
22251
  } else if (outcome.outcome === "react-native") {
21699
22252
  reactNative++;
21700
- const svcName = import_node_path75.default.basename(svc.dir);
22253
+ const svcName = import_node_path76.default.basename(svc.dir);
21701
22254
  console.log(
21702
22255
  `neat: ${svc.dir} detected as React Native / Expo
21703
22256
  The installer doesn't cover this runtime deterministically.
@@ -21708,7 +22261,7 @@ async function applyInstallersOver(services, project, options = {}) {
21708
22261
  );
21709
22262
  } else if (outcome.outcome === "bun") {
21710
22263
  bun++;
21711
- const svcName = import_node_path75.default.basename(svc.dir);
22264
+ const svcName = import_node_path76.default.basename(svc.dir);
21712
22265
  console.log(
21713
22266
  `neat: ${svc.dir} detected as Bun
21714
22267
  The installer doesn't cover this runtime deterministically.
@@ -21719,7 +22272,7 @@ async function applyInstallersOver(services, project, options = {}) {
21719
22272
  );
21720
22273
  } else if (outcome.outcome === "deno") {
21721
22274
  deno++;
21722
- const svcName = import_node_path75.default.basename(svc.dir);
22275
+ const svcName = import_node_path76.default.basename(svc.dir);
21723
22276
  console.log(
21724
22277
  `neat: ${svc.dir} detected as Deno
21725
22278
  The installer doesn't cover this runtime deterministically.
@@ -21730,7 +22283,7 @@ async function applyInstallersOver(services, project, options = {}) {
21730
22283
  );
21731
22284
  } else if (outcome.outcome === "cloudflare-workers") {
21732
22285
  cloudflareWorkers++;
21733
- const svcName = import_node_path75.default.basename(svc.dir);
22286
+ const svcName = import_node_path76.default.basename(svc.dir);
21734
22287
  console.log(
21735
22288
  `neat: ${svc.dir} detected as Cloudflare Workers
21736
22289
  The installer doesn't cover this runtime deterministically.
@@ -21741,7 +22294,7 @@ async function applyInstallersOver(services, project, options = {}) {
21741
22294
  );
21742
22295
  } else if (outcome.outcome === "electron") {
21743
22296
  electron++;
21744
- const svcName = import_node_path75.default.basename(svc.dir);
22297
+ const svcName = import_node_path76.default.basename(svc.dir);
21745
22298
  console.log(
21746
22299
  `neat: ${svc.dir} detected as Electron
21747
22300
  The installer doesn't cover this runtime deterministically.
@@ -21754,7 +22307,7 @@ async function applyInstallersOver(services, project, options = {}) {
21754
22307
  if (svc.pkg && (outcome.outcome === "instrumented" || outcome.outcome === "already-instrumented")) {
21755
22308
  const gaps = uninstrumentedLibraries(svc.pkg);
21756
22309
  if (gaps.length > 0) {
21757
- const svcName = import_node_path75.default.basename(svc.dir);
22310
+ const svcName = import_node_path76.default.basename(svc.dir);
21758
22311
  const list = gaps.join(", ");
21759
22312
  const subject = gaps.length === 1 ? "this library" : "these libraries";
21760
22313
  const aux = gaps.length === 1 ? "isn't" : "aren't";
@@ -21960,8 +22513,8 @@ async function persistedPortsFor(scanPath) {
21960
22513
  return { rest: record.ports.rest, otlp: record.ports.otlp, web: record.ports.web };
21961
22514
  }
21962
22515
  async function acquireSpawnLock(scanPath) {
21963
- const lockPath = import_node_path75.default.join(scanPath, "neat-out", "daemon.spawn.lock");
21964
- await import_node_fs42.promises.mkdir(import_node_path75.default.dirname(lockPath), { recursive: true });
22516
+ const lockPath = import_node_path76.default.join(scanPath, "neat-out", "daemon.spawn.lock");
22517
+ await import_node_fs42.promises.mkdir(import_node_path76.default.dirname(lockPath), { recursive: true });
21965
22518
  const STALE_LOCK_MS = 6e4;
21966
22519
  try {
21967
22520
  const fd = await import_node_fs42.promises.open(lockPath, "wx");
@@ -22006,13 +22559,13 @@ async function healthIsForProject(restPort, project) {
22006
22559
  return false;
22007
22560
  }
22008
22561
  function daemonLogPath(projectPath3) {
22009
- return import_node_path75.default.join(projectPath3, "neat-out", "daemon.log");
22562
+ return import_node_path76.default.join(projectPath3, "neat-out", "daemon.log");
22010
22563
  }
22011
22564
  function spawnDaemonDetached(spec) {
22012
- const here = import_node_path75.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
22565
+ const here = import_node_path76.default.dirname((0, import_node_url4.fileURLToPath)(importMetaUrl));
22013
22566
  const candidates = [
22014
- import_node_path75.default.join(here, "neatd.cjs"),
22015
- import_node_path75.default.join(here, "neatd.js")
22567
+ import_node_path76.default.join(here, "neatd.cjs"),
22568
+ import_node_path76.default.join(here, "neatd.js")
22016
22569
  ];
22017
22570
  let entry2 = null;
22018
22571
  const fsSync = require("fs");
@@ -22042,7 +22595,7 @@ function spawnDaemonDetached(spec) {
22042
22595
  let logFd = null;
22043
22596
  if (spec) {
22044
22597
  const logPath = daemonLogPath(spec.projectPath);
22045
- fsSync.mkdirSync(import_node_path75.default.dirname(logPath), { recursive: true });
22598
+ fsSync.mkdirSync(import_node_path76.default.dirname(logPath), { recursive: true });
22046
22599
  logFd = fsSync.openSync(logPath, "a");
22047
22600
  }
22048
22601
  const child = (0, import_node_child_process3.spawn)(process.execPath, [entry2, "start"], {
@@ -22241,7 +22794,7 @@ async function runOrchestrator(opts) {
22241
22794
  result.steps.browser = openBrowser(dashboardUrl);
22242
22795
  }
22243
22796
  const daemonRunning = result.steps.daemon === "spawned" || result.steps.daemon === "already-running";
22244
- const daemonLog = daemonRunning ? import_node_path75.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
22797
+ const daemonLog = daemonRunning ? import_node_path76.default.relative(opts.scanPath, daemonLogPath(opts.scanPath)) : null;
22245
22798
  printSummary(result, graph, dashboardUrl, daemonLog);
22246
22799
  return result;
22247
22800
  }
@@ -22700,7 +23253,7 @@ async function runConnectorCommand(rawArgs, deps = {}) {
22700
23253
 
22701
23254
  // src/hooks-cli.ts
22702
23255
  init_cjs_shims();
22703
- var import_node_path76 = __toESM(require("path"), 1);
23256
+ var import_node_path77 = __toESM(require("path"), 1);
22704
23257
  var import_node_os5 = __toESM(require("os"), 1);
22705
23258
  var import_node_fs43 = require("fs");
22706
23259
  var import_node_url5 = require("url");
@@ -22709,14 +23262,14 @@ var GUIDE_FILENAME = "GRAPH_FIRST.md";
22709
23262
  var GUIDE_INSTALL_NAME = "neat-graph-first.md";
22710
23263
  var HOOK_MATCHER = "Grep|Glob|Bash";
22711
23264
  function moduleDir() {
22712
- return typeof __dirname !== "undefined" ? __dirname : import_node_path76.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
23265
+ return typeof __dirname !== "undefined" ? __dirname : import_node_path77.default.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl));
22713
23266
  }
22714
23267
  async function readSkillAsset(rel) {
22715
23268
  const here = moduleDir();
22716
23269
  const candidates = [
22717
- import_node_path76.default.resolve(here, "../../claude-skill", rel),
22718
- import_node_path76.default.resolve(here, "../../../claude-skill", rel),
22719
- import_node_path76.default.resolve(here, "../claude-skill", rel)
23270
+ import_node_path77.default.resolve(here, "../../claude-skill", rel),
23271
+ import_node_path77.default.resolve(here, "../../../claude-skill", rel),
23272
+ import_node_path77.default.resolve(here, "../claude-skill", rel)
22720
23273
  ];
22721
23274
  for (const candidate of candidates) {
22722
23275
  try {
@@ -22730,17 +23283,17 @@ async function readSkillAsset(rel) {
22730
23283
  }
22731
23284
  function neatHome3() {
22732
23285
  const override = process.env.NEAT_HOME;
22733
- if (override && override.length > 0) return import_node_path76.default.resolve(override);
22734
- return import_node_path76.default.join(import_node_os5.default.homedir(), ".neat");
23286
+ if (override && override.length > 0) return import_node_path77.default.resolve(override);
23287
+ return import_node_path77.default.join(import_node_os5.default.homedir(), ".neat");
22735
23288
  }
22736
23289
  function claudeSettingsPath() {
22737
23290
  const override = process.env.NEAT_CLAUDE_SETTINGS;
22738
- if (override && override.length > 0) return import_node_path76.default.resolve(override);
23291
+ if (override && override.length > 0) return import_node_path77.default.resolve(override);
22739
23292
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os5.default.homedir();
22740
- return import_node_path76.default.join(home, ".claude", "settings.json");
23293
+ return import_node_path77.default.join(home, ".claude", "settings.json");
22741
23294
  }
22742
23295
  function installedHookPath() {
22743
- return import_node_path76.default.join(neatHome3(), "hooks", HOOK_FILENAME);
23296
+ return import_node_path77.default.join(neatHome3(), "hooks", HOOK_FILENAME);
22744
23297
  }
22745
23298
  function isNeatSearchEntry(entry2) {
22746
23299
  return (entry2.hooks ?? []).some(
@@ -22773,9 +23326,9 @@ async function runHooks(opts) {
22773
23326
  const hookScript = await readSkillAsset(`hooks/${HOOK_FILENAME}`);
22774
23327
  const guide = await readSkillAsset(GUIDE_FILENAME);
22775
23328
  const scriptPath = installedHookPath();
22776
- await import_node_fs43.promises.mkdir(import_node_path76.default.dirname(scriptPath), { recursive: true });
23329
+ await import_node_fs43.promises.mkdir(import_node_path77.default.dirname(scriptPath), { recursive: true });
22777
23330
  await import_node_fs43.promises.writeFile(scriptPath, hookScript, { mode: 493 });
22778
- const guidePath = import_node_path76.default.join(neatHome3(), GUIDE_INSTALL_NAME);
23331
+ const guidePath = import_node_path77.default.join(neatHome3(), GUIDE_INSTALL_NAME);
22779
23332
  await import_node_fs43.promises.writeFile(guidePath, guide, "utf8");
22780
23333
  const settingsFile = claudeSettingsPath();
22781
23334
  let settings = {};
@@ -22802,7 +23355,7 @@ async function runHooks(opts) {
22802
23355
  ...settings,
22803
23356
  hooks: { ...hooks, PreToolUse: preToolUse }
22804
23357
  };
22805
- await import_node_fs43.promises.mkdir(import_node_path76.default.dirname(settingsFile), { recursive: true });
23358
+ await import_node_fs43.promises.mkdir(import_node_path77.default.dirname(settingsFile), { recursive: true });
22806
23359
  await import_node_fs43.promises.writeFile(settingsFile, JSON.stringify(merged, null, 2) + "\n", "utf8");
22807
23360
  console.log(`neat hooks: installed the search-nudge hook`);
22808
23361
  console.log(` script: ${scriptPath}`);
@@ -22874,7 +23427,7 @@ async function runHooksCommand(args) {
22874
23427
 
22875
23428
  // src/codex-cli.ts
22876
23429
  init_cjs_shims();
22877
- var import_node_path77 = __toESM(require("path"), 1);
23430
+ var import_node_path78 = __toESM(require("path"), 1);
22878
23431
  var import_node_os6 = __toESM(require("os"), 1);
22879
23432
  var import_node_fs44 = require("fs");
22880
23433
  var import_node_util = require("util");
@@ -22894,14 +23447,14 @@ var NEAT_GRAPH_FIRST_START = "<!-- neat:graph-first -->";
22894
23447
  var NEAT_GRAPH_FIRST_END = "<!-- /neat:graph-first -->";
22895
23448
  function codexConfigPath() {
22896
23449
  const override = process.env.NEAT_CODEX_CONFIG;
22897
- if (override && override.length > 0) return import_node_path77.default.resolve(override);
23450
+ if (override && override.length > 0) return import_node_path78.default.resolve(override);
22898
23451
  const home = process.env.HOME ?? process.env.USERPROFILE ?? import_node_os6.default.homedir();
22899
- return import_node_path77.default.join(home, ".codex", "config.toml");
23452
+ return import_node_path78.default.join(home, ".codex", "config.toml");
22900
23453
  }
22901
23454
  function agentsFilePath() {
22902
23455
  const override = process.env.NEAT_CODEX_AGENTS;
22903
- if (override && override.length > 0) return import_node_path77.default.resolve(override);
22904
- return import_node_path77.default.join(process.cwd(), "AGENTS.md");
23456
+ if (override && override.length > 0) return import_node_path78.default.resolve(override);
23457
+ return import_node_path78.default.join(process.cwd(), "AGENTS.md");
22905
23458
  }
22906
23459
  function isTableHeader(line) {
22907
23460
  return /^\s*\[\[?[^\]]+\]\]?\s*$/.test(line);
@@ -23084,14 +23637,14 @@ async function runCodex(opts) {
23084
23637
  return { exitCode: 0 };
23085
23638
  }
23086
23639
  if (config.changed) {
23087
- await import_node_fs44.promises.mkdir(import_node_path77.default.dirname(configPath), { recursive: true });
23640
+ await import_node_fs44.promises.mkdir(import_node_path78.default.dirname(configPath), { recursive: true });
23088
23641
  await import_node_fs44.promises.writeFile(configPath, config.text, "utf8");
23089
23642
  console.log(`neat codex: wrote [mcp_servers.neat] to ${configPath}`);
23090
23643
  } else {
23091
23644
  console.log(`neat codex: ${configPath} already has NEAT's MCP server`);
23092
23645
  }
23093
23646
  if (agents.changed) {
23094
- await import_node_fs44.promises.mkdir(import_node_path77.default.dirname(agentsPath), { recursive: true });
23647
+ await import_node_fs44.promises.mkdir(import_node_path78.default.dirname(agentsPath), { recursive: true });
23095
23648
  await import_node_fs44.promises.writeFile(agentsPath, agents.text, "utf8");
23096
23649
  console.log(`neat codex: wrote the graph-first block to ${agentsPath}`);
23097
23650
  } else {
@@ -23149,7 +23702,7 @@ async function runCodexCommand(args) {
23149
23702
 
23150
23703
  // src/editors-cli.ts
23151
23704
  init_cjs_shims();
23152
- var import_node_path78 = __toESM(require("path"), 1);
23705
+ var import_node_path79 = __toESM(require("path"), 1);
23153
23706
  var import_node_os7 = __toESM(require("os"), 1);
23154
23707
  var import_node_fs45 = require("fs");
23155
23708
  var import_node_util2 = require("util");
@@ -23175,17 +23728,17 @@ function homeDir() {
23175
23728
  }
23176
23729
  function xdgConfigDir() {
23177
23730
  const xdg = process.env.XDG_CONFIG_HOME;
23178
- return xdg && xdg.length > 0 ? import_node_path78.default.resolve(xdg) : import_node_path78.default.join(homeDir(), ".config");
23731
+ return xdg && xdg.length > 0 ? import_node_path79.default.resolve(xdg) : import_node_path79.default.join(homeDir(), ".config");
23179
23732
  }
23180
23733
  function envOverride(name) {
23181
23734
  const v = process.env[name];
23182
- return v && v.length > 0 ? import_node_path78.default.resolve(v) : void 0;
23735
+ return v && v.length > 0 ? import_node_path79.default.resolve(v) : void 0;
23183
23736
  }
23184
23737
  var CURSOR_CLIENT = {
23185
23738
  id: "cursor",
23186
23739
  label: "Cursor",
23187
23740
  docsUrl: "https://docs.cursor.com/context/mcp",
23188
- mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path78.default.join(homeDir(), ".cursor", "mcp.json"),
23741
+ mcpConfigPath: () => envOverride("NEAT_CURSOR_CONFIG") ?? import_node_path79.default.join(homeDir(), ".cursor", "mcp.json"),
23189
23742
  mcpContainerKey: "mcpServers",
23190
23743
  format: "json",
23191
23744
  // Cursor still reads a single `.cursorrules` at the project root (the modern
@@ -23197,7 +23750,7 @@ var DEVIN_CLIENT = {
23197
23750
  id: "devin",
23198
23751
  label: "Devin Desktop (Cascade)",
23199
23752
  docsUrl: "https://docs.devin.ai/desktop/cascade/mcp",
23200
- mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path78.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
23753
+ mcpConfigPath: () => envOverride("NEAT_DEVIN_CONFIG") ?? import_node_path79.default.join(homeDir(), ".codeium", "windsurf", "mcp_config.json"),
23201
23754
  mcpContainerKey: "mcpServers",
23202
23755
  format: "json",
23203
23756
  rulesFileName: ".windsurfrules"
@@ -23206,7 +23759,7 @@ var GEMINI_CLIENT = {
23206
23759
  id: "gemini",
23207
23760
  label: "Gemini CLI",
23208
23761
  docsUrl: "https://github.com/google-gemini/gemini-cli/blob/main/docs/tools/mcp-server.md",
23209
- mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path78.default.join(homeDir(), ".gemini", "settings.json"),
23762
+ mcpConfigPath: () => envOverride("NEAT_GEMINI_CONFIG") ?? import_node_path79.default.join(homeDir(), ".gemini", "settings.json"),
23210
23763
  mcpContainerKey: "mcpServers",
23211
23764
  format: "json",
23212
23765
  rulesFileName: "GEMINI.md"
@@ -23215,7 +23768,7 @@ var QWEN_CLIENT = {
23215
23768
  id: "qwen",
23216
23769
  label: "Qwen Code",
23217
23770
  docsUrl: "https://qwenlm.github.io/qwen-code-docs/en/users/features/mcp/",
23218
- mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path78.default.join(homeDir(), ".qwen", "settings.json"),
23771
+ mcpConfigPath: () => envOverride("NEAT_QWEN_CONFIG") ?? import_node_path79.default.join(homeDir(), ".qwen", "settings.json"),
23219
23772
  mcpContainerKey: "mcpServers",
23220
23773
  format: "json",
23221
23774
  rulesFileName: "QWEN.md"
@@ -23224,7 +23777,7 @@ var AMAZONQ_CLIENT = {
23224
23777
  id: "amazonq",
23225
23778
  label: "Amazon Q Developer CLI",
23226
23779
  docsUrl: "https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html",
23227
- mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path78.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
23780
+ mcpConfigPath: () => envOverride("NEAT_AMAZONQ_CONFIG") ?? import_node_path79.default.join(homeDir(), ".aws", "amazonq", "mcp.json"),
23228
23781
  mcpContainerKey: "mcpServers",
23229
23782
  format: "json"
23230
23783
  };
@@ -23232,7 +23785,7 @@ var ROOCODE_CLIENT = {
23232
23785
  id: "roocode",
23233
23786
  label: "Roo Code",
23234
23787
  docsUrl: "https://roocodeinc.github.io/Roo-Code/features/mcp/using-mcp-in-roo",
23235
- mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path78.default.join(process.cwd(), ".roo", "mcp.json"),
23788
+ mcpConfigPath: () => envOverride("NEAT_ROOCODE_CONFIG") ?? import_node_path79.default.join(process.cwd(), ".roo", "mcp.json"),
23236
23789
  mcpContainerKey: "mcpServers",
23237
23790
  format: "json"
23238
23791
  };
@@ -23245,9 +23798,9 @@ var ZED_CLIENT = {
23245
23798
  if (override) return override;
23246
23799
  if (process.platform === "win32") {
23247
23800
  const appData = process.env.APPDATA;
23248
- if (appData && appData.length > 0) return import_node_path78.default.join(appData, "Zed", "settings.json");
23801
+ if (appData && appData.length > 0) return import_node_path79.default.join(appData, "Zed", "settings.json");
23249
23802
  }
23250
- return import_node_path78.default.join(homeDir(), ".config", "zed", "settings.json");
23803
+ return import_node_path79.default.join(homeDir(), ".config", "zed", "settings.json");
23251
23804
  },
23252
23805
  mcpContainerKey: "context_servers",
23253
23806
  format: "jsonc",
@@ -23257,7 +23810,7 @@ var OPENCODE_CLIENT = {
23257
23810
  id: "opencode",
23258
23811
  label: "OpenCode",
23259
23812
  docsUrl: "https://opencode.ai/docs/mcp-servers/",
23260
- mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path78.default.join(xdgConfigDir(), "opencode", "opencode.json"),
23813
+ mcpConfigPath: () => envOverride("NEAT_OPENCODE_CONFIG") ?? import_node_path79.default.join(xdgConfigDir(), "opencode", "opencode.json"),
23261
23814
  mcpContainerKey: "mcp",
23262
23815
  format: "json",
23263
23816
  serverEntry: NEAT_OPENCODE_SERVER,
@@ -23267,7 +23820,7 @@ var CRUSH_CLIENT = {
23267
23820
  id: "crush",
23268
23821
  label: "Crush",
23269
23822
  docsUrl: "https://charmbracelet-crush.mintlify.app/configuration/mcp",
23270
- mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path78.default.join(xdgConfigDir(), "crush", "crush.json"),
23823
+ mcpConfigPath: () => envOverride("NEAT_CRUSH_CONFIG") ?? import_node_path79.default.join(xdgConfigDir(), "crush", "crush.json"),
23271
23824
  mcpContainerKey: "mcp",
23272
23825
  format: "json",
23273
23826
  serverEntry: NEAT_CRUSH_SERVER,
@@ -23372,7 +23925,7 @@ async function runEditorInstall(client, opts) {
23372
23925
  const mcpPath = client.mcpConfigPath();
23373
23926
  const serverEntry = client.serverEntry ?? NEAT_MCP_SERVER;
23374
23927
  const hasRules = typeof client.rulesFileName === "string";
23375
- const rulesPath = hasRules ? import_node_path78.default.join(opts.projectDir, client.rulesFileName) : "";
23928
+ const rulesPath = hasRules ? import_node_path79.default.join(opts.projectDir, client.rulesFileName) : "";
23376
23929
  const mcp = await planMcp(client, mcpPath);
23377
23930
  if (mcp === null) return { exitCode: 1 };
23378
23931
  let existingRules = "";
@@ -23415,10 +23968,10 @@ async function runEditorInstall(client, opts) {
23415
23968
  );
23416
23969
  return { exitCode: 0 };
23417
23970
  }
23418
- await import_node_fs45.promises.mkdir(import_node_path78.default.dirname(mcpPath), { recursive: true });
23971
+ await import_node_fs45.promises.mkdir(import_node_path79.default.dirname(mcpPath), { recursive: true });
23419
23972
  await import_node_fs45.promises.writeFile(mcpPath, mcp.text, "utf8");
23420
23973
  if (hasRules) {
23421
- await import_node_fs45.promises.mkdir(import_node_path78.default.dirname(rulesPath), { recursive: true });
23974
+ await import_node_fs45.promises.mkdir(import_node_path79.default.dirname(rulesPath), { recursive: true });
23422
23975
  await import_node_fs45.promises.writeFile(rulesPath, newRules, "utf8");
23423
23976
  }
23424
23977
  console.log(`neat ${client.id}: wired NEAT into ${client.label}`);
@@ -23482,11 +24035,11 @@ async function runEditorCommand(clientId, args, projectDir = process.cwd()) {
23482
24035
 
23483
24036
  // src/monitor.ts
23484
24037
  init_cjs_shims();
23485
- var import_types83 = require("@neat.is/types");
24038
+ var import_types84 = require("@neat.is/types");
23486
24039
 
23487
24040
  // src/cli-client.ts
23488
24041
  init_cjs_shims();
23489
- var import_types82 = require("@neat.is/types");
24042
+ var import_types83 = require("@neat.is/types");
23490
24043
  var HttpError = class extends Error {
23491
24044
  constructor(status2, message, responseBody = "") {
23492
24045
  super(message);
@@ -23511,10 +24064,10 @@ function createHttpClient(baseUrl, bearerToken) {
23511
24064
  const root = baseUrl.replace(/\/$/, "");
23512
24065
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
23513
24066
  return {
23514
- async get(path81) {
24067
+ async get(path82) {
23515
24068
  let res;
23516
24069
  try {
23517
- res = await fetch(`${root}${path81}`, {
24070
+ res = await fetch(`${root}${path82}`, {
23518
24071
  headers: { ...authHeader }
23519
24072
  });
23520
24073
  } catch (err) {
@@ -23526,16 +24079,16 @@ function createHttpClient(baseUrl, bearerToken) {
23526
24079
  const body = await res.text().catch(() => "");
23527
24080
  throw new HttpError(
23528
24081
  res.status,
23529
- `${res.status} ${res.statusText} on GET ${path81}: ${body}`,
24082
+ `${res.status} ${res.statusText} on GET ${path82}: ${body}`,
23530
24083
  body
23531
24084
  );
23532
24085
  }
23533
24086
  return await res.json();
23534
24087
  },
23535
- async post(path81, body) {
24088
+ async post(path82, body) {
23536
24089
  let res;
23537
24090
  try {
23538
- res = await fetch(`${root}${path81}`, {
24091
+ res = await fetch(`${root}${path82}`, {
23539
24092
  method: "POST",
23540
24093
  headers: { "content-type": "application/json", ...authHeader },
23541
24094
  body: JSON.stringify(body)
@@ -23549,7 +24102,7 @@ function createHttpClient(baseUrl, bearerToken) {
23549
24102
  const text = await res.text().catch(() => "");
23550
24103
  throw new HttpError(
23551
24104
  res.status,
23552
- `${res.status} ${res.statusText} on POST ${path81}: ${text}`,
24105
+ `${res.status} ${res.statusText} on POST ${path82}: ${text}`,
23553
24106
  text
23554
24107
  );
23555
24108
  }
@@ -23563,12 +24116,12 @@ function projectPath(project, suffix) {
23563
24116
  }
23564
24117
  async function runRootCause(client, input) {
23565
24118
  const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : "";
23566
- const path81 = projectPath(
24119
+ const path82 = projectPath(
23567
24120
  input.project,
23568
24121
  `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`
23569
24122
  );
23570
24123
  try {
23571
- const result = await client.get(path81);
24124
+ const result = await client.get(path82);
23572
24125
  const arrowPath = result.traversalPath.join(" \u2190 ");
23573
24126
  const provenances = result.edgeProvenances.length ? result.edgeProvenances.join(", ") : "(direct, no edges traversed)";
23574
24127
  const summary = `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` + result.rootCauseReason + (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : "");
@@ -23594,12 +24147,12 @@ async function runRootCause(client, input) {
23594
24147
  }
23595
24148
  async function runBlastRadius(client, input) {
23596
24149
  const qs = input.depth !== void 0 ? `?depth=${input.depth}` : "";
23597
- const path81 = projectPath(
24150
+ const path82 = projectPath(
23598
24151
  input.project,
23599
24152
  `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`
23600
24153
  );
23601
24154
  try {
23602
- const result = await client.get(path81);
24155
+ const result = await client.get(path82);
23603
24156
  if (result.totalAffected === 0) {
23604
24157
  return {
23605
24158
  summary: `${result.origin} has no dependents. Nothing else would break if it failed.`
@@ -23628,17 +24181,17 @@ async function runBlastRadius(client, input) {
23628
24181
  }
23629
24182
  }
23630
24183
  function formatBlastEntry(n) {
23631
- const tag = n.edgeProvenance === import_types82.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
24184
+ const tag = n.edgeProvenance === import_types83.Provenance.STALE ? " [STALE \u2014 last seen too long ago]" : "";
23632
24185
  return ` \u2022 ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`;
23633
24186
  }
23634
24187
  async function runDependencies(client, input) {
23635
24188
  const depth = input.depth ?? 3;
23636
- const path81 = projectPath(
24189
+ const path82 = projectPath(
23637
24190
  input.project,
23638
24191
  `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`
23639
24192
  );
23640
24193
  try {
23641
- const result = await client.get(path81);
24194
+ const result = await client.get(path82);
23642
24195
  if (result.total === 0) {
23643
24196
  return {
23644
24197
  summary: depth === 1 ? `${input.nodeId} has no direct dependencies in the graph.` : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`
@@ -23685,7 +24238,7 @@ async function runObservedDependencies(client, input) {
23685
24238
  if (result.observed) {
23686
24239
  return {
23687
24240
  summary: `${input.nodeId} makes no outbound runtime calls, but OTel has observed it receiving traffic on ${result.inboundObservedCount} inbound call path${result.inboundObservedCount === 1 ? "" : "s"} \u2014 it's a pure receiver.`,
23688
- provenance: import_types82.Provenance.OBSERVED
24241
+ provenance: import_types83.Provenance.OBSERVED
23689
24242
  };
23690
24243
  }
23691
24244
  const note = result.hasExtractedOutbound ? " Static (EXTRACTED) dependencies exist but no runtime traffic has been seen \u2014 is OTel running?" : "";
@@ -23695,7 +24248,7 @@ async function runObservedDependencies(client, input) {
23695
24248
  return {
23696
24249
  summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? "y" : "ies"} confirmed by OTel.`,
23697
24250
  block: blockLines.join("\n"),
23698
- provenance: import_types82.Provenance.OBSERVED
24251
+ provenance: import_types83.Provenance.OBSERVED
23699
24252
  };
23700
24253
  } catch (err) {
23701
24254
  if (err instanceof HttpError && err.status === 404) {
@@ -23730,9 +24283,9 @@ function formatDuration(ms) {
23730
24283
  return `${Math.round(h / 24)}d`;
23731
24284
  }
23732
24285
  async function runIncidents(client, input) {
23733
- const path81 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
24286
+ const path82 = input.nodeId ? projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`) : projectPath(input.project, "/incidents");
23734
24287
  try {
23735
- const body = await client.get(path81);
24288
+ const body = await client.get(path82);
23736
24289
  const events = body.events;
23737
24290
  if (events.length === 0) {
23738
24291
  return {
@@ -23749,7 +24302,7 @@ async function runIncidents(client, input) {
23749
24302
  return {
23750
24303
  summary: `${target} has ${body.total} recorded incident${body.total === 1 ? "" : "s"}; showing the ${ordered.length} most recent.`,
23751
24304
  block: blockLines.join("\n"),
23752
- provenance: import_types82.Provenance.OBSERVED
24305
+ provenance: import_types83.Provenance.OBSERVED
23753
24306
  };
23754
24307
  } catch (err) {
23755
24308
  if (err instanceof HttpError && err.status === 404) {
@@ -23858,7 +24411,7 @@ async function runStaleEdges(client, input) {
23858
24411
  return {
23859
24412
  summary: `${events.length} stale-edge transition${events.length === 1 ? "" : "s"} recorded${input.edgeType ? ` for ${input.edgeType}` : ""}.`,
23860
24413
  block: blockLines.join("\n"),
23861
- provenance: import_types82.Provenance.STALE
24414
+ provenance: import_types83.Provenance.STALE
23862
24415
  };
23863
24416
  }
23864
24417
  async function runPolicies(client, input) {
@@ -24017,10 +24570,10 @@ async function pushSnapshotToRemote(input) {
24017
24570
 
24018
24571
  // src/monitor.ts
24019
24572
  var OBSERVED_DEP_EDGE_TYPES = /* @__PURE__ */ new Set([
24020
- import_types83.EdgeType.CALLS,
24021
- import_types83.EdgeType.CONNECTS_TO,
24022
- import_types83.EdgeType.PUBLISHES_TO,
24023
- import_types83.EdgeType.CONSUMES_FROM
24573
+ import_types84.EdgeType.CALLS,
24574
+ import_types84.EdgeType.CONNECTS_TO,
24575
+ import_types84.EdgeType.PUBLISHES_TO,
24576
+ import_types84.EdgeType.CONSUMES_FROM
24024
24577
  ]);
24025
24578
  function divergenceKey(d) {
24026
24579
  const column = "column" in d && d.column ? d.column : "";
@@ -24065,7 +24618,7 @@ function formatDivergenceLine2(d) {
24065
24618
  }
24066
24619
  }
24067
24620
  function formatStaleLine(edgeId) {
24068
- const parsed = (0, import_types83.parseEdgeId)(edgeId);
24621
+ const parsed = (0, import_types84.parseEdgeId)(edgeId);
24069
24622
  if (parsed) {
24070
24623
  return `\u22EF stale ${parsed.source} \u2192 ${parsed.target} (observed edge went quiet)`;
24071
24624
  }
@@ -24078,7 +24631,7 @@ function divergenceJson(d) {
24078
24631
  return JSON.stringify({ kind: "divergence", ...d });
24079
24632
  }
24080
24633
  function staleJson(edgeId) {
24081
- const parsed = (0, import_types83.parseEdgeId)(edgeId);
24634
+ const parsed = (0, import_types84.parseEdgeId)(edgeId);
24082
24635
  return JSON.stringify({
24083
24636
  kind: "stale",
24084
24637
  edgeId,
@@ -24148,7 +24701,7 @@ var MonitorEmitter = class {
24148
24701
  // ignores non-OBSERVED edges and non-dependency edge types (structural
24149
24702
  // ownership), so only real runtime dependencies reach stdout.
24150
24703
  emitObservedEdge(edge) {
24151
- if (edge.provenance !== import_types83.Provenance.OBSERVED) return false;
24704
+ if (edge.provenance !== import_types84.Provenance.OBSERVED) return false;
24152
24705
  if (!OBSERVED_DEP_EDGE_TYPES.has(edge.type)) return false;
24153
24706
  const key = `edge|${edge.id}`;
24154
24707
  if (this.seen.has(key)) return false;
@@ -24306,7 +24859,7 @@ async function runMonitor(opts) {
24306
24859
  case "edge-added": {
24307
24860
  const payload = safeParse(frame.data);
24308
24861
  const edge = payload?.edge;
24309
- if (edge && edge.provenance === import_types83.Provenance.OBSERVED) {
24862
+ if (edge && edge.provenance === import_types84.Provenance.OBSERVED) {
24310
24863
  emitter.emitObservedEdge(edge);
24311
24864
  divergences.schedule();
24312
24865
  }
@@ -24386,7 +24939,7 @@ function sleep(ms, signal) {
24386
24939
 
24387
24940
  // src/cli-verbs.ts
24388
24941
  init_cjs_shims();
24389
- var import_node_path79 = __toESM(require("path"), 1);
24942
+ var import_node_path80 = __toESM(require("path"), 1);
24390
24943
  async function resolveProjectEntry(opts) {
24391
24944
  const entries = await listProjects();
24392
24945
  if (opts.project) {
@@ -24396,7 +24949,7 @@ async function resolveProjectEntry(opts) {
24396
24949
  const cwd = opts.cwd ?? process.cwd();
24397
24950
  const resolvedCwd = await normalizeProjectPath(cwd);
24398
24951
  for (const entry2 of entries) {
24399
- if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path79.default.sep}`)) {
24952
+ if (resolvedCwd === entry2.path || resolvedCwd.startsWith(`${entry2.path}${import_node_path80.default.sep}`)) {
24400
24953
  return entry2;
24401
24954
  }
24402
24955
  }
@@ -24549,7 +25102,7 @@ async function runSync(opts) {
24549
25102
  }
24550
25103
 
24551
25104
  // src/cli.ts
24552
- var import_types84 = require("@neat.is/types");
25105
+ var import_types85 = require("@neat.is/types");
24553
25106
  function isNpxInvocation() {
24554
25107
  if (process.env.npm_command === "exec") return true;
24555
25108
  const execpath = process.env.npm_execpath ?? "";
@@ -24920,12 +25473,12 @@ async function runInit(opts) {
24920
25473
  printDiscoveryReport(opts, services);
24921
25474
  const sections = opts.noInstall ? [] : await buildPatchSections(services, opts.project);
24922
25475
  const patch = renderPatch(sections);
24923
- const patchPath = import_node_path80.default.join(opts.scanPath, "neat.patch");
25476
+ const patchPath = import_node_path81.default.join(opts.scanPath, "neat.patch");
24924
25477
  if (opts.dryRun) {
24925
25478
  await import_node_fs46.promises.writeFile(patchPath, patch, "utf8");
24926
25479
  written.push(patchPath);
24927
25480
  console.log(`dry-run: patch written to ${patchPath}`);
24928
- const gitignorePath = import_node_path80.default.join(opts.scanPath, ".gitignore");
25481
+ const gitignorePath = import_node_path81.default.join(opts.scanPath, ".gitignore");
24929
25482
  const gitignoreExists = await import_node_fs46.promises.stat(gitignorePath).then(() => true).catch(() => false);
24930
25483
  const verb = gitignoreExists ? "append" : "create";
24931
25484
  console.log(`dry-run: would ${verb} ${gitignorePath} (add neat-out/)`);
@@ -24937,9 +25490,9 @@ async function runInit(opts) {
24937
25490
  const graph = getGraph(graphKey);
24938
25491
  const projectPaths = pathsForProject(
24939
25492
  graphKey,
24940
- import_node_path80.default.join(opts.scanPath, "neat-out")
25493
+ import_node_path81.default.join(opts.scanPath, "neat-out")
24941
25494
  );
24942
- const errorsPath = import_node_path80.default.join(import_node_path80.default.dirname(opts.outPath), import_node_path80.default.basename(projectPaths.errorsPath));
25495
+ const errorsPath = import_node_path81.default.join(import_node_path81.default.dirname(opts.outPath), import_node_path81.default.basename(projectPaths.errorsPath));
24943
25496
  const result = await extractFromDirectory(graph, opts.scanPath, { errorsPath });
24944
25497
  await saveGraphToDisk(graph, opts.outPath);
24945
25498
  written.push(opts.outPath);
@@ -25058,9 +25611,9 @@ var CLAUDE_SKILL_CONFIG = {
25058
25611
  };
25059
25612
  function claudeConfigPath() {
25060
25613
  const override = process.env.NEAT_CLAUDE_CONFIG;
25061
- if (override && override.length > 0) return import_node_path80.default.resolve(override);
25614
+ if (override && override.length > 0) return import_node_path81.default.resolve(override);
25062
25615
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
25063
- return import_node_path80.default.join(home, ".claude.json");
25616
+ return import_node_path81.default.join(home, ".claude.json");
25064
25617
  }
25065
25618
  async function runSkill(opts) {
25066
25619
  const snippet2 = JSON.stringify(CLAUDE_SKILL_CONFIG, null, 2) + "\n";
@@ -25084,7 +25637,7 @@ async function runSkill(opts) {
25084
25637
  ...existing,
25085
25638
  mcpServers: { ...mcp, neat: CLAUDE_SKILL_CONFIG.mcpServers.neat }
25086
25639
  };
25087
- await import_node_fs46.promises.mkdir(import_node_path80.default.dirname(target), { recursive: true });
25640
+ await import_node_fs46.promises.mkdir(import_node_path81.default.dirname(target), { recursive: true });
25088
25641
  await import_node_fs46.promises.writeFile(target, JSON.stringify(merged, null, 2) + "\n", "utf8");
25089
25642
  console.log(`neat skill: wrote mcpServers.neat to ${target}`);
25090
25643
  console.log("restart Claude Code to pick up the new MCP server.");
@@ -25171,12 +25724,12 @@ async function main() {
25171
25724
  console.error("neat init: --apply and --dry-run are mutually exclusive");
25172
25725
  process.exit(2);
25173
25726
  }
25174
- const scanPath = import_node_path80.default.resolve(target);
25727
+ const scanPath = import_node_path81.default.resolve(target);
25175
25728
  const projectExplicit = parsed.project !== null;
25176
- const projectName = projectExplicit ? project : import_node_path80.default.basename(scanPath);
25729
+ const projectName = projectExplicit ? project : import_node_path81.default.basename(scanPath);
25177
25730
  const projectKey = projectExplicit ? project : DEFAULT_PROJECT;
25178
- const fallback = pathsForProject(projectKey, import_node_path80.default.join(scanPath, "neat-out")).snapshotPath;
25179
- const outPath = import_node_path80.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
25731
+ const fallback = pathsForProject(projectKey, import_node_path81.default.join(scanPath, "neat-out")).snapshotPath;
25732
+ const outPath = import_node_path81.default.resolve(process.env.NEAT_OUT_PATH ?? fallback);
25180
25733
  const result = await runInit({
25181
25734
  scanPath,
25182
25735
  outPath,
@@ -25197,21 +25750,21 @@ async function main() {
25197
25750
  usage4();
25198
25751
  process.exit(2);
25199
25752
  }
25200
- const scanPath = import_node_path80.default.resolve(target);
25753
+ const scanPath = import_node_path81.default.resolve(target);
25201
25754
  const stat = await import_node_fs46.promises.stat(scanPath).catch(() => null);
25202
25755
  if (!stat || !stat.isDirectory()) {
25203
25756
  console.error(`neat watch: ${scanPath} is not a directory`);
25204
25757
  process.exit(2);
25205
25758
  }
25206
- const projectPaths = pathsForProject(project, import_node_path80.default.join(scanPath, "neat-out"));
25207
- const outPath = import_node_path80.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
25208
- const errorsPath = import_node_path80.default.resolve(
25209
- process.env.NEAT_ERRORS_PATH ?? import_node_path80.default.join(import_node_path80.default.dirname(outPath), import_node_path80.default.basename(projectPaths.errorsPath))
25759
+ const projectPaths = pathsForProject(project, import_node_path81.default.join(scanPath, "neat-out"));
25760
+ const outPath = import_node_path81.default.resolve(process.env.NEAT_OUT_PATH ?? projectPaths.snapshotPath);
25761
+ const errorsPath = import_node_path81.default.resolve(
25762
+ process.env.NEAT_ERRORS_PATH ?? import_node_path81.default.join(import_node_path81.default.dirname(outPath), import_node_path81.default.basename(projectPaths.errorsPath))
25210
25763
  );
25211
- const staleEventsPath = import_node_path80.default.resolve(
25212
- process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path80.default.join(import_node_path80.default.dirname(outPath), import_node_path80.default.basename(projectPaths.staleEventsPath))
25764
+ const staleEventsPath = import_node_path81.default.resolve(
25765
+ process.env.NEAT_STALE_EVENTS_PATH ?? import_node_path81.default.join(import_node_path81.default.dirname(outPath), import_node_path81.default.basename(projectPaths.staleEventsPath))
25213
25766
  );
25214
- const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path80.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
25767
+ const embeddingsCachePath = process.env.NEAT_EMBEDDINGS_CACHE_PATH ? import_node_path81.default.resolve(process.env.NEAT_EMBEDDINGS_CACHE_PATH) : void 0;
25215
25768
  const handle = await startWatch(getGraph(project), {
25216
25769
  scanPath,
25217
25770
  outPath,
@@ -25220,7 +25773,7 @@ async function main() {
25220
25773
  project,
25221
25774
  // Resolve NEAT_HOME so a `neat watch` picks up connectors added to
25222
25775
  // ~/.neat/connectors.json (#871). Same resolution the rest of the CLI uses.
25223
- neatHome: process.env.NEAT_HOME ? import_node_path80.default.resolve(process.env.NEAT_HOME) : import_node_path80.default.join(import_node_os8.default.homedir(), ".neat"),
25776
+ neatHome: process.env.NEAT_HOME ? import_node_path81.default.resolve(process.env.NEAT_HOME) : import_node_path81.default.join(import_node_os8.default.homedir(), ".neat"),
25224
25777
  ...embeddingsCachePath ? { embeddingsCachePath } : {},
25225
25778
  host: process.env.HOST ?? "0.0.0.0",
25226
25779
  port: Number(process.env.PORT ?? 8080),
@@ -25402,11 +25955,11 @@ async function main() {
25402
25955
  process.exit(1);
25403
25956
  }
25404
25957
  async function tryOrchestrator(cmd, parsed) {
25405
- const scanPath = import_node_path80.default.resolve(cmd);
25958
+ const scanPath = import_node_path81.default.resolve(cmd);
25406
25959
  const stat = await import_node_fs46.promises.stat(scanPath).catch(() => null);
25407
25960
  if (!stat || !stat.isDirectory()) return null;
25408
25961
  const projectExplicit = parsed.project !== null;
25409
- const projectName = projectExplicit ? parsed.project : import_node_path80.default.basename(scanPath);
25962
+ const projectName = projectExplicit ? parsed.project : import_node_path81.default.basename(scanPath);
25410
25963
  const result = await runOrchestrator({
25411
25964
  scanPath,
25412
25965
  project: projectName,
@@ -25595,10 +26148,10 @@ async function runQueryVerb(cmd, parsed) {
25595
26148
  const parts = parsed.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
25596
26149
  const out = [];
25597
26150
  for (const p of parts) {
25598
- const r = import_types84.DivergenceTypeSchema.safeParse(p);
26151
+ const r = import_types85.DivergenceTypeSchema.safeParse(p);
25599
26152
  if (!r.success) {
25600
26153
  console.error(
25601
- `neat divergences: unknown --type "${p}". allowed: ${import_types84.DivergenceTypeSchema.options.join(", ")}`
26154
+ `neat divergences: unknown --type "${p}". allowed: ${import_types85.DivergenceTypeSchema.options.join(", ")}`
25602
26155
  );
25603
26156
  return 2;
25604
26157
  }