hecks 3.9.1 → 3.11.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.
Files changed (504) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
  3. data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
  4. data/exe/hecks +4 -4
  5. data/lib/hecks/adapters/driven/d1.rb +2 -10
  6. data/lib/hecks/adapters/driven/heki.adapter +1 -1
  7. data/lib/hecks/adapters/driven/heki.rb +2 -12
  8. data/lib/hecks/adapters/driven/lambda.adapter +1 -1
  9. data/lib/hecks/adapters/driven/lambda.rb +3 -18
  10. data/lib/hecks/adapters/driven/postgres.rb +4 -11
  11. data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
  12. data/lib/hecks/adapters/driven/sqlite.rb +2 -10
  13. data/lib/hecks/adapters/driving/admission.rb +46 -0
  14. data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
  15. data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
  16. data/lib/hecks/adapters/driving/cli.rb +127 -0
  17. data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
  18. data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
  19. data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
  20. data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
  21. data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
  22. data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
  23. data/lib/hecks/adapters/driving/command_request.rb +121 -0
  24. data/lib/hecks/adapters/driving/handle.rb +214 -0
  25. data/lib/hecks/adapters/driving/json.rb +136 -0
  26. data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
  27. data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
  28. data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
  29. data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
  30. data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
  31. data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
  32. data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
  33. data/lib/hecks/adapters/driving/mcp.rb +159 -0
  34. data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
  35. data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
  36. data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
  37. data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
  38. data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
  39. data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
  40. data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
  41. data/lib/hecks/adapters/driving/ruby.rb +83 -0
  42. data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
  43. data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
  44. data/lib/hecks/behaviors/expectations/boot.rb +1 -1
  45. data/lib/hecks/bench/ruby_runner.rb +1 -1
  46. data/lib/hecks/bluebook/ask_resolution.rb +130 -0
  47. data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
  48. data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
  49. data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
  50. data/lib/hecks/bluebook/assembly/marks.rb +1 -0
  51. data/lib/hecks/bluebook/attribute.rb +1 -1
  52. data/lib/hecks/bluebook/behaviour/command.rb +13 -8
  53. data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
  54. data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
  55. data/lib/hecks/bluebook/capabilities.rb +91 -9
  56. data/lib/hecks/bluebook/domain_port.rb +16 -4
  57. data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
  58. data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
  59. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
  60. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
  61. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
  62. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
  63. data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
  64. data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
  65. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  66. data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
  67. data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
  68. data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
  69. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
  70. data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
  71. data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
  72. data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
  73. data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
  74. data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
  75. data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
  76. data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
  77. data/lib/hecks/bluebook/expression/projection.json +36 -0
  78. data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
  79. data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
  80. data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
  81. data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
  82. data/lib/hecks/bluebook/expression/resolver.rb +20 -1
  83. data/lib/hecks/bluebook/hecksagon.rb +23 -3
  84. data/lib/hecks/bluebook/lifecycle.rb +22 -3
  85. data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
  86. data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
  87. data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
  88. data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
  89. data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
  90. data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
  91. data/lib/hecks/bluebook/meta_validator.rb +12 -12
  92. data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
  93. data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
  94. data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
  95. data/lib/hecks/bluebook/model_check.rb +8 -2
  96. data/lib/hecks/bluebook/policy.rb +4 -2
  97. data/lib/hecks/bluebook/smoke_test.rb +1 -1
  98. data/lib/hecks/bluebook/synthesizer.rb +2 -0
  99. data/lib/hecks/bluebook.rb +1 -0
  100. data/lib/hecks/cli/command_table.rb +1 -1
  101. data/lib/hecks/cli/console.rb +2 -1
  102. data/lib/hecks/cli/document.rb +1 -1
  103. data/lib/hecks/cli/launcher_words.rb +4 -4
  104. data/lib/hecks/cli/mcp.rb +4 -4
  105. data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
  106. data/lib/hecks/cli/project_cli/templates.rb +5 -5
  107. data/lib/hecks/cli/project_cli.rb +9 -6
  108. data/lib/hecks/cli/run.rb +2 -2
  109. data/lib/hecks/cli/smoke_http/checks.rb +4 -1
  110. data/lib/hecks/cli/smoke_http/transport.rb +3 -3
  111. data/lib/hecks/cli/smoke_http.rb +3 -0
  112. data/lib/hecks/codemod.rb +5 -2
  113. data/lib/hecks/corpus/routes.rb +7 -0
  114. data/lib/hecks/corpus/rust.rb +3 -2
  115. data/lib/hecks/deploy/bluebook/deploy.bluebook +124 -2
  116. data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
  117. data/lib/hecks/deploy/oidc.json +16 -3
  118. data/lib/hecks/doc/reference/rendering.rb +6 -3
  119. data/lib/hecks/forms/app/commands.rb +8 -7
  120. data/lib/hecks/forms/app/queries.rb +2 -1
  121. data/lib/hecks/forms/app.rb +1 -1
  122. data/lib/hecks/forms/record_renderer.rb +1 -1
  123. data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
  124. data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
  125. data/lib/hecks/fuzzing/failure_triage.rb +2 -1
  126. data/lib/hecks/fuzzing/mutation/run.rb +11 -4
  127. data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
  128. data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
  129. data/lib/hecks/fuzzing/properties/guards.rb +1 -1
  130. data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
  131. data/lib/hecks/fuzzing/self_consistency.rb +6 -2
  132. data/lib/hecks/fuzzing/shrinker.rb +3 -1
  133. data/lib/hecks/fuzzing/value_generator.rb +19 -5
  134. data/lib/hecks/gate/stages.yml +4 -10
  135. data/lib/hecks/grammar/evolve.rb +1 -1
  136. data/lib/hecks/grammar/expression_operators.json +372 -0
  137. data/lib/hecks/grammar/operators.rb +3 -1
  138. data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
  139. data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
  140. data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
  141. data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
  142. data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
  143. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
  144. data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
  145. data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
  146. data/lib/hecks/hecks/adapters/deploy_toolchain/clearing.rb +51 -0
  147. data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
  148. data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -0
  149. data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
  150. data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
  151. data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
  152. data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
  153. data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
  154. data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
  155. data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
  156. data/lib/hecks/hecks/adapters/local_files.rb +2 -2
  157. data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
  158. data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
  159. data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
  160. data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
  161. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
  162. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
  163. data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
  164. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
  165. data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
  166. data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
  167. data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
  168. data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
  169. data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
  170. data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
  171. data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
  172. data/lib/hecks/hecks/adapters/site_toolchain/role_prober.rb +78 -0
  173. data/lib/hecks/hecks/adapters/site_toolchain.rb +29 -4
  174. data/lib/hecks/hecks/adapters/terminal.rb +13 -11
  175. data/lib/hecks/hecks/adapters/world_clearing.rb +65 -0
  176. data/lib/hecks/hecks/codebase.behaviors +13 -1
  177. data/lib/hecks/hecks/codebase.bluebook +239 -5
  178. data/lib/hecks/hecks/custodian.behaviors +24 -6
  179. data/lib/hecks/hecks/custodian.bluebook +6 -1227
  180. data/lib/hecks/hecks/deploy.behaviors +1 -1
  181. data/lib/hecks/hecks/deploy.world +1 -3
  182. data/lib/hecks/hecks/hecks.behaviors +1 -1
  183. data/lib/hecks/hecks/hecks.hecksagon +61 -18
  184. data/lib/hecks/hecks/hecks.world +11 -10
  185. data/lib/hecks/hecks/rehearsal.bluebook +477 -0
  186. data/lib/hecks/hecks/site.behaviors +1 -1
  187. data/lib/hecks/hecks/site.world +0 -1
  188. data/lib/hecks/hecks/tooling.bluebook +1238 -0
  189. data/lib/hecks/indifferent_key.rb +20 -0
  190. data/lib/hecks/language/adapter.bluebook +13 -3
  191. data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
  192. data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
  193. data/lib/hecks/language/bluebook/entity.bluebook +18 -0
  194. data/lib/hecks/language/bluebook/policy.bluebook +9 -1
  195. data/lib/hecks/language/bluebook/query.bluebook +1 -1
  196. data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
  197. data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
  198. data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
  199. data/lib/hecks/language/oidc.json +10 -0
  200. data/lib/hecks/language/port.bluebook +4 -4
  201. data/lib/hecks/literal.rb +12 -3
  202. data/lib/hecks/ports/key_vault.rb +7 -2
  203. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
  204. data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
  205. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
  206. data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
  207. data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
  208. data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
  209. data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +15 -0
  210. data/lib/hecks/projections/deploy/fargate/settings.rb +3 -1
  211. data/lib/hecks/projections/deploy/fargate/stack/readers.rb +1 -0
  212. data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
  213. data/lib/hecks/projections/deploy/shared.rb +5 -2
  214. data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +7 -4
  215. data/lib/hecks/projections/model/deviations.rb +2 -2
  216. data/lib/hecks/projections/model.rb +2 -2
  217. data/lib/hecks/projections/oidc.rb +13 -1
  218. data/lib/hecks/projections/shape.rb +5 -2
  219. data/lib/hecks/projections/site/cms_editor/attributes.rb +95 -0
  220. data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
  221. data/lib/hecks/projections/site/cms_editor/checks.rb +77 -0
  222. data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
  223. data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
  224. data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
  225. data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
  226. data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
  227. data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
  228. data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
  229. data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
  230. data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
  231. data/lib/hecks/projections/site/cms_editor/media.rb +85 -0
  232. data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
  233. data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
  234. data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
  235. data/lib/hecks/projections/site/cms_editor/pretty.rb +40 -0
  236. data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
  237. data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
  238. data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
  239. data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
  240. data/lib/hecks/projections/site/cms_editor/schema.rb +161 -0
  241. data/lib/hecks/projections/site/cms_editor/setting.rb +130 -0
  242. data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
  243. data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +36 -0
  244. data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +370 -0
  245. data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
  246. data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +87 -0
  247. data/lib/hecks/projections/site/cms_editor/templates/src/auth/session.ts.tmpl +36 -0
  248. data/lib/hecks/projections/site/cms_editor/templates/src/auth/sso.ts.tmpl +20 -0
  249. data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
  250. data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
  251. data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
  252. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
  253. data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
  254. data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
  255. data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
  256. data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
  257. data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
  258. data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
  259. data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
  260. data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
  261. data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
  262. data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
  263. data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
  264. data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
  265. data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +133 -0
  266. data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +29 -0
  267. data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
  268. data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +43 -0
  269. data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +177 -0
  270. data/lib/hecks/projections/site/cms_editor/templates/src/media/multipart.ts.tmpl +53 -0
  271. data/lib/hecks/projections/site/cms_editor/templates/src/media/sniff.ts.tmpl +94 -0
  272. data/lib/hecks/projections/site/cms_editor/templates/src/media/storage.ts.tmpl +50 -0
  273. data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
  274. data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +214 -0
  275. data/lib/hecks/projections/site/cms_editor/templates/src/server.ts.tmpl +65 -0
  276. data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
  277. data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
  278. data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
  279. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +123 -0
  280. data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_parse.js.tmpl +320 -0
  281. data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
  282. data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
  283. data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +275 -0
  284. data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +61 -0
  285. data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
  286. data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +92 -0
  287. data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
  288. data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
  289. data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +63 -0
  290. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +213 -0
  291. data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
  292. data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
  293. data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
  294. data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
  295. data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
  296. data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
  297. data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
  298. data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
  299. data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
  300. data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
  301. data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
  302. data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +17 -0
  303. data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
  304. data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
  305. data/lib/hecks/projections/site/cms_editor.rb +133 -0
  306. data/lib/hecks/projections/site/role_probe.rb +74 -0
  307. data/lib/hecks/projector/cli_audience.rb +2 -1
  308. data/lib/hecks/projector/cli_projector/listing.rb +1 -1
  309. data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
  310. data/lib/hecks/projector/docs_projector/closing.rb +1 -1
  311. data/lib/hecks/projector/docs_projector.rb +1 -1
  312. data/lib/hecks/projector/exporter/bindings.rb +40 -10
  313. data/lib/hecks/projector/exporter/capability_values.rb +55 -0
  314. data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
  315. data/lib/hecks/projector/target.rb +11 -0
  316. data/lib/hecks/projector.rb +32 -5
  317. data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
  318. data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
  319. data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
  320. data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
  321. data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
  322. data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
  323. data/lib/hecks/quality_control/quality_control.bluebook +6 -2
  324. data/lib/hecks/release/approval.rb +64 -0
  325. data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
  326. data/lib/hecks/router/namespace_installer.rb +16 -16
  327. data/lib/hecks/router.rb +1 -1
  328. data/lib/hecks/runtime/best_effort.rb +22 -0
  329. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
  330. data/lib/hecks/runtime/command_interpreter.rb +3 -2
  331. data/lib/hecks/runtime/command_rules/emission.rb +1 -1
  332. data/lib/hecks/runtime/dispatcher.rb +4 -4
  333. data/lib/hecks/runtime/entity_element/location.rb +1 -1
  334. data/lib/hecks/runtime/event.rb +5 -0
  335. data/lib/hecks/runtime/identity.rb +7 -6
  336. data/lib/hecks/runtime/interpreting.rb +2 -1
  337. data/lib/hecks/runtime/invocation_builder.rb +1 -1
  338. data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
  339. data/lib/hecks/runtime/loader.rb +10 -10
  340. data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
  341. data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
  342. data/lib/hecks/runtime/policy_interpreter.rb +6 -6
  343. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
  344. data/lib/hecks/runtime/reaction_invocation.rb +1 -1
  345. data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
  346. data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
  347. data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
  348. data/lib/hecks/runtime/registry/merging.rb +1 -1
  349. data/lib/hecks/runtime/registry/providers.rb +16 -0
  350. data/lib/hecks/runtime/registry/read_repository.rb +6 -4
  351. data/lib/hecks/runtime/registry/verification.rb +4 -1
  352. data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
  353. data/lib/hecks/runtime/registry.rb +1 -1
  354. data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
  355. data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
  356. data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
  357. data/lib/hecks/runtime/saga_interpreter.rb +3 -3
  358. data/lib/hecks/runtime/tenant_check.rb +5 -4
  359. data/lib/hecks/runtime/value/coercion.rb +2 -2
  360. data/lib/hecks/runtime/value/validation.rb +16 -4
  361. data/lib/hecks/runtime.rb +9 -9
  362. data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
  363. data/lib/hecks/rust_build/wasm.rb +1 -1
  364. data/lib/hecks/rust_build/wasm_browser.rb +1 -1
  365. data/lib/hecks/rust_build.rb +11 -0
  366. data/lib/hecks/served_options.rb +53 -0
  367. data/lib/hecks/site/bluebook/site.bluebook +29 -1
  368. data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
  369. data/lib/hecks/storehouse/audit.rb +1 -1
  370. data/lib/hecks/storehouse/dispatching.rb +8 -6
  371. data/lib/hecks/storehouse/introspection.rb +2 -2
  372. data/lib/hecks/storehouse/querying.rb +6 -4
  373. data/lib/hecks/storehouse.rb +4 -4
  374. data/lib/hecks/tools/comment_style/source_file.rb +4 -1
  375. data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
  376. data/lib/hecks/tools/lanes/rows.rb +6 -0
  377. data/lib/hecks/tools/oidc_manifests.rb +4 -5
  378. data/lib/hecks/tools/regeneration_run.rb +11 -4
  379. data/lib/hecks/tools/site_routes/cli.rb +1 -1
  380. data/lib/hecks/tools/site_routes/editor_files.rb +45 -0
  381. data/lib/hecks/tools/site_routes.rb +14 -7
  382. data/lib/hecks/tools/tenant_provisioning.rb +2 -2
  383. data/lib/hecks/tools/tools_doc.rb +4 -4
  384. data/lib/hecks/version.rb +1 -1
  385. data/lib/hecks/vocabulary.rb +4 -4
  386. data/lib/hecks.rb +15 -15
  387. data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
  388. data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
  389. data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
  390. data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
  391. data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
  392. data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
  393. data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
  394. data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
  395. data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
  396. data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
  397. data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
  398. data/rust/.gitignore +6 -0
  399. data/rust/Cargo.toml +9 -1
  400. data/rust/build/src/binding_pass.rs +103 -0
  401. data/rust/build/src/cargo_sync.rs +6 -0
  402. data/rust/build/src/lineage_pass.rs +3 -3
  403. data/rust/build/src/main.rs +17 -5
  404. data/rust/build/src/pipeline.rs +48 -5
  405. data/rust/build/src/seams_pass.rs +123 -0
  406. data/rust/build/src/translation_pass.rs +167 -0
  407. data/rust/codegen/src/asks.rs +103 -0
  408. data/rust/codegen/src/bridging.rs +1 -1
  409. data/rust/codegen/src/commands.rs +13 -8
  410. data/rust/codegen/src/constraints.rs +25 -0
  411. data/rust/codegen/src/domain_generator.rs +11 -9
  412. data/rust/codegen/src/expr_emitter.rs +13 -0
  413. data/rust/codegen/src/json_codec.rs +54 -15
  414. data/rust/codegen/src/main.rs +2 -1
  415. data/rust/codegen/src/mutations.rs +174 -4
  416. data/rust/codegen/src/naming.rs +30 -0
  417. data/rust/codegen/src/queries.rs +11 -2
  418. data/rust/codegen/src/reactions.rs +1 -1
  419. data/rust/codegen/src/types.rs +166 -8
  420. data/rust/host/HECKS_RELEASE +1 -1
  421. data/rust/host/src/api.rs +4 -4
  422. data/rust/host/src/bin/validate_harness.rs +43 -0
  423. data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
  424. data/rust/host/src/boundary_fuzz/ir.rs +1 -0
  425. data/rust/host/src/boundary_fuzz/server.rs +1 -1
  426. data/rust/host/src/checkout.rs +35 -19
  427. data/rust/host/src/commerce_ir.rs +408 -2
  428. data/rust/host/src/expr_json.rs +118 -0
  429. data/rust/host/src/mint.rs +1 -1
  430. data/rust/host/src/payments/tests.rs +27 -1
  431. data/rust/host/src/payments.rs +12 -4
  432. data/rust/host/src/query_step.rs +22 -4
  433. data/rust/host/src/reference_validate.rs +653 -108
  434. data/rust/host/src/resend.rs +2 -0
  435. data/rust/host/src/server.rs +19 -6
  436. data/rust/host/src/ui_schema.rs +132 -5
  437. data/rust/host/src/web/newsletter.rs +7 -8
  438. data/rust/host/src/web/newsletter_send.rs +38 -13
  439. data/rust/host/src/web/registrations.rs +229 -26
  440. data/rust/host/src/web.rs +3 -3
  441. data/rust/parser/src/emit.rs +38 -6
  442. data/rust/parser/src/expr/ast_json.rs +7 -1
  443. data/rust/parser/src/expr/resolver.rs +171 -6
  444. data/rust/parser/src/ir.rs +7 -0
  445. data/rust/parser/src/keywords.rs +24 -0
  446. data/rust/parser/src/main.rs +3 -0
  447. data/rust/parser/src/parse/aggregate.rs +2 -1
  448. data/rust/parser/src/parse/chapter.rs +2 -2
  449. data/rust/parser/src/parse/command.rs +2 -0
  450. data/rust/parser/src/parse/hecksagon.rs +5 -2
  451. data/rust/parser/src/parse/lifecycle.rs +81 -0
  452. data/rust/parser/src/parse/mod.rs +15 -1
  453. data/rust/parser/src/parse/policy.rs +13 -2
  454. data/rust/parser/src/parse/value_object.rs +13 -8
  455. data/rust/parser/src/ruby_value.rs +1 -1
  456. data/rust/src/exemplar/commands.rs +4 -4
  457. data/rust/src/exemplar/constraints.rs +1 -1
  458. data/rust/src/exemplar/json.rs +43 -1
  459. data/rust/src/kernel/cli.rs +7 -5
  460. data/rust/src/kernel/dispatch.rs +2 -2
  461. data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
  462. data/rust/src/kernel/expr.rs +73 -4
  463. data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
  464. data/rust/src/kernel/expression_operators/presence.rs +41 -3
  465. data/rust/src/kernel/expression_operators/text.rs +26 -2
  466. data/rust/src/kernel/json.rs +15 -0
  467. data/rust/src/kernel/mod.rs +1 -1
  468. data/rust/src/kernel/orchestrate.rs +54 -5
  469. data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
  470. data/rust/src/kernel/pattern.rs +60 -0
  471. data/rust/src/kernel/reference_lookup.rs +90 -7
  472. metadata +174 -36
  473. data/lib/hecks/doors/cli_door/nesting.rb +0 -34
  474. data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
  475. data/lib/hecks/doors/cli_door.rb +0 -124
  476. data/lib/hecks/doors/cli_runner/answers.rb +0 -138
  477. data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
  478. data/lib/hecks/doors/cli_runner/settling.rb +0 -115
  479. data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
  480. data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
  481. data/lib/hecks/doors/cli_runner.rb +0 -66
  482. data/lib/hecks/doors/command_request.rb +0 -119
  483. data/lib/hecks/doors/handle.rb +0 -208
  484. data/lib/hecks/doors/json_door.rb +0 -132
  485. data/lib/hecks/doors/launcher_options.rb +0 -247
  486. data/lib/hecks/doors/mcp_door/properties.rb +0 -47
  487. data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
  488. data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
  489. data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
  490. data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
  491. data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
  492. data/lib/hecks/doors/mcp_door.rb +0 -156
  493. data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
  494. data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
  495. data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
  496. data/lib/hecks/doors/mcp_door_scope.rb +0 -201
  497. data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
  498. data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
  499. data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
  500. data/lib/hecks/doors/ruby_door.rb +0 -80
  501. data/lib/hecks/doors/usage_cache.rb +0 -139
  502. data/prebuilt/0ee7f83cba59ce7d1c45c65ba166b071779f70143449f584f33ff7989f64eccb.marshal +0 -0
  503. data/prebuilt/6e4a6bcf0aa880e2e6b056e1846b70e5a768090b9dc998bbadeaf5d6bcfd3db8.marshal +0 -0
  504. data/prebuilt/verdicts-91aaf93ce8981bd9a754948f075d95650dc2ec6435015376f5c60992b36a24b1.json +0 -1
@@ -1,119 +0,0 @@
1
- require_relative "../runtime/errors"
2
-
3
- module Hecks
4
- module Doors
5
- # Turns an external command request into the dispatcher's `to:`/`with:` envelope.
6
- #
7
- # aggregate command: { to: "record-id", with: { declared: "facts" } }
8
- # entity command: { to: { aggregate: "...", entity: "..." }, with: { ... } }
9
- module CommandRequest
10
- module_function
11
-
12
- # Splits a request, explicit envelope or flat Hash, into routing (`to:`) and facts (`with:`).
13
- # Keys are symbolized at every depth; in a flat Hash everything but the receiver is a fact.
14
- #
15
- # @param input [Hash] the request, with String or Symbol keys
16
- # @param receiver [Symbol, nil] `:aggregate`, `:entity`, or `nil` for a command with none
17
- # @param legacy_receiver [Symbol, String, Hash, nil] the flat key that may carry the
18
- # receiver instead of `to:`: a name for `:aggregate`, `{ aggregate:, entity: }` for
19
- # `:entity`
20
- # @return [Hash{Symbol => Object}] `{ with: facts }`, plus `to:` when `receiver` is set
21
- # @raise [Runtime::TypeMismatch] if the request is malformed or the route does not fit
22
- # the receiver kind
23
- # @raise [ArgumentError] if `receiver` is not `nil`, `:aggregate` or `:entity`
24
- def normalize(input, receiver:, legacy_receiver: nil)
25
- request = symbolize(input)
26
- raise Runtime::TypeMismatch, "a command request must be a hash" unless request.is_a?(Hash)
27
-
28
- route, facts = split(request, receiver: receiver, legacy_receiver: legacy_receiver)
29
- validate_route!(route, receiver)
30
-
31
- envelope = { with: facts }
32
- envelope[:to] = route if receiver
33
- envelope
34
- end
35
-
36
- def split(request, receiver:, legacy_receiver:)
37
- return split_envelope(request) if request.key?(:with)
38
-
39
- flat = request.dup
40
- route = flat.delete(:to)
41
- route = take_legacy_route(flat, receiver, legacy_receiver) if route.nil?
42
- [route, flat]
43
- end
44
- private_class_method :split
45
-
46
- def split_envelope(request)
47
- loose = request.keys - [:to, :with]
48
- unless loose.empty?
49
- raise Runtime::TypeMismatch,
50
- "an explicit command envelope takes routing in to: and facts in with:, not loose #{loose.sort.join(", ")}"
51
- end
52
-
53
- facts = request[:with]
54
- raise Runtime::TypeMismatch, "with: must be a hash of command facts" unless facts.is_a?(Hash)
55
-
56
- [request[:to], facts]
57
- end
58
- private_class_method :split_envelope
59
-
60
- def take_legacy_route(flat, receiver, legacy_receiver)
61
- return unless legacy_receiver
62
-
63
- return flat.delete(legacy_receiver.to_sym) if receiver == :aggregate
64
-
65
- return unless receiver == :entity && legacy_receiver.is_a?(Hash)
66
-
67
- aggregate_key = legacy_receiver.fetch(:aggregate).to_sym
68
- entity_key = legacy_receiver.fetch(:entity).to_sym
69
- return unless flat.key?(aggregate_key) || flat.key?(entity_key)
70
-
71
- { aggregate: flat.delete(aggregate_key), entity: flat.delete(entity_key) }
72
- end
73
- private_class_method :take_legacy_route
74
-
75
- # One self-contained check per closed receiver kind, plus a backstop.
76
- def validate_route!(route, receiver)
77
- case receiver
78
- when nil then refuse_receiver(route)
79
- when :aggregate then require_identity(route)
80
- when :entity then require_entity_route(route)
81
- else raise ArgumentError, "unknown receiver kind #{receiver.inspect}"
82
- end
83
- end
84
- private_class_method :validate_route!
85
-
86
- def refuse_receiver(route)
87
- raise Runtime::TypeMismatch, "this command does not take a receiver in to:" unless route.nil?
88
- end
89
- private_class_method :refuse_receiver
90
-
91
- def require_identity(route)
92
- return unless route.nil? || route.to_s.empty? || route.is_a?(Hash)
93
-
94
- raise Runtime::TypeMismatch, "to: must name the receiving aggregate identity"
95
- end
96
- private_class_method :require_identity
97
-
98
- def require_entity_route(route)
99
- raise Runtime::TypeMismatch, "to: for an entity command must contain aggregate: and entity:" unless route.is_a?(Hash)
100
-
101
- extra = route.keys - [:aggregate, :entity]
102
- missing = [:aggregate, :entity].select { |key| route[key].nil? || route[key].to_s.empty? }
103
- return if extra.empty? && missing.empty?
104
-
105
- raise Runtime::TypeMismatch, "to: for an entity command must contain only aggregate: and entity:"
106
- end
107
- private_class_method :require_entity_route
108
-
109
- def symbolize(value)
110
- case value
111
- when Hash then value.to_h { |key, nested| [key.to_sym, symbolize(nested)] }
112
- when Array then value.map { |nested| symbolize(nested) }
113
- else value
114
- end
115
- end
116
- private_class_method :symbolize
117
- end
118
- end
119
- end
@@ -1,208 +0,0 @@
1
- require_relative "../naming"
2
- require_relative "../runtime/caller"
3
- require_relative "../ports/authorization"
4
-
5
- module Hecks
6
- module Doors
7
- # A record in hand: what `Pizza.create_pizza!(...)` and `Pizza.find(id)`
8
- # give back. One shared class per aggregate; verbs are per-handle singleton methods.
9
- #
10
- # Pizza.create_pizza!(...).add_topping!(...).purchase!(...)
11
- class Handle
12
- attr_reader :id
13
-
14
- # @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted
15
- # dispatcher this record's verbs, `events` and `reload` go through
16
- # @param domain [String] the owning chapter's name, the first half of `fqn`
17
- # @param aggregate [Bluebook::Aggregate] the IR of the aggregate this record is one of
18
- # @param instance [Runtime::Instance] the stored record; its `id` and `state` are
19
- # read once here and the instance itself is not retained
20
- def initialize(dispatcher:, domain:, aggregate:, instance:)
21
- @dispatcher = dispatcher
22
- @domain = domain
23
- @ir = aggregate
24
- @id = instance.id
25
- @state = instance.state
26
- define_reference_accessors
27
- define_verb_methods
28
- end
29
-
30
- # Reads one field's raw stored value, without hydrating a reference the way the
31
- # reference accessor of the same name does.
32
- #
33
- # @param key [Symbol, String] the attribute name
34
- # @return [Object, nil] the value held in state (a scalar, a value object, a list,
35
- # or a referenced record's id); `nil` when the field is unset or not in state
36
- def [](key) = redacted(key.to_sym)
37
-
38
- # Answers the record's state as a Hash, with `:id` merged in last so it
39
- # always wins over a same-named `id` attribute (BurningManPrep::Item).
40
- #
41
- # @return [Hash{Symbol => Object}] every state field by attribute name, plus `:id`
42
- def to_h = @state.to_h { |key, _| [key, redacted(key)] }.merge(id: @id)
43
-
44
- # Names the aggregate this record belongs to, in the form every dispatch verb and
45
- # event is addressed by.
46
- #
47
- # @return [String] the fully qualified aggregate name, such as `"Pizzas::Pizza"`
48
- def fqn = "#{@domain}::#{@ir.hecks_name}"
49
-
50
- # Lists the events this one record has emitted, filtered out of the dispatcher's
51
- # whole event log on each call.
52
- #
53
- # @return [Array<Runtime::Event>] this record's events in the order the log holds
54
- # them; `[]` when it has emitted none
55
- def events
56
- @dispatcher.events.select { |event| event.aggregate == fqn && event.id == @id }
57
- end
58
-
59
- # Refreshes this handle's state from the repository, picking up writes made through
60
- # another handle or door. Keeps the current state when the record is not found.
61
- #
62
- # @return [Doors::Handle] this handle, so the call chains
63
- # @raise [Runtime::WiringError] if the aggregate's persistence bind cannot be
64
- # resolved into a repository
65
- def reload
66
- stored = repository.find(@id)
67
- @state = stored.state if stored
68
- self
69
- end
70
-
71
- # Equality is (fqn, id): two handles to the same record are equal, and
72
- # `other.is_a?(self.class)` can't tell aggregates apart since they share one class.
73
- #
74
- # @param other [Object] anything; only another `Handle` can be equal
75
- # @return [Boolean] true when `other` is a `Handle` with the same `fqn` and `id`
76
- def ==(other) = other.is_a?(Handle) && other.fqn == fqn && other.id == @id
77
- alias eql? ==
78
- def hash = [Handle, fqn, @id].hash
79
-
80
- def inspect
81
- fields = @state.map { |key, value| "#{key}=#{value.inspect}" }.join(" ")
82
- "#<#{@ir.hecks_name} #{@id} #{fields}>"
83
- end
84
- alias to_s inspect
85
-
86
- # Answers a field reader: `pizza.name` reads `name` out of state. A
87
- # declared field not yet written arrives here too, as `nil`.
88
- #
89
- # @param name [Symbol] the method called, read as an attribute or lifecycle field name
90
- # @param args [Array<Object>] ignored by a reader; passed on to `super` otherwise
91
- # @param kwargs [Hash{Symbol => Object}] ignored by a reader; passed on to `super` otherwise
92
- # @return [Object, nil] the field's value; `nil` when nothing is written yet
93
- # @raise [NoMethodError] if `name` is neither a key in state nor a declared field
94
- def method_missing(name, *args, **kwargs, &)
95
- return redacted(name) if @state.key?(name) || reader?(name)
96
-
97
- super
98
- end
99
-
100
- def respond_to_missing?(name, include_private = false)
101
- @state.key?(name) || reader?(name) || super
102
- end
103
-
104
- private
105
-
106
- def repository = @dispatcher.registry.repository(@domain, @ir)
107
-
108
- # Masks any Privacy::Marking leaf the caller isn't granted to read, and
109
- # masks an unidentified caller outright (a leak is worse than a refusal
110
- # commands allow). Nesting is masked one level deep only.
111
- def redacted(field)
112
- raw = @state[field]
113
- marked_rows(field).each do |row|
114
- raw = masked(raw, row) unless authorized_for?(row[:readable_by][:value].to_s)
115
- end
116
-
117
- raw
118
- end
119
-
120
- # The privacy markings that name `field` or a path beneath it.
121
- def marked_rows(field)
122
- marked_paths.select { |row| row[:attribute_path][:value].to_s.split(".", 2).first == field.to_s }
123
- end
124
-
125
- # `raw` with the marked leaf masked: the whole value for a marked field, one nested leaf of a
126
- # value object for a marked path beneath it.
127
- def masked(raw, row)
128
- segments = row[:attribute_path][:value].to_s.split(".", 2)
129
- return "[redacted]" if segments.size == 1
130
-
131
- raw.is_a?(Runtime::Value) ? raw.with(segments[1], "[redacted]") : raw
132
- end
133
-
134
- # Cached per handle so an unattached-Privacy domain never pays for a query
135
- # dispatch on every read.
136
- def marked_paths
137
- return @marked_paths if defined?(@marked_paths)
138
- return @marked_paths = [] unless @dispatcher.registry.bluebook("Privacy")
139
-
140
- @marked_paths = @dispatcher.query("Privacy::Marking.ForDomain", domain: fqn)
141
- end
142
-
143
- # False outright for an unidentified caller or an unattached domain, never
144
- # the weak fallback a command's own role check allows.
145
- def authorized_for?(role)
146
- caller = Runtime::Caller.current
147
- return false unless caller&.actor_id
148
- return false unless @dispatcher.registry.authorization_provider_for(@domain)
149
-
150
- Ports::Authorization.holds_role?(@dispatcher.registry, actor_id: caller.actor_id, role: role,
151
- as_of: caller.as_of, scope: caller.scope)
152
- end
153
-
154
- def reader?(name)
155
- !@ir.attribute(name).nil? || @ir.lifecycle&.field&.to_sym == name
156
- end
157
-
158
- # Real singleton methods, not method_missing: a verb named `freeze` or
159
- # `send` (both real, e.g. Account::Freeze, ExternalTransfer::Send) would
160
- # otherwise silently hit the Kernel method instead of dispatching.
161
- def define_verb_methods
162
- @ir.commands.reject(&:creates?).each do |command|
163
- define_singleton_method("#{Naming.snake(command.hecks_name)}!") do |**args|
164
- run(command, **args)
165
- end
166
- end
167
- end
168
-
169
- # `@ir.identified_by` is only the single-head shorthand (nil for a
170
- # composite identity, e.g. SafeDepositBox's branch_code/box_number);
171
- # this reads every head through `identity_heads` instead.
172
- def run(command, **args)
173
- @state = @dispatcher.dispatch("#{fqn}.#{command.hecks_name}", to: @id, with: args).instance.state
174
- self
175
- end
176
-
177
- # Lazy hop: `transfer.source_account` resolves to the actual record only
178
- # when called; nothing loads until then, so chains like
179
- # `payment.disputed_by_customer.name` stay lazy at every step.
180
- #
181
- # Defined before verb methods so a same-named verb wins the name on the
182
- # rare collision; `initialize` calls this first for that reason.
183
- #
184
- # The accessor is named like the attribute itself, never `<name>_id`
185
- # (ADR 0025) — `piece.account` and `piece[:account]` (bracket access,
186
- # the raw id) never collide despite sharing a name.
187
- def define_reference_accessors
188
- @ir.attributes.select(&:reference?).each { |attribute| define_reference_accessor(attribute) }
189
- end
190
-
191
- # One lazy reader for a reference attribute.
192
- def define_reference_accessor(attribute)
193
- target = attribute.type.resolve
194
- # Cross-domain, or otherwise unresolvable — no accessor rather than a guess.
195
- return unless target
196
-
197
- target_fqn = "#{@domain}::#{target.hecks_name}"
198
- define_singleton_method(attribute.name) do
199
- value = self[attribute.name]
200
- door = Object.const_get(target_fqn)
201
- next Array(value).map { |identity| door.find(identity) } if attribute.list?
202
-
203
- value && door.find(value)
204
- end
205
- end
206
- end
207
- end
208
- end
@@ -1,132 +0,0 @@
1
- require "json"
2
- require_relative "handle"
3
- require_relative "command_request"
4
- require_relative "../naming"
5
- require_relative "../runtime/errors"
6
- require_relative "../runtime/value"
7
-
8
- module Hecks
9
- module Doors
10
- # The JSON door: translates URL segments and parsed JSON bodies to and from the facade.
11
- # No HTTP lives here; every miss raises `Runtime::NotFound`, distinguished by message.
12
- module JsonDoor
13
- module_function
14
-
15
- # Resolves a domain name and an aggregate name, as two URL segments carry them, to
16
- # that aggregate's door.
17
- #
18
- # The name is checked against the current boot's IR before Ruby's constant table, so
19
- # a stale same-named constant from an earlier boot is never returned.
20
- #
21
- # @param dispatcher [Runtime::Dispatcher, Runtime::RemoteDispatcher] the booted dispatcher
22
- # @param domain [String, Symbol] the chapter name, such as `"Banking"`
23
- # @param name [String, Symbol] the aggregate's declared name, such as `"Customer"`
24
- # @return [Module] the aggregate door installed at `domain::name`
25
- # @raise [Runtime::NotFound] if the chapter or its aggregate is not declared
26
- # @raise [NameError] if the aggregate has no facade constant (`install_doors: false`)
27
- def aggregate(dispatcher, domain, name)
28
- ir = dispatcher.registry.bluebook(domain)&.aggregate(name)
29
- raise Runtime::NotFound, "#{domain} declares no aggregate named #{name.inspect}" unless ir
30
-
31
- Object.const_get("#{domain}::#{ir.hecks_name}")
32
- end
33
-
34
- # Names the door method that creates a record of this aggregate: the first command
35
- # it declares that `creates?`, spelled as a Ruby caller would (`"create_pizza!"`).
36
- #
37
- # @param klass [Module] an aggregate door, as `aggregate` returns
38
- # @return [String] the method name to `public_send` to the door
39
- # @raise [Runtime::NotFound] if the aggregate declares no creating command
40
- def creating_command(klass)
41
- creating = klass.ir.commands.find(&:creates?)
42
- raise Runtime::NotFound, "#{klass.ir.hecks_name} declares no creating command" unless creating
43
-
44
- "#{Naming.snake(creating.hecks_name)}!"
45
- end
46
-
47
- # Confirms that a command name arriving as text is one a `Handle` of this aggregate
48
- # answers, before a caller `public_send`s it.
49
- #
50
- # A `Handle` defines only the non-creating commands, so the creating command is
51
- # rejected here rather than failing later as a raw `NoMethodError`.
52
- #
53
- # @param klass [Module] an aggregate door, as `aggregate` returns
54
- # @param name [String, Symbol] the wanted method name with its bang, such as
55
- # `"add_topping!"`
56
- # @return [String] `name` as a String, unchanged, when a `Handle` answers it
57
- # @raise [Runtime::NotFound] if no non-creating command has that method name
58
- def validate_command!(klass, name)
59
- wanted = name.to_s
60
- dispatchable = klass.ir.commands.reject(&:creates?).map { |command| "#{Naming.snake(command.hecks_name)}!" }
61
- return wanted if dispatchable.include?(wanted)
62
-
63
- raise Runtime::NotFound, "#{klass.ir.hecks_name} declares no command named #{wanted.inspect}"
64
- end
65
-
66
- # Fetches one record by id, raising instead of answering nil on a miss.
67
- #
68
- # @param klass [Module] an aggregate door, as `aggregate` returns
69
- # @param id [String] the record's identity, as the URL carried it
70
- # @return [Doors::Handle] the record in hand
71
- # @raise [Runtime::NotFound] if the repository holds no record with that id
72
- def find!(klass, id)
73
- klass.find(id) or raise Runtime::NotFound, "no #{klass.ir.hecks_name} found for id #{id.inspect}"
74
- end
75
-
76
- # Converts every Hash key to a Symbol, at every depth of a parsed JSON body.
77
- #
78
- # @param value [Hash, Array, Object] parsed JSON: a Hash or Array is walked, any
79
- # other value is a leaf
80
- # @return [Hash{Symbol => Object}, Array, Object] a new structure of the same shape
81
- # with Symbol keys; a leaf is returned as it came
82
- def deep_symbolize(value)
83
- case value
84
- when Hash then value.to_h { |k, v| [k.to_sym, deep_symbolize(v)] }
85
- when Array then value.map { |item| deep_symbolize(item) }
86
- else value
87
- end
88
- end
89
-
90
- # Unwraps a record, a query row, or any value holding `Runtime::Value`s into plain
91
- # Hashes, Arrays and scalars a JSON encoder can walk.
92
- #
93
- # Delegates to `Runtime::Value.materialize`; the only addition is `#to_h` on a
94
- # `Handle`, which `materialize` does not recognize.
95
- #
96
- # @param value [Doors::Handle, Runtime::Value, Hash, Array, Object] what to unwrap;
97
- # a `Handle` is read through its `to_h`, so its `:id` comes along
98
- # @return [Hash, Array, Object] the same data with every `Runtime::Value` replaced
99
- # by a Hash of its fields; any other value is returned as it came
100
- def materialize(value)
101
- value = value.to_h if value.is_a?(Handle)
102
- Runtime::Value.materialize(value)
103
- end
104
-
105
- # Turns a command's JSON body, raw or already parsed, into the `to:`/`with:`
106
- # envelope a dispatcher takes. The result splats into `Dispatcher#dispatch`.
107
- #
108
- # @param body [String, Hash] raw JSON text, or the Hash it parses to
109
- # @param receiver [Symbol, nil] `:aggregate`, `:entity`, or `nil` for none
110
- # (see `CommandRequest.normalize`)
111
- # @param legacy_receiver [Symbol, String, Hash{Symbol => Symbol, String}, nil] the flat key,
112
- # or pair of keys, a body without `to` may name its receiver under; `nil` accepts none
113
- # @return [Hash{Symbol => Object}] `{ with: facts }`, plus `to:` unless `receiver` is `nil`
114
- # @raise [JSON::ParserError] if `body` is a String that is not valid JSON
115
- # @raise [Runtime::TypeMismatch] if the body is not a JSON object or its routing is malformed
116
- # @raise [ArgumentError] if `receiver` is not `nil`, `:aggregate` or `:entity`
117
- def command_request(body, receiver:, legacy_receiver: nil)
118
- input = body.is_a?(String) ? parse(body) : body
119
- CommandRequest.normalize(input, receiver: receiver, legacy_receiver: legacy_receiver)
120
- end
121
-
122
- # Parses a raw request body, leaving keys as Strings. `JSON::ParserError` propagates
123
- # unwrapped.
124
- #
125
- # @param raw_json [String] JSON text, such as a POST body
126
- # @return [Hash{String => Object}, Array, String, Numeric, Boolean, nil] whatever
127
- # the text encodes
128
- # @raise [JSON::ParserError] if the text is not valid JSON
129
- def parse(raw_json) = JSON.parse(raw_json)
130
- end
131
- end
132
- end