@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/server.cjs CHANGED
@@ -59,8 +59,8 @@ function mountBearerAuth(app, opts) {
59
59
  ]);
60
60
  const publicRead = opts.publicRead === true;
61
61
  app.addHook("preHandler", (req, reply, done) => {
62
- const path67 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
- if (exactUnauthPaths.has(path67) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path67)) {
62
+ const path68 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
+ if (exactUnauthPaths.has(path68) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path68)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -341,7 +341,7 @@ function pickEnv(spanAttrs, resourceAttrs) {
341
341
  return ENV_FALLBACK;
342
342
  }
343
343
  function normalizeDbSystem(attrs) {
344
- const raw = attrs["db.system"];
344
+ const raw = attrs["db.system"] ?? attrs["db.system.name"];
345
345
  if (typeof raw !== "string") return void 0;
346
346
  return raw === "mongoose" ? "mongodb" : raw;
347
347
  }
@@ -413,8 +413,8 @@ function websocketChannelPathOf(attrs) {
413
413
  const v = attrs[key];
414
414
  if (typeof v === "string" && v.length > 0) {
415
415
  const q = v.indexOf("?");
416
- const path67 = q === -1 ? v : v.slice(0, q);
417
- if (path67.length > 0) return path67;
416
+ const path68 = q === -1 ? v : v.slice(0, q);
417
+ if (path68.length > 0) return path68;
418
418
  }
419
419
  }
420
420
  return void 0;
@@ -433,6 +433,9 @@ function parseOtlpRequest(body) {
433
433
  for (const ss of rs.scopeSpans ?? []) {
434
434
  for (const span of ss.spans ?? []) {
435
435
  const attrs = attrsToRecord(span.attributes);
436
+ const dbSqlText = typeof attrs["db.statement"] === "string" ? attrs["db.statement"] : typeof attrs["db.query.text"] === "string" ? attrs["db.query.text"] : void 0;
437
+ const dbSystemName = normalizeDbSystem(attrs);
438
+ 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;
436
439
  const parsed = {
437
440
  service,
438
441
  resourceServiceNamePresent,
@@ -447,11 +450,11 @@ function parseOtlpRequest(body) {
447
450
  durationNanos: durationNanos(span.startTimeUnixNano, span.endTimeUnixNano),
448
451
  env: pickEnv(attrs, resourceAttrs),
449
452
  attributes: attrs,
450
- dbSystem: normalizeDbSystem(attrs),
453
+ dbSystem: dbSystemName,
451
454
  dbName: typeof attrs["db.name"] === "string" ? attrs["db.name"] : void 0,
452
455
  dbCollection: typeof attrs["db.collection.name"] === "string" ? attrs["db.collection.name"] : typeof attrs["db.mongodb.collection"] === "string" ? attrs["db.mongodb.collection"] : void 0,
453
- dbTable: typeof attrs["db.statement"] === "string" ? tableFromSqlStatement(attrs["db.statement"]) ?? void 0 : void 0,
454
- dbColumns: typeof attrs["db.statement"] === "string" ? columnsFromSqlStatement(attrs["db.statement"]) : void 0,
456
+ dbTable: directDbTable ?? (dbSqlText ? tableFromSqlStatement(dbSqlText) ?? void 0 : void 0),
457
+ dbColumns: dbSqlText ? columnsFromSqlStatement(dbSqlText) : void 0,
455
458
  httpRoute: typeof attrs["http.route"] === "string" ? attrs["http.route"] : void 0,
456
459
  httpMethod: typeof attrs["http.request.method"] === "string" ? attrs["http.request.method"] : typeof attrs["http.method"] === "string" ? attrs["http.method"] : void 0,
457
460
  messagingSystem: typeof attrs["messaging.system"] === "string" ? attrs["messaging.system"] : void 0,
@@ -712,7 +715,7 @@ var init_otel = __esm({
712
715
 
713
716
  // src/server.ts
714
717
  init_cjs_shims();
715
- var import_node_path66 = __toESM(require("path"), 1);
718
+ var import_node_path67 = __toESM(require("path"), 1);
716
719
 
717
720
  // src/graph.ts
718
721
  init_cjs_shims();
@@ -736,7 +739,7 @@ function getGraph(project = DEFAULT_PROJECT) {
736
739
  init_cjs_shims();
737
740
  var import_fastify2 = __toESM(require("fastify"), 1);
738
741
  var import_cors = __toESM(require("@fastify/cors"), 1);
739
- var import_types79 = require("@neat.is/types");
742
+ var import_types80 = require("@neat.is/types");
740
743
 
741
744
  // src/extend/index.ts
742
745
  init_cjs_shims();
@@ -848,12 +851,12 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
848
851
  ]);
849
852
  async function findHookFiles(scanPath) {
850
853
  const found = [];
851
- const walk8 = async (dir) => {
854
+ const walk9 = async (dir) => {
852
855
  const entries = await import_node_fs2.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
853
856
  for (const entry of entries) {
854
857
  if (entry.isDirectory()) {
855
858
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
856
- await walk8(import_node_path2.default.join(dir, entry.name));
859
+ await walk9(import_node_path2.default.join(dir, entry.name));
857
860
  } else if (entry.isFile()) {
858
861
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
859
862
  const rel = import_node_path2.default.relative(scanPath, import_node_path2.default.join(dir, entry.name));
@@ -862,7 +865,7 @@ async function findHookFiles(scanPath) {
862
865
  }
863
866
  }
864
867
  };
865
- await walk8(scanPath);
868
+ await walk9(scanPath);
866
869
  return found.sort();
867
870
  }
868
871
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
@@ -2265,14 +2268,14 @@ function buildServiceHostIndex(services) {
2265
2268
  }
2266
2269
  async function walkSourceFiles(dir) {
2267
2270
  const out = [];
2268
- async function walk8(current) {
2271
+ async function walk9(current) {
2269
2272
  const entries = await import_node_fs7.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2270
2273
  for (const entry of entries) {
2271
2274
  const full = import_node_path7.default.join(current, entry.name);
2272
2275
  if (entry.isDirectory()) {
2273
2276
  if (IGNORED_DIRS.has(entry.name)) continue;
2274
2277
  if (await isPythonVenvDir(full)) continue;
2275
- await walk8(full);
2278
+ await walk9(full);
2276
2279
  } else if (entry.isFile() && SERVICE_FILE_EXTENSIONS.has(import_node_path7.default.extname(entry.name)) && // Skip NEAT's own generated `otel-init.*` bootstrap — extracting it
2277
2280
  // would attribute our instrumentation imports to the user's service.
2278
2281
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2280,7 +2283,7 @@ async function walkSourceFiles(dir) {
2280
2283
  }
2281
2284
  }
2282
2285
  }
2283
- await walk8(dir);
2286
+ await walk9(dir);
2284
2287
  return out;
2285
2288
  }
2286
2289
  async function loadSourceFiles(dir) {
@@ -2794,7 +2797,7 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
2794
2797
  ]);
2795
2798
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2796
2799
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
2797
- function ginRoutesFromSource(source, parser) {
2800
+ function goRouterRoutesFromSource(source, parser, framework) {
2798
2801
  const tree = parseSource2(parser, source);
2799
2802
  const prefixes = /* @__PURE__ */ new Map();
2800
2803
  const out = [];
@@ -2804,10 +2807,12 @@ function ginRoutesFromSource(source, parser) {
2804
2807
  const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
2805
2808
  if (name && value?.type === "call_expression") {
2806
2809
  const fn2 = value.childForFieldName("function");
2807
- const field = fn2?.childForFieldName("field")?.text;
2808
- const first2 = value.childForFieldName("arguments")?.namedChild(0);
2809
- if (field === "Group" && first2?.type === "interpreted_string_literal") {
2810
- prefixes.set(name, first2.text.slice(1, -1));
2810
+ if (fn2?.childForFieldName("field")?.text === "Group") {
2811
+ const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
2812
+ if (leaf2 !== null) {
2813
+ const parent = fn2.childForFieldName("operand")?.text ?? "";
2814
+ prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
2815
+ }
2811
2816
  }
2812
2817
  }
2813
2818
  return;
@@ -2818,18 +2823,32 @@ function ginRoutesFromSource(source, parser) {
2818
2823
  const method = fn.childForFieldName("field")?.text?.toUpperCase();
2819
2824
  if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
2820
2825
  const receiver = fn.childForFieldName("operand")?.text ?? "";
2821
- const first = node.childForFieldName("arguments")?.namedChild(0);
2822
- if (first?.type !== "interpreted_string_literal") return;
2823
- const leaf = first.text.slice(1, -1);
2826
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
2827
+ if (leaf === null) return;
2824
2828
  out.push({
2825
- method: method === "ALL" ? "ALL" : method,
2829
+ method,
2826
2830
  pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
2827
2831
  line: node.startPosition.row + 1,
2828
- framework: "gin"
2832
+ framework
2829
2833
  });
2830
2834
  });
2831
2835
  return out;
2832
2836
  }
2837
+ function goStringLiteral(node) {
2838
+ if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
2839
+ return node.text.slice(1, -1);
2840
+ }
2841
+ return null;
2842
+ }
2843
+ function ginRoutesFromSource(source, parser) {
2844
+ return goRouterRoutesFromSource(source, parser, "gin");
2845
+ }
2846
+ function echoRoutesFromSource(source, parser) {
2847
+ return goRouterRoutesFromSource(source, parser, "echo");
2848
+ }
2849
+ function fiberRoutesFromSource(source, parser) {
2850
+ return goRouterRoutesFromSource(source, parser, "fiber");
2851
+ }
2833
2852
  var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
2834
2853
  var NESTJS_METHODS = /* @__PURE__ */ new Map([
2835
2854
  ["Get", "GET"],
@@ -3414,9 +3433,9 @@ function rubyRocketRoute(args) {
3414
3433
  if (!pair || pair.type !== "pair") continue;
3415
3434
  const k = pair.childForFieldName("key");
3416
3435
  if (k?.type !== "string") continue;
3417
- const path67 = rubyLiteral(k);
3418
- if (path67 === null) continue;
3419
- return { path: path67, target: rubyLiteral(pair.childForFieldName("value")) };
3436
+ const path68 = rubyLiteral(k);
3437
+ if (path68 === null) continue;
3438
+ return { path: path68, target: rubyLiteral(pair.childForFieldName("value")) };
3420
3439
  }
3421
3440
  return null;
3422
3441
  }
@@ -4093,9 +4112,11 @@ async function addRoutes(graph, services) {
4093
4112
  const hasFlask = deps["flask"] !== void 0;
4094
4113
  const hasDjango = deps["django"] !== void 0;
4095
4114
  const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
4115
+ const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
4116
+ const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
4096
4117
  const hasRails = deps["rails"] !== void 0;
4097
4118
  const hasLaravel = deps["laravel/framework"] !== void 0;
4098
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
4119
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasRails && !hasLaravel)
4099
4120
  continue;
4100
4121
  const files = await loadSourceFiles(service.dir);
4101
4122
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4119,7 +4140,10 @@ async function addRoutes(graph, services) {
4119
4140
  } else if (isRb) {
4120
4141
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4121
4142
  } else if (isGo) {
4122
- routes = hasGin ? ginRoutesFromSource(file.content, goParser) : [];
4143
+ if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4144
+ else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
4145
+ else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
4146
+ else routes = [];
4123
4147
  } else if (isPy) {
4124
4148
  routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
4125
4149
  if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
@@ -5669,19 +5693,19 @@ function confidenceFromMix(edges, now = Date.now()) {
5669
5693
  function longestIncomingWalk(graph, start, maxDepth) {
5670
5694
  let best = { path: [start], edges: [] };
5671
5695
  const visited = /* @__PURE__ */ new Set([start]);
5672
- function step(node, path67, edges) {
5673
- if (path67.length > best.path.length) {
5674
- best = { path: [...path67], edges: [...edges] };
5696
+ function step(node, path68, edges) {
5697
+ if (path68.length > best.path.length) {
5698
+ best = { path: [...path68], edges: [...edges] };
5675
5699
  }
5676
- if (path67.length - 1 >= maxDepth) return;
5700
+ if (path68.length - 1 >= maxDepth) return;
5677
5701
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
5678
5702
  for (const [srcId, edge] of incoming) {
5679
5703
  if (visited.has(srcId)) continue;
5680
5704
  visited.add(srcId);
5681
- path67.push(srcId);
5705
+ path68.push(srcId);
5682
5706
  edges.push(edge);
5683
- step(srcId, path67, edges);
5684
- path67.pop();
5707
+ step(srcId, path68, edges);
5708
+ path68.pop();
5685
5709
  edges.pop();
5686
5710
  visited.delete(srcId);
5687
5711
  }
@@ -5689,11 +5713,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
5689
5713
  step(start, [start], []);
5690
5714
  return best;
5691
5715
  }
5692
- function databaseRootCauseShape(graph, origin, walk8) {
5716
+ function databaseRootCauseShape(graph, origin, walk9) {
5693
5717
  const targetDb = origin;
5694
5718
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
5695
5719
  if (candidatePairs.length === 0) return null;
5696
- for (const id of walk8.path) {
5720
+ for (const id of walk9.path) {
5697
5721
  const owner = resolveOwningService(graph, id);
5698
5722
  if (!owner) continue;
5699
5723
  const { id: serviceId9, svc } = owner;
@@ -5720,8 +5744,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
5720
5744
  }
5721
5745
  return null;
5722
5746
  }
5723
- function serviceRootCauseShape(graph, _origin, walk8) {
5724
- for (const id of walk8.path) {
5747
+ function serviceRootCauseShape(graph, _origin, walk9) {
5748
+ for (const id of walk9.path) {
5725
5749
  const owner = resolveOwningService(graph, id);
5726
5750
  if (!owner) continue;
5727
5751
  const { id: serviceId9, svc } = owner;
@@ -5757,15 +5781,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
5757
5781
  }
5758
5782
  return null;
5759
5783
  }
5760
- function fileRootCauseShape(graph, origin, walk8) {
5784
+ function fileRootCauseShape(graph, origin, walk9) {
5761
5785
  const owner = resolveOwningService(graph, origin.id);
5762
5786
  if (!owner) return null;
5763
- return serviceRootCauseShape(graph, owner.svc, walk8);
5787
+ return serviceRootCauseShape(graph, owner.svc, walk9);
5764
5788
  }
5765
- function symbolRootCauseShape(graph, origin, walk8) {
5789
+ function symbolRootCauseShape(graph, origin, walk9) {
5766
5790
  const owner = resolveOwningService(graph, origin.id);
5767
5791
  if (!owner) return null;
5768
- return serviceRootCauseShape(graph, owner.svc, walk8);
5792
+ return serviceRootCauseShape(graph, owner.svc, walk9);
5769
5793
  }
5770
5794
  var rootCauseShapes = {
5771
5795
  [import_types8.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -5778,16 +5802,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
5778
5802
  const origin = graph.getNodeAttributes(errorNodeId);
5779
5803
  const shape = rootCauseShapes[origin.type];
5780
5804
  if (shape) {
5781
- const walk8 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
5782
- const match = shape(graph, origin, walk8);
5805
+ const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
5806
+ const match = shape(graph, origin, walk9);
5783
5807
  if (match) {
5784
5808
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
5785
5809
  return import_types8.RootCauseResultSchema.parse({
5786
5810
  rootCauseNode: match.rootCauseNode,
5787
5811
  rootCauseReason: reason,
5788
- traversalPath: walk8.path,
5789
- edgeProvenances: walk8.edges.map((e) => e.provenance),
5790
- confidence: confidenceFromMix(walk8.edges),
5812
+ traversalPath: walk9.path,
5813
+ edgeProvenances: walk9.edges.map((e) => e.provenance),
5814
+ confidence: confidenceFromMix(walk9.edges),
5791
5815
  fixRecommendation: match.fixRecommendation
5792
5816
  });
5793
5817
  }
@@ -5888,26 +5912,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
5888
5912
  return best;
5889
5913
  }
5890
5914
  function followFailingCallChain(graph, originServiceId, maxDepth) {
5891
- const path67 = [originServiceId];
5915
+ const path68 = [originServiceId];
5892
5916
  const edges = [];
5893
5917
  const visited = /* @__PURE__ */ new Set([originServiceId]);
5894
5918
  let current = originServiceId;
5895
5919
  for (let depth = 0; depth < maxDepth; depth++) {
5896
5920
  const hop = dominantFailingCall(graph, current, visited);
5897
5921
  if (!hop) break;
5898
- path67.push(hop.nextService);
5922
+ path68.push(hop.nextService);
5899
5923
  edges.push(hop.edge);
5900
5924
  visited.add(hop.nextService);
5901
5925
  current = hop.nextService;
5902
5926
  }
5903
5927
  if (edges.length === 0) return null;
5904
- return { path: path67, edges, culprit: current };
5928
+ return { path: path68, edges, culprit: current };
5905
5929
  }
5906
5930
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
5907
5931
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
5908
5932
  if (!chain) return null;
5909
5933
  const culprit = chain.culprit;
5910
- const path67 = [...chain.path];
5934
+ const path68 = [...chain.path];
5911
5935
  const edgeProvenances = chain.edges.map((e) => e.provenance);
5912
5936
  const baseConfidence = confidenceFromMix(chain.edges);
5913
5937
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -5915,14 +5939,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
5915
5939
  if (loc) {
5916
5940
  let rootCauseNode = culprit;
5917
5941
  if (loc.fileNode) {
5918
- path67.push(loc.fileNode);
5942
+ path68.push(loc.fileNode);
5919
5943
  edgeProvenances.push(import_types8.Provenance.OBSERVED);
5920
5944
  rootCauseNode = loc.fileNode;
5921
5945
  }
5922
5946
  return import_types8.RootCauseResultSchema.parse({
5923
5947
  rootCauseNode,
5924
5948
  rootCauseReason: loc.rootCauseReason,
5925
- traversalPath: path67,
5949
+ traversalPath: path68,
5926
5950
  edgeProvenances,
5927
5951
  confidence,
5928
5952
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -5934,7 +5958,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
5934
5958
  return import_types8.RootCauseResultSchema.parse({
5935
5959
  rootCauseNode: culprit,
5936
5960
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
5937
- traversalPath: path67,
5961
+ traversalPath: path68,
5938
5962
  edgeProvenances,
5939
5963
  confidence,
5940
5964
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -6557,6 +6581,12 @@ function parseGoMod(source) {
6557
6581
  }
6558
6582
  return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
6559
6583
  }
6584
+ function goFramework(deps) {
6585
+ if (deps["github.com/gin-gonic/gin"]) return "gin";
6586
+ if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
6587
+ if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
6588
+ return void 0;
6589
+ }
6560
6590
  async function discoverGoService(scanPath, dir) {
6561
6591
  let raw;
6562
6592
  try {
@@ -6568,6 +6598,7 @@ async function discoverGoService(scanPath, dir) {
6568
6598
  if (!mod) return null;
6569
6599
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
6570
6600
  const pkg = { name, dependencies: mod.dependencies };
6601
+ const framework = goFramework(mod.dependencies);
6571
6602
  const node = {
6572
6603
  id: (0, import_types10.serviceId)(name),
6573
6604
  type: import_types10.NodeType.ServiceNode,
@@ -6575,7 +6606,7 @@ async function discoverGoService(scanPath, dir) {
6575
6606
  language: "go",
6576
6607
  dependencies: mod.dependencies,
6577
6608
  repoPath: import_node_path12.default.relative(scanPath, dir),
6578
- ...mod.dependencies["github.com/gin-gonic/gin"] ? { framework: "gin" } : {}
6609
+ ...framework ? { framework } : {}
6579
6610
  };
6580
6611
  return { pkg, dir, node };
6581
6612
  }
@@ -7475,7 +7506,7 @@ async function addSymbolEdges(graph, services) {
7475
7506
  return best;
7476
7507
  };
7477
7508
  const requests = [];
7478
- const walk8 = (node) => {
7509
+ const walk9 = (node) => {
7479
7510
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
7480
7511
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
7481
7512
  if (self && self.kind === "class") {
@@ -7521,10 +7552,10 @@ async function addSymbolEdges(graph, services) {
7521
7552
  }
7522
7553
  for (let i = 0; i < node.namedChildCount; i++) {
7523
7554
  const child = node.namedChild(i);
7524
- if (child) walk8(child);
7555
+ if (child) walk9(child);
7525
7556
  }
7526
7557
  };
7527
- walk8(root);
7558
+ walk9(root);
7528
7559
  for (const req of requests) {
7529
7560
  const targetSid = resolveTarget(req.targetName, req.wantKind);
7530
7561
  if (!targetSid) continue;
@@ -8537,20 +8568,20 @@ var import_node_path30 = __toESM(require("path"), 1);
8537
8568
  var import_types19 = require("@neat.is/types");
8538
8569
  async function walkConfigFiles(dir) {
8539
8570
  const out = [];
8540
- async function walk8(current) {
8571
+ async function walk9(current) {
8541
8572
  const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true });
8542
8573
  for (const entry of entries) {
8543
8574
  const full = import_node_path30.default.join(current, entry.name);
8544
8575
  if (entry.isDirectory()) {
8545
8576
  if (IGNORED_DIRS.has(entry.name)) continue;
8546
8577
  if (await isPythonVenvDir(full)) continue;
8547
- await walk8(full);
8578
+ await walk9(full);
8548
8579
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
8549
8580
  out.push(full);
8550
8581
  }
8551
8582
  }
8552
8583
  }
8553
- await walk8(dir);
8584
+ await walk9(dir);
8554
8585
  return out;
8555
8586
  }
8556
8587
  async function addConfigNodes(graph, services, scanPath) {
@@ -8640,20 +8671,20 @@ function grpcMethodsFromProto(content, fqPackage) {
8640
8671
  }
8641
8672
  async function walkProtoFiles(dir) {
8642
8673
  const out = [];
8643
- async function walk8(current) {
8674
+ async function walk9(current) {
8644
8675
  const entries = await import_node_fs19.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8645
8676
  for (const entry of entries) {
8646
8677
  const full = import_node_path31.default.join(current, entry.name);
8647
8678
  if (entry.isDirectory()) {
8648
8679
  if (IGNORED_DIRS.has(entry.name)) continue;
8649
8680
  if (await isPythonVenvDir(full)) continue;
8650
- await walk8(full);
8681
+ await walk9(full);
8651
8682
  } else if (entry.isFile() && import_node_path31.default.extname(entry.name) === PROTO_EXTENSION) {
8652
8683
  out.push(full);
8653
8684
  }
8654
8685
  }
8655
8686
  }
8656
- await walk8(dir);
8687
+ await walk9(dir);
8657
8688
  return out;
8658
8689
  }
8659
8690
  async function addGrpcMethods(graph, services) {
@@ -8721,7 +8752,7 @@ async function addGrpcMethods(graph, services) {
8721
8752
 
8722
8753
  // src/extract/calls/index.ts
8723
8754
  init_cjs_shims();
8724
- var import_types37 = require("@neat.is/types");
8755
+ var import_types38 = require("@neat.is/types");
8725
8756
 
8726
8757
  // src/extract/calls/http.ts
8727
8758
  init_cjs_shims();
@@ -9475,7 +9506,7 @@ function isFirestoreClientFactory(node) {
9475
9506
  }
9476
9507
  function firestoreClientVars(root) {
9477
9508
  const vars = /* @__PURE__ */ new Set();
9478
- const walk8 = (node) => {
9509
+ const walk9 = (node) => {
9479
9510
  if (node.type === "variable_declarator") {
9480
9511
  const name = node.childForFieldName("name");
9481
9512
  let value = node.childForFieldName("value");
@@ -9484,9 +9515,9 @@ function firestoreClientVars(root) {
9484
9515
  vars.add(name.text);
9485
9516
  }
9486
9517
  }
9487
- for (const c of namedChildren(node)) walk8(c);
9518
+ for (const c of namedChildren(node)) walk9(c);
9488
9519
  };
9489
- walk8(root);
9520
+ walk9(root);
9490
9521
  return vars;
9491
9522
  }
9492
9523
  function isClientExpr(node, clientVars) {
@@ -9641,7 +9672,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9641
9672
  }
9642
9673
  s.add(field);
9643
9674
  };
9644
- const walk8 = (node) => {
9675
+ const walk9 = (node) => {
9645
9676
  if (node.type === "call_expression") {
9646
9677
  const fn = node.childForFieldName("function");
9647
9678
  const line = node.startPosition.row + 1;
@@ -9681,9 +9712,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9681
9712
  }
9682
9713
  }
9683
9714
  }
9684
- for (const c of namedChildren(node)) walk8(c);
9715
+ for (const c of namedChildren(node)) walk9(c);
9685
9716
  };
9686
- walk8(tree.rootNode);
9717
+ walk9(tree.rootNode);
9687
9718
  const out = [];
9688
9719
  for (const [collPath, line] of collLine) {
9689
9720
  const byField = writes.get(collPath);
@@ -10478,7 +10509,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
10478
10509
  const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
10479
10510
  const out = [];
10480
10511
  const seen = /* @__PURE__ */ new Set();
10481
- const walk8 = (node) => {
10512
+ const walk9 = (node) => {
10482
10513
  if (node.type === "call_expression") {
10483
10514
  const fn = node.childForFieldName("function");
10484
10515
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -10506,9 +10537,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
10506
10537
  }
10507
10538
  }
10508
10539
  }
10509
- for (const c of namedChildren4(node)) walk8(c);
10540
+ for (const c of namedChildren4(node)) walk9(c);
10510
10541
  };
10511
- walk8(tree.rootNode);
10542
+ walk9(tree.rootNode);
10512
10543
  return out;
10513
10544
  }
10514
10545
  function enclosingVarName(call) {
@@ -10530,7 +10561,7 @@ function enclosingVarName(call) {
10530
10561
  function collectDrizzleTables(root) {
10531
10562
  const tables = [];
10532
10563
  const varToTable = /* @__PURE__ */ new Map();
10533
- const walk8 = (node) => {
10564
+ const walk9 = (node) => {
10534
10565
  if (node.type === "call_expression") {
10535
10566
  const fn = node.childForFieldName("function");
10536
10567
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -10545,9 +10576,9 @@ function collectDrizzleTables(root) {
10545
10576
  }
10546
10577
  }
10547
10578
  }
10548
- for (const c of namedChildren4(node)) walk8(c);
10579
+ for (const c of namedChildren4(node)) walk9(c);
10549
10580
  };
10550
- walk8(root);
10581
+ walk9(root);
10551
10582
  return { tables, varToTable };
10552
10583
  }
10553
10584
  function referencesTargetVar(call) {
@@ -10570,7 +10601,7 @@ function drizzleForeignKeys(file, serviceDir) {
10570
10601
  const seen = /* @__PURE__ */ new Set();
10571
10602
  for (const table of tables) {
10572
10603
  if (!table.object) continue;
10573
- const walk8 = (node) => {
10604
+ const walk9 = (node) => {
10574
10605
  if (node.type === "call_expression") {
10575
10606
  const targetVar = referencesTargetVar(node);
10576
10607
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -10591,9 +10622,9 @@ function drizzleForeignKeys(file, serviceDir) {
10591
10622
  }
10592
10623
  }
10593
10624
  }
10594
- for (const c of namedChildren4(node)) walk8(c);
10625
+ for (const c of namedChildren4(node)) walk9(c);
10595
10626
  };
10596
- walk8(table.object);
10627
+ walk9(table.object);
10597
10628
  }
10598
10629
  return out;
10599
10630
  }
@@ -11673,15 +11704,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
11673
11704
  return out;
11674
11705
  }
11675
11706
 
11707
+ // src/extract/calls/gorm.ts
11708
+ init_cjs_shims();
11709
+ var import_node_path50 = __toESM(require("path"), 1);
11710
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
11711
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
11712
+ var import_types37 = require("@neat.is/types");
11713
+ var GORM_IMPORT_RE = /gorm\.io\/gorm/;
11714
+ var PARSE_CHUNK11 = 16384;
11715
+ function makeGoParser3() {
11716
+ const p = new import_tree_sitter15.default();
11717
+ p.setLanguage(import_tree_sitter_go4.default);
11718
+ return p;
11719
+ }
11720
+ function parseSource10(parser, source) {
11721
+ return parser.parse(
11722
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
11723
+ );
11724
+ }
11725
+ function walk8(node, visit) {
11726
+ visit(node);
11727
+ for (let i = 0; i < node.namedChildCount; i++) {
11728
+ const c = node.namedChild(i);
11729
+ if (c) walk8(c, visit);
11730
+ }
11731
+ }
11732
+ var COMMON_INITIALISMS = [
11733
+ "ASCII",
11734
+ "HTTPS",
11735
+ "UTF8",
11736
+ "XSRF",
11737
+ "HTML",
11738
+ "HTTP",
11739
+ "JSON",
11740
+ "UUID",
11741
+ "XMPP",
11742
+ "ACL",
11743
+ "API",
11744
+ "CPU",
11745
+ "CSS",
11746
+ "DNS",
11747
+ "EOF",
11748
+ "GUID",
11749
+ "LHS",
11750
+ "QPS",
11751
+ "RAM",
11752
+ "RHS",
11753
+ "RPC",
11754
+ "SLA",
11755
+ "SQL",
11756
+ "SSH",
11757
+ "TCP",
11758
+ "TLS",
11759
+ "TTL",
11760
+ "UDP",
11761
+ "UID",
11762
+ "URI",
11763
+ "URL",
11764
+ "UID",
11765
+ "XSS",
11766
+ "ID",
11767
+ "IP",
11768
+ "UI",
11769
+ "VM",
11770
+ "XML"
11771
+ ].sort((a, b) => b.length - a.length);
11772
+ function titleCase(word) {
11773
+ return word.charAt(0) + word.slice(1).toLowerCase();
11774
+ }
11775
+ function replaceInitialisms(name) {
11776
+ let out = "";
11777
+ let i = 0;
11778
+ while (i < name.length) {
11779
+ let matched = false;
11780
+ for (const init of COMMON_INITIALISMS) {
11781
+ if (name.startsWith(init, i)) {
11782
+ out += titleCase(init);
11783
+ i += init.length;
11784
+ matched = true;
11785
+ break;
11786
+ }
11787
+ }
11788
+ if (!matched) {
11789
+ out += name[i];
11790
+ i++;
11791
+ }
11792
+ }
11793
+ return out;
11794
+ }
11795
+ var isUpper = (c) => c >= "A" && c <= "Z";
11796
+ var isDigit = (c) => c >= "0" && c <= "9";
11797
+ function toDBName(name) {
11798
+ if (name === "") return "";
11799
+ const value = replaceInitialisms(name);
11800
+ if (value.length === 1) return value.toLowerCase();
11801
+ let buf = "";
11802
+ let lastCase = false;
11803
+ let curCase = isUpper(value[0]);
11804
+ for (let i = 0; i < value.length - 1; i++) {
11805
+ const v = value[i];
11806
+ const nextCase = isUpper(value[i + 1]);
11807
+ const nextNumber = isDigit(value[i + 1]);
11808
+ if (curCase) {
11809
+ if (lastCase && (nextCase || nextNumber)) {
11810
+ buf += v.toLowerCase();
11811
+ } else {
11812
+ if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
11813
+ buf += v.toLowerCase();
11814
+ }
11815
+ } else {
11816
+ buf += v;
11817
+ }
11818
+ lastCase = curCase;
11819
+ curCase = nextCase;
11820
+ }
11821
+ const last = value[value.length - 1];
11822
+ if (curCase) {
11823
+ if (!lastCase && value.length > 1) buf += "_";
11824
+ buf += last.toLowerCase();
11825
+ } else {
11826
+ buf += last;
11827
+ }
11828
+ return buf;
11829
+ }
11830
+ var UNCOUNTABLE = /* @__PURE__ */ new Set([
11831
+ "equipment",
11832
+ "information",
11833
+ "rice",
11834
+ "money",
11835
+ "species",
11836
+ "series",
11837
+ "fish",
11838
+ "sheep",
11839
+ "jeans",
11840
+ "police"
11841
+ ]);
11842
+ var IRREGULAR = [
11843
+ ["person", "people"],
11844
+ ["man", "men"],
11845
+ ["child", "children"],
11846
+ ["sex", "sexes"],
11847
+ ["move", "moves"]
11848
+ ];
11849
+ var PLURAL_RULES = [
11850
+ [/(quiz)$/i, "$1zes"],
11851
+ [/^(ox)$/i, "$1en"],
11852
+ [/([ml])ouse$/i, "$1ice"],
11853
+ [/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
11854
+ [/(x|ch|ss|sh)$/i, "$1es"],
11855
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
11856
+ [/(hive)$/i, "$1s"],
11857
+ [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
11858
+ [/sis$/i, "ses"],
11859
+ [/([ti])um$/i, "$1a"],
11860
+ [/([ti])a$/i, "$1a"],
11861
+ [/(buffal|tomat)o$/i, "$1oes"],
11862
+ [/(bu)s$/i, "$1ses"],
11863
+ [/(alias|status)$/i, "$1es"],
11864
+ [/(octop|vir)i$/i, "$1i"],
11865
+ [/(octop|vir)us$/i, "$1i"],
11866
+ [/(ax|test)is$/i, "$1es"],
11867
+ [/s$/i, "s"]
11868
+ ];
11869
+ function pluralize3(word) {
11870
+ if (word === "") return word;
11871
+ const lower = word.toLowerCase();
11872
+ for (const u of UNCOUNTABLE) {
11873
+ if (lower === u || lower.endsWith("_" + u)) return word;
11874
+ }
11875
+ for (const [sing, plur] of IRREGULAR) {
11876
+ const re = new RegExp(sing + "$", "i");
11877
+ if (re.test(word)) return word.replace(re, plur);
11878
+ }
11879
+ for (const [re, rep] of PLURAL_RULES) {
11880
+ if (re.test(word)) return word.replace(re, rep);
11881
+ }
11882
+ return word + "s";
11883
+ }
11884
+ function deriveTableName(structName) {
11885
+ return pluralize3(toDBName(structName));
11886
+ }
11887
+ function stringLiteralValue(node) {
11888
+ if (!node) return null;
11889
+ if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
11890
+ const t = node.text;
11891
+ return t.length >= 2 ? t.slice(1, -1) : "";
11892
+ }
11893
+ return null;
11894
+ }
11895
+ function parseGormTag(tagNode) {
11896
+ const tag = {};
11897
+ if (!tagNode) return tag;
11898
+ let inner = tagNode.text;
11899
+ if (inner.length >= 2) inner = inner.slice(1, -1);
11900
+ if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
11901
+ const m = inner.match(/gorm:"([^"]*)"/);
11902
+ if (!m) return tag;
11903
+ for (const part of m[1].split(";")) {
11904
+ if (part === "") continue;
11905
+ const idx = part.indexOf(":");
11906
+ const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
11907
+ const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
11908
+ if (key === "-") tag.skip = true;
11909
+ else if (key === "column") tag.column = value;
11910
+ else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
11911
+ else if (key === "foreignkey") tag.foreignKey = value;
11912
+ else if (key === "many2many") tag.many2many = value;
11913
+ else if (key === "embedded") tag.embedded = true;
11914
+ else if (key === "embeddedprefix") tag.embeddedPrefix = value;
11915
+ }
11916
+ return tag;
11917
+ }
11918
+ function unwrapType(typeNode) {
11919
+ let isSlice = false;
11920
+ let isPointer = false;
11921
+ let n = typeNode;
11922
+ while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
11923
+ if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
11924
+ if (n.type === "pointer_type") isPointer = true;
11925
+ n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
11926
+ }
11927
+ if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11928
+ if (n.type === "type_identifier") {
11929
+ return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
11930
+ }
11931
+ if (n.type === "qualified_type") {
11932
+ const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
11933
+ const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
11934
+ return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
11935
+ }
11936
+ return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
11937
+ }
11938
+ function readField(fieldDecl) {
11939
+ const names = [];
11940
+ let tagNode = null;
11941
+ for (let i = 0; i < fieldDecl.namedChildCount; i++) {
11942
+ const c = fieldDecl.namedChild(i);
11943
+ if (!c) continue;
11944
+ if (c.type === "field_identifier") names.push(c.text);
11945
+ else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
11946
+ }
11947
+ const typeNode = fieldDecl.childForFieldName("type");
11948
+ const t = unwrapType(typeNode);
11949
+ return {
11950
+ names,
11951
+ typeName: t.name,
11952
+ qualifier: t.qualifier,
11953
+ isSlice: t.isSlice,
11954
+ isPointer: t.isPointer,
11955
+ isQualified: t.isQualified,
11956
+ tag: parseGormTag(tagNode),
11957
+ line: fieldDecl.startPosition.row + 1
11958
+ };
11959
+ }
11960
+ function collectStructs(tree) {
11961
+ const structs = /* @__PURE__ */ new Map();
11962
+ walk8(tree.rootNode, (node) => {
11963
+ if (node.type !== "type_spec") return;
11964
+ const nameNode = node.childForFieldName("name");
11965
+ const typeNode = node.childForFieldName("type");
11966
+ if (!nameNode || typeNode?.type !== "struct_type") return;
11967
+ const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
11968
+ const fields = [];
11969
+ if (list && list.type === "field_declaration_list") {
11970
+ for (let i = 0; i < list.namedChildCount; i++) {
11971
+ const fd = list.namedChild(i);
11972
+ if (fd?.type === "field_declaration") fields.push(readField(fd));
11973
+ }
11974
+ }
11975
+ structs.set(nameNode.text, {
11976
+ name: nameNode.text,
11977
+ fields,
11978
+ line: node.startPosition.row + 1
11979
+ });
11980
+ });
11981
+ return structs;
11982
+ }
11983
+ var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
11984
+ "AutoMigrate",
11985
+ "Model",
11986
+ "Create",
11987
+ "Find",
11988
+ "First",
11989
+ "Take",
11990
+ "Last",
11991
+ "Save",
11992
+ "Delete",
11993
+ "Where",
11994
+ "FirstOrCreate",
11995
+ "FirstOrInit"
11996
+ ]);
11997
+ function compositeStructName(arg) {
11998
+ let n = arg;
11999
+ if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
12000
+ if (!n || n.type !== "composite_literal") return null;
12001
+ const typeNode = n.childForFieldName("type");
12002
+ if (!typeNode) return null;
12003
+ if (typeNode.type === "type_identifier") return typeNode.text;
12004
+ if (typeNode.type === "qualified_type") {
12005
+ return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
12006
+ }
12007
+ return null;
12008
+ }
12009
+ function collectCallModels(tree) {
12010
+ const models = /* @__PURE__ */ new Set();
12011
+ walk8(tree.rootNode, (node) => {
12012
+ if (node.type !== "call_expression") return;
12013
+ const fn = node.childForFieldName("function");
12014
+ if (fn?.type !== "selector_expression") return;
12015
+ const method = fn.childForFieldName("field")?.text;
12016
+ if (!method || !GORM_MODEL_METHODS.has(method)) return;
12017
+ const args = node.childForFieldName("arguments");
12018
+ if (!args) return;
12019
+ for (let i = 0; i < args.namedChildCount; i++) {
12020
+ const arg = args.namedChild(i);
12021
+ if (!arg) continue;
12022
+ const name = compositeStructName(arg);
12023
+ if (name) models.add(name);
12024
+ }
12025
+ });
12026
+ return models;
12027
+ }
12028
+ function collectTableNameOverrides(tree) {
12029
+ const overrides = /* @__PURE__ */ new Map();
12030
+ const declarers = /* @__PURE__ */ new Set();
12031
+ walk8(tree.rootNode, (node) => {
12032
+ if (node.type !== "method_declaration") return;
12033
+ if (node.childForFieldName("name")?.text !== "TableName") return;
12034
+ const receiver = node.childForFieldName("receiver");
12035
+ if (!receiver) return;
12036
+ let recvType = null;
12037
+ for (let i = 0; i < receiver.namedChildCount; i++) {
12038
+ const pd = receiver.namedChild(i);
12039
+ if (pd?.type !== "parameter_declaration") continue;
12040
+ const t = unwrapType(pd.childForFieldName("type"));
12041
+ recvType = t.name;
12042
+ }
12043
+ if (!recvType) return;
12044
+ declarers.add(recvType);
12045
+ const body = node.childForFieldName("body");
12046
+ if (!body) return;
12047
+ let literal = null;
12048
+ walk8(body, (n) => {
12049
+ if (literal !== null) return;
12050
+ if (n.type !== "return_statement") return;
12051
+ const exprList = n.namedChild(0);
12052
+ const first = exprList?.namedChild(0) ?? exprList;
12053
+ const v = stringLiteralValue(first);
12054
+ if (v) literal = v;
12055
+ });
12056
+ if (literal !== null) overrides.set(recvType, literal);
12057
+ });
12058
+ return { overrides, declarers };
12059
+ }
12060
+ function isRelationField(field, structs) {
12061
+ if (field.names.length === 0) return false;
12062
+ if (field.isQualified) return false;
12063
+ if (!field.typeName) return false;
12064
+ return structs.has(field.typeName);
12065
+ }
12066
+ function isGormModelEmbed(field) {
12067
+ return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
12068
+ }
12069
+ function analyze(tree) {
12070
+ const structs = collectStructs(tree);
12071
+ const { overrides, declarers } = collectTableNameOverrides(tree);
12072
+ const callModels = collectCallModels(tree);
12073
+ const models = /* @__PURE__ */ new Set();
12074
+ for (const [name, info] of structs) {
12075
+ if (info.fields.some(isGormModelEmbed)) models.add(name);
12076
+ }
12077
+ for (const name of callModels) if (structs.has(name)) models.add(name);
12078
+ for (const name of declarers) if (structs.has(name)) models.add(name);
12079
+ let grew = true;
12080
+ while (grew) {
12081
+ grew = false;
12082
+ for (const name of Array.from(models)) {
12083
+ const info = structs.get(name);
12084
+ if (!info) continue;
12085
+ for (const field of info.fields) {
12086
+ if (!isRelationField(field, structs)) continue;
12087
+ const target = field.typeName;
12088
+ if (!models.has(target) && structs.has(target)) {
12089
+ models.add(target);
12090
+ grew = true;
12091
+ }
12092
+ }
12093
+ }
12094
+ }
12095
+ const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
12096
+ return { structs, models, tableFor };
12097
+ }
12098
+ function collectColumns(struct, structs, seen, prefix, out, emitted) {
12099
+ if (seen.has(struct.name)) return;
12100
+ seen.add(struct.name);
12101
+ const add = (col) => {
12102
+ const full = prefix + col;
12103
+ if (!emitted.has(full)) {
12104
+ emitted.add(full);
12105
+ out.push(full);
12106
+ }
12107
+ };
12108
+ for (const field of struct.fields) {
12109
+ if (field.tag.skip) continue;
12110
+ if (field.names.length === 0) {
12111
+ if (isGormModelEmbed(field)) {
12112
+ add("id");
12113
+ add("created_at");
12114
+ add("updated_at");
12115
+ add("deleted_at");
12116
+ } else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
12117
+ collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
12118
+ }
12119
+ continue;
12120
+ }
12121
+ if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
12122
+ collectColumns(
12123
+ structs.get(field.typeName),
12124
+ structs,
12125
+ seen,
12126
+ prefix + (field.tag.embeddedPrefix ?? ""),
12127
+ out,
12128
+ emitted
12129
+ );
12130
+ continue;
12131
+ }
12132
+ if (isRelationField(field, structs)) continue;
12133
+ if (field.names.length === 1 && field.tag.column) {
12134
+ add(field.tag.column);
12135
+ } else {
12136
+ for (const n of field.names) add(toDBName(n));
12137
+ }
12138
+ }
12139
+ seen.delete(struct.name);
12140
+ }
12141
+ function gormEndpointsFromFile(file, serviceDir) {
12142
+ if (import_node_path50.default.extname(file.path) !== ".go") return [];
12143
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
12144
+ const tree = parseSource10(makeGoParser3(), file.content);
12145
+ const { structs, models, tableFor } = analyze(tree);
12146
+ const out = [];
12147
+ const seenTables = /* @__PURE__ */ new Set();
12148
+ for (const name of models) {
12149
+ const struct = structs.get(name);
12150
+ if (!struct) continue;
12151
+ const table = tableFor(name);
12152
+ if (seenTables.has(table)) continue;
12153
+ seenTables.add(table);
12154
+ const columns = [];
12155
+ collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
12156
+ out.push({
12157
+ infraId: (0, import_types37.infraId)("sql-table", table),
12158
+ name: table,
12159
+ kind: "sql-table",
12160
+ edgeType: "CALLS",
12161
+ confidenceKind: "structural",
12162
+ ...columns.length > 0 ? { columns } : {},
12163
+ evidence: {
12164
+ file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
12165
+ line: struct.line,
12166
+ snippet: snippet(file.content, struct.line)
12167
+ }
12168
+ });
12169
+ }
12170
+ return out;
12171
+ }
12172
+ function gormForeignKeys(file, serviceDir) {
12173
+ if (import_node_path50.default.extname(file.path) !== ".go") return [];
12174
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
12175
+ const tree = parseSource10(makeGoParser3(), file.content);
12176
+ const { structs, models, tableFor } = analyze(tree);
12177
+ const out = [];
12178
+ const seen = /* @__PURE__ */ new Set();
12179
+ const emit = (childTable, parentTable, line) => {
12180
+ if (!childTable || !parentTable || childTable === parentTable) return;
12181
+ const key = `${childTable}->${parentTable}`;
12182
+ if (seen.has(key)) return;
12183
+ seen.add(key);
12184
+ out.push({
12185
+ childTable,
12186
+ parentTable,
12187
+ evidence: {
12188
+ file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
12189
+ line,
12190
+ snippet: snippet(file.content, line)
12191
+ }
12192
+ });
12193
+ };
12194
+ for (const name of models) {
12195
+ const struct = structs.get(name);
12196
+ if (!struct) continue;
12197
+ const thisTable = tableFor(name);
12198
+ const scalarNames = new Set(
12199
+ struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
12200
+ );
12201
+ for (const field of struct.fields) {
12202
+ if (field.tag.skip) continue;
12203
+ if (!isRelationField(field, structs)) continue;
12204
+ const relTable = tableFor(field.typeName);
12205
+ if (field.tag.many2many) {
12206
+ emit(field.tag.many2many, thisTable, field.line);
12207
+ emit(field.tag.many2many, relTable, field.line);
12208
+ continue;
12209
+ }
12210
+ if (field.isSlice) {
12211
+ emit(relTable, thisTable, field.line);
12212
+ continue;
12213
+ }
12214
+ const convFk = field.names[0] + "ID";
12215
+ const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
12216
+ if (belongsTo) emit(thisTable, relTable, field.line);
12217
+ else emit(relTable, thisTable, field.line);
12218
+ }
12219
+ }
12220
+ return out;
12221
+ }
12222
+
11676
12223
  // src/extract/calls/index.ts
11677
12224
  function edgeTypeFromEndpoint(ep) {
11678
12225
  switch (ep.edgeType) {
11679
12226
  case "PUBLISHES_TO":
11680
- return import_types37.EdgeType.PUBLISHES_TO;
12227
+ return import_types38.EdgeType.PUBLISHES_TO;
11681
12228
  case "CONSUMES_FROM":
11682
- return import_types37.EdgeType.CONSUMES_FROM;
12229
+ return import_types38.EdgeType.CONSUMES_FROM;
11683
12230
  default:
11684
- return import_types37.EdgeType.CALLS;
12231
+ return import_types38.EdgeType.CALLS;
11685
12232
  }
11686
12233
  }
11687
12234
  function isAwsKind(kind) {
@@ -11714,6 +12261,11 @@ async function addExternalEndpointEdges(graph, services) {
11714
12261
  } catch (err) {
11715
12262
  recordExtractionError("go SQL call extraction", file.path, err);
11716
12263
  }
12264
+ try {
12265
+ endpoints.push(...gormEndpointsFromFile(file, service.dir));
12266
+ } catch (err) {
12267
+ recordExtractionError("gorm data-axis extraction", file.path, err);
12268
+ }
11717
12269
  try {
11718
12270
  endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
11719
12271
  endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
@@ -11736,7 +12288,7 @@ async function addExternalEndpointEdges(graph, services) {
11736
12288
  if (!graph.hasNode(ep.infraId)) {
11737
12289
  const node = {
11738
12290
  id: ep.infraId,
11739
- type: import_types37.NodeType.InfraNode,
12291
+ type: import_types38.NodeType.InfraNode,
11740
12292
  name: ep.name,
11741
12293
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
11742
12294
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -11749,21 +12301,21 @@ async function addExternalEndpointEdges(graph, services) {
11749
12301
  }
11750
12302
  if (ep.columns && ep.columns.length > 0) {
11751
12303
  const node = graph.getNodeAttributes(ep.infraId);
11752
- if (node.type === import_types37.NodeType.InfraNode) {
12304
+ if (node.type === import_types38.NodeType.InfraNode) {
11753
12305
  graph.replaceNodeAttributes(ep.infraId, {
11754
12306
  ...node,
11755
12307
  columns: foldColumns(
11756
12308
  node.columns,
11757
12309
  ep.columns,
11758
- import_types37.Provenance.EXTRACTED,
11759
- (0, import_types37.confidenceForExtracted)(ep.confidenceKind)
12310
+ import_types38.Provenance.EXTRACTED,
12311
+ (0, import_types38.confidenceForExtracted)(ep.confidenceKind)
11760
12312
  )
11761
12313
  });
11762
12314
  }
11763
12315
  }
11764
12316
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
11765
12317
  const node = graph.getNodeAttributes(ep.infraId);
11766
- if (node.type === import_types37.NodeType.InfraNode) {
12318
+ if (node.type === import_types38.NodeType.InfraNode) {
11767
12319
  graph.replaceNodeAttributes(ep.infraId, {
11768
12320
  ...node,
11769
12321
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -11771,7 +12323,7 @@ async function addExternalEndpointEdges(graph, services) {
11771
12323
  }
11772
12324
  }
11773
12325
  const edgeType = edgeTypeFromEndpoint(ep);
11774
- const confidence = (0, import_types37.confidenceForExtracted)(ep.confidenceKind);
12326
+ const confidence = (0, import_types38.confidenceForExtracted)(ep.confidenceKind);
11775
12327
  const relFile = toPosix(ep.evidence.file);
11776
12328
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
11777
12329
  graph,
@@ -11781,7 +12333,7 @@ async function addExternalEndpointEdges(graph, services) {
11781
12333
  );
11782
12334
  nodesAdded += n;
11783
12335
  edgesAdded += e;
11784
- if (!(0, import_types37.passesExtractedFloor)(confidence)) {
12336
+ if (!(0, import_types38.passesExtractedFloor)(confidence)) {
11785
12337
  noteExtractedDropped({
11786
12338
  source: fileNodeId,
11787
12339
  target: ep.infraId,
@@ -11801,7 +12353,7 @@ async function addExternalEndpointEdges(graph, services) {
11801
12353
  source: fileNodeId,
11802
12354
  target: ep.infraId,
11803
12355
  type: edgeType,
11804
- provenance: import_types37.Provenance.EXTRACTED,
12356
+ provenance: import_types38.Provenance.EXTRACTED,
11805
12357
  confidence,
11806
12358
  evidence: ep.evidence
11807
12359
  };
@@ -11824,7 +12376,7 @@ async function addCallEdges(graph, services) {
11824
12376
 
11825
12377
  // src/extract/table-edges.ts
11826
12378
  init_cjs_shims();
11827
- var import_types38 = require("@neat.is/types");
12379
+ var import_types39 = require("@neat.is/types");
11828
12380
  async function addTableEdges(graph, services) {
11829
12381
  let nodesAdded = 0;
11830
12382
  let edgesAdded = 0;
@@ -11838,6 +12390,7 @@ async function addTableEdges(graph, services) {
11838
12390
  refs.push(...sqlalchemyForeignKeys(file, service.dir));
11839
12391
  refs.push(...railsSchemaForeignKeys(file, service.dir));
11840
12392
  refs.push(...laravelMigrationForeignKeys(file, service.dir));
12393
+ refs.push(...gormForeignKeys(file, service.dir));
11841
12394
  modelRefs.push(...railsModelForeignKeys(file, service.dir));
11842
12395
  modelRefs.push(...laravelModelForeignKeys(file, service.dir));
11843
12396
  } catch (err) {
@@ -11851,20 +12404,20 @@ async function addTableEdges(graph, services) {
11851
12404
  }
11852
12405
  refs.push(...modelRefs);
11853
12406
  for (const ref of refs) {
11854
- const childId = (0, import_types38.infraId)("sql-table", ref.childTable);
11855
- const parentId = (0, import_types38.infraId)("sql-table", ref.parentTable);
12407
+ const childId = (0, import_types39.infraId)("sql-table", ref.childTable);
12408
+ const parentId = (0, import_types39.infraId)("sql-table", ref.parentTable);
11856
12409
  if (childId === parentId) continue;
11857
12410
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
11858
12411
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
11859
- const edgeId = (0, import_types38.extractedEdgeId)(childId, parentId, import_types38.EdgeType.REFERENCES);
12412
+ const edgeId = (0, import_types39.extractedEdgeId)(childId, parentId, import_types39.EdgeType.REFERENCES);
11860
12413
  if (graph.hasEdge(edgeId)) continue;
11861
12414
  const edge = {
11862
12415
  id: edgeId,
11863
12416
  source: childId,
11864
12417
  target: parentId,
11865
- type: import_types38.EdgeType.REFERENCES,
11866
- provenance: import_types38.Provenance.EXTRACTED,
11867
- confidence: (0, import_types38.confidenceForExtracted)("structural"),
12418
+ type: import_types39.EdgeType.REFERENCES,
12419
+ provenance: import_types39.Provenance.EXTRACTED,
12420
+ confidence: (0, import_types39.confidenceForExtracted)("structural"),
11868
12421
  evidence: ref.evidence
11869
12422
  };
11870
12423
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -11877,7 +12430,7 @@ function ensureTableNode(graph, id, name) {
11877
12430
  if (graph.hasNode(id)) return 0;
11878
12431
  const node = {
11879
12432
  id,
11880
- type: import_types38.NodeType.InfraNode,
12433
+ type: import_types39.NodeType.InfraNode,
11881
12434
  name,
11882
12435
  provider: "self",
11883
12436
  kind: "sql-table"
@@ -11891,16 +12444,16 @@ init_cjs_shims();
11891
12444
 
11892
12445
  // src/extract/infra/docker-compose.ts
11893
12446
  init_cjs_shims();
11894
- var import_node_path50 = __toESM(require("path"), 1);
11895
- var import_types40 = require("@neat.is/types");
12447
+ var import_node_path51 = __toESM(require("path"), 1);
12448
+ var import_types41 = require("@neat.is/types");
11896
12449
 
11897
12450
  // src/extract/infra/shared.ts
11898
12451
  init_cjs_shims();
11899
- var import_types39 = require("@neat.is/types");
12452
+ var import_types40 = require("@neat.is/types");
11900
12453
  function makeInfraNode(kind, name, provider = "self", extras) {
11901
12454
  return {
11902
- id: (0, import_types39.infraId)(kind, name),
11903
- type: import_types39.NodeType.InfraNode,
12455
+ id: (0, import_types40.infraId)(kind, name),
12456
+ type: import_types40.NodeType.InfraNode,
11904
12457
  name,
11905
12458
  provider,
11906
12459
  kind,
@@ -11944,8 +12497,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
11944
12497
  source: anchorId,
11945
12498
  target: node.id,
11946
12499
  type: edgeType,
11947
- provenance: import_types39.Provenance.EXTRACTED,
11948
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
12500
+ provenance: import_types40.Provenance.EXTRACTED,
12501
+ confidence: (0, import_types40.confidenceForExtracted)("structural"),
11949
12502
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11950
12503
  };
11951
12504
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11962,7 +12515,7 @@ function dependsOnList(value) {
11962
12515
  }
11963
12516
  function serviceNameToServiceNode(name, services) {
11964
12517
  for (const s of services) {
11965
- if (s.node.name === name || import_node_path50.default.basename(s.dir) === name) return s.node.id;
12518
+ if (s.node.name === name || import_node_path51.default.basename(s.dir) === name) return s.node.id;
11966
12519
  }
11967
12520
  return null;
11968
12521
  }
@@ -11971,7 +12524,7 @@ async function addComposeInfra(graph, scanPath, services) {
11971
12524
  let edgesAdded = 0;
11972
12525
  let composePath = null;
11973
12526
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
11974
- const abs = import_node_path50.default.join(scanPath, name);
12527
+ const abs = import_node_path51.default.join(scanPath, name);
11975
12528
  if (await exists2(abs)) {
11976
12529
  composePath = abs;
11977
12530
  break;
@@ -11984,13 +12537,13 @@ async function addComposeInfra(graph, scanPath, services) {
11984
12537
  } catch (err) {
11985
12538
  recordExtractionError(
11986
12539
  "infra docker-compose",
11987
- import_node_path50.default.relative(scanPath, composePath),
12540
+ import_node_path51.default.relative(scanPath, composePath),
11988
12541
  err
11989
12542
  );
11990
12543
  return { nodesAdded, edgesAdded };
11991
12544
  }
11992
12545
  if (!compose?.services) return { nodesAdded, edgesAdded };
11993
- const evidenceFile = import_node_path50.default.relative(scanPath, composePath).split(import_node_path50.default.sep).join("/");
12546
+ const evidenceFile = import_node_path51.default.relative(scanPath, composePath).split(import_node_path51.default.sep).join("/");
11994
12547
  const composeNameToNodeId = /* @__PURE__ */ new Map();
11995
12548
  for (const [composeName, svc] of Object.entries(compose.services)) {
11996
12549
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -12012,15 +12565,15 @@ async function addComposeInfra(graph, scanPath, services) {
12012
12565
  for (const dep of dependsOnList(svc.depends_on)) {
12013
12566
  const targetId = composeNameToNodeId.get(dep);
12014
12567
  if (!targetId) continue;
12015
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types40.EdgeType.DEPENDS_ON);
12568
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types41.EdgeType.DEPENDS_ON);
12016
12569
  if (graph.hasEdge(edgeId)) continue;
12017
12570
  const edge = {
12018
12571
  id: edgeId,
12019
12572
  source: sourceId,
12020
12573
  target: targetId,
12021
- type: import_types40.EdgeType.DEPENDS_ON,
12022
- provenance: import_types40.Provenance.EXTRACTED,
12023
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12574
+ type: import_types41.EdgeType.DEPENDS_ON,
12575
+ provenance: import_types41.Provenance.EXTRACTED,
12576
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
12024
12577
  evidence: { file: evidenceFile }
12025
12578
  };
12026
12579
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12032,9 +12585,9 @@ async function addComposeInfra(graph, scanPath, services) {
12032
12585
 
12033
12586
  // src/extract/infra/dockerfile.ts
12034
12587
  init_cjs_shims();
12035
- var import_node_path51 = __toESM(require("path"), 1);
12588
+ var import_node_path52 = __toESM(require("path"), 1);
12036
12589
  var import_node_fs20 = require("fs");
12037
- var import_types41 = require("@neat.is/types");
12590
+ var import_types42 = require("@neat.is/types");
12038
12591
  function readDockerfile(content) {
12039
12592
  let image = null;
12040
12593
  const ports = [];
@@ -12063,7 +12616,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12063
12616
  let nodesAdded = 0;
12064
12617
  let edgesAdded = 0;
12065
12618
  for (const service of services) {
12066
- const dockerfilePath = import_node_path51.default.join(service.dir, "Dockerfile");
12619
+ const dockerfilePath = import_node_path52.default.join(service.dir, "Dockerfile");
12067
12620
  if (!await exists2(dockerfilePath)) continue;
12068
12621
  let content;
12069
12622
  try {
@@ -12071,7 +12624,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12071
12624
  } catch (err) {
12072
12625
  recordExtractionError(
12073
12626
  "infra dockerfile",
12074
- import_node_path51.default.relative(scanPath, dockerfilePath),
12627
+ import_node_path52.default.relative(scanPath, dockerfilePath),
12075
12628
  err
12076
12629
  );
12077
12630
  continue;
@@ -12083,8 +12636,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12083
12636
  graph.addNode(node.id, node);
12084
12637
  nodesAdded++;
12085
12638
  }
12086
- const relDockerfile = toPosix(import_node_path51.default.relative(service.dir, dockerfilePath));
12087
- const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, dockerfilePath));
12639
+ const relDockerfile = toPosix(import_node_path52.default.relative(service.dir, dockerfilePath));
12640
+ const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, dockerfilePath));
12088
12641
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
12089
12642
  graph,
12090
12643
  service.pkg.name,
@@ -12093,15 +12646,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12093
12646
  );
12094
12647
  nodesAdded += fn;
12095
12648
  edgesAdded += fe;
12096
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types41.EdgeType.RUNS_ON);
12649
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types42.EdgeType.RUNS_ON);
12097
12650
  if (!graph.hasEdge(edgeId)) {
12098
12651
  const edge = {
12099
12652
  id: edgeId,
12100
12653
  source: fileNodeId,
12101
12654
  target: node.id,
12102
- type: import_types41.EdgeType.RUNS_ON,
12103
- provenance: import_types41.Provenance.EXTRACTED,
12104
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12655
+ type: import_types42.EdgeType.RUNS_ON,
12656
+ provenance: import_types42.Provenance.EXTRACTED,
12657
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
12105
12658
  evidence: {
12106
12659
  file: evidenceFile,
12107
12660
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -12116,15 +12669,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12116
12669
  graph.addNode(portNode.id, portNode);
12117
12670
  nodesAdded++;
12118
12671
  }
12119
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types41.EdgeType.CONNECTS_TO);
12672
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types42.EdgeType.CONNECTS_TO);
12120
12673
  if (graph.hasEdge(portEdgeId)) continue;
12121
12674
  const portEdge = {
12122
12675
  id: portEdgeId,
12123
12676
  source: fileNodeId,
12124
12677
  target: portNode.id,
12125
- type: import_types41.EdgeType.CONNECTS_TO,
12126
- provenance: import_types41.Provenance.EXTRACTED,
12127
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12678
+ type: import_types42.EdgeType.CONNECTS_TO,
12679
+ provenance: import_types42.Provenance.EXTRACTED,
12680
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
12128
12681
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
12129
12682
  };
12130
12683
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -12137,8 +12690,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12137
12690
  // src/extract/infra/terraform.ts
12138
12691
  init_cjs_shims();
12139
12692
  var import_node_fs21 = require("fs");
12140
- var import_node_path52 = __toESM(require("path"), 1);
12141
- var import_types42 = require("@neat.is/types");
12693
+ var import_node_path53 = __toESM(require("path"), 1);
12694
+ var import_types43 = require("@neat.is/types");
12142
12695
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
12143
12696
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
12144
12697
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -12148,11 +12701,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
12148
12701
  for (const entry of entries) {
12149
12702
  if (entry.isDirectory()) {
12150
12703
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
12151
- const child = import_node_path52.default.join(start, entry.name);
12704
+ const child = import_node_path53.default.join(start, entry.name);
12152
12705
  if (await isPythonVenvDir(child)) continue;
12153
12706
  out.push(...await walkTfFiles(child, depth + 1, max));
12154
12707
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
12155
- out.push(import_node_path52.default.join(start, entry.name));
12708
+ out.push(import_node_path53.default.join(start, entry.name));
12156
12709
  }
12157
12710
  }
12158
12711
  return out;
@@ -12184,7 +12737,7 @@ async function addTerraformResources(graph, scanPath) {
12184
12737
  const files = await walkTfFiles(scanPath);
12185
12738
  for (const file of files) {
12186
12739
  const content = await import_node_fs21.promises.readFile(file, "utf8");
12187
- const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, file));
12740
+ const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, file));
12188
12741
  const resources = [];
12189
12742
  const byKey = /* @__PURE__ */ new Map();
12190
12743
  RESOURCE_RE.lastIndex = 0;
@@ -12219,16 +12772,16 @@ async function addTerraformResources(graph, scanPath) {
12219
12772
  if (!target) continue;
12220
12773
  if (seen.has(target.nodeId)) continue;
12221
12774
  seen.add(target.nodeId);
12222
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types42.EdgeType.DEPENDS_ON);
12775
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types43.EdgeType.DEPENDS_ON);
12223
12776
  if (graph.hasEdge(edgeId)) continue;
12224
12777
  const line = lineAt2(content, resource.bodyOffset + ref.index);
12225
12778
  const edge = {
12226
12779
  id: edgeId,
12227
12780
  source: resource.nodeId,
12228
12781
  target: target.nodeId,
12229
- type: import_types42.EdgeType.DEPENDS_ON,
12230
- provenance: import_types42.Provenance.EXTRACTED,
12231
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12782
+ type: import_types43.EdgeType.DEPENDS_ON,
12783
+ provenance: import_types43.Provenance.EXTRACTED,
12784
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
12232
12785
  evidence: { file: evidenceFile, line, snippet: key }
12233
12786
  };
12234
12787
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12242,7 +12795,7 @@ async function addTerraformResources(graph, scanPath) {
12242
12795
  // src/extract/infra/k8s.ts
12243
12796
  init_cjs_shims();
12244
12797
  var import_node_fs22 = require("fs");
12245
- var import_node_path53 = __toESM(require("path"), 1);
12798
+ var import_node_path54 = __toESM(require("path"), 1);
12246
12799
  var import_yaml3 = require("yaml");
12247
12800
  var K8S_KIND_TO_INFRA_KIND = {
12248
12801
  Service: "k8s-service",
@@ -12260,11 +12813,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
12260
12813
  for (const entry of entries) {
12261
12814
  if (entry.isDirectory()) {
12262
12815
  if (IGNORED_DIRS.has(entry.name)) continue;
12263
- const child = import_node_path53.default.join(start, entry.name);
12816
+ const child = import_node_path54.default.join(start, entry.name);
12264
12817
  if (await isPythonVenvDir(child)) continue;
12265
12818
  out.push(...await walkYamlFiles2(child, depth + 1, max));
12266
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path53.default.extname(entry.name))) {
12267
- out.push(import_node_path53.default.join(start, entry.name));
12819
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path54.default.extname(entry.name))) {
12820
+ out.push(import_node_path54.default.join(start, entry.name));
12268
12821
  }
12269
12822
  }
12270
12823
  return out;
@@ -12298,13 +12851,13 @@ async function addK8sResources(graph, scanPath) {
12298
12851
  // src/extract/infra/cloudflare.ts
12299
12852
  init_cjs_shims();
12300
12853
  var import_node_fs23 = require("fs");
12301
- var import_node_path54 = __toESM(require("path"), 1);
12854
+ var import_node_path55 = __toESM(require("path"), 1);
12302
12855
  var import_smol_toml2 = require("smol-toml");
12303
- var import_types43 = require("@neat.is/types");
12856
+ var import_types44 = require("@neat.is/types");
12304
12857
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
12305
12858
  async function readWranglerConfig(dir) {
12306
12859
  for (const filename of WRANGLER_FILENAMES) {
12307
- const abs = import_node_path54.default.join(dir, filename);
12860
+ const abs = import_node_path55.default.join(dir, filename);
12308
12861
  if (!await exists2(abs)) continue;
12309
12862
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
12310
12863
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12348,8 +12901,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
12348
12901
  source: anchorId,
12349
12902
  target: node.id,
12350
12903
  type: edgeType,
12351
- provenance: import_types43.Provenance.EXTRACTED,
12352
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
12904
+ provenance: import_types44.Provenance.EXTRACTED,
12905
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
12353
12906
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
12354
12907
  };
12355
12908
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12367,11 +12920,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12367
12920
  try {
12368
12921
  read = await readWranglerConfig(service.dir);
12369
12922
  } catch (err) {
12370
- recordExtractionError("infra cloudflare", import_node_path54.default.relative(scanPath, service.dir), err);
12923
+ recordExtractionError("infra cloudflare", import_node_path55.default.relative(scanPath, service.dir), err);
12371
12924
  continue;
12372
12925
  }
12373
12926
  if (!read || !read.config.name) continue;
12374
- const evidenceFile = toPosix(import_node_path54.default.relative(scanPath, import_node_path54.default.join(service.dir, read.relFile)));
12927
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, read.relFile)));
12375
12928
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
12376
12929
  }
12377
12930
  for (const worker of discovered) {
@@ -12383,7 +12936,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12383
12936
  }
12384
12937
  let anchorId = service.node.id;
12385
12938
  if (config.main) {
12386
- const entryRelPath = toPosix(import_node_path54.default.normalize(config.main));
12939
+ const entryRelPath = toPosix(import_node_path55.default.normalize(config.main));
12387
12940
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
12388
12941
  graph,
12389
12942
  service.pkg.name,
@@ -12410,15 +12963,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12410
12963
  nodesAdded++;
12411
12964
  }
12412
12965
  if (runtimeNode.id !== anchorId) {
12413
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types43.EdgeType.RUNS_ON);
12966
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types44.EdgeType.RUNS_ON);
12414
12967
  if (!graph.hasEdge(runsOnId)) {
12415
12968
  const edge = {
12416
12969
  id: runsOnId,
12417
12970
  source: anchorId,
12418
12971
  target: runtimeNode.id,
12419
- type: import_types43.EdgeType.RUNS_ON,
12420
- provenance: import_types43.Provenance.EXTRACTED,
12421
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
12972
+ type: import_types44.EdgeType.RUNS_ON,
12973
+ provenance: import_types44.Provenance.EXTRACTED,
12974
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
12422
12975
  evidence: {
12423
12976
  file: evidenceFile,
12424
12977
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -12432,7 +12985,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12432
12985
  const result = addResourceEdge(
12433
12986
  graph,
12434
12987
  anchorId,
12435
- import_types43.EdgeType.CONNECTS_TO,
12988
+ import_types44.EdgeType.CONNECTS_TO,
12436
12989
  "cloudflare-route",
12437
12990
  route,
12438
12991
  evidenceFile,
@@ -12456,7 +13009,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12456
13009
  const result = addResourceEdge(
12457
13010
  graph,
12458
13011
  anchorId,
12459
- import_types43.EdgeType.DEPENDS_ON,
13012
+ import_types44.EdgeType.DEPENDS_ON,
12460
13013
  group.kind,
12461
13014
  name,
12462
13015
  evidenceFile,
@@ -12470,7 +13023,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12470
13023
  const result = addResourceEdge(
12471
13024
  graph,
12472
13025
  anchorId,
12473
- import_types43.EdgeType.DEPENDS_ON,
13026
+ import_types44.EdgeType.DEPENDS_ON,
12474
13027
  "cloudflare-cron",
12475
13028
  cron,
12476
13029
  evidenceFile,
@@ -12483,7 +13036,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12483
13036
  const result = addResourceEdge(
12484
13037
  graph,
12485
13038
  anchorId,
12486
- import_types43.EdgeType.DEPENDS_ON,
13039
+ import_types44.EdgeType.DEPENDS_ON,
12487
13040
  "cloudflare-env-var",
12488
13041
  varName,
12489
13042
  evidenceFile,
@@ -12496,15 +13049,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12496
13049
  if (!svc.service) continue;
12497
13050
  const target = workerIndex.get(svc.service);
12498
13051
  if (target && target.anchorId !== anchorId) {
12499
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types43.EdgeType.CALLS);
13052
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types44.EdgeType.CALLS);
12500
13053
  if (!graph.hasEdge(edgeId)) {
12501
13054
  const edge = {
12502
13055
  id: edgeId,
12503
13056
  source: anchorId,
12504
13057
  target: target.anchorId,
12505
- type: import_types43.EdgeType.CALLS,
12506
- provenance: import_types43.Provenance.EXTRACTED,
12507
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
13058
+ type: import_types44.EdgeType.CALLS,
13059
+ provenance: import_types44.Provenance.EXTRACTED,
13060
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
12508
13061
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
12509
13062
  };
12510
13063
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12515,7 +13068,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12515
13068
  const result = addResourceEdge(
12516
13069
  graph,
12517
13070
  anchorId,
12518
- import_types43.EdgeType.DEPENDS_ON,
13071
+ import_types44.EdgeType.DEPENDS_ON,
12519
13072
  "cloudflare-service-binding",
12520
13073
  svc.service,
12521
13074
  evidenceFile,
@@ -12531,12 +13084,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12531
13084
  // src/extract/infra/vercel.ts
12532
13085
  init_cjs_shims();
12533
13086
  var import_node_fs24 = require("fs");
12534
- var import_node_path55 = __toESM(require("path"), 1);
12535
- var import_types44 = require("@neat.is/types");
13087
+ var import_node_path56 = __toESM(require("path"), 1);
13088
+ var import_types45 = require("@neat.is/types");
12536
13089
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
12537
13090
  async function readVercelConfig(dir) {
12538
13091
  for (const filename of VERCEL_CONFIG_FILENAMES) {
12539
- const abs = import_node_path55.default.join(dir, filename);
13092
+ const abs = import_node_path56.default.join(dir, filename);
12540
13093
  if (!await exists2(abs)) continue;
12541
13094
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
12542
13095
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -12545,7 +13098,7 @@ async function readVercelConfig(dir) {
12545
13098
  return null;
12546
13099
  }
12547
13100
  async function readLinkedProjectName(dir) {
12548
- const abs = import_node_path55.default.join(dir, ".vercel", "project.json");
13101
+ const abs = import_node_path56.default.join(dir, ".vercel", "project.json");
12549
13102
  if (!await exists2(abs)) return void 0;
12550
13103
  const parsed = JSON.parse(await import_node_fs24.promises.readFile(abs, "utf8"));
12551
13104
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -12563,7 +13116,7 @@ async function addVercelServices(graph, services, scanPath) {
12563
13116
  read = await readVercelConfig(service.dir);
12564
13117
  projectName = await readLinkedProjectName(service.dir);
12565
13118
  } catch (err) {
12566
- recordExtractionError("infra vercel", import_node_path55.default.relative(scanPath, service.dir), err);
13119
+ recordExtractionError("infra vercel", import_node_path56.default.relative(scanPath, service.dir), err);
12567
13120
  continue;
12568
13121
  }
12569
13122
  if (!read && !projectName) continue;
@@ -12579,7 +13132,7 @@ async function addVercelServices(graph, services, scanPath) {
12579
13132
  const anchorId = service.node.id;
12580
13133
  if (!read) continue;
12581
13134
  const { config, relFile, raw } = read;
12582
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
13135
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12583
13136
  const add = (edgeType, kind, name) => {
12584
13137
  if (!name) return;
12585
13138
  const result = emitPlatformResourceEdge(
@@ -12595,12 +13148,12 @@ async function addVercelServices(graph, services, scanPath) {
12595
13148
  nodesAdded += result.nodesAdded;
12596
13149
  edgesAdded += result.edgesAdded;
12597
13150
  };
12598
- add(import_types44.EdgeType.RUNS_ON, "vercel", "vercel");
12599
- for (const cron of config.crons ?? []) add(import_types44.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
12600
- for (const varName of Object.keys(config.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12601
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types44.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
13151
+ add(import_types45.EdgeType.RUNS_ON, "vercel", "vercel");
13152
+ for (const cron of config.crons ?? []) add(import_types45.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
13153
+ for (const varName of Object.keys(config.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
13154
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12602
13155
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
12603
- add(import_types44.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
13156
+ add(import_types45.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12604
13157
  }
12605
13158
  }
12606
13159
  return { nodesAdded, edgesAdded };
@@ -12609,13 +13162,13 @@ async function addVercelServices(graph, services, scanPath) {
12609
13162
  // src/extract/infra/railway.ts
12610
13163
  init_cjs_shims();
12611
13164
  var import_node_fs25 = require("fs");
12612
- var import_node_path56 = __toESM(require("path"), 1);
13165
+ var import_node_path57 = __toESM(require("path"), 1);
12613
13166
  var import_smol_toml3 = require("smol-toml");
12614
- var import_types45 = require("@neat.is/types");
13167
+ var import_types46 = require("@neat.is/types");
12615
13168
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
12616
13169
  async function readRailwayConfig(dir) {
12617
13170
  for (const filename of RAILWAY_FILENAMES) {
12618
- const abs = import_node_path56.default.join(dir, filename);
13171
+ const abs = import_node_path57.default.join(dir, filename);
12619
13172
  if (!await exists2(abs)) continue;
12620
13173
  const raw = await import_node_fs25.promises.readFile(abs, "utf8");
12621
13174
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12631,7 +13184,7 @@ async function addRailwayServices(graph, services, scanPath) {
12631
13184
  try {
12632
13185
  read = await readRailwayConfig(service.dir);
12633
13186
  } catch (err) {
12634
- recordExtractionError("infra railway", import_node_path56.default.relative(scanPath, service.dir), err);
13187
+ recordExtractionError("infra railway", import_node_path57.default.relative(scanPath, service.dir), err);
12635
13188
  continue;
12636
13189
  }
12637
13190
  if (!read) continue;
@@ -12641,7 +13194,7 @@ async function addRailwayServices(graph, services, scanPath) {
12641
13194
  }
12642
13195
  const anchorId = service.node.id;
12643
13196
  const { config, relFile, raw } = read;
12644
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
13197
+ const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
12645
13198
  const add = (edgeType, kind, name) => {
12646
13199
  if (!name) return;
12647
13200
  const result = emitPlatformResourceEdge(
@@ -12657,9 +13210,9 @@ async function addRailwayServices(graph, services, scanPath) {
12657
13210
  nodesAdded += result.nodesAdded;
12658
13211
  edgesAdded += result.edgesAdded;
12659
13212
  };
12660
- add(import_types45.EdgeType.RUNS_ON, "railway", "railway");
12661
- add(import_types45.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
12662
- add(import_types45.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
13213
+ add(import_types46.EdgeType.RUNS_ON, "railway", "railway");
13214
+ add(import_types46.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
13215
+ add(import_types46.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12663
13216
  }
12664
13217
  return { nodesAdded, edgesAdded };
12665
13218
  }
@@ -12667,12 +13220,12 @@ async function addRailwayServices(graph, services, scanPath) {
12667
13220
  // src/extract/infra/supabase.ts
12668
13221
  init_cjs_shims();
12669
13222
  var import_node_fs26 = require("fs");
12670
- var import_node_path57 = __toESM(require("path"), 1);
13223
+ var import_node_path58 = __toESM(require("path"), 1);
12671
13224
  var import_smol_toml4 = require("smol-toml");
12672
- var import_types46 = require("@neat.is/types");
13225
+ var import_types47 = require("@neat.is/types");
12673
13226
  async function readSupabaseConfig(dir) {
12674
- const relFile = import_node_path57.default.join("supabase", "config.toml");
12675
- const abs = import_node_path57.default.join(dir, relFile);
13227
+ const relFile = import_node_path58.default.join("supabase", "config.toml");
13228
+ const abs = import_node_path58.default.join(dir, relFile);
12676
13229
  if (!await exists2(abs)) return null;
12677
13230
  const raw = await import_node_fs26.promises.readFile(abs, "utf8");
12678
13231
  const config = (0, import_smol_toml4.parse)(raw);
@@ -12686,7 +13239,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12686
13239
  try {
12687
13240
  read = await readSupabaseConfig(service.dir);
12688
13241
  } catch (err) {
12689
- recordExtractionError("infra supabase", import_node_path57.default.relative(scanPath, service.dir), err);
13242
+ recordExtractionError("infra supabase", import_node_path58.default.relative(scanPath, service.dir), err);
12690
13243
  continue;
12691
13244
  }
12692
13245
  if (!read) continue;
@@ -12701,7 +13254,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12701
13254
  });
12702
13255
  }
12703
13256
  const anchorId = service.node.id;
12704
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
13257
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, import_node_path58.default.join(service.dir, relFile)));
12705
13258
  const add = (edgeType, kind, name) => {
12706
13259
  if (!name) return;
12707
13260
  const result = emitPlatformResourceEdge(
@@ -12717,10 +13270,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
12717
13270
  nodesAdded += result.nodesAdded;
12718
13271
  edgesAdded += result.edgesAdded;
12719
13272
  };
12720
- add(import_types46.EdgeType.RUNS_ON, "supabase", "supabase");
12721
- for (const fn of Object.keys(config.functions ?? {})) add(import_types46.EdgeType.DEPENDS_ON, "supabase-function", fn);
12722
- if (config.storage) add(import_types46.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
12723
- if (config.auth) add(import_types46.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
13273
+ add(import_types47.EdgeType.RUNS_ON, "supabase", "supabase");
13274
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types47.EdgeType.DEPENDS_ON, "supabase-function", fn);
13275
+ if (config.storage) add(import_types47.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
13276
+ if (config.auth) add(import_types47.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12724
13277
  }
12725
13278
  return { nodesAdded, edgesAdded };
12726
13279
  }
@@ -12743,14 +13296,14 @@ async function addInfra(graph, scanPath, services) {
12743
13296
 
12744
13297
  // src/extract/zod-shapes.ts
12745
13298
  init_cjs_shims();
12746
- var import_node_path58 = __toESM(require("path"), 1);
12747
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13299
+ var import_node_path59 = __toESM(require("path"), 1);
13300
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
12748
13301
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
12749
- var import_types47 = require("@neat.is/types");
13302
+ var import_types48 = require("@neat.is/types");
12750
13303
  var ZOD_IMPORT_RE = /\bzod\b/;
12751
13304
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12752
13305
  function parserForExt3(ext) {
12753
- const p = new import_tree_sitter15.default();
13306
+ const p = new import_tree_sitter16.default();
12754
13307
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12755
13308
  return p;
12756
13309
  }
@@ -12838,7 +13391,7 @@ function topLevelSchemas(root) {
12838
13391
  }
12839
13392
  function zodShapesFromFile(file, serviceDir) {
12840
13393
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12841
- const tree = parseSource3(parserForExt3(import_node_path58.default.extname(file.path)), file.content);
13394
+ const tree = parseSource3(parserForExt3(import_node_path59.default.extname(file.path)), file.content);
12842
13395
  const out = [];
12843
13396
  const seen = /* @__PURE__ */ new Set();
12844
13397
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -12852,11 +13405,11 @@ function zodShapesFromFile(file, serviceDir) {
12852
13405
  seen.add(name);
12853
13406
  const line = call.startPosition.row + 1;
12854
13407
  out.push({
12855
- infraId: (0, import_types47.infraId)("zod-schema", name),
13408
+ infraId: (0, import_types48.infraId)("zod-schema", name),
12856
13409
  name,
12857
13410
  fields,
12858
13411
  evidence: {
12859
- file: import_node_path58.default.relative(serviceDir, file.path),
13412
+ file: import_node_path59.default.relative(serviceDir, file.path),
12860
13413
  line,
12861
13414
  snippet: snippet(file.content, line)
12862
13415
  }
@@ -12887,7 +13440,7 @@ async function addZodShapes(graph, services) {
12887
13440
  if (!graph.hasNode(shape.infraId)) {
12888
13441
  const node = {
12889
13442
  id: shape.infraId,
12890
- type: import_types47.NodeType.InfraNode,
13443
+ type: import_types48.NodeType.InfraNode,
12891
13444
  name: shape.name,
12892
13445
  provider: "self",
12893
13446
  kind: "zod-schema"
@@ -12897,14 +13450,14 @@ async function addZodShapes(graph, services) {
12897
13450
  }
12898
13451
  if (shape.fields.length > 0) {
12899
13452
  const node = graph.getNodeAttributes(shape.infraId);
12900
- if (node.type === import_types47.NodeType.InfraNode) {
13453
+ if (node.type === import_types48.NodeType.InfraNode) {
12901
13454
  graph.replaceNodeAttributes(shape.infraId, {
12902
13455
  ...node,
12903
13456
  columns: foldColumns(
12904
13457
  node.columns,
12905
13458
  shape.fields,
12906
- import_types47.Provenance.EXTRACTED,
12907
- (0, import_types47.confidenceForExtracted)("structural")
13459
+ import_types48.Provenance.EXTRACTED,
13460
+ (0, import_types48.confidenceForExtracted)("structural")
12908
13461
  )
12909
13462
  });
12910
13463
  }
@@ -12918,15 +13471,15 @@ async function addZodShapes(graph, services) {
12918
13471
  );
12919
13472
  nodesAdded += n;
12920
13473
  edgesAdded += e;
12921
- const edgeId = (0, import_types47.extractedEdgeId)(fileNodeId, shape.infraId, import_types47.EdgeType.CONTAINS);
13474
+ const edgeId = (0, import_types48.extractedEdgeId)(fileNodeId, shape.infraId, import_types48.EdgeType.CONTAINS);
12922
13475
  if (!graph.hasEdge(edgeId)) {
12923
13476
  const edge = {
12924
13477
  id: edgeId,
12925
13478
  source: fileNodeId,
12926
13479
  target: shape.infraId,
12927
- type: import_types47.EdgeType.CONTAINS,
12928
- provenance: import_types47.Provenance.EXTRACTED,
12929
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
13480
+ type: import_types48.EdgeType.CONTAINS,
13481
+ provenance: import_types48.Provenance.EXTRACTED,
13482
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
12930
13483
  evidence: shape.evidence
12931
13484
  };
12932
13485
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -12940,7 +13493,7 @@ async function addZodShapes(graph, services) {
12940
13493
 
12941
13494
  // src/extract/firestore-rules.ts
12942
13495
  init_cjs_shims();
12943
- var import_types48 = require("@neat.is/types");
13496
+ var import_types49 = require("@neat.is/types");
12944
13497
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
12945
13498
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
12946
13499
  function stripComments(src) {
@@ -13080,7 +13633,7 @@ async function addFirestoreRules(graph, services) {
13080
13633
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
13081
13634
  graph.forEachNode((id, attrs) => {
13082
13635
  const node = attrs;
13083
- if (node.type !== import_types48.NodeType.InfraNode) return;
13636
+ if (node.type !== import_types49.NodeType.InfraNode) return;
13084
13637
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
13085
13638
  const fields = guards.get(collectionKeyFromName(node.name));
13086
13639
  if (!fields || fields.size === 0) return;
@@ -13093,17 +13646,17 @@ async function addFirestoreRules(graph, services) {
13093
13646
  }
13094
13647
 
13095
13648
  // src/extract/index.ts
13096
- var import_node_path60 = __toESM(require("path"), 1);
13649
+ var import_node_path61 = __toESM(require("path"), 1);
13097
13650
 
13098
13651
  // src/extract/retire.ts
13099
13652
  init_cjs_shims();
13100
13653
  var import_node_fs27 = require("fs");
13101
- var import_node_path59 = __toESM(require("path"), 1);
13102
- var import_types49 = require("@neat.is/types");
13654
+ var import_node_path60 = __toESM(require("path"), 1);
13655
+ var import_types50 = require("@neat.is/types");
13103
13656
  function dropOrphanedFileNodes(graph) {
13104
13657
  const orphans = [];
13105
13658
  graph.forEachNode((id, attrs) => {
13106
- if (attrs.type !== import_types49.NodeType.FileNode) return;
13659
+ if (attrs.type !== import_types50.NodeType.FileNode) return;
13107
13660
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
13108
13661
  orphans.push(id);
13109
13662
  }
@@ -13116,14 +13669,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
13116
13669
  const bases = [scanPath, ...serviceDirs];
13117
13670
  graph.forEachEdge((id, attrs) => {
13118
13671
  const edge = attrs;
13119
- if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
13672
+ if (edge.provenance !== import_types50.Provenance.EXTRACTED) return;
13120
13673
  const evidenceFile = edge.evidence?.file;
13121
13674
  if (!evidenceFile) return;
13122
- if (import_node_path59.default.isAbsolute(evidenceFile)) {
13675
+ if (import_node_path60.default.isAbsolute(evidenceFile)) {
13123
13676
  if (!(0, import_node_fs27.existsSync)(evidenceFile)) toDrop.push(id);
13124
13677
  return;
13125
13678
  }
13126
- const found = bases.some((base) => (0, import_node_fs27.existsSync)(import_node_path59.default.join(base, evidenceFile)));
13679
+ const found = bases.some((base) => (0, import_node_fs27.existsSync)(import_node_path60.default.join(base, evidenceFile)));
13127
13680
  if (!found) toDrop.push(id);
13128
13681
  });
13129
13682
  for (const id of toDrop) graph.dropEdge(id);
@@ -13180,7 +13733,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
13180
13733
  }
13181
13734
  const droppedEntries = drainDroppedExtracted();
13182
13735
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
13183
- const rejectedPath = import_node_path60.default.join(import_node_path60.default.dirname(opts.errorsPath), "rejected.ndjson");
13736
+ const rejectedPath = import_node_path61.default.join(import_node_path61.default.dirname(opts.errorsPath), "rejected.ndjson");
13184
13737
  try {
13185
13738
  await writeRejectedExtracted(droppedEntries, rejectedPath);
13186
13739
  } catch (err) {
@@ -13292,8 +13845,8 @@ function canonicalJson(value) {
13292
13845
  // src/persist.ts
13293
13846
  init_cjs_shims();
13294
13847
  var import_node_fs29 = require("fs");
13295
- var import_node_path61 = __toESM(require("path"), 1);
13296
- var import_types50 = require("@neat.is/types");
13848
+ var import_node_path62 = __toESM(require("path"), 1);
13849
+ var import_types51 = require("@neat.is/types");
13297
13850
  var SCHEMA_VERSION = 7;
13298
13851
  function migrateV1ToV2(payload) {
13299
13852
  const nodes = payload.graph.nodes;
@@ -13317,7 +13870,7 @@ function migrateV5ToV6(payload) {
13317
13870
  if (Array.isArray(nodes)) {
13318
13871
  for (const node of nodes) {
13319
13872
  const attrs = node.attributes;
13320
- if (!attrs || attrs.type !== import_types50.NodeType.InfraNode) continue;
13873
+ if (!attrs || attrs.type !== import_types51.NodeType.InfraNode) continue;
13321
13874
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
13322
13875
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
13323
13876
  }
@@ -13333,12 +13886,12 @@ function migrateV2ToV3(payload) {
13333
13886
  for (const edge of edges) {
13334
13887
  const attrs = edge.attributes;
13335
13888
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
13336
- attrs.provenance = import_types50.Provenance.OBSERVED;
13889
+ attrs.provenance = import_types51.Provenance.OBSERVED;
13337
13890
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
13338
13891
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
13339
13892
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
13340
13893
  if (type && source && target) {
13341
- const newId = (0, import_types50.observedEdgeId)(source, target, type);
13894
+ const newId = (0, import_types51.observedEdgeId)(source, target, type);
13342
13895
  attrs.id = newId;
13343
13896
  if (edge.key) edge.key = newId;
13344
13897
  }
@@ -13347,7 +13900,7 @@ function migrateV2ToV3(payload) {
13347
13900
  return { ...payload, schemaVersion: 3 };
13348
13901
  }
13349
13902
  async function ensureDir(filePath) {
13350
- await import_node_fs29.promises.mkdir(import_node_path61.default.dirname(filePath), { recursive: true });
13903
+ await import_node_fs29.promises.mkdir(import_node_path62.default.dirname(filePath), { recursive: true });
13351
13904
  }
13352
13905
  async function saveGraphToDisk(graph, outPath) {
13353
13906
  await ensureDir(outPath);
@@ -13437,23 +13990,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
13437
13990
 
13438
13991
  // src/projects.ts
13439
13992
  init_cjs_shims();
13440
- var import_node_path62 = __toESM(require("path"), 1);
13993
+ var import_node_path63 = __toESM(require("path"), 1);
13441
13994
  function pathsForProject(project, baseDir) {
13442
13995
  if (project === DEFAULT_PROJECT) {
13443
13996
  return {
13444
- snapshotPath: import_node_path62.default.join(baseDir, "graph.json"),
13445
- errorsPath: import_node_path62.default.join(baseDir, "errors.ndjson"),
13446
- staleEventsPath: import_node_path62.default.join(baseDir, "stale-events.ndjson"),
13447
- embeddingsCachePath: import_node_path62.default.join(baseDir, "embeddings.json"),
13448
- policyViolationsPath: import_node_path62.default.join(baseDir, "policy-violations.ndjson")
13997
+ snapshotPath: import_node_path63.default.join(baseDir, "graph.json"),
13998
+ errorsPath: import_node_path63.default.join(baseDir, "errors.ndjson"),
13999
+ staleEventsPath: import_node_path63.default.join(baseDir, "stale-events.ndjson"),
14000
+ embeddingsCachePath: import_node_path63.default.join(baseDir, "embeddings.json"),
14001
+ policyViolationsPath: import_node_path63.default.join(baseDir, "policy-violations.ndjson")
13449
14002
  };
13450
14003
  }
13451
14004
  return {
13452
- snapshotPath: import_node_path62.default.join(baseDir, `${project}.json`),
13453
- errorsPath: import_node_path62.default.join(baseDir, `errors.${project}.ndjson`),
13454
- staleEventsPath: import_node_path62.default.join(baseDir, `stale-events.${project}.ndjson`),
13455
- embeddingsCachePath: import_node_path62.default.join(baseDir, `embeddings.${project}.json`),
13456
- policyViolationsPath: import_node_path62.default.join(baseDir, `policy-violations.${project}.ndjson`)
14005
+ snapshotPath: import_node_path63.default.join(baseDir, `${project}.json`),
14006
+ errorsPath: import_node_path63.default.join(baseDir, `errors.${project}.ndjson`),
14007
+ staleEventsPath: import_node_path63.default.join(baseDir, `stale-events.${project}.ndjson`),
14008
+ embeddingsCachePath: import_node_path63.default.join(baseDir, `embeddings.${project}.json`),
14009
+ policyViolationsPath: import_node_path63.default.join(baseDir, `policy-violations.${project}.ndjson`)
13457
14010
  };
13458
14011
  }
13459
14012
  var Projects = class {
@@ -13495,18 +14048,18 @@ function parseExtraProjects(raw) {
13495
14048
  init_cjs_shims();
13496
14049
  var import_node_fs30 = require("fs");
13497
14050
  var import_node_os3 = __toESM(require("os"), 1);
13498
- var import_node_path63 = __toESM(require("path"), 1);
13499
- var import_types51 = require("@neat.is/types");
14051
+ var import_node_path64 = __toESM(require("path"), 1);
14052
+ var import_types52 = require("@neat.is/types");
13500
14053
  function neatHome() {
13501
14054
  const override = process.env.NEAT_HOME;
13502
- if (override && override.length > 0) return import_node_path63.default.resolve(override);
13503
- return import_node_path63.default.join(import_node_os3.default.homedir(), ".neat");
14055
+ if (override && override.length > 0) return import_node_path64.default.resolve(override);
14056
+ return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
13504
14057
  }
13505
14058
  function registryPath() {
13506
- return import_node_path63.default.join(neatHome(), "projects.json");
14059
+ return import_node_path64.default.join(neatHome(), "projects.json");
13507
14060
  }
13508
14061
  function daemonsDir() {
13509
- return import_node_path63.default.join(neatHome(), "daemons");
14062
+ return import_node_path64.default.join(neatHome(), "daemons");
13510
14063
  }
13511
14064
  function isFiniteInt(v) {
13512
14065
  return typeof v === "number" && Number.isFinite(v);
@@ -13547,7 +14100,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
13547
14100
  const out = [];
13548
14101
  for (const name of names) {
13549
14102
  if (!name.endsWith(".json")) continue;
13550
- const file = import_node_path63.default.join(dir, name);
14103
+ const file = import_node_path64.default.join(dir, name);
13551
14104
  let raw;
13552
14105
  try {
13553
14106
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -13586,7 +14139,7 @@ async function readRegistry() {
13586
14139
  throw err;
13587
14140
  }
13588
14141
  const parsed = JSON.parse(raw);
13589
- return import_types51.RegistryFileSchema.parse(parsed);
14142
+ return import_types52.RegistryFileSchema.parse(parsed);
13590
14143
  }
13591
14144
  async function getProject(name) {
13592
14145
  const reg = await readRegistry();
@@ -13661,7 +14214,7 @@ init_auth();
13661
14214
  // src/connectors-config.ts
13662
14215
  init_cjs_shims();
13663
14216
  var import_node_os4 = __toESM(require("os"), 1);
13664
- var import_node_path64 = __toESM(require("path"), 1);
14217
+ var import_node_path65 = __toESM(require("path"), 1);
13665
14218
  var import_node_fs31 = require("fs");
13666
14219
  var CONNECTORS_CONFIG_VERSION = 1;
13667
14220
  var EnvRefUnsetError = class extends Error {
@@ -13676,11 +14229,11 @@ var EnvRefUnsetError = class extends Error {
13676
14229
  };
13677
14230
  function neatHome2() {
13678
14231
  const override = process.env.NEAT_HOME;
13679
- if (override && override.length > 0) return import_node_path64.default.resolve(override);
13680
- return import_node_path64.default.join(import_node_os4.default.homedir(), ".neat");
14232
+ if (override && override.length > 0) return import_node_path65.default.resolve(override);
14233
+ return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
13681
14234
  }
13682
14235
  function connectorsConfigPath(home = neatHome2()) {
13683
- return import_node_path64.default.join(home, "connectors.json");
14236
+ return import_node_path65.default.join(home, "connectors.json");
13684
14237
  }
13685
14238
  var MODE_MASK_LOOSER_THAN_0600 = 63;
13686
14239
  async function warnIfModeLooserThan0600(file) {
@@ -13867,15 +14420,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
13867
14420
 
13868
14421
  // src/connectors/index.ts
13869
14422
  init_cjs_shims();
13870
- var import_types52 = require("@neat.is/types");
14423
+ var import_types53 = require("@neat.is/types");
13871
14424
  var NO_ENV = "unknown";
13872
14425
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
13873
14426
  if (!graph.hasNode(targetNodeId)) return void 0;
13874
14427
  const sites = [];
13875
14428
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
13876
14429
  const edge = graph.getEdgeAttributes(edgeId);
13877
- if (edge.provenance !== import_types52.Provenance.EXTRACTED) continue;
13878
- const parsed = (0, import_types52.parseFileId)(edge.source);
14430
+ if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
14431
+ const parsed = (0, import_types53.parseFileId)(edge.source);
13879
14432
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
13880
14433
  const site = { relPath: edge.evidence.file };
13881
14434
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -13886,7 +14439,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
13886
14439
  function routeCallSiteFor(graph, targetNodeId) {
13887
14440
  if (!graph.hasNode(targetNodeId)) return void 0;
13888
14441
  const attrs = graph.getNodeAttributes(targetNodeId);
13889
- if (attrs.type !== import_types52.NodeType.RouteNode || !attrs.path) return void 0;
14442
+ if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
13890
14443
  const site = { relPath: attrs.path };
13891
14444
  if (attrs.line !== void 0) site.line = attrs.line;
13892
14445
  return site;
@@ -14326,10 +14879,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
14326
14879
  // src/connectors/supabase/map.ts
14327
14880
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
14328
14881
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
14329
- function targetFromRestPath(path67) {
14330
- const rpcMatch = REST_RPC_PATH_RE.exec(path67);
14882
+ function targetFromRestPath(path68) {
14883
+ const rpcMatch = REST_RPC_PATH_RE.exec(path68);
14331
14884
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
14332
- const tableMatch = REST_TABLE_PATH_RE.exec(path67);
14885
+ const tableMatch = REST_TABLE_PATH_RE.exec(path68);
14333
14886
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
14334
14887
  return null;
14335
14888
  }
@@ -14440,23 +14993,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
14440
14993
 
14441
14994
  // src/connectors/supabase/resolve.ts
14442
14995
  init_cjs_shims();
14443
- var import_types54 = require("@neat.is/types");
14996
+ var import_types55 = require("@neat.is/types");
14444
14997
  function createSupabaseResolveTarget(graph, config) {
14445
14998
  return (signal, _ctx) => {
14446
14999
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
14447
15000
  return null;
14448
15001
  }
14449
- const subResourceId = (0, import_types54.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15002
+ const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
14450
15003
  if (graph.hasNode(subResourceId)) {
14451
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15004
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14452
15005
  }
14453
- const bareResourceId = (0, import_types54.infraId)(signal.targetKind, signal.targetName);
15006
+ const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
14454
15007
  if (graph.hasNode(bareResourceId)) {
14455
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15008
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14456
15009
  }
14457
- const projectLevelId = (0, import_types54.infraId)("supabase", config.nodeRef);
15010
+ const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
14458
15011
  if (graph.hasNode(projectLevelId)) {
14459
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15012
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14460
15013
  }
14461
15014
  return null;
14462
15015
  };
@@ -14549,7 +15102,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
14549
15102
 
14550
15103
  // src/connectors/railway/index.ts
14551
15104
  init_cjs_shims();
14552
- var import_types58 = require("@neat.is/types");
15105
+ var import_types59 = require("@neat.is/types");
14553
15106
 
14554
15107
  // src/connectors/railway/client.ts
14555
15108
  init_cjs_shims();
@@ -14700,7 +15253,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
14700
15253
  const out = [];
14701
15254
  graph.forEachNode((_id, attrs) => {
14702
15255
  const node = attrs;
14703
- if (node.type !== import_types58.NodeType.RouteNode) return;
15256
+ if (node.type !== import_types59.NodeType.RouteNode) return;
14704
15257
  const route = attrs;
14705
15258
  if (route.service !== serviceName) return;
14706
15259
  out.push({
@@ -14804,12 +15357,12 @@ function createRailwayResolveTarget(config) {
14804
15357
  const serviceName = config.serviceNameById[config.serviceId];
14805
15358
  if (!serviceName) return null;
14806
15359
  if (signal.targetKind === ROUTE_TARGET_KIND) {
14807
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types58.EdgeType.CALLS };
15360
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
14808
15361
  }
14809
15362
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
14810
15363
  const peerName = config.serviceNameById[signal.targetName];
14811
15364
  if (!peerName) return null;
14812
- return { targetNodeId: (0, import_types58.serviceId)(peerName), serviceName, edgeType: import_types58.EdgeType.CONNECTS_TO };
15365
+ return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
14813
15366
  }
14814
15367
  return null;
14815
15368
  };
@@ -14933,9 +15486,9 @@ function parseFirebaseTargetName(targetName) {
14933
15486
  const secondSep = rest.indexOf(FIELD_SEP);
14934
15487
  if (secondSep === -1) return null;
14935
15488
  const method = rest.slice(0, secondSep);
14936
- const path67 = rest.slice(secondSep + 1);
14937
- if (!resourceName || !method || !path67) return null;
14938
- return { resourceName, method, path: path67 };
15489
+ const path68 = rest.slice(secondSep + 1);
15490
+ if (!resourceName || !method || !path68) return null;
15491
+ return { resourceName, method, path: path68 };
14939
15492
  }
14940
15493
  function resourceNameFor(type, labels) {
14941
15494
  if (!labels) return null;
@@ -14973,14 +15526,14 @@ function mapLogEntryToSignal(entry) {
14973
15526
  if (!req) return null;
14974
15527
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
14975
15528
  const method = req.requestMethod.toUpperCase();
14976
- const path67 = pathFromRequestUrl(req.requestUrl);
14977
- if (path67 === null) return null;
15529
+ const path68 = pathFromRequestUrl(req.requestUrl);
15530
+ if (path68 === null) return null;
14978
15531
  const timestamp = entry.timestamp;
14979
15532
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
14980
15533
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
14981
15534
  return {
14982
15535
  targetKind: resourceType,
14983
- targetName: packFirebaseTargetName({ resourceName, method, path: path67 }),
15536
+ targetName: packFirebaseTargetName({ resourceName, method, path: path68 }),
14984
15537
  callCount: 1,
14985
15538
  errorCount: isError ? 1 : 0,
14986
15539
  lastObservedIso: timestamp
@@ -14997,7 +15550,7 @@ function mapLogEntriesToSignals(entries) {
14997
15550
 
14998
15551
  // src/connectors/firebase/resolve.ts
14999
15552
  init_cjs_shims();
15000
- var import_types59 = require("@neat.is/types");
15553
+ var import_types60 = require("@neat.is/types");
15001
15554
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
15002
15555
  switch (resourceType) {
15003
15556
  case "cloud_function":
@@ -15012,7 +15565,7 @@ function routeEntriesFor(graph, serviceName) {
15012
15565
  const entries = [];
15013
15566
  graph.forEachNode((_id, attrs) => {
15014
15567
  const node = attrs;
15015
- if (node.type !== import_types59.NodeType.RouteNode) return;
15568
+ if (node.type !== import_types60.NodeType.RouteNode) return;
15016
15569
  const route = attrs;
15017
15570
  if (route.service !== serviceName) return;
15018
15571
  entries.push({
@@ -15044,7 +15597,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
15044
15597
  return {
15045
15598
  targetNodeId: match.routeNodeId,
15046
15599
  serviceName,
15047
- edgeType: import_types59.EdgeType.CALLS
15600
+ edgeType: import_types60.EdgeType.CALLS
15048
15601
  };
15049
15602
  };
15050
15603
  }
@@ -15071,7 +15624,7 @@ init_cjs_shims();
15071
15624
 
15072
15625
  // src/connectors/cloudflare/connector.ts
15073
15626
  init_cjs_shims();
15074
- var import_types61 = require("@neat.is/types");
15627
+ var import_types62 = require("@neat.is/types");
15075
15628
 
15076
15629
  // src/connectors/cloudflare/client.ts
15077
15630
  init_cjs_shims();
@@ -15187,7 +15740,7 @@ function mapEventToSignal(event) {
15187
15740
  if (Number.isNaN(observedAt.getTime())) return null;
15188
15741
  const statusCode = metadata?.statusCode;
15189
15742
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
15190
- const path67 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15743
+ const path68 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15191
15744
  return {
15192
15745
  targetKind: CLOUDFLARE_TARGET_KIND,
15193
15746
  targetName: scriptName,
@@ -15195,7 +15748,7 @@ function mapEventToSignal(event) {
15195
15748
  errorCount: isError ? 1 : 0,
15196
15749
  lastObservedIso: observedAt.toISOString(),
15197
15750
  method,
15198
- ...path67 ? { path: path67 } : {},
15751
+ ...path68 ? { path: path68 } : {},
15199
15752
  ...typeof statusCode === "number" ? { statusCode } : {},
15200
15753
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
15201
15754
  };
@@ -15235,19 +15788,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
15235
15788
  graph.forEachNode((id, attrs) => {
15236
15789
  if (found) return;
15237
15790
  const a = attrs;
15238
- if (a.type === import_types61.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15791
+ if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15239
15792
  found = id;
15240
15793
  }
15241
15794
  });
15242
15795
  return found;
15243
15796
  }
15244
- function findMatchingRouteNode(graph, serviceName, method, path67) {
15245
- const normalizedPath = normalizePathTemplate(path67);
15797
+ function findMatchingRouteNode(graph, serviceName, method, path68) {
15798
+ const normalizedPath = normalizePathTemplate(path68);
15246
15799
  let found = null;
15247
15800
  graph.forEachNode((id, attrs) => {
15248
15801
  if (found) return;
15249
15802
  const a = attrs;
15250
- if (a.type !== import_types61.NodeType.RouteNode || a.service !== serviceName) return;
15803
+ if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
15251
15804
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
15252
15805
  const routeMethod = (a.method ?? "").toUpperCase();
15253
15806
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -15259,18 +15812,18 @@ function createCloudflareResolveTarget(config, graph) {
15259
15812
  return (signal) => {
15260
15813
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
15261
15814
  const scriptName = signal.targetName;
15262
- const { method, path: path67 } = signal;
15815
+ const { method, path: path68 } = signal;
15263
15816
  const resolveRouteGrain = (serviceName, wholeFileId) => {
15264
- if (!method || !path67) return wholeFileId;
15265
- return findMatchingRouteNode(graph, serviceName, method, path67) ?? wholeFileId;
15817
+ if (!method || !path68) return wholeFileId;
15818
+ return findMatchingRouteNode(graph, serviceName, method, path68) ?? wholeFileId;
15266
15819
  };
15267
15820
  const mapping = config.workers?.[scriptName];
15268
15821
  if (mapping) {
15269
- const wholeFileId = (0, import_types61.fileId)(mapping.service, mapping.entryFile);
15822
+ const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
15270
15823
  return {
15271
15824
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
15272
15825
  serviceName: mapping.service,
15273
- edgeType: import_types61.EdgeType.CALLS
15826
+ edgeType: import_types62.EdgeType.CALLS
15274
15827
  };
15275
15828
  }
15276
15829
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -15279,13 +15832,13 @@ function createCloudflareResolveTarget(config, graph) {
15279
15832
  return {
15280
15833
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
15281
15834
  serviceName: fileNode.service,
15282
- edgeType: import_types61.EdgeType.CALLS
15835
+ edgeType: import_types62.EdgeType.CALLS
15283
15836
  };
15284
15837
  }
15285
15838
  return {
15286
- targetNodeId: (0, import_types61.infraId)("cloudflare-worker", scriptName),
15839
+ targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
15287
15840
  serviceName: scriptName,
15288
- edgeType: import_types61.EdgeType.CALLS,
15841
+ edgeType: import_types62.EdgeType.CALLS,
15289
15842
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
15290
15843
  };
15291
15844
  };
@@ -15481,14 +16034,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
15481
16034
 
15482
16035
  // src/connectors/neon/resolve.ts
15483
16036
  init_cjs_shims();
15484
- var import_types65 = require("@neat.is/types");
16037
+ var import_types66 = require("@neat.is/types");
15485
16038
  function createNeonResolveTarget(config) {
15486
16039
  return (signal) => {
15487
16040
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
15488
16041
  return {
15489
- targetNodeId: (0, import_types65.infraId)("sql-table", signal.targetName),
16042
+ targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
15490
16043
  serviceName: config.serviceName,
15491
- edgeType: import_types65.EdgeType.CALLS,
16044
+ edgeType: import_types66.EdgeType.CALLS,
15492
16045
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
15493
16046
  };
15494
16047
  };
@@ -15614,9 +16167,9 @@ function parseCloudRunTargetName(targetName) {
15614
16167
  const secondSep = rest.indexOf(FIELD_SEP2);
15615
16168
  if (secondSep === -1) return null;
15616
16169
  const method = rest.slice(0, secondSep);
15617
- const path67 = rest.slice(secondSep + 1);
15618
- if (!serviceName || !method || !path67) return null;
15619
- return { serviceName, method, path: path67 };
16170
+ const path68 = rest.slice(secondSep + 1);
16171
+ if (!serviceName || !method || !path68) return null;
16172
+ return { serviceName, method, path: path68 };
15620
16173
  }
15621
16174
 
15622
16175
  // src/connectors/cloud-run/map.ts
@@ -15645,14 +16198,14 @@ function mapLogEntryToSignal2(entry) {
15645
16198
  if (!req) return null;
15646
16199
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
15647
16200
  const method = req.requestMethod.toUpperCase();
15648
- const path67 = pathFromRequestUrl2(req.requestUrl);
15649
- if (path67 === null) return null;
16201
+ const path68 = pathFromRequestUrl2(req.requestUrl);
16202
+ if (path68 === null) return null;
15650
16203
  const timestamp = entry.timestamp;
15651
16204
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15652
16205
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
15653
16206
  return {
15654
16207
  targetKind: CLOUD_RUN_TARGET_KIND,
15655
- targetName: packCloudRunTargetName({ serviceName, method, path: path67 }),
16208
+ targetName: packCloudRunTargetName({ serviceName, method, path: path68 }),
15656
16209
  callCount: 1,
15657
16210
  errorCount: isError ? 1 : 0,
15658
16211
  lastObservedIso: timestamp
@@ -15669,14 +16222,14 @@ function mapLogEntriesToSignals2(entries) {
15669
16222
 
15670
16223
  // src/connectors/cloud-run/resolve.ts
15671
16224
  init_cjs_shims();
15672
- var import_types69 = require("@neat.is/types");
16225
+ var import_types70 = require("@neat.is/types");
15673
16226
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
15674
16227
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
15675
16228
  let found = null;
15676
16229
  graph.forEachNode((_id, attrs) => {
15677
16230
  if (found) return;
15678
16231
  const node = attrs;
15679
- if (node.type !== import_types69.NodeType.RouteNode) return;
16232
+ if (node.type !== import_types70.NodeType.RouteNode) return;
15680
16233
  const route = attrs;
15681
16234
  if (route.service !== serviceName || !route.pathTemplate) return;
15682
16235
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -15691,23 +16244,23 @@ function createCloudRunResolveTarget(graph, config) {
15691
16244
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
15692
16245
  const identity = parseCloudRunTargetName(signal.targetName);
15693
16246
  if (!identity) return null;
15694
- const { serviceName: gcpServiceName, method, path: path67 } = identity;
16247
+ const { serviceName: gcpServiceName, method, path: path68 } = identity;
15695
16248
  const mappedService = config.serviceMap?.[gcpServiceName];
15696
16249
  if (mappedService) {
15697
16250
  const routeNodeId = findMatchingRouteNode2(
15698
16251
  graph,
15699
16252
  mappedService,
15700
16253
  method,
15701
- normalizePathTemplate(path67)
16254
+ normalizePathTemplate(path68)
15702
16255
  );
15703
16256
  if (routeNodeId) {
15704
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types69.EdgeType.CALLS };
16257
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
15705
16258
  }
15706
16259
  }
15707
16260
  return {
15708
- targetNodeId: (0, import_types69.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16261
+ targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
15709
16262
  serviceName: mappedService ?? gcpServiceName,
15710
- edgeType: import_types69.EdgeType.CALLS,
16263
+ edgeType: import_types70.EdgeType.CALLS,
15711
16264
  ensureInfraNode: {
15712
16265
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
15713
16266
  name: gcpServiceName,
@@ -15748,7 +16301,7 @@ function createCloudRunConnector(graph, config = {}) {
15748
16301
 
15749
16302
  // src/connectors/render/index.ts
15750
16303
  init_cjs_shims();
15751
- var import_types72 = require("@neat.is/types");
16304
+ var import_types73 = require("@neat.is/types");
15752
16305
 
15753
16306
  // src/connectors/render/types.ts
15754
16307
  init_cjs_shims();
@@ -15826,7 +16379,7 @@ function buildRenderRouteIndex(graph, serviceName) {
15826
16379
  const out = [];
15827
16380
  graph.forEachNode((_id, attrs) => {
15828
16381
  const node = attrs;
15829
- if (node.type !== import_types72.NodeType.RouteNode) return;
16382
+ if (node.type !== import_types73.NodeType.RouteNode) return;
15830
16383
  const route = attrs;
15831
16384
  if (route.service !== serviceName) return;
15832
16385
  out.push({
@@ -15911,7 +16464,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
15911
16464
  function createRenderResolveTarget(config) {
15912
16465
  return (signal) => {
15913
16466
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
15914
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types72.EdgeType.CALLS };
16467
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
15915
16468
  }
15916
16469
  return null;
15917
16470
  };
@@ -16049,21 +16602,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
16049
16602
 
16050
16603
  // src/connectors/planetscale/resolve.ts
16051
16604
  init_cjs_shims();
16052
- var import_types76 = require("@neat.is/types");
16605
+ var import_types77 = require("@neat.is/types");
16053
16606
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
16054
16607
  function createPlanetscaleResolveTarget(graph, config) {
16055
16608
  const databaseName = `${config.organization}/${config.database}`;
16056
16609
  return (signal, _ctx) => {
16057
16610
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16058
- const tableId = (0, import_types76.infraId)("sql-table", signal.targetName);
16611
+ const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
16059
16612
  if (graph.hasNode(tableId)) {
16060
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types76.EdgeType.CALLS };
16613
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
16061
16614
  }
16062
- const providerId = (0, import_types76.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16615
+ const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16063
16616
  return {
16064
16617
  targetNodeId: providerId,
16065
16618
  serviceName: config.serviceName,
16066
- edgeType: import_types76.EdgeType.CALLS,
16619
+ edgeType: import_types77.EdgeType.CALLS,
16067
16620
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
16068
16621
  };
16069
16622
  };
@@ -16691,11 +17244,11 @@ function registerRoutes(scope, ctx) {
16691
17244
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
16692
17245
  const parsed = [];
16693
17246
  for (const c of candidates) {
16694
- const r = import_types79.DivergenceTypeSchema.safeParse(c);
17247
+ const r = import_types80.DivergenceTypeSchema.safeParse(c);
16695
17248
  if (!r.success) {
16696
17249
  return reply.code(400).send({
16697
17250
  error: `unknown divergence type "${c}"`,
16698
- allowed: import_types79.DivergenceTypeSchema.options
17251
+ allowed: import_types80.DivergenceTypeSchema.options
16699
17252
  });
16700
17253
  }
16701
17254
  parsed.push(r.data);
@@ -17004,7 +17557,7 @@ function registerRoutes(scope, ctx) {
17004
17557
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
17005
17558
  let violations = await log.readAll();
17006
17559
  if (req.query.severity) {
17007
- const sev = import_types79.PolicySeveritySchema.safeParse(req.query.severity);
17560
+ const sev = import_types80.PolicySeveritySchema.safeParse(req.query.severity);
17008
17561
  if (!sev.success) {
17009
17562
  return reply.code(400).send({
17010
17563
  error: "invalid severity",
@@ -17043,7 +17596,7 @@ function registerRoutes(scope, ctx) {
17043
17596
  scope.post("/policies/check", async (req, reply) => {
17044
17597
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
17045
17598
  if (!proj) return;
17046
- const parsed = import_types79.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17599
+ const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17047
17600
  if (!parsed.success) {
17048
17601
  return reply.code(400).send({
17049
17602
  error: "invalid /policies/check body",
@@ -17365,7 +17918,7 @@ init_otel_grpc();
17365
17918
  // src/search.ts
17366
17919
  init_cjs_shims();
17367
17920
  var import_node_fs32 = require("fs");
17368
- var import_node_path65 = __toESM(require("path"), 1);
17921
+ var import_node_path66 = __toESM(require("path"), 1);
17369
17922
  var import_node_crypto4 = require("crypto");
17370
17923
  var DEFAULT_LIMIT = 10;
17371
17924
  var NOMIC_DIM = 768;
@@ -17528,7 +18081,7 @@ async function readCache(cachePath) {
17528
18081
  }
17529
18082
  }
17530
18083
  async function writeCache(cachePath, cache) {
17531
- await import_node_fs32.promises.mkdir(import_node_path65.default.dirname(cachePath), { recursive: true });
18084
+ await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(cachePath), { recursive: true });
17532
18085
  await import_node_fs32.promises.writeFile(cachePath, JSON.stringify(cache));
17533
18086
  }
17534
18087
  var VectorIndex = class {
@@ -17709,14 +18262,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
17709
18262
  async function main() {
17710
18263
  const baseDirEnv = process.env.NEAT_OUT_DIR;
17711
18264
  const legacyOutPath = process.env.NEAT_OUT_PATH;
17712
- const baseDir = baseDirEnv ? import_node_path66.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path66.default.resolve(import_node_path66.default.dirname(legacyOutPath)) : import_node_path66.default.resolve("./neat-out");
17713
- const defaultScanPath = import_node_path66.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
18265
+ const baseDir = baseDirEnv ? import_node_path67.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path67.default.resolve(import_node_path67.default.dirname(legacyOutPath)) : import_node_path67.default.resolve("./neat-out");
18266
+ const defaultScanPath = import_node_path67.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
17714
18267
  const registry = new Projects();
17715
18268
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
17716
18269
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
17717
18270
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
17718
18271
  const projectScan = process.env[envKey];
17719
- await bootProject(registry, name, projectScan ? import_node_path66.default.resolve(projectScan) : void 0, baseDir);
18272
+ await bootProject(registry, name, projectScan ? import_node_path67.default.resolve(projectScan) : void 0, baseDir);
17720
18273
  }
17721
18274
  const host = process.env.HOST ?? "0.0.0.0";
17722
18275
  const port = Number(process.env.PORT ?? 8080);