shokupan 0.10.5 → 0.11.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/{analyzer-CKLGLFtx.cjs → analyzer-BAhvpNY_.cjs} +2 -7
- package/dist/{analyzer-CKLGLFtx.cjs.map → analyzer-BAhvpNY_.cjs.map} +1 -1
- package/dist/{analyzer-BqIe1p0R.js → analyzer-CnKnQ5KV.js} +3 -8
- package/dist/{analyzer-BqIe1p0R.js.map → analyzer-CnKnQ5KV.js.map} +1 -1
- package/dist/{analyzer.impl-D9Yi1Hax.cjs → analyzer.impl-CfpMu4-g.cjs} +586 -40
- package/dist/analyzer.impl-CfpMu4-g.cjs.map +1 -0
- package/dist/{analyzer.impl-CV6W1Eq7.js → analyzer.impl-DCiqlXI5.js} +586 -40
- package/dist/analyzer.impl-DCiqlXI5.js.map +1 -0
- package/dist/cli.cjs +206 -18
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +206 -18
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts +6 -1
- package/dist/index.cjs +2339 -984
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2336 -982
- package/dist/index.js.map +1 -1
- package/dist/plugins/application/api-explorer/static/explorer-client.mjs +375 -29
- package/dist/plugins/application/api-explorer/static/style.css +327 -8
- package/dist/plugins/application/api-explorer/static/theme.css +7 -2
- package/dist/plugins/application/asyncapi/generator.d.ts +4 -0
- package/dist/plugins/application/asyncapi/static/asyncapi-client.mjs +154 -22
- package/dist/plugins/application/asyncapi/static/style.css +24 -8
- package/dist/plugins/application/dashboard/fetch-interceptor.d.ts +107 -0
- package/dist/plugins/application/dashboard/metrics-collector.d.ts +38 -2
- package/dist/plugins/application/dashboard/plugin.d.ts +44 -1
- package/dist/plugins/application/dashboard/static/charts.js +127 -62
- package/dist/plugins/application/dashboard/static/client.js +160 -0
- package/dist/plugins/application/dashboard/static/graph.mjs +167 -56
- package/dist/plugins/application/dashboard/static/reactflow.css +20 -10
- package/dist/plugins/application/dashboard/static/registry.js +112 -8
- package/dist/plugins/application/dashboard/static/requests.js +868 -58
- package/dist/plugins/application/dashboard/static/styles.css +186 -14
- package/dist/plugins/application/dashboard/static/tabs.js +44 -9
- package/dist/plugins/application/dashboard/static/theme.css +7 -2
- package/dist/plugins/application/openapi/analyzer.impl.d.ts +61 -1
- package/dist/plugins/application/openapi/openapi.d.ts +3 -0
- package/dist/plugins/application/shared/ast-utils.d.ts +7 -0
- package/dist/router.d.ts +55 -16
- package/dist/shokupan.d.ts +7 -2
- package/dist/util/adapter/adapters.d.ts +19 -0
- package/dist/util/adapter/filesystem.d.ts +20 -0
- package/dist/util/controller-scanner.d.ts +4 -0
- package/dist/util/cpu-monitor.d.ts +2 -0
- package/dist/util/middleware-tracker.d.ts +10 -0
- package/dist/util/types.d.ts +37 -0
- package/package.json +5 -5
- package/dist/analyzer.impl-CV6W1Eq7.js.map +0 -1
- package/dist/analyzer.impl-D9Yi1Hax.cjs.map +0 -1
- package/dist/http-server-BEMPIs33.cjs +0 -85
- package/dist/http-server-BEMPIs33.cjs.map +0 -1
- package/dist/http-server-CCeagTyU.js +0 -68
- package/dist/http-server-CCeagTyU.js.map +0 -1
- package/dist/plugins/application/dashboard/static/poll.js +0 -146
|
@@ -18,6 +18,10 @@ function renderPath(path) {
|
|
|
18
18
|
|
|
19
19
|
let out = '';
|
|
20
20
|
parts.forEach((part, index) => {
|
|
21
|
+
if (part === '.well-known') {
|
|
22
|
+
out += `/<span class="path-segment" style="color: #8b5cf6; font-weight: bold;">${part}</span>`;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
21
25
|
if (part.startsWith(":")) {
|
|
22
26
|
out += `/<span class="path-segment path-param">${part}</span>`;
|
|
23
27
|
return;
|
|
@@ -35,8 +39,44 @@ function renderPath(path) {
|
|
|
35
39
|
const GroupNode = ({ data }) => {
|
|
36
40
|
return React.createElement('div', { style: { padding: '10px', height: '100%' } },
|
|
37
41
|
React.createElement('div', { style: { fontWeight: 'bold', borderBottom: '1px solid rgba(255,255,255,0.1)', paddingBottom: '5px', marginBottom: '5px' } },
|
|
38
|
-
data.type === "controller" ? data.label : "Router: " + data.label
|
|
42
|
+
data.type === "controller" ? data.label : (data.data.metadata?.pluginName ? data.label : "Router: " + data.label)
|
|
43
|
+
),
|
|
44
|
+
/* Middleware Rendering */
|
|
45
|
+
(data.data?.children?.middleware || [])?.map((mw, i) =>
|
|
46
|
+
React.createElement('div', {
|
|
47
|
+
key: 'mw-' + i, style: {
|
|
48
|
+
background: '#7e22ce20', color: '#fff', padding: '2px 6px', borderRadius: '4px', fontSize: '10px', margin: '2px 0 4px 0', border: '1px solid #6b21a8',
|
|
49
|
+
display: 'flex', alignItems: 'center', justifyContent: 'space-between'
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
React.createElement('span', { style: { fontWeight: 'bold' } }, "MW"),
|
|
53
|
+
React.createElement('span', { style: { font: 'var(--shokupan-font-mono)' } }, mw.name || "Middleware")
|
|
54
|
+
)
|
|
55
|
+
),
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/* Event Rendering */
|
|
59
|
+
(data.data?.children?.events || [])?.map((ev, i) =>
|
|
60
|
+
React.createElement('div', { key: 'ev-' + i, style: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: '12px', margin: '2px 0' } },
|
|
61
|
+
React.createElement('span', {
|
|
62
|
+
style: {
|
|
63
|
+
padding: '2px 6px',
|
|
64
|
+
borderRadius: '4px',
|
|
65
|
+
background: '#60a5fa20',
|
|
66
|
+
border: '1px solid #60a5fa',
|
|
67
|
+
color: 'white',
|
|
68
|
+
fontWeight: 'bold',
|
|
69
|
+
fontSize: '10px',
|
|
70
|
+
minWidth: '40px',
|
|
71
|
+
textAlign: 'center'
|
|
72
|
+
}
|
|
73
|
+
}, "WS"),
|
|
74
|
+
React.createElement('span', {
|
|
75
|
+
style: { font: 'var(--shokupan-font-mono)', color: '#cbd5e1' }
|
|
76
|
+
}, ev.name)
|
|
77
|
+
)
|
|
39
78
|
),
|
|
79
|
+
|
|
40
80
|
data.data?.children?.routes?.map((r, i) =>
|
|
41
81
|
React.createElement('div', { key: i, style: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: '12px', margin: '2px 0' } },
|
|
42
82
|
React.createElement('span', {
|
|
@@ -60,7 +100,7 @@ const GroupNode = ({ data }) => {
|
|
|
60
100
|
}
|
|
61
101
|
}, r.method),
|
|
62
102
|
React.createElement('span', {
|
|
63
|
-
style: {
|
|
103
|
+
style: { font: 'var(--shokupan-font-mono)', color: r.isFailed ? '#ef4444' : '#cbd5e1', fontWeight: r.isFailed ? 'bold' : 'normal' },
|
|
64
104
|
dangerouslySetInnerHTML: { __html: renderPath(r.path) }
|
|
65
105
|
})
|
|
66
106
|
)
|
|
@@ -208,9 +248,59 @@ const GraphComponent = () => {
|
|
|
208
248
|
// Label logic matches GroupNode
|
|
209
249
|
header.textContent = container.type === "controller"
|
|
210
250
|
? (container.name + (container.metadata?.pluginName ? `\n[${container.metadata.pluginName}]` : ''))
|
|
211
|
-
: "Router: " + container.path;
|
|
251
|
+
: (container.metadata?.pluginName ? `[${container.metadata.pluginName}]` : "Router: " + container.path);
|
|
212
252
|
nodeEl.appendChild(header);
|
|
213
253
|
|
|
254
|
+
// Mimic Middleware
|
|
255
|
+
const middleware = container.children?.middleware || [];
|
|
256
|
+
for (const mw of middleware) {
|
|
257
|
+
const mwEl = document.createElement("div");
|
|
258
|
+
mwEl.style.display = "flex";
|
|
259
|
+
mwEl.style.alignItems = "center";
|
|
260
|
+
mwEl.style.justifyContent = "space-between";
|
|
261
|
+
mwEl.style.padding = "2px 6px";
|
|
262
|
+
mwEl.style.fontSize = "10px";
|
|
263
|
+
mwEl.style.margin = "2px 0 4px 0";
|
|
264
|
+
mwEl.style.border = "1px solid transparent"; // Match border in component
|
|
265
|
+
|
|
266
|
+
const label = document.createElement("span");
|
|
267
|
+
label.textContent = "MW";
|
|
268
|
+
label.style.fontWeight = "bold";
|
|
269
|
+
mwEl.appendChild(label);
|
|
270
|
+
|
|
271
|
+
const val = document.createElement("span");
|
|
272
|
+
val.textContent = mw.name || "Middleware";
|
|
273
|
+
val.style.font = "var(--shokupan-font-mono)";
|
|
274
|
+
mwEl.appendChild(val);
|
|
275
|
+
|
|
276
|
+
nodeEl.appendChild(mwEl);
|
|
277
|
+
nodeEl.appendChild(mwEl);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Mimic Events
|
|
281
|
+
const events = container.children?.events || [];
|
|
282
|
+
for (const ev of events) {
|
|
283
|
+
const row = document.createElement("div");
|
|
284
|
+
row.style.display = "flex";
|
|
285
|
+
row.style.alignItems = "center";
|
|
286
|
+
row.style.gap = "8px";
|
|
287
|
+
row.style.margin = "2px 0";
|
|
288
|
+
|
|
289
|
+
const badge = document.createElement("span");
|
|
290
|
+
badge.textContent = "WS";
|
|
291
|
+
badge.style.padding = "2px 6px";
|
|
292
|
+
badge.style.fontSize = "10px";
|
|
293
|
+
badge.style.fontWeight = "bold";
|
|
294
|
+
row.appendChild(badge);
|
|
295
|
+
|
|
296
|
+
const name = document.createElement("span");
|
|
297
|
+
name.textContent = ev.name;
|
|
298
|
+
name.style.font = "var(--shokupan-font-mono)";
|
|
299
|
+
row.appendChild(name);
|
|
300
|
+
|
|
301
|
+
nodeEl.appendChild(row);
|
|
302
|
+
}
|
|
303
|
+
|
|
214
304
|
// Mimic Routes
|
|
215
305
|
for (const route of routes) {
|
|
216
306
|
const row = document.createElement("div");
|
|
@@ -228,7 +318,7 @@ const GraphComponent = () => {
|
|
|
228
318
|
|
|
229
319
|
const path = document.createElement("span");
|
|
230
320
|
path.textContent = route.path;
|
|
231
|
-
path.style.
|
|
321
|
+
path.style.font = "var(--shokupan-font-mono)";
|
|
232
322
|
// path.style.color... doesn't affect size
|
|
233
323
|
row.appendChild(path);
|
|
234
324
|
|
|
@@ -250,34 +340,36 @@ const GraphComponent = () => {
|
|
|
250
340
|
|
|
251
341
|
const elkEdges = [];
|
|
252
342
|
const elkNodes = [
|
|
253
|
-
|
|
254
|
-
const id = makeId("middleware", parentId, idx, mw.name);
|
|
255
|
-
return {
|
|
256
|
-
id,
|
|
257
|
-
label: mw.metadata?.pluginName || mw.name || "Unknown Middleware",
|
|
258
|
-
...calculateNodeBounds(mw),
|
|
259
|
-
// width: 140,
|
|
260
|
-
// height: 40,
|
|
261
|
-
type: "middleware",
|
|
262
|
-
style: getNodeStyle(id),
|
|
263
|
-
data: mw
|
|
264
|
-
};
|
|
265
|
-
}),
|
|
343
|
+
// Middleware nodes removed
|
|
266
344
|
...(routers || []).map((r, idx) => {
|
|
267
345
|
const id = makeId("router", parentId, idx, r.path);
|
|
268
346
|
const { nodes, edges } = addRecursedLevel(r.children, id);
|
|
269
347
|
restChildrenNodes.push(...nodes);
|
|
270
348
|
restChildrenEdges.push(...edges);
|
|
271
349
|
|
|
350
|
+
const isPlugin = r.metadata?.pluginName;
|
|
351
|
+
const label = isPlugin
|
|
352
|
+
? `[${r.metadata.pluginName}]`
|
|
353
|
+
: r.path;
|
|
354
|
+
|
|
355
|
+
const baseStyle = getNodeStyle(id);
|
|
356
|
+
const routerStyle = isPlugin
|
|
357
|
+
? {
|
|
358
|
+
...baseStyle,
|
|
359
|
+
background: '#f59e0b10',
|
|
360
|
+
color: '#f59e0b',
|
|
361
|
+
border: '1px solid #f59e0b'
|
|
362
|
+
}
|
|
363
|
+
: {
|
|
364
|
+
...baseStyle
|
|
365
|
+
};
|
|
366
|
+
|
|
272
367
|
return {
|
|
273
368
|
id,
|
|
274
|
-
label:
|
|
369
|
+
label: label,
|
|
275
370
|
...calculateNodeBounds(r),
|
|
276
371
|
type: "router",
|
|
277
|
-
style:
|
|
278
|
-
...getNodeStyle(id),
|
|
279
|
-
backgroundColor: 'red'
|
|
280
|
-
},
|
|
372
|
+
style: routerStyle,
|
|
281
373
|
data: r
|
|
282
374
|
};
|
|
283
375
|
}),
|
|
@@ -305,33 +397,12 @@ const GraphComponent = () => {
|
|
|
305
397
|
};
|
|
306
398
|
});
|
|
307
399
|
|
|
308
|
-
|
|
309
|
-
// Create middleware edges
|
|
310
|
-
middleware?.forEach((mw, idx) => {
|
|
311
|
-
const id = makeId("middleware", parentId, idx, mw.name);
|
|
312
|
-
|
|
313
|
-
let sourceId = idx === 0 ?
|
|
314
|
-
parentId ? parentId : "entrypoint-http"
|
|
315
|
-
: makeId("middleware", parentId, idx - 1, middleware[idx - 1].name);
|
|
316
|
-
|
|
317
|
-
elkEdges.push({
|
|
318
|
-
id,
|
|
319
|
-
sources: [sourceId],
|
|
320
|
-
targets: [id],
|
|
321
|
-
type: "straight",
|
|
322
|
-
style: {
|
|
323
|
-
...getEdgeStyle(mw),
|
|
324
|
-
backgroundColor: 'blue'
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
lastMiddlewareId = id;
|
|
328
|
-
});
|
|
329
|
-
|
|
400
|
+
// Simplified Edge Creation - Direct connections
|
|
330
401
|
routers?.forEach((r, idx) => {
|
|
331
402
|
const id = makeId("router", parentId, idx, r.path);
|
|
332
403
|
elkEdges.push({
|
|
333
|
-
id
|
|
334
|
-
sources: [
|
|
404
|
+
id: `edge_${parentId}_${id}`,
|
|
405
|
+
sources: [parentId],
|
|
335
406
|
targets: [id],
|
|
336
407
|
style: {
|
|
337
408
|
...getEdgeStyle(r),
|
|
@@ -341,10 +412,9 @@ const GraphComponent = () => {
|
|
|
341
412
|
});
|
|
342
413
|
controllers?.forEach((ctrl, idx) => {
|
|
343
414
|
const id = makeId("controller", parentId, idx, ctrl.path);
|
|
344
|
-
console.log({ id, lastMiddlewareId });
|
|
345
415
|
elkEdges.push({
|
|
346
|
-
id
|
|
347
|
-
sources: [
|
|
416
|
+
id: `edge_${parentId}_${id}`,
|
|
417
|
+
sources: [parentId],
|
|
348
418
|
targets: [id],
|
|
349
419
|
style: {
|
|
350
420
|
...getEdgeStyle(ctrl),
|
|
@@ -359,10 +429,48 @@ const GraphComponent = () => {
|
|
|
359
429
|
return { nodes, edges };
|
|
360
430
|
}
|
|
361
431
|
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
432
|
+
const rootNodeId = "router_root";
|
|
433
|
+
const rootNodeData = {
|
|
434
|
+
type: 'router',
|
|
435
|
+
metadata: { name: 'Root Application' },
|
|
436
|
+
path: '/',
|
|
437
|
+
children: {
|
|
438
|
+
routes: registryData.routes,
|
|
439
|
+
middleware: registryData.middleware,
|
|
440
|
+
events: registryData.events
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
// Pass rootNodeId as parent to children
|
|
445
|
+
const { nodes: childrenNodes, edges: childrenEdges } = addRecursedLevel(registryData, rootNodeId);
|
|
446
|
+
|
|
447
|
+
// Create explicit Root Node
|
|
448
|
+
const rootNode = {
|
|
449
|
+
id: rootNodeId,
|
|
450
|
+
label: "Root Application",
|
|
451
|
+
...calculateNodeBounds(rootNodeData),
|
|
452
|
+
type: "router",
|
|
453
|
+
style: getNodeStyle(rootNodeId),
|
|
454
|
+
data: rootNodeData
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// Combine all nodes
|
|
458
|
+
const elkNodes = [
|
|
459
|
+
rootNode,
|
|
460
|
+
...childrenNodes,
|
|
461
|
+
{ id: "entrypoint-http", width: 64, height: 64, type: "entrypoint" }
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
// Connect Entrypoint -> Root
|
|
465
|
+
const entrypointEdge = {
|
|
466
|
+
id: 'edge_entrypoint_root',
|
|
467
|
+
sources: ['entrypoint-http'],
|
|
468
|
+
targets: [rootNodeId],
|
|
469
|
+
type: "straight",
|
|
470
|
+
style: { stroke: '#3b82f6' }
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const elkEdges = [entrypointEdge, ...childrenEdges];
|
|
366
474
|
|
|
367
475
|
const nodeNodeGap = '20';
|
|
368
476
|
const nodeEdgeGap = '20';
|
|
@@ -378,6 +486,7 @@ const GraphComponent = () => {
|
|
|
378
486
|
'elk.layered.spacing.edgeNodeBetweenLayers': nodeEdgeGap,
|
|
379
487
|
'elk.layered.wrapping.additionalEdgeSpacing': nodeEdgeGap,
|
|
380
488
|
'elk.layered.nodePlacement.strategy': 'NETWORK_SIMPLEX',
|
|
489
|
+
'elk.layered.nodePlacement.bk.fixedAlignment': 'LEFTUP',
|
|
381
490
|
},
|
|
382
491
|
children: elkNodes,
|
|
383
492
|
edges: elkEdges
|
|
@@ -394,6 +503,7 @@ const GraphComponent = () => {
|
|
|
394
503
|
}
|
|
395
504
|
|
|
396
505
|
const style = NODE_STYLES[node.type] || {};
|
|
506
|
+
const customStyle = node.style || {};
|
|
397
507
|
const isGroup = node.children && node.children.length > 0;
|
|
398
508
|
|
|
399
509
|
flowNodes.push({
|
|
@@ -402,6 +512,7 @@ const GraphComponent = () => {
|
|
|
402
512
|
data: node,
|
|
403
513
|
style: {
|
|
404
514
|
...style,
|
|
515
|
+
...customStyle,
|
|
405
516
|
width: node.width,
|
|
406
517
|
height: node.height,
|
|
407
518
|
zIndex: isGroup ? -1 : 1
|
|
@@ -450,10 +561,10 @@ const GraphComponent = () => {
|
|
|
450
561
|
width: '100vw',
|
|
451
562
|
height: '100vh',
|
|
452
563
|
zIndex: 9999,
|
|
453
|
-
backgroundColor: '
|
|
564
|
+
backgroundColor: 'var(--bg-primary)' // Match theme
|
|
454
565
|
} : {
|
|
455
566
|
width: '100%',
|
|
456
|
-
height: '
|
|
567
|
+
height: '100%',
|
|
457
568
|
position: 'relative'
|
|
458
569
|
}
|
|
459
570
|
},
|
|
@@ -487,8 +598,8 @@ const GraphComponent = () => {
|
|
|
487
598
|
color: 'white',
|
|
488
599
|
padding: '6px 12px',
|
|
489
600
|
borderRadius: '6px',
|
|
601
|
+
font: 'var(--shokupan-font-mono)',
|
|
490
602
|
fontSize: '12px',
|
|
491
|
-
fontFamily: 'monospace',
|
|
492
603
|
border: '1px solid #475569'
|
|
493
604
|
}
|
|
494
605
|
}, `${Math.round(zoom * 100)}%`),
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
.react-flow__controls-button {
|
|
2
|
-
color: #000 !important;
|
|
3
|
-
fill: #000 !important;
|
|
4
|
-
border-bottom: 1px solid #eee;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.react-flow__controls-button svg {
|
|
8
|
-
fill: currentColor;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
1
|
.react-flow__node.react-flow__node-router {
|
|
12
2
|
height: fit-content !important;
|
|
13
3
|
}
|
|
@@ -15,4 +5,24 @@
|
|
|
15
5
|
.react-flow__handle {
|
|
16
6
|
opacity: 0;
|
|
17
7
|
pointer-events: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.react-flow__attribution {
|
|
11
|
+
background-color: var(--palette-dark-secondary);
|
|
12
|
+
border-top-left-radius: 4px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.react-flow__attribution a {
|
|
16
|
+
color: #000;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.react-flow__controls {
|
|
22
|
+
--xy-controls-button-background-color: var(--bg-primary);
|
|
23
|
+
--xy-controls-button-border-color-props: #202020;
|
|
24
|
+
--xy-controls-button-color: var(--text-primary);
|
|
25
|
+
|
|
26
|
+
--xy-controls-button-color-hover: var(--palette-dark-primary);
|
|
27
|
+
--xy-controls-button-background-color-hover: var(--bg-primary);
|
|
18
28
|
}
|
|
@@ -1,18 +1,81 @@
|
|
|
1
1
|
|
|
2
2
|
window.renderRegistry = function renderRegistry(node, container) {
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const config = window.SHOKUPAN_CONFIG || {};
|
|
4
|
+
const rootPath = config.rootPath || "";
|
|
5
|
+
const linkPattern = config.linkPattern || "vscode://file/{{absolute}}:{{line}}";
|
|
5
6
|
|
|
6
7
|
if (!node) {
|
|
7
8
|
container.innerHTML = '<div style="color: var(--text-secondary)">No registry data available</div>';
|
|
8
9
|
return;
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
// 0. Pre-process paths for shortening
|
|
13
|
+
// Collect all paths
|
|
14
|
+
const allFilePaths = new Set();
|
|
15
|
+
const collectPaths = (n) => {
|
|
16
|
+
if (!n) return;
|
|
17
|
+
if (n.metadata && n.metadata.file) allFilePaths.add(n.metadata.file);
|
|
18
|
+
if (n.middleware) n.middleware.forEach(collectPaths);
|
|
19
|
+
if (n.routers) n.routers.forEach(collectPaths);
|
|
20
|
+
if (n.controllers) n.controllers.forEach(collectPaths);
|
|
21
|
+
if (n.routes) n.routes.forEach(collectPaths);
|
|
22
|
+
if (n.events) n.events.forEach(collectPaths);
|
|
23
|
+
if (n.children) collectPaths(n.children); // recursive for routers
|
|
24
|
+
};
|
|
25
|
+
collectPaths(node);
|
|
26
|
+
|
|
27
|
+
// Compute Shortest Unique Paths
|
|
28
|
+
const shortPathMap = {};
|
|
29
|
+
const pathsByFilename = {};
|
|
30
|
+
|
|
31
|
+
allFilePaths.forEach(p => {
|
|
32
|
+
const parts = p.split('/');
|
|
33
|
+
const filename = parts.pop();
|
|
34
|
+
if (!pathsByFilename[filename]) pathsByFilename[filename] = [];
|
|
35
|
+
pathsByFilename[filename].push({ full: p, parts: parts });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
Object.keys(pathsByFilename).forEach(filename => {
|
|
39
|
+
const group = pathsByFilename[filename];
|
|
40
|
+
if (group.length === 1) {
|
|
41
|
+
shortPathMap[group[0].full] = filename;
|
|
42
|
+
} else {
|
|
43
|
+
// Collision handling
|
|
44
|
+
group.forEach(item => {
|
|
45
|
+
let suffix = filename;
|
|
46
|
+
let depth = 0;
|
|
47
|
+
// Add parent dirs until unique within this group
|
|
48
|
+
// Note: This is a simple approach. Strictly we need to check uniqueness against ALL other paths in group.
|
|
49
|
+
while (true) {
|
|
50
|
+
if (depth >= item.parts.length) break; // Should not happen if they are different files
|
|
51
|
+
|
|
52
|
+
// Check if current suffix is unique in group
|
|
53
|
+
const conflicts = group.filter(g => {
|
|
54
|
+
if (g.full === item.full) return false;
|
|
55
|
+
// Construct suffix for g with same depth
|
|
56
|
+
const gParts = g.full.split('/');
|
|
57
|
+
const gSuffix = gParts.slice(gParts.length - 1 - depth).join('/');
|
|
58
|
+
return gSuffix === suffix;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
if (conflicts.length === 0) break;
|
|
62
|
+
|
|
63
|
+
// prepend parent
|
|
64
|
+
const parent = item.parts[item.parts.length - 1 - depth];
|
|
65
|
+
suffix = parent + '/' + suffix;
|
|
66
|
+
depth++;
|
|
67
|
+
}
|
|
68
|
+
shortPathMap[item.full] = suffix;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
11
73
|
const wrapper = document.createElement('div');
|
|
12
74
|
|
|
13
75
|
// Helper to clean paths
|
|
14
76
|
const cleanPath = (p) => {
|
|
15
77
|
if (!p) return '';
|
|
78
|
+
if (shortPathMap[p]) return shortPathMap[p];
|
|
16
79
|
if (p.startsWith(rootPath)) return p.slice(rootPath.length + 1);
|
|
17
80
|
return p;
|
|
18
81
|
};
|
|
@@ -42,7 +105,13 @@ window.renderRegistry = function renderRegistry(node, container) {
|
|
|
42
105
|
const builtin = metadata.isBuiltin ? `<span class="badge" style="background: #059669; margin-left:10px;">BUILTIN</span>` : '';
|
|
43
106
|
const pluginName = metadata.pluginName ? `<span style="color: #6ee7b7; margin-left: 5px;">[${metadata.pluginName}]</span>` : '';
|
|
44
107
|
|
|
45
|
-
|
|
108
|
+
// Use relative (short) path for display
|
|
109
|
+
return `<a href="${link}" style="text-decoration: none; color: inherit;">
|
|
110
|
+
<span class="tree-meta" style="cursor: pointer; text-decoration: underline;">
|
|
111
|
+
${relative}:${line}</span>
|
|
112
|
+
</a>
|
|
113
|
+
${!metadata.pluginName ? displayNameStr : ''} ${builtin} ${pluginName}
|
|
114
|
+
`;
|
|
46
115
|
}
|
|
47
116
|
return '';
|
|
48
117
|
};
|
|
@@ -53,9 +122,10 @@ window.renderRegistry = function renderRegistry(node, container) {
|
|
|
53
122
|
if (node.routes) node.routes.forEach(i => allItems.push({ ...i, kind: 'route' }));
|
|
54
123
|
if (node.routers) node.routers.forEach(i => allItems.push({ ...i, kind: 'router' }));
|
|
55
124
|
if (node.controllers) node.controllers.forEach(i => allItems.push({ ...i, kind: 'controller' }));
|
|
125
|
+
if (node.events) node.events.forEach(i => allItems.push({ ...i, kind: 'event' }));
|
|
56
126
|
|
|
57
127
|
// 2. Sort by Order
|
|
58
|
-
const kindPriority = { 'middleware': 0, 'router': 1, 'controller': 2, 'route': 3 };
|
|
128
|
+
const kindPriority = { 'middleware': 0, 'router': 1, 'controller': 2, 'route': 3, 'event': 4 };
|
|
59
129
|
allItems.sort((a, b) => {
|
|
60
130
|
const pA = kindPriority[a.kind] !== undefined ? kindPriority[a.kind] : 99;
|
|
61
131
|
const pB = kindPriority[b.kind] !== undefined ? kindPriority[b.kind] : 99;
|
|
@@ -101,9 +171,9 @@ window.renderRegistry = function renderRegistry(node, container) {
|
|
|
101
171
|
return `
|
|
102
172
|
<div class="tooltip-text">
|
|
103
173
|
<div style="font-weight:bold; margin-bottom:4px; border-bottom:1px solid var(--text-secondary); padding-bottom:2px;">Metrics</div>
|
|
104
|
-
<div style="display:flex; justify-content:space-between;"><span>Requests:</span> <span style="font-
|
|
105
|
-
<div style="display:flex; justify-content:space-between;"><span>Traffic:</span> <span style="font-
|
|
106
|
-
<div style="display:flex; justify-content:space-between;"><span>Failures:</span> <span style="font-
|
|
174
|
+
<div style="display:flex; justify-content:space-between;"><span>Requests:</span> <span style="font: var(--shokupan-font-mono)">${m.requests}</span></div>
|
|
175
|
+
<div style="display:flex; justify-content:space-between;"><span>Traffic:</span> <span style="font: var(--shokupan-font-mono)">${percent}%</span></div>
|
|
176
|
+
<div style="display:flex; justify-content:space-between;"><span>Failures:</span> <span style="font: var(--shokupan-font-mono); color:${m.failures > 0 ? '#ef4444' : 'inherit'}">${m.failures} (${failRate}%)</span></div>
|
|
107
177
|
</div>
|
|
108
178
|
`;
|
|
109
179
|
}
|
|
@@ -113,6 +183,10 @@ window.renderRegistry = function renderRegistry(node, container) {
|
|
|
113
183
|
|
|
114
184
|
let out = '';
|
|
115
185
|
parts.forEach((part, index) => {
|
|
186
|
+
if (part === '.well-known') {
|
|
187
|
+
out += `/<span class="path-segment" style="color: #8b5cf6; font-weight: bold;">${part}</span>`;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
116
190
|
if (part.startsWith(":")) {
|
|
117
191
|
out += `/<span class="path-segment path-param">${part}</span>`;
|
|
118
192
|
return;
|
|
@@ -152,8 +226,18 @@ window.renderRegistry = function renderRegistry(node, container) {
|
|
|
152
226
|
header.className = 'tree-item tooltip'; // Add tooltip class
|
|
153
227
|
const meta = createFileMeta(item.metadata, 'Router');
|
|
154
228
|
const tooltipHtml = getTooltipHtml(item.id);
|
|
229
|
+
const isPlugin = item.metadata && item.metadata.pluginName;
|
|
230
|
+
const badgeLabel = isPlugin ? 'PLUGIN' : 'ROUTER';
|
|
231
|
+
const badgeClass = isPlugin ? 'badge-PLUGIN' : 'badge-ROUTER';
|
|
232
|
+
|
|
233
|
+
// Add custom style for PLUGIN badge if needed, or rely on generic badge class + modifier
|
|
234
|
+
// For now, let's inject a style for badge-PLUGIN if it doesn't exist, or just inline it.
|
|
235
|
+
// Actually, let's just use inline style for the distinctive color if it's a plugin
|
|
236
|
+
// to ensure it stands out without editing CSS file.
|
|
237
|
+
const badgeStyle = isPlugin ? 'background: #f59e0b; color: #000;' : '';
|
|
238
|
+
|
|
155
239
|
header.innerHTML = `
|
|
156
|
-
<span class="badge
|
|
240
|
+
<span class="badge ${badgeClass}" style="${badgeStyle}">${badgeLabel}</span>
|
|
157
241
|
<span class="tree-label">${renderPath(item.path)}</span>
|
|
158
242
|
${meta}
|
|
159
243
|
${tooltipHtml}
|
|
@@ -243,6 +327,26 @@ window.renderRegistry = function renderRegistry(node, container) {
|
|
|
243
327
|
`;
|
|
244
328
|
wrapper.appendChild(div);
|
|
245
329
|
}
|
|
330
|
+
|
|
331
|
+
// Event
|
|
332
|
+
else if (item.kind === 'event') {
|
|
333
|
+
const div = document.createElement('div');
|
|
334
|
+
div.className = 'tree-item tooltip'; // Add tooltip class
|
|
335
|
+
|
|
336
|
+
// Event Badge style
|
|
337
|
+
const badgeStyle = "width: 50px; text-align: center; display: inline-block;";
|
|
338
|
+
|
|
339
|
+
const meta = createFileMeta(item.metadata, item.handlerName);
|
|
340
|
+
const tHtml = getTooltipHtml(item.id);
|
|
341
|
+
|
|
342
|
+
div.innerHTML = `
|
|
343
|
+
<span class="badge badge-SEND" style="${badgeStyle}">WS</span>
|
|
344
|
+
<span class="tree-label">${item.name}</span>
|
|
345
|
+
${meta}
|
|
346
|
+
${tHtml}
|
|
347
|
+
`;
|
|
348
|
+
wrapper.appendChild(div);
|
|
349
|
+
}
|
|
246
350
|
});
|
|
247
351
|
container.innerHTML = '';
|
|
248
352
|
container.appendChild(wrapper);
|