@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
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
touchLastSeen,
|
|
32
32
|
upsertObservedEdge,
|
|
33
33
|
writeAtomically
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-QQDZSIY3.js";
|
|
35
35
|
import {
|
|
36
36
|
assertBindAuthority,
|
|
37
37
|
buildOtelReceiver,
|
|
@@ -2461,4 +2461,4 @@ export {
|
|
|
2461
2461
|
resolveHost,
|
|
2462
2462
|
startDaemon
|
|
2463
2463
|
};
|
|
2464
|
-
//# sourceMappingURL=chunk-
|
|
2464
|
+
//# sourceMappingURL=chunk-5MGHWP6R.js.map
|
|
@@ -483,7 +483,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
483
483
|
for (const id of walk3.path) {
|
|
484
484
|
const owner = resolveOwningService(graph, id);
|
|
485
485
|
if (!owner) continue;
|
|
486
|
-
const { id:
|
|
486
|
+
const { id: serviceId5, svc } = owner;
|
|
487
487
|
const deps = svc.dependencies ?? {};
|
|
488
488
|
for (const pair of candidatePairs) {
|
|
489
489
|
const declared = deps[pair.driver];
|
|
@@ -496,7 +496,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
496
496
|
);
|
|
497
497
|
if (!result.compatible) {
|
|
498
498
|
return {
|
|
499
|
-
rootCauseNode:
|
|
499
|
+
rootCauseNode: serviceId5,
|
|
500
500
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
501
501
|
...result.minDriverVersion ? {
|
|
502
502
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -511,7 +511,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
511
511
|
for (const id of walk3.path) {
|
|
512
512
|
const owner = resolveOwningService(graph, id);
|
|
513
513
|
if (!owner) continue;
|
|
514
|
-
const { id:
|
|
514
|
+
const { id: serviceId5, svc } = owner;
|
|
515
515
|
const deps = svc.dependencies ?? {};
|
|
516
516
|
const serviceNodeEngine = svc.nodeEngine;
|
|
517
517
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -520,7 +520,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
520
520
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
521
521
|
if (!result.compatible && result.reason) {
|
|
522
522
|
return {
|
|
523
|
-
rootCauseNode:
|
|
523
|
+
rootCauseNode: serviceId5,
|
|
524
524
|
rootCauseReason: result.reason,
|
|
525
525
|
...result.requiredNodeVersion ? {
|
|
526
526
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -535,7 +535,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
535
535
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
536
536
|
if (!result.compatible && result.reason) {
|
|
537
537
|
return {
|
|
538
|
-
rootCauseNode:
|
|
538
|
+
rootCauseNode: serviceId5,
|
|
539
539
|
rootCauseReason: result.reason,
|
|
540
540
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
541
541
|
};
|
|
@@ -626,9 +626,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
626
626
|
function isFailingCallEdge(e) {
|
|
627
627
|
return e.type === EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
628
628
|
}
|
|
629
|
-
function callSourcesForService(graph,
|
|
630
|
-
const ids = [
|
|
631
|
-
for (const edgeId of graph.outboundEdges(
|
|
629
|
+
function callSourcesForService(graph, serviceId5) {
|
|
630
|
+
const ids = [serviceId5];
|
|
631
|
+
for (const edgeId of graph.outboundEdges(serviceId5)) {
|
|
632
632
|
const e = graph.getEdgeAttributes(edgeId);
|
|
633
633
|
if (e.type !== EdgeType.CONTAINS) continue;
|
|
634
634
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -645,9 +645,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
645
645
|
}
|
|
646
646
|
return id < curId;
|
|
647
647
|
}
|
|
648
|
-
function dominantFailingCall(graph,
|
|
648
|
+
function dominantFailingCall(graph, serviceId5, visited) {
|
|
649
649
|
let best = null;
|
|
650
|
-
for (const src of callSourcesForService(graph,
|
|
650
|
+
for (const src of callSourcesForService(graph, serviceId5)) {
|
|
651
651
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
652
652
|
const e = graph.getEdgeAttributes(edgeId);
|
|
653
653
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -1923,6 +1923,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
|
1923
1923
|
graph.addNode(id, node);
|
|
1924
1924
|
return id;
|
|
1925
1925
|
}
|
|
1926
|
+
function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
|
|
1927
|
+
if (!graph.hasNode(serviceNodeId)) return null;
|
|
1928
|
+
const sources = [serviceNodeId];
|
|
1929
|
+
for (const edgeId of graph.outboundEdges(serviceNodeId)) {
|
|
1930
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
1931
|
+
if (e.type === EdgeType3.CONTAINS) sources.push(e.target);
|
|
1932
|
+
}
|
|
1933
|
+
const matches = /* @__PURE__ */ new Set();
|
|
1934
|
+
for (const src of sources) {
|
|
1935
|
+
if (!graph.hasNode(src)) continue;
|
|
1936
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
1937
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
1938
|
+
if (edge.type !== EdgeType3.CONNECTS_TO || edge.provenance !== Provenance2.EXTRACTED) continue;
|
|
1939
|
+
if (!graph.hasNode(edge.target)) continue;
|
|
1940
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
1941
|
+
if (target.type !== NodeType3.DatabaseNode || target.engine !== engine) continue;
|
|
1942
|
+
matches.add(edge.target);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
return matches.size === 1 ? [...matches][0] : null;
|
|
1946
|
+
}
|
|
1926
1947
|
function ensureFrontierNode(graph, host, ts) {
|
|
1927
1948
|
const id = frontierIdFor(host);
|
|
1928
1949
|
if (graph.hasNode(id)) {
|
|
@@ -2181,13 +2202,18 @@ async function handleSpan(ctx, span) {
|
|
|
2181
2202
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
2182
2203
|
targetId = databaseId(host);
|
|
2183
2204
|
} else {
|
|
2184
|
-
const
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
localName
|
|
2189
|
-
|
|
2190
|
-
|
|
2205
|
+
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
2206
|
+
if (declared) {
|
|
2207
|
+
targetId = declared;
|
|
2208
|
+
} else {
|
|
2209
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
2210
|
+
targetId = ensureLocalDatabaseNode(
|
|
2211
|
+
ctx.graph,
|
|
2212
|
+
span.service,
|
|
2213
|
+
localName,
|
|
2214
|
+
span.dbSystem
|
|
2215
|
+
);
|
|
2216
|
+
}
|
|
2191
2217
|
}
|
|
2192
2218
|
const result = upsertObservedEdge(
|
|
2193
2219
|
ctx.graph,
|
|
@@ -2371,29 +2397,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
2371
2397
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
2372
2398
|
});
|
|
2373
2399
|
let promoted = 0;
|
|
2374
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
2400
|
+
for (const { frontierId: frontierId2, serviceId: serviceId5 } of toPromote) {
|
|
2375
2401
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
2376
2402
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
2377
2403
|
if (!gate.allowed) {
|
|
2378
2404
|
continue;
|
|
2379
2405
|
}
|
|
2380
2406
|
}
|
|
2381
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
2407
|
+
rewireFrontierEdges(graph, frontierId2, serviceId5);
|
|
2382
2408
|
graph.dropNode(frontierId2);
|
|
2383
2409
|
promoted++;
|
|
2384
2410
|
}
|
|
2385
2411
|
return promoted;
|
|
2386
2412
|
}
|
|
2387
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
2413
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId5) {
|
|
2388
2414
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
2389
2415
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
2390
2416
|
for (const edgeId of inbound) {
|
|
2391
2417
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
2392
|
-
rebuildEdge(graph, edge, edge.source,
|
|
2418
|
+
rebuildEdge(graph, edge, edge.source, serviceId5, edgeId);
|
|
2393
2419
|
}
|
|
2394
2420
|
for (const edgeId of outbound) {
|
|
2395
2421
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
2396
|
-
rebuildEdge(graph, edge,
|
|
2422
|
+
rebuildEdge(graph, edge, serviceId5, edge.target, edgeId);
|
|
2397
2423
|
}
|
|
2398
2424
|
}
|
|
2399
2425
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -3195,9 +3221,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
3195
3221
|
"StatefulSet",
|
|
3196
3222
|
"DaemonSet"
|
|
3197
3223
|
]);
|
|
3198
|
-
function addAliases(graph,
|
|
3199
|
-
if (!graph.hasNode(
|
|
3200
|
-
const node = graph.getNodeAttributes(
|
|
3224
|
+
function addAliases(graph, serviceId5, candidates) {
|
|
3225
|
+
if (!graph.hasNode(serviceId5)) return;
|
|
3226
|
+
const node = graph.getNodeAttributes(serviceId5);
|
|
3201
3227
|
if (node.type !== NodeType5.ServiceNode) return;
|
|
3202
3228
|
const set = new Set(node.aliases ?? []);
|
|
3203
3229
|
for (const c of candidates) {
|
|
@@ -3207,7 +3233,7 @@ function addAliases(graph, serviceId4, candidates) {
|
|
|
3207
3233
|
}
|
|
3208
3234
|
if (set.size === 0) return;
|
|
3209
3235
|
const updated = { ...node, aliases: [...set].sort() };
|
|
3210
|
-
graph.replaceNodeAttributes(
|
|
3236
|
+
graph.replaceNodeAttributes(serviceId5, updated);
|
|
3211
3237
|
}
|
|
3212
3238
|
function indexServicesByName(services) {
|
|
3213
3239
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -3240,12 +3266,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
3240
3266
|
}
|
|
3241
3267
|
if (!compose?.services) return;
|
|
3242
3268
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
3243
|
-
const
|
|
3244
|
-
if (!
|
|
3269
|
+
const serviceId5 = serviceIndex.get(composeName);
|
|
3270
|
+
if (!serviceId5) continue;
|
|
3245
3271
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
3246
3272
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
3247
3273
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
3248
|
-
addAliases(graph,
|
|
3274
|
+
addAliases(graph, serviceId5, aliases);
|
|
3249
3275
|
}
|
|
3250
3276
|
}
|
|
3251
3277
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -3906,6 +3932,29 @@ async function readIfExists(filePath) {
|
|
|
3906
3932
|
return null;
|
|
3907
3933
|
}
|
|
3908
3934
|
}
|
|
3935
|
+
async function resolveEnvVar(serviceDir, name) {
|
|
3936
|
+
const entries = await fs12.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
3937
|
+
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
3938
|
+
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
3939
|
+
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
3940
|
+
for (const fileName of envNames) {
|
|
3941
|
+
const content = await readIfExists(path14.join(serviceDir, fileName));
|
|
3942
|
+
if (!content) continue;
|
|
3943
|
+
for (const line of content.split("\n")) {
|
|
3944
|
+
const trimmed = line.trim();
|
|
3945
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
3946
|
+
const eq = trimmed.indexOf("=");
|
|
3947
|
+
if (eq < 0) continue;
|
|
3948
|
+
if (trimmed.slice(0, eq).trim() !== name) continue;
|
|
3949
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
3950
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
3951
|
+
value = value.slice(1, -1);
|
|
3952
|
+
}
|
|
3953
|
+
return value || null;
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
return null;
|
|
3957
|
+
}
|
|
3909
3958
|
async function findFirst(serviceDir, candidates) {
|
|
3910
3959
|
for (const rel of candidates) {
|
|
3911
3960
|
const abs = path14.join(serviceDir, rel);
|
|
@@ -4002,6 +4051,14 @@ async function parse3(serviceDir) {
|
|
|
4002
4051
|
const config = parseConnectionString(urlMatch[1]);
|
|
4003
4052
|
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4004
4053
|
}
|
|
4054
|
+
const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
|
|
4055
|
+
if (envMatch) {
|
|
4056
|
+
const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
|
|
4057
|
+
if (resolved) {
|
|
4058
|
+
const config = parseConnectionString(resolved);
|
|
4059
|
+
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4005
4062
|
return [
|
|
4006
4063
|
{
|
|
4007
4064
|
host: `${engine}-prisma`,
|
|
@@ -6768,19 +6825,30 @@ import {
|
|
|
6768
6825
|
EdgeType as EdgeType20,
|
|
6769
6826
|
NodeType as NodeType15,
|
|
6770
6827
|
parseEdgeId,
|
|
6771
|
-
|
|
6828
|
+
parseFileId,
|
|
6829
|
+
Provenance as Provenance18,
|
|
6830
|
+
serviceId as serviceId4
|
|
6772
6831
|
} from "@neat.is/types";
|
|
6773
6832
|
function bucketKey(source, target, type) {
|
|
6774
6833
|
return `${type}|${source}|${target}`;
|
|
6775
6834
|
}
|
|
6835
|
+
function bucketSourceFor(graph, edge) {
|
|
6836
|
+
if (edge.type !== EdgeType20.CONNECTS_TO) return edge.source;
|
|
6837
|
+
const parsed = parseFileId(edge.source);
|
|
6838
|
+
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
6839
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
6840
|
+
if (target.type !== NodeType15.DatabaseNode) return edge.source;
|
|
6841
|
+
return serviceId4(parsed.service);
|
|
6842
|
+
}
|
|
6776
6843
|
function bucketEdges(graph) {
|
|
6777
6844
|
const buckets = /* @__PURE__ */ new Map();
|
|
6778
6845
|
graph.forEachEdge((id, attrs) => {
|
|
6779
6846
|
const e = attrs;
|
|
6780
6847
|
const parsed = parseEdgeId(id);
|
|
6781
6848
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
6782
|
-
const
|
|
6783
|
-
const
|
|
6849
|
+
const source = bucketSourceFor(graph, e);
|
|
6850
|
+
const key = bucketKey(source, e.target, e.type);
|
|
6851
|
+
const cur = buckets.get(key) ?? { source, target: e.target, type: e.type };
|
|
6784
6852
|
switch (provenance) {
|
|
6785
6853
|
case Provenance18.EXTRACTED:
|
|
6786
6854
|
cur.extracted = e;
|
|
@@ -9233,4 +9301,4 @@ export {
|
|
|
9233
9301
|
recordConnectorPoll,
|
|
9234
9302
|
buildApi
|
|
9235
9303
|
};
|
|
9236
|
-
//# sourceMappingURL=chunk-
|
|
9304
|
+
//# sourceMappingURL=chunk-QQDZSIY3.js.map
|