archsight 0.2.4 → 0.2.5

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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/lib/archsight/cli.rb +67 -0
  3. data/lib/archsight/graph.rb +10 -0
  4. data/lib/archsight/import/executor.rb +11 -3
  5. data/lib/archsight/import/handlers/cpp_grapher.rb +193 -0
  6. data/lib/archsight/import/handlers/crystal_grapher.rb +186 -0
  7. data/lib/archsight/import/handlers/elixir_grapher.rb +202 -0
  8. data/lib/archsight/import/handlers/go_grapher.rb +127 -0
  9. data/lib/archsight/import/handlers/grapher.rb +552 -0
  10. data/lib/archsight/import/handlers/java_grapher.rb +286 -0
  11. data/lib/archsight/import/handlers/javascript_grapher.rb +340 -0
  12. data/lib/archsight/import/handlers/python_grapher.rb +270 -0
  13. data/lib/archsight/import/handlers/repository.rb +41 -17
  14. data/lib/archsight/import/handlers/ruby_grapher.rb +203 -0
  15. data/lib/archsight/import/handlers/rust_grapher.rb +227 -0
  16. data/lib/archsight/import/registry.rb +23 -0
  17. data/lib/archsight/resources/import.rb +1 -0
  18. data/lib/archsight/resources/technology_artifact.rb +17 -0
  19. data/lib/archsight/version.rb +1 -1
  20. data/lib/archsight/web/api/json_helpers.rb +1 -1
  21. data/lib/archsight/web/public/vue/ApiDocsPage-C0y953v0.css +1 -0
  22. data/lib/archsight/web/public/vue/ApiDocsPage-DHSCaHEn.js +1 -0
  23. data/lib/archsight/web/public/vue/DocPage-DszOPlFy.js +1 -0
  24. data/lib/archsight/web/public/vue/EditorPage-CPZ0Ei4l.css +1 -0
  25. data/lib/archsight/web/public/vue/EditorPage-DsiuZ7fg.js +35 -0
  26. data/lib/archsight/web/public/vue/ErrorPage-C4JutrYc.js +2 -0
  27. data/lib/archsight/web/public/vue/ErrorPage-uMDnfY5_.css +1 -0
  28. data/lib/archsight/web/public/vue/GraphView-Bqlbt6dK.js +1 -0
  29. data/lib/archsight/web/public/vue/GraphView-Cj2V2stN.css +1 -0
  30. data/lib/archsight/web/public/vue/InstanceRouter-D8SEY2eu.js +2 -0
  31. data/lib/archsight/web/public/vue/InstanceRouter-D9hclKFt.css +1 -0
  32. data/lib/archsight/web/public/vue/KindList-CPDaNron.js +1 -0
  33. data/lib/archsight/web/public/vue/ResourceList-B5w9yiyS.js +1 -0
  34. data/lib/archsight/web/public/vue/ResourceList-DxZfNbOg.css +1 -0
  35. data/lib/archsight/web/public/vue/SearchResults-DSHpVO-c.css +1 -0
  36. data/lib/archsight/web/public/vue/SearchResults-FpkhdBFu.js +1 -0
  37. data/lib/archsight/web/public/vue/architecture-7EHR7CIX-DpNNjAIc.js +1 -0
  38. data/lib/archsight/web/public/vue/eventmodeling-FCH6USID-CiThxoWl.js +1 -0
  39. data/lib/archsight/web/public/vue/gitGraph-WXDBUCRP-BODMGpAm.js +1 -0
  40. data/lib/archsight/web/public/vue/graphviz-09t3o0af.js +13 -0
  41. data/lib/archsight/web/public/vue/index-BW0IzY6X.css +1 -0
  42. data/lib/archsight/web/public/vue/index-T1YqCmM1.js +2 -0
  43. data/lib/archsight/web/public/vue/info-J43DQDTF-fLq04sri.js +1 -0
  44. data/lib/archsight/web/public/vue/katex-5qHlIbPR.js +261 -0
  45. data/lib/archsight/web/public/vue/mermaid-DYyHQk7x.js +3093 -0
  46. data/lib/archsight/web/public/vue/packet-YPE3B663-DoY1fbqu.js +1 -0
  47. data/lib/archsight/web/public/vue/pie-LRSECV5Y-C7ZQVwRe.js +1 -0
  48. data/lib/archsight/web/public/vue/radar-GUYGQ44K-CRtY5oqf.js +1 -0
  49. data/lib/archsight/web/public/vue/rolldown-runtime-QTnfLwEv.js +1 -0
  50. data/lib/archsight/web/public/vue/treeView-BLDUP644-Csx2WLLh.js +1 -0
  51. data/lib/archsight/web/public/vue/treemap-LRROVOQU-CfEnRbTx.js +1 -0
  52. data/lib/archsight/web/public/vue/{useGraphviz-C5lv_BWF.js → useGraphviz-EKSrE4q_.js} +5 -4
  53. data/lib/archsight/web/public/vue/useHighlight-BcVbGyrK.js +10 -0
  54. data/lib/archsight/web/public/vue/useMermaid-CIZxhy_r.js +2 -0
  55. data/lib/archsight/web/public/vue/usePanZoom-C2slpyY9.js +11 -0
  56. data/lib/archsight/web/public/vue/wardley-L42UT6IY-97oUvxhz.js +1 -0
  57. data/lib/archsight/web/public/vue.html +4 -3
  58. metadata +51 -72
  59. data/lib/archsight/web/public/vue/ApiDocsPage-DHOFUCYc.js +0 -1
  60. data/lib/archsight/web/public/vue/ApiDocsPage-DhNTOH4o.css +0 -1
  61. data/lib/archsight/web/public/vue/DocPage-CV66qgTr.js +0 -1
  62. data/lib/archsight/web/public/vue/EditorPage-Dq0MuTnp.css +0 -1
  63. data/lib/archsight/web/public/vue/EditorPage-KqBivY-B.js +0 -34
  64. data/lib/archsight/web/public/vue/ErrorPage-CwPT3JUr.css +0 -1
  65. data/lib/archsight/web/public/vue/ErrorPage-DcbC8Kf1.js +0 -2
  66. data/lib/archsight/web/public/vue/GraphView-Bg_l-F-Q.js +0 -1
  67. data/lib/archsight/web/public/vue/GraphView-DRcIqAiR.css +0 -1
  68. data/lib/archsight/web/public/vue/InstanceRouter-4VEtZM7n.css +0 -1
  69. data/lib/archsight/web/public/vue/InstanceRouter-D-twdyZY.js +0 -2
  70. data/lib/archsight/web/public/vue/KindList-CPImTKNb.js +0 -1
  71. data/lib/archsight/web/public/vue/ResourceList-DMxm0cGh.js +0 -1
  72. data/lib/archsight/web/public/vue/ResourceList-DP-z-j71.css +0 -1
  73. data/lib/archsight/web/public/vue/SearchResults-BGHbg48-.css +0 -1
  74. data/lib/archsight/web/public/vue/SearchResults-BqUHEWHE.js +0 -1
  75. data/lib/archsight/web/public/vue/_baseUniq-BjkdEi26.js +0 -1
  76. data/lib/archsight/web/public/vue/architectureDiagram-VXUJARFQ-JN7CxdtP.js +0 -36
  77. data/lib/archsight/web/public/vue/blockDiagram-VD42YOAC-teziPFHX.js +0 -122
  78. data/lib/archsight/web/public/vue/c4Diagram-YG6GDRKO-CVNrzCCc.js +0 -10
  79. data/lib/archsight/web/public/vue/chunk-4BX2VUAB-BQUARyyA.js +0 -1
  80. data/lib/archsight/web/public/vue/chunk-55IACEB6-BgJn0Waa.js +0 -1
  81. data/lib/archsight/web/public/vue/chunk-B4BG7PRW-0ghAfB1t.js +0 -165
  82. data/lib/archsight/web/public/vue/chunk-DI55MBZ5-HJo1DW2B.js +0 -220
  83. data/lib/archsight/web/public/vue/chunk-FMBD7UC4-C1GwGKgX.js +0 -15
  84. data/lib/archsight/web/public/vue/chunk-QN33PNHL-BOoA1KfJ.js +0 -1
  85. data/lib/archsight/web/public/vue/chunk-QZHKN3VN-BcTNH3IX.js +0 -1
  86. data/lib/archsight/web/public/vue/chunk-TZMSLE5B-58bQF3J5.js +0 -1
  87. data/lib/archsight/web/public/vue/classDiagram-2ON5EDUG-DAA8tpbN.js +0 -1
  88. data/lib/archsight/web/public/vue/classDiagram-v2-WZHVMYZB-DAA8tpbN.js +0 -1
  89. data/lib/archsight/web/public/vue/clone-BPcOyh7U.js +0 -1
  90. data/lib/archsight/web/public/vue/cose-bilkent-S5V4N54A-Bv8iR4rF.js +0 -1
  91. data/lib/archsight/web/public/vue/cytoscape.esm-5J0xJHOV.js +0 -321
  92. data/lib/archsight/web/public/vue/dagre-6UL2VRFP-B8ZPRhgU.js +0 -4
  93. data/lib/archsight/web/public/vue/diagram-PSM6KHXK-Dp1bZnNq.js +0 -24
  94. data/lib/archsight/web/public/vue/diagram-QEK2KX5R-DZBsDWP6.js +0 -43
  95. data/lib/archsight/web/public/vue/diagram-S2PKOQOG-BKOnLWNk.js +0 -24
  96. data/lib/archsight/web/public/vue/erDiagram-Q2GNP2WA-V6FqHPc9.js +0 -60
  97. data/lib/archsight/web/public/vue/flowDiagram-NV44I4VS-BipXjPVT.js +0 -162
  98. data/lib/archsight/web/public/vue/ganttDiagram-JELNMOA3-DUdVPVK1.js +0 -267
  99. data/lib/archsight/web/public/vue/gitGraphDiagram-V2S2FVAM-Bcf_apTG.js +0 -65
  100. data/lib/archsight/web/public/vue/graph-C4e9XILZ.js +0 -1
  101. data/lib/archsight/web/public/vue/graphviz-CJms5bxZ.js +0 -13
  102. data/lib/archsight/web/public/vue/index-BUI400cn.js +0 -2
  103. data/lib/archsight/web/public/vue/index-Tiu4C-Sb.css +0 -1
  104. data/lib/archsight/web/public/vue/infoDiagram-HS3SLOUP-DBwnExXO.js +0 -2
  105. data/lib/archsight/web/public/vue/journeyDiagram-XKPGCS4Q-D4b8PEzB.js +0 -139
  106. data/lib/archsight/web/public/vue/kanban-definition-3W4ZIXB7-R6r4NPxJ.js +0 -89
  107. data/lib/archsight/web/public/vue/katex-C-M49wc6.js +0 -261
  108. data/lib/archsight/web/public/vue/layout-CXL8NrCi.js +0 -1
  109. data/lib/archsight/web/public/vue/mermaid-MmHzOPPB.js +0 -250
  110. data/lib/archsight/web/public/vue/min-B4MPxNTL.js +0 -1
  111. data/lib/archsight/web/public/vue/mindmap-definition-VGOIOE7T-BypPT67y.js +0 -68
  112. data/lib/archsight/web/public/vue/pieDiagram-ADFJNKIX-BJ4nb15u.js +0 -30
  113. data/lib/archsight/web/public/vue/quadrantDiagram-AYHSOK5B-D4Ee5XRs.js +0 -7
  114. data/lib/archsight/web/public/vue/requirementDiagram-UZGBJVZJ-DuxpUmt2.js +0 -64
  115. data/lib/archsight/web/public/vue/sankeyDiagram-TZEHDZUN-CgWlWmza.js +0 -10
  116. data/lib/archsight/web/public/vue/sequenceDiagram-WL72ISMW-v2l2CbI0.js +0 -145
  117. data/lib/archsight/web/public/vue/stateDiagram-FKZM4ZOC-B5ROi2_1.js +0 -1
  118. data/lib/archsight/web/public/vue/stateDiagram-v2-4FDKWEC3-CQhha5H7.js +0 -1
  119. data/lib/archsight/web/public/vue/timeline-definition-IT6M3QCI-Bnx2HvxP.js +0 -61
  120. data/lib/archsight/web/public/vue/treemap-GDKQZRPO-BVho_qBy.js +0 -162
  121. data/lib/archsight/web/public/vue/useHighlight-yg_u-WUA.js +0 -10
  122. data/lib/archsight/web/public/vue/useMermaid-BPBwn39g.js +0 -1
  123. data/lib/archsight/web/public/vue/usePanZoom-CgSmFLId.js +0 -11
  124. data/lib/archsight/web/public/vue/xychartDiagram-PRI3JC2R-CDkeZXF1.js +0 -7
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "grapher"
4
+ require_relative "../registry"
5
+
6
+ # RustGrapher — analyses a Rust repository and generates a GraphViz DOT
7
+ # graph of its crate/module structure, stored as architecture/rust/modules
8
+ # on the TechnologyArtifact.
9
+ #
10
+ # Supports single-crate projects (Cargo.toml at root) and Cargo workspaces
11
+ # (root Cargo.toml with [workspace] members). Uses pure static regex analysis
12
+ # of `use crate::` and `use <workspace_member>::` statements — no Rust
13
+ # toolchain required.
14
+ #
15
+ # File-to-package mapping follows Rust conventions:
16
+ # src/lib.rs, src/main.rs → crate root package
17
+ # src/foo.rs → crate/foo
18
+ # src/foo/mod.rs → crate/foo (directory module)
19
+ # src/foo/bar.rs → crate/foo/bar → capped to crate/foo
20
+ #
21
+ # Configuration:
22
+ # import/config/path - Path to the Rust repository root
23
+ # import/config/ranksep - Horizontal gap between rank columns (default: 0.6)
24
+ # import/config/nodesep - Vertical gap between nodes in a column (default: 0.15)
25
+ class Archsight::Import::Handlers::RustGrapher < Archsight::Import::Handlers::Grapher
26
+ def self.language_name = "rust"
27
+
28
+ def self.applicable?(path)
29
+ File.exist?(File.join(path, "Cargo.toml")) ||
30
+ Dir.glob(File.join(path, "*/Cargo.toml")).any?
31
+ end
32
+
33
+ def wrap_single_module?
34
+ true
35
+ end
36
+
37
+ SKIP_DIRS = %w[target .git tests test benches examples].freeze
38
+
39
+ # MAX_PKG_DEPTH = 2: crate/feature is the natural Rust module depth.
40
+ MAX_PKG_DEPTH = 2
41
+
42
+ # use crate::foo::bar::Baz — capture everything after crate::
43
+ USE_CRATE_RE = /^\s*use\s+crate::((?:\w+::)*\w+)/
44
+
45
+ # use some_crate::path — capture full path for workspace dep resolution
46
+ USE_PATH_RE = /^\s*use\s+((?:\w+::)*\w+)/
47
+
48
+ # Identifiers that are never workspace crate names
49
+ BUILTIN_PREFIXES = %w[crate super self].freeze
50
+
51
+ private
52
+
53
+ # ── Module discovery ─────────────────────────────────────────────────────
54
+
55
+ def discover_modules(repo_root)
56
+ return workspace_discover(repo_root) if workspace?(repo_root)
57
+
58
+ mod_name = parse_crate_name(File.join(repo_root, "Cargo.toml")) ||
59
+ File.basename(repo_root)
60
+ [[".", mod_name]]
61
+ end
62
+
63
+ def workspace?(repo_root)
64
+ content = File.read(File.join(repo_root, "Cargo.toml"), encoding: "utf-8")
65
+ content.match?(/^\[workspace\]/)
66
+ rescue StandardError
67
+ false
68
+ end
69
+
70
+ def workspace_discover(repo_root)
71
+ members = workspace_members(repo_root)
72
+
73
+ if members.empty?
74
+ # Workspace with no members — fall back to treating root as a single crate
75
+ mod_name = parse_crate_name(File.join(repo_root, "Cargo.toml")) ||
76
+ File.basename(repo_root)
77
+ return [[".", mod_name]]
78
+ end
79
+
80
+ members.filter_map do |rel_dir|
81
+ mod_name = parse_crate_name(File.join(repo_root, rel_dir, "Cargo.toml")) ||
82
+ File.basename(rel_dir)
83
+ [rel_dir, mod_name]
84
+ end
85
+ end
86
+
87
+ def workspace_members(repo_root)
88
+ content = File.read(File.join(repo_root, "Cargo.toml"), encoding: "utf-8")
89
+ ws_section = content[/^\[workspace\].*?(?=^\[|\z)/m] || ""
90
+ members_block = ws_section[/\bmembers\s*=\s*\[(.*?)\]/m, 1] || ""
91
+ raw = members_block.scan(/"([^"]+)"/).flatten
92
+
93
+ raw.flat_map { |pat| Dir.glob(File.join(repo_root, pat)) }
94
+ .map { |d| d.delete_prefix("#{repo_root}/") }
95
+ .reject { |rel| SKIP_DIRS.any? { |d| rel.split("/").include?(d) } }
96
+ .select { |rel| File.exist?(File.join(repo_root, rel, "Cargo.toml")) }
97
+ end
98
+
99
+ def parse_crate_name(cargo_toml_path)
100
+ content = File.read(cargo_toml_path, encoding: "utf-8")
101
+ # Extract from [package] only — avoid matching workspace.package or other sections
102
+ pkg_section = content[/^\[package\].*?(?=^\[|\z)/m]
103
+ pkg_section&.match(/^name\s*=\s*"([^"]+)"/)&.[](1)
104
+ rescue StandardError
105
+ nil
106
+ end
107
+
108
+ # ── Package collection ────────────────────────────────────────────────────
109
+
110
+ def collect_packages(repo_root, modules, _prefix)
111
+ # Rust normalises Cargo.toml hyphens to underscores in `use` statements
112
+ known_crates = modules.each_with_object({}) do |(_, mod_name), h|
113
+ h[mod_name.gsub("-", "_")] = mod_name
114
+ end
115
+
116
+ all_pkgs = {}
117
+
118
+ modules.each do |rel_dir, mod_name|
119
+ mod_dir = rel_dir == "." ? repo_root : File.join(repo_root, rel_dir)
120
+ src_dir = Dir.exist?(File.join(mod_dir, "src")) ? File.join(mod_dir, "src") : mod_dir
121
+ scan_src_dir(src_dir, mod_name, known_crates, all_pkgs)
122
+ end
123
+
124
+ pkg_set = all_pkgs.keys.to_set
125
+ all_pkgs.each_value { |deps| deps.select! { |d| pkg_set.include?(d) } }
126
+
127
+ all_pkgs
128
+ end
129
+
130
+ # ── Scanning ─────────────────────────────────────────────────────────────
131
+
132
+ def scan_src_dir(src_dir, mod_name, known_crates, all_pkgs)
133
+ safe_glob(File.join(src_dir, "**", "*.rs")).each do |rs_file|
134
+ rel = rs_file.delete_prefix("#{src_dir}/")
135
+ rel_parts = rel.split("/")
136
+ next if rel_parts.any? { |p| SKIP_DIRS.include?(p) }
137
+
138
+ pkg = cap_depth(file_to_pkg(rs_file, src_dir, mod_name), mod_name)
139
+ all_pkgs[pkg] ||= []
140
+
141
+ # lib.rs and main.rs are crate entry points — they wire binaries and
142
+ # re-export the public API but don't express module architecture.
143
+ # Extracting their deps would produce hub-spoke edges from the invisible
144
+ # root node to every child, which matches no other language grapher's style.
145
+ next if %w[lib.rs main.rs].include?(rel)
146
+
147
+ extract_deps(rs_file, mod_name, known_crates).each do |dep|
148
+ dep = cap_depth(dep, mod_name)
149
+ next if dep == pkg || all_pkgs[pkg].include?(dep)
150
+
151
+ all_pkgs[pkg] << dep
152
+ end
153
+ end
154
+ end
155
+
156
+ # ── Dependency extraction ─────────────────────────────────────────────────
157
+
158
+ def extract_deps(rs_file, mod_name, known_crates)
159
+ content = File.read(rs_file, encoding: "utf-8")
160
+ deps = []
161
+
162
+ content.each_line do |line|
163
+ # Intra-crate: use crate::foo::bar::Baz
164
+ if (m = line.match(USE_CRATE_RE))
165
+ path = m[1].gsub("::", "/")
166
+ deps << "#{mod_name}/#{path}"
167
+ next
168
+ end
169
+
170
+ # Cross-crate workspace: use other_crate::path::to::Type
171
+ # Check workspace membership before any stdlib filtering so that
172
+ # workspace crates named "core" or "alloc" are resolved correctly.
173
+ next unless (m = line.match(USE_PATH_RE))
174
+
175
+ parts = m[1].split("::")
176
+ next if BUILTIN_PREFIXES.include?(parts.first)
177
+
178
+ target_mod = known_crates[parts.first]
179
+ next unless target_mod
180
+
181
+ # Cap the sub-path to MAX_PKG_DEPTH-1 levels so the dep lands on a
182
+ # real package rather than a type name (e.g. ::User → drop it).
183
+ if parts.length > 1
184
+ sub_parts = parts[1..].first(MAX_PKG_DEPTH - 1)
185
+ deps << "#{target_mod}/#{sub_parts.join("/")}"
186
+ else
187
+ deps << target_mod
188
+ end
189
+ end
190
+
191
+ deps.uniq
192
+ rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError
193
+ []
194
+ end
195
+
196
+ # ── Package path helpers ──────────────────────────────────────────────────
197
+
198
+ # Maps a .rs file to its package path.
199
+ # src/lib.rs, src/main.rs → crate root (mod_name)
200
+ # src/foo/mod.rs → mod_name/foo
201
+ # src/foo.rs → mod_name/foo
202
+ # src/foo/bar.rs → mod_name/foo/bar (capped later)
203
+ def file_to_pkg(abs_path, src_dir, mod_name)
204
+ rel = abs_path.delete_prefix("#{src_dir}/").delete_suffix(".rs")
205
+ return mod_name if %w[lib main].include?(rel)
206
+
207
+ rel = rel.delete_suffix("/mod")
208
+ return mod_name if rel == mod_name
209
+ return rel if rel.start_with?("#{mod_name}/")
210
+
211
+ "#{mod_name}/#{rel}"
212
+ end
213
+
214
+ def cap_depth(pkg, mod_name)
215
+ return pkg if pkg != mod_name && !pkg.start_with?("#{mod_name}/")
216
+
217
+ suffix = pkg.delete_prefix("#{mod_name}/")
218
+ return mod_name if suffix == pkg
219
+
220
+ parts = suffix.split("/")
221
+ return pkg if parts.length <= MAX_PKG_DEPTH - 1
222
+
223
+ "#{mod_name}/#{parts.first(MAX_PKG_DEPTH - 1).join("/")}"
224
+ end
225
+ end
226
+
227
+ Archsight::Import::Registry.register("rust-grapher", Archsight::Import::Handlers::RustGrapher)
@@ -37,6 +37,29 @@ module Archsight::Import::Registry
37
37
  handler_class
38
38
  end
39
39
 
40
+ # Return all grapher handler classes that can handle the given path.
41
+ # Each class opts in by implementing `self.applicable?(path)` returning true/false.
42
+ def handlers_for(path)
43
+ @handlers.values.select do |h|
44
+ h.respond_to?(:applicable?) && begin
45
+ h.applicable?(path)
46
+ rescue Errno::ELOOP, Errno::ENOTDIR
47
+ false
48
+ end
49
+ end
50
+ end
51
+
52
+ # Look up a grapher handler by its declared language name.
53
+ # Handler classes opt in by implementing `self.language_name`.
54
+ def handler_for_language(lang)
55
+ @handlers.values.find { |h| h.respond_to?(:language_name) && h.language_name == lang }
56
+ end
57
+
58
+ # Reverse lookup: return the registered name for a handler class, or nil.
59
+ def name_for(klass)
60
+ @handlers.key(klass)
61
+ end
62
+
40
63
  # List all registered handler names
41
64
  # @return [Array<String>] Handler names
42
65
  def handlers
@@ -42,6 +42,7 @@ class Archsight::Resources::Import < Archsight::Resources::Base
42
42
  gitlab github repository
43
43
  rest-api rest-api-index
44
44
  jira-discover jira-metrics
45
+ go-grapher
45
46
  ]
46
47
 
47
48
  # Output configuration
@@ -220,6 +220,23 @@ class Archsight::Resources::TechnologyArtifact < Archsight::Resources::Base
220
220
  description: "Total unique contributors",
221
221
  type: Integer
222
222
 
223
+ annotation "architecture/modules",
224
+ description: "GraphViz DOT graph of module/package structure (legacy single-language key; superseded by architecture/<lang>/modules)",
225
+ title: "Module Structure",
226
+ sidebar: false
227
+
228
+ {
229
+ "go" => "Go", "python" => "Python", "java" => "Java",
230
+ "typescript" => "TypeScript", "javascript" => "JavaScript",
231
+ "rust" => "Rust", "ruby" => "Ruby", "crystal" => "Crystal",
232
+ "csharp" => "C#", "zig" => "Zig", "elixir" => "Elixir"
233
+ }.each do |lang, label|
234
+ annotation "architecture/#{lang}/modules",
235
+ description: "GraphViz DOT — #{label} module/package structure",
236
+ title: "#{label} Module Structure",
237
+ sidebar: false
238
+ end
239
+
223
240
  relation :servedBy, :technologyComponents, :TechnologyArtifact
224
241
  relation :suppliedBy, :technologyComponents, :TechnologyService
225
242
  relation :maintainedBy, :businessActors, :BusinessActor
@@ -4,5 +4,5 @@
4
4
  # Do not edit manually.
5
5
 
6
6
  module Archsight
7
- VERSION = "0.2.4"
7
+ VERSION = "0.2.5"
8
8
  end
@@ -198,7 +198,7 @@ module Archsight::Web::API::JsonHelpers
198
198
  when :heading
199
199
  { type: "heading", level: section[:level], text: section[:text] }
200
200
  when :text
201
- { type: "text", content: section[:content] }
201
+ { type: "text", content: markdown(section[:content]) }
202
202
  when :message
203
203
  { type: "message", level: section[:level].to_s, message: section[:message] }
204
204
  when :table
@@ -0,0 +1 @@
1
+ .back-link[data-v-0e03906d]{z-index:100;background:#fff;border:1px solid #ddd;border-radius:4px;padding:6px 14px;font-size:.875rem;text-decoration:none;position:fixed;top:12px;right:12px;box-shadow:0 1px 4px #0000001a}
@@ -0,0 +1 @@
1
+ import{B as e,N as t,S as n,T as r,U as i,g as a,j as o,t as s,w as c,y as l,z as u}from"./index-T1YqCmM1.js";var d={class:`api-docs-page`},f={key:0,class:`pico-color-red-500`},p=s({__name:`ApiDocsPage`,setup(s){let p=a(),m=u(null),h=u(null);function g(e){return new Promise((t,n)=>{if(window.Redoc)return t();let r=document.createElement(`script`);r.src=e,r.onload=t,r.onerror=()=>n(Error(`Failed to load ReDoc`)),document.head.appendChild(r)})}return o(async()=>{try{await g(`https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js`),window.Redoc.init(`/api/v1/openapi.yaml`,{},m.value)}catch(e){h.value=e.message}}),(a,o)=>(t(),r(`div`,d,[n(`a`,{href:`#`,class:`back-link`,onClick:o[0]||=l(t=>e(p).back(),[`prevent`])},`← Back`),h.value?(t(),r(`div`,f,i(h.value),1)):c(``,!0),n(`div`,{ref_key:`container`,ref:m},null,512)]))}},[[`__scopeId`,`data-v-0e03906d`]]);export{p as default};
@@ -0,0 +1 @@
1
+ import{I as e,N as t,S as n,T as r,a as i,k as a,z as o}from"./index-T1YqCmM1.js";import{t as s}from"./useHighlight-BcVbGyrK.js";import{t as c}from"./useMermaid-CIZxhy_r.js";var l=[`innerHTML`],u={key:1},d={__name:`DocPage`,props:{filename:String},setup(d){let f=d,p=o(``),m=o(!0),h=o(null);async function g(){m.value=!0,p.value=await i(f.filename)||`<p>Documentation not found.</p>`,m.value=!1,await a(),h.value&&(s(h.value),c(h.value))}return e(()=>f.filename,g,{immediate:!0}),(e,i)=>m.value?(t(),r(`article`,u,[...i[0]||=[n(`p`,null,`Loading...`,-1)]])):(t(),r(`div`,{key:0,ref_key:`articleEl`,ref:h,innerHTML:p.value},null,8,l))}};export{d as default};
@@ -0,0 +1 @@
1
+ .field-group[data-v-6dfd17ff]{margin-bottom:0}.field-group label[data-v-6dfd17ff]{margin-bottom:.25rem}.field-group label .required[data-v-6dfd17ff]{color:var(--del-color);margin-left:.15rem}.field-group input[data-v-6dfd17ff],.field-group select[data-v-6dfd17ff],.field-group textarea[data-v-6dfd17ff]{margin-bottom:0}.field-description[data-v-6dfd17ff]{color:var(--muted-color);margin-top:.25rem;font-size:.8rem}.field-error[data-v-6dfd17ff]{color:var(--del-color);margin-top:.25rem;font-size:.8rem}.field-full-width[data-v-6dfd17ff]{grid-column:1/-1}.markdown-field textarea[data-v-6dfd17ff]{font-family:var(--font-family-monospace);resize:vertical;min-height:200px}.code-field textarea[data-v-6dfd17ff]{resize:vertical;tab-size:2;white-space:pre;overflow-wrap:normal;min-height:600px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:.9em;line-height:1.5;overflow-x:auto}.future-hint[data-v-6dfd17ff]{color:var(--muted-color);align-items:center;gap:.35rem;margin-top:.5rem;font-size:.8rem;display:flex}.label-row[data-v-6dfd17ff]{justify-content:space-between;align-items:center;margin-bottom:.25rem;display:flex}.label-row label[data-v-6dfd17ff]{margin-bottom:0}.btn-edit-markdown[data-v-6dfd17ff]{color:var(--muted-color);border:1px solid var(--muted-border-color);border-radius:var(--border-radius);cursor:pointer;background:0 0;align-items:center;gap:.35rem;padding:.25rem .5rem;font-size:.85rem;transition:all .15s;display:inline-flex}.btn-edit-markdown[data-v-6dfd17ff]:hover{color:var(--primary);border-color:var(--primary);background-color:#0000}.btn-edit-markdown i[data-v-6dfd17ff]{font-size:.9rem}@media (prefers-color-scheme:dark){.btn-edit-markdown[data-v-6dfd17ff]{color:#8b949e;border-color:#ffffff26}.btn-edit-markdown[data-v-6dfd17ff]:hover{color:#58a6ff;border-color:#58a6ff}}.relations-editor[data-v-e27dc1c0]{flex-direction:column;gap:.75rem;display:flex}#relations-list[data-v-e27dc1c0]{background-color:var(--background-color);border:1px solid var(--muted-border-color);border-radius:4px;flex-direction:column;display:flex}#relations-list[data-v-e27dc1c0]:empty{display:none}.relation-item[data-v-e27dc1c0]{border-bottom:1px solid var(--muted-border-color);justify-content:space-between;align-items:center;gap:.5rem;padding:.35rem .5rem;font-size:.9rem;display:flex}.relation-item[data-v-e27dc1c0]:last-child{border-bottom:none}.relation-text[data-v-e27dc1c0]{white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.relation-text strong[data-v-e27dc1c0]{font-weight:600}.relation-text em[data-v-e27dc1c0]{color:var(--muted-color);font-style:normal}.btn-remove[data-v-e27dc1c0]{width:1.5rem;height:1.5rem;color:var(--muted-color);cursor:pointer;background:0 0;border:none;border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;margin:0;padding:0;display:inline-flex}.btn-remove[data-v-e27dc1c0]:hover{background-color:var(--del-color);color:#fff}.btn-remove i[data-v-e27dc1c0]{font-size:.85rem}.add-relation-row[data-v-e27dc1c0]{align-items:center;gap:.5rem;display:flex}.add-relation-row select[data-v-e27dc1c0]{flex:1;margin:0}.add-relation-row select[data-v-e27dc1c0]:first-child{flex:1.2}.btn-add[data-v-e27dc1c0]{white-space:nowrap;flex-shrink:0;margin:0}.no-relations[data-v-e27dc1c0]{color:var(--muted-color);margin:0;font-style:italic}@media (width<=768px){.add-relation-row[data-v-e27dc1c0]{flex-direction:column}.add-relation-row select[data-v-e27dc1c0],.add-relation-row button[data-v-e27dc1c0]{width:100%}}.yaml-output-container[data-v-f7e6a550]{width:100%}.yaml-success[data-v-f7e6a550]{color:var(--ins-color);align-items:center;gap:.5rem;margin:0 0 1rem;font-weight:500;display:flex}#yaml-content[data-v-f7e6a550]{margin:0 calc(var(--block-spacing-horizontal) * -1);padding:var(--block-spacing-horizontal);background-color:var(--code-background-color);border-radius:0;overflow-x:auto}#yaml-content code[data-v-f7e6a550]{white-space:pre;padding:0;font-size:.9em;line-height:1.5}footer[data-v-f7e6a550]{justify-content:space-between;align-items:center;gap:1rem;display:flex}.yaml-file-info[data-v-f7e6a550],.yaml-instructions[data-v-f7e6a550]{color:var(--muted-color);flex:1;gap:.5rem;min-width:0;margin:0;font-size:.9em;line-height:1.5;display:flex}.yaml-file-info[data-v-f7e6a550]{align-items:center}.yaml-instructions[data-v-f7e6a550]{align-items:flex-start}.yaml-file-info>span[data-v-f7e6a550],.yaml-instructions>span[data-v-f7e6a550]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.yaml-file-info>i[data-v-f7e6a550],.yaml-instructions>i[data-v-f7e6a550]{flex-shrink:0}.yaml-actions[data-v-f7e6a550]{flex-shrink:0;gap:.5rem;display:flex}.yaml-actions button[data-v-f7e6a550],.yaml-actions [role=button][data-v-f7e6a550]{width:auto;margin:0}.conflict-error[data-v-f7e6a550]{background-color:var(--pico-mark-background-color);color:var(--pico-del-color);border-radius:var(--pico-border-radius);align-items:center;gap:.5rem;margin-bottom:1rem;padding:1rem;display:flex}.conflict-error i[data-v-f7e6a550]{flex-shrink:0}@media (width<=768px){footer[data-v-f7e6a550]{flex-direction:column;align-items:stretch}.yaml-actions[data-v-f7e6a550]{flex-direction:column}.yaml-actions button[data-v-f7e6a550],.yaml-actions [role=button][data-v-f7e6a550]{width:100%}}#markdown-editor-overlay[data-v-9ca3d00d]{z-index:1000;position:fixed;inset:0}.markdown-editor-backdrop[data-v-9ca3d00d]{background-color:#00000080;position:absolute;inset:0}.markdown-editor-panel[data-v-9ca3d00d]{background-color:var(--card-background-color,#fff);border-radius:12px;flex-direction:column;display:flex;position:absolute;inset:2rem;overflow:hidden;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.lexical-toolbar[data-v-9ca3d00d]{background-color:var(--card-background-color,#fff);z-index:1;flex-wrap:wrap;align-items:center;gap:.25rem;padding:.5rem 1rem;display:flex;box-shadow:0 2px 4px #0000001a}.toolbar-btn[data-v-9ca3d00d]{border-radius:var(--border-radius);min-width:2rem;height:2rem;color:var(--color);cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;margin:0;padding:0 .5rem;font-size:.85rem;font-weight:600;transition:all .15s;display:inline-flex}.toolbar-btn[data-v-9ca3d00d]:hover{background-color:var(--muted-border-color);border-color:var(--muted-border-color)}.toolbar-btn[data-v-9ca3d00d]:active{background-color:var(--primary);border-color:var(--primary);color:var(--primary-inverse)}.toolbar-btn i[data-v-9ca3d00d]{font-size:1.1rem}.toolbar-divider[data-v-9ca3d00d]{background-color:var(--muted-border-color);width:1px;height:1.5rem;margin:0 .5rem}.toolbar-spacer[data-v-9ca3d00d]{flex:1}.toolbar-action[data-v-9ca3d00d]{margin:0;padding:.4rem .75rem;font-size:.85rem}.toolbar-select[data-v-9ca3d00d]{border:1px solid var(--pico-muted-border-color,#e4e4e7);border-radius:var(--pico-border-radius,.375rem);width:auto;min-width:130px;max-width:180px;height:2rem;color:var(--pico-color,inherit);cursor:pointer;background-color:#0000;flex-shrink:0;margin:0;padding:0 .75rem;font-size:.85rem}.toolbar-select[data-v-9ca3d00d]:hover{border-color:var(--pico-primary,#1e88e5)}.toolbar-select[data-v-9ca3d00d]:focus{border-color:var(--pico-primary,#1e88e5);outline:none}.lexical-editor-container[data-v-9ca3d00d]{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}#lexical-editor-root[data-v-9ca3d00d]{min-height:0;font-family:var(--font-family);flex:1;padding:1rem 1.5rem;line-height:1.6;position:relative;overflow-y:auto}#lexical-editor-root[data-v-9ca3d00d]:focus{outline:none}#lexical-editor-root[data-v-9ca3d00d] p{margin:0 0 .75rem}#lexical-editor-root[data-v-9ca3d00d] p:last-child{margin-bottom:0}#lexical-editor-root[data-v-9ca3d00d] h1,#lexical-editor-root[data-v-9ca3d00d] h2,#lexical-editor-root[data-v-9ca3d00d] h3,#lexical-editor-root[data-v-9ca3d00d] h4{margin:1rem 0 .5rem;font-weight:600}#lexical-editor-root[data-v-9ca3d00d] h1:first-child,#lexical-editor-root[data-v-9ca3d00d] h2:first-child,#lexical-editor-root[data-v-9ca3d00d] h3:first-child{margin-top:0}#lexical-editor-root[data-v-9ca3d00d] ul,#lexical-editor-root[data-v-9ca3d00d] ol{margin:.5rem 0;padding-left:1.5rem}#lexical-editor-root[data-v-9ca3d00d] li{margin:.25rem 0}#lexical-editor-root[data-v-9ca3d00d] blockquote{border-left:3px solid var(--pico-muted-border-color,#e4e4e7);color:var(--pico-muted-color,#71717a);margin:.75rem 0;padding:.5rem 1rem;font-style:italic}#lexical-editor-root[data-v-9ca3d00d] code{background-color:var(--code-background-color,#f4f4f5);border:1px solid var(--pico-muted-border-color,#e4e4e7);font-family:var(--pico-font-family-monospace,monospace);border-radius:3px;padding:.15rem .35rem;font-size:.9em}#lexical-editor-root[data-v-9ca3d00d] pre{background-color:var(--code-background-color);border-radius:var(--border-radius);margin:.75rem 0;padding:1rem;position:relative;overflow-x:auto}#lexical-editor-root[data-v-9ca3d00d] pre code{background:0 0;padding:0}#lexical-editor-root[data-v-9ca3d00d] .lexical-code{background-color:var(--code-background-color,#f4f4f5);border-top:1px solid var(--pico-muted-border-color,#e4e4e7);border-bottom:1px solid var(--pico-muted-border-color,#e4e4e7);min-width:calc(100% + 3rem);font-family:var(--pico-font-family-monospace,monospace);white-space:pre;tab-size:2;box-sizing:border-box;border-radius:0;margin:.75rem -1.5rem;padding:1rem 1.5rem;font-size:.9em;line-height:1.5;display:block;position:relative;overflow-x:auto}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenComment{color:#6a737d}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenPunctuation{color:#24292e}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenProperty{color:#005cc5}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenSelector{color:#032f62}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenOperator{color:#005cc5}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenAttr{color:#d73a49}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenVariable{color:#e36209}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenFunction{color:#6f42c1}#lexical-editor-root[data-v-9ca3d00d] a{color:var(--primary);text-decoration:underline}#lexical-editor-root[data-v-9ca3d00d] .lexical-link{color:var(--pico-primary,#1e88e5);cursor:pointer;text-decoration:underline}#lexical-editor-root[data-v-9ca3d00d] .lexical-link:hover{text-decoration:none}#lexical-editor-root[data-v-9ca3d00d] strong,#lexical-editor-root[data-v-9ca3d00d] .lexical-bold{font-weight:700}#lexical-editor-root[data-v-9ca3d00d] em,#lexical-editor-root[data-v-9ca3d00d] .lexical-italic{font-style:italic}#lexical-editor-root[data-v-9ca3d00d] u,#lexical-editor-root[data-v-9ca3d00d] .lexical-underline{text-decoration:underline}#lexical-editor-root[data-v-9ca3d00d] s,#lexical-editor-root[data-v-9ca3d00d] .lexical-strikethrough{text-decoration:line-through}@media (prefers-color-scheme:dark){.markdown-editor-panel[data-v-9ca3d00d]{background-color:var(--card-background-color,#1e1e1e);box-shadow:0 8px 32px #00000080,0 2px 8px #0000004d}.markdown-editor-backdrop[data-v-9ca3d00d]{background-color:#000000b3}.lexical-toolbar[data-v-9ca3d00d]{background-color:var(--card-background-color,#1e1e1e);box-shadow:0 2px 4px #0000004d}.toolbar-btn[data-v-9ca3d00d]{color:var(--color,#c9d1d9)}.toolbar-btn[data-v-9ca3d00d]:hover{background-color:#ffffff1a;border-color:#ffffff1a}.toolbar-btn[data-v-9ca3d00d]:active{background-color:var(--primary);border-color:var(--primary)}.toolbar-divider[data-v-9ca3d00d]{background-color:#ffffff26}.toolbar-select[data-v-9ca3d00d]{color:var(--color,#c9d1d9);background-color:#0000;border-color:#fff3}.toolbar-select[data-v-9ca3d00d]:hover,.toolbar-select[data-v-9ca3d00d]:focus{border-color:var(--primary,#58a6ff)}#lexical-editor-root[data-v-9ca3d00d]{color:var(--color,#c9d1d9);background-color:var(--card-background-color,#1e1e1e)}#lexical-editor-root[data-v-9ca3d00d] blockquote{color:#8b949e;border-left-color:#fff3}#lexical-editor-root[data-v-9ca3d00d] code{color:#c9d1d9;background-color:#6e768166;border-color:#ffffff1a}#lexical-editor-root[data-v-9ca3d00d] .lexical-code{color:#c9d1d9;background-color:#0d1117;border-color:#ffffff1a}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenComment{color:#8b949e}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenPunctuation{color:#c9d1d9}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenProperty{color:#79c0ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenSelector{color:#a5d6ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenOperator{color:#79c0ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenAttr{color:#ff7b72}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenVariable{color:#ffa657}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenFunction{color:#d2a8ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-link,#lexical-editor-root[data-v-9ca3d00d] a{color:#58a6ff}}.editor-form[data-v-998cb080]{max-width:100%}.annotations-grid[data-v-998cb080]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1rem;display:grid}.annotations-grid .field-full-width[data-v-998cb080]{grid-column:1/-1}.form-actions[data-v-998cb080]{gap:.75rem;margin-top:1rem;display:flex}.form-actions button[data-v-998cb080]{width:auto;margin:0;padding:.5rem 1.25rem}.btn-back[data-v-998cb080]{align-items:center;gap:.25rem;text-decoration:none;display:inline-flex}.relations-editor .relation-item[data-v-998cb080]{align-items:center;gap:.5rem;padding:.25rem 0;display:flex}.relations-editor .relation-text[data-v-998cb080]{flex:1}.relations-editor .btn-remove[data-v-998cb080]{cursor:pointer;color:var(--pico-del-color,#c62828);background:0 0;border:none;width:auto;margin:0;padding:.25rem}.relations-editor .add-relation-row[data-v-998cb080]{align-items:flex-start;gap:.5rem;margin-top:.5rem;display:flex}.relations-editor .add-relation-row select[data-v-998cb080]{flex:1;margin-bottom:0}.relations-editor .btn-add[data-v-998cb080]{white-space:nowrap;margin-bottom:0}.no-relations[data-v-998cb080]{color:var(--pico-muted-color);font-style:italic}.field-group[data-v-998cb080]{margin-bottom:.5rem}.field-group .label-row[data-v-998cb080]{align-items:center;gap:.5rem;margin-bottom:.25rem;display:flex}.field-group .required[data-v-998cb080]{color:var(--pico-del-color,#c62828)}.field-group .field-description[data-v-998cb080]{color:var(--pico-muted-color);margin-top:.125rem;display:block}.field-group .field-error[data-v-998cb080]{color:var(--pico-del-color,#c62828);margin-top:.125rem;font-size:.875rem}.field-group .btn-edit-markdown[data-v-998cb080]{border:1px solid var(--pico-primary);color:var(--pico-primary);cursor:pointer;background:0 0;border-radius:4px;width:auto;margin:0;padding:.125rem .5rem;font-size:.75rem}.field-group .future-hint[data-v-998cb080]{color:var(--pico-muted-color);margin-top:.125rem;display:block}.field-group textarea[data-v-998cb080]{min-height:8rem;font-family:inherit}.field-group .code-field textarea[data-v-998cb080]{font-family:monospace}.yaml-output-container .yaml-success[data-v-998cb080]{color:var(--pico-ins-color,#2e7d32)}.yaml-output-container pre[data-v-998cb080]{max-height:60vh;overflow:auto}.yaml-output-container .yaml-actions[data-v-998cb080]{gap:.5rem;margin-top:.5rem;display:flex}.yaml-output-container .yaml-file-info[data-v-998cb080],.yaml-output-container .yaml-instructions[data-v-998cb080]{color:var(--pico-muted-color)}.yaml-output-container .copied[data-v-998cb080],.yaml-output-container .saved[data-v-998cb080]{opacity:.8}.conflict-error[data-v-998cb080]{background:var(--pico-del-color,#c62828);color:#fff;border-radius:4px;align-items:center;gap:.5rem;margin-bottom:1rem;padding:.75rem 1rem;display:flex}.editor-container[data-v-998cb080]{width:100%}.editor-header[data-v-998cb080]{border-bottom:1px solid var(--muted-border-color);justify-content:space-between;align-items:center;margin-bottom:1.5rem;padding-bottom:1rem;display:flex}.editor-header h2[data-v-998cb080]{align-items:center;gap:.75rem;margin:0;display:flex}.header-actions[data-v-998cb080]{align-items:center;gap:.5rem;display:flex}.btn-header[data-v-998cb080]{color:var(--muted-color);border-radius:var(--border-radius);cursor:pointer;background:0 0;border:1px solid #0000;align-items:center;gap:.35rem;padding:.4rem .75rem;font-size:.9rem;text-decoration:none;transition:all .15s;display:inline-flex}.btn-header[data-v-998cb080]:hover{color:var(--primary);border-color:var(--primary)}.yaml-output>header[data-v-998cb080]{justify-content:space-between;align-items:center;display:flex}.yaml-output>header h3[data-v-998cb080]{align-items:center;gap:.5rem;margin:0;display:flex}@media (width<=768px){.editor-header[data-v-998cb080]{flex-direction:column;align-items:flex-start;gap:1rem}.annotations-grid[data-v-998cb080]{grid-template-columns:1fr}}