hecks 3.14.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. checksums.yaml +4 -4
  2. data/examples/pizzas/adapters/fake_processor.rb +34 -0
  3. data/examples/pizzas/adapters/payment_signature.rb +66 -0
  4. data/examples/pizzas/adapters/payment_webhook.rb +87 -0
  5. data/examples/pizzas/bluebook/pizzas.behaviors +33 -0
  6. data/examples/pizzas/bluebook/pizzas.hecksagon +5 -3
  7. data/examples/pizzas/config.ru +22 -0
  8. data/lib/hecks/adapters/driven/prism.rb +0 -2
  9. data/lib/hecks/adapters/driven.rb +0 -2
  10. data/lib/hecks/adapters/driving/json.rb +1 -7
  11. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -2
  12. data/lib/hecks/bluebook/dsl/derivation_words.rb +58 -0
  13. data/lib/hecks/bluebook/dsl/translation_builder.rb +6 -37
  14. data/lib/hecks/bluebook/expression/ast_json.rb +0 -9
  15. data/lib/hecks/bluebook/hecksagon.rb +1 -1
  16. data/lib/hecks/bluebook/meta_validator/translation_judge/rules.rb +7 -10
  17. data/lib/hecks/bluebook/meta_validator/translation_judge.rb +4 -8
  18. data/lib/hecks/bluebook/meta_validator.rb +0 -11
  19. data/lib/hecks/bluebook/translation.rb +18 -10
  20. data/lib/hecks/bluebook/translation_expression/evaluator.rb +63 -0
  21. data/lib/hecks/bluebook/translation_expression/parser.rb +127 -0
  22. data/lib/hecks/bluebook/translation_expression.rb +39 -0
  23. data/lib/hecks/bluebook/translation_plan.rb +70 -0
  24. data/lib/hecks/bluebook.rb +2 -0
  25. data/lib/hecks/chapters.rb +3 -3
  26. data/lib/hecks/composer/adapters/claude_composer.adapter +3 -0
  27. data/lib/hecks/composer/adapters/claude_composer.rb +83 -0
  28. data/lib/hecks/composer/adapters/fake_agent.adapter +3 -0
  29. data/lib/hecks/composer/adapters/fake_agent.rb +55 -0
  30. data/lib/hecks/composer/adapters/jev.adapter +3 -0
  31. data/lib/hecks/composer/adapters/jev.rb +66 -0
  32. data/lib/hecks/composer/agent.rb +113 -0
  33. data/lib/hecks/composer/assistant.rb +110 -0
  34. data/lib/hecks/composer/behavior_run.rb +53 -0
  35. data/lib/hecks/composer/bluebook/composer.bluebook +178 -0
  36. data/lib/hecks/composer/bluebook/composer.hecksagon +16 -0
  37. data/lib/hecks/composer/board.rb +65 -0
  38. data/lib/hecks/composer/board_edit/command_text.rb +39 -0
  39. data/lib/hecks/composer/board_edit/source.rb +84 -0
  40. data/lib/hecks/composer/board_edit.rb +144 -0
  41. data/lib/hecks/composer/check.rb +89 -0
  42. data/lib/hecks/composer/domains.rb +81 -0
  43. data/lib/hecks/composer/jobs.rb +134 -0
  44. data/lib/hecks/composer/layout.rb +37 -0
  45. data/lib/hecks/composer/live.rb +150 -0
  46. data/lib/hecks/composer/outline.rb +48 -0
  47. data/lib/hecks/composer/page.html +989 -0
  48. data/lib/hecks/composer/panels.rb +142 -0
  49. data/lib/hecks/composer/parse.rb +83 -0
  50. data/lib/hecks/composer/plain.rb +28 -0
  51. data/lib/hecks/composer/ports/composer_agent.port +7 -0
  52. data/lib/hecks/composer/session.rb +141 -0
  53. data/lib/hecks/composer/toolbox.rb +166 -0
  54. data/lib/hecks/composer/web.rb +156 -0
  55. data/lib/hecks/composer/workbench.rb +51 -0
  56. data/lib/hecks/composer/workspace.rb +81 -0
  57. data/lib/hecks/composer.rb +17 -0
  58. data/lib/hecks/corpus/adapters.rb +22 -0
  59. data/lib/hecks/corpus/rust.rb +1 -1
  60. data/lib/hecks/corpus/worlds.rb +19 -0
  61. data/lib/hecks/corpus.rb +5 -0
  62. data/lib/hecks/forms/app/commands.rb +2 -3
  63. data/lib/hecks/forms/app/queries.rb +2 -3
  64. data/lib/hecks/forms/app/records.rb +17 -5
  65. data/lib/hecks/forms/command_form_renderer.rb +42 -16
  66. data/lib/hecks/forms/field_controls.rb +2 -2
  67. data/lib/hecks/forms/field_renderer.rb +1 -1
  68. data/lib/hecks/forms/field_shape.rb +36 -17
  69. data/lib/hecks/forms/index_renderer.rb +2 -1
  70. data/lib/hecks/forms/page.rb +1 -1
  71. data/lib/hecks/forms/params.rb +0 -13
  72. data/lib/hecks/forms/query_form_renderer.rb +7 -6
  73. data/lib/hecks/forms/record_renderer.rb +11 -7
  74. data/lib/hecks/forms/rule_words.rb +100 -0
  75. data/lib/hecks/forms/value_object_shape.rb +2 -1
  76. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -3
  77. data/lib/hecks/fuzzing/isolated_boot/worlds.rb +3 -6
  78. data/lib/hecks/fuzzing/replay/filters.rb +2 -6
  79. data/lib/hecks/gate/stages.yml +25 -0
  80. data/lib/hecks/grammar/evolve/seeds.rb +0 -6
  81. data/lib/hecks/grammar/evolve.rb +0 -3
  82. data/lib/hecks/grammar/translation.bluebook +1 -1
  83. data/lib/hecks/hecks/adapters/codebase/composing.rb +56 -0
  84. data/lib/hecks/hecks/adapters/codebase/source_tree.rb +3 -1
  85. data/lib/hecks/hecks/adapters/journal_store/changes.rb +0 -26
  86. data/lib/hecks/hecks/adapters/journal_store/examination.rb +2 -23
  87. data/lib/hecks/hecks/adapters/journal_store/readings.rb +3 -11
  88. data/lib/hecks/hecks/adapters/journal_store.rb +0 -11
  89. data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +4 -11
  90. data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +0 -9
  91. data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +0 -4
  92. data/lib/hecks/hecks/codebase.behaviors +6 -0
  93. data/lib/hecks/hecks/codebase.bluebook +21 -0
  94. data/lib/hecks/hecks/composer.behaviors +68 -0
  95. data/lib/hecks/hecks/context_map.hecksagon +1 -1
  96. data/lib/hecks/hecks/custodian.behaviors +0 -6
  97. data/lib/hecks/hecks/custodian.bluebook +3 -80
  98. data/lib/hecks/hecks/hecks.hecksagon +2 -2
  99. data/lib/hecks/hecks/hecks.world +1 -1
  100. data/lib/hecks/hecks/rehearsal.bluebook +7 -13
  101. data/lib/hecks/kernel_capabilities/templates.rb +4 -4
  102. data/lib/hecks/kernel_capabilities.rb +1 -1
  103. data/lib/hecks/language/bluebook/vocabulary.bluebook +2 -3
  104. data/lib/hecks/language/translation/translation_aggregate.bluebook +21 -24
  105. data/lib/hecks/ports/access_control.rb +2 -2
  106. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -41
  107. data/lib/hecks/ports/persistence/plugins/era/lineage/coverage.rb +7 -7
  108. data/lib/hecks/ports/persistence/plugins/era/lineage/rewrite.rb +25 -2
  109. data/lib/hecks/ports/persistence/plugins/era/lineage/rules.rb +4 -4
  110. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +17 -25
  111. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/base_sql.rb +0 -12
  112. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/domain_rename.rb +1 -1
  113. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -22
  114. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/provisioning.rb +0 -1
  115. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage.rb +9 -3
  116. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +3 -29
  117. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/refusals.rb +2 -26
  118. data/lib/hecks/ports/persistence/plugins/era/translation/audit/layer_two.rb +6 -33
  119. data/lib/hecks/ports/persistence/plugins/era/translation/audit/unfed_report.rb +3 -3
  120. data/lib/hecks/ports/persistence/plugins/era/translation/audit.rb +2 -4
  121. data/lib/hecks/ports/persistence/plugins/era/translation/expression_sql.rb +52 -0
  122. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +23 -29
  123. data/lib/hecks/ports/persistence/plugins/era/translation/scaffold.rb +1 -1
  124. data/lib/hecks/ports/persistence/plugins/era/translation.rb +0 -1
  125. data/lib/hecks/ports/persistence/plugins/era.rb +3 -6
  126. data/lib/hecks/ports.rb +0 -1
  127. data/lib/hecks/projection_files.rb +3 -1
  128. data/lib/hecks/projections/deploy/templates/lambda/nat_comment_oauth.tmpl +1 -1
  129. data/lib/hecks/projections/glossary/entries.rb +10 -4
  130. data/lib/hecks/projections/glossary/groups.rb +42 -0
  131. data/lib/hecks/projections/glossary/html/page.rb +7 -2
  132. data/lib/hecks/projections/glossary/html/parser.rb +1 -1
  133. data/lib/hecks/projections/glossary/html/sections.rb +31 -11
  134. data/lib/hecks/projections/glossary/html.rb +1 -2
  135. data/lib/hecks/projections/glossary/markdown.rb +31 -28
  136. data/lib/hecks/projections/glossary/mermaid.rb +3 -0
  137. data/lib/hecks/projections/glossary/page.css +143 -153
  138. data/lib/hecks/projections/glossary/page.js +63 -3
  139. data/lib/hecks/projections/glossary/sentences/actions.rb +88 -0
  140. data/lib/hecks/projections/glossary/sentences/paths.rb +77 -0
  141. data/lib/hecks/projections/glossary/sentences/queries.rb +90 -0
  142. data/lib/hecks/projections/glossary/sentences/value_objects.rb +32 -5
  143. data/lib/hecks/projections/glossary/sentences.rb +13 -3
  144. data/lib/hecks/projections/glossary/slugs.rb +4 -1
  145. data/lib/hecks/projections/glossary.rb +2 -1
  146. data/lib/hecks/projections/plain_english.rb +97 -0
  147. data/lib/hecks/projections/rust_vocabulary/arguments.rb +2 -2
  148. data/lib/hecks/projections/rust_vocabulary/enum_body.rb +20 -25
  149. data/lib/hecks/projections/rust_vocabulary/tests.rb +2 -2
  150. data/lib/hecks/projections/rust_vocabulary.rb +14 -6
  151. data/lib/hecks/projections/site/cms_editor/color.rb +0 -6
  152. data/lib/hecks/projections/statements/actions.rb +99 -0
  153. data/lib/hecks/projections/statements/relationships.rb +86 -0
  154. data/lib/hecks/projections/statements.rb +15 -24
  155. data/lib/hecks/projector/exporter/adapters.rb +112 -0
  156. data/lib/hecks/projector/exporter/translations.rb +18 -35
  157. data/lib/hecks/projector/exporter/worlds.rb +103 -0
  158. data/lib/hecks/projector/exporter.rb +5 -1
  159. data/lib/hecks/quality_control/adapters/agent.rb +6 -7
  160. data/lib/hecks/quality_control/adapters/agent_profile.rb +16 -114
  161. data/lib/hecks/quality_control/cli/qa_mine_combinations.rb +1 -1
  162. data/lib/hecks/runtime/loader/boot_steps.rb +0 -20
  163. data/lib/hecks/runtime/outbox.rb +2 -9
  164. data/lib/hecks/runtime/port_operation_interpreter.rb +1 -9
  165. data/lib/hecks/runtime/remote_dispatcher.rb +2 -2
  166. data/lib/hecks/runtime/value.rb +13 -0
  167. data/lib/hecks/rust_build/project_rust/target_ir.rb +23 -4
  168. data/lib/hecks/rust_build/wasm.rb +0 -12
  169. data/lib/hecks/rust_build/write_if_changed.rb +12 -12
  170. data/lib/hecks/rust_format.rb +77 -0
  171. data/lib/hecks/tools/fuzz_sweep/findings.rb +3 -9
  172. data/lib/hecks/tools/fuzz_sweep.rb +1 -3
  173. data/lib/hecks/tools/translation_audit/verdicts.rb +3 -28
  174. data/lib/hecks/tools/translation_audit.rb +8 -10
  175. data/lib/hecks/tools/translation_scaffold/reporting.rb +1 -1
  176. data/lib/hecks/version.rb +1 -1
  177. data/lib/hecks/vocabulary.rb +2 -1
  178. data/lib/hecks.rb +3 -0
  179. data/prebuilt/{a883a7e568667aa5ac904c82daff356bdf9e996252a36af6def4a4a43efb967e.marshal → 02c09c73b272c1fb5a9ae6d87bb5e5e529208e00f6b4b597dc2c0fd70a9c0964.marshal} +0 -0
  180. data/prebuilt/{1a6f596be124228be93eba1d86dbc9d2933190fa389b284331241cb9f27115a0.marshal → bb84a835cf6a15136bad5ca03aa5bf93a5b92f5f72444618d3ef2412b987fb32.marshal} +0 -0
  181. data/prebuilt/{verdicts-872993b1aa26d73fe48f28251374b8585fed145d02aab42be0f00de386b6ff80.json → verdicts-f1f10da14c8e8f01b4c9b7e3e2d74be8cd2bf4676364ed17fa16975d2a076d75.json} +1 -1
  182. data/rust/Cargo.toml +40 -0
  183. data/rust/build/Cargo.toml +36 -0
  184. data/rust/build/clippy.toml +4 -0
  185. data/rust/build/src/adapters_pass.rs +196 -0
  186. data/rust/build/src/binding_pass.rs +54 -13
  187. data/rust/build/src/build_artifact.rs +23 -5
  188. data/rust/build/src/cargo_sync.rs +69 -26
  189. data/rust/build/src/lineage_pass.rs +101 -87
  190. data/rust/build/src/main.rs +11 -2
  191. data/rust/build/src/pipeline.rs +80 -19
  192. data/rust/build/src/reserved_names.rs +6 -50
  193. data/rust/build/src/resolve.rs +82 -35
  194. data/rust/build/src/rustfmt.rs +100 -0
  195. data/rust/build/src/seams_pass.rs +85 -16
  196. data/rust/build/src/subprocess.rs +30 -6
  197. data/rust/build/src/tmp.rs +5 -2
  198. data/rust/build/src/translation_pass.rs +72 -22
  199. data/rust/build/src/world_pass.rs +109 -0
  200. data/rust/clippy.toml +4 -0
  201. data/rust/codegen/Cargo.toml +36 -0
  202. data/rust/codegen/clippy.toml +4 -0
  203. data/rust/codegen/src/asks.rs +40 -14
  204. data/rust/codegen/src/attr.rs +2 -2
  205. data/rust/codegen/src/bridging.rs +676 -117
  206. data/rust/codegen/src/commands.rs +979 -373
  207. data/rust/codegen/src/constraints.rs +88 -25
  208. data/rust/codegen/src/dependency_planning.rs +128 -34
  209. data/rust/codegen/src/domain_generator.rs +300 -170
  210. data/rust/codegen/src/exemplar.rs +87 -32
  211. data/rust/codegen/src/expr_emitter.rs +54 -6
  212. data/rust/codegen/src/fielded.rs +193 -41
  213. data/rust/codegen/src/hecks_naming.rs +124 -16
  214. data/rust/codegen/src/hecks_naming_tests.rs +81 -0
  215. data/rust/codegen/src/json.rs +80 -44
  216. data/rust/codegen/src/json_codec.rs +585 -149
  217. data/rust/codegen/src/literal.rs +104 -138
  218. data/rust/codegen/src/main.rs +145 -52
  219. data/rust/codegen/src/manifest.rs +76 -14
  220. data/rust/codegen/src/mutations.rs +1212 -239
  221. data/rust/codegen/src/naming.rs +111 -60
  222. data/rust/codegen/src/optional_pass.rs +90 -28
  223. data/rust/codegen/src/ports.rs +5 -4
  224. data/rust/codegen/src/prelude.rs +139 -49
  225. data/rust/codegen/src/queries.rs +601 -226
  226. data/rust/codegen/src/reactions.rs +268 -74
  227. data/rust/codegen/src/read_models.rs +606 -205
  228. data/rust/codegen/src/reference_specs.rs +16 -3
  229. data/rust/codegen/src/registry.rs +128 -79
  230. data/rust/codegen/src/reserved_names.rs +6 -50
  231. data/rust/codegen/src/shared.rs +71 -22
  232. data/rust/codegen/src/sidecars.rs +15 -5
  233. data/rust/codegen/src/skip_reason.rs +8 -2
  234. data/rust/codegen/src/types.rs +306 -108
  235. data/rust/host/Cargo.lock +1 -0
  236. data/rust/host/Cargo.toml +40 -4
  237. data/rust/host/HECKS_RELEASE +1 -1
  238. data/rust/host/clippy.toml +4 -0
  239. data/rust/host/src/api.rs +698 -218
  240. data/rust/host/src/auth.rs +491 -214
  241. data/rust/host/src/b64.rs +91 -0
  242. data/rust/host/src/bin/expr_harness.rs +6 -2
  243. data/rust/host/src/bin/lineage_harness.rs +47 -11
  244. data/rust/host/src/bin/mint_harness.rs +70 -37
  245. data/rust/host/src/bin/validate_harness.rs +16 -8
  246. data/rust/host/src/boot.rs +144 -68
  247. data/rust/host/src/boundary_fuzz/api.rs +130 -32
  248. data/rust/host/src/boundary_fuzz/auth.rs +108 -23
  249. data/rust/host/src/boundary_fuzz/expr_json.rs +85 -11
  250. data/rust/host/src/boundary_fuzz/ir.rs +164 -12
  251. data/rust/host/src/boundary_fuzz/server.rs +283 -36
  252. data/rust/host/src/boundary_fuzz/web.rs +56 -8
  253. data/rust/host/src/checkout.rs +78 -32
  254. data/rust/host/src/clock.rs +8 -0
  255. data/rust/host/src/commerce_ir.rs +333 -95
  256. data/rust/host/src/crypto.rs +33 -0
  257. data/rust/host/src/dispatch.rs +990 -331
  258. data/rust/host/src/expr_json.rs +1021 -273
  259. data/rust/host/src/extension.rs +24 -4
  260. data/rust/host/src/field_hints.rs +11 -7
  261. data/rust/host/src/fuzz_support.rs +85 -13
  262. data/rust/host/src/host.rs +100 -0
  263. data/rust/host/src/http.rs +22 -0
  264. data/rust/host/src/ir.rs +326 -16
  265. data/rust/host/src/journal.rs +277 -176
  266. data/rust/host/src/lambda_client.rs +219 -53
  267. data/rust/host/src/lib.rs +16 -4
  268. data/rust/host/src/log.rs +62 -11
  269. data/rust/host/src/mint.rs +1153 -326
  270. data/rust/host/src/needs.rs +185 -44
  271. data/rust/host/src/payments/keystore.rs +197 -45
  272. data/rust/host/src/payments/roles.rs +39 -7
  273. data/rust/host/src/payments/tests.rs +1427 -282
  274. data/rust/host/src/payments.rs +391 -112
  275. data/rust/host/src/platform_signin/attack_tests.rs +5 -5
  276. data/rust/host/src/platform_signin/config.rs +11 -11
  277. data/rust/host/src/platform_signin/cookie.rs +2 -2
  278. data/rust/host/src/platform_signin/extension.rs +2 -1
  279. data/rust/host/src/platform_signin/handler.rs +9 -8
  280. data/rust/host/src/platform_signin/jti.rs +4 -2
  281. data/rust/host/src/platform_signin/jwks.rs +2 -1
  282. data/rust/host/src/platform_signin/jws.rs +2 -37
  283. data/rust/host/src/platform_signin/known_answer_tests.rs +5 -1
  284. data/rust/host/src/platform_signin/mode_tests.rs +5 -3
  285. data/rust/host/src/platform_signin/pages.rs +20 -20
  286. data/rust/host/src/platform_signin/session.rs +8 -1
  287. data/rust/host/src/platform_signin/settings.rs +6 -6
  288. data/rust/host/src/platform_signin/strict_json.rs +12 -9
  289. data/rust/host/src/platform_signin/verify.rs +41 -41
  290. data/rust/host/src/platform_signin/wiring_support.rs +13 -18
  291. data/rust/host/src/presentation.rs +207 -63
  292. data/rust/host/src/presentation_write.rs +502 -187
  293. data/rust/host/src/query_step.rs +69 -18
  294. data/rust/host/src/rate_limit.rs +541 -142
  295. data/rust/host/src/reference_transform.rs +77 -25
  296. data/rust/host/src/reference_validate.rs +443 -104
  297. data/rust/host/src/resend.rs +190 -55
  298. data/rust/host/src/secrets.rs +116 -21
  299. data/rust/host/src/server.rs +483 -104
  300. data/rust/host/src/storage_shape.rs +88 -21
  301. data/rust/host/src/test_pg.rs +17 -7
  302. data/rust/host/src/ui_schema.rs +495 -207
  303. data/rust/host/src/wasm_runner.rs +21 -6
  304. data/rust/host/src/web/commerce.rs +101 -23
  305. data/rust/host/src/web/newsletter/cooldown.rs +10 -3
  306. data/rust/host/src/web/newsletter/template.rs +26 -7
  307. data/rust/host/src/web/newsletter/tests.rs +112 -23
  308. data/rust/host/src/web/newsletter.rs +209 -59
  309. data/rust/host/src/web/newsletter_send.rs +251 -79
  310. data/rust/host/src/web/registration_receipt/tests.rs +16 -4
  311. data/rust/host/src/web/registration_receipt.rs +100 -24
  312. data/rust/host/src/web/registrations.rs +1384 -332
  313. data/rust/host/src/web/resend_webhook.rs +138 -80
  314. data/rust/host/src/web.rs +2416 -508
  315. data/rust/lsp/Cargo.toml +36 -0
  316. data/rust/lsp/clippy.toml +4 -0
  317. data/rust/lsp/src/json.rs +29 -22
  318. data/rust/lsp/src/main.rs +81 -31
  319. data/rust/lsp/src/outline.rs +23 -7
  320. data/rust/lsp/src/rpc.rs +17 -4
  321. data/rust/parser/Cargo.toml +36 -0
  322. data/rust/parser/clippy.toml +4 -0
  323. data/rust/parser/src/build/closed_sets.rs +6 -1
  324. data/rust/parser/src/build/identity.rs +4 -3
  325. data/rust/parser/src/build/mod.rs +1 -1
  326. data/rust/parser/src/build/named_pattern.rs +4 -1
  327. data/rust/parser/src/build/naming.rs +2 -80
  328. data/rust/parser/src/build/pattern_subset.rs +6 -8
  329. data/rust/parser/src/build/query_derive.rs +3 -1
  330. data/rust/parser/src/build/query_inference.rs +14 -5
  331. data/rust/parser/src/build/read_model.rs +4 -1
  332. data/rust/parser/src/build/references.rs +4 -3
  333. data/rust/parser/src/canonical.rs +34 -15
  334. data/rust/parser/src/emit.rs +187 -105
  335. data/rust/parser/src/expr/ast_json.rs +134 -42
  336. data/rust/parser/src/expr/evaluator.rs +27 -9
  337. data/rust/parser/src/expr/mod.rs +54 -13
  338. data/rust/parser/src/expr/resolver.rs +299 -92
  339. data/rust/parser/src/ir.rs +35 -3
  340. data/rust/parser/src/keywords.rs +7168 -516
  341. data/rust/parser/src/lex.rs +11 -25
  342. data/rust/parser/src/lib.rs +1 -0
  343. data/rust/parser/src/main.rs +65 -6
  344. data/rust/parser/src/parse/adapter.rs +157 -0
  345. data/rust/parser/src/parse/aggregate.rs +19 -10
  346. data/rust/parser/src/parse/chapter.rs +16 -17
  347. data/rust/parser/src/parse/command.rs +35 -16
  348. data/rust/parser/src/parse/domain_port.rs +4 -7
  349. data/rust/parser/src/parse/entity.rs +21 -11
  350. data/rust/parser/src/parse/hecksagon.rs +10 -4
  351. data/rust/parser/src/parse/lifecycle.rs +9 -5
  352. data/rust/parser/src/parse/mod.rs +22 -91
  353. data/rust/parser/src/parse/needs.rs +21 -12
  354. data/rust/parser/src/parse/policy.rs +2 -2
  355. data/rust/parser/src/parse/process_manager.rs +9 -11
  356. data/rust/parser/src/parse/query.rs +8 -4
  357. data/rust/parser/src/parse/read_model.rs +57 -16
  358. data/rust/parser/src/parse/value_object.rs +7 -2
  359. data/rust/parser/src/parse/world.rs +322 -0
  360. data/rust/parser/src/ruby_value.rs +7 -6
  361. data/rust/src/exemplar/json.rs +4 -4
  362. data/rust/src/exemplar/mutations.rs +18 -0
  363. data/rust/src/exemplar/read_models.rs +1 -10
  364. data/rust/src/kernel/attribute_shapes/composite.rs +54 -14
  365. data/rust/src/kernel/attribute_shapes/list.rs +2 -2
  366. data/rust/src/kernel/attribute_shapes/mod.rs +8 -8
  367. data/rust/src/kernel/attribute_shapes/scalar.rs +2 -2
  368. data/rust/src/kernel/cli.rs +229 -112
  369. data/rust/src/kernel/dispatch.rs +323 -115
  370. data/rust/src/kernel/dispatch_guard_tests.rs +327 -65
  371. data/rust/src/kernel/expr.rs +203 -86
  372. data/rust/src/kernel/expression_operators/arithmetic.rs +138 -25
  373. data/rust/src/kernel/expression_operators/comparison.rs +87 -2
  374. data/rust/src/kernel/expression_operators/enumeration.rs +106 -22
  375. data/rust/src/kernel/expression_operators/logical.rs +9 -3
  376. data/rust/src/kernel/expression_operators/membership.rs +3 -1
  377. data/rust/src/kernel/expression_operators/mod.rs +34 -21
  378. data/rust/src/kernel/expression_operators/pattern_match.rs +46 -11
  379. data/rust/src/kernel/expression_operators/positional.rs +84 -30
  380. data/rust/src/kernel/expression_operators/presence.rs +52 -13
  381. data/rust/src/kernel/expression_operators/sign_test.rs +3 -1
  382. data/rust/src/kernel/expression_operators/sized.rs +24 -8
  383. data/rust/src/kernel/expression_operators/text.rs +120 -27
  384. data/rust/src/kernel/expression_operators/to_string.rs +10 -4
  385. data/rust/src/kernel/json.rs +230 -148
  386. data/rust/src/kernel/mod.rs +46 -33
  387. data/rust/src/kernel/named_query.rs +63 -15
  388. data/rust/src/kernel/needs.rs +112 -25
  389. data/rust/src/kernel/orchestrate.rs +980 -581
  390. data/rust/src/kernel/orchestrate_guard_tests.rs +538 -140
  391. data/rust/src/kernel/pattern.rs +107 -34
  392. data/rust/src/kernel/query_comparators.rs +118 -41
  393. data/rust/src/kernel/query_ordering.rs +13 -5
  394. data/rust/src/kernel/read_model.rs +479 -181
  395. data/rust/src/kernel/reference_lookup.rs +122 -26
  396. data/rust/src/kernel/repository.rs +293 -66
  397. data/rust/src/kernel/routing.rs +58 -42
  398. data/rust/src/kernel/ruby_float.rs +139 -0
  399. data/rust/src/kernel/vocab/aggregate_dispatch_order.rs +57 -57
  400. data/rust/src/kernel/vocab/entity_dispatch_order.rs +54 -54
  401. data/rust/src/kernel/vocab/field_hint.rs +19 -25
  402. data/rust/src/kernel/vocab/mod.rs +6 -6
  403. data/rust/src/kernel/vocab/query_comparator.rs +23 -24
  404. data/rust/src/kernel/vocab/refusal_template.rs +501 -297
  405. data/rust/src/lib.rs +50 -0
  406. data/rust/src/main.rs +4 -1
  407. data/rust/web/Cargo.toml +36 -0
  408. data/rust/web/clippy.toml +4 -0
  409. metadata +82 -15
  410. data/examples/pizzas/bluebook/hecksagon/mock_stripe_payment_adapter.rb +0 -36
  411. data/lib/hecks/adapters/driven/google_authentication.adapter +0 -3
  412. data/lib/hecks/adapters/driven/google_authentication.rb +0 -99
  413. data/lib/hecks/adapters/driven/mock_stripe_adapter.adapter +0 -3
  414. data/lib/hecks/adapters/driven/mock_stripe_adapter.rb +0 -32
  415. data/lib/hecks/ports/authentication.port +0 -6
  416. data/lib/hecks/ports/authentication.rb +0 -67
  417. data/lib/hecks/ports/persistence/plugins/era/translation/approval_file.rb +0 -255
  418. data/lib/hecks/ports/persistence/plugins/era/translation/audit/approval_digest.rb +0 -22
  419. data/rust/host/src/approval.rs +0 -652
@@ -63,11 +63,20 @@ impl Clock for ProcessClock {
63
63
 
64
64
  /// The command's declared needs, each with the type of the attribute of the same name.
65
65
  fn declared_needs<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, Option<&'a str>)> {
66
- let Some((qualified_aggregate, command_name)) = verb.rsplit_once('.') else { return Vec::new() };
67
- let aggregate = qualified_aggregate.rsplit("::").next().unwrap_or(qualified_aggregate);
66
+ let Some((qualified_aggregate, command_name)) = verb.rsplit_once('.') else {
67
+ return Vec::new();
68
+ };
69
+ let aggregate = qualified_aggregate
70
+ .rsplit("::")
71
+ .next()
72
+ .unwrap_or(qualified_aggregate);
68
73
  let named = |value: &Value, name: &str| value.get("name").and_then(Value::as_str) == Some(name);
69
74
  let list = |value: &'a Value, key: &str| -> Vec<&'a Value> {
70
- value.get(key).and_then(Value::as_array).map(|a| a.iter().collect()).unwrap_or_default()
75
+ value
76
+ .get(key)
77
+ .and_then(Value::as_array)
78
+ .map(|a| a.iter().collect())
79
+ .unwrap_or_default()
71
80
  };
72
81
  let mut needs = Vec::new();
73
82
  for command in list(domain_ir, "aggregates")
@@ -77,9 +86,15 @@ fn declared_needs<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, Option<
77
86
  .filter(|c| named(c, command_name))
78
87
  {
79
88
  let attributes = list(command, "attributes");
80
- for fact in list(command, "needs").into_iter().filter_map(|n| n.get("fact").and_then(Value::as_str)) {
81
- let attribute_type =
82
- attributes.iter().find(|a| named(a, fact)).and_then(|a| a.get("type")).and_then(Value::as_str);
89
+ for fact in list(command, "needs")
90
+ .into_iter()
91
+ .filter_map(|n| n.get("fact").and_then(Value::as_str))
92
+ {
93
+ let attribute_type = attributes
94
+ .iter()
95
+ .find(|a| named(a, fact))
96
+ .and_then(|a| a.get("type"))
97
+ .and_then(Value::as_str);
83
98
  needs.push((fact, attribute_type));
84
99
  }
85
100
  }
@@ -90,13 +105,31 @@ fn declared_needs<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, Option<
90
105
  /// The kernel answers a reaction's command from this (it has no IR of its own), and the same
91
106
  /// step's `occurred_at` keeps the answer the same on replay.
92
107
  pub fn table(domain_ir: &Value) -> Value {
93
- let domain = domain_ir.get("name").and_then(Value::as_str).unwrap_or_default();
108
+ let domain = domain_ir
109
+ .get("name")
110
+ .and_then(Value::as_str)
111
+ .unwrap_or_default();
94
112
  let mut verbs = serde_json::Map::new();
95
- let aggregates = domain_ir.get("aggregates").and_then(Value::as_array).into_iter().flatten();
113
+ let aggregates = domain_ir
114
+ .get("aggregates")
115
+ .and_then(Value::as_array)
116
+ .into_iter()
117
+ .flatten();
96
118
  for aggregate in aggregates {
97
- let aggregate_name = aggregate.get("name").and_then(Value::as_str).unwrap_or_default();
98
- for command in aggregate.get("commands").and_then(Value::as_array).into_iter().flatten() {
99
- let command_name = command.get("name").and_then(Value::as_str).unwrap_or_default();
119
+ let aggregate_name = aggregate
120
+ .get("name")
121
+ .and_then(Value::as_str)
122
+ .unwrap_or_default();
123
+ for command in aggregate
124
+ .get("commands")
125
+ .and_then(Value::as_array)
126
+ .into_iter()
127
+ .flatten()
128
+ {
129
+ let command_name = command
130
+ .get("name")
131
+ .and_then(Value::as_str)
132
+ .unwrap_or_default();
100
133
  let verb = format!("{domain}::{aggregate_name}.{command_name}");
101
134
  let needs: Vec<Value> = declared_needs(domain_ir, &verb)
102
135
  .into_iter()
@@ -111,7 +144,10 @@ pub fn table(domain_ir: &Value) -> Value {
111
144
  }
112
145
 
113
146
  fn list<'a>(value: &'a Value, key: &str) -> &'a [Value] {
114
- value.get(key).and_then(Value::as_array).map(Vec::as_slice).unwrap_or(&[])
147
+ value
148
+ .get(key)
149
+ .and_then(Value::as_array)
150
+ .map_or(&[][..], Vec::as_slice)
115
151
  }
116
152
 
117
153
  fn named(value: &Value, name: &str) -> bool {
@@ -125,17 +161,23 @@ fn command_for<'a>(domain_ir: &'a Value, verb: &str) -> Option<&'a Value> {
125
161
  let aggregate = segments.next()?.rsplit("::").next()?;
126
162
  let mut path: Vec<&str> = segments.collect();
127
163
  let command_name = path.pop()?;
128
- let mut node = list(domain_ir, "aggregates").iter().find(|a| named(a, aggregate))?;
164
+ let mut node = list(domain_ir, "aggregates")
165
+ .iter()
166
+ .find(|a| named(a, aggregate))?;
129
167
  for entity in path {
130
168
  node = list(node, "entities").iter().find(|e| named(e, entity))?;
131
169
  }
132
- list(node, "commands").iter().find(|c| named(c, command_name))
170
+ list(node, "commands")
171
+ .iter()
172
+ .find(|c| named(c, command_name))
133
173
  }
134
174
 
135
175
  /// The command's attributes that declare a default (`attribute :runs, Count, default: 30`), each
136
176
  /// with the declared value. An attribute whose default is null declares none.
137
177
  fn declared_defaults<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, &'a Value)> {
138
- let Some(command) = command_for(domain_ir, verb) else { return Vec::new() };
178
+ let Some(command) = command_for(domain_ir, verb) else {
179
+ return Vec::new();
180
+ };
139
181
  list(command, "attributes")
140
182
  .iter()
141
183
  .filter_map(|a| {
@@ -149,28 +191,52 @@ fn declared_defaults<'a>(domain_ir: &'a Value, verb: &str) -> Vec<(&'a str, &'a
149
191
  /// `{ verb: { attribute: value } }`, entity commands included. The kernel fills a reaction's
150
192
  /// command from this the same way the host fills the outermost one.
151
193
  pub fn defaults_table(domain_ir: &Value) -> Value {
152
- let domain = domain_ir.get("name").and_then(Value::as_str).unwrap_or_default();
194
+ let domain = domain_ir
195
+ .get("name")
196
+ .and_then(Value::as_str)
197
+ .unwrap_or_default();
153
198
  let mut verbs = serde_json::Map::new();
154
199
  for aggregate in list(domain_ir, "aggregates") {
155
- let aggregate_name = aggregate.get("name").and_then(Value::as_str).unwrap_or_default();
156
- collect_defaults(domain_ir, aggregate, &format!("{domain}::{aggregate_name}"), &mut verbs);
200
+ let aggregate_name = aggregate
201
+ .get("name")
202
+ .and_then(Value::as_str)
203
+ .unwrap_or_default();
204
+ collect_defaults(
205
+ domain_ir,
206
+ aggregate,
207
+ &format!("{domain}::{aggregate_name}"),
208
+ &mut verbs,
209
+ );
157
210
  }
158
211
  Value::Object(verbs)
159
212
  }
160
213
 
161
214
  /// Adds the defaults of `node`'s commands, then of each entity nested in it, under `prefix`.
162
- fn collect_defaults(domain_ir: &Value, node: &Value, prefix: &str, verbs: &mut serde_json::Map<String, Value>) {
215
+ fn collect_defaults(
216
+ domain_ir: &Value,
217
+ node: &Value,
218
+ prefix: &str,
219
+ verbs: &mut serde_json::Map<String, Value>,
220
+ ) {
163
221
  for command in list(node, "commands") {
164
- let command_name = command.get("name").and_then(Value::as_str).unwrap_or_default();
222
+ let command_name = command
223
+ .get("name")
224
+ .and_then(Value::as_str)
225
+ .unwrap_or_default();
165
226
  let verb = format!("{prefix}.{command_name}");
166
- let defaults: serde_json::Map<String, Value> =
167
- declared_defaults(domain_ir, &verb).into_iter().map(|(name, value)| (name.to_string(), value.clone())).collect();
227
+ let defaults: serde_json::Map<String, Value> = declared_defaults(domain_ir, &verb)
228
+ .into_iter()
229
+ .map(|(name, value)| (name.to_string(), value.clone()))
230
+ .collect();
168
231
  if !defaults.is_empty() {
169
232
  verbs.insert(verb, Value::Object(defaults));
170
233
  }
171
234
  }
172
235
  for entity in list(node, "entities") {
173
- let entity_name = entity.get("name").and_then(Value::as_str).unwrap_or_default();
236
+ let entity_name = entity
237
+ .get("name")
238
+ .and_then(Value::as_str)
239
+ .unwrap_or_default();
174
240
  collect_defaults(domain_ir, entity, &format!("{prefix}.{entity_name}"), verbs);
175
241
  }
176
242
  }
@@ -189,8 +255,16 @@ fn answer(fact: &str, attribute_type: Option<&str>, clock: &dyn Clock) -> Option
189
255
  // `now` is epoch seconds; `today` the day those fall in, whole days since the epoch (UTC).
190
256
  "now" | "today" => {
191
257
  let secs = clock.now_secs();
192
- let answer = if fact == "today" { secs.div_euclid(86_400) } else { secs };
193
- Some(if attribute_type == Some("Integer") { json!(answer) } else { json!({ "value": answer }) })
258
+ let answer = if fact == "today" {
259
+ secs.div_euclid(86_400)
260
+ } else {
261
+ secs
262
+ };
263
+ Some(if attribute_type == Some("Integer") {
264
+ json!(answer)
265
+ } else {
266
+ json!({ "value": answer })
267
+ })
194
268
  }
195
269
  _ => None,
196
270
  }
@@ -216,9 +290,17 @@ pub fn fill_needs(domain_ir: &Value, verb: &str, args: &mut Value, clock: &dyn C
216
290
 
217
291
  /// The facts the query `question` (`Domain::Aggregate.Query`) needs, each with the type of the
218
292
  /// argument of the same name. An entity's query is not looked up here.
219
- fn declared_query_needs<'a>(domain_ir: &'a Value, question: &str) -> Vec<(&'a str, Option<&'a str>)> {
220
- let Some((qualified_aggregate, query_name)) = question.rsplit_once('.') else { return Vec::new() };
221
- let aggregate = qualified_aggregate.rsplit("::").next().unwrap_or(qualified_aggregate);
293
+ fn declared_query_needs<'a>(
294
+ domain_ir: &'a Value,
295
+ question: &str,
296
+ ) -> Vec<(&'a str, Option<&'a str>)> {
297
+ let Some((qualified_aggregate, query_name)) = question.rsplit_once('.') else {
298
+ return Vec::new();
299
+ };
300
+ let aggregate = qualified_aggregate
301
+ .rsplit("::")
302
+ .next()
303
+ .unwrap_or(qualified_aggregate);
222
304
  let Some(query) = list(domain_ir, "aggregates")
223
305
  .iter()
224
306
  .find(|a| named(a, aggregate))
@@ -231,8 +313,11 @@ fn declared_query_needs<'a>(domain_ir: &'a Value, question: &str) -> Vec<(&'a st
231
313
  .iter()
232
314
  .filter_map(|n| n.get("fact").and_then(Value::as_str))
233
315
  .map(|fact| {
234
- let attribute_type =
235
- attributes.iter().find(|a| named(a, fact)).and_then(|a| a.get("type")).and_then(Value::as_str);
316
+ let attribute_type = attributes
317
+ .iter()
318
+ .find(|a| named(a, fact))
319
+ .and_then(|a| a.get("type"))
320
+ .and_then(Value::as_str);
236
321
  (fact, attribute_type)
237
322
  })
238
323
  .collect()
@@ -246,7 +331,9 @@ pub fn fill_query_needs(domain_ir: &Value, question: &str, args: &mut Value, clo
246
331
  if needs.is_empty() {
247
332
  return;
248
333
  }
249
- let Some(facts) = args.as_object_mut() else { return };
334
+ let Some(facts) = args.as_object_mut() else {
335
+ return;
336
+ };
250
337
  for (fact, attribute_type) in needs {
251
338
  if facts.contains_key(fact) {
252
339
  continue;
@@ -297,9 +384,19 @@ mod tests {
297
384
  .unwrap()
298
385
  .iter()
299
386
  .flat_map(|a| {
300
- a["commands"].as_array().unwrap().iter().filter(|c| !c["needs"].as_array().unwrap().is_empty()).map(
301
- move |c| format!("{}::{}.{}", ir["name"].as_str().unwrap(), a["name"].as_str().unwrap(), c["name"].as_str().unwrap()),
302
- )
387
+ a["commands"]
388
+ .as_array()
389
+ .unwrap()
390
+ .iter()
391
+ .filter(|c| !c["needs"].as_array().unwrap().is_empty())
392
+ .map(move |c| {
393
+ format!(
394
+ "{}::{}.{}",
395
+ ir["name"].as_str().unwrap(),
396
+ a["name"].as_str().unwrap(),
397
+ c["name"].as_str().unwrap()
398
+ )
399
+ })
303
400
  })
304
401
  .next()
305
402
  .expect("the lease_clock domain declares a command that needs :now")
@@ -309,7 +406,12 @@ mod tests {
309
406
  fn a_needed_fact_the_caller_left_out_is_filled_from_the_clock() {
310
407
  let ir = lease_ir();
311
408
  let mut args = json!({"with": {"holder": "a"}});
312
- fill_needs(&ir, &first_needing(&ir), &mut args, &FixedClock(1_700_000_000));
409
+ fill_needs(
410
+ &ir,
411
+ &first_needing(&ir),
412
+ &mut args,
413
+ &FixedClock(1_700_000_000),
414
+ );
313
415
  assert_eq!(args["with"]["now"], json!({"value": 1_700_000_000}));
314
416
  assert_eq!(args["with"]["holder"], "a");
315
417
  }
@@ -355,7 +457,10 @@ mod tests {
355
457
  #[test]
356
458
  fn the_kernel_table_lists_only_commands_that_need_something() {
357
459
  let table = table(&integer_ir());
358
- assert_eq!(table, json!({"Qc::Check.Run": [{"fact": "now", "type": "Integer"}]}));
460
+ assert_eq!(
461
+ table,
462
+ json!({"Qc::Check.Run": [{"fact": "now", "type": "Integer"}]})
463
+ );
359
464
  assert_eq!(super::table(&json!({"name": "D"})), json!({}));
360
465
  }
361
466
 
@@ -383,7 +488,10 @@ mod tests {
383
488
  let mut args = json!({"with": {"ref": {"value": "a"}}});
384
489
  fill_defaults(&defaulted_ir(), "Qc::Check.Start", &mut args);
385
490
  assert_eq!(args["with"]["runs"], 30);
386
- assert!(args["with"].get("note").is_none(), "a null default declares none");
491
+ assert!(
492
+ args["with"].get("note").is_none(),
493
+ "a null default declares none"
494
+ );
387
495
  }
388
496
 
389
497
  #[test]
@@ -412,7 +520,10 @@ mod tests {
412
520
 
413
521
  #[test]
414
522
  fn the_kernel_table_lists_each_command_that_declares_a_default() {
415
- assert_eq!(defaults_table(&defaulted_ir()), json!({"Qc::Check.Start": {"runs": 30}}));
523
+ assert_eq!(
524
+ defaults_table(&defaulted_ir()),
525
+ json!({"Qc::Check.Start": {"runs": 30}})
526
+ );
416
527
  assert_eq!(defaults_table(&integer_ir()), json!({}));
417
528
  }
418
529
 
@@ -467,17 +578,32 @@ mod tests {
467
578
  #[test]
468
579
  fn a_needed_query_fact_the_caller_left_out_is_filled_from_the_clock() {
469
580
  let mut args = json!({});
470
- fill_query_needs(&query_ir(), "Lease::Slot.Expired", &mut args, &FixedClock(1_700_000_000));
581
+ fill_query_needs(
582
+ &query_ir(),
583
+ "Lease::Slot.Expired",
584
+ &mut args,
585
+ &FixedClock(1_700_000_000),
586
+ );
471
587
  assert_eq!(args["now"], json!({"value": 1_700_000_000}));
472
588
  }
473
589
 
474
590
  #[test]
475
591
  fn a_query_value_the_caller_supplied_is_kept_even_when_null() {
476
592
  let mut args = json!({"now": null});
477
- fill_query_needs(&query_ir(), "Lease::Slot.Expired", &mut args, &FixedClock(5));
593
+ fill_query_needs(
594
+ &query_ir(),
595
+ "Lease::Slot.Expired",
596
+ &mut args,
597
+ &FixedClock(5),
598
+ );
478
599
  assert!(args["now"].is_null());
479
600
  let mut named = json!({"now": {"value": 9}});
480
- fill_query_needs(&query_ir(), "Lease::Slot.Expired", &mut named, &FixedClock(5));
601
+ fill_query_needs(
602
+ &query_ir(),
603
+ "Lease::Slot.Expired",
604
+ &mut named,
605
+ &FixedClock(5),
606
+ );
481
607
  assert_eq!(named["now"], json!({"value": 9}));
482
608
  }
483
609
 
@@ -485,15 +611,30 @@ mod tests {
485
611
  fn a_query_that_needs_nothing_or_names_nothing_is_untouched() {
486
612
  let mut args = json!({});
487
613
  fill_query_needs(&query_ir(), "Lease::Slot.Plain", &mut args, &FixedClock(5));
488
- fill_query_needs(&query_ir(), "Lease::Slot.Missing", &mut args, &FixedClock(5));
489
- fill_query_needs(&query_ir(), "Lease::Shelf.Expired", &mut args, &FixedClock(5));
614
+ fill_query_needs(
615
+ &query_ir(),
616
+ "Lease::Slot.Missing",
617
+ &mut args,
618
+ &FixedClock(5),
619
+ );
620
+ fill_query_needs(
621
+ &query_ir(),
622
+ "Lease::Shelf.Expired",
623
+ &mut args,
624
+ &FixedClock(5),
625
+ );
490
626
  assert_eq!(args, json!({}));
491
627
  }
492
628
 
493
629
  #[test]
494
630
  fn today_is_whole_days_since_the_epoch_shaped_for_its_argument() {
495
631
  let mut args = json!({});
496
- fill_query_needs(&query_ir(), "Lease::Slot.IssuedToday", &mut args, &FixedClock((19_000 * 86_400) + 86_399));
632
+ fill_query_needs(
633
+ &query_ir(),
634
+ "Lease::Slot.IssuedToday",
635
+ &mut args,
636
+ &FixedClock((19_000 * 86_400) + 86_399),
637
+ );
497
638
  assert_eq!(args["today"], json!(19_000));
498
639
 
499
640
  let mut command = json!({"with": {}});