hecks 2.9.0 → 3.0.1
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 +13 -4
- 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 +15 -8
- 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 +15 -5
- 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,133 @@
|
|
|
1
|
+
//! Hand-written, domain-agnostic floor that every generated per-command function calls into.
|
|
2
|
+
|
|
3
|
+
pub mod attribute_shapes;
|
|
4
|
+
pub mod cli;
|
|
5
|
+
pub mod dispatch;
|
|
6
|
+
pub mod expr;
|
|
7
|
+
pub mod expression_operators;
|
|
8
|
+
pub mod json;
|
|
9
|
+
pub mod named_query;
|
|
10
|
+
pub mod orchestrate;
|
|
11
|
+
pub mod pattern;
|
|
12
|
+
pub mod query_ordering;
|
|
13
|
+
pub mod vocab;
|
|
14
|
+
// Kept at its old path so existing `refusal_wording::RefusalSite` call sites still resolve.
|
|
15
|
+
pub use vocab::refusal_template as refusal_wording;
|
|
16
|
+
pub mod read_model;
|
|
17
|
+
pub mod query_comparators;
|
|
18
|
+
pub mod reference_lookup;
|
|
19
|
+
pub mod repository;
|
|
20
|
+
pub mod routing;
|
|
21
|
+
|
|
22
|
+
pub use dispatch::{aggregate_step_site, apply_entity_command, decode_aggregate_arguments, decode_entity_arguments, dispatch, dispatch_entity, enforce_invariants, entity_step_site, ArgumentGates, EnsuresSpec, EntityInvariants, GivenSpec, Hydrate, InvariantSet, InvariantSpec, StepSite, TransitionCheck};
|
|
23
|
+
pub use expr::{interpret, BlockMode, Bound, Comparison, EvalContext, Expr, Field, Fielded, NoFields, Value, WithParent};
|
|
24
|
+
pub use json::Json;
|
|
25
|
+
pub use named_query::{QueryCondition, QueryConditionValue, QueryDef};
|
|
26
|
+
pub use orchestrate::{
|
|
27
|
+
orchestrate, CompletedCompensation, CrossDomainPolicyRule, DispatchSpec, Handler, PendingCrossDomainReaction, PolicyRule, ProcessManagerDef,
|
|
28
|
+
SagaInstance, Tables, WithValue, MAX_REACTION_DEPTH, REFUSED,
|
|
29
|
+
};
|
|
30
|
+
pub use reference_lookup::{
|
|
31
|
+
command_deref, owner_deref, parent_deref, seeded_projections, DerefNode, ProjectedFieldSpec, ReferenceLookup, ReferenceSpec, ReferenceTable,
|
|
32
|
+
SetProjectedField, WithReferences, DEREFERENCE_DEPTH,
|
|
33
|
+
};
|
|
34
|
+
pub use refusal_wording::RefusalSite;
|
|
35
|
+
pub use repository::{check_reference, check_role_via, filter_entries, holds_role_via, row_json, AggregateScan, InMemoryRepository, Repository};
|
|
36
|
+
pub use routing::{CommandInvocation, RoutingEnvelope};
|
|
37
|
+
|
|
38
|
+
/// A domain event raised by a successful command dispatch.
|
|
39
|
+
#[derive(Debug, Clone)]
|
|
40
|
+
pub struct Event {
|
|
41
|
+
pub name: String,
|
|
42
|
+
pub aggregate: String,
|
|
43
|
+
pub id: String,
|
|
44
|
+
/// The dispatching command's arguments as structured JSON.
|
|
45
|
+
pub payload: json::Json,
|
|
46
|
+
/// Wall-clock timestamp resolved by the host; the kernel has no clock, to stay deterministic.
|
|
47
|
+
/// Every event from one top-level step, cascading reactions included, gets the same value.
|
|
48
|
+
/// `None` until `orchestrate` stamps it, and for callers that never supply one.
|
|
49
|
+
pub occurred_at: Option<String>,
|
|
50
|
+
/// Runtime-only saga bookkeeping, never serialized. Stamped by `orchestrate` when a saga leg
|
|
51
|
+
/// causes the event, so an unrelated saga correlating on another field cannot claim it.
|
|
52
|
+
pub correlation: Option<std::collections::HashMap<String, String>>,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Why a command dispatch was refused; each variant carries the refusal message.
|
|
56
|
+
///
|
|
57
|
+
/// Ruby's `UnknownVerb` has no variant: routing an unknown command name happens before dispatch.
|
|
58
|
+
/// `AbsentArgument` and `UnknownArgument` are raised only at the JSON boundary (`from_json`),
|
|
59
|
+
/// where the input has no static shape; typed args structs make them unconstructible afterwards.
|
|
60
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
61
|
+
pub enum Refusal {
|
|
62
|
+
GivenNotMet(String),
|
|
63
|
+
EnsuresNotMet(String),
|
|
64
|
+
InvariantViolation(String),
|
|
65
|
+
LifecycleRefused(String),
|
|
66
|
+
AlreadyExists(String),
|
|
67
|
+
NotFound(String),
|
|
68
|
+
TypeMismatch(String),
|
|
69
|
+
AbsentArgument(String),
|
|
70
|
+
UnknownArgument(String),
|
|
71
|
+
Unauthorized(String),
|
|
72
|
+
Fault(String),
|
|
73
|
+
/// `corrects` against a record that never emitted the corrected event.
|
|
74
|
+
NothingToCorrect(String),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
impl Refusal {
|
|
78
|
+
/// The refusal class name, spelled as Ruby raises it; the semantics corpus compares it.
|
|
79
|
+
pub fn kind(&self) -> &'static str {
|
|
80
|
+
match self {
|
|
81
|
+
Refusal::GivenNotMet(_) => "GivenNotMet",
|
|
82
|
+
Refusal::EnsuresNotMet(_) => "EnsuresNotMet",
|
|
83
|
+
Refusal::InvariantViolation(_) => "InvariantViolation",
|
|
84
|
+
Refusal::LifecycleRefused(_) => "LifecycleRefused",
|
|
85
|
+
Refusal::AlreadyExists(_) => "AlreadyExists",
|
|
86
|
+
Refusal::NotFound(_) => "NotFound",
|
|
87
|
+
Refusal::TypeMismatch(_) => "TypeMismatch",
|
|
88
|
+
Refusal::AbsentArgument(_) => "AbsentArgument",
|
|
89
|
+
Refusal::UnknownArgument(_) => "UnknownArgument",
|
|
90
|
+
Refusal::Unauthorized(_) => "Unauthorized",
|
|
91
|
+
Refusal::Fault(_) => "Fault",
|
|
92
|
+
Refusal::NothingToCorrect(_) => "NothingToCorrect",
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
impl std::fmt::Display for Refusal {
|
|
98
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
99
|
+
match self {
|
|
100
|
+
Refusal::GivenNotMet(msg)
|
|
101
|
+
| Refusal::EnsuresNotMet(msg)
|
|
102
|
+
| Refusal::InvariantViolation(msg)
|
|
103
|
+
| Refusal::LifecycleRefused(msg)
|
|
104
|
+
| Refusal::AlreadyExists(msg)
|
|
105
|
+
| Refusal::NotFound(msg)
|
|
106
|
+
| Refusal::TypeMismatch(msg)
|
|
107
|
+
| Refusal::AbsentArgument(msg)
|
|
108
|
+
| Refusal::UnknownArgument(msg)
|
|
109
|
+
| Refusal::Unauthorized(msg)
|
|
110
|
+
| Refusal::Fault(msg)
|
|
111
|
+
| Refusal::NothingToCorrect(msg) => write!(f, "{msg}"),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/// The saved record plus the events it raised, or a refusal that leaves state untouched.
|
|
117
|
+
pub type DispatchResult<T> = Result<(T, Vec<Event>), Refusal>;
|
|
118
|
+
|
|
119
|
+
/// One aggregate record saved by a dispatch, reported so a host can journal per aggregate.
|
|
120
|
+
///
|
|
121
|
+
/// `operation` is always `"save"`: no dispatch path deletes.
|
|
122
|
+
#[derive(Debug, Clone)]
|
|
123
|
+
pub struct MutationRecord {
|
|
124
|
+
pub aggregate: String,
|
|
125
|
+
pub id: String,
|
|
126
|
+
pub operation: &'static str,
|
|
127
|
+
pub state: Json,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/// Lets `dispatch`/`dispatch_entity`, generic over the record type, call a record's `to_json()`.
|
|
131
|
+
pub trait ToJson {
|
|
132
|
+
fn to_json(&self) -> Json;
|
|
133
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
//! Interprets declared `query "Name" do ... end` blocks compiled to static `QueryDef` rows.
|
|
2
|
+
//! A query outside the compiled subset has no row and is refused by the string-form step.
|
|
3
|
+
|
|
4
|
+
use super::refusal_wording::UnauthorizedTenantRequiredArgs;
|
|
5
|
+
use super::{query_comparators::QueryComparator, query_ordering, repository, AggregateScan, Json, Refusal};
|
|
6
|
+
|
|
7
|
+
/// One declared query, compiled.
|
|
8
|
+
///
|
|
9
|
+
/// `verb` is the qualified "Domain::Aggregate.QueryName"; `aggregate` is the bare
|
|
10
|
+
/// "Domain::Aggregate" that `AggregateScan::scan` expects.
|
|
11
|
+
#[derive(Debug, Clone, Copy)]
|
|
12
|
+
pub struct QueryDef {
|
|
13
|
+
pub verb: &'static str,
|
|
14
|
+
pub aggregate: &'static str,
|
|
15
|
+
pub conditions: &'static [QueryCondition],
|
|
16
|
+
/// `where` clauses that hop through a reference (`customer/status`), after `conditions`.
|
|
17
|
+
pub reference_hop_conditions: &'static [super::read_model::ReferenceHopCondition],
|
|
18
|
+
pub order_by: Option<query_ordering::OrderBy>,
|
|
19
|
+
pub offset: Option<query_ordering::Offset>,
|
|
20
|
+
pub limit: Option<query_ordering::Limit>,
|
|
21
|
+
/// `None` when no `authorize` is declared, or it declares no `tenant:`.
|
|
22
|
+
pub authorization: Option<TenantAuth>,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// `authorize policy, tenant: :field`, mirroring `Runtime::TenantScope.apply`.
|
|
26
|
+
///
|
|
27
|
+
/// A missing tenant arg refuses `Unauthorized`; otherwise `field == args[tenant]` is ANDed
|
|
28
|
+
/// onto the query's conditions at codegen time. `tenant_field` is both the field and the arg key.
|
|
29
|
+
#[derive(Debug, Clone, Copy)]
|
|
30
|
+
pub struct TenantAuth {
|
|
31
|
+
/// The bare declared query name (not the qualified verb), used as `{query}` in the refusal.
|
|
32
|
+
pub query_name: &'static str,
|
|
33
|
+
pub tenant_field: &'static str,
|
|
34
|
+
/// The `authorize` policy, carried but not enforced: Ruby never reads it either, and enforcing
|
|
35
|
+
/// it here would make Rust refuse queries Ruby answers.
|
|
36
|
+
pub policy: &'static str,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// One where clause, compiled; `value` is what a declared query adds over the ad hoc filter.
|
|
40
|
+
#[derive(Debug, Clone, Copy)]
|
|
41
|
+
pub struct QueryCondition {
|
|
42
|
+
pub field: &'static str,
|
|
43
|
+
pub comparator: QueryComparator,
|
|
44
|
+
pub value: QueryConditionValue,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[derive(Debug, Clone, Copy)]
|
|
48
|
+
pub enum QueryConditionValue {
|
|
49
|
+
/// A wire string baked in at codegen time. Emitted only for string-shaped fields or the
|
|
50
|
+
/// `in`/`contains` comparators, which stringify their argument in Ruby too. Never for
|
|
51
|
+
/// gt/gte/lt/lte.
|
|
52
|
+
Literal(&'static str),
|
|
53
|
+
/// A literal for a field proven numeric at codegen time. Resolves to `Json::Num`, never
|
|
54
|
+
/// `Json::Float`: it is only a comparison operand and is never serialised back out.
|
|
55
|
+
NumericLiteral(f64),
|
|
56
|
+
/// A caller-bound attribute name, read from the call's `args`; a missing key reads as
|
|
57
|
+
/// `Json::Null`, as Ruby's `resolve_query_value` does.
|
|
58
|
+
Arg(&'static str),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// Runs a declared query: chained `filter_entries` per condition (keeping id order), then
|
|
62
|
+
/// `query_ordering::apply` for `order_by`/`offset`/`limit`, as Ruby filters then orders.
|
|
63
|
+
pub fn run(store: &impl AggregateScan, def: &QueryDef, args: &Json, caller_role: Option<&str>) -> Result<Vec<(String, Json)>, Refusal> {
|
|
64
|
+
run_cross_domain(store, def, args, &[], caller_role)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// `run` with an explicit `cross_domain` search list for `NoneInState` conditions.
|
|
68
|
+
///
|
|
69
|
+
/// `caller_role` is threaded through for future tenant-policy enforcement and is not yet checked.
|
|
70
|
+
pub fn run_cross_domain(
|
|
71
|
+
store: &impl AggregateScan,
|
|
72
|
+
def: &QueryDef,
|
|
73
|
+
args: &Json,
|
|
74
|
+
cross_domain: &[(&str, &dyn AggregateScan)],
|
|
75
|
+
_caller_role: Option<&str>,
|
|
76
|
+
) -> Result<Vec<(String, Json)>, Refusal> {
|
|
77
|
+
if let Some(auth) = &def.authorization {
|
|
78
|
+
if args.get(auth.tenant_field).is_none() {
|
|
79
|
+
return Err(Refusal::Unauthorized(
|
|
80
|
+
UnauthorizedTenantRequiredArgs { query: auth.query_name, field: auth.tenant_field }.render_args(),
|
|
81
|
+
));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Reduced once per condition, as `filter_entries` reduces its operand.
|
|
86
|
+
let wants: Vec<(&QueryCondition, Json)> = def
|
|
87
|
+
.conditions
|
|
88
|
+
.iter()
|
|
89
|
+
.map(|condition| {
|
|
90
|
+
let want = match condition.value {
|
|
91
|
+
QueryConditionValue::Literal(text) => Json::Str(text.to_string()),
|
|
92
|
+
QueryConditionValue::NumericLiteral(n) => Json::Num(n, None),
|
|
93
|
+
QueryConditionValue::Arg(name) => args.get(name).cloned().unwrap_or(Json::Null),
|
|
94
|
+
};
|
|
95
|
+
(condition, super::query_comparators::comparable(&want))
|
|
96
|
+
})
|
|
97
|
+
.collect();
|
|
98
|
+
|
|
99
|
+
// Every condition is tested against the borrowed row, so only a row that passes them all is
|
|
100
|
+
// cloned. ANDing per row keeps what chaining one filter per condition kept.
|
|
101
|
+
let mut entries: Vec<(String, Json)> = Vec::new();
|
|
102
|
+
let known = store.scan_each(def.aggregate, &mut |id, record| {
|
|
103
|
+
let kept = wants
|
|
104
|
+
.iter()
|
|
105
|
+
.all(|(c, want)| repository::entry_matches(record, c.field, c.comparator, want, cross_domain));
|
|
106
|
+
if kept {
|
|
107
|
+
entries.push((id.to_string(), record.clone()));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
if !known {
|
|
111
|
+
return Err(Refusal::TypeMismatch(format!("unknown aggregate {:?}", def.aggregate)));
|
|
112
|
+
}
|
|
113
|
+
entries.sort_by(|a, b| a.0.cmp(&b.0));
|
|
114
|
+
|
|
115
|
+
entries = super::read_model::apply_reference_hops(entries, def.reference_hop_conditions, args, store)?;
|
|
116
|
+
|
|
117
|
+
Ok(query_ordering::apply(entries, def.order_by.as_ref(), def.offset.as_ref(), def.limit.as_ref(), args))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Finds a query by verb; a linear scan is enough for the small generated `QUERIES` table.
|
|
121
|
+
pub fn find<'a>(table: &'a [QueryDef], verb: &str) -> Option<&'a QueryDef> {
|
|
122
|
+
table.iter().find(|def| def.verb == verb)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/// A declared entity query (`Domain::Aggregate.Entity.Query`), compiled.
|
|
126
|
+
///
|
|
127
|
+
/// Mirrors `Runtime::QueryInterpreter#entity_rows`: filter each record's `list_field` elements,
|
|
128
|
+
/// order by parent id then identity keys, then apply offset and limit.
|
|
129
|
+
#[derive(Debug, Clone, Copy)]
|
|
130
|
+
pub struct EntityQueryDef {
|
|
131
|
+
pub verb: &'static str,
|
|
132
|
+
pub aggregate: &'static str,
|
|
133
|
+
/// The aggregate's list attribute holding this entity (`withdrawals`).
|
|
134
|
+
pub list_field: &'static str,
|
|
135
|
+
/// The key each row names its owning record under (`atm_card`).
|
|
136
|
+
pub parent_key: &'static str,
|
|
137
|
+
/// The heads of the entity's `identified_by` paths, in order.
|
|
138
|
+
pub identity_keys: &'static [&'static str],
|
|
139
|
+
pub conditions: &'static [QueryCondition],
|
|
140
|
+
pub order_by: Option<query_ordering::OrderBy>,
|
|
141
|
+
pub offset: Option<query_ordering::Offset>,
|
|
142
|
+
pub limit: Option<query_ordering::Limit>,
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
pub fn find_entity<'a>(table: &'a [EntityQueryDef], verb: &str) -> Option<&'a EntityQueryDef> {
|
|
146
|
+
table.iter().find(|def| def.verb == verb)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
pub fn run_entity(store: &impl AggregateScan, def: &EntityQueryDef, args: &Json) -> Result<Vec<Json>, Refusal> {
|
|
150
|
+
let records = store
|
|
151
|
+
.scan(def.aggregate)
|
|
152
|
+
.ok_or_else(|| Refusal::TypeMismatch(format!("unknown aggregate {:?}", def.aggregate)))?;
|
|
153
|
+
|
|
154
|
+
// One entry per element, keyed by parent id, the `(id, record)` shape `filter_entries` takes.
|
|
155
|
+
let mut entries: Vec<(String, Json)> = Vec::new();
|
|
156
|
+
for (parent_id, record) in records {
|
|
157
|
+
if let Some(elements) = record.get(def.list_field).and_then(Json::as_array) {
|
|
158
|
+
entries.extend(elements.iter().map(|element| (parent_id.clone(), element.clone())));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for condition in def.conditions {
|
|
163
|
+
let want = match condition.value {
|
|
164
|
+
QueryConditionValue::Literal(text) => Json::Str(text.to_string()),
|
|
165
|
+
QueryConditionValue::NumericLiteral(n) => Json::Num(n, None),
|
|
166
|
+
QueryConditionValue::Arg(name) => args.get(name).cloned().unwrap_or(Json::Null),
|
|
167
|
+
};
|
|
168
|
+
entries = repository::filter_entries(entries, condition.field, condition.comparator, &want);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// `query_ordering::apply` re-sorts stably by parent id, so identity order survives per parent.
|
|
172
|
+
entries.sort_by(|(parent_a, a), (parent_b, b)| parent_a.cmp(parent_b).then_with(|| identity_order(a, b, def.identity_keys)));
|
|
173
|
+
let ordered = query_ordering::apply(entries, def.order_by.as_ref(), def.offset.as_ref(), def.limit.as_ref(), args);
|
|
174
|
+
|
|
175
|
+
Ok(ordered.into_iter().map(|(parent_id, element)| row_under_parent(def.parent_key, parent_id, element)).collect())
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
fn identity_order(a: &Json, b: &Json, keys: &[&str]) -> std::cmp::Ordering {
|
|
179
|
+
keys.iter()
|
|
180
|
+
.map(|key| compare_comparable(comparable(a.get(key)), comparable(b.get(key))))
|
|
181
|
+
.find(|ordering| *ordering != std::cmp::Ordering::Equal)
|
|
182
|
+
.unwrap_or(std::cmp::Ordering::Equal)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// A single-field value object reduces to its one value.
|
|
186
|
+
fn comparable(value: Option<&Json>) -> Option<&Json> {
|
|
187
|
+
match value {
|
|
188
|
+
Some(Json::Object(fields)) if fields.len() == 1 => comparable(Some(&fields[0].1)),
|
|
189
|
+
other => other,
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
fn compare_comparable(a: Option<&Json>, b: Option<&Json>) -> std::cmp::Ordering {
|
|
194
|
+
use std::cmp::Ordering;
|
|
195
|
+
match (a, b) {
|
|
196
|
+
(Some(Json::Num(x, _)), Some(Json::Num(y, _))) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
|
|
197
|
+
(Some(Json::Str(x)), Some(Json::Str(y))) => x.cmp(y),
|
|
198
|
+
(None | Some(Json::Null), None | Some(Json::Null)) => Ordering::Equal,
|
|
199
|
+
(None | Some(Json::Null), _) => Ordering::Less,
|
|
200
|
+
(_, None | Some(Json::Null)) => Ordering::Greater,
|
|
201
|
+
_ => Ordering::Equal,
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/// `{ parent_key => record.id }.merge(element)`: the parent key leads, and an element field of
|
|
206
|
+
/// the same name wins its value.
|
|
207
|
+
fn row_under_parent(parent_key: &str, parent_id: String, element: Json) -> Json {
|
|
208
|
+
let mut fields = vec![(parent_key.to_string(), Json::Str(parent_id))];
|
|
209
|
+
if let Json::Object(pairs) = element {
|
|
210
|
+
for (key, value) in pairs {
|
|
211
|
+
if key == parent_key {
|
|
212
|
+
fields[0].1 = value;
|
|
213
|
+
} else {
|
|
214
|
+
fields.push((key, value));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
Json::Object(fields)
|
|
219
|
+
}
|