archsight 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -4
  3. data/README.md +17 -65
  4. data/chart/archsight/README.md +1 -158
  5. data/docs/architecture.md +39 -0
  6. data/docs/docker.md +49 -0
  7. data/{lib/archsight/web/doc → docs}/index.md.erb +3 -1
  8. data/docs/kubernetes.md +149 -0
  9. data/docs/licenses.md +307 -0
  10. data/lib/archsight/cli.rb +1 -1
  11. data/lib/archsight/graph.rb +1 -51
  12. data/lib/archsight/helpers.rb +0 -20
  13. data/lib/archsight/import/handlers/github.rb +3 -1
  14. data/lib/archsight/import/handlers/gitlab.rb +15 -5
  15. data/lib/archsight/import/handlers/repository.rb +53 -5
  16. data/lib/archsight/import/handlers/rest_api.rb +13 -1
  17. data/lib/archsight/import/license_analyzer.rb +650 -0
  18. data/lib/archsight/linter.rb +1 -1
  19. data/lib/archsight/mcp/base.rb +11 -0
  20. data/lib/archsight/renderer.rb +4 -4
  21. data/lib/archsight/resources/business_actor.rb +17 -2
  22. data/lib/archsight/resources/technology_artifact.rb +48 -0
  23. data/lib/archsight/version.rb +1 -1
  24. data/lib/archsight/web/api/docs.rb +37 -2
  25. data/lib/archsight/web/api/json_helpers.rb +79 -13
  26. data/lib/archsight/web/api/openapi/spec.yaml +699 -0
  27. data/lib/archsight/web/api/routes.rb +23 -0
  28. data/lib/archsight/web/application.rb +40 -128
  29. data/lib/archsight/web/editor/helpers.rb +150 -0
  30. data/lib/archsight/web/editor/routes.rb +85 -212
  31. data/lib/archsight/web/public/vue/ApiDocsPage-B1RqTNqh.js +1 -0
  32. data/lib/archsight/web/public/vue/ApiDocsPage-DhNTOH4o.css +1 -0
  33. data/lib/archsight/web/public/vue/DocPage-DzwBgBd4.js +1 -0
  34. data/lib/archsight/web/public/vue/EditorPage-D_miHSv4.js +34 -0
  35. data/lib/archsight/web/public/vue/EditorPage-Dq0MuTnp.css +1 -0
  36. data/lib/archsight/web/public/vue/ErrorPage-CQQtPey3.js +2 -0
  37. data/lib/archsight/web/public/vue/ErrorPage-CwPT3JUr.css +1 -0
  38. data/lib/archsight/web/public/vue/GraphView-DRcIqAiR.css +1 -0
  39. data/lib/archsight/web/public/vue/GraphView-T9jFH_qg.js +1 -0
  40. data/lib/archsight/web/public/vue/InstanceRouter-1Sm-CRhf.js +2 -0
  41. data/lib/archsight/web/public/vue/InstanceRouter-BJkDRXZY.css +1 -0
  42. data/lib/archsight/web/public/vue/KindList-JA_L_-Cz.js +1 -0
  43. data/lib/archsight/web/public/vue/ResourceList-8iqavWdg.js +1 -0
  44. data/lib/archsight/web/public/vue/ResourceList-DP-z-j71.css +1 -0
  45. data/lib/archsight/web/public/vue/SearchResults-BGHbg48-.css +1 -0
  46. data/lib/archsight/web/public/vue/SearchResults-BdgFeHcm.js +1 -0
  47. data/lib/archsight/web/public/vue/_basePickBy-CVgieyx-.js +1 -0
  48. data/lib/archsight/web/public/vue/_baseUniq-BNfrOSaP.js +1 -0
  49. data/lib/archsight/web/public/vue/architectureDiagram-VXUJARFQ-CJXNpTr5.js +36 -0
  50. data/lib/archsight/web/public/vue/blockDiagram-VD42YOAC-B5488Hes.js +122 -0
  51. data/lib/archsight/web/public/vue/c4Diagram-YG6GDRKO-eYY3hprM.js +10 -0
  52. data/lib/archsight/web/public/vue/chunk-4BX2VUAB-ZoXeL4D1.js +1 -0
  53. data/lib/archsight/web/public/vue/chunk-55IACEB6-rNtQYnu_.js +1 -0
  54. data/lib/archsight/web/public/vue/chunk-B4BG7PRW-DolAeVV9.js +165 -0
  55. data/lib/archsight/web/public/vue/chunk-DI55MBZ5-DnN0f_hj.js +220 -0
  56. data/lib/archsight/web/public/vue/chunk-FMBD7UC4-BQWOCMuR.js +15 -0
  57. data/lib/archsight/web/public/vue/chunk-QN33PNHL-DId301Kb.js +1 -0
  58. data/lib/archsight/web/public/vue/chunk-QZHKN3VN-xbY0NLgv.js +1 -0
  59. data/lib/archsight/web/public/vue/chunk-TZMSLE5B-CgF9_37b.js +1 -0
  60. data/lib/archsight/web/public/vue/classDiagram-2ON5EDUG-jGlvI-Za.js +1 -0
  61. data/lib/archsight/web/public/vue/classDiagram-v2-WZHVMYZB-jGlvI-Za.js +1 -0
  62. data/lib/archsight/web/public/vue/clone-6iRPe1-W.js +1 -0
  63. data/lib/archsight/web/public/vue/cose-bilkent-S5V4N54A-CB9Zfu50.js +1 -0
  64. data/lib/archsight/web/public/vue/cytoscape.esm-5J0xJHOV.js +321 -0
  65. data/lib/archsight/web/public/vue/dagre-6UL2VRFP-BqkmE-LI.js +4 -0
  66. data/lib/archsight/web/public/vue/diagram-PSM6KHXK-CKBfqtw3.js +24 -0
  67. data/lib/archsight/web/public/vue/diagram-QEK2KX5R-B78rOlvK.js +43 -0
  68. data/lib/archsight/web/public/vue/diagram-S2PKOQOG-BlXC6Cia.js +24 -0
  69. data/lib/archsight/web/public/vue/erDiagram-Q2GNP2WA-BnliyziJ.js +60 -0
  70. data/lib/archsight/web/public/vue/flowDiagram-NV44I4VS-wuqPowTd.js +162 -0
  71. data/lib/archsight/web/public/vue/ganttDiagram-JELNMOA3-GSffAIH3.js +267 -0
  72. data/lib/archsight/web/public/vue/gitGraphDiagram-V2S2FVAM-OA7VyugW.js +65 -0
  73. data/lib/archsight/web/public/vue/graph-BXHAtA0S.js +1 -0
  74. data/lib/archsight/web/public/vue/graphviz-CJms5bxZ.js +13 -0
  75. data/lib/archsight/web/public/vue/index-DsEsN0_K.js +2 -0
  76. data/lib/archsight/web/public/vue/index-Tiu4C-Sb.css +1 -0
  77. data/lib/archsight/web/public/vue/infoDiagram-HS3SLOUP-nlVe2qgv.js +2 -0
  78. data/lib/archsight/web/public/vue/journeyDiagram-XKPGCS4Q-CtTIcKwf.js +139 -0
  79. data/lib/archsight/web/public/vue/kanban-definition-3W4ZIXB7-837KX0sW.js +89 -0
  80. data/lib/archsight/web/public/vue/katex-C-M49wc6.js +261 -0
  81. data/lib/archsight/web/public/vue/layout-DtE0QdL6.js +1 -0
  82. data/lib/archsight/web/public/vue/mermaid-DpPHPFQh.js +250 -0
  83. data/lib/archsight/web/public/vue/mindmap-definition-VGOIOE7T-9gLF2AoY.js +68 -0
  84. data/lib/archsight/web/public/vue/pieDiagram-ADFJNKIX-CyCNgw3u.js +30 -0
  85. data/lib/archsight/web/public/vue/quadrantDiagram-AYHSOK5B-CkPh8g02.js +7 -0
  86. data/lib/archsight/web/public/vue/requirementDiagram-UZGBJVZJ-Dkt6OSlY.js +64 -0
  87. data/lib/archsight/web/public/vue/sankeyDiagram-TZEHDZUN-BqprTk8x.js +10 -0
  88. data/lib/archsight/web/public/vue/sequenceDiagram-WL72ISMW-CTmTe1FQ.js +145 -0
  89. data/lib/archsight/web/public/vue/stateDiagram-FKZM4ZOC-CphqmkEU.js +1 -0
  90. data/lib/archsight/web/public/vue/stateDiagram-v2-4FDKWEC3-CxaDW5sW.js +1 -0
  91. data/lib/archsight/web/public/vue/timeline-definition-IT6M3QCI-CSQUZkyE.js +61 -0
  92. data/lib/archsight/web/public/vue/treemap-GDKQZRPO-DTojm7Yr.js +162 -0
  93. data/lib/archsight/web/public/{css/graph.css → vue/useGraphviz-A5s4h76R.js} +2 -1
  94. data/lib/archsight/web/public/vue/useHighlight-C6Kb5G3l.js +10 -0
  95. data/lib/archsight/web/public/vue/useMermaid-DqxTrLRB.js +1 -0
  96. data/lib/archsight/web/public/vue/usePanZoom-BybZ_rfh.js +11 -0
  97. data/lib/archsight/web/public/vue/xychartDiagram-PRI3JC2R-B1ZJZtDC.js +7 -0
  98. data/lib/archsight/web/public/vue.html +15 -0
  99. data/media/artifact.jpg +0 -0
  100. data/media/service.jpg +0 -0
  101. metadata +86 -84
  102. data/lib/archsight/web/public/css/artifact.css +0 -995
  103. data/lib/archsight/web/public/css/base.css +0 -201
  104. data/lib/archsight/web/public/css/editor.css +0 -863
  105. data/lib/archsight/web/public/css/highlight.min.css +0 -10
  106. data/lib/archsight/web/public/css/iconoir.css +0 -22
  107. data/lib/archsight/web/public/css/instance.css +0 -824
  108. data/lib/archsight/web/public/css/layout.css +0 -421
  109. data/lib/archsight/web/public/css/mermaid-layers.css +0 -188
  110. data/lib/archsight/web/public/css/pico.min.css +0 -4
  111. data/lib/archsight/web/public/img/archimate.png +0 -0
  112. data/lib/archsight/web/public/img/togaf-high-level.png +0 -0
  113. data/lib/archsight/web/public/js/editor.js +0 -421
  114. data/lib/archsight/web/public/js/graph-zoom.js +0 -18
  115. data/lib/archsight/web/public/js/highlight.min.js +0 -3899
  116. data/lib/archsight/web/public/js/htmx.min.js +0 -1
  117. data/lib/archsight/web/public/js/lexical-editor.js +0 -308
  118. data/lib/archsight/web/public/js/mermaid-init.js +0 -88
  119. data/lib/archsight/web/public/js/mermaid.min.js +0 -2811
  120. data/lib/archsight/web/public/js/sparkline.js +0 -42
  121. data/lib/archsight/web/public/js/svg-pan-zoom.min.js +0 -3
  122. data/lib/archsight/web/public/js/svg-zoom-controls.js +0 -93
  123. data/lib/archsight/web/views/api_docs.erb +0 -19
  124. data/lib/archsight/web/views/index.haml +0 -12
  125. data/lib/archsight/web/views/partials/artifact/_activity.haml +0 -55
  126. data/lib/archsight/web/views/partials/artifact/_agentic.haml +0 -25
  127. data/lib/archsight/web/views/partials/artifact/_deployment.haml +0 -29
  128. data/lib/archsight/web/views/partials/artifact/_git_info.haml +0 -16
  129. data/lib/archsight/web/views/partials/artifact/_language_stats.haml +0 -53
  130. data/lib/archsight/web/views/partials/artifact/_links.haml +0 -24
  131. data/lib/archsight/web/views/partials/artifact/_project_estimate.haml +0 -32
  132. data/lib/archsight/web/views/partials/artifact/_repositories.haml +0 -55
  133. data/lib/archsight/web/views/partials/artifact/_team.haml +0 -83
  134. data/lib/archsight/web/views/partials/artifact/_workflow.haml +0 -69
  135. data/lib/archsight/web/views/partials/components/_activity.haml +0 -37
  136. data/lib/archsight/web/views/partials/components/_git.haml +0 -17
  137. data/lib/archsight/web/views/partials/components/_jira.haml +0 -18
  138. data/lib/archsight/web/views/partials/components/_languages.haml +0 -29
  139. data/lib/archsight/web/views/partials/components/_owner.haml +0 -15
  140. data/lib/archsight/web/views/partials/components/_repositories.haml +0 -37
  141. data/lib/archsight/web/views/partials/components/_status.haml +0 -23
  142. data/lib/archsight/web/views/partials/editor/_field.haml +0 -80
  143. data/lib/archsight/web/views/partials/editor/_form.haml +0 -131
  144. data/lib/archsight/web/views/partials/editor/_relations.haml +0 -39
  145. data/lib/archsight/web/views/partials/editor/_yaml_output.haml +0 -33
  146. data/lib/archsight/web/views/partials/instance/_analysis_detail.haml +0 -67
  147. data/lib/archsight/web/views/partials/instance/_analysis_result.haml +0 -64
  148. data/lib/archsight/web/views/partials/instance/_detail.haml +0 -107
  149. data/lib/archsight/web/views/partials/instance/_graph.haml +0 -6
  150. data/lib/archsight/web/views/partials/instance/_import_detail.haml +0 -87
  151. data/lib/archsight/web/views/partials/instance/_list.haml +0 -84
  152. data/lib/archsight/web/views/partials/instance/_relations.haml +0 -43
  153. data/lib/archsight/web/views/partials/instance/_requirements.haml +0 -41
  154. data/lib/archsight/web/views/partials/instance/_view_detail.haml +0 -57
  155. data/lib/archsight/web/views/partials/layout/_content.haml +0 -50
  156. data/lib/archsight/web/views/partials/layout/_error.haml +0 -22
  157. data/lib/archsight/web/views/partials/layout/_head.haml +0 -26
  158. data/lib/archsight/web/views/partials/layout/_navigation.haml +0 -21
  159. data/lib/archsight/web/views/partials/layout/_sidebar.haml +0 -27
  160. data/lib/archsight/web/views/search.haml +0 -53
  161. /data/{lib/archsight/web/doc → docs}/archimate.md +0 -0
  162. /data/{lib/archsight/web/doc → docs}/computed_annotations.md +0 -0
  163. /data/{lib/archsight/web/doc → docs}/icons.md +0 -0
  164. /data/{lib/archsight/web/doc → docs}/import.md +0 -0
  165. /data/{lib/archsight/web/doc → docs}/modeling.md +0 -0
  166. /data/{lib/archsight/web/doc → docs}/search.md +0 -0
  167. /data/{lib/archsight/web/doc → docs}/togaf.md +0 -0
  168. /data/{lib/archsight/web/doc → docs}/tool.md +0 -0
@@ -4,6 +4,7 @@ require "sinatra/base"
4
4
  require "sinatra/extension"
5
5
  require_relative "../../editor"
6
6
  require_relative "form_builder"
7
+ require_relative "helpers"
7
8
 
8
9
  module Archsight
9
10
  module Web
@@ -12,220 +13,124 @@ module Archsight
12
13
  module Routes
13
14
  extend Sinatra::Extension
14
15
 
15
- helpers do
16
- # Get form fields for a resource kind
17
- def editor_fields(kind)
18
- FormBuilder.fields_for(kind)
19
- end
20
-
21
- # Get relation verbs for a resource kind
22
- def relation_verbs(kind)
23
- Archsight::Editor.relation_verbs(kind)
24
- end
25
-
26
- # Get available relations for a resource kind
27
- def available_relations(kind)
28
- Archsight::Editor.available_relations(kind)
29
- end
30
-
31
- # Extract annotations from form params
32
- def extract_annotations(params)
33
- annotations = params["annotations"] || {}
34
- annotations.transform_values { |v| v.is_a?(String) ? v.strip : v }
35
- end
16
+ helpers Archsight::Web::Editor::Helpers
36
17
 
37
- # Extract relations from form params
38
- # Relations come in as arrays: verb[], kind[], name[]
39
- def parse_form_relations(params)
40
- relations = []
41
- relation_data = params["relations"] || []
18
+ # --- HTML routes (serve Vue SPA) ---
42
19
 
43
- relation_data.each do |rel|
44
- next unless rel.is_a?(Hash)
45
-
46
- verb = rel["verb"]&.strip
47
- kind = rel["kind"]&.strip
48
- name = rel["name"]&.strip
49
-
50
- next if verb.nil? || verb.empty?
51
- next if kind.nil? || kind.empty?
52
- next if name.nil? || name.empty?
53
-
54
- # Find existing relation group or create new one
55
- existing = relations.find { |r| r[:verb] == verb && r[:kind] == kind }
56
- if existing
57
- existing[:names] << name unless existing[:names].include?(name)
58
- else
59
- relations << { verb: verb, kind: kind, names: [name] }
60
- end
61
- end
62
-
63
- relations
64
- end
20
+ get "/kinds/:kind/new" do
21
+ halt 404, "Kind not found" unless Archsight::Resources[params["kind"]]
22
+ serve_vue
23
+ end
65
24
 
66
- # Validate content hash for optimistic locking
67
- def validate_content_hash(instance, expected_hash)
68
- Archsight::Editor::ContentHasher.validate(
69
- path: instance.path_ref.path,
70
- start_line: instance.path_ref.line_no,
71
- expected_hash: expected_hash
72
- )
73
- end
25
+ get "/kinds/:kind/instances/:name/edit" do
26
+ halt 404, "Kind not found" unless Archsight::Resources[params["kind"]]
27
+ halt 404, "Instance not found" unless db.instance_by_kind(params["kind"], params["name"])
28
+ serve_vue
74
29
  end
75
30
 
76
- # Create mode - empty form
77
- get "/kinds/:kind/new" do
78
- @kind = params["kind"]
79
- @klass = Archsight::Resources[@kind]
80
- halt 404, "Kind not found" unless @klass
31
+ # Legacy POST generate routes — Vue SPA handles these now
32
+ post("/kinds/:kind/generate") { serve_vue }
33
+ post("/kinds/:kind/instances/:name/generate") { serve_vue }
81
34
 
82
- @editor_mode = true
83
- @mode = :create
84
- @name = ""
85
- @annotations = {}
86
- @relations = []
87
- @fields = editor_fields(@kind)
88
- @errors = {}
35
+ # --- JSON API routes ---
89
36
 
90
- haml :index
91
- end
37
+ get "/api/v1/editor/kinds/:kind/form" do
38
+ content_type :json
39
+ kind = params["kind"]
40
+ klass = Archsight::Resources[kind]
41
+ halt 404, JSON.generate({ error: "Kind not found" }) unless klass
92
42
 
93
- # Create mode - generate YAML
94
- post "/kinds/:kind/generate" do
95
- @kind = params["kind"]
96
- @klass = Archsight::Resources[@kind]
97
- halt 404, "Kind not found" unless @klass
43
+ data = build_form_metadata(kind, klass)
44
+ data[:mode] = "create"
45
+ JSON.generate(data)
46
+ end
98
47
 
99
- @editor_mode = true
100
- @mode = :create
101
- @name = (params["name"] || "").strip
102
- @annotations = extract_annotations(params)
103
- @relations = parse_form_relations(params)
104
- @fields = editor_fields(@kind)
48
+ get "/api/v1/editor/kinds/:kind/instances/:name/form" do
49
+ content_type :json
50
+ kind = params["kind"]
51
+ klass = Archsight::Resources[kind]
52
+ halt 404, JSON.generate({ error: "Kind not found" }) unless klass
105
53
 
106
- # Validate
107
- validation = Archsight::Editor.validate(@kind, name: @name, annotations: @annotations)
54
+ instance = db.instance_by_kind(kind, params["name"])
55
+ halt 404, JSON.generate({ error: "Instance not found" }) unless instance
108
56
 
109
- unless validation[:valid]
110
- @errors = validation[:errors]
111
- return haml :index
112
- end
57
+ data = build_form_metadata(kind, klass)
58
+ data[:mode] = "edit"
59
+ data[:name] = instance.name
60
+ data[:annotations] = instance.annotations.dup
61
+ data[:relations] = extract_instance_relations(instance)
62
+ data[:path_ref] = "#{instance.path_ref.path}:#{instance.path_ref.line_no}"
113
63
 
114
- # Build and render YAML
115
- resource = Archsight::Editor.build_resource(
116
- kind: @kind,
117
- name: @name,
118
- annotations: @annotations,
119
- relations: @relations
64
+ original_content = Archsight::Editor::FileWriter.read_document(
65
+ path: instance.path_ref.path, start_line: instance.path_ref.line_no
120
66
  )
121
-
122
- @generated_yaml = Archsight::Editor.to_yaml(resource)
123
- @errors = {}
124
-
125
- haml :index
67
+ data[:content_hash] = Archsight::Editor::ContentHasher.hash(original_content)
68
+ JSON.generate(data)
126
69
  end
127
70
 
128
- # Edit mode - pre-filled form
129
- get "/kinds/:kind/instances/:name/edit" do
130
- @kind = params["kind"]
131
- @instance_name = params["name"]
132
- @klass = Archsight::Resources[@kind]
133
- halt 404, "Kind not found" unless @klass
71
+ post "/api/v1/editor/kinds/:kind/generate" do
72
+ content_type :json
73
+ kind = params["kind"]
74
+ halt 404, JSON.generate({ error: "Kind not found" }) unless Archsight::Resources[kind]
134
75
 
135
- instance = db.instance_by_kind(@kind, @instance_name)
136
- halt 404, "Instance not found" unless instance
76
+ body = parse_json_body
77
+ name = (body["name"] || "").strip
78
+ annotations = extract_annotations(body)
79
+ relations = parse_json_relations(body["relations"] || [])
137
80
 
138
- @editor_mode = true
139
- @mode = :edit
140
- @name = instance.name
141
- @annotations = instance.annotations.dup
142
- @relations = extract_instance_relations(instance)
143
- @fields = editor_fields(@kind)
144
- @errors = {}
81
+ validation = Archsight::Editor.validate(kind, name: name, annotations: annotations)
82
+ return JSON.generate({ yaml: nil, errors: validation[:errors] }) unless validation[:valid]
145
83
 
146
- # Compute content hash for optimistic locking
147
- original_content = Archsight::Editor::FileWriter.read_document(
148
- path: instance.path_ref.path,
149
- start_line: instance.path_ref.line_no
84
+ resource = Archsight::Editor.build_resource(
85
+ kind: kind, name: name, annotations: annotations, relations: relations
150
86
  )
151
- @content_hash = Archsight::Editor::ContentHasher.hash(original_content)
152
-
153
- haml :index
87
+ JSON.generate({ yaml: Archsight::Editor.to_yaml(resource), errors: nil })
154
88
  end
155
89
 
156
- # Edit mode - generate YAML
157
- post "/kinds/:kind/instances/:name/generate" do
158
- @kind = params["kind"]
159
- @instance_name = params["name"]
160
- @klass = Archsight::Resources[@kind]
161
- halt 404, "Kind not found" unless @klass
162
-
163
- # Get original instance for path_ref (for inline save)
164
- original_instance = db.instance_by_kind(@kind, @instance_name)
165
- @path_ref = original_instance&.path_ref
166
-
167
- @editor_mode = true
168
- @mode = :edit
169
- @name = (params["name_field"] || @instance_name).strip
170
- @annotations = extract_annotations(params)
171
- @relations = parse_form_relations(params)
172
- @fields = editor_fields(@kind)
173
- @content_hash = params["content_hash"]
90
+ post "/api/v1/editor/kinds/:kind/instances/:name/generate" do
91
+ content_type :json
92
+ kind = params["kind"]
93
+ instance_name = params["name"]
94
+ halt 404, JSON.generate({ error: "Kind not found" }) unless Archsight::Resources[kind]
174
95
 
175
- # Validate
176
- validation = Archsight::Editor.validate(@kind, name: @name, annotations: @annotations)
96
+ body = parse_json_body
97
+ name = (body["name"] || instance_name).strip
98
+ annotations = extract_annotations(body)
99
+ relations = parse_json_relations(body["relations"] || [])
177
100
 
178
- unless validation[:valid]
179
- @errors = validation[:errors]
180
- return haml :index
181
- end
101
+ validation = Archsight::Editor.validate(kind, name: name, annotations: annotations)
102
+ return JSON.generate({ yaml: nil, errors: validation[:errors] }) unless validation[:valid]
182
103
 
183
- # Build and render YAML
184
104
  resource = Archsight::Editor.build_resource(
185
- kind: @kind,
186
- name: @name,
187
- annotations: @annotations,
188
- relations: @relations
105
+ kind: kind, name: name, annotations: annotations, relations: relations
189
106
  )
190
107
 
191
- @generated_yaml = Archsight::Editor.to_yaml(resource)
192
- @errors = {}
108
+ original = db.instance_by_kind(kind, instance_name)
109
+ path_ref = original&.path_ref ? "#{original.path_ref.path}:#{original.path_ref.line_no}" : nil
193
110
 
194
- haml :index
111
+ JSON.generate({
112
+ yaml: Archsight::Editor.to_yaml(resource), errors: nil,
113
+ path_ref: path_ref, content_hash: body["content_hash"]
114
+ })
195
115
  end
196
116
 
197
- # Save YAML to source file (inline edit)
198
117
  post "/api/v1/editor/kinds/:kind/instances/:name/save" do
199
118
  content_type :json
200
-
201
- # Check if inline edit is enabled
202
119
  halt 403, JSON.generate({ success: false, error: "Inline edit is disabled. Start server with --inline-edit flag." }) unless settings.inline_edit_enabled
203
120
 
204
- kind = params["kind"]
205
- name = params["name"]
206
-
207
- begin
208
- body = JSON.parse(request.body.read)
209
- yaml_content = body["yaml"]
210
- expected_hash = body["content_hash"]
211
- rescue JSON::ParserError
212
- halt 400, JSON.generate({ success: false, error: "Invalid JSON" })
213
- end
214
-
215
- instance = db.instance_by_kind(kind, name)
121
+ body = parse_json_body
122
+ instance = db.instance_by_kind(params["kind"], params["name"])
216
123
  halt 404, JSON.generate({ success: false, error: "Instance not found" }) unless instance
217
124
 
218
- # Optimistic locking: verify content hasn't changed since edit started
219
- if (conflict = validate_content_hash(instance, expected_hash))
125
+ if (conflict = validate_content_hash(instance, body["content_hash"]))
220
126
  status 409
221
127
  return JSON.generate({ success: false }.merge(conflict))
222
128
  end
223
129
 
224
130
  begin
225
131
  Archsight::Editor::FileWriter.replace_document(
226
- path: instance.path_ref.path,
227
- start_line: instance.path_ref.line_no,
228
- new_yaml: yaml_content
132
+ path: instance.path_ref.path, start_line: instance.path_ref.line_no,
133
+ new_yaml: body["yaml"]
229
134
  )
230
135
  db.reload!
231
136
  JSON.generate({ success: true, message: "Saved to #{instance.path_ref}" })
@@ -235,56 +140,24 @@ module Archsight
235
140
  end
236
141
  end
237
142
 
238
- # HTMX API - Get instance names for a kind (for relation dropdown)
239
143
  get "/api/v1/editor/kinds/:kind/instances" do
240
144
  kind = params["kind"]
241
- klass = Archsight::Resources[kind]
242
- halt 404, "Kind not found" unless klass
243
-
244
- instances = db.instances_by_kind(kind).keys.sort
245
-
145
+ halt 404, "Kind not found" unless Archsight::Resources[kind]
246
146
  content_type :json
247
- JSON.generate(instances)
147
+ JSON.generate(db.instances_by_kind(kind).keys.sort)
248
148
  end
249
149
 
250
- # HTMX API - Get valid target kinds for a verb
251
150
  get "/api/v1/editor/relation-kinds" do
252
- kind = params["kind"]
253
- verb = params["verb"]
254
- halt 400, "Kind and verb required" unless kind && verb
255
-
256
- target_kinds = Archsight::Editor.target_kinds_for_verb(kind, verb)
257
-
151
+ halt 400, "Kind and verb required" unless params["kind"] && params["verb"]
258
152
  content_type :json
259
- JSON.generate(target_kinds)
153
+ JSON.generate(Archsight::Editor.target_kinds_for_verb(params["kind"], params["verb"]))
260
154
  end
261
155
 
262
156
  helpers do
263
- # Extract relations from an existing instance into form format
264
- # Returns relations with target class names (e.g., "BusinessActor")
265
- # not relation names (e.g., "businessActors")
266
- def extract_instance_relations(instance)
267
- relations = []
268
-
269
- instance.spec.each do |verb, relation_groups|
270
- next unless relation_groups.is_a?(Hash)
271
-
272
- relation_groups.each do |relation_name, targets|
273
- next unless targets.is_a?(Array)
274
-
275
- # Look up the target class name from the relation definition
276
- target_class = Archsight::Editor.target_class_for_relation(instance.kind, verb, relation_name)
277
- next unless target_class
278
-
279
- targets.each do |target|
280
- # Target could be an instance object or a string
281
- target_name = target.respond_to?(:name) ? target.name : target.to_s
282
- relations << { verb: verb, kind: target_class, name: target_name }
283
- end
284
- end
285
- end
286
-
287
- relations
157
+ def parse_json_body
158
+ JSON.parse(request.body.read)
159
+ rescue JSON::ParserError
160
+ halt 400, JSON.generate({ error: "Invalid JSON" })
288
161
  end
289
162
  end
290
163
  end
@@ -0,0 +1 @@
1
+ import{_ as u,l as _,c as s,a as c,v as m,t as f,b as v,r as i,z as k,o as d,B as w}from"./index-DsEsN0_K.js";import"./mermaid-DpPHPFQh.js";const h={class:"api-docs-page"},y={key:0,class:"pico-color-red-500"},g={__name:"ApiDocsPage",setup(B){const l=k(),r=i(null),t=i(null);function p(o){return new Promise((e,n)=>{if(window.Redoc)return e();const a=document.createElement("script");a.src=o,a.onload=e,a.onerror=()=>n(new Error("Failed to load ReDoc")),document.head.appendChild(a)})}return _(async()=>{try{await p("https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"),window.Redoc.init("/api/v1/openapi.yaml",{},r.value)}catch(o){t.value=o.message}}),(o,e)=>(d(),s("div",h,[c("a",{href:"#",class:"back-link",onClick:e[0]||(e[0]=m(n=>w(l).back(),["prevent"]))},"← Back"),t.value?(d(),s("div",y,f(t.value),1)):v("",!0),c("div",{ref_key:"container",ref:r},null,512)]))}},R=u(g,[["__scopeId","data-v-0e03906d"]]);export{R as default};
@@ -0,0 +1 @@
1
+ .back-link[data-v-0e03906d]{position:fixed;top:12px;right:12px;z-index:100;padding:6px 14px;background:#fff;border:1px solid #ddd;border-radius:4px;text-decoration:none;font-size:.875rem;box-shadow:0 1px 4px #0000001a}
@@ -0,0 +1 @@
1
+ import{w as m,c as i,a as f,r as a,o as s,j as p,k as d}from"./index-DsEsN0_K.js";import{r as v}from"./useMermaid-DqxTrLRB.js";import{h as _}from"./useHighlight-C6Kb5G3l.js";import"./mermaid-DpPHPFQh.js";import"./usePanZoom-BybZ_rfh.js";const k=["innerHTML"],g={key:1},D={__name:"DocPage",props:{filename:String},setup(c){const n=c,o=a(""),t=a(!0),e=a(null);async function u(){t.value=!0;const r=await p(n.filename);o.value=r||"<p>Documentation not found.</p>",t.value=!1,await d(),e.value&&(_(e.value),v(e.value))}return m(()=>n.filename,u,{immediate:!0}),(r,l)=>t.value?(s(),i("article",g,[...l[0]||(l[0]=[f("p",null,"Loading...",-1)])])):(s(),i("div",{key:0,ref_key:"articleEl",ref:e,innerHTML:o.value},null,8,k))}};export{D as default};