hecks 1.3.0 → 1.5.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/lib/hecks/adapters/driven/claude_code.rb +72 -7
- data/lib/hecks/adapters/driven/d1.rb +187 -23
- data/lib/hecks/adapters/driven/folder.rb +83 -10
- data/lib/hecks/adapters/driven/google_authentication.rb +33 -12
- data/lib/hecks/adapters/driven/governance_authorization.rb +87 -18
- data/lib/hecks/adapters/driven/heki/journal.rb +13 -4
- data/lib/hecks/adapters/driven/heki/saga_store.rb +56 -10
- data/lib/hecks/adapters/driven/heki/snapshot.rb +1 -1
- data/lib/hecks/adapters/driven/heki.rb +106 -9
- data/lib/hecks/adapters/driven/identity_registry.rb +12 -2
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +25 -3
- data/lib/hecks/adapters/driven/in_process_key_vault.adapter +3 -0
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +53 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +67 -14
- data/lib/hecks/adapters/driven/lambda.rb +82 -35
- data/lib/hecks/adapters/driven/local_storage.rb +83 -10
- data/lib/hecks/adapters/driven/memory.rb +205 -9
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +21 -1
- data/lib/hecks/adapters/driven/postgres/codec.rb +27 -11
- data/lib/hecks/adapters/driven/postgres/outbox.rb +40 -2
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +23 -7
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +14 -14
- data/lib/hecks/adapters/driven/postgres.rb +175 -28
- data/lib/hecks/adapters/driven/prism.rb +50 -11
- data/lib/hecks/adapters/driven/secure_random_identity.rb +3 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +34 -22
- data/lib/hecks/adapters/driven/sqlite/codec.rb +38 -10
- data/lib/hecks/adapters/driven/sqlite/projection.rb +60 -32
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +12 -12
- data/lib/hecks/adapters/driven/sqlite.rb +181 -21
- data/lib/hecks/adapters/driven/system_clock.rb +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.adapter +3 -0
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +66 -0
- data/lib/hecks/adapters/driven.rb +6 -4
- data/lib/hecks/adapters/driving/github_webhook.rb +31 -18
- data/lib/hecks/behaviors/dsl.rb +60 -2
- data/lib/hecks/behaviors/expectations.rb +190 -29
- data/lib/hecks/behaviors/ir.rb +12 -1
- data/lib/hecks/behaviors/rspec.rb +9 -1
- data/lib/hecks/behaviors/runner.rb +21 -2
- data/lib/hecks/behaviors.rb +9 -1
- data/lib/hecks/bluebook/aggregate.rb +43 -13
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +17 -10
- data/lib/hecks/bluebook/assembly/build.rb +26 -5
- data/lib/hecks/bluebook/assembly/contract.rb +98 -23
- data/lib/hecks/bluebook/assembly/contracts.rb +59 -52
- data/lib/hecks/bluebook/assembly/marks.rb +159 -30
- data/lib/hecks/bluebook/assembly/specializer.rb +38 -21
- data/lib/hecks/bluebook/assembly.rb +32 -14
- data/lib/hecks/bluebook/attribute.rb +26 -12
- data/lib/hecks/bluebook/behaviour/aggregate.rb +43 -11
- data/lib/hecks/bluebook/behaviour/attribute.rb +18 -5
- data/lib/hecks/bluebook/behaviour/chapter.rb +76 -5
- data/lib/hecks/bluebook/behaviour/command.rb +55 -25
- data/lib/hecks/bluebook/behaviour/domain_port.rb +27 -7
- data/lib/hecks/bluebook/behaviour/entity.rb +20 -8
- data/lib/hecks/bluebook/behaviour/hexagon.rb +30 -4
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +27 -6
- data/lib/hecks/bluebook/behaviour/policy.rb +42 -17
- data/lib/hecks/bluebook/behaviour/process_manager.rb +39 -8
- data/lib/hecks/bluebook/behaviour/query.rb +6 -1
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -8
- data/lib/hecks/bluebook/behaviour/traits.rb +48 -12
- data/lib/hecks/bluebook/behaviour/value_object.rb +21 -9
- data/lib/hecks/bluebook/capabilities.rb +27 -0
- data/lib/hecks/bluebook/chapter.rb +51 -9
- data/lib/hecks/bluebook/command.rb +62 -17
- data/lib/hecks/bluebook/domain_port.rb +34 -9
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +24 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +49 -49
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +282 -123
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +112 -75
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +81 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +486 -117
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +179 -47
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +116 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +284 -122
- data/lib/hecks/bluebook/dsl/const_shim.rb +46 -15
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +90 -25
- data/lib/hecks/bluebook/dsl/entity_builder.rb +191 -61
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +148 -132
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +130 -30
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +38 -21
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +27 -4
- data/lib/hecks/bluebook/dsl/policy_builder.rb +86 -36
- data/lib/hecks/bluebook/dsl/port_builder.rb +38 -7
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +56 -22
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +111 -47
- data/lib/hecks/bluebook/dsl/query_builder.rb +37 -8
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +127 -52
- data/lib/hecks/bluebook/dsl/rule_reference.rb +97 -43
- data/lib/hecks/bluebook/dsl/translation_builder.rb +150 -44
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +68 -20
- data/lib/hecks/bluebook/dsl/word_gate.rb +59 -53
- data/lib/hecks/bluebook/dsl/world_builder.rb +51 -8
- data/lib/hecks/bluebook/entity.rb +40 -11
- data/lib/hecks/bluebook/expression/ast_json.rb +128 -36
- data/lib/hecks/bluebook/expression/ast_reader.rb +32 -3
- data/lib/hecks/bluebook/expression/canonical_form.rb +55 -16
- data/lib/hecks/bluebook/expression/evaluator.rb +221 -43
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +54 -18
- data/lib/hecks/bluebook/expression/resolver.rb +369 -128
- data/lib/hecks/bluebook/hexagon.rb +35 -1
- data/lib/hecks/bluebook/lifecycle.rb +12 -1
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/judge.rb +126 -108
- data/lib/hecks/bluebook/meta_validator/plan.rb +81 -46
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +3 -2
- data/lib/hecks/bluebook/meta_validator/readings.rb +200 -50
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +68 -41
- data/lib/hecks/bluebook/meta_validator/shapes.rb +166 -21
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +286 -46
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +11 -10
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -5
- data/lib/hecks/bluebook/meta_validator.rb +235 -139
- data/lib/hecks/bluebook/model_check.rb +434 -104
- data/lib/hecks/bluebook/pattern_subset.rb +32 -10
- data/lib/hecks/bluebook/policy.rb +15 -13
- data/lib/hecks/bluebook/process_manager.rb +27 -14
- data/lib/hecks/bluebook/project_discovery.rb +5 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +50 -6
- data/lib/hecks/bluebook/query.rb +31 -4
- data/lib/hecks/bluebook/read_model.rb +35 -15
- data/lib/hecks/bluebook/reference.rb +26 -13
- data/lib/hecks/bluebook/smoke_test.rb +46 -23
- data/lib/hecks/bluebook/synthesizer.rb +46 -12
- data/lib/hecks/bluebook/translation.rb +34 -5
- data/lib/hecks/bluebook/value_object.rb +29 -11
- data/lib/hecks/bluebook.rb +5 -6
- data/lib/hecks/codemod.rb +138 -50
- data/lib/hecks/construct.rb +21 -7
- data/lib/hecks/corpus.rb +438 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +19 -0
- data/lib/hecks/doc/reference.rb +200 -31
- data/lib/hecks/embryonaut_bluebook.rb +38 -15
- data/lib/hecks/facade/cli_door.rb +69 -10
- data/lib/hecks/facade/cli_runner.rb +105 -24
- data/lib/hecks/facade/command_request.rb +23 -0
- data/lib/hecks/facade/handle.rb +155 -35
- data/lib/hecks/facade/json_door.rb +106 -25
- data/lib/hecks/facade/surface/aggregate_door.rb +50 -27
- data/lib/hecks/facade/surface/chapter.rb +26 -17
- data/lib/hecks/facade/surface.rb +16 -3
- data/lib/hecks/facade.rb +15 -4
- data/lib/hecks/forms/app.rb +46 -30
- data/lib/hecks/forms/command_form_renderer.rb +70 -9
- data/lib/hecks/forms/field_renderer.rb +142 -6
- data/lib/hecks/forms/field_shape.rb +183 -19
- data/lib/hecks/forms/html.rb +51 -7
- data/lib/hecks/forms/index_renderer.rb +14 -2
- data/lib/hecks/forms/page.rb +14 -0
- data/lib/hecks/forms/params.rb +120 -23
- data/lib/hecks/forms/port_argument.rb +14 -2
- data/lib/hecks/forms/query_form_renderer.rb +65 -2
- data/lib/hecks/forms/record_renderer.rb +60 -2
- data/lib/hecks/forms/record_table.rb +28 -1
- data/lib/hecks/forms/reference_options.rb +24 -0
- data/lib/hecks/forms/value_object_shape.rb +13 -3
- data/lib/hecks/forms.rb +24 -4
- data/lib/hecks/fqn.rb +59 -1
- data/lib/hecks/framework/bluebook/governance.bluebook +9 -0
- data/lib/hecks/framework/bluebook/privacy.bluebook +155 -0
- data/lib/hecks/framework/oidc.json +15 -0
- data/lib/hecks/framework.rb +79 -25
- data/lib/hecks/freezer.rb +27 -11
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +241 -106
- data/lib/hecks/fuzzing/combination_miner.rb +178 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +241 -45
- data/lib/hecks/fuzzing/coverage_campaign.rb +161 -0
- data/lib/hecks/fuzzing/differential.rb +192 -0
- data/lib/hecks/fuzzing/domain_generator.rb +871 -0
- data/lib/hecks/fuzzing/era_boundary.rb +68 -19
- data/lib/hecks/fuzzing/form_census.rb +121 -20
- data/lib/hecks/fuzzing/generated_domain_check.rb +171 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +45 -6
- data/lib/hecks/fuzzing/isolated_boot.rb +137 -80
- data/lib/hecks/fuzzing/nondeterministic.rb +79 -0
- data/lib/hecks/fuzzing/persistence_parity.rb +111 -21
- data/lib/hecks/fuzzing/properties/corrections.rb +34 -9
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +300 -51
- data/lib/hecks/fuzzing/properties/guards.rb +86 -42
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +80 -32
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +40 -35
- data/lib/hecks/fuzzing/properties/outbox.rb +70 -32
- data/lib/hecks/fuzzing/properties/querying.rb +82 -28
- data/lib/hecks/fuzzing/properties.rb +84 -51
- data/lib/hecks/fuzzing/qa_settings.rb +164 -0
- data/lib/hecks/fuzzing/replay.rb +241 -133
- data/lib/hecks/fuzzing/rotation_priority.rb +48 -28
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +139 -0
- data/lib/hecks/fuzzing/self_consistency.rb +315 -137
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +46 -40
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +18 -11
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +13 -12
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +21 -12
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +59 -32
- data/lib/hecks/fuzzing/sequence_generator.rb +158 -23
- data/lib/hecks/fuzzing/shrinker.rb +309 -0
- data/lib/hecks/fuzzing/structural_skips.rb +37 -130
- data/lib/hecks/fuzzing/sweep_depth.rb +12 -4
- data/lib/hecks/fuzzing/target_capabilities.rb +107 -29
- data/lib/hecks/fuzzing/value_generator.rb +110 -22
- data/lib/hecks/fuzzing.rb +5 -0
- data/lib/hecks/grammar/evolve.rb +188 -12
- data/lib/hecks/grammar.rb +53 -7
- data/lib/hecks/ir.rb +51 -20
- data/lib/hecks/language/bluebook/bluebook.bluebook +41 -0
- data/lib/hecks/language/bluebook/policy.bluebook +11 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +348 -13
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +11 -0
- data/lib/hecks/language/oidc.json +5 -0
- data/lib/hecks/literal.rb +41 -9
- data/lib/hecks/naming.rb +112 -31
- data/lib/hecks/ports/access_control.rb +53 -2
- data/lib/hecks/ports/agent/answers.rb +83 -6
- data/lib/hecks/ports/agent.rb +119 -35
- data/lib/hecks/ports/authentication.rb +44 -4
- data/lib/hecks/ports/authorization.rb +50 -11
- data/lib/hecks/ports/clock.rb +42 -23
- data/lib/hecks/ports/extraction.rb +16 -0
- data/lib/hecks/ports/identity_assignment.rb +23 -2
- data/lib/hecks/ports/identity_generation.rb +17 -3
- data/lib/hecks/ports/identity_resolution.rb +17 -1
- data/lib/hecks/ports/key_vault.port +6 -0
- data/lib/hecks/ports/key_vault.rb +58 -0
- data/lib/hecks/ports/loading.rb +4 -0
- data/lib/hecks/ports/persistence/append_only.rb +172 -8
- data/lib/hecks/ports/persistence/binding_policy.rb +34 -0
- data/lib/hecks/ports/persistence/codec_boundary.rb +178 -0
- data/lib/hecks/ports/persistence/execution.rb +4 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +12 -1
- data/lib/hecks/ports/persistence/plugin.rb +42 -4
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +218 -25
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +77 -9
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +81 -24
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +29 -18
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +144 -60
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +103 -8
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +98 -23
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +282 -109
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +63 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +118 -66
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +51 -28
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +34 -5
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +25 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +129 -34
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +51 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +33 -9
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +16 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +95 -6
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +314 -90
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +68 -10
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +9 -2
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +48 -8
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +16 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +36 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +23 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +58 -19
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +96 -5
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +15 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +9 -1
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era.rb +12 -2
- data/lib/hecks/ports/persistence/remote_runtime.rb +9 -2
- data/lib/hecks/ports/persistence/repository_factory.rb +23 -3
- data/lib/hecks/ports/persistence/state_codec.rb +319 -0
- data/lib/hecks/ports/persistence.rb +36 -1
- data/lib/hecks/ports/projection.rb +61 -7
- data/lib/hecks/ports/query/in_memory.rb +41 -8
- data/lib/hecks/ports/query/ordering.rb +21 -6
- data/lib/hecks/ports/query.rb +35 -0
- data/lib/hecks/ports.rb +1 -0
- data/lib/hecks/projections/bootstrap_table.rb +147 -0
- data/lib/hecks/projections/diagrams.rb +315 -79
- data/lib/hecks/projections/glossary/html.rb +91 -3
- data/lib/hecks/projections/glossary/markdown.rb +50 -3
- data/lib/hecks/projections/glossary/mermaid.rb +49 -1
- data/lib/hecks/projections/glossary/sections.rb +1 -1
- data/lib/hecks/projections/glossary/sentences.rb +110 -5
- data/lib/hecks/projections/glossary.rb +173 -31
- data/lib/hecks/projections/ir.rb +1 -1
- data/lib/hecks/projections/model/deviations.rb +62 -17
- data/lib/hecks/projections/model.rb +75 -21
- data/lib/hecks/projections/oidc.rb +45 -8
- data/lib/hecks/projections/parser_table.rb +53 -12
- data/lib/hecks/projections/reference.rb +15 -3
- data/lib/hecks/projections/rust_vocabulary.rb +646 -0
- data/lib/hecks/projections/shape.rb +10 -2
- data/lib/hecks/projections/statements.rb +69 -22
- data/lib/hecks/projections/vocabulary.rb +26 -9
- data/lib/hecks/projections.rb +5 -3
- data/lib/hecks/projector/cli_projector.rb +245 -37
- data/lib/hecks/projector/docs_projector.rb +154 -28
- data/lib/hecks/projector/exporter.rb +104 -29
- data/lib/hecks/projector/ir_projector.rb +7 -1
- data/lib/hecks/projector/narrate_projector.rb +144 -23
- data/lib/hecks/projector/target.rb +42 -18
- data/lib/hecks/projector.rb +86 -18
- data/lib/hecks/query_ir.rb +94 -47
- data/lib/hecks/query_specification/common/comparators.rb +19 -3
- data/lib/hecks/query_specification/common/comparison.rb +109 -27
- data/lib/hecks/query_specification/common/dsl.rb +65 -9
- data/lib/hecks/query_specification/common/null_policy.rb +61 -15
- data/lib/hecks/query_specification/common/null_semantics.rb +4 -0
- data/lib/hecks/query_specification/common/options.rb +25 -0
- data/lib/hecks/query_specification/field_path.rb +69 -15
- data/lib/hecks/query_specification/hop_path.rb +57 -20
- data/lib/hecks/query_specification/read_model/specification.rb +4 -0
- data/lib/hecks/rendering.rb +9 -3
- data/lib/hecks/router/namespace_installer.rb +16 -3
- data/lib/hecks/router.rb +56 -1
- data/lib/hecks/runtime/aggregate_lock.rb +20 -11
- data/lib/hecks/runtime/boot_gates.rb +21 -3
- data/lib/hecks/runtime/caller.rb +40 -8
- data/lib/hecks/runtime/capability_graph.rb +13 -2
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +40 -41
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +45 -67
- data/lib/hecks/runtime/command_interpreter.rb +195 -113
- data/lib/hecks/runtime/command_rules/admissibility.rb +231 -80
- data/lib/hecks/runtime/command_rules/arithmetic.rb +154 -96
- data/lib/hecks/runtime/command_rules/authorization.rb +38 -17
- data/lib/hecks/runtime/command_rules/emission.rb +18 -1
- data/lib/hecks/runtime/command_rules/references.rb +145 -55
- data/lib/hecks/runtime/command_rules.rb +3 -0
- data/lib/hecks/runtime/dependency_planning.rb +56 -11
- data/lib/hecks/runtime/dispatcher.rb +263 -92
- data/lib/hecks/runtime/entity_element.rb +304 -67
- data/lib/hecks/runtime/entity_interpreter.rb +149 -88
- data/lib/hecks/runtime/errors.rb +37 -23
- data/lib/hecks/runtime/event.rb +15 -10
- data/lib/hecks/runtime/identity.rb +91 -23
- data/lib/hecks/runtime/instance.rb +103 -18
- data/lib/hecks/runtime/interpreting.rb +24 -16
- data/lib/hecks/runtime/invocation.rb +358 -0
- data/lib/hecks/runtime/loader.rb +105 -19
- data/lib/hecks/runtime/outbox.rb +164 -26
- data/lib/hecks/runtime/policy_interpreter.rb +73 -60
- data/lib/hecks/runtime/port_operation_interpreter.rb +42 -19
- data/lib/hecks/runtime/query_interpreter.rb +93 -74
- data/lib/hecks/runtime/reaction_invocation.rb +73 -28
- data/lib/hecks/runtime/read_model_interpreter.rb +60 -44
- data/lib/hecks/runtime/rebuild_sweep.rb +32 -4
- data/lib/hecks/runtime/reference_hop.rb +48 -6
- data/lib/hecks/runtime/refusal_wording.rb +142 -115
- data/lib/hecks/runtime/registry/saga_persistence.rb +32 -21
- data/lib/hecks/runtime/registry/verification.rb +153 -28
- data/lib/hecks/runtime/registry.rb +202 -32
- data/lib/hecks/runtime/remote_dispatcher.rb +125 -24
- data/lib/hecks/runtime/routing.rb +36 -154
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +25 -27
- data/lib/hecks/runtime/saga_interpreter.rb +90 -76
- data/lib/hecks/runtime/saga_pending_dispatch.rb +12 -12
- data/lib/hecks/runtime/tenant_check.rb +33 -13
- data/lib/hecks/runtime/tenant_scope.rb +23 -5
- data/lib/hecks/runtime/value/admission.rb +75 -30
- data/lib/hecks/runtime/value/coercion.rb +555 -142
- data/lib/hecks/runtime/value/entity_list_coercion.rb +132 -60
- data/lib/hecks/runtime/value.rb +71 -21
- data/lib/hecks/runtime.rb +39 -7
- data/lib/hecks/storehouse.rb +368 -72
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +130 -0
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +32 -0
- data/lib/hecks/version.rb +3 -3
- data/lib/hecks/vocabulary.rb +205 -4
- data/lib/hecks.rb +91 -11
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +19 -11
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +24 -12
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +38 -14
- metadata +28 -2
|
@@ -6,8 +6,8 @@ require_relative "../../runtime/instance"
|
|
|
6
6
|
|
|
7
7
|
module Hecks
|
|
8
8
|
module Adapters
|
|
9
|
-
#
|
|
10
|
-
# own adapter, resolved through the
|
|
9
|
+
# **The read-side half of lambda routing** — `persisted_by("Lambda")`'s
|
|
10
|
+
# own adapter, resolved through the same `Ports::Persistence`
|
|
11
11
|
# machinery `persisted_by("Postgres")`/`persisted_by("Memory")`
|
|
12
12
|
# already use (`Runtime::Registry#repository`, `RepositoryFactory
|
|
13
13
|
# .build`) — no new framework plumbing, just a new adapter class at
|
|
@@ -16,7 +16,7 @@ module Hecks
|
|
|
16
16
|
# compose through one shared `Client` per domain rather than each
|
|
17
17
|
# inventing its own AWS wiring.
|
|
18
18
|
#
|
|
19
|
-
#
|
|
19
|
+
# **Read-only, deliberately** — `append`/`project` raise rather than
|
|
20
20
|
# silently no-op. A write reaching this class would mean
|
|
21
21
|
# `Runtime::CommandInterpreter` ran locally against a Lambda-routed
|
|
22
22
|
# domain, which is exactly the bypass `Runtime::RemoteDispatcher`
|
|
@@ -29,6 +29,16 @@ module Hecks
|
|
|
29
29
|
|
|
30
30
|
attr_reader :aggregate
|
|
31
31
|
|
|
32
|
+
# Resolves which Lambda function's Store to read from and builds the client that
|
|
33
|
+
# reads it.
|
|
34
|
+
#
|
|
35
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate whose records this adapter reads
|
|
36
|
+
# @param settings [Hash{Symbol, String => Object}] world settings for the binding:
|
|
37
|
+
# `domain` (prefixes the instances lookup; default the aggregate's own name),
|
|
38
|
+
# `region` (default `"us-east-1"`) and `function` (the function name, when it is not
|
|
39
|
+
# `hecks-<domain>`), each read under a Symbol or a String key
|
|
40
|
+
# @param root [String, nil] the boot's own project directory, used with `DOMAIN_NAME`
|
|
41
|
+
# to resolve which function to call; nil falls further back to `domain`
|
|
32
42
|
def initialize(aggregate:, settings: {}, root: nil)
|
|
33
43
|
@aggregate = aggregate
|
|
34
44
|
domain =
|
|
@@ -39,59 +49,81 @@ module Hecks
|
|
|
39
49
|
else
|
|
40
50
|
aggregate.name
|
|
41
51
|
end
|
|
42
|
-
region =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# TWO DIFFERENT "domain"s, deliberately not conflated: `domain`
|
|
51
|
-
# (this aggregate's OWN bluebook name — "Identity", "Governance")
|
|
52
|
+
region = setting(settings, :region, "us-east-1")
|
|
53
|
+
# Named, when this deployment's function isn't `hecks-<domain>`
|
|
54
|
+
# — `Client`'s own comment has the real case that needs it.
|
|
55
|
+
# Absent (every domain whose stack name was never pinned), the
|
|
56
|
+
# computation below is exactly as it was.
|
|
57
|
+
function = setting(settings, :function, nil)
|
|
58
|
+
# Two different "domain"s, deliberately not conflated: `domain`
|
|
59
|
+
# (this aggregate's own bluebook name — "Identity", "Governance")
|
|
52
60
|
# only ever prefixes the instances lookup, since that's how
|
|
53
61
|
# rust/host's own `Store::instances()` keys every record
|
|
54
62
|
# (`registry.rb`'s own `"#{a[:domain_name]}::#{a[:name]}#"`
|
|
55
63
|
# dump format, unchanged by which chapter attached it). The
|
|
56
|
-
#
|
|
57
|
-
# and Identity aggregates are compiled into the
|
|
64
|
+
# function to actually call is a different question: Governance
|
|
65
|
+
# and Identity aggregates are compiled into the attaching
|
|
58
66
|
# domain's own Lambda (one merged `Store` per target — Phase 0's
|
|
59
67
|
# framework-bluebook work), never a function of their own, so
|
|
60
68
|
# `settings[:domain]` is the wrong signal for `Client.new`.
|
|
61
69
|
# `root` is the boot's own directory (`Registry#root`, shared by
|
|
62
|
-
#
|
|
70
|
+
# every bluebook in one registry regardless of which one
|
|
63
71
|
# attached it) — `File.basename(root)` reproduces the exact
|
|
64
72
|
# same string `bin/project_deploy`'s own `stack_name` computes
|
|
65
|
-
# from the domain
|
|
66
|
-
# different functions for the same deploy... on a
|
|
73
|
+
# from the domain path, so the two can never name two
|
|
74
|
+
# different functions for the same deploy... on a local boot,
|
|
67
75
|
# where `root` is a real project directory. Inside the deployed
|
|
68
|
-
# Lambda itself `root` is
|
|
76
|
+
# Lambda itself `root` is always `/var/task` (every Lambda's own
|
|
69
77
|
# fixed code root, regardless of domain) — `File.basename` gives
|
|
70
78
|
# "task", not the domain name, and invokes the wrong function
|
|
71
79
|
# entirely. A real, live AccessDeniedException on
|
|
72
80
|
# "hecks-task" caught this: invisible through every earlier
|
|
73
81
|
# phase's own verify step, all run from a local boot, until
|
|
74
|
-
# WebFunction became the first Ruby process to
|
|
82
|
+
# WebFunction became the first Ruby process to ever make this
|
|
75
83
|
# exact call from inside a deployed Lambda. `DOMAIN_NAME` (set
|
|
76
84
|
# by bin/project_deploy's own WebFunction Environment) is the
|
|
77
85
|
# real, unambiguous signal in that specific context; the
|
|
78
86
|
# root-basename heuristic stays as the local-boot fallback,
|
|
79
87
|
# unchanged.
|
|
80
88
|
function_domain = ENV["DOMAIN_NAME"] || (root ? File.basename(root) : domain)
|
|
81
|
-
@client = Client.new(domain: function_domain, region: region)
|
|
89
|
+
@client = Client.new(domain: function_domain, region: region, function: function)
|
|
82
90
|
@prefix = "#{domain}::#{aggregate.hecks_name}#"
|
|
83
91
|
end
|
|
84
92
|
|
|
93
|
+
# Looks up the current record for one aggregate identity, reading the Lambda's own
|
|
94
|
+
# Store fresh on every call.
|
|
95
|
+
#
|
|
96
|
+
# @param id [String, Object] the aggregate identity, compared as `id.to_s`
|
|
97
|
+
# @return [Runtime::Instance, nil] the decoded record, or nil when no record has that id
|
|
85
98
|
def find(id)
|
|
86
99
|
instances[id.to_s]
|
|
87
100
|
end
|
|
88
101
|
|
|
102
|
+
# Lists every stored record, in id order unless an ordering attribute is given.
|
|
103
|
+
#
|
|
104
|
+
# @param order_by [String, Symbol, nil] attribute (or dotted value-object path) to sort
|
|
105
|
+
# by; nil orders by id alone
|
|
106
|
+
# @param direction [Symbol, String] `:asc` or `:desc`
|
|
107
|
+
# @return [Array<Runtime::Instance>] the decoded records, `[]` when the Store holds none
|
|
108
|
+
# with this aggregate's own prefix
|
|
109
|
+
# @raise [Runtime::WiringError] if `order_by` names no attribute of the aggregate
|
|
89
110
|
def all(order_by: nil, direction: :asc)
|
|
90
111
|
InMemoryOrdering.ordered(instances.values, aggregate: @aggregate, order_by: order_by, direction: direction)
|
|
91
112
|
end
|
|
92
113
|
|
|
114
|
+
# Counts the records currently held for this aggregate.
|
|
115
|
+
#
|
|
116
|
+
# @return [Integer] number of records
|
|
93
117
|
def count = instances.size
|
|
94
118
|
|
|
119
|
+
# Answers a declared query by filtering, ordering and paging the held records in Ruby.
|
|
120
|
+
#
|
|
121
|
+
# @param specification [QuerySpecification::Common::Options] the declared query
|
|
122
|
+
# @param args [Hash{Symbol => Object}] values for the specification's symbolic operands
|
|
123
|
+
# @param context [Hash] execution context from `Ports::Query.execute`; accepted for the
|
|
124
|
+
# port's call shape and not read
|
|
125
|
+
# @return [Array<Runtime::Instance>] the matching records, `[]` when none match
|
|
126
|
+
# @raise [Runtime::WiringError] if a where clause uses an operation no comparator handles
|
|
95
127
|
def query(specification, args = {}, context: {})
|
|
96
128
|
Ports::Query::InMemory.execute(instances.values, specification, args)
|
|
97
129
|
end
|
|
@@ -105,18 +137,36 @@ module Hecks
|
|
|
105
137
|
# 1 — there is no local write-ahead log for `recover!` to replay),
|
|
106
138
|
# `append`/`project` raise rather than silently no-op.
|
|
107
139
|
|
|
140
|
+
# Reads a world setting under either a Symbol or a String key, with a fallback.
|
|
141
|
+
#
|
|
142
|
+
# A `.world` block's settings arrive symbol-keyed from the DSL and
|
|
143
|
+
# string-keyed from a round-tripped export, so every read has to
|
|
144
|
+
# accept both — one helper rather than the same five lines per
|
|
145
|
+
# key.
|
|
146
|
+
#
|
|
147
|
+
# @param settings [Hash] the world settings Hash to read from
|
|
148
|
+
# @param key [Symbol] the setting name, tried as itself and as `key.to_s`
|
|
149
|
+
# @param fallback [Object] the value to return when neither key is present
|
|
150
|
+
# @return [Object] the setting's value, or `fallback` when absent
|
|
151
|
+
def setting(settings, key, fallback)
|
|
152
|
+
return settings[key] if settings.key?(key)
|
|
153
|
+
return settings[key.to_s] if settings.key?(key.to_s)
|
|
154
|
+
|
|
155
|
+
fallback
|
|
156
|
+
end
|
|
157
|
+
|
|
108
158
|
private
|
|
109
159
|
|
|
110
|
-
#
|
|
160
|
+
# Re-fetched every call, deliberately not memoized across them —
|
|
111
161
|
# this adapter itself is long-lived (one instance per aggregate,
|
|
112
|
-
# held by the registry `RUNTIME = Hecks.boot(...)` builds
|
|
162
|
+
# held by the registry `RUNTIME = Hecks.boot(...)` builds once
|
|
113
163
|
# per Lambda web process — WebFunction's own top-level constant,
|
|
114
164
|
# reused warm across every HTTP request that process serves, not
|
|
115
|
-
# rebuilt per request the way
|
|
116
|
-
#
|
|
165
|
+
# rebuilt per request the way memoizing here would wrongly assume).
|
|
166
|
+
# A real, live bug caught this: a mutation
|
|
117
167
|
# dispatched fine (RemoteDispatcher always calls the dispatch
|
|
118
|
-
# Lambda fresh) and the very next `.all` on the
|
|
119
|
-
# container kept returning the state from
|
|
168
|
+
# Lambda fresh) and the very next `.all` on the same warm
|
|
169
|
+
# container kept returning the state from before that mutation,
|
|
120
170
|
# forever, until the container cold-started — memoizing here
|
|
121
171
|
# made every write invisible to every read on a warm container.
|
|
122
172
|
# Keyed by bare id (the part after "Domain::Aggregate#"), not the
|
|
@@ -130,15 +180,12 @@ module Hecks
|
|
|
130
180
|
end.to_h
|
|
131
181
|
end
|
|
132
182
|
|
|
133
|
-
# Lambda's own JSON response is already Ruby-decoded with
|
|
134
|
-
# keys (plain `JSON.parse`, no `symbolize_names:`) —
|
|
135
|
-
#
|
|
136
|
-
#
|
|
137
|
-
# round trip through `JSON.generate`/`JSON.parse` is simpler and
|
|
138
|
-
# safer than a hand-rolled deep-symbolize helper for what is not
|
|
139
|
-
# a hot path.
|
|
183
|
+
# Lambda's own JSON response is already Ruby-decoded with string
|
|
184
|
+
# keys (plain `JSON.parse`, no `symbolize_names:`) — decoded through
|
|
185
|
+
# the state codec (PR A3), the same IR-driven spelling every other
|
|
186
|
+
# adapter's read produces.
|
|
140
187
|
def build_instance(id, state)
|
|
141
|
-
Runtime::Instance.new(aggregate: @aggregate, id: id, state:
|
|
188
|
+
Runtime::Instance.new(aggregate: @aggregate, id: id, state: Ports::Persistence::StateCodec.decode(@aggregate, state))
|
|
142
189
|
end
|
|
143
190
|
end
|
|
144
191
|
end
|
|
@@ -5,13 +5,13 @@ require_relative "../../runtime/instance"
|
|
|
5
5
|
|
|
6
6
|
module Hecks
|
|
7
7
|
module Adapters
|
|
8
|
-
# A
|
|
8
|
+
# **A browser-hosted domain's own declared intent** — not a second Memory
|
|
9
9
|
# wearing a different name. Ruby has no way to reach a real browser's
|
|
10
10
|
# `window.localStorage` at all (it is per-tab, per-origin, JS-only,
|
|
11
11
|
# unreachable over any network the way D1's own REST API is) — so
|
|
12
12
|
# this Ruby-side adapter is honestly a stand-in: in-process, ephemeral,
|
|
13
13
|
# mechanically identical to Memory. What earns it a name of its own
|
|
14
|
-
# is what it
|
|
14
|
+
# is what it declares, not what it happens to do in Ruby: `persisted_by
|
|
15
15
|
# "LocalStorage"` says "this domain expects real, durable, single-
|
|
16
16
|
# device storage the moment it's actually running where it's meant to
|
|
17
17
|
# run" — the same distinction Heki (real local durability) already
|
|
@@ -20,26 +20,35 @@ module Hecks
|
|
|
20
20
|
# model_check` all get a domain that boots and behaves correctly
|
|
21
21
|
# against this adapter; only a real browser gets the real durability.
|
|
22
22
|
#
|
|
23
|
-
#
|
|
23
|
+
# The real browser half lives outside this file entirely: `rust/web`'s
|
|
24
24
|
# `dispatch(json)` (docs/implemented/decisions/0015) takes an optional
|
|
25
25
|
# `"seed"` (the exact `"instances"` shape it also answers with) plus
|
|
26
26
|
# `"steps"` — a host rehydrates from a prior snapshot and replays only
|
|
27
27
|
# the new command(s), rather than the whole history every call. A
|
|
28
28
|
# page bound to this adapter is expected to hold that snapshot in
|
|
29
29
|
# `window.localStorage` itself (get on load, set after every
|
|
30
|
-
# `dispatch`) — the seed/instances round trip
|
|
30
|
+
# `dispatch`) — the seed/instances round trip is the adapter, once
|
|
31
31
|
# you're in the one runtime that can actually reach the storage this
|
|
32
32
|
# name promises.
|
|
33
33
|
class LocalStorage
|
|
34
|
-
#
|
|
34
|
+
# Tenant-capable trivially, same reasoning as Memory's own — a
|
|
35
35
|
# browser tab is exactly one origin, exactly one user; there is no
|
|
36
36
|
# second tenant this in-process Hash could ever confuse a first
|
|
37
37
|
# one with.
|
|
38
|
+
#
|
|
39
|
+
# @return [Boolean] true, always
|
|
38
40
|
def self.tenant_capable? = true
|
|
41
|
+
|
|
42
|
+
# Lists the optional persistence behaviours this adapter advertises.
|
|
43
|
+
#
|
|
44
|
+
# @return [Array<Symbol>] `[:atomic_put]`
|
|
39
45
|
def persistence_capabilities = [:atomic_put]
|
|
40
46
|
|
|
41
47
|
attr_reader :aggregate, :events
|
|
42
48
|
|
|
49
|
+
# @param aggregate [Bluebook::Aggregate] the aggregate this store persists
|
|
50
|
+
# @param settings [Hash] accepted but not read by this class
|
|
51
|
+
# @param root [String, nil] accepted but not read by this class
|
|
43
52
|
def initialize(aggregate:, settings: {}, root: nil)
|
|
44
53
|
@aggregate = aggregate
|
|
45
54
|
@records = {}
|
|
@@ -47,40 +56,86 @@ module Hecks
|
|
|
47
56
|
@entries = []
|
|
48
57
|
end
|
|
49
58
|
|
|
59
|
+
# Reads one record's current projected state.
|
|
60
|
+
#
|
|
61
|
+
# @param id [String, Object] the record's identity, compared as `id.to_s`
|
|
62
|
+
# @return [Runtime::Instance, nil] the stored record, or nil when no record has that id
|
|
50
63
|
def find(id) = @records[id.to_s]
|
|
64
|
+
|
|
65
|
+
# Counts the records currently projected.
|
|
66
|
+
#
|
|
67
|
+
# @return [Integer] number of stored records
|
|
51
68
|
def count = @records.size
|
|
52
69
|
|
|
70
|
+
# Lists every record currently projected.
|
|
71
|
+
#
|
|
72
|
+
# @param order_by [String, Symbol, nil] an attribute name to sort by; nil keeps
|
|
73
|
+
# insertion order
|
|
74
|
+
# @param direction [Symbol] `:asc` or `:desc`
|
|
75
|
+
# @return [Array<Runtime::Instance>] the stored records; `[]` when there are none
|
|
53
76
|
def all(order_by: nil, direction: :asc)
|
|
54
77
|
InMemoryOrdering.ordered(@records.values, aggregate: @aggregate, order_by: order_by, direction: direction)
|
|
55
78
|
end
|
|
56
79
|
|
|
57
|
-
#
|
|
80
|
+
# Answers a declared query specification against the projected records.
|
|
81
|
+
#
|
|
82
|
+
# The decision the guide asks for, made explicitly: no compiled
|
|
58
83
|
# dialect of its own, same as Heki/Memory — a personal-scale local
|
|
59
84
|
# store answering by walking `all` is correct on day one, and
|
|
60
85
|
# nothing about a browser tab's own data volume asks for pushdown.
|
|
86
|
+
#
|
|
87
|
+
# @param specification [QuerySpecification::Common::Options,
|
|
88
|
+
# Bluebook::Behaviour::ReadModel::FilteredOptions] the declared query specification
|
|
89
|
+
# @param args [Hash{Symbol => Object}] bound values for the specification's placeholders
|
|
90
|
+
# @param context [Hash{Symbol => Object}] call context; `:registry` is read and passed
|
|
91
|
+
# through for registry-aware comparisons
|
|
92
|
+
# @return [Array<Runtime::Instance>] the matching records, ordered and paged
|
|
61
93
|
def query(specification, args = {}, context: {})
|
|
62
94
|
Ports::Query::InMemory.execute(all, specification, args, registry: context[:registry])
|
|
63
95
|
end
|
|
64
96
|
|
|
97
|
+
# Records one journal entry, holding a codec copy of its state, the same as `Memory#append`.
|
|
98
|
+
#
|
|
99
|
+
# @param entry [Ports::Persistence::Entry] the save or delete to journal
|
|
100
|
+
# @return [Ports::Persistence::Entry] the caller's own `entry`, not the journalled copy
|
|
65
101
|
def append(entry)
|
|
66
|
-
|
|
102
|
+
copied = Ports::Persistence::Entry.new(operation: entry.operation, id: entry.id,
|
|
103
|
+
state: copy(entry.state), mirrors: entry.mirrors)
|
|
104
|
+
@entries << copied
|
|
67
105
|
entry
|
|
68
106
|
end
|
|
69
107
|
|
|
108
|
+
# Applies one journaled entry to the current-state store.
|
|
109
|
+
#
|
|
110
|
+
# @param entry [Persistence::Entry] the save or delete to materialize
|
|
111
|
+
# @return [Runtime::Instance, nil] on a save, the newly stored record; on a delete, the
|
|
112
|
+
# removed record, or nil when no record had that id
|
|
70
113
|
def project(entry)
|
|
71
114
|
if entry.save?
|
|
72
|
-
@records[entry.id] = Runtime::Instance.new(aggregate: @aggregate, id: entry.id, state: entry.state
|
|
115
|
+
@records[entry.id] = Runtime::Instance.new(aggregate: @aggregate, id: entry.id, state: copy(entry.state))
|
|
73
116
|
else
|
|
74
117
|
@records.delete(entry.id)
|
|
75
118
|
end
|
|
76
119
|
end
|
|
77
120
|
|
|
121
|
+
# Journals and projects an instance's state.
|
|
122
|
+
#
|
|
123
|
+
# @param instance [Runtime::Instance] the record to persist
|
|
124
|
+
# @return [Runtime::Instance] the newly stored record (a copy of `instance`'s state)
|
|
78
125
|
def save(instance)
|
|
79
|
-
entry = Ports::Persistence::Entry.new(operation: "save", id: instance.id.to_s, state: instance.state
|
|
126
|
+
entry = Ports::Persistence::Entry.new(operation: "save", id: instance.id.to_s, state: copy(instance.state))
|
|
80
127
|
append(entry)
|
|
81
128
|
project(entry)
|
|
82
129
|
end
|
|
83
130
|
|
|
131
|
+
# Writes an entry through an existence check, refusing to replace an existing record
|
|
132
|
+
# when asked.
|
|
133
|
+
#
|
|
134
|
+
# @param entry [Persistence::Entry] the entry to persist (its `id` checked for an
|
|
135
|
+
# existing record)
|
|
136
|
+
# @param insert_only [Boolean] true to refuse replacing a record that already exists
|
|
137
|
+
# @return [Symbol] `:conflicted` when `insert_only` met an existing record and nothing
|
|
138
|
+
# was written; `:inserted` or `:replaced` otherwise
|
|
84
139
|
def atomic_put(entry, insert_only: false)
|
|
85
140
|
exists = @records.key?(entry.id.to_s)
|
|
86
141
|
return :conflicted if insert_only && exists
|
|
@@ -91,16 +146,30 @@ module Hecks
|
|
|
91
146
|
status
|
|
92
147
|
end
|
|
93
148
|
|
|
149
|
+
# Journals and projects the removal of one record.
|
|
150
|
+
#
|
|
151
|
+
# @param id [String, Object] the record's identity, compared as `id.to_s`
|
|
152
|
+
# @return [Runtime::Instance, nil] the removed record, or nil when no record had that id
|
|
94
153
|
def delete(id)
|
|
95
154
|
entry = Ports::Persistence::Entry.new(operation: "delete", id: id.to_s, state: nil)
|
|
96
155
|
append(entry)
|
|
97
156
|
project(entry)
|
|
98
157
|
end
|
|
99
158
|
|
|
159
|
+
# Records one emitted event in this adapter's in-memory event log.
|
|
160
|
+
#
|
|
161
|
+
# @param event [Runtime::Event] the event to record
|
|
162
|
+
# @return [Array<Runtime::Event>] the adapter's in-memory event log, including `event`
|
|
100
163
|
def record_event(event) = @events << event
|
|
101
164
|
|
|
165
|
+
# Reads the whole journal, oldest entry first.
|
|
166
|
+
#
|
|
167
|
+
# @return [Array<Persistence::Entry>] a shallow copy of every appended entry
|
|
102
168
|
def entries = @entries.dup
|
|
103
169
|
|
|
170
|
+
# Clears the stored records, events and journal so a kept runtime starts clean.
|
|
171
|
+
#
|
|
172
|
+
# @return [LocalStorage] self, so a caller can chain after resetting
|
|
104
173
|
def reset!
|
|
105
174
|
@records = {}
|
|
106
175
|
@events = []
|
|
@@ -108,7 +177,11 @@ module Hecks
|
|
|
108
177
|
self
|
|
109
178
|
end
|
|
110
179
|
|
|
111
|
-
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def copy(state) = Ports::Persistence::StateCodec.copy(@aggregate, state)
|
|
183
|
+
|
|
184
|
+
# Not lineage_capable? — deliberately absent, the same trade Heki
|
|
112
185
|
# makes and states plainly (writing-an-adapter.md's own section on
|
|
113
186
|
# it): a domain bound here has no edge for its own shape to travel
|
|
114
187
|
# across if it ever changes; that must be hand-migrated, or the
|