@neat.is/core 0.8.1 → 0.8.2-dev.20260818

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 path68 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
- if (exactUnauthPaths.has(path68) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path68)) {
62
+ const path71 = (req.url.split("?")[0] ?? "").replace(/\/+$/, "");
63
+ if (exactUnauthPaths.has(path71) || PROJECT_SCOPED_UNAUTH_PATTERN.test(path71)) {
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_path47.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
- return import_node_path47.default.resolve(here, "..", "proto");
195
+ const here = import_node_path50.default.dirname((0, import_node_url.fileURLToPath)(importMetaUrl));
196
+ return import_node_path50.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_path47, import_node_crypto2, grpc, protoLoader;
264
+ var import_node_url, import_node_path50, 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_path47 = __toESM(require("path"), 1);
270
+ import_node_path50 = __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 path68 = q === -1 ? v : v.slice(0, q);
417
- if (path68.length > 0) return path68;
416
+ const path71 = q === -1 ? v : v.slice(0, q);
417
+ if (path71.length > 0) return path71;
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_path48.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
480
- const protoRoot = import_node_path48.default.resolve(here, "..", "proto");
479
+ const here = import_node_path51.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl));
480
+ const protoRoot = import_node_path51.default.resolve(here, "..", "proto");
481
481
  const root = new import_protobufjs.default.Root();
482
- root.resolvePath = (_origin, target) => import_node_path48.default.resolve(protoRoot, target);
482
+ root.resolvePath = (_origin, target) => import_node_path51.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_path48, import_node_url2, import_fastify, import_protobufjs, ENV_ATTR_CANONICAL, ENV_ATTR_COMPAT, ENV_FALLBACK, exportTraceServiceRequestType, exportTraceServiceResponseType, cachedProtobufResponseBody;
713
+ var import_node_path51, 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_path48 = __toESM(require("path"), 1);
718
+ import_node_path51 = __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_path67 = __toESM(require("path"), 1);
734
+ var import_node_path70 = __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_types85 = require("@neat.is/types");
758
+ var import_types88 = require("@neat.is/types");
759
759
 
760
760
  // src/extend/index.ts
761
761
  init_cjs_shims();
@@ -2015,7 +2015,7 @@ var import_node_fs5 = require("fs");
2015
2015
  var import_node_path5 = __toESM(require("path"), 1);
2016
2016
  var import_yaml = require("yaml");
2017
2017
  var import_types3 = require("@neat.is/types");
2018
- var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php"]);
2018
+ var SERVICE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".tsx", ".py", ".go", ".rb", ".php", ".cs", ".java"]);
2019
2019
  var CONFIG_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".yaml", ".yml"]);
2020
2020
  var JSON_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["app.json", "app.config.json", "eas.json"]);
2021
2021
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
@@ -2338,6 +2338,10 @@ function languageForPath(relPath) {
2338
2338
  return "ruby";
2339
2339
  case ".php":
2340
2340
  return "php";
2341
+ case ".cs":
2342
+ return "csharp";
2343
+ case ".java":
2344
+ return "java";
2341
2345
  case ".ts":
2342
2346
  case ".tsx":
2343
2347
  return "typescript";
@@ -2874,8 +2878,8 @@ function chiRoutesFromSource(source, parser) {
2874
2878
  chiWalk(tree.rootNode, "", out);
2875
2879
  return out;
2876
2880
  }
2877
- function stripChiRegex(path68) {
2878
- return path68.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2881
+ function stripChiRegex(path71) {
2882
+ return path71.replace(/\{([^{}:]+):[^{}]*\}/g, "{$1}");
2879
2883
  }
2880
2884
  function chiWalk(node, prefix, out) {
2881
2885
  for (let i = 0; i < node.namedChildCount; i++) {
@@ -3547,9 +3551,9 @@ function rubyRocketRoute(args) {
3547
3551
  if (!pair || pair.type !== "pair") continue;
3548
3552
  const k = pair.childForFieldName("key");
3549
3553
  if (k?.type !== "string") continue;
3550
- const path68 = rubyLiteral(k);
3551
- if (path68 === null) continue;
3552
- return { path: path68, target: rubyLiteral(pair.childForFieldName("value")) };
3554
+ const path71 = rubyLiteral(k);
3555
+ if (path71 === null) continue;
3556
+ return { path: path71, target: rubyLiteral(pair.childForFieldName("value")) };
3553
3557
  }
3554
3558
  return null;
3555
3559
  }
@@ -5592,29 +5596,29 @@ function promoteFrontierNodes(graph, opts = {}) {
5592
5596
  toPromote.push({ frontierId: id, serviceId: target });
5593
5597
  });
5594
5598
  let promoted = 0;
5595
- for (const { frontierId: frontierId2, serviceId: serviceId9 } of toPromote) {
5599
+ for (const { frontierId: frontierId2, serviceId: serviceId12 } of toPromote) {
5596
5600
  if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
5597
5601
  const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
5598
5602
  if (!gate.allowed) {
5599
5603
  continue;
5600
5604
  }
5601
5605
  }
5602
- rewireFrontierEdges(graph, frontierId2, serviceId9);
5606
+ rewireFrontierEdges(graph, frontierId2, serviceId12);
5603
5607
  graph.dropNode(frontierId2);
5604
5608
  promoted++;
5605
5609
  }
5606
5610
  return promoted;
5607
5611
  }
5608
- function rewireFrontierEdges(graph, frontierId2, serviceId9) {
5612
+ function rewireFrontierEdges(graph, frontierId2, serviceId12) {
5609
5613
  const inbound = [...graph.inboundEdges(frontierId2)];
5610
5614
  const outbound = [...graph.outboundEdges(frontierId2)];
5611
5615
  for (const edgeId of inbound) {
5612
5616
  const edge = graph.getEdgeAttributes(edgeId);
5613
- rebuildEdge(graph, edge, edge.source, serviceId9, edgeId);
5617
+ rebuildEdge(graph, edge, edge.source, serviceId12, edgeId);
5614
5618
  }
5615
5619
  for (const edgeId of outbound) {
5616
5620
  const edge = graph.getEdgeAttributes(edgeId);
5617
- rebuildEdge(graph, edge, serviceId9, edge.target, edgeId);
5621
+ rebuildEdge(graph, edge, serviceId12, edge.target, edgeId);
5618
5622
  }
5619
5623
  }
5620
5624
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
@@ -5933,19 +5937,19 @@ function confidenceFromMix(edges, now = Date.now()) {
5933
5937
  function longestIncomingWalk(graph, start, maxDepth) {
5934
5938
  let best = { path: [start], edges: [] };
5935
5939
  const visited = /* @__PURE__ */ new Set([start]);
5936
- function step(node, path68, edges) {
5937
- if (path68.length > best.path.length) {
5938
- best = { path: [...path68], edges: [...edges] };
5940
+ function step(node, path71, edges) {
5941
+ if (path71.length > best.path.length) {
5942
+ best = { path: [...path71], edges: [...edges] };
5939
5943
  }
5940
- if (path68.length - 1 >= maxDepth) return;
5944
+ if (path71.length - 1 >= maxDepth) return;
5941
5945
  const incoming = bestEdgeBySource(graph, graph.inboundEdges(node));
5942
5946
  for (const [srcId, edge] of incoming) {
5943
5947
  if (visited.has(srcId)) continue;
5944
5948
  visited.add(srcId);
5945
- path68.push(srcId);
5949
+ path71.push(srcId);
5946
5950
  edges.push(edge);
5947
- step(srcId, path68, edges);
5948
- path68.pop();
5951
+ step(srcId, path71, edges);
5952
+ path71.pop();
5949
5953
  edges.pop();
5950
5954
  visited.delete(srcId);
5951
5955
  }
@@ -5960,7 +5964,7 @@ function databaseRootCauseShape(graph, origin, walk9) {
5960
5964
  for (const id of walk9.path) {
5961
5965
  const owner = resolveOwningService(graph, id);
5962
5966
  if (!owner) continue;
5963
- const { id: serviceId9, svc } = owner;
5967
+ const { id: serviceId12, svc } = owner;
5964
5968
  const deps = svc.dependencies ?? {};
5965
5969
  for (const pair of candidatePairs) {
5966
5970
  const declared = deps[pair.driver];
@@ -5973,7 +5977,7 @@ function databaseRootCauseShape(graph, origin, walk9) {
5973
5977
  );
5974
5978
  if (!result.compatible) {
5975
5979
  return {
5976
- rootCauseNode: serviceId9,
5980
+ rootCauseNode: serviceId12,
5977
5981
  rootCauseReason: result.reason ?? "incompatible driver",
5978
5982
  ...result.minDriverVersion ? {
5979
5983
  fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
@@ -5988,7 +5992,7 @@ function serviceRootCauseShape(graph, _origin, walk9) {
5988
5992
  for (const id of walk9.path) {
5989
5993
  const owner = resolveOwningService(graph, id);
5990
5994
  if (!owner) continue;
5991
- const { id: serviceId9, svc } = owner;
5995
+ const { id: serviceId12, svc } = owner;
5992
5996
  const deps = svc.dependencies ?? {};
5993
5997
  const serviceNodeEngine = svc.nodeEngine;
5994
5998
  for (const constraint of nodeEngineConstraints()) {
@@ -5997,7 +6001,7 @@ function serviceRootCauseShape(graph, _origin, walk9) {
5997
6001
  const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
5998
6002
  if (!result.compatible && result.reason) {
5999
6003
  return {
6000
- rootCauseNode: serviceId9,
6004
+ rootCauseNode: serviceId12,
6001
6005
  rootCauseReason: result.reason,
6002
6006
  ...result.requiredNodeVersion ? {
6003
6007
  fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
@@ -6012,7 +6016,7 @@ function serviceRootCauseShape(graph, _origin, walk9) {
6012
6016
  const result = checkPackageConflict(conflict, declared, requiredDeclared);
6013
6017
  if (!result.compatible && result.reason) {
6014
6018
  return {
6015
- rootCauseNode: serviceId9,
6019
+ rootCauseNode: serviceId12,
6016
6020
  rootCauseReason: result.reason,
6017
6021
  fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
6018
6022
  };
@@ -6113,9 +6117,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
6113
6117
  function isFailingCallEdge(e) {
6114
6118
  return e.type === import_types8.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
6115
6119
  }
6116
- function callSourcesForService(graph, serviceId9) {
6117
- const ids = [serviceId9];
6118
- for (const edgeId of graph.outboundEdges(serviceId9)) {
6120
+ function callSourcesForService(graph, serviceId12) {
6121
+ const ids = [serviceId12];
6122
+ for (const edgeId of graph.outboundEdges(serviceId12)) {
6119
6123
  const e = graph.getEdgeAttributes(edgeId);
6120
6124
  if (e.type !== import_types8.EdgeType.CONTAINS) continue;
6121
6125
  const tgt = graph.getNodeAttributes(e.target);
@@ -6139,9 +6143,9 @@ function failingCallDominates(e, id, curEdge, curId) {
6139
6143
  }
6140
6144
  return id < curId;
6141
6145
  }
6142
- function dominantFailingCall(graph, serviceId9, visited) {
6146
+ function dominantFailingCall(graph, serviceId12, visited) {
6143
6147
  let best = null;
6144
- for (const src of callSourcesForService(graph, serviceId9)) {
6148
+ for (const src of callSourcesForService(graph, serviceId12)) {
6145
6149
  for (const edgeId of graph.outboundEdges(src)) {
6146
6150
  const e = graph.getEdgeAttributes(edgeId);
6147
6151
  if (!isFailingCallEdge(e)) continue;
@@ -6156,26 +6160,26 @@ function dominantFailingCall(graph, serviceId9, visited) {
6156
6160
  return best;
6157
6161
  }
6158
6162
  function followFailingCallChain(graph, originServiceId, maxDepth) {
6159
- const path68 = [originServiceId];
6163
+ const path71 = [originServiceId];
6160
6164
  const edges = [];
6161
6165
  const visited = /* @__PURE__ */ new Set([originServiceId]);
6162
6166
  let current = originServiceId;
6163
6167
  for (let depth = 0; depth < maxDepth; depth++) {
6164
6168
  const hop = dominantFailingCall(graph, current, visited);
6165
6169
  if (!hop) break;
6166
- path68.push(hop.nextService);
6170
+ path71.push(hop.nextService);
6167
6171
  edges.push(hop.edge);
6168
6172
  visited.add(hop.nextService);
6169
6173
  current = hop.nextService;
6170
6174
  }
6171
6175
  if (edges.length === 0) return null;
6172
- return { path: path68, edges, culprit: current };
6176
+ return { path: path71, edges, culprit: current };
6173
6177
  }
6174
6178
  function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6175
6179
  const chain = followFailingCallChain(graph, originId, ROOT_CAUSE_MAX_DEPTH);
6176
6180
  if (!chain) return null;
6177
6181
  const culprit = chain.culprit;
6178
- const path68 = [...chain.path];
6182
+ const path71 = [...chain.path];
6179
6183
  const edgeProvenances = chain.edges.map((e) => e.provenance);
6180
6184
  const baseConfidence = confidenceFromMix(chain.edges);
6181
6185
  const confidence = Math.max(0, Math.min(1, baseConfidence * INCIDENT_ROOT_CAUSE_CONFIDENCE));
@@ -6183,14 +6187,14 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6183
6187
  if (loc) {
6184
6188
  let rootCauseNode = culprit;
6185
6189
  if (loc.fileNode) {
6186
- path68.push(loc.fileNode);
6190
+ path71.push(loc.fileNode);
6187
6191
  edgeProvenances.push(import_types8.Provenance.OBSERVED);
6188
6192
  rootCauseNode = loc.fileNode;
6189
6193
  }
6190
6194
  return import_types8.RootCauseResultSchema.parse({
6191
6195
  rootCauseNode,
6192
6196
  rootCauseReason: loc.rootCauseReason,
6193
- traversalPath: path68,
6197
+ traversalPath: path71,
6194
6198
  edgeProvenances,
6195
6199
  confidence,
6196
6200
  ...loc.fixRecommendation ? { fixRecommendation: loc.fixRecommendation } : {}
@@ -6202,7 +6206,7 @@ function crossServiceRootCause(graph, originId, incidents, errorEvent) {
6202
6206
  return import_types8.RootCauseResultSchema.parse({
6203
6207
  rootCauseNode: culprit,
6204
6208
  rootCauseReason: `${culpritName} is failing downstream calls (${errs} observed error${errs === 1 ? "" : "s"})`,
6205
- traversalPath: path68,
6209
+ traversalPath: path71,
6206
6210
  edgeProvenances,
6207
6211
  confidence,
6208
6212
  fixRecommendation: `Inspect ${culpritName}'s failing handler`
@@ -6638,10 +6642,10 @@ function enrichWithNavigation(graph, errorNodeId, legacy, incidents, now) {
6638
6642
  let traversalPath = legacy.traversalPath;
6639
6643
  let edgeProvenances = legacy.edgeProvenances;
6640
6644
  if (top.node !== seedNode) {
6641
- const path68 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
6642
- if (path68) {
6643
- traversalPath = path68.nodes;
6644
- edgeProvenances = path68.edges.map((e) => e.provenance);
6645
+ const path71 = findPath(graph, errorNodeId, top.node, "up", ROOT_CAUSE_MAX_DEPTH);
6646
+ if (path71) {
6647
+ traversalPath = path71.nodes;
6648
+ edgeProvenances = path71.edges.map((e) => e.provenance);
6645
6649
  } else {
6646
6650
  traversalPath = [errorNodeId, top.node];
6647
6651
  edgeProvenances = [top.provenance ?? import_types8.Provenance.OBSERVED];
@@ -7053,11 +7057,11 @@ init_cjs_shims();
7053
7057
 
7054
7058
  // src/extract/services.ts
7055
7059
  init_cjs_shims();
7056
- var import_node_fs14 = require("fs");
7057
- var import_node_path16 = __toESM(require("path"), 1);
7060
+ var import_node_fs17 = require("fs");
7061
+ var import_node_path19 = __toESM(require("path"), 1);
7058
7062
  var import_ignore = __toESM(require("ignore"), 1);
7059
7063
  var import_minimatch2 = require("minimatch");
7060
- var import_types13 = require("@neat.is/types");
7064
+ var import_types16 = require("@neat.is/types");
7061
7065
 
7062
7066
  // src/extract/python.ts
7063
7067
  init_cjs_shims();
@@ -7250,19 +7254,234 @@ async function discoverPhpService(scanPath, dir) {
7250
7254
  return { pkg, dir, node };
7251
7255
  }
7252
7256
 
7253
- // src/extract/owners.ts
7257
+ // src/extract/csharp.ts
7254
7258
  init_cjs_shims();
7255
7259
  var import_node_fs13 = require("fs");
7256
7260
  var import_node_path15 = __toESM(require("path"), 1);
7261
+ var import_types13 = require("@neat.is/types");
7262
+ var CSHARP_PROJECT_EXTS = /* @__PURE__ */ new Set([".csproj", ".sln"]);
7263
+ async function projectFiles(dir) {
7264
+ const entries = await import_node_fs13.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7265
+ const out = [];
7266
+ for (const entry of entries) {
7267
+ if (!entry.isFile()) continue;
7268
+ if (CSHARP_PROJECT_EXTS.has(import_node_path15.default.extname(entry.name).toLowerCase())) out.push(entry.name);
7269
+ }
7270
+ return out.sort();
7271
+ }
7272
+ var NESTED_PROJECT_DIR = "src";
7273
+ async function resolveProjectDir(dir) {
7274
+ if ((await projectFiles(dir)).length > 0) return dir;
7275
+ const nested = import_node_path15.default.join(dir, NESTED_PROJECT_DIR);
7276
+ if ((await projectFiles(nested)).length > 0) return nested;
7277
+ return null;
7278
+ }
7279
+ async function hasCsharpProject(dir) {
7280
+ return await resolveProjectDir(dir) !== null;
7281
+ }
7282
+ function parseCsproj(source) {
7283
+ const dependencies = {};
7284
+ const re = /<PackageReference\b([^>]*?)\/?>/g;
7285
+ let m;
7286
+ while ((m = re.exec(source)) !== null) {
7287
+ const attrs = m[1] ?? "";
7288
+ const include = attrs.match(/\bInclude\s*=\s*"([^"]+)"/)?.[1];
7289
+ if (!include) continue;
7290
+ const version = attrs.match(/\bVersion\s*=\s*"([^"]+)"/)?.[1];
7291
+ dependencies[include] = version ?? "";
7292
+ }
7293
+ return { dependencies };
7294
+ }
7295
+ async function discoverCsharpService(scanPath, dir) {
7296
+ const projectDir = await resolveProjectDir(dir);
7297
+ if (projectDir === null) return null;
7298
+ const projects = await projectFiles(projectDir);
7299
+ const csproj = projects.find((p) => p.toLowerCase().endsWith(".csproj"));
7300
+ const name = csproj ? import_node_path15.default.basename(csproj, import_node_path15.default.extname(csproj)) : import_node_path15.default.basename(dir);
7301
+ let dependencies = {};
7302
+ if (csproj) {
7303
+ const raw = await import_node_fs13.promises.readFile(import_node_path15.default.join(projectDir, csproj), "utf8").catch(() => "");
7304
+ dependencies = parseCsproj(raw).dependencies;
7305
+ }
7306
+ const pkg = { name, dependencies };
7307
+ const node = {
7308
+ id: (0, import_types13.serviceId)(name),
7309
+ type: import_types13.NodeType.ServiceNode,
7310
+ name,
7311
+ language: "csharp",
7312
+ dependencies,
7313
+ // Anchor on the directory that holds the project and its `.cs` files, so file
7314
+ // attribution reaches the source whether it sits at the root or under `src/`.
7315
+ repoPath: import_node_path15.default.relative(scanPath, projectDir)
7316
+ };
7317
+ return { pkg, dir: projectDir, node };
7318
+ }
7319
+
7320
+ // src/extract/java.ts
7321
+ init_cjs_shims();
7322
+ var import_node_fs14 = require("fs");
7323
+ var import_node_path16 = __toESM(require("path"), 1);
7324
+ var import_types14 = require("@neat.is/types");
7325
+ var JAVA_MANIFESTS = ["pom.xml", "build.gradle", "build.gradle.kts"];
7326
+ async function hasJavaManifest(dir) {
7327
+ for (const manifest of JAVA_MANIFESTS) {
7328
+ if (await exists2(import_node_path16.default.join(dir, manifest))) return true;
7329
+ }
7330
+ return false;
7331
+ }
7332
+ function parsePomXml(source) {
7333
+ const dependencies = {};
7334
+ const depBlock = /<dependency\b[^>]*>([\s\S]*?)<\/dependency>/g;
7335
+ let d;
7336
+ while ((d = depBlock.exec(source)) !== null) {
7337
+ const body = d[1] ?? "";
7338
+ const groupId = body.match(/<groupId>\s*([^<]+?)\s*<\/groupId>/)?.[1];
7339
+ const artifactId = body.match(/<artifactId>\s*([^<]+?)\s*<\/artifactId>/)?.[1];
7340
+ if (!groupId || !artifactId) continue;
7341
+ const version = body.match(/<version>\s*([^<]+?)\s*<\/version>/)?.[1];
7342
+ dependencies[`${groupId}:${artifactId}`] = version ?? "";
7343
+ }
7344
+ const withoutBlocks = source.replace(/<parent\b[^>]*>[\s\S]*?<\/parent>/g, "").replace(/<dependencyManagement\b[^>]*>[\s\S]*?<\/dependencyManagement>/g, "").replace(/<dependencies\b[^>]*>[\s\S]*?<\/dependencies>/g, "").replace(/<build\b[^>]*>[\s\S]*?<\/build>/g, "");
7345
+ const name = withoutBlocks.match(/<artifactId>\s*([^<]+?)\s*<\/artifactId>/)?.[1];
7346
+ return { name, dependencies };
7347
+ }
7348
+ var GRADLE_CONFIGS = "implementation|api|compileOnly|runtimeOnly|compileOnlyApi|testImplementation|testRuntimeOnly|testCompileOnly|annotationProcessor|developmentOnly|runtimeClasspath";
7349
+ function parseGradle(source) {
7350
+ const dependencies = {};
7351
+ const re = new RegExp(`\\b(?:${GRADLE_CONFIGS})\\s*\\(?\\s*(['"])([^'"]+)\\1`, "g");
7352
+ let m;
7353
+ while ((m = re.exec(source)) !== null) {
7354
+ const coord = m[2] ?? "";
7355
+ const parts = coord.split(":");
7356
+ if (parts.length < 2 || !parts[0] || !parts[1]) continue;
7357
+ const key = `${parts[0]}:${parts[1]}`;
7358
+ dependencies[key] = parts.slice(2).join(":");
7359
+ }
7360
+ return { dependencies };
7361
+ }
7362
+ async function discoverJavaService(scanPath, dir) {
7363
+ const pomPath = import_node_path16.default.join(dir, "pom.xml");
7364
+ const gradlePath = import_node_path16.default.join(dir, "build.gradle");
7365
+ const gradleKtsPath = import_node_path16.default.join(dir, "build.gradle.kts");
7366
+ let manifest = null;
7367
+ if (await exists2(pomPath)) {
7368
+ manifest = parsePomXml(await import_node_fs14.promises.readFile(pomPath, "utf8").catch(() => ""));
7369
+ } else if (await exists2(gradlePath)) {
7370
+ manifest = parseGradle(await import_node_fs14.promises.readFile(gradlePath, "utf8").catch(() => ""));
7371
+ } else if (await exists2(gradleKtsPath)) {
7372
+ manifest = parseGradle(await import_node_fs14.promises.readFile(gradleKtsPath, "utf8").catch(() => ""));
7373
+ }
7374
+ if (!manifest) return null;
7375
+ const name = manifest.name?.trim() || import_node_path16.default.basename(dir);
7376
+ const dependencies = manifest.dependencies;
7377
+ const pkg = { name, dependencies };
7378
+ const node = {
7379
+ id: (0, import_types14.serviceId)(name),
7380
+ type: import_types14.NodeType.ServiceNode,
7381
+ name,
7382
+ language: "java",
7383
+ dependencies,
7384
+ repoPath: import_node_path16.default.relative(scanPath, dir)
7385
+ };
7386
+ return { pkg, dir, node };
7387
+ }
7388
+
7389
+ // src/extract/dockerfile-service.ts
7390
+ init_cjs_shims();
7391
+ var import_node_fs15 = require("fs");
7392
+ var import_node_path17 = __toESM(require("path"), 1);
7393
+ var import_types15 = require("@neat.is/types");
7394
+ function languageForSourceExt(ext) {
7395
+ switch (ext) {
7396
+ case ".py":
7397
+ return "python";
7398
+ case ".go":
7399
+ return "go";
7400
+ case ".rb":
7401
+ return "ruby";
7402
+ case ".php":
7403
+ return "php";
7404
+ case ".cs":
7405
+ return "csharp";
7406
+ case ".java":
7407
+ return "java";
7408
+ case ".ts":
7409
+ case ".tsx":
7410
+ return "typescript";
7411
+ case ".js":
7412
+ case ".mjs":
7413
+ case ".cjs":
7414
+ return "javascript";
7415
+ default:
7416
+ return null;
7417
+ }
7418
+ }
7419
+ var LANGUAGE_PRECEDENCE = ["typescript", "javascript", "python", "go", "ruby", "php", "csharp", "java"];
7420
+ async function inferServiceLanguage(dir) {
7421
+ const entries = await import_node_fs15.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
7422
+ const counts = /* @__PURE__ */ new Map();
7423
+ for (const entry of entries) {
7424
+ if (!entry.isFile()) continue;
7425
+ if (isNeatAuthoredSourceFile(entry.name)) continue;
7426
+ const ext = import_node_path17.default.extname(entry.name).toLowerCase();
7427
+ if (!SERVICE_FILE_EXTENSIONS.has(ext)) continue;
7428
+ const lang = languageForSourceExt(ext);
7429
+ if (!lang) continue;
7430
+ counts.set(lang, (counts.get(lang) ?? 0) + 1);
7431
+ }
7432
+ if (counts.size === 0) return null;
7433
+ let best = null;
7434
+ let bestCount = -1;
7435
+ for (const lang of LANGUAGE_PRECEDENCE) {
7436
+ const c = counts.get(lang) ?? 0;
7437
+ if (c > bestCount) {
7438
+ best = lang;
7439
+ bestCount = c;
7440
+ }
7441
+ }
7442
+ return best;
7443
+ }
7444
+ async function hasLanguageManifest(dir) {
7445
+ return await exists2(import_node_path17.default.join(dir, "package.json")) || await exists2(import_node_path17.default.join(dir, "pyproject.toml")) || await exists2(import_node_path17.default.join(dir, "requirements.txt")) || await exists2(import_node_path17.default.join(dir, "setup.py")) || await exists2(import_node_path17.default.join(dir, "go.mod")) || await exists2(import_node_path17.default.join(dir, "Gemfile")) || await exists2(import_node_path17.default.join(dir, "composer.json")) || // C#'s marker is a glob (`*.csproj` / `*.sln`), so it reads the directory
7446
+ // rather than probing a fixed filename (ADR-196).
7447
+ await hasCsharpProject(dir) || // Java keys on a Maven / Gradle build manifest (ADR-197).
7448
+ await hasJavaManifest(dir);
7449
+ }
7450
+ async function hasDockerfileDeclaredService(dir) {
7451
+ if (!await exists2(import_node_path17.default.join(dir, "Dockerfile"))) return false;
7452
+ return await inferServiceLanguage(dir) !== null;
7453
+ }
7454
+ async function discoverDockerfileService(scanPath, dir) {
7455
+ if (!await exists2(import_node_path17.default.join(dir, "Dockerfile"))) return null;
7456
+ if (await hasLanguageManifest(dir)) return null;
7457
+ const language = await inferServiceLanguage(dir);
7458
+ if (!language) return null;
7459
+ const name = import_node_path17.default.basename(dir);
7460
+ const pkg = { name, dependencies: {} };
7461
+ const node = {
7462
+ id: (0, import_types15.serviceId)(name),
7463
+ type: import_types15.NodeType.ServiceNode,
7464
+ name,
7465
+ language,
7466
+ dependencies: {},
7467
+ repoPath: import_node_path17.default.relative(scanPath, dir)
7468
+ };
7469
+ return { pkg, dir, node };
7470
+ }
7471
+
7472
+ // src/extract/owners.ts
7473
+ init_cjs_shims();
7474
+ var import_node_fs16 = require("fs");
7475
+ var import_node_path18 = __toESM(require("path"), 1);
7257
7476
  var import_minimatch = require("minimatch");
7258
7477
  async function loadCodeowners(scanPath) {
7259
7478
  const candidates = [
7260
- import_node_path15.default.join(scanPath, "CODEOWNERS"),
7261
- import_node_path15.default.join(scanPath, ".github", "CODEOWNERS")
7479
+ import_node_path18.default.join(scanPath, "CODEOWNERS"),
7480
+ import_node_path18.default.join(scanPath, ".github", "CODEOWNERS")
7262
7481
  ];
7263
7482
  for (const file of candidates) {
7264
7483
  if (await exists2(file)) {
7265
- const raw = await import_node_fs13.promises.readFile(file, "utf8");
7484
+ const raw = await import_node_fs16.promises.readFile(file, "utf8");
7266
7485
  return parseCodeowners(raw);
7267
7486
  }
7268
7487
  }
@@ -7280,7 +7499,7 @@ function parseCodeowners(raw) {
7280
7499
  return { rules };
7281
7500
  }
7282
7501
  function matchOwner(file, repoPath) {
7283
- const normalized = repoPath.split(import_node_path15.default.sep).join("/");
7502
+ const normalized = repoPath.split(import_node_path18.default.sep).join("/");
7284
7503
  for (const rule of file.rules) {
7285
7504
  if (matchesPattern(rule.pattern, normalized)) return rule.owners;
7286
7505
  }
@@ -7296,7 +7515,7 @@ function matchesPattern(rawPattern, repoPath) {
7296
7515
  return false;
7297
7516
  }
7298
7517
  async function readPackageJsonAuthor(serviceDir) {
7299
- const pkgPath = import_node_path15.default.join(serviceDir, "package.json");
7518
+ const pkgPath = import_node_path18.default.join(serviceDir, "package.json");
7300
7519
  if (!await exists2(pkgPath)) return null;
7301
7520
  try {
7302
7521
  const pkg = await readJson(pkgPath);
@@ -7333,33 +7552,36 @@ function workspaceGlobs(pkg) {
7333
7552
  return null;
7334
7553
  }
7335
7554
  async function hasPythonManifest(dir) {
7336
- return await exists2(import_node_path16.default.join(dir, "pyproject.toml")) || await exists2(import_node_path16.default.join(dir, "requirements.txt")) || await exists2(import_node_path16.default.join(dir, "setup.py"));
7555
+ return await exists2(import_node_path19.default.join(dir, "pyproject.toml")) || await exists2(import_node_path19.default.join(dir, "requirements.txt")) || await exists2(import_node_path19.default.join(dir, "setup.py"));
7337
7556
  }
7338
7557
  async function hasGoManifest(dir) {
7339
- return exists2(import_node_path16.default.join(dir, "go.mod"));
7558
+ return exists2(import_node_path19.default.join(dir, "go.mod"));
7340
7559
  }
7341
7560
  async function hasRubyManifest(dir) {
7342
- return exists2(import_node_path16.default.join(dir, "Gemfile"));
7561
+ return exists2(import_node_path19.default.join(dir, "Gemfile"));
7343
7562
  }
7344
7563
  async function hasPhpManifest(dir) {
7345
- return exists2(import_node_path16.default.join(dir, "composer.json"));
7564
+ return exists2(import_node_path19.default.join(dir, "composer.json"));
7565
+ }
7566
+ async function hasCsharpManifest(dir) {
7567
+ return hasCsharpProject(dir);
7346
7568
  }
7347
7569
  async function loadGitignore(scanPath) {
7348
- const gitignorePath = import_node_path16.default.join(scanPath, ".gitignore");
7570
+ const gitignorePath = import_node_path19.default.join(scanPath, ".gitignore");
7349
7571
  if (!await exists2(gitignorePath)) return null;
7350
- const raw = await import_node_fs14.promises.readFile(gitignorePath, "utf8");
7572
+ const raw = await import_node_fs17.promises.readFile(gitignorePath, "utf8");
7351
7573
  return (0, import_ignore.default)().add(raw);
7352
7574
  }
7353
7575
  async function walkDirs(start, scanPath, options, visit) {
7354
7576
  async function recurse(current, depth) {
7355
7577
  if (depth > options.maxDepth) return;
7356
- const entries = await import_node_fs14.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7578
+ const entries = await import_node_fs17.promises.readdir(current, { withFileTypes: true }).catch(() => []);
7357
7579
  for (const entry of entries) {
7358
7580
  if (!entry.isDirectory()) continue;
7359
7581
  if (IGNORED_DIRS.has(entry.name)) continue;
7360
- const child = import_node_path16.default.join(current, entry.name);
7582
+ const child = import_node_path19.default.join(current, entry.name);
7361
7583
  if (options.ig) {
7362
- const rel = import_node_path16.default.relative(scanPath, child).split(import_node_path16.default.sep).join("/");
7584
+ const rel = import_node_path19.default.relative(scanPath, child).split(import_node_path19.default.sep).join("/");
7363
7585
  if (rel && options.ig.ignores(rel + "/")) continue;
7364
7586
  }
7365
7587
  if (await isPythonVenvDir(child)) continue;
@@ -7375,8 +7597,8 @@ async function expandWorkspaceGlobs(scanPath, globs) {
7375
7597
  for (const raw of globs) {
7376
7598
  const pattern = raw.replace(/^\.\//, "");
7377
7599
  if (!pattern.includes("*")) {
7378
- const candidate = import_node_path16.default.join(scanPath, pattern);
7379
- if (await exists2(import_node_path16.default.join(candidate, "package.json"))) found.add(candidate);
7600
+ const candidate = import_node_path19.default.join(scanPath, pattern);
7601
+ if (await exists2(import_node_path19.default.join(candidate, "package.json"))) found.add(candidate);
7380
7602
  continue;
7381
7603
  }
7382
7604
  const segments = pattern.split("/");
@@ -7385,13 +7607,13 @@ async function expandWorkspaceGlobs(scanPath, globs) {
7385
7607
  if (seg.includes("*")) break;
7386
7608
  staticSegments.push(seg);
7387
7609
  }
7388
- const start = import_node_path16.default.join(scanPath, ...staticSegments);
7610
+ const start = import_node_path19.default.join(scanPath, ...staticSegments);
7389
7611
  if (!await exists2(start)) continue;
7390
7612
  const hasDoubleStar = pattern.includes("**");
7391
7613
  const walkDepth = hasDoubleStar ? scanDepth : Math.max(0, segments.length - staticSegments.length - 1);
7392
7614
  await walkDirs(start, scanPath, { maxDepth: walkDepth, ig: null }, async (dir) => {
7393
- const rel = import_node_path16.default.relative(scanPath, dir).split(import_node_path16.default.sep).join("/");
7394
- if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists2(import_node_path16.default.join(dir, "package.json"))) {
7615
+ const rel = import_node_path19.default.relative(scanPath, dir).split(import_node_path19.default.sep).join("/");
7616
+ if ((0, import_minimatch2.minimatch)(rel, pattern) && await exists2(import_node_path19.default.join(dir, "package.json"))) {
7395
7617
  found.add(dir);
7396
7618
  }
7397
7619
  });
@@ -7414,31 +7636,31 @@ function detectJsFramework(pkg) {
7414
7636
  async function detectJsServiceLanguage(dir, pkg) {
7415
7637
  const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
7416
7638
  if (deps["typescript"] !== void 0) return "typescript";
7417
- const entries = await import_node_fs14.promises.readdir(dir).catch(() => []);
7639
+ const entries = await import_node_fs17.promises.readdir(dir).catch(() => []);
7418
7640
  if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
7419
7641
  return "javascript";
7420
7642
  }
7421
7643
  async function discoverNodeService(scanPath, dir) {
7422
- const pkgPath = import_node_path16.default.join(dir, "package.json");
7644
+ const pkgPath = import_node_path19.default.join(dir, "package.json");
7423
7645
  if (!await exists2(pkgPath)) return null;
7424
7646
  let pkg;
7425
7647
  try {
7426
7648
  pkg = await readJson(pkgPath);
7427
7649
  } catch (err) {
7428
- recordExtractionError("services", import_node_path16.default.relative(scanPath, pkgPath), err);
7650
+ recordExtractionError("services", import_node_path19.default.relative(scanPath, pkgPath), err);
7429
7651
  return null;
7430
7652
  }
7431
7653
  if (!pkg.name) return null;
7432
7654
  const framework = detectJsFramework(pkg);
7433
7655
  const language = await detectJsServiceLanguage(dir, pkg);
7434
7656
  const node = {
7435
- id: (0, import_types13.serviceId)(pkg.name),
7436
- type: import_types13.NodeType.ServiceNode,
7657
+ id: (0, import_types16.serviceId)(pkg.name),
7658
+ type: import_types16.NodeType.ServiceNode,
7437
7659
  name: pkg.name,
7438
7660
  language,
7439
7661
  version: pkg.version,
7440
7662
  dependencies: pkg.dependencies ?? {},
7441
- repoPath: import_node_path16.default.relative(scanPath, dir),
7663
+ repoPath: import_node_path19.default.relative(scanPath, dir),
7442
7664
  ...pkg.engines?.node ? { nodeEngine: pkg.engines.node } : {},
7443
7665
  ...framework ? { framework } : {}
7444
7666
  };
@@ -7449,18 +7671,18 @@ async function discoverPyService(scanPath, dir) {
7449
7671
  if (!py) return null;
7450
7672
  const pkg = pythonToPackage(py);
7451
7673
  const node = {
7452
- id: (0, import_types13.serviceId)(py.name),
7453
- type: import_types13.NodeType.ServiceNode,
7674
+ id: (0, import_types16.serviceId)(py.name),
7675
+ type: import_types16.NodeType.ServiceNode,
7454
7676
  name: py.name,
7455
7677
  language: "python",
7456
7678
  version: py.version,
7457
7679
  dependencies: py.dependencies,
7458
- repoPath: import_node_path16.default.relative(scanPath, dir)
7680
+ repoPath: import_node_path19.default.relative(scanPath, dir)
7459
7681
  };
7460
7682
  return { pkg, dir, node };
7461
7683
  }
7462
7684
  async function discoverServices(scanPath) {
7463
- const rootPkgPath = import_node_path16.default.join(scanPath, "package.json");
7685
+ const rootPkgPath = import_node_path19.default.join(scanPath, "package.json");
7464
7686
  let rootPkg = null;
7465
7687
  if (await exists2(rootPkgPath)) {
7466
7688
  try {
@@ -7468,7 +7690,7 @@ async function discoverServices(scanPath) {
7468
7690
  } catch (err) {
7469
7691
  recordExtractionError(
7470
7692
  "services workspaces",
7471
- import_node_path16.default.relative(scanPath, rootPkgPath),
7693
+ import_node_path19.default.relative(scanPath, rootPkgPath),
7472
7694
  err
7473
7695
  );
7474
7696
  }
@@ -7480,7 +7702,7 @@ async function discoverServices(scanPath) {
7480
7702
  } else {
7481
7703
  if (rootPkg && rootPkg.name) {
7482
7704
  candidateDirs.push(scanPath);
7483
- } else if (await hasPythonManifest(scanPath) || await hasGoManifest(scanPath) || await hasRubyManifest(scanPath) || await hasPhpManifest(scanPath)) {
7705
+ } else if (await hasPythonManifest(scanPath) || await hasGoManifest(scanPath) || await hasRubyManifest(scanPath) || await hasPhpManifest(scanPath) || await hasCsharpManifest(scanPath) || await hasJavaManifest(scanPath)) {
7484
7706
  candidateDirs.push(scanPath);
7485
7707
  }
7486
7708
  const ig = await loadGitignore(scanPath);
@@ -7489,9 +7711,11 @@ async function discoverServices(scanPath) {
7489
7711
  scanPath,
7490
7712
  { maxDepth: parseScanDepth(), ig },
7491
7713
  async (dir) => {
7492
- if (await exists2(import_node_path16.default.join(dir, "package.json"))) {
7714
+ if (await exists2(import_node_path19.default.join(dir, "package.json"))) {
7493
7715
  candidateDirs.push(dir);
7494
- } else if (await hasPythonManifest(dir) || await hasGoManifest(dir) || await hasRubyManifest(dir) || await hasPhpManifest(dir)) {
7716
+ } else if (await hasPythonManifest(dir) || await hasGoManifest(dir) || await hasRubyManifest(dir) || await hasPhpManifest(dir) || await hasCsharpManifest(dir) || await hasJavaManifest(dir)) {
7717
+ candidateDirs.push(dir);
7718
+ } else if (await hasDockerfileDeclaredService(dir)) {
7495
7719
  candidateDirs.push(dir);
7496
7720
  }
7497
7721
  }
@@ -7499,14 +7723,17 @@ async function discoverServices(scanPath) {
7499
7723
  }
7500
7724
  candidateDirs.sort();
7501
7725
  const seen = /* @__PURE__ */ new Map();
7726
+ const seenDirs = /* @__PURE__ */ new Set();
7502
7727
  const out = [];
7503
7728
  for (const dir of candidateDirs) {
7504
- const service = await discoverNodeService(scanPath, dir) ?? await discoverPyService(scanPath, dir) ?? await discoverGoService(scanPath, dir) ?? await discoverRubyService(scanPath, dir) ?? await discoverPhpService(scanPath, dir);
7729
+ const service = await discoverNodeService(scanPath, dir) ?? await discoverPyService(scanPath, dir) ?? await discoverGoService(scanPath, dir) ?? await discoverRubyService(scanPath, dir) ?? await discoverPhpService(scanPath, dir) ?? await discoverCsharpService(scanPath, dir) ?? await discoverJavaService(scanPath, dir) ?? await discoverDockerfileService(scanPath, dir);
7505
7730
  if (!service) continue;
7731
+ if (seenDirs.has(service.dir)) continue;
7732
+ seenDirs.add(service.dir);
7506
7733
  const existingDir = seen.get(service.node.name);
7507
7734
  if (existingDir !== void 0) {
7508
- const a = import_node_path16.default.relative(scanPath, existingDir) || ".";
7509
- const b = import_node_path16.default.relative(scanPath, dir) || ".";
7735
+ const a = import_node_path19.default.relative(scanPath, existingDir) || ".";
7736
+ const b = import_node_path19.default.relative(scanPath, dir) || ".";
7510
7737
  console.warn(
7511
7738
  `[neat] duplicate package name "${service.node.name}" \u2014 keeping ${a}, ignoring ${b}`
7512
7739
  );
@@ -7543,20 +7770,20 @@ function addServiceNodes(graph, services) {
7543
7770
 
7544
7771
  // src/extract/aliases.ts
7545
7772
  init_cjs_shims();
7546
- var import_node_path17 = __toESM(require("path"), 1);
7547
- var import_node_fs15 = require("fs");
7773
+ var import_node_path20 = __toESM(require("path"), 1);
7774
+ var import_node_fs18 = require("fs");
7548
7775
  var import_yaml2 = require("yaml");
7549
- var import_types14 = require("@neat.is/types");
7776
+ var import_types17 = require("@neat.is/types");
7550
7777
  var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
7551
7778
  "Service",
7552
7779
  "Deployment",
7553
7780
  "StatefulSet",
7554
7781
  "DaemonSet"
7555
7782
  ]);
7556
- function addAliases(graph, serviceId9, candidates) {
7557
- if (!graph.hasNode(serviceId9)) return;
7558
- const node = graph.getNodeAttributes(serviceId9);
7559
- if (node.type !== import_types14.NodeType.ServiceNode) return;
7783
+ function addAliases(graph, serviceId12, candidates) {
7784
+ if (!graph.hasNode(serviceId12)) return;
7785
+ const node = graph.getNodeAttributes(serviceId12);
7786
+ if (node.type !== import_types17.NodeType.ServiceNode) return;
7560
7787
  const set = new Set(node.aliases ?? []);
7561
7788
  for (const c of candidates) {
7562
7789
  if (!c) continue;
@@ -7565,20 +7792,20 @@ function addAliases(graph, serviceId9, candidates) {
7565
7792
  }
7566
7793
  if (set.size === 0) return;
7567
7794
  const updated = { ...node, aliases: [...set].sort() };
7568
- graph.replaceNodeAttributes(serviceId9, updated);
7795
+ graph.replaceNodeAttributes(serviceId12, updated);
7569
7796
  }
7570
7797
  function indexServicesByName(services) {
7571
7798
  const map = /* @__PURE__ */ new Map();
7572
7799
  for (const s of services) {
7573
7800
  map.set(s.node.name, s.node.id);
7574
- map.set(import_node_path17.default.basename(s.dir), s.node.id);
7801
+ map.set(import_node_path20.default.basename(s.dir), s.node.id);
7575
7802
  }
7576
7803
  return map;
7577
7804
  }
7578
7805
  async function collectComposeAliases(graph, scanPath, serviceIndex) {
7579
7806
  let composePath = null;
7580
7807
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
7581
- const abs = import_node_path17.default.join(scanPath, name);
7808
+ const abs = import_node_path20.default.join(scanPath, name);
7582
7809
  if (await exists2(abs)) {
7583
7810
  composePath = abs;
7584
7811
  break;
@@ -7591,19 +7818,19 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
7591
7818
  } catch (err) {
7592
7819
  recordExtractionError(
7593
7820
  "aliases compose",
7594
- import_node_path17.default.relative(scanPath, composePath),
7821
+ import_node_path20.default.relative(scanPath, composePath),
7595
7822
  err
7596
7823
  );
7597
7824
  return;
7598
7825
  }
7599
7826
  if (!compose?.services) return;
7600
7827
  for (const [composeName, svc] of Object.entries(compose.services)) {
7601
- const serviceId9 = serviceIndex.get(composeName);
7602
- if (!serviceId9) continue;
7828
+ const serviceId12 = serviceIndex.get(composeName);
7829
+ if (!serviceId12) continue;
7603
7830
  const aliases = /* @__PURE__ */ new Set([composeName]);
7604
7831
  if (svc.container_name) aliases.add(svc.container_name);
7605
7832
  if (svc.hostname) aliases.add(svc.hostname);
7606
- addAliases(graph, serviceId9, aliases);
7833
+ addAliases(graph, serviceId12, aliases);
7607
7834
  }
7608
7835
  }
7609
7836
  var LABEL_KEYS = /* @__PURE__ */ new Set([
@@ -7634,11 +7861,11 @@ function parseDockerfileLabels(content) {
7634
7861
  }
7635
7862
  async function collectDockerfileAliases(graph, services) {
7636
7863
  for (const service of services) {
7637
- const dockerfilePath = import_node_path17.default.join(service.dir, "Dockerfile");
7864
+ const dockerfilePath = import_node_path20.default.join(service.dir, "Dockerfile");
7638
7865
  if (!await exists2(dockerfilePath)) continue;
7639
7866
  let content;
7640
7867
  try {
7641
- content = await import_node_fs15.promises.readFile(dockerfilePath, "utf8");
7868
+ content = await import_node_fs18.promises.readFile(dockerfilePath, "utf8");
7642
7869
  } catch (err) {
7643
7870
  recordExtractionError("aliases dockerfile", dockerfilePath, err);
7644
7871
  continue;
@@ -7650,15 +7877,15 @@ async function collectDockerfileAliases(graph, services) {
7650
7877
  async function walkYamlFiles(start, depth = 0, max = 5) {
7651
7878
  if (depth > max) return [];
7652
7879
  const out = [];
7653
- const entries = await import_node_fs15.promises.readdir(start, { withFileTypes: true }).catch(() => []);
7880
+ const entries = await import_node_fs18.promises.readdir(start, { withFileTypes: true }).catch(() => []);
7654
7881
  for (const entry of entries) {
7655
7882
  if (entry.isDirectory()) {
7656
7883
  if (IGNORED_DIRS.has(entry.name)) continue;
7657
- const child = import_node_path17.default.join(start, entry.name);
7884
+ const child = import_node_path20.default.join(start, entry.name);
7658
7885
  if (await isPythonVenvDir(child)) continue;
7659
7886
  out.push(...await walkYamlFiles(child, depth + 1, max));
7660
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path17.default.extname(entry.name))) {
7661
- out.push(import_node_path17.default.join(start, entry.name));
7887
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path20.default.extname(entry.name))) {
7888
+ out.push(import_node_path20.default.join(start, entry.name));
7662
7889
  }
7663
7890
  }
7664
7891
  return out;
@@ -7685,7 +7912,7 @@ function k8sServiceTarget(doc, byName) {
7685
7912
  async function collectK8sAliases(graph, scanPath, serviceIndex) {
7686
7913
  const files = await walkYamlFiles(scanPath);
7687
7914
  for (const file of files) {
7688
- const content = await import_node_fs15.promises.readFile(file, "utf8");
7915
+ const content = await import_node_fs18.promises.readFile(file, "utf8");
7689
7916
  let docs;
7690
7917
  try {
7691
7918
  docs = (0, import_yaml2.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -7710,14 +7937,14 @@ async function addServiceAliases(graph, scanPath, services) {
7710
7937
 
7711
7938
  // src/extract/files.ts
7712
7939
  init_cjs_shims();
7713
- var import_node_path18 = __toESM(require("path"), 1);
7940
+ var import_node_path21 = __toESM(require("path"), 1);
7714
7941
  async function addFiles(graph, services) {
7715
7942
  let nodesAdded = 0;
7716
7943
  let edgesAdded = 0;
7717
7944
  for (const service of services) {
7718
7945
  const filePaths = await walkSourceFiles(service.dir);
7719
7946
  for (const filePath of filePaths) {
7720
- const relPath = toPosix(import_node_path18.default.relative(service.dir, filePath));
7947
+ const relPath = toPosix(import_node_path21.default.relative(service.dir, filePath));
7721
7948
  const { nodesAdded: n, edgesAdded: e } = ensureFileNode(
7722
7949
  graph,
7723
7950
  service.pkg.name,
@@ -7733,11 +7960,17 @@ async function addFiles(graph, services) {
7733
7960
 
7734
7961
  // src/extract/symbols.ts
7735
7962
  init_cjs_shims();
7736
- var import_node_path19 = __toESM(require("path"), 1);
7963
+ var import_node_path22 = __toESM(require("path"), 1);
7737
7964
  var import_tree_sitter3 = __toESM(require("tree-sitter"), 1);
7738
7965
  var import_tree_sitter_javascript3 = __toESM(require("tree-sitter-javascript"), 1);
7739
7966
  var import_tree_sitter_typescript = __toESM(require("tree-sitter-typescript"), 1);
7740
- var import_types15 = require("@neat.is/types");
7967
+ var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
7968
+ var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
7969
+ var import_tree_sitter_ruby2 = __toESM(require("tree-sitter-ruby"), 1);
7970
+ var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
7971
+ var import_tree_sitter_c_sharp = __toESM(require("tree-sitter-c-sharp"), 1);
7972
+ var import_tree_sitter_java = __toESM(require("tree-sitter-java"), 1);
7973
+ var import_types18 = require("@neat.is/types");
7741
7974
  var PARSE_CHUNK3 = 16384;
7742
7975
  var GRAMMAR_BY_EXT = {
7743
7976
  ".ts": import_tree_sitter_typescript.default.typescript,
@@ -7747,6 +7980,15 @@ var GRAMMAR_BY_EXT = {
7747
7980
  ".mjs": import_tree_sitter_javascript3.default,
7748
7981
  ".cjs": import_tree_sitter_javascript3.default
7749
7982
  };
7983
+ var SYMBOL_GRAMMAR_BY_EXT = {
7984
+ ...GRAMMAR_BY_EXT,
7985
+ ".py": import_tree_sitter_python3.default,
7986
+ ".go": import_tree_sitter_go3.default,
7987
+ ".rb": import_tree_sitter_ruby2.default,
7988
+ ".php": import_tree_sitter_php2.default.php_only,
7989
+ ".cs": import_tree_sitter_c_sharp.default,
7990
+ ".java": import_tree_sitter_java.default
7991
+ };
7750
7992
  function parseSource3(parser, source) {
7751
7993
  return parser.parse(
7752
7994
  (index) => index >= source.length ? "" : source.slice(index, index + PARSE_CHUNK3)
@@ -7807,14 +8049,375 @@ function collectSymbolDefs(root) {
7807
8049
  break;
7808
8050
  }
7809
8051
  }
7810
- for (let i = 0; i < node.namedChildCount; i++) {
7811
- const child = node.namedChild(i);
7812
- if (child) visit(child, classCtx);
7813
- }
8052
+ for (let i = 0; i < node.namedChildCount; i++) {
8053
+ const child = node.namedChild(i);
8054
+ if (child) visit(child, classCtx);
8055
+ }
8056
+ };
8057
+ visit(root, void 0);
8058
+ return out;
8059
+ }
8060
+ function collectPythonSymbolDefs(root) {
8061
+ const out = [];
8062
+ const push = (kind, qualname, node) => {
8063
+ out.push({
8064
+ kind,
8065
+ qualname,
8066
+ startLine: node.startPosition.row + 1,
8067
+ endLine: node.endPosition.row + 1
8068
+ });
8069
+ };
8070
+ const visit = (node, classCtx) => {
8071
+ switch (node.type) {
8072
+ case "function_definition": {
8073
+ const name = node.childForFieldName("name")?.text;
8074
+ if (name) {
8075
+ if (classCtx) {
8076
+ const kind = name === "__init__" ? "constructor" : "method";
8077
+ push(kind, `${classCtx}.${name}`, node);
8078
+ } else {
8079
+ push("function", name, node);
8080
+ }
8081
+ }
8082
+ const body = node.childForFieldName("body");
8083
+ if (body) {
8084
+ for (let i = 0; i < body.namedChildCount; i++) {
8085
+ const child = body.namedChild(i);
8086
+ if (child) visit(child, void 0);
8087
+ }
8088
+ }
8089
+ return;
8090
+ }
8091
+ case "class_definition": {
8092
+ const name = node.childForFieldName("name")?.text;
8093
+ if (name) push("class", name, node);
8094
+ const body = node.childForFieldName("body");
8095
+ if (body) {
8096
+ for (let i = 0; i < body.namedChildCount; i++) {
8097
+ const child = body.namedChild(i);
8098
+ if (child) visit(child, name ?? classCtx);
8099
+ }
8100
+ }
8101
+ return;
8102
+ }
8103
+ case "decorated_definition": {
8104
+ const def = node.childForFieldName("definition");
8105
+ if (def) visit(def, classCtx);
8106
+ return;
8107
+ }
8108
+ }
8109
+ for (let i = 0; i < node.namedChildCount; i++) {
8110
+ const child = node.namedChild(i);
8111
+ if (child) visit(child, classCtx);
8112
+ }
8113
+ };
8114
+ visit(root, void 0);
8115
+ return out;
8116
+ }
8117
+ function goReceiverType(node) {
8118
+ const receiver = node.childForFieldName("receiver");
8119
+ if (!receiver) return void 0;
8120
+ for (let i = 0; i < receiver.namedChildCount; i++) {
8121
+ const param = receiver.namedChild(i);
8122
+ if (param?.type !== "parameter_declaration") continue;
8123
+ let typeNode = param.childForFieldName("type");
8124
+ if (typeNode?.type === "pointer_type") typeNode = typeNode.namedChild(0) ?? typeNode;
8125
+ if (typeNode?.type === "generic_type") {
8126
+ typeNode = typeNode.childForFieldName("type") ?? typeNode.namedChild(0) ?? typeNode;
8127
+ }
8128
+ if (typeNode?.type === "type_identifier") return typeNode.text;
8129
+ return typeNode?.text;
8130
+ }
8131
+ return void 0;
8132
+ }
8133
+ function collectGoSymbolDefs(root) {
8134
+ const out = [];
8135
+ const push = (kind, qualname, node) => {
8136
+ out.push({
8137
+ kind,
8138
+ qualname,
8139
+ startLine: node.startPosition.row + 1,
8140
+ endLine: node.endPosition.row + 1
8141
+ });
8142
+ };
8143
+ const visit = (node) => {
8144
+ switch (node.type) {
8145
+ case "function_declaration": {
8146
+ const name = node.childForFieldName("name")?.text;
8147
+ if (name) push("function", name, node);
8148
+ break;
8149
+ }
8150
+ case "method_declaration": {
8151
+ const name = node.childForFieldName("name")?.text;
8152
+ if (name) {
8153
+ const recv = goReceiverType(node);
8154
+ push("method", recv ? `${recv}.${name}` : name, node);
8155
+ }
8156
+ break;
8157
+ }
8158
+ }
8159
+ for (let i = 0; i < node.namedChildCount; i++) {
8160
+ const child = node.namedChild(i);
8161
+ if (child) visit(child);
8162
+ }
8163
+ };
8164
+ visit(root);
8165
+ return out;
8166
+ }
8167
+ function collectRubySymbolDefs(root) {
8168
+ const out = [];
8169
+ const push = (kind, qualname, node) => {
8170
+ out.push({
8171
+ kind,
8172
+ qualname,
8173
+ startLine: node.startPosition.row + 1,
8174
+ endLine: node.endPosition.row + 1
8175
+ });
8176
+ };
8177
+ const nameText = (node) => node.childForFieldName("name")?.text.replace(/::/g, ".");
8178
+ const walkBody = (node, ctx) => {
8179
+ const body = node.childForFieldName("body");
8180
+ if (!body) return;
8181
+ for (let i = 0; i < body.namedChildCount; i++) {
8182
+ const child = body.namedChild(i);
8183
+ if (child) visit(child, ctx);
8184
+ }
8185
+ };
8186
+ const visit = (node, classCtx) => {
8187
+ switch (node.type) {
8188
+ case "method":
8189
+ case "singleton_method": {
8190
+ const name = node.childForFieldName("name")?.text;
8191
+ if (name) {
8192
+ if (classCtx) {
8193
+ const kind = node.type === "method" && name === "initialize" ? "constructor" : "method";
8194
+ push(kind, `${classCtx}.${name}`, node);
8195
+ } else {
8196
+ push("function", name, node);
8197
+ }
8198
+ }
8199
+ walkBody(node, void 0);
8200
+ return;
8201
+ }
8202
+ case "class":
8203
+ case "module": {
8204
+ const name = nameText(node);
8205
+ if (name) push("class", classCtx ? `${classCtx}.${name}` : name, node);
8206
+ walkBody(node, name ? classCtx ? `${classCtx}.${name}` : name : classCtx);
8207
+ return;
8208
+ }
8209
+ }
8210
+ for (let i = 0; i < node.namedChildCount; i++) {
8211
+ const child = node.namedChild(i);
8212
+ if (child) visit(child, classCtx);
8213
+ }
8214
+ };
8215
+ visit(root, void 0);
8216
+ return out;
8217
+ }
8218
+ function collectPhpSymbolDefs(root) {
8219
+ const out = [];
8220
+ const push = (kind, qualname, node) => {
8221
+ out.push({
8222
+ kind,
8223
+ qualname,
8224
+ startLine: node.startPosition.row + 1,
8225
+ endLine: node.endPosition.row + 1
8226
+ });
8227
+ };
8228
+ const dot = (s) => s.replace(/\\/g, ".").replace(/::/g, ".").replace(/^\.+/, "");
8229
+ const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8230
+ const walkChildren = (container, nsCtx, classCtx) => {
8231
+ let ns = nsCtx;
8232
+ for (let i = 0; i < container.namedChildCount; i++) {
8233
+ const child = container.namedChild(i);
8234
+ if (!child) continue;
8235
+ if (child.type === "namespace_definition" && !child.childForFieldName("body")) {
8236
+ const nameNode = child.childForFieldName("name");
8237
+ if (nameNode) ns = dot(nameNode.text);
8238
+ continue;
8239
+ }
8240
+ visit(child, ns, classCtx);
8241
+ }
8242
+ };
8243
+ const walkBody = (node, nsCtx, classCtx) => {
8244
+ const body = node.childForFieldName("body");
8245
+ if (body) walkChildren(body, nsCtx, classCtx);
8246
+ };
8247
+ const visit = (node, nsCtx, classCtx) => {
8248
+ switch (node.type) {
8249
+ case "function_definition": {
8250
+ const name = node.childForFieldName("name")?.text;
8251
+ if (name) push("function", join(nsCtx, name), node);
8252
+ walkBody(node, nsCtx, void 0);
8253
+ return;
8254
+ }
8255
+ case "class_declaration":
8256
+ case "trait_declaration":
8257
+ case "interface_declaration": {
8258
+ const name = node.childForFieldName("name")?.text;
8259
+ const full = name ? join(nsCtx, name) : classCtx;
8260
+ if (name) push("class", full, node);
8261
+ walkBody(node, nsCtx, full);
8262
+ return;
8263
+ }
8264
+ case "method_declaration": {
8265
+ const name = node.childForFieldName("name")?.text;
8266
+ if (name) {
8267
+ const kind = name === "__construct" ? "constructor" : "method";
8268
+ push(kind, join(classCtx ?? nsCtx, name), node);
8269
+ }
8270
+ return;
8271
+ }
8272
+ case "namespace_definition": {
8273
+ const nameNode = node.childForFieldName("name");
8274
+ const ns = nameNode ? dot(nameNode.text) : nsCtx;
8275
+ walkBody(node, ns, classCtx);
8276
+ return;
8277
+ }
8278
+ }
8279
+ walkChildren(node, nsCtx, classCtx);
8280
+ };
8281
+ walkChildren(root, void 0, void 0);
8282
+ return out;
8283
+ }
8284
+ function collectCsharpSymbolDefs(root) {
8285
+ const out = [];
8286
+ const push = (kind, qualname, node) => {
8287
+ out.push({
8288
+ kind,
8289
+ qualname,
8290
+ startLine: node.startPosition.row + 1,
8291
+ endLine: node.endPosition.row + 1
8292
+ });
8293
+ };
8294
+ const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8295
+ const walkChildren = (container, nsCtx, classCtx) => {
8296
+ let ns = nsCtx;
8297
+ for (let i = 0; i < container.namedChildCount; i++) {
8298
+ const child = container.namedChild(i);
8299
+ if (!child) continue;
8300
+ if (child.type === "file_scoped_namespace_declaration") {
8301
+ const nameNode = child.childForFieldName("name");
8302
+ if (nameNode) ns = join(nsCtx, nameNode.text);
8303
+ continue;
8304
+ }
8305
+ visit(child, ns, classCtx);
8306
+ }
8307
+ };
8308
+ const walkBody = (node, nsCtx, classCtx) => {
8309
+ const body = node.childForFieldName("body");
8310
+ if (body) walkChildren(body, nsCtx, classCtx);
8311
+ };
8312
+ const visit = (node, nsCtx, classCtx) => {
8313
+ switch (node.type) {
8314
+ case "namespace_declaration": {
8315
+ const nameNode = node.childForFieldName("name");
8316
+ const ns = nameNode ? join(nsCtx, nameNode.text) : nsCtx;
8317
+ walkBody(node, ns, classCtx);
8318
+ return;
8319
+ }
8320
+ case "class_declaration":
8321
+ case "interface_declaration":
8322
+ case "struct_declaration":
8323
+ case "record_declaration": {
8324
+ const name = node.childForFieldName("name")?.text;
8325
+ const full = name ? join(classCtx ?? nsCtx, name) : classCtx;
8326
+ if (name) push("class", full, node);
8327
+ walkBody(node, nsCtx, full);
8328
+ return;
8329
+ }
8330
+ case "method_declaration": {
8331
+ const name = node.childForFieldName("name")?.text;
8332
+ if (name) push("method", join(classCtx ?? nsCtx, name), node);
8333
+ return;
8334
+ }
8335
+ case "constructor_declaration": {
8336
+ const name = node.childForFieldName("name")?.text;
8337
+ if (name) push("constructor", join(classCtx ?? nsCtx, name), node);
8338
+ return;
8339
+ }
8340
+ }
8341
+ walkChildren(node, nsCtx, classCtx);
8342
+ };
8343
+ walkChildren(root, void 0, void 0);
8344
+ return out;
8345
+ }
8346
+ function collectJavaSymbolDefs(root) {
8347
+ const out = [];
8348
+ const push = (kind, qualname, node) => {
8349
+ out.push({
8350
+ kind,
8351
+ qualname,
8352
+ startLine: node.startPosition.row + 1,
8353
+ endLine: node.endPosition.row + 1
8354
+ });
8355
+ };
8356
+ const join = (prefix, name) => prefix ? `${prefix}.${name}` : name;
8357
+ let pkg;
8358
+ for (let i = 0; i < root.namedChildCount; i++) {
8359
+ const child = root.namedChild(i);
8360
+ if (child?.type === "package_declaration") {
8361
+ pkg = child.namedChild(0)?.text;
8362
+ break;
8363
+ }
8364
+ }
8365
+ const walkChildren = (container, classCtx) => {
8366
+ for (let i = 0; i < container.namedChildCount; i++) {
8367
+ const child = container.namedChild(i);
8368
+ if (child) visit(child, classCtx);
8369
+ }
8370
+ };
8371
+ const walkBody = (node, classCtx) => {
8372
+ const body = node.childForFieldName("body");
8373
+ if (body) walkChildren(body, classCtx);
8374
+ };
8375
+ const visit = (node, classCtx) => {
8376
+ switch (node.type) {
8377
+ case "class_declaration":
8378
+ case "interface_declaration":
8379
+ case "enum_declaration":
8380
+ case "record_declaration": {
8381
+ const name = node.childForFieldName("name")?.text;
8382
+ const full = name ? join(classCtx ?? pkg, name) : classCtx;
8383
+ if (name) push("class", full, node);
8384
+ walkBody(node, full);
8385
+ return;
8386
+ }
8387
+ case "method_declaration": {
8388
+ const name = node.childForFieldName("name")?.text;
8389
+ if (name) push("method", join(classCtx ?? pkg, name), node);
8390
+ return;
8391
+ }
8392
+ case "constructor_declaration": {
8393
+ const name = node.childForFieldName("name")?.text;
8394
+ if (name) push("constructor", join(classCtx ?? pkg, name), node);
8395
+ return;
8396
+ }
8397
+ }
8398
+ walkChildren(node, classCtx);
7814
8399
  };
7815
- visit(root, void 0);
8400
+ walkChildren(root, void 0);
7816
8401
  return out;
7817
8402
  }
8403
+ function collectSymbolDefsForExt(ext, root) {
8404
+ switch (ext) {
8405
+ case ".py":
8406
+ return collectPythonSymbolDefs(root);
8407
+ case ".go":
8408
+ return collectGoSymbolDefs(root);
8409
+ case ".rb":
8410
+ return collectRubySymbolDefs(root);
8411
+ case ".php":
8412
+ return collectPhpSymbolDefs(root);
8413
+ case ".cs":
8414
+ return collectCsharpSymbolDefs(root);
8415
+ case ".java":
8416
+ return collectJavaSymbolDefs(root);
8417
+ default:
8418
+ return collectSymbolDefs(root);
8419
+ }
8420
+ }
7818
8421
  function disambiguate(defs) {
7819
8422
  const counts = /* @__PURE__ */ new Map();
7820
8423
  for (const def of defs) counts.set(def.qualname, (counts.get(def.qualname) ?? 0) + 1);
@@ -7829,7 +8432,7 @@ function disambiguate(defs) {
7829
8432
  async function addSymbols(graph, services) {
7830
8433
  const parsers = /* @__PURE__ */ new Map();
7831
8434
  const parserForExt5 = (ext) => {
7832
- const grammar = GRAMMAR_BY_EXT[ext];
8435
+ const grammar = SYMBOL_GRAMMAR_BY_EXT[ext];
7833
8436
  if (!grammar) return null;
7834
8437
  let parser = parsers.get(ext);
7835
8438
  if (!parser) {
@@ -7844,13 +8447,14 @@ async function addSymbols(graph, services) {
7844
8447
  for (const service of services) {
7845
8448
  const files = await loadSourceFiles(service.dir);
7846
8449
  for (const file of files) {
7847
- const parser = parserForExt5(import_node_path19.default.extname(file.path));
8450
+ const ext = import_node_path22.default.extname(file.path);
8451
+ const parser = parserForExt5(ext);
7848
8452
  if (!parser) continue;
7849
- const relPath = toPosix(import_node_path19.default.relative(service.dir, file.path));
8453
+ const relPath = toPosix(import_node_path22.default.relative(service.dir, file.path));
7850
8454
  let defs;
7851
8455
  try {
7852
8456
  const tree = parseSource3(parser, file.content);
7853
- defs = collectSymbolDefs(tree.rootNode);
8457
+ defs = collectSymbolDefsForExt(ext, tree.rootNode);
7854
8458
  } catch (err) {
7855
8459
  recordExtractionError("symbol extraction", file.path, err);
7856
8460
  continue;
@@ -7865,11 +8469,11 @@ async function addSymbols(graph, services) {
7865
8469
  nodesAdded += fn;
7866
8470
  edgesAdded += fe;
7867
8471
  for (const { def, disambiguator } of disambiguate(defs)) {
7868
- const sid = (0, import_types15.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
8472
+ const sid = (0, import_types18.symbolId)(service.pkg.name, relPath, def.qualname, disambiguator);
7869
8473
  if (!graph.hasNode(sid)) {
7870
8474
  const node = {
7871
8475
  id: sid,
7872
- type: import_types15.NodeType.SymbolNode,
8476
+ type: import_types18.NodeType.SymbolNode,
7873
8477
  kind: def.kind,
7874
8478
  qualname: def.qualname,
7875
8479
  span: { startLine: def.startLine, endLine: def.endLine },
@@ -7880,15 +8484,15 @@ async function addSymbols(graph, services) {
7880
8484
  graph.addNode(sid, node);
7881
8485
  nodesAdded++;
7882
8486
  }
7883
- const containsId = (0, import_types15.extractedEdgeId)(fileNodeId, sid, import_types15.EdgeType.CONTAINS);
8487
+ const containsId = (0, import_types18.extractedEdgeId)(fileNodeId, sid, import_types18.EdgeType.CONTAINS);
7884
8488
  if (!graph.hasEdge(containsId)) {
7885
8489
  const edge = {
7886
8490
  id: containsId,
7887
8491
  source: fileNodeId,
7888
8492
  target: sid,
7889
- type: import_types15.EdgeType.CONTAINS,
7890
- provenance: import_types15.Provenance.EXTRACTED,
7891
- confidence: (0, import_types15.confidenceForExtracted)("structural"),
8493
+ type: import_types18.EdgeType.CONTAINS,
8494
+ provenance: import_types18.Provenance.EXTRACTED,
8495
+ confidence: (0, import_types18.confidenceForExtracted)("structural"),
7892
8496
  evidence: {
7893
8497
  file: relPath,
7894
8498
  line: def.startLine,
@@ -7906,9 +8510,9 @@ async function addSymbols(graph, services) {
7906
8510
 
7907
8511
  // src/extract/symbol-edges.ts
7908
8512
  init_cjs_shims();
7909
- var import_node_path20 = __toESM(require("path"), 1);
8513
+ var import_node_path23 = __toESM(require("path"), 1);
7910
8514
  var import_tree_sitter4 = __toESM(require("tree-sitter"), 1);
7911
- var import_types16 = require("@neat.is/types");
8515
+ var import_types19 = require("@neat.is/types");
7912
8516
  function extendsInfo(classHeritage) {
7913
8517
  for (let i = 0; i < classHeritage.namedChildCount; i++) {
7914
8518
  const child = classHeritage.namedChild(i);
@@ -8013,10 +8617,10 @@ async function addSymbolEdges(graph, services) {
8013
8617
  const tsPaths = await loadTsPathConfig(service.dir);
8014
8618
  const files = await loadSourceFiles(service.dir);
8015
8619
  for (const file of files) {
8016
- const parser = parserForExt5(import_node_path20.default.extname(file.path));
8620
+ const parser = parserForExt5(import_node_path23.default.extname(file.path));
8017
8621
  if (!parser) continue;
8018
- const relPath = toPosix(import_node_path20.default.relative(service.dir, file.path));
8019
- const fileDir = import_node_path20.default.dirname(file.path);
8622
+ const relPath = toPosix(import_node_path23.default.relative(service.dir, file.path));
8623
+ const fileDir = import_node_path23.default.dirname(file.path);
8020
8624
  let root;
8021
8625
  try {
8022
8626
  root = parseSource3(parser, file.content).rootNode;
@@ -8026,7 +8630,7 @@ async function addSymbolEdges(graph, services) {
8026
8630
  }
8027
8631
  const disambiguated = disambiguate(collectSymbolDefs(root));
8028
8632
  const locals = disambiguated.map(({ def, disambiguator }) => ({
8029
- sid: (0, import_types16.symbolId)(serviceName, relPath, def.qualname, disambiguator),
8633
+ sid: (0, import_types19.symbolId)(serviceName, relPath, def.qualname, disambiguator),
8030
8634
  qualname: def.qualname,
8031
8635
  kind: def.kind,
8032
8636
  startLine: def.startLine,
@@ -8055,10 +8659,10 @@ async function addSymbolEdges(graph, services) {
8055
8659
  if (local) return local.kind === wantKind ? local.sid : null;
8056
8660
  const imp = resolvedImports.get(name);
8057
8661
  if (imp) {
8058
- const candidate = (0, import_types16.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
8662
+ const candidate = (0, import_types19.symbolId)(serviceName, imp.targetRelPath, imp.importedName);
8059
8663
  if (graph.hasNode(candidate)) {
8060
8664
  const node = graph.getNodeAttributes(candidate);
8061
- if (node.type === import_types16.NodeType.SymbolNode && node.kind === wantKind) return candidate;
8665
+ if (node.type === import_types19.NodeType.SymbolNode && node.kind === wantKind) return candidate;
8062
8666
  }
8063
8667
  }
8064
8668
  return null;
@@ -8085,7 +8689,7 @@ async function addSymbolEdges(graph, services) {
8085
8689
  sourceSid: self.sid,
8086
8690
  targetName: ext.name,
8087
8691
  wantKind: "class",
8088
- edgeType: import_types16.EdgeType.INHERITS,
8692
+ edgeType: import_types19.EdgeType.INHERITS,
8089
8693
  line: ext.line
8090
8694
  });
8091
8695
  }
@@ -8094,7 +8698,7 @@ async function addSymbolEdges(graph, services) {
8094
8698
  sourceSid: self.sid,
8095
8699
  targetName: impl.name,
8096
8700
  wantKind: "class",
8097
- edgeType: import_types16.EdgeType.IMPLEMENTS,
8701
+ edgeType: import_types19.EdgeType.IMPLEMENTS,
8098
8702
  line: impl.line
8099
8703
  });
8100
8704
  }
@@ -8110,7 +8714,7 @@ async function addSymbolEdges(graph, services) {
8110
8714
  sourceSid: caller.sid,
8111
8715
  targetName: fn.text,
8112
8716
  wantKind: "function",
8113
- edgeType: import_types16.EdgeType.CALLS,
8717
+ edgeType: import_types19.EdgeType.CALLS,
8114
8718
  line
8115
8719
  });
8116
8720
  }
@@ -8126,15 +8730,15 @@ async function addSymbolEdges(graph, services) {
8126
8730
  const targetSid = resolveTarget(req.targetName, req.wantKind);
8127
8731
  if (!targetSid) continue;
8128
8732
  if (targetSid === req.sourceSid) continue;
8129
- const edgeId = (0, import_types16.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
8733
+ const edgeId = (0, import_types19.extractedEdgeId)(req.sourceSid, targetSid, req.edgeType);
8130
8734
  if (graph.hasEdge(edgeId)) continue;
8131
8735
  const edge = {
8132
8736
  id: edgeId,
8133
8737
  source: req.sourceSid,
8134
8738
  target: targetSid,
8135
8739
  type: req.edgeType,
8136
- provenance: import_types16.Provenance.EXTRACTED,
8137
- confidence: (0, import_types16.confidenceForExtracted)("structural"),
8740
+ provenance: import_types19.Provenance.EXTRACTED,
8741
+ confidence: (0, import_types19.confidenceForExtracted)("structural"),
8138
8742
  evidence: {
8139
8743
  file: relPath,
8140
8744
  line: req.line,
@@ -8151,9 +8755,9 @@ async function addSymbolEdges(graph, services) {
8151
8755
 
8152
8756
  // src/extract/actions.ts
8153
8757
  init_cjs_shims();
8154
- var import_node_path21 = __toESM(require("path"), 1);
8758
+ var import_node_path24 = __toESM(require("path"), 1);
8155
8759
  var import_tree_sitter5 = __toESM(require("tree-sitter"), 1);
8156
- var import_types17 = require("@neat.is/types");
8760
+ var import_types20 = require("@neat.is/types");
8157
8761
  function stringLiteralText2(node) {
8158
8762
  for (let i = 0; i < node.childCount; i++) {
8159
8763
  const child = node.child(i);
@@ -8298,9 +8902,9 @@ async function addServerActions(graph, services) {
8298
8902
  const files = await loadSourceFiles(service.dir);
8299
8903
  for (const file of files) {
8300
8904
  if (isTestPath(file.path)) continue;
8301
- const parser = parserForExt5(import_node_path21.default.extname(file.path));
8905
+ const parser = parserForExt5(import_node_path24.default.extname(file.path));
8302
8906
  if (!parser) continue;
8303
- const relPath = toPosix(import_node_path21.default.relative(service.dir, file.path));
8907
+ const relPath = toPosix(import_node_path24.default.relative(service.dir, file.path));
8304
8908
  let root;
8305
8909
  try {
8306
8910
  root = parseSource3(parser, file.content).rootNode;
@@ -8323,11 +8927,11 @@ async function addServerActions(graph, services) {
8323
8927
  nodesAdded += fn;
8324
8928
  edgesAdded += fe;
8325
8929
  for (const action of actions) {
8326
- const aid = (0, import_types17.serverActionId)(service.pkg.name, relPath, action.exportName);
8930
+ const aid = (0, import_types20.serverActionId)(service.pkg.name, relPath, action.exportName);
8327
8931
  if (!graph.hasNode(aid)) {
8328
8932
  const node = {
8329
8933
  id: aid,
8330
- type: import_types17.NodeType.ServerActionNode,
8934
+ type: import_types20.NodeType.ServerActionNode,
8331
8935
  name: action.exportName,
8332
8936
  service: service.pkg.name,
8333
8937
  module: relPath,
@@ -8339,15 +8943,15 @@ async function addServerActions(graph, services) {
8339
8943
  graph.addNode(aid, node);
8340
8944
  nodesAdded++;
8341
8945
  }
8342
- const containsId = (0, import_types17.extractedEdgeId)(fileNodeId, aid, import_types17.EdgeType.CONTAINS);
8946
+ const containsId = (0, import_types20.extractedEdgeId)(fileNodeId, aid, import_types20.EdgeType.CONTAINS);
8343
8947
  if (!graph.hasEdge(containsId)) {
8344
8948
  const edge = {
8345
8949
  id: containsId,
8346
8950
  source: fileNodeId,
8347
8951
  target: aid,
8348
- type: import_types17.EdgeType.CONTAINS,
8349
- provenance: import_types17.Provenance.EXTRACTED,
8350
- confidence: (0, import_types17.confidenceForExtracted)("structural"),
8952
+ type: import_types20.EdgeType.CONTAINS,
8953
+ provenance: import_types20.Provenance.EXTRACTED,
8954
+ confidence: (0, import_types20.confidenceForExtracted)("structural"),
8351
8955
  evidence: {
8352
8956
  file: relPath,
8353
8957
  line: action.line,
@@ -8361,10 +8965,10 @@ async function addServerActions(graph, services) {
8361
8965
  }
8362
8966
  for (const file of files) {
8363
8967
  if (isTestPath(file.path)) continue;
8364
- const parser = parserForExt5(import_node_path21.default.extname(file.path));
8968
+ const parser = parserForExt5(import_node_path24.default.extname(file.path));
8365
8969
  if (!parser) continue;
8366
- const relPath = toPosix(import_node_path21.default.relative(service.dir, file.path));
8367
- const fileDir = import_node_path21.default.dirname(file.path);
8970
+ const relPath = toPosix(import_node_path24.default.relative(service.dir, file.path));
8971
+ const fileDir = import_node_path24.default.dirname(file.path);
8368
8972
  let root;
8369
8973
  try {
8370
8974
  root = parseSource3(parser, file.content).rootNode;
@@ -8377,7 +8981,7 @@ async function addServerActions(graph, services) {
8377
8981
  for (const [local, binding] of bindings) {
8378
8982
  const target = await resolveJsImport(binding.specifier, fileDir, service.dir, tsPaths);
8379
8983
  if (!target) continue;
8380
- const aid = (0, import_types17.serverActionId)(service.pkg.name, target, binding.importedName);
8984
+ const aid = (0, import_types20.serverActionId)(service.pkg.name, target, binding.importedName);
8381
8985
  if (graph.hasNode(aid)) actionBindings.set(local, aid);
8382
8986
  }
8383
8987
  if (actionBindings.size === 0) continue;
@@ -8392,15 +8996,15 @@ async function addServerActions(graph, services) {
8392
8996
  nodesAdded += ensured.nodesAdded;
8393
8997
  edgesAdded += ensured.edgesAdded;
8394
8998
  }
8395
- const callsId = (0, import_types17.extractedEdgeId)(clientFileId, aid, import_types17.EdgeType.CALLS);
8999
+ const callsId = (0, import_types20.extractedEdgeId)(clientFileId, aid, import_types20.EdgeType.CALLS);
8396
9000
  if (graph.hasEdge(callsId)) continue;
8397
9001
  const edge = {
8398
9002
  id: callsId,
8399
9003
  source: clientFileId,
8400
9004
  target: aid,
8401
- type: import_types17.EdgeType.CALLS,
8402
- provenance: import_types17.Provenance.EXTRACTED,
8403
- confidence: (0, import_types17.confidenceForExtracted)("structural"),
9005
+ type: import_types20.EdgeType.CALLS,
9006
+ provenance: import_types20.Provenance.EXTRACTED,
9007
+ confidence: (0, import_types20.confidenceForExtracted)("structural"),
8404
9008
  evidence: {
8405
9009
  file: relPath,
8406
9010
  line,
@@ -8417,14 +9021,14 @@ async function addServerActions(graph, services) {
8417
9021
 
8418
9022
  // src/extract/databases/index.ts
8419
9023
  init_cjs_shims();
8420
- var import_node_path29 = __toESM(require("path"), 1);
8421
- var import_types18 = require("@neat.is/types");
9024
+ var import_node_path32 = __toESM(require("path"), 1);
9025
+ var import_types21 = require("@neat.is/types");
8422
9026
 
8423
9027
  // src/extract/databases/db-config-yaml.ts
8424
9028
  init_cjs_shims();
8425
- var import_node_path22 = __toESM(require("path"), 1);
9029
+ var import_node_path25 = __toESM(require("path"), 1);
8426
9030
  async function parse(serviceDir) {
8427
- const yamlPath = import_node_path22.default.join(serviceDir, "db-config.yaml");
9031
+ const yamlPath = import_node_path25.default.join(serviceDir, "db-config.yaml");
8428
9032
  if (!await exists2(yamlPath)) return [];
8429
9033
  const raw = await readYaml(yamlPath);
8430
9034
  return [
@@ -8442,13 +9046,13 @@ var dbConfigYamlParser = { name: "db-config.yaml", parse };
8442
9046
 
8443
9047
  // src/extract/databases/dotenv.ts
8444
9048
  init_cjs_shims();
8445
- var import_node_fs17 = require("fs");
8446
- var import_node_path24 = __toESM(require("path"), 1);
9049
+ var import_node_fs20 = require("fs");
9050
+ var import_node_path27 = __toESM(require("path"), 1);
8447
9051
 
8448
9052
  // src/extract/databases/shared.ts
8449
9053
  init_cjs_shims();
8450
- var import_node_fs16 = require("fs");
8451
- var import_node_path23 = __toESM(require("path"), 1);
9054
+ var import_node_fs19 = require("fs");
9055
+ var import_node_path26 = __toESM(require("path"), 1);
8452
9056
  function schemeToEngine(scheme) {
8453
9057
  const s = scheme.toLowerCase().split("+")[0];
8454
9058
  switch (s) {
@@ -8487,18 +9091,18 @@ function parseConnectionString(url) {
8487
9091
  }
8488
9092
  async function readIfExists(filePath) {
8489
9093
  try {
8490
- return await import_node_fs16.promises.readFile(filePath, "utf8");
9094
+ return await import_node_fs19.promises.readFile(filePath, "utf8");
8491
9095
  } catch {
8492
9096
  return null;
8493
9097
  }
8494
9098
  }
8495
9099
  async function resolveEnvVar(serviceDir, name) {
8496
- const entries = await import_node_fs16.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9100
+ const entries = await import_node_fs19.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
8497
9101
  const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
8498
9102
  const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
8499
9103
  envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
8500
9104
  for (const fileName of envNames) {
8501
- const content = await readIfExists(import_node_path23.default.join(serviceDir, fileName));
9105
+ const content = await readIfExists(import_node_path26.default.join(serviceDir, fileName));
8502
9106
  if (!content) continue;
8503
9107
  for (const line of content.split("\n")) {
8504
9108
  const trimmed = line.trim();
@@ -8517,7 +9121,7 @@ async function resolveEnvVar(serviceDir, name) {
8517
9121
  }
8518
9122
  async function findFirst(serviceDir, candidates) {
8519
9123
  for (const rel of candidates) {
8520
- const abs = import_node_path23.default.join(serviceDir, rel);
9124
+ const abs = import_node_path26.default.join(serviceDir, rel);
8521
9125
  const content = await readIfExists(abs);
8522
9126
  if (content !== null) return abs;
8523
9127
  }
@@ -8575,15 +9179,15 @@ function parseDotenvLine(line) {
8575
9179
  return { key, value };
8576
9180
  }
8577
9181
  async function parse2(serviceDir) {
8578
- const entries = await import_node_fs17.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
9182
+ const entries = await import_node_fs20.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
8579
9183
  const configs = [];
8580
9184
  const seen = /* @__PURE__ */ new Set();
8581
9185
  for (const entry of entries) {
8582
9186
  if (!entry.isFile()) continue;
8583
9187
  const match = isConfigFile(entry.name);
8584
9188
  if (!match.match || match.fileType !== "env") continue;
8585
- const filePath = import_node_path24.default.join(serviceDir, entry.name);
8586
- const content = await import_node_fs17.promises.readFile(filePath, "utf8");
9189
+ const filePath = import_node_path27.default.join(serviceDir, entry.name);
9190
+ const content = await import_node_fs20.promises.readFile(filePath, "utf8");
8587
9191
  for (const line of content.split("\n")) {
8588
9192
  const parsed = parseDotenvLine(line);
8589
9193
  if (!parsed) continue;
@@ -8602,9 +9206,9 @@ var dotenvParser = { name: ".env", parse: parse2 };
8602
9206
 
8603
9207
  // src/extract/databases/prisma.ts
8604
9208
  init_cjs_shims();
8605
- var import_node_path25 = __toESM(require("path"), 1);
9209
+ var import_node_path28 = __toESM(require("path"), 1);
8606
9210
  async function parse3(serviceDir) {
8607
- const schemaPath = import_node_path25.default.join(serviceDir, "prisma", "schema.prisma");
9211
+ const schemaPath = import_node_path28.default.join(serviceDir, "prisma", "schema.prisma");
8608
9212
  const content = await readIfExists(schemaPath);
8609
9213
  if (!content) return [];
8610
9214
  const block = content.match(/datasource\s+\w+\s*\{([^}]*)\}/s);
@@ -8762,10 +9366,10 @@ var knexParser = { name: "knex", parse: parse5 };
8762
9366
 
8763
9367
  // src/extract/databases/ormconfig.ts
8764
9368
  init_cjs_shims();
8765
- var import_node_path26 = __toESM(require("path"), 1);
9369
+ var import_node_path29 = __toESM(require("path"), 1);
8766
9370
  async function parse6(serviceDir) {
8767
9371
  for (const candidate of ["ormconfig.json", "ormconfig.yaml", "ormconfig.yml"]) {
8768
- const abs = import_node_path26.default.join(serviceDir, candidate);
9372
+ const abs = import_node_path29.default.join(serviceDir, candidate);
8769
9373
  if (!await exists2(abs)) continue;
8770
9374
  const raw = candidate.endsWith(".json") ? await readJson(abs) : await readYaml(abs);
8771
9375
  const entries = Array.isArray(raw) ? raw : [raw];
@@ -8825,9 +9429,9 @@ var typeormParser = { name: "typeorm", parse: parse7 };
8825
9429
 
8826
9430
  // src/extract/databases/sequelize.ts
8827
9431
  init_cjs_shims();
8828
- var import_node_path27 = __toESM(require("path"), 1);
9432
+ var import_node_path30 = __toESM(require("path"), 1);
8829
9433
  async function parse8(serviceDir) {
8830
- const configPath = import_node_path27.default.join(serviceDir, "config", "config.json");
9434
+ const configPath = import_node_path30.default.join(serviceDir, "config", "config.json");
8831
9435
  if (!await exists2(configPath)) return [];
8832
9436
  const raw = await readJson(configPath);
8833
9437
  const out = [];
@@ -8854,7 +9458,7 @@ var sequelizeParser = { name: "sequelize", parse: parse8 };
8854
9458
 
8855
9459
  // src/extract/databases/docker-compose.ts
8856
9460
  init_cjs_shims();
8857
- var import_node_path28 = __toESM(require("path"), 1);
9461
+ var import_node_path31 = __toESM(require("path"), 1);
8858
9462
  function portFromService(svc) {
8859
9463
  for (const raw of svc.ports ?? []) {
8860
9464
  const str = String(raw);
@@ -8881,7 +9485,7 @@ function databaseFromEnv(svc) {
8881
9485
  }
8882
9486
  async function parse9(serviceDir) {
8883
9487
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
8884
- const abs = import_node_path28.default.join(serviceDir, name);
9488
+ const abs = import_node_path31.default.join(serviceDir, name);
8885
9489
  if (!await exists2(abs)) continue;
8886
9490
  const raw = await readYaml(abs);
8887
9491
  if (!raw?.services) return [];
@@ -8922,8 +9526,8 @@ function compatibleDriversFor(engine) {
8922
9526
  }
8923
9527
  function toDatabaseNode(config) {
8924
9528
  return {
8925
- id: (0, import_types18.databaseId)(config.host),
8926
- type: import_types18.NodeType.DatabaseNode,
9529
+ id: (0, import_types21.databaseId)(config.host),
9530
+ type: import_types21.NodeType.DatabaseNode,
8927
9531
  name: config.database || config.host,
8928
9532
  engine: config.engine,
8929
9533
  engineVersion: config.engineVersion,
@@ -9053,7 +9657,7 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9053
9657
  discoveredVia: mergedDiscoveredVia
9054
9658
  });
9055
9659
  }
9056
- const relConfigFile = toPosix(import_node_path29.default.relative(service.dir, config.sourceFile));
9660
+ const relConfigFile = toPosix(import_node_path32.default.relative(service.dir, config.sourceFile));
9057
9661
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9058
9662
  graph,
9059
9663
  service.pkg.name,
@@ -9062,14 +9666,14 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9062
9666
  );
9063
9667
  nodesAdded += fn;
9064
9668
  edgesAdded += fe;
9065
- const evidenceFile = toPosix(import_node_path29.default.relative(scanPath, config.sourceFile));
9669
+ const evidenceFile = toPosix(import_node_path32.default.relative(scanPath, config.sourceFile));
9066
9670
  const edge = {
9067
- id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types18.EdgeType.CONNECTS_TO),
9671
+ id: (0, import_types3.extractedEdgeId)(fileNodeId, dbNode.id, import_types21.EdgeType.CONNECTS_TO),
9068
9672
  source: fileNodeId,
9069
9673
  target: dbNode.id,
9070
- type: import_types18.EdgeType.CONNECTS_TO,
9071
- provenance: import_types18.Provenance.EXTRACTED,
9072
- confidence: (0, import_types18.confidenceForExtracted)("structural"),
9674
+ type: import_types21.EdgeType.CONNECTS_TO,
9675
+ provenance: import_types21.Provenance.EXTRACTED,
9676
+ confidence: (0, import_types21.confidenceForExtracted)("structural"),
9073
9677
  evidence: { file: evidenceFile }
9074
9678
  };
9075
9679
  if (!graph.hasEdge(edge.id)) {
@@ -9080,26 +9684,26 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9080
9684
  if (allConfigs.length === 1) {
9081
9685
  const primary = allConfigs[0];
9082
9686
  service.node.dbConnectionTarget = primary.port ? `${primary.host}:${primary.port}` : primary.host;
9083
- const relPath = import_node_path29.default.relative(scanPath, primary.sourceFile);
9084
- const cfgId = (0, import_types18.configId)(relPath);
9687
+ const relPath = import_node_path32.default.relative(scanPath, primary.sourceFile);
9688
+ const cfgId = (0, import_types21.configId)(relPath);
9085
9689
  if (!graph.hasNode(cfgId)) {
9086
9690
  const cfgNode = {
9087
9691
  id: cfgId,
9088
- type: import_types18.NodeType.ConfigNode,
9089
- name: import_node_path29.default.basename(primary.sourceFile),
9692
+ type: import_types21.NodeType.ConfigNode,
9693
+ name: import_node_path32.default.basename(primary.sourceFile),
9090
9694
  path: relPath,
9091
- fileType: isConfigFile(import_node_path29.default.basename(primary.sourceFile)).fileType || "config"
9695
+ fileType: isConfigFile(import_node_path32.default.basename(primary.sourceFile)).fileType || "config"
9092
9696
  };
9093
9697
  graph.addNode(cfgId, cfgNode);
9094
9698
  nodesAdded++;
9095
9699
  }
9096
9700
  const cfgEdge = {
9097
- id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types18.EdgeType.CONFIGURED_BY),
9701
+ id: (0, import_types3.extractedEdgeId)(service.node.id, cfgId, import_types21.EdgeType.CONFIGURED_BY),
9098
9702
  source: service.node.id,
9099
9703
  target: cfgId,
9100
- type: import_types18.EdgeType.CONFIGURED_BY,
9101
- provenance: import_types18.Provenance.EXTRACTED,
9102
- confidence: (0, import_types18.confidenceForExtracted)("structural"),
9704
+ type: import_types21.EdgeType.CONFIGURED_BY,
9705
+ provenance: import_types21.Provenance.EXTRACTED,
9706
+ confidence: (0, import_types21.confidenceForExtracted)("structural"),
9103
9707
  evidence: { file: toPosix(relPath) }
9104
9708
  };
9105
9709
  if (!graph.hasEdge(cfgEdge.id)) {
@@ -9129,15 +9733,15 @@ async function addDatabasesAndCompat(graph, services, scanPath) {
9129
9733
 
9130
9734
  // src/extract/configs.ts
9131
9735
  init_cjs_shims();
9132
- var import_node_fs18 = require("fs");
9133
- var import_node_path30 = __toESM(require("path"), 1);
9134
- var import_types19 = require("@neat.is/types");
9736
+ var import_node_fs21 = require("fs");
9737
+ var import_node_path33 = __toESM(require("path"), 1);
9738
+ var import_types22 = require("@neat.is/types");
9135
9739
  async function walkConfigFiles(dir) {
9136
9740
  const out = [];
9137
9741
  async function walk9(current) {
9138
- const entries = await import_node_fs18.promises.readdir(current, { withFileTypes: true });
9742
+ const entries = await import_node_fs21.promises.readdir(current, { withFileTypes: true });
9139
9743
  for (const entry of entries) {
9140
- const full = import_node_path30.default.join(current, entry.name);
9744
+ const full = import_node_path33.default.join(current, entry.name);
9141
9745
  if (entry.isDirectory()) {
9142
9746
  if (IGNORED_DIRS.has(entry.name)) continue;
9143
9747
  if (await isPythonVenvDir(full)) continue;
@@ -9156,19 +9760,19 @@ async function addConfigNodes(graph, services, scanPath) {
9156
9760
  for (const service of services) {
9157
9761
  const configFiles = await walkConfigFiles(service.dir);
9158
9762
  for (const file of configFiles) {
9159
- const relPath = import_node_path30.default.relative(scanPath, file);
9763
+ const relPath = import_node_path33.default.relative(scanPath, file);
9160
9764
  const node = {
9161
- id: (0, import_types19.configId)(relPath),
9162
- type: import_types19.NodeType.ConfigNode,
9163
- name: import_node_path30.default.basename(file),
9765
+ id: (0, import_types22.configId)(relPath),
9766
+ type: import_types22.NodeType.ConfigNode,
9767
+ name: import_node_path33.default.basename(file),
9164
9768
  path: relPath,
9165
- fileType: isConfigFile(import_node_path30.default.basename(file)).fileType
9769
+ fileType: isConfigFile(import_node_path33.default.basename(file)).fileType
9166
9770
  };
9167
9771
  if (!graph.hasNode(node.id)) {
9168
9772
  graph.addNode(node.id, node);
9169
9773
  nodesAdded++;
9170
9774
  }
9171
- const relToService = toPosix(import_node_path30.default.relative(service.dir, file));
9775
+ const relToService = toPosix(import_node_path33.default.relative(service.dir, file));
9172
9776
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
9173
9777
  graph,
9174
9778
  service.pkg.name,
@@ -9178,13 +9782,13 @@ async function addConfigNodes(graph, services, scanPath) {
9178
9782
  nodesAdded += fn;
9179
9783
  edgesAdded += fe;
9180
9784
  const edge = {
9181
- id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types19.EdgeType.CONFIGURED_BY),
9785
+ id: (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types22.EdgeType.CONFIGURED_BY),
9182
9786
  source: fileNodeId,
9183
9787
  target: node.id,
9184
- type: import_types19.EdgeType.CONFIGURED_BY,
9185
- provenance: import_types19.Provenance.EXTRACTED,
9186
- confidence: (0, import_types19.confidenceForExtracted)("structural"),
9187
- evidence: { file: relPath.split(import_node_path30.default.sep).join("/") }
9788
+ type: import_types22.EdgeType.CONFIGURED_BY,
9789
+ provenance: import_types22.Provenance.EXTRACTED,
9790
+ confidence: (0, import_types22.confidenceForExtracted)("structural"),
9791
+ evidence: { file: relPath.split(import_node_path33.default.sep).join("/") }
9188
9792
  };
9189
9793
  if (!graph.hasEdge(edge.id)) {
9190
9794
  graph.addEdgeWithKey(edge.id, edge.source, edge.target, edge);
@@ -9197,9 +9801,9 @@ async function addConfigNodes(graph, services, scanPath) {
9197
9801
 
9198
9802
  // src/extract/proto.ts
9199
9803
  init_cjs_shims();
9200
- var import_node_fs19 = require("fs");
9201
- var import_node_path31 = __toESM(require("path"), 1);
9202
- var import_types20 = require("@neat.is/types");
9804
+ var import_node_fs22 = require("fs");
9805
+ var import_node_path34 = __toESM(require("path"), 1);
9806
+ var import_types23 = require("@neat.is/types");
9203
9807
  var PROTO_EXTENSION = ".proto";
9204
9808
  function packageOf(content) {
9205
9809
  const m = content.match(/^\s*package\s+([A-Za-z_][A-Za-z0-9_.]*)\s*;/m);
@@ -9238,14 +9842,14 @@ function grpcMethodsFromProto(content, fqPackage) {
9238
9842
  async function walkProtoFiles(dir) {
9239
9843
  const out = [];
9240
9844
  async function walk9(current) {
9241
- const entries = await import_node_fs19.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9845
+ const entries = await import_node_fs22.promises.readdir(current, { withFileTypes: true }).catch(() => []);
9242
9846
  for (const entry of entries) {
9243
- const full = import_node_path31.default.join(current, entry.name);
9847
+ const full = import_node_path34.default.join(current, entry.name);
9244
9848
  if (entry.isDirectory()) {
9245
9849
  if (IGNORED_DIRS.has(entry.name)) continue;
9246
9850
  if (await isPythonVenvDir(full)) continue;
9247
9851
  await walk9(full);
9248
- } else if (entry.isFile() && import_node_path31.default.extname(entry.name) === PROTO_EXTENSION) {
9852
+ } else if (entry.isFile() && import_node_path34.default.extname(entry.name) === PROTO_EXTENSION) {
9249
9853
  out.push(full);
9250
9854
  }
9251
9855
  }
@@ -9260,10 +9864,10 @@ async function addGrpcMethods(graph, services) {
9260
9864
  const protoPaths = await walkProtoFiles(service.dir);
9261
9865
  for (const protoPath of protoPaths) {
9262
9866
  if (isTestPath(protoPath)) continue;
9263
- const relFile = toPosix(import_node_path31.default.relative(service.dir, protoPath));
9867
+ const relFile = toPosix(import_node_path34.default.relative(service.dir, protoPath));
9264
9868
  let content;
9265
9869
  try {
9266
- content = await import_node_fs19.promises.readFile(protoPath, "utf8");
9870
+ content = await import_node_fs22.promises.readFile(protoPath, "utf8");
9267
9871
  } catch (err) {
9268
9872
  recordExtractionError("proto extraction", protoPath, err);
9269
9873
  continue;
@@ -9277,11 +9881,11 @@ async function addGrpcMethods(graph, services) {
9277
9881
  }
9278
9882
  if (methods.length === 0) continue;
9279
9883
  for (const method of methods) {
9280
- const mid = (0, import_types20.grpcMethodId)(method.rpcService, method.rpcMethod);
9884
+ const mid = (0, import_types23.grpcMethodId)(method.rpcService, method.rpcMethod);
9281
9885
  if (!graph.hasNode(mid)) {
9282
9886
  const node = {
9283
9887
  id: mid,
9284
- type: import_types20.NodeType.GrpcMethodNode,
9888
+ type: import_types23.NodeType.GrpcMethodNode,
9285
9889
  name: `${method.rpcService}/${method.rpcMethod}`,
9286
9890
  rpcService: method.rpcService,
9287
9891
  rpcMethod: method.rpcMethod,
@@ -9292,15 +9896,15 @@ async function addGrpcMethods(graph, services) {
9292
9896
  graph.addNode(mid, node);
9293
9897
  nodesAdded++;
9294
9898
  }
9295
- const containsId = (0, import_types20.extractedEdgeId)(service.node.id, mid, import_types20.EdgeType.CONTAINS);
9899
+ const containsId = (0, import_types23.extractedEdgeId)(service.node.id, mid, import_types23.EdgeType.CONTAINS);
9296
9900
  if (!graph.hasEdge(containsId)) {
9297
9901
  const edge = {
9298
9902
  id: containsId,
9299
9903
  source: service.node.id,
9300
9904
  target: mid,
9301
- type: import_types20.EdgeType.CONTAINS,
9302
- provenance: import_types20.Provenance.EXTRACTED,
9303
- confidence: (0, import_types20.confidenceForExtracted)("structural"),
9905
+ type: import_types23.EdgeType.CONTAINS,
9906
+ provenance: import_types23.Provenance.EXTRACTED,
9907
+ confidence: (0, import_types23.confidenceForExtracted)("structural"),
9304
9908
  evidence: {
9305
9909
  file: relFile,
9306
9910
  line: method.line,
@@ -9318,16 +9922,16 @@ async function addGrpcMethods(graph, services) {
9318
9922
 
9319
9923
  // src/extract/calls/index.ts
9320
9924
  init_cjs_shims();
9321
- var import_types38 = require("@neat.is/types");
9925
+ var import_types41 = require("@neat.is/types");
9322
9926
 
9323
9927
  // src/extract/calls/http.ts
9324
9928
  init_cjs_shims();
9325
- var import_node_path32 = __toESM(require("path"), 1);
9929
+ var import_node_path35 = __toESM(require("path"), 1);
9326
9930
  var import_tree_sitter6 = __toESM(require("tree-sitter"), 1);
9327
9931
  var import_tree_sitter_javascript4 = __toESM(require("tree-sitter-javascript"), 1);
9328
9932
  var import_tree_sitter_typescript2 = __toESM(require("tree-sitter-typescript"), 1);
9329
- var import_tree_sitter_python3 = __toESM(require("tree-sitter-python"), 1);
9330
- var import_types21 = require("@neat.is/types");
9933
+ var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
9934
+ var import_types24 = require("@neat.is/types");
9331
9935
  var STRING_LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["string_fragment", "string_content"]);
9332
9936
  var JSX_EXTERNAL_LINK_TAGS = /* @__PURE__ */ new Set(["a", "Link", "NavLink", "ExternalLink", "Anchor"]);
9333
9937
  function isInsideJsxExternalLink(node) {
@@ -9383,7 +9987,7 @@ var GRAMMAR_BY_EXT2 = {
9383
9987
  ".jsx": import_tree_sitter_javascript4.default,
9384
9988
  ".mjs": import_tree_sitter_javascript4.default,
9385
9989
  ".cjs": import_tree_sitter_javascript4.default,
9386
- ".py": import_tree_sitter_python3.default
9990
+ ".py": import_tree_sitter_python4.default
9387
9991
  };
9388
9992
  function parserForExt(ext, cache) {
9389
9993
  const grammar = GRAMMAR_BY_EXT2[ext] ?? import_tree_sitter_javascript4.default;
@@ -9405,7 +10009,7 @@ async function addHttpCallEdges(graph, services) {
9405
10009
  const seen = /* @__PURE__ */ new Set();
9406
10010
  for (const file of files) {
9407
10011
  if (isTestPath(file.path)) continue;
9408
- const parser = parserForExt(import_node_path32.default.extname(file.path), parserCache);
10012
+ const parser = parserForExt(import_node_path35.default.extname(file.path), parserCache);
9409
10013
  let sites;
9410
10014
  try {
9411
10015
  sites = callsFromSource(file.content, parser, knownHosts);
@@ -9414,14 +10018,14 @@ async function addHttpCallEdges(graph, services) {
9414
10018
  continue;
9415
10019
  }
9416
10020
  if (sites.length === 0) continue;
9417
- const relFile = toPosix(import_node_path32.default.relative(service.dir, file.path));
10021
+ const relFile = toPosix(import_node_path35.default.relative(service.dir, file.path));
9418
10022
  for (const site of sites) {
9419
10023
  const targetId = hostToNodeId.get(site.host);
9420
10024
  if (!targetId || targetId === service.node.id) continue;
9421
10025
  const dedupKey = `${relFile}|${targetId}`;
9422
10026
  if (seen.has(dedupKey)) continue;
9423
10027
  seen.add(dedupKey);
9424
- const confidence = (0, import_types21.confidenceForExtracted)("url-literal-service-target");
10028
+ const confidence = (0, import_types24.confidenceForExtracted)("url-literal-service-target");
9425
10029
  const ev = {
9426
10030
  file: relFile,
9427
10031
  line: site.line,
@@ -9435,25 +10039,25 @@ async function addHttpCallEdges(graph, services) {
9435
10039
  );
9436
10040
  nodesAdded += n;
9437
10041
  edgesAdded += e;
9438
- if (!(0, import_types21.passesExtractedFloor)(confidence)) {
10042
+ if (!(0, import_types24.passesExtractedFloor)(confidence)) {
9439
10043
  noteExtractedDropped({
9440
10044
  source: fileNodeId,
9441
10045
  target: targetId,
9442
- type: import_types21.EdgeType.CALLS,
10046
+ type: import_types24.EdgeType.CALLS,
9443
10047
  confidence,
9444
10048
  confidenceKind: "url-literal-service-target",
9445
10049
  evidence: ev
9446
10050
  });
9447
10051
  continue;
9448
10052
  }
9449
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types21.EdgeType.CALLS);
10053
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, targetId, import_types24.EdgeType.CALLS);
9450
10054
  if (!graph.hasEdge(edgeId)) {
9451
10055
  const edge = {
9452
10056
  id: edgeId,
9453
10057
  source: fileNodeId,
9454
10058
  target: targetId,
9455
- type: import_types21.EdgeType.CALLS,
9456
- provenance: import_types21.Provenance.EXTRACTED,
10059
+ type: import_types24.EdgeType.CALLS,
10060
+ provenance: import_types24.Provenance.EXTRACTED,
9457
10061
  confidence,
9458
10062
  evidence: ev
9459
10063
  };
@@ -9468,10 +10072,10 @@ async function addHttpCallEdges(graph, services) {
9468
10072
 
9469
10073
  // src/extract/calls/route-match.ts
9470
10074
  init_cjs_shims();
9471
- var import_node_path33 = __toESM(require("path"), 1);
10075
+ var import_node_path36 = __toESM(require("path"), 1);
9472
10076
  var import_tree_sitter7 = __toESM(require("tree-sitter"), 1);
9473
10077
  var import_tree_sitter_javascript5 = __toESM(require("tree-sitter-javascript"), 1);
9474
- var import_types22 = require("@neat.is/types");
10078
+ var import_types25 = require("@neat.is/types");
9475
10079
  var PARSE_CHUNK5 = 16384;
9476
10080
  function parseSource5(parser, source) {
9477
10081
  return parser.parse(
@@ -9636,9 +10240,9 @@ function buildRouteIndex(graph) {
9636
10240
  const index = /* @__PURE__ */ new Map();
9637
10241
  graph.forEachNode((_id, attrs) => {
9638
10242
  const node = attrs;
9639
- if (node.type !== import_types22.NodeType.RouteNode) return;
10243
+ if (node.type !== import_types25.NodeType.RouteNode) return;
9640
10244
  const route = attrs;
9641
- const owner = (0, import_types22.serviceId)(route.service);
10245
+ const owner = (0, import_types25.serviceId)(route.service);
9642
10246
  const entry = {
9643
10247
  method: route.method.toUpperCase(),
9644
10248
  normalizedPath: normalizePathTemplate(route.pathTemplate),
@@ -9667,7 +10271,7 @@ async function addRouteCallEdges(graph, services) {
9667
10271
  const seen = /* @__PURE__ */ new Set();
9668
10272
  for (const file of files) {
9669
10273
  if (isTestPath(file.path)) continue;
9670
- if (!JS_CLIENT_EXTENSIONS.has(import_node_path33.default.extname(file.path))) continue;
10274
+ if (!JS_CLIENT_EXTENSIONS.has(import_node_path36.default.extname(file.path))) continue;
9671
10275
  let sites;
9672
10276
  try {
9673
10277
  sites = clientCallSitesFromSource(file.content, jsParser, knownHosts);
@@ -9676,7 +10280,7 @@ async function addRouteCallEdges(graph, services) {
9676
10280
  continue;
9677
10281
  }
9678
10282
  if (sites.length === 0) continue;
9679
- const relFile = toPosix(import_node_path33.default.relative(service.dir, file.path));
10283
+ const relFile = toPosix(import_node_path36.default.relative(service.dir, file.path));
9680
10284
  for (const site of sites) {
9681
10285
  const serverServiceId = hostToNodeId.get(site.host);
9682
10286
  if (!serverServiceId || serverServiceId === service.node.id) continue;
@@ -9696,7 +10300,7 @@ async function addRouteCallEdges(graph, services) {
9696
10300
  );
9697
10301
  nodesAdded += n;
9698
10302
  edgesAdded += e;
9699
- const confidence = (0, import_types22.confidenceForExtracted)("verified-call-site");
10303
+ const confidence = (0, import_types25.confidenceForExtracted)("verified-call-site");
9700
10304
  const ev = {
9701
10305
  file: relFile,
9702
10306
  line: site.line,
@@ -9704,25 +10308,25 @@ async function addRouteCallEdges(graph, services) {
9704
10308
  method: site.method ?? match.method,
9705
10309
  pathTemplate: site.pathTemplate
9706
10310
  };
9707
- if (!(0, import_types22.passesExtractedFloor)(confidence)) {
10311
+ if (!(0, import_types25.passesExtractedFloor)(confidence)) {
9708
10312
  noteExtractedDropped({
9709
10313
  source: fileNodeId,
9710
10314
  target: match.routeNodeId,
9711
- type: import_types22.EdgeType.CALLS,
10315
+ type: import_types25.EdgeType.CALLS,
9712
10316
  confidence,
9713
10317
  confidenceKind: "verified-call-site",
9714
10318
  evidence: ev
9715
10319
  });
9716
10320
  continue;
9717
10321
  }
9718
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types22.EdgeType.CALLS);
10322
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, match.routeNodeId, import_types25.EdgeType.CALLS);
9719
10323
  if (!graph.hasEdge(edgeId)) {
9720
10324
  const edge = {
9721
10325
  id: edgeId,
9722
10326
  source: fileNodeId,
9723
10327
  target: match.routeNodeId,
9724
- type: import_types22.EdgeType.CALLS,
9725
- provenance: import_types22.Provenance.EXTRACTED,
10328
+ type: import_types25.EdgeType.CALLS,
10329
+ provenance: import_types25.Provenance.EXTRACTED,
9726
10330
  confidence,
9727
10331
  evidence: ev
9728
10332
  };
@@ -9737,8 +10341,8 @@ async function addRouteCallEdges(graph, services) {
9737
10341
 
9738
10342
  // src/extract/calls/kafka.ts
9739
10343
  init_cjs_shims();
9740
- var import_node_path34 = __toESM(require("path"), 1);
9741
- var import_types23 = require("@neat.is/types");
10344
+ var import_node_path37 = __toESM(require("path"), 1);
10345
+ var import_types26 = require("@neat.is/types");
9742
10346
  var PRODUCER_TOPIC_RE = /(?:producer|kafkaProducer)[\s\S]{0,40}?\.send\s*\(\s*\{[\s\S]{0,200}?topic\s*:\s*['"`]([^'"`]+)['"`]/g;
9743
10347
  var CONSUMER_TOPIC_RE = /(?:consumer|kafkaConsumer)[\s\S]{0,40}?\.(?:subscribe|run)\s*\(\s*\{[\s\S]{0,200}?topic[s]?\s*:\s*(?:\[\s*)?['"`]([^'"`]+)['"`]/g;
9744
10348
  function findAll(re, text) {
@@ -9759,7 +10363,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
9759
10363
  seen.add(key);
9760
10364
  const line = lineOf(file.content, topic);
9761
10365
  out.push({
9762
- infraId: (0, import_types23.infraId)("kafka-topic", topic),
10366
+ infraId: (0, import_types26.infraId)("kafka-topic", topic),
9763
10367
  name: topic,
9764
10368
  kind: "kafka-topic",
9765
10369
  edgeType,
@@ -9768,7 +10372,7 @@ function kafkaEndpointsFromFile(file, serviceDir) {
9768
10372
  // tier (ADR-066).
9769
10373
  confidenceKind: "verified-call-site",
9770
10374
  evidence: {
9771
- file: import_node_path34.default.relative(serviceDir, file.path),
10375
+ file: import_node_path37.default.relative(serviceDir, file.path),
9772
10376
  line,
9773
10377
  snippet: snippet(file.content, line)
9774
10378
  }
@@ -9781,8 +10385,8 @@ function kafkaEndpointsFromFile(file, serviceDir) {
9781
10385
 
9782
10386
  // src/extract/calls/redis.ts
9783
10387
  init_cjs_shims();
9784
- var import_node_path35 = __toESM(require("path"), 1);
9785
- var import_types24 = require("@neat.is/types");
10388
+ var import_node_path38 = __toESM(require("path"), 1);
10389
+ var import_types27 = require("@neat.is/types");
9786
10390
  var REDIS_URL_RE = /redis(?:s)?:\/\/(?:[^@'"`\s]+@)?([^:/'"`\s]+)(?::(\d+))?/g;
9787
10391
  function redisEndpointsFromFile(file, serviceDir) {
9788
10392
  const out = [];
@@ -9795,7 +10399,7 @@ function redisEndpointsFromFile(file, serviceDir) {
9795
10399
  seen.add(host);
9796
10400
  const line = lineOf(file.content, host);
9797
10401
  out.push({
9798
- infraId: (0, import_types24.infraId)("redis", host),
10402
+ infraId: (0, import_types27.infraId)("redis", host),
9799
10403
  name: host,
9800
10404
  kind: "redis",
9801
10405
  edgeType: "CALLS",
@@ -9804,7 +10408,7 @@ function redisEndpointsFromFile(file, serviceDir) {
9804
10408
  // support tier (ADR-066).
9805
10409
  confidenceKind: "url-with-structural-support",
9806
10410
  evidence: {
9807
- file: import_node_path35.default.relative(serviceDir, file.path),
10411
+ file: import_node_path38.default.relative(serviceDir, file.path),
9808
10412
  line,
9809
10413
  snippet: snippet(file.content, line)
9810
10414
  }
@@ -9815,8 +10419,8 @@ function redisEndpointsFromFile(file, serviceDir) {
9815
10419
 
9816
10420
  // src/extract/calls/aws.ts
9817
10421
  init_cjs_shims();
9818
- var import_node_path36 = __toESM(require("path"), 1);
9819
- var import_types25 = require("@neat.is/types");
10422
+ var import_node_path39 = __toESM(require("path"), 1);
10423
+ var import_types28 = require("@neat.is/types");
9820
10424
  var S3_BUCKET_RE = /Bucket\s*:\s*['"`]([^'"`]+)['"`]/g;
9821
10425
  var DYNAMO_TABLE_RE = /TableName\s*:\s*['"`]([^'"`]+)['"`]/g;
9822
10426
  function hasMarker(text, markers) {
@@ -9840,7 +10444,7 @@ function awsEndpointsFromFile(file, serviceDir) {
9840
10444
  seen.add(key);
9841
10445
  const line = lineOf(file.content, name);
9842
10446
  out.push({
9843
- infraId: (0, import_types25.infraId)(kind, name),
10447
+ infraId: (0, import_types28.infraId)(kind, name),
9844
10448
  name,
9845
10449
  kind,
9846
10450
  edgeType: "CALLS",
@@ -9849,7 +10453,7 @@ function awsEndpointsFromFile(file, serviceDir) {
9849
10453
  // (ADR-066).
9850
10454
  confidenceKind: "verified-call-site",
9851
10455
  evidence: {
9852
- file: import_node_path36.default.relative(serviceDir, file.path),
10456
+ file: import_node_path39.default.relative(serviceDir, file.path),
9853
10457
  line,
9854
10458
  snippet: snippet(file.content, line)
9855
10459
  }
@@ -9874,8 +10478,8 @@ function awsEndpointsFromFile(file, serviceDir) {
9874
10478
 
9875
10479
  // src/extract/calls/grpc.ts
9876
10480
  init_cjs_shims();
9877
- var import_node_path37 = __toESM(require("path"), 1);
9878
- var import_types26 = require("@neat.is/types");
10481
+ var import_node_path40 = __toESM(require("path"), 1);
10482
+ var import_types29 = require("@neat.is/types");
9879
10483
  var GRPC_CLIENT_RE = /new\s+([A-Z][A-Za-z0-9_]*)Client\s*\(\s*['"`]?([^,'"`)]+)?/g;
9880
10484
  var AWS_SDK_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@aws-sdk\/client-([a-z0-9-]+)['"`]/g;
9881
10485
  var GRPC_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@grpc\/grpc-js['"`]|_grpc_pb['"`]/;
@@ -9924,7 +10528,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
9924
10528
  const { kind } = classified;
9925
10529
  const line = lineOf(file.content, m[0]);
9926
10530
  out.push({
9927
- infraId: (0, import_types26.infraId)(kind, name),
10531
+ infraId: (0, import_types29.infraId)(kind, name),
9928
10532
  name,
9929
10533
  kind,
9930
10534
  edgeType: "CALLS",
@@ -9933,7 +10537,7 @@ function grpcEndpointsFromFile(file, serviceDir) {
9933
10537
  // tier (ADR-066).
9934
10538
  confidenceKind: "verified-call-site",
9935
10539
  evidence: {
9936
- file: import_node_path37.default.relative(serviceDir, file.path),
10540
+ file: import_node_path40.default.relative(serviceDir, file.path),
9937
10541
  line,
9938
10542
  snippet: snippet(file.content, line)
9939
10543
  }
@@ -9944,8 +10548,8 @@ function grpcEndpointsFromFile(file, serviceDir) {
9944
10548
 
9945
10549
  // src/extract/calls/supabase.ts
9946
10550
  init_cjs_shims();
9947
- var import_node_path38 = __toESM(require("path"), 1);
9948
- var import_types27 = require("@neat.is/types");
10551
+ var import_node_path41 = __toESM(require("path"), 1);
10552
+ var import_types30 = require("@neat.is/types");
9949
10553
  var SUPABASE_JS_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/supabase-js['"`]/;
9950
10554
  var SUPABASE_SSR_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])@supabase\/ssr['"`]/;
9951
10555
  var SUPABASE_CLIENT_RE = /\b(createClient|createServerClient|createBrowserClient)\s*\(\s*(?:['"`]([^'"`]*)['"`])?/g;
@@ -9993,7 +10597,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
9993
10597
  seen.add(name);
9994
10598
  const line = lineOf(file.content, m[0]);
9995
10599
  out.push({
9996
- infraId: (0, import_types27.infraId)("supabase", name),
10600
+ infraId: (0, import_types30.infraId)("supabase", name),
9997
10601
  name,
9998
10602
  kind: "supabase",
9999
10603
  edgeType: "CALLS",
@@ -10003,7 +10607,7 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10003
10607
  // tier (ADR-066), the same grade aws.ts / grpc.ts emit at.
10004
10608
  confidenceKind: "verified-call-site",
10005
10609
  evidence: {
10006
- file: import_node_path38.default.relative(serviceDir, file.path),
10610
+ file: import_node_path41.default.relative(serviceDir, file.path),
10007
10611
  line,
10008
10612
  snippet: snippet(file.content, line)
10009
10613
  }
@@ -10024,13 +10628,13 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10024
10628
  seen.add(key);
10025
10629
  const line = lineOf(file.content, am[0]);
10026
10630
  out.push({
10027
- infraId: (0, import_types27.infraId)(kind, resource),
10631
+ infraId: (0, import_types30.infraId)(kind, resource),
10028
10632
  name: resource,
10029
10633
  kind,
10030
10634
  edgeType: "CALLS",
10031
10635
  confidenceKind: "verified-call-site",
10032
10636
  evidence: {
10033
- file: import_node_path38.default.relative(serviceDir, file.path),
10637
+ file: import_node_path41.default.relative(serviceDir, file.path),
10034
10638
  line,
10035
10639
  snippet: snippet(file.content, line)
10036
10640
  }
@@ -10042,10 +10646,10 @@ function supabaseEndpointsFromFile(file, serviceDir) {
10042
10646
 
10043
10647
  // src/extract/calls/firestore.ts
10044
10648
  init_cjs_shims();
10045
- var import_node_path39 = __toESM(require("path"), 1);
10649
+ var import_node_path42 = __toESM(require("path"), 1);
10046
10650
  var import_tree_sitter8 = __toESM(require("tree-sitter"), 1);
10047
10651
  var import_tree_sitter_javascript6 = __toESM(require("tree-sitter-javascript"), 1);
10048
- var import_types28 = require("@neat.is/types");
10652
+ var import_types31 = require("@neat.is/types");
10049
10653
  var FIRESTORE_CLIENT_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase\/firestore['"`]/;
10050
10654
  var FIRESTORE_ADMIN_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])firebase-admin(?:\/firestore)?['"`]/;
10051
10655
  function parserForExt2(ext) {
@@ -10213,7 +10817,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10213
10817
  const hasAdmin = FIRESTORE_ADMIN_IMPORT_RE.test(file.content);
10214
10818
  if (!hasClient && !hasAdmin) return [];
10215
10819
  const fileSdk = hasClient && !hasAdmin ? "client" : hasAdmin && !hasClient ? "admin" : null;
10216
- const tree = parseSource3(parserForExt2(import_node_path39.default.extname(file.path)), file.content);
10820
+ const tree = parseSource3(parserForExt2(import_node_path42.default.extname(file.path)), file.content);
10217
10821
  const clientVars = firestoreClientVars(tree.rootNode);
10218
10822
  const collLine = /* @__PURE__ */ new Map();
10219
10823
  const writes = /* @__PURE__ */ new Map();
@@ -10306,7 +10910,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10306
10910
  }
10307
10911
  }
10308
10912
  out.push({
10309
- infraId: (0, import_types28.infraId)("firestore-collection", collPath),
10913
+ infraId: (0, import_types31.infraId)("firestore-collection", collPath),
10310
10914
  name: collPath,
10311
10915
  kind: "firestore-collection",
10312
10916
  edgeType: "CALLS",
@@ -10317,7 +10921,7 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10317
10921
  ...columnSet.size > 0 ? { columns: [...columnSet] } : {},
10318
10922
  ...sdkWrites ? { sdkWrites } : {},
10319
10923
  evidence: {
10320
- file: import_node_path39.default.relative(serviceDir, file.path),
10924
+ file: import_node_path42.default.relative(serviceDir, file.path),
10321
10925
  line,
10322
10926
  snippet: snippet(file.content, line)
10323
10927
  }
@@ -10328,8 +10932,8 @@ function firestoreEndpointsFromFile(file, serviceDir) {
10328
10932
 
10329
10933
  // src/extract/calls/mongoose.ts
10330
10934
  init_cjs_shims();
10331
- var import_node_path40 = __toESM(require("path"), 1);
10332
- var import_types29 = require("@neat.is/types");
10935
+ var import_node_path43 = __toESM(require("path"), 1);
10936
+ var import_types32 = require("@neat.is/types");
10333
10937
  var MONGOOSE_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongoose['"`]/;
10334
10938
  var MONGODB_IMPORT_RE = /(?:from\s+['"`]|require\(\s*['"`])mongodb['"`]/;
10335
10939
  var PLURALIZE_DISABLED_RE = /\bpluralize\s*\(\s*(?:null|false)\s*\)/;
@@ -10474,12 +11078,12 @@ function collectModelDefs(content, pluralizeOn) {
10474
11078
  function endpoint(r, file, serviceDir, matchText) {
10475
11079
  const line = lineOf(file.content, matchText);
10476
11080
  return {
10477
- infraId: (0, import_types29.infraId)(r.kind, r.name),
11081
+ infraId: (0, import_types32.infraId)(r.kind, r.name),
10478
11082
  name: r.name,
10479
11083
  kind: r.kind,
10480
11084
  edgeType: "CALLS",
10481
11085
  confidenceKind: "verified-call-site",
10482
- evidence: { file: import_node_path40.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11086
+ evidence: { file: import_node_path43.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
10483
11087
  };
10484
11088
  }
10485
11089
  function mongooseEndpointsFromFile(file, serviceDir) {
@@ -10569,7 +11173,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
10569
11173
  const registry = /* @__PURE__ */ new Map();
10570
11174
  for (const f of mongooseFiles) {
10571
11175
  const fx = fileExportsOf(f.content, pluralizeOn);
10572
- if (fx) registry.set(toPosix(import_node_path40.default.relative(serviceDir, f.path)), fx);
11176
+ if (fx) registry.set(toPosix(import_node_path43.default.relative(serviceDir, f.path)), fx);
10573
11177
  }
10574
11178
  if (registry.size === 0) return [];
10575
11179
  const out = [];
@@ -10580,7 +11184,7 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
10580
11184
  const directColl = /* @__PURE__ */ new Map();
10581
11185
  const nsExports = /* @__PURE__ */ new Map();
10582
11186
  for (const b of bindings) {
10583
- const resolvedRel = await resolveJsImport(b.specifier, import_node_path40.default.dirname(f.path), serviceDir, null);
11187
+ const resolvedRel = await resolveJsImport(b.specifier, import_node_path43.default.dirname(f.path), serviceDir, null);
10584
11188
  if (!resolvedRel) continue;
10585
11189
  const fx = registry.get(resolvedRel);
10586
11190
  if (!fx) continue;
@@ -10624,15 +11228,15 @@ async function mongooseCrossFileEndpoints(files, serviceDir) {
10624
11228
 
10625
11229
  // src/extract/calls/sqlalchemy.ts
10626
11230
  init_cjs_shims();
10627
- var import_node_path41 = __toESM(require("path"), 1);
11231
+ var import_node_path44 = __toESM(require("path"), 1);
10628
11232
  var import_tree_sitter9 = __toESM(require("tree-sitter"), 1);
10629
- var import_tree_sitter_python4 = __toESM(require("tree-sitter-python"), 1);
10630
- var import_types30 = require("@neat.is/types");
11233
+ var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
11234
+ var import_types33 = require("@neat.is/types");
10631
11235
  var SQLALCHEMY_IMPORT_RE = /(?:from|import)\s+(?:flask_sqlalchemy|sqlalchemy)\b/;
10632
11236
  var PARSE_CHUNK6 = 16384;
10633
11237
  function makePyParser3() {
10634
11238
  const p = new import_tree_sitter9.default();
10635
- p.setLanguage(import_tree_sitter_python4.default);
11239
+ p.setLanguage(import_tree_sitter_python5.default);
10636
11240
  return p;
10637
11241
  }
10638
11242
  function parseSource6(parser, source) {
@@ -10763,7 +11367,7 @@ function sqlalchemyForeignKeys(file, serviceDir) {
10763
11367
  childTable,
10764
11368
  parentTable,
10765
11369
  evidence: {
10766
- file: import_node_path41.default.relative(serviceDir, file.path),
11370
+ file: import_node_path44.default.relative(serviceDir, file.path),
10767
11371
  line,
10768
11372
  snippet: snippet(file.content, line)
10769
11373
  }
@@ -10781,14 +11385,14 @@ function sqlalchemyEndpointsFromFile(file, serviceDir) {
10781
11385
  if (seen.has(name)) return;
10782
11386
  seen.add(name);
10783
11387
  out.push({
10784
- infraId: (0, import_types30.infraId)("sql-table", name),
11388
+ infraId: (0, import_types33.infraId)("sql-table", name),
10785
11389
  name,
10786
11390
  kind: "sql-table",
10787
11391
  edgeType: "CALLS",
10788
11392
  confidenceKind: "verified-call-site",
10789
11393
  ...columns && columns.length > 0 ? { columns } : {},
10790
11394
  evidence: {
10791
- file: import_node_path41.default.relative(serviceDir, file.path),
11395
+ file: import_node_path44.default.relative(serviceDir, file.path),
10792
11396
  line,
10793
11397
  snippet: snippet(file.content, line)
10794
11398
  }
@@ -10889,13 +11493,13 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
10889
11493
  if (seen.has(key)) continue;
10890
11494
  seen.add(key);
10891
11495
  out.push({
10892
- infraId: (0, import_types30.infraId)("sql-table", t),
11496
+ infraId: (0, import_types33.infraId)("sql-table", t),
10893
11497
  name: t,
10894
11498
  kind: "sql-table",
10895
11499
  edgeType: "CALLS",
10896
11500
  confidenceKind: "verified-call-site",
10897
11501
  evidence: {
10898
- file: import_node_path41.default.relative(serviceDir, file.path),
11502
+ file: import_node_path44.default.relative(serviceDir, file.path),
10899
11503
  line,
10900
11504
  snippet: snippet(file.content, line)
10901
11505
  }
@@ -10907,15 +11511,15 @@ function pythonOrmCrossFileEndpoints(files, serviceDir) {
10907
11511
 
10908
11512
  // src/extract/calls/django-orm.ts
10909
11513
  init_cjs_shims();
10910
- var import_node_path42 = __toESM(require("path"), 1);
11514
+ var import_node_path45 = __toESM(require("path"), 1);
10911
11515
  var import_tree_sitter10 = __toESM(require("tree-sitter"), 1);
10912
- var import_tree_sitter_python5 = __toESM(require("tree-sitter-python"), 1);
10913
- var import_types31 = require("@neat.is/types");
11516
+ var import_tree_sitter_python6 = __toESM(require("tree-sitter-python"), 1);
11517
+ var import_types34 = require("@neat.is/types");
10914
11518
  var DJANGO_IMPORT_RE = /(?:from|import)\s+django\b/;
10915
11519
  var PARSE_CHUNK7 = 16384;
10916
11520
  function makePyParser4() {
10917
11521
  const p = new import_tree_sitter10.default();
10918
- p.setLanguage(import_tree_sitter_python5.default);
11522
+ p.setLanguage(import_tree_sitter_python6.default);
10919
11523
  return p;
10920
11524
  }
10921
11525
  function parseSource7(parser, source) {
@@ -10977,7 +11581,7 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
10977
11581
  const tree = parseSource7(makePyParser4(), file.content);
10978
11582
  const out = [];
10979
11583
  const seen = /* @__PURE__ */ new Set();
10980
- const defaultAppLabel = import_node_path42.default.basename(import_node_path42.default.dirname(file.path));
11584
+ const defaultAppLabel = import_node_path45.default.basename(import_node_path45.default.dirname(file.path));
10981
11585
  walk4(tree.rootNode, (node) => {
10982
11586
  if (node.type !== "class_definition") return;
10983
11587
  if (!extendsDjangoModel(node)) return;
@@ -10990,12 +11594,12 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
10990
11594
  seen.add(table);
10991
11595
  const line = node.startPosition.row + 1;
10992
11596
  out.push({
10993
- infraId: (0, import_types31.infraId)("sql-table", table),
11597
+ infraId: (0, import_types34.infraId)("sql-table", table),
10994
11598
  name: table,
10995
11599
  kind: "sql-table",
10996
11600
  edgeType: "CALLS",
10997
11601
  confidenceKind: "verified-call-site",
10998
- evidence: { file: import_node_path42.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
11602
+ evidence: { file: import_node_path45.default.relative(serviceDir, file.path), line, snippet: snippet(file.content, line) }
10999
11603
  });
11000
11604
  });
11001
11605
  return out;
@@ -11003,10 +11607,10 @@ function djangoOrmEndpointsFromFile(file, serviceDir) {
11003
11607
 
11004
11608
  // src/extract/calls/drizzle.ts
11005
11609
  init_cjs_shims();
11006
- var import_node_path43 = __toESM(require("path"), 1);
11610
+ var import_node_path46 = __toESM(require("path"), 1);
11007
11611
  var import_tree_sitter11 = __toESM(require("tree-sitter"), 1);
11008
11612
  var import_tree_sitter_javascript7 = __toESM(require("tree-sitter-javascript"), 1);
11009
- var import_types32 = require("@neat.is/types");
11613
+ var import_types35 = require("@neat.is/types");
11010
11614
  var DRIZZLE_IMPORT_RE = /drizzle-orm/;
11011
11615
  var TABLE_BUILDERS = /* @__PURE__ */ new Set(["pgTable", "mysqlTable", "sqliteTable"]);
11012
11616
  function parserForExt3(ext) {
@@ -11081,7 +11685,7 @@ function columnsFromObject(obj) {
11081
11685
  }
11082
11686
  function drizzleEndpointsFromFile(file, serviceDir) {
11083
11687
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11084
- const tree = parseSource3(parserForExt3(import_node_path43.default.extname(file.path)), file.content);
11688
+ const tree = parseSource3(parserForExt3(import_node_path46.default.extname(file.path)), file.content);
11085
11689
  const out = [];
11086
11690
  const seen = /* @__PURE__ */ new Set();
11087
11691
  const walk9 = (node) => {
@@ -11097,14 +11701,14 @@ function drizzleEndpointsFromFile(file, serviceDir) {
11097
11701
  const columns = columnsFromObject(obj);
11098
11702
  const line = node.startPosition.row + 1;
11099
11703
  out.push({
11100
- infraId: (0, import_types32.infraId)("sql-table", tableName),
11704
+ infraId: (0, import_types35.infraId)("sql-table", tableName),
11101
11705
  name: tableName,
11102
11706
  kind: "sql-table",
11103
11707
  edgeType: "CALLS",
11104
11708
  confidenceKind: "structural",
11105
11709
  columns,
11106
11710
  evidence: {
11107
- file: import_node_path43.default.relative(serviceDir, file.path),
11711
+ file: import_node_path46.default.relative(serviceDir, file.path),
11108
11712
  line,
11109
11713
  snippet: snippet(file.content, line)
11110
11714
  }
@@ -11170,7 +11774,7 @@ function referencesTargetVar(call) {
11170
11774
  }
11171
11775
  function drizzleForeignKeys(file, serviceDir) {
11172
11776
  if (!DRIZZLE_IMPORT_RE.test(file.content)) return [];
11173
- const tree = parseSource3(parserForExt3(import_node_path43.default.extname(file.path)), file.content);
11777
+ const tree = parseSource3(parserForExt3(import_node_path46.default.extname(file.path)), file.content);
11174
11778
  const { tables, varToTable } = collectDrizzleTables(tree.rootNode);
11175
11779
  const out = [];
11176
11780
  const seen = /* @__PURE__ */ new Set();
@@ -11189,7 +11793,7 @@ function drizzleForeignKeys(file, serviceDir) {
11189
11793
  childTable: table.tableName,
11190
11794
  parentTable,
11191
11795
  evidence: {
11192
- file: import_node_path43.default.relative(serviceDir, file.path),
11796
+ file: import_node_path46.default.relative(serviceDir, file.path),
11193
11797
  line,
11194
11798
  snippet: snippet(file.content, line)
11195
11799
  }
@@ -11206,8 +11810,8 @@ function drizzleForeignKeys(file, serviceDir) {
11206
11810
 
11207
11811
  // src/extract/calls/prisma.ts
11208
11812
  init_cjs_shims();
11209
- var import_node_path44 = __toESM(require("path"), 1);
11210
- var import_types33 = require("@neat.is/types");
11813
+ var import_node_path47 = __toESM(require("path"), 1);
11814
+ var import_types36 = require("@neat.is/types");
11211
11815
  var SCALAR_TYPES = /* @__PURE__ */ new Set([
11212
11816
  "Int",
11213
11817
  "String",
@@ -11255,14 +11859,14 @@ function prismaColumnsFromSchema(file, serviceDir) {
11255
11859
  if (seenTable.has(b.tableName)) return;
11256
11860
  seenTable.add(b.tableName);
11257
11861
  out.push({
11258
- infraId: (0, import_types33.infraId)("sql-table", b.tableName),
11862
+ infraId: (0, import_types36.infraId)("sql-table", b.tableName),
11259
11863
  name: b.tableName,
11260
11864
  kind: "sql-table",
11261
11865
  edgeType: "CALLS",
11262
11866
  confidenceKind: "structural",
11263
11867
  columns: b.columns,
11264
11868
  evidence: {
11265
- file: import_node_path44.default.relative(serviceDir, file.path),
11869
+ file: import_node_path47.default.relative(serviceDir, file.path),
11266
11870
  line: b.startLine,
11267
11871
  snippet: snippet(content, b.startLine)
11268
11872
  }
@@ -11323,7 +11927,7 @@ function prismaColumnsFromSchema(file, serviceDir) {
11323
11927
  }
11324
11928
  async function prismaColumnEndpoints(serviceDir) {
11325
11929
  const schemaPath = await findFirst(serviceDir, [
11326
- import_node_path44.default.join("prisma", "schema.prisma"),
11930
+ import_node_path47.default.join("prisma", "schema.prisma"),
11327
11931
  "schema.prisma"
11328
11932
  ]);
11329
11933
  if (!schemaPath) return [];
@@ -11398,7 +12002,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11398
12002
  childTable: current.table,
11399
12003
  parentTable,
11400
12004
  evidence: {
11401
- file: import_node_path44.default.relative(serviceDir, file.path),
12005
+ file: import_node_path47.default.relative(serviceDir, file.path),
11402
12006
  line: lineNo,
11403
12007
  snippet: snippet(content, lineNo)
11404
12008
  }
@@ -11413,7 +12017,7 @@ function prismaForeignKeysFromSchema(file, serviceDir) {
11413
12017
  }
11414
12018
  async function prismaForeignKeys(serviceDir) {
11415
12019
  const schemaPath = await findFirst(serviceDir, [
11416
- import_node_path44.default.join("prisma", "schema.prisma"),
12020
+ import_node_path47.default.join("prisma", "schema.prisma"),
11417
12021
  "schema.prisma"
11418
12022
  ]);
11419
12023
  if (!schemaPath) return [];
@@ -11424,15 +12028,15 @@ async function prismaForeignKeys(serviceDir) {
11424
12028
 
11425
12029
  // src/extract/calls/activerecord.ts
11426
12030
  init_cjs_shims();
11427
- var import_node_path45 = __toESM(require("path"), 1);
12031
+ var import_node_path48 = __toESM(require("path"), 1);
11428
12032
  var import_tree_sitter12 = __toESM(require("tree-sitter"), 1);
11429
- var import_tree_sitter_ruby2 = __toESM(require("tree-sitter-ruby"), 1);
11430
- var import_types34 = require("@neat.is/types");
12033
+ var import_tree_sitter_ruby3 = __toESM(require("tree-sitter-ruby"), 1);
12034
+ var import_types37 = require("@neat.is/types");
11431
12035
  var AR_SCHEMA_RE = /ActiveRecord::Schema/;
11432
12036
  var PARSE_CHUNK8 = 16384;
11433
12037
  function makeRubyParser2() {
11434
12038
  const p = new import_tree_sitter12.default();
11435
- p.setLanguage(import_tree_sitter_ruby2.default);
12039
+ p.setLanguage(import_tree_sitter_ruby3.default);
11436
12040
  return p;
11437
12041
  }
11438
12042
  function parseSource8(parser, source) {
@@ -11625,14 +12229,14 @@ function railsSchemaEndpointsFromFile(file, serviceDir) {
11625
12229
  if (!table || seen.has(table.name)) return;
11626
12230
  seen.add(table.name);
11627
12231
  out.push({
11628
- infraId: (0, import_types34.infraId)("sql-table", table.name),
12232
+ infraId: (0, import_types37.infraId)("sql-table", table.name),
11629
12233
  name: table.name,
11630
12234
  kind: "sql-table",
11631
12235
  edgeType: "CALLS",
11632
12236
  confidenceKind: "structural",
11633
12237
  ...table.columns.length > 0 ? { columns: table.columns } : {},
11634
12238
  evidence: {
11635
- file: import_node_path45.default.relative(serviceDir, file.path),
12239
+ file: import_node_path48.default.relative(serviceDir, file.path),
11636
12240
  line: table.line,
11637
12241
  snippet: snippet(file.content, table.line)
11638
12242
  }
@@ -11654,7 +12258,7 @@ function railsSchemaForeignKeys(file, serviceDir) {
11654
12258
  childTable,
11655
12259
  parentTable,
11656
12260
  evidence: {
11657
- file: import_node_path45.default.relative(serviceDir, file.path),
12261
+ file: import_node_path48.default.relative(serviceDir, file.path),
11658
12262
  line,
11659
12263
  snippet: snippet(file.content, line)
11660
12264
  }
@@ -11731,13 +12335,13 @@ function railsModelEndpointsFromFile(file, serviceDir) {
11731
12335
  seen.add(table);
11732
12336
  const line = node.startPosition.row + 1;
11733
12337
  out.push({
11734
- infraId: (0, import_types34.infraId)("sql-table", table),
12338
+ infraId: (0, import_types37.infraId)("sql-table", table),
11735
12339
  name: table,
11736
12340
  kind: "sql-table",
11737
12341
  edgeType: "CALLS",
11738
12342
  confidenceKind: "verified-call-site",
11739
12343
  evidence: {
11740
- file: import_node_path45.default.relative(serviceDir, file.path),
12344
+ file: import_node_path48.default.relative(serviceDir, file.path),
11741
12345
  line,
11742
12346
  snippet: snippet(file.content, line)
11743
12347
  }
@@ -11774,7 +12378,7 @@ function railsModelForeignKeys(file, serviceDir) {
11774
12378
  childTable,
11775
12379
  parentTable,
11776
12380
  evidence: {
11777
- file: import_node_path45.default.relative(serviceDir, file.path),
12381
+ file: import_node_path48.default.relative(serviceDir, file.path),
11778
12382
  line,
11779
12383
  snippet: snippet(file.content, line)
11780
12384
  }
@@ -11786,15 +12390,15 @@ function railsModelForeignKeys(file, serviceDir) {
11786
12390
 
11787
12391
  // src/extract/calls/eloquent.ts
11788
12392
  init_cjs_shims();
11789
- var import_node_path46 = __toESM(require("path"), 1);
12393
+ var import_node_path49 = __toESM(require("path"), 1);
11790
12394
  var import_tree_sitter13 = __toESM(require("tree-sitter"), 1);
11791
- var import_tree_sitter_php2 = __toESM(require("tree-sitter-php"), 1);
11792
- var import_types35 = require("@neat.is/types");
12395
+ var import_tree_sitter_php3 = __toESM(require("tree-sitter-php"), 1);
12396
+ var import_types38 = require("@neat.is/types");
11793
12397
  var LARAVEL_SCHEMA_RE = /\bSchema::(create|createIfNotExists|table)\b/;
11794
12398
  var PARSE_CHUNK9 = 16384;
11795
12399
  function makePhpParser2() {
11796
12400
  const p = new import_tree_sitter13.default();
11797
- p.setLanguage(import_tree_sitter_php2.default.php_only);
12401
+ p.setLanguage(import_tree_sitter_php3.default.php_only);
11798
12402
  return p;
11799
12403
  }
11800
12404
  function parseSource9(parser, source) {
@@ -12036,14 +12640,14 @@ function laravelMigrationEndpointsFromFile(file, serviceDir) {
12036
12640
  }
12037
12641
  }
12038
12642
  out.push({
12039
- infraId: (0, import_types35.infraId)("sql-table", bp.table),
12643
+ infraId: (0, import_types38.infraId)("sql-table", bp.table),
12040
12644
  name: bp.table,
12041
12645
  kind: "sql-table",
12042
12646
  edgeType: "CALLS",
12043
12647
  confidenceKind: "structural",
12044
12648
  ...columns.length > 0 ? { columns } : {},
12045
12649
  evidence: {
12046
- file: import_node_path46.default.relative(serviceDir, file.path),
12650
+ file: import_node_path49.default.relative(serviceDir, file.path),
12047
12651
  line: bp.line,
12048
12652
  snippet: snippet(file.content, bp.line)
12049
12653
  }
@@ -12065,7 +12669,7 @@ function laravelMigrationForeignKeys(file, serviceDir) {
12065
12669
  childTable,
12066
12670
  parentTable,
12067
12671
  evidence: {
12068
- file: import_node_path46.default.relative(serviceDir, file.path),
12672
+ file: import_node_path49.default.relative(serviceDir, file.path),
12069
12673
  line,
12070
12674
  snippet: snippet(file.content, line)
12071
12675
  }
@@ -12179,13 +12783,13 @@ function laravelModelEndpointsFromFile(file, serviceDir) {
12179
12783
  seen.add(table);
12180
12784
  const line = node.startPosition.row + 1;
12181
12785
  out.push({
12182
- infraId: (0, import_types35.infraId)("sql-table", table),
12786
+ infraId: (0, import_types38.infraId)("sql-table", table),
12183
12787
  name: table,
12184
12788
  kind: "sql-table",
12185
12789
  edgeType: "CALLS",
12186
12790
  confidenceKind: "verified-call-site",
12187
12791
  evidence: {
12188
- file: import_node_path46.default.relative(serviceDir, file.path),
12792
+ file: import_node_path49.default.relative(serviceDir, file.path),
12189
12793
  line,
12190
12794
  snippet: snippet(file.content, line)
12191
12795
  }
@@ -12221,7 +12825,7 @@ function laravelModelForeignKeys(file, serviceDir) {
12221
12825
  childTable,
12222
12826
  parentTable,
12223
12827
  evidence: {
12224
- file: import_node_path46.default.relative(serviceDir, file.path),
12828
+ file: import_node_path49.default.relative(serviceDir, file.path),
12225
12829
  line,
12226
12830
  snippet: snippet(file.content, line)
12227
12831
  }
@@ -12233,10 +12837,10 @@ function laravelModelForeignKeys(file, serviceDir) {
12233
12837
 
12234
12838
  // src/extract/calls/go.ts
12235
12839
  init_cjs_shims();
12236
- var import_node_path49 = __toESM(require("path"), 1);
12840
+ var import_node_path52 = __toESM(require("path"), 1);
12237
12841
  var import_tree_sitter14 = __toESM(require("tree-sitter"), 1);
12238
- var import_tree_sitter_go3 = __toESM(require("tree-sitter-go"), 1);
12239
- var import_types36 = require("@neat.is/types");
12842
+ var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12843
+ var import_types39 = require("@neat.is/types");
12240
12844
  init_otel();
12241
12845
  var PARSE_CHUNK10 = 16384;
12242
12846
  var DATABASE_SQL_METHODS = /* @__PURE__ */ new Set([
@@ -12265,7 +12869,7 @@ var DATABASE_SQL_IMPORT = "database/sql";
12265
12869
  var SQLX_IMPORT = "github.com/jmoiron/sqlx";
12266
12870
  function makeGoParser3() {
12267
12871
  const p = new import_tree_sitter14.default();
12268
- p.setLanguage(import_tree_sitter_go3.default);
12872
+ p.setLanguage(import_tree_sitter_go4.default);
12269
12873
  return p;
12270
12874
  }
12271
12875
  function parseSource10(parser, source) {
@@ -12308,7 +12912,7 @@ function firstStringLiteralArg(argsNode) {
12308
12912
  return null;
12309
12913
  }
12310
12914
  function goSqlEndpointsFromFile(file, serviceDir) {
12311
- if (import_node_path49.default.extname(file.path) !== ".go") return [];
12915
+ if (import_node_path52.default.extname(file.path) !== ".go") return [];
12312
12916
  if (!file.content.includes(DATABASE_SQL_IMPORT) && !file.content.includes(SQLX_IMPORT)) return [];
12313
12917
  const tree = parseSource10(makeGoParser3(), file.content);
12314
12918
  const importsDatabaseSql = goImportsAny(tree.rootNode, /* @__PURE__ */ new Set([DATABASE_SQL_IMPORT]));
@@ -12330,14 +12934,14 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12330
12934
  const columns = columnsFromSqlStatement(sql);
12331
12935
  const line = node.startPosition.row + 1;
12332
12936
  out.push({
12333
- infraId: (0, import_types36.infraId)("sql-table", table),
12937
+ infraId: (0, import_types39.infraId)("sql-table", table),
12334
12938
  name: table,
12335
12939
  kind: "sql-table",
12336
12940
  edgeType: "CALLS",
12337
12941
  confidenceKind: "verified-call-site",
12338
12942
  ...columns.length > 0 ? { columns } : {},
12339
12943
  evidence: {
12340
- file: toPosix(import_node_path49.default.relative(serviceDir, file.path)),
12944
+ file: toPosix(import_node_path52.default.relative(serviceDir, file.path)),
12341
12945
  line,
12342
12946
  snippet: snippet(file.content, line)
12343
12947
  }
@@ -12348,15 +12952,15 @@ function goSqlEndpointsFromFile(file, serviceDir) {
12348
12952
 
12349
12953
  // src/extract/calls/gorm.ts
12350
12954
  init_cjs_shims();
12351
- var import_node_path50 = __toESM(require("path"), 1);
12955
+ var import_node_path53 = __toESM(require("path"), 1);
12352
12956
  var import_tree_sitter15 = __toESM(require("tree-sitter"), 1);
12353
- var import_tree_sitter_go4 = __toESM(require("tree-sitter-go"), 1);
12354
- var import_types37 = require("@neat.is/types");
12957
+ var import_tree_sitter_go5 = __toESM(require("tree-sitter-go"), 1);
12958
+ var import_types40 = require("@neat.is/types");
12355
12959
  var GORM_IMPORT_RE = /gorm\.io\/gorm/;
12356
12960
  var PARSE_CHUNK11 = 16384;
12357
12961
  function makeGoParser4() {
12358
12962
  const p = new import_tree_sitter15.default();
12359
- p.setLanguage(import_tree_sitter_go4.default);
12963
+ p.setLanguage(import_tree_sitter_go5.default);
12360
12964
  return p;
12361
12965
  }
12362
12966
  function parseSource11(parser, source) {
@@ -12781,7 +13385,7 @@ function collectColumns(struct, structs, seen, prefix, out, emitted) {
12781
13385
  seen.delete(struct.name);
12782
13386
  }
12783
13387
  function gormEndpointsFromFile(file, serviceDir) {
12784
- if (import_node_path50.default.extname(file.path) !== ".go") return [];
13388
+ if (import_node_path53.default.extname(file.path) !== ".go") return [];
12785
13389
  if (!GORM_IMPORT_RE.test(file.content)) return [];
12786
13390
  const tree = parseSource11(makeGoParser4(), file.content);
12787
13391
  const { structs, models, tableFor } = analyze(tree);
@@ -12796,14 +13400,14 @@ function gormEndpointsFromFile(file, serviceDir) {
12796
13400
  const columns = [];
12797
13401
  collectColumns(struct, structs, /* @__PURE__ */ new Set(), "", columns, /* @__PURE__ */ new Set());
12798
13402
  out.push({
12799
- infraId: (0, import_types37.infraId)("sql-table", table),
13403
+ infraId: (0, import_types40.infraId)("sql-table", table),
12800
13404
  name: table,
12801
13405
  kind: "sql-table",
12802
13406
  edgeType: "CALLS",
12803
13407
  confidenceKind: "structural",
12804
13408
  ...columns.length > 0 ? { columns } : {},
12805
13409
  evidence: {
12806
- file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
13410
+ file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
12807
13411
  line: struct.line,
12808
13412
  snippet: snippet(file.content, struct.line)
12809
13413
  }
@@ -12812,7 +13416,7 @@ function gormEndpointsFromFile(file, serviceDir) {
12812
13416
  return out;
12813
13417
  }
12814
13418
  function gormForeignKeys(file, serviceDir) {
12815
- if (import_node_path50.default.extname(file.path) !== ".go") return [];
13419
+ if (import_node_path53.default.extname(file.path) !== ".go") return [];
12816
13420
  if (!GORM_IMPORT_RE.test(file.content)) return [];
12817
13421
  const tree = parseSource11(makeGoParser4(), file.content);
12818
13422
  const { structs, models, tableFor } = analyze(tree);
@@ -12827,7 +13431,7 @@ function gormForeignKeys(file, serviceDir) {
12827
13431
  childTable,
12828
13432
  parentTable,
12829
13433
  evidence: {
12830
- file: toPosix(import_node_path50.default.relative(serviceDir, file.path)),
13434
+ file: toPosix(import_node_path53.default.relative(serviceDir, file.path)),
12831
13435
  line,
12832
13436
  snippet: snippet(file.content, line)
12833
13437
  }
@@ -12866,11 +13470,11 @@ function gormForeignKeys(file, serviceDir) {
12866
13470
  function edgeTypeFromEndpoint(ep) {
12867
13471
  switch (ep.edgeType) {
12868
13472
  case "PUBLISHES_TO":
12869
- return import_types38.EdgeType.PUBLISHES_TO;
13473
+ return import_types41.EdgeType.PUBLISHES_TO;
12870
13474
  case "CONSUMES_FROM":
12871
- return import_types38.EdgeType.CONSUMES_FROM;
13475
+ return import_types41.EdgeType.CONSUMES_FROM;
12872
13476
  default:
12873
- return import_types38.EdgeType.CALLS;
13477
+ return import_types41.EdgeType.CALLS;
12874
13478
  }
12875
13479
  }
12876
13480
  function isAwsKind(kind) {
@@ -12930,7 +13534,7 @@ async function addExternalEndpointEdges(graph, services) {
12930
13534
  if (!graph.hasNode(ep.infraId)) {
12931
13535
  const node = {
12932
13536
  id: ep.infraId,
12933
- type: import_types38.NodeType.InfraNode,
13537
+ type: import_types41.NodeType.InfraNode,
12934
13538
  name: ep.name,
12935
13539
  // #238 — `aws-*` covers AWS-SDK client kinds (aws-s3, aws-dynamodb,
12936
13540
  // aws-cognito-identity-provider, …); `s3-` / `dynamodb-` cover the
@@ -12943,21 +13547,21 @@ async function addExternalEndpointEdges(graph, services) {
12943
13547
  }
12944
13548
  if (ep.columns && ep.columns.length > 0) {
12945
13549
  const node = graph.getNodeAttributes(ep.infraId);
12946
- if (node.type === import_types38.NodeType.InfraNode) {
13550
+ if (node.type === import_types41.NodeType.InfraNode) {
12947
13551
  graph.replaceNodeAttributes(ep.infraId, {
12948
13552
  ...node,
12949
13553
  columns: foldColumns(
12950
13554
  node.columns,
12951
13555
  ep.columns,
12952
- import_types38.Provenance.EXTRACTED,
12953
- (0, import_types38.confidenceForExtracted)(ep.confidenceKind)
13556
+ import_types41.Provenance.EXTRACTED,
13557
+ (0, import_types41.confidenceForExtracted)(ep.confidenceKind)
12954
13558
  )
12955
13559
  });
12956
13560
  }
12957
13561
  }
12958
13562
  if (ep.sdkWrites && Object.keys(ep.sdkWrites).length > 0) {
12959
13563
  const node = graph.getNodeAttributes(ep.infraId);
12960
- if (node.type === import_types38.NodeType.InfraNode) {
13564
+ if (node.type === import_types41.NodeType.InfraNode) {
12961
13565
  graph.replaceNodeAttributes(ep.infraId, {
12962
13566
  ...node,
12963
13567
  columns: foldSdkWrites(node.columns, ep.sdkWrites)
@@ -12965,7 +13569,7 @@ async function addExternalEndpointEdges(graph, services) {
12965
13569
  }
12966
13570
  }
12967
13571
  const edgeType = edgeTypeFromEndpoint(ep);
12968
- const confidence = (0, import_types38.confidenceForExtracted)(ep.confidenceKind);
13572
+ const confidence = (0, import_types41.confidenceForExtracted)(ep.confidenceKind);
12969
13573
  const relFile = toPosix(ep.evidence.file);
12970
13574
  const { fileNodeId, nodesAdded: n, edgesAdded: e } = ensureFileNode(
12971
13575
  graph,
@@ -12975,7 +13579,7 @@ async function addExternalEndpointEdges(graph, services) {
12975
13579
  );
12976
13580
  nodesAdded += n;
12977
13581
  edgesAdded += e;
12978
- if (!(0, import_types38.passesExtractedFloor)(confidence)) {
13582
+ if (!(0, import_types41.passesExtractedFloor)(confidence)) {
12979
13583
  noteExtractedDropped({
12980
13584
  source: fileNodeId,
12981
13585
  target: ep.infraId,
@@ -12995,7 +13599,7 @@ async function addExternalEndpointEdges(graph, services) {
12995
13599
  source: fileNodeId,
12996
13600
  target: ep.infraId,
12997
13601
  type: edgeType,
12998
- provenance: import_types38.Provenance.EXTRACTED,
13602
+ provenance: import_types41.Provenance.EXTRACTED,
12999
13603
  confidence,
13000
13604
  evidence: ep.evidence
13001
13605
  };
@@ -13018,7 +13622,7 @@ async function addCallEdges(graph, services) {
13018
13622
 
13019
13623
  // src/extract/table-edges.ts
13020
13624
  init_cjs_shims();
13021
- var import_types39 = require("@neat.is/types");
13625
+ var import_types42 = require("@neat.is/types");
13022
13626
  async function addTableEdges(graph, services) {
13023
13627
  let nodesAdded = 0;
13024
13628
  let edgesAdded = 0;
@@ -13046,20 +13650,20 @@ async function addTableEdges(graph, services) {
13046
13650
  }
13047
13651
  refs.push(...modelRefs);
13048
13652
  for (const ref of refs) {
13049
- const childId = (0, import_types39.infraId)("sql-table", ref.childTable);
13050
- const parentId = (0, import_types39.infraId)("sql-table", ref.parentTable);
13653
+ const childId = (0, import_types42.infraId)("sql-table", ref.childTable);
13654
+ const parentId = (0, import_types42.infraId)("sql-table", ref.parentTable);
13051
13655
  if (childId === parentId) continue;
13052
13656
  nodesAdded += ensureTableNode(graph, childId, ref.childTable);
13053
13657
  nodesAdded += ensureTableNode(graph, parentId, ref.parentTable);
13054
- const edgeId = (0, import_types39.extractedEdgeId)(childId, parentId, import_types39.EdgeType.REFERENCES);
13658
+ const edgeId = (0, import_types42.extractedEdgeId)(childId, parentId, import_types42.EdgeType.REFERENCES);
13055
13659
  if (graph.hasEdge(edgeId)) continue;
13056
13660
  const edge = {
13057
13661
  id: edgeId,
13058
13662
  source: childId,
13059
13663
  target: parentId,
13060
- type: import_types39.EdgeType.REFERENCES,
13061
- provenance: import_types39.Provenance.EXTRACTED,
13062
- confidence: (0, import_types39.confidenceForExtracted)("structural"),
13664
+ type: import_types42.EdgeType.REFERENCES,
13665
+ provenance: import_types42.Provenance.EXTRACTED,
13666
+ confidence: (0, import_types42.confidenceForExtracted)("structural"),
13063
13667
  evidence: ref.evidence
13064
13668
  };
13065
13669
  graph.addEdgeWithKey(edgeId, childId, parentId, edge);
@@ -13072,7 +13676,7 @@ function ensureTableNode(graph, id, name) {
13072
13676
  if (graph.hasNode(id)) return 0;
13073
13677
  const node = {
13074
13678
  id,
13075
- type: import_types39.NodeType.InfraNode,
13679
+ type: import_types42.NodeType.InfraNode,
13076
13680
  name,
13077
13681
  provider: "self",
13078
13682
  kind: "sql-table"
@@ -13086,16 +13690,16 @@ init_cjs_shims();
13086
13690
 
13087
13691
  // src/extract/infra/docker-compose.ts
13088
13692
  init_cjs_shims();
13089
- var import_node_path51 = __toESM(require("path"), 1);
13090
- var import_types41 = require("@neat.is/types");
13693
+ var import_node_path54 = __toESM(require("path"), 1);
13694
+ var import_types44 = require("@neat.is/types");
13091
13695
 
13092
13696
  // src/extract/infra/shared.ts
13093
13697
  init_cjs_shims();
13094
- var import_types40 = require("@neat.is/types");
13698
+ var import_types43 = require("@neat.is/types");
13095
13699
  function makeInfraNode(kind, name, provider = "self", extras) {
13096
13700
  return {
13097
- id: (0, import_types40.infraId)(kind, name),
13098
- type: import_types40.NodeType.InfraNode,
13701
+ id: (0, import_types43.infraId)(kind, name),
13702
+ type: import_types43.NodeType.InfraNode,
13099
13703
  name,
13100
13704
  provider,
13101
13705
  kind,
@@ -13139,8 +13743,8 @@ function emitPlatformResourceEdge(graph, anchorId, edgeType, kind, name, provide
13139
13743
  source: anchorId,
13140
13744
  target: node.id,
13141
13745
  type: edgeType,
13142
- provenance: import_types40.Provenance.EXTRACTED,
13143
- confidence: (0, import_types40.confidenceForExtracted)("structural"),
13746
+ provenance: import_types43.Provenance.EXTRACTED,
13747
+ confidence: (0, import_types43.confidenceForExtracted)("structural"),
13144
13748
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13145
13749
  };
13146
13750
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13157,7 +13761,7 @@ function dependsOnList(value) {
13157
13761
  }
13158
13762
  function serviceNameToServiceNode(name, services) {
13159
13763
  for (const s of services) {
13160
- if (s.node.name === name || import_node_path51.default.basename(s.dir) === name) return s.node.id;
13764
+ if (s.node.name === name || import_node_path54.default.basename(s.dir) === name) return s.node.id;
13161
13765
  }
13162
13766
  return null;
13163
13767
  }
@@ -13166,7 +13770,7 @@ async function addComposeInfra(graph, scanPath, services) {
13166
13770
  let edgesAdded = 0;
13167
13771
  let composePath = null;
13168
13772
  for (const name of ["docker-compose.yml", "docker-compose.yaml"]) {
13169
- const abs = import_node_path51.default.join(scanPath, name);
13773
+ const abs = import_node_path54.default.join(scanPath, name);
13170
13774
  if (await exists2(abs)) {
13171
13775
  composePath = abs;
13172
13776
  break;
@@ -13179,13 +13783,13 @@ async function addComposeInfra(graph, scanPath, services) {
13179
13783
  } catch (err) {
13180
13784
  recordExtractionError(
13181
13785
  "infra docker-compose",
13182
- import_node_path51.default.relative(scanPath, composePath),
13786
+ import_node_path54.default.relative(scanPath, composePath),
13183
13787
  err
13184
13788
  );
13185
13789
  return { nodesAdded, edgesAdded };
13186
13790
  }
13187
13791
  if (!compose?.services) return { nodesAdded, edgesAdded };
13188
- const evidenceFile = import_node_path51.default.relative(scanPath, composePath).split(import_node_path51.default.sep).join("/");
13792
+ const evidenceFile = import_node_path54.default.relative(scanPath, composePath).split(import_node_path54.default.sep).join("/");
13189
13793
  const composeNameToNodeId = /* @__PURE__ */ new Map();
13190
13794
  for (const [composeName, svc] of Object.entries(compose.services)) {
13191
13795
  const matchedServiceId = serviceNameToServiceNode(composeName, services);
@@ -13207,15 +13811,15 @@ async function addComposeInfra(graph, scanPath, services) {
13207
13811
  for (const dep of dependsOnList(svc.depends_on)) {
13208
13812
  const targetId = composeNameToNodeId.get(dep);
13209
13813
  if (!targetId) continue;
13210
- const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types41.EdgeType.DEPENDS_ON);
13814
+ const edgeId = (0, import_types3.extractedEdgeId)(sourceId, targetId, import_types44.EdgeType.DEPENDS_ON);
13211
13815
  if (graph.hasEdge(edgeId)) continue;
13212
13816
  const edge = {
13213
13817
  id: edgeId,
13214
13818
  source: sourceId,
13215
13819
  target: targetId,
13216
- type: import_types41.EdgeType.DEPENDS_ON,
13217
- provenance: import_types41.Provenance.EXTRACTED,
13218
- confidence: (0, import_types41.confidenceForExtracted)("structural"),
13820
+ type: import_types44.EdgeType.DEPENDS_ON,
13821
+ provenance: import_types44.Provenance.EXTRACTED,
13822
+ confidence: (0, import_types44.confidenceForExtracted)("structural"),
13219
13823
  evidence: { file: evidenceFile }
13220
13824
  };
13221
13825
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13227,9 +13831,9 @@ async function addComposeInfra(graph, scanPath, services) {
13227
13831
 
13228
13832
  // src/extract/infra/dockerfile.ts
13229
13833
  init_cjs_shims();
13230
- var import_node_path52 = __toESM(require("path"), 1);
13231
- var import_node_fs20 = require("fs");
13232
- var import_types42 = require("@neat.is/types");
13834
+ var import_node_path55 = __toESM(require("path"), 1);
13835
+ var import_node_fs23 = require("fs");
13836
+ var import_types45 = require("@neat.is/types");
13233
13837
  function readDockerfile(content) {
13234
13838
  let image = null;
13235
13839
  const ports = [];
@@ -13258,15 +13862,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13258
13862
  let nodesAdded = 0;
13259
13863
  let edgesAdded = 0;
13260
13864
  for (const service of services) {
13261
- const dockerfilePath = import_node_path52.default.join(service.dir, "Dockerfile");
13865
+ const dockerfilePath = import_node_path55.default.join(service.dir, "Dockerfile");
13262
13866
  if (!await exists2(dockerfilePath)) continue;
13263
13867
  let content;
13264
13868
  try {
13265
- content = await import_node_fs20.promises.readFile(dockerfilePath, "utf8");
13869
+ content = await import_node_fs23.promises.readFile(dockerfilePath, "utf8");
13266
13870
  } catch (err) {
13267
13871
  recordExtractionError(
13268
13872
  "infra dockerfile",
13269
- import_node_path52.default.relative(scanPath, dockerfilePath),
13873
+ import_node_path55.default.relative(scanPath, dockerfilePath),
13270
13874
  err
13271
13875
  );
13272
13876
  continue;
@@ -13278,8 +13882,8 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13278
13882
  graph.addNode(node.id, node);
13279
13883
  nodesAdded++;
13280
13884
  }
13281
- const relDockerfile = toPosix(import_node_path52.default.relative(service.dir, dockerfilePath));
13282
- const evidenceFile = toPosix(import_node_path52.default.relative(scanPath, dockerfilePath));
13885
+ const relDockerfile = toPosix(import_node_path55.default.relative(service.dir, dockerfilePath));
13886
+ const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, dockerfilePath));
13283
13887
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13284
13888
  graph,
13285
13889
  service.pkg.name,
@@ -13288,15 +13892,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13288
13892
  );
13289
13893
  nodesAdded += fn;
13290
13894
  edgesAdded += fe;
13291
- const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types42.EdgeType.RUNS_ON);
13895
+ const edgeId = (0, import_types3.extractedEdgeId)(fileNodeId, node.id, import_types45.EdgeType.RUNS_ON);
13292
13896
  if (!graph.hasEdge(edgeId)) {
13293
13897
  const edge = {
13294
13898
  id: edgeId,
13295
13899
  source: fileNodeId,
13296
13900
  target: node.id,
13297
- type: import_types42.EdgeType.RUNS_ON,
13298
- provenance: import_types42.Provenance.EXTRACTED,
13299
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
13901
+ type: import_types45.EdgeType.RUNS_ON,
13902
+ provenance: import_types45.Provenance.EXTRACTED,
13903
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13300
13904
  evidence: {
13301
13905
  file: evidenceFile,
13302
13906
  ...facts.entrypoint ? { snippet: facts.entrypoint.slice(0, 120) } : {}
@@ -13311,15 +13915,15 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13311
13915
  graph.addNode(portNode.id, portNode);
13312
13916
  nodesAdded++;
13313
13917
  }
13314
- const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types42.EdgeType.CONNECTS_TO);
13918
+ const portEdgeId = (0, import_types3.extractedEdgeId)(fileNodeId, portNode.id, import_types45.EdgeType.CONNECTS_TO);
13315
13919
  if (graph.hasEdge(portEdgeId)) continue;
13316
13920
  const portEdge = {
13317
13921
  id: portEdgeId,
13318
13922
  source: fileNodeId,
13319
13923
  target: portNode.id,
13320
- type: import_types42.EdgeType.CONNECTS_TO,
13321
- provenance: import_types42.Provenance.EXTRACTED,
13322
- confidence: (0, import_types42.confidenceForExtracted)("structural"),
13924
+ type: import_types45.EdgeType.CONNECTS_TO,
13925
+ provenance: import_types45.Provenance.EXTRACTED,
13926
+ confidence: (0, import_types45.confidenceForExtracted)("structural"),
13323
13927
  evidence: { file: evidenceFile, snippet: `EXPOSE ${port}` }
13324
13928
  };
13325
13929
  graph.addEdgeWithKey(portEdgeId, portEdge.source, portEdge.target, portEdge);
@@ -13331,23 +13935,23 @@ async function addDockerfileRuntimes(graph, services, scanPath) {
13331
13935
 
13332
13936
  // src/extract/infra/terraform.ts
13333
13937
  init_cjs_shims();
13334
- var import_node_fs21 = require("fs");
13335
- var import_node_path53 = __toESM(require("path"), 1);
13336
- var import_types43 = require("@neat.is/types");
13938
+ var import_node_fs24 = require("fs");
13939
+ var import_node_path56 = __toESM(require("path"), 1);
13940
+ var import_types46 = require("@neat.is/types");
13337
13941
  var RESOURCE_RE = /resource\s+"(aws_[A-Za-z0-9_]+)"\s+"([A-Za-z0-9_-]+)"/g;
13338
13942
  var REFERENCE_RE = /(?<![\w.])(aws_[A-Za-z0-9_]+)\.([A-Za-z0-9_-]+)/g;
13339
13943
  async function walkTfFiles(start, depth = 0, max = 5) {
13340
13944
  if (depth > max) return [];
13341
13945
  const out = [];
13342
- const entries = await import_node_fs21.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13946
+ const entries = await import_node_fs24.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13343
13947
  for (const entry of entries) {
13344
13948
  if (entry.isDirectory()) {
13345
13949
  if (IGNORED_DIRS.has(entry.name) || entry.name === ".terraform") continue;
13346
- const child = import_node_path53.default.join(start, entry.name);
13950
+ const child = import_node_path56.default.join(start, entry.name);
13347
13951
  if (await isPythonVenvDir(child)) continue;
13348
13952
  out.push(...await walkTfFiles(child, depth + 1, max));
13349
13953
  } else if (entry.isFile() && entry.name.endsWith(".tf")) {
13350
- out.push(import_node_path53.default.join(start, entry.name));
13954
+ out.push(import_node_path56.default.join(start, entry.name));
13351
13955
  }
13352
13956
  }
13353
13957
  return out;
@@ -13378,8 +13982,8 @@ async function addTerraformResources(graph, scanPath) {
13378
13982
  let edgesAdded = 0;
13379
13983
  const files = await walkTfFiles(scanPath);
13380
13984
  for (const file of files) {
13381
- const content = await import_node_fs21.promises.readFile(file, "utf8");
13382
- const evidenceFile = toPosix(import_node_path53.default.relative(scanPath, file));
13985
+ const content = await import_node_fs24.promises.readFile(file, "utf8");
13986
+ const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, file));
13383
13987
  const resources = [];
13384
13988
  const byKey = /* @__PURE__ */ new Map();
13385
13989
  RESOURCE_RE.lastIndex = 0;
@@ -13414,16 +14018,16 @@ async function addTerraformResources(graph, scanPath) {
13414
14018
  if (!target) continue;
13415
14019
  if (seen.has(target.nodeId)) continue;
13416
14020
  seen.add(target.nodeId);
13417
- const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types43.EdgeType.DEPENDS_ON);
14021
+ const edgeId = (0, import_types3.extractedEdgeId)(resource.nodeId, target.nodeId, import_types46.EdgeType.DEPENDS_ON);
13418
14022
  if (graph.hasEdge(edgeId)) continue;
13419
14023
  const line = lineAt2(content, resource.bodyOffset + ref.index);
13420
14024
  const edge = {
13421
14025
  id: edgeId,
13422
14026
  source: resource.nodeId,
13423
14027
  target: target.nodeId,
13424
- type: import_types43.EdgeType.DEPENDS_ON,
13425
- provenance: import_types43.Provenance.EXTRACTED,
13426
- confidence: (0, import_types43.confidenceForExtracted)("structural"),
14028
+ type: import_types46.EdgeType.DEPENDS_ON,
14029
+ provenance: import_types46.Provenance.EXTRACTED,
14030
+ confidence: (0, import_types46.confidenceForExtracted)("structural"),
13427
14031
  evidence: { file: evidenceFile, line, snippet: key }
13428
14032
  };
13429
14033
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13436,8 +14040,8 @@ async function addTerraformResources(graph, scanPath) {
13436
14040
 
13437
14041
  // src/extract/infra/k8s.ts
13438
14042
  init_cjs_shims();
13439
- var import_node_fs22 = require("fs");
13440
- var import_node_path54 = __toESM(require("path"), 1);
14043
+ var import_node_fs25 = require("fs");
14044
+ var import_node_path57 = __toESM(require("path"), 1);
13441
14045
  var import_yaml3 = require("yaml");
13442
14046
  var K8S_KIND_TO_INFRA_KIND = {
13443
14047
  Service: "k8s-service",
@@ -13451,15 +14055,15 @@ var K8S_KIND_TO_INFRA_KIND = {
13451
14055
  async function walkYamlFiles2(start, depth = 0, max = 5) {
13452
14056
  if (depth > max) return [];
13453
14057
  const out = [];
13454
- const entries = await import_node_fs22.promises.readdir(start, { withFileTypes: true }).catch(() => []);
14058
+ const entries = await import_node_fs25.promises.readdir(start, { withFileTypes: true }).catch(() => []);
13455
14059
  for (const entry of entries) {
13456
14060
  if (entry.isDirectory()) {
13457
14061
  if (IGNORED_DIRS.has(entry.name)) continue;
13458
- const child = import_node_path54.default.join(start, entry.name);
14062
+ const child = import_node_path57.default.join(start, entry.name);
13459
14063
  if (await isPythonVenvDir(child)) continue;
13460
14064
  out.push(...await walkYamlFiles2(child, depth + 1, max));
13461
- } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path54.default.extname(entry.name))) {
13462
- out.push(import_node_path54.default.join(start, entry.name));
14065
+ } else if (entry.isFile() && CONFIG_FILE_EXTENSIONS.has(import_node_path57.default.extname(entry.name))) {
14066
+ out.push(import_node_path57.default.join(start, entry.name));
13463
14067
  }
13464
14068
  }
13465
14069
  return out;
@@ -13468,7 +14072,7 @@ async function addK8sResources(graph, scanPath) {
13468
14072
  let nodesAdded = 0;
13469
14073
  const files = await walkYamlFiles2(scanPath);
13470
14074
  for (const file of files) {
13471
- const content = await import_node_fs22.promises.readFile(file, "utf8");
14075
+ const content = await import_node_fs25.promises.readFile(file, "utf8");
13472
14076
  let docs;
13473
14077
  try {
13474
14078
  docs = (0, import_yaml3.parseAllDocuments)(content).map((d) => d.toJSON());
@@ -13492,16 +14096,16 @@ async function addK8sResources(graph, scanPath) {
13492
14096
 
13493
14097
  // src/extract/infra/cloudflare.ts
13494
14098
  init_cjs_shims();
13495
- var import_node_fs23 = require("fs");
13496
- var import_node_path55 = __toESM(require("path"), 1);
14099
+ var import_node_fs26 = require("fs");
14100
+ var import_node_path58 = __toESM(require("path"), 1);
13497
14101
  var import_smol_toml2 = require("smol-toml");
13498
- var import_types44 = require("@neat.is/types");
14102
+ var import_types47 = require("@neat.is/types");
13499
14103
  var WRANGLER_FILENAMES = ["wrangler.toml", "wrangler.jsonc", "wrangler.json"];
13500
14104
  async function readWranglerConfig(dir) {
13501
14105
  for (const filename of WRANGLER_FILENAMES) {
13502
- const abs = import_node_path55.default.join(dir, filename);
14106
+ const abs = import_node_path58.default.join(dir, filename);
13503
14107
  if (!await exists2(abs)) continue;
13504
- const raw = await import_node_fs23.promises.readFile(abs, "utf8");
14108
+ const raw = await import_node_fs26.promises.readFile(abs, "utf8");
13505
14109
  const config = filename === "wrangler.toml" ? (0, import_smol_toml2.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
13506
14110
  return { config, relFile: filename, raw };
13507
14111
  }
@@ -13543,8 +14147,8 @@ function addResourceEdge(graph, anchorId, edgeType, kind, name, evidenceFile, li
13543
14147
  source: anchorId,
13544
14148
  target: node.id,
13545
14149
  type: edgeType,
13546
- provenance: import_types44.Provenance.EXTRACTED,
13547
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14150
+ provenance: import_types47.Provenance.EXTRACTED,
14151
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13548
14152
  evidence: { file: evidenceFile, ...line !== void 0 ? { line } : {} }
13549
14153
  };
13550
14154
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13562,11 +14166,11 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13562
14166
  try {
13563
14167
  read = await readWranglerConfig(service.dir);
13564
14168
  } catch (err) {
13565
- recordExtractionError("infra cloudflare", import_node_path55.default.relative(scanPath, service.dir), err);
14169
+ recordExtractionError("infra cloudflare", import_node_path58.default.relative(scanPath, service.dir), err);
13566
14170
  continue;
13567
14171
  }
13568
14172
  if (!read || !read.config.name) continue;
13569
- const evidenceFile = toPosix(import_node_path55.default.relative(scanPath, import_node_path55.default.join(service.dir, read.relFile)));
14173
+ const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, import_node_path58.default.join(service.dir, read.relFile)));
13570
14174
  discovered.push({ service, config: read.config, relFile: read.relFile, raw: read.raw, evidenceFile });
13571
14175
  }
13572
14176
  for (const worker of discovered) {
@@ -13578,7 +14182,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13578
14182
  }
13579
14183
  let anchorId = service.node.id;
13580
14184
  if (config.main) {
13581
- const entryRelPath = toPosix(import_node_path55.default.normalize(config.main));
14185
+ const entryRelPath = toPosix(import_node_path58.default.normalize(config.main));
13582
14186
  const { fileNodeId, nodesAdded: fn, edgesAdded: fe } = ensureFileNode(
13583
14187
  graph,
13584
14188
  service.pkg.name,
@@ -13605,15 +14209,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13605
14209
  nodesAdded++;
13606
14210
  }
13607
14211
  if (runtimeNode.id !== anchorId) {
13608
- const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types44.EdgeType.RUNS_ON);
14212
+ const runsOnId = (0, import_types3.extractedEdgeId)(anchorId, runtimeNode.id, import_types47.EdgeType.RUNS_ON);
13609
14213
  if (!graph.hasEdge(runsOnId)) {
13610
14214
  const edge = {
13611
14215
  id: runsOnId,
13612
14216
  source: anchorId,
13613
14217
  target: runtimeNode.id,
13614
- type: import_types44.EdgeType.RUNS_ON,
13615
- provenance: import_types44.Provenance.EXTRACTED,
13616
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14218
+ type: import_types47.EdgeType.RUNS_ON,
14219
+ provenance: import_types47.Provenance.EXTRACTED,
14220
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13617
14221
  evidence: {
13618
14222
  file: evidenceFile,
13619
14223
  ...config.compatibility_date ? { snippet: `compatibility_date = ${config.compatibility_date}`.slice(0, 120) } : {}
@@ -13627,7 +14231,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13627
14231
  const result = addResourceEdge(
13628
14232
  graph,
13629
14233
  anchorId,
13630
- import_types44.EdgeType.CONNECTS_TO,
14234
+ import_types47.EdgeType.CONNECTS_TO,
13631
14235
  "cloudflare-route",
13632
14236
  route,
13633
14237
  evidenceFile,
@@ -13651,7 +14255,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13651
14255
  const result = addResourceEdge(
13652
14256
  graph,
13653
14257
  anchorId,
13654
- import_types44.EdgeType.DEPENDS_ON,
14258
+ import_types47.EdgeType.DEPENDS_ON,
13655
14259
  group.kind,
13656
14260
  name,
13657
14261
  evidenceFile,
@@ -13665,7 +14269,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13665
14269
  const result = addResourceEdge(
13666
14270
  graph,
13667
14271
  anchorId,
13668
- import_types44.EdgeType.DEPENDS_ON,
14272
+ import_types47.EdgeType.DEPENDS_ON,
13669
14273
  "cloudflare-cron",
13670
14274
  cron,
13671
14275
  evidenceFile,
@@ -13678,7 +14282,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13678
14282
  const result = addResourceEdge(
13679
14283
  graph,
13680
14284
  anchorId,
13681
- import_types44.EdgeType.DEPENDS_ON,
14285
+ import_types47.EdgeType.DEPENDS_ON,
13682
14286
  "cloudflare-env-var",
13683
14287
  varName,
13684
14288
  evidenceFile,
@@ -13691,15 +14295,15 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13691
14295
  if (!svc.service) continue;
13692
14296
  const target = workerIndex.get(svc.service);
13693
14297
  if (target && target.anchorId !== anchorId) {
13694
- const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types44.EdgeType.CALLS);
14298
+ const edgeId = (0, import_types3.extractedEdgeId)(anchorId, target.anchorId, import_types47.EdgeType.CALLS);
13695
14299
  if (!graph.hasEdge(edgeId)) {
13696
14300
  const edge = {
13697
14301
  id: edgeId,
13698
14302
  source: anchorId,
13699
14303
  target: target.anchorId,
13700
- type: import_types44.EdgeType.CALLS,
13701
- provenance: import_types44.Provenance.EXTRACTED,
13702
- confidence: (0, import_types44.confidenceForExtracted)("structural"),
14304
+ type: import_types47.EdgeType.CALLS,
14305
+ provenance: import_types47.Provenance.EXTRACTED,
14306
+ confidence: (0, import_types47.confidenceForExtracted)("structural"),
13703
14307
  evidence: { file: evidenceFile, line: lineContaining2(raw, svc.service) }
13704
14308
  };
13705
14309
  graph.addEdgeWithKey(edgeId, edge.source, edge.target, edge);
@@ -13710,7 +14314,7 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13710
14314
  const result = addResourceEdge(
13711
14315
  graph,
13712
14316
  anchorId,
13713
- import_types44.EdgeType.DEPENDS_ON,
14317
+ import_types47.EdgeType.DEPENDS_ON,
13714
14318
  "cloudflare-service-binding",
13715
14319
  svc.service,
13716
14320
  evidenceFile,
@@ -13725,24 +14329,24 @@ async function addCloudflareWorkers(graph, services, scanPath) {
13725
14329
 
13726
14330
  // src/extract/infra/vercel.ts
13727
14331
  init_cjs_shims();
13728
- var import_node_fs24 = require("fs");
13729
- var import_node_path56 = __toESM(require("path"), 1);
13730
- var import_types45 = require("@neat.is/types");
14332
+ var import_node_fs27 = require("fs");
14333
+ var import_node_path59 = __toESM(require("path"), 1);
14334
+ var import_types48 = require("@neat.is/types");
13731
14335
  var VERCEL_CONFIG_FILENAMES = ["vercel.json", "vercel.jsonc"];
13732
14336
  async function readVercelConfig(dir) {
13733
14337
  for (const filename of VERCEL_CONFIG_FILENAMES) {
13734
- const abs = import_node_path56.default.join(dir, filename);
14338
+ const abs = import_node_path59.default.join(dir, filename);
13735
14339
  if (!await exists2(abs)) continue;
13736
- const raw = await import_node_fs24.promises.readFile(abs, "utf8");
14340
+ const raw = await import_node_fs27.promises.readFile(abs, "utf8");
13737
14341
  const config = JSON.parse(maskCommentsInSource(raw));
13738
14342
  return { config, relFile: filename, raw };
13739
14343
  }
13740
14344
  return null;
13741
14345
  }
13742
14346
  async function readLinkedProjectName(dir) {
13743
- const abs = import_node_path56.default.join(dir, ".vercel", "project.json");
14347
+ const abs = import_node_path59.default.join(dir, ".vercel", "project.json");
13744
14348
  if (!await exists2(abs)) return void 0;
13745
- const parsed = JSON.parse(await import_node_fs24.promises.readFile(abs, "utf8"));
14349
+ const parsed = JSON.parse(await import_node_fs27.promises.readFile(abs, "utf8"));
13746
14350
  return typeof parsed.projectName === "string" ? parsed.projectName : void 0;
13747
14351
  }
13748
14352
  function routeSource(route) {
@@ -13758,7 +14362,7 @@ async function addVercelServices(graph, services, scanPath) {
13758
14362
  read = await readVercelConfig(service.dir);
13759
14363
  projectName = await readLinkedProjectName(service.dir);
13760
14364
  } catch (err) {
13761
- recordExtractionError("infra vercel", import_node_path56.default.relative(scanPath, service.dir), err);
14365
+ recordExtractionError("infra vercel", import_node_path59.default.relative(scanPath, service.dir), err);
13762
14366
  continue;
13763
14367
  }
13764
14368
  if (!read && !projectName) continue;
@@ -13774,7 +14378,7 @@ async function addVercelServices(graph, services, scanPath) {
13774
14378
  const anchorId = service.node.id;
13775
14379
  if (!read) continue;
13776
14380
  const { config, relFile, raw } = read;
13777
- const evidenceFile = toPosix(import_node_path56.default.relative(scanPath, import_node_path56.default.join(service.dir, relFile)));
14381
+ const evidenceFile = toPosix(import_node_path59.default.relative(scanPath, import_node_path59.default.join(service.dir, relFile)));
13778
14382
  const add = (edgeType, kind, name) => {
13779
14383
  if (!name) return;
13780
14384
  const result = emitPlatformResourceEdge(
@@ -13790,12 +14394,12 @@ async function addVercelServices(graph, services, scanPath) {
13790
14394
  nodesAdded += result.nodesAdded;
13791
14395
  edgesAdded += result.edgesAdded;
13792
14396
  };
13793
- add(import_types45.EdgeType.RUNS_ON, "vercel", "vercel");
13794
- for (const cron of config.crons ?? []) add(import_types45.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
13795
- for (const varName of Object.keys(config.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
13796
- for (const varName of Object.keys(config.build?.env ?? {})) add(import_types45.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14397
+ add(import_types48.EdgeType.RUNS_ON, "vercel", "vercel");
14398
+ for (const cron of config.crons ?? []) add(import_types48.EdgeType.DEPENDS_ON, "vercel-cron", cron.path ?? cron.schedule);
14399
+ for (const varName of Object.keys(config.env ?? {})) add(import_types48.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
14400
+ for (const varName of Object.keys(config.build?.env ?? {})) add(import_types48.EdgeType.DEPENDS_ON, "vercel-env-var", varName);
13797
14401
  for (const route of [...config.rewrites ?? [], ...config.redirects ?? [], ...config.routes ?? []]) {
13798
- add(import_types45.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
14402
+ add(import_types48.EdgeType.CONNECTS_TO, "vercel-route", routeSource(route));
13799
14403
  }
13800
14404
  }
13801
14405
  return { nodesAdded, edgesAdded };
@@ -13803,16 +14407,16 @@ async function addVercelServices(graph, services, scanPath) {
13803
14407
 
13804
14408
  // src/extract/infra/railway.ts
13805
14409
  init_cjs_shims();
13806
- var import_node_fs25 = require("fs");
13807
- var import_node_path57 = __toESM(require("path"), 1);
14410
+ var import_node_fs28 = require("fs");
14411
+ var import_node_path60 = __toESM(require("path"), 1);
13808
14412
  var import_smol_toml3 = require("smol-toml");
13809
- var import_types46 = require("@neat.is/types");
14413
+ var import_types49 = require("@neat.is/types");
13810
14414
  var RAILWAY_FILENAMES = ["railway.toml", "railway.json", "railway.jsonc"];
13811
14415
  async function readRailwayConfig(dir) {
13812
14416
  for (const filename of RAILWAY_FILENAMES) {
13813
- const abs = import_node_path57.default.join(dir, filename);
14417
+ const abs = import_node_path60.default.join(dir, filename);
13814
14418
  if (!await exists2(abs)) continue;
13815
- const raw = await import_node_fs25.promises.readFile(abs, "utf8");
14419
+ const raw = await import_node_fs28.promises.readFile(abs, "utf8");
13816
14420
  const config = filename === "railway.toml" ? (0, import_smol_toml3.parse)(raw) : JSON.parse(maskCommentsInSource(raw));
13817
14421
  return { config, relFile: filename, raw };
13818
14422
  }
@@ -13826,7 +14430,7 @@ async function addRailwayServices(graph, services, scanPath) {
13826
14430
  try {
13827
14431
  read = await readRailwayConfig(service.dir);
13828
14432
  } catch (err) {
13829
- recordExtractionError("infra railway", import_node_path57.default.relative(scanPath, service.dir), err);
14433
+ recordExtractionError("infra railway", import_node_path60.default.relative(scanPath, service.dir), err);
13830
14434
  continue;
13831
14435
  }
13832
14436
  if (!read) continue;
@@ -13836,7 +14440,7 @@ async function addRailwayServices(graph, services, scanPath) {
13836
14440
  }
13837
14441
  const anchorId = service.node.id;
13838
14442
  const { config, relFile, raw } = read;
13839
- const evidenceFile = toPosix(import_node_path57.default.relative(scanPath, import_node_path57.default.join(service.dir, relFile)));
14443
+ const evidenceFile = toPosix(import_node_path60.default.relative(scanPath, import_node_path60.default.join(service.dir, relFile)));
13840
14444
  const add = (edgeType, kind, name) => {
13841
14445
  if (!name) return;
13842
14446
  const result = emitPlatformResourceEdge(
@@ -13852,24 +14456,24 @@ async function addRailwayServices(graph, services, scanPath) {
13852
14456
  nodesAdded += result.nodesAdded;
13853
14457
  edgesAdded += result.edgesAdded;
13854
14458
  };
13855
- add(import_types46.EdgeType.RUNS_ON, "railway", "railway");
13856
- add(import_types46.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
13857
- add(import_types46.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
14459
+ add(import_types49.EdgeType.RUNS_ON, "railway", "railway");
14460
+ add(import_types49.EdgeType.CONNECTS_TO, "railway-route", config.deploy?.healthcheckPath);
14461
+ add(import_types49.EdgeType.DEPENDS_ON, "railway-cron", config.deploy?.cronSchedule);
13858
14462
  }
13859
14463
  return { nodesAdded, edgesAdded };
13860
14464
  }
13861
14465
 
13862
14466
  // src/extract/infra/supabase.ts
13863
14467
  init_cjs_shims();
13864
- var import_node_fs26 = require("fs");
13865
- var import_node_path58 = __toESM(require("path"), 1);
14468
+ var import_node_fs29 = require("fs");
14469
+ var import_node_path61 = __toESM(require("path"), 1);
13866
14470
  var import_smol_toml4 = require("smol-toml");
13867
- var import_types47 = require("@neat.is/types");
14471
+ var import_types50 = require("@neat.is/types");
13868
14472
  async function readSupabaseConfig(dir) {
13869
- const relFile = import_node_path58.default.join("supabase", "config.toml");
13870
- const abs = import_node_path58.default.join(dir, relFile);
14473
+ const relFile = import_node_path61.default.join("supabase", "config.toml");
14474
+ const abs = import_node_path61.default.join(dir, relFile);
13871
14475
  if (!await exists2(abs)) return null;
13872
- const raw = await import_node_fs26.promises.readFile(abs, "utf8");
14476
+ const raw = await import_node_fs29.promises.readFile(abs, "utf8");
13873
14477
  const config = (0, import_smol_toml4.parse)(raw);
13874
14478
  return { config, relFile, raw };
13875
14479
  }
@@ -13881,7 +14485,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
13881
14485
  try {
13882
14486
  read = await readSupabaseConfig(service.dir);
13883
14487
  } catch (err) {
13884
- recordExtractionError("infra supabase", import_node_path58.default.relative(scanPath, service.dir), err);
14488
+ recordExtractionError("infra supabase", import_node_path61.default.relative(scanPath, service.dir), err);
13885
14489
  continue;
13886
14490
  }
13887
14491
  if (!read) continue;
@@ -13896,7 +14500,7 @@ async function addSupabaseProjects(graph, services, scanPath) {
13896
14500
  });
13897
14501
  }
13898
14502
  const anchorId = service.node.id;
13899
- const evidenceFile = toPosix(import_node_path58.default.relative(scanPath, import_node_path58.default.join(service.dir, relFile)));
14503
+ const evidenceFile = toPosix(import_node_path61.default.relative(scanPath, import_node_path61.default.join(service.dir, relFile)));
13900
14504
  const add = (edgeType, kind, name) => {
13901
14505
  if (!name) return;
13902
14506
  const result = emitPlatformResourceEdge(
@@ -13912,10 +14516,10 @@ async function addSupabaseProjects(graph, services, scanPath) {
13912
14516
  nodesAdded += result.nodesAdded;
13913
14517
  edgesAdded += result.edgesAdded;
13914
14518
  };
13915
- add(import_types47.EdgeType.RUNS_ON, "supabase", "supabase");
13916
- for (const fn of Object.keys(config.functions ?? {})) add(import_types47.EdgeType.DEPENDS_ON, "supabase-function", fn);
13917
- if (config.storage) add(import_types47.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
13918
- if (config.auth) add(import_types47.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
14519
+ add(import_types50.EdgeType.RUNS_ON, "supabase", "supabase");
14520
+ for (const fn of Object.keys(config.functions ?? {})) add(import_types50.EdgeType.DEPENDS_ON, "supabase-function", fn);
14521
+ if (config.storage) add(import_types50.EdgeType.DEPENDS_ON, "supabase-storage", "storage");
14522
+ if (config.auth) add(import_types50.EdgeType.DEPENDS_ON, "supabase-auth", "auth");
13919
14523
  }
13920
14524
  return { nodesAdded, edgesAdded };
13921
14525
  }
@@ -13938,10 +14542,10 @@ async function addInfra(graph, scanPath, services) {
13938
14542
 
13939
14543
  // src/extract/zod-shapes.ts
13940
14544
  init_cjs_shims();
13941
- var import_node_path59 = __toESM(require("path"), 1);
14545
+ var import_node_path62 = __toESM(require("path"), 1);
13942
14546
  var import_tree_sitter16 = __toESM(require("tree-sitter"), 1);
13943
14547
  var import_tree_sitter_javascript8 = __toESM(require("tree-sitter-javascript"), 1);
13944
- var import_types48 = require("@neat.is/types");
14548
+ var import_types51 = require("@neat.is/types");
13945
14549
  var ZOD_IMPORT_RE = /\bzod\b/;
13946
14550
  var ZOD_OBJECTS = /* @__PURE__ */ new Set(["z", "zod"]);
13947
14551
  function parserForExt4(ext) {
@@ -14033,7 +14637,7 @@ function topLevelSchemas(root) {
14033
14637
  }
14034
14638
  function zodShapesFromFile(file, serviceDir) {
14035
14639
  if (!ZOD_IMPORT_RE.test(file.content)) return [];
14036
- const tree = parseSource3(parserForExt4(import_node_path59.default.extname(file.path)), file.content);
14640
+ const tree = parseSource3(parserForExt4(import_node_path62.default.extname(file.path)), file.content);
14037
14641
  const out = [];
14038
14642
  const seen = /* @__PURE__ */ new Set();
14039
14643
  for (const { name, call } of topLevelSchemas(tree.rootNode)) {
@@ -14047,11 +14651,11 @@ function zodShapesFromFile(file, serviceDir) {
14047
14651
  seen.add(name);
14048
14652
  const line = call.startPosition.row + 1;
14049
14653
  out.push({
14050
- infraId: (0, import_types48.infraId)("zod-schema", name),
14654
+ infraId: (0, import_types51.infraId)("zod-schema", name),
14051
14655
  name,
14052
14656
  fields,
14053
14657
  evidence: {
14054
- file: import_node_path59.default.relative(serviceDir, file.path),
14658
+ file: import_node_path62.default.relative(serviceDir, file.path),
14055
14659
  line,
14056
14660
  snippet: snippet(file.content, line)
14057
14661
  }
@@ -14082,7 +14686,7 @@ async function addZodShapes(graph, services) {
14082
14686
  if (!graph.hasNode(shape.infraId)) {
14083
14687
  const node = {
14084
14688
  id: shape.infraId,
14085
- type: import_types48.NodeType.InfraNode,
14689
+ type: import_types51.NodeType.InfraNode,
14086
14690
  name: shape.name,
14087
14691
  provider: "self",
14088
14692
  kind: "zod-schema"
@@ -14092,14 +14696,14 @@ async function addZodShapes(graph, services) {
14092
14696
  }
14093
14697
  if (shape.fields.length > 0) {
14094
14698
  const node = graph.getNodeAttributes(shape.infraId);
14095
- if (node.type === import_types48.NodeType.InfraNode) {
14699
+ if (node.type === import_types51.NodeType.InfraNode) {
14096
14700
  graph.replaceNodeAttributes(shape.infraId, {
14097
14701
  ...node,
14098
14702
  columns: foldColumns(
14099
14703
  node.columns,
14100
14704
  shape.fields,
14101
- import_types48.Provenance.EXTRACTED,
14102
- (0, import_types48.confidenceForExtracted)("structural")
14705
+ import_types51.Provenance.EXTRACTED,
14706
+ (0, import_types51.confidenceForExtracted)("structural")
14103
14707
  )
14104
14708
  });
14105
14709
  }
@@ -14113,15 +14717,15 @@ async function addZodShapes(graph, services) {
14113
14717
  );
14114
14718
  nodesAdded += n;
14115
14719
  edgesAdded += e;
14116
- const edgeId = (0, import_types48.extractedEdgeId)(fileNodeId, shape.infraId, import_types48.EdgeType.CONTAINS);
14720
+ const edgeId = (0, import_types51.extractedEdgeId)(fileNodeId, shape.infraId, import_types51.EdgeType.CONTAINS);
14117
14721
  if (!graph.hasEdge(edgeId)) {
14118
14722
  const edge = {
14119
14723
  id: edgeId,
14120
14724
  source: fileNodeId,
14121
14725
  target: shape.infraId,
14122
- type: import_types48.EdgeType.CONTAINS,
14123
- provenance: import_types48.Provenance.EXTRACTED,
14124
- confidence: (0, import_types48.confidenceForExtracted)("structural"),
14726
+ type: import_types51.EdgeType.CONTAINS,
14727
+ provenance: import_types51.Provenance.EXTRACTED,
14728
+ confidence: (0, import_types51.confidenceForExtracted)("structural"),
14125
14729
  evidence: shape.evidence
14126
14730
  };
14127
14731
  graph.addEdgeWithKey(edgeId, fileNodeId, shape.infraId, edge);
@@ -14135,7 +14739,7 @@ async function addZodShapes(graph, services) {
14135
14739
 
14136
14740
  // src/extract/firestore-rules.ts
14137
14741
  init_cjs_shims();
14138
- var import_types49 = require("@neat.is/types");
14742
+ var import_types52 = require("@neat.is/types");
14139
14743
  var FIRESTORE_COLLECTION_KIND = "firestore-collection";
14140
14744
  var WRITE_METHODS = /* @__PURE__ */ new Set(["write", "create", "update"]);
14141
14745
  function stripComments(src) {
@@ -14275,7 +14879,7 @@ async function addFirestoreRules(graph, services) {
14275
14879
  if (guards.size === 0) return { nodesAdded: 0, edgesAdded: 0 };
14276
14880
  graph.forEachNode((id, attrs) => {
14277
14881
  const node = attrs;
14278
- if (node.type !== import_types49.NodeType.InfraNode) return;
14882
+ if (node.type !== import_types52.NodeType.InfraNode) return;
14279
14883
  if (node.kind !== FIRESTORE_COLLECTION_KIND) return;
14280
14884
  const fields = guards.get(collectionKeyFromName(node.name));
14281
14885
  if (!fields || fields.size === 0) return;
@@ -14288,17 +14892,17 @@ async function addFirestoreRules(graph, services) {
14288
14892
  }
14289
14893
 
14290
14894
  // src/extract/index.ts
14291
- var import_node_path61 = __toESM(require("path"), 1);
14895
+ var import_node_path64 = __toESM(require("path"), 1);
14292
14896
 
14293
14897
  // src/extract/retire.ts
14294
14898
  init_cjs_shims();
14295
- var import_node_fs27 = require("fs");
14296
- var import_node_path60 = __toESM(require("path"), 1);
14297
- var import_types50 = require("@neat.is/types");
14899
+ var import_node_fs30 = require("fs");
14900
+ var import_node_path63 = __toESM(require("path"), 1);
14901
+ var import_types53 = require("@neat.is/types");
14298
14902
  function dropOrphanedFileNodes(graph) {
14299
14903
  const orphans = [];
14300
14904
  graph.forEachNode((id, attrs) => {
14301
- if (attrs.type !== import_types50.NodeType.FileNode) return;
14905
+ if (attrs.type !== import_types53.NodeType.FileNode) return;
14302
14906
  if (graph.inboundEdges(id).length === 0 && graph.outboundEdges(id).length === 0) {
14303
14907
  orphans.push(id);
14304
14908
  }
@@ -14311,14 +14915,14 @@ function retireExtractedEdgesByMissingFile(graph, scanPath, serviceDirs = []) {
14311
14915
  const bases = [scanPath, ...serviceDirs];
14312
14916
  graph.forEachEdge((id, attrs) => {
14313
14917
  const edge = attrs;
14314
- if (edge.provenance !== import_types50.Provenance.EXTRACTED) return;
14918
+ if (edge.provenance !== import_types53.Provenance.EXTRACTED) return;
14315
14919
  const evidenceFile = edge.evidence?.file;
14316
14920
  if (!evidenceFile) return;
14317
- if (import_node_path60.default.isAbsolute(evidenceFile)) {
14318
- if (!(0, import_node_fs27.existsSync)(evidenceFile)) toDrop.push(id);
14921
+ if (import_node_path63.default.isAbsolute(evidenceFile)) {
14922
+ if (!(0, import_node_fs30.existsSync)(evidenceFile)) toDrop.push(id);
14319
14923
  return;
14320
14924
  }
14321
- const found = bases.some((base) => (0, import_node_fs27.existsSync)(import_node_path60.default.join(base, evidenceFile)));
14925
+ const found = bases.some((base) => (0, import_node_fs30.existsSync)(import_node_path63.default.join(base, evidenceFile)));
14322
14926
  if (!found) toDrop.push(id);
14323
14927
  });
14324
14928
  for (const id of toDrop) graph.dropEdge(id);
@@ -14375,7 +14979,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14375
14979
  }
14376
14980
  const droppedEntries = drainDroppedExtracted();
14377
14981
  if (isRejectedLogEnabled() && opts.errorsPath && droppedEntries.length > 0) {
14378
- const rejectedPath = import_node_path61.default.join(import_node_path61.default.dirname(opts.errorsPath), "rejected.ndjson");
14982
+ const rejectedPath = import_node_path64.default.join(import_node_path64.default.dirname(opts.errorsPath), "rejected.ndjson");
14379
14983
  try {
14380
14984
  await writeRejectedExtracted(droppedEntries, rejectedPath);
14381
14985
  } catch (err) {
@@ -14409,7 +15013,7 @@ async function extractFromDirectory(graph, scanPath, opts = {}) {
14409
15013
 
14410
15014
  // src/diff.ts
14411
15015
  init_cjs_shims();
14412
- var import_node_fs28 = require("fs");
15016
+ var import_node_fs31 = require("fs");
14413
15017
  async function loadSnapshotForDiff(target) {
14414
15018
  if (/^https?:\/\//i.test(target)) {
14415
15019
  const res = await fetch(target);
@@ -14418,7 +15022,7 @@ async function loadSnapshotForDiff(target) {
14418
15022
  }
14419
15023
  return await res.json();
14420
15024
  }
14421
- const raw = await import_node_fs28.promises.readFile(target, "utf8");
15025
+ const raw = await import_node_fs31.promises.readFile(target, "utf8");
14422
15026
  return JSON.parse(raw);
14423
15027
  }
14424
15028
  function indexEntries(entries) {
@@ -14486,9 +15090,9 @@ function canonicalJson(value) {
14486
15090
 
14487
15091
  // src/persist.ts
14488
15092
  init_cjs_shims();
14489
- var import_node_fs29 = require("fs");
14490
- var import_node_path62 = __toESM(require("path"), 1);
14491
- var import_types51 = require("@neat.is/types");
15093
+ var import_node_fs32 = require("fs");
15094
+ var import_node_path65 = __toESM(require("path"), 1);
15095
+ var import_types54 = require("@neat.is/types");
14492
15096
  var SCHEMA_VERSION = 7;
14493
15097
  function migrateV1ToV2(payload) {
14494
15098
  const nodes = payload.graph.nodes;
@@ -14512,7 +15116,7 @@ function migrateV5ToV6(payload) {
14512
15116
  if (Array.isArray(nodes)) {
14513
15117
  for (const node of nodes) {
14514
15118
  const attrs = node.attributes;
14515
- if (!attrs || attrs.type !== import_types51.NodeType.InfraNode) continue;
15119
+ if (!attrs || attrs.type !== import_types54.NodeType.InfraNode) continue;
14516
15120
  if (attrs.kind !== "sql-table" && attrs.kind !== "supabase-table") continue;
14517
15121
  if (!Array.isArray(attrs.columns)) attrs.columns = [];
14518
15122
  }
@@ -14528,12 +15132,12 @@ function migrateV2ToV3(payload) {
14528
15132
  for (const edge of edges) {
14529
15133
  const attrs = edge.attributes;
14530
15134
  if (!attrs || attrs.provenance !== "FRONTIER") continue;
14531
- attrs.provenance = import_types51.Provenance.OBSERVED;
15135
+ attrs.provenance = import_types54.Provenance.OBSERVED;
14532
15136
  const type = typeof attrs.type === "string" ? attrs.type : void 0;
14533
15137
  const source = typeof attrs.source === "string" ? attrs.source : void 0;
14534
15138
  const target = typeof attrs.target === "string" ? attrs.target : void 0;
14535
15139
  if (type && source && target) {
14536
- const newId = (0, import_types51.observedEdgeId)(source, target, type);
15140
+ const newId = (0, import_types54.observedEdgeId)(source, target, type);
14537
15141
  attrs.id = newId;
14538
15142
  if (edge.key) edge.key = newId;
14539
15143
  }
@@ -14542,7 +15146,7 @@ function migrateV2ToV3(payload) {
14542
15146
  return { ...payload, schemaVersion: 3 };
14543
15147
  }
14544
15148
  async function ensureDir(filePath) {
14545
- await import_node_fs29.promises.mkdir(import_node_path62.default.dirname(filePath), { recursive: true });
15149
+ await import_node_fs32.promises.mkdir(import_node_path65.default.dirname(filePath), { recursive: true });
14546
15150
  }
14547
15151
  async function saveGraphToDisk(graph, outPath) {
14548
15152
  await ensureDir(outPath);
@@ -14552,13 +15156,13 @@ async function saveGraphToDisk(graph, outPath) {
14552
15156
  graph: graph.export()
14553
15157
  };
14554
15158
  const tmp = `${outPath}.tmp`;
14555
- await import_node_fs29.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
14556
- await import_node_fs29.promises.rename(tmp, outPath);
15159
+ await import_node_fs32.promises.writeFile(tmp, JSON.stringify(payload), "utf8");
15160
+ await import_node_fs32.promises.rename(tmp, outPath);
14557
15161
  }
14558
15162
  async function loadGraphFromDisk(graph, outPath) {
14559
15163
  let raw;
14560
15164
  try {
14561
- raw = await import_node_fs29.promises.readFile(outPath, "utf8");
15165
+ raw = await import_node_fs32.promises.readFile(outPath, "utf8");
14562
15166
  } catch (err) {
14563
15167
  if (err.code === "ENOENT") return;
14564
15168
  throw err;
@@ -14632,23 +15236,23 @@ function startPersistLoop(graph, outPath, opts = {}) {
14632
15236
 
14633
15237
  // src/projects.ts
14634
15238
  init_cjs_shims();
14635
- var import_node_path63 = __toESM(require("path"), 1);
15239
+ var import_node_path66 = __toESM(require("path"), 1);
14636
15240
  function pathsForProject(project, baseDir) {
14637
15241
  if (project === DEFAULT_PROJECT) {
14638
15242
  return {
14639
- snapshotPath: import_node_path63.default.join(baseDir, "graph.json"),
14640
- errorsPath: import_node_path63.default.join(baseDir, "errors.ndjson"),
14641
- staleEventsPath: import_node_path63.default.join(baseDir, "stale-events.ndjson"),
14642
- embeddingsCachePath: import_node_path63.default.join(baseDir, "embeddings.json"),
14643
- policyViolationsPath: import_node_path63.default.join(baseDir, "policy-violations.ndjson")
15243
+ snapshotPath: import_node_path66.default.join(baseDir, "graph.json"),
15244
+ errorsPath: import_node_path66.default.join(baseDir, "errors.ndjson"),
15245
+ staleEventsPath: import_node_path66.default.join(baseDir, "stale-events.ndjson"),
15246
+ embeddingsCachePath: import_node_path66.default.join(baseDir, "embeddings.json"),
15247
+ policyViolationsPath: import_node_path66.default.join(baseDir, "policy-violations.ndjson")
14644
15248
  };
14645
15249
  }
14646
15250
  return {
14647
- snapshotPath: import_node_path63.default.join(baseDir, `${project}.json`),
14648
- errorsPath: import_node_path63.default.join(baseDir, `errors.${project}.ndjson`),
14649
- staleEventsPath: import_node_path63.default.join(baseDir, `stale-events.${project}.ndjson`),
14650
- embeddingsCachePath: import_node_path63.default.join(baseDir, `embeddings.${project}.json`),
14651
- policyViolationsPath: import_node_path63.default.join(baseDir, `policy-violations.${project}.ndjson`)
15251
+ snapshotPath: import_node_path66.default.join(baseDir, `${project}.json`),
15252
+ errorsPath: import_node_path66.default.join(baseDir, `errors.${project}.ndjson`),
15253
+ staleEventsPath: import_node_path66.default.join(baseDir, `stale-events.${project}.ndjson`),
15254
+ embeddingsCachePath: import_node_path66.default.join(baseDir, `embeddings.${project}.json`),
15255
+ policyViolationsPath: import_node_path66.default.join(baseDir, `policy-violations.${project}.ndjson`)
14652
15256
  };
14653
15257
  }
14654
15258
  var Projects = class {
@@ -14688,20 +15292,20 @@ function parseExtraProjects(raw) {
14688
15292
 
14689
15293
  // src/registry.ts
14690
15294
  init_cjs_shims();
14691
- var import_node_fs30 = require("fs");
15295
+ var import_node_fs33 = require("fs");
14692
15296
  var import_node_os3 = __toESM(require("os"), 1);
14693
- var import_node_path64 = __toESM(require("path"), 1);
14694
- var import_types52 = require("@neat.is/types");
15297
+ var import_node_path67 = __toESM(require("path"), 1);
15298
+ var import_types55 = require("@neat.is/types");
14695
15299
  function neatHome() {
14696
15300
  const override = process.env.NEAT_HOME;
14697
- if (override && override.length > 0) return import_node_path64.default.resolve(override);
14698
- return import_node_path64.default.join(import_node_os3.default.homedir(), ".neat");
15301
+ if (override && override.length > 0) return import_node_path67.default.resolve(override);
15302
+ return import_node_path67.default.join(import_node_os3.default.homedir(), ".neat");
14699
15303
  }
14700
15304
  function registryPath() {
14701
- return import_node_path64.default.join(neatHome(), "projects.json");
15305
+ return import_node_path67.default.join(neatHome(), "projects.json");
14702
15306
  }
14703
15307
  function daemonsDir() {
14704
- return import_node_path64.default.join(neatHome(), "daemons");
15308
+ return import_node_path67.default.join(neatHome(), "daemons");
14705
15309
  }
14706
15310
  function isFiniteInt(v) {
14707
15311
  return typeof v === "number" && Number.isFinite(v);
@@ -14734,7 +15338,7 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
14734
15338
  const dir = daemonsDir();
14735
15339
  let names;
14736
15340
  try {
14737
- names = await import_node_fs30.promises.readdir(dir);
15341
+ names = await import_node_fs33.promises.readdir(dir);
14738
15342
  } catch (err) {
14739
15343
  if (err.code === "ENOENT") return [];
14740
15344
  throw err;
@@ -14742,10 +15346,10 @@ async function discoverDaemons(probe = defaultDiscoveryProbe) {
14742
15346
  const out = [];
14743
15347
  for (const name of names) {
14744
15348
  if (!name.endsWith(".json")) continue;
14745
- const file = import_node_path64.default.join(dir, name);
15349
+ const file = import_node_path67.default.join(dir, name);
14746
15350
  let raw;
14747
15351
  try {
14748
- raw = await import_node_fs30.promises.readFile(file, "utf8");
15352
+ raw = await import_node_fs33.promises.readFile(file, "utf8");
14749
15353
  } catch {
14750
15354
  continue;
14751
15355
  }
@@ -14773,7 +15377,7 @@ async function readRegistry() {
14773
15377
  const file = registryPath();
14774
15378
  let raw;
14775
15379
  try {
14776
- raw = await import_node_fs30.promises.readFile(file, "utf8");
15380
+ raw = await import_node_fs33.promises.readFile(file, "utf8");
14777
15381
  } catch (err) {
14778
15382
  if (err.code === "ENOENT") {
14779
15383
  return { version: 1, projects: [] };
@@ -14781,7 +15385,7 @@ async function readRegistry() {
14781
15385
  throw err;
14782
15386
  }
14783
15387
  const parsed = JSON.parse(raw);
14784
- return import_types52.RegistryFileSchema.parse(parsed);
15388
+ return import_types55.RegistryFileSchema.parse(parsed);
14785
15389
  }
14786
15390
  async function getProject(name) {
14787
15391
  const reg = await readRegistry();
@@ -14856,8 +15460,8 @@ init_auth();
14856
15460
  // src/connectors-config.ts
14857
15461
  init_cjs_shims();
14858
15462
  var import_node_os4 = __toESM(require("os"), 1);
14859
- var import_node_path65 = __toESM(require("path"), 1);
14860
- var import_node_fs31 = require("fs");
15463
+ var import_node_path68 = __toESM(require("path"), 1);
15464
+ var import_node_fs34 = require("fs");
14861
15465
  var CONNECTORS_CONFIG_VERSION = 1;
14862
15466
  var EnvRefUnsetError = class extends Error {
14863
15467
  ref;
@@ -14871,17 +15475,17 @@ var EnvRefUnsetError = class extends Error {
14871
15475
  };
14872
15476
  function neatHome2() {
14873
15477
  const override = process.env.NEAT_HOME;
14874
- if (override && override.length > 0) return import_node_path65.default.resolve(override);
14875
- return import_node_path65.default.join(import_node_os4.default.homedir(), ".neat");
15478
+ if (override && override.length > 0) return import_node_path68.default.resolve(override);
15479
+ return import_node_path68.default.join(import_node_os4.default.homedir(), ".neat");
14876
15480
  }
14877
15481
  function connectorsConfigPath(home = neatHome2()) {
14878
- return import_node_path65.default.join(home, "connectors.json");
15482
+ return import_node_path68.default.join(home, "connectors.json");
14879
15483
  }
14880
15484
  var MODE_MASK_LOOSER_THAN_0600 = 63;
14881
15485
  async function warnIfModeLooserThan0600(file) {
14882
15486
  if (process.platform === "win32") return;
14883
15487
  try {
14884
- const stat = await import_node_fs31.promises.stat(file);
15488
+ const stat = await import_node_fs34.promises.stat(file);
14885
15489
  if ((stat.mode & MODE_MASK_LOOSER_THAN_0600) !== 0) {
14886
15490
  const mode = (stat.mode & 511).toString(8).padStart(3, "0");
14887
15491
  console.warn(
@@ -14895,7 +15499,7 @@ async function readConnectorsConfig(home = neatHome2()) {
14895
15499
  const file = connectorsConfigPath(home);
14896
15500
  let raw;
14897
15501
  try {
14898
- raw = await import_node_fs31.promises.readFile(file, "utf8");
15502
+ raw = await import_node_fs34.promises.readFile(file, "utf8");
14899
15503
  } catch (err) {
14900
15504
  if (err.code === "ENOENT") {
14901
15505
  return { version: CONNECTORS_CONFIG_VERSION, connectors: [] };
@@ -15062,15 +15666,15 @@ function getConnectorStatus(id, now = Date.now(), thresholdMs = CONNECTOR_STALE_
15062
15666
 
15063
15667
  // src/connectors/index.ts
15064
15668
  init_cjs_shims();
15065
- var import_types53 = require("@neat.is/types");
15669
+ var import_types56 = require("@neat.is/types");
15066
15670
  var NO_ENV = "unknown";
15067
15671
  function staticCallSiteFor(graph, serviceName, targetNodeId) {
15068
15672
  if (!graph.hasNode(targetNodeId)) return void 0;
15069
15673
  const sites = [];
15070
15674
  for (const edgeId of graph.inboundEdges(targetNodeId)) {
15071
15675
  const edge = graph.getEdgeAttributes(edgeId);
15072
- if (edge.provenance !== import_types53.Provenance.EXTRACTED) continue;
15073
- const parsed = (0, import_types53.parseFileId)(edge.source);
15676
+ if (edge.provenance !== import_types56.Provenance.EXTRACTED) continue;
15677
+ const parsed = (0, import_types56.parseFileId)(edge.source);
15074
15678
  if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
15075
15679
  const site = { relPath: edge.evidence.file };
15076
15680
  if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
@@ -15081,7 +15685,7 @@ function staticCallSiteFor(graph, serviceName, targetNodeId) {
15081
15685
  function routeCallSiteFor(graph, targetNodeId) {
15082
15686
  if (!graph.hasNode(targetNodeId)) return void 0;
15083
15687
  const attrs = graph.getNodeAttributes(targetNodeId);
15084
- if (attrs.type !== import_types53.NodeType.RouteNode || !attrs.path) return void 0;
15688
+ if (attrs.type !== import_types56.NodeType.RouteNode || !attrs.path) return void 0;
15085
15689
  const site = { relPath: attrs.path };
15086
15690
  if (attrs.line !== void 0) site.line = attrs.line;
15087
15691
  return site;
@@ -15538,10 +16142,10 @@ var SUPABASE_RPC_TARGET_KIND = "supabase-rpc";
15538
16142
  // src/connectors/supabase/map.ts
15539
16143
  var REST_RPC_PATH_RE = /^\/rest\/v1\/rpc\/([^/?]+)/;
15540
16144
  var REST_TABLE_PATH_RE = /^\/rest\/v1\/([^/?]+)/;
15541
- function targetFromRestPath(path68) {
15542
- const rpcMatch = REST_RPC_PATH_RE.exec(path68);
16145
+ function targetFromRestPath(path71) {
16146
+ const rpcMatch = REST_RPC_PATH_RE.exec(path71);
15543
16147
  if (rpcMatch) return { targetKind: SUPABASE_RPC_TARGET_KIND, name: rpcMatch[1] };
15544
- const tableMatch = REST_TABLE_PATH_RE.exec(path68);
16148
+ const tableMatch = REST_TABLE_PATH_RE.exec(path71);
15545
16149
  if (tableMatch) return { targetKind: SUPABASE_TABLE_TARGET_KIND, name: tableMatch[1] };
15546
16150
  return null;
15547
16151
  }
@@ -15652,23 +16256,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
15652
16256
 
15653
16257
  // src/connectors/supabase/resolve.ts
15654
16258
  init_cjs_shims();
15655
- var import_types55 = require("@neat.is/types");
16259
+ var import_types58 = require("@neat.is/types");
15656
16260
  function createSupabaseResolveTarget(graph, config) {
15657
16261
  return (signal, _ctx) => {
15658
16262
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
15659
16263
  return null;
15660
16264
  }
15661
- const subResourceId = (0, import_types55.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
16265
+ const subResourceId = (0, import_types58.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
15662
16266
  if (graph.hasNode(subResourceId)) {
15663
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
16267
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types58.EdgeType.CALLS };
15664
16268
  }
15665
- const bareResourceId = (0, import_types55.infraId)(signal.targetKind, signal.targetName);
16269
+ const bareResourceId = (0, import_types58.infraId)(signal.targetKind, signal.targetName);
15666
16270
  if (graph.hasNode(bareResourceId)) {
15667
- return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
16271
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types58.EdgeType.CALLS };
15668
16272
  }
15669
- const projectLevelId = (0, import_types55.infraId)("supabase", config.nodeRef);
16273
+ const projectLevelId = (0, import_types58.infraId)("supabase", config.nodeRef);
15670
16274
  if (graph.hasNode(projectLevelId)) {
15671
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types55.EdgeType.CALLS };
16275
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types58.EdgeType.CALLS };
15672
16276
  }
15673
16277
  return null;
15674
16278
  };
@@ -15761,7 +16365,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
15761
16365
 
15762
16366
  // src/connectors/railway/index.ts
15763
16367
  init_cjs_shims();
15764
- var import_types59 = require("@neat.is/types");
16368
+ var import_types62 = require("@neat.is/types");
15765
16369
 
15766
16370
  // src/connectors/railway/client.ts
15767
16371
  init_cjs_shims();
@@ -15912,7 +16516,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
15912
16516
  const out = [];
15913
16517
  graph.forEachNode((_id, attrs) => {
15914
16518
  const node = attrs;
15915
- if (node.type !== import_types59.NodeType.RouteNode) return;
16519
+ if (node.type !== import_types62.NodeType.RouteNode) return;
15916
16520
  const route = attrs;
15917
16521
  if (route.service !== serviceName) return;
15918
16522
  out.push({
@@ -16016,12 +16620,12 @@ function createRailwayResolveTarget(config) {
16016
16620
  const serviceName = config.serviceNameById[config.serviceId];
16017
16621
  if (!serviceName) return null;
16018
16622
  if (signal.targetKind === ROUTE_TARGET_KIND) {
16019
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types59.EdgeType.CALLS };
16623
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types62.EdgeType.CALLS };
16020
16624
  }
16021
16625
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
16022
16626
  const peerName = config.serviceNameById[signal.targetName];
16023
16627
  if (!peerName) return null;
16024
- return { targetNodeId: (0, import_types59.serviceId)(peerName), serviceName, edgeType: import_types59.EdgeType.CONNECTS_TO };
16628
+ return { targetNodeId: (0, import_types62.serviceId)(peerName), serviceName, edgeType: import_types62.EdgeType.CONNECTS_TO };
16025
16629
  }
16026
16630
  return null;
16027
16631
  };
@@ -16145,9 +16749,9 @@ function parseFirebaseTargetName(targetName) {
16145
16749
  const secondSep = rest.indexOf(FIELD_SEP);
16146
16750
  if (secondSep === -1) return null;
16147
16751
  const method = rest.slice(0, secondSep);
16148
- const path68 = rest.slice(secondSep + 1);
16149
- if (!resourceName || !method || !path68) return null;
16150
- return { resourceName, method, path: path68 };
16752
+ const path71 = rest.slice(secondSep + 1);
16753
+ if (!resourceName || !method || !path71) return null;
16754
+ return { resourceName, method, path: path71 };
16151
16755
  }
16152
16756
  function resourceNameFor(type, labels) {
16153
16757
  if (!labels) return null;
@@ -16185,14 +16789,14 @@ function mapLogEntryToSignal(entry) {
16185
16789
  if (!req) return null;
16186
16790
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
16187
16791
  const method = req.requestMethod.toUpperCase();
16188
- const path68 = pathFromRequestUrl(req.requestUrl);
16189
- if (path68 === null) return null;
16792
+ const path71 = pathFromRequestUrl(req.requestUrl);
16793
+ if (path71 === null) return null;
16190
16794
  const timestamp = entry.timestamp;
16191
16795
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
16192
16796
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD2;
16193
16797
  return {
16194
16798
  targetKind: resourceType,
16195
- targetName: packFirebaseTargetName({ resourceName, method, path: path68 }),
16799
+ targetName: packFirebaseTargetName({ resourceName, method, path: path71 }),
16196
16800
  callCount: 1,
16197
16801
  errorCount: isError ? 1 : 0,
16198
16802
  lastObservedIso: timestamp
@@ -16209,7 +16813,7 @@ function mapLogEntriesToSignals(entries) {
16209
16813
 
16210
16814
  // src/connectors/firebase/resolve.ts
16211
16815
  init_cjs_shims();
16212
- var import_types60 = require("@neat.is/types");
16816
+ var import_types63 = require("@neat.is/types");
16213
16817
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
16214
16818
  switch (resourceType) {
16215
16819
  case "cloud_function":
@@ -16224,7 +16828,7 @@ function routeEntriesFor(graph, serviceName) {
16224
16828
  const entries = [];
16225
16829
  graph.forEachNode((_id, attrs) => {
16226
16830
  const node = attrs;
16227
- if (node.type !== import_types60.NodeType.RouteNode) return;
16831
+ if (node.type !== import_types63.NodeType.RouteNode) return;
16228
16832
  const route = attrs;
16229
16833
  if (route.service !== serviceName) return;
16230
16834
  entries.push({
@@ -16256,7 +16860,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
16256
16860
  return {
16257
16861
  targetNodeId: match.routeNodeId,
16258
16862
  serviceName,
16259
- edgeType: import_types60.EdgeType.CALLS
16863
+ edgeType: import_types63.EdgeType.CALLS
16260
16864
  };
16261
16865
  };
16262
16866
  }
@@ -16283,7 +16887,7 @@ init_cjs_shims();
16283
16887
 
16284
16888
  // src/connectors/cloudflare/connector.ts
16285
16889
  init_cjs_shims();
16286
- var import_types62 = require("@neat.is/types");
16890
+ var import_types65 = require("@neat.is/types");
16287
16891
 
16288
16892
  // src/connectors/cloudflare/client.ts
16289
16893
  init_cjs_shims();
@@ -16399,7 +17003,7 @@ function mapEventToSignal(event) {
16399
17003
  if (Number.isNaN(observedAt.getTime())) return null;
16400
17004
  const statusCode = metadata?.statusCode;
16401
17005
  const isError = typeof statusCode === "number" && statusCode >= ERROR_STATUS_THRESHOLD3;
16402
- const path68 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
17006
+ const path71 = metadata?.trigger ? parsePathFromTrigger(metadata.trigger) : void 0;
16403
17007
  return {
16404
17008
  targetKind: CLOUDFLARE_TARGET_KIND,
16405
17009
  targetName: scriptName,
@@ -16407,7 +17011,7 @@ function mapEventToSignal(event) {
16407
17011
  errorCount: isError ? 1 : 0,
16408
17012
  lastObservedIso: observedAt.toISOString(),
16409
17013
  method,
16410
- ...path68 ? { path: path68 } : {},
17014
+ ...path71 ? { path: path71 } : {},
16411
17015
  ...typeof statusCode === "number" ? { statusCode } : {},
16412
17016
  ...typeof metadata?.duration === "number" ? { duration: metadata.duration } : {}
16413
17017
  };
@@ -16447,19 +17051,19 @@ function findTaggedWorkerFileNode(graph, workerName) {
16447
17051
  graph.forEachNode((id, attrs) => {
16448
17052
  if (found) return;
16449
17053
  const a = attrs;
16450
- if (a.type === import_types62.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
17054
+ if (a.type === import_types65.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
16451
17055
  found = id;
16452
17056
  }
16453
17057
  });
16454
17058
  return found;
16455
17059
  }
16456
- function findMatchingRouteNode(graph, serviceName, method, path68) {
16457
- const normalizedPath = normalizePathTemplate(path68);
17060
+ function findMatchingRouteNode(graph, serviceName, method, path71) {
17061
+ const normalizedPath = normalizePathTemplate(path71);
16458
17062
  let found = null;
16459
17063
  graph.forEachNode((id, attrs) => {
16460
17064
  if (found) return;
16461
17065
  const a = attrs;
16462
- if (a.type !== import_types62.NodeType.RouteNode || a.service !== serviceName) return;
17066
+ if (a.type !== import_types65.NodeType.RouteNode || a.service !== serviceName) return;
16463
17067
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
16464
17068
  const routeMethod = (a.method ?? "").toUpperCase();
16465
17069
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -16471,18 +17075,18 @@ function createCloudflareResolveTarget(config, graph) {
16471
17075
  return (signal) => {
16472
17076
  if (signal.targetKind !== CLOUDFLARE_TARGET_KIND) return null;
16473
17077
  const scriptName = signal.targetName;
16474
- const { method, path: path68 } = signal;
17078
+ const { method, path: path71 } = signal;
16475
17079
  const resolveRouteGrain = (serviceName, wholeFileId) => {
16476
- if (!method || !path68) return wholeFileId;
16477
- return findMatchingRouteNode(graph, serviceName, method, path68) ?? wholeFileId;
17080
+ if (!method || !path71) return wholeFileId;
17081
+ return findMatchingRouteNode(graph, serviceName, method, path71) ?? wholeFileId;
16478
17082
  };
16479
17083
  const mapping = config.workers?.[scriptName];
16480
17084
  if (mapping) {
16481
- const wholeFileId = (0, import_types62.fileId)(mapping.service, mapping.entryFile);
17085
+ const wholeFileId = (0, import_types65.fileId)(mapping.service, mapping.entryFile);
16482
17086
  return {
16483
17087
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
16484
17088
  serviceName: mapping.service,
16485
- edgeType: import_types62.EdgeType.CALLS
17089
+ edgeType: import_types65.EdgeType.CALLS
16486
17090
  };
16487
17091
  }
16488
17092
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -16491,13 +17095,13 @@ function createCloudflareResolveTarget(config, graph) {
16491
17095
  return {
16492
17096
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
16493
17097
  serviceName: fileNode.service,
16494
- edgeType: import_types62.EdgeType.CALLS
17098
+ edgeType: import_types65.EdgeType.CALLS
16495
17099
  };
16496
17100
  }
16497
17101
  return {
16498
- targetNodeId: (0, import_types62.infraId)("cloudflare-worker", scriptName),
17102
+ targetNodeId: (0, import_types65.infraId)("cloudflare-worker", scriptName),
16499
17103
  serviceName: scriptName,
16500
- edgeType: import_types62.EdgeType.CALLS,
17104
+ edgeType: import_types65.EdgeType.CALLS,
16501
17105
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
16502
17106
  };
16503
17107
  };
@@ -16693,14 +17297,14 @@ function diffNeonStatementsToSignals(rows, previous, observedAtIso) {
16693
17297
 
16694
17298
  // src/connectors/neon/resolve.ts
16695
17299
  init_cjs_shims();
16696
- var import_types66 = require("@neat.is/types");
17300
+ var import_types69 = require("@neat.is/types");
16697
17301
  function createNeonResolveTarget(config) {
16698
17302
  return (signal) => {
16699
17303
  if (signal.targetKind !== NEON_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
16700
17304
  return {
16701
- targetNodeId: (0, import_types66.infraId)("sql-table", signal.targetName),
17305
+ targetNodeId: (0, import_types69.infraId)("sql-table", signal.targetName),
16702
17306
  serviceName: config.serviceName,
16703
- edgeType: import_types66.EdgeType.CALLS,
17307
+ edgeType: import_types69.EdgeType.CALLS,
16704
17308
  ensureInfraNode: { kind: "sql-table", name: signal.targetName, provider: "neon" }
16705
17309
  };
16706
17310
  };
@@ -16826,9 +17430,9 @@ function parseCloudRunTargetName(targetName) {
16826
17430
  const secondSep = rest.indexOf(FIELD_SEP2);
16827
17431
  if (secondSep === -1) return null;
16828
17432
  const method = rest.slice(0, secondSep);
16829
- const path68 = rest.slice(secondSep + 1);
16830
- if (!serviceName || !method || !path68) return null;
16831
- return { serviceName, method, path: path68 };
17433
+ const path71 = rest.slice(secondSep + 1);
17434
+ if (!serviceName || !method || !path71) return null;
17435
+ return { serviceName, method, path: path71 };
16832
17436
  }
16833
17437
 
16834
17438
  // src/connectors/cloud-run/map.ts
@@ -16857,14 +17461,14 @@ function mapLogEntryToSignal2(entry) {
16857
17461
  if (!req) return null;
16858
17462
  if (typeof req.requestMethod !== "string" || req.requestMethod.length === 0) return null;
16859
17463
  const method = req.requestMethod.toUpperCase();
16860
- const path68 = pathFromRequestUrl2(req.requestUrl);
16861
- if (path68 === null) return null;
17464
+ const path71 = pathFromRequestUrl2(req.requestUrl);
17465
+ if (path71 === null) return null;
16862
17466
  const timestamp = entry.timestamp;
16863
17467
  if (typeof timestamp !== "string" || timestamp.length === 0) return null;
16864
17468
  const isError = typeof req.status === "number" && req.status >= ERROR_STATUS_THRESHOLD4;
16865
17469
  return {
16866
17470
  targetKind: CLOUD_RUN_TARGET_KIND,
16867
- targetName: packCloudRunTargetName({ serviceName, method, path: path68 }),
17471
+ targetName: packCloudRunTargetName({ serviceName, method, path: path71 }),
16868
17472
  callCount: 1,
16869
17473
  errorCount: isError ? 1 : 0,
16870
17474
  lastObservedIso: timestamp
@@ -16881,14 +17485,14 @@ function mapLogEntriesToSignals2(entries) {
16881
17485
 
16882
17486
  // src/connectors/cloud-run/resolve.ts
16883
17487
  init_cjs_shims();
16884
- var import_types70 = require("@neat.is/types");
17488
+ var import_types73 = require("@neat.is/types");
16885
17489
  var CLOUD_RUN_SERVICE_INFRA_KIND = "cloud-run-service";
16886
17490
  function findMatchingRouteNode2(graph, serviceName, method, normalizedPath) {
16887
17491
  let found = null;
16888
17492
  graph.forEachNode((_id, attrs) => {
16889
17493
  if (found) return;
16890
17494
  const node = attrs;
16891
- if (node.type !== import_types70.NodeType.RouteNode) return;
17495
+ if (node.type !== import_types73.NodeType.RouteNode) return;
16892
17496
  const route = attrs;
16893
17497
  if (route.service !== serviceName || !route.pathTemplate) return;
16894
17498
  if (normalizePathTemplate(route.pathTemplate) !== normalizedPath) return;
@@ -16903,23 +17507,23 @@ function createCloudRunResolveTarget(graph, config) {
16903
17507
  if (signal.targetKind !== CLOUD_RUN_TARGET_KIND) return null;
16904
17508
  const identity = parseCloudRunTargetName(signal.targetName);
16905
17509
  if (!identity) return null;
16906
- const { serviceName: gcpServiceName, method, path: path68 } = identity;
17510
+ const { serviceName: gcpServiceName, method, path: path71 } = identity;
16907
17511
  const mappedService = config.serviceMap?.[gcpServiceName];
16908
17512
  if (mappedService) {
16909
17513
  const routeNodeId = findMatchingRouteNode2(
16910
17514
  graph,
16911
17515
  mappedService,
16912
17516
  method,
16913
- normalizePathTemplate(path68)
17517
+ normalizePathTemplate(path71)
16914
17518
  );
16915
17519
  if (routeNodeId) {
16916
- return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types70.EdgeType.CALLS };
17520
+ return { targetNodeId: routeNodeId, serviceName: mappedService, edgeType: import_types73.EdgeType.CALLS };
16917
17521
  }
16918
17522
  }
16919
17523
  return {
16920
- targetNodeId: (0, import_types70.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
17524
+ targetNodeId: (0, import_types73.infraId)(CLOUD_RUN_SERVICE_INFRA_KIND, gcpServiceName),
16921
17525
  serviceName: mappedService ?? gcpServiceName,
16922
- edgeType: import_types70.EdgeType.CALLS,
17526
+ edgeType: import_types73.EdgeType.CALLS,
16923
17527
  ensureInfraNode: {
16924
17528
  kind: CLOUD_RUN_SERVICE_INFRA_KIND,
16925
17529
  name: gcpServiceName,
@@ -16960,7 +17564,7 @@ function createCloudRunConnector(graph, config = {}) {
16960
17564
 
16961
17565
  // src/connectors/render/index.ts
16962
17566
  init_cjs_shims();
16963
- var import_types73 = require("@neat.is/types");
17567
+ var import_types76 = require("@neat.is/types");
16964
17568
 
16965
17569
  // src/connectors/render/types.ts
16966
17570
  init_cjs_shims();
@@ -17038,7 +17642,7 @@ function buildRenderRouteIndex(graph, serviceName) {
17038
17642
  const out = [];
17039
17643
  graph.forEachNode((_id, attrs) => {
17040
17644
  const node = attrs;
17041
- if (node.type !== import_types73.NodeType.RouteNode) return;
17645
+ if (node.type !== import_types76.NodeType.RouteNode) return;
17042
17646
  const route = attrs;
17043
17647
  if (route.service !== serviceName) return;
17044
17648
  out.push({
@@ -17123,7 +17727,7 @@ function mapRenderRequestLogsToSignals(entries, routeIndex) {
17123
17727
  function createRenderResolveTarget(config) {
17124
17728
  return (signal) => {
17125
17729
  if (signal.targetKind === ROUTE_TARGET_KIND2) {
17126
- return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types73.EdgeType.CALLS };
17730
+ return { targetNodeId: signal.targetName, serviceName: config.serviceName, edgeType: import_types76.EdgeType.CALLS };
17127
17731
  }
17128
17732
  return null;
17129
17733
  };
@@ -17261,21 +17865,21 @@ function mapInsightsToSignals(rows, observedAtIso) {
17261
17865
 
17262
17866
  // src/connectors/planetscale/resolve.ts
17263
17867
  init_cjs_shims();
17264
- var import_types77 = require("@neat.is/types");
17868
+ var import_types80 = require("@neat.is/types");
17265
17869
  var PLANETSCALE_DATABASE_KIND = "planetscale-database";
17266
17870
  function createPlanetscaleResolveTarget(graph, config) {
17267
17871
  const databaseName = `${config.organization}/${config.database}`;
17268
17872
  return (signal, _ctx) => {
17269
17873
  if (signal.targetKind !== PLANETSCALE_SQL_TABLE_TARGET_KIND || !signal.targetName) return null;
17270
- const tableId = (0, import_types77.infraId)("sql-table", signal.targetName);
17874
+ const tableId = (0, import_types80.infraId)("sql-table", signal.targetName);
17271
17875
  if (graph.hasNode(tableId)) {
17272
- return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types77.EdgeType.CALLS };
17876
+ return { targetNodeId: tableId, serviceName: config.serviceName, edgeType: import_types80.EdgeType.CALLS };
17273
17877
  }
17274
- const providerId = (0, import_types77.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
17878
+ const providerId = (0, import_types80.infraId)(PLANETSCALE_DATABASE_KIND, databaseName);
17275
17879
  return {
17276
17880
  targetNodeId: providerId,
17277
17881
  serviceName: config.serviceName,
17278
- edgeType: import_types77.EdgeType.CALLS,
17882
+ edgeType: import_types80.EdgeType.CALLS,
17279
17883
  ensureInfraNode: { kind: PLANETSCALE_DATABASE_KIND, name: databaseName, provider: "planetscale" }
17280
17884
  };
17281
17885
  };
@@ -17540,7 +18144,7 @@ function mapBuildsToSignals(builds, serviceName) {
17540
18144
 
17541
18145
  // src/connectors/eas/resolve.ts
17542
18146
  init_cjs_shims();
17543
- var import_types82 = require("@neat.is/types");
18147
+ var import_types85 = require("@neat.is/types");
17544
18148
  var NO_ENV2 = "unknown";
17545
18149
  var EAS_JSON_PHASES = /* @__PURE__ */ new Set(["READ_EAS_JSON"]);
17546
18150
  var APP_CONFIG_PHASES = /* @__PURE__ */ new Set([
@@ -17556,8 +18160,8 @@ function configBasenamesForPhase(phase) {
17556
18160
  function configNodeService(graph, configNodeId) {
17557
18161
  for (const edgeId of graph.inboundEdges(configNodeId)) {
17558
18162
  const edge = graph.getEdgeAttributes(edgeId);
17559
- if (edge.type !== import_types82.EdgeType.CONFIGURED_BY) continue;
17560
- const parsed = (0, import_types82.parseFileId)(edge.source);
18163
+ if (edge.type !== import_types85.EdgeType.CONFIGURED_BY) continue;
18164
+ const parsed = (0, import_types85.parseFileId)(edge.source);
17561
18165
  if (parsed) return parsed.service;
17562
18166
  }
17563
18167
  return null;
@@ -17568,7 +18172,7 @@ function findConfigNode(graph, basenames, serviceName) {
17568
18172
  graph.forEachNode((id, attrs) => {
17569
18173
  if (scoped) return;
17570
18174
  const node = attrs;
17571
- if (node.type !== import_types82.NodeType.ConfigNode) return;
18175
+ if (node.type !== import_types85.NodeType.ConfigNode) return;
17572
18176
  if (typeof node.name !== "string" || !basenames.includes(node.name)) return;
17573
18177
  if (anyMatch === null) anyMatch = id;
17574
18178
  if (configNodeService(graph, id) === serviceName) scoped = id;
@@ -17585,13 +18189,13 @@ function createEasResolveTarget(graph) {
17585
18189
  if (basenames.length > 0) {
17586
18190
  const configNodeId = findConfigNode(graph, basenames, serviceName);
17587
18191
  if (configNodeId) {
17588
- return { targetNodeId: configNodeId, serviceName, edgeType: import_types82.EdgeType.CALLS };
18192
+ return { targetNodeId: configNodeId, serviceName, edgeType: import_types85.EdgeType.CALLS };
17589
18193
  }
17590
18194
  }
17591
18195
  return {
17592
18196
  targetNodeId: resolveFusedServiceId(graph, serviceName, NO_ENV2),
17593
18197
  serviceName,
17594
- edgeType: import_types82.EdgeType.CALLS
18198
+ edgeType: import_types85.EdgeType.CALLS
17595
18199
  };
17596
18200
  };
17597
18201
  }
@@ -18270,11 +18874,11 @@ function registerRoutes(scope, ctx) {
18270
18874
  const candidates = req.query.type.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
18271
18875
  const parsed = [];
18272
18876
  for (const c of candidates) {
18273
- const r = import_types85.DivergenceTypeSchema.safeParse(c);
18877
+ const r = import_types88.DivergenceTypeSchema.safeParse(c);
18274
18878
  if (!r.success) {
18275
18879
  return reply.code(400).send({
18276
18880
  error: `unknown divergence type "${c}"`,
18277
- allowed: import_types85.DivergenceTypeSchema.options
18881
+ allowed: import_types88.DivergenceTypeSchema.options
18278
18882
  });
18279
18883
  }
18280
18884
  parsed.push(r.data);
@@ -18616,7 +19220,7 @@ function registerRoutes(scope, ctx) {
18616
19220
  const log = new PolicyViolationsLog(proj.paths.policyViolationsPath);
18617
19221
  let violations = await log.readAll();
18618
19222
  if (req.query.severity) {
18619
- const sev = import_types85.PolicySeveritySchema.safeParse(req.query.severity);
19223
+ const sev = import_types88.PolicySeveritySchema.safeParse(req.query.severity);
18620
19224
  if (!sev.success) {
18621
19225
  return reply.code(400).send({
18622
19226
  error: "invalid severity",
@@ -18655,7 +19259,7 @@ function registerRoutes(scope, ctx) {
18655
19259
  scope.post("/policies/check", async (req, reply) => {
18656
19260
  const proj = resolveProject(registry, req, reply, ctx.bootstrap, ctx.singleProject);
18657
19261
  if (!proj) return;
18658
- const parsed = import_types85.PoliciesCheckBodySchema.safeParse(req.body ?? {});
19262
+ const parsed = import_types88.PoliciesCheckBodySchema.safeParse(req.body ?? {});
18659
19263
  if (!parsed.success) {
18660
19264
  return reply.code(400).send({
18661
19265
  error: "invalid /policies/check body",
@@ -18976,8 +19580,8 @@ init_otel_grpc();
18976
19580
 
18977
19581
  // src/search.ts
18978
19582
  init_cjs_shims();
18979
- var import_node_fs32 = require("fs");
18980
- var import_node_path66 = __toESM(require("path"), 1);
19583
+ var import_node_fs35 = require("fs");
19584
+ var import_node_path69 = __toESM(require("path"), 1);
18981
19585
  var import_node_crypto4 = require("crypto");
18982
19586
  var DEFAULT_LIMIT = 10;
18983
19587
  var NOMIC_DIM = 768;
@@ -19131,7 +19735,7 @@ async function pickEmbedder() {
19131
19735
  }
19132
19736
  async function readCache(cachePath) {
19133
19737
  try {
19134
- const raw = await import_node_fs32.promises.readFile(cachePath, "utf8");
19738
+ const raw = await import_node_fs35.promises.readFile(cachePath, "utf8");
19135
19739
  const parsed = JSON.parse(raw);
19136
19740
  if (parsed.version !== 1) return null;
19137
19741
  return parsed;
@@ -19140,8 +19744,8 @@ async function readCache(cachePath) {
19140
19744
  }
19141
19745
  }
19142
19746
  async function writeCache(cachePath, cache) {
19143
- await import_node_fs32.promises.mkdir(import_node_path66.default.dirname(cachePath), { recursive: true });
19144
- await import_node_fs32.promises.writeFile(cachePath, JSON.stringify(cache));
19747
+ await import_node_fs35.promises.mkdir(import_node_path69.default.dirname(cachePath), { recursive: true });
19748
+ await import_node_fs35.promises.writeFile(cachePath, JSON.stringify(cache));
19145
19749
  }
19146
19750
  var VectorIndex = class {
19147
19751
  constructor(embedder, cachePath) {
@@ -19355,14 +19959,14 @@ async function bootProject(registry, name, scanPath, baseDir) {
19355
19959
  async function main() {
19356
19960
  const baseDirEnv = process.env.NEAT_OUT_DIR;
19357
19961
  const legacyOutPath = process.env.NEAT_OUT_PATH;
19358
- const baseDir = baseDirEnv ? import_node_path67.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path67.default.resolve(import_node_path67.default.dirname(legacyOutPath)) : import_node_path67.default.resolve("./neat-out");
19359
- const defaultScanPath = import_node_path67.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
19962
+ const baseDir = baseDirEnv ? import_node_path70.default.resolve(baseDirEnv) : legacyOutPath ? import_node_path70.default.resolve(import_node_path70.default.dirname(legacyOutPath)) : import_node_path70.default.resolve("./neat-out");
19963
+ const defaultScanPath = import_node_path70.default.resolve(process.env.NEAT_SCAN_PATH ?? "./demo");
19360
19964
  const registry = new Projects();
19361
19965
  await bootProject(registry, DEFAULT_PROJECT, defaultScanPath, baseDir);
19362
19966
  for (const name of parseExtraProjects(process.env.NEAT_PROJECTS)) {
19363
19967
  const envKey = `NEAT_PROJECT_SCAN_PATH_${name.toUpperCase().replace(/[^A-Z0-9]/g, "_")}`;
19364
19968
  const projectScan = process.env[envKey];
19365
- await bootProject(registry, name, projectScan ? import_node_path67.default.resolve(projectScan) : void 0, baseDir);
19969
+ await bootProject(registry, name, projectScan ? import_node_path70.default.resolve(projectScan) : void 0, baseDir);
19366
19970
  }
19367
19971
  const host = process.env.HOST ?? "0.0.0.0";
19368
19972
  const port = Number(process.env.PORT ?? 8080);