mxrb 0.1.3 → 0.1.4

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 +126 -15
  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 +1749 -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 +1361 -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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a734dae7c32bec492facdf80e775897c3b31af47fc595f4d4096f2cd27f091ee
4
- data.tar.gz: db3a5207301da5c8f384515adf334c5739fba69379aec3c69175a653e68beff2
3
+ metadata.gz: 2d95f5a294643904d99fdac9afbf3d0e6f08e1aa209c8aff81b812daec751c9e
4
+ data.tar.gz: eba324a63596fc22694d5fb672a3a1bc072e937d1d4d92715c0f907c68f23931
5
5
  SHA512:
6
- metadata.gz: 4aedde89b67e5d52e438b38e2f2c9835a9ca2407950a6c7ee04f10aa74fb0d1e9fae0fc380b50e3c3155fc7509eb0d513e90461c8058949849f9c4146df162c7
7
- data.tar.gz: ed923449e412dc172462c951373408558244983db0d84b38d2a550791219190146064d900e2b6eb459fc8e6aa250def2c62614854e1e8b25d13192a6b437bd6e
6
+ metadata.gz: f3499ae32520b02952d2ded0622ec8be158a8d7e3ae36da5937b4cd5e187fb963100ad01925917f1d583db5b50dcf52b5a171abc09c128267dfcdb4d812ffe59
7
+ data.tar.gz: d8c1c51f90002b755ad9a3f03e4656c352cb09744ff8a893cae380cbb9d0b3b81074acbf758c788935b30ae6741efe8df7d5f87cfc06f3b2d25424211914c64c
data/bin/mxrb CHANGED
@@ -62,16 +62,103 @@ def render_index_advice(advice, json:) # rubocop:disable Metrics/AbcSize
62
62
  puts '[mxrb] No evidence-backed index candidates' if advice.candidates.empty?
63
63
  end
64
64
 
65
+ def extract_cli_value!(arguments, name) # rubocop:disable Metrics/AbcSize
66
+ attached = arguments.select { _1.start_with?("#{name}=") }
67
+ index = arguments.index(name)
68
+ raise ArgumentError, "use #{name} only once" if attached.size + (index ? 1 : 0) > 1
69
+
70
+ return arguments.delete(attached.first).split('=', 2).last if attached.one?
71
+ return unless index
72
+
73
+ raise ArgumentError, "#{name} requires a value" unless arguments[index + 1]
74
+
75
+ arguments.slice!(index, 2).last
76
+ end
77
+
78
+ def load_environment!(name, root:)
79
+ Mxrb::Environment.load(name, root:)
80
+ rescue Mxrb::Environment::Error, Mxrb::Environment::InvalidName => e
81
+ abort "[mxrb] #{e.message}"
82
+ end
83
+
65
84
  SCAFFOLD_COMMANDS = (Mxrb::Scaffold::Help::COMMANDS.keys - ["design"]).freeze
66
85
 
67
86
  progress_disabled = ARGV.delete("--no-progress")
68
87
  command = ARGV.shift
88
+ command = 'diagram-er' if command == 'domain-model'
69
89
  Mxrb::Progress.configure(enabled: false) if progress_disabled || ARGV.include?("--json")
70
90
 
91
+ command_flags = %w[--commands --comands]
92
+ if command.nil?
93
+ puts Mxrb::CLI::Help.welcome(release: Mxrb::CLI::Releases.new.status)
94
+ exit
95
+ elsif %w[--help -h help].include?(command)
96
+ path = command == 'help' ? ARGV.reject { _1.start_with?('-') } : []
97
+ puts(path.empty? ? Mxrb::CLI::Help.overview : Mxrb::CLI::Help.command(path))
98
+ exit
99
+ elsif command_flags.include?(command)
100
+ puts Mxrb::CLI::Help.commands
101
+ exit
102
+ elsif (commands_flag = (ARGV & command_flags).first)
103
+ ARGV.delete(commands_flag)
104
+ puts Mxrb::CLI::Help.commands(command)
105
+ exit
106
+ elsif (help_flag = (ARGV & %w[--help -h]).first)
107
+ help_index = ARGV.index(help_flag)
108
+ path = [command] + ARGV.take(help_index).reject { _1.start_with?('-') }.take(1)
109
+ puts Mxrb::CLI::Help.command(path)
110
+ exit
111
+ end
112
+
71
113
  case command
72
114
  when '--version', '-v'
73
115
  puts "mxrb #{Mxrb::VERSION}"
74
116
 
117
+ when 'changelog'
118
+ version = ARGV.shift
119
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
120
+ begin
121
+ release = Mxrb::CLI::Releases.new.changelog(version)
122
+ puts "#{release.fetch(:title)}#{release[:published_at] ? " (#{release[:published_at]})" : ''}"
123
+ puts
124
+ puts release.fetch(:body).empty? ? 'No release notes were published.' : release.fetch(:body)
125
+ puts
126
+ puts release.fetch(:url)
127
+ rescue Mxrb::CLI::ReleaseError => e
128
+ abort "[mxrb] #{e.message}"
129
+ end
130
+
131
+ when 'update'
132
+ check = !ARGV.delete('--check').nil?
133
+ show_changelog = !ARGV.delete('--changelog').nil?
134
+ abort 'Use only one of --check or --changelog' if check && show_changelog
135
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
136
+ begin
137
+ releases = Mxrb::CLI::Releases.new
138
+ if show_changelog
139
+ release = releases.changelog
140
+ puts "#{release.fetch(:title)}\n\n#{release.fetch(:body)}\n\n#{release.fetch(:url)}"
141
+ elsif check
142
+ status = releases.status(force: true)
143
+ abort '[mxrb] Could not check the latest published version' unless status.latest
144
+ if status.available?
145
+ puts "[mxrb] Update available: #{status.installed} -> #{status.latest}"
146
+ puts '[mxrb] Review it with `mxrb changelog`; install it with `mxrb update`.'
147
+ else
148
+ puts "[mxrb] #{status.installed} is the latest published version."
149
+ end
150
+ else
151
+ status = Mxrb::CLI::Updater.new(releases:).update!
152
+ if status.available?
153
+ puts "[mxrb] Updated MXRB from #{status.installed} to #{status.latest}."
154
+ else
155
+ puts "[mxrb] #{status.installed} is already the latest published version."
156
+ end
157
+ end
158
+ rescue Mxrb::CLI::ReleaseError => e
159
+ abort "[mxrb] #{e.message}"
160
+ end
161
+
75
162
  when 'doctor'
76
163
  json = ARGV.delete('--json')
77
164
  root = ARGV.shift || Dir.pwd
@@ -236,6 +323,15 @@ when "generate"
236
323
  ENV["MXRB_OUTPUT_PATH"] = File.expand_path(target)
237
324
  end
238
325
  load File.expand_path(source)
326
+ generated_path = target || Mxrb.last_defined_path
327
+ if generated_path && File.file?(generated_path)
328
+ result = Mxrb.validate(generated_path)
329
+ result.warnings.each { warn "[mxrb] warning: #{_1}" }
330
+ unless result.valid?
331
+ result.errors.each { warn "[mxrb] error: #{_1}" }
332
+ abort "[mxrb] Generated MPR failed validation"
333
+ end
334
+ end
239
335
  puts "[mxrb] Generated #{target || 'project from definition'}"
240
336
 
241
337
  when "widgets"
@@ -306,10 +402,260 @@ when "frontend"
306
402
  exit 1 unless plan.safe?
307
403
 
308
404
  when "export"
309
- source = ARGV.shift or abort "Usage: mxrb export <file.mpr> <ruby-project-dir>"
310
- output = ARGV.shift or abort "Usage: mxrb export <file.mpr> <ruby-project-dir>"
311
- destination = Mxrb::Exporter.new(source, output).export!
312
- puts "[mxrb] Exported Ruby project to #{destination}"
405
+ source = ARGV.shift or abort "Usage: mxrb export <file.mpr> <ruby-project-dir> [--mode mendix|ruby]"
406
+ output = ARGV.shift or abort "Usage: mxrb export <file.mpr> <ruby-project-dir> [--mode mendix|ruby]"
407
+ mode_equals = ARGV.find { _1.start_with?('--mode=') }
408
+ ARGV.delete(mode_equals) if mode_equals
409
+ mode_index = ARGV.index("--mode")
410
+ abort 'Use only one --mode option' if mode_equals && mode_index
411
+ mode = if mode_equals
412
+ mode_equals.split('=', 2).last
413
+ elsif mode_index
414
+ ARGV.slice!(mode_index, 2).last
415
+ else
416
+ 'mendix'
417
+ end
418
+ abort "--mode requires mendix or ruby" unless %w[mendix ruby].include?(mode)
419
+ stack_flags = %w[--flymetothemoon --onrails].select { ARGV.delete(_1) }
420
+ abort 'Use only one of --flymetothemoon or --onrails' if stack_flags.size > 1
421
+ abort 'Ruby stack presets require --mode ruby' if stack_flags.any? && mode != 'ruby'
422
+ stack = stack_flags.first&.delete_prefix('--')
423
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
424
+ destination = Mxrb::Exporter.new(source, output, mode:).export!
425
+ Mxrb::RubyApp::Preset.apply!(destination, stack) if stack
426
+ suffix = stack ? " with #{stack}" : ''
427
+ puts "[mxrb] Exported #{mode} project#{suffix} to #{destination}"
428
+
429
+ when 'diagram-er'
430
+ lifecycle_action = %w[up down status destroy __serve].include?(ARGV.first) ? ARGV.shift : nil
431
+ domain_usage = if lifecycle_action
432
+ "Usage: mxrb diagram-er #{lifecycle_action} FILE.mpr [options]"
433
+ else
434
+ 'Usage: mxrb diagram-er FILE.mpr [--module NAME] [--output FILE] [--port PORT] [--force]'
435
+ end
436
+ source = ARGV.shift or abort domain_usage
437
+ option = lambda do |name|
438
+ index = ARGV.index(name)
439
+ next unless index
440
+
441
+ abort "#{name} requires a value" unless ARGV[index + 1]
442
+ ARGV.slice!(index, 2).last
443
+ end
444
+ modules = []
445
+ modules_given = false
446
+ while (index = ARGV.index('--module'))
447
+ abort '--module requires a value' unless ARGV[index + 1]
448
+ modules_given = true
449
+ modules << ARGV.slice!(index, 2).last
450
+ end
451
+ output = option.call('--output')
452
+ port = option.call('--port')
453
+ token = option.call('--token') if lifecycle_action == '__serve'
454
+ state_root = option.call('--state-root') if lifecycle_action == '__serve'
455
+ force = !ARGV.delete('--force').nil?
456
+ yes = !ARGV.delete('--yes').nil?
457
+ json = !ARGV.delete('--json').nil?
458
+ begin
459
+ if lifecycle_action
460
+ lifecycle = Mxrb::DomainDiagram::Lifecycle.new(
461
+ source, state_root: state_root || Mxrb::DomainDiagram::Lifecycle.default_state_root,
462
+ executable: File.expand_path(__FILE__)
463
+ )
464
+ selected_modules = modules if modules_given
465
+ if lifecycle_action == '__serve' && !(output && port && token && state_root)
466
+ abort '__serve requires --output, --port, --token, and --state-root'
467
+ end
468
+ status = case lifecycle_action
469
+ when 'up'
470
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
471
+ lifecycle.up(output:, modules: selected_modules, port:, force:)
472
+ when 'down'
473
+ valid = ARGV.empty? && !output && !port && !force && !yes && !json
474
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless valid
475
+ lifecycle.down
476
+ when 'status'
477
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty? && !output && !port && !force && !yes
478
+ lifecycle.status
479
+ when 'destroy'
480
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty? && !output && !port && !force && !json
481
+ lifecycle.destroy(confirm: yes)
482
+ when '__serve'
483
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty? && !force && !yes && !json
484
+ lifecycle.run_worker(output:, modules:, port:, token:)
485
+ exit
486
+ end
487
+ if json
488
+ puts JSON.pretty_generate(status.to_h)
489
+ else
490
+ puts "[mxrb] ER diagram: #{status.state}"
491
+ puts "[mxrb] URL: #{status.url}" if status.url
492
+ puts "[mxrb] Layout MPR: #{status.output}" if status.output
493
+ puts "[mxrb] Log: #{status.log}" if status.log
494
+ end
495
+ exit
496
+ end
497
+
498
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty? && !yes && !json && !token && !state_root
499
+ server = Mxrb::DomainDiagram::Server.new(
500
+ source, output:, modules:, port: port || 4568, force:
501
+ )
502
+ puts "[mxrb] ER Diagram: http://127.0.0.1:#{port || 4568}"
503
+ puts "[mxrb] Layout MPR: #{server.output}"
504
+ puts '[mxrb] Use Exportar PNG in the browser to create the diagram image.'
505
+ trap('INT') { Thread.new { server.shutdown } }
506
+ server.start
507
+ rescue ArgumentError => e
508
+ abort "[mxrb] #{e.message}"
509
+ end
510
+
511
+ when 'uml'
512
+ uml_usage = 'Usage: mxrb uml FILE.mpr [--export class|activity|sequence] ' \
513
+ '[--format mermaid|plantuml] [options]'
514
+ source = ARGV.shift or abort uml_usage
515
+ pull = lambda do |name|
516
+ equals = ARGV.find { _1.start_with?("#{name}=") }
517
+ if equals
518
+ ARGV.delete(equals)
519
+ next equals.split('=', 2).last
520
+ end
521
+ index = ARGV.index(name)
522
+ next unless index
523
+
524
+ abort "#{name} requires a value" unless ARGV[index + 1]
525
+ ARGV.slice!(index, 2).last
526
+ end
527
+ modules = []
528
+ loop do
529
+ value = pull.call('--module')
530
+ break unless value
531
+
532
+ modules << value
533
+ end
534
+ export = pull.call('--export')
535
+ format = pull.call('--format') || 'mermaid'
536
+ microflow = pull.call('--microflow')
537
+ root = pull.call('--root')
538
+ depth = pull.call('--depth') || 2
539
+ port = pull.call('--port') || 4569
540
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
541
+ abort '--format requires mermaid or plantuml' unless %w[mermaid plantuml].include?(format)
542
+ abort '--export requires class, activity, or sequence' if export && !%w[class activity sequence].include?(export)
543
+ begin
544
+ unless export
545
+ viewer_options = modules.empty? && !microflow && !root && depth.to_s == '2' && format == 'mermaid'
546
+ abort 'Viewer mode only accepts --port' unless viewer_options
547
+
548
+ server = Mxrb::Uml::Server.new(source, port:)
549
+ puts "[mxrb] UML viewer: http://127.0.0.1:#{port}"
550
+ puts "[mxrb] Project modeler: http://127.0.0.1:#{port}/modeler"
551
+ puts '[mxrb] Use Ctrl+C to stop the server.'
552
+ trap('INT') { Thread.new { server.shutdown } }
553
+ server.start
554
+ exit
555
+ end
556
+
557
+ project = Mxrb::Model::Project.open(source)
558
+ diagram = case export
559
+ when 'class'
560
+ selected = project.modules
561
+ unless modules.empty?
562
+ unknown = modules - selected.map(&:name)
563
+ raise ArgumentError, "unknown modules: #{unknown.join(', ')}" unless unknown.empty?
564
+
565
+ selected = selected.select { modules.include?(_1.name) }
566
+ end
567
+ Mxrb::Uml::ClassDiagram.new(selected)
568
+ when 'activity'
569
+ raise ArgumentError, '--microflow is required for activity export' if microflow.to_s.empty?
570
+
571
+ module_name, flow_name = microflow.split('.', 2)
572
+ flow = project.modules.find { _1.name == module_name }&.microflows&.find { _1.name == flow_name }
573
+ raise ArgumentError, "microflow not found: #{microflow}" unless flow
574
+
575
+ Mxrb::Uml::ActivityDiagram.new(flow)
576
+ when 'sequence'
577
+ raise ArgumentError, 'use either --root or --module for sequence export' if root && modules.any?
578
+ raise ArgumentError, '--root or --module is required for sequence export' unless root || modules.one?
579
+
580
+ Mxrb::Uml::SequenceDiagram.new(
581
+ project.semantic_index, root:, module_name: modules.first, depth:
582
+ )
583
+ end
584
+ puts(format == 'plantuml' ? diagram.to_plantuml : diagram.to_mermaid)
585
+ rescue ArgumentError, Mxrb::Error => e
586
+ abort "[mxrb] #{e.message}"
587
+ ensure
588
+ project&.close
589
+ end
590
+
591
+ when "run"
592
+ run_usage = <<~USAGE
593
+ Usage: mxrb run [DIR] [options]
594
+
595
+ Start the Ruby backend and React + Vite frontend together.
596
+
597
+ Options:
598
+ --host HOST Bind host (default: 127.0.0.1)
599
+ --server-port PORT Ruby backend port (default: 9292)
600
+ --client-port PORT React + Vite port (default: 5173)
601
+ --environment NAME Load .env and config/environments/NAME.env
602
+ --no-frontend Start only the Ruby backend
603
+
604
+ Compatibility aliases: --api-port for --server-port, --port for --client-port.
605
+ The global --no-progress option is optional and only suppresses progress output.
606
+ USAGE
607
+ if %w[-h --help help].include?(ARGV.first)
608
+ puts run_usage
609
+ exit
610
+ end
611
+ root = ARGV.first&.start_with?("--") ? Dir.pwd : (ARGV.shift || Dir.pwd)
612
+ environment_name = extract_cli_value!(ARGV, '--environment')
613
+ option = lambda do |names, fallback|
614
+ matches = Array(names).filter_map do |name|
615
+ index = ARGV.index(name)
616
+ [name, index] if index
617
+ end
618
+ abort "Use only one of #{Array(names).join(' or ')}" if matches.size > 1
619
+ next fallback if matches.empty?
620
+
621
+ name, index = matches.first
622
+ abort "#{name} requires a value" unless ARGV[index + 1]
623
+ ARGV.slice!(index, 2).last
624
+ end
625
+ host = option.call("--host", "127.0.0.1")
626
+ server_port = option.call(%w[--server-port --api-port], 9292)
627
+ client_port = option.call(%w[--client-port --port], 5173)
628
+ frontend = !ARGV.delete("--no-frontend")
629
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
630
+ environment = load_environment!(environment_name, root:)
631
+ supervisor = Mxrb::RubyApp::Supervisor.new(
632
+ root, host:, api_port: server_port, frontend_port: client_port, frontend:, environment:
633
+ )
634
+ puts "[mxrb] Environment: #{environment.name}"
635
+ puts "[mxrb] Ruby server: http://#{host}:#{server_port}"
636
+ puts "[mxrb] React client: http://#{host}:#{client_port}" if frontend
637
+ trap("INT") { Thread.new { supervisor.shutdown } }
638
+ supervisor.start
639
+
640
+ when 'env'
641
+ root = ARGV.first&.start_with?('--') ? Dir.pwd : (ARGV.shift || Dir.pwd)
642
+ environment_name = extract_cli_value!(ARGV, '--environment')
643
+ json = ARGV.delete('--json')
644
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
645
+ environment = load_environment!(environment_name, root:)
646
+ payload = {
647
+ environment: environment.name, requested: environment.requested_name,
648
+ root: environment.root, sources: environment.sources,
649
+ keys: environment.to_h.keys.sort
650
+ }
651
+ if json
652
+ puts JSON.pretty_generate(payload)
653
+ else
654
+ puts "Environment : #{payload.fetch(:environment)}"
655
+ puts "Root : #{payload.fetch(:root)}"
656
+ payload.fetch(:sources).each { puts "Source : #{_1}" }
657
+ puts "Keys : #{payload.fetch(:keys).join(', ')}"
658
+ end
313
659
 
314
660
  when "validate"
315
661
  path = ARGV.shift or abort "Usage: mxrb validate <file.mpr>"
@@ -627,6 +973,71 @@ when "oql"
627
973
  end
628
974
  end
629
975
 
976
+ when "query"
977
+ usage = 'Usage: mxrb query "SELECT ..." --from sql|oql ' \
978
+ '[--to oql|sql] [--dialect ansi|postgresql|sql_server] ' \
979
+ '[--project FILE.mpr] [--input FILE] [--json]'
980
+ option = lambda do |name|
981
+ index = ARGV.index(name)
982
+ next unless index
983
+
984
+ abort "#{name} requires a value" unless ARGV[index + 1]
985
+ ARGV.slice!(index, 2).last
986
+ end
987
+ source_language = option.call('--from') || abort('--from is required: use sql or oql')
988
+ abort '--from must be sql or oql' unless %w[sql oql].include?(source_language)
989
+
990
+ target_language = option.call('--to') || { 'sql' => 'oql', 'oql' => 'sql' }.fetch(source_language)
991
+ unless [%w[sql oql], %w[oql sql]].include?([source_language, target_language])
992
+ abort 'Supported query conversions are sql to oql and oql to sql'
993
+ end
994
+ dialect = (option.call('--dialect') || 'postgresql').to_sym
995
+ project_path = option.call('--project')
996
+ abort '--project applies only when converting SQL to OQL' if project_path && source_language != 'sql'
997
+
998
+ input = option.call('--input')
999
+ json = !ARGV.delete('--json').nil?
1000
+ abort 'Query must be provided either as an argument or through --input' if input && !ARGV.empty?
1001
+ source = if input == '-'
1002
+ $stdin.read
1003
+ elsif input
1004
+ File.read(input)
1005
+ else
1006
+ ARGV.shift || abort(usage)
1007
+ end
1008
+ abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
1009
+ begin
1010
+ projection = if source_language == 'sql'
1011
+ if project_path
1012
+ Mxrb.open(project_path) { _1.sql_to_oql(source, dialect:) }
1013
+ else
1014
+ Mxrb::Oql::ReverseTranslator.new(dialect:).translate(source)
1015
+ end
1016
+ else
1017
+ Mxrb::Oql::Translator.new(dialect:).translate_source(source)
1018
+ end
1019
+ rescue ArgumentError, Mxrb::Error, Errno::ENOENT => e
1020
+ abort "[mxrb] error: #{e.message}"
1021
+ end
1022
+ sql = projection.sql
1023
+ oql = source_language == 'oql' ? projection.query.oql : projection.oql
1024
+ payload = {
1025
+ supported: projection.supported?, from: source_language, to: target_language,
1026
+ sql:, oql:,
1027
+ dialect: projection.dialect, confidence: projection.confidence,
1028
+ parameters: projection.parameters, warnings: projection.warnings
1029
+ }
1030
+ if json
1031
+ puts JSON.pretty_generate(payload)
1032
+ else
1033
+ puts "#{source_language.upcase} source:"
1034
+ puts source
1035
+ puts "#{target_language.upcase} (#{projection.dialect}, #{projection.confidence}):"
1036
+ puts((target_language == 'sql' ? sql : oql) || '(not generated)')
1037
+ projection.warnings.each { warn "[mxrb] warning: #{_1}" }
1038
+ end
1039
+ exit 1 unless projection.supported?
1040
+
630
1041
  when "analyze"
631
1042
  option = lambda do |name|
632
1043
  index = ARGV.index(name)
@@ -958,6 +1369,7 @@ when "test"
958
1369
  plan_only = ARGV.delete("--plan")
959
1370
  docker = ARGV.delete("--docker")
960
1371
  native = ARGV.delete("--native")
1372
+ environment_name = extract_cli_value!(ARGV, '--environment')
961
1373
  abort "--native and --docker are mutually exclusive" if native && docker
962
1374
  report_option = lambda do |name|
963
1375
  index = ARGV.index(name)
@@ -988,9 +1400,11 @@ when "test"
988
1400
  else
989
1401
  Mxrb::Runtime::Executor
990
1402
  end
991
- execution = executor.new(
992
- path, definition, plan: plan, output: $stdout
993
- ).run
1403
+ environment = load_environment!(environment_name, root: File.dirname(File.expand_path(path)))
1404
+ puts "[mxrb] Environment: #{environment.name}"
1405
+ execution = environment.with do
1406
+ executor.new(path, definition, plan: plan, output: $stdout).run
1407
+ end
994
1408
  execution.result.tests.each do |test|
995
1409
  puts "[#{test.passed? ? 'pass' : 'fail'}] #{test.name}: #{test.message}"
996
1410
  end
@@ -1487,13 +1901,29 @@ when "tree"
1487
1901
  end
1488
1902
 
1489
1903
  when "init"
1490
- name = ARGV.shift or abort "Usage: mxrb init <project_name> [--mxrb-path DIR]"
1904
+ name = ARGV.shift or abort "Usage: mxrb init <project_name> [--mode mendix|ruby] [--mxrb-path DIR]"
1905
+ mode_equals = ARGV.find { _1.start_with?('--mode=') }
1906
+ ARGV.delete(mode_equals) if mode_equals
1907
+ mode_index = ARGV.index('--mode')
1908
+ abort 'Use only one --mode option' if mode_equals && mode_index
1909
+ mode = if mode_equals
1910
+ mode_equals.split('=', 2).last
1911
+ elsif mode_index
1912
+ ARGV.slice!(mode_index, 2).last
1913
+ else
1914
+ 'mendix'
1915
+ end
1916
+ abort '--mode requires mendix or ruby' unless %w[mendix ruby].include?(mode)
1917
+ stack_flags = %w[--flymetothemoon --onrails].select { ARGV.delete(_1) }
1918
+ abort 'Use only one of --flymetothemoon or --onrails' if stack_flags.size > 1
1919
+ abort 'Ruby stack presets require --mode ruby' if stack_flags.any? && mode != 'ruby'
1920
+ stack = stack_flags.first&.delete_prefix('--')
1491
1921
  path_index = ARGV.index('--mxrb-path')
1492
1922
  mxrb_path = path_index ? ARGV.slice!(path_index, 2).last : nil
1493
1923
  abort '--mxrb-path requires a value' if path_index && mxrb_path.nil?
1494
1924
  abort "Unknown arguments: #{ARGV.join(' ')}" unless ARGV.empty?
1495
1925
  begin
1496
- result = Mxrb::Initializer.new(name, mxrb_path:).scaffold
1926
+ result = Mxrb::Initializer.new(name, mxrb_path:, mode:, stack:).scaffold
1497
1927
  rescue ArgumentError => e
1498
1928
  abort "[mxrb] error: #{e.message}"
1499
1929
  end
@@ -1501,9 +1931,19 @@ when "init"
1501
1931
  puts "\nDone. Next steps:"
1502
1932
  puts " cd #{result.root}"
1503
1933
  puts " bundle install"
1504
- puts " bundle exec mxrb generate project.rb"
1934
+ if mode == 'ruby'
1935
+ puts ' npm install --prefix frontend'
1936
+ puts ' bundle exec mxrb run .'
1937
+ puts ' bundle exec ruby project.rb # materialize build/*.mpr for Mendix'
1938
+ else
1939
+ puts ' bundle exec mxrb generate project.rb'
1940
+ end
1505
1941
 
1506
1942
  else
1943
+ unless ENV['MXRB_LEGACY_HELP'] == '1'
1944
+ abort "[mxrb] Unknown command #{command.inspect}. Run `mxrb --commands` to list commands " \
1945
+ 'or `mxrb --help` for guided examples.'
1946
+ end
1507
1947
  puts <<~USAGE
1508
1948
  mxrb — Pure-Ruby Mendix .mpr toolkit
1509
1949
 
@@ -1533,6 +1973,11 @@ else
1533
1973
  frontend migrate <file.mpr> Preview frontend schema/layout/theme migration
1534
1974
  [--apply] [--json] Apply only a safe plan or emit structured output
1535
1975
  export <file.mpr> <directory> Convert an MPR to editable Ruby
1976
+ --mode mendix|ruby Mendix DSL (default) or executable Ruby app
1977
+ run [directory] Start Ruby API and React + Vite together
1978
+ --host HOST --port PORT Vite host/port (default 127.0.0.1:5173)
1979
+ --api-port PORT Ruby API port (default 9292)
1980
+ --no-frontend Start only the Ruby API
1536
1981
  validate <file.mpr> Check MPR structure, hashes and v2 contents
1537
1982
  preflight <file.mpr> [--json] Audit native compiler/runtime compatibility
1538
1983
  compare <left.mpr> <right.mpr> Compare structural MPR snapshots
data/docs/de-DE/README.md CHANGED
@@ -13,6 +13,7 @@
13
13
  - [Ruby-First-Roadmap](ruby-first-roadmap.md)
14
14
  - [Validierungsmatrix](validation-matrix.md)
15
15
  - [Nativer Compiler und Runtime](compiler.md)
16
+ - [Ruby-Runtime ohne Java und Umgebungen](runtime-ruby.md)
16
17
  - [Qualitätsbericht für Build und Runtime](native-runtime-quality-report.md)
17
18
  - [Projekte erstellen und bearbeiten](writing.md)
18
19
  - [Projekt-Scaffolds](scaffolds.md)
@@ -44,6 +44,48 @@ exakten Mendix Runtime geprüft werden. Assoziationspfad-Joins gelten als nicht
44
44
  unterstützt, solange Runtime-Speichermetadaten den korrekten Join nicht
45
45
  beweisen können.
46
46
 
47
+ ## Sichere Konvertierung von SQL nach OQL
48
+
49
+ Der Rückweg akzeptiert eine schreibgeschützte `SELECT`-Teilmenge in PostgreSQL,
50
+ SQL Server oder ANSI und erzeugt logisches OQL. Mit dem Projekt werden die
51
+ exakte Groß-/Kleinschreibung von Modulen, Entitäten und Attributen
52
+ wiederhergestellt:
53
+
54
+ ```ruby
55
+ projection = Mxrb.open("Shop.mpr") do |project|
56
+ project.sql_to_oql(
57
+ 'SELECT p."name" FROM "shop$product" p WHERE p."name" = :Name',
58
+ dialect: :postgresql
59
+ )
60
+ end
61
+
62
+ puts projection.oql if projection.supported?
63
+ # SELECT p/Name FROM Shop.Product p WHERE p/Name = $Name
64
+ ```
65
+
66
+ Der einheitliche Befehl `query` konvertiert in beide Richtungen. `--input -`
67
+ liest stdin und `--json` liefert das typisierte Ergebnis:
68
+
69
+ ```sh
70
+ bundle exec mxrb query \
71
+ 'SELECT p."name" FROM "shop$product" p WHERE p."name" = :Name' \
72
+ --from sql --to oql --project Shop.mpr --dialect postgresql
73
+ bundle exec mxrb query 'SELECT p/Name FROM Shop.Product p' \
74
+ --from oql --to sql --dialect sql_server
75
+ bundle exec mxrb query --input query.sql --from sql --dialect sql_server --json
76
+ ```
77
+
78
+ Die Teilmenge umfasst Aliase, explizite Joins, kommaseparierte Quellen,
79
+ `WHERE`, `GROUP BY`, `HAVING`, `UNION`, `ORDER BY`, `LIMIT`/`OFFSET`, bekannte
80
+ OQL-Funktionen und benannte Parameter (`:Name`, `@Name` oder `$Name`). Physische
81
+ Tabellen `module$entity` werden zu `Module.Entity`; ohne `--project` wird die
82
+ Schreibweise abgeleitet und das Ergebnis erhält die Vertrauensstufe `inferred`.
83
+
84
+ Schreiboperationen, mehrere Anweisungen, CTEs, Tabellen-Unterabfragen in
85
+ `FROM`/`JOIN`, positionale Parameter, unbekannte Funktionen und Erweiterungen ohne sichere OQL-Entsprechung wie
86
+ `ILIKE`, `DISTINCT ON`, `TOP`, `::` und die Verkettung `||` liefern
87
+ `supported? == false` mit einer Erklärung in `warnings`.
88
+
47
89
  ## Dialektabhängige Analyse
48
90
 
49
91
  `Oql::Analyzer` erkennt Kosten- und Portabilitätsmuster im Originaltext,