hecks 2.7.0 → 2.9.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 +11 -0
- 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 +18 -99
- 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 +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- 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 +15 -37
- 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/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 +54 -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 +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -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 +38 -26
- 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 +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- 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 +17 -65
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- 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 +12 -87
- 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 +26 -189
- 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 +11 -42
- 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 +19 -106
- 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 +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- 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 +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- data/lib/hecks/bluebook/pattern_subset.rb +11 -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 +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- 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 +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- 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 +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- 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 +30 -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/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- 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 +15 -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 +12 -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 +98 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -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_door_scope.rb +95 -0
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- 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 +62 -65
- 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 +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- 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 +18 -75
- 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 +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- 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 +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
- 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.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 +23 -103
- 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/projections/bootstrap_table.rb +11 -43
- 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 +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +151 -679
- 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/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 +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- 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 +20 -582
- 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 +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +9 -16
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- 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 +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +46 -138
- 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/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 +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- 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 +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +125 -307
- 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 +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +65 -220
- 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 +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +139 -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 +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +103 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- 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 +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +146 -567
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- 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/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- 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
- metadata +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
|
@@ -6,7 +6,12 @@ module Hecks
|
|
|
6
6
|
module Persistence
|
|
7
7
|
# `mirrors` is durable replication intent. It is part of the same
|
|
8
8
|
# append as the authoritative state, never a second outbox store.
|
|
9
|
-
|
|
9
|
+
#
|
|
10
|
+
# `sequence` is the journal position an adapter assigned this entry
|
|
11
|
+
# (set by `append` on a live write, or by `entries`/`entries_since` on
|
|
12
|
+
# a replayed one); nil for an adapter that assigns none. It rides
|
|
13
|
+
# along so `project` can advance a checkpoint without a second lookup.
|
|
14
|
+
Entry = Struct.new(:operation, :id, :state, :mirrors, :sequence, keyword_init: true) do
|
|
10
15
|
# Tells a projecting adapter that this entry writes a record.
|
|
11
16
|
#
|
|
12
17
|
# @return [Boolean] true when `operation` is the String `"save"`
|
|
@@ -18,9 +23,8 @@ module Hecks
|
|
|
18
23
|
def delete? = operation == "delete"
|
|
19
24
|
end
|
|
20
25
|
|
|
21
|
-
# Makes append-before-projection a port invariant
|
|
22
|
-
#
|
|
23
|
-
# entry stream and materialize current state from it.
|
|
26
|
+
# Makes append-before-projection a port invariant: adapters keep their own durable
|
|
27
|
+
# format, but must accept the same entry stream and materialize state from it.
|
|
24
28
|
class AppendOnly
|
|
25
29
|
attr_reader :adapter
|
|
26
30
|
|
|
@@ -71,6 +75,17 @@ module Hecks
|
|
|
71
75
|
# undecoded (`CodecBoundary.check_entries!`)
|
|
72
76
|
def entries = @adapter.entries
|
|
73
77
|
|
|
78
|
+
# Reads how far this repository's journal has been compacted, when the adapter tracks
|
|
79
|
+
# that (Postgres, Sqlite).
|
|
80
|
+
#
|
|
81
|
+
# @return [Integer] the highest journal `sequence` already deleted by compaction; 0 for
|
|
82
|
+
# an adapter that tracks no such thing, or nothing compacted yet
|
|
83
|
+
def compacted_through
|
|
84
|
+
return 0 unless @adapter.respond_to?(:compacted_through)
|
|
85
|
+
|
|
86
|
+
@adapter.compacted_through
|
|
87
|
+
end
|
|
88
|
+
|
|
74
89
|
# Lists the optional persistence behaviours the adapter advertises.
|
|
75
90
|
#
|
|
76
91
|
# @return [Array<Symbol>] frozen capability names such as `:atomic_put`,
|
|
@@ -96,12 +111,8 @@ module Hecks
|
|
|
96
111
|
|
|
97
112
|
# Reads the events the adapter has durably recorded.
|
|
98
113
|
#
|
|
99
|
-
#
|
|
100
|
-
#
|
|
101
|
-
# `@adapter` while `@adapter` is still nil (class-body time,
|
|
102
|
-
# before `initialize` ever runs) and silently skips defining the
|
|
103
|
-
# method at all. Found live: nothing in this codebase called
|
|
104
|
-
# `AppendOnly#events` before Memory got a `reset!` test that did.
|
|
114
|
+
# Written as an `if` guard, not a trailing modifier, which would evaluate against
|
|
115
|
+
# `@adapter` before `initialize` runs and silently skip defining the method.
|
|
105
116
|
#
|
|
106
117
|
# @return [Array<Runtime::Event>, nil] recorded events, oldest first; nil when the
|
|
107
118
|
# adapter keeps no event log
|
|
@@ -109,15 +120,39 @@ module Hecks
|
|
|
109
120
|
@adapter.events if @adapter.respond_to?(:events)
|
|
110
121
|
end
|
|
111
122
|
|
|
112
|
-
#
|
|
123
|
+
# Reads back one record's durably recorded events, oldest first, scoped by the
|
|
124
|
+
# adapter itself when it can (a SQL adapter pushes this down as a `WHERE` clause
|
|
125
|
+
# instead of `#events`'s whole-table read); falls back to filtering `#events` for
|
|
126
|
+
# an adapter with no scoped lookup of its own.
|
|
113
127
|
#
|
|
114
|
-
#
|
|
115
|
-
#
|
|
128
|
+
# @param aggregate [String] the `"domain::AggregateName"` key events are stored under
|
|
129
|
+
# @param id [String, Object] the record's identity, matched as `id.to_s`
|
|
130
|
+
# @return [Array<Runtime::Event>, nil] the record's events; nil when the adapter
|
|
131
|
+
# keeps no event log at all
|
|
132
|
+
def events_for(aggregate:, id:)
|
|
133
|
+
return @adapter.events_for(aggregate: aggregate, id: id) if @adapter.respond_to?(:events_for)
|
|
134
|
+
|
|
135
|
+
events&.select { |event| event.aggregate == aggregate && event.id.to_s == id.to_s }
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Replays unprojected journal entries through `project` to rebuild the projected records.
|
|
139
|
+
#
|
|
140
|
+
# An append is durable before a projection is attempted; replaying restores a
|
|
141
|
+
# snapshot/table after a crash in that small window. Postgres and Sqlite keep their
|
|
142
|
+
# projected table in the same transaction as the journal append, so they never fall
|
|
143
|
+
# behind it — each tracks how far it has replayed (`checkpoint`) and only re-walks
|
|
144
|
+
# entries past that point (`entries_since`), bounded by activity since the last boot,
|
|
145
|
+
# not total history. An adapter without that pair (Memory, Heki, D1, PostgresEra,
|
|
146
|
+
# RemoteRuntime) is unaffected: it still replays every entry.
|
|
116
147
|
#
|
|
117
148
|
# @return [Persistence::AppendOnly] self, so a factory can build and recover in one
|
|
118
149
|
# expression
|
|
119
150
|
def recover!
|
|
120
|
-
|
|
151
|
+
if @adapter.respond_to?(:checkpoint) && @adapter.respond_to?(:entries_since)
|
|
152
|
+
@adapter.entries_since(@adapter.checkpoint).each { |entry| project(entry) }
|
|
153
|
+
else
|
|
154
|
+
entries.each { |entry| project(entry) }
|
|
155
|
+
end
|
|
121
156
|
self
|
|
122
157
|
end
|
|
123
158
|
|
|
@@ -142,29 +177,15 @@ module Hecks
|
|
|
142
177
|
|
|
143
178
|
# Journals an instance's state, projects it, and reports how the write landed.
|
|
144
179
|
#
|
|
145
|
-
# Returns an
|
|
146
|
-
#
|
|
147
|
-
# `RebuildSweep#refresh`) reads it that way.
|
|
148
|
-
#
|
|
149
|
-
# `expected_version:` requests optimistic-concurrency CAS — commit
|
|
150
|
-
# only if the stored record's version still matches what this
|
|
151
|
-
# instance was read at. It is `nil` both when a caller explicitly
|
|
152
|
-
# doesn't want CAS (`RebuildSweep#refresh`'s own projection-field
|
|
153
|
-
# touch-up, which has no `given` to protect) and when the instance
|
|
154
|
-
# is brand new (never read from storage, so `instance.version` is
|
|
155
|
-
# nil) — both cases fall through to the plain, unconditional
|
|
156
|
-
# `project(entry)` below, byte-for-byte today's behavior. Only an
|
|
157
|
-
# adapter that both receives a non-nil `expected_version` and
|
|
158
|
-
# declares `:optimistic_concurrency` gets CAS treatment; every
|
|
159
|
-
# other adapter/call site is unaffected.
|
|
180
|
+
# Returns an Outcome, not a bare Instance, so callers can check `:saved` vs `:stale`;
|
|
181
|
+
# `expected_version:` only triggers CAS on an adapter declaring `:optimistic_concurrency`.
|
|
160
182
|
#
|
|
161
183
|
# @param instance [Runtime::Instance] the record to persist; its `state` is shallow
|
|
162
184
|
# copied into the entry
|
|
163
185
|
# @param expected_version [Integer, nil] the version the instance was read at, or nil
|
|
164
186
|
# for an unconditional write
|
|
165
|
-
# @return [Persistence::Outcome] status `:saved` or `:stale`;
|
|
166
|
-
#
|
|
167
|
-
# it is the instance passed in. The entry is journaled even when the result is `:stale`
|
|
187
|
+
# @return [Persistence::Outcome] status `:saved` or `:stale`; `instance` is the
|
|
188
|
+
# adapter's projected result on `:saved`, else the instance passed in
|
|
168
189
|
# @raise [Runtime::WiringError] when the adapter's `append` or `project` refuses
|
|
169
190
|
def save(instance, expected_version: nil)
|
|
170
191
|
entry = Entry.new(operation: "save", id: instance.id.to_s, state: instance.state.dup)
|
|
@@ -218,18 +239,7 @@ module Hecks
|
|
|
218
239
|
|
|
219
240
|
# Records one emitted event durably, when the adapter keeps an event log.
|
|
220
241
|
#
|
|
221
|
-
#
|
|
222
|
-
# `events` above, and it bit for real here: this guard evaluated
|
|
223
|
-
# against `@adapter` at class-body time (nil, always false), so
|
|
224
|
-
# `record_event` was never defined at all. `emission.rb`'s own
|
|
225
|
-
# `repository.record_event(event) if repository.respond_to?(:record_event)`
|
|
226
|
-
# therefore never fired for any adapter, ever — every declared
|
|
227
|
-
# `emits` was computed and reported in `registry.event_log` (an
|
|
228
|
-
# in-process array, gone at exit) but never durably recorded.
|
|
229
|
-
# Caught because a live tail of a domain's own persisted events
|
|
230
|
-
# found nothing to tail. `sqlite_spec.rb`/`postgres_spec.rb`/
|
|
231
|
-
# `postgres_era_spec.rb` all call `adapter.record_event` directly,
|
|
232
|
-
# bypassing this wrapper — which is exactly why no spec noticed.
|
|
242
|
+
# Written as an `if` guard, not a trailing modifier — same reason as `events` above.
|
|
233
243
|
#
|
|
234
244
|
# @param event [Runtime::Event] the event to record
|
|
235
245
|
# @return [Object, nil] adapter-defined write result (an Array for Memory, a driver
|
|
@@ -240,17 +250,9 @@ module Hecks
|
|
|
240
250
|
|
|
241
251
|
# Runs the block inside the adapter's transaction, or plainly when it has none.
|
|
242
252
|
#
|
|
243
|
-
# One
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
# (Sqlite, Postgres) commits the aggregate row, its journal
|
|
247
|
-
# entry, and the outbox rows together or not at all. An adapter
|
|
248
|
-
# without one just yields: Memory has nothing to roll back, and
|
|
249
|
-
# a file adapter's own `with_lock` already serialises its pair
|
|
250
|
-
# of writes. Nested calls are the adapter's problem to make
|
|
251
|
-
# re-entrant (both SQL adapters check for an open transaction
|
|
252
|
-
# first) — a reaction dispatched from inside a drain never nests
|
|
253
|
-
# here anyway, because draining happens after this block returns.
|
|
253
|
+
# One commit boundary for save + emit + outbox: an adapter with a real transaction
|
|
254
|
+
# (Sqlite, Postgres) commits the row, journal entry, and outbox rows together or
|
|
255
|
+
# not at all; Memory just yields, having nothing to roll back.
|
|
254
256
|
#
|
|
255
257
|
# @yield the writes to commit together; an exception raised inside rolls back an
|
|
256
258
|
# adapter-owned transaction and propagates
|
|
@@ -264,11 +266,9 @@ module Hecks
|
|
|
264
266
|
|
|
265
267
|
# Runs the block while holding the adapter's cross-process write lock.
|
|
266
268
|
#
|
|
267
|
-
# Only
|
|
268
|
-
#
|
|
269
|
-
#
|
|
270
|
-
# calling it, so the plain `yield` fallback here only guards
|
|
271
|
-
# against a stray direct call, not the real dispatch path.
|
|
269
|
+
# Only PostgresEra advertises `:cross_process_lock` (ADR 0036); the real dispatch
|
|
270
|
+
# path checks `capabilities` first, so this `yield` fallback only guards a stray
|
|
271
|
+
# direct call.
|
|
272
272
|
#
|
|
273
273
|
# @yield the dispatch to run with writers serialised
|
|
274
274
|
# @return [Object] adapter-defined; the block's own value when the adapter has no
|
|
@@ -279,10 +279,7 @@ module Hecks
|
|
|
279
279
|
yield
|
|
280
280
|
end
|
|
281
281
|
|
|
282
|
-
#
|
|
283
|
-
# together the way `save_saga`/`delete_saga`/`each_saga` are
|
|
284
|
-
# (`Registry::SagaPersistence`): an adapter either has an outbox
|
|
285
|
-
# or it doesn't, never half of one. See `Runtime::Outbox`.
|
|
282
|
+
# An adapter implements the whole outbox contract or none of it (see Runtime::Outbox).
|
|
286
283
|
OUTBOX_METHODS = %i[outbox_enqueue outbox_claim outbox_settle outbox_rows].freeze
|
|
287
284
|
|
|
288
285
|
# Reports whether the adapter implements the whole outbox contract; the answer is
|
|
@@ -308,7 +305,7 @@ module Hecks
|
|
|
308
305
|
#
|
|
309
306
|
# @param id [Integer] the row id assigned by `outbox_enqueue`
|
|
310
307
|
# @return [Boolean] true when this call claimed the row; false when it is missing or
|
|
311
|
-
#
|
|
308
|
+
# not pending
|
|
312
309
|
def outbox_claim(id) = @adapter.outbox_claim(id)
|
|
313
310
|
|
|
314
311
|
# Records the final status of a claimed outbox row.
|
|
@@ -4,23 +4,13 @@ require_relative "../../runtime/registry"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Ports
|
|
6
6
|
module Persistence
|
|
7
|
-
# Resolves one aggregate's persistence topology
|
|
8
|
-
# authoritative
|
|
9
|
-
# never silently become a read source.
|
|
7
|
+
# Resolves one aggregate's persistence topology; an unmarked binding is
|
|
8
|
+
# authoritative, and roles are opt-in so no adapter silently becomes a read source.
|
|
10
9
|
module BindingPolicy
|
|
11
10
|
module_function
|
|
12
11
|
|
|
13
|
-
# Picks the one bind that names an aggregate's authoritative store.
|
|
14
|
-
#
|
|
15
|
-
# A domain with no hecksagon gets the default in-memory bind. A domain that has one
|
|
16
|
-
# must bind the aggregate exactly once without a role.
|
|
17
|
-
#
|
|
18
|
-
# A `default_adapter` a world declares (`Registry#default_adapter_for` — the
|
|
19
|
-
# chapter's own world, else the project's) stands in wherever the hecksagon binds
|
|
20
|
-
# nothing for the aggregate, and replaces the in-memory fallback for a domain
|
|
21
|
-
# with no hecksagon. It is never consulted once the hecksagon binds the aggregate
|
|
22
|
-
# or declares a domain-level default of its own, so an explicit chapter decision
|
|
23
|
-
# always wins.
|
|
12
|
+
# Picks the one bind that names an aggregate's authoritative store. A world's
|
|
13
|
+
# `default_adapter` only fills a hecksagon's gap; an explicit bind always wins.
|
|
24
14
|
#
|
|
25
15
|
# @param registry [Runtime::Registry] the registry holding the domain's hecksagon
|
|
26
16
|
# @param domain [String, Symbol] name of the domain the aggregate belongs to
|
|
@@ -4,47 +4,15 @@ require_relative "../../runtime/errors"
|
|
|
4
4
|
module Hecks
|
|
5
5
|
module Ports
|
|
6
6
|
module Persistence
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# convention until something refuses the bypass; this is that
|
|
10
|
-
# something, and it is installed by `RepositoryFactory.build` — the
|
|
11
|
-
# one place every runtime repository is made — so no adapter can opt
|
|
12
|
-
# out of it.
|
|
13
|
-
#
|
|
14
|
-
# ## The mechanism, two halves
|
|
15
|
-
#
|
|
16
|
-
# 1. `guard!(adapter)` extends the adapter object (not its class,
|
|
17
|
-
# not a proxy — `is_a?`, `class`, and `===` stay the adapter's
|
|
18
|
-
# own) with a module that wraps every public method the adapter's
|
|
19
|
-
# class defines. Each call runs inside the boundary: a
|
|
20
|
-
# thread-local flag, re-entrant, restored on the way out. A block
|
|
21
|
-
# the caller passes (`transaction`, `with_write_lock`,
|
|
22
|
-
# `each_saga`) runs outside it — that block is the dispatch
|
|
23
|
-
# itself (hydrate, entity views, mutation), not adapter code.
|
|
24
|
-
# Reaching the adapter through `repository.adapter` (the query
|
|
25
|
-
# port, saga persistence, `bin/heki_compact`) is guarded all the
|
|
26
|
-
# same: the wrapper is on the object.
|
|
27
|
-
#
|
|
28
|
-
# 2. `Runtime::Instance#initialize` asks `check_state!` whenever it
|
|
29
|
-
# is handed `state:`. Outside the boundary that is a no-op; inside
|
|
30
|
-
# it, state `StateCodec.decoded?` rejects refuses by name. An
|
|
31
|
-
# `entries` answer is checked the same way on its way out, since a
|
|
32
|
-
# journal `Entry` is not an `Instance`.
|
|
33
|
-
#
|
|
34
|
-
# ## Checked, not silently decoded
|
|
35
|
-
#
|
|
36
|
-
# An adapter that forgets the codec is a bug in that adapter, and
|
|
37
|
-
# decoding for it here would hide the forgetting. Hydration does not
|
|
38
|
-
# respell keys either (A4) — `Value.hydrate` refuses a non-Symbol
|
|
39
|
-
# top-level key everywhere — so this boundary's deep check and
|
|
40
|
-
# hydration's shallow one agree.
|
|
7
|
+
# Refuses to let a guarded adapter build an `Instance` from undecoded state.
|
|
8
|
+
# `RepositoryFactory.build` installs it, so no adapter can opt out.
|
|
41
9
|
module CodecBoundary
|
|
42
10
|
KEY = :hecks_persistence_codec_boundary
|
|
43
11
|
|
|
44
12
|
module_function
|
|
45
13
|
|
|
46
|
-
# Extends
|
|
47
|
-
# boundary; guarding
|
|
14
|
+
# Extends the adapter object so every public method its class defines runs inside the
|
|
15
|
+
# boundary; guarding twice wraps once.
|
|
48
16
|
#
|
|
49
17
|
# @param adapter [Object] a driven persistence adapter instance
|
|
50
18
|
# @return [Object] the same adapter object, now including `Guarded`
|
|
@@ -53,33 +21,22 @@ module Hecks
|
|
|
53
21
|
adapter
|
|
54
22
|
end
|
|
55
23
|
|
|
56
|
-
#
|
|
24
|
+
# Whether this thread is currently inside a guarded adapter call.
|
|
57
25
|
#
|
|
58
|
-
# @return [Boolean]
|
|
59
|
-
# caller's block or returning
|
|
26
|
+
# @return [Boolean]
|
|
60
27
|
def active? = Thread.current[KEY] == true
|
|
61
28
|
|
|
62
|
-
# Runs the block with the boundary
|
|
63
|
-
# setting afterwards.
|
|
64
|
-
#
|
|
65
|
-
# @yield adapter code whose `Instance` construction is to be checked
|
|
66
|
-
# @return [Object] the block's value
|
|
29
|
+
# Runs the block with the boundary on for this thread; re-entrant.
|
|
67
30
|
def within(&) = with_flag(true, &)
|
|
68
31
|
|
|
69
|
-
# Runs the block with the boundary
|
|
70
|
-
# setting afterwards.
|
|
71
|
-
#
|
|
72
|
-
# @yield caller code, such as a dispatch running inside the adapter's transaction
|
|
73
|
-
# @return [Object] the block's value
|
|
32
|
+
# Runs the block with the boundary off for this thread; re-entrant.
|
|
74
33
|
def outside(&) = with_flag(false, &)
|
|
75
34
|
|
|
76
|
-
# Refuses state
|
|
77
|
-
# outside the boundary it checks nothing.
|
|
35
|
+
# Refuses undecoded state headed for `Runtime::Instance.new`; a no-op outside the boundary.
|
|
78
36
|
#
|
|
79
37
|
# @param aggregate [Bluebook::Aggregate, Bluebook::Entity] the construct the state
|
|
80
38
|
# belongs to
|
|
81
39
|
# @param state [Hash, Runtime::Value] the state handed to `Runtime::Instance.new`
|
|
82
|
-
# @return [nil] when the boundary is inactive or the state is decoded
|
|
83
40
|
# @raise [Runtime::WiringError] if the boundary is active and
|
|
84
41
|
# `StateCodec.decoded?` rejects the state
|
|
85
42
|
def check_state!(aggregate, state)
|
|
@@ -93,13 +50,12 @@ module Hecks
|
|
|
93
50
|
end
|
|
94
51
|
|
|
95
52
|
# Refuses a guarded adapter's `entries` answer if any entry's state is undecoded.
|
|
53
|
+
# A journal `Entry` is not an `Instance`, so `check_state!` never sees it.
|
|
96
54
|
#
|
|
97
|
-
# @param adapter [Object] the guarded adapter
|
|
98
|
-
# @param entries [Array<Persistence::Entry>, Object]
|
|
99
|
-
# returned; anything that is not an Array passes through unchecked
|
|
55
|
+
# @param adapter [Object] the guarded adapter
|
|
56
|
+
# @param entries [Array<Persistence::Entry>, Object] anything but an Array passes unchecked
|
|
100
57
|
# @return [Array<Persistence::Entry>, Object] `entries` itself
|
|
101
|
-
# @raise [Runtime::WiringError] if an `Entry`
|
|
102
|
-
# `StateCodec.decoded?` rejects
|
|
58
|
+
# @raise [Runtime::WiringError] if an `Entry` carries state `StateCodec.decoded?` rejects
|
|
103
59
|
def check_entries!(adapter, entries)
|
|
104
60
|
return entries unless entries.is_a?(Array)
|
|
105
61
|
|
|
@@ -113,13 +69,7 @@ module Hecks
|
|
|
113
69
|
entries
|
|
114
70
|
end
|
|
115
71
|
|
|
116
|
-
# Sets the thread-local
|
|
117
|
-
# the boundary re-entrant.
|
|
118
|
-
#
|
|
119
|
-
# @param value [Boolean] true to switch the boundary on, false to switch it off
|
|
120
|
-
# @yield the code to run under that setting
|
|
121
|
-
# @return [Object] the block's value; the earlier flag is restored even when the
|
|
122
|
-
# block raises
|
|
72
|
+
# Sets the thread-local flag for the block and restores it after, even on a raise.
|
|
123
73
|
def with_flag(value)
|
|
124
74
|
previous = Thread.current[KEY]
|
|
125
75
|
Thread.current[KEY] = value
|
|
@@ -128,24 +78,18 @@ module Hecks
|
|
|
128
78
|
Thread.current[KEY] = previous
|
|
129
79
|
end
|
|
130
80
|
|
|
131
|
-
# Marks a guarded adapter so a second `guard!`
|
|
132
|
-
# authoritative repository built over one object) never wraps twice.
|
|
81
|
+
# Marks a guarded adapter so a second `guard!` never wraps it twice.
|
|
133
82
|
module Guarded; end
|
|
134
83
|
|
|
135
|
-
#
|
|
136
|
-
# a cache, deliberately mutable.
|
|
84
|
+
# A per-adapter-class cache, filled lazily under `WRAPPERS_LOCK`; deliberately mutable.
|
|
137
85
|
WRAPPERS = {} # rubocop:disable Style/MutableConstant
|
|
138
86
|
WRAPPERS_LOCK = Mutex.new
|
|
139
87
|
|
|
140
88
|
# Builds, or fetches from the cache, the module that guards one adapter class.
|
|
141
|
-
#
|
|
142
|
-
# One wrapper module per adapter class, built once: every public
|
|
143
|
-
# instance method the class (and its ancestors below Object)
|
|
144
|
-
# defines, each forwarding to `super` inside the boundary.
|
|
89
|
+
# Each public method forwards to `super` inside the boundary.
|
|
145
90
|
#
|
|
146
91
|
# @param klass [Class] the adapter's class
|
|
147
|
-
# @return [Module] an anonymous module including `Guarded`,
|
|
148
|
-
# instances of `klass`
|
|
92
|
+
# @return [Module] an anonymous module including `Guarded`, to `extend` onto instances
|
|
149
93
|
def wrapper_for(klass)
|
|
150
94
|
WRAPPERS_LOCK.synchronize do
|
|
151
95
|
WRAPPERS[klass] ||= Module.new do
|
|
@@ -161,12 +105,8 @@ module Hecks
|
|
|
161
105
|
end
|
|
162
106
|
end
|
|
163
107
|
|
|
164
|
-
# Wraps the caller's
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
# @param block [Proc, nil] the block the caller passed to the adapter method
|
|
168
|
-
# @return [Proc, nil] a proc forwarding its arguments to `block` under `outside`; nil
|
|
169
|
-
# when no block was given
|
|
108
|
+
# Wraps the caller's block so it runs outside the boundary when the adapter yields to it;
|
|
109
|
+
# that block is the dispatch itself, not adapter code.
|
|
170
110
|
def outside_block(block)
|
|
171
111
|
return nil unless block
|
|
172
112
|
|
|
@@ -1,32 +1,16 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Ports
|
|
3
3
|
module Persistence
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# (optional) saga-persistence capability: Memory, deliberately
|
|
7
|
-
# (sagas stay in-memory-only, exactly as they always have), and
|
|
8
|
-
# any `RemoteRuntime`-shaped adapter (Lambda; the real durability
|
|
9
|
-
# for those lives on the other side of the call — Phase 1, not
|
|
10
|
-
# here). `SagaInterpreter`'s hook points call through
|
|
11
|
-
# `saga_persistence(domain)` unconditionally; this is what makes
|
|
12
|
-
# that safe without a `respond_to?` check at every call site,
|
|
13
|
-
# mirroring the same optional-capability shape
|
|
14
|
-
# `Ports::Persistence::AppendOnly` already gives adapters that
|
|
15
|
-
# don't implement `reset!`/`events`/`record_event`.
|
|
4
|
+
# A saga store that keeps nothing, for adapters without the optional saga-persistence
|
|
5
|
+
# capability; callers call through it unconditionally instead of checking `respond_to?`.
|
|
16
6
|
class NullSagaStore
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# @return [nil] always; nothing is stored
|
|
7
|
+
# Discards the checkpoint.
|
|
20
8
|
def save_saga(**) = nil
|
|
21
9
|
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# @return [nil] always; there is nothing to delete
|
|
10
|
+
# Ignores the request; there is nothing to delete.
|
|
25
11
|
def delete_saga(**) = nil
|
|
26
12
|
|
|
27
13
|
# Yields nothing, because no saga is ever stored here.
|
|
28
|
-
#
|
|
29
|
-
# @return [nil] always; a block, if given, is never called
|
|
30
14
|
def each_saga(*) = nil
|
|
31
15
|
end
|
|
32
16
|
|
|
@@ -1,28 +1,12 @@
|
|
|
1
1
|
module Hecks
|
|
2
2
|
module Ports
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# 0033 and `Plugin`'s own header for the seam this fills.
|
|
3
|
+
# Holds the persistence-plugin registry and its `register_plugin`/`plugin?`/`each_plugin`
|
|
4
|
+
# surface.
|
|
6
5
|
module Persistence
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# same way an adapter's own `.rb` file already registers itself
|
|
10
|
-
# (`Runtime::Registry#add_adapter`) rather than core listing adapter
|
|
11
|
-
# names anywhere. `register_plugin` is the entire contract a plugin
|
|
12
|
-
# must satisfy: anything responding to `contribute_boot_gates(registry,
|
|
13
|
-
# gates)` (a no-op is a valid implementation).
|
|
6
|
+
# The persistence-plugin registry (ADR 0033). A plugin registers itself when required, and
|
|
7
|
+
# need only respond to `contribute_boot_gates(registry, gates)`; a no-op is valid.
|
|
14
8
|
#
|
|
15
|
-
#
|
|
16
|
-
# (ADR 0027) or `Runtime::BootGates` (ADR 0031) — each of those is
|
|
17
|
-
# scoped to its own seam (IR-in/artifact-out; per-boot phased gates).
|
|
18
|
-
# A shared base is deferred until a third registry actually wants one.
|
|
19
|
-
#
|
|
20
|
-
# **Name-keyed, process-wide, not per-boot** — unlike `BootGates` (one
|
|
21
|
-
# instance per `Loader.boot` call, because gate registration is
|
|
22
|
-
# capability-conditional per registry), a persistence plugin is either
|
|
23
|
-
# `require`d into this process or it isn't; there is no "this boot's
|
|
24
|
-
# registry doesn't need it" case to isolate against, so a plain
|
|
25
|
-
# module-level hash is the right shape here, not an instantiable class.
|
|
9
|
+
# Process-wide and name-keyed: a plugin is either required into this process or it is not.
|
|
26
10
|
module Plugin
|
|
27
11
|
@plugins = {}
|
|
28
12
|
|
|
@@ -37,33 +21,25 @@ module Hecks
|
|
|
37
21
|
@plugins[name.to_sym] = plugin
|
|
38
22
|
end
|
|
39
23
|
|
|
40
|
-
#
|
|
24
|
+
# Whether a plugin is registered under `name`.
|
|
41
25
|
#
|
|
42
|
-
# @
|
|
43
|
-
# @return [Boolean] true when a plugin is registered under `name`
|
|
26
|
+
# @return [Boolean]
|
|
44
27
|
def registered?(name)
|
|
45
28
|
@plugins.key?(name.to_sym)
|
|
46
29
|
end
|
|
47
30
|
|
|
48
31
|
# Yields every registered plugin, in registration order.
|
|
49
|
-
#
|
|
50
|
-
# @yieldparam plugin [Object] a registered plugin
|
|
51
|
-
# @return [Hash{Symbol => Object}, Enumerator] the plugin table when a block is
|
|
52
|
-
# given, otherwise an Enumerator over the plugins
|
|
53
32
|
def each(&)
|
|
54
33
|
@plugins.each_value(&)
|
|
55
34
|
end
|
|
56
35
|
|
|
57
|
-
#
|
|
36
|
+
# Whether at least one plugin is registered.
|
|
58
37
|
#
|
|
59
|
-
# @return [Boolean]
|
|
38
|
+
# @return [Boolean]
|
|
60
39
|
def any? = !@plugins.empty?
|
|
61
40
|
end
|
|
62
41
|
end
|
|
63
42
|
|
|
64
|
-
# `Ports::Persistence.plugin?(:era)` / `.each_plugin` — the ordinary
|
|
65
|
-
# call surface; `Plugin.register`/`.registered?`/`.each` stay reachable
|
|
66
|
-
# directly for a plugin's own file to call at require-time.
|
|
67
43
|
module_function
|
|
68
44
|
|
|
69
45
|
# Registers a persistence plugin; a plugin's own file calls this when it is required.
|
|
@@ -73,22 +49,17 @@ module Hecks
|
|
|
73
49
|
# @return [Object] the plugin just registered
|
|
74
50
|
def register_plugin(name, plugin) = Plugin.register(name, plugin)
|
|
75
51
|
|
|
76
|
-
#
|
|
52
|
+
# Whether the named persistence plugin is loaded in this process.
|
|
77
53
|
#
|
|
78
|
-
# @
|
|
79
|
-
# @return [Boolean] true when a plugin is registered under `name`
|
|
54
|
+
# @return [Boolean]
|
|
80
55
|
def plugin?(name) = Plugin.registered?(name)
|
|
81
56
|
|
|
82
57
|
# Yields every loaded persistence plugin, in registration order.
|
|
83
|
-
#
|
|
84
|
-
# @yieldparam plugin [Object] a registered plugin
|
|
85
|
-
# @return [Hash{Symbol => Object}, Enumerator] the plugin table when a block is given,
|
|
86
|
-
# otherwise an Enumerator over the plugins
|
|
87
58
|
def each_plugin(&) = Plugin.each(&)
|
|
88
59
|
|
|
89
|
-
#
|
|
60
|
+
# Whether any persistence plugin is loaded in this process.
|
|
90
61
|
#
|
|
91
|
-
# @return [Boolean]
|
|
62
|
+
# @return [Boolean]
|
|
92
63
|
def plugins_loaded? = Plugin.any?
|
|
93
64
|
end
|
|
94
65
|
end
|