@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/{chunk-4M2YMGUM.js → chunk-BI3XKGVG.js} +162 -34
- package/dist/chunk-BI3XKGVG.js.map +1 -0
- package/dist/{chunk-2AJHEZIB.js → chunk-O4PCNQFG.js} +39 -12
- package/dist/chunk-O4PCNQFG.js.map +1 -0
- package/dist/cli.cjs +193 -62
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +208 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +208 -55
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +158 -32
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-2AJHEZIB.js.map +0 -1
- package/dist/chunk-4M2YMGUM.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeSpanToProject,
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-O4PCNQFG.js";
|
|
5
5
|
import {
|
|
6
6
|
ProjectNameCollisionError,
|
|
7
7
|
addProject,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
thresholdForEdgeType,
|
|
38
38
|
touchLastSeen,
|
|
39
39
|
writeAtomically
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-BI3XKGVG.js";
|
|
41
41
|
import {
|
|
42
42
|
startOtelGrpcReceiver
|
|
43
43
|
} from "./chunk-I72HTUOG.js";
|
package/dist/neatd.cjs
CHANGED
|
@@ -1240,7 +1240,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1240
1240
|
for (const id of walk3.path) {
|
|
1241
1241
|
const owner = resolveOwningService(graph, id);
|
|
1242
1242
|
if (!owner) continue;
|
|
1243
|
-
const { id:
|
|
1243
|
+
const { id: serviceId6, svc } = owner;
|
|
1244
1244
|
const deps = svc.dependencies ?? {};
|
|
1245
1245
|
for (const pair of candidatePairs) {
|
|
1246
1246
|
const declared = deps[pair.driver];
|
|
@@ -1253,7 +1253,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1253
1253
|
);
|
|
1254
1254
|
if (!result.compatible) {
|
|
1255
1255
|
return {
|
|
1256
|
-
rootCauseNode:
|
|
1256
|
+
rootCauseNode: serviceId6,
|
|
1257
1257
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1258
1258
|
...result.minDriverVersion ? {
|
|
1259
1259
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1268,7 +1268,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1268
1268
|
for (const id of walk3.path) {
|
|
1269
1269
|
const owner = resolveOwningService(graph, id);
|
|
1270
1270
|
if (!owner) continue;
|
|
1271
|
-
const { id:
|
|
1271
|
+
const { id: serviceId6, svc } = owner;
|
|
1272
1272
|
const deps = svc.dependencies ?? {};
|
|
1273
1273
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1274
1274
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1277,7 +1277,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1277
1277
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1278
1278
|
if (!result.compatible && result.reason) {
|
|
1279
1279
|
return {
|
|
1280
|
-
rootCauseNode:
|
|
1280
|
+
rootCauseNode: serviceId6,
|
|
1281
1281
|
rootCauseReason: result.reason,
|
|
1282
1282
|
...result.requiredNodeVersion ? {
|
|
1283
1283
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1292,7 +1292,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1292
1292
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1293
1293
|
if (!result.compatible && result.reason) {
|
|
1294
1294
|
return {
|
|
1295
|
-
rootCauseNode:
|
|
1295
|
+
rootCauseNode: serviceId6,
|
|
1296
1296
|
rootCauseReason: result.reason,
|
|
1297
1297
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1298
1298
|
};
|
|
@@ -1383,9 +1383,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1383
1383
|
function isFailingCallEdge(e) {
|
|
1384
1384
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1385
1385
|
}
|
|
1386
|
-
function callSourcesForService(graph,
|
|
1387
|
-
const ids = [
|
|
1388
|
-
for (const edgeId of graph.outboundEdges(
|
|
1386
|
+
function callSourcesForService(graph, serviceId6) {
|
|
1387
|
+
const ids = [serviceId6];
|
|
1388
|
+
for (const edgeId of graph.outboundEdges(serviceId6)) {
|
|
1389
1389
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1390
1390
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1391
1391
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1402,9 +1402,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1402
1402
|
}
|
|
1403
1403
|
return id < curId;
|
|
1404
1404
|
}
|
|
1405
|
-
function dominantFailingCall(graph,
|
|
1405
|
+
function dominantFailingCall(graph, serviceId6, visited) {
|
|
1406
1406
|
let best = null;
|
|
1407
|
-
for (const src of callSourcesForService(graph,
|
|
1407
|
+
for (const src of callSourcesForService(graph, serviceId6)) {
|
|
1408
1408
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1409
1409
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1410
1410
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -2586,6 +2586,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
|
2586
2586
|
graph.addNode(id, node);
|
|
2587
2587
|
return id;
|
|
2588
2588
|
}
|
|
2589
|
+
function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
|
|
2590
|
+
if (!graph.hasNode(serviceNodeId)) return null;
|
|
2591
|
+
const sources = [serviceNodeId];
|
|
2592
|
+
for (const edgeId of graph.outboundEdges(serviceNodeId)) {
|
|
2593
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
2594
|
+
if (e.type === import_types3.EdgeType.CONTAINS) sources.push(e.target);
|
|
2595
|
+
}
|
|
2596
|
+
const matches = /* @__PURE__ */ new Set();
|
|
2597
|
+
for (const src of sources) {
|
|
2598
|
+
if (!graph.hasNode(src)) continue;
|
|
2599
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
2600
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
2601
|
+
if (edge.type !== import_types3.EdgeType.CONNECTS_TO || edge.provenance !== import_types3.Provenance.EXTRACTED) continue;
|
|
2602
|
+
if (!graph.hasNode(edge.target)) continue;
|
|
2603
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
2604
|
+
if (target.type !== import_types3.NodeType.DatabaseNode || target.engine !== engine) continue;
|
|
2605
|
+
matches.add(edge.target);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
return matches.size === 1 ? [...matches][0] : null;
|
|
2609
|
+
}
|
|
2589
2610
|
function ensureFrontierNode(graph, host, ts) {
|
|
2590
2611
|
const id = frontierIdFor(host);
|
|
2591
2612
|
if (graph.hasNode(id)) {
|
|
@@ -2606,6 +2627,7 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
2606
2627
|
}
|
|
2607
2628
|
function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence) {
|
|
2608
2629
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
2630
|
+
const grain = source.startsWith("file:") ? "file" : "service";
|
|
2609
2631
|
const id = makeObservedEdgeId(type, source, target);
|
|
2610
2632
|
if (graph.hasEdge(id)) {
|
|
2611
2633
|
const existing = graph.getEdgeAttributes(id);
|
|
@@ -2622,7 +2644,9 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
2622
2644
|
lastObserved: ts,
|
|
2623
2645
|
callCount: newSpanCount,
|
|
2624
2646
|
signal: newSignal,
|
|
2625
|
-
confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
|
|
2647
|
+
confidence: (0, import_types3.confidenceForObservedSignal)(newSignal),
|
|
2648
|
+
grain
|
|
2649
|
+
// backfills legacy edges that predate ADR-142
|
|
2626
2650
|
};
|
|
2627
2651
|
graph.replaceEdgeAttributes(id, updated);
|
|
2628
2652
|
return { edge: updated, created: false };
|
|
@@ -2642,6 +2666,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
2642
2666
|
lastObserved: ts,
|
|
2643
2667
|
callCount: 1,
|
|
2644
2668
|
signal,
|
|
2669
|
+
grain,
|
|
2645
2670
|
// Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).
|
|
2646
2671
|
// Only set when code.filepath was present on the span — never fabricated.
|
|
2647
2672
|
...evidence ? { evidence } : {}
|
|
@@ -2844,13 +2869,18 @@ async function handleSpan(ctx, span) {
|
|
|
2844
2869
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
2845
2870
|
targetId = (0, import_types3.databaseId)(host);
|
|
2846
2871
|
} else {
|
|
2847
|
-
const
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
localName
|
|
2852
|
-
|
|
2853
|
-
|
|
2872
|
+
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
2873
|
+
if (declared) {
|
|
2874
|
+
targetId = declared;
|
|
2875
|
+
} else {
|
|
2876
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
2877
|
+
targetId = ensureLocalDatabaseNode(
|
|
2878
|
+
ctx.graph,
|
|
2879
|
+
span.service,
|
|
2880
|
+
localName,
|
|
2881
|
+
span.dbSystem
|
|
2882
|
+
);
|
|
2883
|
+
}
|
|
2854
2884
|
}
|
|
2855
2885
|
const result = upsertObservedEdge(
|
|
2856
2886
|
ctx.graph,
|
|
@@ -3034,29 +3064,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3034
3064
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
3035
3065
|
});
|
|
3036
3066
|
let promoted = 0;
|
|
3037
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
3067
|
+
for (const { frontierId: frontierId2, serviceId: serviceId6 } of toPromote) {
|
|
3038
3068
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
3039
3069
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
3040
3070
|
if (!gate.allowed) {
|
|
3041
3071
|
continue;
|
|
3042
3072
|
}
|
|
3043
3073
|
}
|
|
3044
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
3074
|
+
rewireFrontierEdges(graph, frontierId2, serviceId6);
|
|
3045
3075
|
graph.dropNode(frontierId2);
|
|
3046
3076
|
promoted++;
|
|
3047
3077
|
}
|
|
3048
3078
|
return promoted;
|
|
3049
3079
|
}
|
|
3050
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
3080
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId6) {
|
|
3051
3081
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
3052
3082
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
3053
3083
|
for (const edgeId of inbound) {
|
|
3054
3084
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3055
|
-
rebuildEdge(graph, edge, edge.source,
|
|
3085
|
+
rebuildEdge(graph, edge, edge.source, serviceId6, edgeId);
|
|
3056
3086
|
}
|
|
3057
3087
|
for (const edgeId of outbound) {
|
|
3058
3088
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3059
|
-
rebuildEdge(graph, edge,
|
|
3089
|
+
rebuildEdge(graph, edge, serviceId6, edge.target, edgeId);
|
|
3060
3090
|
}
|
|
3061
3091
|
}
|
|
3062
3092
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -3849,9 +3879,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
3849
3879
|
"StatefulSet",
|
|
3850
3880
|
"DaemonSet"
|
|
3851
3881
|
]);
|
|
3852
|
-
function addAliases(graph,
|
|
3853
|
-
if (!graph.hasNode(
|
|
3854
|
-
const node = graph.getNodeAttributes(
|
|
3882
|
+
function addAliases(graph, serviceId6, candidates) {
|
|
3883
|
+
if (!graph.hasNode(serviceId6)) return;
|
|
3884
|
+
const node = graph.getNodeAttributes(serviceId6);
|
|
3855
3885
|
if (node.type !== import_types6.NodeType.ServiceNode) return;
|
|
3856
3886
|
const set = new Set(node.aliases ?? []);
|
|
3857
3887
|
for (const c of candidates) {
|
|
@@ -3861,7 +3891,7 @@ function addAliases(graph, serviceId5, candidates) {
|
|
|
3861
3891
|
}
|
|
3862
3892
|
if (set.size === 0) return;
|
|
3863
3893
|
const updated = { ...node, aliases: [...set].sort() };
|
|
3864
|
-
graph.replaceNodeAttributes(
|
|
3894
|
+
graph.replaceNodeAttributes(serviceId6, updated);
|
|
3865
3895
|
}
|
|
3866
3896
|
function indexServicesByName(services) {
|
|
3867
3897
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3894,12 +3924,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
3894
3924
|
}
|
|
3895
3925
|
if (!compose?.services) return;
|
|
3896
3926
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3897
|
-
const
|
|
3898
|
-
if (!
|
|
3927
|
+
const serviceId6 = serviceIndex.get(composeName);
|
|
3928
|
+
if (!serviceId6) continue;
|
|
3899
3929
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
3900
3930
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
3901
3931
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
3902
|
-
addAliases(graph,
|
|
3932
|
+
addAliases(graph, serviceId6, aliases);
|
|
3903
3933
|
}
|
|
3904
3934
|
}
|
|
3905
3935
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -4549,6 +4579,29 @@ async function readIfExists(filePath) {
|
|
|
4549
4579
|
return null;
|
|
4550
4580
|
}
|
|
4551
4581
|
}
|
|
4582
|
+
async function resolveEnvVar(serviceDir, name) {
|
|
4583
|
+
const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
4584
|
+
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
4585
|
+
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
4586
|
+
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
4587
|
+
for (const fileName of envNames) {
|
|
4588
|
+
const content = await readIfExists(import_node_path14.default.join(serviceDir, fileName));
|
|
4589
|
+
if (!content) continue;
|
|
4590
|
+
for (const line of content.split("\n")) {
|
|
4591
|
+
const trimmed = line.trim();
|
|
4592
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
4593
|
+
const eq = trimmed.indexOf("=");
|
|
4594
|
+
if (eq < 0) continue;
|
|
4595
|
+
if (trimmed.slice(0, eq).trim() !== name) continue;
|
|
4596
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
4597
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
4598
|
+
value = value.slice(1, -1);
|
|
4599
|
+
}
|
|
4600
|
+
return value || null;
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
return null;
|
|
4604
|
+
}
|
|
4552
4605
|
async function findFirst(serviceDir, candidates) {
|
|
4553
4606
|
for (const rel of candidates) {
|
|
4554
4607
|
const abs = import_node_path14.default.join(serviceDir, rel);
|
|
@@ -4646,6 +4699,14 @@ async function parse3(serviceDir) {
|
|
|
4646
4699
|
const config = parseConnectionString(urlMatch[1]);
|
|
4647
4700
|
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4648
4701
|
}
|
|
4702
|
+
const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
|
|
4703
|
+
if (envMatch) {
|
|
4704
|
+
const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
|
|
4705
|
+
if (resolved) {
|
|
4706
|
+
const config = parseConnectionString(resolved);
|
|
4707
|
+
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4649
4710
|
return [
|
|
4650
4711
|
{
|
|
4651
4712
|
host: `${engine}-prisma`,
|
|
@@ -4689,6 +4750,15 @@ async function parse4(serviceDir) {
|
|
|
4689
4750
|
const config = parseConnectionString(urlMatch[1]);
|
|
4690
4751
|
if (config) return [{ ...config, sourceFile: filePath }];
|
|
4691
4752
|
}
|
|
4753
|
+
const urlEnvMatch = content.match(
|
|
4754
|
+
/(?:url|connectionString)\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
|
|
4755
|
+
);
|
|
4756
|
+
if (urlEnvMatch) {
|
|
4757
|
+
const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
|
|
4758
|
+
const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
|
|
4759
|
+
const config = resolved ? parseConnectionString(resolved) : null;
|
|
4760
|
+
if (config) return [{ ...config, sourceFile: filePath }];
|
|
4761
|
+
}
|
|
4692
4762
|
const hostMatch = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
4693
4763
|
if (hostMatch) {
|
|
4694
4764
|
const portMatch = content.match(/port\s*:\s*(\d+)/);
|
|
@@ -4742,6 +4812,15 @@ async function parse5(serviceDir) {
|
|
|
4742
4812
|
const config = parseConnectionString(urlMatch[1]);
|
|
4743
4813
|
if (config) return [{ ...config, sourceFile: filePath }];
|
|
4744
4814
|
}
|
|
4815
|
+
const urlEnvMatch = content.match(
|
|
4816
|
+
/connection\s*:\s*process\.env(?:\.([A-Za-z_$][\w$]*)|\[\s*['"]([^'"]+)['"]\s*\])/
|
|
4817
|
+
);
|
|
4818
|
+
if (urlEnvMatch) {
|
|
4819
|
+
const varName = urlEnvMatch[1] ?? urlEnvMatch[2];
|
|
4820
|
+
const resolved = varName ? await resolveEnvVar(serviceDir, varName) : null;
|
|
4821
|
+
const config = resolved ? parseConnectionString(resolved) : null;
|
|
4822
|
+
if (config) return [{ ...config, sourceFile: filePath }];
|
|
4823
|
+
}
|
|
4745
4824
|
const host = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/)?.[1];
|
|
4746
4825
|
if (host) {
|
|
4747
4826
|
const port = content.match(/port\s*:\s*(\d+)/)?.[1];
|
|
@@ -6290,6 +6369,16 @@ function constructorMatchesImport(name, ctx) {
|
|
|
6290
6369
|
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
6291
6370
|
return ctx.hasSupabaseSsr;
|
|
6292
6371
|
}
|
|
6372
|
+
var SUPABASE_CLIENT_ASSIGN_RE = /(?:const|let|var)\s+(\w+)\s*=\s*(?:await\s+)?(createClient|createServerClient|createBrowserClient)\s*\(/g;
|
|
6373
|
+
function supabaseClientVars(content, ctx) {
|
|
6374
|
+
const vars = /* @__PURE__ */ new Set();
|
|
6375
|
+
SUPABASE_CLIENT_ASSIGN_RE.lastIndex = 0;
|
|
6376
|
+
let m;
|
|
6377
|
+
while ((m = SUPABASE_CLIENT_ASSIGN_RE.exec(content)) !== null) {
|
|
6378
|
+
if (constructorMatchesImport(m[2], ctx)) vars.add(m[1]);
|
|
6379
|
+
}
|
|
6380
|
+
return vars;
|
|
6381
|
+
}
|
|
6293
6382
|
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
6294
6383
|
const ctx = readImports2(file.content);
|
|
6295
6384
|
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
@@ -6322,6 +6411,34 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6322
6411
|
}
|
|
6323
6412
|
});
|
|
6324
6413
|
}
|
|
6414
|
+
const clientVars = supabaseClientVars(file.content, ctx);
|
|
6415
|
+
for (const clientVar of clientVars) {
|
|
6416
|
+
const accessRe = new RegExp(
|
|
6417
|
+
`\\b${clientVar}\\s*\\.\\s*(from|rpc)\\s*\\(\\s*['"\`]([\\w.-]+)['"\`]`,
|
|
6418
|
+
"g"
|
|
6419
|
+
);
|
|
6420
|
+
let am;
|
|
6421
|
+
while ((am = accessRe.exec(file.content)) !== null) {
|
|
6422
|
+
const kind = am[1] === "rpc" ? "supabase-rpc" : "supabase-table";
|
|
6423
|
+
const resource = am[2];
|
|
6424
|
+
const key = `${kind}/${resource}`;
|
|
6425
|
+
if (seen.has(key)) continue;
|
|
6426
|
+
seen.add(key);
|
|
6427
|
+
const line = lineOf(file.content, am[0]);
|
|
6428
|
+
out.push({
|
|
6429
|
+
infraId: (0, import_types19.infraId)(kind, resource),
|
|
6430
|
+
name: resource,
|
|
6431
|
+
kind,
|
|
6432
|
+
edgeType: "CALLS",
|
|
6433
|
+
confidenceKind: "verified-call-site",
|
|
6434
|
+
evidence: {
|
|
6435
|
+
file: import_node_path30.default.relative(serviceDir, file.path),
|
|
6436
|
+
line,
|
|
6437
|
+
snippet: snippet(file.content, line)
|
|
6438
|
+
}
|
|
6439
|
+
});
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6325
6442
|
return out;
|
|
6326
6443
|
}
|
|
6327
6444
|
|
|
@@ -7882,14 +7999,23 @@ var import_types31 = require("@neat.is/types");
|
|
|
7882
7999
|
function bucketKey(source, target, type) {
|
|
7883
8000
|
return `${type}|${source}|${target}`;
|
|
7884
8001
|
}
|
|
8002
|
+
function bucketSourceFor(graph, edge) {
|
|
8003
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
8004
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
8005
|
+
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
8006
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
8007
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
8008
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
8009
|
+
}
|
|
7885
8010
|
function bucketEdges(graph) {
|
|
7886
8011
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7887
8012
|
graph.forEachEdge((id, attrs) => {
|
|
7888
8013
|
const e = attrs;
|
|
7889
8014
|
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7890
8015
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7891
|
-
const
|
|
7892
|
-
const
|
|
8016
|
+
const source = bucketSourceFor(graph, e);
|
|
8017
|
+
const key = bucketKey(source, e.target, e.type);
|
|
8018
|
+
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7893
8019
|
switch (provenance) {
|
|
7894
8020
|
case import_types31.Provenance.EXTRACTED:
|
|
7895
8021
|
cur.extracted = e;
|
|
@@ -9492,7 +9618,30 @@ init_otel();
|
|
|
9492
9618
|
|
|
9493
9619
|
// src/connectors/index.ts
|
|
9494
9620
|
init_cjs_shims();
|
|
9621
|
+
var import_types34 = require("@neat.is/types");
|
|
9495
9622
|
var NO_ENV = "unknown";
|
|
9623
|
+
function staticCallSiteFor(graph, serviceName, targetNodeId) {
|
|
9624
|
+
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9625
|
+
const sites = [];
|
|
9626
|
+
for (const edgeId of graph.inboundEdges(targetNodeId)) {
|
|
9627
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
9628
|
+
if (edge.provenance !== import_types34.Provenance.EXTRACTED) continue;
|
|
9629
|
+
const parsed = (0, import_types34.parseFileId)(edge.source);
|
|
9630
|
+
if (!parsed || parsed.service !== serviceName || !edge.evidence) continue;
|
|
9631
|
+
const site = { relPath: edge.evidence.file };
|
|
9632
|
+
if (edge.evidence.line !== void 0) site.line = edge.evidence.line;
|
|
9633
|
+
sites.push(site);
|
|
9634
|
+
}
|
|
9635
|
+
return sites.length === 1 ? sites[0] : void 0;
|
|
9636
|
+
}
|
|
9637
|
+
function routeCallSiteFor(graph, targetNodeId) {
|
|
9638
|
+
if (!graph.hasNode(targetNodeId)) return void 0;
|
|
9639
|
+
const attrs = graph.getNodeAttributes(targetNodeId);
|
|
9640
|
+
if (attrs.type !== import_types34.NodeType.RouteNode || !attrs.path) return void 0;
|
|
9641
|
+
const site = { relPath: attrs.path };
|
|
9642
|
+
if (attrs.line !== void 0) site.line = attrs.line;
|
|
9643
|
+
return site;
|
|
9644
|
+
}
|
|
9496
9645
|
async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
9497
9646
|
const signals = await connector.poll(ctx);
|
|
9498
9647
|
let edgesCreated = 0;
|
|
@@ -9509,7 +9658,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
9509
9658
|
ensureInfraNode(graph, kind, name, provider);
|
|
9510
9659
|
}
|
|
9511
9660
|
const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
9512
|
-
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } :
|
|
9661
|
+
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
|
|
9513
9662
|
const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
|
|
9514
9663
|
const evidence = callSite ? {
|
|
9515
9664
|
file: reconcileObservedRelPath(graph, resolved.serviceName, callSite.relPath),
|
|
@@ -10060,19 +10209,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10060
10209
|
|
|
10061
10210
|
// src/connectors/supabase/resolve.ts
|
|
10062
10211
|
init_cjs_shims();
|
|
10063
|
-
var
|
|
10212
|
+
var import_types36 = require("@neat.is/types");
|
|
10064
10213
|
function createSupabaseResolveTarget(graph, config) {
|
|
10065
10214
|
return (signal, _ctx) => {
|
|
10066
10215
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10067
10216
|
return null;
|
|
10068
10217
|
}
|
|
10069
|
-
const subResourceId = (0,
|
|
10218
|
+
const subResourceId = (0, import_types36.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10070
10219
|
if (graph.hasNode(subResourceId)) {
|
|
10071
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
10220
|
+
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
|
|
10221
|
+
}
|
|
10222
|
+
const bareResourceId = (0, import_types36.infraId)(signal.targetKind, signal.targetName);
|
|
10223
|
+
if (graph.hasNode(bareResourceId)) {
|
|
10224
|
+
return { targetNodeId: bareResourceId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
|
|
10072
10225
|
}
|
|
10073
|
-
const projectLevelId = (0,
|
|
10226
|
+
const projectLevelId = (0, import_types36.infraId)("supabase", config.nodeRef);
|
|
10074
10227
|
if (graph.hasNode(projectLevelId)) {
|
|
10075
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10228
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
|
|
10076
10229
|
}
|
|
10077
10230
|
return null;
|
|
10078
10231
|
};
|
|
@@ -10165,7 +10318,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10165
10318
|
|
|
10166
10319
|
// src/connectors/railway/index.ts
|
|
10167
10320
|
init_cjs_shims();
|
|
10168
|
-
var
|
|
10321
|
+
var import_types40 = require("@neat.is/types");
|
|
10169
10322
|
|
|
10170
10323
|
// src/connectors/railway/client.ts
|
|
10171
10324
|
init_cjs_shims();
|
|
@@ -10291,7 +10444,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10291
10444
|
const out = [];
|
|
10292
10445
|
graph.forEachNode((_id, attrs) => {
|
|
10293
10446
|
const node = attrs;
|
|
10294
|
-
if (node.type !==
|
|
10447
|
+
if (node.type !== import_types40.NodeType.RouteNode) return;
|
|
10295
10448
|
const route = attrs;
|
|
10296
10449
|
if (route.service !== serviceName) return;
|
|
10297
10450
|
out.push({
|
|
@@ -10387,12 +10540,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10387
10540
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10388
10541
|
if (!serviceName) return null;
|
|
10389
10542
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10390
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
10543
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types40.EdgeType.CALLS };
|
|
10391
10544
|
}
|
|
10392
10545
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10393
10546
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10394
10547
|
if (!peerName) return null;
|
|
10395
|
-
return { targetNodeId: (0,
|
|
10548
|
+
return { targetNodeId: (0, import_types40.serviceId)(peerName), serviceName, edgeType: import_types40.EdgeType.CONNECTS_TO };
|
|
10396
10549
|
}
|
|
10397
10550
|
return null;
|
|
10398
10551
|
};
|
|
@@ -10579,7 +10732,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
10579
10732
|
|
|
10580
10733
|
// src/connectors/firebase/resolve.ts
|
|
10581
10734
|
init_cjs_shims();
|
|
10582
|
-
var
|
|
10735
|
+
var import_types41 = require("@neat.is/types");
|
|
10583
10736
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
10584
10737
|
switch (resourceType) {
|
|
10585
10738
|
case "cloud_function":
|
|
@@ -10594,7 +10747,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
10594
10747
|
const entries = [];
|
|
10595
10748
|
graph.forEachNode((_id, attrs) => {
|
|
10596
10749
|
const node = attrs;
|
|
10597
|
-
if (node.type !==
|
|
10750
|
+
if (node.type !== import_types41.NodeType.RouteNode) return;
|
|
10598
10751
|
const route = attrs;
|
|
10599
10752
|
if (route.service !== serviceName) return;
|
|
10600
10753
|
entries.push({
|
|
@@ -10626,7 +10779,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
10626
10779
|
return {
|
|
10627
10780
|
targetNodeId: match.routeNodeId,
|
|
10628
10781
|
serviceName,
|
|
10629
|
-
edgeType:
|
|
10782
|
+
edgeType: import_types41.EdgeType.CALLS
|
|
10630
10783
|
};
|
|
10631
10784
|
};
|
|
10632
10785
|
}
|
|
@@ -10653,7 +10806,7 @@ init_cjs_shims();
|
|
|
10653
10806
|
|
|
10654
10807
|
// src/connectors/cloudflare/connector.ts
|
|
10655
10808
|
init_cjs_shims();
|
|
10656
|
-
var
|
|
10809
|
+
var import_types43 = require("@neat.is/types");
|
|
10657
10810
|
|
|
10658
10811
|
// src/connectors/cloudflare/client.ts
|
|
10659
10812
|
init_cjs_shims();
|
|
@@ -10814,7 +10967,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
10814
10967
|
graph.forEachNode((id, attrs) => {
|
|
10815
10968
|
if (found) return;
|
|
10816
10969
|
const a = attrs;
|
|
10817
|
-
if (a.type ===
|
|
10970
|
+
if (a.type === import_types43.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
10818
10971
|
found = id;
|
|
10819
10972
|
}
|
|
10820
10973
|
});
|
|
@@ -10826,7 +10979,7 @@ function findMatchingRouteNode(graph, serviceName, method, path53) {
|
|
|
10826
10979
|
graph.forEachNode((id, attrs) => {
|
|
10827
10980
|
if (found) return;
|
|
10828
10981
|
const a = attrs;
|
|
10829
|
-
if (a.type !==
|
|
10982
|
+
if (a.type !== import_types43.NodeType.RouteNode || a.service !== serviceName) return;
|
|
10830
10983
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
10831
10984
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
10832
10985
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -10845,11 +10998,11 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
10845
10998
|
};
|
|
10846
10999
|
const mapping = config.workers?.[scriptName];
|
|
10847
11000
|
if (mapping) {
|
|
10848
|
-
const wholeFileId = (0,
|
|
11001
|
+
const wholeFileId = (0, import_types43.fileId)(mapping.service, mapping.entryFile);
|
|
10849
11002
|
return {
|
|
10850
11003
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
10851
11004
|
serviceName: mapping.service,
|
|
10852
|
-
edgeType:
|
|
11005
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
10853
11006
|
};
|
|
10854
11007
|
}
|
|
10855
11008
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -10858,13 +11011,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
10858
11011
|
return {
|
|
10859
11012
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
10860
11013
|
serviceName: fileNode.service,
|
|
10861
|
-
edgeType:
|
|
11014
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
10862
11015
|
};
|
|
10863
11016
|
}
|
|
10864
11017
|
return {
|
|
10865
|
-
targetNodeId: (0,
|
|
11018
|
+
targetNodeId: (0, import_types43.infraId)("cloudflare-worker", scriptName),
|
|
10866
11019
|
serviceName: scriptName,
|
|
10867
|
-
edgeType:
|
|
11020
|
+
edgeType: import_types43.EdgeType.CALLS,
|
|
10868
11021
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
10869
11022
|
};
|
|
10870
11023
|
};
|
|
@@ -11123,7 +11276,7 @@ function unroutedErrorsPath(neatHome4) {
|
|
|
11123
11276
|
}
|
|
11124
11277
|
|
|
11125
11278
|
// src/daemon.ts
|
|
11126
|
-
var
|
|
11279
|
+
var import_types46 = require("@neat.is/types");
|
|
11127
11280
|
function daemonJsonPath(scanPath) {
|
|
11128
11281
|
return import_node_path50.default.join(scanPath, "neat-out", "daemon.json");
|
|
11129
11282
|
}
|
|
@@ -11258,7 +11411,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
11258
11411
|
if (!serviceName) return true;
|
|
11259
11412
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
11260
11413
|
return graph.someNode(
|
|
11261
|
-
(_id, attrs) => attrs.type ===
|
|
11414
|
+
(_id, attrs) => attrs.type === import_types46.NodeType.ServiceNode && attrs.name === serviceName
|
|
11262
11415
|
);
|
|
11263
11416
|
}
|
|
11264
11417
|
async function bootstrapProject(entry2, connectors = [], neatHome4) {
|