hecks 2.8.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 +50 -7
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +32 -100
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +10 -32
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres/shared_connection.rb +180 -0
- data/lib/hecks/adapters/driven/postgres.rb +149 -136
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +48 -38
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +76 -62
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- 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/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +56 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +39 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +14 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +11 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +25 -31
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +16 -50
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +49 -67
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +16 -69
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +96 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +63 -169
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +3 -1
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +42 -89
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +148 -197
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +38 -43
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +46 -111
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +39 -661
- data/lib/hecks/bluebook/hexagon.rb +18 -33
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +25 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +53 -267
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -306
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +246 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +87 -352
- data/lib/hecks/bluebook/model_check/client_profile.rb +6 -21
- data/lib/hecks/bluebook/model_check.rb +122 -596
- data/lib/hecks/bluebook/pattern_subset.rb +49 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +27 -51
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +9 -99
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +4 -23
- 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 +5 -23
- 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 +4 -6
- data/lib/hecks/cli/mcp.rb +5 -6
- data/lib/hecks/cli/model_check.rb +24 -88
- data/lib/hecks/cli/pattern_cases.rb +47 -0
- data/lib/hecks/cli/present.rb +39 -0
- data/lib/hecks/cli/project_cli.rb +223 -64
- data/lib/hecks/cli/project_diagrams.rb +4 -10
- 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 +15 -61
- 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 +7 -19
- data/lib/hecks/cli/spec_example.rb +27 -0
- data/lib/hecks/cli/statements.rb +32 -0
- data/lib/hecks/cli/stores.rb +4 -6
- data/lib/hecks/cli/stress_concurrency_specs.rb +177 -0
- data/lib/hecks/cli.rb +216 -73
- data/lib/hecks/codemod.rb +324 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/corpus.rb +423 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +617 -158
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +41 -11
- 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 +14 -34
- data/lib/hecks/doors/handle.rb +206 -0
- data/lib/hecks/doors/json_door.rb +132 -0
- data/lib/hecks/doors/launcher_options.rb +129 -0
- data/lib/hecks/{cli → doors}/mcp_door.rb +25 -120
- data/lib/hecks/doors/mcp_door_scope.rb +97 -0
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +162 -0
- data/lib/hecks/doors/ruby_door/chapter.rb +72 -0
- data/lib/hecks/doors/ruby_door.rb +77 -0
- data/lib/hecks/doors.rb +61 -0
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +5 -72
- data/lib/hecks/engine_agreement.rb +156 -0
- data/lib/hecks/facade.rb +23 -51
- data/lib/hecks/forms/app.rb +21 -76
- data/lib/hecks/forms/banking_presentation.rb +50 -0
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +31 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- 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/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- 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/ir.rb +14 -83
- data/lib/hecks/kernel_capabilities.rb +194 -0
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +26 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +19 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +83 -198
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +14 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +26 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/persistence_legacy_fixture/regenerate.rb +161 -0
- data/lib/hecks/persistence_legacy_fixture.rb +113 -0
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +61 -64
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +44 -144
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +20 -85
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +6 -25
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +18 -24
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +19 -76
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +43 -127
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +131 -555
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- 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 +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +50 -47
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +18 -124
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +1 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +47 -108
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projection_files.rb +285 -0
- data/lib/hecks/projections/bootstrap_table.rb +12 -44
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +40 -207
- data/lib/hecks/projections/deploy/lambda.rb +191 -708
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +3 -59
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- 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/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +48 -147
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +22 -584
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +16 -43
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +13 -32
- data/lib/hecks/projections/reference.rb +4 -18
- data/lib/hecks/projections/rust_vocabulary.rb +13 -251
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +154 -400
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +42 -134
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- 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/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +5 -8
- data/lib/hecks/release/runner/clean_tree.rb +42 -0
- data/lib/hecks/release/runner/commands.rb +2 -4
- data/lib/hecks/release/runner/console.rb +6 -0
- data/lib/hecks/release/runner/gem_publisher.rb +15 -9
- data/lib/hecks/release/runner/npm_publisher.rb +9 -53
- data/lib/hecks/release/runner/preflight.rb +29 -20
- data/lib/hecks/release/runner/published.rb +8 -26
- data/lib/hecks/release/runner/tagger.rb +31 -23
- data/lib/hecks/release/runner.rb +60 -38
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +15 -39
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/adapter_lookup.rb +124 -0
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +27 -160
- data/lib/hecks/runtime/command_interpreter.rb +124 -306
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +50 -104
- data/lib/hecks/runtime/dispatcher.rb +134 -243
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +64 -219
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +14 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +109 -194
- data/lib/hecks/runtime/outbox.rb +39 -262
- data/lib/hecks/runtime/policy_interpreter.rb +43 -202
- data/lib/hecks/runtime/port_operation_interpreter.rb +28 -97
- data/lib/hecks/runtime/query_interpreter.rb +122 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/reaction_outcome.rb +89 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +117 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +28 -87
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +213 -227
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +156 -247
- data/lib/hecks/runtime/remote_dispatcher.rb +68 -154
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +162 -760
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value/field_checks.rb +172 -0
- data/lib/hecks/runtime/value.rb +15 -84
- data/lib/hecks/runtime.rb +35 -69
- 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 +155 -582
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +10 -34
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- 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/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks.rb +52 -90
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- 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 +478 -12
- data/lib/hecks/facade/cli_door.rb +0 -178
- data/lib/hecks/facade/cli_runner.rb +0 -271
- data/lib/hecks/facade/handle.rb +0 -293
- data/lib/hecks/facade/json_door.rb +0 -247
- data/lib/hecks/facade/surface/aggregate_door.rb +0 -222
- data/lib/hecks/facade/surface/chapter.rb +0 -116
- data/lib/hecks/facade/surface.rb +0 -61
- data/lib/hecks/mcp_door_scope.rb +0 -176
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Adapters
|
|
3
3
|
class Sqlite
|
|
4
|
-
#
|
|
5
|
-
# beside it, the shared events table, and the two ALTERs that let an
|
|
6
|
-
# older database grow the columns newer code writes.
|
|
4
|
+
# DDL for one aggregate: head table, entry table, events, saga and outbox tables.
|
|
7
5
|
module SchemaBuilder
|
|
8
6
|
private
|
|
9
7
|
|
|
@@ -12,15 +10,7 @@ module Hecks
|
|
|
12
10
|
@db.execute(
|
|
13
11
|
"CREATE TABLE IF NOT EXISTS #{quoted_table} (id TEXT PRIMARY KEY#{', ' unless columns.empty?}#{columns.join(', ')})"
|
|
14
12
|
)
|
|
15
|
-
#
|
|
16
|
-
# `create_aggregate_table!` is the one piece of DDL `D1`
|
|
17
|
-
# already calls verbatim through this same shared module
|
|
18
|
-
# (`d1.rb`'s own header: "reuses Sqlite::SchemaBuilder and
|
|
19
|
-
# Sqlite::Codec unchanged"). Folding index creation in here,
|
|
20
|
-
# right after the table it indexes exists, is what makes D1's
|
|
21
|
-
# own automatic indexing a real, running thing rather than an
|
|
22
|
-
# aspiration that only Sqlite ever calls — no D1-specific
|
|
23
|
-
# wiring needed, because D1 never has to know this exists.
|
|
13
|
+
# Called here so D1, which reuses this module, gets automatic indexing too.
|
|
24
14
|
ensure_indexes!
|
|
25
15
|
end
|
|
26
16
|
|
|
@@ -35,6 +25,10 @@ module Hecks
|
|
|
35
25
|
occurred_at TEXT
|
|
36
26
|
)
|
|
37
27
|
SQL
|
|
28
|
+
# Backs #events_for's per-record lookup (a `corrects` command's history read).
|
|
29
|
+
@db.execute(
|
|
30
|
+
"CREATE INDEX IF NOT EXISTS hecks_events_aggregate_id_idx ON events (aggregate, aggregate_id)"
|
|
31
|
+
)
|
|
38
32
|
end
|
|
39
33
|
|
|
40
34
|
def create_entry_table!
|
|
@@ -63,14 +57,8 @@ module Hecks
|
|
|
63
57
|
@db.execute("ALTER TABLE #{quoted_entry_table} ADD COLUMN mirrors TEXT")
|
|
64
58
|
end
|
|
65
59
|
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# its own `events`-table DDL (`d1.rb`), gets this for free too.
|
|
69
|
-
# `domain` stays an explicit column even though SQLite has no
|
|
70
|
-
# schema/namespace concept the way Postgres does — matches
|
|
71
|
-
# `hecks_saga_instances`' own Postgres shape (§3) and covers the
|
|
72
|
-
# (uncommon but real) case of a domain explicitly sharing one
|
|
73
|
-
# `database` file/D1 database with another.
|
|
60
|
+
# Shared with D1, which includes this module. `domain` is an explicit column so
|
|
61
|
+
# two domains can share one database file.
|
|
74
62
|
def create_saga_table!
|
|
75
63
|
@db.execute(<<~SQL)
|
|
76
64
|
CREATE TABLE IF NOT EXISTS hecks_saga_instances (
|
|
@@ -83,14 +71,8 @@ module Hecks
|
|
|
83
71
|
PRIMARY KEY (domain, process_manager, correlation)
|
|
84
72
|
)
|
|
85
73
|
SQL
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
# — the same reason Postgres's own `create_saga_table!` needs
|
|
89
|
-
# its own `ADD COLUMN IF NOT EXISTS`. SQLite/D1's own `ALTER
|
|
90
|
-
# TABLE ... ADD COLUMN` has no `IF NOT EXISTS` guard on every
|
|
91
|
-
# version this adapter supports, so a duplicate-column error
|
|
92
|
-
# is caught and treated as "already there" rather than relied
|
|
93
|
-
# on to never happen.
|
|
74
|
+
# The table may predate this column, and SQLite lacks ADD COLUMN IF NOT EXISTS,
|
|
75
|
+
# so a duplicate-column error is treated as already applied.
|
|
94
76
|
@db.execute("ALTER TABLE hecks_saga_instances ADD COLUMN completed_compensations TEXT NOT NULL DEFAULT '[]'")
|
|
95
77
|
rescue StandardError => e
|
|
96
78
|
raise unless e.message.include?("duplicate column name")
|
|
@@ -118,27 +100,36 @@ module Hecks
|
|
|
118
100
|
@db.execute("CREATE INDEX IF NOT EXISTS idx_hecks_outbox_status ON hecks_outbox(aggregate, status)")
|
|
119
101
|
end
|
|
120
102
|
|
|
103
|
+
# One row per aggregate table, naming the highest entry `sequence` this table has
|
|
104
|
+
# already had projected into it — shared across every aggregate in the database file,
|
|
105
|
+
# since it is keyed by table name rather than declared per aggregate.
|
|
106
|
+
def create_checkpoint_table!
|
|
107
|
+
@db.execute(<<~SQL)
|
|
108
|
+
CREATE TABLE IF NOT EXISTS hecks_checkpoints (
|
|
109
|
+
aggregate_table TEXT PRIMARY KEY,
|
|
110
|
+
last_sequence INTEGER NOT NULL DEFAULT 0
|
|
111
|
+
)
|
|
112
|
+
SQL
|
|
113
|
+
ensure_checkpoint_compacted_through_column!
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# CREATE TABLE IF NOT EXISTS never adds a column to an existing table, so this healing
|
|
117
|
+
# step matches ensure_entry_operation_column!'s own for the entry table.
|
|
118
|
+
def ensure_checkpoint_compacted_through_column!
|
|
119
|
+
columns = @db.execute("PRAGMA table_info(hecks_checkpoints)").map { |row| row["name"] }
|
|
120
|
+
return if columns.include?("compacted_through")
|
|
121
|
+
|
|
122
|
+
@db.execute("ALTER TABLE hecks_checkpoints ADD COLUMN compacted_through INTEGER NOT NULL DEFAULT 0")
|
|
123
|
+
end
|
|
124
|
+
|
|
121
125
|
def sql_type(attr)
|
|
122
126
|
return "TEXT" if attr.list?
|
|
123
127
|
|
|
124
128
|
SQL_TYPES.fetch(attr.type, "TEXT")
|
|
125
129
|
end
|
|
126
130
|
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
# unconditionally on every boot, `CREATE INDEX IF NOT EXISTS`,
|
|
130
|
-
# the same self-healing idiom `postgres_era.rb`'s own
|
|
131
|
-
# `ensure_head_snapshot!`/`ensure_first_head!` already use) ────
|
|
132
|
-
|
|
133
|
-
# Every field a declared query ever filters or sorts on, across
|
|
134
|
-
# the aggregate's own queries and every entity's own queries —
|
|
135
|
-
# `query_surfaces` in `dsl/aggregate_builder.rb` walks the
|
|
136
|
-
# identical pair. An entity's query still runs against this
|
|
137
|
-
# table: it compiles through `query_expression`, which is
|
|
138
|
-
# `@aggregate`-scoped, not entity-scoped, so an entity's
|
|
139
|
-
# declared `where`/`order_by` is indexed here too, same as any
|
|
140
|
-
# other declared query — not skipped as "some other table's
|
|
141
|
-
# concern."
|
|
131
|
+
# Indexes every field a declared query filters or sorts on, entity queries included
|
|
132
|
+
# (they compile against this table too). Idempotent, so it is safe on every boot.
|
|
142
133
|
def ensure_indexes!
|
|
143
134
|
declared_query_fields.each { |field| ensure_index_for_field!(field) }
|
|
144
135
|
end
|
|
@@ -149,38 +140,10 @@ module Hecks
|
|
|
149
140
|
.compact.map(&:to_s).uniq
|
|
150
141
|
end
|
|
151
142
|
|
|
152
|
-
#
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
#
|
|
156
|
-
# own `quote_ident(name)` phrasing (reached by calling
|
|
157
|
-
# `query_expression` itself, not a second copy of its
|
|
158
|
-
# plain-vs-nested decision);
|
|
159
|
-
# - a non-list value-object attribute, referenced bare or
|
|
160
|
-
# through a member path (`field` or `field.member`) — an
|
|
161
|
-
# expression index over `query_expression(field)`'s own
|
|
162
|
-
# `json_extract(...)` text. Reusing the query compiler's own
|
|
163
|
-
# expression, not re-deriving the string a second way, is
|
|
164
|
-
# the whole point: a textual mismatch between the index and
|
|
165
|
-
# what a real query compiles to is invisible to SQLite's
|
|
166
|
-
# planner, and two independent copies of this logic can only
|
|
167
|
-
# drift apart over time;
|
|
168
|
-
# - a list-typed attribute — no index. SQLite's `contains`
|
|
169
|
-
# compiles to `EXISTS (SELECT 1 FROM json_each(col) WHERE
|
|
170
|
-
# ...)` (`list_contains_clause`, in `sql_query_builder.rb`)
|
|
171
|
-
# — an element-membership scan a plain index on the raw
|
|
172
|
-
# column (or even an expression index on it) does nothing to
|
|
173
|
-
# speed, since neither indexes the elements individually.
|
|
174
|
-
# SQLite has no inverted/array index short of FTS5/R-tree,
|
|
175
|
-
# and neither fits a `list_of` scalar or value-object field
|
|
176
|
-
# — well beyond this plan's scope. Left unindexed
|
|
177
|
-
# deliberately, not a silent gap.
|
|
178
|
-
#
|
|
179
|
-
# A field naming neither a real attribute nor the lifecycle
|
|
180
|
-
# field can't happen through the DSL today — `seal_query_field`
|
|
181
|
-
# (`dsl/aggregate_builder.rb`) already refuses it at parse time.
|
|
182
|
-
# This runs at adapter boot, not parse time, so it skips rather
|
|
183
|
-
# than crashes ugly if that invariant is ever violated.
|
|
143
|
+
# A scalar or lifecycle field gets a btree index; a value-object path gets an expression
|
|
144
|
+
# index over `query_expression`'s own text, so the planner matches what queries compile to.
|
|
145
|
+
# List fields are left unindexed: SQLite cannot index json_each elements.
|
|
146
|
+
# Unknown fields are skipped rather than raised, since the DSL already refuses them.
|
|
184
147
|
def ensure_index_for_field!(field)
|
|
185
148
|
name, * = field.to_s.split(".")
|
|
186
149
|
attribute = @aggregate.attribute(name)
|
|
@@ -195,15 +158,8 @@ module Hecks
|
|
|
195
158
|
)
|
|
196
159
|
end
|
|
197
160
|
|
|
198
|
-
#
|
|
199
|
-
#
|
|
200
|
-
# table the way a column name is; two different aggregates each
|
|
201
|
-
# indexing a field called "name" would collide without it. The
|
|
202
|
-
# field itself is sanitized (a dotted path's "." in particular)
|
|
203
|
-
# to stay a valid identifier while remaining visibly tied to
|
|
204
|
-
# what it indexes — collision-free across every field/path this
|
|
205
|
-
# aggregate declares, since `declared_query_fields` already
|
|
206
|
-
# de-duplicates the field strings themselves.
|
|
161
|
+
# Table-prefixed because SQLite index names are global to the database.
|
|
162
|
+
# The field is sanitized (a dotted path's ".") to stay a valid identifier.
|
|
207
163
|
def index_name(field)
|
|
208
164
|
sanitized = field.to_s.gsub(/[^a-zA-Z0-9_]/, "_")
|
|
209
165
|
"idx_#{table}_#{sanitized}"
|
|
@@ -14,10 +14,8 @@ require_relative "../../runtime/instance"
|
|
|
14
14
|
|
|
15
15
|
module Hecks
|
|
16
16
|
module Adapters
|
|
17
|
-
# The SQLite store: one table per aggregate head, an append-only entry
|
|
18
|
-
#
|
|
19
|
-
# column codec in sqlite/codec.rb, and the query compilation is the
|
|
20
|
-
# shared SqlQueryBuilder — this file supplies only SQLite's dialect.
|
|
17
|
+
# The SQLite store: one table per aggregate head, an append-only entry table beside it.
|
|
18
|
+
# Supplies SQLite's dialect to the shared SqlQueryBuilder.
|
|
21
19
|
class Sqlite
|
|
22
20
|
include SqlQueryBuilder
|
|
23
21
|
include SchemaBuilder
|
|
@@ -44,18 +42,13 @@ module Hecks
|
|
|
44
42
|
# @raise [LoadError] if the `sqlite3` gem is not installed
|
|
45
43
|
# @raise [SQLite3::Exception] if the file cannot be opened or a table cannot be created
|
|
46
44
|
def initialize(aggregate:, settings: {}, root: nil)
|
|
47
|
-
#
|
|
48
|
-
# need the gem installed. `require "hecks"` alone must not
|
|
49
|
-
# force a database client library nobody asked for.
|
|
45
|
+
# Lazy so a domain that never wires Sqlite does not need the gem installed.
|
|
50
46
|
require "sqlite3"
|
|
51
47
|
|
|
52
48
|
@aggregate = aggregate
|
|
53
49
|
@path = resolve_path(settings, root)
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
# directly-instantiated adapter (specs), same fallback shape
|
|
57
|
-
# Postgres's own @domain already uses.
|
|
58
|
-
@domain = (
|
|
50
|
+
# Scopes saga rows; falls back to the aggregate's name for a directly built adapter.
|
|
51
|
+
@domain = (
|
|
59
52
|
if settings.key?(:domain)
|
|
60
53
|
settings[:domain]
|
|
61
54
|
elsif settings.key?("domain")
|
|
@@ -68,8 +61,7 @@ module Hecks
|
|
|
68
61
|
FileUtils.mkdir_p(File.dirname(@path))
|
|
69
62
|
@db = SQLite3::Database.new(@path)
|
|
70
63
|
@db.results_as_hash = true
|
|
71
|
-
# The append is the recovery commit point
|
|
72
|
-
# explicit instead of inheriting a process-wide pragma choice.
|
|
64
|
+
# The append is the recovery commit point, so the fsync policy is explicit.
|
|
73
65
|
@db.execute("PRAGMA synchronous = FULL")
|
|
74
66
|
|
|
75
67
|
create_aggregate_table!
|
|
@@ -79,14 +71,13 @@ module Hecks
|
|
|
79
71
|
create_event_table!
|
|
80
72
|
create_saga_table!
|
|
81
73
|
create_outbox_table!
|
|
74
|
+
create_checkpoint_table!
|
|
82
75
|
end
|
|
83
76
|
|
|
84
77
|
# Runs the block inside one SQLite transaction, joining an already-open one.
|
|
85
78
|
#
|
|
86
|
-
#
|
|
87
|
-
# and
|
|
88
|
-
# save+emit pair; SQLite3 refuses a BEGIN inside a BEGIN, so the
|
|
89
|
-
# inner call joins the outer one instead. Same shape Postgres uses.
|
|
79
|
+
# Re-entrant because `atomic_put` and `Interpreting#run_dispatch_order` both open one,
|
|
80
|
+
# and SQLite3 refuses a BEGIN inside a BEGIN.
|
|
90
81
|
#
|
|
91
82
|
# @yield the writes to commit together; an exception raised inside rolls the
|
|
92
83
|
# outermost transaction back
|
|
@@ -117,9 +108,7 @@ module Hecks
|
|
|
117
108
|
|
|
118
109
|
# Lists every stored record, ordered by id unless an ordering attribute is given.
|
|
119
110
|
#
|
|
120
|
-
# order_by is a runtime value
|
|
121
|
-
# full reasoning; whitelisted the identical way before it ever
|
|
122
|
-
# reaches order_expression.
|
|
111
|
+
# order_by is a runtime value, so it is checked against the aggregate before use.
|
|
123
112
|
#
|
|
124
113
|
# @param order_by [String, Symbol, nil] attribute (or dotted value-object path) to sort
|
|
125
114
|
# by; nil orders by id alone
|
|
@@ -154,21 +143,21 @@ module Hecks
|
|
|
154
143
|
|
|
155
144
|
# Inserts one journal row, outside any transaction of its own.
|
|
156
145
|
#
|
|
146
|
+
# Stamps the row's assigned `sequence` onto `entry` in place, so a `project` call right
|
|
147
|
+
# after (same object, same transaction) can advance the checkpoint with no extra query.
|
|
148
|
+
#
|
|
157
149
|
# @param entry [Ports::Persistence::Entry] the save or delete to journal; `state` is
|
|
158
150
|
# encoded through the state codec and `mirrors` stored as JSON, or NULL when nil
|
|
159
|
-
# @return [Ports::Persistence::Entry] the same `entry`
|
|
151
|
+
# @return [Ports::Persistence::Entry] the same `entry`, `sequence` now set
|
|
160
152
|
# @raise [SQLite3::Exception] if the insert fails
|
|
161
153
|
def append(entry)
|
|
162
154
|
@db.execute(
|
|
163
155
|
"INSERT INTO #{quoted_entry_table} (aggregate_id, operation, state, mirrors) VALUES (?, ?, ?, ?)",
|
|
164
|
-
# `mirrors`
|
|
165
|
-
# mirrors hash must bind a real SQL NULL, not the four-character
|
|
166
|
-
# JSON text `"null"` (`JSON.generate(nil)`), or a future `IS NULL`
|
|
167
|
-
# check against it would never match. Same guard `postgres_era.rb`
|
|
168
|
-
# already uses for its own journal's `mirrors` column.
|
|
156
|
+
# `mirrors` is nullable: an absent hash must bind SQL NULL, not the JSON text "null".
|
|
169
157
|
[entry.id, entry.operation, JSON.generate(Ports::Persistence::StateCodec.encode(@aggregate, entry.state)),
|
|
170
158
|
entry.mirrors && JSON.generate(entry.mirrors)]
|
|
171
159
|
)
|
|
160
|
+
entry.sequence = @db.last_insert_row_id
|
|
172
161
|
entry
|
|
173
162
|
end
|
|
174
163
|
|
|
@@ -179,6 +168,7 @@ module Hecks
|
|
|
179
168
|
# for a delete, the `DELETE` statement's empty result rows
|
|
180
169
|
# @raise [SQLite3::Exception] if the statement fails
|
|
181
170
|
def project(entry)
|
|
171
|
+
advance_checkpoint!(entry.sequence) if entry.sequence
|
|
182
172
|
return @db.execute("DELETE FROM #{quoted_table} WHERE id = ?", [entry.id]) if entry.delete?
|
|
183
173
|
|
|
184
174
|
instance = Runtime::Instance.new(aggregate: @aggregate, id: entry.id, state: entry.state)
|
|
@@ -201,15 +191,65 @@ module Hecks
|
|
|
201
191
|
# @raise [SQLite3::Exception] if the statement fails
|
|
202
192
|
# @raise [JSON::ParserError] if a stored `state` or `mirrors` value is not valid JSON
|
|
203
193
|
def entries
|
|
204
|
-
|
|
205
|
-
state
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
194
|
+
entries_matching(
|
|
195
|
+
"SELECT aggregate_id, operation, state, mirrors, sequence FROM #{quoted_entry_table} ORDER BY sequence"
|
|
196
|
+
)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Reads only the journal rows past a given `sequence`, for `AppendOnly#recover!` to replay
|
|
200
|
+
# after a checkpoint instead of the whole journal.
|
|
201
|
+
#
|
|
202
|
+
# @param sequence [Integer] the highest `sequence` already projected; rows at or below it
|
|
203
|
+
# are skipped
|
|
204
|
+
# @return [Array<Ports::Persistence::Entry>] the journalled entries past `sequence`, in
|
|
205
|
+
# append order, decoded exactly as `entries` decodes them; `[]` when none are newer
|
|
206
|
+
# @raise [SQLite3::Exception] if the statement fails
|
|
207
|
+
# @raise [JSON::ParserError] if a stored `state` or `mirrors` value is not valid JSON
|
|
208
|
+
def entries_since(sequence)
|
|
209
|
+
entries_matching(
|
|
210
|
+
"SELECT aggregate_id, operation, state, mirrors, sequence FROM #{quoted_entry_table} " \
|
|
211
|
+
"WHERE sequence > ? ORDER BY sequence",
|
|
212
|
+
[sequence]
|
|
213
|
+
)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Reads the highest journal `sequence` this table has already had projected into it.
|
|
217
|
+
#
|
|
218
|
+
# @return [Integer] `0` when the table has never been checkpointed (a fresh table, or one
|
|
219
|
+
# from before this bookkeeping existed) — `entries_since(0)` then reads the whole journal,
|
|
220
|
+
# matching `entries`' own full replay
|
|
221
|
+
# @raise [SQLite3::Exception] if the statement fails
|
|
222
|
+
def checkpoint
|
|
223
|
+
@db.get_first_value("SELECT last_sequence FROM hecks_checkpoints WHERE aggregate_table = ?", [table]).to_i
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Reads the highest journal `sequence` compaction has already deleted from this table.
|
|
227
|
+
#
|
|
228
|
+
# @return [Integer] `0` when nothing has ever been compacted
|
|
229
|
+
# @raise [SQLite3::Exception] if the statement fails
|
|
230
|
+
def compacted_through
|
|
231
|
+
@db.get_first_value("SELECT compacted_through FROM hecks_checkpoints WHERE aggregate_table = ?", [table]).to_i
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Deletes every journal row at or before `through` and records it, so a `:strict`
|
|
235
|
+
# projection catch-up can tell whether it still has the history it needs.
|
|
236
|
+
#
|
|
237
|
+
# Never moves `compacted_through` backwards, and never touches the aggregate's own
|
|
238
|
+
# table — only the journal, which a `:refresh` projection rebuild never needs.
|
|
239
|
+
#
|
|
240
|
+
# @param through [Integer] the highest `sequence` to delete
|
|
241
|
+
# @return [Integer] the number of journal rows deleted
|
|
242
|
+
# @raise [SQLite3::Exception] if a statement fails
|
|
243
|
+
def compact_entries!(through:)
|
|
244
|
+
@db.execute("DELETE FROM #{quoted_entry_table} WHERE sequence <= ?", [through])
|
|
245
|
+
removed = @db.changes
|
|
246
|
+
@db.execute(
|
|
247
|
+
"INSERT INTO hecks_checkpoints (aggregate_table, compacted_through) VALUES (?, ?) " \
|
|
248
|
+
"ON CONFLICT (aggregate_table) DO UPDATE SET " \
|
|
249
|
+
"compacted_through = MAX(compacted_through, excluded.compacted_through)",
|
|
250
|
+
[table, through]
|
|
251
|
+
)
|
|
252
|
+
removed
|
|
213
253
|
end
|
|
214
254
|
|
|
215
255
|
# Deletes every row of the aggregate's table and its journal; events, saga rows and
|
|
@@ -238,9 +278,7 @@ module Hecks
|
|
|
238
278
|
|
|
239
279
|
# Stores an entry and reports whether it inserted, replaced or conflicted.
|
|
240
280
|
#
|
|
241
|
-
# The outcome lookup, journal append and snapshot replacement share one
|
|
242
|
-
# SQLite transaction. The runtime performs no preliminary find; this
|
|
243
|
-
# adapter-native operation owns both concurrency and outcome reporting.
|
|
281
|
+
# The outcome lookup, journal append and snapshot replacement share one transaction.
|
|
244
282
|
#
|
|
245
283
|
# @param entry [Ports::Persistence::Entry] the save to store
|
|
246
284
|
# @param insert_only [Boolean] when true, an existing row is left untouched
|
|
@@ -305,16 +343,33 @@ module Hecks
|
|
|
305
343
|
end
|
|
306
344
|
end
|
|
307
345
|
|
|
346
|
+
# Reads back one record's recorded events, oldest first — pushed down as a `WHERE`
|
|
347
|
+
# clause (`hecks_events_aggregate_id_idx`) instead of filtering `#events`'s whole-table
|
|
348
|
+
# read, since a `corrects` command's history lookup only ever needs this one record.
|
|
349
|
+
#
|
|
350
|
+
# @param aggregate [String] the `"domain::AggregateName"` key events are stored under
|
|
351
|
+
# @param id [String, Object] the record's identity, matched as `id.to_s`
|
|
352
|
+
# @return [Array<Runtime::Event>] the record's stored events; `[]` when it has none
|
|
353
|
+
# @raise [SQLite3::Exception] if the statement fails
|
|
354
|
+
def events_for(aggregate:, id:)
|
|
355
|
+
@db.execute(
|
|
356
|
+
"SELECT * FROM events WHERE aggregate = ? AND aggregate_id = ? ORDER BY id",
|
|
357
|
+
[aggregate, id.to_s]
|
|
358
|
+
).map do |row|
|
|
359
|
+
Runtime::Event.new(
|
|
360
|
+
name: row["name"],
|
|
361
|
+
aggregate: row["aggregate"],
|
|
362
|
+
id: row["aggregate_id"],
|
|
363
|
+
payload: JSON.parse(row["payload"], symbolize_names: true),
|
|
364
|
+
occurred_at: row["occurred_at"]
|
|
365
|
+
)
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
308
369
|
# Inserts new outbox rows as pending, skipping any whose `delivery_id` already exists.
|
|
309
370
|
#
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
# save's transaction), keyed by the aggregate's storage name so an
|
|
313
|
-
# adapter instance only ever reads back its own rows even when
|
|
314
|
-
# several aggregates share one file. `INSERT OR IGNORE` on the
|
|
315
|
-
# unique delivery_id makes a re-enqueue of the same (event,
|
|
316
|
-
# consumer) a no-op; `outbox_claim`'s `WHERE status = 'pending'`
|
|
317
|
-
# is the compare-and-set that lets exactly one relay win a row.
|
|
371
|
+
# Rows share this aggregate's database so the enqueue joins the save's transaction, and
|
|
372
|
+
# are keyed by storage name so aggregates sharing a file read back only their own.
|
|
318
373
|
#
|
|
319
374
|
# @param rows [Array<Runtime::Outbox::Row>] rows to enqueue; each accepted row has its
|
|
320
375
|
# `id` and `status` assigned in place. `row.aggregate` is stored as given
|
|
@@ -389,19 +444,6 @@ module Hecks
|
|
|
389
444
|
# Replaces one saga instance's checkpoint, keyed by domain, process manager and
|
|
390
445
|
# correlation.
|
|
391
446
|
#
|
|
392
|
-
# ── the optional saga-persistence capability (§2) — reuses the
|
|
393
|
-
# DDL every SQLite-backed aggregate table already lives beside
|
|
394
|
-
# (`create_saga_table!`, `Sqlite::SchemaBuilder`, shared with D1).
|
|
395
|
-
# SQLite's `resolve_path` defaults to one `.db` file per
|
|
396
|
-
# aggregate unless a domain shares one `database` setting across
|
|
397
|
-
# its aggregates — since saga persistence resolves through
|
|
398
|
-
# whichever adapter instance backs the domain's first aggregate
|
|
399
|
-
# (`Registry#saga_persistence`), this table ends up living inside
|
|
400
|
-
# that one aggregate's own file by default. Correct and durable
|
|
401
|
-
# either way; a domain that wants an obviously-named saga store
|
|
402
|
-
# already gets one by sharing `database` across its aggregates,
|
|
403
|
-
# the recommended, common case.
|
|
404
|
-
#
|
|
405
447
|
# @param process_manager [String, Symbol] the process manager's name
|
|
406
448
|
# @param correlation [String, Object] the instance's correlation value, stored as
|
|
407
449
|
# `correlation.to_s`
|
|
@@ -462,7 +504,31 @@ module Hecks
|
|
|
462
504
|
|
|
463
505
|
private
|
|
464
506
|
|
|
465
|
-
#
|
|
507
|
+
# Shared decode step for `entries`/`entries_since` — runs a query already selecting
|
|
508
|
+
# `aggregate_id, operation, state, mirrors, sequence` and builds one Entry per row.
|
|
509
|
+
def entries_matching(sql, binds = [])
|
|
510
|
+
@db.execute(sql, binds).map do |row|
|
|
511
|
+
state = JSON.parse(row["state"])
|
|
512
|
+
Ports::Persistence::Entry.new(
|
|
513
|
+
operation: row["operation"] || "save",
|
|
514
|
+
id: row["aggregate_id"],
|
|
515
|
+
state: Ports::Persistence::StateCodec.decode(@aggregate, state),
|
|
516
|
+
mirrors: row["mirrors"] && JSON.parse(row["mirrors"]),
|
|
517
|
+
sequence: row["sequence"].to_i
|
|
518
|
+
)
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Advances this table's checkpoint to `sequence`, never backwards — two overlapping
|
|
523
|
+
# `project` calls for out-of-order entries must not let an older sequence win.
|
|
524
|
+
def advance_checkpoint!(sequence)
|
|
525
|
+
@db.execute(
|
|
526
|
+
"INSERT INTO hecks_checkpoints (aggregate_table, last_sequence) VALUES (?, ?) " \
|
|
527
|
+
"ON CONFLICT (aggregate_table) DO UPDATE SET " \
|
|
528
|
+
"last_sequence = MAX(last_sequence, excluded.last_sequence)",
|
|
529
|
+
[table, sequence]
|
|
530
|
+
)
|
|
531
|
+
end
|
|
466
532
|
|
|
467
533
|
def outbox_row(row)
|
|
468
534
|
Runtime::Outbox::Row.new(
|
|
@@ -499,8 +565,7 @@ module Hecks
|
|
|
499
565
|
"json_extract(#{quote_ident(name)}, '#{json_path}')"
|
|
500
566
|
end
|
|
501
567
|
|
|
502
|
-
# SQLite has no bare OFFSET
|
|
503
|
-
# unbounded spelling, exactly for this case.
|
|
568
|
+
# SQLite has no bare OFFSET; LIMIT -1 is its unbounded spelling.
|
|
504
569
|
def unbounded_limit = " LIMIT -1"
|
|
505
570
|
|
|
506
571
|
def order_clause(order_by, policy)
|
|
@@ -511,8 +576,6 @@ module Hecks
|
|
|
511
576
|
@db.execute(sql, binds).map { |row| Runtime::Instance.new(aggregate: @aggregate, id: row["id"], state: decode(row)) }
|
|
512
577
|
end
|
|
513
578
|
|
|
514
|
-
# ── the rest of the dialect ─────────────────────────────────────
|
|
515
|
-
|
|
516
579
|
def quote_ident(name)
|
|
517
580
|
%("#{name.to_s.gsub('"', '""')}")
|
|
518
581
|
end
|
|
@@ -4,38 +4,22 @@ require "fileutils"
|
|
|
4
4
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Adapters
|
|
7
|
-
# The imperative half of provisioning
|
|
8
|
-
# `
|
|
9
|
-
#
|
|
10
|
-
# hexagonal reasoning `GithubChecks` already gives for its own
|
|
11
|
-
# port: real, impure, side-effecting work (writing the overlay
|
|
12
|
-
# file) belongs in an adapter, not in a bare script or a command
|
|
13
|
-
# handler. `Deploy::Tenant`'s own `asks "Provision"` operation calls
|
|
14
|
-
# `#provision` and turns whatever it returns into `TenantProvisioned`,
|
|
15
|
-
# or whatever it raises into `ProvisioningRefused` — see
|
|
16
|
-
# `PortOperationInterpreter#ask`'s own "every failure is an answer"
|
|
17
|
-
# comment for why nothing here needs its own rescue.
|
|
18
|
-
#
|
|
19
|
-
# ## What it leaves out
|
|
20
|
-
#
|
|
21
|
-
# Deliberately does not boot the target domain — that step (proving
|
|
22
|
-
# it boots for real, running the tenant_capable? gate) stays in
|
|
23
|
-
# `bin/project_tenant` itself, called after this operation answers,
|
|
24
|
-
# not nested inside it. Booting a whole domain from inside an
|
|
25
|
-
# adapter method that is itself running mid-dispatch (this port
|
|
26
|
-
# operation) is a real, separate concern from writing one file, and
|
|
27
|
-
# keeping the two apart avoids a nested `Hecks.boot` call ever
|
|
28
|
-
# running inside another boot's own dispatch call stack.
|
|
29
|
-
#
|
|
30
|
-
# ## Return shape
|
|
31
|
-
#
|
|
32
|
-
# Returns a hash shaped like `Tenancy::Tenant.Register`'s own
|
|
33
|
-
# arguments, on purpose — `tenancy.hecksagon`'s own `translates`
|
|
34
|
-
# reaction forwards an answered event's payload verbatim, so this
|
|
35
|
-
# adapter's own return value is the translation from Deploy's
|
|
36
|
-
# vocabulary into Tenancy's, decided here rather than in a mapping
|
|
37
|
-
# layer neither side could see into.
|
|
7
|
+
# The imperative half of tenant provisioning, behind `Deploy::Tenant.port
|
|
8
|
+
# "TenantProvisioning"`, for `Tenant.Provision` and for hecks deploy provision; booting the
|
|
9
|
+
# domain and its capability gate stay in hecks deploy provision.
|
|
38
10
|
class TenantProvisioner
|
|
11
|
+
# Raised when a value is outside its allow-list or the overlay path leaves `environments/`.
|
|
12
|
+
class Refused < ArgumentError; end
|
|
13
|
+
|
|
14
|
+
# Each argument's allow-list. `\A...\z` anchors: `$` would let a newline end the value early.
|
|
15
|
+
IDENTIFIER = /\A[a-z][a-z0-9_]*\z/
|
|
16
|
+
LABEL = /\A[A-Za-z0-9][A-Za-z0-9_ .:-]*\z/
|
|
17
|
+
CONSTANT = /\A[A-Z][A-Za-z0-9]*\z/
|
|
18
|
+
CONNECTION = /\A[^\x00-\x20\x7f]+\z/
|
|
19
|
+
|
|
20
|
+
RULES = { slug: IDENTIFIER, schema: IDENTIFIER, domain: LABEL, realm: LABEL,
|
|
21
|
+
database: CONNECTION, adapter: CONSTANT }.freeze
|
|
22
|
+
|
|
39
23
|
# Accepts the arguments every driven adapter is built with and keeps none of them.
|
|
40
24
|
#
|
|
41
25
|
# @param aggregate [Object, nil] unused
|
|
@@ -43,44 +27,74 @@ module Hecks
|
|
|
43
27
|
# @param root [String, nil] unused
|
|
44
28
|
def initialize(aggregate: nil, settings: {}, root: nil); end
|
|
45
29
|
|
|
46
|
-
# Writes the tenant's environment overlay
|
|
30
|
+
# Writes the tenant's environment overlay under `<directory>/environments/`.
|
|
47
31
|
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
# domain's own declared name (`domain`, e.g. "Scratch") is not a
|
|
51
|
-
# filesystem path, and File.expand_path(domain, Dir.pwd) silently
|
|
52
|
-
# resolved to the wrong directory (relative to whatever the
|
|
53
|
-
# running process' own cwd happened to be) whenever the CLI's own
|
|
54
|
-
# `--domain=` value didn't happen to equal its own directory
|
|
55
|
-
# argument by coincidence. `bin/project_tenant` passes its own
|
|
56
|
-
# real `domain_directory` local straight through.
|
|
32
|
+
# Each argument arrives bare or wrapped as `{ value: }`; the record's other fields are
|
|
33
|
+
# ignored. Values are allow-listed and written as Ruby string literals.
|
|
57
34
|
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# @param
|
|
61
|
-
# @
|
|
62
|
-
# @
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
overlay_path = File.join(domain_directory, "environments", "#{slug[:value] || slug}.world")
|
|
35
|
+
# @param slug [String, Hash] names the overlay file
|
|
36
|
+
# @param directory [String, Hash] the domain's own directory on disk (not its declared name)
|
|
37
|
+
# @param adapter [String, Hash, nil] the adapter the overlay binds; PostgresEra when none
|
|
38
|
+
# @return [Hash{Symbol => Object}] `:slug`, `:domain`, `:realm`, `:schema` and `:output`
|
|
39
|
+
# @raise [Refused] when a value is outside its allow-list or the path leaves `environments/`
|
|
40
|
+
def write_overlay(slug:, domain:, realm:, schema:, database:, directory:, adapter: nil, **)
|
|
41
|
+
values = { slug: unwrap(slug), domain: unwrap(domain), realm: unwrap(realm),
|
|
42
|
+
schema: unwrap(schema), database: unwrap(database),
|
|
43
|
+
adapter: adapter_name(adapter) }
|
|
44
|
+
values.each { |name, value| check(name, value) }
|
|
45
|
+
|
|
46
|
+
overlay_path = overlay_path_for(unwrap(directory), values[:slug])
|
|
47
|
+
refuse_missing_directory(unwrap(directory))
|
|
72
48
|
FileUtils.mkdir_p(File.dirname(overlay_path))
|
|
73
|
-
File.write(overlay_path,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
49
|
+
File.write(overlay_path, render(values))
|
|
50
|
+
|
|
51
|
+
{ slug: slug, domain: domain, realm: realm, schema: schema,
|
|
52
|
+
output: { value: "wrote #{overlay_path}\n" } }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# A tenant overlay belongs to a domain that exists: a missing directory is a mistyped path,
|
|
58
|
+
# not a place to create.
|
|
59
|
+
def refuse_missing_directory(directory)
|
|
60
|
+
return if File.directory?(directory.to_s)
|
|
61
|
+
|
|
62
|
+
raise Refused, "directory #{directory.to_s.inspect} is not an existing domain directory"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def render(values)
|
|
66
|
+
<<~WORLD
|
|
67
|
+
Hecks.world #{values[:domain].inspect} do
|
|
68
|
+
realm #{values[:realm].inspect}
|
|
69
|
+
persisted_by(#{values[:adapter].inspect}) do
|
|
70
|
+
database #{values[:database].inspect}
|
|
71
|
+
schema #{values[:schema].inspect}
|
|
79
72
|
end
|
|
80
73
|
end
|
|
81
74
|
WORLD
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def unwrap(argument) = argument.is_a?(Hash) ? argument[:value] : argument
|
|
78
|
+
|
|
79
|
+
def check(name, value)
|
|
80
|
+
return if value.is_a?(String) && RULES.fetch(name).match?(value)
|
|
81
|
+
|
|
82
|
+
raise Refused, "#{name} #{value.inspect} is not an accepted #{name}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def overlay_path_for(directory, slug)
|
|
86
|
+
raise Refused, "directory is required" unless directory.is_a?(String) && !directory.empty?
|
|
87
|
+
|
|
88
|
+
environments = File.join(File.expand_path(directory), "environments")
|
|
89
|
+
path = File.expand_path("#{slug}.world", environments)
|
|
90
|
+
return path if path.start_with?("#{environments}#{File::SEPARATOR}")
|
|
91
|
+
|
|
92
|
+
raise Refused, "overlay path #{path} is outside #{environments}"
|
|
93
|
+
end
|
|
82
94
|
|
|
83
|
-
|
|
95
|
+
def adapter_name(adapter)
|
|
96
|
+
name = unwrap(adapter)
|
|
97
|
+
name.nil? || name.to_s.empty? ? "PostgresEra" : name
|
|
84
98
|
end
|
|
85
99
|
end
|
|
86
100
|
end
|