mxrb 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.de-DE.md +108 -0
- data/README.md +172 -0
- data/README.pt-BR.md +109 -0
- data/bin/mxrb +1613 -0
- data/docs/README.md +10 -0
- data/docs/de-DE/README.md +24 -0
- data/docs/de-DE/architectural-patterns.md +26 -0
- data/docs/de-DE/architectural-standard.md +48 -0
- data/docs/de-DE/architecture.md +76 -0
- data/docs/de-DE/compiler.md +162 -0
- data/docs/de-DE/conventions.md +19 -0
- data/docs/de-DE/design-system.md +52 -0
- data/docs/de-DE/entity-dsl.md +53 -0
- data/docs/de-DE/native-runtime-quality-report.md +74 -0
- data/docs/de-DE/oql-sql.md +211 -0
- data/docs/de-DE/platform-api-integration.md +36 -0
- data/docs/de-DE/platform-operations.md +243 -0
- data/docs/de-DE/project-structure.md +32 -0
- data/docs/de-DE/ruby-first-roadmap.md +116 -0
- data/docs/de-DE/scaffolds.md +59 -0
- data/docs/de-DE/semantic-refactoring.md +56 -0
- data/docs/de-DE/team-server.md +33 -0
- data/docs/de-DE/validation-matrix.md +64 -0
- data/docs/de-DE/vetclinic-acceptance.md +24 -0
- data/docs/de-DE/writing.md +138 -0
- data/docs/en-US/README.md +24 -0
- data/docs/en-US/architectural-patterns.md +151 -0
- data/docs/en-US/architectural-standard.md +89 -0
- data/docs/en-US/architecture.md +170 -0
- data/docs/en-US/compiler.md +157 -0
- data/docs/en-US/conventions.md +82 -0
- data/docs/en-US/design-system.md +163 -0
- data/docs/en-US/entity-dsl.md +53 -0
- data/docs/en-US/native-runtime-quality-report.md +73 -0
- data/docs/en-US/oql-sql.md +201 -0
- data/docs/en-US/platform-api-integration.md +36 -0
- data/docs/en-US/platform-operations.md +244 -0
- data/docs/en-US/project-structure.md +49 -0
- data/docs/en-US/ruby-first-roadmap.md +186 -0
- data/docs/en-US/scaffolds.md +57 -0
- data/docs/en-US/semantic-refactoring.md +198 -0
- data/docs/en-US/team-server.md +32 -0
- data/docs/en-US/validation-matrix.md +257 -0
- data/docs/en-US/vetclinic-acceptance.md +23 -0
- data/docs/en-US/writing.md +655 -0
- data/docs/pt-BR/README.md +24 -0
- data/docs/pt-BR/architectural-patterns.md +25 -0
- data/docs/pt-BR/architectural-standard.md +533 -0
- data/docs/pt-BR/architecture.md +300 -0
- data/docs/pt-BR/compiler.md +162 -0
- data/docs/pt-BR/conventions.md +19 -0
- data/docs/pt-BR/design-system.md +65 -0
- data/docs/pt-BR/entity-dsl.md +116 -0
- data/docs/pt-BR/native-runtime-quality-report.md +73 -0
- data/docs/pt-BR/oql-sql.md +208 -0
- data/docs/pt-BR/platform-api-integration.md +36 -0
- data/docs/pt-BR/platform-operations.md +260 -0
- data/docs/pt-BR/project-structure.md +32 -0
- data/docs/pt-BR/ruby-first-roadmap.md +239 -0
- data/docs/pt-BR/scaffolds.md +104 -0
- data/docs/pt-BR/semantic-refactoring.md +55 -0
- data/docs/pt-BR/team-server.md +59 -0
- data/docs/pt-BR/validation-matrix.md +86 -0
- data/docs/pt-BR/vetclinic-acceptance.md +50 -0
- data/docs/pt-BR/writing.md +177 -0
- data/examples/sudoku_evaluation.rb +14 -0
- data/examples/sudoku_functional_test.rb +28 -0
- data/lib/mxrb/architecture/graph.rb +124 -0
- data/lib/mxrb/architecture/validator.rb +98 -0
- data/lib/mxrb/benchmark.rb +33 -0
- data/lib/mxrb/compare.rb +390 -0
- data/lib/mxrb/compiler/adapter.rb +96 -0
- data/lib/mxrb/compiler/artifact_document_compiler.rb +102 -0
- data/lib/mxrb/compiler/artifact_materializer.rb +53 -0
- data/lib/mxrb/compiler/client_model_materializer.rb +42 -0
- data/lib/mxrb/compiler/code_action_document_compiler.rb +64 -0
- data/lib/mxrb/compiler/code_action_materializer.rb +35 -0
- data/lib/mxrb/compiler/code_action_type_compiler.rb +32 -0
- data/lib/mxrb/compiler/combo_box_bundle_compiler.rb +314 -0
- data/lib/mxrb/compiler/compatibility_analyzer.rb +169 -0
- data/lib/mxrb/compiler/constants_materializer.rb +111 -0
- data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +290 -0
- data/lib/mxrb/compiler/database_connector_action_compiler.rb +280 -0
- data/lib/mxrb/compiler/deployment_asset_copier.rb +60 -0
- data/lib/mxrb/compiler/deployment_bootstrapper.rb +208 -0
- data/lib/mxrb/compiler/deployment_materializer.rb +43 -0
- data/lib/mxrb/compiler/deployment_metadata.rb +64 -0
- data/lib/mxrb/compiler/domain_document_compiler.rb +153 -0
- data/lib/mxrb/compiler/domain_model_materializer.rb +35 -0
- data/lib/mxrb/compiler/domain_security_compiler.rb +82 -0
- data/lib/mxrb/compiler/gallery_bundle_compiler.rb +190 -0
- data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +281 -0
- data/lib/mxrb/compiler/image_bundle_compiler.rb +216 -0
- data/lib/mxrb/compiler/java_proxy_generator.rb +509 -0
- data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +267 -0
- data/lib/mxrb/compiler/legacy_page_builder.rb +287 -0
- data/lib/mxrb/compiler/mda.rb +99 -0
- data/lib/mxrb/compiler/microflow_document_compiler.rb +85 -0
- data/lib/mxrb/compiler/microflow_materializer.rb +48 -0
- data/lib/mxrb/compiler/microflow_node_compiler.rb +250 -0
- data/lib/mxrb/compiler/model_package.rb +116 -0
- data/lib/mxrb/compiler/model_values.rb +67 -0
- data/lib/mxrb/compiler/nanoflow_program_compiler.rb +483 -0
- data/lib/mxrb/compiler/navigation_document_compiler.rb +93 -0
- data/lib/mxrb/compiler/packager.rb +100 -0
- data/lib/mxrb/compiler/page_bundle_builder.rb +56 -0
- data/lib/mxrb/compiler/page_bundle_compiler.rb +2004 -0
- data/lib/mxrb/compiler/page_document_compiler.rb +77 -0
- data/lib/mxrb/compiler/portable_packager.rb +356 -0
- data/lib/mxrb/compiler/project_jar_archive.rb +84 -0
- data/lib/mxrb/compiler/project_jar_builder.rb +121 -0
- data/lib/mxrb/compiler/project_materializer.rb +139 -0
- data/lib/mxrb/compiler/project_model_orderer.rb +52 -0
- data/lib/mxrb/compiler/runtime_data_types.rb +31 -0
- data/lib/mxrb/compiler/runtime_model_schema.rb +90 -0
- data/lib/mxrb/compiler/schemas/runtime-10.24.0.73019.json +1052 -0
- data/lib/mxrb/compiler/schemas/runtime-11.json +1215 -0
- data/lib/mxrb/compiler/schemas/runtime-6.10.8.json +1148 -0
- data/lib/mxrb/compiler/schemas/runtime-7.17.0.json +1144 -0
- data/lib/mxrb/compiler/schemas/runtime-7.5.0.json +1215 -0
- data/lib/mxrb/compiler/schemas/runtime-9.6.1.29396.json +868 -0
- data/lib/mxrb/compiler/schemas/system-model-10.24.0.73019.b64 +1135 -0
- data/lib/mxrb/compiler/schemas/system-model-11.12.1.b64 +815 -0
- data/lib/mxrb/compiler/schemas/system-model-6.10.8.b64 +497 -0
- data/lib/mxrb/compiler/schemas/system-model-7.17.0.b64 +519 -0
- data/lib/mxrb/compiler/schemas/system-model-7.5.0.b64 +504 -0
- data/lib/mxrb/compiler/schemas/system-model-9.6.1.29396.b64 +816 -0
- data/lib/mxrb/compiler/security_materializer.rb +104 -0
- data/lib/mxrb/compiler/settings_document_compiler.rb +45 -0
- data/lib/mxrb/compiler/settings_materializer.rb +25 -0
- data/lib/mxrb/compiler/source_model.rb +128 -0
- data/lib/mxrb/compiler/system_model_seed.rb +88 -0
- data/lib/mxrb/compiler/system_queue_materializer.rb +54 -0
- data/lib/mxrb/compiler/system_text_materializer.rb +49 -0
- data/lib/mxrb/compiler/translation_materializer.rb +95 -0
- data/lib/mxrb/compiler/web_bundle_builder.rb +155 -0
- data/lib/mxrb/compiler/web_list_data_source.rb +139 -0
- data/lib/mxrb/compiler/web_operation_compiler.rb +614 -0
- data/lib/mxrb/compiler/web_shell_materializer.rb +194 -0
- data/lib/mxrb/compiler/widget_package_extractor.rb +63 -0
- data/lib/mxrb/doctor.rb +116 -0
- data/lib/mxrb/dsl/builder.rb +1636 -0
- data/lib/mxrb/errors.rb +18 -0
- data/lib/mxrb/evaluation.rb +131 -0
- data/lib/mxrb/exporter.rb +1716 -0
- data/lib/mxrb/frontend/migrator.rb +775 -0
- data/lib/mxrb/functional.rb +307 -0
- data/lib/mxrb/github/annotator.rb +161 -0
- data/lib/mxrb/initializer.rb +257 -0
- data/lib/mxrb/integrity/validator.rb +136 -0
- data/lib/mxrb/io/bson_codec.rb +145 -0
- data/lib/mxrb/io/mpr_file.rb +558 -0
- data/lib/mxrb/io/mxunit_codec.rb +42 -0
- data/lib/mxrb/marketplace.rb +380 -0
- data/lib/mxrb/model/association.rb +98 -0
- data/lib/mxrb/model/attribute.rb +100 -0
- data/lib/mxrb/model/connector.rb +17 -0
- data/lib/mxrb/model/design_materializer.rb +133 -0
- data/lib/mxrb/model/design_migration.rb +126 -0
- data/lib/mxrb/model/design_system.rb +111 -0
- data/lib/mxrb/model/domain_model.rb +51 -0
- data/lib/mxrb/model/entity.rb +180 -0
- data/lib/mxrb/model/menu.rb +38 -0
- data/lib/mxrb/model/microflow.rb +100 -0
- data/lib/mxrb/model/module.rb +142 -0
- data/lib/mxrb/model/navigation.rb +127 -0
- data/lib/mxrb/model/page.rb +410 -0
- data/lib/mxrb/model/project.rb +187 -0
- data/lib/mxrb/model/unit.rb +48 -0
- data/lib/mxrb/module_initializer.rb +75 -0
- data/lib/mxrb/official_marketplace/content_api.rb +292 -0
- data/lib/mxrb/official_marketplace/dependency_resolver.rb +329 -0
- data/lib/mxrb/official_marketplace/lifecycle.rb +367 -0
- data/lib/mxrb/official_marketplace/module_package_importer.rb +318 -0
- data/lib/mxrb/official_marketplace/widget_package_installer.rb +414 -0
- data/lib/mxrb/official_marketplace.rb +720 -0
- data/lib/mxrb/oql/analyzer.rb +155 -0
- data/lib/mxrb/oql/index_advisor.rb +85 -0
- data/lib/mxrb/oql/plan_analyzer.rb +185 -0
- data/lib/mxrb/oql/server.rb +132 -0
- data/lib/mxrb/oql/sql_server_plan_analyzer.rb +190 -0
- data/lib/mxrb/oql/sql_server_workload_analyzer.rb +80 -0
- data/lib/mxrb/oql/workload_analyzer.rb +175 -0
- data/lib/mxrb/oql/workload_baseline.rb +60 -0
- data/lib/mxrb/oql.rb +424 -0
- data/lib/mxrb/project_lifecycle.rb +76 -0
- data/lib/mxrb/protocols/plan.rb +86 -0
- data/lib/mxrb/protocols.rb +123 -0
- data/lib/mxrb/runtime/database_workspace.rb +677 -0
- data/lib/mxrb/runtime/docker_executor.rb +41 -0
- data/lib/mxrb/runtime/docker_workspace.rb +29 -0
- data/lib/mxrb/runtime/executor.rb +169 -0
- data/lib/mxrb/runtime/java_locator.rb +26 -0
- data/lib/mxrb/runtime/native.rb +474 -0
- data/lib/mxrb/runtime/sql_server_database.rb +146 -0
- data/lib/mxrb/runtime/toolchain.rb +82 -0
- data/lib/mxrb/scaffold/cli.rb +113 -0
- data/lib/mxrb/scaffold/generator.rb +246 -0
- data/lib/mxrb/scaffold/help.rb +80 -0
- data/lib/mxrb/scaffold/page_templates.rb +54 -0
- data/lib/mxrb/scaffold/recipes.rb +198 -0
- data/lib/mxrb/scaffold/registry.rb +82 -0
- data/lib/mxrb/scaffold/templates.rb +723 -0
- data/lib/mxrb/scaffold/transaction.rb +84 -0
- data/lib/mxrb/schema/tables.rb +55 -0
- data/lib/mxrb/semantic/analyzer.rb +473 -0
- data/lib/mxrb/semantic/batch_plan.rb +68 -0
- data/lib/mxrb/semantic/domain_mutator.rb +419 -0
- data/lib/mxrb/semantic/embedder.rb +51 -0
- data/lib/mxrb/semantic/extractor.rb +290 -0
- data/lib/mxrb/semantic/index.rb +623 -0
- data/lib/mxrb/semantic/inliner.rb +216 -0
- data/lib/mxrb/semantic/mover.rb +153 -0
- data/lib/mxrb/semantic/onnx_embedder.rb +34 -0
- data/lib/mxrb/semantic/remover.rb +70 -0
- data/lib/mxrb/semantic/renamer.rb +211 -0
- data/lib/mxrb/semantic/tfidf_embedder.rb +46 -0
- data/lib/mxrb/semantic/vec_store.rb +75 -0
- data/lib/mxrb/team_server.rb +390 -0
- data/lib/mxrb/templates/project/10.24.0.73019.json +32 -0
- data/lib/mxrb/templates/project/11.12.1.json +32 -0
- data/lib/mxrb/templates/project/6.10.8.json +50 -0
- data/lib/mxrb/templates/project/7.17.0.json +50 -0
- data/lib/mxrb/templates/project/7.5.0.json +50 -0
- data/lib/mxrb/templates/project/9.6.1.29396.json +50 -0
- data/lib/mxrb/version.rb +5 -0
- data/lib/mxrb/widget_package.rb +338 -0
- data/lib/mxrb/widget_synchronizer.rb +31 -0
- data/lib/mxrb/writer.rb +3775 -0
- data/lib/mxrb.rb +188 -0
- data/marketplace/catalog.json +10 -0
- data/marketplace/modules/shared-kernel/domain/README.md +4 -0
- data/marketplace/modules/shared-kernel/module.rb +5 -0
- data/marketplace/modules/shared-kernel/mxrb-module.json +9 -0
- metadata +447 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'digest'
|
|
5
|
+
|
|
6
|
+
module Mxrb
|
|
7
|
+
module Compiler
|
|
8
|
+
# Completes web templates normally hydrated by Studio Pro's proprietary build stage.
|
|
9
|
+
class WebShellMaterializer # rubocop:disable Metrics/ClassLength
|
|
10
|
+
CACHE_REVISION = 6
|
|
11
|
+
PLACEHOLDER = /\{\{[a-z0-9_-]+\}\}/i
|
|
12
|
+
DYNAMIC_PAGE_CACHE = /
|
|
13
|
+
(?:\(0,[A-Za-z0-9$_.]+\)\(\))\.getConfig\("isDevModeEnabled"\)\?"":
|
|
14
|
+
`\?\$\{((?:\(0,[A-Za-z0-9$_.]+\)\(\))\.getConfig\("cachebust"\))\}`
|
|
15
|
+
/x
|
|
16
|
+
SELF_IMPORT = %r{import\*as\s+(?<binding>[A-Za-z_$][A-Za-z0-9_$]*)\s+from"\./(?<asset>[A-Za-z0-9_.-]+\.js)";
|
|
17
|
+
e\.C\(\k<binding>\),}x
|
|
18
|
+
|
|
19
|
+
def initialize(web, version:)
|
|
20
|
+
@web = File.expand_path(web)
|
|
21
|
+
@version = version.to_s
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def materialize
|
|
25
|
+
return 0 unless File.directory?(@web)
|
|
26
|
+
|
|
27
|
+
changed = html_files.count { render_html(_1) }
|
|
28
|
+
changed += materialize_dynamic_imports
|
|
29
|
+
write_missing(File.join(@web, 'js', 'login_i18n.js'), login_i18n)
|
|
30
|
+
write_missing(File.join(@web, 'lib', 'bootstrap', 'css', 'bootstrap.min.css'), login_styles)
|
|
31
|
+
# React Client requests this conventional aggregate even when every widget
|
|
32
|
+
# ships its styles through JavaScript or the compiled theme.
|
|
33
|
+
write_missing(File.join(@web, 'dist', 'widgets.css'), '')
|
|
34
|
+
changed
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# React Client deliberately omits page cache tokens in developer mode. Native mxrb
|
|
38
|
+
# rebuilds can then leave an already-open browser on an obsolete generated page.
|
|
39
|
+
def materialize_dynamic_imports
|
|
40
|
+
Dir.glob(File.join(@web, 'dist', '**', '*.js')).sort.count do |path|
|
|
41
|
+
source = File.binread(path)
|
|
42
|
+
rendered = render_javascript(source)
|
|
43
|
+
next false if source == rendered
|
|
44
|
+
|
|
45
|
+
write_versioned_chunk(path, rendered)
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def html_files = Dir.glob(File.join(@web, '*.html')).sort
|
|
53
|
+
|
|
54
|
+
def render_javascript(source)
|
|
55
|
+
rendered = source.gsub(DYNAMIC_PAGE_CACHE) do
|
|
56
|
+
%(`?#{cache_token}\${#{Regexp.last_match(1)}}`)
|
|
57
|
+
end
|
|
58
|
+
rendered.gsub(SELF_IMPORT) do |match|
|
|
59
|
+
asset = Regexp.last_match(:asset)
|
|
60
|
+
match.sub(%("./#{asset}"), %("./#{asset}?#{cache_token}"))
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def render_html(path)
|
|
65
|
+
source = File.read(path)
|
|
66
|
+
rendered = source.gsub('{{cachebust}}', cache_token)
|
|
67
|
+
rendered = rendered.gsub('{{unsupportedbrowser}}', unsupported_browser)
|
|
68
|
+
rendered = rendered.gsub('{{themecss}}', theme_css)
|
|
69
|
+
rendered = rendered.gsub('{{manifest}}', manifest)
|
|
70
|
+
rendered = rendered.gsub(PLACEHOLDER, '')
|
|
71
|
+
rendered = inject_navigation_compatibility(rendered) if File.basename(path) == 'index.html'
|
|
72
|
+
return false if source == rendered
|
|
73
|
+
|
|
74
|
+
File.write(path, rendered)
|
|
75
|
+
true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Runtime's manifest handler accepts an opaque decimal cache token only.
|
|
79
|
+
def cache_token
|
|
80
|
+
Digest::SHA256.hexdigest("mxrb:web-shell:#{CACHE_REVISION}:#{@version}")[0, 15].to_i(16).to_s
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def write_versioned_chunk(path, rendered)
|
|
84
|
+
old_stem = File.basename(path, '.js')
|
|
85
|
+
return File.binwrite(path, rendered) unless old_stem.match?(/\A[0-9a-f]{16}\z/)
|
|
86
|
+
|
|
87
|
+
new_stem = Digest::SHA256.hexdigest(rendered)[0, 16]
|
|
88
|
+
rewrite_chunk_references(old_stem, new_stem)
|
|
89
|
+
destination = File.join(File.dirname(path), "#{new_stem}.js")
|
|
90
|
+
File.binwrite(destination, rendered)
|
|
91
|
+
FileUtils.rm_f(path) unless destination == path
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def rewrite_chunk_references(old_stem, new_stem)
|
|
95
|
+
Dir.glob(File.join(@web, 'dist', '**', '*.js')).sort.each do |path|
|
|
96
|
+
source = File.binread(path)
|
|
97
|
+
rendered = source.gsub(old_stem, new_stem)
|
|
98
|
+
File.binwrite(path, rendered) unless source == rendered
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def theme_css
|
|
103
|
+
return '' unless File.file?(File.join(@web, 'theme.compiled.css'))
|
|
104
|
+
|
|
105
|
+
%(<link rel="stylesheet" href="theme.compiled.css?#{cache_token}">)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def manifest
|
|
109
|
+
%(<link rel="manifest" href="manifest.webmanifest?#{cache_token}" crossorigin="use-credentials">)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def inject_navigation_compatibility(html)
|
|
113
|
+
return html if html.include?('data-mxrb-navigation-compat') || !html.include?('</head>')
|
|
114
|
+
|
|
115
|
+
html.sub('</head>', "#{navigation_compatibility}\n</head>")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def navigation_compatibility # rubocop:disable Metrics/MethodLength
|
|
119
|
+
<<~HTML.chomp
|
|
120
|
+
<script data-mxrb-navigation-compat="#{CACHE_REVISION}">
|
|
121
|
+
(() => {
|
|
122
|
+
const install = () => {
|
|
123
|
+
const ui = window.mx && window.mx.ui;
|
|
124
|
+
if (!ui || typeof ui.openForm2 !== "function") return false;
|
|
125
|
+
if (typeof ui.openForm !== "function") {
|
|
126
|
+
ui.openForm = (page, options, onLoad, onError) =>
|
|
127
|
+
Promise.resolve(ui.openForm2(
|
|
128
|
+
page, {}, undefined, undefined, options || { location: "content" }
|
|
129
|
+
)).then(onLoad, onError);
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
};
|
|
133
|
+
if (!install()) {
|
|
134
|
+
const timer = window.setInterval(() => install() && window.clearInterval(timer), 50);
|
|
135
|
+
window.setTimeout(() => window.clearInterval(timer), 60000);
|
|
136
|
+
}
|
|
137
|
+
})();
|
|
138
|
+
</script>
|
|
139
|
+
HTML
|
|
140
|
+
end # rubocop:enable Metrics/MethodLength
|
|
141
|
+
|
|
142
|
+
def unsupported_browser
|
|
143
|
+
<<~HTML.chomp
|
|
144
|
+
<script type="text/javascript">
|
|
145
|
+
try { eval("async () => {}"); }
|
|
146
|
+
catch (error) {
|
|
147
|
+
var homeUrl = window.location.origin + window.location.pathname;
|
|
148
|
+
var appUrl = homeUrl.slice(0, homeUrl.lastIndexOf("/") + 1);
|
|
149
|
+
window.location.replace(appUrl + "unsupported-browser.html");
|
|
150
|
+
}
|
|
151
|
+
</script>
|
|
152
|
+
HTML
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def write_missing(path, contents)
|
|
156
|
+
return if File.exist?(path)
|
|
157
|
+
|
|
158
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
159
|
+
File.write(path, contents)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def login_i18n
|
|
163
|
+
<<~JS
|
|
164
|
+
window.i18nMap = {
|
|
165
|
+
loginHeader: "Sign in", username: "User name", password: "Password",
|
|
166
|
+
loginButton: "Sign in", goHomeButton: "Go home",
|
|
167
|
+
http401: "The user name or password is incorrect.",
|
|
168
|
+
http402: "The application license does not permit this sign-in.",
|
|
169
|
+
http403: "Access is denied.", http404: "The application is unavailable.",
|
|
170
|
+
http500: "The application encountered an error.",
|
|
171
|
+
http503: "The application is starting. Try again shortly.",
|
|
172
|
+
httpdefault: "Sign-in failed. Try again."
|
|
173
|
+
};
|
|
174
|
+
JS
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def login_styles
|
|
178
|
+
<<~CSS
|
|
179
|
+
*{box-sizing:border-box}html,body{margin:0;font-family:Inter,Arial,sans-serif;color:#17212b}
|
|
180
|
+
body{background:linear-gradient(135deg,#f4f8fb,#e6f1f6)}
|
|
181
|
+
.form-group{margin-bottom:16px}.form-control{display:block;width:100%;height:42px;padding:10px 12px;
|
|
182
|
+
border:1px solid #b8c5cf;border-radius:6px;background:#fff;font-size:14px;box-shadow:inset 0 1px 2px #0000000d}
|
|
183
|
+
.form-control:focus{border-color:#0595db;outline:0;box-shadow:0 0 0 3px #0595db26}
|
|
184
|
+
.btn{display:inline-block;padding:10px 18px;border:0;border-radius:6px;font-weight:600;cursor:pointer}
|
|
185
|
+
.btn-primary{width:100%;color:#fff;background:#0595db}.btn-primary:hover{background:#087fb8}
|
|
186
|
+
.btn[disabled]{cursor:not-allowed;opacity:.6}.alert{padding:10px 12px;margin-bottom:16px;border-radius:6px}
|
|
187
|
+
.alert-danger{color:#842029;background:#f8d7da;border:1px solid #f5c2c7}
|
|
188
|
+
.login-container{filter:drop-shadow(0 18px 35px #1c526626)}
|
|
189
|
+
.login-form{background:#fff;border-radius:12px}.login-form label{display:block;margin-bottom:6px}
|
|
190
|
+
CSS
|
|
191
|
+
end
|
|
192
|
+
end # rubocop:enable Metrics/ClassLength
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'zip'
|
|
5
|
+
|
|
6
|
+
module Mxrb
|
|
7
|
+
module Compiler
|
|
8
|
+
# Expands project-owned pluggable widget packages into the web build tree.
|
|
9
|
+
# rubocop:disable Metrics
|
|
10
|
+
class WidgetPackageExtractor
|
|
11
|
+
def initialize(project_root, web_root)
|
|
12
|
+
@source = File.join(project_root, 'widgets')
|
|
13
|
+
@destination = File.join(web_root, 'widgets')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def extract
|
|
17
|
+
return 0 unless File.directory?(@source)
|
|
18
|
+
|
|
19
|
+
FileUtils.mkdir_p(@destination)
|
|
20
|
+
Dir.glob(File.join(@source, '*.mpk')).sort.sum { extract_package(_1) }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def extract_package(path)
|
|
26
|
+
Zip::File.open(path) do |archive|
|
|
27
|
+
entries = archive.reject(&:directory?)
|
|
28
|
+
entries.each { safe_destination(_1.name) }
|
|
29
|
+
runtime_roots = entries.select { _1.name.end_with?('.mjs') }
|
|
30
|
+
.map { File.dirname(_1.name) }.uniq
|
|
31
|
+
runtime_entries = entries.select do |entry|
|
|
32
|
+
runtime_roots.any? do |root|
|
|
33
|
+
if root == '.'
|
|
34
|
+
entry.name.match?(/\.(?:m?js|css)\z/) || entry.name.start_with?('assets/')
|
|
35
|
+
else
|
|
36
|
+
entry.name.start_with?("#{root}/")
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
runtime_entries.each { write_entry(_1) }
|
|
41
|
+
return runtime_entries.length
|
|
42
|
+
end
|
|
43
|
+
rescue Zip::Error => e
|
|
44
|
+
raise CompilationError, "invalid widget package #{path}: #{e.message}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def write_entry(entry)
|
|
48
|
+
destination = safe_destination(entry.name)
|
|
49
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
50
|
+
File.binwrite(destination, entry.get_input_stream.read)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def safe_destination(name)
|
|
54
|
+
destination = File.expand_path(name, @destination)
|
|
55
|
+
prefix = "#{File.expand_path(@destination)}#{File::SEPARATOR}"
|
|
56
|
+
return destination if destination.start_with?(prefix)
|
|
57
|
+
|
|
58
|
+
raise CompilationError, "unsafe widget package entry #{name}"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
# rubocop:enable Metrics
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/mxrb/doctor.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
|
|
5
|
+
module Mxrb
|
|
6
|
+
DoctorCheck = Data.define(:name, :status, :message) do
|
|
7
|
+
def ok? = status == :ok
|
|
8
|
+
end
|
|
9
|
+
DoctorReport = Data.define(:root, :checks) do
|
|
10
|
+
def valid? = checks.all? { _1.status != :error }
|
|
11
|
+
def errors = checks.select { _1.status == :error }
|
|
12
|
+
def warnings = checks.select { _1.status == :warning }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Diagnoses project structure and optional external toolchain availability.
|
|
16
|
+
class Doctor
|
|
17
|
+
def initialize(root = Dir.pwd, runner: Open3.method(:capture3))
|
|
18
|
+
expanded = File.expand_path(root)
|
|
19
|
+
@root = File.file?(expanded) ? File.dirname(expanded) : expanded
|
|
20
|
+
@runner = runner
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
DoctorReport.new(@root, [
|
|
25
|
+
ruby_check, project_check, modules_check, aggregators_check,
|
|
26
|
+
mpr_check, executable_check('bundle'), executable_check('java'),
|
|
27
|
+
executable_check('docker', warning: true), runtime_check
|
|
28
|
+
].freeze)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def ruby_check
|
|
34
|
+
status = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('4.0') ? :ok : :error
|
|
35
|
+
DoctorCheck.new(:ruby, status, "Ruby #{RUBY_VERSION}")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def project_check = file_check(:project, File.join(@root, 'project.rb'), 'project.rb')
|
|
39
|
+
|
|
40
|
+
def modules_check
|
|
41
|
+
modules = Dir[File.join(@root, 'modules', '*', 'module.rb')]
|
|
42
|
+
status = modules.empty? ? :error : :ok
|
|
43
|
+
DoctorCheck.new(:modules, status, "#{modules.size} module definition(s)")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def aggregators_check
|
|
47
|
+
files = Dir[File.join(@root, 'modules', '**', '*.rb')]
|
|
48
|
+
missing = files.select { File.read(_1).include?('evaluate_dir') }
|
|
49
|
+
.flat_map { missing_evaluate_dirs(_1) }
|
|
50
|
+
status = missing.empty? ? :ok : :warning
|
|
51
|
+
message = missing.empty? ? 'all referenced directories exist' : missing.join(', ')
|
|
52
|
+
DoctorCheck.new(:aggregators, status, message)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def missing_evaluate_dirs(path)
|
|
56
|
+
File.read(path).scan(/evaluate_dir File\.join\(__dir__,\s*"([^"]+)"\)/).filter_map do |match|
|
|
57
|
+
directory = File.join(File.dirname(path), match.first)
|
|
58
|
+
directory unless File.directory?(directory)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def mpr_check
|
|
63
|
+
path = Dir[File.join(@root, '*.mpr')].first
|
|
64
|
+
return DoctorCheck.new(:mpr, :warning, 'no generated MPR found') unless path
|
|
65
|
+
|
|
66
|
+
result = Mxrb.validate(path)
|
|
67
|
+
DoctorCheck.new(:mpr, result.valid? ? :ok : :error, "#{result.errors.size} validation error(s)")
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
DoctorCheck.new(:mpr, :error, e.message)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def executable_check(name, warning: false)
|
|
73
|
+
available = executable_candidates(name).any? { command_available?(_1) }
|
|
74
|
+
result = available ? :ok : unavailable_status(warning)
|
|
75
|
+
DoctorCheck.new(name.to_sym, result, available ? 'available' : 'not available')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def command_available?(command)
|
|
79
|
+
_output, _error, status = @runner.call(command, '--version')
|
|
80
|
+
status.success?
|
|
81
|
+
rescue Errno::ENOENT
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def unavailable_status(warning) = warning ? :warning : :error
|
|
86
|
+
|
|
87
|
+
def executable_candidates(name)
|
|
88
|
+
return [name] unless name == 'java'
|
|
89
|
+
|
|
90
|
+
java_home = ENV['JAVA_HOME'].to_s
|
|
91
|
+
candidates = [java_home.empty? ? nil : File.join(java_home, 'bin', 'java'), name]
|
|
92
|
+
candidates.concat(Dir.glob(File.join(Dir.home, '.asdf', 'installs', 'java', '*', 'bin', 'java')).sort.reverse)
|
|
93
|
+
candidates.compact.uniq
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def runtime_check
|
|
97
|
+
configured = ENV['MXRB_MENDIX_HOME'].to_s
|
|
98
|
+
roots = [configured, File.join(Dir.home, '.local', 'share', 'mendix')].reject(&:empty?)
|
|
99
|
+
candidates = roots.flat_map { runtime_candidates(_1) }.uniq
|
|
100
|
+
found = candidates.find do |root|
|
|
101
|
+
Runtime::RUNTIME_REQUIRED_FILES.all? { File.file?(File.join(root, _1)) }
|
|
102
|
+
end
|
|
103
|
+
DoctorCheck.new(:runtime, found ? :ok : :warning, found || 'not found')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def runtime_candidates(root)
|
|
107
|
+
direct = File.basename(root) == 'runtime' ? root : File.join(root, 'runtime')
|
|
108
|
+
[direct] + Dir.glob(File.join(root, '*', 'runtime'))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def file_check(name, path, label)
|
|
112
|
+
DoctorCheck.new(name, File.file?(path) ? :ok : :error,
|
|
113
|
+
File.file?(path) ? "#{label} found" : "#{label} missing")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|