@neat.is/core 0.5.0 → 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-5MGHWP6R.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-QQDZSIY3.js";
71
+ } from "./chunk-BI3XKGVG.js";
72
72
  import {
73
73
  startOtelGrpcReceiver
74
74
  } from "./chunk-I72HTUOG.js";
package/dist/index.cjs CHANGED
@@ -2664,6 +2664,7 @@ function ensureFrontierNode(graph, host, ts) {
2664
2664
  }
2665
2665
  function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence) {
2666
2666
  if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
2667
+ const grain = source.startsWith("file:") ? "file" : "service";
2667
2668
  const id = makeObservedEdgeId(type, source, target);
2668
2669
  if (graph.hasEdge(id)) {
2669
2670
  const existing = graph.getEdgeAttributes(id);
@@ -2680,7 +2681,9 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
2680
2681
  lastObserved: ts,
2681
2682
  callCount: newSpanCount,
2682
2683
  signal: newSignal,
2683
- confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
2684
+ confidence: (0, import_types3.confidenceForObservedSignal)(newSignal),
2685
+ grain
2686
+ // backfills legacy edges that predate ADR-142
2684
2687
  };
2685
2688
  graph.replaceEdgeAttributes(id, updated);
2686
2689
  return { edge: updated, created: false };
@@ -2700,6 +2703,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
2700
2703
  lastObserved: ts,
2701
2704
  callCount: 1,
2702
2705
  signal,
2706
+ grain,
2703
2707
  // Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).
2704
2708
  // Only set when code.filepath was present on the span — never fabricated.
2705
2709
  ...evidence ? { evidence } : {}
@@ -4786,6 +4790,15 @@ async function parse4(serviceDir) {
4786
4790
  const config = parseConnectionString(urlMatch[1]);
4787
4791
  if (config) return [{ ...config, sourceFile: filePath }];
4788
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
+ }
4789
4802
  const hostMatch = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/);
4790
4803
  if (hostMatch) {
4791
4804
  const portMatch = content.match(/port\s*:\s*(\d+)/);
@@ -4839,6 +4852,15 @@ async function parse5(serviceDir) {
4839
4852
  const config = parseConnectionString(urlMatch[1]);
4840
4853
  if (config) return [{ ...config, sourceFile: filePath }];
4841
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
+ }
4842
4864
  const host = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/)?.[1];
4843
4865
  if (host) {
4844
4866
  const port = content.match(/port\s*:\s*(\d+)/)?.[1];
@@ -6387,6 +6409,16 @@ function constructorMatchesImport(name, ctx) {
6387
6409
  if (name === "createClient") return ctx.hasSupabaseJs;
6388
6410
  return ctx.hasSupabaseSsr;
6389
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
+ }
6390
6422
  function supabaseEndpointsFromFile(file, serviceDir) {
6391
6423
  const ctx = readImports2(file.content);
6392
6424
  if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
@@ -6419,6 +6451,34 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6419
6451
  }
6420
6452
  });
6421
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
+ }
6422
6482
  return out;
6423
6483
  }
6424
6484
 
@@ -9664,7 +9724,30 @@ init_otel();
9664
9724
 
9665
9725
  // src/connectors/index.ts
9666
9726
  init_cjs_shims();
9727
+ var import_types34 = require("@neat.is/types");
9667
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
+ }
9668
9751
  async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9669
9752
  const signals = await connector.poll(ctx);
9670
9753
  let edgesCreated = 0;
@@ -9681,7 +9764,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
9681
9764
  ensureInfraNode(graph, kind, name, provider);
9682
9765
  }
9683
9766
  const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
9684
- 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);
9685
9768
  const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
9686
9769
  const evidence = callSite ? {
9687
9770
  file: reconcileObservedRelPath(graph, resolved.serviceName, callSite.relPath),
@@ -10232,19 +10315,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
10232
10315
 
10233
10316
  // src/connectors/supabase/resolve.ts
10234
10317
  init_cjs_shims();
10235
- var import_types35 = require("@neat.is/types");
10318
+ var import_types36 = require("@neat.is/types");
10236
10319
  function createSupabaseResolveTarget(graph, config) {
10237
10320
  return (signal, _ctx) => {
10238
10321
  if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
10239
10322
  return null;
10240
10323
  }
10241
- 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}`);
10242
10325
  if (graph.hasNode(subResourceId)) {
10243
- 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 };
10244
10331
  }
10245
- const projectLevelId = (0, import_types35.infraId)("supabase", config.nodeRef);
10332
+ const projectLevelId = (0, import_types36.infraId)("supabase", config.nodeRef);
10246
10333
  if (graph.hasNode(projectLevelId)) {
10247
- return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types35.EdgeType.CALLS };
10334
+ return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
10248
10335
  }
10249
10336
  return null;
10250
10337
  };
@@ -10337,7 +10424,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
10337
10424
 
10338
10425
  // src/connectors/railway/index.ts
10339
10426
  init_cjs_shims();
10340
- var import_types39 = require("@neat.is/types");
10427
+ var import_types40 = require("@neat.is/types");
10341
10428
 
10342
10429
  // src/connectors/railway/client.ts
10343
10430
  init_cjs_shims();
@@ -10463,7 +10550,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
10463
10550
  const out = [];
10464
10551
  graph.forEachNode((_id, attrs) => {
10465
10552
  const node = attrs;
10466
- if (node.type !== import_types39.NodeType.RouteNode) return;
10553
+ if (node.type !== import_types40.NodeType.RouteNode) return;
10467
10554
  const route = attrs;
10468
10555
  if (route.service !== serviceName) return;
10469
10556
  out.push({
@@ -10559,12 +10646,12 @@ function createRailwayResolveTarget(config) {
10559
10646
  const serviceName = config.serviceNameById[config.serviceId];
10560
10647
  if (!serviceName) return null;
10561
10648
  if (signal.targetKind === ROUTE_TARGET_KIND) {
10562
- return { targetNodeId: signal.targetName, serviceName, edgeType: import_types39.EdgeType.CALLS };
10649
+ return { targetNodeId: signal.targetName, serviceName, edgeType: import_types40.EdgeType.CALLS };
10563
10650
  }
10564
10651
  if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
10565
10652
  const peerName = config.serviceNameById[signal.targetName];
10566
10653
  if (!peerName) return null;
10567
- 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 };
10568
10655
  }
10569
10656
  return null;
10570
10657
  };
@@ -10751,7 +10838,7 @@ function mapLogEntriesToSignals(entries) {
10751
10838
 
10752
10839
  // src/connectors/firebase/resolve.ts
10753
10840
  init_cjs_shims();
10754
- var import_types40 = require("@neat.is/types");
10841
+ var import_types41 = require("@neat.is/types");
10755
10842
  function neatServiceNameFor(resourceType, resourceName, serviceMap) {
10756
10843
  switch (resourceType) {
10757
10844
  case "cloud_function":
@@ -10766,7 +10853,7 @@ function routeEntriesFor(graph, serviceName) {
10766
10853
  const entries = [];
10767
10854
  graph.forEachNode((_id, attrs) => {
10768
10855
  const node = attrs;
10769
- if (node.type !== import_types40.NodeType.RouteNode) return;
10856
+ if (node.type !== import_types41.NodeType.RouteNode) return;
10770
10857
  const route = attrs;
10771
10858
  if (route.service !== serviceName) return;
10772
10859
  entries.push({
@@ -10798,7 +10885,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
10798
10885
  return {
10799
10886
  targetNodeId: match.routeNodeId,
10800
10887
  serviceName,
10801
- edgeType: import_types40.EdgeType.CALLS
10888
+ edgeType: import_types41.EdgeType.CALLS
10802
10889
  };
10803
10890
  };
10804
10891
  }
@@ -10825,7 +10912,7 @@ init_cjs_shims();
10825
10912
 
10826
10913
  // src/connectors/cloudflare/connector.ts
10827
10914
  init_cjs_shims();
10828
- var import_types42 = require("@neat.is/types");
10915
+ var import_types43 = require("@neat.is/types");
10829
10916
 
10830
10917
  // src/connectors/cloudflare/client.ts
10831
10918
  init_cjs_shims();
@@ -10986,7 +11073,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
10986
11073
  graph.forEachNode((id, attrs) => {
10987
11074
  if (found) return;
10988
11075
  const a = attrs;
10989
- 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) {
10990
11077
  found = id;
10991
11078
  }
10992
11079
  });
@@ -10998,7 +11085,7 @@ function findMatchingRouteNode(graph, serviceName, method, path51) {
10998
11085
  graph.forEachNode((id, attrs) => {
10999
11086
  if (found) return;
11000
11087
  const a = attrs;
11001
- if (a.type !== import_types42.NodeType.RouteNode || a.service !== serviceName) return;
11088
+ if (a.type !== import_types43.NodeType.RouteNode || a.service !== serviceName) return;
11002
11089
  if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
11003
11090
  const routeMethod = (a.method ?? "").toUpperCase();
11004
11091
  if (routeMethod !== "ALL" && routeMethod !== method) return;
@@ -11017,11 +11104,11 @@ function createCloudflareResolveTarget(config, graph) {
11017
11104
  };
11018
11105
  const mapping = config.workers?.[scriptName];
11019
11106
  if (mapping) {
11020
- const wholeFileId = (0, import_types42.fileId)(mapping.service, mapping.entryFile);
11107
+ const wholeFileId = (0, import_types43.fileId)(mapping.service, mapping.entryFile);
11021
11108
  return {
11022
11109
  targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
11023
11110
  serviceName: mapping.service,
11024
- edgeType: import_types42.EdgeType.CALLS
11111
+ edgeType: import_types43.EdgeType.CALLS
11025
11112
  };
11026
11113
  }
11027
11114
  const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
@@ -11030,13 +11117,13 @@ function createCloudflareResolveTarget(config, graph) {
11030
11117
  return {
11031
11118
  targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
11032
11119
  serviceName: fileNode.service,
11033
- edgeType: import_types42.EdgeType.CALLS
11120
+ edgeType: import_types43.EdgeType.CALLS
11034
11121
  };
11035
11122
  }
11036
11123
  return {
11037
- targetNodeId: (0, import_types42.infraId)("cloudflare-worker", scriptName),
11124
+ targetNodeId: (0, import_types43.infraId)("cloudflare-worker", scriptName),
11038
11125
  serviceName: scriptName,
11039
- edgeType: import_types42.EdgeType.CALLS,
11126
+ edgeType: import_types43.EdgeType.CALLS,
11040
11127
  ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
11041
11128
  };
11042
11129
  };
@@ -11295,7 +11382,7 @@ function unroutedErrorsPath(neatHome3) {
11295
11382
  }
11296
11383
 
11297
11384
  // src/daemon.ts
11298
- var import_types45 = require("@neat.is/types");
11385
+ var import_types46 = require("@neat.is/types");
11299
11386
  function daemonJsonPath(scanPath) {
11300
11387
  return import_node_path50.default.join(scanPath, "neat-out", "daemon.json");
11301
11388
  }
@@ -11416,7 +11503,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
11416
11503
  if (!serviceName) return true;
11417
11504
  if (serviceNameMatchesProject(serviceName, project)) return true;
11418
11505
  return graph.someNode(
11419
- (_id, attrs) => attrs.type === import_types45.NodeType.ServiceNode && attrs.name === serviceName
11506
+ (_id, attrs) => attrs.type === import_types46.NodeType.ServiceNode && attrs.name === serviceName
11420
11507
  );
11421
11508
  }
11422
11509
  async function bootstrapProject(entry, connectors = [], neatHome3) {