@githolon/dsl 0.108.7 → 0.110.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.
Files changed (308) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +80 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +214 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +268 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23950 -15776
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +550 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_event_desk_close_test.dart +16 -0
  74. package/dart/test/reception_span_trace_test.dart +23 -0
  75. package/dart/test/replica_readiness_test.dart +156 -0
  76. package/dart/test/runtime_projections_test.dart +92 -0
  77. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  78. package/dart/test/signing_identity_kernel_test.dart +75 -0
  79. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  80. package/dart/test/sync_status_reducer_test.dart +419 -0
  81. package/dart/test/test_harness_test.dart +71 -95
  82. package/dart/test/typed_query_watch_test.dart +170 -0
  83. package/dart/test/typed_watch_registry_test.dart +249 -0
  84. package/dart_lints/README.md +18 -0
  85. package/dart_lints/lib/nomos_lints.dart +70 -0
  86. package/dart_lints/pubspec.lock +580 -0
  87. package/dart_lints/pubspec.yaml +12 -0
  88. package/dart_lints/test/reference_identity_test.dart +75 -0
  89. package/dart_types/CHANGELOG.md +18 -0
  90. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  91. package/dart_types/lib/nomos_types.dart +9 -1
  92. package/dart_types/lib/src/admission_decision.dart +37 -0
  93. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  94. package/dart_types/lib/src/approved_model.dart +65 -0
  95. package/dart_types/lib/src/custody.dart +10 -18
  96. package/dart_types/lib/src/error_info.dart +4 -4
  97. package/dart_types/lib/src/errors.dart +29 -29
  98. package/dart_types/lib/src/foreign_ref.dart +62 -33
  99. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  100. package/dart_types/lib/src/nomos_ref.dart +3 -1
  101. package/dart_types/lib/src/principal.dart +2 -2
  102. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  103. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  105. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  107. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  149. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  150. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  151. package/dart_types/lib/src/subscriptions.dart +116 -23
  152. package/dart_types/pubspec.yaml +1 -1
  153. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  154. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  155. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  156. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  157. package/operation_contract_check.mjs +24 -0
  158. package/package.json +11 -6
  159. package/src/aggregate.ts +70 -10
  160. package/src/application_topology.ts +18 -11
  161. package/src/attested.ts +3 -0
  162. package/src/attested_core.ts +46 -3
  163. package/src/birth_meter_ensure.ts +19 -7
  164. package/src/births.ts +29 -4
  165. package/src/build_package.ts +140 -13
  166. package/src/business_birth.ts +181 -4
  167. package/src/codegen_call_proto.ts +1276 -0
  168. package/src/codegen_call_usda.ts +49 -6
  169. package/src/codegen_dart.ts +83 -44
  170. package/src/codegen_dart_client.ts +458 -800
  171. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  172. package/src/codegen_dart_framework_calls.ts +320 -37
  173. package/src/codegen_dart_indexed_variant.ts +159 -0
  174. package/src/codegen_dart_scenario.ts +42 -13
  175. package/src/codegen_proof.ts +3 -3
  176. package/src/codegen_provider_dart.ts +5 -0
  177. package/src/codegen_query_proto.ts +158 -0
  178. package/src/codegen_refusal.ts +189 -0
  179. package/src/codegen_result_proto.ts +931 -0
  180. package/src/codegen_result_usda.ts +4 -0
  181. package/src/codegen_ts.ts +464 -616
  182. package/src/codegen_workspace_access.ts +105 -0
  183. package/src/compatibility_graph.ts +759 -0
  184. package/src/compatibility_usda.ts +264 -0
  185. package/src/compile_package_main.ts +2017 -1195
  186. package/src/custody_placement.ts +156 -0
  187. package/src/custody_placement_usda.ts +91 -0
  188. package/src/decl_shapes.ts +21 -2
  189. package/src/describe_model.ts +1 -0
  190. package/src/directive.ts +59 -2
  191. package/src/drivers.ts +51 -0
  192. package/src/engine_entry.ts +52 -8
  193. package/src/evolve_preview.ts +11 -4
  194. package/src/exists.ts +2 -0
  195. package/src/fields.ts +75 -11
  196. package/src/flow_gate.ts +2 -1
  197. package/src/framework/application_home.ts +170 -18
  198. package/src/framework/attested_receipts.ts +8 -3
  199. package/src/framework/auth_provider_law.ts +169 -9
  200. package/src/framework/birth_certificate_law.ts +4 -0
  201. package/src/framework/capability.ts +34 -3
  202. package/src/framework/child_authentication.ts +346 -0
  203. package/src/framework/console_governance.ts +295 -0
  204. package/src/framework/delegated_roles.ts +301 -0
  205. package/src/framework/domain_lifecycle.ts +26 -13
  206. package/src/framework/founding_genesis.ts +193 -0
  207. package/src/framework/framework_aggregates.ts +1 -1
  208. package/src/framework/genesis.ts +6 -1
  209. package/src/framework/governance_relations.ts +19 -0
  210. package/src/framework/home.ts +5 -734
  211. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  212. package/src/framework/model_authority_repair.ts +116 -0
  213. package/src/framework/model_authority_seed.ts +185 -0
  214. package/src/framework/model_transition.ts +211 -0
  215. package/src/framework/owned_custody.ts +1 -1
  216. package/src/framework/principal_enrollment.ts +302 -0
  217. package/src/framework/receiving_group.ts +857 -0
  218. package/src/framework/receiving_group_recovery.ts +314 -0
  219. package/src/framework/relations.ts +4 -8
  220. package/src/framework/signing_identity.ts +242 -95
  221. package/src/framework/source_roster.ts +11 -5
  222. package/src/framework/workspace_access.ts +149 -0
  223. package/src/framework/workspace_invariant.ts +73 -52
  224. package/src/framework/workspaces.ts +407 -40
  225. package/src/framework_call_definitions.ts +32 -3
  226. package/src/generated_client_bundle_boundary.ts +97 -0
  227. package/src/generated_client_names.ts +1 -0
  228. package/src/identity_usda.ts +27 -0
  229. package/src/index.ts +183 -12
  230. package/src/indexed_variant.ts +83 -0
  231. package/src/intent.ts +173 -45
  232. package/src/law_check.ts +8 -5
  233. package/src/law_dependency_index.ts +231 -0
  234. package/src/law_graph.ts +508 -71
  235. package/src/law_graph_usda.ts +170 -24
  236. package/src/law_identity.ts +119 -81
  237. package/src/law_source_map.ts +4 -1
  238. package/src/law_units.ts +498 -0
  239. package/src/law_value_type.ts +7 -3
  240. package/src/member_authentication.ts +302 -0
  241. package/src/null_meta.ts +1 -1
  242. package/src/openusd_compiled_layer.ts +132 -0
  243. package/src/operation_contract_check.ts +31 -0
  244. package/src/ordered_read.ts +2 -1
  245. package/src/package_config.ts +33 -12
  246. package/src/package_dependencies.ts +1 -0
  247. package/src/plan_source_slice.ts +30 -1
  248. package/src/protobuf_field_number.ts +36 -0
  249. package/src/provenance_wire.ts +6 -0
  250. package/src/query.ts +2 -1
  251. package/src/read.ts +50 -0
  252. package/src/relation.ts +4 -1
  253. package/src/relation_schema.ts +15 -2
  254. package/src/report.ts +2 -2
  255. package/src/sqlite_computation_bundle.ts +78 -0
  256. package/src/sqlite_computed.ts +102 -0
  257. package/src/sqlite_counts.ts +154 -0
  258. package/src/sqlite_layout.ts +823 -0
  259. package/src/sqlite_reads.ts +275 -0
  260. package/src/sqlite_samples.ts +185 -0
  261. package/src/stable_id_identity_bridge.ts +1 -1
  262. package/src/stable_ids_for_module.ts +16 -4
  263. package/src/test_call_usda.ts +6 -6
  264. package/src/usda_relationship_integrity.ts +20 -3
  265. package/src/usda_text.ts +21 -1
  266. package/src/value_object.ts +81 -11
  267. package/src/wire.ts +22 -0
  268. package/src/wire_encode.ts +227 -20
  269. package/src/workspace_sharding.ts +15 -6
  270. package/src/workspace_sharing.ts +51 -49
  271. package/src/zod_introspection.ts +2 -0
  272. package/compat_package.mjs +0 -46
  273. package/dart/lib/src/home.dart +0 -15
  274. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  275. package/dart/lib/src/source_trust.dart +0 -51
  276. package/dart/lib/src/webview_bridge.dart +0 -6540
  277. package/dart/lib/src/workspace_name.dart +0 -30
  278. package/dart/test/acquire_session_credentials_test.dart +0 -150
  279. package/dart/test/acquire_session_test.dart +0 -534
  280. package/dart/test/adv_realm_interference_test.dart +0 -600
  281. package/dart/test/cloud_bound_topology_test.dart +0 -168
  282. package/dart/test/cluster_births_test.dart +0 -202
  283. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  284. package/dart/test/cluster_local_autosync_test.dart +0 -123
  285. package/dart/test/cluster_scale_test.dart +0 -122
  286. package/dart/test/compat_test.dart +0 -61
  287. package/dart/test/dlq_session_surface_test.dart +0 -222
  288. package/dart/test/first_app_gauntlet_test.dart +0 -386
  289. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  290. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  291. package/dart/test/home_parity_test.dart +0 -13
  292. package/dart/test/local_sync_inert_test.dart +0 -107
  293. package/dart/test/node_runner_transport_test.dart +0 -78
  294. package/dart/test/parent_signer_recovery_test.dart +0 -155
  295. package/dart/test/residency_refusal_test.dart +0 -49
  296. package/dart/test/sealing_client_test.dart +0 -181
  297. package/dart/test/session_bound_test.dart +0 -285
  298. package/dart/test/stale_bundle_test.dart +0 -80
  299. package/dart/test/vo_read_typing_test.dart +0 -131
  300. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  301. package/dart/test/webview_bridge_test.dart +0 -1930
  302. package/dart/test/workspace_name_test.dart +0 -24
  303. package/runtime/generated-application-host.js +0 -22860
  304. package/src/codegen_query_usda.ts +0 -214
  305. package/src/compat_manifest.ts +0 -26
  306. package/src/compat_package_main.ts +0 -224
  307. package/src/shape_compat.ts +0 -352
  308. package/src/usd_state.ts +0 -505
@@ -0,0 +1,759 @@
1
+ // NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
2
+ // remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
3
+ // wasm32-wasip1 artifact. If a file isn't this / hosting this / authoring for this
4
+ // / proving this — it's gone.
5
+ //
6
+ // COMPATIBILITY GRAPH — compiler-owned extraction only.
7
+ //
8
+ // This module projects the typed DSL/law graph into the language-neutral vocabulary
9
+ // emitted as compatibility USDA and used to generate stateless facades. It does not
10
+ // decide whether a client fits installed law. That authoritative comparison belongs
11
+ // to the Rust kernel's Compatibility Desk.
12
+
13
+ import { z } from "zod";
14
+ import type { DomainModule } from "./codegen_dart.js";
15
+ import { lowerLawValueType, type LawValueType } from "./law_value_type.js";
16
+ import type { StableIds } from "./stable_ids_types.js";
17
+ import type { Field } from "./fields.js";
18
+ import type { GeometryKind } from "./value_object.js";
19
+ import { isBusinessIntent } from "./intent.js";
20
+ import { isFrameworkAggregate } from "./framework/framework_aggregates.js";
21
+ import type {
22
+ LawAnonymousValueTypeNode,
23
+ LawAggregateNode,
24
+ LawFieldNode,
25
+ LawGraph,
26
+ LawIntentNode,
27
+ LawPayloadContractNode,
28
+ LawTypeNode,
29
+ } from "./law_graph.js";
30
+
31
+ /** One payload field's normalized shape: its kind + whether the schema makes it optional. */
32
+ export interface PayloadFieldShape {
33
+ readonly kind: string;
34
+ readonly optional: boolean;
35
+ /** Canonical Protobuf allocation encoded by this client or emitted by this law. */
36
+ readonly wireKey?: string;
37
+ /** Previously shipped allocations accepted by installed law during an overlap window. */
38
+ readonly wireAliases?: readonly string[];
39
+ /** Exact value-contract fingerprint. It changes when the field's accepted shape changes. */
40
+ readonly schemaIdentity?: string;
41
+ /** Root in the domain-local recursive value-type graph. */
42
+ readonly type?: string;
43
+ }
44
+
45
+ /**
46
+ * The compatibility vocabulary for domain values. This is a graph rather than
47
+ * an inline tree so recursive values and shared nested value objects remain
48
+ * finite and retain their authored structure.
49
+ */
50
+ export type CompatibilityValueShape =
51
+ | {
52
+ readonly kind:
53
+ | "string"
54
+ | "int"
55
+ | "double"
56
+ | "bool"
57
+ | "null"
58
+ | "unknown"
59
+ | "reference";
60
+ }
61
+ | { readonly kind: "enum"; readonly values: readonly string[] }
62
+ | { readonly kind: "geometry"; readonly geometry: GeometryKind }
63
+ | {
64
+ readonly kind: "literal";
65
+ readonly value: string | number | boolean | null;
66
+ }
67
+ | { readonly kind: "list"; readonly element: string }
68
+ | { readonly kind: "map"; readonly value: string }
69
+ | {
70
+ readonly kind: "record";
71
+ readonly fields: Readonly<
72
+ Record<string, { readonly required: boolean; readonly type: string }>
73
+ >;
74
+ }
75
+ | { readonly kind: "sum"; readonly variants: readonly string[] }
76
+ | { readonly kind: "recursive"; readonly body: string };
77
+ /** A directive's payload contract — the field set + types the gate validates against. */
78
+ export interface DirectivePayloadShape {
79
+ readonly fields: Record<string, PayloadFieldShape>;
80
+ /** Nominal refusal family SID -> output type graph root. */
81
+ readonly domainRefusals?: Readonly<Record<string, string>>;
82
+ /** New generated decisions preserve unsupported output contracts explicitly. */
83
+ readonly acceptsUnknownRefusals?: true;
84
+ }
85
+ /** An aggregate's read contract — fields keyed by STABLE ID (rename-safe), each carrying
86
+ * its current label (for human-readable verdicts) + its read kind. */
87
+ export interface AggregateReadShape {
88
+ readonly sid: string;
89
+ readonly label: string;
90
+ /** keyed by FIELD SID. */
91
+ readonly fields: Record<
92
+ string,
93
+ { kind: string; label: string; type?: string }
94
+ >;
95
+ }
96
+ /** A domain's full structural interface — the offer (a workspace's installed law) AND
97
+ * the requirement (the law a client was generated from) are the SAME vocabulary. */
98
+ export interface DomainInterface {
99
+ readonly v: 1;
100
+ readonly domain: string;
101
+ readonly domainSid?: string;
102
+ /** keyed by DIRECTIVE ID (the dispatched name). */
103
+ readonly directives: Record<string, DirectivePayloadShape>;
104
+ /** keyed by AGGREGATE SID. */
105
+ readonly aggregates: Record<string, AggregateReadShape>;
106
+ /** Domain-local recursive type graph referenced by directive and aggregate fields. */
107
+ readonly types?: Readonly<Record<string, CompatibilityValueShape>>;
108
+ /** ATTESTED READS (slice B + dynamic policies): directive id → its declared foreign-read edges —
109
+ * a STATIC `{queryId, from}` edge or a dynamic `{queryId, policy, fromPayload}` edge — the
110
+ * cross-workspace dependencies this law consumes on its write path. OMITTED when none (era rule). */
111
+ readonly foreignReads?: Record<
112
+ string,
113
+ { queryId: string; from?: string; policy?: string; fromPayload?: string }[]
114
+ >;
115
+ }
116
+
117
+ /** Remove framework-managed aggregate fields from the client-facing structural interface. Stable ids for
118
+ * those fields remain in the law/lockfile; clients negotiate only the business shape they can see. */
119
+ export function publicClientInterface(
120
+ modules: readonly DomainModule[],
121
+ offered: Readonly<Record<string, DomainInterface>>
122
+ ): Record<string, DomainInterface> {
123
+ const hidden = new Map<string, Map<string, Set<string>>>();
124
+ const publicAggregates = new Map<string, Set<string>>();
125
+ for (const mod of modules) {
126
+ const domain = mod.domain ?? mod.name;
127
+ const byAggregate = hidden.get(domain) ?? new Map<string, Set<string>>();
128
+ hidden.set(domain, byAggregate);
129
+ const visible = publicAggregates.get(domain) ?? new Set<string>();
130
+ publicAggregates.set(domain, visible);
131
+ for (const aggregate of mod.aggregates ?? []) {
132
+ if (
133
+ (aggregate as { businessBirthInternal?: unknown })
134
+ .businessBirthInternal !== true &&
135
+ !isFrameworkAggregate(aggregate)
136
+ ) {
137
+ visible.add(aggregate.id);
138
+ }
139
+ const fields =
140
+ (aggregate as { internalFields?: readonly string[] }).internalFields ??
141
+ [];
142
+ if (fields.length > 0) byAggregate.set(aggregate.id, new Set(fields));
143
+ }
144
+ }
145
+ const result: Record<string, DomainInterface> = {};
146
+ for (const [domain, value] of Object.entries(offered)) {
147
+ const byAggregate = hidden.get(domain);
148
+ const visible = publicAggregates.get(domain);
149
+ const aggregates: Record<string, AggregateReadShape> = {};
150
+ for (const [sid, aggregate] of Object.entries(value.aggregates)) {
151
+ if (visible !== undefined && !visible.has(aggregate.label)) continue;
152
+ const fields = byAggregate?.get(aggregate.label);
153
+ aggregates[sid] =
154
+ fields === undefined
155
+ ? aggregate
156
+ : {
157
+ ...aggregate,
158
+ fields: Object.fromEntries(
159
+ Object.entries(aggregate.fields).filter(
160
+ ([, field]) => !fields.has(field.label)
161
+ )
162
+ ),
163
+ };
164
+ }
165
+ result[domain] = { ...value, aggregates };
166
+ }
167
+ return result;
168
+ }
169
+
170
+ /** Normalize the canonical language-neutral DSL value graph to the compatibility
171
+ * vocabulary. Language generators are projections of this graph, never authorities
172
+ * for it. */
173
+ function payloadFieldKind(value: LawValueType): string {
174
+ switch (value.kind) {
175
+ case "enum":
176
+ return "enum";
177
+ case "string":
178
+ return "string";
179
+ case "integer":
180
+ return "int";
181
+ case "number":
182
+ return "double";
183
+ case "boolean":
184
+ return "bool";
185
+ case "literal":
186
+ return typeof value.value === "boolean"
187
+ ? "bool"
188
+ : typeof value.value === "number"
189
+ ? Number.isInteger(value.value)
190
+ ? "int"
191
+ : "double"
192
+ : typeof value.value === "string"
193
+ ? "string"
194
+ : "json";
195
+ case "list":
196
+ return value.element.kind === "string" ? "set" : "json";
197
+ case "nullable":
198
+ case "default":
199
+ case "recursive":
200
+ return payloadFieldKind(value.value);
201
+ case "map":
202
+ case "record":
203
+ case "sum":
204
+ case "null":
205
+ case "unknown":
206
+ case "recursive-ref":
207
+ return "json";
208
+ }
209
+ }
210
+
211
+ /** The aggregate read kind when no stable-id kind is recorded (pre-#58 fallback): the
212
+ * field's own declared kind, with the read-collapsed CRDTs normalized to their scalar. */
213
+ function aggFieldReadKind(f: Field): string {
214
+ switch (f.kind) {
215
+ case "counter":
216
+ return "int";
217
+ case "position":
218
+ return "string";
219
+ default:
220
+ return f.kind;
221
+ }
222
+ }
223
+
224
+ /** Extract a domain's structural interface from its loaded module + (optionally) its
225
+ * stable-id surface. Without stable ids (pre-#58) aggregate fields key by NAME and the
226
+ * kind comes from the field declaration — a graceful, rename-unaware fallback. */
227
+ export function domainInterface(
228
+ mod: DomainModule,
229
+ stableIds?: StableIds
230
+ ): DomainInterface {
231
+ const isInternal = (value: unknown): boolean =>
232
+ !!value &&
233
+ typeof value === "object" &&
234
+ (value as { businessBirthInternal?: unknown }).businessBirthInternal ===
235
+ true;
236
+ const enumTypes = new Map<string, string>();
237
+ // DETERMINISTIC KEY ORDER — directive ids, aggregate sids, and field keys are SORTED, so the
238
+ // emitted interface is byte-stable and the layered-USD fold (which merges + sorts) reproduces it
239
+ // byte-identically (the `nomosStableIds`/`nomosReadGate` convention). architecture/shape_compatibility.md.
240
+ const sortedRec = <T>(entries: Array<[string, T]>): Record<string, T> => {
241
+ const out: Record<string, T> = {};
242
+ for (const [k, v] of entries.sort((a, b) =>
243
+ a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0
244
+ ))
245
+ out[k] = v;
246
+ return out;
247
+ };
248
+ const directives: Record<string, DirectivePayloadShape> = {};
249
+ const dirEntries: Array<[string, DirectivePayloadShape]> = [];
250
+ for (const d of (mod.directives ?? []) as Array<{
251
+ id?: unknown;
252
+ payloadSchema?: unknown;
253
+ businessBirth?: { publicPayloadSchema?: unknown };
254
+ }>) {
255
+ if (!d || typeof d.id !== "string") continue;
256
+ if (isInternal(d)) continue;
257
+ const fieldEntries: Array<[string, PayloadFieldShape]> = [];
258
+ const schema = d.businessBirth?.publicPayloadSchema ?? d.payloadSchema;
259
+ if (schema && typeof schema === "object") {
260
+ const payload = lowerLawValueType(schema as z.ZodTypeAny);
261
+ if (payload.kind !== "record") {
262
+ throw new Error(
263
+ `directive '${d.id}' payload must lower to a typed record`
264
+ );
265
+ }
266
+ for (const field of payload.fields) {
267
+ fieldEntries.push([
268
+ field.name,
269
+ { kind: payloadFieldKind(field.value), optional: !field.required },
270
+ ]);
271
+ }
272
+ }
273
+ dirEntries.push([
274
+ d.businessBirth === undefined || isBusinessIntent(d) ? d.id : "birth",
275
+ { fields: sortedRec(fieldEntries) },
276
+ ]);
277
+ }
278
+ Object.assign(directives, sortedRec(dirEntries));
279
+ const aggregates: Record<string, AggregateReadShape> = {};
280
+ const aggEntries: Array<[string, AggregateReadShape]> = [];
281
+ for (const agg of (mod.aggregates ?? []) as Array<{
282
+ id: string;
283
+ fields: Record<string, Field>;
284
+ }>) {
285
+ // This is also the stored-law evolution surface. Internal aggregates retain
286
+ // durable identities; only publicClientInterface hides them from app clients.
287
+ const label = agg.id;
288
+ const sa = stableIds?.aggregates?.[label];
289
+ const aggSid = sa?.sid ?? label;
290
+ const fieldEntries: Array<[string, { kind: string; label: string }]> = [];
291
+ for (const [fname, f] of Object.entries(agg.fields ?? {})) {
292
+ // This is the workspace's complete structural offer and the input to law-evolution
293
+ // checking, so framework-managed stored fields belong here too. Only the generated
294
+ // client's view hides them, in publicClientInterface below. Filtering them at both
295
+ // seams makes an unchanged private field look deleted when the current offer is
296
+ // compared with the committed identity ledger.
297
+ if (!f || f.kind === "hasMany") continue;
298
+ const fsid = sa?.fields?.[fname]?.sid ?? fname;
299
+ const kind = sa?.fields?.[fname]?.kind ?? aggFieldReadKind(f);
300
+ fieldEntries.push([fsid, { kind, label: fname }]);
301
+ }
302
+ aggEntries.push([
303
+ aggSid,
304
+ { sid: aggSid, label, fields: sortedRec(fieldEntries) },
305
+ ]);
306
+ }
307
+ Object.assign(aggregates, sortedRec(aggEntries));
308
+ // ATTESTED READS (slice B): the declared trust edges — so shape-compat + client codegen can
309
+ // SEE the cross-workspace dependency (typed clients don't call it; it's write-path-internal).
310
+ type ForeignEdge = {
311
+ queryId: string;
312
+ from?: string;
313
+ policy?: string;
314
+ fromPayload?: string;
315
+ };
316
+ const foreignReadEntries: Array<[string, ForeignEdge[]]> = [];
317
+ for (const d of (mod.directives ?? []) as Array<{
318
+ id?: unknown;
319
+ declaredForeignReads?: ForeignEdge[];
320
+ }>) {
321
+ if (!d || typeof d.id !== "string") continue;
322
+ if (isInternal(d)) continue;
323
+ if (
324
+ d.declaredForeignReads !== undefined &&
325
+ d.declaredForeignReads.length > 0
326
+ ) {
327
+ foreignReadEntries.push([
328
+ d.id,
329
+ d.declaredForeignReads.map((e) => ({ ...e })),
330
+ ]);
331
+ }
332
+ }
333
+ const foreignReads = sortedRec(foreignReadEntries);
334
+ return {
335
+ v: 1,
336
+ domain:
337
+ (mod as { domain?: string; name?: string }).domain ??
338
+ (mod as { name?: string }).name ??
339
+ "",
340
+ ...(stableIds?.domain !== undefined ? { domainSid: stableIds.domain } : {}),
341
+ directives,
342
+ aggregates,
343
+ ...(foreignReadEntries.length > 0 ? { foreignReads } : {}),
344
+ };
345
+ }
346
+
347
+ /**
348
+ * Project the compatibility contract from canonical typed law.
349
+ *
350
+ * This is deliberately the TypeScript twin of the kernel's
351
+ * `interface_offer_from_stage`: generated client requirements and installed
352
+ * workspace offers are now two projections of the same graph/USDA, rather than
353
+ * independently interpreting DomainModule and hoping the shapes coincide.
354
+ */
355
+ export function domainInterfaceFromLawGraph(graph: LawGraph): DomainInterface {
356
+ const nodes = new Map<string, LawGraph["nodes"][number]>(
357
+ graph.nodes.map((node) => [node.entity.id, node])
358
+ );
359
+ const sorted = <T>(
360
+ entries: readonly (readonly [string, T])[]
361
+ ): Record<string, T> =>
362
+ Object.fromEntries([...entries].sort(([a], [b]) => a.localeCompare(b)));
363
+ const valueNode = (id: string): LawAnonymousValueTypeNode | undefined => {
364
+ const node = nodes.get(id);
365
+ return node?.kind === "structural" && node.structuralKind === "value-type"
366
+ ? node
367
+ : undefined;
368
+ };
369
+ const valueFieldNode = (id: string) => {
370
+ const node = nodes.get(id);
371
+ return node?.kind === "structural" && node.structuralKind === "value-field"
372
+ ? node
373
+ : undefined;
374
+ };
375
+ const valueVariantNode = (id: string) => {
376
+ const node = nodes.get(id);
377
+ return node?.kind === "structural" &&
378
+ node.structuralKind === "value-variant"
379
+ ? node
380
+ : undefined;
381
+ };
382
+ const resolvedTypeId = (id: string, seen = new Set<string>()): string => {
383
+ if (seen.has(id))
384
+ throw new Error(`compatibility value wrapper cycle at '${id}'`);
385
+ const node = valueNode(id);
386
+ // Compatibility describes the exact language-neutral value accepted at the installed law
387
+ // boundary. A semantic value-object declaration names that value for generated APIs, but it
388
+ // is not a substitute wire shape: discriminated-union arms, defaults and other schema
389
+ // refinements live on the structural graph rooted at this exact field. Redirecting to the
390
+ // named declaration erased required `type` discriminators and made the generated client look
391
+ // incompatible with the very law from which it was compiled.
392
+ if (
393
+ node?.expression.kind !== "nullable" &&
394
+ node?.expression.kind !== "default"
395
+ )
396
+ return id;
397
+ seen.add(id);
398
+ return resolvedTypeId(node.expression.value.id, seen);
399
+ };
400
+ const kindOf = (type: LawAnonymousValueTypeNode | undefined): string => {
401
+ if (type === undefined) return "json";
402
+ switch (type.expression.kind) {
403
+ case "enum":
404
+ return "enum";
405
+ case "string":
406
+ return "string";
407
+ case "integer":
408
+ return "int";
409
+ case "number":
410
+ return "double";
411
+ case "boolean":
412
+ return "bool";
413
+ case "list":
414
+ return valueNode(type.expression.element.id)?.expression.kind ===
415
+ "string"
416
+ ? "set"
417
+ : "json";
418
+ // Match the installed USDA projection exactly. Structured, wrapped and
419
+ // nullable values remain one typed aggregate at the law boundary, exposed
420
+ // as a structured compatibility leaf rather than guessed from Zod.
421
+ case "default":
422
+ case "nullable":
423
+ case "record":
424
+ case "map":
425
+ case "sum":
426
+ case "null":
427
+ case "literal":
428
+ case "recursive":
429
+ case "unknown":
430
+ return "json";
431
+ }
432
+ };
433
+
434
+ const directives = sorted(
435
+ graph.nodes
436
+ .filter(
437
+ (node): node is LawIntentNode =>
438
+ node.kind === "intent" &&
439
+ node.public &&
440
+ !node.entity.label.startsWith("nomos_internal_")
441
+ )
442
+ .map((intent): readonly [string, DirectivePayloadShape] => {
443
+ const contract = nodes.get(
444
+ (intent.publicAccepts ?? intent.accepts).id
445
+ ) as LawPayloadContractNode | undefined;
446
+ const fields =
447
+ contract?.kind === "payload-contract"
448
+ ? contract.fields.flatMap((ref) => {
449
+ const field = nodes.get(ref.id);
450
+ if (
451
+ field?.kind !== "structural" ||
452
+ field.structuralKind !== "value-field"
453
+ )
454
+ return [];
455
+ return [
456
+ [
457
+ field.name,
458
+ {
459
+ kind: kindOf(valueNode(field.value.id)),
460
+ optional: !field.required,
461
+ wireKey: field.entity.wireKey ?? field.entity.sid,
462
+ ...(field.entity.wireAliases === undefined
463
+ ? {}
464
+ : { wireAliases: field.entity.wireAliases }),
465
+ schemaIdentity: field.entity.sid,
466
+ type: resolvedTypeId(field.value.id),
467
+ },
468
+ ] as const,
469
+ ];
470
+ })
471
+ : [];
472
+ const domainRefusals = sorted((intent.domainRefusals ?? []).map(ref => {
473
+ const type = nodes.get(ref.id);
474
+ if (type?.kind !== "type") throw new Error(`refusal output ${ref.id} is not nominal`);
475
+ return [type.entity.sid, type.entity.id] as const;
476
+ }));
477
+ return [intent.publicRoute, { fields: sorted(fields),
478
+ ...(Object.keys(domainRefusals).length === 0 ? {} : { domainRefusals, acceptsUnknownRefusals: true }),
479
+ }];
480
+ })
481
+ );
482
+
483
+ const referenceTypeId = (field: LawFieldNode): string =>
484
+ `reference:${field.entity.sid}`;
485
+
486
+ const aggregates = sorted(
487
+ graph.nodes
488
+ .filter(
489
+ (node): node is LawAggregateNode =>
490
+ node.kind === "aggregate" &&
491
+ !node.entity.label.startsWith("nomos_internal_")
492
+ )
493
+ .map((aggregate): readonly [string, AggregateReadShape] => {
494
+ const fields = aggregate.fields.flatMap((ref) => {
495
+ const field = nodes.get(ref.id) as LawFieldNode | undefined;
496
+ if (field?.kind !== "field") return [];
497
+ return [
498
+ [
499
+ field.entity.sid,
500
+ {
501
+ kind:
502
+ field.refTarget === undefined
503
+ ? field.storageKind ?? "json"
504
+ : "reference",
505
+ label: field.entity.label,
506
+ type:
507
+ field.refTarget === undefined
508
+ ? resolvedTypeId(field.value.id)
509
+ : referenceTypeId(field),
510
+ },
511
+ ] as const,
512
+ ];
513
+ });
514
+ return [
515
+ aggregate.entity.sid,
516
+ {
517
+ sid: aggregate.entity.sid,
518
+ label: aggregate.entity.label,
519
+ fields: sorted(fields),
520
+ },
521
+ ];
522
+ })
523
+ );
524
+
525
+ // A reference remains a nominal application value whether its transport is a local ID or a
526
+ // structured workspace/type/id address. Preserve the semantic category in compatibility;
527
+ // the operation schema independently describes that transport. The target can become an additional opinion
528
+ // without changing this vocabulary.
529
+ const referenceTypes = graph.nodes
530
+ .filter(
531
+ (node): node is LawFieldNode =>
532
+ node.kind === "field" && node.refTarget !== undefined
533
+ )
534
+ .map((field): readonly [string, CompatibilityValueShape] => [
535
+ referenceTypeId(field),
536
+ { kind: "reference" },
537
+ ]);
538
+
539
+ const anonymousTypes = graph.nodes
540
+ .filter(
541
+ (node): node is LawAnonymousValueTypeNode =>
542
+ node.kind === "structural" &&
543
+ node.structuralKind === "value-type" &&
544
+ node.expression.kind !== "nullable" &&
545
+ node.expression.kind !== "default"
546
+ )
547
+ .map((node): readonly [string, CompatibilityValueShape] => {
548
+ const expression = node.expression;
549
+ switch (expression.kind) {
550
+ case "string":
551
+ return [node.entity.id, { kind: "string" }];
552
+ case "integer":
553
+ return [node.entity.id, { kind: "int" }];
554
+ case "number":
555
+ return [node.entity.id, { kind: "double" }];
556
+ case "boolean":
557
+ return [node.entity.id, { kind: "bool" }];
558
+ case "null":
559
+ return [node.entity.id, { kind: "null" }];
560
+ case "unknown":
561
+ return [node.entity.id, { kind: "unknown" }];
562
+ case "enum":
563
+ return [
564
+ node.entity.id,
565
+ { kind: "enum", values: [...expression.values].sort() },
566
+ ];
567
+ case "literal":
568
+ return [node.entity.id, { kind: "literal", value: expression.value }];
569
+ case "list":
570
+ return [
571
+ node.entity.id,
572
+ { kind: "list", element: resolvedTypeId(expression.element.id) },
573
+ ];
574
+ case "map":
575
+ return [
576
+ node.entity.id,
577
+ { kind: "map", value: resolvedTypeId(expression.value.id) },
578
+ ];
579
+ case "record": {
580
+ const fields = expression.fields.flatMap((ref) => {
581
+ const field = valueFieldNode(ref.id);
582
+ return field === undefined
583
+ ? []
584
+ : [
585
+ [
586
+ field.name,
587
+ {
588
+ required: field.required,
589
+ type: resolvedTypeId(field.value.id),
590
+ },
591
+ ] as const,
592
+ ];
593
+ });
594
+ return [node.entity.id, { kind: "record", fields: sorted(fields) }];
595
+ }
596
+ case "sum": {
597
+ const variants = expression.variants
598
+ .flatMap((ref) => {
599
+ const variant = valueVariantNode(ref.id);
600
+ return variant === undefined
601
+ ? []
602
+ : [resolvedTypeId(variant.value.id)];
603
+ })
604
+ .sort();
605
+ return [node.entity.id, { kind: "sum", variants }];
606
+ }
607
+ case "recursive":
608
+ return [
609
+ node.entity.id,
610
+ { kind: "recursive", body: resolvedTypeId(expression.value.id) },
611
+ ];
612
+ case "nullable":
613
+ case "default":
614
+ throw new Error(
615
+ "compatibility wrappers are resolved before type emission"
616
+ );
617
+ }
618
+ });
619
+ const declaredTypes = graph.nodes
620
+ .filter((node): node is LawTypeNode => node.kind === "type")
621
+ .map((node): readonly [string, CompatibilityValueShape] => {
622
+ if (node.form === "geometry") {
623
+ if (node.geometry === undefined) {
624
+ throw new Error(
625
+ `compatibility type '${node.entity.label}' has no geometry kind`
626
+ );
627
+ }
628
+ return [node.entity.id, { kind: "geometry", geometry: node.geometry }];
629
+ }
630
+ if (node.form === "sum") {
631
+ return [
632
+ node.entity.id,
633
+ {
634
+ kind: "sum",
635
+ variants: node.variants
636
+ .flatMap((ref) => {
637
+ const variant = valueVariantNode(ref.id);
638
+ return variant === undefined
639
+ ? []
640
+ : [resolvedTypeId(variant.value.id)];
641
+ })
642
+ .sort(),
643
+ },
644
+ ];
645
+ }
646
+ const fields = node.fields.flatMap((ref) => {
647
+ const field = nodes.get(ref.id);
648
+ return field?.kind !== "field"
649
+ ? []
650
+ : [
651
+ [
652
+ field.entity.label,
653
+ {
654
+ required: field.required,
655
+ type: resolvedTypeId(field.value.id),
656
+ },
657
+ ] as const,
658
+ ];
659
+ });
660
+ return [node.entity.id, { kind: "record", fields: sorted(fields) }];
661
+ });
662
+ const allTypes = sorted([
663
+ ...anonymousTypes,
664
+ ...declaredTypes,
665
+ ...referenceTypes,
666
+ ]);
667
+ const reachable = new Set<string>();
668
+ const visitType = (id: string, path: string): void => {
669
+ const resolved = resolvedTypeId(id);
670
+ if (reachable.has(resolved)) return;
671
+ const type = allTypes[resolved];
672
+ if (type === undefined)
673
+ throw new Error(
674
+ `compatibility member '${path}' references missing value type '${resolved}'`
675
+ );
676
+ if (type.kind === "unknown") {
677
+ throw new Error(
678
+ `compatibility member '${path}' exposes an opaque JSON/unknown value; ` +
679
+ `declare a typed value object so the generated client and installed law can be compared ` +
680
+ `(law type ${resolved}: ${JSON.stringify(
681
+ graph.nodes
682
+ .filter((node) => node.entity.id === resolved)
683
+ .map((node) => ({
684
+ label: node.entity.label,
685
+ owner:
686
+ node.kind === "structural" && "owner" in node
687
+ ? node.owner.id
688
+ : null,
689
+ expression:
690
+ node.kind === "structural" &&
691
+ node.structuralKind === "value-type"
692
+ ? node.expression
693
+ : node.kind,
694
+ }))
695
+ )})`
696
+ );
697
+ }
698
+ reachable.add(resolved);
699
+ switch (type.kind) {
700
+ case "list":
701
+ visitType(type.element, `${path}[]`);
702
+ break;
703
+ case "map":
704
+ visitType(type.value, `${path}{}`);
705
+ break;
706
+ case "record":
707
+ for (const [name, field] of Object.entries(type.fields))
708
+ visitType(field.type, `${path}.${name}`);
709
+ break;
710
+ case "sum":
711
+ for (const [index, variant] of type.variants.entries())
712
+ visitType(variant, `${path}|${index}`);
713
+ break;
714
+ case "recursive":
715
+ visitType(type.body, `${path}↻`);
716
+ break;
717
+ case "string":
718
+ case "int":
719
+ case "double":
720
+ case "bool":
721
+ case "null":
722
+ case "enum":
723
+ case "literal":
724
+ case "geometry":
725
+ case "reference":
726
+ break;
727
+ }
728
+ };
729
+ for (const [route, directive] of Object.entries(directives)) {
730
+ for (const [family, type] of Object.entries(directive.domainRefusals ?? {})) {
731
+ visitType(type, `${graph.domain.entity.label}.${route} refusal ${family}`);
732
+ }
733
+ for (const [name, field] of Object.entries(directive.fields)) {
734
+ if (field.type !== undefined)
735
+ visitType(field.type, `${graph.domain.entity.label}.${route}.${name}`);
736
+ }
737
+ }
738
+ for (const aggregate of Object.values(aggregates)) {
739
+ for (const field of Object.values(aggregate.fields)) {
740
+ if (field.type !== undefined)
741
+ visitType(
742
+ field.type,
743
+ `${graph.domain.entity.label}.${aggregate.label}.${field.label}`
744
+ );
745
+ }
746
+ }
747
+ const types = sorted(
748
+ Object.entries(allTypes).filter(([id]) => reachable.has(id))
749
+ );
750
+
751
+ return {
752
+ v: 1,
753
+ domain: graph.domain.entity.label,
754
+ domainSid: graph.domain.entity.sid,
755
+ directives,
756
+ aggregates,
757
+ types,
758
+ };
759
+ }