@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/neatd.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import {
|
|
3
3
|
reconcileDaemonRecordSync,
|
|
4
4
|
startDaemon
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-5MGHWP6R.js";
|
|
6
6
|
import {
|
|
7
7
|
listProjects,
|
|
8
8
|
registryPath
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-QQDZSIY3.js";
|
|
10
10
|
import {
|
|
11
11
|
BindAuthorityError,
|
|
12
12
|
__require
|
package/dist/server.cjs
CHANGED
|
@@ -1445,7 +1445,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1445
1445
|
for (const id of walk3.path) {
|
|
1446
1446
|
const owner = resolveOwningService(graph, id);
|
|
1447
1447
|
if (!owner) continue;
|
|
1448
|
-
const { id:
|
|
1448
|
+
const { id: serviceId5, svc } = owner;
|
|
1449
1449
|
const deps = svc.dependencies ?? {};
|
|
1450
1450
|
for (const pair of candidatePairs) {
|
|
1451
1451
|
const declared = deps[pair.driver];
|
|
@@ -1458,7 +1458,7 @@ function databaseRootCauseShape(graph, origin, walk3) {
|
|
|
1458
1458
|
);
|
|
1459
1459
|
if (!result.compatible) {
|
|
1460
1460
|
return {
|
|
1461
|
-
rootCauseNode:
|
|
1461
|
+
rootCauseNode: serviceId5,
|
|
1462
1462
|
rootCauseReason: result.reason ?? "incompatible driver",
|
|
1463
1463
|
...result.minDriverVersion ? {
|
|
1464
1464
|
fixRecommendation: `Upgrade ${svc.name} ${pair.driver} driver to >= ${result.minDriverVersion}`
|
|
@@ -1473,7 +1473,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1473
1473
|
for (const id of walk3.path) {
|
|
1474
1474
|
const owner = resolveOwningService(graph, id);
|
|
1475
1475
|
if (!owner) continue;
|
|
1476
|
-
const { id:
|
|
1476
|
+
const { id: serviceId5, svc } = owner;
|
|
1477
1477
|
const deps = svc.dependencies ?? {};
|
|
1478
1478
|
const serviceNodeEngine = svc.nodeEngine;
|
|
1479
1479
|
for (const constraint of nodeEngineConstraints()) {
|
|
@@ -1482,7 +1482,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1482
1482
|
const result = checkNodeEngineConstraint(constraint, declared, serviceNodeEngine);
|
|
1483
1483
|
if (!result.compatible && result.reason) {
|
|
1484
1484
|
return {
|
|
1485
|
-
rootCauseNode:
|
|
1485
|
+
rootCauseNode: serviceId5,
|
|
1486
1486
|
rootCauseReason: result.reason,
|
|
1487
1487
|
...result.requiredNodeVersion ? {
|
|
1488
1488
|
fixRecommendation: `Bump ${svc.name}'s engines.node to >= ${result.requiredNodeVersion}`
|
|
@@ -1497,7 +1497,7 @@ function serviceRootCauseShape(graph, _origin, walk3) {
|
|
|
1497
1497
|
const result = checkPackageConflict(conflict, declared, requiredDeclared);
|
|
1498
1498
|
if (!result.compatible && result.reason) {
|
|
1499
1499
|
return {
|
|
1500
|
-
rootCauseNode:
|
|
1500
|
+
rootCauseNode: serviceId5,
|
|
1501
1501
|
rootCauseReason: result.reason,
|
|
1502
1502
|
fixRecommendation: `Upgrade ${svc.name}'s ${conflict.requires.name} to >= ${conflict.requires.minVersion}`
|
|
1503
1503
|
};
|
|
@@ -1588,9 +1588,9 @@ function rootCauseFromIncidents(nodeId, incidents, errorEvent) {
|
|
|
1588
1588
|
function isFailingCallEdge(e) {
|
|
1589
1589
|
return e.type === import_types.EdgeType.CALLS && (e.signal?.errorCount ?? 0) > 0;
|
|
1590
1590
|
}
|
|
1591
|
-
function callSourcesForService(graph,
|
|
1592
|
-
const ids = [
|
|
1593
|
-
for (const edgeId of graph.outboundEdges(
|
|
1591
|
+
function callSourcesForService(graph, serviceId5) {
|
|
1592
|
+
const ids = [serviceId5];
|
|
1593
|
+
for (const edgeId of graph.outboundEdges(serviceId5)) {
|
|
1594
1594
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1595
1595
|
if (e.type !== import_types.EdgeType.CONTAINS) continue;
|
|
1596
1596
|
const tgt = graph.getNodeAttributes(e.target);
|
|
@@ -1607,9 +1607,9 @@ function failingCallDominates(e, id, curEdge, curId) {
|
|
|
1607
1607
|
}
|
|
1608
1608
|
return id < curId;
|
|
1609
1609
|
}
|
|
1610
|
-
function dominantFailingCall(graph,
|
|
1610
|
+
function dominantFailingCall(graph, serviceId5, visited) {
|
|
1611
1611
|
let best = null;
|
|
1612
|
-
for (const src of callSourcesForService(graph,
|
|
1612
|
+
for (const src of callSourcesForService(graph, serviceId5)) {
|
|
1613
1613
|
for (const edgeId of graph.outboundEdges(src)) {
|
|
1614
1614
|
const e = graph.getEdgeAttributes(edgeId);
|
|
1615
1615
|
if (!isFailingCallEdge(e)) continue;
|
|
@@ -1820,14 +1820,23 @@ function getObservedDependencies(graph, nodeId) {
|
|
|
1820
1820
|
function bucketKey(source, target, type) {
|
|
1821
1821
|
return `${type}|${source}|${target}`;
|
|
1822
1822
|
}
|
|
1823
|
+
function bucketSourceFor(graph, edge) {
|
|
1824
|
+
if (edge.type !== import_types2.EdgeType.CONNECTS_TO) return edge.source;
|
|
1825
|
+
const parsed = (0, import_types2.parseFileId)(edge.source);
|
|
1826
|
+
if (!parsed || !graph.hasNode(edge.target)) return edge.source;
|
|
1827
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
1828
|
+
if (target.type !== import_types2.NodeType.DatabaseNode) return edge.source;
|
|
1829
|
+
return (0, import_types2.serviceId)(parsed.service);
|
|
1830
|
+
}
|
|
1823
1831
|
function bucketEdges(graph) {
|
|
1824
1832
|
const buckets = /* @__PURE__ */ new Map();
|
|
1825
1833
|
graph.forEachEdge((id, attrs) => {
|
|
1826
1834
|
const e = attrs;
|
|
1827
1835
|
const parsed = (0, import_types2.parseEdgeId)(id);
|
|
1828
1836
|
const provenance = parsed?.provenance ?? e.provenance;
|
|
1829
|
-
const
|
|
1830
|
-
const
|
|
1837
|
+
const source = bucketSourceFor(graph, e);
|
|
1838
|
+
const key = bucketKey(source, e.target, e.type);
|
|
1839
|
+
const cur = buckets.get(key) ?? { source, target: e.target, type: e.type };
|
|
1831
1840
|
switch (provenance) {
|
|
1832
1841
|
case import_types2.Provenance.EXTRACTED:
|
|
1833
1842
|
cur.extracted = e;
|
|
@@ -3116,6 +3125,27 @@ function ensureLocalDatabaseNode(graph, serviceName, name, engine) {
|
|
|
3116
3125
|
graph.addNode(id, node);
|
|
3117
3126
|
return id;
|
|
3118
3127
|
}
|
|
3128
|
+
function findDeclaredDatabaseForService(graph, serviceNodeId, engine) {
|
|
3129
|
+
if (!graph.hasNode(serviceNodeId)) return null;
|
|
3130
|
+
const sources = [serviceNodeId];
|
|
3131
|
+
for (const edgeId of graph.outboundEdges(serviceNodeId)) {
|
|
3132
|
+
const e = graph.getEdgeAttributes(edgeId);
|
|
3133
|
+
if (e.type === import_types4.EdgeType.CONTAINS) sources.push(e.target);
|
|
3134
|
+
}
|
|
3135
|
+
const matches = /* @__PURE__ */ new Set();
|
|
3136
|
+
for (const src of sources) {
|
|
3137
|
+
if (!graph.hasNode(src)) continue;
|
|
3138
|
+
for (const edgeId of graph.outboundEdges(src)) {
|
|
3139
|
+
const edge = graph.getEdgeAttributes(edgeId);
|
|
3140
|
+
if (edge.type !== import_types4.EdgeType.CONNECTS_TO || edge.provenance !== import_types4.Provenance.EXTRACTED) continue;
|
|
3141
|
+
if (!graph.hasNode(edge.target)) continue;
|
|
3142
|
+
const target = graph.getNodeAttributes(edge.target);
|
|
3143
|
+
if (target.type !== import_types4.NodeType.DatabaseNode || target.engine !== engine) continue;
|
|
3144
|
+
matches.add(edge.target);
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
return matches.size === 1 ? [...matches][0] : null;
|
|
3148
|
+
}
|
|
3119
3149
|
function ensureFrontierNode(graph, host, ts) {
|
|
3120
3150
|
const id = frontierIdFor(host);
|
|
3121
3151
|
if (graph.hasNode(id)) {
|
|
@@ -3349,13 +3379,18 @@ async function handleSpan(ctx, span) {
|
|
|
3349
3379
|
ensureDatabaseNode(ctx.graph, host, span.dbSystem);
|
|
3350
3380
|
targetId = (0, import_types4.databaseId)(host);
|
|
3351
3381
|
} else {
|
|
3352
|
-
const
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
localName
|
|
3357
|
-
|
|
3358
|
-
|
|
3382
|
+
const declared = findDeclaredDatabaseForService(ctx.graph, sourceId, span.dbSystem);
|
|
3383
|
+
if (declared) {
|
|
3384
|
+
targetId = declared;
|
|
3385
|
+
} else {
|
|
3386
|
+
const localName = span.dbName ?? span.dbSystem;
|
|
3387
|
+
targetId = ensureLocalDatabaseNode(
|
|
3388
|
+
ctx.graph,
|
|
3389
|
+
span.service,
|
|
3390
|
+
localName,
|
|
3391
|
+
span.dbSystem
|
|
3392
|
+
);
|
|
3393
|
+
}
|
|
3359
3394
|
}
|
|
3360
3395
|
const result = upsertObservedEdge(
|
|
3361
3396
|
ctx.graph,
|
|
@@ -3539,29 +3574,29 @@ function promoteFrontierNodes(graph, opts = {}) {
|
|
|
3539
3574
|
toPromote.push({ frontierId: id, serviceId: target });
|
|
3540
3575
|
});
|
|
3541
3576
|
let promoted = 0;
|
|
3542
|
-
for (const { frontierId: frontierId2, serviceId:
|
|
3577
|
+
for (const { frontierId: frontierId2, serviceId: serviceId5 } of toPromote) {
|
|
3543
3578
|
if (opts.policies && opts.policies.length > 0 && opts.policyCtx) {
|
|
3544
3579
|
const gate = canPromoteFrontier(graph, frontierId2, opts.policies, opts.policyCtx);
|
|
3545
3580
|
if (!gate.allowed) {
|
|
3546
3581
|
continue;
|
|
3547
3582
|
}
|
|
3548
3583
|
}
|
|
3549
|
-
rewireFrontierEdges(graph, frontierId2,
|
|
3584
|
+
rewireFrontierEdges(graph, frontierId2, serviceId5);
|
|
3550
3585
|
graph.dropNode(frontierId2);
|
|
3551
3586
|
promoted++;
|
|
3552
3587
|
}
|
|
3553
3588
|
return promoted;
|
|
3554
3589
|
}
|
|
3555
|
-
function rewireFrontierEdges(graph, frontierId2,
|
|
3590
|
+
function rewireFrontierEdges(graph, frontierId2, serviceId5) {
|
|
3556
3591
|
const inbound = [...graph.inboundEdges(frontierId2)];
|
|
3557
3592
|
const outbound = [...graph.outboundEdges(frontierId2)];
|
|
3558
3593
|
for (const edgeId of inbound) {
|
|
3559
3594
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3560
|
-
rebuildEdge(graph, edge, edge.source,
|
|
3595
|
+
rebuildEdge(graph, edge, edge.source, serviceId5, edgeId);
|
|
3561
3596
|
}
|
|
3562
3597
|
for (const edgeId of outbound) {
|
|
3563
3598
|
const edge = graph.getEdgeAttributes(edgeId);
|
|
3564
|
-
rebuildEdge(graph, edge,
|
|
3599
|
+
rebuildEdge(graph, edge, serviceId5, edge.target, edgeId);
|
|
3565
3600
|
}
|
|
3566
3601
|
}
|
|
3567
3602
|
function rebuildEdge(graph, edge, newSource, newTarget, oldEdgeId) {
|
|
@@ -4357,9 +4392,9 @@ var K8S_KINDS_WITH_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
|
4357
4392
|
"StatefulSet",
|
|
4358
4393
|
"DaemonSet"
|
|
4359
4394
|
]);
|
|
4360
|
-
function addAliases(graph,
|
|
4361
|
-
if (!graph.hasNode(
|
|
4362
|
-
const node = graph.getNodeAttributes(
|
|
4395
|
+
function addAliases(graph, serviceId5, candidates) {
|
|
4396
|
+
if (!graph.hasNode(serviceId5)) return;
|
|
4397
|
+
const node = graph.getNodeAttributes(serviceId5);
|
|
4363
4398
|
if (node.type !== import_types7.NodeType.ServiceNode) return;
|
|
4364
4399
|
const set = new Set(node.aliases ?? []);
|
|
4365
4400
|
for (const c of candidates) {
|
|
@@ -4369,7 +4404,7 @@ function addAliases(graph, serviceId4, candidates) {
|
|
|
4369
4404
|
}
|
|
4370
4405
|
if (set.size === 0) return;
|
|
4371
4406
|
const updated = { ...node, aliases: [...set].sort() };
|
|
4372
|
-
graph.replaceNodeAttributes(
|
|
4407
|
+
graph.replaceNodeAttributes(serviceId5, updated);
|
|
4373
4408
|
}
|
|
4374
4409
|
function indexServicesByName(services) {
|
|
4375
4410
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -4402,12 +4437,12 @@ async function collectComposeAliases(graph, scanPath, serviceIndex) {
|
|
|
4402
4437
|
}
|
|
4403
4438
|
if (!compose?.services) return;
|
|
4404
4439
|
for (const [composeName, svc] of Object.entries(compose.services)) {
|
|
4405
|
-
const
|
|
4406
|
-
if (!
|
|
4440
|
+
const serviceId5 = serviceIndex.get(composeName);
|
|
4441
|
+
if (!serviceId5) continue;
|
|
4407
4442
|
const aliases = /* @__PURE__ */ new Set([composeName]);
|
|
4408
4443
|
if (svc.container_name) aliases.add(svc.container_name);
|
|
4409
4444
|
if (svc.hostname) aliases.add(svc.hostname);
|
|
4410
|
-
addAliases(graph,
|
|
4445
|
+
addAliases(graph, serviceId5, aliases);
|
|
4411
4446
|
}
|
|
4412
4447
|
}
|
|
4413
4448
|
var LABEL_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -5057,6 +5092,29 @@ async function readIfExists(filePath) {
|
|
|
5057
5092
|
return null;
|
|
5058
5093
|
}
|
|
5059
5094
|
}
|
|
5095
|
+
async function resolveEnvVar(serviceDir, name) {
|
|
5096
|
+
const entries = await import_node_fs14.promises.readdir(serviceDir, { withFileTypes: true }).catch(() => []);
|
|
5097
|
+
const envNames = entries.filter((e) => e.isFile() && (e.name === ".env" || e.name.startsWith(".env."))).map((e) => e.name);
|
|
5098
|
+
const rank = (n) => n === ".env.local" ? 0 : n === ".env" ? 1 : 2;
|
|
5099
|
+
envNames.sort((a, b) => rank(a) - rank(b) || a.localeCompare(b));
|
|
5100
|
+
for (const fileName of envNames) {
|
|
5101
|
+
const content = await readIfExists(import_node_path16.default.join(serviceDir, fileName));
|
|
5102
|
+
if (!content) continue;
|
|
5103
|
+
for (const line of content.split("\n")) {
|
|
5104
|
+
const trimmed = line.trim();
|
|
5105
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
5106
|
+
const eq = trimmed.indexOf("=");
|
|
5107
|
+
if (eq < 0) continue;
|
|
5108
|
+
if (trimmed.slice(0, eq).trim() !== name) continue;
|
|
5109
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
5110
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5111
|
+
value = value.slice(1, -1);
|
|
5112
|
+
}
|
|
5113
|
+
return value || null;
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
return null;
|
|
5117
|
+
}
|
|
5060
5118
|
async function findFirst(serviceDir, candidates) {
|
|
5061
5119
|
for (const rel of candidates) {
|
|
5062
5120
|
const abs = import_node_path16.default.join(serviceDir, rel);
|
|
@@ -5154,6 +5212,14 @@ async function parse3(serviceDir) {
|
|
|
5154
5212
|
const config = parseConnectionString(urlMatch[1]);
|
|
5155
5213
|
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
5156
5214
|
}
|
|
5215
|
+
const envMatch = body.match(/url\s*=\s*env\(\s*"([^"]+)"\s*\)/);
|
|
5216
|
+
if (envMatch) {
|
|
5217
|
+
const resolved = await resolveEnvVar(serviceDir, envMatch[1]);
|
|
5218
|
+
if (resolved) {
|
|
5219
|
+
const config = parseConnectionString(resolved);
|
|
5220
|
+
if (config) return [{ ...config, sourceFile: schemaPath }];
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5157
5223
|
return [
|
|
5158
5224
|
{
|
|
5159
5225
|
host: `${engine}-prisma`,
|