hecks 3.2.0 → 3.2.1
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/lib/hecks/bluebook/dsl/hecksagon_builder.rb +3 -3
- data/lib/hecks/cli/check_era.rb +3 -39
- data/lib/hecks/doors/mcp_door.rb +16 -2
- data/lib/hecks/doors/mcp_door_scope.rb +43 -4
- data/lib/hecks/doors/mcp_guide_cache.rb +75 -0
- data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +17 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +10 -2
- data/lib/hecks/hecks/adapters/rust_workspace.rb +1 -1
- data/lib/hecks/hecks/adapters/site_toolchain.rb +21 -11
- data/lib/hecks/language/bluebook/vocabulary.bluebook +7 -7
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +1 -1
- data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +3 -3
- data/lib/hecks/projections/deploy/box/settings.rb +23 -2
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +2 -0
- data/lib/hecks/projections/deploy/box/templates/render-compose-taskdef.sh.tmpl +17 -0
- data/lib/hecks/projections/deploy/box.rb +13 -2
- data/lib/hecks/projections/site/edge/behaviours.rb +5 -3
- data/lib/hecks/projections/site/edge/checks.rb +31 -5
- data/lib/hecks/projections/site/edge.rb +25 -11
- data/lib/hecks/projections/site/routes_ts.rb +50 -27
- data/lib/hecks/projections/site/site_cdn.rb +7 -4
- data/lib/hecks/projections/site/table/checks.rb +53 -7
- data/lib/hecks/projections/site/table/link_builder.rb +64 -0
- data/lib/hecks/projections/site/table/row_builder.rb +21 -6
- data/lib/hecks/projections/site/table.rb +16 -5
- data/lib/hecks/rust_build/kernel_input.rb +18 -6
- data/lib/hecks/rust_build/project_rust.rb +6 -132
- data/lib/hecks/site/bluebook/site.bluebook +27 -13
- data/lib/hecks/storehouse.rb +35 -1
- data/lib/hecks/tools/comment_style.rb +4 -2
- data/lib/hecks/tools/rust_comment_style.rb +1 -1
- data/lib/hecks/tools/site_routes.rb +69 -28
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +1 -1
- data/lib/hecks.rb +1 -0
- data/rust/build/src/lineage_pass.rs +1 -1
- data/rust/build/src/pipeline.rs +1 -1
- data/rust/codegen/src/attr.rs +1 -1
- data/rust/codegen/src/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +1 -1
- data/rust/codegen/src/constraints.rs +1 -1
- data/rust/codegen/src/dependency_planning.rs +1 -1
- data/rust/codegen/src/domain_generator.rs +2 -2
- data/rust/codegen/src/exemplar.rs +1 -1
- data/rust/codegen/src/expr_emitter.rs +1 -1
- data/rust/codegen/src/fielded.rs +1 -1
- data/rust/codegen/src/json_codec.rs +1 -1
- data/rust/codegen/src/main.rs +3 -4
- data/rust/codegen/src/mutations.rs +1 -1
- data/rust/codegen/src/naming.rs +1 -1
- data/rust/codegen/src/ports.rs +1 -1
- data/rust/codegen/src/queries.rs +3 -3
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/read_models.rs +2 -2
- data/rust/codegen/src/reference_specs.rs +1 -1
- data/rust/codegen/src/registry.rs +4 -4
- data/rust/codegen/src/shared.rs +1 -1
- data/rust/codegen/src/skip_reason.rs +1 -1
- data/rust/codegen/src/types.rs +4 -4
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/needs.rs +94 -27
- data/rust/src/exemplar/commands.rs +1 -1
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/fielded.rs +1 -1
- data/rust/src/exemplar/json.rs +1 -1
- data/rust/src/exemplar/mod.rs +1 -1
- data/rust/src/exemplar/mutations.rs +1 -1
- data/rust/src/exemplar/queries.rs +1 -1
- data/rust/src/exemplar/reactions.rs +1 -1
- data/rust/src/exemplar/read_models.rs +1 -1
- data/rust/src/exemplar/registry.rs +1 -1
- data/rust/src/exemplar/types.rs +1 -1
- data/rust/src/kernel/cli.rs +2 -2
- data/rust/src/kernel/read_model.rs +1 -1
- data/rust/src/lib.rs +1 -1
- metadata +3 -23
- data/rust/project/bridging.rb +0 -257
- data/rust/project/commands.rb +0 -877
- data/rust/project/constraints.rb +0 -86
- data/rust/project/dependency_planning.rb +0 -178
- data/rust/project/domain_generator.rb +0 -983
- data/rust/project/exemplar.rb +0 -148
- data/rust/project/expr_emitter.rb +0 -72
- data/rust/project/fielded.rb +0 -141
- data/rust/project/json_codec.rb +0 -641
- data/rust/project/mutations.rb +0 -695
- data/rust/project/naming.rb +0 -223
- data/rust/project/ports.rb +0 -65
- data/rust/project/queries.rb +0 -493
- data/rust/project/reactions.rb +0 -317
- data/rust/project/read_models.rb +0 -690
- data/rust/project/reference_specs.rb +0 -27
- data/rust/project/registry.rb +0 -396
- data/rust/project/skip_reason.rb +0 -29
- data/rust/project/types.rb +0 -225
- data/rust/project/write_if_changed.rb +0 -7
- data/rust/project.rb +0 -30
- data/rust/project_rust_pipeline.rb +0 -391
data/rust/project/bridging.rb
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
module RustProjection
|
|
2
|
-
module Projector
|
|
3
|
-
module_function
|
|
4
|
-
|
|
5
|
-
# `use` statements for value-object types this aggregate's commands
|
|
6
|
-
# reference by name but don't declare locally (same chapter only).
|
|
7
|
-
def cross_aggregate_vo_imports(aggregate, domain_value_object_owner, mod_name)
|
|
8
|
-
local_names = aggregate[:value_objects].map { |vo| vo[:name] }.to_set
|
|
9
|
-
attrs = (aggregate[:commands] + aggregate[:entities].flat_map { |e| e[:commands] } +
|
|
10
|
-
aggregate[:ports].flat_map { |p| p[:operations] })
|
|
11
|
-
.flat_map { |c| c[:attributes] }
|
|
12
|
-
|
|
13
|
-
# A locally-declared type is never "foreign", even if some other
|
|
14
|
-
# aggregate owns a VO of the same name domain-wide.
|
|
15
|
-
foreign_types = attrs.map { |a| a[:type] }.uniq.reject { |type| local_names.include?(type) }
|
|
16
|
-
foreign_types.filter_map { |type| domain_value_object_owner[type] && [type, domain_value_object_owner[type]] }
|
|
17
|
-
.sort_by { |(type, owner)| [owner, type] }
|
|
18
|
-
.map { |(type, owner)| "use crate::generated::#{mod_name}::#{owner.downcase}::#{rust_ident(type)};" }
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Whether target_vo's fields can all be filled from source_vo's fields
|
|
22
|
-
# (matched by name) or the target's own defaults.
|
|
23
|
-
def vo_field_bridgeable?(source_vo, target_vo)
|
|
24
|
-
return false unless source_vo && target_vo
|
|
25
|
-
return closed_sets_bridgeable?(source_vo, target_vo) if source_vo[:closed_set] || target_vo[:closed_set]
|
|
26
|
-
|
|
27
|
-
target_vo[:attributes].all? do |t_attr|
|
|
28
|
-
source_vo[:attributes].any? { |s_attr| s_attr[:name] == t_attr[:name] } || !t_attr[:default].nil?
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Source closed-set members mappable to target members, or nil unless
|
|
33
|
-
# every source member is also a target member (so the bridge can't fail).
|
|
34
|
-
def closed_set_bridge_members(source_vo, target_vo)
|
|
35
|
-
return nil unless source_vo[:closed_set] && target_vo[:closed_set]
|
|
36
|
-
return nil unless source_vo[:attributes].size == 1 && target_vo[:attributes].size == 1
|
|
37
|
-
|
|
38
|
-
target_values = target_vo[:members].map { |row| row.to_h.values.first.to_s }
|
|
39
|
-
source_rows = source_vo[:members]
|
|
40
|
-
return nil unless source_rows.all? { |row| target_values.include?(row.to_h.values.first.to_s) }
|
|
41
|
-
|
|
42
|
-
source_rows
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def closed_sets_bridgeable?(source_vo, target_vo)
|
|
46
|
-
!closed_set_bridge_members(source_vo, target_vo).nil?
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def vo_field_rhs(source_expr, source_vo, target_type, value_objects_by_name)
|
|
50
|
-
target_vo = value_objects_by_name[target_type]
|
|
51
|
-
if (rows = closed_set_bridge_members(source_vo, target_vo))
|
|
52
|
-
arms = rows.map { |row| "#{rust_ident(source_vo[:name])}::#{closed_set_variant(row)} => #{rust_ident(target_type)}::#{closed_set_variant(row)}" }
|
|
53
|
-
return "match &#{source_expr} { #{arms.join(', ')} }"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
fields = target_vo[:attributes].map do |t_attr|
|
|
57
|
-
field = rust_ident_field(t_attr[:name])
|
|
58
|
-
if source_vo[:attributes].any? { |s_attr| s_attr[:name] == t_attr[:name] }
|
|
59
|
-
"#{field}: #{source_expr}.#{field}.clone()"
|
|
60
|
-
else
|
|
61
|
-
"#{field}: #{literal_rhs(t_attr[:default])}"
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
"#{rust_ident(target_type)} { #{fields.join(', ')} }"
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Whether a value can move from source_type to target_type. Shared
|
|
68
|
-
# with `value_rhs`, which performs the bridge this checks for.
|
|
69
|
-
def bridgeable_value_types?(source_type, target_type, value_objects_by_name)
|
|
70
|
-
return true if source_type == target_type
|
|
71
|
-
# Compares Rust representations, not declared type names: a
|
|
72
|
-
# `Reference<T>` and a bare `String` are both just `String`.
|
|
73
|
-
return true if effective_scalar_type(source_type) && effective_scalar_type(source_type) == effective_scalar_type(target_type)
|
|
74
|
-
|
|
75
|
-
source_vo = value_objects_by_name[source_type]
|
|
76
|
-
target_vo = value_objects_by_name[target_type]
|
|
77
|
-
return vo_field_bridgeable?(source_vo, target_vo) if target_vo
|
|
78
|
-
|
|
79
|
-
# A single-field source VO unwraps into a bare scalar target when
|
|
80
|
-
# that one field's own Rust representation already matches it.
|
|
81
|
-
return false unless source_vo && !source_vo[:closed_set] && source_vo[:attributes].size == 1
|
|
82
|
-
|
|
83
|
-
unwrapped_type = source_vo[:attributes].first[:type]
|
|
84
|
-
unwrapped_type == target_type ||
|
|
85
|
-
(effective_scalar_type(unwrapped_type) && effective_scalar_type(unwrapped_type) == effective_scalar_type(target_type))
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def value_rhs(source_expr, source_type, target_type, value_objects_by_name)
|
|
89
|
-
return "#{source_expr}.clone()" if source_type == target_type
|
|
90
|
-
return "#{source_expr}.clone()" if effective_scalar_type(source_type) && effective_scalar_type(source_type) == effective_scalar_type(target_type)
|
|
91
|
-
|
|
92
|
-
source_vo = value_objects_by_name[source_type]
|
|
93
|
-
target_vo = value_objects_by_name[target_type]
|
|
94
|
-
return vo_field_rhs(source_expr, source_vo, target_type, value_objects_by_name) if target_vo
|
|
95
|
-
|
|
96
|
-
raise "unsupported coercion #{source_type} -> #{target_type} — bridgeable_value_types? should have caught this" unless source_vo && !source_vo[:closed_set] && source_vo[:attributes].size == 1
|
|
97
|
-
|
|
98
|
-
"#{source_expr}.#{rust_ident_field(source_vo[:attributes].first[:name])}.clone()"
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Whether a list `:set` needs per-element rebuilding, rather than one
|
|
102
|
-
# `.clone()`, because source and target elements differ in representation.
|
|
103
|
-
def list_bridge_requires_element_mapping?(source_type, target_type)
|
|
104
|
-
return false if source_type == target_type
|
|
105
|
-
|
|
106
|
-
!(effective_scalar_type(source_type) && effective_scalar_type(source_type) == effective_scalar_type(target_type))
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# `value_rhs`, mapped over each element of a list `:set` whose source
|
|
110
|
-
# and target element types differ.
|
|
111
|
-
def list_value_rhs(source_expr, source_type, target_type, value_objects_by_name)
|
|
112
|
-
"#{source_expr}.iter().map(|item| #{value_rhs('item', source_type, target_type, value_objects_by_name)}).collect()"
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def literal_set_bridgeable?(value, target_type, value_objects_by_name)
|
|
116
|
-
return literal_hash_bridgeable?(value, target_type, value_objects_by_name) if value.is_a?(Hash) && target_type
|
|
117
|
-
return false if value.is_a?(Hash)
|
|
118
|
-
return false unless value.is_a?(String) || value.is_a?(Numeric) || value == true || value == false
|
|
119
|
-
return true unless target_type && value_objects_by_name[target_type]
|
|
120
|
-
|
|
121
|
-
# A scalar literal into a single-field VO stands for that field's
|
|
122
|
-
# value (mirrors `Value.for_attribute`'s runtime rewrap).
|
|
123
|
-
sole = sole_field_of(target_type, value_objects_by_name)
|
|
124
|
-
return false unless sole
|
|
125
|
-
|
|
126
|
-
literal_hash_bridgeable?({ sole => value }, target_type, value_objects_by_name)
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# The right-hand side for a literal into target_type: a Hash, a
|
|
130
|
-
# scalar rewrapped into a single-field VO, or the scalar itself.
|
|
131
|
-
def literal_rhs_for(value, target_type, value_objects_by_name)
|
|
132
|
-
return literal_hash_rhs(value, target_type, value_objects_by_name) if value.is_a?(Hash)
|
|
133
|
-
|
|
134
|
-
sole = target_type && value_objects_by_name[target_type] && sole_field_of(target_type, value_objects_by_name)
|
|
135
|
-
return literal_hash_rhs({ sole => value }, target_type, value_objects_by_name) if sole
|
|
136
|
-
|
|
137
|
-
literal_rhs(value)
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Whether a literal Hash supplies every field a target VO (or one
|
|
141
|
-
# of its closed-set members) needs.
|
|
142
|
-
def literal_hash_bridgeable?(hash, target_type, value_objects_by_name)
|
|
143
|
-
vo = value_objects_by_name[target_type]
|
|
144
|
-
return false unless vo
|
|
145
|
-
|
|
146
|
-
if vo[:closed_set]
|
|
147
|
-
vo[:members].any? { |member| member.all? { |field, value| [hash[field.to_sym], hash[field.to_s]].include?(value) } }
|
|
148
|
-
else
|
|
149
|
-
vo[:attributes].all? { |attr| hash.key?(attr[:name].to_sym) || hash.key?(attr[:name].to_s) }
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def literal_hash_rhs(hash, target_type, value_objects_by_name)
|
|
154
|
-
vo = value_objects_by_name[target_type]
|
|
155
|
-
raise "unsupported literal hash source #{hash.inspect} -> #{target_type} — literal_hash_bridgeable? should have caught this" unless vo
|
|
156
|
-
|
|
157
|
-
if vo[:closed_set]
|
|
158
|
-
row = vo[:members].find { |member| member.all? { |field, value| [hash[field.to_sym], hash[field.to_s]].include?(value) } }
|
|
159
|
-
raise "literal #{hash.inspect} matches no member of #{target_type} — literal_hash_bridgeable? should have caught this" unless row
|
|
160
|
-
|
|
161
|
-
"#{rust_ident(target_type)}::#{closed_set_variant(row)}"
|
|
162
|
-
else
|
|
163
|
-
fields = vo[:attributes].map do |attr|
|
|
164
|
-
key = [attr[:name].to_sym, attr[:name].to_s].find { |k| hash.key?(k) }
|
|
165
|
-
raise "literal #{hash.inspect} missing field #{attr[:name]} for #{target_type} — literal_hash_bridgeable? should have caught this" unless key
|
|
166
|
-
|
|
167
|
-
"#{rust_ident_field(attr[:name])}: #{literal_rhs(hash[key])}"
|
|
168
|
-
end
|
|
169
|
-
"#{rust_ident(target_type)} { #{fields.join(', ')} }"
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def integer_field_of(vo)
|
|
174
|
-
return nil unless vo && !vo[:closed_set]
|
|
175
|
-
|
|
176
|
-
attr = vo[:attributes].find { |a| a[:type] == "Integer" }
|
|
177
|
-
attr && attr[:name]
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# The attribute and its one Integer field an `:increment`/`:decrement`
|
|
181
|
-
# touches, or nil unless the target is a single-field integer VO.
|
|
182
|
-
def arithmetic_target_field(mutation, aggregate, value_objects_by_name)
|
|
183
|
-
target_attr = aggregate[:attributes].find { |a| a[:name].to_s == mutation[:target].to_s }
|
|
184
|
-
return nil unless target_attr && !target_attr[:list]
|
|
185
|
-
|
|
186
|
-
field = integer_field_of(value_objects_by_name[target_attr[:type]])
|
|
187
|
-
field && [target_attr, field]
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# The amount side of an `:increment`/`:decrement` as a raw Rust
|
|
191
|
-
# integer expression. Returns nil, not raise, when nothing bridges.
|
|
192
|
-
def arithmetic_amount_expr(source, command, value_objects_by_name, target_integer_field)
|
|
193
|
-
if source[:kind] == "literal"
|
|
194
|
-
value = source[:value]
|
|
195
|
-
return literal_rhs(value) if value.is_a?(Integer)
|
|
196
|
-
return nil unless value.is_a?(Hash)
|
|
197
|
-
|
|
198
|
-
key = [target_integer_field.to_sym, target_integer_field.to_s].find { |k| value.key?(k) }
|
|
199
|
-
key && value[key].is_a?(Integer) ? literal_rhs(value[key]) : nil
|
|
200
|
-
elsif source[:kind] == "argument"
|
|
201
|
-
arg_attr = command[:attributes].find { |a| a[:name].to_s == source[:name] }
|
|
202
|
-
return nil unless arg_attr
|
|
203
|
-
return "args.#{rust_ident_field(arg_attr[:name])}" if arg_attr[:type] == "Integer"
|
|
204
|
-
|
|
205
|
-
field = integer_field_of(value_objects_by_name[arg_attr[:type]])
|
|
206
|
-
field && "args.#{rust_ident_field(arg_attr[:name])}.#{rust_ident_field(field)}"
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
# `clamp:`'s literal `[min, max]` bounds, or nil for anything else
|
|
211
|
-
# (non-literal source, wrong length, non-Integer bound).
|
|
212
|
-
def clamp_bounds_ints(source)
|
|
213
|
-
return nil unless source[:kind] == "literal"
|
|
214
|
-
|
|
215
|
-
value = source[:value]
|
|
216
|
-
return nil unless value.is_a?(Array) && value.size == 2 && value.all? { |v| v.is_a?(Integer) }
|
|
217
|
-
|
|
218
|
-
value
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
# The default for an attribute a creating command's arguments don't
|
|
222
|
-
# mention: its own default, or one built from its VO's field defaults.
|
|
223
|
-
def creation_default_rhs(attr, value_objects_by_name)
|
|
224
|
-
default = attr[:default]
|
|
225
|
-
unless default.nil?
|
|
226
|
-
return literal_rhs(default) unless default.is_a?(Hash)
|
|
227
|
-
|
|
228
|
-
# Completes a partial Hash default with the target VO's own
|
|
229
|
-
# per-field defaults first; `literal_hash_rhs` requires every
|
|
230
|
-
# field present.
|
|
231
|
-
return literal_hash_rhs(complete_hash_default(default, attr[:type], value_objects_by_name), attr[:type], value_objects_by_name)
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
vo = value_objects_by_name[attr[:type]]
|
|
235
|
-
return nil unless vo && !vo[:closed_set] && vo[:attributes].all? { |f| !f[:default].nil? }
|
|
236
|
-
|
|
237
|
-
fields = vo[:attributes].map { |f| "#{rust_ident_field(f[:name])}: #{literal_rhs(f[:default])}" }
|
|
238
|
-
"#{rust_ident(attr[:type])} { #{fields.join(', ')} }"
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
# Fields present in `hash`, plus any the target VO defaults on its
|
|
242
|
-
# own; a field with neither is left out, not set to nil.
|
|
243
|
-
def complete_hash_default(hash, target_type, value_objects_by_name)
|
|
244
|
-
vo = value_objects_by_name[target_type]
|
|
245
|
-
return hash unless vo && !vo[:closed_set]
|
|
246
|
-
|
|
247
|
-
vo[:attributes].each_with_object({}) do |field, completed|
|
|
248
|
-
key = [field[:name].to_sym, field[:name].to_s].find { |k| hash.key?(k) }
|
|
249
|
-
if key
|
|
250
|
-
completed[field[:name].to_sym] = hash[key]
|
|
251
|
-
elsif !field[:default].nil?
|
|
252
|
-
completed[field[:name].to_sym] = field[:default]
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
end
|