@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/cli.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveNeatVersion,
10
10
  validateConnectorEntry,
11
11
  writeDaemonRecord
12
- } from "./chunk-2AJHEZIB.js";
12
+ } from "./chunk-O4PCNQFG.js";
13
13
  import {
14
14
  buildSearchIndex
15
15
  } from "./chunk-BIY46Q6U.js";
@@ -68,7 +68,7 @@ import {
68
68
  startPersistLoop,
69
69
  startStalenessLoop,
70
70
  upsertConnectorEntry
71
- } from "./chunk-4M2YMGUM.js";
71
+ } from "./chunk-BI3XKGVG.js";
72
72
  import {
73
73
  startOtelGrpcReceiver
74
74
  } from "./chunk-I72HTUOG.js";
package/dist/index.cjs CHANGED
@@ -1277,7 +1277,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
1277
1277
  for (const id of walk3.path) {
1278
1278
  const owner = resolveOwningService(graph, id);
1279
1279
  if (!owner) continue;
1280
- const { id: serviceId5, svc } = owner;
1280
+ const { id: serviceId6, svc } = owner;
1281
1281
  const deps = svc.dependencies ?? {};
1282
1282
  for (const pair of candidatePairs) {
1283
1283
  const declared = deps[pair.driver];
@@ -1290,7 +1290,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
1290
1290
  );
1291
1291
  if (!result.compatible) {
1292
1292
  return {
1293
- rootCauseNode: serviceId5,
1293
+ rootCauseNode: serviceId6,
1294
1294
  rootCauseReason: result.reason ?? "incompatible driver",
1295
1295
  ...result.minDriverVersion ? {
1296
1296
  fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
@@ -1305,7 +1305,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1305
1305
  for (const id of walk3.path) {
1306
1306
  const owner = resolveOwningService(graph, id);
1307
1307
  if (!owner) continue;
1308
- const { id: serviceId5, svc } = owner;
1308
+ const { id: serviceId6, svc } = owner;
1309
1309
  const deps = svc.dependencies ?? {};
1310
1310
  const serviceNodeEngine = svc.nodeEngine;
1311
1311
  for (const constraint of nodeEngineConstraints()) {
@@ -1314,7 +1314,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1314
1314
  const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
1315
1315
  if (!result.compatible && result.reason) {
1316
1316
  return {
1317
- rootCauseNode: serviceId5,
1317
+ rootCauseNode: serviceId6,
1318
1318
  rootCauseReason: result.reason,
1319
1319
  ...result.requiredNodeVersion ? {
1320
1320
  fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
@@ -1329,7 +1329,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1329
1329
  const result = checkPackageConflict(conflict, declared, requiredDeclared);
1330
1330
  if (!result.compatible && result.reason) {
1331
1331
  return {
1332
- rootCauseNode: serviceId5,
1332
+ rootCauseNode: serviceId6,
1333
1333
  rootCauseReason: result.reason,
1334
1334
  fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
1335
1335
  };
@@ -1420,9 +1420,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
1420
1420
  function isFailingCallEdge(e) {
1421
1421
  return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
1422
1422
  }
1423
- function callSourcesForService(graph, serviceId5) {
1424
- const ids = [serviceId5];
1425
- for (const edgeId of graph.outboundEdges(serviceId5)) {
1423
+ function callSourcesForService(graph, serviceId6) {
1424
+ const ids = [serviceId6];
1425
+ for (const edgeId of graph.outboundEdges(serviceId6)) {
1426
1426
  const e = graph.getEdgeAttributes(edgeId);
1427
1427
  if (e.type !== import_types.EdgeType.CONTAINS) continue;
1428
1428
  const tgt = graph.getNodeAttributes(e.target);
@@ -1439,9 +1439,9 @@ function failingCallDominates(e, id, curEdge, curId) {
1439
1439
  }
1440
1440
  return id < curId;
1441
1441
  }
1442
- function dominantFailingCall(graph, serviceId5, visited) {
1442
+ function dominantFailingCall(graph, serviceId6, visited) {
1443
1443
  let best = null;
1444
- for (const src of callSourcesForService(graph, serviceId5)) {
1444
+ for (const src of callSourcesForService(graph, serviceId6)) {
1445
1445
  for (const edgeId of graph.outboundEdges(src)) {
1446
1446
  const e = graph.getEdgeAttributes(edgeId);
1447
1447
  if (!isFailingCallEdge(e)) continue;
@@ -2623,6 +2623,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
2623
2623
  graph.addNode(id, node);
2624
2624
  return id;
2625
2625
  }
2626
+ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
2627
+ if (!graph.hasNode(serviceNodeId)) return null;
2628
+ const sources = [serviceNodeId];
2629
+ for (const edgeId of graph.outboundEdges(serviceNodeId)) {
2630
+ const e = graph.getEdgeAttributes(edgeId);
2631
+ if (e.type === import_types3.EdgeType.CONTAINS) sources.push(e.target);
2632
+ }
2633
+ const matches = /* @__PURE__ */ new Set();
2634
+ for (const src of sources) {
2635
+ if (!graph.hasNode(src)) continue;
2636
+ for (const edgeId of graph.outboundEdges(src)) {
2637
+ const edge = graph.getEdgeAttributes(edgeId);
2638
+ if (edge.type !== import_types3.EdgeType.CONNECTS_TO || edge.provenance !== import_types3.Provenance.EXTRACTED) continue;
2639
+ if (!graph.hasNode(edge.target)) continue;
2640
+ const target = graph.getNodeAttributes(edge.target);
2641
+ if (target.type !== import_types3.NodeType.DatabaseNode || target.engine !== engine) continue;
2642
+ matches.add(edge.target);
2643
+ }
2644
+ }
2645
+ return matches.size === 1 ? [...matches][0] : null;
2646
+ }
2626
2647
  function ensureFrontierNode(graph, host, ts) {
2627
2648
  const id = frontierIdFor(host);
2628
2649
  if (graph.hasNode(id)) {
@@ -2643,6 +2664,7 @@ function ensureFrontierNode(graph, host, ts) {
2643
2664
  }
2644
2665
  function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence) {
2645
2666
  if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
2667
+ const grain = source.startsWith("file:") ? "file" : "service";
2646
2668
  const id = makeObservedEdgeId(type, source, target);
2647
2669
  if (graph.hasEdge(id)) {
2648
2670
  const existing = graph.getEdgeAttributes(id);
@@ -2659,7 +2681,9 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
2659
2681
  lastObserved: ts,
2660
2682
  callCount: newSpanCount,
2661
2683
  signal: newSignal,
2662
- confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
2684
+ confidence: (0, import_types3.confidenceForObservedSignal)(newSignal),
2685
+ grain
2686
+ // backfills legacy edges that predate ADR-142
2663
2687
  };
2664
2688
  graph.replaceEdgeAttributes(id, updated);
2665
2689
  return { edge: updated, created: false };
@@ -2679,6 +2703,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
2679
2703
  lastObserved: ts,
2680
2704
  callCount: 1,
2681
2705
  signal,
2706
+ grain,
2682
2707
  // Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).
2683
2708
  // Only set when code.filepath was present on the span — never fabricated.
2684
2709
  ...evidence ? { evidence } : {}
@@ -2881,13 +2906,18 @@ async function handleSpan(ctx, span) {
2881
2906
  ensureDatabaseNode(ctx.graph, host, span.dbSystem);
2882
2907
  targetId = (0, import_types3.databaseId)(host);
2883
2908
  } else {
2884
- const localName = span.dbName ?? span.dbSystem;
2885
- targetId = ensureLocalDatabaseNode(
2886
- ctx.graph,
2887
- span.service,
2888
- localName,
2889
- span.dbSystem
2890
- );
2909
+ const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
2910
+ if (declared) {
2911
+ targetId = declared;
2912
+ } else {
2913
+ const localName = span.dbName ?? span.dbSystem;
2914
+ targetId = ensureLocalDatabaseNode(
2915
+ ctx.graph,
2916
+ span.service,
2917
+ localName,
2918
+ span.dbSystem
2919
+ );
2920
+ }
2891
2921
  }
2892
2922
  const result = upsertObservedEdge(
2893
2923
  ctx.graph,
@@ -3071,29 +3101,29 @@ function promoteFrontierNodes(graph, opts = {}) {
3071
3101
  toPromote.push({ frontierId: id, serviceId: target });
3072
3102
  });
3073
3103
  let promoted = 0;
3074
- for (const { frontierId: frontierId2, serviceId: serviceId5 } of toPromote) {
3104
+ for (const { frontierId: frontierId2, serviceId: serviceId6 } of toPromote) {
3075
3105
  if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
3076
3106
  const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
3077
3107
  if (!gate.allowed) {
3078
3108
  continue;
3079
3109
  }
3080
3110
  }
3081
- rewireFrontierEdges(graph, frontierId2, serviceId5);
3111
+ rewireFrontierEdges(graph, frontierId2, serviceId6);
3082
3112
  graph.dropNode(frontierId2);
3083
3113
  promoted++;
3084
3114
  }
3085
3115
  return promoted;
3086
3116
  }
3087
- function rewireFrontierEdges(graph, frontierId2, serviceId5) {
3117
+ function rewireFrontierEdges(graph, frontierId2, serviceId6) {
3088
3118
  const inbound = [...graph.inboundEdges(frontierId2)];
3089
3119
  const outbound = [...graph.outboundEdges(frontierId2)];
3090
3120
  for (const edgeId of inbound) {
3091
3121
  const edge = graph.getEdgeAttributes(edgeId);
3092
- rebuildEdge(graph, edge, edge.source, serviceId5, edgeId);
3122
+ rebuildEdge(graph, edge, edge.source, serviceId6, edgeId);
3093
3123
  }
3094
3124
  for (const edgeId of outbound) {
3095
3125
  const edge = graph.getEdgeAttributes(edgeId);
3096
- rebuildEdge(graph, edge, serviceId5, edge.target, edgeId);
3126
+ rebuildEdge(graph, edge, serviceId6, edge.target, edgeId);
3097
3127
  }
3098
3128
  }
3099
3129
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
@@ -3889,9 +3919,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
3889
3919
  "StatefulSet",
3890
3920
  "DaemonSet"
3891
3921
  ]);
3892
- function addAliases(graph, serviceId5, candidates) {
3893
- if (!graph.hasNode(serviceId5)) return;
3894
- const node = graph.getNodeAttributes(serviceId5);
3922
+ function addAliases(graph, serviceId6, candidates) {
3923
+ if (!graph.hasNode(serviceId6)) return;
3924
+ const node = graph.getNodeAttributes(serviceId6);
3895
3925
  if (node.type !== import_types6.NodeType.ServiceNode) return;
3896
3926
  const set = new Set(node.aliases ?? []);
3897
3927
  for (const c of candidates) {
@@ -3901,7 +3931,7 @@ function addAliases(graph, serviceId5, candidates) {
3901
3931
  }
3902
3932
  if (set.size === 0) return;
3903
3933
  const updated = { ...node, aliases: [...set].sort() };
3904
- graph.replaceNodeAttributes(serviceId5, updated);
3934
+ graph.replaceNodeAttributes(serviceId6, updated);
3905
3935
  }
3906
3936
  function indexServicesByName(services) {
3907
3937
  const map = /* @__PURE__ */ new Map();
@@ -3934,12 +3964,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
3934
3964
  }
3935
3965
  if (!compose?.services) return;
3936
3966
  for (const [composeName, svc] of Object.entries(compose.services)) {
3937
- const serviceId5 = serviceIndex.get(composeName);
3938
- if (!serviceId5) continue;
3967
+ const serviceId6 = serviceIndex.get(composeName);
3968
+ if (!serviceId6) continue;
3939
3969
  const aliases = /* @__PURE__ */ new Set([composeName]);
3940
3970
  if (svc.container_name) aliases.add(svc.container_name);
3941
3971
  if (svc.hostname) aliases.add(svc.hostname);
3942
- addAliases(graph, serviceId5, aliases);
3972
+ addAliases(graph, serviceId6, aliases);
3943
3973
  }
3944
3974
  }
3945
3975
  var LABEL_KEYS = /* @__PURE__ */ new Set([
@@ -4589,6 +4619,29 @@ async function readIfExists(filePath) {
4589
4619
  return null;
4590
4620
  }
4591
4621
  }
4622
+ async function resolveEnvVar(serviceDir, name) {
4623
+ const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
4624
+ const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
4625
+ const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
4626
+ envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
4627
+ for (const fileName of envNames) {
4628
+ const content = await readIfExists(import_node_path14.default.join(serviceDir, fileName));
4629
+ if (!content) continue;
4630
+ for (const line of content.split("\n")) {
4631
+ const trimmed = line.trim();
4632
+ if (!trimmed || trimmed.startsWith("#")) continue;
4633
+ const eq = trimmed.indexOf("=");
4634
+ if (eq < 0) continue;
4635
+ if (trimmed.slice(0, eq).trim() !== name) continue;
4636
+ let value = trimmed.slice(eq + 1).trim();
4637
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
4638
+ value = value.slice(1, -1);
4639
+ }
4640
+ return value || null;
4641
+ }
4642
+ }
4643
+ return null;
4644
+ }
4592
4645
  async function findFirst(serviceDir, candidates) {
4593
4646
  for (const rel of candidates) {
4594
4647
  const abs = import_node_path14.default.join(serviceDir, rel);
@@ -4686,6 +4739,14 @@ async function parse3(serviceDir) {
4686
4739
  const config = parseConnectionString(urlMatch[1]);
4687
4740
  if (config) return [{ ...config, sourceFile: schemaPath }];
4688
4741
  }
4742
+ const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
4743
+ if (envMatch) {
4744
+ const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
4745
+ if (resolved) {
4746
+ const config = parseConnectionString(resolved);
4747
+ if (config) return [{ ...config, sourceFile: schemaPath }];
4748
+ }
4749
+ }
4689
4750
  return [
4690
4751
  {
4691
4752
  host: `${engine}-prisma`,
@@ -4729,6 +4790,15 @@ async function parse4(serviceDir) {
4729
4790
  const config = parseConnectionString(urlMatch[1]);
4730
4791
  if (config) return [{ ...config, sourceFile: filePath }];
4731
4792
  }
4793
+ const urlEnvMatch = content.match(
4794
+ /(?:url|connectionString)\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
4795
+ );
4796
+ if (urlEnvMatch) {
4797
+ const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
4798
+ const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
4799
+ const config = resolved ? parseConnectionString(resolved) : null;
4800
+ if (config) return [{ ...config, sourceFile: filePath }];
4801
+ }
4732
4802
  const hostMatch = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/);
4733
4803
  if (hostMatch) {
4734
4804
  const portMatch = content.match(/port\s*:\s*(\d+)/);
@@ -4782,6 +4852,15 @@ async function parse5(serviceDir) {
4782
4852
  const config = parseConnectionString(urlMatch[1]);
4783
4853
  if (config) return [{ ...config, sourceFile: filePath }];
4784
4854
  }
4855
+ const urlEnvMatch = content.match(
4856
+ /connection\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
4857
+ );
4858
+ if (urlEnvMatch) {
4859
+ const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
4860
+ const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
4861
+ const config = resolved ? parseConnectionString(resolved) : null;
4862
+ if (config) return [{ ...config, sourceFile: filePath }];
4863
+ }
4785
4864
  const host = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/)?.[1];
4786
4865
  if (host) {
4787
4866
  const port = content.match(/port\s*:\s*(\d+)/)?.[1];
@@ -6330,6 +6409,16 @@ function constructorMatchesImport(name, ctx) {
6330
6409
  if (name === "createClient") return ctx.hasSupabaseJs;
6331
6410
  return ctx.hasSupabaseSsr;
6332
6411
  }
6412
+ var SUPABASE_CLIENT_ASSIGN_RE = /(?:const|let|var)\s+(\w+)\s*=\s*(?:await\s+)?(createClient|createServerClient|createBrowserClient)\s*\(/g;
6413
+ function supabaseClientVars(content, ctx) {
6414
+ const vars = /* @__PURE__ */ new Set();
6415
+ SUPABASE_CLIENT_ASSIGN_RE.lastIndex = 0;
6416
+ let m;
6417
+ while ((m = SUPABASE_CLIENT_ASSIGN_RE.exec(content)) !== null) {
6418
+ if (constructorMatchesImport(m[2], ctx)) vars.add(m[1]);
6419
+ }
6420
+ return vars;
6421
+ }
6333
6422
  function supabaseEndpointsFromFile(file, serviceDir) {
6334
6423
  const ctx = readImports2(file.content);
6335
6424
  if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
@@ -6362,6 +6451,34 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6362
6451
  }
6363
6452
  });
6364
6453
  }
6454
+ const clientVars = supabaseClientVars(file.content, ctx);
6455
+ for (const clientVar of clientVars) {
6456
+ const accessRe = new RegExp(
6457
+ `\\b${clientVar}\\s*\\.\\s*(from|rpc)\\s*\\(\\s*['"\`]([\\w.-]+)['"\`]`,
6458
+ "g"
6459
+ );
6460
+ let am;
6461
+ while ((am = accessRe.exec(file.content)) !== null) {
6462
+ const kind = am[1] === "rpc" ? "supabase-rpc" : "supabase-table";
6463
+ const resource = am[2];
6464
+ const key = `${kind}/${resource}`;
6465
+ if (seen.has(key)) continue;
6466
+ seen.add(key);
6467
+ const line = lineOf(file.content, am[0]);
6468
+ out.push({
6469
+ infraId: (0, import_types19.infraId)(kind, resource),
6470
+ name: resource,
6471
+ kind,
6472
+ edgeType: "CALLS",
6473
+ confidenceKind: "verified-call-site",
6474
+ evidence: {
6475
+ file: import_node_path30.default.relative(serviceDir, file.path),
6476
+ line,
6477
+ snippet: snippet(file.content, line)
6478
+ }
6479
+ });
6480
+ }
6481
+ }
6365
6482
  return out;
6366
6483
  }
6367
6484
 
@@ -7870,14 +7987,23 @@ var import_types31 = require("@neat.is/types");
7870
7987
  function bucketKey(source, target, type) {
7871
7988
  return `${type}|${source}|${target}`;
7872
7989
  }
7990
+ function bucketSourceFor(graph, edge) {
7991
+ if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
7992
+ const parsed = (0, import_types31.parseFileId)(edge.source);
7993
+ if (!parsed || !graph.hasNode(edge.target)) return edge.source;
7994
+ const target = graph.getNodeAttributes(edge.target);
7995
+ if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
7996
+ return (0, import_types31.serviceId)(parsed.service);
7997
+ }
7873
7998
  function bucketEdges(graph) {
7874
7999
  const buckets2 = /* @__PURE__ */ new Map();
7875
8000
  graph.forEachEdge((id, attrs) => {
7876
8001
  const e = attrs;
7877
8002
  const parsed = (0, import_types31.parseEdgeId)(id);
7878
8003
  const provenance = parsed?.provenance ?? e.provenance;
7879
- const key = bucketKey(e.source, e.target, e.type);
7880
- const cur = buckets2.get(key) ?? { source: e.source, target: e.target, type: e.type };
8004
+ const source = bucketSourceFor(graph, e);
8005
+ const key = bucketKey(source, e.target, e.type);
8006
+ const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
7881
8007
  switch (provenance) {
7882
8008
  case import_types31.Provenance.EXTRACTED:
7883
8009
  cur.extracted = e;
@@ -9598,7 +9724,30 @@ init_otel();
9598
9724
 
9599
9725
  // src/connectors/index.ts
9600
9726
  init_cjs_shims();
9727
+ var import_types34 = require("@neat.is/types");
9601
9728
  var NO_ENV = "unknown";
9729
+ function staticCallSiteFor(graph, serviceName, targetNodeId) {
9730
+ if (!graph.hasNode(targetNodeId)) return void 0;
9731
+ const sites = [];
9732
+ for (const edgeId of graph.inboundEdges(targetNodeId)) {
9733
+ const edge = graph.getEdgeAttributes(edgeId);
9734
+ if (edge.provenance !== import_types34.Provenance.EXTRACTED) continue;
9735
+ const parsed = (0, import_types34.parseFileId)(edge.source);
9736
+ if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
9737
+ const site = { relPath: edge.evidence.file };
9738
+ if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
9739
+ sites.push(site);
9740
+ }
9741
+ return sites.length === 1 ? sites[0] : void 0;
9742
+ }
9743
+ function routeCallSiteFor(graph, targetNodeId) {
9744
+ if (!graph.hasNode(targetNodeId)) return void 0;
9745
+ const attrs = graph.getNodeAttributes(targetNodeId);
9746
+ if (attrs.type !== import_types34.NodeType.RouteNode || !attrs.path) return void 0;
9747
+ const site = { relPath: attrs.path };
9748
+ if (attrs.line !== void 0) site.line = attrs.line;
9749
+ return site;
9750
+ }
9602
9751
  async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9603
9752
  const signals = await connector.poll(ctx);
9604
9753
  let edgesCreated = 0;
@@ -9615,7 +9764,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9615
9764
  ensureInfraNode(graph, kind, name, provider);
9616
9765
  }
9617
9766
  const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
9618
- const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : void 0;
9767
+ const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
9619
9768
  const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
9620
9769
  const evidence = callSite ? {
9621
9770
  file: reconcileObservedRelPath(graph, resolved.serviceName, callSite.relPath),
@@ -10166,19 +10315,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10166
10315
 
10167
10316
  // src/connectors/supabase/resolve.ts
10168
10317
  init_cjs_shims();
10169
- var import_types35 = require("@neat.is/types");
10318
+ var import_types36 = require("@neat.is/types");
10170
10319
  function createSupabaseResolveTarget(graph, config) {
10171
10320
  return (signal, _ctx) => {
10172
10321
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10173
10322
  return null;
10174
10323
  }
10175
- const subResourceId = (0, import_types35.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10324
+ const subResourceId = (0, import_types36.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
10176
10325
  if (graph.hasNode(subResourceId)) {
10177
- return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types35.EdgeType.CALLS };
10326
+ return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10327
+ }
10328
+ const bareResourceId = (0, import_types36.infraId)(signal.targetKind, signal.targetName);
10329
+ if (graph.hasNode(bareResourceId)) {
10330
+ return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10178
10331
  }
10179
- const projectLevelId = (0, import_types35.infraId)("supabase", config.nodeRef);
10332
+ const projectLevelId = (0, import_types36.infraId)("supabase", config.nodeRef);
10180
10333
  if (graph.hasNode(projectLevelId)) {
10181
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types35.EdgeType.CALLS };
10334
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10182
10335
  }
10183
10336
  return null;
10184
10337
  };
@@ -10271,7 +10424,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10271
10424
 
10272
10425
  // src/connectors/railway/index.ts
10273
10426
  init_cjs_shims();
10274
- var import_types39 = require("@neat.is/types");
10427
+ var import_types40 = require("@neat.is/types");
10275
10428
 
10276
10429
  // src/connectors/railway/client.ts
10277
10430
  init_cjs_shims();
@@ -10397,7 +10550,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
10397
10550
  const out = [];
10398
10551
  graph.forEachNode((_id, attrs) => {
10399
10552
  const node = attrs;
10400
- if (node.type !== import_types39.NodeType.RouteNode) return;
10553
+ if (node.type !== import_types40.NodeType.RouteNode) return;
10401
10554
  const route = attrs;
10402
10555
  if (route.service !== serviceName) return;
10403
10556
  out.push({
@@ -10493,12 +10646,12 @@ function createRailwayResolveTarget(config) {
10493
10646
  const serviceName = config.serviceNameById[config.serviceId];
10494
10647
  if (!serviceName) return null;
10495
10648
  if (signal.targetKind === ROUTE_TARGET_KIND) {
10496
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types39.EdgeType.CALLS };
10649
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types40.EdgeType.CALLS };
10497
10650
  }
10498
10651
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
10499
10652
  const peerName = config.serviceNameById[signal.targetName];
10500
10653
  if (!peerName) return null;
10501
- return { targetNodeId: (0, import_types39.serviceId)(peerName), serviceName, edgeType: import_types39.EdgeType.CONNECTS_TO };
10654
+ return { targetNodeId: (0, import_types40.serviceId)(peerName), serviceName, edgeType: import_types40.EdgeType.CONNECTS_TO };
10502
10655
  }
10503
10656
  return null;
10504
10657
  };
@@ -10685,7 +10838,7 @@ function mapLogEntriesToSignals(entries) {
10685
10838
 
10686
10839
  // src/connectors/firebase/resolve.ts
10687
10840
  init_cjs_shims();
10688
- var import_types40 = require("@neat.is/types");
10841
+ var import_types41 = require("@neat.is/types");
10689
10842
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
10690
10843
  switch (resourceType) {
10691
10844
  case "cloud_function":
@@ -10700,7 +10853,7 @@ function routeEntriesFor(graph, serviceName) {
10700
10853
  const entries = [];
10701
10854
  graph.forEachNode((_id, attrs) => {
10702
10855
  const node = attrs;
10703
- if (node.type !== import_types40.NodeType.RouteNode) return;
10856
+ if (node.type !== import_types41.NodeType.RouteNode) return;
10704
10857
  const route = attrs;
10705
10858
  if (route.service !== serviceName) return;
10706
10859
  entries.push({
@@ -10732,7 +10885,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
10732
10885
  return {
10733
10886
  targetNodeId: match.routeNodeId,
10734
10887
  serviceName,
10735
- edgeType: import_types40.EdgeType.CALLS
10888
+ edgeType: import_types41.EdgeType.CALLS
10736
10889
  };
10737
10890
  };
10738
10891
  }
@@ -10759,7 +10912,7 @@ init_cjs_shims();
10759
10912
 
10760
10913
  // src/connectors/cloudflare/connector.ts
10761
10914
  init_cjs_shims();
10762
- var import_types42 = require("@neat.is/types");
10915
+ var import_types43 = require("@neat.is/types");
10763
10916
 
10764
10917
  // src/connectors/cloudflare/client.ts
10765
10918
  init_cjs_shims();
@@ -10920,7 +11073,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
10920
11073
  graph.forEachNode((id, attrs) => {
10921
11074
  if (found) return;
10922
11075
  const a = attrs;
10923
- if (a.type === import_types42.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
11076
+ if (a.type === import_types43.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
10924
11077
  found = id;
10925
11078
  }
10926
11079
  });
@@ -10932,7 +11085,7 @@ function findMatchingRouteNode(graph, serviceName, method, path51) {
10932
11085
  graph.forEachNode((id, attrs) => {
10933
11086
  if (found) return;
10934
11087
  const a = attrs;
10935
- if (a.type !== import_types42.NodeType.RouteNode || a.service !== serviceName) return;
11088
+ if (a.type !== import_types43.NodeType.RouteNode || a.service !== serviceName) return;
10936
11089
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
10937
11090
  const routeMethod = (a.method ?? "").toUpperCase();
10938
11091
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -10951,11 +11104,11 @@ function createCloudflareResolveTarget(config, graph) {
10951
11104
  };
10952
11105
  const mapping = config.workers?.[scriptName];
10953
11106
  if (mapping) {
10954
- const wholeFileId = (0, import_types42.fileId)(mapping.service, mapping.entryFile);
11107
+ const wholeFileId = (0, import_types43.fileId)(mapping.service, mapping.entryFile);
10955
11108
  return {
10956
11109
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
10957
11110
  serviceName: mapping.service,
10958
- edgeType: import_types42.EdgeType.CALLS
11111
+ edgeType: import_types43.EdgeType.CALLS
10959
11112
  };
10960
11113
  }
10961
11114
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -10964,13 +11117,13 @@ function createCloudflareResolveTarget(config, graph) {
10964
11117
  return {
10965
11118
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
10966
11119
  serviceName: fileNode.service,
10967
- edgeType: import_types42.EdgeType.CALLS
11120
+ edgeType: import_types43.EdgeType.CALLS
10968
11121
  };
10969
11122
  }
10970
11123
  return {
10971
- targetNodeId: (0, import_types42.infraId)("cloudflare-worker", scriptName),
11124
+ targetNodeId: (0, import_types43.infraId)("cloudflare-worker", scriptName),
10972
11125
  serviceName: scriptName,
10973
- edgeType: import_types42.EdgeType.CALLS,
11126
+ edgeType: import_types43.EdgeType.CALLS,
10974
11127
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
10975
11128
  };
10976
11129
  };
@@ -11229,7 +11382,7 @@ function unroutedErrorsPath(neatHome3) {
11229
11382
  }
11230
11383
 
11231
11384
  // src/daemon.ts
11232
- var import_types45 = require("@neat.is/types");
11385
+ var import_types46 = require("@neat.is/types");
11233
11386
  function daemonJsonPath(scanPath) {
11234
11387
  return import_node_path50.default.join(scanPath, "neat-out", "daemon.json");
11235
11388
  }
@@ -11350,7 +11503,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
11350
11503
  if (!serviceName) return true;
11351
11504
  if (serviceNameMatchesProject(serviceName, project)) return true;
11352
11505
  return graph.someNode(
11353
- (_id, attrs) => attrs.type === import_types45.NodeType.ServiceNode && attrs.name === serviceName
11506
+ (_id, attrs) => attrs.type === import_types46.NodeType.ServiceNode && attrs.name === serviceName
11354
11507
  );
11355
11508
  }
11356
11509
  async function bootstrapProject(entry, connectors = [], neatHome3) {