hecks 3.13.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/pizzas/adapters/fake_processor.rb +34 -0
- data/examples/pizzas/adapters/payment_signature.rb +66 -0
- data/examples/pizzas/adapters/payment_webhook.rb +87 -0
- data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
- data/examples/pizzas/bluebook/pizzas.bluebook +4 -4
- data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +1 -7
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +15 -5
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
- data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
- data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
- data/lib/hecks/bluebook/hecksagon.rb +1 -1
- data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
- data/lib/hecks/bluebook/meta_validator.rb +0 -11
- data/lib/hecks/bluebook/named_pattern.rb +33 -0
- data/lib/hecks/bluebook/translation.rb +18 -10
- data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
- data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
- data/lib/hecks/bluebook/translation_expression.rb +39 -0
- data/lib/hecks/bluebook/translation_plan.rb +70 -0
- data/lib/hecks/bluebook.rb +3 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/cli/domain_stub.rb +1 -1
- data/lib/hecks/cli/interview_aggregate.rb +4 -4
- data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
- data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
- data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
- data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
- data/lib/hecks/composer/adapters/jev.adapter +3 -0
- data/lib/hecks/composer/adapters/jev.rb +66 -0
- data/lib/hecks/composer/agent.rb +113 -0
- data/lib/hecks/composer/assistant.rb +110 -0
- data/lib/hecks/composer/behavior_run.rb +53 -0
- data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
- data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
- data/lib/hecks/composer/board.rb +65 -0
- data/lib/hecks/composer/board_edit/command_text.rb +39 -0
- data/lib/hecks/composer/board_edit/source.rb +84 -0
- data/lib/hecks/composer/board_edit.rb +144 -0
- data/lib/hecks/composer/check.rb +89 -0
- data/lib/hecks/composer/domains.rb +81 -0
- data/lib/hecks/composer/jobs.rb +134 -0
- data/lib/hecks/composer/layout.rb +37 -0
- data/lib/hecks/composer/live.rb +150 -0
- data/lib/hecks/composer/outline.rb +48 -0
- data/lib/hecks/composer/page.html +989 -0
- data/lib/hecks/composer/panels.rb +142 -0
- data/lib/hecks/composer/parse.rb +83 -0
- data/lib/hecks/composer/plain.rb +28 -0
- data/lib/hecks/composer/ports/composer_agent.port +7 -0
- data/lib/hecks/composer/session.rb +141 -0
- data/lib/hecks/composer/toolbox.rb +166 -0
- data/lib/hecks/composer/web.rb +156 -0
- data/lib/hecks/composer/workbench.rb +51 -0
- data/lib/hecks/composer/workspace.rb +81 -0
- data/lib/hecks/composer.rb +17 -0
- data/lib/hecks/corpus/adapters.rb +22 -0
- data/lib/hecks/corpus/rust.rb +1 -1
- data/lib/hecks/corpus/worlds.rb +19 -0
- data/lib/hecks/corpus.rb +5 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -34
- data/lib/hecks/forms/app/commands.rb +2 -3
- data/lib/hecks/forms/app/queries.rb +2 -3
- data/lib/hecks/forms/app/records.rb +17 -5
- data/lib/hecks/forms/command_form_renderer.rb +42 -16
- data/lib/hecks/forms/field_controls.rb +2 -2
- data/lib/hecks/forms/field_renderer.rb +1 -1
- data/lib/hecks/forms/field_shape.rb +36 -17
- data/lib/hecks/forms/index_renderer.rb +2 -1
- data/lib/hecks/forms/page.rb +1 -1
- data/lib/hecks/forms/params.rb +0 -13
- data/lib/hecks/forms/query_form_renderer.rb +7 -6
- data/lib/hecks/forms/record_renderer.rb +11 -7
- data/lib/hecks/forms/rule_words.rb +100 -0
- data/lib/hecks/forms/value_object_shape.rb +2 -1
- data/lib/hecks/framework/bluebook/change.bluebook +2 -2
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- data/lib/hecks/fuzzing/domain_generator/block_rendering.rb +1 -1
- data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
- data/lib/hecks/fuzzing/replay/filters.rb +2 -6
- data/lib/hecks/gate/stages.yml +25 -0
- data/lib/hecks/grammar/evolve/seeds.rb +0 -6
- data/lib/hecks/grammar/evolve.rb +0 -3
- data/lib/hecks/grammar/translation.bluebook +1 -1
- data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
- data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
- data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
- data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
- data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
- data/lib/hecks/hecks/codebase.behaviors +6 -0
- data/lib/hecks/hecks/codebase.bluebook +58 -37
- data/lib/hecks/hecks/composer.behaviors +68 -0
- data/lib/hecks/hecks/context_map.hecksagon +1 -1
- data/lib/hecks/hecks/custodian.behaviors +0 -6
- data/lib/hecks/hecks/custodian.bluebook +15 -92
- data/lib/hecks/hecks/hecks.bluebook +2 -2
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +10 -16
- data/lib/hecks/hecks/tooling.bluebook +22 -22
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/aggregate.bluebook +1 -0
- data/lib/hecks/language/bluebook/command.bluebook +1 -0
- data/lib/hecks/language/bluebook/entity.bluebook +1 -0
- data/lib/hecks/language/bluebook/query.bluebook +1 -0
- data/lib/hecks/language/bluebook/shape.bluebook +8 -7
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- data/lib/hecks/language/hecksagon/port_operation.bluebook +1 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
- data/lib/hecks/ports/access_control.rb +2 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
- data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
- data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
- data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
- data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
- data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
- data/lib/hecks/ports.rb +0 -1
- data/lib/hecks/projection_files.rb +3 -1
- data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
- data/lib/hecks/projections/glossary/entries.rb +10 -4
- data/lib/hecks/projections/glossary/groups.rb +42 -0
- data/lib/hecks/projections/glossary/html/page.rb +7 -2
- data/lib/hecks/projections/glossary/html/parser.rb +1 -1
- data/lib/hecks/projections/glossary/html/sections.rb +31 -11
- data/lib/hecks/projections/glossary/html.rb +1 -2
- data/lib/hecks/projections/glossary/markdown.rb +31 -28
- data/lib/hecks/projections/glossary/mermaid.rb +3 -0
- data/lib/hecks/projections/glossary/page.css +143 -153
- data/lib/hecks/projections/glossary/page.js +63 -3
- data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
- data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
- data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
- data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
- data/lib/hecks/projections/glossary/sentences.rb +13 -3
- data/lib/hecks/projections/glossary/slugs.rb +4 -1
- data/lib/hecks/projections/glossary.rb +2 -1
- data/lib/hecks/projections/plain_english.rb +97 -0
- data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
- data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
- data/lib/hecks/projections/rust_vocabulary.rb +14 -6
- data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
- data/lib/hecks/projections/statements/actions.rb +99 -0
- data/lib/hecks/projections/statements/relationships.rb +86 -0
- data/lib/hecks/projections/statements.rb +15 -24
- data/lib/hecks/projector/exporter/adapters.rb +112 -0
- data/lib/hecks/projector/exporter/translations.rb +18 -35
- data/lib/hecks/projector/exporter/worlds.rb +103 -0
- data/lib/hecks/projector/exporter.rb +5 -1
- data/lib/hecks/quality_control/adapters/agent.rb +6 -7
- data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
- data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +51 -51
- data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
- data/lib/hecks/runtime/outbox.rb +2 -9
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
- data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
- data/lib/hecks/runtime/value.rb +13 -0
- data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
- data/lib/hecks/rust_build/wasm.rb +0 -12
- data/lib/hecks/rust_build/write_if_changed.rb +12 -12
- data/lib/hecks/rust_format.rb +77 -0
- data/lib/hecks/site/bluebook/site.bluebook +5 -5
- data/lib/hecks/sme/bluebook/sme.bluebook +12 -12
- data/lib/hecks/tickets/bluebook/tickets.bluebook +3 -3
- data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
- data/lib/hecks/tools/fuzz_sweep.rb +1 -3
- data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
- data/lib/hecks/tools/translation_audit.rb +8 -10
- data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +2 -1
- data/lib/hecks.rb +3 -0
- data/prebuilt/{58208399bada10b0db99928c48e38789288fc58f7b71a72b21bfa386680e32ed.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
- data/prebuilt/{2b59737fbdbd5f44edda0fddce2611f45d76087b3a7c782d1f1a17ea58c77948.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
- data/prebuilt/verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json +1 -0
- data/rust/Cargo.toml +40 -0
- data/rust/build/Cargo.toml +36 -0
- data/rust/build/clippy.toml +4 -0
- data/rust/build/src/adapters_pass.rs +196 -0
- data/rust/build/src/binding_pass.rs +54 -13
- data/rust/build/src/build_artifact.rs +23 -5
- data/rust/build/src/cargo_sync.rs +69 -26
- data/rust/build/src/lineage_pass.rs +101 -87
- data/rust/build/src/main.rs +11 -2
- data/rust/build/src/pipeline.rs +80 -19
- data/rust/build/src/reserved_names.rs +6 -50
- data/rust/build/src/resolve.rs +82 -35
- data/rust/build/src/rustfmt.rs +100 -0
- data/rust/build/src/seams_pass.rs +85 -16
- data/rust/build/src/subprocess.rs +30 -6
- data/rust/build/src/tmp.rs +5 -2
- data/rust/build/src/translation_pass.rs +72 -22
- data/rust/build/src/world_pass.rs +109 -0
- data/rust/clippy.toml +4 -0
- data/rust/codegen/Cargo.toml +36 -0
- data/rust/codegen/clippy.toml +4 -0
- data/rust/codegen/src/asks.rs +40 -14
- data/rust/codegen/src/attr.rs +2 -2
- data/rust/codegen/src/bridging.rs +676 -117
- data/rust/codegen/src/commands.rs +979 -373
- data/rust/codegen/src/constraints.rs +88 -25
- data/rust/codegen/src/dependency_planning.rs +128 -34
- data/rust/codegen/src/domain_generator.rs +300 -170
- data/rust/codegen/src/exemplar.rs +87 -32
- data/rust/codegen/src/expr_emitter.rs +54 -6
- data/rust/codegen/src/fielded.rs +193 -41
- data/rust/codegen/src/hecks_naming.rs +124 -16
- data/rust/codegen/src/hecks_naming_tests.rs +81 -0
- data/rust/codegen/src/json.rs +80 -44
- data/rust/codegen/src/json_codec.rs +585 -149
- data/rust/codegen/src/literal.rs +104 -138
- data/rust/codegen/src/main.rs +145 -52
- data/rust/codegen/src/manifest.rs +76 -14
- data/rust/codegen/src/mutations.rs +1212 -239
- data/rust/codegen/src/naming.rs +111 -60
- data/rust/codegen/src/optional_pass.rs +90 -28
- data/rust/codegen/src/ports.rs +5 -4
- data/rust/codegen/src/prelude.rs +139 -49
- data/rust/codegen/src/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +606 -205
- data/rust/codegen/src/reference_specs.rs +16 -3
- data/rust/codegen/src/registry.rs +128 -79
- data/rust/codegen/src/reserved_names.rs +6 -50
- data/rust/codegen/src/shared.rs +71 -22
- data/rust/codegen/src/sidecars.rs +15 -5
- data/rust/codegen/src/skip_reason.rs +8 -2
- data/rust/codegen/src/types.rs +306 -108
- data/rust/host/Cargo.lock +1 -0
- data/rust/host/Cargo.toml +40 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/clippy.toml +4 -0
- data/rust/host/src/api.rs +698 -218
- data/rust/host/src/auth.rs +491 -214
- data/rust/host/src/b64.rs +91 -0
- data/rust/host/src/bin/expr_harness.rs +6 -2
- data/rust/host/src/bin/lineage_harness.rs +47 -11
- data/rust/host/src/bin/mint_harness.rs +70 -37
- data/rust/host/src/bin/validate_harness.rs +16 -8
- data/rust/host/src/boot.rs +144 -68
- data/rust/host/src/boundary_fuzz/api.rs +130 -32
- data/rust/host/src/boundary_fuzz/auth.rs +108 -23
- data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
- data/rust/host/src/boundary_fuzz/ir.rs +164 -12
- data/rust/host/src/boundary_fuzz/server.rs +283 -36
- data/rust/host/src/boundary_fuzz/web.rs +56 -8
- data/rust/host/src/checkout.rs +78 -32
- data/rust/host/src/clock.rs +8 -0
- data/rust/host/src/commerce_ir.rs +333 -95
- data/rust/host/src/crypto.rs +33 -0
- data/rust/host/src/dispatch.rs +990 -331
- data/rust/host/src/expr_json.rs +1021 -273
- data/rust/host/src/extension.rs +24 -4
- data/rust/host/src/field_hints.rs +11 -7
- data/rust/host/src/fuzz_support.rs +85 -13
- data/rust/host/src/host.rs +100 -0
- data/rust/host/src/http.rs +22 -0
- data/rust/host/src/ir.rs +326 -16
- data/rust/host/src/journal.rs +277 -176
- data/rust/host/src/lambda_client.rs +219 -53
- data/rust/host/src/lib.rs +16 -4
- data/rust/host/src/log.rs +62 -11
- data/rust/host/src/mint.rs +1153 -326
- data/rust/host/src/needs.rs +185 -44
- data/rust/host/src/payments/keystore.rs +197 -45
- data/rust/host/src/payments/roles.rs +191 -0
- data/rust/host/src/payments/tests.rs +1445 -275
- data/rust/host/src/payments.rs +409 -121
- data/rust/host/src/platform_signin/attack_tests.rs +5 -5
- data/rust/host/src/platform_signin/config.rs +11 -11
- data/rust/host/src/platform_signin/cookie.rs +2 -2
- data/rust/host/src/platform_signin/extension.rs +2 -1
- data/rust/host/src/platform_signin/handler.rs +9 -8
- data/rust/host/src/platform_signin/jti.rs +4 -2
- data/rust/host/src/platform_signin/jwks.rs +2 -1
- data/rust/host/src/platform_signin/jws.rs +2 -37
- data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
- data/rust/host/src/platform_signin/mode_tests.rs +5 -3
- data/rust/host/src/platform_signin/pages.rs +20 -20
- data/rust/host/src/platform_signin/session.rs +8 -1
- data/rust/host/src/platform_signin/settings.rs +6 -6
- data/rust/host/src/platform_signin/strict_json.rs +12 -9
- data/rust/host/src/platform_signin/verify.rs +41 -41
- data/rust/host/src/platform_signin/wiring_support.rs +13 -18
- data/rust/host/src/presentation.rs +207 -63
- data/rust/host/src/presentation_write.rs +502 -187
- data/rust/host/src/query_step.rs +69 -18
- data/rust/host/src/rate_limit.rs +541 -142
- data/rust/host/src/reference_transform.rs +77 -25
- data/rust/host/src/reference_validate.rs +443 -104
- data/rust/host/src/resend.rs +190 -55
- data/rust/host/src/secrets.rs +116 -21
- data/rust/host/src/server.rs +483 -104
- data/rust/host/src/storage_shape.rs +88 -21
- data/rust/host/src/test_pg.rs +17 -7
- data/rust/host/src/ui_schema.rs +495 -207
- data/rust/host/src/wasm_runner.rs +21 -6
- data/rust/host/src/web/commerce.rs +101 -23
- data/rust/host/src/web/newsletter/cooldown.rs +10 -3
- data/rust/host/src/web/newsletter/template.rs +26 -7
- data/rust/host/src/web/newsletter/tests.rs +112 -23
- data/rust/host/src/web/newsletter.rs +209 -59
- data/rust/host/src/web/newsletter_send.rs +251 -79
- data/rust/host/src/web/registration_receipt/tests.rs +16 -4
- data/rust/host/src/web/registration_receipt.rs +100 -24
- data/rust/host/src/web/registrations.rs +1384 -332
- data/rust/host/src/web/resend_webhook.rs +138 -80
- data/rust/host/src/web.rs +2416 -508
- data/rust/lsp/Cargo.toml +36 -0
- data/rust/lsp/clippy.toml +4 -0
- data/rust/lsp/src/json.rs +29 -22
- data/rust/lsp/src/main.rs +81 -31
- data/rust/lsp/src/outline.rs +23 -7
- data/rust/lsp/src/rpc.rs +17 -4
- data/rust/parser/Cargo.toml +36 -0
- data/rust/parser/clippy.toml +4 -0
- data/rust/parser/src/build/closed_sets.rs +6 -1
- data/rust/parser/src/build/identity.rs +4 -3
- data/rust/parser/src/build/mod.rs +1 -0
- data/rust/parser/src/build/named_pattern.rs +26 -0
- data/rust/parser/src/build/naming.rs +2 -80
- data/rust/parser/src/build/pattern_subset.rs +6 -8
- data/rust/parser/src/build/query_derive.rs +3 -1
- data/rust/parser/src/build/query_inference.rs +14 -5
- data/rust/parser/src/build/read_model.rs +4 -1
- data/rust/parser/src/build/references.rs +4 -3
- data/rust/parser/src/canonical.rs +34 -15
- data/rust/parser/src/emit.rs +187 -105
- data/rust/parser/src/expr/ast_json.rs +134 -42
- data/rust/parser/src/expr/evaluator.rs +27 -9
- data/rust/parser/src/expr/mod.rs +54 -13
- data/rust/parser/src/expr/resolver.rs +299 -92
- data/rust/parser/src/ir.rs +35 -3
- data/rust/parser/src/keywords.rs +7168 -510
- data/rust/parser/src/lex.rs +11 -25
- data/rust/parser/src/lib.rs +1 -0
- data/rust/parser/src/main.rs +65 -6
- data/rust/parser/src/parse/adapter.rs +157 -0
- data/rust/parser/src/parse/aggregate.rs +19 -10
- data/rust/parser/src/parse/chapter.rs +16 -17
- data/rust/parser/src/parse/command.rs +35 -16
- data/rust/parser/src/parse/domain_port.rs +4 -7
- data/rust/parser/src/parse/entity.rs +21 -11
- data/rust/parser/src/parse/hecksagon.rs +10 -4
- data/rust/parser/src/parse/lifecycle.rs +9 -5
- data/rust/parser/src/parse/mod.rs +43 -91
- data/rust/parser/src/parse/needs.rs +21 -12
- data/rust/parser/src/parse/policy.rs +2 -2
- data/rust/parser/src/parse/process_manager.rs +9 -11
- data/rust/parser/src/parse/query.rs +8 -4
- data/rust/parser/src/parse/read_model.rs +57 -16
- data/rust/parser/src/parse/value_object.rs +7 -2
- data/rust/parser/src/parse/world.rs +322 -0
- data/rust/parser/src/ruby_value.rs +7 -6
- data/rust/src/exemplar/json.rs +4 -4
- data/rust/src/exemplar/mutations.rs +18 -0
- data/rust/src/exemplar/read_models.rs +1 -10
- data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
- data/rust/src/kernel/attribute_shapes/list.rs +2 -2
- data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
- data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
- data/rust/src/kernel/cli.rs +229 -112
- data/rust/src/kernel/dispatch.rs +323 -115
- data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
- data/rust/src/kernel/expr.rs +203 -86
- data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
- data/rust/src/kernel/expression_operators/comparison.rs +87 -2
- data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
- data/rust/src/kernel/expression_operators/logical.rs +9 -3
- data/rust/src/kernel/expression_operators/membership.rs +3 -1
- data/rust/src/kernel/expression_operators/mod.rs +34 -21
- data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
- data/rust/src/kernel/expression_operators/positional.rs +84 -30
- data/rust/src/kernel/expression_operators/presence.rs +52 -13
- data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
- data/rust/src/kernel/expression_operators/sized.rs +24 -8
- data/rust/src/kernel/expression_operators/text.rs +120 -27
- data/rust/src/kernel/expression_operators/to_string.rs +10 -4
- data/rust/src/kernel/json.rs +230 -148
- data/rust/src/kernel/mod.rs +46 -33
- data/rust/src/kernel/named_query.rs +63 -15
- data/rust/src/kernel/needs.rs +112 -25
- data/rust/src/kernel/orchestrate.rs +980 -581
- data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
- data/rust/src/kernel/pattern.rs +107 -34
- data/rust/src/kernel/query_comparators.rs +118 -41
- data/rust/src/kernel/query_ordering.rs +13 -5
- data/rust/src/kernel/read_model.rs +479 -181
- data/rust/src/kernel/reference_lookup.rs +122 -26
- data/rust/src/kernel/repository.rs +293 -66
- data/rust/src/kernel/routing.rs +58 -42
- data/rust/src/kernel/ruby_float.rs +139 -0
- data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
- data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
- data/rust/src/kernel/vocab/field_hint.rs +19 -25
- data/rust/src/kernel/vocab/mod.rs +6 -6
- data/rust/src/kernel/vocab/query_comparator.rs +23 -24
- data/rust/src/kernel/vocab/refusal_template.rs +501 -297
- data/rust/src/lib.rs +50 -0
- data/rust/src/main.rs +4 -1
- data/rust/web/Cargo.toml +36 -0
- data/rust/web/clippy.toml +4 -0
- metadata +85 -15
- data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
- data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
- data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
- data/lib/hecks/ports/authentication.port +0 -6
- data/lib/hecks/ports/authentication.rb +0 -67
- data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
- data/prebuilt/verdicts-48318ca172edd21443b1ad02801c697fff100b746e828f09db987647641a5df2.json +0 -1
- data/rust/host/src/approval.rs +0 -652
data/rust/src/kernel/cli.rs
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
//! The stdin/stdout JSON CLI shared by the native binary and the wasm32-wasip1 module.
|
|
2
2
|
//! `run` reads `{"steps": [...]}` and hands each step to `kernel::orchestrate` (ADR 0012).
|
|
3
3
|
|
|
4
|
-
use super::{
|
|
5
|
-
|
|
4
|
+
use super::{
|
|
5
|
+
named_query, orchestrate, query_comparators, read_model, repository, AggregateScan,
|
|
6
|
+
CompletedCompensation, Event, Json, MutationRecord, PendingCrossDomainReaction, Refusal,
|
|
7
|
+
SagaInstance, Tables,
|
|
8
|
+
};
|
|
9
|
+
use crate::generated::active::{
|
|
10
|
+
command_attributes_for_verb, command_creates, dispatch_by_name, entity_identity_head_for_path,
|
|
11
|
+
identity_head_for_aggregate, reference_key_for_aggregate, Store, CROSS_DOMAIN_POLICIES,
|
|
12
|
+
POLICIES, PROCESS_MANAGERS, QUERIES, READ_MODELS,
|
|
13
|
+
};
|
|
6
14
|
use std::collections::HashMap;
|
|
7
15
|
|
|
8
16
|
// Builds and invariant-checks a named query's value-object arguments (ADR 0037).
|
|
@@ -23,9 +31,8 @@ pub fn run(input: &str) -> String {
|
|
|
23
31
|
// Optional `"defaults"`: each command's declared argument defaults, filled the same way.
|
|
24
32
|
super::needs::install_defaults(parsed.get("defaults"));
|
|
25
33
|
|
|
26
|
-
let steps =
|
|
27
|
-
|
|
28
|
-
None => return error_output("expected a top-level {\"steps\": [...]} object"),
|
|
34
|
+
let Some(steps) = parsed.get("steps").and_then(Json::as_array) else {
|
|
35
|
+
return error_output("expected a top-level {\"steps\": [...]} object");
|
|
29
36
|
};
|
|
30
37
|
|
|
31
38
|
// Optional `"seed"`: prior state as `"instances"` emits it, so a host need not replay history.
|
|
@@ -40,34 +47,46 @@ pub fn run(input: &str) -> String {
|
|
|
40
47
|
let mut refusals: Vec<(String, Refusal)> = Vec::new();
|
|
41
48
|
// Process-manager instances are domain-level, so not a `Store` field.
|
|
42
49
|
// Optional `"sagas"` seeds in-flight saga state the way `"seed"` seeds aggregates.
|
|
43
|
-
let mut sagas: HashMap<(String, String), SagaInstance> =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
50
|
+
let mut sagas: HashMap<(String, String), SagaInstance> = parsed
|
|
51
|
+
.get("sagas")
|
|
52
|
+
.and_then(Json::as_array)
|
|
53
|
+
.map_or_else(HashMap::new, |entries| {
|
|
54
|
+
entries
|
|
55
|
+
.iter()
|
|
56
|
+
.filter_map(|entry| {
|
|
57
|
+
let process_manager = entry.get("process_manager")?.as_str()?.to_string();
|
|
58
|
+
let correlation = entry.get("correlation")?.as_str()?.to_string();
|
|
59
|
+
let state = entry.get("state")?.as_str()?.to_string();
|
|
60
|
+
let memory = entry
|
|
61
|
+
.get("memory")
|
|
62
|
+
.cloned()
|
|
63
|
+
.unwrap_or_else(|| Json::Object(vec![]));
|
|
64
|
+
// Optional: an instance persisted without a ledger rehydrates with an empty one.
|
|
65
|
+
let completed_compensations = entry
|
|
66
|
+
.get("completed_compensations")
|
|
67
|
+
.and_then(Json::as_array)
|
|
68
|
+
.map(|entries| {
|
|
69
|
+
entries
|
|
70
|
+
.iter()
|
|
71
|
+
.filter_map(|e| {
|
|
72
|
+
let command_name = e.get("command_name")?.as_str()?.to_string();
|
|
73
|
+
let args = e.get("args").cloned().unwrap_or(Json::Null);
|
|
74
|
+
Some(CompletedCompensation { command_name, args })
|
|
75
|
+
})
|
|
76
|
+
.collect()
|
|
77
|
+
})
|
|
78
|
+
.unwrap_or_default();
|
|
79
|
+
Some((
|
|
80
|
+
(process_manager, correlation),
|
|
81
|
+
SagaInstance {
|
|
82
|
+
state,
|
|
83
|
+
memory,
|
|
84
|
+
completed_compensations,
|
|
85
|
+
},
|
|
86
|
+
))
|
|
87
|
+
})
|
|
88
|
+
.collect()
|
|
89
|
+
});
|
|
71
90
|
// One entry per step, parallel to `steps`, so a host can read `.last()` for the newest step.
|
|
72
91
|
let mut mutations_per_step: Vec<Vec<MutationRecord>> = Vec::new();
|
|
73
92
|
// Parallel to `mutations_per_step`: policies that named a domain not compiled into this
|
|
@@ -92,7 +111,11 @@ pub fn run(input: &str) -> String {
|
|
|
92
111
|
// for the run alone (ADR 0081), so the log holds no value that depends on the clock.
|
|
93
112
|
let offered = args;
|
|
94
113
|
let filled = match step.get("query") {
|
|
95
|
-
Some(Json::Str(question)) => super::needs::enrich_query(
|
|
114
|
+
Some(Json::Str(question)) => super::needs::enrich_query(
|
|
115
|
+
question,
|
|
116
|
+
args,
|
|
117
|
+
step.get("occurred_at").and_then(Json::as_str),
|
|
118
|
+
),
|
|
96
119
|
_ => None,
|
|
97
120
|
};
|
|
98
121
|
let args = filled.as_ref().unwrap_or(args);
|
|
@@ -110,27 +133,15 @@ pub fn run(input: &str) -> String {
|
|
|
110
133
|
// reference twin is the same engine.
|
|
111
134
|
// `reference_interpret` delegates to the same `entity_rows`).
|
|
112
135
|
Json::Str(question) if question.contains("::") && named_query::find_entity(ENTITY_QUERIES, question).is_some() => {
|
|
136
|
+
// reason: the match guard above only admits a `question` that resolves.
|
|
137
|
+
#[allow(clippy::expect_used)]
|
|
113
138
|
let def = named_query::find_entity(ENTITY_QUERIES, question).expect("matched by the guard above");
|
|
114
139
|
match named_query::run_entity(&store, def, args) {
|
|
115
140
|
Ok(rows) => {
|
|
116
|
-
|
|
117
|
-
query_results.push(Json::obj(vec![
|
|
118
|
-
("query", Json::Str(question.clone())),
|
|
119
|
-
("args", offered.clone()),
|
|
120
|
-
("rows", rows.clone()),
|
|
121
|
-
("reference_rows", rows),
|
|
122
|
-
]));
|
|
141
|
+
query_results.push(answered_entry(question, offered, Json::Array(rows), true));
|
|
123
142
|
}
|
|
124
143
|
Err(refusal) => {
|
|
125
|
-
|
|
126
|
-
query_results.push(Json::obj(vec![
|
|
127
|
-
("query", Json::Str(question.clone())),
|
|
128
|
-
("args", offered.clone()),
|
|
129
|
-
("rows", Json::Null),
|
|
130
|
-
("error", Json::Str(message.clone())),
|
|
131
|
-
("reference_rows", Json::Null),
|
|
132
|
-
("reference_error", Json::Str(message)),
|
|
133
|
-
]));
|
|
144
|
+
query_results.push(refused_entry(question, offered, &refusal, true));
|
|
134
145
|
refusals.push((question.clone(), refusal));
|
|
135
146
|
}
|
|
136
147
|
}
|
|
@@ -139,26 +150,13 @@ pub fn run(input: &str) -> String {
|
|
|
139
150
|
Some(def) => match check_query_args(question, args).and_then(|()| named_query::run(&store, def, args, caller_role)) {
|
|
140
151
|
Ok(entries) => {
|
|
141
152
|
let rows = Json::Array(entries.into_iter().map(|(id, record)| repository::row_json(id, record)).collect());
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
("args", offered.clone()),
|
|
145
|
-
("rows", rows.clone()),
|
|
146
|
-
// Same as `rows`: one compiled path (named_query.rs).
|
|
147
|
-
("reference_rows", rows),
|
|
148
|
-
]));
|
|
153
|
+
// The reference twin is `rows`: one compiled path (named_query.rs).
|
|
154
|
+
query_results.push(answered_entry(question, offered, rows, true));
|
|
149
155
|
}
|
|
150
156
|
// A refused query still gets an entry with `rows: null`, as in Ruby; one
|
|
151
157
|
// compiled path means `error` and `reference_error` are the same string.
|
|
152
158
|
Err(refusal) => {
|
|
153
|
-
|
|
154
|
-
query_results.push(Json::obj(vec![
|
|
155
|
-
("query", Json::Str(question.clone())),
|
|
156
|
-
("args", offered.clone()),
|
|
157
|
-
("rows", Json::Null),
|
|
158
|
-
("error", Json::Str(message.clone())),
|
|
159
|
-
("reference_rows", Json::Null),
|
|
160
|
-
("reference_error", Json::Str(message)),
|
|
161
|
-
]));
|
|
159
|
+
query_results.push(refused_entry(question, offered, &refusal, true));
|
|
162
160
|
refusals.push((question.clone(), refusal));
|
|
163
161
|
}
|
|
164
162
|
},
|
|
@@ -179,24 +177,11 @@ pub fn run(input: &str) -> String {
|
|
|
179
177
|
},
|
|
180
178
|
Json::Str(question) => match read_model::find(READ_MODELS, question) {
|
|
181
179
|
Some(def) => match read_model::run(&store, def, args) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
("args", offered.clone()),
|
|
186
|
-
("rows", Json::Array(vec![row])),
|
|
187
|
-
// No `reference_rows` key: Ruby sets it only for questions with a
|
|
188
|
-
// reference twin, and a read model has none.
|
|
189
|
-
]));
|
|
190
|
-
}
|
|
191
|
-
// As above, minus the reference keys.
|
|
180
|
+
// No reference keys: Ruby sets them only for questions with a reference twin,
|
|
181
|
+
// and a read model has none.
|
|
182
|
+
Ok(row) => query_results.push(answered_entry(question, offered, Json::Array(vec![row]), false)),
|
|
192
183
|
Err(refusal) => {
|
|
193
|
-
|
|
194
|
-
query_results.push(Json::obj(vec![
|
|
195
|
-
("query", Json::Str(question.clone())),
|
|
196
|
-
("args", offered.clone()),
|
|
197
|
-
("rows", Json::Null),
|
|
198
|
-
("error", Json::Str(message)),
|
|
199
|
-
]));
|
|
184
|
+
query_results.push(refused_entry(question, offered, &refusal, false));
|
|
200
185
|
refusals.push((question.clone(), refusal));
|
|
201
186
|
}
|
|
202
187
|
},
|
|
@@ -235,16 +220,21 @@ pub fn run(input: &str) -> String {
|
|
|
235
220
|
// See `actor_id` on the command step below.
|
|
236
221
|
let caller_actor_id = step.get("actor_id").and_then(Json::as_str);
|
|
237
222
|
let command_input = dry_run_command_input(args);
|
|
238
|
-
dry_runs.push(dry_run(
|
|
223
|
+
dry_runs.push(dry_run(
|
|
224
|
+
&store,
|
|
225
|
+
verb,
|
|
226
|
+
&command_input,
|
|
227
|
+
caller_role,
|
|
228
|
+
caller_actor_id,
|
|
229
|
+
));
|
|
239
230
|
mutations_per_step.push(Vec::new());
|
|
240
231
|
cross_domain_per_step.push(Vec::new());
|
|
241
232
|
reactions_per_step.push(Json::Array(Vec::new()));
|
|
242
233
|
continue;
|
|
243
234
|
}
|
|
244
235
|
|
|
245
|
-
let verb =
|
|
246
|
-
|
|
247
|
-
None => return error_output("step missing \"verb\", \"dry_run\", or \"query\""),
|
|
236
|
+
let Some(verb) = step.get("verb").and_then(Json::as_str) else {
|
|
237
|
+
return error_output("step missing \"verb\", \"dry_run\", or \"query\"");
|
|
248
238
|
};
|
|
249
239
|
|
|
250
240
|
// Per-step `role:` stands in for Ruby's ambient `Hecks.as_caller`; passed to the top-level
|
|
@@ -306,19 +296,34 @@ pub fn run(input: &str) -> String {
|
|
|
306
296
|
let refusals_json = Json::Array(
|
|
307
297
|
refusals
|
|
308
298
|
.iter()
|
|
309
|
-
.map(|(verb, r)|
|
|
299
|
+
.map(|(verb, r)| {
|
|
300
|
+
Json::obj(vec![
|
|
301
|
+
("verb", Json::str(verb.clone())),
|
|
302
|
+
("error", Json::str(r.to_string())),
|
|
303
|
+
("kind", Json::str(r.kind().to_string())),
|
|
304
|
+
])
|
|
305
|
+
})
|
|
310
306
|
.collect(),
|
|
311
307
|
);
|
|
312
308
|
let mutations_json = Json::Array(
|
|
313
309
|
mutations_per_step
|
|
314
310
|
.iter()
|
|
315
|
-
.map(|step_mutations|
|
|
311
|
+
.map(|step_mutations| {
|
|
312
|
+
Json::Array(step_mutations.iter().map(mutation_to_json).collect())
|
|
313
|
+
})
|
|
316
314
|
.collect(),
|
|
317
315
|
);
|
|
318
316
|
let cross_domain_json = Json::Array(
|
|
319
317
|
cross_domain_per_step
|
|
320
318
|
.iter()
|
|
321
|
-
.map(|step_reactions|
|
|
319
|
+
.map(|step_reactions| {
|
|
320
|
+
Json::Array(
|
|
321
|
+
step_reactions
|
|
322
|
+
.iter()
|
|
323
|
+
.map(cross_domain_reaction_to_json)
|
|
324
|
+
.collect(),
|
|
325
|
+
)
|
|
326
|
+
})
|
|
322
327
|
.collect(),
|
|
323
328
|
);
|
|
324
329
|
|
|
@@ -332,7 +337,10 @@ pub fn run(input: &str) -> String {
|
|
|
332
337
|
("cross_domain_reactions".to_string(), cross_domain_json),
|
|
333
338
|
// Whole-run logs, not per-step.
|
|
334
339
|
("reactions".to_string(), Json::Array(reaction_log)),
|
|
335
|
-
(
|
|
340
|
+
(
|
|
341
|
+
"reactions_per_step".to_string(),
|
|
342
|
+
Json::Array(reactions_per_step),
|
|
343
|
+
),
|
|
336
344
|
("sagas".to_string(), Json::Array(saga_log)),
|
|
337
345
|
// State at the end of the run, fed back as `"sagas"` next time; the `"sagas"` key above
|
|
338
346
|
// is the transition log, including refused transitions.
|
|
@@ -343,11 +351,31 @@ pub fn run(input: &str) -> String {
|
|
|
343
351
|
|
|
344
352
|
/// Evaluates a command against a throwaway copy of the store; `"error"` carries the refusal
|
|
345
353
|
/// a real dispatch would have raised.
|
|
346
|
-
fn dry_run(
|
|
354
|
+
fn dry_run(
|
|
355
|
+
store: &Store,
|
|
356
|
+
verb: &str,
|
|
357
|
+
command_input: &Json,
|
|
358
|
+
caller_role: Option<&str>,
|
|
359
|
+
caller_actor_id: Option<&str>,
|
|
360
|
+
) -> Json {
|
|
347
361
|
let mut scratch = store.clone();
|
|
348
|
-
match dispatch_by_name(
|
|
349
|
-
|
|
350
|
-
|
|
362
|
+
match dispatch_by_name(
|
|
363
|
+
&mut scratch,
|
|
364
|
+
verb,
|
|
365
|
+
command_input,
|
|
366
|
+
caller_role,
|
|
367
|
+
caller_actor_id,
|
|
368
|
+
&mut Vec::new(),
|
|
369
|
+
) {
|
|
370
|
+
Ok(_) => Json::obj(vec![
|
|
371
|
+
("verb", Json::str(verb.to_string())),
|
|
372
|
+
("ok", Json::Bool(true)),
|
|
373
|
+
]),
|
|
374
|
+
Err(refusal) => Json::obj(vec![
|
|
375
|
+
("verb", Json::str(verb.to_string())),
|
|
376
|
+
("ok", Json::Bool(false)),
|
|
377
|
+
("error", Json::str(refusal.to_string())),
|
|
378
|
+
]),
|
|
351
379
|
}
|
|
352
380
|
}
|
|
353
381
|
|
|
@@ -365,14 +393,17 @@ fn tables() -> Tables<'static> {
|
|
|
365
393
|
}
|
|
366
394
|
}
|
|
367
395
|
|
|
396
|
+
/// Saga instances by (saga, correlation id), as `serve` keeps them across steps.
|
|
397
|
+
type SagaMap = HashMap<(String, String), SagaInstance>;
|
|
398
|
+
|
|
368
399
|
/// `--serve` mode: one JSON step per stdin line, one answer per stdout line, store kept across
|
|
369
400
|
/// lines. Beyond `verb` it takes `dry_run`, `snapshot`, `restore` and `instances` steps.
|
|
370
401
|
pub fn serve(input: impl std::io::BufRead, mut output: impl std::io::Write) {
|
|
371
402
|
let mut store = Store::new();
|
|
372
|
-
let mut sagas
|
|
403
|
+
let mut sagas = SagaMap::new();
|
|
373
404
|
let mut reaction_log: Vec<Json> = Vec::new();
|
|
374
405
|
let mut saga_log: Vec<Json> = Vec::new();
|
|
375
|
-
let mut snapshot: Option<(Store,
|
|
406
|
+
let mut snapshot: Option<(Store, SagaMap)> = None;
|
|
376
407
|
let ok = || Json::obj(vec![("ok", Json::Bool(true))]);
|
|
377
408
|
|
|
378
409
|
for line in input.lines() {
|
|
@@ -381,7 +412,10 @@ pub fn serve(input: impl std::io::BufRead, mut output: impl std::io::Write) {
|
|
|
381
412
|
continue;
|
|
382
413
|
}
|
|
383
414
|
let answer = match Json::parse(&line) {
|
|
384
|
-
Err(e) => Json::obj(vec![
|
|
415
|
+
Err(e) => Json::obj(vec![
|
|
416
|
+
("ok", Json::Bool(false)),
|
|
417
|
+
("error", Json::str(format!("invalid JSON: {e}"))),
|
|
418
|
+
]),
|
|
385
419
|
Ok(step) => {
|
|
386
420
|
let empty_args = Json::Object(vec![]);
|
|
387
421
|
let args = step.get("args").unwrap_or(&empty_args);
|
|
@@ -395,15 +429,27 @@ pub fn serve(input: impl std::io::BufRead, mut output: impl std::io::Write) {
|
|
|
395
429
|
match &snapshot {
|
|
396
430
|
Some((s, g)) => {
|
|
397
431
|
store = s.clone();
|
|
398
|
-
sagas
|
|
432
|
+
sagas.clone_from(g);
|
|
399
433
|
ok()
|
|
400
434
|
}
|
|
401
|
-
None => Json::obj(vec![
|
|
435
|
+
None => Json::obj(vec![
|
|
436
|
+
("ok", Json::Bool(false)),
|
|
437
|
+
("error", Json::str("no snapshot to restore")),
|
|
438
|
+
]),
|
|
402
439
|
}
|
|
403
440
|
} else if step.get("instances").is_some() {
|
|
404
|
-
Json::obj(vec![
|
|
441
|
+
Json::obj(vec![
|
|
442
|
+
("ok", Json::Bool(true)),
|
|
443
|
+
("instances", Json::Object(store.instances())),
|
|
444
|
+
])
|
|
405
445
|
} else if let Some(verb) = step.get("dry_run").and_then(Json::as_str) {
|
|
406
|
-
dry_run(
|
|
446
|
+
dry_run(
|
|
447
|
+
&store,
|
|
448
|
+
verb,
|
|
449
|
+
&dry_run_command_input(args),
|
|
450
|
+
caller_role,
|
|
451
|
+
caller_actor_id,
|
|
452
|
+
)
|
|
407
453
|
} else if let Some(verb) = step.get("verb").and_then(Json::as_str) {
|
|
408
454
|
let mut events: Vec<Event> = Vec::new();
|
|
409
455
|
let outcome = orchestrate(
|
|
@@ -425,11 +471,26 @@ pub fn serve(input: impl std::io::BufRead, mut output: impl std::io::Write) {
|
|
|
425
471
|
&mut saga_log,
|
|
426
472
|
);
|
|
427
473
|
match outcome {
|
|
428
|
-
Ok(()) => Json::obj(vec![
|
|
429
|
-
|
|
474
|
+
Ok(()) => Json::obj(vec![
|
|
475
|
+
("ok", Json::Bool(true)),
|
|
476
|
+
(
|
|
477
|
+
"events",
|
|
478
|
+
Json::Array(events.iter().map(event_to_json).collect()),
|
|
479
|
+
),
|
|
480
|
+
]),
|
|
481
|
+
Err(refusal) => Json::obj(vec![
|
|
482
|
+
("ok", Json::Bool(false)),
|
|
483
|
+
("error", Json::str(refusal.to_string())),
|
|
484
|
+
]),
|
|
430
485
|
}
|
|
431
486
|
} else {
|
|
432
|
-
Json::obj(vec![
|
|
487
|
+
Json::obj(vec![
|
|
488
|
+
("ok", Json::Bool(false)),
|
|
489
|
+
(
|
|
490
|
+
"error",
|
|
491
|
+
Json::str("step needs verb, dry_run, snapshot, restore, or instances"),
|
|
492
|
+
),
|
|
493
|
+
])
|
|
433
494
|
}
|
|
434
495
|
}
|
|
435
496
|
};
|
|
@@ -452,7 +513,10 @@ fn run_filter(store: &Store, filter: &Json) -> Result<Vec<Json>, Refusal> {
|
|
|
452
513
|
.ok_or_else(|| Refusal::Fault(format!("unknown aggregate {aggregate:?}")))?;
|
|
453
514
|
|
|
454
515
|
let matched = repository::filter_entries(entries, field, comparator, value);
|
|
455
|
-
Ok(matched
|
|
516
|
+
Ok(matched
|
|
517
|
+
.into_iter()
|
|
518
|
+
.map(|(id, record)| repository::row_json(id, record))
|
|
519
|
+
.collect())
|
|
456
520
|
}
|
|
457
521
|
|
|
458
522
|
fn required_str<'a>(filter: &'a Json, key: &str) -> Result<&'a str, Refusal> {
|
|
@@ -464,9 +528,45 @@ fn required_str<'a>(filter: &'a Json, key: &str) -> Result<&'a str, Refusal> {
|
|
|
464
528
|
|
|
465
529
|
/// The `refusals` verb column for a refused ad hoc filter, which has no verb; tolerates
|
|
466
530
|
/// missing or mistyped fields.
|
|
531
|
+
/// The log entry for an answered `query` step. A question with a reference twin also carries
|
|
532
|
+
/// `reference_rows`, the same rows because both come from one compiled path.
|
|
533
|
+
fn answered_entry(question: &str, offered: &Json, rows: Json, with_reference: bool) -> Json {
|
|
534
|
+
let mut fields = vec![
|
|
535
|
+
("query", Json::Str(question.to_string())),
|
|
536
|
+
("args", offered.clone()),
|
|
537
|
+
("rows", rows.clone()),
|
|
538
|
+
];
|
|
539
|
+
if with_reference {
|
|
540
|
+
fields.push(("reference_rows", rows));
|
|
541
|
+
}
|
|
542
|
+
Json::obj(fields)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/// The log entry for a refused `query` step: `rows: null` and the refusal's text, with the
|
|
546
|
+
/// reference twin's null rows and identical error when the question has one.
|
|
547
|
+
fn refused_entry(question: &str, offered: &Json, refusal: &Refusal, with_reference: bool) -> Json {
|
|
548
|
+
let message = refusal.to_string();
|
|
549
|
+
let mut fields = vec![
|
|
550
|
+
("query", Json::Str(question.to_string())),
|
|
551
|
+
("args", offered.clone()),
|
|
552
|
+
("rows", Json::Null),
|
|
553
|
+
("error", Json::Str(message.clone())),
|
|
554
|
+
];
|
|
555
|
+
if with_reference {
|
|
556
|
+
fields.push(("reference_rows", Json::Null));
|
|
557
|
+
fields.push(("reference_error", Json::Str(message)));
|
|
558
|
+
}
|
|
559
|
+
Json::obj(fields)
|
|
560
|
+
}
|
|
561
|
+
|
|
467
562
|
fn filter_label(filter: &Json) -> String {
|
|
468
563
|
let field = |key: &str| filter.get(key).and_then(Json::as_str).unwrap_or("");
|
|
469
|
-
format!(
|
|
564
|
+
format!(
|
|
565
|
+
"filter {}.{} {}",
|
|
566
|
+
field("aggregate"),
|
|
567
|
+
field("field"),
|
|
568
|
+
field("op")
|
|
569
|
+
)
|
|
470
570
|
}
|
|
471
571
|
|
|
472
572
|
fn command_input<'a>(step: &'a Json, legacy_args: &'a Json) -> &'a Json {
|
|
@@ -544,7 +644,10 @@ fn event_to_json(event: &Event) -> Json {
|
|
|
544
644
|
("id", Json::str(event.id.clone())),
|
|
545
645
|
("payload", event.payload.clone()),
|
|
546
646
|
// Key order matches Ruby's `Event#to_h`; `None` serialises as null.
|
|
547
|
-
(
|
|
647
|
+
(
|
|
648
|
+
"occurred_at",
|
|
649
|
+
event.occurred_at.clone().map_or(Json::Null, Json::Str),
|
|
650
|
+
),
|
|
548
651
|
])
|
|
549
652
|
}
|
|
550
653
|
|
|
@@ -573,8 +676,14 @@ mod routing_tests {
|
|
|
573
676
|
let selected = command_input(&step, &legacy_args);
|
|
574
677
|
let invocation = crate::kernel::CommandInvocation::from_json(selected).unwrap();
|
|
575
678
|
assert_eq!(invocation.route(), None);
|
|
576
|
-
assert_eq!(
|
|
577
|
-
|
|
679
|
+
assert_eq!(
|
|
680
|
+
invocation.facts().get("branch_code").and_then(Json::as_str),
|
|
681
|
+
Some("DOWNTOWN")
|
|
682
|
+
);
|
|
683
|
+
assert_eq!(
|
|
684
|
+
invocation.facts().get("box_number").and_then(Json::as_i64),
|
|
685
|
+
Some(12)
|
|
686
|
+
);
|
|
578
687
|
}
|
|
579
688
|
|
|
580
689
|
// A domain fact named `to` must dry-run as a plain fact, never a routing attempt
|
|
@@ -591,11 +700,19 @@ mod routing_tests {
|
|
|
591
700
|
|
|
592
701
|
assert_eq!(invocation.route(), None);
|
|
593
702
|
assert_eq!(
|
|
594
|
-
invocation
|
|
703
|
+
invocation
|
|
704
|
+
.facts()
|
|
705
|
+
.get("to")
|
|
706
|
+
.and_then(|to| to.get("value"))
|
|
707
|
+
.and_then(Json::as_i64),
|
|
595
708
|
Some(570)
|
|
596
709
|
);
|
|
597
710
|
assert_eq!(
|
|
598
|
-
invocation
|
|
711
|
+
invocation
|
|
712
|
+
.facts()
|
|
713
|
+
.get("name")
|
|
714
|
+
.and_then(|name| name.get("value"))
|
|
715
|
+
.and_then(Json::as_str),
|
|
599
716
|
Some("crew-1")
|
|
600
717
|
);
|
|
601
718
|
}
|