hecks 2.7.0 → 2.9.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/exe/hecks +11 -0
- data/lib/hecks/adapters/driven/claude_code.rb +4 -104
- data/lib/hecks/adapters/driven/d1.rb +44 -237
- data/lib/hecks/adapters/driven/folder.rb +17 -76
- data/lib/hecks/adapters/driven/google_authentication.rb +15 -49
- data/lib/hecks/adapters/driven/governance_authorization.rb +23 -70
- data/lib/hecks/adapters/driven/heki/journal.rb +8 -41
- data/lib/hecks/adapters/driven/heki/saga_store.rb +12 -54
- data/lib/hecks/adapters/driven/heki/snapshot.rb +4 -12
- data/lib/hecks/adapters/driven/heki.rb +13 -42
- data/lib/hecks/adapters/driven/identity_registry.rb +3 -14
- data/lib/hecks/adapters/driven/in_memory_ordering.rb +9 -26
- data/lib/hecks/adapters/driven/in_process_key_vault.rb +2 -14
- data/lib/hecks/adapters/driven/lambda/client.rb +10 -51
- data/lib/hecks/adapters/driven/lambda.rb +10 -84
- data/lib/hecks/adapters/driven/local_storage.rb +5 -40
- data/lib/hecks/adapters/driven/memory.rb +18 -99
- data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +5 -21
- data/lib/hecks/adapters/driven/postgres/codec.rb +6 -28
- data/lib/hecks/adapters/driven/postgres/outbox.rb +3 -12
- data/lib/hecks/adapters/driven/postgres/reconnect.rb +4 -30
- data/lib/hecks/adapters/driven/postgres/schema_builder.rb +34 -108
- data/lib/hecks/adapters/driven/postgres.rb +145 -134
- data/lib/hecks/adapters/driven/postgres_era.adapter +4 -15
- data/lib/hecks/adapters/driven/prism.rb +7 -30
- data/lib/hecks/adapters/driven/sql_query_builder.rb +21 -121
- data/lib/hecks/adapters/driven/sqlite/codec.rb +8 -40
- data/lib/hecks/adapters/driven/sqlite/projection.rb +26 -82
- data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +40 -84
- data/lib/hecks/adapters/driven/sqlite.rb +128 -65
- data/lib/hecks/adapters/driven/tenant_provisioner.rb +15 -37
- data/lib/hecks/adapters/driven.rb +5 -36
- data/lib/hecks/adapters/driving/github_webhook.rb +8 -76
- data/lib/hecks/behaviors/dsl.rb +7 -25
- data/lib/hecks/behaviors/expectations.rb +32 -142
- data/lib/hecks/behaviors/ir.rb +7 -20
- data/lib/hecks/behaviors/rspec.rb +6 -17
- data/lib/hecks/behaviors/runner.rb +10 -34
- data/lib/hecks/behaviors.rb +4 -8
- data/lib/hecks/bluebook/aggregate.rb +9 -75
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +9 -38
- data/lib/hecks/bluebook/assembly/build.rb +4 -15
- data/lib/hecks/bluebook/assembly/contract.rb +15 -136
- data/lib/hecks/bluebook/assembly/contracts.rb +54 -174
- data/lib/hecks/bluebook/assembly/marks.rb +31 -223
- data/lib/hecks/bluebook/assembly/specializer.rb +6 -52
- data/lib/hecks/bluebook/assembly.rb +6 -45
- data/lib/hecks/bluebook/attribute.rb +12 -61
- data/lib/hecks/bluebook/behaviour/aggregate.rb +10 -39
- data/lib/hecks/bluebook/behaviour/attribute.rb +3 -14
- data/lib/hecks/bluebook/behaviour/chapter.rb +17 -42
- data/lib/hecks/bluebook/behaviour/command.rb +14 -66
- data/lib/hecks/bluebook/behaviour/domain_port.rb +4 -20
- data/lib/hecks/bluebook/behaviour/entity.rb +7 -27
- data/lib/hecks/bluebook/behaviour/hexagon.rb +23 -41
- data/lib/hecks/bluebook/behaviour/lifecycle.rb +15 -40
- data/lib/hecks/bluebook/behaviour/policy.rb +23 -52
- data/lib/hecks/bluebook/behaviour/process_manager.rb +30 -57
- data/lib/hecks/bluebook/behaviour/read_model.rb +38 -26
- data/lib/hecks/bluebook/behaviour/traits.rb +27 -64
- data/lib/hecks/bluebook/behaviour/value_object.rb +5 -24
- data/lib/hecks/bluebook/capabilities.rb +17 -49
- data/lib/hecks/bluebook/chapter.rb +10 -48
- data/lib/hecks/bluebook/command.rb +23 -81
- data/lib/hecks/bluebook/domain_port.rb +14 -63
- data/lib/hecks/bluebook/dsl/aggregate_builder/sealing.rb +50 -229
- data/lib/hecks/bluebook/dsl/aggregate_builder.rb +50 -377
- data/lib/hecks/bluebook/dsl/attribute_collector.rb +33 -204
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +17 -65
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +86 -708
- data/lib/hecks/bluebook/dsl/bluebook_builder.rb +56 -170
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +1 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +74 -546
- data/lib/hecks/bluebook/dsl/const_shim.rb +8 -41
- data/lib/hecks/bluebook/dsl/domain_port_builder.rb +12 -87
- data/lib/hecks/bluebook/dsl/entity_builder.rb +44 -330
- data/lib/hecks/bluebook/dsl/generic_dispatch.rb +20 -269
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +26 -189
- data/lib/hecks/bluebook/dsl/identity_declaration.rb +13 -88
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +6 -20
- data/lib/hecks/bluebook/dsl/policy_builder.rb +16 -88
- data/lib/hecks/bluebook/dsl/port_builder.rb +2 -6
- data/lib/hecks/bluebook/dsl/port_operation_builder.rb +22 -96
- data/lib/hecks/bluebook/dsl/process_manager_builder.rb +31 -184
- data/lib/hecks/bluebook/dsl/query_builder.rb +11 -42
- data/lib/hecks/bluebook/dsl/read_model_builder.rb +75 -201
- data/lib/hecks/bluebook/dsl/rule_reference.rb +22 -163
- data/lib/hecks/bluebook/dsl/translation_builder.rb +28 -114
- data/lib/hecks/bluebook/dsl/value_object_builder.rb +10 -124
- data/lib/hecks/bluebook/dsl/word_gate.rb +16 -162
- data/lib/hecks/bluebook/dsl/world_builder.rb +19 -106
- data/lib/hecks/bluebook/entity.rb +13 -71
- data/lib/hecks/bluebook/expression/ast_json.rb +24 -190
- data/lib/hecks/bluebook/expression/ast_reader.rb +12 -54
- data/lib/hecks/bluebook/expression/canonical_form.rb +11 -88
- data/lib/hecks/bluebook/expression/evaluator.rb +47 -295
- data/lib/hecks/bluebook/expression/resolver/block_predicates.rb +30 -147
- data/lib/hecks/bluebook/expression/resolver.rb +37 -660
- data/lib/hecks/bluebook/hexagon.rb +5 -28
- data/lib/hecks/bluebook/lifecycle.rb +5 -16
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +1 -7
- data/lib/hecks/bluebook/meta_validator/judge.rb +111 -361
- data/lib/hecks/bluebook/meta_validator/plan.rb +33 -162
- data/lib/hecks/bluebook/meta_validator/port_judge.rb +2 -7
- data/lib/hecks/bluebook/meta_validator/readings.rb +56 -342
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +24 -176
- data/lib/hecks/bluebook/meta_validator/shapes.rb +54 -268
- data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +47 -308
- data/lib/hecks/bluebook/meta_validator/translation_judge.rb +7 -43
- data/lib/hecks/bluebook/meta_validator/world_judge.rb +6 -18
- data/lib/hecks/bluebook/meta_validator.rb +79 -349
- data/lib/hecks/bluebook/model_check/client_profile.rb +10 -91
- data/lib/hecks/bluebook/model_check.rb +102 -600
- data/lib/hecks/bluebook/pattern_subset.rb +11 -69
- data/lib/hecks/bluebook/process_manager.rb +13 -83
- data/lib/hecks/bluebook/project_loader.rb +3 -39
- data/lib/hecks/bluebook/project_register.rb +12 -60
- data/lib/hecks/bluebook/query.rb +9 -48
- data/lib/hecks/bluebook/read_model.rb +57 -81
- data/lib/hecks/bluebook/reference.rb +7 -41
- data/lib/hecks/bluebook/smoke_test.rb +8 -98
- data/lib/hecks/bluebook/synthesizer.rb +13 -62
- data/lib/hecks/bluebook/translation.rb +26 -62
- data/lib/hecks/bluebook/value_object.rb +11 -36
- data/lib/hecks/bluebook.rb +5 -22
- data/lib/hecks/cache_dir.rb +84 -0
- data/lib/hecks/cli/document.rb +86 -0
- data/lib/hecks/cli/ir.rb +45 -0
- data/lib/hecks/cli/mcp.rb +31 -0
- data/lib/hecks/cli/mcp_door.rb +366 -0
- data/lib/hecks/cli/model_check.rb +203 -0
- data/lib/hecks/cli/project_cli.rb +90 -0
- data/lib/hecks/cli/project_diagrams.rb +36 -0
- data/lib/hecks/cli/run.rb +173 -0
- data/lib/hecks/cli/smoke_test.rb +45 -0
- data/lib/hecks/cli/stores.rb +53 -0
- data/lib/hecks/cli.rb +160 -0
- data/lib/hecks/construct.rb +8 -34
- data/lib/hecks/deploy/bluebook/deploy.bluebook +33 -142
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +2 -9
- data/lib/hecks/embryonaut_bluebook/lock.rb +3 -35
- data/lib/hecks/embryonaut_bluebook/shape.rb +1 -17
- data/lib/hecks/embryonaut_bluebook/vendor.rb +8 -53
- data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +4 -19
- data/lib/hecks/embryonaut_bluebook.rb +4 -71
- data/lib/hecks/facade/cli_door.rb +24 -106
- data/lib/hecks/facade/cli_runner.rb +28 -172
- data/lib/hecks/facade/command_request.rb +13 -33
- data/lib/hecks/facade/handle.rb +34 -121
- data/lib/hecks/facade/json_door.rb +31 -146
- data/lib/hecks/facade/surface/aggregate_door.rb +39 -104
- data/lib/hecks/facade/surface/chapter.rb +10 -54
- data/lib/hecks/facade/surface.rb +8 -23
- data/lib/hecks/facade.rb +10 -25
- data/lib/hecks/forms/app.rb +15 -70
- data/lib/hecks/forms/command_form_renderer.rb +10 -29
- data/lib/hecks/forms/field_renderer.rb +17 -156
- data/lib/hecks/forms/field_shape.rb +30 -222
- data/lib/hecks/forms/html.rb +12 -47
- data/lib/hecks/forms/index_renderer.rb +1 -4
- data/lib/hecks/forms/page.rb +3 -9
- data/lib/hecks/forms/params.rb +14 -60
- data/lib/hecks/forms/port_argument.rb +2 -19
- data/lib/hecks/forms/query_form_renderer.rb +4 -79
- data/lib/hecks/forms/record_renderer.rb +8 -75
- data/lib/hecks/forms/record_table.rb +7 -50
- data/lib/hecks/forms/reference_options.rb +3 -30
- data/lib/hecks/forms/value_object_shape.rb +8 -35
- data/lib/hecks/forms.rb +5 -33
- data/lib/hecks/fqn.rb +5 -12
- data/lib/hecks/framework/bluebook/compliance.bluebook +16 -65
- data/lib/hecks/framework/bluebook/console_settings.bluebook +47 -134
- data/lib/hecks/framework/bluebook/framework.hecksagon +5 -14
- data/lib/hecks/framework/bluebook/governance.bluebook +13 -34
- data/lib/hecks/framework/bluebook/identity.bluebook +11 -26
- data/lib/hecks/framework/bluebook/privacy.bluebook +8 -33
- data/lib/hecks/framework.rb +11 -80
- data/lib/hecks/freezer.rb +5 -39
- data/lib/hecks/grammar/expression.bluebook +15 -40
- data/lib/hecks/grammar/translation.bluebook +3 -6
- data/lib/hecks/grammar.rb +16 -41
- data/lib/hecks/ir.rb +14 -83
- data/lib/hecks/language/adapter.bluebook +2 -7
- data/lib/hecks/language/bluebook/aggregate.bluebook +56 -221
- data/lib/hecks/language/bluebook/attaches/paging.bluebook +6 -15
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -83
- data/lib/hecks/language/bluebook/bluebook.hecksagon +3 -15
- data/lib/hecks/language/bluebook/command.bluebook +27 -144
- data/lib/hecks/language/bluebook/entity.bluebook +24 -97
- data/lib/hecks/language/bluebook/policy.bluebook +16 -67
- data/lib/hecks/language/bluebook/process_manager.bluebook +28 -151
- data/lib/hecks/language/bluebook/projection.bluebook +126 -60
- data/lib/hecks/language/bluebook/query.bluebook +12 -43
- data/lib/hecks/language/bluebook/shape.bluebook +23 -73
- data/lib/hecks/language/bluebook/syntax.bluebook +51 -262
- data/lib/hecks/language/bluebook/vocabulary.bluebook +98 -197
- data/lib/hecks/language/hecksagon/adapter_binding.bluebook +3 -10
- data/lib/hecks/language/hecksagon/domain_port.bluebook +4 -19
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +6 -25
- data/lib/hecks/language/hecksagon/port_operation.bluebook +3 -14
- data/lib/hecks/language/oidc.json +35 -0
- data/lib/hecks/language/port.bluebook +4 -17
- data/lib/hecks/language/translation/translation.bluebook +3 -9
- data/lib/hecks/language/translation/translation_aggregate.bluebook +5 -24
- data/lib/hecks/language/world/wiring.bluebook +1 -3
- data/lib/hecks/language/world/world.bluebook +5 -7
- data/lib/hecks/literal.rb +8 -50
- data/lib/hecks/mcp_door_scope.rb +95 -0
- data/lib/hecks/mcp_stdio_guard.rb +4 -41
- data/lib/hecks/naming.rb +29 -137
- data/lib/hecks/ports/access_control.rb +7 -23
- data/lib/hecks/ports/agent/answers.rb +5 -23
- data/lib/hecks/ports/agent.rb +47 -172
- data/lib/hecks/ports/authentication.rb +12 -39
- data/lib/hecks/ports/authorization.rb +16 -43
- data/lib/hecks/ports/clock.rb +5 -45
- data/lib/hecks/ports/extraction.rb +7 -12
- data/lib/hecks/ports/identity_assignment.rb +8 -29
- data/lib/hecks/ports/identity_generation.rb +6 -29
- data/lib/hecks/ports/identity_resolution.rb +2 -10
- data/lib/hecks/ports/key_vault.rb +9 -45
- data/lib/hecks/ports/persistence/append_only.rb +62 -65
- data/lib/hecks/ports/persistence/binding_policy.rb +4 -14
- data/lib/hecks/ports/persistence/codec_boundary.rb +20 -80
- data/lib/hecks/ports/persistence/null_saga_store.rb +4 -20
- data/lib/hecks/ports/persistence/plugin.rb +13 -42
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +38 -141
- data/lib/hecks/ports/persistence/plugins/era/era_guard/shape_diff.rb +15 -115
- data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +16 -84
- data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +7 -26
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -22
- data/lib/hecks/ports/persistence/plugins/era/lineage.rb +55 -181
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/events.rb +77 -0
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +12 -122
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/field_cache.rb +28 -128
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +73 -474
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/mint_transaction.rb +31 -124
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +45 -219
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/resumable_backfill.rb +13 -108
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/tail_merge.rb +18 -75
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/transform_installer.rb +9 -49
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +6 -123
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/coverage_check.rb +23 -55
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +14 -72
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/merge_coordinator.rb +7 -16
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +14 -115
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager.rb +13 -45
- data/lib/hecks/ports/persistence/plugins/era/postgres_era.rb +127 -554
- data/lib/hecks/ports/persistence/plugins/era/storage_shape.rb +13 -63
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +2 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_one.rb +3 -14
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +13 -58
- data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -17
- data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +9 -26
- data/lib/hecks/ports/persistence/plugins/era/translation/reattest.rb +9 -25
- data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +22 -122
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/differ.rb +34 -123
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/renderer.rb +5 -12
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold/writer.rb +3 -9
- data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +2 -14
- data/lib/hecks/ports/persistence/plugins/era.rb +4 -23
- data/lib/hecks/ports/persistence/postgres_dump/connection.rb +7 -23
- data/lib/hecks/ports/persistence/postgres_dump.rb +4 -35
- data/lib/hecks/ports/persistence/remote_runtime.rb +2 -24
- data/lib/hecks/ports/persistence/repository_factory.rb +8 -27
- data/lib/hecks/ports/persistence/state_codec.rb +23 -103
- data/lib/hecks/ports/persistence.rb +7 -14
- data/lib/hecks/ports/projection.rb +42 -84
- data/lib/hecks/ports/query/in_memory.rb +10 -58
- data/lib/hecks/ports/query/ordering.rb +11 -32
- data/lib/hecks/ports/query.rb +14 -37
- data/lib/hecks/ports.rb +2 -8
- data/lib/hecks/projections/bootstrap_table.rb +11 -43
- data/lib/hecks/projections/deploy/fargate/assembly.rb +2 -15
- data/lib/hecks/projections/deploy/fargate/cdn.rb +1 -48
- data/lib/hecks/projections/deploy/fargate/cdn_default.rb +2 -11
- data/lib/hecks/projections/deploy/fargate/check.rb +5 -14
- data/lib/hecks/projections/deploy/fargate/containers.rb +3 -78
- data/lib/hecks/projections/deploy/fargate/extras.rb +3 -44
- data/lib/hecks/projections/deploy/fargate/monitoring.rb +0 -44
- data/lib/hecks/projections/deploy/fargate/settings.rb +2 -54
- data/lib/hecks/projections/deploy/fargate/yaml.rb +13 -58
- data/lib/hecks/projections/deploy/fargate.rb +35 -202
- data/lib/hecks/projections/deploy/lambda.rb +151 -679
- data/lib/hecks/projections/deploy/preview/access_yaml.rb +1 -33
- data/lib/hecks/projections/deploy/preview/containers.rb +6 -111
- data/lib/hecks/projections/deploy/preview/routing_yaml.rb +4 -56
- data/lib/hecks/projections/deploy/preview/script.rb +2 -14
- data/lib/hecks/projections/deploy/preview/settings.rb +3 -33
- data/lib/hecks/projections/deploy/preview/task_yaml.rb +2 -38
- data/lib/hecks/projections/deploy/preview/template.rb +2 -58
- data/lib/hecks/projections/deploy/preview.rb +2 -67
- data/lib/hecks/projections/deploy/scripts/settings.rb +3 -25
- data/lib/hecks/projections/deploy/scripts.rb +4 -57
- data/lib/hecks/projections/deploy/shared.rb +47 -146
- data/lib/hecks/projections/deploy/smoke.rb +4 -61
- data/lib/hecks/projections/deploy/template_diff/comparer.rb +3 -25
- data/lib/hecks/projections/deploy/template_diff/loader.rb +2 -11
- data/lib/hecks/projections/deploy/template_diff.rb +5 -57
- data/lib/hecks/projections/diagrams.rb +20 -582
- data/lib/hecks/projections/glossary/html.rb +14 -130
- data/lib/hecks/projections/glossary/markdown.rb +8 -69
- data/lib/hecks/projections/glossary/mermaid.rb +9 -66
- data/lib/hecks/projections/glossary/sensitivity.rb +5 -34
- data/lib/hecks/projections/glossary/sentences.rb +14 -159
- data/lib/hecks/projections/glossary.rb +30 -222
- data/lib/hecks/projections/ir.rb +3 -10
- data/lib/hecks/projections/model/deviations.rb +15 -42
- data/lib/hecks/projections/model.rb +12 -51
- data/lib/hecks/projections/oidc.rb +12 -56
- data/lib/hecks/projections/parser_table.rb +9 -28
- data/lib/hecks/projections/reference.rb +9 -16
- data/lib/hecks/projections/rust_vocabulary.rb +12 -250
- data/lib/hecks/projections/shape.rb +3 -29
- data/lib/hecks/projections/statements.rb +7 -86
- data/lib/hecks/projections/vocabulary.rb +6 -22
- data/lib/hecks/projections.rb +2 -16
- data/lib/hecks/projector/cli_projector.rb +60 -375
- data/lib/hecks/projector/docs_projector.rb +10 -201
- data/lib/hecks/projector/exporter.rb +46 -138
- data/lib/hecks/projector/ir_projector.rb +2 -9
- data/lib/hecks/projector/narrate_projector.rb +15 -189
- data/lib/hecks/projector/target.rb +13 -76
- data/lib/hecks/projector.rb +30 -121
- data/lib/hecks/query_specification/common/comparators.rb +5 -26
- data/lib/hecks/query_specification/common/comparison.rb +35 -143
- data/lib/hecks/query_specification/common/dsl.rb +8 -24
- data/lib/hecks/query_specification/common/null_policy.rb +20 -70
- data/lib/hecks/query_specification/common/options.rb +2 -6
- data/lib/hecks/query_specification/common/where_clause.rb +2 -10
- data/lib/hecks/query_specification/field_path.rb +37 -77
- data/lib/hecks/query_specification/hop_path.rb +12 -76
- data/lib/hecks/release/gem_pin.rb +2 -26
- data/lib/hecks/release/runner/ci_publisher.rb +93 -0
- data/lib/hecks/release/runner/commands.rb +66 -0
- data/lib/hecks/release/runner/console.rb +52 -0
- data/lib/hecks/release/runner/gem_publisher.rb +43 -0
- data/lib/hecks/release/runner/git.rb +48 -0
- data/lib/hecks/release/runner/npm_publisher.rb +75 -0
- data/lib/hecks/release/runner/preflight.rb +104 -0
- data/lib/hecks/release/runner/published.rb +55 -0
- data/lib/hecks/release/runner/tagger.rb +80 -0
- data/lib/hecks/release/runner.rb +215 -0
- data/lib/hecks/rendering.rb +4 -26
- data/lib/hecks/router/namespace_installer.rb +12 -36
- data/lib/hecks/router.rb +4 -8
- data/lib/hecks/runtime/aggregate_lock.rb +8 -33
- data/lib/hecks/runtime/boot_gates.rb +3 -18
- data/lib/hecks/runtime/caller.rb +19 -59
- data/lib/hecks/runtime/capability_graph.rb +5 -21
- data/lib/hecks/runtime/command_interpreter/argument_gate.rb +16 -78
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +23 -160
- data/lib/hecks/runtime/command_interpreter.rb +125 -307
- data/lib/hecks/runtime/command_rules/admissibility.rb +95 -366
- data/lib/hecks/runtime/command_rules/arithmetic.rb +66 -196
- data/lib/hecks/runtime/command_rules/authorization.rb +13 -45
- data/lib/hecks/runtime/command_rules/emission.rb +8 -21
- data/lib/hecks/runtime/command_rules/references.rb +23 -202
- data/lib/hecks/runtime/dependency_planning.rb +30 -102
- data/lib/hecks/runtime/dispatcher.rb +82 -241
- data/lib/hecks/runtime/entity_element.rb +40 -462
- data/lib/hecks/runtime/entity_interpreter.rb +65 -220
- data/lib/hecks/runtime/errors.rb +32 -115
- data/lib/hecks/runtime/event.rb +3 -25
- data/lib/hecks/runtime/identity.rb +18 -154
- data/lib/hecks/runtime/instance.rb +18 -150
- data/lib/hecks/runtime/interpreting.rb +11 -70
- data/lib/hecks/runtime/invocation.rb +28 -164
- data/lib/hecks/runtime/loader.rb +48 -186
- data/lib/hecks/runtime/outbox.rb +38 -261
- data/lib/hecks/runtime/policy_interpreter.rb +38 -200
- data/lib/hecks/runtime/port_operation_interpreter.rb +26 -87
- data/lib/hecks/runtime/query_interpreter.rb +45 -187
- data/lib/hecks/runtime/reaction_invocation.rb +36 -138
- data/lib/hecks/runtime/read_model_interpreter.rb +139 -208
- data/lib/hecks/runtime/rebuild_sweep.rb +8 -54
- data/lib/hecks/runtime/reference_hop.rb +8 -81
- data/lib/hecks/runtime/refusal_wording.rb +8 -89
- data/lib/hecks/runtime/registry/saga_persistence.rb +10 -102
- data/lib/hecks/runtime/registry/verification.rb +52 -225
- data/lib/hecks/runtime/registry/world_defaults.rb +4 -53
- data/lib/hecks/runtime/registry.rb +103 -244
- data/lib/hecks/runtime/remote_dispatcher.rb +37 -152
- data/lib/hecks/runtime/routing.rb +4 -15
- data/lib/hecks/runtime/saga_interpreter/correlation.rb +14 -61
- data/lib/hecks/runtime/saga_interpreter.rb +79 -263
- data/lib/hecks/runtime/saga_pending_dispatch.rb +2 -43
- data/lib/hecks/runtime/tenant_check.rb +6 -68
- data/lib/hecks/runtime/tenant_scope.rb +3 -28
- data/lib/hecks/runtime/value/admission.rb +16 -67
- data/lib/hecks/runtime/value/coercion.rb +155 -641
- data/lib/hecks/runtime/value/entity_list_coercion.rb +26 -219
- data/lib/hecks/runtime/value.rb +12 -84
- data/lib/hecks/runtime.rb +17 -65
- data/lib/hecks/storehouse.rb +146 -567
- data/lib/hecks/tenancy/bluebook/tenancy.bluebook +8 -32
- data/lib/hecks/tenancy/bluebook/tenancy.hecksagon +6 -23
- data/lib/hecks/vendoring/git_environment.rb +2 -8
- data/lib/hecks/vendoring/git_source.rb +4 -10
- data/lib/hecks/vendoring.rb +7 -46
- data/lib/hecks/version.rb +3 -14
- data/lib/hecks/vocabulary.rb +5 -0
- data/lib/hecks.rb +32 -82
- data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +8 -46
- data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +11 -80
- data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +10 -99
- metadata +30 -59
- data/lib/hecks/bench/cli.rb +0 -74
- data/lib/hecks/bench/environment.rb +0 -100
- data/lib/hecks/bench/postgres_probe.rb +0 -41
- data/lib/hecks/bench/report.rb +0 -116
- data/lib/hecks/bench/ruby_runner.rb +0 -151
- data/lib/hecks/bench/run.rb +0 -55
- data/lib/hecks/bench/rust_runner.rb +0 -165
- data/lib/hecks/bench/stats.rb +0 -75
- data/lib/hecks/bench/suite.rb +0 -172
- data/lib/hecks/bench/workload.rb +0 -137
- data/lib/hecks/bench.rb +0 -28
- data/lib/hecks/codemod.rb +0 -443
- data/lib/hecks/corpus.rb +0 -479
- data/lib/hecks/doc/reference.rb +0 -585
- data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
- data/lib/hecks/fuzzing/combination_miner.rb +0 -178
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
- data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
- data/lib/hecks/fuzzing/differential.rb +0 -192
- data/lib/hecks/fuzzing/domain_generator.rb +0 -871
- data/lib/hecks/fuzzing/era_boundary.rb +0 -154
- data/lib/hecks/fuzzing/form_census.rb +0 -285
- data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
- data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
- data/lib/hecks/fuzzing/isolated_boot.rb +0 -658
- data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
- data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
- data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
- data/lib/hecks/fuzzing/properties/guards.rb +0 -398
- data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
- data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
- data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
- data/lib/hecks/fuzzing/properties/querying.rb +0 -260
- data/lib/hecks/fuzzing/properties.rb +0 -267
- data/lib/hecks/fuzzing/qa_settings.rb +0 -164
- data/lib/hecks/fuzzing/replay.rb +0 -930
- data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
- data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
- data/lib/hecks/fuzzing/self_consistency.rb +0 -825
- data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
- data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
- data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
- data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
- data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
- data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
- data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
- data/lib/hecks/fuzzing/shrinker.rb +0 -309
- data/lib/hecks/fuzzing/structural_skips.rb +0 -53
- data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
- data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
- data/lib/hecks/fuzzing/value_generator.rb +0 -340
- data/lib/hecks/fuzzing.rb +0 -23
- data/lib/hecks/grammar/evolve.rb +0 -524
- data/lib/hecks/query_ir.rb +0 -482
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f6baf48801bf4f341c3a39a05215c2a0ce3f345b22068f84d0e04c91c3c76f6
|
|
4
|
+
data.tar.gz: 6eda1623f06f2607400ff7a2b13c10775ed60416c91f36f7d4a36bdb9d6adf39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8d8670a30de7d7318a84cf6a25f78b394c6974fb42f61697fbe20d0bc5642455740c644fd055cfe7f347d0bed254ab93fd42ce596fc444af6de8bcde86e7291
|
|
7
|
+
data.tar.gz: dc3b18d73758d235d369c0158729b8186e8d6a6d8c2c5040d26f772876b43d8ff8eb29a784e166585af1d890bf5620de0fa456934bf6568ba96d1318858e9079
|
data/exe/hecks
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# The `hecks` command the gem installs: run, read, check and serve a domain.
|
|
4
|
+
# Routes to `Hecks::CLI` (lib/hecks/cli.rb); run with no arguments to list subcommands.
|
|
5
|
+
|
|
6
|
+
# The gem's own `lib` goes first, as for every `bin/` script, so this also
|
|
7
|
+
# runs straight from a checkout.
|
|
8
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
|
9
|
+
require "hecks/cli"
|
|
10
|
+
|
|
11
|
+
exit Hecks::CLI.start(ARGV)
|
|
@@ -6,31 +6,8 @@ require_relative "../../ports/agent"
|
|
|
6
6
|
|
|
7
7
|
module Hecks
|
|
8
8
|
module Adapters
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# `Ports::Agent`'s own scripted double (`spec/fixtures/scripted_
|
|
12
|
-
# agent.{adapter,rb}`) is the deterministic sibling every spec binds
|
|
13
|
-
# instead, the same relationship `SecureRandomIdentity` already has
|
|
14
|
-
# to `SequentialIdentity`.
|
|
15
|
-
#
|
|
16
|
-
# **This file owns transport only** — spawning the process, unwrapping
|
|
17
|
-
# the CLI's own JSON envelope (`{"result": "..."}`) down to the
|
|
18
|
-
# model's raw text, and parsing that text as JSON. It hands back a
|
|
19
|
-
# plain Hash. Whether that Hash has the keys a caller asked for, and
|
|
20
|
-
# whether its values are within the closed vocabularies this port
|
|
21
|
-
# recognizes (a critique's `kind`, a proposal's `verb` pattern) is
|
|
22
|
-
# `Ports::Agent::Answers`' job, not this file's — see `ports/agent.rb`'s
|
|
23
|
-
# own header for why that split is load-bearing.
|
|
24
|
-
#
|
|
25
|
-
# `Open3.capture2` with an explicit argv array, never a shell string
|
|
26
|
-
# — the same shape every other subprocess call in this repo already
|
|
27
|
-
# uses (`bin/rust_conformance`), and the reason `claude` being
|
|
28
|
-
# shell-aliased to `claude --dangerously-skip-permissions` in a
|
|
29
|
-
# human's own terminal never reaches this code at all: an argv spawn
|
|
30
|
-
# invokes the real binary directly, no shell, no alias.
|
|
31
|
-
#
|
|
32
|
-
# `--allowedTools ""` — this adapter asks a question and reads back
|
|
33
|
-
# text; it never wants the model reaching for a tool mid-answer.
|
|
9
|
+
# Real `agent` fulfillment: one `claude -p --output-format json` process per call.
|
|
10
|
+
# Transport only; shape and vocabulary checks belong to `Ports::Agent::Answers`.
|
|
34
11
|
module ClaudeCode
|
|
35
12
|
TIMEOUT_SECONDS = 120
|
|
36
13
|
|
|
@@ -40,22 +17,6 @@ module Hecks
|
|
|
40
17
|
|
|
41
18
|
module_function
|
|
42
19
|
|
|
43
|
-
# Asks the model for the next best interview question, as a raw parsed-JSON reply.
|
|
44
|
-
#
|
|
45
|
-
# **The next best question**. `state` is whatever
|
|
46
|
-
# `Interview::Session#declaration`/`#gaps` produced — passed
|
|
47
|
-
# through as JSON, not reformatted, so this adapter never
|
|
48
|
-
# re-derives what the session already knows.
|
|
49
|
-
#
|
|
50
|
-
# @param state [Hash] the interview's whole current picture (declaration plus gaps),
|
|
51
|
-
# JSON-able
|
|
52
|
-
# @param asked [Array<Object>] JSON-able record of the questions already asked
|
|
53
|
-
# @return [Object] the parsed JSON reply, expected to be a Hash holding a `"questions"`
|
|
54
|
-
# array; shape validated downstream by `Ports::Agent::Answers.questions`
|
|
55
|
-
# @raise [Ports::Agent::Unavailable] if the `claude` binary is missing, the subprocess
|
|
56
|
-
# fails, or the call times out
|
|
57
|
-
# @raise [Ports::Agent::ValidationError] if the CLI's own envelope has no `"result"`, or
|
|
58
|
-
# the result text is not valid JSON
|
|
59
20
|
def ask(state:, asked:)
|
|
60
21
|
call(
|
|
61
22
|
system: SYSTEM_PREFIX + "Given the domain model so far, ask the single best next " \
|
|
@@ -65,18 +26,6 @@ module Hecks
|
|
|
65
26
|
)
|
|
66
27
|
end
|
|
67
28
|
|
|
68
|
-
# Turns a human's sentence into proposed declarations, as a raw parsed-JSON reply.
|
|
69
|
-
#
|
|
70
|
-
# Prose -> proposed declarations.
|
|
71
|
-
#
|
|
72
|
-
# @param prose [String] a human's plain-English sentence
|
|
73
|
-
# @param state [Hash] the interview's whole current picture, JSON-able
|
|
74
|
-
# @return [Object] the parsed JSON reply, expected to be a Hash holding a `"proposals"`
|
|
75
|
-
# array; shape validated downstream by `Ports::Agent::Answers.proposals`
|
|
76
|
-
# @raise [Ports::Agent::Unavailable] if the `claude` binary is missing, the subprocess
|
|
77
|
-
# fails, or the call times out
|
|
78
|
-
# @raise [Ports::Agent::ValidationError] if the CLI's own envelope has no `"result"`, or
|
|
79
|
-
# the result text is not valid JSON
|
|
80
29
|
def interpret(prose:, state:)
|
|
81
30
|
call(
|
|
82
31
|
system: SYSTEM_PREFIX + "Given the domain model so far and a sentence the human just said, " \
|
|
@@ -89,22 +38,6 @@ module Hecks
|
|
|
89
38
|
)
|
|
90
39
|
end
|
|
91
40
|
|
|
92
|
-
# Asks the model to judge a declared model on taste, as a raw parsed-JSON reply.
|
|
93
|
-
#
|
|
94
|
-
# What is wrong with this as a model — closed to the same kind
|
|
95
|
-
# vocabulary `Ports::Agent::CRITIQUE_KINDS` declares, spelled out
|
|
96
|
-
# here too since the system prompt is the only place the model
|
|
97
|
-
# itself ever sees that list.
|
|
98
|
-
#
|
|
99
|
-
# @param declared [Hash] the chapter as declared so far, JSON-able
|
|
100
|
-
# @param refusals [Array<Object>] JSON-able refusals the language itself already raised
|
|
101
|
-
# @param findings [Array<Object>] JSON-able mechanical findings already found
|
|
102
|
-
# @return [Object] the parsed JSON reply, expected to be a Hash holding a `"findings"`
|
|
103
|
-
# array; shape validated downstream by `Ports::Agent::Answers.findings`
|
|
104
|
-
# @raise [Ports::Agent::Unavailable] if the `claude` binary is missing, the subprocess
|
|
105
|
-
# fails, or the call times out
|
|
106
|
-
# @raise [Ports::Agent::ValidationError] if the CLI's own envelope has no `"result"`, or
|
|
107
|
-
# the result text is not valid JSON
|
|
108
41
|
def critique(declared:, refusals:, findings:)
|
|
109
42
|
kinds = Ports::Agent::CRITIQUE_KINDS.join(", ")
|
|
110
43
|
call(
|
|
@@ -117,22 +50,7 @@ module Hecks
|
|
|
117
50
|
)
|
|
118
51
|
end
|
|
119
52
|
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
# **Vocabulary help**. Named `suggest_name`, not `name` — see
|
|
123
|
-
# `Ports::Agent#suggest_name`'s own comment for why `name` is
|
|
124
|
-
# never a safe module-function name here.
|
|
125
|
-
#
|
|
126
|
-
# @param meaning [String] what the new name needs to mean
|
|
127
|
-
# @param kind [String] the kind of construct being named, such as `"event"`
|
|
128
|
-
# @param near [Array<String>] names already in use nearby, which a suggestion must not
|
|
129
|
-
# collide with
|
|
130
|
-
# @return [Object] the parsed JSON reply, expected to be a Hash holding a `"names"`
|
|
131
|
-
# array; shape validated downstream by `Ports::Agent::Answers.suggestions`
|
|
132
|
-
# @raise [Ports::Agent::Unavailable] if the `claude` binary is missing, the subprocess
|
|
133
|
-
# fails, or the call times out
|
|
134
|
-
# @raise [Ports::Agent::ValidationError] if the CLI's own envelope has no `"result"`, or
|
|
135
|
-
# the result text is not valid JSON
|
|
53
|
+
# Named `suggest_name`, not `name`: `name` is never a safe module-function name here.
|
|
136
54
|
def suggest_name(meaning:, kind:, near:)
|
|
137
55
|
call(
|
|
138
56
|
system: SYSTEM_PREFIX + "Suggest a name for a #{kind} meaning \"#{meaning}\", distinct from " \
|
|
@@ -142,18 +60,7 @@ module Hecks
|
|
|
142
60
|
)
|
|
143
61
|
end
|
|
144
62
|
|
|
145
|
-
#
|
|
146
|
-
|
|
147
|
-
# Runs `claude` as a subprocess with `payload` on stdin and returns its parsed JSON reply.
|
|
148
|
-
#
|
|
149
|
-
# @param system [String] the system-prompt text appended via `--append-system-prompt`
|
|
150
|
-
# @param payload [Hash] the JSON-able payload written to the subprocess's stdin
|
|
151
|
-
# @return [Object] the parsed JSON value nested under the CLI envelope's `"result"` key
|
|
152
|
-
# (see `unwrap`)
|
|
153
|
-
# @raise [Ports::Agent::Unavailable] if the `claude` binary is missing, the subprocess
|
|
154
|
-
# exits non-zero, or the call does not finish within `TIMEOUT_SECONDS`
|
|
155
|
-
# @raise [Ports::Agent::ValidationError] if the envelope has no `"result"`, or the
|
|
156
|
-
# result text is not valid JSON (see `unwrap`)
|
|
63
|
+
# Argv spawn, never a shell, so a `claude` shell alias cannot apply; no tools are allowed.
|
|
157
64
|
def call(system:, payload:)
|
|
158
65
|
stdout, status = Timeout.timeout(TIMEOUT_SECONDS) do
|
|
159
66
|
Open3.capture2(
|
|
@@ -171,13 +78,6 @@ module Hecks
|
|
|
171
78
|
raise Ports::Agent::Unavailable, "claude is not on PATH: #{e.message}"
|
|
172
79
|
end
|
|
173
80
|
|
|
174
|
-
# Unwraps the CLI's own JSON envelope down to the model's parsed reply.
|
|
175
|
-
#
|
|
176
|
-
# @param stdout [String] the raw stdout of the `claude` CLI invocation, the outer
|
|
177
|
-
# `{"result": "..."}` envelope
|
|
178
|
-
# @return [Object] the JSON value parsed out of the envelope's `"result"` string
|
|
179
|
-
# @raise [Ports::Agent::ValidationError] if the envelope has no `"result"` key, or
|
|
180
|
-
# `result` is not valid JSON
|
|
181
81
|
def unwrap(stdout)
|
|
182
82
|
envelope = JSON.parse(stdout)
|
|
183
83
|
result = envelope["result"]
|
|
@@ -5,7 +5,7 @@ require "uri"
|
|
|
5
5
|
require_relative "sql_query_builder"
|
|
6
6
|
require_relative "sqlite/schema_builder"
|
|
7
7
|
require_relative "sqlite/codec"
|
|
8
|
-
require_relative "sqlite"
|
|
8
|
+
require_relative "sqlite"
|
|
9
9
|
require_relative "../../ports/persistence/append_only"
|
|
10
10
|
require_relative "../../query_specification/common/null_policy"
|
|
11
11
|
require_relative "../../query_specification/common/order_by"
|
|
@@ -15,59 +15,23 @@ require_relative "../../runtime/instance"
|
|
|
15
15
|
|
|
16
16
|
module Hecks
|
|
17
17
|
module Adapters
|
|
18
|
-
# Cloudflare D1
|
|
19
|
-
#
|
|
20
|
-
# reuses Sqlite::SchemaBuilder and Sqlite::Codec unchanged (the DDL
|
|
21
|
-
# and the column encode/decode) and SqlQueryBuilder's dialect hooks
|
|
22
|
-
# are copied near-verbatim from sqlite.rb — the only real difference
|
|
23
|
-
# is the transport (D1::Connection, an HTTP call per query, vs a
|
|
24
|
-
# persistent local sqlite3 handle). See sqlite.rb's own header
|
|
25
|
-
# comment: "this file supplies only SQLite's dialect" — true here too.
|
|
18
|
+
# Cloudflare D1 (managed SQLite over REST): reuses Sqlite's schema and codec unchanged.
|
|
19
|
+
# Only the transport differs, one HTTP call per query.
|
|
26
20
|
class D1
|
|
27
|
-
#
|
|
28
|
-
# interface (execute/get_first_row/get_first_value, rows as
|
|
29
|
-
# column-name-keyed hashes) that Sqlite::SchemaBuilder, Sqlite::Codec,
|
|
30
|
-
# and this file's own methods already assume. One stateless HTTP call
|
|
31
|
-
# per execute, not a persistent connection — D1 has no connection to
|
|
32
|
-
# hold open.
|
|
21
|
+
# HTTP transport exposing the slice of `SQLite3::Database` the shared SQLite code uses.
|
|
33
22
|
class Connection
|
|
34
23
|
ENDPOINT = "https://api.cloudflare.com/client/v4".freeze
|
|
35
24
|
|
|
36
|
-
# @param account_id [String] the Cloudflare account that owns the database
|
|
37
|
-
# @param database_id [String] the D1 database's id
|
|
38
|
-
# @param api_token [String] a Cloudflare API token, sent as a bearer token on every
|
|
39
|
-
# request
|
|
40
25
|
def initialize(account_id:, database_id:, api_token:)
|
|
41
26
|
@uri = URI("#{ENDPOINT}/accounts/#{account_id}/d1/database/#{database_id}/query")
|
|
42
27
|
@api_token = api_token
|
|
43
28
|
end
|
|
44
29
|
|
|
45
|
-
# Runs one statement in its own HTTP request and returns its rows.
|
|
46
|
-
#
|
|
47
|
-
# @param sql [String] the statement, with `?` placeholders
|
|
48
|
-
# @param binds [Array<Object>] one value per placeholder, in order
|
|
49
|
-
# @return [Array<Hash{String => Object}>] the result rows keyed by column name; `[]`
|
|
50
|
-
# for a statement that returns none
|
|
51
|
-
# @raise [Runtime::WiringError] if D1 reports the query failed or answers with a body
|
|
52
|
-
# that is not JSON
|
|
53
30
|
def execute(sql, binds = [])
|
|
54
31
|
response_results({ sql: sql, params: binds }).first.fetch("results", [])
|
|
55
32
|
end
|
|
56
33
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# D1 batches are SQL transactions: statements execute in order and a
|
|
60
|
-
# failure rolls the entire sequence back. Keep the tuple-shaped local
|
|
61
|
-
# seam small so adapter code and focused fakes do not need to know the
|
|
62
|
-
# REST request envelope.
|
|
63
|
-
# https://developers.cloudflare.com/d1/worker-api/d1-database/#batch
|
|
64
|
-
#
|
|
65
|
-
# @param statements [Array<Array(String, Array)>] `[sql, binds]` pairs in execution
|
|
66
|
-
# order; nil binds mean none
|
|
67
|
-
# @return [Array<Array<Hash{String => Object}>>] each statement's result rows, in
|
|
68
|
-
# the order given
|
|
69
|
-
# @raise [Runtime::WiringError] if D1 reports the request or any one statement failed,
|
|
70
|
-
# or answers with a body that is not JSON
|
|
34
|
+
# D1 batches are transactions: statements run in order and a failure rolls all back.
|
|
71
35
|
def batch(statements)
|
|
72
36
|
payload = {
|
|
73
37
|
batch: statements.map do |sql, binds|
|
|
@@ -104,11 +68,7 @@ module Hecks
|
|
|
104
68
|
raise Runtime::WiringError, "D1 query failed: non-JSON response (HTTP #{response.code}): #{response.body}"
|
|
105
69
|
end
|
|
106
70
|
|
|
107
|
-
#
|
|
108
|
-
# a per-statement `failed["error"]`/`failed["message"]` (checked
|
|
109
|
-
# by key presence, not truthiness, so an explicit `nil` still
|
|
110
|
-
# counts as "the key was there") is always more specific to which
|
|
111
|
-
# statement failed, when either is present.
|
|
71
|
+
# Prefers the statement's own error/message (by key presence) over the whole-response one.
|
|
112
72
|
def failed_statement_detail(failed, messages)
|
|
113
73
|
detail =
|
|
114
74
|
if failed.key?("error")
|
|
@@ -123,26 +83,10 @@ module Hecks
|
|
|
123
83
|
|
|
124
84
|
public
|
|
125
85
|
|
|
126
|
-
# Runs one statement and keeps only its first row, as `SQLite3::Database` does.
|
|
127
|
-
#
|
|
128
|
-
# @param sql [String] the statement, with `?` placeholders
|
|
129
|
-
# @param binds [Array<Object>] one value per placeholder, in order
|
|
130
|
-
# @return [Hash{String => Object}, nil] the first row keyed by column name, or nil when
|
|
131
|
-
# the statement returned none
|
|
132
|
-
# @raise [Runtime::WiringError] if D1 reports the query failed or answers with a body
|
|
133
|
-
# that is not JSON
|
|
134
86
|
def get_first_row(sql, binds = [])
|
|
135
87
|
execute(sql, binds).first
|
|
136
88
|
end
|
|
137
89
|
|
|
138
|
-
# Runs one statement and keeps only the first column of its first row.
|
|
139
|
-
#
|
|
140
|
-
# @param sql [String] the statement, with `?` placeholders
|
|
141
|
-
# @param binds [Array<Object>] one value per placeholder, in order
|
|
142
|
-
# @return [Object, nil] the value as D1's JSON carries it, or nil when the statement
|
|
143
|
-
# returned no row
|
|
144
|
-
# @raise [Runtime::WiringError] if D1 reports the query failed or answers with a body
|
|
145
|
-
# that is not JSON
|
|
146
90
|
def get_first_value(sql, binds = [])
|
|
147
91
|
get_first_row(sql, binds)&.values&.first
|
|
148
92
|
end
|
|
@@ -156,22 +100,11 @@ module Hecks
|
|
|
156
100
|
|
|
157
101
|
attr_reader :aggregate
|
|
158
102
|
|
|
159
|
-
# Names the optional persistence capabilities `Ports::Persistence::AppendOnly` may rely on.
|
|
160
|
-
#
|
|
161
|
-
# @return [Array<Symbol>] `[:atomic_put]`
|
|
162
103
|
def persistence_capabilities = [:atomic_put]
|
|
163
104
|
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
# @param aggregate [Bluebook::Aggregate] the aggregate whose table this adapter owns
|
|
168
|
-
# @param settings [Hash{Symbol, String => Object}] world settings for the binding:
|
|
169
|
-
# `account_id`, `database_id` and `api_token` (all required) and `domain` (scopes saga
|
|
170
|
-
# rows, default the aggregate's name), each read under a Symbol or a String key
|
|
171
|
-
# @param root [String, nil] project root directory; accepted for the shared adapter
|
|
172
|
-
# constructor shape and ignored
|
|
173
|
-
# @raise [Runtime::WiringError] if a required setting is missing or empty, or D1 rejects
|
|
174
|
-
# a table-creation statement
|
|
105
|
+
# Creates the aggregate, journal, event and saga tables if absent.
|
|
106
|
+
# Settings: `account_id`, `database_id`, `api_token` (required) and `domain` (saga scope).
|
|
107
|
+
# @raise [Runtime::WiringError] if a required setting is missing or empty
|
|
175
108
|
def initialize(aggregate:, settings: {}, root: nil)
|
|
176
109
|
@aggregate = aggregate
|
|
177
110
|
|
|
@@ -183,12 +116,7 @@ module Hecks
|
|
|
183
116
|
end
|
|
184
117
|
|
|
185
118
|
@db = Connection.new(account_id: account_id, database_id: database_id, api_token: api_token)
|
|
186
|
-
#
|
|
187
|
-
# (§2/§4) — D1's domain isolation is by whole-database identity
|
|
188
|
-
# (one D1 database per domain in practice), so unlike Sqlite's
|
|
189
|
-
# own per-aggregate-file default, there's no "which file does
|
|
190
|
-
# this end up in" ambiguity here: every aggregate on D1 within a
|
|
191
|
-
# domain already shares one database.
|
|
119
|
+
# Saga rows are scoped by domain; one D1 database per domain, so aggregates share it.
|
|
192
120
|
@domain = (
|
|
193
121
|
if settings.key?(:domain)
|
|
194
122
|
settings[:domain]
|
|
@@ -199,9 +127,7 @@ module Hecks
|
|
|
199
127
|
end
|
|
200
128
|
).to_s
|
|
201
129
|
|
|
202
|
-
# No PRAGMA synchronous
|
|
203
|
-
# durable service; there is no local fsync policy for a caller to
|
|
204
|
-
# tune, and the query endpoint has no PRAGMA write-access to it.
|
|
130
|
+
# No PRAGMA synchronous: D1 is managed and its query endpoint disallows PRAGMA writes.
|
|
205
131
|
create_aggregate_table!
|
|
206
132
|
create_entry_table!
|
|
207
133
|
ensure_entry_operation_column!
|
|
@@ -210,16 +136,8 @@ module Hecks
|
|
|
210
136
|
create_saga_table!
|
|
211
137
|
end
|
|
212
138
|
|
|
213
|
-
# Names the aggregate's table; the journal table is keyed off it.
|
|
214
|
-
#
|
|
215
|
-
# @return [String] the aggregate's snake_case storage name, unquoted
|
|
216
139
|
def table = @aggregate.storage_name
|
|
217
140
|
|
|
218
|
-
# Reads the current row for one aggregate identity.
|
|
219
|
-
#
|
|
220
|
-
# @param id [String, Object] the aggregate identity, bound as `id.to_s`
|
|
221
|
-
# @return [Runtime::Instance, nil] the decoded record, or nil when no row has that id
|
|
222
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
223
141
|
def find(id)
|
|
224
142
|
row = @db.get_first_row("SELECT * FROM #{quoted_table} WHERE id = ?", [id.to_s])
|
|
225
143
|
return nil unless row
|
|
@@ -227,19 +145,8 @@ module Hecks
|
|
|
227
145
|
Runtime::Instance.new(aggregate: @aggregate, id: row["id"], state: decode(row))
|
|
228
146
|
end
|
|
229
147
|
|
|
230
|
-
# Lists every
|
|
231
|
-
#
|
|
232
|
-
# order_by is a runtime value — see postgres.rb's own all for the
|
|
233
|
-
# full reasoning; whitelisted the identical way, same order_clause
|
|
234
|
-
# Sqlite's own all reuses (D1 speaks the identical dialect).
|
|
235
|
-
#
|
|
236
|
-
# @param order_by [String, Symbol, nil] attribute (or dotted value-object path) to sort
|
|
237
|
-
# by, with id as the tie-break; nil orders by id alone
|
|
238
|
-
# @param direction [Symbol, String] `:asc` or `:desc`, case-insensitive; anything else
|
|
239
|
-
# sorts ascending
|
|
240
|
-
# @return [Array<Runtime::Instance>] the decoded records, `[]` when the table is empty
|
|
241
|
-
# @raise [Runtime::WiringError] if `order_by` names no attribute of the aggregate, or D1
|
|
242
|
-
# rejects the statement
|
|
148
|
+
# Lists every record, ordered by id or by `order_by` when it names an attribute.
|
|
149
|
+
# @raise [Runtime::WiringError] if `order_by` names no attribute of the aggregate
|
|
243
150
|
def all(order_by: nil, direction: :asc)
|
|
244
151
|
order_sql = "ORDER BY id"
|
|
245
152
|
if order_by
|
|
@@ -258,37 +165,17 @@ module Hecks
|
|
|
258
165
|
end
|
|
259
166
|
end
|
|
260
167
|
|
|
261
|
-
# Counts the rows in the aggregate's table, deleted records excluded.
|
|
262
|
-
#
|
|
263
|
-
# @return [Integer] number of current records
|
|
264
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
265
168
|
def count = @db.get_first_value("SELECT COUNT(*) FROM #{quoted_table}").to_i
|
|
266
169
|
|
|
267
|
-
# Inserts one journal row in its own HTTP request.
|
|
268
|
-
#
|
|
269
|
-
# @param entry [Ports::Persistence::Entry] the save or delete to journal; `state` is
|
|
270
|
-
# encoded through the state codec and `mirrors` stored as JSON, or NULL when nil
|
|
271
|
-
# @return [Ports::Persistence::Entry] the same `entry`
|
|
272
|
-
# @raise [Runtime::WiringError] if D1 rejects the insert
|
|
273
170
|
def append(entry)
|
|
274
171
|
@db.execute(
|
|
275
172
|
"INSERT INTO #{quoted_entry_table} (aggregate_id, operation, state, mirrors) VALUES (?, ?, ?, ?)",
|
|
276
|
-
#
|
|
277
|
-
# mirrors hash must bind a real SQL NULL, not the four-character
|
|
278
|
-
# JSON text `"null"` (`JSON.generate(nil)`), or a future `IS NULL`
|
|
279
|
-
# check against it would never match. Same guard `postgres_era.rb`
|
|
280
|
-
# already uses for its own journal's `mirrors` column.
|
|
173
|
+
# Absent mirrors bind SQL NULL, not the JSON text "null" (the column is nullable).
|
|
281
174
|
[entry.id, entry.operation, state_json(entry.state), entry.mirrors && JSON.generate(entry.mirrors)]
|
|
282
175
|
)
|
|
283
176
|
entry
|
|
284
177
|
end
|
|
285
178
|
|
|
286
|
-
# Replaces or deletes the aggregate's row for one journal entry.
|
|
287
|
-
#
|
|
288
|
-
# @param entry [Ports::Persistence::Entry] the save or delete to materialize
|
|
289
|
-
# @return [Runtime::Instance, Array] for a save, a new instance over the entry's state;
|
|
290
|
-
# for a delete, the `DELETE` statement's empty result rows
|
|
291
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
292
179
|
def project(entry)
|
|
293
180
|
return @db.execute("DELETE FROM #{quoted_table} WHERE id = ?", [entry.id]) if entry.delete?
|
|
294
181
|
|
|
@@ -304,13 +191,7 @@ module Hecks
|
|
|
304
191
|
instance
|
|
305
192
|
end
|
|
306
193
|
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
# @return [Array<Ports::Persistence::Entry>] every journalled entry, state decoded
|
|
310
|
-
# through the state codec and `mirrors` parsed with String keys (nil when none were
|
|
311
|
-
# stored); a NULL `operation` reads as `"save"`; `[]` when nothing has been appended
|
|
312
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
313
|
-
# @raise [JSON::ParserError] if a stored `state` or `mirrors` value is not valid JSON
|
|
194
|
+
# The whole journal in append order, for `AppendOnly#recover!` to replay.
|
|
314
195
|
def entries
|
|
315
196
|
@db.execute("SELECT aggregate_id, operation, state, mirrors FROM #{quoted_entry_table} ORDER BY sequence").map do |row|
|
|
316
197
|
state = JSON.parse(row["state"])
|
|
@@ -323,70 +204,25 @@ module Hecks
|
|
|
323
204
|
end
|
|
324
205
|
end
|
|
325
206
|
|
|
326
|
-
#
|
|
327
|
-
# are left in place.
|
|
328
|
-
#
|
|
329
|
-
# @return [Adapters::D1] self
|
|
330
|
-
# @raise [Runtime::WiringError] if D1 rejects a statement
|
|
207
|
+
# Clears the aggregate table and its journal; events and saga rows stay.
|
|
331
208
|
def reset!
|
|
332
209
|
@db.execute("DELETE FROM #{quoted_table}")
|
|
333
210
|
@db.execute("DELETE FROM #{quoted_entry_table}")
|
|
334
211
|
self
|
|
335
212
|
end
|
|
336
213
|
|
|
337
|
-
# Journals
|
|
338
|
-
# requests rather than one batch.
|
|
339
|
-
#
|
|
340
|
-
# @param instance [Runtime::Instance] the instance to store
|
|
341
|
-
# @return [Runtime::Instance] a new instance over a shallow copy of the saved state
|
|
342
|
-
# @raise [Runtime::WiringError] if D1 rejects either statement; a journal row already
|
|
343
|
-
# inserted stays
|
|
214
|
+
# Journals then projects as two HTTP requests; the journal row stays if projecting fails.
|
|
344
215
|
def save(instance)
|
|
345
216
|
entry = Ports::Persistence::Entry.new(operation: "save", id: instance.id.to_s, state: instance.state.dup)
|
|
346
217
|
append(entry)
|
|
347
218
|
project(entry)
|
|
348
219
|
end
|
|
349
220
|
|
|
350
|
-
# Stores an entry
|
|
351
|
-
# batched HTTP request.
|
|
352
|
-
#
|
|
353
|
-
# Classification, durable journal append and current-state projection
|
|
354
|
-
# are one D1 batch transaction and therefore one HTTP request. The first
|
|
355
|
-
# statement supplies the outcome from database state inside that same
|
|
356
|
-
# transaction; the runtime performs no preliminary find.
|
|
221
|
+
# Stores an entry in one D1 batch; returns `:inserted`, `:replaced` or `:conflicted`.
|
|
357
222
|
#
|
|
358
|
-
#
|
|
359
|
-
#
|
|
360
|
-
#
|
|
361
|
-
# pass that check before either wrote). D1's batch has no conditional
|
|
362
|
-
# branch of its own, true, but it does not need one: a batch's own
|
|
363
|
-
# statements already execute in order, atomically, as one transaction
|
|
364
|
-
# (Connection#batch's own comment) — the exact guarantee the single-
|
|
365
|
-
# connection adapters' own `@db.transaction do ... end` gets locally.
|
|
366
|
-
# So the existence check sits inside the batch as its own first
|
|
367
|
-
# statement, and the two writes are individually gated with `WHERE NOT
|
|
368
|
-
# EXISTS (...)` against that same table, evaluated in the same
|
|
369
|
-
# transaction — a row that already existed makes both writes into
|
|
370
|
-
# real, zero-row no-ops rather than skipping them from the Ruby side,
|
|
371
|
-
# matching Sqlite#atomic_put's `next` (skip append and project both,
|
|
372
|
-
# together) with no second HTTP call and no gap for another writer to
|
|
373
|
-
# land in between the check and the write.
|
|
374
|
-
#
|
|
375
|
-
# Three SQL statements, built here and batched together as one
|
|
376
|
-
# transaction below — see the paragraph above on the real TOCTOU gap
|
|
377
|
-
# this exact shape closes (the existence check inside the
|
|
378
|
-
# batch, not run as a separate earlier round trip). Splitting the
|
|
379
|
-
# per-statement builders out would still need columns/values/slots/
|
|
380
|
-
# not_exists/quoted_table threaded into each, and would separate
|
|
381
|
-
# three pieces of one atomic batch across methods with no single
|
|
382
|
-
# place left to see that they are, together, the fix.
|
|
383
|
-
#
|
|
384
|
-
# @param entry [Ports::Persistence::Entry] the save to store
|
|
385
|
-
# @param insert_only [Boolean] when true, an existing row turns both writes into
|
|
386
|
-
# zero-row no-ops
|
|
387
|
-
# @return [Symbol] `:inserted`, `:replaced`, or `:conflicted` when `insert_only` met an
|
|
388
|
-
# existing row and nothing was written
|
|
389
|
-
# @raise [Runtime::WiringError] if D1 rejects the batch; the whole batch is rolled back
|
|
223
|
+
# The existence check is the batch's first statement and both writes are gated by
|
|
224
|
+
# WHERE NOT EXISTS, so `insert_only` has no gap between check and write.
|
|
225
|
+
# @return [Symbol] `:conflicted` only when `insert_only` met an existing row
|
|
390
226
|
# rubocop:disable-next Metrics/AbcSize
|
|
391
227
|
# rubocop:disable-next Metrics/MethodLength
|
|
392
228
|
def atomic_put(entry, insert_only: false)
|
|
@@ -405,7 +241,7 @@ module Hecks
|
|
|
405
241
|
"THEN 'replaced' ELSE 'inserted' END AS status"
|
|
406
242
|
end
|
|
407
243
|
|
|
408
|
-
# `mirrors` is nullable
|
|
244
|
+
# `mirrors` is nullable; see `append`.
|
|
409
245
|
encoded_mirrors = entry.mirrors && JSON.generate(entry.mirrors)
|
|
410
246
|
|
|
411
247
|
entry_sql, entry_binds =
|
|
@@ -444,12 +280,6 @@ module Hecks
|
|
|
444
280
|
results.fetch(0).fetch(0).fetch("status").to_sym
|
|
445
281
|
end
|
|
446
282
|
|
|
447
|
-
# Journals a delete and then removes the row, whether or not a row exists, as two
|
|
448
|
-
# separate HTTP requests.
|
|
449
|
-
#
|
|
450
|
-
# @param id [String, Object] the aggregate identity, journalled as `id.to_s`
|
|
451
|
-
# @return [Boolean] always true
|
|
452
|
-
# @raise [Runtime::WiringError] if D1 rejects either statement
|
|
453
283
|
def delete(id)
|
|
454
284
|
entry = Ports::Persistence::Entry.new(operation: "delete", id: id.to_s, state: nil)
|
|
455
285
|
append(entry)
|
|
@@ -457,11 +287,6 @@ module Hecks
|
|
|
457
287
|
true
|
|
458
288
|
end
|
|
459
289
|
|
|
460
|
-
# Inserts an emitted event into the database's shared `events` table.
|
|
461
|
-
#
|
|
462
|
-
# @param event [Runtime::Event] the emitted event; `payload` is stored as JSON
|
|
463
|
-
# @return [Array] the insert's empty result rows; callers ignore it
|
|
464
|
-
# @raise [Runtime::WiringError] if D1 rejects the insert
|
|
465
290
|
def record_event(event)
|
|
466
291
|
@db.execute(
|
|
467
292
|
"INSERT INTO events (name, aggregate, aggregate_id, payload, occurred_at) VALUES (?, ?, ?, ?, ?)",
|
|
@@ -469,12 +294,7 @@ module Hecks
|
|
|
469
294
|
)
|
|
470
295
|
end
|
|
471
296
|
|
|
472
|
-
#
|
|
473
|
-
# table, not only this aggregate's rows.
|
|
474
|
-
#
|
|
475
|
-
# @return [Array<Runtime::Event>] the stored events, `payload` parsed with Symbol keys
|
|
476
|
-
# and `occurred_at` as stored; `[]` when none are recorded
|
|
477
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
297
|
+
# Every recorded event in the shared `events` table, not only this aggregate's.
|
|
478
298
|
def events
|
|
479
299
|
@db.execute("SELECT * FROM events ORDER BY id").map do |row|
|
|
480
300
|
Runtime::Event.new(
|
|
@@ -487,13 +307,24 @@ module Hecks
|
|
|
487
307
|
end
|
|
488
308
|
end
|
|
489
309
|
|
|
490
|
-
#
|
|
491
|
-
#
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
310
|
+
# One record's recorded events, oldest first — pushed down as a `WHERE` clause
|
|
311
|
+
# (`hecks_events_aggregate_id_idx`) instead of filtering `#events`'s whole-table read.
|
|
312
|
+
def events_for(aggregate:, id:)
|
|
313
|
+
@db.execute(
|
|
314
|
+
"SELECT * FROM events WHERE aggregate = ? AND aggregate_id = ? ORDER BY id",
|
|
315
|
+
[aggregate, id.to_s]
|
|
316
|
+
).map do |row|
|
|
317
|
+
Runtime::Event.new(
|
|
318
|
+
name: row["name"],
|
|
319
|
+
aggregate: row["aggregate"],
|
|
320
|
+
id: row["aggregate_id"],
|
|
321
|
+
payload: JSON.parse(row["payload"], symbolize_names: true),
|
|
322
|
+
occurred_at: row["occurred_at"]
|
|
323
|
+
)
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Persists a saga's checkpointed state as one D1 statement.
|
|
497
328
|
#
|
|
498
329
|
# @param process_manager [String, Symbol] the process manager's name
|
|
499
330
|
# @param correlation [String, Object] the instance's correlation value, stored as
|
|
@@ -513,13 +344,6 @@ module Hecks
|
|
|
513
344
|
)
|
|
514
345
|
end
|
|
515
346
|
|
|
516
|
-
# Removes a finished saga instance's checkpoint; a missing row is not an error.
|
|
517
|
-
#
|
|
518
|
-
# @param process_manager [String, Symbol] the process manager's name
|
|
519
|
-
# @param correlation [String, Object] the instance's correlation value, matched as
|
|
520
|
-
# `correlation.to_s`
|
|
521
|
-
# @return [Array] the statement's empty result rows; callers ignore it
|
|
522
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
523
347
|
def delete_saga(process_manager:, correlation:)
|
|
524
348
|
@db.execute(
|
|
525
349
|
"DELETE FROM hecks_saga_instances WHERE domain = ? AND process_manager = ? AND correlation = ?",
|
|
@@ -527,18 +351,7 @@ module Hecks
|
|
|
527
351
|
)
|
|
528
352
|
end
|
|
529
353
|
|
|
530
|
-
# Yields
|
|
531
|
-
# `Registry#rehydrate_sagas!` to restore at boot.
|
|
532
|
-
#
|
|
533
|
-
# @yieldparam process_manager [String] the process manager's name
|
|
534
|
-
# @yieldparam correlation [String] the instance's correlation value
|
|
535
|
-
# @yieldparam state [String] the saga's state name
|
|
536
|
-
# @yieldparam memory [Hash{Symbol => Object}] the saga's memory, Symbol keys at every depth
|
|
537
|
-
# @yieldparam completed_compensations [Array] the completed-compensation ledger, `[]`
|
|
538
|
-
# when the column is NULL
|
|
539
|
-
# @return [Enumerator, Array<Hash>] an enumerator over the same five values when no
|
|
540
|
-
# block is given; otherwise the raw result rows
|
|
541
|
-
# @raise [Runtime::WiringError] if D1 rejects the statement
|
|
354
|
+
# Yields each checkpointed saga of this domain, for `Registry#rehydrate_sagas!`.
|
|
542
355
|
def each_saga
|
|
543
356
|
return enum_for(:each_saga) unless block_given?
|
|
544
357
|
|
|
@@ -555,10 +368,7 @@ module Hecks
|
|
|
555
368
|
|
|
556
369
|
private
|
|
557
370
|
|
|
558
|
-
#
|
|
559
|
-
# D1 speaks the same dialect (copied, not shared by module include,
|
|
560
|
-
# because Sqlite's own copies are private instance methods on a
|
|
561
|
-
# different class — see the file header) ───────────────────────
|
|
371
|
+
# SqlQueryBuilder dialect hooks, copied from Sqlite's private methods.
|
|
562
372
|
def select_list = "*"
|
|
563
373
|
def from_relation = quoted_table
|
|
564
374
|
def dialect_name = "D1"
|
|
@@ -585,8 +395,7 @@ module Hecks
|
|
|
585
395
|
"json_extract(#{quote_ident(name)}, '#{json_path}')"
|
|
586
396
|
end
|
|
587
397
|
|
|
588
|
-
# SQLite
|
|
589
|
-
# its own documented unbounded spelling, exactly for this case.
|
|
398
|
+
# SQLite has no bare OFFSET; LIMIT -1 is its unbounded spelling.
|
|
590
399
|
def unbounded_limit = " LIMIT -1"
|
|
591
400
|
|
|
592
401
|
def order_clause(order_by, policy)
|
|
@@ -597,8 +406,6 @@ module Hecks
|
|
|
597
406
|
@db.execute(sql, binds).map { |row| Runtime::Instance.new(aggregate: @aggregate, id: row["id"], state: decode(row)) }
|
|
598
407
|
end
|
|
599
408
|
|
|
600
|
-
# ── the rest of the dialect ─────────────────────────────────────
|
|
601
|
-
|
|
602
409
|
def quote_ident(name)
|
|
603
410
|
%("#{name.to_s.gsub('"', '""')}")
|
|
604
411
|
end
|