@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/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
resolveNeatVersion,
|
|
10
10
|
validateConnectorEntry,
|
|
11
11
|
writeDaemonRecord
|
|
12
|
-
} from "./chunk-
|
|
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-
|
|
71
|
+
} from "./chunk-BI3XKGVG.js";
|
|
72
72
|
import {
|
|
73
73
|
startOtelGrpcReceiver
|
|
74
74
|
} from "./chunk-I72HTUOG.js";
|
package/dist/index.cjs
CHANGED
|
@@ -1277,7 +1277,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1277
1277
|
for (const id of walk3.path) {
|
|
1278
1278
|
const owner = resolveOwningService(graph, id);
|
|
1279
1279
|
if (!owner) continue;
|
|
1280
|
-
const { id:
|
|
1280
|
+
const { id: serviceId6, svc } = owner;
|
|
1281
1281
|
const deps = svc.dependencies ?? {};
|
|
1282
1282
|
for (const pair of candidatePairs) {
|
|
1283
1283
|
const declared = deps[pair.driver];
|
|
@@ -1290,7 +1290,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1290
1290
|
);
|
|
1291
1291
|
if (!result.compatible) {
|
|
1292
1292
|
return {
|
|
1293
|
-
rootCauseNode:
|
|
1293
|
+
rootCauseNode: serviceId6,
|
|
1294
1294
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1295
1295
|
...result.minDriverVersion ? {
|
|
1296
1296
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1305,7 +1305,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1305
1305
|
for (const id of walk3.path) {
|
|
1306
1306
|
const owner = resolveOwningService(graph, id);
|
|
1307
1307
|
if (!owner) continue;
|
|
1308
|
-
const { id:
|
|
1308
|
+
const { id: serviceId6, svc } = owner;
|
|
1309
1309
|
const deps = svc.dependencies ?? {};
|
|
1310
1310
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1311
1311
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1314,7 +1314,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1314
1314
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1315
1315
|
if (!result.compatible && result.reason) {
|
|
1316
1316
|
return {
|
|
1317
|
-
rootCauseNode:
|
|
1317
|
+
rootCauseNode: serviceId6,
|
|
1318
1318
|
rootCauseReason: result.reason,
|
|
1319
1319
|
...result.requiredNodeVersion ? {
|
|
1320
1320
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1329,7 +1329,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1329
1329
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1330
1330
|
if (!result.compatible && result.reason) {
|
|
1331
1331
|
return {
|
|
1332
|
-
rootCauseNode:
|
|
1332
|
+
rootCauseNode: serviceId6,
|
|
1333
1333
|
rootCauseReason: result.reason,
|
|
1334
1334
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1335
1335
|
};
|
|
@@ -1420,9 +1420,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1420
1420
|
function isFailingCallEdge(e) {
|
|
1421
1421
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1422
1422
|
}
|
|
1423
|
-
function callSourcesForService(graph,
|
|
1424
|
-
const ids = [
|
|
1425
|
-
for (const edgeId of graph.outboundEdges(
|
|
1423
|
+
function callSourcesForService(graph, serviceId6) {
|
|
1424
|
+
const ids = [serviceId6];
|
|
1425
|
+
for (const edgeId of graph.outboundEdges(serviceId6)) {
|
|
1426
1426
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1427
1427
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1428
1428
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1439,9 +1439,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1439
1439
|
}
|
|
1440
1440
|
return id < curId;
|
|
1441
1441
|
}
|
|
1442
|
-
function dominantFailingCall(graph,
|
|
1442
|
+
function dominantFailingCall(graph, serviceId6, visited) {
|
|
1443
1443
|
let best = null;
|
|
1444
|
-
for (const src of callSourcesForService(graph,
|
|
1444
|
+
for (const src of callSourcesForService(graph, serviceId6)) {
|
|
1445
1445
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1446
1446
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1447
1447
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -2623,6 +2623,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
|
2623
2623
|
graph.addNode(id, node);
|
|
2624
2624
|
return id;
|
|
2625
2625
|
}
|
|
2626
|
+
function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
|
|
2627
|
+
if (!graph.hasNode(serviceNodeId)) return null;
|
|
2628
|
+
const sources = [serviceNodeId];
|
|
2629
|
+
for (const edgeId of graph.outboundEdges(serviceNodeId)) {
|
|
2630
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
2631
|
+
if (e.type === import_types3.EdgeType.CONTAINS) sources.push(e.target);
|
|
2632
|
+
}
|
|
2633
|
+
const matches = /* @__PURE__ */ new Set();
|
|
2634
|
+
for (const src of sources) {
|
|
2635
|
+
if (!graph.hasNode(src)) continue;
|
|
2636
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
2637
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
2638
|
+
if (edge.type !== import_types3.EdgeType.CONNECTS_TO || edge.provenance !== import_types3.Provenance.EXTRACTED) continue;
|
|
2639
|
+
if (!graph.hasNode(edge.target)) continue;
|
|
2640
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
2641
|
+
if (target.type !== import_types3.NodeType.DatabaseNode || target.engine !== engine) continue;
|
|
2642
|
+
matches.add(edge.target);
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
return matches.size === 1 ? [...matches][0] : null;
|
|
2646
|
+
}
|
|
2626
2647
|
function ensureFrontierNode(graph, host, ts) {
|
|
2627
2648
|
const id = frontierIdFor(host);
|
|
2628
2649
|
if (graph.hasNode(id)) {
|
|
@@ -2643,6 +2664,7 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
2643
2664
|
}
|
|
2644
2665
|
function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence) {
|
|
2645
2666
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
2667
|
+
const grain = source.startsWith("file:") ? "file" : "service";
|
|
2646
2668
|
const id = makeObservedEdgeId(type, source, target);
|
|
2647
2669
|
if (graph.hasEdge(id)) {
|
|
2648
2670
|
const existing = graph.getEdgeAttributes(id);
|
|
@@ -2659,7 +2681,9 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
2659
2681
|
lastObserved: ts,
|
|
2660
2682
|
callCount: newSpanCount,
|
|
2661
2683
|
signal: newSignal,
|
|
2662
|
-
confidence: (0, import_types3.confidenceForObservedSignal)(newSignal)
|
|
2684
|
+
confidence: (0, import_types3.confidenceForObservedSignal)(newSignal),
|
|
2685
|
+
grain
|
|
2686
|
+
// backfills legacy edges that predate ADR-142
|
|
2663
2687
|
};
|
|
2664
2688
|
graph.replaceEdgeAttributes(id, updated);
|
|
2665
2689
|
return { edge: updated, created: false };
|
|
@@ -2679,6 +2703,7 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
2679
2703
|
lastObserved: ts,
|
|
2680
2704
|
callCount: 1,
|
|
2681
2705
|
signal,
|
|
2706
|
+
grain,
|
|
2682
2707
|
// Call-site evidence from span code.* semconv (file-awareness.md §4 + §6).
|
|
2683
2708
|
// Only set when code.filepath was present on the span — never fabricated.
|
|
2684
2709
|
...evidence ? { evidence } : {}
|
|
@@ -2881,13 +2906,18 @@ async function handleSpan(ctx, span) {
|
|
|
2881
2906
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
2882
2907
|
targetId = (0, import_types3.databaseId)(host);
|
|
2883
2908
|
} else {
|
|
2884
|
-
const
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
localName
|
|
2889
|
-
|
|
2890
|
-
|
|
2909
|
+
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
2910
|
+
if (declared) {
|
|
2911
|
+
targetId = declared;
|
|
2912
|
+
} else {
|
|
2913
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
2914
|
+
targetId = ensureLocalDatabaseNode(
|
|
2915
|
+
ctx.graph,
|
|
2916
|
+
span.service,
|
|
2917
|
+
localName,
|
|
2918
|
+
span.dbSystem
|
|
2919
|
+
);
|
|
2920
|
+
}
|
|
2891
2921
|
}
|
|
2892
2922
|
const result = upsertObservedEdge(
|
|
2893
2923
|
ctx.graph,
|
|
@@ -3071,29 +3101,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3071
3101
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
3072
3102
|
});
|
|
3073
3103
|
let promoted = 0;
|
|
3074
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
3104
|
+
for (const { frontierId: frontierId2, serviceId: serviceId6 } of toPromote) {
|
|
3075
3105
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
3076
3106
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
3077
3107
|
if (!gate.allowed) {
|
|
3078
3108
|
continue;
|
|
3079
3109
|
}
|
|
3080
3110
|
}
|
|
3081
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
3111
|
+
rewireFrontierEdges(graph, frontierId2, serviceId6);
|
|
3082
3112
|
graph.dropNode(frontierId2);
|
|
3083
3113
|
promoted++;
|
|
3084
3114
|
}
|
|
3085
3115
|
return promoted;
|
|
3086
3116
|
}
|
|
3087
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
3117
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId6) {
|
|
3088
3118
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
3089
3119
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
3090
3120
|
for (const edgeId of inbound) {
|
|
3091
3121
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3092
|
-
rebuildEdge(graph, edge, edge.source,
|
|
3122
|
+
rebuildEdge(graph, edge, edge.source, serviceId6, edgeId);
|
|
3093
3123
|
}
|
|
3094
3124
|
for (const edgeId of outbound) {
|
|
3095
3125
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3096
|
-
rebuildEdge(graph, edge,
|
|
3126
|
+
rebuildEdge(graph, edge, serviceId6, edge.target, edgeId);
|
|
3097
3127
|
}
|
|
3098
3128
|
}
|
|
3099
3129
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -3889,9 +3919,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
3889
3919
|
"StatefulSet",
|
|
3890
3920
|
"DaemonSet"
|
|
3891
3921
|
]);
|
|
3892
|
-
function addAliases(graph,
|
|
3893
|
-
if (!graph.hasNode(
|
|
3894
|
-
const node = graph.getNodeAttributes(
|
|
3922
|
+
function addAliases(graph, serviceId6, candidates) {
|
|
3923
|
+
if (!graph.hasNode(serviceId6)) return;
|
|
3924
|
+
const node = graph.getNodeAttributes(serviceId6);
|
|
3895
3925
|
if (node.type !== import_types6.NodeType.ServiceNode) return;
|
|
3896
3926
|
const set = new Set(node.aliases ?? []);
|
|
3897
3927
|
for (const c of candidates) {
|
|
@@ -3901,7 +3931,7 @@ function addAliases(graph, serviceId5, candidates) {
|
|
|
3901
3931
|
}
|
|
3902
3932
|
if (set.size === 0) return;
|
|
3903
3933
|
const updated = { ...node, aliases: [...set].sort() };
|
|
3904
|
-
graph.replaceNodeAttributes(
|
|
3934
|
+
graph.replaceNodeAttributes(serviceId6, updated);
|
|
3905
3935
|
}
|
|
3906
3936
|
function indexServicesByName(services) {
|
|
3907
3937
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3934,12 +3964,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
3934
3964
|
}
|
|
3935
3965
|
if (!compose?.services) return;
|
|
3936
3966
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3937
|
-
const
|
|
3938
|
-
if (!
|
|
3967
|
+
const serviceId6 = serviceIndex.get(composeName);
|
|
3968
|
+
if (!serviceId6) continue;
|
|
3939
3969
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
3940
3970
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
3941
3971
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
3942
|
-
addAliases(graph,
|
|
3972
|
+
addAliases(graph, serviceId6, aliases);
|
|
3943
3973
|
}
|
|
3944
3974
|
}
|
|
3945
3975
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -4589,6 +4619,29 @@ async function readIfExists(filePath) {
|
|
|
4589
4619
|
return null;
|
|
4590
4620
|
}
|
|
4591
4621
|
}
|
|
4622
|
+
async function resolveEnvVar(serviceDir, name) {
|
|
4623
|
+
const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
4624
|
+
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
4625
|
+
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
4626
|
+
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
4627
|
+
for (const fileName of envNames) {
|
|
4628
|
+
const content = await readIfExists(import_node_path14.default.join(serviceDir, fileName));
|
|
4629
|
+
if (!content) continue;
|
|
4630
|
+
for (const line of content.split("\n")) {
|
|
4631
|
+
const trimmed = line.trim();
|
|
4632
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
4633
|
+
const eq = trimmed.indexOf("=");
|
|
4634
|
+
if (eq < 0) continue;
|
|
4635
|
+
if (trimmed.slice(0, eq).trim() !== name) continue;
|
|
4636
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
4637
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
4638
|
+
value = value.slice(1, -1);
|
|
4639
|
+
}
|
|
4640
|
+
return value || null;
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
return null;
|
|
4644
|
+
}
|
|
4592
4645
|
async function findFirst(serviceDir, candidates) {
|
|
4593
4646
|
for (const rel of candidates) {
|
|
4594
4647
|
const abs = import_node_path14.default.join(serviceDir, rel);
|
|
@@ -4686,6 +4739,14 @@ async function parse3(serviceDir) {
|
|
|
4686
4739
|
const config = parseConnectionString(urlMatch[1]);
|
|
4687
4740
|
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4688
4741
|
}
|
|
4742
|
+
const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
|
|
4743
|
+
if (envMatch) {
|
|
4744
|
+
const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
|
|
4745
|
+
if (resolved) {
|
|
4746
|
+
const config = parseConnectionString(resolved);
|
|
4747
|
+
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4689
4750
|
return [
|
|
4690
4751
|
{
|
|
4691
4752
|
host: `${engine}-prisma`,
|
|
@@ -4729,6 +4790,15 @@ async function parse4(serviceDir) {
|
|
|
4729
4790
|
const config = parseConnectionString(urlMatch[1]);
|
|
4730
4791
|
if (config) return [{ ...config, sourceFile: filePath }];
|
|
4731
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
|
+
}
|
|
4732
4802
|
const hostMatch = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/);
|
|
4733
4803
|
if (hostMatch) {
|
|
4734
4804
|
const portMatch = content.match(/port\s*:\s*(\d+)/);
|
|
@@ -4782,6 +4852,15 @@ async function parse5(serviceDir) {
|
|
|
4782
4852
|
const config = parseConnectionString(urlMatch[1]);
|
|
4783
4853
|
if (config) return [{ ...config, sourceFile: filePath }];
|
|
4784
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
|
+
}
|
|
4785
4864
|
const host = content.match(/host\s*:\s*['"`]([^'"`]+)['"`]/)?.[1];
|
|
4786
4865
|
if (host) {
|
|
4787
4866
|
const port = content.match(/port\s*:\s*(\d+)/)?.[1];
|
|
@@ -6330,6 +6409,16 @@ function constructorMatchesImport(name, ctx) {
|
|
|
6330
6409
|
if (name === "createClient") return ctx.hasSupabaseJs;
|
|
6331
6410
|
return ctx.hasSupabaseSsr;
|
|
6332
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
|
+
}
|
|
6333
6422
|
function supabaseEndpointsFromFile(file, serviceDir) {
|
|
6334
6423
|
const ctx = readImports2(file.content);
|
|
6335
6424
|
if (!ctx.hasSupabaseJs && !ctx.hasSupabaseSsr) return [];
|
|
@@ -6362,6 +6451,34 @@ function supabaseEndpointsFromFile(file, serviceDir) {
|
|
|
6362
6451
|
}
|
|
6363
6452
|
});
|
|
6364
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
|
+
}
|
|
6365
6482
|
return out;
|
|
6366
6483
|
}
|
|
6367
6484
|
|
|
@@ -7870,14 +7987,23 @@ var import_types31 = require("@neat.is/types");
|
|
|
7870
7987
|
function bucketKey(source, target, type) {
|
|
7871
7988
|
return `${type}|${source}|${target}`;
|
|
7872
7989
|
}
|
|
7990
|
+
function bucketSourceFor(graph, edge) {
|
|
7991
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
7992
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
7993
|
+
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7994
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
7995
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
7996
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
7997
|
+
}
|
|
7873
7998
|
function bucketEdges(graph) {
|
|
7874
7999
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7875
8000
|
graph.forEachEdge((id, attrs) => {
|
|
7876
8001
|
const e = attrs;
|
|
7877
8002
|
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7878
8003
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7879
|
-
const
|
|
7880
|
-
const
|
|
8004
|
+
const source = bucketSourceFor(graph, e);
|
|
8005
|
+
const key = bucketKey(source, e.target, e.type);
|
|
8006
|
+
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7881
8007
|
switch (provenance) {
|
|
7882
8008
|
case import_types31.Provenance.EXTRACTED:
|
|
7883
8009
|
cur.extracted = e;
|
|
@@ -9598,7 +9724,30 @@ init_otel();
|
|
|
9598
9724
|
|
|
9599
9725
|
// src/connectors/index.ts
|
|
9600
9726
|
init_cjs_shims();
|
|
9727
|
+
var import_types34 = require("@neat.is/types");
|
|
9601
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
|
+
}
|
|
9602
9751
|
async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
9603
9752
|
const signals = await connector.poll(ctx);
|
|
9604
9753
|
let edgesCreated = 0;
|
|
@@ -9615,7 +9764,7 @@ async function runConnectorPoll(connector, ctx, graph, resolveTarget) {
|
|
|
9615
9764
|
ensureInfraNode(graph, kind, name, provider);
|
|
9616
9765
|
}
|
|
9617
9766
|
const serviceNodeId = ensureServiceNode(graph, resolved.serviceName, NO_ENV);
|
|
9618
|
-
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } :
|
|
9767
|
+
const callSite = signal.callSite ? { relPath: signal.callSite.file, line: signal.callSite.line } : routeCallSiteFor(graph, resolved.targetNodeId) ?? staticCallSiteFor(graph, resolved.serviceName, resolved.targetNodeId);
|
|
9619
9768
|
const sourceId = callSite ? ensureObservedFileNode(graph, resolved.serviceName, serviceNodeId, callSite) : serviceNodeId;
|
|
9620
9769
|
const evidence = callSite ? {
|
|
9621
9770
|
file: reconcileObservedRelPath(graph, resolved.serviceName, callSite.relPath),
|
|
@@ -10166,19 +10315,23 @@ async function fetchPgStatStatements(connectionString, limit = DEFAULT_STATEMENT
|
|
|
10166
10315
|
|
|
10167
10316
|
// src/connectors/supabase/resolve.ts
|
|
10168
10317
|
init_cjs_shims();
|
|
10169
|
-
var
|
|
10318
|
+
var import_types36 = require("@neat.is/types");
|
|
10170
10319
|
function createSupabaseResolveTarget(graph, config) {
|
|
10171
10320
|
return (signal, _ctx) => {
|
|
10172
10321
|
if (signal.targetKind !== SUPABASE_TABLE_TARGET_KIND && signal.targetKind !== SUPABASE_RPC_TARGET_KIND) {
|
|
10173
10322
|
return null;
|
|
10174
10323
|
}
|
|
10175
|
-
const subResourceId = (0,
|
|
10324
|
+
const subResourceId = (0, import_types36.infraId)(signal.targetKind, `${config.nodeRef}/${signal.targetName}`);
|
|
10176
10325
|
if (graph.hasNode(subResourceId)) {
|
|
10177
|
-
return { targetNodeId: subResourceId, serviceName: config.serviceName, edgeType:
|
|
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 };
|
|
10178
10331
|
}
|
|
10179
|
-
const projectLevelId = (0,
|
|
10332
|
+
const projectLevelId = (0, import_types36.infraId)("supabase", config.nodeRef);
|
|
10180
10333
|
if (graph.hasNode(projectLevelId)) {
|
|
10181
|
-
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType:
|
|
10334
|
+
return { targetNodeId: projectLevelId, serviceName: config.serviceName, edgeType: import_types36.EdgeType.CALLS };
|
|
10182
10335
|
}
|
|
10183
10336
|
return null;
|
|
10184
10337
|
};
|
|
@@ -10271,7 +10424,7 @@ function createSupabaseConnector(graph, config, deps = {}) {
|
|
|
10271
10424
|
|
|
10272
10425
|
// src/connectors/railway/index.ts
|
|
10273
10426
|
init_cjs_shims();
|
|
10274
|
-
var
|
|
10427
|
+
var import_types40 = require("@neat.is/types");
|
|
10275
10428
|
|
|
10276
10429
|
// src/connectors/railway/client.ts
|
|
10277
10430
|
init_cjs_shims();
|
|
@@ -10397,7 +10550,7 @@ function buildRailwayRouteIndex(graph, serviceName) {
|
|
|
10397
10550
|
const out = [];
|
|
10398
10551
|
graph.forEachNode((_id, attrs) => {
|
|
10399
10552
|
const node = attrs;
|
|
10400
|
-
if (node.type !==
|
|
10553
|
+
if (node.type !== import_types40.NodeType.RouteNode) return;
|
|
10401
10554
|
const route = attrs;
|
|
10402
10555
|
if (route.service !== serviceName) return;
|
|
10403
10556
|
out.push({
|
|
@@ -10493,12 +10646,12 @@ function createRailwayResolveTarget(config) {
|
|
|
10493
10646
|
const serviceName = config.serviceNameById[config.serviceId];
|
|
10494
10647
|
if (!serviceName) return null;
|
|
10495
10648
|
if (signal.targetKind === ROUTE_TARGET_KIND) {
|
|
10496
|
-
return { targetNodeId: signal.targetName, serviceName, edgeType:
|
|
10649
|
+
return { targetNodeId: signal.targetName, serviceName, edgeType: import_types40.EdgeType.CALLS };
|
|
10497
10650
|
}
|
|
10498
10651
|
if (signal.targetKind === PEER_SERVICE_TARGET_KIND) {
|
|
10499
10652
|
const peerName = config.serviceNameById[signal.targetName];
|
|
10500
10653
|
if (!peerName) return null;
|
|
10501
|
-
return { targetNodeId: (0,
|
|
10654
|
+
return { targetNodeId: (0, import_types40.serviceId)(peerName), serviceName, edgeType: import_types40.EdgeType.CONNECTS_TO };
|
|
10502
10655
|
}
|
|
10503
10656
|
return null;
|
|
10504
10657
|
};
|
|
@@ -10685,7 +10838,7 @@ function mapLogEntriesToSignals(entries) {
|
|
|
10685
10838
|
|
|
10686
10839
|
// src/connectors/firebase/resolve.ts
|
|
10687
10840
|
init_cjs_shims();
|
|
10688
|
-
var
|
|
10841
|
+
var import_types41 = require("@neat.is/types");
|
|
10689
10842
|
function neatServiceNameFor(resourceType, resourceName, serviceMap) {
|
|
10690
10843
|
switch (resourceType) {
|
|
10691
10844
|
case "cloud_function":
|
|
@@ -10700,7 +10853,7 @@ function routeEntriesFor(graph, serviceName) {
|
|
|
10700
10853
|
const entries = [];
|
|
10701
10854
|
graph.forEachNode((_id, attrs) => {
|
|
10702
10855
|
const node = attrs;
|
|
10703
|
-
if (node.type !==
|
|
10856
|
+
if (node.type !== import_types41.NodeType.RouteNode) return;
|
|
10704
10857
|
const route = attrs;
|
|
10705
10858
|
if (route.service !== serviceName) return;
|
|
10706
10859
|
entries.push({
|
|
@@ -10732,7 +10885,7 @@ function createFirebaseResolveTarget(graph, serviceMap) {
|
|
|
10732
10885
|
return {
|
|
10733
10886
|
targetNodeId: match.routeNodeId,
|
|
10734
10887
|
serviceName,
|
|
10735
|
-
edgeType:
|
|
10888
|
+
edgeType: import_types41.EdgeType.CALLS
|
|
10736
10889
|
};
|
|
10737
10890
|
};
|
|
10738
10891
|
}
|
|
@@ -10759,7 +10912,7 @@ init_cjs_shims();
|
|
|
10759
10912
|
|
|
10760
10913
|
// src/connectors/cloudflare/connector.ts
|
|
10761
10914
|
init_cjs_shims();
|
|
10762
|
-
var
|
|
10915
|
+
var import_types43 = require("@neat.is/types");
|
|
10763
10916
|
|
|
10764
10917
|
// src/connectors/cloudflare/client.ts
|
|
10765
10918
|
init_cjs_shims();
|
|
@@ -10920,7 +11073,7 @@ function findTaggedWorkerFileNode(graph, workerName) {
|
|
|
10920
11073
|
graph.forEachNode((id, attrs) => {
|
|
10921
11074
|
if (found) return;
|
|
10922
11075
|
const a = attrs;
|
|
10923
|
-
if (a.type ===
|
|
11076
|
+
if (a.type === import_types43.NodeType.FileNode && a.platform === "cloudflare" && a.platformName === workerName) {
|
|
10924
11077
|
found = id;
|
|
10925
11078
|
}
|
|
10926
11079
|
});
|
|
@@ -10932,7 +11085,7 @@ function findMatchingRouteNode(graph, serviceName, method, path51) {
|
|
|
10932
11085
|
graph.forEachNode((id, attrs) => {
|
|
10933
11086
|
if (found) return;
|
|
10934
11087
|
const a = attrs;
|
|
10935
|
-
if (a.type !==
|
|
11088
|
+
if (a.type !== import_types43.NodeType.RouteNode || a.service !== serviceName) return;
|
|
10936
11089
|
if (!a.pathTemplate || normalizePathTemplate(a.pathTemplate) !== normalizedPath) return;
|
|
10937
11090
|
const routeMethod = (a.method ?? "").toUpperCase();
|
|
10938
11091
|
if (routeMethod !== "ALL" && routeMethod !== method) return;
|
|
@@ -10951,11 +11104,11 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
10951
11104
|
};
|
|
10952
11105
|
const mapping = config.workers?.[scriptName];
|
|
10953
11106
|
if (mapping) {
|
|
10954
|
-
const wholeFileId = (0,
|
|
11107
|
+
const wholeFileId = (0, import_types43.fileId)(mapping.service, mapping.entryFile);
|
|
10955
11108
|
return {
|
|
10956
11109
|
targetNodeId: resolveRouteGrain(mapping.service, wholeFileId),
|
|
10957
11110
|
serviceName: mapping.service,
|
|
10958
|
-
edgeType:
|
|
11111
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
10959
11112
|
};
|
|
10960
11113
|
}
|
|
10961
11114
|
const taggedFileId = findTaggedWorkerFileNode(graph, scriptName);
|
|
@@ -10964,13 +11117,13 @@ function createCloudflareResolveTarget(config, graph) {
|
|
|
10964
11117
|
return {
|
|
10965
11118
|
targetNodeId: resolveRouteGrain(fileNode.service, taggedFileId),
|
|
10966
11119
|
serviceName: fileNode.service,
|
|
10967
|
-
edgeType:
|
|
11120
|
+
edgeType: import_types43.EdgeType.CALLS
|
|
10968
11121
|
};
|
|
10969
11122
|
}
|
|
10970
11123
|
return {
|
|
10971
|
-
targetNodeId: (0,
|
|
11124
|
+
targetNodeId: (0, import_types43.infraId)("cloudflare-worker", scriptName),
|
|
10972
11125
|
serviceName: scriptName,
|
|
10973
|
-
edgeType:
|
|
11126
|
+
edgeType: import_types43.EdgeType.CALLS,
|
|
10974
11127
|
ensureInfraNode: { kind: "cloudflare-worker", name: scriptName, provider: "cloudflare" }
|
|
10975
11128
|
};
|
|
10976
11129
|
};
|
|
@@ -11229,7 +11382,7 @@ function unroutedErrorsPath(neatHome3) {
|
|
|
11229
11382
|
}
|
|
11230
11383
|
|
|
11231
11384
|
// src/daemon.ts
|
|
11232
|
-
var
|
|
11385
|
+
var import_types46 = require("@neat.is/types");
|
|
11233
11386
|
function daemonJsonPath(scanPath) {
|
|
11234
11387
|
return import_node_path50.default.join(scanPath, "neat-out", "daemon.json");
|
|
11235
11388
|
}
|
|
@@ -11350,7 +11503,7 @@ function spanBelongsToSingleProject(graph, project, serviceName) {
|
|
|
11350
11503
|
if (!serviceName) return true;
|
|
11351
11504
|
if (serviceNameMatchesProject(serviceName, project)) return true;
|
|
11352
11505
|
return graph.someNode(
|
|
11353
|
-
(_id, attrs) => attrs.type ===
|
|
11506
|
+
(_id, attrs) => attrs.type === import_types46.NodeType.ServiceNode && attrs.name === serviceName
|
|
11354
11507
|
);
|
|
11355
11508
|
}
|
|
11356
11509
|
async function bootstrapProject(entry, connectors = [], neatHome3) {
|