@neat.is/core 0.9.1-dev.20260819 → 0.9.2-dev.20260821

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 path74 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
- if (exactUnauthPaths.has(path74) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path74)) {
62
+ const path76 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
+ if (exactUnauthPaths.has(path76) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path76)) {
64
64
  done();
65
65
  return;
66
66
  }
@@ -192,8 +192,8 @@ function reshapeGrpcRequest(req) {
192
192
  };
193
193
  }
194
194
  function resolveProtoRoot() {
195
- const here = import_node_path53.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
- return import_node_path53.default.resolve(here, "..", "proto");
195
+ const here = import_node_path54.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
+ return import_node_path54.default.resolve(here, "..", "proto");
197
197
  }
198
198
  function loadTraceService() {
199
199
  const protoRoot = resolveProtoRoot();
@@ -261,13 +261,13 @@ async function startOtelGrpcReceiver(opts) {
261
261
  })
262
262
  };
263
263
  }
264
- var import_node_url, import_node_path53, import_node_crypto2, grpc, protoLoader;
264
+ var import_node_url, import_node_path54, import_node_crypto2, grpc, protoLoader;
265
265
  var init_otel_grpc = __esm({
266
266
  "src/otel-grpc.ts"() {
267
267
  "use strict";
268
268
  init_cjs_shims();
269
269
  import_node_url = require("url");
270
- import_node_path53 = __toESM(require("path"), 1);
270
+ import_node_path54 = __toESM(require("path"), 1);
271
271
  import_node_crypto2 = require("crypto");
272
272
  grpc = __toESM(require("@grpc/grpc-js"), 1);
273
273
  protoLoader = __toESM(require("@grpc/proto-loader"), 1);
@@ -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 path74 = q === -1 ? v : v.slice(0, q);
417
- if (path74.length > 0) return path74;
416
+ const path76 = q === -1 ? v : v.slice(0, q);
417
+ if (path76.length > 0) return path76;
418
418
  }
419
419
  }
420
420
  return void 0;
@@ -476,10 +476,10 @@ function parseOtlpRequest(body) {
476
476
  return out;
477
477
  }
478
478
  function loadProtoRoot() {
479
- const here = import_node_path54.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
480
- const protoRoot = import_node_path54.default.resolve(here, "..", "proto");
479
+ const here = import_node_path55.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
480
+ const protoRoot = import_node_path55.default.resolve(here, "..", "proto");
481
481
  const root = new import_protobufjs.default.Root();
482
- root.resolvePath = (_origin, target) => import_node_path54.default.resolve(protoRoot, target);
482
+ root.resolvePath = (_origin, target) => import_node_path55.default.resolve(protoRoot, target);
483
483
  root.loadSync(
484
484
  "opentelemetry/proto/collector/trace/v1/trace_service.proto",
485
485
  { keepCase: true }
@@ -524,11 +524,42 @@ async function decodeProtobufBody(buf) {
524
524
  const { reshapeGrpcRequest: reshapeGrpcRequest2 } = await Promise.resolve().then(() => (init_otel_grpc(), otel_grpc_exports));
525
525
  return reshapeGrpcRequest2(decoded);
526
526
  }
527
+ function decompressorForEncoding(encoding) {
528
+ switch (encoding) {
529
+ case "gzip":
530
+ case "x-gzip":
531
+ return import_node_zlib.default.createGunzip();
532
+ case "deflate":
533
+ return import_node_zlib.default.createInflate();
534
+ default:
535
+ return null;
536
+ }
537
+ }
527
538
  async function buildOtelReceiver(opts) {
528
539
  const app = (0, import_fastify.default)({
529
540
  logger: false,
530
541
  bodyLimit: opts.bodyLimit ?? 16 * 1024 * 1024
531
542
  });
543
+ app.addHook("preParsing", (req, _reply, payload, done) => {
544
+ const encoding = (req.headers["content-encoding"] ?? "").toString().trim().toLowerCase();
545
+ if (encoding === "" || encoding === "identity") {
546
+ done(null, payload);
547
+ return;
548
+ }
549
+ const decompressor = decompressorForEncoding(encoding);
550
+ if (!decompressor) {
551
+ done(null, payload);
552
+ return;
553
+ }
554
+ const tracked = decompressor;
555
+ tracked.receivedEncodedLength = 0;
556
+ payload.on("data", (chunk) => {
557
+ tracked.receivedEncodedLength = (tracked.receivedEncodedLength ?? 0) + chunk.length;
558
+ });
559
+ payload.on("error", (err) => decompressor.destroy(err));
560
+ payload.pipe(decompressor);
561
+ done(null, decompressor);
562
+ });
532
563
  const REJECT_WARN_INTERVAL_MS = 6e4;
533
564
  let lastRejectWarnAt = 0;
534
565
  const warnRejectedOtlp = () => {
@@ -710,13 +741,14 @@ async function buildOtelReceiver(opts) {
710
741
  };
711
742
  return decorated;
712
743
  }
713
- var import_node_path54, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
744
+ var import_node_path55, import_node_url2, import_node_zlib, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
714
745
  var init_otel = __esm({
715
746
  "src/otel.ts"() {
716
747
  "use strict";
717
748
  init_cjs_shims();
718
- import_node_path54 = __toESM(require("path"), 1);
749
+ import_node_path55 = __toESM(require("path"), 1);
719
750
  import_node_url2 = require("url");
751
+ import_node_zlib = __toESM(require("zlib"), 1);
720
752
  import_fastify = __toESM(require("fastify"), 1);
721
753
  import_protobufjs = __toESM(require("protobufjs"), 1);
722
754
  init_auth();
@@ -731,7 +763,7 @@ var init_otel = __esm({
731
763
 
732
764
  // src/server.ts
733
765
  init_cjs_shims();
734
- var import_node_path73 = __toESM(require("path"), 1);
766
+ var import_node_path75 = __toESM(require("path"), 1);
735
767
 
736
768
  // src/graph.ts
737
769
  init_cjs_shims();
@@ -755,7 +787,7 @@ function getGraph(project = DEFAULT_PROJECT) {
755
787
  init_cjs_shims();
756
788
  var import_fastify2 = __toESM(require("fastify"), 1);
757
789
  var import_cors = __toESM(require("@fastify/cors"), 1);
758
- var import_types91 = require("@neat.is/types");
790
+ var import_types92 = require("@neat.is/types");
759
791
 
760
792
  // src/extend/index.ts
761
793
  init_cjs_shims();
@@ -867,12 +899,12 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
867
899
  ]);
868
900
  async function findHookFiles(scanPath) {
869
901
  const found = [];
870
- const walk9 = async (dir) => {
902
+ const walk10 = async (dir) => {
871
903
  const entries = await import_node_fs2.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
872
904
  for (const entry of entries) {
873
905
  if (entry.isDirectory()) {
874
906
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
875
- await walk9(import_node_path2.default.join(dir, entry.name));
907
+ await walk10(import_node_path2.default.join(dir, entry.name));
876
908
  } else if (entry.isFile()) {
877
909
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
878
910
  const rel = import_node_path2.default.relative(scanPath, import_node_path2.default.join(dir, entry.name));
@@ -881,7 +913,7 @@ async function findHookFiles(scanPath) {
881
913
  }
882
914
  }
883
915
  };
884
- await walk9(scanPath);
916
+ await walk10(scanPath);
885
917
  return found.sort();
886
918
  }
887
919
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
@@ -2007,6 +2039,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2007
2039
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2008
2040
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2009
2041
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2042
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2010
2043
  var import_types6 = require("@neat.is/types");
2011
2044
 
2012
2045
  // src/extract/shared.ts
@@ -2287,7 +2320,7 @@ function buildServiceHostIndex(services) {
2287
2320
  async function walkSourceFiles(dir, excludeDirs = []) {
2288
2321
  const excluded = new Set(excludeDirs.map((d) => import_node_path7.default.resolve(d)));
2289
2322
  const out = [];
2290
- async function walk9(current) {
2323
+ async function walk10(current) {
2291
2324
  const entries = await import_node_fs7.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2292
2325
  for (const entry of entries) {
2293
2326
  const full = import_node_path7.default.join(current, entry.name);
@@ -2295,7 +2328,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2295
2328
  if (IGNORED_DIRS.has(entry.name)) continue;
2296
2329
  if (excluded.has(import_node_path7.default.resolve(full))) continue;
2297
2330
  if (await isPythonVenvDir(full)) continue;
2298
- await walk9(full);
2331
+ await walk10(full);
2299
2332
  } 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
2300
2333
  // would attribute our instrumentation imports to the user's service.
2301
2334
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2303,7 +2336,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2303
2336
  }
2304
2337
  }
2305
2338
  }
2306
- await walk9(dir);
2339
+ await walk10(dir);
2307
2340
  return out;
2308
2341
  }
2309
2342
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -2823,6 +2856,11 @@ function makePhpParser() {
2823
2856
  p.setLanguage(import_tree_sitter_php.default.php_only);
2824
2857
  return p;
2825
2858
  }
2859
+ function makeRustParser() {
2860
+ const p = new import_tree_sitter2.default();
2861
+ p.setLanguage(import_tree_sitter_rust.default);
2862
+ return p;
2863
+ }
2826
2864
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
2827
2865
  "get",
2828
2866
  "post",
@@ -2894,8 +2932,8 @@ function chiRoutesFromSource(source, parser) {
2894
2932
  chiWalk(tree.rootNode, "", out);
2895
2933
  return out;
2896
2934
  }
2897
- function stripChiRegex(path74) {
2898
- return path74.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2935
+ function stripChiRegex(path76) {
2936
+ return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2899
2937
  }
2900
2938
  function chiWalk(node, prefix, out) {
2901
2939
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -3567,9 +3605,9 @@ function rubyRocketRoute(args) {
3567
3605
  if (!pair || pair.type !== "pair") continue;
3568
3606
  const k = pair.childForFieldName("key");
3569
3607
  if (k?.type !== "string") continue;
3570
- const path74 = rubyLiteral(k);
3571
- if (path74 === null) continue;
3572
- return { path: path74, target: rubyLiteral(pair.childForFieldName("value")) };
3608
+ const path76 = rubyLiteral(k);
3609
+ if (path76 === null) continue;
3610
+ return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
3573
3611
  }
3574
3612
  return null;
3575
3613
  }
@@ -3739,6 +3777,48 @@ function railsRoutesFromSource(source, parser) {
3739
3777
  });
3740
3778
  return out;
3741
3779
  }
3780
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
3781
+ function sinatraRoutesFromSource(source, parser) {
3782
+ const tree = parseSource2(parser, source);
3783
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
3784
+ const out = [];
3785
+ walk(tree.rootNode, (node) => {
3786
+ if (node.type !== "call") return;
3787
+ if (node.childForFieldName("receiver")) return;
3788
+ const method = node.childForFieldName("method")?.text;
3789
+ if (!method || !SINATRA_VERBS.has(method)) return;
3790
+ if (!node.childForFieldName("block")) return;
3791
+ const first = node.childForFieldName("arguments")?.namedChild(0);
3792
+ if (first?.type !== "string") return;
3793
+ const p = rubyLiteral(first);
3794
+ if (p === null || !p.startsWith("/")) return;
3795
+ out.push({
3796
+ method: method.toUpperCase(),
3797
+ pathTemplate: canonicalizeTemplate(p),
3798
+ line: node.startPosition.row + 1,
3799
+ framework: "sinatra"
3800
+ });
3801
+ });
3802
+ return out;
3803
+ }
3804
+ function fileReferencesSinatra(root) {
3805
+ let found = false;
3806
+ walk(root, (node) => {
3807
+ if (found) return;
3808
+ if (node.type === "call") {
3809
+ const m = node.childForFieldName("method")?.text;
3810
+ if (m === "require" || m === "require_relative") {
3811
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
3812
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
3813
+ }
3814
+ return;
3815
+ }
3816
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
3817
+ found = true;
3818
+ }
3819
+ });
3820
+ return found;
3821
+ }
3742
3822
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
3743
3823
  var LARAVEL_RESOURCE_ROWS = [
3744
3824
  { action: "index", methods: ["GET"], suffix: "" },
@@ -3914,6 +3994,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
3914
3994
  }
3915
3995
  return out;
3916
3996
  }
3997
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
3998
+ function isSlimAppCtor(node) {
3999
+ if (!node) return false;
4000
+ if (node.type === "scoped_call_expression") {
4001
+ const scope = node.childForFieldName("scope")?.text ?? "";
4002
+ const name = node.childForFieldName("name")?.text;
4003
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
4004
+ }
4005
+ if (node.type === "object_creation_expression") {
4006
+ const cls = node.namedChild(0)?.text ?? "";
4007
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
4008
+ }
4009
+ return false;
4010
+ }
4011
+ function isSlimAppType(node) {
4012
+ if (!node || node.type !== "named_type") return false;
4013
+ const text = node.text;
4014
+ return text === "App" || text.endsWith("\\App");
4015
+ }
4016
+ function collectSlimAppVars(root) {
4017
+ const vars = /* @__PURE__ */ new Set();
4018
+ walk(root, (node) => {
4019
+ if (node.type === "assignment_expression") {
4020
+ const left = node.childForFieldName("left");
4021
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
4022
+ vars.add(left.text);
4023
+ }
4024
+ return;
4025
+ }
4026
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
4027
+ const name = node.childForFieldName("name");
4028
+ if (name?.type === "variable_name") vars.add(name.text);
4029
+ }
4030
+ });
4031
+ return vars;
4032
+ }
4033
+ function slimClosureParamVars(closure) {
4034
+ const out = ["$this"];
4035
+ for (let i = 0; i < closure.namedChildCount; i++) {
4036
+ const params = closure.namedChild(i);
4037
+ if (params?.type !== "formal_parameters") continue;
4038
+ for (let j = 0; j < params.namedChildCount; j++) {
4039
+ const param = params.namedChild(j);
4040
+ if (param?.type !== "simple_parameter") continue;
4041
+ for (let k = 0; k < param.namedChildCount; k++) {
4042
+ const v = param.namedChild(k);
4043
+ if (v?.type === "variable_name") {
4044
+ out.push(v.text);
4045
+ break;
4046
+ }
4047
+ }
4048
+ }
4049
+ }
4050
+ return out;
4051
+ }
4052
+ function phpStringArray(node) {
4053
+ const out = [];
4054
+ if (node?.type !== "array_creation_expression") return out;
4055
+ for (let i = 0; i < node.namedChildCount; i++) {
4056
+ const el = node.namedChild(i);
4057
+ if (el?.type !== "array_element_initializer") continue;
4058
+ const s = phpStaticString(el.namedChild(0));
4059
+ if (s !== null) out.push(s);
4060
+ }
4061
+ return out;
4062
+ }
4063
+ function slimRoutesFromSource(source, parser) {
4064
+ const tree = parseSource2(parser, source);
4065
+ const appVars = collectSlimAppVars(tree.rootNode);
4066
+ if (appVars.size === 0) return [];
4067
+ const out = [];
4068
+ slimWalk(tree.rootNode, "", appVars, out);
4069
+ return out;
4070
+ }
4071
+ function slimWalk(node, prefix, appVars, out) {
4072
+ for (let i = 0; i < node.namedChildCount; i++) {
4073
+ const child = node.namedChild(i);
4074
+ if (child) slimHandle(child, prefix, appVars, out);
4075
+ }
4076
+ }
4077
+ function slimHandle(node, prefix, appVars, out) {
4078
+ if (node.type === "member_call_expression") {
4079
+ const obj = node.childForFieldName("object");
4080
+ const method = node.childForFieldName("name")?.text;
4081
+ const args = node.childForFieldName("arguments");
4082
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4083
+ const line = node.startPosition.row + 1;
4084
+ if (method === "group") {
4085
+ const groupPrefix = phpFirstString(args);
4086
+ const closure = laravelGroupClosure(args);
4087
+ if (groupPrefix !== null && closure) {
4088
+ const inner = new Set(appVars);
4089
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4090
+ const body = closure.childForFieldName("body");
4091
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4092
+ }
4093
+ return;
4094
+ }
4095
+ if (SLIM_VERBS.has(method)) {
4096
+ const p = phpFirstString(args);
4097
+ if (p !== null) {
4098
+ out.push({
4099
+ method: method.toUpperCase(),
4100
+ pathTemplate: laravelJoinPath(prefix, p),
4101
+ line,
4102
+ framework: "slim"
4103
+ });
4104
+ }
4105
+ return;
4106
+ }
4107
+ if (method === "any") {
4108
+ const p = phpFirstString(args);
4109
+ if (p !== null) {
4110
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4111
+ }
4112
+ return;
4113
+ }
4114
+ if (method === "map") {
4115
+ const vals = phpArgumentValues(args);
4116
+ const methods = phpStringArray(vals[0]);
4117
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4118
+ if (p !== null) {
4119
+ for (const m of methods) {
4120
+ out.push({
4121
+ method: m.toUpperCase(),
4122
+ pathTemplate: laravelJoinPath(prefix, p),
4123
+ line,
4124
+ framework: "slim"
4125
+ });
4126
+ }
4127
+ }
4128
+ return;
4129
+ }
4130
+ }
4131
+ }
4132
+ slimWalk(node, prefix, appVars, out);
4133
+ }
4134
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4135
+ function rustStringContent(node) {
4136
+ if (!node || node.type !== "string_literal") return null;
4137
+ for (let i = 0; i < node.namedChildCount; i++) {
4138
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4139
+ }
4140
+ return "";
4141
+ }
4142
+ function actixRoutesFromSource(source, parser) {
4143
+ const tree = parseSource2(parser, source);
4144
+ const out = [];
4145
+ walk(tree.rootNode, (node) => {
4146
+ if (node.type === "attribute_item") {
4147
+ actixAttributeRoute(node, out);
4148
+ return;
4149
+ }
4150
+ if (node.type === "call_expression") {
4151
+ actixBuilderRoute(node, out);
4152
+ }
4153
+ });
4154
+ return out;
4155
+ }
4156
+ function actixAttributeRoute(attrItem, out) {
4157
+ const attr = attrItem.namedChild(0);
4158
+ if (!attr || attr.type !== "attribute") return;
4159
+ const nameNode = attr.namedChild(0);
4160
+ if (!nameNode) return;
4161
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4162
+ if (!macro) return;
4163
+ const tokens = attr.childForFieldName("arguments");
4164
+ if (!tokens || tokens.type !== "token_tree") return;
4165
+ const strings = [];
4166
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4167
+ const s = rustStringContent(tokens.namedChild(i));
4168
+ if (s !== null) strings.push(s);
4169
+ }
4170
+ const pathStr = strings[0];
4171
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4172
+ const line = attrItem.startPosition.row + 1;
4173
+ const template = canonicalizeTemplate(pathStr);
4174
+ if (ACTIX_METHODS.has(macro)) {
4175
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4176
+ return;
4177
+ }
4178
+ if (macro === "route") {
4179
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4180
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4181
+ for (const m of list) {
4182
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4183
+ }
4184
+ }
4185
+ }
4186
+ function actixBuilderRoute(call, out) {
4187
+ const fn = call.childForFieldName("function");
4188
+ if (fn?.type !== "field_expression") return;
4189
+ if (fn.childForFieldName("field")?.text !== "route") return;
4190
+ const args = call.childForFieldName("arguments");
4191
+ const pathStr = rustStringContent(args?.namedChild(0));
4192
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4193
+ const second = args?.namedChild(1);
4194
+ if (!second) return;
4195
+ const method = actixBuilderMethod(second);
4196
+ if (!method) return;
4197
+ out.push({
4198
+ method,
4199
+ pathTemplate: canonicalizeTemplate(pathStr),
4200
+ line: call.startPosition.row + 1,
4201
+ framework: "actix-web"
4202
+ });
4203
+ }
4204
+ function actixBuilderMethod(node) {
4205
+ let method = null;
4206
+ walk(node, (n) => {
4207
+ if (method || n.type !== "scoped_identifier") return;
4208
+ const verb = n.childForFieldName("name")?.text;
4209
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4210
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4211
+ });
4212
+ return method;
4213
+ }
3917
4214
  function namedArgs(argsNode) {
3918
4215
  const out = [];
3919
4216
  if (!argsNode) return out;
@@ -4230,6 +4527,7 @@ async function addRoutes(graph, services) {
4230
4527
  const goParser = makeGoParser2();
4231
4528
  const rubyParser = makeRubyParser();
4232
4529
  const phpParser = makePhpParser();
4530
+ const rustParser = makeRustParser();
4233
4531
  let nodesAdded = 0;
4234
4532
  let edgesAdded = 0;
4235
4533
  for (const service of services) {
@@ -4252,7 +4550,10 @@ async function addRoutes(graph, services) {
4252
4550
  const isGoService = service.node.language === "go";
4253
4551
  const hasRails = deps["rails"] !== void 0;
4254
4552
  const hasLaravel = deps["laravel/framework"] !== void 0;
4255
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
4553
+ const hasSlim = deps["slim/slim"] !== void 0;
4554
+ const hasSinatra = deps["sinatra"] !== void 0;
4555
+ const hasActix = deps["actix-web"] !== void 0;
4556
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4256
4557
  continue;
4257
4558
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4258
4559
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4263,7 +4564,8 @@ async function addRoutes(graph, services) {
4263
4564
  const isGo = ext === ".go";
4264
4565
  const isRb = ext === ".rb";
4265
4566
  const isPhp = ext === ".php";
4266
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
4567
+ const isRs = ext === ".rs";
4568
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4267
4569
  const relFile = toPosix(import_node_path9.default.relative(service.dir, file.path));
4268
4570
  let routes;
4269
4571
  try {
@@ -4273,8 +4575,12 @@ async function addRoutes(graph, services) {
4273
4575
  phpParser,
4274
4576
  relFile === "routes/api.php" ? "/api" : ""
4275
4577
  ) : [];
4578
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4276
4579
  } else if (isRb) {
4277
4580
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4581
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
4582
+ } else if (isRs) {
4583
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4278
4584
  } else if (isGo) {
4279
4585
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4280
4586
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -4458,6 +4764,37 @@ function loadIncidentThresholdsFromEnv() {
4458
4764
  return DEFAULT_INCIDENT_THRESHOLDS;
4459
4765
  }
4460
4766
  }
4767
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
4768
+ function latencyStreamCeilingMs() {
4769
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
4770
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
4771
+ const n = Number(raw);
4772
+ if (Number.isFinite(n) && n > 0) return n;
4773
+ console.warn(
4774
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
4775
+ );
4776
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
4777
+ }
4778
+ function spanServesEventStream(attrs) {
4779
+ for (const key of [
4780
+ "http.response.header.content-type",
4781
+ "http.response.header.content_type"
4782
+ ]) {
4783
+ const v = attrs[key];
4784
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
4785
+ for (const item of values) {
4786
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
4787
+ return true;
4788
+ }
4789
+ }
4790
+ }
4791
+ return false;
4792
+ }
4793
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
4794
+ if (span.websocketChannel !== void 0) return true;
4795
+ if (spanServesEventStream(span.attributes)) return true;
4796
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
4797
+ }
4461
4798
  function httpResponseStatusFromAttrs(attrs) {
4462
4799
  for (const key of ["http.response.status_code", "http.status_code"]) {
4463
4800
  const v = attrs[key];
@@ -4509,6 +4846,14 @@ function grpcStatusCodeFromAttrs(attrs) {
4509
4846
  }
4510
4847
  return void 0;
4511
4848
  }
4849
+ function spanRecordsError(span) {
4850
+ if (span.statusCode === 2) return true;
4851
+ const grpc2 = grpcStatusCodeFromAttrs(span.attributes);
4852
+ if (grpc2 !== void 0 && grpc2 !== 0) return true;
4853
+ const httpStatus = httpResponseStatusFromAttrs(span.attributes);
4854
+ if (httpStatus !== void 0 && httpStatus >= 500) return true;
4855
+ return false;
4856
+ }
4512
4857
  function nonHttpFailureMessageFromAttrs(attrs) {
4513
4858
  const grpc2 = grpcStatusCodeFromAttrs(attrs);
4514
4859
  if (grpc2 !== void 0 && grpc2 !== 0) {
@@ -5282,6 +5627,21 @@ async function recordExceptionIncident(ctx, span, ts) {
5282
5627
  };
5283
5628
  await appendErrorEvent(ctx, ev);
5284
5629
  }
5630
+ async function recordGrpcFailureIncident(ctx, span, ts) {
5631
+ const attrs = sanitizeAttributes(span.attributes);
5632
+ const ev = {
5633
+ id: `${span.traceId}:${span.spanId}`,
5634
+ timestamp: ts,
5635
+ service: span.service,
5636
+ traceId: span.traceId,
5637
+ spanId: span.spanId,
5638
+ errorType: "grpc-failure",
5639
+ errorMessage: incidentMessage(span),
5640
+ ...Object.keys(attrs).length > 0 ? { attributes: attrs } : {},
5641
+ affectedNode: incidentAffectedNode(span, ctx.graph, ctx.scanPath)
5642
+ };
5643
+ await appendErrorEvent(ctx, ev);
5644
+ }
5285
5645
  async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5286
5646
  const { threshold, windowMs } = loadIncidentThresholdsFromEnv();
5287
5647
  if (!ctx.burstState) ctx.burstState = /* @__PURE__ */ new Map();
@@ -5325,6 +5685,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5325
5685
  );
5326
5686
  ctx.burstState.delete(key);
5327
5687
  }
5688
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
5689
+ function nextApiRouteTemplate(span) {
5690
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
5691
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
5692
+ return match ? match[1] : void 0;
5693
+ }
5328
5694
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5329
5695
  const target = normalizePathTemplate(httpRoute);
5330
5696
  const m = method?.toUpperCase();
@@ -5346,8 +5712,8 @@ async function handleSpan(ctx, span) {
5346
5712
  warnUnidentifiedSpan(ctx.project ?? DEFAULT_PROJECT);
5347
5713
  }
5348
5714
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5349
- const isError = span.statusCode === 2;
5350
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
5715
+ const isError = spanRecordsError(span);
5716
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
5351
5717
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
5352
5718
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
5353
5719
  cacheSpanService(span, nowMs, callSite);
@@ -5544,12 +5910,13 @@ async function handleSpan(ctx, span) {
5544
5910
  }
5545
5911
  }
5546
5912
  }
5547
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
5913
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
5914
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
5548
5915
  const routeNodeId = findRouteNodeByHttpRoute(
5549
5916
  ctx.graph,
5550
5917
  span.service,
5551
5918
  span.httpMethod,
5552
- span.httpRoute
5919
+ fusionRoute
5553
5920
  );
5554
5921
  if (routeNodeId) {
5555
5922
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -5581,10 +5948,13 @@ async function handleSpan(ctx, span) {
5581
5948
  }
5582
5949
  if (span.statusCode !== 2) {
5583
5950
  const status2 = httpResponseStatus(span);
5951
+ const grpcStatus = grpcStatusCodeFromAttrs(span.attributes);
5584
5952
  if (span.exception) {
5585
5953
  await recordExceptionIncident(ctx, span, ts);
5586
5954
  } else if (status2 !== void 0 && status2 >= 500) {
5587
5955
  await recordFailingResponseIncident(ctx, span, sourceId, ts, status2, 1);
5956
+ } else if (grpcStatus !== void 0 && grpcStatus !== 0) {
5957
+ await recordGrpcFailureIncident(ctx, span, ts);
5588
5958
  } else if (status2 !== void 0 && status2 >= 400 && spanMintsObservedEdge(span.kind)) {
5589
5959
  await advance4xxBurst(ctx, span, sourceId, ts, nowMs, status2);
5590
5960
  }
@@ -5953,19 +6323,19 @@ function confidenceFromMix(edges, now = Date.now()) {
5953
6323
  function longestIncomingWalk(graph, start, maxDepth) {
5954
6324
  let best = { path: [start], edges: [] };
5955
6325
  const visited = /* @__PURE__ */ new Set([start]);
5956
- function step(node, path74, edges) {
5957
- if (path74.length > best.path.length) {
5958
- best = { path: [...path74], edges: [...edges] };
6326
+ function step(node, path76, edges) {
6327
+ if (path76.length > best.path.length) {
6328
+ best = { path: [...path76], edges: [...edges] };
5959
6329
  }
5960
- if (path74.length - 1 >= maxDepth) return;
6330
+ if (path76.length - 1 >= maxDepth) return;
5961
6331
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
5962
6332
  for (const [srcId, edge] of incoming) {
5963
6333
  if (visited.has(srcId)) continue;
5964
6334
  visited.add(srcId);
5965
- path74.push(srcId);
6335
+ path76.push(srcId);
5966
6336
  edges.push(edge);
5967
- step(srcId, path74, edges);
5968
- path74.pop();
6337
+ step(srcId, path76, edges);
6338
+ path76.pop();
5969
6339
  edges.pop();
5970
6340
  visited.delete(srcId);
5971
6341
  }
@@ -5973,11 +6343,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
5973
6343
  step(start, [start], []);
5974
6344
  return best;
5975
6345
  }
5976
- function databaseRootCauseShape(graph, origin, walk9) {
6346
+ function databaseRootCauseShape(graph, origin, walk10) {
5977
6347
  const targetDb = origin;
5978
6348
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
5979
6349
  if (candidatePairs.length === 0) return null;
5980
- for (const id of walk9.path) {
6350
+ for (const id of walk10.path) {
5981
6351
  const owner = resolveOwningService(graph, id);
5982
6352
  if (!owner) continue;
5983
6353
  const { id: serviceId15, svc } = owner;
@@ -6004,8 +6374,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
6004
6374
  }
6005
6375
  return null;
6006
6376
  }
6007
- function serviceRootCauseShape(graph, _origin, walk9) {
6008
- for (const id of walk9.path) {
6377
+ function serviceRootCauseShape(graph, _origin, walk10) {
6378
+ for (const id of walk10.path) {
6009
6379
  const owner = resolveOwningService(graph, id);
6010
6380
  if (!owner) continue;
6011
6381
  const { id: serviceId15, svc } = owner;
@@ -6041,15 +6411,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
6041
6411
  }
6042
6412
  return null;
6043
6413
  }
6044
- function fileRootCauseShape(graph, origin, walk9) {
6414
+ function fileRootCauseShape(graph, origin, walk10) {
6045
6415
  const owner = resolveOwningService(graph, origin.id);
6046
6416
  if (!owner) return null;
6047
- return serviceRootCauseShape(graph, owner.svc, walk9);
6417
+ return serviceRootCauseShape(graph, owner.svc, walk10);
6048
6418
  }
6049
- function symbolRootCauseShape(graph, origin, walk9) {
6419
+ function symbolRootCauseShape(graph, origin, walk10) {
6050
6420
  const owner = resolveOwningService(graph, origin.id);
6051
6421
  if (!owner) return null;
6052
- return serviceRootCauseShape(graph, owner.svc, walk9);
6422
+ return serviceRootCauseShape(graph, owner.svc, walk10);
6053
6423
  }
6054
6424
  var rootCauseShapes = {
6055
6425
  [import_types8.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -6062,25 +6432,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
6062
6432
  const origin = graph.getNodeAttributes(errorNodeId);
6063
6433
  const shape = rootCauseShapes[origin.type];
6064
6434
  if (shape) {
6065
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
6066
- const match = shape(graph, origin, walk9);
6435
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
6436
+ const match = shape(graph, origin, walk10);
6067
6437
  if (match) {
6068
6438
  const reason = errorEvent ? `${match.rootCauseReason} (observed error: ${errorEvent.errorMessage})` : match.rootCauseReason;
6069
- return import_types8.RootCauseResultSchema.parse({
6070
- rootCauseNode: match.rootCauseNode,
6071
- rootCauseReason: reason,
6072
- traversalPath: walk9.path,
6073
- edgeProvenances: walk9.edges.map((e) => e.provenance),
6074
- confidence: confidenceFromMix(walk9.edges),
6075
- fixRecommendation: match.fixRecommendation
6076
- });
6439
+ return {
6440
+ source: "compat",
6441
+ result: import_types8.RootCauseResultSchema.parse({
6442
+ rootCauseNode: match.rootCauseNode,
6443
+ rootCauseReason: reason,
6444
+ traversalPath: walk10.path,
6445
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
6446
+ confidence: confidenceFromMix(walk10.edges),
6447
+ fixRecommendation: match.fixRecommendation
6448
+ })
6449
+ };
6077
6450
  }
6078
6451
  }
6079
6452
  if (origin.type === import_types8.NodeType.ServiceNode) {
6080
6453
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
6081
- if (crossService) return crossService;
6454
+ if (crossService) return { result: crossService, source: "cross-service" };
6082
6455
  }
6083
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
6456
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
6457
+ return incident ? { result: incident, source: "incident" } : null;
6084
6458
  }
6085
6459
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
6086
6460
  function incidentMatchesNode(ev, nodeId) {
@@ -6176,26 +6550,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
6176
6550
  return best;
6177
6551
  }
6178
6552
  function followFailingCallChain(graph, originServiceId, maxDepth) {
6179
- const path74 = [originServiceId];
6553
+ const path76 = [originServiceId];
6180
6554
  const edges = [];
6181
6555
  const visited = /* @__PURE__ */ new Set([originServiceId]);
6182
6556
  let current = originServiceId;
6183
6557
  for (let depth = 0; depth < maxDepth; depth++) {
6184
6558
  const hop = dominantFailingCall(graph, current, visited);
6185
6559
  if (!hop) break;
6186
- path74.push(hop.nextService);
6560
+ path76.push(hop.nextService);
6561
+ edges.push(hop.edge);
6562
+ visited.add(hop.nextService);
6563
+ current = hop.nextService;
6564
+ }
6565
+ if (edges.length === 0) return null;
6566
+ return { path: path76, edges, culprit: current };
6567
+ }
6568
+ function isStaleCallEdge(e) {
6569
+ return e.type === import_types8.EdgeType.CALLS && e.provenance === import_types8.Provenance.STALE;
6570
+ }
6571
+ function staleCallDominates(e, id, curEdge, curId) {
6572
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
6573
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
6574
+ if (ev !== cv) return ev > cv;
6575
+ return id < curId;
6576
+ }
6577
+ function dominantStaleCall(graph, serviceId15, visited) {
6578
+ const bestByCallee = /* @__PURE__ */ new Map();
6579
+ for (const src of callSourcesForService(graph, serviceId15)) {
6580
+ for (const edgeId of graph.outboundEdges(src)) {
6581
+ const e = graph.getEdgeAttributes(edgeId);
6582
+ if (e.type !== import_types8.EdgeType.CALLS) continue;
6583
+ if (isFrontierNode(graph, e.target)) continue;
6584
+ const owner = resolveOwningService(graph, e.target);
6585
+ if (!owner || visited.has(owner.id)) continue;
6586
+ const cur = bestByCallee.get(owner.id);
6587
+ if (!cur || import_types8.PROV_RANK[e.provenance] > import_types8.PROV_RANK[cur.provenance]) {
6588
+ bestByCallee.set(owner.id, e);
6589
+ }
6590
+ }
6591
+ }
6592
+ let best = null;
6593
+ for (const [id, edge] of bestByCallee) {
6594
+ if (!isStaleCallEdge(edge)) continue;
6595
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
6596
+ best = { nextService: id, edge };
6597
+ }
6598
+ }
6599
+ return best;
6600
+ }
6601
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
6602
+ const path76 = [originServiceId];
6603
+ const edges = [];
6604
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
6605
+ let current = originServiceId;
6606
+ for (let depth = 0; depth < maxDepth; depth++) {
6607
+ const hop = dominantStaleCall(graph, current, visited);
6608
+ if (!hop) break;
6609
+ path76.push(hop.nextService);
6187
6610
  edges.push(hop.edge);
6188
6611
  visited.add(hop.nextService);
6189
6612
  current = hop.nextService;
6190
6613
  }
6191
6614
  if (edges.length === 0) return null;
6192
- return { path: path74, edges, culprit: current };
6615
+ return { path: path76, edges, culprit: current };
6193
6616
  }
6194
6617
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6195
6618
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
6196
6619
  if (!chain) return null;
6197
6620
  const culprit = chain.culprit;
6198
- const path74 = [...chain.path];
6621
+ const path76 = [...chain.path];
6199
6622
  const edgeProvenances = chain.edges.map((e) => e.provenance);
6200
6623
  const baseConfidence = confidenceFromMix(chain.edges);
6201
6624
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -6203,14 +6626,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6203
6626
  if (loc) {
6204
6627
  let rootCauseNode = culprit;
6205
6628
  if (loc.fileNode) {
6206
- path74.push(loc.fileNode);
6629
+ path76.push(loc.fileNode);
6207
6630
  edgeProvenances.push(import_types8.Provenance.OBSERVED);
6208
6631
  rootCauseNode = loc.fileNode;
6209
6632
  }
6210
6633
  return import_types8.RootCauseResultSchema.parse({
6211
6634
  rootCauseNode,
6212
6635
  rootCauseReason: loc.rootCauseReason,
6213
- traversalPath: path74,
6636
+ traversalPath: path76,
6214
6637
  edgeProvenances,
6215
6638
  confidence,
6216
6639
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -6222,7 +6645,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6222
6645
  return import_types8.RootCauseResultSchema.parse({
6223
6646
  rootCauseNode: culprit,
6224
6647
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
6225
- traversalPath: path74,
6648
+ traversalPath: path76,
6226
6649
  edgeProvenances,
6227
6650
  confidence,
6228
6651
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -6609,17 +7032,20 @@ function displayNameOf(nodeId) {
6609
7032
  return nodeId.replace(/^[a-z]+:/, "");
6610
7033
  }
6611
7034
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
6612
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
6613
- if (!legacy) return null;
7035
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
7036
+ if (!tagged) return null;
6614
7037
  const navigation = opts?.navigation ?? process.env.NEAT_RCA_NAVIGATION !== "0";
6615
- if (!navigation) return legacy;
6616
- return enrichWithNavigation(graph, errorNodeId, legacy, incidents, opts?.now ?? Date.now());
7038
+ if (!navigation) return tagged.result;
7039
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
6617
7040
  }
6618
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
7041
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
7042
+ const legacy = tagged.result;
6619
7043
  const seedNode = legacy.rootCauseNode;
6620
7044
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
6621
7045
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
6622
7046
  const candidates = [];
7047
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
7048
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
6623
7049
  if (seedCtx && isVictimSeed(seedCtx)) {
6624
7050
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
6625
7051
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -6644,6 +7070,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
6644
7070
  confidence: Math.min(legacy.confidence, 0.4),
6645
7071
  ...lastProv ? { provenance: lastProv } : {}
6646
7072
  });
7073
+ } else if (staleChain) {
7074
+ const culprit = staleChain.culprit;
7075
+ const culpritName = displayNameOf(culprit);
7076
+ const seedName = displayNameOf(seedNode);
7077
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
7078
+ candidates.push({
7079
+ node: culprit,
7080
+ classification: "primary-failure",
7081
+ reason: `${culpritName} is the stale-derived root cause (low confidence): live telemetry for this subgraph has gone quiet, but the last-observed topology traces the failure surfacing at ${seedName} downstream through a STALE call chain to ${culpritName}. Provenance is STALE, so confidence is capped low \u2014 restore instrumentation and re-run to confirm before acting.`,
7082
+ context: nodeContext(graph, culprit, incidents, now),
7083
+ confidence: staleConfidence,
7084
+ provenance: import_types8.Provenance.STALE
7085
+ });
7086
+ candidates.push({
7087
+ node: seedNode,
7088
+ classification: "symptom-only",
7089
+ reason: `The failure surfaced here, but the only causal chain the graph still holds is STALE and runs downstream \u2014 ${seedName} is the surface of a stale-traced failure, not a proven origin.`,
7090
+ context: seedCtx ?? EMPTY_CONTEXT,
7091
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
7092
+ ...lastProv ? { provenance: lastProv } : {}
7093
+ });
6647
7094
  } else {
6648
7095
  candidates.push({
6649
7096
  node: seedNode,
@@ -6657,11 +7104,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
6657
7104
  const top = candidates[0];
6658
7105
  let traversalPath = legacy.traversalPath;
6659
7106
  let edgeProvenances = legacy.edgeProvenances;
6660
- if (top.node !== seedNode) {
6661
- const path74 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
6662
- if (path74) {
6663
- traversalPath = path74.nodes;
6664
- edgeProvenances = path74.edges.map((e) => e.provenance);
7107
+ if (staleChain && top.node === staleChain.culprit) {
7108
+ traversalPath = staleChain.path;
7109
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
7110
+ } else if (top.node !== seedNode) {
7111
+ const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
7112
+ if (path76) {
7113
+ traversalPath = path76.nodes;
7114
+ edgeProvenances = path76.edges.map((e) => e.provenance);
6665
7115
  } else {
6666
7116
  traversalPath = [errorNodeId, top.node];
6667
7117
  edgeProvenances = [top.provenance ?? import_types8.Provenance.OBSERVED];
@@ -6683,6 +7133,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
6683
7133
  return legacy.fixRecommendation;
6684
7134
  }
6685
7135
  const name = top.node.replace(/^service:/, "");
7136
+ if (top.provenance === import_types8.Provenance.STALE) {
7137
+ return `Live telemetry for this path has gone quiet; the last-observed topology traces the failure downstream to ${name}. Restore instrumentation (or re-run with live traces) to confirm, then inspect ${name}.`;
7138
+ }
6686
7139
  if (top.classification === "primary-failure") {
6687
7140
  return `Reduce or throttle the load from ${name} (or scale the saturated downstream capacity it drives) \u2014 the failure originates at this overloading source, not the starved callee.`;
6688
7141
  }
@@ -8169,7 +8622,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
8169
8622
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
8170
8623
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
8171
8624
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
8172
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
8625
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
8173
8626
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
8174
8627
  var import_types21 = require("@neat.is/types");
8175
8628
  var PARSE_CHUNK3 = 16384;
@@ -8190,7 +8643,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
8190
8643
  ".cs": import_tree_sitter_c_sharp.default,
8191
8644
  ".java": import_tree_sitter_java.default,
8192
8645
  ".kt": import_tree_sitter_kotlin.default,
8193
- ".rs": import_tree_sitter_rust.default,
8646
+ ".rs": import_tree_sitter_rust2.default,
8194
8647
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
8195
8648
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
8196
8649
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8630,15 +9083,15 @@ function collectKotlinSymbolDefs(root) {
8630
9083
  });
8631
9084
  };
8632
9085
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8633
- const firstChildOfType = (node, types) => {
9086
+ const firstChildOfType2 = (node, types) => {
8634
9087
  for (let i = 0; i < node.namedChildCount; i++) {
8635
9088
  const child = node.namedChild(i);
8636
9089
  if (child && types.includes(child.type)) return child;
8637
9090
  }
8638
9091
  return void 0;
8639
9092
  };
8640
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8641
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
9093
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
9094
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8642
9095
  let pkg;
8643
9096
  for (let i = 0; i < root.namedChildCount; i++) {
8644
9097
  const child = root.namedChild(i);
@@ -9139,7 +9592,7 @@ async function addSymbolEdges(graph, services) {
9139
9592
  return best;
9140
9593
  };
9141
9594
  const requests = [];
9142
- const walk9 = (node) => {
9595
+ const walk10 = (node) => {
9143
9596
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
9144
9597
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
9145
9598
  if (self && self.kind === "class") {
@@ -9185,10 +9638,10 @@ async function addSymbolEdges(graph, services) {
9185
9638
  }
9186
9639
  for (let i = 0; i < node.namedChildCount; i++) {
9187
9640
  const child = node.namedChild(i);
9188
- if (child) walk9(child);
9641
+ if (child) walk10(child);
9189
9642
  }
9190
9643
  };
9191
- walk9(root);
9644
+ walk10(root);
9192
9645
  for (const req of requests) {
9193
9646
  const targetSid = resolveTarget(req.targetName, req.wantKind);
9194
9647
  if (!targetSid) continue;
@@ -9484,7 +9937,7 @@ async function addServerActions(graph, services) {
9484
9937
 
9485
9938
  // src/extract/databases/index.ts
9486
9939
  init_cjs_shims();
9487
- var import_node_path35 = __toESM(require("path"), 1);
9940
+ var import_node_path36 = __toESM(require("path"), 1);
9488
9941
  var import_types24 = require("@neat.is/types");
9489
9942
 
9490
9943
  // src/extract/databases/db-config-yaml.ts
@@ -9919,9 +10372,163 @@ async function parse8(serviceDir) {
9919
10372
  }
9920
10373
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9921
10374
 
9922
- // src/extract/databases/docker-compose.ts
10375
+ // src/extract/databases/csharp.ts
9923
10376
  init_cjs_shims();
10377
+ var import_node_fs24 = require("fs");
9924
10378
  var import_node_path34 = __toESM(require("path"), 1);
10379
+ var CS_EXT = ".cs";
10380
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
10381
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
10382
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
10383
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
10384
+ function hostIsUnresolved(host) {
10385
+ return host === "" || /[${}]/.test(host);
10386
+ }
10387
+ function looksLikePostgres(s) {
10388
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
10389
+ }
10390
+ function looksLikeRedis(s) {
10391
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
10392
+ }
10393
+ function parsePostgresConnection(raw) {
10394
+ const s = raw.trim();
10395
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
10396
+ const fields = /* @__PURE__ */ new Map();
10397
+ for (const part of s.split(";")) {
10398
+ const eq = part.indexOf("=");
10399
+ if (eq < 0) continue;
10400
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
10401
+ const value = part.slice(eq + 1).trim();
10402
+ if (value && !fields.has(key)) fields.set(key, value);
10403
+ }
10404
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
10405
+ if (!hostRaw) return null;
10406
+ const host = hostRaw.split(",")[0].trim();
10407
+ if (hostIsUnresolved(host)) return null;
10408
+ const portRaw = fields.get("port");
10409
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
10410
+ const database = fields.get("database") ?? fields.get("db") ?? "";
10411
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
10412
+ }
10413
+ function parseRedisEndpoint(raw) {
10414
+ const s = raw.trim();
10415
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
10416
+ const first = s.split(",")[0].trim();
10417
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
10418
+ if (!m) return null;
10419
+ const host = m[1];
10420
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
10421
+ const port = m[2] ? Number(m[2]) : void 0;
10422
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
10423
+ }
10424
+ async function resolveEnvUpTree(startDir, name) {
10425
+ let dir = import_node_path34.default.resolve(startDir);
10426
+ for (let depth = 0; depth < 12; depth++) {
10427
+ const value = await resolveEnvVar(dir, name);
10428
+ if (value !== null) return value;
10429
+ const atRepoRoot = await import_node_fs24.promises.access(import_node_path34.default.join(dir, ".git")).then(() => true).catch(() => false);
10430
+ const parent = import_node_path34.default.dirname(dir);
10431
+ if (atRepoRoot || parent === dir) break;
10432
+ dir = parent;
10433
+ }
10434
+ return null;
10435
+ }
10436
+ async function interpolateEnvRefs(value, dir) {
10437
+ const refs = /* @__PURE__ */ new Set();
10438
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
10439
+ refs.add(m[1] ?? m[2]);
10440
+ }
10441
+ let out = value;
10442
+ for (const name of refs) {
10443
+ const resolved = await resolveEnvUpTree(dir, name);
10444
+ if (resolved === null) continue;
10445
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
10446
+ }
10447
+ return out;
10448
+ }
10449
+ function stringLiterals(masked) {
10450
+ const out = [];
10451
+ STRING_LITERAL_RE.lastIndex = 0;
10452
+ let m;
10453
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
10454
+ return out;
10455
+ }
10456
+ function envKeys(masked) {
10457
+ const out = [];
10458
+ ENV_READ_RE.lastIndex = 0;
10459
+ let m;
10460
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
10461
+ const key = m[1] ?? m[2];
10462
+ if (key) out.push(key);
10463
+ }
10464
+ return out;
10465
+ }
10466
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
10467
+ const out = [];
10468
+ for (const key of keys) {
10469
+ const raw = await resolveEnvUpTree(serviceDir, key);
10470
+ if (raw === null) continue;
10471
+ const value = await interpolateEnvRefs(raw, serviceDir);
10472
+ if (!looksLike(value)) continue;
10473
+ const parsed = parse11(value);
10474
+ if (parsed) out.push(parsed);
10475
+ }
10476
+ for (const lit of literals) {
10477
+ if (!looksLike(lit)) continue;
10478
+ const parsed = parse11(lit);
10479
+ if (parsed) out.push(parsed);
10480
+ }
10481
+ return out;
10482
+ }
10483
+ async function parse9(serviceDir) {
10484
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
10485
+ (f) => import_node_path34.default.extname(f) === CS_EXT
10486
+ );
10487
+ if (files.length === 0) return [];
10488
+ const sources = [];
10489
+ for (const file of files) {
10490
+ const content = await import_node_fs24.promises.readFile(file, "utf8").catch(() => null);
10491
+ if (content !== null) sources.push({ file, content });
10492
+ }
10493
+ let pgGateFile = null;
10494
+ let redisGateFile = null;
10495
+ for (const { file, content } of sources) {
10496
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
10497
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
10498
+ }
10499
+ if (!pgGateFile && !redisGateFile) return [];
10500
+ const literals = [];
10501
+ const keys = [];
10502
+ for (const { content } of sources) {
10503
+ const masked = maskCommentsInSource(content);
10504
+ literals.push(...stringLiterals(masked));
10505
+ keys.push(...envKeys(masked));
10506
+ }
10507
+ const out = [];
10508
+ const seenHosts = /* @__PURE__ */ new Set();
10509
+ const push = (config, sourceFile) => {
10510
+ const dedupe = `${config.engine}:${config.host}`;
10511
+ if (seenHosts.has(dedupe)) return;
10512
+ seenHosts.add(dedupe);
10513
+ out.push({ ...config, sourceFile });
10514
+ };
10515
+ if (pgGateFile) {
10516
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
10517
+ push(pg3, pgGateFile);
10518
+ }
10519
+ }
10520
+ if (redisGateFile) {
10521
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
10522
+ push(redis, redisGateFile);
10523
+ }
10524
+ }
10525
+ return out;
10526
+ }
10527
+ var csharpParser = { name: "csharp", parse: parse9 };
10528
+
10529
+ // src/extract/databases/docker-compose.ts
10530
+ init_cjs_shims();
10531
+ var import_node_path35 = __toESM(require("path"), 1);
9925
10532
  function portFromService(svc) {
9926
10533
  for (const raw of svc.ports ?? []) {
9927
10534
  const str = String(raw);
@@ -9946,9 +10553,9 @@ function databaseFromEnv(svc) {
9946
10553
  };
9947
10554
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9948
10555
  }
9949
- async function parse9(serviceDir) {
10556
+ async function parse10(serviceDir) {
9950
10557
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9951
- const abs = import_node_path34.default.join(serviceDir, name);
10558
+ const abs = import_node_path35.default.join(serviceDir, name);
9952
10559
  if (!await exists2(abs)) continue;
9953
10560
  const raw = await readYaml(abs);
9954
10561
  if (!raw?.services) return [];
@@ -9970,7 +10577,7 @@ async function parse9(serviceDir) {
9970
10577
  }
9971
10578
  return [];
9972
10579
  }
9973
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
10580
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9974
10581
 
9975
10582
  // src/extract/databases/index.ts
9976
10583
  var DB_PARSERS = [
@@ -9982,6 +10589,7 @@ var DB_PARSERS = [
9982
10589
  ormconfigParser,
9983
10590
  typeormParser,
9984
10591
  sequelizeParser,
10592
+ csharpParser,
9985
10593
  dockerComposeParser
9986
10594
  ];
9987
10595
  function compatibleDriversFor(engine) {
@@ -10120,7 +10728,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10120
10728
  discoveredVia: mergedDiscoveredVia
10121
10729
  });
10122
10730
  }
10123
- const relConfigFile = toPosix(import_node_path35.default.relative(service.dir, config.sourceFile));
10731
+ const relConfigFile = toPosix(import_node_path36.default.relative(service.dir, config.sourceFile));
10124
10732
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10125
10733
  graph,
10126
10734
  service.pkg.name,
@@ -10129,7 +10737,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10129
10737
  );
10130
10738
  nodesAdded += fn;
10131
10739
  edgesAdded += fe;
10132
- const evidenceFile = toPosix(import_node_path35.default.relative(scanPath, config.sourceFile));
10740
+ const evidenceFile = toPosix(import_node_path36.default.relative(scanPath, config.sourceFile));
10133
10741
  const edge = {
10134
10742
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types24.EdgeType.CONNECTS_TO),
10135
10743
  source: fileNodeId,
@@ -10147,15 +10755,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10147
10755
  if (allConfigs.length === 1) {
10148
10756
  const primary = allConfigs[0];
10149
10757
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
10150
- const relPath = import_node_path35.default.relative(scanPath, primary.sourceFile);
10758
+ const relPath = import_node_path36.default.relative(scanPath, primary.sourceFile);
10151
10759
  const cfgId = (0, import_types24.configId)(relPath);
10152
10760
  if (!graph.hasNode(cfgId)) {
10153
10761
  const cfgNode = {
10154
10762
  id: cfgId,
10155
10763
  type: import_types24.NodeType.ConfigNode,
10156
- name: import_node_path35.default.basename(primary.sourceFile),
10764
+ name: import_node_path36.default.basename(primary.sourceFile),
10157
10765
  path: relPath,
10158
- fileType: isConfigFile(import_node_path35.default.basename(primary.sourceFile)).fileType || "config"
10766
+ fileType: isConfigFile(import_node_path36.default.basename(primary.sourceFile)).fileType || "config"
10159
10767
  };
10160
10768
  graph.addNode(cfgId, cfgNode);
10161
10769
  nodesAdded++;
@@ -10196,27 +10804,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10196
10804
 
10197
10805
  // src/extract/configs.ts
10198
10806
  init_cjs_shims();
10199
- var import_node_fs24 = require("fs");
10200
- var import_node_path36 = __toESM(require("path"), 1);
10807
+ var import_node_fs25 = require("fs");
10808
+ var import_node_path37 = __toESM(require("path"), 1);
10201
10809
  var import_types25 = require("@neat.is/types");
10202
10810
  async function walkConfigFiles(dir, excludeDirs = []) {
10203
- const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10811
+ const excluded = new Set(excludeDirs.map((d) => import_node_path37.default.resolve(d)));
10204
10812
  const out = [];
10205
- async function walk9(current) {
10206
- const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true });
10813
+ async function walk10(current) {
10814
+ const entries = await import_node_fs25.promises.readdir(current, { withFileTypes: true });
10207
10815
  for (const entry of entries) {
10208
- const full = import_node_path36.default.join(current, entry.name);
10816
+ const full = import_node_path37.default.join(current, entry.name);
10209
10817
  if (entry.isDirectory()) {
10210
10818
  if (IGNORED_DIRS.has(entry.name)) continue;
10211
- if (excluded.has(import_node_path36.default.resolve(full))) continue;
10819
+ if (excluded.has(import_node_path37.default.resolve(full))) continue;
10212
10820
  if (await isPythonVenvDir(full)) continue;
10213
- await walk9(full);
10821
+ await walk10(full);
10214
10822
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
10215
10823
  out.push(full);
10216
10824
  }
10217
10825
  }
10218
10826
  }
10219
- await walk9(dir);
10827
+ await walk10(dir);
10220
10828
  return out;
10221
10829
  }
10222
10830
  async function addConfigNodes(graph, services, scanPath) {
@@ -10225,19 +10833,19 @@ async function addConfigNodes(graph, services, scanPath) {
10225
10833
  for (const service of services) {
10226
10834
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
10227
10835
  for (const file of configFiles) {
10228
- const relPath = import_node_path36.default.relative(scanPath, file);
10836
+ const relPath = import_node_path37.default.relative(scanPath, file);
10229
10837
  const node = {
10230
10838
  id: (0, import_types25.configId)(relPath),
10231
10839
  type: import_types25.NodeType.ConfigNode,
10232
- name: import_node_path36.default.basename(file),
10840
+ name: import_node_path37.default.basename(file),
10233
10841
  path: relPath,
10234
- fileType: isConfigFile(import_node_path36.default.basename(file)).fileType
10842
+ fileType: isConfigFile(import_node_path37.default.basename(file)).fileType
10235
10843
  };
10236
10844
  if (!graph.hasNode(node.id)) {
10237
10845
  graph.addNode(node.id, node);
10238
10846
  nodesAdded++;
10239
10847
  }
10240
- const relToService = toPosix(import_node_path36.default.relative(service.dir, file));
10848
+ const relToService = toPosix(import_node_path37.default.relative(service.dir, file));
10241
10849
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10242
10850
  graph,
10243
10851
  service.pkg.name,
@@ -10253,7 +10861,7 @@ async function addConfigNodes(graph, services, scanPath) {
10253
10861
  type: import_types25.EdgeType.CONFIGURED_BY,
10254
10862
  provenance: import_types25.Provenance.EXTRACTED,
10255
10863
  confidence: (0, import_types25.confidenceForExtracted)("structural"),
10256
- evidence: { file: relPath.split(import_node_path36.default.sep).join("/") }
10864
+ evidence: { file: relPath.split(import_node_path37.default.sep).join("/") }
10257
10865
  };
10258
10866
  if (!graph.hasEdge(edge.id)) {
10259
10867
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -10266,8 +10874,8 @@ async function addConfigNodes(graph, services, scanPath) {
10266
10874
 
10267
10875
  // src/extract/proto.ts
10268
10876
  init_cjs_shims();
10269
- var import_node_fs25 = require("fs");
10270
- var import_node_path37 = __toESM(require("path"), 1);
10877
+ var import_node_fs26 = require("fs");
10878
+ var import_node_path38 = __toESM(require("path"), 1);
10271
10879
  var import_types26 = require("@neat.is/types");
10272
10880
  var PROTO_EXTENSION = ".proto";
10273
10881
  function packageOf(content) {
@@ -10305,23 +10913,23 @@ function grpcMethodsFromProto(content, fqPackage) {
10305
10913
  return out;
10306
10914
  }
10307
10915
  async function walkProtoFiles(dir, excludeDirs = []) {
10308
- const excluded = new Set(excludeDirs.map((d) => import_node_path37.default.resolve(d)));
10916
+ const excluded = new Set(excludeDirs.map((d) => import_node_path38.default.resolve(d)));
10309
10917
  const out = [];
10310
- async function walk9(current) {
10311
- const entries = await import_node_fs25.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10918
+ async function walk10(current) {
10919
+ const entries = await import_node_fs26.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10312
10920
  for (const entry of entries) {
10313
- const full = import_node_path37.default.join(current, entry.name);
10921
+ const full = import_node_path38.default.join(current, entry.name);
10314
10922
  if (entry.isDirectory()) {
10315
10923
  if (IGNORED_DIRS.has(entry.name)) continue;
10316
- if (excluded.has(import_node_path37.default.resolve(full))) continue;
10924
+ if (excluded.has(import_node_path38.default.resolve(full))) continue;
10317
10925
  if (await isPythonVenvDir(full)) continue;
10318
- await walk9(full);
10319
- } else if (entry.isFile() && import_node_path37.default.extname(entry.name) === PROTO_EXTENSION) {
10926
+ await walk10(full);
10927
+ } else if (entry.isFile() && import_node_path38.default.extname(entry.name) === PROTO_EXTENSION) {
10320
10928
  out.push(full);
10321
10929
  }
10322
10930
  }
10323
10931
  }
10324
- await walk9(dir);
10932
+ await walk10(dir);
10325
10933
  return out;
10326
10934
  }
10327
10935
  async function addGrpcMethods(graph, services) {
@@ -10331,10 +10939,10 @@ async function addGrpcMethods(graph, services) {
10331
10939
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
10332
10940
  for (const protoPath of protoPaths) {
10333
10941
  if (isTestPath(protoPath)) continue;
10334
- const relFile = toPosix(import_node_path37.default.relative(service.dir, protoPath));
10942
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, protoPath));
10335
10943
  let content;
10336
10944
  try {
10337
- content = await import_node_fs25.promises.readFile(protoPath, "utf8");
10945
+ content = await import_node_fs26.promises.readFile(protoPath, "utf8");
10338
10946
  } catch (err) {
10339
10947
  recordExtractionError("proto extraction", protoPath, err);
10340
10948
  continue;
@@ -10389,11 +10997,11 @@ async function addGrpcMethods(graph, services) {
10389
10997
 
10390
10998
  // src/extract/calls/index.ts
10391
10999
  init_cjs_shims();
10392
- var import_types44 = require("@neat.is/types");
11000
+ var import_types45 = require("@neat.is/types");
10393
11001
 
10394
11002
  // src/extract/calls/http.ts
10395
11003
  init_cjs_shims();
10396
- var import_node_path38 = __toESM(require("path"), 1);
11004
+ var import_node_path39 = __toESM(require("path"), 1);
10397
11005
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
10398
11006
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
10399
11007
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -10476,7 +11084,7 @@ async function addHttpCallEdges(graph, services) {
10476
11084
  const seen = /* @__PURE__ */ new Set();
10477
11085
  for (const file of files) {
10478
11086
  if (isTestPath(file.path)) continue;
10479
- const parser = parserForExt(import_node_path38.default.extname(file.path), parserCache);
11087
+ const parser = parserForExt(import_node_path39.default.extname(file.path), parserCache);
10480
11088
  let sites;
10481
11089
  try {
10482
11090
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -10485,7 +11093,7 @@ async function addHttpCallEdges(graph, services) {
10485
11093
  continue;
10486
11094
  }
10487
11095
  if (sites.length === 0) continue;
10488
- const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
11096
+ const relFile = toPosix(import_node_path39.default.relative(service.dir, file.path));
10489
11097
  for (const site of sites) {
10490
11098
  const targetId = hostToNodeId.get(site.host);
10491
11099
  if (!targetId || targetId === service.node.id) continue;
@@ -10539,7 +11147,7 @@ async function addHttpCallEdges(graph, services) {
10539
11147
 
10540
11148
  // src/extract/calls/route-match.ts
10541
11149
  init_cjs_shims();
10542
- var import_node_path39 = __toESM(require("path"), 1);
11150
+ var import_node_path40 = __toESM(require("path"), 1);
10543
11151
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
10544
11152
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
10545
11153
  var import_types28 = require("@neat.is/types");
@@ -10738,7 +11346,7 @@ async function addRouteCallEdges(graph, services) {
10738
11346
  const seen = /* @__PURE__ */ new Set();
10739
11347
  for (const file of files) {
10740
11348
  if (isTestPath(file.path)) continue;
10741
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path39.default.extname(file.path))) continue;
11349
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path40.default.extname(file.path))) continue;
10742
11350
  let sites;
10743
11351
  try {
10744
11352
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10747,7 +11355,7 @@ async function addRouteCallEdges(graph, services) {
10747
11355
  continue;
10748
11356
  }
10749
11357
  if (sites.length === 0) continue;
10750
- const relFile = toPosix(import_node_path39.default.relative(service.dir, file.path));
11358
+ const relFile = toPosix(import_node_path40.default.relative(service.dir, file.path));
10751
11359
  for (const site of sites) {
10752
11360
  const serverServiceId = hostToNodeId.get(site.host);
10753
11361
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10808,7 +11416,7 @@ async function addRouteCallEdges(graph, services) {
10808
11416
 
10809
11417
  // src/extract/calls/kafka.ts
10810
11418
  init_cjs_shims();
10811
- var import_node_path40 = __toESM(require("path"), 1);
11419
+ var import_node_path41 = __toESM(require("path"), 1);
10812
11420
  var import_types29 = require("@neat.is/types");
10813
11421
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10814
11422
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
@@ -10896,13 +11504,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10896
11504
  // call sites — verified-call-site tier (ADR-066).
10897
11505
  confidenceKind: "verified-call-site",
10898
11506
  evidence: {
10899
- file: import_node_path40.default.relative(serviceDir, file.path),
11507
+ file: import_node_path41.default.relative(serviceDir, file.path),
10900
11508
  line,
10901
11509
  snippet: snippet(file.content, line)
10902
11510
  }
10903
11511
  });
10904
11512
  };
10905
- if (import_node_path40.default.extname(file.path) === ".go") {
11513
+ if (import_node_path41.default.extname(file.path) === ".go") {
10906
11514
  goSaramaEndpoints(file.content, make);
10907
11515
  } else {
10908
11516
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -10913,7 +11521,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10913
11521
 
10914
11522
  // src/extract/calls/redis.ts
10915
11523
  init_cjs_shims();
10916
- var import_node_path41 = __toESM(require("path"), 1);
11524
+ var import_node_path42 = __toESM(require("path"), 1);
10917
11525
  var import_types30 = require("@neat.is/types");
10918
11526
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10919
11527
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10936,7 +11544,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10936
11544
  // support tier (ADR-066).
10937
11545
  confidenceKind: "url-with-structural-support",
10938
11546
  evidence: {
10939
- file: import_node_path41.default.relative(serviceDir, file.path),
11547
+ file: import_node_path42.default.relative(serviceDir, file.path),
10940
11548
  line,
10941
11549
  snippet: snippet(file.content, line)
10942
11550
  }
@@ -10947,7 +11555,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10947
11555
 
10948
11556
  // src/extract/calls/aws.ts
10949
11557
  init_cjs_shims();
10950
- var import_node_path42 = __toESM(require("path"), 1);
11558
+ var import_node_path43 = __toESM(require("path"), 1);
10951
11559
  var import_types31 = require("@neat.is/types");
10952
11560
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10953
11561
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10981,7 +11589,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10981
11589
  // (ADR-066).
10982
11590
  confidenceKind: "verified-call-site",
10983
11591
  evidence: {
10984
- file: import_node_path42.default.relative(serviceDir, file.path),
11592
+ file: import_node_path43.default.relative(serviceDir, file.path),
10985
11593
  line,
10986
11594
  snippet: snippet(file.content, line)
10987
11595
  }
@@ -11006,7 +11614,7 @@ function awsEndpointsFromFile(file, serviceDir) {
11006
11614
 
11007
11615
  // src/extract/calls/grpc.ts
11008
11616
  init_cjs_shims();
11009
- var import_node_path43 = __toESM(require("path"), 1);
11617
+ var import_node_path44 = __toESM(require("path"), 1);
11010
11618
  var import_types32 = require("@neat.is/types");
11011
11619
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
11012
11620
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -11065,7 +11673,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11065
11673
  // tier (ADR-066).
11066
11674
  confidenceKind: "verified-call-site",
11067
11675
  evidence: {
11068
- file: import_node_path43.default.relative(serviceDir, file.path),
11676
+ file: import_node_path44.default.relative(serviceDir, file.path),
11069
11677
  line,
11070
11678
  snippet: snippet(file.content, line)
11071
11679
  }
@@ -11076,7 +11684,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11076
11684
 
11077
11685
  // src/extract/calls/supabase.ts
11078
11686
  init_cjs_shims();
11079
- var import_node_path44 = __toESM(require("path"), 1);
11687
+ var import_node_path45 = __toESM(require("path"), 1);
11080
11688
  var import_types33 = require("@neat.is/types");
11081
11689
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
11082
11690
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -11135,7 +11743,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11135
11743
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
11136
11744
  confidenceKind: "verified-call-site",
11137
11745
  evidence: {
11138
- file: import_node_path44.default.relative(serviceDir, file.path),
11746
+ file: import_node_path45.default.relative(serviceDir, file.path),
11139
11747
  line,
11140
11748
  snippet: snippet(file.content, line)
11141
11749
  }
@@ -11162,7 +11770,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11162
11770
  edgeType: "CALLS",
11163
11771
  confidenceKind: "verified-call-site",
11164
11772
  evidence: {
11165
- file: import_node_path44.default.relative(serviceDir, file.path),
11773
+ file: import_node_path45.default.relative(serviceDir, file.path),
11166
11774
  line,
11167
11775
  snippet: snippet(file.content, line)
11168
11776
  }
@@ -11174,7 +11782,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11174
11782
 
11175
11783
  // src/extract/calls/firestore.ts
11176
11784
  init_cjs_shims();
11177
- var import_node_path45 = __toESM(require("path"), 1);
11785
+ var import_node_path46 = __toESM(require("path"), 1);
11178
11786
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
11179
11787
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
11180
11788
  var import_types34 = require("@neat.is/types");
@@ -11213,7 +11821,7 @@ function isFirestoreClientFactory(node) {
11213
11821
  }
11214
11822
  function firestoreClientVars(root) {
11215
11823
  const vars = /* @__PURE__ */ new Set();
11216
- const walk9 = (node) => {
11824
+ const walk10 = (node) => {
11217
11825
  if (node.type === "variable_declarator") {
11218
11826
  const name = node.childForFieldName("name");
11219
11827
  let value = node.childForFieldName("value");
@@ -11222,9 +11830,9 @@ function firestoreClientVars(root) {
11222
11830
  vars.add(name.text);
11223
11831
  }
11224
11832
  }
11225
- for (const c of namedChildren(node)) walk9(c);
11833
+ for (const c of namedChildren(node)) walk10(c);
11226
11834
  };
11227
- walk9(root);
11835
+ walk10(root);
11228
11836
  return vars;
11229
11837
  }
11230
11838
  function isClientExpr(node, clientVars) {
@@ -11345,7 +11953,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11345
11953
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
11346
11954
  if (!hasClient && !hasAdmin) return [];
11347
11955
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11348
- const tree = parseSource3(parserForExt2(import_node_path45.default.extname(file.path)), file.content);
11956
+ const tree = parseSource3(parserForExt2(import_node_path46.default.extname(file.path)), file.content);
11349
11957
  const clientVars = firestoreClientVars(tree.rootNode);
11350
11958
  const collLine = /* @__PURE__ */ new Map();
11351
11959
  const writes = /* @__PURE__ */ new Map();
@@ -11379,7 +11987,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11379
11987
  }
11380
11988
  s.add(field);
11381
11989
  };
11382
- const walk9 = (node) => {
11990
+ const walk10 = (node) => {
11383
11991
  if (node.type === "call_expression") {
11384
11992
  const fn = node.childForFieldName("function");
11385
11993
  const line = node.startPosition.row + 1;
@@ -11419,9 +12027,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11419
12027
  }
11420
12028
  }
11421
12029
  }
11422
- for (const c of namedChildren(node)) walk9(c);
12030
+ for (const c of namedChildren(node)) walk10(c);
11423
12031
  };
11424
- walk9(tree.rootNode);
12032
+ walk10(tree.rootNode);
11425
12033
  const out = [];
11426
12034
  for (const [collPath, line] of collLine) {
11427
12035
  const byField = writes.get(collPath);
@@ -11449,7 +12057,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11449
12057
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
11450
12058
  ...sdkWrites ? { sdkWrites } : {},
11451
12059
  evidence: {
11452
- file: import_node_path45.default.relative(serviceDir, file.path),
12060
+ file: import_node_path46.default.relative(serviceDir, file.path),
11453
12061
  line,
11454
12062
  snippet: snippet(file.content, line)
11455
12063
  }
@@ -11460,7 +12068,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11460
12068
 
11461
12069
  // src/extract/calls/mongoose.ts
11462
12070
  init_cjs_shims();
11463
- var import_node_path46 = __toESM(require("path"), 1);
12071
+ var import_node_path47 = __toESM(require("path"), 1);
11464
12072
  var import_types35 = require("@neat.is/types");
11465
12073
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
11466
12074
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -11611,7 +12219,7 @@ function endpoint(r, file, serviceDir, matchText) {
11611
12219
  kind: r.kind,
11612
12220
  edgeType: "CALLS",
11613
12221
  confidenceKind: "verified-call-site",
11614
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12222
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11615
12223
  };
11616
12224
  }
11617
12225
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11701,7 +12309,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11701
12309
  const registry = /* @__PURE__ */ new Map();
11702
12310
  for (const f of mongooseFiles) {
11703
12311
  const fx = fileExportsOf(f.content, pluralizeOn);
11704
- if (fx) registry.set(toPosix(import_node_path46.default.relative(serviceDir, f.path)), fx);
12312
+ if (fx) registry.set(toPosix(import_node_path47.default.relative(serviceDir, f.path)), fx);
11705
12313
  }
11706
12314
  if (registry.size === 0) return [];
11707
12315
  const out = [];
@@ -11712,7 +12320,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11712
12320
  const directColl = /* @__PURE__ */ new Map();
11713
12321
  const nsExports = /* @__PURE__ */ new Map();
11714
12322
  for (const b of bindings) {
11715
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path46.default.dirname(f.path), serviceDir, null);
12323
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path47.default.dirname(f.path), serviceDir, null);
11716
12324
  if (!resolvedRel) continue;
11717
12325
  const fx = registry.get(resolvedRel);
11718
12326
  if (!fx) continue;
@@ -11756,7 +12364,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11756
12364
 
11757
12365
  // src/extract/calls/sqlalchemy.ts
11758
12366
  init_cjs_shims();
11759
- var import_node_path47 = __toESM(require("path"), 1);
12367
+ var import_node_path48 = __toESM(require("path"), 1);
11760
12368
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11761
12369
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11762
12370
  var import_types36 = require("@neat.is/types");
@@ -11895,7 +12503,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11895
12503
  childTable,
11896
12504
  parentTable,
11897
12505
  evidence: {
11898
- file: import_node_path47.default.relative(serviceDir, file.path),
12506
+ file: import_node_path48.default.relative(serviceDir, file.path),
11899
12507
  line,
11900
12508
  snippet: snippet(file.content, line)
11901
12509
  }
@@ -11920,7 +12528,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11920
12528
  confidenceKind: "verified-call-site",
11921
12529
  ...columns && columns.length > 0 ? { columns } : {},
11922
12530
  evidence: {
11923
- file: import_node_path47.default.relative(serviceDir, file.path),
12531
+ file: import_node_path48.default.relative(serviceDir, file.path),
11924
12532
  line,
11925
12533
  snippet: snippet(file.content, line)
11926
12534
  }
@@ -12027,7 +12635,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12027
12635
  edgeType: "CALLS",
12028
12636
  confidenceKind: "verified-call-site",
12029
12637
  evidence: {
12030
- file: import_node_path47.default.relative(serviceDir, file.path),
12638
+ file: import_node_path48.default.relative(serviceDir, file.path),
12031
12639
  line,
12032
12640
  snippet: snippet(file.content, line)
12033
12641
  }
@@ -12039,7 +12647,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12039
12647
 
12040
12648
  // src/extract/calls/django-orm.ts
12041
12649
  init_cjs_shims();
12042
- var import_node_path48 = __toESM(require("path"), 1);
12650
+ var import_node_path49 = __toESM(require("path"), 1);
12043
12651
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
12044
12652
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
12045
12653
  var import_types37 = require("@neat.is/types");
@@ -12109,7 +12717,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12109
12717
  const tree = parseSource7(makePyParser4(), file.content);
12110
12718
  const out = [];
12111
12719
  const seen = /* @__PURE__ */ new Set();
12112
- const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
12720
+ const defaultAppLabel = import_node_path49.default.basename(import_node_path49.default.dirname(file.path));
12113
12721
  walk4(tree.rootNode, (node) => {
12114
12722
  if (node.type !== "class_definition") return;
12115
12723
  if (!extendsDjangoModel(node)) return;
@@ -12127,7 +12735,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12127
12735
  kind: "sql-table",
12128
12736
  edgeType: "CALLS",
12129
12737
  confidenceKind: "verified-call-site",
12130
- evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12738
+ evidence: { file: import_node_path49.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12131
12739
  });
12132
12740
  });
12133
12741
  return out;
@@ -12135,7 +12743,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12135
12743
 
12136
12744
  // src/extract/calls/drizzle.ts
12137
12745
  init_cjs_shims();
12138
- var import_node_path49 = __toESM(require("path"), 1);
12746
+ var import_node_path50 = __toESM(require("path"), 1);
12139
12747
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12140
12748
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12141
12749
  var import_types38 = require("@neat.is/types");
@@ -12213,10 +12821,10 @@ function columnsFromObject(obj) {
12213
12821
  }
12214
12822
  function drizzleEndpointsFromFile(file, serviceDir) {
12215
12823
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12216
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12824
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12217
12825
  const out = [];
12218
12826
  const seen = /* @__PURE__ */ new Set();
12219
- const walk9 = (node) => {
12827
+ const walk10 = (node) => {
12220
12828
  if (node.type === "call_expression") {
12221
12829
  const fn = node.childForFieldName("function");
12222
12830
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -12236,7 +12844,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12236
12844
  confidenceKind: "structural",
12237
12845
  columns,
12238
12846
  evidence: {
12239
- file: import_node_path49.default.relative(serviceDir, file.path),
12847
+ file: import_node_path50.default.relative(serviceDir, file.path),
12240
12848
  line,
12241
12849
  snippet: snippet(file.content, line)
12242
12850
  }
@@ -12244,9 +12852,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12244
12852
  }
12245
12853
  }
12246
12854
  }
12247
- for (const c of namedChildren4(node)) walk9(c);
12855
+ for (const c of namedChildren4(node)) walk10(c);
12248
12856
  };
12249
- walk9(tree.rootNode);
12857
+ walk10(tree.rootNode);
12250
12858
  return out;
12251
12859
  }
12252
12860
  function enclosingVarName(call) {
@@ -12268,7 +12876,7 @@ function enclosingVarName(call) {
12268
12876
  function collectDrizzleTables(root) {
12269
12877
  const tables = [];
12270
12878
  const varToTable = /* @__PURE__ */ new Map();
12271
- const walk9 = (node) => {
12879
+ const walk10 = (node) => {
12272
12880
  if (node.type === "call_expression") {
12273
12881
  const fn = node.childForFieldName("function");
12274
12882
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -12283,9 +12891,9 @@ function collectDrizzleTables(root) {
12283
12891
  }
12284
12892
  }
12285
12893
  }
12286
- for (const c of namedChildren4(node)) walk9(c);
12894
+ for (const c of namedChildren4(node)) walk10(c);
12287
12895
  };
12288
- walk9(root);
12896
+ walk10(root);
12289
12897
  return { tables, varToTable };
12290
12898
  }
12291
12899
  function referencesTargetVar(call) {
@@ -12302,13 +12910,13 @@ function referencesTargetVar(call) {
12302
12910
  }
12303
12911
  function drizzleForeignKeys(file, serviceDir) {
12304
12912
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12305
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12913
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12306
12914
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
12307
12915
  const out = [];
12308
12916
  const seen = /* @__PURE__ */ new Set();
12309
12917
  for (const table of tables) {
12310
12918
  if (!table.object) continue;
12311
- const walk9 = (node) => {
12919
+ const walk10 = (node) => {
12312
12920
  if (node.type === "call_expression") {
12313
12921
  const targetVar = referencesTargetVar(node);
12314
12922
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -12321,7 +12929,7 @@ function drizzleForeignKeys(file, serviceDir) {
12321
12929
  childTable: table.tableName,
12322
12930
  parentTable,
12323
12931
  evidence: {
12324
- file: import_node_path49.default.relative(serviceDir, file.path),
12932
+ file: import_node_path50.default.relative(serviceDir, file.path),
12325
12933
  line,
12326
12934
  snippet: snippet(file.content, line)
12327
12935
  }
@@ -12329,16 +12937,16 @@ function drizzleForeignKeys(file, serviceDir) {
12329
12937
  }
12330
12938
  }
12331
12939
  }
12332
- for (const c of namedChildren4(node)) walk9(c);
12940
+ for (const c of namedChildren4(node)) walk10(c);
12333
12941
  };
12334
- walk9(table.object);
12942
+ walk10(table.object);
12335
12943
  }
12336
12944
  return out;
12337
12945
  }
12338
12946
 
12339
12947
  // src/extract/calls/prisma.ts
12340
12948
  init_cjs_shims();
12341
- var import_node_path50 = __toESM(require("path"), 1);
12949
+ var import_node_path51 = __toESM(require("path"), 1);
12342
12950
  var import_types39 = require("@neat.is/types");
12343
12951
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
12344
12952
  "Int",
@@ -12394,7 +13002,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12394
13002
  confidenceKind: "structural",
12395
13003
  columns: b.columns,
12396
13004
  evidence: {
12397
- file: import_node_path50.default.relative(serviceDir, file.path),
13005
+ file: import_node_path51.default.relative(serviceDir, file.path),
12398
13006
  line: b.startLine,
12399
13007
  snippet: snippet(content, b.startLine)
12400
13008
  }
@@ -12455,7 +13063,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12455
13063
  }
12456
13064
  async function prismaColumnEndpoints(serviceDir) {
12457
13065
  const schemaPath = await findFirst(serviceDir, [
12458
- import_node_path50.default.join("prisma", "schema.prisma"),
13066
+ import_node_path51.default.join("prisma", "schema.prisma"),
12459
13067
  "schema.prisma"
12460
13068
  ]);
12461
13069
  if (!schemaPath) return [];
@@ -12530,7 +13138,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12530
13138
  childTable: current.table,
12531
13139
  parentTable,
12532
13140
  evidence: {
12533
- file: import_node_path50.default.relative(serviceDir, file.path),
13141
+ file: import_node_path51.default.relative(serviceDir, file.path),
12534
13142
  line: lineNo,
12535
13143
  snippet: snippet(content, lineNo)
12536
13144
  }
@@ -12545,7 +13153,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12545
13153
  }
12546
13154
  async function prismaForeignKeys(serviceDir) {
12547
13155
  const schemaPath = await findFirst(serviceDir, [
12548
- import_node_path50.default.join("prisma", "schema.prisma"),
13156
+ import_node_path51.default.join("prisma", "schema.prisma"),
12549
13157
  "schema.prisma"
12550
13158
  ]);
12551
13159
  if (!schemaPath) return [];
@@ -12556,7 +13164,7 @@ async function prismaForeignKeys(serviceDir) {
12556
13164
 
12557
13165
  // src/extract/calls/activerecord.ts
12558
13166
  init_cjs_shims();
12559
- var import_node_path51 = __toESM(require("path"), 1);
13167
+ var import_node_path52 = __toESM(require("path"), 1);
12560
13168
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12561
13169
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
12562
13170
  var import_types40 = require("@neat.is/types");
@@ -12764,7 +13372,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12764
13372
  confidenceKind: "structural",
12765
13373
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12766
13374
  evidence: {
12767
- file: import_node_path51.default.relative(serviceDir, file.path),
13375
+ file: import_node_path52.default.relative(serviceDir, file.path),
12768
13376
  line: table.line,
12769
13377
  snippet: snippet(file.content, table.line)
12770
13378
  }
@@ -12786,7 +13394,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12786
13394
  childTable,
12787
13395
  parentTable,
12788
13396
  evidence: {
12789
- file: import_node_path51.default.relative(serviceDir, file.path),
13397
+ file: import_node_path52.default.relative(serviceDir, file.path),
12790
13398
  line,
12791
13399
  snippet: snippet(file.content, line)
12792
13400
  }
@@ -12869,7 +13477,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12869
13477
  edgeType: "CALLS",
12870
13478
  confidenceKind: "verified-call-site",
12871
13479
  evidence: {
12872
- file: import_node_path51.default.relative(serviceDir, file.path),
13480
+ file: import_node_path52.default.relative(serviceDir, file.path),
12873
13481
  line,
12874
13482
  snippet: snippet(file.content, line)
12875
13483
  }
@@ -12906,7 +13514,7 @@ function railsModelForeignKeys(file, serviceDir) {
12906
13514
  childTable,
12907
13515
  parentTable,
12908
13516
  evidence: {
12909
- file: import_node_path51.default.relative(serviceDir, file.path),
13517
+ file: import_node_path52.default.relative(serviceDir, file.path),
12910
13518
  line,
12911
13519
  snippet: snippet(file.content, line)
12912
13520
  }
@@ -12918,7 +13526,7 @@ function railsModelForeignKeys(file, serviceDir) {
12918
13526
 
12919
13527
  // src/extract/calls/eloquent.ts
12920
13528
  init_cjs_shims();
12921
- var import_node_path52 = __toESM(require("path"), 1);
13529
+ var import_node_path53 = __toESM(require("path"), 1);
12922
13530
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12923
13531
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12924
13532
  var import_types41 = require("@neat.is/types");
@@ -13175,7 +13783,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13175
13783
  confidenceKind: "structural",
13176
13784
  ...columns.length > 0 ? { columns } : {},
13177
13785
  evidence: {
13178
- file: import_node_path52.default.relative(serviceDir, file.path),
13786
+ file: import_node_path53.default.relative(serviceDir, file.path),
13179
13787
  line: bp.line,
13180
13788
  snippet: snippet(file.content, bp.line)
13181
13789
  }
@@ -13197,7 +13805,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13197
13805
  childTable,
13198
13806
  parentTable,
13199
13807
  evidence: {
13200
- file: import_node_path52.default.relative(serviceDir, file.path),
13808
+ file: import_node_path53.default.relative(serviceDir, file.path),
13201
13809
  line,
13202
13810
  snippet: snippet(file.content, line)
13203
13811
  }
@@ -13317,7 +13925,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
13317
13925
  edgeType: "CALLS",
13318
13926
  confidenceKind: "verified-call-site",
13319
13927
  evidence: {
13320
- file: import_node_path52.default.relative(serviceDir, file.path),
13928
+ file: import_node_path53.default.relative(serviceDir, file.path),
13321
13929
  line,
13322
13930
  snippet: snippet(file.content, line)
13323
13931
  }
@@ -13353,7 +13961,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13353
13961
  childTable,
13354
13962
  parentTable,
13355
13963
  evidence: {
13356
- file: import_node_path52.default.relative(serviceDir, file.path),
13964
+ file: import_node_path53.default.relative(serviceDir, file.path),
13357
13965
  line,
13358
13966
  snippet: snippet(file.content, line)
13359
13967
  }
@@ -13365,7 +13973,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13365
13973
 
13366
13974
  // src/extract/calls/go.ts
13367
13975
  init_cjs_shims();
13368
- var import_node_path55 = __toESM(require("path"), 1);
13976
+ var import_node_path56 = __toESM(require("path"), 1);
13369
13977
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13370
13978
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
13371
13979
  var import_types42 = require("@neat.is/types");
@@ -13440,7 +14048,7 @@ function firstStringLiteralArg(argsNode) {
13440
14048
  return null;
13441
14049
  }
13442
14050
  function goSqlEndpointsFromFile(file, serviceDir) {
13443
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
14051
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
13444
14052
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
13445
14053
  const tree = parseSource10(makeGoParser3(), file.content);
13446
14054
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -13469,7 +14077,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13469
14077
  confidenceKind: "verified-call-site",
13470
14078
  ...columns.length > 0 ? { columns } : {},
13471
14079
  evidence: {
13472
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14080
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
13473
14081
  line,
13474
14082
  snippet: snippet(file.content, line)
13475
14083
  }
@@ -13480,7 +14088,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13480
14088
 
13481
14089
  // src/extract/calls/gorm.ts
13482
14090
  init_cjs_shims();
13483
- var import_node_path56 = __toESM(require("path"), 1);
14091
+ var import_node_path57 = __toESM(require("path"), 1);
13484
14092
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13485
14093
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
13486
14094
  var import_types43 = require("@neat.is/types");
@@ -13913,7 +14521,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13913
14521
  seen.delete(struct.name);
13914
14522
  }
13915
14523
  function gormEndpointsFromFile(file, serviceDir) {
13916
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14524
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
13917
14525
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13918
14526
  const tree = parseSource11(makeGoParser4(), file.content);
13919
14527
  const { structs, models, tableFor } = analyze(tree);
@@ -13935,7 +14543,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13935
14543
  confidenceKind: "structural",
13936
14544
  ...columns.length > 0 ? { columns } : {},
13937
14545
  evidence: {
13938
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14546
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
13939
14547
  line: struct.line,
13940
14548
  snippet: snippet(file.content, struct.line)
13941
14549
  }
@@ -13944,7 +14552,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13944
14552
  return out;
13945
14553
  }
13946
14554
  function gormForeignKeys(file, serviceDir) {
13947
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14555
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
13948
14556
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13949
14557
  const tree = parseSource11(makeGoParser4(), file.content);
13950
14558
  const { structs, models, tableFor } = analyze(tree);
@@ -13959,7 +14567,7 @@ function gormForeignKeys(file, serviceDir) {
13959
14567
  childTable,
13960
14568
  parentTable,
13961
14569
  evidence: {
13962
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14570
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
13963
14571
  line,
13964
14572
  snippet: snippet(file.content, line)
13965
14573
  }
@@ -13994,15 +14602,131 @@ function gormForeignKeys(file, serviceDir) {
13994
14602
  return out;
13995
14603
  }
13996
14604
 
14605
+ // src/extract/calls/efcore.ts
14606
+ init_cjs_shims();
14607
+ var import_node_path58 = __toESM(require("path"), 1);
14608
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14609
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14610
+ var import_types44 = require("@neat.is/types");
14611
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
14612
+ var PARSE_CHUNK12 = 16384;
14613
+ function makeCsParser() {
14614
+ const p = new import_tree_sitter16.default();
14615
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
14616
+ return p;
14617
+ }
14618
+ function parseSource12(parser, source) {
14619
+ return parser.parse(
14620
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
14621
+ );
14622
+ }
14623
+ function walk9(node, visit) {
14624
+ visit(node);
14625
+ for (let i = 0; i < node.namedChildCount; i++) {
14626
+ const c = node.namedChild(i);
14627
+ if (c) walk9(c, visit);
14628
+ }
14629
+ }
14630
+ function firstChildOfType(node, type) {
14631
+ for (let i = 0; i < node.namedChildCount; i++) {
14632
+ const c = node.namedChild(i);
14633
+ if (c?.type === type) return c;
14634
+ }
14635
+ return null;
14636
+ }
14637
+ function csStringLiteral(node) {
14638
+ if (node.type === "string_literal") {
14639
+ let out = "";
14640
+ for (let i = 0; i < node.namedChildCount; i++) {
14641
+ const c = node.namedChild(i);
14642
+ if (c?.type === "string_literal_content") out += c.text;
14643
+ }
14644
+ return out;
14645
+ }
14646
+ if (node.type === "verbatim_string_literal") {
14647
+ const t = node.text;
14648
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
14649
+ }
14650
+ return null;
14651
+ }
14652
+ function attributeName(attr) {
14653
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
14654
+ if (!nameNode) return null;
14655
+ const text = nameNode.text;
14656
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
14657
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
14658
+ }
14659
+ function tableFromAttribute(attr) {
14660
+ if (attributeName(attr) !== "Table") return null;
14661
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
14662
+ if (!args) return null;
14663
+ for (let i = 0; i < args.namedChildCount; i++) {
14664
+ const arg = args.namedChild(i);
14665
+ if (arg?.type !== "attribute_argument") continue;
14666
+ const first = arg.namedChild(0);
14667
+ if (!first) continue;
14668
+ const value = csStringLiteral(first);
14669
+ if (value !== null) return value;
14670
+ return null;
14671
+ }
14672
+ return null;
14673
+ }
14674
+ function tableFromToTable(call) {
14675
+ const fn = call.childForFieldName("function");
14676
+ if (fn?.type !== "member_access_expression") return null;
14677
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14678
+ if (method?.text !== "ToTable") return null;
14679
+ const args = call.childForFieldName("arguments");
14680
+ const firstArg2 = args?.namedChild(0);
14681
+ if (firstArg2?.type !== "argument") return null;
14682
+ const value = firstArg2.namedChild(0);
14683
+ return value ? csStringLiteral(value) : null;
14684
+ }
14685
+ function efcoreEndpointsFromFile(file, serviceDir) {
14686
+ if (import_node_path58.default.extname(file.path) !== ".cs") return [];
14687
+ if (!EFCORE_GATE.test(file.content)) return [];
14688
+ const tree = parseSource12(makeCsParser(), file.content);
14689
+ const out = [];
14690
+ const seen = /* @__PURE__ */ new Set();
14691
+ const push = (name, line) => {
14692
+ if (!name || seen.has(name)) return;
14693
+ seen.add(name);
14694
+ out.push({
14695
+ infraId: (0, import_types44.infraId)("sql-table", name),
14696
+ name,
14697
+ kind: "sql-table",
14698
+ edgeType: "CALLS",
14699
+ confidenceKind: "structural",
14700
+ evidence: {
14701
+ file: toPosix(import_node_path58.default.relative(serviceDir, file.path)),
14702
+ line,
14703
+ snippet: snippet(file.content, line)
14704
+ }
14705
+ });
14706
+ };
14707
+ walk9(tree.rootNode, (node) => {
14708
+ if (node.type === "attribute") {
14709
+ const table = tableFromAttribute(node);
14710
+ if (table) push(table, node.startPosition.row + 1);
14711
+ return;
14712
+ }
14713
+ if (node.type === "invocation_expression") {
14714
+ const table = tableFromToTable(node);
14715
+ if (table) push(table, node.startPosition.row + 1);
14716
+ }
14717
+ });
14718
+ return out;
14719
+ }
14720
+
13997
14721
  // src/extract/calls/index.ts
13998
14722
  function edgeTypeFromEndpoint(ep) {
13999
14723
  switch (ep.edgeType) {
14000
14724
  case "PUBLISHES_TO":
14001
- return import_types44.EdgeType.PUBLISHES_TO;
14725
+ return import_types45.EdgeType.PUBLISHES_TO;
14002
14726
  case "CONSUMES_FROM":
14003
- return import_types44.EdgeType.CONSUMES_FROM;
14727
+ return import_types45.EdgeType.CONSUMES_FROM;
14004
14728
  default:
14005
- return import_types44.EdgeType.CALLS;
14729
+ return import_types45.EdgeType.CALLS;
14006
14730
  }
14007
14731
  }
14008
14732
  function isAwsKind(kind) {
@@ -14052,6 +14776,11 @@ async function addExternalEndpointEdges(graph, services) {
14052
14776
  } catch (err) {
14053
14777
  recordExtractionError("laravel eloquent extraction", file.path, err);
14054
14778
  }
14779
+ try {
14780
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14781
+ } catch (err) {
14782
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14783
+ }
14055
14784
  }
14056
14785
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
14057
14786
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -14062,7 +14791,7 @@ async function addExternalEndpointEdges(graph, services) {
14062
14791
  if (!graph.hasNode(ep.infraId)) {
14063
14792
  const node = {
14064
14793
  id: ep.infraId,
14065
- type: import_types44.NodeType.InfraNode,
14794
+ type: import_types45.NodeType.InfraNode,
14066
14795
  name: ep.name,
14067
14796
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
14068
14797
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -14075,21 +14804,21 @@ async function addExternalEndpointEdges(graph, services) {
14075
14804
  }
14076
14805
  if (ep.columns && ep.columns.length > 0) {
14077
14806
  const node = graph.getNodeAttributes(ep.infraId);
14078
- if (node.type === import_types44.NodeType.InfraNode) {
14807
+ if (node.type === import_types45.NodeType.InfraNode) {
14079
14808
  graph.replaceNodeAttributes(ep.infraId, {
14080
14809
  ...node,
14081
14810
  columns: foldColumns(
14082
14811
  node.columns,
14083
14812
  ep.columns,
14084
- import_types44.Provenance.EXTRACTED,
14085
- (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
14813
+ import_types45.Provenance.EXTRACTED,
14814
+ (0, import_types45.confidenceForExtracted)(ep.confidenceKind)
14086
14815
  )
14087
14816
  });
14088
14817
  }
14089
14818
  }
14090
14819
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
14091
14820
  const node = graph.getNodeAttributes(ep.infraId);
14092
- if (node.type === import_types44.NodeType.InfraNode) {
14821
+ if (node.type === import_types45.NodeType.InfraNode) {
14093
14822
  graph.replaceNodeAttributes(ep.infraId, {
14094
14823
  ...node,
14095
14824
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -14097,7 +14826,7 @@ async function addExternalEndpointEdges(graph, services) {
14097
14826
  }
14098
14827
  }
14099
14828
  const edgeType = edgeTypeFromEndpoint(ep);
14100
- const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
14829
+ const confidence = (0, import_types45.confidenceForExtracted)(ep.confidenceKind);
14101
14830
  const relFile = toPosix(ep.evidence.file);
14102
14831
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
14103
14832
  graph,
@@ -14107,7 +14836,7 @@ async function addExternalEndpointEdges(graph, services) {
14107
14836
  );
14108
14837
  nodesAdded += n;
14109
14838
  edgesAdded += e;
14110
- if (!(0, import_types44.passesExtractedFloor)(confidence)) {
14839
+ if (!(0, import_types45.passesExtractedFloor)(confidence)) {
14111
14840
  noteExtractedDropped({
14112
14841
  source: fileNodeId,
14113
14842
  target: ep.infraId,
@@ -14127,7 +14856,7 @@ async function addExternalEndpointEdges(graph, services) {
14127
14856
  source: fileNodeId,
14128
14857
  target: ep.infraId,
14129
14858
  type: edgeType,
14130
- provenance: import_types44.Provenance.EXTRACTED,
14859
+ provenance: import_types45.Provenance.EXTRACTED,
14131
14860
  confidence,
14132
14861
  evidence: ep.evidence
14133
14862
  };
@@ -14150,7 +14879,7 @@ async function addCallEdges(graph, services) {
14150
14879
 
14151
14880
  // src/extract/table-edges.ts
14152
14881
  init_cjs_shims();
14153
- var import_types45 = require("@neat.is/types");
14882
+ var import_types46 = require("@neat.is/types");
14154
14883
  async function addTableEdges(graph, services) {
14155
14884
  let nodesAdded = 0;
14156
14885
  let edgesAdded = 0;
@@ -14178,20 +14907,20 @@ async function addTableEdges(graph, services) {
14178
14907
  }
14179
14908
  refs.push(...modelRefs);
14180
14909
  for (const ref of refs) {
14181
- const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14182
- const parentId = (0, import_types45.infraId)("sql-table", ref.parentTable);
14910
+ const childId = (0, import_types46.infraId)("sql-table", ref.childTable);
14911
+ const parentId = (0, import_types46.infraId)("sql-table", ref.parentTable);
14183
14912
  if (childId === parentId) continue;
14184
14913
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
14185
14914
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
14186
- const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
14915
+ const edgeId = (0, import_types46.extractedEdgeId)(childId, parentId, import_types46.EdgeType.REFERENCES);
14187
14916
  if (graph.hasEdge(edgeId)) continue;
14188
14917
  const edge = {
14189
14918
  id: edgeId,
14190
14919
  source: childId,
14191
14920
  target: parentId,
14192
- type: import_types45.EdgeType.REFERENCES,
14193
- provenance: import_types45.Provenance.EXTRACTED,
14194
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14921
+ type: import_types46.EdgeType.REFERENCES,
14922
+ provenance: import_types46.Provenance.EXTRACTED,
14923
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
14195
14924
  evidence: ref.evidence
14196
14925
  };
14197
14926
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -14204,7 +14933,7 @@ function ensureTableNode(graph, id, name) {
14204
14933
  if (graph.hasNode(id)) return 0;
14205
14934
  const node = {
14206
14935
  id,
14207
- type: import_types45.NodeType.InfraNode,
14936
+ type: import_types46.NodeType.InfraNode,
14208
14937
  name,
14209
14938
  provider: "self",
14210
14939
  kind: "sql-table"
@@ -14218,16 +14947,16 @@ init_cjs_shims();
14218
14947
 
14219
14948
  // src/extract/infra/docker-compose.ts
14220
14949
  init_cjs_shims();
14221
- var import_node_path57 = __toESM(require("path"), 1);
14222
- var import_types47 = require("@neat.is/types");
14950
+ var import_node_path59 = __toESM(require("path"), 1);
14951
+ var import_types48 = require("@neat.is/types");
14223
14952
 
14224
14953
  // src/extract/infra/shared.ts
14225
14954
  init_cjs_shims();
14226
- var import_types46 = require("@neat.is/types");
14955
+ var import_types47 = require("@neat.is/types");
14227
14956
  function makeInfraNode(kind, name, provider = "self", extras) {
14228
14957
  return {
14229
- id: (0, import_types46.infraId)(kind, name),
14230
- type: import_types46.NodeType.InfraNode,
14958
+ id: (0, import_types47.infraId)(kind, name),
14959
+ type: import_types47.NodeType.InfraNode,
14231
14960
  name,
14232
14961
  provider,
14233
14962
  kind,
@@ -14271,8 +15000,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
14271
15000
  source: anchorId,
14272
15001
  target: node.id,
14273
15002
  type: edgeType,
14274
- provenance: import_types46.Provenance.EXTRACTED,
14275
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
15003
+ provenance: import_types47.Provenance.EXTRACTED,
15004
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
14276
15005
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14277
15006
  };
14278
15007
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14289,7 +15018,7 @@ function dependsOnList(value) {
14289
15018
  }
14290
15019
  function serviceNameToServiceNode(name, services) {
14291
15020
  for (const s of services) {
14292
- if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
15021
+ if (s.node.name === name || import_node_path59.default.basename(s.dir) === name) return s.node.id;
14293
15022
  }
14294
15023
  return null;
14295
15024
  }
@@ -14298,7 +15027,7 @@ async function addComposeInfra(graph, scanPath, services) {
14298
15027
  let edgesAdded = 0;
14299
15028
  let composePath = null;
14300
15029
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
14301
- const abs = import_node_path57.default.join(scanPath, name);
15030
+ const abs = import_node_path59.default.join(scanPath, name);
14302
15031
  if (await exists2(abs)) {
14303
15032
  composePath = abs;
14304
15033
  break;
@@ -14311,13 +15040,13 @@ async function addComposeInfra(graph, scanPath, services) {
14311
15040
  } catch (err) {
14312
15041
  recordExtractionError(
14313
15042
  "infra docker-compose",
14314
- import_node_path57.default.relative(scanPath, composePath),
15043
+ import_node_path59.default.relative(scanPath, composePath),
14315
15044
  err
14316
15045
  );
14317
15046
  return { nodesAdded, edgesAdded };
14318
15047
  }
14319
15048
  if (!compose?.services) return { nodesAdded, edgesAdded };
14320
- const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
15049
+ const evidenceFile = import_node_path59.default.relative(scanPath, composePath).split(import_node_path59.default.sep).join("/");
14321
15050
  const composeNameToNodeId = /* @__PURE__ */ new Map();
14322
15051
  for (const [composeName, svc] of Object.entries(compose.services)) {
14323
15052
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -14339,15 +15068,15 @@ async function addComposeInfra(graph, scanPath, services) {
14339
15068
  for (const dep of dependsOnList(svc.depends_on)) {
14340
15069
  const targetId = composeNameToNodeId.get(dep);
14341
15070
  if (!targetId) continue;
14342
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
15071
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types48.EdgeType.DEPENDS_ON);
14343
15072
  if (graph.hasEdge(edgeId)) continue;
14344
15073
  const edge = {
14345
15074
  id: edgeId,
14346
15075
  source: sourceId,
14347
15076
  target: targetId,
14348
- type: import_types47.EdgeType.DEPENDS_ON,
14349
- provenance: import_types47.Provenance.EXTRACTED,
14350
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
15077
+ type: import_types48.EdgeType.DEPENDS_ON,
15078
+ provenance: import_types48.Provenance.EXTRACTED,
15079
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
14351
15080
  evidence: { file: evidenceFile }
14352
15081
  };
14353
15082
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14359,9 +15088,9 @@ async function addComposeInfra(graph, scanPath, services) {
14359
15088
 
14360
15089
  // src/extract/infra/dockerfile.ts
14361
15090
  init_cjs_shims();
14362
- var import_node_path58 = __toESM(require("path"), 1);
14363
- var import_node_fs26 = require("fs");
14364
- var import_types48 = require("@neat.is/types");
15091
+ var import_node_path60 = __toESM(require("path"), 1);
15092
+ var import_node_fs27 = require("fs");
15093
+ var import_types49 = require("@neat.is/types");
14365
15094
  function readDockerfile(content) {
14366
15095
  let image = null;
14367
15096
  const ports = [];
@@ -14390,15 +15119,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14390
15119
  let nodesAdded = 0;
14391
15120
  let edgesAdded = 0;
14392
15121
  for (const service of services) {
14393
- const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
15122
+ const dockerfilePath = import_node_path60.default.join(service.dir, "Dockerfile");
14394
15123
  if (!await exists2(dockerfilePath)) continue;
14395
15124
  let content;
14396
15125
  try {
14397
- content = await import_node_fs26.promises.readFile(dockerfilePath, "utf8");
15126
+ content = await import_node_fs27.promises.readFile(dockerfilePath, "utf8");
14398
15127
  } catch (err) {
14399
15128
  recordExtractionError(
14400
15129
  "infra dockerfile",
14401
- import_node_path58.default.relative(scanPath, dockerfilePath),
15130
+ import_node_path60.default.relative(scanPath, dockerfilePath),
14402
15131
  err
14403
15132
  );
14404
15133
  continue;
@@ -14410,8 +15139,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14410
15139
  graph.addNode(node.id, node);
14411
15140
  nodesAdded++;
14412
15141
  }
14413
- const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14414
- const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
15142
+ const relDockerfile = toPosix(import_node_path60.default.relative(service.dir, dockerfilePath));
15143
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, dockerfilePath));
14415
15144
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14416
15145
  graph,
14417
15146
  service.pkg.name,
@@ -14420,15 +15149,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14420
15149
  );
14421
15150
  nodesAdded += fn;
14422
15151
  edgesAdded += fe;
14423
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
15152
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types49.EdgeType.RUNS_ON);
14424
15153
  if (!graph.hasEdge(edgeId)) {
14425
15154
  const edge = {
14426
15155
  id: edgeId,
14427
15156
  source: fileNodeId,
14428
15157
  target: node.id,
14429
- type: import_types48.EdgeType.RUNS_ON,
14430
- provenance: import_types48.Provenance.EXTRACTED,
14431
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15158
+ type: import_types49.EdgeType.RUNS_ON,
15159
+ provenance: import_types49.Provenance.EXTRACTED,
15160
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14432
15161
  evidence: {
14433
15162
  file: evidenceFile,
14434
15163
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -14443,15 +15172,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14443
15172
  graph.addNode(portNode.id, portNode);
14444
15173
  nodesAdded++;
14445
15174
  }
14446
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
15175
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types49.EdgeType.CONNECTS_TO);
14447
15176
  if (graph.hasEdge(portEdgeId)) continue;
14448
15177
  const portEdge = {
14449
15178
  id: portEdgeId,
14450
15179
  source: fileNodeId,
14451
15180
  target: portNode.id,
14452
- type: import_types48.EdgeType.CONNECTS_TO,
14453
- provenance: import_types48.Provenance.EXTRACTED,
14454
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15181
+ type: import_types49.EdgeType.CONNECTS_TO,
15182
+ provenance: import_types49.Provenance.EXTRACTED,
15183
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14455
15184
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
14456
15185
  };
14457
15186
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -14463,23 +15192,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14463
15192
 
14464
15193
  // src/extract/infra/terraform.ts
14465
15194
  init_cjs_shims();
14466
- var import_node_fs27 = require("fs");
14467
- var import_node_path59 = __toESM(require("path"), 1);
14468
- var import_types49 = require("@neat.is/types");
15195
+ var import_node_fs28 = require("fs");
15196
+ var import_node_path61 = __toESM(require("path"), 1);
15197
+ var import_types50 = require("@neat.is/types");
14469
15198
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
14470
15199
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
14471
15200
  async function walkTfFiles(start, depth = 0, max = 5) {
14472
15201
  if (depth > max) return [];
14473
15202
  const out = [];
14474
- const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15203
+ const entries = await import_node_fs28.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14475
15204
  for (const entry of entries) {
14476
15205
  if (entry.isDirectory()) {
14477
15206
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
14478
- const child = import_node_path59.default.join(start, entry.name);
15207
+ const child = import_node_path61.default.join(start, entry.name);
14479
15208
  if (await isPythonVenvDir(child)) continue;
14480
15209
  out.push(...await walkTfFiles(child, depth + 1, max));
14481
15210
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
14482
- out.push(import_node_path59.default.join(start, entry.name));
15211
+ out.push(import_node_path61.default.join(start, entry.name));
14483
15212
  }
14484
15213
  }
14485
15214
  return out;
@@ -14510,8 +15239,8 @@ async function addTerraformResources(graph, scanPath) {
14510
15239
  let edgesAdded = 0;
14511
15240
  const files = await walkTfFiles(scanPath);
14512
15241
  for (const file of files) {
14513
- const content = await import_node_fs27.promises.readFile(file, "utf8");
14514
- const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, file));
15242
+ const content = await import_node_fs28.promises.readFile(file, "utf8");
15243
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, file));
14515
15244
  const resources = [];
14516
15245
  const byKey = /* @__PURE__ */ new Map();
14517
15246
  RESOURCE_RE.lastIndex = 0;
@@ -14546,16 +15275,16 @@ async function addTerraformResources(graph, scanPath) {
14546
15275
  if (!target) continue;
14547
15276
  if (seen.has(target.nodeId)) continue;
14548
15277
  seen.add(target.nodeId);
14549
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
15278
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types50.EdgeType.DEPENDS_ON);
14550
15279
  if (graph.hasEdge(edgeId)) continue;
14551
15280
  const line = lineAt2(content, resource.bodyOffset + ref.index);
14552
15281
  const edge = {
14553
15282
  id: edgeId,
14554
15283
  source: resource.nodeId,
14555
15284
  target: target.nodeId,
14556
- type: import_types49.EdgeType.DEPENDS_ON,
14557
- provenance: import_types49.Provenance.EXTRACTED,
14558
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
15285
+ type: import_types50.EdgeType.DEPENDS_ON,
15286
+ provenance: import_types50.Provenance.EXTRACTED,
15287
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14559
15288
  evidence: { file: evidenceFile, line, snippet: key }
14560
15289
  };
14561
15290
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14568,8 +15297,8 @@ async function addTerraformResources(graph, scanPath) {
14568
15297
 
14569
15298
  // src/extract/infra/k8s.ts
14570
15299
  init_cjs_shims();
14571
- var import_node_fs28 = require("fs");
14572
- var import_node_path60 = __toESM(require("path"), 1);
15300
+ var import_node_fs29 = require("fs");
15301
+ var import_node_path62 = __toESM(require("path"), 1);
14573
15302
  var import_yaml3 = require("yaml");
14574
15303
  var K8S_KIND_TO_INFRA_KIND = {
14575
15304
  Service: "k8s-service",
@@ -14583,15 +15312,15 @@ var K8S_KIND_TO_INFRA_KIND = {
14583
15312
  async function walkYamlFiles2(start, depth = 0, max = 5) {
14584
15313
  if (depth > max) return [];
14585
15314
  const out = [];
14586
- const entries = await import_node_fs28.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15315
+ const entries = await import_node_fs29.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14587
15316
  for (const entry of entries) {
14588
15317
  if (entry.isDirectory()) {
14589
15318
  if (IGNORED_DIRS.has(entry.name)) continue;
14590
- const child = import_node_path60.default.join(start, entry.name);
15319
+ const child = import_node_path62.default.join(start, entry.name);
14591
15320
  if (await isPythonVenvDir(child)) continue;
14592
15321
  out.push(...await walkYamlFiles2(child, depth + 1, max));
14593
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
14594
- out.push(import_node_path60.default.join(start, entry.name));
15322
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path62.default.extname(entry.name))) {
15323
+ out.push(import_node_path62.default.join(start, entry.name));
14595
15324
  }
14596
15325
  }
14597
15326
  return out;
@@ -14600,7 +15329,7 @@ async function addK8sResources(graph, scanPath) {
14600
15329
  let nodesAdded = 0;
14601
15330
  const files = await walkYamlFiles2(scanPath);
14602
15331
  for (const file of files) {
14603
- const content = await import_node_fs28.promises.readFile(file, "utf8");
15332
+ const content = await import_node_fs29.promises.readFile(file, "utf8");
14604
15333
  let docs;
14605
15334
  try {
14606
15335
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14624,16 +15353,16 @@ async function addK8sResources(graph, scanPath) {
14624
15353
 
14625
15354
  // src/extract/infra/cloudflare.ts
14626
15355
  init_cjs_shims();
14627
- var import_node_fs29 = require("fs");
14628
- var import_node_path61 = __toESM(require("path"), 1);
15356
+ var import_node_fs30 = require("fs");
15357
+ var import_node_path63 = __toESM(require("path"), 1);
14629
15358
  var import_smol_toml3 = require("smol-toml");
14630
- var import_types50 = require("@neat.is/types");
15359
+ var import_types51 = require("@neat.is/types");
14631
15360
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14632
15361
  async function readWranglerConfig(dir) {
14633
15362
  for (const filename of WRANGLER_FILENAMES) {
14634
- const abs = import_node_path61.default.join(dir, filename);
15363
+ const abs = import_node_path63.default.join(dir, filename);
14635
15364
  if (!await exists2(abs)) continue;
14636
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15365
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14637
15366
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14638
15367
  return { config, relFile: filename, raw };
14639
15368
  }
@@ -14675,8 +15404,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14675
15404
  source: anchorId,
14676
15405
  target: node.id,
14677
15406
  type: edgeType,
14678
- provenance: import_types50.Provenance.EXTRACTED,
14679
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15407
+ provenance: import_types51.Provenance.EXTRACTED,
15408
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14680
15409
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14681
15410
  };
14682
15411
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14694,11 +15423,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14694
15423
  try {
14695
15424
  read = await readWranglerConfig(service.dir);
14696
15425
  } catch (err) {
14697
- recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
15426
+ recordExtractionError("infra cloudflare", import_node_path63.default.relative(scanPath, service.dir), err);
14698
15427
  continue;
14699
15428
  }
14700
15429
  if (!read || !read.config.name) continue;
14701
- const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, read.relFile)));
15430
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, read.relFile)));
14702
15431
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14703
15432
  }
14704
15433
  for (const worker of discovered) {
@@ -14710,7 +15439,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14710
15439
  }
14711
15440
  let anchorId = service.node.id;
14712
15441
  if (config.main) {
14713
- const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
15442
+ const entryRelPath = toPosix(import_node_path63.default.normalize(config.main));
14714
15443
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14715
15444
  graph,
14716
15445
  service.pkg.name,
@@ -14737,15 +15466,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14737
15466
  nodesAdded++;
14738
15467
  }
14739
15468
  if (runtimeNode.id !== anchorId) {
14740
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
15469
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types51.EdgeType.RUNS_ON);
14741
15470
  if (!graph.hasEdge(runsOnId)) {
14742
15471
  const edge = {
14743
15472
  id: runsOnId,
14744
15473
  source: anchorId,
14745
15474
  target: runtimeNode.id,
14746
- type: import_types50.EdgeType.RUNS_ON,
14747
- provenance: import_types50.Provenance.EXTRACTED,
14748
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15475
+ type: import_types51.EdgeType.RUNS_ON,
15476
+ provenance: import_types51.Provenance.EXTRACTED,
15477
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14749
15478
  evidence: {
14750
15479
  file: evidenceFile,
14751
15480
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14759,7 +15488,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14759
15488
  const result = addResourceEdge(
14760
15489
  graph,
14761
15490
  anchorId,
14762
- import_types50.EdgeType.CONNECTS_TO,
15491
+ import_types51.EdgeType.CONNECTS_TO,
14763
15492
  "cloudflare-route",
14764
15493
  route,
14765
15494
  evidenceFile,
@@ -14783,7 +15512,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14783
15512
  const result = addResourceEdge(
14784
15513
  graph,
14785
15514
  anchorId,
14786
- import_types50.EdgeType.DEPENDS_ON,
15515
+ import_types51.EdgeType.DEPENDS_ON,
14787
15516
  group.kind,
14788
15517
  name,
14789
15518
  evidenceFile,
@@ -14797,7 +15526,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14797
15526
  const result = addResourceEdge(
14798
15527
  graph,
14799
15528
  anchorId,
14800
- import_types50.EdgeType.DEPENDS_ON,
15529
+ import_types51.EdgeType.DEPENDS_ON,
14801
15530
  "cloudflare-cron",
14802
15531
  cron,
14803
15532
  evidenceFile,
@@ -14810,7 +15539,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14810
15539
  const result = addResourceEdge(
14811
15540
  graph,
14812
15541
  anchorId,
14813
- import_types50.EdgeType.DEPENDS_ON,
15542
+ import_types51.EdgeType.DEPENDS_ON,
14814
15543
  "cloudflare-env-var",
14815
15544
  varName,
14816
15545
  evidenceFile,
@@ -14823,15 +15552,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14823
15552
  if (!svc.service) continue;
14824
15553
  const target = workerIndex.get(svc.service);
14825
15554
  if (target && target.anchorId !== anchorId) {
14826
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
15555
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types51.EdgeType.CALLS);
14827
15556
  if (!graph.hasEdge(edgeId)) {
14828
15557
  const edge = {
14829
15558
  id: edgeId,
14830
15559
  source: anchorId,
14831
15560
  target: target.anchorId,
14832
- type: import_types50.EdgeType.CALLS,
14833
- provenance: import_types50.Provenance.EXTRACTED,
14834
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15561
+ type: import_types51.EdgeType.CALLS,
15562
+ provenance: import_types51.Provenance.EXTRACTED,
15563
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14835
15564
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14836
15565
  };
14837
15566
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14842,7 +15571,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14842
15571
  const result = addResourceEdge(
14843
15572
  graph,
14844
15573
  anchorId,
14845
- import_types50.EdgeType.DEPENDS_ON,
15574
+ import_types51.EdgeType.DEPENDS_ON,
14846
15575
  "cloudflare-service-binding",
14847
15576
  svc.service,
14848
15577
  evidenceFile,
@@ -14857,24 +15586,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14857
15586
 
14858
15587
  // src/extract/infra/vercel.ts
14859
15588
  init_cjs_shims();
14860
- var import_node_fs30 = require("fs");
14861
- var import_node_path62 = __toESM(require("path"), 1);
14862
- var import_types51 = require("@neat.is/types");
15589
+ var import_node_fs31 = require("fs");
15590
+ var import_node_path64 = __toESM(require("path"), 1);
15591
+ var import_types52 = require("@neat.is/types");
14863
15592
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14864
15593
  async function readVercelConfig(dir) {
14865
15594
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14866
- const abs = import_node_path62.default.join(dir, filename);
15595
+ const abs = import_node_path64.default.join(dir, filename);
14867
15596
  if (!await exists2(abs)) continue;
14868
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15597
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14869
15598
  const config = JSON.parse(maskCommentsInSource(raw));
14870
15599
  return { config, relFile: filename, raw };
14871
15600
  }
14872
15601
  return null;
14873
15602
  }
14874
15603
  async function readLinkedProjectName(dir) {
14875
- const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
15604
+ const abs = import_node_path64.default.join(dir, ".vercel", "project.json");
14876
15605
  if (!await exists2(abs)) return void 0;
14877
- const parsed = JSON.parse(await import_node_fs30.promises.readFile(abs, "utf8"));
15606
+ const parsed = JSON.parse(await import_node_fs31.promises.readFile(abs, "utf8"));
14878
15607
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14879
15608
  }
14880
15609
  function routeSource(route) {
@@ -14890,7 +15619,7 @@ async function addVercelServices(graph, services, scanPath) {
14890
15619
  read = await readVercelConfig(service.dir);
14891
15620
  projectName = await readLinkedProjectName(service.dir);
14892
15621
  } catch (err) {
14893
- recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
15622
+ recordExtractionError("infra vercel", import_node_path64.default.relative(scanPath, service.dir), err);
14894
15623
  continue;
14895
15624
  }
14896
15625
  if (!read && !projectName) continue;
@@ -14906,7 +15635,7 @@ async function addVercelServices(graph, services, scanPath) {
14906
15635
  const anchorId = service.node.id;
14907
15636
  if (!read) continue;
14908
15637
  const { config, relFile, raw } = read;
14909
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15638
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14910
15639
  const add = (edgeType, kind, name) => {
14911
15640
  if (!name) return;
14912
15641
  const result = emitPlatformResourceEdge(
@@ -14922,12 +15651,12 @@ async function addVercelServices(graph, services, scanPath) {
14922
15651
  nodesAdded += result.nodesAdded;
14923
15652
  edgesAdded += result.edgesAdded;
14924
15653
  };
14925
- add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
14926
- for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14927
- for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14928
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15654
+ add(import_types52.EdgeType.RUNS_ON, "vercel", "vercel");
15655
+ for (const cron of config.crons ?? []) add(import_types52.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15656
+ for (const varName of Object.keys(config.env ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15657
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14929
15658
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14930
- add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15659
+ add(import_types52.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14931
15660
  }
14932
15661
  }
14933
15662
  return { nodesAdded, edgesAdded };
@@ -14935,16 +15664,16 @@ async function addVercelServices(graph, services, scanPath) {
14935
15664
 
14936
15665
  // src/extract/infra/railway.ts
14937
15666
  init_cjs_shims();
14938
- var import_node_fs31 = require("fs");
14939
- var import_node_path63 = __toESM(require("path"), 1);
15667
+ var import_node_fs32 = require("fs");
15668
+ var import_node_path65 = __toESM(require("path"), 1);
14940
15669
  var import_smol_toml4 = require("smol-toml");
14941
- var import_types52 = require("@neat.is/types");
15670
+ var import_types53 = require("@neat.is/types");
14942
15671
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14943
15672
  async function readRailwayConfig(dir) {
14944
15673
  for (const filename of RAILWAY_FILENAMES) {
14945
- const abs = import_node_path63.default.join(dir, filename);
15674
+ const abs = import_node_path65.default.join(dir, filename);
14946
15675
  if (!await exists2(abs)) continue;
14947
- const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15676
+ const raw = await import_node_fs32.promises.readFile(abs, "utf8");
14948
15677
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14949
15678
  return { config, relFile: filename, raw };
14950
15679
  }
@@ -14958,7 +15687,7 @@ async function addRailwayServices(graph, services, scanPath) {
14958
15687
  try {
14959
15688
  read = await readRailwayConfig(service.dir);
14960
15689
  } catch (err) {
14961
- recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
15690
+ recordExtractionError("infra railway", import_node_path65.default.relative(scanPath, service.dir), err);
14962
15691
  continue;
14963
15692
  }
14964
15693
  if (!read) continue;
@@ -14968,7 +15697,7 @@ async function addRailwayServices(graph, services, scanPath) {
14968
15697
  }
14969
15698
  const anchorId = service.node.id;
14970
15699
  const { config, relFile, raw } = read;
14971
- const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15700
+ const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
14972
15701
  const add = (edgeType, kind, name) => {
14973
15702
  if (!name) return;
14974
15703
  const result = emitPlatformResourceEdge(
@@ -14984,24 +15713,24 @@ async function addRailwayServices(graph, services, scanPath) {
14984
15713
  nodesAdded += result.nodesAdded;
14985
15714
  edgesAdded += result.edgesAdded;
14986
15715
  };
14987
- add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
14988
- add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14989
- add(import_types52.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
15716
+ add(import_types53.EdgeType.RUNS_ON, "railway", "railway");
15717
+ add(import_types53.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15718
+ add(import_types53.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14990
15719
  }
14991
15720
  return { nodesAdded, edgesAdded };
14992
15721
  }
14993
15722
 
14994
15723
  // src/extract/infra/supabase.ts
14995
15724
  init_cjs_shims();
14996
- var import_node_fs32 = require("fs");
14997
- var import_node_path64 = __toESM(require("path"), 1);
15725
+ var import_node_fs33 = require("fs");
15726
+ var import_node_path66 = __toESM(require("path"), 1);
14998
15727
  var import_smol_toml5 = require("smol-toml");
14999
- var import_types53 = require("@neat.is/types");
15728
+ var import_types54 = require("@neat.is/types");
15000
15729
  async function readSupabaseConfig(dir) {
15001
- const relFile = import_node_path64.default.join("supabase", "config.toml");
15002
- const abs = import_node_path64.default.join(dir, relFile);
15730
+ const relFile = import_node_path66.default.join("supabase", "config.toml");
15731
+ const abs = import_node_path66.default.join(dir, relFile);
15003
15732
  if (!await exists2(abs)) return null;
15004
- const raw = await import_node_fs32.promises.readFile(abs, "utf8");
15733
+ const raw = await import_node_fs33.promises.readFile(abs, "utf8");
15005
15734
  const config = (0, import_smol_toml5.parse)(raw);
15006
15735
  return { config, relFile, raw };
15007
15736
  }
@@ -15013,7 +15742,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15013
15742
  try {
15014
15743
  read = await readSupabaseConfig(service.dir);
15015
15744
  } catch (err) {
15016
- recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
15745
+ recordExtractionError("infra supabase", import_node_path66.default.relative(scanPath, service.dir), err);
15017
15746
  continue;
15018
15747
  }
15019
15748
  if (!read) continue;
@@ -15028,7 +15757,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15028
15757
  });
15029
15758
  }
15030
15759
  const anchorId = service.node.id;
15031
- const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15760
+ const evidenceFile = toPosix(import_node_path66.default.relative(scanPath, import_node_path66.default.join(service.dir, relFile)));
15032
15761
  const add = (edgeType, kind, name) => {
15033
15762
  if (!name) return;
15034
15763
  const result = emitPlatformResourceEdge(
@@ -15044,10 +15773,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
15044
15773
  nodesAdded += result.nodesAdded;
15045
15774
  edgesAdded += result.edgesAdded;
15046
15775
  };
15047
- add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
15048
- for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
15049
- if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15050
- if (config.auth) add(import_types53.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15776
+ add(import_types54.EdgeType.RUNS_ON, "supabase", "supabase");
15777
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types54.EdgeType.DEPENDS_ON, "supabase-function", fn);
15778
+ if (config.storage) add(import_types54.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15779
+ if (config.auth) add(import_types54.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15051
15780
  }
15052
15781
  return { nodesAdded, edgesAdded };
15053
15782
  }
@@ -15070,14 +15799,14 @@ async function addInfra(graph, scanPath, services) {
15070
15799
 
15071
15800
  // src/extract/zod-shapes.ts
15072
15801
  init_cjs_shims();
15073
- var import_node_path65 = __toESM(require("path"), 1);
15074
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15802
+ var import_node_path67 = __toESM(require("path"), 1);
15803
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
15075
15804
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15076
- var import_types54 = require("@neat.is/types");
15805
+ var import_types55 = require("@neat.is/types");
15077
15806
  var ZOD_IMPORT_RE = /\bzod\b/;
15078
15807
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
15079
15808
  function parserForExt4(ext) {
15080
- const p = new import_tree_sitter16.default();
15809
+ const p = new import_tree_sitter17.default();
15081
15810
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
15082
15811
  return p;
15083
15812
  }
@@ -15165,7 +15894,7 @@ function topLevelSchemas(root) {
15165
15894
  }
15166
15895
  function zodShapesFromFile(file, serviceDir) {
15167
15896
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
15168
- const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
15897
+ const tree = parseSource3(parserForExt4(import_node_path67.default.extname(file.path)), file.content);
15169
15898
  const out = [];
15170
15899
  const seen = /* @__PURE__ */ new Set();
15171
15900
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -15179,11 +15908,11 @@ function zodShapesFromFile(file, serviceDir) {
15179
15908
  seen.add(name);
15180
15909
  const line = call.startPosition.row + 1;
15181
15910
  out.push({
15182
- infraId: (0, import_types54.infraId)("zod-schema", name),
15911
+ infraId: (0, import_types55.infraId)("zod-schema", name),
15183
15912
  name,
15184
15913
  fields,
15185
15914
  evidence: {
15186
- file: import_node_path65.default.relative(serviceDir, file.path),
15915
+ file: import_node_path67.default.relative(serviceDir, file.path),
15187
15916
  line,
15188
15917
  snippet: snippet(file.content, line)
15189
15918
  }
@@ -15214,7 +15943,7 @@ async function addZodShapes(graph, services) {
15214
15943
  if (!graph.hasNode(shape.infraId)) {
15215
15944
  const node = {
15216
15945
  id: shape.infraId,
15217
- type: import_types54.NodeType.InfraNode,
15946
+ type: import_types55.NodeType.InfraNode,
15218
15947
  name: shape.name,
15219
15948
  provider: "self",
15220
15949
  kind: "zod-schema"
@@ -15224,14 +15953,14 @@ async function addZodShapes(graph, services) {
15224
15953
  }
15225
15954
  if (shape.fields.length > 0) {
15226
15955
  const node = graph.getNodeAttributes(shape.infraId);
15227
- if (node.type === import_types54.NodeType.InfraNode) {
15956
+ if (node.type === import_types55.NodeType.InfraNode) {
15228
15957
  graph.replaceNodeAttributes(shape.infraId, {
15229
15958
  ...node,
15230
15959
  columns: foldColumns(
15231
15960
  node.columns,
15232
15961
  shape.fields,
15233
- import_types54.Provenance.EXTRACTED,
15234
- (0, import_types54.confidenceForExtracted)("structural")
15962
+ import_types55.Provenance.EXTRACTED,
15963
+ (0, import_types55.confidenceForExtracted)("structural")
15235
15964
  )
15236
15965
  });
15237
15966
  }
@@ -15245,15 +15974,15 @@ async function addZodShapes(graph, services) {
15245
15974
  );
15246
15975
  nodesAdded += n;
15247
15976
  edgesAdded += e;
15248
- const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
15977
+ const edgeId = (0, import_types55.extractedEdgeId)(fileNodeId, shape.infraId, import_types55.EdgeType.CONTAINS);
15249
15978
  if (!graph.hasEdge(edgeId)) {
15250
15979
  const edge = {
15251
15980
  id: edgeId,
15252
15981
  source: fileNodeId,
15253
15982
  target: shape.infraId,
15254
- type: import_types54.EdgeType.CONTAINS,
15255
- provenance: import_types54.Provenance.EXTRACTED,
15256
- confidence: (0, import_types54.confidenceForExtracted)("structural"),
15983
+ type: import_types55.EdgeType.CONTAINS,
15984
+ provenance: import_types55.Provenance.EXTRACTED,
15985
+ confidence: (0, import_types55.confidenceForExtracted)("structural"),
15257
15986
  evidence: shape.evidence
15258
15987
  };
15259
15988
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -15267,7 +15996,7 @@ async function addZodShapes(graph, services) {
15267
15996
 
15268
15997
  // src/extract/firestore-rules.ts
15269
15998
  init_cjs_shims();
15270
- var import_types55 = require("@neat.is/types");
15999
+ var import_types56 = require("@neat.is/types");
15271
16000
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
15272
16001
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
15273
16002
  function stripComments(src) {
@@ -15407,7 +16136,7 @@ async function addFirestoreRules(graph, services) {
15407
16136
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
15408
16137
  graph.forEachNode((id, attrs) => {
15409
16138
  const node = attrs;
15410
- if (node.type !== import_types55.NodeType.InfraNode) return;
16139
+ if (node.type !== import_types56.NodeType.InfraNode) return;
15411
16140
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
15412
16141
  const fields = guards.get(collectionKeyFromName(node.name));
15413
16142
  if (!fields || fields.size === 0) return;
@@ -15420,17 +16149,17 @@ async function addFirestoreRules(graph, services) {
15420
16149
  }
15421
16150
 
15422
16151
  // src/extract/index.ts
15423
- var import_node_path67 = __toESM(require("path"), 1);
16152
+ var import_node_path69 = __toESM(require("path"), 1);
15424
16153
 
15425
16154
  // src/extract/retire.ts
15426
16155
  init_cjs_shims();
15427
- var import_node_fs33 = require("fs");
15428
- var import_node_path66 = __toESM(require("path"), 1);
15429
- var import_types56 = require("@neat.is/types");
16156
+ var import_node_fs34 = require("fs");
16157
+ var import_node_path68 = __toESM(require("path"), 1);
16158
+ var import_types57 = require("@neat.is/types");
15430
16159
  function dropOrphanedFileNodes(graph) {
15431
16160
  const orphans = [];
15432
16161
  graph.forEachNode((id, attrs) => {
15433
- if (attrs.type !== import_types56.NodeType.FileNode) return;
16162
+ if (attrs.type !== import_types57.NodeType.FileNode) return;
15434
16163
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
15435
16164
  orphans.push(id);
15436
16165
  }
@@ -15443,14 +16172,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
15443
16172
  const bases = [scanPath, ...serviceDirs];
15444
16173
  graph.forEachEdge((id, attrs) => {
15445
16174
  const edge = attrs;
15446
- if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
16175
+ if (edge.provenance !== import_types57.Provenance.EXTRACTED) return;
15447
16176
  const evidenceFile = edge.evidence?.file;
15448
16177
  if (!evidenceFile) return;
15449
- if (import_node_path66.default.isAbsolute(evidenceFile)) {
15450
- if (!(0, import_node_fs33.existsSync)(evidenceFile)) toDrop.push(id);
16178
+ if (import_node_path68.default.isAbsolute(evidenceFile)) {
16179
+ if (!(0, import_node_fs34.existsSync)(evidenceFile)) toDrop.push(id);
15451
16180
  return;
15452
16181
  }
15453
- const found = bases.some((base) => (0, import_node_fs33.existsSync)(import_node_path66.default.join(base, evidenceFile)));
16182
+ const found = bases.some((base) => (0, import_node_fs34.existsSync)(import_node_path68.default.join(base, evidenceFile)));
15454
16183
  if (!found) toDrop.push(id);
15455
16184
  });
15456
16185
  for (const id of toDrop) graph.dropEdge(id);
@@ -15507,7 +16236,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
15507
16236
  }
15508
16237
  const droppedEntries = drainDroppedExtracted();
15509
16238
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
15510
- const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
16239
+ const rejectedPath = import_node_path69.default.join(import_node_path69.default.dirname(opts.errorsPath), "rejected.ndjson");
15511
16240
  try {
15512
16241
  await writeRejectedExtracted(droppedEntries, rejectedPath);
15513
16242
  } catch (err) {
@@ -15541,7 +16270,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
15541
16270
 
15542
16271
  // src/diff.ts
15543
16272
  init_cjs_shims();
15544
- var import_node_fs34 = require("fs");
16273
+ var import_node_fs35 = require("fs");
15545
16274
  async function loadSnapshotForDiff(target) {
15546
16275
  if (/^https?:\/\//i.test(target)) {
15547
16276
  const res = await fetch(target);
@@ -15550,7 +16279,7 @@ async function loadSnapshotForDiff(target) {
15550
16279
  }
15551
16280
  return await res.json();
15552
16281
  }
15553
- const raw = await import_node_fs34.promises.readFile(target, "utf8");
16282
+ const raw = await import_node_fs35.promises.readFile(target, "utf8");
15554
16283
  return JSON.parse(raw);
15555
16284
  }
15556
16285
  function indexEntries(entries) {
@@ -15618,9 +16347,9 @@ function canonicalJson(value) {
15618
16347
 
15619
16348
  // src/persist.ts
15620
16349
  init_cjs_shims();
15621
- var import_node_fs35 = require("fs");
15622
- var import_node_path68 = __toESM(require("path"), 1);
15623
- var import_types57 = require("@neat.is/types");
16350
+ var import_node_fs36 = require("fs");
16351
+ var import_node_path70 = __toESM(require("path"), 1);
16352
+ var import_types58 = require("@neat.is/types");
15624
16353
  var SCHEMA_VERSION = 7;
15625
16354
  function migrateV1ToV2(payload) {
15626
16355
  const nodes = payload.graph.nodes;
@@ -15644,7 +16373,7 @@ function migrateV5ToV6(payload) {
15644
16373
  if (Array.isArray(nodes)) {
15645
16374
  for (const node of nodes) {
15646
16375
  const attrs = node.attributes;
15647
- if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
16376
+ if (!attrs || attrs.type !== import_types58.NodeType.InfraNode) continue;
15648
16377
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
15649
16378
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
15650
16379
  }
@@ -15660,12 +16389,12 @@ function migrateV2ToV3(payload) {
15660
16389
  for (const edge of edges) {
15661
16390
  const attrs = edge.attributes;
15662
16391
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
15663
- attrs.provenance = import_types57.Provenance.OBSERVED;
16392
+ attrs.provenance = import_types58.Provenance.OBSERVED;
15664
16393
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
15665
16394
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
15666
16395
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
15667
16396
  if (type && source && target) {
15668
- const newId = (0, import_types57.observedEdgeId)(source, target, type);
16397
+ const newId = (0, import_types58.observedEdgeId)(source, target, type);
15669
16398
  attrs.id = newId;
15670
16399
  if (edge.key) edge.key = newId;
15671
16400
  }
@@ -15674,7 +16403,7 @@ function migrateV2ToV3(payload) {
15674
16403
  return { ...payload, schemaVersion: 3 };
15675
16404
  }
15676
16405
  async function ensureDir(filePath) {
15677
- await import_node_fs35.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
16406
+ await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(filePath), { recursive: true });
15678
16407
  }
15679
16408
  async function saveGraphToDisk(graph, outPath) {
15680
16409
  await ensureDir(outPath);
@@ -15684,13 +16413,13 @@ async function saveGraphToDisk(graph, outPath) {
15684
16413
  graph: graph.export()
15685
16414
  };
15686
16415
  const tmp = `${outPath}.tmp`;
15687
- await import_node_fs35.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15688
- await import_node_fs35.promises.rename(tmp, outPath);
16416
+ await import_node_fs36.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
16417
+ await import_node_fs36.promises.rename(tmp, outPath);
15689
16418
  }
15690
16419
  async function loadGraphFromDisk(graph, outPath) {
15691
16420
  let raw;
15692
16421
  try {
15693
- raw = await import_node_fs35.promises.readFile(outPath, "utf8");
16422
+ raw = await import_node_fs36.promises.readFile(outPath, "utf8");
15694
16423
  } catch (err) {
15695
16424
  if (err.code === "ENOENT") return;
15696
16425
  throw err;
@@ -15764,23 +16493,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
15764
16493
 
15765
16494
  // src/projects.ts
15766
16495
  init_cjs_shims();
15767
- var import_node_path69 = __toESM(require("path"), 1);
16496
+ var import_node_path71 = __toESM(require("path"), 1);
15768
16497
  function pathsForProject(project, baseDir) {
15769
16498
  if (project === DEFAULT_PROJECT) {
15770
16499
  return {
15771
- snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
15772
- errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
15773
- staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
15774
- embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
15775
- policyViolationsPath: import_node_path69.default.join(baseDir, "policy-violations.ndjson")
16500
+ snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
16501
+ errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
16502
+ staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
16503
+ embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
16504
+ policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
15776
16505
  };
15777
16506
  }
15778
16507
  return {
15779
- snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
15780
- errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
15781
- staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
15782
- embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
15783
- policyViolationsPath: import_node_path69.default.join(baseDir, `policy-violations.${project}.ndjson`)
16508
+ snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
16509
+ errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
16510
+ staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
16511
+ embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
16512
+ policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
15784
16513
  };
15785
16514
  }
15786
16515
  var Projects = class {
@@ -15820,20 +16549,20 @@ function parseExtraProjects(raw) {
15820
16549
 
15821
16550
  // src/registry.ts
15822
16551
  init_cjs_shims();
15823
- var import_node_fs36 = require("fs");
16552
+ var import_node_fs37 = require("fs");
15824
16553
  var import_node_os3 = __toESM(require("os"), 1);
15825
- var import_node_path70 = __toESM(require("path"), 1);
15826
- var import_types58 = require("@neat.is/types");
16554
+ var import_node_path72 = __toESM(require("path"), 1);
16555
+ var import_types59 = require("@neat.is/types");
15827
16556
  function neatHome() {
15828
16557
  const override = process.env.NEAT_HOME;
15829
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15830
- return import_node_path70.default.join(import_node_os3.default.homedir(), ".neat");
16558
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16559
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15831
16560
  }
15832
16561
  function registryPath() {
15833
- return import_node_path70.default.join(neatHome(), "projects.json");
16562
+ return import_node_path72.default.join(neatHome(), "projects.json");
15834
16563
  }
15835
16564
  function daemonsDir() {
15836
- return import_node_path70.default.join(neatHome(), "daemons");
16565
+ return import_node_path72.default.join(neatHome(), "daemons");
15837
16566
  }
15838
16567
  function isFiniteInt(v) {
15839
16568
  return typeof v === "number" && Number.isFinite(v);
@@ -15866,7 +16595,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15866
16595
  const dir = daemonsDir();
15867
16596
  let names;
15868
16597
  try {
15869
- names = await import_node_fs36.promises.readdir(dir);
16598
+ names = await import_node_fs37.promises.readdir(dir);
15870
16599
  } catch (err) {
15871
16600
  if (err.code === "ENOENT") return [];
15872
16601
  throw err;
@@ -15874,10 +16603,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15874
16603
  const out = [];
15875
16604
  for (const name of names) {
15876
16605
  if (!name.endsWith(".json")) continue;
15877
- const file = import_node_path70.default.join(dir, name);
16606
+ const file = import_node_path72.default.join(dir, name);
15878
16607
  let raw;
15879
16608
  try {
15880
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16609
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15881
16610
  } catch {
15882
16611
  continue;
15883
16612
  }
@@ -15905,7 +16634,7 @@ async function readRegistry() {
15905
16634
  const file = registryPath();
15906
16635
  let raw;
15907
16636
  try {
15908
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16637
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15909
16638
  } catch (err) {
15910
16639
  if (err.code === "ENOENT") {
15911
16640
  return { version: 1, projects: [] };
@@ -15913,7 +16642,7 @@ async function readRegistry() {
15913
16642
  throw err;
15914
16643
  }
15915
16644
  const parsed = JSON.parse(raw);
15916
- return import_types58.RegistryFileSchema.parse(parsed);
16645
+ return import_types59.RegistryFileSchema.parse(parsed);
15917
16646
  }
15918
16647
  async function getProject(name) {
15919
16648
  const reg = await readRegistry();
@@ -15988,8 +16717,8 @@ init_auth();
15988
16717
  // src/connectors-config.ts
15989
16718
  init_cjs_shims();
15990
16719
  var import_node_os4 = __toESM(require("os"), 1);
15991
- var import_node_path71 = __toESM(require("path"), 1);
15992
- var import_node_fs37 = require("fs");
16720
+ var import_node_path73 = __toESM(require("path"), 1);
16721
+ var import_node_fs38 = require("fs");
15993
16722
  var CONNECTORS_CONFIG_VERSION = 1;
15994
16723
  var EnvRefUnsetError = class extends Error {
15995
16724
  ref;
@@ -16003,17 +16732,17 @@ var EnvRefUnsetError = class extends Error {
16003
16732
  };
16004
16733
  function neatHome2() {
16005
16734
  const override = process.env.NEAT_HOME;
16006
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
16007
- return import_node_path71.default.join(import_node_os4.default.homedir(), ".neat");
16735
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
16736
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16008
16737
  }
16009
16738
  function connectorsConfigPath(home = neatHome2()) {
16010
- return import_node_path71.default.join(home, "connectors.json");
16739
+ return import_node_path73.default.join(home, "connectors.json");
16011
16740
  }
16012
16741
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16013
16742
  async function warnIfModeLooserThan0600(file) {
16014
16743
  if (process.platform === "win32") return;
16015
16744
  try {
16016
- const stat = await import_node_fs37.promises.stat(file);
16745
+ const stat = await import_node_fs38.promises.stat(file);
16017
16746
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16018
16747
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16019
16748
  console.warn(
@@ -16027,7 +16756,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16027
16756
  const file = connectorsConfigPath(home);
16028
16757
  let raw;
16029
16758
  try {
16030
- raw = await import_node_fs37.promises.readFile(file, "utf8");
16759
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16031
16760
  } catch (err) {
16032
16761
  if (err.code === "ENOENT") {
16033
16762
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16194,15 +16923,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16194
16923
 
16195
16924
  // src/connectors/index.ts
16196
16925
  init_cjs_shims();
16197
- var import_types59 = require("@neat.is/types");
16926
+ var import_types60 = require("@neat.is/types");
16198
16927
  var NO_ENV = "unknown";
16199
16928
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16200
16929
  if (!graph.hasNode(targetNodeId)) return void 0;
16201
16930
  const sites = [];
16202
16931
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16203
16932
  const edge = graph.getEdgeAttributes(edgeId);
16204
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16205
- const parsed = (0, import_types59.parseFileId)(edge.source);
16933
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
16934
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16206
16935
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16207
16936
  const site = { relPath: edge.evidence.file };
16208
16937
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16213,7 +16942,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16213
16942
  function routeCallSiteFor(graph, targetNodeId) {
16214
16943
  if (!graph.hasNode(targetNodeId)) return void 0;
16215
16944
  const attrs = graph.getNodeAttributes(targetNodeId);
16216
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
16945
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16217
16946
  const site = { relPath: attrs.path };
16218
16947
  if (attrs.line !== void 0) site.line = attrs.line;
16219
16948
  return site;
@@ -16670,10 +17399,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
16670
17399
  // src/connectors/supabase/map.ts
16671
17400
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
16672
17401
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
16673
- function targetFromRestPath(path74) {
16674
- const rpcMatch = REST_RPC_PATH_RE.exec(path74);
17402
+ function targetFromRestPath(path76) {
17403
+ const rpcMatch = REST_RPC_PATH_RE.exec(path76);
16675
17404
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
16676
- const tableMatch = REST_TABLE_PATH_RE.exec(path74);
17405
+ const tableMatch = REST_TABLE_PATH_RE.exec(path76);
16677
17406
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
16678
17407
  return null;
16679
17408
  }
@@ -16784,23 +17513,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
16784
17513
 
16785
17514
  // src/connectors/supabase/resolve.ts
16786
17515
  init_cjs_shims();
16787
- var import_types61 = require("@neat.is/types");
17516
+ var import_types62 = require("@neat.is/types");
16788
17517
  function createSupabaseResolveTarget(graph, config) {
16789
17518
  return (signal, _ctx) => {
16790
17519
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
16791
17520
  return null;
16792
17521
  }
16793
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17522
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
16794
17523
  if (graph.hasNode(subResourceId)) {
16795
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17524
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16796
17525
  }
16797
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17526
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
16798
17527
  if (graph.hasNode(bareResourceId)) {
16799
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17528
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16800
17529
  }
16801
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17530
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
16802
17531
  if (graph.hasNode(projectLevelId)) {
16803
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17532
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16804
17533
  }
16805
17534
  return null;
16806
17535
  };
@@ -16893,7 +17622,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
16893
17622
 
16894
17623
  // src/connectors/railway/index.ts
16895
17624
  init_cjs_shims();
16896
- var import_types65 = require("@neat.is/types");
17625
+ var import_types66 = require("@neat.is/types");
16897
17626
 
16898
17627
  // src/connectors/railway/client.ts
16899
17628
  init_cjs_shims();
@@ -17044,7 +17773,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17044
17773
  const out = [];
17045
17774
  graph.forEachNode((_id, attrs) => {
17046
17775
  const node = attrs;
17047
- if (node.type !== import_types65.NodeType.RouteNode) return;
17776
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17048
17777
  const route = attrs;
17049
17778
  if (route.service !== serviceName) return;
17050
17779
  out.push({
@@ -17148,12 +17877,12 @@ function createRailwayResolveTarget(config) {
17148
17877
  const serviceName = config.serviceNameById[config.serviceId];
17149
17878
  if (!serviceName) return null;
17150
17879
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17151
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
17880
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17152
17881
  }
17153
17882
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17154
17883
  const peerName = config.serviceNameById[signal.targetName];
17155
17884
  if (!peerName) return null;
17156
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
17885
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17157
17886
  }
17158
17887
  return null;
17159
17888
  };
@@ -17277,9 +18006,9 @@ function parseFirebaseTargetName(targetName) {
17277
18006
  const secondSep = rest.indexOf(FIELD_SEP);
17278
18007
  if (secondSep === -1) return null;
17279
18008
  const method = rest.slice(0, secondSep);
17280
- const path74 = rest.slice(secondSep + 1);
17281
- if (!resourceName || !method || !path74) return null;
17282
- return { resourceName, method, path: path74 };
18009
+ const path76 = rest.slice(secondSep + 1);
18010
+ if (!resourceName || !method || !path76) return null;
18011
+ return { resourceName, method, path: path76 };
17283
18012
  }
17284
18013
  function resourceNameFor(type, labels) {
17285
18014
  if (!labels) return null;
@@ -17317,14 +18046,14 @@ function mapLogEntryToSignal(entry) {
17317
18046
  if (!req) return null;
17318
18047
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17319
18048
  const method = req.requestMethod.toUpperCase();
17320
- const path74 = pathFromRequestUrl(req.requestUrl);
17321
- if (path74 === null) return null;
18049
+ const path76 = pathFromRequestUrl(req.requestUrl);
18050
+ if (path76 === null) return null;
17322
18051
  const timestamp = entry.timestamp;
17323
18052
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17324
18053
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
17325
18054
  return {
17326
18055
  targetKind: resourceType,
17327
- targetName: packFirebaseTargetName({ resourceName, method, path: path74 }),
18056
+ targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
17328
18057
  callCount: 1,
17329
18058
  errorCount: isError ? 1 : 0,
17330
18059
  lastObservedIso: timestamp
@@ -17341,7 +18070,7 @@ function mapLogEntriesToSignals(entries) {
17341
18070
 
17342
18071
  // src/connectors/firebase/resolve.ts
17343
18072
  init_cjs_shims();
17344
- var import_types66 = require("@neat.is/types");
18073
+ var import_types67 = require("@neat.is/types");
17345
18074
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17346
18075
  switch (resourceType) {
17347
18076
  case "cloud_function":
@@ -17356,7 +18085,7 @@ function routeEntriesFor(graph, serviceName) {
17356
18085
  const entries = [];
17357
18086
  graph.forEachNode((_id, attrs) => {
17358
18087
  const node = attrs;
17359
- if (node.type !== import_types66.NodeType.RouteNode) return;
18088
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17360
18089
  const route = attrs;
17361
18090
  if (route.service !== serviceName) return;
17362
18091
  entries.push({
@@ -17388,7 +18117,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17388
18117
  return {
17389
18118
  targetNodeId: match.routeNodeId,
17390
18119
  serviceName,
17391
- edgeType: import_types66.EdgeType.CALLS
18120
+ edgeType: import_types67.EdgeType.CALLS
17392
18121
  };
17393
18122
  };
17394
18123
  }
@@ -17415,7 +18144,7 @@ init_cjs_shims();
17415
18144
 
17416
18145
  // src/connectors/cloudflare/connector.ts
17417
18146
  init_cjs_shims();
17418
- var import_types68 = require("@neat.is/types");
18147
+ var import_types69 = require("@neat.is/types");
17419
18148
 
17420
18149
  // src/connectors/cloudflare/client.ts
17421
18150
  init_cjs_shims();
@@ -17531,7 +18260,7 @@ function mapEventToSignal(event) {
17531
18260
  if (Number.isNaN(observedAt.getTime())) return null;
17532
18261
  const statusCode = metadata?.statusCode;
17533
18262
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17534
- const path74 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18263
+ const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17535
18264
  return {
17536
18265
  targetKind: CLOUDFLARE_TARGET_KIND,
17537
18266
  targetName: scriptName,
@@ -17539,7 +18268,7 @@ function mapEventToSignal(event) {
17539
18268
  errorCount: isError ? 1 : 0,
17540
18269
  lastObservedIso: observedAt.toISOString(),
17541
18270
  method,
17542
- ...path74 ? { path: path74 } : {},
18271
+ ...path76 ? { path: path76 } : {},
17543
18272
  ...typeof statusCode === "number" ? { statusCode } : {},
17544
18273
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17545
18274
  };
@@ -17579,19 +18308,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17579
18308
  graph.forEachNode((id, attrs) => {
17580
18309
  if (found) return;
17581
18310
  const a = attrs;
17582
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18311
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
17583
18312
  found = id;
17584
18313
  }
17585
18314
  });
17586
18315
  return found;
17587
18316
  }
17588
- function findMatchingRouteNode(graph, serviceName, method, path74) {
17589
- const normalizedPath = normalizePathTemplate(path74);
18317
+ function findMatchingRouteNode(graph, serviceName, method, path76) {
18318
+ const normalizedPath = normalizePathTemplate(path76);
17590
18319
  let found = null;
17591
18320
  graph.forEachNode((id, attrs) => {
17592
18321
  if (found) return;
17593
18322
  const a = attrs;
17594
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18323
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17595
18324
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17596
18325
  const routeMethod = (a.method ?? "").toUpperCase();
17597
18326
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17603,18 +18332,18 @@ function createCloudflareResolveTarget(config, graph) {
17603
18332
  return (signal) => {
17604
18333
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17605
18334
  const scriptName = signal.targetName;
17606
- const { method, path: path74 } = signal;
18335
+ const { method, path: path76 } = signal;
17607
18336
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17608
- if (!method || !path74) return wholeFileId;
17609
- return findMatchingRouteNode(graph, serviceName, method, path74) ?? wholeFileId;
18337
+ if (!method || !path76) return wholeFileId;
18338
+ return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
17610
18339
  };
17611
18340
  const mapping = config.workers?.[scriptName];
17612
18341
  if (mapping) {
17613
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18342
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17614
18343
  return {
17615
18344
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17616
18345
  serviceName: mapping.service,
17617
- edgeType: import_types68.EdgeType.CALLS
18346
+ edgeType: import_types69.EdgeType.CALLS
17618
18347
  };
17619
18348
  }
17620
18349
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17623,13 +18352,13 @@ function createCloudflareResolveTarget(config, graph) {
17623
18352
  return {
17624
18353
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17625
18354
  serviceName: fileNode.service,
17626
- edgeType: import_types68.EdgeType.CALLS
18355
+ edgeType: import_types69.EdgeType.CALLS
17627
18356
  };
17628
18357
  }
17629
18358
  return {
17630
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18359
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17631
18360
  serviceName: scriptName,
17632
- edgeType: import_types68.EdgeType.CALLS,
18361
+ edgeType: import_types69.EdgeType.CALLS,
17633
18362
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
17634
18363
  };
17635
18364
  };
@@ -17825,14 +18554,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
17825
18554
 
17826
18555
  // src/connectors/neon/resolve.ts
17827
18556
  init_cjs_shims();
17828
- var import_types72 = require("@neat.is/types");
18557
+ var import_types73 = require("@neat.is/types");
17829
18558
  function createNeonResolveTarget(config) {
17830
18559
  return (signal) => {
17831
18560
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
17832
18561
  return {
17833
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18562
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
17834
18563
  serviceName: config.serviceName,
17835
- edgeType: import_types72.EdgeType.CALLS,
18564
+ edgeType: import_types73.EdgeType.CALLS,
17836
18565
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
17837
18566
  };
17838
18567
  };
@@ -17958,9 +18687,9 @@ function parseCloudRunTargetName(targetName) {
17958
18687
  const secondSep = rest.indexOf(FIELD_SEP2);
17959
18688
  if (secondSep === -1) return null;
17960
18689
  const method = rest.slice(0, secondSep);
17961
- const path74 = rest.slice(secondSep + 1);
17962
- if (!serviceName || !method || !path74) return null;
17963
- return { serviceName, method, path: path74 };
18690
+ const path76 = rest.slice(secondSep + 1);
18691
+ if (!serviceName || !method || !path76) return null;
18692
+ return { serviceName, method, path: path76 };
17964
18693
  }
17965
18694
 
17966
18695
  // src/connectors/cloud-run/map.ts
@@ -17989,14 +18718,14 @@ function mapLogEntryToSignal2(entry) {
17989
18718
  if (!req) return null;
17990
18719
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17991
18720
  const method = req.requestMethod.toUpperCase();
17992
- const path74 = pathFromRequestUrl2(req.requestUrl);
17993
- if (path74 === null) return null;
18721
+ const path76 = pathFromRequestUrl2(req.requestUrl);
18722
+ if (path76 === null) return null;
17994
18723
  const timestamp = entry.timestamp;
17995
18724
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17996
18725
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
17997
18726
  return {
17998
18727
  targetKind: CLOUD_RUN_TARGET_KIND,
17999
- targetName: packCloudRunTargetName({ serviceName, method, path: path74 }),
18728
+ targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
18000
18729
  callCount: 1,
18001
18730
  errorCount: isError ? 1 : 0,
18002
18731
  lastObservedIso: timestamp
@@ -18013,14 +18742,14 @@ function mapLogEntriesToSignals2(entries) {
18013
18742
 
18014
18743
  // src/connectors/cloud-run/resolve.ts
18015
18744
  init_cjs_shims();
18016
- var import_types76 = require("@neat.is/types");
18745
+ var import_types77 = require("@neat.is/types");
18017
18746
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18018
18747
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18019
18748
  let found = null;
18020
18749
  graph.forEachNode((_id, attrs) => {
18021
18750
  if (found) return;
18022
18751
  const node = attrs;
18023
- if (node.type !== import_types76.NodeType.RouteNode) return;
18752
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18024
18753
  const route = attrs;
18025
18754
  if (route.service !== serviceName || !route.pathTemplate) return;
18026
18755
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18035,23 +18764,23 @@ function createCloudRunResolveTarget(graph, config) {
18035
18764
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18036
18765
  const identity = parseCloudRunTargetName(signal.targetName);
18037
18766
  if (!identity) return null;
18038
- const { serviceName: gcpServiceName, method, path: path74 } = identity;
18767
+ const { serviceName: gcpServiceName, method, path: path76 } = identity;
18039
18768
  const mappedService = config.serviceMap?.[gcpServiceName];
18040
18769
  if (mappedService) {
18041
18770
  const routeNodeId = findMatchingRouteNode2(
18042
18771
  graph,
18043
18772
  mappedService,
18044
18773
  method,
18045
- normalizePathTemplate(path74)
18774
+ normalizePathTemplate(path76)
18046
18775
  );
18047
18776
  if (routeNodeId) {
18048
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
18777
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18049
18778
  }
18050
18779
  }
18051
18780
  return {
18052
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18781
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18053
18782
  serviceName: mappedService ?? gcpServiceName,
18054
- edgeType: import_types76.EdgeType.CALLS,
18783
+ edgeType: import_types77.EdgeType.CALLS,
18055
18784
  ensureInfraNode: {
18056
18785
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18057
18786
  name: gcpServiceName,
@@ -18092,7 +18821,7 @@ function createCloudRunConnector(graph, config = {}) {
18092
18821
 
18093
18822
  // src/connectors/render/index.ts
18094
18823
  init_cjs_shims();
18095
- var import_types79 = require("@neat.is/types");
18824
+ var import_types80 = require("@neat.is/types");
18096
18825
 
18097
18826
  // src/connectors/render/types.ts
18098
18827
  init_cjs_shims();
@@ -18170,7 +18899,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18170
18899
  const out = [];
18171
18900
  graph.forEachNode((_id, attrs) => {
18172
18901
  const node = attrs;
18173
- if (node.type !== import_types79.NodeType.RouteNode) return;
18902
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18174
18903
  const route = attrs;
18175
18904
  if (route.service !== serviceName) return;
18176
18905
  out.push({
@@ -18255,7 +18984,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18255
18984
  function createRenderResolveTarget(config) {
18256
18985
  return (signal) => {
18257
18986
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18258
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
18987
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18259
18988
  }
18260
18989
  return null;
18261
18990
  };
@@ -18393,21 +19122,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18393
19122
 
18394
19123
  // src/connectors/planetscale/resolve.ts
18395
19124
  init_cjs_shims();
18396
- var import_types83 = require("@neat.is/types");
19125
+ var import_types84 = require("@neat.is/types");
18397
19126
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18398
19127
  function createPlanetscaleResolveTarget(graph, config) {
18399
19128
  const databaseName = `${config.organization}/${config.database}`;
18400
19129
  return (signal, _ctx) => {
18401
19130
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18402
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19131
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18403
19132
  if (graph.hasNode(tableId)) {
18404
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19133
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18405
19134
  }
18406
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19135
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18407
19136
  return {
18408
19137
  targetNodeId: providerId,
18409
19138
  serviceName: config.serviceName,
18410
- edgeType: import_types83.EdgeType.CALLS,
19139
+ edgeType: import_types84.EdgeType.CALLS,
18411
19140
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18412
19141
  };
18413
19142
  };
@@ -18672,7 +19401,7 @@ function mapBuildsToSignals(builds, serviceName) {
18672
19401
 
18673
19402
  // src/connectors/eas/resolve.ts
18674
19403
  init_cjs_shims();
18675
- var import_types88 = require("@neat.is/types");
19404
+ var import_types89 = require("@neat.is/types");
18676
19405
  var NO_ENV2 = "unknown";
18677
19406
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
18678
19407
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -18688,8 +19417,8 @@ function configBasenamesForPhase(phase) {
18688
19417
  function configNodeService(graph, configNodeId) {
18689
19418
  for (const edgeId of graph.inboundEdges(configNodeId)) {
18690
19419
  const edge = graph.getEdgeAttributes(edgeId);
18691
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
18692
- const parsed = (0, import_types88.parseFileId)(edge.source);
19420
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19421
+ const parsed = (0, import_types89.parseFileId)(edge.source);
18693
19422
  if (parsed) return parsed.service;
18694
19423
  }
18695
19424
  return null;
@@ -18700,7 +19429,7 @@ function findConfigNode(graph, basenames, serviceName) {
18700
19429
  graph.forEachNode((id, attrs) => {
18701
19430
  if (scoped) return;
18702
19431
  const node = attrs;
18703
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19432
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
18704
19433
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
18705
19434
  if (anyMatch === null) anyMatch = id;
18706
19435
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -18717,13 +19446,13 @@ function createEasResolveTarget(graph) {
18717
19446
  if (basenames.length > 0) {
18718
19447
  const configNodeId = findConfigNode(graph, basenames, serviceName);
18719
19448
  if (configNodeId) {
18720
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19449
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
18721
19450
  }
18722
19451
  }
18723
19452
  return {
18724
19453
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
18725
19454
  serviceName,
18726
- edgeType: import_types88.EdgeType.CALLS
19455
+ edgeType: import_types89.EdgeType.CALLS
18727
19456
  };
18728
19457
  };
18729
19458
  }
@@ -19402,11 +20131,11 @@ function registerRoutes(scope, ctx) {
19402
20131
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19403
20132
  const parsed = [];
19404
20133
  for (const c of candidates) {
19405
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20134
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19406
20135
  if (!r.success) {
19407
20136
  return reply.code(400).send({
19408
20137
  error: `unknown divergence type "${c}"`,
19409
- allowed: import_types91.DivergenceTypeSchema.options
20138
+ allowed: import_types92.DivergenceTypeSchema.options
19410
20139
  });
19411
20140
  }
19412
20141
  parsed.push(r.data);
@@ -19748,7 +20477,7 @@ function registerRoutes(scope, ctx) {
19748
20477
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
19749
20478
  let violations = await log.readAll();
19750
20479
  if (req.query.severity) {
19751
- const sev = import_types91.PolicySeveritySchema.safeParse(req.query.severity);
20480
+ const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
19752
20481
  if (!sev.success) {
19753
20482
  return reply.code(400).send({
19754
20483
  error: "invalid severity",
@@ -19787,7 +20516,7 @@ function registerRoutes(scope, ctx) {
19787
20516
  scope.post("/policies/check", async (req, reply) => {
19788
20517
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
19789
20518
  if (!proj) return;
19790
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20519
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
19791
20520
  if (!parsed.success) {
19792
20521
  return reply.code(400).send({
19793
20522
  error: "invalid /policies/check body",
@@ -20108,8 +20837,8 @@ init_otel_grpc();
20108
20837
 
20109
20838
  // src/search.ts
20110
20839
  init_cjs_shims();
20111
- var import_node_fs38 = require("fs");
20112
- var import_node_path72 = __toESM(require("path"), 1);
20840
+ var import_node_fs39 = require("fs");
20841
+ var import_node_path74 = __toESM(require("path"), 1);
20113
20842
  var import_node_crypto4 = require("crypto");
20114
20843
  var DEFAULT_LIMIT = 10;
20115
20844
  var NOMIC_DIM = 768;
@@ -20263,7 +20992,7 @@ async function pickEmbedder() {
20263
20992
  }
20264
20993
  async function readCache(cachePath) {
20265
20994
  try {
20266
- const raw = await import_node_fs38.promises.readFile(cachePath, "utf8");
20995
+ const raw = await import_node_fs39.promises.readFile(cachePath, "utf8");
20267
20996
  const parsed = JSON.parse(raw);
20268
20997
  if (parsed.version !== 1) return null;
20269
20998
  return parsed;
@@ -20272,8 +21001,8 @@ async function readCache(cachePath) {
20272
21001
  }
20273
21002
  }
20274
21003
  async function writeCache(cachePath, cache) {
20275
- await import_node_fs38.promises.mkdir(import_node_path72.default.dirname(cachePath), { recursive: true });
20276
- await import_node_fs38.promises.writeFile(cachePath, JSON.stringify(cache));
21004
+ await import_node_fs39.promises.mkdir(import_node_path74.default.dirname(cachePath), { recursive: true });
21005
+ await import_node_fs39.promises.writeFile(cachePath, JSON.stringify(cache));
20277
21006
  }
20278
21007
  var VectorIndex = class {
20279
21008
  constructor(embedder, cachePath) {
@@ -20487,14 +21216,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
20487
21216
  async function main() {
20488
21217
  const baseDirEnv = process.env.NEAT_OUT_DIR;
20489
21218
  const legacyOutPath = process.env.NEAT_OUT_PATH;
20490
- const baseDir = baseDirEnv ? import_node_path73.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path73.default.resolve(import_node_path73.default.dirname(legacyOutPath)) : import_node_path73.default.resolve("./neat-out");
20491
- const defaultScanPath = import_node_path73.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
21219
+ const baseDir = baseDirEnv ? import_node_path75.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path75.default.resolve(import_node_path75.default.dirname(legacyOutPath)) : import_node_path75.default.resolve("./neat-out");
21220
+ const defaultScanPath = import_node_path75.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
20492
21221
  const registry = new Projects();
20493
21222
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
20494
21223
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
20495
21224
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
20496
21225
  const projectScan = process.env[envKey];
20497
- await bootProject(registry, name, projectScan ? import_node_path73.default.resolve(projectScan) : void 0, baseDir);
21226
+ await bootProject(registry, name, projectScan ? import_node_path75.default.resolve(projectScan) : void 0, baseDir);
20498
21227
  }
20499
21228
  const host = process.env.HOST ?? "0.0.0.0";
20500
21229
  const port = Number(process.env.PORT ?? 8080);