@githolon/dsl 0.108.7 → 0.110.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.
- package/assets/operation-contract.wasm +0 -0
- package/check_package.mjs +12 -15
- package/compile_package.mjs +17 -18
- package/compile_proof.mjs +16 -1
- package/dart/CHANGELOG.md +76 -1
- package/dart/README.md +25 -2
- package/dart/lib/src/admission_decision.dart +41 -0
- package/dart/lib/src/application_framework_calls.dart +52 -2
- package/dart/lib/src/application_model_adoption.dart +187 -0
- package/dart/lib/src/application_status.dart +73 -0
- package/dart/lib/src/console_enrolment.dart +271 -0
- package/dart/lib/src/console_http_io.dart +26 -0
- package/dart/lib/src/console_http_stub.dart +7 -0
- package/dart/lib/src/dev_cloud.dart +69 -22
- package/dart/lib/src/generated_call.dart +83 -23
- package/dart/lib/src/kernel_primitives.dart +119 -0
- package/dart/lib/src/nms_conformance_runtime.dart +1 -1
- package/dart/lib/src/nms_generated_runtime.dart +32 -10
- package/dart/lib/src/nms_test_environment.dart +1 -1
- package/dart/lib/src/node_runner_transport.dart +41 -46
- package/dart/lib/src/observed_session_directory.dart +70 -0
- package/dart/lib/src/operation_receipts.dart +112 -0
- package/dart/lib/src/peer_application_streams.dart +103 -0
- package/dart/lib/src/reception/dart_reception.dart +4280 -0
- package/dart/lib/src/reception/event_desk.dart +211 -0
- package/dart/lib/src/reception/extremum_read.dart +46 -0
- package/dart/lib/src/reception/local_copies.dart +371 -0
- package/dart/lib/src/reception/peer_office_connection.dart +11 -0
- package/dart/lib/src/reception/progress_desk.dart +267 -0
- package/dart/lib/src/reception/reception_contract.dart +11 -0
- package/dart/lib/src/reception/reception_session.dart +284 -0
- package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
- package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
- package/dart/lib/src/replica_readiness.dart +123 -0
- package/dart/lib/src/runner/node-entry.mjs +23905 -15774
- package/dart/lib/src/runtime_v2_gateway.dart +56 -3
- package/dart/lib/src/runtime_v2_transport.dart +544 -3
- package/dart/lib/src/session_bound.dart +200 -311
- package/dart/lib/src/signing_identity_kernel.dart +84 -0
- package/dart/lib/src/sync_status_projection.dart +548 -0
- package/dart/lib/src/sync_status_reducer.dart +170 -0
- package/dart/lib/src/test_harness.dart +809 -1153
- package/dart/lib/src/testing/test_custody.dart +66 -0
- package/dart/lib/src/testing/test_peer.dart +328 -0
- package/dart/lib/src/typed_watch_registry.dart +169 -0
- package/dart/lib/src/workspace_open_progress.dart +27 -0
- package/dart/pubspec.lock +24 -1
- package/dart/pubspec.yaml +7 -2
- package/dart/pubspec_overrides.yaml +2 -0
- package/dart/test/admission_decision_test.dart +48 -0
- package/dart/test/background_model_adoption_test.dart +228 -0
- package/dart/test/bundle_version_pairing_test.dart +4 -4
- package/dart/test/child_authentication_repair_test.dart +359 -0
- package/dart/test/cloud_bound_local_shell_test.dart +119 -0
- package/dart/test/console_enrolment_refusal_test.dart +52 -0
- package/dart/test/console_enrolment_test.dart +227 -0
- package/dart/test/errors_test.dart +5 -5
- package/dart/test/event_plane_seam_test.dart +50 -164
- package/dart/test/extremum_read_test.dart +58 -0
- package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
- package/dart/test/generated_query_test.dart +32 -0
- package/dart/test/harness_phase_timeout_test.dart +18 -23
- package/dart/test/kernel_author_after_read_test.dart +242 -0
- package/dart/test/kernel_fence_test.dart +137 -0
- package/dart/test/local_copies_test.dart +222 -0
- package/dart/test/model_adoption_test.dart +198 -0
- package/dart/test/node_peer_local_custody_test.dart +153 -0
- package/dart/test/offer_authentication_proof_test.dart +352 -52
- package/dart/test/operation_compatibility_test.dart +151 -0
- package/dart/test/platform_credential_effect_test.dart +386 -0
- package/dart/test/receiving_group_open_test.dart +213 -0
- package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
- package/dart/test/reception_span_trace_test.dart +23 -0
- package/dart/test/replica_readiness_test.dart +156 -0
- package/dart/test/runtime_projections_test.dart +92 -0
- package/dart/test/runtime_v2_gateway_test.dart +131 -0
- package/dart/test/signing_identity_kernel_test.dart +75 -0
- package/dart/test/sync_status_preparation_report_test.dart +29 -0
- package/dart/test/sync_status_reducer_test.dart +419 -0
- package/dart/test/test_harness_test.dart +71 -95
- package/dart/test/typed_query_watch_test.dart +170 -0
- package/dart/test/typed_watch_registry_test.dart +249 -0
- package/dart_lints/README.md +18 -0
- package/dart_lints/lib/nomos_lints.dart +70 -0
- package/dart_lints/pubspec.lock +580 -0
- package/dart_lints/pubspec.yaml +12 -0
- package/dart_lints/test/reference_identity_test.dart +75 -0
- package/dart_types/CHANGELOG.md +18 -0
- package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
- package/dart_types/lib/nomos_types.dart +9 -1
- package/dart_types/lib/src/admission_decision.dart +37 -0
- package/dart_types/lib/src/aggregate_meta.dart +1 -1
- package/dart_types/lib/src/approved_model.dart +65 -0
- package/dart_types/lib/src/custody.dart +10 -18
- package/dart_types/lib/src/error_info.dart +4 -4
- package/dart_types/lib/src/errors.dart +29 -29
- package/dart_types/lib/src/foreign_ref.dart +62 -33
- package/dart_types/lib/src/local_work_rejected.dart +3 -3
- package/dart_types/lib/src/nomos_ref.dart +3 -1
- package/dart_types/lib/src/principal.dart +2 -2
- package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
- package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
- package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
- package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
- package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
- package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
- package/dart_types/lib/src/runtime_outcome.dart +133 -0
- package/dart_types/lib/src/subscriptions.dart +116 -23
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/extremum_subscriptions_test.dart +40 -0
- package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
- package/dart_types/test/nomos_ref_address_test.dart +25 -0
- package/dart_types/test/ordered_subscriptions_test.dart +34 -0
- package/operation_contract_check.mjs +24 -0
- package/package.json +11 -6
- package/src/aggregate.ts +70 -10
- package/src/application_topology.ts +18 -11
- package/src/attested.ts +3 -0
- package/src/attested_core.ts +46 -3
- package/src/birth_meter_ensure.ts +19 -7
- package/src/births.ts +29 -4
- package/src/build_package.ts +140 -13
- package/src/business_birth.ts +181 -4
- package/src/codegen_call_proto.ts +1274 -0
- package/src/codegen_call_usda.ts +49 -6
- package/src/codegen_dart.ts +79 -44
- package/src/codegen_dart_client.ts +458 -800
- package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
- package/src/codegen_dart_framework_calls.ts +320 -37
- package/src/codegen_dart_indexed_variant.ts +159 -0
- package/src/codegen_dart_scenario.ts +42 -13
- package/src/codegen_proof.ts +3 -3
- package/src/codegen_provider_dart.ts +5 -0
- package/src/codegen_query_proto.ts +158 -0
- package/src/codegen_refusal.ts +189 -0
- package/src/codegen_result_proto.ts +931 -0
- package/src/codegen_result_usda.ts +4 -0
- package/src/codegen_ts.ts +464 -616
- package/src/codegen_workspace_access.ts +105 -0
- package/src/compatibility_graph.ts +759 -0
- package/src/compatibility_usda.ts +264 -0
- package/src/compile_package_main.ts +1994 -1189
- package/src/custody_placement.ts +156 -0
- package/src/custody_placement_usda.ts +91 -0
- package/src/decl_shapes.ts +21 -2
- package/src/describe_model.ts +1 -0
- package/src/directive.ts +59 -2
- package/src/drivers.ts +51 -0
- package/src/engine_entry.ts +52 -8
- package/src/evolve_preview.ts +11 -4
- package/src/exists.ts +2 -0
- package/src/fields.ts +75 -11
- package/src/flow_gate.ts +2 -1
- package/src/framework/application_home.ts +170 -18
- package/src/framework/attested_receipts.ts +8 -3
- package/src/framework/auth_provider_law.ts +169 -9
- package/src/framework/birth_certificate_law.ts +4 -0
- package/src/framework/capability.ts +34 -3
- package/src/framework/child_authentication.ts +346 -0
- package/src/framework/console_governance.ts +295 -0
- package/src/framework/delegated_roles.ts +301 -0
- package/src/framework/domain_lifecycle.ts +26 -13
- package/src/framework/founding_genesis.ts +193 -0
- package/src/framework/framework_aggregates.ts +1 -1
- package/src/framework/genesis.ts +6 -1
- package/src/framework/governance_relations.ts +19 -0
- package/src/framework/home.ts +5 -734
- package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
- package/src/framework/model_authority_repair.ts +116 -0
- package/src/framework/model_authority_seed.ts +185 -0
- package/src/framework/model_transition.ts +211 -0
- package/src/framework/owned_custody.ts +1 -1
- package/src/framework/principal_enrollment.ts +302 -0
- package/src/framework/receiving_group.ts +857 -0
- package/src/framework/receiving_group_recovery.ts +314 -0
- package/src/framework/relations.ts +4 -8
- package/src/framework/signing_identity.ts +242 -95
- package/src/framework/source_roster.ts +11 -5
- package/src/framework/workspace_access.ts +149 -0
- package/src/framework/workspace_invariant.ts +73 -52
- package/src/framework/workspaces.ts +407 -40
- package/src/framework_call_definitions.ts +32 -3
- package/src/generated_client_bundle_boundary.ts +97 -0
- package/src/generated_client_names.ts +1 -0
- package/src/identity_usda.ts +27 -0
- package/src/index.ts +183 -12
- package/src/indexed_variant.ts +83 -0
- package/src/intent.ts +173 -45
- package/src/law_check.ts +8 -5
- package/src/law_dependency_index.ts +231 -0
- package/src/law_graph.ts +508 -71
- package/src/law_graph_usda.ts +170 -24
- package/src/law_identity.ts +119 -81
- package/src/law_source_map.ts +4 -1
- package/src/law_units.ts +498 -0
- package/src/law_value_type.ts +7 -3
- package/src/member_authentication.ts +302 -0
- package/src/null_meta.ts +1 -1
- package/src/openusd_compiled_layer.ts +132 -0
- package/src/operation_contract_check.ts +31 -0
- package/src/ordered_read.ts +2 -1
- package/src/package_config.ts +33 -12
- package/src/package_dependencies.ts +1 -0
- package/src/plan_source_slice.ts +30 -1
- package/src/protobuf_field_number.ts +36 -0
- package/src/provenance_wire.ts +6 -0
- package/src/query.ts +2 -1
- package/src/read.ts +50 -0
- package/src/relation.ts +4 -1
- package/src/relation_schema.ts +15 -2
- package/src/report.ts +2 -2
- package/src/sqlite_computation_bundle.ts +78 -0
- package/src/sqlite_computed.ts +102 -0
- package/src/sqlite_counts.ts +154 -0
- package/src/sqlite_layout.ts +823 -0
- package/src/sqlite_reads.ts +275 -0
- package/src/sqlite_samples.ts +185 -0
- package/src/stable_id_identity_bridge.ts +1 -1
- package/src/stable_ids_for_module.ts +16 -4
- package/src/test_call_usda.ts +6 -6
- package/src/usda_relationship_integrity.ts +20 -3
- package/src/usda_text.ts +21 -1
- package/src/value_object.ts +81 -11
- package/src/wire.ts +22 -0
- package/src/wire_encode.ts +227 -20
- package/src/workspace_sharding.ts +15 -6
- package/src/workspace_sharing.ts +51 -49
- package/src/zod_introspection.ts +2 -0
- package/compat_package.mjs +0 -46
- package/dart/lib/src/home.dart +0 -15
- package/dart/lib/src/runner/offline-install.mjs +0 -15367
- package/dart/lib/src/source_trust.dart +0 -51
- package/dart/lib/src/webview_bridge.dart +0 -6540
- package/dart/lib/src/workspace_name.dart +0 -30
- package/dart/test/acquire_session_credentials_test.dart +0 -150
- package/dart/test/acquire_session_test.dart +0 -534
- package/dart/test/adv_realm_interference_test.dart +0 -600
- package/dart/test/cloud_bound_topology_test.dart +0 -168
- package/dart/test/cluster_births_test.dart +0 -202
- package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
- package/dart/test/cluster_local_autosync_test.dart +0 -123
- package/dart/test/cluster_scale_test.dart +0 -122
- package/dart/test/compat_test.dart +0 -61
- package/dart/test/dlq_session_surface_test.dart +0 -222
- package/dart/test/first_app_gauntlet_test.dart +0 -386
- package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
- package/dart/test/four_layer_nested_topology_test.dart +0 -290
- package/dart/test/home_parity_test.dart +0 -13
- package/dart/test/local_sync_inert_test.dart +0 -107
- package/dart/test/node_runner_transport_test.dart +0 -78
- package/dart/test/parent_signer_recovery_test.dart +0 -155
- package/dart/test/residency_refusal_test.dart +0 -49
- package/dart/test/sealing_client_test.dart +0 -181
- package/dart/test/session_bound_test.dart +0 -285
- package/dart/test/stale_bundle_test.dart +0 -80
- package/dart/test/vo_read_typing_test.dart +0 -131
- package/dart/test/watch_after_offer_stall_test.dart +0 -100
- package/dart/test/webview_bridge_test.dart +0 -1930
- package/dart/test/workspace_name_test.dart +0 -24
- package/runtime/generated-application-host.js +0 -22860
- package/src/codegen_query_usda.ts +0 -214
- package/src/compat_manifest.ts +0 -26
- package/src/compat_package_main.ts +0 -224
- package/src/shape_compat.ts +0 -352
- 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
|
+
}
|