@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/index.cjs CHANGED
@@ -60,8 +60,8 @@ function mountBearerAuth(app, opts) {
60
60
  ]);
61
61
  const publicRead = opts.publicRead === true;
62
62
  app.addHook("preHandler", (req, reply, done) => {
63
- const path67 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
- if (exactUnauthPaths.has(path67) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path67)) {
63
+ const path68 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
64
+ if (exactUnauthPaths.has(path68) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path68)) {
65
65
  done();
66
66
  return;
67
67
  }
@@ -342,7 +342,7 @@ function pickEnv(spanAttrs, resourceAttrs) {
342
342
  return ENV_FALLBACK;
343
343
  }
344
344
  function normalizeDbSystem(attrs) {
345
- const raw = attrs["db.system"];
345
+ const raw = attrs["db.system"] ?? attrs["db.system.name"];
346
346
  if (typeof raw !== "string") return void 0;
347
347
  return raw === "mongoose" ? "mongodb" : raw;
348
348
  }
@@ -414,8 +414,8 @@ function websocketChannelPathOf(attrs) {
414
414
  const v = attrs[key];
415
415
  if (typeof v === "string" && v.length > 0) {
416
416
  const q = v.indexOf("?");
417
- const path67 = q === -1 ? v : v.slice(0, q);
418
- if (path67.length > 0) return path67;
417
+ const path68 = q === -1 ? v : v.slice(0, q);
418
+ if (path68.length > 0) return path68;
419
419
  }
420
420
  }
421
421
  return void 0;
@@ -434,6 +434,9 @@ function parseOtlpRequest(body) {
434
434
  for (const ss of rs.scopeSpans ?? []) {
435
435
  for (const span of ss.spans ?? []) {
436
436
  const attrs = attrsToRecord(span.attributes);
437
+ const dbSqlText = typeof attrs["db.statement"] === "string" ? attrs["db.statement"] : typeof attrs["db.query.text"] === "string" ? attrs["db.query.text"] : void 0;
438
+ const dbSystemName = normalizeDbSystem(attrs);
439
+ 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;
437
440
  const parsed = {
438
441
  service,
439
442
  resourceServiceNamePresent,
@@ -448,11 +451,11 @@ function parseOtlpRequest(body) {
448
451
  durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
449
452
  env: pickEnv(attrs, resourceAttrs),
450
453
  attributes: attrs,
451
- dbSystem: normalizeDbSystem(attrs),
454
+ dbSystem: dbSystemName,
452
455
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
453
456
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
454
- dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
455
- dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
457
+ dbTable: directDbTable ?? (dbSqlText ? tableFromSqlStatement(dbSqlText) ?? void 0 : void 0),
458
+ dbColumns: dbSqlText ? columnsFromSqlStatement(dbSqlText) : void 0,
456
459
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
457
460
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
458
461
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -1314,19 +1317,19 @@ function confidenceFromMix(edges, now = Date.now()) {
1314
1317
  function longestIncomingWalk(graph, start, maxDepth) {
1315
1318
  let best = { path: [start], edges: [] };
1316
1319
  const visited = /* @__PURE__ */ new Set([start]);
1317
- function step(node, path67, edges) {
1318
- if (path67.length > best.path.length) {
1319
- best = { path: [...path67], edges: [...edges] };
1320
+ function step(node, path68, edges) {
1321
+ if (path68.length > best.path.length) {
1322
+ best = { path: [...path68], edges: [...edges] };
1320
1323
  }
1321
- if (path67.length - 1 >= maxDepth) return;
1324
+ if (path68.length - 1 >= maxDepth) return;
1322
1325
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
1323
1326
  for (const [srcId, edge] of incoming) {
1324
1327
  if (visited.has(srcId)) continue;
1325
1328
  visited.add(srcId);
1326
- path67.push(srcId);
1329
+ path68.push(srcId);
1327
1330
  edges.push(edge);
1328
- step(srcId, path67, edges);
1329
- path67.pop();
1331
+ step(srcId, path68, edges);
1332
+ path68.pop();
1330
1333
  edges.pop();
1331
1334
  visited.delete(srcId);
1332
1335
  }
@@ -1334,11 +1337,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
1334
1337
  step(start, [start], []);
1335
1338
  return best;
1336
1339
  }
1337
- function databaseRootCauseShape(graph, origin, walk8) {
1340
+ function databaseRootCauseShape(graph, origin, walk9) {
1338
1341
  const targetDb = origin;
1339
1342
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
1340
1343
  if (candidatePairs.length === 0) return null;
1341
- for (const id of walk8.path) {
1344
+ for (const id of walk9.path) {
1342
1345
  const owner = resolveOwningService(graph, id);
1343
1346
  if (!owner) continue;
1344
1347
  const { id: serviceId9, svc } = owner;
@@ -1365,8 +1368,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
1365
1368
  }
1366
1369
  return null;
1367
1370
  }
1368
- function serviceRootCauseShape(graph, _origin, walk8) {
1369
- for (const id of walk8.path) {
1371
+ function serviceRootCauseShape(graph, _origin, walk9) {
1372
+ for (const id of walk9.path) {
1370
1373
  const owner = resolveOwningService(graph, id);
1371
1374
  if (!owner) continue;
1372
1375
  const { id: serviceId9, svc } = owner;
@@ -1402,15 +1405,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
1402
1405
  }
1403
1406
  return null;
1404
1407
  }
1405
- function fileRootCauseShape(graph, origin, walk8) {
1408
+ function fileRootCauseShape(graph, origin, walk9) {
1406
1409
  const owner = resolveOwningService(graph, origin.id);
1407
1410
  if (!owner) return null;
1408
- return serviceRootCauseShape(graph, owner.svc, walk8);
1411
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1409
1412
  }
1410
- function symbolRootCauseShape(graph, origin, walk8) {
1413
+ function symbolRootCauseShape(graph, origin, walk9) {
1411
1414
  const owner = resolveOwningService(graph, origin.id);
1412
1415
  if (!owner) return null;
1413
- return serviceRootCauseShape(graph, owner.svc, walk8);
1416
+ return serviceRootCauseShape(graph, owner.svc, walk9);
1414
1417
  }
1415
1418
  var rootCauseShapes = {
1416
1419
  [import_types.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -1423,16 +1426,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
1423
1426
  const origin = graph.getNodeAttributes(errorNodeId);
1424
1427
  const shape = rootCauseShapes[origin.type];
1425
1428
  if (shape) {
1426
- const walk8 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1427
- const match = shape(graph, origin, walk8);
1429
+ const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
1430
+ const match = shape(graph, origin, walk9);
1428
1431
  if (match) {
1429
1432
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
1430
1433
  return import_types.RootCauseResultSchema.parse({
1431
1434
  rootCauseNode: match.rootCauseNode,
1432
1435
  rootCauseReason: reason,
1433
- traversalPath: walk8.path,
1434
- edgeProvenances: walk8.edges.map((e) => e.provenance),
1435
- confidence: confidenceFromMix(walk8.edges),
1436
+ traversalPath: walk9.path,
1437
+ edgeProvenances: walk9.edges.map((e) => e.provenance),
1438
+ confidence: confidenceFromMix(walk9.edges),
1436
1439
  fixRecommendation: match.fixRecommendation
1437
1440
  });
1438
1441
  }
@@ -1533,26 +1536,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
1533
1536
  return best;
1534
1537
  }
1535
1538
  function followFailingCallChain(graph, originServiceId, maxDepth) {
1536
- const path67 = [originServiceId];
1539
+ const path68 = [originServiceId];
1537
1540
  const edges = [];
1538
1541
  const visited = /* @__PURE__ */ new Set([originServiceId]);
1539
1542
  let current = originServiceId;
1540
1543
  for (let depth = 0; depth < maxDepth; depth++) {
1541
1544
  const hop = dominantFailingCall(graph, current, visited);
1542
1545
  if (!hop) break;
1543
- path67.push(hop.nextService);
1546
+ path68.push(hop.nextService);
1544
1547
  edges.push(hop.edge);
1545
1548
  visited.add(hop.nextService);
1546
1549
  current = hop.nextService;
1547
1550
  }
1548
1551
  if (edges.length === 0) return null;
1549
- return { path: path67, edges, culprit: current };
1552
+ return { path: path68, edges, culprit: current };
1550
1553
  }
1551
1554
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1552
1555
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
1553
1556
  if (!chain) return null;
1554
1557
  const culprit = chain.culprit;
1555
- const path67 = [...chain.path];
1558
+ const path68 = [...chain.path];
1556
1559
  const edgeProvenances = chain.edges.map((e) => e.provenance);
1557
1560
  const baseConfidence = confidenceFromMix(chain.edges);
1558
1561
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -1560,14 +1563,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1560
1563
  if (loc) {
1561
1564
  let rootCauseNode = culprit;
1562
1565
  if (loc.fileNode) {
1563
- path67.push(loc.fileNode);
1566
+ path68.push(loc.fileNode);
1564
1567
  edgeProvenances.push(import_types.Provenance.OBSERVED);
1565
1568
  rootCauseNode = loc.fileNode;
1566
1569
  }
1567
1570
  return import_types.RootCauseResultSchema.parse({
1568
1571
  rootCauseNode,
1569
1572
  rootCauseReason: loc.rootCauseReason,
1570
- traversalPath: path67,
1573
+ traversalPath: path68,
1571
1574
  edgeProvenances,
1572
1575
  confidence,
1573
1576
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -1579,7 +1582,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
1579
1582
  return import_types.RootCauseResultSchema.parse({
1580
1583
  rootCauseNode: culprit,
1581
1584
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
1582
- traversalPath: path67,
1585
+ traversalPath: path68,
1583
1586
  edgeProvenances,
1584
1587
  confidence,
1585
1588
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -2514,14 +2517,14 @@ function buildServiceHostIndex(services) {
2514
2517
  }
2515
2518
  async function walkSourceFiles(dir) {
2516
2519
  const out = [];
2517
- async function walk8(current) {
2520
+ async function walk9(current) {
2518
2521
  const entries = await import_node_fs5.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2519
2522
  for (const entry of entries) {
2520
2523
  const full = import_node_path5.default.join(current, entry.name);
2521
2524
  if (entry.isDirectory()) {
2522
2525
  if (IGNORED_DIRS.has(entry.name)) continue;
2523
2526
  if (await isPythonVenvDir(full)) continue;
2524
- await walk8(full);
2527
+ await walk9(full);
2525
2528
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path5.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2526
2529
  // would attribute our instrumentation imports to the user's service.
2527
2530
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2529,7 +2532,7 @@ async function walkSourceFiles(dir) {
2529
2532
  }
2530
2533
  }
2531
2534
  }
2532
- await walk8(dir);
2535
+ await walk9(dir);
2533
2536
  return out;
2534
2537
  }
2535
2538
  async function loadSourceFiles(dir) {
@@ -3043,7 +3046,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
3043
3046
  ]);
3044
3047
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
3045
3048
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
3046
- function ginRoutesFromSource(source, parser) {
3049
+ function goRouterRoutesFromSource(source, parser, framework) {
3047
3050
  const tree = parseSource2(parser, source);
3048
3051
  const prefixes = /* @__PURE__ */ new Map();
3049
3052
  const out = [];
@@ -3053,10 +3056,12 @@ function ginRoutesFromSource(source, parser) {
3053
3056
  const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
3054
3057
  if (name && value?.type === "call_expression") {
3055
3058
  const fn2 = value.childForFieldName("function");
3056
- const field = fn2?.childForFieldName("field")?.text;
3057
- const first2 = value.childForFieldName("arguments")?.namedChild(0);
3058
- if (field === "Group" && first2?.type === "interpreted_string_literal") {
3059
- prefixes.set(name, first2.text.slice(1, -1));
3059
+ if (fn2?.childForFieldName("field")?.text === "Group") {
3060
+ const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
3061
+ if (leaf2 !== null) {
3062
+ const parent = fn2.childForFieldName("operand")?.text ?? "";
3063
+ prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
3064
+ }
3060
3065
  }
3061
3066
  }
3062
3067
  return;
@@ -3067,18 +3072,32 @@ function ginRoutesFromSource(source, parser) {
3067
3072
  const method = fn.childForFieldName("field")?.text?.toUpperCase();
3068
3073
  if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
3069
3074
  const receiver = fn.childForFieldName("operand")?.text ?? "";
3070
- const first = node.childForFieldName("arguments")?.namedChild(0);
3071
- if (first?.type !== "interpreted_string_literal") return;
3072
- const leaf = first.text.slice(1, -1);
3075
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
3076
+ if (leaf === null) return;
3073
3077
  out.push({
3074
- method: method === "ALL" ? "ALL" : method,
3078
+ method,
3075
3079
  pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
3076
3080
  line: node.startPosition.row + 1,
3077
- framework: "gin"
3081
+ framework
3078
3082
  });
3079
3083
  });
3080
3084
  return out;
3081
3085
  }
3086
+ function goStringLiteral(node) {
3087
+ if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
3088
+ return node.text.slice(1, -1);
3089
+ }
3090
+ return null;
3091
+ }
3092
+ function ginRoutesFromSource(source, parser) {
3093
+ return goRouterRoutesFromSource(source, parser, "gin");
3094
+ }
3095
+ function echoRoutesFromSource(source, parser) {
3096
+ return goRouterRoutesFromSource(source, parser, "echo");
3097
+ }
3098
+ function fiberRoutesFromSource(source, parser) {
3099
+ return goRouterRoutesFromSource(source, parser, "fiber");
3100
+ }
3082
3101
  var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
3083
3102
  var NESTJS_METHODS = /* @__PURE__ */ new Map([
3084
3103
  ["Get", "GET"],
@@ -3663,9 +3682,9 @@ function rubyRocketRoute(args) {
3663
3682
  if (!pair || pair.type !== "pair") continue;
3664
3683
  const k = pair.childForFieldName("key");
3665
3684
  if (k?.type !== "string") continue;
3666
- const path67 = rubyLiteral(k);
3667
- if (path67 === null) continue;
3668
- return { path: path67, target: rubyLiteral(pair.childForFieldName("value")) };
3685
+ const path68 = rubyLiteral(k);
3686
+ if (path68 === null) continue;
3687
+ return { path: path68, target: rubyLiteral(pair.childForFieldName("value")) };
3669
3688
  }
3670
3689
  return null;
3671
3690
  }
@@ -4342,9 +4361,11 @@ async function addRoutes(graph, services) {
4342
4361
  const hasFlask = deps["flask"] !== void 0;
4343
4362
  const hasDjango = deps["django"] !== void 0;
4344
4363
  const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
4364
+ const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
4365
+ const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
4345
4366
  const hasRails = deps["rails"] !== void 0;
4346
4367
  const hasLaravel = deps["laravel/framework"] !== void 0;
4347
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
4368
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
4348
4369
  continue;
4349
4370
  const files = await loadSourceFiles(service.dir);
4350
4371
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4368,7 +4389,10 @@ async function addRoutes(graph, services) {
4368
4389
  } else if (isRb) {
4369
4390
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4370
4391
  } else if (isGo) {
4371
- routes = hasGin ? ginRoutesFromSource(file.content, goParser) : [];
4392
+ if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4393
+ else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
4394
+ else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
4395
+ else routes = [];
4372
4396
  } else if (isPy) {
4373
4397
  routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
4374
4398
  if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
@@ -5978,6 +6002,12 @@ function parseGoMod(source) {
5978
6002
  }
5979
6003
  return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
5980
6004
  }
6005
+ function goFramework(deps) {
6006
+ if (deps["github.com/gin-gonic/gin"]) return "gin";
6007
+ if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
6008
+ if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
6009
+ return void 0;
6010
+ }
5981
6011
  async function discoverGoService(scanPath, dir) {
5982
6012
  let raw;
5983
6013
  try {
@@ -5989,6 +6019,7 @@ async function discoverGoService(scanPath, dir) {
5989
6019
  if (!mod) return null;
5990
6020
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
5991
6021
  const pkg = { name, dependencies: mod.dependencies };
6022
+ const framework = goFramework(mod.dependencies);
5992
6023
  const node = {
5993
6024
  id: (0, import_types9.serviceId)(name),
5994
6025
  type: import_types9.NodeType.ServiceNode,
@@ -5996,7 +6027,7 @@ async function discoverGoService(scanPath, dir) {
5996
6027
  language: "go",
5997
6028
  dependencies: mod.dependencies,
5998
6029
  repoPath: import_node_path10.default.relative(scanPath, dir),
5999
- ...mod.dependencies["github.com/gin-gonic/gin"] ? { framework: "gin" } : {}
6030
+ ...framework ? { framework } : {}
6000
6031
  };
6001
6032
  return { pkg, dir, node };
6002
6033
  }
@@ -6896,7 +6927,7 @@ async function addSymbolEdges(graph, services) {
6896
6927
  return best;
6897
6928
  };
6898
6929
  const requests = [];
6899
- const walk8 = (node) => {
6930
+ const walk9 = (node) => {
6900
6931
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
6901
6932
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
6902
6933
  if (self && self.kind === "class") {
@@ -6942,10 +6973,10 @@ async function addSymbolEdges(graph, services) {
6942
6973
  }
6943
6974
  for (let i = 0; i < node.namedChildCount; i++) {
6944
6975
  const child = node.namedChild(i);
6945
- if (child) walk8(child);
6976
+ if (child) walk9(child);
6946
6977
  }
6947
6978
  };
6948
- walk8(root);
6979
+ walk9(root);
6949
6980
  for (const req of requests) {
6950
6981
  const targetSid = resolveTarget(req.targetName, req.wantKind);
6951
6982
  if (!targetSid) continue;
@@ -7958,20 +7989,20 @@ var import_node_path28 = __toESM(require("path"), 1);
7958
7989
  var import_types18 = require("@neat.is/types");
7959
7990
  async function walkConfigFiles(dir) {
7960
7991
  const out = [];
7961
- async function walk8(current) {
7992
+ async function walk9(current) {
7962
7993
  const entries = await import_node_fs16.promises.readdir(current, { withFileTypes: true });
7963
7994
  for (const entry of entries) {
7964
7995
  const full = import_node_path28.default.join(current, entry.name);
7965
7996
  if (entry.isDirectory()) {
7966
7997
  if (IGNORED_DIRS.has(entry.name)) continue;
7967
7998
  if (await isPythonVenvDir(full)) continue;
7968
- await walk8(full);
7999
+ await walk9(full);
7969
8000
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
7970
8001
  out.push(full);
7971
8002
  }
7972
8003
  }
7973
8004
  }
7974
- await walk8(dir);
8005
+ await walk9(dir);
7975
8006
  return out;
7976
8007
  }
7977
8008
  async function addConfigNodes(graph, services, scanPath) {
@@ -8061,20 +8092,20 @@ function grpcMethodsFromProto(content, fqPackage) {
8061
8092
  }
8062
8093
  async function walkProtoFiles(dir) {
8063
8094
  const out = [];
8064
- async function walk8(current) {
8095
+ async function walk9(current) {
8065
8096
  const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8066
8097
  for (const entry of entries) {
8067
8098
  const full = import_node_path29.default.join(current, entry.name);
8068
8099
  if (entry.isDirectory()) {
8069
8100
  if (IGNORED_DIRS.has(entry.name)) continue;
8070
8101
  if (await isPythonVenvDir(full)) continue;
8071
- await walk8(full);
8102
+ await walk9(full);
8072
8103
  } else if (entry.isFile() && import_node_path29.default.extname(entry.name) === PROTO_EXTENSION) {
8073
8104
  out.push(full);
8074
8105
  }
8075
8106
  }
8076
8107
  }
8077
- await walk8(dir);
8108
+ await walk9(dir);
8078
8109
  return out;
8079
8110
  }
8080
8111
  async function addGrpcMethods(graph, services) {
@@ -8142,7 +8173,7 @@ async function addGrpcMethods(graph, services) {
8142
8173
 
8143
8174
  // src/extract/calls/index.ts
8144
8175
  init_cjs_shims();
8145
- var import_types36 = require("@neat.is/types");
8176
+ var import_types37 = require("@neat.is/types");
8146
8177
 
8147
8178
  // src/extract/calls/http.ts
8148
8179
  init_cjs_shims();
@@ -8896,7 +8927,7 @@ function isFirestoreClientFactory(node) {
8896
8927
  }
8897
8928
  function firestoreClientVars(root) {
8898
8929
  const vars = /* @__PURE__ */ new Set();
8899
- const walk8 = (node) => {
8930
+ const walk9 = (node) => {
8900
8931
  if (node.type === "variable_declarator") {
8901
8932
  const name = node.childForFieldName("name");
8902
8933
  let value = node.childForFieldName("value");
@@ -8905,9 +8936,9 @@ function firestoreClientVars(root) {
8905
8936
  vars.add(name.text);
8906
8937
  }
8907
8938
  }
8908
- for (const c of namedChildren(node)) walk8(c);
8939
+ for (const c of namedChildren(node)) walk9(c);
8909
8940
  };
8910
- walk8(root);
8941
+ walk9(root);
8911
8942
  return vars;
8912
8943
  }
8913
8944
  function isClientExpr(node, clientVars) {
@@ -9062,7 +9093,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9062
9093
  }
9063
9094
  s.add(field);
9064
9095
  };
9065
- const walk8 = (node) => {
9096
+ const walk9 = (node) => {
9066
9097
  if (node.type === "call_expression") {
9067
9098
  const fn = node.childForFieldName("function");
9068
9099
  const line = node.startPosition.row + 1;
@@ -9102,9 +9133,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9102
9133
  }
9103
9134
  }
9104
9135
  }
9105
- for (const c of namedChildren(node)) walk8(c);
9136
+ for (const c of namedChildren(node)) walk9(c);
9106
9137
  };
9107
- walk8(tree.rootNode);
9138
+ walk9(tree.rootNode);
9108
9139
  const out = [];
9109
9140
  for (const [collPath, line] of collLine) {
9110
9141
  const byField = writes.get(collPath);
@@ -9899,7 +9930,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9899
9930
  const tree = parseSource3(parserForExt2(import_node_path41.default.extname(file.path)), file.content);
9900
9931
  const out = [];
9901
9932
  const seen = /* @__PURE__ */ new Set();
9902
- const walk8 = (node) => {
9933
+ const walk9 = (node) => {
9903
9934
  if (node.type === "call_expression") {
9904
9935
  const fn = node.childForFieldName("function");
9905
9936
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9927,9 +9958,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
9927
9958
  }
9928
9959
  }
9929
9960
  }
9930
- for (const c of namedChildren4(node)) walk8(c);
9961
+ for (const c of namedChildren4(node)) walk9(c);
9931
9962
  };
9932
- walk8(tree.rootNode);
9963
+ walk9(tree.rootNode);
9933
9964
  return out;
9934
9965
  }
9935
9966
  function enclosingVarName(call) {
@@ -9951,7 +9982,7 @@ function enclosingVarName(call) {
9951
9982
  function collectDrizzleTables(root) {
9952
9983
  const tables = [];
9953
9984
  const varToTable = /* @__PURE__ */ new Map();
9954
- const walk8 = (node) => {
9985
+ const walk9 = (node) => {
9955
9986
  if (node.type === "call_expression") {
9956
9987
  const fn = node.childForFieldName("function");
9957
9988
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -9966,9 +9997,9 @@ function collectDrizzleTables(root) {
9966
9997
  }
9967
9998
  }
9968
9999
  }
9969
- for (const c of namedChildren4(node)) walk8(c);
10000
+ for (const c of namedChildren4(node)) walk9(c);
9970
10001
  };
9971
- walk8(root);
10002
+ walk9(root);
9972
10003
  return { tables, varToTable };
9973
10004
  }
9974
10005
  function referencesTargetVar(call) {
@@ -9991,7 +10022,7 @@ function drizzleForeignKeys(file, serviceDir) {
9991
10022
  const seen = /* @__PURE__ */ new Set();
9992
10023
  for (const table of tables) {
9993
10024
  if (!table.object) continue;
9994
- const walk8 = (node) => {
10025
+ const walk9 = (node) => {
9995
10026
  if (node.type === "call_expression") {
9996
10027
  const targetVar = referencesTargetVar(node);
9997
10028
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -10012,9 +10043,9 @@ function drizzleForeignKeys(file, serviceDir) {
10012
10043
  }
10013
10044
  }
10014
10045
  }
10015
- for (const c of namedChildren4(node)) walk8(c);
10046
+ for (const c of namedChildren4(node)) walk9(c);
10016
10047
  };
10017
- walk8(table.object);
10048
+ walk9(table.object);
10018
10049
  }
10019
10050
  return out;
10020
10051
  }
@@ -11094,15 +11125,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
11094
11125
  return out;
11095
11126
  }
11096
11127
 
11128
+ // src/extract/calls/gorm.ts
11129
+ init_cjs_shims();
11130
+ var import_node_path48 = __toESM(require("path"), 1);
11131
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
11132
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
11133
+ var import_types36 = require("@neat.is/types");
11134
+ var GORM_IMPORT_RE = /gorm\.io\/gorm/;
11135
+ var PARSE_CHUNK11 = 16384;
11136
+ function makeGoParser3() {
11137
+ const p = new import_tree_sitter15.default();
11138
+ p.setLanguage(import_tree_sitter_go4.default);
11139
+ return p;
11140
+ }
11141
+ function parseSource10(parser, source) {
11142
+ return parser.parse(
11143
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
11144
+ );
11145
+ }
11146
+ function walk8(node, visit) {
11147
+ visit(node);
11148
+ for (let i = 0; i < node.namedChildCount; i++) {
11149
+ const c = node.namedChild(i);
11150
+ if (c) walk8(c, visit);
11151
+ }
11152
+ }
11153
+ var COMMON_INITIALISMS = [
11154
+ "ASCII",
11155
+ "HTTPS",
11156
+ "UTF8",
11157
+ "XSRF",
11158
+ "HTML",
11159
+ "HTTP",
11160
+ "JSON",
11161
+ "UUID",
11162
+ "XMPP",
11163
+ "ACL",
11164
+ "API",
11165
+ "CPU",
11166
+ "CSS",
11167
+ "DNS",
11168
+ "EOF",
11169
+ "GUID",
11170
+ "LHS",
11171
+ "QPS",
11172
+ "RAM",
11173
+ "RHS",
11174
+ "RPC",
11175
+ "SLA",
11176
+ "SQL",
11177
+ "SSH",
11178
+ "TCP",
11179
+ "TLS",
11180
+ "TTL",
11181
+ "UDP",
11182
+ "UID",
11183
+ "URI",
11184
+ "URL",
11185
+ "UID",
11186
+ "XSS",
11187
+ "ID",
11188
+ "IP",
11189
+ "UI",
11190
+ "VM",
11191
+ "XML"
11192
+ ].sort((a, b) => b.length - a.length);
11193
+ function titleCase(word) {
11194
+ return word.charAt(0) + word.slice(1).toLowerCase();
11195
+ }
11196
+ function replaceInitialisms(name) {
11197
+ let out = "";
11198
+ let i = 0;
11199
+ while (i < name.length) {
11200
+ let matched = false;
11201
+ for (const init of COMMON_INITIALISMS) {
11202
+ if (name.startsWith(init, i)) {
11203
+ out += titleCase(init);
11204
+ i += init.length;
11205
+ matched = true;
11206
+ break;
11207
+ }
11208
+ }
11209
+ if (!matched) {
11210
+ out += name[i];
11211
+ i++;
11212
+ }
11213
+ }
11214
+ return out;
11215
+ }
11216
+ var isUpper = (c) => c >= "A" && c <= "Z";
11217
+ var isDigit = (c) => c >= "0" && c <= "9";
11218
+ function toDBName(name) {
11219
+ if (name === "") return "";
11220
+ const value = replaceInitialisms(name);
11221
+ if (value.length === 1) return value.toLowerCase();
11222
+ let buf = "";
11223
+ let lastCase = false;
11224
+ let curCase = isUpper(value[0]);
11225
+ for (let i = 0; i < value.length - 1; i++) {
11226
+ const v = value[i];
11227
+ const nextCase = isUpper(value[i + 1]);
11228
+ const nextNumber = isDigit(value[i + 1]);
11229
+ if (curCase) {
11230
+ if (lastCase && (nextCase || nextNumber)) {
11231
+ buf += v.toLowerCase();
11232
+ } else {
11233
+ if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
11234
+ buf += v.toLowerCase();
11235
+ }
11236
+ } else {
11237
+ buf += v;
11238
+ }
11239
+ lastCase = curCase;
11240
+ curCase = nextCase;
11241
+ }
11242
+ const last = value[value.length - 1];
11243
+ if (curCase) {
11244
+ if (!lastCase && value.length > 1) buf += "_";
11245
+ buf += last.toLowerCase();
11246
+ } else {
11247
+ buf += last;
11248
+ }
11249
+ return buf;
11250
+ }
11251
+ var UNCOUNTABLE = /* @__PURE__ */ new Set([
11252
+ "equipment",
11253
+ "information",
11254
+ "rice",
11255
+ "money",
11256
+ "species",
11257
+ "series",
11258
+ "fish",
11259
+ "sheep",
11260
+ "jeans",
11261
+ "police"
11262
+ ]);
11263
+ var IRREGULAR = [
11264
+ ["person", "people"],
11265
+ ["man", "men"],
11266
+ ["child", "children"],
11267
+ ["sex", "sexes"],
11268
+ ["move", "moves"]
11269
+ ];
11270
+ var PLURAL_RULES = [
11271
+ [/(quiz)$/i, "$1zes"],
11272
+ [/^(ox)$/i, "$1en"],
11273
+ [/([ml])ouse$/i, "$1ice"],
11274
+ [/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
11275
+ [/(x|ch|ss|sh)$/i, "$1es"],
11276
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
11277
+ [/(hive)$/i, "$1s"],
11278
+ [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
11279
+ [/sis$/i, "ses"],
11280
+ [/([ti])um$/i, "$1a"],
11281
+ [/([ti])a$/i, "$1a"],
11282
+ [/(buffal|tomat)o$/i, "$1oes"],
11283
+ [/(bu)s$/i, "$1ses"],
11284
+ [/(alias|status)$/i, "$1es"],
11285
+ [/(octop|vir)i$/i, "$1i"],
11286
+ [/(octop|vir)us$/i, "$1i"],
11287
+ [/(ax|test)is$/i, "$1es"],
11288
+ [/s$/i, "s"]
11289
+ ];
11290
+ function pluralize3(word) {
11291
+ if (word === "") return word;
11292
+ const lower = word.toLowerCase();
11293
+ for (const u of UNCOUNTABLE) {
11294
+ if (lower === u || lower.endsWith("_" + u)) return word;
11295
+ }
11296
+ for (const [sing, plur] of IRREGULAR) {
11297
+ const re = new RegExp(sing + "$", "i");
11298
+ if (re.test(word)) return word.replace(re, plur);
11299
+ }
11300
+ for (const [re, rep] of PLURAL_RULES) {
11301
+ if (re.test(word)) return word.replace(re, rep);
11302
+ }
11303
+ return word + "s";
11304
+ }
11305
+ function deriveTableName(structName) {
11306
+ return pluralize3(toDBName(structName));
11307
+ }
11308
+ function stringLiteralValue(node) {
11309
+ if (!node) return null;
11310
+ if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
11311
+ const t = node.text;
11312
+ return t.length >= 2 ? t.slice(1, -1) : "";
11313
+ }
11314
+ return null;
11315
+ }
11316
+ function parseGormTag(tagNode) {
11317
+ const tag = {};
11318
+ if (!tagNode) return tag;
11319
+ let inner = tagNode.text;
11320
+ if (inner.length >= 2) inner = inner.slice(1, -1);
11321
+ if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
11322
+ const m = inner.match(/gorm:"([^"]*)"/);
11323
+ if (!m) return tag;
11324
+ for (const part of m[1].split(";")) {
11325
+ if (part === "") continue;
11326
+ const idx = part.indexOf(":");
11327
+ const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
11328
+ const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
11329
+ if (key === "-") tag.skip = true;
11330
+ else if (key === "column") tag.column = value;
11331
+ else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
11332
+ else if (key === "foreignkey") tag.foreignKey = value;
11333
+ else if (key === "many2many") tag.many2many = value;
11334
+ else if (key === "embedded") tag.embedded = true;
11335
+ else if (key === "embeddedprefix") tag.embeddedPrefix = value;
11336
+ }
11337
+ return tag;
11338
+ }
11339
+ function unwrapType(typeNode) {
11340
+ let isSlice = false;
11341
+ let isPointer = false;
11342
+ let n = typeNode;
11343
+ while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
11344
+ if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
11345
+ if (n.type === "pointer_type") isPointer = true;
11346
+ n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
11347
+ }
11348
+ if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11349
+ if (n.type === "type_identifier") {
11350
+ return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
11351
+ }
11352
+ if (n.type === "qualified_type") {
11353
+ const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
11354
+ const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
11355
+ return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
11356
+ }
11357
+ return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11358
+ }
11359
+ function readField(fieldDecl) {
11360
+ const names = [];
11361
+ let tagNode = null;
11362
+ for (let i = 0; i < fieldDecl.namedChildCount; i++) {
11363
+ const c = fieldDecl.namedChild(i);
11364
+ if (!c) continue;
11365
+ if (c.type === "field_identifier") names.push(c.text);
11366
+ else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
11367
+ }
11368
+ const typeNode = fieldDecl.childForFieldName("type");
11369
+ const t = unwrapType(typeNode);
11370
+ return {
11371
+ names,
11372
+ typeName: t.name,
11373
+ qualifier: t.qualifier,
11374
+ isSlice: t.isSlice,
11375
+ isPointer: t.isPointer,
11376
+ isQualified: t.isQualified,
11377
+ tag: parseGormTag(tagNode),
11378
+ line: fieldDecl.startPosition.row + 1
11379
+ };
11380
+ }
11381
+ function collectStructs(tree) {
11382
+ const structs = /* @__PURE__ */ new Map();
11383
+ walk8(tree.rootNode, (node) => {
11384
+ if (node.type !== "type_spec") return;
11385
+ const nameNode = node.childForFieldName("name");
11386
+ const typeNode = node.childForFieldName("type");
11387
+ if (!nameNode || typeNode?.type !== "struct_type") return;
11388
+ const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
11389
+ const fields = [];
11390
+ if (list && list.type === "field_declaration_list") {
11391
+ for (let i = 0; i < list.namedChildCount; i++) {
11392
+ const fd = list.namedChild(i);
11393
+ if (fd?.type === "field_declaration") fields.push(readField(fd));
11394
+ }
11395
+ }
11396
+ structs.set(nameNode.text, {
11397
+ name: nameNode.text,
11398
+ fields,
11399
+ line: node.startPosition.row + 1
11400
+ });
11401
+ });
11402
+ return structs;
11403
+ }
11404
+ var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
11405
+ "AutoMigrate",
11406
+ "Model",
11407
+ "Create",
11408
+ "Find",
11409
+ "First",
11410
+ "Take",
11411
+ "Last",
11412
+ "Save",
11413
+ "Delete",
11414
+ "Where",
11415
+ "FirstOrCreate",
11416
+ "FirstOrInit"
11417
+ ]);
11418
+ function compositeStructName(arg) {
11419
+ let n = arg;
11420
+ if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
11421
+ if (!n || n.type !== "composite_literal") return null;
11422
+ const typeNode = n.childForFieldName("type");
11423
+ if (!typeNode) return null;
11424
+ if (typeNode.type === "type_identifier") return typeNode.text;
11425
+ if (typeNode.type === "qualified_type") {
11426
+ return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
11427
+ }
11428
+ return null;
11429
+ }
11430
+ function collectCallModels(tree) {
11431
+ const models = /* @__PURE__ */ new Set();
11432
+ walk8(tree.rootNode, (node) => {
11433
+ if (node.type !== "call_expression") return;
11434
+ const fn = node.childForFieldName("function");
11435
+ if (fn?.type !== "selector_expression") return;
11436
+ const method = fn.childForFieldName("field")?.text;
11437
+ if (!method || !GORM_MODEL_METHODS.has(method)) return;
11438
+ const args = node.childForFieldName("arguments");
11439
+ if (!args) return;
11440
+ for (let i = 0; i < args.namedChildCount; i++) {
11441
+ const arg = args.namedChild(i);
11442
+ if (!arg) continue;
11443
+ const name = compositeStructName(arg);
11444
+ if (name) models.add(name);
11445
+ }
11446
+ });
11447
+ return models;
11448
+ }
11449
+ function collectTableNameOverrides(tree) {
11450
+ const overrides = /* @__PURE__ */ new Map();
11451
+ const declarers = /* @__PURE__ */ new Set();
11452
+ walk8(tree.rootNode, (node) => {
11453
+ if (node.type !== "method_declaration") return;
11454
+ if (node.childForFieldName("name")?.text !== "TableName") return;
11455
+ const receiver = node.childForFieldName("receiver");
11456
+ if (!receiver) return;
11457
+ let recvType = null;
11458
+ for (let i = 0; i < receiver.namedChildCount; i++) {
11459
+ const pd = receiver.namedChild(i);
11460
+ if (pd?.type !== "parameter_declaration") continue;
11461
+ const t = unwrapType(pd.childForFieldName("type"));
11462
+ recvType = t.name;
11463
+ }
11464
+ if (!recvType) return;
11465
+ declarers.add(recvType);
11466
+ const body = node.childForFieldName("body");
11467
+ if (!body) return;
11468
+ let literal = null;
11469
+ walk8(body, (n) => {
11470
+ if (literal !== null) return;
11471
+ if (n.type !== "return_statement") return;
11472
+ const exprList = n.namedChild(0);
11473
+ const first = exprList?.namedChild(0) ?? exprList;
11474
+ const v = stringLiteralValue(first);
11475
+ if (v) literal = v;
11476
+ });
11477
+ if (literal !== null) overrides.set(recvType, literal);
11478
+ });
11479
+ return { overrides, declarers };
11480
+ }
11481
+ function isRelationField(field, structs) {
11482
+ if (field.names.length === 0) return false;
11483
+ if (field.isQualified) return false;
11484
+ if (!field.typeName) return false;
11485
+ return structs.has(field.typeName);
11486
+ }
11487
+ function isGormModelEmbed(field) {
11488
+ return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
11489
+ }
11490
+ function analyze(tree) {
11491
+ const structs = collectStructs(tree);
11492
+ const { overrides, declarers } = collectTableNameOverrides(tree);
11493
+ const callModels = collectCallModels(tree);
11494
+ const models = /* @__PURE__ */ new Set();
11495
+ for (const [name, info] of structs) {
11496
+ if (info.fields.some(isGormModelEmbed)) models.add(name);
11497
+ }
11498
+ for (const name of callModels) if (structs.has(name)) models.add(name);
11499
+ for (const name of declarers) if (structs.has(name)) models.add(name);
11500
+ let grew = true;
11501
+ while (grew) {
11502
+ grew = false;
11503
+ for (const name of Array.from(models)) {
11504
+ const info = structs.get(name);
11505
+ if (!info) continue;
11506
+ for (const field of info.fields) {
11507
+ if (!isRelationField(field, structs)) continue;
11508
+ const target = field.typeName;
11509
+ if (!models.has(target) && structs.has(target)) {
11510
+ models.add(target);
11511
+ grew = true;
11512
+ }
11513
+ }
11514
+ }
11515
+ }
11516
+ const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
11517
+ return { structs, models, tableFor };
11518
+ }
11519
+ function collectColumns(struct, structs, seen, prefix, out, emitted) {
11520
+ if (seen.has(struct.name)) return;
11521
+ seen.add(struct.name);
11522
+ const add = (col) => {
11523
+ const full = prefix + col;
11524
+ if (!emitted.has(full)) {
11525
+ emitted.add(full);
11526
+ out.push(full);
11527
+ }
11528
+ };
11529
+ for (const field of struct.fields) {
11530
+ if (field.tag.skip) continue;
11531
+ if (field.names.length === 0) {
11532
+ if (isGormModelEmbed(field)) {
11533
+ add("id");
11534
+ add("created_at");
11535
+ add("updated_at");
11536
+ add("deleted_at");
11537
+ } else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
11538
+ collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
11539
+ }
11540
+ continue;
11541
+ }
11542
+ if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
11543
+ collectColumns(
11544
+ structs.get(field.typeName),
11545
+ structs,
11546
+ seen,
11547
+ prefix + (field.tag.embeddedPrefix ?? ""),
11548
+ out,
11549
+ emitted
11550
+ );
11551
+ continue;
11552
+ }
11553
+ if (isRelationField(field, structs)) continue;
11554
+ if (field.names.length === 1 && field.tag.column) {
11555
+ add(field.tag.column);
11556
+ } else {
11557
+ for (const n of field.names) add(toDBName(n));
11558
+ }
11559
+ }
11560
+ seen.delete(struct.name);
11561
+ }
11562
+ function gormEndpointsFromFile(file, serviceDir) {
11563
+ if (import_node_path48.default.extname(file.path) !== ".go") return [];
11564
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11565
+ const tree = parseSource10(makeGoParser3(), file.content);
11566
+ const { structs, models, tableFor } = analyze(tree);
11567
+ const out = [];
11568
+ const seenTables = /* @__PURE__ */ new Set();
11569
+ for (const name of models) {
11570
+ const struct = structs.get(name);
11571
+ if (!struct) continue;
11572
+ const table = tableFor(name);
11573
+ if (seenTables.has(table)) continue;
11574
+ seenTables.add(table);
11575
+ const columns = [];
11576
+ collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
11577
+ out.push({
11578
+ infraId: (0, import_types36.infraId)("sql-table", table),
11579
+ name: table,
11580
+ kind: "sql-table",
11581
+ edgeType: "CALLS",
11582
+ confidenceKind: "structural",
11583
+ ...columns.length > 0 ? { columns } : {},
11584
+ evidence: {
11585
+ file: toPosix(import_node_path48.default.relative(serviceDir, file.path)),
11586
+ line: struct.line,
11587
+ snippet: snippet(file.content, struct.line)
11588
+ }
11589
+ });
11590
+ }
11591
+ return out;
11592
+ }
11593
+ function gormForeignKeys(file, serviceDir) {
11594
+ if (import_node_path48.default.extname(file.path) !== ".go") return [];
11595
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
11596
+ const tree = parseSource10(makeGoParser3(), file.content);
11597
+ const { structs, models, tableFor } = analyze(tree);
11598
+ const out = [];
11599
+ const seen = /* @__PURE__ */ new Set();
11600
+ const emit = (childTable, parentTable, line) => {
11601
+ if (!childTable || !parentTable || childTable === parentTable) return;
11602
+ const key = `${childTable}->${parentTable}`;
11603
+ if (seen.has(key)) return;
11604
+ seen.add(key);
11605
+ out.push({
11606
+ childTable,
11607
+ parentTable,
11608
+ evidence: {
11609
+ file: toPosix(import_node_path48.default.relative(serviceDir, file.path)),
11610
+ line,
11611
+ snippet: snippet(file.content, line)
11612
+ }
11613
+ });
11614
+ };
11615
+ for (const name of models) {
11616
+ const struct = structs.get(name);
11617
+ if (!struct) continue;
11618
+ const thisTable = tableFor(name);
11619
+ const scalarNames = new Set(
11620
+ struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
11621
+ );
11622
+ for (const field of struct.fields) {
11623
+ if (field.tag.skip) continue;
11624
+ if (!isRelationField(field, structs)) continue;
11625
+ const relTable = tableFor(field.typeName);
11626
+ if (field.tag.many2many) {
11627
+ emit(field.tag.many2many, thisTable, field.line);
11628
+ emit(field.tag.many2many, relTable, field.line);
11629
+ continue;
11630
+ }
11631
+ if (field.isSlice) {
11632
+ emit(relTable, thisTable, field.line);
11633
+ continue;
11634
+ }
11635
+ const convFk = field.names[0] + "ID";
11636
+ const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
11637
+ if (belongsTo) emit(thisTable, relTable, field.line);
11638
+ else emit(relTable, thisTable, field.line);
11639
+ }
11640
+ }
11641
+ return out;
11642
+ }
11643
+
11097
11644
  // src/extract/calls/index.ts
11098
11645
  function edgeTypeFromEndpoint(ep) {
11099
11646
  switch (ep.edgeType) {
11100
11647
  case "PUBLISHES_TO":
11101
- return import_types36.EdgeType.PUBLISHES_TO;
11648
+ return import_types37.EdgeType.PUBLISHES_TO;
11102
11649
  case "CONSUMES_FROM":
11103
- return import_types36.EdgeType.CONSUMES_FROM;
11650
+ return import_types37.EdgeType.CONSUMES_FROM;
11104
11651
  default:
11105
- return import_types36.EdgeType.CALLS;
11652
+ return import_types37.EdgeType.CALLS;
11106
11653
  }
11107
11654
  }
11108
11655
  function isAwsKind(kind) {
@@ -11135,6 +11682,11 @@ async function addExternalEndpointEdges(graph, services) {
11135
11682
  } catch (err) {
11136
11683
  recordExtractionError("go SQL call extraction", file.path, err);
11137
11684
  }
11685
+ try {
11686
+ endpoints.push(...gormEndpointsFromFile(file, service.dir));
11687
+ } catch (err) {
11688
+ recordExtractionError("gorm data-axis extraction", file.path, err);
11689
+ }
11138
11690
  try {
11139
11691
  endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
11140
11692
  endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
@@ -11157,7 +11709,7 @@ async function addExternalEndpointEdges(graph, services) {
11157
11709
  if (!graph.hasNode(ep.infraId)) {
11158
11710
  const node = {
11159
11711
  id: ep.infraId,
11160
- type: import_types36.NodeType.InfraNode,
11712
+ type: import_types37.NodeType.InfraNode,
11161
11713
  name: ep.name,
11162
11714
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
11163
11715
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -11170,21 +11722,21 @@ async function addExternalEndpointEdges(graph, services) {
11170
11722
  }
11171
11723
  if (ep.columns && ep.columns.length > 0) {
11172
11724
  const node = graph.getNodeAttributes(ep.infraId);
11173
- if (node.type === import_types36.NodeType.InfraNode) {
11725
+ if (node.type === import_types37.NodeType.InfraNode) {
11174
11726
  graph.replaceNodeAttributes(ep.infraId, {
11175
11727
  ...node,
11176
11728
  columns: foldColumns(
11177
11729
  node.columns,
11178
11730
  ep.columns,
11179
- import_types36.Provenance.EXTRACTED,
11180
- (0, import_types36.confidenceForExtracted)(ep.confidenceKind)
11731
+ import_types37.Provenance.EXTRACTED,
11732
+ (0, import_types37.confidenceForExtracted)(ep.confidenceKind)
11181
11733
  )
11182
11734
  });
11183
11735
  }
11184
11736
  }
11185
11737
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
11186
11738
  const node = graph.getNodeAttributes(ep.infraId);
11187
- if (node.type === import_types36.NodeType.InfraNode) {
11739
+ if (node.type === import_types37.NodeType.InfraNode) {
11188
11740
  graph.replaceNodeAttributes(ep.infraId, {
11189
11741
  ...node,
11190
11742
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -11192,7 +11744,7 @@ async function addExternalEndpointEdges(graph, services) {
11192
11744
  }
11193
11745
  }
11194
11746
  const edgeType = edgeTypeFromEndpoint(ep);
11195
- const confidence = (0, import_types36.confidenceForExtracted)(ep.confidenceKind);
11747
+ const confidence = (0, import_types37.confidenceForExtracted)(ep.confidenceKind);
11196
11748
  const relFile = toPosix(ep.evidence.file);
11197
11749
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
11198
11750
  graph,
@@ -11202,7 +11754,7 @@ async function addExternalEndpointEdges(graph, services) {
11202
11754
  );
11203
11755
  nodesAdded += n;
11204
11756
  edgesAdded += e;
11205
- if (!(0, import_types36.passesExtractedFloor)(confidence)) {
11757
+ if (!(0, import_types37.passesExtractedFloor)(confidence)) {
11206
11758
  noteExtractedDropped({
11207
11759
  source: fileNodeId,
11208
11760
  target: ep.infraId,
@@ -11222,7 +11774,7 @@ async function addExternalEndpointEdges(graph, services) {
11222
11774
  source: fileNodeId,
11223
11775
  target: ep.infraId,
11224
11776
  type: edgeType,
11225
- provenance: import_types36.Provenance.EXTRACTED,
11777
+ provenance: import_types37.Provenance.EXTRACTED,
11226
11778
  confidence,
11227
11779
  evidence: ep.evidence
11228
11780
  };
@@ -11245,7 +11797,7 @@ async function addCallEdges(graph, services) {
11245
11797
 
11246
11798
  // src/extract/table-edges.ts
11247
11799
  init_cjs_shims();
11248
- var import_types37 = require("@neat.is/types");
11800
+ var import_types38 = require("@neat.is/types");
11249
11801
  async function addTableEdges(graph, services) {
11250
11802
  let nodesAdded = 0;
11251
11803
  let edgesAdded = 0;
@@ -11259,6 +11811,7 @@ async function addTableEdges(graph, services) {
11259
11811
  refs.push(...sqlalchemyForeignKeys(file, service.dir));
11260
11812
  refs.push(...railsSchemaForeignKeys(file, service.dir));
11261
11813
  refs.push(...laravelMigrationForeignKeys(file, service.dir));
11814
+ refs.push(...gormForeignKeys(file, service.dir));
11262
11815
  modelRefs.push(...railsModelForeignKeys(file, service.dir));
11263
11816
  modelRefs.push(...laravelModelForeignKeys(file, service.dir));
11264
11817
  } catch (err) {
@@ -11272,20 +11825,20 @@ async function addTableEdges(graph, services) {
11272
11825
  }
11273
11826
  refs.push(...modelRefs);
11274
11827
  for (const ref of refs) {
11275
- const childId = (0, import_types37.infraId)("sql-table", ref.childTable);
11276
- const parentId = (0, import_types37.infraId)("sql-table", ref.parentTable);
11828
+ const childId = (0, import_types38.infraId)("sql-table", ref.childTable);
11829
+ const parentId = (0, import_types38.infraId)("sql-table", ref.parentTable);
11277
11830
  if (childId === parentId) continue;
11278
11831
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
11279
11832
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
11280
- const edgeId = (0, import_types37.extractedEdgeId)(childId, parentId, import_types37.EdgeType.REFERENCES);
11833
+ const edgeId = (0, import_types38.extractedEdgeId)(childId, parentId, import_types38.EdgeType.REFERENCES);
11281
11834
  if (graph.hasEdge(edgeId)) continue;
11282
11835
  const edge = {
11283
11836
  id: edgeId,
11284
11837
  source: childId,
11285
11838
  target: parentId,
11286
- type: import_types37.EdgeType.REFERENCES,
11287
- provenance: import_types37.Provenance.EXTRACTED,
11288
- confidence: (0, import_types37.confidenceForExtracted)("structural"),
11839
+ type: import_types38.EdgeType.REFERENCES,
11840
+ provenance: import_types38.Provenance.EXTRACTED,
11841
+ confidence: (0, import_types38.confidenceForExtracted)("structural"),
11289
11842
  evidence: ref.evidence
11290
11843
  };
11291
11844
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -11298,7 +11851,7 @@ function ensureTableNode(graph, id, name) {
11298
11851
  if (graph.hasNode(id)) return 0;
11299
11852
  const node = {
11300
11853
  id,
11301
- type: import_types37.NodeType.InfraNode,
11854
+ type: import_types38.NodeType.InfraNode,
11302
11855
  name,
11303
11856
  provider: "self",
11304
11857
  kind: "sql-table"
@@ -11312,16 +11865,16 @@ init_cjs_shims();
11312
11865
 
11313
11866
  // src/extract/infra/docker-compose.ts
11314
11867
  init_cjs_shims();
11315
- var import_node_path48 = __toESM(require("path"), 1);
11316
- var import_types39 = require("@neat.is/types");
11868
+ var import_node_path49 = __toESM(require("path"), 1);
11869
+ var import_types40 = require("@neat.is/types");
11317
11870
 
11318
11871
  // src/extract/infra/shared.ts
11319
11872
  init_cjs_shims();
11320
- var import_types38 = require("@neat.is/types");
11873
+ var import_types39 = require("@neat.is/types");
11321
11874
  function makeInfraNode(kind, name, provider = "self", extras) {
11322
11875
  return {
11323
- id: (0, import_types38.infraId)(kind, name),
11324
- type: import_types38.NodeType.InfraNode,
11876
+ id: (0, import_types39.infraId)(kind, name),
11877
+ type: import_types39.NodeType.InfraNode,
11325
11878
  name,
11326
11879
  provider,
11327
11880
  kind,
@@ -11365,8 +11918,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
11365
11918
  source: anchorId,
11366
11919
  target: node.id,
11367
11920
  type: edgeType,
11368
- provenance: import_types38.Provenance.EXTRACTED,
11369
- confidence: (0, import_types38.confidenceForExtracted)("structural"),
11921
+ provenance: import_types39.Provenance.EXTRACTED,
11922
+ confidence: (0, import_types39.confidenceForExtracted)("structural"),
11370
11923
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11371
11924
  };
11372
11925
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11383,7 +11936,7 @@ function dependsOnList(value) {
11383
11936
  }
11384
11937
  function serviceNameToServiceNode(name, services) {
11385
11938
  for (const s of services) {
11386
- if (s.node.name === name || import_node_path48.default.basename(s.dir) === name) return s.node.id;
11939
+ if (s.node.name === name || import_node_path49.default.basename(s.dir) === name) return s.node.id;
11387
11940
  }
11388
11941
  return null;
11389
11942
  }
@@ -11392,7 +11945,7 @@ async function addComposeInfra(graph, scanPath, services) {
11392
11945
  let edgesAdded = 0;
11393
11946
  let composePath = null;
11394
11947
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
11395
- const abs = import_node_path48.default.join(scanPath, name);
11948
+ const abs = import_node_path49.default.join(scanPath, name);
11396
11949
  if (await exists(abs)) {
11397
11950
  composePath = abs;
11398
11951
  break;
@@ -11405,13 +11958,13 @@ async function addComposeInfra(graph, scanPath, services) {
11405
11958
  } catch (err) {
11406
11959
  recordExtractionError(
11407
11960
  "infra docker-compose",
11408
- import_node_path48.default.relative(scanPath, composePath),
11961
+ import_node_path49.default.relative(scanPath, composePath),
11409
11962
  err
11410
11963
  );
11411
11964
  return { nodesAdded, edgesAdded };
11412
11965
  }
11413
11966
  if (!compose?.services) return { nodesAdded, edgesAdded };
11414
- const evidenceFile = import_node_path48.default.relative(scanPath, composePath).split(import_node_path48.default.sep).join("/");
11967
+ const evidenceFile = import_node_path49.default.relative(scanPath, composePath).split(import_node_path49.default.sep).join("/");
11415
11968
  const composeNameToNodeId = /* @__PURE__ */ new Map();
11416
11969
  for (const [composeName, svc] of Object.entries(compose.services)) {
11417
11970
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -11433,15 +11986,15 @@ async function addComposeInfra(graph, scanPath, services) {
11433
11986
  for (const dep of dependsOnList(svc.depends_on)) {
11434
11987
  const targetId = composeNameToNodeId.get(dep);
11435
11988
  if (!targetId) continue;
11436
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types39.EdgeType.DEPENDS_ON);
11989
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types40.EdgeType.DEPENDS_ON);
11437
11990
  if (graph.hasEdge(edgeId)) continue;
11438
11991
  const edge = {
11439
11992
  id: edgeId,
11440
11993
  source: sourceId,
11441
11994
  target: targetId,
11442
- type: import_types39.EdgeType.DEPENDS_ON,
11443
- provenance: import_types39.Provenance.EXTRACTED,
11444
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
11995
+ type: import_types40.EdgeType.DEPENDS_ON,
11996
+ provenance: import_types40.Provenance.EXTRACTED,
11997
+ confidence: (0, import_types40.confidenceForExtracted)("structural"),
11445
11998
  evidence: { file: evidenceFile }
11446
11999
  };
11447
12000
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11453,9 +12006,9 @@ async function addComposeInfra(graph, scanPath, services) {
11453
12006
 
11454
12007
  // src/extract/infra/dockerfile.ts
11455
12008
  init_cjs_shims();
11456
- var import_node_path49 = __toESM(require("path"), 1);
12009
+ var import_node_path50 = __toESM(require("path"), 1);
11457
12010
  var import_node_fs18 = require("fs");
11458
- var import_types40 = require("@neat.is/types");
12011
+ var import_types41 = require("@neat.is/types");
11459
12012
  function readDockerfile(content) {
11460
12013
  let image = null;
11461
12014
  const ports = [];
@@ -11484,7 +12037,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11484
12037
  let nodesAdded = 0;
11485
12038
  let edgesAdded = 0;
11486
12039
  for (const service of services) {
11487
- const dockerfilePath = import_node_path49.default.join(service.dir, "Dockerfile");
12040
+ const dockerfilePath = import_node_path50.default.join(service.dir, "Dockerfile");
11488
12041
  if (!await exists(dockerfilePath)) continue;
11489
12042
  let content;
11490
12043
  try {
@@ -11492,7 +12045,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11492
12045
  } catch (err) {
11493
12046
  recordExtractionError(
11494
12047
  "infra dockerfile",
11495
- import_node_path49.default.relative(scanPath, dockerfilePath),
12048
+ import_node_path50.default.relative(scanPath, dockerfilePath),
11496
12049
  err
11497
12050
  );
11498
12051
  continue;
@@ -11504,8 +12057,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11504
12057
  graph.addNode(node.id, node);
11505
12058
  nodesAdded++;
11506
12059
  }
11507
- const relDockerfile = toPosix(import_node_path49.default.relative(service.dir, dockerfilePath));
11508
- const evidenceFile = toPosix(import_node_path49.default.relative(scanPath, dockerfilePath));
12060
+ const relDockerfile = toPosix(import_node_path50.default.relative(service.dir, dockerfilePath));
12061
+ const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, dockerfilePath));
11509
12062
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11510
12063
  graph,
11511
12064
  service.pkg.name,
@@ -11514,15 +12067,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11514
12067
  );
11515
12068
  nodesAdded += fn;
11516
12069
  edgesAdded += fe;
11517
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types40.EdgeType.RUNS_ON);
12070
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types41.EdgeType.RUNS_ON);
11518
12071
  if (!graph.hasEdge(edgeId)) {
11519
12072
  const edge = {
11520
12073
  id: edgeId,
11521
12074
  source: fileNodeId,
11522
12075
  target: node.id,
11523
- type: import_types40.EdgeType.RUNS_ON,
11524
- provenance: import_types40.Provenance.EXTRACTED,
11525
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12076
+ type: import_types41.EdgeType.RUNS_ON,
12077
+ provenance: import_types41.Provenance.EXTRACTED,
12078
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11526
12079
  evidence: {
11527
12080
  file: evidenceFile,
11528
12081
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -11537,15 +12090,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11537
12090
  graph.addNode(portNode.id, portNode);
11538
12091
  nodesAdded++;
11539
12092
  }
11540
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types40.EdgeType.CONNECTS_TO);
12093
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types41.EdgeType.CONNECTS_TO);
11541
12094
  if (graph.hasEdge(portEdgeId)) continue;
11542
12095
  const portEdge = {
11543
12096
  id: portEdgeId,
11544
12097
  source: fileNodeId,
11545
12098
  target: portNode.id,
11546
- type: import_types40.EdgeType.CONNECTS_TO,
11547
- provenance: import_types40.Provenance.EXTRACTED,
11548
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12099
+ type: import_types41.EdgeType.CONNECTS_TO,
12100
+ provenance: import_types41.Provenance.EXTRACTED,
12101
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
11549
12102
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
11550
12103
  };
11551
12104
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -11558,8 +12111,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
11558
12111
  // src/extract/infra/terraform.ts
11559
12112
  init_cjs_shims();
11560
12113
  var import_node_fs19 = require("fs");
11561
- var import_node_path50 = __toESM(require("path"), 1);
11562
- var import_types41 = require("@neat.is/types");
12114
+ var import_node_path51 = __toESM(require("path"), 1);
12115
+ var import_types42 = require("@neat.is/types");
11563
12116
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
11564
12117
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
11565
12118
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -11569,11 +12122,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
11569
12122
  for (const entry of entries) {
11570
12123
  if (entry.isDirectory()) {
11571
12124
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
11572
- const child = import_node_path50.default.join(start, entry.name);
12125
+ const child = import_node_path51.default.join(start, entry.name);
11573
12126
  if (await isPythonVenvDir(child)) continue;
11574
12127
  out.push(...await walkTfFiles(child, depth + 1, max));
11575
12128
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
11576
- out.push(import_node_path50.default.join(start, entry.name));
12129
+ out.push(import_node_path51.default.join(start, entry.name));
11577
12130
  }
11578
12131
  }
11579
12132
  return out;
@@ -11605,7 +12158,7 @@ async function addTerraformResources(graph, scanPath) {
11605
12158
  const files = await walkTfFiles(scanPath);
11606
12159
  for (const file of files) {
11607
12160
  const content = await import_node_fs19.promises.readFile(file, "utf8");
11608
- const evidenceFile = toPosix(import_node_path50.default.relative(scanPath, file));
12161
+ const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, file));
11609
12162
  const resources = [];
11610
12163
  const byKey = /* @__PURE__ */ new Map();
11611
12164
  RESOURCE_RE.lastIndex = 0;
@@ -11640,16 +12193,16 @@ async function addTerraformResources(graph, scanPath) {
11640
12193
  if (!target) continue;
11641
12194
  if (seen.has(target.nodeId)) continue;
11642
12195
  seen.add(target.nodeId);
11643
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types41.EdgeType.DEPENDS_ON);
12196
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types42.EdgeType.DEPENDS_ON);
11644
12197
  if (graph.hasEdge(edgeId)) continue;
11645
12198
  const line = lineAt2(content, resource.bodyOffset + ref.index);
11646
12199
  const edge = {
11647
12200
  id: edgeId,
11648
12201
  source: resource.nodeId,
11649
12202
  target: target.nodeId,
11650
- type: import_types41.EdgeType.DEPENDS_ON,
11651
- provenance: import_types41.Provenance.EXTRACTED,
11652
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12203
+ type: import_types42.EdgeType.DEPENDS_ON,
12204
+ provenance: import_types42.Provenance.EXTRACTED,
12205
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
11653
12206
  evidence: { file: evidenceFile, line, snippet: key }
11654
12207
  };
11655
12208
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11663,7 +12216,7 @@ async function addTerraformResources(graph, scanPath) {
11663
12216
  // src/extract/infra/k8s.ts
11664
12217
  init_cjs_shims();
11665
12218
  var import_node_fs20 = require("fs");
11666
- var import_node_path51 = __toESM(require("path"), 1);
12219
+ var import_node_path52 = __toESM(require("path"), 1);
11667
12220
  var import_yaml3 = require("yaml");
11668
12221
  var K8S_KIND_TO_INFRA_KIND = {
11669
12222
  Service: "k8s-service",
@@ -11681,11 +12234,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
11681
12234
  for (const entry of entries) {
11682
12235
  if (entry.isDirectory()) {
11683
12236
  if (IGNORED_DIRS.has(entry.name)) continue;
11684
- const child = import_node_path51.default.join(start, entry.name);
12237
+ const child = import_node_path52.default.join(start, entry.name);
11685
12238
  if (await isPythonVenvDir(child)) continue;
11686
12239
  out.push(...await walkYamlFiles2(child, depth + 1, max));
11687
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path51.default.extname(entry.name))) {
11688
- out.push(import_node_path51.default.join(start, entry.name));
12240
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path52.default.extname(entry.name))) {
12241
+ out.push(import_node_path52.default.join(start, entry.name));
11689
12242
  }
11690
12243
  }
11691
12244
  return out;
@@ -11719,13 +12272,13 @@ async function addK8sResources(graph, scanPath) {
11719
12272
  // src/extract/infra/cloudflare.ts
11720
12273
  init_cjs_shims();
11721
12274
  var import_node_fs21 = require("fs");
11722
- var import_node_path52 = __toESM(require("path"), 1);
12275
+ var import_node_path53 = __toESM(require("path"), 1);
11723
12276
  var import_smol_toml2 = require("smol-toml");
11724
- var import_types42 = require("@neat.is/types");
12277
+ var import_types43 = require("@neat.is/types");
11725
12278
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
11726
12279
  async function readWranglerConfig(dir) {
11727
12280
  for (const filename of WRANGLER_FILENAMES) {
11728
- const abs = import_node_path52.default.join(dir, filename);
12281
+ const abs = import_node_path53.default.join(dir, filename);
11729
12282
  if (!await exists(abs)) continue;
11730
12283
  const raw = await import_node_fs21.promises.readFile(abs, "utf8");
11731
12284
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -11769,8 +12322,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
11769
12322
  source: anchorId,
11770
12323
  target: node.id,
11771
12324
  type: edgeType,
11772
- provenance: import_types42.Provenance.EXTRACTED,
11773
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12325
+ provenance: import_types43.Provenance.EXTRACTED,
12326
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11774
12327
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11775
12328
  };
11776
12329
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11788,11 +12341,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11788
12341
  try {
11789
12342
  read = await readWranglerConfig(service.dir);
11790
12343
  } catch (err) {
11791
- recordExtractionError("infra cloudflare", import_node_path52.default.relative(scanPath, service.dir), err);
12344
+ recordExtractionError("infra cloudflare", import_node_path53.default.relative(scanPath, service.dir), err);
11792
12345
  continue;
11793
12346
  }
11794
12347
  if (!read || !read.config.name) continue;
11795
- const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, import_node_path52.default.join(service.dir, read.relFile)));
12348
+ const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, read.relFile)));
11796
12349
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
11797
12350
  }
11798
12351
  for (const worker of discovered) {
@@ -11804,7 +12357,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11804
12357
  }
11805
12358
  let anchorId = service.node.id;
11806
12359
  if (config.main) {
11807
- const entryRelPath = toPosix(import_node_path52.default.normalize(config.main));
12360
+ const entryRelPath = toPosix(import_node_path53.default.normalize(config.main));
11808
12361
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
11809
12362
  graph,
11810
12363
  service.pkg.name,
@@ -11831,15 +12384,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11831
12384
  nodesAdded++;
11832
12385
  }
11833
12386
  if (runtimeNode.id !== anchorId) {
11834
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types42.EdgeType.RUNS_ON);
12387
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types43.EdgeType.RUNS_ON);
11835
12388
  if (!graph.hasEdge(runsOnId)) {
11836
12389
  const edge = {
11837
12390
  id: runsOnId,
11838
12391
  source: anchorId,
11839
12392
  target: runtimeNode.id,
11840
- type: import_types42.EdgeType.RUNS_ON,
11841
- provenance: import_types42.Provenance.EXTRACTED,
11842
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12393
+ type: import_types43.EdgeType.RUNS_ON,
12394
+ provenance: import_types43.Provenance.EXTRACTED,
12395
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11843
12396
  evidence: {
11844
12397
  file: evidenceFile,
11845
12398
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -11853,7 +12406,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11853
12406
  const result = addResourceEdge(
11854
12407
  graph,
11855
12408
  anchorId,
11856
- import_types42.EdgeType.CONNECTS_TO,
12409
+ import_types43.EdgeType.CONNECTS_TO,
11857
12410
  "cloudflare-route",
11858
12411
  route,
11859
12412
  evidenceFile,
@@ -11877,7 +12430,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11877
12430
  const result = addResourceEdge(
11878
12431
  graph,
11879
12432
  anchorId,
11880
- import_types42.EdgeType.DEPENDS_ON,
12433
+ import_types43.EdgeType.DEPENDS_ON,
11881
12434
  group.kind,
11882
12435
  name,
11883
12436
  evidenceFile,
@@ -11891,7 +12444,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11891
12444
  const result = addResourceEdge(
11892
12445
  graph,
11893
12446
  anchorId,
11894
- import_types42.EdgeType.DEPENDS_ON,
12447
+ import_types43.EdgeType.DEPENDS_ON,
11895
12448
  "cloudflare-cron",
11896
12449
  cron,
11897
12450
  evidenceFile,
@@ -11904,7 +12457,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11904
12457
  const result = addResourceEdge(
11905
12458
  graph,
11906
12459
  anchorId,
11907
- import_types42.EdgeType.DEPENDS_ON,
12460
+ import_types43.EdgeType.DEPENDS_ON,
11908
12461
  "cloudflare-env-var",
11909
12462
  varName,
11910
12463
  evidenceFile,
@@ -11917,15 +12470,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11917
12470
  if (!svc.service) continue;
11918
12471
  const target = workerIndex.get(svc.service);
11919
12472
  if (target && target.anchorId !== anchorId) {
11920
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types42.EdgeType.CALLS);
12473
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types43.EdgeType.CALLS);
11921
12474
  if (!graph.hasEdge(edgeId)) {
11922
12475
  const edge = {
11923
12476
  id: edgeId,
11924
12477
  source: anchorId,
11925
12478
  target: target.anchorId,
11926
- type: import_types42.EdgeType.CALLS,
11927
- provenance: import_types42.Provenance.EXTRACTED,
11928
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12479
+ type: import_types43.EdgeType.CALLS,
12480
+ provenance: import_types43.Provenance.EXTRACTED,
12481
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
11929
12482
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
11930
12483
  };
11931
12484
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11936,7 +12489,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11936
12489
  const result = addResourceEdge(
11937
12490
  graph,
11938
12491
  anchorId,
11939
- import_types42.EdgeType.DEPENDS_ON,
12492
+ import_types43.EdgeType.DEPENDS_ON,
11940
12493
  "cloudflare-service-binding",
11941
12494
  svc.service,
11942
12495
  evidenceFile,
@@ -11952,12 +12505,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
11952
12505
  // src/extract/infra/vercel.ts
11953
12506
  init_cjs_shims();
11954
12507
  var import_node_fs22 = require("fs");
11955
- var import_node_path53 = __toESM(require("path"), 1);
11956
- var import_types43 = require("@neat.is/types");
12508
+ var import_node_path54 = __toESM(require("path"), 1);
12509
+ var import_types44 = require("@neat.is/types");
11957
12510
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
11958
12511
  async function readVercelConfig(dir) {
11959
12512
  for (const filename of VERCEL_CONFIG_FILENAMES) {
11960
- const abs = import_node_path53.default.join(dir, filename);
12513
+ const abs = import_node_path54.default.join(dir, filename);
11961
12514
  if (!await exists(abs)) continue;
11962
12515
  const raw = await import_node_fs22.promises.readFile(abs, "utf8");
11963
12516
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -11966,7 +12519,7 @@ async function readVercelConfig(dir) {
11966
12519
  return null;
11967
12520
  }
11968
12521
  async function readLinkedProjectName(dir) {
11969
- const abs = import_node_path53.default.join(dir, ".vercel", "project.json");
12522
+ const abs = import_node_path54.default.join(dir, ".vercel", "project.json");
11970
12523
  if (!await exists(abs)) return void 0;
11971
12524
  const parsed = JSON.parse(await import_node_fs22.promises.readFile(abs, "utf8"));
11972
12525
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -11984,7 +12537,7 @@ async function addVercelServices(graph, services, scanPath) {
11984
12537
  read = await readVercelConfig(service.dir);
11985
12538
  projectName = await readLinkedProjectName(service.dir);
11986
12539
  } catch (err) {
11987
- recordExtractionError("infra vercel", import_node_path53.default.relative(scanPath, service.dir), err);
12540
+ recordExtractionError("infra vercel", import_node_path54.default.relative(scanPath, service.dir), err);
11988
12541
  continue;
11989
12542
  }
11990
12543
  if (!read && !projectName) continue;
@@ -12000,7 +12553,7 @@ async function addVercelServices(graph, services, scanPath) {
12000
12553
  const anchorId = service.node.id;
12001
12554
  if (!read) continue;
12002
12555
  const { config, relFile, raw } = read;
12003
- const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, import_node_path53.default.join(service.dir, relFile)));
12556
+ const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
12004
12557
  const add = (edgeType, kind, name) => {
12005
12558
  if (!name) return;
12006
12559
  const result = emitPlatformResourceEdge(
@@ -12016,12 +12569,12 @@ async function addVercelServices(graph, services, scanPath) {
12016
12569
  nodesAdded += result.nodesAdded;
12017
12570
  edgesAdded += result.edgesAdded;
12018
12571
  };
12019
- add(import_types43.EdgeType.RUNS_ON, "vercel", "vercel");
12020
- for (const cron of config.crons ?? []) add(import_types43.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12021
- for (const varName of Object.keys(config.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12022
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types43.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12572
+ add(import_types44.EdgeType.RUNS_ON, "vercel", "vercel");
12573
+ for (const cron of config.crons ?? []) add(import_types44.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12574
+ for (const varName of Object.keys(config.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12575
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12023
12576
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
12024
- add(import_types43.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12577
+ add(import_types44.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12025
12578
  }
12026
12579
  }
12027
12580
  return { nodesAdded, edgesAdded };
@@ -12030,13 +12583,13 @@ async function addVercelServices(graph, services, scanPath) {
12030
12583
  // src/extract/infra/railway.ts
12031
12584
  init_cjs_shims();
12032
12585
  var import_node_fs23 = require("fs");
12033
- var import_node_path54 = __toESM(require("path"), 1);
12586
+ var import_node_path55 = __toESM(require("path"), 1);
12034
12587
  var import_smol_toml3 = require("smol-toml");
12035
- var import_types44 = require("@neat.is/types");
12588
+ var import_types45 = require("@neat.is/types");
12036
12589
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
12037
12590
  async function readRailwayConfig(dir) {
12038
12591
  for (const filename of RAILWAY_FILENAMES) {
12039
- const abs = import_node_path54.default.join(dir, filename);
12592
+ const abs = import_node_path55.default.join(dir, filename);
12040
12593
  if (!await exists(abs)) continue;
12041
12594
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
12042
12595
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12052,7 +12605,7 @@ async function addRailwayServices(graph, services, scanPath) {
12052
12605
  try {
12053
12606
  read = await readRailwayConfig(service.dir);
12054
12607
  } catch (err) {
12055
- recordExtractionError("infra railway", import_node_path54.default.relative(scanPath, service.dir), err);
12608
+ recordExtractionError("infra railway", import_node_path55.default.relative(scanPath, service.dir), err);
12056
12609
  continue;
12057
12610
  }
12058
12611
  if (!read) continue;
@@ -12062,7 +12615,7 @@ async function addRailwayServices(graph, services, scanPath) {
12062
12615
  }
12063
12616
  const anchorId = service.node.id;
12064
12617
  const { config, relFile, raw } = read;
12065
- const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, relFile)));
12618
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12066
12619
  const add = (edgeType, kind, name) => {
12067
12620
  if (!name) return;
12068
12621
  const result = emitPlatformResourceEdge(
@@ -12078,9 +12631,9 @@ async function addRailwayServices(graph, services, scanPath) {
12078
12631
  nodesAdded += result.nodesAdded;
12079
12632
  edgesAdded += result.edgesAdded;
12080
12633
  };
12081
- add(import_types44.EdgeType.RUNS_ON, "railway", "railway");
12082
- add(import_types44.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12083
- add(import_types44.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12634
+ add(import_types45.EdgeType.RUNS_ON, "railway", "railway");
12635
+ add(import_types45.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12636
+ add(import_types45.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12084
12637
  }
12085
12638
  return { nodesAdded, edgesAdded };
12086
12639
  }
@@ -12088,12 +12641,12 @@ async function addRailwayServices(graph, services, scanPath) {
12088
12641
  // src/extract/infra/supabase.ts
12089
12642
  init_cjs_shims();
12090
12643
  var import_node_fs24 = require("fs");
12091
- var import_node_path55 = __toESM(require("path"), 1);
12644
+ var import_node_path56 = __toESM(require("path"), 1);
12092
12645
  var import_smol_toml4 = require("smol-toml");
12093
- var import_types45 = require("@neat.is/types");
12646
+ var import_types46 = require("@neat.is/types");
12094
12647
  async function readSupabaseConfig(dir) {
12095
- const relFile = import_node_path55.default.join("supabase", "config.toml");
12096
- const abs = import_node_path55.default.join(dir, relFile);
12648
+ const relFile = import_node_path56.default.join("supabase", "config.toml");
12649
+ const abs = import_node_path56.default.join(dir, relFile);
12097
12650
  if (!await exists(abs)) return null;
12098
12651
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
12099
12652
  const config = (0, import_smol_toml4.parse)(raw);
@@ -12107,7 +12660,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12107
12660
  try {
12108
12661
  read = await readSupabaseConfig(service.dir);
12109
12662
  } catch (err) {
12110
- recordExtractionError("infra supabase", import_node_path55.default.relative(scanPath, service.dir), err);
12663
+ recordExtractionError("infra supabase", import_node_path56.default.relative(scanPath, service.dir), err);
12111
12664
  continue;
12112
12665
  }
12113
12666
  if (!read) continue;
@@ -12122,7 +12675,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12122
12675
  });
12123
12676
  }
12124
12677
  const anchorId = service.node.id;
12125
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
12678
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12126
12679
  const add = (edgeType, kind, name) => {
12127
12680
  if (!name) return;
12128
12681
  const result = emitPlatformResourceEdge(
@@ -12138,10 +12691,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
12138
12691
  nodesAdded += result.nodesAdded;
12139
12692
  edgesAdded += result.edgesAdded;
12140
12693
  };
12141
- add(import_types45.EdgeType.RUNS_ON, "supabase", "supabase");
12142
- for (const fn of Object.keys(config.functions ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "supabase-function", fn);
12143
- if (config.storage) add(import_types45.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12144
- if (config.auth) add(import_types45.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12694
+ add(import_types46.EdgeType.RUNS_ON, "supabase", "supabase");
12695
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types46.EdgeType.DEPENDS_ON, "supabase-function", fn);
12696
+ if (config.storage) add(import_types46.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12697
+ if (config.auth) add(import_types46.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12145
12698
  }
12146
12699
  return { nodesAdded, edgesAdded };
12147
12700
  }
@@ -12164,14 +12717,14 @@ async function addInfra(graph, scanPath, services) {
12164
12717
 
12165
12718
  // src/extract/zod-shapes.ts
12166
12719
  init_cjs_shims();
12167
- var import_node_path56 = __toESM(require("path"), 1);
12168
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12720
+ var import_node_path57 = __toESM(require("path"), 1);
12721
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
12169
12722
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
12170
- var import_types46 = require("@neat.is/types");
12723
+ var import_types47 = require("@neat.is/types");
12171
12724
  var ZOD_IMPORT_RE = /\bzod\b/;
12172
12725
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12173
12726
  function parserForExt3(ext) {
12174
- const p = new import_tree_sitter15.default();
12727
+ const p = new import_tree_sitter16.default();
12175
12728
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12176
12729
  return p;
12177
12730
  }
@@ -12259,7 +12812,7 @@ function topLevelSchemas(root) {
12259
12812
  }
12260
12813
  function zodShapesFromFile(file, serviceDir) {
12261
12814
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12262
- const tree = parseSource3(parserForExt3(import_node_path56.default.extname(file.path)), file.content);
12815
+ const tree = parseSource3(parserForExt3(import_node_path57.default.extname(file.path)), file.content);
12263
12816
  const out = [];
12264
12817
  const seen = /* @__PURE__ */ new Set();
12265
12818
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -12273,11 +12826,11 @@ function zodShapesFromFile(file, serviceDir) {
12273
12826
  seen.add(name);
12274
12827
  const line = call.startPosition.row + 1;
12275
12828
  out.push({
12276
- infraId: (0, import_types46.infraId)("zod-schema", name),
12829
+ infraId: (0, import_types47.infraId)("zod-schema", name),
12277
12830
  name,
12278
12831
  fields,
12279
12832
  evidence: {
12280
- file: import_node_path56.default.relative(serviceDir, file.path),
12833
+ file: import_node_path57.default.relative(serviceDir, file.path),
12281
12834
  line,
12282
12835
  snippet: snippet(file.content, line)
12283
12836
  }
@@ -12308,7 +12861,7 @@ async function addZodShapes(graph, services) {
12308
12861
  if (!graph.hasNode(shape.infraId)) {
12309
12862
  const node = {
12310
12863
  id: shape.infraId,
12311
- type: import_types46.NodeType.InfraNode,
12864
+ type: import_types47.NodeType.InfraNode,
12312
12865
  name: shape.name,
12313
12866
  provider: "self",
12314
12867
  kind: "zod-schema"
@@ -12318,14 +12871,14 @@ async function addZodShapes(graph, services) {
12318
12871
  }
12319
12872
  if (shape.fields.length > 0) {
12320
12873
  const node = graph.getNodeAttributes(shape.infraId);
12321
- if (node.type === import_types46.NodeType.InfraNode) {
12874
+ if (node.type === import_types47.NodeType.InfraNode) {
12322
12875
  graph.replaceNodeAttributes(shape.infraId, {
12323
12876
  ...node,
12324
12877
  columns: foldColumns(
12325
12878
  node.columns,
12326
12879
  shape.fields,
12327
- import_types46.Provenance.EXTRACTED,
12328
- (0, import_types46.confidenceForExtracted)("structural")
12880
+ import_types47.Provenance.EXTRACTED,
12881
+ (0, import_types47.confidenceForExtracted)("structural")
12329
12882
  )
12330
12883
  });
12331
12884
  }
@@ -12339,15 +12892,15 @@ async function addZodShapes(graph, services) {
12339
12892
  );
12340
12893
  nodesAdded += n;
12341
12894
  edgesAdded += e;
12342
- const edgeId = (0, import_types46.extractedEdgeId)(fileNodeId, shape.infraId, import_types46.EdgeType.CONTAINS);
12895
+ const edgeId = (0, import_types47.extractedEdgeId)(fileNodeId, shape.infraId, import_types47.EdgeType.CONTAINS);
12343
12896
  if (!graph.hasEdge(edgeId)) {
12344
12897
  const edge = {
12345
12898
  id: edgeId,
12346
12899
  source: fileNodeId,
12347
12900
  target: shape.infraId,
12348
- type: import_types46.EdgeType.CONTAINS,
12349
- provenance: import_types46.Provenance.EXTRACTED,
12350
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
12901
+ type: import_types47.EdgeType.CONTAINS,
12902
+ provenance: import_types47.Provenance.EXTRACTED,
12903
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
12351
12904
  evidence: shape.evidence
12352
12905
  };
12353
12906
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -12361,7 +12914,7 @@ async function addZodShapes(graph, services) {
12361
12914
 
12362
12915
  // src/extract/firestore-rules.ts
12363
12916
  init_cjs_shims();
12364
- var import_types47 = require("@neat.is/types");
12917
+ var import_types48 = require("@neat.is/types");
12365
12918
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
12366
12919
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
12367
12920
  function stripComments(src) {
@@ -12501,7 +13054,7 @@ async function addFirestoreRules(graph, services) {
12501
13054
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
12502
13055
  graph.forEachNode((id, attrs) => {
12503
13056
  const node = attrs;
12504
- if (node.type !== import_types47.NodeType.InfraNode) return;
13057
+ if (node.type !== import_types48.NodeType.InfraNode) return;
12505
13058
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
12506
13059
  const fields = guards.get(collectionKeyFromName(node.name));
12507
13060
  if (!fields || fields.size === 0) return;
@@ -12514,17 +13067,17 @@ async function addFirestoreRules(graph, services) {
12514
13067
  }
12515
13068
 
12516
13069
  // src/extract/index.ts
12517
- var import_node_path58 = __toESM(require("path"), 1);
13070
+ var import_node_path59 = __toESM(require("path"), 1);
12518
13071
 
12519
13072
  // src/extract/retire.ts
12520
13073
  init_cjs_shims();
12521
13074
  var import_node_fs25 = require("fs");
12522
- var import_node_path57 = __toESM(require("path"), 1);
12523
- var import_types48 = require("@neat.is/types");
13075
+ var import_node_path58 = __toESM(require("path"), 1);
13076
+ var import_types49 = require("@neat.is/types");
12524
13077
  function dropOrphanedFileNodes(graph) {
12525
13078
  const orphans = [];
12526
13079
  graph.forEachNode((id, attrs) => {
12527
- if (attrs.type !== import_types48.NodeType.FileNode) return;
13080
+ if (attrs.type !== import_types49.NodeType.FileNode) return;
12528
13081
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
12529
13082
  orphans.push(id);
12530
13083
  }
@@ -12537,14 +13090,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
12537
13090
  const bases = [scanPath, ...serviceDirs];
12538
13091
  graph.forEachEdge((id, attrs) => {
12539
13092
  const edge = attrs;
12540
- if (edge.provenance !== import_types48.Provenance.EXTRACTED) return;
13093
+ if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
12541
13094
  const evidenceFile = edge.evidence?.file;
12542
13095
  if (!evidenceFile) return;
12543
- if (import_node_path57.default.isAbsolute(evidenceFile)) {
13096
+ if (import_node_path58.default.isAbsolute(evidenceFile)) {
12544
13097
  if (!(0, import_node_fs25.existsSync)(evidenceFile)) toDrop.push(id);
12545
13098
  return;
12546
13099
  }
12547
- const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path57.default.join(base, evidenceFile)));
13100
+ const found = bases.some((base) => (0, import_node_fs25.existsSync)(import_node_path58.default.join(base, evidenceFile)));
12548
13101
  if (!found) toDrop.push(id);
12549
13102
  });
12550
13103
  for (const id of toDrop) graph.dropEdge(id);
@@ -12601,7 +13154,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12601
13154
  }
12602
13155
  const droppedEntries = drainDroppedExtracted();
12603
13156
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
12604
- const rejectedPath = import_node_path58.default.join(import_node_path58.default.dirname(opts.errorsPath), "rejected.ndjson");
13157
+ const rejectedPath = import_node_path59.default.join(import_node_path59.default.dirname(opts.errorsPath), "rejected.ndjson");
12605
13158
  try {
12606
13159
  await writeRejectedExtracted(droppedEntries, rejectedPath);
12607
13160
  } catch (err) {
@@ -12636,8 +13189,8 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
12636
13189
  // src/persist.ts
12637
13190
  init_cjs_shims();
12638
13191
  var import_node_fs26 = require("fs");
12639
- var import_node_path59 = __toESM(require("path"), 1);
12640
- var import_types49 = require("@neat.is/types");
13192
+ var import_node_path60 = __toESM(require("path"), 1);
13193
+ var import_types50 = require("@neat.is/types");
12641
13194
  var SCHEMA_VERSION = 7;
12642
13195
  function migrateV1ToV2(payload) {
12643
13196
  const nodes = payload.graph.nodes;
@@ -12661,7 +13214,7 @@ function migrateV5ToV6(payload) {
12661
13214
  if (Array.isArray(nodes)) {
12662
13215
  for (const node of nodes) {
12663
13216
  const attrs = node.attributes;
12664
- if (!attrs || attrs.type !== import_types49.NodeType.InfraNode) continue;
13217
+ if (!attrs || attrs.type !== import_types50.NodeType.InfraNode) continue;
12665
13218
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
12666
13219
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
12667
13220
  }
@@ -12677,12 +13230,12 @@ function migrateV2ToV3(payload) {
12677
13230
  for (const edge of edges) {
12678
13231
  const attrs = edge.attributes;
12679
13232
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
12680
- attrs.provenance = import_types49.Provenance.OBSERVED;
13233
+ attrs.provenance = import_types50.Provenance.OBSERVED;
12681
13234
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
12682
13235
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
12683
13236
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
12684
13237
  if (type && source && target) {
12685
- const newId = (0, import_types49.observedEdgeId)(source, target, type);
13238
+ const newId = (0, import_types50.observedEdgeId)(source, target, type);
12686
13239
  attrs.id = newId;
12687
13240
  if (edge.key) edge.key = newId;
12688
13241
  }
@@ -12691,7 +13244,7 @@ function migrateV2ToV3(payload) {
12691
13244
  return { ...payload, schemaVersion: 3 };
12692
13245
  }
12693
13246
  async function ensureDir(filePath) {
12694
- await import_node_fs26.promises.mkdir(import_node_path59.default.dirname(filePath), { recursive: true });
13247
+ await import_node_fs26.promises.mkdir(import_node_path60.default.dirname(filePath), { recursive: true });
12695
13248
  }
12696
13249
  async function saveGraphToDisk(graph, outPath) {
12697
13250
  await ensureDir(outPath);
@@ -12783,19 +13336,19 @@ function startPersistLoop(graph, outPath, opts = {}) {
12783
13336
  init_cjs_shims();
12784
13337
  var import_fastify2 = __toESM(require("fastify"), 1);
12785
13338
  var import_cors = __toESM(require("@fastify/cors"), 1);
12786
- var import_types79 = require("@neat.is/types");
13339
+ var import_types80 = require("@neat.is/types");
12787
13340
 
12788
13341
  // src/extend/index.ts
12789
13342
  init_cjs_shims();
12790
13343
  var import_node_fs28 = require("fs");
12791
- var import_node_path61 = __toESM(require("path"), 1);
13344
+ var import_node_path62 = __toESM(require("path"), 1);
12792
13345
  var import_node_os2 = __toESM(require("os"), 1);
12793
13346
  var import_instrumentation_registry = require("@neat.is/instrumentation-registry");
12794
13347
 
12795
13348
  // src/installers/package-manager.ts
12796
13349
  init_cjs_shims();
12797
13350
  var import_node_fs27 = require("fs");
12798
- var import_node_path60 = __toESM(require("path"), 1);
13351
+ var import_node_path61 = __toESM(require("path"), 1);
12799
13352
  var import_node_child_process = require("child_process");
12800
13353
  var LOCKFILE_PRIORITY = [
12801
13354
  { lockfile: "bun.lockb", pm: "bun", args: ["install", "--no-summary"] },
@@ -12817,22 +13370,22 @@ async function exists2(p) {
12817
13370
  }
12818
13371
  }
12819
13372
  async function detectPackageManager(serviceDir) {
12820
- let dir = import_node_path60.default.resolve(serviceDir);
13373
+ let dir = import_node_path61.default.resolve(serviceDir);
12821
13374
  const stops = /* @__PURE__ */ new Set();
12822
13375
  for (let i = 0; i < 64; i++) {
12823
13376
  if (stops.has(dir)) break;
12824
13377
  stops.add(dir);
12825
13378
  for (const candidate of LOCKFILE_PRIORITY) {
12826
- const lockPath = import_node_path60.default.join(dir, candidate.lockfile);
13379
+ const lockPath = import_node_path61.default.join(dir, candidate.lockfile);
12827
13380
  if (await exists2(lockPath)) {
12828
13381
  return { pm: candidate.pm, cwd: dir, args: [...candidate.args] };
12829
13382
  }
12830
13383
  }
12831
- const parent = import_node_path60.default.dirname(dir);
13384
+ const parent = import_node_path61.default.dirname(dir);
12832
13385
  if (parent === dir) break;
12833
13386
  dir = parent;
12834
13387
  }
12835
- return { pm: "npm", cwd: import_node_path60.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
13388
+ return { pm: "npm", cwd: import_node_path61.default.resolve(serviceDir), args: [...NPM_FALLBACK_ARGS] };
12836
13389
  }
12837
13390
  async function runPackageManagerInstall(cmd) {
12838
13391
  return new Promise((resolve) => {
@@ -12881,7 +13434,7 @@ async function fileExists2(p) {
12881
13434
  }
12882
13435
  }
12883
13436
  async function readPackageJson(scanPath) {
12884
- const pkgPath = import_node_path61.default.join(scanPath, "package.json");
13437
+ const pkgPath = import_node_path62.default.join(scanPath, "package.json");
12885
13438
  const raw = await import_node_fs28.promises.readFile(pkgPath, "utf8");
12886
13439
  return JSON.parse(raw);
12887
13440
  }
@@ -12895,27 +13448,27 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
12895
13448
  ]);
12896
13449
  async function findHookFiles(scanPath) {
12897
13450
  const found = [];
12898
- const walk8 = async (dir) => {
13451
+ const walk9 = async (dir) => {
12899
13452
  const entries = await import_node_fs28.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
12900
13453
  for (const entry of entries) {
12901
13454
  if (entry.isDirectory()) {
12902
13455
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
12903
- await walk8(import_node_path61.default.join(dir, entry.name));
13456
+ await walk9(import_node_path62.default.join(dir, entry.name));
12904
13457
  } else if (entry.isFile()) {
12905
13458
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
12906
- const rel = import_node_path61.default.relative(scanPath, import_node_path61.default.join(dir, entry.name));
12907
- found.push(rel.split(import_node_path61.default.sep).join("/"));
13459
+ const rel = import_node_path62.default.relative(scanPath, import_node_path62.default.join(dir, entry.name));
13460
+ found.push(rel.split(import_node_path62.default.sep).join("/"));
12908
13461
  }
12909
13462
  }
12910
13463
  }
12911
13464
  };
12912
- await walk8(scanPath);
13465
+ await walk9(scanPath);
12913
13466
  return found.sort();
12914
13467
  }
12915
13468
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
12916
13469
  let fallback = null;
12917
13470
  for (const file of hookFiles) {
12918
- const content = await import_node_fs28.promises.readFile(import_node_path61.default.join(scanPath, file), "utf8");
13471
+ const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(scanPath, file), "utf8");
12919
13472
  const patched = splicedContent(content, snippet2);
12920
13473
  if (patched !== null) return { file, content, patched };
12921
13474
  if (fallback === null) fallback = { file, content };
@@ -12923,11 +13476,11 @@ async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
12923
13476
  return { file: fallback.file, content: fallback.content, patched: null };
12924
13477
  }
12925
13478
  function extendLogPath() {
12926
- return process.env.NEAT_EXTEND_LOG ?? import_node_path61.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
13479
+ return process.env.NEAT_EXTEND_LOG ?? import_node_path62.default.join(import_node_os2.default.homedir(), ".neat", "extend-log.ndjson");
12927
13480
  }
12928
13481
  async function appendExtendLog(entry) {
12929
13482
  const logPath = extendLogPath();
12930
- await import_node_fs28.promises.mkdir(import_node_path61.default.dirname(logPath), { recursive: true });
13483
+ await import_node_fs28.promises.mkdir(import_node_path62.default.dirname(logPath), { recursive: true });
12931
13484
  await import_node_fs28.promises.appendFile(logPath, JSON.stringify(entry) + "\n", "utf8");
12932
13485
  }
12933
13486
  function splicedContent(fileContent, snippet2) {
@@ -12986,7 +13539,7 @@ function lookupInstrumentation(library, installedVersion) {
12986
13539
  }
12987
13540
  async function describeProjectInstrumentation(ctx) {
12988
13541
  const hookFiles = await findHookFiles(ctx.scanPath);
12989
- const envNeat = await fileExists2(import_node_path61.default.join(ctx.scanPath, ".env.neat"));
13542
+ const envNeat = await fileExists2(import_node_path62.default.join(ctx.scanPath, ".env.neat"));
12990
13543
  const registryInstrPackages = new Set(
12991
13544
  (0, import_instrumentation_registry.list)().map((e) => e.instrumentation_package).filter((p) => !!p)
12992
13545
  );
@@ -13008,7 +13561,7 @@ async function applyExtension(ctx, args, options) {
13008
13561
  );
13009
13562
  }
13010
13563
  for (const file of hookFiles) {
13011
- const content = await import_node_fs28.promises.readFile(import_node_path61.default.join(ctx.scanPath, file), "utf8");
13564
+ const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(ctx.scanPath, file), "utf8");
13012
13565
  if (content.includes(args.registration_snippet)) {
13013
13566
  return { library: args.library, filesTouched: [], depsAdded: [], installOutput: "", alreadyApplied: true };
13014
13567
  }
@@ -13020,10 +13573,10 @@ async function applyExtension(ctx, args, options) {
13020
13573
  );
13021
13574
  }
13022
13575
  const primaryFile = primary.file;
13023
- const primaryPath = import_node_path61.default.join(ctx.scanPath, primaryFile);
13576
+ const primaryPath = import_node_path62.default.join(ctx.scanPath, primaryFile);
13024
13577
  const filesTouched = [];
13025
13578
  const depsAdded = [];
13026
- const pkgPath = import_node_path61.default.join(ctx.scanPath, "package.json");
13579
+ const pkgPath = import_node_path62.default.join(ctx.scanPath, "package.json");
13027
13580
  const pkg = await readPackageJson(ctx.scanPath);
13028
13581
  if (!(pkg.dependencies ?? {})[args.instrumentation_package]) {
13029
13582
  pkg.dependencies = { ...pkg.dependencies ?? {}, [args.instrumentation_package]: args.version };
@@ -13062,7 +13615,7 @@ async function dryRunExtension(ctx, args) {
13062
13615
  };
13063
13616
  }
13064
13617
  for (const file of hookFiles) {
13065
- const content = await import_node_fs28.promises.readFile(import_node_path61.default.join(ctx.scanPath, file), "utf8");
13618
+ const content = await import_node_fs28.promises.readFile(import_node_path62.default.join(ctx.scanPath, file), "utf8");
13066
13619
  if (content.includes(args.registration_snippet)) {
13067
13620
  return {
13068
13621
  library: args.library,
@@ -13103,7 +13656,7 @@ async function rollbackExtension(ctx, args) {
13103
13656
  if (!match) {
13104
13657
  return { undone: false, message: "no apply found for library" };
13105
13658
  }
13106
- const pkgPath = import_node_path61.default.join(ctx.scanPath, "package.json");
13659
+ const pkgPath = import_node_path62.default.join(ctx.scanPath, "package.json");
13107
13660
  if (await fileExists2(pkgPath)) {
13108
13661
  const pkg = await readPackageJson(ctx.scanPath);
13109
13662
  if (pkg.dependencies?.[match.instrumentation_package]) {
@@ -13114,7 +13667,7 @@ async function rollbackExtension(ctx, args) {
13114
13667
  }
13115
13668
  const hookFiles = await findHookFiles(ctx.scanPath);
13116
13669
  for (const file of hookFiles) {
13117
- const filePath = import_node_path61.default.join(ctx.scanPath, file);
13670
+ const filePath = import_node_path62.default.join(ctx.scanPath, file);
13118
13671
  const content = await import_node_fs28.promises.readFile(filePath, "utf8");
13119
13672
  if (content.includes(match.registration_snippet)) {
13120
13673
  const filtered = content.split("\n").filter((line) => !line.includes(match.registration_snippet)).join("\n");
@@ -13130,39 +13683,39 @@ async function rollbackExtension(ctx, args) {
13130
13683
 
13131
13684
  // src/divergences.ts
13132
13685
  init_cjs_shims();
13133
- var import_types50 = require("@neat.is/types");
13686
+ var import_types51 = require("@neat.is/types");
13134
13687
  function bucketKey(source, target, type) {
13135
13688
  return `${type}|${source}|${target}`;
13136
13689
  }
13137
13690
  function bucketSourceFor(graph, edge) {
13138
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) return edge.source;
13139
- const parsed = (0, import_types50.parseFileId)(edge.source);
13691
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) return edge.source;
13692
+ const parsed = (0, import_types51.parseFileId)(edge.source);
13140
13693
  if (!parsed || !graph.hasNode(edge.target)) return edge.source;
13141
13694
  const target = graph.getNodeAttributes(edge.target);
13142
- if (target.type !== import_types50.NodeType.DatabaseNode) return edge.source;
13143
- return (0, import_types50.serviceId)(parsed.service);
13695
+ if (target.type !== import_types51.NodeType.DatabaseNode) return edge.source;
13696
+ return (0, import_types51.serviceId)(parsed.service);
13144
13697
  }
13145
13698
  function bucketEdges(graph) {
13146
13699
  const buckets2 = /* @__PURE__ */ new Map();
13147
13700
  graph.forEachEdge((id, attrs) => {
13148
13701
  const e = attrs;
13149
- const parsed = (0, import_types50.parseEdgeId)(id);
13702
+ const parsed = (0, import_types51.parseEdgeId)(id);
13150
13703
  const provenance = parsed?.provenance ?? e.provenance;
13151
13704
  const source = bucketSourceFor(graph, e);
13152
13705
  const key = bucketKey(source, e.target, e.type);
13153
13706
  const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
13154
13707
  switch (provenance) {
13155
- case import_types50.Provenance.EXTRACTED:
13708
+ case import_types51.Provenance.EXTRACTED:
13156
13709
  cur.extracted = e;
13157
13710
  break;
13158
- case import_types50.Provenance.OBSERVED:
13711
+ case import_types51.Provenance.OBSERVED:
13159
13712
  cur.observed = e;
13160
13713
  break;
13161
- case import_types50.Provenance.INFERRED:
13714
+ case import_types51.Provenance.INFERRED:
13162
13715
  cur.inferred = e;
13163
13716
  break;
13164
13717
  default:
13165
- if (e.provenance === import_types50.Provenance.STALE) cur.stale = e;
13718
+ if (e.provenance === import_types51.Provenance.STALE) cur.stale = e;
13166
13719
  }
13167
13720
  buckets2.set(key, cur);
13168
13721
  });
@@ -13171,22 +13724,22 @@ function bucketEdges(graph) {
13171
13724
  function nodeIsFrontier(graph, nodeId) {
13172
13725
  if (!graph.hasNode(nodeId)) return false;
13173
13726
  const attrs = graph.getNodeAttributes(nodeId);
13174
- return attrs.type === import_types50.NodeType.FrontierNode;
13727
+ return attrs.type === import_types51.NodeType.FrontierNode;
13175
13728
  }
13176
13729
  function nodeIsWebsocketChannel(graph, nodeId) {
13177
13730
  if (!graph.hasNode(nodeId)) return false;
13178
13731
  const attrs = graph.getNodeAttributes(nodeId);
13179
- return attrs.type === import_types50.NodeType.WebSocketChannelNode;
13732
+ return attrs.type === import_types51.NodeType.WebSocketChannelNode;
13180
13733
  }
13181
13734
  function nodeIsServerAction(graph, nodeId) {
13182
13735
  if (!graph.hasNode(nodeId)) return false;
13183
13736
  const attrs = graph.getNodeAttributes(nodeId);
13184
- return attrs.type === import_types50.NodeType.ServerActionNode;
13737
+ return attrs.type === import_types51.NodeType.ServerActionNode;
13185
13738
  }
13186
13739
  function nodeIsSymbol(graph, nodeId) {
13187
13740
  if (!graph.hasNode(nodeId)) return false;
13188
13741
  const attrs = graph.getNodeAttributes(nodeId);
13189
- return attrs.type === import_types50.NodeType.SymbolNode;
13742
+ return attrs.type === import_types51.NodeType.SymbolNode;
13190
13743
  }
13191
13744
  function clampConfidence(n) {
13192
13745
  if (!Number.isFinite(n)) return 0;
@@ -13206,14 +13759,14 @@ function gradedConfidence(edge) {
13206
13759
  return clampConfidence(confidenceForEdge(edge));
13207
13760
  }
13208
13761
  var OBSERVABLE_EDGE_TYPES = /* @__PURE__ */ new Set([
13209
- import_types50.EdgeType.CALLS,
13210
- import_types50.EdgeType.CONNECTS_TO,
13211
- import_types50.EdgeType.PUBLISHES_TO,
13212
- import_types50.EdgeType.CONSUMES_FROM
13762
+ import_types51.EdgeType.CALLS,
13763
+ import_types51.EdgeType.CONNECTS_TO,
13764
+ import_types51.EdgeType.PUBLISHES_TO,
13765
+ import_types51.EdgeType.CONSUMES_FROM
13213
13766
  ]);
13214
13767
  function detectMissingDivergences(graph, bucket) {
13215
13768
  const out = [];
13216
- if (bucket.type === import_types50.EdgeType.CONTAINS) return out;
13769
+ if (bucket.type === import_types51.EdgeType.CONTAINS) return out;
13217
13770
  if (nodeIsSymbol(graph, bucket.source) || nodeIsSymbol(graph, bucket.target)) return out;
13218
13771
  if (bucket.extracted && !bucket.observed && OBSERVABLE_EDGE_TYPES.has(bucket.type)) {
13219
13772
  if (!nodeIsFrontier(graph, bucket.target) && !nodeIsServerAction(graph, bucket.target)) {
@@ -13255,7 +13808,7 @@ function declaredHostFor(svc) {
13255
13808
  function hasExtractedConfiguredBy(graph, svcId) {
13256
13809
  for (const edgeId of graph.outboundEdges(svcId)) {
13257
13810
  const e = graph.getEdgeAttributes(edgeId);
13258
- if (e.type === import_types50.EdgeType.CONFIGURED_BY && e.provenance === import_types50.Provenance.EXTRACTED) {
13811
+ if (e.type === import_types51.EdgeType.CONFIGURED_BY && e.provenance === import_types51.Provenance.EXTRACTED) {
13259
13812
  return true;
13260
13813
  }
13261
13814
  }
@@ -13268,10 +13821,10 @@ function detectHostMismatch(graph, svcId, svc) {
13268
13821
  const out = [];
13269
13822
  for (const edgeId of graph.outboundEdges(svcId)) {
13270
13823
  const edge = graph.getEdgeAttributes(edgeId);
13271
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13272
- if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
13824
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) continue;
13825
+ if (edge.provenance !== import_types51.Provenance.OBSERVED) continue;
13273
13826
  const target = graph.getNodeAttributes(edge.target);
13274
- if (target.type !== import_types50.NodeType.DatabaseNode) continue;
13827
+ if (target.type !== import_types51.NodeType.DatabaseNode) continue;
13275
13828
  const observedHost = target.host?.trim();
13276
13829
  if (!observedHost) continue;
13277
13830
  if (observedHost === declaredHost) continue;
@@ -13293,10 +13846,10 @@ function detectCompatDivergences(graph, svcId, svc) {
13293
13846
  const deps = svc.dependencies ?? {};
13294
13847
  for (const edgeId of graph.outboundEdges(svcId)) {
13295
13848
  const edge = graph.getEdgeAttributes(edgeId);
13296
- if (edge.type !== import_types50.EdgeType.CONNECTS_TO) continue;
13297
- if (edge.provenance !== import_types50.Provenance.OBSERVED) continue;
13849
+ if (edge.type !== import_types51.EdgeType.CONNECTS_TO) continue;
13850
+ if (edge.provenance !== import_types51.Provenance.OBSERVED) continue;
13298
13851
  const target = graph.getNodeAttributes(edge.target);
13299
- if (target.type !== import_types50.NodeType.DatabaseNode) continue;
13852
+ if (target.type !== import_types51.NodeType.DatabaseNode) continue;
13300
13853
  for (const pair of compatPairs()) {
13301
13854
  if (pair.engine !== target.engine) continue;
13302
13855
  const declared = deps[pair.driver];
@@ -13393,7 +13946,7 @@ function suppressHostMismatchHalves(all) {
13393
13946
  for (const d of all) {
13394
13947
  if (d.type !== "host-mismatch") continue;
13395
13948
  observedHalf.add(`${d.source}->${d.target}`);
13396
- declaredHalf.add((0, import_types50.databaseId)(d.extractedHost));
13949
+ declaredHalf.add((0, import_types51.databaseId)(d.extractedHost));
13397
13950
  }
13398
13951
  if (observedHalf.size === 0) return all;
13399
13952
  return all.filter((d) => {
@@ -13412,13 +13965,13 @@ function computeDivergences(graph, opts = {}) {
13412
13965
  }
13413
13966
  graph.forEachNode((nodeId, attrs) => {
13414
13967
  const n = attrs;
13415
- if (n.type === import_types50.NodeType.ServiceNode) {
13968
+ if (n.type === import_types51.NodeType.ServiceNode) {
13416
13969
  const svc = n;
13417
13970
  for (const d of detectHostMismatch(graph, nodeId, svc)) all.push(d);
13418
13971
  for (const d of detectCompatDivergences(graph, nodeId, svc)) all.push(d);
13419
13972
  return;
13420
13973
  }
13421
- if (n.type === import_types50.NodeType.InfraNode && n.kind === "sql-table") {
13974
+ if (n.type === import_types51.NodeType.InfraNode && n.kind === "sql-table") {
13422
13975
  for (const d of detectColumnDrift(n)) all.push(d);
13423
13976
  }
13424
13977
  });
@@ -13454,7 +14007,7 @@ function computeDivergences(graph, opts = {}) {
13454
14007
  const bc = "column" in b && b.column ? b.column : "";
13455
14008
  return ac.localeCompare(bc);
13456
14009
  });
13457
- return import_types50.DivergenceResultSchema.parse({
14010
+ return import_types51.DivergenceResultSchema.parse({
13458
14011
  divergences: filtered,
13459
14012
  totalAffected: filtered.length,
13460
14013
  computedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13585,23 +14138,23 @@ function canonicalJson(value) {
13585
14138
 
13586
14139
  // src/projects.ts
13587
14140
  init_cjs_shims();
13588
- var import_node_path62 = __toESM(require("path"), 1);
14141
+ var import_node_path63 = __toESM(require("path"), 1);
13589
14142
  function pathsForProject(project, baseDir) {
13590
14143
  if (project === DEFAULT_PROJECT) {
13591
14144
  return {
13592
- snapshotPath: import_node_path62.default.join(baseDir, "graph.json"),
13593
- errorsPath: import_node_path62.default.join(baseDir, "errors.ndjson"),
13594
- staleEventsPath: import_node_path62.default.join(baseDir, "stale-events.ndjson"),
13595
- embeddingsCachePath: import_node_path62.default.join(baseDir, "embeddings.json"),
13596
- policyViolationsPath: import_node_path62.default.join(baseDir, "policy-violations.ndjson")
14145
+ snapshotPath: import_node_path63.default.join(baseDir, "graph.json"),
14146
+ errorsPath: import_node_path63.default.join(baseDir, "errors.ndjson"),
14147
+ staleEventsPath: import_node_path63.default.join(baseDir, "stale-events.ndjson"),
14148
+ embeddingsCachePath: import_node_path63.default.join(baseDir, "embeddings.json"),
14149
+ policyViolationsPath: import_node_path63.default.join(baseDir, "policy-violations.ndjson")
13597
14150
  };
13598
14151
  }
13599
14152
  return {
13600
- snapshotPath: import_node_path62.default.join(baseDir, `${project}.json`),
13601
- errorsPath: import_node_path62.default.join(baseDir, `errors.${project}.ndjson`),
13602
- staleEventsPath: import_node_path62.default.join(baseDir, `stale-events.${project}.ndjson`),
13603
- embeddingsCachePath: import_node_path62.default.join(baseDir, `embeddings.${project}.json`),
13604
- policyViolationsPath: import_node_path62.default.join(baseDir, `policy-violations.${project}.ndjson`)
14153
+ snapshotPath: import_node_path63.default.join(baseDir, `${project}.json`),
14154
+ errorsPath: import_node_path63.default.join(baseDir, `errors.${project}.ndjson`),
14155
+ staleEventsPath: import_node_path63.default.join(baseDir, `stale-events.${project}.ndjson`),
14156
+ embeddingsCachePath: import_node_path63.default.join(baseDir, `embeddings.${project}.json`),
14157
+ policyViolationsPath: import_node_path63.default.join(baseDir, `policy-violations.${project}.ndjson`)
13605
14158
  };
13606
14159
  }
13607
14160
  var Projects = class {
@@ -13639,26 +14192,26 @@ var Projects = class {
13639
14192
  init_cjs_shims();
13640
14193
  var import_node_fs30 = require("fs");
13641
14194
  var import_node_os3 = __toESM(require("os"), 1);
13642
- var import_node_path63 = __toESM(require("path"), 1);
13643
- var import_types51 = require("@neat.is/types");
14195
+ var import_node_path64 = __toESM(require("path"), 1);
14196
+ var import_types52 = require("@neat.is/types");
13644
14197
  var LOCK_TIMEOUT_MS = 5e3;
13645
14198
  var LOCK_RETRY_MS = 50;
13646
14199
  function neatHome() {
13647
14200
  const override = process.env.NEAT_HOME;
13648
- if (override && override.length > 0) return import_node_path63.default.resolve(override);
13649
- return import_node_path63.default.join(import_node_os3.default.homedir(), ".neat");
14201
+ if (override && override.length > 0) return import_node_path64.default.resolve(override);
14202
+ return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
13650
14203
  }
13651
14204
  function registryPath() {
13652
- return import_node_path63.default.join(neatHome(), "projects.json");
14205
+ return import_node_path64.default.join(neatHome(), "projects.json");
13653
14206
  }
13654
14207
  function registryLockPath() {
13655
- return import_node_path63.default.join(neatHome(), "projects.json.lock");
14208
+ return import_node_path64.default.join(neatHome(), "projects.json.lock");
13656
14209
  }
13657
14210
  function daemonPidPath() {
13658
- return import_node_path63.default.join(neatHome(), "neatd.pid");
14211
+ return import_node_path64.default.join(neatHome(), "neatd.pid");
13659
14212
  }
13660
14213
  function daemonsDir() {
13661
- return import_node_path63.default.join(neatHome(), "daemons");
14214
+ return import_node_path64.default.join(neatHome(), "daemons");
13662
14215
  }
13663
14216
  function isFiniteInt(v) {
13664
14217
  return typeof v === "number" && Number.isFinite(v);
@@ -13699,7 +14252,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
13699
14252
  const out = [];
13700
14253
  for (const name of names) {
13701
14254
  if (!name.endsWith(".json")) continue;
13702
- const file = import_node_path63.default.join(dir, name);
14255
+ const file = import_node_path64.default.join(dir, name);
13703
14256
  let raw;
13704
14257
  try {
13705
14258
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -13776,7 +14329,7 @@ function lockHolderMessage(holder, lockPath, timeoutMs) {
13776
14329
  }
13777
14330
  }
13778
14331
  async function normalizeProjectPath(input) {
13779
- const resolved = import_node_path63.default.resolve(input);
14332
+ const resolved = import_node_path64.default.resolve(input);
13780
14333
  try {
13781
14334
  return await import_node_fs30.promises.realpath(resolved);
13782
14335
  } catch {
@@ -13784,7 +14337,7 @@ async function normalizeProjectPath(input) {
13784
14337
  }
13785
14338
  }
13786
14339
  async function writeAtomically(target, contents) {
13787
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(target), { recursive: true });
14340
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(target), { recursive: true });
13788
14341
  const tmp = `${target}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
13789
14342
  const fd = await import_node_fs30.promises.open(tmp, "w");
13790
14343
  try {
@@ -13797,7 +14350,7 @@ async function writeAtomically(target, contents) {
13797
14350
  }
13798
14351
  async function acquireLock(lockPath, timeoutMs = LOCK_TIMEOUT_MS, probe = defaultLockHolderProbe) {
13799
14352
  const deadline = Date.now() + timeoutMs;
13800
- await import_node_fs30.promises.mkdir(import_node_path63.default.dirname(lockPath), { recursive: true });
14353
+ await import_node_fs30.promises.mkdir(import_node_path64.default.dirname(lockPath), { recursive: true });
13801
14354
  let probedHolder = false;
13802
14355
  while (true) {
13803
14356
  try {
@@ -13850,10 +14403,10 @@ async function readRegistry() {
13850
14403
  throw err;
13851
14404
  }
13852
14405
  const parsed = JSON.parse(raw);
13853
- return import_types51.RegistryFileSchema.parse(parsed);
14406
+ return import_types52.RegistryFileSchema.parse(parsed);
13854
14407
  }
13855
14408
  async function writeRegistry(reg) {
13856
- const validated = import_types51.RegistryFileSchema.parse(reg);
14409
+ const validated = import_types52.RegistryFileSchema.parse(reg);
13857
14410
  await writeAtomically(registryPath(), JSON.stringify(validated, null, 2) + "\n");
13858
14411
  }
13859
14412
  var ProjectNameCollisionError = class extends Error {
@@ -14048,7 +14601,7 @@ init_auth();
14048
14601
  // src/connectors-config.ts
14049
14602
  init_cjs_shims();
14050
14603
  var import_node_os4 = __toESM(require("os"), 1);
14051
- var import_node_path64 = __toESM(require("path"), 1);
14604
+ var import_node_path65 = __toESM(require("path"), 1);
14052
14605
  var import_node_fs31 = require("fs");
14053
14606
  var CONNECTORS_CONFIG_VERSION = 1;
14054
14607
  var EnvRefUnsetError = class extends Error {
@@ -14063,11 +14616,11 @@ var EnvRefUnsetError = class extends Error {
14063
14616
  };
14064
14617
  function neatHome2() {
14065
14618
  const override = process.env.NEAT_HOME;
14066
- if (override && override.length > 0) return import_node_path64.default.resolve(override);
14067
- return import_node_path64.default.join(import_node_os4.default.homedir(), ".neat");
14619
+ if (override && override.length > 0) return import_node_path65.default.resolve(override);
14620
+ return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
14068
14621
  }
14069
14622
  function connectorsConfigPath(home = neatHome2()) {
14070
- return import_node_path64.default.join(home, "connectors.json");
14623
+ return import_node_path65.default.join(home, "connectors.json");
14071
14624
  }
14072
14625
  var MODE_MASK_LOOSER_THAN_0600 = 63;
14073
14626
  async function warnIfModeLooserThan0600(file) {
@@ -14254,15 +14807,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
14254
14807
 
14255
14808
  // src/connectors/index.ts
14256
14809
  init_cjs_shims();
14257
- var import_types52 = require("@neat.is/types");
14810
+ var import_types53 = require("@neat.is/types");
14258
14811
  var NO_ENV = "unknown";
14259
14812
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
14260
14813
  if (!graph.hasNode(targetNodeId)) return void 0;
14261
14814
  const sites = [];
14262
14815
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
14263
14816
  const edge = graph.getEdgeAttributes(edgeId);
14264
- if (edge.provenance !== import_types52.Provenance.EXTRACTED) continue;
14265
- const parsed = (0, import_types52.parseFileId)(edge.source);
14817
+ if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
14818
+ const parsed = (0, import_types53.parseFileId)(edge.source);
14266
14819
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
14267
14820
  const site = { relPath: edge.evidence.file };
14268
14821
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -14273,7 +14826,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
14273
14826
  function routeCallSiteFor(graph, targetNodeId) {
14274
14827
  if (!graph.hasNode(targetNodeId)) return void 0;
14275
14828
  const attrs = graph.getNodeAttributes(targetNodeId);
14276
- if (attrs.type !== import_types52.NodeType.RouteNode || !attrs.path) return void 0;
14829
+ if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
14277
14830
  const site = { relPath: attrs.path };
14278
14831
  if (attrs.line !== void 0) site.line = attrs.line;
14279
14832
  return site;
@@ -14754,10 +15307,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
14754
15307
  // src/connectors/supabase/map.ts
14755
15308
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
14756
15309
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
14757
- function targetFromRestPath(path67) {
14758
- const rpcMatch = REST_RPC_PATH_RE.exec(path67);
15310
+ function targetFromRestPath(path68) {
15311
+ const rpcMatch = REST_RPC_PATH_RE.exec(path68);
14759
15312
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
14760
- const tableMatch = REST_TABLE_PATH_RE.exec(path67);
15313
+ const tableMatch = REST_TABLE_PATH_RE.exec(path68);
14761
15314
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
14762
15315
  return null;
14763
15316
  }
@@ -14868,23 +15421,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
14868
15421
 
14869
15422
  // src/connectors/supabase/resolve.ts
14870
15423
  init_cjs_shims();
14871
- var import_types54 = require("@neat.is/types");
15424
+ var import_types55 = require("@neat.is/types");
14872
15425
  function createSupabaseResolveTarget(graph, config) {
14873
15426
  return (signal, _ctx) => {
14874
15427
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
14875
15428
  return null;
14876
15429
  }
14877
- const subResourceId = (0, import_types54.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15430
+ const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
14878
15431
  if (graph.hasNode(subResourceId)) {
14879
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15432
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14880
15433
  }
14881
- const bareResourceId = (0, import_types54.infraId)(signal.targetKind, signal.targetName);
15434
+ const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
14882
15435
  if (graph.hasNode(bareResourceId)) {
14883
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15436
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14884
15437
  }
14885
- const projectLevelId = (0, import_types54.infraId)("supabase", config.nodeRef);
15438
+ const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
14886
15439
  if (graph.hasNode(projectLevelId)) {
14887
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15440
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14888
15441
  }
14889
15442
  return null;
14890
15443
  };
@@ -14977,7 +15530,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
14977
15530
 
14978
15531
  // src/connectors/railway/index.ts
14979
15532
  init_cjs_shims();
14980
- var import_types58 = require("@neat.is/types");
15533
+ var import_types59 = require("@neat.is/types");
14981
15534
 
14982
15535
  // src/connectors/railway/client.ts
14983
15536
  init_cjs_shims();
@@ -15128,7 +15681,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
15128
15681
  const out = [];
15129
15682
  graph.forEachNode((_id, attrs) => {
15130
15683
  const node = attrs;
15131
- if (node.type !== import_types58.NodeType.RouteNode) return;
15684
+ if (node.type !== import_types59.NodeType.RouteNode) return;
15132
15685
  const route = attrs;
15133
15686
  if (route.service !== serviceName) return;
15134
15687
  out.push({
@@ -15232,12 +15785,12 @@ function createRailwayResolveTarget(config) {
15232
15785
  const serviceName = config.serviceNameById[config.serviceId];
15233
15786
  if (!serviceName) return null;
15234
15787
  if (signal.targetKind === ROUTE_TARGET_KIND) {
15235
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types58.EdgeType.CALLS };
15788
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
15236
15789
  }
15237
15790
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
15238
15791
  const peerName = config.serviceNameById[signal.targetName];
15239
15792
  if (!peerName) return null;
15240
- return { targetNodeId: (0, import_types58.serviceId)(peerName), serviceName, edgeType: import_types58.EdgeType.CONNECTS_TO };
15793
+ return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
15241
15794
  }
15242
15795
  return null;
15243
15796
  };
@@ -15361,9 +15914,9 @@ function parseFirebaseTargetName(targetName) {
15361
15914
  const secondSep = rest.indexOf(FIELD_SEP);
15362
15915
  if (secondSep === -1) return null;
15363
15916
  const method = rest.slice(0, secondSep);
15364
- const path67 = rest.slice(secondSep + 1);
15365
- if (!resourceName || !method || !path67) return null;
15366
- return { resourceName, method, path: path67 };
15917
+ const path68 = rest.slice(secondSep + 1);
15918
+ if (!resourceName || !method || !path68) return null;
15919
+ return { resourceName, method, path: path68 };
15367
15920
  }
15368
15921
  function resourceNameFor(type, labels) {
15369
15922
  if (!labels) return null;
@@ -15401,14 +15954,14 @@ function mapLogEntryToSignal(entry) {
15401
15954
  if (!req) return null;
15402
15955
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
15403
15956
  const method = req.requestMethod.toUpperCase();
15404
- const path67 = pathFromRequestUrl(req.requestUrl);
15405
- if (path67 === null) return null;
15957
+ const path68 = pathFromRequestUrl(req.requestUrl);
15958
+ if (path68 === null) return null;
15406
15959
  const timestamp = entry.timestamp;
15407
15960
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15408
15961
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
15409
15962
  return {
15410
15963
  targetKind: resourceType,
15411
- targetName: packFirebaseTargetName({ resourceName, method, path: path67 }),
15964
+ targetName: packFirebaseTargetName({ resourceName, method, path: path68 }),
15412
15965
  callCount: 1,
15413
15966
  errorCount: isError ? 1 : 0,
15414
15967
  lastObservedIso: timestamp
@@ -15425,7 +15978,7 @@ function mapLogEntriesToSignals(entries) {
15425
15978
 
15426
15979
  // src/connectors/firebase/resolve.ts
15427
15980
  init_cjs_shims();
15428
- var import_types59 = require("@neat.is/types");
15981
+ var import_types60 = require("@neat.is/types");
15429
15982
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
15430
15983
  switch (resourceType) {
15431
15984
  case "cloud_function":
@@ -15440,7 +15993,7 @@ function routeEntriesFor(graph, serviceName) {
15440
15993
  const entries = [];
15441
15994
  graph.forEachNode((_id, attrs) => {
15442
15995
  const node = attrs;
15443
- if (node.type !== import_types59.NodeType.RouteNode) return;
15996
+ if (node.type !== import_types60.NodeType.RouteNode) return;
15444
15997
  const route = attrs;
15445
15998
  if (route.service !== serviceName) return;
15446
15999
  entries.push({
@@ -15472,7 +16025,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
15472
16025
  return {
15473
16026
  targetNodeId: match.routeNodeId,
15474
16027
  serviceName,
15475
- edgeType: import_types59.EdgeType.CALLS
16028
+ edgeType: import_types60.EdgeType.CALLS
15476
16029
  };
15477
16030
  };
15478
16031
  }
@@ -15499,7 +16052,7 @@ init_cjs_shims();
15499
16052
 
15500
16053
  // src/connectors/cloudflare/connector.ts
15501
16054
  init_cjs_shims();
15502
- var import_types61 = require("@neat.is/types");
16055
+ var import_types62 = require("@neat.is/types");
15503
16056
 
15504
16057
  // src/connectors/cloudflare/client.ts
15505
16058
  init_cjs_shims();
@@ -15615,7 +16168,7 @@ function mapEventToSignal(event) {
15615
16168
  if (Number.isNaN(observedAt.getTime())) return null;
15616
16169
  const statusCode = metadata?.statusCode;
15617
16170
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
15618
- const path67 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
16171
+ const path68 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15619
16172
  return {
15620
16173
  targetKind: CLOUDFLARE_TARGET_KIND,
15621
16174
  targetName: scriptName,
@@ -15623,7 +16176,7 @@ function mapEventToSignal(event) {
15623
16176
  errorCount: isError ? 1 : 0,
15624
16177
  lastObservedIso: observedAt.toISOString(),
15625
16178
  method,
15626
- ...path67 ? { path: path67 } : {},
16179
+ ...path68 ? { path: path68 } : {},
15627
16180
  ...typeof statusCode === "number" ? { statusCode } : {},
15628
16181
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
15629
16182
  };
@@ -15663,19 +16216,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
15663
16216
  graph.forEachNode((id, attrs) => {
15664
16217
  if (found) return;
15665
16218
  const a = attrs;
15666
- if (a.type === import_types61.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
16219
+ if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15667
16220
  found = id;
15668
16221
  }
15669
16222
  });
15670
16223
  return found;
15671
16224
  }
15672
- function findMatchingRouteNode(graph, serviceName, method, path67) {
15673
- const normalizedPath = normalizePathTemplate(path67);
16225
+ function findMatchingRouteNode(graph, serviceName, method, path68) {
16226
+ const normalizedPath = normalizePathTemplate(path68);
15674
16227
  let found = null;
15675
16228
  graph.forEachNode((id, attrs) => {
15676
16229
  if (found) return;
15677
16230
  const a = attrs;
15678
- if (a.type !== import_types61.NodeType.RouteNode || a.service !== serviceName) return;
16231
+ if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
15679
16232
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
15680
16233
  const routeMethod = (a.method ?? "").toUpperCase();
15681
16234
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -15687,18 +16240,18 @@ function createCloudflareResolveTarget(config, graph) {
15687
16240
  return (signal) => {
15688
16241
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
15689
16242
  const scriptName = signal.targetName;
15690
- const { method, path: path67 } = signal;
16243
+ const { method, path: path68 } = signal;
15691
16244
  const resolveRouteGrain = (serviceName, wholeFileId) => {
15692
- if (!method || !path67) return wholeFileId;
15693
- return findMatchingRouteNode(graph, serviceName, method, path67) ?? wholeFileId;
16245
+ if (!method || !path68) return wholeFileId;
16246
+ return findMatchingRouteNode(graph, serviceName, method, path68) ?? wholeFileId;
15694
16247
  };
15695
16248
  const mapping = config.workers?.[scriptName];
15696
16249
  if (mapping) {
15697
- const wholeFileId = (0, import_types61.fileId)(mapping.service, mapping.entryFile);
16250
+ const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
15698
16251
  return {
15699
16252
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
15700
16253
  serviceName: mapping.service,
15701
- edgeType: import_types61.EdgeType.CALLS
16254
+ edgeType: import_types62.EdgeType.CALLS
15702
16255
  };
15703
16256
  }
15704
16257
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -15707,13 +16260,13 @@ function createCloudflareResolveTarget(config, graph) {
15707
16260
  return {
15708
16261
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
15709
16262
  serviceName: fileNode.service,
15710
- edgeType: import_types61.EdgeType.CALLS
16263
+ edgeType: import_types62.EdgeType.CALLS
15711
16264
  };
15712
16265
  }
15713
16266
  return {
15714
- targetNodeId: (0, import_types61.infraId)("cloudflare-worker", scriptName),
16267
+ targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
15715
16268
  serviceName: scriptName,
15716
- edgeType: import_types61.EdgeType.CALLS,
16269
+ edgeType: import_types62.EdgeType.CALLS,
15717
16270
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
15718
16271
  };
15719
16272
  };
@@ -15909,14 +16462,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
15909
16462
 
15910
16463
  // src/connectors/neon/resolve.ts
15911
16464
  init_cjs_shims();
15912
- var import_types65 = require("@neat.is/types");
16465
+ var import_types66 = require("@neat.is/types");
15913
16466
  function createNeonResolveTarget(config) {
15914
16467
  return (signal) => {
15915
16468
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
15916
16469
  return {
15917
- targetNodeId: (0, import_types65.infraId)("sql-table", signal.targetName),
16470
+ targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
15918
16471
  serviceName: config.serviceName,
15919
- edgeType: import_types65.EdgeType.CALLS,
16472
+ edgeType: import_types66.EdgeType.CALLS,
15920
16473
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
15921
16474
  };
15922
16475
  };
@@ -16042,9 +16595,9 @@ function parseCloudRunTargetName(targetName) {
16042
16595
  const secondSep = rest.indexOf(FIELD_SEP2);
16043
16596
  if (secondSep === -1) return null;
16044
16597
  const method = rest.slice(0, secondSep);
16045
- const path67 = rest.slice(secondSep + 1);
16046
- if (!serviceName || !method || !path67) return null;
16047
- return { serviceName, method, path: path67 };
16598
+ const path68 = rest.slice(secondSep + 1);
16599
+ if (!serviceName || !method || !path68) return null;
16600
+ return { serviceName, method, path: path68 };
16048
16601
  }
16049
16602
 
16050
16603
  // src/connectors/cloud-run/map.ts
@@ -16073,14 +16626,14 @@ function mapLogEntryToSignal2(entry) {
16073
16626
  if (!req) return null;
16074
16627
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
16075
16628
  const method = req.requestMethod.toUpperCase();
16076
- const path67 = pathFromRequestUrl2(req.requestUrl);
16077
- if (path67 === null) return null;
16629
+ const path68 = pathFromRequestUrl2(req.requestUrl);
16630
+ if (path68 === null) return null;
16078
16631
  const timestamp = entry.timestamp;
16079
16632
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
16080
16633
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
16081
16634
  return {
16082
16635
  targetKind: CLOUD_RUN_TARGET_KIND,
16083
- targetName: packCloudRunTargetName({ serviceName, method, path: path67 }),
16636
+ targetName: packCloudRunTargetName({ serviceName, method, path: path68 }),
16084
16637
  callCount: 1,
16085
16638
  errorCount: isError ? 1 : 0,
16086
16639
  lastObservedIso: timestamp
@@ -16097,14 +16650,14 @@ function mapLogEntriesToSignals2(entries) {
16097
16650
 
16098
16651
  // src/connectors/cloud-run/resolve.ts
16099
16652
  init_cjs_shims();
16100
- var import_types69 = require("@neat.is/types");
16653
+ var import_types70 = require("@neat.is/types");
16101
16654
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
16102
16655
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
16103
16656
  let found = null;
16104
16657
  graph.forEachNode((_id, attrs) => {
16105
16658
  if (found) return;
16106
16659
  const node = attrs;
16107
- if (node.type !== import_types69.NodeType.RouteNode) return;
16660
+ if (node.type !== import_types70.NodeType.RouteNode) return;
16108
16661
  const route = attrs;
16109
16662
  if (route.service !== serviceName || !route.pathTemplate) return;
16110
16663
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -16119,23 +16672,23 @@ function createCloudRunResolveTarget(graph, config) {
16119
16672
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
16120
16673
  const identity = parseCloudRunTargetName(signal.targetName);
16121
16674
  if (!identity) return null;
16122
- const { serviceName: gcpServiceName, method, path: path67 } = identity;
16675
+ const { serviceName: gcpServiceName, method, path: path68 } = identity;
16123
16676
  const mappedService = config.serviceMap?.[gcpServiceName];
16124
16677
  if (mappedService) {
16125
16678
  const routeNodeId = findMatchingRouteNode2(
16126
16679
  graph,
16127
16680
  mappedService,
16128
16681
  method,
16129
- normalizePathTemplate(path67)
16682
+ normalizePathTemplate(path68)
16130
16683
  );
16131
16684
  if (routeNodeId) {
16132
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types69.EdgeType.CALLS };
16685
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
16133
16686
  }
16134
16687
  }
16135
16688
  return {
16136
- targetNodeId: (0, import_types69.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16689
+ targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16137
16690
  serviceName: mappedService ?? gcpServiceName,
16138
- edgeType: import_types69.EdgeType.CALLS,
16691
+ edgeType: import_types70.EdgeType.CALLS,
16139
16692
  ensureInfraNode: {
16140
16693
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
16141
16694
  name: gcpServiceName,
@@ -16176,7 +16729,7 @@ function createCloudRunConnector(graph, config = {}) {
16176
16729
 
16177
16730
  // src/connectors/render/index.ts
16178
16731
  init_cjs_shims();
16179
- var import_types72 = require("@neat.is/types");
16732
+ var import_types73 = require("@neat.is/types");
16180
16733
 
16181
16734
  // src/connectors/render/types.ts
16182
16735
  init_cjs_shims();
@@ -16254,7 +16807,7 @@ function buildRenderRouteIndex(graph, serviceName) {
16254
16807
  const out = [];
16255
16808
  graph.forEachNode((_id, attrs) => {
16256
16809
  const node = attrs;
16257
- if (node.type !== import_types72.NodeType.RouteNode) return;
16810
+ if (node.type !== import_types73.NodeType.RouteNode) return;
16258
16811
  const route = attrs;
16259
16812
  if (route.service !== serviceName) return;
16260
16813
  out.push({
@@ -16339,7 +16892,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
16339
16892
  function createRenderResolveTarget(config) {
16340
16893
  return (signal) => {
16341
16894
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
16342
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types72.EdgeType.CALLS };
16895
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
16343
16896
  }
16344
16897
  return null;
16345
16898
  };
@@ -16477,21 +17030,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
16477
17030
 
16478
17031
  // src/connectors/planetscale/resolve.ts
16479
17032
  init_cjs_shims();
16480
- var import_types76 = require("@neat.is/types");
17033
+ var import_types77 = require("@neat.is/types");
16481
17034
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
16482
17035
  function createPlanetscaleResolveTarget(graph, config) {
16483
17036
  const databaseName = `${config.organization}/${config.database}`;
16484
17037
  return (signal, _ctx) => {
16485
17038
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16486
- const tableId = (0, import_types76.infraId)("sql-table", signal.targetName);
17039
+ const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
16487
17040
  if (graph.hasNode(tableId)) {
16488
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types76.EdgeType.CALLS };
17041
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
16489
17042
  }
16490
- const providerId = (0, import_types76.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
17043
+ const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16491
17044
  return {
16492
17045
  targetNodeId: providerId,
16493
17046
  serviceName: config.serviceName,
16494
- edgeType: import_types76.EdgeType.CALLS,
17047
+ edgeType: import_types77.EdgeType.CALLS,
16495
17048
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
16496
17049
  };
16497
17050
  };
@@ -17161,11 +17714,11 @@ function registerRoutes(scope, ctx) {
17161
17714
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
17162
17715
  const parsed = [];
17163
17716
  for (const c of candidates) {
17164
- const r = import_types79.DivergenceTypeSchema.safeParse(c);
17717
+ const r = import_types80.DivergenceTypeSchema.safeParse(c);
17165
17718
  if (!r.success) {
17166
17719
  return reply.code(400).send({
17167
17720
  error: `unknown divergence type "${c}"`,
17168
- allowed: import_types79.DivergenceTypeSchema.options
17721
+ allowed: import_types80.DivergenceTypeSchema.options
17169
17722
  });
17170
17723
  }
17171
17724
  parsed.push(r.data);
@@ -17474,7 +18027,7 @@ function registerRoutes(scope, ctx) {
17474
18027
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
17475
18028
  let violations = await log.readAll();
17476
18029
  if (req.query.severity) {
17477
- const sev = import_types79.PolicySeveritySchema.safeParse(req.query.severity);
18030
+ const sev = import_types80.PolicySeveritySchema.safeParse(req.query.severity);
17478
18031
  if (!sev.success) {
17479
18032
  return reply.code(400).send({
17480
18033
  error: "invalid severity",
@@ -17513,7 +18066,7 @@ function registerRoutes(scope, ctx) {
17513
18066
  scope.post("/policies/check", async (req, reply) => {
17514
18067
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
17515
18068
  if (!proj) return;
17516
- const parsed = import_types79.PoliciesCheckBodySchema.safeParse(req.body ?? {});
18069
+ const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17517
18070
  if (!parsed.success) {
17518
18071
  return reply.code(400).send({
17519
18072
  error: "invalid /policies/check body",
@@ -17835,7 +18388,7 @@ init_otel_grpc();
17835
18388
  // src/daemon.ts
17836
18389
  init_cjs_shims();
17837
18390
  var import_node_fs33 = require("fs");
17838
- var import_node_path66 = __toESM(require("path"), 1);
18391
+ var import_node_path67 = __toESM(require("path"), 1);
17839
18392
  var import_node_module = require("module");
17840
18393
  init_otel();
17841
18394
  init_auth();
@@ -17843,7 +18396,7 @@ init_auth();
17843
18396
  // src/unrouted.ts
17844
18397
  init_cjs_shims();
17845
18398
  var import_node_fs32 = require("fs");
17846
- var import_node_path65 = __toESM(require("path"), 1);
18399
+ var import_node_path66 = __toESM(require("path"), 1);
17847
18400
  function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new Date()) {
17848
18401
  return {
17849
18402
  timestamp: now.toISOString(),
@@ -17853,34 +18406,34 @@ function buildUnroutedSpanRecord(serviceName, traceId, now = /* @__PURE__ */ new
17853
18406
  };
17854
18407
  }
17855
18408
  async function appendUnroutedSpan(neatHome3, record) {
17856
- const target = import_node_path65.default.join(neatHome3, "errors.ndjson");
18409
+ const target = import_node_path66.default.join(neatHome3, "errors.ndjson");
17857
18410
  await import_node_fs32.promises.mkdir(neatHome3, { recursive: true });
17858
18411
  await import_node_fs32.promises.appendFile(target, JSON.stringify(record) + "\n", "utf8");
17859
18412
  }
17860
18413
  function unroutedErrorsPath(neatHome3) {
17861
- return import_node_path65.default.join(neatHome3, "errors.ndjson");
18414
+ return import_node_path66.default.join(neatHome3, "errors.ndjson");
17862
18415
  }
17863
18416
 
17864
18417
  // src/daemon.ts
17865
- var import_types80 = require("@neat.is/types");
18418
+ var import_types81 = require("@neat.is/types");
17866
18419
  function daemonJsonPath(scanPath) {
17867
- return import_node_path66.default.join(scanPath, "neat-out", "daemon.json");
18420
+ return import_node_path67.default.join(scanPath, "neat-out", "daemon.json");
17868
18421
  }
17869
18422
  function daemonsDiscoveryDir(home) {
17870
18423
  const base = home && home.length > 0 ? home : neatHomeFromEnv();
17871
- return import_node_path66.default.join(base, "daemons");
18424
+ return import_node_path67.default.join(base, "daemons");
17872
18425
  }
17873
18426
  function daemonDiscoveryPath(project, home) {
17874
- return import_node_path66.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
18427
+ return import_node_path67.default.join(daemonsDiscoveryDir(home), `${sanitizeDiscoveryName(project)}.json`);
17875
18428
  }
17876
18429
  function sanitizeDiscoveryName(project) {
17877
18430
  return project.replace(/[^A-Za-z0-9._-]/g, "_");
17878
18431
  }
17879
18432
  function neatHomeFromEnv() {
17880
18433
  const env = process.env.NEAT_HOME;
17881
- if (env && env.length > 0) return import_node_path66.default.resolve(env);
18434
+ if (env && env.length > 0) return import_node_path67.default.resolve(env);
17882
18435
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17883
- return import_node_path66.default.join(home, ".neat");
18436
+ return import_node_path67.default.join(home, ".neat");
17884
18437
  }
17885
18438
  function resolveNeatVersion() {
17886
18439
  if (process.env.NEAT_LOCAL_VERSION && process.env.NEAT_LOCAL_VERSION.length > 0) {
@@ -17935,11 +18488,11 @@ function teardownSlot(slot) {
17935
18488
  }
17936
18489
  }
17937
18490
  function neatHomeFor(opts) {
17938
- if (opts.neatHome && opts.neatHome.length > 0) return import_node_path66.default.resolve(opts.neatHome);
18491
+ if (opts.neatHome && opts.neatHome.length > 0) return import_node_path67.default.resolve(opts.neatHome);
17939
18492
  const env = process.env.NEAT_HOME;
17940
- if (env && env.length > 0) return import_node_path66.default.resolve(env);
18493
+ if (env && env.length > 0) return import_node_path67.default.resolve(env);
17941
18494
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
17942
- return import_node_path66.default.join(home, ".neat");
18495
+ return import_node_path67.default.join(home, ".neat");
17943
18496
  }
17944
18497
  function routeSpanToProject(serviceName, projects) {
17945
18498
  if (!serviceName) return DEFAULT_PROJECT;
@@ -17987,11 +18540,11 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
17987
18540
  if (!serviceName) return true;
17988
18541
  if (serviceNameMatchesProject(serviceName, project)) return true;
17989
18542
  return graph.someNode(
17990
- (_id, attrs) => attrs.type === import_types80.NodeType.ServiceNode && attrs.name === serviceName
18543
+ (_id, attrs) => attrs.type === import_types81.NodeType.ServiceNode && attrs.name === serviceName
17991
18544
  );
17992
18545
  }
17993
18546
  async function bootstrapProject(entry, connectors = [], neatHome3) {
17994
- const paths = pathsForProject(entry.name, import_node_path66.default.join(entry.path, "neat-out"));
18547
+ const paths = pathsForProject(entry.name, import_node_path67.default.join(entry.path, "neat-out"));
17995
18548
  try {
17996
18549
  const stat = await import_node_fs33.promises.stat(entry.path);
17997
18550
  if (!stat.isDirectory()) {
@@ -18107,7 +18660,7 @@ async function startDaemon(opts = {}) {
18107
18660
  const projectArg = typeof opts.project === "string" && opts.project.length > 0 ? opts.project : process.env.NEAT_PROJECT && process.env.NEAT_PROJECT.length > 0 ? process.env.NEAT_PROJECT : null;
18108
18661
  const projectPathArg = opts.projectPath && opts.projectPath.length > 0 ? opts.projectPath : process.env.NEAT_PROJECT_PATH && process.env.NEAT_PROJECT_PATH.length > 0 ? process.env.NEAT_PROJECT_PATH : null;
18109
18662
  const singleProject = projectArg;
18110
- const singleProjectPath = singleProject && projectPathArg ? import_node_path66.default.resolve(projectPathArg) : null;
18663
+ const singleProjectPath = singleProject && projectPathArg ? import_node_path67.default.resolve(projectPathArg) : null;
18111
18664
  if (singleProject && !singleProjectPath) {
18112
18665
  throw new Error(
18113
18666
  `neatd: project "${singleProject}" given without a projectPath; pass NEAT_PROJECT_PATH alongside NEAT_PROJECT.`
@@ -18122,7 +18675,7 @@ async function startDaemon(opts = {}) {
18122
18675
  );
18123
18676
  }
18124
18677
  }
18125
- const pidPath = import_node_path66.default.join(home, "neatd.pid");
18678
+ const pidPath = import_node_path67.default.join(home, "neatd.pid");
18126
18679
  await writeAtomically(pidPath, `${process.pid}
18127
18680
  `);
18128
18681
  const slots = /* @__PURE__ */ new Map();
@@ -18534,8 +19087,8 @@ async function startDaemon(opts = {}) {
18534
19087
  let registryWatcher = null;
18535
19088
  let reloadTimer = null;
18536
19089
  if (!singleProject) try {
18537
- const regDir = import_node_path66.default.dirname(regPath);
18538
- const regBase = import_node_path66.default.basename(regPath);
19090
+ const regDir = import_node_path67.default.dirname(regPath);
19091
+ const regBase = import_node_path67.default.basename(regPath);
18539
19092
  registryWatcher = (0, import_node_fs33.watch)(regDir, (_eventType, filename) => {
18540
19093
  if (filename !== null && filename !== regBase) return;
18541
19094
  if (reloadTimer) clearTimeout(reloadTimer);