osscodeiq 0.0.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.
- osscodeiq/__init__.py +0 -0
- osscodeiq/analyzer.py +467 -0
- osscodeiq/cache/__init__.py +0 -0
- osscodeiq/cache/hasher.py +23 -0
- osscodeiq/cache/store.py +300 -0
- osscodeiq/classifiers/__init__.py +0 -0
- osscodeiq/classifiers/layer_classifier.py +69 -0
- osscodeiq/cli.py +721 -0
- osscodeiq/config.py +113 -0
- osscodeiq/detectors/__init__.py +0 -0
- osscodeiq/detectors/auth/__init__.py +0 -0
- osscodeiq/detectors/auth/certificate_auth.py +139 -0
- osscodeiq/detectors/auth/ldap_auth.py +89 -0
- osscodeiq/detectors/auth/session_header_auth.py +120 -0
- osscodeiq/detectors/base.py +41 -0
- osscodeiq/detectors/config/__init__.py +0 -0
- osscodeiq/detectors/config/batch_structure.py +128 -0
- osscodeiq/detectors/config/cloudformation.py +183 -0
- osscodeiq/detectors/config/docker_compose.py +179 -0
- osscodeiq/detectors/config/github_actions.py +150 -0
- osscodeiq/detectors/config/gitlab_ci.py +216 -0
- osscodeiq/detectors/config/helm_chart.py +187 -0
- osscodeiq/detectors/config/ini_structure.py +101 -0
- osscodeiq/detectors/config/json_structure.py +72 -0
- osscodeiq/detectors/config/kubernetes.py +305 -0
- osscodeiq/detectors/config/kubernetes_rbac.py +212 -0
- osscodeiq/detectors/config/openapi.py +194 -0
- osscodeiq/detectors/config/package_json.py +99 -0
- osscodeiq/detectors/config/properties_detector.py +108 -0
- osscodeiq/detectors/config/pyproject_toml.py +169 -0
- osscodeiq/detectors/config/sql_structure.py +155 -0
- osscodeiq/detectors/config/toml_structure.py +93 -0
- osscodeiq/detectors/config/tsconfig_json.py +105 -0
- osscodeiq/detectors/config/yaml_structure.py +82 -0
- osscodeiq/detectors/cpp/__init__.py +0 -0
- osscodeiq/detectors/cpp/cpp_structures.py +192 -0
- osscodeiq/detectors/csharp/__init__.py +0 -0
- osscodeiq/detectors/csharp/csharp_efcore.py +184 -0
- osscodeiq/detectors/csharp/csharp_minimal_apis.py +156 -0
- osscodeiq/detectors/csharp/csharp_structures.py +317 -0
- osscodeiq/detectors/docs/__init__.py +0 -0
- osscodeiq/detectors/docs/markdown_structure.py +117 -0
- osscodeiq/detectors/frontend/__init__.py +0 -0
- osscodeiq/detectors/frontend/angular_components.py +177 -0
- osscodeiq/detectors/frontend/frontend_routes.py +259 -0
- osscodeiq/detectors/frontend/react_components.py +148 -0
- osscodeiq/detectors/frontend/svelte_components.py +84 -0
- osscodeiq/detectors/frontend/vue_components.py +150 -0
- osscodeiq/detectors/generic/__init__.py +1 -0
- osscodeiq/detectors/generic/imports_detector.py +413 -0
- osscodeiq/detectors/go/__init__.py +0 -0
- osscodeiq/detectors/go/go_orm.py +202 -0
- osscodeiq/detectors/go/go_structures.py +162 -0
- osscodeiq/detectors/go/go_web.py +157 -0
- osscodeiq/detectors/iac/__init__.py +0 -0
- osscodeiq/detectors/iac/bicep.py +135 -0
- osscodeiq/detectors/iac/dockerfile.py +182 -0
- osscodeiq/detectors/iac/terraform.py +188 -0
- osscodeiq/detectors/java/__init__.py +0 -0
- osscodeiq/detectors/java/azure_functions.py +424 -0
- osscodeiq/detectors/java/azure_messaging.py +350 -0
- osscodeiq/detectors/java/class_hierarchy.py +349 -0
- osscodeiq/detectors/java/config_def.py +82 -0
- osscodeiq/detectors/java/cosmos_db.py +105 -0
- osscodeiq/detectors/java/graphql_resolver.py +188 -0
- osscodeiq/detectors/java/grpc_service.py +142 -0
- osscodeiq/detectors/java/ibm_mq.py +178 -0
- osscodeiq/detectors/java/jaxrs.py +160 -0
- osscodeiq/detectors/java/jdbc.py +196 -0
- osscodeiq/detectors/java/jms.py +116 -0
- osscodeiq/detectors/java/jpa_entity.py +143 -0
- osscodeiq/detectors/java/kafka.py +113 -0
- osscodeiq/detectors/java/kafka_protocol.py +70 -0
- osscodeiq/detectors/java/micronaut.py +248 -0
- osscodeiq/detectors/java/module_deps.py +191 -0
- osscodeiq/detectors/java/public_api.py +206 -0
- osscodeiq/detectors/java/quarkus.py +176 -0
- osscodeiq/detectors/java/rabbitmq.py +150 -0
- osscodeiq/detectors/java/raw_sql.py +136 -0
- osscodeiq/detectors/java/repository.py +131 -0
- osscodeiq/detectors/java/rmi.py +129 -0
- osscodeiq/detectors/java/spring_events.py +117 -0
- osscodeiq/detectors/java/spring_rest.py +168 -0
- osscodeiq/detectors/java/spring_security.py +212 -0
- osscodeiq/detectors/java/tibco_ems.py +193 -0
- osscodeiq/detectors/java/websocket.py +188 -0
- osscodeiq/detectors/kotlin/__init__.py +0 -0
- osscodeiq/detectors/kotlin/kotlin_structures.py +124 -0
- osscodeiq/detectors/kotlin/ktor_routes.py +163 -0
- osscodeiq/detectors/proto/__init__.py +0 -0
- osscodeiq/detectors/proto/proto_structure.py +153 -0
- osscodeiq/detectors/python/__init__.py +0 -0
- osscodeiq/detectors/python/celery_tasks.py +88 -0
- osscodeiq/detectors/python/django_auth.py +132 -0
- osscodeiq/detectors/python/django_models.py +157 -0
- osscodeiq/detectors/python/django_views.py +74 -0
- osscodeiq/detectors/python/fastapi_auth.py +143 -0
- osscodeiq/detectors/python/fastapi_routes.py +68 -0
- osscodeiq/detectors/python/flask_routes.py +67 -0
- osscodeiq/detectors/python/kafka_python.py +175 -0
- osscodeiq/detectors/python/pydantic_models.py +115 -0
- osscodeiq/detectors/python/python_structures.py +234 -0
- osscodeiq/detectors/python/sqlalchemy_models.py +82 -0
- osscodeiq/detectors/registry.py +100 -0
- osscodeiq/detectors/rust/__init__.py +0 -0
- osscodeiq/detectors/rust/actix_web.py +234 -0
- osscodeiq/detectors/rust/rust_structures.py +174 -0
- osscodeiq/detectors/scala/__init__.py +0 -0
- osscodeiq/detectors/scala/scala_structures.py +128 -0
- osscodeiq/detectors/shell/__init__.py +0 -0
- osscodeiq/detectors/shell/bash_detector.py +127 -0
- osscodeiq/detectors/shell/powershell_detector.py +118 -0
- osscodeiq/detectors/typescript/__init__.py +0 -0
- osscodeiq/detectors/typescript/express_routes.py +55 -0
- osscodeiq/detectors/typescript/fastify_routes.py +156 -0
- osscodeiq/detectors/typescript/graphql_resolvers.py +100 -0
- osscodeiq/detectors/typescript/kafka_js.py +164 -0
- osscodeiq/detectors/typescript/mongoose_orm.py +151 -0
- osscodeiq/detectors/typescript/nestjs_controllers.py +99 -0
- osscodeiq/detectors/typescript/nestjs_guards.py +138 -0
- osscodeiq/detectors/typescript/passport_jwt.py +133 -0
- osscodeiq/detectors/typescript/prisma_orm.py +96 -0
- osscodeiq/detectors/typescript/remix_routes.py +160 -0
- osscodeiq/detectors/typescript/sequelize_orm.py +136 -0
- osscodeiq/detectors/typescript/typeorm_entities.py +86 -0
- osscodeiq/detectors/typescript/typescript_structures.py +185 -0
- osscodeiq/detectors/utils.py +49 -0
- osscodeiq/discovery/__init__.py +11 -0
- osscodeiq/discovery/change_detector.py +97 -0
- osscodeiq/discovery/file_discovery.py +342 -0
- osscodeiq/flow/__init__.py +0 -0
- osscodeiq/flow/engine.py +78 -0
- osscodeiq/flow/models.py +72 -0
- osscodeiq/flow/renderer.py +127 -0
- osscodeiq/flow/templates/interactive.html +252 -0
- osscodeiq/flow/vendor/cytoscape-dagre.min.js +8 -0
- osscodeiq/flow/vendor/cytoscape.min.js +32 -0
- osscodeiq/flow/vendor/dagre.min.js +3809 -0
- osscodeiq/flow/views.py +357 -0
- osscodeiq/graph/__init__.py +0 -0
- osscodeiq/graph/backend.py +52 -0
- osscodeiq/graph/backends/__init__.py +23 -0
- osscodeiq/graph/backends/kuzu.py +576 -0
- osscodeiq/graph/backends/networkx.py +135 -0
- osscodeiq/graph/backends/sqlite_backend.py +406 -0
- osscodeiq/graph/builder.py +297 -0
- osscodeiq/graph/query.py +228 -0
- osscodeiq/graph/store.py +183 -0
- osscodeiq/graph/views.py +231 -0
- osscodeiq/models/__init__.py +17 -0
- osscodeiq/models/graph.py +116 -0
- osscodeiq/output/__init__.py +0 -0
- osscodeiq/output/dot.py +171 -0
- osscodeiq/output/mermaid.py +160 -0
- osscodeiq/output/safety.py +58 -0
- osscodeiq/output/serializers.py +42 -0
- osscodeiq/parsing/__init__.py +5 -0
- osscodeiq/parsing/languages/__init__.py +0 -0
- osscodeiq/parsing/languages/base.py +23 -0
- osscodeiq/parsing/languages/java.py +68 -0
- osscodeiq/parsing/languages/python.py +57 -0
- osscodeiq/parsing/languages/typescript.py +95 -0
- osscodeiq/parsing/parser_manager.py +125 -0
- osscodeiq/parsing/structured/__init__.py +0 -0
- osscodeiq/parsing/structured/gradle_parser.py +78 -0
- osscodeiq/parsing/structured/json_parser.py +24 -0
- osscodeiq/parsing/structured/properties_parser.py +56 -0
- osscodeiq/parsing/structured/sql_parser.py +54 -0
- osscodeiq/parsing/structured/xml_parser.py +148 -0
- osscodeiq/parsing/structured/yaml_parser.py +38 -0
- osscodeiq/server/__init__.py +7 -0
- osscodeiq/server/app.py +53 -0
- osscodeiq/server/mcp_server.py +174 -0
- osscodeiq/server/middleware.py +16 -0
- osscodeiq/server/routes.py +184 -0
- osscodeiq/server/service.py +445 -0
- osscodeiq/server/templates/welcome.html +56 -0
- osscodeiq-0.0.0.dist-info/METADATA +30 -0
- osscodeiq-0.0.0.dist-info/RECORD +183 -0
- osscodeiq-0.0.0.dist-info/WHEEL +5 -0
- osscodeiq-0.0.0.dist-info/entry_points.txt +2 -0
- osscodeiq-0.0.0.dist-info/licenses/LICENSE +21 -0
- osscodeiq-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Renderers for flow diagrams — Mermaid, JSON, and interactive HTML."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from osscodeiq.flow.models import FlowDiagram, FlowEdge, FlowNode, FlowSubgraph
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _sanitize_id(raw: str) -> str:
|
|
14
|
+
return re.sub(r"\W", "_", raw)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _escape_label(text: str) -> str:
|
|
18
|
+
for ch in ('"', '|', '[', ']', '{', '}', '(', ')', '<', '>', '#'):
|
|
19
|
+
text = text.replace(ch, f"&#{ord(ch)};")
|
|
20
|
+
return text
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Node shapes by kind
|
|
24
|
+
_SHAPES: dict[str, tuple[str, str]] = {
|
|
25
|
+
"trigger": ("([", "])"), # stadium
|
|
26
|
+
"pipeline": ("[", "]"), # rectangle
|
|
27
|
+
"job": ("[", "]"),
|
|
28
|
+
"endpoint": ("{{", "}}"), # hexagon
|
|
29
|
+
"entity": ("[(", ")]"), # cylinder
|
|
30
|
+
"database": ("[(", ")]"),
|
|
31
|
+
"guard": (">", "]"), # flag
|
|
32
|
+
"middleware": (">", "]"),
|
|
33
|
+
"component": ("([", "])"),
|
|
34
|
+
"messaging": ("[/", "\\]"), # parallelogram
|
|
35
|
+
"k8s": ("[", "]"),
|
|
36
|
+
"docker": ("[", "]"),
|
|
37
|
+
"terraform": ("[", "]"),
|
|
38
|
+
"infra": ("[", "]"),
|
|
39
|
+
"code": ("[", "]"),
|
|
40
|
+
"service": ("[", "]"),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_EDGE_STYLES = {
|
|
44
|
+
"solid": "-->",
|
|
45
|
+
"dotted": "-.->",
|
|
46
|
+
"thick": "==>",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_STYLE_CLASSES = {
|
|
50
|
+
"success": ":::success",
|
|
51
|
+
"warning": ":::warning",
|
|
52
|
+
"danger": ":::danger",
|
|
53
|
+
"default": "",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def render_mermaid(diagram: FlowDiagram) -> str:
|
|
58
|
+
"""Render a FlowDiagram as a Mermaid flowchart string."""
|
|
59
|
+
lines = [f"graph {diagram.direction}"]
|
|
60
|
+
|
|
61
|
+
# Style definitions
|
|
62
|
+
lines.append(" classDef success fill:#d4edda,stroke:#28a745,color:#155724")
|
|
63
|
+
lines.append(" classDef warning fill:#fff3cd,stroke:#ffc107,color:#856404")
|
|
64
|
+
lines.append(" classDef danger fill:#f8d7da,stroke:#dc3545,color:#721c24")
|
|
65
|
+
lines.append("")
|
|
66
|
+
|
|
67
|
+
for sg in diagram.subgraphs:
|
|
68
|
+
sg_id = _sanitize_id(sg.id)
|
|
69
|
+
lines.append(f' subgraph {sg_id}["{_escape_label(sg.label)}"]')
|
|
70
|
+
for node in sorted(sg.nodes, key=lambda n: n.id):
|
|
71
|
+
nid = _sanitize_id(node.id)
|
|
72
|
+
label = _escape_label(node.label)
|
|
73
|
+
open_br, close_br = _SHAPES.get(node.kind, ("[", "]"))
|
|
74
|
+
style_class = _STYLE_CLASSES.get(node.style, "")
|
|
75
|
+
lines.append(f" {nid}{open_br}\"{label}\"{close_br}{style_class}")
|
|
76
|
+
lines.append(" end")
|
|
77
|
+
lines.append("")
|
|
78
|
+
|
|
79
|
+
for node in sorted(diagram.loose_nodes, key=lambda n: n.id):
|
|
80
|
+
nid = _sanitize_id(node.id)
|
|
81
|
+
label = _escape_label(node.label)
|
|
82
|
+
open_br, close_br = _SHAPES.get(node.kind, ("[", "]"))
|
|
83
|
+
style_class = _STYLE_CLASSES.get(node.style, "")
|
|
84
|
+
lines.append(f" {nid}{open_br}\"{label}\"{close_br}{style_class}")
|
|
85
|
+
|
|
86
|
+
lines.append("")
|
|
87
|
+
for edge in sorted(diagram.edges, key=lambda e: (e.source, e.target)):
|
|
88
|
+
src = _sanitize_id(edge.source)
|
|
89
|
+
tgt = _sanitize_id(edge.target)
|
|
90
|
+
arrow = _EDGE_STYLES.get(edge.style, "-->")
|
|
91
|
+
if edge.label:
|
|
92
|
+
lines.append(f" {src} {arrow}|{_escape_label(edge.label)}| {tgt}")
|
|
93
|
+
else:
|
|
94
|
+
lines.append(f" {src} {arrow} {tgt}")
|
|
95
|
+
|
|
96
|
+
return "\n".join(lines)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def render_json(diagram: FlowDiagram) -> str:
|
|
100
|
+
"""Render a FlowDiagram as a JSON string."""
|
|
101
|
+
return json.dumps(diagram.to_dict(), indent=2)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def render_html(views: dict[str, FlowDiagram], stats: dict[str, Any], project_name: str = "Project") -> str:
|
|
105
|
+
"""Render all views into a self-contained interactive HTML file."""
|
|
106
|
+
views_data = {}
|
|
107
|
+
for name, diagram in sorted(views.items()):
|
|
108
|
+
views_data[name] = diagram.to_dict()
|
|
109
|
+
|
|
110
|
+
template_path = Path(__file__).parent / "templates" / "interactive.html"
|
|
111
|
+
template = template_path.read_text(encoding="utf-8")
|
|
112
|
+
|
|
113
|
+
# Inline vendor JS for offline/firewall use
|
|
114
|
+
vendor_dir = Path(__file__).parent / "vendor"
|
|
115
|
+
for placeholder, filename in [
|
|
116
|
+
("{{VENDOR_DAGRE}}", "dagre.min.js"),
|
|
117
|
+
("{{VENDOR_CYTOSCAPE}}", "cytoscape.min.js"),
|
|
118
|
+
("{{VENDOR_CYTOSCAPE_DAGRE}}", "cytoscape-dagre.min.js"),
|
|
119
|
+
]:
|
|
120
|
+
vendor_path = vendor_dir / filename
|
|
121
|
+
template = template.replace(placeholder, vendor_path.read_text(encoding="utf-8"))
|
|
122
|
+
|
|
123
|
+
html = template.replace("{{VIEWS_DATA}}", json.dumps(views_data, indent=2))
|
|
124
|
+
html = html.replace("{{STATS}}", json.dumps(stats, indent=2))
|
|
125
|
+
html = html.replace("{{PROJECT_NAME}}", json.dumps(project_name))
|
|
126
|
+
|
|
127
|
+
return html
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>OSSCodeIQ — Architecture Flow</title>
|
|
7
|
+
<script>{{VENDOR_DAGRE}}</script>
|
|
8
|
+
<script>{{VENDOR_CYTOSCAPE}}</script>
|
|
9
|
+
<script>{{VENDOR_CYTOSCAPE_DAGRE}}</script>
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--bg: #0a0a0f; --bg-surface: #111118; --bg-elevated: #1a1a24;
|
|
13
|
+
--border: #25253a; --border-hover: #3a3a55;
|
|
14
|
+
--text: #e4e4ed; --text-muted: #8888a0; --text-dim: #55556a;
|
|
15
|
+
--accent: #6366f1; --accent-hover: #818cf8;
|
|
16
|
+
}
|
|
17
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
18
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
|
|
19
|
+
|
|
20
|
+
.header { display: flex; align-items: center; justify-content: space-between; padding: 12px 20px; border-bottom: 1px solid var(--border); background: var(--bg-surface); flex-shrink: 0; }
|
|
21
|
+
.header-left { display: flex; align-items: center; gap: 12px; }
|
|
22
|
+
.logo { width: 32px; height: 32px; border-radius: 8px; background: var(--accent); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; font-size: 12px; flex-shrink: 0; }
|
|
23
|
+
.header-title { font-size: 15px; font-weight: 600; color: var(--text); }
|
|
24
|
+
.header-subtitle { font-size: 11px; color: var(--text-muted); margin-top: 1px; }
|
|
25
|
+
.header-right { display: flex; align-items: center; gap: 8px; }
|
|
26
|
+
.stat-pill { display: flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 6px; background: var(--bg-elevated); border: 1px solid var(--border); font-size: 12px; }
|
|
27
|
+
.stat-value { font-weight: 700; }
|
|
28
|
+
.stat-label { color: var(--text-muted); text-transform: uppercase; font-size: 10px; letter-spacing: 0.05em; }
|
|
29
|
+
|
|
30
|
+
.nav { display: flex; align-items: center; gap: 4px; padding: 8px 20px; border-bottom: 1px solid var(--border); background: var(--bg-surface); flex-shrink: 0; flex-wrap: wrap; }
|
|
31
|
+
.nav-btn { padding: 6px 14px; border-radius: 6px; border: 1px solid transparent; background: none; color: var(--text-muted); font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.15s; }
|
|
32
|
+
.nav-btn:hover { color: var(--text); background: var(--bg-elevated); border-color: var(--border); }
|
|
33
|
+
.nav-btn.active { background: var(--accent); color: #fff; border-color: var(--accent); }
|
|
34
|
+
|
|
35
|
+
.graph-wrap { flex: 1; position: relative; overflow: hidden; }
|
|
36
|
+
#cy { width: 100%; height: 100%; }
|
|
37
|
+
|
|
38
|
+
.controls { position: absolute; bottom: 16px; right: 16px; display: flex; flex-direction: column; gap: 4px; z-index: 10; }
|
|
39
|
+
.ctrl-btn { width: 36px; height: 36px; border-radius: 8px; border: 1px solid var(--border); background: var(--bg-surface); color: var(--text-muted); font-size: 18px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.15s; }
|
|
40
|
+
.ctrl-btn:hover { background: var(--bg-elevated); color: var(--text); border-color: var(--border-hover); }
|
|
41
|
+
|
|
42
|
+
.info-panel { position: absolute; top: 12px; right: 12px; max-width: 280px; padding: 12px 16px; border-radius: 10px; background: var(--bg-surface); border: 1px solid var(--border); font-size: 12px; z-index: 10; display: none; }
|
|
43
|
+
.info-panel.visible { display: block; }
|
|
44
|
+
.info-title { font-weight: 600; font-size: 14px; margin-bottom: 6px; }
|
|
45
|
+
.info-row { display: flex; justify-content: space-between; padding: 3px 0; }
|
|
46
|
+
.info-key { color: var(--text-muted); }
|
|
47
|
+
.info-val { font-weight: 500; }
|
|
48
|
+
.info-close { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 16px; }
|
|
49
|
+
|
|
50
|
+
.view-stats { position: absolute; top: 12px; left: 12px; display: flex; gap: 6px; flex-wrap: wrap; z-index: 10; }
|
|
51
|
+
.view-stat { padding: 3px 8px; border-radius: 5px; background: var(--bg-surface); border: 1px solid var(--border); font-size: 11px; color: var(--text-muted); }
|
|
52
|
+
.view-stat span { color: var(--text); font-weight: 600; }
|
|
53
|
+
|
|
54
|
+
.footer { display: flex; align-items: center; justify-content: space-between; padding: 8px 20px; border-top: 1px solid var(--border); background: var(--bg-surface); font-size: 11px; color: var(--text-dim); flex-shrink: 0; }
|
|
55
|
+
.footer strong { color: var(--text-muted); }
|
|
56
|
+
|
|
57
|
+
.hint { position: absolute; bottom: 16px; left: 16px; font-size: 11px; color: var(--text-dim); z-index: 10; }
|
|
58
|
+
</style>
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
|
|
62
|
+
<div class="header">
|
|
63
|
+
<div class="header-left">
|
|
64
|
+
<div class="logo">IQ</div>
|
|
65
|
+
<div>
|
|
66
|
+
<div class="header-title" id="project-name"></div>
|
|
67
|
+
<div class="header-subtitle">Architecture Flow</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="header-right" id="global-stats"></div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="nav" id="nav"></div>
|
|
74
|
+
|
|
75
|
+
<div class="graph-wrap">
|
|
76
|
+
<div class="view-stats" id="view-stats"></div>
|
|
77
|
+
<div id="cy"></div>
|
|
78
|
+
<div class="info-panel" id="info-panel">
|
|
79
|
+
<button class="info-close" onclick="closeInfo()">×</button>
|
|
80
|
+
<div class="info-title" id="info-title"></div>
|
|
81
|
+
<div id="info-body"></div>
|
|
82
|
+
</div>
|
|
83
|
+
<div class="controls">
|
|
84
|
+
<button class="ctrl-btn" onclick="zoomIn()" title="Zoom in">+</button>
|
|
85
|
+
<button class="ctrl-btn" onclick="zoomOut()" title="Zoom out">−</button>
|
|
86
|
+
<button class="ctrl-btn" onclick="fitGraph()" title="Fit to screen">⤢</button>
|
|
87
|
+
<button class="ctrl-btn" onclick="resetGraph()" title="Reset view">↺</button>
|
|
88
|
+
</div>
|
|
89
|
+
<div class="hint">Scroll to zoom · Drag to pan · Click nodes for details</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<footer class="footer">
|
|
93
|
+
<span>Generated by <strong>OSSCodeIQ</strong> — No AI, pure deterministic analysis</span>
|
|
94
|
+
<span id="gen-time"></span>
|
|
95
|
+
</footer>
|
|
96
|
+
|
|
97
|
+
<script>
|
|
98
|
+
var VIEWS_DATA = {{VIEWS_DATA}};
|
|
99
|
+
var STATS = {{STATS}};
|
|
100
|
+
var PROJECT_NAME = {{PROJECT_NAME}};
|
|
101
|
+
|
|
102
|
+
var VIEW_LABELS = {overview:"Overview", ci:"CI/CD Pipeline", deploy:"Deployment", runtime:"Runtime", auth:"Security"};
|
|
103
|
+
var currentView = "overview";
|
|
104
|
+
var cy = null;
|
|
105
|
+
|
|
106
|
+
var KIND_COLORS = {
|
|
107
|
+
trigger:{bg:"#7c3aed",border:"#6d28d9"},pipeline:{bg:"#2563eb",border:"#1d4ed8"},
|
|
108
|
+
job:{bg:"#3b82f6",border:"#2563eb"},endpoint:{bg:"#06b6d4",border:"#0891b2"},
|
|
109
|
+
entity:{bg:"#8b5cf6",border:"#7c3aed"},database:{bg:"#8b5cf6",border:"#7c3aed"},
|
|
110
|
+
guard:{bg:"#f59e0b",border:"#d97706"},middleware:{bg:"#f97316",border:"#ea580c"},
|
|
111
|
+
component:{bg:"#10b981",border:"#059669"},messaging:{bg:"#ec4899",border:"#db2777"},
|
|
112
|
+
k8s:{bg:"#326ce5",border:"#2558c5"},docker:{bg:"#0db7ed",border:"#099dd0"},
|
|
113
|
+
terraform:{bg:"#7b42bc",border:"#643499"},infra:{bg:"#64748b",border:"#475569"},
|
|
114
|
+
code:{bg:"#6366f1",border:"#4f46e5"},service:{bg:"#6366f1",border:"#4f46e5"}
|
|
115
|
+
};
|
|
116
|
+
var STYLE_COLORS = {success:{bg:"#166534",border:"#22c55e"},warning:{bg:"#854d0e",border:"#eab308"},danger:{bg:"#991b1b",border:"#ef4444"}};
|
|
117
|
+
var DEFAULT_COLOR = {bg:"#6366f1",border:"#4f46e5"};
|
|
118
|
+
|
|
119
|
+
function zoomIn(){ if(cy){cy.zoom({level:cy.zoom()*1.3,renderedPosition:{x:cy.width()/2,y:cy.height()/2}});} }
|
|
120
|
+
function zoomOut(){ if(cy){cy.zoom({level:cy.zoom()/1.3,renderedPosition:{x:cy.width()/2,y:cy.height()/2}});} }
|
|
121
|
+
function fitGraph(){ if(cy) cy.fit(undefined,40); }
|
|
122
|
+
function resetGraph(){ if(cy){cy.fit(undefined,40);cy.center();} }
|
|
123
|
+
|
|
124
|
+
function buildElements(viewData) {
|
|
125
|
+
var els = [];
|
|
126
|
+
if (!viewData) return els;
|
|
127
|
+
(viewData.subgraphs||[]).forEach(function(sg){
|
|
128
|
+
els.push({data:{id:sg.id,label:sg.label,isParent:true,drillDown:sg.drill_down_view}});
|
|
129
|
+
(sg.nodes||[]).forEach(function(n){
|
|
130
|
+
var c = (n.style && n.style!=="default" && STYLE_COLORS[n.style]) || KIND_COLORS[n.kind] || DEFAULT_COLOR;
|
|
131
|
+
els.push({data:{id:n.id,label:n.label,kind:n.kind,parent:sg.id,bgColor:c.bg,borderColor:c.border,props:n.properties,nodeStyle:n.style}});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
(viewData.loose_nodes||[]).forEach(function(n){
|
|
135
|
+
var c = (n.style && n.style!=="default" && STYLE_COLORS[n.style]) || KIND_COLORS[n.kind] || DEFAULT_COLOR;
|
|
136
|
+
els.push({data:{id:n.id,label:n.label,kind:n.kind,bgColor:c.bg,borderColor:c.border,props:n.properties,nodeStyle:n.style}});
|
|
137
|
+
});
|
|
138
|
+
(viewData.edges||[]).forEach(function(e,i){
|
|
139
|
+
els.push({data:{id:"e"+i,source:e.source,target:e.target,label:e.label||"",edgeStyle:e.style||"solid"}});
|
|
140
|
+
});
|
|
141
|
+
return els;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function initCy(elements) {
|
|
145
|
+
if (cy) cy.destroy();
|
|
146
|
+
cy = cytoscape({
|
|
147
|
+
container: document.getElementById("cy"),
|
|
148
|
+
elements: elements,
|
|
149
|
+
minZoom: 0.15, maxZoom: 5, wheelSensitivity: 0.3,
|
|
150
|
+
style: [
|
|
151
|
+
{selector:"node[?isParent]",style:{
|
|
152
|
+
"background-color":"#15151f","background-opacity":0.6,
|
|
153
|
+
"border-width":1,"border-color":"#25253a","border-opacity":0.8,
|
|
154
|
+
"shape":"round-rectangle","padding":"24px",
|
|
155
|
+
"label":"data(label)","text-valign":"top","text-halign":"center",
|
|
156
|
+
"font-size":"13px","font-weight":"600","color":"#8888a0","text-margin-y":-6
|
|
157
|
+
}},
|
|
158
|
+
{selector:"node[!isParent]",style:{
|
|
159
|
+
"background-color":"data(bgColor)","border-width":2,"border-color":"data(borderColor)",
|
|
160
|
+
"label":"data(label)","text-valign":"center","text-halign":"center",
|
|
161
|
+
"font-size":"11px","font-weight":"500","color":"#fff",
|
|
162
|
+
"width":"label","height":"label","padding":"10px",
|
|
163
|
+
"shape":"round-rectangle","text-wrap":"wrap","text-max-width":"120px",
|
|
164
|
+
"transition-property":"border-width,border-color","transition-duration":"0.15s"
|
|
165
|
+
}},
|
|
166
|
+
{selector:"edge",style:{
|
|
167
|
+
"width":2,"line-color":"#3a3a55","target-arrow-color":"#3a3a55",
|
|
168
|
+
"target-arrow-shape":"triangle","arrow-scale":0.8,"curve-style":"bezier",
|
|
169
|
+
"label":"data(label)","font-size":"10px","color":"#6a6a80",
|
|
170
|
+
"text-rotation":"autorotate","text-margin-y":-10,
|
|
171
|
+
"text-background-color":"#0a0a0f","text-background-opacity":0.8,"text-background-padding":"2px"
|
|
172
|
+
}},
|
|
173
|
+
{selector:"edge[edgeStyle='dotted']",style:{"line-style":"dashed","line-dash-pattern":[6,4],"line-color":"#55556a","target-arrow-color":"#55556a"}},
|
|
174
|
+
{selector:"edge[edgeStyle='thick']",style:{"width":3,"line-color":"#6366f1","target-arrow-color":"#6366f1"}},
|
|
175
|
+
{selector:"node[!isParent]:active,node[!isParent]:selected",style:{"border-width":3,"border-color":"#818cf8"}}
|
|
176
|
+
],
|
|
177
|
+
layout:{name:"dagre",rankDir:"LR",nodeSep:50,rankSep:80,padding:40}
|
|
178
|
+
});
|
|
179
|
+
cy.on("tap","node[!isParent]",function(evt){ showInfo(evt.target.data()); });
|
|
180
|
+
cy.on("dbltap","node[?isParent]",function(evt){
|
|
181
|
+
var dd = evt.target.data().drillDown;
|
|
182
|
+
if (dd && VIEWS_DATA[dd]) switchView(dd);
|
|
183
|
+
});
|
|
184
|
+
cy.on("tap",function(evt){ if(evt.target===cy) closeInfo(); });
|
|
185
|
+
cy.fit(undefined,40);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function showInfo(d) {
|
|
189
|
+
document.getElementById("info-title").textContent = d.label;
|
|
190
|
+
var body = document.getElementById("info-body");
|
|
191
|
+
var rows = '<div class="info-row"><span class="info-key">Kind</span><span class="info-val">' + d.kind + '</span></div>';
|
|
192
|
+
if (d.props) {
|
|
193
|
+
Object.keys(d.props).forEach(function(k){
|
|
194
|
+
var v = d.props[k];
|
|
195
|
+
rows += '<div class="info-row"><span class="info-key">' + k.replace(/_/g," ") + '</span><span class="info-val">' + v + '</span></div>';
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
body.innerHTML = rows;
|
|
199
|
+
document.getElementById("info-panel").classList.add("visible");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function closeInfo() { document.getElementById("info-panel").classList.remove("visible"); }
|
|
203
|
+
|
|
204
|
+
function switchView(view) {
|
|
205
|
+
currentView = view;
|
|
206
|
+
document.querySelectorAll(".nav-btn").forEach(function(btn){ btn.classList.toggle("active",btn.dataset.view===view); });
|
|
207
|
+
var data = VIEWS_DATA[view];
|
|
208
|
+
var statsEl = document.getElementById("view-stats");
|
|
209
|
+
statsEl.innerHTML = "";
|
|
210
|
+
if (data && data.stats) {
|
|
211
|
+
Object.keys(data.stats).forEach(function(k){
|
|
212
|
+
var pill = document.createElement("div");
|
|
213
|
+
pill.className = "view-stat";
|
|
214
|
+
var val = data.stats[k];
|
|
215
|
+
pill.textContent = k.replace(/_/g," ") + " ";
|
|
216
|
+
var s = document.createElement("span");
|
|
217
|
+
s.textContent = typeof val === "number" ? val.toLocaleString() : val;
|
|
218
|
+
pill.appendChild(s);
|
|
219
|
+
statsEl.appendChild(pill);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
var elements = buildElements(data);
|
|
223
|
+
if (elements.length > 0) { initCy(elements); }
|
|
224
|
+
else if (cy) { cy.destroy(); cy = null; }
|
|
225
|
+
closeInfo();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function init() {
|
|
229
|
+
document.getElementById("project-name").textContent = PROJECT_NAME;
|
|
230
|
+
var gs = document.getElementById("global-stats");
|
|
231
|
+
var n = document.createElement("div"); n.className="stat-pill";
|
|
232
|
+
n.innerHTML = '<span class="stat-value" style="color:#6366f1">'+(STATS.total_nodes||0).toLocaleString()+'</span><span class="stat-label">Nodes</span>';
|
|
233
|
+
gs.appendChild(n);
|
|
234
|
+
var e = document.createElement("div"); e.className="stat-pill";
|
|
235
|
+
e.innerHTML = '<span class="stat-value" style="color:#22c55e">'+(STATS.total_edges||0).toLocaleString()+'</span><span class="stat-label">Edges</span>';
|
|
236
|
+
gs.appendChild(e);
|
|
237
|
+
var nav = document.getElementById("nav");
|
|
238
|
+
Object.keys(VIEW_LABELS).forEach(function(v){
|
|
239
|
+
var btn = document.createElement("button");
|
|
240
|
+
btn.className = "nav-btn"; btn.dataset.view = v;
|
|
241
|
+
btn.textContent = VIEW_LABELS[v];
|
|
242
|
+
btn.addEventListener("click",function(){ switchView(v); });
|
|
243
|
+
nav.appendChild(btn);
|
|
244
|
+
});
|
|
245
|
+
document.getElementById("gen-time").textContent = new Date().toLocaleDateString();
|
|
246
|
+
switchView("overview");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
document.addEventListener("DOMContentLoaded", init);
|
|
250
|
+
</script>
|
|
251
|
+
</body>
|
|
252
|
+
</html>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minified by jsDelivr using Terser v5.37.0.
|
|
3
|
+
* Original file: /npm/cytoscape-dagre@2.5.0/cytoscape-dagre.js
|
|
4
|
+
*
|
|
5
|
+
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
|
6
|
+
*/
|
|
7
|
+
!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("dagre")):"function"==typeof define&&define.amd?define(["dagre"],n):"object"==typeof exports?exports.cytoscapeDagre=n(require("dagre")):e.cytoscapeDagre=n(e.dagre)}(this,(function(e){return function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=0)}([function(e,n,t){var r=t(1),o=function(e){e&&e("layout","dagre",r)};"undefined"!=typeof cytoscape&&o(cytoscape),e.exports=o},function(e,n,t){function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}var o=function(e){return"function"==typeof e},i=t(2),a=t(3),u=t(4);function c(e){this.options=a({},i,e)}c.prototype.run=function(){var e=this.options,n=e.cy,t=e.eles,i=function(e,n){return o(n)?n.apply(e,[e]):n},a=e.boundingBox||{x1:0,y1:0,w:n.width(),h:n.height()};void 0===a.x2&&(a.x2=a.x1+a.w),void 0===a.w&&(a.w=a.x2-a.x1),void 0===a.y2&&(a.y2=a.y1+a.h),void 0===a.h&&(a.h=a.y2-a.y1);var c=new u.graphlib.Graph({multigraph:!0,compound:!0}),d={},f=function(e,n){null!=n&&(d[e]=n)};f("nodesep",e.nodeSep),f("edgesep",e.edgeSep),f("ranksep",e.rankSep),f("rankdir",e.rankDir),f("align",e.align),f("ranker",e.ranker),f("acyclicer",e.acyclicer),c.setGraph(d),c.setDefaultEdgeLabel((function(){return{}})),c.setDefaultNodeLabel((function(){return{}}));var s=t.nodes();o(e.sort)&&(s=s.sort(e.sort));for(var y=0;y<s.length;y++){var l=s[y],p=l.layoutDimensions(e);c.setNode(l.id(),{width:p.w,height:p.h,name:l.id()})}for(var g=0;g<s.length;g++){var h=s[g];h.isChild()&&c.setParent(h.id(),h.parent().id())}var v=t.edges().stdFilter((function(e){return!e.source().isParent()&&!e.target().isParent()}));o(e.sort)&&(v=v.sort(e.sort));for(var x=0;x<v.length;x++){var b=v[x];c.setEdge(b.source().id(),b.target().id(),{minlen:i(b,e.minLen),weight:i(b,e.edgeWeight),name:b.id()},b.id())}u.layout(c);for(var m,S=c.nodes(),j=0;j<S.length;j++){var w=S[j],O=c.node(w);n.getElementById(w).scratch().dagre=O}e.boundingBox?(m={x1:1/0,x2:-1/0,y1:1/0,y2:-1/0},s.forEach((function(e){var n=e.scratch().dagre;m.x1=Math.min(m.x1,n.x),m.x2=Math.max(m.x2,n.x),m.y1=Math.min(m.y1,n.y),m.y2=Math.max(m.y2,n.y)})),m.w=m.x2-m.x1,m.h=m.y2-m.y1):m=a;return s.layoutPositions(this,e,(function(n){var t=(n="object"===r(n)?n:this).scratch().dagre;return function(n){if(e.boundingBox){var t=0===m.w?0:(n.x-m.x1)/m.w,r=0===m.h?0:(n.y-m.y1)/m.h;return{x:a.x1+t*a.w,y:a.y1+r*a.h}}return n}({x:t.x,y:t.y})})),this},e.exports=c},function(e,n){var t={nodeSep:void 0,edgeSep:void 0,rankSep:void 0,rankDir:void 0,align:void 0,acyclicer:void 0,ranker:void 0,minLen:function(e){return 1},edgeWeight:function(e){return 1},fit:!0,padding:30,spacingFactor:void 0,nodeDimensionsIncludeLabels:!1,animate:!1,animateFilter:function(e,n){return!0},animationDuration:500,animationEasing:void 0,boundingBox:void 0,transform:function(e,n){return n},ready:function(){},sort:void 0,stop:function(){}};e.exports=t},function(e,n){e.exports=null!=Object.assign?Object.assign.bind(Object):function(e){for(var n=arguments.length,t=new Array(n>1?n-1:0),r=1;r<n;r++)t[r-1]=arguments[r];return t.forEach((function(n){Object.keys(n).forEach((function(t){return e[t]=n[t]}))})),e}},function(n,t){n.exports=e}])}));
|
|
8
|
+
//# sourceMappingURL=/sm/ab0d5f70f38f0c419b568ba8a2aacc228dcda8b4c69c3ad59680a3ee9e63a6d0.map
|