mxrb 0.1.3 → 0.1.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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mxrb +455 -10
  3. data/docs/de-DE/README.md +1 -0
  4. data/docs/de-DE/oql-sql.md +42 -0
  5. data/docs/de-DE/project-structure.md +201 -2
  6. data/docs/de-DE/runtime-ruby.md +104 -0
  7. data/docs/de-DE/runtime-schema-migrations.md +44 -0
  8. data/docs/de-DE/validation-matrix.md +42 -4
  9. data/docs/en-US/README.md +1 -0
  10. data/docs/en-US/oql-sql.md +41 -0
  11. data/docs/en-US/project-structure.md +192 -2
  12. data/docs/en-US/runtime-ruby.md +100 -0
  13. data/docs/en-US/runtime-schema-migrations.md +41 -0
  14. data/docs/en-US/validation-matrix.md +39 -4
  15. data/docs/pt-BR/README.md +1 -0
  16. data/docs/pt-BR/oql-sql.md +41 -0
  17. data/docs/pt-BR/project-structure.md +197 -2
  18. data/docs/pt-BR/runtime-ruby.md +108 -0
  19. data/docs/pt-BR/runtime-schema-migrations.md +40 -0
  20. data/docs/pt-BR/validation-matrix.md +42 -5
  21. data/lib/mxrb/cli/help.rb +426 -0
  22. data/lib/mxrb/cli/release.rb +137 -0
  23. data/lib/mxrb/compiler/compatibility_analyzer.rb +1 -1
  24. data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +8 -1
  25. data/lib/mxrb/compiler/domain_document_compiler.rb +3 -0
  26. data/lib/mxrb/compiler/gallery_bundle_compiler.rb +11 -1
  27. data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +76 -19
  28. data/lib/mxrb/compiler/legacy_custom_widget_compiler.rb +159 -0
  29. data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +43 -7
  30. data/lib/mxrb/compiler/legacy_page_builder.rb +1248 -23
  31. data/lib/mxrb/compiler/page_bundle_compiler.rb +13 -4
  32. data/lib/mxrb/compiler/web_bundle_builder.rb +5 -1
  33. data/lib/mxrb/compiler/web_shell_materializer.rb +42 -2
  34. data/lib/mxrb/compiler/widget_package_extractor.rb +3 -0
  35. data/lib/mxrb/domain_diagram/lifecycle.rb +272 -0
  36. data/lib/mxrb/domain_diagram.rb +409 -0
  37. data/lib/mxrb/dsl/builder.rb +43 -9
  38. data/lib/mxrb/environment.rb +226 -0
  39. data/lib/mxrb/exporter.rb +89 -15
  40. data/lib/mxrb/http/server.rb +107 -0
  41. data/lib/mxrb/initializer.rb +80 -9
  42. data/lib/mxrb/integrity/validator.rb +14 -0
  43. data/lib/mxrb/io/bson_codec.rb +42 -1
  44. data/lib/mxrb/io/mpr_file.rb +146 -3
  45. data/lib/mxrb/model/attribute.rb +3 -0
  46. data/lib/mxrb/model/entity.rb +28 -2
  47. data/lib/mxrb/model/microflow.rb +3 -1
  48. data/lib/mxrb/model/page.rb +257 -17
  49. data/lib/mxrb/model/project.rb +3 -0
  50. data/lib/mxrb/modeler/catalog.rb +178 -0
  51. data/lib/mxrb/oql/reverse_translator.rb +394 -0
  52. data/lib/mxrb/oql/server.rb +4 -8
  53. data/lib/mxrb/oql.rb +23 -1
  54. data/lib/mxrb/project_lifecycle.rb +5 -2
  55. data/lib/mxrb/ruby_app/exporter.rb +2508 -0
  56. data/lib/mxrb/ruby_app/preset.rb +506 -0
  57. data/lib/mxrb/ruby_app/session_manager.rb +122 -0
  58. data/lib/mxrb/ruby_app.rb +1369 -0
  59. data/lib/mxrb/runtime/access_control.rb +408 -0
  60. data/lib/mxrb/runtime/native.rb +1015 -93
  61. data/lib/mxrb/runtime/scheduler.rb +396 -0
  62. data/lib/mxrb/runtime/schema_migrator.rb +598 -0
  63. data/lib/mxrb/runtime/shared_store.rb +335 -0
  64. data/lib/mxrb/runtime/sqlite_store.rb +729 -0
  65. data/lib/mxrb/scaffold/generator.rb +11 -3
  66. data/lib/mxrb/uml/activity_diagram.rb +118 -0
  67. data/lib/mxrb/uml/class_diagram.rb +118 -0
  68. data/lib/mxrb/uml/sequence_diagram.rb +112 -0
  69. data/lib/mxrb/uml/server.rb +146 -0
  70. data/lib/mxrb/uml/support.rb +35 -0
  71. data/lib/mxrb/version.rb +1 -1
  72. data/lib/mxrb/web_ui/assets/abnfDiagram-N423BO3Z-C4JD3cEC.js +1 -0
  73. data/lib/mxrb/web_ui/assets/arc-3Z53kgFp.js +1 -0
  74. data/lib/mxrb/web_ui/assets/architecture-TIHT7OUA-BStBTLea.js +1 -0
  75. data/lib/mxrb/web_ui/assets/architectureDiagram-T3A2C74G-DBwqnM4J.js +36 -0
  76. data/lib/mxrb/web_ui/assets/array-BifhSqXX.js +1 -0
  77. data/lib/mxrb/web_ui/assets/blockDiagram-VBNYF7ZC-DLtuHt-j.js +132 -0
  78. data/lib/mxrb/web_ui/assets/c4Diagram-5PPSVZJV-ConSk_pe.js +10 -0
  79. data/lib/mxrb/web_ui/assets/channel-BhPvD7Dt.js +1 -0
  80. data/lib/mxrb/web_ui/assets/chunk-2GRJ4B5K-D2HpWsf_.js +1 -0
  81. data/lib/mxrb/web_ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  82. data/lib/mxrb/web_ui/assets/chunk-4I5QYGJK-CiIdNwaF.js +1 -0
  83. data/lib/mxrb/web_ui/assets/chunk-5RXB4S5H-Bu_AaEkE.js +231 -0
  84. data/lib/mxrb/web_ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  85. data/lib/mxrb/web_ui/assets/chunk-6Q2QTUOP-wQkNmt_X.js +88 -0
  86. data/lib/mxrb/web_ui/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  87. data/lib/mxrb/web_ui/assets/chunk-GF5L2VYU-G8MmiBzU.js +206 -0
  88. data/lib/mxrb/web_ui/assets/chunk-I66GZJ75-CulcrLPh.js +127 -0
  89. data/lib/mxrb/web_ui/assets/chunk-JQJVKLGR-ukjzcdlL.js +156 -0
  90. data/lib/mxrb/web_ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  91. data/lib/mxrb/web_ui/assets/chunk-KBJHAD2P-Wm35lYov.js +1 -0
  92. data/lib/mxrb/web_ui/assets/chunk-KEIR6QF5-BICK3FdT.js +161 -0
  93. data/lib/mxrb/web_ui/assets/chunk-NSK5VX7P-D2nYrCY4.js +2 -0
  94. data/lib/mxrb/web_ui/assets/chunk-QR6OTTB3-BrdIzg4Q.js +62 -0
  95. data/lib/mxrb/web_ui/assets/chunk-RYQCIY6F-CCfZMcW6.js +1 -0
  96. data/lib/mxrb/web_ui/assets/chunk-UBXNYLIW-BPEVYsFK.js +1 -0
  97. data/lib/mxrb/web_ui/assets/chunk-W5SLKNZC-2E6OPhA4.js +1 -0
  98. data/lib/mxrb/web_ui/assets/chunk-WRU74C26-CBIvOyWp.js +70 -0
  99. data/lib/mxrb/web_ui/assets/chunk-XXDRQBXY-pH58XAyl.js +1 -0
  100. data/lib/mxrb/web_ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  101. data/lib/mxrb/web_ui/assets/classDiagram-JCYQIIEL-xiqo4D89.js +1 -0
  102. data/lib/mxrb/web_ui/assets/classDiagram-v2-OCEON4UE-xiqo4D89.js +1 -0
  103. data/lib/mxrb/web_ui/assets/cose-bilkent-JH36ORCC-t_flPpii.js +1 -0
  104. data/lib/mxrb/web_ui/assets/cynefin-VYW2F7L2-BIqktTEv.js +1 -0
  105. data/lib/mxrb/web_ui/assets/cynefinDiagram-MW4NZA55-C2d--lut.js +62 -0
  106. data/lib/mxrb/web_ui/assets/cytoscape.esm-B-NFISlW.js +321 -0
  107. data/lib/mxrb/web_ui/assets/dagre-Buvkdvvj.js +1 -0
  108. data/lib/mxrb/web_ui/assets/dagre-VZM6K2ZE-CSz2_tb5.js +4 -0
  109. data/lib/mxrb/web_ui/assets/defaultLocale-BFoDCU3G.js +1 -0
  110. data/lib/mxrb/web_ui/assets/diagram-7IWD3JNH-DCt7u7V_.js +30 -0
  111. data/lib/mxrb/web_ui/assets/diagram-B4RE2ZJO-DqZBqkMu.js +3 -0
  112. data/lib/mxrb/web_ui/assets/diagram-LBJQPF4R-ClIYbIsI.js +24 -0
  113. data/lib/mxrb/web_ui/assets/diagram-Q27KOJAE-DFxhvthJ.js +24 -0
  114. data/lib/mxrb/web_ui/assets/diagram-UB23O5K3-BSUTXptv.js +41 -0
  115. data/lib/mxrb/web_ui/assets/dist-D2qOEbeJ.js +1 -0
  116. data/lib/mxrb/web_ui/assets/domain-mGOZ1KCG.css +1 -0
  117. data/lib/mxrb/web_ui/assets/domain-qsjXhy4v.js +1 -0
  118. data/lib/mxrb/web_ui/assets/ebnfDiagram-BXEA7PRR-B5reICEi.js +1 -0
  119. data/lib/mxrb/web_ui/assets/erDiagram-JOGREHBK-Co5Mxn2p.js +85 -0
  120. data/lib/mxrb/web_ui/assets/eventmodeling-45OFAUF4-C1ylhSks.js +1 -0
  121. data/lib/mxrb/web_ui/assets/flowDiagram-UKHOOZJN-CHXaJIaS.js +1 -0
  122. data/lib/mxrb/web_ui/assets/ganttDiagram-PKOTCBZU-DPPPaWaz.js +292 -0
  123. data/lib/mxrb/web_ui/assets/gitGraph-TEB2WS4Q-qS77ywA9.js +1 -0
  124. data/lib/mxrb/web_ui/assets/gitGraphDiagram-DS77QQ5N-DpAv6-BT.js +106 -0
  125. data/lib/mxrb/web_ui/assets/graphlib-DS17s2tU.js +1 -0
  126. data/lib/mxrb/web_ui/assets/info-DKCQHKI2-C9VD1Y4e.js +1 -0
  127. data/lib/mxrb/web_ui/assets/infoDiagram-6WML65LV-Bk1kbcCy.js +2 -0
  128. data/lib/mxrb/web_ui/assets/init-C-OQMol4.js +1 -0
  129. data/lib/mxrb/web_ui/assets/ishikawaDiagram-WSZJBQD7-0gcISBFR.js +70 -0
  130. data/lib/mxrb/web_ui/assets/journeyDiagram-NVQOT4AX-Dt0RQnlh.js +139 -0
  131. data/lib/mxrb/web_ui/assets/jsx-runtime-DV0a5kSb.js +9 -0
  132. data/lib/mxrb/web_ui/assets/kanban-definition-27J2QSJJ-DIPQM7w9.js +89 -0
  133. data/lib/mxrb/web_ui/assets/katex-CXMH3UgJ.js +257 -0
  134. data/lib/mxrb/web_ui/assets/line-C0rYD-tL.js +1 -0
  135. data/lib/mxrb/web_ui/assets/linear-BPPxWorj.js +1 -0
  136. data/lib/mxrb/web_ui/assets/map-BaFkSB1l.js +1 -0
  137. data/lib/mxrb/web_ui/assets/mermaid-parser.core-B_UPzTxa.js +7 -0
  138. data/lib/mxrb/web_ui/assets/mindmap-definition-FAOFIHXS-CdJRSMuk.js +96 -0
  139. data/lib/mxrb/web_ui/assets/modeler-BNqHthwk.css +1 -0
  140. data/lib/mxrb/web_ui/assets/modeler-CQmqflbg.js +1 -0
  141. data/lib/mxrb/web_ui/assets/ordinal-BDEzSJ7C.js +1 -0
  142. data/lib/mxrb/web_ui/assets/packet-7NZHBO7P-CwQ4NSBs.js +1 -0
  143. data/lib/mxrb/web_ui/assets/path-fybaL0A-.js +1 -0
  144. data/lib/mxrb/web_ui/assets/pegDiagram-VL7TDLO6-D40S7ppx.js +1 -0
  145. data/lib/mxrb/web_ui/assets/pie-RZYD4A2V-CJroHMKh.js +1 -0
  146. data/lib/mxrb/web_ui/assets/pieDiagram-7S7Q4E2Y-QB54__BV.js +39 -0
  147. data/lib/mxrb/web_ui/assets/quadrantDiagram-CIZ2JOQS-DBhDS6ag.js +7 -0
  148. data/lib/mxrb/web_ui/assets/radar-I7S5WNFK-DdIUwd2d.js +1 -0
  149. data/lib/mxrb/web_ui/assets/railroad-3IZDKUUU-B1Lw6Xyq.js +1 -0
  150. data/lib/mxrb/web_ui/assets/railroad-abnf-AHOZXSZD-CQgQoeak.js +1 -0
  151. data/lib/mxrb/web_ui/assets/railroad-ebnf-EBAXGLYW-D7qEQ2x8.js +1 -0
  152. data/lib/mxrb/web_ui/assets/railroad-peg-LSFZ7HO6-C005Vxbb.js +1 -0
  153. data/lib/mxrb/web_ui/assets/railroadDiagram-AXF67PYL-BBP2qk7M.js +1 -0
  154. data/lib/mxrb/web_ui/assets/requirementDiagram-LRYGKXZP-D2cLsAoI.js +84 -0
  155. data/lib/mxrb/web_ui/assets/rough.esm-Dy-Kn_BL.js +1 -0
  156. data/lib/mxrb/web_ui/assets/sankeyDiagram-W5VNT64P-Bpi3FiP2.js +40 -0
  157. data/lib/mxrb/web_ui/assets/sequenceDiagram-SI44F4Z6-DCRcDlVe.js +162 -0
  158. data/lib/mxrb/web_ui/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  159. data/lib/mxrb/web_ui/assets/src-CvxfUak2.js +1 -0
  160. data/lib/mxrb/web_ui/assets/stateDiagram-OKZ733FA-C2GCEoxl.js +1 -0
  161. data/lib/mxrb/web_ui/assets/stateDiagram-v2-UEYNNEHI-D2gWgedh.js +1 -0
  162. data/lib/mxrb/web_ui/assets/swimlanes-SLNWSIFB-CRI9irEW.js +2 -0
  163. data/lib/mxrb/web_ui/assets/swimlanesDiagram-ULZ7WXOC-C5Wb_-hZ.js +8 -0
  164. data/lib/mxrb/web_ui/assets/theme-_mxc7BdY.css +1 -0
  165. data/lib/mxrb/web_ui/assets/timeline-definition-Z64GVDOM-B1-mNg-h.js +120 -0
  166. data/lib/mxrb/web_ui/assets/treeView-QDETBFTQ-7CTvIwKq.js +1 -0
  167. data/lib/mxrb/web_ui/assets/treemap-6X3UGDF4-CtL22pq5.js +1 -0
  168. data/lib/mxrb/web_ui/assets/uml-C3KAFGTq.js +51 -0
  169. data/lib/mxrb/web_ui/assets/uml-Dn-qd01X.css +1 -0
  170. data/lib/mxrb/web_ui/assets/vennDiagram-T6HMQDX7-BzVnUEAy.js +34 -0
  171. data/lib/mxrb/web_ui/assets/wardley-OPB4EBWU-JNi-QNag.js +1 -0
  172. data/lib/mxrb/web_ui/assets/wardleyDiagram-T6FBY63Y-BUwmAQ-B.js +78 -0
  173. data/lib/mxrb/web_ui/assets/xychartDiagram-ELKLHX3M-DEQS8sKp.js +7 -0
  174. data/lib/mxrb/web_ui/domain.html +15 -0
  175. data/lib/mxrb/web_ui/modeler.html +16 -0
  176. data/lib/mxrb/web_ui/uml.html +31 -0
  177. data/lib/mxrb/web_ui.rb +89 -0
  178. data/lib/mxrb/widget_certification.rb +208 -0
  179. data/lib/mxrb/writer.rb +200 -24
  180. data/lib/mxrb.rb +26 -1
  181. metadata +156 -5
@@ -157,15 +157,23 @@ module Mxrb
157
157
  @transaction.write(path, lines.join)
158
158
  end
159
159
 
160
- def connect_project_dotenv(source)
160
+ def connect_project_dotenv(source) # rubocop:disable Metrics/MethodLength
161
+ return source if source.include?('Mxrb::Environment.load(root: __dir__).apply')
162
+
161
163
  dotenv_line = 'Dotenv.load(File.join(__dir__, ".env"))'
162
164
  return source if source.include?(dotenv_line)
163
165
 
164
166
  lines = source.lines
165
167
  require_line = lines.index { _1.match?(%r{^require ["']dotenv/load["']\s*$}) }
166
- raise ArgumentError, 'project.rb must require dotenv/load before scaffolding demo users' unless require_line
168
+ return lines.insert(require_line + 1, "#{dotenv_line}\n").join if require_line
169
+
170
+ mxrb_require = lines.index { _1.match?(/^require ["']mxrb["']\s*$/) }
171
+ unless mxrb_require
172
+ raise ArgumentError,
173
+ 'project.rb must require mxrb or dotenv/load before scaffolding demo users'
174
+ end
167
175
 
168
- lines.insert(require_line + 1, "#{dotenv_line}\n").join
176
+ lines.insert(mxrb_require + 1, "\nMxrb::Environment.load(root: __dir__).apply\n").join
169
177
  end
170
178
 
171
179
  def security_initialization_error
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support'
4
+
5
+ module Mxrb
6
+ module Uml
7
+ # Renders the native object/sequence-flow graph of one Mendix microflow.
8
+ # rubocop:disable Metrics
9
+ class ActivityDiagram
10
+ def initialize(microflow)
11
+ @microflow = microflow
12
+ @objects = Array(microflow.objects)
13
+ @flows = Array(microflow.flows)
14
+ @objects_by_id = @objects.to_h { [raw_object_id(_1), _1] }
15
+ end
16
+
17
+ def to_mermaid
18
+ lines = ['flowchart TD']
19
+ @objects.each { lines << " #{mermaid_node(_1)}" }
20
+ @flows.each do |flow|
21
+ line = flow_line(flow, :mermaid)
22
+ lines << " #{line}" if line
23
+ end
24
+ "#{lines.join("\n")}\n"
25
+ end
26
+
27
+ def to_plantuml
28
+ lines = ['@startuml', "title #{Support.plantuml_text(@microflow.name)}"]
29
+ @objects.each { lines << plantuml_node(_1) }
30
+ @flows.each do |flow|
31
+ line = flow_line(flow, :plantuml)
32
+ lines << line if line
33
+ end
34
+ lines << '@enduml'
35
+ "#{lines.join("\n")}\n"
36
+ end
37
+
38
+ private
39
+
40
+ def mermaid_node(object)
41
+ id = node_id(object)
42
+ label = Support.mermaid_text(object_label(object))
43
+ case object_type(object)
44
+ when 'StartEvent' then %(#{id}(["Start"]))
45
+ when 'EndEvent', 'ErrorEvent', 'ContinueEvent' then %(#{id}(["#{label}"]))
46
+ when 'DecisionMergeActivity', 'ExclusiveMerge' then %(#{id}{"#{label}"})
47
+ else %(#{id}["#{label}"])
48
+ end
49
+ end
50
+
51
+ def plantuml_node(object)
52
+ id = node_id(object)
53
+ label = Support.plantuml_text(object_label(object))
54
+ shape = %w[DecisionMergeActivity ExclusiveMerge].include?(object_type(object)) ? 'choice' : 'state'
55
+ %(#{shape} "#{label}" as #{id})
56
+ end
57
+
58
+ def flow_line(flow, format)
59
+ origin = pointer(flow, 'OriginPointer', 'Origin')
60
+ destination = pointer(flow, 'DestinationPointer', 'Destination')
61
+ return unless @objects_by_id.key?(origin) && @objects_by_id.key?(destination)
62
+
63
+ label = flow_label(flow)
64
+ if format == :mermaid
65
+ middle = label.empty? ? '-->' : %(-->|"#{Support.mermaid_text(label)}"|)
66
+ "#{node_id(@objects_by_id.fetch(origin))} #{middle} #{node_id(@objects_by_id.fetch(destination))}"
67
+ else
68
+ suffix = label.empty? ? '' : " : #{Support.plantuml_text(label)}"
69
+ "#{node_id(@objects_by_id.fetch(origin))} --> #{node_id(@objects_by_id.fetch(destination))}#{suffix}"
70
+ end
71
+ end
72
+
73
+ def object_label(object)
74
+ type = object_type(object)
75
+ return 'Start' if type == 'StartEvent'
76
+ return Support.words(type) unless type == 'ActionActivity'
77
+
78
+ caption = object['Caption'] || object['caption']
79
+ action = object['Action'] || object['action'] || {}
80
+ generated = object['AutoGenerateCaption'] == true || caption.to_s == 'Activity'
81
+ return caption unless caption.to_s.empty? || generated
82
+
83
+ call = action['MicroflowCall'] || action['microflowCall'] || {}
84
+ target = call['Microflow'] || call['microflow']
85
+ return "Call #{target}" unless target.to_s.empty?
86
+
87
+ Support.words(action['$Type'] || action['Type'] || 'Action')
88
+ end
89
+
90
+ def flow_label(flow)
91
+ direct = flow['Caption'] || flow['Label'] || flow['Condition']
92
+ return direct.to_s unless direct.to_s.empty?
93
+
94
+ value = flow['CaseValue'] || flow['caseValue']
95
+ value = Mxrb::IO::BsonCodec.parse_array(flow['CaseValues'])[:items].first if value.nil? && flow['CaseValues']
96
+ return '' if value.nil?
97
+
98
+ if value.is_a?(Hash)
99
+ return '' if (value['$Type'] || value['Type']).to_s.end_with?('$NoCase')
100
+
101
+ (value['StringRepresentation'] || value['Value'] || Support.words(value['$Type'])).to_s
102
+ else
103
+ value.to_s
104
+ end
105
+ end
106
+
107
+ def pointer(flow, *keys)
108
+ value = keys.filter_map { flow[_1] }.first
109
+ Mxrb::IO::BsonCodec.extract_id(value).to_s
110
+ end
111
+
112
+ def raw_object_id(object) = Mxrb::IO::BsonCodec.extract_id(object['$ID'] || object['ID']).to_s
113
+ def object_type(object) = (object['$Type'] || object['Type']).to_s.delete_prefix('Microflows$')
114
+ def node_id(object) = Support.identifier(raw_object_id(object), prefix: 'activity')
115
+ end
116
+ # rubocop:enable Metrics
117
+ end
118
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support'
4
+
5
+ module Mxrb
6
+ module Uml
7
+ # Projects Mendix domain models as Mermaid and PlantUML class diagrams.
8
+ # rubocop:disable Metrics/AbcSize
9
+ class ClassDiagram
10
+ TYPES = {
11
+ string: 'String', integer: 'Integer', long: 'Long', float: 'Float',
12
+ decimal: 'Decimal', boolean: 'Boolean', datetime: 'DateTime',
13
+ autonumber: 'AutoNumber', hashstring: 'HashedString', binary: 'Binary',
14
+ enum: 'Enumeration'
15
+ }.freeze
16
+
17
+ def initialize(modules)
18
+ @modules = Array(modules)
19
+ @entities = @modules.flat_map(&:entities)
20
+ @module_by_entity = @modules.each_with_object({}) do |mod, result|
21
+ mod.entities.each { result[_1.id.to_s] = mod.name.to_s }
22
+ end
23
+ @entity_by_key = @entities.each_with_object({}) do |entity, result|
24
+ qualified = qualified_name(entity)
25
+ result[entity.id.to_s] = entity
26
+ result[qualified] = entity
27
+ end
28
+ end
29
+
30
+ def to_mermaid
31
+ lines = ['classDiagram']
32
+ @entities.each { append_mermaid_class(lines, _1) }
33
+ associations.each { lines << mermaid_association(_1) }
34
+ "#{lines.join("\n")}\n"
35
+ end
36
+
37
+ def to_plantuml
38
+ lines = ['@startuml', 'hide empty methods']
39
+ @entities.each { append_plantuml_class(lines, _1) }
40
+ associations.each { lines << plantuml_association(_1) }
41
+ lines << '@enduml'
42
+ "#{lines.join("\n")}\n"
43
+ end
44
+
45
+ private
46
+
47
+ def append_mermaid_class(lines, entity)
48
+ identifier = entity_identifier(entity)
49
+ lines << %(class #{identifier}["#{Support.mermaid_text(qualified_name(entity))}"])
50
+ lines << "class #{identifier} {"
51
+ entity.attributes.each do |attribute|
52
+ lines << " +#{attribute_type(attribute)} #{Support.mermaid_text(attribute.name)}"
53
+ end
54
+ lines << '}'
55
+ stereotype(entity).then { lines << "<<#{_1}>> #{identifier}" if _1 }
56
+ end
57
+
58
+ def append_plantuml_class(lines, entity)
59
+ identifier = entity_identifier(entity)
60
+ suffix = stereotype(entity).then { _1 ? " <<#{_1}>>" : '' }
61
+ lines << %(class "#{Support.plantuml_text(qualified_name(entity))}" as #{identifier}#{suffix} {)
62
+ entity.attributes.each do |attribute|
63
+ lines << " +#{Support.plantuml_text(attribute.name)} : #{attribute_type(attribute)}"
64
+ end
65
+ lines << '}'
66
+ end
67
+
68
+ def associations
69
+ @modules.flat_map(&:associations).filter_map do |association|
70
+ from = @entity_by_key[association.from_entity_id.to_s]
71
+ to = @entity_by_key[association.to_entity_id.to_s]
72
+ [association, from, to] if from && to
73
+ end
74
+ end
75
+
76
+ def mermaid_association(tuple)
77
+ association, from, to = tuple
78
+ left, right = multiplicities(association)
79
+ label = Support.mermaid_text(association.name)
80
+ %(#{entity_identifier(from)} "#{left}" --> "#{right}" #{entity_identifier(to)} : #{label})
81
+ end
82
+
83
+ def plantuml_association(tuple)
84
+ association, from, to = tuple
85
+ left, right = multiplicities(association)
86
+ label = Support.plantuml_text(association.name)
87
+ %(#{entity_identifier(from)} "#{left}" --> "#{right}" #{entity_identifier(to)} : #{label})
88
+ end
89
+
90
+ def multiplicities(association)
91
+ association.association_type.to_sym == :ReferenceSet ? ['*', '*'] : %w[1 N]
92
+ end
93
+
94
+ def attribute_type(attribute)
95
+ return Support.words(attribute.enumeration.to_s.split('.').last) unless attribute.enumeration.to_s.empty?
96
+
97
+ TYPES.fetch(attribute.type.to_sym, Support.words(attribute.type))
98
+ end
99
+
100
+ def stereotype(entity)
101
+ return 'OQL View' if entity.respond_to?(:oql_view?) && entity.oql_view?
102
+ return 'DTO' unless entity.persistable == true
103
+
104
+ nil
105
+ end
106
+
107
+ def qualified_name(entity)
108
+ value = entity.qualified_name.to_s
109
+ return value unless value.empty?
110
+
111
+ "#{@module_by_entity.fetch(entity.id.to_s)}.#{entity.name}"
112
+ end
113
+
114
+ def entity_identifier(entity) = Support.identifier(qualified_name(entity), prefix: 'class')
115
+ end
116
+ # rubocop:enable Metrics/AbcSize
117
+ end
118
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'support'
4
+
5
+ module Mxrb
6
+ module Uml
7
+ # Builds call-chain sequence diagrams from the existing semantic index.
8
+ # rubocop:disable Metrics
9
+ class SequenceDiagram
10
+ CALLABLE_KINDS = %i[microflow nanoflow].freeze
11
+ MAX_DEPTH = 100
12
+
13
+ def initialize(index, root: nil, module_name: nil, depth: 2)
14
+ raise ArgumentError, 'choose either root or module_name' if root && module_name
15
+ raise ArgumentError, 'root or module_name is required' unless root || module_name
16
+
17
+ @index = index
18
+ @root = root
19
+ @module_name = module_name
20
+ @depth = Integer(depth)
21
+ raise ArgumentError, 'depth must be zero or greater' if @depth.negative?
22
+ raise ArgumentError, "depth cannot exceed #{MAX_DEPTH}" if @depth > MAX_DEPTH
23
+
24
+ @edges, @selected = root ? root_graph : module_graph
25
+ @participants = participants
26
+ end
27
+
28
+ def to_mermaid
29
+ lines = ['sequenceDiagram']
30
+ @participants.each do |artifact|
31
+ lines << %( participant #{participant_id(artifact)} as #{Support.mermaid_text(artifact.qualified_name)})
32
+ end
33
+ @edges.each do |source, target|
34
+ lines << " #{participant_id(source)}->>#{participant_id(target)}: call"
35
+ end
36
+ append_mermaid_empty_note(lines) if @edges.empty?
37
+ "#{lines.join("\n")}\n"
38
+ end
39
+
40
+ def to_plantuml
41
+ lines = ['@startuml']
42
+ @participants.each do |artifact|
43
+ lines << %(participant "#{Support.plantuml_text(artifact.qualified_name)}" as #{participant_id(artifact)})
44
+ end
45
+ @edges.each do |source, target|
46
+ lines << "#{participant_id(source)} -> #{participant_id(target)} : call"
47
+ end
48
+ lines << 'note "No call relationships found" as MXRB' if @edges.empty?
49
+ lines << '@enduml'
50
+ "#{lines.join("\n")}\n"
51
+ end
52
+
53
+ private
54
+
55
+ def root_graph
56
+ root = @index.find(@root)
57
+ raise ArgumentError, "microflow not found: #{@root}" unless root && CALLABLE_KINDS.include?(root.kind)
58
+
59
+ edges = []
60
+ visited = {}
61
+ visit = lambda do |source, level|
62
+ return if level >= @depth || visited.key?(source.id)
63
+
64
+ visited[source.id] = true
65
+ callees(source).each do |target|
66
+ edges << [source, target]
67
+ visit.call(target, level + 1)
68
+ end
69
+ end
70
+ visit.call(root, 0)
71
+ [edges.uniq { [_1.id, _2.id] }, [root]]
72
+ end
73
+
74
+ def module_graph
75
+ sources = @index.artifacts.select do |artifact|
76
+ artifact.module_name == @module_name.to_s && CALLABLE_KINDS.include?(artifact.kind)
77
+ end
78
+ raise ArgumentError, "module not found or has no microflows: #{@module_name}" if sources.empty?
79
+
80
+ edges = sources.flat_map do |source|
81
+ callees(source).filter_map do |target|
82
+ [source, target] if target.module_name == @module_name.to_s
83
+ end
84
+ end
85
+ edges = edges.uniq { [_1.id, _2.id] }
86
+ [edges, sources]
87
+ end
88
+
89
+ def callees(source)
90
+ @index.references_from(source).filter_map do |reference|
91
+ reference.target if reference.relation == :calls && CALLABLE_KINDS.include?(reference.target.kind)
92
+ end.uniq(&:id)
93
+ end
94
+
95
+ def participants
96
+ (@selected + @edges.flatten).uniq(&:id).sort_by(&:qualified_name)
97
+ end
98
+
99
+ def append_mermaid_empty_note(lines)
100
+ participant = @participants.first
101
+ lines << if participant
102
+ " Note over #{participant_id(participant)}: No call relationships found"
103
+ else
104
+ ' Note over MXRB: No call relationships found'
105
+ end
106
+ end
107
+
108
+ def participant_id(artifact) = Support.identifier(artifact.qualified_name, prefix: 'participant')
109
+ end
110
+ # rubocop:enable Metrics
111
+ end
112
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ require_relative '../web_ui'
6
+ require_relative '../http/server'
7
+ require_relative '../modeler/catalog'
8
+
9
+ module Mxrb
10
+ module Uml
11
+ # Loopback-only unified viewer for class, activity, and sequence diagrams.
12
+ # rubocop:disable Metrics
13
+ class Server
14
+ LOOPBACK_HOSTS = %w[127.0.0.1 ::1 localhost].freeze
15
+
16
+ attr_reader :source, :host, :port
17
+
18
+ def initialize(source, host: '127.0.0.1', port: 4569)
19
+ @source = File.expand_path(source)
20
+ raise ArgumentError, "MPR not found: #{@source}" unless File.file?(@source)
21
+
22
+ @host = host.to_s
23
+ raise ArgumentError, 'UML server must bind to loopback' unless LOOPBACK_HOSTS.include?(@host)
24
+
25
+ @port = Integer(port)
26
+ end
27
+
28
+ def start
29
+ @server = Http::Server.new(host:, port:) { dispatch(_1, _2) }
30
+ @server.start { yield(_1) if block_given? }
31
+ end
32
+
33
+ def shutdown = @server&.shutdown
34
+
35
+ private
36
+
37
+ def dispatch(request, response)
38
+ return render(response, 200, WebUi.page('uml'), 'text/html; charset=utf-8') \
39
+ if request.request_method == 'GET' && request.path == '/'
40
+ return render(response, 200, WebUi.page('modeler'), 'text/html; charset=utf-8') \
41
+ if request.request_method == 'GET' && request.path == '/modeler'
42
+
43
+ if request.request_method == 'GET' && request.path.start_with?('/assets/')
44
+ asset = WebUi.asset(request.path)
45
+ return render(response, 200, *asset) if asset
46
+
47
+ return json(response, 404, ok: false, error: 'not_found')
48
+ end
49
+ return json(response, 405, ok: false, error: 'method_not_allowed') unless request.request_method == 'GET'
50
+
51
+ payload = case request.path
52
+ when '/api/class' then class_payload(request.query)
53
+ when '/api/activity' then activity_payload(request.query)
54
+ when '/api/sequence' then sequence_payload(request.query)
55
+ when '/api/catalog' then catalog_payload
56
+ when '/api/project' then Modeler::Catalog.new(source).to_h
57
+ end
58
+ return json(response, 404, ok: false, error: 'not_found') unless payload
59
+
60
+ json(response, 200, payload)
61
+ rescue ArgumentError, KeyError, Mxrb::Error => e
62
+ json(response, 422, ok: false, error: e.message)
63
+ end
64
+
65
+ def class_payload(query)
66
+ selected = query.fetch('modules', '').split(',').map(&:strip).reject(&:empty?)
67
+ with_project do |project|
68
+ modules = project.modules
69
+ unless selected.empty?
70
+ unknown = selected - modules.map(&:name)
71
+ raise ArgumentError, "unknown modules: #{unknown.join(', ')}" unless unknown.empty?
72
+
73
+ modules = modules.select { selected.include?(_1.name) }
74
+ end
75
+ formats(ClassDiagram.new(modules))
76
+ end
77
+ end
78
+
79
+ def activity_payload(query)
80
+ qualified = query.fetch('microflow') { raise ArgumentError, 'microflow is required' }
81
+ with_project do |project|
82
+ flow = find_microflow(project, qualified)
83
+ raise ArgumentError, "microflow not found: #{qualified}" unless flow
84
+
85
+ formats(ActivityDiagram.new(flow))
86
+ end
87
+ end
88
+
89
+ def sequence_payload(query)
90
+ root = query['root']
91
+ module_name = query['module']
92
+ raise ArgumentError, 'root or module is required' if root.to_s.empty? && module_name.to_s.empty?
93
+
94
+ with_project do |project|
95
+ diagram = SequenceDiagram.new(
96
+ project.semantic_index,
97
+ root: root.to_s.empty? ? nil : root,
98
+ module_name: module_name.to_s.empty? ? nil : module_name,
99
+ depth: query.fetch('depth', 2)
100
+ )
101
+ formats(diagram)
102
+ end
103
+ end
104
+
105
+ def catalog_payload
106
+ with_project do |project|
107
+ {
108
+ modules: project.modules.map(&:name).sort,
109
+ microflows: project.modules.flat_map do |mod|
110
+ mod.microflows.map { "#{mod.name}.#{_1.name}" }
111
+ end.sort
112
+ }
113
+ end
114
+ end
115
+
116
+ def find_microflow(project, qualified)
117
+ module_name, flow_name = qualified.to_s.split('.', 2)
118
+ return unless flow_name
119
+
120
+ project.modules.find { _1.name == module_name }&.microflows&.find { _1.name == flow_name }
121
+ end
122
+
123
+ def with_project
124
+ project = Model::Project.open(source)
125
+ yield project
126
+ ensure
127
+ project&.close
128
+ end
129
+
130
+ def formats(diagram) = { mermaid: diagram.to_mermaid, plantuml: diagram.to_plantuml }
131
+
132
+ def json(response, status, payload)
133
+ render(response, status, JSON.generate(payload), 'application/json; charset=utf-8')
134
+ end
135
+
136
+ def render(response, status, body, type)
137
+ response.status = status
138
+ response['Content-Type'] = type
139
+ response['Cache-Control'] = 'no-store'
140
+ response['X-Content-Type-Options'] = 'nosniff'
141
+ response.body = body
142
+ end
143
+ end
144
+ # rubocop:enable Metrics
145
+ end
146
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Mxrb
6
+ module Uml
7
+ # Shared, deterministic escaping and identifier helpers for text formats.
8
+ module Support
9
+ module_function
10
+
11
+ def identifier(value, prefix: 'n')
12
+ raw = value.to_s
13
+ body = raw.gsub(/[^A-Za-z0-9_]/, '_')
14
+ body = prefix if body.empty?
15
+ body = "#{prefix}_#{body}" unless body.match?(/\A[A-Za-z_]/)
16
+ body += "_#{Digest::SHA256.hexdigest(raw)[0, 8]}" unless body == raw
17
+ body
18
+ end
19
+
20
+ def mermaid_text(value)
21
+ value.to_s.gsub('&', '&amp;').gsub('<', '&lt;').gsub('>', '&gt;')
22
+ .gsub('"', '&quot;').gsub(/[\r\n]+/, ' ')
23
+ end
24
+
25
+ def plantuml_text(value)
26
+ value.to_s.gsub('\\') { '\\\\' }.gsub('"', '\\"').gsub(/[\r\n]+/, ' ')
27
+ end
28
+
29
+ def words(value)
30
+ value.to_s.delete_prefix('Microflows$').delete_suffix('Action')
31
+ .gsub(/([a-z0-9])([A-Z])/, '\\1 \\2').tr('_', ' ').strip
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/mxrb/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mxrb
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -0,0 +1 @@
1
+ import{n as e}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as t}from"./src-CvxfUak2.js";import"./chunk-I66GZJ75-CulcrLPh.js";import"./uml-C3KAFGTq.js";import{m as n,t as r}from"./mermaid-parser.core-B_UPzTxa.js";import{n as i,r as a,t as o}from"./chunk-6Q2QTUOP-wQkNmt_X.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{o.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
@@ -0,0 +1 @@
1
+ import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-D2qOEbeJ.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
@@ -0,0 +1 @@
1
+ import"./chunk-KEIR6QF5-BICK3FdT.js";import{M as e}from"./mermaid-parser.core-B_UPzTxa.js";export{e as createArchitectureServices};