@neat.is/core 0.4.31-dev.20260718 → 0.5.1-dev.20260719

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/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-2AJHEZIB.js";
4
+ } from "./chunk-O4PCNQFG.js";
5
5
  import {
6
6
  ProjectNameCollisionError,
7
7
  addProject,
@@ -37,7 +37,7 @@ import {
37
37
  thresholdForEdgeType,
38
38
  touchLastSeen,
39
39
  writeAtomically
40
- } from "./chunk-4M2YMGUM.js";
40
+ } from "./chunk-BI3XKGVG.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
43
  } from "./chunk-I72HTUOG.js";
package/dist/neatd.cjs CHANGED
@@ -1240,7 +1240,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
1240
1240
  for (const id of walk3.path) {
1241
1241
  const owner = resolveOwningService(graph, id);
1242
1242
  if (!owner) continue;
1243
- const { id: serviceId5, svc } = owner;
1243
+ const { id: serviceId6, svc } = owner;
1244
1244
  const deps = svc.dependencies ?? {};
1245
1245
  for (const pair of candidatePairs) {
1246
1246
  const declared = deps[pair.driver];
@@ -1253,7 +1253,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
1253
1253
  );
1254
1254
  if (!result.compatible) {
1255
1255
  return {
1256
- rootCauseNode: serviceId5,
1256
+ rootCauseNode: serviceId6,
1257
1257
  rootCauseReason: result.reason ?? "incompatible driver",
1258
1258
  ...result.minDriverVersion ? {
1259
1259
  fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
@@ -1268,7 +1268,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1268
1268
  for (const id of walk3.path) {
1269
1269
  const owner = resolveOwningService(graph, id);
1270
1270
  if (!owner) continue;
1271
- const { id: serviceId5, svc } = owner;
1271
+ const { id: serviceId6, svc } = owner;
1272
1272
  const deps = svc.dependencies ?? {};
1273
1273
  const serviceNodeEngine = svc.nodeEngine;
1274
1274
  for (const constraint of nodeEngineConstraints()) {
@@ -1277,7 +1277,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1277
1277
  const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
1278
1278
  if (!result.compatible && result.reason) {
1279
1279
  return {
1280
- rootCauseNode: serviceId5,
1280
+ rootCauseNode: serviceId6,
1281
1281
  rootCauseReason: result.reason,
1282
1282
  ...result.requiredNodeVersion ? {
1283
1283
  fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
@@ -1292,7 +1292,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1292
1292
  const result = checkPackageConflict(conflict, declared, requiredDeclared);
1293
1293
  if (!result.compatible && result.reason) {
1294
1294
  return {
1295
- rootCauseNode: serviceId5,
1295
+ rootCauseNode: serviceId6,
1296
1296
  rootCauseReason: result.reason,
1297
1297
  fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
1298
1298
  };
@@ -1383,9 +1383,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
1383
1383
  function isFailingCallEdge(e) {
1384
1384
  return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
1385
1385
  }
1386
- function callSourcesForService(graph, serviceId5) {
1387
- const ids = [serviceId5];
1388
- for (const edgeId of graph.outboundEdges(serviceId5)) {
1386
+ function callSourcesForService(graph, serviceId6) {
1387
+ const ids = [serviceId6];
1388
+ for (const edgeId of graph.outboundEdges(serviceId6)) {
1389
1389
  const e = graph.getEdgeAttributes(edgeId);
1390
1390
  if (e.type !== import_types.EdgeType.CONTAINS) continue;
1391
1391
  const tgt = graph.getNodeAttributes(e.target);
@@ -1402,9 +1402,9 @@ function failingCallDominates(e, id, curEdge, curId) {
1402
1402
  }
1403
1403
  return id < curId;
1404
1404
  }
1405
- function dominantFailingCall(graph, serviceId5, visited) {
1405
+ function dominantFailingCall(graph, serviceId6, visited) {
1406
1406
  let best = null;
1407
- for (const src of callSourcesForService(graph, serviceId5)) {
1407
+ for (const src of callSourcesForService(graph, serviceId6)) {
1408
1408
  for (const edgeId of graph.outboundEdges(src)) {
1409
1409
  const e = graph.getEdgeAttributes(edgeId);
1410
1410
  if (!isFailingCallEdge(e)) continue;
@@ -2586,6 +2586,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
2586
2586
  graph.addNode(id, node);
2587
2587
  return id;
2588
2588
  }
2589
+ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
2590
+ if (!graph.hasNode(serviceNodeId)) return null;
2591
+ const sources = [serviceNodeId];
2592
+ for (const edgeId of graph.outboundEdges(serviceNodeId)) {
2593
+ const e = graph.getEdgeAttributes(edgeId);
2594
+ if (e.type === import_types3.EdgeType.CONTAINS) sources.push(e.target);
2595
+ }
2596
+ const matches = /* @__PURE__ */ new Set();
2597
+ for (const src of sources) {
2598
+ if (!graph.hasNode(src)) continue;
2599
+ for (const edgeId of graph.outboundEdges(src)) {
2600
+ const edge = graph.getEdgeAttributes(edgeId);
2601
+ if (edge.type !== import_types3.EdgeType.CONNECTS_TO || edge.provenance !== import_types3.Provenance.EXTRACTED) continue;
2602
+ if (!graph.hasNode(edge.target)) continue;
2603
+ const target = graph.getNodeAttributes(edge.target);
2604
+ if (target.type !== import_types3.NodeType.DatabaseNode || target.engine !== engine) continue;
2605
+ matches.add(edge.target);
2606
+ }
2607
+ }
2608
+ return matches.size === 1 ? [...matches][0] : null;
2609
+ }
2589
2610
  function ensureFrontierNode(graph, host, ts) {
2590
2611
  const id = frontierIdFor(host);
2591
2612
  if (graph.hasNode(id)) {
@@ -2606,6 +2627,7 @@ function ensureFrontierNode(graph, host, ts) {
2606
2627
  }
2607
2628
  function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence) {
2608
2629
  if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
2630
+ const grain = source.startsWith("file:") ? "file" : "service";
2609
2631
  const id = makeObservedEdgeId(type, source, target);
2610
2632
  if (graph.hasEdge(id)) {
2611
2633
  const existing = graph.getEdgeAttributes(id);
@@ -2622,7 +2644,9 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
2622
2644
  lastObserved: ts,
2623
2645
  callCount: newSpanCount,
2624
2646
  signal: newSignal,
2625
- confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
2647
+ confidence: (0, import_types3.confidenceForObservedSignal)(newSignal),
2648
+ grain
2649
+ // backfills legacy edges that predate ADR-142
2626
2650
  };
2627
2651
  graph.replaceEdgeAttributes(id, updated);
2628
2652
  return { edge: updated, created: false };
@@ -2642,6 +2666,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
2642
2666
  lastObserved: ts,
2643
2667
  callCount: 1,
2644
2668
  signal,
2669
+ grain,
2645
2670
  // Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).
2646
2671
  // Only set when code.filepath was present on the span — never fabricated.
2647
2672
  ...evidence ? { evidence } : {}
@@ -2844,13 +2869,18 @@ async function handleSpan(ctx, span) {
2844
2869
  ensureDatabaseNode(ctx.graph, host, span.dbSystem);
2845
2870
  targetId = (0, import_types3.databaseId)(host);
2846
2871
  } else {
2847
- const localName = span.dbName ?? span.dbSystem;
2848
- targetId = ensureLocalDatabaseNode(
2849
- ctx.graph,
2850
- span.service,
2851
- localName,
2852
- span.dbSystem
2853
- );
2872
+ const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
2873
+ if (declared) {
2874
+ targetId = declared;
2875
+ } else {
2876
+ const localName = span.dbName ?? span.dbSystem;
2877
+ targetId = ensureLocalDatabaseNode(
2878
+ ctx.graph,
2879
+ span.service,
2880
+ localName,
2881
+ span.dbSystem
2882
+ );
2883
+ }
2854
2884
  }
2855
2885
  const result = upsertObservedEdge(
2856
2886
  ctx.graph,
@@ -3034,29 +3064,29 @@ function promoteFrontierNodes(graph, opts = {}) {
3034
3064
  toPromote.push({ frontierId: id, serviceId: target });
3035
3065
  });
3036
3066
  let promoted = 0;
3037
- for (const { frontierId: frontierId2, serviceId: serviceId5 } of toPromote) {
3067
+ for (const { frontierId: frontierId2, serviceId: serviceId6 } of toPromote) {
3038
3068
  if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
3039
3069
  const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
3040
3070
  if (!gate.allowed) {
3041
3071
  continue;
3042
3072
  }
3043
3073
  }
3044
- rewireFrontierEdges(graph, frontierId2, serviceId5);
3074
+ rewireFrontierEdges(graph, frontierId2, serviceId6);
3045
3075
  graph.dropNode(frontierId2);
3046
3076
  promoted++;
3047
3077
  }
3048
3078
  return promoted;
3049
3079
  }
3050
- function rewireFrontierEdges(graph, frontierId2, serviceId5) {
3080
+ function rewireFrontierEdges(graph, frontierId2, serviceId6) {
3051
3081
  const inbound = [...graph.inboundEdges(frontierId2)];
3052
3082
  const outbound = [...graph.outboundEdges(frontierId2)];
3053
3083
  for (const edgeId of inbound) {
3054
3084
  const edge = graph.getEdgeAttributes(edgeId);
3055
- rebuildEdge(graph, edge, edge.source, serviceId5, edgeId);
3085
+ rebuildEdge(graph, edge, edge.source, serviceId6, edgeId);
3056
3086
  }
3057
3087
  for (const edgeId of outbound) {
3058
3088
  const edge = graph.getEdgeAttributes(edgeId);
3059
- rebuildEdge(graph, edge, serviceId5, edge.target, edgeId);
3089
+ rebuildEdge(graph, edge, serviceId6, edge.target, edgeId);
3060
3090
  }
3061
3091
  }
3062
3092
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
@@ -3849,9 +3879,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
3849
3879
  "StatefulSet",
3850
3880
  "DaemonSet"
3851
3881
  ]);
3852
- function addAliases(graph, serviceId5, candidates) {
3853
- if (!graph.hasNode(serviceId5)) return;
3854
- const node = graph.getNodeAttributes(serviceId5);
3882
+ function addAliases(graph, serviceId6, candidates) {
3883
+ if (!graph.hasNode(serviceId6)) return;
3884
+ const node = graph.getNodeAttributes(serviceId6);
3855
3885
  if (node.type !== import_types6.NodeType.ServiceNode) return;
3856
3886
  const set = new Set(node.aliases ?? []);
3857
3887
  for (const c of candidates) {
@@ -3861,7 +3891,7 @@ function addAliases(graph, serviceId5, candidates) {
3861
3891
  }
3862
3892
  if (set.size === 0) return;
3863
3893
  const updated = { ...node, aliases: [...set].sort() };
3864
- graph.replaceNodeAttributes(serviceId5, updated);
3894
+ graph.replaceNodeAttributes(serviceId6, updated);
3865
3895
  }
3866
3896
  function indexServicesByName(services) {
3867
3897
  const map = /* @__PURE__ */ new Map();
@@ -3894,12 +3924,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
3894
3924
  }
3895
3925
  if (!compose?.services) return;
3896
3926
  for (const [composeName, svc] of Object.entries(compose.services)) {
3897
- const serviceId5 = serviceIndex.get(composeName);
3898
- if (!serviceId5) continue;
3927
+ const serviceId6 = serviceIndex.get(composeName);
3928
+ if (!serviceId6) continue;
3899
3929
  const aliases = /* @__PURE__ */ new Set([composeName]);
3900
3930
  if (svc.container_name) aliases.add(svc.container_name);
3901
3931
  if (svc.hostname) aliases.add(svc.hostname);
3902
- addAliases(graph, serviceId5, aliases);
3932
+ addAliases(graph, serviceId6, aliases);
3903
3933
  }
3904
3934
  }
3905
3935
  var LABEL_KEYS = /* @__PURE__ */ new Set([
@@ -4549,6 +4579,29 @@ async function readIfExists(filePath) {
4549
4579
  return null;
4550
4580
  }
4551
4581
  }
4582
+ async function resolveEnvVar(serviceDir, name) {
4583
+ const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
4584
+ const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
4585
+ const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
4586
+ envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
4587
+ for (const fileName of envNames) {
4588
+ const content = await readIfExists(import_node_path14.default.join(serviceDir, fileName));
4589
+ if (!content) continue;
4590
+ for (const line of content.split("\n")) {
4591
+ const trimmed = line.trim();
4592
+ if (!trimmed || trimmed.startsWith("#")) continue;
4593
+ const eq = trimmed.indexOf("=");
4594
+ if (eq < 0) continue;
4595
+ if (trimmed.slice(0, eq).trim() !== name) continue;
4596
+ let value = trimmed.slice(eq + 1).trim();
4597
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
4598
+ value = value.slice(1, -1);
4599
+ }
4600
+ return value || null;
4601
+ }
4602
+ }
4603
+ return null;
4604
+ }
4552
4605
  async function findFirst(serviceDir, candidates) {
4553
4606
  for (const rel of candidates) {
4554
4607
  const abs = import_node_path14.default.join(serviceDir, rel);
@@ -4646,6 +4699,14 @@ async function parse3(serviceDir) {
4646
4699
  const config = parseConnectionString(urlMatch[1]);
4647
4700
  if (config) return [{ ...config, sourceFile: schemaPath }];
4648
4701
  }
4702
+ const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
4703
+ if (envMatch) {
4704
+ const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
4705
+ if (resolved) {
4706
+ const config = parseConnectionString(resolved);
4707
+ if (config) return [{ ...config, sourceFile: schemaPath }];
4708
+ }
4709
+ }
4649
4710
  return [
4650
4711
  {
4651
4712
  host: `${engine}-prisma`,
@@ -4689,6 +4750,15 @@ async function parse4(serviceDir) {
4689
4750
  const config = parseConnectionString(urlMatch[1]);
4690
4751
  if (config) return [{ ...config, sourceFile: filePath }];
4691
4752
  }
4753
+ const urlEnvMatch = content.match(
4754
+ /(?:url|connectionString)\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
4755
+ );
4756
+ if (urlEnvMatch) {
4757
+ const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
4758
+ const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
4759
+ const config = resolved ? parseConnectionString(resolved) : null;
4760
+ if (config) return [{ ...config, sourceFile: filePath }];
4761
+ }
4692
4762
  const hostMatch = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/);
4693
4763
  if (hostMatch) {
4694
4764
  const portMatch = content.match(/port\s*:\s*(\d+)/);
@@ -4742,6 +4812,15 @@ async function parse5(serviceDir) {
4742
4812
  const config = parseConnectionString(urlMatch[1]);
4743
4813
  if (config) return [{ ...config, sourceFile: filePath }];
4744
4814
  }
4815
+ const urlEnvMatch = content.match(
4816
+ /connection\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
4817
+ );
4818
+ if (urlEnvMatch) {
4819
+ const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
4820
+ const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
4821
+ const config = resolved ? parseConnectionString(resolved) : null;
4822
+ if (config) return [{ ...config, sourceFile: filePath }];
4823
+ }
4745
4824
  const host = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/)?.[1];
4746
4825
  if (host) {
4747
4826
  const port = content.match(/port\s*:\s*(\d+)/)?.[1];
@@ -6290,6 +6369,16 @@ function constructorMatchesImport(name, ctx) {
6290
6369
  if (name === "createClient") return ctx.hasSupabaseJs;
6291
6370
  return ctx.hasSupabaseSsr;
6292
6371
  }
6372
+ var SUPABASE_CLIENT_ASSIGN_RE = /(?:const|let|var)\s+(\w+)\s*=\s*(?:await\s+)?(createClient|createServerClient|createBrowserClient)\s*\(/g;
6373
+ function supabaseClientVars(content, ctx) {
6374
+ const vars = /* @__PURE__ */ new Set();
6375
+ SUPABASE_CLIENT_ASSIGN_RE.lastIndex = 0;
6376
+ let m;
6377
+ while ((m = SUPABASE_CLIENT_ASSIGN_RE.exec(content)) !== null) {
6378
+ if (constructorMatchesImport(m[2], ctx)) vars.add(m[1]);
6379
+ }
6380
+ return vars;
6381
+ }
6293
6382
  function supabaseEndpointsFromFile(file, serviceDir) {
6294
6383
  const ctx = readImports2(file.content);
6295
6384
  if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
@@ -6322,6 +6411,34 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6322
6411
  }
6323
6412
  });
6324
6413
  }
6414
+ const clientVars = supabaseClientVars(file.content, ctx);
6415
+ for (const clientVar of clientVars) {
6416
+ const accessRe = new RegExp(
6417
+ `\\b${clientVar}\\s*\\.\\s*(from|rpc)\\s*\\(\\s*['"\`]([\\w.-]+)['"\`]`,
6418
+ "g"
6419
+ );
6420
+ let am;
6421
+ while ((am = accessRe.exec(file.content)) !== null) {
6422
+ const kind = am[1] === "rpc" ? "supabase-rpc" : "supabase-table";
6423
+ const resource = am[2];
6424
+ const key = `${kind}/${resource}`;
6425
+ if (seen.has(key)) continue;
6426
+ seen.add(key);
6427
+ const line = lineOf(file.content, am[0]);
6428
+ out.push({
6429
+ infraId: (0, import_types19.infraId)(kind, resource),
6430
+ name: resource,
6431
+ kind,
6432
+ edgeType: "CALLS",
6433
+ confidenceKind: "verified-call-site",
6434
+ evidence: {
6435
+ file: import_node_path30.default.relative(serviceDir, file.path),
6436
+ line,
6437
+ snippet: snippet(file.content, line)
6438
+ }
6439
+ });
6440
+ }
6441
+ }
6325
6442
  return out;
6326
6443
  }
6327
6444
 
@@ -7882,14 +7999,23 @@ var import_types31 = require("@neat.is/types");
7882
7999
  function bucketKey(source, target, type) {
7883
8000
  return `${type}|${source}|${target}`;
7884
8001
  }
8002
+ function bucketSourceFor(graph, edge) {
8003
+ if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
8004
+ const parsed = (0, import_types31.parseFileId)(edge.source);
8005
+ if (!parsed || !graph.hasNode(edge.target)) return edge.source;
8006
+ const target = graph.getNodeAttributes(edge.target);
8007
+ if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
8008
+ return (0, import_types31.serviceId)(parsed.service);
8009
+ }
7885
8010
  function bucketEdges(graph) {
7886
8011
  const buckets2 = /* @__PURE__ */ new Map();
7887
8012
  graph.forEachEdge((id, attrs) => {
7888
8013
  const e = attrs;
7889
8014
  const parsed = (0, import_types31.parseEdgeId)(id);
7890
8015
  const provenance = parsed?.provenance ?? e.provenance;
7891
- const key = bucketKey(e.source, e.target, e.type);
7892
- const cur = buckets2.get(key) ?? { source: e.source, target: e.target, type: e.type };
8016
+ const source = bucketSourceFor(graph, e);
8017
+ const key = bucketKey(source, e.target, e.type);
8018
+ const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
7893
8019
  switch (provenance) {
7894
8020
  case import_types31.Provenance.EXTRACTED:
7895
8021
  cur.extracted = e;
@@ -9492,7 +9618,30 @@ init_otel();
9492
9618
 
9493
9619
  // src/connectors/index.ts
9494
9620
  init_cjs_shims();
9621
+ var import_types34 = require("@neat.is/types");
9495
9622
  var NO_ENV = "unknown";
9623
+ function staticCallSiteFor(graph, serviceName, targetNodeId) {
9624
+ if (!graph.hasNode(targetNodeId)) return void 0;
9625
+ const sites = [];
9626
+ for (const edgeId of graph.inboundEdges(targetNodeId)) {
9627
+ const edge = graph.getEdgeAttributes(edgeId);
9628
+ if (edge.provenance !== import_types34.Provenance.EXTRACTED) continue;
9629
+ const parsed = (0, import_types34.parseFileId)(edge.source);
9630
+ if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
9631
+ const site = { relPath: edge.evidence.file };
9632
+ if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
9633
+ sites.push(site);
9634
+ }
9635
+ return sites.length === 1 ? sites[0] : void 0;
9636
+ }
9637
+ function routeCallSiteFor(graph, targetNodeId) {
9638
+ if (!graph.hasNode(targetNodeId)) return void 0;
9639
+ const attrs = graph.getNodeAttributes(targetNodeId);
9640
+ if (attrs.type !== import_types34.NodeType.RouteNode || !attrs.path) return void 0;
9641
+ const site = { relPath: attrs.path };
9642
+ if (attrs.line !== void 0) site.line = attrs.line;
9643
+ return site;
9644
+ }
9496
9645
  async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9497
9646
  const signals = await connector.poll(ctx);
9498
9647
  let edgesCreated = 0;
@@ -9509,7 +9658,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9509
9658
  ensureInfraNode(graph, kind, name, provider);
9510
9659
  }
9511
9660
  const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
9512
- const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : void 0;
9661
+ const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
9513
9662
  const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
9514
9663
  const evidence = callSite ? {
9515
9664
  file: reconcileObservedRelPath(graph, resolved.serviceName, callSite.relPath),
@@ -10060,19 +10209,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10060
10209
 
10061
10210
  // src/connectors/supabase/resolve.ts
10062
10211
  init_cjs_shims();
10063
- var import_types35 = require("@neat.is/types");
10212
+ var import_types36 = require("@neat.is/types");
10064
10213
  function createSupabaseResolveTarget(graph, config) {
10065
10214
  return (signal, _ctx) => {
10066
10215
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10067
10216
  return null;
10068
10217
  }
10069
- const subResourceId = (0, import_types35.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10218
+ const subResourceId = (0, import_types36.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10070
10219
  if (graph.hasNode(subResourceId)) {
10071
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types35.EdgeType.CALLS };
10220
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10221
+ }
10222
+ const bareResourceId = (0, import_types36.infraId)(signal.targetKind, signal.targetName);
10223
+ if (graph.hasNode(bareResourceId)) {
10224
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10072
10225
  }
10073
- const projectLevelId = (0, import_types35.infraId)("supabase", config.nodeRef);
10226
+ const projectLevelId = (0, import_types36.infraId)("supabase", config.nodeRef);
10074
10227
  if (graph.hasNode(projectLevelId)) {
10075
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types35.EdgeType.CALLS };
10228
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10076
10229
  }
10077
10230
  return null;
10078
10231
  };
@@ -10165,7 +10318,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10165
10318
 
10166
10319
  // src/connectors/railway/index.ts
10167
10320
  init_cjs_shims();
10168
- var import_types39 = require("@neat.is/types");
10321
+ var import_types40 = require("@neat.is/types");
10169
10322
 
10170
10323
  // src/connectors/railway/client.ts
10171
10324
  init_cjs_shims();
@@ -10291,7 +10444,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
10291
10444
  const out = [];
10292
10445
  graph.forEachNode((_id, attrs) => {
10293
10446
  const node = attrs;
10294
- if (node.type !== import_types39.NodeType.RouteNode) return;
10447
+ if (node.type !== import_types40.NodeType.RouteNode) return;
10295
10448
  const route = attrs;
10296
10449
  if (route.service !== serviceName) return;
10297
10450
  out.push({
@@ -10387,12 +10540,12 @@ function createRailwayResolveTarget(config) {
10387
10540
  const serviceName = config.serviceNameById[config.serviceId];
10388
10541
  if (!serviceName) return null;
10389
10542
  if (signal.targetKind === ROUTE_TARGET_KIND) {
10390
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types39.EdgeType.CALLS };
10543
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types40.EdgeType.CALLS };
10391
10544
  }
10392
10545
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
10393
10546
  const peerName = config.serviceNameById[signal.targetName];
10394
10547
  if (!peerName) return null;
10395
- return { targetNodeId: (0, import_types39.serviceId)(peerName), serviceName, edgeType: import_types39.EdgeType.CONNECTS_TO };
10548
+ return { targetNodeId: (0, import_types40.serviceId)(peerName), serviceName, edgeType: import_types40.EdgeType.CONNECTS_TO };
10396
10549
  }
10397
10550
  return null;
10398
10551
  };
@@ -10579,7 +10732,7 @@ function mapLogEntriesToSignals(entries) {
10579
10732
 
10580
10733
  // src/connectors/firebase/resolve.ts
10581
10734
  init_cjs_shims();
10582
- var import_types40 = require("@neat.is/types");
10735
+ var import_types41 = require("@neat.is/types");
10583
10736
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
10584
10737
  switch (resourceType) {
10585
10738
  case "cloud_function":
@@ -10594,7 +10747,7 @@ function routeEntriesFor(graph, serviceName) {
10594
10747
  const entries = [];
10595
10748
  graph.forEachNode((_id, attrs) => {
10596
10749
  const node = attrs;
10597
- if (node.type !== import_types40.NodeType.RouteNode) return;
10750
+ if (node.type !== import_types41.NodeType.RouteNode) return;
10598
10751
  const route = attrs;
10599
10752
  if (route.service !== serviceName) return;
10600
10753
  entries.push({
@@ -10626,7 +10779,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
10626
10779
  return {
10627
10780
  targetNodeId: match.routeNodeId,
10628
10781
  serviceName,
10629
- edgeType: import_types40.EdgeType.CALLS
10782
+ edgeType: import_types41.EdgeType.CALLS
10630
10783
  };
10631
10784
  };
10632
10785
  }
@@ -10653,7 +10806,7 @@ init_cjs_shims();
10653
10806
 
10654
10807
  // src/connectors/cloudflare/connector.ts
10655
10808
  init_cjs_shims();
10656
- var import_types42 = require("@neat.is/types");
10809
+ var import_types43 = require("@neat.is/types");
10657
10810
 
10658
10811
  // src/connectors/cloudflare/client.ts
10659
10812
  init_cjs_shims();
@@ -10814,7 +10967,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
10814
10967
  graph.forEachNode((id, attrs) => {
10815
10968
  if (found) return;
10816
10969
  const a = attrs;
10817
- if (a.type === import_types42.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
10970
+ if (a.type === import_types43.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
10818
10971
  found = id;
10819
10972
  }
10820
10973
  });
@@ -10826,7 +10979,7 @@ function findMatchingRouteNode(graph, serviceName, method, path53) {
10826
10979
  graph.forEachNode((id, attrs) => {
10827
10980
  if (found) return;
10828
10981
  const a = attrs;
10829
- if (a.type !== import_types42.NodeType.RouteNode || a.service !== serviceName) return;
10982
+ if (a.type !== import_types43.NodeType.RouteNode || a.service !== serviceName) return;
10830
10983
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
10831
10984
  const routeMethod = (a.method ?? "").toUpperCase();
10832
10985
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -10845,11 +10998,11 @@ function createCloudflareResolveTarget(config, graph) {
10845
10998
  };
10846
10999
  const mapping = config.workers?.[scriptName];
10847
11000
  if (mapping) {
10848
- const wholeFileId = (0, import_types42.fileId)(mapping.service, mapping.entryFile);
11001
+ const wholeFileId = (0, import_types43.fileId)(mapping.service, mapping.entryFile);
10849
11002
  return {
10850
11003
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
10851
11004
  serviceName: mapping.service,
10852
- edgeType: import_types42.EdgeType.CALLS
11005
+ edgeType: import_types43.EdgeType.CALLS
10853
11006
  };
10854
11007
  }
10855
11008
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -10858,13 +11011,13 @@ function createCloudflareResolveTarget(config, graph) {
10858
11011
  return {
10859
11012
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
10860
11013
  serviceName: fileNode.service,
10861
- edgeType: import_types42.EdgeType.CALLS
11014
+ edgeType: import_types43.EdgeType.CALLS
10862
11015
  };
10863
11016
  }
10864
11017
  return {
10865
- targetNodeId: (0, import_types42.infraId)("cloudflare-worker", scriptName),
11018
+ targetNodeId: (0, import_types43.infraId)("cloudflare-worker", scriptName),
10866
11019
  serviceName: scriptName,
10867
- edgeType: import_types42.EdgeType.CALLS,
11020
+ edgeType: import_types43.EdgeType.CALLS,
10868
11021
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
10869
11022
  };
10870
11023
  };
@@ -11123,7 +11276,7 @@ function unroutedErrorsPath(neatHome4) {
11123
11276
  }
11124
11277
 
11125
11278
  // src/daemon.ts
11126
- var import_types45 = require("@neat.is/types");
11279
+ var import_types46 = require("@neat.is/types");
11127
11280
  function daemonJsonPath(scanPath) {
11128
11281
  return import_node_path50.default.join(scanPath, "neat-out", "daemon.json");
11129
11282
  }
@@ -11258,7 +11411,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
11258
11411
  if (!serviceName) return true;
11259
11412
  if (serviceNameMatchesProject(serviceName, project)) return true;
11260
11413
  return graph.someNode(
11261
- (_id, attrs) => attrs.type === import_types45.NodeType.ServiceNode && attrs.name === serviceName
11414
+ (_id, attrs) => attrs.type === import_types46.NodeType.ServiceNode && attrs.name === serviceName
11262
11415
  );
11263
11416
  }
11264
11417
  async function bootstrapProject(entry2, connectors = [], neatHome4) {