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

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 }
@@ -710,12 +710,12 @@ async function buildOtelReceiver(opts) {
710
710
  };
711
711
  return decorated;
712
712
  }
713
- var import_node_path54, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
713
+ var import_node_path55, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
714
714
  var init_otel = __esm({
715
715
  "src/otel.ts"() {
716
716
  "use strict";
717
717
  init_cjs_shims();
718
- import_node_path54 = __toESM(require("path"), 1);
718
+ import_node_path55 = __toESM(require("path"), 1);
719
719
  import_node_url2 = require("url");
720
720
  import_fastify = __toESM(require("fastify"), 1);
721
721
  import_protobufjs = __toESM(require("protobufjs"), 1);
@@ -731,7 +731,7 @@ var init_otel = __esm({
731
731
 
732
732
  // src/server.ts
733
733
  init_cjs_shims();
734
- var import_node_path73 = __toESM(require("path"), 1);
734
+ var import_node_path75 = __toESM(require("path"), 1);
735
735
 
736
736
  // src/graph.ts
737
737
  init_cjs_shims();
@@ -755,7 +755,7 @@ function getGraph(project = DEFAULT_PROJECT) {
755
755
  init_cjs_shims();
756
756
  var import_fastify2 = __toESM(require("fastify"), 1);
757
757
  var import_cors = __toESM(require("@fastify/cors"), 1);
758
- var import_types91 = require("@neat.is/types");
758
+ var import_types92 = require("@neat.is/types");
759
759
 
760
760
  // src/extend/index.ts
761
761
  init_cjs_shims();
@@ -867,12 +867,12 @@ var HOOK_WALK_SKIP_DIRS = /* @__PURE__ */ new Set([
867
867
  ]);
868
868
  async function findHookFiles(scanPath) {
869
869
  const found = [];
870
- const walk9 = async (dir) => {
870
+ const walk10 = async (dir) => {
871
871
  const entries = await import_node_fs2.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
872
872
  for (const entry of entries) {
873
873
  if (entry.isDirectory()) {
874
874
  if (entry.name.startsWith(".") || HOOK_WALK_SKIP_DIRS.has(entry.name)) continue;
875
- await walk9(import_node_path2.default.join(dir, entry.name));
875
+ await walk10(import_node_path2.default.join(dir, entry.name));
876
876
  } else if (entry.isFile()) {
877
877
  if ((entry.name.startsWith("instrumentation") || entry.name.startsWith("otel-init")) && /\.(ts|js|cjs|mjs)$/.test(entry.name)) {
878
878
  const rel = import_node_path2.default.relative(scanPath, import_node_path2.default.join(dir, entry.name));
@@ -881,7 +881,7 @@ async function findHookFiles(scanPath) {
881
881
  }
882
882
  }
883
883
  };
884
- await walk9(scanPath);
884
+ await walk10(scanPath);
885
885
  return found.sort();
886
886
  }
887
887
  async function pickPrimaryHookFile(scanPath, hookFiles, snippet2) {
@@ -2007,6 +2007,7 @@ var import_tree_sitter_python2 = __toESM(require("tree-sitter-python"), 1);
2007
2007
  var import_tree_sitter_go2 = __toESM(require("tree-sitter-go"), 1);
2008
2008
  var import_tree_sitter_ruby = __toESM(require("tree-sitter-ruby"), 1);
2009
2009
  var import_tree_sitter_php = __toESM(require("tree-sitter-php"), 1);
2010
+ var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
2010
2011
  var import_types6 = require("@neat.is/types");
2011
2012
 
2012
2013
  // src/extract/shared.ts
@@ -2287,7 +2288,7 @@ function buildServiceHostIndex(services) {
2287
2288
  async function walkSourceFiles(dir, excludeDirs = []) {
2288
2289
  const excluded = new Set(excludeDirs.map((d) => import_node_path7.default.resolve(d)));
2289
2290
  const out = [];
2290
- async function walk9(current) {
2291
+ async function walk10(current) {
2291
2292
  const entries = await import_node_fs7.promises.readdir(current, { withFileTypes: true }).catch(() => []);
2292
2293
  for (const entry of entries) {
2293
2294
  const full = import_node_path7.default.join(current, entry.name);
@@ -2295,7 +2296,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2295
2296
  if (IGNORED_DIRS.has(entry.name)) continue;
2296
2297
  if (excluded.has(import_node_path7.default.resolve(full))) continue;
2297
2298
  if (await isPythonVenvDir(full)) continue;
2298
- await walk9(full);
2299
+ await walk10(full);
2299
2300
  } 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
2301
  // would attribute our instrumentation imports to the user's service.
2301
2302
  !isNeatAuthoredSourceFile(entry.name)) {
@@ -2303,7 +2304,7 @@ async function walkSourceFiles(dir, excludeDirs = []) {
2303
2304
  }
2304
2305
  }
2305
2306
  }
2306
- await walk9(dir);
2307
+ await walk10(dir);
2307
2308
  return out;
2308
2309
  }
2309
2310
  async function loadSourceFiles(dir, excludeDirs = []) {
@@ -2823,6 +2824,11 @@ function makePhpParser() {
2823
2824
  p.setLanguage(import_tree_sitter_php.default.php_only);
2824
2825
  return p;
2825
2826
  }
2827
+ function makeRustParser() {
2828
+ const p = new import_tree_sitter2.default();
2829
+ p.setLanguage(import_tree_sitter_rust.default);
2830
+ return p;
2831
+ }
2826
2832
  var ROUTER_METHODS = /* @__PURE__ */ new Set([
2827
2833
  "get",
2828
2834
  "post",
@@ -2894,8 +2900,8 @@ function chiRoutesFromSource(source, parser) {
2894
2900
  chiWalk(tree.rootNode, "", out);
2895
2901
  return out;
2896
2902
  }
2897
- function stripChiRegex(path74) {
2898
- return path74.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2903
+ function stripChiRegex(path76) {
2904
+ return path76.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2899
2905
  }
2900
2906
  function chiWalk(node, prefix, out) {
2901
2907
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -3567,9 +3573,9 @@ function rubyRocketRoute(args) {
3567
3573
  if (!pair || pair.type !== "pair") continue;
3568
3574
  const k = pair.childForFieldName("key");
3569
3575
  if (k?.type !== "string") continue;
3570
- const path74 = rubyLiteral(k);
3571
- if (path74 === null) continue;
3572
- return { path: path74, target: rubyLiteral(pair.childForFieldName("value")) };
3576
+ const path76 = rubyLiteral(k);
3577
+ if (path76 === null) continue;
3578
+ return { path: path76, target: rubyLiteral(pair.childForFieldName("value")) };
3573
3579
  }
3574
3580
  return null;
3575
3581
  }
@@ -3739,6 +3745,48 @@ function railsRoutesFromSource(source, parser) {
3739
3745
  });
3740
3746
  return out;
3741
3747
  }
3748
+ var SINATRA_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
3749
+ function sinatraRoutesFromSource(source, parser) {
3750
+ const tree = parseSource2(parser, source);
3751
+ if (!fileReferencesSinatra(tree.rootNode)) return [];
3752
+ const out = [];
3753
+ walk(tree.rootNode, (node) => {
3754
+ if (node.type !== "call") return;
3755
+ if (node.childForFieldName("receiver")) return;
3756
+ const method = node.childForFieldName("method")?.text;
3757
+ if (!method || !SINATRA_VERBS.has(method)) return;
3758
+ if (!node.childForFieldName("block")) return;
3759
+ const first = node.childForFieldName("arguments")?.namedChild(0);
3760
+ if (first?.type !== "string") return;
3761
+ const p = rubyLiteral(first);
3762
+ if (p === null || !p.startsWith("/")) return;
3763
+ out.push({
3764
+ method: method.toUpperCase(),
3765
+ pathTemplate: canonicalizeTemplate(p),
3766
+ line: node.startPosition.row + 1,
3767
+ framework: "sinatra"
3768
+ });
3769
+ });
3770
+ return out;
3771
+ }
3772
+ function fileReferencesSinatra(root) {
3773
+ let found = false;
3774
+ walk(root, (node) => {
3775
+ if (found) return;
3776
+ if (node.type === "call") {
3777
+ const m = node.childForFieldName("method")?.text;
3778
+ if (m === "require" || m === "require_relative") {
3779
+ const s = rubyLiteral(node.childForFieldName("arguments")?.namedChild(0));
3780
+ if (s !== null && /^sinatra\b/.test(s)) found = true;
3781
+ }
3782
+ return;
3783
+ }
3784
+ if (node.type === "constant" && node.text === "Sinatra" || node.type === "scope_resolution" && node.text.startsWith("Sinatra")) {
3785
+ found = true;
3786
+ }
3787
+ });
3788
+ return found;
3789
+ }
3742
3790
  var LARAVEL_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options"]);
3743
3791
  var LARAVEL_RESOURCE_ROWS = [
3744
3792
  { action: "index", methods: ["GET"], suffix: "" },
@@ -3914,6 +3962,223 @@ function laravelRoutesFromSource(source, parser, basePrefix = "") {
3914
3962
  }
3915
3963
  return out;
3916
3964
  }
3965
+ var SLIM_VERBS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "options", "head"]);
3966
+ function isSlimAppCtor(node) {
3967
+ if (!node) return false;
3968
+ if (node.type === "scoped_call_expression") {
3969
+ const scope = node.childForFieldName("scope")?.text ?? "";
3970
+ const name = node.childForFieldName("name")?.text;
3971
+ return name === "create" && (scope === "AppFactory" || scope.endsWith("\\AppFactory"));
3972
+ }
3973
+ if (node.type === "object_creation_expression") {
3974
+ const cls = node.namedChild(0)?.text ?? "";
3975
+ return cls === "App" || cls.endsWith("\\App") || cls.includes("Slim");
3976
+ }
3977
+ return false;
3978
+ }
3979
+ function isSlimAppType(node) {
3980
+ if (!node || node.type !== "named_type") return false;
3981
+ const text = node.text;
3982
+ return text === "App" || text.endsWith("\\App");
3983
+ }
3984
+ function collectSlimAppVars(root) {
3985
+ const vars = /* @__PURE__ */ new Set();
3986
+ walk(root, (node) => {
3987
+ if (node.type === "assignment_expression") {
3988
+ const left = node.childForFieldName("left");
3989
+ if (left?.type === "variable_name" && isSlimAppCtor(node.childForFieldName("right"))) {
3990
+ vars.add(left.text);
3991
+ }
3992
+ return;
3993
+ }
3994
+ if (node.type === "simple_parameter" && isSlimAppType(node.childForFieldName("type"))) {
3995
+ const name = node.childForFieldName("name");
3996
+ if (name?.type === "variable_name") vars.add(name.text);
3997
+ }
3998
+ });
3999
+ return vars;
4000
+ }
4001
+ function slimClosureParamVars(closure) {
4002
+ const out = ["$this"];
4003
+ for (let i = 0; i < closure.namedChildCount; i++) {
4004
+ const params = closure.namedChild(i);
4005
+ if (params?.type !== "formal_parameters") continue;
4006
+ for (let j = 0; j < params.namedChildCount; j++) {
4007
+ const param = params.namedChild(j);
4008
+ if (param?.type !== "simple_parameter") continue;
4009
+ for (let k = 0; k < param.namedChildCount; k++) {
4010
+ const v = param.namedChild(k);
4011
+ if (v?.type === "variable_name") {
4012
+ out.push(v.text);
4013
+ break;
4014
+ }
4015
+ }
4016
+ }
4017
+ }
4018
+ return out;
4019
+ }
4020
+ function phpStringArray(node) {
4021
+ const out = [];
4022
+ if (node?.type !== "array_creation_expression") return out;
4023
+ for (let i = 0; i < node.namedChildCount; i++) {
4024
+ const el = node.namedChild(i);
4025
+ if (el?.type !== "array_element_initializer") continue;
4026
+ const s = phpStaticString(el.namedChild(0));
4027
+ if (s !== null) out.push(s);
4028
+ }
4029
+ return out;
4030
+ }
4031
+ function slimRoutesFromSource(source, parser) {
4032
+ const tree = parseSource2(parser, source);
4033
+ const appVars = collectSlimAppVars(tree.rootNode);
4034
+ if (appVars.size === 0) return [];
4035
+ const out = [];
4036
+ slimWalk(tree.rootNode, "", appVars, out);
4037
+ return out;
4038
+ }
4039
+ function slimWalk(node, prefix, appVars, out) {
4040
+ for (let i = 0; i < node.namedChildCount; i++) {
4041
+ const child = node.namedChild(i);
4042
+ if (child) slimHandle(child, prefix, appVars, out);
4043
+ }
4044
+ }
4045
+ function slimHandle(node, prefix, appVars, out) {
4046
+ if (node.type === "member_call_expression") {
4047
+ const obj = node.childForFieldName("object");
4048
+ const method = node.childForFieldName("name")?.text;
4049
+ const args = node.childForFieldName("arguments");
4050
+ if (obj?.type === "variable_name" && method && appVars.has(obj.text)) {
4051
+ const line = node.startPosition.row + 1;
4052
+ if (method === "group") {
4053
+ const groupPrefix = phpFirstString(args);
4054
+ const closure = laravelGroupClosure(args);
4055
+ if (groupPrefix !== null && closure) {
4056
+ const inner = new Set(appVars);
4057
+ for (const v of slimClosureParamVars(closure)) inner.add(v);
4058
+ const body = closure.childForFieldName("body");
4059
+ if (body) slimWalk(body, laravelJoinPath(prefix, groupPrefix), inner, out);
4060
+ }
4061
+ return;
4062
+ }
4063
+ if (SLIM_VERBS.has(method)) {
4064
+ const p = phpFirstString(args);
4065
+ if (p !== null) {
4066
+ out.push({
4067
+ method: method.toUpperCase(),
4068
+ pathTemplate: laravelJoinPath(prefix, p),
4069
+ line,
4070
+ framework: "slim"
4071
+ });
4072
+ }
4073
+ return;
4074
+ }
4075
+ if (method === "any") {
4076
+ const p = phpFirstString(args);
4077
+ if (p !== null) {
4078
+ out.push({ method: "ALL", pathTemplate: laravelJoinPath(prefix, p), line, framework: "slim" });
4079
+ }
4080
+ return;
4081
+ }
4082
+ if (method === "map") {
4083
+ const vals = phpArgumentValues(args);
4084
+ const methods = phpStringArray(vals[0]);
4085
+ const p = vals.length > 1 ? phpStaticString(vals[1]) : null;
4086
+ if (p !== null) {
4087
+ for (const m of methods) {
4088
+ out.push({
4089
+ method: m.toUpperCase(),
4090
+ pathTemplate: laravelJoinPath(prefix, p),
4091
+ line,
4092
+ framework: "slim"
4093
+ });
4094
+ }
4095
+ }
4096
+ return;
4097
+ }
4098
+ }
4099
+ }
4100
+ slimWalk(node, prefix, appVars, out);
4101
+ }
4102
+ var ACTIX_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete", "head", "options", "trace"]);
4103
+ function rustStringContent(node) {
4104
+ if (!node || node.type !== "string_literal") return null;
4105
+ for (let i = 0; i < node.namedChildCount; i++) {
4106
+ if (node.namedChild(i)?.type === "string_content") return node.namedChild(i).text;
4107
+ }
4108
+ return "";
4109
+ }
4110
+ function actixRoutesFromSource(source, parser) {
4111
+ const tree = parseSource2(parser, source);
4112
+ const out = [];
4113
+ walk(tree.rootNode, (node) => {
4114
+ if (node.type === "attribute_item") {
4115
+ actixAttributeRoute(node, out);
4116
+ return;
4117
+ }
4118
+ if (node.type === "call_expression") {
4119
+ actixBuilderRoute(node, out);
4120
+ }
4121
+ });
4122
+ return out;
4123
+ }
4124
+ function actixAttributeRoute(attrItem, out) {
4125
+ const attr = attrItem.namedChild(0);
4126
+ if (!attr || attr.type !== "attribute") return;
4127
+ const nameNode = attr.namedChild(0);
4128
+ if (!nameNode) return;
4129
+ const macro = nameNode.type === "identifier" ? nameNode.text : nameNode.type === "scoped_identifier" ? nameNode.childForFieldName("name")?.text ?? null : null;
4130
+ if (!macro) return;
4131
+ const tokens = attr.childForFieldName("arguments");
4132
+ if (!tokens || tokens.type !== "token_tree") return;
4133
+ const strings = [];
4134
+ for (let i = 0; i < tokens.namedChildCount; i++) {
4135
+ const s = rustStringContent(tokens.namedChild(i));
4136
+ if (s !== null) strings.push(s);
4137
+ }
4138
+ const pathStr = strings[0];
4139
+ if (pathStr === void 0 || !pathStr.startsWith("/")) return;
4140
+ const line = attrItem.startPosition.row + 1;
4141
+ const template = canonicalizeTemplate(pathStr);
4142
+ if (ACTIX_METHODS.has(macro)) {
4143
+ out.push({ method: macro.toUpperCase(), pathTemplate: template, line, framework: "actix-web" });
4144
+ return;
4145
+ }
4146
+ if (macro === "route") {
4147
+ const methods = strings.slice(1).filter((m) => ACTIX_METHODS.has(m.toLowerCase()));
4148
+ const list = methods.length > 0 ? methods.map((m) => m.toUpperCase()) : ["ALL"];
4149
+ for (const m of list) {
4150
+ out.push({ method: m, pathTemplate: template, line, framework: "actix-web" });
4151
+ }
4152
+ }
4153
+ }
4154
+ function actixBuilderRoute(call, out) {
4155
+ const fn = call.childForFieldName("function");
4156
+ if (fn?.type !== "field_expression") return;
4157
+ if (fn.childForFieldName("field")?.text !== "route") return;
4158
+ const args = call.childForFieldName("arguments");
4159
+ const pathStr = rustStringContent(args?.namedChild(0));
4160
+ if (pathStr === null || !pathStr.startsWith("/")) return;
4161
+ const second = args?.namedChild(1);
4162
+ if (!second) return;
4163
+ const method = actixBuilderMethod(second);
4164
+ if (!method) return;
4165
+ out.push({
4166
+ method,
4167
+ pathTemplate: canonicalizeTemplate(pathStr),
4168
+ line: call.startPosition.row + 1,
4169
+ framework: "actix-web"
4170
+ });
4171
+ }
4172
+ function actixBuilderMethod(node) {
4173
+ let method = null;
4174
+ walk(node, (n) => {
4175
+ if (method || n.type !== "scoped_identifier") return;
4176
+ const verb = n.childForFieldName("name")?.text;
4177
+ const scopeLeaf = n.childForFieldName("path")?.text?.split("::").pop();
4178
+ if (scopeLeaf === "web" && verb && ACTIX_METHODS.has(verb)) method = verb.toUpperCase();
4179
+ });
4180
+ return method;
4181
+ }
3917
4182
  function namedArgs(argsNode) {
3918
4183
  const out = [];
3919
4184
  if (!argsNode) return out;
@@ -4230,6 +4495,7 @@ async function addRoutes(graph, services) {
4230
4495
  const goParser = makeGoParser2();
4231
4496
  const rubyParser = makeRubyParser();
4232
4497
  const phpParser = makePhpParser();
4498
+ const rustParser = makeRustParser();
4233
4499
  let nodesAdded = 0;
4234
4500
  let edgesAdded = 0;
4235
4501
  for (const service of services) {
@@ -4252,7 +4518,10 @@ async function addRoutes(graph, services) {
4252
4518
  const isGoService = service.node.language === "go";
4253
4519
  const hasRails = deps["rails"] !== void 0;
4254
4520
  const hasLaravel = deps["laravel/framework"] !== void 0;
4255
- if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel)
4521
+ const hasSlim = deps["slim/slim"] !== void 0;
4522
+ const hasSinatra = deps["sinatra"] !== void 0;
4523
+ const hasActix = deps["actix-web"] !== void 0;
4524
+ if (!hasExpress && !hasFastify && !hasHono && !hasNext && !hasNestjs && !hasFastapi && !hasFlask && !hasDjango && !hasGin && !hasEcho && !hasFiber && !hasChi && !isGoService && !hasRails && !hasLaravel && !hasSlim && !hasSinatra && !hasActix)
4256
4525
  continue;
4257
4526
  const files = await loadSourceFiles(service.dir, service.excludeDirs);
4258
4527
  const mountPrefixes = hasExpress ? await expressMountPrefixes(files, service.dir, await loadTsPathConfig(service.dir)) : /* @__PURE__ */ new Map();
@@ -4263,7 +4532,8 @@ async function addRoutes(graph, services) {
4263
4532
  const isGo = ext === ".go";
4264
4533
  const isRb = ext === ".rb";
4265
4534
  const isPhp = ext === ".php";
4266
- if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp) continue;
4535
+ const isRs = ext === ".rs";
4536
+ if (!JS_ROUTE_EXTENSIONS.has(ext) && !isPy && !isGo && !isRb && !isPhp && !isRs) continue;
4267
4537
  const relFile = toPosix(import_node_path9.default.relative(service.dir, file.path));
4268
4538
  let routes;
4269
4539
  try {
@@ -4273,8 +4543,12 @@ async function addRoutes(graph, services) {
4273
4543
  phpParser,
4274
4544
  relFile === "routes/api.php" ? "/api" : ""
4275
4545
  ) : [];
4546
+ if (hasSlim) routes = routes.concat(slimRoutesFromSource(file.content, phpParser));
4276
4547
  } else if (isRb) {
4277
4548
  routes = hasRails && relFile === "config/routes.rb" ? railsRoutesFromSource(file.content, rubyParser) : [];
4549
+ if (hasSinatra) routes = routes.concat(sinatraRoutesFromSource(file.content, rubyParser));
4550
+ } else if (isRs) {
4551
+ routes = hasActix ? actixRoutesFromSource(file.content, rustParser) : [];
4278
4552
  } else if (isGo) {
4279
4553
  if (hasGin) routes = ginRoutesFromSource(file.content, goParser);
4280
4554
  else if (hasEcho) routes = echoRoutesFromSource(file.content, goParser);
@@ -4458,6 +4732,37 @@ function loadIncidentThresholdsFromEnv() {
4458
4732
  return DEFAULT_INCIDENT_THRESHOLDS;
4459
4733
  }
4460
4734
  }
4735
+ var DEFAULT_LATENCY_STREAM_CEILING_MS = 6e4;
4736
+ function latencyStreamCeilingMs() {
4737
+ const raw = process.env.NEAT_LATENCY_STREAM_CEILING_MS;
4738
+ if (!raw) return DEFAULT_LATENCY_STREAM_CEILING_MS;
4739
+ const n = Number(raw);
4740
+ if (Number.isFinite(n) && n > 0) return n;
4741
+ console.warn(
4742
+ `[neat] NEAT_LATENCY_STREAM_CEILING_MS could not be parsed (${raw}); using default`
4743
+ );
4744
+ return DEFAULT_LATENCY_STREAM_CEILING_MS;
4745
+ }
4746
+ function spanServesEventStream(attrs) {
4747
+ for (const key of [
4748
+ "http.response.header.content-type",
4749
+ "http.response.header.content_type"
4750
+ ]) {
4751
+ const v = attrs[key];
4752
+ const values = Array.isArray(v) ? v : v !== void 0 && v !== null ? [v] : [];
4753
+ for (const item of values) {
4754
+ if (typeof item === "string" && item.toLowerCase().includes("text/event-stream")) {
4755
+ return true;
4756
+ }
4757
+ }
4758
+ }
4759
+ return false;
4760
+ }
4761
+ function spanIsStreaming(span, ceilingMs = latencyStreamCeilingMs()) {
4762
+ if (span.websocketChannel !== void 0) return true;
4763
+ if (spanServesEventStream(span.attributes)) return true;
4764
+ return span.durationNanos > BigInt(Math.round(ceilingMs)) * 1000000n;
4765
+ }
4461
4766
  function httpResponseStatusFromAttrs(attrs) {
4462
4767
  for (const key of ["http.response.status_code", "http.status_code"]) {
4463
4768
  const v = attrs[key];
@@ -5325,6 +5630,12 @@ async function advance4xxBurst(ctx, span, affectedNode, ts, nowMs, status2) {
5325
5630
  );
5326
5631
  ctx.burstState.delete(key);
5327
5632
  }
5633
+ var NEXT_API_ROUTE_SPAN_NAME = /^executing api route \((?:pages|app)\) (\/\S*)$/;
5634
+ function nextApiRouteTemplate(span) {
5635
+ const raw = pickAttr(span, "next.span_name") ?? span.name;
5636
+ const match = raw ? NEXT_API_ROUTE_SPAN_NAME.exec(raw) : null;
5637
+ return match ? match[1] : void 0;
5638
+ }
5328
5639
  function findRouteNodeByHttpRoute(graph, serviceName, method, httpRoute) {
5329
5640
  const target = normalizePathTemplate(httpRoute);
5330
5641
  const m = method?.toUpperCase();
@@ -5347,7 +5658,7 @@ async function handleSpan(ctx, span) {
5347
5658
  }
5348
5659
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
5349
5660
  const isError = span.statusCode === 2;
5350
- const durationMs = span.durationNanos > 0n ? Number(span.durationNanos) / 1e6 : void 0;
5661
+ const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
5351
5662
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
5352
5663
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
5353
5664
  cacheSpanService(span, nowMs, callSite);
@@ -5544,12 +5855,13 @@ async function handleSpan(ctx, span) {
5544
5855
  }
5545
5856
  }
5546
5857
  }
5547
- if (span.httpRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
5858
+ const fusionRoute = nextApiRouteTemplate(span) ?? span.httpRoute;
5859
+ if (fusionRoute && (span.kind === 2 || span.kind === 1 || span.kind === 0 || span.kind === void 0)) {
5548
5860
  const routeNodeId = findRouteNodeByHttpRoute(
5549
5861
  ctx.graph,
5550
5862
  span.service,
5551
5863
  span.httpMethod,
5552
- span.httpRoute
5864
+ fusionRoute
5553
5865
  );
5554
5866
  if (routeNodeId) {
5555
5867
  const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
@@ -5953,19 +6265,19 @@ function confidenceFromMix(edges, now = Date.now()) {
5953
6265
  function longestIncomingWalk(graph, start, maxDepth) {
5954
6266
  let best = { path: [start], edges: [] };
5955
6267
  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] };
6268
+ function step(node, path76, edges) {
6269
+ if (path76.length > best.path.length) {
6270
+ best = { path: [...path76], edges: [...edges] };
5959
6271
  }
5960
- if (path74.length - 1 >= maxDepth) return;
6272
+ if (path76.length - 1 >= maxDepth) return;
5961
6273
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
5962
6274
  for (const [srcId, edge] of incoming) {
5963
6275
  if (visited.has(srcId)) continue;
5964
6276
  visited.add(srcId);
5965
- path74.push(srcId);
6277
+ path76.push(srcId);
5966
6278
  edges.push(edge);
5967
- step(srcId, path74, edges);
5968
- path74.pop();
6279
+ step(srcId, path76, edges);
6280
+ path76.pop();
5969
6281
  edges.pop();
5970
6282
  visited.delete(srcId);
5971
6283
  }
@@ -5973,11 +6285,11 @@ function longestIncomingWalk(graph, start, maxDepth) {
5973
6285
  step(start, [start], []);
5974
6286
  return best;
5975
6287
  }
5976
- function databaseRootCauseShape(graph, origin, walk9) {
6288
+ function databaseRootCauseShape(graph, origin, walk10) {
5977
6289
  const targetDb = origin;
5978
6290
  const candidatePairs = compatPairs().filter((p) => p.engine === targetDb.engine);
5979
6291
  if (candidatePairs.length === 0) return null;
5980
- for (const id of walk9.path) {
6292
+ for (const id of walk10.path) {
5981
6293
  const owner = resolveOwningService(graph, id);
5982
6294
  if (!owner) continue;
5983
6295
  const { id: serviceId15, svc } = owner;
@@ -6004,8 +6316,8 @@ function databaseRootCauseShape(graph, origin, walk9) {
6004
6316
  }
6005
6317
  return null;
6006
6318
  }
6007
- function serviceRootCauseShape(graph, _origin, walk9) {
6008
- for (const id of walk9.path) {
6319
+ function serviceRootCauseShape(graph, _origin, walk10) {
6320
+ for (const id of walk10.path) {
6009
6321
  const owner = resolveOwningService(graph, id);
6010
6322
  if (!owner) continue;
6011
6323
  const { id: serviceId15, svc } = owner;
@@ -6041,15 +6353,15 @@ function serviceRootCauseShape(graph, _origin, walk9) {
6041
6353
  }
6042
6354
  return null;
6043
6355
  }
6044
- function fileRootCauseShape(graph, origin, walk9) {
6356
+ function fileRootCauseShape(graph, origin, walk10) {
6045
6357
  const owner = resolveOwningService(graph, origin.id);
6046
6358
  if (!owner) return null;
6047
- return serviceRootCauseShape(graph, owner.svc, walk9);
6359
+ return serviceRootCauseShape(graph, owner.svc, walk10);
6048
6360
  }
6049
- function symbolRootCauseShape(graph, origin, walk9) {
6361
+ function symbolRootCauseShape(graph, origin, walk10) {
6050
6362
  const owner = resolveOwningService(graph, origin.id);
6051
6363
  if (!owner) return null;
6052
- return serviceRootCauseShape(graph, owner.svc, walk9);
6364
+ return serviceRootCauseShape(graph, owner.svc, walk10);
6053
6365
  }
6054
6366
  var rootCauseShapes = {
6055
6367
  [import_types8.NodeType.DatabaseNode]: databaseRootCauseShape,
@@ -6062,25 +6374,29 @@ function legacyRootCause(graph, errorNodeId, errorEvent, incidents) {
6062
6374
  const origin = graph.getNodeAttributes(errorNodeId);
6063
6375
  const shape = rootCauseShapes[origin.type];
6064
6376
  if (shape) {
6065
- const walk9 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
6066
- const match = shape(graph, origin, walk9);
6377
+ const walk10 = longestIncomingWalk(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH);
6378
+ const match = shape(graph, origin, walk10);
6067
6379
  if (match) {
6068
6380
  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
- });
6381
+ return {
6382
+ source: "compat",
6383
+ result: import_types8.RootCauseResultSchema.parse({
6384
+ rootCauseNode: match.rootCauseNode,
6385
+ rootCauseReason: reason,
6386
+ traversalPath: walk10.path,
6387
+ edgeProvenances: walk10.edges.map((e) => e.provenance),
6388
+ confidence: confidenceFromMix(walk10.edges),
6389
+ fixRecommendation: match.fixRecommendation
6390
+ })
6391
+ };
6077
6392
  }
6078
6393
  }
6079
6394
  if (origin.type === import_types8.NodeType.ServiceNode) {
6080
6395
  const crossService = crossServiceRootCause(graph, errorNodeId, incidents, errorEvent);
6081
- if (crossService) return crossService;
6396
+ if (crossService) return { result: crossService, source: "cross-service" };
6082
6397
  }
6083
- return rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
6398
+ const incident = rootCauseFromIncidents(errorNodeId, incidents, errorEvent);
6399
+ return incident ? { result: incident, source: "incident" } : null;
6084
6400
  }
6085
6401
  var INCIDENT_ROOT_CAUSE_CONFIDENCE = 0.6;
6086
6402
  function incidentMatchesNode(ev, nodeId) {
@@ -6176,26 +6492,75 @@ function dominantFailingCall(graph, serviceId15, visited) {
6176
6492
  return best;
6177
6493
  }
6178
6494
  function followFailingCallChain(graph, originServiceId, maxDepth) {
6179
- const path74 = [originServiceId];
6495
+ const path76 = [originServiceId];
6180
6496
  const edges = [];
6181
6497
  const visited = /* @__PURE__ */ new Set([originServiceId]);
6182
6498
  let current = originServiceId;
6183
6499
  for (let depth = 0; depth < maxDepth; depth++) {
6184
6500
  const hop = dominantFailingCall(graph, current, visited);
6185
6501
  if (!hop) break;
6186
- path74.push(hop.nextService);
6502
+ path76.push(hop.nextService);
6187
6503
  edges.push(hop.edge);
6188
6504
  visited.add(hop.nextService);
6189
6505
  current = hop.nextService;
6190
6506
  }
6191
6507
  if (edges.length === 0) return null;
6192
- return { path: path74, edges, culprit: current };
6508
+ return { path: path76, edges, culprit: current };
6509
+ }
6510
+ function isStaleCallEdge(e) {
6511
+ return e.type === import_types8.EdgeType.CALLS && e.provenance === import_types8.Provenance.STALE;
6512
+ }
6513
+ function staleCallDominates(e, id, curEdge, curId) {
6514
+ const ev = e.signal?.spanCount ?? e.callCount ?? 0;
6515
+ const cv = curEdge.signal?.spanCount ?? curEdge.callCount ?? 0;
6516
+ if (ev !== cv) return ev > cv;
6517
+ return id < curId;
6518
+ }
6519
+ function dominantStaleCall(graph, serviceId15, visited) {
6520
+ const bestByCallee = /* @__PURE__ */ new Map();
6521
+ for (const src of callSourcesForService(graph, serviceId15)) {
6522
+ for (const edgeId of graph.outboundEdges(src)) {
6523
+ const e = graph.getEdgeAttributes(edgeId);
6524
+ if (e.type !== import_types8.EdgeType.CALLS) continue;
6525
+ if (isFrontierNode(graph, e.target)) continue;
6526
+ const owner = resolveOwningService(graph, e.target);
6527
+ if (!owner || visited.has(owner.id)) continue;
6528
+ const cur = bestByCallee.get(owner.id);
6529
+ if (!cur || import_types8.PROV_RANK[e.provenance] > import_types8.PROV_RANK[cur.provenance]) {
6530
+ bestByCallee.set(owner.id, e);
6531
+ }
6532
+ }
6533
+ }
6534
+ let best = null;
6535
+ for (const [id, edge] of bestByCallee) {
6536
+ if (!isStaleCallEdge(edge)) continue;
6537
+ if (!best || staleCallDominates(edge, id, best.edge, best.nextService)) {
6538
+ best = { nextService: id, edge };
6539
+ }
6540
+ }
6541
+ return best;
6542
+ }
6543
+ function followStaleCallChain(graph, originServiceId, maxDepth) {
6544
+ const path76 = [originServiceId];
6545
+ const edges = [];
6546
+ const visited = /* @__PURE__ */ new Set([originServiceId]);
6547
+ let current = originServiceId;
6548
+ for (let depth = 0; depth < maxDepth; depth++) {
6549
+ const hop = dominantStaleCall(graph, current, visited);
6550
+ if (!hop) break;
6551
+ path76.push(hop.nextService);
6552
+ edges.push(hop.edge);
6553
+ visited.add(hop.nextService);
6554
+ current = hop.nextService;
6555
+ }
6556
+ if (edges.length === 0) return null;
6557
+ return { path: path76, edges, culprit: current };
6193
6558
  }
6194
6559
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6195
6560
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
6196
6561
  if (!chain) return null;
6197
6562
  const culprit = chain.culprit;
6198
- const path74 = [...chain.path];
6563
+ const path76 = [...chain.path];
6199
6564
  const edgeProvenances = chain.edges.map((e) => e.provenance);
6200
6565
  const baseConfidence = confidenceFromMix(chain.edges);
6201
6566
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -6203,14 +6568,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6203
6568
  if (loc) {
6204
6569
  let rootCauseNode = culprit;
6205
6570
  if (loc.fileNode) {
6206
- path74.push(loc.fileNode);
6571
+ path76.push(loc.fileNode);
6207
6572
  edgeProvenances.push(import_types8.Provenance.OBSERVED);
6208
6573
  rootCauseNode = loc.fileNode;
6209
6574
  }
6210
6575
  return import_types8.RootCauseResultSchema.parse({
6211
6576
  rootCauseNode,
6212
6577
  rootCauseReason: loc.rootCauseReason,
6213
- traversalPath: path74,
6578
+ traversalPath: path76,
6214
6579
  edgeProvenances,
6215
6580
  confidence,
6216
6581
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -6222,7 +6587,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6222
6587
  return import_types8.RootCauseResultSchema.parse({
6223
6588
  rootCauseNode: culprit,
6224
6589
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
6225
- traversalPath: path74,
6590
+ traversalPath: path76,
6226
6591
  edgeProvenances,
6227
6592
  confidence,
6228
6593
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -6609,17 +6974,20 @@ function displayNameOf(nodeId) {
6609
6974
  return nodeId.replace(/^[a-z]+:/, "");
6610
6975
  }
6611
6976
  function getRootCause(graph, errorNodeId, errorEvent, incidents, opts) {
6612
- const legacy = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
6613
- if (!legacy) return null;
6977
+ const tagged = legacyRootCause(graph, errorNodeId, errorEvent, incidents);
6978
+ if (!tagged) return null;
6614
6979
  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());
6980
+ if (!navigation) return tagged.result;
6981
+ return enrichWithNavigation(graph, errorNodeId, tagged, incidents, opts?.now ?? Date.now());
6617
6982
  }
6618
- function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
6983
+ function enrichWithNavigation(graph, errorNodeId, tagged, incidents, now) {
6984
+ const legacy = tagged.result;
6619
6985
  const seedNode = legacy.rootCauseNode;
6620
6986
  const seedCtx = graph.hasNode(seedNode) ? nodeContext(graph, seedNode, incidents, now) : null;
6621
6987
  const lastProv = legacy.edgeProvenances[legacy.edgeProvenances.length - 1];
6622
6988
  const candidates = [];
6989
+ const deadEndOnSymptom = tagged.source === "incident" && seedNode === errorNodeId && legacy.traversalPath.length === 1;
6990
+ const staleChain = deadEndOnSymptom && !(seedCtx && isVictimSeed(seedCtx)) ? followStaleCallChain(graph, errorNodeId, ROOT_CAUSE_MAX_DEPTH) : null;
6623
6991
  if (seedCtx && isVictimSeed(seedCtx)) {
6624
6992
  const origin = findLoadOrigin(graph, errorNodeId, incidents, now);
6625
6993
  const staleNote = seedCtx.stale ? "; it has gone STALE under load" : "";
@@ -6644,6 +7012,27 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
6644
7012
  confidence: Math.min(legacy.confidence, 0.4),
6645
7013
  ...lastProv ? { provenance: lastProv } : {}
6646
7014
  });
7015
+ } else if (staleChain) {
7016
+ const culprit = staleChain.culprit;
7017
+ const culpritName = displayNameOf(culprit);
7018
+ const seedName = displayNameOf(seedNode);
7019
+ const staleConfidence = confidenceFromMix(staleChain.edges, now);
7020
+ candidates.push({
7021
+ node: culprit,
7022
+ classification: "primary-failure",
7023
+ 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.`,
7024
+ context: nodeContext(graph, culprit, incidents, now),
7025
+ confidence: staleConfidence,
7026
+ provenance: import_types8.Provenance.STALE
7027
+ });
7028
+ candidates.push({
7029
+ node: seedNode,
7030
+ classification: "symptom-only",
7031
+ 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.`,
7032
+ context: seedCtx ?? EMPTY_CONTEXT,
7033
+ confidence: Math.min(legacy.confidence, PROVENANCE_CEILING.STALE),
7034
+ ...lastProv ? { provenance: lastProv } : {}
7035
+ });
6647
7036
  } else {
6648
7037
  candidates.push({
6649
7038
  node: seedNode,
@@ -6657,11 +7046,14 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
6657
7046
  const top = candidates[0];
6658
7047
  let traversalPath = legacy.traversalPath;
6659
7048
  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);
7049
+ if (staleChain && top.node === staleChain.culprit) {
7050
+ traversalPath = staleChain.path;
7051
+ edgeProvenances = staleChain.edges.map((e) => e.provenance);
7052
+ } else if (top.node !== seedNode) {
7053
+ const path76 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
7054
+ if (path76) {
7055
+ traversalPath = path76.nodes;
7056
+ edgeProvenances = path76.edges.map((e) => e.provenance);
6665
7057
  } else {
6666
7058
  traversalPath = [errorNodeId, top.node];
6667
7059
  edgeProvenances = [top.provenance ?? import_types8.Provenance.OBSERVED];
@@ -6683,6 +7075,9 @@ function fixRecommendationForTop(top, seedNode, legacy) {
6683
7075
  return legacy.fixRecommendation;
6684
7076
  }
6685
7077
  const name = top.node.replace(/^service:/, "");
7078
+ if (top.provenance === import_types8.Provenance.STALE) {
7079
+ 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}.`;
7080
+ }
6686
7081
  if (top.classification === "primary-failure") {
6687
7082
  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
7083
  }
@@ -8169,7 +8564,7 @@ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
8169
8564
  var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
8170
8565
  var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
8171
8566
  var import_tree_sitter_kotlin = __toESM(require("tree-sitter-kotlin"), 1);
8172
- var import_tree_sitter_rust = __toESM(require("tree-sitter-rust"), 1);
8567
+ var import_tree_sitter_rust2 = __toESM(require("tree-sitter-rust"), 1);
8173
8568
  var import_tree_sitter_cpp = __toESM(require("tree-sitter-cpp"), 1);
8174
8569
  var import_types21 = require("@neat.is/types");
8175
8570
  var PARSE_CHUNK3 = 16384;
@@ -8190,7 +8585,7 @@ var SYMBOL_GRAMMAR_BY_EXT = {
8190
8585
  ".cs": import_tree_sitter_c_sharp.default,
8191
8586
  ".java": import_tree_sitter_java.default,
8192
8587
  ".kt": import_tree_sitter_kotlin.default,
8193
- ".rs": import_tree_sitter_rust.default,
8588
+ ".rs": import_tree_sitter_rust2.default,
8194
8589
  // C++ (ADR-202) — only the UNAMBIGUOUS extensions. `.cpp` / `.cc` / `.cxx` /
8195
8590
  // `.c++` are implementation files; `.hpp` / `.hh` / `.hxx` / `.h++` are C++-only
8196
8591
  // headers. `.h` and `.c` are deliberately absent: they are shared with C (a
@@ -8630,15 +9025,15 @@ function collectKotlinSymbolDefs(root) {
8630
9025
  });
8631
9026
  };
8632
9027
  const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8633
- const firstChildOfType = (node, types) => {
9028
+ const firstChildOfType2 = (node, types) => {
8634
9029
  for (let i = 0; i < node.namedChildCount; i++) {
8635
9030
  const child = node.namedChild(i);
8636
9031
  if (child && types.includes(child.type)) return child;
8637
9032
  }
8638
9033
  return void 0;
8639
9034
  };
8640
- const nameOf = (node, ...types) => firstChildOfType(node, types)?.text;
8641
- const bodyOf = (node) => firstChildOfType(node, ["class_body", "enum_class_body"]);
9035
+ const nameOf = (node, ...types) => firstChildOfType2(node, types)?.text;
9036
+ const bodyOf = (node) => firstChildOfType2(node, ["class_body", "enum_class_body"]);
8642
9037
  let pkg;
8643
9038
  for (let i = 0; i < root.namedChildCount; i++) {
8644
9039
  const child = root.namedChild(i);
@@ -9139,7 +9534,7 @@ async function addSymbolEdges(graph, services) {
9139
9534
  return best;
9140
9535
  };
9141
9536
  const requests = [];
9142
- const walk9 = (node) => {
9537
+ const walk10 = (node) => {
9143
9538
  if (node.type === "class_declaration" || node.type === "abstract_class_declaration" || node.type === "class") {
9144
9539
  const self = localBySpan.get(`${node.startPosition.row + 1}:${node.endPosition.row + 1}`);
9145
9540
  if (self && self.kind === "class") {
@@ -9185,10 +9580,10 @@ async function addSymbolEdges(graph, services) {
9185
9580
  }
9186
9581
  for (let i = 0; i < node.namedChildCount; i++) {
9187
9582
  const child = node.namedChild(i);
9188
- if (child) walk9(child);
9583
+ if (child) walk10(child);
9189
9584
  }
9190
9585
  };
9191
- walk9(root);
9586
+ walk10(root);
9192
9587
  for (const req of requests) {
9193
9588
  const targetSid = resolveTarget(req.targetName, req.wantKind);
9194
9589
  if (!targetSid) continue;
@@ -9484,7 +9879,7 @@ async function addServerActions(graph, services) {
9484
9879
 
9485
9880
  // src/extract/databases/index.ts
9486
9881
  init_cjs_shims();
9487
- var import_node_path35 = __toESM(require("path"), 1);
9882
+ var import_node_path36 = __toESM(require("path"), 1);
9488
9883
  var import_types24 = require("@neat.is/types");
9489
9884
 
9490
9885
  // src/extract/databases/db-config-yaml.ts
@@ -9919,9 +10314,163 @@ async function parse8(serviceDir) {
9919
10314
  }
9920
10315
  var sequelizeParser = { name: "sequelize", parse: parse8 };
9921
10316
 
9922
- // src/extract/databases/docker-compose.ts
10317
+ // src/extract/databases/csharp.ts
9923
10318
  init_cjs_shims();
10319
+ var import_node_fs24 = require("fs");
9924
10320
  var import_node_path34 = __toESM(require("path"), 1);
10321
+ var CS_EXT = ".cs";
10322
+ var NPGSQL_GATE = /\bUseNpgsql\b|\bNpgsql\b/;
10323
+ var REDIS_GATE = /\bConnectionMultiplexer\b|\bStackExchange\.Redis\b|\bConfigurationOptions\.Parse\b|\bAddStackExchangeRedisCache\b/;
10324
+ var ENV_READ_RE = /(?:GetEnvironmentVariable|GetConnectionString)\(\s*"([^"]+)"\s*\)|Configuration\s*\[\s*"([^"]+)"\s*\]/g;
10325
+ var STRING_LITERAL_RE = /@?"([^"\\]*(?:\\.[^"\\]*)*)"/g;
10326
+ function hostIsUnresolved(host) {
10327
+ return host === "" || /[${}]/.test(host);
10328
+ }
10329
+ function looksLikePostgres(s) {
10330
+ return /(?:^|;)\s*(?:host|server|data\s*source)\s*=/i.test(s) || /^postgres(?:ql)?:\/\//i.test(s);
10331
+ }
10332
+ function looksLikeRedis(s) {
10333
+ return /^rediss?:\/\//i.test(s) || /^[A-Za-z0-9_.-]+:\d+(?:$|,)/.test(s) || /,\s*(?:ssl|abortconnect|allowadmin|connecttimeout|password|user)\s*=/i.test(s);
10334
+ }
10335
+ function parsePostgresConnection(raw) {
10336
+ const s = raw.trim();
10337
+ if (/^postgres(?:ql)?:\/\//i.test(s)) return parseConnectionString(s);
10338
+ const fields = /* @__PURE__ */ new Map();
10339
+ for (const part of s.split(";")) {
10340
+ const eq = part.indexOf("=");
10341
+ if (eq < 0) continue;
10342
+ const key = part.slice(0, eq).trim().toLowerCase().replace(/\s+/g, " ");
10343
+ const value = part.slice(eq + 1).trim();
10344
+ if (value && !fields.has(key)) fields.set(key, value);
10345
+ }
10346
+ const hostRaw = fields.get("host") ?? fields.get("server") ?? fields.get("data source");
10347
+ if (!hostRaw) return null;
10348
+ const host = hostRaw.split(",")[0].trim();
10349
+ if (hostIsUnresolved(host)) return null;
10350
+ const portRaw = fields.get("port");
10351
+ const port = portRaw && /^\d+$/.test(portRaw) ? Number(portRaw) : void 0;
10352
+ const database = fields.get("database") ?? fields.get("db") ?? "";
10353
+ return { host, port, database, engine: "postgresql", engineVersion: "unknown" };
10354
+ }
10355
+ function parseRedisEndpoint(raw) {
10356
+ const s = raw.trim();
10357
+ if (/^rediss?:\/\//i.test(s)) return parseConnectionString(s);
10358
+ const first = s.split(",")[0].trim();
10359
+ const m = first.match(/^([A-Za-z0-9_.-]+)(?::(\d+))?$/);
10360
+ if (!m) return null;
10361
+ const host = m[1];
10362
+ if (hostIsUnresolved(host) || host.includes("=")) return null;
10363
+ const port = m[2] ? Number(m[2]) : void 0;
10364
+ return { host, port, database: "", engine: "redis", engineVersion: "unknown" };
10365
+ }
10366
+ async function resolveEnvUpTree(startDir, name) {
10367
+ let dir = import_node_path34.default.resolve(startDir);
10368
+ for (let depth = 0; depth < 12; depth++) {
10369
+ const value = await resolveEnvVar(dir, name);
10370
+ if (value !== null) return value;
10371
+ const atRepoRoot = await import_node_fs24.promises.access(import_node_path34.default.join(dir, ".git")).then(() => true).catch(() => false);
10372
+ const parent = import_node_path34.default.dirname(dir);
10373
+ if (atRepoRoot || parent === dir) break;
10374
+ dir = parent;
10375
+ }
10376
+ return null;
10377
+ }
10378
+ async function interpolateEnvRefs(value, dir) {
10379
+ const refs = /* @__PURE__ */ new Set();
10380
+ for (const m of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g)) {
10381
+ refs.add(m[1] ?? m[2]);
10382
+ }
10383
+ let out = value;
10384
+ for (const name of refs) {
10385
+ const resolved = await resolveEnvUpTree(dir, name);
10386
+ if (resolved === null) continue;
10387
+ out = out.split(`\${${name}}`).join(resolved).replace(new RegExp(`\\$${name}\\b`, "g"), resolved);
10388
+ }
10389
+ return out;
10390
+ }
10391
+ function stringLiterals(masked) {
10392
+ const out = [];
10393
+ STRING_LITERAL_RE.lastIndex = 0;
10394
+ let m;
10395
+ while ((m = STRING_LITERAL_RE.exec(masked)) !== null) out.push(m[1]);
10396
+ return out;
10397
+ }
10398
+ function envKeys(masked) {
10399
+ const out = [];
10400
+ ENV_READ_RE.lastIndex = 0;
10401
+ let m;
10402
+ while ((m = ENV_READ_RE.exec(masked)) !== null) {
10403
+ const key = m[1] ?? m[2];
10404
+ if (key) out.push(key);
10405
+ }
10406
+ return out;
10407
+ }
10408
+ async function resolveConfigs(literals, keys, serviceDir, looksLike, parse11) {
10409
+ const out = [];
10410
+ for (const key of keys) {
10411
+ const raw = await resolveEnvUpTree(serviceDir, key);
10412
+ if (raw === null) continue;
10413
+ const value = await interpolateEnvRefs(raw, serviceDir);
10414
+ if (!looksLike(value)) continue;
10415
+ const parsed = parse11(value);
10416
+ if (parsed) out.push(parsed);
10417
+ }
10418
+ for (const lit of literals) {
10419
+ if (!looksLike(lit)) continue;
10420
+ const parsed = parse11(lit);
10421
+ if (parsed) out.push(parsed);
10422
+ }
10423
+ return out;
10424
+ }
10425
+ async function parse9(serviceDir) {
10426
+ const files = (await walkSourceFiles(serviceDir).catch(() => [])).filter(
10427
+ (f) => import_node_path34.default.extname(f) === CS_EXT
10428
+ );
10429
+ if (files.length === 0) return [];
10430
+ const sources = [];
10431
+ for (const file of files) {
10432
+ const content = await import_node_fs24.promises.readFile(file, "utf8").catch(() => null);
10433
+ if (content !== null) sources.push({ file, content });
10434
+ }
10435
+ let pgGateFile = null;
10436
+ let redisGateFile = null;
10437
+ for (const { file, content } of sources) {
10438
+ if (pgGateFile === null && NPGSQL_GATE.test(content)) pgGateFile = file;
10439
+ if (redisGateFile === null && REDIS_GATE.test(content)) redisGateFile = file;
10440
+ }
10441
+ if (!pgGateFile && !redisGateFile) return [];
10442
+ const literals = [];
10443
+ const keys = [];
10444
+ for (const { content } of sources) {
10445
+ const masked = maskCommentsInSource(content);
10446
+ literals.push(...stringLiterals(masked));
10447
+ keys.push(...envKeys(masked));
10448
+ }
10449
+ const out = [];
10450
+ const seenHosts = /* @__PURE__ */ new Set();
10451
+ const push = (config, sourceFile) => {
10452
+ const dedupe = `${config.engine}:${config.host}`;
10453
+ if (seenHosts.has(dedupe)) return;
10454
+ seenHosts.add(dedupe);
10455
+ out.push({ ...config, sourceFile });
10456
+ };
10457
+ if (pgGateFile) {
10458
+ for (const pg3 of await resolveConfigs(literals, keys, serviceDir, looksLikePostgres, parsePostgresConnection)) {
10459
+ push(pg3, pgGateFile);
10460
+ }
10461
+ }
10462
+ if (redisGateFile) {
10463
+ for (const redis of await resolveConfigs(literals, keys, serviceDir, looksLikeRedis, parseRedisEndpoint)) {
10464
+ push(redis, redisGateFile);
10465
+ }
10466
+ }
10467
+ return out;
10468
+ }
10469
+ var csharpParser = { name: "csharp", parse: parse9 };
10470
+
10471
+ // src/extract/databases/docker-compose.ts
10472
+ init_cjs_shims();
10473
+ var import_node_path35 = __toESM(require("path"), 1);
9925
10474
  function portFromService(svc) {
9926
10475
  for (const raw of svc.ports ?? []) {
9927
10476
  const str = String(raw);
@@ -9946,9 +10495,9 @@ function databaseFromEnv(svc) {
9946
10495
  };
9947
10496
  return get("POSTGRES_DB") ?? get("MYSQL_DATABASE") ?? get("MONGO_INITDB_DATABASE") ?? "";
9948
10497
  }
9949
- async function parse9(serviceDir) {
10498
+ async function parse10(serviceDir) {
9950
10499
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
9951
- const abs = import_node_path34.default.join(serviceDir, name);
10500
+ const abs = import_node_path35.default.join(serviceDir, name);
9952
10501
  if (!await exists2(abs)) continue;
9953
10502
  const raw = await readYaml(abs);
9954
10503
  if (!raw?.services) return [];
@@ -9970,7 +10519,7 @@ async function parse9(serviceDir) {
9970
10519
  }
9971
10520
  return [];
9972
10521
  }
9973
- var dockerComposeParser = { name: "docker-compose", parse: parse9 };
10522
+ var dockerComposeParser = { name: "docker-compose", parse: parse10 };
9974
10523
 
9975
10524
  // src/extract/databases/index.ts
9976
10525
  var DB_PARSERS = [
@@ -9982,6 +10531,7 @@ var DB_PARSERS = [
9982
10531
  ormconfigParser,
9983
10532
  typeormParser,
9984
10533
  sequelizeParser,
10534
+ csharpParser,
9985
10535
  dockerComposeParser
9986
10536
  ];
9987
10537
  function compatibleDriversFor(engine) {
@@ -10120,7 +10670,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10120
10670
  discoveredVia: mergedDiscoveredVia
10121
10671
  });
10122
10672
  }
10123
- const relConfigFile = toPosix(import_node_path35.default.relative(service.dir, config.sourceFile));
10673
+ const relConfigFile = toPosix(import_node_path36.default.relative(service.dir, config.sourceFile));
10124
10674
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10125
10675
  graph,
10126
10676
  service.pkg.name,
@@ -10129,7 +10679,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10129
10679
  );
10130
10680
  nodesAdded += fn;
10131
10681
  edgesAdded += fe;
10132
- const evidenceFile = toPosix(import_node_path35.default.relative(scanPath, config.sourceFile));
10682
+ const evidenceFile = toPosix(import_node_path36.default.relative(scanPath, config.sourceFile));
10133
10683
  const edge = {
10134
10684
  id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types24.EdgeType.CONNECTS_TO),
10135
10685
  source: fileNodeId,
@@ -10147,15 +10697,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10147
10697
  if (allConfigs.length === 1) {
10148
10698
  const primary = allConfigs[0];
10149
10699
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
10150
- const relPath = import_node_path35.default.relative(scanPath, primary.sourceFile);
10700
+ const relPath = import_node_path36.default.relative(scanPath, primary.sourceFile);
10151
10701
  const cfgId = (0, import_types24.configId)(relPath);
10152
10702
  if (!graph.hasNode(cfgId)) {
10153
10703
  const cfgNode = {
10154
10704
  id: cfgId,
10155
10705
  type: import_types24.NodeType.ConfigNode,
10156
- name: import_node_path35.default.basename(primary.sourceFile),
10706
+ name: import_node_path36.default.basename(primary.sourceFile),
10157
10707
  path: relPath,
10158
- fileType: isConfigFile(import_node_path35.default.basename(primary.sourceFile)).fileType || "config"
10708
+ fileType: isConfigFile(import_node_path36.default.basename(primary.sourceFile)).fileType || "config"
10159
10709
  };
10160
10710
  graph.addNode(cfgId, cfgNode);
10161
10711
  nodesAdded++;
@@ -10196,27 +10746,27 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
10196
10746
 
10197
10747
  // src/extract/configs.ts
10198
10748
  init_cjs_shims();
10199
- var import_node_fs24 = require("fs");
10200
- var import_node_path36 = __toESM(require("path"), 1);
10749
+ var import_node_fs25 = require("fs");
10750
+ var import_node_path37 = __toESM(require("path"), 1);
10201
10751
  var import_types25 = require("@neat.is/types");
10202
10752
  async function walkConfigFiles(dir, excludeDirs = []) {
10203
- const excluded = new Set(excludeDirs.map((d) => import_node_path36.default.resolve(d)));
10753
+ const excluded = new Set(excludeDirs.map((d) => import_node_path37.default.resolve(d)));
10204
10754
  const out = [];
10205
- async function walk9(current) {
10206
- const entries = await import_node_fs24.promises.readdir(current, { withFileTypes: true });
10755
+ async function walk10(current) {
10756
+ const entries = await import_node_fs25.promises.readdir(current, { withFileTypes: true });
10207
10757
  for (const entry of entries) {
10208
- const full = import_node_path36.default.join(current, entry.name);
10758
+ const full = import_node_path37.default.join(current, entry.name);
10209
10759
  if (entry.isDirectory()) {
10210
10760
  if (IGNORED_DIRS.has(entry.name)) continue;
10211
- if (excluded.has(import_node_path36.default.resolve(full))) continue;
10761
+ if (excluded.has(import_node_path37.default.resolve(full))) continue;
10212
10762
  if (await isPythonVenvDir(full)) continue;
10213
- await walk9(full);
10763
+ await walk10(full);
10214
10764
  } else if (entry.isFile() && isConfigFile(entry.name).match) {
10215
10765
  out.push(full);
10216
10766
  }
10217
10767
  }
10218
10768
  }
10219
- await walk9(dir);
10769
+ await walk10(dir);
10220
10770
  return out;
10221
10771
  }
10222
10772
  async function addConfigNodes(graph, services, scanPath) {
@@ -10225,19 +10775,19 @@ async function addConfigNodes(graph, services, scanPath) {
10225
10775
  for (const service of services) {
10226
10776
  const configFiles = await walkConfigFiles(service.dir, service.excludeDirs);
10227
10777
  for (const file of configFiles) {
10228
- const relPath = import_node_path36.default.relative(scanPath, file);
10778
+ const relPath = import_node_path37.default.relative(scanPath, file);
10229
10779
  const node = {
10230
10780
  id: (0, import_types25.configId)(relPath),
10231
10781
  type: import_types25.NodeType.ConfigNode,
10232
- name: import_node_path36.default.basename(file),
10782
+ name: import_node_path37.default.basename(file),
10233
10783
  path: relPath,
10234
- fileType: isConfigFile(import_node_path36.default.basename(file)).fileType
10784
+ fileType: isConfigFile(import_node_path37.default.basename(file)).fileType
10235
10785
  };
10236
10786
  if (!graph.hasNode(node.id)) {
10237
10787
  graph.addNode(node.id, node);
10238
10788
  nodesAdded++;
10239
10789
  }
10240
- const relToService = toPosix(import_node_path36.default.relative(service.dir, file));
10790
+ const relToService = toPosix(import_node_path37.default.relative(service.dir, file));
10241
10791
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
10242
10792
  graph,
10243
10793
  service.pkg.name,
@@ -10253,7 +10803,7 @@ async function addConfigNodes(graph, services, scanPath) {
10253
10803
  type: import_types25.EdgeType.CONFIGURED_BY,
10254
10804
  provenance: import_types25.Provenance.EXTRACTED,
10255
10805
  confidence: (0, import_types25.confidenceForExtracted)("structural"),
10256
- evidence: { file: relPath.split(import_node_path36.default.sep).join("/") }
10806
+ evidence: { file: relPath.split(import_node_path37.default.sep).join("/") }
10257
10807
  };
10258
10808
  if (!graph.hasEdge(edge.id)) {
10259
10809
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -10266,8 +10816,8 @@ async function addConfigNodes(graph, services, scanPath) {
10266
10816
 
10267
10817
  // src/extract/proto.ts
10268
10818
  init_cjs_shims();
10269
- var import_node_fs25 = require("fs");
10270
- var import_node_path37 = __toESM(require("path"), 1);
10819
+ var import_node_fs26 = require("fs");
10820
+ var import_node_path38 = __toESM(require("path"), 1);
10271
10821
  var import_types26 = require("@neat.is/types");
10272
10822
  var PROTO_EXTENSION = ".proto";
10273
10823
  function packageOf(content) {
@@ -10305,23 +10855,23 @@ function grpcMethodsFromProto(content, fqPackage) {
10305
10855
  return out;
10306
10856
  }
10307
10857
  async function walkProtoFiles(dir, excludeDirs = []) {
10308
- const excluded = new Set(excludeDirs.map((d) => import_node_path37.default.resolve(d)));
10858
+ const excluded = new Set(excludeDirs.map((d) => import_node_path38.default.resolve(d)));
10309
10859
  const out = [];
10310
- async function walk9(current) {
10311
- const entries = await import_node_fs25.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10860
+ async function walk10(current) {
10861
+ const entries = await import_node_fs26.promises.readdir(current, { withFileTypes: true }).catch(() => []);
10312
10862
  for (const entry of entries) {
10313
- const full = import_node_path37.default.join(current, entry.name);
10863
+ const full = import_node_path38.default.join(current, entry.name);
10314
10864
  if (entry.isDirectory()) {
10315
10865
  if (IGNORED_DIRS.has(entry.name)) continue;
10316
- if (excluded.has(import_node_path37.default.resolve(full))) continue;
10866
+ if (excluded.has(import_node_path38.default.resolve(full))) continue;
10317
10867
  if (await isPythonVenvDir(full)) continue;
10318
- await walk9(full);
10319
- } else if (entry.isFile() && import_node_path37.default.extname(entry.name) === PROTO_EXTENSION) {
10868
+ await walk10(full);
10869
+ } else if (entry.isFile() && import_node_path38.default.extname(entry.name) === PROTO_EXTENSION) {
10320
10870
  out.push(full);
10321
10871
  }
10322
10872
  }
10323
10873
  }
10324
- await walk9(dir);
10874
+ await walk10(dir);
10325
10875
  return out;
10326
10876
  }
10327
10877
  async function addGrpcMethods(graph, services) {
@@ -10331,10 +10881,10 @@ async function addGrpcMethods(graph, services) {
10331
10881
  const protoPaths = await walkProtoFiles(service.dir, service.excludeDirs);
10332
10882
  for (const protoPath of protoPaths) {
10333
10883
  if (isTestPath(protoPath)) continue;
10334
- const relFile = toPosix(import_node_path37.default.relative(service.dir, protoPath));
10884
+ const relFile = toPosix(import_node_path38.default.relative(service.dir, protoPath));
10335
10885
  let content;
10336
10886
  try {
10337
- content = await import_node_fs25.promises.readFile(protoPath, "utf8");
10887
+ content = await import_node_fs26.promises.readFile(protoPath, "utf8");
10338
10888
  } catch (err) {
10339
10889
  recordExtractionError("proto extraction", protoPath, err);
10340
10890
  continue;
@@ -10389,11 +10939,11 @@ async function addGrpcMethods(graph, services) {
10389
10939
 
10390
10940
  // src/extract/calls/index.ts
10391
10941
  init_cjs_shims();
10392
- var import_types44 = require("@neat.is/types");
10942
+ var import_types45 = require("@neat.is/types");
10393
10943
 
10394
10944
  // src/extract/calls/http.ts
10395
10945
  init_cjs_shims();
10396
- var import_node_path38 = __toESM(require("path"), 1);
10946
+ var import_node_path39 = __toESM(require("path"), 1);
10397
10947
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
10398
10948
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
10399
10949
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
@@ -10476,7 +11026,7 @@ async function addHttpCallEdges(graph, services) {
10476
11026
  const seen = /* @__PURE__ */ new Set();
10477
11027
  for (const file of files) {
10478
11028
  if (isTestPath(file.path)) continue;
10479
- const parser = parserForExt(import_node_path38.default.extname(file.path), parserCache);
11029
+ const parser = parserForExt(import_node_path39.default.extname(file.path), parserCache);
10480
11030
  let sites;
10481
11031
  try {
10482
11032
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -10485,7 +11035,7 @@ async function addHttpCallEdges(graph, services) {
10485
11035
  continue;
10486
11036
  }
10487
11037
  if (sites.length === 0) continue;
10488
- const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
11038
+ const relFile = toPosix(import_node_path39.default.relative(service.dir, file.path));
10489
11039
  for (const site of sites) {
10490
11040
  const targetId = hostToNodeId.get(site.host);
10491
11041
  if (!targetId || targetId === service.node.id) continue;
@@ -10539,7 +11089,7 @@ async function addHttpCallEdges(graph, services) {
10539
11089
 
10540
11090
  // src/extract/calls/route-match.ts
10541
11091
  init_cjs_shims();
10542
- var import_node_path39 = __toESM(require("path"), 1);
11092
+ var import_node_path40 = __toESM(require("path"), 1);
10543
11093
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
10544
11094
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
10545
11095
  var import_types28 = require("@neat.is/types");
@@ -10738,7 +11288,7 @@ async function addRouteCallEdges(graph, services) {
10738
11288
  const seen = /* @__PURE__ */ new Set();
10739
11289
  for (const file of files) {
10740
11290
  if (isTestPath(file.path)) continue;
10741
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path39.default.extname(file.path))) continue;
11291
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path40.default.extname(file.path))) continue;
10742
11292
  let sites;
10743
11293
  try {
10744
11294
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -10747,7 +11297,7 @@ async function addRouteCallEdges(graph, services) {
10747
11297
  continue;
10748
11298
  }
10749
11299
  if (sites.length === 0) continue;
10750
- const relFile = toPosix(import_node_path39.default.relative(service.dir, file.path));
11300
+ const relFile = toPosix(import_node_path40.default.relative(service.dir, file.path));
10751
11301
  for (const site of sites) {
10752
11302
  const serverServiceId = hostToNodeId.get(site.host);
10753
11303
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -10808,7 +11358,7 @@ async function addRouteCallEdges(graph, services) {
10808
11358
 
10809
11359
  // src/extract/calls/kafka.ts
10810
11360
  init_cjs_shims();
10811
- var import_node_path40 = __toESM(require("path"), 1);
11361
+ var import_node_path41 = __toESM(require("path"), 1);
10812
11362
  var import_types29 = require("@neat.is/types");
10813
11363
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
10814
11364
  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 +11446,13 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10896
11446
  // call sites — verified-call-site tier (ADR-066).
10897
11447
  confidenceKind: "verified-call-site",
10898
11448
  evidence: {
10899
- file: import_node_path40.default.relative(serviceDir, file.path),
11449
+ file: import_node_path41.default.relative(serviceDir, file.path),
10900
11450
  line,
10901
11451
  snippet: snippet(file.content, line)
10902
11452
  }
10903
11453
  });
10904
11454
  };
10905
- if (import_node_path40.default.extname(file.path) === ".go") {
11455
+ if (import_node_path41.default.extname(file.path) === ".go") {
10906
11456
  goSaramaEndpoints(file.content, make);
10907
11457
  } else {
10908
11458
  for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
@@ -10913,7 +11463,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
10913
11463
 
10914
11464
  // src/extract/calls/redis.ts
10915
11465
  init_cjs_shims();
10916
- var import_node_path41 = __toESM(require("path"), 1);
11466
+ var import_node_path42 = __toESM(require("path"), 1);
10917
11467
  var import_types30 = require("@neat.is/types");
10918
11468
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10919
11469
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10936,7 +11486,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10936
11486
  // support tier (ADR-066).
10937
11487
  confidenceKind: "url-with-structural-support",
10938
11488
  evidence: {
10939
- file: import_node_path41.default.relative(serviceDir, file.path),
11489
+ file: import_node_path42.default.relative(serviceDir, file.path),
10940
11490
  line,
10941
11491
  snippet: snippet(file.content, line)
10942
11492
  }
@@ -10947,7 +11497,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10947
11497
 
10948
11498
  // src/extract/calls/aws.ts
10949
11499
  init_cjs_shims();
10950
- var import_node_path42 = __toESM(require("path"), 1);
11500
+ var import_node_path43 = __toESM(require("path"), 1);
10951
11501
  var import_types31 = require("@neat.is/types");
10952
11502
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10953
11503
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10981,7 +11531,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10981
11531
  // (ADR-066).
10982
11532
  confidenceKind: "verified-call-site",
10983
11533
  evidence: {
10984
- file: import_node_path42.default.relative(serviceDir, file.path),
11534
+ file: import_node_path43.default.relative(serviceDir, file.path),
10985
11535
  line,
10986
11536
  snippet: snippet(file.content, line)
10987
11537
  }
@@ -11006,7 +11556,7 @@ function awsEndpointsFromFile(file, serviceDir) {
11006
11556
 
11007
11557
  // src/extract/calls/grpc.ts
11008
11558
  init_cjs_shims();
11009
- var import_node_path43 = __toESM(require("path"), 1);
11559
+ var import_node_path44 = __toESM(require("path"), 1);
11010
11560
  var import_types32 = require("@neat.is/types");
11011
11561
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
11012
11562
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -11065,7 +11615,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11065
11615
  // tier (ADR-066).
11066
11616
  confidenceKind: "verified-call-site",
11067
11617
  evidence: {
11068
- file: import_node_path43.default.relative(serviceDir, file.path),
11618
+ file: import_node_path44.default.relative(serviceDir, file.path),
11069
11619
  line,
11070
11620
  snippet: snippet(file.content, line)
11071
11621
  }
@@ -11076,7 +11626,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11076
11626
 
11077
11627
  // src/extract/calls/supabase.ts
11078
11628
  init_cjs_shims();
11079
- var import_node_path44 = __toESM(require("path"), 1);
11629
+ var import_node_path45 = __toESM(require("path"), 1);
11080
11630
  var import_types33 = require("@neat.is/types");
11081
11631
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
11082
11632
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -11135,7 +11685,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11135
11685
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
11136
11686
  confidenceKind: "verified-call-site",
11137
11687
  evidence: {
11138
- file: import_node_path44.default.relative(serviceDir, file.path),
11688
+ file: import_node_path45.default.relative(serviceDir, file.path),
11139
11689
  line,
11140
11690
  snippet: snippet(file.content, line)
11141
11691
  }
@@ -11162,7 +11712,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11162
11712
  edgeType: "CALLS",
11163
11713
  confidenceKind: "verified-call-site",
11164
11714
  evidence: {
11165
- file: import_node_path44.default.relative(serviceDir, file.path),
11715
+ file: import_node_path45.default.relative(serviceDir, file.path),
11166
11716
  line,
11167
11717
  snippet: snippet(file.content, line)
11168
11718
  }
@@ -11174,7 +11724,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11174
11724
 
11175
11725
  // src/extract/calls/firestore.ts
11176
11726
  init_cjs_shims();
11177
- var import_node_path45 = __toESM(require("path"), 1);
11727
+ var import_node_path46 = __toESM(require("path"), 1);
11178
11728
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
11179
11729
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
11180
11730
  var import_types34 = require("@neat.is/types");
@@ -11213,7 +11763,7 @@ function isFirestoreClientFactory(node) {
11213
11763
  }
11214
11764
  function firestoreClientVars(root) {
11215
11765
  const vars = /* @__PURE__ */ new Set();
11216
- const walk9 = (node) => {
11766
+ const walk10 = (node) => {
11217
11767
  if (node.type === "variable_declarator") {
11218
11768
  const name = node.childForFieldName("name");
11219
11769
  let value = node.childForFieldName("value");
@@ -11222,9 +11772,9 @@ function firestoreClientVars(root) {
11222
11772
  vars.add(name.text);
11223
11773
  }
11224
11774
  }
11225
- for (const c of namedChildren(node)) walk9(c);
11775
+ for (const c of namedChildren(node)) walk10(c);
11226
11776
  };
11227
- walk9(root);
11777
+ walk10(root);
11228
11778
  return vars;
11229
11779
  }
11230
11780
  function isClientExpr(node, clientVars) {
@@ -11345,7 +11895,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11345
11895
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
11346
11896
  if (!hasClient && !hasAdmin) return [];
11347
11897
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11348
- const tree = parseSource3(parserForExt2(import_node_path45.default.extname(file.path)), file.content);
11898
+ const tree = parseSource3(parserForExt2(import_node_path46.default.extname(file.path)), file.content);
11349
11899
  const clientVars = firestoreClientVars(tree.rootNode);
11350
11900
  const collLine = /* @__PURE__ */ new Map();
11351
11901
  const writes = /* @__PURE__ */ new Map();
@@ -11379,7 +11929,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11379
11929
  }
11380
11930
  s.add(field);
11381
11931
  };
11382
- const walk9 = (node) => {
11932
+ const walk10 = (node) => {
11383
11933
  if (node.type === "call_expression") {
11384
11934
  const fn = node.childForFieldName("function");
11385
11935
  const line = node.startPosition.row + 1;
@@ -11419,9 +11969,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11419
11969
  }
11420
11970
  }
11421
11971
  }
11422
- for (const c of namedChildren(node)) walk9(c);
11972
+ for (const c of namedChildren(node)) walk10(c);
11423
11973
  };
11424
- walk9(tree.rootNode);
11974
+ walk10(tree.rootNode);
11425
11975
  const out = [];
11426
11976
  for (const [collPath, line] of collLine) {
11427
11977
  const byField = writes.get(collPath);
@@ -11449,7 +11999,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11449
11999
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
11450
12000
  ...sdkWrites ? { sdkWrites } : {},
11451
12001
  evidence: {
11452
- file: import_node_path45.default.relative(serviceDir, file.path),
12002
+ file: import_node_path46.default.relative(serviceDir, file.path),
11453
12003
  line,
11454
12004
  snippet: snippet(file.content, line)
11455
12005
  }
@@ -11460,7 +12010,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11460
12010
 
11461
12011
  // src/extract/calls/mongoose.ts
11462
12012
  init_cjs_shims();
11463
- var import_node_path46 = __toESM(require("path"), 1);
12013
+ var import_node_path47 = __toESM(require("path"), 1);
11464
12014
  var import_types35 = require("@neat.is/types");
11465
12015
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
11466
12016
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -11611,7 +12161,7 @@ function endpoint(r, file, serviceDir, matchText) {
11611
12161
  kind: r.kind,
11612
12162
  edgeType: "CALLS",
11613
12163
  confidenceKind: "verified-call-site",
11614
- evidence: { file: import_node_path46.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12164
+ evidence: { file: import_node_path47.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11615
12165
  };
11616
12166
  }
11617
12167
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11701,7 +12251,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11701
12251
  const registry = /* @__PURE__ */ new Map();
11702
12252
  for (const f of mongooseFiles) {
11703
12253
  const fx = fileExportsOf(f.content, pluralizeOn);
11704
- if (fx) registry.set(toPosix(import_node_path46.default.relative(serviceDir, f.path)), fx);
12254
+ if (fx) registry.set(toPosix(import_node_path47.default.relative(serviceDir, f.path)), fx);
11705
12255
  }
11706
12256
  if (registry.size === 0) return [];
11707
12257
  const out = [];
@@ -11712,7 +12262,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11712
12262
  const directColl = /* @__PURE__ */ new Map();
11713
12263
  const nsExports = /* @__PURE__ */ new Map();
11714
12264
  for (const b of bindings) {
11715
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path46.default.dirname(f.path), serviceDir, null);
12265
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path47.default.dirname(f.path), serviceDir, null);
11716
12266
  if (!resolvedRel) continue;
11717
12267
  const fx = registry.get(resolvedRel);
11718
12268
  if (!fx) continue;
@@ -11756,7 +12306,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11756
12306
 
11757
12307
  // src/extract/calls/sqlalchemy.ts
11758
12308
  init_cjs_shims();
11759
- var import_node_path47 = __toESM(require("path"), 1);
12309
+ var import_node_path48 = __toESM(require("path"), 1);
11760
12310
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11761
12311
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11762
12312
  var import_types36 = require("@neat.is/types");
@@ -11895,7 +12445,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11895
12445
  childTable,
11896
12446
  parentTable,
11897
12447
  evidence: {
11898
- file: import_node_path47.default.relative(serviceDir, file.path),
12448
+ file: import_node_path48.default.relative(serviceDir, file.path),
11899
12449
  line,
11900
12450
  snippet: snippet(file.content, line)
11901
12451
  }
@@ -11920,7 +12470,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11920
12470
  confidenceKind: "verified-call-site",
11921
12471
  ...columns && columns.length > 0 ? { columns } : {},
11922
12472
  evidence: {
11923
- file: import_node_path47.default.relative(serviceDir, file.path),
12473
+ file: import_node_path48.default.relative(serviceDir, file.path),
11924
12474
  line,
11925
12475
  snippet: snippet(file.content, line)
11926
12476
  }
@@ -12027,7 +12577,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12027
12577
  edgeType: "CALLS",
12028
12578
  confidenceKind: "verified-call-site",
12029
12579
  evidence: {
12030
- file: import_node_path47.default.relative(serviceDir, file.path),
12580
+ file: import_node_path48.default.relative(serviceDir, file.path),
12031
12581
  line,
12032
12582
  snippet: snippet(file.content, line)
12033
12583
  }
@@ -12039,7 +12589,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
12039
12589
 
12040
12590
  // src/extract/calls/django-orm.ts
12041
12591
  init_cjs_shims();
12042
- var import_node_path48 = __toESM(require("path"), 1);
12592
+ var import_node_path49 = __toESM(require("path"), 1);
12043
12593
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
12044
12594
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
12045
12595
  var import_types37 = require("@neat.is/types");
@@ -12109,7 +12659,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12109
12659
  const tree = parseSource7(makePyParser4(), file.content);
12110
12660
  const out = [];
12111
12661
  const seen = /* @__PURE__ */ new Set();
12112
- const defaultAppLabel = import_node_path48.default.basename(import_node_path48.default.dirname(file.path));
12662
+ const defaultAppLabel = import_node_path49.default.basename(import_node_path49.default.dirname(file.path));
12113
12663
  walk4(tree.rootNode, (node) => {
12114
12664
  if (node.type !== "class_definition") return;
12115
12665
  if (!extendsDjangoModel(node)) return;
@@ -12127,7 +12677,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12127
12677
  kind: "sql-table",
12128
12678
  edgeType: "CALLS",
12129
12679
  confidenceKind: "verified-call-site",
12130
- evidence: { file: import_node_path48.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12680
+ evidence: { file: import_node_path49.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
12131
12681
  });
12132
12682
  });
12133
12683
  return out;
@@ -12135,7 +12685,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12135
12685
 
12136
12686
  // src/extract/calls/drizzle.ts
12137
12687
  init_cjs_shims();
12138
- var import_node_path49 = __toESM(require("path"), 1);
12688
+ var import_node_path50 = __toESM(require("path"), 1);
12139
12689
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12140
12690
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12141
12691
  var import_types38 = require("@neat.is/types");
@@ -12213,10 +12763,10 @@ function columnsFromObject(obj) {
12213
12763
  }
12214
12764
  function drizzleEndpointsFromFile(file, serviceDir) {
12215
12765
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12216
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12766
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12217
12767
  const out = [];
12218
12768
  const seen = /* @__PURE__ */ new Set();
12219
- const walk9 = (node) => {
12769
+ const walk10 = (node) => {
12220
12770
  if (node.type === "call_expression") {
12221
12771
  const fn = node.childForFieldName("function");
12222
12772
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -12236,7 +12786,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12236
12786
  confidenceKind: "structural",
12237
12787
  columns,
12238
12788
  evidence: {
12239
- file: import_node_path49.default.relative(serviceDir, file.path),
12789
+ file: import_node_path50.default.relative(serviceDir, file.path),
12240
12790
  line,
12241
12791
  snippet: snippet(file.content, line)
12242
12792
  }
@@ -12244,9 +12794,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12244
12794
  }
12245
12795
  }
12246
12796
  }
12247
- for (const c of namedChildren4(node)) walk9(c);
12797
+ for (const c of namedChildren4(node)) walk10(c);
12248
12798
  };
12249
- walk9(tree.rootNode);
12799
+ walk10(tree.rootNode);
12250
12800
  return out;
12251
12801
  }
12252
12802
  function enclosingVarName(call) {
@@ -12268,7 +12818,7 @@ function enclosingVarName(call) {
12268
12818
  function collectDrizzleTables(root) {
12269
12819
  const tables = [];
12270
12820
  const varToTable = /* @__PURE__ */ new Map();
12271
- const walk9 = (node) => {
12821
+ const walk10 = (node) => {
12272
12822
  if (node.type === "call_expression") {
12273
12823
  const fn = node.childForFieldName("function");
12274
12824
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -12283,9 +12833,9 @@ function collectDrizzleTables(root) {
12283
12833
  }
12284
12834
  }
12285
12835
  }
12286
- for (const c of namedChildren4(node)) walk9(c);
12836
+ for (const c of namedChildren4(node)) walk10(c);
12287
12837
  };
12288
- walk9(root);
12838
+ walk10(root);
12289
12839
  return { tables, varToTable };
12290
12840
  }
12291
12841
  function referencesTargetVar(call) {
@@ -12302,13 +12852,13 @@ function referencesTargetVar(call) {
12302
12852
  }
12303
12853
  function drizzleForeignKeys(file, serviceDir) {
12304
12854
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12305
- const tree = parseSource3(parserForExt3(import_node_path49.default.extname(file.path)), file.content);
12855
+ const tree = parseSource3(parserForExt3(import_node_path50.default.extname(file.path)), file.content);
12306
12856
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
12307
12857
  const out = [];
12308
12858
  const seen = /* @__PURE__ */ new Set();
12309
12859
  for (const table of tables) {
12310
12860
  if (!table.object) continue;
12311
- const walk9 = (node) => {
12861
+ const walk10 = (node) => {
12312
12862
  if (node.type === "call_expression") {
12313
12863
  const targetVar = referencesTargetVar(node);
12314
12864
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -12321,7 +12871,7 @@ function drizzleForeignKeys(file, serviceDir) {
12321
12871
  childTable: table.tableName,
12322
12872
  parentTable,
12323
12873
  evidence: {
12324
- file: import_node_path49.default.relative(serviceDir, file.path),
12874
+ file: import_node_path50.default.relative(serviceDir, file.path),
12325
12875
  line,
12326
12876
  snippet: snippet(file.content, line)
12327
12877
  }
@@ -12329,16 +12879,16 @@ function drizzleForeignKeys(file, serviceDir) {
12329
12879
  }
12330
12880
  }
12331
12881
  }
12332
- for (const c of namedChildren4(node)) walk9(c);
12882
+ for (const c of namedChildren4(node)) walk10(c);
12333
12883
  };
12334
- walk9(table.object);
12884
+ walk10(table.object);
12335
12885
  }
12336
12886
  return out;
12337
12887
  }
12338
12888
 
12339
12889
  // src/extract/calls/prisma.ts
12340
12890
  init_cjs_shims();
12341
- var import_node_path50 = __toESM(require("path"), 1);
12891
+ var import_node_path51 = __toESM(require("path"), 1);
12342
12892
  var import_types39 = require("@neat.is/types");
12343
12893
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
12344
12894
  "Int",
@@ -12394,7 +12944,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12394
12944
  confidenceKind: "structural",
12395
12945
  columns: b.columns,
12396
12946
  evidence: {
12397
- file: import_node_path50.default.relative(serviceDir, file.path),
12947
+ file: import_node_path51.default.relative(serviceDir, file.path),
12398
12948
  line: b.startLine,
12399
12949
  snippet: snippet(content, b.startLine)
12400
12950
  }
@@ -12455,7 +13005,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12455
13005
  }
12456
13006
  async function prismaColumnEndpoints(serviceDir) {
12457
13007
  const schemaPath = await findFirst(serviceDir, [
12458
- import_node_path50.default.join("prisma", "schema.prisma"),
13008
+ import_node_path51.default.join("prisma", "schema.prisma"),
12459
13009
  "schema.prisma"
12460
13010
  ]);
12461
13011
  if (!schemaPath) return [];
@@ -12530,7 +13080,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12530
13080
  childTable: current.table,
12531
13081
  parentTable,
12532
13082
  evidence: {
12533
- file: import_node_path50.default.relative(serviceDir, file.path),
13083
+ file: import_node_path51.default.relative(serviceDir, file.path),
12534
13084
  line: lineNo,
12535
13085
  snippet: snippet(content, lineNo)
12536
13086
  }
@@ -12545,7 +13095,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12545
13095
  }
12546
13096
  async function prismaForeignKeys(serviceDir) {
12547
13097
  const schemaPath = await findFirst(serviceDir, [
12548
- import_node_path50.default.join("prisma", "schema.prisma"),
13098
+ import_node_path51.default.join("prisma", "schema.prisma"),
12549
13099
  "schema.prisma"
12550
13100
  ]);
12551
13101
  if (!schemaPath) return [];
@@ -12556,7 +13106,7 @@ async function prismaForeignKeys(serviceDir) {
12556
13106
 
12557
13107
  // src/extract/calls/activerecord.ts
12558
13108
  init_cjs_shims();
12559
- var import_node_path51 = __toESM(require("path"), 1);
13109
+ var import_node_path52 = __toESM(require("path"), 1);
12560
13110
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12561
13111
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
12562
13112
  var import_types40 = require("@neat.is/types");
@@ -12764,7 +13314,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12764
13314
  confidenceKind: "structural",
12765
13315
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12766
13316
  evidence: {
12767
- file: import_node_path51.default.relative(serviceDir, file.path),
13317
+ file: import_node_path52.default.relative(serviceDir, file.path),
12768
13318
  line: table.line,
12769
13319
  snippet: snippet(file.content, table.line)
12770
13320
  }
@@ -12786,7 +13336,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12786
13336
  childTable,
12787
13337
  parentTable,
12788
13338
  evidence: {
12789
- file: import_node_path51.default.relative(serviceDir, file.path),
13339
+ file: import_node_path52.default.relative(serviceDir, file.path),
12790
13340
  line,
12791
13341
  snippet: snippet(file.content, line)
12792
13342
  }
@@ -12869,7 +13419,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12869
13419
  edgeType: "CALLS",
12870
13420
  confidenceKind: "verified-call-site",
12871
13421
  evidence: {
12872
- file: import_node_path51.default.relative(serviceDir, file.path),
13422
+ file: import_node_path52.default.relative(serviceDir, file.path),
12873
13423
  line,
12874
13424
  snippet: snippet(file.content, line)
12875
13425
  }
@@ -12906,7 +13456,7 @@ function railsModelForeignKeys(file, serviceDir) {
12906
13456
  childTable,
12907
13457
  parentTable,
12908
13458
  evidence: {
12909
- file: import_node_path51.default.relative(serviceDir, file.path),
13459
+ file: import_node_path52.default.relative(serviceDir, file.path),
12910
13460
  line,
12911
13461
  snippet: snippet(file.content, line)
12912
13462
  }
@@ -12918,7 +13468,7 @@ function railsModelForeignKeys(file, serviceDir) {
12918
13468
 
12919
13469
  // src/extract/calls/eloquent.ts
12920
13470
  init_cjs_shims();
12921
- var import_node_path52 = __toESM(require("path"), 1);
13471
+ var import_node_path53 = __toESM(require("path"), 1);
12922
13472
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12923
13473
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12924
13474
  var import_types41 = require("@neat.is/types");
@@ -13175,7 +13725,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13175
13725
  confidenceKind: "structural",
13176
13726
  ...columns.length > 0 ? { columns } : {},
13177
13727
  evidence: {
13178
- file: import_node_path52.default.relative(serviceDir, file.path),
13728
+ file: import_node_path53.default.relative(serviceDir, file.path),
13179
13729
  line: bp.line,
13180
13730
  snippet: snippet(file.content, bp.line)
13181
13731
  }
@@ -13197,7 +13747,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13197
13747
  childTable,
13198
13748
  parentTable,
13199
13749
  evidence: {
13200
- file: import_node_path52.default.relative(serviceDir, file.path),
13750
+ file: import_node_path53.default.relative(serviceDir, file.path),
13201
13751
  line,
13202
13752
  snippet: snippet(file.content, line)
13203
13753
  }
@@ -13317,7 +13867,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
13317
13867
  edgeType: "CALLS",
13318
13868
  confidenceKind: "verified-call-site",
13319
13869
  evidence: {
13320
- file: import_node_path52.default.relative(serviceDir, file.path),
13870
+ file: import_node_path53.default.relative(serviceDir, file.path),
13321
13871
  line,
13322
13872
  snippet: snippet(file.content, line)
13323
13873
  }
@@ -13353,7 +13903,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13353
13903
  childTable,
13354
13904
  parentTable,
13355
13905
  evidence: {
13356
- file: import_node_path52.default.relative(serviceDir, file.path),
13906
+ file: import_node_path53.default.relative(serviceDir, file.path),
13357
13907
  line,
13358
13908
  snippet: snippet(file.content, line)
13359
13909
  }
@@ -13365,7 +13915,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13365
13915
 
13366
13916
  // src/extract/calls/go.ts
13367
13917
  init_cjs_shims();
13368
- var import_node_path55 = __toESM(require("path"), 1);
13918
+ var import_node_path56 = __toESM(require("path"), 1);
13369
13919
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13370
13920
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
13371
13921
  var import_types42 = require("@neat.is/types");
@@ -13440,7 +13990,7 @@ function firstStringLiteralArg(argsNode) {
13440
13990
  return null;
13441
13991
  }
13442
13992
  function goSqlEndpointsFromFile(file, serviceDir) {
13443
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
13993
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
13444
13994
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
13445
13995
  const tree = parseSource10(makeGoParser3(), file.content);
13446
13996
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -13469,7 +14019,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13469
14019
  confidenceKind: "verified-call-site",
13470
14020
  ...columns.length > 0 ? { columns } : {},
13471
14021
  evidence: {
13472
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14022
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
13473
14023
  line,
13474
14024
  snippet: snippet(file.content, line)
13475
14025
  }
@@ -13480,7 +14030,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13480
14030
 
13481
14031
  // src/extract/calls/gorm.ts
13482
14032
  init_cjs_shims();
13483
- var import_node_path56 = __toESM(require("path"), 1);
14033
+ var import_node_path57 = __toESM(require("path"), 1);
13484
14034
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13485
14035
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
13486
14036
  var import_types43 = require("@neat.is/types");
@@ -13913,7 +14463,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13913
14463
  seen.delete(struct.name);
13914
14464
  }
13915
14465
  function gormEndpointsFromFile(file, serviceDir) {
13916
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14466
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
13917
14467
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13918
14468
  const tree = parseSource11(makeGoParser4(), file.content);
13919
14469
  const { structs, models, tableFor } = analyze(tree);
@@ -13935,7 +14485,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13935
14485
  confidenceKind: "structural",
13936
14486
  ...columns.length > 0 ? { columns } : {},
13937
14487
  evidence: {
13938
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14488
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
13939
14489
  line: struct.line,
13940
14490
  snippet: snippet(file.content, struct.line)
13941
14491
  }
@@ -13944,7 +14494,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13944
14494
  return out;
13945
14495
  }
13946
14496
  function gormForeignKeys(file, serviceDir) {
13947
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14497
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
13948
14498
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13949
14499
  const tree = parseSource11(makeGoParser4(), file.content);
13950
14500
  const { structs, models, tableFor } = analyze(tree);
@@ -13959,7 +14509,7 @@ function gormForeignKeys(file, serviceDir) {
13959
14509
  childTable,
13960
14510
  parentTable,
13961
14511
  evidence: {
13962
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14512
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
13963
14513
  line,
13964
14514
  snippet: snippet(file.content, line)
13965
14515
  }
@@ -13994,15 +14544,131 @@ function gormForeignKeys(file, serviceDir) {
13994
14544
  return out;
13995
14545
  }
13996
14546
 
14547
+ // src/extract/calls/efcore.ts
14548
+ init_cjs_shims();
14549
+ var import_node_path58 = __toESM(require("path"), 1);
14550
+ var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
14551
+ var import_tree_sitter_c_sharp2 = __toESM(require("tree-sitter-c-sharp"), 1);
14552
+ var import_types44 = require("@neat.is/types");
14553
+ var EFCORE_GATE = /Microsoft\.EntityFrameworkCore|DataAnnotations\.Schema|\bDbContext\b|\bDbSet\s*</;
14554
+ var PARSE_CHUNK12 = 16384;
14555
+ function makeCsParser() {
14556
+ const p = new import_tree_sitter16.default();
14557
+ p.setLanguage(import_tree_sitter_c_sharp2.default);
14558
+ return p;
14559
+ }
14560
+ function parseSource12(parser, source) {
14561
+ return parser.parse(
14562
+ (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK12)
14563
+ );
14564
+ }
14565
+ function walk9(node, visit) {
14566
+ visit(node);
14567
+ for (let i = 0; i < node.namedChildCount; i++) {
14568
+ const c = node.namedChild(i);
14569
+ if (c) walk9(c, visit);
14570
+ }
14571
+ }
14572
+ function firstChildOfType(node, type) {
14573
+ for (let i = 0; i < node.namedChildCount; i++) {
14574
+ const c = node.namedChild(i);
14575
+ if (c?.type === type) return c;
14576
+ }
14577
+ return null;
14578
+ }
14579
+ function csStringLiteral(node) {
14580
+ if (node.type === "string_literal") {
14581
+ let out = "";
14582
+ for (let i = 0; i < node.namedChildCount; i++) {
14583
+ const c = node.namedChild(i);
14584
+ if (c?.type === "string_literal_content") out += c.text;
14585
+ }
14586
+ return out;
14587
+ }
14588
+ if (node.type === "verbatim_string_literal") {
14589
+ const t = node.text;
14590
+ return t.length >= 3 ? t.slice(2, -1).replace(/""/g, '"') : "";
14591
+ }
14592
+ return null;
14593
+ }
14594
+ function attributeName(attr) {
14595
+ const nameNode = attr.childForFieldName("name") ?? attr.namedChild(0);
14596
+ if (!nameNode) return null;
14597
+ const text = nameNode.text;
14598
+ const base = text.includes(".") ? text.slice(text.lastIndexOf(".") + 1) : text;
14599
+ return base.endsWith("Attribute") ? base.slice(0, -"Attribute".length) : base;
14600
+ }
14601
+ function tableFromAttribute(attr) {
14602
+ if (attributeName(attr) !== "Table") return null;
14603
+ const args = attr.childForFieldName("arguments") ?? firstChildOfType(attr, "attribute_argument_list");
14604
+ if (!args) return null;
14605
+ for (let i = 0; i < args.namedChildCount; i++) {
14606
+ const arg = args.namedChild(i);
14607
+ if (arg?.type !== "attribute_argument") continue;
14608
+ const first = arg.namedChild(0);
14609
+ if (!first) continue;
14610
+ const value = csStringLiteral(first);
14611
+ if (value !== null) return value;
14612
+ return null;
14613
+ }
14614
+ return null;
14615
+ }
14616
+ function tableFromToTable(call) {
14617
+ const fn = call.childForFieldName("function");
14618
+ if (fn?.type !== "member_access_expression") return null;
14619
+ const method = fn.childForFieldName("name") ?? fn.namedChild(fn.namedChildCount - 1);
14620
+ if (method?.text !== "ToTable") return null;
14621
+ const args = call.childForFieldName("arguments");
14622
+ const firstArg2 = args?.namedChild(0);
14623
+ if (firstArg2?.type !== "argument") return null;
14624
+ const value = firstArg2.namedChild(0);
14625
+ return value ? csStringLiteral(value) : null;
14626
+ }
14627
+ function efcoreEndpointsFromFile(file, serviceDir) {
14628
+ if (import_node_path58.default.extname(file.path) !== ".cs") return [];
14629
+ if (!EFCORE_GATE.test(file.content)) return [];
14630
+ const tree = parseSource12(makeCsParser(), file.content);
14631
+ const out = [];
14632
+ const seen = /* @__PURE__ */ new Set();
14633
+ const push = (name, line) => {
14634
+ if (!name || seen.has(name)) return;
14635
+ seen.add(name);
14636
+ out.push({
14637
+ infraId: (0, import_types44.infraId)("sql-table", name),
14638
+ name,
14639
+ kind: "sql-table",
14640
+ edgeType: "CALLS",
14641
+ confidenceKind: "structural",
14642
+ evidence: {
14643
+ file: toPosix(import_node_path58.default.relative(serviceDir, file.path)),
14644
+ line,
14645
+ snippet: snippet(file.content, line)
14646
+ }
14647
+ });
14648
+ };
14649
+ walk9(tree.rootNode, (node) => {
14650
+ if (node.type === "attribute") {
14651
+ const table = tableFromAttribute(node);
14652
+ if (table) push(table, node.startPosition.row + 1);
14653
+ return;
14654
+ }
14655
+ if (node.type === "invocation_expression") {
14656
+ const table = tableFromToTable(node);
14657
+ if (table) push(table, node.startPosition.row + 1);
14658
+ }
14659
+ });
14660
+ return out;
14661
+ }
14662
+
13997
14663
  // src/extract/calls/index.ts
13998
14664
  function edgeTypeFromEndpoint(ep) {
13999
14665
  switch (ep.edgeType) {
14000
14666
  case "PUBLISHES_TO":
14001
- return import_types44.EdgeType.PUBLISHES_TO;
14667
+ return import_types45.EdgeType.PUBLISHES_TO;
14002
14668
  case "CONSUMES_FROM":
14003
- return import_types44.EdgeType.CONSUMES_FROM;
14669
+ return import_types45.EdgeType.CONSUMES_FROM;
14004
14670
  default:
14005
- return import_types44.EdgeType.CALLS;
14671
+ return import_types45.EdgeType.CALLS;
14006
14672
  }
14007
14673
  }
14008
14674
  function isAwsKind(kind) {
@@ -14052,6 +14718,11 @@ async function addExternalEndpointEdges(graph, services) {
14052
14718
  } catch (err) {
14053
14719
  recordExtractionError("laravel eloquent extraction", file.path, err);
14054
14720
  }
14721
+ try {
14722
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14723
+ } catch (err) {
14724
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14725
+ }
14055
14726
  }
14056
14727
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
14057
14728
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -14062,7 +14733,7 @@ async function addExternalEndpointEdges(graph, services) {
14062
14733
  if (!graph.hasNode(ep.infraId)) {
14063
14734
  const node = {
14064
14735
  id: ep.infraId,
14065
- type: import_types44.NodeType.InfraNode,
14736
+ type: import_types45.NodeType.InfraNode,
14066
14737
  name: ep.name,
14067
14738
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
14068
14739
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -14075,21 +14746,21 @@ async function addExternalEndpointEdges(graph, services) {
14075
14746
  }
14076
14747
  if (ep.columns && ep.columns.length > 0) {
14077
14748
  const node = graph.getNodeAttributes(ep.infraId);
14078
- if (node.type === import_types44.NodeType.InfraNode) {
14749
+ if (node.type === import_types45.NodeType.InfraNode) {
14079
14750
  graph.replaceNodeAttributes(ep.infraId, {
14080
14751
  ...node,
14081
14752
  columns: foldColumns(
14082
14753
  node.columns,
14083
14754
  ep.columns,
14084
- import_types44.Provenance.EXTRACTED,
14085
- (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
14755
+ import_types45.Provenance.EXTRACTED,
14756
+ (0, import_types45.confidenceForExtracted)(ep.confidenceKind)
14086
14757
  )
14087
14758
  });
14088
14759
  }
14089
14760
  }
14090
14761
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
14091
14762
  const node = graph.getNodeAttributes(ep.infraId);
14092
- if (node.type === import_types44.NodeType.InfraNode) {
14763
+ if (node.type === import_types45.NodeType.InfraNode) {
14093
14764
  graph.replaceNodeAttributes(ep.infraId, {
14094
14765
  ...node,
14095
14766
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -14097,7 +14768,7 @@ async function addExternalEndpointEdges(graph, services) {
14097
14768
  }
14098
14769
  }
14099
14770
  const edgeType = edgeTypeFromEndpoint(ep);
14100
- const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
14771
+ const confidence = (0, import_types45.confidenceForExtracted)(ep.confidenceKind);
14101
14772
  const relFile = toPosix(ep.evidence.file);
14102
14773
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
14103
14774
  graph,
@@ -14107,7 +14778,7 @@ async function addExternalEndpointEdges(graph, services) {
14107
14778
  );
14108
14779
  nodesAdded += n;
14109
14780
  edgesAdded += e;
14110
- if (!(0, import_types44.passesExtractedFloor)(confidence)) {
14781
+ if (!(0, import_types45.passesExtractedFloor)(confidence)) {
14111
14782
  noteExtractedDropped({
14112
14783
  source: fileNodeId,
14113
14784
  target: ep.infraId,
@@ -14127,7 +14798,7 @@ async function addExternalEndpointEdges(graph, services) {
14127
14798
  source: fileNodeId,
14128
14799
  target: ep.infraId,
14129
14800
  type: edgeType,
14130
- provenance: import_types44.Provenance.EXTRACTED,
14801
+ provenance: import_types45.Provenance.EXTRACTED,
14131
14802
  confidence,
14132
14803
  evidence: ep.evidence
14133
14804
  };
@@ -14150,7 +14821,7 @@ async function addCallEdges(graph, services) {
14150
14821
 
14151
14822
  // src/extract/table-edges.ts
14152
14823
  init_cjs_shims();
14153
- var import_types45 = require("@neat.is/types");
14824
+ var import_types46 = require("@neat.is/types");
14154
14825
  async function addTableEdges(graph, services) {
14155
14826
  let nodesAdded = 0;
14156
14827
  let edgesAdded = 0;
@@ -14178,20 +14849,20 @@ async function addTableEdges(graph, services) {
14178
14849
  }
14179
14850
  refs.push(...modelRefs);
14180
14851
  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);
14852
+ const childId = (0, import_types46.infraId)("sql-table", ref.childTable);
14853
+ const parentId = (0, import_types46.infraId)("sql-table", ref.parentTable);
14183
14854
  if (childId === parentId) continue;
14184
14855
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
14185
14856
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
14186
- const edgeId = (0, import_types45.extractedEdgeId)(childId, parentId, import_types45.EdgeType.REFERENCES);
14857
+ const edgeId = (0, import_types46.extractedEdgeId)(childId, parentId, import_types46.EdgeType.REFERENCES);
14187
14858
  if (graph.hasEdge(edgeId)) continue;
14188
14859
  const edge = {
14189
14860
  id: edgeId,
14190
14861
  source: childId,
14191
14862
  target: parentId,
14192
- type: import_types45.EdgeType.REFERENCES,
14193
- provenance: import_types45.Provenance.EXTRACTED,
14194
- confidence: (0, import_types45.confidenceForExtracted)("structural"),
14863
+ type: import_types46.EdgeType.REFERENCES,
14864
+ provenance: import_types46.Provenance.EXTRACTED,
14865
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
14195
14866
  evidence: ref.evidence
14196
14867
  };
14197
14868
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -14204,7 +14875,7 @@ function ensureTableNode(graph, id, name) {
14204
14875
  if (graph.hasNode(id)) return 0;
14205
14876
  const node = {
14206
14877
  id,
14207
- type: import_types45.NodeType.InfraNode,
14878
+ type: import_types46.NodeType.InfraNode,
14208
14879
  name,
14209
14880
  provider: "self",
14210
14881
  kind: "sql-table"
@@ -14218,16 +14889,16 @@ init_cjs_shims();
14218
14889
 
14219
14890
  // src/extract/infra/docker-compose.ts
14220
14891
  init_cjs_shims();
14221
- var import_node_path57 = __toESM(require("path"), 1);
14222
- var import_types47 = require("@neat.is/types");
14892
+ var import_node_path59 = __toESM(require("path"), 1);
14893
+ var import_types48 = require("@neat.is/types");
14223
14894
 
14224
14895
  // src/extract/infra/shared.ts
14225
14896
  init_cjs_shims();
14226
- var import_types46 = require("@neat.is/types");
14897
+ var import_types47 = require("@neat.is/types");
14227
14898
  function makeInfraNode(kind, name, provider = "self", extras) {
14228
14899
  return {
14229
- id: (0, import_types46.infraId)(kind, name),
14230
- type: import_types46.NodeType.InfraNode,
14900
+ id: (0, import_types47.infraId)(kind, name),
14901
+ type: import_types47.NodeType.InfraNode,
14231
14902
  name,
14232
14903
  provider,
14233
14904
  kind,
@@ -14271,8 +14942,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
14271
14942
  source: anchorId,
14272
14943
  target: node.id,
14273
14944
  type: edgeType,
14274
- provenance: import_types46.Provenance.EXTRACTED,
14275
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14945
+ provenance: import_types47.Provenance.EXTRACTED,
14946
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
14276
14947
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14277
14948
  };
14278
14949
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14289,7 +14960,7 @@ function dependsOnList(value) {
14289
14960
  }
14290
14961
  function serviceNameToServiceNode(name, services) {
14291
14962
  for (const s of services) {
14292
- if (s.node.name === name || import_node_path57.default.basename(s.dir) === name) return s.node.id;
14963
+ if (s.node.name === name || import_node_path59.default.basename(s.dir) === name) return s.node.id;
14293
14964
  }
14294
14965
  return null;
14295
14966
  }
@@ -14298,7 +14969,7 @@ async function addComposeInfra(graph, scanPath, services) {
14298
14969
  let edgesAdded = 0;
14299
14970
  let composePath = null;
14300
14971
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
14301
- const abs = import_node_path57.default.join(scanPath, name);
14972
+ const abs = import_node_path59.default.join(scanPath, name);
14302
14973
  if (await exists2(abs)) {
14303
14974
  composePath = abs;
14304
14975
  break;
@@ -14311,13 +14982,13 @@ async function addComposeInfra(graph, scanPath, services) {
14311
14982
  } catch (err) {
14312
14983
  recordExtractionError(
14313
14984
  "infra docker-compose",
14314
- import_node_path57.default.relative(scanPath, composePath),
14985
+ import_node_path59.default.relative(scanPath, composePath),
14315
14986
  err
14316
14987
  );
14317
14988
  return { nodesAdded, edgesAdded };
14318
14989
  }
14319
14990
  if (!compose?.services) return { nodesAdded, edgesAdded };
14320
- const evidenceFile = import_node_path57.default.relative(scanPath, composePath).split(import_node_path57.default.sep).join("/");
14991
+ const evidenceFile = import_node_path59.default.relative(scanPath, composePath).split(import_node_path59.default.sep).join("/");
14321
14992
  const composeNameToNodeId = /* @__PURE__ */ new Map();
14322
14993
  for (const [composeName, svc] of Object.entries(compose.services)) {
14323
14994
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -14339,15 +15010,15 @@ async function addComposeInfra(graph, scanPath, services) {
14339
15010
  for (const dep of dependsOnList(svc.depends_on)) {
14340
15011
  const targetId = composeNameToNodeId.get(dep);
14341
15012
  if (!targetId) continue;
14342
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types47.EdgeType.DEPENDS_ON);
15013
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types48.EdgeType.DEPENDS_ON);
14343
15014
  if (graph.hasEdge(edgeId)) continue;
14344
15015
  const edge = {
14345
15016
  id: edgeId,
14346
15017
  source: sourceId,
14347
15018
  target: targetId,
14348
- type: import_types47.EdgeType.DEPENDS_ON,
14349
- provenance: import_types47.Provenance.EXTRACTED,
14350
- confidence: (0, import_types47.confidenceForExtracted)("structural"),
15019
+ type: import_types48.EdgeType.DEPENDS_ON,
15020
+ provenance: import_types48.Provenance.EXTRACTED,
15021
+ confidence: (0, import_types48.confidenceForExtracted)("structural"),
14351
15022
  evidence: { file: evidenceFile }
14352
15023
  };
14353
15024
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14359,9 +15030,9 @@ async function addComposeInfra(graph, scanPath, services) {
14359
15030
 
14360
15031
  // src/extract/infra/dockerfile.ts
14361
15032
  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");
15033
+ var import_node_path60 = __toESM(require("path"), 1);
15034
+ var import_node_fs27 = require("fs");
15035
+ var import_types49 = require("@neat.is/types");
14365
15036
  function readDockerfile(content) {
14366
15037
  let image = null;
14367
15038
  const ports = [];
@@ -14390,15 +15061,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14390
15061
  let nodesAdded = 0;
14391
15062
  let edgesAdded = 0;
14392
15063
  for (const service of services) {
14393
- const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
15064
+ const dockerfilePath = import_node_path60.default.join(service.dir, "Dockerfile");
14394
15065
  if (!await exists2(dockerfilePath)) continue;
14395
15066
  let content;
14396
15067
  try {
14397
- content = await import_node_fs26.promises.readFile(dockerfilePath, "utf8");
15068
+ content = await import_node_fs27.promises.readFile(dockerfilePath, "utf8");
14398
15069
  } catch (err) {
14399
15070
  recordExtractionError(
14400
15071
  "infra dockerfile",
14401
- import_node_path58.default.relative(scanPath, dockerfilePath),
15072
+ import_node_path60.default.relative(scanPath, dockerfilePath),
14402
15073
  err
14403
15074
  );
14404
15075
  continue;
@@ -14410,8 +15081,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14410
15081
  graph.addNode(node.id, node);
14411
15082
  nodesAdded++;
14412
15083
  }
14413
- const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14414
- const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, dockerfilePath));
15084
+ const relDockerfile = toPosix(import_node_path60.default.relative(service.dir, dockerfilePath));
15085
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, dockerfilePath));
14415
15086
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14416
15087
  graph,
14417
15088
  service.pkg.name,
@@ -14420,15 +15091,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14420
15091
  );
14421
15092
  nodesAdded += fn;
14422
15093
  edgesAdded += fe;
14423
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types48.EdgeType.RUNS_ON);
15094
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types49.EdgeType.RUNS_ON);
14424
15095
  if (!graph.hasEdge(edgeId)) {
14425
15096
  const edge = {
14426
15097
  id: edgeId,
14427
15098
  source: fileNodeId,
14428
15099
  target: node.id,
14429
- type: import_types48.EdgeType.RUNS_ON,
14430
- provenance: import_types48.Provenance.EXTRACTED,
14431
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15100
+ type: import_types49.EdgeType.RUNS_ON,
15101
+ provenance: import_types49.Provenance.EXTRACTED,
15102
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14432
15103
  evidence: {
14433
15104
  file: evidenceFile,
14434
15105
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -14443,15 +15114,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14443
15114
  graph.addNode(portNode.id, portNode);
14444
15115
  nodesAdded++;
14445
15116
  }
14446
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types48.EdgeType.CONNECTS_TO);
15117
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types49.EdgeType.CONNECTS_TO);
14447
15118
  if (graph.hasEdge(portEdgeId)) continue;
14448
15119
  const portEdge = {
14449
15120
  id: portEdgeId,
14450
15121
  source: fileNodeId,
14451
15122
  target: portNode.id,
14452
- type: import_types48.EdgeType.CONNECTS_TO,
14453
- provenance: import_types48.Provenance.EXTRACTED,
14454
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
15123
+ type: import_types49.EdgeType.CONNECTS_TO,
15124
+ provenance: import_types49.Provenance.EXTRACTED,
15125
+ confidence: (0, import_types49.confidenceForExtracted)("structural"),
14455
15126
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
14456
15127
  };
14457
15128
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -14463,23 +15134,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14463
15134
 
14464
15135
  // src/extract/infra/terraform.ts
14465
15136
  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");
15137
+ var import_node_fs28 = require("fs");
15138
+ var import_node_path61 = __toESM(require("path"), 1);
15139
+ var import_types50 = require("@neat.is/types");
14469
15140
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
14470
15141
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
14471
15142
  async function walkTfFiles(start, depth = 0, max = 5) {
14472
15143
  if (depth > max) return [];
14473
15144
  const out = [];
14474
- const entries = await import_node_fs27.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15145
+ const entries = await import_node_fs28.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14475
15146
  for (const entry of entries) {
14476
15147
  if (entry.isDirectory()) {
14477
15148
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
14478
- const child = import_node_path59.default.join(start, entry.name);
15149
+ const child = import_node_path61.default.join(start, entry.name);
14479
15150
  if (await isPythonVenvDir(child)) continue;
14480
15151
  out.push(...await walkTfFiles(child, depth + 1, max));
14481
15152
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
14482
- out.push(import_node_path59.default.join(start, entry.name));
15153
+ out.push(import_node_path61.default.join(start, entry.name));
14483
15154
  }
14484
15155
  }
14485
15156
  return out;
@@ -14510,8 +15181,8 @@ async function addTerraformResources(graph, scanPath) {
14510
15181
  let edgesAdded = 0;
14511
15182
  const files = await walkTfFiles(scanPath);
14512
15183
  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));
15184
+ const content = await import_node_fs28.promises.readFile(file, "utf8");
15185
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, file));
14515
15186
  const resources = [];
14516
15187
  const byKey = /* @__PURE__ */ new Map();
14517
15188
  RESOURCE_RE.lastIndex = 0;
@@ -14546,16 +15217,16 @@ async function addTerraformResources(graph, scanPath) {
14546
15217
  if (!target) continue;
14547
15218
  if (seen.has(target.nodeId)) continue;
14548
15219
  seen.add(target.nodeId);
14549
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types49.EdgeType.DEPENDS_ON);
15220
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types50.EdgeType.DEPENDS_ON);
14550
15221
  if (graph.hasEdge(edgeId)) continue;
14551
15222
  const line = lineAt2(content, resource.bodyOffset + ref.index);
14552
15223
  const edge = {
14553
15224
  id: edgeId,
14554
15225
  source: resource.nodeId,
14555
15226
  target: target.nodeId,
14556
- type: import_types49.EdgeType.DEPENDS_ON,
14557
- provenance: import_types49.Provenance.EXTRACTED,
14558
- confidence: (0, import_types49.confidenceForExtracted)("structural"),
15227
+ type: import_types50.EdgeType.DEPENDS_ON,
15228
+ provenance: import_types50.Provenance.EXTRACTED,
15229
+ confidence: (0, import_types50.confidenceForExtracted)("structural"),
14559
15230
  evidence: { file: evidenceFile, line, snippet: key }
14560
15231
  };
14561
15232
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14568,8 +15239,8 @@ async function addTerraformResources(graph, scanPath) {
14568
15239
 
14569
15240
  // src/extract/infra/k8s.ts
14570
15241
  init_cjs_shims();
14571
- var import_node_fs28 = require("fs");
14572
- var import_node_path60 = __toESM(require("path"), 1);
15242
+ var import_node_fs29 = require("fs");
15243
+ var import_node_path62 = __toESM(require("path"), 1);
14573
15244
  var import_yaml3 = require("yaml");
14574
15245
  var K8S_KIND_TO_INFRA_KIND = {
14575
15246
  Service: "k8s-service",
@@ -14583,15 +15254,15 @@ var K8S_KIND_TO_INFRA_KIND = {
14583
15254
  async function walkYamlFiles2(start, depth = 0, max = 5) {
14584
15255
  if (depth > max) return [];
14585
15256
  const out = [];
14586
- const entries = await import_node_fs28.promises.readdir(start, { withFileTypes: true }).catch(() => []);
15257
+ const entries = await import_node_fs29.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14587
15258
  for (const entry of entries) {
14588
15259
  if (entry.isDirectory()) {
14589
15260
  if (IGNORED_DIRS.has(entry.name)) continue;
14590
- const child = import_node_path60.default.join(start, entry.name);
15261
+ const child = import_node_path62.default.join(start, entry.name);
14591
15262
  if (await isPythonVenvDir(child)) continue;
14592
15263
  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));
15264
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path62.default.extname(entry.name))) {
15265
+ out.push(import_node_path62.default.join(start, entry.name));
14595
15266
  }
14596
15267
  }
14597
15268
  return out;
@@ -14600,7 +15271,7 @@ async function addK8sResources(graph, scanPath) {
14600
15271
  let nodesAdded = 0;
14601
15272
  const files = await walkYamlFiles2(scanPath);
14602
15273
  for (const file of files) {
14603
- const content = await import_node_fs28.promises.readFile(file, "utf8");
15274
+ const content = await import_node_fs29.promises.readFile(file, "utf8");
14604
15275
  let docs;
14605
15276
  try {
14606
15277
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14624,16 +15295,16 @@ async function addK8sResources(graph, scanPath) {
14624
15295
 
14625
15296
  // src/extract/infra/cloudflare.ts
14626
15297
  init_cjs_shims();
14627
- var import_node_fs29 = require("fs");
14628
- var import_node_path61 = __toESM(require("path"), 1);
15298
+ var import_node_fs30 = require("fs");
15299
+ var import_node_path63 = __toESM(require("path"), 1);
14629
15300
  var import_smol_toml3 = require("smol-toml");
14630
- var import_types50 = require("@neat.is/types");
15301
+ var import_types51 = require("@neat.is/types");
14631
15302
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14632
15303
  async function readWranglerConfig(dir) {
14633
15304
  for (const filename of WRANGLER_FILENAMES) {
14634
- const abs = import_node_path61.default.join(dir, filename);
15305
+ const abs = import_node_path63.default.join(dir, filename);
14635
15306
  if (!await exists2(abs)) continue;
14636
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15307
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14637
15308
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14638
15309
  return { config, relFile: filename, raw };
14639
15310
  }
@@ -14675,8 +15346,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14675
15346
  source: anchorId,
14676
15347
  target: node.id,
14677
15348
  type: edgeType,
14678
- provenance: import_types50.Provenance.EXTRACTED,
14679
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15349
+ provenance: import_types51.Provenance.EXTRACTED,
15350
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14680
15351
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14681
15352
  };
14682
15353
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14694,11 +15365,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14694
15365
  try {
14695
15366
  read = await readWranglerConfig(service.dir);
14696
15367
  } catch (err) {
14697
- recordExtractionError("infra cloudflare", import_node_path61.default.relative(scanPath, service.dir), err);
15368
+ recordExtractionError("infra cloudflare", import_node_path63.default.relative(scanPath, service.dir), err);
14698
15369
  continue;
14699
15370
  }
14700
15371
  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)));
15372
+ const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, read.relFile)));
14702
15373
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14703
15374
  }
14704
15375
  for (const worker of discovered) {
@@ -14710,7 +15381,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14710
15381
  }
14711
15382
  let anchorId = service.node.id;
14712
15383
  if (config.main) {
14713
- const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
15384
+ const entryRelPath = toPosix(import_node_path63.default.normalize(config.main));
14714
15385
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14715
15386
  graph,
14716
15387
  service.pkg.name,
@@ -14737,15 +15408,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14737
15408
  nodesAdded++;
14738
15409
  }
14739
15410
  if (runtimeNode.id !== anchorId) {
14740
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types50.EdgeType.RUNS_ON);
15411
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types51.EdgeType.RUNS_ON);
14741
15412
  if (!graph.hasEdge(runsOnId)) {
14742
15413
  const edge = {
14743
15414
  id: runsOnId,
14744
15415
  source: anchorId,
14745
15416
  target: runtimeNode.id,
14746
- type: import_types50.EdgeType.RUNS_ON,
14747
- provenance: import_types50.Provenance.EXTRACTED,
14748
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15417
+ type: import_types51.EdgeType.RUNS_ON,
15418
+ provenance: import_types51.Provenance.EXTRACTED,
15419
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14749
15420
  evidence: {
14750
15421
  file: evidenceFile,
14751
15422
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14759,7 +15430,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14759
15430
  const result = addResourceEdge(
14760
15431
  graph,
14761
15432
  anchorId,
14762
- import_types50.EdgeType.CONNECTS_TO,
15433
+ import_types51.EdgeType.CONNECTS_TO,
14763
15434
  "cloudflare-route",
14764
15435
  route,
14765
15436
  evidenceFile,
@@ -14783,7 +15454,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14783
15454
  const result = addResourceEdge(
14784
15455
  graph,
14785
15456
  anchorId,
14786
- import_types50.EdgeType.DEPENDS_ON,
15457
+ import_types51.EdgeType.DEPENDS_ON,
14787
15458
  group.kind,
14788
15459
  name,
14789
15460
  evidenceFile,
@@ -14797,7 +15468,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14797
15468
  const result = addResourceEdge(
14798
15469
  graph,
14799
15470
  anchorId,
14800
- import_types50.EdgeType.DEPENDS_ON,
15471
+ import_types51.EdgeType.DEPENDS_ON,
14801
15472
  "cloudflare-cron",
14802
15473
  cron,
14803
15474
  evidenceFile,
@@ -14810,7 +15481,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14810
15481
  const result = addResourceEdge(
14811
15482
  graph,
14812
15483
  anchorId,
14813
- import_types50.EdgeType.DEPENDS_ON,
15484
+ import_types51.EdgeType.DEPENDS_ON,
14814
15485
  "cloudflare-env-var",
14815
15486
  varName,
14816
15487
  evidenceFile,
@@ -14823,15 +15494,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14823
15494
  if (!svc.service) continue;
14824
15495
  const target = workerIndex.get(svc.service);
14825
15496
  if (target && target.anchorId !== anchorId) {
14826
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types50.EdgeType.CALLS);
15497
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types51.EdgeType.CALLS);
14827
15498
  if (!graph.hasEdge(edgeId)) {
14828
15499
  const edge = {
14829
15500
  id: edgeId,
14830
15501
  source: anchorId,
14831
15502
  target: target.anchorId,
14832
- type: import_types50.EdgeType.CALLS,
14833
- provenance: import_types50.Provenance.EXTRACTED,
14834
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15503
+ type: import_types51.EdgeType.CALLS,
15504
+ provenance: import_types51.Provenance.EXTRACTED,
15505
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14835
15506
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14836
15507
  };
14837
15508
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14842,7 +15513,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14842
15513
  const result = addResourceEdge(
14843
15514
  graph,
14844
15515
  anchorId,
14845
- import_types50.EdgeType.DEPENDS_ON,
15516
+ import_types51.EdgeType.DEPENDS_ON,
14846
15517
  "cloudflare-service-binding",
14847
15518
  svc.service,
14848
15519
  evidenceFile,
@@ -14857,24 +15528,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14857
15528
 
14858
15529
  // src/extract/infra/vercel.ts
14859
15530
  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");
15531
+ var import_node_fs31 = require("fs");
15532
+ var import_node_path64 = __toESM(require("path"), 1);
15533
+ var import_types52 = require("@neat.is/types");
14863
15534
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14864
15535
  async function readVercelConfig(dir) {
14865
15536
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14866
- const abs = import_node_path62.default.join(dir, filename);
15537
+ const abs = import_node_path64.default.join(dir, filename);
14867
15538
  if (!await exists2(abs)) continue;
14868
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15539
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14869
15540
  const config = JSON.parse(maskCommentsInSource(raw));
14870
15541
  return { config, relFile: filename, raw };
14871
15542
  }
14872
15543
  return null;
14873
15544
  }
14874
15545
  async function readLinkedProjectName(dir) {
14875
- const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
15546
+ const abs = import_node_path64.default.join(dir, ".vercel", "project.json");
14876
15547
  if (!await exists2(abs)) return void 0;
14877
- const parsed = JSON.parse(await import_node_fs30.promises.readFile(abs, "utf8"));
15548
+ const parsed = JSON.parse(await import_node_fs31.promises.readFile(abs, "utf8"));
14878
15549
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14879
15550
  }
14880
15551
  function routeSource(route) {
@@ -14890,7 +15561,7 @@ async function addVercelServices(graph, services, scanPath) {
14890
15561
  read = await readVercelConfig(service.dir);
14891
15562
  projectName = await readLinkedProjectName(service.dir);
14892
15563
  } catch (err) {
14893
- recordExtractionError("infra vercel", import_node_path62.default.relative(scanPath, service.dir), err);
15564
+ recordExtractionError("infra vercel", import_node_path64.default.relative(scanPath, service.dir), err);
14894
15565
  continue;
14895
15566
  }
14896
15567
  if (!read && !projectName) continue;
@@ -14906,7 +15577,7 @@ async function addVercelServices(graph, services, scanPath) {
14906
15577
  const anchorId = service.node.id;
14907
15578
  if (!read) continue;
14908
15579
  const { config, relFile, raw } = read;
14909
- const evidenceFile = toPosix(import_node_path62.default.relative(scanPath, import_node_path62.default.join(service.dir, relFile)));
15580
+ const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
14910
15581
  const add = (edgeType, kind, name) => {
14911
15582
  if (!name) return;
14912
15583
  const result = emitPlatformResourceEdge(
@@ -14922,12 +15593,12 @@ async function addVercelServices(graph, services, scanPath) {
14922
15593
  nodesAdded += result.nodesAdded;
14923
15594
  edgesAdded += result.edgesAdded;
14924
15595
  };
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);
15596
+ add(import_types52.EdgeType.RUNS_ON, "vercel", "vercel");
15597
+ for (const cron of config.crons ?? []) add(import_types52.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
15598
+ for (const varName of Object.keys(config.env ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
15599
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types52.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14929
15600
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14930
- add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15601
+ add(import_types52.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14931
15602
  }
14932
15603
  }
14933
15604
  return { nodesAdded, edgesAdded };
@@ -14935,16 +15606,16 @@ async function addVercelServices(graph, services, scanPath) {
14935
15606
 
14936
15607
  // src/extract/infra/railway.ts
14937
15608
  init_cjs_shims();
14938
- var import_node_fs31 = require("fs");
14939
- var import_node_path63 = __toESM(require("path"), 1);
15609
+ var import_node_fs32 = require("fs");
15610
+ var import_node_path65 = __toESM(require("path"), 1);
14940
15611
  var import_smol_toml4 = require("smol-toml");
14941
- var import_types52 = require("@neat.is/types");
15612
+ var import_types53 = require("@neat.is/types");
14942
15613
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14943
15614
  async function readRailwayConfig(dir) {
14944
15615
  for (const filename of RAILWAY_FILENAMES) {
14945
- const abs = import_node_path63.default.join(dir, filename);
15616
+ const abs = import_node_path65.default.join(dir, filename);
14946
15617
  if (!await exists2(abs)) continue;
14947
- const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15618
+ const raw = await import_node_fs32.promises.readFile(abs, "utf8");
14948
15619
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14949
15620
  return { config, relFile: filename, raw };
14950
15621
  }
@@ -14958,7 +15629,7 @@ async function addRailwayServices(graph, services, scanPath) {
14958
15629
  try {
14959
15630
  read = await readRailwayConfig(service.dir);
14960
15631
  } catch (err) {
14961
- recordExtractionError("infra railway", import_node_path63.default.relative(scanPath, service.dir), err);
15632
+ recordExtractionError("infra railway", import_node_path65.default.relative(scanPath, service.dir), err);
14962
15633
  continue;
14963
15634
  }
14964
15635
  if (!read) continue;
@@ -14968,7 +15639,7 @@ async function addRailwayServices(graph, services, scanPath) {
14968
15639
  }
14969
15640
  const anchorId = service.node.id;
14970
15641
  const { config, relFile, raw } = read;
14971
- const evidenceFile = toPosix(import_node_path63.default.relative(scanPath, import_node_path63.default.join(service.dir, relFile)));
15642
+ const evidenceFile = toPosix(import_node_path65.default.relative(scanPath, import_node_path65.default.join(service.dir, relFile)));
14972
15643
  const add = (edgeType, kind, name) => {
14973
15644
  if (!name) return;
14974
15645
  const result = emitPlatformResourceEdge(
@@ -14984,24 +15655,24 @@ async function addRailwayServices(graph, services, scanPath) {
14984
15655
  nodesAdded += result.nodesAdded;
14985
15656
  edgesAdded += result.edgesAdded;
14986
15657
  };
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);
15658
+ add(import_types53.EdgeType.RUNS_ON, "railway", "railway");
15659
+ add(import_types53.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
15660
+ add(import_types53.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14990
15661
  }
14991
15662
  return { nodesAdded, edgesAdded };
14992
15663
  }
14993
15664
 
14994
15665
  // src/extract/infra/supabase.ts
14995
15666
  init_cjs_shims();
14996
- var import_node_fs32 = require("fs");
14997
- var import_node_path64 = __toESM(require("path"), 1);
15667
+ var import_node_fs33 = require("fs");
15668
+ var import_node_path66 = __toESM(require("path"), 1);
14998
15669
  var import_smol_toml5 = require("smol-toml");
14999
- var import_types53 = require("@neat.is/types");
15670
+ var import_types54 = require("@neat.is/types");
15000
15671
  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);
15672
+ const relFile = import_node_path66.default.join("supabase", "config.toml");
15673
+ const abs = import_node_path66.default.join(dir, relFile);
15003
15674
  if (!await exists2(abs)) return null;
15004
- const raw = await import_node_fs32.promises.readFile(abs, "utf8");
15675
+ const raw = await import_node_fs33.promises.readFile(abs, "utf8");
15005
15676
  const config = (0, import_smol_toml5.parse)(raw);
15006
15677
  return { config, relFile, raw };
15007
15678
  }
@@ -15013,7 +15684,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15013
15684
  try {
15014
15685
  read = await readSupabaseConfig(service.dir);
15015
15686
  } catch (err) {
15016
- recordExtractionError("infra supabase", import_node_path64.default.relative(scanPath, service.dir), err);
15687
+ recordExtractionError("infra supabase", import_node_path66.default.relative(scanPath, service.dir), err);
15017
15688
  continue;
15018
15689
  }
15019
15690
  if (!read) continue;
@@ -15028,7 +15699,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
15028
15699
  });
15029
15700
  }
15030
15701
  const anchorId = service.node.id;
15031
- const evidenceFile = toPosix(import_node_path64.default.relative(scanPath, import_node_path64.default.join(service.dir, relFile)));
15702
+ const evidenceFile = toPosix(import_node_path66.default.relative(scanPath, import_node_path66.default.join(service.dir, relFile)));
15032
15703
  const add = (edgeType, kind, name) => {
15033
15704
  if (!name) return;
15034
15705
  const result = emitPlatformResourceEdge(
@@ -15044,10 +15715,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
15044
15715
  nodesAdded += result.nodesAdded;
15045
15716
  edgesAdded += result.edgesAdded;
15046
15717
  };
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");
15718
+ add(import_types54.EdgeType.RUNS_ON, "supabase", "supabase");
15719
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types54.EdgeType.DEPENDS_ON, "supabase-function", fn);
15720
+ if (config.storage) add(import_types54.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
15721
+ if (config.auth) add(import_types54.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
15051
15722
  }
15052
15723
  return { nodesAdded, edgesAdded };
15053
15724
  }
@@ -15070,14 +15741,14 @@ async function addInfra(graph, scanPath, services) {
15070
15741
 
15071
15742
  // src/extract/zod-shapes.ts
15072
15743
  init_cjs_shims();
15073
- var import_node_path65 = __toESM(require("path"), 1);
15074
- var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
15744
+ var import_node_path67 = __toESM(require("path"), 1);
15745
+ var import_tree_sitter17 = __toESM(require("tree-sitter"), 1);
15075
15746
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15076
- var import_types54 = require("@neat.is/types");
15747
+ var import_types55 = require("@neat.is/types");
15077
15748
  var ZOD_IMPORT_RE = /\bzod\b/;
15078
15749
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
15079
15750
  function parserForExt4(ext) {
15080
- const p = new import_tree_sitter16.default();
15751
+ const p = new import_tree_sitter17.default();
15081
15752
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
15082
15753
  return p;
15083
15754
  }
@@ -15165,7 +15836,7 @@ function topLevelSchemas(root) {
15165
15836
  }
15166
15837
  function zodShapesFromFile(file, serviceDir) {
15167
15838
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
15168
- const tree = parseSource3(parserForExt4(import_node_path65.default.extname(file.path)), file.content);
15839
+ const tree = parseSource3(parserForExt4(import_node_path67.default.extname(file.path)), file.content);
15169
15840
  const out = [];
15170
15841
  const seen = /* @__PURE__ */ new Set();
15171
15842
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -15179,11 +15850,11 @@ function zodShapesFromFile(file, serviceDir) {
15179
15850
  seen.add(name);
15180
15851
  const line = call.startPosition.row + 1;
15181
15852
  out.push({
15182
- infraId: (0, import_types54.infraId)("zod-schema", name),
15853
+ infraId: (0, import_types55.infraId)("zod-schema", name),
15183
15854
  name,
15184
15855
  fields,
15185
15856
  evidence: {
15186
- file: import_node_path65.default.relative(serviceDir, file.path),
15857
+ file: import_node_path67.default.relative(serviceDir, file.path),
15187
15858
  line,
15188
15859
  snippet: snippet(file.content, line)
15189
15860
  }
@@ -15214,7 +15885,7 @@ async function addZodShapes(graph, services) {
15214
15885
  if (!graph.hasNode(shape.infraId)) {
15215
15886
  const node = {
15216
15887
  id: shape.infraId,
15217
- type: import_types54.NodeType.InfraNode,
15888
+ type: import_types55.NodeType.InfraNode,
15218
15889
  name: shape.name,
15219
15890
  provider: "self",
15220
15891
  kind: "zod-schema"
@@ -15224,14 +15895,14 @@ async function addZodShapes(graph, services) {
15224
15895
  }
15225
15896
  if (shape.fields.length > 0) {
15226
15897
  const node = graph.getNodeAttributes(shape.infraId);
15227
- if (node.type === import_types54.NodeType.InfraNode) {
15898
+ if (node.type === import_types55.NodeType.InfraNode) {
15228
15899
  graph.replaceNodeAttributes(shape.infraId, {
15229
15900
  ...node,
15230
15901
  columns: foldColumns(
15231
15902
  node.columns,
15232
15903
  shape.fields,
15233
- import_types54.Provenance.EXTRACTED,
15234
- (0, import_types54.confidenceForExtracted)("structural")
15904
+ import_types55.Provenance.EXTRACTED,
15905
+ (0, import_types55.confidenceForExtracted)("structural")
15235
15906
  )
15236
15907
  });
15237
15908
  }
@@ -15245,15 +15916,15 @@ async function addZodShapes(graph, services) {
15245
15916
  );
15246
15917
  nodesAdded += n;
15247
15918
  edgesAdded += e;
15248
- const edgeId = (0, import_types54.extractedEdgeId)(fileNodeId, shape.infraId, import_types54.EdgeType.CONTAINS);
15919
+ const edgeId = (0, import_types55.extractedEdgeId)(fileNodeId, shape.infraId, import_types55.EdgeType.CONTAINS);
15249
15920
  if (!graph.hasEdge(edgeId)) {
15250
15921
  const edge = {
15251
15922
  id: edgeId,
15252
15923
  source: fileNodeId,
15253
15924
  target: shape.infraId,
15254
- type: import_types54.EdgeType.CONTAINS,
15255
- provenance: import_types54.Provenance.EXTRACTED,
15256
- confidence: (0, import_types54.confidenceForExtracted)("structural"),
15925
+ type: import_types55.EdgeType.CONTAINS,
15926
+ provenance: import_types55.Provenance.EXTRACTED,
15927
+ confidence: (0, import_types55.confidenceForExtracted)("structural"),
15257
15928
  evidence: shape.evidence
15258
15929
  };
15259
15930
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -15267,7 +15938,7 @@ async function addZodShapes(graph, services) {
15267
15938
 
15268
15939
  // src/extract/firestore-rules.ts
15269
15940
  init_cjs_shims();
15270
- var import_types55 = require("@neat.is/types");
15941
+ var import_types56 = require("@neat.is/types");
15271
15942
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
15272
15943
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
15273
15944
  function stripComments(src) {
@@ -15407,7 +16078,7 @@ async function addFirestoreRules(graph, services) {
15407
16078
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
15408
16079
  graph.forEachNode((id, attrs) => {
15409
16080
  const node = attrs;
15410
- if (node.type !== import_types55.NodeType.InfraNode) return;
16081
+ if (node.type !== import_types56.NodeType.InfraNode) return;
15411
16082
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
15412
16083
  const fields = guards.get(collectionKeyFromName(node.name));
15413
16084
  if (!fields || fields.size === 0) return;
@@ -15420,17 +16091,17 @@ async function addFirestoreRules(graph, services) {
15420
16091
  }
15421
16092
 
15422
16093
  // src/extract/index.ts
15423
- var import_node_path67 = __toESM(require("path"), 1);
16094
+ var import_node_path69 = __toESM(require("path"), 1);
15424
16095
 
15425
16096
  // src/extract/retire.ts
15426
16097
  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");
16098
+ var import_node_fs34 = require("fs");
16099
+ var import_node_path68 = __toESM(require("path"), 1);
16100
+ var import_types57 = require("@neat.is/types");
15430
16101
  function dropOrphanedFileNodes(graph) {
15431
16102
  const orphans = [];
15432
16103
  graph.forEachNode((id, attrs) => {
15433
- if (attrs.type !== import_types56.NodeType.FileNode) return;
16104
+ if (attrs.type !== import_types57.NodeType.FileNode) return;
15434
16105
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
15435
16106
  orphans.push(id);
15436
16107
  }
@@ -15443,14 +16114,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
15443
16114
  const bases = [scanPath, ...serviceDirs];
15444
16115
  graph.forEachEdge((id, attrs) => {
15445
16116
  const edge = attrs;
15446
- if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
16117
+ if (edge.provenance !== import_types57.Provenance.EXTRACTED) return;
15447
16118
  const evidenceFile = edge.evidence?.file;
15448
16119
  if (!evidenceFile) return;
15449
- if (import_node_path66.default.isAbsolute(evidenceFile)) {
15450
- if (!(0, import_node_fs33.existsSync)(evidenceFile)) toDrop.push(id);
16120
+ if (import_node_path68.default.isAbsolute(evidenceFile)) {
16121
+ if (!(0, import_node_fs34.existsSync)(evidenceFile)) toDrop.push(id);
15451
16122
  return;
15452
16123
  }
15453
- const found = bases.some((base) => (0, import_node_fs33.existsSync)(import_node_path66.default.join(base, evidenceFile)));
16124
+ const found = bases.some((base) => (0, import_node_fs34.existsSync)(import_node_path68.default.join(base, evidenceFile)));
15454
16125
  if (!found) toDrop.push(id);
15455
16126
  });
15456
16127
  for (const id of toDrop) graph.dropEdge(id);
@@ -15507,7 +16178,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
15507
16178
  }
15508
16179
  const droppedEntries = drainDroppedExtracted();
15509
16180
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
15510
- const rejectedPath = import_node_path67.default.join(import_node_path67.default.dirname(opts.errorsPath), "rejected.ndjson");
16181
+ const rejectedPath = import_node_path69.default.join(import_node_path69.default.dirname(opts.errorsPath), "rejected.ndjson");
15511
16182
  try {
15512
16183
  await writeRejectedExtracted(droppedEntries, rejectedPath);
15513
16184
  } catch (err) {
@@ -15541,7 +16212,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
15541
16212
 
15542
16213
  // src/diff.ts
15543
16214
  init_cjs_shims();
15544
- var import_node_fs34 = require("fs");
16215
+ var import_node_fs35 = require("fs");
15545
16216
  async function loadSnapshotForDiff(target) {
15546
16217
  if (/^https?:\/\//i.test(target)) {
15547
16218
  const res = await fetch(target);
@@ -15550,7 +16221,7 @@ async function loadSnapshotForDiff(target) {
15550
16221
  }
15551
16222
  return await res.json();
15552
16223
  }
15553
- const raw = await import_node_fs34.promises.readFile(target, "utf8");
16224
+ const raw = await import_node_fs35.promises.readFile(target, "utf8");
15554
16225
  return JSON.parse(raw);
15555
16226
  }
15556
16227
  function indexEntries(entries) {
@@ -15618,9 +16289,9 @@ function canonicalJson(value) {
15618
16289
 
15619
16290
  // src/persist.ts
15620
16291
  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");
16292
+ var import_node_fs36 = require("fs");
16293
+ var import_node_path70 = __toESM(require("path"), 1);
16294
+ var import_types58 = require("@neat.is/types");
15624
16295
  var SCHEMA_VERSION = 7;
15625
16296
  function migrateV1ToV2(payload) {
15626
16297
  const nodes = payload.graph.nodes;
@@ -15644,7 +16315,7 @@ function migrateV5ToV6(payload) {
15644
16315
  if (Array.isArray(nodes)) {
15645
16316
  for (const node of nodes) {
15646
16317
  const attrs = node.attributes;
15647
- if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
16318
+ if (!attrs || attrs.type !== import_types58.NodeType.InfraNode) continue;
15648
16319
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
15649
16320
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
15650
16321
  }
@@ -15660,12 +16331,12 @@ function migrateV2ToV3(payload) {
15660
16331
  for (const edge of edges) {
15661
16332
  const attrs = edge.attributes;
15662
16333
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
15663
- attrs.provenance = import_types57.Provenance.OBSERVED;
16334
+ attrs.provenance = import_types58.Provenance.OBSERVED;
15664
16335
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
15665
16336
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
15666
16337
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
15667
16338
  if (type && source && target) {
15668
- const newId = (0, import_types57.observedEdgeId)(source, target, type);
16339
+ const newId = (0, import_types58.observedEdgeId)(source, target, type);
15669
16340
  attrs.id = newId;
15670
16341
  if (edge.key) edge.key = newId;
15671
16342
  }
@@ -15674,7 +16345,7 @@ function migrateV2ToV3(payload) {
15674
16345
  return { ...payload, schemaVersion: 3 };
15675
16346
  }
15676
16347
  async function ensureDir(filePath) {
15677
- await import_node_fs35.promises.mkdir(import_node_path68.default.dirname(filePath), { recursive: true });
16348
+ await import_node_fs36.promises.mkdir(import_node_path70.default.dirname(filePath), { recursive: true });
15678
16349
  }
15679
16350
  async function saveGraphToDisk(graph, outPath) {
15680
16351
  await ensureDir(outPath);
@@ -15684,13 +16355,13 @@ async function saveGraphToDisk(graph, outPath) {
15684
16355
  graph: graph.export()
15685
16356
  };
15686
16357
  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);
16358
+ await import_node_fs36.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
16359
+ await import_node_fs36.promises.rename(tmp, outPath);
15689
16360
  }
15690
16361
  async function loadGraphFromDisk(graph, outPath) {
15691
16362
  let raw;
15692
16363
  try {
15693
- raw = await import_node_fs35.promises.readFile(outPath, "utf8");
16364
+ raw = await import_node_fs36.promises.readFile(outPath, "utf8");
15694
16365
  } catch (err) {
15695
16366
  if (err.code === "ENOENT") return;
15696
16367
  throw err;
@@ -15764,23 +16435,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
15764
16435
 
15765
16436
  // src/projects.ts
15766
16437
  init_cjs_shims();
15767
- var import_node_path69 = __toESM(require("path"), 1);
16438
+ var import_node_path71 = __toESM(require("path"), 1);
15768
16439
  function pathsForProject(project, baseDir) {
15769
16440
  if (project === DEFAULT_PROJECT) {
15770
16441
  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")
16442
+ snapshotPath: import_node_path71.default.join(baseDir, "graph.json"),
16443
+ errorsPath: import_node_path71.default.join(baseDir, "errors.ndjson"),
16444
+ staleEventsPath: import_node_path71.default.join(baseDir, "stale-events.ndjson"),
16445
+ embeddingsCachePath: import_node_path71.default.join(baseDir, "embeddings.json"),
16446
+ policyViolationsPath: import_node_path71.default.join(baseDir, "policy-violations.ndjson")
15776
16447
  };
15777
16448
  }
15778
16449
  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`)
16450
+ snapshotPath: import_node_path71.default.join(baseDir, `${project}.json`),
16451
+ errorsPath: import_node_path71.default.join(baseDir, `errors.${project}.ndjson`),
16452
+ staleEventsPath: import_node_path71.default.join(baseDir, `stale-events.${project}.ndjson`),
16453
+ embeddingsCachePath: import_node_path71.default.join(baseDir, `embeddings.${project}.json`),
16454
+ policyViolationsPath: import_node_path71.default.join(baseDir, `policy-violations.${project}.ndjson`)
15784
16455
  };
15785
16456
  }
15786
16457
  var Projects = class {
@@ -15820,20 +16491,20 @@ function parseExtraProjects(raw) {
15820
16491
 
15821
16492
  // src/registry.ts
15822
16493
  init_cjs_shims();
15823
- var import_node_fs36 = require("fs");
16494
+ var import_node_fs37 = require("fs");
15824
16495
  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");
16496
+ var import_node_path72 = __toESM(require("path"), 1);
16497
+ var import_types59 = require("@neat.is/types");
15827
16498
  function neatHome() {
15828
16499
  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");
16500
+ if (override && override.length > 0) return import_node_path72.default.resolve(override);
16501
+ return import_node_path72.default.join(import_node_os3.default.homedir(), ".neat");
15831
16502
  }
15832
16503
  function registryPath() {
15833
- return import_node_path70.default.join(neatHome(), "projects.json");
16504
+ return import_node_path72.default.join(neatHome(), "projects.json");
15834
16505
  }
15835
16506
  function daemonsDir() {
15836
- return import_node_path70.default.join(neatHome(), "daemons");
16507
+ return import_node_path72.default.join(neatHome(), "daemons");
15837
16508
  }
15838
16509
  function isFiniteInt(v) {
15839
16510
  return typeof v === "number" && Number.isFinite(v);
@@ -15866,7 +16537,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15866
16537
  const dir = daemonsDir();
15867
16538
  let names;
15868
16539
  try {
15869
- names = await import_node_fs36.promises.readdir(dir);
16540
+ names = await import_node_fs37.promises.readdir(dir);
15870
16541
  } catch (err) {
15871
16542
  if (err.code === "ENOENT") return [];
15872
16543
  throw err;
@@ -15874,10 +16545,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15874
16545
  const out = [];
15875
16546
  for (const name of names) {
15876
16547
  if (!name.endsWith(".json")) continue;
15877
- const file = import_node_path70.default.join(dir, name);
16548
+ const file = import_node_path72.default.join(dir, name);
15878
16549
  let raw;
15879
16550
  try {
15880
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16551
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15881
16552
  } catch {
15882
16553
  continue;
15883
16554
  }
@@ -15905,7 +16576,7 @@ async function readRegistry() {
15905
16576
  const file = registryPath();
15906
16577
  let raw;
15907
16578
  try {
15908
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16579
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15909
16580
  } catch (err) {
15910
16581
  if (err.code === "ENOENT") {
15911
16582
  return { version: 1, projects: [] };
@@ -15913,7 +16584,7 @@ async function readRegistry() {
15913
16584
  throw err;
15914
16585
  }
15915
16586
  const parsed = JSON.parse(raw);
15916
- return import_types58.RegistryFileSchema.parse(parsed);
16587
+ return import_types59.RegistryFileSchema.parse(parsed);
15917
16588
  }
15918
16589
  async function getProject(name) {
15919
16590
  const reg = await readRegistry();
@@ -15988,8 +16659,8 @@ init_auth();
15988
16659
  // src/connectors-config.ts
15989
16660
  init_cjs_shims();
15990
16661
  var import_node_os4 = __toESM(require("os"), 1);
15991
- var import_node_path71 = __toESM(require("path"), 1);
15992
- var import_node_fs37 = require("fs");
16662
+ var import_node_path73 = __toESM(require("path"), 1);
16663
+ var import_node_fs38 = require("fs");
15993
16664
  var CONNECTORS_CONFIG_VERSION = 1;
15994
16665
  var EnvRefUnsetError = class extends Error {
15995
16666
  ref;
@@ -16003,17 +16674,17 @@ var EnvRefUnsetError = class extends Error {
16003
16674
  };
16004
16675
  function neatHome2() {
16005
16676
  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");
16677
+ if (override && override.length > 0) return import_node_path73.default.resolve(override);
16678
+ return import_node_path73.default.join(import_node_os4.default.homedir(), ".neat");
16008
16679
  }
16009
16680
  function connectorsConfigPath(home = neatHome2()) {
16010
- return import_node_path71.default.join(home, "connectors.json");
16681
+ return import_node_path73.default.join(home, "connectors.json");
16011
16682
  }
16012
16683
  var MODE_MASK_LOOSER_THAN_0600 = 63;
16013
16684
  async function warnIfModeLooserThan0600(file) {
16014
16685
  if (process.platform === "win32") return;
16015
16686
  try {
16016
- const stat = await import_node_fs37.promises.stat(file);
16687
+ const stat = await import_node_fs38.promises.stat(file);
16017
16688
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
16018
16689
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
16019
16690
  console.warn(
@@ -16027,7 +16698,7 @@ async function readConnectorsConfig(home = neatHome2()) {
16027
16698
  const file = connectorsConfigPath(home);
16028
16699
  let raw;
16029
16700
  try {
16030
- raw = await import_node_fs37.promises.readFile(file, "utf8");
16701
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
16031
16702
  } catch (err) {
16032
16703
  if (err.code === "ENOENT") {
16033
16704
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16194,15 +16865,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16194
16865
 
16195
16866
  // src/connectors/index.ts
16196
16867
  init_cjs_shims();
16197
- var import_types59 = require("@neat.is/types");
16868
+ var import_types60 = require("@neat.is/types");
16198
16869
  var NO_ENV = "unknown";
16199
16870
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16200
16871
  if (!graph.hasNode(targetNodeId)) return void 0;
16201
16872
  const sites = [];
16202
16873
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16203
16874
  const edge = graph.getEdgeAttributes(edgeId);
16204
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16205
- const parsed = (0, import_types59.parseFileId)(edge.source);
16875
+ if (edge.provenance !== import_types60.Provenance.EXTRACTED) continue;
16876
+ const parsed = (0, import_types60.parseFileId)(edge.source);
16206
16877
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16207
16878
  const site = { relPath: edge.evidence.file };
16208
16879
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16213,7 +16884,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16213
16884
  function routeCallSiteFor(graph, targetNodeId) {
16214
16885
  if (!graph.hasNode(targetNodeId)) return void 0;
16215
16886
  const attrs = graph.getNodeAttributes(targetNodeId);
16216
- if (attrs.type !== import_types59.NodeType.RouteNode || !attrs.path) return void 0;
16887
+ if (attrs.type !== import_types60.NodeType.RouteNode || !attrs.path) return void 0;
16217
16888
  const site = { relPath: attrs.path };
16218
16889
  if (attrs.line !== void 0) site.line = attrs.line;
16219
16890
  return site;
@@ -16670,10 +17341,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
16670
17341
  // src/connectors/supabase/map.ts
16671
17342
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
16672
17343
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
16673
- function targetFromRestPath(path74) {
16674
- const rpcMatch = REST_RPC_PATH_RE.exec(path74);
17344
+ function targetFromRestPath(path76) {
17345
+ const rpcMatch = REST_RPC_PATH_RE.exec(path76);
16675
17346
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
16676
- const tableMatch = REST_TABLE_PATH_RE.exec(path74);
17347
+ const tableMatch = REST_TABLE_PATH_RE.exec(path76);
16677
17348
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
16678
17349
  return null;
16679
17350
  }
@@ -16784,23 +17455,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
16784
17455
 
16785
17456
  // src/connectors/supabase/resolve.ts
16786
17457
  init_cjs_shims();
16787
- var import_types61 = require("@neat.is/types");
17458
+ var import_types62 = require("@neat.is/types");
16788
17459
  function createSupabaseResolveTarget(graph, config) {
16789
17460
  return (signal, _ctx) => {
16790
17461
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
16791
17462
  return null;
16792
17463
  }
16793
- const subResourceId = (0, import_types61.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
17464
+ const subResourceId = (0, import_types62.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
16794
17465
  if (graph.hasNode(subResourceId)) {
16795
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17466
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16796
17467
  }
16797
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17468
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
16798
17469
  if (graph.hasNode(bareResourceId)) {
16799
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17470
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16800
17471
  }
16801
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17472
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
16802
17473
  if (graph.hasNode(projectLevelId)) {
16803
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17474
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16804
17475
  }
16805
17476
  return null;
16806
17477
  };
@@ -16893,7 +17564,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
16893
17564
 
16894
17565
  // src/connectors/railway/index.ts
16895
17566
  init_cjs_shims();
16896
- var import_types65 = require("@neat.is/types");
17567
+ var import_types66 = require("@neat.is/types");
16897
17568
 
16898
17569
  // src/connectors/railway/client.ts
16899
17570
  init_cjs_shims();
@@ -17044,7 +17715,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
17044
17715
  const out = [];
17045
17716
  graph.forEachNode((_id, attrs) => {
17046
17717
  const node = attrs;
17047
- if (node.type !== import_types65.NodeType.RouteNode) return;
17718
+ if (node.type !== import_types66.NodeType.RouteNode) return;
17048
17719
  const route = attrs;
17049
17720
  if (route.service !== serviceName) return;
17050
17721
  out.push({
@@ -17148,12 +17819,12 @@ function createRailwayResolveTarget(config) {
17148
17819
  const serviceName = config.serviceNameById[config.serviceId];
17149
17820
  if (!serviceName) return null;
17150
17821
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17151
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
17822
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17152
17823
  }
17153
17824
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17154
17825
  const peerName = config.serviceNameById[signal.targetName];
17155
17826
  if (!peerName) return null;
17156
- return { targetNodeId: (0, import_types65.serviceId)(peerName), serviceName, edgeType: import_types65.EdgeType.CONNECTS_TO };
17827
+ return { targetNodeId: (0, import_types66.serviceId)(peerName), serviceName, edgeType: import_types66.EdgeType.CONNECTS_TO };
17157
17828
  }
17158
17829
  return null;
17159
17830
  };
@@ -17277,9 +17948,9 @@ function parseFirebaseTargetName(targetName) {
17277
17948
  const secondSep = rest.indexOf(FIELD_SEP);
17278
17949
  if (secondSep === -1) return null;
17279
17950
  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 };
17951
+ const path76 = rest.slice(secondSep + 1);
17952
+ if (!resourceName || !method || !path76) return null;
17953
+ return { resourceName, method, path: path76 };
17283
17954
  }
17284
17955
  function resourceNameFor(type, labels) {
17285
17956
  if (!labels) return null;
@@ -17317,14 +17988,14 @@ function mapLogEntryToSignal(entry) {
17317
17988
  if (!req) return null;
17318
17989
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17319
17990
  const method = req.requestMethod.toUpperCase();
17320
- const path74 = pathFromRequestUrl(req.requestUrl);
17321
- if (path74 === null) return null;
17991
+ const path76 = pathFromRequestUrl(req.requestUrl);
17992
+ if (path76 === null) return null;
17322
17993
  const timestamp = entry.timestamp;
17323
17994
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17324
17995
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
17325
17996
  return {
17326
17997
  targetKind: resourceType,
17327
- targetName: packFirebaseTargetName({ resourceName, method, path: path74 }),
17998
+ targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
17328
17999
  callCount: 1,
17329
18000
  errorCount: isError ? 1 : 0,
17330
18001
  lastObservedIso: timestamp
@@ -17341,7 +18012,7 @@ function mapLogEntriesToSignals(entries) {
17341
18012
 
17342
18013
  // src/connectors/firebase/resolve.ts
17343
18014
  init_cjs_shims();
17344
- var import_types66 = require("@neat.is/types");
18015
+ var import_types67 = require("@neat.is/types");
17345
18016
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17346
18017
  switch (resourceType) {
17347
18018
  case "cloud_function":
@@ -17356,7 +18027,7 @@ function routeEntriesFor(graph, serviceName) {
17356
18027
  const entries = [];
17357
18028
  graph.forEachNode((_id, attrs) => {
17358
18029
  const node = attrs;
17359
- if (node.type !== import_types66.NodeType.RouteNode) return;
18030
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17360
18031
  const route = attrs;
17361
18032
  if (route.service !== serviceName) return;
17362
18033
  entries.push({
@@ -17388,7 +18059,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17388
18059
  return {
17389
18060
  targetNodeId: match.routeNodeId,
17390
18061
  serviceName,
17391
- edgeType: import_types66.EdgeType.CALLS
18062
+ edgeType: import_types67.EdgeType.CALLS
17392
18063
  };
17393
18064
  };
17394
18065
  }
@@ -17415,7 +18086,7 @@ init_cjs_shims();
17415
18086
 
17416
18087
  // src/connectors/cloudflare/connector.ts
17417
18088
  init_cjs_shims();
17418
- var import_types68 = require("@neat.is/types");
18089
+ var import_types69 = require("@neat.is/types");
17419
18090
 
17420
18091
  // src/connectors/cloudflare/client.ts
17421
18092
  init_cjs_shims();
@@ -17531,7 +18202,7 @@ function mapEventToSignal(event) {
17531
18202
  if (Number.isNaN(observedAt.getTime())) return null;
17532
18203
  const statusCode = metadata?.statusCode;
17533
18204
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17534
- const path74 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18205
+ const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17535
18206
  return {
17536
18207
  targetKind: CLOUDFLARE_TARGET_KIND,
17537
18208
  targetName: scriptName,
@@ -17539,7 +18210,7 @@ function mapEventToSignal(event) {
17539
18210
  errorCount: isError ? 1 : 0,
17540
18211
  lastObservedIso: observedAt.toISOString(),
17541
18212
  method,
17542
- ...path74 ? { path: path74 } : {},
18213
+ ...path76 ? { path: path76 } : {},
17543
18214
  ...typeof statusCode === "number" ? { statusCode } : {},
17544
18215
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17545
18216
  };
@@ -17579,19 +18250,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17579
18250
  graph.forEachNode((id, attrs) => {
17580
18251
  if (found) return;
17581
18252
  const a = attrs;
17582
- if (a.type === import_types68.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
18253
+ if (a.type === import_types69.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
17583
18254
  found = id;
17584
18255
  }
17585
18256
  });
17586
18257
  return found;
17587
18258
  }
17588
- function findMatchingRouteNode(graph, serviceName, method, path74) {
17589
- const normalizedPath = normalizePathTemplate(path74);
18259
+ function findMatchingRouteNode(graph, serviceName, method, path76) {
18260
+ const normalizedPath = normalizePathTemplate(path76);
17590
18261
  let found = null;
17591
18262
  graph.forEachNode((id, attrs) => {
17592
18263
  if (found) return;
17593
18264
  const a = attrs;
17594
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18265
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17595
18266
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17596
18267
  const routeMethod = (a.method ?? "").toUpperCase();
17597
18268
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17603,18 +18274,18 @@ function createCloudflareResolveTarget(config, graph) {
17603
18274
  return (signal) => {
17604
18275
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17605
18276
  const scriptName = signal.targetName;
17606
- const { method, path: path74 } = signal;
18277
+ const { method, path: path76 } = signal;
17607
18278
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17608
- if (!method || !path74) return wholeFileId;
17609
- return findMatchingRouteNode(graph, serviceName, method, path74) ?? wholeFileId;
18279
+ if (!method || !path76) return wholeFileId;
18280
+ return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
17610
18281
  };
17611
18282
  const mapping = config.workers?.[scriptName];
17612
18283
  if (mapping) {
17613
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18284
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17614
18285
  return {
17615
18286
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17616
18287
  serviceName: mapping.service,
17617
- edgeType: import_types68.EdgeType.CALLS
18288
+ edgeType: import_types69.EdgeType.CALLS
17618
18289
  };
17619
18290
  }
17620
18291
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17623,13 +18294,13 @@ function createCloudflareResolveTarget(config, graph) {
17623
18294
  return {
17624
18295
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17625
18296
  serviceName: fileNode.service,
17626
- edgeType: import_types68.EdgeType.CALLS
18297
+ edgeType: import_types69.EdgeType.CALLS
17627
18298
  };
17628
18299
  }
17629
18300
  return {
17630
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18301
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17631
18302
  serviceName: scriptName,
17632
- edgeType: import_types68.EdgeType.CALLS,
18303
+ edgeType: import_types69.EdgeType.CALLS,
17633
18304
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
17634
18305
  };
17635
18306
  };
@@ -17825,14 +18496,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
17825
18496
 
17826
18497
  // src/connectors/neon/resolve.ts
17827
18498
  init_cjs_shims();
17828
- var import_types72 = require("@neat.is/types");
18499
+ var import_types73 = require("@neat.is/types");
17829
18500
  function createNeonResolveTarget(config) {
17830
18501
  return (signal) => {
17831
18502
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
17832
18503
  return {
17833
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18504
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
17834
18505
  serviceName: config.serviceName,
17835
- edgeType: import_types72.EdgeType.CALLS,
18506
+ edgeType: import_types73.EdgeType.CALLS,
17836
18507
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
17837
18508
  };
17838
18509
  };
@@ -17958,9 +18629,9 @@ function parseCloudRunTargetName(targetName) {
17958
18629
  const secondSep = rest.indexOf(FIELD_SEP2);
17959
18630
  if (secondSep === -1) return null;
17960
18631
  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 };
18632
+ const path76 = rest.slice(secondSep + 1);
18633
+ if (!serviceName || !method || !path76) return null;
18634
+ return { serviceName, method, path: path76 };
17964
18635
  }
17965
18636
 
17966
18637
  // src/connectors/cloud-run/map.ts
@@ -17989,14 +18660,14 @@ function mapLogEntryToSignal2(entry) {
17989
18660
  if (!req) return null;
17990
18661
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17991
18662
  const method = req.requestMethod.toUpperCase();
17992
- const path74 = pathFromRequestUrl2(req.requestUrl);
17993
- if (path74 === null) return null;
18663
+ const path76 = pathFromRequestUrl2(req.requestUrl);
18664
+ if (path76 === null) return null;
17994
18665
  const timestamp = entry.timestamp;
17995
18666
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17996
18667
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
17997
18668
  return {
17998
18669
  targetKind: CLOUD_RUN_TARGET_KIND,
17999
- targetName: packCloudRunTargetName({ serviceName, method, path: path74 }),
18670
+ targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
18000
18671
  callCount: 1,
18001
18672
  errorCount: isError ? 1 : 0,
18002
18673
  lastObservedIso: timestamp
@@ -18013,14 +18684,14 @@ function mapLogEntriesToSignals2(entries) {
18013
18684
 
18014
18685
  // src/connectors/cloud-run/resolve.ts
18015
18686
  init_cjs_shims();
18016
- var import_types76 = require("@neat.is/types");
18687
+ var import_types77 = require("@neat.is/types");
18017
18688
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
18018
18689
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
18019
18690
  let found = null;
18020
18691
  graph.forEachNode((_id, attrs) => {
18021
18692
  if (found) return;
18022
18693
  const node = attrs;
18023
- if (node.type !== import_types76.NodeType.RouteNode) return;
18694
+ if (node.type !== import_types77.NodeType.RouteNode) return;
18024
18695
  const route = attrs;
18025
18696
  if (route.service !== serviceName || !route.pathTemplate) return;
18026
18697
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -18035,23 +18706,23 @@ function createCloudRunResolveTarget(graph, config) {
18035
18706
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
18036
18707
  const identity = parseCloudRunTargetName(signal.targetName);
18037
18708
  if (!identity) return null;
18038
- const { serviceName: gcpServiceName, method, path: path74 } = identity;
18709
+ const { serviceName: gcpServiceName, method, path: path76 } = identity;
18039
18710
  const mappedService = config.serviceMap?.[gcpServiceName];
18040
18711
  if (mappedService) {
18041
18712
  const routeNodeId = findMatchingRouteNode2(
18042
18713
  graph,
18043
18714
  mappedService,
18044
18715
  method,
18045
- normalizePathTemplate(path74)
18716
+ normalizePathTemplate(path76)
18046
18717
  );
18047
18718
  if (routeNodeId) {
18048
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
18719
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
18049
18720
  }
18050
18721
  }
18051
18722
  return {
18052
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18723
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18053
18724
  serviceName: mappedService ?? gcpServiceName,
18054
- edgeType: import_types76.EdgeType.CALLS,
18725
+ edgeType: import_types77.EdgeType.CALLS,
18055
18726
  ensureInfraNode: {
18056
18727
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
18057
18728
  name: gcpServiceName,
@@ -18092,7 +18763,7 @@ function createCloudRunConnector(graph, config = {}) {
18092
18763
 
18093
18764
  // src/connectors/render/index.ts
18094
18765
  init_cjs_shims();
18095
- var import_types79 = require("@neat.is/types");
18766
+ var import_types80 = require("@neat.is/types");
18096
18767
 
18097
18768
  // src/connectors/render/types.ts
18098
18769
  init_cjs_shims();
@@ -18170,7 +18841,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18170
18841
  const out = [];
18171
18842
  graph.forEachNode((_id, attrs) => {
18172
18843
  const node = attrs;
18173
- if (node.type !== import_types79.NodeType.RouteNode) return;
18844
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18174
18845
  const route = attrs;
18175
18846
  if (route.service !== serviceName) return;
18176
18847
  out.push({
@@ -18255,7 +18926,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18255
18926
  function createRenderResolveTarget(config) {
18256
18927
  return (signal) => {
18257
18928
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18258
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types79.EdgeType.CALLS };
18929
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
18259
18930
  }
18260
18931
  return null;
18261
18932
  };
@@ -18393,21 +19064,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18393
19064
 
18394
19065
  // src/connectors/planetscale/resolve.ts
18395
19066
  init_cjs_shims();
18396
- var import_types83 = require("@neat.is/types");
19067
+ var import_types84 = require("@neat.is/types");
18397
19068
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18398
19069
  function createPlanetscaleResolveTarget(graph, config) {
18399
19070
  const databaseName = `${config.organization}/${config.database}`;
18400
19071
  return (signal, _ctx) => {
18401
19072
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18402
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19073
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18403
19074
  if (graph.hasNode(tableId)) {
18404
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19075
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18405
19076
  }
18406
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19077
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18407
19078
  return {
18408
19079
  targetNodeId: providerId,
18409
19080
  serviceName: config.serviceName,
18410
- edgeType: import_types83.EdgeType.CALLS,
19081
+ edgeType: import_types84.EdgeType.CALLS,
18411
19082
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18412
19083
  };
18413
19084
  };
@@ -18672,7 +19343,7 @@ function mapBuildsToSignals(builds, serviceName) {
18672
19343
 
18673
19344
  // src/connectors/eas/resolve.ts
18674
19345
  init_cjs_shims();
18675
- var import_types88 = require("@neat.is/types");
19346
+ var import_types89 = require("@neat.is/types");
18676
19347
  var NO_ENV2 = "unknown";
18677
19348
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
18678
19349
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -18688,8 +19359,8 @@ function configBasenamesForPhase(phase) {
18688
19359
  function configNodeService(graph, configNodeId) {
18689
19360
  for (const edgeId of graph.inboundEdges(configNodeId)) {
18690
19361
  const edge = graph.getEdgeAttributes(edgeId);
18691
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
18692
- const parsed = (0, import_types88.parseFileId)(edge.source);
19362
+ if (edge.type !== import_types89.EdgeType.CONFIGURED_BY) continue;
19363
+ const parsed = (0, import_types89.parseFileId)(edge.source);
18693
19364
  if (parsed) return parsed.service;
18694
19365
  }
18695
19366
  return null;
@@ -18700,7 +19371,7 @@ function findConfigNode(graph, basenames, serviceName) {
18700
19371
  graph.forEachNode((id, attrs) => {
18701
19372
  if (scoped) return;
18702
19373
  const node = attrs;
18703
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19374
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
18704
19375
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
18705
19376
  if (anyMatch === null) anyMatch = id;
18706
19377
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -18717,13 +19388,13 @@ function createEasResolveTarget(graph) {
18717
19388
  if (basenames.length > 0) {
18718
19389
  const configNodeId = findConfigNode(graph, basenames, serviceName);
18719
19390
  if (configNodeId) {
18720
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19391
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
18721
19392
  }
18722
19393
  }
18723
19394
  return {
18724
19395
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
18725
19396
  serviceName,
18726
- edgeType: import_types88.EdgeType.CALLS
19397
+ edgeType: import_types89.EdgeType.CALLS
18727
19398
  };
18728
19399
  };
18729
19400
  }
@@ -19402,11 +20073,11 @@ function registerRoutes(scope, ctx) {
19402
20073
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19403
20074
  const parsed = [];
19404
20075
  for (const c of candidates) {
19405
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20076
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19406
20077
  if (!r.success) {
19407
20078
  return reply.code(400).send({
19408
20079
  error: `unknown divergence type "${c}"`,
19409
- allowed: import_types91.DivergenceTypeSchema.options
20080
+ allowed: import_types92.DivergenceTypeSchema.options
19410
20081
  });
19411
20082
  }
19412
20083
  parsed.push(r.data);
@@ -19748,7 +20419,7 @@ function registerRoutes(scope, ctx) {
19748
20419
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
19749
20420
  let violations = await log.readAll();
19750
20421
  if (req.query.severity) {
19751
- const sev = import_types91.PolicySeveritySchema.safeParse(req.query.severity);
20422
+ const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
19752
20423
  if (!sev.success) {
19753
20424
  return reply.code(400).send({
19754
20425
  error: "invalid severity",
@@ -19787,7 +20458,7 @@ function registerRoutes(scope, ctx) {
19787
20458
  scope.post("/policies/check", async (req, reply) => {
19788
20459
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
19789
20460
  if (!proj) return;
19790
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20461
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
19791
20462
  if (!parsed.success) {
19792
20463
  return reply.code(400).send({
19793
20464
  error: "invalid /policies/check body",
@@ -20108,8 +20779,8 @@ init_otel_grpc();
20108
20779
 
20109
20780
  // src/search.ts
20110
20781
  init_cjs_shims();
20111
- var import_node_fs38 = require("fs");
20112
- var import_node_path72 = __toESM(require("path"), 1);
20782
+ var import_node_fs39 = require("fs");
20783
+ var import_node_path74 = __toESM(require("path"), 1);
20113
20784
  var import_node_crypto4 = require("crypto");
20114
20785
  var DEFAULT_LIMIT = 10;
20115
20786
  var NOMIC_DIM = 768;
@@ -20263,7 +20934,7 @@ async function pickEmbedder() {
20263
20934
  }
20264
20935
  async function readCache(cachePath) {
20265
20936
  try {
20266
- const raw = await import_node_fs38.promises.readFile(cachePath, "utf8");
20937
+ const raw = await import_node_fs39.promises.readFile(cachePath, "utf8");
20267
20938
  const parsed = JSON.parse(raw);
20268
20939
  if (parsed.version !== 1) return null;
20269
20940
  return parsed;
@@ -20272,8 +20943,8 @@ async function readCache(cachePath) {
20272
20943
  }
20273
20944
  }
20274
20945
  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));
20946
+ await import_node_fs39.promises.mkdir(import_node_path74.default.dirname(cachePath), { recursive: true });
20947
+ await import_node_fs39.promises.writeFile(cachePath, JSON.stringify(cache));
20277
20948
  }
20278
20949
  var VectorIndex = class {
20279
20950
  constructor(embedder, cachePath) {
@@ -20487,14 +21158,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
20487
21158
  async function main() {
20488
21159
  const baseDirEnv = process.env.NEAT_OUT_DIR;
20489
21160
  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");
21161
+ 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");
21162
+ const defaultScanPath = import_node_path75.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
20492
21163
  const registry = new Projects();
20493
21164
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
20494
21165
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
20495
21166
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
20496
21167
  const projectScan = process.env[envKey];
20497
- await bootProject(registry, name, projectScan ? import_node_path73.default.resolve(projectScan) : void 0, baseDir);
21168
+ await bootProject(registry, name, projectScan ? import_node_path75.default.resolve(projectScan) : void 0, baseDir);
20498
21169
  }
20499
21170
  const host = process.env.HOST ?? "0.0.0.0";
20500
21171
  const port = Number(process.env.PORT ?? 8080);