@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/neatd.js CHANGED
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  reconcileDaemonRecordSync,
4
4
  startDaemon
5
- } from "./chunk-2AJHEZIB.js";
5
+ } from "./chunk-O4PCNQFG.js";
6
6
  import {
7
7
  listProjects,
8
8
  registryPath
9
- } from "./chunk-4M2YMGUM.js";
9
+ } from "./chunk-BI3XKGVG.js";
10
10
  import {
11
11
  BindAuthorityError,
12
12
  __require
package/dist/server.cjs CHANGED
@@ -1445,7 +1445,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
1445
1445
  for (const id of walk3.path) {
1446
1446
  const owner = resolveOwningService(graph, id);
1447
1447
  if (!owner) continue;
1448
- const { id: serviceId4, svc } = owner;
1448
+ const { id: serviceId5, svc } = owner;
1449
1449
  const deps = svc.dependencies ?? {};
1450
1450
  for (const pair of candidatePairs) {
1451
1451
  const declared = deps[pair.driver];
@@ -1458,7 +1458,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
1458
1458
  );
1459
1459
  if (!result.compatible) {
1460
1460
  return {
1461
- rootCauseNode: serviceId4,
1461
+ rootCauseNode: serviceId5,
1462
1462
  rootCauseReason: result.reason ?? "incompatible driver",
1463
1463
  ...result.minDriverVersion ? {
1464
1464
  fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
@@ -1473,7 +1473,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1473
1473
  for (const id of walk3.path) {
1474
1474
  const owner = resolveOwningService(graph, id);
1475
1475
  if (!owner) continue;
1476
- const { id: serviceId4, svc } = owner;
1476
+ const { id: serviceId5, svc } = owner;
1477
1477
  const deps = svc.dependencies ?? {};
1478
1478
  const serviceNodeEngine = svc.nodeEngine;
1479
1479
  for (const constraint of nodeEngineConstraints()) {
@@ -1482,7 +1482,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1482
1482
  const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
1483
1483
  if (!result.compatible && result.reason) {
1484
1484
  return {
1485
- rootCauseNode: serviceId4,
1485
+ rootCauseNode: serviceId5,
1486
1486
  rootCauseReason: result.reason,
1487
1487
  ...result.requiredNodeVersion ? {
1488
1488
  fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
@@ -1497,7 +1497,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
1497
1497
  const result = checkPackageConflict(conflict, declared, requiredDeclared);
1498
1498
  if (!result.compatible && result.reason) {
1499
1499
  return {
1500
- rootCauseNode: serviceId4,
1500
+ rootCauseNode: serviceId5,
1501
1501
  rootCauseReason: result.reason,
1502
1502
  fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
1503
1503
  };
@@ -1588,9 +1588,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
1588
1588
  function isFailingCallEdge(e) {
1589
1589
  return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
1590
1590
  }
1591
- function callSourcesForService(graph, serviceId4) {
1592
- const ids = [serviceId4];
1593
- for (const edgeId of graph.outboundEdges(serviceId4)) {
1591
+ function callSourcesForService(graph, serviceId5) {
1592
+ const ids = [serviceId5];
1593
+ for (const edgeId of graph.outboundEdges(serviceId5)) {
1594
1594
  const e = graph.getEdgeAttributes(edgeId);
1595
1595
  if (e.type !== import_types.EdgeType.CONTAINS) continue;
1596
1596
  const tgt = graph.getNodeAttributes(e.target);
@@ -1607,9 +1607,9 @@ function failingCallDominates(e, id, curEdge, curId) {
1607
1607
  }
1608
1608
  return id < curId;
1609
1609
  }
1610
- function dominantFailingCall(graph, serviceId4, visited) {
1610
+ function dominantFailingCall(graph, serviceId5, visited) {
1611
1611
  let best = null;
1612
- for (const src of callSourcesForService(graph, serviceId4)) {
1612
+ for (const src of callSourcesForService(graph, serviceId5)) {
1613
1613
  for (const edgeId of graph.outboundEdges(src)) {
1614
1614
  const e = graph.getEdgeAttributes(edgeId);
1615
1615
  if (!isFailingCallEdge(e)) continue;
@@ -1820,14 +1820,23 @@ function getObservedDependencies(graph, nodeId) {
1820
1820
  function bucketKey(source, target, type) {
1821
1821
  return `${type}|${source}|${target}`;
1822
1822
  }
1823
+ function bucketSourceFor(graph, edge) {
1824
+ if (edge.type !== import_types2.EdgeType.CONNECTS_TO) return edge.source;
1825
+ const parsed = (0, import_types2.parseFileId)(edge.source);
1826
+ if (!parsed || !graph.hasNode(edge.target)) return edge.source;
1827
+ const target = graph.getNodeAttributes(edge.target);
1828
+ if (target.type !== import_types2.NodeType.DatabaseNode) return edge.source;
1829
+ return (0, import_types2.serviceId)(parsed.service);
1830
+ }
1823
1831
  function bucketEdges(graph) {
1824
1832
  const buckets = /* @__PURE__ */ new Map();
1825
1833
  graph.forEachEdge((id, attrs) => {
1826
1834
  const e = attrs;
1827
1835
  const parsed = (0, import_types2.parseEdgeId)(id);
1828
1836
  const provenance = parsed?.provenance ?? e.provenance;
1829
- const key = bucketKey(e.source, e.target, e.type);
1830
- const cur = buckets.get(key) ?? { source: e.source, target: e.target, type: e.type };
1837
+ const source = bucketSourceFor(graph, e);
1838
+ const key = bucketKey(source, e.target, e.type);
1839
+ const cur = buckets.get(key) ?? { source, target: e.target, type: e.type };
1831
1840
  switch (provenance) {
1832
1841
  case import_types2.Provenance.EXTRACTED:
1833
1842
  cur.extracted = e;
@@ -3116,6 +3125,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
3116
3125
  graph.addNode(id, node);
3117
3126
  return id;
3118
3127
  }
3128
+ function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
3129
+ if (!graph.hasNode(serviceNodeId)) return null;
3130
+ const sources = [serviceNodeId];
3131
+ for (const edgeId of graph.outboundEdges(serviceNodeId)) {
3132
+ const e = graph.getEdgeAttributes(edgeId);
3133
+ if (e.type === import_types4.EdgeType.CONTAINS) sources.push(e.target);
3134
+ }
3135
+ const matches = /* @__PURE__ */ new Set();
3136
+ for (const src of sources) {
3137
+ if (!graph.hasNode(src)) continue;
3138
+ for (const edgeId of graph.outboundEdges(src)) {
3139
+ const edge = graph.getEdgeAttributes(edgeId);
3140
+ if (edge.type !== import_types4.EdgeType.CONNECTS_TO || edge.provenance !== import_types4.Provenance.EXTRACTED) continue;
3141
+ if (!graph.hasNode(edge.target)) continue;
3142
+ const target = graph.getNodeAttributes(edge.target);
3143
+ if (target.type !== import_types4.NodeType.DatabaseNode || target.engine !== engine) continue;
3144
+ matches.add(edge.target);
3145
+ }
3146
+ }
3147
+ return matches.size === 1 ? [...matches][0] : null;
3148
+ }
3119
3149
  function ensureFrontierNode(graph, host, ts) {
3120
3150
  const id = frontierIdFor(host);
3121
3151
  if (graph.hasNode(id)) {
@@ -3136,6 +3166,7 @@ function ensureFrontierNode(graph, host, ts) {
3136
3166
  }
3137
3167
  function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence) {
3138
3168
  if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
3169
+ const grain = source.startsWith("file:") ? "file" : "service";
3139
3170
  const id = makeObservedEdgeId(type, source, target);
3140
3171
  if (graph.hasEdge(id)) {
3141
3172
  const existing = graph.getEdgeAttributes(id);
@@ -3152,7 +3183,9 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
3152
3183
  lastObserved: ts,
3153
3184
  callCount: newSpanCount,
3154
3185
  signal: newSignal,
3155
- confidence: (0, import_types4.confidenceForObservedSignal)(newSignal)
3186
+ confidence: (0, import_types4.confidenceForObservedSignal)(newSignal),
3187
+ grain
3188
+ // backfills legacy edges that predate ADR-142
3156
3189
  };
3157
3190
  graph.replaceEdgeAttributes(id, updated);
3158
3191
  return { edge: updated, created: false };
@@ -3172,6 +3205,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
3172
3205
  lastObserved: ts,
3173
3206
  callCount: 1,
3174
3207
  signal,
3208
+ grain,
3175
3209
  // Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).
3176
3210
  // Only set when code.filepath was present on the span — never fabricated.
3177
3211
  ...evidence ? { evidence } : {}
@@ -3349,13 +3383,18 @@ async function handleSpan(ctx, span) {
3349
3383
  ensureDatabaseNode(ctx.graph, host, span.dbSystem);
3350
3384
  targetId = (0, import_types4.databaseId)(host);
3351
3385
  } else {
3352
- const localName = span.dbName ?? span.dbSystem;
3353
- targetId = ensureLocalDatabaseNode(
3354
- ctx.graph,
3355
- span.service,
3356
- localName,
3357
- span.dbSystem
3358
- );
3386
+ const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
3387
+ if (declared) {
3388
+ targetId = declared;
3389
+ } else {
3390
+ const localName = span.dbName ?? span.dbSystem;
3391
+ targetId = ensureLocalDatabaseNode(
3392
+ ctx.graph,
3393
+ span.service,
3394
+ localName,
3395
+ span.dbSystem
3396
+ );
3397
+ }
3359
3398
  }
3360
3399
  const result = upsertObservedEdge(
3361
3400
  ctx.graph,
@@ -3539,29 +3578,29 @@ function promoteFrontierNodes(graph, opts = {}) {
3539
3578
  toPromote.push({ frontierId: id, serviceId: target });
3540
3579
  });
3541
3580
  let promoted = 0;
3542
- for (const { frontierId: frontierId2, serviceId: serviceId4 } of toPromote) {
3581
+ for (const { frontierId: frontierId2, serviceId: serviceId5 } of toPromote) {
3543
3582
  if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
3544
3583
  const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
3545
3584
  if (!gate.allowed) {
3546
3585
  continue;
3547
3586
  }
3548
3587
  }
3549
- rewireFrontierEdges(graph, frontierId2, serviceId4);
3588
+ rewireFrontierEdges(graph, frontierId2, serviceId5);
3550
3589
  graph.dropNode(frontierId2);
3551
3590
  promoted++;
3552
3591
  }
3553
3592
  return promoted;
3554
3593
  }
3555
- function rewireFrontierEdges(graph, frontierId2, serviceId4) {
3594
+ function rewireFrontierEdges(graph, frontierId2, serviceId5) {
3556
3595
  const inbound = [...graph.inboundEdges(frontierId2)];
3557
3596
  const outbound = [...graph.outboundEdges(frontierId2)];
3558
3597
  for (const edgeId of inbound) {
3559
3598
  const edge = graph.getEdgeAttributes(edgeId);
3560
- rebuildEdge(graph, edge, edge.source, serviceId4, edgeId);
3599
+ rebuildEdge(graph, edge, edge.source, serviceId5, edgeId);
3561
3600
  }
3562
3601
  for (const edgeId of outbound) {
3563
3602
  const edge = graph.getEdgeAttributes(edgeId);
3564
- rebuildEdge(graph, edge, serviceId4, edge.target, edgeId);
3603
+ rebuildEdge(graph, edge, serviceId5, edge.target, edgeId);
3565
3604
  }
3566
3605
  }
3567
3606
  function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
@@ -4357,9 +4396,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
4357
4396
  "StatefulSet",
4358
4397
  "DaemonSet"
4359
4398
  ]);
4360
- function addAliases(graph, serviceId4, candidates) {
4361
- if (!graph.hasNode(serviceId4)) return;
4362
- const node = graph.getNodeAttributes(serviceId4);
4399
+ function addAliases(graph, serviceId5, candidates) {
4400
+ if (!graph.hasNode(serviceId5)) return;
4401
+ const node = graph.getNodeAttributes(serviceId5);
4363
4402
  if (node.type !== import_types7.NodeType.ServiceNode) return;
4364
4403
  const set = new Set(node.aliases ?? []);
4365
4404
  for (const c of candidates) {
@@ -4369,7 +4408,7 @@ function addAliases(graph, serviceId4, candidates) {
4369
4408
  }
4370
4409
  if (set.size === 0) return;
4371
4410
  const updated = { ...node, aliases: [...set].sort() };
4372
- graph.replaceNodeAttributes(serviceId4, updated);
4411
+ graph.replaceNodeAttributes(serviceId5, updated);
4373
4412
  }
4374
4413
  function indexServicesByName(services) {
4375
4414
  const map = /* @__PURE__ */ new Map();
@@ -4402,12 +4441,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
4402
4441
  }
4403
4442
  if (!compose?.services) return;
4404
4443
  for (const [composeName, svc] of Object.entries(compose.services)) {
4405
- const serviceId4 = serviceIndex.get(composeName);
4406
- if (!serviceId4) continue;
4444
+ const serviceId5 = serviceIndex.get(composeName);
4445
+ if (!serviceId5) continue;
4407
4446
  const aliases = /* @__PURE__ */ new Set([composeName]);
4408
4447
  if (svc.container_name) aliases.add(svc.container_name);
4409
4448
  if (svc.hostname) aliases.add(svc.hostname);
4410
- addAliases(graph, serviceId4, aliases);
4449
+ addAliases(graph, serviceId5, aliases);
4411
4450
  }
4412
4451
  }
4413
4452
  var LABEL_KEYS = /* @__PURE__ */ new Set([
@@ -5057,6 +5096,29 @@ async function readIfExists(filePath) {
5057
5096
  return null;
5058
5097
  }
5059
5098
  }
5099
+ async function resolveEnvVar(serviceDir, name) {
5100
+ const entries = await import_node_fs14.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
5101
+ const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
5102
+ const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
5103
+ envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
5104
+ for (const fileName of envNames) {
5105
+ const content = await readIfExists(import_node_path16.default.join(serviceDir, fileName));
5106
+ if (!content) continue;
5107
+ for (const line of content.split("\n")) {
5108
+ const trimmed = line.trim();
5109
+ if (!trimmed || trimmed.startsWith("#")) continue;
5110
+ const eq = trimmed.indexOf("=");
5111
+ if (eq < 0) continue;
5112
+ if (trimmed.slice(0, eq).trim() !== name) continue;
5113
+ let value = trimmed.slice(eq + 1).trim();
5114
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
5115
+ value = value.slice(1, -1);
5116
+ }
5117
+ return value || null;
5118
+ }
5119
+ }
5120
+ return null;
5121
+ }
5060
5122
  async function findFirst(serviceDir, candidates) {
5061
5123
  for (const rel of candidates) {
5062
5124
  const abs = import_node_path16.default.join(serviceDir, rel);
@@ -5154,6 +5216,14 @@ async function parse3(serviceDir) {
5154
5216
  const config = parseConnectionString(urlMatch[1]);
5155
5217
  if (config) return [{ ...config, sourceFile: schemaPath }];
5156
5218
  }
5219
+ const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
5220
+ if (envMatch) {
5221
+ const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
5222
+ if (resolved) {
5223
+ const config = parseConnectionString(resolved);
5224
+ if (config) return [{ ...config, sourceFile: schemaPath }];
5225
+ }
5226
+ }
5157
5227
  return [
5158
5228
  {
5159
5229
  host: `${engine}-prisma`,
@@ -5197,6 +5267,15 @@ async function parse4(serviceDir) {
5197
5267
  const config = parseConnectionString(urlMatch[1]);
5198
5268
  if (config) return [{ ...config, sourceFile: filePath }];
5199
5269
  }
5270
+ const urlEnvMatch = content.match(
5271
+ /(?:url|connectionString)\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
5272
+ );
5273
+ if (urlEnvMatch) {
5274
+ const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
5275
+ const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
5276
+ const config = resolved ? parseConnectionString(resolved) : null;
5277
+ if (config) return [{ ...config, sourceFile: filePath }];
5278
+ }
5200
5279
  const hostMatch = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/);
5201
5280
  if (hostMatch) {
5202
5281
  const portMatch = content.match(/port\s*:\s*(\d+)/);
@@ -5250,6 +5329,15 @@ async function parse5(serviceDir) {
5250
5329
  const config = parseConnectionString(urlMatch[1]);
5251
5330
  if (config) return [{ ...config, sourceFile: filePath }];
5252
5331
  }
5332
+ const urlEnvMatch = content.match(
5333
+ /connection\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
5334
+ );
5335
+ if (urlEnvMatch) {
5336
+ const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
5337
+ const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
5338
+ const config = resolved ? parseConnectionString(resolved) : null;
5339
+ if (config) return [{ ...config, sourceFile: filePath }];
5340
+ }
5253
5341
  const host = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/)?.[1];
5254
5342
  if (host) {
5255
5343
  const port = content.match(/port\s*:\s*(\d+)/)?.[1];
@@ -6798,6 +6886,16 @@ function constructorMatchesImport(name, ctx) {
6798
6886
  if (name === "createClient") return ctx.hasSupabaseJs;
6799
6887
  return ctx.hasSupabaseSsr;
6800
6888
  }
6889
+ var SUPABASE_CLIENT_ASSIGN_RE = /(?:const|let|var)\s+(\w+)\s*=\s*(?:await\s+)?(createClient|createServerClient|createBrowserClient)\s*\(/g;
6890
+ function supabaseClientVars(content, ctx) {
6891
+ const vars = /* @__PURE__ */ new Set();
6892
+ SUPABASE_CLIENT_ASSIGN_RE.lastIndex = 0;
6893
+ let m;
6894
+ while ((m = SUPABASE_CLIENT_ASSIGN_RE.exec(content)) !== null) {
6895
+ if (constructorMatchesImport(m[2], ctx)) vars.add(m[1]);
6896
+ }
6897
+ return vars;
6898
+ }
6801
6899
  function supabaseEndpointsFromFile(file, serviceDir) {
6802
6900
  const ctx = readImports2(file.content);
6803
6901
  if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
@@ -6830,6 +6928,34 @@ function supabaseEndpointsFromFile(file, serviceDir) {
6830
6928
  }
6831
6929
  });
6832
6930
  }
6931
+ const clientVars = supabaseClientVars(file.content, ctx);
6932
+ for (const clientVar of clientVars) {
6933
+ const accessRe = new RegExp(
6934
+ `\\b${clientVar}\\s*\\.\\s*(from|rpc)\\s*\\(\\s*['"\`]([\\w.-]+)['"\`]`,
6935
+ "g"
6936
+ );
6937
+ let am;
6938
+ while ((am = accessRe.exec(file.content)) !== null) {
6939
+ const kind = am[1] === "rpc" ? "supabase-rpc" : "supabase-table";
6940
+ const resource = am[2];
6941
+ const key = `${kind}/${resource}`;
6942
+ if (seen.has(key)) continue;
6943
+ seen.add(key);
6944
+ const line = lineOf(file.content, am[0]);
6945
+ out.push({
6946
+ infraId: (0, import_types20.infraId)(kind, resource),
6947
+ name: resource,
6948
+ kind,
6949
+ edgeType: "CALLS",
6950
+ confidenceKind: "verified-call-site",
6951
+ evidence: {
6952
+ file: import_node_path32.default.relative(serviceDir, file.path),
6953
+ line,
6954
+ snippet: snippet(file.content, line)
6955
+ }
6956
+ });
6957
+ }
6958
+ }
6833
6959
  return out;
6834
6960
  }
6835
6961