@kwirthmagnify/kwirth-plugin-status 0.2.5 → 0.2.7
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/back.js +13 -3
- package/front.js +80 -39
- package/package.json +1 -1
package/back.js
CHANGED
|
@@ -41,10 +41,19 @@ var require_kwirth_common_back = __commonJS({
|
|
|
41
41
|
var index_exports = {};
|
|
42
42
|
__export(index_exports, {
|
|
43
43
|
StatusChannel: () => StatusChannel,
|
|
44
|
-
default: () => index_default
|
|
44
|
+
default: () => index_default,
|
|
45
|
+
toStatusEdges: () => toStatusEdges
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(index_exports);
|
|
47
48
|
var import_kwirth_common_back = __toESM(require_kwirth_common_back(), 1);
|
|
49
|
+
var toStatusEdges = (subscriptions) => {
|
|
50
|
+
const edges = [];
|
|
51
|
+
for (const s of subscriptions) {
|
|
52
|
+
const consumerId = s.consumerId ?? s.channelId;
|
|
53
|
+
if (consumerId) edges.push({ providerId: s.providerId, consumerId, since: s.since });
|
|
54
|
+
}
|
|
55
|
+
return edges;
|
|
56
|
+
};
|
|
48
57
|
var statsOf = (p) => {
|
|
49
58
|
if (!p.getStats) return void 0;
|
|
50
59
|
try {
|
|
@@ -257,7 +266,7 @@ var StatusChannel = class {
|
|
|
257
266
|
this.subscriptionsOf = () => {
|
|
258
267
|
if (!this.clusterInfo.getSubscriptions) return [];
|
|
259
268
|
try {
|
|
260
|
-
return this.clusterInfo.getSubscriptions() ?? [];
|
|
269
|
+
return toStatusEdges(this.clusterInfo.getSubscriptions() ?? []);
|
|
261
270
|
} catch {
|
|
262
271
|
return [];
|
|
263
272
|
}
|
|
@@ -300,5 +309,6 @@ var StatusChannel = class {
|
|
|
300
309
|
var index_default = StatusChannel;
|
|
301
310
|
// Annotate the CommonJS export names for ESM import in node:
|
|
302
311
|
0 && (module.exports = {
|
|
303
|
-
StatusChannel
|
|
312
|
+
StatusChannel,
|
|
313
|
+
toStatusEdges
|
|
304
314
|
});
|
package/front.js
CHANGED
|
@@ -96,6 +96,9 @@
|
|
|
96
96
|
var import_material = __toESM(require_material(), 1);
|
|
97
97
|
var StatusIcon = (props) => /* @__PURE__ */ import_react.default.createElement(import_material.SvgIcon, { ...props, viewBox: "0 0 24 24" }, /* @__PURE__ */ import_react.default.createElement("g", { fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("rect", { x: "2.6", y: "4.6", width: "18.8", height: "14.8", rx: "2.2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6 12h2.6l1.5-3.4 2.6 6.8 1.5-3.4H18" })));
|
|
98
98
|
|
|
99
|
+
// src/common/StatusTypes.ts
|
|
100
|
+
var PROVIDER_CONSUMER_PREFIX = "provider:";
|
|
101
|
+
|
|
99
102
|
// src/front/StatusData.ts
|
|
100
103
|
var countUnbrokeredConsumers = (components) => components.reduce((n, c) => {
|
|
101
104
|
if (c.subscribers === void 0 || c.knownConsumers === void 0) return n;
|
|
@@ -122,6 +125,56 @@
|
|
|
122
125
|
var import_react2 = __toESM(require_react(), 1);
|
|
123
126
|
var import_material2 = __toESM(require_material(), 1);
|
|
124
127
|
var import_react3 = __toESM(require_react2(), 1);
|
|
128
|
+
|
|
129
|
+
// src/front/StatusGraph.ts
|
|
130
|
+
var CHANNEL_NODE_PREFIX = "channel:";
|
|
131
|
+
var isProviderConsumer = (consumerId) => consumerId.startsWith(PROVIDER_CONSUMER_PREFIX);
|
|
132
|
+
var consumerNodeId = (consumerId) => isProviderConsumer(consumerId) ? consumerId.slice(PROVIDER_CONSUMER_PREFIX.length) : `${CHANNEL_NODE_PREFIX}${consumerId}`;
|
|
133
|
+
var channelsOf = (edges) => [...new Set(edges.filter((e) => !isProviderConsumer(e.consumerId)).map((e) => e.consumerId))];
|
|
134
|
+
var layerOf = (nodeId, targets) => {
|
|
135
|
+
if (nodeId.startsWith(CHANNEL_NODE_PREFIX)) return "LAST" /* LAST */;
|
|
136
|
+
return targets.has(nodeId) ? void 0 : "FIRST" /* FIRST */;
|
|
137
|
+
};
|
|
138
|
+
var elkGraphOf = (nodeIds, lines) => {
|
|
139
|
+
const targets = new Set(lines.map((l) => l.target));
|
|
140
|
+
return {
|
|
141
|
+
id: "root",
|
|
142
|
+
layoutOptions: {
|
|
143
|
+
"elk.algorithm": "layered",
|
|
144
|
+
/*
|
|
145
|
+
De arriba abajo: los productores en la capa de arriba y los consumidores debajo.
|
|
146
|
+
Se lee como un diagrama de flujo —el dato cae— y aprovecha el ancho de la pantalla,
|
|
147
|
+
que es donde sobra sitio cuando hay muchos nodos.
|
|
148
|
+
*/
|
|
149
|
+
"elk.direction": "DOWN",
|
|
150
|
+
"elk.spacing.nodeNode": "40",
|
|
151
|
+
"elk.layered.spacing.nodeNodeBetweenLayers": "110",
|
|
152
|
+
/*
|
|
153
|
+
UN solo grafo, no uno por componente. De serie elk coloca cada componente conexo por
|
|
154
|
+
su cuenta y luego los apila: un par suelto como sugarless -> sugarless salia en su
|
|
155
|
+
propio bloque de dos filas, con su canal por ENCIMA de productores del bloque grande.
|
|
156
|
+
*/
|
|
157
|
+
"elk.separateConnectedComponents": "false"
|
|
158
|
+
},
|
|
159
|
+
/*
|
|
160
|
+
The layer of each node (see layerOf): channels at the bottom, producers that consume nothing
|
|
161
|
+
at the top, and a provider that consumes another provider left free, so elk puts it below
|
|
162
|
+
what it reads and every line goes down.
|
|
163
|
+
*/
|
|
164
|
+
children: nodeIds.map((id) => {
|
|
165
|
+
const layer = layerOf(id, targets);
|
|
166
|
+
return {
|
|
167
|
+
id,
|
|
168
|
+
width: 230,
|
|
169
|
+
height: 56,
|
|
170
|
+
...layer ? { layoutOptions: { "elk.layered.layering.layerConstraint": layer } } : {}
|
|
171
|
+
};
|
|
172
|
+
}),
|
|
173
|
+
edges: lines.map((l) => ({ id: l.id, sources: [l.source], targets: [l.target] }))
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/front/StatusDiagram.tsx
|
|
125
178
|
var COLOR = {
|
|
126
179
|
["active" /* ACTIVE */]: "#2e7d32",
|
|
127
180
|
["idle" /* IDLE */]: "#616161",
|
|
@@ -132,16 +185,24 @@
|
|
|
132
185
|
["unknown" /* UNKNOWN */]: "#616161"
|
|
133
186
|
};
|
|
134
187
|
var colocar = async (nodos, aristas) => {
|
|
188
|
+
const destinos = new Set(aristas.map((a) => a.target));
|
|
135
189
|
const filas = () => {
|
|
136
190
|
const pos = {};
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
191
|
+
const fila = (n) => {
|
|
192
|
+
switch (layerOf(n.id, destinos)) {
|
|
193
|
+
case "FIRST" /* FIRST */:
|
|
194
|
+
return 0;
|
|
195
|
+
case "LAST" /* LAST */:
|
|
196
|
+
return 2;
|
|
197
|
+
default:
|
|
198
|
+
return 1;
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
for (const f of [0, 1, 2]) {
|
|
202
|
+
nodos.filter((n) => fila(n) === f).forEach((n, i) => {
|
|
203
|
+
pos[n.id] = { x: i * 260, y: f * 220 };
|
|
204
|
+
});
|
|
205
|
+
}
|
|
145
206
|
return pos;
|
|
146
207
|
};
|
|
147
208
|
const loadElk = window.__kwirth__?.loadElk;
|
|
@@ -149,22 +210,7 @@
|
|
|
149
210
|
try {
|
|
150
211
|
const ELK = await loadElk();
|
|
151
212
|
const elk = new ELK();
|
|
152
|
-
const g = await elk.layout(
|
|
153
|
-
id: "root",
|
|
154
|
-
layoutOptions: {
|
|
155
|
-
"elk.algorithm": "layered",
|
|
156
|
-
/*
|
|
157
|
-
De arriba abajo: los productores en la capa de arriba y los consumidores debajo.
|
|
158
|
-
Se lee como un diagrama de flujo —el dato cae— y aprovecha el ancho de la pantalla,
|
|
159
|
-
que es donde sobra sitio cuando hay muchos nodos.
|
|
160
|
-
*/
|
|
161
|
-
"elk.direction": "DOWN",
|
|
162
|
-
"elk.spacing.nodeNode": "40",
|
|
163
|
-
"elk.layered.spacing.nodeNodeBetweenLayers": "110"
|
|
164
|
-
},
|
|
165
|
-
children: nodos.map((n) => ({ id: n.id, width: 230, height: 56 })),
|
|
166
|
-
edges: aristas.map((e) => ({ id: e.id, sources: [e.source], targets: [e.target] }))
|
|
167
|
-
});
|
|
213
|
+
const g = await elk.layout(elkGraphOf(nodos.map((n) => n.id), aristas));
|
|
168
214
|
const pos = {};
|
|
169
215
|
for (const c of g.children ?? []) pos[c.id] = { x: c.x, y: c.y };
|
|
170
216
|
return Object.keys(pos).length === nodos.length ? pos : filas();
|
|
@@ -190,13 +236,13 @@
|
|
|
190
236
|
const { nodos, aristas, canalesSueltos } = import_react2.default.useMemo(() => {
|
|
191
237
|
const productores = inventory.components.filter((c) => c.kind === "provider" /* PROVIDER */ || c.kind === "pluvider" /* PLUVIDER */);
|
|
192
238
|
const idsProductores = new Set(productores.map((p) => p.id));
|
|
193
|
-
const canales =
|
|
239
|
+
const canales = channelsOf(inventory.edges);
|
|
194
240
|
const vecinos = /* @__PURE__ */ new Set();
|
|
195
241
|
if (seleccionado) {
|
|
196
242
|
vecinos.add(seleccionado);
|
|
197
243
|
for (const e of inventory.edges) {
|
|
198
244
|
const origen = e.providerId;
|
|
199
|
-
const destino =
|
|
245
|
+
const destino = consumerNodeId(e.consumerId);
|
|
200
246
|
if (origen === seleccionado) vecinos.add(destino);
|
|
201
247
|
if (destino === seleccionado) vecinos.add(origen);
|
|
202
248
|
}
|
|
@@ -248,21 +294,16 @@
|
|
|
248
294
|
}))
|
|
249
295
|
];
|
|
250
296
|
const aristas2 = inventory.edges.map((e) => ({
|
|
251
|
-
id: `${e.providerId}->${e.
|
|
297
|
+
id: `${e.providerId}->${e.consumerId}`,
|
|
252
298
|
source: e.providerId,
|
|
253
|
-
target:
|
|
299
|
+
target: consumerNodeId(e.consumerId),
|
|
254
300
|
/*
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
mismo error que poner un 0 donde no hay dato — parecería información y sería decoración.
|
|
260
|
-
|
|
261
|
-
Cuando los contadores de S4 midan caudal de verdad, el movimiento (o el grosor) podrá
|
|
262
|
-
significar algo, y entonces se pone.
|
|
263
|
-
*/
|
|
301
|
+
Una línea en movimiento se lee como "por aquí está pasando algo ahora mismo", así que
|
|
302
|
+
solo se mueve cuando eso se ha MEDIDO (ver 'viva'). Quieta, lo único que dice es que la
|
|
303
|
+
suscripción existe. Cómo frena con auto-refresco está en el contenedor del ReactFlow.
|
|
304
|
+
*/
|
|
264
305
|
...(() => {
|
|
265
|
-
const tocaAlSeleccionado = Boolean(seleccionado) && (e.providerId === seleccionado ||
|
|
306
|
+
const tocaAlSeleccionado = Boolean(seleccionado) && (e.providerId === seleccionado || consumerNodeId(e.consumerId) === seleccionado);
|
|
266
307
|
const viva = active.has(e.providerId);
|
|
267
308
|
const color = tocaAlSeleccionado ? "#7fd8b0" : viva ? "#5fc79a" : "#4a8";
|
|
268
309
|
const ancho = tocaAlSeleccionado ? 3 : viva ? 2 : 1;
|
|
@@ -275,7 +316,7 @@
|
|
|
275
316
|
markerEnd: { type: import_react3.MarkerType.ArrowClosed, color, width: punta, height: punta }
|
|
276
317
|
};
|
|
277
318
|
})()
|
|
278
|
-
})).filter((e) => idsProductores.has(e.source));
|
|
319
|
+
})).filter((e) => idsProductores.has(e.source) && (e.target.startsWith(CHANNEL_NODE_PREFIX) || idsProductores.has(e.target)));
|
|
279
320
|
const canalesSueltos2 = inventory.edges.length - aristas2.length;
|
|
280
321
|
return { nodos: nodos2, aristas: aristas2, canalesSueltos: canalesSueltos2 };
|
|
281
322
|
}, [inventory, active, seleccionado, colores.fondoNodo, colores.fondoCanal, colores.texto, colores.bordeCanal]);
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"id": "status",
|
|
5
5
|
"name": "@kwirthmagnify/kwirth-plugin-status",
|
|
6
6
|
"displayName": "Kwirth Status",
|
|
7
|
-
"version": "0.2.
|
|
7
|
+
"version": "0.2.7",
|
|
8
8
|
"description": "A look inside Kwirth: what is installed, how it is doing and who consumes what",
|
|
9
9
|
"icon": "<svg viewBox='0 0 24 24'><g fill='none' stroke='currentColor' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'><rect x='2.6' y='4.6' width='18.8' height='14.8' rx='2.2'/><path d='M6 12h2.6l1.5-3.4 2.6 6.8 1.5-3.4H18'/></g></svg>",
|
|
10
10
|
"requiresRestart": false,
|