hecks 0.2.0 → 1.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 +5 -5
- data/lib/hecks/adapters/driven/claude_code.adapter +3 -0
- data/lib/hecks/adapters/driven/claude_code.rb +127 -0
- data/lib/hecks/adapters/driven/d1.adapter +12 -0
- data/lib/hecks/adapters/driven/d1.rb +427 -0
- data/lib/hecks/adapters/driven/folder.adapter +3 -0
- data/lib/hecks/adapters/driven/folder.rb +199 -0
- data/lib/hecks/adapters/driven/google_authentication.adapter +3 -0
- data/lib/hecks/adapters/driven/google_authentication.rb +100 -0
- data/lib/hecks/adapters/driven/governance_authorization.adapter +3 -0
- data/lib/hecks/adapters/driven/governance_authorization.rb +101 -0
- data/lib/hecks/adapters/driven/heki/journal.rb +56 -0
- data/lib/hecks/adapters/driven/heki/saga_store.rb +99 -0
- data/lib/hecks/adapters/driven/heki/snapshot.rb +65 -0
- data/lib/hecks/adapters/driven/heki.adapter +4 -0
- data/lib/hecks/adapters/driven/heki.rb +179 -0
- data/lib/hecks/adapters/driven/identity_registry.adapter +3 -0
- data/lib/hecks/adapters/driven/identity_registry.rb +26 -0
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +51 -0
- data/lib/hecks/adapters/driven/lambda/client.rb +63 -0
- data/lib/hecks/adapters/driven/lambda.adapter +4 -0
- data/lib/hecks/adapters/driven/lambda.rb +145 -0
- data/lib/hecks/adapters/driven/memory.adapter +3 -0
- data/lib/hecks/adapters/driven/memory.rb +103 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +3 -0
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +28 -0
- data/lib/hecks/adapters/driven/postgres/codec.rb +88 -0
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +207 -0
- data/lib/hecks/adapters/driven/postgres.adapter +5 -0
- data/lib/hecks/adapters/driven/postgres.rb +438 -0
- data/lib/hecks/adapters/driven/postgres_era.adapter +17 -0
- data/lib/hecks/adapters/driven/prism.adapter +3 -0
- data/lib/hecks/adapters/driven/prism.rb +80 -0
- data/lib/hecks/adapters/driven/secure_random_identity.adapter +3 -0
- data/lib/hecks/adapters/driven/secure_random_identity.rb +14 -0
- data/lib/hecks/adapters/driven/sql_query_builder.rb +221 -0
- data/lib/hecks/adapters/driven/sqlite/codec.rb +80 -0
- data/lib/hecks/adapters/driven/sqlite/projection.rb +172 -0
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +192 -0
- data/lib/hecks/adapters/driven/sqlite.adapter +9 -0
- data/lib/hecks/adapters/driven/sqlite.rb +306 -0
- data/lib/hecks/adapters/driven/system_clock.adapter +3 -0
- data/lib/hecks/adapters/driven/system_clock.rb +14 -0
- data/lib/hecks/adapters/driven.rb +54 -0
- data/lib/hecks/adapters.rb +6 -0
- data/lib/hecks/behaviors/dsl.rb +125 -0
- data/lib/hecks/behaviors/expectations.rb +340 -0
- data/lib/hecks/behaviors/ir.rb +31 -0
- data/lib/hecks/behaviors/rspec.rb +42 -0
- data/lib/hecks/behaviors/runner.rb +96 -0
- data/lib/hecks/behaviors.rb +25 -0
- data/lib/hecks/bluebook/aggregate.rb +108 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +134 -0
- data/lib/hecks/bluebook/assembly/build.rb +48 -0
- data/lib/hecks/bluebook/assembly/contract.rb +112 -0
- data/lib/hecks/bluebook/assembly/contracts.rb +438 -0
- data/lib/hecks/bluebook/assembly/marks.rb +228 -0
- data/lib/hecks/bluebook/assembly/specializer.rb +70 -0
- data/lib/hecks/bluebook/assembly.rb +91 -0
- data/lib/hecks/bluebook/attribute.rb +96 -0
- data/lib/hecks/bluebook/behaviour/aggregate.rb +83 -0
- data/lib/hecks/bluebook/behaviour/attribute.rb +27 -0
- data/lib/hecks/bluebook/behaviour/chapter.rb +72 -0
- data/lib/hecks/bluebook/behaviour/command.rb +116 -0
- data/lib/hecks/bluebook/behaviour/domain_port.rb +25 -0
- data/lib/hecks/bluebook/behaviour/entity.rb +59 -0
- data/lib/hecks/bluebook/behaviour/hexagon.rb +50 -0
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +68 -0
- data/lib/hecks/bluebook/behaviour/policy.rb +52 -0
- data/lib/hecks/bluebook/behaviour/process_manager.rb +51 -0
- data/lib/hecks/bluebook/behaviour/query.rb +10 -0
- data/lib/hecks/bluebook/behaviour/read_model.rb +29 -0
- data/lib/hecks/bluebook/behaviour/traits.rb +81 -0
- data/lib/hecks/bluebook/behaviour/value_object.rb +33 -0
- data/lib/hecks/bluebook/chapter.rb +78 -0
- data/lib/hecks/bluebook/command.rb +124 -0
- data/lib/hecks/bluebook/domain_port.rb +102 -0
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +34 -0
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +1018 -0
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +348 -0
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +71 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +1087 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +714 -0
- data/lib/hecks/bluebook/dsl/const_shim.rb +81 -0
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +79 -0
- data/lib/hecks/bluebook/dsl/entity_builder.rb +430 -0
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +366 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +163 -0
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +191 -0
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +44 -0
- data/lib/hecks/bluebook/dsl/malformed.rb +7 -0
- data/lib/hecks/bluebook/dsl/policy_builder.rb +135 -0
- data/lib/hecks/bluebook/dsl/port_builder.rb +39 -0
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +142 -0
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +306 -0
- data/lib/hecks/bluebook/dsl/query_builder.rb +113 -0
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +233 -0
- data/lib/hecks/bluebook/dsl/rule_reference.rb +173 -0
- data/lib/hecks/bluebook/dsl/translation_builder.rb +243 -0
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +178 -0
- data/lib/hecks/bluebook/dsl/word_gate.rb +228 -0
- data/lib/hecks/bluebook/dsl/world_builder.rb +117 -0
- data/lib/hecks/bluebook/dsl.rb +45 -0
- data/lib/hecks/bluebook/entity.rb +103 -0
- data/lib/hecks/bluebook/expression/canonical_form.rb +123 -0
- data/lib/hecks/bluebook/expression/evaluator.rb +304 -0
- data/lib/hecks/bluebook/expression/projection.json +218 -0
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +233 -0
- data/lib/hecks/bluebook/expression/resolver.rb +781 -0
- data/lib/hecks/bluebook/expression.rb +13 -0
- data/lib/hecks/bluebook/hexagon.rb +60 -0
- data/lib/hecks/bluebook/lifecycle.rb +42 -0
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +54 -0
- data/lib/hecks/bluebook/meta_validator/judge.rb +621 -0
- data/lib/hecks/bluebook/meta_validator/plan.rb +332 -0
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +51 -0
- data/lib/hecks/bluebook/meta_validator/readings.rb +360 -0
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +351 -0
- data/lib/hecks/bluebook/meta_validator/shapes.rb +266 -0
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +255 -0
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +138 -0
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +78 -0
- data/lib/hecks/bluebook/meta_validator.rb +542 -0
- data/lib/hecks/bluebook/model_check.rb +445 -0
- data/lib/hecks/bluebook/pattern_subset.rb +184 -0
- data/lib/hecks/bluebook/policy.rb +41 -0
- data/lib/hecks/bluebook/process_manager.rb +128 -0
- data/lib/hecks/bluebook/project_discovery.rb +30 -0
- data/lib/hecks/bluebook/project_loader.rb +40 -0
- data/lib/hecks/bluebook/project_register.rb +82 -0
- data/lib/hecks/bluebook/query.rb +61 -0
- data/lib/hecks/bluebook/read_model.rb +109 -0
- data/lib/hecks/bluebook/reference.rb +74 -0
- data/lib/hecks/bluebook/smoke_test.rb +166 -0
- data/lib/hecks/bluebook/synthesizer.rb +95 -0
- data/lib/hecks/bluebook/translation.rb +92 -0
- data/lib/hecks/bluebook/value_object.rb +66 -0
- data/lib/hecks/bluebook.rb +74 -0
- data/lib/hecks/codemod.rb +342 -0
- data/lib/hecks/construct.rb +71 -0
- data/lib/hecks/deploy/bluebook/deploy.bluebook +219 -0
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +4 -0
- data/lib/hecks/deploy/oidc.json +18 -0
- data/lib/hecks/doc/reference.rb +410 -0
- data/lib/hecks/embryonaut_bluebook.rb +75 -0
- data/lib/hecks/facade/cli_door.rb +119 -0
- data/lib/hecks/facade/cli_runner.rb +190 -0
- data/lib/hecks/facade/command_request.rb +105 -0
- data/lib/hecks/facade/handle.rb +173 -0
- data/lib/hecks/facade/json_door.rb +166 -0
- data/lib/hecks/facade/surface/aggregate_door.rb +185 -0
- data/lib/hecks/facade/surface/chapter.rb +107 -0
- data/lib/hecks/facade/surface.rb +48 -0
- data/lib/hecks/facade.rb +44 -0
- data/lib/hecks/forms/app.rb +341 -0
- data/lib/hecks/forms/command_form_renderer.rb +113 -0
- data/lib/hecks/forms/examples/banking_console.bluebook +3 -0
- data/lib/hecks/forms/field_renderer.rb +177 -0
- data/lib/hecks/forms/field_shape.rb +232 -0
- data/lib/hecks/forms/html.rb +84 -0
- data/lib/hecks/forms/index_renderer.rb +35 -0
- data/lib/hecks/forms/page.rb +157 -0
- data/lib/hecks/forms/params.rb +161 -0
- data/lib/hecks/forms/port_argument.rb +46 -0
- data/lib/hecks/forms/query_form_renderer.rb +114 -0
- data/lib/hecks/forms/record_renderer.rb +119 -0
- data/lib/hecks/forms/record_table.rb +68 -0
- data/lib/hecks/forms/reference_options.rb +30 -0
- data/lib/hecks/forms/value_object_shape.rb +46 -0
- data/lib/hecks/forms.rb +54 -0
- data/lib/hecks/fqn.rb +94 -0
- data/lib/hecks/framework/bluebook/compliance.bluebook +1 -0
- data/lib/hecks/framework/bluebook/console_settings.bluebook +489 -0
- data/lib/hecks/framework/bluebook/framework.hecksagon +32 -0
- data/lib/hecks/framework/bluebook/governance.bluebook +145 -0
- data/lib/hecks/framework/bluebook/identity.bluebook +90 -0
- data/lib/hecks/framework/oidc.json +39 -0
- data/lib/hecks/framework.rb +90 -0
- data/lib/hecks/freezer.rb +67 -0
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
- data/lib/hecks/fuzzing/invalid_value_generator.rb +86 -0
- data/lib/hecks/fuzzing/isolated_boot.rb +286 -0
- data/lib/hecks/fuzzing/properties.rb +1192 -0
- data/lib/hecks/fuzzing/replay.rb +668 -0
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +93 -0
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +80 -0
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +96 -0
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +250 -0
- data/lib/hecks/fuzzing/sequence_generator.rb +131 -0
- data/lib/hecks/fuzzing/value_generator.rb +200 -0
- data/lib/hecks/fuzzing.rb +12 -0
- data/lib/hecks/grammar/evolve.rb +324 -0
- data/lib/hecks/grammar/expression.bluebook +420 -0
- data/lib/hecks/grammar/expression_operators.json +1648 -0
- data/lib/hecks/grammar/grammar.hecksagon +20 -0
- data/lib/hecks/grammar/oidc.json +69 -0
- data/lib/hecks/grammar/translation.bluebook +203 -0
- data/lib/hecks/grammar.rb +178 -0
- data/lib/hecks/ir.rb +126 -0
- data/lib/hecks/language/adapter.bluebook +116 -0
- data/lib/hecks/language/bluebook/aggregate.bluebook +581 -0
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +76 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +256 -0
- data/lib/hecks/language/bluebook/bluebook.hecksagon +24 -0
- data/lib/hecks/language/bluebook/command.bluebook +471 -0
- data/lib/hecks/language/bluebook/entity.bluebook +392 -0
- data/lib/hecks/language/bluebook/policy.bluebook +189 -0
- data/lib/hecks/language/bluebook/process_manager.bluebook +381 -0
- data/lib/hecks/language/bluebook/projection.bluebook +267 -0
- data/lib/hecks/language/bluebook/query.bluebook +245 -0
- data/lib/hecks/language/bluebook/shape.bluebook +292 -0
- data/lib/hecks/language/bluebook/syntax.bluebook +448 -0
- data/lib/hecks/language/bluebook/vocabulary.bluebook +379 -0
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +51 -0
- data/lib/hecks/language/hecksagon/domain_port.bluebook +76 -0
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +131 -0
- data/lib/hecks/language/hecksagon/port_operation.bluebook +102 -0
- data/lib/hecks/language/oidc.json +333 -0
- data/lib/hecks/language/port.bluebook +120 -0
- data/lib/hecks/language/translation/translation.bluebook +110 -0
- data/lib/hecks/language/translation/translation_aggregate.bluebook +267 -0
- data/lib/hecks/language/world/wiring.bluebook +62 -0
- data/lib/hecks/language/world/world.bluebook +84 -0
- data/lib/hecks/literal.rb +125 -0
- data/lib/hecks/naming.rb +174 -0
- data/lib/hecks/ports/access_control.port +9 -0
- data/lib/hecks/ports/access_control.rb +62 -0
- data/lib/hecks/ports/agent/answers.rb +104 -0
- data/lib/hecks/ports/agent.port +8 -0
- data/lib/hecks/ports/agent.rb +167 -0
- data/lib/hecks/ports/authentication.port +6 -0
- data/lib/hecks/ports/authentication.rb +50 -0
- data/lib/hecks/ports/authorization.port +7 -0
- data/lib/hecks/ports/authorization.rb +62 -0
- data/lib/hecks/ports/clock.port +5 -0
- data/lib/hecks/ports/clock.rb +62 -0
- data/lib/hecks/ports/extraction.port +5 -0
- data/lib/hecks/ports/extraction.rb +37 -0
- data/lib/hecks/ports/identity_assignment.port +5 -0
- data/lib/hecks/ports/identity_assignment.rb +45 -0
- data/lib/hecks/ports/identity_generation.port +5 -0
- data/lib/hecks/ports/identity_generation.rb +49 -0
- data/lib/hecks/ports/identity_resolution.port +5 -0
- data/lib/hecks/ports/identity_resolution.rb +40 -0
- data/lib/hecks/ports/loading.port +4 -0
- data/lib/hecks/ports/loading.rb +13 -0
- data/lib/hecks/ports/persistence/append_only.rb +138 -0
- data/lib/hecks/ports/persistence/binding_policy.rb +56 -0
- data/lib/hecks/ports/persistence/execution.rb +23 -0
- data/lib/hecks/ports/persistence/null_saga_store.rb +25 -0
- data/lib/hecks/ports/persistence/plugin.rb +54 -0
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +169 -0
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +128 -0
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +161 -0
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +61 -0
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +304 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +171 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +190 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +478 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +166 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +314 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +168 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +170 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +134 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +137 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +89 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +85 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +43 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +132 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +74 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +758 -0
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +120 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +31 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +46 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +102 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +46 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +70 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +72 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +120 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +183 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +41 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +28 -0
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +30 -0
- data/lib/hecks/ports/persistence/plugins/era/translation.rb +11 -0
- data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
- data/lib/hecks/ports/persistence/remote_runtime.rb +42 -0
- data/lib/hecks/ports/persistence/repository_factory.rb +30 -0
- data/lib/hecks/ports/persistence.port +4 -0
- data/lib/hecks/ports/persistence.rb +39 -0
- data/lib/hecks/ports/projection.port +4 -0
- data/lib/hecks/ports/projection.rb +96 -0
- data/lib/hecks/ports/query/in_memory.rb +60 -0
- data/lib/hecks/ports/query/ordering.rb +41 -0
- data/lib/hecks/ports/query.rb +36 -0
- data/lib/hecks/ports.rb +27 -0
- data/lib/hecks/projections/diagrams.rb +642 -0
- data/lib/hecks/projections/ir.rb +18 -0
- data/lib/hecks/projections/model/deviations.rb +98 -0
- data/lib/hecks/projections/model.rb +145 -0
- data/lib/hecks/projections/oidc.rb +110 -0
- data/lib/hecks/projections/parser_table.rb +159 -0
- data/lib/hecks/projections/reference.rb +38 -0
- data/lib/hecks/projections/shape.rb +44 -0
- data/lib/hecks/projections/statements.rb +110 -0
- data/lib/hecks/projections/vocabulary.rb +100 -0
- data/lib/hecks/projections.rb +32 -0
- data/lib/hecks/projector/cli_projector.rb +446 -0
- data/lib/hecks/projector/docs_projector.rb +321 -0
- data/lib/hecks/projector/exporter.rb +158 -0
- data/lib/hecks/projector/ir_projector.rb +18 -0
- data/lib/hecks/projector/narrate_projector.rb +243 -0
- data/lib/hecks/projector/target.rb +97 -0
- data/lib/hecks/projector.rb +186 -0
- data/lib/hecks/query_ir.rb +411 -0
- data/lib/hecks/query_specification/common/authorization_spec.rb +9 -0
- data/lib/hecks/query_specification/common/comparators.rb +25 -0
- data/lib/hecks/query_specification/common/comparison.rb +174 -0
- data/lib/hecks/query_specification/common/cursor_spec.rb +9 -0
- data/lib/hecks/query_specification/common/dsl.rb +58 -0
- data/lib/hecks/query_specification/common/inspection_spec.rb +9 -0
- data/lib/hecks/query_specification/common/limit_spec.rb +9 -0
- data/lib/hecks/query_specification/common/null_policy.rb +92 -0
- data/lib/hecks/query_specification/common/null_semantics.rb +11 -0
- data/lib/hecks/query_specification/common/offset_spec.rb +9 -0
- data/lib/hecks/query_specification/common/options.rb +32 -0
- data/lib/hecks/query_specification/common/order_by.rb +9 -0
- data/lib/hecks/query_specification/common/specification.rb +10 -0
- data/lib/hecks/query_specification/common/where_clause.rb +9 -0
- data/lib/hecks/query_specification/field_path.rb +107 -0
- data/lib/hecks/query_specification/hop_path.rb +132 -0
- data/lib/hecks/query_specification/read_model/specification.rb +18 -0
- data/lib/hecks/query_specification.rb +14 -0
- data/lib/hecks/rendering.rb +48 -0
- data/lib/hecks/router/namespace_installer.rb +157 -0
- data/lib/hecks/router.rb +70 -0
- data/lib/hecks/runtime/aggregate_lock.rb +45 -0
- data/lib/hecks/runtime/boot_gates.rb +41 -0
- data/lib/hecks/runtime/caller.rb +66 -0
- data/lib/hecks/runtime/capability_graph.rb +44 -0
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +135 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +260 -0
- data/lib/hecks/runtime/command_interpreter.rb +497 -0
- data/lib/hecks/runtime/command_rules/admissibility.rb +357 -0
- data/lib/hecks/runtime/command_rules/arithmetic.rb +260 -0
- data/lib/hecks/runtime/command_rules/authorization.rb +65 -0
- data/lib/hecks/runtime/command_rules/emission.rb +34 -0
- data/lib/hecks/runtime/command_rules/references.rb +189 -0
- data/lib/hecks/runtime/command_rules.rb +28 -0
- data/lib/hecks/runtime/dependency_planning.rb +245 -0
- data/lib/hecks/runtime/dispatcher.rb +301 -0
- data/lib/hecks/runtime/entity_element.rb +253 -0
- data/lib/hecks/runtime/entity_interpreter.rb +299 -0
- data/lib/hecks/runtime/errors.rb +122 -0
- data/lib/hecks/runtime/event.rb +51 -0
- data/lib/hecks/runtime/identity.rb +156 -0
- data/lib/hecks/runtime/instance.rb +175 -0
- data/lib/hecks/runtime/interpreting.rb +90 -0
- data/lib/hecks/runtime/loader.rb +184 -0
- data/lib/hecks/runtime/policy_interpreter.rb +366 -0
- data/lib/hecks/runtime/port_operation_interpreter.rb +210 -0
- data/lib/hecks/runtime/query_interpreter.rb +286 -0
- data/lib/hecks/runtime/reaction_invocation.rb +253 -0
- data/lib/hecks/runtime/read_model_interpreter.rb +341 -0
- data/lib/hecks/runtime/rebuild_sweep.rb +74 -0
- data/lib/hecks/runtime/reference_hop.rb +99 -0
- data/lib/hecks/runtime/refusal_wording.rb +126 -0
- data/lib/hecks/runtime/registry/saga_persistence.rb +142 -0
- data/lib/hecks/runtime/registry/verification.rb +246 -0
- data/lib/hecks/runtime/registry.rb +279 -0
- data/lib/hecks/runtime/remote_dispatcher.rb +143 -0
- data/lib/hecks/runtime/routing.rb +96 -0
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +97 -0
- data/lib/hecks/runtime/saga_interpreter.rb +503 -0
- data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
- data/lib/hecks/runtime/tenant_check.rb +84 -0
- data/lib/hecks/runtime/tenant_scope.rb +55 -0
- data/lib/hecks/runtime/value/admission.rb +130 -0
- data/lib/hecks/runtime/value/coercion.rb +565 -0
- data/lib/hecks/runtime/value/invariant_violation.rb +5 -0
- data/lib/hecks/runtime/value.rb +125 -0
- data/lib/hecks/runtime.rb +107 -0
- data/lib/hecks/storehouse.rb +632 -0
- data/lib/hecks/version.rb +16 -0
- data/lib/hecks/vocabulary.rb +218 -0
- data/lib/hecks.rb +123 -6
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
- metadata +412 -222
- data/bin/hecks +0 -7
- data/bin/hecks-package +0 -65
- data/bin/hecks_console +0 -12
- data/bin/hecks_serverless +0 -6
- data/lib/cli/build.rb +0 -14
- data/lib/cli/command_runner.rb +0 -28
- data/lib/cli/console.rb +0 -10
- data/lib/cli/generate.rb +0 -37
- data/lib/cli/hecks-cli.rb +0 -27
- data/lib/cli/test.rb +0 -57
- data/lib/console/commands.rb +0 -8
- data/lib/console/hecks-console.rb +0 -1
- data/lib/packager/README.md +0 -0
- data/lib/packager/app_runner.rb +0 -21
- data/lib/packager/args.rb +0 -26
- data/lib/packager/compatibility/fixnum.rb +0 -6
- data/lib/packager/hecks.rb +0 -39
- data/lib/packager/query_runner.rb +0 -21
- data/lib/packager/resources/Dockerfile +0 -11
- data/lib/packager/resources/app_binary +0 -7
- data/lib/packager/resources/bundle_config +0 -3
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz +0 -0
- data/lib/packager/resources/traveling-ruby-20150715-2.2.2-osx.tar.gz +0 -0
- data/lib/serverless/Domain +0 -32
- data/lib/serverless/cli.rb +0 -75
- data/lib/serverless/resources/command_name.js +0 -5
- data/lib/serverless/resources/environment.js +0 -7
- data/lib/serverless/resources/handler.js.tt +0 -28
- data/lib/serverless/resources/run_binary.js +0 -22
- data/lib/serverless/resources/serverless.yml +0 -20
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
require_relative "bluebook/meta_validator"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
# SHARED MACHINERY for a codemod that migrates real `.bluebook` source
|
|
5
|
+
# once a DSL builder change makes some previously-required declaration
|
|
6
|
+
# optional/redundant — pulled out of `bin/codemod_implicit_append_fields`
|
|
7
|
+
# (the first one built), which needed three real, hard-won fixes before
|
|
8
|
+
# it could be trusted: a process-lifetime AST cache with no
|
|
9
|
+
# invalidation, a batch-revert granularity that let one unsafe
|
|
10
|
+
# candidate sink every other safe one sharing its boot, and (in the
|
|
11
|
+
# SPINE the codemod migrates FOR, not here) an append-at-end insertion
|
|
12
|
+
# that only round-tripped correctly for whichever field happened to be
|
|
13
|
+
# last. None of those are guessable in advance; they only surface by
|
|
14
|
+
# actually running a real edit against real self-hosted code. This
|
|
15
|
+
# module is that lesson, kept — the NEXT codemod plugs in two rule-
|
|
16
|
+
# specific procs (`find_candidates`, `apply_candidate`) and inherits
|
|
17
|
+
# the boot/safety-net machinery rather than rediscovering it.
|
|
18
|
+
#
|
|
19
|
+
# A CODEMOD IS NOT PATTERN-MATCHING ALONE. Deciding "is this line safe
|
|
20
|
+
# to delete" means knowing what the RUNTIME would resolve it to — so
|
|
21
|
+
# every codemod built on this module follows the same three steps:
|
|
22
|
+
# 1. Boot the real domain (or the self-hosted meta-domain) and read
|
|
23
|
+
# its IR to find CANDIDATES — provided by the caller's own
|
|
24
|
+
# `find_candidates`, since the actual redundancy rule is specific
|
|
25
|
+
# to whichever spine change this migration serves.
|
|
26
|
+
# 2. Locate and remove each candidate's own source text — the
|
|
27
|
+
# caller's own `apply_candidate`.
|
|
28
|
+
# 3. Re-boot from the edited text and diff the full IR export
|
|
29
|
+
# against the pre-edit export. Byte-identical -> keep. Anything
|
|
30
|
+
# else (including a RAISED exception — the self-hosted
|
|
31
|
+
# meta-domain DISPATCHES itself into being, S14, so a bad edit
|
|
32
|
+
# can surface as a runtime refusal, not just a differing export)
|
|
33
|
+
# -> revert and report SKIPPED, never silently guessed past.
|
|
34
|
+
#
|
|
35
|
+
# DESIGN-TIME CHECKLIST for the SPINE change a future codemod migrates
|
|
36
|
+
# corpus text for — both items below are real bugs THIS module's own
|
|
37
|
+
# first use found, not hypothetical:
|
|
38
|
+
# - Does the resolved value get inserted into an ORDER-SENSITIVE
|
|
39
|
+
# list (the exported IR is array-order-sensitive throughout)? If
|
|
40
|
+
# so, the spine's own insertion must preserve the ORIGINAL
|
|
41
|
+
# position, not just append — an append-at-end insertion only
|
|
42
|
+
# round-trips correctly for a value that already happened to be
|
|
43
|
+
# last.
|
|
44
|
+
# - Does resolution depend on ANOTHER construct already being
|
|
45
|
+
# declared (the aggregate/entity a `sets`/`append:` field resolves
|
|
46
|
+
# against)? If a creator command can be declared BEFORE the
|
|
47
|
+
# construct it creates (real, live: `command "Handler"` before
|
|
48
|
+
# `entity "Handler"`, one file down), one-pass resolution
|
|
49
|
+
# genuinely cannot see it yet — not a codemod bug, a structural
|
|
50
|
+
# limit worth naming rather than working around.
|
|
51
|
+
module Codemod
|
|
52
|
+
ROOT = File.expand_path("../..", __dir__)
|
|
53
|
+
|
|
54
|
+
EXAMPLE_ROOTS = Dir.glob(File.join(ROOT, "examples", "*")).select { |p| File.directory?(p) }.sort
|
|
55
|
+
META_FILES = (Dir.glob(File.join(ROOT, "lib/hecks/grammar/*.bluebook")) +
|
|
56
|
+
Dir.glob(File.join(ROOT, "lib/hecks/framework/bluebook/*.bluebook")) +
|
|
57
|
+
Dir.glob(File.join(ROOT, "lib/hecks/language/bluebook/**/*.bluebook"))).sort
|
|
58
|
+
|
|
59
|
+
# THE SAME LIGHTWEIGHT PATH `spec/spec_helper.rb`'s own
|
|
60
|
+
# `boot_in_memory` uses — `Hecks.with_registry` satisfies
|
|
61
|
+
# `Hecks.bluebook`'s own `collect`'s "loaded outside a boot" check
|
|
62
|
+
# without `Hecks.boot`'s full era-check/adapter-wiring path, which
|
|
63
|
+
# needs a LIVE Postgres connection for `compliance` and would make
|
|
64
|
+
# every codemod depend on a database it has no reason to touch — a
|
|
65
|
+
# codemod only ever reads a chapter's own declared IR, never a
|
|
66
|
+
# stored record.
|
|
67
|
+
PERSISTENCE_PORT = File.join(ROOT, "lib/hecks/ports/persistence.port")
|
|
68
|
+
EXTRACTION_PORT = File.join(ROOT, "lib/hecks/ports/extraction.port")
|
|
69
|
+
MEMORY_ADAPTER = File.join(ROOT, "lib/hecks/adapters/driven/memory.adapter")
|
|
70
|
+
PRISM_ADAPTER = File.join(ROOT, "lib/hecks/adapters/driven/prism.adapter")
|
|
71
|
+
|
|
72
|
+
def self.export_json(registry) = Hecks::Projector::Exporter.json(registry)
|
|
73
|
+
|
|
74
|
+
# `Hecks::Adapters::Prism` caches a file's parsed AST for the
|
|
75
|
+
# life of the PROCESS, keyed by path — fine for every existing
|
|
76
|
+
# caller (a file loads once per process: one `bin/ir` run, one
|
|
77
|
+
# rspec worker), but a codemod legitimately reloads the SAME path
|
|
78
|
+
# after editing it, and a stale cached tree reports a
|
|
79
|
+
# `given`/`ensures` block at its OLD line number, which no longer
|
|
80
|
+
# matches the freshly re-executed file's own `block.source_location`
|
|
81
|
+
# — surfacing as "did not survive extraction" on a perfectly valid
|
|
82
|
+
# file. `Prism.forget` is the real invalidation API this module's
|
|
83
|
+
# own first use motivated (found here, fixed at the source rather
|
|
84
|
+
# than left as a private `TREES.clear` poke from outside).
|
|
85
|
+
def self.load_bluebook(path)
|
|
86
|
+
paths = if path.is_a?(Array)
|
|
87
|
+
path
|
|
88
|
+
elsif File.directory?(path)
|
|
89
|
+
Dir.glob(File.join(path, "*.bluebook"))
|
|
90
|
+
else
|
|
91
|
+
[path]
|
|
92
|
+
end
|
|
93
|
+
paths.each { |file| Hecks::Adapters::Prism.forget(file) }
|
|
94
|
+
|
|
95
|
+
registry = Hecks::Runtime::Registry.new
|
|
96
|
+
Hecks.with_registry(registry) do
|
|
97
|
+
Kernel.load(PERSISTENCE_PORT)
|
|
98
|
+
Kernel.load(EXTRACTION_PORT)
|
|
99
|
+
Kernel.load(MEMORY_ADAPTER)
|
|
100
|
+
Kernel.load(PRISM_ADAPTER)
|
|
101
|
+
Hecks::Bluebook::MetaValidator.defer { paths.each { |file| Kernel.load(file) } }
|
|
102
|
+
Hecks::Bluebook::MetaValidator.judge_deferred!(registry)
|
|
103
|
+
end
|
|
104
|
+
registry
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# `forget_all`, not a single `forget` — the meta-domain is NINE
|
|
108
|
+
# files (`MetaValidator::GRAMMAR_FILES`) merged into one registry,
|
|
109
|
+
# and a caller here (the codemod runner) doesn't generally know in
|
|
110
|
+
# advance which ONE it just edited.
|
|
111
|
+
def self.boot_meta
|
|
112
|
+
Hecks::Adapters::Prism.forget_all
|
|
113
|
+
Hecks::Bluebook::MetaValidator.instance_variable_set(:@grammar_registry, nil)
|
|
114
|
+
export_json(Hecks::Bluebook::MetaValidator.grammar_registry)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.meta_registry
|
|
118
|
+
Hecks::Adapters::Prism.forget_all
|
|
119
|
+
Hecks::Bluebook::MetaValidator.instance_variable_set(:@grammar_registry, nil)
|
|
120
|
+
Hecks::Bluebook::MetaValidator.grammar_registry
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Walks every aggregate (and every nested entity, recursively)
|
|
124
|
+
# across every chapter in a booted registry, yielding [owning
|
|
125
|
+
# construct, command] pairs — `construct` is whichever
|
|
126
|
+
# Aggregate/Entity actually OWNS the command, the same distinction
|
|
127
|
+
# `AggregateBuilder#command` vs `EntityBuilder#command` already
|
|
128
|
+
# draws. Generic enough for any rule that needs to walk real
|
|
129
|
+
# commands, not specific to the attribute-redundancy rule.
|
|
130
|
+
def self.each_command(registry)
|
|
131
|
+
registry.bluebooks.each_value do |chapter|
|
|
132
|
+
chapter.aggregates.each do |aggregate|
|
|
133
|
+
walk = lambda do |construct|
|
|
134
|
+
construct.commands.each { |command| yield construct, command }
|
|
135
|
+
construct.entities.each(&walk) if construct.respond_to?(:entities)
|
|
136
|
+
end
|
|
137
|
+
walk.call(aggregate)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def self.owner_attribute(construct, name)
|
|
143
|
+
construct.attributes.find { |attr| attr.name.to_s == name.to_s }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# A LIST attribute's own element construct — the value object or
|
|
147
|
+
# entity `list_of(...)` names, resolved by `hecks_name` the same way
|
|
148
|
+
# `AttributeCollector#resolve_identity_field!` already does. Shared
|
|
149
|
+
# because "what does this list actually hold" is a question any
|
|
150
|
+
# append-shaped rule needs answered, not just this one.
|
|
151
|
+
def self.element_construct_for(construct, list_field)
|
|
152
|
+
list_attr = owner_attribute(construct, list_field)
|
|
153
|
+
return nil unless list_attr&.list?
|
|
154
|
+
|
|
155
|
+
pool = construct.respond_to?(:value_objects) ? construct.value_objects.dup : []
|
|
156
|
+
pool.concat(construct.entities) if construct.respond_to?(:entities)
|
|
157
|
+
pool.find { |c| c.hecks_name.to_s == list_attr.type.to_s }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Either a raised exception OR a differing export counts as unsafe
|
|
161
|
+
# — see the module header on why the meta-domain specifically can
|
|
162
|
+
# raise. Returns [value_or_nil, error_message_or_nil].
|
|
163
|
+
def self.safely
|
|
164
|
+
[yield, nil]
|
|
165
|
+
rescue StandardError => e
|
|
166
|
+
[nil, "#{e.class}: #{e.message}"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# THE REUSABLE RUNNER — every real bug fix this module carries lives
|
|
170
|
+
# here, not in a caller's own script. A caller supplies:
|
|
171
|
+
#
|
|
172
|
+
# find_candidates: ->(registry) { [...] }
|
|
173
|
+
# Given a booted registry, return every CANDIDATE this rule
|
|
174
|
+
# could migrate. A candidate is whatever shape the caller wants
|
|
175
|
+
# — `apply_candidate` is the only other thing that reads it.
|
|
176
|
+
#
|
|
177
|
+
# apply_candidate: ->(text, candidate) { [new_text, changed_bool] }
|
|
178
|
+
# Given one file's current text and one candidate, return the
|
|
179
|
+
# edited text and whether a match was actually found/removed —
|
|
180
|
+
# `false` (text unchanged) when the candidate doesn't apply to
|
|
181
|
+
# THIS file, which is how the meta-domain's multi-file search
|
|
182
|
+
# below finds the right one without the caller needing to know
|
|
183
|
+
# which file a candidate lives in ahead of time.
|
|
184
|
+
#
|
|
185
|
+
# label: ->(candidate) { "..." }
|
|
186
|
+
# One-line description for the results report.
|
|
187
|
+
class Runner
|
|
188
|
+
def initialize(find_candidates:, apply_candidate:, label:)
|
|
189
|
+
@find_candidates = find_candidates
|
|
190
|
+
@apply_candidate = apply_candidate
|
|
191
|
+
@label = label
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def run(dry_run: false)
|
|
195
|
+
results = { applied: [], skipped: [], clean: [] }
|
|
196
|
+
run_example_domains(results, dry_run)
|
|
197
|
+
run_meta_domain(results, dry_run)
|
|
198
|
+
results
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def report(results, dry_run:)
|
|
202
|
+
puts "== results (#{dry_run ? 'DRY RUN — nothing written' : 'applied'}) =="
|
|
203
|
+
puts "clean (no candidates): #{results[:clean].join(', ')}" unless results[:clean].empty?
|
|
204
|
+
results[:applied].each { |r| puts "APPLIED #{r[:file]}: #{r[:candidates].join(', ')}" }
|
|
205
|
+
results[:skipped].each { |r| puts "SKIPPED #{r[:file]} (#{r[:reason]}): #{Array(r[:candidates]).join(', ')}" }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
private
|
|
209
|
+
|
|
210
|
+
def apply_many(text, candidates)
|
|
211
|
+
applied = []
|
|
212
|
+
candidates.each do |c|
|
|
213
|
+
text, changed = @apply_candidate.call(text, c)
|
|
214
|
+
applied << c if changed
|
|
215
|
+
end
|
|
216
|
+
[text, applied]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# The per-candidate write/verify/revert sequence below is one
|
|
220
|
+
# coherent unit (see the comment ahead of the candidates.each
|
|
221
|
+
# loop for why it can't batch) — splitting it across methods just
|
|
222
|
+
# to satisfy the line count would scatter state four ways for no
|
|
223
|
+
# readability gain.
|
|
224
|
+
# rubocop:disable-next Metrics/BlockLength
|
|
225
|
+
def run_example_domains(results, dry_run)
|
|
226
|
+
Codemod::EXAMPLE_ROOTS.each do |domain_dir|
|
|
227
|
+
bluebook_files = Dir.glob(File.join(domain_dir, "bluebook", "*.bluebook"))
|
|
228
|
+
next if bluebook_files.empty?
|
|
229
|
+
|
|
230
|
+
before_json = Codemod.export_json(Codemod.load_bluebook(bluebook_files))
|
|
231
|
+
candidates = @find_candidates.call(Codemod.load_bluebook(bluebook_files))
|
|
232
|
+
|
|
233
|
+
if candidates.empty?
|
|
234
|
+
results[:clean] << domain_dir
|
|
235
|
+
next
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
live = bluebook_files.to_h { |file| [file, File.read(file)] }
|
|
239
|
+
applied_by_file = Hash.new { |hash, file| hash[file] = [] }
|
|
240
|
+
|
|
241
|
+
candidates.each do |candidate|
|
|
242
|
+
target_file = bluebook_files.find { |file| apply_many(live[file], [candidate]).last.any? }
|
|
243
|
+
unless target_file
|
|
244
|
+
results[:skipped] << { file: domain_dir, reason: "no matching source line", candidates: [@label.call(candidate)] }
|
|
245
|
+
next
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
original_text = live[target_file]
|
|
249
|
+
text, = apply_many(original_text, [candidate])
|
|
250
|
+
live[target_file] = text
|
|
251
|
+
File.write(target_file, text)
|
|
252
|
+
after_json, error = Codemod.safely do
|
|
253
|
+
Codemod.export_json(Codemod.load_bluebook(bluebook_files))
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
if after_json == before_json && !dry_run
|
|
257
|
+
applied_by_file[target_file] << candidate
|
|
258
|
+
elsif after_json == before_json
|
|
259
|
+
live[target_file] = original_text
|
|
260
|
+
File.write(target_file, original_text)
|
|
261
|
+
Codemod.safely { Codemod.load_bluebook(bluebook_files) }
|
|
262
|
+
applied_by_file[target_file] << candidate
|
|
263
|
+
else
|
|
264
|
+
live[target_file] = original_text
|
|
265
|
+
File.write(target_file, original_text)
|
|
266
|
+
Codemod.safely { Codemod.load_bluebook(bluebook_files) }
|
|
267
|
+
reason = error ? "reboot raised after edit (#{error}) — reverted" : "IR changed after edit — reverted"
|
|
268
|
+
results[:skipped] << { file: target_file, reason: reason, candidates: [@label.call(candidate)] }
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
applied_by_file.each do |file, applied|
|
|
273
|
+
results[:applied] << { file: file, candidates: applied.map(&@label) }
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# PER-CANDIDATE, not one batched write-then-verify — the
|
|
279
|
+
# meta-domain is ONE shared registry (SyntaxBoot DISPATCHES it
|
|
280
|
+
# into itself, S14), so a single unsafe candidate among many would
|
|
281
|
+
# otherwise sink every OTHER, genuinely safe candidate in the same
|
|
282
|
+
# run: this module's first real run found exactly that (25
|
|
283
|
+
# candidates, one dispatch-time break, all 25 reverted as a batch
|
|
284
|
+
# before this per-candidate loop existed). `before_meta` stays the
|
|
285
|
+
# fixed reference throughout — a truly safe candidate never
|
|
286
|
+
# changes the IR, so the running state's own export should always
|
|
287
|
+
# still equal the pristine original after each kept edit, by
|
|
288
|
+
# definition, no moving target needed.
|
|
289
|
+
def run_meta_domain(results, dry_run)
|
|
290
|
+
before_meta = Codemod.boot_meta
|
|
291
|
+
meta_candidates = @find_candidates.call(Codemod.meta_registry)
|
|
292
|
+
|
|
293
|
+
if meta_candidates.empty?
|
|
294
|
+
results[:clean] << "meta-domain"
|
|
295
|
+
return
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
live_meta = Codemod::META_FILES.to_h { |f| [f, File.read(f)] }
|
|
299
|
+
applied_by_file = Hash.new { |h, k| h[k] = [] }
|
|
300
|
+
|
|
301
|
+
meta_candidates.each do |candidate|
|
|
302
|
+
target_file = Codemod::META_FILES.find { |f| apply_many(live_meta[f], [candidate]).last.any? }
|
|
303
|
+
unless target_file
|
|
304
|
+
results[:skipped] << { file: "meta-domain", reason: "no matching source line", candidates: [@label.call(candidate)] }
|
|
305
|
+
next
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
text, = apply_many(live_meta[target_file], [candidate])
|
|
309
|
+
original_text = live_meta[target_file]
|
|
310
|
+
live_meta[target_file] = text
|
|
311
|
+
Codemod::META_FILES.each { |f| File.write(f, live_meta[f]) }
|
|
312
|
+
|
|
313
|
+
# DRY RUN STILL VERIFIES — see run_example_domains' own
|
|
314
|
+
# comment; the edit is always written and rebooted for real,
|
|
315
|
+
# then always reverted afterward when dry-run (whether or not
|
|
316
|
+
# it was safe) so the next candidate is judged against the
|
|
317
|
+
# true original state, never an accumulated hypothetical.
|
|
318
|
+
after_meta, error = Codemod.safely { Codemod.boot_meta }
|
|
319
|
+
|
|
320
|
+
if after_meta == before_meta && !dry_run
|
|
321
|
+
applied_by_file[target_file] << candidate
|
|
322
|
+
elsif after_meta == before_meta # dry-run, safe — revert, but count as applied
|
|
323
|
+
live_meta[target_file] = original_text
|
|
324
|
+
File.write(target_file, original_text)
|
|
325
|
+
Codemod.safely { Codemod.boot_meta }
|
|
326
|
+
applied_by_file[target_file] << candidate
|
|
327
|
+
else
|
|
328
|
+
live_meta[target_file] = original_text
|
|
329
|
+
File.write(target_file, original_text)
|
|
330
|
+
Codemod.safely { Codemod.boot_meta } # restore cache/memoized state to the reverted text
|
|
331
|
+
reason = error ? "reboot raised (#{error})" : "IR changed"
|
|
332
|
+
results[:skipped] << { file: "meta-domain", reason: reason, candidates: [@label.call(candidate)] }
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
applied_by_file.each do |file, candidates|
|
|
337
|
+
results[:applied] << { file: file, candidates: candidates.map(&@label) }
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
# The invisible field a built construct carries.
|
|
3
|
+
#
|
|
4
|
+
# A construct is a RECORD WITH AN OWNER CHAIN — the chapter (Bluebook)
|
|
5
|
+
# owns its aggregates, an aggregate owns everything declared on it — and the
|
|
6
|
+
# bluebook identity is carried in its own field, under a `hecks_` prefix that
|
|
7
|
+
# no domain attribute can collide with. Invisible means exactly that: not an
|
|
8
|
+
# attribute, not a key in `to_h`, not a reader on instances. Framework
|
|
9
|
+
# metadata about the construct, not part of the domain it describes.
|
|
10
|
+
#
|
|
11
|
+
# The identity is COMPUTED by walking owners rather than stamped, so nothing
|
|
12
|
+
# has to be re-stamped when a chapter is assembled after its aggregates:
|
|
13
|
+
#
|
|
14
|
+
# Pizzas the chapter — no owner
|
|
15
|
+
# Pizzas::Pizza an aggregate joins its chapter with ::
|
|
16
|
+
# Pizzas::Pizza.Price everything else joins its owner with .
|
|
17
|
+
#
|
|
18
|
+
# That spelling is not invented here. It is the id `MetaValidator::Judge`
|
|
19
|
+
# already mints in `#identify`, so a construct and the meta-domain's
|
|
20
|
+
# record OF that construct carry the same identity, and there is no
|
|
21
|
+
# translation table between them to be quietly wrong in.
|
|
22
|
+
#
|
|
23
|
+
# Usage:
|
|
24
|
+
#
|
|
25
|
+
# price = Class.new(ValueObject) # a declaration holder
|
|
26
|
+
# price.hecks_name = "Price"
|
|
27
|
+
# price.hecks_owner = pizza_ir # the Aggregate that declares it
|
|
28
|
+
# price.hecks_fqn # => "Pizzas::Pizza.Price"
|
|
29
|
+
module Construct
|
|
30
|
+
# A construct asked for an identity it cannot compute.
|
|
31
|
+
class Unowned < StandardError; end
|
|
32
|
+
|
|
33
|
+
# What declares this one — the chapter above an aggregate, the aggregate
|
|
34
|
+
# above a value object. nil for a chapter, which is the top.
|
|
35
|
+
attr_accessor :hecks_owner
|
|
36
|
+
|
|
37
|
+
attr_writer :hecks_name
|
|
38
|
+
|
|
39
|
+
# A chapter is the only construct that legitimately has no owner. Everything
|
|
40
|
+
# else is DECLARED IN something, so a missing owner is an unstamped construct
|
|
41
|
+
# rather than a top — see hecks_fqn.
|
|
42
|
+
attr_writer :hecks_root
|
|
43
|
+
|
|
44
|
+
def hecks_root? = @hecks_root ? true : false
|
|
45
|
+
|
|
46
|
+
# The name as the bluebook declares it, never the constant path.
|
|
47
|
+
def hecks_name = @hecks_name
|
|
48
|
+
|
|
49
|
+
# How this construct joins its owner. An aggregate is a member of its
|
|
50
|
+
# chapter's namespace (`::`) ; everything else is declared ON its owner
|
|
51
|
+
# (`.`). Overridden by Aggregate, defaulted here for every other construct.
|
|
52
|
+
def hecks_separator = "."
|
|
53
|
+
|
|
54
|
+
# REFUSES rather than guesses. A construct with no owner and no claim to be a
|
|
55
|
+
# chapter has simply not been stamped yet — entity commands are in that state
|
|
56
|
+
# while entities are still IR objects — and answering the bare name would be a
|
|
57
|
+
# plausible half-truth that no test would notice. That shape of falsehood is
|
|
58
|
+
# what this repo keeps finding, so it goes red instead.
|
|
59
|
+
def hecks_fqn
|
|
60
|
+
return hecks_name.to_s if hecks_root?
|
|
61
|
+
|
|
62
|
+
unless hecks_owner
|
|
63
|
+
raise Construct::Unowned,
|
|
64
|
+
"#{hecks_name.inspect} cannot say what declares it, so it has no " \
|
|
65
|
+
"identity — it was never stamped with an owner"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
"#{hecks_owner.hecks_fqn}#{hecks_separator}#{hecks_name}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# A TOOLING-INTERNAL DOMAIN, not a business one — never attached via
|
|
2
|
+
# `uses_framework`, never seen by an end-user domain. `bin/project_deploy`
|
|
3
|
+
# boots this directory directly (Ports::Loading.bootstrap + load_domain,
|
|
4
|
+
# the same pipeline lib/hecks/runtime/loader.rb uses, minus a
|
|
5
|
+
# `.world`/`.hecksagon` this domain has no need for) and dispatches one
|
|
6
|
+
# command against it before generating anything from a domain's own
|
|
7
|
+
# `deployed_to("AwsLambda")` settings.
|
|
8
|
+
#
|
|
9
|
+
# WHY THIS EXISTS: `world.bluebook`'s `Wiring`/`Setting` is a DELIBERATELY
|
|
10
|
+
# open, untyped map — "a verb's schema is not even contained in the file
|
|
11
|
+
# being parsed" (syntax.bluebook's own header on this). That's the right
|
|
12
|
+
# call for `Wiring` itself (an adapter verb's shape genuinely isn't the
|
|
13
|
+
# world language's business), but it leaves `deployed_to`'s ACTUAL shape
|
|
14
|
+
# validated nowhere — `bin/project_deploy` used to check it by hand
|
|
15
|
+
# (`deploy_settings[:adapter] == "AwsLambda" or abort ...`,
|
|
16
|
+
# `fetch(:region) { abort ... }`), the exact raw-Ruby-refusal pattern
|
|
17
|
+
# every other kind of bluebook mistake in this codebase does NOT use.
|
|
18
|
+
# This closes that gap the same way any other rule gets enforced here:
|
|
19
|
+
# as a real command, real invariants, a real corpus-testable refusal —
|
|
20
|
+
# not a hand-written abort string discovered only by running the script.
|
|
21
|
+
Hecks.bluebook "Deploy" do
|
|
22
|
+
aggregate "LambdaTarget" do
|
|
23
|
+
description "One domain's validated AWS Lambda deploy target — what deployed_to(\"AwsLambda\") must resolve to before bin/project_deploy generates anything from it."
|
|
24
|
+
|
|
25
|
+
identified_by :domain
|
|
26
|
+
|
|
27
|
+
attribute :domain, DomainName
|
|
28
|
+
attribute :region, Region
|
|
29
|
+
attribute :memory, Memory
|
|
30
|
+
attribute :timeout, Timeout
|
|
31
|
+
attribute :database, Database
|
|
32
|
+
attribute :web, Web
|
|
33
|
+
|
|
34
|
+
value_object "DomainName" do
|
|
35
|
+
attribute :value, String
|
|
36
|
+
invariant("a domain is named") { !value.to_s.empty? }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
value_object "Region" do
|
|
40
|
+
attribute :value, String
|
|
41
|
+
invariant("a region is named") { !value.to_s.empty? }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# "Postgres" (a plain AWS::RDS::DBInstance, Embryonaut's own live
|
|
45
|
+
# choice — bin/project_deploy's Ruby-side default when a domain's
|
|
46
|
+
# own deployed_to("AwsLambda") names none, so every domain
|
|
47
|
+
# declared before this attribute existed keeps generating the
|
|
48
|
+
# identical template it always has) or "Aurora" (Aurora Serverless
|
|
49
|
+
# v2, Postgres-compatible — the SAME `Adapters::Postgres`/
|
|
50
|
+
# `tokio_postgres` code on both the Ruby and rust/host sides
|
|
51
|
+
# either way; this only changes which CloudFormation resource
|
|
52
|
+
# bin/project_deploy emits, never a runtime adapter).
|
|
53
|
+
# "Shared" — no RDS/VPC of this domain's own at all; it borrows
|
|
54
|
+
# another already-deployed domain's instance instead (the
|
|
55
|
+
# storehouse), isolated from that domain's own data by a native
|
|
56
|
+
# Postgres SCHEMA (this domain's own lowercase name), not a
|
|
57
|
+
# separate database. Requires a sibling `owner "OtherDomain"`
|
|
58
|
+
# setting inside the SAME deployed_to("AwsLambda") block, naming
|
|
59
|
+
# which already-deployed domain owns the shared instance —
|
|
60
|
+
# bin/project_deploy checks for that setting itself (Ruby-level,
|
|
61
|
+
# the same as `pg_version`/`google_oauth_present` below it), not
|
|
62
|
+
# here: which domain owns the instance is a deploy-time fact, not
|
|
63
|
+
# a business invariant this validated aggregate's job to hold.
|
|
64
|
+
value_object "Database" do
|
|
65
|
+
attribute :value, String, one_of: ["Postgres", "Aurora", "Shared"]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# "None" (the default — nothing changes for a domain that names
|
|
69
|
+
# neither this nor drops a `lambda_handler.rb`, e.g. Banking) or
|
|
70
|
+
# "Rust" — the domain's OWN Lambda (rust/host) also serves a public
|
|
71
|
+
# web UI in-process (rust/host/src/web.rs), reading HECKS_IR_PATH
|
|
72
|
+
# (a plain-JSON sidecar bin/project_wasm writes beside the .wasm
|
|
73
|
+
# artifact) and dispatching through the SAME in-process
|
|
74
|
+
# dispatch::handle/read the internal {"verb"}/{"read"} path already
|
|
75
|
+
# uses — no second Lambda, no VPC Interface Endpoint, no Ruby.
|
|
76
|
+
# Deliberately NOT "Ruby" as a third member here: a Ruby WebFunction
|
|
77
|
+
# is the pre-existing, SEPARATE `lambda_handler.rb`-file-presence
|
|
78
|
+
# mechanism (Embryonaut's own), untouched by this attribute —
|
|
79
|
+
# bin/project_deploy refuses generation outright if a domain
|
|
80
|
+
# declares `web "Rust"` AND has a `lambda_handler.rb`, rather than
|
|
81
|
+
# silently picking one.
|
|
82
|
+
value_object "Web" do
|
|
83
|
+
attribute :value, String, one_of: ["None", "Rust"]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# 128-10240 MB — AWS Lambda's own real ceiling and floor, not a
|
|
87
|
+
# number this domain invented. A typo here used to fail deep inside
|
|
88
|
+
# a real `sam deploy`, sometimes minutes in; now it refuses before
|
|
89
|
+
# a single file is generated.
|
|
90
|
+
value_object "Memory" do
|
|
91
|
+
attribute :value, Integer
|
|
92
|
+
invariant("memory is at least 128 MB") { value >= 128 }
|
|
93
|
+
invariant("memory is at most 10240 MB") { value <= 10240 }
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# 900s — Lambda's own real ceiling.
|
|
97
|
+
value_object "Timeout" do
|
|
98
|
+
attribute :value, Integer
|
|
99
|
+
invariant("a timeout is positive") { value.positive? }
|
|
100
|
+
invariant("a timeout fits Lambda's own 900s ceiling") { value <= 900 }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# ALL FOUR REQUIRED — deliberately, not `optional: true` +
|
|
104
|
+
# `default:`. Confirmed directly against CommandInterpreter's own
|
|
105
|
+
# `normalize_args`/`coerce_declared_arguments` (interpreting.rb):
|
|
106
|
+
# `next unless normalized.key?(attribute.name)` skips coercion
|
|
107
|
+
# entirely for an absent key, so a command-level `default:` is
|
|
108
|
+
# honored ONLY at the JSON/Rust-codegen boundary (`scalar_from_json_
|
|
109
|
+
# expr`'s own domain), never by Ruby's own direct dispatch — a
|
|
110
|
+
# `default:` here would be quietly false advertising. "What memory
|
|
111
|
+
# defaults to when unspecified" stays exactly where it already
|
|
112
|
+
# lived (bin/project_deploy's own `fetch(:memory, 512)`); this
|
|
113
|
+
# command validates a FULLY-RESOLVED target, and bin/project_deploy
|
|
114
|
+
# resolves the default before ever dispatching.
|
|
115
|
+
command "Declare" do
|
|
116
|
+
goal "Validate one domain's AwsLambda deploy target before anything is generated from it"
|
|
117
|
+
|
|
118
|
+
attribute :domain, DomainName
|
|
119
|
+
attribute :region, Region
|
|
120
|
+
attribute :memory, Memory
|
|
121
|
+
attribute :timeout, Timeout
|
|
122
|
+
attribute :database, Database
|
|
123
|
+
attribute :web, Web
|
|
124
|
+
|
|
125
|
+
sets :domain
|
|
126
|
+
sets :region
|
|
127
|
+
sets :memory
|
|
128
|
+
sets :timeout
|
|
129
|
+
sets :database
|
|
130
|
+
sets :web
|
|
131
|
+
|
|
132
|
+
emits "LambdaTargetDeclared"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# `bin/project_tenant`'S OWN VALIDATED SHAPE — same split LambdaTarget
|
|
137
|
+
# draws with `bin/project_deploy`: this aggregate validates, that
|
|
138
|
+
# script GENERATES/ACTS (writes `environments/<slug>.world`, ensures
|
|
139
|
+
# the Postgres schema, boots once to prove it). Deliberately NOT a
|
|
140
|
+
# saga/process manager with durable runtime state — every step
|
|
141
|
+
# `bin/project_tenant` takes is already idempotent by construction
|
|
142
|
+
# (`CREATE SCHEMA IF NOT EXISTS` — PostgresEra#connect_for's own,
|
|
143
|
+
# File.write of the overlay — a plain overwrite, `Hecks.boot` itself),
|
|
144
|
+
# the same "generator script, not a business process" shape
|
|
145
|
+
# `bin/project_deploy` already is; a saga's own resumability earns
|
|
146
|
+
# its cost coordinating a real distributed transaction across
|
|
147
|
+
# multiple ports with real failure boundaries (Payments::Payment's
|
|
148
|
+
# own PaymentGateway, say) — three local, idempotent, single-process
|
|
149
|
+
# steps have no such boundary to resume across. A durable, reactable
|
|
150
|
+
# RUNTIME fact ("this tenant exists, is active, is suspended") is a
|
|
151
|
+
# different, later, business-facing thing — deliberately not built
|
|
152
|
+
# here, and deliberately not named Deploy::Tenant to avoid colliding
|
|
153
|
+
# with this validation-only aggregate should it ever land.
|
|
154
|
+
aggregate "Tenant" do
|
|
155
|
+
description "One tenant's validated identity — which realm and Postgres schema `bin/project_tenant` provisions before booting a domain directory under Runtime.boot's own `environment:` overlay for it."
|
|
156
|
+
|
|
157
|
+
identified_by :slug
|
|
158
|
+
|
|
159
|
+
attribute :slug, Slug
|
|
160
|
+
attribute :domain, DomainName
|
|
161
|
+
attribute :realm, Realm
|
|
162
|
+
attribute :schema, Schema
|
|
163
|
+
|
|
164
|
+
# LOWERCASE, NO SPACES — a slug becomes both the `environments/
|
|
165
|
+
# <slug>.world` filename and (by convention, not enforced here —
|
|
166
|
+
# `schema` is its own, separately-declared field precisely so a
|
|
167
|
+
# tenant CAN choose a different schema name than its slug)
|
|
168
|
+
# ordinarily the schema name too.
|
|
169
|
+
value_object "Slug" do
|
|
170
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
171
|
+
invariant("a tenant slug is named") { !value.to_s.empty? }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
value_object "DomainName" do
|
|
175
|
+
attribute :value, String
|
|
176
|
+
invariant("a tenant names the domain it is for") { !value.to_s.empty? }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
value_object "Realm" do
|
|
180
|
+
attribute :value, String
|
|
181
|
+
invariant("a tenant realm is named") { !value.to_s.empty? }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# POSTGRES'S OWN REAL IDENTIFIER RULES, loosely — lowercase (this
|
|
185
|
+
# adapter never quotes a mixed-case identifier back out consistently
|
|
186
|
+
# once created), letters/digits/underscores, starting with a letter.
|
|
187
|
+
# NOT a byte-length ceiling too (Postgres's own real 63-byte
|
|
188
|
+
# NAMEDATALEN limit) — the expression grammar invariants compile
|
|
189
|
+
# through admits a narrow arithmetic/comparison subset, not
|
|
190
|
+
# arbitrary method dispatch (`bytesize` included; found live,
|
|
191
|
+
# writing this: `Bluebook::Expression::Evaluator` tries to resolve
|
|
192
|
+
# a dotted chain as a FIELD lookup, not a method call, the same
|
|
193
|
+
# "admits + but not -, no parentheses" narrowness
|
|
194
|
+
# embryonaut_bluebooks/payments' own Refund command already
|
|
195
|
+
# documents). Postgres itself refuses a too-long identifier loudly
|
|
196
|
+
# on its own; not worth fighting the expression grammar for a
|
|
197
|
+
# corner case this rare.
|
|
198
|
+
value_object "Schema" do
|
|
199
|
+
attribute :value, String, pattern: '^[a-z][a-z0-9_]*$'
|
|
200
|
+
invariant("a schema is named") { !value.to_s.empty? }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
command "Declare" do
|
|
204
|
+
goal "Validate one tenant's shape before bin/project_tenant provisions its schema and overlay world"
|
|
205
|
+
|
|
206
|
+
attribute :slug, Slug
|
|
207
|
+
attribute :domain, DomainName
|
|
208
|
+
attribute :realm, Realm
|
|
209
|
+
attribute :schema, Schema
|
|
210
|
+
|
|
211
|
+
sets :slug
|
|
212
|
+
sets :domain
|
|
213
|
+
sets :realm
|
|
214
|
+
sets :schema
|
|
215
|
+
|
|
216
|
+
emits "TenantDeclared"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"audience": "Deploy",
|
|
3
|
+
"scopes": [
|
|
4
|
+
{
|
|
5
|
+
"scope": "deploy:lambda_target.declare",
|
|
6
|
+
"verb": "Deploy::LambdaTarget.Declare",
|
|
7
|
+
"role": null
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"scope": "deploy:tenant.declare",
|
|
11
|
+
"verb": "Deploy::Tenant.Declare",
|
|
12
|
+
"role": null
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"roles": [
|
|
16
|
+
|
|
17
|
+
]
|
|
18
|
+
}
|