@neat.is/core 0.9.0 → 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);
6503
+ edges.push(hop.edge);
6504
+ visited.add(hop.nextService);
6505
+ current = hop.nextService;
6506
+ }
6507
+ if (edges.length === 0) return null;
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);
6187
6552
  edges.push(hop.edge);
6188
6553
  visited.add(hop.nextService);
6189
6554
  current = hop.nextService;
6190
6555
  }
6191
6556
  if (edges.length === 0) return null;
6192
- return { path: path74, edges, culprit: current };
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,10 +11358,15 @@ 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;
11365
+ var SARAMA_IMPORT_RE = /"[^"]*\/sarama"/;
11366
+ var GO_PRODUCER_RE = /ProducerMessage\s*\{[\s\S]{0,300}?\bTopic\s*:\s*(?:"([^"]+)"|`([^`]+)`|([A-Za-z_]\w*))/g;
11367
+ var GO_CONSUMER_RE = /\.Consume\s*\([\s\S]{0,120}?\[\]string\s*\{([^}]*)\}/g;
11368
+ var GO_STRING_LITERAL_RE = /"([^"]+)"|`([^`]+)`/g;
11369
+ var GO_IDENT_RE = /[A-Za-z_]\w*/g;
10815
11370
  function findAll(re, text) {
10816
11371
  re.lastIndex = 0;
10817
11372
  const out = [];
@@ -10821,38 +11376,94 @@ function findAll(re, text) {
10821
11376
  }
10822
11377
  return out;
10823
11378
  }
11379
+ function resolveGoLiteral(ident, text) {
11380
+ const esc = ident.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11381
+ const re = new RegExp(
11382
+ `\\b${esc}(?:\\s+\\w[\\w.\\[\\]*]*)?\\s*(?::=|=)\\s*(?:"([^"]+)"|\`([^\`]+)\`)`
11383
+ );
11384
+ const m = re.exec(text);
11385
+ if (!m) return void 0;
11386
+ return m[1] ?? m[2];
11387
+ }
11388
+ function producerTopicAnchor(match) {
11389
+ const [full] = match;
11390
+ const tokenLen = match[1] ? match[1].length + 2 : match[2] ? match[2].length + 2 : match[3]?.length ?? 0;
11391
+ return match.index + full.length - tokenLen;
11392
+ }
11393
+ function goSaramaEndpoints(text, emit) {
11394
+ if (!SARAMA_IMPORT_RE.test(text)) return;
11395
+ GO_PRODUCER_RE.lastIndex = 0;
11396
+ let m;
11397
+ while ((m = GO_PRODUCER_RE.exec(text)) !== null) {
11398
+ const literal = m[1] ?? m[2];
11399
+ const anchor = producerTopicAnchor(m);
11400
+ if (literal) {
11401
+ emit(literal, "PUBLISHES_TO", anchor);
11402
+ } else if (m[3]) {
11403
+ const resolved = resolveGoLiteral(m[3], text);
11404
+ if (resolved) emit(resolved, "PUBLISHES_TO", anchor);
11405
+ }
11406
+ }
11407
+ GO_CONSUMER_RE.lastIndex = 0;
11408
+ while ((m = GO_CONSUMER_RE.exec(text)) !== null) {
11409
+ const inside = m[1] ?? "";
11410
+ const anchor = m.index + Math.max(0, m[0].indexOf("[]string"));
11411
+ let sawLiteral = false;
11412
+ GO_STRING_LITERAL_RE.lastIndex = 0;
11413
+ let s;
11414
+ while ((s = GO_STRING_LITERAL_RE.exec(inside)) !== null) {
11415
+ const topic = s[1] ?? s[2];
11416
+ if (topic) {
11417
+ emit(topic, "CONSUMES_FROM", anchor);
11418
+ sawLiteral = true;
11419
+ }
11420
+ }
11421
+ if (sawLiteral) continue;
11422
+ GO_IDENT_RE.lastIndex = 0;
11423
+ let id;
11424
+ while ((id = GO_IDENT_RE.exec(inside)) !== null) {
11425
+ const resolved = resolveGoLiteral(id[0], text);
11426
+ if (resolved) emit(resolved, "CONSUMES_FROM", anchor);
11427
+ }
11428
+ }
11429
+ }
10824
11430
  function kafkaEndpointsFromFile(file, serviceDir) {
10825
11431
  const out = [];
10826
11432
  const seen = /* @__PURE__ */ new Set();
10827
- const make = (topic, edgeType) => {
11433
+ const make = (topic, edgeType, anchor) => {
10828
11434
  const key = `${edgeType}|${topic}`;
10829
11435
  if (seen.has(key)) return;
10830
11436
  seen.add(key);
10831
- const line = lineOf(file.content, topic);
11437
+ const line = anchor !== void 0 ? file.content.slice(0, anchor).split("\n").length : lineOf(file.content, topic);
10832
11438
  out.push({
10833
11439
  infraId: (0, import_types29.infraId)("kafka-topic", topic),
10834
11440
  name: topic,
10835
11441
  kind: "kafka-topic",
10836
11442
  edgeType,
10837
- // `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
10838
- // framework-aware (kafkajs / node-rdkafka shape). Verified-call-site
10839
- // tier (ADR-066).
11443
+ // JS: `producer.send({topic: 'x'})` / `consumer.subscribe({topic: 'x'})`
11444
+ // (kafkajs / node-rdkafka). Go: `sarama.ProducerMessage{Topic: 'x'}` /
11445
+ // `consumerGroup.Consume(ctx, []string{'x'}, …)`. Both are framework-aware
11446
+ // call sites — verified-call-site tier (ADR-066).
10840
11447
  confidenceKind: "verified-call-site",
10841
11448
  evidence: {
10842
- file: import_node_path40.default.relative(serviceDir, file.path),
11449
+ file: import_node_path41.default.relative(serviceDir, file.path),
10843
11450
  line,
10844
11451
  snippet: snippet(file.content, line)
10845
11452
  }
10846
11453
  });
10847
11454
  };
10848
- for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
10849
- for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
11455
+ if (import_node_path41.default.extname(file.path) === ".go") {
11456
+ goSaramaEndpoints(file.content, make);
11457
+ } else {
11458
+ for (const { topic } of findAll(PRODUCER_TOPIC_RE, file.content)) make(topic, "PUBLISHES_TO");
11459
+ for (const { topic } of findAll(CONSUMER_TOPIC_RE, file.content)) make(topic, "CONSUMES_FROM");
11460
+ }
10850
11461
  return out;
10851
11462
  }
10852
11463
 
10853
11464
  // src/extract/calls/redis.ts
10854
11465
  init_cjs_shims();
10855
- var import_node_path41 = __toESM(require("path"), 1);
11466
+ var import_node_path42 = __toESM(require("path"), 1);
10856
11467
  var import_types30 = require("@neat.is/types");
10857
11468
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
10858
11469
  function redisEndpointsFromFile(file, serviceDir) {
@@ -10875,7 +11486,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10875
11486
  // support tier (ADR-066).
10876
11487
  confidenceKind: "url-with-structural-support",
10877
11488
  evidence: {
10878
- file: import_node_path41.default.relative(serviceDir, file.path),
11489
+ file: import_node_path42.default.relative(serviceDir, file.path),
10879
11490
  line,
10880
11491
  snippet: snippet(file.content, line)
10881
11492
  }
@@ -10886,7 +11497,7 @@ function redisEndpointsFromFile(file, serviceDir) {
10886
11497
 
10887
11498
  // src/extract/calls/aws.ts
10888
11499
  init_cjs_shims();
10889
- var import_node_path42 = __toESM(require("path"), 1);
11500
+ var import_node_path43 = __toESM(require("path"), 1);
10890
11501
  var import_types31 = require("@neat.is/types");
10891
11502
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
10892
11503
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
@@ -10920,7 +11531,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10920
11531
  // (ADR-066).
10921
11532
  confidenceKind: "verified-call-site",
10922
11533
  evidence: {
10923
- file: import_node_path42.default.relative(serviceDir, file.path),
11534
+ file: import_node_path43.default.relative(serviceDir, file.path),
10924
11535
  line,
10925
11536
  snippet: snippet(file.content, line)
10926
11537
  }
@@ -10945,7 +11556,7 @@ function awsEndpointsFromFile(file, serviceDir) {
10945
11556
 
10946
11557
  // src/extract/calls/grpc.ts
10947
11558
  init_cjs_shims();
10948
- var import_node_path43 = __toESM(require("path"), 1);
11559
+ var import_node_path44 = __toESM(require("path"), 1);
10949
11560
  var import_types32 = require("@neat.is/types");
10950
11561
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
10951
11562
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
@@ -11004,7 +11615,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11004
11615
  // tier (ADR-066).
11005
11616
  confidenceKind: "verified-call-site",
11006
11617
  evidence: {
11007
- file: import_node_path43.default.relative(serviceDir, file.path),
11618
+ file: import_node_path44.default.relative(serviceDir, file.path),
11008
11619
  line,
11009
11620
  snippet: snippet(file.content, line)
11010
11621
  }
@@ -11015,7 +11626,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
11015
11626
 
11016
11627
  // src/extract/calls/supabase.ts
11017
11628
  init_cjs_shims();
11018
- var import_node_path44 = __toESM(require("path"), 1);
11629
+ var import_node_path45 = __toESM(require("path"), 1);
11019
11630
  var import_types33 = require("@neat.is/types");
11020
11631
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
11021
11632
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
@@ -11074,7 +11685,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11074
11685
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
11075
11686
  confidenceKind: "verified-call-site",
11076
11687
  evidence: {
11077
- file: import_node_path44.default.relative(serviceDir, file.path),
11688
+ file: import_node_path45.default.relative(serviceDir, file.path),
11078
11689
  line,
11079
11690
  snippet: snippet(file.content, line)
11080
11691
  }
@@ -11101,7 +11712,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11101
11712
  edgeType: "CALLS",
11102
11713
  confidenceKind: "verified-call-site",
11103
11714
  evidence: {
11104
- file: import_node_path44.default.relative(serviceDir, file.path),
11715
+ file: import_node_path45.default.relative(serviceDir, file.path),
11105
11716
  line,
11106
11717
  snippet: snippet(file.content, line)
11107
11718
  }
@@ -11113,7 +11724,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
11113
11724
 
11114
11725
  // src/extract/calls/firestore.ts
11115
11726
  init_cjs_shims();
11116
- var import_node_path45 = __toESM(require("path"), 1);
11727
+ var import_node_path46 = __toESM(require("path"), 1);
11117
11728
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
11118
11729
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
11119
11730
  var import_types34 = require("@neat.is/types");
@@ -11152,7 +11763,7 @@ function isFirestoreClientFactory(node) {
11152
11763
  }
11153
11764
  function firestoreClientVars(root) {
11154
11765
  const vars = /* @__PURE__ */ new Set();
11155
- const walk9 = (node) => {
11766
+ const walk10 = (node) => {
11156
11767
  if (node.type === "variable_declarator") {
11157
11768
  const name = node.childForFieldName("name");
11158
11769
  let value = node.childForFieldName("value");
@@ -11161,9 +11772,9 @@ function firestoreClientVars(root) {
11161
11772
  vars.add(name.text);
11162
11773
  }
11163
11774
  }
11164
- for (const c of namedChildren(node)) walk9(c);
11775
+ for (const c of namedChildren(node)) walk10(c);
11165
11776
  };
11166
- walk9(root);
11777
+ walk10(root);
11167
11778
  return vars;
11168
11779
  }
11169
11780
  function isClientExpr(node, clientVars) {
@@ -11284,7 +11895,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11284
11895
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
11285
11896
  if (!hasClient && !hasAdmin) return [];
11286
11897
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
11287
- 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);
11288
11899
  const clientVars = firestoreClientVars(tree.rootNode);
11289
11900
  const collLine = /* @__PURE__ */ new Map();
11290
11901
  const writes = /* @__PURE__ */ new Map();
@@ -11318,7 +11929,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11318
11929
  }
11319
11930
  s.add(field);
11320
11931
  };
11321
- const walk9 = (node) => {
11932
+ const walk10 = (node) => {
11322
11933
  if (node.type === "call_expression") {
11323
11934
  const fn = node.childForFieldName("function");
11324
11935
  const line = node.startPosition.row + 1;
@@ -11358,9 +11969,9 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11358
11969
  }
11359
11970
  }
11360
11971
  }
11361
- for (const c of namedChildren(node)) walk9(c);
11972
+ for (const c of namedChildren(node)) walk10(c);
11362
11973
  };
11363
- walk9(tree.rootNode);
11974
+ walk10(tree.rootNode);
11364
11975
  const out = [];
11365
11976
  for (const [collPath, line] of collLine) {
11366
11977
  const byField = writes.get(collPath);
@@ -11388,7 +11999,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11388
11999
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
11389
12000
  ...sdkWrites ? { sdkWrites } : {},
11390
12001
  evidence: {
11391
- file: import_node_path45.default.relative(serviceDir, file.path),
12002
+ file: import_node_path46.default.relative(serviceDir, file.path),
11392
12003
  line,
11393
12004
  snippet: snippet(file.content, line)
11394
12005
  }
@@ -11399,7 +12010,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
11399
12010
 
11400
12011
  // src/extract/calls/mongoose.ts
11401
12012
  init_cjs_shims();
11402
- var import_node_path46 = __toESM(require("path"), 1);
12013
+ var import_node_path47 = __toESM(require("path"), 1);
11403
12014
  var import_types35 = require("@neat.is/types");
11404
12015
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
11405
12016
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
@@ -11550,7 +12161,7 @@ function endpoint(r, file, serviceDir, matchText) {
11550
12161
  kind: r.kind,
11551
12162
  edgeType: "CALLS",
11552
12163
  confidenceKind: "verified-call-site",
11553
- 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) }
11554
12165
  };
11555
12166
  }
11556
12167
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -11640,7 +12251,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11640
12251
  const registry = /* @__PURE__ */ new Map();
11641
12252
  for (const f of mongooseFiles) {
11642
12253
  const fx = fileExportsOf(f.content, pluralizeOn);
11643
- 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);
11644
12255
  }
11645
12256
  if (registry.size === 0) return [];
11646
12257
  const out = [];
@@ -11651,7 +12262,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11651
12262
  const directColl = /* @__PURE__ */ new Map();
11652
12263
  const nsExports = /* @__PURE__ */ new Map();
11653
12264
  for (const b of bindings) {
11654
- 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);
11655
12266
  if (!resolvedRel) continue;
11656
12267
  const fx = registry.get(resolvedRel);
11657
12268
  if (!fx) continue;
@@ -11695,7 +12306,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
11695
12306
 
11696
12307
  // src/extract/calls/sqlalchemy.ts
11697
12308
  init_cjs_shims();
11698
- var import_node_path47 = __toESM(require("path"), 1);
12309
+ var import_node_path48 = __toESM(require("path"), 1);
11699
12310
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
11700
12311
  var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11701
12312
  var import_types36 = require("@neat.is/types");
@@ -11834,7 +12445,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
11834
12445
  childTable,
11835
12446
  parentTable,
11836
12447
  evidence: {
11837
- file: import_node_path47.default.relative(serviceDir, file.path),
12448
+ file: import_node_path48.default.relative(serviceDir, file.path),
11838
12449
  line,
11839
12450
  snippet: snippet(file.content, line)
11840
12451
  }
@@ -11859,7 +12470,7 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
11859
12470
  confidenceKind: "verified-call-site",
11860
12471
  ...columns && columns.length > 0 ? { columns } : {},
11861
12472
  evidence: {
11862
- file: import_node_path47.default.relative(serviceDir, file.path),
12473
+ file: import_node_path48.default.relative(serviceDir, file.path),
11863
12474
  line,
11864
12475
  snippet: snippet(file.content, line)
11865
12476
  }
@@ -11966,7 +12577,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11966
12577
  edgeType: "CALLS",
11967
12578
  confidenceKind: "verified-call-site",
11968
12579
  evidence: {
11969
- file: import_node_path47.default.relative(serviceDir, file.path),
12580
+ file: import_node_path48.default.relative(serviceDir, file.path),
11970
12581
  line,
11971
12582
  snippet: snippet(file.content, line)
11972
12583
  }
@@ -11978,7 +12589,7 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
11978
12589
 
11979
12590
  // src/extract/calls/django-orm.ts
11980
12591
  init_cjs_shims();
11981
- var import_node_path48 = __toESM(require("path"), 1);
12592
+ var import_node_path49 = __toESM(require("path"), 1);
11982
12593
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
11983
12594
  var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11984
12595
  var import_types37 = require("@neat.is/types");
@@ -12048,7 +12659,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12048
12659
  const tree = parseSource7(makePyParser4(), file.content);
12049
12660
  const out = [];
12050
12661
  const seen = /* @__PURE__ */ new Set();
12051
- 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));
12052
12663
  walk4(tree.rootNode, (node) => {
12053
12664
  if (node.type !== "class_definition") return;
12054
12665
  if (!extendsDjangoModel(node)) return;
@@ -12066,7 +12677,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12066
12677
  kind: "sql-table",
12067
12678
  edgeType: "CALLS",
12068
12679
  confidenceKind: "verified-call-site",
12069
- 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) }
12070
12681
  });
12071
12682
  });
12072
12683
  return out;
@@ -12074,7 +12685,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
12074
12685
 
12075
12686
  // src/extract/calls/drizzle.ts
12076
12687
  init_cjs_shims();
12077
- var import_node_path49 = __toESM(require("path"), 1);
12688
+ var import_node_path50 = __toESM(require("path"), 1);
12078
12689
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
12079
12690
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
12080
12691
  var import_types38 = require("@neat.is/types");
@@ -12152,10 +12763,10 @@ function columnsFromObject(obj) {
12152
12763
  }
12153
12764
  function drizzleEndpointsFromFile(file, serviceDir) {
12154
12765
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12155
- 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);
12156
12767
  const out = [];
12157
12768
  const seen = /* @__PURE__ */ new Set();
12158
- const walk9 = (node) => {
12769
+ const walk10 = (node) => {
12159
12770
  if (node.type === "call_expression") {
12160
12771
  const fn = node.childForFieldName("function");
12161
12772
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -12175,7 +12786,7 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12175
12786
  confidenceKind: "structural",
12176
12787
  columns,
12177
12788
  evidence: {
12178
- file: import_node_path49.default.relative(serviceDir, file.path),
12789
+ file: import_node_path50.default.relative(serviceDir, file.path),
12179
12790
  line,
12180
12791
  snippet: snippet(file.content, line)
12181
12792
  }
@@ -12183,9 +12794,9 @@ function drizzleEndpointsFromFile(file, serviceDir) {
12183
12794
  }
12184
12795
  }
12185
12796
  }
12186
- for (const c of namedChildren4(node)) walk9(c);
12797
+ for (const c of namedChildren4(node)) walk10(c);
12187
12798
  };
12188
- walk9(tree.rootNode);
12799
+ walk10(tree.rootNode);
12189
12800
  return out;
12190
12801
  }
12191
12802
  function enclosingVarName(call) {
@@ -12207,7 +12818,7 @@ function enclosingVarName(call) {
12207
12818
  function collectDrizzleTables(root) {
12208
12819
  const tables = [];
12209
12820
  const varToTable = /* @__PURE__ */ new Map();
12210
- const walk9 = (node) => {
12821
+ const walk10 = (node) => {
12211
12822
  if (node.type === "call_expression") {
12212
12823
  const fn = node.childForFieldName("function");
12213
12824
  if (fn?.type === "identifier" && TABLE_BUILDERS.has(fn.text)) {
@@ -12222,9 +12833,9 @@ function collectDrizzleTables(root) {
12222
12833
  }
12223
12834
  }
12224
12835
  }
12225
- for (const c of namedChildren4(node)) walk9(c);
12836
+ for (const c of namedChildren4(node)) walk10(c);
12226
12837
  };
12227
- walk9(root);
12838
+ walk10(root);
12228
12839
  return { tables, varToTable };
12229
12840
  }
12230
12841
  function referencesTargetVar(call) {
@@ -12241,13 +12852,13 @@ function referencesTargetVar(call) {
12241
12852
  }
12242
12853
  function drizzleForeignKeys(file, serviceDir) {
12243
12854
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
12244
- 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);
12245
12856
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
12246
12857
  const out = [];
12247
12858
  const seen = /* @__PURE__ */ new Set();
12248
12859
  for (const table of tables) {
12249
12860
  if (!table.object) continue;
12250
- const walk9 = (node) => {
12861
+ const walk10 = (node) => {
12251
12862
  if (node.type === "call_expression") {
12252
12863
  const targetVar = referencesTargetVar(node);
12253
12864
  const parentTable = targetVar ? varToTable.get(targetVar) : void 0;
@@ -12260,7 +12871,7 @@ function drizzleForeignKeys(file, serviceDir) {
12260
12871
  childTable: table.tableName,
12261
12872
  parentTable,
12262
12873
  evidence: {
12263
- file: import_node_path49.default.relative(serviceDir, file.path),
12874
+ file: import_node_path50.default.relative(serviceDir, file.path),
12264
12875
  line,
12265
12876
  snippet: snippet(file.content, line)
12266
12877
  }
@@ -12268,16 +12879,16 @@ function drizzleForeignKeys(file, serviceDir) {
12268
12879
  }
12269
12880
  }
12270
12881
  }
12271
- for (const c of namedChildren4(node)) walk9(c);
12882
+ for (const c of namedChildren4(node)) walk10(c);
12272
12883
  };
12273
- walk9(table.object);
12884
+ walk10(table.object);
12274
12885
  }
12275
12886
  return out;
12276
12887
  }
12277
12888
 
12278
12889
  // src/extract/calls/prisma.ts
12279
12890
  init_cjs_shims();
12280
- var import_node_path50 = __toESM(require("path"), 1);
12891
+ var import_node_path51 = __toESM(require("path"), 1);
12281
12892
  var import_types39 = require("@neat.is/types");
12282
12893
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
12283
12894
  "Int",
@@ -12333,7 +12944,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12333
12944
  confidenceKind: "structural",
12334
12945
  columns: b.columns,
12335
12946
  evidence: {
12336
- file: import_node_path50.default.relative(serviceDir, file.path),
12947
+ file: import_node_path51.default.relative(serviceDir, file.path),
12337
12948
  line: b.startLine,
12338
12949
  snippet: snippet(content, b.startLine)
12339
12950
  }
@@ -12394,7 +13005,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
12394
13005
  }
12395
13006
  async function prismaColumnEndpoints(serviceDir) {
12396
13007
  const schemaPath = await findFirst(serviceDir, [
12397
- import_node_path50.default.join("prisma", "schema.prisma"),
13008
+ import_node_path51.default.join("prisma", "schema.prisma"),
12398
13009
  "schema.prisma"
12399
13010
  ]);
12400
13011
  if (!schemaPath) return [];
@@ -12469,7 +13080,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12469
13080
  childTable: current.table,
12470
13081
  parentTable,
12471
13082
  evidence: {
12472
- file: import_node_path50.default.relative(serviceDir, file.path),
13083
+ file: import_node_path51.default.relative(serviceDir, file.path),
12473
13084
  line: lineNo,
12474
13085
  snippet: snippet(content, lineNo)
12475
13086
  }
@@ -12484,7 +13095,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
12484
13095
  }
12485
13096
  async function prismaForeignKeys(serviceDir) {
12486
13097
  const schemaPath = await findFirst(serviceDir, [
12487
- import_node_path50.default.join("prisma", "schema.prisma"),
13098
+ import_node_path51.default.join("prisma", "schema.prisma"),
12488
13099
  "schema.prisma"
12489
13100
  ]);
12490
13101
  if (!schemaPath) return [];
@@ -12495,7 +13106,7 @@ async function prismaForeignKeys(serviceDir) {
12495
13106
 
12496
13107
  // src/extract/calls/activerecord.ts
12497
13108
  init_cjs_shims();
12498
- var import_node_path51 = __toESM(require("path"), 1);
13109
+ var import_node_path52 = __toESM(require("path"), 1);
12499
13110
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
12500
13111
  var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
12501
13112
  var import_types40 = require("@neat.is/types");
@@ -12703,7 +13314,7 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
12703
13314
  confidenceKind: "structural",
12704
13315
  ...table.columns.length > 0 ? { columns: table.columns } : {},
12705
13316
  evidence: {
12706
- file: import_node_path51.default.relative(serviceDir, file.path),
13317
+ file: import_node_path52.default.relative(serviceDir, file.path),
12707
13318
  line: table.line,
12708
13319
  snippet: snippet(file.content, table.line)
12709
13320
  }
@@ -12725,7 +13336,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
12725
13336
  childTable,
12726
13337
  parentTable,
12727
13338
  evidence: {
12728
- file: import_node_path51.default.relative(serviceDir, file.path),
13339
+ file: import_node_path52.default.relative(serviceDir, file.path),
12729
13340
  line,
12730
13341
  snippet: snippet(file.content, line)
12731
13342
  }
@@ -12808,7 +13419,7 @@ function railsModelEndpointsFromFile(file, serviceDir) {
12808
13419
  edgeType: "CALLS",
12809
13420
  confidenceKind: "verified-call-site",
12810
13421
  evidence: {
12811
- file: import_node_path51.default.relative(serviceDir, file.path),
13422
+ file: import_node_path52.default.relative(serviceDir, file.path),
12812
13423
  line,
12813
13424
  snippet: snippet(file.content, line)
12814
13425
  }
@@ -12845,7 +13456,7 @@ function railsModelForeignKeys(file, serviceDir) {
12845
13456
  childTable,
12846
13457
  parentTable,
12847
13458
  evidence: {
12848
- file: import_node_path51.default.relative(serviceDir, file.path),
13459
+ file: import_node_path52.default.relative(serviceDir, file.path),
12849
13460
  line,
12850
13461
  snippet: snippet(file.content, line)
12851
13462
  }
@@ -12857,7 +13468,7 @@ function railsModelForeignKeys(file, serviceDir) {
12857
13468
 
12858
13469
  // src/extract/calls/eloquent.ts
12859
13470
  init_cjs_shims();
12860
- var import_node_path52 = __toESM(require("path"), 1);
13471
+ var import_node_path53 = __toESM(require("path"), 1);
12861
13472
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
12862
13473
  var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12863
13474
  var import_types41 = require("@neat.is/types");
@@ -13114,7 +13725,7 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
13114
13725
  confidenceKind: "structural",
13115
13726
  ...columns.length > 0 ? { columns } : {},
13116
13727
  evidence: {
13117
- file: import_node_path52.default.relative(serviceDir, file.path),
13728
+ file: import_node_path53.default.relative(serviceDir, file.path),
13118
13729
  line: bp.line,
13119
13730
  snippet: snippet(file.content, bp.line)
13120
13731
  }
@@ -13136,7 +13747,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
13136
13747
  childTable,
13137
13748
  parentTable,
13138
13749
  evidence: {
13139
- file: import_node_path52.default.relative(serviceDir, file.path),
13750
+ file: import_node_path53.default.relative(serviceDir, file.path),
13140
13751
  line,
13141
13752
  snippet: snippet(file.content, line)
13142
13753
  }
@@ -13256,7 +13867,7 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
13256
13867
  edgeType: "CALLS",
13257
13868
  confidenceKind: "verified-call-site",
13258
13869
  evidence: {
13259
- file: import_node_path52.default.relative(serviceDir, file.path),
13870
+ file: import_node_path53.default.relative(serviceDir, file.path),
13260
13871
  line,
13261
13872
  snippet: snippet(file.content, line)
13262
13873
  }
@@ -13292,7 +13903,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13292
13903
  childTable,
13293
13904
  parentTable,
13294
13905
  evidence: {
13295
- file: import_node_path52.default.relative(serviceDir, file.path),
13906
+ file: import_node_path53.default.relative(serviceDir, file.path),
13296
13907
  line,
13297
13908
  snippet: snippet(file.content, line)
13298
13909
  }
@@ -13304,7 +13915,7 @@ function laravelModelForeignKeys(file, serviceDir) {
13304
13915
 
13305
13916
  // src/extract/calls/go.ts
13306
13917
  init_cjs_shims();
13307
- var import_node_path55 = __toESM(require("path"), 1);
13918
+ var import_node_path56 = __toESM(require("path"), 1);
13308
13919
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
13309
13920
  var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
13310
13921
  var import_types42 = require("@neat.is/types");
@@ -13379,7 +13990,7 @@ function firstStringLiteralArg(argsNode) {
13379
13990
  return null;
13380
13991
  }
13381
13992
  function goSqlEndpointsFromFile(file, serviceDir) {
13382
- if (import_node_path55.default.extname(file.path) !== ".go") return [];
13993
+ if (import_node_path56.default.extname(file.path) !== ".go") return [];
13383
13994
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
13384
13995
  const tree = parseSource10(makeGoParser3(), file.content);
13385
13996
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -13408,7 +14019,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13408
14019
  confidenceKind: "verified-call-site",
13409
14020
  ...columns.length > 0 ? { columns } : {},
13410
14021
  evidence: {
13411
- file: toPosix(import_node_path55.default.relative(serviceDir, file.path)),
14022
+ file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
13412
14023
  line,
13413
14024
  snippet: snippet(file.content, line)
13414
14025
  }
@@ -13419,7 +14030,7 @@ function goSqlEndpointsFromFile(file, serviceDir) {
13419
14030
 
13420
14031
  // src/extract/calls/gorm.ts
13421
14032
  init_cjs_shims();
13422
- var import_node_path56 = __toESM(require("path"), 1);
14033
+ var import_node_path57 = __toESM(require("path"), 1);
13423
14034
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
13424
14035
  var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
13425
14036
  var import_types43 = require("@neat.is/types");
@@ -13852,7 +14463,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
13852
14463
  seen.delete(struct.name);
13853
14464
  }
13854
14465
  function gormEndpointsFromFile(file, serviceDir) {
13855
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14466
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
13856
14467
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13857
14468
  const tree = parseSource11(makeGoParser4(), file.content);
13858
14469
  const { structs, models, tableFor } = analyze(tree);
@@ -13874,7 +14485,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13874
14485
  confidenceKind: "structural",
13875
14486
  ...columns.length > 0 ? { columns } : {},
13876
14487
  evidence: {
13877
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14488
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
13878
14489
  line: struct.line,
13879
14490
  snippet: snippet(file.content, struct.line)
13880
14491
  }
@@ -13883,7 +14494,7 @@ function gormEndpointsFromFile(file, serviceDir) {
13883
14494
  return out;
13884
14495
  }
13885
14496
  function gormForeignKeys(file, serviceDir) {
13886
- if (import_node_path56.default.extname(file.path) !== ".go") return [];
14497
+ if (import_node_path57.default.extname(file.path) !== ".go") return [];
13887
14498
  if (!GORM_IMPORT_RE.test(file.content)) return [];
13888
14499
  const tree = parseSource11(makeGoParser4(), file.content);
13889
14500
  const { structs, models, tableFor } = analyze(tree);
@@ -13898,7 +14509,7 @@ function gormForeignKeys(file, serviceDir) {
13898
14509
  childTable,
13899
14510
  parentTable,
13900
14511
  evidence: {
13901
- file: toPosix(import_node_path56.default.relative(serviceDir, file.path)),
14512
+ file: toPosix(import_node_path57.default.relative(serviceDir, file.path)),
13902
14513
  line,
13903
14514
  snippet: snippet(file.content, line)
13904
14515
  }
@@ -13933,15 +14544,131 @@ function gormForeignKeys(file, serviceDir) {
13933
14544
  return out;
13934
14545
  }
13935
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
+
13936
14663
  // src/extract/calls/index.ts
13937
14664
  function edgeTypeFromEndpoint(ep) {
13938
14665
  switch (ep.edgeType) {
13939
14666
  case "PUBLISHES_TO":
13940
- return import_types44.EdgeType.PUBLISHES_TO;
14667
+ return import_types45.EdgeType.PUBLISHES_TO;
13941
14668
  case "CONSUMES_FROM":
13942
- return import_types44.EdgeType.CONSUMES_FROM;
14669
+ return import_types45.EdgeType.CONSUMES_FROM;
13943
14670
  default:
13944
- return import_types44.EdgeType.CALLS;
14671
+ return import_types45.EdgeType.CALLS;
13945
14672
  }
13946
14673
  }
13947
14674
  function isAwsKind(kind) {
@@ -13991,6 +14718,11 @@ async function addExternalEndpointEdges(graph, services) {
13991
14718
  } catch (err) {
13992
14719
  recordExtractionError("laravel eloquent extraction", file.path, err);
13993
14720
  }
14721
+ try {
14722
+ endpoints.push(...efcoreEndpointsFromFile(file, service.dir));
14723
+ } catch (err) {
14724
+ recordExtractionError("efcore data-axis extraction", file.path, err);
14725
+ }
13994
14726
  }
13995
14727
  endpoints.push(...await mongooseCrossFileEndpoints(maskedFiles, service.dir));
13996
14728
  endpoints.push(...pythonOrmCrossFileEndpoints(maskedFiles, service.dir));
@@ -14001,7 +14733,7 @@ async function addExternalEndpointEdges(graph, services) {
14001
14733
  if (!graph.hasNode(ep.infraId)) {
14002
14734
  const node = {
14003
14735
  id: ep.infraId,
14004
- type: import_types44.NodeType.InfraNode,
14736
+ type: import_types45.NodeType.InfraNode,
14005
14737
  name: ep.name,
14006
14738
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
14007
14739
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -14014,21 +14746,21 @@ async function addExternalEndpointEdges(graph, services) {
14014
14746
  }
14015
14747
  if (ep.columns && ep.columns.length > 0) {
14016
14748
  const node = graph.getNodeAttributes(ep.infraId);
14017
- if (node.type === import_types44.NodeType.InfraNode) {
14749
+ if (node.type === import_types45.NodeType.InfraNode) {
14018
14750
  graph.replaceNodeAttributes(ep.infraId, {
14019
14751
  ...node,
14020
14752
  columns: foldColumns(
14021
14753
  node.columns,
14022
14754
  ep.columns,
14023
- import_types44.Provenance.EXTRACTED,
14024
- (0, import_types44.confidenceForExtracted)(ep.confidenceKind)
14755
+ import_types45.Provenance.EXTRACTED,
14756
+ (0, import_types45.confidenceForExtracted)(ep.confidenceKind)
14025
14757
  )
14026
14758
  });
14027
14759
  }
14028
14760
  }
14029
14761
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
14030
14762
  const node = graph.getNodeAttributes(ep.infraId);
14031
- if (node.type === import_types44.NodeType.InfraNode) {
14763
+ if (node.type === import_types45.NodeType.InfraNode) {
14032
14764
  graph.replaceNodeAttributes(ep.infraId, {
14033
14765
  ...node,
14034
14766
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -14036,7 +14768,7 @@ async function addExternalEndpointEdges(graph, services) {
14036
14768
  }
14037
14769
  }
14038
14770
  const edgeType = edgeTypeFromEndpoint(ep);
14039
- const confidence = (0, import_types44.confidenceForExtracted)(ep.confidenceKind);
14771
+ const confidence = (0, import_types45.confidenceForExtracted)(ep.confidenceKind);
14040
14772
  const relFile = toPosix(ep.evidence.file);
14041
14773
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
14042
14774
  graph,
@@ -14046,7 +14778,7 @@ async function addExternalEndpointEdges(graph, services) {
14046
14778
  );
14047
14779
  nodesAdded += n;
14048
14780
  edgesAdded += e;
14049
- if (!(0, import_types44.passesExtractedFloor)(confidence)) {
14781
+ if (!(0, import_types45.passesExtractedFloor)(confidence)) {
14050
14782
  noteExtractedDropped({
14051
14783
  source: fileNodeId,
14052
14784
  target: ep.infraId,
@@ -14066,7 +14798,7 @@ async function addExternalEndpointEdges(graph, services) {
14066
14798
  source: fileNodeId,
14067
14799
  target: ep.infraId,
14068
14800
  type: edgeType,
14069
- provenance: import_types44.Provenance.EXTRACTED,
14801
+ provenance: import_types45.Provenance.EXTRACTED,
14070
14802
  confidence,
14071
14803
  evidence: ep.evidence
14072
14804
  };
@@ -14089,7 +14821,7 @@ async function addCallEdges(graph, services) {
14089
14821
 
14090
14822
  // src/extract/table-edges.ts
14091
14823
  init_cjs_shims();
14092
- var import_types45 = require("@neat.is/types");
14824
+ var import_types46 = require("@neat.is/types");
14093
14825
  async function addTableEdges(graph, services) {
14094
14826
  let nodesAdded = 0;
14095
14827
  let edgesAdded = 0;
@@ -14117,20 +14849,20 @@ async function addTableEdges(graph, services) {
14117
14849
  }
14118
14850
  refs.push(...modelRefs);
14119
14851
  for (const ref of refs) {
14120
- const childId = (0, import_types45.infraId)("sql-table", ref.childTable);
14121
- 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);
14122
14854
  if (childId === parentId) continue;
14123
14855
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
14124
14856
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
14125
- 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);
14126
14858
  if (graph.hasEdge(edgeId)) continue;
14127
14859
  const edge = {
14128
14860
  id: edgeId,
14129
14861
  source: childId,
14130
14862
  target: parentId,
14131
- type: import_types45.EdgeType.REFERENCES,
14132
- provenance: import_types45.Provenance.EXTRACTED,
14133
- 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"),
14134
14866
  evidence: ref.evidence
14135
14867
  };
14136
14868
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -14143,7 +14875,7 @@ function ensureTableNode(graph, id, name) {
14143
14875
  if (graph.hasNode(id)) return 0;
14144
14876
  const node = {
14145
14877
  id,
14146
- type: import_types45.NodeType.InfraNode,
14878
+ type: import_types46.NodeType.InfraNode,
14147
14879
  name,
14148
14880
  provider: "self",
14149
14881
  kind: "sql-table"
@@ -14157,16 +14889,16 @@ init_cjs_shims();
14157
14889
 
14158
14890
  // src/extract/infra/docker-compose.ts
14159
14891
  init_cjs_shims();
14160
- var import_node_path57 = __toESM(require("path"), 1);
14161
- var import_types47 = require("@neat.is/types");
14892
+ var import_node_path59 = __toESM(require("path"), 1);
14893
+ var import_types48 = require("@neat.is/types");
14162
14894
 
14163
14895
  // src/extract/infra/shared.ts
14164
14896
  init_cjs_shims();
14165
- var import_types46 = require("@neat.is/types");
14897
+ var import_types47 = require("@neat.is/types");
14166
14898
  function makeInfraNode(kind, name, provider = "self", extras) {
14167
14899
  return {
14168
- id: (0, import_types46.infraId)(kind, name),
14169
- type: import_types46.NodeType.InfraNode,
14900
+ id: (0, import_types47.infraId)(kind, name),
14901
+ type: import_types47.NodeType.InfraNode,
14170
14902
  name,
14171
14903
  provider,
14172
14904
  kind,
@@ -14210,8 +14942,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
14210
14942
  source: anchorId,
14211
14943
  target: node.id,
14212
14944
  type: edgeType,
14213
- provenance: import_types46.Provenance.EXTRACTED,
14214
- confidence: (0, import_types46.confidenceForExtracted)("structural"),
14945
+ provenance: import_types47.Provenance.EXTRACTED,
14946
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
14215
14947
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14216
14948
  };
14217
14949
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14228,7 +14960,7 @@ function dependsOnList(value) {
14228
14960
  }
14229
14961
  function serviceNameToServiceNode(name, services) {
14230
14962
  for (const s of services) {
14231
- 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;
14232
14964
  }
14233
14965
  return null;
14234
14966
  }
@@ -14237,7 +14969,7 @@ async function addComposeInfra(graph, scanPath, services) {
14237
14969
  let edgesAdded = 0;
14238
14970
  let composePath = null;
14239
14971
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
14240
- const abs = import_node_path57.default.join(scanPath, name);
14972
+ const abs = import_node_path59.default.join(scanPath, name);
14241
14973
  if (await exists2(abs)) {
14242
14974
  composePath = abs;
14243
14975
  break;
@@ -14250,13 +14982,13 @@ async function addComposeInfra(graph, scanPath, services) {
14250
14982
  } catch (err) {
14251
14983
  recordExtractionError(
14252
14984
  "infra docker-compose",
14253
- import_node_path57.default.relative(scanPath, composePath),
14985
+ import_node_path59.default.relative(scanPath, composePath),
14254
14986
  err
14255
14987
  );
14256
14988
  return { nodesAdded, edgesAdded };
14257
14989
  }
14258
14990
  if (!compose?.services) return { nodesAdded, edgesAdded };
14259
- 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("/");
14260
14992
  const composeNameToNodeId = /* @__PURE__ */ new Map();
14261
14993
  for (const [composeName, svc] of Object.entries(compose.services)) {
14262
14994
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -14278,15 +15010,15 @@ async function addComposeInfra(graph, scanPath, services) {
14278
15010
  for (const dep of dependsOnList(svc.depends_on)) {
14279
15011
  const targetId = composeNameToNodeId.get(dep);
14280
15012
  if (!targetId) continue;
14281
- 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);
14282
15014
  if (graph.hasEdge(edgeId)) continue;
14283
15015
  const edge = {
14284
15016
  id: edgeId,
14285
15017
  source: sourceId,
14286
15018
  target: targetId,
14287
- type: import_types47.EdgeType.DEPENDS_ON,
14288
- provenance: import_types47.Provenance.EXTRACTED,
14289
- 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"),
14290
15022
  evidence: { file: evidenceFile }
14291
15023
  };
14292
15024
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14298,9 +15030,9 @@ async function addComposeInfra(graph, scanPath, services) {
14298
15030
 
14299
15031
  // src/extract/infra/dockerfile.ts
14300
15032
  init_cjs_shims();
14301
- var import_node_path58 = __toESM(require("path"), 1);
14302
- var import_node_fs26 = require("fs");
14303
- 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");
14304
15036
  function readDockerfile(content) {
14305
15037
  let image = null;
14306
15038
  const ports = [];
@@ -14329,15 +15061,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14329
15061
  let nodesAdded = 0;
14330
15062
  let edgesAdded = 0;
14331
15063
  for (const service of services) {
14332
- const dockerfilePath = import_node_path58.default.join(service.dir, "Dockerfile");
15064
+ const dockerfilePath = import_node_path60.default.join(service.dir, "Dockerfile");
14333
15065
  if (!await exists2(dockerfilePath)) continue;
14334
15066
  let content;
14335
15067
  try {
14336
- content = await import_node_fs26.promises.readFile(dockerfilePath, "utf8");
15068
+ content = await import_node_fs27.promises.readFile(dockerfilePath, "utf8");
14337
15069
  } catch (err) {
14338
15070
  recordExtractionError(
14339
15071
  "infra dockerfile",
14340
- import_node_path58.default.relative(scanPath, dockerfilePath),
15072
+ import_node_path60.default.relative(scanPath, dockerfilePath),
14341
15073
  err
14342
15074
  );
14343
15075
  continue;
@@ -14349,8 +15081,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14349
15081
  graph.addNode(node.id, node);
14350
15082
  nodesAdded++;
14351
15083
  }
14352
- const relDockerfile = toPosix(import_node_path58.default.relative(service.dir, dockerfilePath));
14353
- 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));
14354
15086
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14355
15087
  graph,
14356
15088
  service.pkg.name,
@@ -14359,15 +15091,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14359
15091
  );
14360
15092
  nodesAdded += fn;
14361
15093
  edgesAdded += fe;
14362
- 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);
14363
15095
  if (!graph.hasEdge(edgeId)) {
14364
15096
  const edge = {
14365
15097
  id: edgeId,
14366
15098
  source: fileNodeId,
14367
15099
  target: node.id,
14368
- type: import_types48.EdgeType.RUNS_ON,
14369
- provenance: import_types48.Provenance.EXTRACTED,
14370
- 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"),
14371
15103
  evidence: {
14372
15104
  file: evidenceFile,
14373
15105
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -14382,15 +15114,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14382
15114
  graph.addNode(portNode.id, portNode);
14383
15115
  nodesAdded++;
14384
15116
  }
14385
- 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);
14386
15118
  if (graph.hasEdge(portEdgeId)) continue;
14387
15119
  const portEdge = {
14388
15120
  id: portEdgeId,
14389
15121
  source: fileNodeId,
14390
15122
  target: portNode.id,
14391
- type: import_types48.EdgeType.CONNECTS_TO,
14392
- provenance: import_types48.Provenance.EXTRACTED,
14393
- 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"),
14394
15126
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
14395
15127
  };
14396
15128
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -14402,23 +15134,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
14402
15134
 
14403
15135
  // src/extract/infra/terraform.ts
14404
15136
  init_cjs_shims();
14405
- var import_node_fs27 = require("fs");
14406
- var import_node_path59 = __toESM(require("path"), 1);
14407
- 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");
14408
15140
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
14409
15141
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
14410
15142
  async function walkTfFiles(start, depth = 0, max = 5) {
14411
15143
  if (depth > max) return [];
14412
15144
  const out = [];
14413
- 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(() => []);
14414
15146
  for (const entry of entries) {
14415
15147
  if (entry.isDirectory()) {
14416
15148
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
14417
- const child = import_node_path59.default.join(start, entry.name);
15149
+ const child = import_node_path61.default.join(start, entry.name);
14418
15150
  if (await isPythonVenvDir(child)) continue;
14419
15151
  out.push(...await walkTfFiles(child, depth + 1, max));
14420
15152
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
14421
- out.push(import_node_path59.default.join(start, entry.name));
15153
+ out.push(import_node_path61.default.join(start, entry.name));
14422
15154
  }
14423
15155
  }
14424
15156
  return out;
@@ -14449,8 +15181,8 @@ async function addTerraformResources(graph, scanPath) {
14449
15181
  let edgesAdded = 0;
14450
15182
  const files = await walkTfFiles(scanPath);
14451
15183
  for (const file of files) {
14452
- const content = await import_node_fs27.promises.readFile(file, "utf8");
14453
- 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));
14454
15186
  const resources = [];
14455
15187
  const byKey = /* @__PURE__ */ new Map();
14456
15188
  RESOURCE_RE.lastIndex = 0;
@@ -14485,16 +15217,16 @@ async function addTerraformResources(graph, scanPath) {
14485
15217
  if (!target) continue;
14486
15218
  if (seen.has(target.nodeId)) continue;
14487
15219
  seen.add(target.nodeId);
14488
- 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);
14489
15221
  if (graph.hasEdge(edgeId)) continue;
14490
15222
  const line = lineAt2(content, resource.bodyOffset + ref.index);
14491
15223
  const edge = {
14492
15224
  id: edgeId,
14493
15225
  source: resource.nodeId,
14494
15226
  target: target.nodeId,
14495
- type: import_types49.EdgeType.DEPENDS_ON,
14496
- provenance: import_types49.Provenance.EXTRACTED,
14497
- 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"),
14498
15230
  evidence: { file: evidenceFile, line, snippet: key }
14499
15231
  };
14500
15232
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14507,8 +15239,8 @@ async function addTerraformResources(graph, scanPath) {
14507
15239
 
14508
15240
  // src/extract/infra/k8s.ts
14509
15241
  init_cjs_shims();
14510
- var import_node_fs28 = require("fs");
14511
- var import_node_path60 = __toESM(require("path"), 1);
15242
+ var import_node_fs29 = require("fs");
15243
+ var import_node_path62 = __toESM(require("path"), 1);
14512
15244
  var import_yaml3 = require("yaml");
14513
15245
  var K8S_KIND_TO_INFRA_KIND = {
14514
15246
  Service: "k8s-service",
@@ -14522,15 +15254,15 @@ var K8S_KIND_TO_INFRA_KIND = {
14522
15254
  async function walkYamlFiles2(start, depth = 0, max = 5) {
14523
15255
  if (depth > max) return [];
14524
15256
  const out = [];
14525
- 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(() => []);
14526
15258
  for (const entry of entries) {
14527
15259
  if (entry.isDirectory()) {
14528
15260
  if (IGNORED_DIRS.has(entry.name)) continue;
14529
- const child = import_node_path60.default.join(start, entry.name);
15261
+ const child = import_node_path62.default.join(start, entry.name);
14530
15262
  if (await isPythonVenvDir(child)) continue;
14531
15263
  out.push(...await walkYamlFiles2(child, depth + 1, max));
14532
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path60.default.extname(entry.name))) {
14533
- 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));
14534
15266
  }
14535
15267
  }
14536
15268
  return out;
@@ -14539,7 +15271,7 @@ async function addK8sResources(graph, scanPath) {
14539
15271
  let nodesAdded = 0;
14540
15272
  const files = await walkYamlFiles2(scanPath);
14541
15273
  for (const file of files) {
14542
- const content = await import_node_fs28.promises.readFile(file, "utf8");
15274
+ const content = await import_node_fs29.promises.readFile(file, "utf8");
14543
15275
  let docs;
14544
15276
  try {
14545
15277
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -14563,16 +15295,16 @@ async function addK8sResources(graph, scanPath) {
14563
15295
 
14564
15296
  // src/extract/infra/cloudflare.ts
14565
15297
  init_cjs_shims();
14566
- var import_node_fs29 = require("fs");
14567
- var import_node_path61 = __toESM(require("path"), 1);
15298
+ var import_node_fs30 = require("fs");
15299
+ var import_node_path63 = __toESM(require("path"), 1);
14568
15300
  var import_smol_toml3 = require("smol-toml");
14569
- var import_types50 = require("@neat.is/types");
15301
+ var import_types51 = require("@neat.is/types");
14570
15302
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
14571
15303
  async function readWranglerConfig(dir) {
14572
15304
  for (const filename of WRANGLER_FILENAMES) {
14573
- const abs = import_node_path61.default.join(dir, filename);
15305
+ const abs = import_node_path63.default.join(dir, filename);
14574
15306
  if (!await exists2(abs)) continue;
14575
- const raw = await import_node_fs29.promises.readFile(abs, "utf8");
15307
+ const raw = await import_node_fs30.promises.readFile(abs, "utf8");
14576
15308
  const config = filename === "wrangler.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14577
15309
  return { config, relFile: filename, raw };
14578
15310
  }
@@ -14614,8 +15346,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
14614
15346
  source: anchorId,
14615
15347
  target: node.id,
14616
15348
  type: edgeType,
14617
- provenance: import_types50.Provenance.EXTRACTED,
14618
- confidence: (0, import_types50.confidenceForExtracted)("structural"),
15349
+ provenance: import_types51.Provenance.EXTRACTED,
15350
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14619
15351
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
14620
15352
  };
14621
15353
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14633,11 +15365,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14633
15365
  try {
14634
15366
  read = await readWranglerConfig(service.dir);
14635
15367
  } catch (err) {
14636
- 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);
14637
15369
  continue;
14638
15370
  }
14639
15371
  if (!read || !read.config.name) continue;
14640
- 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)));
14641
15373
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
14642
15374
  }
14643
15375
  for (const worker of discovered) {
@@ -14649,7 +15381,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14649
15381
  }
14650
15382
  let anchorId = service.node.id;
14651
15383
  if (config.main) {
14652
- const entryRelPath = toPosix(import_node_path61.default.normalize(config.main));
15384
+ const entryRelPath = toPosix(import_node_path63.default.normalize(config.main));
14653
15385
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
14654
15386
  graph,
14655
15387
  service.pkg.name,
@@ -14676,15 +15408,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14676
15408
  nodesAdded++;
14677
15409
  }
14678
15410
  if (runtimeNode.id !== anchorId) {
14679
- 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);
14680
15412
  if (!graph.hasEdge(runsOnId)) {
14681
15413
  const edge = {
14682
15414
  id: runsOnId,
14683
15415
  source: anchorId,
14684
15416
  target: runtimeNode.id,
14685
- type: import_types50.EdgeType.RUNS_ON,
14686
- provenance: import_types50.Provenance.EXTRACTED,
14687
- 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"),
14688
15420
  evidence: {
14689
15421
  file: evidenceFile,
14690
15422
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -14698,7 +15430,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14698
15430
  const result = addResourceEdge(
14699
15431
  graph,
14700
15432
  anchorId,
14701
- import_types50.EdgeType.CONNECTS_TO,
15433
+ import_types51.EdgeType.CONNECTS_TO,
14702
15434
  "cloudflare-route",
14703
15435
  route,
14704
15436
  evidenceFile,
@@ -14722,7 +15454,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14722
15454
  const result = addResourceEdge(
14723
15455
  graph,
14724
15456
  anchorId,
14725
- import_types50.EdgeType.DEPENDS_ON,
15457
+ import_types51.EdgeType.DEPENDS_ON,
14726
15458
  group.kind,
14727
15459
  name,
14728
15460
  evidenceFile,
@@ -14736,7 +15468,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14736
15468
  const result = addResourceEdge(
14737
15469
  graph,
14738
15470
  anchorId,
14739
- import_types50.EdgeType.DEPENDS_ON,
15471
+ import_types51.EdgeType.DEPENDS_ON,
14740
15472
  "cloudflare-cron",
14741
15473
  cron,
14742
15474
  evidenceFile,
@@ -14749,7 +15481,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14749
15481
  const result = addResourceEdge(
14750
15482
  graph,
14751
15483
  anchorId,
14752
- import_types50.EdgeType.DEPENDS_ON,
15484
+ import_types51.EdgeType.DEPENDS_ON,
14753
15485
  "cloudflare-env-var",
14754
15486
  varName,
14755
15487
  evidenceFile,
@@ -14762,15 +15494,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14762
15494
  if (!svc.service) continue;
14763
15495
  const target = workerIndex.get(svc.service);
14764
15496
  if (target && target.anchorId !== anchorId) {
14765
- 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);
14766
15498
  if (!graph.hasEdge(edgeId)) {
14767
15499
  const edge = {
14768
15500
  id: edgeId,
14769
15501
  source: anchorId,
14770
15502
  target: target.anchorId,
14771
- type: import_types50.EdgeType.CALLS,
14772
- provenance: import_types50.Provenance.EXTRACTED,
14773
- 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"),
14774
15506
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
14775
15507
  };
14776
15508
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -14781,7 +15513,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14781
15513
  const result = addResourceEdge(
14782
15514
  graph,
14783
15515
  anchorId,
14784
- import_types50.EdgeType.DEPENDS_ON,
15516
+ import_types51.EdgeType.DEPENDS_ON,
14785
15517
  "cloudflare-service-binding",
14786
15518
  svc.service,
14787
15519
  evidenceFile,
@@ -14796,24 +15528,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
14796
15528
 
14797
15529
  // src/extract/infra/vercel.ts
14798
15530
  init_cjs_shims();
14799
- var import_node_fs30 = require("fs");
14800
- var import_node_path62 = __toESM(require("path"), 1);
14801
- 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");
14802
15534
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
14803
15535
  async function readVercelConfig(dir) {
14804
15536
  for (const filename of VERCEL_CONFIG_FILENAMES) {
14805
- const abs = import_node_path62.default.join(dir, filename);
15537
+ const abs = import_node_path64.default.join(dir, filename);
14806
15538
  if (!await exists2(abs)) continue;
14807
- const raw = await import_node_fs30.promises.readFile(abs, "utf8");
15539
+ const raw = await import_node_fs31.promises.readFile(abs, "utf8");
14808
15540
  const config = JSON.parse(maskCommentsInSource(raw));
14809
15541
  return { config, relFile: filename, raw };
14810
15542
  }
14811
15543
  return null;
14812
15544
  }
14813
15545
  async function readLinkedProjectName(dir) {
14814
- const abs = import_node_path62.default.join(dir, ".vercel", "project.json");
15546
+ const abs = import_node_path64.default.join(dir, ".vercel", "project.json");
14815
15547
  if (!await exists2(abs)) return void 0;
14816
- 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"));
14817
15549
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
14818
15550
  }
14819
15551
  function routeSource(route) {
@@ -14829,7 +15561,7 @@ async function addVercelServices(graph, services, scanPath) {
14829
15561
  read = await readVercelConfig(service.dir);
14830
15562
  projectName = await readLinkedProjectName(service.dir);
14831
15563
  } catch (err) {
14832
- 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);
14833
15565
  continue;
14834
15566
  }
14835
15567
  if (!read && !projectName) continue;
@@ -14845,7 +15577,7 @@ async function addVercelServices(graph, services, scanPath) {
14845
15577
  const anchorId = service.node.id;
14846
15578
  if (!read) continue;
14847
15579
  const { config, relFile, raw } = read;
14848
- 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)));
14849
15581
  const add = (edgeType, kind, name) => {
14850
15582
  if (!name) return;
14851
15583
  const result = emitPlatformResourceEdge(
@@ -14861,12 +15593,12 @@ async function addVercelServices(graph, services, scanPath) {
14861
15593
  nodesAdded += result.nodesAdded;
14862
15594
  edgesAdded += result.edgesAdded;
14863
15595
  };
14864
- add(import_types51.EdgeType.RUNS_ON, "vercel", "vercel");
14865
- for (const cron of config.crons ?? []) add(import_types51.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14866
- for (const varName of Object.keys(config.env ?? {})) add(import_types51.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14867
- 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);
14868
15600
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
14869
- add(import_types51.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
15601
+ add(import_types52.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14870
15602
  }
14871
15603
  }
14872
15604
  return { nodesAdded, edgesAdded };
@@ -14874,16 +15606,16 @@ async function addVercelServices(graph, services, scanPath) {
14874
15606
 
14875
15607
  // src/extract/infra/railway.ts
14876
15608
  init_cjs_shims();
14877
- var import_node_fs31 = require("fs");
14878
- var import_node_path63 = __toESM(require("path"), 1);
15609
+ var import_node_fs32 = require("fs");
15610
+ var import_node_path65 = __toESM(require("path"), 1);
14879
15611
  var import_smol_toml4 = require("smol-toml");
14880
- var import_types52 = require("@neat.is/types");
15612
+ var import_types53 = require("@neat.is/types");
14881
15613
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
14882
15614
  async function readRailwayConfig(dir) {
14883
15615
  for (const filename of RAILWAY_FILENAMES) {
14884
- const abs = import_node_path63.default.join(dir, filename);
15616
+ const abs = import_node_path65.default.join(dir, filename);
14885
15617
  if (!await exists2(abs)) continue;
14886
- const raw = await import_node_fs31.promises.readFile(abs, "utf8");
15618
+ const raw = await import_node_fs32.promises.readFile(abs, "utf8");
14887
15619
  const config = filename === "railway.toml" ? (0, import_smol_toml4.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
14888
15620
  return { config, relFile: filename, raw };
14889
15621
  }
@@ -14897,7 +15629,7 @@ async function addRailwayServices(graph, services, scanPath) {
14897
15629
  try {
14898
15630
  read = await readRailwayConfig(service.dir);
14899
15631
  } catch (err) {
14900
- 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);
14901
15633
  continue;
14902
15634
  }
14903
15635
  if (!read) continue;
@@ -14907,7 +15639,7 @@ async function addRailwayServices(graph, services, scanPath) {
14907
15639
  }
14908
15640
  const anchorId = service.node.id;
14909
15641
  const { config, relFile, raw } = read;
14910
- 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)));
14911
15643
  const add = (edgeType, kind, name) => {
14912
15644
  if (!name) return;
14913
15645
  const result = emitPlatformResourceEdge(
@@ -14923,24 +15655,24 @@ async function addRailwayServices(graph, services, scanPath) {
14923
15655
  nodesAdded += result.nodesAdded;
14924
15656
  edgesAdded += result.edgesAdded;
14925
15657
  };
14926
- add(import_types52.EdgeType.RUNS_ON, "railway", "railway");
14927
- add(import_types52.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14928
- 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);
14929
15661
  }
14930
15662
  return { nodesAdded, edgesAdded };
14931
15663
  }
14932
15664
 
14933
15665
  // src/extract/infra/supabase.ts
14934
15666
  init_cjs_shims();
14935
- var import_node_fs32 = require("fs");
14936
- var import_node_path64 = __toESM(require("path"), 1);
15667
+ var import_node_fs33 = require("fs");
15668
+ var import_node_path66 = __toESM(require("path"), 1);
14937
15669
  var import_smol_toml5 = require("smol-toml");
14938
- var import_types53 = require("@neat.is/types");
15670
+ var import_types54 = require("@neat.is/types");
14939
15671
  async function readSupabaseConfig(dir) {
14940
- const relFile = import_node_path64.default.join("supabase", "config.toml");
14941
- 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);
14942
15674
  if (!await exists2(abs)) return null;
14943
- const raw = await import_node_fs32.promises.readFile(abs, "utf8");
15675
+ const raw = await import_node_fs33.promises.readFile(abs, "utf8");
14944
15676
  const config = (0, import_smol_toml5.parse)(raw);
14945
15677
  return { config, relFile, raw };
14946
15678
  }
@@ -14952,7 +15684,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14952
15684
  try {
14953
15685
  read = await readSupabaseConfig(service.dir);
14954
15686
  } catch (err) {
14955
- 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);
14956
15688
  continue;
14957
15689
  }
14958
15690
  if (!read) continue;
@@ -14967,7 +15699,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
14967
15699
  });
14968
15700
  }
14969
15701
  const anchorId = service.node.id;
14970
- 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)));
14971
15703
  const add = (edgeType, kind, name) => {
14972
15704
  if (!name) return;
14973
15705
  const result = emitPlatformResourceEdge(
@@ -14983,10 +15715,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
14983
15715
  nodesAdded += result.nodesAdded;
14984
15716
  edgesAdded += result.edgesAdded;
14985
15717
  };
14986
- add(import_types53.EdgeType.RUNS_ON, "supabase", "supabase");
14987
- for (const fn of Object.keys(config.functions ?? {})) add(import_types53.EdgeType.DEPENDS_ON, "supabase-function", fn);
14988
- if (config.storage) add(import_types53.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14989
- 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");
14990
15722
  }
14991
15723
  return { nodesAdded, edgesAdded };
14992
15724
  }
@@ -15009,14 +15741,14 @@ async function addInfra(graph, scanPath, services) {
15009
15741
 
15010
15742
  // src/extract/zod-shapes.ts
15011
15743
  init_cjs_shims();
15012
- var import_node_path65 = __toESM(require("path"), 1);
15013
- 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);
15014
15746
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
15015
- var import_types54 = require("@neat.is/types");
15747
+ var import_types55 = require("@neat.is/types");
15016
15748
  var ZOD_IMPORT_RE = /\bzod\b/;
15017
15749
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
15018
15750
  function parserForExt4(ext) {
15019
- const p = new import_tree_sitter16.default();
15751
+ const p = new import_tree_sitter17.default();
15020
15752
  p.setLanguage(GRAMMAR_BY_EXT[ext] ?? import_tree_sitter_javascript8.default);
15021
15753
  return p;
15022
15754
  }
@@ -15104,7 +15836,7 @@ function topLevelSchemas(root) {
15104
15836
  }
15105
15837
  function zodShapesFromFile(file, serviceDir) {
15106
15838
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
15107
- 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);
15108
15840
  const out = [];
15109
15841
  const seen = /* @__PURE__ */ new Set();
15110
15842
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -15118,11 +15850,11 @@ function zodShapesFromFile(file, serviceDir) {
15118
15850
  seen.add(name);
15119
15851
  const line = call.startPosition.row + 1;
15120
15852
  out.push({
15121
- infraId: (0, import_types54.infraId)("zod-schema", name),
15853
+ infraId: (0, import_types55.infraId)("zod-schema", name),
15122
15854
  name,
15123
15855
  fields,
15124
15856
  evidence: {
15125
- file: import_node_path65.default.relative(serviceDir, file.path),
15857
+ file: import_node_path67.default.relative(serviceDir, file.path),
15126
15858
  line,
15127
15859
  snippet: snippet(file.content, line)
15128
15860
  }
@@ -15153,7 +15885,7 @@ async function addZodShapes(graph, services) {
15153
15885
  if (!graph.hasNode(shape.infraId)) {
15154
15886
  const node = {
15155
15887
  id: shape.infraId,
15156
- type: import_types54.NodeType.InfraNode,
15888
+ type: import_types55.NodeType.InfraNode,
15157
15889
  name: shape.name,
15158
15890
  provider: "self",
15159
15891
  kind: "zod-schema"
@@ -15163,14 +15895,14 @@ async function addZodShapes(graph, services) {
15163
15895
  }
15164
15896
  if (shape.fields.length > 0) {
15165
15897
  const node = graph.getNodeAttributes(shape.infraId);
15166
- if (node.type === import_types54.NodeType.InfraNode) {
15898
+ if (node.type === import_types55.NodeType.InfraNode) {
15167
15899
  graph.replaceNodeAttributes(shape.infraId, {
15168
15900
  ...node,
15169
15901
  columns: foldColumns(
15170
15902
  node.columns,
15171
15903
  shape.fields,
15172
- import_types54.Provenance.EXTRACTED,
15173
- (0, import_types54.confidenceForExtracted)("structural")
15904
+ import_types55.Provenance.EXTRACTED,
15905
+ (0, import_types55.confidenceForExtracted)("structural")
15174
15906
  )
15175
15907
  });
15176
15908
  }
@@ -15184,15 +15916,15 @@ async function addZodShapes(graph, services) {
15184
15916
  );
15185
15917
  nodesAdded += n;
15186
15918
  edgesAdded += e;
15187
- 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);
15188
15920
  if (!graph.hasEdge(edgeId)) {
15189
15921
  const edge = {
15190
15922
  id: edgeId,
15191
15923
  source: fileNodeId,
15192
15924
  target: shape.infraId,
15193
- type: import_types54.EdgeType.CONTAINS,
15194
- provenance: import_types54.Provenance.EXTRACTED,
15195
- 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"),
15196
15928
  evidence: shape.evidence
15197
15929
  };
15198
15930
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -15206,7 +15938,7 @@ async function addZodShapes(graph, services) {
15206
15938
 
15207
15939
  // src/extract/firestore-rules.ts
15208
15940
  init_cjs_shims();
15209
- var import_types55 = require("@neat.is/types");
15941
+ var import_types56 = require("@neat.is/types");
15210
15942
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
15211
15943
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
15212
15944
  function stripComments(src) {
@@ -15346,7 +16078,7 @@ async function addFirestoreRules(graph, services) {
15346
16078
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
15347
16079
  graph.forEachNode((id, attrs) => {
15348
16080
  const node = attrs;
15349
- if (node.type !== import_types55.NodeType.InfraNode) return;
16081
+ if (node.type !== import_types56.NodeType.InfraNode) return;
15350
16082
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
15351
16083
  const fields = guards.get(collectionKeyFromName(node.name));
15352
16084
  if (!fields || fields.size === 0) return;
@@ -15359,17 +16091,17 @@ async function addFirestoreRules(graph, services) {
15359
16091
  }
15360
16092
 
15361
16093
  // src/extract/index.ts
15362
- var import_node_path67 = __toESM(require("path"), 1);
16094
+ var import_node_path69 = __toESM(require("path"), 1);
15363
16095
 
15364
16096
  // src/extract/retire.ts
15365
16097
  init_cjs_shims();
15366
- var import_node_fs33 = require("fs");
15367
- var import_node_path66 = __toESM(require("path"), 1);
15368
- 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");
15369
16101
  function dropOrphanedFileNodes(graph) {
15370
16102
  const orphans = [];
15371
16103
  graph.forEachNode((id, attrs) => {
15372
- if (attrs.type !== import_types56.NodeType.FileNode) return;
16104
+ if (attrs.type !== import_types57.NodeType.FileNode) return;
15373
16105
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
15374
16106
  orphans.push(id);
15375
16107
  }
@@ -15382,14 +16114,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
15382
16114
  const bases = [scanPath, ...serviceDirs];
15383
16115
  graph.forEachEdge((id, attrs) => {
15384
16116
  const edge = attrs;
15385
- if (edge.provenance !== import_types56.Provenance.EXTRACTED) return;
16117
+ if (edge.provenance !== import_types57.Provenance.EXTRACTED) return;
15386
16118
  const evidenceFile = edge.evidence?.file;
15387
16119
  if (!evidenceFile) return;
15388
- if (import_node_path66.default.isAbsolute(evidenceFile)) {
15389
- 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);
15390
16122
  return;
15391
16123
  }
15392
- 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)));
15393
16125
  if (!found) toDrop.push(id);
15394
16126
  });
15395
16127
  for (const id of toDrop) graph.dropEdge(id);
@@ -15446,7 +16178,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
15446
16178
  }
15447
16179
  const droppedEntries = drainDroppedExtracted();
15448
16180
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
15449
- 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");
15450
16182
  try {
15451
16183
  await writeRejectedExtracted(droppedEntries, rejectedPath);
15452
16184
  } catch (err) {
@@ -15480,7 +16212,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
15480
16212
 
15481
16213
  // src/diff.ts
15482
16214
  init_cjs_shims();
15483
- var import_node_fs34 = require("fs");
16215
+ var import_node_fs35 = require("fs");
15484
16216
  async function loadSnapshotForDiff(target) {
15485
16217
  if (/^https?:\/\//i.test(target)) {
15486
16218
  const res = await fetch(target);
@@ -15489,7 +16221,7 @@ async function loadSnapshotForDiff(target) {
15489
16221
  }
15490
16222
  return await res.json();
15491
16223
  }
15492
- const raw = await import_node_fs34.promises.readFile(target, "utf8");
16224
+ const raw = await import_node_fs35.promises.readFile(target, "utf8");
15493
16225
  return JSON.parse(raw);
15494
16226
  }
15495
16227
  function indexEntries(entries) {
@@ -15557,9 +16289,9 @@ function canonicalJson(value) {
15557
16289
 
15558
16290
  // src/persist.ts
15559
16291
  init_cjs_shims();
15560
- var import_node_fs35 = require("fs");
15561
- var import_node_path68 = __toESM(require("path"), 1);
15562
- 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");
15563
16295
  var SCHEMA_VERSION = 7;
15564
16296
  function migrateV1ToV2(payload) {
15565
16297
  const nodes = payload.graph.nodes;
@@ -15583,7 +16315,7 @@ function migrateV5ToV6(payload) {
15583
16315
  if (Array.isArray(nodes)) {
15584
16316
  for (const node of nodes) {
15585
16317
  const attrs = node.attributes;
15586
- if (!attrs || attrs.type !== import_types57.NodeType.InfraNode) continue;
16318
+ if (!attrs || attrs.type !== import_types58.NodeType.InfraNode) continue;
15587
16319
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
15588
16320
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
15589
16321
  }
@@ -15599,12 +16331,12 @@ function migrateV2ToV3(payload) {
15599
16331
  for (const edge of edges) {
15600
16332
  const attrs = edge.attributes;
15601
16333
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
15602
- attrs.provenance = import_types57.Provenance.OBSERVED;
16334
+ attrs.provenance = import_types58.Provenance.OBSERVED;
15603
16335
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
15604
16336
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
15605
16337
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
15606
16338
  if (type && source && target) {
15607
- const newId = (0, import_types57.observedEdgeId)(source, target, type);
16339
+ const newId = (0, import_types58.observedEdgeId)(source, target, type);
15608
16340
  attrs.id = newId;
15609
16341
  if (edge.key) edge.key = newId;
15610
16342
  }
@@ -15613,7 +16345,7 @@ function migrateV2ToV3(payload) {
15613
16345
  return { ...payload, schemaVersion: 3 };
15614
16346
  }
15615
16347
  async function ensureDir(filePath) {
15616
- 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 });
15617
16349
  }
15618
16350
  async function saveGraphToDisk(graph, outPath) {
15619
16351
  await ensureDir(outPath);
@@ -15623,13 +16355,13 @@ async function saveGraphToDisk(graph, outPath) {
15623
16355
  graph: graph.export()
15624
16356
  };
15625
16357
  const tmp = `${outPath}.tmp`;
15626
- await import_node_fs35.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15627
- 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);
15628
16360
  }
15629
16361
  async function loadGraphFromDisk(graph, outPath) {
15630
16362
  let raw;
15631
16363
  try {
15632
- raw = await import_node_fs35.promises.readFile(outPath, "utf8");
16364
+ raw = await import_node_fs36.promises.readFile(outPath, "utf8");
15633
16365
  } catch (err) {
15634
16366
  if (err.code === "ENOENT") return;
15635
16367
  throw err;
@@ -15703,23 +16435,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
15703
16435
 
15704
16436
  // src/projects.ts
15705
16437
  init_cjs_shims();
15706
- var import_node_path69 = __toESM(require("path"), 1);
16438
+ var import_node_path71 = __toESM(require("path"), 1);
15707
16439
  function pathsForProject(project, baseDir) {
15708
16440
  if (project === DEFAULT_PROJECT) {
15709
16441
  return {
15710
- snapshotPath: import_node_path69.default.join(baseDir, "graph.json"),
15711
- errorsPath: import_node_path69.default.join(baseDir, "errors.ndjson"),
15712
- staleEventsPath: import_node_path69.default.join(baseDir, "stale-events.ndjson"),
15713
- embeddingsCachePath: import_node_path69.default.join(baseDir, "embeddings.json"),
15714
- 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")
15715
16447
  };
15716
16448
  }
15717
16449
  return {
15718
- snapshotPath: import_node_path69.default.join(baseDir, `${project}.json`),
15719
- errorsPath: import_node_path69.default.join(baseDir, `errors.${project}.ndjson`),
15720
- staleEventsPath: import_node_path69.default.join(baseDir, `stale-events.${project}.ndjson`),
15721
- embeddingsCachePath: import_node_path69.default.join(baseDir, `embeddings.${project}.json`),
15722
- 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`)
15723
16455
  };
15724
16456
  }
15725
16457
  var Projects = class {
@@ -15759,20 +16491,20 @@ function parseExtraProjects(raw) {
15759
16491
 
15760
16492
  // src/registry.ts
15761
16493
  init_cjs_shims();
15762
- var import_node_fs36 = require("fs");
16494
+ var import_node_fs37 = require("fs");
15763
16495
  var import_node_os3 = __toESM(require("os"), 1);
15764
- var import_node_path70 = __toESM(require("path"), 1);
15765
- var import_types58 = require("@neat.is/types");
16496
+ var import_node_path72 = __toESM(require("path"), 1);
16497
+ var import_types59 = require("@neat.is/types");
15766
16498
  function neatHome() {
15767
16499
  const override = process.env.NEAT_HOME;
15768
- if (override && override.length > 0) return import_node_path70.default.resolve(override);
15769
- 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");
15770
16502
  }
15771
16503
  function registryPath() {
15772
- return import_node_path70.default.join(neatHome(), "projects.json");
16504
+ return import_node_path72.default.join(neatHome(), "projects.json");
15773
16505
  }
15774
16506
  function daemonsDir() {
15775
- return import_node_path70.default.join(neatHome(), "daemons");
16507
+ return import_node_path72.default.join(neatHome(), "daemons");
15776
16508
  }
15777
16509
  function isFiniteInt(v) {
15778
16510
  return typeof v === "number" && Number.isFinite(v);
@@ -15805,7 +16537,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15805
16537
  const dir = daemonsDir();
15806
16538
  let names;
15807
16539
  try {
15808
- names = await import_node_fs36.promises.readdir(dir);
16540
+ names = await import_node_fs37.promises.readdir(dir);
15809
16541
  } catch (err) {
15810
16542
  if (err.code === "ENOENT") return [];
15811
16543
  throw err;
@@ -15813,10 +16545,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
15813
16545
  const out = [];
15814
16546
  for (const name of names) {
15815
16547
  if (!name.endsWith(".json")) continue;
15816
- const file = import_node_path70.default.join(dir, name);
16548
+ const file = import_node_path72.default.join(dir, name);
15817
16549
  let raw;
15818
16550
  try {
15819
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16551
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15820
16552
  } catch {
15821
16553
  continue;
15822
16554
  }
@@ -15844,7 +16576,7 @@ async function readRegistry() {
15844
16576
  const file = registryPath();
15845
16577
  let raw;
15846
16578
  try {
15847
- raw = await import_node_fs36.promises.readFile(file, "utf8");
16579
+ raw = await import_node_fs37.promises.readFile(file, "utf8");
15848
16580
  } catch (err) {
15849
16581
  if (err.code === "ENOENT") {
15850
16582
  return { version: 1, projects: [] };
@@ -15852,7 +16584,7 @@ async function readRegistry() {
15852
16584
  throw err;
15853
16585
  }
15854
16586
  const parsed = JSON.parse(raw);
15855
- return import_types58.RegistryFileSchema.parse(parsed);
16587
+ return import_types59.RegistryFileSchema.parse(parsed);
15856
16588
  }
15857
16589
  async function getProject(name) {
15858
16590
  const reg = await readRegistry();
@@ -15927,8 +16659,8 @@ init_auth();
15927
16659
  // src/connectors-config.ts
15928
16660
  init_cjs_shims();
15929
16661
  var import_node_os4 = __toESM(require("os"), 1);
15930
- var import_node_path71 = __toESM(require("path"), 1);
15931
- var import_node_fs37 = require("fs");
16662
+ var import_node_path73 = __toESM(require("path"), 1);
16663
+ var import_node_fs38 = require("fs");
15932
16664
  var CONNECTORS_CONFIG_VERSION = 1;
15933
16665
  var EnvRefUnsetError = class extends Error {
15934
16666
  ref;
@@ -15942,17 +16674,17 @@ var EnvRefUnsetError = class extends Error {
15942
16674
  };
15943
16675
  function neatHome2() {
15944
16676
  const override = process.env.NEAT_HOME;
15945
- if (override && override.length > 0) return import_node_path71.default.resolve(override);
15946
- 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");
15947
16679
  }
15948
16680
  function connectorsConfigPath(home = neatHome2()) {
15949
- return import_node_path71.default.join(home, "connectors.json");
16681
+ return import_node_path73.default.join(home, "connectors.json");
15950
16682
  }
15951
16683
  var MODE_MASK_LOOSER_THAN_0600 = 63;
15952
16684
  async function warnIfModeLooserThan0600(file) {
15953
16685
  if (process.platform === "win32") return;
15954
16686
  try {
15955
- const stat = await import_node_fs37.promises.stat(file);
16687
+ const stat = await import_node_fs38.promises.stat(file);
15956
16688
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
15957
16689
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
15958
16690
  console.warn(
@@ -15966,7 +16698,7 @@ async function readConnectorsConfig(home = neatHome2()) {
15966
16698
  const file = connectorsConfigPath(home);
15967
16699
  let raw;
15968
16700
  try {
15969
- raw = await import_node_fs37.promises.readFile(file, "utf8");
16701
+ raw = await import_node_fs38.promises.readFile(file, "utf8");
15970
16702
  } catch (err) {
15971
16703
  if (err.code === "ENOENT") {
15972
16704
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -16133,15 +16865,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
16133
16865
 
16134
16866
  // src/connectors/index.ts
16135
16867
  init_cjs_shims();
16136
- var import_types59 = require("@neat.is/types");
16868
+ var import_types60 = require("@neat.is/types");
16137
16869
  var NO_ENV = "unknown";
16138
16870
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
16139
16871
  if (!graph.hasNode(targetNodeId)) return void 0;
16140
16872
  const sites = [];
16141
16873
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
16142
16874
  const edge = graph.getEdgeAttributes(edgeId);
16143
- if (edge.provenance !== import_types59.Provenance.EXTRACTED) continue;
16144
- 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);
16145
16877
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
16146
16878
  const site = { relPath: edge.evidence.file };
16147
16879
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -16152,7 +16884,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
16152
16884
  function routeCallSiteFor(graph, targetNodeId) {
16153
16885
  if (!graph.hasNode(targetNodeId)) return void 0;
16154
16886
  const attrs = graph.getNodeAttributes(targetNodeId);
16155
- 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;
16156
16888
  const site = { relPath: attrs.path };
16157
16889
  if (attrs.line !== void 0) site.line = attrs.line;
16158
16890
  return site;
@@ -16609,10 +17341,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
16609
17341
  // src/connectors/supabase/map.ts
16610
17342
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
16611
17343
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
16612
- function targetFromRestPath(path74) {
16613
- const rpcMatch = REST_RPC_PATH_RE.exec(path74);
17344
+ function targetFromRestPath(path76) {
17345
+ const rpcMatch = REST_RPC_PATH_RE.exec(path76);
16614
17346
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
16615
- const tableMatch = REST_TABLE_PATH_RE.exec(path74);
17347
+ const tableMatch = REST_TABLE_PATH_RE.exec(path76);
16616
17348
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
16617
17349
  return null;
16618
17350
  }
@@ -16723,23 +17455,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
16723
17455
 
16724
17456
  // src/connectors/supabase/resolve.ts
16725
17457
  init_cjs_shims();
16726
- var import_types61 = require("@neat.is/types");
17458
+ var import_types62 = require("@neat.is/types");
16727
17459
  function createSupabaseResolveTarget(graph, config) {
16728
17460
  return (signal, _ctx) => {
16729
17461
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
16730
17462
  return null;
16731
17463
  }
16732
- 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}`);
16733
17465
  if (graph.hasNode(subResourceId)) {
16734
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17466
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16735
17467
  }
16736
- const bareResourceId = (0, import_types61.infraId)(signal.targetKind, signal.targetName);
17468
+ const bareResourceId = (0, import_types62.infraId)(signal.targetKind, signal.targetName);
16737
17469
  if (graph.hasNode(bareResourceId)) {
16738
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17470
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16739
17471
  }
16740
- const projectLevelId = (0, import_types61.infraId)("supabase", config.nodeRef);
17472
+ const projectLevelId = (0, import_types62.infraId)("supabase", config.nodeRef);
16741
17473
  if (graph.hasNode(projectLevelId)) {
16742
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types61.EdgeType.CALLS };
17474
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types62.EdgeType.CALLS };
16743
17475
  }
16744
17476
  return null;
16745
17477
  };
@@ -16832,7 +17564,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
16832
17564
 
16833
17565
  // src/connectors/railway/index.ts
16834
17566
  init_cjs_shims();
16835
- var import_types65 = require("@neat.is/types");
17567
+ var import_types66 = require("@neat.is/types");
16836
17568
 
16837
17569
  // src/connectors/railway/client.ts
16838
17570
  init_cjs_shims();
@@ -16983,7 +17715,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
16983
17715
  const out = [];
16984
17716
  graph.forEachNode((_id, attrs) => {
16985
17717
  const node = attrs;
16986
- if (node.type !== import_types65.NodeType.RouteNode) return;
17718
+ if (node.type !== import_types66.NodeType.RouteNode) return;
16987
17719
  const route = attrs;
16988
17720
  if (route.service !== serviceName) return;
16989
17721
  out.push({
@@ -17087,12 +17819,12 @@ function createRailwayResolveTarget(config) {
17087
17819
  const serviceName = config.serviceNameById[config.serviceId];
17088
17820
  if (!serviceName) return null;
17089
17821
  if (signal.targetKind === ROUTE_TARGET_KIND) {
17090
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types65.EdgeType.CALLS };
17822
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types66.EdgeType.CALLS };
17091
17823
  }
17092
17824
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
17093
17825
  const peerName = config.serviceNameById[signal.targetName];
17094
17826
  if (!peerName) return null;
17095
- 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 };
17096
17828
  }
17097
17829
  return null;
17098
17830
  };
@@ -17216,9 +17948,9 @@ function parseFirebaseTargetName(targetName) {
17216
17948
  const secondSep = rest.indexOf(FIELD_SEP);
17217
17949
  if (secondSep === -1) return null;
17218
17950
  const method = rest.slice(0, secondSep);
17219
- const path74 = rest.slice(secondSep + 1);
17220
- if (!resourceName || !method || !path74) return null;
17221
- 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 };
17222
17954
  }
17223
17955
  function resourceNameFor(type, labels) {
17224
17956
  if (!labels) return null;
@@ -17256,14 +17988,14 @@ function mapLogEntryToSignal(entry) {
17256
17988
  if (!req) return null;
17257
17989
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17258
17990
  const method = req.requestMethod.toUpperCase();
17259
- const path74 = pathFromRequestUrl(req.requestUrl);
17260
- if (path74 === null) return null;
17991
+ const path76 = pathFromRequestUrl(req.requestUrl);
17992
+ if (path76 === null) return null;
17261
17993
  const timestamp = entry.timestamp;
17262
17994
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17263
17995
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
17264
17996
  return {
17265
17997
  targetKind: resourceType,
17266
- targetName: packFirebaseTargetName({ resourceName, method, path: path74 }),
17998
+ targetName: packFirebaseTargetName({ resourceName, method, path: path76 }),
17267
17999
  callCount: 1,
17268
18000
  errorCount: isError ? 1 : 0,
17269
18001
  lastObservedIso: timestamp
@@ -17280,7 +18012,7 @@ function mapLogEntriesToSignals(entries) {
17280
18012
 
17281
18013
  // src/connectors/firebase/resolve.ts
17282
18014
  init_cjs_shims();
17283
- var import_types66 = require("@neat.is/types");
18015
+ var import_types67 = require("@neat.is/types");
17284
18016
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
17285
18017
  switch (resourceType) {
17286
18018
  case "cloud_function":
@@ -17295,7 +18027,7 @@ function routeEntriesFor(graph, serviceName) {
17295
18027
  const entries = [];
17296
18028
  graph.forEachNode((_id, attrs) => {
17297
18029
  const node = attrs;
17298
- if (node.type !== import_types66.NodeType.RouteNode) return;
18030
+ if (node.type !== import_types67.NodeType.RouteNode) return;
17299
18031
  const route = attrs;
17300
18032
  if (route.service !== serviceName) return;
17301
18033
  entries.push({
@@ -17327,7 +18059,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
17327
18059
  return {
17328
18060
  targetNodeId: match.routeNodeId,
17329
18061
  serviceName,
17330
- edgeType: import_types66.EdgeType.CALLS
18062
+ edgeType: import_types67.EdgeType.CALLS
17331
18063
  };
17332
18064
  };
17333
18065
  }
@@ -17354,7 +18086,7 @@ init_cjs_shims();
17354
18086
 
17355
18087
  // src/connectors/cloudflare/connector.ts
17356
18088
  init_cjs_shims();
17357
- var import_types68 = require("@neat.is/types");
18089
+ var import_types69 = require("@neat.is/types");
17358
18090
 
17359
18091
  // src/connectors/cloudflare/client.ts
17360
18092
  init_cjs_shims();
@@ -17470,7 +18202,7 @@ function mapEventToSignal(event) {
17470
18202
  if (Number.isNaN(observedAt.getTime())) return null;
17471
18203
  const statusCode = metadata?.statusCode;
17472
18204
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
17473
- const path74 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
18205
+ const path76 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17474
18206
  return {
17475
18207
  targetKind: CLOUDFLARE_TARGET_KIND,
17476
18208
  targetName: scriptName,
@@ -17478,7 +18210,7 @@ function mapEventToSignal(event) {
17478
18210
  errorCount: isError ? 1 : 0,
17479
18211
  lastObservedIso: observedAt.toISOString(),
17480
18212
  method,
17481
- ...path74 ? { path: path74 } : {},
18213
+ ...path76 ? { path: path76 } : {},
17482
18214
  ...typeof statusCode === "number" ? { statusCode } : {},
17483
18215
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
17484
18216
  };
@@ -17518,19 +18250,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
17518
18250
  graph.forEachNode((id, attrs) => {
17519
18251
  if (found) return;
17520
18252
  const a = attrs;
17521
- 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) {
17522
18254
  found = id;
17523
18255
  }
17524
18256
  });
17525
18257
  return found;
17526
18258
  }
17527
- function findMatchingRouteNode(graph, serviceName, method, path74) {
17528
- const normalizedPath = normalizePathTemplate(path74);
18259
+ function findMatchingRouteNode(graph, serviceName, method, path76) {
18260
+ const normalizedPath = normalizePathTemplate(path76);
17529
18261
  let found = null;
17530
18262
  graph.forEachNode((id, attrs) => {
17531
18263
  if (found) return;
17532
18264
  const a = attrs;
17533
- if (a.type !== import_types68.NodeType.RouteNode || a.service !== serviceName) return;
18265
+ if (a.type !== import_types69.NodeType.RouteNode || a.service !== serviceName) return;
17534
18266
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
17535
18267
  const routeMethod = (a.method ?? "").toUpperCase();
17536
18268
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -17542,18 +18274,18 @@ function createCloudflareResolveTarget(config, graph) {
17542
18274
  return (signal) => {
17543
18275
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
17544
18276
  const scriptName = signal.targetName;
17545
- const { method, path: path74 } = signal;
18277
+ const { method, path: path76 } = signal;
17546
18278
  const resolveRouteGrain = (serviceName, wholeFileId) => {
17547
- if (!method || !path74) return wholeFileId;
17548
- return findMatchingRouteNode(graph, serviceName, method, path74) ?? wholeFileId;
18279
+ if (!method || !path76) return wholeFileId;
18280
+ return findMatchingRouteNode(graph, serviceName, method, path76) ?? wholeFileId;
17549
18281
  };
17550
18282
  const mapping = config.workers?.[scriptName];
17551
18283
  if (mapping) {
17552
- const wholeFileId = (0, import_types68.fileId)(mapping.service, mapping.entryFile);
18284
+ const wholeFileId = (0, import_types69.fileId)(mapping.service, mapping.entryFile);
17553
18285
  return {
17554
18286
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
17555
18287
  serviceName: mapping.service,
17556
- edgeType: import_types68.EdgeType.CALLS
18288
+ edgeType: import_types69.EdgeType.CALLS
17557
18289
  };
17558
18290
  }
17559
18291
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -17562,13 +18294,13 @@ function createCloudflareResolveTarget(config, graph) {
17562
18294
  return {
17563
18295
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
17564
18296
  serviceName: fileNode.service,
17565
- edgeType: import_types68.EdgeType.CALLS
18297
+ edgeType: import_types69.EdgeType.CALLS
17566
18298
  };
17567
18299
  }
17568
18300
  return {
17569
- targetNodeId: (0, import_types68.infraId)("cloudflare-worker", scriptName),
18301
+ targetNodeId: (0, import_types69.infraId)("cloudflare-worker", scriptName),
17570
18302
  serviceName: scriptName,
17571
- edgeType: import_types68.EdgeType.CALLS,
18303
+ edgeType: import_types69.EdgeType.CALLS,
17572
18304
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
17573
18305
  };
17574
18306
  };
@@ -17764,14 +18496,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
17764
18496
 
17765
18497
  // src/connectors/neon/resolve.ts
17766
18498
  init_cjs_shims();
17767
- var import_types72 = require("@neat.is/types");
18499
+ var import_types73 = require("@neat.is/types");
17768
18500
  function createNeonResolveTarget(config) {
17769
18501
  return (signal) => {
17770
18502
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
17771
18503
  return {
17772
- targetNodeId: (0, import_types72.infraId)("sql-table", signal.targetName),
18504
+ targetNodeId: (0, import_types73.infraId)("sql-table", signal.targetName),
17773
18505
  serviceName: config.serviceName,
17774
- edgeType: import_types72.EdgeType.CALLS,
18506
+ edgeType: import_types73.EdgeType.CALLS,
17775
18507
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
17776
18508
  };
17777
18509
  };
@@ -17897,9 +18629,9 @@ function parseCloudRunTargetName(targetName) {
17897
18629
  const secondSep = rest.indexOf(FIELD_SEP2);
17898
18630
  if (secondSep === -1) return null;
17899
18631
  const method = rest.slice(0, secondSep);
17900
- const path74 = rest.slice(secondSep + 1);
17901
- if (!serviceName || !method || !path74) return null;
17902
- 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 };
17903
18635
  }
17904
18636
 
17905
18637
  // src/connectors/cloud-run/map.ts
@@ -17928,14 +18660,14 @@ function mapLogEntryToSignal2(entry) {
17928
18660
  if (!req) return null;
17929
18661
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
17930
18662
  const method = req.requestMethod.toUpperCase();
17931
- const path74 = pathFromRequestUrl2(req.requestUrl);
17932
- if (path74 === null) return null;
18663
+ const path76 = pathFromRequestUrl2(req.requestUrl);
18664
+ if (path76 === null) return null;
17933
18665
  const timestamp = entry.timestamp;
17934
18666
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
17935
18667
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
17936
18668
  return {
17937
18669
  targetKind: CLOUD_RUN_TARGET_KIND,
17938
- targetName: packCloudRunTargetName({ serviceName, method, path: path74 }),
18670
+ targetName: packCloudRunTargetName({ serviceName, method, path: path76 }),
17939
18671
  callCount: 1,
17940
18672
  errorCount: isError ? 1 : 0,
17941
18673
  lastObservedIso: timestamp
@@ -17952,14 +18684,14 @@ function mapLogEntriesToSignals2(entries) {
17952
18684
 
17953
18685
  // src/connectors/cloud-run/resolve.ts
17954
18686
  init_cjs_shims();
17955
- var import_types76 = require("@neat.is/types");
18687
+ var import_types77 = require("@neat.is/types");
17956
18688
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
17957
18689
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
17958
18690
  let found = null;
17959
18691
  graph.forEachNode((_id, attrs) => {
17960
18692
  if (found) return;
17961
18693
  const node = attrs;
17962
- if (node.type !== import_types76.NodeType.RouteNode) return;
18694
+ if (node.type !== import_types77.NodeType.RouteNode) return;
17963
18695
  const route = attrs;
17964
18696
  if (route.service !== serviceName || !route.pathTemplate) return;
17965
18697
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -17974,23 +18706,23 @@ function createCloudRunResolveTarget(graph, config) {
17974
18706
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
17975
18707
  const identity = parseCloudRunTargetName(signal.targetName);
17976
18708
  if (!identity) return null;
17977
- const { serviceName: gcpServiceName, method, path: path74 } = identity;
18709
+ const { serviceName: gcpServiceName, method, path: path76 } = identity;
17978
18710
  const mappedService = config.serviceMap?.[gcpServiceName];
17979
18711
  if (mappedService) {
17980
18712
  const routeNodeId = findMatchingRouteNode2(
17981
18713
  graph,
17982
18714
  mappedService,
17983
18715
  method,
17984
- normalizePathTemplate(path74)
18716
+ normalizePathTemplate(path76)
17985
18717
  );
17986
18718
  if (routeNodeId) {
17987
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types76.EdgeType.CALLS };
18719
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types77.EdgeType.CALLS };
17988
18720
  }
17989
18721
  }
17990
18722
  return {
17991
- targetNodeId: (0, import_types76.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
18723
+ targetNodeId: (0, import_types77.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
17992
18724
  serviceName: mappedService ?? gcpServiceName,
17993
- edgeType: import_types76.EdgeType.CALLS,
18725
+ edgeType: import_types77.EdgeType.CALLS,
17994
18726
  ensureInfraNode: {
17995
18727
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
17996
18728
  name: gcpServiceName,
@@ -18031,7 +18763,7 @@ function createCloudRunConnector(graph, config = {}) {
18031
18763
 
18032
18764
  // src/connectors/render/index.ts
18033
18765
  init_cjs_shims();
18034
- var import_types79 = require("@neat.is/types");
18766
+ var import_types80 = require("@neat.is/types");
18035
18767
 
18036
18768
  // src/connectors/render/types.ts
18037
18769
  init_cjs_shims();
@@ -18109,7 +18841,7 @@ function buildRenderRouteIndex(graph, serviceName) {
18109
18841
  const out = [];
18110
18842
  graph.forEachNode((_id, attrs) => {
18111
18843
  const node = attrs;
18112
- if (node.type !== import_types79.NodeType.RouteNode) return;
18844
+ if (node.type !== import_types80.NodeType.RouteNode) return;
18113
18845
  const route = attrs;
18114
18846
  if (route.service !== serviceName) return;
18115
18847
  out.push({
@@ -18194,7 +18926,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
18194
18926
  function createRenderResolveTarget(config) {
18195
18927
  return (signal) => {
18196
18928
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
18197
- 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 };
18198
18930
  }
18199
18931
  return null;
18200
18932
  };
@@ -18332,21 +19064,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
18332
19064
 
18333
19065
  // src/connectors/planetscale/resolve.ts
18334
19066
  init_cjs_shims();
18335
- var import_types83 = require("@neat.is/types");
19067
+ var import_types84 = require("@neat.is/types");
18336
19068
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
18337
19069
  function createPlanetscaleResolveTarget(graph, config) {
18338
19070
  const databaseName = `${config.organization}/${config.database}`;
18339
19071
  return (signal, _ctx) => {
18340
19072
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
18341
- const tableId = (0, import_types83.infraId)("sql-table", signal.targetName);
19073
+ const tableId = (0, import_types84.infraId)("sql-table", signal.targetName);
18342
19074
  if (graph.hasNode(tableId)) {
18343
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types83.EdgeType.CALLS };
19075
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types84.EdgeType.CALLS };
18344
19076
  }
18345
- const providerId = (0, import_types83.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
19077
+ const providerId = (0, import_types84.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
18346
19078
  return {
18347
19079
  targetNodeId: providerId,
18348
19080
  serviceName: config.serviceName,
18349
- edgeType: import_types83.EdgeType.CALLS,
19081
+ edgeType: import_types84.EdgeType.CALLS,
18350
19082
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
18351
19083
  };
18352
19084
  };
@@ -18611,7 +19343,7 @@ function mapBuildsToSignals(builds, serviceName) {
18611
19343
 
18612
19344
  // src/connectors/eas/resolve.ts
18613
19345
  init_cjs_shims();
18614
- var import_types88 = require("@neat.is/types");
19346
+ var import_types89 = require("@neat.is/types");
18615
19347
  var NO_ENV2 = "unknown";
18616
19348
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
18617
19349
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -18627,8 +19359,8 @@ function configBasenamesForPhase(phase) {
18627
19359
  function configNodeService(graph, configNodeId) {
18628
19360
  for (const edgeId of graph.inboundEdges(configNodeId)) {
18629
19361
  const edge = graph.getEdgeAttributes(edgeId);
18630
- if (edge.type !== import_types88.EdgeType.CONFIGURED_BY) continue;
18631
- 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);
18632
19364
  if (parsed) return parsed.service;
18633
19365
  }
18634
19366
  return null;
@@ -18639,7 +19371,7 @@ function findConfigNode(graph, basenames, serviceName) {
18639
19371
  graph.forEachNode((id, attrs) => {
18640
19372
  if (scoped) return;
18641
19373
  const node = attrs;
18642
- if (node.type !== import_types88.NodeType.ConfigNode) return;
19374
+ if (node.type !== import_types89.NodeType.ConfigNode) return;
18643
19375
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
18644
19376
  if (anyMatch === null) anyMatch = id;
18645
19377
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -18656,13 +19388,13 @@ function createEasResolveTarget(graph) {
18656
19388
  if (basenames.length > 0) {
18657
19389
  const configNodeId = findConfigNode(graph, basenames, serviceName);
18658
19390
  if (configNodeId) {
18659
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types88.EdgeType.CALLS };
19391
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types89.EdgeType.CALLS };
18660
19392
  }
18661
19393
  }
18662
19394
  return {
18663
19395
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
18664
19396
  serviceName,
18665
- edgeType: import_types88.EdgeType.CALLS
19397
+ edgeType: import_types89.EdgeType.CALLS
18666
19398
  };
18667
19399
  };
18668
19400
  }
@@ -19341,11 +20073,11 @@ function registerRoutes(scope, ctx) {
19341
20073
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
19342
20074
  const parsed = [];
19343
20075
  for (const c of candidates) {
19344
- const r = import_types91.DivergenceTypeSchema.safeParse(c);
20076
+ const r = import_types92.DivergenceTypeSchema.safeParse(c);
19345
20077
  if (!r.success) {
19346
20078
  return reply.code(400).send({
19347
20079
  error: `unknown divergence type "${c}"`,
19348
- allowed: import_types91.DivergenceTypeSchema.options
20080
+ allowed: import_types92.DivergenceTypeSchema.options
19349
20081
  });
19350
20082
  }
19351
20083
  parsed.push(r.data);
@@ -19687,7 +20419,7 @@ function registerRoutes(scope, ctx) {
19687
20419
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
19688
20420
  let violations = await log.readAll();
19689
20421
  if (req.query.severity) {
19690
- const sev = import_types91.PolicySeveritySchema.safeParse(req.query.severity);
20422
+ const sev = import_types92.PolicySeveritySchema.safeParse(req.query.severity);
19691
20423
  if (!sev.success) {
19692
20424
  return reply.code(400).send({
19693
20425
  error: "invalid severity",
@@ -19726,7 +20458,7 @@ function registerRoutes(scope, ctx) {
19726
20458
  scope.post("/policies/check", async (req, reply) => {
19727
20459
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
19728
20460
  if (!proj) return;
19729
- const parsed = import_types91.PoliciesCheckBodySchema.safeParse(req.body ?? {});
20461
+ const parsed = import_types92.PoliciesCheckBodySchema.safeParse(req.body ?? {});
19730
20462
  if (!parsed.success) {
19731
20463
  return reply.code(400).send({
19732
20464
  error: "invalid /policies/check body",
@@ -20047,8 +20779,8 @@ init_otel_grpc();
20047
20779
 
20048
20780
  // src/search.ts
20049
20781
  init_cjs_shims();
20050
- var import_node_fs38 = require("fs");
20051
- var import_node_path72 = __toESM(require("path"), 1);
20782
+ var import_node_fs39 = require("fs");
20783
+ var import_node_path74 = __toESM(require("path"), 1);
20052
20784
  var import_node_crypto4 = require("crypto");
20053
20785
  var DEFAULT_LIMIT = 10;
20054
20786
  var NOMIC_DIM = 768;
@@ -20202,7 +20934,7 @@ async function pickEmbedder() {
20202
20934
  }
20203
20935
  async function readCache(cachePath) {
20204
20936
  try {
20205
- const raw = await import_node_fs38.promises.readFile(cachePath, "utf8");
20937
+ const raw = await import_node_fs39.promises.readFile(cachePath, "utf8");
20206
20938
  const parsed = JSON.parse(raw);
20207
20939
  if (parsed.version !== 1) return null;
20208
20940
  return parsed;
@@ -20211,8 +20943,8 @@ async function readCache(cachePath) {
20211
20943
  }
20212
20944
  }
20213
20945
  async function writeCache(cachePath, cache) {
20214
- await import_node_fs38.promises.mkdir(import_node_path72.default.dirname(cachePath), { recursive: true });
20215
- 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));
20216
20948
  }
20217
20949
  var VectorIndex = class {
20218
20950
  constructor(embedder, cachePath) {
@@ -20426,14 +21158,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
20426
21158
  async function main() {
20427
21159
  const baseDirEnv = process.env.NEAT_OUT_DIR;
20428
21160
  const legacyOutPath = process.env.NEAT_OUT_PATH;
20429
- 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");
20430
- 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");
20431
21163
  const registry = new Projects();
20432
21164
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
20433
21165
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
20434
21166
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
20435
21167
  const projectScan = process.env[envKey];
20436
- 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);
20437
21169
  }
20438
21170
  const host = process.env.HOST ?? "0.0.0.0";
20439
21171
  const port = Number(process.env.PORT ?? 8080);