@neat.is/core 0.9.5 → 0.9.6

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
@@ -6,7 +6,7 @@ import {
6
6
  resolveHost,
7
7
  resolveNeatVersion,
8
8
  writeDaemonRecord
9
- } from "./chunk-4SLKQNG7.js";
9
+ } from "./chunk-XYAZOGEX.js";
10
10
  import {
11
11
  buildSearchIndex
12
12
  } from "./chunk-BC53SCT7.js";
@@ -75,7 +75,7 @@ import {
75
75
  startStalenessLoop,
76
76
  upsertConnectorEntry,
77
77
  validateConnectorEntry
78
- } from "./chunk-DGAI4VOE.js";
78
+ } from "./chunk-XOGTJVUM.js";
79
79
  import {
80
80
  startOtelGrpcReceiver
81
81
  } from "./chunk-ERE47MCR.js";
package/dist/index.cjs CHANGED
@@ -10762,27 +10762,72 @@ function walk2(node, visit) {
10762
10762
  if (child) walk2(child, visit);
10763
10763
  }
10764
10764
  }
10765
- function reconstructUrl(node) {
10766
- if (node.type === "string") {
10765
+ function resolveStaticString(node, constMap, depth = 0) {
10766
+ if (!node || depth > 4) return null;
10767
+ if (node.type === "string") return stringText(node);
10768
+ if (node.type === "template_string") {
10769
+ let out = "";
10770
+ let sawSub = false;
10767
10771
  for (let i = 0; i < node.namedChildCount; i++) {
10768
10772
  const child = node.namedChild(i);
10769
- if (child?.type === "string_fragment") return child.text;
10773
+ if (!child) continue;
10774
+ if (child.type === "string_fragment") out += child.text;
10775
+ else if (child.type === "template_substitution") sawSub = true;
10770
10776
  }
10771
- return "";
10777
+ return sawSub ? null : out;
10778
+ }
10779
+ if (node.type === "identifier") return constMap.get(node.text) ?? null;
10780
+ if (node.type === "binary_expression") {
10781
+ const left = resolveStaticString(node.childForFieldName("left"), constMap, depth + 1);
10782
+ if (left !== null) return left;
10783
+ return resolveStaticString(node.childForFieldName("right"), constMap, depth + 1);
10784
+ }
10785
+ if (node.type === "parenthesized_expression") {
10786
+ return resolveStaticString(node.namedChild(0), constMap, depth + 1);
10787
+ }
10788
+ return null;
10789
+ }
10790
+ function collectStaticStringBindings(root) {
10791
+ const map = /* @__PURE__ */ new Map();
10792
+ walk2(root, (node) => {
10793
+ if (node.type !== "variable_declarator") return;
10794
+ const name = node.childForFieldName("name");
10795
+ if (!name || name.type !== "identifier") return;
10796
+ const resolved = resolveStaticString(node.childForFieldName("value"), map);
10797
+ if (resolved !== null) map.set(name.text, resolved);
10798
+ });
10799
+ return map;
10800
+ }
10801
+ function reconstructUrl(node, constMap) {
10802
+ if (node.type === "string") {
10803
+ const s = stringText(node);
10804
+ return s === null ? null : { url: s, approximate: false };
10772
10805
  }
10773
10806
  if (node.type === "template_string") {
10774
10807
  let out = "";
10808
+ let approximate = false;
10809
+ let sawPart = false;
10775
10810
  for (let i = 0; i < node.namedChildCount; i++) {
10776
10811
  const child = node.namedChild(i);
10777
10812
  if (!child) continue;
10778
- if (child.type === "string_fragment") out += child.text;
10779
- else if (child.type === "template_substitution") out += ":param";
10813
+ if (child.type === "string_fragment") {
10814
+ out += child.text;
10815
+ sawPart = true;
10816
+ } else if (child.type === "template_substitution") {
10817
+ sawPart = true;
10818
+ const resolved = resolveStaticString(child.namedChild(0), constMap);
10819
+ if (resolved !== null) out += resolved;
10820
+ else {
10821
+ out += ":param";
10822
+ approximate = true;
10823
+ }
10824
+ }
10780
10825
  }
10781
- if (out.length === 0) {
10826
+ if (!sawPart) {
10782
10827
  const raw = node.text;
10783
- return raw.length >= 2 ? raw.slice(1, -1) : "";
10828
+ return { url: raw.length >= 2 ? raw.slice(1, -1) : "", approximate: false };
10784
10829
  }
10785
- return out;
10830
+ return { url: out, approximate };
10786
10831
  }
10787
10832
  return null;
10788
10833
  }
@@ -10836,23 +10881,57 @@ function matchHost(urlStr, knownHosts) {
10836
10881
  }
10837
10882
  return null;
10838
10883
  }
10884
+ function hostSlotUnresolved(url) {
10885
+ const schemeRel = url.indexOf("//");
10886
+ let authority;
10887
+ if (schemeRel >= 0) {
10888
+ const rest = url.slice(schemeRel + 2);
10889
+ const slash = rest.indexOf("/");
10890
+ authority = slash >= 0 ? rest.slice(0, slash) : rest;
10891
+ } else {
10892
+ const slash = url.indexOf("/");
10893
+ authority = slash >= 0 ? url.slice(0, slash) : url;
10894
+ }
10895
+ return authority.length === 0 || authority.includes(":param");
10896
+ }
10897
+ function pathHasLiteralAnchor(pathTemplate) {
10898
+ const segs = normalizePathTemplate(pathTemplate).split("/").filter((s) => s.length > 0);
10899
+ if (segs.length === 0) return false;
10900
+ return segs.some((s) => s !== ":param");
10901
+ }
10839
10902
  function clientCallSitesFromSource(source, parser, knownHosts) {
10840
10903
  const tree = parseSource5(parser, source);
10841
10904
  const out = [];
10905
+ const constMap = collectStaticStringBindings(tree.rootNode);
10842
10906
  const push = (urlNode, method, callNode) => {
10843
- const urlStr = reconstructUrl(urlNode);
10844
- if (!urlStr) return;
10845
- const host = matchHost(urlStr, knownHosts);
10846
- if (!host) return;
10847
- const p = pathOf(urlStr);
10848
- if (p === null) return;
10907
+ const rec = reconstructUrl(urlNode, constMap);
10908
+ if (!rec) return;
10849
10909
  const line = callNode.startPosition.row + 1;
10910
+ const host = matchHost(rec.url, knownHosts);
10911
+ if (host === null) {
10912
+ if (rec.approximate && hostSlotUnresolved(rec.url)) {
10913
+ out.push({
10914
+ host: null,
10915
+ method,
10916
+ pathTemplate: "",
10917
+ line,
10918
+ snippet: snippet(source, line),
10919
+ approximate: true,
10920
+ reason: "base URL interpolation could not be resolved statically"
10921
+ });
10922
+ }
10923
+ return;
10924
+ }
10925
+ const p = pathOf(rec.url);
10926
+ if (p === null) return;
10850
10927
  out.push({
10851
10928
  host,
10852
10929
  method,
10853
10930
  pathTemplate: p,
10854
10931
  line,
10855
- snippet: snippet(source, line)
10932
+ snippet: snippet(source, line),
10933
+ approximate: rec.approximate,
10934
+ reason: rec.approximate ? "path segment interpolation could not be resolved statically" : void 0
10856
10935
  });
10857
10936
  };
10858
10937
  walk2(tree.rootNode, (node) => {
@@ -10948,6 +11027,35 @@ async function addRouteCallEdges(graph, services) {
10948
11027
  if (sites.length === 0) continue;
10949
11028
  const relFile = toPosix(import_node_path38.default.relative(service.dir, file.path));
10950
11029
  for (const site of sites) {
11030
+ if (site.host === null) {
11031
+ const dedupKey2 = `${relFile}|unresolved-host`;
11032
+ if (seen.has(dedupKey2)) continue;
11033
+ seen.add(dedupKey2);
11034
+ const { fileNodeId: fileNodeId2, nodesAdded: n2, edgesAdded: e2 } = ensureFileNode(
11035
+ graph,
11036
+ service.pkg.name,
11037
+ service.node.id,
11038
+ relFile
11039
+ );
11040
+ nodesAdded += n2;
11041
+ edgesAdded += e2;
11042
+ noteExtractedDropped({
11043
+ source: fileNodeId2,
11044
+ target: "route:unresolved",
11045
+ type: import_types27.EdgeType.CALLS,
11046
+ confidence: (0, import_types27.confidenceForExtracted)("reconstructed-approximate"),
11047
+ confidenceKind: "reconstructed-approximate",
11048
+ evidence: {
11049
+ file: relFile,
11050
+ line: site.line,
11051
+ snippet: site.snippet,
11052
+ method: site.method,
11053
+ approximate: true,
11054
+ reason: site.reason
11055
+ }
11056
+ });
11057
+ continue;
11058
+ }
10951
11059
  const serverServiceId = hostToNodeId.get(site.host);
10952
11060
  if (!serverServiceId || serverServiceId === service.node.id) continue;
10953
11061
  const entries = routeIndex.get(serverServiceId);
@@ -10966,13 +11074,16 @@ async function addRouteCallEdges(graph, services) {
10966
11074
  );
10967
11075
  nodesAdded += n;
10968
11076
  edgesAdded += e;
10969
- const confidence = (0, import_types27.confidenceForExtracted)("verified-call-site");
11077
+ const anchored = pathHasLiteralAnchor(site.pathTemplate);
11078
+ const confidenceKind = site.approximate && !anchored ? "reconstructed-approximate" : "verified-call-site";
11079
+ const confidence = (0, import_types27.confidenceForExtracted)(confidenceKind);
10970
11080
  const ev = {
10971
11081
  file: relFile,
10972
11082
  line: site.line,
10973
11083
  snippet: site.snippet,
10974
11084
  method: site.method ?? match.method,
10975
- pathTemplate: site.pathTemplate
11085
+ pathTemplate: site.pathTemplate,
11086
+ ...confidenceKind === "reconstructed-approximate" ? { approximate: true, reason: site.reason } : {}
10976
11087
  };
10977
11088
  if (!(0, import_types27.passesExtractedFloor)(confidence)) {
10978
11089
  noteExtractedDropped({
@@ -10980,7 +11091,7 @@ async function addRouteCallEdges(graph, services) {
10980
11091
  target: match.routeNodeId,
10981
11092
  type: import_types27.EdgeType.CALLS,
10982
11093
  confidence,
10983
- confidenceKind: "verified-call-site",
11094
+ confidenceKind,
10984
11095
  evidence: ev
10985
11096
  });
10986
11097
  continue;