@neat.is/core 0.4.20-dev.20260620 → 0.4.20-dev.20260622

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.cjs CHANGED
@@ -1953,13 +1953,14 @@ var parentSpanCache = /* @__PURE__ */ new Map();
1953
1953
  function parentSpanKey(traceId, spanId) {
1954
1954
  return `${traceId}:${spanId}`;
1955
1955
  }
1956
- function cacheSpanService(span, now) {
1956
+ function cacheSpanService(span, now, callSite) {
1957
1957
  if (!span.traceId || !span.spanId) return;
1958
1958
  const key = parentSpanKey(span.traceId, span.spanId);
1959
1959
  parentSpanCache.delete(key);
1960
1960
  parentSpanCache.set(key, {
1961
1961
  service: span.service,
1962
1962
  env: span.env ?? "unknown",
1963
+ ...callSite ? { callSite } : {},
1963
1964
  expiresAt: now + PARENT_SPAN_CACHE_TTL_MS
1964
1965
  });
1965
1966
  while (parentSpanCache.size > PARENT_SPAN_CACHE_SIZE) {
@@ -1975,7 +1976,11 @@ function lookupParentSpan(traceId, parentSpanId, now) {
1975
1976
  parentSpanCache.delete(parentSpanKey(traceId, parentSpanId));
1976
1977
  return null;
1977
1978
  }
1978
- return { service: entry.service, env: entry.env };
1979
+ return {
1980
+ service: entry.service,
1981
+ env: entry.env,
1982
+ ...entry.callSite ? { callSite: entry.callSite } : {}
1983
+ };
1979
1984
  }
1980
1985
  function resolveServiceId(graph, host, env) {
1981
1986
  const envTagged = (0, import_types3.serviceId)(host, env);
@@ -2248,9 +2253,9 @@ async function handleSpan(ctx, span) {
2248
2253
  }
2249
2254
  const sourceId = ensureServiceNode(ctx.graph, span.service, env);
2250
2255
  const isError = span.statusCode === 2;
2251
- cacheSpanService(span, nowMs);
2252
2256
  const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
2253
2257
  const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
2258
+ cacheSpanService(span, nowMs, callSite);
2254
2259
  const observedSource = () => callSite ? ensureObservedFileNode(ctx.graph, span.service, sourceId, callSite) : sourceId;
2255
2260
  const callSiteEvidence = callSite ? { file: callSite.relPath, ...callSite.line !== void 0 ? { line: callSite.line } : {} } : void 0;
2256
2261
  let affectedNode = sourceId;
@@ -2307,13 +2312,19 @@ async function handleSpan(ctx, span) {
2307
2312
  const parent = lookupParentSpan(span.traceId, span.parentSpanId, nowMs);
2308
2313
  if (parent && parent.service !== span.service) {
2309
2314
  const parentId = ensureServiceNode(ctx.graph, parent.service, parent.env);
2315
+ const fallbackSource = parent.callSite ? ensureObservedFileNode(ctx.graph, parent.service, parentId, parent.callSite) : parentId;
2316
+ const fallbackEvidence = parent.callSite ? {
2317
+ file: parent.callSite.relPath,
2318
+ ...parent.callSite.line !== void 0 ? { line: parent.callSite.line } : {}
2319
+ } : void 0;
2310
2320
  upsertObservedEdge(
2311
2321
  ctx.graph,
2312
2322
  import_types3.EdgeType.CALLS,
2313
- parentId,
2323
+ fallbackSource,
2314
2324
  sourceId,
2315
2325
  ts,
2316
- isError
2326
+ isError,
2327
+ fallbackEvidence
2317
2328
  );
2318
2329
  }
2319
2330
  }
@@ -2903,6 +2914,9 @@ function workspaceGlobs(pkg) {
2903
2914
  if (Array.isArray(ws.packages)) return ws.packages.length > 0 ? ws.packages : null;
2904
2915
  return null;
2905
2916
  }
2917
+ async function hasPythonManifest(dir) {
2918
+ return await exists(import_node_path8.default.join(dir, "pyproject.toml")) || await exists(import_node_path8.default.join(dir, "requirements.txt")) || await exists(import_node_path8.default.join(dir, "setup.py"));
2919
+ }
2906
2920
  async function loadGitignore(scanPath) {
2907
2921
  const gitignorePath = import_node_path8.default.join(scanPath, ".gitignore");
2908
2922
  if (!await exists(gitignorePath)) return null;
@@ -2969,6 +2983,13 @@ function detectJsFramework(pkg) {
2969
2983
  if (deps["astro"] !== void 0) return "astro";
2970
2984
  return void 0;
2971
2985
  }
2986
+ async function detectJsServiceLanguage(dir, pkg) {
2987
+ const deps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
2988
+ if (deps["typescript"] !== void 0) return "typescript";
2989
+ const entries = await import_node_fs8.promises.readdir(dir).catch(() => []);
2990
+ if (entries.some((name) => /^tsconfig(\..+)?\.json$/.test(name))) return "typescript";
2991
+ return "javascript";
2992
+ }
2972
2993
  async function discoverNodeService(scanPath, dir) {
2973
2994
  const pkgPath = import_node_path8.default.join(dir, "package.json");
2974
2995
  if (!await exists(pkgPath)) return null;
@@ -2981,11 +3002,12 @@ async function discoverNodeService(scanPath, dir) {
2981
3002
  }
2982
3003
  if (!pkg.name) return null;
2983
3004
  const framework = detectJsFramework(pkg);
3005
+ const language = await detectJsServiceLanguage(dir, pkg);
2984
3006
  const node = {
2985
3007
  id: (0, import_types5.serviceId)(pkg.name),
2986
3008
  type: import_types5.NodeType.ServiceNode,
2987
3009
  name: pkg.name,
2988
- language: "javascript",
3010
+ language,
2989
3011
  version: pkg.version,
2990
3012
  dependencies: pkg.dependencies ?? {},
2991
3013
  repoPath: import_node_path8.default.relative(scanPath, dir),
@@ -3028,7 +3050,11 @@ async function discoverServices(scanPath) {
3028
3050
  if (wsGlobs) {
3029
3051
  candidateDirs.push(...await expandWorkspaceGlobs(scanPath, wsGlobs));
3030
3052
  } else {
3031
- if (rootPkg && rootPkg.name) candidateDirs.push(scanPath);
3053
+ if (rootPkg && rootPkg.name) {
3054
+ candidateDirs.push(scanPath);
3055
+ } else if (await hasPythonManifest(scanPath)) {
3056
+ candidateDirs.push(scanPath);
3057
+ }
3032
3058
  const ig = await loadGitignore(scanPath);
3033
3059
  await walkDirs(
3034
3060
  scanPath,
@@ -3037,7 +3063,7 @@ async function discoverServices(scanPath) {
3037
3063
  async (dir) => {
3038
3064
  if (await exists(import_node_path8.default.join(dir, "package.json"))) {
3039
3065
  candidateDirs.push(dir);
3040
- } else if (await exists(import_node_path8.default.join(dir, "pyproject.toml")) || await exists(import_node_path8.default.join(dir, "requirements.txt")) || await exists(import_node_path8.default.join(dir, "setup.py"))) {
3066
+ } else if (await hasPythonManifest(dir)) {
3041
3067
  candidateDirs.push(dir);
3042
3068
  }
3043
3069
  }