hecks 3.9.1 → 3.11.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/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +1 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +1 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +124 -2
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
- data/lib/hecks/deploy/oidc.json +16 -3
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +4 -10
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/clearing.rb +51 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -0
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/site_toolchain/role_prober.rb +78 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +29 -4
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/adapters/world_clearing.rb +65 -0
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +61 -18
- data/lib/hecks/hecks/hecks.world +11 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +15 -0
- data/lib/hecks/projections/deploy/fargate/settings.rb +3 -1
- data/lib/hecks/projections/deploy/fargate/stack/readers.rb +1 -0
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +7 -4
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +95 -0
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +77 -0
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/pretty.rb +40 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +161 -0
- data/lib/hecks/projections/site/cms_editor/setting.rb +130 -0
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +370 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/session.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/sso.ts.tmpl +20 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +133 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +29 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +177 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/multipart.ts.tmpl +53 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/sniff.ts.tmpl +94 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/storage.ts.tmpl +50 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +214 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/server.ts.tmpl +65 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +123 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_parse.js.tmpl +320 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +92 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +63 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +17 -0
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +133 -0
- data/lib/hecks/projections/site/role_probe.rb +74 -0
- data/lib/hecks/projector/cli_audience.rb +2 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +29 -1
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/site_routes/cli.rb +1 -1
- data/lib/hecks/tools/site_routes/editor_files.rb +45 -0
- data/lib/hecks/tools/site_routes.rb +14 -7
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +4 -4
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
- data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
- data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +9 -1
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +11 -9
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +2 -1
- data/rust/codegen/src/mutations.rs +174 -4
- data/rust/codegen/src/naming.rs +30 -0
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +3 -3
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +13 -8
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +73 -4
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/presence.rs +41 -3
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +174 -36
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/prebuilt/0ee7f83cba59ce7d1c45c65ba166b071779f70143449f584f33ff7989f64eccb.marshal +0 -0
- data/prebuilt/6e4a6bcf0aa880e2e6b056e1846b70e5a768090b9dc998bbadeaf5d6bcfd3db8.marshal +0 -0
- data/prebuilt/verdicts-91aaf93ce8981bd9a754948f075d95650dc2ec6435015376f5c60992b36a24b1.json +0 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
require_relative "capabilities/attribute_values"
|
|
2
|
+
|
|
1
3
|
module Hecks
|
|
2
4
|
module Bluebook
|
|
3
5
|
# What a chapter may declare it provides — trusted in place of a literal
|
|
4
6
|
# chapter-name check by role checks, adapters and the fuzzer.
|
|
5
7
|
module Capabilities
|
|
8
|
+
extend AttributeValues
|
|
9
|
+
|
|
6
10
|
AUTHORIZATION = "authorization".freeze
|
|
7
11
|
# Who may sign in and with what role, resolved by declaration rather
|
|
8
12
|
# than an env var naming the aggregate.
|
|
@@ -30,6 +34,31 @@ module Hecks
|
|
|
30
34
|
# declaration rather than the literal name "PaymentConnection".
|
|
31
35
|
PAYMENT_CONNECTION = "payment_connection".freeze
|
|
32
36
|
|
|
37
|
+
# The checkout boundary in front of a payment processor: how long a
|
|
38
|
+
# signed webhook stays fresh and how long a session holds its seat,
|
|
39
|
+
# recognised by declaration rather than the literal chapter name.
|
|
40
|
+
CHECKOUT = "checkout".freeze
|
|
41
|
+
|
|
42
|
+
# Marking a domain's attributes sensitive and reading the markings back, recognised by
|
|
43
|
+
# declaration rather than the literal chapter name "Privacy".
|
|
44
|
+
PRIVACY = "privacy".freeze
|
|
45
|
+
|
|
46
|
+
# A subject's key record, shredded to erase them, recognised by declaration rather than the
|
|
47
|
+
# literal names "Privacy" and "SubjectKey".
|
|
48
|
+
SUBJECT_KEYS = "subject_keys".freeze
|
|
49
|
+
|
|
50
|
+
# The kinds of contract entry a capability may name: `:command` and
|
|
51
|
+
# `:query` ("Aggregate.Name"), `:port_operation` ("Aggregate.Port.Operation")
|
|
52
|
+
# and `:mark` ("Aggregate.mark_name", the states of that aggregate's
|
|
53
|
+
# lifecycle mark) and `:duration` ("Aggregate.attribute", the whole-seconds
|
|
54
|
+
# `default:` of that attribute, ADR 0098). `:mark` and `:duration` entries are
|
|
55
|
+
# optional; every other entry is required.
|
|
56
|
+
#
|
|
57
|
+
# ADR 0099 adds two more optional kinds: `:text` ("Aggregate.attribute", the string
|
|
58
|
+
# `default:` of that attribute, a word the domain owns) and `:attribute`
|
|
59
|
+
# ("Aggregate.attribute", the attribute's own name, for the host to read a field by).
|
|
60
|
+
OPTIONAL_KINDS = %i[mark duration text attribute].freeze
|
|
61
|
+
|
|
33
62
|
CONTRACTS = {
|
|
34
63
|
AUTHORIZATION => {
|
|
35
64
|
# every assignment an actor holds, current or historical
|
|
@@ -57,13 +86,25 @@ module Hecks
|
|
|
57
86
|
}.freeze,
|
|
58
87
|
NEWSLETTER => {
|
|
59
88
|
# a guest signs up; the aggregate it names is the subscriber
|
|
60
|
-
subscribe:
|
|
89
|
+
subscribe: :command,
|
|
61
90
|
# attach a display name to an existing subscriber
|
|
62
|
-
add_name:
|
|
91
|
+
add_name: :command,
|
|
63
92
|
# a subscriber confirms their address from the emailed link
|
|
64
|
-
confirm:
|
|
93
|
+
confirm: :command,
|
|
65
94
|
# a subscriber leaves from the emailed link
|
|
66
|
-
unsubscribe:
|
|
95
|
+
unsubscribe: :command,
|
|
96
|
+
# Optional: the subscriber states that await the emailed confirm
|
|
97
|
+
# link, spelled "Subscriber.awaiting_confirmation"
|
|
98
|
+
awaiting_confirmation: :mark,
|
|
99
|
+
# Optional: the subscriber states that receive each issue sent
|
|
100
|
+
receives_issues: :mark,
|
|
101
|
+
# Optional: the subscriber states of someone who has left
|
|
102
|
+
left: :mark,
|
|
103
|
+
# Optional: how many seconds the emailed confirm link stays valid,
|
|
104
|
+
# spelled "Subscriber.confirm_window"
|
|
105
|
+
confirm_window: :duration,
|
|
106
|
+
# Optional: how many seconds an emailed unsubscribe link stays valid
|
|
107
|
+
unsubscribe_window: :duration
|
|
67
108
|
}.freeze,
|
|
68
109
|
NEWSLETTER_ISSUES => {
|
|
69
110
|
# mark an issue sent; the issue aggregate is the one it names
|
|
@@ -74,17 +115,31 @@ module Hecks
|
|
|
74
115
|
}.freeze,
|
|
75
116
|
PAYMENTS => {
|
|
76
117
|
# start a payment; the aggregate it names is the payment
|
|
77
|
-
initiate:
|
|
118
|
+
initiate: :command,
|
|
78
119
|
# the processor reports the money arrived
|
|
79
|
-
succeeded:
|
|
120
|
+
succeeded: :port_operation,
|
|
80
121
|
# the processor reports the payment failed or expired
|
|
81
|
-
failed:
|
|
122
|
+
failed: :port_operation,
|
|
123
|
+
# Optional: the lifecycle states of the payment that hold a seat,
|
|
124
|
+
# spelled "Payment.holds_seat" (Aggregate.mark_name); resolves to the
|
|
125
|
+
# states of that aggregate's lifecycle `mark :holds_seat`
|
|
126
|
+
holds_seat: :mark,
|
|
127
|
+
# Optional: the failure reason a lapsed checkout hold records, spelled
|
|
128
|
+
# "Payment.lapse_reason" (the string `default:` of that attribute)
|
|
129
|
+
lapse_reason: :text
|
|
82
130
|
}.freeze,
|
|
83
131
|
REGISTRATIONS => {
|
|
84
132
|
# schedule a session; the aggregate it names is the event
|
|
85
|
-
schedule:
|
|
133
|
+
schedule: :command,
|
|
86
134
|
# a guest asks for a place; the aggregate it names is the registration
|
|
87
|
-
request:
|
|
135
|
+
request: :command,
|
|
136
|
+
# Optional: the registration attribute that stamps when it was asked for,
|
|
137
|
+
# spelled "Registration.requested_at"
|
|
138
|
+
registered_at: :attribute,
|
|
139
|
+
# Optional: the registration states that give a seat back, spelled
|
|
140
|
+
# "Registration.frees_seat" (Aggregate.mark_name); resolves to the
|
|
141
|
+
# states of that aggregate's lifecycle `mark :frees_seat`
|
|
142
|
+
frees_seat: :mark
|
|
88
143
|
}.freeze,
|
|
89
144
|
PAYMENT_CONNECTION => {
|
|
90
145
|
# link the processor account; the aggregate it names is the connection
|
|
@@ -101,8 +156,35 @@ module Hecks
|
|
|
101
156
|
enable: :command,
|
|
102
157
|
# switch taking payments off
|
|
103
158
|
disable: :command
|
|
159
|
+
}.freeze,
|
|
160
|
+
PRIVACY => {
|
|
161
|
+
# flag one attribute of a domain sensitive
|
|
162
|
+
mark_sensitive: :command,
|
|
163
|
+
# every marking of one domain, for masking a read
|
|
164
|
+
markings_for: :query
|
|
165
|
+
}.freeze,
|
|
166
|
+
SUBJECT_KEYS => {
|
|
167
|
+
# the key record of one subject, if any
|
|
168
|
+
key_for: :query,
|
|
169
|
+
# record the subject's key as destroyed
|
|
170
|
+
shred: :command
|
|
171
|
+
}.freeze,
|
|
172
|
+
CHECKOUT => {
|
|
173
|
+
# Optional: how many seconds either side of now a signed webhook's
|
|
174
|
+
# timestamp may sit, spelled "WebhookReceipt.tolerance"
|
|
175
|
+
webhook_tolerance: :duration,
|
|
176
|
+
# Optional: how many seconds a session holds its seat
|
|
177
|
+
session_hold: :duration
|
|
104
178
|
}.freeze
|
|
105
179
|
}.freeze
|
|
180
|
+
|
|
181
|
+
# The keys a capability's `provides` must name.
|
|
182
|
+
#
|
|
183
|
+
# @param capability [String] a `CONTRACTS` key
|
|
184
|
+
# @return [Array<Symbol>] the keys whose kind is not optional
|
|
185
|
+
def self.required_keys(capability)
|
|
186
|
+
CONTRACTS.fetch(capability).reject { |_, kind| OPTIONAL_KINDS.include?(kind) }.keys
|
|
187
|
+
end
|
|
106
188
|
end
|
|
107
189
|
end
|
|
108
190
|
end
|
|
@@ -14,13 +14,22 @@ module Hecks
|
|
|
14
14
|
|
|
15
15
|
attr_reader :hecks_name, :attributes, :emits, :direction, :answers, :refuses, :to
|
|
16
16
|
|
|
17
|
+
# Marks this operation as the one the hecksagon picks (`ask_via`) when its ask name is
|
|
18
|
+
# declared on more than one port of the aggregate.
|
|
19
|
+
#
|
|
20
|
+
# @return [true]
|
|
21
|
+
def choose! = @chosen = true
|
|
22
|
+
|
|
23
|
+
# @return [Boolean] whether the hecksagon picked this operation with `ask_via`
|
|
24
|
+
def chosen? = @chosen == true
|
|
25
|
+
|
|
17
26
|
# `:inbound` (`tells`) is an external fact arriving as an event; it emits and nothing more.
|
|
18
27
|
# `:outbound` (`asks`) is the domain wanting something from outside, naming both
|
|
19
28
|
# `answers` and `refuses` so the failure is visible to the model.
|
|
20
29
|
#
|
|
21
30
|
# Every optional keyword and what it holds when the declaration omits it.
|
|
22
31
|
FIELD_DEFAULTS = {
|
|
23
|
-
attributes: [], emits: [], direction: :inbound, answers: nil, refuses: nil, to: nil
|
|
32
|
+
attributes: [], emits: [], direction: :inbound, answers: nil, refuses: nil, to: nil, chosen: false
|
|
24
33
|
}.freeze
|
|
25
34
|
|
|
26
35
|
# @param name [String, Symbol] the operation's declared name
|
|
@@ -30,6 +39,7 @@ module Hecks
|
|
|
30
39
|
# @param answers [String, nil] an outbound operation's event for the adapter's answer
|
|
31
40
|
# @param refuses [String, nil] an outbound operation's event for the adapter's refusal
|
|
32
41
|
# @param to [String, nil] the aggregate this operation routes to, if any
|
|
42
|
+
# @param chosen [Boolean] whether the hecksagon picked this operation with `ask_via`
|
|
33
43
|
def initialize(name:, **given)
|
|
34
44
|
fields = KeywordFields.fill(given, FIELD_DEFAULTS)
|
|
35
45
|
@hecks_name = name.to_s
|
|
@@ -48,15 +58,17 @@ module Hecks
|
|
|
48
58
|
# @return [Boolean] whether this operation is a `tells`/`operation`
|
|
49
59
|
def inbound? = @direction == :inbound
|
|
50
60
|
|
|
51
|
-
# `direction`/`answers`/`refuses`/`to` are merged in only when set: the Rust parser
|
|
52
|
-
# emits none of them, so an unconditional key would break parser_parity_spec.
|
|
61
|
+
# `direction`/`answers`/`refuses`/`to`/`chosen` are merged in only when set: the Rust parser
|
|
62
|
+
# emits none of them unprompted, so an unconditional key would break parser_parity_spec.
|
|
53
63
|
#
|
|
54
64
|
# @return [Hash] the declared emission, plus `direction`/`answers`/`refuses` for an
|
|
55
|
-
# outbound operation
|
|
65
|
+
# outbound operation, `to` when a routing target is declared, and `chosen` when the
|
|
66
|
+
# hecksagon picked it with `ask_via`
|
|
56
67
|
def to_h
|
|
57
68
|
shape = super
|
|
58
69
|
shape = shape.merge(direction: @direction.to_s, answers: @answers, refuses: @refuses) unless inbound?
|
|
59
70
|
shape = shape.merge(to: @to) if @to
|
|
71
|
+
shape = shape.merge(chosen: true) if chosen?
|
|
60
72
|
shape
|
|
61
73
|
end
|
|
62
74
|
end
|
|
@@ -8,13 +8,17 @@ module Hecks
|
|
|
8
8
|
class AdapterBuilder
|
|
9
9
|
GRAMMAR_CONTEXT = "Adapter".freeze
|
|
10
10
|
|
|
11
|
+
# Marks a field that gives no `default:`; `nil` and `false` are defaults too.
|
|
12
|
+
NO_DEFAULT = Object.new.freeze
|
|
13
|
+
|
|
11
14
|
include WordGate
|
|
12
15
|
|
|
13
16
|
# @param name [String] the adapter's name, as written after `Hecks.adapter`
|
|
14
17
|
def initialize(name)
|
|
15
|
-
@name
|
|
16
|
-
@fields
|
|
17
|
-
@secrets
|
|
18
|
+
@name = name
|
|
19
|
+
@fields = []
|
|
20
|
+
@secrets = []
|
|
21
|
+
@defaults = {}
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
# Names the port this adapter implements; a repeated call replaces the earlier name.
|
|
@@ -26,8 +30,13 @@ module Hecks
|
|
|
26
30
|
# Declares one plain setting a `.world` file may supply for this adapter.
|
|
27
31
|
#
|
|
28
32
|
# @param name [Symbol, String] the setting's name, such as `:database`
|
|
33
|
+
# @param default [Object] the value used when the world gives none; a string may hold
|
|
34
|
+
# `{name}` or `{storage_name}`, filled from the aggregate the adapter is built for
|
|
29
35
|
# @return [Array<Symbol>] every field declared so far, this one last
|
|
30
|
-
def field(name
|
|
36
|
+
def field(name, default: NO_DEFAULT)
|
|
37
|
+
@defaults[name.to_sym] = default unless default.equal?(NO_DEFAULT)
|
|
38
|
+
@fields << name.to_sym
|
|
39
|
+
end
|
|
31
40
|
|
|
32
41
|
# Declares one setting whose value is a secret, such as an API token.
|
|
33
42
|
#
|
|
@@ -35,12 +44,15 @@ module Hecks
|
|
|
35
44
|
# @return [Array<Symbol>] every secret declared so far, this one last
|
|
36
45
|
def secret(name) = @secrets << name.to_sym
|
|
37
46
|
|
|
38
|
-
# Assembles the collected port, fields and secrets, judged by the adapter
|
|
47
|
+
# Assembles the collected port, fields, defaults and secrets, judged by the adapter
|
|
48
|
+
# language.
|
|
39
49
|
#
|
|
40
50
|
# @return [Bluebook::Adapter] the adapter, returned once the language accepts it
|
|
41
51
|
# @raise [Bluebook::DSL::Malformed] if the adapter language refuses the declaration
|
|
42
52
|
def build
|
|
43
|
-
MetaValidator.call_adapter(
|
|
53
|
+
MetaValidator.call_adapter(
|
|
54
|
+
Adapter.new(name: @name, port: @port, fields: @fields, secrets: @secrets, defaults: @defaults)
|
|
55
|
+
)
|
|
44
56
|
end
|
|
45
57
|
|
|
46
58
|
# Evaluates an `Hecks.adapter` block against a fresh builder and returns what it built.
|
|
@@ -29,6 +29,18 @@ module Hecks
|
|
|
29
29
|
self
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Picks which port answers an ask this aggregate declares on more than one port.
|
|
33
|
+
#
|
|
34
|
+
# @param name [String, Symbol] the ask's declared name, such as `"Probe"`
|
|
35
|
+
# @param port [String, Symbol] the port whose `asks` of that name is the one policies reach
|
|
36
|
+
# @return [BindingProxy] self
|
|
37
|
+
# @raise [Bluebook::DSL::Malformed] if no such aggregate, port or ask is declared yet
|
|
38
|
+
def ask_via(name, port:)
|
|
39
|
+
domain, aggregate_name = @fqn.split("::")
|
|
40
|
+
AskResolution.pick!(Hecks.current_registry.bluebook(domain), aggregate_name, name, port)
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
|
|
32
44
|
def method_missing(verb, *args, **kwargs, &block)
|
|
33
45
|
# A bare call (no args/kwargs/block) starts a Privacy marking chain instead
|
|
34
46
|
# of a Bind — no real `.hecksagon` bind is ever called with zero arguments,
|
|
@@ -68,6 +80,7 @@ module Hecks
|
|
|
68
80
|
operation.attributes.select(&:reference?).each { |attribute| attribute.type.declared_in = aggregate_ir }
|
|
69
81
|
end
|
|
70
82
|
aggregate_ir.add_port(built)
|
|
83
|
+
AskResolution.bind_resolved(Hecks.current_registry.bluebook(@fqn.split("::").first))
|
|
71
84
|
end
|
|
72
85
|
end
|
|
73
86
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module DSL
|
|
4
|
+
class BluebookBuilder
|
|
5
|
+
module Validation
|
|
6
|
+
# Checks that every name and dotted path a computed `sets` source reads resolves, once
|
|
7
|
+
# every aggregate and value object in the chapter exists.
|
|
8
|
+
module ComputedSources
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# Refuses a computed source whose operand path names nothing declared, runs through
|
|
12
|
+
# a list or a reference, or ends on a value object with more than one field.
|
|
13
|
+
def validate_computed_sources!(bluebook)
|
|
14
|
+
value_objects = bluebook.aggregates.flat_map(&:value_objects).to_h { |vo| [vo.hecks_name.to_s, vo] }
|
|
15
|
+
bluebook.aggregates.each do |aggregate|
|
|
16
|
+
aggregate.commands.each { |command| check_computed_command!(aggregate, command, value_objects) }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def check_computed_command!(aggregate, command, value_objects)
|
|
21
|
+
command.mutations.select { |mutation| mutation.source.is_a?(Computed) }.each do |mutation|
|
|
22
|
+
operand_paths(Expression::Resolver.parse(mutation.source.text)).each do |path|
|
|
23
|
+
check_operand_path!(PathCheck.new(aggregate, command, mutation, value_objects), path)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Where a path is read: the aggregate and command it belongs to, the mutation, and the
|
|
29
|
+
# chapter's value objects by name.
|
|
30
|
+
PathCheck = Struct.new(:aggregate, :command, :mutation, :value_objects)
|
|
31
|
+
private_constant :PathCheck
|
|
32
|
+
|
|
33
|
+
def operand_paths(node)
|
|
34
|
+
return [node.path] if node.is_a?(Expression::Resolver::Lookup)
|
|
35
|
+
return [] unless node.is_a?(Struct)
|
|
36
|
+
|
|
37
|
+
node.members.flat_map { |member| operand_paths(node[member]) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def check_operand_path!(check, path)
|
|
41
|
+
head, *segments = path.split(".")
|
|
42
|
+
attribute = operand_head(check, head)
|
|
43
|
+
return if attribute.nil? && segments.empty? && stored_scalar?(check, head)
|
|
44
|
+
|
|
45
|
+
refuse_operand!(check, path, "names no argument or field of #{check.aggregate.hecks_name}") unless attribute
|
|
46
|
+
walk_operand_path!(check, path, attribute, segments)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The command's argument, else the owner's field.
|
|
50
|
+
def operand_head(check, name)
|
|
51
|
+
check.command.attributes.find { |attr| attr.name.to_s == name } ||
|
|
52
|
+
check.aggregate.attributes.find { |attr| attr.name.to_s == name }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# A lifecycle or projected field is a plain scalar of the record, so a name that stops
|
|
56
|
+
# there needs no further check.
|
|
57
|
+
def stored_scalar?(check, name)
|
|
58
|
+
check.aggregate.lifecycle&.field.to_s == name || check.aggregate.projected_fields.any? { |f| f.name.to_s == name }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def walk_operand_path!(check, path, attribute, segments)
|
|
62
|
+
segments.each do |segment|
|
|
63
|
+
refuse_operand!(check, path, "runs through #{attribute.name}, which is a list") if attribute.list?
|
|
64
|
+
refuse_operand!(check, path, "runs through #{attribute.name}, which is a reference") if attribute.reference?
|
|
65
|
+
attribute = field_of(check, path, attribute, segment)
|
|
66
|
+
end
|
|
67
|
+
refuse_operand!(check, path, "is a list") if attribute.list?
|
|
68
|
+
refuse_operand!(check, path, "is a reference") if attribute.reference?
|
|
69
|
+
refuse_multi_field!(check, path, attribute)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def field_of(check, path, attribute, segment)
|
|
73
|
+
holder = check.value_objects[attribute.type.to_s]
|
|
74
|
+
refuse_operand!(check, path, "runs through #{attribute.name}, which is not a value object") unless holder
|
|
75
|
+
holder.attributes.find { |field| field.name.to_s == segment } ||
|
|
76
|
+
refuse_operand!(check, path, "names #{segment}, which #{holder.hecks_name} does not declare")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# A name that stops on a value object reads as its one field, so it needs exactly one.
|
|
80
|
+
def refuse_multi_field!(check, path, attribute)
|
|
81
|
+
holder = check.value_objects[attribute.type.to_s]
|
|
82
|
+
return unless holder && holder.attributes.size > 1
|
|
83
|
+
|
|
84
|
+
example = "#{path}.#{holder.attributes.first.name}"
|
|
85
|
+
refuse_operand!(check, path, "is a #{holder.hecks_name} with several fields — name one, as #{example}")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def refuse_operand!(check, path, reason)
|
|
89
|
+
raise Malformed,
|
|
90
|
+
"#{check.aggregate.hecks_name}.#{check.command.hecks_name}'s sets :#{check.mutation.target} reads " \
|
|
91
|
+
"#{path}, which #{reason}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Bluebook
|
|
3
|
+
module DSL
|
|
4
|
+
class BluebookBuilder
|
|
5
|
+
module Validation
|
|
6
|
+
# The names a `for_each` policy's `with:` may read off a fan-out row, mirroring what
|
|
7
|
+
# `PolicyInterpreter::Arguments#trigger_args` offers a projection at runtime.
|
|
8
|
+
module FanOutRows
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# The names a row of the policy's `for_each` query carries: the row's id, the
|
|
12
|
+
# aggregate's attributes, lifecycle field and projected fields, and the key the
|
|
13
|
+
# trigger addresses a row by. Nil when the query's aggregate is not in this chapter,
|
|
14
|
+
# which leaves the source unchecked.
|
|
15
|
+
#
|
|
16
|
+
# @return [Array<Symbol>, nil]
|
|
17
|
+
def fan_out_row_names(policy, aggregates, target)
|
|
18
|
+
return nil if policy.for_each.to_s.empty? || policy.for_each.to_s.include?("::")
|
|
19
|
+
|
|
20
|
+
aggregate_name = policy.for_each.to_s.split(".", 2).first
|
|
21
|
+
aggregate = aggregates.find { |candidate| candidate.hecks_name == aggregate_name }
|
|
22
|
+
return nil unless aggregate
|
|
23
|
+
|
|
24
|
+
[:id, *row_field_names(aggregate), *row_key_names(target, aggregate_name)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def row_field_names(aggregate)
|
|
28
|
+
names = aggregate.attributes.map(&:name) + aggregate.projected_fields.map(&:name)
|
|
29
|
+
names += aggregate.identity_heads.map(&:to_sym)
|
|
30
|
+
aggregate.lifecycle ? names << aggregate.lifecycle.field.to_sym : names
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def row_key_names(target, aggregate_name)
|
|
34
|
+
key = target&.addressing_key_for(aggregate_name)
|
|
35
|
+
key ? [key.to_sym] : []
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -4,7 +4,8 @@ module Hecks
|
|
|
4
4
|
class BluebookBuilder
|
|
5
5
|
module Validation
|
|
6
6
|
# Checks that each `projects` reference resolves to an aggregate declaring the remote
|
|
7
|
-
# field as a scalar, once every aggregate in the chapter
|
|
7
|
+
# field as a scalar or single-field value object, once every aggregate in the chapter
|
|
8
|
+
# exists.
|
|
8
9
|
module ProjectedFields
|
|
9
10
|
private
|
|
10
11
|
|
|
@@ -65,8 +66,19 @@ module Hecks
|
|
|
65
66
|
"value, never a reference, a value object, or a list"
|
|
66
67
|
end
|
|
67
68
|
|
|
69
|
+
# A scalar, or a single-field value object, which a projection unwraps to its one
|
|
70
|
+
# scalar (ADR 0025 addendum); a multi-field value object, a reference and a list are
|
|
71
|
+
# not.
|
|
68
72
|
def projectable_scalar?(target, attribute)
|
|
69
|
-
|
|
73
|
+
return false if attribute.list? || attribute.reference?
|
|
74
|
+
|
|
75
|
+
value_object = target.value_object(attribute.type)
|
|
76
|
+
value_object.nil? || single_scalar_field?(value_object)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def single_scalar_field?(value_object)
|
|
80
|
+
sole = value_object.sole_attribute
|
|
81
|
+
!sole.nil? && !sole.list? && !sole.reference?
|
|
70
82
|
end
|
|
71
83
|
end
|
|
72
84
|
end
|
|
@@ -5,6 +5,15 @@ module Hecks
|
|
|
5
5
|
module Validation
|
|
6
6
|
# Checks every `provides` row of a chapter against `Capabilities::CONTRACTS`.
|
|
7
7
|
module Provisions
|
|
8
|
+
# The checks for the kinds whose verb is not a command or a query.
|
|
9
|
+
SPECIAL_KIND_CHECKS = {
|
|
10
|
+
port_operation: :validate_provided_port_operation!,
|
|
11
|
+
mark: :validate_provided_mark!,
|
|
12
|
+
duration: :validate_provided_duration!,
|
|
13
|
+
text: :validate_provided_text!,
|
|
14
|
+
attribute: :validate_provided_attribute!
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
8
17
|
private
|
|
9
18
|
|
|
10
19
|
# Checks every `provides` row against `Capabilities::CONTRACTS`.
|
|
@@ -26,14 +35,26 @@ module Hecks
|
|
|
26
35
|
|
|
27
36
|
def refuse_wrong_provision_keys!(bluebook, capability, rows, contract)
|
|
28
37
|
keys = rows.map { |row| row.key.to_sym }
|
|
29
|
-
|
|
38
|
+
required = Capabilities.required_keys(capability)
|
|
39
|
+
return if provision_keys_valid?(keys, required, contract.keys)
|
|
30
40
|
|
|
31
41
|
raise Malformed, "#{bluebook.name} provides #{capability.inspect} with #{keys.join(", ")}, but " \
|
|
32
|
-
"#{capability} needs
|
|
42
|
+
"#{capability} needs #{provision_keys_phrase(required, contract.keys - required)}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def provision_keys_valid?(keys, required, allowed)
|
|
46
|
+
(keys - allowed).empty? && (required - keys).empty? && keys.uniq.size == keys.size
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def provision_keys_phrase(required, optional)
|
|
50
|
+
return "exactly #{required.join(", ")}" if optional.empty?
|
|
51
|
+
|
|
52
|
+
"#{required.join(", ")} and may add #{optional.join(", ")}"
|
|
33
53
|
end
|
|
34
54
|
|
|
35
55
|
def validate_provided_verb!(bluebook, capability, row, kind)
|
|
36
|
-
|
|
56
|
+
special = SPECIAL_KIND_CHECKS[kind]
|
|
57
|
+
return send(special, bluebook, capability, row) if special
|
|
37
58
|
|
|
38
59
|
aggregate_name, member = row.verb.split(".", 2)
|
|
39
60
|
aggregate = bluebook.aggregate(aggregate_name)
|
|
@@ -54,6 +75,51 @@ module Hecks
|
|
|
54
75
|
"which is not spelled \"Aggregate.Port.Operation\" over an aggregate this chapter declares"
|
|
55
76
|
end
|
|
56
77
|
|
|
78
|
+
# A mark verb is spelled "Aggregate.mark_name" and must name a mark declared on that
|
|
79
|
+
# aggregate's lifecycle.
|
|
80
|
+
def validate_provided_mark!(bluebook, capability, row)
|
|
81
|
+
aggregate_name, mark = row.verb.split(".", 2)
|
|
82
|
+
aggregate = bluebook.aggregate(aggregate_name)
|
|
83
|
+
return if aggregate && mark && aggregate.lifecycle&.marked(mark)
|
|
84
|
+
|
|
85
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect} #{row.key}: #{row.verb.inspect}, " \
|
|
86
|
+
"which names no lifecycle mark this chapter declares (spelled " \
|
|
87
|
+
"\"Aggregate.mark_name\", with `mark :mark_name, ...` in that aggregate's lifecycle)"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# A duration verb is spelled "Aggregate.attribute" and must name an attribute of
|
|
91
|
+
# that aggregate whose `default:` is a whole, positive number of seconds (a bare
|
|
92
|
+
# integer, or the `{ value: N }` fill of a one-field value object).
|
|
93
|
+
def validate_provided_duration!(bluebook, capability, row)
|
|
94
|
+
return if Capabilities.duration_of(bluebook, row.verb)
|
|
95
|
+
|
|
96
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect} #{row.key}: #{row.verb.inspect}, " \
|
|
97
|
+
"which names no attribute with a whole-seconds default this chapter declares " \
|
|
98
|
+
"(spelled \"Aggregate.attribute\", with `attribute :attribute, Seconds, " \
|
|
99
|
+
"default: { value: 1800 }` in that aggregate)"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A text verb is spelled "Aggregate.attribute" and must name an attribute of that
|
|
103
|
+
# aggregate whose `default:` is a non-empty string (bare, or a one-field value object's
|
|
104
|
+
# `{ value: "..." }` fill).
|
|
105
|
+
def validate_provided_text!(bluebook, capability, row)
|
|
106
|
+
return if Capabilities.text_of(bluebook, row.verb)
|
|
107
|
+
|
|
108
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect} #{row.key}: #{row.verb.inspect}, " \
|
|
109
|
+
"which names no attribute with a text default this chapter declares " \
|
|
110
|
+
"(spelled \"Aggregate.attribute\", with `attribute :attribute, Reason, " \
|
|
111
|
+
"default: { value: \"word\" }` in that aggregate)"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# An attribute verb is spelled "Aggregate.attribute" and must name an attribute of that
|
|
115
|
+
# aggregate.
|
|
116
|
+
def validate_provided_attribute!(bluebook, capability, row)
|
|
117
|
+
return if Capabilities.attribute_of(bluebook, row.verb)
|
|
118
|
+
|
|
119
|
+
raise Malformed, "#{bluebook.name} provides #{capability.inspect} #{row.key}: #{row.verb.inspect}, " \
|
|
120
|
+
"which names no attribute this chapter declares (spelled \"Aggregate.attribute\")"
|
|
121
|
+
end
|
|
122
|
+
|
|
57
123
|
def provided_member_names(aggregate, kind)
|
|
58
124
|
kind == :command ? aggregate.commands.map(&:hecks_name) : aggregate.queries.map(&:name)
|
|
59
125
|
end
|
|
@@ -11,18 +11,20 @@ module Hecks
|
|
|
11
11
|
WithContext = Struct.new(:lookup, :aggregates, :correlation_heads)
|
|
12
12
|
# One `with:` to check: the command it targets, the event it reads from, the spec
|
|
13
13
|
# itself, how a refusal names it, and the process manager when it is a saga leg.
|
|
14
|
-
WithSpec = Struct.new(:command_ref, :event_name, :with_spec, :label, :process_manager)
|
|
14
|
+
WithSpec = Struct.new(:command_ref, :event_name, :with_spec, :label, :process_manager, :row_names)
|
|
15
15
|
# What a `with:` source may read: the correlation key, the emitter's identity heads and
|
|
16
|
-
# the event shapes whose fields are legal
|
|
17
|
-
|
|
16
|
+
# the event shapes whose fields are legal, and a fan-out row's names (empty off a
|
|
17
|
+
# fan-out).
|
|
18
|
+
Readable = Struct.new(:correlation, :identity, :shapes, :row)
|
|
18
19
|
private_constant :WithContext, :WithSpec, :Readable
|
|
19
20
|
|
|
20
21
|
private
|
|
21
22
|
|
|
22
23
|
# Checks `with:` projections against the source event's shape and the target's fields.
|
|
23
24
|
# Same-chapter only: an unresolvable cross-chapter source or target is skipped.
|
|
24
|
-
# A for_each policy's source is a query row
|
|
25
|
-
# is
|
|
25
|
+
# A for_each policy's source is the event plus a query row; the row's names are known
|
|
26
|
+
# only when the query's aggregate is in this chapter, otherwise only the target is
|
|
27
|
+
# checked.
|
|
26
28
|
def validate_with_projections!(policies, process_managers, aggregates)
|
|
27
29
|
context = WithContext.new(command_lookup(aggregates), aggregates, correlation_heads(process_managers))
|
|
28
30
|
|
|
@@ -37,9 +39,13 @@ module Hecks
|
|
|
37
39
|
def check_policy_with!(policy, context)
|
|
38
40
|
return if policy.with_spec.to_a.empty?
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
check_with_spec!(policy_with_spec(policy, context), context)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def policy_with_spec(policy, context)
|
|
46
|
+
row = fan_out_row_names(policy, context.aggregates, context.lookup[policy.trigger_command])
|
|
47
|
+
source_event = policy.for_each.to_s.empty? || row ? policy.on_event : nil
|
|
48
|
+
WithSpec.new(policy.trigger_command, source_event, policy.with_spec, "#{policy.name}'s trigger", nil, row)
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
def check_dispatch_with!(process_manager, handler, dispatch, context)
|
|
@@ -47,7 +53,7 @@ module Hecks
|
|
|
47
53
|
|
|
48
54
|
label = "#{process_manager.name}'s dispatch #{dispatch.command_name}"
|
|
49
55
|
check_with_spec!(WithSpec.new(dispatch.command_name, handler.event_type, dispatch.with_spec,
|
|
50
|
-
label, process_manager), context)
|
|
56
|
+
label, process_manager, nil), context)
|
|
51
57
|
end
|
|
52
58
|
|
|
53
59
|
# Checks one `with:` spec: each key must be a field the target command accepts,
|
|
@@ -73,7 +79,7 @@ module Hecks
|
|
|
73
79
|
identity = pm.nil? && event_name ? event_identity_heads_for(event_name, aggregates) : []
|
|
74
80
|
shapes = [event_name && event_shape_for(event_name, aggregates),
|
|
75
81
|
pm && event_shape_for(pm.starts_on, aggregates)].compact
|
|
76
|
-
Readable.new(pm&.correlates_by && pm.correlation_head, identity, shapes)
|
|
82
|
+
Readable.new(pm&.correlates_by && pm.correlation_head, identity, shapes, spec.row_names.to_a)
|
|
77
83
|
end
|
|
78
84
|
|
|
79
85
|
def refuse_undeclared_with_field!(spec, field)
|
|
@@ -84,6 +90,7 @@ module Hecks
|
|
|
84
90
|
def refuse_unreadable_with_source!(spec, source, readable)
|
|
85
91
|
return unless source.is_a?(::Symbol)
|
|
86
92
|
return if source == readable.correlation || readable.identity.include?(source)
|
|
93
|
+
return if readable.row.include?(source)
|
|
87
94
|
return if readable.shapes.empty? || shape_reads?(readable.shapes, source)
|
|
88
95
|
|
|
89
96
|
raise Malformed,
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
require_relative "validation/provisions"
|
|
2
2
|
require_relative "validation/references"
|
|
3
3
|
require_relative "validation/event_shapes"
|
|
4
|
+
require_relative "validation/fan_out_rows"
|
|
4
5
|
require_relative "validation/with_projections"
|
|
5
6
|
require_relative "validation/query_hops"
|
|
6
7
|
require_relative "validation/projected_fields"
|
|
8
|
+
require_relative "validation/computed_sources"
|
|
7
9
|
require_relative "validation/correlation_keys"
|
|
8
10
|
|
|
9
11
|
module Hecks
|
|
@@ -17,9 +19,11 @@ module Hecks
|
|
|
17
19
|
include Provisions
|
|
18
20
|
include References
|
|
19
21
|
include EventShapes
|
|
22
|
+
include FanOutRows
|
|
20
23
|
include WithProjections
|
|
21
24
|
include QueryHops
|
|
22
25
|
include ProjectedFields
|
|
26
|
+
include ComputedSources
|
|
23
27
|
include CorrelationKeys
|
|
24
28
|
|
|
25
29
|
# Runs every whole-chapter check against one assembled chapter.
|
|
@@ -58,6 +62,7 @@ module Hecks
|
|
|
58
62
|
infer_hop_query_arguments!(bluebook)
|
|
59
63
|
validate_query_hops!(bluebook)
|
|
60
64
|
validate_projected_fields!(bluebook)
|
|
65
|
+
validate_computed_sources!(bluebook)
|
|
61
66
|
end
|
|
62
67
|
end
|
|
63
68
|
end
|