@intentius/behold 0.12.0 → 0.13.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/cli.js +232 -25
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2405,14 +2405,60 @@ function isOwnNameAttr(kind, key) {
|
|
|
2405
2405
|
return typeSegment(kind).toLowerCase().includes(prefix);
|
|
2406
2406
|
}
|
|
2407
2407
|
var MIN_NAME_LEN = 6;
|
|
2408
|
+
var MAX_NAME_LEN = 200;
|
|
2409
|
+
var MAX_DEPTH = 12;
|
|
2410
|
+
var SKIP_KEYS = /* @__PURE__ */ new Set([
|
|
2411
|
+
"status",
|
|
2412
|
+
"annotations",
|
|
2413
|
+
"labels",
|
|
2414
|
+
"matchLabels",
|
|
2415
|
+
"managedFields",
|
|
2416
|
+
"ownerReferences",
|
|
2417
|
+
"creationTimestamp",
|
|
2418
|
+
"resourceVersion",
|
|
2419
|
+
"uid",
|
|
2420
|
+
"selfLink",
|
|
2421
|
+
"generation",
|
|
2422
|
+
"finalizers"
|
|
2423
|
+
]);
|
|
2424
|
+
function joinable(v) {
|
|
2425
|
+
return v.length >= MIN_NAME_LEN && v.length <= MAX_NAME_LEN && !/\s/.test(v);
|
|
2426
|
+
}
|
|
2427
|
+
function stringLeaves(attrs) {
|
|
2428
|
+
const out = [];
|
|
2429
|
+
const onPath = /* @__PURE__ */ new Set();
|
|
2430
|
+
const step = (v, key, path, depth) => {
|
|
2431
|
+
if (typeof v === "string") {
|
|
2432
|
+
out.push({ path, key, value: v });
|
|
2433
|
+
return;
|
|
2434
|
+
}
|
|
2435
|
+
if (depth >= MAX_DEPTH || !v || typeof v !== "object") return;
|
|
2436
|
+
if (onPath.has(v)) return;
|
|
2437
|
+
onPath.add(v);
|
|
2438
|
+
if (Array.isArray(v)) {
|
|
2439
|
+
for (const item of v) step(item, key, path, depth + 1);
|
|
2440
|
+
} else {
|
|
2441
|
+
for (const [k, val] of Object.entries(v)) {
|
|
2442
|
+
if (k.startsWith("_") || SKIP_KEYS.has(k)) continue;
|
|
2443
|
+
step(val, k, path ? `${path}.${k}` : k, depth + 1);
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
onPath.delete(v);
|
|
2447
|
+
};
|
|
2448
|
+
for (const [k, v] of Object.entries(attrs ?? {})) {
|
|
2449
|
+
if (k.startsWith("_") || SKIP_KEYS.has(k)) continue;
|
|
2450
|
+
step(v, k, k, 0);
|
|
2451
|
+
}
|
|
2452
|
+
return out;
|
|
2453
|
+
}
|
|
2408
2454
|
function addValueMatchEdges(ir) {
|
|
2409
2455
|
const nodes = ir.nodes;
|
|
2410
2456
|
const owner = /* @__PURE__ */ new Map();
|
|
2411
2457
|
for (const n of nodes) {
|
|
2412
|
-
for (const
|
|
2413
|
-
if (
|
|
2414
|
-
if (!isOwnNameAttr(n.kind,
|
|
2415
|
-
owner.set(
|
|
2458
|
+
for (const { path, key, value } of stringLeaves(n.attrs)) {
|
|
2459
|
+
if (path !== key && path !== `metadata.${key}`) continue;
|
|
2460
|
+
if (!joinable(value) || !isOwnNameAttr(n.kind, key)) continue;
|
|
2461
|
+
owner.set(value, owner.has(value) ? null : n.id);
|
|
2416
2462
|
}
|
|
2417
2463
|
}
|
|
2418
2464
|
if (owner.size === 0) return ir;
|
|
@@ -2420,14 +2466,14 @@ function addValueMatchEdges(ir) {
|
|
|
2420
2466
|
for (const e of ir.edges) declared.add(`${e.from}\0${e.to}`);
|
|
2421
2467
|
const added = /* @__PURE__ */ new Set();
|
|
2422
2468
|
for (const a of nodes) {
|
|
2423
|
-
for (const
|
|
2424
|
-
if (
|
|
2425
|
-
const b = owner.get(
|
|
2469
|
+
for (const { path, value } of stringLeaves(a.attrs)) {
|
|
2470
|
+
if (!joinable(value)) continue;
|
|
2471
|
+
const b = owner.get(value);
|
|
2426
2472
|
if (!b || b === a.id) continue;
|
|
2427
2473
|
const key = `${a.id}\0${b}`;
|
|
2428
2474
|
if (declared.has(key) || added.has(key)) continue;
|
|
2429
2475
|
added.add(key);
|
|
2430
|
-
ir.edges.push({ from: a.id, to: b, kind: "ref", viaAttr:
|
|
2476
|
+
ir.edges.push({ from: a.id, to: b, kind: "ref", viaAttr: path, inferred: true });
|
|
2431
2477
|
}
|
|
2432
2478
|
}
|
|
2433
2479
|
return ir;
|
|
@@ -2440,6 +2486,11 @@ var WORKLOAD_KINDS = /* @__PURE__ */ new Set([
|
|
|
2440
2486
|
"K8s::Apps::DaemonSet",
|
|
2441
2487
|
"K8s::Apps::ReplicaSet"
|
|
2442
2488
|
]);
|
|
2489
|
+
var SELECTOR_CARRIERS = [
|
|
2490
|
+
{ kind: "K8s::Core::Service", read: (spec) => spec?.selector, viaAttr: "selector" },
|
|
2491
|
+
{ kind: "K8s::Policy::PodDisruptionBudget", read: (spec) => labelSelector(spec?.selector), viaAttr: "selector" },
|
|
2492
|
+
{ kind: "K8s::Networking::NetworkPolicy", read: (spec) => labelSelector(spec?.podSelector), viaAttr: "podSelector" }
|
|
2493
|
+
];
|
|
2443
2494
|
function rec(v) {
|
|
2444
2495
|
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
2445
2496
|
}
|
|
@@ -2462,6 +2513,13 @@ function labelRecord(v) {
|
|
|
2462
2513
|
}
|
|
2463
2514
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
2464
2515
|
}
|
|
2516
|
+
function labelSelector(v) {
|
|
2517
|
+
const sel = rec(v);
|
|
2518
|
+
if (!sel) return void 0;
|
|
2519
|
+
const expressions = sel.matchExpressions;
|
|
2520
|
+
if (Array.isArray(expressions) && expressions.length > 0) return void 0;
|
|
2521
|
+
return sel.matchLabels;
|
|
2522
|
+
}
|
|
2465
2523
|
function selects(selector, labels) {
|
|
2466
2524
|
return Object.entries(selector).every(([k, v]) => labels[k] === v);
|
|
2467
2525
|
}
|
|
@@ -2556,15 +2614,19 @@ function deriveK8sEdges(nodes) {
|
|
|
2556
2614
|
const target = candidates.length === 1 ? candidates[0] : candidates.find((p) => namespaceOf(p) === namespaceOf(n));
|
|
2557
2615
|
if (target) add(n.id, target.id, viaAttr);
|
|
2558
2616
|
};
|
|
2617
|
+
const addSelector = (n, raw, viaAttr) => {
|
|
2618
|
+
const selector = labelRecord(raw);
|
|
2619
|
+
if (!selector) return;
|
|
2620
|
+
for (const w of workloads) {
|
|
2621
|
+
if (namespaceOf(w) !== namespaceOf(n)) continue;
|
|
2622
|
+
const labels = templateLabels(w);
|
|
2623
|
+
if (labels && selects(selector, labels)) add(n.id, w.id, viaAttr);
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2559
2626
|
for (const n of k8s) {
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
for (const w of workloads) {
|
|
2564
|
-
if (namespaceOf(w) !== namespaceOf(n)) continue;
|
|
2565
|
-
const labels = templateLabels(w);
|
|
2566
|
-
if (labels && selects(selector, labels)) add(n.id, w.id, "selector");
|
|
2567
|
-
}
|
|
2627
|
+
const carrier = SELECTOR_CARRIERS.find((c) => c.kind === n.kind);
|
|
2628
|
+
if (carrier) {
|
|
2629
|
+
addSelector(n, carrier.read(rec(n.attrs?.spec)), carrier.viaAttr);
|
|
2568
2630
|
continue;
|
|
2569
2631
|
}
|
|
2570
2632
|
if (n.kind === "K8s::Networking::Ingress") {
|
|
@@ -2850,6 +2912,10 @@ async function mapPool(items, width, fn) {
|
|
|
2850
2912
|
await Promise.all(Array.from({ length: Math.max(1, Math.min(width, items.length)) }, () => worker()));
|
|
2851
2913
|
return results;
|
|
2852
2914
|
}
|
|
2915
|
+
function estateMembers(projectDirs) {
|
|
2916
|
+
const names = shortStackNames([...projectDirs]);
|
|
2917
|
+
return projectDirs.map((dir, i) => ({ name: names[i], dir }));
|
|
2918
|
+
}
|
|
2853
2919
|
async function composeEstate(projectDirs, opts = {}) {
|
|
2854
2920
|
const names = shortStackNames(projectDirs);
|
|
2855
2921
|
const stacks = await mapPool(projectDirs, estateReadPool(projectDirs.length), async (dir, i) => ({
|
|
@@ -3942,8 +4008,42 @@ function projectTopology(ir, env, boundContext, sourceRoots) {
|
|
|
3942
4008
|
}
|
|
3943
4009
|
placeHelmReleases(nodes, byContainer, ir, env, boundContext);
|
|
3944
4010
|
nestReleaseBoxes(byContainer);
|
|
4011
|
+
retainCrossLensEdges(ir, projections, nodes, edges, byContainer);
|
|
3945
4012
|
return { ir: { nodes, edges, groups: {} }, byContainer };
|
|
3946
4013
|
}
|
|
4014
|
+
function retainCrossLensEdges(ir, projections, nodes, edges, byContainer) {
|
|
4015
|
+
const cards = new Set(nodes.map((n) => n.id));
|
|
4016
|
+
const perLens = projections.map((p) => new Set(p.ir.nodes.map((n) => n.id)));
|
|
4017
|
+
const pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
4018
|
+
const drawn = new Set(edges.map((e) => pairKey(e.from, e.to)));
|
|
4019
|
+
const parentsOf = /* @__PURE__ */ new Map();
|
|
4020
|
+
for (const [parent, children] of Object.entries(byContainer))
|
|
4021
|
+
for (const c of children) (parentsOf.get(c) ?? parentsOf.set(c, []).get(c)).push(parent);
|
|
4022
|
+
const nests = (outer, inner) => {
|
|
4023
|
+
const seen = /* @__PURE__ */ new Set([inner]);
|
|
4024
|
+
let frontier = [...parentsOf.get(inner) ?? []];
|
|
4025
|
+
while (frontier.length) {
|
|
4026
|
+
const next = [];
|
|
4027
|
+
for (const p of frontier) {
|
|
4028
|
+
if (seen.has(p)) continue;
|
|
4029
|
+
seen.add(p);
|
|
4030
|
+
if (p === outer) return true;
|
|
4031
|
+
for (const up of parentsOf.get(p) ?? []) if (!seen.has(up)) next.push(up);
|
|
4032
|
+
}
|
|
4033
|
+
frontier = next;
|
|
4034
|
+
}
|
|
4035
|
+
return false;
|
|
4036
|
+
};
|
|
4037
|
+
for (const e of ir.edges) {
|
|
4038
|
+
if (e.from === e.to || !cards.has(e.from) || !cards.has(e.to)) continue;
|
|
4039
|
+
if (perLens.some((ids) => ids.has(e.from) && ids.has(e.to))) continue;
|
|
4040
|
+
const key = pairKey(e.from, e.to);
|
|
4041
|
+
if (drawn.has(key)) continue;
|
|
4042
|
+
if (nests(e.from, e.to) || nests(e.to, e.from)) continue;
|
|
4043
|
+
drawn.add(key);
|
|
4044
|
+
edges.push(e);
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
3947
4047
|
function placeHelmReleases(nodes, byContainer, ir, env, boundContext) {
|
|
3948
4048
|
const releases = nodes.filter((n) => n.kind === "Helm::Release");
|
|
3949
4049
|
if (releases.length === 0) return;
|
|
@@ -7765,6 +7865,110 @@ async function openRollbackBranches(projectDir, env) {
|
|
|
7765
7865
|
}
|
|
7766
7866
|
}
|
|
7767
7867
|
|
|
7868
|
+
// src/estate-edges.ts
|
|
7869
|
+
import { statSync as statSync6 } from "node:fs";
|
|
7870
|
+
var rec5 = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
7871
|
+
var str5 = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
|
|
7872
|
+
var realIsDir2 = (p) => {
|
|
7873
|
+
try {
|
|
7874
|
+
return statSync6(p).isDirectory();
|
|
7875
|
+
} catch {
|
|
7876
|
+
return false;
|
|
7877
|
+
}
|
|
7878
|
+
};
|
|
7879
|
+
function declaredDeliveries(n) {
|
|
7880
|
+
const spec = rec5(n.attrs?.spec);
|
|
7881
|
+
if (n.kind === "K8s::Flux::Kustomization") {
|
|
7882
|
+
const path = str5(spec?.path);
|
|
7883
|
+
return path ? [{ from: n.id, path, viaAttr: "reconciles" }] : [];
|
|
7884
|
+
}
|
|
7885
|
+
if (n.kind === "K8s::Argo::Application") {
|
|
7886
|
+
const sources = [spec?.source, ...Array.isArray(spec?.sources) ? spec.sources : []];
|
|
7887
|
+
const out = [];
|
|
7888
|
+
for (const s of sources) {
|
|
7889
|
+
const path = str5(rec5(s)?.path);
|
|
7890
|
+
if (path) out.push({ from: n.id, path, viaAttr: "delivers" });
|
|
7891
|
+
}
|
|
7892
|
+
return out;
|
|
7893
|
+
}
|
|
7894
|
+
return [];
|
|
7895
|
+
}
|
|
7896
|
+
function membershipOf(ir, members) {
|
|
7897
|
+
const named = new Set(members.map((m) => m.name));
|
|
7898
|
+
const byStack = ir.groups?.byStack;
|
|
7899
|
+
const out = /* @__PURE__ */ new Map();
|
|
7900
|
+
const grouped = rec5(byStack);
|
|
7901
|
+
if (grouped) {
|
|
7902
|
+
for (const [name, ids] of Object.entries(grouped)) {
|
|
7903
|
+
if (!named.has(name) || !Array.isArray(ids)) continue;
|
|
7904
|
+
for (const id of ids) if (typeof id === "string") out.set(id, name);
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7907
|
+
if (out.size > 0) return out;
|
|
7908
|
+
const byLength = [...members].sort((a, b) => b.name.length - a.name.length);
|
|
7909
|
+
for (const n of ir.nodes) {
|
|
7910
|
+
const m = byLength.find((c) => n.id.startsWith(`${c.name}/`));
|
|
7911
|
+
if (m) out.set(n.id, m.name);
|
|
7912
|
+
}
|
|
7913
|
+
return out;
|
|
7914
|
+
}
|
|
7915
|
+
function deriveEstateMemberEdges(ir, members, isDir = realIsDir2) {
|
|
7916
|
+
if (members.length < 2) return [];
|
|
7917
|
+
const memberOf = membershipOf(ir, members);
|
|
7918
|
+
if (memberOf.size === 0) return [];
|
|
7919
|
+
const present = new Set(ir.nodes.map((n) => n.id));
|
|
7920
|
+
const pointedAt = /* @__PURE__ */ new Set();
|
|
7921
|
+
for (const e of ir.edges) {
|
|
7922
|
+
const from = memberOf.get(e.from);
|
|
7923
|
+
if (from && from === memberOf.get(e.to)) pointedAt.add(e.to);
|
|
7924
|
+
}
|
|
7925
|
+
const entryNodes = (name, lexicon) => {
|
|
7926
|
+
const own = ir.nodes.filter((n) => memberOf.get(n.id) === name && n.lexicon === lexicon).map((n) => n.id);
|
|
7927
|
+
const entries = own.filter((id) => !pointedAt.has(id));
|
|
7928
|
+
return entries.length > 0 ? entries : own;
|
|
7929
|
+
};
|
|
7930
|
+
const out = [];
|
|
7931
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7932
|
+
for (const n of ir.nodes) {
|
|
7933
|
+
const home = memberOf.get(n.id);
|
|
7934
|
+
if (!home) continue;
|
|
7935
|
+
for (const delivery of declaredDeliveries(n)) {
|
|
7936
|
+
let winner;
|
|
7937
|
+
let tied = false;
|
|
7938
|
+
for (const m of members) {
|
|
7939
|
+
if (m.name === home) continue;
|
|
7940
|
+
const score = pathAlignment(delivery.path, m.dir, isDir);
|
|
7941
|
+
if (score === 0) continue;
|
|
7942
|
+
if (!winner || score > winner.score) {
|
|
7943
|
+
winner = { name: m.name, score };
|
|
7944
|
+
tied = false;
|
|
7945
|
+
} else if (score === winner.score) {
|
|
7946
|
+
tied = true;
|
|
7947
|
+
}
|
|
7948
|
+
}
|
|
7949
|
+
if (!winner || tied) continue;
|
|
7950
|
+
for (const to of entryNodes(winner.name, n.lexicon)) {
|
|
7951
|
+
if (!present.has(to) || to === delivery.from) continue;
|
|
7952
|
+
const key = `${delivery.from}\0${to}`;
|
|
7953
|
+
if (seen.has(key)) continue;
|
|
7954
|
+
seen.add(key);
|
|
7955
|
+
out.push({ from: delivery.from, to, kind: "ref", viaAttr: delivery.viaAttr, inferred: true });
|
|
7956
|
+
}
|
|
7957
|
+
}
|
|
7958
|
+
}
|
|
7959
|
+
return out;
|
|
7960
|
+
}
|
|
7961
|
+
function addEstateMemberEdges(ir, members, isDir = realIsDir2) {
|
|
7962
|
+
const existing = new Set(ir.edges.map((e) => `${e.from}\0${e.to}`));
|
|
7963
|
+
for (const e of deriveEstateMemberEdges(ir, members, isDir)) {
|
|
7964
|
+
const key = `${e.from}\0${e.to}`;
|
|
7965
|
+
if (existing.has(key)) continue;
|
|
7966
|
+
existing.add(key);
|
|
7967
|
+
ir.edges.push(e);
|
|
7968
|
+
}
|
|
7969
|
+
return ir;
|
|
7970
|
+
}
|
|
7971
|
+
|
|
7768
7972
|
// src/events.ts
|
|
7769
7973
|
import { watch, existsSync as existsSync11 } from "node:fs";
|
|
7770
7974
|
import { join as join16 } from "node:path";
|
|
@@ -8996,8 +9200,9 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
8996
9200
|
let metaEnv = cfg.env ?? null;
|
|
8997
9201
|
if (multi) {
|
|
8998
9202
|
ir = await composeEstate(cfg.projectDirs, logical ? { ...opts, detail: 3 } : opts);
|
|
8999
|
-
ir = addValueMatchEdges(ir);
|
|
9000
9203
|
ir = addK8sDeclaredEdges(ir);
|
|
9204
|
+
ir = addValueMatchEdges(ir);
|
|
9205
|
+
ir = addEstateMemberEdges(ir, estateMembers(cfg.projectDirs));
|
|
9001
9206
|
ir = markOperatorHome(ir);
|
|
9002
9207
|
const estateContext = await boundK8sContext(metaEnv ?? void 0);
|
|
9003
9208
|
ir = addClusterAnchorEdges(ir, estateContext);
|
|
@@ -9033,7 +9238,7 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9033
9238
|
ir = joinCiProgress(ir, runner.applyProgress);
|
|
9034
9239
|
} else if (logical) {
|
|
9035
9240
|
const raw = mergeClusterRoot(await graphIr(cfg.projectDir, { ...opts, detail: 3 }), await clusterRootGraphIr(cfg.projectDir, opts));
|
|
9036
|
-
const base = addK8sDeclaredEdges(
|
|
9241
|
+
const base = addValueMatchEdges(addK8sDeclaredEdges(raw));
|
|
9037
9242
|
const { ir: projected, byContainer } = projectTopology(base, metaEnv ?? void 0, await boundK8sContext(metaEnv ?? void 0), [await graphPath(cfg.projectDir, opts), cfg.projectDir]);
|
|
9038
9243
|
const { svg: svg2 } = renderArchitecture(projected, byContainer);
|
|
9039
9244
|
const logicalNote = notesFor("logical", projected, void 0, base.nodes.length);
|
|
@@ -9041,8 +9246,8 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9041
9246
|
} else {
|
|
9042
9247
|
ir = mergeClusterRoot(await graphIr(cfg.projectDir, opts), await clusterRootGraphIr(cfg.projectDir, opts));
|
|
9043
9248
|
if ((opts.detail ?? 2) < 3) ir = pruneImports(ir);
|
|
9044
|
-
ir = addValueMatchEdges(ir);
|
|
9045
9249
|
ir = addK8sDeclaredEdges(ir);
|
|
9250
|
+
ir = addValueMatchEdges(ir);
|
|
9046
9251
|
ir = markOperatorHome(ir);
|
|
9047
9252
|
ir = addClusterAnchorEdges(ir, await boundK8sContext(metaEnv ?? void 0));
|
|
9048
9253
|
}
|
|
@@ -9064,7 +9269,7 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9064
9269
|
const { svg } = renderGraph(ir, multi ? { boxes: "byStack" } : { radial });
|
|
9065
9270
|
const srcZoom = components ? "components" : logical ? "logical" : opts.detail === 1 ? "composites" : opts.detail === 3 ? "attributes" : "resources";
|
|
9066
9271
|
const estateLensNote = multi && components ? "the components lens doesn't apply to a composed estate yet \u2014 showing the composed entity graph" : void 0;
|
|
9067
|
-
const srcNote = multi ? estateLensNote : notesFor(srcZoom, ir, srcCompositeEdgesAttached);
|
|
9272
|
+
const srcNote = multi ? estateLensNote : notesFor(srcZoom, ir, srcCompositeEdgesAttached, void 0, opts.detail ?? 2);
|
|
9068
9273
|
return c.json({
|
|
9069
9274
|
ir,
|
|
9070
9275
|
svg,
|
|
@@ -9173,8 +9378,9 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9173
9378
|
const composed = est.ir;
|
|
9174
9379
|
let ir2 = runtime ? attachRuntimeContainment(composed) : pruneRuntimeChildren(composed);
|
|
9175
9380
|
if ((detail ?? 2) < 3) ir2 = pruneImports(ir2);
|
|
9176
|
-
ir2 = addValueMatchEdges(ir2);
|
|
9177
9381
|
ir2 = addK8sDeclaredEdges(ir2);
|
|
9382
|
+
ir2 = addValueMatchEdges(ir2);
|
|
9383
|
+
ir2 = addEstateMemberEdges(ir2, estateMembers(cfg.projectDirs));
|
|
9178
9384
|
ir2 = markOperatorHome(ir2);
|
|
9179
9385
|
const boundContext2 = await boundK8sContext(env);
|
|
9180
9386
|
ir2 = addClusterAnchorEdges(ir2, boundContext2);
|
|
@@ -9200,11 +9406,12 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9200
9406
|
});
|
|
9201
9407
|
}
|
|
9202
9408
|
const { svg: svg2 } = renderGraph(ir2, { boxes: runtime ? "byContainer" : "byStack" });
|
|
9409
|
+
const zoom2 = runtime ? "runtime" : detail === 1 ? "composites" : detail === 3 ? "attributes" : "resources";
|
|
9203
9410
|
const note2 = [
|
|
9204
9411
|
coverNote,
|
|
9205
9412
|
namespaceJoinNote(est.joined),
|
|
9206
9413
|
namespaceMismatchNote(withoutJoinedMembers(ir2.nodes, est.joined)),
|
|
9207
|
-
|
|
9414
|
+
notesFor(zoom2, ir2, void 0, void 0, detail ?? 2)
|
|
9208
9415
|
].filter(Boolean).join(" \xB7 ");
|
|
9209
9416
|
return c.json({
|
|
9210
9417
|
ir: ir2,
|
|
@@ -9226,15 +9433,15 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9226
9433
|
ir = runtime ? attachRuntimeContainment(ir) : pruneRuntimeChildren(ir);
|
|
9227
9434
|
if (logical) {
|
|
9228
9435
|
const logicalBefore = ir.nodes.length;
|
|
9229
|
-
const { ir: projected, byContainer } = projectTopology(addK8sDeclaredEdges(
|
|
9436
|
+
const { ir: projected, byContainer } = projectTopology(addValueMatchEdges(addK8sDeclaredEdges(ir)), env, boundContext, [await graphPath(cfg.projectDir, opts), cfg.projectDir]);
|
|
9230
9437
|
const { svg: svg2 } = renderArchitecture(projected, byContainer);
|
|
9231
9438
|
const logicalTierNote = tierMismatchNote(projected, beholdConfig.tiers, query.tier);
|
|
9232
9439
|
const logicalNote = [logicalTierNote, notesFor("logical", projected, void 0, logicalBefore)].filter(Boolean).join(" \xB7 ");
|
|
9233
9440
|
return c.json({ ir: projected, svg: svg2, byContainer, meta: { projectDir: cfg.projectDir, env, mode: "logical", ...logicalNote ? { note: logicalNote } : {} } });
|
|
9234
9441
|
}
|
|
9235
9442
|
if ((query.detail ?? 2) < 3) ir = pruneImports(ir);
|
|
9236
|
-
ir = addValueMatchEdges(ir);
|
|
9237
9443
|
ir = addK8sDeclaredEdges(ir);
|
|
9444
|
+
ir = addValueMatchEdges(ir);
|
|
9238
9445
|
ir = markOperatorHome(ir);
|
|
9239
9446
|
ir = addClusterAnchorEdges(ir, boundContext);
|
|
9240
9447
|
let compositeEdgesAttached;
|
|
@@ -9279,8 +9486,8 @@ function createApp(cfg, broadcaster = new Broadcaster(), frames = new FrameBuffe
|
|
|
9279
9486
|
else pruneRuntimeChildren(result.ir);
|
|
9280
9487
|
}
|
|
9281
9488
|
if ((optsFromQuery(new URL(c.req.url)).detail ?? 2) < 3) pruneImports(result.ir);
|
|
9282
|
-
addValueMatchEdges(result.ir);
|
|
9283
9489
|
addK8sDeclaredEdges(result.ir);
|
|
9490
|
+
addValueMatchEdges(result.ir);
|
|
9284
9491
|
addClusterAnchorEdges(result.ir, await boundK8sContext(env));
|
|
9285
9492
|
const { svg } = renderGraph(result.ir, { boxes: "byContainer" });
|
|
9286
9493
|
return c.json({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/behold",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
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": {
|