@githolon/dsl 0.108.6 → 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 +2083 -1248
- 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,1274 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LawAnonymousValueFieldNode,
|
|
3
|
+
LawAnonymousValueTypeNode,
|
|
4
|
+
LawAnonymousValueVariantNode,
|
|
5
|
+
LawGraph,
|
|
6
|
+
LawIntentNode,
|
|
7
|
+
LawPayloadContractNode,
|
|
8
|
+
LawRowReadNode,
|
|
9
|
+
LawReadNode,
|
|
10
|
+
LawStructuralRef,
|
|
11
|
+
LawTypeNode,
|
|
12
|
+
LawValueTypeRef,
|
|
13
|
+
} from "./law_graph.js";
|
|
14
|
+
import type { GeometryKind } from "./fields.js";
|
|
15
|
+
import { dartMemberName, unionArmClassName } from "./codegen_dart.js";
|
|
16
|
+
import { mintStableId } from "./stable_ids.js";
|
|
17
|
+
import { protobufFieldNumber } from "./protobuf_field_number.js";
|
|
18
|
+
import { provenanceRowIdentity, provenanceFieldIdentity, provenanceAuthorIdentity, provenanceContestedIdentity } from "./provenance_wire.js";
|
|
19
|
+
|
|
20
|
+
export interface TsCallProtoFieldAccess {
|
|
21
|
+
readonly value: string;
|
|
22
|
+
readonly present?: string;
|
|
23
|
+
/** The generated client has already lowered this nominal value to its
|
|
24
|
+
* canonical scalar/record representation. Do not validate/lower it twice. */
|
|
25
|
+
readonly canonical?: true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DartCallProtoFieldAccess {
|
|
29
|
+
readonly value: string;
|
|
30
|
+
readonly present?: string;
|
|
31
|
+
readonly slot?: true;
|
|
32
|
+
readonly omit?: true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CallProtoEncoder {
|
|
36
|
+
readonly functionName: string;
|
|
37
|
+
readonly typeUrl: string;
|
|
38
|
+
readonly publicDirective: string;
|
|
39
|
+
readonly source: readonly string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface OperationProtoLaw {
|
|
43
|
+
readonly graph: LawGraph;
|
|
44
|
+
readonly paths: ReadonlyMap<string, string>;
|
|
45
|
+
/** Additional private compiler operations which have a generated client
|
|
46
|
+
* encoder. Public intents are always included. */
|
|
47
|
+
readonly directives?: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface OperationProtoSchema {
|
|
51
|
+
readonly fileName: "operation_payloads.proto";
|
|
52
|
+
readonly dartFileName: "operation_payloads.pb.dart";
|
|
53
|
+
readonly source: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const symbol = (value: string): string =>
|
|
57
|
+
value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]/, "_$&");
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Derive a permanent Protobuf field identity from the stable law identity.
|
|
61
|
+
*
|
|
62
|
+
* This deliberately does not use declaration order. Adding a field therefore
|
|
63
|
+
* cannot renumber any existing field. The compiler rejects the vanishingly
|
|
64
|
+
* unlikely collision inside each message instead of creating a schema registry.
|
|
65
|
+
*
|
|
66
|
+
* ONE FACT, ONE REPRESENTATION. The Peer Office derives the same numbers when it
|
|
67
|
+
* encodes a call from a workspace's own installed law, so the definition lives in a
|
|
68
|
+
* dependency-free leaf both sides import (`./protobuf_field_number.ts`) and this
|
|
69
|
+
* compiler re-exports it. A second copy here is exactly the drift the doctrine forbids.
|
|
70
|
+
*/
|
|
71
|
+
export { protobufFieldNumber } from "./protobuf_field_number.js";
|
|
72
|
+
|
|
73
|
+
function requireUniqueFieldNumbers(
|
|
74
|
+
fields: readonly { readonly label: string; readonly sid: string }[],
|
|
75
|
+
where: string,
|
|
76
|
+
): void {
|
|
77
|
+
const numbers = new Map<number, string>();
|
|
78
|
+
for (const field of fields) {
|
|
79
|
+
const number = protobufFieldNumber(field.sid);
|
|
80
|
+
const prior = numbers.get(number);
|
|
81
|
+
if (prior !== undefined) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`call Protobuf codegen: ${where} maps both '${prior}' and '${field.label}' to field ${number}`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
numbers.set(number, field.label);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* OpenUSD composition may share structurally identical anonymous records between
|
|
92
|
+
* operation contracts. `compileSparseLawGraphUsda` records that sharing by
|
|
93
|
+
* redirecting each duplicate field path to the canonical field prim. The wire
|
|
94
|
+
* identity must follow that composed path too; using the duplicate graph-local
|
|
95
|
+
* SID makes generated clients speak a different Protobuf schema from the law the
|
|
96
|
+
* kernel actually installs.
|
|
97
|
+
*/
|
|
98
|
+
export function composedWireIdentity(
|
|
99
|
+
paths: ReadonlyMap<string, string>,
|
|
100
|
+
entity: { readonly id: string; readonly sid: string; readonly wireKey?: string },
|
|
101
|
+
): string {
|
|
102
|
+
if (entity.wireKey !== undefined) return entity.wireKey;
|
|
103
|
+
const path = paths.get(entity.id);
|
|
104
|
+
const canonical = path?.match(/\/(?:Fields\/f_|Variants\/v_)([0-9a-f]{16})$/i)?.[1];
|
|
105
|
+
return canonical ?? entity.sid;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface NominalSumArm {
|
|
109
|
+
readonly discriminator: string;
|
|
110
|
+
readonly variant: LawAnonymousValueVariantNode;
|
|
111
|
+
readonly value: LawAnonymousValueTypeNode;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function nominalSumArms(
|
|
115
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
116
|
+
sum: LawAnonymousValueTypeNode,
|
|
117
|
+
where: string,
|
|
118
|
+
): readonly NominalSumArm[] {
|
|
119
|
+
if (sum.expression.kind !== "sum" || sum.expression.semantic?.kind !== "value-object") {
|
|
120
|
+
throw new Error(`${where} contains a structural sum; declare it with variant(...)`);
|
|
121
|
+
}
|
|
122
|
+
const arms = sum.expression.variants.map((ref): NominalSumArm => {
|
|
123
|
+
const variant = nodes.get(ref.id);
|
|
124
|
+
if (variant?.kind !== "structural" || variant.structuralKind !== "value-variant") {
|
|
125
|
+
throw new Error(`${where} references non-variant '${ref.id}'`);
|
|
126
|
+
}
|
|
127
|
+
const value = nodes.get(variant.value.id);
|
|
128
|
+
if (value?.kind !== "structural" || value.structuralKind !== "value-type" || value.expression.kind !== "record") {
|
|
129
|
+
throw new Error(`${where} variant '${variant.entity.label}' is not a nominal record arm`);
|
|
130
|
+
}
|
|
131
|
+
const discriminators = value.expression.fields.flatMap((fieldRef) => {
|
|
132
|
+
const field = nodes.get(fieldRef.id);
|
|
133
|
+
if (field?.kind !== "structural" || field.structuralKind !== "value-field" || field.name !== "type") return [];
|
|
134
|
+
const type = nodes.get(field.value.id);
|
|
135
|
+
return field.required && type?.kind === "structural" && type.structuralKind === "value-type" &&
|
|
136
|
+
type.expression.kind === "literal" && typeof type.expression.value === "string"
|
|
137
|
+
? [type.expression.value]
|
|
138
|
+
: [];
|
|
139
|
+
});
|
|
140
|
+
if (discriminators.length !== 1) {
|
|
141
|
+
throw new Error(`${where} variant '${variant.entity.label}' needs one required literal type discriminator`);
|
|
142
|
+
}
|
|
143
|
+
return { discriminator: discriminators[0]!, variant, value };
|
|
144
|
+
});
|
|
145
|
+
requireUniqueFieldNumbers(
|
|
146
|
+
arms.map((arm) => ({ label: arm.discriminator, sid: arm.variant.entity.sid })),
|
|
147
|
+
`${where} union`,
|
|
148
|
+
);
|
|
149
|
+
return [...arms].sort((a, b) => a.discriminator.localeCompare(b.discriminator));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The public law intent that takes `directive` as one of its steps, when one exists.
|
|
154
|
+
*
|
|
155
|
+
* A directive a business intent composes (a private effect, or a capability obligation it
|
|
156
|
+
* `.directs`) is offered THROUGH that intent: its route and payload contract are the publisher's.
|
|
157
|
+
* The graph's typed step edge is the authority; codegen must not rediscover this by scanning
|
|
158
|
+
* TypeScript objects.
|
|
159
|
+
*/
|
|
160
|
+
export function publishingIntentOf(
|
|
161
|
+
graph: LawGraph,
|
|
162
|
+
domain: string,
|
|
163
|
+
directive: string,
|
|
164
|
+
): LawIntentNode | undefined {
|
|
165
|
+
const direct = graph.nodes.find((node): node is LawIntentNode =>
|
|
166
|
+
node.kind === "intent" && (node.entity.label === directive || node.publicRoute === directive));
|
|
167
|
+
if (direct === undefined) throw new Error(`call Protobuf codegen: domain '${domain}' has no '${directive}' intent`);
|
|
168
|
+
const publishers = graph.nodes.filter((node): node is LawIntentNode =>
|
|
169
|
+
node.kind === "intent" &&
|
|
170
|
+
node.public &&
|
|
171
|
+
node.steps.some((step) => step.id === direct.entity.id));
|
|
172
|
+
if (publishers.length > 1) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`call Protobuf codegen: '${domain}/${directive}' is published by more than one law intent ` +
|
|
175
|
+
`(${publishers.map((publisher) => publisher.entity.label).join(", ")})`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return publishers[0];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function operation(
|
|
182
|
+
graph: LawGraph,
|
|
183
|
+
domain: string,
|
|
184
|
+
directive: string,
|
|
185
|
+
): {
|
|
186
|
+
readonly nodes: ReadonlyMap<string, LawGraph["nodes"][number]>;
|
|
187
|
+
readonly intent: LawIntentNode;
|
|
188
|
+
readonly contract: LawPayloadContractNode;
|
|
189
|
+
readonly publicDirective: string;
|
|
190
|
+
readonly typeUrl: string;
|
|
191
|
+
} {
|
|
192
|
+
const nodes = new Map(graph.nodes.map((node) => [node.entity.id, node]));
|
|
193
|
+
const publisher = publishingIntentOf(graph, domain, directive);
|
|
194
|
+
const direct = graph.nodes.find((node): node is LawIntentNode =>
|
|
195
|
+
node.kind === "intent" && (node.entity.label === directive || node.publicRoute === directive))!;
|
|
196
|
+
// A composed directive is never a second client route: it is offered through its publisher.
|
|
197
|
+
const intent = publisher ?? direct;
|
|
198
|
+
const contract = nodes.get(intent.accepts.id);
|
|
199
|
+
if (contract?.kind !== "payload-contract") {
|
|
200
|
+
throw new Error(`call Protobuf codegen: '${domain}/${directive}' accepts no payload contract`);
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
nodes,
|
|
204
|
+
intent,
|
|
205
|
+
contract,
|
|
206
|
+
publicDirective: intent.publicRoute ?? intent.entity.label,
|
|
207
|
+
typeUrl: `type.nomos.dev/nomos.operation.v1.Payload_${contract.entity.sid}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const fieldNode = (
|
|
212
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
213
|
+
ref: LawStructuralRef<"value-field">,
|
|
214
|
+
): LawAnonymousValueFieldNode => {
|
|
215
|
+
const node = nodes.get(ref.id);
|
|
216
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-field") {
|
|
217
|
+
throw new Error(`call Protobuf codegen: '${ref.id}' is not a value field`);
|
|
218
|
+
}
|
|
219
|
+
return node;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const typeNode = (
|
|
223
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
224
|
+
id: string,
|
|
225
|
+
): LawAnonymousValueTypeNode => {
|
|
226
|
+
const node = nodes.get(id);
|
|
227
|
+
if (node?.kind !== "structural" || node.structuralKind !== "value-type") {
|
|
228
|
+
throw new Error(`call Protobuf codegen: '${id}' is not a value type`);
|
|
229
|
+
}
|
|
230
|
+
return node;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export const structuralGeometryKind = (
|
|
234
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
235
|
+
node: LawAnonymousValueTypeNode,
|
|
236
|
+
): GeometryKind | undefined => {
|
|
237
|
+
// The IR carries the geo.* declaration. Do not guess from a record member
|
|
238
|
+
// named `type`: USDA and both generated languages must see the same fact.
|
|
239
|
+
return node.expression.semantic?.kind === "value-object"
|
|
240
|
+
? node.expression.semantic.geometry ?? structuralDeclaredValueObject(nodes, node)?.geometry
|
|
241
|
+
: undefined;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const structuralDeclaredValueObject = (
|
|
245
|
+
nodes: ReadonlyMap<string, LawGraph["nodes"][number]>,
|
|
246
|
+
node: LawAnonymousValueTypeNode,
|
|
247
|
+
): LawTypeNode | undefined => {
|
|
248
|
+
const semantic = node.expression.semantic;
|
|
249
|
+
if (semantic?.kind !== "value-object") return undefined;
|
|
250
|
+
return [...nodes.values()].find((candidate): candidate is LawTypeNode =>
|
|
251
|
+
candidate.kind === "type" &&
|
|
252
|
+
candidate.entity.label === semantic.name);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/** Anonymous structural SIDs are content-addressed under their immediate
|
|
256
|
+
* owner. Framework-owned shapes can therefore recur in several domains while
|
|
257
|
+
* resolving a nominal child to a different domain type. The wire message is
|
|
258
|
+
* contextual to the composed domain; nominal children retain their own stable
|
|
259
|
+
* declared identities. */
|
|
260
|
+
const structuralWireIdentity = (
|
|
261
|
+
graph: LawGraph,
|
|
262
|
+
node: LawAnonymousValueTypeNode,
|
|
263
|
+
): string => operationMessageIdentity(graph, node.entity.sid);
|
|
264
|
+
|
|
265
|
+
/** Message-class identity inside one composed application schema. The Any
|
|
266
|
+
* type URL remains the stable law member/contract identity understood by the
|
|
267
|
+
* kernel; this contextual identity only prevents distinct domain projections
|
|
268
|
+
* from colliding in generated language bindings. */
|
|
269
|
+
export const operationMessageIdentity = (
|
|
270
|
+
graph: LawGraph,
|
|
271
|
+
contractSid: string,
|
|
272
|
+
): string => mintStableId(
|
|
273
|
+
`operation-wire:${graph.domain.entity.sid}:${contractSid}`,
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
export const protoMessageName = (kind: string, sid: string): string =>
|
|
277
|
+
`${kind}_${sid.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
278
|
+
|
|
279
|
+
export const protoFieldName = (sid: string): string =>
|
|
280
|
+
`f${sid.replace(/[^A-Za-z0-9_]/g, "_")}`;
|
|
281
|
+
|
|
282
|
+
const dartProtoFieldName = protoFieldName;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Generate the application operation schema from the same composed law graph
|
|
286
|
+
* that emits law.usda. This is the sole wire-schema authority: Dart/TypeScript
|
|
287
|
+
* bindings are produced from this file by protoc, never by handwritten tag
|
|
288
|
+
* writers in either generated client.
|
|
289
|
+
*/
|
|
290
|
+
export function generateOperationProtoSchema(
|
|
291
|
+
laws: readonly OperationProtoLaw[],
|
|
292
|
+
): OperationProtoSchema {
|
|
293
|
+
const definitions = new Map<string, string>();
|
|
294
|
+
const add = (name: string, body: string): void => {
|
|
295
|
+
const prior = definitions.get(name);
|
|
296
|
+
if (prior !== undefined && prior !== body) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`operation Protobuf schema: '${name}' has two different composed definitions\n` +
|
|
299
|
+
`--- first ---\n${prior}\n--- second ---\n${body}`,
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
definitions.set(name, body);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
for (const { graph, paths, directives } of laws) {
|
|
306
|
+
const nodes = new Map(graph.nodes.map((node) => [node.entity.id, node]));
|
|
307
|
+
const declaredValueObjects = new Map<string, LawTypeNode>(
|
|
308
|
+
graph.nodes.flatMap((node) =>
|
|
309
|
+
node.kind === "type"
|
|
310
|
+
? [[node.entity.label, node] as const]
|
|
311
|
+
: []),
|
|
312
|
+
);
|
|
313
|
+
const field = (ref: LawStructuralRef<"value-field">): LawAnonymousValueFieldNode =>
|
|
314
|
+
fieldNode(nodes, ref);
|
|
315
|
+
const value = (id: string): LawAnonymousValueTypeNode => typeNode(nodes, id);
|
|
316
|
+
|
|
317
|
+
const valueRef = (ref: LawValueTypeRef, where: string): string => {
|
|
318
|
+
const node = nodes.get(ref.id);
|
|
319
|
+
if (node?.kind === "structural" && node.structuralKind === "value-type") {
|
|
320
|
+
return wireType(node, where);
|
|
321
|
+
}
|
|
322
|
+
if (node?.kind === "type") return ensureDeclaredType(node, where);
|
|
323
|
+
throw new Error(`operation Protobuf schema: '${where}' references no declared value type`);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const wireType = (node: LawAnonymousValueTypeNode, where: string): string => {
|
|
327
|
+
switch (node.expression.kind) {
|
|
328
|
+
case "default": return wireType(value(node.expression.value.id), where);
|
|
329
|
+
case "recursive": throw new Error(`operation Protobuf schema: '${where}' contains recursive values`);
|
|
330
|
+
case "string":
|
|
331
|
+
case "enum": return "string";
|
|
332
|
+
case "integer": return "sint64";
|
|
333
|
+
case "number": return "double";
|
|
334
|
+
case "boolean": return "bool";
|
|
335
|
+
case "literal": {
|
|
336
|
+
const literal = node.expression.value;
|
|
337
|
+
if (typeof literal === "string") return "string";
|
|
338
|
+
if (typeof literal === "boolean") return "bool";
|
|
339
|
+
if (typeof literal === "number" && Number.isInteger(literal)) return "sint64";
|
|
340
|
+
if (typeof literal === "number") return "double";
|
|
341
|
+
return ensureMessage(node, where);
|
|
342
|
+
}
|
|
343
|
+
case "null":
|
|
344
|
+
case "nullable":
|
|
345
|
+
case "record":
|
|
346
|
+
case "list":
|
|
347
|
+
case "map":
|
|
348
|
+
case "sum": return ensureMessage(node, where);
|
|
349
|
+
case "unknown": throw new Error(`operation Protobuf schema: '${where}' contains an unstructured value`);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const singularField = (type: string, name: string, number: number, comment: string): string => {
|
|
354
|
+
const scalar = type === "string" || type === "sint64" || type === "double" || type === "bool";
|
|
355
|
+
return ` // ${comment}\n ${scalar ? "optional " : ""}${type} ${name} = ${number};`;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/** A custody field that admits explicit null has three states: omitted,
|
|
359
|
+
* explicit null, and value. Proto3 scalar presence carries only two, so the
|
|
360
|
+
* generated contract needs an explicit envelope. Its identity is derived
|
|
361
|
+
* from the stable law field, never declaration order. */
|
|
362
|
+
const nullableFieldType = (
|
|
363
|
+
field: Extract<LawGraph["nodes"][number], { readonly kind: "field" }>,
|
|
364
|
+
inner: string,
|
|
365
|
+
where: string,
|
|
366
|
+
): string => {
|
|
367
|
+
const identity = operationMessageIdentity(
|
|
368
|
+
graph,
|
|
369
|
+
`${composedWireIdentity(paths, field.entity)}:nullable-result`,
|
|
370
|
+
);
|
|
371
|
+
const name = protoMessageName("NullableField", identity);
|
|
372
|
+
add(name, [
|
|
373
|
+
`message ${name} {`,
|
|
374
|
+
` // True means the lawfully authored value is explicitly null.`,
|
|
375
|
+
` oneof selected {`,
|
|
376
|
+
` bool is_null = 1;`,
|
|
377
|
+
` ${inner} value = 2;`,
|
|
378
|
+
` }`,
|
|
379
|
+
`}`,
|
|
380
|
+
].join("\n"));
|
|
381
|
+
return name;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/** The read shape of a `Surfaced(inner)` field is lawfully wider than its
|
|
385
|
+
* authored scalar: either one resolved value or the bounded observation set
|
|
386
|
+
* retained by the kernel. Generate that oneof from the field's stable law
|
|
387
|
+
* identity so static protoc bindings and the kernel's dynamic descriptor
|
|
388
|
+
* are the same contract. */
|
|
389
|
+
const surfacedFieldType = (
|
|
390
|
+
field: Extract<LawGraph["nodes"][number], { readonly kind: "field" }>,
|
|
391
|
+
inner: string,
|
|
392
|
+
where: string,
|
|
393
|
+
): string => {
|
|
394
|
+
const identity = operationMessageIdentity(
|
|
395
|
+
graph,
|
|
396
|
+
`${composedWireIdentity(paths, field.entity)}:surfaced-result`,
|
|
397
|
+
);
|
|
398
|
+
const observation = protoMessageName("SurfacedObservation", identity);
|
|
399
|
+
const pending = protoMessageName("SurfacedPending", identity);
|
|
400
|
+
const surfaced = protoMessageName("SurfacedField", identity);
|
|
401
|
+
add(observation, [
|
|
402
|
+
`message ${observation} {`,
|
|
403
|
+
singularField(inner, "value", 1, `${where} competing value.`),
|
|
404
|
+
` optional sint64 physical = 2;`,
|
|
405
|
+
` optional uint32 logical = 3;`,
|
|
406
|
+
` optional string replica = 4;`,
|
|
407
|
+
` optional string author = 5;`,
|
|
408
|
+
`}`,
|
|
409
|
+
].join("\n"));
|
|
410
|
+
add(pending, [
|
|
411
|
+
`message ${pending} {`,
|
|
412
|
+
` repeated ${observation} observations = 1;`,
|
|
413
|
+
`}`,
|
|
414
|
+
].join("\n"));
|
|
415
|
+
add(surfaced, [
|
|
416
|
+
`message ${surfaced} {`,
|
|
417
|
+
` oneof selected {`,
|
|
418
|
+
` ${inner} resolved = 1;`,
|
|
419
|
+
` ${pending} pending = 2;`,
|
|
420
|
+
` }`,
|
|
421
|
+
`}`,
|
|
422
|
+
].join("\n"));
|
|
423
|
+
return surfaced;
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
const structuralDeclared = (node: LawAnonymousValueTypeNode): LawTypeNode | undefined => {
|
|
427
|
+
const semantic = node.expression.semantic;
|
|
428
|
+
return semantic?.kind === "value-object"
|
|
429
|
+
? declaredValueObjects.get(semantic.name)
|
|
430
|
+
: undefined;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const ensureGeometryMessage = (name: string, geometry: GeometryKind): string => {
|
|
434
|
+
add(name, [
|
|
435
|
+
`message ${name} {`,
|
|
436
|
+
` // RFC 7946 coordinates flattened into packed Protobuf primitives.`,
|
|
437
|
+
` repeated double coordinates = 1 [packed = true];`,
|
|
438
|
+
...(geometry === "Point"
|
|
439
|
+
? []
|
|
440
|
+
: [` // Cumulative coordinate counts after each position.`, ` repeated uint32 position_ends = 2 [packed = true];`]),
|
|
441
|
+
...(geometry === "Polygon"
|
|
442
|
+
? [` // Cumulative position counts after each linear ring.`, ` repeated uint32 ring_ends = 3 [packed = true];`]
|
|
443
|
+
: []),
|
|
444
|
+
`}`,
|
|
445
|
+
].join("\n"));
|
|
446
|
+
return name;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
function ensureMessage(node: LawAnonymousValueTypeNode, where: string): string {
|
|
450
|
+
const geometry = structuralGeometryKind(nodes, node);
|
|
451
|
+
if (geometry !== undefined) {
|
|
452
|
+
return ensureGeometryMessage(
|
|
453
|
+
protoMessageName("Value", structuralWireIdentity(graph, node)),
|
|
454
|
+
geometry,
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const nodeSid = structuralWireIdentity(graph, node);
|
|
458
|
+
switch (node.expression.kind) {
|
|
459
|
+
case "default": return wireType(value(node.expression.value.id), where);
|
|
460
|
+
case "recursive": throw new Error(`operation Protobuf schema: '${where}' contains recursive values`);
|
|
461
|
+
case "null": {
|
|
462
|
+
const name = protoMessageName("Null", nodeSid);
|
|
463
|
+
add(name, `message ${name} {}`);
|
|
464
|
+
return name;
|
|
465
|
+
}
|
|
466
|
+
case "literal": {
|
|
467
|
+
if (node.expression.value !== null) return wireType(node, where);
|
|
468
|
+
const name = protoMessageName("Null", nodeSid);
|
|
469
|
+
add(name, `message ${name} {}`);
|
|
470
|
+
return name;
|
|
471
|
+
}
|
|
472
|
+
case "nullable": {
|
|
473
|
+
const name = protoMessageName("Nullable", nodeSid);
|
|
474
|
+
const inner = wireType(value(node.expression.value.id), `${where}.value`);
|
|
475
|
+
add(name, [
|
|
476
|
+
`message ${name} {`,
|
|
477
|
+
` oneof selected {`,
|
|
478
|
+
` bool is_null = 1;`,
|
|
479
|
+
` ${inner} value = 2;`,
|
|
480
|
+
` }`,
|
|
481
|
+
`}`,
|
|
482
|
+
].join("\n"));
|
|
483
|
+
return name;
|
|
484
|
+
}
|
|
485
|
+
case "record": {
|
|
486
|
+
const name = protoMessageName("Value", nodeSid);
|
|
487
|
+
const fields = node.expression.fields.map(field);
|
|
488
|
+
requireUniqueFieldNumbers(fields.map((item) => ({
|
|
489
|
+
label: item.name,
|
|
490
|
+
sid: composedWireIdentity(paths, item.entity),
|
|
491
|
+
})), where);
|
|
492
|
+
const lines = fields.map((item) => {
|
|
493
|
+
const sid = composedWireIdentity(paths, item.entity);
|
|
494
|
+
return singularField(
|
|
495
|
+
wireType(value(item.value.id), `${where}.${item.name}`),
|
|
496
|
+
protoFieldName(sid),
|
|
497
|
+
protobufFieldNumber(sid),
|
|
498
|
+
`${item.name}${item.required ? " (required by law)" : " (optional)"}`,
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
add(name, [`message ${name} {`, ...lines, `}`].join("\n"));
|
|
502
|
+
return name;
|
|
503
|
+
}
|
|
504
|
+
case "list": {
|
|
505
|
+
const name = protoMessageName("List", nodeSid);
|
|
506
|
+
const item = wireType(value(node.expression.element.id), `${where}[]`);
|
|
507
|
+
add(name, [`message ${name} {`, ` repeated ${item} items = 1;`, `}`].join("\n"));
|
|
508
|
+
return name;
|
|
509
|
+
}
|
|
510
|
+
case "map": {
|
|
511
|
+
const name = protoMessageName("Map", nodeSid);
|
|
512
|
+
const entry = protoMessageName("MapEntry", nodeSid);
|
|
513
|
+
const item = wireType(value(node.expression.value.id), `${where}.*`);
|
|
514
|
+
add(entry, [
|
|
515
|
+
`message ${entry} {`,
|
|
516
|
+
` optional string key = 1;`,
|
|
517
|
+
singularField(item, "value", 2, "Map value."),
|
|
518
|
+
`}`,
|
|
519
|
+
].join("\n"));
|
|
520
|
+
add(name, [`message ${name} {`, ` repeated ${entry} entries = 1;`, `}`].join("\n"));
|
|
521
|
+
return name;
|
|
522
|
+
}
|
|
523
|
+
case "sum": {
|
|
524
|
+
const name = protoMessageName("Sum", nodeSid);
|
|
525
|
+
const arms = nominalSumArms(nodes, node, where);
|
|
526
|
+
const lines = arms.map((arm) => {
|
|
527
|
+
const sid = composedWireIdentity(paths, arm.variant.entity);
|
|
528
|
+
return [
|
|
529
|
+
` // ${arm.discriminator}`,
|
|
530
|
+
` ${wireType(arm.value, `${where}.${arm.discriminator}`)} ${protoFieldName(sid)} = ${protobufFieldNumber(sid)};`,
|
|
531
|
+
].join("\n");
|
|
532
|
+
});
|
|
533
|
+
add(name, [`message ${name} {`, ` oneof choice {`, ...lines, ` }`, `}`].join("\n"));
|
|
534
|
+
return name;
|
|
535
|
+
}
|
|
536
|
+
case "string":
|
|
537
|
+
case "enum":
|
|
538
|
+
case "integer":
|
|
539
|
+
case "number":
|
|
540
|
+
case "boolean": return wireType(node, where);
|
|
541
|
+
case "unknown": throw new Error(`operation Protobuf schema: '${where}' contains an unstructured value`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function ensureDeclaredType(node: LawTypeNode, where: string): string {
|
|
546
|
+
if (node.form === "geometry") {
|
|
547
|
+
if (node.geometry === undefined) {
|
|
548
|
+
throw new Error(`operation Protobuf schema: '${where}' uses geometry without a declared kind`);
|
|
549
|
+
}
|
|
550
|
+
const name = protoMessageName("Value", node.entity.sid);
|
|
551
|
+
return ensureGeometryMessage(name, node.geometry);
|
|
552
|
+
}
|
|
553
|
+
if (node.form === "sum") {
|
|
554
|
+
const name = protoMessageName("Sum", node.entity.sid);
|
|
555
|
+
const arms = node.variants.map((ref) => {
|
|
556
|
+
const variant = nodes.get(ref.id);
|
|
557
|
+
if (variant?.kind !== "structural" || variant.structuralKind !== "value-variant" || variant.label === undefined) {
|
|
558
|
+
throw new Error(`operation Protobuf schema: '${where}' has an invalid declared sum arm '${ref.id}'`);
|
|
559
|
+
}
|
|
560
|
+
return variant;
|
|
561
|
+
});
|
|
562
|
+
requireUniqueFieldNumbers(
|
|
563
|
+
arms.map((arm) => ({ label: arm.label!, sid: composedWireIdentity(paths, arm.entity) })),
|
|
564
|
+
`${where} union`,
|
|
565
|
+
);
|
|
566
|
+
const lines = arms.map((arm) => {
|
|
567
|
+
const sid = composedWireIdentity(paths, arm.entity);
|
|
568
|
+
return [
|
|
569
|
+
` // ${arm.label}`,
|
|
570
|
+
` ${valueRef(arm.value, `${where}.${arm.label}`)} ${protoFieldName(sid)} = ${protobufFieldNumber(sid)};`,
|
|
571
|
+
].join("\n");
|
|
572
|
+
});
|
|
573
|
+
add(name, [`message ${name} {`, ` oneof choice {`, ...lines, ` }`, `}`].join("\n"));
|
|
574
|
+
return name;
|
|
575
|
+
}
|
|
576
|
+
const name = protoMessageName("Value", node.entity.sid);
|
|
577
|
+
const fields = node.fields.map((ref) => {
|
|
578
|
+
const field = nodes.get(ref.id);
|
|
579
|
+
if (field?.kind !== "field") {
|
|
580
|
+
throw new Error(`operation Protobuf schema: '${where}' references non-field '${ref.id}'`);
|
|
581
|
+
}
|
|
582
|
+
return field;
|
|
583
|
+
});
|
|
584
|
+
requireUniqueFieldNumbers(
|
|
585
|
+
fields.map((item) => ({ label: item.entity.label, sid: composedWireIdentity(paths, item.entity) })),
|
|
586
|
+
where,
|
|
587
|
+
);
|
|
588
|
+
const lines = fields.map((item) => {
|
|
589
|
+
const sid = composedWireIdentity(paths, item.entity);
|
|
590
|
+
return singularField(
|
|
591
|
+
valueRef(item.value, `${where}.${item.entity.label}`),
|
|
592
|
+
protoFieldName(sid),
|
|
593
|
+
protobufFieldNumber(sid),
|
|
594
|
+
`${item.entity.label}${item.required ? " (required by law)" : " (optional)"}`,
|
|
595
|
+
);
|
|
596
|
+
});
|
|
597
|
+
add(name, [`message ${name} {`, ...lines, `}`].join("\n"));
|
|
598
|
+
return name;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const selected = new Map<string, LawIntentNode>();
|
|
602
|
+
for (const intent of graph.nodes.filter(
|
|
603
|
+
(node): node is LawIntentNode => node.kind === "intent" && node.public,
|
|
604
|
+
)) {
|
|
605
|
+
selected.set(intent.entity.id, intent);
|
|
606
|
+
}
|
|
607
|
+
for (const directive of directives ?? []) {
|
|
608
|
+
// Use the same publisher resolution as both generated call encoders.
|
|
609
|
+
// A private owned-record route can publish through another intent; its
|
|
610
|
+
// transport message must match the payload the encoder actually sends.
|
|
611
|
+
const resolved = operation(graph, graph.domain.entity.label, directive);
|
|
612
|
+
selected.set(resolved.intent.entity.id, resolved.intent);
|
|
613
|
+
}
|
|
614
|
+
for (const intent of selected.values()) {
|
|
615
|
+
const contract = nodes.get(intent.accepts.id);
|
|
616
|
+
if (contract?.kind !== "payload-contract") continue;
|
|
617
|
+
const name = protoMessageName("Payload", operationMessageIdentity(graph, contract.entity.sid));
|
|
618
|
+
const fields = contract.fields.map(field);
|
|
619
|
+
requireUniqueFieldNumbers(fields.map((item) => ({
|
|
620
|
+
label: item.name,
|
|
621
|
+
sid: composedWireIdentity(paths, item.entity),
|
|
622
|
+
})), `${graph.domain.entity.label}/${intent.entity.label}`);
|
|
623
|
+
const lines = fields.map((item) => {
|
|
624
|
+
const sid = composedWireIdentity(paths, item.entity);
|
|
625
|
+
return singularField(
|
|
626
|
+
wireType(value(item.value.id), `${intent.entity.label}.${item.name}`),
|
|
627
|
+
protoFieldName(sid),
|
|
628
|
+
protobufFieldNumber(sid),
|
|
629
|
+
`${item.name}${item.required ? " (required by law)" : " (optional)"}`,
|
|
630
|
+
);
|
|
631
|
+
});
|
|
632
|
+
add(name, [`message ${name} {`, ...lines, `}`].join("\n"));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
for (const query of graph.nodes.filter(
|
|
636
|
+
(node): node is LawRowReadNode =>
|
|
637
|
+
node.kind === "read" && (node.readKind === "query" || node.readKind === "ordered"),
|
|
638
|
+
)) {
|
|
639
|
+
const fields = (query.readKind === "query" ? query.key : []).map((key) => {
|
|
640
|
+
const root = nodes.get(key.field.id);
|
|
641
|
+
if (root?.kind !== "field") {
|
|
642
|
+
throw new Error(
|
|
643
|
+
`query Protobuf schema: '${graph.domain.entity.label}/${query.entity.label}' ` +
|
|
644
|
+
`references non-field '${key.field.id}'`,
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
const terminalRef = key.path.at(-1);
|
|
648
|
+
const terminal = terminalRef === undefined ? root : nodes.get(terminalRef.id);
|
|
649
|
+
const terminalValue = terminal?.kind === "field" ||
|
|
650
|
+
(terminal?.kind === "structural" && terminal.structuralKind === "value-field")
|
|
651
|
+
? terminal.value
|
|
652
|
+
: undefined;
|
|
653
|
+
const sid = terminal !== undefined && "entity" in terminal
|
|
654
|
+
? composedWireIdentity(paths, terminal.entity)
|
|
655
|
+
: composedWireIdentity(paths, root.entity);
|
|
656
|
+
const scalar = (() => {
|
|
657
|
+
if (terminal?.kind === "structural" && terminal.structuralKind === "value-variant") {
|
|
658
|
+
return "string";
|
|
659
|
+
}
|
|
660
|
+
if (terminalValue !== undefined) {
|
|
661
|
+
let candidate = nodes.get(terminalValue.id);
|
|
662
|
+
const seen = new Set<string>();
|
|
663
|
+
while (
|
|
664
|
+
candidate?.kind === "structural" && candidate.structuralKind === "value-type" &&
|
|
665
|
+
(candidate.expression.kind === "default" || candidate.expression.kind === "nullable" || candidate.expression.kind === "recursive")
|
|
666
|
+
) {
|
|
667
|
+
if (seen.has(candidate.entity.id)) break;
|
|
668
|
+
seen.add(candidate.entity.id);
|
|
669
|
+
candidate = nodes.get(candidate.expression.value.id);
|
|
670
|
+
}
|
|
671
|
+
if (candidate?.kind === "structural" && candidate.structuralKind === "value-type") {
|
|
672
|
+
switch (candidate.expression.kind) {
|
|
673
|
+
case "integer": return "sint64";
|
|
674
|
+
case "number": return "double";
|
|
675
|
+
case "boolean": return "bool";
|
|
676
|
+
case "string": case "enum": case "literal": return "string";
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
switch (root.storageKind) {
|
|
681
|
+
case "int": case "counter": return "sint64";
|
|
682
|
+
case "bool": return "bool";
|
|
683
|
+
case "string": case "enum": case "ref": case "set": return "string";
|
|
684
|
+
}
|
|
685
|
+
throw new Error(
|
|
686
|
+
`query Protobuf schema: '${graph.domain.entity.label}/${query.entity.label}' ` +
|
|
687
|
+
`cannot index '${key.authoredPath}' as a scalar law value`,
|
|
688
|
+
);
|
|
689
|
+
})();
|
|
690
|
+
return {
|
|
691
|
+
label: key.authoredPath,
|
|
692
|
+
sid,
|
|
693
|
+
line: singularField(
|
|
694
|
+
scalar,
|
|
695
|
+
protoFieldName(sid),
|
|
696
|
+
protobufFieldNumber(sid),
|
|
697
|
+
`${key.authoredPath} (required query key)`,
|
|
698
|
+
),
|
|
699
|
+
};
|
|
700
|
+
});
|
|
701
|
+
requireUniqueFieldNumbers(fields, `${graph.domain.entity.label}/${query.entity.label} query`);
|
|
702
|
+
const name = protoMessageName("Query", operationMessageIdentity(graph, query.entity.sid));
|
|
703
|
+
add(name, [`message ${name} {`, ...fields.map((field) => field.line), `}`].join("\n"));
|
|
704
|
+
|
|
705
|
+
const contract = nodes.get(query.contract.id);
|
|
706
|
+
if (contract?.kind !== "read-contract" || contract.returns.kind !== "rows") {
|
|
707
|
+
throw new Error(
|
|
708
|
+
`query Protobuf schema: '${graph.domain.entity.label}/${query.entity.label}' has no row result contract`,
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
const aggregate = nodes.get(contract.returns.aggregate.id);
|
|
712
|
+
if (aggregate?.kind !== "aggregate") {
|
|
713
|
+
throw new Error(
|
|
714
|
+
`query Protobuf schema: '${graph.domain.entity.label}/${query.entity.label}' returns no aggregate`,
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
const rowName = protoMessageName("Read", operationMessageIdentity(graph, aggregate.entity.sid));
|
|
718
|
+
const rowFields: Array<{ readonly label: string; readonly sid: string; readonly line: string }> = [];
|
|
719
|
+
const idSid = `${aggregate.entity.sid}:result:id`;
|
|
720
|
+
rowFields.push({
|
|
721
|
+
label: "id",
|
|
722
|
+
sid: idSid,
|
|
723
|
+
line: singularField("string", protoFieldName(idSid), protobufFieldNumber(idSid), "Stable aggregate id."),
|
|
724
|
+
});
|
|
725
|
+
for (const fieldRef of aggregate.fields) {
|
|
726
|
+
const field = nodes.get(fieldRef.id);
|
|
727
|
+
if (field?.kind !== "field") {
|
|
728
|
+
throw new Error(`query Protobuf schema: '${aggregate.entity.label}' references non-field '${fieldRef.id}'`);
|
|
729
|
+
}
|
|
730
|
+
const sid = composedWireIdentity(paths, field.entity);
|
|
731
|
+
const where = `${aggregate.entity.label}.${field.entity.label}`;
|
|
732
|
+
const authored = valueRef(field.value, where);
|
|
733
|
+
const inner = field.mergeDrivers[0]?.kind === "Surfaced"
|
|
734
|
+
? surfacedFieldType(field, authored, where)
|
|
735
|
+
: authored;
|
|
736
|
+
const type = field.nullable ? nullableFieldType(field, inner, where) : inner;
|
|
737
|
+
rowFields.push({
|
|
738
|
+
label: field.entity.label,
|
|
739
|
+
sid,
|
|
740
|
+
line: singularField(type, protoFieldName(sid), protobufFieldNumber(sid), `${field.entity.label}${field.required ? " (required by law)" : " (optional)"}`),
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
for (const read of graph.nodes.filter(
|
|
744
|
+
(node): node is LawReadNode =>
|
|
745
|
+
node.kind === "read" &&
|
|
746
|
+
node.of.id === aggregate.entity.id &&
|
|
747
|
+
(node.readKind === "derived" || node.readKind === "temporalEnum" || node.readKind === "combined"),
|
|
748
|
+
)) {
|
|
749
|
+
const readContract = nodes.get(read.contract.id);
|
|
750
|
+
if (readContract?.kind !== "read-contract" || readContract.returns.kind !== "scalar") {
|
|
751
|
+
throw new Error(`query Protobuf schema: computed read '${read.entity.label}' has no scalar contract`);
|
|
752
|
+
}
|
|
753
|
+
const sid = composedWireIdentity(paths, read.entity);
|
|
754
|
+
rowFields.push({
|
|
755
|
+
label: read.entity.label,
|
|
756
|
+
sid,
|
|
757
|
+
line: singularField(
|
|
758
|
+
valueRef(readContract.returns.value, `${aggregate.entity.label}.${read.entity.label}`),
|
|
759
|
+
protoFieldName(sid),
|
|
760
|
+
protobufFieldNumber(sid),
|
|
761
|
+
`${read.entity.label} (computed read)`,
|
|
762
|
+
),
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
const attributed = aggregate.fields.map(ref => nodes.get(ref.id)).filter(
|
|
766
|
+
(node): node is Extract<LawGraph["nodes"][number], { kind: "field" }> =>
|
|
767
|
+
node?.kind === "field" && node.mergeDrivers[0]?.kind === "Attributed",
|
|
768
|
+
);
|
|
769
|
+
if (attributed.length > 0) {
|
|
770
|
+
add("ProvenanceAuthor", `message ProvenanceAuthor { oneof selected { bool is_null = 1; string value = 2; } }`);
|
|
771
|
+
add("FieldProvenance", [
|
|
772
|
+
`message FieldProvenance {`,
|
|
773
|
+
singularField("ProvenanceAuthor", protoFieldName(provenanceAuthorIdentity), protobufFieldNumber(provenanceAuthorIdentity), "Verified author, explicitly null when unknown."),
|
|
774
|
+
singularField("bool", protoFieldName(provenanceContestedIdentity), protobufFieldNumber(provenanceContestedIdentity), "Whether the current value displaced a concurrent version."),
|
|
775
|
+
`}`,
|
|
776
|
+
].join("\n"));
|
|
777
|
+
const sid = provenanceRowIdentity(aggregate.entity.sid);
|
|
778
|
+
const name = protoMessageName("ReadProvenance", operationMessageIdentity(graph, sid));
|
|
779
|
+
const members = attributed.map(field => ({
|
|
780
|
+
label: field.entity.label, sid: provenanceFieldIdentity(composedWireIdentity(paths, field.entity)),
|
|
781
|
+
}));
|
|
782
|
+
requireUniqueFieldNumbers(members, `${aggregate.entity.label} provenance`);
|
|
783
|
+
add(name, [`message ${name} {`, ...members.map(field => singularField(
|
|
784
|
+
"FieldProvenance", protoFieldName(field.sid), protobufFieldNumber(field.sid), field.label,
|
|
785
|
+
)), `}`].join("\n"));
|
|
786
|
+
rowFields.push({ label: "__prov", sid, line: singularField(name, protoFieldName(sid), protobufFieldNumber(sid), "Existing per-field provenance, absent before attribution.") });
|
|
787
|
+
}
|
|
788
|
+
requireUniqueFieldNumbers(rowFields, `${graph.domain.entity.label}/${aggregate.entity.label} result`);
|
|
789
|
+
add(rowName, [`message ${rowName} {`, ...rowFields.map((field) => field.line), `}`].join("\n"));
|
|
790
|
+
|
|
791
|
+
const resultName = protoMessageName("QueryResult", operationMessageIdentity(graph, query.entity.sid));
|
|
792
|
+
const rowsSid = `${query.entity.sid}:result:rows`;
|
|
793
|
+
const cursorSid = `${query.entity.sid}:result:cursor`;
|
|
794
|
+
const doneSid = `${query.entity.sid}:result:done`;
|
|
795
|
+
requireUniqueFieldNumbers([
|
|
796
|
+
{ label: "rows", sid: rowsSid },
|
|
797
|
+
{ label: "cursor", sid: cursorSid },
|
|
798
|
+
{ label: "done", sid: doneSid },
|
|
799
|
+
], `${graph.domain.entity.label}/${query.entity.label} result envelope`);
|
|
800
|
+
add(resultName, [
|
|
801
|
+
`message ${resultName} {`,
|
|
802
|
+
` repeated ${rowName} ${protoFieldName(rowsSid)} = ${protobufFieldNumber(rowsSid)};`,
|
|
803
|
+
singularField("string", protoFieldName(cursorSid), protobufFieldNumber(cursorSid), "Opaque continuation cursor when paging."),
|
|
804
|
+
singularField("bool", protoFieldName(doneSid), protobufFieldNumber(doneSid), "Whether the page reached the current frontier."),
|
|
805
|
+
`}`,
|
|
806
|
+
].join("\n"));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
return {
|
|
811
|
+
fileName: "operation_payloads.proto",
|
|
812
|
+
dartFileName: "operation_payloads.pb.dart",
|
|
813
|
+
source: [
|
|
814
|
+
`syntax = "proto3";`,
|
|
815
|
+
`package nomos.operation.v1;`,
|
|
816
|
+
``,
|
|
817
|
+
`// GENERATED from the composed Nomos law graph. DO NOT EDIT.`,
|
|
818
|
+
`// Field numbers are permanent stable-law identities; names are diagnostic only.`,
|
|
819
|
+
``,
|
|
820
|
+
...[...definitions.entries()]
|
|
821
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
822
|
+
.flatMap(([, definition]) => [definition, ``]),
|
|
823
|
+
].join("\n"),
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
export const TS_CALL_PROTO_PRELUDE = [
|
|
828
|
+
`declare const _nmsGeneratedCallBrand: unique symbol;`,
|
|
829
|
+
`interface NmsGeneratedCall<Domain extends string = string, Directive extends string = string> {`,
|
|
830
|
+
` readonly domain: Domain;`,
|
|
831
|
+
` readonly directive: Directive;`,
|
|
832
|
+
` readonly protobufType: string;`,
|
|
833
|
+
` readonly protobufPayload: Uint8Array;`,
|
|
834
|
+
` readonly routingCandidates: readonly string[];`,
|
|
835
|
+
` readonly [_nmsGeneratedCallBrand]: readonly [Domain, Directive];`,
|
|
836
|
+
`}`,
|
|
837
|
+
`const _nmsGeneratedCall = <Domain extends string, Directive extends string>(domain: Domain, directive: Directive, protobufType: string, protobufPayload: Uint8Array, routingCandidates: readonly string[]): NmsGeneratedCall<Domain, Directive> =>`,
|
|
838
|
+
` ({ domain, directive, protobufType, protobufPayload, routingCandidates: [...new Set(routingCandidates)] }) as unknown as NmsGeneratedCall<Domain, Directive>;`,
|
|
839
|
+
`const _nmsProtoString = (value: string, routingCandidates: string[]): string => {`,
|
|
840
|
+
` routingCandidates.push(value);`,
|
|
841
|
+
` return value;`,
|
|
842
|
+
`};`,
|
|
843
|
+
`const _nmsProtoInt = (value: number): bigint => {`,
|
|
844
|
+
` if (!Number.isSafeInteger(value)) throw new TypeError("Nomos integers must be safe integers");`,
|
|
845
|
+
` return BigInt(value);`,
|
|
846
|
+
`};`,
|
|
847
|
+
`const _nmsProtoNumber = (value: number): number => {`,
|
|
848
|
+
` if (!Number.isFinite(value)) throw new TypeError("Nomos numbers must be finite");`,
|
|
849
|
+
` return value;`,
|
|
850
|
+
`};`,
|
|
851
|
+
``,
|
|
852
|
+
] as const;
|
|
853
|
+
|
|
854
|
+
export const DART_CALL_PROTO_PRELUDE = [
|
|
855
|
+
`Int64 _nmsProtoInt(num value) {`,
|
|
856
|
+
` if (!value.isFinite || value != value.truncateToDouble() || value.abs() > 9007199254740991) {`,
|
|
857
|
+
` throw ArgumentError.value(value, 'value', 'Nomos integers must be safe integers');`,
|
|
858
|
+
` }`,
|
|
859
|
+
` return Int64(value.toInt());`,
|
|
860
|
+
`}`,
|
|
861
|
+
`double _nmsProtoNumber(num value) {`,
|
|
862
|
+
` final result = value.toDouble();`,
|
|
863
|
+
` if (!result.isFinite) throw ArgumentError.value(value, 'value', 'Nomos numbers must be finite');`,
|
|
864
|
+
` return result;`,
|
|
865
|
+
`}`,
|
|
866
|
+
``,
|
|
867
|
+
] as const;
|
|
868
|
+
|
|
869
|
+
function tsCanonicalValue(
|
|
870
|
+
value: string,
|
|
871
|
+
node: LawAnonymousValueTypeNode,
|
|
872
|
+
workspace: string,
|
|
873
|
+
field: string,
|
|
874
|
+
valueIsCanonical = false,
|
|
875
|
+
): string {
|
|
876
|
+
const semantic = node.expression.semantic;
|
|
877
|
+
if (semantic === undefined || valueIsCanonical) return value;
|
|
878
|
+
switch (semantic.kind) {
|
|
879
|
+
case "id-reference": return `_nmsReferenceId(${value}, ${JSON.stringify(semantic.target)}, ${semantic.targetKind === "contract" ? '\"\"' : workspace}, ${JSON.stringify(field)})`;
|
|
880
|
+
case "address-reference": return `_nmsCheckedReferenceData(${value}, ${JSON.stringify(semantic.target)}, "", ${JSON.stringify(field)})`;
|
|
881
|
+
case "versioned-reference": return `({ pinned: _nmsReferenceData(${value}.pinned), series: _nmsReferenceData(${value}.series) })`;
|
|
882
|
+
case "recipient": return `_nmsRecipientAddress(${value}, ${JSON.stringify(field)})`;
|
|
883
|
+
default: return value;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function dartCanonicalValue(
|
|
888
|
+
value: string,
|
|
889
|
+
node: LawAnonymousValueTypeNode,
|
|
890
|
+
valueIsCanonical = false,
|
|
891
|
+
): string {
|
|
892
|
+
const semantic = node.expression.semantic;
|
|
893
|
+
if (semantic === undefined || valueIsCanonical) return value;
|
|
894
|
+
switch (semantic.kind) {
|
|
895
|
+
case "id-reference": return `NmsInternalNomosRefs.id(${value})`;
|
|
896
|
+
case "address-reference": return `(workspace: NmsInternalNomosRefs.workspace(${value}), type: NmsInternalNomosRefs.type(${value}), id: NmsInternalNomosRefs.id(${value}))`;
|
|
897
|
+
case "versioned-reference": return `(pinned: (workspace: NmsInternalNomosRefs.workspace(${value}.pinned), type: NmsInternalNomosRefs.type(${value}.pinned), id: NmsInternalNomosRefs.id(${value}.pinned)), series: (workspace: NmsInternalNomosRefs.workspace(${value}.series), type: NmsInternalNomosRefs.type(${value}.series), id: NmsInternalNomosRefs.id(${value}.series)))`;
|
|
898
|
+
case "recipient": return `NmsInternalRecipients.address(${value})`;
|
|
899
|
+
case "sha256-digest":
|
|
900
|
+
case "git-object-id":
|
|
901
|
+
case "branded-id":
|
|
902
|
+
case "unit": return `${value}.value`;
|
|
903
|
+
default: return value;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/** Emit one exact Protobuf operation encoder from the same graph as law.usda. */
|
|
908
|
+
export function emitTsCallProtoEncoder(
|
|
909
|
+
graph: LawGraph,
|
|
910
|
+
paths: ReadonlyMap<string, string>,
|
|
911
|
+
domain: string,
|
|
912
|
+
directive: string,
|
|
913
|
+
payloadType: string,
|
|
914
|
+
access?: ReadonlyMap<string, TsCallProtoFieldAccess>,
|
|
915
|
+
): CallProtoEncoder {
|
|
916
|
+
const spec = operation(graph, domain, directive);
|
|
917
|
+
const fields = spec.contract.fields.map((ref) => fieldNode(spec.nodes, ref));
|
|
918
|
+
requireUniqueFieldNumbers(fields.map((field) => ({
|
|
919
|
+
label: field.name,
|
|
920
|
+
sid: composedWireIdentity(paths, field.entity),
|
|
921
|
+
})), `${domain}/${directive}`);
|
|
922
|
+
let local = 0;
|
|
923
|
+
const fresh = (prefix: string): string => `_${prefix}${local++}`;
|
|
924
|
+
|
|
925
|
+
/** Build a MessageInitShape expression for protoc-gen-es. Nested records,
|
|
926
|
+
* lists, maps and unions stay typed objects until the maintained Protobuf
|
|
927
|
+
* runtime serializes the one top-level generated message. */
|
|
928
|
+
const emitValue = (raw: string, typeId: string, where: string, valueIsCanonical = false): string => {
|
|
929
|
+
const node = typeNode(spec.nodes, typeId);
|
|
930
|
+
const value = tsCanonicalValue(raw, node, "nmsInternalWorkspace", where, valueIsCanonical);
|
|
931
|
+
const geometry = structuralGeometryKind(spec.nodes, node);
|
|
932
|
+
if (geometry !== undefined) {
|
|
933
|
+
const selected = fresh("geometry");
|
|
934
|
+
const coordinates = fresh("coordinates");
|
|
935
|
+
if (geometry === "Point") {
|
|
936
|
+
return `(() => { const ${selected} = ${value}; return { coordinates: [...${selected}.coordinates] }; })()`;
|
|
937
|
+
}
|
|
938
|
+
const positions = fresh("positionEnds");
|
|
939
|
+
if (geometry === "Polygon") {
|
|
940
|
+
const rings = fresh("ringEnds");
|
|
941
|
+
const ring = fresh("ring");
|
|
942
|
+
const position = fresh("position");
|
|
943
|
+
return `(() => { const ${selected} = ${value}; const ${coordinates}: number[] = []; const ${positions}: number[] = []; const ${rings}: number[] = []; let _nmsPositionCount = 0; for (const ${ring} of ${selected}.coordinates) { for (const ${position} of ${ring}) { ${coordinates}.push(...${position}); ${positions}.push(${coordinates}.length); _nmsPositionCount += 1; } ${rings}.push(_nmsPositionCount); } return { coordinates: ${coordinates}, positionEnds: ${positions}, ringEnds: ${rings} }; })()`;
|
|
944
|
+
}
|
|
945
|
+
const position = fresh("position");
|
|
946
|
+
return `(() => { const ${selected} = ${value}; const ${coordinates}: number[] = []; const ${positions}: number[] = []; for (const ${position} of ${selected}.coordinates) { ${coordinates}.push(...${position}); ${positions}.push(${coordinates}.length); } return { coordinates: ${coordinates}, positionEnds: ${positions} }; })()`;
|
|
947
|
+
}
|
|
948
|
+
switch (node.expression.kind) {
|
|
949
|
+
case "default": return emitValue(value, node.expression.value.id, where);
|
|
950
|
+
case "recursive": throw new Error(`call Protobuf codegen: '${domain}/${directive}' contains recursive values`);
|
|
951
|
+
case "nullable": {
|
|
952
|
+
const selected = fresh("nullable");
|
|
953
|
+
return `(() => { const ${selected} = ${value}; return ${selected} === null ? { selected: { case: "isNull" as const, value: true } } : { selected: { case: "value" as const, value: ${emitValue(selected, node.expression.value.id, where)} } }; })()`;
|
|
954
|
+
}
|
|
955
|
+
case "string":
|
|
956
|
+
case "enum":
|
|
957
|
+
return `_nmsProtoString(${value}, _nmsRoutingCandidates)`;
|
|
958
|
+
case "integer": return `_nmsProtoInt(${value})`;
|
|
959
|
+
case "number": return `_nmsProtoNumber(${value})`;
|
|
960
|
+
case "boolean": return value;
|
|
961
|
+
case "null": return `{}`;
|
|
962
|
+
case "literal": {
|
|
963
|
+
const literal = JSON.stringify(node.expression.value);
|
|
964
|
+
if (typeof node.expression.value === "string") return `_nmsProtoString(${literal}, _nmsRoutingCandidates)`;
|
|
965
|
+
if (typeof node.expression.value === "boolean") return literal;
|
|
966
|
+
if (typeof node.expression.value === "number" && Number.isInteger(node.expression.value)) return `BigInt(${literal})`;
|
|
967
|
+
if (typeof node.expression.value === "number") return literal;
|
|
968
|
+
return `{}`;
|
|
969
|
+
}
|
|
970
|
+
case "record": {
|
|
971
|
+
const recordFields = node.expression.fields.map((ref) => fieldNode(spec.nodes, ref));
|
|
972
|
+
requireUniqueFieldNumbers(recordFields.map((item) => ({
|
|
973
|
+
label: item.name,
|
|
974
|
+
sid: composedWireIdentity(paths, item.entity),
|
|
975
|
+
})), where);
|
|
976
|
+
const selected = fresh("record");
|
|
977
|
+
const properties = recordFields.map((child) => {
|
|
978
|
+
const childRaw = `${selected}[${JSON.stringify(child.name)}]`;
|
|
979
|
+
const fieldName = protoFieldName(composedWireIdentity(paths, child.entity));
|
|
980
|
+
const encoded = emitValue(child.required ? childRaw : `(${childRaw})!`, child.value.id, `${where}.${child.name}`);
|
|
981
|
+
return `${fieldName}: ${child.required ? encoded : `${childRaw} === undefined ? undefined : ${encoded}`}`;
|
|
982
|
+
});
|
|
983
|
+
return `(() => { const ${selected} = (${value})!; return { ${properties.join(", ")} }; })()`;
|
|
984
|
+
}
|
|
985
|
+
case "list": {
|
|
986
|
+
const item = fresh("item");
|
|
987
|
+
// Parenthesize the nested expression: when the list element is itself
|
|
988
|
+
// a generated message initializer (`{ items: ... }`), an unwrapped
|
|
989
|
+
// arrow body is parsed as a statement block and silently becomes
|
|
990
|
+
// `void[]` instead of the declared nested value.
|
|
991
|
+
return `{ items: ${value}.map((${item}) => (${emitValue(item, node.expression.element.id, `${where}[]`)})) }`;
|
|
992
|
+
}
|
|
993
|
+
case "map": {
|
|
994
|
+
const entries = fresh("entries");
|
|
995
|
+
const key = fresh("key");
|
|
996
|
+
const item = fresh("item");
|
|
997
|
+
return `(() => { const ${entries} = Object.entries(${value}).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0); return { entries: ${entries}.map(([${key}, ${item}]) => ({ key: _nmsProtoString(${key}, _nmsRoutingCandidates), value: ${emitValue(item, node.expression.value.id, `${where}.*`)} })) }; })()`;
|
|
998
|
+
}
|
|
999
|
+
case "sum": {
|
|
1000
|
+
const arms = nominalSumArms(spec.nodes, node, `${domain}/${directive} ${where}`);
|
|
1001
|
+
const selected = fresh("sum");
|
|
1002
|
+
const cases: string[] = [];
|
|
1003
|
+
for (const arm of arms) {
|
|
1004
|
+
const fieldName = protoFieldName(composedWireIdentity(paths, arm.variant.entity));
|
|
1005
|
+
cases.push(`case ${JSON.stringify(arm.discriminator)}: return { choice: { case: ${JSON.stringify(fieldName)} as const, value: ${emitValue(selected, arm.value.entity.id, `${where}.${arm.discriminator}`)} } };`);
|
|
1006
|
+
}
|
|
1007
|
+
return `(() => { const ${selected} = ${value}; switch (${selected}.type) { ${cases.join(" ")} default: throw new TypeError("Unknown nominal union arm: " + String((${selected} as unknown as { readonly type?: unknown }).type)); } })()`;
|
|
1008
|
+
}
|
|
1009
|
+
case "unknown": throw new Error(`call Protobuf codegen: '${domain}/${directive}' contains an unstructured value at ${where}`);
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
const messageName = protoMessageName("Payload", operationMessageIdentity(graph, spec.contract.entity.sid));
|
|
1014
|
+
const schema = `_nmsop.${messageName}Schema`;
|
|
1015
|
+
const properties: string[] = [];
|
|
1016
|
+
const lines = [
|
|
1017
|
+
`function _nmsCallProto_${symbol(domain)}_${symbol(directive)}(payload: ${payloadType}, nmsInternalWorkspace: string): NmsGeneratedCall<${JSON.stringify(domain)}, ${JSON.stringify(spec.publicDirective)}> {`,
|
|
1018
|
+
` const _nmsRoutingCandidates: string[] = [];`,
|
|
1019
|
+
];
|
|
1020
|
+
for (const field of fields) {
|
|
1021
|
+
const fieldAccess = access?.get(field.name);
|
|
1022
|
+
if (fieldAccess?.present === "false") continue;
|
|
1023
|
+
const raw = fieldAccess?.value ?? `payload[${JSON.stringify(field.name)}]`;
|
|
1024
|
+
const fieldName = protoFieldName(composedWireIdentity(paths, field.entity));
|
|
1025
|
+
if (field.required) properties.push(`${fieldName}: ${emitValue(raw, field.value.id, field.name, fieldAccess?.canonical === true)}`);
|
|
1026
|
+
else {
|
|
1027
|
+
const optional = fresh("optional");
|
|
1028
|
+
lines.push(` const ${optional} = ${raw};`);
|
|
1029
|
+
properties.push(`${fieldName}: ${optional} !== undefined && (${fieldAccess?.present ?? "true"}) ? ${emitValue(`${optional}!`, field.value.id, field.name, fieldAccess?.canonical === true)} : undefined`);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
lines.push(
|
|
1033
|
+
` const message = create(${schema}, { ${properties.join(", ")} });`,
|
|
1034
|
+
` return _nmsGeneratedCall(${JSON.stringify(domain)}, ${JSON.stringify(spec.publicDirective)}, ${JSON.stringify(spec.typeUrl)}, toBinary(${schema}, message), _nmsRoutingCandidates);`,
|
|
1035
|
+
`}`,
|
|
1036
|
+
``,
|
|
1037
|
+
);
|
|
1038
|
+
return {
|
|
1039
|
+
functionName: `_nmsCallProto_${symbol(domain)}_${symbol(directive)}`,
|
|
1040
|
+
typeUrl: spec.typeUrl,
|
|
1041
|
+
publicDirective: spec.publicDirective,
|
|
1042
|
+
source: lines,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const dartString = (value: string): string =>
|
|
1047
|
+
`'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\$/g, "\\$")}'`;
|
|
1048
|
+
|
|
1049
|
+
/** Dart twin of {@link emitTsCallProtoEncoder}; both derive the same field identities. */
|
|
1050
|
+
export function emitDartCallProtoEncoder(
|
|
1051
|
+
graph: LawGraph,
|
|
1052
|
+
paths: ReadonlyMap<string, string>,
|
|
1053
|
+
domain: string,
|
|
1054
|
+
directive: string,
|
|
1055
|
+
payloadType: string,
|
|
1056
|
+
access: ReadonlyMap<string, DartCallProtoFieldAccess>,
|
|
1057
|
+
extraParameters: readonly string[] = [],
|
|
1058
|
+
): CallProtoEncoder {
|
|
1059
|
+
const spec = operation(graph, domain, directive);
|
|
1060
|
+
const fields = spec.contract.fields.map((ref) => fieldNode(spec.nodes, ref));
|
|
1061
|
+
requireUniqueFieldNumbers(fields.map((field) => ({
|
|
1062
|
+
label: field.name,
|
|
1063
|
+
sid: composedWireIdentity(paths, field.entity),
|
|
1064
|
+
})), `${domain}/${directive}`);
|
|
1065
|
+
let local = 0;
|
|
1066
|
+
const fresh = (prefix: string): string => `_${prefix}${local++}`;
|
|
1067
|
+
const structuralProtoName = (kind: string, node: LawAnonymousValueTypeNode): string =>
|
|
1068
|
+
protoMessageName(kind, structuralWireIdentity(graph, node));
|
|
1069
|
+
const emitValue = (
|
|
1070
|
+
out: string[],
|
|
1071
|
+
raw: string,
|
|
1072
|
+
typeId: string,
|
|
1073
|
+
where: string,
|
|
1074
|
+
accept: (expression: string) => void,
|
|
1075
|
+
valueIsCanonical = false,
|
|
1076
|
+
): void => {
|
|
1077
|
+
const node = typeNode(spec.nodes, typeId);
|
|
1078
|
+
const semantic = node.expression.semantic;
|
|
1079
|
+
const lowersNominalReference = semantic?.kind === "id-reference" ||
|
|
1080
|
+
semantic?.kind === "address-reference" ||
|
|
1081
|
+
semantic?.kind === "versioned-reference" ||
|
|
1082
|
+
semantic?.kind === "recipient";
|
|
1083
|
+
const value = dartCanonicalValue(raw, node, valueIsCanonical);
|
|
1084
|
+
const geometry = structuralGeometryKind(spec.nodes, node);
|
|
1085
|
+
if (geometry !== undefined) {
|
|
1086
|
+
const message = fresh("geometryMessage");
|
|
1087
|
+
const position = fresh("position");
|
|
1088
|
+
out.push(`final ${message} = _nmsop.${structuralProtoName("Value", node)}();`);
|
|
1089
|
+
if (geometry === "Point") {
|
|
1090
|
+
out.push(`${message}.coordinates.addAll(${value}.coordinates.map((coordinate) => coordinate.toDouble()));`);
|
|
1091
|
+
} else if (geometry === "Polygon") {
|
|
1092
|
+
const ring = fresh("ring");
|
|
1093
|
+
const count = fresh("positionCount");
|
|
1094
|
+
out.push(`var ${count} = 0;`, `for (final ${ring} in ${value}.coordinates) {`, ` for (final ${position} in ${ring}) {`, ` ${message}.coordinates.addAll(${position}.map((coordinate) => coordinate.toDouble()));`, ` ${message}.positionEnds.add(${message}.coordinates.length);`, ` ${count} += 1;`, ` }`, ` ${message}.ringEnds.add(${count});`, `}`);
|
|
1095
|
+
} else {
|
|
1096
|
+
out.push(`for (final ${position} in ${value}.coordinates) {`, ` ${message}.coordinates.addAll(${position}.map((coordinate) => coordinate.toDouble()));`, ` ${message}.positionEnds.add(${message}.coordinates.length);`, `}`);
|
|
1097
|
+
}
|
|
1098
|
+
accept(message);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
switch (node.expression.kind) {
|
|
1102
|
+
case "default": emitValue(out, value, node.expression.value.id, where, accept); return;
|
|
1103
|
+
case "recursive": throw new Error(`Dart call Protobuf codegen: '${domain}/${directive}' contains recursive values`);
|
|
1104
|
+
case "nullable": {
|
|
1105
|
+
const selected = fresh("nullable");
|
|
1106
|
+
const message = fresh("nullableMessage");
|
|
1107
|
+
out.push(
|
|
1108
|
+
`final ${selected} = ${value};`,
|
|
1109
|
+
`final ${message} = _nmsop.${structuralProtoName("Nullable", node)}();`,
|
|
1110
|
+
`if (${selected} == null) {`,
|
|
1111
|
+
` ${message}.isNull = true;`,
|
|
1112
|
+
`} else {`,
|
|
1113
|
+
);
|
|
1114
|
+
const body: string[] = [];
|
|
1115
|
+
emitValue(body, selected, node.expression.value.id, where, (expression) => {
|
|
1116
|
+
body.push(` ${message}.value = ${expression};`);
|
|
1117
|
+
});
|
|
1118
|
+
out.push(...body.map((line) => ` ${line}`), `}`);
|
|
1119
|
+
accept(message);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
case "string": out.push(`_nmsRoutingCandidates.add(${value});`); accept(value); return;
|
|
1123
|
+
case "enum": out.push(`_nmsRoutingCandidates.add(${value}.wire);`); accept(`${value}.wire`); return;
|
|
1124
|
+
case "integer": accept(`_nmsProtoInt(${value})`); return;
|
|
1125
|
+
case "number": accept(`_nmsProtoNumber(${value})`); return;
|
|
1126
|
+
case "boolean": accept(value); return;
|
|
1127
|
+
case "null": accept(`_nmsop.${structuralProtoName("Null", node)}()`); return;
|
|
1128
|
+
case "literal": {
|
|
1129
|
+
const literal = node.expression.value;
|
|
1130
|
+
if (typeof literal === "string") {
|
|
1131
|
+
out.push(`_nmsRoutingCandidates.add(${dartString(literal)});`);
|
|
1132
|
+
accept(dartString(literal));
|
|
1133
|
+
} else if (typeof literal === "boolean") accept(String(literal));
|
|
1134
|
+
else if (typeof literal === "number" && Number.isInteger(literal)) accept(`Int64(${literal})`);
|
|
1135
|
+
else if (typeof literal === "number") accept(String(literal));
|
|
1136
|
+
else accept(`_nmsop.${structuralProtoName("Null", node)}()`);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
case "record": {
|
|
1140
|
+
const recordFields = node.expression.fields.map((ref) => fieldNode(spec.nodes, ref));
|
|
1141
|
+
requireUniqueFieldNumbers(recordFields.map((item) => ({
|
|
1142
|
+
label: item.name,
|
|
1143
|
+
sid: composedWireIdentity(paths, item.entity),
|
|
1144
|
+
})), where);
|
|
1145
|
+
const message = fresh("recordMessage");
|
|
1146
|
+
out.push(`final ${message} = _nmsop.${structuralProtoName("Value", node)}();`);
|
|
1147
|
+
for (const child of recordFields) {
|
|
1148
|
+
const childRaw = `${value}.${dartMemberName(child.name)}`;
|
|
1149
|
+
const member = `${message}.${dartProtoFieldName(composedWireIdentity(paths, child.entity))}`;
|
|
1150
|
+
const body: string[] = [];
|
|
1151
|
+
emitValue(
|
|
1152
|
+
body,
|
|
1153
|
+
child.required ? childRaw : `${childRaw}!`,
|
|
1154
|
+
child.value.id,
|
|
1155
|
+
`${where}.${child.name}`,
|
|
1156
|
+
(expression) => body.push(`${member} = ${expression};`),
|
|
1157
|
+
valueIsCanonical || lowersNominalReference,
|
|
1158
|
+
);
|
|
1159
|
+
if (child.required) out.push(...body);
|
|
1160
|
+
else out.push(`if (${childRaw} != null) {`, ...body.map((line) => ` ${line}`), `}`);
|
|
1161
|
+
}
|
|
1162
|
+
accept(message);
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
case "list": {
|
|
1166
|
+
const item = fresh("item");
|
|
1167
|
+
const message = fresh("listMessage");
|
|
1168
|
+
out.push(`final ${message} = _nmsop.${structuralProtoName("List", node)}();`, `for (final ${item} in ${value}) {`);
|
|
1169
|
+
const body: string[] = [];
|
|
1170
|
+
emitValue(body, item, node.expression.element.id, `${where}[]`, (expression) => {
|
|
1171
|
+
body.push(`${message}.items.add(${expression});`);
|
|
1172
|
+
});
|
|
1173
|
+
out.push(...body.map((line) => ` ${line}`), `}`);
|
|
1174
|
+
accept(message);
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
case "map": {
|
|
1178
|
+
const key = fresh("key");
|
|
1179
|
+
const item = fresh("item");
|
|
1180
|
+
const message = fresh("mapMessage");
|
|
1181
|
+
const entry = fresh("mapEntry");
|
|
1182
|
+
const keys = fresh("mapKeys");
|
|
1183
|
+
out.push(
|
|
1184
|
+
`final ${message} = _nmsop.${structuralProtoName("Map", node)}();`,
|
|
1185
|
+
`final ${keys} = ${value}.keys.toList()..sort();`,
|
|
1186
|
+
`for (final ${key} in ${keys}) {`,
|
|
1187
|
+
` final ${item} = ${value}[${key}]!;`,
|
|
1188
|
+
` _nmsRoutingCandidates.add(${key});`,
|
|
1189
|
+
` final ${entry} = _nmsop.${structuralProtoName("MapEntry", node)}()..key = ${key};`,
|
|
1190
|
+
);
|
|
1191
|
+
const body: string[] = [];
|
|
1192
|
+
emitValue(body, item, node.expression.value.id, `${where}.*`, (expression) => {
|
|
1193
|
+
body.push(`${entry}.value = ${expression};`);
|
|
1194
|
+
});
|
|
1195
|
+
out.push(...body.map((line) => ` ${line}`), ` ${message}.entries.add(${entry});`, `}`);
|
|
1196
|
+
accept(message);
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
case "sum": {
|
|
1200
|
+
const arms = nominalSumArms(spec.nodes, node, `${domain}/${directive} ${where}`);
|
|
1201
|
+
const sumSemantic = node.expression.semantic;
|
|
1202
|
+
const sumName = sumSemantic?.kind === "value-object"
|
|
1203
|
+
? sumSemantic.name
|
|
1204
|
+
: undefined;
|
|
1205
|
+
if (sumName === undefined) {
|
|
1206
|
+
throw new Error(`Dart call Protobuf codegen: '${domain}/${directive}' contains a non-nominal sum at ${where}`);
|
|
1207
|
+
}
|
|
1208
|
+
const selected = fresh("sum");
|
|
1209
|
+
const message = fresh("sumMessage");
|
|
1210
|
+
out.push(`final ${selected} = ${value};`, `final ${message} = _nmsop.${structuralProtoName("Sum", node)}();`);
|
|
1211
|
+
arms.forEach((arm, index) => {
|
|
1212
|
+
const armType = unionArmClassName(sumName, arm.discriminator);
|
|
1213
|
+
const armValue = fresh("arm");
|
|
1214
|
+
const member = `${message}.${dartProtoFieldName(composedWireIdentity(paths, arm.variant.entity))}`;
|
|
1215
|
+
out.push(`${index === 0 ? "if" : "else if"} (${selected} is ${armType}) {`, ` final ${armValue} = ${selected} as ${armType};`);
|
|
1216
|
+
const body: string[] = [];
|
|
1217
|
+
emitValue(body, armValue, arm.value.entity.id, `${where}.${arm.discriminator}`, (expression) => {
|
|
1218
|
+
body.push(`${member} = ${expression};`);
|
|
1219
|
+
});
|
|
1220
|
+
out.push(...body.map((line) => ` ${line}`), `}`);
|
|
1221
|
+
});
|
|
1222
|
+
out.push(`else { throw ArgumentError.value(${selected}, ${dartString(sumName)}, 'Unknown nominal union arm'); }`);
|
|
1223
|
+
accept(message);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
case "unknown": throw new Error(`Dart call Protobuf codegen: '${domain}/${directive}' contains an unstructured value at ${where}`);
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
const parameters = [`${payloadType} payload`, ...extraParameters].join(", ");
|
|
1231
|
+
const message = fresh("payloadMessage");
|
|
1232
|
+
const lines = [
|
|
1233
|
+
`NmsGeneratedCall _nmsCallProto_${symbol(domain)}_${symbol(directive)}(${parameters}) {`,
|
|
1234
|
+
` final ${message} = _nmsop.${protoMessageName("Payload", operationMessageIdentity(graph, spec.contract.entity.sid))}();`,
|
|
1235
|
+
` final _nmsRoutingCandidates = <String>[];`,
|
|
1236
|
+
];
|
|
1237
|
+
for (const field of fields) {
|
|
1238
|
+
const fieldAccess = access.get(field.name);
|
|
1239
|
+
if (fieldAccess?.omit === true || fieldAccess?.present === "false") continue;
|
|
1240
|
+
const raw = fieldAccess?.slot === true ? `${fieldAccess.value}.value` : fieldAccess?.value ?? `payload.${dartMemberName(field.name)}`;
|
|
1241
|
+
const fieldSemantic = typeNode(spec.nodes, field.value.id).expression.semantic;
|
|
1242
|
+
const fieldValueIsCanonical = fieldSemantic?.kind === "id-reference" ||
|
|
1243
|
+
fieldSemantic?.kind === "address-reference" ||
|
|
1244
|
+
fieldSemantic?.kind === "versioned-reference" ||
|
|
1245
|
+
fieldSemantic?.kind === "recipient" ||
|
|
1246
|
+
fieldSemantic?.kind === "sha256-digest" ||
|
|
1247
|
+
fieldSemantic?.kind === "git-object-id" ||
|
|
1248
|
+
fieldSemantic?.kind === "branded-id" ||
|
|
1249
|
+
fieldSemantic?.kind === "unit";
|
|
1250
|
+
const body: string[] = [];
|
|
1251
|
+
const member = `${message}.${dartProtoFieldName(composedWireIdentity(paths, field.entity))}`;
|
|
1252
|
+
emitValue(body, raw, field.value.id, field.name, (expression) => {
|
|
1253
|
+
body.push(`${member} = ${expression};`);
|
|
1254
|
+
}, fieldValueIsCanonical);
|
|
1255
|
+
if (field.required) lines.push(...body.map((line) => ` ${line}`));
|
|
1256
|
+
else lines.push(` if (${fieldAccess?.present ?? `${raw} != null`}) {`, ...body.map((line) => ` ${line}`), ` }`);
|
|
1257
|
+
}
|
|
1258
|
+
lines.push(
|
|
1259
|
+
` return NmsInternalGeneratedCalls.create(`,
|
|
1260
|
+
` domain: ${dartString(domain)},`,
|
|
1261
|
+
` directive: ${dartString(spec.publicDirective)},`,
|
|
1262
|
+
` protobufType: ${dartString(spec.typeUrl)},`,
|
|
1263
|
+
` protobufPayload: ${message}.writeToBuffer(),`,
|
|
1264
|
+
` );`,
|
|
1265
|
+
`}`,
|
|
1266
|
+
``,
|
|
1267
|
+
);
|
|
1268
|
+
return {
|
|
1269
|
+
functionName: `_nmsCallProto_${symbol(domain)}_${symbol(directive)}`,
|
|
1270
|
+
typeUrl: spec.typeUrl,
|
|
1271
|
+
publicDirective: spec.publicDirective,
|
|
1272
|
+
source: lines,
|
|
1273
|
+
};
|
|
1274
|
+
}
|