@neat.is/core 0.4.30 → 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/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
resolveNeatVersion,
|
|
10
10
|
validateConnectorEntry,
|
|
11
11
|
writeDaemonRecord
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-5MGHWP6R.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-QQDZSIY3.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)) {
|
|
@@ -2881,13 +2902,18 @@ async function handleSpan(ctx, span) {
|
|
|
2881
2902
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
2882
2903
|
targetId = (0, import_types3.databaseId)(host);
|
|
2883
2904
|
} else {
|
|
2884
|
-
const
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
localName
|
|
2889
|
-
|
|
2890
|
-
|
|
2905
|
+
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
2906
|
+
if (declared) {
|
|
2907
|
+
targetId = declared;
|
|
2908
|
+
} else {
|
|
2909
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
2910
|
+
targetId = ensureLocalDatabaseNode(
|
|
2911
|
+
ctx.graph,
|
|
2912
|
+
span.service,
|
|
2913
|
+
localName,
|
|
2914
|
+
span.dbSystem
|
|
2915
|
+
);
|
|
2916
|
+
}
|
|
2891
2917
|
}
|
|
2892
2918
|
const result = upsertObservedEdge(
|
|
2893
2919
|
ctx.graph,
|
|
@@ -3071,29 +3097,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3071
3097
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
3072
3098
|
});
|
|
3073
3099
|
let promoted = 0;
|
|
3074
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
3100
|
+
for (const { frontierId: frontierId2, serviceId: serviceId6 } of toPromote) {
|
|
3075
3101
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
3076
3102
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
3077
3103
|
if (!gate.allowed) {
|
|
3078
3104
|
continue;
|
|
3079
3105
|
}
|
|
3080
3106
|
}
|
|
3081
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
3107
|
+
rewireFrontierEdges(graph, frontierId2, serviceId6);
|
|
3082
3108
|
graph.dropNode(frontierId2);
|
|
3083
3109
|
promoted++;
|
|
3084
3110
|
}
|
|
3085
3111
|
return promoted;
|
|
3086
3112
|
}
|
|
3087
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
3113
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId6) {
|
|
3088
3114
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
3089
3115
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
3090
3116
|
for (const edgeId of inbound) {
|
|
3091
3117
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3092
|
-
rebuildEdge(graph, edge, edge.source,
|
|
3118
|
+
rebuildEdge(graph, edge, edge.source, serviceId6, edgeId);
|
|
3093
3119
|
}
|
|
3094
3120
|
for (const edgeId of outbound) {
|
|
3095
3121
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3096
|
-
rebuildEdge(graph, edge,
|
|
3122
|
+
rebuildEdge(graph, edge, serviceId6, edge.target, edgeId);
|
|
3097
3123
|
}
|
|
3098
3124
|
}
|
|
3099
3125
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -3889,9 +3915,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
3889
3915
|
"StatefulSet",
|
|
3890
3916
|
"DaemonSet"
|
|
3891
3917
|
]);
|
|
3892
|
-
function addAliases(graph,
|
|
3893
|
-
if (!graph.hasNode(
|
|
3894
|
-
const node = graph.getNodeAttributes(
|
|
3918
|
+
function addAliases(graph, serviceId6, candidates) {
|
|
3919
|
+
if (!graph.hasNode(serviceId6)) return;
|
|
3920
|
+
const node = graph.getNodeAttributes(serviceId6);
|
|
3895
3921
|
if (node.type !== import_types6.NodeType.ServiceNode) return;
|
|
3896
3922
|
const set = new Set(node.aliases ?? []);
|
|
3897
3923
|
for (const c of candidates) {
|
|
@@ -3901,7 +3927,7 @@ function addAliases(graph, serviceId5, candidates) {
|
|
|
3901
3927
|
}
|
|
3902
3928
|
if (set.size === 0) return;
|
|
3903
3929
|
const updated = { ...node, aliases: [...set].sort() };
|
|
3904
|
-
graph.replaceNodeAttributes(
|
|
3930
|
+
graph.replaceNodeAttributes(serviceId6, updated);
|
|
3905
3931
|
}
|
|
3906
3932
|
function indexServicesByName(services) {
|
|
3907
3933
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3934,12 +3960,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
3934
3960
|
}
|
|
3935
3961
|
if (!compose?.services) return;
|
|
3936
3962
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3937
|
-
const
|
|
3938
|
-
if (!
|
|
3963
|
+
const serviceId6 = serviceIndex.get(composeName);
|
|
3964
|
+
if (!serviceId6) continue;
|
|
3939
3965
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
3940
3966
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
3941
3967
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
3942
|
-
addAliases(graph,
|
|
3968
|
+
addAliases(graph, serviceId6, aliases);
|
|
3943
3969
|
}
|
|
3944
3970
|
}
|
|
3945
3971
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -4589,6 +4615,29 @@ async function readIfExists(filePath) {
|
|
|
4589
4615
|
return null;
|
|
4590
4616
|
}
|
|
4591
4617
|
}
|
|
4618
|
+
async function resolveEnvVar(serviceDir, name) {
|
|
4619
|
+
const entries = await import_node_fs12.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
4620
|
+
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
4621
|
+
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
4622
|
+
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
4623
|
+
for (const fileName of envNames) {
|
|
4624
|
+
const content = await readIfExists(import_node_path14.default.join(serviceDir, fileName));
|
|
4625
|
+
if (!content) continue;
|
|
4626
|
+
for (const line of content.split("\n")) {
|
|
4627
|
+
const trimmed = line.trim();
|
|
4628
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
4629
|
+
const eq = trimmed.indexOf("=");
|
|
4630
|
+
if (eq < 0) continue;
|
|
4631
|
+
if (trimmed.slice(0, eq).trim() !== name) continue;
|
|
4632
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
4633
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
4634
|
+
value = value.slice(1, -1);
|
|
4635
|
+
}
|
|
4636
|
+
return value || null;
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
return null;
|
|
4640
|
+
}
|
|
4592
4641
|
async function findFirst(serviceDir, candidates) {
|
|
4593
4642
|
for (const rel of candidates) {
|
|
4594
4643
|
const abs = import_node_path14.default.join(serviceDir, rel);
|
|
@@ -4686,6 +4735,14 @@ async function parse3(serviceDir) {
|
|
|
4686
4735
|
const config = parseConnectionString(urlMatch[1]);
|
|
4687
4736
|
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4688
4737
|
}
|
|
4738
|
+
const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
|
|
4739
|
+
if (envMatch) {
|
|
4740
|
+
const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
|
|
4741
|
+
if (resolved) {
|
|
4742
|
+
const config = parseConnectionString(resolved);
|
|
4743
|
+
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4689
4746
|
return [
|
|
4690
4747
|
{
|
|
4691
4748
|
host: `${engine}-prisma`,
|
|
@@ -7870,14 +7927,23 @@ var import_types31 = require("@neat.is/types");
|
|
|
7870
7927
|
function bucketKey(source, target, type) {
|
|
7871
7928
|
return `${type}|${source}|${target}`;
|
|
7872
7929
|
}
|
|
7930
|
+
function bucketSourceFor(graph, edge) {
|
|
7931
|
+
if (edge.type !== import_types31.EdgeType.CONNECTS_TO) return edge.source;
|
|
7932
|
+
const parsed = (0, import_types31.parseFileId)(edge.source);
|
|
7933
|
+
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
7934
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
7935
|
+
if (target.type !== import_types31.NodeType.DatabaseNode) return edge.source;
|
|
7936
|
+
return (0, import_types31.serviceId)(parsed.service);
|
|
7937
|
+
}
|
|
7873
7938
|
function bucketEdges(graph) {
|
|
7874
7939
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
7875
7940
|
graph.forEachEdge((id, attrs) => {
|
|
7876
7941
|
const e = attrs;
|
|
7877
7942
|
const parsed = (0, import_types31.parseEdgeId)(id);
|
|
7878
7943
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
7879
|
-
const
|
|
7880
|
-
const
|
|
7944
|
+
const source = bucketSourceFor(graph, e);
|
|
7945
|
+
const key = bucketKey(source, e.target, e.type);
|
|
7946
|
+
const cur = buckets2.get(key) ?? { source, target: e.target, type: e.type };
|
|
7881
7947
|
switch (provenance) {
|
|
7882
7948
|
case import_types31.Provenance.EXTRACTED:
|
|
7883
7949
|
cur.extracted = e;
|