hecks 2.9.0 → 3.0.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 +4 -4
- data/exe/hecks +51 -6
- data/lib/hecks/adapters/driven/heki/journal.rb +1 -1
- data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +1 -1
- data/lib/hecks/adapters/driven/memory.rb +16 -3
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +6 -2
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +4 -2
- data/lib/hecks/adapters/driven/prism.rb +41 -8
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +73 -16
- data/lib/hecks/behaviors/expectations.rb +1 -1
- data/lib/hecks/behaviors.rb +1 -1
- data/lib/hecks/bench/cli.rb +62 -0
- data/lib/hecks/bench/environment.rb +71 -0
- data/lib/hecks/bench/postgres_probe.rb +30 -0
- data/lib/hecks/bench/report.rb +76 -0
- data/lib/hecks/bench/ruby_runner.rb +93 -0
- data/lib/hecks/bench/run.rb +55 -0
- data/lib/hecks/bench/rust_runner.rb +101 -0
- data/lib/hecks/bench/stats.rb +46 -0
- data/lib/hecks/bench/suite.rb +98 -0
- data/lib/hecks/bench/workload.rb +89 -0
- data/lib/hecks/bench.rb +17 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +2 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/aggregate.rb +30 -1
- data/lib/hecks/bluebook/behaviour/domain_port.rb +7 -0
- data/lib/hecks/bluebook/chapter.rb +6 -2
- data/lib/hecks/bluebook/domain_port.rb +36 -5
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +10 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +8 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -1
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +31 -3
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +122 -3
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +1 -1
- data/lib/hecks/bluebook/dsl/query_builder.rb +27 -1
- data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -1
- data/lib/hecks/bluebook/dsl/world_builder.rb +29 -2
- data/lib/hecks/bluebook/expression/canonical_form.rb +1 -1
- data/lib/hecks/bluebook/expression/resolver.rb +3 -2
- data/lib/hecks/bluebook/hexagon.rb +13 -5
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +1 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator.rb +9 -4
- data/lib/hecks/bluebook/model_check.rb +23 -0
- data/lib/hecks/bluebook/pattern_subset.rb +38 -0
- data/lib/hecks/bluebook/query.rb +20 -5
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/canonical_json.rb +30 -0
- data/lib/hecks/chapters.rb +102 -0
- data/lib/hecks/cli/behaviors.rb +61 -0
- data/lib/hecks/cli/canonicalise.rb +25 -0
- data/lib/hecks/cli/check_engine_agreement.rb +32 -0
- data/lib/hecks/cli/check_era.rb +88 -0
- data/lib/hecks/cli/console.rb +66 -0
- data/lib/hecks/cli/deploy_template_diff.rb +40 -0
- data/lib/hecks/cli/doc_coverage.rb +32 -0
- data/lib/hecks/cli/document.rb +2 -2
- data/lib/hecks/cli/expression_projection.rb +41 -0
- data/lib/hecks/cli/follow.rb +88 -0
- data/lib/hecks/cli/history.rb +44 -0
- data/lib/hecks/cli/ir.rb +1 -1
- data/lib/hecks/cli/mcp.rb +3 -3
- data/lib/hecks/cli/model_check.rb +7 -4
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +224 -32
- data/lib/hecks/cli/project_diagrams.rb +4 -3
- data/lib/hecks/cli/project_glossary.rb +44 -0
- data/lib/hecks/cli/reference.rb +46 -0
- data/lib/hecks/cli/refresh_projections.rb +44 -0
- data/lib/hecks/cli/refresh_rspec_runtime_baseline.rb +150 -0
- data/lib/hecks/cli/release.rb +70 -0
- data/lib/hecks/cli/release_gem.rb +59 -0
- data/lib/hecks/cli/rspec_io_parallel_files.rb +137 -0
- data/lib/hecks/cli/rspec_shard_files.rb +66 -0
- data/lib/hecks/cli/run.rb +3 -3
- data/lib/hecks/cli/seed_semantics_corpus.rb +71 -0
- data/lib/hecks/cli/serve_query_ir_mcp.rb +30 -0
- data/lib/hecks/cli/shape.rb +70 -0
- data/lib/hecks/cli/smoke_http.rb +230 -0
- data/lib/hecks/cli/smoke_test.rb +5 -3
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -5
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +201 -52
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +591 -23
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -4
- data/lib/hecks/deploy/oidc.json +82 -2
- data/lib/hecks/doc/reference.rb +351 -0
- data/lib/hecks/doors/cli_door.rb +196 -0
- data/lib/hecks/doors/cli_runner.rb +297 -0
- data/lib/hecks/{facade → doors}/command_request.rb +1 -1
- data/lib/hecks/{facade → doors}/handle.rb +2 -2
- data/lib/hecks/{facade → doors}/json_door.rb +4 -4
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +7 -7
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/{facade/surface → doors/ruby_door}/aggregate_door.rb +8 -3
- data/lib/hecks/{facade/surface → doors/ruby_door}/chapter.rb +3 -3
- data/lib/hecks/{facade/surface.rb → doors/ruby_door.rb} +36 -5
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +1 -1
- data/lib/hecks/embryonaut_bluebook.rb +1 -1
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +24 -37
- data/lib/hecks/forms/app.rb +6 -6
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/field_shape.rb +2 -1
- data/lib/hecks/forms/port_argument.rb +1 -1
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +393 -0
- data/lib/hecks/fuzzing/combination_miner.rb +86 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +214 -0
- data/lib/hecks/fuzzing/coverage_campaign.rb +143 -0
- data/lib/hecks/fuzzing/differential.rb +156 -0
- data/lib/hecks/fuzzing/domain_generator.rb +615 -0
- data/lib/hecks/fuzzing/era_boundary.rb +71 -0
- data/lib/hecks/fuzzing/form_census.rb +136 -0
- data/lib/hecks/fuzzing/generated_domain_check.rb +93 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +64 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +301 -0
- data/lib/hecks/fuzzing/nondeterministic.rb +51 -0
- data/lib/hecks/fuzzing/outside_world.rb +45 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +102 -0
- data/lib/hecks/fuzzing/properties/corrections.rb +88 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +314 -0
- data/lib/hecks/fuzzing/properties/guards.rb +190 -0
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +300 -0
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +93 -0
- data/lib/hecks/fuzzing/properties/outbox.rb +89 -0
- data/lib/hecks/fuzzing/properties/querying.rb +175 -0
- data/lib/hecks/fuzzing/properties.rb +162 -0
- data/lib/hecks/fuzzing/qa_settings.rb +123 -0
- data/lib/hecks/fuzzing/replay.rb +505 -0
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -0
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +108 -0
- data/lib/hecks/fuzzing/self_consistency.rb +339 -0
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +385 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +132 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +95 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +77 -0
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +109 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +237 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +189 -0
- data/lib/hecks/fuzzing/shrinker.rb +166 -0
- data/lib/hecks/fuzzing/structural_skips.rb +33 -0
- data/lib/hecks/fuzzing/sweep_depth.rb +43 -0
- data/lib/hecks/fuzzing/target_capabilities.rb +163 -0
- data/lib/hecks/fuzzing/value_generator.rb +216 -0
- data/lib/hecks/fuzzing.rb +23 -0
- data/lib/hecks/grammar/evolve.rb +336 -0
- data/lib/hecks/hecks/adapters/codebase/codemods.rb +45 -0
- data/lib/hecks/hecks/adapters/codebase/conformance.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +202 -0
- data/lib/hecks/hecks/adapters/codebase/gem_registry.rb +80 -0
- data/lib/hecks/hecks/adapters/codebase/kernel_tables.rb +54 -0
- data/lib/hecks/hecks/adapters/codebase/language.rb +240 -0
- data/lib/hecks/hecks/adapters/codebase/npm_registry.rb +102 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +142 -0
- data/lib/hecks/hecks/adapters/codebase/regeneration.rb +67 -0
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +90 -0
- data/lib/hecks/hecks/adapters/codebase/ruby_child.rb +83 -0
- data/lib/hecks/hecks/adapters/codebase/secret_vault.rb +36 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.adapter +7 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +189 -0
- data/lib/hecks/hecks/adapters/codebase/sqlite_fixture.rb +71 -0
- data/lib/hecks/hecks/adapters/codebase/style.rb +171 -0
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +55 -0
- data/lib/hecks/hecks/adapters/codebase/test_suite.rb +165 -0
- data/lib/hecks/hecks/adapters/codebase/tree.rb +111 -0
- data/lib/hecks/hecks/adapters/console_capture.rb +79 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +108 -0
- data/lib/hecks/hecks/adapters/git.adapter +7 -0
- data/lib/hecks/hecks/adapters/git.rb +102 -0
- data/lib/hecks/hecks/adapters/host_http.adapter +7 -0
- data/lib/hecks/hecks/adapters/host_http.rb +65 -0
- data/lib/hecks/hecks/adapters/in_process_boot.adapter +7 -0
- data/lib/hecks/hecks/adapters/in_process_boot.rb +197 -0
- data/lib/hecks/hecks/adapters/in_process_operations.rb +202 -0
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +143 -0
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +145 -0
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +132 -0
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +135 -0
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +163 -0
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +187 -0
- data/lib/hecks/hecks/adapters/journal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/journal_store.rb +64 -0
- data/lib/hecks/hecks/adapters/local_files.adapter +7 -0
- data/lib/hecks/hecks/adapters/local_files.rb +46 -0
- data/lib/hecks/hecks/adapters/pg_admin.adapter +7 -0
- data/lib/hecks/hecks/adapters/pg_admin.rb +245 -0
- data/lib/hecks/hecks/adapters/process_pool.adapter +7 -0
- data/lib/hecks/hecks/adapters/process_pool.rb +119 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.adapter +7 -0
- data/lib/hecks/hecks/adapters/rust_toolchain.rb +179 -0
- data/lib/hecks/hecks/adapters/rust_workspace.rb +150 -0
- data/lib/hecks/hecks/adapters/shell.adapter +7 -0
- data/lib/hecks/hecks/adapters/shell.rb +49 -0
- data/lib/hecks/hecks/adapters/terminal.adapter +7 -0
- data/lib/hecks/hecks/adapters/terminal.rb +74 -0
- data/lib/hecks/hecks/codebase.behaviors +172 -0
- data/lib/hecks/hecks/codebase.bluebook +2453 -0
- data/lib/hecks/hecks/context_map.hecksagon +13 -0
- data/lib/hecks/hecks/custodian.behaviors +179 -0
- data/lib/hecks/hecks/custodian.bluebook +1847 -0
- data/lib/hecks/hecks/deploy.behaviors +74 -0
- data/lib/hecks/hecks/deploy.world +12 -0
- data/lib/hecks/hecks/environments/memory.world +10 -0
- data/lib/hecks/hecks/governance.world +7 -0
- data/lib/hecks/hecks/hecks.behaviors +62 -0
- data/lib/hecks/hecks/hecks.bluebook +124 -0
- data/lib/hecks/hecks/hecks.hecksagon +390 -0
- data/lib/hecks/hecks/hecks.world +42 -0
- data/lib/hecks/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +11 -0
- data/lib/hecks/language/bluebook/query.bluebook +7 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +4 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +10 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +20 -0
- data/lib/hecks/mcp_stdio_guard.rb +2 -2
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +8 -5
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +4 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +15 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +32 -15
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +6 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +227 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +43 -24
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/state_codec.rb +24 -5
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +1 -1
- data/lib/hecks/projections/deploy/fargate.rb +5 -5
- data/lib/hecks/projections/deploy/lambda.rb +43 -32
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/template.rb +1 -1
- data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts/expected-era.header +1 -1
- data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +3 -3
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/shared.rb +1 -1
- data/lib/hecks/projections/diagrams.rb +3 -3
- data/lib/hecks/projections/model/deviations.rb +1 -1
- data/lib/hecks/projections/parser_table.rb +4 -4
- data/lib/hecks/projections/reference.rb +2 -3
- data/lib/hecks/projections/rust_vocabulary.rb +1 -1
- data/lib/hecks/projector/cli_projector.rb +106 -37
- data/lib/hecks/projector.rb +1 -1
- data/lib/hecks/quality_control/adapters/agent.adapter +7 -0
- data/lib/hecks/quality_control/adapters/agent.rb +58 -0
- data/lib/hecks/quality_control/adapters/angle_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/angle_tools.rb +15 -0
- data/lib/hecks/quality_control/adapters/clearance_recorder.rb +36 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/clearance_tools.rb +17 -0
- data/lib/hecks/quality_control/adapters/git_pr.adapter +7 -0
- data/lib/hecks/quality_control/adapters/git_pr.rb +226 -0
- data/lib/hecks/quality_control/adapters/github_checks.adapter +9 -0
- data/lib/hecks/quality_control/adapters/github_checks.rb +83 -0
- data/lib/hecks/quality_control/adapters/github_ci_webhook.rb +56 -0
- data/lib/hecks/quality_control/adapters/github_issues.adapter +7 -0
- data/lib/hecks/quality_control/adapters/github_issues.rb +49 -0
- data/lib/hecks/quality_control/adapters/qa_tool.rb +59 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/sweep_tools.rb +51 -0
- data/lib/hecks/quality_control/adapters/target_tools.adapter +7 -0
- data/lib/hecks/quality_control/adapters/target_tools.rb +43 -0
- data/lib/hecks/quality_control/cli/child.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_concurrency_racer.rb +45 -0
- data/lib/hecks/quality_control/cli/qa_discover_external_domains.rb +248 -0
- data/lib/hecks/quality_control/cli/qa_domain_novelty.rb +163 -0
- data/lib/hecks/quality_control/cli/qa_generated_domains.rb +450 -0
- data/lib/hecks/quality_control/cli/qa_log_bug.rb +195 -0
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +220 -0
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +305 -0
- data/lib/hecks/quality_control/cli/qa_postgres_migrate.rb +154 -0
- data/lib/hecks/quality_control/cli/qa_postgres_role.rb +159 -0
- data/lib/hecks/quality_control/cli/qa_pr_check.rb +232 -0
- data/lib/hecks/quality_control/cli/qa_seed_angles.rb +142 -0
- data/lib/hecks/quality_control/cli/qa_seed_targets.rb +69 -0
- data/lib/hecks/quality_control/cli/qa_sweep/all_mode.rb +280 -0
- data/lib/hecks/quality_control/cli/qa_sweep/checks.rb +329 -0
- data/lib/hecks/quality_control/cli/qa_sweep/dials.rb +92 -0
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +117 -0
- data/lib/hecks/quality_control/cli/qa_sweep/parsing.rb +175 -0
- data/lib/hecks/quality_control/cli/qa_sweep/release_mode.rb +60 -0
- data/lib/hecks/quality_control/cli/qa_sweep.rb +611 -0
- data/lib/hecks/quality_control/cli/qa_tick.rb +274 -0
- data/lib/hecks/quality_control/quality_control.bluebook +2224 -0
- data/lib/hecks/quality_control/quality_control.hecksagon +13 -0
- data/lib/hecks/quality_control/quality_control.ports.hecksagon +54 -0
- data/lib/hecks/query_ir.rb +346 -0
- data/lib/hecks/query_ir_mcp.rb +198 -0
- data/lib/hecks/release/runner/ci_publisher.rb +2 -2
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +14 -7
- data/lib/hecks/release/runner/npm_publisher.rb +6 -36
- data/lib/hecks/release/runner/preflight.rb +26 -2
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +30 -15
- data/lib/hecks/release/runner.rb +57 -6
- data/lib/hecks/router/namespace_installer.rb +3 -3
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +4 -0
- data/lib/hecks/runtime/dependency_planning.rb +20 -2
- data/lib/hecks/runtime/dispatcher.rb +53 -3
- data/lib/hecks/runtime/interpreting.rb +3 -0
- data/lib/hecks/runtime/loader.rb +65 -12
- data/lib/hecks/runtime/outbox.rb +1 -1
- data/lib/hecks/runtime/policy_interpreter.rb +5 -2
- data/lib/hecks/runtime/port_operation_interpreter.rb +2 -10
- data/lib/hecks/runtime/query_interpreter.rb +77 -0
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/refusal_wording.rb +23 -1
- data/lib/hecks/runtime/registry/verification.rb +163 -4
- data/lib/hecks/runtime/registry.rb +57 -3
- data/lib/hecks/runtime/remote_dispatcher.rb +31 -2
- data/lib/hecks/runtime/value/coercion.rb +23 -135
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +4 -1
- data/lib/hecks/runtime.rb +20 -6
- data/lib/hecks/rust_build/conformance.rb +158 -0
- data/lib/hecks/rust_build/conformance_fuzz.rb +71 -0
- data/lib/hecks/rust_build/coverage.rb +218 -0
- data/lib/hecks/rust_build/native_build.rb +124 -0
- data/lib/hecks/rust_build/project_rust/root_mod.rb +120 -0
- data/lib/hecks/rust_build/project_rust.rb +332 -0
- data/lib/hecks/rust_build/wasm.rb +105 -0
- data/lib/hecks/rust_build/wasm_browser.rb +113 -0
- data/lib/hecks/rust_build.rb +144 -0
- data/lib/hecks/storehouse.rb +16 -16
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +2 -2
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +1 -1
- data/lib/hecks/three_zero/forms.yml +127 -0
- data/lib/hecks/three_zero.rb +12 -0
- data/lib/hecks/tools/argument_gate_matrix.rb +316 -0
- data/lib/hecks/tools/comment_style.rb +1070 -0
- data/lib/hecks/tools/corpus_report.rb +87 -0
- data/lib/hecks/tools/deploy_recipe.rb +255 -0
- data/lib/hecks/tools/deploy_recipe_lint.rb +282 -0
- data/lib/hecks/tools/drop_implicit_append_fields.rb +95 -0
- data/lib/hecks/tools/era_projection_backfill.rb +94 -0
- data/lib/hecks/tools/era_reattest.rb +146 -0
- data/lib/hecks/tools/evolve_run.rb +189 -0
- data/lib/hecks/tools/fuzz_sweep.rb +373 -0
- data/lib/hecks/tools/heki_compaction.rb +100 -0
- data/lib/hecks/tools/hoist_local_givens.rb +246 -0
- data/lib/hecks/tools/journal_compaction.rb +89 -0
- data/lib/hecks/tools/oidc_manifests.rb +70 -0
- data/lib/hecks/tools/query_ir_run.rb +81 -0
- data/lib/hecks/tools/regeneration_run.rb +166 -0
- data/lib/hecks/tools/rust_comment_style.rb +642 -0
- data/lib/hecks/tools/sequence_script.rb +62 -0
- data/lib/hecks/tools/tail_merge.rb +117 -0
- data/lib/hecks/tools/tenant_provisioning.rb +142 -0
- data/lib/hecks/tools/translation_audit.rb +208 -0
- data/lib/hecks/tools/translation_scaffold.rb +131 -0
- data/lib/hecks/tools.rb +86 -0
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks.rb +32 -15
- data/qa/settings.yml +66 -0
- data/rust/.cargo/config.toml +6 -0
- data/rust/.gitignore +2 -0
- data/rust/Cargo.lock +54 -0
- data/rust/Cargo.toml +61 -0
- data/rust/build/.cargo/config.toml +3 -0
- data/rust/build/.gitignore +1 -0
- data/rust/build/Cargo.lock +7 -0
- data/rust/build/Cargo.toml +19 -0
- data/rust/build/src/build_artifact.rs +47 -0
- data/rust/build/src/cargo_sync.rs +248 -0
- data/rust/build/src/json.rs +421 -0
- data/rust/build/src/lineage_pass.rs +285 -0
- data/rust/build/src/main.rs +51 -0
- data/rust/build/src/optional_pass.rs +150 -0
- data/rust/build/src/pipeline.rs +264 -0
- data/rust/build/src/reserved_names.rs +76 -0
- data/rust/build/src/resolve.rs +124 -0
- data/rust/build/src/root.rs +25 -0
- data/rust/build/src/sidecars.rs +73 -0
- data/rust/build/src/subprocess.rs +51 -0
- data/rust/build/src/tmp.rs +31 -0
- data/rust/codegen/.cargo/config.toml +3 -0
- data/rust/codegen/.gitignore +1 -0
- data/rust/codegen/Cargo.lock +7 -0
- data/rust/codegen/Cargo.toml +20 -0
- data/rust/codegen/src/attr.rs +32 -0
- data/rust/codegen/src/bridging.rs +481 -0
- data/rust/codegen/src/commands.rs +1226 -0
- data/rust/codegen/src/constraints.rs +109 -0
- data/rust/codegen/src/dependency_planning.rs +209 -0
- data/rust/codegen/src/domain_generator.rs +1288 -0
- data/rust/codegen/src/exemplar.rs +230 -0
- data/rust/codegen/src/expr_emitter.rs +87 -0
- data/rust/codegen/src/fielded.rs +192 -0
- data/rust/codegen/src/hecks_naming.rs +33 -0
- data/rust/codegen/src/json.rs +335 -0
- data/rust/codegen/src/json_codec.rs +697 -0
- data/rust/codegen/src/literal.rs +207 -0
- data/rust/codegen/src/main.rs +290 -0
- data/rust/codegen/src/manifest.rs +227 -0
- data/rust/codegen/src/mutations.rs +784 -0
- data/rust/codegen/src/naming.rs +286 -0
- data/rust/codegen/src/ports.rs +208 -0
- data/rust/codegen/src/prelude.rs +116 -0
- data/rust/codegen/src/queries.rs +786 -0
- data/rust/codegen/src/reactions.rs +532 -0
- data/rust/codegen/src/read_models.rs +697 -0
- data/rust/codegen/src/reference_specs.rs +40 -0
- data/rust/codegen/src/registry.rs +966 -0
- data/rust/codegen/src/reserved_names.rs +76 -0
- data/rust/codegen/src/shared.rs +93 -0
- data/rust/codegen/src/skip_reason.rs +25 -0
- data/rust/codegen/src/types.rs +362 -0
- data/rust/host/.cargo/config.toml +3 -0
- data/rust/host/.gitignore +1 -0
- data/rust/host/Cargo.lock +4297 -0
- data/rust/host/Cargo.toml +61 -0
- data/rust/host/HECKS_RELEASE +1 -0
- data/rust/host/rds-ca-bundle.pem +2736 -0
- data/rust/host/src/api.rs +1596 -0
- data/rust/host/src/approval.rs +652 -0
- data/rust/host/src/auth.rs +1287 -0
- data/rust/host/src/bin/expr_harness.rs +46 -0
- data/rust/host/src/bin/lineage_harness.rs +119 -0
- data/rust/host/src/bin/mint_harness.rs +135 -0
- data/rust/host/src/checkout.rs +265 -0
- data/rust/host/src/dispatch.rs +1443 -0
- data/rust/host/src/expr_json.rs +1117 -0
- data/rust/host/src/field_hints.rs +52 -0
- data/rust/host/src/ir.rs +733 -0
- data/rust/host/src/journal.rs +874 -0
- data/rust/host/src/lambda_client.rs +378 -0
- data/rust/host/src/log.rs +147 -0
- data/rust/host/src/main.rs +474 -0
- data/rust/host/src/mint.rs +1865 -0
- data/rust/host/src/payments/keystore.rs +330 -0
- data/rust/host/src/payments/tests.rs +1400 -0
- data/rust/host/src/payments.rs +767 -0
- data/rust/host/src/presentation.rs +698 -0
- data/rust/host/src/presentation_write.rs +1431 -0
- data/rust/host/src/query_step.rs +149 -0
- data/rust/host/src/rate_limit.rs +904 -0
- data/rust/host/src/reference_transform.rs +241 -0
- data/rust/host/src/reference_validate.rs +338 -0
- data/rust/host/src/resend.rs +296 -0
- data/rust/host/src/secrets.rs +106 -0
- data/rust/host/src/server.rs +663 -0
- data/rust/host/src/storage_shape.rs +175 -0
- data/rust/host/src/test_pg.rs +40 -0
- data/rust/host/src/ui_schema.rs +1351 -0
- data/rust/host/src/wasm_runner.rs +191 -0
- data/rust/host/src/web/newsletter/cooldown.rs +126 -0
- data/rust/host/src/web/newsletter/tests.rs +104 -0
- data/rust/host/src/web/newsletter.rs +310 -0
- data/rust/host/src/web/newsletter_send.rs +357 -0
- data/rust/host/src/web/registrations.rs +1505 -0
- data/rust/host/src/web.rs +2937 -0
- data/rust/lsp/.gitignore +1 -0
- data/rust/lsp/Cargo.lock +7 -0
- data/rust/lsp/Cargo.toml +16 -0
- data/rust/lsp/README.md +133 -0
- data/rust/lsp/src/diagnostics.rs +165 -0
- data/rust/lsp/src/json.rs +385 -0
- data/rust/lsp/src/main.rs +399 -0
- data/rust/lsp/src/outline.rs +177 -0
- data/rust/lsp/src/rpc.rs +67 -0
- data/rust/parser/.cargo/config.toml +5 -0
- data/rust/parser/.gitignore +1 -0
- data/rust/parser/Cargo.lock +7 -0
- data/rust/parser/Cargo.toml +21 -0
- data/rust/parser/src/build/closed_sets.rs +27 -0
- data/rust/parser/src/build/identity.rs +276 -0
- data/rust/parser/src/build/mod.rs +12 -0
- data/rust/parser/src/build/naming.rs +141 -0
- data/rust/parser/src/build/pattern_subset.rs +161 -0
- data/rust/parser/src/build/query_derive.rs +72 -0
- data/rust/parser/src/build/query_inference.rs +269 -0
- data/rust/parser/src/build/query_options.rs +100 -0
- data/rust/parser/src/build/read_model.rs +94 -0
- data/rust/parser/src/build/references.rs +32 -0
- data/rust/parser/src/canonical.rs +114 -0
- data/rust/parser/src/diag.rs +55 -0
- data/rust/parser/src/emit.rs +953 -0
- data/rust/parser/src/expr/ast_json.rs +147 -0
- data/rust/parser/src/expr/evaluator.rs +81 -0
- data/rust/parser/src/expr/mod.rs +84 -0
- data/rust/parser/src/expr/resolver.rs +497 -0
- data/rust/parser/src/ir.rs +387 -0
- data/rust/parser/src/keywords.rs +585 -0
- data/rust/parser/src/lex.rs +662 -0
- data/rust/parser/src/main.rs +252 -0
- data/rust/parser/src/parse/aggregate.rs +612 -0
- data/rust/parser/src/parse/chapter.rs +517 -0
- data/rust/parser/src/parse/command.rs +637 -0
- data/rust/parser/src/parse/domain_port.rs +127 -0
- data/rust/parser/src/parse/entity.rs +527 -0
- data/rust/parser/src/parse/file.rs +67 -0
- data/rust/parser/src/parse/hecksagon.rs +231 -0
- data/rust/parser/src/parse/lifecycle.rs +162 -0
- data/rust/parser/src/parse/mod.rs +1262 -0
- data/rust/parser/src/parse/policy.rs +87 -0
- data/rust/parser/src/parse/process_manager.rs +305 -0
- data/rust/parser/src/parse/query.rs +100 -0
- data/rust/parser/src/parse/read_model.rs +214 -0
- data/rust/parser/src/parse/value_object.rs +250 -0
- data/rust/parser/src/ruby_value.rs +422 -0
- data/rust/project/bridging.rb +257 -0
- data/rust/project/commands.rb +877 -0
- data/rust/project/constraints.rb +86 -0
- data/rust/project/dependency_planning.rb +178 -0
- data/rust/project/domain_generator.rb +980 -0
- data/rust/project/exemplar.rb +148 -0
- data/rust/project/expr_emitter.rb +72 -0
- data/rust/project/fielded.rb +141 -0
- data/rust/project/json_codec.rb +641 -0
- data/rust/project/mutations.rb +721 -0
- data/rust/project/naming.rb +205 -0
- data/rust/project/ports.rb +65 -0
- data/rust/project/queries.rb +493 -0
- data/rust/project/reactions.rb +317 -0
- data/rust/project/read_models.rb +690 -0
- data/rust/project/reference_specs.rb +27 -0
- data/rust/project/registry.rb +396 -0
- data/rust/project/skip_reason.rb +29 -0
- data/rust/project/types.rb +225 -0
- data/rust/project/write_if_changed.rb +83 -0
- data/rust/project.rb +30 -0
- data/rust/project_rust_pipeline.rb +383 -0
- data/rust/src/exemplar/commands.rs +279 -0
- data/rust/src/exemplar/constraints.rs +18 -0
- data/rust/src/exemplar/fielded.rs +280 -0
- data/rust/src/exemplar/json.rs +277 -0
- data/rust/src/exemplar/mod.rs +17 -0
- data/rust/src/exemplar/mutations.rs +80 -0
- data/rust/src/exemplar/queries.rs +33 -0
- data/rust/src/exemplar/reactions.rs +88 -0
- data/rust/src/exemplar/read_models.rs +48 -0
- data/rust/src/exemplar/registry.rs +166 -0
- data/rust/src/exemplar/types.rs +44 -0
- data/rust/src/kernel/attribute_shapes/composite.rs +88 -0
- data/rust/src/kernel/attribute_shapes/list.rs +18 -0
- data/rust/src/kernel/attribute_shapes/mod.rs +44 -0
- data/rust/src/kernel/attribute_shapes/optional.rs +9 -0
- data/rust/src/kernel/attribute_shapes/scalar.rs +39 -0
- data/rust/src/kernel/cli.rs +585 -0
- data/rust/src/kernel/dispatch.rs +1090 -0
- data/rust/src/kernel/expr.rs +458 -0
- data/rust/src/kernel/expression_operators/arithmetic.rs +128 -0
- data/rust/src/kernel/expression_operators/comparison.rs +48 -0
- data/rust/src/kernel/expression_operators/enumeration.rs +232 -0
- data/rust/src/kernel/expression_operators/logical.rs +13 -0
- data/rust/src/kernel/expression_operators/membership.rs +18 -0
- data/rust/src/kernel/expression_operators/mod.rs +68 -0
- data/rust/src/kernel/expression_operators/pattern_match.rs +73 -0
- data/rust/src/kernel/expression_operators/positional.rs +126 -0
- data/rust/src/kernel/expression_operators/presence.rs +101 -0
- data/rust/src/kernel/expression_operators/sign_test.rs +16 -0
- data/rust/src/kernel/expression_operators/sized.rs +35 -0
- data/rust/src/kernel/expression_operators/text.rs +79 -0
- data/rust/src/kernel/expression_operators/to_string.rs +18 -0
- data/rust/src/kernel/json.rs +779 -0
- data/rust/src/kernel/mod.rs +133 -0
- data/rust/src/kernel/named_query.rs +219 -0
- data/rust/src/kernel/orchestrate.rs +1810 -0
- data/rust/src/kernel/pattern.rs +307 -0
- data/rust/src/kernel/query_comparators.rs +206 -0
- data/rust/src/kernel/query_ordering.rs +103 -0
- data/rust/src/kernel/read_model.rs +914 -0
- data/rust/src/kernel/reference_lookup.rs +149 -0
- data/rust/src/kernel/repository.rs +468 -0
- data/rust/src/kernel/routing.rs +612 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +118 -0
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +114 -0
- data/rust/src/kernel/vocab/field_hint.rs +70 -0
- data/rust/src/kernel/vocab/mod.rs +20 -0
- data/rust/src/kernel/vocab/query_comparator.rs +65 -0
- data/rust/src/kernel/vocab/refusal_template.rs +1592 -0
- data/rust/src/lib.rs +8 -0
- data/rust/src/main.rs +16 -0
- data/rust/web/.gitignore +1 -0
- data/rust/web/Cargo.lock +119 -0
- data/rust/web/Cargo.toml +18 -0
- data/rust/web/src/lib.rs +8 -0
- metadata +477 -12
- data/lib/hecks/facade/cli_door.rb +0 -96
- data/lib/hecks/facade/cli_runner.rb +0 -127
- data/lib/hecks/mcp_door_scope.rb +0 -95
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "../../hecks"
|
|
6
|
+
require_relative "../bluebook/meta_validator"
|
|
7
|
+
require_relative "../ports/persistence/plugins/era"
|
|
8
|
+
require_relative "../../../rust/project"
|
|
9
|
+
|
|
10
|
+
module Hecks
|
|
11
|
+
module RustBuild
|
|
12
|
+
# Generates Rust source for one domain into the workspace's `src/generated/`, and keeps the
|
|
13
|
+
# workspace's `Cargo.toml` features in step; `rust/project.rb` (`RustProjection`) holds the
|
|
14
|
+
# architecture of the generator itself.
|
|
15
|
+
#
|
|
16
|
+
# ProjectRust.call(["path/to/domain"])
|
|
17
|
+
#
|
|
18
|
+
# The era plugin is required unconditionally so the generator works for any domain, lineage
|
|
19
|
+
# capable or not; `pg` stays lazy inside it, so nothing here opens a database connection.
|
|
20
|
+
# With `HECKS_PARSER=rust` and `HECKS_CODEGEN=rust` together, the whole pipeline runs through
|
|
21
|
+
# `rust/project_rust_pipeline.rb` instead, with no Ruby load of the domain.
|
|
22
|
+
class ProjectRust
|
|
23
|
+
# The optional attachments `rust/host` reads from `ir.json`, each omitted when nothing
|
|
24
|
+
# attached provides it: exporter method, then the key it is written under.
|
|
25
|
+
SEAMS = %i[authorization membership identity newsletter newsletter_issues payments
|
|
26
|
+
registrations payment_connection].freeze
|
|
27
|
+
|
|
28
|
+
# @param argv [Array<String>] the domain's directory
|
|
29
|
+
# @return [Integer] the exit status
|
|
30
|
+
# @raise [Failure] when the domain's name cannot be a Rust module and Cargo feature name
|
|
31
|
+
def self.call(argv)
|
|
32
|
+
domain = argv.first or raise Failure, "usage: hecks project_rust <domain>"
|
|
33
|
+
new(domain).call
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @param domain [String] the domain's directory
|
|
37
|
+
def initialize(domain)
|
|
38
|
+
@domain = domain
|
|
39
|
+
@mod_name = File.basename(domain)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [Integer] the exit status
|
|
43
|
+
def call
|
|
44
|
+
validate_name!
|
|
45
|
+
if ENV["HECKS_PARSER"] == "rust" && ENV["HECKS_CODEGEN"] == "rust"
|
|
46
|
+
require_relative "../../../rust/project_rust_pipeline"
|
|
47
|
+
RustProjectPipeline.call(@domain)
|
|
48
|
+
else
|
|
49
|
+
generate
|
|
50
|
+
end
|
|
51
|
+
0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# The name becomes a directory, a `pub mod` identifier and a Cargo feature, so it is checked
|
|
57
|
+
# before any side effect (the `meta/` directory is rewritten below).
|
|
58
|
+
def validate_name!
|
|
59
|
+
return if RustProjection::Projector.valid_domain_mod_name?(@mod_name)
|
|
60
|
+
|
|
61
|
+
raise Failure, "hecks project_rust: domain name #{@mod_name.inspect} (from #{@domain.inspect}) can't " \
|
|
62
|
+
"be used as-is — it has to double as a Rust module identifier and a Cargo feature " \
|
|
63
|
+
"name, and this one is either not a plain lowercase identifier, is a Rust keyword, " \
|
|
64
|
+
"or collides with a reserved Cargo.toml key " \
|
|
65
|
+
"(#{RustProjection::Projector::CARGO_RESERVED_DOMAIN_NAMES.join(', ')}). " \
|
|
66
|
+
"Rename the domain directory."
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def generate
|
|
70
|
+
load_registry
|
|
71
|
+
@rust_dir = ENV.fetch("HECKS_RUST_DIR", File.join(RustBuild::ROOT, "rust"))
|
|
72
|
+
@out_root = File.join(@rust_dir, "src/generated")
|
|
73
|
+
FileUtils.mkdir_p(@out_root)
|
|
74
|
+
build_target_ir
|
|
75
|
+
write_meta
|
|
76
|
+
write_target
|
|
77
|
+
write_root_mod
|
|
78
|
+
sync_cargo_features
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Round-trips a value through JSON so generators see the string-keyed shape a real `ir.json`
|
|
82
|
+
# carries, never live Ruby symbols.
|
|
83
|
+
def json_shaped(payload) = JSON.parse(JSON.generate(payload), symbolize_names: true)
|
|
84
|
+
|
|
85
|
+
# `root:` is the domain's own directory: a domain that declares `uses_embryonaut_bluebook`
|
|
86
|
+
# resolves its vendored chapters beneath it.
|
|
87
|
+
def load_registry
|
|
88
|
+
@registry = Hecks::Runtime::Registry.new(root: File.expand_path(@domain))
|
|
89
|
+
Hecks.with_registry(@registry) do
|
|
90
|
+
lib = File.expand_path("..", __dir__)
|
|
91
|
+
%w[ports/persistence.port ports/extraction.port adapters/driven/memory.adapter
|
|
92
|
+
adapters/driven/prism.adapter adapters/driven/postgres_era.adapter].each do |file|
|
|
93
|
+
Kernel.load(File.join(lib, file))
|
|
94
|
+
end
|
|
95
|
+
@bluebook_dir = File.join(@domain, "bluebook")
|
|
96
|
+
Hecks::Adapters::Folder.new.load_bluebooks(@bluebook_dir)
|
|
97
|
+
load_siblings
|
|
98
|
+
load_environment
|
|
99
|
+
end
|
|
100
|
+
@domain_name = @registry.bluebooks.keys.first
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Translation edges, hecksagons (whose `uses_framework` loads a framework chapter into the
|
|
104
|
+
# same registry) and worlds (whose `default_adapter` binds what a hecksagon leaves out), in
|
|
105
|
+
# the order the language loads them.
|
|
106
|
+
def load_siblings
|
|
107
|
+
Dir[File.join(@bluebook_dir, "translations", "*.bluebook")].each { |file| Kernel.load(file) }
|
|
108
|
+
Dir.glob(File.join(@bluebook_dir, "*.hecksagon")).each { |file| Kernel.load(file) }
|
|
109
|
+
Dir.glob(File.join(@bluebook_dir, "*.world")).each { |file| Kernel.load(file) }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Production is the default environment when `environments/production.hecksagon` exists
|
|
113
|
+
# (`rust/host` is the production runtime); `HECKS_PROJECT_ENVIRONMENT` overrides it.
|
|
114
|
+
def load_environment
|
|
115
|
+
environment = ENV.fetch("HECKS_PROJECT_ENVIRONMENT") do
|
|
116
|
+
File.exist?(File.join(@bluebook_dir, "environments", "production.hecksagon")) ? "production" : nil
|
|
117
|
+
end
|
|
118
|
+
return if environment.nil? || environment.empty?
|
|
119
|
+
|
|
120
|
+
folder = Hecks::Adapters::Folder.new
|
|
121
|
+
folder.load_each(@bluebook_dir, [File.join("environments", "#{environment}.hecksagon")])
|
|
122
|
+
folder.load_each(@bluebook_dir, [File.join("environments", "#{environment}.world")])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# The target's IR, with the binding facts that ride beside it (they are not shape facts, so
|
|
126
|
+
# `bluebook.to_h` never mentions them).
|
|
127
|
+
def build_target_ir
|
|
128
|
+
exporter = Hecks::Projector::Exporter
|
|
129
|
+
@ir = json_shaped(exporter.call(@registry).fetch(@domain_name))
|
|
130
|
+
@ir[:lineage] = json_shaped(exporter.lineage(@registry, @domain_name))
|
|
131
|
+
@ir[:persistence] = json_shaped(exporter.persistence(@registry, @domain_name))
|
|
132
|
+
seams = SEAMS.to_h { |seam| [seam, exporter.public_send(seam, @registry, @domain_name)] }
|
|
133
|
+
seams.each { |seam, value| @ir[seam] = json_shaped(value) unless value.empty? }
|
|
134
|
+
if !seams[:membership].empty? && seams[:identity].empty?
|
|
135
|
+
raise Failure, "#{@domain_name} provides membership but not identity — rust/host Google sign-in " \
|
|
136
|
+
"cannot register or link an identity from ir.json. Attach Identity " \
|
|
137
|
+
"(`uses_framework \"Identity\"` plus a sibling Hecks.hecksagon \"Identity\") so the " \
|
|
138
|
+
"hecksagon, not a deploy-time guess, names the identity verbs."
|
|
139
|
+
end
|
|
140
|
+
add_edges_and_source
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Edges carry their own precompiled SQL, so a boot-time mint only executes it. Committed
|
|
144
|
+
# approvals sit beside the edges. The source text is verbatim: the era's integrity digest is
|
|
145
|
+
# the SHA256 of it, not of anything re-derived from the parsed IR.
|
|
146
|
+
def add_edges_and_source
|
|
147
|
+
edges = Hecks::Projector::Exporter.translations(@registry).select { |edge| edge[:domain] == @domain_name }
|
|
148
|
+
@ir[:translations] = json_shaped(edges)
|
|
149
|
+
approvals = Hecks::Translation::ApprovalFile.read_all(@bluebook_dir)
|
|
150
|
+
@ir[:approvals] = json_shaped(approvals) unless approvals.empty?
|
|
151
|
+
@ir[:source_text] = Hecks::Runtime::EraCheck.source_text_for(
|
|
152
|
+
@registry.bluebooks.fetch(@domain_name), @bluebook_dir, registry: @registry
|
|
153
|
+
)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# The self-hosted language goes through the same codegen as the target, read from the
|
|
157
|
+
# grammar registry `MetaValidator` boots with.
|
|
158
|
+
def write_meta
|
|
159
|
+
meta_ir = json_shaped(
|
|
160
|
+
Hecks::Projector::Exporter.call(Hecks::Bluebook::MetaValidator.grammar_registry).fetch("Bluebook")
|
|
161
|
+
)
|
|
162
|
+
dir = File.join(@out_root, "meta")
|
|
163
|
+
# Tracked per directory: several domains coexist on disk, and `pop_and_prune` deletes only
|
|
164
|
+
# the files this run never touched. `active` is a set of feature re-exports in the root
|
|
165
|
+
# `mod.rs`, never a directory.
|
|
166
|
+
RustProjection::WriteIfChanged.push_directory(dir)
|
|
167
|
+
FileUtils.rm_rf(File.join(@out_root, "active"))
|
|
168
|
+
result = RustProjection::DomainGenerator.call(
|
|
169
|
+
meta_ir, "the self-hosted language (lib/hecks/language/bluebook)", dir, "meta"
|
|
170
|
+
)
|
|
171
|
+
write_merged(File.join(dir, "merged.rs"), result[:aggregates], result[:queries], result[:read_models],
|
|
172
|
+
policies: RustProjection::Projector.emit_policy_table(meta_ir[:name], meta_ir[:policies],
|
|
173
|
+
meta_ir[:aggregates]),
|
|
174
|
+
cross: RustProjection::Projector.emit_cross_domain_policy_table(meta_ir[:name],
|
|
175
|
+
meta_ir[:policies]),
|
|
176
|
+
managers: meta_ir[:process_managers],
|
|
177
|
+
keys: [[meta_ir[:name], result[:aggregates].map { |a| a[:name] }]])
|
|
178
|
+
append_merged_mod(File.join(dir, "mod.rs"))
|
|
179
|
+
RustProjection::WriteIfChanged.pop_and_prune(dir)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def write_target
|
|
183
|
+
dir = File.join(@out_root, @mod_name)
|
|
184
|
+
RustProjection::WriteIfChanged.push_directory(dir)
|
|
185
|
+
result = RustProjection::DomainGenerator.call(@ir, @domain, dir, @mod_name)
|
|
186
|
+
chapters = write_chapters
|
|
187
|
+
aggregates = result[:aggregates] + chapters[:aggregates]
|
|
188
|
+
write_merged(File.join(dir, "merged.rs"), aggregates, result[:queries] + chapters[:queries],
|
|
189
|
+
result[:read_models] + chapters[:read_models],
|
|
190
|
+
policies: RustProjection::Projector.emit_merged_policy_table(chapters[:policy_sources]),
|
|
191
|
+
cross: RustProjection::Projector.emit_merged_cross_domain_policy_table(chapters[:policy_sources]),
|
|
192
|
+
managers: @ir[:process_managers] + chapters[:process_managers],
|
|
193
|
+
keys: reference_keys(result[:aggregates], chapters))
|
|
194
|
+
append_merged_mod(File.join(dir, "mod.rs"))
|
|
195
|
+
RustProjection::WriteIfChanged.pop_and_prune(dir)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Every other bluebook the target's `uses_framework` calls pulled into the registry, each
|
|
199
|
+
# generated into its own module. A framework chapter is a function of its bluebook alone, so
|
|
200
|
+
# regenerating one is safe whichever domain last touched it.
|
|
201
|
+
def write_chapters
|
|
202
|
+
totals = { aggregates: [], queries: [], read_models: [], process_managers: [],
|
|
203
|
+
policy_sources: [{ domain_name: @domain_name, policies: @ir[:policies],
|
|
204
|
+
aggregates: @ir[:aggregates] }],
|
|
205
|
+
mod_names: { @mod_name => @domain_name } }
|
|
206
|
+
vendored = @registry.hecksagons.values.flat_map(&:vendored_bluebooks)
|
|
207
|
+
(@registry.bluebooks.keys - [@domain_name]).each { |chapter| write_chapter(chapter, vendored, totals) }
|
|
208
|
+
totals
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def write_chapter(chapter_name, vendored, totals)
|
|
212
|
+
mod_name = chapter_name.downcase
|
|
213
|
+
totals[:mod_names][mod_name] = chapter_name
|
|
214
|
+
chapter_ir = json_shaped(Hecks::Projector::Exporter.call(@registry).fetch(chapter_name))
|
|
215
|
+
result = nil
|
|
216
|
+
RustProjection::WriteIfChanged.track_directory(File.join(@out_root, mod_name)) do
|
|
217
|
+
result = RustProjection::DomainGenerator.call(chapter_ir, "#{@domain} (#{attachment(chapter_name, vendored)})",
|
|
218
|
+
File.join(@out_root, mod_name), mod_name,
|
|
219
|
+
merged_module: false)
|
|
220
|
+
end
|
|
221
|
+
totals[:aggregates].concat(result[:aggregates])
|
|
222
|
+
totals[:queries].concat(result[:queries])
|
|
223
|
+
totals[:read_models].concat(result[:read_models])
|
|
224
|
+
totals[:process_managers].concat(chapter_ir[:process_managers])
|
|
225
|
+
totals[:policy_sources] << { domain_name: chapter_name, policies: chapter_ir[:policies],
|
|
226
|
+
aggregates: chapter_ir[:aggregates] }
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# How the target attached a chapter, across every hecksagon the registry loaded: a chapter a
|
|
230
|
+
# sibling hecksagon vendors in is labelled as vendored.
|
|
231
|
+
def attachment(chapter_name, vendored)
|
|
232
|
+
name = vendored.find { |candidate| Hecks::Naming.pascal(candidate.to_s) == chapter_name }
|
|
233
|
+
name ? "uses_embryonaut_bluebook #{name.inspect}" : "uses_framework #{chapter_name.inspect}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def reference_keys(target_aggregates, chapters)
|
|
237
|
+
chapters[:mod_names].map do |mod_name, chapter_name|
|
|
238
|
+
own = mod_name == @mod_name ? target_aggregates : chapters[:aggregates].select { |a| a[:chapter_mod] == mod_name }
|
|
239
|
+
[chapter_name, own.map { |a| a[:name] }]
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Writes a module's `merged.rs`: the registry and every dispatch table over its aggregates.
|
|
244
|
+
def write_merged(path, aggregates, queries, read_models, policies:, cross:, managers:, keys:)
|
|
245
|
+
projector = RustProjection::Projector
|
|
246
|
+
wrote = RustProjection::WriteIfChanged.block(path) do |file|
|
|
247
|
+
[projector.emit_registry(aggregates), projector.emit_reference_lookup(aggregates), policies, cross,
|
|
248
|
+
projector.emit_process_manager_table(managers), projector.emit_reference_key_table(keys),
|
|
249
|
+
projector.emit_creates_table(aggregates), projector.emit_identity_head_table(aggregates),
|
|
250
|
+
projector.emit_entity_identity_head_table(aggregates),
|
|
251
|
+
projector.emit_command_attributes_table(aggregates), projector.emit_query_table(queries),
|
|
252
|
+
projector.emit_query_arg_check_table(queries)].each do |table|
|
|
253
|
+
file.puts table
|
|
254
|
+
file.puts
|
|
255
|
+
end
|
|
256
|
+
# A read model's `group_by` function is written before the table names it.
|
|
257
|
+
read_models.each do |model|
|
|
258
|
+
next unless model[:group_by_fn_body]
|
|
259
|
+
|
|
260
|
+
file.puts model[:group_by_fn_body]
|
|
261
|
+
file.puts
|
|
262
|
+
end
|
|
263
|
+
file.puts projector.emit_read_model_table(read_models)
|
|
264
|
+
end
|
|
265
|
+
puts(wrote ? "wrote #{path}" : "#{path} unchanged")
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Appends the `pub mod merged;` line once; appending it again would grow the file and bump
|
|
269
|
+
# its mtime on every run.
|
|
270
|
+
def append_merged_mod(path)
|
|
271
|
+
return if File.exist?(path) && File.read(path).include?("pub mod merged;")
|
|
272
|
+
|
|
273
|
+
File.open(path, "a") { |file| file.puts "pub mod merged;" }
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# `src/generated/mod.rs`: every domain ever generated, scanned off disk. A directory is a
|
|
277
|
+
# selectable domain exactly when it carries its own `merged.rs`; a framework chapter has
|
|
278
|
+
# none and is always compiled in.
|
|
279
|
+
def write_root_mod
|
|
280
|
+
all_dirs = Dir.children(@out_root).select { |name| File.directory?(File.join(@out_root, name)) }.sort
|
|
281
|
+
@domains = all_dirs.select { |name| File.exist?(File.join(@out_root, name, "merged.rs")) }
|
|
282
|
+
path = File.join(@out_root, "mod.rs")
|
|
283
|
+
wrote = RustProjection::WriteIfChanged.block(path) do |file|
|
|
284
|
+
RootMod.new(file, all_dirs, @domains, @mod_name).write
|
|
285
|
+
end
|
|
286
|
+
puts(wrote ? "wrote #{path}" : "#{path} unchanged")
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# One Cargo feature per domain on disk (added when missing, never removed), with `default`
|
|
290
|
+
# set to this run's domain so a bare `cargo build` builds it. A feature is inserted right
|
|
291
|
+
# after `default = [...]`, not appended: past `[[bin]]` it would parse into that table.
|
|
292
|
+
def sync_cargo_features
|
|
293
|
+
path = File.join(@rust_dir, "Cargo.toml")
|
|
294
|
+
manifest = File.read(path)
|
|
295
|
+
manifest = manifest.sub(/\A/, FEATURES_HEADER) unless manifest.include?("[features]")
|
|
296
|
+
@domains.each { |name| manifest = add_feature(manifest, name) }
|
|
297
|
+
manifest = manifest.sub(/^default\s*=.*$/, "default = [#{@mod_name.inspect}]")
|
|
298
|
+
# An unchanged manifest is not rewritten: a new mtime makes Cargo rebuild the whole crate.
|
|
299
|
+
if RustProjection::WriteIfChanged.call(path, manifest)
|
|
300
|
+
puts "wrote #{path} (default feature: #{@mod_name})"
|
|
301
|
+
else
|
|
302
|
+
puts "#{path} unchanged (default feature already #{@mod_name})"
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Checked against the `[features]` table only: `[package]`'s `name` or a `[[bin]]` path can
|
|
307
|
+
# share a word with a domain name.
|
|
308
|
+
def add_feature(manifest, name)
|
|
309
|
+
table = manifest[/^\[features\](?:\n(?!\[).*)*$/] || ""
|
|
310
|
+
return manifest if table =~ /^#{Regexp.escape(name)}\s*=/
|
|
311
|
+
|
|
312
|
+
manifest.sub(/^default\s*=.*$/) { "#{Regexp.last_match(0)}\n#{name} = []" }
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
FEATURES_HEADER = <<~TOML
|
|
316
|
+
# One feature per generated domain: it selects which domain's own
|
|
317
|
+
# generated::<domain>::merged module `generated::active` re-exports
|
|
318
|
+
# (rust/src/generated/mod.rs). `hecks project_rust` adds a feature when a new domain is
|
|
319
|
+
# generated and never removes one (the domain's own generated/<name>/ directory is the
|
|
320
|
+
# source of truth for whether it exists). `default` tracks the domain generated most
|
|
321
|
+
# recently, so a bare `cargo build` keeps working; any other still-generated domain stays
|
|
322
|
+
# reachable via `--no-default-features --features <name>`.
|
|
323
|
+
[features]
|
|
324
|
+
default = []
|
|
325
|
+
|
|
326
|
+
TOML
|
|
327
|
+
private_constant :FEATURES_HEADER
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
require_relative "project_rust/root_mod"
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "../rust_build"
|
|
5
|
+
require_relative "project_rust"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module RustBuild
|
|
9
|
+
# Regenerates a domain's Rust source and cross-compiles it to `wasm32-wasip1`
|
|
10
|
+
# (docs/decisions/0012-wasm-via-wasi-stdio.md).
|
|
11
|
+
#
|
|
12
|
+
# Wasm.call(["path/to/domain"])
|
|
13
|
+
#
|
|
14
|
+
# Writes `<workspace>/dist/<domain>.wasm`, run with
|
|
15
|
+
# `wasmtime run rust/dist/<domain>.wasm < script.json`, and the domain's `ir.json` beside it,
|
|
16
|
+
# which `rust/host` reads at runtime. `HECKS_RUST_DIR` names the workspace to build in and
|
|
17
|
+
# `CARGO_TARGET_DIR` where build output goes, beside the workspace by default.
|
|
18
|
+
module Wasm
|
|
19
|
+
# The Cargo target the module is built for.
|
|
20
|
+
TARGET = "wasm32-wasip1"
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
# @param argv [Array<String>] the domain's directory
|
|
25
|
+
# @return [Integer] the exit status
|
|
26
|
+
# @raise [Failure] when the target is not installed or a step fails
|
|
27
|
+
def call(argv)
|
|
28
|
+
domain = argv.first or raise Failure, "usage: hecks build_wasm <domain>"
|
|
29
|
+
return build_with_rust(domain) if ENV["HECKS_PARSER"] == "rust" && ENV["HECKS_CODEGEN"] == "rust"
|
|
30
|
+
|
|
31
|
+
require_target!
|
|
32
|
+
rust_dir = RustBuild.rust_dir
|
|
33
|
+
scratch = scratch_copy(rust_dir)
|
|
34
|
+
puts "== regenerating #{scratch}/src/generated/ for #{domain} =="
|
|
35
|
+
status = RustBuild.with_env("HECKS_RUST_DIR" => scratch) { ProjectRust.call([domain]) }
|
|
36
|
+
raise Failure, "project_rust failed for #{domain}" unless status.zero?
|
|
37
|
+
|
|
38
|
+
compile(scratch)
|
|
39
|
+
publish(scratch, rust_dir, File.basename(domain))
|
|
40
|
+
0
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# With the Rust parser and codegen, `hecks-build` runs the same steps and writes the same
|
|
44
|
+
# outputs.
|
|
45
|
+
def build_with_rust(domain)
|
|
46
|
+
build_dir = File.join(ROOT, "rust", "build")
|
|
47
|
+
puts "== cargo build (hecks-build) =="
|
|
48
|
+
RustBuild.command!("cargo", "build", chdir: build_dir)
|
|
49
|
+
puts "== hecks-build #{domain} --wasm =="
|
|
50
|
+
RustBuild.command!(File.join(build_dir, "target", "debug", "hecks-build"), domain, "--wasm")
|
|
51
|
+
0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def require_target!
|
|
55
|
+
return if system("rustup target list --installed 2>/dev/null | grep -qx #{TARGET}")
|
|
56
|
+
|
|
57
|
+
raise Failure, <<~MSG
|
|
58
|
+
#{TARGET} isn't installed for this toolchain. Install it once with:
|
|
59
|
+
|
|
60
|
+
rustup target add #{TARGET}
|
|
61
|
+
|
|
62
|
+
then re-run the build.
|
|
63
|
+
MSG
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Built in a scratch copy because generating rewrites `Cargo.toml` and `src/generated/`,
|
|
67
|
+
# which would dirty tracked files in a checkout.
|
|
68
|
+
def scratch_copy(rust_dir, name = "project_wasm")
|
|
69
|
+
scratch = if ENV.key?("HECKS_RUST_DIR")
|
|
70
|
+
File.join(rust_dir, "scratch", name)
|
|
71
|
+
else
|
|
72
|
+
File.join(ROOT, "tmp", name, "rust")
|
|
73
|
+
end
|
|
74
|
+
FileUtils.rm_rf(scratch)
|
|
75
|
+
FileUtils.mkdir_p(scratch)
|
|
76
|
+
%w[Cargo.toml Cargo.lock src].each { |entry| FileUtils.cp_r(File.join(rust_dir, entry), scratch) }
|
|
77
|
+
scratch
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# `rustup run`, not bare `cargo`: a `cargo` earlier on PATH may never have seen
|
|
81
|
+
# `rustup target add` and fail with "can't find crate for `std`".
|
|
82
|
+
def compile(scratch)
|
|
83
|
+
puts "== cargo build --release --target #{TARGET} =="
|
|
84
|
+
target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(RustBuild.rust_dir, "target"))
|
|
85
|
+
RustBuild.command!("rustup", "run", "stable", "cargo", "build", "--release", "--target", TARGET,
|
|
86
|
+
chdir: scratch, env: { "CARGO_TARGET_DIR" => target_dir })
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def publish(scratch, rust_dir, name)
|
|
90
|
+
dist = File.join(rust_dir, "dist")
|
|
91
|
+
FileUtils.mkdir_p(dist)
|
|
92
|
+
target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(rust_dir, "target"))
|
|
93
|
+
out = File.join(dist, "#{name}.wasm")
|
|
94
|
+
FileUtils.cp(File.join(target_dir, TARGET, "release", "rust.wasm"), out)
|
|
95
|
+
puts "wrote #{out}"
|
|
96
|
+
puts %(run it: wasmtime run #{out} < script.json)
|
|
97
|
+
sidecar = File.join(scratch, "src", "generated", name, "ir.json")
|
|
98
|
+
return unless File.exist?(sidecar)
|
|
99
|
+
|
|
100
|
+
FileUtils.cp(sidecar, File.join(dist, "#{name}.ir.json"))
|
|
101
|
+
puts "wrote #{File.join(dist, "#{name}.ir.json")}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "../rust_build"
|
|
5
|
+
require_relative "project_rust"
|
|
6
|
+
require_relative "wasm"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module RustBuild
|
|
10
|
+
# Builds the `rust/web` wasm-bindgen crate over a domain's generated code
|
|
11
|
+
# (docs/decisions/0015-wasm-bindgen-browser-projection.md).
|
|
12
|
+
#
|
|
13
|
+
# WasmBrowser.call(["path/to/domain"])
|
|
14
|
+
#
|
|
15
|
+
# Writes `<workspace>/dist/browser/<domain>/{<domain>.js,<domain>_bg.wasm,*.d.ts}`, an ES
|
|
16
|
+
# module whose `dispatch(json)` returns the same `{"instances","events","refusals"}` JSON as
|
|
17
|
+
# the WASI module.
|
|
18
|
+
module WasmBrowser
|
|
19
|
+
# The Cargo target the module is built for.
|
|
20
|
+
TARGET = "wasm32-unknown-unknown"
|
|
21
|
+
|
|
22
|
+
module_function
|
|
23
|
+
|
|
24
|
+
# @param argv [Array<String>] the domain's directory
|
|
25
|
+
# @return [Integer] the exit status
|
|
26
|
+
# @raise [Failure] when the target or the matching wasm-bindgen CLI is missing, or a step
|
|
27
|
+
# fails
|
|
28
|
+
def call(argv)
|
|
29
|
+
domain = argv.first or raise Failure, "usage: hecks build_browser_wasm <domain>"
|
|
30
|
+
rust_dir = RustBuild.rust_dir
|
|
31
|
+
web_dir = File.join(rust_dir, "web")
|
|
32
|
+
name = File.basename(domain)
|
|
33
|
+
require_target!
|
|
34
|
+
require_cli!(web_dir)
|
|
35
|
+
scratch = scratch_copy(rust_dir)
|
|
36
|
+
puts "== regenerating #{scratch}/src/generated/ for #{domain} =="
|
|
37
|
+
status = RustBuild.with_env("HECKS_RUST_DIR" => scratch) { ProjectRust.call([domain]) }
|
|
38
|
+
raise Failure, "project_rust failed for #{domain}" unless status.zero?
|
|
39
|
+
|
|
40
|
+
compile(File.join(scratch, "web"), web_dir)
|
|
41
|
+
bind(web_dir, name)
|
|
42
|
+
0
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Generated in a scratch copy, as `Wasm` does, because generating rewrites `Cargo.toml` and
|
|
46
|
+
# `src/generated/`, which would dirty tracked files in a checkout. The web crate's path
|
|
47
|
+
# dependency `..` resolves to the scratch workspace.
|
|
48
|
+
def scratch_copy(rust_dir)
|
|
49
|
+
scratch = Wasm.scratch_copy(rust_dir, "project_wasm_browser")
|
|
50
|
+
FileUtils.mkdir_p(File.join(scratch, "web"))
|
|
51
|
+
%w[Cargo.toml Cargo.lock src].each do |entry|
|
|
52
|
+
from = File.join(rust_dir, "web", entry)
|
|
53
|
+
FileUtils.cp_r(from, File.join(scratch, "web")) if File.exist?(from)
|
|
54
|
+
end
|
|
55
|
+
scratch
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def require_target!
|
|
59
|
+
return if system("rustup target list --installed 2>/dev/null | grep -qx #{TARGET}")
|
|
60
|
+
|
|
61
|
+
raise Failure, <<~MSG
|
|
62
|
+
#{TARGET} isn't installed for this toolchain. Install it once with:
|
|
63
|
+
|
|
64
|
+
rustup target add #{TARGET}
|
|
65
|
+
|
|
66
|
+
then re-run the build.
|
|
67
|
+
MSG
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# The wasm-bindgen CLI must match the crate version pinned in `rust/web/Cargo.toml`; a
|
|
71
|
+
# mismatch fails only when the CLI runs, so the pin is read back, not hardcoded.
|
|
72
|
+
def require_cli!(web_dir)
|
|
73
|
+
pinned = File.read(File.join(web_dir, "Cargo.toml"))[/^wasm-bindgen = "=(.+)"/, 1] or
|
|
74
|
+
raise Failure, "rust/web/Cargo.toml: couldn't find a pinned (\"=x.y.z\") wasm-bindgen version"
|
|
75
|
+
installed = `wasm-bindgen --version 2>/dev/null`[/[\d.]+/]
|
|
76
|
+
return if installed == pinned
|
|
77
|
+
|
|
78
|
+
raise Failure, <<~MSG
|
|
79
|
+
wasm-bindgen CLI #{installed.inspect} != the pinned crate version #{pinned.inspect}. Install the matching CLI once with:
|
|
80
|
+
|
|
81
|
+
cargo install wasm-bindgen-cli --version #{pinned} --locked
|
|
82
|
+
|
|
83
|
+
then re-run the build.
|
|
84
|
+
MSG
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def compile(scratch_web, web_dir)
|
|
88
|
+
puts "== cargo build --release --target #{TARGET} (rust/web) =="
|
|
89
|
+
target_dir = ENV.fetch("CARGO_TARGET_DIR", File.join(web_dir, "target"))
|
|
90
|
+
RustBuild.command!("rustup", "run", "stable", "cargo", "build", "--release", "--target", TARGET,
|
|
91
|
+
chdir: scratch_web, env: { "CARGO_TARGET_DIR" => target_dir })
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def bind(web_dir, name)
|
|
95
|
+
out_dir = File.join(RustBuild.rust_dir, "dist", "browser", name)
|
|
96
|
+
FileUtils.mkdir_p(out_dir)
|
|
97
|
+
built = File.join(ENV.fetch("CARGO_TARGET_DIR", File.join(web_dir, "target")), TARGET, "release",
|
|
98
|
+
"rust_web.wasm")
|
|
99
|
+
puts "== wasm-bindgen --target web =="
|
|
100
|
+
RustBuild.command!("wasm-bindgen", "--target", "web", "--out-dir", out_dir, "--out-name", name, built)
|
|
101
|
+
puts "wrote #{out_dir}/"
|
|
102
|
+
puts <<~MSG
|
|
103
|
+
use it in a browser:
|
|
104
|
+
<script type="module">
|
|
105
|
+
import init, { dispatch } from "./#{name}.js";
|
|
106
|
+
await init();
|
|
107
|
+
const result = dispatch(JSON.stringify({ steps: [...] }));
|
|
108
|
+
</script>
|
|
109
|
+
MSG
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|