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.
Files changed (183) hide show
  1. osscodeiq/__init__.py +0 -0
  2. osscodeiq/analyzer.py +467 -0
  3. osscodeiq/cache/__init__.py +0 -0
  4. osscodeiq/cache/hasher.py +23 -0
  5. osscodeiq/cache/store.py +300 -0
  6. osscodeiq/classifiers/__init__.py +0 -0
  7. osscodeiq/classifiers/layer_classifier.py +69 -0
  8. osscodeiq/cli.py +721 -0
  9. osscodeiq/config.py +113 -0
  10. osscodeiq/detectors/__init__.py +0 -0
  11. osscodeiq/detectors/auth/__init__.py +0 -0
  12. osscodeiq/detectors/auth/certificate_auth.py +139 -0
  13. osscodeiq/detectors/auth/ldap_auth.py +89 -0
  14. osscodeiq/detectors/auth/session_header_auth.py +120 -0
  15. osscodeiq/detectors/base.py +41 -0
  16. osscodeiq/detectors/config/__init__.py +0 -0
  17. osscodeiq/detectors/config/batch_structure.py +128 -0
  18. osscodeiq/detectors/config/cloudformation.py +183 -0
  19. osscodeiq/detectors/config/docker_compose.py +179 -0
  20. osscodeiq/detectors/config/github_actions.py +150 -0
  21. osscodeiq/detectors/config/gitlab_ci.py +216 -0
  22. osscodeiq/detectors/config/helm_chart.py +187 -0
  23. osscodeiq/detectors/config/ini_structure.py +101 -0
  24. osscodeiq/detectors/config/json_structure.py +72 -0
  25. osscodeiq/detectors/config/kubernetes.py +305 -0
  26. osscodeiq/detectors/config/kubernetes_rbac.py +212 -0
  27. osscodeiq/detectors/config/openapi.py +194 -0
  28. osscodeiq/detectors/config/package_json.py +99 -0
  29. osscodeiq/detectors/config/properties_detector.py +108 -0
  30. osscodeiq/detectors/config/pyproject_toml.py +169 -0
  31. osscodeiq/detectors/config/sql_structure.py +155 -0
  32. osscodeiq/detectors/config/toml_structure.py +93 -0
  33. osscodeiq/detectors/config/tsconfig_json.py +105 -0
  34. osscodeiq/detectors/config/yaml_structure.py +82 -0
  35. osscodeiq/detectors/cpp/__init__.py +0 -0
  36. osscodeiq/detectors/cpp/cpp_structures.py +192 -0
  37. osscodeiq/detectors/csharp/__init__.py +0 -0
  38. osscodeiq/detectors/csharp/csharp_efcore.py +184 -0
  39. osscodeiq/detectors/csharp/csharp_minimal_apis.py +156 -0
  40. osscodeiq/detectors/csharp/csharp_structures.py +317 -0
  41. osscodeiq/detectors/docs/__init__.py +0 -0
  42. osscodeiq/detectors/docs/markdown_structure.py +117 -0
  43. osscodeiq/detectors/frontend/__init__.py +0 -0
  44. osscodeiq/detectors/frontend/angular_components.py +177 -0
  45. osscodeiq/detectors/frontend/frontend_routes.py +259 -0
  46. osscodeiq/detectors/frontend/react_components.py +148 -0
  47. osscodeiq/detectors/frontend/svelte_components.py +84 -0
  48. osscodeiq/detectors/frontend/vue_components.py +150 -0
  49. osscodeiq/detectors/generic/__init__.py +1 -0
  50. osscodeiq/detectors/generic/imports_detector.py +413 -0
  51. osscodeiq/detectors/go/__init__.py +0 -0
  52. osscodeiq/detectors/go/go_orm.py +202 -0
  53. osscodeiq/detectors/go/go_structures.py +162 -0
  54. osscodeiq/detectors/go/go_web.py +157 -0
  55. osscodeiq/detectors/iac/__init__.py +0 -0
  56. osscodeiq/detectors/iac/bicep.py +135 -0
  57. osscodeiq/detectors/iac/dockerfile.py +182 -0
  58. osscodeiq/detectors/iac/terraform.py +188 -0
  59. osscodeiq/detectors/java/__init__.py +0 -0
  60. osscodeiq/detectors/java/azure_functions.py +424 -0
  61. osscodeiq/detectors/java/azure_messaging.py +350 -0
  62. osscodeiq/detectors/java/class_hierarchy.py +349 -0
  63. osscodeiq/detectors/java/config_def.py +82 -0
  64. osscodeiq/detectors/java/cosmos_db.py +105 -0
  65. osscodeiq/detectors/java/graphql_resolver.py +188 -0
  66. osscodeiq/detectors/java/grpc_service.py +142 -0
  67. osscodeiq/detectors/java/ibm_mq.py +178 -0
  68. osscodeiq/detectors/java/jaxrs.py +160 -0
  69. osscodeiq/detectors/java/jdbc.py +196 -0
  70. osscodeiq/detectors/java/jms.py +116 -0
  71. osscodeiq/detectors/java/jpa_entity.py +143 -0
  72. osscodeiq/detectors/java/kafka.py +113 -0
  73. osscodeiq/detectors/java/kafka_protocol.py +70 -0
  74. osscodeiq/detectors/java/micronaut.py +248 -0
  75. osscodeiq/detectors/java/module_deps.py +191 -0
  76. osscodeiq/detectors/java/public_api.py +206 -0
  77. osscodeiq/detectors/java/quarkus.py +176 -0
  78. osscodeiq/detectors/java/rabbitmq.py +150 -0
  79. osscodeiq/detectors/java/raw_sql.py +136 -0
  80. osscodeiq/detectors/java/repository.py +131 -0
  81. osscodeiq/detectors/java/rmi.py +129 -0
  82. osscodeiq/detectors/java/spring_events.py +117 -0
  83. osscodeiq/detectors/java/spring_rest.py +168 -0
  84. osscodeiq/detectors/java/spring_security.py +212 -0
  85. osscodeiq/detectors/java/tibco_ems.py +193 -0
  86. osscodeiq/detectors/java/websocket.py +188 -0
  87. osscodeiq/detectors/kotlin/__init__.py +0 -0
  88. osscodeiq/detectors/kotlin/kotlin_structures.py +124 -0
  89. osscodeiq/detectors/kotlin/ktor_routes.py +163 -0
  90. osscodeiq/detectors/proto/__init__.py +0 -0
  91. osscodeiq/detectors/proto/proto_structure.py +153 -0
  92. osscodeiq/detectors/python/__init__.py +0 -0
  93. osscodeiq/detectors/python/celery_tasks.py +88 -0
  94. osscodeiq/detectors/python/django_auth.py +132 -0
  95. osscodeiq/detectors/python/django_models.py +157 -0
  96. osscodeiq/detectors/python/django_views.py +74 -0
  97. osscodeiq/detectors/python/fastapi_auth.py +143 -0
  98. osscodeiq/detectors/python/fastapi_routes.py +68 -0
  99. osscodeiq/detectors/python/flask_routes.py +67 -0
  100. osscodeiq/detectors/python/kafka_python.py +175 -0
  101. osscodeiq/detectors/python/pydantic_models.py +115 -0
  102. osscodeiq/detectors/python/python_structures.py +234 -0
  103. osscodeiq/detectors/python/sqlalchemy_models.py +82 -0
  104. osscodeiq/detectors/registry.py +100 -0
  105. osscodeiq/detectors/rust/__init__.py +0 -0
  106. osscodeiq/detectors/rust/actix_web.py +234 -0
  107. osscodeiq/detectors/rust/rust_structures.py +174 -0
  108. osscodeiq/detectors/scala/__init__.py +0 -0
  109. osscodeiq/detectors/scala/scala_structures.py +128 -0
  110. osscodeiq/detectors/shell/__init__.py +0 -0
  111. osscodeiq/detectors/shell/bash_detector.py +127 -0
  112. osscodeiq/detectors/shell/powershell_detector.py +118 -0
  113. osscodeiq/detectors/typescript/__init__.py +0 -0
  114. osscodeiq/detectors/typescript/express_routes.py +55 -0
  115. osscodeiq/detectors/typescript/fastify_routes.py +156 -0
  116. osscodeiq/detectors/typescript/graphql_resolvers.py +100 -0
  117. osscodeiq/detectors/typescript/kafka_js.py +164 -0
  118. osscodeiq/detectors/typescript/mongoose_orm.py +151 -0
  119. osscodeiq/detectors/typescript/nestjs_controllers.py +99 -0
  120. osscodeiq/detectors/typescript/nestjs_guards.py +138 -0
  121. osscodeiq/detectors/typescript/passport_jwt.py +133 -0
  122. osscodeiq/detectors/typescript/prisma_orm.py +96 -0
  123. osscodeiq/detectors/typescript/remix_routes.py +160 -0
  124. osscodeiq/detectors/typescript/sequelize_orm.py +136 -0
  125. osscodeiq/detectors/typescript/typeorm_entities.py +86 -0
  126. osscodeiq/detectors/typescript/typescript_structures.py +185 -0
  127. osscodeiq/detectors/utils.py +49 -0
  128. osscodeiq/discovery/__init__.py +11 -0
  129. osscodeiq/discovery/change_detector.py +97 -0
  130. osscodeiq/discovery/file_discovery.py +342 -0
  131. osscodeiq/flow/__init__.py +0 -0
  132. osscodeiq/flow/engine.py +78 -0
  133. osscodeiq/flow/models.py +72 -0
  134. osscodeiq/flow/renderer.py +127 -0
  135. osscodeiq/flow/templates/interactive.html +252 -0
  136. osscodeiq/flow/vendor/cytoscape-dagre.min.js +8 -0
  137. osscodeiq/flow/vendor/cytoscape.min.js +32 -0
  138. osscodeiq/flow/vendor/dagre.min.js +3809 -0
  139. osscodeiq/flow/views.py +357 -0
  140. osscodeiq/graph/__init__.py +0 -0
  141. osscodeiq/graph/backend.py +52 -0
  142. osscodeiq/graph/backends/__init__.py +23 -0
  143. osscodeiq/graph/backends/kuzu.py +576 -0
  144. osscodeiq/graph/backends/networkx.py +135 -0
  145. osscodeiq/graph/backends/sqlite_backend.py +406 -0
  146. osscodeiq/graph/builder.py +297 -0
  147. osscodeiq/graph/query.py +228 -0
  148. osscodeiq/graph/store.py +183 -0
  149. osscodeiq/graph/views.py +231 -0
  150. osscodeiq/models/__init__.py +17 -0
  151. osscodeiq/models/graph.py +116 -0
  152. osscodeiq/output/__init__.py +0 -0
  153. osscodeiq/output/dot.py +171 -0
  154. osscodeiq/output/mermaid.py +160 -0
  155. osscodeiq/output/safety.py +58 -0
  156. osscodeiq/output/serializers.py +42 -0
  157. osscodeiq/parsing/__init__.py +5 -0
  158. osscodeiq/parsing/languages/__init__.py +0 -0
  159. osscodeiq/parsing/languages/base.py +23 -0
  160. osscodeiq/parsing/languages/java.py +68 -0
  161. osscodeiq/parsing/languages/python.py +57 -0
  162. osscodeiq/parsing/languages/typescript.py +95 -0
  163. osscodeiq/parsing/parser_manager.py +125 -0
  164. osscodeiq/parsing/structured/__init__.py +0 -0
  165. osscodeiq/parsing/structured/gradle_parser.py +78 -0
  166. osscodeiq/parsing/structured/json_parser.py +24 -0
  167. osscodeiq/parsing/structured/properties_parser.py +56 -0
  168. osscodeiq/parsing/structured/sql_parser.py +54 -0
  169. osscodeiq/parsing/structured/xml_parser.py +148 -0
  170. osscodeiq/parsing/structured/yaml_parser.py +38 -0
  171. osscodeiq/server/__init__.py +7 -0
  172. osscodeiq/server/app.py +53 -0
  173. osscodeiq/server/mcp_server.py +174 -0
  174. osscodeiq/server/middleware.py +16 -0
  175. osscodeiq/server/routes.py +184 -0
  176. osscodeiq/server/service.py +445 -0
  177. osscodeiq/server/templates/welcome.html +56 -0
  178. osscodeiq-0.0.0.dist-info/METADATA +30 -0
  179. osscodeiq-0.0.0.dist-info/RECORD +183 -0
  180. osscodeiq-0.0.0.dist-info/WHEEL +5 -0
  181. osscodeiq-0.0.0.dist-info/entry_points.txt +2 -0
  182. osscodeiq-0.0.0.dist-info/licenses/LICENSE +21 -0
  183. 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()">&times;</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">&minus;</button>
86
+ <button class="ctrl-btn" onclick="fitGraph()" title="Fit to screen">&#x2922;</button>
87
+ <button class="ctrl-btn" onclick="resetGraph()" title="Reset view">&#x21ba;</button>
88
+ </div>
89
+ <div class="hint">Scroll to zoom &middot; Drag to pan &middot; Click nodes for details</div>
90
+ </div>
91
+
92
+ <footer class="footer">
93
+ <span>Generated by <strong>OSSCodeIQ</strong> &mdash; 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