hecks 3.14.0 → 5.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.hecksagon +5 -3
- data/examples/pizzas/config.ru +22 -0
- data/exe/hecks +1 -1
- data/lib/hecks/adapters/driven/prism.rb +0 -2
- data/lib/hecks/adapters/driven.rb +0 -2
- data/lib/hecks/adapters/driving/json.rb +9 -16
- 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/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 +2 -0
- data/lib/hecks/chapters.rb +3 -3
- data/lib/hecks/cli/project_cli/launcher_source.rb +1 -1
- 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/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/app/responses.rb +3 -1
- 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/fuzzing/bounded_exhaustive_expressions.rb +0 -3
- 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/first_admin.rb +3 -3
- 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 +21 -0
- 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 +3 -80
- data/lib/hecks/hecks/hecks.hecksagon +2 -2
- data/lib/hecks/hecks/hecks.world +1 -1
- data/lib/hecks/hecks/rehearsal.bluebook +7 -13
- data/lib/hecks/kernel_capabilities/templates.rb +4 -4
- data/lib/hecks/kernel_capabilities.rb +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
- 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/site/cms_editor/templates/src/host.ts.tmpl +7 -2
- 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/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/unwrapping.rb +60 -0
- data/lib/hecks/runtime/value.rb +10 -21
- 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/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/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → ad68f9e06c8068c2a4176b57abf3083d591d5d8980c2b3e897b97331f965b643.marshal} +0 -0
- data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → ee22ec8642c4006717ba2b80f46e5efb81de459eef9dfec043e838c0ad4fd720.marshal} +0 -0
- data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-b69f5dcbba79c20daa0493476fa7c317004a30aa2bc3d3dc6a722ea70ae2b43b.json} +1 -1
- 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 +306 -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 +151 -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/presenters.rs +335 -0
- data/rust/codegen/src/queries.rs +601 -226
- data/rust/codegen/src/reactions.rs +268 -74
- data/rust/codegen/src/read_models.rs +574 -229
- 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 +1037 -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 +39 -7
- data/rust/host/src/payments/tests.rs +1427 -282
- data/rust/host/src/payments.rs +391 -112
- 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 +555 -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 -1
- data/rust/parser/src/build/named_pattern.rs +4 -1
- 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 -516
- 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 +22 -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 +244 -114
- 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 +244 -148
- data/rust/src/kernel/mod.rs +47 -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/present.rs +261 -0
- 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/rust/host/src/approval.rs +0 -652
|
@@ -9,7 +9,7 @@ module Hecks
|
|
|
9
9
|
# Checks one aggregate's records across the edge for id conservation and, where a
|
|
10
10
|
# per-record comparison is possible, agreement with the reference transform.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
12
|
+
# Rekeyed aggregates are exempt from the comparison.
|
|
13
13
|
#
|
|
14
14
|
# @param violations [Array<String>] collector this method appends messages to
|
|
15
15
|
# @param aggregate [Bluebook::Aggregate] the current era's IR for the aggregate
|
|
@@ -24,7 +24,7 @@ module Hecks
|
|
|
24
24
|
check_id_conservation!(violations, aggregate, before, after, rekeyed: rekeyed)
|
|
25
25
|
|
|
26
26
|
return unless declared
|
|
27
|
-
# A
|
|
27
|
+
# A rekeyed record's `after` cannot be looked up by its old id.
|
|
28
28
|
return if rekeyed
|
|
29
29
|
|
|
30
30
|
check_value_preservation!(violations, aggregate, declared, before, after)
|
|
@@ -56,12 +56,11 @@ module Hecks
|
|
|
56
56
|
# @return [void]
|
|
57
57
|
def check_value_preservation!(violations, aggregate, declared, before, after)
|
|
58
58
|
rules = Ports::Persistence::Lineage.from_declared(declared, aggregate.name)
|
|
59
|
-
compute_paths = compute_paths_of(declared)
|
|
60
59
|
|
|
61
60
|
before.each do |id, state|
|
|
62
61
|
next unless after.key?(id)
|
|
63
62
|
|
|
64
|
-
diverged = divergence(rules, id, state, after[id]
|
|
63
|
+
diverged = divergence(rules, id, state, after[id])
|
|
65
64
|
next if diverged.empty?
|
|
66
65
|
|
|
67
66
|
violations << "#{aggregate.name}##{id}: the translated state diverges from the reference " \
|
|
@@ -75,28 +74,6 @@ module Hecks
|
|
|
75
74
|
# @return [Hash{String => Object}, nil] a deep copy with String keys at every depth
|
|
76
75
|
def normalize(state) = JSON.parse(JSON.generate(state))
|
|
77
76
|
|
|
78
|
-
# Removes the paths a compute owns from a normalized state, in place.
|
|
79
|
-
#
|
|
80
|
-
# A dotted path removes only its own member, never the whole top-level key, so sibling
|
|
81
|
-
# members stay under the equivalence check and silent loss there is still caught.
|
|
82
|
-
#
|
|
83
|
-
# @param state [Hash{String => Object}] a normalized state; mutated
|
|
84
|
-
# @param paths [Array<String>] bare (`"price_cents"`) or dotted (`"price.cents"`)
|
|
85
|
-
# compute paths
|
|
86
|
-
# @return [Hash{String => Object}] `state` itself, with those paths deleted
|
|
87
|
-
def strip_compute_paths(state, paths)
|
|
88
|
-
paths.each do |path|
|
|
89
|
-
segments = path.split(".")
|
|
90
|
-
if segments.length == 1
|
|
91
|
-
state.delete(segments.first)
|
|
92
|
-
else
|
|
93
|
-
parent = segments[0..-2].reduce(state) { |node, segment| node.is_a?(Hash) ? node[segment] : nil }
|
|
94
|
-
parent.delete(segments.last) if parent.is_a?(Hash)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
state
|
|
98
|
-
end
|
|
99
|
-
|
|
100
77
|
private
|
|
101
78
|
|
|
102
79
|
# The message for a rekeying edge whose record count changed, or nil.
|
|
@@ -118,15 +95,11 @@ module Hecks
|
|
|
118
95
|
"(lost #{lost.sort.inspect}, gained #{gained.sort.inspect})"
|
|
119
96
|
end
|
|
120
97
|
|
|
121
|
-
def compute_paths_of(declared)
|
|
122
|
-
declared.computes.flat_map { |compute| [compute.from, compute.to] }.map(&:to_s)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
98
|
# The keys at which one record's translated state differs from the reference transform.
|
|
126
|
-
def divergence(rules, id, state, translated
|
|
99
|
+
def divergence(rules, id, state, translated)
|
|
127
100
|
entry = Ports::Persistence::Entry.new(operation: "save", id: id, state: state.transform_keys(&:to_sym))
|
|
128
|
-
expected =
|
|
129
|
-
actual =
|
|
101
|
+
expected = normalize(rules.translate(entry).state)
|
|
102
|
+
actual = normalize(translated)
|
|
130
103
|
(expected.keys | actual.keys).reject { |key| expected[key] == actual[key] }
|
|
131
104
|
end
|
|
132
105
|
end
|
|
@@ -8,7 +8,7 @@ module Hecks
|
|
|
8
8
|
#
|
|
9
9
|
# @param aggregate [Bluebook::Aggregate] the current era's IR for the aggregate
|
|
10
10
|
# @param declared [Bluebook::TranslationAggregate, nil] this edge's rules; the
|
|
11
|
-
# destinations of its renames, moves, converts and
|
|
11
|
+
# destinations of its renames, moves, converts and derives count as fed
|
|
12
12
|
# @param after [Hash{String => Hash}] translated state per record id
|
|
13
13
|
# @return [Array<String>] names of unfed attributes, in declaration order; `[]` when
|
|
14
14
|
# every attribute is fed or `after` holds no records
|
|
@@ -25,12 +25,12 @@ module Hecks
|
|
|
25
25
|
# The top-level attribute names an edge's destinations feed.
|
|
26
26
|
#
|
|
27
27
|
# @param declared [Bluebook::TranslationAggregate, nil] this edge's rules
|
|
28
|
-
# @return [Array<String>] the destinations of renames, moves, converts and
|
|
28
|
+
# @return [Array<String>] the destinations of renames, moves, converts and derives, cut to
|
|
29
29
|
# their top-level name; `[]` when there is no edge
|
|
30
30
|
def fed_names(declared)
|
|
31
31
|
return [] unless declared
|
|
32
32
|
|
|
33
|
-
rules = [declared.moves, declared.converts, declared.
|
|
33
|
+
rules = [declared.moves, declared.converts, declared.derives].flat_map { |list| list.map(&:to) }
|
|
34
34
|
(declared.renames.values.map(&:to_s) + rules).map { |path| path.to_s.split(".").first }
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -3,12 +3,11 @@ require "json"
|
|
|
3
3
|
require_relative "audit/layer_one"
|
|
4
4
|
require_relative "audit/layer_two"
|
|
5
5
|
require_relative "audit/unfed_report"
|
|
6
|
-
require_relative "audit/approval_digest"
|
|
7
6
|
|
|
8
7
|
module Hecks
|
|
9
8
|
module Translation
|
|
10
|
-
# Derives the audit's assertions per layer (audit/layer_one, layer_two, unfed_report
|
|
11
|
-
#
|
|
9
|
+
# Derives the audit's assertions per layer (audit/layer_one, layer_two, unfed_report);
|
|
10
|
+
# Layer 3, the human-approved sample, is assembled in `check`.
|
|
12
11
|
module Audit
|
|
13
12
|
Verdict = Struct.new(:violations, :dropped, :unfed, :samples, keyword_init: true) do
|
|
14
13
|
# Reports whether the mechanical layers found nothing to refuse.
|
|
@@ -23,7 +22,6 @@ module Hecks
|
|
|
23
22
|
extend LayerOne
|
|
24
23
|
extend LayerTwo
|
|
25
24
|
extend UnfedReport
|
|
26
|
-
extend ApprovalDigest
|
|
27
25
|
|
|
28
26
|
module_function
|
|
29
27
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Translation
|
|
3
|
+
# Compiles a `Bluebook::TranslationExpression` tree into a Postgres text expression over a
|
|
4
|
+
# jsonb record. The compiled form yields NULL wherever the expression has no value, and
|
|
5
|
+
# agrees with `TranslationExpression.evaluate` on every record.
|
|
6
|
+
module ExpressionSql
|
|
7
|
+
UPPER = ("A".."Z").to_a.join.freeze
|
|
8
|
+
LOWER = UPPER.downcase.freeze
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
# @param tree [Array] a tree `Bluebook::TranslationExpression.parse` returned
|
|
13
|
+
# @param record [String] the SQL that names the jsonb record the expression reads
|
|
14
|
+
# @return [String] a SQL expression of type text
|
|
15
|
+
def compile(tree, record)
|
|
16
|
+
case tree.first
|
|
17
|
+
when "lit" then text(tree[1])
|
|
18
|
+
when "null" then "NULL::text"
|
|
19
|
+
when "ref" then reference(tree[1], record)
|
|
20
|
+
else function(tree.first, tree.drop(1), record)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def function(name, arguments, record)
|
|
25
|
+
return lookup(arguments, record) if name == "lookup"
|
|
26
|
+
|
|
27
|
+
parts = arguments.map { |argument| compile(argument, record) }
|
|
28
|
+
case name
|
|
29
|
+
when "lower" then "translate(#{parts.first}, #{text(UPPER)}, #{text(LOWER)})"
|
|
30
|
+
when "upper" then "translate(#{parts.first}, #{text(LOWER)}, #{text(UPPER)})"
|
|
31
|
+
when "concat" then "(#{parts.join(" || ")})"
|
|
32
|
+
else "replace(#{parts.join(", ")})"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Only text counts as a value: a number, object, array or null reads as no value.
|
|
37
|
+
def reference(path, record)
|
|
38
|
+
at = RuleCompiler.path_literal(path)
|
|
39
|
+
"(CASE WHEN jsonb_typeof(#{record} #> #{at}) = 'string' THEN #{record} #>> #{at} END)"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def lookup(arguments, record)
|
|
43
|
+
key, table, fallback = arguments
|
|
44
|
+
branches = table.map { |from, to| "WHEN #{text(from)} THEN #{text(to)}" }.join(" ")
|
|
45
|
+
"(SELECT CASE WHEN __v IS NULL THEN NULL ELSE CASE __v #{branches} ELSE #{compile(fallback, record)} END END " \
|
|
46
|
+
"FROM (SELECT #{compile(key, record)} AS __v) __lookup)"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def text(value) = "#{RuleCompiler.text_literal(value)}::text"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "json"
|
|
2
|
+
require_relative "expression_sql"
|
|
2
3
|
|
|
3
4
|
module Hecks
|
|
4
5
|
module Translation
|
|
@@ -7,19 +8,34 @@ module Hecks
|
|
|
7
8
|
module RuleCompiler
|
|
8
9
|
module_function
|
|
9
10
|
|
|
10
|
-
# Compiles one edge's rename/move/convert/drop/
|
|
11
|
+
# Compiles one edge's derive/rename/move/convert/drop/backfill rules into a nested
|
|
11
12
|
# SQL expression over `state`, composed innermost-first in that phase order.
|
|
12
13
|
def compile_rules(declared)
|
|
13
|
-
expression =
|
|
14
|
+
expression = compile_derives("state", declared.derives)
|
|
15
|
+
expression = compile_renames(expression, declared.renames)
|
|
14
16
|
expression = compile_moves(expression, declared.moves)
|
|
15
17
|
expression = compile_converts(expression, declared.converts)
|
|
16
18
|
expression = compile_drops(expression, declared.drops)
|
|
17
|
-
expression = compile_computes(expression, declared.computes)
|
|
18
19
|
# Backfilled last, same order as `Lineage#translate`'s in-process pass, so the
|
|
19
20
|
# boot-time mint audit (which reads only this compiled SQL) sees the same result.
|
|
20
21
|
compile_backfills(expression, declared.backfills)
|
|
21
22
|
end
|
|
22
23
|
|
|
24
|
+
# Wraps `expression` in each derive, every one reading the record as it was stored.
|
|
25
|
+
def compile_derives(expression, derives)
|
|
26
|
+
derives.inject(expression) { |sql, derive| compile_derive(sql, derive) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Wraps `expression` so a derive's text lands at its destination when the expression has a
|
|
30
|
+
# value; the expression reads the stored `state`, never the rules composed before it.
|
|
31
|
+
def compile_derive(expression, derive)
|
|
32
|
+
value = ExpressionSql.compile(derive.tree, "state")
|
|
33
|
+
"(SELECT CASE WHEN __d IS NULL THEN __s " \
|
|
34
|
+
"ELSE hecks_tr_insert(__s, #{path_literal(derive.to)}, to_jsonb(__d), " \
|
|
35
|
+
"#{text_literal("derive #{derive.to}")}) END " \
|
|
36
|
+
"FROM (SELECT (#{expression}) AS __s) __outer, LATERAL (SELECT #{value} AS __d) __derived)"
|
|
37
|
+
end
|
|
38
|
+
|
|
23
39
|
# Folds each rule of one kind into `expression`, innermost-first.
|
|
24
40
|
def compile_renames(expression, renames)
|
|
25
41
|
renames.inject(expression) { |sql, (old_name, new_name)| compile_rename(sql, old_name, new_name) }
|
|
@@ -31,8 +47,6 @@ module Hecks
|
|
|
31
47
|
|
|
32
48
|
def compile_drops(expression, drops) = drops.inject(expression) { |sql, name| compile_drop(sql, name) }
|
|
33
49
|
|
|
34
|
-
def compile_computes(expression, computes) = computes.inject(expression) { |sql, compute| compile_compute(sql, compute) }
|
|
35
|
-
|
|
36
50
|
def compile_backfills(expression, backfills)
|
|
37
51
|
backfills.inject(expression) { |sql, backfill| compile_backfill(sql, backfill) }
|
|
38
52
|
end
|
|
@@ -71,25 +85,12 @@ module Hecks
|
|
|
71
85
|
"CASE WHEN #{guard} THEN #{compile_id_expression(declared)} ELSE aggregate_id END AS aggregate_id"
|
|
72
86
|
end
|
|
73
87
|
|
|
74
|
-
#
|
|
75
|
-
#
|
|
88
|
+
# A `rekey_to` id: the value of the derive it names, read from the record's stored `state`,
|
|
89
|
+
# or the old id for a record where the derive has no value.
|
|
76
90
|
def compile_id_expression(declared)
|
|
77
91
|
rekey = declared.rekeys.first
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
# Wraps `expression` so a compute's SQL result lands at its declared destination
|
|
82
|
-
# when the source path is present; the whole record stays readable as `__s`.
|
|
83
|
-
def compile_compute(expression, compute)
|
|
84
|
-
from = compute.from.to_s
|
|
85
|
-
to = compute.to.to_s
|
|
86
|
-
"(SELECT CASE WHEN (__x).present THEN " \
|
|
87
|
-
"hecks_tr_insert((__x).remaining, #{path_literal(to)}, to_jsonb((#{compute.sql})), " \
|
|
88
|
-
"#{text_literal("compute #{from} to: #{to}")}) " \
|
|
89
|
-
"ELSE __s END " \
|
|
90
|
-
"FROM (SELECT (#{expression}) AS __s) __outer, " \
|
|
91
|
-
"LATERAL (SELECT hecks_tr_extract(__s, #{path_literal(from)}) AS __x) __extract, " \
|
|
92
|
-
"LATERAL (SELECT (__s #>> #{path_literal(from)}) AS #{quote(from)}) __fields)"
|
|
92
|
+
derive = declared.derives.find { |candidate| candidate.to == rekey.to }
|
|
93
|
+
"COALESCE(#{ExpressionSql.compile(derive.tree, "state")}, aggregate_id)"
|
|
93
94
|
end
|
|
94
95
|
|
|
95
96
|
# Wraps `expression` so a backfill's default lands at its declared path only when
|
|
@@ -103,13 +104,6 @@ module Hecks
|
|
|
103
104
|
"FROM (SELECT (#{expression}) AS __s) __outer)"
|
|
104
105
|
end
|
|
105
106
|
|
|
106
|
-
# Quotes a Postgres identifier; requires "pg" lazily so a non-Postgres-bound
|
|
107
|
-
# domain never gains a hard dependency on the gem just by loading this file.
|
|
108
|
-
def quote(name)
|
|
109
|
-
require "pg"
|
|
110
|
-
PG::Connection.quote_ident(name.to_s)
|
|
111
|
-
end
|
|
112
|
-
|
|
113
107
|
# Renders a Ruby value as a single-quoted SQL text literal, escaping embedded quotes.
|
|
114
108
|
def text_literal(text) = "'#{text.to_s.gsub("'", "''")}'"
|
|
115
109
|
|
|
@@ -5,7 +5,7 @@ require_relative "scaffold/writer"
|
|
|
5
5
|
module Hecks
|
|
6
6
|
module Translation
|
|
7
7
|
# Diffs the held era's storage shape against the current one and writes the edge file.
|
|
8
|
-
# Ambiguities become parse-refusing `unresolved` constructs; it never proposes
|
|
8
|
+
# Ambiguities become parse-refusing `unresolved` constructs; it never proposes drop or derive.
|
|
9
9
|
module Scaffold
|
|
10
10
|
Edge = Struct.new(:domain, :from, :to, :ordinal, :label, :aggregates, :retired, keyword_init: true)
|
|
11
11
|
ScaffoldedAggregate = Struct.new(:name, :was, :rules, keyword_init: true)
|
|
@@ -18,20 +18,17 @@ module Hecks
|
|
|
18
18
|
|
|
19
19
|
# Registers this plugin's era gates on one boot's gate list.
|
|
20
20
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
21
|
+
# One `:pre_verify` gate, `era_check` (ADR 0031), registered only when the registry
|
|
22
|
+
# binds something lineage-capable.
|
|
23
23
|
#
|
|
24
24
|
# @param registry [Runtime::Registry] the registry being booted, asked whether any
|
|
25
25
|
# bluebook's first aggregate is bound to a lineage-capable adapter
|
|
26
26
|
# @param gates [Runtime::BootGates] this boot's gate list, registered onto in place
|
|
27
27
|
# @return [Runtime::BootGates, nil] `gates` when `:era_check` was registered; nil when
|
|
28
|
-
# the registry binds nothing lineage-capable
|
|
28
|
+
# the registry binds nothing lineage-capable
|
|
29
29
|
# @raise [Runtime::WiringError] if an aggregate's persistence binding is missing,
|
|
30
30
|
# ambiguous, or carries an unsupported role
|
|
31
31
|
def contribute_boot_gates(registry, gates)
|
|
32
|
-
gates.register(:era_compute_rules, lambda { |reg, _dir|
|
|
33
|
-
Runtime::EraCheck.check_compute_rules_for_registry!(reg)
|
|
34
|
-
}, phase: :pre_verify)
|
|
35
32
|
gates.register(:era_check, Runtime::EraCheck.method(:check_lineage!), phase: :pre_verify) if
|
|
36
33
|
Runtime::EraCheck.lineage_capable_registry?(registry)
|
|
37
34
|
end
|
data/lib/hecks/ports.rb
CHANGED
|
@@ -17,7 +17,6 @@ require_relative "ports/identity_generation"
|
|
|
17
17
|
require_relative "ports/key_vault"
|
|
18
18
|
require_relative "ports/authorization"
|
|
19
19
|
require_relative "ports/identity_resolution"
|
|
20
|
-
require_relative "ports/authentication"
|
|
21
20
|
require_relative "ports/access_control"
|
|
22
21
|
require_relative "ports/identity_assignment"
|
|
23
22
|
require_relative "ports/agent"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "json"
|
|
5
5
|
require "tempfile"
|
|
6
|
+
require_relative "rust_format"
|
|
6
7
|
require_relative "projection_files/texts"
|
|
7
8
|
require_relative "projection_files/expression_tables"
|
|
8
9
|
|
|
@@ -55,7 +56,8 @@ module Hecks
|
|
|
55
56
|
def build(name, root: ROOT)
|
|
56
57
|
raise ArgumentError, "no projection named #{name.inspect}" unless NAMES.include?(name.to_sym)
|
|
57
58
|
|
|
58
|
-
public_send(name, root)
|
|
59
|
+
result = public_send(name, root)
|
|
60
|
+
Result.new(RustFormat.files(result.content), result.stale)
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
# Writes one projection, each file through a temporary file and a rename so a reader never sees
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#
|
|
2
2
|
# @@web_logical_id@@ is the ONE exception -- real Google OAuth token
|
|
3
|
-
# exchange (
|
|
3
|
+
# exchange (the Rust web host's Google sign-in posting to
|
|
4
4
|
# https://oauth2.googleapis.com/token) is a genuine third-party HTTPS
|
|
5
5
|
# call with no AWS PrivateLink/VPC Endpoint option (unlike
|
|
6
6
|
# lambda:InvokeFunction, above), and @@web_logical_id@@ is ALSO
|
|
@@ -77,20 +77,26 @@ module Hecks
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def verb_entries(bluebook, homes)
|
|
80
|
-
|
|
80
|
+
shapes = bluebook.aggregates.to_h { |aggregate| [aggregate.hecks_name, aggregate.value_objects] }
|
|
81
|
+
holders(bluebook).flat_map do |holder|
|
|
82
|
+
command_entries(holder, homes) + query_entries(holder, homes, shapes[homes[holder.hecks_name]])
|
|
83
|
+
end
|
|
81
84
|
end
|
|
82
85
|
|
|
83
86
|
def command_entries(holder, homes)
|
|
84
87
|
holder.commands.map do |command|
|
|
85
88
|
Entry.new(name: command.hecks_name, kind: :command, within: holder.hecks_name,
|
|
86
|
-
section: homes[holder.hecks_name],
|
|
89
|
+
section: homes[holder.hecks_name],
|
|
90
|
+
facts: { command: command, holder: holder, within: homes[holder.hecks_name] })
|
|
87
91
|
end
|
|
88
92
|
end
|
|
89
93
|
|
|
90
|
-
|
|
94
|
+
# A query carries its holder and the aggregate's value objects, to speak its fields.
|
|
95
|
+
def query_entries(holder, homes, value_objects)
|
|
91
96
|
holder.queries.map do |query|
|
|
92
97
|
Entry.new(name: query.hecks_name, kind: :query, within: holder.hecks_name,
|
|
93
|
-
section: homes[holder.hecks_name],
|
|
98
|
+
section: homes[holder.hecks_name],
|
|
99
|
+
facts: { query: query, holder: holder, value_objects: Array(value_objects) })
|
|
94
100
|
end
|
|
95
101
|
end
|
|
96
102
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Projections
|
|
3
|
+
module Glossary
|
|
4
|
+
# The groups an aggregate's terms are listed under, so a reader meets what can be done
|
|
5
|
+
# with a thing before the lists and the parts it is made of. Each group is a `###`
|
|
6
|
+
# heading in the Markdown, so GitHub and the page give the terms the same slugs.
|
|
7
|
+
module Groups
|
|
8
|
+
# Kind => the group's heading, in the order the groups are shown.
|
|
9
|
+
LABELS = {
|
|
10
|
+
command: "Actions", event: "What it records", policy: "What happens on its own",
|
|
11
|
+
saga: "What happens on its own", query: "Lists", entity: "What it is made of",
|
|
12
|
+
value_object: "What it is made of"
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
ORDER = LABELS.values.uniq.freeze
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
# @param entry [Glossary::Entry] a term
|
|
20
|
+
# @return [Integer] where its group sits; terms outside every group come first
|
|
21
|
+
def rank(entry) = ORDER.index(LABELS[entry.kind]) || -1
|
|
22
|
+
|
|
23
|
+
# A section's terms with a group heading before the first term of each group. Only an
|
|
24
|
+
# aggregate's section is grouped.
|
|
25
|
+
#
|
|
26
|
+
# @param section [Glossary::Section] the section, its terms already sorted
|
|
27
|
+
# @return [Array<Array(Symbol, Object)>] `[:group, label]` and `[:term, entry]` pairs
|
|
28
|
+
def walk(section)
|
|
29
|
+
return section.terms.map { |entry| [:term, entry] } unless section.aggregate
|
|
30
|
+
|
|
31
|
+
last = nil
|
|
32
|
+
section.terms.flat_map do |entry|
|
|
33
|
+
label = LABELS[entry.kind]
|
|
34
|
+
heading = label == last ? [] : [[:group, label]]
|
|
35
|
+
last = label
|
|
36
|
+
heading + [[:term, entry]]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -41,6 +41,7 @@ module Hecks
|
|
|
41
41
|
<p class="sub">Glossary</p>
|
|
42
42
|
<input type="search" placeholder="Find a term" aria-label="Find a term" autocomplete="off">
|
|
43
43
|
<p class="count"></p>
|
|
44
|
+
<p class="progress" hidden></p>
|
|
44
45
|
<ol>
|
|
45
46
|
@@ITEMS@@
|
|
46
47
|
</ol>
|
|
@@ -69,7 +70,7 @@ module Hecks
|
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
def rail(title, sections, slugs)
|
|
72
|
-
domain =
|
|
73
|
+
domain = Page.domain(title)
|
|
73
74
|
items = sections.map do |section|
|
|
74
75
|
heading = section[:heading]
|
|
75
76
|
%(<li><a href="##{slugs[heading]}">#{Html.inline(heading.text)}</a></li>)
|
|
@@ -77,13 +78,17 @@ module Hecks
|
|
|
77
78
|
fill(RAIL, "DOMAIN" => Html.escape(domain), "ITEMS" => items.join("\n"))
|
|
78
79
|
end
|
|
79
80
|
|
|
81
|
+
# @param title [String] the document's title, such as "Pizzas glossary"
|
|
82
|
+
# @return [String] the domain's name alone
|
|
83
|
+
def domain(title) = title.delete_suffix(" glossary")
|
|
84
|
+
|
|
80
85
|
def front_matter(blocks)
|
|
81
86
|
["<header>", *blocks.filter_map { |block| front_part(block) }, "</header>"].join("\n")
|
|
82
87
|
end
|
|
83
88
|
|
|
84
89
|
def front_part(block)
|
|
85
90
|
case block.type
|
|
86
|
-
when :heading then "<h1>#{Html.inline(block.text
|
|
91
|
+
when :heading then "<h1>#{Html.inline(Page.domain(block.text))}</h1>"
|
|
87
92
|
when :quote then "<p class=\"vision\">#{Html.inline(block.text)}</p>"
|
|
88
93
|
when :paragraph then "<p class=\"lede\">#{Html.inline(block.text)}</p>"
|
|
89
94
|
when :mermaid then Html.figure(block, "How it all fits together")
|
|
@@ -25,7 +25,7 @@ module Hecks
|
|
|
25
25
|
|
|
26
26
|
case line
|
|
27
27
|
when /\A```mermaid/ then fence(lines, index)
|
|
28
|
-
when /\A(\#{1,
|
|
28
|
+
when /\A(\#{1,4}) (.+)\z/ then [heading(Regexp.last_match), index + 1]
|
|
29
29
|
when /\A> / then [Block.new(type: :quote, text: line[2..]), index + 1]
|
|
30
30
|
when /\A- / then list(lines, index)
|
|
31
31
|
else paragraph(lines, index)
|
|
@@ -4,6 +4,11 @@ module Hecks
|
|
|
4
4
|
module Html
|
|
5
5
|
# Groups parsed blocks into the page's sections and renders each section's terms.
|
|
6
6
|
module Sections
|
|
7
|
+
# Paragraph openings that mark a rule, and the class that sets each apart on the page.
|
|
8
|
+
LEADS = {
|
|
9
|
+
"Always true:" => "rule", "Allowed only when:" => "rule", "Afterwards, always:" => "rule"
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
7
12
|
module_function
|
|
8
13
|
|
|
9
14
|
# GitHub's slugs for these headings, so links the Markdown carries land here too.
|
|
@@ -26,14 +31,14 @@ module Hecks
|
|
|
26
31
|
sections
|
|
27
32
|
end
|
|
28
33
|
|
|
29
|
-
# Each `###` opens
|
|
30
|
-
# captions whatever figure or list follows it.
|
|
34
|
+
# Each `###` opens a group of terms and each `####` a term running to the next. A
|
|
35
|
+
# bold-only paragraph captions whatever figure or list follows it.
|
|
31
36
|
def section_html(section, slugs)
|
|
32
37
|
heading = section[:heading]
|
|
33
38
|
state = { parts: [%(<section id="#{slugs[heading]}">), "<h2>#{Html.inline(heading.text)}</h2>"],
|
|
34
|
-
caption: nil,
|
|
39
|
+
caption: nil, open: nil }
|
|
35
40
|
section[:blocks].drop(1).each { |block| section_block(block, state, slugs) }
|
|
36
|
-
state
|
|
41
|
+
close(state, nil)
|
|
37
42
|
state[:parts] << "</section>"
|
|
38
43
|
state[:parts].join("\n")
|
|
39
44
|
end
|
|
@@ -41,7 +46,7 @@ module Hecks
|
|
|
41
46
|
# Renders one block into `state[:parts]`, mutating `state`.
|
|
42
47
|
def section_block(block, state, slugs)
|
|
43
48
|
case block.type
|
|
44
|
-
when :heading then open_term(block, state, slugs)
|
|
49
|
+
when :heading then block.level == 3 ? open_group(block, state, slugs) : open_term(block, state, slugs)
|
|
45
50
|
when :mermaid then state[:parts] << Html.figure(block, state.delete(:caption))
|
|
46
51
|
when :list then list(block, state)
|
|
47
52
|
when :quote then state[:parts] << "<p class=\"about\">#{Html.inline(block.text)}</p>"
|
|
@@ -49,11 +54,26 @@ module Hecks
|
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
|
57
|
+
def open_group(block, state, slugs)
|
|
58
|
+
close(state, nil)
|
|
59
|
+
state[:parts] << %(<h3 class="group" id="#{slugs[block]}">#{Html.inline(block.text)}</h3>)
|
|
60
|
+
end
|
|
61
|
+
|
|
52
62
|
def open_term(block, state, slugs)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
parts << %(<article class="term" id="#{slugs[block]}">) << "<
|
|
56
|
-
state[:
|
|
63
|
+
close(state, :terms)
|
|
64
|
+
state[:parts] << "<div class=\"terms\">" unless state[:open]
|
|
65
|
+
state[:parts] << %(<article class="term" id="#{slugs[block]}">) << "<h4>#{Html.inline(block.text)}</h4>"
|
|
66
|
+
state[:open] = :article
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Closes the open article, and the terms around it unless `keep` is `:terms`.
|
|
70
|
+
def close(state, keep)
|
|
71
|
+
state[:parts] << "</article>" if state[:open] == :article
|
|
72
|
+
state[:open] = :terms if state[:open]
|
|
73
|
+
return if keep == :terms
|
|
74
|
+
|
|
75
|
+
state[:parts] << "</div>" if state[:open]
|
|
76
|
+
state[:open] = nil
|
|
57
77
|
end
|
|
58
78
|
|
|
59
79
|
def list(block, state)
|
|
@@ -67,8 +87,8 @@ module Hecks
|
|
|
67
87
|
if text.match?(/\A\*\*[^*]+\*\*\z/)
|
|
68
88
|
state[:caption] = text.delete("*")
|
|
69
89
|
else
|
|
70
|
-
klass = text.start_with?(
|
|
71
|
-
state[:parts] << "<p#{klass}>#{Html.inline(text)}</p>"
|
|
90
|
+
klass = LEADS.find { |lead, _class| text.start_with?(lead) }&.last
|
|
91
|
+
state[:parts] << "<p#{%( class="#{klass}") if klass}>#{Html.inline(text)}</p>"
|
|
72
92
|
end
|
|
73
93
|
end
|
|
74
94
|
end
|
|
@@ -11,8 +11,7 @@ module Hecks
|
|
|
11
11
|
module Html
|
|
12
12
|
Block = Struct.new(:type, :text, :items, :level, keyword_init: true)
|
|
13
13
|
|
|
14
|
-
FONTS = "https://fonts.googleapis.com/css2?family=
|
|
15
|
-
"0,6..72,600;1,6..72,400&family=Instrument+Sans:wght@400;500;600&display=swap".freeze
|
|
14
|
+
FONTS = "https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&display=swap".freeze
|
|
16
15
|
MERMAID = "https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.1/mermaid.min.js".freeze
|
|
17
16
|
|
|
18
17
|
module_function
|