kbve 0.1.0__py3-none-any.whl

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.
kbve/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """KBVE - Core library for async servers, Nx workspace tooling, and MDX rendering."""
2
+
3
+ from .models.server_models import ServerConfig # noqa: F401
4
+ from .server.app_server import AppServer # noqa: F401
5
+ from .server.grpc_server import GrpcServer # noqa: F401
6
+ from .server.http_server import HttpServer # noqa: F401
7
+ from .server.services.health_service import HealthServicer # noqa: F401
8
+ from .server.services.echo_service import EchoServicer # noqa: F401
9
+
10
+ __version__ = "0.1.0"
kbve/api/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """KBVE API utilities."""
@@ -0,0 +1 @@
1
+ """KBVE API clients."""
@@ -0,0 +1 @@
1
+ """KBVE API utilities."""
kbve/mdx/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ """KBVE MDX/Starlight rendering primitives."""
2
+
3
+ from .escape import escape_mdx # noqa: F401
4
+ from .renderer import MdxWriter # noqa: F401
kbve/mdx/escape.py ADDED
@@ -0,0 +1,14 @@
1
+ """MDX/JSX text escaping utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ def escape_mdx(text: str) -> str:
7
+ """Escape characters that MDX/JSX would interpret as markup."""
8
+ return (text
9
+ .replace("&", "&")
10
+ .replace("<", "&lt;")
11
+ .replace(">", "&gt;")
12
+ .replace("{", "&#123;")
13
+ .replace("}", "&#125;")
14
+ .replace("|", "\\|"))
kbve/mdx/renderer.py ADDED
@@ -0,0 +1,220 @@
1
+ """Starlight MDX rendering primitives.
2
+
3
+ Provides ``MdxWriter``, a composable builder for generating Astro Starlight
4
+ MDX pages with frontmatter, cards, tabs, mermaid diagrams, tables, and
5
+ admonitions.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import io
11
+ from typing import Any
12
+
13
+
14
+ class MdxWriter:
15
+ """Composable builder for Starlight MDX content.
16
+
17
+ Usage::
18
+
19
+ w = MdxWriter()
20
+ w.frontmatter(title="My Page", description="A page.")
21
+ w.imports("Card", "CardGrid", source="@astrojs/starlight/components")
22
+ w.heading("Hello", level=2)
23
+ w.admonition("note", "Auto-generated", "Some body text.")
24
+ w.card_grid_start()
25
+ w.card("3 Apps", icon="rocket", body="app1, app2, app3")
26
+ w.card_grid_end()
27
+ w.mermaid_pie("Title", {"Apps": 3, "Libs": 5})
28
+ w.table(["Col A", "Col B"], [["a", "b"]])
29
+ content = w.render()
30
+ """
31
+
32
+ def __init__(self) -> None:
33
+ self._buf = io.StringIO()
34
+
35
+ # ── Frontmatter ──────────────────────────────────────────────
36
+
37
+ def frontmatter(self, **kwargs: Any) -> "MdxWriter":
38
+ """Write YAML frontmatter block.
39
+
40
+ Supports flat keys and one level of nesting via dict values::
41
+
42
+ w.frontmatter(
43
+ title="Page",
44
+ sidebar={"label": "Nav", "order": 1},
45
+ )
46
+ """
47
+ colon = ":"
48
+ self._buf.write("---\n")
49
+ for key, val in kwargs.items():
50
+ if isinstance(val, dict):
51
+ self._buf.write(f"{key}{colon}\n")
52
+ for k, v in val.items():
53
+ if isinstance(v, str) and "\n" in v:
54
+ self._buf.write(f" {k}{colon} |\n")
55
+ for line in v.splitlines():
56
+ self._buf.write(f" {line}\n")
57
+ else:
58
+ self._buf.write(f" {k}{colon} {v}\n")
59
+ elif isinstance(val, bool):
60
+ bval = "true" if val else "false"
61
+ self._buf.write(f"{key}{colon} {bval}\n")
62
+ else:
63
+ self._buf.write(f"{key}{colon} {val}\n")
64
+ self._buf.write("---\n\n")
65
+ return self
66
+
67
+ # ── Imports ──────────────────────────────────────────────────
68
+
69
+ def imports(self, *components: str, source: str) -> "MdxWriter":
70
+ """Write an ESM import statement."""
71
+ names = ", ".join(components)
72
+ lbrace = "{"
73
+ rbrace = "}"
74
+ semi = ";"
75
+ self._buf.write(
76
+ f"import {lbrace} {names} {rbrace} from '{source}'{semi}\n\n"
77
+ )
78
+ return self
79
+
80
+ # ── Headings & text ──────────────────────────────────────────
81
+
82
+ def heading(self, text: str, level: int = 2) -> "MdxWriter":
83
+ prefix = "#" * level
84
+ self._buf.write(f"{prefix} {text}\n\n")
85
+ return self
86
+
87
+ def text(self, content: str) -> "MdxWriter":
88
+ self._buf.write(f"{content}\n\n")
89
+ return self
90
+
91
+ def raw(self, content: str) -> "MdxWriter":
92
+ """Write raw content without trailing newlines."""
93
+ self._buf.write(content)
94
+ return self
95
+
96
+ # ── Admonitions ──────────────────────────────────────────────
97
+
98
+ def admonition(
99
+ self, kind: str, title: str, body: str,
100
+ ) -> "MdxWriter":
101
+ """Write a Starlight admonition (note, tip, caution, danger)."""
102
+ prefix = ":" * 3
103
+ self._buf.write(
104
+ f"{prefix}{kind}[{title}]\n{body}\n{prefix}\n\n"
105
+ )
106
+ return self
107
+
108
+ # ── Cards ────────────────────────────────────────────────────
109
+
110
+ def card_grid_start(self) -> "MdxWriter":
111
+ self._buf.write("<CardGrid>\n")
112
+ return self
113
+
114
+ def card_grid_end(self) -> "MdxWriter":
115
+ self._buf.write("</CardGrid>\n\n")
116
+ return self
117
+
118
+ def card(
119
+ self, title: str, icon: str, body: str,
120
+ ) -> "MdxWriter":
121
+ line1 = f' <Card title="{title}" icon="{icon}">'
122
+ line2 = f" {body}"
123
+ self._buf.write(f"{line1}\n{line2}\n </Card>\n")
124
+ return self
125
+
126
+ # ── Tabs ─────────────────────────────────────────────────────
127
+
128
+ def tabs_start(self) -> "MdxWriter":
129
+ self._buf.write("<Tabs>\n")
130
+ return self
131
+
132
+ def tabs_end(self) -> "MdxWriter":
133
+ self._buf.write("</Tabs>\n\n")
134
+ return self
135
+
136
+ def tab_start(self, label: str) -> "MdxWriter":
137
+ tag = f' <TabItem label="{label}">'
138
+ self._buf.write(f"{tag}\n\n")
139
+ return self
140
+
141
+ def tab_end(self) -> "MdxWriter":
142
+ self._buf.write(" </TabItem>\n")
143
+ return self
144
+
145
+ # ── Mermaid ──────────────────────────────────────────────────
146
+
147
+ def mermaid_pie(
148
+ self, title: str, data: dict[str, int | float],
149
+ ) -> "MdxWriter":
150
+ """Write a Mermaid pie chart."""
151
+ self._buf.write("```mermaid\npie showData\n")
152
+ self._buf.write(f" title {title}\n")
153
+ for label, value in data.items():
154
+ if value > 0:
155
+ self._buf.write(
156
+ ' "' + label + '" : ' + str(value) + '\n'
157
+ )
158
+ self._buf.write("```\n\n")
159
+ return self
160
+
161
+ def mermaid_graph(self, lines: list[str]) -> "MdxWriter":
162
+ """Write a raw Mermaid graph block."""
163
+ self._buf.write("```mermaid\n")
164
+ self._buf.write("\n".join(lines))
165
+ self._buf.write("\n```\n\n")
166
+ return self
167
+
168
+ # ── Tables ───────────────────────────────────────────────────
169
+
170
+ def table(
171
+ self,
172
+ headers: list[str],
173
+ rows: list[list[str]],
174
+ alignments: list[str] | None = None,
175
+ ) -> "MdxWriter":
176
+ """Write a Markdown table.
177
+
178
+ ``alignments`` can contain ``"left"``, ``"center"``, or ``"right"``
179
+ per column. Defaults to left-aligned.
180
+ """
181
+ if alignments is None:
182
+ alignments = ["left"] * len(headers)
183
+
184
+ sep_map = {
185
+ "left": "---",
186
+ "center": ":---:",
187
+ "right": "---:",
188
+ }
189
+ header_line = "| " + " | ".join(headers) + " |"
190
+ sep_line = "| " + " | ".join(
191
+ sep_map.get(a, "---") for a in alignments
192
+ ) + " |"
193
+
194
+ self._buf.write(header_line + "\n")
195
+ self._buf.write(sep_line + "\n")
196
+ for row in rows:
197
+ self._buf.write("| " + " | ".join(row) + " |\n")
198
+ self._buf.write("\n")
199
+ return self
200
+
201
+ # ── Details / collapsible ────────────────────────────────────
202
+
203
+ def details_start(self, summary: str) -> "MdxWriter":
204
+ self._buf.write(f"<details>\n<summary>{summary}</summary>\n\n")
205
+ return self
206
+
207
+ def details_end(self) -> "MdxWriter":
208
+ self._buf.write("\n</details>\n\n")
209
+ return self
210
+
211
+ # ── Render ───────────────────────────────────────────────────
212
+
213
+ def render(self) -> str:
214
+ """Return the accumulated MDX content as a string."""
215
+ return self._buf.getvalue()
216
+
217
+ def write_to(self, path: str) -> None:
218
+ """Write the accumulated MDX content to a file."""
219
+ with open(path, "w") as f:
220
+ f.write(self.render())
@@ -0,0 +1,3 @@
1
+ """KBVE models."""
2
+
3
+ from .server_models import ServerConfig # noqa: F401
@@ -0,0 +1,13 @@
1
+ """Server configuration models."""
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class ServerConfig(BaseModel):
7
+ """Configuration for the unified HTTP + gRPC server."""
8
+
9
+ http_host: str = "0.0.0.0"
10
+ http_port: int = 8000
11
+ grpc_host: str = "0.0.0.0"
12
+ grpc_port: int = 50051
13
+ log_level: str = "info"
kbve/nx/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ """KBVE Nx workspace integrations — graph analysis and security auditing."""
2
+
3
+ from .graph import ( # noqa: F401
4
+ parse_graph,
5
+ group_projects_by_type,
6
+ collect_edges,
7
+ top_hubs,
8
+ ProjectRow,
9
+ )
10
+ from .security import ( # noqa: F401
11
+ normalize_severity,
12
+ parse_npm,
13
+ parse_cargo,
14
+ parse_python,
15
+ parse_codeql,
16
+ parse_dependabot,
17
+ build_summary,
18
+ parse_all_ecosystems,
19
+ SEVERITY_ORDER,
20
+ )
kbve/nx/graph.py ADDED
@@ -0,0 +1,131 @@
1
+ """Nx project graph parsing and analysis.
2
+
3
+ Parses Nx graph JSON into structured data for downstream rendering
4
+ (MDX, JSON reports, API responses, etc.).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import re
11
+ from dataclasses import dataclass
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+
16
+ @dataclass
17
+ class ProjectRow:
18
+ """A single project extracted from the Nx graph."""
19
+
20
+ name: str
21
+ project_type: str
22
+ root: str
23
+ dep_count: int
24
+ dependent_count: int
25
+
26
+
27
+ @dataclass
28
+ class GraphData:
29
+ """Parsed and analysed Nx project graph."""
30
+
31
+ nodes: dict[str, Any]
32
+ deps: dict[str, list[dict[str, str]]]
33
+ rows: list[ProjectRow]
34
+ by_type: dict[str, list[str]]
35
+ edges: set[tuple[str, str]]
36
+ edges_by_source: dict[str, list[str]]
37
+
38
+
39
+ def mermaid_id(name: str) -> str:
40
+ """Sanitize a project name into a valid Mermaid node ID."""
41
+ return re.sub(r"[^a-zA-Z0-9_]", "_", name)
42
+
43
+
44
+ def parse_graph(source: str | Path | dict) -> GraphData:
45
+ """Parse an Nx graph from a file path, JSON string, or dict.
46
+
47
+ Returns a fully analysed ``GraphData`` with rows, groupings, and edges
48
+ ready for rendering.
49
+ """
50
+ if isinstance(source, dict):
51
+ data = source
52
+ elif isinstance(source, Path) or (
53
+ isinstance(source, str) and not source.lstrip().startswith("{")
54
+ ):
55
+ with open(source) as f:
56
+ data = json.load(f)
57
+ else:
58
+ data = json.loads(source)
59
+
60
+ nodes: dict[str, Any] = data["graph"]["nodes"]
61
+ deps: dict[str, list[dict[str, str]]] = data["graph"]["dependencies"]
62
+
63
+ by_type = group_projects_by_type(nodes)
64
+ rows = _build_rows(nodes, deps)
65
+ edges, edges_by_source = collect_edges(deps)
66
+
67
+ return GraphData(
68
+ nodes=nodes,
69
+ deps=deps,
70
+ rows=rows,
71
+ by_type=by_type,
72
+ edges=edges,
73
+ edges_by_source=edges_by_source,
74
+ )
75
+
76
+
77
+ def group_projects_by_type(
78
+ nodes: dict[str, Any],
79
+ ) -> dict[str, list[str]]:
80
+ """Group project names by their Nx project type."""
81
+ by_type: dict[str, list[str]] = {}
82
+ for name, node in sorted(nodes.items()):
83
+ ptype = node.get("type", "unknown")
84
+ by_type.setdefault(ptype, []).append(name)
85
+ return by_type
86
+
87
+
88
+ def collect_edges(
89
+ deps: dict[str, list[dict[str, str]]],
90
+ ) -> tuple[set[tuple[str, str]], dict[str, list[str]]]:
91
+ """Collect unique directed edges from Nx dependency data.
92
+
93
+ Returns ``(edge_set, edges_by_source)``.
94
+ """
95
+ seen: set[tuple[str, str]] = set()
96
+ by_source: dict[str, list[str]] = {}
97
+ for dep_list in deps.values():
98
+ for d in dep_list:
99
+ edge = (d["source"], d["target"])
100
+ if edge not in seen:
101
+ seen.add(edge)
102
+ by_source.setdefault(d["source"], []).append(d["target"])
103
+ return seen, by_source
104
+
105
+
106
+ def top_hubs(rows: list[ProjectRow], n: int = 5) -> list[ProjectRow]:
107
+ """Return the *n* most-depended-on projects."""
108
+ return sorted(rows, key=lambda r: r.dependent_count, reverse=True)[:n]
109
+
110
+
111
+ def _build_rows(
112
+ nodes: dict[str, Any],
113
+ deps: dict[str, list[dict[str, str]]],
114
+ ) -> list[ProjectRow]:
115
+ """Build a sorted list of ``ProjectRow`` from raw graph data."""
116
+ rows: list[ProjectRow] = []
117
+ for name in sorted(nodes):
118
+ node = nodes[name]
119
+ dep_count = len(deps.get(name, []))
120
+ dependents = sum(
121
+ 1 for d_list in deps.values() for d in d_list
122
+ if d["target"] == name
123
+ )
124
+ rows.append(ProjectRow(
125
+ name=name,
126
+ project_type=node.get("type", "unknown"),
127
+ root=node.get("data", {}).get("root", ""),
128
+ dep_count=dep_count,
129
+ dependent_count=dependents,
130
+ ))
131
+ return rows