@intentius/behold 0.13.0 → 0.13.1
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/cli.js +52 -23
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3590,6 +3590,9 @@ var K8S_PLUMBING_KINDS = /* @__PURE__ */ new Set([
|
|
|
3590
3590
|
"K8s::Rbac::ClusterRole",
|
|
3591
3591
|
"K8s::Rbac::ClusterRoleBinding"
|
|
3592
3592
|
]);
|
|
3593
|
+
function namespaceBoxKey(ns) {
|
|
3594
|
+
return `namespace ${ns}`;
|
|
3595
|
+
}
|
|
3593
3596
|
function metaStr(node, key) {
|
|
3594
3597
|
const meta = node.attrs?.metadata;
|
|
3595
3598
|
const v = meta?.[key];
|
|
@@ -3623,19 +3626,20 @@ function projectK8sLogical(ir, env, boundContext) {
|
|
|
3623
3626
|
const cluster = boundManagedCluster(ir.nodes, boundContext);
|
|
3624
3627
|
const CLUSTER_TITLE = cluster ? cluster.id : env ? `cluster ${env}` : "cluster";
|
|
3625
3628
|
const CLUSTER_SCOPED2 = "cluster-scoped";
|
|
3626
|
-
const namespaceTitle = (ns) => `namespace ${ns}`;
|
|
3627
3629
|
const byContainer = {};
|
|
3628
3630
|
const child = (parent, c) => {
|
|
3629
3631
|
const arr = byContainer[parent] ?? (byContainer[parent] = []);
|
|
3630
3632
|
if (!arr.includes(c)) arr.push(c);
|
|
3631
3633
|
};
|
|
3632
|
-
|
|
3634
|
+
const namespaceBoxes = {};
|
|
3635
|
+
const boxFor = (ns) => namespaceBoxes[ns] ??= namespaceBoxKey(ns);
|
|
3636
|
+
for (const ns of [...declaredNamespaces(k8s)].sort()) child(CLUSTER_TITLE, boxFor(ns));
|
|
3633
3637
|
const headline = k8s.filter((n) => !K8S_PLUMBING_KINDS.has(n.kind));
|
|
3634
3638
|
let clusterScoped = false;
|
|
3635
3639
|
for (const n of headline) {
|
|
3636
3640
|
const ns = metaStr(n, "namespace");
|
|
3637
3641
|
if (ns) {
|
|
3638
|
-
child(
|
|
3642
|
+
child(boxFor(ns), n.id);
|
|
3639
3643
|
} else {
|
|
3640
3644
|
clusterScoped = true;
|
|
3641
3645
|
child(CLUSTER_SCOPED2, n.id);
|
|
@@ -3644,7 +3648,7 @@ function projectK8sLogical(ir, env, boundContext) {
|
|
|
3644
3648
|
if (clusterScoped) child(CLUSTER_TITLE, CLUSTER_SCOPED2);
|
|
3645
3649
|
const kept = new Set(headline.map((n) => n.id));
|
|
3646
3650
|
const edges = ir.edges.filter((e) => kept.has(e.from) && kept.has(e.to));
|
|
3647
|
-
return { ir: { nodes: headline, edges, groups: {} }, byContainer };
|
|
3651
|
+
return { ir: { nodes: headline, edges, groups: {} }, byContainer, clusterBox: CLUSTER_TITLE, namespaceBoxes };
|
|
3648
3652
|
}
|
|
3649
3653
|
|
|
3650
3654
|
// src/logical-helm.ts
|
|
@@ -3998,6 +4002,7 @@ function projectTopology(ir, env, boundContext, sourceRoots) {
|
|
|
3998
4002
|
const nodes = [];
|
|
3999
4003
|
const edges = [];
|
|
4000
4004
|
const byContainer = {};
|
|
4005
|
+
const boxes = { namespaces: {} };
|
|
4001
4006
|
for (const p of projections) {
|
|
4002
4007
|
nodes.push(...p.ir.nodes);
|
|
4003
4008
|
edges.push(...p.ir.edges);
|
|
@@ -4005,11 +4010,13 @@ function projectTopology(ir, env, boundContext, sourceRoots) {
|
|
|
4005
4010
|
const arr = byContainer[parent] ?? (byContainer[parent] = []);
|
|
4006
4011
|
for (const c of children) if (!arr.includes(c)) arr.push(c);
|
|
4007
4012
|
}
|
|
4013
|
+
if (p.clusterBox) boxes.cluster = p.clusterBox;
|
|
4014
|
+
Object.assign(boxes.namespaces, p.namespaceBoxes);
|
|
4008
4015
|
}
|
|
4009
|
-
placeHelmReleases(nodes, byContainer,
|
|
4016
|
+
placeHelmReleases(nodes, byContainer, boxes);
|
|
4010
4017
|
nestReleaseBoxes(byContainer);
|
|
4011
4018
|
retainCrossLensEdges(ir, projections, nodes, edges, byContainer);
|
|
4012
|
-
return { ir: { nodes, edges, groups: {} }, byContainer };
|
|
4019
|
+
return { ir: { nodes, edges, groups: {} }, byContainer, namespaceBoxes: boxes.namespaces };
|
|
4013
4020
|
}
|
|
4014
4021
|
function retainCrossLensEdges(ir, projections, nodes, edges, byContainer) {
|
|
4015
4022
|
const cards = new Set(nodes.map((n) => n.id));
|
|
@@ -4044,23 +4051,26 @@ function retainCrossLensEdges(ir, projections, nodes, edges, byContainer) {
|
|
|
4044
4051
|
edges.push(e);
|
|
4045
4052
|
}
|
|
4046
4053
|
}
|
|
4047
|
-
function placeHelmReleases(nodes, byContainer,
|
|
4054
|
+
function placeHelmReleases(nodes, byContainer, boxes) {
|
|
4048
4055
|
const releases = nodes.filter((n) => n.kind === "Helm::Release");
|
|
4049
4056
|
if (releases.length === 0) return;
|
|
4050
4057
|
const child = (parent, c) => {
|
|
4051
4058
|
const arr = byContainer[parent] ?? (byContainer[parent] = []);
|
|
4052
4059
|
if (!arr.includes(c)) arr.push(c);
|
|
4053
4060
|
};
|
|
4054
|
-
const
|
|
4055
|
-
const clusterTitle = cluster ? cluster.id : env ? `cluster ${env}` : "cluster";
|
|
4056
|
-
const clusterBoxExists = byContainer[clusterTitle] !== void 0;
|
|
4061
|
+
const clusterBox = boxes.cluster !== void 0 && byContainer[boxes.cluster] !== void 0 ? boxes.cluster : void 0;
|
|
4057
4062
|
for (const r of releases) {
|
|
4058
4063
|
const ns = r.attrs?.namespace;
|
|
4059
4064
|
if (typeof ns !== "string" || ns.length === 0) continue;
|
|
4060
|
-
const nsBox =
|
|
4061
|
-
if (byContainer[nsBox]
|
|
4062
|
-
|
|
4063
|
-
|
|
4065
|
+
const nsBox = boxes.namespaces[ns];
|
|
4066
|
+
if (nsBox !== void 0 && byContainer[nsBox] !== void 0) {
|
|
4067
|
+
child(nsBox, r.id);
|
|
4068
|
+
continue;
|
|
4069
|
+
}
|
|
4070
|
+
if (clusterBox === void 0) continue;
|
|
4071
|
+
const observed = nsBox ?? namespaceBoxKey(ns);
|
|
4072
|
+
child(clusterBox, observed);
|
|
4073
|
+
child(observed, r.id);
|
|
4064
4074
|
}
|
|
4065
4075
|
}
|
|
4066
4076
|
function nestReleaseBoxes(byContainer) {
|
|
@@ -5360,6 +5370,12 @@ function renderArchitecture(ir, byContainer, opts = {}) {
|
|
|
5360
5370
|
nodesep: Math.round(48 + spread * 48),
|
|
5361
5371
|
ranksep: Math.round(60 + spread * 56)
|
|
5362
5372
|
});
|
|
5373
|
+
if (opts.groupMarks) {
|
|
5374
|
+
for (const box of layout.groups ?? []) {
|
|
5375
|
+
const mark = box.id !== void 0 ? opts.groupMarks[box.id] : void 0;
|
|
5376
|
+
if (mark !== void 0) box.mark = mark;
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5363
5379
|
const svg = renderSvg(ir, layout, {
|
|
5364
5380
|
fit: true,
|
|
5365
5381
|
hideTitle: true,
|
|
@@ -6882,6 +6898,17 @@ function markOperatorHome(ir) {
|
|
|
6882
6898
|
})
|
|
6883
6899
|
};
|
|
6884
6900
|
}
|
|
6901
|
+
var OPERATOR_HOME_GLYPH = {
|
|
6902
|
+
body: '<path d="M16.5 6.63A7 7 0 1 1 6.64 7.5"/><path d="M16.5 6.63 14.3 5.9M16.5 6.63 15.9 8.1"/>'
|
|
6903
|
+
};
|
|
6904
|
+
function operatorHomeBoxMarks(ir, namespaceBoxes) {
|
|
6905
|
+
const out = {};
|
|
6906
|
+
for (const home of operatorHomes(ir)) {
|
|
6907
|
+
const box = namespaceBoxes[home.namespace];
|
|
6908
|
+
if (box) out[box] = OPERATOR_HOME_GLYPH;
|
|
6909
|
+
}
|
|
6910
|
+
return out;
|
|
6911
|
+
}
|
|
6885
6912
|
function operatorStrip(rows, now = Date.now()) {
|
|
6886
6913
|
const out = rows.map((r) => {
|
|
6887
6914
|
const expiresAt = r.lease ? Date.parse(r.lease.expiresAt) : NaN;
|
|
@@ -9208,8 +9235,8 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9208
9235
|
ir = addClusterAnchorEdges(ir, estateContext);
|
|
9209
9236
|
if (logical) {
|
|
9210
9237
|
const logicalBefore = ir.nodes.length;
|
|
9211
|
-
const { ir: projected, byContainer } = projectTopology(ir, metaEnv ?? void 0, estateContext, await estateSourceRoots(opts));
|
|
9212
|
-
const { svg: svg2 } = renderArchitecture(projected, byContainer);
|
|
9238
|
+
const { ir: projected, byContainer, namespaceBoxes } = projectTopology(ir, metaEnv ?? void 0, estateContext, await estateSourceRoots(opts));
|
|
9239
|
+
const { svg: svg2 } = renderArchitecture(projected, byContainer, { groupMarks: operatorHomeBoxMarks(ir, namespaceBoxes ?? {}) });
|
|
9213
9240
|
const logicalNote = notesFor("logical", projected, void 0, logicalBefore);
|
|
9214
9241
|
return c.json({
|
|
9215
9242
|
ir: projected,
|
|
@@ -9238,9 +9265,10 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9238
9265
|
ir = joinCiProgress(ir, runner.applyProgress);
|
|
9239
9266
|
} else if (logical) {
|
|
9240
9267
|
const raw = mergeClusterRoot(await graphIr(cfg.projectDir, { ...opts, detail: 3 }), await clusterRootGraphIr(cfg.projectDir, opts));
|
|
9241
|
-
const
|
|
9242
|
-
const
|
|
9243
|
-
const {
|
|
9268
|
+
const logicalContext = await boundK8sContext(metaEnv ?? void 0);
|
|
9269
|
+
const base = addClusterAnchorEdges(addValueMatchEdges(addK8sDeclaredEdges(raw)), logicalContext);
|
|
9270
|
+
const { ir: projected, byContainer, namespaceBoxes } = projectTopology(base, metaEnv ?? void 0, logicalContext, [await graphPath(cfg.projectDir, opts), cfg.projectDir]);
|
|
9271
|
+
const { svg: svg2 } = renderArchitecture(projected, byContainer, { groupMarks: operatorHomeBoxMarks(base, namespaceBoxes ?? {}) });
|
|
9244
9272
|
const logicalNote = notesFor("logical", projected, void 0, base.nodes.length);
|
|
9245
9273
|
return c.json({ ir: projected, svg: svg2, byContainer, meta: { projectDir: cfg.projectDir, env: metaEnv, tier: opts.tier ?? null, target: opts.target ?? null, mode: "logical", ...logicalNote ? { note: logicalNote } : {} } });
|
|
9246
9274
|
} else {
|
|
@@ -9390,8 +9418,8 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9390
9418
|
].join("; ") : void 0;
|
|
9391
9419
|
if (logical) {
|
|
9392
9420
|
const logicalBefore = ir2.nodes.length;
|
|
9393
|
-
const { ir: projected, byContainer } = projectTopology(ir2, env, boundContext2, await estateSourceRoots(query));
|
|
9394
|
-
const { svg: svg3 } = renderArchitecture(projected, byContainer);
|
|
9421
|
+
const { ir: projected, byContainer, namespaceBoxes } = projectTopology(ir2, env, boundContext2, await estateSourceRoots(query));
|
|
9422
|
+
const { svg: svg3 } = renderArchitecture(projected, byContainer, { groupMarks: operatorHomeBoxMarks(ir2, namespaceBoxes ?? {}) });
|
|
9395
9423
|
const note3 = [
|
|
9396
9424
|
notesFor("logical", projected, void 0, logicalBefore),
|
|
9397
9425
|
coverNote,
|
|
@@ -9433,8 +9461,9 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9433
9461
|
ir = runtime ? attachRuntimeContainment(ir) : pruneRuntimeChildren(ir);
|
|
9434
9462
|
if (logical) {
|
|
9435
9463
|
const logicalBefore = ir.nodes.length;
|
|
9436
|
-
const
|
|
9437
|
-
const {
|
|
9464
|
+
const projectionInput = addClusterAnchorEdges(addValueMatchEdges(addK8sDeclaredEdges(ir)), boundContext);
|
|
9465
|
+
const { ir: projected, byContainer, namespaceBoxes } = projectTopology(projectionInput, env, boundContext, [await graphPath(cfg.projectDir, opts), cfg.projectDir]);
|
|
9466
|
+
const { svg: svg2 } = renderArchitecture(projected, byContainer, { groupMarks: operatorHomeBoxMarks(projectionInput, namespaceBoxes ?? {}) });
|
|
9438
9467
|
const logicalTierNote = tierMismatchNote(projected, beholdConfig.tiers, query.tier);
|
|
9439
9468
|
const logicalNote = [logicalTierNote, notesFor("logical", projected, void 0, logicalBefore)].filter(Boolean).join(" \xB7 ");
|
|
9440
9469
|
return c.json({ ir: projected, svg: svg2, byContainer, meta: { projectDir: cfg.projectDir, env, mode: "logical", ...logicalNote ? { note: logicalNote } : {} } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/behold",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "behold — a live control plane on chant. See your whole estate (every substrate in one graph), coloured by drift; act through delegated, gated Ops.",
|
|
6
6
|
"bin": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@hono/node-server": "^2.0.5",
|
|
38
38
|
"@intentius/chant": "^0.52.1",
|
|
39
39
|
"@intentius/chant-k8s-client": "^0.52.1",
|
|
40
|
-
"@intentius/pinhole": "^0.3.
|
|
40
|
+
"@intentius/pinhole": "^0.3.7",
|
|
41
41
|
"hono": "^4.12.34"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|