@neat.is/core 0.7.6 → 0.7.8

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) {
@@ -2793,8 +2796,9 @@ var ROUTER_METHODS = /* @__PURE__ */ new Set([
2793
2796
  "all"
2794
2797
  ]);
2795
2798
  var NEXT_APP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2799
+ var NET_HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
2796
2800
  var JS_ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"]);
2797
- function ginRoutesFromSource(source, parser) {
2801
+ function goRouterRoutesFromSource(source, parser, framework) {
2798
2802
  const tree = parseSource2(parser, source);
2799
2803
  const prefixes = /* @__PURE__ */ new Map();
2800
2804
  const out = [];
@@ -2804,10 +2808,12 @@ function ginRoutesFromSource(source, parser) {
2804
2808
  const value = node.childForFieldName("right")?.namedChild(0) ?? node.childForFieldName("value");
2805
2809
  if (name && value?.type === "call_expression") {
2806
2810
  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));
2811
+ if (fn2?.childForFieldName("field")?.text === "Group") {
2812
+ const leaf2 = goStringLiteral(value.childForFieldName("arguments")?.namedChild(0));
2813
+ if (leaf2 !== null) {
2814
+ const parent = fn2.childForFieldName("operand")?.text ?? "";
2815
+ prefixes.set(name, (prefixes.get(parent) ?? "") + leaf2);
2816
+ }
2811
2817
  }
2812
2818
  }
2813
2819
  return;
@@ -2818,18 +2824,127 @@ function ginRoutesFromSource(source, parser) {
2818
2824
  const method = fn.childForFieldName("field")?.text?.toUpperCase();
2819
2825
  if (!method || !ROUTER_METHODS.has(method.toLowerCase())) return;
2820
2826
  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);
2827
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
2828
+ if (leaf === null) return;
2824
2829
  out.push({
2825
- method: method === "ALL" ? "ALL" : method,
2830
+ method,
2826
2831
  pathTemplate: canonicalizeTemplate((prefixes.get(receiver) ?? "") + leaf),
2827
2832
  line: node.startPosition.row + 1,
2828
- framework: "gin"
2833
+ framework
2829
2834
  });
2830
2835
  });
2831
2836
  return out;
2832
2837
  }
2838
+ function goStringLiteral(node) {
2839
+ if (node?.type === "interpreted_string_literal" || node?.type === "raw_string_literal") {
2840
+ return node.text.slice(1, -1);
2841
+ }
2842
+ return null;
2843
+ }
2844
+ function ginRoutesFromSource(source, parser) {
2845
+ return goRouterRoutesFromSource(source, parser, "gin");
2846
+ }
2847
+ function echoRoutesFromSource(source, parser) {
2848
+ return goRouterRoutesFromSource(source, parser, "echo");
2849
+ }
2850
+ function fiberRoutesFromSource(source, parser) {
2851
+ return goRouterRoutesFromSource(source, parser, "fiber");
2852
+ }
2853
+ function chiRoutesFromSource(source, parser) {
2854
+ const tree = parseSource2(parser, source);
2855
+ const out = [];
2856
+ chiWalk(tree.rootNode, "", out);
2857
+ return out;
2858
+ }
2859
+ function stripChiRegex(path68) {
2860
+ return path68.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2861
+ }
2862
+ function chiWalk(node, prefix, out) {
2863
+ for (let i = 0; i < node.namedChildCount; i++) {
2864
+ const child = node.namedChild(i);
2865
+ if (child) chiHandle(child, prefix, out);
2866
+ }
2867
+ }
2868
+ function chiHandle(node, prefix, out) {
2869
+ if (node.type === "call_expression") {
2870
+ const fn = node.childForFieldName("function");
2871
+ if (fn?.type === "selector_expression") {
2872
+ const field = fn.childForFieldName("field")?.text;
2873
+ const args = node.childForFieldName("arguments");
2874
+ if (field === "Route") {
2875
+ const leaf = goStringLiteral(args?.namedChild(0));
2876
+ const closure = args?.namedChild(1);
2877
+ if (leaf !== null && closure?.type === "func_literal") {
2878
+ const body = closure.childForFieldName("body");
2879
+ if (body) chiWalk(body, prefix + leaf, out);
2880
+ }
2881
+ return;
2882
+ }
2883
+ if (field === "Group") {
2884
+ const closure = args?.namedChild(0);
2885
+ if (closure?.type === "func_literal") {
2886
+ const body = closure.childForFieldName("body");
2887
+ if (body) chiWalk(body, prefix, out);
2888
+ }
2889
+ return;
2890
+ }
2891
+ if (field === "Mount") {
2892
+ return;
2893
+ }
2894
+ if (field && ROUTER_METHODS.has(field.toLowerCase())) {
2895
+ const leaf = goStringLiteral(args?.namedChild(0));
2896
+ if (leaf !== null) {
2897
+ out.push({
2898
+ method: field.toUpperCase(),
2899
+ pathTemplate: canonicalizeTemplate(stripChiRegex(prefix + leaf)),
2900
+ line: node.startPosition.row + 1,
2901
+ framework: "chi"
2902
+ });
2903
+ }
2904
+ return;
2905
+ }
2906
+ }
2907
+ }
2908
+ chiWalk(node, prefix, out);
2909
+ }
2910
+ function netHttpRoutesFromSource(source, parser) {
2911
+ const tree = parseSource2(parser, source);
2912
+ if (!goImportsNetHttp(tree.rootNode)) return [];
2913
+ const out = [];
2914
+ walk(tree.rootNode, (node) => {
2915
+ if (node.type !== "call_expression") return;
2916
+ const fn = node.childForFieldName("function");
2917
+ if (fn?.type !== "selector_expression") return;
2918
+ const field = fn.childForFieldName("field")?.text;
2919
+ if (field !== "HandleFunc" && field !== "Handle") return;
2920
+ const leaf = goStringLiteral(node.childForFieldName("arguments")?.namedChild(0));
2921
+ if (leaf === null) return;
2922
+ const sp = leaf.indexOf(" ");
2923
+ if (sp < 0) return;
2924
+ const method = leaf.slice(0, sp);
2925
+ const rest = leaf.slice(sp + 1);
2926
+ if (!NET_HTTP_METHODS.has(method)) return;
2927
+ if (!rest.startsWith("/")) return;
2928
+ out.push({
2929
+ method,
2930
+ pathTemplate: canonicalizeTemplate(rest),
2931
+ line: node.startPosition.row + 1,
2932
+ framework: "net/http"
2933
+ });
2934
+ });
2935
+ return out;
2936
+ }
2937
+ function goImportsNetHttp(root) {
2938
+ let found = false;
2939
+ walk(root, (node) => {
2940
+ if (found || node.type !== "import_spec") return;
2941
+ for (let i = 0; i < node.namedChildCount; i++) {
2942
+ const child = node.namedChild(i);
2943
+ if (goStringLiteral(child) === "net/http") found = true;
2944
+ }
2945
+ });
2946
+ return found;
2947
+ }
2833
2948
  var FASTAPI_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head", "trace"]);
2834
2949
  var NESTJS_METHODS = /* @__PURE__ */ new Map([
2835
2950
  ["Get", "GET"],
@@ -3414,9 +3529,9 @@ function rubyRocketRoute(args) {
3414
3529
  if (!pair || pair.type !== "pair") continue;
3415
3530
  const k = pair.childForFieldName("key");
3416
3531
  if (k?.type !== "string") continue;
3417
- const path67 = rubyLiteral(k);
3418
- if (path67 === null) continue;
3419
- return { path: path67, target: rubyLiteral(pair.childForFieldName("value")) };
3532
+ const path68 = rubyLiteral(k);
3533
+ if (path68 === null) continue;
3534
+ return { path: path68, target: rubyLiteral(pair.childForFieldName("value")) };
3420
3535
  }
3421
3536
  return null;
3422
3537
  }
@@ -4093,9 +4208,13 @@ async function addRoutes(graph, services) {
4093
4208
  const hasFlask = deps["flask"] !== void 0;
4094
4209
  const hasDjango = deps["django"] !== void 0;
4095
4210
  const hasGin = deps["github.com/gin-gonic/gin"] !== void 0;
4211
+ const hasEcho = deps["github.com/labstack/echo/v4"] !== void 0 || deps["github.com/labstack/echo"] !== void 0;
4212
+ const hasFiber = deps["github.com/gofiber/fiber/v2"] !== void 0 || deps["github.com/gofiber/fiber/v3"] !== void 0;
4213
+ const hasChi = deps["github.com/go-chi/chi/v5"] !== void 0 || deps["github.com/go-chi/chi"] !== void 0;
4214
+ const isGoService = service.node.language === "go";
4096
4215
  const hasRails = deps["rails"] !== void 0;
4097
4216
  const hasLaravel = deps["laravel/framework"] !== void 0;
4098
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasRails && !hasLaravel)
4217
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
4099
4218
  continue;
4100
4219
  const files = await loadSourceFiles(service.dir);
4101
4220
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4119,7 +4238,12 @@ async function addRoutes(graph, services) {
4119
4238
  } else if (isRb) {
4120
4239
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4121
4240
  } else if (isGo) {
4122
- routes = hasGin ? ginRoutesFromSource(file.content, goParser) : [];
4241
+ if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4242
+ else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
4243
+ else if (hasFiber) routes = fiberRoutesFromSource(file.content, goParser);
4244
+ else if (hasChi) routes = chiRoutesFromSource(file.content, goParser);
4245
+ else routes = [];
4246
+ routes = routes.concat(netHttpRoutesFromSource(file.content, goParser));
4123
4247
  } else if (isPy) {
4124
4248
  routes = hasFastapi || hasFlask ? pythonRoutesFromSource(file.content, pyParser, hasFastapi ? "fastapi" : "flask") : [];
4125
4249
  if (hasDjango) routes = routes.concat(djangoRoutesFromSource(file.content, pyParser));
@@ -5669,19 +5793,19 @@ function confidenceFromMix(edges, now = Date.now()) {
5669
5793
  function longestIncomingWalk(graph, start, maxDepth) {
5670
5794
  let best = { path: [start], edges: [] };
5671
5795
  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] };
5796
+ function step(node, path68, edges) {
5797
+ if (path68.length > best.path.length) {
5798
+ best = { path: [...path68], edges: [...edges] };
5675
5799
  }
5676
- if (path67.length - 1 >= maxDepth) return;
5800
+ if (path68.length - 1 >= maxDepth) return;
5677
5801
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
5678
5802
  for (const [srcId, edge] of incoming) {
5679
5803
  if (visited.has(srcId)) continue;
5680
5804
  visited.add(srcId);
5681
- path67.push(srcId);
5805
+ path68.push(srcId);
5682
5806
  edges.push(edge);
5683
- step(srcId, path67, edges);
5684
- path67.pop();
5807
+ step(srcId, path68, edges);
5808
+ path68.pop();
5685
5809
  edges.pop();
5686
5810
  visited.delete(srcId);
5687
5811
  }
@@ -5689,11 +5813,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
5689
5813
  step(start, [start], []);
5690
5814
  return best;
5691
5815
  }
5692
- function databaseRootCauseShape(graph, origin, walk8) {
5816
+ function databaseRootCauseShape(graph, origin, walk9) {
5693
5817
  const targetDb = origin;
5694
5818
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
5695
5819
  if (candidatePairs.length === 0) return null;
5696
- for (const id of walk8.path) {
5820
+ for (const id of walk9.path) {
5697
5821
  const owner = resolveOwningService(graph, id);
5698
5822
  if (!owner) continue;
5699
5823
  const { id: serviceId9, svc } = owner;
@@ -5720,8 +5844,8 @@ function databaseRootCauseShape(graph, origin, walk8) {
5720
5844
  }
5721
5845
  return null;
5722
5846
  }
5723
- function serviceRootCauseShape(graph, _origin, walk8) {
5724
- for (const id of walk8.path) {
5847
+ function serviceRootCauseShape(graph, _origin, walk9) {
5848
+ for (const id of walk9.path) {
5725
5849
  const owner = resolveOwningService(graph, id);
5726
5850
  if (!owner) continue;
5727
5851
  const { id: serviceId9, svc } = owner;
@@ -5757,15 +5881,15 @@ function serviceRootCauseShape(graph, _origin, walk8) {
5757
5881
  }
5758
5882
  return null;
5759
5883
  }
5760
- function fileRootCauseShape(graph, origin, walk8) {
5884
+ function fileRootCauseShape(graph, origin, walk9) {
5761
5885
  const owner = resolveOwningService(graph, origin.id);
5762
5886
  if (!owner) return null;
5763
- return serviceRootCauseShape(graph, owner.svc, walk8);
5887
+ return serviceRootCauseShape(graph, owner.svc, walk9);
5764
5888
  }
5765
- function symbolRootCauseShape(graph, origin, walk8) {
5889
+ function symbolRootCauseShape(graph, origin, walk9) {
5766
5890
  const owner = resolveOwningService(graph, origin.id);
5767
5891
  if (!owner) return null;
5768
- return serviceRootCauseShape(graph, owner.svc, walk8);
5892
+ return serviceRootCauseShape(graph, owner.svc, walk9);
5769
5893
  }
5770
5894
  var rootCauseShapes = {
5771
5895
  [import_types8.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -5778,16 +5902,16 @@ function getRootCause(graph, errorNodeId, errorEvent, incidents) {
5778
5902
  const origin = graph.getNodeAttributes(errorNodeId);
5779
5903
  const shape = rootCauseShapes[origin.type];
5780
5904
  if (shape) {
5781
- const walk8 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
5782
- const match = shape(graph, origin, walk8);
5905
+ const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
5906
+ const match = shape(graph, origin, walk9);
5783
5907
  if (match) {
5784
5908
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
5785
5909
  return import_types8.RootCauseResultSchema.parse({
5786
5910
  rootCauseNode: match.rootCauseNode,
5787
5911
  rootCauseReason: reason,
5788
- traversalPath: walk8.path,
5789
- edgeProvenances: walk8.edges.map((e) => e.provenance),
5790
- confidence: confidenceFromMix(walk8.edges),
5912
+ traversalPath: walk9.path,
5913
+ edgeProvenances: walk9.edges.map((e) => e.provenance),
5914
+ confidence: confidenceFromMix(walk9.edges),
5791
5915
  fixRecommendation: match.fixRecommendation
5792
5916
  });
5793
5917
  }
@@ -5888,26 +6012,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
5888
6012
  return best;
5889
6013
  }
5890
6014
  function followFailingCallChain(graph, originServiceId, maxDepth) {
5891
- const path67 = [originServiceId];
6015
+ const path68 = [originServiceId];
5892
6016
  const edges = [];
5893
6017
  const visited = /* @__PURE__ */ new Set([originServiceId]);
5894
6018
  let current = originServiceId;
5895
6019
  for (let depth = 0; depth < maxDepth; depth++) {
5896
6020
  const hop = dominantFailingCall(graph, current, visited);
5897
6021
  if (!hop) break;
5898
- path67.push(hop.nextService);
6022
+ path68.push(hop.nextService);
5899
6023
  edges.push(hop.edge);
5900
6024
  visited.add(hop.nextService);
5901
6025
  current = hop.nextService;
5902
6026
  }
5903
6027
  if (edges.length === 0) return null;
5904
- return { path: path67, edges, culprit: current };
6028
+ return { path: path68, edges, culprit: current };
5905
6029
  }
5906
6030
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
5907
6031
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
5908
6032
  if (!chain) return null;
5909
6033
  const culprit = chain.culprit;
5910
- const path67 = [...chain.path];
6034
+ const path68 = [...chain.path];
5911
6035
  const edgeProvenances = chain.edges.map((e) => e.provenance);
5912
6036
  const baseConfidence = confidenceFromMix(chain.edges);
5913
6037
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -5915,14 +6039,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
5915
6039
  if (loc) {
5916
6040
  let rootCauseNode = culprit;
5917
6041
  if (loc.fileNode) {
5918
- path67.push(loc.fileNode);
6042
+ path68.push(loc.fileNode);
5919
6043
  edgeProvenances.push(import_types8.Provenance.OBSERVED);
5920
6044
  rootCauseNode = loc.fileNode;
5921
6045
  }
5922
6046
  return import_types8.RootCauseResultSchema.parse({
5923
6047
  rootCauseNode,
5924
6048
  rootCauseReason: loc.rootCauseReason,
5925
- traversalPath: path67,
6049
+ traversalPath: path68,
5926
6050
  edgeProvenances,
5927
6051
  confidence,
5928
6052
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -5934,7 +6058,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
5934
6058
  return import_types8.RootCauseResultSchema.parse({
5935
6059
  rootCauseNode: culprit,
5936
6060
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
5937
- traversalPath: path67,
6061
+ traversalPath: path68,
5938
6062
  edgeProvenances,
5939
6063
  confidence,
5940
6064
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -6557,6 +6681,13 @@ function parseGoMod(source) {
6557
6681
  }
6558
6682
  return { module: module2, ...goVersion ? { goVersion } : {}, dependencies };
6559
6683
  }
6684
+ function goFramework(deps) {
6685
+ if (deps["github.com/gin-gonic/gin"]) return "gin";
6686
+ if (deps["github.com/labstack/echo/v4"] || deps["github.com/labstack/echo"]) return "echo";
6687
+ if (deps["github.com/gofiber/fiber/v2"] || deps["github.com/gofiber/fiber/v3"]) return "fiber";
6688
+ if (deps["github.com/go-chi/chi/v5"] || deps["github.com/go-chi/chi"]) return "chi";
6689
+ return void 0;
6690
+ }
6560
6691
  async function discoverGoService(scanPath, dir) {
6561
6692
  let raw;
6562
6693
  try {
@@ -6568,6 +6699,7 @@ async function discoverGoService(scanPath, dir) {
6568
6699
  if (!mod) return null;
6569
6700
  const name = mod.module.split("/").filter(Boolean).pop() ?? mod.module;
6570
6701
  const pkg = { name, dependencies: mod.dependencies };
6702
+ const framework = goFramework(mod.dependencies);
6571
6703
  const node = {
6572
6704
  id: (0, import_types10.serviceId)(name),
6573
6705
  type: import_types10.NodeType.ServiceNode,
@@ -6575,7 +6707,7 @@ async function discoverGoService(scanPath, dir) {
6575
6707
  language: "go",
6576
6708
  dependencies: mod.dependencies,
6577
6709
  repoPath: import_node_path12.default.relative(scanPath, dir),
6578
- ...mod.dependencies["github.com/gin-gonic/gin"] ? { framework: "gin" } : {}
6710
+ ...framework ? { framework } : {}
6579
6711
  };
6580
6712
  return { pkg, dir, node };
6581
6713
  }
@@ -7475,7 +7607,7 @@ async function addSymbolEdges(graph, services) {
7475
7607
  return best;
7476
7608
  };
7477
7609
  const requests = [];
7478
- const walk8 = (node) => {
7610
+ const walk9 = (node) => {
7479
7611
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
7480
7612
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
7481
7613
  if (self && self.kind === "class") {
@@ -7521,10 +7653,10 @@ async function addSymbolEdges(graph, services) {
7521
7653
  }
7522
7654
  for (let i = 0; i < node.namedChildCount; i++) {
7523
7655
  const child = node.namedChild(i);
7524
- if (child) walk8(child);
7656
+ if (child) walk9(child);
7525
7657
  }
7526
7658
  };
7527
- walk8(root);
7659
+ walk9(root);
7528
7660
  for (const req of requests) {
7529
7661
  const targetSid = resolveTarget(req.targetName, req.wantKind);
7530
7662
  if (!targetSid) continue;
@@ -8537,20 +8669,20 @@ var import_node_path30 = __toESM(require("path"), 1);
8537
8669
  var import_types19 = require("@neat.is/types");
8538
8670
  async function walkConfigFiles(dir) {
8539
8671
  const out = [];
8540
- async function walk8(current) {
8672
+ async function walk9(current) {
8541
8673
  const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true });
8542
8674
  for (const entry of entries) {
8543
8675
  const full = import_node_path30.default.join(current, entry.name);
8544
8676
  if (entry.isDirectory()) {
8545
8677
  if (IGNORED_DIRS.has(entry.name)) continue;
8546
8678
  if (await isPythonVenvDir(full)) continue;
8547
- await walk8(full);
8679
+ await walk9(full);
8548
8680
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
8549
8681
  out.push(full);
8550
8682
  }
8551
8683
  }
8552
8684
  }
8553
- await walk8(dir);
8685
+ await walk9(dir);
8554
8686
  return out;
8555
8687
  }
8556
8688
  async function addConfigNodes(graph, services, scanPath) {
@@ -8640,20 +8772,20 @@ function grpcMethodsFromProto(content, fqPackage) {
8640
8772
  }
8641
8773
  async function walkProtoFiles(dir) {
8642
8774
  const out = [];
8643
- async function walk8(current) {
8775
+ async function walk9(current) {
8644
8776
  const entries = await import_node_fs19.promises.readdir(current, { withFileTypes: true }).catch(() => []);
8645
8777
  for (const entry of entries) {
8646
8778
  const full = import_node_path31.default.join(current, entry.name);
8647
8779
  if (entry.isDirectory()) {
8648
8780
  if (IGNORED_DIRS.has(entry.name)) continue;
8649
8781
  if (await isPythonVenvDir(full)) continue;
8650
- await walk8(full);
8782
+ await walk9(full);
8651
8783
  } else if (entry.isFile() && import_node_path31.default.extname(entry.name) === PROTO_EXTENSION) {
8652
8784
  out.push(full);
8653
8785
  }
8654
8786
  }
8655
8787
  }
8656
- await walk8(dir);
8788
+ await walk9(dir);
8657
8789
  return out;
8658
8790
  }
8659
8791
  async function addGrpcMethods(graph, services) {
@@ -8721,7 +8853,7 @@ async function addGrpcMethods(graph, services) {
8721
8853
 
8722
8854
  // src/extract/calls/index.ts
8723
8855
  init_cjs_shims();
8724
- var import_types37 = require("@neat.is/types");
8856
+ var import_types38 = require("@neat.is/types");
8725
8857
 
8726
8858
  // src/extract/calls/http.ts
8727
8859
  init_cjs_shims();
@@ -9475,7 +9607,7 @@ function isFirestoreClientFactory(node) {
9475
9607
  }
9476
9608
  function firestoreClientVars(root) {
9477
9609
  const vars = /* @__PURE__ */ new Set();
9478
- const walk8 = (node) => {
9610
+ const walk9 = (node) => {
9479
9611
  if (node.type === "variable_declarator") {
9480
9612
  const name = node.childForFieldName("name");
9481
9613
  let value = node.childForFieldName("value");
@@ -9484,9 +9616,9 @@ function firestoreClientVars(root) {
9484
9616
  vars.add(name.text);
9485
9617
  }
9486
9618
  }
9487
- for (const c of namedChildren(node)) walk8(c);
9619
+ for (const c of namedChildren(node)) walk9(c);
9488
9620
  };
9489
- walk8(root);
9621
+ walk9(root);
9490
9622
  return vars;
9491
9623
  }
9492
9624
  function isClientExpr(node, clientVars) {
@@ -9641,7 +9773,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9641
9773
  }
9642
9774
  s.add(field);
9643
9775
  };
9644
- const walk8 = (node) => {
9776
+ const walk9 = (node) => {
9645
9777
  if (node.type === "call_expression") {
9646
9778
  const fn = node.childForFieldName("function");
9647
9779
  const line = node.startPosition.row + 1;
@@ -9681,9 +9813,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
9681
9813
  }
9682
9814
  }
9683
9815
  }
9684
- for (const c of namedChildren(node)) walk8(c);
9816
+ for (const c of namedChildren(node)) walk9(c);
9685
9817
  };
9686
- walk8(tree.rootNode);
9818
+ walk9(tree.rootNode);
9687
9819
  const out = [];
9688
9820
  for (const [collPath, line] of collLine) {
9689
9821
  const byField = writes.get(collPath);
@@ -10478,7 +10610,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
10478
10610
  const tree = parseSource3(parserForExt2(import_node_path43.default.extname(file.path)), file.content);
10479
10611
  const out = [];
10480
10612
  const seen = /* @__PURE__ */ new Set();
10481
- const walk8 = (node) => {
10613
+ const walk9 = (node) => {
10482
10614
  if (node.type === "call_expression") {
10483
10615
  const fn = node.childForFieldName("function");
10484
10616
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -10506,9 +10638,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
10506
10638
  }
10507
10639
  }
10508
10640
  }
10509
- for (const c of namedChildren4(node)) walk8(c);
10641
+ for (const c of namedChildren4(node)) walk9(c);
10510
10642
  };
10511
- walk8(tree.rootNode);
10643
+ walk9(tree.rootNode);
10512
10644
  return out;
10513
10645
  }
10514
10646
  function enclosingVarName(call) {
@@ -10530,7 +10662,7 @@ function enclosingVarName(call) {
10530
10662
  function collectDrizzleTables(root) {
10531
10663
  const tables = [];
10532
10664
  const varToTable = /* @__PURE__ */ new Map();
10533
- const walk8 = (node) => {
10665
+ const walk9 = (node) => {
10534
10666
  if (node.type === "call_expression") {
10535
10667
  const fn = node.childForFieldName("function");
10536
10668
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -10545,9 +10677,9 @@ function collectDrizzleTables(root) {
10545
10677
  }
10546
10678
  }
10547
10679
  }
10548
- for (const c of namedChildren4(node)) walk8(c);
10680
+ for (const c of namedChildren4(node)) walk9(c);
10549
10681
  };
10550
- walk8(root);
10682
+ walk9(root);
10551
10683
  return { tables, varToTable };
10552
10684
  }
10553
10685
  function referencesTargetVar(call) {
@@ -10570,7 +10702,7 @@ function drizzleForeignKeys(file, serviceDir) {
10570
10702
  const seen = /* @__PURE__ */ new Set();
10571
10703
  for (const table of tables) {
10572
10704
  if (!table.object) continue;
10573
- const walk8 = (node) => {
10705
+ const walk9 = (node) => {
10574
10706
  if (node.type === "call_expression") {
10575
10707
  const targetVar = referencesTargetVar(node);
10576
10708
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -10591,9 +10723,9 @@ function drizzleForeignKeys(file, serviceDir) {
10591
10723
  }
10592
10724
  }
10593
10725
  }
10594
- for (const c of namedChildren4(node)) walk8(c);
10726
+ for (const c of namedChildren4(node)) walk9(c);
10595
10727
  };
10596
- walk8(table.object);
10728
+ walk9(table.object);
10597
10729
  }
10598
10730
  return out;
10599
10731
  }
@@ -11673,15 +11805,531 @@ function goSqlEndpointsFromFile(file, serviceDir) {
11673
11805
  return out;
11674
11806
  }
11675
11807
 
11808
+ // src/extract/calls/gorm.ts
11809
+ init_cjs_shims();
11810
+ var import_node_path50 = __toESM(require("path"), 1);
11811
+ var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
11812
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
11813
+ var import_types37 = require("@neat.is/types");
11814
+ var GORM_IMPORT_RE = /gorm\.io\/gorm/;
11815
+ var PARSE_CHUNK11 = 16384;
11816
+ function makeGoParser3() {
11817
+ const p = new import_tree_sitter15.default();
11818
+ p.setLanguage(import_tree_sitter_go4.default);
11819
+ return p;
11820
+ }
11821
+ function parseSource10(parser, source) {
11822
+ return parser.parse(
11823
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK11)
11824
+ );
11825
+ }
11826
+ function walk8(node, visit) {
11827
+ visit(node);
11828
+ for (let i = 0; i < node.namedChildCount; i++) {
11829
+ const c = node.namedChild(i);
11830
+ if (c) walk8(c, visit);
11831
+ }
11832
+ }
11833
+ var COMMON_INITIALISMS = [
11834
+ "ASCII",
11835
+ "HTTPS",
11836
+ "UTF8",
11837
+ "XSRF",
11838
+ "HTML",
11839
+ "HTTP",
11840
+ "JSON",
11841
+ "UUID",
11842
+ "XMPP",
11843
+ "ACL",
11844
+ "API",
11845
+ "CPU",
11846
+ "CSS",
11847
+ "DNS",
11848
+ "EOF",
11849
+ "GUID",
11850
+ "LHS",
11851
+ "QPS",
11852
+ "RAM",
11853
+ "RHS",
11854
+ "RPC",
11855
+ "SLA",
11856
+ "SQL",
11857
+ "SSH",
11858
+ "TCP",
11859
+ "TLS",
11860
+ "TTL",
11861
+ "UDP",
11862
+ "UID",
11863
+ "URI",
11864
+ "URL",
11865
+ "UID",
11866
+ "XSS",
11867
+ "ID",
11868
+ "IP",
11869
+ "UI",
11870
+ "VM",
11871
+ "XML"
11872
+ ].sort((a, b) => b.length - a.length);
11873
+ function titleCase(word) {
11874
+ return word.charAt(0) + word.slice(1).toLowerCase();
11875
+ }
11876
+ function replaceInitialisms(name) {
11877
+ let out = "";
11878
+ let i = 0;
11879
+ while (i < name.length) {
11880
+ let matched = false;
11881
+ for (const init of COMMON_INITIALISMS) {
11882
+ if (name.startsWith(init, i)) {
11883
+ out += titleCase(init);
11884
+ i += init.length;
11885
+ matched = true;
11886
+ break;
11887
+ }
11888
+ }
11889
+ if (!matched) {
11890
+ out += name[i];
11891
+ i++;
11892
+ }
11893
+ }
11894
+ return out;
11895
+ }
11896
+ var isUpper = (c) => c >= "A" && c <= "Z";
11897
+ var isDigit = (c) => c >= "0" && c <= "9";
11898
+ function toDBName(name) {
11899
+ if (name === "") return "";
11900
+ const value = replaceInitialisms(name);
11901
+ if (value.length === 1) return value.toLowerCase();
11902
+ let buf = "";
11903
+ let lastCase = false;
11904
+ let curCase = isUpper(value[0]);
11905
+ for (let i = 0; i < value.length - 1; i++) {
11906
+ const v = value[i];
11907
+ const nextCase = isUpper(value[i + 1]);
11908
+ const nextNumber = isDigit(value[i + 1]);
11909
+ if (curCase) {
11910
+ if (lastCase && (nextCase || nextNumber)) {
11911
+ buf += v.toLowerCase();
11912
+ } else {
11913
+ if (i > 0 && value[i - 1] !== "_" && lastCase !== curCase) buf += "_";
11914
+ buf += v.toLowerCase();
11915
+ }
11916
+ } else {
11917
+ buf += v;
11918
+ }
11919
+ lastCase = curCase;
11920
+ curCase = nextCase;
11921
+ }
11922
+ const last = value[value.length - 1];
11923
+ if (curCase) {
11924
+ if (!lastCase && value.length > 1) buf += "_";
11925
+ buf += last.toLowerCase();
11926
+ } else {
11927
+ buf += last;
11928
+ }
11929
+ return buf;
11930
+ }
11931
+ var UNCOUNTABLE = /* @__PURE__ */ new Set([
11932
+ "equipment",
11933
+ "information",
11934
+ "rice",
11935
+ "money",
11936
+ "species",
11937
+ "series",
11938
+ "fish",
11939
+ "sheep",
11940
+ "jeans",
11941
+ "police"
11942
+ ]);
11943
+ var IRREGULAR = [
11944
+ ["person", "people"],
11945
+ ["man", "men"],
11946
+ ["child", "children"],
11947
+ ["sex", "sexes"],
11948
+ ["move", "moves"]
11949
+ ];
11950
+ var PLURAL_RULES = [
11951
+ [/(quiz)$/i, "$1zes"],
11952
+ [/^(ox)$/i, "$1en"],
11953
+ [/([ml])ouse$/i, "$1ice"],
11954
+ [/(matr|vert|ind)(?:ix|ex)$/i, "$1ices"],
11955
+ [/(x|ch|ss|sh)$/i, "$1es"],
11956
+ [/([^aeiouy]|qu)y$/i, "$1ies"],
11957
+ [/(hive)$/i, "$1s"],
11958
+ [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
11959
+ [/sis$/i, "ses"],
11960
+ [/([ti])um$/i, "$1a"],
11961
+ [/([ti])a$/i, "$1a"],
11962
+ [/(buffal|tomat)o$/i, "$1oes"],
11963
+ [/(bu)s$/i, "$1ses"],
11964
+ [/(alias|status)$/i, "$1es"],
11965
+ [/(octop|vir)i$/i, "$1i"],
11966
+ [/(octop|vir)us$/i, "$1i"],
11967
+ [/(ax|test)is$/i, "$1es"],
11968
+ [/s$/i, "s"]
11969
+ ];
11970
+ function pluralize3(word) {
11971
+ if (word === "") return word;
11972
+ const lower = word.toLowerCase();
11973
+ for (const u of UNCOUNTABLE) {
11974
+ if (lower === u || lower.endsWith("_" + u)) return word;
11975
+ }
11976
+ for (const [sing, plur] of IRREGULAR) {
11977
+ const re = new RegExp(sing + "$", "i");
11978
+ if (re.test(word)) return word.replace(re, plur);
11979
+ }
11980
+ for (const [re, rep] of PLURAL_RULES) {
11981
+ if (re.test(word)) return word.replace(re, rep);
11982
+ }
11983
+ return word + "s";
11984
+ }
11985
+ function deriveTableName(structName) {
11986
+ return pluralize3(toDBName(structName));
11987
+ }
11988
+ function stringLiteralValue(node) {
11989
+ if (!node) return null;
11990
+ if (node.type === "interpreted_string_literal" || node.type === "raw_string_literal") {
11991
+ const t = node.text;
11992
+ return t.length >= 2 ? t.slice(1, -1) : "";
11993
+ }
11994
+ return null;
11995
+ }
11996
+ function parseGormTag(tagNode) {
11997
+ const tag = {};
11998
+ if (!tagNode) return tag;
11999
+ let inner = tagNode.text;
12000
+ if (inner.length >= 2) inner = inner.slice(1, -1);
12001
+ if (tagNode.type === "interpreted_string_literal") inner = inner.replace(/\\"/g, '"');
12002
+ const m = inner.match(/gorm:"([^"]*)"/);
12003
+ if (!m) return tag;
12004
+ for (const part of m[1].split(";")) {
12005
+ if (part === "") continue;
12006
+ const idx = part.indexOf(":");
12007
+ const key = (idx >= 0 ? part.slice(0, idx) : part).trim().toLowerCase();
12008
+ const value = idx >= 0 ? part.slice(idx + 1).trim() : "";
12009
+ if (key === "-") tag.skip = true;
12010
+ else if (key === "column") tag.column = value;
12011
+ else if (key === "primarykey" || key === "primary_key") tag.primaryKey = true;
12012
+ else if (key === "foreignkey") tag.foreignKey = value;
12013
+ else if (key === "many2many") tag.many2many = value;
12014
+ else if (key === "embedded") tag.embedded = true;
12015
+ else if (key === "embeddedprefix") tag.embeddedPrefix = value;
12016
+ }
12017
+ return tag;
12018
+ }
12019
+ function unwrapType(typeNode) {
12020
+ let isSlice = false;
12021
+ let isPointer = false;
12022
+ let n = typeNode;
12023
+ while (n && (n.type === "slice_type" || n.type === "array_type" || n.type === "pointer_type")) {
12024
+ if (n.type === "slice_type" || n.type === "array_type") isSlice = true;
12025
+ if (n.type === "pointer_type") isPointer = true;
12026
+ n = n.childForFieldName("element") ?? n.namedChild(n.namedChildCount - 1);
12027
+ }
12028
+ if (!n) return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
12029
+ if (n.type === "type_identifier") {
12030
+ return { name: n.text, qualifier: null, isSlice, isPointer, isQualified: false };
12031
+ }
12032
+ if (n.type === "qualified_type") {
12033
+ const pkg = n.childForFieldName("package")?.text ?? n.namedChild(0)?.text ?? null;
12034
+ const nm = n.childForFieldName("name")?.text ?? n.namedChild(1)?.text ?? null;
12035
+ return { name: nm, qualifier: pkg, isSlice, isPointer, isQualified: true };
12036
+ }
12037
+ return { name: null, qualifier: null, isSlice, isPointer, isQualified: false };
12038
+ }
12039
+ function readField(fieldDecl) {
12040
+ const names = [];
12041
+ let tagNode = null;
12042
+ for (let i = 0; i < fieldDecl.namedChildCount; i++) {
12043
+ const c = fieldDecl.namedChild(i);
12044
+ if (!c) continue;
12045
+ if (c.type === "field_identifier") names.push(c.text);
12046
+ else if (c.type === "raw_string_literal" || c.type === "interpreted_string_literal") tagNode = c;
12047
+ }
12048
+ const typeNode = fieldDecl.childForFieldName("type");
12049
+ const t = unwrapType(typeNode);
12050
+ return {
12051
+ names,
12052
+ typeName: t.name,
12053
+ qualifier: t.qualifier,
12054
+ isSlice: t.isSlice,
12055
+ isPointer: t.isPointer,
12056
+ isQualified: t.isQualified,
12057
+ tag: parseGormTag(tagNode),
12058
+ line: fieldDecl.startPosition.row + 1
12059
+ };
12060
+ }
12061
+ function collectStructs(tree) {
12062
+ const structs = /* @__PURE__ */ new Map();
12063
+ walk8(tree.rootNode, (node) => {
12064
+ if (node.type !== "type_spec") return;
12065
+ const nameNode = node.childForFieldName("name");
12066
+ const typeNode = node.childForFieldName("type");
12067
+ if (!nameNode || typeNode?.type !== "struct_type") return;
12068
+ const list = typeNode.childForFieldName("body") ?? typeNode.namedChild(0);
12069
+ const fields = [];
12070
+ if (list && list.type === "field_declaration_list") {
12071
+ for (let i = 0; i < list.namedChildCount; i++) {
12072
+ const fd = list.namedChild(i);
12073
+ if (fd?.type === "field_declaration") fields.push(readField(fd));
12074
+ }
12075
+ }
12076
+ structs.set(nameNode.text, {
12077
+ name: nameNode.text,
12078
+ fields,
12079
+ line: node.startPosition.row + 1
12080
+ });
12081
+ });
12082
+ return structs;
12083
+ }
12084
+ var GORM_MODEL_METHODS = /* @__PURE__ */ new Set([
12085
+ "AutoMigrate",
12086
+ "Model",
12087
+ "Create",
12088
+ "Find",
12089
+ "First",
12090
+ "Take",
12091
+ "Last",
12092
+ "Save",
12093
+ "Delete",
12094
+ "Where",
12095
+ "FirstOrCreate",
12096
+ "FirstOrInit"
12097
+ ]);
12098
+ function compositeStructName(arg) {
12099
+ let n = arg;
12100
+ if (n.type === "unary_expression") n = n.childForFieldName("operand") ?? n.namedChild(0);
12101
+ if (!n || n.type !== "composite_literal") return null;
12102
+ const typeNode = n.childForFieldName("type");
12103
+ if (!typeNode) return null;
12104
+ if (typeNode.type === "type_identifier") return typeNode.text;
12105
+ if (typeNode.type === "qualified_type") {
12106
+ return typeNode.childForFieldName("name")?.text ?? typeNode.namedChild(1)?.text ?? null;
12107
+ }
12108
+ return null;
12109
+ }
12110
+ function collectCallModels(tree) {
12111
+ const models = /* @__PURE__ */ new Set();
12112
+ walk8(tree.rootNode, (node) => {
12113
+ if (node.type !== "call_expression") return;
12114
+ const fn = node.childForFieldName("function");
12115
+ if (fn?.type !== "selector_expression") return;
12116
+ const method = fn.childForFieldName("field")?.text;
12117
+ if (!method || !GORM_MODEL_METHODS.has(method)) return;
12118
+ const args = node.childForFieldName("arguments");
12119
+ if (!args) return;
12120
+ for (let i = 0; i < args.namedChildCount; i++) {
12121
+ const arg = args.namedChild(i);
12122
+ if (!arg) continue;
12123
+ const name = compositeStructName(arg);
12124
+ if (name) models.add(name);
12125
+ }
12126
+ });
12127
+ return models;
12128
+ }
12129
+ function collectTableNameOverrides(tree) {
12130
+ const overrides = /* @__PURE__ */ new Map();
12131
+ const declarers = /* @__PURE__ */ new Set();
12132
+ walk8(tree.rootNode, (node) => {
12133
+ if (node.type !== "method_declaration") return;
12134
+ if (node.childForFieldName("name")?.text !== "TableName") return;
12135
+ const receiver = node.childForFieldName("receiver");
12136
+ if (!receiver) return;
12137
+ let recvType = null;
12138
+ for (let i = 0; i < receiver.namedChildCount; i++) {
12139
+ const pd = receiver.namedChild(i);
12140
+ if (pd?.type !== "parameter_declaration") continue;
12141
+ const t = unwrapType(pd.childForFieldName("type"));
12142
+ recvType = t.name;
12143
+ }
12144
+ if (!recvType) return;
12145
+ declarers.add(recvType);
12146
+ const body = node.childForFieldName("body");
12147
+ if (!body) return;
12148
+ let literal = null;
12149
+ walk8(body, (n) => {
12150
+ if (literal !== null) return;
12151
+ if (n.type !== "return_statement") return;
12152
+ const exprList = n.namedChild(0);
12153
+ const first = exprList?.namedChild(0) ?? exprList;
12154
+ const v = stringLiteralValue(first);
12155
+ if (v) literal = v;
12156
+ });
12157
+ if (literal !== null) overrides.set(recvType, literal);
12158
+ });
12159
+ return { overrides, declarers };
12160
+ }
12161
+ function isRelationField(field, structs) {
12162
+ if (field.names.length === 0) return false;
12163
+ if (field.isQualified) return false;
12164
+ if (!field.typeName) return false;
12165
+ return structs.has(field.typeName);
12166
+ }
12167
+ function isGormModelEmbed(field) {
12168
+ return field.names.length === 0 && field.qualifier === "gorm" && field.typeName === "Model";
12169
+ }
12170
+ function analyze(tree) {
12171
+ const structs = collectStructs(tree);
12172
+ const { overrides, declarers } = collectTableNameOverrides(tree);
12173
+ const callModels = collectCallModels(tree);
12174
+ const models = /* @__PURE__ */ new Set();
12175
+ for (const [name, info] of structs) {
12176
+ if (info.fields.some(isGormModelEmbed)) models.add(name);
12177
+ }
12178
+ for (const name of callModels) if (structs.has(name)) models.add(name);
12179
+ for (const name of declarers) if (structs.has(name)) models.add(name);
12180
+ let grew = true;
12181
+ while (grew) {
12182
+ grew = false;
12183
+ for (const name of Array.from(models)) {
12184
+ const info = structs.get(name);
12185
+ if (!info) continue;
12186
+ for (const field of info.fields) {
12187
+ if (!isRelationField(field, structs)) continue;
12188
+ const target = field.typeName;
12189
+ if (!models.has(target) && structs.has(target)) {
12190
+ models.add(target);
12191
+ grew = true;
12192
+ }
12193
+ }
12194
+ }
12195
+ }
12196
+ const tableFor = (structName) => overrides.get(structName) ?? deriveTableName(structName);
12197
+ return { structs, models, tableFor };
12198
+ }
12199
+ function collectColumns(struct, structs, seen, prefix, out, emitted) {
12200
+ if (seen.has(struct.name)) return;
12201
+ seen.add(struct.name);
12202
+ const add = (col) => {
12203
+ const full = prefix + col;
12204
+ if (!emitted.has(full)) {
12205
+ emitted.add(full);
12206
+ out.push(full);
12207
+ }
12208
+ };
12209
+ for (const field of struct.fields) {
12210
+ if (field.tag.skip) continue;
12211
+ if (field.names.length === 0) {
12212
+ if (isGormModelEmbed(field)) {
12213
+ add("id");
12214
+ add("created_at");
12215
+ add("updated_at");
12216
+ add("deleted_at");
12217
+ } else if (!field.isQualified && field.typeName && structs.has(field.typeName)) {
12218
+ collectColumns(structs.get(field.typeName), structs, seen, prefix, out, emitted);
12219
+ }
12220
+ continue;
12221
+ }
12222
+ if (field.tag.embedded && !field.isQualified && field.typeName && structs.has(field.typeName)) {
12223
+ collectColumns(
12224
+ structs.get(field.typeName),
12225
+ structs,
12226
+ seen,
12227
+ prefix + (field.tag.embeddedPrefix ?? ""),
12228
+ out,
12229
+ emitted
12230
+ );
12231
+ continue;
12232
+ }
12233
+ if (isRelationField(field, structs)) continue;
12234
+ if (field.names.length === 1 && field.tag.column) {
12235
+ add(field.tag.column);
12236
+ } else {
12237
+ for (const n of field.names) add(toDBName(n));
12238
+ }
12239
+ }
12240
+ seen.delete(struct.name);
12241
+ }
12242
+ function gormEndpointsFromFile(file, serviceDir) {
12243
+ if (import_node_path50.default.extname(file.path) !== ".go") return [];
12244
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
12245
+ const tree = parseSource10(makeGoParser3(), file.content);
12246
+ const { structs, models, tableFor } = analyze(tree);
12247
+ const out = [];
12248
+ const seenTables = /* @__PURE__ */ new Set();
12249
+ for (const name of models) {
12250
+ const struct = structs.get(name);
12251
+ if (!struct) continue;
12252
+ const table = tableFor(name);
12253
+ if (seenTables.has(table)) continue;
12254
+ seenTables.add(table);
12255
+ const columns = [];
12256
+ collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
12257
+ out.push({
12258
+ infraId: (0, import_types37.infraId)("sql-table", table),
12259
+ name: table,
12260
+ kind: "sql-table",
12261
+ edgeType: "CALLS",
12262
+ confidenceKind: "structural",
12263
+ ...columns.length > 0 ? { columns } : {},
12264
+ evidence: {
12265
+ file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
12266
+ line: struct.line,
12267
+ snippet: snippet(file.content, struct.line)
12268
+ }
12269
+ });
12270
+ }
12271
+ return out;
12272
+ }
12273
+ function gormForeignKeys(file, serviceDir) {
12274
+ if (import_node_path50.default.extname(file.path) !== ".go") return [];
12275
+ if (!GORM_IMPORT_RE.test(file.content)) return [];
12276
+ const tree = parseSource10(makeGoParser3(), file.content);
12277
+ const { structs, models, tableFor } = analyze(tree);
12278
+ const out = [];
12279
+ const seen = /* @__PURE__ */ new Set();
12280
+ const emit = (childTable, parentTable, line) => {
12281
+ if (!childTable || !parentTable || childTable === parentTable) return;
12282
+ const key = `${childTable}->${parentTable}`;
12283
+ if (seen.has(key)) return;
12284
+ seen.add(key);
12285
+ out.push({
12286
+ childTable,
12287
+ parentTable,
12288
+ evidence: {
12289
+ file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
12290
+ line,
12291
+ snippet: snippet(file.content, line)
12292
+ }
12293
+ });
12294
+ };
12295
+ for (const name of models) {
12296
+ const struct = structs.get(name);
12297
+ if (!struct) continue;
12298
+ const thisTable = tableFor(name);
12299
+ const scalarNames = new Set(
12300
+ struct.fields.filter((f) => f.names.length > 0 && !isRelationField(f, structs)).flatMap((f) => f.names)
12301
+ );
12302
+ for (const field of struct.fields) {
12303
+ if (field.tag.skip) continue;
12304
+ if (!isRelationField(field, structs)) continue;
12305
+ const relTable = tableFor(field.typeName);
12306
+ if (field.tag.many2many) {
12307
+ emit(field.tag.many2many, thisTable, field.line);
12308
+ emit(field.tag.many2many, relTable, field.line);
12309
+ continue;
12310
+ }
12311
+ if (field.isSlice) {
12312
+ emit(relTable, thisTable, field.line);
12313
+ continue;
12314
+ }
12315
+ const convFk = field.names[0] + "ID";
12316
+ const belongsTo = scalarNames.has(convFk) || (field.tag.foreignKey ? scalarNames.has(field.tag.foreignKey) : false);
12317
+ if (belongsTo) emit(thisTable, relTable, field.line);
12318
+ else emit(relTable, thisTable, field.line);
12319
+ }
12320
+ }
12321
+ return out;
12322
+ }
12323
+
11676
12324
  // src/extract/calls/index.ts
11677
12325
  function edgeTypeFromEndpoint(ep) {
11678
12326
  switch (ep.edgeType) {
11679
12327
  case "PUBLISHES_TO":
11680
- return import_types37.EdgeType.PUBLISHES_TO;
12328
+ return import_types38.EdgeType.PUBLISHES_TO;
11681
12329
  case "CONSUMES_FROM":
11682
- return import_types37.EdgeType.CONSUMES_FROM;
12330
+ return import_types38.EdgeType.CONSUMES_FROM;
11683
12331
  default:
11684
- return import_types37.EdgeType.CALLS;
12332
+ return import_types38.EdgeType.CALLS;
11685
12333
  }
11686
12334
  }
11687
12335
  function isAwsKind(kind) {
@@ -11714,6 +12362,11 @@ async function addExternalEndpointEdges(graph, services) {
11714
12362
  } catch (err) {
11715
12363
  recordExtractionError("go SQL call extraction", file.path, err);
11716
12364
  }
12365
+ try {
12366
+ endpoints.push(...gormEndpointsFromFile(file, service.dir));
12367
+ } catch (err) {
12368
+ recordExtractionError("gorm data-axis extraction", file.path, err);
12369
+ }
11717
12370
  try {
11718
12371
  endpoints.push(...railsSchemaEndpointsFromFile(file, service.dir));
11719
12372
  endpoints.push(...railsModelEndpointsFromFile(file, service.dir));
@@ -11736,7 +12389,7 @@ async function addExternalEndpointEdges(graph, services) {
11736
12389
  if (!graph.hasNode(ep.infraId)) {
11737
12390
  const node = {
11738
12391
  id: ep.infraId,
11739
- type: import_types37.NodeType.InfraNode,
12392
+ type: import_types38.NodeType.InfraNode,
11740
12393
  name: ep.name,
11741
12394
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
11742
12395
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -11749,21 +12402,21 @@ async function addExternalEndpointEdges(graph, services) {
11749
12402
  }
11750
12403
  if (ep.columns && ep.columns.length > 0) {
11751
12404
  const node = graph.getNodeAttributes(ep.infraId);
11752
- if (node.type === import_types37.NodeType.InfraNode) {
12405
+ if (node.type === import_types38.NodeType.InfraNode) {
11753
12406
  graph.replaceNodeAttributes(ep.infraId, {
11754
12407
  ...node,
11755
12408
  columns: foldColumns(
11756
12409
  node.columns,
11757
12410
  ep.columns,
11758
- import_types37.Provenance.EXTRACTED,
11759
- (0, import_types37.confidenceForExtracted)(ep.confidenceKind)
12411
+ import_types38.Provenance.EXTRACTED,
12412
+ (0, import_types38.confidenceForExtracted)(ep.confidenceKind)
11760
12413
  )
11761
12414
  });
11762
12415
  }
11763
12416
  }
11764
12417
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
11765
12418
  const node = graph.getNodeAttributes(ep.infraId);
11766
- if (node.type === import_types37.NodeType.InfraNode) {
12419
+ if (node.type === import_types38.NodeType.InfraNode) {
11767
12420
  graph.replaceNodeAttributes(ep.infraId, {
11768
12421
  ...node,
11769
12422
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -11771,7 +12424,7 @@ async function addExternalEndpointEdges(graph, services) {
11771
12424
  }
11772
12425
  }
11773
12426
  const edgeType = edgeTypeFromEndpoint(ep);
11774
- const confidence = (0, import_types37.confidenceForExtracted)(ep.confidenceKind);
12427
+ const confidence = (0, import_types38.confidenceForExtracted)(ep.confidenceKind);
11775
12428
  const relFile = toPosix(ep.evidence.file);
11776
12429
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
11777
12430
  graph,
@@ -11781,7 +12434,7 @@ async function addExternalEndpointEdges(graph, services) {
11781
12434
  );
11782
12435
  nodesAdded += n;
11783
12436
  edgesAdded += e;
11784
- if (!(0, import_types37.passesExtractedFloor)(confidence)) {
12437
+ if (!(0, import_types38.passesExtractedFloor)(confidence)) {
11785
12438
  noteExtractedDropped({
11786
12439
  source: fileNodeId,
11787
12440
  target: ep.infraId,
@@ -11801,7 +12454,7 @@ async function addExternalEndpointEdges(graph, services) {
11801
12454
  source: fileNodeId,
11802
12455
  target: ep.infraId,
11803
12456
  type: edgeType,
11804
- provenance: import_types37.Provenance.EXTRACTED,
12457
+ provenance: import_types38.Provenance.EXTRACTED,
11805
12458
  confidence,
11806
12459
  evidence: ep.evidence
11807
12460
  };
@@ -11824,7 +12477,7 @@ async function addCallEdges(graph, services) {
11824
12477
 
11825
12478
  // src/extract/table-edges.ts
11826
12479
  init_cjs_shims();
11827
- var import_types38 = require("@neat.is/types");
12480
+ var import_types39 = require("@neat.is/types");
11828
12481
  async function addTableEdges(graph, services) {
11829
12482
  let nodesAdded = 0;
11830
12483
  let edgesAdded = 0;
@@ -11838,6 +12491,7 @@ async function addTableEdges(graph, services) {
11838
12491
  refs.push(...sqlalchemyForeignKeys(file, service.dir));
11839
12492
  refs.push(...railsSchemaForeignKeys(file, service.dir));
11840
12493
  refs.push(...laravelMigrationForeignKeys(file, service.dir));
12494
+ refs.push(...gormForeignKeys(file, service.dir));
11841
12495
  modelRefs.push(...railsModelForeignKeys(file, service.dir));
11842
12496
  modelRefs.push(...laravelModelForeignKeys(file, service.dir));
11843
12497
  } catch (err) {
@@ -11851,20 +12505,20 @@ async function addTableEdges(graph, services) {
11851
12505
  }
11852
12506
  refs.push(...modelRefs);
11853
12507
  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);
12508
+ const childId = (0, import_types39.infraId)("sql-table", ref.childTable);
12509
+ const parentId = (0, import_types39.infraId)("sql-table", ref.parentTable);
11856
12510
  if (childId === parentId) continue;
11857
12511
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
11858
12512
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
11859
- const edgeId = (0, import_types38.extractedEdgeId)(childId, parentId, import_types38.EdgeType.REFERENCES);
12513
+ const edgeId = (0, import_types39.extractedEdgeId)(childId, parentId, import_types39.EdgeType.REFERENCES);
11860
12514
  if (graph.hasEdge(edgeId)) continue;
11861
12515
  const edge = {
11862
12516
  id: edgeId,
11863
12517
  source: childId,
11864
12518
  target: parentId,
11865
- type: import_types38.EdgeType.REFERENCES,
11866
- provenance: import_types38.Provenance.EXTRACTED,
11867
- confidence: (0, import_types38.confidenceForExtracted)("structural"),
12519
+ type: import_types39.EdgeType.REFERENCES,
12520
+ provenance: import_types39.Provenance.EXTRACTED,
12521
+ confidence: (0, import_types39.confidenceForExtracted)("structural"),
11868
12522
  evidence: ref.evidence
11869
12523
  };
11870
12524
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -11877,7 +12531,7 @@ function ensureTableNode(graph, id, name) {
11877
12531
  if (graph.hasNode(id)) return 0;
11878
12532
  const node = {
11879
12533
  id,
11880
- type: import_types38.NodeType.InfraNode,
12534
+ type: import_types39.NodeType.InfraNode,
11881
12535
  name,
11882
12536
  provider: "self",
11883
12537
  kind: "sql-table"
@@ -11891,16 +12545,16 @@ init_cjs_shims();
11891
12545
 
11892
12546
  // src/extract/infra/docker-compose.ts
11893
12547
  init_cjs_shims();
11894
- var import_node_path50 = __toESM(require("path"), 1);
11895
- var import_types40 = require("@neat.is/types");
12548
+ var import_node_path51 = __toESM(require("path"), 1);
12549
+ var import_types41 = require("@neat.is/types");
11896
12550
 
11897
12551
  // src/extract/infra/shared.ts
11898
12552
  init_cjs_shims();
11899
- var import_types39 = require("@neat.is/types");
12553
+ var import_types40 = require("@neat.is/types");
11900
12554
  function makeInfraNode(kind, name, provider = "self", extras) {
11901
12555
  return {
11902
- id: (0, import_types39.infraId)(kind, name),
11903
- type: import_types39.NodeType.InfraNode,
12556
+ id: (0, import_types40.infraId)(kind, name),
12557
+ type: import_types40.NodeType.InfraNode,
11904
12558
  name,
11905
12559
  provider,
11906
12560
  kind,
@@ -11944,8 +12598,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
11944
12598
  source: anchorId,
11945
12599
  target: node.id,
11946
12600
  type: edgeType,
11947
- provenance: import_types39.Provenance.EXTRACTED,
11948
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
12601
+ provenance: import_types40.Provenance.EXTRACTED,
12602
+ confidence: (0, import_types40.confidenceForExtracted)("structural"),
11949
12603
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
11950
12604
  };
11951
12605
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -11962,7 +12616,7 @@ function dependsOnList(value) {
11962
12616
  }
11963
12617
  function serviceNameToServiceNode(name, services) {
11964
12618
  for (const s of services) {
11965
- if (s.node.name === name || import_node_path50.default.basename(s.dir) === name) return s.node.id;
12619
+ if (s.node.name === name || import_node_path51.default.basename(s.dir) === name) return s.node.id;
11966
12620
  }
11967
12621
  return null;
11968
12622
  }
@@ -11971,7 +12625,7 @@ async function addComposeInfra(graph, scanPath, services) {
11971
12625
  let edgesAdded = 0;
11972
12626
  let composePath = null;
11973
12627
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
11974
- const abs = import_node_path50.default.join(scanPath, name);
12628
+ const abs = import_node_path51.default.join(scanPath, name);
11975
12629
  if (await exists2(abs)) {
11976
12630
  composePath = abs;
11977
12631
  break;
@@ -11984,13 +12638,13 @@ async function addComposeInfra(graph, scanPath, services) {
11984
12638
  } catch (err) {
11985
12639
  recordExtractionError(
11986
12640
  "infra docker-compose",
11987
- import_node_path50.default.relative(scanPath, composePath),
12641
+ import_node_path51.default.relative(scanPath, composePath),
11988
12642
  err
11989
12643
  );
11990
12644
  return { nodesAdded, edgesAdded };
11991
12645
  }
11992
12646
  if (!compose?.services) return { nodesAdded, edgesAdded };
11993
- const evidenceFile = import_node_path50.default.relative(scanPath, composePath).split(import_node_path50.default.sep).join("/");
12647
+ const evidenceFile = import_node_path51.default.relative(scanPath, composePath).split(import_node_path51.default.sep).join("/");
11994
12648
  const composeNameToNodeId = /* @__PURE__ */ new Map();
11995
12649
  for (const [composeName, svc] of Object.entries(compose.services)) {
11996
12650
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -12012,15 +12666,15 @@ async function addComposeInfra(graph, scanPath, services) {
12012
12666
  for (const dep of dependsOnList(svc.depends_on)) {
12013
12667
  const targetId = composeNameToNodeId.get(dep);
12014
12668
  if (!targetId) continue;
12015
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types40.EdgeType.DEPENDS_ON);
12669
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types41.EdgeType.DEPENDS_ON);
12016
12670
  if (graph.hasEdge(edgeId)) continue;
12017
12671
  const edge = {
12018
12672
  id: edgeId,
12019
12673
  source: sourceId,
12020
12674
  target: targetId,
12021
- type: import_types40.EdgeType.DEPENDS_ON,
12022
- provenance: import_types40.Provenance.EXTRACTED,
12023
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
12675
+ type: import_types41.EdgeType.DEPENDS_ON,
12676
+ provenance: import_types41.Provenance.EXTRACTED,
12677
+ confidence: (0, import_types41.confidenceForExtracted)("structural"),
12024
12678
  evidence: { file: evidenceFile }
12025
12679
  };
12026
12680
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12032,9 +12686,9 @@ async function addComposeInfra(graph, scanPath, services) {
12032
12686
 
12033
12687
  // src/extract/infra/dockerfile.ts
12034
12688
  init_cjs_shims();
12035
- var import_node_path51 = __toESM(require("path"), 1);
12689
+ var import_node_path52 = __toESM(require("path"), 1);
12036
12690
  var import_node_fs20 = require("fs");
12037
- var import_types41 = require("@neat.is/types");
12691
+ var import_types42 = require("@neat.is/types");
12038
12692
  function readDockerfile(content) {
12039
12693
  let image = null;
12040
12694
  const ports = [];
@@ -12063,7 +12717,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12063
12717
  let nodesAdded = 0;
12064
12718
  let edgesAdded = 0;
12065
12719
  for (const service of services) {
12066
- const dockerfilePath = import_node_path51.default.join(service.dir, "Dockerfile");
12720
+ const dockerfilePath = import_node_path52.default.join(service.dir, "Dockerfile");
12067
12721
  if (!await exists2(dockerfilePath)) continue;
12068
12722
  let content;
12069
12723
  try {
@@ -12071,7 +12725,7 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12071
12725
  } catch (err) {
12072
12726
  recordExtractionError(
12073
12727
  "infra dockerfile",
12074
- import_node_path51.default.relative(scanPath, dockerfilePath),
12728
+ import_node_path52.default.relative(scanPath, dockerfilePath),
12075
12729
  err
12076
12730
  );
12077
12731
  continue;
@@ -12083,8 +12737,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12083
12737
  graph.addNode(node.id, node);
12084
12738
  nodesAdded++;
12085
12739
  }
12086
- const relDockerfile = toPosix(import_node_path51.default.relative(service.dir, dockerfilePath));
12087
- const evidenceFile = toPosix(import_node_path51.default.relative(scanPath, dockerfilePath));
12740
+ const relDockerfile = toPosix(import_node_path52.default.relative(service.dir, dockerfilePath));
12741
+ const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, dockerfilePath));
12088
12742
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
12089
12743
  graph,
12090
12744
  service.pkg.name,
@@ -12093,15 +12747,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12093
12747
  );
12094
12748
  nodesAdded += fn;
12095
12749
  edgesAdded += fe;
12096
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types41.EdgeType.RUNS_ON);
12750
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types42.EdgeType.RUNS_ON);
12097
12751
  if (!graph.hasEdge(edgeId)) {
12098
12752
  const edge = {
12099
12753
  id: edgeId,
12100
12754
  source: fileNodeId,
12101
12755
  target: node.id,
12102
- type: import_types41.EdgeType.RUNS_ON,
12103
- provenance: import_types41.Provenance.EXTRACTED,
12104
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12756
+ type: import_types42.EdgeType.RUNS_ON,
12757
+ provenance: import_types42.Provenance.EXTRACTED,
12758
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
12105
12759
  evidence: {
12106
12760
  file: evidenceFile,
12107
12761
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -12116,15 +12770,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12116
12770
  graph.addNode(portNode.id, portNode);
12117
12771
  nodesAdded++;
12118
12772
  }
12119
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types41.EdgeType.CONNECTS_TO);
12773
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types42.EdgeType.CONNECTS_TO);
12120
12774
  if (graph.hasEdge(portEdgeId)) continue;
12121
12775
  const portEdge = {
12122
12776
  id: portEdgeId,
12123
12777
  source: fileNodeId,
12124
12778
  target: portNode.id,
12125
- type: import_types41.EdgeType.CONNECTS_TO,
12126
- provenance: import_types41.Provenance.EXTRACTED,
12127
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
12779
+ type: import_types42.EdgeType.CONNECTS_TO,
12780
+ provenance: import_types42.Provenance.EXTRACTED,
12781
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
12128
12782
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
12129
12783
  };
12130
12784
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -12137,8 +12791,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
12137
12791
  // src/extract/infra/terraform.ts
12138
12792
  init_cjs_shims();
12139
12793
  var import_node_fs21 = require("fs");
12140
- var import_node_path52 = __toESM(require("path"), 1);
12141
- var import_types42 = require("@neat.is/types");
12794
+ var import_node_path53 = __toESM(require("path"), 1);
12795
+ var import_types43 = require("@neat.is/types");
12142
12796
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
12143
12797
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
12144
12798
  async function walkTfFiles(start, depth = 0, max = 5) {
@@ -12148,11 +12802,11 @@ async function walkTfFiles(start, depth = 0, max = 5) {
12148
12802
  for (const entry of entries) {
12149
12803
  if (entry.isDirectory()) {
12150
12804
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
12151
- const child = import_node_path52.default.join(start, entry.name);
12805
+ const child = import_node_path53.default.join(start, entry.name);
12152
12806
  if (await isPythonVenvDir(child)) continue;
12153
12807
  out.push(...await walkTfFiles(child, depth + 1, max));
12154
12808
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
12155
- out.push(import_node_path52.default.join(start, entry.name));
12809
+ out.push(import_node_path53.default.join(start, entry.name));
12156
12810
  }
12157
12811
  }
12158
12812
  return out;
@@ -12184,7 +12838,7 @@ async function addTerraformResources(graph, scanPath) {
12184
12838
  const files = await walkTfFiles(scanPath);
12185
12839
  for (const file of files) {
12186
12840
  const content = await import_node_fs21.promises.readFile(file, "utf8");
12187
- const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, file));
12841
+ const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, file));
12188
12842
  const resources = [];
12189
12843
  const byKey = /* @__PURE__ */ new Map();
12190
12844
  RESOURCE_RE.lastIndex = 0;
@@ -12219,16 +12873,16 @@ async function addTerraformResources(graph, scanPath) {
12219
12873
  if (!target) continue;
12220
12874
  if (seen.has(target.nodeId)) continue;
12221
12875
  seen.add(target.nodeId);
12222
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types42.EdgeType.DEPENDS_ON);
12876
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types43.EdgeType.DEPENDS_ON);
12223
12877
  if (graph.hasEdge(edgeId)) continue;
12224
12878
  const line = lineAt2(content, resource.bodyOffset + ref.index);
12225
12879
  const edge = {
12226
12880
  id: edgeId,
12227
12881
  source: resource.nodeId,
12228
12882
  target: target.nodeId,
12229
- type: import_types42.EdgeType.DEPENDS_ON,
12230
- provenance: import_types42.Provenance.EXTRACTED,
12231
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
12883
+ type: import_types43.EdgeType.DEPENDS_ON,
12884
+ provenance: import_types43.Provenance.EXTRACTED,
12885
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
12232
12886
  evidence: { file: evidenceFile, line, snippet: key }
12233
12887
  };
12234
12888
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12242,7 +12896,7 @@ async function addTerraformResources(graph, scanPath) {
12242
12896
  // src/extract/infra/k8s.ts
12243
12897
  init_cjs_shims();
12244
12898
  var import_node_fs22 = require("fs");
12245
- var import_node_path53 = __toESM(require("path"), 1);
12899
+ var import_node_path54 = __toESM(require("path"), 1);
12246
12900
  var import_yaml3 = require("yaml");
12247
12901
  var K8S_KIND_TO_INFRA_KIND = {
12248
12902
  Service: "k8s-service",
@@ -12260,11 +12914,11 @@ async function walkYamlFiles2(start, depth = 0, max = 5) {
12260
12914
  for (const entry of entries) {
12261
12915
  if (entry.isDirectory()) {
12262
12916
  if (IGNORED_DIRS.has(entry.name)) continue;
12263
- const child = import_node_path53.default.join(start, entry.name);
12917
+ const child = import_node_path54.default.join(start, entry.name);
12264
12918
  if (await isPythonVenvDir(child)) continue;
12265
12919
  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));
12920
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path54.default.extname(entry.name))) {
12921
+ out.push(import_node_path54.default.join(start, entry.name));
12268
12922
  }
12269
12923
  }
12270
12924
  return out;
@@ -12298,13 +12952,13 @@ async function addK8sResources(graph, scanPath) {
12298
12952
  // src/extract/infra/cloudflare.ts
12299
12953
  init_cjs_shims();
12300
12954
  var import_node_fs23 = require("fs");
12301
- var import_node_path54 = __toESM(require("path"), 1);
12955
+ var import_node_path55 = __toESM(require("path"), 1);
12302
12956
  var import_smol_toml2 = require("smol-toml");
12303
- var import_types43 = require("@neat.is/types");
12957
+ var import_types44 = require("@neat.is/types");
12304
12958
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
12305
12959
  async function readWranglerConfig(dir) {
12306
12960
  for (const filename of WRANGLER_FILENAMES) {
12307
- const abs = import_node_path54.default.join(dir, filename);
12961
+ const abs = import_node_path55.default.join(dir, filename);
12308
12962
  if (!await exists2(abs)) continue;
12309
12963
  const raw = await import_node_fs23.promises.readFile(abs, "utf8");
12310
12964
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12348,8 +13002,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
12348
13002
  source: anchorId,
12349
13003
  target: node.id,
12350
13004
  type: edgeType,
12351
- provenance: import_types43.Provenance.EXTRACTED,
12352
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
13005
+ provenance: import_types44.Provenance.EXTRACTED,
13006
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
12353
13007
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
12354
13008
  };
12355
13009
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12367,11 +13021,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12367
13021
  try {
12368
13022
  read = await readWranglerConfig(service.dir);
12369
13023
  } catch (err) {
12370
- recordExtractionError("infra cloudflare", import_node_path54.default.relative(scanPath, service.dir), err);
13024
+ recordExtractionError("infra cloudflare", import_node_path55.default.relative(scanPath, service.dir), err);
12371
13025
  continue;
12372
13026
  }
12373
13027
  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)));
13028
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, read.relFile)));
12375
13029
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
12376
13030
  }
12377
13031
  for (const worker of discovered) {
@@ -12383,7 +13037,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12383
13037
  }
12384
13038
  let anchorId = service.node.id;
12385
13039
  if (config.main) {
12386
- const entryRelPath = toPosix(import_node_path54.default.normalize(config.main));
13040
+ const entryRelPath = toPosix(import_node_path55.default.normalize(config.main));
12387
13041
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
12388
13042
  graph,
12389
13043
  service.pkg.name,
@@ -12410,15 +13064,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12410
13064
  nodesAdded++;
12411
13065
  }
12412
13066
  if (runtimeNode.id !== anchorId) {
12413
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types43.EdgeType.RUNS_ON);
13067
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types44.EdgeType.RUNS_ON);
12414
13068
  if (!graph.hasEdge(runsOnId)) {
12415
13069
  const edge = {
12416
13070
  id: runsOnId,
12417
13071
  source: anchorId,
12418
13072
  target: runtimeNode.id,
12419
- type: import_types43.EdgeType.RUNS_ON,
12420
- provenance: import_types43.Provenance.EXTRACTED,
12421
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
13073
+ type: import_types44.EdgeType.RUNS_ON,
13074
+ provenance: import_types44.Provenance.EXTRACTED,
13075
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
12422
13076
  evidence: {
12423
13077
  file: evidenceFile,
12424
13078
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -12432,7 +13086,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12432
13086
  const result = addResourceEdge(
12433
13087
  graph,
12434
13088
  anchorId,
12435
- import_types43.EdgeType.CONNECTS_TO,
13089
+ import_types44.EdgeType.CONNECTS_TO,
12436
13090
  "cloudflare-route",
12437
13091
  route,
12438
13092
  evidenceFile,
@@ -12456,7 +13110,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12456
13110
  const result = addResourceEdge(
12457
13111
  graph,
12458
13112
  anchorId,
12459
- import_types43.EdgeType.DEPENDS_ON,
13113
+ import_types44.EdgeType.DEPENDS_ON,
12460
13114
  group.kind,
12461
13115
  name,
12462
13116
  evidenceFile,
@@ -12470,7 +13124,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12470
13124
  const result = addResourceEdge(
12471
13125
  graph,
12472
13126
  anchorId,
12473
- import_types43.EdgeType.DEPENDS_ON,
13127
+ import_types44.EdgeType.DEPENDS_ON,
12474
13128
  "cloudflare-cron",
12475
13129
  cron,
12476
13130
  evidenceFile,
@@ -12483,7 +13137,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12483
13137
  const result = addResourceEdge(
12484
13138
  graph,
12485
13139
  anchorId,
12486
- import_types43.EdgeType.DEPENDS_ON,
13140
+ import_types44.EdgeType.DEPENDS_ON,
12487
13141
  "cloudflare-env-var",
12488
13142
  varName,
12489
13143
  evidenceFile,
@@ -12496,15 +13150,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12496
13150
  if (!svc.service) continue;
12497
13151
  const target = workerIndex.get(svc.service);
12498
13152
  if (target && target.anchorId !== anchorId) {
12499
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types43.EdgeType.CALLS);
13153
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types44.EdgeType.CALLS);
12500
13154
  if (!graph.hasEdge(edgeId)) {
12501
13155
  const edge = {
12502
13156
  id: edgeId,
12503
13157
  source: anchorId,
12504
13158
  target: target.anchorId,
12505
- type: import_types43.EdgeType.CALLS,
12506
- provenance: import_types43.Provenance.EXTRACTED,
12507
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
13159
+ type: import_types44.EdgeType.CALLS,
13160
+ provenance: import_types44.Provenance.EXTRACTED,
13161
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
12508
13162
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
12509
13163
  };
12510
13164
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -12515,7 +13169,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12515
13169
  const result = addResourceEdge(
12516
13170
  graph,
12517
13171
  anchorId,
12518
- import_types43.EdgeType.DEPENDS_ON,
13172
+ import_types44.EdgeType.DEPENDS_ON,
12519
13173
  "cloudflare-service-binding",
12520
13174
  svc.service,
12521
13175
  evidenceFile,
@@ -12531,12 +13185,12 @@ async function addCloudflareWorkers(graph, services, scanPath) {
12531
13185
  // src/extract/infra/vercel.ts
12532
13186
  init_cjs_shims();
12533
13187
  var import_node_fs24 = require("fs");
12534
- var import_node_path55 = __toESM(require("path"), 1);
12535
- var import_types44 = require("@neat.is/types");
13188
+ var import_node_path56 = __toESM(require("path"), 1);
13189
+ var import_types45 = require("@neat.is/types");
12536
13190
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
12537
13191
  async function readVercelConfig(dir) {
12538
13192
  for (const filename of VERCEL_CONFIG_FILENAMES) {
12539
- const abs = import_node_path55.default.join(dir, filename);
13193
+ const abs = import_node_path56.default.join(dir, filename);
12540
13194
  if (!await exists2(abs)) continue;
12541
13195
  const raw = await import_node_fs24.promises.readFile(abs, "utf8");
12542
13196
  const config = JSON.parse(maskCommentsInSource(raw));
@@ -12545,7 +13199,7 @@ async function readVercelConfig(dir) {
12545
13199
  return null;
12546
13200
  }
12547
13201
  async function readLinkedProjectName(dir) {
12548
- const abs = import_node_path55.default.join(dir, ".vercel", "project.json");
13202
+ const abs = import_node_path56.default.join(dir, ".vercel", "project.json");
12549
13203
  if (!await exists2(abs)) return void 0;
12550
13204
  const parsed = JSON.parse(await import_node_fs24.promises.readFile(abs, "utf8"));
12551
13205
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
@@ -12563,7 +13217,7 @@ async function addVercelServices(graph, services, scanPath) {
12563
13217
  read = await readVercelConfig(service.dir);
12564
13218
  projectName = await readLinkedProjectName(service.dir);
12565
13219
  } catch (err) {
12566
- recordExtractionError("infra vercel", import_node_path55.default.relative(scanPath, service.dir), err);
13220
+ recordExtractionError("infra vercel", import_node_path56.default.relative(scanPath, service.dir), err);
12567
13221
  continue;
12568
13222
  }
12569
13223
  if (!read && !projectName) continue;
@@ -12579,7 +13233,7 @@ async function addVercelServices(graph, services, scanPath) {
12579
13233
  const anchorId = service.node.id;
12580
13234
  if (!read) continue;
12581
13235
  const { config, relFile, raw } = read;
12582
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, relFile)));
13236
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
12583
13237
  const add = (edgeType, kind, name) => {
12584
13238
  if (!name) return;
12585
13239
  const result = emitPlatformResourceEdge(
@@ -12595,12 +13249,12 @@ async function addVercelServices(graph, services, scanPath) {
12595
13249
  nodesAdded += result.nodesAdded;
12596
13250
  edgesAdded += result.edgesAdded;
12597
13251
  };
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);
13252
+ add(import_types45.EdgeType.RUNS_ON, "vercel", "vercel");
13253
+ for (const cron of config.crons ?? []) add(import_types45.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
13254
+ for (const varName of Object.keys(config.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
13255
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
12602
13256
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
12603
- add(import_types44.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
13257
+ add(import_types45.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
12604
13258
  }
12605
13259
  }
12606
13260
  return { nodesAdded, edgesAdded };
@@ -12609,13 +13263,13 @@ async function addVercelServices(graph, services, scanPath) {
12609
13263
  // src/extract/infra/railway.ts
12610
13264
  init_cjs_shims();
12611
13265
  var import_node_fs25 = require("fs");
12612
- var import_node_path56 = __toESM(require("path"), 1);
13266
+ var import_node_path57 = __toESM(require("path"), 1);
12613
13267
  var import_smol_toml3 = require("smol-toml");
12614
- var import_types45 = require("@neat.is/types");
13268
+ var import_types46 = require("@neat.is/types");
12615
13269
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
12616
13270
  async function readRailwayConfig(dir) {
12617
13271
  for (const filename of RAILWAY_FILENAMES) {
12618
- const abs = import_node_path56.default.join(dir, filename);
13272
+ const abs = import_node_path57.default.join(dir, filename);
12619
13273
  if (!await exists2(abs)) continue;
12620
13274
  const raw = await import_node_fs25.promises.readFile(abs, "utf8");
12621
13275
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
@@ -12631,7 +13285,7 @@ async function addRailwayServices(graph, services, scanPath) {
12631
13285
  try {
12632
13286
  read = await readRailwayConfig(service.dir);
12633
13287
  } catch (err) {
12634
- recordExtractionError("infra railway", import_node_path56.default.relative(scanPath, service.dir), err);
13288
+ recordExtractionError("infra railway", import_node_path57.default.relative(scanPath, service.dir), err);
12635
13289
  continue;
12636
13290
  }
12637
13291
  if (!read) continue;
@@ -12641,7 +13295,7 @@ async function addRailwayServices(graph, services, scanPath) {
12641
13295
  }
12642
13296
  const anchorId = service.node.id;
12643
13297
  const { config, relFile, raw } = read;
12644
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
13298
+ const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
12645
13299
  const add = (edgeType, kind, name) => {
12646
13300
  if (!name) return;
12647
13301
  const result = emitPlatformResourceEdge(
@@ -12657,9 +13311,9 @@ async function addRailwayServices(graph, services, scanPath) {
12657
13311
  nodesAdded += result.nodesAdded;
12658
13312
  edgesAdded += result.edgesAdded;
12659
13313
  };
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);
13314
+ add(import_types46.EdgeType.RUNS_ON, "railway", "railway");
13315
+ add(import_types46.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
13316
+ add(import_types46.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
12663
13317
  }
12664
13318
  return { nodesAdded, edgesAdded };
12665
13319
  }
@@ -12667,12 +13321,12 @@ async function addRailwayServices(graph, services, scanPath) {
12667
13321
  // src/extract/infra/supabase.ts
12668
13322
  init_cjs_shims();
12669
13323
  var import_node_fs26 = require("fs");
12670
- var import_node_path57 = __toESM(require("path"), 1);
13324
+ var import_node_path58 = __toESM(require("path"), 1);
12671
13325
  var import_smol_toml4 = require("smol-toml");
12672
- var import_types46 = require("@neat.is/types");
13326
+ var import_types47 = require("@neat.is/types");
12673
13327
  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);
13328
+ const relFile = import_node_path58.default.join("supabase", "config.toml");
13329
+ const abs = import_node_path58.default.join(dir, relFile);
12676
13330
  if (!await exists2(abs)) return null;
12677
13331
  const raw = await import_node_fs26.promises.readFile(abs, "utf8");
12678
13332
  const config = (0, import_smol_toml4.parse)(raw);
@@ -12686,7 +13340,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12686
13340
  try {
12687
13341
  read = await readSupabaseConfig(service.dir);
12688
13342
  } catch (err) {
12689
- recordExtractionError("infra supabase", import_node_path57.default.relative(scanPath, service.dir), err);
13343
+ recordExtractionError("infra supabase", import_node_path58.default.relative(scanPath, service.dir), err);
12690
13344
  continue;
12691
13345
  }
12692
13346
  if (!read) continue;
@@ -12701,7 +13355,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
12701
13355
  });
12702
13356
  }
12703
13357
  const anchorId = service.node.id;
12704
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
13358
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, import_node_path58.default.join(service.dir, relFile)));
12705
13359
  const add = (edgeType, kind, name) => {
12706
13360
  if (!name) return;
12707
13361
  const result = emitPlatformResourceEdge(
@@ -12717,10 +13371,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
12717
13371
  nodesAdded += result.nodesAdded;
12718
13372
  edgesAdded += result.edgesAdded;
12719
13373
  };
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");
13374
+ add(import_types47.EdgeType.RUNS_ON, "supabase", "supabase");
13375
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types47.EdgeType.DEPENDS_ON, "supabase-function", fn);
13376
+ if (config.storage) add(import_types47.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
13377
+ if (config.auth) add(import_types47.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
12724
13378
  }
12725
13379
  return { nodesAdded, edgesAdded };
12726
13380
  }
@@ -12743,14 +13397,14 @@ async function addInfra(graph, scanPath, services) {
12743
13397
 
12744
13398
  // src/extract/zod-shapes.ts
12745
13399
  init_cjs_shims();
12746
- var import_node_path58 = __toESM(require("path"), 1);
12747
- var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13400
+ var import_node_path59 = __toESM(require("path"), 1);
13401
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
12748
13402
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
12749
- var import_types47 = require("@neat.is/types");
13403
+ var import_types48 = require("@neat.is/types");
12750
13404
  var ZOD_IMPORT_RE = /\bzod\b/;
12751
13405
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
12752
13406
  function parserForExt3(ext) {
12753
- const p = new import_tree_sitter15.default();
13407
+ const p = new import_tree_sitter16.default();
12754
13408
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
12755
13409
  return p;
12756
13410
  }
@@ -12838,7 +13492,7 @@ function topLevelSchemas(root) {
12838
13492
  }
12839
13493
  function zodShapesFromFile(file, serviceDir) {
12840
13494
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
12841
- const tree = parseSource3(parserForExt3(import_node_path58.default.extname(file.path)), file.content);
13495
+ const tree = parseSource3(parserForExt3(import_node_path59.default.extname(file.path)), file.content);
12842
13496
  const out = [];
12843
13497
  const seen = /* @__PURE__ */ new Set();
12844
13498
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -12852,11 +13506,11 @@ function zodShapesFromFile(file, serviceDir) {
12852
13506
  seen.add(name);
12853
13507
  const line = call.startPosition.row + 1;
12854
13508
  out.push({
12855
- infraId: (0, import_types47.infraId)("zod-schema", name),
13509
+ infraId: (0, import_types48.infraId)("zod-schema", name),
12856
13510
  name,
12857
13511
  fields,
12858
13512
  evidence: {
12859
- file: import_node_path58.default.relative(serviceDir, file.path),
13513
+ file: import_node_path59.default.relative(serviceDir, file.path),
12860
13514
  line,
12861
13515
  snippet: snippet(file.content, line)
12862
13516
  }
@@ -12887,7 +13541,7 @@ async function addZodShapes(graph, services) {
12887
13541
  if (!graph.hasNode(shape.infraId)) {
12888
13542
  const node = {
12889
13543
  id: shape.infraId,
12890
- type: import_types47.NodeType.InfraNode,
13544
+ type: import_types48.NodeType.InfraNode,
12891
13545
  name: shape.name,
12892
13546
  provider: "self",
12893
13547
  kind: "zod-schema"
@@ -12897,14 +13551,14 @@ async function addZodShapes(graph, services) {
12897
13551
  }
12898
13552
  if (shape.fields.length > 0) {
12899
13553
  const node = graph.getNodeAttributes(shape.infraId);
12900
- if (node.type === import_types47.NodeType.InfraNode) {
13554
+ if (node.type === import_types48.NodeType.InfraNode) {
12901
13555
  graph.replaceNodeAttributes(shape.infraId, {
12902
13556
  ...node,
12903
13557
  columns: foldColumns(
12904
13558
  node.columns,
12905
13559
  shape.fields,
12906
- import_types47.Provenance.EXTRACTED,
12907
- (0, import_types47.confidenceForExtracted)("structural")
13560
+ import_types48.Provenance.EXTRACTED,
13561
+ (0, import_types48.confidenceForExtracted)("structural")
12908
13562
  )
12909
13563
  });
12910
13564
  }
@@ -12918,15 +13572,15 @@ async function addZodShapes(graph, services) {
12918
13572
  );
12919
13573
  nodesAdded += n;
12920
13574
  edgesAdded += e;
12921
- const edgeId = (0, import_types47.extractedEdgeId)(fileNodeId, shape.infraId, import_types47.EdgeType.CONTAINS);
13575
+ const edgeId = (0, import_types48.extractedEdgeId)(fileNodeId, shape.infraId, import_types48.EdgeType.CONTAINS);
12922
13576
  if (!graph.hasEdge(edgeId)) {
12923
13577
  const edge = {
12924
13578
  id: edgeId,
12925
13579
  source: fileNodeId,
12926
13580
  target: shape.infraId,
12927
- type: import_types47.EdgeType.CONTAINS,
12928
- provenance: import_types47.Provenance.EXTRACTED,
12929
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
13581
+ type: import_types48.EdgeType.CONTAINS,
13582
+ provenance: import_types48.Provenance.EXTRACTED,
13583
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
12930
13584
  evidence: shape.evidence
12931
13585
  };
12932
13586
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -12940,7 +13594,7 @@ async function addZodShapes(graph, services) {
12940
13594
 
12941
13595
  // src/extract/firestore-rules.ts
12942
13596
  init_cjs_shims();
12943
- var import_types48 = require("@neat.is/types");
13597
+ var import_types49 = require("@neat.is/types");
12944
13598
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
12945
13599
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
12946
13600
  function stripComments(src) {
@@ -13080,7 +13734,7 @@ async function addFirestoreRules(graph, services) {
13080
13734
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
13081
13735
  graph.forEachNode((id, attrs) => {
13082
13736
  const node = attrs;
13083
- if (node.type !== import_types48.NodeType.InfraNode) return;
13737
+ if (node.type !== import_types49.NodeType.InfraNode) return;
13084
13738
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
13085
13739
  const fields = guards.get(collectionKeyFromName(node.name));
13086
13740
  if (!fields || fields.size === 0) return;
@@ -13093,17 +13747,17 @@ async function addFirestoreRules(graph, services) {
13093
13747
  }
13094
13748
 
13095
13749
  // src/extract/index.ts
13096
- var import_node_path60 = __toESM(require("path"), 1);
13750
+ var import_node_path61 = __toESM(require("path"), 1);
13097
13751
 
13098
13752
  // src/extract/retire.ts
13099
13753
  init_cjs_shims();
13100
13754
  var import_node_fs27 = require("fs");
13101
- var import_node_path59 = __toESM(require("path"), 1);
13102
- var import_types49 = require("@neat.is/types");
13755
+ var import_node_path60 = __toESM(require("path"), 1);
13756
+ var import_types50 = require("@neat.is/types");
13103
13757
  function dropOrphanedFileNodes(graph) {
13104
13758
  const orphans = [];
13105
13759
  graph.forEachNode((id, attrs) => {
13106
- if (attrs.type !== import_types49.NodeType.FileNode) return;
13760
+ if (attrs.type !== import_types50.NodeType.FileNode) return;
13107
13761
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
13108
13762
  orphans.push(id);
13109
13763
  }
@@ -13116,14 +13770,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
13116
13770
  const bases = [scanPath, ...serviceDirs];
13117
13771
  graph.forEachEdge((id, attrs) => {
13118
13772
  const edge = attrs;
13119
- if (edge.provenance !== import_types49.Provenance.EXTRACTED) return;
13773
+ if (edge.provenance !== import_types50.Provenance.EXTRACTED) return;
13120
13774
  const evidenceFile = edge.evidence?.file;
13121
13775
  if (!evidenceFile) return;
13122
- if (import_node_path59.default.isAbsolute(evidenceFile)) {
13776
+ if (import_node_path60.default.isAbsolute(evidenceFile)) {
13123
13777
  if (!(0, import_node_fs27.existsSync)(evidenceFile)) toDrop.push(id);
13124
13778
  return;
13125
13779
  }
13126
- const found = bases.some((base) => (0, import_node_fs27.existsSync)(import_node_path59.default.join(base, evidenceFile)));
13780
+ const found = bases.some((base) => (0, import_node_fs27.existsSync)(import_node_path60.default.join(base, evidenceFile)));
13127
13781
  if (!found) toDrop.push(id);
13128
13782
  });
13129
13783
  for (const id of toDrop) graph.dropEdge(id);
@@ -13180,7 +13834,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
13180
13834
  }
13181
13835
  const droppedEntries = drainDroppedExtracted();
13182
13836
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
13183
- const rejectedPath = import_node_path60.default.join(import_node_path60.default.dirname(opts.errorsPath), "rejected.ndjson");
13837
+ const rejectedPath = import_node_path61.default.join(import_node_path61.default.dirname(opts.errorsPath), "rejected.ndjson");
13184
13838
  try {
13185
13839
  await writeRejectedExtracted(droppedEntries, rejectedPath);
13186
13840
  } catch (err) {
@@ -13292,8 +13946,8 @@ function canonicalJson(value) {
13292
13946
  // src/persist.ts
13293
13947
  init_cjs_shims();
13294
13948
  var import_node_fs29 = require("fs");
13295
- var import_node_path61 = __toESM(require("path"), 1);
13296
- var import_types50 = require("@neat.is/types");
13949
+ var import_node_path62 = __toESM(require("path"), 1);
13950
+ var import_types51 = require("@neat.is/types");
13297
13951
  var SCHEMA_VERSION = 7;
13298
13952
  function migrateV1ToV2(payload) {
13299
13953
  const nodes = payload.graph.nodes;
@@ -13317,7 +13971,7 @@ function migrateV5ToV6(payload) {
13317
13971
  if (Array.isArray(nodes)) {
13318
13972
  for (const node of nodes) {
13319
13973
  const attrs = node.attributes;
13320
- if (!attrs || attrs.type !== import_types50.NodeType.InfraNode) continue;
13974
+ if (!attrs || attrs.type !== import_types51.NodeType.InfraNode) continue;
13321
13975
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
13322
13976
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
13323
13977
  }
@@ -13333,12 +13987,12 @@ function migrateV2ToV3(payload) {
13333
13987
  for (const edge of edges) {
13334
13988
  const attrs = edge.attributes;
13335
13989
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
13336
- attrs.provenance = import_types50.Provenance.OBSERVED;
13990
+ attrs.provenance = import_types51.Provenance.OBSERVED;
13337
13991
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
13338
13992
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
13339
13993
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
13340
13994
  if (type && source && target) {
13341
- const newId = (0, import_types50.observedEdgeId)(source, target, type);
13995
+ const newId = (0, import_types51.observedEdgeId)(source, target, type);
13342
13996
  attrs.id = newId;
13343
13997
  if (edge.key) edge.key = newId;
13344
13998
  }
@@ -13347,7 +14001,7 @@ function migrateV2ToV3(payload) {
13347
14001
  return { ...payload, schemaVersion: 3 };
13348
14002
  }
13349
14003
  async function ensureDir(filePath) {
13350
- await import_node_fs29.promises.mkdir(import_node_path61.default.dirname(filePath), { recursive: true });
14004
+ await import_node_fs29.promises.mkdir(import_node_path62.default.dirname(filePath), { recursive: true });
13351
14005
  }
13352
14006
  async function saveGraphToDisk(graph, outPath) {
13353
14007
  await ensureDir(outPath);
@@ -13437,23 +14091,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
13437
14091
 
13438
14092
  // src/projects.ts
13439
14093
  init_cjs_shims();
13440
- var import_node_path62 = __toESM(require("path"), 1);
14094
+ var import_node_path63 = __toESM(require("path"), 1);
13441
14095
  function pathsForProject(project, baseDir) {
13442
14096
  if (project === DEFAULT_PROJECT) {
13443
14097
  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")
14098
+ snapshotPath: import_node_path63.default.join(baseDir, "graph.json"),
14099
+ errorsPath: import_node_path63.default.join(baseDir, "errors.ndjson"),
14100
+ staleEventsPath: import_node_path63.default.join(baseDir, "stale-events.ndjson"),
14101
+ embeddingsCachePath: import_node_path63.default.join(baseDir, "embeddings.json"),
14102
+ policyViolationsPath: import_node_path63.default.join(baseDir, "policy-violations.ndjson")
13449
14103
  };
13450
14104
  }
13451
14105
  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`)
14106
+ snapshotPath: import_node_path63.default.join(baseDir, `${project}.json`),
14107
+ errorsPath: import_node_path63.default.join(baseDir, `errors.${project}.ndjson`),
14108
+ staleEventsPath: import_node_path63.default.join(baseDir, `stale-events.${project}.ndjson`),
14109
+ embeddingsCachePath: import_node_path63.default.join(baseDir, `embeddings.${project}.json`),
14110
+ policyViolationsPath: import_node_path63.default.join(baseDir, `policy-violations.${project}.ndjson`)
13457
14111
  };
13458
14112
  }
13459
14113
  var Projects = class {
@@ -13495,18 +14149,18 @@ function parseExtraProjects(raw) {
13495
14149
  init_cjs_shims();
13496
14150
  var import_node_fs30 = require("fs");
13497
14151
  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");
14152
+ var import_node_path64 = __toESM(require("path"), 1);
14153
+ var import_types52 = require("@neat.is/types");
13500
14154
  function neatHome() {
13501
14155
  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");
14156
+ if (override && override.length > 0) return import_node_path64.default.resolve(override);
14157
+ return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
13504
14158
  }
13505
14159
  function registryPath() {
13506
- return import_node_path63.default.join(neatHome(), "projects.json");
14160
+ return import_node_path64.default.join(neatHome(), "projects.json");
13507
14161
  }
13508
14162
  function daemonsDir() {
13509
- return import_node_path63.default.join(neatHome(), "daemons");
14163
+ return import_node_path64.default.join(neatHome(), "daemons");
13510
14164
  }
13511
14165
  function isFiniteInt(v) {
13512
14166
  return typeof v === "number" && Number.isFinite(v);
@@ -13547,7 +14201,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
13547
14201
  const out = [];
13548
14202
  for (const name of names) {
13549
14203
  if (!name.endsWith(".json")) continue;
13550
- const file = import_node_path63.default.join(dir, name);
14204
+ const file = import_node_path64.default.join(dir, name);
13551
14205
  let raw;
13552
14206
  try {
13553
14207
  raw = await import_node_fs30.promises.readFile(file, "utf8");
@@ -13586,7 +14240,7 @@ async function readRegistry() {
13586
14240
  throw err;
13587
14241
  }
13588
14242
  const parsed = JSON.parse(raw);
13589
- return import_types51.RegistryFileSchema.parse(parsed);
14243
+ return import_types52.RegistryFileSchema.parse(parsed);
13590
14244
  }
13591
14245
  async function getProject(name) {
13592
14246
  const reg = await readRegistry();
@@ -13661,7 +14315,7 @@ init_auth();
13661
14315
  // src/connectors-config.ts
13662
14316
  init_cjs_shims();
13663
14317
  var import_node_os4 = __toESM(require("os"), 1);
13664
- var import_node_path64 = __toESM(require("path"), 1);
14318
+ var import_node_path65 = __toESM(require("path"), 1);
13665
14319
  var import_node_fs31 = require("fs");
13666
14320
  var CONNECTORS_CONFIG_VERSION = 1;
13667
14321
  var EnvRefUnsetError = class extends Error {
@@ -13676,11 +14330,11 @@ var EnvRefUnsetError = class extends Error {
13676
14330
  };
13677
14331
  function neatHome2() {
13678
14332
  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");
14333
+ if (override && override.length > 0) return import_node_path65.default.resolve(override);
14334
+ return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
13681
14335
  }
13682
14336
  function connectorsConfigPath(home = neatHome2()) {
13683
- return import_node_path64.default.join(home, "connectors.json");
14337
+ return import_node_path65.default.join(home, "connectors.json");
13684
14338
  }
13685
14339
  var MODE_MASK_LOOSER_THAN_0600 = 63;
13686
14340
  async function warnIfModeLooserThan0600(file) {
@@ -13867,15 +14521,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
13867
14521
 
13868
14522
  // src/connectors/index.ts
13869
14523
  init_cjs_shims();
13870
- var import_types52 = require("@neat.is/types");
14524
+ var import_types53 = require("@neat.is/types");
13871
14525
  var NO_ENV = "unknown";
13872
14526
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
13873
14527
  if (!graph.hasNode(targetNodeId)) return void 0;
13874
14528
  const sites = [];
13875
14529
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
13876
14530
  const edge = graph.getEdgeAttributes(edgeId);
13877
- if (edge.provenance !== import_types52.Provenance.EXTRACTED) continue;
13878
- const parsed = (0, import_types52.parseFileId)(edge.source);
14531
+ if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
14532
+ const parsed = (0, import_types53.parseFileId)(edge.source);
13879
14533
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
13880
14534
  const site = { relPath: edge.evidence.file };
13881
14535
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -13886,7 +14540,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
13886
14540
  function routeCallSiteFor(graph, targetNodeId) {
13887
14541
  if (!graph.hasNode(targetNodeId)) return void 0;
13888
14542
  const attrs = graph.getNodeAttributes(targetNodeId);
13889
- if (attrs.type !== import_types52.NodeType.RouteNode || !attrs.path) return void 0;
14543
+ if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
13890
14544
  const site = { relPath: attrs.path };
13891
14545
  if (attrs.line !== void 0) site.line = attrs.line;
13892
14546
  return site;
@@ -14326,10 +14980,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
14326
14980
  // src/connectors/supabase/map.ts
14327
14981
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
14328
14982
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
14329
- function targetFromRestPath(path67) {
14330
- const rpcMatch = REST_RPC_PATH_RE.exec(path67);
14983
+ function targetFromRestPath(path68) {
14984
+ const rpcMatch = REST_RPC_PATH_RE.exec(path68);
14331
14985
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
14332
- const tableMatch = REST_TABLE_PATH_RE.exec(path67);
14986
+ const tableMatch = REST_TABLE_PATH_RE.exec(path68);
14333
14987
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
14334
14988
  return null;
14335
14989
  }
@@ -14440,23 +15094,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
14440
15094
 
14441
15095
  // src/connectors/supabase/resolve.ts
14442
15096
  init_cjs_shims();
14443
- var import_types54 = require("@neat.is/types");
15097
+ var import_types55 = require("@neat.is/types");
14444
15098
  function createSupabaseResolveTarget(graph, config) {
14445
15099
  return (signal, _ctx) => {
14446
15100
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
14447
15101
  return null;
14448
15102
  }
14449
- const subResourceId = (0, import_types54.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15103
+ const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
14450
15104
  if (graph.hasNode(subResourceId)) {
14451
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15105
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14452
15106
  }
14453
- const bareResourceId = (0, import_types54.infraId)(signal.targetKind, signal.targetName);
15107
+ const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
14454
15108
  if (graph.hasNode(bareResourceId)) {
14455
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15109
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14456
15110
  }
14457
- const projectLevelId = (0, import_types54.infraId)("supabase", config.nodeRef);
15111
+ const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
14458
15112
  if (graph.hasNode(projectLevelId)) {
14459
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types54.EdgeType.CALLS };
15113
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
14460
15114
  }
14461
15115
  return null;
14462
15116
  };
@@ -14549,7 +15203,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
14549
15203
 
14550
15204
  // src/connectors/railway/index.ts
14551
15205
  init_cjs_shims();
14552
- var import_types58 = require("@neat.is/types");
15206
+ var import_types59 = require("@neat.is/types");
14553
15207
 
14554
15208
  // src/connectors/railway/client.ts
14555
15209
  init_cjs_shims();
@@ -14700,7 +15354,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
14700
15354
  const out = [];
14701
15355
  graph.forEachNode((_id, attrs) => {
14702
15356
  const node = attrs;
14703
- if (node.type !== import_types58.NodeType.RouteNode) return;
15357
+ if (node.type !== import_types59.NodeType.RouteNode) return;
14704
15358
  const route = attrs;
14705
15359
  if (route.service !== serviceName) return;
14706
15360
  out.push({
@@ -14804,12 +15458,12 @@ function createRailwayResolveTarget(config) {
14804
15458
  const serviceName = config.serviceNameById[config.serviceId];
14805
15459
  if (!serviceName) return null;
14806
15460
  if (signal.targetKind === ROUTE_TARGET_KIND) {
14807
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types58.EdgeType.CALLS };
15461
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
14808
15462
  }
14809
15463
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
14810
15464
  const peerName = config.serviceNameById[signal.targetName];
14811
15465
  if (!peerName) return null;
14812
- return { targetNodeId: (0, import_types58.serviceId)(peerName), serviceName, edgeType: import_types58.EdgeType.CONNECTS_TO };
15466
+ return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
14813
15467
  }
14814
15468
  return null;
14815
15469
  };
@@ -14933,9 +15587,9 @@ function parseFirebaseTargetName(targetName) {
14933
15587
  const secondSep = rest.indexOf(FIELD_SEP);
14934
15588
  if (secondSep === -1) return null;
14935
15589
  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 };
15590
+ const path68 = rest.slice(secondSep + 1);
15591
+ if (!resourceName || !method || !path68) return null;
15592
+ return { resourceName, method, path: path68 };
14939
15593
  }
14940
15594
  function resourceNameFor(type, labels) {
14941
15595
  if (!labels) return null;
@@ -14973,14 +15627,14 @@ function mapLogEntryToSignal(entry) {
14973
15627
  if (!req) return null;
14974
15628
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
14975
15629
  const method = req.requestMethod.toUpperCase();
14976
- const path67 = pathFromRequestUrl(req.requestUrl);
14977
- if (path67 === null) return null;
15630
+ const path68 = pathFromRequestUrl(req.requestUrl);
15631
+ if (path68 === null) return null;
14978
15632
  const timestamp = entry.timestamp;
14979
15633
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
14980
15634
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
14981
15635
  return {
14982
15636
  targetKind: resourceType,
14983
- targetName: packFirebaseTargetName({ resourceName, method, path: path67 }),
15637
+ targetName: packFirebaseTargetName({ resourceName, method, path: path68 }),
14984
15638
  callCount: 1,
14985
15639
  errorCount: isError ? 1 : 0,
14986
15640
  lastObservedIso: timestamp
@@ -14997,7 +15651,7 @@ function mapLogEntriesToSignals(entries) {
14997
15651
 
14998
15652
  // src/connectors/firebase/resolve.ts
14999
15653
  init_cjs_shims();
15000
- var import_types59 = require("@neat.is/types");
15654
+ var import_types60 = require("@neat.is/types");
15001
15655
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
15002
15656
  switch (resourceType) {
15003
15657
  case "cloud_function":
@@ -15012,7 +15666,7 @@ function routeEntriesFor(graph, serviceName) {
15012
15666
  const entries = [];
15013
15667
  graph.forEachNode((_id, attrs) => {
15014
15668
  const node = attrs;
15015
- if (node.type !== import_types59.NodeType.RouteNode) return;
15669
+ if (node.type !== import_types60.NodeType.RouteNode) return;
15016
15670
  const route = attrs;
15017
15671
  if (route.service !== serviceName) return;
15018
15672
  entries.push({
@@ -15044,7 +15698,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
15044
15698
  return {
15045
15699
  targetNodeId: match.routeNodeId,
15046
15700
  serviceName,
15047
- edgeType: import_types59.EdgeType.CALLS
15701
+ edgeType: import_types60.EdgeType.CALLS
15048
15702
  };
15049
15703
  };
15050
15704
  }
@@ -15071,7 +15725,7 @@ init_cjs_shims();
15071
15725
 
15072
15726
  // src/connectors/cloudflare/connector.ts
15073
15727
  init_cjs_shims();
15074
- var import_types61 = require("@neat.is/types");
15728
+ var import_types62 = require("@neat.is/types");
15075
15729
 
15076
15730
  // src/connectors/cloudflare/client.ts
15077
15731
  init_cjs_shims();
@@ -15187,7 +15841,7 @@ function mapEventToSignal(event) {
15187
15841
  if (Number.isNaN(observedAt.getTime())) return null;
15188
15842
  const statusCode = metadata?.statusCode;
15189
15843
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
15190
- const path67 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15844
+ const path68 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
15191
15845
  return {
15192
15846
  targetKind: CLOUDFLARE_TARGET_KIND,
15193
15847
  targetName: scriptName,
@@ -15195,7 +15849,7 @@ function mapEventToSignal(event) {
15195
15849
  errorCount: isError ? 1 : 0,
15196
15850
  lastObservedIso: observedAt.toISOString(),
15197
15851
  method,
15198
- ...path67 ? { path: path67 } : {},
15852
+ ...path68 ? { path: path68 } : {},
15199
15853
  ...typeof statusCode === "number" ? { statusCode } : {},
15200
15854
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
15201
15855
  };
@@ -15235,19 +15889,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
15235
15889
  graph.forEachNode((id, attrs) => {
15236
15890
  if (found) return;
15237
15891
  const a = attrs;
15238
- if (a.type === import_types61.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15892
+ if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
15239
15893
  found = id;
15240
15894
  }
15241
15895
  });
15242
15896
  return found;
15243
15897
  }
15244
- function findMatchingRouteNode(graph, serviceName, method, path67) {
15245
- const normalizedPath = normalizePathTemplate(path67);
15898
+ function findMatchingRouteNode(graph, serviceName, method, path68) {
15899
+ const normalizedPath = normalizePathTemplate(path68);
15246
15900
  let found = null;
15247
15901
  graph.forEachNode((id, attrs) => {
15248
15902
  if (found) return;
15249
15903
  const a = attrs;
15250
- if (a.type !== import_types61.NodeType.RouteNode || a.service !== serviceName) return;
15904
+ if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
15251
15905
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
15252
15906
  const routeMethod = (a.method ?? "").toUpperCase();
15253
15907
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -15259,18 +15913,18 @@ function createCloudflareResolveTarget(config, graph) {
15259
15913
  return (signal) => {
15260
15914
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
15261
15915
  const scriptName = signal.targetName;
15262
- const { method, path: path67 } = signal;
15916
+ const { method, path: path68 } = signal;
15263
15917
  const resolveRouteGrain = (serviceName, wholeFileId) => {
15264
- if (!method || !path67) return wholeFileId;
15265
- return findMatchingRouteNode(graph, serviceName, method, path67) ?? wholeFileId;
15918
+ if (!method || !path68) return wholeFileId;
15919
+ return findMatchingRouteNode(graph, serviceName, method, path68) ?? wholeFileId;
15266
15920
  };
15267
15921
  const mapping = config.workers?.[scriptName];
15268
15922
  if (mapping) {
15269
- const wholeFileId = (0, import_types61.fileId)(mapping.service, mapping.entryFile);
15923
+ const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
15270
15924
  return {
15271
15925
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
15272
15926
  serviceName: mapping.service,
15273
- edgeType: import_types61.EdgeType.CALLS
15927
+ edgeType: import_types62.EdgeType.CALLS
15274
15928
  };
15275
15929
  }
15276
15930
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -15279,13 +15933,13 @@ function createCloudflareResolveTarget(config, graph) {
15279
15933
  return {
15280
15934
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
15281
15935
  serviceName: fileNode.service,
15282
- edgeType: import_types61.EdgeType.CALLS
15936
+ edgeType: import_types62.EdgeType.CALLS
15283
15937
  };
15284
15938
  }
15285
15939
  return {
15286
- targetNodeId: (0, import_types61.infraId)("cloudflare-worker", scriptName),
15940
+ targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
15287
15941
  serviceName: scriptName,
15288
- edgeType: import_types61.EdgeType.CALLS,
15942
+ edgeType: import_types62.EdgeType.CALLS,
15289
15943
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
15290
15944
  };
15291
15945
  };
@@ -15481,14 +16135,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
15481
16135
 
15482
16136
  // src/connectors/neon/resolve.ts
15483
16137
  init_cjs_shims();
15484
- var import_types65 = require("@neat.is/types");
16138
+ var import_types66 = require("@neat.is/types");
15485
16139
  function createNeonResolveTarget(config) {
15486
16140
  return (signal) => {
15487
16141
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
15488
16142
  return {
15489
- targetNodeId: (0, import_types65.infraId)("sql-table", signal.targetName),
16143
+ targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
15490
16144
  serviceName: config.serviceName,
15491
- edgeType: import_types65.EdgeType.CALLS,
16145
+ edgeType: import_types66.EdgeType.CALLS,
15492
16146
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
15493
16147
  };
15494
16148
  };
@@ -15614,9 +16268,9 @@ function parseCloudRunTargetName(targetName) {
15614
16268
  const secondSep = rest.indexOf(FIELD_SEP2);
15615
16269
  if (secondSep === -1) return null;
15616
16270
  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 };
16271
+ const path68 = rest.slice(secondSep + 1);
16272
+ if (!serviceName || !method || !path68) return null;
16273
+ return { serviceName, method, path: path68 };
15620
16274
  }
15621
16275
 
15622
16276
  // src/connectors/cloud-run/map.ts
@@ -15645,14 +16299,14 @@ function mapLogEntryToSignal2(entry) {
15645
16299
  if (!req) return null;
15646
16300
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
15647
16301
  const method = req.requestMethod.toUpperCase();
15648
- const path67 = pathFromRequestUrl2(req.requestUrl);
15649
- if (path67 === null) return null;
16302
+ const path68 = pathFromRequestUrl2(req.requestUrl);
16303
+ if (path68 === null) return null;
15650
16304
  const timestamp = entry.timestamp;
15651
16305
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
15652
16306
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
15653
16307
  return {
15654
16308
  targetKind: CLOUD_RUN_TARGET_KIND,
15655
- targetName: packCloudRunTargetName({ serviceName, method, path: path67 }),
16309
+ targetName: packCloudRunTargetName({ serviceName, method, path: path68 }),
15656
16310
  callCount: 1,
15657
16311
  errorCount: isError ? 1 : 0,
15658
16312
  lastObservedIso: timestamp
@@ -15669,14 +16323,14 @@ function mapLogEntriesToSignals2(entries) {
15669
16323
 
15670
16324
  // src/connectors/cloud-run/resolve.ts
15671
16325
  init_cjs_shims();
15672
- var import_types69 = require("@neat.is/types");
16326
+ var import_types70 = require("@neat.is/types");
15673
16327
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
15674
16328
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
15675
16329
  let found = null;
15676
16330
  graph.forEachNode((_id, attrs) => {
15677
16331
  if (found) return;
15678
16332
  const node = attrs;
15679
- if (node.type !== import_types69.NodeType.RouteNode) return;
16333
+ if (node.type !== import_types70.NodeType.RouteNode) return;
15680
16334
  const route = attrs;
15681
16335
  if (route.service !== serviceName || !route.pathTemplate) return;
15682
16336
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -15691,23 +16345,23 @@ function createCloudRunResolveTarget(graph, config) {
15691
16345
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
15692
16346
  const identity = parseCloudRunTargetName(signal.targetName);
15693
16347
  if (!identity) return null;
15694
- const { serviceName: gcpServiceName, method, path: path67 } = identity;
16348
+ const { serviceName: gcpServiceName, method, path: path68 } = identity;
15695
16349
  const mappedService = config.serviceMap?.[gcpServiceName];
15696
16350
  if (mappedService) {
15697
16351
  const routeNodeId = findMatchingRouteNode2(
15698
16352
  graph,
15699
16353
  mappedService,
15700
16354
  method,
15701
- normalizePathTemplate(path67)
16355
+ normalizePathTemplate(path68)
15702
16356
  );
15703
16357
  if (routeNodeId) {
15704
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types69.EdgeType.CALLS };
16358
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
15705
16359
  }
15706
16360
  }
15707
16361
  return {
15708
- targetNodeId: (0, import_types69.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16362
+ targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
15709
16363
  serviceName: mappedService ?? gcpServiceName,
15710
- edgeType: import_types69.EdgeType.CALLS,
16364
+ edgeType: import_types70.EdgeType.CALLS,
15711
16365
  ensureInfraNode: {
15712
16366
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
15713
16367
  name: gcpServiceName,
@@ -15748,7 +16402,7 @@ function createCloudRunConnector(graph, config = {}) {
15748
16402
 
15749
16403
  // src/connectors/render/index.ts
15750
16404
  init_cjs_shims();
15751
- var import_types72 = require("@neat.is/types");
16405
+ var import_types73 = require("@neat.is/types");
15752
16406
 
15753
16407
  // src/connectors/render/types.ts
15754
16408
  init_cjs_shims();
@@ -15826,7 +16480,7 @@ function buildRenderRouteIndex(graph, serviceName) {
15826
16480
  const out = [];
15827
16481
  graph.forEachNode((_id, attrs) => {
15828
16482
  const node = attrs;
15829
- if (node.type !== import_types72.NodeType.RouteNode) return;
16483
+ if (node.type !== import_types73.NodeType.RouteNode) return;
15830
16484
  const route = attrs;
15831
16485
  if (route.service !== serviceName) return;
15832
16486
  out.push({
@@ -15911,7 +16565,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
15911
16565
  function createRenderResolveTarget(config) {
15912
16566
  return (signal) => {
15913
16567
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
15914
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types72.EdgeType.CALLS };
16568
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
15915
16569
  }
15916
16570
  return null;
15917
16571
  };
@@ -16049,21 +16703,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
16049
16703
 
16050
16704
  // src/connectors/planetscale/resolve.ts
16051
16705
  init_cjs_shims();
16052
- var import_types76 = require("@neat.is/types");
16706
+ var import_types77 = require("@neat.is/types");
16053
16707
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
16054
16708
  function createPlanetscaleResolveTarget(graph, config) {
16055
16709
  const databaseName = `${config.organization}/${config.database}`;
16056
16710
  return (signal, _ctx) => {
16057
16711
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16058
- const tableId = (0, import_types76.infraId)("sql-table", signal.targetName);
16712
+ const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
16059
16713
  if (graph.hasNode(tableId)) {
16060
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types76.EdgeType.CALLS };
16714
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
16061
16715
  }
16062
- const providerId = (0, import_types76.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16716
+ const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
16063
16717
  return {
16064
16718
  targetNodeId: providerId,
16065
16719
  serviceName: config.serviceName,
16066
- edgeType: import_types76.EdgeType.CALLS,
16720
+ edgeType: import_types77.EdgeType.CALLS,
16067
16721
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
16068
16722
  };
16069
16723
  };
@@ -16691,11 +17345,11 @@ function registerRoutes(scope, ctx) {
16691
17345
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
16692
17346
  const parsed = [];
16693
17347
  for (const c of candidates) {
16694
- const r = import_types79.DivergenceTypeSchema.safeParse(c);
17348
+ const r = import_types80.DivergenceTypeSchema.safeParse(c);
16695
17349
  if (!r.success) {
16696
17350
  return reply.code(400).send({
16697
17351
  error: `unknown divergence type "${c}"`,
16698
- allowed: import_types79.DivergenceTypeSchema.options
17352
+ allowed: import_types80.DivergenceTypeSchema.options
16699
17353
  });
16700
17354
  }
16701
17355
  parsed.push(r.data);
@@ -17004,7 +17658,7 @@ function registerRoutes(scope, ctx) {
17004
17658
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
17005
17659
  let violations = await log.readAll();
17006
17660
  if (req.query.severity) {
17007
- const sev = import_types79.PolicySeveritySchema.safeParse(req.query.severity);
17661
+ const sev = import_types80.PolicySeveritySchema.safeParse(req.query.severity);
17008
17662
  if (!sev.success) {
17009
17663
  return reply.code(400).send({
17010
17664
  error: "invalid severity",
@@ -17043,7 +17697,7 @@ function registerRoutes(scope, ctx) {
17043
17697
  scope.post("/policies/check", async (req, reply) => {
17044
17698
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
17045
17699
  if (!proj) return;
17046
- const parsed = import_types79.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17700
+ const parsed = import_types80.PoliciesCheckBodySchema.safeParse(req.body ?? {});
17047
17701
  if (!parsed.success) {
17048
17702
  return reply.code(400).send({
17049
17703
  error: "invalid /policies/check body",
@@ -17365,7 +18019,7 @@ init_otel_grpc();
17365
18019
  // src/search.ts
17366
18020
  init_cjs_shims();
17367
18021
  var import_node_fs32 = require("fs");
17368
- var import_node_path65 = __toESM(require("path"), 1);
18022
+ var import_node_path66 = __toESM(require("path"), 1);
17369
18023
  var import_node_crypto4 = require("crypto");
17370
18024
  var DEFAULT_LIMIT = 10;
17371
18025
  var NOMIC_DIM = 768;
@@ -17528,7 +18182,7 @@ async function readCache(cachePath) {
17528
18182
  }
17529
18183
  }
17530
18184
  async function writeCache(cachePath, cache) {
17531
- await import_node_fs32.promises.mkdir(import_node_path65.default.dirname(cachePath), { recursive: true });
18185
+ await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(cachePath), { recursive: true });
17532
18186
  await import_node_fs32.promises.writeFile(cachePath, JSON.stringify(cache));
17533
18187
  }
17534
18188
  var VectorIndex = class {
@@ -17709,14 +18363,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
17709
18363
  async function main() {
17710
18364
  const baseDirEnv = process.env.NEAT_OUT_DIR;
17711
18365
  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");
18366
+ 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");
18367
+ const defaultScanPath = import_node_path67.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
17714
18368
  const registry = new Projects();
17715
18369
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
17716
18370
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
17717
18371
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
17718
18372
  const projectScan = process.env[envKey];
17719
- await bootProject(registry, name, projectScan ? import_node_path66.default.resolve(projectScan) : void 0, baseDir);
18373
+ await bootProject(registry, name, projectScan ? import_node_path67.default.resolve(projectScan) : void 0, baseDir);
17720
18374
  }
17721
18375
  const host = process.env.HOST ?? "0.0.0.0";
17722
18376
  const port = Number(process.env.PORT ?? 8080);