hecks 3.10.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +158 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +70 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/newcomer.rb +50 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/read_models.rb +1 -1
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +4 -15
- data/lib/hecks/bluebook/assembly/scalars.rb +39 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +24 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction.rb +6 -3
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +2 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +34 -4
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +33 -7
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +53 -18
- data/lib/hecks/hecks/hecks.world +13 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.behaviors +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.behaviors +19 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +63 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projection_files/expression_tables.rb +7 -2
- data/lib/hecks/projections/deploy/box/compose.rb +6 -4
- data/lib/hecks/projections/deploy/box/edge.rb +62 -0
- data/lib/hecks/projections/deploy/box/logging.rb +99 -0
- data/lib/hecks/projections/deploy/box/roll_scripts.rb +16 -8
- data/lib/hecks/projections/deploy/box/settings/cdn.rb +40 -0
- data/lib/hecks/projections/deploy/box/settings/logs.rb +40 -0
- data/lib/hecks/projections/deploy/box/settings/patterns.rb +11 -0
- data/lib/hecks/projections/deploy/box/settings/types.rb +16 -1
- data/lib/hecks/projections/deploy/box/settings.rb +6 -2
- data/lib/hecks/projections/deploy/box/stack_yaml.rb +5 -2
- data/lib/hecks/projections/deploy/box/templates/Makefile.tmpl +16 -0
- data/lib/hecks/projections/deploy/box/templates/box.yaml.tmpl +8 -4
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +32 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/box/templates/edge.mk.tmpl +17 -0
- data/lib/hecks/projections/deploy/box/templates/edge.yaml.tmpl +65 -0
- data/lib/hecks/projections/deploy/box/templates/log-group.yaml.tmpl +12 -0
- data/lib/hecks/projections/deploy/box/templates/log-policy.yaml.tmpl +7 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +5 -4
- data/lib/hecks/projections/deploy/box/templates/render-compose.sh.tmpl +5 -4
- data/lib/hecks/projections/deploy/box.rb +15 -3
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +26 -6
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +14 -1
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +1 -1
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +96 -19
- data/lib/hecks/projections/site/cms_editor/setting.rb +52 -9
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +18 -1
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +251 -57
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +32 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +78 -24
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +6 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +19 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +31 -8
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +131 -2
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +8 -5
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +229 -34
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +40 -81
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +29 -12
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +53 -17
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +178 -88
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +2 -1
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +49 -23
- data/lib/hecks/projections/site/role_probe.rb +2 -0
- data/lib/hecks/projections/site/site_root/workflow.rb +5 -0
- data/lib/hecks/projector/cli_audience.rb +22 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/cli_projector/usage.rb +15 -4
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/host.rb +11 -4
- data/lib/hecks/rust_build/prebuilt_binary.rb +101 -0
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +1 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/ci_gate_decision.rb +35 -0
- data/lib/hecks/tools/ci_gates.rb +20 -1
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/files.rb +8 -0
- data/lib/hecks/tools/lanes/prebuilt_workflow.rb +81 -0
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/lanes.rb +1 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/release_only.rb +111 -0
- data/lib/hecks/tools/site_routes/editor_files.rb +12 -5
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +19 -5
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/9ee54e2f0d4d6d3fe046827779b221e02de665fba911a048ae5dd8b8fbfb7d90.marshal +0 -0
- data/prebuilt/d74a00e996222c1d8b676d5959092effc46b4dc59e0555f3bf9554ac58e3094a.marshal +0 -0
- data/prebuilt/verdicts-df19f35868d3284a70f55e42dcef87ef4e65e6a678e5135f72e1f6cff4c1357b.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +6 -0
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +2 -0
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +1 -0
- data/rust/codegen/src/mutations.rs +128 -3
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/extension.rs +13 -1
- data/rust/host/src/lib.rs +1 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/platform_signin/attack_tests.rs +1100 -0
- data/rust/host/src/platform_signin/config.rs +512 -0
- data/rust/host/src/platform_signin/cookie.rs +237 -0
- data/rust/host/src/platform_signin/extension.rs +190 -0
- data/rust/host/src/platform_signin/fetcher.rs +322 -0
- data/rust/host/src/platform_signin/handler.rs +251 -0
- data/rust/host/src/platform_signin/handler_tests.rs +554 -0
- data/rust/host/src/platform_signin/jti.rs +196 -0
- data/rust/host/src/platform_signin/jwks.rs +542 -0
- data/rust/host/src/platform_signin/jws.rs +249 -0
- data/rust/host/src/platform_signin/known_answer_tests.rs +237 -0
- data/rust/host/src/platform_signin/mapping_tests.rs +254 -0
- data/rust/host/src/platform_signin/mod.rs +75 -0
- data/rust/host/src/platform_signin/mode_tests.rs +382 -0
- data/rust/host/src/platform_signin/pages.rs +204 -0
- data/rust/host/src/platform_signin/query.rs +233 -0
- data/rust/host/src/platform_signin/session.rs +151 -0
- data/rust/host/src/platform_signin/settings.rs +385 -0
- data/rust/host/src/platform_signin/strict_json.rs +351 -0
- data/rust/host/src/platform_signin/test_support.rs +235 -0
- data/rust/host/src/platform_signin/ttl_tests.rs +94 -0
- data/rust/host/src/platform_signin/verify.rs +604 -0
- data/rust/host/src/platform_signin/wiring_support.rs +319 -0
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +53 -9
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +34 -17
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +66 -3
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +178 -38
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_widget.js.tmpl +0 -282
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/media_picker.js.tmpl +0 -109
- data/prebuilt/c28d6d9db14eea3e17161e9d9c262efee30b256cf97693a7cfd552bce24b2f7f.marshal +0 -0
- data/prebuilt/cb375cba61ca0dbe8ddb9739c306510fe05e5f4b7a9b6955caff8224c63ea0e0.marshal +0 -0
- data/prebuilt/verdicts-d6d584ad43b5f3cebf88aafbbdbf024c38574acf18e06adffbc555f7786b1c2f.json +0 -1
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
# 40 the box stack has no instance
|
|
9
9
|
# 41 the roll did not succeed on the box
|
|
10
10
|
# 42 the roll succeeded but the box is not healthy after it
|
|
11
|
+
# 43 no restore anchor: the database's backups cannot be read, or are kept fewer days than
|
|
12
|
+
# MIN_BACKUP_RETENTION_DAYS (default 3)
|
|
13
|
+
@@LOG_EXIT_CODE@@
|
|
14
|
+
#
|
|
15
|
+
# Before anything changes, the roll names the point the database can be restored to: the instance, the
|
|
16
|
+
# moment this roll starts, and how long its continuous backups are kept. A roll that mints a new era is
|
|
17
|
+
# a one-way door, so this is the rollback anchor, and it costs nothing (a point-in-time restore uses the
|
|
18
|
+
# backups the instance already keeps, where a manual snapshot would be stored until someone deleted it).
|
|
19
|
+
# SKIP_RESTORE_ANCHOR=1 skips it.
|
|
11
20
|
#
|
|
12
21
|
# Before the roll replaces a container, the box saves that container's log to
|
|
13
22
|
# /var/log/hecks-captures/<container>-<UTC timestamp>.log and keeps the newest 14 per container
|
|
@@ -15,6 +24,7 @@
|
|
|
15
24
|
# cannot run, the roll goes on. SKIP_LOG_CAPTURE=1 skips it; LOG_CAPTURE_SERVICES="a b" limits it to
|
|
16
25
|
# those services (deploy-service.sh sets the one it rolls).
|
|
17
26
|
#
|
|
27
|
+
@@LOG_NOTE@@
|
|
18
28
|
@@USAGE@@
|
|
19
29
|
set -euo pipefail
|
|
20
30
|
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
@@ -29,6 +39,28 @@ DB_HOST=$(out "$RDS_STACK" DbEndpoint)
|
|
|
29
39
|
@@DB_SECRET_LINE@@
|
|
30
40
|
[ -n "$BOX" ] && [ "$BOX" != None ] || { echo "no instance in stack $BOX_STACK" >&2; exit 40; }
|
|
31
41
|
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
|
|
42
|
+
@@LOG_GROUP_CHECK@@
|
|
43
|
+
|
|
44
|
+
if [ "${SKIP_RESTORE_ANCHOR:-}" = 1 ]; then
|
|
45
|
+
echo "==> skipping the restore anchor (SKIP_RESTORE_ANCHOR=1)"
|
|
46
|
+
else
|
|
47
|
+
DB_ID=${DB_HOST%%.*}
|
|
48
|
+
BACKUPS=$(aws rds describe-db-instances --db-instance-identifier "$DB_ID" \
|
|
49
|
+
--query 'DBInstances[0].[BackupRetentionPeriod,LatestRestorableTime]' --output text) || BACKUPS=
|
|
50
|
+
read -r KEPT_DAYS RESTORABLE <<< "$BACKUPS"
|
|
51
|
+
case "${KEPT_DAYS:-}" in ''|*[!0-9]*)
|
|
52
|
+
echo "==> no restore anchor: could not read the backups of $DB_ID (SKIP_RESTORE_ANCHOR=1 rolls without one)" >&2
|
|
53
|
+
exit 43 ;;
|
|
54
|
+
esac
|
|
55
|
+
if [ "$KEPT_DAYS" -lt "${MIN_BACKUP_RETENTION_DAYS:-3}" ]; then
|
|
56
|
+
echo "==> no restore anchor: $DB_ID keeps backups $KEPT_DAYS day(s), fewer than ${MIN_BACKUP_RETENTION_DAYS:-3}" >&2
|
|
57
|
+
exit 43
|
|
58
|
+
fi
|
|
59
|
+
ANCHOR_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
60
|
+
echo "==> restore anchor: $DB_ID restorable to $ANCHOR_AT (latest $RESTORABLE, backups kept $KEPT_DAYS days)"
|
|
61
|
+
echo " to return: aws rds restore-db-instance-to-point-in-time --source-db-instance-identifier $DB_ID \\"
|
|
62
|
+
echo " --target-db-instance-identifier $DB_ID-restore --restore-time $ANCHOR_AT"
|
|
63
|
+
fi
|
|
32
64
|
|
|
33
65
|
WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT
|
|
34
66
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
# 32 the box's Compose file is unreadable 33 the box stack has no instance
|
|
25
25
|
# 34 the stack has no parameter for the container 35 the stack update failed or did not settle
|
|
26
26
|
# 36 a parameter other than the container's changed 37 the task definition lacks the pushed image
|
|
27
|
-
# 38 the task definition has no such container 40-
|
|
27
|
+
# 38 the task definition has no such container 40-43 deploy-box.sh's own
|
|
28
28
|
#
|
|
29
29
|
# Environment:
|
|
30
30
|
# LOCAL_IMAGE local image to push, default <ecr repository>:latest
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# GENERATED by hecks deploy project -- re-run it to refresh this file rather than hand-editing.
|
|
2
|
+
# Included by the Makefile beside it; the aliases come from the domain's deployed_to("AwsBox")
|
|
3
|
+
# block (cdn).
|
|
4
|
+
|
|
5
|
+
EDGE_STACK = @@EDGE_STACK@@
|
|
6
|
+
|
|
7
|
+
.PHONY: edge
|
|
8
|
+
|
|
9
|
+
# Creates or updates the CloudFront edge stack in front of the box (run `make stacks` first: the
|
|
10
|
+
# box's AppOriginDomain output is the distribution's origin). The certificate is never created
|
|
11
|
+
# here: issue it in ACM in us-east-1, validate its DNS records, then pass its ARN.
|
|
12
|
+
# make edge CERTIFICATE_ARN=arn:aws:acm:us-east-1:<account>:certificate/<id>
|
|
13
|
+
edge:
|
|
14
|
+
@test -n "$(CERTIFICATE_ARN)" || { echo "usage: make edge CERTIFICATE_ARN=arn:aws:acm:us-east-1:..." >&2; exit 1; }
|
|
15
|
+
aws cloudformation deploy --region @@REGION@@ --template-file edge.yaml --stack-name $(EDGE_STACK) \
|
|
16
|
+
--parameter-overrides CertificateArn=$(CERTIFICATE_ARN) \
|
|
17
|
+
AppOriginDomain=$$(aws cloudformation describe-stacks --region @@REGION@@ --stack-name $(BOX_STACK) --query "Stacks[0].Outputs[?OutputKey=='AppOriginDomain'].OutputValue" --output text)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
AWSTemplateFormatVersion: "2010-09-09"
|
|
2
|
+
Description: >
|
|
3
|
+
The CloudFront edge of @@STACK@@: one distribution in front of the app box. Its only origin is the
|
|
4
|
+
box's public DNS name over plain http on port 80, and every request to it carries the origin secret
|
|
5
|
+
header the box's proxy checks. Nothing is cached: the origin decides what a response means.
|
|
6
|
+
|
|
7
|
+
Parameters:
|
|
8
|
+
AppOriginDomain:
|
|
9
|
+
Type: String
|
|
10
|
+
AllowedPattern: ".+"
|
|
11
|
+
ConstraintDescription: the box stack's AppOriginDomain output
|
|
12
|
+
Description: DNS name of the app box, the distribution's only origin.
|
|
13
|
+
CertificateArn:
|
|
14
|
+
Type: String
|
|
15
|
+
AllowedPattern: "arn:aws:acm:us-east-1:.+"
|
|
16
|
+
ConstraintDescription: an ACM certificate in us-east-1 that covers every alias
|
|
17
|
+
Description: >
|
|
18
|
+
An issued ACM certificate in us-east-1 that covers every name in DomainNames. It is never created
|
|
19
|
+
here: DNS validation is a step an operator performs.
|
|
20
|
+
DomainNames:
|
|
21
|
+
Type: CommaDelimitedList
|
|
22
|
+
Default: "@@ALIASES@@"
|
|
23
|
+
Description: The host names the distribution answers on.
|
|
24
|
+
|
|
25
|
+
Resources:
|
|
26
|
+
Distribution:
|
|
27
|
+
Type: AWS::CloudFront::Distribution
|
|
28
|
+
DeletionPolicy: Retain
|
|
29
|
+
UpdateReplacePolicy: Retain
|
|
30
|
+
Properties:
|
|
31
|
+
DistributionConfig:
|
|
32
|
+
Enabled: true
|
|
33
|
+
HttpVersion: http2
|
|
34
|
+
IPV6Enabled: true
|
|
35
|
+
Aliases: !Ref DomainNames
|
|
36
|
+
ViewerCertificate:
|
|
37
|
+
AcmCertificateArn: !Ref CertificateArn
|
|
38
|
+
SslSupportMethod: sni-only
|
|
39
|
+
MinimumProtocolVersion: TLSv1.2_2021
|
|
40
|
+
Origins:
|
|
41
|
+
- Id: BoxOrigin
|
|
42
|
+
DomainName: !Ref AppOriginDomain
|
|
43
|
+
OriginCustomHeaders:
|
|
44
|
+
# Resolved by CloudFormation from Secrets Manager when the stack deploys, so the value
|
|
45
|
+
# never appears on a command line or in this file.
|
|
46
|
+
- HeaderName: @@ORIGIN_HEADER@@
|
|
47
|
+
HeaderValue: "{{resolve:secretsmanager:@@ORIGIN_SECRET@@:SecretString}}"
|
|
48
|
+
CustomOriginConfig:
|
|
49
|
+
OriginProtocolPolicy: http-only
|
|
50
|
+
OriginSSLProtocols: [TLSv1.2]
|
|
51
|
+
DefaultCacheBehavior:
|
|
52
|
+
TargetOriginId: BoxOrigin
|
|
53
|
+
ViewerProtocolPolicy: redirect-to-https
|
|
54
|
+
AllowedMethods: [GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE]
|
|
55
|
+
CachedMethods: [GET, HEAD]
|
|
56
|
+
Compress: true
|
|
57
|
+
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
|
|
58
|
+
OriginRequestPolicyId: 216adef6-5c7f-47e4-b989-5492eafa07d3 # Managed-AllViewer
|
|
59
|
+
|
|
60
|
+
Outputs:
|
|
61
|
+
DistributionId:
|
|
62
|
+
Value: !Ref Distribution
|
|
63
|
+
DistributionDomainName:
|
|
64
|
+
Description: Point each alias's DNS record (CNAME or alias) at this name.
|
|
65
|
+
Value: !GetAtt Distribution.DomainName
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Every container's log, one stream per service. The group is made here and not by Docker
|
|
2
|
+
# (awslogs-create-group is "false"), so the role below needs no right to create groups, and the
|
|
3
|
+
# retention is a declaration. A production group outlives the stack: the evidence a container
|
|
4
|
+
# wrote must not go with a deleted stack.
|
|
5
|
+
BoxLogGroup:
|
|
6
|
+
Type: AWS::Logs::LogGroup
|
|
7
|
+
DeletionPolicy: !If [IsProduction, Retain, Delete]
|
|
8
|
+
UpdateReplacePolicy: !If [IsProduction, Retain, Delete]
|
|
9
|
+
Properties:
|
|
10
|
+
LogGroupName: @@LOG_GROUP@@
|
|
11
|
+
RetentionInDays: @@RETENTION_DAYS@@
|
|
12
|
+
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# When the world names origin_env, the named origin secret is compared with those variables in the
|
|
14
14
|
# task definition and the render refuses on any difference (Caddy checks the CDN's header against
|
|
15
15
|
# the secret; the containers check it against their own copy). Values are never printed.
|
|
16
|
+
@@COMPOSE_LOG_NOTE@@
|
|
16
17
|
set -euo pipefail
|
|
17
18
|
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
18
19
|
DB_HOST=${1:?rds endpoint}
|
|
@@ -36,7 +37,7 @@ if [ -n "$ORIGIN_ENV" ]; then
|
|
|
36
37
|
fi
|
|
37
38
|
|
|
38
39
|
jq --argjson def "$DEF" --arg host "$DB_HOST" --arg secret "$DB_SECRET" '
|
|
39
|
-
|
|
40
|
+
@@LOG_DEF@@
|
|
40
41
|
def env(c): ((c.environment // []) | map({(.name): .value}) | add // {})
|
|
41
42
|
| (if has("DB_HOST") then .DB_HOST = $host | .DB_SECRET_ARN = $secret else . end);
|
|
42
43
|
. as $in
|
|
@@ -44,16 +45,16 @@ jq --argjson def "$DEF" --arg host "$DB_HOST" --arg secret "$DB_SECRET" '
|
|
|
44
45
|
($in.services | with_entries(.value |= (. as $s
|
|
45
46
|
| ($def | map(select(.name == $s.name)) | first) as $c
|
|
46
47
|
| if $c == null then error("task definition has no container named " + $s.name) else
|
|
47
|
-
{image: $c.image, network_mode: "host", restart: "unless-stopped", logging:
|
|
48
|
+
{image: $c.image, network_mode: "host", restart: "unless-stopped", logging: @@LOG_SERVICE@@,
|
|
48
49
|
environment: ({PORT: ($s.port | tostring)} + env($c))}
|
|
49
50
|
+ (if (($c.secrets // []) | length) > 0 then {env_file: [($s.name + ".secrets.env")]} else {} end)
|
|
50
51
|
end)))
|
|
51
52
|
+ {caddy: ({image: "@@PROXY_IMAGE@@", network_mode: "host", restart: "unless-stopped",
|
|
52
|
-
volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "./caddy-extra:/etc/caddy/extra:ro"], logging:
|
|
53
|
+
volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "./caddy-extra:/etc/caddy/extra:ro"], logging: @@LOG_CADDY@@}
|
|
53
54
|
+ (if $in.origin then {env_file: ["caddy.secrets.env"]} else {} end))}
|
|
54
55
|
+ (if $in.tunnel then {cloudflared: {image: $in.tunnel.image, network_mode: "host", restart: "unless-stopped",
|
|
55
56
|
command: ["tunnel", "--no-autoupdate", "--url", $in.tunnel.url, "run"],
|
|
56
|
-
env_file: ["cloudflared.secrets.env"], logging:
|
|
57
|
+
env_file: ["cloudflared.secrets.env"], logging: @@LOG_TUNNEL@@}} else {} end))}' \
|
|
57
58
|
"$HERE/services.json" > compose.json
|
|
58
59
|
|
|
59
60
|
jq --argjson def "$DEF" '. as $in
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
# Tags default to "latest". Every container gets DB_HOST, DB_NAME, DB_SECRET_ARN and PORT, and runs on the
|
|
7
7
|
# box's own network, so the proxy reaches each one on 127.0.0.1:<port>. Secrets named in services.json
|
|
8
8
|
# are written to secrets.json for fetch-secrets.sh to resolve on the box.
|
|
9
|
+
@@COMPOSE_LOG_NOTE@@
|
|
9
10
|
set -euo pipefail
|
|
10
11
|
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
11
12
|
DB_HOST=${1:?rds endpoint}
|
|
@@ -18,20 +19,20 @@ ECR="$ACCOUNT.dkr.ecr.$REGION.amazonaws.com"
|
|
|
18
19
|
TAGS=$(printf '%s\n' "$@" | jq -R 'select(length > 0) | split("=") | {(.[0]): .[1]}' | jq -s 'add // {}')
|
|
19
20
|
|
|
20
21
|
jq --arg ecr "$ECR" --argjson tags "$TAGS" --arg host "$DB_HOST" --arg secret "$DB_SECRET" --arg db "@@DB_NAME@@" '
|
|
21
|
-
|
|
22
|
+
@@LOG_DEF@@
|
|
22
23
|
. as $in
|
|
23
24
|
| {services: (
|
|
24
25
|
($in.services | with_entries(.value |= (
|
|
25
26
|
{image: ($ecr + "/" + .repository + ":" + ($tags[.name] // "latest")),
|
|
26
|
-
network_mode: "host", restart: "unless-stopped", logging:
|
|
27
|
+
network_mode: "host", restart: "unless-stopped", logging: @@LOG_SERVICE@@,
|
|
27
28
|
environment: ({PORT: (.port | tostring), DB_HOST: $host, DB_NAME: $db, DB_SECRET_ARN: $secret} + .env)}
|
|
28
29
|
+ (if (.secrets | length) > 0 then {env_file: [(.name + ".secrets.env")]} else {} end))))
|
|
29
30
|
+ {caddy: ({image: "@@PROXY_IMAGE@@", network_mode: "host", restart: "unless-stopped",
|
|
30
|
-
volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "./caddy-extra:/etc/caddy/extra:ro"], logging:
|
|
31
|
+
volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "./caddy-extra:/etc/caddy/extra:ro"], logging: @@LOG_CADDY@@}
|
|
31
32
|
+ (if $in.origin then {env_file: ["caddy.secrets.env"]} else {} end))}
|
|
32
33
|
+ (if $in.tunnel then {cloudflared: {image: $in.tunnel.image, network_mode: "host", restart: "unless-stopped",
|
|
33
34
|
command: ["tunnel", "--no-autoupdate", "--url", $in.tunnel.url, "run"],
|
|
34
|
-
env_file: ["cloudflared.secrets.env"], logging:
|
|
35
|
+
env_file: ["cloudflared.secrets.env"], logging: @@LOG_TUNNEL@@}} else {} end))}' \
|
|
35
36
|
"$HERE/services.json" > compose.json
|
|
36
37
|
|
|
37
38
|
jq '. as $in
|
|
@@ -5,10 +5,12 @@ require_relative "box/hosting"
|
|
|
5
5
|
require_relative "box/roll_recipe"
|
|
6
6
|
require_relative "box/caddy"
|
|
7
7
|
require_relative "box/access"
|
|
8
|
+
require_relative "box/logging"
|
|
8
9
|
require_relative "box/stack_yaml"
|
|
9
10
|
require_relative "box/compose"
|
|
10
11
|
require_relative "box/shared_database"
|
|
11
12
|
require_relative "box/roll_scripts"
|
|
13
|
+
require_relative "box/edge"
|
|
12
14
|
|
|
13
15
|
module Hecks
|
|
14
16
|
module Projections
|
|
@@ -21,10 +23,12 @@ module Hecks
|
|
|
21
23
|
extend Projector::Target
|
|
22
24
|
extend Caddy
|
|
23
25
|
extend Access
|
|
26
|
+
extend Logging
|
|
24
27
|
extend StackYaml
|
|
25
28
|
extend Compose
|
|
26
29
|
extend SharedDatabase
|
|
27
30
|
extend RollScripts
|
|
31
|
+
extend Edge
|
|
28
32
|
|
|
29
33
|
projects_as :aws_box, needs_world: true, emits: :files
|
|
30
34
|
|
|
@@ -123,7 +127,15 @@ module Hecks
|
|
|
123
127
|
volume_gb: { value: settings.fetch(:volume_gb, 30) },
|
|
124
128
|
database_class: { value: settings.fetch(:database_class, "db.t4g.small") },
|
|
125
129
|
storage_gb: { value: settings.fetch(:storage_gb, 20) }
|
|
126
|
-
}
|
|
130
|
+
}.merge(cdn_arguments(settings[:cdn]))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# @param cdn [Hash{Symbol => Object}, nil] the world's `cdn` setting
|
|
134
|
+
# @return [Hash{Symbol => Array}] the `cdn_aliases` argument, or nothing without a `cdn`
|
|
135
|
+
def cdn_arguments(cdn)
|
|
136
|
+
return {} unless cdn.is_a?(Hash) && cdn[:aliases].is_a?(Array)
|
|
137
|
+
|
|
138
|
+
{ cdn_aliases: cdn[:aliases].map { |name| { value: name } } }
|
|
127
139
|
end
|
|
128
140
|
|
|
129
141
|
# @param plan [Settings::Plan] the resolved settings
|
|
@@ -136,8 +148,8 @@ module Hecks
|
|
|
136
148
|
"render-compose.sh" => render_compose_sh(plan, region),
|
|
137
149
|
"fetch-secrets.sh" => File.read(File.join(TEMPLATE_DIR, "fetch-secrets.sh")),
|
|
138
150
|
"deploy-box.sh" => deploy_box_sh(plan, region),
|
|
139
|
-
"Makefile" => makefile(plan)
|
|
140
|
-
}.merge(database_files(plan)).merge(migration_files(plan))
|
|
151
|
+
"Makefile" => makefile(plan, region)
|
|
152
|
+
}.merge(database_files(plan)).merge(migration_files(plan)).merge(edge_files(plan, region))
|
|
141
153
|
.then { |files| Hosting.extend_files(files, plan: plan, region: region) }
|
|
142
154
|
end
|
|
143
155
|
|
|
@@ -365,7 +365,7 @@ signup_admin() {
|
|
|
365
365
|
# Prints what to paste into the browser console to sign in as the preview's first
|
|
366
366
|
# admin: a session for that email signed with THIS preview's session secret (the
|
|
367
367
|
# secret itself is never printed). Anyone with AWS access to the account could
|
|
368
|
-
# mint the same, which is the point: it is the deployer's
|
|
368
|
+
# mint the same, which is the point: it is the deployer's entry, not a public one.
|
|
369
369
|
cmd_login() {
|
|
370
370
|
[ -n "$(stack_status)" ] || die "no preview for branch '$BRANCH' - run 'preview.sh deploy'"
|
|
371
371
|
admin_identity
|
|
@@ -7,6 +7,9 @@ module Hecks
|
|
|
7
7
|
# VPC, RDS/Aurora, bastion and cross-domain invoke plumbing shared by the
|
|
8
8
|
# `Lambda` and `Fargate` deploy targets. Plain module functions, not a Target.
|
|
9
9
|
module Shared
|
|
10
|
+
# Raised when a bastion parameter names a stack output that is not declared.
|
|
11
|
+
class Error < RuntimeError; end
|
|
12
|
+
|
|
10
13
|
# The names a domain's own VPC and database are rendered by.
|
|
11
14
|
#
|
|
12
15
|
# @!attribute [r] shared [Boolean] whether this domain borrows another domain's RDS instance
|
|
@@ -66,11 +69,11 @@ module Hecks
|
|
|
66
69
|
|
|
67
70
|
# Generation-time check that every bastion parameter maps to a stack output.
|
|
68
71
|
#
|
|
69
|
-
# @raise [
|
|
72
|
+
# @raise [Error] if a parameter names a `from_output` absent from `stack_outputs`
|
|
70
73
|
def check_bastion_parameters!(bastion_parameters, stack_outputs)
|
|
71
74
|
bastion_parameters.each do |param|
|
|
72
75
|
stack_outputs.any? { |o| o[:key] == param[:from_output] } or
|
|
73
|
-
raise "bastion parameter #{param[:name]} references undeclared stack output #{param[:from_output]}"
|
|
76
|
+
raise Error, "bastion parameter #{param[:name]} references undeclared stack output #{param[:from_output]}"
|
|
74
77
|
end
|
|
75
78
|
end
|
|
76
79
|
|
|
@@ -17,8 +17,8 @@ module Hecks
|
|
|
17
17
|
|
|
18
18
|
# One model field gathered from several declared ones.
|
|
19
19
|
FOLDED = {
|
|
20
|
-
"Aggregate" => { lifecycle: %i[state_field state_start transitions] },
|
|
21
|
-
"Entity" => { lifecycle: %i[state_field state_start transitions] },
|
|
20
|
+
"Aggregate" => { lifecycle: %i[state_field state_start transitions marks] },
|
|
21
|
+
"Entity" => { lifecycle: %i[state_field state_start transitions marks] },
|
|
22
22
|
"Query" => { order_by: %i[order_field order_way] }
|
|
23
23
|
}.freeze
|
|
24
24
|
|
|
@@ -16,10 +16,10 @@ module Hecks
|
|
|
16
16
|
construct: "Policy",
|
|
17
17
|
file: "policy.rb",
|
|
18
18
|
behaviour: "Behaviour::Policy",
|
|
19
|
-
readers: %i[name on_event trigger_command target_domain expect_undelivered where for_each with_spec],
|
|
19
|
+
readers: %i[name on_event trigger_command ask target_domain expect_undelivered where for_each with_spec],
|
|
20
20
|
accessors: %i[aggregate],
|
|
21
21
|
defaults: { name: nil, on_event: "nil", trigger_command: "nil",
|
|
22
|
-
target_domain: "nil", expect_undelivered: "false", where: "nil", for_each: "nil",
|
|
22
|
+
ask: "nil", target_domain: "nil", expect_undelivered: "false", where: "nil", for_each: "nil",
|
|
23
23
|
with_spec: "[]", aggregate: "nil" },
|
|
24
24
|
# The language holds a flag as "true" and the builder as `true`; both become a boolean.
|
|
25
25
|
coerce: { name: ".to_s", aggregate: "&.to_s", expect_undelivered: ".to_s == \"true\"" },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "json"
|
|
1
2
|
require_relative "../naming"
|
|
2
3
|
require_relative "../projector"
|
|
3
4
|
|
|
@@ -35,6 +36,17 @@ module Hecks
|
|
|
35
36
|
}
|
|
36
37
|
end
|
|
37
38
|
|
|
39
|
+
# The manifest as the text `oidc.json` holds: indented JSON ending in a newline, with an empty
|
|
40
|
+
# list as `[]`. `JSON.pretty_generate` spells an empty list `[\n\n]` in some json releases
|
|
41
|
+
# (2.7.2 among them) and `[]` in others, so the file is normalised here and a committed
|
|
42
|
+
# manifest compares equal whichever json a machine has.
|
|
43
|
+
#
|
|
44
|
+
# @param manifest [Hash{String => Object}] what `call` returns
|
|
45
|
+
# @return [String] the text of the manifest file
|
|
46
|
+
def render(manifest)
|
|
47
|
+
"#{JSON.pretty_generate(manifest).gsub(/\[\n\s*\]/, "[]")}\n"
|
|
48
|
+
end
|
|
49
|
+
|
|
38
50
|
# Every command's scope entry, entity commands included (they dispatch as `Entity.Command`).
|
|
39
51
|
# Sorted by scope so two manifest versions diff cleanly.
|
|
40
52
|
#
|
|
@@ -79,7 +91,7 @@ module Hecks
|
|
|
79
91
|
end
|
|
80
92
|
|
|
81
93
|
# One scope entry per command, spelled like `banking:account.open`; snake-cased through
|
|
82
|
-
# `Naming.snake`, as the facade names its
|
|
94
|
+
# `Naming.snake`, as the facade names its module methods.
|
|
83
95
|
#
|
|
84
96
|
# @param commands [Array<Bluebook::Command>] the commands to project
|
|
85
97
|
# @param verb_prefix [String] the owning aggregate or entity's fully-qualified
|
|
@@ -6,6 +6,9 @@ module Hecks
|
|
|
6
6
|
# The storage shape of one bluebook, the form `StorageShape.mint_hash` hashes to name an era.
|
|
7
7
|
# Diffing two of these answers whether a change bumps the era.
|
|
8
8
|
module Shape
|
|
9
|
+
# Raised when the shape projection is asked for without the era plugin loaded.
|
|
10
|
+
class Error < RuntimeError; end
|
|
11
|
+
|
|
9
12
|
extend Projector::Target
|
|
10
13
|
|
|
11
14
|
projects_as :shape
|
|
@@ -15,8 +18,8 @@ module Hecks
|
|
|
15
18
|
# Registered even when the era plugin is unloaded; the call then refuses clearly.
|
|
16
19
|
def call(bluebook:, options: {})
|
|
17
20
|
unless Ports::Persistence.plugin?(:era)
|
|
18
|
-
raise "the :shape projection needs the era persistence plugin loaded " \
|
|
19
|
-
|
|
21
|
+
raise Error, "the :shape projection needs the era persistence plugin loaded " \
|
|
22
|
+
"(require \"hecks/ports/persistence/plugins/era\")"
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
Runtime::StorageShape.project(bluebook)
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "blocks"
|
|
4
|
+
require_relative "hints"
|
|
5
|
+
|
|
3
6
|
module Hecks
|
|
4
7
|
module Projections
|
|
5
8
|
module Site
|
|
@@ -9,11 +12,8 @@ module Hecks
|
|
|
9
12
|
#
|
|
10
13
|
# A type that names one of the aggregate's value objects is an `object`; a reference is a
|
|
11
14
|
# `reference`; the primitives are `integer`, `number`, `boolean` and `text`, and any other
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# An attribute whose value object has the shape of a structured document, a `blocks`
|
|
15
|
-
# list of a value object that has `kind` and `spans`, carries `widget: "body"` and is
|
|
16
|
-
# edited with the rich-text widget. The shape decides it, never a name.
|
|
15
|
+
# is text. The shape decides what more it is, never a name: a body (`widget: "body"`), a
|
|
16
|
+
# block list (see `Blocks`), a moment, a `<kind>:` key (see `Hints`) or a picture.
|
|
17
17
|
class Attributes
|
|
18
18
|
# The parts a value object needs for its list of blocks to be a document body's blocks.
|
|
19
19
|
BLOCK_PARTS = %w[kind spans].freeze
|
|
@@ -26,6 +26,7 @@ module Hecks
|
|
|
26
26
|
def initialize(objects)
|
|
27
27
|
@by_name = objects.to_h { |object| [object.hecks_name, object] }
|
|
28
28
|
@objects = @by_name.keys
|
|
29
|
+
@hints = Hints.new(@by_name)
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
# @param attribute [Bluebook::Attribute] a declared attribute
|
|
@@ -35,11 +36,30 @@ module Hecks
|
|
|
35
36
|
return reference(attribute, type.target_name) if type.is_a?(Bluebook::Reference)
|
|
36
37
|
|
|
37
38
|
entry = base(attribute, type.to_s, kind(type.to_s))
|
|
38
|
-
|
|
39
|
+
return entry.merge("widget" => "body") if body?(attribute)
|
|
40
|
+
|
|
41
|
+
entry.merge(@hints.of(attribute), listing(attribute), pictured(attribute))
|
|
39
42
|
end
|
|
40
43
|
|
|
41
44
|
private
|
|
42
45
|
|
|
46
|
+
# @return [Hash{String => Object}] `blockList` when the attribute is a list of blocks
|
|
47
|
+
def listing(attribute)
|
|
48
|
+
found = Blocks.of(attribute, @by_name)
|
|
49
|
+
found ? { "blockList" => found } : {}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [Hash{String => Object}] `picture` (the parts that name the file, its
|
|
53
|
+
# description and its caption) when the value object is a picture: it has a media key
|
|
54
|
+
# and an `alt` and is not a block of a body or a list of mixed blocks
|
|
55
|
+
def pictured(attribute)
|
|
56
|
+
object = @by_name[attribute.type.to_s]
|
|
57
|
+
return {} unless object && !Blocks.body_block?(object) && !listing(attribute).key?("blockList")
|
|
58
|
+
|
|
59
|
+
found = Blocks.picture(object)
|
|
60
|
+
found&.key?("key") && found.key?("alt") ? { "picture" => found } : {}
|
|
61
|
+
end
|
|
62
|
+
|
|
43
63
|
# A single (not listed) attribute whose value object holds a list of document blocks.
|
|
44
64
|
def body?(attribute)
|
|
45
65
|
return false if attribute.list?
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Projections
|
|
5
|
+
module Site
|
|
6
|
+
module CmsEditor
|
|
7
|
+
# A list of mixed blocks, found by shape: a `list_of` value object that has a discriminator,
|
|
8
|
+
# a part that is one of a closed set of kinds. The editor draws it as ordered cards.
|
|
9
|
+
#
|
|
10
|
+
# The discriminator is a part whose type is a closed set (`one_of`) of one plain value, or,
|
|
11
|
+
# failing that, a text part that an invariant restricts to a literal list
|
|
12
|
+
# (`["a", "b"].include?(kind)`). A value object that holds `spans` is a body's block, which
|
|
13
|
+
# the rich-text widget edits, and is never a block list.
|
|
14
|
+
#
|
|
15
|
+
# Which slots each kind uses is read from a declared table: a closed set of rows with the
|
|
16
|
+
# parts `kind`, `requires` and `uses` whose kinds are among the discriminator's. With no
|
|
17
|
+
# such table every slot is shown, and the invariants that name a kind only hint which slots
|
|
18
|
+
# it reads. A limit is read from an invariant or given of the form `<list>.size <= <n>`.
|
|
19
|
+
module Blocks
|
|
20
|
+
# The parts a table of rules must have.
|
|
21
|
+
RULE_PARTS = %w[kind requires uses].freeze
|
|
22
|
+
|
|
23
|
+
# The parts that make a value object's picture, in the vocabulary of a body's image block.
|
|
24
|
+
PICTURE = { "key" => "media_ref", "alt" => "alt", "caption" => "caption" }.freeze
|
|
25
|
+
|
|
26
|
+
# An invariant that restricts a text part to a literal list of kinds.
|
|
27
|
+
LITERALS = /\A\[((?:"[^"]+"(?:, )?)+)\]\.include\?\((\w+)\)\z/
|
|
28
|
+
|
|
29
|
+
# An invariant that holds unless the kind is the named one: what the rest of it reads.
|
|
30
|
+
GUARD = /\A(\w+)(?:\.value)? != "([^"]+)" \|\| (.*)\z/m
|
|
31
|
+
|
|
32
|
+
# A limit written as a size comparison.
|
|
33
|
+
LIMIT = /\A(\w+)\.size <= (\d+)\z/
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
# @param attribute [Bluebook::Attribute] a declared attribute
|
|
38
|
+
# @param objects [Hash{String => Bluebook::ValueObject}] the aggregate's value objects
|
|
39
|
+
# @return [Hash{String => Object}, nil] the block list the attribute is, or nil
|
|
40
|
+
def of(attribute, objects)
|
|
41
|
+
item = objects[attribute.type.to_s]
|
|
42
|
+
return nil unless attribute.list? && item && !body_block?(item)
|
|
43
|
+
|
|
44
|
+
found = discriminator(item, objects)
|
|
45
|
+
found && describe(item, objects, *found)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Boolean] whether the value object holds `spans`, as a body's block does
|
|
49
|
+
def body_block?(item) = item.attributes.any? { |part| part.name.to_s == "spans" }
|
|
50
|
+
|
|
51
|
+
# @return [Array(String, Array<String>), nil] the part and the kinds it is one of
|
|
52
|
+
def discriminator(item, objects)
|
|
53
|
+
closed(item, objects) || literal(item)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [Array(String, Array<String>), nil] a part typed as a closed set of plain values
|
|
57
|
+
def closed(item, objects)
|
|
58
|
+
item.attributes.each do |part|
|
|
59
|
+
kinds = members_of(objects[part.type.to_s]) unless part.list?
|
|
60
|
+
return [part.name.to_s, kinds] if kinds && !kinds.empty?
|
|
61
|
+
end
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Array<String>, nil] the values of a closed set of one plain part
|
|
66
|
+
def members_of(set)
|
|
67
|
+
return nil unless set&.closed_set? && set.attributes.size == 1
|
|
68
|
+
|
|
69
|
+
set.members.filter_map { |member| member[set.attributes.first.name]&.to_s }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @return [Array(String, Array<String>), nil] a text part restricted by a literal list
|
|
73
|
+
def literal(item)
|
|
74
|
+
found = literals(item).find { |match| item.attributes.any? { |part| part.name.to_s == match[2] && !part.list? } }
|
|
75
|
+
found && [found[2], found[1].scan(/"([^"]+)"/).flatten]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [Array<MatchData>] the invariants that restrict a part to a literal list
|
|
79
|
+
def literals(item) = item.invariants.filter_map { |rule| LITERALS.match(rule.canonical.to_s) }
|
|
80
|
+
|
|
81
|
+
def describe(item, objects, name, kinds)
|
|
82
|
+
table = rules(objects, kinds)
|
|
83
|
+
found = { "discriminator" => name, "kinds" => kinds, "limits" => limits(item), "pictures" => pictures(item, objects) }
|
|
84
|
+
found["rules"] = table if table
|
|
85
|
+
found["mentions"] = mentions(item, name, kinds) unless table
|
|
86
|
+
found.reject { |_, value| value.respond_to?(:empty?) && value.empty? }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @return [Hash{String => Hash}, nil] each kind's `requires` and `uses`, from the table
|
|
90
|
+
def rules(objects, kinds)
|
|
91
|
+
table = objects.values.find { |object| ruled?(object, kinds) }
|
|
92
|
+
return nil unless table
|
|
93
|
+
|
|
94
|
+
table.members.to_h { |row| [row[:kind].to_s, { "requires" => words(row[:requires]), "uses" => words(row[:uses]) }] }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def ruled?(object, kinds)
|
|
98
|
+
object.closed_set? && (RULE_PARTS - object.attributes.map { |part| part.name.to_s }).empty? &&
|
|
99
|
+
object.members.all? { |row| kinds.include?(row[:kind].to_s) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def words(text) = text.to_s.split(",").map(&:strip).reject(&:empty?)
|
|
103
|
+
|
|
104
|
+
# @return [Hash{String => Array<String>}] the slots each kind's guarded invariants read
|
|
105
|
+
def mentions(item, name, kinds)
|
|
106
|
+
parts = item.attributes.map { |part| part.name.to_s } - [name]
|
|
107
|
+
guarded(item, name).each_with_object({}) do |(kind, rest), found|
|
|
108
|
+
next unless kinds.include?(kind)
|
|
109
|
+
|
|
110
|
+
found[kind] = ((found[kind] || []) | parts.select { |part| rest.match?(/\b#{Regexp.escape(part)}\b/) })
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def guarded(item, name)
|
|
115
|
+
item.invariants.filter_map do |rule|
|
|
116
|
+
match = GUARD.match(rule.canonical.to_s)
|
|
117
|
+
[match[2], match[3]] if match && match[1] == name
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# @return [Hash{String => Integer}] each list part of the item and its declared maximum
|
|
122
|
+
def limits(item)
|
|
123
|
+
item.invariants.filter_map do |rule|
|
|
124
|
+
match = LIMIT.match(rule.canonical.to_s)
|
|
125
|
+
[match[1], match[2].to_i] if match
|
|
126
|
+
end.to_h
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @param commands [Array<Bluebook::Command>] the aggregate's commands
|
|
130
|
+
# @param name [String] a list attribute's name
|
|
131
|
+
# @return [Integer, nil] the most the list may hold, from a command's `given`
|
|
132
|
+
def maximum(commands, name)
|
|
133
|
+
sizes = commands.flat_map(&:givens).filter_map do |rule|
|
|
134
|
+
match = LIMIT.match(rule.canonical.to_s)
|
|
135
|
+
match[2].to_i if match && match[1] == name
|
|
136
|
+
end
|
|
137
|
+
sizes.min
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @return [Hash{String => Hash}] the value object and each value object its lists hold,
|
|
141
|
+
# to the parts that name a picture, for those that have the parts
|
|
142
|
+
def pictures(item, objects)
|
|
143
|
+
held = item.attributes.select(&:list?).filter_map { |part| objects[part.type.to_s] }
|
|
144
|
+
[item, *held].filter_map { |object| (found = picture(object)) && [object.hecks_name, found] }.to_h
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def picture(object)
|
|
148
|
+
names = object.attributes.reject(&:list?).map { |part| part.name.to_s }
|
|
149
|
+
return nil unless names.include?(PICTURE["key"]) && names.include?(PICTURE["alt"])
|
|
150
|
+
|
|
151
|
+
PICTURE.select { |_, part| names.include?(part) }
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|