@neat.is/core 0.4.31-dev.20260718 → 0.5.0
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-2AJHEZIB.js → chunk-5MGHWP6R.js} +2 -2
- package/dist/{chunk-4M2YMGUM.js → chunk-QQDZSIY3.js} +101 -33
- package/dist/chunk-QQDZSIY3.js.map +1 -0
- package/dist/cli.cjs +97 -31
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/index.cjs +97 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/neatd.cjs +97 -31
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +2 -2
- package/dist/server.cjs +97 -31
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-4M2YMGUM.js.map +0 -1
- /package/dist/{chunk-2AJHEZIB.js.map → chunk-5MGHWP6R.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeSpanToProject,
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5MGHWP6R.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-QQDZSIY3.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)) {
|
|
@@ -2844,13 +2865,18 @@ async function handleSpan(ctx, span) {
|
|
|
2844
2865
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
2845
2866
|
targetId = (0, import_types3.databaseId)(host);
|
|
2846
2867
|
} else {
|
|
2847
|
-
const
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
localName
|
|
2852
|
-
|
|
2853
|
-
|
|
2868
|
+
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
2869
|
+
if (declared) {
|
|
2870
|
+
targetId = declared;
|
|
2871
|
+
} else {
|
|
2872
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
2873
|
+
targetId = ensureLocalDatabaseNode(
|
|
2874
|
+
ctx.graph,
|
|
2875
|
+
span.service,
|
|
2876
|
+
localName,
|
|
2877
|
+
span.dbSystem
|
|
2878
|
+
);
|
|
2879
|
+
}
|
|
2854
2880
|
}
|
|
2855
2881
|
const result = upsertObservedEdge(
|
|
2856
2882
|
ctx.graph,
|
|
@@ -3034,29 +3060,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3034
3060
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
3035
3061
|
});
|
|
3036
3062
|
let promoted = 0;
|
|
3037
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
3063
|
+
for (const { frontierId: frontierId2, serviceId: serviceId6 } of toPromote) {
|
|
3038
3064
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
3039
3065
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
3040
3066
|
if (!gate.allowed) {
|
|
3041
3067
|
continue;
|
|
3042
3068
|
}
|
|
3043
3069
|
}
|
|
3044
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
3070
|
+
rewireFrontierEdges(graph, frontierId2, serviceId6);
|
|
3045
3071
|
graph.dropNode(frontierId2);
|
|
3046
3072
|
promoted++;
|
|
3047
3073
|
}
|
|
3048
3074
|
return promoted;
|
|
3049
3075
|
}
|
|
3050
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
3076
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId6) {
|
|
3051
3077
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
3052
3078
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
3053
3079
|
for (const edgeId of inbound) {
|
|
3054
3080
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3055
|
-
rebuildEdge(graph, edge, edge.source,
|
|
3081
|
+
rebuildEdge(graph, edge, edge.source, serviceId6, edgeId);
|
|
3056
3082
|
}
|
|
3057
3083
|
for (const edgeId of outbound) {
|
|
3058
3084
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3059
|
-
rebuildEdge(graph, edge,
|
|
3085
|
+
rebuildEdge(graph, edge, serviceId6, edge.target, edgeId);
|
|
3060
3086
|
}
|
|
3061
3087
|
}
|
|
3062
3088
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -3849,9 +3875,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
3849
3875
|
"StatefulSet",
|
|
3850
3876
|
"DaemonSet"
|
|
3851
3877
|
]);
|
|
3852
|
-
function addAliases(graph,
|
|
3853
|
-
if (!graph.hasNode(
|
|
3854
|
-
const node = graph.getNodeAttributes(
|
|
3878
|
+
function addAliases(graph, serviceId6, candidates) {
|
|
3879
|
+
if (!graph.hasNode(serviceId6)) return;
|
|
3880
|
+
const node = graph.getNodeAttributes(serviceId6);
|
|
3855
3881
|
if (node.type !== import_types6.NodeType.ServiceNode) return;
|
|
3856
3882
|
const set = new Set(node.aliases ?? []);
|
|
3857
3883
|
for (const c of candidates) {
|
|
@@ -3861,7 +3887,7 @@ function addAliases(graph, serviceId5, candidates) {
|
|
|
3861
3887
|
}
|
|
3862
3888
|
if (set.size === 0) return;
|
|
3863
3889
|
const updated = { ...node, aliases: [...set].sort() };
|
|
3864
|
-
graph.replaceNodeAttributes(
|
|
3890
|
+
graph.replaceNodeAttributes(serviceId6, updated);
|
|
3865
3891
|
}
|
|
3866
3892
|
function indexServicesByName(services) {
|
|
3867
3893
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3894,12 +3920,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
3894
3920
|
}
|
|
3895
3921
|
if (!compose?.services) return;
|
|
3896
3922
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3897
|
-
const
|
|
3898
|
-
if (!
|
|
3923
|
+
const serviceId6 = serviceIndex.get(composeName);
|
|
3924
|
+
if (!serviceId6) continue;
|
|
3899
3925
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
3900
3926
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
3901
3927
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
3902
|
-
addAliases(graph,
|
|
3928
|
+
addAliases(graph, serviceId6, aliases);
|
|
3903
3929
|
}
|
|
3904
3930
|
}
|
|
3905
3931
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -4549,6 +4575,29 @@ async function readIfExists(filePath) {
|
|
|
4549
4575
|
return null;
|
|
4550
4576
|
}
|
|
4551
4577
|
}
|
|
4578
|
+
async function resolveEnvVar(serviceDir, name) {
|
|
4579
|
+
const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
4580
|
+
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
4581
|
+
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
4582
|
+
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
4583
|
+
for (const fileName of envNames) {
|
|
4584
|
+
const content = await readIfExists(import_node_path14.default.join(serviceDir, fileName));
|
|
4585
|
+
if (!content) continue;
|
|
4586
|
+
for (const line of content.split("\n")) {
|
|
4587
|
+
const trimmed = line.trim();
|
|
4588
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
4589
|
+
const eq = trimmed.indexOf("=");
|
|
4590
|
+
if (eq < 0) continue;
|
|
4591
|
+
if (trimmed.slice(0, eq).trim() !== name) continue;
|
|
4592
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
4593
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
4594
|
+
value = value.slice(1, -1);
|
|
4595
|
+
}
|
|
4596
|
+
return value || null;
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
return null;
|
|
4600
|
+
}
|
|
4552
4601
|
async function findFirst(serviceDir, candidates) {
|
|
4553
4602
|
for (const rel of candidates) {
|
|
4554
4603
|
const abs = import_node_path14.default.join(serviceDir, rel);
|
|
@@ -4646,6 +4695,14 @@ async function parse3(serviceDir) {
|
|
|
4646
4695
|
const config = parseConnectionString(urlMatch[1]);
|
|
4647
4696
|
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4648
4697
|
}
|
|
4698
|
+
const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
|
|
4699
|
+
if (envMatch) {
|
|
4700
|
+
const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
|
|
4701
|
+
if (resolved) {
|
|
4702
|
+
const config = parseConnectionString(resolved);
|
|
4703
|
+
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4649
4706
|
return [
|
|
4650
4707
|
{
|
|
4651
4708
|
host: `${engine}-prisma`,
|
|
@@ -7882,14 +7939,23 @@ var import_types31 = require("@neat.is/types");
|
|
|
7882
7939
|
function bucketKey(source, target, type) {
|
|
7883
7940
|
return `${type}|${source}|${target}`;
|
|
7884
7941
|
}
|
|
7942
|
+
function bucketSourceFor(graph, edge) {
|
|
7943
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
7944
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
7945
|
+
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7946
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
7947
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
7948
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
7949
|
+
}
|
|
7885
7950
|
function bucketEdges(graph) {
|
|
7886
7951
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7887
7952
|
graph.forEachEdge((id, attrs) => {
|
|
7888
7953
|
const e = attrs;
|
|
7889
7954
|
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7890
7955
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7891
|
-
const
|
|
7892
|
-
const
|
|
7956
|
+
const source = bucketSourceFor(graph, e);
|
|
7957
|
+
const key = bucketKey(source, e.target, e.type);
|
|
7958
|
+
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7893
7959
|
switch (provenance) {
|
|
7894
7960
|
case import_types31.Provenance.EXTRACTED:
|
|
7895
7961
|
cur.extracted = e;
|