@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Directive } from "./directive.js";
|
|
1
2
|
// NOMOS — Nomos Sovereign: participants act · verify · remember LOCALLY; hosted
|
|
2
3
|
// remotes are replaceable custody/transport, not truth. ⇒ ONE Nomos GitHolon
|
|
3
4
|
// wasm32-wasip1 artifact {kernel · projection · embedded
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
* It emits into `outDir` (default `<config dir>/build`):
|
|
31
32
|
* * `<name>.law/` — the exact logically named, human-readable USDA stage.
|
|
32
33
|
* * `<name>.law.usdz` — a lossless transport archive of that closure.
|
|
34
|
+
* * `<name>.compatibility.usda` — the generated application's opaque interface requirement.
|
|
33
35
|
* * `<name>.proof.mts` — the GENERATED PROOF (codegen_proof.ts): a
|
|
34
36
|
* runnable, domain-shaped live e2e synthesized from the law's own directives /
|
|
35
37
|
* queries / counts. Run it with `githolon proof`. Never fatal: an unsampleable
|
|
@@ -41,8 +43,18 @@
|
|
|
41
43
|
* --platform=neutral --charset=utf8`, no minify), and refused if it does not assign
|
|
42
44
|
* `globalThis.plan`.
|
|
43
45
|
*/
|
|
44
|
-
import { execFileSync } from "node:child_process";
|
|
46
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
47
|
+
import { promisify } from "node:util";
|
|
48
|
+
import { checkOperationContracts } from "./operation_contract_check.js";
|
|
49
|
+
import {
|
|
50
|
+
APPLICATION_CONTRACT_MODULE,
|
|
51
|
+
assertGeneratedClientBundleBoundary,
|
|
52
|
+
protobufPackageRoot,
|
|
53
|
+
} from "./generated_client_bundle_boundary.js";
|
|
45
54
|
import { createHash } from "node:crypto";
|
|
55
|
+
import { tmpdir } from "node:os";
|
|
56
|
+
import { inspectSqliteLayouts } from "./sqlite_layout.js";
|
|
57
|
+
import { sqliteDescriptorSamples } from "./sqlite_samples.js";
|
|
46
58
|
import {
|
|
47
59
|
expandCapabilityExports,
|
|
48
60
|
isCapabilityDecl,
|
|
@@ -51,8 +63,10 @@ import {
|
|
|
51
63
|
copyFileSync,
|
|
52
64
|
existsSync,
|
|
53
65
|
mkdirSync,
|
|
66
|
+
mkdtempSync,
|
|
54
67
|
readdirSync,
|
|
55
68
|
readFileSync,
|
|
69
|
+
realpathSync,
|
|
56
70
|
renameSync,
|
|
57
71
|
rmSync,
|
|
58
72
|
writeFileSync,
|
|
@@ -82,13 +96,21 @@ import {
|
|
|
82
96
|
} from "./evolution_files.js";
|
|
83
97
|
import { assertEvolutionModuleExports } from "./evolution.js";
|
|
84
98
|
import { readHistoricalIdentityFrontier } from "./identity_frontier.js";
|
|
85
|
-
import {
|
|
86
|
-
|
|
99
|
+
import {
|
|
100
|
+
dartBridgeClientOperationDirectiveIds,
|
|
101
|
+
generateDartDomainClient,
|
|
102
|
+
} from "./codegen_dart_client.js";
|
|
103
|
+
import {
|
|
104
|
+
dartFrameworkOperationDirectiveIds,
|
|
105
|
+
generateDartApplicationFrameworkCalls,
|
|
106
|
+
} from "./codegen_dart_framework_calls.js";
|
|
107
|
+
import { fixedFrameworkCallDefinitions } from "./framework_call_definitions.js";
|
|
108
|
+
import { BUSINESS_CORRECTION_DIRECTIVE } from "./framework/business_corrections.js";
|
|
87
109
|
import {
|
|
88
110
|
dartApplicationClassName,
|
|
89
111
|
dartApplicationPublicClassNames,
|
|
90
112
|
generateDartApplication,
|
|
91
|
-
} from "./
|
|
113
|
+
} from "./codegen_dart_facade.js";
|
|
92
114
|
import {
|
|
93
115
|
dartScenarioGivenClassName,
|
|
94
116
|
dartScenarioClassName,
|
|
@@ -110,6 +132,7 @@ import {
|
|
|
110
132
|
import { isNamespacedAggregate } from "./read_projection_namespace.js";
|
|
111
133
|
import {
|
|
112
134
|
collectPayloadValueObjectDescriptors,
|
|
135
|
+
collectSchemaValueObjectDescriptors,
|
|
113
136
|
collectValueObjectDescriptors,
|
|
114
137
|
} from "./value_object.js";
|
|
115
138
|
import { finishCount, type AnyCount } from "./count.js";
|
|
@@ -137,13 +160,22 @@ import {
|
|
|
137
160
|
bindBusinessBirthPolicyDomains,
|
|
138
161
|
expandBusinessBirthExports,
|
|
139
162
|
} from "./business_birth.js";
|
|
140
|
-
import {
|
|
163
|
+
import {
|
|
164
|
+
OWNED_DEVICE_APPROVAL_DIRECTIVE,
|
|
165
|
+
OWNED_INITIALIZE_DIRECTIVE,
|
|
166
|
+
OWNED_RECOVERY_ACCEPT_DIRECTIVE,
|
|
167
|
+
OWNED_RECOVERY_COMPLETE_DIRECTIVE,
|
|
168
|
+
OWNED_RECOVERY_PROTECT_DIRECTIVE,
|
|
169
|
+
OWNED_RECOVERY_REQUEST_DIRECTIVE,
|
|
170
|
+
withOwnedCustodyExports,
|
|
171
|
+
} from "./framework/owned_custody.js";
|
|
141
172
|
import {
|
|
142
173
|
OwnedCustodyValue,
|
|
143
174
|
OwnedDeviceRequestValue,
|
|
144
175
|
} from "./framework/owned_custody_value.js";
|
|
145
176
|
import { expandRoleExports, rolesOf } from "./role.js";
|
|
146
177
|
import { collectEngineRouting } from "./engine_entry.js";
|
|
178
|
+
import { sqliteComputationBundle } from "./sqlite_computation_bundle.js";
|
|
147
179
|
import {
|
|
148
180
|
canonicalizeEngineJavascript,
|
|
149
181
|
canonicalizeGeneratedClientJavascript,
|
|
@@ -154,6 +186,9 @@ import {
|
|
|
154
186
|
isQueryDecl,
|
|
155
187
|
isSpatialDecl,
|
|
156
188
|
isSumDecl,
|
|
189
|
+
isExtremumDecl,
|
|
190
|
+
isExistsDecl,
|
|
191
|
+
isOrderedReadDecl,
|
|
157
192
|
isWorkspaceInvariantDecl,
|
|
158
193
|
type DeclBag,
|
|
159
194
|
} from "./decl_shapes.js";
|
|
@@ -170,6 +205,8 @@ import {
|
|
|
170
205
|
type ShardingLawSpec,
|
|
171
206
|
} from "./workspace_sharding.js";
|
|
172
207
|
import { finishExtremum, type AnyExtremum } from "./extremum.js";
|
|
208
|
+
import { finishExists, type AnyExists } from "./exists.js";
|
|
209
|
+
import type { OrderedReadDecl } from "./ordered_read.js";
|
|
173
210
|
import type { WorkspaceInvariantDecl } from "./framework/workspace_invariant.js";
|
|
174
211
|
import {
|
|
175
212
|
discoverRelationTypeDeclarations,
|
|
@@ -197,6 +234,8 @@ import {
|
|
|
197
234
|
import { reconcileStableIdsWithIdentity } from "./stable_id_identity_bridge.js";
|
|
198
235
|
import { LAW_FRAMEWORK_PARENT } from "./law_identity.js";
|
|
199
236
|
import { discoverLawGraph, type LawGraph } from "./law_graph.js";
|
|
237
|
+
import { LawDependencyIndex, commandWorkingSetReport } from "./law_dependency_index.js";
|
|
238
|
+
import { generateDartWorkspaceAccessRead } from "./codegen_workspace_access.js";
|
|
200
239
|
import {
|
|
201
240
|
compileSparseLawGraphUsda,
|
|
202
241
|
emitFrameworkLawUsda,
|
|
@@ -207,7 +246,6 @@ import {
|
|
|
207
246
|
} from "./dart_barrel_guard.js";
|
|
208
247
|
import {
|
|
209
248
|
generateTsClient,
|
|
210
|
-
generateTsLifecycleOperator,
|
|
211
249
|
tsApplicationName,
|
|
212
250
|
tsClientFactoryName,
|
|
213
251
|
recursiveLazyTypes,
|
|
@@ -216,25 +254,54 @@ import {
|
|
|
216
254
|
generateOpenApiFragment,
|
|
217
255
|
mergeOpenApiFragments,
|
|
218
256
|
} from "./codegen_openapi.js";
|
|
219
|
-
import {
|
|
220
|
-
|
|
257
|
+
import {
|
|
258
|
+
domainInterfaceFromLawGraph,
|
|
259
|
+
publicClientInterface,
|
|
260
|
+
type DomainInterface,
|
|
261
|
+
} from "./compatibility_graph.js";
|
|
262
|
+
import { proofLegsIndex } from "./codegen_proof.js";
|
|
221
263
|
import { usdzPack } from "./usdz.js";
|
|
264
|
+
import {
|
|
265
|
+
LAW_UNIT_MANIFEST,
|
|
266
|
+
assertLawUnitsCoverPackage,
|
|
267
|
+
lawUnitDirectoryName,
|
|
268
|
+
packageRoutesLayerUsda,
|
|
269
|
+
splitLawUnits,
|
|
270
|
+
} from "./law_units.js";
|
|
222
271
|
import { checkDomainModules, formatFindings, hasRefusal } from "./law_check.js";
|
|
223
272
|
import {
|
|
224
273
|
currentEvolveLaw,
|
|
225
274
|
evolveDiff,
|
|
226
275
|
evolveLawFromLockfile,
|
|
227
276
|
} from "./evolve_preview.js";
|
|
228
|
-
import type { NomosCompat } from "./compat_manifest.js";
|
|
229
277
|
import {
|
|
230
278
|
requiredKernelContracts,
|
|
231
279
|
type KernelContractProbeModule,
|
|
232
280
|
} from "./kernel_capabilities.js";
|
|
233
281
|
import { APPLICATION_HOME_DOMAIN } from "./framework/application_home_domain.js";
|
|
234
282
|
import { applicationHomePolicyLaw } from "./framework/application_home.js";
|
|
283
|
+
import * as childAuthenticationModule from "./framework/child_authentication.js";
|
|
284
|
+
import * as receivingGroupRecoveryModule from "./framework/receiving_group_recovery.js";
|
|
285
|
+
import * as modelTransitionModule from "./framework/model_transition.js";
|
|
286
|
+
import * as modelAuthorityRepairModule from "./framework/model_authority_repair.js";
|
|
287
|
+
import { SEAT_INHERITED_MODEL_AUTHORITY_ID } from "./framework/model_authority_seed.js";
|
|
235
288
|
import { isApplicationHomeGenesis } from "./application_home_genesis.js";
|
|
236
289
|
import { resolveApplicationTopology } from "./application_topology.js";
|
|
237
|
-
import {
|
|
290
|
+
import {
|
|
291
|
+
isCustodyPlacementDeclaration,
|
|
292
|
+
type CustodyPlacementDeclaration,
|
|
293
|
+
} from "./custody_placement.js";
|
|
294
|
+
import {
|
|
295
|
+
custodyPlacementDeclarationsUsda,
|
|
296
|
+
type DomainCustodyPlacement,
|
|
297
|
+
} from "./custody_placement_usda.js";
|
|
298
|
+
import { isApplicationLanguage, isBusinessIntent } from "./intent.js";
|
|
299
|
+
import {
|
|
300
|
+
emitTsCallProtoEncoder,
|
|
301
|
+
generateOperationProtoSchema,
|
|
302
|
+
type OperationProtoLaw,
|
|
303
|
+
TS_CALL_PROTO_PRELUDE,
|
|
304
|
+
} from "./codegen_call_proto.js";
|
|
238
305
|
import {
|
|
239
306
|
emitTsCallUsdaEncoder,
|
|
240
307
|
TS_CALL_USDA_PRELUDE,
|
|
@@ -266,6 +333,11 @@ import {
|
|
|
266
333
|
PlanSourceSliceProgram,
|
|
267
334
|
type PlanSourceSliceTarget,
|
|
268
335
|
} from "./plan_source_slice.js";
|
|
336
|
+
import {
|
|
337
|
+
encodeOpenUsdUsdc,
|
|
338
|
+
flattenOpenUsdToUsda,
|
|
339
|
+
} from "./openusd_compiled_layer.js";
|
|
340
|
+
import { emitApplicationCompatibilityUsda } from "./compatibility_usda.js";
|
|
269
341
|
|
|
270
342
|
const DSL_DIR = path.resolve(
|
|
271
343
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
@@ -293,7 +365,7 @@ const BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES: Readonly<
|
|
|
293
365
|
exportName: "seedAuthProvider",
|
|
294
366
|
},
|
|
295
367
|
seedBirthAttestation: {
|
|
296
|
-
modulePath: path.join(DSL_DIR, "src", "framework", "
|
|
368
|
+
modulePath: path.join(DSL_DIR, "src", "framework", "receiving_group.ts"),
|
|
297
369
|
exportName: "seedBirthAttestation",
|
|
298
370
|
},
|
|
299
371
|
seedGenesis: {
|
|
@@ -304,8 +376,47 @@ const BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES: Readonly<
|
|
|
304
376
|
modulePath: path.join(DSL_DIR, "src", "birth_meter_ensure.ts"),
|
|
305
377
|
exportName: "nomosBirthRecordEnsure",
|
|
306
378
|
},
|
|
379
|
+
// MODEL AUTHORITY DESCENDS (architecture/parent_directed_model_transition.md, lane A): the child-genesis
|
|
380
|
+
// seed every framework birth embeds; its plan closure is the seed module, never the whole tenant domain.
|
|
381
|
+
[SEAT_INHERITED_MODEL_AUTHORITY_ID]: {
|
|
382
|
+
modulePath: path.join(DSL_DIR, "src", "framework", "model_authority_seed.ts"),
|
|
383
|
+
exportName: "seatInheritedModelAuthority",
|
|
384
|
+
},
|
|
307
385
|
};
|
|
308
386
|
const BUSINESS_BIRTH_FRAMEWORK_PLAN_DOMAIN = "__nomos_birth_genesis_plan_v1";
|
|
387
|
+
/** The framework child-authentication law composed into the application-home domain; its members are
|
|
388
|
+
* module-level exports so plan slicing and workspace-effect target encoders resolve them by name. */
|
|
389
|
+
const CHILD_AUTHENTICATION_MODULE_PATH = path.join(
|
|
390
|
+
DSL_DIR,
|
|
391
|
+
"src",
|
|
392
|
+
"framework",
|
|
393
|
+
"child_authentication.ts"
|
|
394
|
+
);
|
|
395
|
+
/** The framework receiving-group recovery door (composed only when the package declares the
|
|
396
|
+
* `platform-identity-provider` posture); its members are module-level singletons for the same reason. */
|
|
397
|
+
const RECEIVING_GROUP_RECOVERY_MODULE_PATH = path.join(
|
|
398
|
+
DSL_DIR,
|
|
399
|
+
"src",
|
|
400
|
+
"framework",
|
|
401
|
+
"receiving_group_recovery.ts"
|
|
402
|
+
);
|
|
403
|
+
/** The framework parent-directed model transition (the platform's direction + the child's attested-parent
|
|
404
|
+
* acceptor, composed whenever the package births children); module-level singletons for the same reason. */
|
|
405
|
+
const MODEL_TRANSITION_MODULE_PATH = path.join(
|
|
406
|
+
DSL_DIR,
|
|
407
|
+
"src",
|
|
408
|
+
"framework",
|
|
409
|
+
"model_transition.ts"
|
|
410
|
+
);
|
|
411
|
+
/** The framework model-authority self-heal (the child's ask, the parent's serve and the child's
|
|
412
|
+
* attested-parent acceptor, composed whenever the package births children); module-level singletons so
|
|
413
|
+
* plan slicing and workspace-effect target encoders resolve them by name. */
|
|
414
|
+
const MODEL_AUTHORITY_REPAIR_MODULE_PATH = path.join(
|
|
415
|
+
DSL_DIR,
|
|
416
|
+
"src",
|
|
417
|
+
"framework",
|
|
418
|
+
"model_authority_repair.ts"
|
|
419
|
+
);
|
|
309
420
|
|
|
310
421
|
interface DomainConfig extends NormalizedNomosPackageDomainConfig {
|
|
311
422
|
readonly key: string;
|
|
@@ -316,9 +427,11 @@ interface DomainConfig extends NormalizedNomosPackageDomainConfig {
|
|
|
316
427
|
readonly counts?: readonly string[];
|
|
317
428
|
/** Escape hatch only since #57 — exported SumDecls auto-discover like queries/counts. */
|
|
318
429
|
readonly sums?: readonly string[];
|
|
319
|
-
/** Maintained MIN/MAX
|
|
430
|
+
/** Maintained MIN/MAX export-name escape hatches; exported declarations auto-discover. */
|
|
320
431
|
readonly mins?: readonly string[];
|
|
321
432
|
readonly maxes?: readonly string[];
|
|
433
|
+
readonly exists_?: readonly string[];
|
|
434
|
+
readonly orderedReads?: readonly string[];
|
|
322
435
|
readonly spatials?: readonly string[];
|
|
323
436
|
readonly deriveds?: readonly string[];
|
|
324
437
|
readonly combineds?: readonly string[];
|
|
@@ -349,7 +462,7 @@ interface DomainConfig extends NormalizedNomosPackageDomainConfig {
|
|
|
349
462
|
};
|
|
350
463
|
}
|
|
351
464
|
|
|
352
|
-
type DartSupportImport = "
|
|
465
|
+
type DartSupportImport = "package";
|
|
353
466
|
|
|
354
467
|
interface PackageConfig extends NomosPackageConfig {
|
|
355
468
|
readonly domains: readonly DomainConfig[];
|
|
@@ -362,14 +475,13 @@ interface PackageConfig extends NomosPackageConfig {
|
|
|
362
475
|
* Typed DART frontend (the Flutter sibling of the generated TS client; #codegen_dart).
|
|
363
476
|
* `false`/absent = skip (default). `true` = emit into `<outDir>/dart/`;
|
|
364
477
|
* `{ out: "relative/dir" }` = emit there (resolved against the config dir).
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
478
|
+
* Generated Flutter applications import the fixed `nomos_types` and
|
|
479
|
+
* `nomos_client` packages. Runtime implementation is never copied into a
|
|
480
|
+
* generated domain package.
|
|
368
481
|
* Per composed domain one `<name>.dart` (payload classes + read models + typed read
|
|
369
482
|
* accessors via the SAME `generateDartDomain` the co2 tenant emit uses), PLUS the
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
* can vendor the directory as-is, no pub deps.
|
|
483
|
+
* A generated directory contains only domain stationery, value types,
|
|
484
|
+
* stateless facades and its compatibility matrix.
|
|
373
485
|
*/
|
|
374
486
|
readonly dart?:
|
|
375
487
|
| boolean
|
|
@@ -388,16 +500,16 @@ interface PackageConfig extends NomosPackageConfig {
|
|
|
388
500
|
* produces THREE sibling pub packages under `out` instead of one flat tree:
|
|
389
501
|
* • `types` — pure data (payload classes, read models, VOs/enums, typed accessors over
|
|
390
502
|
* the injected NomosReads seams, the aggregate-meta registry). Depends ONLY on the
|
|
391
|
-
* pure `nomos_types` core — frontend-safe: no
|
|
503
|
+
* pure `nomos_types` core — frontend-safe: no session, no sessions, no lifecycle.
|
|
392
504
|
* • `client` — the effectful runtime clients (`<name>.client.dart`, offer methods over
|
|
393
|
-
*
|
|
505
|
+
* NomosSession). Depends on the types package + `nomos_client`.
|
|
394
506
|
* • `test` — optional; generated business scenarios for widget/integration tests.
|
|
395
507
|
* The layering line: UI code depends on `types`; composition/runtime code on `client`;
|
|
396
508
|
* generated TYPES may flow to the UI, generated CLIENTS may not.
|
|
397
509
|
* Each package's `pubspec.yaml` keeps tenant-owned versioning/publishing metadata and custom
|
|
398
510
|
* dependencies; Nomos-owned framework dependency floors are reconciled on every compile.
|
|
399
511
|
* Barrels + `lib/src/generated/` are generated-owned and rewritten every compile.
|
|
400
|
-
*
|
|
512
|
+
* Uses the fixed runtime packages; generated code never vendors lifecycle machinery.
|
|
401
513
|
*/
|
|
402
514
|
readonly packages?: {
|
|
403
515
|
readonly types: string;
|
|
@@ -445,6 +557,8 @@ interface PackageConfig extends NomosPackageConfig {
|
|
|
445
557
|
readonly compact?: boolean;
|
|
446
558
|
}
|
|
447
559
|
|
|
560
|
+
const execFileAsync = promisify(execFile);
|
|
561
|
+
|
|
448
562
|
function fail(msg: string): never {
|
|
449
563
|
console.error(`nomos-compile: ${msg}`);
|
|
450
564
|
process.exit(1);
|
|
@@ -456,6 +570,12 @@ interface EmittedLawClosureAsset {
|
|
|
456
570
|
readonly utf8: string;
|
|
457
571
|
}
|
|
458
572
|
|
|
573
|
+
interface EmittedBinaryLawClosureAsset {
|
|
574
|
+
readonly logicalPath: string;
|
|
575
|
+
readonly sha256: string;
|
|
576
|
+
readonly bytes: Uint8Array;
|
|
577
|
+
}
|
|
578
|
+
|
|
459
579
|
interface UsdPlanAsset {
|
|
460
580
|
readonly sha256: string;
|
|
461
581
|
readonly javascript: string;
|
|
@@ -491,6 +611,15 @@ interface EmittedLawClosure {
|
|
|
491
611
|
}[];
|
|
492
612
|
}
|
|
493
613
|
|
|
614
|
+
/** Match Rust's `BTreeMap<String, _>` ordering for authenticated logical paths.
|
|
615
|
+
* `localeCompare` is collation, not byte ordering: for example it places
|
|
616
|
+
* `catalogue_directory.usda` before `catalogue.usda`, which gives the compiler
|
|
617
|
+
* and kernel different closure identities for the same package. Logical paths
|
|
618
|
+
* are restricted to ASCII by the kernel, so ordinary string comparison is the
|
|
619
|
+
* exact shared ordering here. */
|
|
620
|
+
const compareLogicalPath = (left: string, right: string): number =>
|
|
621
|
+
left < right ? -1 : left > right ? 1 : 0;
|
|
622
|
+
|
|
494
623
|
/** Build the production-shaped v3 stage entirely from compiler-authored bytes. The deploy host may
|
|
495
624
|
* shuttle this object, but it cannot select layers, compose opinions or mint any identity: the kernel
|
|
496
625
|
* inventories the USDZ/Git tree and re-hashes every logically named layer. */
|
|
@@ -501,7 +630,11 @@ function emitLawClosure(
|
|
|
501
630
|
domainAssets: readonly EmittedLawClosureAsset[],
|
|
502
631
|
runtime: UsdRuntimeAsset,
|
|
503
632
|
plans: readonly UsdPlanAsset[],
|
|
504
|
-
routes: readonly UsdPlanRoute[]
|
|
633
|
+
routes: readonly UsdPlanRoute[],
|
|
634
|
+
derivedAssets: readonly (
|
|
635
|
+
| EmittedLawClosureAsset
|
|
636
|
+
| EmittedBinaryLawClosureAsset
|
|
637
|
+
)[] = []
|
|
505
638
|
): EmittedLawClosure {
|
|
506
639
|
const planByHash = new Map(plans.map((plan) => [plan.sha256, plan] as const));
|
|
507
640
|
if (sha256HexUtf8(runtime.javascript) !== runtime.sha256) {
|
|
@@ -517,44 +650,13 @@ function emitLawClosure(
|
|
|
517
650
|
}
|
|
518
651
|
}
|
|
519
652
|
const contentAsset = (sha256: string): string => `@nomos+sha256:${sha256}@`;
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
' string "nomos:format" = "nomos.law-layer.v3"',
|
|
525
|
-
' string "nomos:logicalPath" = "runtime/routes"',
|
|
526
|
-
" }",
|
|
527
|
-
")",
|
|
528
|
-
"",
|
|
529
|
-
'def Scope "__NomosRuntime_7e9c"',
|
|
530
|
-
"{",
|
|
531
|
-
' custom string nomos:kind = "runtime"',
|
|
532
|
-
' custom string nomos:format = "nomos.openusd-plan-metadata.v3"',
|
|
533
|
-
"",
|
|
534
|
-
' def Scope "Routes"',
|
|
535
|
-
" {",
|
|
536
|
-
];
|
|
537
|
-
[...routes]
|
|
538
|
-
.sort((left, right) => left.selector.localeCompare(right.selector))
|
|
539
|
-
.forEach((route, index) => {
|
|
540
|
-
if (!planByHash.has(route.planSha256)) {
|
|
541
|
-
fail(
|
|
542
|
-
`v3 closure: route '${route.selector}' names missing plan '${route.planSha256}'`
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
routeOpinions.push(
|
|
546
|
-
` def Scope "r${String(index).padStart(6, "0")}"`,
|
|
547
|
-
" {",
|
|
548
|
-
` custom string nomos:selector = ${JSON.stringify(
|
|
549
|
-
route.selector
|
|
550
|
-
)}`,
|
|
551
|
-
` custom string nomos:plan = "${route.planSha256}"`,
|
|
552
|
-
" }",
|
|
553
|
-
""
|
|
653
|
+
for (const route of routes) {
|
|
654
|
+
if (!planByHash.has(route.planSha256)) {
|
|
655
|
+
fail(
|
|
656
|
+
`v3 closure: route '${route.selector}' names missing plan '${route.planSha256}'`
|
|
554
657
|
);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
routeOpinions.push(" }", "}", "");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
558
660
|
|
|
559
661
|
const textAsset = (
|
|
560
662
|
logicalPath: string,
|
|
@@ -573,7 +675,7 @@ function emitLawClosure(
|
|
|
573
675
|
runtimeDependencies,
|
|
574
676
|
})
|
|
575
677
|
);
|
|
576
|
-
const routeLayer = textAsset("runtime/routes.usda",
|
|
678
|
+
const routeLayer = textAsset("runtime/routes.usda", packageRoutesLayerUsda(routes));
|
|
577
679
|
const runtimeSource = textAsset(
|
|
578
680
|
`runtime/bases/${runtime.sha256}.mjs`,
|
|
579
681
|
runtime.javascript
|
|
@@ -658,9 +760,26 @@ function emitLawClosure(
|
|
|
658
760
|
...domainAssets,
|
|
659
761
|
framework,
|
|
660
762
|
]);
|
|
661
|
-
|
|
662
|
-
|
|
763
|
+
// Derived assets are compiler-produced, authenticated finished goods. They are deliberately
|
|
764
|
+
// inventoried by the closure without becoming sublayers of the authoritative source stage.
|
|
765
|
+
// In particular, the runtime read projection is an OpenUSD flatten of a population-masked
|
|
766
|
+
// source stage: composing it back into that stage would duplicate every read opinion. Its USDA
|
|
767
|
+
// source remains relationship-checked; the mechanically equivalent USDC cache is binary OpenUSD.
|
|
768
|
+
assertUsdaRelationshipIntegrity(
|
|
769
|
+
derivedAssets.filter(
|
|
770
|
+
(asset): asset is EmittedLawClosureAsset => "utf8" in asset
|
|
771
|
+
),
|
|
772
|
+
// Standard OpenUSD population masks do not pull relationship targets into the stage. The read
|
|
773
|
+
// engine consumes authored storage kinds and exact ref/field paths; nominal value-type prims
|
|
774
|
+
// remain in the full authenticated law and are intentionally absent from this acceleration view.
|
|
775
|
+
{ allowMissingRelationships: new Set(["nomos:type", "nomos:returns"]) }
|
|
663
776
|
);
|
|
777
|
+
const custodyAssets = [
|
|
778
|
+
...sublayers,
|
|
779
|
+
runtimeSource,
|
|
780
|
+
...planSources,
|
|
781
|
+
...derivedAssets,
|
|
782
|
+
].sort((left, right) => compareLogicalPath(left.logicalPath, right.logicalPath));
|
|
664
783
|
const root = textAsset(
|
|
665
784
|
"root.usda",
|
|
666
785
|
[
|
|
@@ -704,7 +823,7 @@ function emitLawClosure(
|
|
|
704
823
|
].join("\n")
|
|
705
824
|
);
|
|
706
825
|
const assets = [root, ...custodyAssets].sort((left, right) =>
|
|
707
|
-
left.logicalPath
|
|
826
|
+
compareLogicalPath(left.logicalPath, right.logicalPath)
|
|
708
827
|
);
|
|
709
828
|
const identity = createHash("sha256");
|
|
710
829
|
identity.update("nomos-law-stage-v3\0");
|
|
@@ -719,10 +838,11 @@ function emitLawClosure(
|
|
|
719
838
|
return {
|
|
720
839
|
rootLogicalPath: "root.usda",
|
|
721
840
|
identity: identity.digest("hex"),
|
|
722
|
-
assets: assets.map((
|
|
723
|
-
logicalPath,
|
|
724
|
-
sha256,
|
|
725
|
-
bytes:
|
|
841
|
+
assets: assets.map((asset) => ({
|
|
842
|
+
logicalPath: asset.logicalPath,
|
|
843
|
+
sha256: asset.sha256,
|
|
844
|
+
bytes:
|
|
845
|
+
"bytes" in asset ? asset.bytes : new TextEncoder().encode(asset.utf8),
|
|
726
846
|
})),
|
|
727
847
|
};
|
|
728
848
|
}
|
|
@@ -990,142 +1110,34 @@ function resolveNamed<T>(
|
|
|
990
1110
|
* self-contained (no pub path-dependency on the framework package).
|
|
991
1111
|
*/
|
|
992
1112
|
const DART_PACKAGE_IMPORT = `import 'package:nomos_client/src/nms_generated_runtime.dart';`;
|
|
993
|
-
const DART_RELATIVE_IMPORT = `import 'nomos_client.dart';`;
|
|
994
1113
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
* the barrel (`dsl/dart/lib/`). `schema_validation.dart` is deliberately NOT
|
|
998
|
-
* vendored: nothing the codegen emits references it, and it would drag the external
|
|
999
|
-
* `json_schema` pub dependency into every vendoring app.
|
|
1000
|
-
*/
|
|
1001
|
-
// The PURE CORE support files, vendored from the `nomos_types` package (dsl/dart_types/lib/src —
|
|
1002
|
-
// the layered split: wire types, offer codec, NomosReads, aggregate meta, error info). Zero
|
|
1003
|
-
// external pub deps. `schema_validation.dart` is deliberately NOT vendored: nothing the codegen
|
|
1004
|
-
// emits references it, and it would drag the external `json_schema` pub dependency into every
|
|
1005
|
-
// vendoring app.
|
|
1006
|
-
const DART_SUPPORT_CORE_SRC = [
|
|
1114
|
+
// Only used to delete output produced by the retired self-contained runtime generator.
|
|
1115
|
+
const LEGACY_VENDORED_DART_SUPPORT = [
|
|
1007
1116
|
"wire.dart",
|
|
1008
1117
|
"driver.dart",
|
|
1009
1118
|
"offer.dart",
|
|
1010
1119
|
"subscriptions.dart",
|
|
1011
|
-
// Nominal custody/content identities used by generated payloads and read models.
|
|
1012
1120
|
"sha256_digest.dart",
|
|
1013
1121
|
"git_object_id.dart",
|
|
1014
|
-
// The tri-state slot (NomosSlot<T>) for `nullable`/`clearable` fields (null_semantics.md) — the
|
|
1015
|
-
// barrel re-exports it and generated read-model/payload classes reference it, so it must vendor.
|
|
1016
1122
|
"null_slot.dart",
|
|
1017
|
-
// Runtime aggregate-meta support types (NomosAggregateMeta/NomosFieldMeta) — the generated
|
|
1018
|
-
// per-domain registry consts reference them through the barrel.
|
|
1019
1123
|
"aggregate_meta.dart",
|
|
1020
|
-
// The data-only error surface (NomosErrorInfo/NomosBridgeError) + the typed diagnostics
|
|
1021
|
-
// (nomosErrorFromInfo / MissingOfferAuthority / …) — the vendored bridge AND the generated
|
|
1022
|
-
// `<name>.client.dart` reference these through the barrel. No external pub deps.
|
|
1023
1124
|
"error_info.dart",
|
|
1024
1125
|
"errors.dart",
|
|
1025
|
-
// The foreign-ref locator runtime (ForeignRef<T>/NomosForeignReader/…, t.foreignRef) — a generated read
|
|
1026
|
-
// model with a `t.foreignRef` field references `ForeignRef<Target>` through the barrel, so it must vendor.
|
|
1027
1126
|
"foreign_ref.dart",
|
|
1028
1127
|
"activity.dart",
|
|
1029
1128
|
"nomos_ref.dart",
|
|
1030
|
-
// Immutable publication pin + stable series and the required version-status sum
|
|
1031
|
-
// type. Pure data; the generated application owns the effectful check.
|
|
1032
1129
|
"versioned_nomos_ref.dart",
|
|
1033
|
-
// Optional first-class references are explicit required choices in generated
|
|
1034
|
-
// action inputs: absent or present, never an accidentally omitted argument.
|
|
1035
1130
|
"optional_ref.dart",
|
|
1036
|
-
// Branded identity wrappers (NomosPrincipal/NomosDeviceSecret) — the generated cross-workspace birth
|
|
1037
|
-
// methods type `actor`/`authorSecret` as these (not bare strings), so both must vendor. Pure data.
|
|
1038
1131
|
"principal.dart",
|
|
1039
|
-
// The custody-lifecycle types (NomosCustodyBreak/NomosCustodyStatus/CustodyBreakPolicy) — the generated
|
|
1040
|
-
// client + the vendored bridge reference them through the barrel, so they must vendor. Pure data.
|
|
1041
1132
|
"custody.dart",
|
|
1042
|
-
// THE LOCAL-WORK-REJECTED LIFECYCLE (seated_barrier_and_seal_obligation.md Part 4):
|
|
1043
|
-
// NomosLocalWorkRejectedEvent/NomosRejectedIntent — the vendored bridge's localWorkRejectedEvents
|
|
1044
|
-
// stream types ride through the barrel, so they must vendor. Pure data.
|
|
1045
1133
|
"local_work_rejected.dart",
|
|
1046
|
-
// SURFACED MERGE CONFLICTS: SurfacedValue<T>/SurfacedObservation<T> — a generated read model with a
|
|
1047
|
-
// `.merge(Surfaced(...))` field references them through the barrel, so they must vendor. Pure data.
|
|
1048
1134
|
"surfaced.dart",
|
|
1049
|
-
] as const;
|
|
1050
|
-
// The RUNTIME support files, vendored from `nomos_client` (dsl/dart/lib/src): the async WebView
|
|
1051
|
-
// bridge the generated `<name>.client.dart` drives, and the provider surface. Their
|
|
1052
|
-
// `package:nomos_types/...` imports are rewritten to the local vendored shim (see emitDart).
|
|
1053
|
-
// (`home.dart` is NOT vendored — the home-onboarding runtime drags `package:crypto`; nothing the
|
|
1054
|
-
// codegen emits references it.)
|
|
1055
|
-
const DART_SUPPORT_RUNTIME_SRC = [
|
|
1056
1135
|
"provider.dart",
|
|
1057
|
-
"webview_bridge.dart",
|
|
1058
1136
|
"session_bound.dart",
|
|
1059
1137
|
"record_origin.dart",
|
|
1060
|
-
// Canonical USDA application-call/result syntax. Generated clients refer to
|
|
1061
|
-
// these through the support barrel; omitting them from a vendored package
|
|
1062
|
-
// silently resurrected the old map bridge at integration time.
|
|
1063
1138
|
"generated_call.dart",
|
|
1064
1139
|
"result_usda.dart",
|
|
1065
1140
|
] as const;
|
|
1066
|
-
const DART_SUPPORT_SRC = [
|
|
1067
|
-
...DART_SUPPORT_CORE_SRC,
|
|
1068
|
-
...DART_SUPPORT_RUNTIME_SRC,
|
|
1069
|
-
] as const;
|
|
1070
|
-
/** The local shim replacing `package:nomos_types/nomos_types.dart` inside vendored runtime files. */
|
|
1071
|
-
const VENDORED_TYPES_SHIM = "nomos_types.dart";
|
|
1072
|
-
const VENDORED_KERNEL_PROTOCOL_SHIM = "nomos_kernel_protocol.dart";
|
|
1073
|
-
const VENDORED_META_DEP = "^1.12.0";
|
|
1074
|
-
const VENDORED_PROTOBUF_DEP = "^5.0.0";
|
|
1075
|
-
const VENDORED_FIXNUM_DEP = "^1.1.1";
|
|
1076
|
-
const VENDORED_DART_SDK_FLOOR = "3.3.0";
|
|
1077
|
-
|
|
1078
|
-
/**
|
|
1079
|
-
* Vendored runtime files carry package:meta's @internal boundary. Keep the
|
|
1080
|
-
* containing Dart package honest by declaring that implementation dependency
|
|
1081
|
-
* instead of stripping the annotation from generated copies.
|
|
1082
|
-
*/
|
|
1083
|
-
function ensureVendoredPackageRequirements(dartOut: string): void {
|
|
1084
|
-
let dir = path.resolve(dartOut);
|
|
1085
|
-
let pubspec: string | undefined;
|
|
1086
|
-
for (let hops = 0; hops < 8; hops++) {
|
|
1087
|
-
const candidate = path.join(dir, "pubspec.yaml");
|
|
1088
|
-
if (existsSync(candidate)) {
|
|
1089
|
-
pubspec = candidate;
|
|
1090
|
-
break;
|
|
1091
|
-
}
|
|
1092
|
-
const parent = path.dirname(dir);
|
|
1093
|
-
if (parent === dir) break;
|
|
1094
|
-
dir = parent;
|
|
1095
|
-
}
|
|
1096
|
-
if (pubspec === undefined) return;
|
|
1097
|
-
const source = readFileSync(pubspec, "utf8");
|
|
1098
|
-
let next = source;
|
|
1099
|
-
const sdkLine = /^(\s+sdk:\s*)([^#\r\n]+)(\s*(?:#.*)?)$/m.exec(next);
|
|
1100
|
-
if (sdkLine === null) {
|
|
1101
|
-
fail(
|
|
1102
|
-
"generated Dart semantic types need an environment.sdk constraint in " +
|
|
1103
|
-
pubspec
|
|
1104
|
-
);
|
|
1105
|
-
}
|
|
1106
|
-
const declared = sdkLine[2]!.trim().replace(/^['"]|['"]$/g, "");
|
|
1107
|
-
const firstVersion = /(\d+)\.(\d+)(?:\.\d+)?/.exec(declared);
|
|
1108
|
-
if (firstVersion === null) {
|
|
1109
|
-
fail(
|
|
1110
|
-
`generated Dart semantic types need SDK >=${VENDORED_DART_SDK_FLOOR}; ` +
|
|
1111
|
-
`cannot interpret environment.sdk '${declared}' in ${pubspec}`
|
|
1112
|
-
);
|
|
1113
|
-
}
|
|
1114
|
-
const major = Number(firstVersion[1]);
|
|
1115
|
-
const minor = Number(firstVersion[2]);
|
|
1116
|
-
if (major < 3 || (major === 3 && minor < 3)) {
|
|
1117
|
-
next = next.replace(
|
|
1118
|
-
sdkLine[0],
|
|
1119
|
-
`${sdkLine[1]}\">=${VENDORED_DART_SDK_FLOOR} <4.0.0\"${sdkLine[3] ?? ""}`
|
|
1120
|
-
);
|
|
1121
|
-
}
|
|
1122
|
-
if (next !== source) writeFileSync(pubspec, next, "utf8");
|
|
1123
|
-
reconcilePubspec(dir, "", {
|
|
1124
|
-
meta: VENDORED_META_DEP,
|
|
1125
|
-
protobuf: VENDORED_PROTOBUF_DEP,
|
|
1126
|
-
fixnum: VENDORED_FIXNUM_DEP,
|
|
1127
|
-
});
|
|
1128
|
-
}
|
|
1129
1141
|
|
|
1130
1142
|
/** The generated-owned subdirectory, relative to `dartOut` (the pub package's `lib/`). WIPED and
|
|
1131
1143
|
* rewritten on every compile — nothing hand-written may live in it. The stable facade (the package
|
|
@@ -1157,7 +1169,7 @@ function collectPackageContracts(
|
|
|
1157
1169
|
/**
|
|
1158
1170
|
* CROSS-DOMAIN REF TRAVERSAL map (dart_codegen_parity.md §2a follow-on): every aggregate id in the package
|
|
1159
1171
|
* → its owning domain (`domain` for the generated client class name; `name` for the sibling
|
|
1160
|
-
* `<name>.client.dart` import). Handed to `
|
|
1172
|
+
* `<name>.client.dart` import). Handed to `generateDartDomainClient` so a same-workspace `t.ref` whose
|
|
1161
1173
|
* target lives in ANOTHER domain of the package emits a resolver taking that domain's client. A same-domain
|
|
1162
1174
|
* ref never consults this (its target is in the local module); a foreign ref uses its own locator.
|
|
1163
1175
|
*/
|
|
@@ -1358,8 +1370,13 @@ function dartValueObjectsForModule(
|
|
|
1358
1370
|
>();
|
|
1359
1371
|
for (const descriptor of [
|
|
1360
1372
|
...(mod.valueObjects ?? []),
|
|
1373
|
+
...(mod.indexedVariants ?? []).flatMap((binding) => [binding.value.descriptor, ...binding.value.dependencies]),
|
|
1361
1374
|
...collectValueObjectDescriptors(mod.aggregates),
|
|
1362
1375
|
...collectPayloadValueObjectDescriptors(codegenDirectives),
|
|
1376
|
+
...collectSchemaValueObjectDescriptors([
|
|
1377
|
+
...(mod.deriveds ?? []).map((read) => read.returns),
|
|
1378
|
+
...(mod.combineds ?? []).map((read) => read.returns),
|
|
1379
|
+
]),
|
|
1363
1380
|
]) {
|
|
1364
1381
|
const prior = byName.get(descriptor.name);
|
|
1365
1382
|
if (
|
|
@@ -1508,7 +1525,7 @@ function collectDartSharedSymbols(
|
|
|
1508
1525
|
}
|
|
1509
1526
|
|
|
1510
1527
|
/** Per-domain skip/import options against the shared-symbol dedup — the SAME derivation the flat
|
|
1511
|
-
* emit and the layered-packages emit both apply before generateDartDomain / the
|
|
1528
|
+
* emit and the layered-packages emit both apply before generateDartDomain / the session client. */
|
|
1512
1529
|
function domainDedupOpts(
|
|
1513
1530
|
mod: DomainModule,
|
|
1514
1531
|
shared: DartSharedSymbols
|
|
@@ -1564,7 +1581,7 @@ function domainDedupOpts(
|
|
|
1564
1581
|
}
|
|
1565
1582
|
|
|
1566
1583
|
/** Remove the PRE-barrel flat layout's generated files from `dartOut` root (they now live under
|
|
1567
|
-
* `src/generated/`):
|
|
1584
|
+
* `src/generated/`): retired copied support + the per-domain files. Targeted by exact known
|
|
1568
1585
|
* name — `dartOut` is typically the tenant package's `lib/`, so never sweep directories. */
|
|
1569
1586
|
function removeLegacyFlatDartLayout(
|
|
1570
1587
|
dartOut: string,
|
|
@@ -1572,7 +1589,7 @@ function removeLegacyFlatDartLayout(
|
|
|
1572
1589
|
): void {
|
|
1573
1590
|
rmSync(path.join(dartOut, "nomos_client.dart"), { force: true });
|
|
1574
1591
|
rmSync(path.join(dartOut, "nomos_framework.dart"), { force: true });
|
|
1575
|
-
for (const f of
|
|
1592
|
+
for (const f of LEGACY_VENDORED_DART_SUPPORT)
|
|
1576
1593
|
rmSync(path.join(dartOut, "src", f), { force: true });
|
|
1577
1594
|
for (const name of moduleNames) {
|
|
1578
1595
|
rmSync(path.join(dartOut, `${name}.dart`), { force: true });
|
|
@@ -1583,8 +1600,8 @@ function removeLegacyFlatDartLayout(
|
|
|
1583
1600
|
/**
|
|
1584
1601
|
* Emit the typed Dart for the composed domain modules into `dartOut`: one
|
|
1585
1602
|
* `<name>.dart` per domain (the SAME `generateDartDomain` path the co2 tenant's
|
|
1586
|
-
* `emit_dart.ts` drives)
|
|
1587
|
-
*
|
|
1603
|
+
* `emit_dart.ts` drives). Runtime support is always imported from the fixed
|
|
1604
|
+
* `nomos_client` package.
|
|
1588
1605
|
* Everything lands under `dartOut/src/generated/` (generated-owned, wiped per
|
|
1589
1606
|
* compile) behind ONE generated package barrel `dartOut/<barrelName>.dart` — the
|
|
1590
1607
|
* analyzer-clean plug-and-play entrypoint (zero `show`/`hide`; framework-shared
|
|
@@ -1644,6 +1661,491 @@ function beginAtomicGenDir(genDir: string): void {
|
|
|
1644
1661
|
});
|
|
1645
1662
|
}
|
|
1646
1663
|
|
|
1664
|
+
/** Select the operation contracts shared by both generated language bindings. */
|
|
1665
|
+
function operationProtoLaws(
|
|
1666
|
+
modules: readonly DomainModule[],
|
|
1667
|
+
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
1668
|
+
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
|
|
1669
|
+
): readonly OperationProtoLaw[] {
|
|
1670
|
+
const fixedDirectives = (graph: LawGraph): string[] => {
|
|
1671
|
+
const selected = fixedFrameworkCallDefinitions.flatMap((definition) => {
|
|
1672
|
+
const route = definition.routeId ?? definition.directive.id;
|
|
1673
|
+
return graph.nodes.some(
|
|
1674
|
+
(node) =>
|
|
1675
|
+
node.kind === "intent" &&
|
|
1676
|
+
(node.entity.label === route ||
|
|
1677
|
+
node.publicRoute === route ||
|
|
1678
|
+
node.entity.label === definition.directive.id ||
|
|
1679
|
+
node.publicRoute === definition.directive.id)
|
|
1680
|
+
)
|
|
1681
|
+
? [route]
|
|
1682
|
+
: [];
|
|
1683
|
+
});
|
|
1684
|
+
if (
|
|
1685
|
+
graph.nodes.some(
|
|
1686
|
+
(node) =>
|
|
1687
|
+
node.kind === "intent" &&
|
|
1688
|
+
(node.entity.label === BUSINESS_CORRECTION_DIRECTIVE ||
|
|
1689
|
+
node.publicRoute === BUSINESS_CORRECTION_DIRECTIVE)
|
|
1690
|
+
)
|
|
1691
|
+
) {
|
|
1692
|
+
selected.push(BUSINESS_CORRECTION_DIRECTIVE);
|
|
1693
|
+
}
|
|
1694
|
+
// Generated owned-record actions and the device approval door are emitted
|
|
1695
|
+
// by the TypeScript Reception as nominal calls. Their private law routes
|
|
1696
|
+
// still need operation payload messages in the packed client schema.
|
|
1697
|
+
for (const node of graph.nodes) {
|
|
1698
|
+
if (node.kind !== "intent") continue;
|
|
1699
|
+
const route = node.publicRoute ?? node.entity.label;
|
|
1700
|
+
if (/^nomos_internal_(?:share|unshare|transfer)_owned_record_[0-9a-f_]+$/.test(route)
|
|
1701
|
+
|| [OWNED_DEVICE_APPROVAL_DIRECTIVE, OWNED_INITIALIZE_DIRECTIVE,
|
|
1702
|
+
OWNED_RECOVERY_ACCEPT_DIRECTIVE, OWNED_RECOVERY_COMPLETE_DIRECTIVE,
|
|
1703
|
+
OWNED_RECOVERY_PROTECT_DIRECTIVE, OWNED_RECOVERY_REQUEST_DIRECTIVE].includes(route)) {
|
|
1704
|
+
selected.push(route);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
// The declared receiving-group recovery door lives in the compiler-owned application-home graph
|
|
1708
|
+
// (no tenant DomainModule reaches this list through `dartFrameworkOperationDirectiveIds`); the
|
|
1709
|
+
// generated Dart binds it like a fixed framework call, so its payload rides the same contract.
|
|
1710
|
+
if (
|
|
1711
|
+
graph.nodes.some(
|
|
1712
|
+
(node) =>
|
|
1713
|
+
node.kind === "intent" &&
|
|
1714
|
+
node.entity.label === receivingGroupRecoveryModule.RECOVER_RECEIVING_GROUP_DEVICE_ID
|
|
1715
|
+
)
|
|
1716
|
+
) {
|
|
1717
|
+
selected.push(receivingGroupRecoveryModule.RECOVER_RECEIVING_GROUP_DEVICE_ID);
|
|
1718
|
+
}
|
|
1719
|
+
// The framework child-authentication repair is composed into the same graph whenever the package
|
|
1720
|
+
// births children and is bound by the generated Dart the same way (by id), so its payload message
|
|
1721
|
+
// must ride the operation transport contract too.
|
|
1722
|
+
if (
|
|
1723
|
+
graph.nodes.some(
|
|
1724
|
+
(node) =>
|
|
1725
|
+
node.kind === "intent" &&
|
|
1726
|
+
node.entity.label === childAuthenticationModule.REPAIR_CHILD_AUTHENTICATION_ID
|
|
1727
|
+
)
|
|
1728
|
+
) {
|
|
1729
|
+
selected.push(childAuthenticationModule.REPAIR_CHILD_AUTHENTICATION_ID);
|
|
1730
|
+
}
|
|
1731
|
+
// The parent-directed model transition's PLATFORM DOOR (architecture/parent_directed_model_transition.md)
|
|
1732
|
+
// is composed into the same graph whenever the package births children, and the operator CLI authors it
|
|
1733
|
+
// remotely as a nominal DirectiveCall — so its payload message must ride the operation transport
|
|
1734
|
+
// contract exactly as the child-authentication repair's does. The child-side acceptor needs no entry:
|
|
1735
|
+
// nothing outside the kernel ever encodes it (it arrives as a carried effect, never as a client call).
|
|
1736
|
+
if (
|
|
1737
|
+
graph.nodes.some(
|
|
1738
|
+
(node) =>
|
|
1739
|
+
node.kind === "intent" &&
|
|
1740
|
+
node.entity.label === modelTransitionModule.DIRECT_CHILD_MODEL_TRANSITION_ID
|
|
1741
|
+
)
|
|
1742
|
+
) {
|
|
1743
|
+
selected.push(modelTransitionModule.DIRECT_CHILD_MODEL_TRANSITION_ID);
|
|
1744
|
+
}
|
|
1745
|
+
// THE MODEL-AUTHORITY SELF-SEAT (same design, §9): the Reception offers it on the workspace itself
|
|
1746
|
+
// as a nominal DirectiveCall, so its holder-set payload rides the operation transport contract too.
|
|
1747
|
+
if (
|
|
1748
|
+
graph.nodes.some(
|
|
1749
|
+
(node) =>
|
|
1750
|
+
node.kind === "intent" &&
|
|
1751
|
+
node.entity.label === modelAuthorityRepairModule.SEAT_MODEL_AUTHORITY_ID
|
|
1752
|
+
)
|
|
1753
|
+
) {
|
|
1754
|
+
selected.push(modelAuthorityRepairModule.SEAT_MODEL_AUTHORITY_ID);
|
|
1755
|
+
}
|
|
1756
|
+
return [...new Set(selected)];
|
|
1757
|
+
};
|
|
1758
|
+
const covered = new Set<string>();
|
|
1759
|
+
const laws = modules.flatMap((module) => {
|
|
1760
|
+
const graph = lawGraphs.get(module.name);
|
|
1761
|
+
const paths = lawPaths.get(module.name);
|
|
1762
|
+
if (graph === undefined || paths === undefined) return [];
|
|
1763
|
+
covered.add(module.name);
|
|
1764
|
+
// The transport schema is the typed boundary between an application and
|
|
1765
|
+
// the Peer Office, not a second encoding of the kernel's entire interior.
|
|
1766
|
+
// generateOperationProtoSchema includes every public intent itself. Add
|
|
1767
|
+
// only the private framework capabilities which the Peer Office invokes
|
|
1768
|
+
// directly; private law steps remain inside the kernel and need no host
|
|
1769
|
+
// transport contract.
|
|
1770
|
+
const directives = new Set<string>();
|
|
1771
|
+
for (const directive of dartBridgeClientOperationDirectiveIds(module, graph)) {
|
|
1772
|
+
directives.add(directive);
|
|
1773
|
+
}
|
|
1774
|
+
for (const directive of dartFrameworkOperationDirectiveIds({
|
|
1775
|
+
module,
|
|
1776
|
+
graph,
|
|
1777
|
+
paths,
|
|
1778
|
+
})) {
|
|
1779
|
+
directives.add(directive);
|
|
1780
|
+
}
|
|
1781
|
+
for (const directive of fixedDirectives(graph)) directives.add(directive);
|
|
1782
|
+
for (const policy of module.applicationWorkspaceSharing ?? []) {
|
|
1783
|
+
directives.add(policy.invitationLaw.redeem.id);
|
|
1784
|
+
}
|
|
1785
|
+
return [{ graph, paths, directives: [...directives] }];
|
|
1786
|
+
});
|
|
1787
|
+
// Dependency and compiler-owned framework graphs also participate in the
|
|
1788
|
+
// generated Peer Office surface even when they have no tenant DomainModule
|
|
1789
|
+
// in this package. Their public members are included automatically; retain
|
|
1790
|
+
// only the fixed private capabilities that generated runtime code invokes.
|
|
1791
|
+
for (const [name, graph] of lawGraphs) {
|
|
1792
|
+
if (covered.has(name)) continue;
|
|
1793
|
+
const paths = lawPaths.get(name);
|
|
1794
|
+
if (paths === undefined) continue;
|
|
1795
|
+
laws.push({ graph, paths, directives: fixedDirectives(graph) });
|
|
1796
|
+
}
|
|
1797
|
+
return laws;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
function operationProtoSchema(
|
|
1801
|
+
modules: readonly DomainModule[],
|
|
1802
|
+
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
1803
|
+
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
|
|
1804
|
+
) {
|
|
1805
|
+
return generateOperationProtoSchema(
|
|
1806
|
+
operationProtoLaws(modules, lawGraphs, lawPaths)
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
function protocGenEs(): string {
|
|
1811
|
+
try {
|
|
1812
|
+
const packageRoot = path.dirname(
|
|
1813
|
+
createRequire(import.meta.url).resolve(
|
|
1814
|
+
"@bufbuild/protoc-gen-es/package.json"
|
|
1815
|
+
)
|
|
1816
|
+
);
|
|
1817
|
+
return path.join(packageRoot, "bin", "protoc-gen-es");
|
|
1818
|
+
} catch {
|
|
1819
|
+
throw new Error(
|
|
1820
|
+
"TypeScript operation Protobuf generation requires @bufbuild/protoc-gen-es"
|
|
1821
|
+
);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/** Emit the single application operation schema and its generated TypeScript
|
|
1826
|
+
* binding. The generated application bundle consumes this module directly;
|
|
1827
|
+
* no client is permitted to reproduce Protobuf field tags. */
|
|
1828
|
+
function emitTsOperationProto(
|
|
1829
|
+
cacheRoot: string,
|
|
1830
|
+
outDir: string,
|
|
1831
|
+
lawPath: string,
|
|
1832
|
+
modules: readonly DomainModule[],
|
|
1833
|
+
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
1834
|
+
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
|
|
1835
|
+
): string[] {
|
|
1836
|
+
const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
|
|
1837
|
+
const protoPath = path.join(outDir, schema.fileName);
|
|
1838
|
+
writeFileSync(protoPath, schema.source, "utf8");
|
|
1839
|
+
// Standard protoc owns the descriptor format. The kernel checker compares
|
|
1840
|
+
// this complete schema, not sample values or strings in a generated client.
|
|
1841
|
+
execFileSync("protoc", ["-I", outDir, "--include_imports",
|
|
1842
|
+
`--descriptor_set_out=${path.join(outDir, "operation_payloads.descriptor.pb")}`, protoPath], { stdio: "pipe" });
|
|
1843
|
+
checkOperationContracts(lawPath, path.join(outDir, "operation_payloads.descriptor.pb"), DSL_DIR, cacheRoot);
|
|
1844
|
+
const binding = path.join(outDir, "operation_payloads_pb.ts");
|
|
1845
|
+
const generator = protocGenEs();
|
|
1846
|
+
const protocVersion = execFileSync("protoc", ["--version"], {
|
|
1847
|
+
encoding: "utf8",
|
|
1848
|
+
}).trim();
|
|
1849
|
+
const { cacheDir, cachedBinding } = tsOperationProtoCache(
|
|
1850
|
+
cacheRoot,
|
|
1851
|
+
schema.source,
|
|
1852
|
+
protocVersion,
|
|
1853
|
+
generator
|
|
1854
|
+
);
|
|
1855
|
+
if (existsSync(cachedBinding)) {
|
|
1856
|
+
copyFileSync(cachedBinding, binding);
|
|
1857
|
+
return [protoPath, binding];
|
|
1858
|
+
}
|
|
1859
|
+
try {
|
|
1860
|
+
execFileSync("protoc", tsProtocArgs(outDir, protoPath, generator), {
|
|
1861
|
+
stdio: "pipe",
|
|
1862
|
+
});
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
1865
|
+
throw new Error(
|
|
1866
|
+
`TypeScript operation Protobuf generation failed. Install protoc and ` +
|
|
1867
|
+
`@bufbuild/protoc-gen-es. ${detail}`
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
if (!existsSync(binding)) {
|
|
1871
|
+
throw new Error(
|
|
1872
|
+
"TypeScript operation Protobuf generation did not emit 'operation_payloads_pb.ts'"
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
publishCachedBinding(binding, cacheDir, cachedBinding);
|
|
1876
|
+
return [protoPath, binding];
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// protoc-gen-es / protoc-gen-dart are pure, expensive transforms over the application schema
|
|
1880
|
+
// (the Dart plugin is a Dart script whose JIT start alone is seconds). Their exact output is
|
|
1881
|
+
// cached by every input that can affect the bytes. There is no semantic cache manifest: the
|
|
1882
|
+
// key is content-addressed, the generated binding is disposable, and the normal TypeScript /
|
|
1883
|
+
// Dart compile still validates every hit. The prewarm variants below populate the same cache
|
|
1884
|
+
// from a scratch directory while the rest of the compile proceeds; the emit then hits it.
|
|
1885
|
+
function tsOperationProtoCache(
|
|
1886
|
+
cacheRoot: string,
|
|
1887
|
+
schemaSource: string,
|
|
1888
|
+
protocVersion: string,
|
|
1889
|
+
generator: string
|
|
1890
|
+
): { cacheDir: string; cachedBinding: string } {
|
|
1891
|
+
const generatorPackage = path.join(
|
|
1892
|
+
path.dirname(path.dirname(generator)),
|
|
1893
|
+
"package.json"
|
|
1894
|
+
);
|
|
1895
|
+
const cacheIdentity = createHash("sha256")
|
|
1896
|
+
.update("nomos.operation-protobuf.typescript.v1\0")
|
|
1897
|
+
.update(schemaSource)
|
|
1898
|
+
.update("\0")
|
|
1899
|
+
.update(protocVersion)
|
|
1900
|
+
.update("\0target=ts,import_extension=js\0")
|
|
1901
|
+
.update(readFileSync(generatorPackage))
|
|
1902
|
+
.digest("hex");
|
|
1903
|
+
const cacheDir = path.join(
|
|
1904
|
+
cacheRoot,
|
|
1905
|
+
"node_modules",
|
|
1906
|
+
".cache",
|
|
1907
|
+
"nomos",
|
|
1908
|
+
"operation-protobuf",
|
|
1909
|
+
"typescript"
|
|
1910
|
+
);
|
|
1911
|
+
return { cacheDir, cachedBinding: path.join(cacheDir, `${cacheIdentity}.ts`) };
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
function tsProtocArgs(
|
|
1915
|
+
includeDir: string,
|
|
1916
|
+
protoPath: string,
|
|
1917
|
+
generator: string
|
|
1918
|
+
): string[] {
|
|
1919
|
+
return [
|
|
1920
|
+
"-I",
|
|
1921
|
+
includeDir,
|
|
1922
|
+
`--plugin=protoc-gen-es=${generator}`,
|
|
1923
|
+
`--es_out=${includeDir}`,
|
|
1924
|
+
"--es_opt=target=ts,import_extension=js",
|
|
1925
|
+
protoPath,
|
|
1926
|
+
];
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
function dartProtocPlugin(): string | undefined {
|
|
1930
|
+
const localPlugin = path.resolve(
|
|
1931
|
+
DSL_DIR,
|
|
1932
|
+
"..",
|
|
1933
|
+
"nomos_flutter",
|
|
1934
|
+
"tool",
|
|
1935
|
+
"protoc-gen-dart"
|
|
1936
|
+
);
|
|
1937
|
+
const configuredPlugin = process.env.NOMOS_PROTOC_GEN_DART;
|
|
1938
|
+
return configuredPlugin !== undefined && configuredPlugin !== ""
|
|
1939
|
+
? path.resolve(configuredPlugin)
|
|
1940
|
+
: existsSync(localPlugin)
|
|
1941
|
+
? localPlugin
|
|
1942
|
+
: undefined;
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
function dartProtocArgs(
|
|
1946
|
+
includeDir: string,
|
|
1947
|
+
protoPath: string,
|
|
1948
|
+
plugin: string | undefined
|
|
1949
|
+
): string[] {
|
|
1950
|
+
return [
|
|
1951
|
+
"-I",
|
|
1952
|
+
includeDir,
|
|
1953
|
+
...(plugin === undefined ? [] : [`--plugin=protoc-gen-dart=${plugin}`]),
|
|
1954
|
+
`--dart_out=${includeDir}`,
|
|
1955
|
+
protoPath,
|
|
1956
|
+
];
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
function dartOperationProtoCache(
|
|
1960
|
+
schemaSource: string,
|
|
1961
|
+
protocVersion: string,
|
|
1962
|
+
plugin: string | undefined
|
|
1963
|
+
): { cacheDir: string; cachedBinding: string } {
|
|
1964
|
+
const generatorIdentity = (() => {
|
|
1965
|
+
if (plugin === undefined) return "PATH:protoc-gen-dart";
|
|
1966
|
+
const files = [
|
|
1967
|
+
plugin,
|
|
1968
|
+
path.join(path.dirname(plugin), "protoc_plugin.dart"),
|
|
1969
|
+
path.join(path.dirname(path.dirname(plugin)), "pubspec.lock"),
|
|
1970
|
+
];
|
|
1971
|
+
const digest = createHash("sha256");
|
|
1972
|
+
for (const file of files) {
|
|
1973
|
+
digest.update(`\0${file}\0`);
|
|
1974
|
+
if (existsSync(file)) digest.update(readFileSync(file));
|
|
1975
|
+
}
|
|
1976
|
+
return digest.digest("hex");
|
|
1977
|
+
})();
|
|
1978
|
+
const cacheIdentity = createHash("sha256")
|
|
1979
|
+
.update("nomos.operation-protobuf.dart.v1\0")
|
|
1980
|
+
.update(schemaSource)
|
|
1981
|
+
.update("\0")
|
|
1982
|
+
.update(protocVersion)
|
|
1983
|
+
.update("\0")
|
|
1984
|
+
.update(generatorIdentity)
|
|
1985
|
+
.digest("hex");
|
|
1986
|
+
const cacheDir = path.join(
|
|
1987
|
+
process.cwd(),
|
|
1988
|
+
"node_modules",
|
|
1989
|
+
".cache",
|
|
1990
|
+
"nomos",
|
|
1991
|
+
"operation-protobuf",
|
|
1992
|
+
"dart"
|
|
1993
|
+
);
|
|
1994
|
+
return { cacheDir, cachedBinding: path.join(cacheDir, `${cacheIdentity}.dart`) };
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
function publishCachedBinding(
|
|
1998
|
+
generated: string,
|
|
1999
|
+
cacheDir: string,
|
|
2000
|
+
cachedBinding: string
|
|
2001
|
+
): void {
|
|
2002
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
2003
|
+
const temporaryCache = `${cachedBinding}.tmp-${process.pid}`;
|
|
2004
|
+
copyFileSync(generated, temporaryCache);
|
|
2005
|
+
renameSync(temporaryCache, cachedBinding);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
/** Generate into a scratch directory beside the cache and publish only the binding the emit
|
|
2009
|
+
* copies out; a failure here is silent because the real emit reports it with its own remedy. */
|
|
2010
|
+
async function prewarmOperationProtoCache(
|
|
2011
|
+
cacheDir: string,
|
|
2012
|
+
cachedBinding: string,
|
|
2013
|
+
schema: { readonly fileName: string; readonly source: string },
|
|
2014
|
+
generatedName: string,
|
|
2015
|
+
args: (includeDir: string, protoPath: string) => string[]
|
|
2016
|
+
): Promise<void> {
|
|
2017
|
+
if (existsSync(cachedBinding)) return;
|
|
2018
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
2019
|
+
const scratch = mkdtempSync(path.join(cacheDir, ".prewarm-"));
|
|
2020
|
+
try {
|
|
2021
|
+
const protoPath = path.join(scratch, schema.fileName);
|
|
2022
|
+
writeFileSync(protoPath, schema.source, "utf8");
|
|
2023
|
+
await execFileAsync("protoc", args(scratch, protoPath));
|
|
2024
|
+
const generated = path.join(scratch, generatedName);
|
|
2025
|
+
if (existsSync(generated))
|
|
2026
|
+
publishCachedBinding(generated, cacheDir, cachedBinding);
|
|
2027
|
+
} catch {
|
|
2028
|
+
// The emit's own protoc invocation names the remedy.
|
|
2029
|
+
} finally {
|
|
2030
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
async function prewarmTsOperationProto(
|
|
2035
|
+
cacheRoot: string,
|
|
2036
|
+
modules: readonly DomainModule[],
|
|
2037
|
+
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
2038
|
+
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
|
|
2039
|
+
): Promise<void> {
|
|
2040
|
+
try {
|
|
2041
|
+
const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
|
|
2042
|
+
const generator = protocGenEs();
|
|
2043
|
+
const { stdout } = await execFileAsync("protoc", ["--version"], {
|
|
2044
|
+
encoding: "utf8",
|
|
2045
|
+
});
|
|
2046
|
+
const { cacheDir, cachedBinding } = tsOperationProtoCache(
|
|
2047
|
+
cacheRoot,
|
|
2048
|
+
schema.source,
|
|
2049
|
+
stdout.trim(),
|
|
2050
|
+
generator
|
|
2051
|
+
);
|
|
2052
|
+
await prewarmOperationProtoCache(
|
|
2053
|
+
cacheDir,
|
|
2054
|
+
cachedBinding,
|
|
2055
|
+
schema,
|
|
2056
|
+
"operation_payloads_pb.ts",
|
|
2057
|
+
(includeDir, protoPath) => tsProtocArgs(includeDir, protoPath, generator)
|
|
2058
|
+
);
|
|
2059
|
+
} catch {
|
|
2060
|
+
// The emit's own protoc invocation names the remedy.
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
async function prewarmDartOperationProto(
|
|
2065
|
+
modules: readonly DomainModule[],
|
|
2066
|
+
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
2067
|
+
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
|
|
2068
|
+
): Promise<void> {
|
|
2069
|
+
try {
|
|
2070
|
+
const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
|
|
2071
|
+
const plugin = dartProtocPlugin();
|
|
2072
|
+
const { stdout } = await execFileAsync("protoc", ["--version"], {
|
|
2073
|
+
encoding: "utf8",
|
|
2074
|
+
});
|
|
2075
|
+
const { cacheDir, cachedBinding } = dartOperationProtoCache(
|
|
2076
|
+
schema.source,
|
|
2077
|
+
stdout.trim(),
|
|
2078
|
+
plugin
|
|
2079
|
+
);
|
|
2080
|
+
await prewarmOperationProtoCache(
|
|
2081
|
+
cacheDir,
|
|
2082
|
+
cachedBinding,
|
|
2083
|
+
schema,
|
|
2084
|
+
schema.dartFileName,
|
|
2085
|
+
(includeDir, protoPath) => dartProtocArgs(includeDir, protoPath, plugin)
|
|
2086
|
+
);
|
|
2087
|
+
} catch {
|
|
2088
|
+
// The emit's own protoc invocation names the remedy.
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
function emitDartOperationProto(
|
|
2093
|
+
genDir: string,
|
|
2094
|
+
modules: readonly DomainModule[],
|
|
2095
|
+
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
2096
|
+
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>
|
|
2097
|
+
): string[] {
|
|
2098
|
+
const schema = operationProtoSchema(modules, lawGraphs, lawPaths);
|
|
2099
|
+
const protoPath = path.join(genDir, schema.fileName);
|
|
2100
|
+
writeFileSync(protoPath, schema.source, "utf8");
|
|
2101
|
+
const plugin = dartProtocPlugin();
|
|
2102
|
+
const protocVersion = execFileSync("protoc", ["--version"], {
|
|
2103
|
+
encoding: "utf8",
|
|
2104
|
+
}).trim();
|
|
2105
|
+
const { cacheDir, cachedBinding } = dartOperationProtoCache(
|
|
2106
|
+
schema.source,
|
|
2107
|
+
protocVersion,
|
|
2108
|
+
plugin
|
|
2109
|
+
);
|
|
2110
|
+
const generatedBinding = path.join(genDir, schema.dartFileName);
|
|
2111
|
+
if (existsSync(cachedBinding)) {
|
|
2112
|
+
copyFileSync(cachedBinding, generatedBinding);
|
|
2113
|
+
return [protoPath, generatedBinding];
|
|
2114
|
+
}
|
|
2115
|
+
try {
|
|
2116
|
+
execFileSync("protoc", dartProtocArgs(genDir, protoPath, plugin), {
|
|
2117
|
+
stdio: "pipe",
|
|
2118
|
+
});
|
|
2119
|
+
} catch (error) {
|
|
2120
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2121
|
+
throw new Error(
|
|
2122
|
+
`Dart operation Protobuf generation failed. Install protoc and protoc-gen-dart ` +
|
|
2123
|
+
`(or set NOMOS_PROTOC_GEN_DART). ${detail}`
|
|
2124
|
+
);
|
|
2125
|
+
}
|
|
2126
|
+
const generated = readdirSync(genDir)
|
|
2127
|
+
.filter(
|
|
2128
|
+
(name) =>
|
|
2129
|
+
name.startsWith("operation_payloads.pb") && name.endsWith(".dart")
|
|
2130
|
+
)
|
|
2131
|
+
.sort()
|
|
2132
|
+
.map((name) => path.join(genDir, name));
|
|
2133
|
+
if (!generated.some((file) => path.basename(file) === schema.dartFileName)) {
|
|
2134
|
+
throw new Error(
|
|
2135
|
+
`Dart operation Protobuf generation did not emit '${schema.dartFileName}'`
|
|
2136
|
+
);
|
|
2137
|
+
}
|
|
2138
|
+
// Runtime clients use only the generated messages. Reflection JSON and the
|
|
2139
|
+
// empty enum companion are generator by-products, not another contract or
|
|
2140
|
+
// shipped source of truth.
|
|
2141
|
+
for (const file of generated) {
|
|
2142
|
+
if (path.basename(file) !== schema.dartFileName)
|
|
2143
|
+
rmSync(file, { force: true });
|
|
2144
|
+
}
|
|
2145
|
+
publishCachedBinding(generatedBinding, cacheDir, cachedBinding);
|
|
2146
|
+
return [protoPath, generatedBinding];
|
|
2147
|
+
}
|
|
2148
|
+
|
|
1647
2149
|
function emitDart(
|
|
1648
2150
|
modules: readonly DomainModule[],
|
|
1649
2151
|
dartOut: string,
|
|
@@ -1651,9 +2153,8 @@ function emitDart(
|
|
|
1651
2153
|
lawGraphs: ReadonlyMap<string, LawGraph>,
|
|
1652
2154
|
lawPaths: ReadonlyMap<string, ReadonlyMap<string, string>>,
|
|
1653
2155
|
packageInterface: Record<string, DomainInterface> = {},
|
|
1654
|
-
supportImport: DartSupportImport = "
|
|
2156
|
+
supportImport: DartSupportImport = "package",
|
|
1655
2157
|
barrelName = "nomos_domains",
|
|
1656
|
-
compat?: NomosCompat,
|
|
1657
2158
|
names?: Readonly<Record<string, string>>,
|
|
1658
2159
|
describePackageName = barrelName
|
|
1659
2160
|
): string[] {
|
|
@@ -1667,133 +2168,8 @@ function emitDart(
|
|
|
1667
2168
|
// Transactional: back the prior generated dir aside instead of destroying it (compile atomicity).
|
|
1668
2169
|
beginAtomicGenDir(genDir);
|
|
1669
2170
|
|
|
1670
|
-
//
|
|
1671
|
-
|
|
1672
|
-
if (supportImport === "vendored") {
|
|
1673
|
-
ensureVendoredPackageRequirements(dartOut);
|
|
1674
|
-
mkdirSync(path.join(genDir, "src"), { recursive: true });
|
|
1675
|
-
// Core files come from the pure `nomos_types` package, runtime files from `nomos_client`
|
|
1676
|
-
// (the layered split). Runtime files import the core via `package:nomos_types/...` — the
|
|
1677
|
-
// vendored copy rewrites that to the local shim so the directory stays self-contained.
|
|
1678
|
-
const coreDir = path.join(DSL_DIR, "dart_types", "lib", "src");
|
|
1679
|
-
const clientDir = path.join(DSL_DIR, "dart", "lib", "src");
|
|
1680
|
-
for (const f of DART_SUPPORT_CORE_SRC)
|
|
1681
|
-
copyFileSync(path.join(coreDir, f), path.join(genDir, "src", f));
|
|
1682
|
-
for (const f of DART_SUPPORT_RUNTIME_SRC) {
|
|
1683
|
-
const src = readFileSync(path.join(clientDir, f), "utf8")
|
|
1684
|
-
.replace(
|
|
1685
|
-
/import 'package:nomos_types\/nomos_types\.dart'/g,
|
|
1686
|
-
`import '${VENDORED_TYPES_SHIM}'`
|
|
1687
|
-
)
|
|
1688
|
-
.replace(
|
|
1689
|
-
/import 'package:nomos_types\/src\/(?:nomos_ref|foreign_ref|versioned_nomos_ref)\.dart'/g,
|
|
1690
|
-
`import '${VENDORED_TYPES_SHIM}'`
|
|
1691
|
-
)
|
|
1692
|
-
.replace(
|
|
1693
|
-
/import 'package:nomos_types\/nomos_kernel_protocol\.dart'/g,
|
|
1694
|
-
`import '${VENDORED_KERNEL_PROTOCOL_SHIM}'`
|
|
1695
|
-
);
|
|
1696
|
-
writeFileSync(path.join(genDir, "src", f), src, "utf8");
|
|
1697
|
-
}
|
|
1698
|
-
// Protobuf is the sole kernel/host message schema. Flat vendored packages receive a generated
|
|
1699
|
-
// projection of that schema, never a handwritten certificate mirror. The compiler copies only the
|
|
1700
|
-
// generated kernel files this runtime uses; `generate:kernel-proto-dart` remains their source.
|
|
1701
|
-
const generatedKernelProto = path.join(
|
|
1702
|
-
DSL_DIR,
|
|
1703
|
-
"dart_types",
|
|
1704
|
-
"lib",
|
|
1705
|
-
"src",
|
|
1706
|
-
"proto",
|
|
1707
|
-
"nomos",
|
|
1708
|
-
"kernel",
|
|
1709
|
-
"v1"
|
|
1710
|
-
);
|
|
1711
|
-
const vendoredKernelProto = path.join(
|
|
1712
|
-
genDir,
|
|
1713
|
-
"src",
|
|
1714
|
-
"proto",
|
|
1715
|
-
"nomos",
|
|
1716
|
-
"kernel",
|
|
1717
|
-
"v1"
|
|
1718
|
-
);
|
|
1719
|
-
mkdirSync(vendoredKernelProto, { recursive: true });
|
|
1720
|
-
for (const f of [
|
|
1721
|
-
"kernel.pb.dart",
|
|
1722
|
-
"kernel.pbenum.dart",
|
|
1723
|
-
"progress.pb.dart",
|
|
1724
|
-
"progress.pbenum.dart",
|
|
1725
|
-
]) {
|
|
1726
|
-
copyFileSync(
|
|
1727
|
-
path.join(generatedKernelProto, f),
|
|
1728
|
-
path.join(vendoredKernelProto, f)
|
|
1729
|
-
);
|
|
1730
|
-
}
|
|
1731
|
-
const generatedRuntimeProto = path.join(
|
|
1732
|
-
DSL_DIR,
|
|
1733
|
-
"dart_types",
|
|
1734
|
-
"lib",
|
|
1735
|
-
"src",
|
|
1736
|
-
"proto",
|
|
1737
|
-
"nomos",
|
|
1738
|
-
"runtime",
|
|
1739
|
-
"v2"
|
|
1740
|
-
);
|
|
1741
|
-
const vendoredRuntimeProto = path.join(
|
|
1742
|
-
genDir,
|
|
1743
|
-
"src",
|
|
1744
|
-
"proto",
|
|
1745
|
-
"nomos",
|
|
1746
|
-
"runtime",
|
|
1747
|
-
"v2"
|
|
1748
|
-
);
|
|
1749
|
-
mkdirSync(vendoredRuntimeProto, { recursive: true });
|
|
1750
|
-
for (const f of ["runtime.pb.dart", "runtime.pbenum.dart"]) {
|
|
1751
|
-
copyFileSync(
|
|
1752
|
-
path.join(generatedRuntimeProto, f),
|
|
1753
|
-
path.join(vendoredRuntimeProto, f)
|
|
1754
|
-
);
|
|
1755
|
-
}
|
|
1756
|
-
writeFileSync(
|
|
1757
|
-
path.join(genDir, "src", VENDORED_KERNEL_PROTOCOL_SHIM),
|
|
1758
|
-
[
|
|
1759
|
-
"// GENERATED from proto/nomos/kernel/v1/kernel.proto by nomos-compile. DO NOT EDIT.",
|
|
1760
|
-
"library;",
|
|
1761
|
-
"",
|
|
1762
|
-
"export 'proto/nomos/kernel/v1/kernel.pb.dart';",
|
|
1763
|
-
"export 'proto/nomos/kernel/v1/progress.pb.dart';",
|
|
1764
|
-
"export 'proto/nomos/runtime/v2/runtime.pb.dart';",
|
|
1765
|
-
"",
|
|
1766
|
-
].join("\n"),
|
|
1767
|
-
"utf8"
|
|
1768
|
-
);
|
|
1769
|
-
// The local shim the rewritten runtime imports resolve through (the vendored twin of the
|
|
1770
|
-
// nomos_types barrel — core files only).
|
|
1771
|
-
writeFileSync(
|
|
1772
|
-
path.join(genDir, "src", VENDORED_TYPES_SHIM),
|
|
1773
|
-
[
|
|
1774
|
-
"// GENERATED by nomos-compile — the vendored twin of the nomos_types barrel. DO NOT EDIT.",
|
|
1775
|
-
"library;",
|
|
1776
|
-
"",
|
|
1777
|
-
...DART_SUPPORT_CORE_SRC.map((f) => `export '${f}';`),
|
|
1778
|
-
"",
|
|
1779
|
-
].join("\n"),
|
|
1780
|
-
"utf8"
|
|
1781
|
-
);
|
|
1782
|
-
// The vendored support barrel — explicit (never derived from the real nomos_client barrel,
|
|
1783
|
-
// which post-split re-exports the nomos_types PACKAGE the vendored tree must not depend on).
|
|
1784
|
-
writeFileSync(
|
|
1785
|
-
path.join(genDir, "nomos_client.dart"),
|
|
1786
|
-
[
|
|
1787
|
-
"// GENERATED by nomos-compile — the vendored nomos_client support barrel. DO NOT EDIT.",
|
|
1788
|
-
"library;",
|
|
1789
|
-
"",
|
|
1790
|
-
...DART_SUPPORT_SRC.map((f) => `export 'src/${f}';`),
|
|
1791
|
-
"",
|
|
1792
|
-
].join("\n"),
|
|
1793
|
-
"utf8"
|
|
1794
|
-
);
|
|
1795
|
-
}
|
|
1796
|
-
// (package-support mode vendors nothing — the genDir wipe above already cleared any prior set)
|
|
2171
|
+
// Fixed runtime support is imported from nomos_client; generated packages never copy it.
|
|
2172
|
+
ensureFlatDartPackageRequirements(dartOut);
|
|
1797
2173
|
|
|
1798
2174
|
// 2. FRAMEWORK-SHARED symbol dedup (Dart-codegen hygiene). Framework-owned symbols fold into MANY
|
|
1799
2175
|
// domains, so their generated Dart would be DEFINED once per domain — a package barrel re-exporting
|
|
@@ -1822,6 +2198,7 @@ function emitDart(
|
|
|
1822
2198
|
: undefined;
|
|
1823
2199
|
|
|
1824
2200
|
const written: string[] = [];
|
|
2201
|
+
written.push(...emitDartOperationProto(genDir, modules, lawGraphs, lawPaths));
|
|
1825
2202
|
|
|
1826
2203
|
const writeGenerated = (fileBase: string, src: string): string => {
|
|
1827
2204
|
if (!src.includes(DART_PACKAGE_IMPORT)) {
|
|
@@ -1830,23 +2207,16 @@ function emitDart(
|
|
|
1830
2207
|
);
|
|
1831
2208
|
}
|
|
1832
2209
|
const p = path.join(genDir, `${fileBase}.dart`);
|
|
1833
|
-
writeFileSync(
|
|
1834
|
-
p,
|
|
1835
|
-
supportImport === "vendored"
|
|
1836
|
-
? src.replace(DART_PACKAGE_IMPORT, DART_RELATIVE_IMPORT)
|
|
1837
|
-
: src,
|
|
1838
|
-
"utf8"
|
|
1839
|
-
);
|
|
2210
|
+
writeFileSync(p, src, "utf8");
|
|
1840
2211
|
domainSources.push(src);
|
|
1841
2212
|
sourceByFile.set(fileBase, src);
|
|
1842
2213
|
written.push(p);
|
|
1843
2214
|
return p;
|
|
1844
2215
|
};
|
|
1845
2216
|
|
|
1846
|
-
// Compiler-owned framework ceremonies
|
|
1847
|
-
//
|
|
1848
|
-
//
|
|
1849
|
-
// infrastructure a typed setup lane without reintroducing raw payload maps.
|
|
2217
|
+
// Compiler-owned framework ceremonies are generated stationery over the
|
|
2218
|
+
// fixed nomos_client API. Keeping this file private to `src/generated` gives
|
|
2219
|
+
// tests and generated facades a typed setup lane without runtime lifecycle.
|
|
1850
2220
|
const applicationFrameworkCallsSource = generateDartApplicationFrameworkCalls(
|
|
1851
2221
|
modules.flatMap((module) => {
|
|
1852
2222
|
const graph = lawGraphs.get(module.name);
|
|
@@ -1854,37 +2224,22 @@ function emitDart(
|
|
|
1854
2224
|
return graph === undefined || paths === undefined
|
|
1855
2225
|
? []
|
|
1856
2226
|
: [{ module, graph, paths }];
|
|
1857
|
-
})
|
|
2227
|
+
}),
|
|
2228
|
+
{ lawSha256: domainHash }
|
|
1858
2229
|
);
|
|
1859
2230
|
if (applicationFrameworkCallsSource !== undefined) {
|
|
1860
|
-
const source =
|
|
1861
|
-
supportImport === "vendored"
|
|
1862
|
-
? applicationFrameworkCallsSource
|
|
1863
|
-
.replace(
|
|
1864
|
-
"import 'package:nomos_types/nomos_kernel_protocol.dart' show BirthCertificate;",
|
|
1865
|
-
`import 'src/${VENDORED_KERNEL_PROTOCOL_SHIM}' show BirthCertificate;`
|
|
1866
|
-
)
|
|
1867
|
-
.replace(
|
|
1868
|
-
"import 'package:nomos_types/nomos_types.dart' show NomosSlot, Sha256Digest;",
|
|
1869
|
-
`import 'src/${VENDORED_TYPES_SHIM}' show NomosSlot, Sha256Digest;`
|
|
1870
|
-
)
|
|
1871
|
-
.replace(
|
|
1872
|
-
"import 'package:nomos_client/src/application_framework_calls.dart';",
|
|
1873
|
-
"import 'src/application_framework_calls.dart';"
|
|
1874
|
-
)
|
|
1875
|
-
.replace(
|
|
1876
|
-
"import 'package:nomos_client/src/generated_call.dart';",
|
|
1877
|
-
"import 'src/generated_call.dart';"
|
|
1878
|
-
)
|
|
1879
|
-
: applicationFrameworkCallsSource;
|
|
1880
2231
|
const applicationFrameworkCallsPath = path.join(
|
|
1881
2232
|
genDir,
|
|
1882
2233
|
"application.framework_calls.dart"
|
|
1883
2234
|
);
|
|
1884
|
-
writeFileSync(
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2235
|
+
writeFileSync(
|
|
2236
|
+
applicationFrameworkCallsPath,
|
|
2237
|
+
applicationFrameworkCallsSource,
|
|
2238
|
+
"utf8"
|
|
2239
|
+
);
|
|
2240
|
+
written.push(applicationFrameworkCallsPath);
|
|
2241
|
+
}
|
|
2242
|
+
|
|
1888
2243
|
// 2a. the shared framework file — the framework aggregates + directives + queries, ONCE. Its own
|
|
1889
2244
|
// surface is emitted in full (no skip). Imported by every domain that folds any framework symbol.
|
|
1890
2245
|
if (shared.frameworkMod !== undefined) {
|
|
@@ -1934,11 +2289,11 @@ function emitDart(
|
|
|
1934
2289
|
// break — fail with a clear diagnostic. Contract-mediated refs go through contracts.dart (no concrete edge).
|
|
1935
2290
|
guardNoConcreteImportCycle(modules, aggIdToDartFile);
|
|
1936
2291
|
|
|
1937
|
-
// 2b. one generated file per composed domain — the TYPES (`<name>.dart`) + the async
|
|
2292
|
+
// 2b. one generated file per composed domain — the TYPES (`<name>.dart`) + the async session
|
|
1938
2293
|
// CLIENT (`<name>.client.dart`, the Flutter sibling of `<name>.client.ts`).
|
|
1939
2294
|
for (const mod of modules) {
|
|
1940
2295
|
// Default the read-model allowlist to EVERY aggregate when the domain didn't pin one, so
|
|
1941
|
-
// `<name>.dart` carries a read model per aggregate — the
|
|
2296
|
+
// `<name>.dart` carries a read model per aggregate — the session client imports them. A domain
|
|
1942
2297
|
// that pins `readModelAggregates` keeps its choice (byte-identical to before this default).
|
|
1943
2298
|
// Skip framework symbols this domain folds (they live in the shared file); reference them via the
|
|
1944
2299
|
// framework import. Only pass the import when this domain actually folds a framework symbol.
|
|
@@ -2009,14 +2364,13 @@ function emitDart(
|
|
|
2009
2364
|
});
|
|
2010
2365
|
writeGenerated(mod.name, src);
|
|
2011
2366
|
|
|
2012
|
-
const clientSrc =
|
|
2367
|
+
const clientSrc = generateDartDomainClient(modForDart, {
|
|
2013
2368
|
domainHash,
|
|
2014
2369
|
callUsda: {
|
|
2015
2370
|
graph: lawGraphs.get(mod.name)!,
|
|
2016
2371
|
paths: lawPaths.get(mod.name)!,
|
|
2017
2372
|
},
|
|
2018
2373
|
interface: packageInterface[modForDart.domain ?? modForDart.name],
|
|
2019
|
-
...(compat !== undefined ? { compat } : {}),
|
|
2020
2374
|
supportImport,
|
|
2021
2375
|
...(domainFrameworkImport !== undefined
|
|
2022
2376
|
? { frameworkImportUri: domainFrameworkImport }
|
|
@@ -2028,6 +2382,7 @@ function emitDart(
|
|
|
2028
2382
|
const applicationSrc = generateDartApplication(modForDart, {
|
|
2029
2383
|
supportImport,
|
|
2030
2384
|
packageAggregateDomains,
|
|
2385
|
+
callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! },
|
|
2031
2386
|
});
|
|
2032
2387
|
const clientFile = `${mod.name}.client.dart`;
|
|
2033
2388
|
const applicationFile = `${mod.name}.application.dart`;
|
|
@@ -2036,7 +2391,7 @@ function emitDart(
|
|
|
2036
2391
|
applicationSrc,
|
|
2037
2392
|
clientFile,
|
|
2038
2393
|
applicationFile,
|
|
2039
|
-
new Set(dartApplicationPublicClassNames(modForDart))
|
|
2394
|
+
new Set(dartApplicationPublicClassNames(modForDart, { callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }))
|
|
2040
2395
|
);
|
|
2041
2396
|
const cp = path.join(genDir, clientFile);
|
|
2042
2397
|
writeFileSync(cp, encapsulated.clientPart, "utf8");
|
|
@@ -2101,9 +2456,7 @@ function emitDart(
|
|
|
2101
2456
|
),
|
|
2102
2457
|
// The generated clients import their runtime support directly. Keeping it out of the public barrel is
|
|
2103
2458
|
// the surface firewall: a tenant import receives business types and application APIs, never raw runtime.
|
|
2104
|
-
|
|
2105
|
-
? `export '${genRel}/src/${VENDORED_TYPES_SHIM}' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`
|
|
2106
|
-
: `export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`,
|
|
2459
|
+
`export 'package:nomos_types/nomos_types.dart' show NomosRef, NomosOptionalRef, NomosSlot, VersionedNomosRef, ReferenceVersionStatus, ReferenceVersionCurrent, ReferenceVersionOutdated, ReferenceVersionUnavailable${sha256DigestExport}${gitObjectIdExport};`,
|
|
2107
2460
|
...(contractsImportUri !== undefined
|
|
2108
2461
|
? tenantDartExport(
|
|
2109
2462
|
`${genRel}/${CONTRACTS_FILE}.dart`,
|
|
@@ -2124,7 +2477,7 @@ function emitDart(
|
|
|
2124
2477
|
),
|
|
2125
2478
|
`export '${genRel}/${
|
|
2126
2479
|
m.name
|
|
2127
|
-
}.application.dart' show ${dartApplicationPublicClassNames(m).join(
|
|
2480
|
+
}.application.dart' show ${dartApplicationPublicClassNames(m, { callUsda: { graph: lawGraphs.get(m.name)!, paths: lawPaths.get(m.name)! } }).join(
|
|
2128
2481
|
", "
|
|
2129
2482
|
)};`,
|
|
2130
2483
|
]),
|
|
@@ -2187,18 +2540,44 @@ const dartTypesImportFor = (generatedSource: string): string => {
|
|
|
2187
2540
|
* just the floor generated code needs — a lagging pin forces tenants into dependency_overrides. */
|
|
2188
2541
|
// 0.3.0: SurfacedValue<T>/SurfacedObservation/PendingConflictRow — generated Surfaced-field typing +
|
|
2189
2542
|
// the pendingConflicts read rows.
|
|
2190
|
-
const NOMOS_TYPES_DEP = "^0.
|
|
2543
|
+
const NOMOS_TYPES_DEP = "^0.11.2";
|
|
2191
2544
|
// 0.48.x: session-bound clients (XClient.bind/NomosClientInterface/DomainClientSessionMismatch/
|
|
2192
2545
|
// DomainNotInstalledForSession, engine.follow, pendingConflicts lanes) — the generated client
|
|
2193
2546
|
// REQUIRES this surface. 0.48.1 fixes NomosSession.watchPendingConflicts delegation.
|
|
2194
|
-
const NOMOS_CLIENT_DEP = "^0.
|
|
2195
|
-
const NOMOS_FLUTTER_DEP = "^0.64.3";
|
|
2547
|
+
const NOMOS_CLIENT_DEP = "^0.66.5";
|
|
2196
2548
|
// DRIFT TRIPWIRE: pubspec_pins.test.ts asserts these equal the CURRENT versions in dsl/dart/pubspec.yaml
|
|
2197
2549
|
// and dsl/dart_types/pubspec.yaml — a Dart release without this bump fails the suite, so tenants never
|
|
2198
2550
|
// land back in dependency_overrides (the exact co2 report this closes, third occurrence).
|
|
2199
2551
|
|
|
2200
2552
|
const isPubPackageName = (s: string): boolean => /^[a-z][a-z0-9_]*$/.test(s);
|
|
2201
2553
|
|
|
2554
|
+
/** The tenant pub package a FLAT `dart.out` writes into, if any: the nearest pubspec at or above
|
|
2555
|
+
* the output directory. ONE definition, because two places need the same answer — the dependency
|
|
2556
|
+
* reconciler that pins nomos_client into that pubspec, and the source-dogfood override that makes
|
|
2557
|
+
* the pin resolvable from a checkout. A flat `dart.out: pkg/lib` puts that pubspec ABOVE
|
|
2558
|
+
* `dart.out`, where the analyze gate's own package walk deliberately stops. */
|
|
2559
|
+
function flatDartPackageRoot(dartOut: string): string | undefined {
|
|
2560
|
+
let directory = path.resolve(dartOut);
|
|
2561
|
+
for (let hops = 0; hops < 8; hops++) {
|
|
2562
|
+
if (existsSync(path.join(directory, "pubspec.yaml"))) return directory;
|
|
2563
|
+
const parent = path.dirname(directory);
|
|
2564
|
+
if (parent === directory) return undefined;
|
|
2565
|
+
directory = parent;
|
|
2566
|
+
}
|
|
2567
|
+
return undefined;
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
function ensureFlatDartPackageRequirements(dartOut: string): void {
|
|
2571
|
+
const directory = flatDartPackageRoot(dartOut);
|
|
2572
|
+
if (directory === undefined) return;
|
|
2573
|
+
reconcilePubspec(directory, "", {
|
|
2574
|
+
nomos_types: NOMOS_TYPES_DEP,
|
|
2575
|
+
nomos_client: NOMOS_CLIENT_DEP,
|
|
2576
|
+
protobuf: "^5.0.0",
|
|
2577
|
+
fixnum: "^1.1.1",
|
|
2578
|
+
});
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2202
2581
|
/** `import` lines a barrel needs so its `typedef` aliases can SEE the generated symbols
|
|
2203
2582
|
* (an `export` alone re-exports but does not bring names into scope). Empty when no aliases. */
|
|
2204
2583
|
const aliasImportLines = (
|
|
@@ -2253,7 +2632,7 @@ function tenantDartExportNames(src: string): string[] {
|
|
|
2253
2632
|
}
|
|
2254
2633
|
|
|
2255
2634
|
/**
|
|
2256
|
-
* Turn the generated
|
|
2635
|
+
* Turn the generated session client into a Dart `part` of the business application library.
|
|
2257
2636
|
*
|
|
2258
2637
|
* `lib/src` is only a convention: a tenant can deep-import a normal Dart library. A part file is a
|
|
2259
2638
|
* language boundary — it cannot be imported on its own — and its underscore-prefixed declarations are
|
|
@@ -2330,7 +2709,7 @@ function encapsulateDartClient(
|
|
|
2330
2709
|
continue;
|
|
2331
2710
|
// Preserve business-type re-exports so one application import carries its
|
|
2332
2711
|
// payloads and read models. Framework/runtime exports belonged to the retired
|
|
2333
|
-
//
|
|
2712
|
+
// session library and must not enter tenant autocomplete.
|
|
2334
2713
|
if (
|
|
2335
2714
|
kind === "export" &&
|
|
2336
2715
|
client.directives.includes(line) &&
|
|
@@ -2406,7 +2785,7 @@ function generatedPackageApplication(
|
|
|
2406
2785
|
}[],
|
|
2407
2786
|
homeDomain: string,
|
|
2408
2787
|
rootDomains: readonly string[],
|
|
2409
|
-
|
|
2788
|
+
parentWorkspaceDomains: readonly string[],
|
|
2410
2789
|
hasApplicationFrameworkCalls: boolean
|
|
2411
2790
|
): string {
|
|
2412
2791
|
const application = dartApplicationClassName(packageName);
|
|
@@ -2431,31 +2810,26 @@ function generatedPackageApplication(
|
|
|
2431
2810
|
"generated Dart package application could not bind every co-resident root domain"
|
|
2432
2811
|
);
|
|
2433
2812
|
}
|
|
2434
|
-
const
|
|
2435
|
-
.filter(({ domain }) =>
|
|
2813
|
+
const parentMembers = domains
|
|
2814
|
+
.filter(({ domain }) => parentWorkspaceDomains.includes(domain))
|
|
2436
2815
|
.map(({ domain, file }, index) => ({
|
|
2437
2816
|
domain,
|
|
2438
2817
|
file,
|
|
2439
|
-
prefix: `
|
|
2818
|
+
prefix: `_nmsParentDomain${index}`,
|
|
2440
2819
|
member: dartApplicationMember(domain),
|
|
2441
2820
|
application: dartApplicationClassName(domain),
|
|
2442
2821
|
}));
|
|
2443
|
-
if (
|
|
2822
|
+
if (parentMembers.length !== parentWorkspaceDomains.length) {
|
|
2444
2823
|
throw new Error(
|
|
2445
|
-
"generated Dart package application could not describe every verified
|
|
2824
|
+
"generated Dart package application could not describe every verified parent-workspace domain"
|
|
2446
2825
|
);
|
|
2447
2826
|
}
|
|
2448
|
-
const invitationPolicies = members.flatMap(
|
|
2449
|
-
|
|
2827
|
+
const invitationPolicies = members.flatMap(
|
|
2828
|
+
({ domain, member, invitationPolicies }) =>
|
|
2829
|
+
invitationPolicies.map((policy) => ({ domain, member, ...policy }))
|
|
2450
2830
|
);
|
|
2451
2831
|
const hasInvitations = invitationPolicies.length > 0;
|
|
2452
|
-
const
|
|
2453
|
-
if (rootMember === undefined) {
|
|
2454
|
-
throw new Error(
|
|
2455
|
-
`generated Dart package application cannot expose the model for home domain '${homeDomain}'`
|
|
2456
|
-
);
|
|
2457
|
-
}
|
|
2458
|
-
const surfaceMembers = [...members, ...remoteMembers];
|
|
2832
|
+
const surfaceMembers = [...members, ...parentMembers];
|
|
2459
2833
|
const duplicate = surfaceMembers.find(
|
|
2460
2834
|
(candidate, index) =>
|
|
2461
2835
|
surfaceMembers.findIndex((other) => other.member === candidate.member) !==
|
|
@@ -2467,11 +2841,9 @@ function generatedPackageApplication(
|
|
|
2467
2841
|
);
|
|
2468
2842
|
}
|
|
2469
2843
|
const frameworkMembers = new Map<string, string>([
|
|
2470
|
-
["nomosWidget", "the generated Nomos Flutter bootstrap"],
|
|
2471
2844
|
["currentUser", "the generated signed-in application participant"],
|
|
2472
2845
|
["currentStatus", "the generated application status"],
|
|
2473
2846
|
["watchStatus", "the generated application status watch"],
|
|
2474
|
-
["model", "the generated model lifecycle"],
|
|
2475
2847
|
["recipient", "the generated typed recipient boundary"],
|
|
2476
2848
|
["createInvitation", "the generated invitation capability"],
|
|
2477
2849
|
["redeemInvitation", "the generated invitation redemption"],
|
|
@@ -2492,16 +2864,21 @@ function generatedPackageApplication(
|
|
|
2492
2864
|
}
|
|
2493
2865
|
return [
|
|
2494
2866
|
`// AUTO-GENERATED by nomos-compile — the one tenant application for "${packageName}".`,
|
|
2495
|
-
`// Domain clients remain generated implementation details behind this
|
|
2867
|
+
`// Domain clients remain generated implementation details behind this stateless business facade.`,
|
|
2496
2868
|
`// ignore_for_file: invalid_use_of_internal_member`,
|
|
2497
|
-
`import 'package:
|
|
2498
|
-
`import 'package:
|
|
2499
|
-
`import 'package:nomos_client/src/nms_generated_runtime.dart' show NmsInternalUsers, NomosSession, User${
|
|
2869
|
+
`import 'package:nomos_client/src/application_status.dart' show ApplicationStatus;`,
|
|
2870
|
+
`import 'package:nomos_client/src/nms_generated_runtime.dart' show NmsInternalUsers, User${
|
|
2500
2871
|
hasInvitations ? ", Invitation, Recipient" : ""
|
|
2501
2872
|
};`,
|
|
2502
|
-
`import 'package:
|
|
2503
|
-
hasInvitations ? ",
|
|
2873
|
+
`import 'package:nomos_client/src/reception/reception_session.dart' show NmsReceptionSession${
|
|
2874
|
+
hasInvitations ? ", NmsReceptionInvitationPolicy" : ""
|
|
2504
2875
|
};`,
|
|
2876
|
+
...(hasApplicationFrameworkCalls
|
|
2877
|
+
? [
|
|
2878
|
+
`import 'package:nomos_client/src/application_framework_calls.dart' show NmsApplicationFrameworkCalls;`,
|
|
2879
|
+
`import 'package:nomos_client/src/reception/reception_contract.dart' show NmsReceptionContract;`,
|
|
2880
|
+
]
|
|
2881
|
+
: []),
|
|
2505
2882
|
...(hasApplicationFrameworkCalls
|
|
2506
2883
|
? [
|
|
2507
2884
|
`import 'application.framework_calls.dart' show nomosInternalApplicationFrameworkCalls;`,
|
|
@@ -2510,151 +2887,116 @@ function generatedPackageApplication(
|
|
|
2510
2887
|
...members.map(
|
|
2511
2888
|
({ file, prefix }) => `import '${file}.application.dart' as ${prefix};`
|
|
2512
2889
|
),
|
|
2513
|
-
...
|
|
2890
|
+
...parentMembers.map(
|
|
2514
2891
|
({ file, prefix }) => `import '${file}.application.dart' as ${prefix};`
|
|
2515
2892
|
),
|
|
2516
2893
|
``,
|
|
2517
|
-
`/// The
|
|
2894
|
+
`/// The typed ${packageName} vocabulary bound to one Peer Office handle.`,
|
|
2518
2895
|
`///`,
|
|
2519
2896
|
`/// Keep business UI and services on this object. Nomos owns identity, persistence,`,
|
|
2520
2897
|
`/// synchronization, workspace navigation, access delivery and recovery underneath it;`,
|
|
2521
2898
|
`/// do not build a second runtime host, repository, session, or application-state mirror.`,
|
|
2522
2899
|
`///`,
|
|
2523
|
-
`///
|
|
2524
|
-
|
|
2525
|
-
`/// return ${application}.nomosWidget(`,
|
|
2526
|
-
`/// cloud: cloud,`,
|
|
2527
|
-
`/// application: '${packageName}',`,
|
|
2528
|
-
`/// signIn: signIn,`,
|
|
2529
|
-
`/// builder: (context, application) => MyBusinessUi(application: application),`,
|
|
2530
|
-
`/// );`,
|
|
2531
|
-
"/// ```",
|
|
2532
|
-
`/// Pass that same [${application}] down. It is the application state.`,
|
|
2900
|
+
`/// The platform shell opens Reception, then calls [receive${application}] once.`,
|
|
2901
|
+
`/// Pass that same [${application}] down; generated code never opens custody or a kernel.`,
|
|
2533
2902
|
`final class ${application} {`,
|
|
2534
|
-
` final
|
|
2535
|
-
...(hasInvitations
|
|
2536
|
-
? [` final NmsInternalApplicationUserBinding _nmsUser;`]
|
|
2537
|
-
: []),
|
|
2903
|
+
` final NmsReceptionSession _nmsReception;`,
|
|
2538
2904
|
` final User currentUser;`,
|
|
2539
2905
|
...members.flatMap(({ domain, member, application: child, prefix }) => [
|
|
2540
2906
|
` /// Business actions, records and watches declared by ${domain}.`,
|
|
2541
2907
|
` final ${prefix}.${child} ${member};`,
|
|
2542
2908
|
]),
|
|
2543
|
-
...
|
|
2909
|
+
...parentMembers.flatMap(
|
|
2544
2910
|
({ domain, member, application: child, prefix }) => [
|
|
2545
|
-
` /// Typed surface for verified
|
|
2546
|
-
` ///
|
|
2911
|
+
` /// Typed law surface for verified parent workspace domain ${domain}.`,
|
|
2912
|
+
` /// Its separate workspace is lineage; where that custody runs is an independent placement decision.`,
|
|
2547
2913
|
` ${prefix}.${child} get ${member} => throw StateError(`,
|
|
2548
|
-
` 'The
|
|
2914
|
+
` 'The ${domain} domain belongs to the verified parent workspace and needs a typed remote-workspace binding.',`,
|
|
2549
2915
|
` );`,
|
|
2550
2916
|
]
|
|
2551
2917
|
),
|
|
2552
|
-
` const ${application}._({required
|
|
2553
|
-
hasInvitations
|
|
2554
|
-
? "required NmsInternalApplicationUserBinding nmsUser, "
|
|
2555
|
-
: ""
|
|
2556
|
-
}required this.currentUser, ${members
|
|
2918
|
+
` const ${application}._({required NmsReceptionSession nmsReception, required this.currentUser, ${members
|
|
2557
2919
|
.map(({ member }) => `required this.${member}`)
|
|
2558
|
-
.join(", ")}}) :
|
|
2559
|
-
hasInvitations ? ", _nmsUser = nmsUser" : ""
|
|
2560
|
-
};`,
|
|
2561
|
-
``,
|
|
2562
|
-
` /// Prove, adopt or restore the package law against this workspace's exact history.`,
|
|
2563
|
-
` ${rootMember.prefix}.NomosModel get model => ${rootMember.member}.model;`,
|
|
2920
|
+
.join(", ")}}) : _nmsReception = nmsReception;`,
|
|
2564
2921
|
``,
|
|
2565
2922
|
` /// Current durability and convergence for the receiving-group workspace.`,
|
|
2566
2923
|
` /// A slow or offline sibling never makes this workspace's readiness ambiguous.`,
|
|
2567
|
-
` ApplicationStatus get currentStatus =>
|
|
2924
|
+
` ApplicationStatus get currentStatus => _nmsReception.currentStatus;`,
|
|
2568
2925
|
``,
|
|
2569
2926
|
` /// React whenever this receiving group saves, receives work, goes offline or needs attention.`,
|
|
2570
|
-
` Stream<ApplicationStatus> watchStatus() =>
|
|
2927
|
+
` Stream<ApplicationStatus> watchStatus() => _nmsReception.streams.status;`,
|
|
2571
2928
|
``,
|
|
2572
2929
|
` /// Wait until this receiving group's accepted work is in custody and caught up.`,
|
|
2573
2930
|
` Future<void> whenCurrent({Duration timeout = const Duration(minutes: 2)}) =>`,
|
|
2574
|
-
`
|
|
2931
|
+
` _nmsReception.whenCurrent(timeout: timeout);`,
|
|
2575
2932
|
``,
|
|
2576
2933
|
` /// Aggregate posture across every child currently owned by this generated application.`,
|
|
2577
|
-
` ApplicationStatus get applicationStatus =>
|
|
2934
|
+
` ApplicationStatus get applicationStatus => _nmsReception.applicationStatus;`,
|
|
2578
2935
|
``,
|
|
2579
2936
|
` /// React to aggregate posture across every generated child currently in this application plane.`,
|
|
2580
|
-
` Stream<ApplicationStatus> watchApplicationStatus() =>
|
|
2937
|
+
` Stream<ApplicationStatus> watchApplicationStatus() => _nmsReception.streams.applicationStatus;`,
|
|
2581
2938
|
``,
|
|
2582
2939
|
` /// Explicitly wait for every generated child currently owned by this application.`,
|
|
2583
2940
|
` Future<void> whenApplicationCurrent({Duration timeout = const Duration(minutes: 2)}) =>`,
|
|
2584
|
-
`
|
|
2941
|
+
` _nmsReception.whenApplicationCurrent(timeout: timeout);`,
|
|
2585
2942
|
...(hasInvitations
|
|
2586
2943
|
? [
|
|
2587
2944
|
``,
|
|
2588
2945
|
` /// Turn an identity-provider uid into a typed invitation recipient.`,
|
|
2589
|
-
` Recipient<User> recipient(String uid) =>
|
|
2946
|
+
` Recipient<User> recipient(String uid) => _nmsReception.recipient<User>(uid);`,
|
|
2590
2947
|
``,
|
|
2591
2948
|
` /// Create an invitation; transport its link however your product chooses.`,
|
|
2592
|
-
` Future<Invitation<User>> createInvitation() =>
|
|
2949
|
+
` Future<Invitation<User>> createInvitation() => _nmsReception.createInvitation<User>();`,
|
|
2593
2950
|
``,
|
|
2594
2951
|
` /// Redeem a transported invitation as [currentUser].`,
|
|
2595
|
-
` Future<void> redeemInvitation(String link) =>
|
|
2596
|
-
` _nmsSession,`,
|
|
2952
|
+
` Future<void> redeemInvitation(String link) => _nmsReception.redeemInvitation<User>(`,
|
|
2597
2953
|
` frameworkCalls: nomosInternalApplicationFrameworkCalls,`,
|
|
2598
2954
|
` link: link,`,
|
|
2599
|
-
|
|
2955
|
+
// Compose each domain application's own policies. Rebuilding them here cannot work: the
|
|
2956
|
+
// pending lookup is a framework-internal sharing query that lives on the domain CLIENT
|
|
2957
|
+
// and is deliberately absent from the domain APPLICATION this member holds.
|
|
2600
2958
|
` policies: [`,
|
|
2601
|
-
...invitationPolicies.
|
|
2602
|
-
`
|
|
2603
|
-
|
|
2604
|
-
` domainHash: ${JSON.stringify(policy.domainHash)},`,
|
|
2605
|
-
` query: ${JSON.stringify(policy.query)},`,
|
|
2606
|
-
` redeem: ${JSON.stringify(policy.redeem)},`,
|
|
2607
|
-
` personField: ${JSON.stringify(policy.personField)},`,
|
|
2608
|
-
` roleField: ${JSON.stringify(policy.roleField)},`,
|
|
2609
|
-
` scopeField: ${JSON.stringify(policy.scopeField)},`,
|
|
2610
|
-
` ),`,
|
|
2611
|
-
]),
|
|
2959
|
+
...[...new Set(invitationPolicies.map((policy) => policy.member))].map(
|
|
2960
|
+
(member) => ` ...${member}.nomosInternalInvitationPolicies,`
|
|
2961
|
+
),
|
|
2612
2962
|
` ],`,
|
|
2613
2963
|
` );`,
|
|
2614
2964
|
]
|
|
2615
2965
|
: []),
|
|
2616
2966
|
``,
|
|
2617
|
-
`
|
|
2618
|
-
` ///`,
|
|
2619
|
-
` /// This is the only application bootstrap tenant code should own.`,
|
|
2620
|
-
` ///`,
|
|
2621
|
-
` /// The object supplied to [builder] is the application state. Keep it as the only`,
|
|
2622
|
-
` /// business-state dependency; do not recreate its records in providers, repositories or caches.`,
|
|
2623
|
-
` static Widget nomosWidget({`,
|
|
2624
|
-
` Key? key,`,
|
|
2625
|
-
` required String cloud,`,
|
|
2626
|
-
` required String application,`,
|
|
2627
|
-
` required ApplicationSignIn signIn,`,
|
|
2628
|
-
` required Widget Function(BuildContext context, ${application} application) builder,`,
|
|
2629
|
-
` Widget Function(BuildContext context)? loadingBuilder,`,
|
|
2630
|
-
` Widget Function(BuildContext context, Object error, VoidCallback retry)? errorBuilder,`,
|
|
2631
|
-
` ApplicationModelChange? onModelChange,`,
|
|
2632
|
-
` ValueChanged<NomosApplicationStartupState>? onStartupStateChanged,`,
|
|
2633
|
-
` Duration startupTimeout = const Duration(minutes: 3),`,
|
|
2634
|
-
` }) => NmsInternalApplicationHost.home<${application}>(`,
|
|
2635
|
-
` key: key, cloud: cloud, application: application, signIn: signIn,`,
|
|
2636
|
-
...(hasApplicationFrameworkCalls
|
|
2637
|
-
? [` frameworkCalls: nomosInternalApplicationFrameworkCalls,`]
|
|
2638
|
-
: []),
|
|
2639
|
-
` bind: nomosInternalBind${application}, builder: builder,`,
|
|
2640
|
-
` loadingBuilder: loadingBuilder, errorBuilder: errorBuilder,`,
|
|
2641
|
-
` onModelChange: onModelChange,`,
|
|
2642
|
-
` onStartupStateChanged: onStartupStateChanged,`,
|
|
2643
|
-
` startupTimeout: startupTimeout,`,
|
|
2644
|
-
` );`,
|
|
2967
|
+
` Future<void> close() => _nmsReception.close();`,
|
|
2645
2968
|
`}`,
|
|
2646
2969
|
``,
|
|
2647
|
-
`Future<${application}>
|
|
2648
|
-
`
|
|
2970
|
+
`Future<${application}> receive${application}(`,
|
|
2971
|
+
` NmsReceptionSession reception,`,
|
|
2649
2972
|
`) async => ${application}._(`,
|
|
2650
|
-
`
|
|
2651
|
-
|
|
2652
|
-
` currentUser: NmsInternalUsers.create(identityId: user.nmsInternalIdentityId, identityAddress: user.nmsInternalIdentityAddress),`,
|
|
2973
|
+
` nmsReception: reception,`,
|
|
2974
|
+
` currentUser: NmsInternalUsers.create(identityId: reception.user.identityId, identityAddress: reception.user.identityAddress),`,
|
|
2653
2975
|
...members.map(
|
|
2654
2976
|
({ member, application: child, prefix }) =>
|
|
2655
|
-
` ${member}: await ${prefix}.
|
|
2977
|
+
` ${member}: await ${prefix}.nomosInternalReceive${child}(reception),`
|
|
2656
2978
|
),
|
|
2657
2979
|
`);`,
|
|
2980
|
+
...(hasApplicationFrameworkCalls
|
|
2981
|
+
? [
|
|
2982
|
+
``,
|
|
2983
|
+
`/// Static law vocabulary handed to the fixed Reception office.`,
|
|
2984
|
+
`/// It owns no lifecycle, custody, kernel, credentials, timers, or sync state.`,
|
|
2985
|
+
`final class ${application}Reception implements NmsReceptionContract<${application}> {`,
|
|
2986
|
+
` const ${application}Reception();`,
|
|
2987
|
+
``,
|
|
2988
|
+
` @override`,
|
|
2989
|
+
` NmsApplicationFrameworkCalls get frameworkCalls => nomosInternalApplicationFrameworkCalls;`,
|
|
2990
|
+
``,
|
|
2991
|
+
` @override`,
|
|
2992
|
+
` Future<${application}> receive(NmsReceptionSession session) => receive${application}(session);`,
|
|
2993
|
+
`}`,
|
|
2994
|
+
``,
|
|
2995
|
+
`const ${application}Reception ${dartApplicationMember(
|
|
2996
|
+
packageName
|
|
2997
|
+
)}Reception = ${application}Reception();`,
|
|
2998
|
+
]
|
|
2999
|
+
: []),
|
|
2658
3000
|
``,
|
|
2659
3001
|
].join("\n");
|
|
2660
3002
|
}
|
|
@@ -2759,7 +3101,8 @@ function cursedNameHints(
|
|
|
2759
3101
|
function reconcilePubspec(
|
|
2760
3102
|
pkgDir: string,
|
|
2761
3103
|
yaml: string,
|
|
2762
|
-
managedDependencies: Readonly<Record<string, string>> = {}
|
|
3104
|
+
managedDependencies: Readonly<Record<string, string>> = {},
|
|
3105
|
+
retiredDependencies: readonly string[] = []
|
|
2763
3106
|
): boolean {
|
|
2764
3107
|
const p = path.join(pkgDir, "pubspec.yaml");
|
|
2765
3108
|
if (!existsSync(p)) {
|
|
@@ -2767,7 +3110,11 @@ function reconcilePubspec(
|
|
|
2767
3110
|
writeFileSync(p, yaml, "utf8");
|
|
2768
3111
|
return true;
|
|
2769
3112
|
}
|
|
2770
|
-
if (
|
|
3113
|
+
if (
|
|
3114
|
+
Object.keys(managedDependencies).length === 0 &&
|
|
3115
|
+
retiredDependencies.length === 0
|
|
3116
|
+
)
|
|
3117
|
+
return false;
|
|
2771
3118
|
|
|
2772
3119
|
const source = readFileSync(p, "utf8");
|
|
2773
3120
|
const trailingNewline = source.endsWith("\n");
|
|
@@ -2790,6 +3137,27 @@ function reconcilePubspec(
|
|
|
2790
3137
|
dependenciesEnd += 1;
|
|
2791
3138
|
}
|
|
2792
3139
|
|
|
3140
|
+
// Retire dependencies emitted by an older compiler without disturbing any
|
|
3141
|
+
// tenant-owned metadata or unrelated dependency.
|
|
3142
|
+
for (const name of retiredDependencies) {
|
|
3143
|
+
const prefix = ` ${name}:`;
|
|
3144
|
+
const existing = lines.findIndex(
|
|
3145
|
+
(line, index) =>
|
|
3146
|
+
index > dependenciesStart &&
|
|
3147
|
+
index < dependenciesEnd &&
|
|
3148
|
+
line.startsWith(prefix)
|
|
3149
|
+
);
|
|
3150
|
+
if (existing < 0) continue;
|
|
3151
|
+
let nestedEnd = existing + 1;
|
|
3152
|
+
while (
|
|
3153
|
+
nestedEnd < dependenciesEnd &&
|
|
3154
|
+
/^(?: {4,}|\t)/.test(lines[nestedEnd]!)
|
|
3155
|
+
)
|
|
3156
|
+
nestedEnd += 1;
|
|
3157
|
+
lines.splice(existing, nestedEnd - existing);
|
|
3158
|
+
dependenciesEnd -= nestedEnd - existing;
|
|
3159
|
+
}
|
|
3160
|
+
|
|
2793
3161
|
for (const [name, constraint] of Object.entries(managedDependencies)) {
|
|
2794
3162
|
const prefix = ` ${name}:`;
|
|
2795
3163
|
const existing = lines.findIndex(
|
|
@@ -2869,6 +3237,34 @@ function reconcileGeneratedLawAsset(
|
|
|
2869
3237
|
return true;
|
|
2870
3238
|
}
|
|
2871
3239
|
|
|
3240
|
+
/** Remove compiler-owned law assets from a generated client package.
|
|
3241
|
+
* Workspace law is rolled out by its cloud host/operator; a consumer must not
|
|
3242
|
+
* carry a candidate capable of mutating shared custody during application open. */
|
|
3243
|
+
function removeGeneratedClientLawAssets(pkgDir: string): boolean {
|
|
3244
|
+
const p = path.join(pkgDir, "pubspec.yaml");
|
|
3245
|
+
if (!existsSync(p)) return false;
|
|
3246
|
+
const source = readFileSync(p, "utf8");
|
|
3247
|
+
const withoutGeneratedAssets = source
|
|
3248
|
+
.split(/(?<=\n)/)
|
|
3249
|
+
.filter(
|
|
3250
|
+
(line) =>
|
|
3251
|
+
!/^\s+-\s+lib\/src\/generated\/[^\s]+\.(?:deploy\.json|law\.usdz)\s*$/.test(
|
|
3252
|
+
line.trimEnd()
|
|
3253
|
+
)
|
|
3254
|
+
)
|
|
3255
|
+
.join("");
|
|
3256
|
+
// A previously generated client may contain no tenant Flutter settings once
|
|
3257
|
+
// its embedded law is removed. Do not leave an empty `assets:` mapping: it
|
|
3258
|
+
// is invalid pubspec YAML and makes the generated package fail analysis.
|
|
3259
|
+
const next = withoutGeneratedAssets.replace(
|
|
3260
|
+
/(?:^|\n)flutter:\s*\n\s{2}assets:\s*\n?$/,
|
|
3261
|
+
"\n"
|
|
3262
|
+
);
|
|
3263
|
+
if (next === source) return false;
|
|
3264
|
+
writeFileSync(p, next, "utf8");
|
|
3265
|
+
return true;
|
|
3266
|
+
}
|
|
3267
|
+
|
|
2872
3268
|
/**
|
|
2873
3269
|
* THE LAYERED-PACKAGES EMIT (`dart.packages`) — three sibling pub packages under `dartOut`:
|
|
2874
3270
|
* `<types>/` (pure data, depends only on `nomos_types`), `<client>/` (effectful runtime clients,
|
|
@@ -2895,7 +3291,6 @@ function emitDartPackages(
|
|
|
2895
3291
|
readonly barrelExports?: "all" | "explicit";
|
|
2896
3292
|
},
|
|
2897
3293
|
names: Readonly<Record<string, string>> | undefined,
|
|
2898
|
-
compat?: NomosCompat,
|
|
2899
3294
|
describePackageName = packages.client,
|
|
2900
3295
|
lawUsdzPath?: string,
|
|
2901
3296
|
applicationMaxChildWorkspacesPerUser?: number,
|
|
@@ -2917,7 +3312,7 @@ function emitDartPackages(
|
|
|
2917
3312
|
...(packages.compat !== undefined ? [packages.compat] : []),
|
|
2918
3313
|
];
|
|
2919
3314
|
if (new Set(roleNames).size !== roleNames.length)
|
|
2920
|
-
fail(`dart.packages: types/
|
|
3315
|
+
fail(`dart.packages: types/peer-office/test/compat names must be distinct`);
|
|
2921
3316
|
|
|
2922
3317
|
const packageContracts = collectPackageContracts(modules);
|
|
2923
3318
|
const describeModel = buildNomosDescribeModel(modules, describePackageName);
|
|
@@ -2938,7 +3333,7 @@ function emitDartPackages(
|
|
|
2938
3333
|
const typesSources = new Map<string, string>(); // fileBase → src (for alias validation + purity)
|
|
2939
3334
|
// The SIBLING generated type files a types-package file MAY import (a cross-domain `t.foreignRef` types its
|
|
2940
3335
|
// field as the target aggregate's class, which lives in the target domain's file). A sibling types file is
|
|
2941
|
-
// still PURE (no runtime/
|
|
3336
|
+
// still PURE (no runtime/session surface — the purity gate exists to keep nomos_client out, not to isolate
|
|
2942
3337
|
// domain files from each other; Dart allows type-import cycles for mutual refs).
|
|
2943
3338
|
const siblingTypeFiles = new Set<string>([
|
|
2944
3339
|
...modules.map((m) => `${m.name}.dart`),
|
|
@@ -2954,14 +3349,14 @@ function emitDartPackages(
|
|
|
2954
3349
|
}
|
|
2955
3350
|
const pure = src.replace(DART_PACKAGE_IMPORT, dartTypesImportFor(src));
|
|
2956
3351
|
// PURITY, fail-closed: only the nomos_types barrel, the framework file, and SIBLING generated type files
|
|
2957
|
-
// (cross-domain foreignRef targets) may be imported. Runtime/
|
|
3352
|
+
// (cross-domain foreignRef targets) may be imported. Runtime/session surfaces (nomos_client) stay out.
|
|
2958
3353
|
for (const line of pure.split("\n")) {
|
|
2959
3354
|
const m = /^import\s+'([^']+)'/.exec(line.trim());
|
|
2960
3355
|
if (
|
|
2961
3356
|
m !== null &&
|
|
2962
3357
|
// `dart:*` is a pure SDK library (dart:convert for jsonEncode of a json field, dart:math, …) — it
|
|
2963
3358
|
// carries NO cross-PACKAGE coupling, which is the only thing this gate exists to prevent. The
|
|
2964
|
-
// purpose is keeping nomos_client (the runtime/
|
|
3359
|
+
// purpose is keeping nomos_client (the runtime/session surface) out of the pure types layer, not
|
|
2965
3360
|
// banning the Dart SDK. Allow every dart:* import; keep the cross-package allowlist otherwise.
|
|
2966
3361
|
!m[1]!.startsWith("dart:") &&
|
|
2967
3362
|
m[1] !== "package:nomos_types/nomos_types.dart" &&
|
|
@@ -3214,7 +3609,7 @@ function emitDartPackages(
|
|
|
3214
3609
|
const typesBarrel = [
|
|
3215
3610
|
"// GENERATED by nomos-compile — the TYPES package's public entrypoint. DO NOT EDIT.",
|
|
3216
3611
|
"// Pure data: payload classes, read models, VOs/enums, typed read accessors over the",
|
|
3217
|
-
"// injected NomosReads seams. Frontend-safe — no
|
|
3612
|
+
"// injected NomosReads seams. Frontend-safe — no session, no sessions, no lifecycle.",
|
|
3218
3613
|
"library;",
|
|
3219
3614
|
"",
|
|
3220
3615
|
...explicitBarrelNote,
|
|
@@ -3241,7 +3636,7 @@ function emitDartPackages(
|
|
|
3241
3636
|
pkgDir(packages.types),
|
|
3242
3637
|
[
|
|
3243
3638
|
`name: ${packages.types}`,
|
|
3244
|
-
`description: Generated Nomos domain TYPES — pure data, frontend-safe (no
|
|
3639
|
+
`description: Generated Nomos domain TYPES — pure data, frontend-safe (no session, no runtime).`,
|
|
3245
3640
|
`version: 0.1.0`,
|
|
3246
3641
|
`publish_to: none # tenant-owned: remove to publish`,
|
|
3247
3642
|
`environment:`,
|
|
@@ -3255,6 +3650,21 @@ function emitDartPackages(
|
|
|
3255
3650
|
|
|
3256
3651
|
// ── the CLIENT package — effectful runtime, depends on types + nomos_client ──
|
|
3257
3652
|
const clientGen = freshGenDir(packages.client);
|
|
3653
|
+
const operationProtoModules = [
|
|
3654
|
+
...new Map(
|
|
3655
|
+
[...modules, ...frameworkModules].map(
|
|
3656
|
+
(module) => [module.name, module] as const
|
|
3657
|
+
)
|
|
3658
|
+
).values(),
|
|
3659
|
+
];
|
|
3660
|
+
written.push(
|
|
3661
|
+
...emitDartOperationProto(
|
|
3662
|
+
clientGen,
|
|
3663
|
+
operationProtoModules,
|
|
3664
|
+
lawGraphs,
|
|
3665
|
+
lawPaths
|
|
3666
|
+
)
|
|
3667
|
+
);
|
|
3258
3668
|
const applicationFrameworkCallsFile = "application.framework_calls";
|
|
3259
3669
|
const applicationFrameworkCallsSource = generateDartApplicationFrameworkCalls(
|
|
3260
3670
|
frameworkModules.flatMap((module) => {
|
|
@@ -3263,8 +3673,21 @@ function emitDartPackages(
|
|
|
3263
3673
|
return graph === undefined || paths === undefined
|
|
3264
3674
|
? []
|
|
3265
3675
|
: [{ module, graph, paths }];
|
|
3266
|
-
})
|
|
3676
|
+
}),
|
|
3677
|
+
{ lawSha256: domainHash }
|
|
3267
3678
|
);
|
|
3679
|
+
const workspaceAccessReadImports = new Map<string, string>();
|
|
3680
|
+
for (const module of operationProtoModules) {
|
|
3681
|
+
const graph = lawGraphs.get(module.name);
|
|
3682
|
+
const paths = lawPaths.get(module.name);
|
|
3683
|
+
if (graph === undefined || paths === undefined) continue;
|
|
3684
|
+
if (!graph.nodes.some(node => node.kind === "read" && node.entity.label === "receivedWorkspaceAccess")) continue;
|
|
3685
|
+
const file = `${module.name}.workspace_access.dart`;
|
|
3686
|
+
const target = path.join(clientGen, file);
|
|
3687
|
+
writeFileSync(target, generateDartWorkspaceAccessRead(graph, paths), "utf8");
|
|
3688
|
+
written.push(target);
|
|
3689
|
+
workspaceAccessReadImports.set(module.name, file);
|
|
3690
|
+
}
|
|
3268
3691
|
if (applicationFrameworkCallsSource !== undefined) {
|
|
3269
3692
|
const applicationFrameworkCallsPath = path.join(
|
|
3270
3693
|
clientGen,
|
|
@@ -3278,18 +3701,6 @@ function emitDartPackages(
|
|
|
3278
3701
|
written.push(applicationFrameworkCallsPath);
|
|
3279
3702
|
}
|
|
3280
3703
|
const embeddedLawName = `${describePackageName}.law.usdz`;
|
|
3281
|
-
const applicationLawPath =
|
|
3282
|
-
lawUsdzPath === undefined
|
|
3283
|
-
? undefined
|
|
3284
|
-
: path.join(clientGen, embeddedLawName);
|
|
3285
|
-
if (applicationLawPath !== undefined) {
|
|
3286
|
-
copyFileSync(lawUsdzPath!, applicationLawPath);
|
|
3287
|
-
written.push(applicationLawPath);
|
|
3288
|
-
}
|
|
3289
|
-
const applicationLawAssetKey =
|
|
3290
|
-
applicationLawPath === undefined
|
|
3291
|
-
? undefined
|
|
3292
|
-
: `packages/${packages.client}/lib/${genRel}/${embeddedLawName}`;
|
|
3293
3704
|
const typesImportUri = `package:${packages.types}/${packages.types}.dart`;
|
|
3294
3705
|
const implementationTypeImports = explicitBarrel
|
|
3295
3706
|
? modules.map((m) => `package:${packages.types}/${genRel}/${m.name}.dart`)
|
|
@@ -3310,18 +3721,15 @@ function emitDartPackages(
|
|
|
3310
3721
|
name !== OwnedDeviceRequestValue.name
|
|
3311
3722
|
);
|
|
3312
3723
|
for (const { mod, dd } of perDomain) {
|
|
3724
|
+
const workspaceAccessReadImportUri = workspaceAccessReadImports.get(mod.name);
|
|
3313
3725
|
const makeClient = (imports: readonly string[]) =>
|
|
3314
|
-
|
|
3726
|
+
generateDartDomainClient(dd.modForDart, {
|
|
3315
3727
|
domainHash,
|
|
3316
3728
|
callUsda: {
|
|
3317
3729
|
graph: lawGraphs.get(mod.name)!,
|
|
3318
3730
|
paths: lawPaths.get(mod.name)!,
|
|
3319
3731
|
},
|
|
3320
|
-
...(applicationLawAssetKey !== undefined
|
|
3321
|
-
? { lawAssetKey: applicationLawAssetKey }
|
|
3322
|
-
: {}),
|
|
3323
3732
|
interface: packageInterface[dd.modForDart.domain ?? dd.modForDart.name],
|
|
3324
|
-
...(compat !== undefined ? { compat } : {}),
|
|
3325
3733
|
supportImport: "package",
|
|
3326
3734
|
typesImportUri,
|
|
3327
3735
|
emitActivity: true,
|
|
@@ -3336,6 +3744,7 @@ function emitDartPackages(
|
|
|
3336
3744
|
const makeApplication = (imports: readonly string[]) =>
|
|
3337
3745
|
generateDartApplication(dd.modForDart, {
|
|
3338
3746
|
supportImport: "package",
|
|
3747
|
+
callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! },
|
|
3339
3748
|
typesImportUri,
|
|
3340
3749
|
packageAggregateDomains: buildPackageAggregateDomains(modules),
|
|
3341
3750
|
flutterHost: true,
|
|
@@ -3345,6 +3754,9 @@ function emitDartPackages(
|
|
|
3345
3754
|
: {
|
|
3346
3755
|
applicationFrameworkCallsImportUri: `${applicationFrameworkCallsFile}.dart`,
|
|
3347
3756
|
}),
|
|
3757
|
+
...(workspaceAccessReadImportUri === undefined
|
|
3758
|
+
? {}
|
|
3759
|
+
: { workspaceAccessReadImportUri }),
|
|
3348
3760
|
...(imports.length > 0 ? { explicitTypeImports: imports } : {}),
|
|
3349
3761
|
});
|
|
3350
3762
|
let clientSrc = makeClient(implementationTypeImports);
|
|
@@ -3377,7 +3789,7 @@ function emitDartPackages(
|
|
|
3377
3789
|
applicationSrc,
|
|
3378
3790
|
clientFile,
|
|
3379
3791
|
applicationFile,
|
|
3380
|
-
new Set(dartApplicationPublicClassNames(dd.modForDart))
|
|
3792
|
+
new Set(dartApplicationPublicClassNames(dd.modForDart, { callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }))
|
|
3381
3793
|
);
|
|
3382
3794
|
const cp = path.join(clientGen, clientFile);
|
|
3383
3795
|
writeFileSync(cp, encapsulated.clientPart, "utf8");
|
|
@@ -3413,7 +3825,7 @@ function emitDartPackages(
|
|
|
3413
3825
|
})),
|
|
3414
3826
|
topology.homeDomain,
|
|
3415
3827
|
topology.rootDomains,
|
|
3416
|
-
topology.
|
|
3828
|
+
topology.parentWorkspaceDomains,
|
|
3417
3829
|
applicationFrameworkCallsSource !== undefined
|
|
3418
3830
|
),
|
|
3419
3831
|
"utf8"
|
|
@@ -3424,10 +3836,8 @@ function emitDartPackages(
|
|
|
3424
3836
|
"// GENERATED by nomos-compile — the CLIENT package's public entrypoint. DO NOT EDIT.",
|
|
3425
3837
|
`/// The generated business application for ${describePackageName}.`,
|
|
3426
3838
|
`///`,
|
|
3427
|
-
`/// Import this library,
|
|
3428
|
-
|
|
3429
|
-
)}.nomosWidget] once, and pass the`,
|
|
3430
|
-
`/// supplied [${dartApplicationClassName(
|
|
3839
|
+
`/// Import this library, bind one Peer Office handle, and pass the supplied`,
|
|
3840
|
+
`/// [${dartApplicationClassName(
|
|
3431
3841
|
describePackageName
|
|
3432
3842
|
)}] through the UI. Its typed actions, records,`,
|
|
3433
3843
|
`/// watches, references and status are the canonical application state. Do not recreate them`,
|
|
@@ -3439,7 +3849,7 @@ function emitDartPackages(
|
|
|
3439
3849
|
"library;",
|
|
3440
3850
|
"",
|
|
3441
3851
|
`export 'package:${packages.types}/${genRel}/${businessSharedFile}';`,
|
|
3442
|
-
`export 'package:
|
|
3852
|
+
`export 'package:nomos_client/src/workspace_open_progress.dart' show ApplicationWorkspaceOpenProgress;`,
|
|
3443
3853
|
`export 'package:nomos_types/nomos_types.dart' show CustodyError, CustodyPhase, CustodySource, NomosRecommendedAction;`,
|
|
3444
3854
|
...tenantTypesExportKeys
|
|
3445
3855
|
.map((file) => {
|
|
@@ -3460,14 +3870,23 @@ function emitDartPackages(
|
|
|
3460
3870
|
: []),
|
|
3461
3871
|
...(perDomain.length > 1
|
|
3462
3872
|
? [
|
|
3463
|
-
`export 'package:
|
|
3873
|
+
`export 'package:nomos_client/src/application_status.dart' show ApplicationStatus, ApplicationStatusPhase;`,
|
|
3464
3874
|
`export '${genRel}/${packageApplicationFile}.dart' show ${dartApplicationClassName(
|
|
3465
3875
|
describePackageName
|
|
3466
|
-
)}
|
|
3876
|
+
)}${
|
|
3877
|
+
applicationFrameworkCallsSource === undefined
|
|
3878
|
+
? ""
|
|
3879
|
+
: `, ${dartApplicationClassName(
|
|
3880
|
+
describePackageName
|
|
3881
|
+
)}Reception, ${dartApplicationMember(
|
|
3882
|
+
describePackageName
|
|
3883
|
+
)}Reception`
|
|
3884
|
+
};`,
|
|
3467
3885
|
...perDomain.map(({ mod, dd }) => {
|
|
3468
3886
|
const packageClass = dartApplicationClassName(describePackageName);
|
|
3469
3887
|
const publicNames = dartApplicationPublicClassNames(
|
|
3470
|
-
dd.modForDart
|
|
3888
|
+
dd.modForDart,
|
|
3889
|
+
{ callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }
|
|
3471
3890
|
).filter((name) => name !== packageClass);
|
|
3472
3891
|
return `export '${genRel}/${
|
|
3473
3892
|
mod.name
|
|
@@ -3480,7 +3899,7 @@ function emitDartPackages(
|
|
|
3480
3899
|
mod.name
|
|
3481
3900
|
}.application.dart' show ${dartApplicationPublicClassNames(
|
|
3482
3901
|
dd.modForDart,
|
|
3483
|
-
{ tenantRoot: true }
|
|
3902
|
+
{ tenantRoot: true, callUsda: { graph: lawGraphs.get(mod.name)!, paths: lawPaths.get(mod.name)! } }
|
|
3484
3903
|
).join(", ")};`
|
|
3485
3904
|
)),
|
|
3486
3905
|
"",
|
|
@@ -3501,27 +3920,22 @@ function emitDartPackages(
|
|
|
3501
3920
|
`publish_to: none # tenant-owned: remove to publish`,
|
|
3502
3921
|
`environment:`,
|
|
3503
3922
|
` sdk: ^3.5.0`,
|
|
3504
|
-
` flutter: ">=3.10.0"`,
|
|
3505
3923
|
`dependencies:`,
|
|
3506
|
-
` flutter:`,
|
|
3507
|
-
` sdk: flutter`,
|
|
3508
3924
|
` nomos_client: ${NOMOS_CLIENT_DEP}`,
|
|
3509
|
-
`
|
|
3925
|
+
` protobuf: ^5.0.0`,
|
|
3926
|
+
` fixnum: ^1.1.1`,
|
|
3510
3927
|
` ${packages.types}:`,
|
|
3511
3928
|
` path: ../${packages.types} # co-emitted sibling; replace with a hosted constraint when publishing`,
|
|
3512
|
-
...(applicationLawPath === undefined
|
|
3513
|
-
? []
|
|
3514
|
-
: [`flutter:`, ` assets:`, ` - lib/${genRel}/${embeddedLawName}`]),
|
|
3515
3929
|
``,
|
|
3516
3930
|
].join("\n"),
|
|
3517
|
-
{
|
|
3931
|
+
{
|
|
3932
|
+
nomos_client: NOMOS_CLIENT_DEP,
|
|
3933
|
+
protobuf: "^5.0.0",
|
|
3934
|
+
fixnum: "^1.1.1",
|
|
3935
|
+
},
|
|
3936
|
+
["flutter", "nomos_flutter"]
|
|
3518
3937
|
);
|
|
3519
|
-
|
|
3520
|
-
reconcileGeneratedLawAsset(
|
|
3521
|
-
pkgDir(packages.client),
|
|
3522
|
-
`lib/${genRel}/${embeddedLawName}`
|
|
3523
|
-
);
|
|
3524
|
-
}
|
|
3938
|
+
removeGeneratedClientLawAssets(pkgDir(packages.client));
|
|
3525
3939
|
|
|
3526
3940
|
// ── the TEST package (optional) — generated business scenarios, test-only dependency ──
|
|
3527
3941
|
if (packages.test !== undefined) {
|
|
@@ -3726,6 +4140,45 @@ function emitDartPackages(
|
|
|
3726
4140
|
|
|
3727
4141
|
// ── main ──────────────────────────────────────────────────────────────────────────
|
|
3728
4142
|
|
|
4143
|
+
/**
|
|
4144
|
+
* Fail unless the `@githolon/dsl` the law in `lawDir` resolves is this compiler's own module instance:
|
|
4145
|
+
* the same package directory, and the same loaded module (namespace identity), so the law and the
|
|
4146
|
+
* compiler share one authoring drain and one endorsement registry.
|
|
4147
|
+
*/
|
|
4148
|
+
async function assertOneDslInstance(lawDir: string): Promise<void> {
|
|
4149
|
+
const ownPackage = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
4150
|
+
let lawPackage: string;
|
|
4151
|
+
let lawEntry: string;
|
|
4152
|
+
try {
|
|
4153
|
+
const lawRequire = createRequire(pathToFileURL(path.join(lawDir, "noop.js")));
|
|
4154
|
+
lawPackage = realpathSync(lawRequire.resolve("@githolon/dsl/package.json"));
|
|
4155
|
+
lawEntry = realpathSync(path.join(path.dirname(lawPackage), "src", "index.ts"));
|
|
4156
|
+
} catch {
|
|
4157
|
+
// The law cannot resolve the DSL by name (a workspace law importing framework sources by path):
|
|
4158
|
+
// the compiler's own instance is the only one its modules can reach.
|
|
4159
|
+
return;
|
|
4160
|
+
}
|
|
4161
|
+
const versionOf = (file: string): string => {
|
|
4162
|
+
try {
|
|
4163
|
+
return (JSON.parse(readFileSync(file, "utf8")) as { version?: string }).version ?? "?";
|
|
4164
|
+
} catch {
|
|
4165
|
+
return "?";
|
|
4166
|
+
}
|
|
4167
|
+
};
|
|
4168
|
+
const split = (why: string): never =>
|
|
4169
|
+
fail(
|
|
4170
|
+
`TWO @githolon/dsl instances (${why}): the law resolves ${versionOf(lawPackage)} at ` +
|
|
4171
|
+
`${path.dirname(lawPackage)}, this compiler runs ${versionOf(ownPackage)} at ${path.dirname(ownPackage)}. ` +
|
|
4172
|
+
`Split instances split the DSL's registries, so plan probes come back empty and authority flows are not ` +
|
|
4173
|
+
`harvested: the law would compile weaker than written. Run the law package's own \`npx githolon compile\`, ` +
|
|
4174
|
+
`or make its @githolon/dsl resolve to this compiler's.`
|
|
4175
|
+
);
|
|
4176
|
+
if (realpathSync(ownPackage) !== lawPackage) split("two package directories");
|
|
4177
|
+
const own = await import(pathToFileURL(realpathSync(fileURLToPath(new URL("./index.ts", import.meta.url)))).href);
|
|
4178
|
+
const law = await import(pathToFileURL(lawEntry).href);
|
|
4179
|
+
if (own !== law) split("two loaded module instances");
|
|
4180
|
+
}
|
|
4181
|
+
|
|
3729
4182
|
async function main(): Promise<void> {
|
|
3730
4183
|
const argv = process.argv.slice(2);
|
|
3731
4184
|
const supportedFlags = new Set([
|
|
@@ -3733,6 +4186,8 @@ async function main(): Promise<void> {
|
|
|
3733
4186
|
"--compact",
|
|
3734
4187
|
"--analyze",
|
|
3735
4188
|
"--preflight",
|
|
4189
|
+
"--inspect-sqlite",
|
|
4190
|
+
"--inspect-law",
|
|
3736
4191
|
]);
|
|
3737
4192
|
for (const flag of argv.filter((arg) => arg.startsWith("--"))) {
|
|
3738
4193
|
if (supportedFlags.has(flag)) continue;
|
|
@@ -3755,6 +4210,15 @@ async function main(): Promise<void> {
|
|
|
3755
4210
|
// spends minutes producing artifacts that will immediately be discarded.
|
|
3756
4211
|
const preflightFlag =
|
|
3757
4212
|
argv.includes("--preflight") || process.env.NOMOS_PREFLIGHT === "1";
|
|
4213
|
+
const sqliteInspection = argv.includes("--inspect-sqlite");
|
|
4214
|
+
const lawInspection = argv.includes("--inspect-law");
|
|
4215
|
+
const graphInspection = sqliteInspection || lawInspection;
|
|
4216
|
+
if (graphInspection && preflightFlag) {
|
|
4217
|
+
fail("graph inspection requires the typed graph; do not combine it with --preflight");
|
|
4218
|
+
}
|
|
4219
|
+
if (sqliteInspection && lawInspection) {
|
|
4220
|
+
fail("select one graph inspection: --inspect-law or --inspect-sqlite");
|
|
4221
|
+
}
|
|
3758
4222
|
// §"Shipped (1–4)" follow-up: `githolon compile --analyze` (or NOMOS_ANALYZE=1) runs
|
|
3759
4223
|
// `dart analyze` on the generated Dart package after emission and fails the compile on red.
|
|
3760
4224
|
const analyzeFlag =
|
|
@@ -3763,51 +4227,45 @@ async function main(): Promise<void> {
|
|
|
3763
4227
|
const configPath = path.resolve(process.cwd(), configArg);
|
|
3764
4228
|
if (!existsSync(configPath)) fail(`config not found: ${configPath}`);
|
|
3765
4229
|
const cfgDir = path.dirname(configPath);
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
);
|
|
3788
|
-
} catch {
|
|
3789
|
-
return "?";
|
|
3790
|
-
}
|
|
3791
|
-
};
|
|
3792
|
-
console.error(
|
|
3793
|
-
`⚠ nomos-compile: TWO @githolon/dsl instances — the law resolves ${vOf(
|
|
3794
|
-
lawDslPkg
|
|
3795
|
-
)} at ${path.dirname(lawDslPkg)}, but this compiler runs ${vOf(
|
|
3796
|
-
path.resolve(ownDslPkg)
|
|
3797
|
-
)} at ${path.dirname(path.resolve(ownDslPkg))}. ` +
|
|
3798
|
-
`Split instances split the DSL's module registries (phantom lint errors, empty plan probes, id drift) — even at the same version string when the code differs. ` +
|
|
3799
|
-
`Run the law package's OWN \`npx githolon compile\`, or align the two installs.`
|
|
3800
|
-
);
|
|
3801
|
-
}
|
|
3802
|
-
} catch {
|
|
3803
|
-
/* no dsl resolvable from the law dir (vendored/workspace setups) — nothing to compare */
|
|
4230
|
+
const timing = process.env.NOMOS_COMPILE_TIMING === "1";
|
|
4231
|
+
const timingStart = performance.now();
|
|
4232
|
+
let timingLast = timingStart;
|
|
4233
|
+
const stage = (label: string): void => {
|
|
4234
|
+
if (!timing) return;
|
|
4235
|
+
const now = performance.now();
|
|
4236
|
+
console.error(
|
|
4237
|
+
`[nomos-timing] ${label.padEnd(36)} +${((now - timingLast) / 1000)
|
|
4238
|
+
.toFixed(3)
|
|
4239
|
+
.padStart(6)}s t=${((now - timingStart) / 1000).toFixed(3)}s`
|
|
4240
|
+
);
|
|
4241
|
+
timingLast = now;
|
|
4242
|
+
};
|
|
4243
|
+
if (timing) {
|
|
4244
|
+
console.error(
|
|
4245
|
+
`[nomos-timing] ${"process boot → main()".padEnd(36)} +${(
|
|
4246
|
+
timingStart / 1000
|
|
4247
|
+
)
|
|
4248
|
+
.toFixed(3)
|
|
4249
|
+
.padStart(6)}s`
|
|
4250
|
+
);
|
|
3804
4251
|
}
|
|
3805
4252
|
|
|
4253
|
+
// ONE DSL INSTANCE, OR NO COMPILE. The law's `import "@githolon/dsl"` resolves from the LAW's own
|
|
4254
|
+
// node_modules, while this compiler runs from wherever it was invoked. Two instances split every
|
|
4255
|
+
// module-level registry (the fluent-authoring drain, the endorsement records, brands): plan probes come
|
|
4256
|
+
// back empty, authority flows are silently not harvested, and the compiled law is weaker than the one
|
|
4257
|
+
// written. That is a different law, so it is an error, never a warning.
|
|
4258
|
+
await assertOneDslInstance(cfgDir);
|
|
4259
|
+
|
|
3806
4260
|
const cfgModule = (await import(pathToFileURL(configPath).href)) as {
|
|
3807
4261
|
default?: unknown;
|
|
3808
4262
|
};
|
|
3809
4263
|
const configValue = cfgModule.default;
|
|
4264
|
+
stage("config import");
|
|
3810
4265
|
if (isApplicationCompositionConfig(configValue)) {
|
|
4266
|
+
if (graphInspection) {
|
|
4267
|
+
fail("graph inspection requires a law package, not an application composition; nothing was emitted");
|
|
4268
|
+
}
|
|
3811
4269
|
try {
|
|
3812
4270
|
const emitted = compileApplicationComposition(configValue, configPath, {
|
|
3813
4271
|
requireUsdcat,
|
|
@@ -3949,6 +4407,7 @@ async function main(): Promise<void> {
|
|
|
3949
4407
|
applicationBirthPolicyDomains.push(domain.key);
|
|
3950
4408
|
}
|
|
3951
4409
|
}
|
|
4410
|
+
stage("domain discovery imports");
|
|
3952
4411
|
if (
|
|
3953
4412
|
cfg.application?.homeGenesis !== undefined &&
|
|
3954
4413
|
applicationHomeGenesisSource === undefined
|
|
@@ -3980,7 +4439,11 @@ async function main(): Promise<void> {
|
|
|
3980
4439
|
]),
|
|
3981
4440
|
];
|
|
3982
4441
|
|
|
3983
|
-
|
|
4442
|
+
// Inspection executes normal declaration/composition/identity resolution but
|
|
4443
|
+
// NEVER cleans tenant build output, writes its identity, or emits a release.
|
|
4444
|
+
const outDir = graphInspection
|
|
4445
|
+
? mkdtempSync(path.join(tmpdir(), lawInspection ? "nomos-law-inspect-" : "nomos-sqlite-inspect-"))
|
|
4446
|
+
: path.resolve(cfgDir, cfg.outDir ?? "build");
|
|
3984
4447
|
mkdirSync(outDir, { recursive: true });
|
|
3985
4448
|
// Interrupted pre-cutover compiles left thousands of private source/probe files beside public
|
|
3986
4449
|
// artifacts. They are neither custody nor diagnostics; clear only the compiler-owned namespace.
|
|
@@ -4058,6 +4521,7 @@ async function main(): Promise<void> {
|
|
|
4058
4521
|
const nativeWorkspaceInvariantReadIds = new Set<string>();
|
|
4059
4522
|
const routingInput: Record<string, Mod[]> = {};
|
|
4060
4523
|
const domainModules: DomainModule[] = [];
|
|
4524
|
+
const custodyPlacementDeclarations: DomainCustodyPlacement[] = [];
|
|
4061
4525
|
const directiveExportSources: Array<{
|
|
4062
4526
|
readonly directive: object;
|
|
4063
4527
|
readonly domainKey: string;
|
|
@@ -4170,6 +4634,10 @@ async function main(): Promise<void> {
|
|
|
4170
4634
|
exportName: string;
|
|
4171
4635
|
}
|
|
4172
4636
|
>();
|
|
4637
|
+
const sharingRedeems: Array<{
|
|
4638
|
+
readonly directive: object & { readonly id: string };
|
|
4639
|
+
readonly modulePath: string;
|
|
4640
|
+
}> = [];
|
|
4173
4641
|
for (const rel of d.modules) {
|
|
4174
4642
|
const abs = path.resolve(cfgDir, rel);
|
|
4175
4643
|
if (isEvolutionModulePath(abs)) {
|
|
@@ -4210,6 +4678,19 @@ async function main(): Promise<void> {
|
|
|
4210
4678
|
aggregateId: (value as { aggregateId: string }).aggregateId,
|
|
4211
4679
|
});
|
|
4212
4680
|
}
|
|
4681
|
+
// A `workspaceSharing(...)` policy LOWERS an invitation-redeem directive that carries a
|
|
4682
|
+
// workspace effect (it publishes the received access into the recipient's Home). Collect
|
|
4683
|
+
// them here and register after the whole domain is loaded — a tenant MAY also export the
|
|
4684
|
+
// same object directly (`export const redeem = sharing.invitationLaw.redeem`), and two
|
|
4685
|
+
// entries for one directive is the same refusal as none.
|
|
4686
|
+
if (isApplicationLanguage(value)) {
|
|
4687
|
+
for (const policy of value.workspaceSharing) {
|
|
4688
|
+
sharingRedeems.push({
|
|
4689
|
+
directive: policy.invitationLaw.redeem,
|
|
4690
|
+
modulePath: abs,
|
|
4691
|
+
});
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4213
4694
|
// `birth(Aggregate)` is intentionally a high-level declaration. Its public parent birth and
|
|
4214
4695
|
// private child seed are compiler-lowered executable offers, so preserve their authored
|
|
4215
4696
|
// symbol origin here rather than condemning both to the whole-domain fallback plan.
|
|
@@ -4239,6 +4720,26 @@ async function main(): Promise<void> {
|
|
|
4239
4720
|
}
|
|
4240
4721
|
}
|
|
4241
4722
|
}
|
|
4723
|
+
// The lowered redeem has no authored export of its own — `withWorkspaceSharingInvitationExports`
|
|
4724
|
+
// folds it into the merged bag under a synthetic name so custody and the executable engine
|
|
4725
|
+
// registry see the one seam. Register that same synthetic name, or the workspace effect it
|
|
4726
|
+
// carries resolves to zero configured exports and the compile refuses law the tenant never
|
|
4727
|
+
// wrote, with an instruction ("export it once") they cannot follow.
|
|
4728
|
+
for (const redeem of sharingRedeems) {
|
|
4729
|
+
if (
|
|
4730
|
+
directiveExportSources.some(
|
|
4731
|
+
(candidate) => candidate.directive === redeem.directive
|
|
4732
|
+
)
|
|
4733
|
+
)
|
|
4734
|
+
continue;
|
|
4735
|
+
directiveExportSources.push({
|
|
4736
|
+
directive: redeem.directive,
|
|
4737
|
+
domainKey: d.key,
|
|
4738
|
+
graphName: d.name ?? d.key,
|
|
4739
|
+
modulePath: redeem.modulePath,
|
|
4740
|
+
exportName: `__nomosWorkspaceSharingDirective:${redeem.directive.id}`,
|
|
4741
|
+
});
|
|
4742
|
+
}
|
|
4242
4743
|
const configuredPaths = new Set(
|
|
4243
4744
|
d.modules.map((rel) => path.resolve(cfgDir, rel))
|
|
4244
4745
|
);
|
|
@@ -4272,19 +4773,58 @@ async function main(): Promise<void> {
|
|
|
4272
4773
|
);
|
|
4273
4774
|
}
|
|
4274
4775
|
const homeGenesis = cfg.application?.homeGenesis;
|
|
4275
|
-
const
|
|
4276
|
-
cfg.application?.parentWorkspaceRoles ?? [];
|
|
4776
|
+
const receivingGroupRecovery = cfg.application?.receivingGroup?.recovery;
|
|
4277
4777
|
const policyMod = applicationHomePolicyLaw(
|
|
4278
4778
|
max,
|
|
4279
4779
|
applicationBirthPolicyDomains,
|
|
4280
4780
|
homeGenesis,
|
|
4281
|
-
|
|
4781
|
+
receivingGroupRecovery,
|
|
4282
4782
|
) as Mod;
|
|
4283
4783
|
mods.push(policyMod);
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4784
|
+
// The composed policy law composes the framework's child-authentication singletons into THIS
|
|
4785
|
+
// domain (`childAuthenticationExports`). Each is a module-level export of
|
|
4786
|
+
// `framework/child_authentication.ts`, so it is a declaration export of this domain exactly like an
|
|
4787
|
+
// authored module export: the workspace effects it authors (verifier-epoch pair, child repair) resolve
|
|
4788
|
+
// to one owning domain, and the runtime target encoder can `require` the very object by name.
|
|
4789
|
+
// The receiving-group recovery door (`receivingGroupRecoveryLaw`, composed only under the declared
|
|
4790
|
+
// `platform-identity-provider` posture) is registered the same way: its members are module-level
|
|
4791
|
+
// singletons of `framework/receiving_group_recovery.ts`, never function-scoped copies.
|
|
4792
|
+
const composedPolicyValues = new Set(Object.values(policyMod));
|
|
4793
|
+
const registeredComposedDirectives = new Set<object>();
|
|
4794
|
+
for (const [modulePath, frameworkModule] of [
|
|
4795
|
+
[CHILD_AUTHENTICATION_MODULE_PATH, childAuthenticationModule],
|
|
4796
|
+
[RECEIVING_GROUP_RECOVERY_MODULE_PATH, receivingGroupRecoveryModule],
|
|
4797
|
+
[MODEL_TRANSITION_MODULE_PATH, modelTransitionModule],
|
|
4798
|
+
[MODEL_AUTHORITY_REPAIR_MODULE_PATH, modelAuthorityRepairModule],
|
|
4799
|
+
] as const) {
|
|
4800
|
+
for (const [exportName, value] of Object.entries(frameworkModule)) {
|
|
4801
|
+
if (
|
|
4802
|
+
composedPolicyValues.has(value) &&
|
|
4803
|
+
value !== null &&
|
|
4804
|
+
typeof value === "object" &&
|
|
4805
|
+
typeof (value as { id?: unknown }).id === "string" &&
|
|
4806
|
+
typeof (value as { plan?: unknown }).plan === "function" &&
|
|
4807
|
+
typeof (value as { aggregateId?: unknown }).aggregateId === "string" &&
|
|
4808
|
+
!registeredComposedDirectives.has(value)
|
|
4809
|
+
) {
|
|
4810
|
+
registeredComposedDirectives.add(value);
|
|
4811
|
+
directiveExportSources.push({
|
|
4812
|
+
directive: value,
|
|
4813
|
+
domainKey: d.key,
|
|
4814
|
+
graphName: d.name ?? d.key,
|
|
4815
|
+
modulePath,
|
|
4816
|
+
exportName,
|
|
4817
|
+
});
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
const homeGenesisExpr =
|
|
4822
|
+
applicationHomeGenesisSource === undefined
|
|
4823
|
+
? "undefined"
|
|
4824
|
+
: `require(${JSON.stringify(
|
|
4825
|
+
applicationHomeGenesisSource.modulePath
|
|
4826
|
+
)})` +
|
|
4827
|
+
`[${JSON.stringify(applicationHomeGenesisSource.exportName)}]`;
|
|
4288
4828
|
sourceExprs.push(
|
|
4289
4829
|
`() => require(${JSON.stringify(
|
|
4290
4830
|
path.join(DSL_DIR, "src", "framework", "application_home.ts")
|
|
@@ -4292,7 +4832,10 @@ async function main(): Promise<void> {
|
|
|
4292
4832
|
`.applicationHomePolicyLaw(` +
|
|
4293
4833
|
`${JSON.stringify(max)}, ${JSON.stringify(
|
|
4294
4834
|
applicationBirthPolicyDomains
|
|
4295
|
-
)}, ${homeGenesisExpr}
|
|
4835
|
+
)}, ${homeGenesisExpr}` +
|
|
4836
|
+
// A declared posture rides the re-emitted source as a JSON string; an undeclared one is
|
|
4837
|
+
// omitted (not `undefined`), keeping the emitted expression byte-identical for older packages.
|
|
4838
|
+
`${receivingGroupRecovery === undefined ? "" : `, ${JSON.stringify(receivingGroupRecovery)}`})`
|
|
4296
4839
|
);
|
|
4297
4840
|
}
|
|
4298
4841
|
let merged: Mod = {};
|
|
@@ -4315,6 +4858,19 @@ async function main(): Promise<void> {
|
|
|
4315
4858
|
// `relationType("workspace")` (the same seam composeDomainModule expands) so this plane's openapi /
|
|
4316
4859
|
// relation-schema discovery sees the grant/revoke/seed directives + the teeth-bearing schema.
|
|
4317
4860
|
merged = expandRoleExports(merged);
|
|
4861
|
+
|
|
4862
|
+
// Placement is intentionally discovered beside law, never composed into it.
|
|
4863
|
+
// The compiler ships these typed options as a separate finished good. A
|
|
4864
|
+
// workspace must explicitly adopt one on its placement frontier; installing
|
|
4865
|
+
// or evolving main law can therefore never relocate custody as a side effect.
|
|
4866
|
+
for (const declaration of Object.values(merged).filter(
|
|
4867
|
+
isCustodyPlacementDeclaration
|
|
4868
|
+
) as CustodyPlacementDeclaration[]) {
|
|
4869
|
+
custodyPlacementDeclarations.push({
|
|
4870
|
+
domain: d.key,
|
|
4871
|
+
declaration,
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4318
4874
|
let shardingSums: AnySum[] = [];
|
|
4319
4875
|
let shardingMins: AnyExtremum[] = [];
|
|
4320
4876
|
let shardingMaxes: AnyExtremum[] = [];
|
|
@@ -4561,7 +5117,14 @@ async function main(): Promise<void> {
|
|
|
4561
5117
|
usesRelations &&
|
|
4562
5118
|
!queries.some((q) => q.id === relationTuplesByObject.id)
|
|
4563
5119
|
) {
|
|
4564
|
-
|
|
5120
|
+
// The audit query is framework law carried by the package, not tenant
|
|
5121
|
+
// application vocabulary. Keep it available to the Peer Office while
|
|
5122
|
+
// preventing the generated reception facade from treating it as a
|
|
5123
|
+
// public realm member.
|
|
5124
|
+
queries.push({
|
|
5125
|
+
...relationTuplesByObject,
|
|
5126
|
+
businessBirthInternal: true,
|
|
5127
|
+
} as QueryDecl);
|
|
4565
5128
|
}
|
|
4566
5129
|
}
|
|
4567
5130
|
const counts = [
|
|
@@ -4623,14 +5186,25 @@ async function main(): Promise<void> {
|
|
|
4623
5186
|
...shardingSums,
|
|
4624
5187
|
]),
|
|
4625
5188
|
];
|
|
4626
|
-
const
|
|
5189
|
+
const extrema = discover<AnyExtremum>(merged, isExtremumDecl);
|
|
5190
|
+
const exists_ = [...new Set([
|
|
5191
|
+
...discover<AnyExists>(merged, isExistsDecl),
|
|
5192
|
+
...resolveNamed<AnyExists>(merged, d.exists_, "exists_"),
|
|
5193
|
+
])];
|
|
5194
|
+
const orderedReads = [...new Set([
|
|
5195
|
+
...discover<OrderedReadDecl>(merged, isOrderedReadDecl),
|
|
5196
|
+
...resolveNamed<OrderedReadDecl>(merged, d.orderedReads, "orderedReads"),
|
|
5197
|
+
])];
|
|
5198
|
+
const mins = [...new Set([
|
|
5199
|
+
...extrema.filter((read) => read.kind === "min"),
|
|
4627
5200
|
...resolveNamed<AnyExtremum>(merged, d.mins, "mins"),
|
|
4628
5201
|
...shardingMins,
|
|
4629
|
-
];
|
|
4630
|
-
const maxes = [
|
|
5202
|
+
])];
|
|
5203
|
+
const maxes = [...new Set([
|
|
5204
|
+
...extrema.filter((read) => read.kind === "max"),
|
|
4631
5205
|
...resolveNamed<AnyExtremum>(merged, d.maxes, "maxes"),
|
|
4632
5206
|
...shardingMaxes,
|
|
4633
|
-
];
|
|
5207
|
+
])];
|
|
4634
5208
|
// `capability()` declarations AUTO-DISCOVER (the marketplace lane, M2 —
|
|
4635
5209
|
// "declare ONE thing"); the config-named lane stays for hand-rolled
|
|
4636
5210
|
// impureCapability quartets. Deduped by task-aggregate id (a config-named
|
|
@@ -4679,6 +5253,8 @@ async function main(): Promise<void> {
|
|
|
4679
5253
|
...(sums.length > 0 ? { sums } : {}),
|
|
4680
5254
|
...(mins.length > 0 ? { mins } : {}),
|
|
4681
5255
|
...(maxes.length > 0 ? { maxes } : {}),
|
|
5256
|
+
...(exists_.length > 0 ? { exists_ } : {}),
|
|
5257
|
+
...(orderedReads.length > 0 ? { orderedReads } : {}),
|
|
4682
5258
|
...(workspaceTypes.length > 0 ? { workspaceTypes } : {}),
|
|
4683
5259
|
...(workspaceInvariants.length > 0 ? { workspaceInvariants } : {}),
|
|
4684
5260
|
// Relation declarations are already present in `modules` (including config-named exports), and
|
|
@@ -4716,12 +5292,25 @@ async function main(): Promise<void> {
|
|
|
4716
5292
|
executableDirectives.map((directive) => [directive.id, directive])
|
|
4717
5293
|
)
|
|
4718
5294
|
);
|
|
5295
|
+
const publicIntentSourceFor = (directiveId: string) => {
|
|
5296
|
+
const publicIntent = (composed.applicationIntents ?? []).find(
|
|
5297
|
+
(intent) =>
|
|
5298
|
+
isBusinessIntent(intent) &&
|
|
5299
|
+
intent.steps.length === 1 &&
|
|
5300
|
+
intent.steps[0]?.directive.id === directiveId
|
|
5301
|
+
);
|
|
5302
|
+
return (
|
|
5303
|
+
directOfferExports.get(publicIntent?.id ?? directiveId) ??
|
|
5304
|
+
directOfferExports.get(directiveId)
|
|
5305
|
+
);
|
|
5306
|
+
};
|
|
4719
5307
|
offerSourceExportsByDomain.set(
|
|
4720
5308
|
d.key,
|
|
4721
5309
|
new Map(
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
5310
|
+
executableDirectiveIds.flatMap((directiveId) => {
|
|
5311
|
+
const source = publicIntentSourceFor(directiveId);
|
|
5312
|
+
return source === undefined ? [] : [[directiveId, source] as const];
|
|
5313
|
+
})
|
|
4725
5314
|
)
|
|
4726
5315
|
);
|
|
4727
5316
|
const invariantSources = new Map<
|
|
@@ -4765,8 +5354,8 @@ async function main(): Promise<void> {
|
|
|
4765
5354
|
|
|
4766
5355
|
// ── THE SELF-DESCRIBE DECLS (G1): the FINISHED plain read/maintenance decls the lump's
|
|
4767
5356
|
// `describe` reconstructs the openapi fragment from. Directives + aggregates are read from
|
|
4768
|
-
// the live module exports IN the lump; these ride alongside (
|
|
4769
|
-
//
|
|
5357
|
+
// the live module exports IN the lump; these ride alongside (sharding reads are
|
|
5358
|
+
// framework-appended — not shape-rediscoverable). Omit-when-empty so a
|
|
4770
5359
|
// decl-free domain contributes nothing (and offers still derive from its directives).
|
|
4771
5360
|
{
|
|
4772
5361
|
const declsForDomain: Record<string, unknown[]> = {};
|
|
@@ -4783,6 +5372,10 @@ async function main(): Promise<void> {
|
|
|
4783
5372
|
declsForDomain.mins = mins.map((e) => finishExtremum(e));
|
|
4784
5373
|
if (maxes.length > 0)
|
|
4785
5374
|
declsForDomain.maxes = maxes.map((e) => finishExtremum(e));
|
|
5375
|
+
if (exists_.length > 0)
|
|
5376
|
+
declsForDomain.exists_ = exists_.map(finishExists);
|
|
5377
|
+
if (orderedReads.length > 0)
|
|
5378
|
+
declsForDomain.orderedReads = orderedReads;
|
|
4786
5379
|
if (spatials.length > 0)
|
|
4787
5380
|
declsForDomain.spatials = spatials.map((s) => ({
|
|
4788
5381
|
id: s.id,
|
|
@@ -4871,6 +5464,22 @@ async function main(): Promise<void> {
|
|
|
4871
5464
|
}
|
|
4872
5465
|
}
|
|
4873
5466
|
|
|
5467
|
+
const custodyPlacementDeclarationsPath = path.join(
|
|
5468
|
+
outDir,
|
|
5469
|
+
`${cfg.name}.custody-placement.usda`
|
|
5470
|
+
);
|
|
5471
|
+
if (custodyPlacementDeclarations.length > 0) {
|
|
5472
|
+
writeFileSync(
|
|
5473
|
+
custodyPlacementDeclarationsPath,
|
|
5474
|
+
custodyPlacementDeclarationsUsda(custodyPlacementDeclarations),
|
|
5475
|
+
"utf8"
|
|
5476
|
+
);
|
|
5477
|
+
} else {
|
|
5478
|
+
// A recompile after declarations are removed must not leave an apparently
|
|
5479
|
+
// current operational-policy artifact beside the package.
|
|
5480
|
+
rmSync(custodyPlacementDeclarationsPath, { force: true });
|
|
5481
|
+
}
|
|
5482
|
+
|
|
4874
5483
|
const dependencyUseFindings = packageDependencyUsageFindings({
|
|
4875
5484
|
packageName: cfg.name,
|
|
4876
5485
|
exportedDomains: cfg.domains.map((domain) => domain.key),
|
|
@@ -4903,6 +5512,20 @@ async function main(): Promise<void> {
|
|
|
4903
5512
|
// custody emission. A statically invalid package should fail in seconds and must never leave a
|
|
4904
5513
|
// plausible deployable artifact behind merely because the runtime representation changed shape.
|
|
4905
5514
|
const authorTimeFindings = checkDomainModules(domainModules);
|
|
5515
|
+
// Framework machinery composed into an application is not that application's business law: the private
|
|
5516
|
+
// receiving group, nomos_internal_ ids, and every directive a birth-capable law received from the framework
|
|
5517
|
+
// (marked businessBirthInternal). Their refusals still surface, as private refusals, below.
|
|
5518
|
+
const frameworkComposedDirectives = new Set(domainModules.flatMap((mod) =>
|
|
5519
|
+
(mod.directives as readonly { readonly id: string; readonly businessBirthInternal?: unknown }[])
|
|
5520
|
+
.filter((directive) => directive.businessBirthInternal === true)
|
|
5521
|
+
.map((directive) => `${mod.domain ?? mod.name}\0${directive.id}`)));
|
|
5522
|
+
const isFrameworkAuthorTimeFinding = (
|
|
5523
|
+
finding: (typeof authorTimeFindings)[number]
|
|
5524
|
+
) =>
|
|
5525
|
+
finding.domain === APPLICATION_HOME_DOMAIN ||
|
|
5526
|
+
finding.directiveId?.startsWith("nomos_internal_") === true ||
|
|
5527
|
+
(finding.directiveId !== undefined &&
|
|
5528
|
+
frameworkComposedDirectives.has(`${finding.domain}\0${finding.directiveId}`));
|
|
4906
5529
|
// Self-description is a compiler-owned presentation projection, not executable law. Build it
|
|
4907
5530
|
// once from the same composed modules and place it only in the separately routed describe plan;
|
|
4908
5531
|
// exact offer/read plans must not carry OpenAPI generation and Zod JSON-Schema machinery.
|
|
@@ -4915,9 +5538,7 @@ async function main(): Promise<void> {
|
|
|
4915
5538
|
|
|
4916
5539
|
if (preflightFlag) {
|
|
4917
5540
|
const businessFindings = authorTimeFindings.filter(
|
|
4918
|
-
(finding) =>
|
|
4919
|
-
finding.domain !== APPLICATION_HOME_DOMAIN &&
|
|
4920
|
-
finding.directiveId?.startsWith("nomos_internal_") !== true
|
|
5541
|
+
(finding) => !isFrameworkAuthorTimeFinding(finding)
|
|
4921
5542
|
);
|
|
4922
5543
|
console.log(
|
|
4923
5544
|
`nomos-preflight: ${cfg.name} (${domainModules.length} domain(s))`
|
|
@@ -4943,11 +5564,6 @@ async function main(): Promise<void> {
|
|
|
4943
5564
|
return;
|
|
4944
5565
|
}
|
|
4945
5566
|
|
|
4946
|
-
const isFrameworkAuthorTimeFinding = (
|
|
4947
|
-
finding: (typeof authorTimeFindings)[number]
|
|
4948
|
-
) =>
|
|
4949
|
-
finding.domain === APPLICATION_HOME_DOMAIN ||
|
|
4950
|
-
finding.directiveId?.startsWith("nomos_internal_") === true;
|
|
4951
5567
|
const earlyBusinessRefusals = authorTimeFindings.filter(
|
|
4952
5568
|
(finding) =>
|
|
4953
5569
|
!isFrameworkAuthorTimeFinding(finding) && finding.level === "refuse"
|
|
@@ -4976,6 +5592,7 @@ async function main(): Promise<void> {
|
|
|
4976
5592
|
process.exitCode = 1;
|
|
4977
5593
|
return;
|
|
4978
5594
|
}
|
|
5595
|
+
stage("import + compose domains");
|
|
4979
5596
|
|
|
4980
5597
|
// ── STABLE-ID CONTINUITY (#58 — names are labels, identity is minted) ──
|
|
4981
5598
|
// Derive each composed domain's stable ids from the PRIOR compile: the committed
|
|
@@ -5093,7 +5710,12 @@ async function main(): Promise<void> {
|
|
|
5093
5710
|
const discoveredGraph = discoverLawGraph(
|
|
5094
5711
|
graphModule,
|
|
5095
5712
|
emittedStableIds,
|
|
5096
|
-
nextIdentity
|
|
5713
|
+
nextIdentity,
|
|
5714
|
+
{ workspaceEffectDomain: (target) => {
|
|
5715
|
+
const owners = directiveExportSources.filter(candidate => candidate.directive === target);
|
|
5716
|
+
if (owners.length !== 1) fail(`workspace effect '${target.id}' must have exactly one declaration export; found ${owners.length}`);
|
|
5717
|
+
return owners[0]!.domainKey;
|
|
5718
|
+
} }
|
|
5097
5719
|
);
|
|
5098
5720
|
for (const node of discoveredGraph.graph.nodes) {
|
|
5099
5721
|
if (
|
|
@@ -5155,6 +5777,53 @@ async function main(): Promise<void> {
|
|
|
5155
5777
|
};
|
|
5156
5778
|
}
|
|
5157
5779
|
|
|
5780
|
+
if (lawInspection) {
|
|
5781
|
+
const graphs = [...typedLawGraphs.values()];
|
|
5782
|
+
const paths = new Map<LawGraph["nodes"][number], string>();
|
|
5783
|
+
const compiledGraphs = new Map<LawGraph, ReturnType<typeof compileSparseLawGraphUsda>>();
|
|
5784
|
+
for (const graph of graphs) {
|
|
5785
|
+
const compiled = compileSparseLawGraphUsda(graph, `domains/${graph.domain.entity.label}.usda`, graphs);
|
|
5786
|
+
compiledGraphs.set(graph, compiled);
|
|
5787
|
+
for (const node of graph.nodes) {
|
|
5788
|
+
const lawPath = compiled.paths.get(node.entity.id);
|
|
5789
|
+
if (lawPath !== undefined) paths.set(node, lawPath);
|
|
5790
|
+
}
|
|
5791
|
+
}
|
|
5792
|
+
const report = {
|
|
5793
|
+
...commandWorkingSetReport(graphs, new LawDependencyIndex(graphs, paths,
|
|
5794
|
+
(graph, dependency) => compiledGraphs.get(graph)!.resolveDependency(dependency))),
|
|
5795
|
+
packageName: cfg.name, configPath,
|
|
5796
|
+
identitySha256: existsSync(lockPath)
|
|
5797
|
+
? createHash("sha256").update(readFileSync(lockPath)).digest("hex") : null,
|
|
5798
|
+
};
|
|
5799
|
+
const reportPath = path.join(outDir, "command-working-sets.json");
|
|
5800
|
+
writeFileSync(reportPath, JSON.stringify(report, null, 2) + "\n");
|
|
5801
|
+
console.log(JSON.stringify({ kind: report.kind, graphNodes: report.graphNodes,
|
|
5802
|
+
commands: report.commands.length, reportPath }));
|
|
5803
|
+
return;
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5806
|
+
if (sqliteInspection) {
|
|
5807
|
+
const report = {
|
|
5808
|
+
...inspectSqliteLayouts([...typedLawGraphs.values()]),
|
|
5809
|
+
computationEngine: await sqliteComputationBundle(
|
|
5810
|
+
entryDomainSourceByKey,
|
|
5811
|
+
collectEngineRouting(routingInput)
|
|
5812
|
+
),
|
|
5813
|
+
packageName: cfg.name,
|
|
5814
|
+
configPath,
|
|
5815
|
+
descriptorSamples: sqliteDescriptorSamples(domainModules, typedLawGraphs),
|
|
5816
|
+
identitySha256: existsSync(lockPath)
|
|
5817
|
+
? createHash("sha256").update(readFileSync(lockPath)).digest("hex")
|
|
5818
|
+
: null,
|
|
5819
|
+
scope: "declaration/storage mapping only; not runtime reads, query coverage or release approval",
|
|
5820
|
+
};
|
|
5821
|
+
const reportPath = path.join(outDir, "sqlite-layout-report.json");
|
|
5822
|
+
writeFileSync(reportPath, JSON.stringify(report, null, 2) + "\n");
|
|
5823
|
+
console.log(JSON.stringify({ kind: report.kind, ...report.summary, reportPath }));
|
|
5824
|
+
return;
|
|
5825
|
+
}
|
|
5826
|
+
|
|
5158
5827
|
// ── ORDINARY-COMPILE EVOLUTION GATE ──────────────────────────────────────────────
|
|
5159
5828
|
// Agents reliably run `compile`; they do not reliably discover an optional migration command.
|
|
5160
5829
|
// Compare the candidate against the committed identity frontier here, after stable-id
|
|
@@ -5182,25 +5851,13 @@ async function main(): Promise<void> {
|
|
|
5182
5851
|
const locked =
|
|
5183
5852
|
historicalLock.domains[mod.name] ?? historicalLock.domains[domainKey];
|
|
5184
5853
|
if (locked !== undefined) {
|
|
5185
|
-
const currentImplementationDetailAggregateIds = new Set(
|
|
5186
|
-
mod.aggregates
|
|
5187
|
-
.filter(
|
|
5188
|
-
(aggregate) =>
|
|
5189
|
-
(aggregate as { businessBirthInternal?: unknown })
|
|
5190
|
-
.businessBirthInternal === true
|
|
5191
|
-
)
|
|
5192
|
-
.map((aggregate) => aggregate.id)
|
|
5193
|
-
);
|
|
5194
5854
|
// Framework aggregates are shared implementation law, reconciled under @framework rather
|
|
5195
5855
|
// than owned by this tenant domain. Domain-scoped implementation aggregates such as
|
|
5196
|
-
// NomosSourceFrontier
|
|
5197
|
-
//
|
|
5198
|
-
// as its topology changes; never instruct a tenant to author a retirement for Nomos internals.
|
|
5856
|
+
// NomosSourceFrontier still hold durable state even when hidden from the public interface.
|
|
5857
|
+
// Keep them in the evolution check: hiding an aggregate is not authority to replace its SID.
|
|
5199
5858
|
const localAggregates = Object.fromEntries(
|
|
5200
5859
|
Object.entries(locked.stableIds.aggregates).filter(
|
|
5201
|
-
([
|
|
5202
|
-
!frameworkAggregateSids.has(aggregate.sid) &&
|
|
5203
|
-
!currentImplementationDetailAggregateIds.has(aggregateId)
|
|
5860
|
+
([, aggregate]) => !frameworkAggregateSids.has(aggregate.sid)
|
|
5204
5861
|
)
|
|
5205
5862
|
);
|
|
5206
5863
|
priorDomainsForPackage[domainKey] = {
|
|
@@ -5253,13 +5910,14 @@ async function main(): Promise<void> {
|
|
|
5253
5910
|
const packageInterface: Record<string, DomainInterface> = {};
|
|
5254
5911
|
for (const mod of presentationDomainModules) {
|
|
5255
5912
|
const key = mod.domain ?? mod.name;
|
|
5256
|
-
const di = domainInterface(mod, nextLock.domains[mod.name]?.stableIds);
|
|
5257
5913
|
const graph = typedLawGraphs.get(mod.name);
|
|
5258
5914
|
if (graph === undefined) {
|
|
5259
5915
|
fail(
|
|
5260
5916
|
`compiler surface invariant: domain '${key}' has no typed USDA graph`
|
|
5261
5917
|
);
|
|
5262
5918
|
}
|
|
5919
|
+
const offeredInterface = domainInterfaceFromLawGraph(graph);
|
|
5920
|
+
const di = publicClientInterface([mod], { [key]: offeredInterface })[key]!;
|
|
5263
5921
|
const graphRoutes = graph.nodes
|
|
5264
5922
|
.filter((node) => node.kind === "intent" && node.public)
|
|
5265
5923
|
.map((node) => (node.kind === "intent" ? node.publicRoute : ""))
|
|
@@ -5308,12 +5966,16 @@ async function main(): Promise<void> {
|
|
|
5308
5966
|
}
|
|
5309
5967
|
: di;
|
|
5310
5968
|
}
|
|
5311
|
-
//
|
|
5312
|
-
//
|
|
5313
|
-
//
|
|
5314
|
-
//
|
|
5315
|
-
|
|
5316
|
-
|
|
5969
|
+
// The application compatibility requirement is a first-class USDA build artifact. It is deliberately
|
|
5970
|
+
// NOT installed law: clients carry these opaque bytes to the kernel, which compares the requirement
|
|
5971
|
+
// against authenticated installed-law stages. The compiler and generated clients all derive it from
|
|
5972
|
+
// this same in-memory graph; no JSON/hash compatibility twin exists.
|
|
5973
|
+
const compatibilityUsda = emitApplicationCompatibilityUsda(packageInterface);
|
|
5974
|
+
const compatibilityUsdaPath = path.join(
|
|
5975
|
+
outDir,
|
|
5976
|
+
`${cfg.name}.compatibility.usda`
|
|
5977
|
+
);
|
|
5978
|
+
writeFileSync(compatibilityUsdaPath, compatibilityUsda, "utf8");
|
|
5317
5979
|
|
|
5318
5980
|
// ── RECURSIVE-PAYLOAD NOTES (z.lazy) — a self-referencing tree/JSON payload field is FULLY
|
|
5319
5981
|
// supported: each codegen layer mints a named recursive type for it (TS `type <Name>`, Dart
|
|
@@ -5351,6 +6013,9 @@ async function main(): Promise<void> {
|
|
|
5351
6013
|
// exact texts under their logical paths; no parallel layer directory or JSON reconstruction exists.
|
|
5352
6014
|
const v3Closure = [...typedLawGraphs.values()];
|
|
5353
6015
|
const v3DomainAssets: EmittedLawClosureAsset[] = [];
|
|
6016
|
+
const v3DomainRoots: string[] = [];
|
|
6017
|
+
const v3LawPathByNode = new Map<LawGraph["nodes"][number], string>();
|
|
6018
|
+
const v3CompiledGraphs = new Map<LawGraph, ReturnType<typeof compileSparseLawGraphUsda>>();
|
|
5354
6019
|
const typedDomainIdentityByName = new Map<string, string>();
|
|
5355
6020
|
const typedLawPathsByName = new Map<string, ReadonlyMap<string, string>>();
|
|
5356
6021
|
for (const [domain, graph] of typedLawGraphs) {
|
|
@@ -5361,6 +6026,7 @@ async function main(): Promise<void> {
|
|
|
5361
6026
|
logicalPath,
|
|
5362
6027
|
v3Closure
|
|
5363
6028
|
);
|
|
6029
|
+
v3CompiledGraphs.set(graph, compiledLayer);
|
|
5364
6030
|
const layerText = compiledLayer.text;
|
|
5365
6031
|
const sha256 = sha256HexUtf8(layerText);
|
|
5366
6032
|
v3DomainAssets.push({
|
|
@@ -5368,10 +6034,51 @@ async function main(): Promise<void> {
|
|
|
5368
6034
|
sha256,
|
|
5369
6035
|
utf8: layerText,
|
|
5370
6036
|
});
|
|
6037
|
+
const domainRoot = compiledLayer.paths.get(graph.domain.entity.id);
|
|
6038
|
+
if (domainRoot === undefined) {
|
|
6039
|
+
fail(`domain '${domain}' did not emit its canonical OpenUSD root path`);
|
|
6040
|
+
}
|
|
6041
|
+
v3DomainRoots.push(domainRoot);
|
|
6042
|
+
for (const node of graph.nodes) {
|
|
6043
|
+
const lawPath = compiledLayer.paths.get(node.entity.id);
|
|
6044
|
+
if (lawPath !== undefined) v3LawPathByNode.set(node, lawPath);
|
|
6045
|
+
}
|
|
5371
6046
|
typedDomainIdentityByName.set(domain, sha256);
|
|
5372
6047
|
typedLawPathsByName.set(domain, compiledLayer.paths);
|
|
5373
6048
|
}
|
|
5374
6049
|
|
|
6050
|
+
// Index the transient graph once. Both projections follow the same nominal edges, including
|
|
6051
|
+
// transitive cross-domain dependencies. Per-command selection also follows reverse guard edges;
|
|
6052
|
+
// the retained package command projection remains the compatibility consumer until the runtime
|
|
6053
|
+
// can carry a member-scoped contract without presenting it as a complete package.
|
|
6054
|
+
const lawDependencies = new LawDependencyIndex(v3Closure, v3LawPathByNode,
|
|
6055
|
+
(graph, dependency) => v3CompiledGraphs.get(graph)!.resolveDependency(dependency));
|
|
6056
|
+
const commonProjectionPaths = [
|
|
6057
|
+
"/Nomos/Package",
|
|
6058
|
+
"/Nomos/Framework",
|
|
6059
|
+
"/__NomosRuntime_7e9c/Routes",
|
|
6060
|
+
...v3DomainRoots.map((root) => `${root}/Retirements`),
|
|
6061
|
+
];
|
|
6062
|
+
const readSeeds = v3Closure.flatMap(graph => graph.nodes.filter(node =>
|
|
6063
|
+
node.kind === "aggregate" || node.kind === "read" ||
|
|
6064
|
+
node.kind === "relation" || node.kind === "object-type"));
|
|
6065
|
+
const commandSeeds = v3Closure.flatMap(graph => graph.nodes.filter(node =>
|
|
6066
|
+
node.kind === "intent" || node.kind === "policy" || node.kind === "invariant" ||
|
|
6067
|
+
node.kind === "capability" || node.kind === "workspace-type" ||
|
|
6068
|
+
(node.kind === "structural" && node.structuralKind === "flow")));
|
|
6069
|
+
const readProjectionPaths = new Set([
|
|
6070
|
+
...commonProjectionPaths, ...lawDependencies.select(readSeeds).paths,
|
|
6071
|
+
]);
|
|
6072
|
+
const commandProjectionPaths = new Set([
|
|
6073
|
+
...commonProjectionPaths, ...lawDependencies.select(commandSeeds, true).paths,
|
|
6074
|
+
]);
|
|
6075
|
+
// Diagnostic only: these paths are not executable authority or a new law file format. A future
|
|
6076
|
+
// member-scoped USD artifact must prove its complete semantic contract before replacing the
|
|
6077
|
+
// package-wide runtime view. In particular USD masks include descendants, so node counts alone
|
|
6078
|
+
// are not a claim about materialized prim count, file I/O, RSS, or first-command latency.
|
|
6079
|
+
writeFileSync(path.join(outDir, "command-working-sets.json"),
|
|
6080
|
+
JSON.stringify(commandWorkingSetReport(v3Closure, lawDependencies), null, 2) + "\n");
|
|
6081
|
+
|
|
5375
6082
|
// Some exact directives are deliberately private implementation details: they must not appear in
|
|
5376
6083
|
// the tenant's generated client, but typed application-law edges can still require the kernel to
|
|
5377
6084
|
// execute them. Preserve that distinction here. The exact Directive object and its one configured
|
|
@@ -5380,39 +6087,44 @@ async function main(): Promise<void> {
|
|
|
5380
6087
|
const retainExactExecutableDirective = (
|
|
5381
6088
|
edge: string,
|
|
5382
6089
|
declaredDomain: string | undefined,
|
|
5383
|
-
directive: Parameters<typeof synthesizePreconditionInvariants>[0]
|
|
6090
|
+
directive: Parameters<typeof synthesizePreconditionInvariants>[0]
|
|
5384
6091
|
) => {
|
|
5385
6092
|
const matches = directiveExportSources.filter(
|
|
5386
|
-
(candidate) => candidate.directive === directive
|
|
6093
|
+
(candidate) => candidate.directive === directive
|
|
5387
6094
|
);
|
|
5388
6095
|
if (matches.length !== 1) {
|
|
5389
6096
|
fail(
|
|
5390
|
-
`${edge} targets '${
|
|
6097
|
+
`${edge} targets '${
|
|
6098
|
+
declaredDomain === undefined
|
|
6099
|
+
? directive.id
|
|
6100
|
+
: `${declaredDomain}/${directive.id}`
|
|
6101
|
+
}', ` +
|
|
5391
6102
|
`but that exact Directive object is ` +
|
|
5392
|
-
`exported by ${matches.length} configured law modules; export it once
|
|
6103
|
+
`exported by ${matches.length} configured law modules; export it once`
|
|
5393
6104
|
);
|
|
5394
6105
|
}
|
|
5395
6106
|
const target = matches[0]!;
|
|
5396
6107
|
if (declaredDomain !== undefined && target.domainKey !== declaredDomain) {
|
|
5397
6108
|
fail(
|
|
5398
6109
|
`${edge} declares '${declaredDomain}/${directive.id}', but the exact Directive export belongs ` +
|
|
5399
|
-
`to domain '${target.domainKey}'
|
|
6110
|
+
`to domain '${target.domainKey}'`
|
|
5400
6111
|
);
|
|
5401
6112
|
}
|
|
5402
|
-
const targetIds =
|
|
6113
|
+
const targetIds =
|
|
6114
|
+
executableDirectiveIdsByDomain.get(target.domainKey) ?? [];
|
|
5403
6115
|
if (!targetIds.includes(directive.id)) {
|
|
5404
6116
|
executableDirectiveIdsByDomain.set(
|
|
5405
6117
|
target.domainKey,
|
|
5406
|
-
[...targetIds, directive.id].sort()
|
|
6118
|
+
[...targetIds, directive.id].sort()
|
|
5407
6119
|
);
|
|
5408
6120
|
}
|
|
5409
6121
|
const targetDirectives = new Map(
|
|
5410
|
-
executableDirectivesByDomain.get(target.domainKey) ?? []
|
|
6122
|
+
executableDirectivesByDomain.get(target.domainKey) ?? []
|
|
5411
6123
|
);
|
|
5412
6124
|
targetDirectives.set(directive.id, directive);
|
|
5413
6125
|
executableDirectivesByDomain.set(target.domainKey, targetDirectives);
|
|
5414
6126
|
const targetExports = new Map(
|
|
5415
|
-
offerSourceExportsByDomain.get(target.domainKey) ?? []
|
|
6127
|
+
offerSourceExportsByDomain.get(target.domainKey) ?? []
|
|
5416
6128
|
);
|
|
5417
6129
|
targetExports.set(directive.id, {
|
|
5418
6130
|
modulePath: target.modulePath,
|
|
@@ -5430,7 +6142,7 @@ async function main(): Promise<void> {
|
|
|
5430
6142
|
retainExactExecutableDirective(
|
|
5431
6143
|
"application home genesis",
|
|
5432
6144
|
step.domain,
|
|
5433
|
-
step.directive
|
|
6145
|
+
step.directive
|
|
5434
6146
|
);
|
|
5435
6147
|
}
|
|
5436
6148
|
|
|
@@ -5450,10 +6162,19 @@ async function main(): Promise<void> {
|
|
|
5450
6162
|
for (const module of domainModules) {
|
|
5451
6163
|
const sourceDomain = module.domain ?? module.name;
|
|
5452
6164
|
for (const directive of module.directives ?? []) {
|
|
5453
|
-
|
|
5454
|
-
for (const effect of directive.workspaceEffects) {
|
|
6165
|
+
for (const effect of directive.workspaceEffects ?? []) {
|
|
5455
6166
|
const sourceMatches = directiveExportSources.filter(
|
|
5456
|
-
(candidate) =>
|
|
6167
|
+
(candidate) => {
|
|
6168
|
+
if (candidate.directive === directive) return true;
|
|
6169
|
+
// Composition may add static read/auth requirements by copying a declaration.
|
|
6170
|
+
// Its executable plan, payload contract and outbound papers remain the exact
|
|
6171
|
+
// source objects; do not confuse that with an unrelated same-named command.
|
|
6172
|
+
const source = candidate.directive as Directive<unknown>;
|
|
6173
|
+
return candidate.domainKey === sourceDomain &&
|
|
6174
|
+
source.id === directive.id && source.plan === directive.plan &&
|
|
6175
|
+
source.payloadSchema === directive.payloadSchema &&
|
|
6176
|
+
source.workspaceEffects === directive.workspaceEffects;
|
|
6177
|
+
}
|
|
5457
6178
|
);
|
|
5458
6179
|
if (sourceMatches.length !== 1) {
|
|
5459
6180
|
fail(
|
|
@@ -5465,7 +6186,7 @@ async function main(): Promise<void> {
|
|
|
5465
6186
|
const target = retainExactExecutableDirective(
|
|
5466
6187
|
`workspace effect '${sourceDomain}/${directive.id}'`,
|
|
5467
6188
|
undefined,
|
|
5468
|
-
effect.target
|
|
6189
|
+
effect.target
|
|
5469
6190
|
);
|
|
5470
6191
|
const graph = typedLawGraphs.get(target.graphName);
|
|
5471
6192
|
const paths = typedLawPathsByName.get(target.graphName);
|
|
@@ -5474,6 +6195,9 @@ async function main(): Promise<void> {
|
|
|
5474
6195
|
`workspace effect target '${target.graphName}/${effect.target.id}' has no canonical law graph`
|
|
5475
6196
|
);
|
|
5476
6197
|
}
|
|
6198
|
+
// This call is authored by authenticated source law inside the kernel
|
|
6199
|
+
// plan and becomes a carried Layer. It does not cross the public client
|
|
6200
|
+
// ABI whose operation payload is Protobuf.
|
|
5477
6201
|
const generated = emitTsCallUsdaEncoder(
|
|
5478
6202
|
graph,
|
|
5479
6203
|
paths,
|
|
@@ -5493,6 +6217,7 @@ async function main(): Promise<void> {
|
|
|
5493
6217
|
}
|
|
5494
6218
|
}
|
|
5495
6219
|
}
|
|
6220
|
+
stage("stable ids + evolve gate + interface");
|
|
5496
6221
|
|
|
5497
6222
|
// ── reports ──
|
|
5498
6223
|
const entryReports: string[] = [];
|
|
@@ -5521,6 +6246,67 @@ async function main(): Promise<void> {
|
|
|
5521
6246
|
// law whose invariant declarations are inconsistent refuses to COMPILE, and the
|
|
5522
6247
|
// table can never disagree with what a full force would register).
|
|
5523
6248
|
const routing = collectEngineRouting(routingInput);
|
|
6249
|
+
stage(" ↳ collectEngineRouting");
|
|
6250
|
+
const dartCfg = cfg.dart ?? false;
|
|
6251
|
+
if (
|
|
6252
|
+
dartCfg !== false &&
|
|
6253
|
+
dartCfg !== true &&
|
|
6254
|
+
(typeof dartCfg !== "object" ||
|
|
6255
|
+
dartCfg === null ||
|
|
6256
|
+
(dartCfg.out !== undefined && typeof dartCfg.out !== "string") ||
|
|
6257
|
+
(dartCfg.support !== undefined && dartCfg.support !== "package") ||
|
|
6258
|
+
(dartCfg.barrel !== undefined &&
|
|
6259
|
+
!/^[a-z][a-z0-9_]*$/.test(String(dartCfg.barrel))) ||
|
|
6260
|
+
(dartCfg.names !== undefined &&
|
|
6261
|
+
(typeof dartCfg.names !== "object" ||
|
|
6262
|
+
dartCfg.names === null ||
|
|
6263
|
+
Array.isArray(dartCfg.names))) ||
|
|
6264
|
+
(dartCfg.packages !== undefined &&
|
|
6265
|
+
(typeof dartCfg.packages !== "object" ||
|
|
6266
|
+
dartCfg.packages === null ||
|
|
6267
|
+
typeof dartCfg.packages.types !== "string" ||
|
|
6268
|
+
typeof dartCfg.packages.client !== "string")))
|
|
6269
|
+
) {
|
|
6270
|
+
fail(
|
|
6271
|
+
`config key 'dart' must be false, true, or { out?: "relative/dir", support?: "package", barrel?: "snake_case_name", packages?: { types, client, test?, compat? }, names?: { Generated: "Alias" } }; got ${JSON.stringify(
|
|
6272
|
+
dartCfg
|
|
6273
|
+
)}`
|
|
6274
|
+
);
|
|
6275
|
+
}
|
|
6276
|
+
const dartPackages =
|
|
6277
|
+
typeof dartCfg === "object" ? dartCfg.packages : undefined;
|
|
6278
|
+
const dartNames = typeof dartCfg === "object" ? dartCfg.names : undefined;
|
|
6279
|
+
// The operation Protobuf bindings depend only on the composed law, so start their generators
|
|
6280
|
+
// now: protoc-gen-dart alone costs seconds of JIT start on a cold cache, and nothing below
|
|
6281
|
+
// needs the bindings until the TypeScript and Dart emits await these.
|
|
6282
|
+
const tsOperationProtoPrewarm = prewarmTsOperationProto(
|
|
6283
|
+
cfgDir,
|
|
6284
|
+
[
|
|
6285
|
+
...new Map(
|
|
6286
|
+
[...closedDomainModules, ...presentationDomainModules].map(
|
|
6287
|
+
(module) => [module.name, module] as const
|
|
6288
|
+
)
|
|
6289
|
+
).values(),
|
|
6290
|
+
],
|
|
6291
|
+
typedLawGraphs,
|
|
6292
|
+
typedLawPathsByName
|
|
6293
|
+
);
|
|
6294
|
+
const dartOperationProtoPrewarm =
|
|
6295
|
+
dartCfg === false
|
|
6296
|
+
? Promise.resolve()
|
|
6297
|
+
: prewarmDartOperationProto(
|
|
6298
|
+
dartPackages === undefined
|
|
6299
|
+
? presentationDomainModules
|
|
6300
|
+
: [
|
|
6301
|
+
...new Map(
|
|
6302
|
+
[...presentationDomainModules, ...closedDomainModules].map(
|
|
6303
|
+
(module) => [module.name, module] as const
|
|
6304
|
+
)
|
|
6305
|
+
).values(),
|
|
6306
|
+
],
|
|
6307
|
+
typedLawGraphs,
|
|
6308
|
+
typedLawPathsByName
|
|
6309
|
+
);
|
|
5524
6310
|
// Zanzibar identities are namespaced by the domain's STABLE USDA identity, not its mutable dispatch
|
|
5525
6311
|
// label. Carry that one label→identity table into every plan asset so writeTuple() emits the same
|
|
5526
6312
|
// object type the typed law graph placed on gates and relation declarations.
|
|
@@ -5610,6 +6396,7 @@ async function main(): Promise<void> {
|
|
|
5610
6396
|
pathToFileURL(path.join(path.dirname(esbuildPkgJson), "lib", "main.js"))
|
|
5611
6397
|
.href
|
|
5612
6398
|
)) as typeof import("esbuild");
|
|
6399
|
+
stage(" ↳ esbuild import");
|
|
5613
6400
|
|
|
5614
6401
|
const compact =
|
|
5615
6402
|
process.env.NOMOS_READABLE === "1"
|
|
@@ -5687,18 +6474,25 @@ async function main(): Promise<void> {
|
|
|
5687
6474
|
])
|
|
5688
6475
|
);
|
|
5689
6476
|
let planSequence = 0;
|
|
6477
|
+
const PLAN_ENTRY_SOURCEFILE = ".nomos_plan.ts";
|
|
5690
6478
|
const orderedDomainKeys = [...entryDomainSourceByKey.keys()];
|
|
5691
6479
|
const frameworkPlanModulePaths = [
|
|
5692
|
-
...new Set(
|
|
5693
|
-
Object.values(BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES).map(
|
|
6480
|
+
...new Set([
|
|
6481
|
+
...Object.values(BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES).map(
|
|
5694
6482
|
(source) => source.modulePath
|
|
5695
|
-
)
|
|
5696
|
-
|
|
6483
|
+
),
|
|
6484
|
+
CHILD_AUTHENTICATION_MODULE_PATH,
|
|
6485
|
+
RECEIVING_GROUP_RECOVERY_MODULE_PATH,
|
|
6486
|
+
MODEL_TRANSITION_MODULE_PATH,
|
|
6487
|
+
MODEL_AUTHORITY_REPAIR_MODULE_PATH,
|
|
6488
|
+
]),
|
|
5697
6489
|
];
|
|
6490
|
+
stage(" ↳ plan compiler identity");
|
|
5698
6491
|
const planSourceSlicer = new PlanSourceSliceProgram(
|
|
5699
6492
|
[...configuredModulePaths, ...frameworkPlanModulePaths],
|
|
5700
6493
|
cfgDir
|
|
5701
6494
|
);
|
|
6495
|
+
stage(" ↳ PlanSourceSliceProgram (ts.createProgram)");
|
|
5702
6496
|
const routingFor = (domainKeys: readonly string[]) => {
|
|
5703
6497
|
const wanted = new Set(domainKeys);
|
|
5704
6498
|
const retain = (
|
|
@@ -5886,6 +6680,7 @@ async function main(): Promise<void> {
|
|
|
5886
6680
|
const authoredRuntimeImports = planSourceSlicer.externalRuntimeImports(
|
|
5887
6681
|
publicRuntimeCandidates
|
|
5888
6682
|
);
|
|
6683
|
+
stage(" ↳ externalRuntimeImports");
|
|
5889
6684
|
const sharedRuntimeModules: Array<{
|
|
5890
6685
|
readonly id: string;
|
|
5891
6686
|
readonly source: string;
|
|
@@ -5925,7 +6720,10 @@ async function main(): Promise<void> {
|
|
|
5925
6720
|
let sharedRuntimeProfileInputs:
|
|
5926
6721
|
| readonly { readonly module: string; readonly bytesInOutput: number }[]
|
|
5927
6722
|
| undefined;
|
|
5928
|
-
|
|
6723
|
+
let sharedRuntimeProfileCompilation: Promise<number> | undefined;
|
|
6724
|
+
const compileSharedRuntimeProfile = (): Promise<number> =>
|
|
6725
|
+
(sharedRuntimeProfileCompilation ??= compileSharedRuntimeProfileOnce());
|
|
6726
|
+
const compileSharedRuntimeProfileOnce = async (): Promise<number> => {
|
|
5929
6727
|
if (sharedRuntimeProfileBytes !== undefined)
|
|
5930
6728
|
return sharedRuntimeProfileBytes;
|
|
5931
6729
|
const sourcePath = path.join(outDir, ".nomos_shared_runtime_profile.ts");
|
|
@@ -6120,10 +6918,16 @@ async function main(): Promise<void> {
|
|
|
6120
6918
|
if (cached !== undefined) {
|
|
6121
6919
|
bundled = cached.javascript;
|
|
6122
6920
|
} else {
|
|
6123
|
-
|
|
6124
|
-
|
|
6921
|
+
// The entry rides esbuild's stdin under one constant name: a numbered temp file put its
|
|
6922
|
+
// sequence number into the bundle's module header, so identical law could hash differently
|
|
6923
|
+
// depending on how many plans a warm cache had skipped before it.
|
|
6125
6924
|
const result = await esbuildMod.build({
|
|
6126
|
-
|
|
6925
|
+
stdin: {
|
|
6926
|
+
contents: entrySource,
|
|
6927
|
+
loader: "ts",
|
|
6928
|
+
resolveDir: cfgDir,
|
|
6929
|
+
sourcefile: PLAN_ENTRY_SOURCEFILE,
|
|
6930
|
+
},
|
|
6127
6931
|
absWorkingDir: cfgDir,
|
|
6128
6932
|
bundle: true,
|
|
6129
6933
|
write: false,
|
|
@@ -6140,7 +6944,6 @@ async function main(): Promise<void> {
|
|
|
6140
6944
|
? {}
|
|
6141
6945
|
: { plugins: [planSourceSlicer.plugin(effectiveSliceTargets)] }),
|
|
6142
6946
|
});
|
|
6143
|
-
rmSync(sourcePath, { force: true });
|
|
6144
6947
|
const output = result.outputFiles?.[0];
|
|
6145
6948
|
if (output === undefined)
|
|
6146
6949
|
fail("shared-runtime plan compile produced no JavaScript output");
|
|
@@ -6159,13 +6962,11 @@ async function main(): Promise<void> {
|
|
|
6159
6962
|
);
|
|
6160
6963
|
}
|
|
6161
6964
|
const inputs = Object.keys(emitted)
|
|
6965
|
+
.filter((module) => module !== PLAN_ENTRY_SOURCEFILE)
|
|
6162
6966
|
.map((module) =>
|
|
6163
6967
|
path.isAbsolute(module) ? module : path.resolve(cfgDir, module)
|
|
6164
6968
|
)
|
|
6165
|
-
.filter(
|
|
6166
|
-
(file) =>
|
|
6167
|
-
path.resolve(file) !== path.resolve(sourcePath) && existsSync(file)
|
|
6168
|
-
)
|
|
6969
|
+
.filter((file) => existsSync(file))
|
|
6169
6970
|
.map((file) => ({ path: file, sha256: planInputDigest(file) }))
|
|
6170
6971
|
.sort((left, right) => left.path.localeCompare(right.path));
|
|
6171
6972
|
if (inputs.length > 0) {
|
|
@@ -6246,11 +7047,28 @@ async function main(): Promise<void> {
|
|
|
6246
7047
|
}
|
|
6247
7048
|
}
|
|
6248
7049
|
};
|
|
6249
|
-
|
|
6250
|
-
const
|
|
7050
|
+
const publicOfferSelector = (domain: string, directiveId: string): string => {
|
|
7051
|
+
const graph = typedLawGraphs.get(domain);
|
|
7052
|
+
const intent = graph?.nodes.find(
|
|
7053
|
+
(node) => node.kind === "intent" && node.entity.label === directiveId
|
|
7054
|
+
);
|
|
7055
|
+
return `plan:offer:${domain}:${
|
|
7056
|
+
intent?.kind === "intent" ? intent.publicRoute : directiveId
|
|
7057
|
+
}`;
|
|
7058
|
+
};
|
|
7059
|
+
stage("engine entry + routing");
|
|
7060
|
+
// Every plan below is an independent esbuild bundle (memoized by signature, deduplicated by
|
|
7061
|
+
// content hash, and emitted sorted by selector/hash), so compile them concurrently and bind
|
|
7062
|
+
// routes afterwards in declaration order.
|
|
7063
|
+
const domainPlans = await Promise.all(
|
|
7064
|
+
orderedDomainKeys.map((key) => compilePlan([key], false, false))
|
|
7065
|
+
);
|
|
7066
|
+
orderedDomainKeys.forEach((key, index) => {
|
|
7067
|
+
const asset = domainPlans[index]!;
|
|
6251
7068
|
domainPlanByKey.set(key, asset);
|
|
6252
7069
|
routeAsset(`plan:domain:${key}`, asset);
|
|
6253
|
-
}
|
|
7070
|
+
});
|
|
7071
|
+
stage("shared runtime + domain plans");
|
|
6254
7072
|
|
|
6255
7073
|
// Public offers are addressed more narrowly than domains. Split direct TypeScript exports by
|
|
6256
7074
|
// authored source span, then symbol-slice each shard so one large domain file cannot impose one
|
|
@@ -6267,10 +7085,14 @@ async function main(): Promise<void> {
|
|
|
6267
7085
|
// 8 KiB; an irreducible single offer remains explicit and attributable instead of being hidden
|
|
6268
7086
|
// inside a domain-wide fallback.
|
|
6269
7087
|
const OFFER_PLAN_MAX_UTF8_BYTES = 48 * 1024;
|
|
6270
|
-
|
|
6271
|
-
|
|
7088
|
+
interface OfferShardEntry {
|
|
7089
|
+
directiveId: string;
|
|
7090
|
+
exportName: string;
|
|
7091
|
+
aggregateId: string;
|
|
7092
|
+
position: number;
|
|
7093
|
+
}
|
|
7094
|
+
const domainOfferPlans = await Promise.all(orderedDomainKeys.map(async (domain) => {
|
|
6272
7095
|
const exports = offerSourceExportsByDomain.get(domain) ?? new Map();
|
|
6273
|
-
const routed = new Set<string>();
|
|
6274
7096
|
|
|
6275
7097
|
// Business-birth lowering injects several framework offers which execute as one child-genesis
|
|
6276
7098
|
// transaction. Compile their typed source closures into one shared plan: preparing any one exact
|
|
@@ -6283,6 +7105,7 @@ async function main(): Promise<void> {
|
|
|
6283
7105
|
const target = BUSINESS_BIRTH_FRAMEWORK_PLAN_SOURCES[directiveId];
|
|
6284
7106
|
return target === undefined ? [] : [{ directiveId, ...target }];
|
|
6285
7107
|
});
|
|
7108
|
+
let frameworkPlan: Promise<UsdPlanAsset> | undefined;
|
|
6286
7109
|
if (frameworkTargets.length > 0) {
|
|
6287
7110
|
const selectedModules = new Map<
|
|
6288
7111
|
string,
|
|
@@ -6310,7 +7133,7 @@ async function main(): Promise<void> {
|
|
|
6310
7133
|
const source = ` ${JSON.stringify(
|
|
6311
7134
|
BUSINESS_BIRTH_FRAMEWORK_PLAN_DOMAIN
|
|
6312
7135
|
)}: [${selectedSources.join(", ")}],`;
|
|
6313
|
-
|
|
7136
|
+
frameworkPlan = compilePlan([domain], false, false, {
|
|
6314
7137
|
sourceOverrides: new Map([[domain, source]]),
|
|
6315
7138
|
includePreloads: false,
|
|
6316
7139
|
cacheKey: "business-birth-framework",
|
|
@@ -6318,23 +7141,13 @@ async function main(): Promise<void> {
|
|
|
6318
7141
|
routedDomainFallback: BUSINESS_BIRTH_FRAMEWORK_PLAN_DOMAIN,
|
|
6319
7142
|
includeMintInstalls: false,
|
|
6320
7143
|
});
|
|
6321
|
-
for (const target of frameworkTargets) {
|
|
6322
|
-
routeAsset(`plan:offer:${domain}:${target.directiveId}`, asset);
|
|
6323
|
-
retainGeneratedInvariantRoutes(domain, target.directiveId, asset);
|
|
6324
|
-
routed.add(target.directiveId);
|
|
6325
|
-
}
|
|
6326
7144
|
}
|
|
6327
|
-
const
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
exportName: string;
|
|
6332
|
-
aggregateId: string;
|
|
6333
|
-
position: number;
|
|
6334
|
-
}>
|
|
6335
|
-
>();
|
|
7145
|
+
const routedByFramework = new Set(
|
|
7146
|
+
frameworkTargets.map((target) => target.directiveId)
|
|
7147
|
+
);
|
|
7148
|
+
const byModule = new Map<string, OfferShardEntry[]>();
|
|
6336
7149
|
for (const [directiveId, source] of exports) {
|
|
6337
|
-
if (
|
|
7150
|
+
if (routedByFramework.has(directiveId)) continue;
|
|
6338
7151
|
const position = planSourceSlicer.exportPosition({
|
|
6339
7152
|
modulePath: source.modulePath,
|
|
6340
7153
|
exportName: source.exportName,
|
|
@@ -6349,14 +7162,14 @@ async function main(): Promise<void> {
|
|
|
6349
7162
|
});
|
|
6350
7163
|
byModule.set(source.modulePath, entries);
|
|
6351
7164
|
}
|
|
6352
|
-
|
|
7165
|
+
const offers = await Promise.all([...byModule].map(async ([modulePath, entries]) => {
|
|
6353
7166
|
entries.sort(
|
|
6354
7167
|
(left, right) =>
|
|
6355
7168
|
left.position - right.position ||
|
|
6356
7169
|
left.directiveId.localeCompare(right.directiveId)
|
|
6357
7170
|
);
|
|
6358
|
-
const shards:
|
|
6359
|
-
let current:
|
|
7171
|
+
const shards: OfferShardEntry[][] = [];
|
|
7172
|
+
let current: OfferShardEntry[] = [];
|
|
6360
7173
|
let shardStart = 0;
|
|
6361
7174
|
for (const entry of entries) {
|
|
6362
7175
|
if (
|
|
@@ -6371,8 +7184,8 @@ async function main(): Promise<void> {
|
|
|
6371
7184
|
}
|
|
6372
7185
|
if (current.length > 0) shards.push(current);
|
|
6373
7186
|
const compileOfferShard = async (
|
|
6374
|
-
shard:
|
|
6375
|
-
): Promise<
|
|
7187
|
+
shard: OfferShardEntry[]
|
|
7188
|
+
): Promise<Array<{ entry: OfferShardEntry; asset: UsdPlanAsset }>> => {
|
|
6376
7189
|
const selected: PlanSourceSliceTarget[] = shard.map((entry) => ({
|
|
6377
7190
|
modulePath,
|
|
6378
7191
|
exportName: entry.exportName,
|
|
@@ -6404,34 +7217,55 @@ async function main(): Promise<void> {
|
|
|
6404
7217
|
let splitAt = shard.findIndex((entry) => entry.position >= midpoint);
|
|
6405
7218
|
if (splitAt <= 0 || splitAt >= shard.length)
|
|
6406
7219
|
splitAt = Math.ceil(shard.length / 2);
|
|
6407
|
-
await
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
routeAsset(`plan:offer:${domain}:${entry.directiveId}`, asset);
|
|
6413
|
-
retainGeneratedInvariantRoutes(domain, entry.directiveId, asset);
|
|
6414
|
-
if (entry.directiveId.startsWith("nomosSeed")) {
|
|
6415
|
-
// The generated seed writes the born aggregate immediately. Its `.fromBirth()` / user
|
|
6416
|
-
// aggregate invariant is part of the same authored symbol closure, so route that first
|
|
6417
|
-
// gate check through the already-warmed slice instead of cold-loading the whole domain.
|
|
6418
|
-
aggregateInvariantPlanOverrides.set(entry.aggregateId, asset);
|
|
6419
|
-
aggregateInvariantPlanOverrides.set(
|
|
6420
|
-
`${domain}.${entry.aggregateId}`,
|
|
6421
|
-
asset
|
|
6422
|
-
);
|
|
6423
|
-
}
|
|
6424
|
-
routed.add(entry.directiveId);
|
|
7220
|
+
const [left, right] = await Promise.all([
|
|
7221
|
+
compileOfferShard(shard.slice(0, splitAt)),
|
|
7222
|
+
compileOfferShard(shard.slice(splitAt)),
|
|
7223
|
+
]);
|
|
7224
|
+
return [...left, ...right];
|
|
6425
7225
|
}
|
|
7226
|
+
return shard.map((entry) => ({ entry, asset }));
|
|
6426
7227
|
};
|
|
6427
|
-
|
|
7228
|
+
return (await Promise.all(shards.map(compileOfferShard))).flat();
|
|
7229
|
+
}));
|
|
7230
|
+
return {
|
|
7231
|
+
domain,
|
|
7232
|
+
frameworkTargets,
|
|
7233
|
+
frameworkAsset: frameworkPlan === undefined ? undefined : await frameworkPlan,
|
|
7234
|
+
offers: offers.flat(),
|
|
7235
|
+
};
|
|
7236
|
+
}));
|
|
7237
|
+
for (const { domain, frameworkTargets, frameworkAsset, offers } of domainOfferPlans) {
|
|
7238
|
+
const fallback = domainPlanByKey.get(domain)!;
|
|
7239
|
+
const routed = new Set<string>();
|
|
7240
|
+
if (frameworkAsset !== undefined) {
|
|
7241
|
+
for (const target of frameworkTargets) {
|
|
7242
|
+
routeAsset(publicOfferSelector(domain, target.directiveId), frameworkAsset);
|
|
7243
|
+
retainGeneratedInvariantRoutes(domain, target.directiveId, frameworkAsset);
|
|
7244
|
+
routed.add(target.directiveId);
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
for (const { entry, asset } of offers) {
|
|
7248
|
+
routeAsset(publicOfferSelector(domain, entry.directiveId), asset);
|
|
7249
|
+
retainGeneratedInvariantRoutes(domain, entry.directiveId, asset);
|
|
7250
|
+
if (entry.directiveId.startsWith("nomosSeed")) {
|
|
7251
|
+
// The generated seed writes the born aggregate immediately. Its `.fromBirth()` / user
|
|
7252
|
+
// aggregate invariant is part of the same authored symbol closure, so route that first
|
|
7253
|
+
// gate check through the already-warmed slice instead of cold-loading the whole domain.
|
|
7254
|
+
aggregateInvariantPlanOverrides.set(entry.aggregateId, asset);
|
|
7255
|
+
aggregateInvariantPlanOverrides.set(
|
|
7256
|
+
`${domain}.${entry.aggregateId}`,
|
|
7257
|
+
asset
|
|
7258
|
+
);
|
|
7259
|
+
}
|
|
7260
|
+
routed.add(entry.directiveId);
|
|
6428
7261
|
}
|
|
6429
7262
|
for (const directiveId of executableDirectiveIdsByDomain.get(domain) ??
|
|
6430
7263
|
[]) {
|
|
6431
7264
|
if (!routed.has(directiveId))
|
|
6432
|
-
routeAsset(
|
|
7265
|
+
routeAsset(publicOfferSelector(domain, directiveId), fallback);
|
|
6433
7266
|
}
|
|
6434
7267
|
}
|
|
7268
|
+
stage("offer shard plans");
|
|
6435
7269
|
// Hand-authored workspace invariants are independently addressable law too. The previous
|
|
6436
7270
|
// fallback compiled their entire domain merely to run one `reads`/`assert` pair, even though
|
|
6437
7271
|
// offer compilation already knows how to retain one authored export closure. Build that exact
|
|
@@ -6439,6 +7273,10 @@ async function main(): Promise<void> {
|
|
|
6439
7273
|
// When `language(...)` remapped private effect invariants onto public intents, retain only those
|
|
6440
7274
|
// public intent exports and reconstruct that language so the existing remapping machinery remains
|
|
6441
7275
|
// the sole owner of payload adaptation.
|
|
7276
|
+
const workspaceInvariantPlans: Array<{
|
|
7277
|
+
readonly invariantIds: readonly string[];
|
|
7278
|
+
readonly plan: Promise<UsdPlanAsset>;
|
|
7279
|
+
}> = [];
|
|
6442
7280
|
for (const domain of orderedDomainKeys) {
|
|
6443
7281
|
const byDirective = new Map<
|
|
6444
7282
|
string,
|
|
@@ -6521,39 +7359,48 @@ async function main(): Promise<void> {
|
|
|
6521
7359
|
const domainSource = ` ${JSON.stringify(
|
|
6522
7360
|
domain
|
|
6523
7361
|
)}: [${moduleExpression}],`;
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
.
|
|
6529
|
-
|
|
7362
|
+
workspaceInvariantPlans.push({
|
|
7363
|
+
invariantIds: members.map(([id]) => id),
|
|
7364
|
+
plan: compilePlan([domain], false, false, {
|
|
7365
|
+
sourceOverrides: new Map([[domain, domainSource]]),
|
|
7366
|
+
sliceTargets: [...selectedByKey.values()],
|
|
7367
|
+
cacheKey: `workspace-invariants:${directiveId}:${members
|
|
7368
|
+
.map(([id]) => id)
|
|
7369
|
+
.join(",")}`,
|
|
7370
|
+
}),
|
|
6530
7371
|
});
|
|
6531
|
-
for (const [invariantId] of members) {
|
|
6532
|
-
workspaceInvariantPlanOverrides.set(invariantId, asset);
|
|
6533
|
-
}
|
|
6534
7372
|
}
|
|
6535
7373
|
}
|
|
6536
|
-
|
|
7374
|
+
for (const { invariantIds, plan } of workspaceInvariantPlans) {
|
|
7375
|
+
const asset = await plan;
|
|
7376
|
+
for (const invariantId of invariantIds) {
|
|
7377
|
+
workspaceInvariantPlanOverrides.set(invariantId, asset);
|
|
7378
|
+
}
|
|
7379
|
+
}
|
|
7380
|
+
stage("workspace invariant plans");
|
|
7381
|
+
const reportRoutes: Promise<void>[] = [
|
|
7382
|
+
routePlan("describe", orderedDomainKeys, false, true),
|
|
7383
|
+
];
|
|
6537
7384
|
for (const reportId of Object.keys(cfg.reports ?? {}))
|
|
6538
|
-
|
|
6539
|
-
const routeTable =
|
|
7385
|
+
reportRoutes.push(routePlan(`report:report:${reportId}`, [], true));
|
|
7386
|
+
const routeTable = (
|
|
6540
7387
|
prefix: string,
|
|
6541
7388
|
table: Readonly<Record<string, readonly string[]>> | undefined
|
|
6542
|
-
):
|
|
7389
|
+
): void => {
|
|
6543
7390
|
for (const [id, domains] of Object.entries(table ?? {}))
|
|
6544
|
-
|
|
7391
|
+
reportRoutes.push(routePlan(`${prefix}${id}`, domains));
|
|
6545
7392
|
};
|
|
6546
|
-
|
|
6547
|
-
|
|
7393
|
+
routeTable("report:derive:", routing.derivedOf);
|
|
7394
|
+
routeTable("report:combine:", routing.combinedOf);
|
|
6548
7395
|
for (const [id, domains] of Object.entries(
|
|
6549
7396
|
routing.aggregateInvariantOf ?? {}
|
|
6550
7397
|
)) {
|
|
6551
7398
|
const override = aggregateInvariantPlanOverrides.get(id);
|
|
6552
7399
|
if (override !== undefined)
|
|
6553
7400
|
routeAsset(`report:aggregateInvariant:${id}`, override);
|
|
6554
|
-
else
|
|
7401
|
+
else reportRoutes.push(routePlan(`report:aggregateInvariant:${id}`, domains));
|
|
6555
7402
|
}
|
|
6556
|
-
|
|
7403
|
+
routeTable("report:invariant:", routing.relationOf);
|
|
6557
7404
|
for (const [id, domains] of Object.entries(
|
|
6558
7405
|
routing.workspaceInvariantOf ?? {}
|
|
6559
7406
|
)) {
|
|
@@ -6569,11 +7416,13 @@ async function main(): Promise<void> {
|
|
|
6569
7416
|
routeAsset(`report:workspaceInvariant:${id}`, override);
|
|
6570
7417
|
} else {
|
|
6571
7418
|
if (!nativeWorkspaceInvariantReadIds.has(id)) {
|
|
6572
|
-
|
|
7419
|
+
reportRoutes.push(routePlan(`report:workspaceInvariantReads:${id}`, domains));
|
|
6573
7420
|
}
|
|
6574
|
-
|
|
7421
|
+
reportRoutes.push(routePlan(`report:workspaceInvariant:${id}`, domains));
|
|
6575
7422
|
}
|
|
6576
7423
|
}
|
|
7424
|
+
await Promise.all(reportRoutes);
|
|
7425
|
+
stage("describe + report plans");
|
|
6577
7426
|
// Adaptive offer splitting deliberately compiles candidate parents before retaining their children.
|
|
6578
7427
|
// Only route-reachable content is custody; exploratory compiler work never becomes law bytes.
|
|
6579
7428
|
const routedPlanHashes = new Set(planRoutes.map((route) => route.planSha256));
|
|
@@ -6711,6 +7560,8 @@ async function main(): Promise<void> {
|
|
|
6711
7560
|
}
|
|
6712
7561
|
}
|
|
6713
7562
|
|
|
7563
|
+
stage("captured-read gate");
|
|
7564
|
+
let bootProbeFailure: Promise<string | undefined> | undefined;
|
|
6714
7565
|
// ── 1b. THE FROZEN-SANDBOX BOOT PROBE (fail-closed) ──
|
|
6715
7566
|
// The sealed engine freezes globalThis after a plan asset's top level; a lazy
|
|
6716
7567
|
// domain's module init runs LATER, inside the dispatch. Any module that writes
|
|
@@ -6833,28 +7684,32 @@ async function main(): Promise<void> {
|
|
|
6833
7684
|
return { planPath, checks };
|
|
6834
7685
|
});
|
|
6835
7686
|
writeFileSync(probeJobsPath, JSON.stringify(probeJobs), "utf8");
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
}
|
|
7687
|
+
// The probe is its own child process: let it run while the law closure is composed and
|
|
7688
|
+
// usdcat projects it, and judge its verdict before the package is sealed.
|
|
7689
|
+
bootProbeFailure = execFileAsync(process.execPath, [probePath, probeJobsPath], {
|
|
7690
|
+
encoding: "utf8",
|
|
7691
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
7692
|
+
})
|
|
7693
|
+
.then(() => undefined)
|
|
7694
|
+
.catch((e: unknown) => {
|
|
6842
7695
|
const stderr = String((e as { stderr?: string }).stderr ?? "").trim();
|
|
6843
7696
|
const [domainKey = "?", msg = stderr] = stderr.includes("\u0000")
|
|
6844
7697
|
? stderr.split("\u0000")
|
|
6845
7698
|
: ["?", stderr];
|
|
6846
|
-
|
|
7699
|
+
return (
|
|
6847
7700
|
`domain '${domainKey}' failed its selected-plan registry probe — the compiler routed an ` +
|
|
6848
7701
|
`offer to a plan that cannot dispatch it, or a module initializer failed after the ` +
|
|
6849
7702
|
`sandbox freeze: ${msg}`
|
|
6850
7703
|
);
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
7704
|
+
})
|
|
7705
|
+
.finally(() => {
|
|
7706
|
+
rmSync(probePath, { force: true });
|
|
7707
|
+
rmSync(probeJobsPath, { force: true });
|
|
7708
|
+
for (const planPath of probePlanPaths) rmSync(planPath, { force: true });
|
|
7709
|
+
});
|
|
6856
7710
|
}
|
|
6857
7711
|
|
|
7712
|
+
stage("boot probe (started)");
|
|
6858
7713
|
// ── 2. Canonical typed USDA closure ──
|
|
6859
7714
|
// There is no flattened package and no JSON law intermediate. Sparse typed source opinions plus
|
|
6860
7715
|
// compiler-owned plan/framework layers are the package; composition is deferred to LawStage.
|
|
@@ -6865,7 +7720,7 @@ async function main(): Promise<void> {
|
|
|
6865
7720
|
sha256: sha256HexUtf8(sharedRuntimeProfileJavascript),
|
|
6866
7721
|
javascript: sharedRuntimeProfileJavascript,
|
|
6867
7722
|
};
|
|
6868
|
-
|
|
7723
|
+
let lawClosure = emitLawClosure(
|
|
6869
7724
|
cfg.name,
|
|
6870
7725
|
domainModules.map((module) => module.domain ?? module.name),
|
|
6871
7726
|
runtimeDependencies,
|
|
@@ -6874,9 +7729,6 @@ async function main(): Promise<void> {
|
|
|
6874
7729
|
routedPlanAssets,
|
|
6875
7730
|
planRoutes
|
|
6876
7731
|
);
|
|
6877
|
-
// The composed USDA closure is the law. Generated clients, lifecycle rows and
|
|
6878
|
-
// compatibility metadata all bind this identity, never the retired flattened envelope.
|
|
6879
|
-
const domainHash = lawClosure.identity;
|
|
6880
7732
|
const lawClosureAssetsPath = path.join(outDir, `${cfg.name}.law`);
|
|
6881
7733
|
const lawUsdzPath = path.join(outDir, `${cfg.name}.law.usdz`);
|
|
6882
7734
|
rmSync(path.join(outDir, `${cfg.name}.law-closure.usda`), { force: true });
|
|
@@ -6884,13 +7736,166 @@ async function main(): Promise<void> {
|
|
|
6884
7736
|
recursive: true,
|
|
6885
7737
|
force: true,
|
|
6886
7738
|
});
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
const
|
|
6891
|
-
|
|
6892
|
-
|
|
7739
|
+
const writeClosureAssets = (closure: EmittedLawClosure): void => {
|
|
7740
|
+
rmSync(lawClosureAssetsPath, { recursive: true, force: true });
|
|
7741
|
+
mkdirSync(lawClosureAssetsPath, { recursive: true });
|
|
7742
|
+
for (const asset of closure.assets) {
|
|
7743
|
+
const assetPath = path.join(lawClosureAssetsPath, asset.logicalPath);
|
|
7744
|
+
mkdirSync(path.dirname(assetPath), { recursive: true });
|
|
7745
|
+
writeFileSync(assetPath, asset.bytes);
|
|
7746
|
+
}
|
|
7747
|
+
};
|
|
7748
|
+
writeClosureAssets(lawClosure);
|
|
7749
|
+
|
|
7750
|
+
// Package standard OpenUSD finished goods once. Sparse USDA remains the authored, inspectable
|
|
7751
|
+
// source law. The full USDC layer is its reversible compiled form for one-time installation;
|
|
7752
|
+
// the smaller read projection is the population-masked form used for ordinary reopen. Both are
|
|
7753
|
+
// covered by the final closure inventory, so a host can never substitute an unbound cache.
|
|
7754
|
+
const usdcat = resolveUsdcat();
|
|
7755
|
+
let compiledReadProjection = false;
|
|
7756
|
+
let compiledCommandProjection = false;
|
|
7757
|
+
if (usdcat !== undefined) {
|
|
7758
|
+
const provisionalRootPath = path.join(
|
|
7759
|
+
lawClosureAssetsPath,
|
|
7760
|
+
lawClosure.rootLogicalPath
|
|
7761
|
+
);
|
|
7762
|
+
let composedLawText: string;
|
|
7763
|
+
try {
|
|
7764
|
+
composedLawText = await flattenOpenUsdToUsda({
|
|
7765
|
+
usdcat,
|
|
7766
|
+
sourcePath: provisionalRootPath,
|
|
7767
|
+
description: "the complete authenticated law stage",
|
|
7768
|
+
});
|
|
7769
|
+
} catch (error) {
|
|
7770
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
7771
|
+
}
|
|
7772
|
+
// The composed stage feeds three independent usdcat pipelines — its own USDC encoding, and the
|
|
7773
|
+
// read and command projections (a masked flatten, then an encoding). Each step is a separate
|
|
7774
|
+
// stock-OpenUSD process with its own temporary files, so run the pipelines concurrently.
|
|
7775
|
+
const temporaryComposedLaw = path.join(
|
|
7776
|
+
outDir,
|
|
7777
|
+
".nomos-composed-law.mask-source.usda"
|
|
7778
|
+
);
|
|
7779
|
+
let composedLawUsdc: Uint8Array;
|
|
7780
|
+
let readProjectionText: string;
|
|
7781
|
+
let readProjectionUsdc: Uint8Array;
|
|
7782
|
+
let commandProjectionText: string;
|
|
7783
|
+
let commandProjectionUsdc: Uint8Array;
|
|
7784
|
+
try {
|
|
7785
|
+
writeFileSync(temporaryComposedLaw, composedLawText);
|
|
7786
|
+
const project = async (
|
|
7787
|
+
populationMask: readonly string[],
|
|
7788
|
+
temporaryStem: string,
|
|
7789
|
+
description: string
|
|
7790
|
+
): Promise<[string, Uint8Array]> => {
|
|
7791
|
+
const usda = await flattenOpenUsdToUsda({
|
|
7792
|
+
usdcat,
|
|
7793
|
+
sourcePath: temporaryComposedLaw,
|
|
7794
|
+
populationMask,
|
|
7795
|
+
description,
|
|
7796
|
+
});
|
|
7797
|
+
return [
|
|
7798
|
+
usda,
|
|
7799
|
+
await encodeOpenUsdUsdc({
|
|
7800
|
+
usdcat,
|
|
7801
|
+
usda,
|
|
7802
|
+
temporaryDirectory: outDir,
|
|
7803
|
+
temporaryStem,
|
|
7804
|
+
description,
|
|
7805
|
+
}),
|
|
7806
|
+
];
|
|
7807
|
+
};
|
|
7808
|
+
[
|
|
7809
|
+
composedLawUsdc,
|
|
7810
|
+
[readProjectionText, readProjectionUsdc],
|
|
7811
|
+
[commandProjectionText, commandProjectionUsdc],
|
|
7812
|
+
] = await Promise.all([
|
|
7813
|
+
encodeOpenUsdUsdc({
|
|
7814
|
+
usdcat,
|
|
7815
|
+
usda: composedLawText,
|
|
7816
|
+
temporaryDirectory: outDir,
|
|
7817
|
+
temporaryStem: "nomos-composed-law",
|
|
7818
|
+
description: "the complete authenticated law stage",
|
|
7819
|
+
}),
|
|
7820
|
+
project(
|
|
7821
|
+
[...readProjectionPaths],
|
|
7822
|
+
"nomos-read-projection",
|
|
7823
|
+
"the authenticated read projection"
|
|
7824
|
+
),
|
|
7825
|
+
project(
|
|
7826
|
+
[...commandProjectionPaths],
|
|
7827
|
+
"nomos-command-projection",
|
|
7828
|
+
"the authenticated command projection"
|
|
7829
|
+
),
|
|
7830
|
+
]);
|
|
7831
|
+
} catch (error) {
|
|
7832
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
7833
|
+
} finally {
|
|
7834
|
+
rmSync(temporaryComposedLaw, { force: true });
|
|
7835
|
+
}
|
|
7836
|
+
const composedLawCache: EmittedBinaryLawClosureAsset = {
|
|
7837
|
+
logicalPath: "runtime/composed-law.usdc",
|
|
7838
|
+
sha256: createHash("sha256").update(composedLawUsdc).digest("hex"),
|
|
7839
|
+
bytes: composedLawUsdc,
|
|
7840
|
+
};
|
|
7841
|
+
const readProjection: EmittedLawClosureAsset = {
|
|
7842
|
+
logicalPath: "runtime/read-projection.usda",
|
|
7843
|
+
sha256: sha256HexUtf8(readProjectionText),
|
|
7844
|
+
utf8: readProjectionText,
|
|
7845
|
+
};
|
|
7846
|
+
// Unlike the command acceleration view, the read projection is a complete nominal contract:
|
|
7847
|
+
// it must be able to encode every declared result without consulting the full package. Catch a
|
|
7848
|
+
// missing transitive value type at compile time rather than during an application's first read.
|
|
7849
|
+
assertUsdaRelationshipIntegrity([readProjection]);
|
|
7850
|
+
// USDA remains the canonical, inspectable derived layer. USDC is the OpenUSD-standard parsed
|
|
7851
|
+
// cache of those exact bytes, produced by the same stock tool during package compilation. Every
|
|
7852
|
+
// Nomos host executes the same WASM kernel containing mxpv/openusd, so this cache crosses no
|
|
7853
|
+
// language boundary and requires no second runtime implementation.
|
|
7854
|
+
const readProjectionCache: EmittedBinaryLawClosureAsset = {
|
|
7855
|
+
logicalPath: "runtime/read-projection.usdc",
|
|
7856
|
+
sha256: createHash("sha256").update(readProjectionUsdc).digest("hex"),
|
|
7857
|
+
bytes: readProjectionUsdc,
|
|
7858
|
+
};
|
|
7859
|
+
const commandProjection: EmittedLawClosureAsset = {
|
|
7860
|
+
logicalPath: "runtime/command-projection.usda",
|
|
7861
|
+
sha256: sha256HexUtf8(commandProjectionText),
|
|
7862
|
+
utf8: commandProjectionText,
|
|
7863
|
+
};
|
|
7864
|
+
const commandProjectionCache: EmittedBinaryLawClosureAsset = {
|
|
7865
|
+
logicalPath: "runtime/command-projection.usdc",
|
|
7866
|
+
sha256: createHash("sha256").update(commandProjectionUsdc).digest("hex"),
|
|
7867
|
+
bytes: commandProjectionUsdc,
|
|
7868
|
+
};
|
|
7869
|
+
lawClosure = emitLawClosure(
|
|
7870
|
+
cfg.name,
|
|
7871
|
+
domainModules.map((module) => module.domain ?? module.name),
|
|
7872
|
+
runtimeDependencies,
|
|
7873
|
+
v3DomainAssets,
|
|
7874
|
+
sharedRuntimeAsset,
|
|
7875
|
+
routedPlanAssets,
|
|
7876
|
+
planRoutes,
|
|
7877
|
+
[
|
|
7878
|
+
composedLawCache,
|
|
7879
|
+
readProjection,
|
|
7880
|
+
readProjectionCache,
|
|
7881
|
+
commandProjection,
|
|
7882
|
+
commandProjectionCache,
|
|
7883
|
+
]
|
|
7884
|
+
);
|
|
7885
|
+
writeClosureAssets(lawClosure);
|
|
7886
|
+
compiledReadProjection = true;
|
|
7887
|
+
compiledCommandProjection = true;
|
|
7888
|
+
}
|
|
7889
|
+
|
|
7890
|
+
stage("law closure + usdcat projections");
|
|
7891
|
+
{
|
|
7892
|
+
const probeFailure = await bootProbeFailure;
|
|
7893
|
+
if (probeFailure !== undefined) fail(probeFailure);
|
|
6893
7894
|
}
|
|
7895
|
+
stage("boot probe (verdict)");
|
|
7896
|
+
// The composed USDA closure is the law. Generated clients, lifecycle rows and
|
|
7897
|
+
// compatibility metadata all bind this identity, never the retired flattened envelope.
|
|
7898
|
+
const domainHash = lawClosure.identity;
|
|
6894
7899
|
const rootAsset = lawClosure.assets.find(
|
|
6895
7900
|
(asset) => asset.logicalPath === lawClosure.rootLogicalPath
|
|
6896
7901
|
);
|
|
@@ -6903,6 +7908,33 @@ async function main(): Promise<void> {
|
|
|
6903
7908
|
.map((asset) => [`layers/${asset.logicalPath}`, asset.bytes] as const),
|
|
6904
7909
|
]);
|
|
6905
7910
|
writeFileSync(lawUsdzPath, lawUsdz);
|
|
7911
|
+
// The compiled law's identity on its own line, for release gates that need the hash and nothing else.
|
|
7912
|
+
writeFileSync(path.join(outDir, `${cfg.name}.law.sha256`), `${domainHash}\n`, "utf8");
|
|
7913
|
+
// D21: the same law, authored as per-domain units the kernel composes. Split from the closure just
|
|
7914
|
+
// emitted (its bytes and identity are untouched), previewed for exact coverage, and written as plain
|
|
7915
|
+
// trees: in custody each unit is a git tree, so git owns compression and deduplication.
|
|
7916
|
+
{
|
|
7917
|
+
const lawUnits = (() => {
|
|
7918
|
+
try {
|
|
7919
|
+
const units = splitLawUnits(lawClosure.assets, planRoutes);
|
|
7920
|
+
assertLawUnitsCoverPackage(units, lawClosure.assets, planRoutes);
|
|
7921
|
+
return units;
|
|
7922
|
+
} catch (error) {
|
|
7923
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
7924
|
+
}
|
|
7925
|
+
})();
|
|
7926
|
+
const unitsPath = path.join(outDir, `${cfg.name}.units`);
|
|
7927
|
+
rmSync(unitsPath, { recursive: true, force: true });
|
|
7928
|
+
for (const unit of lawUnits) {
|
|
7929
|
+
const unitPath = path.join(unitsPath, lawUnitDirectoryName(unit));
|
|
7930
|
+
for (const asset of unit.assets) {
|
|
7931
|
+
const assetPath = path.join(unitPath, asset.logicalPath);
|
|
7932
|
+
mkdirSync(path.dirname(assetPath), { recursive: true });
|
|
7933
|
+
writeFileSync(assetPath, asset.bytes);
|
|
7934
|
+
}
|
|
7935
|
+
writeFileSync(path.join(unitPath, LAW_UNIT_MANIFEST), unit.manifest);
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
6906
7938
|
const layeredRootPath = path.join(
|
|
6907
7939
|
lawClosureAssetsPath,
|
|
6908
7940
|
lawClosure.rootLogicalPath
|
|
@@ -6911,7 +7943,6 @@ async function main(): Promise<void> {
|
|
|
6911
7943
|
let usdcatFlattenVerified = false;
|
|
6912
7944
|
let usdcatFlattenCached = false;
|
|
6913
7945
|
const composedUsdIdentity = domainHash;
|
|
6914
|
-
const usdcat = resolveUsdcat();
|
|
6915
7946
|
if (usdcat === undefined) {
|
|
6916
7947
|
if (requireUsdcat) {
|
|
6917
7948
|
fail(
|
|
@@ -6970,6 +8001,7 @@ async function main(): Promise<void> {
|
|
|
6970
8001
|
}
|
|
6971
8002
|
}
|
|
6972
8003
|
|
|
8004
|
+
stage("usdz pack + usdcat load proof");
|
|
6973
8005
|
// ── 2c. THE PUBLISHED OPENUSD PACKAGE ASSET ──
|
|
6974
8006
|
// `rootPart` is the explicit package/application boundary from the OpenUSD composition ADR.
|
|
6975
8007
|
// The asset references the REAL authored layer stack. It deliberately contains neither the
|
|
@@ -7011,7 +8043,7 @@ async function main(): Promise<void> {
|
|
|
7011
8043
|
}
|
|
7012
8044
|
}
|
|
7013
8045
|
|
|
7014
|
-
// ── 3. typed per-domain identity (diagnostic/
|
|
8046
|
+
// ── 3. typed per-domain identity (diagnostic/peer-office compatibility metadata) ──
|
|
7015
8047
|
// A domain's identity is the digest of its canonical sparse USDA layer. Do not rebuild the retired
|
|
7016
8048
|
// canonical JSON manifest merely to print or embed a second, competing identity.
|
|
7017
8049
|
const identityHashes: Record<string, string> = {};
|
|
@@ -7023,13 +8055,9 @@ async function main(): Promise<void> {
|
|
|
7023
8055
|
identityHashes[module.domain ?? module.name] = hash;
|
|
7024
8056
|
}
|
|
7025
8057
|
|
|
7026
|
-
// ── 3a.
|
|
7027
|
-
//
|
|
7028
|
-
//
|
|
7029
|
-
// block is baked into both generated clients (TS + Dart) and mirrored into the deploy JSON
|
|
7030
|
-
// so a connect() can classify silent / shape-compat / incompatible. FRONTEND-ONLY: it is
|
|
7031
|
-
// emitted like the dart codegen hints — it does NOT enter the USD IR / package bytes /
|
|
7032
|
-
// domainHash (the package `text` above is already hashed and does not move).
|
|
8058
|
+
// ── 3a. CLIENT/LAW compatibility is emitted solely as a USDA matrix from the
|
|
8059
|
+
// canonical compiled interface graph by each language generator. There is deliberately
|
|
8060
|
+
// no parallel JSON/hash manifest: the kernel compares that matrix with authenticated law.
|
|
7033
8061
|
const dslVersion = ((): string => {
|
|
7034
8062
|
try {
|
|
7035
8063
|
return (
|
|
@@ -7040,12 +8068,6 @@ async function main(): Promise<void> {
|
|
|
7040
8068
|
return "unknown";
|
|
7041
8069
|
}
|
|
7042
8070
|
})();
|
|
7043
|
-
const compatDomains: NomosCompat["domains"] = {};
|
|
7044
|
-
for (const m of presentationDomainModules) {
|
|
7045
|
-
const key = m.domain ?? m.name;
|
|
7046
|
-
const identityHash = identityHashes[key];
|
|
7047
|
-
compatDomains[key] = identityHash === undefined ? {} : { identityHash };
|
|
7048
|
-
}
|
|
7049
8071
|
// RELEASE COHERENCE — typed OSGi Contract Namespace requirements derived from law shape.
|
|
7050
8072
|
const kernelContracts = (() => {
|
|
7051
8073
|
const probes: KernelContractProbeModule[] = domainModules.map((m) => ({
|
|
@@ -7058,14 +8080,6 @@ async function main(): Promise<void> {
|
|
|
7058
8080
|
}));
|
|
7059
8081
|
return requiredKernelContracts(probes);
|
|
7060
8082
|
})();
|
|
7061
|
-
const nomosCompat: NomosCompat = {
|
|
7062
|
-
packageName: cfg.name,
|
|
7063
|
-
lawHash: domainHash,
|
|
7064
|
-
interfaceHash: sha256HexUtf8(canonicalJson(packageInterface)),
|
|
7065
|
-
domains: compatDomains,
|
|
7066
|
-
generator: { dsl: dslVersion, schema: 1 },
|
|
7067
|
-
kernelContracts,
|
|
7068
|
-
};
|
|
7069
8083
|
|
|
7070
8084
|
// ── 3b. NO OPENAPI SIDECAR. OpenAPI remains the sanctioned standard for describing this package's
|
|
7071
8085
|
// API — but it is SERVED, not stored: the kernel derives it from the installed typed USDA IR
|
|
@@ -7090,78 +8104,26 @@ async function main(): Promise<void> {
|
|
|
7090
8104
|
identityHash: identityHashes[m.domain ?? m.name],
|
|
7091
8105
|
})),
|
|
7092
8106
|
};
|
|
7093
|
-
// ── 4b. the
|
|
7094
|
-
//
|
|
7095
|
-
//
|
|
7096
|
-
// synthesizer can't sample yet still compiles — the skip names its remedy.
|
|
7097
|
-
let proofPath: string | undefined;
|
|
7098
|
-
let proofSkip: string | undefined;
|
|
7099
|
-
let proofDomains: string[] = [];
|
|
8107
|
+
// ── 4b. BIRTHS DX — the per-domain legs index (codegen_proof.ts) is the compiler's audit of every
|
|
8108
|
+
// creating directive: a `.births()` directive that can't be proven says WHY here — never a
|
|
8109
|
+
// silent skip. (The runnable `<name>.proof.mts` this once emitted is gone with its proof cloud.)
|
|
7100
8110
|
let birthWarnings: string[] = [];
|
|
7101
8111
|
let proofNotes: string[] = [];
|
|
7102
8112
|
try {
|
|
7103
|
-
const
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
interface: packageInterface,
|
|
7107
|
-
...(cfg.application?.maxChildWorkspacesPerUser === undefined
|
|
7108
|
-
? {}
|
|
7109
|
-
: {
|
|
7110
|
-
applicationMaxChildWorkspacesPerUser:
|
|
7111
|
-
cfg.application.maxChildWorkspacesPerUser,
|
|
7112
|
-
}),
|
|
7113
|
-
});
|
|
7114
|
-
proofPath = path.join(outDir, `${cfg.name}.proof.mts`);
|
|
7115
|
-
writeFileSync(proofPath, proofSrc, "utf8");
|
|
7116
|
-
// THE PER-DOMAIN LEGS INDEX: every domain with a creating write, so
|
|
7117
|
-
// `githolon proof --domain <key>` proves ANY of them offline (the .mts
|
|
7118
|
-
// proves the primary one live). A multi-domain package no longer locks the
|
|
7119
|
-
// proof to whichever domain happens to be first.
|
|
7120
|
-
const legsIndex = proofLegsIndex(
|
|
7121
|
-
presentationDomainModules,
|
|
7122
|
-
packageInterface
|
|
7123
|
-
);
|
|
7124
|
-
if (legsIndex !== null) {
|
|
7125
|
-
writeFileSync(
|
|
7126
|
-
path.join(outDir, `${cfg.name}.proof-legs.json`),
|
|
7127
|
-
JSON.stringify(legsIndex, null, 2) + "\n",
|
|
7128
|
-
"utf8"
|
|
7129
|
-
);
|
|
7130
|
-
proofDomains = Object.keys(legsIndex.domains);
|
|
7131
|
-
birthWarnings = Object.values(legsIndex.domains).flatMap(
|
|
8113
|
+
const idx = proofLegsIndex(presentationDomainModules, packageInterface);
|
|
8114
|
+
if (idx !== null) {
|
|
8115
|
+
birthWarnings = Object.values(idx.domains).flatMap(
|
|
7132
8116
|
(l) => l.birthDiagnostics ?? []
|
|
7133
8117
|
);
|
|
7134
|
-
|
|
7135
|
-
// failure / attestation lane / gated-create authority gap) — never a silent drop.
|
|
7136
|
-
proofNotes = Object.values(legsIndex.domains).flatMap(
|
|
8118
|
+
proofNotes = Object.values(idx.domains).flatMap(
|
|
7137
8119
|
(l) => l.proofDiagnostics ?? []
|
|
7138
8120
|
);
|
|
7139
8121
|
}
|
|
7140
|
-
} catch
|
|
7141
|
-
|
|
7142
|
-
// A skipped generation must not leave yesterday's client story in build/. Otherwise editors and CI
|
|
7143
|
-
// typecheck a stale proof which no longer describes the compiled law — worse than having no proof.
|
|
7144
|
-
rmSync(path.join(outDir, `${cfg.name}.proof.mts`), { force: true });
|
|
7145
|
-
rmSync(path.join(outDir, `${cfg.name}.proof-legs.json`), { force: true });
|
|
7146
|
-
}
|
|
7147
|
-
// BIRTHS DX: a `.births()` directive that can't be (or can only shakily be) proven says WHY here —
|
|
7148
|
-
// never a silent skip, never an opaque runtime `born=[]` first contact. When the .mts generation
|
|
7149
|
-
// itself failed, still derive the per-domain diagnostics (best-effort).
|
|
7150
|
-
if (proofSkip !== undefined && birthWarnings.length === 0) {
|
|
7151
|
-
try {
|
|
7152
|
-
const idx = proofLegsIndex(presentationDomainModules, packageInterface);
|
|
7153
|
-
if (idx !== null) {
|
|
7154
|
-
birthWarnings = Object.values(idx.domains).flatMap(
|
|
7155
|
-
(l) => l.birthDiagnostics ?? []
|
|
7156
|
-
);
|
|
7157
|
-
proofNotes = Object.values(idx.domains).flatMap(
|
|
7158
|
-
(l) => l.proofDiagnostics ?? []
|
|
7159
|
-
);
|
|
7160
|
-
}
|
|
7161
|
-
} catch {
|
|
7162
|
-
/* diagnostics are best-effort */
|
|
7163
|
-
}
|
|
8122
|
+
} catch {
|
|
8123
|
+
/* diagnostics are best-effort */
|
|
7164
8124
|
}
|
|
8125
|
+
rmSync(path.join(outDir, `${cfg.name}.proof.mts`), { force: true });
|
|
8126
|
+
rmSync(path.join(outDir, `${cfg.name}.proof-legs.json`), { force: true });
|
|
7165
8127
|
|
|
7166
8128
|
const summaryTxt = [
|
|
7167
8129
|
`${cfg.name} — compiled Nomos domain package`,
|
|
@@ -7172,6 +8134,9 @@ async function main(): Promise<void> {
|
|
|
7172
8134
|
`canonical law: ${path.basename(
|
|
7173
8135
|
lawClosureAssetsPath
|
|
7174
8136
|
)}/ # exact logically named, human-readable USDA`,
|
|
8137
|
+
`client compatibility: ${path.basename(
|
|
8138
|
+
compatibilityUsdaPath
|
|
8139
|
+
)} # opaque USDA requirement; the kernel compares it with installed law`,
|
|
7175
8140
|
...summary.domains.flatMap((d) => [
|
|
7176
8141
|
`domain '${d.domain}'${
|
|
7177
8142
|
d.identityHash
|
|
@@ -7190,14 +8155,11 @@ async function main(): Promise<void> {
|
|
|
7190
8155
|
packageAssetPath
|
|
7191
8156
|
)} — the real USD package root consumed by application composition`
|
|
7192
8157
|
: `package asset: NOT EMITTED — declare rootPart: { name, kind: "component" | "assembly" }`,
|
|
7193
|
-
`typed application: ${cfg.name}.
|
|
8158
|
+
`typed application: ${cfg.name}.reception.mjs — bind${cfg.name
|
|
7194
8159
|
.split(/[^A-Za-z0-9]+/)
|
|
7195
8160
|
.filter(Boolean)
|
|
7196
8161
|
.map((part) => part[0]!.toUpperCase() + part.slice(1))
|
|
7197
|
-
.join("")}Application(
|
|
7198
|
-
proofPath !== undefined
|
|
7199
|
-
? `generated proof: ${cfg.name}.proof.mts — fresh: githolon proof; existing custody: githolon proof --upgrade`
|
|
7200
|
-
: `generated proof: SKIPPED — ${proofSkip}`,
|
|
8162
|
+
.join("")}Application(peerOfficeBinding)`,
|
|
7201
8163
|
...(priorLock === undefined
|
|
7202
8164
|
? []
|
|
7203
8165
|
: [
|
|
@@ -7221,6 +8183,10 @@ async function main(): Promise<void> {
|
|
|
7221
8183
|
domainHash,
|
|
7222
8184
|
artifacts: [
|
|
7223
8185
|
{ name: path.basename(lawUsdzPath) },
|
|
8186
|
+
{
|
|
8187
|
+
name: path.basename(compatibilityUsdaPath),
|
|
8188
|
+
sha256: createHash("sha256").update(compatibilityUsda).digest("hex"),
|
|
8189
|
+
},
|
|
7224
8190
|
...(packageAssetPath === undefined
|
|
7225
8191
|
? []
|
|
7226
8192
|
: [{ name: path.basename(packageAssetPath) }]),
|
|
@@ -7245,20 +8211,15 @@ async function main(): Promise<void> {
|
|
|
7245
8211
|
"utf8"
|
|
7246
8212
|
);
|
|
7247
8213
|
|
|
8214
|
+
stage("package asset + identity + summary");
|
|
7248
8215
|
// ── 5. the typed TS client (#10 — the web sibling of codegen_dart), with the
|
|
7249
8216
|
// deployed law's content hash baked in (emitted AFTER the package is hashed) ──
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
);
|
|
7257
|
-
const operatorPath = path.join(outDir, `${cfg.name}.operator.mjs`);
|
|
7258
|
-
const operatorDeclarationPath = path.join(
|
|
7259
|
-
outDir,
|
|
7260
|
-
`${cfg.name}.operator.d.mts`
|
|
7261
|
-
);
|
|
8217
|
+
// ONE TypeScript Reception per application: the typed surface tenant code binds
|
|
8218
|
+
// (`receive<Name>Application`) and, in the same file, the holon-level factories a code-mode or
|
|
8219
|
+
// unattended peer binds to any NomosHolon. It was two artifacts (".client" + ".headless") that
|
|
8220
|
+
// differed only in whether those factories were exported.
|
|
8221
|
+
const clientPath = path.join(outDir, `${cfg.name}.reception.mjs`);
|
|
8222
|
+
const clientDeclarationPath = path.join(outDir, `${cfg.name}.reception.d.mts`);
|
|
7262
8223
|
const frameworkOperatorPath = path.join(outDir, `${cfg.name}.framework.mjs`);
|
|
7263
8224
|
const frameworkOperatorDeclarationPath = path.join(
|
|
7264
8225
|
outDir,
|
|
@@ -7273,7 +8234,7 @@ async function main(): Promise<void> {
|
|
|
7273
8234
|
domainHash,
|
|
7274
8235
|
lawAssetName: `${cfg.name}.law.usdz`,
|
|
7275
8236
|
interface: packageInterface,
|
|
7276
|
-
|
|
8237
|
+
kernelContracts,
|
|
7277
8238
|
homeDomain: resolveApplicationTopology(presentationDomainModules)
|
|
7278
8239
|
.homeDomain,
|
|
7279
8240
|
callUsda: [...typedLawGraphs].map(([name, graph]) => {
|
|
@@ -7283,84 +8244,94 @@ async function main(): Promise<void> {
|
|
|
7283
8244
|
return { graph, paths };
|
|
7284
8245
|
}),
|
|
7285
8246
|
} as const;
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
8247
|
+
// Both language clients are generated from this one composed application
|
|
8248
|
+
// schema. The TypeScript binding is bundled into the application client;
|
|
8249
|
+
// Dart's generated binding is emitted into its package below.
|
|
8250
|
+
await tsOperationProtoPrewarm;
|
|
8251
|
+
emitTsOperationProto(
|
|
8252
|
+
cfgDir,
|
|
8253
|
+
outDir,
|
|
8254
|
+
lawUsdzPath,
|
|
8255
|
+
[
|
|
8256
|
+
...new Map(
|
|
8257
|
+
[...closedDomainModules, ...presentationDomainModules].map(
|
|
8258
|
+
(module) => [module.name, module] as const
|
|
8259
|
+
)
|
|
8260
|
+
).values(),
|
|
8261
|
+
],
|
|
8262
|
+
typedLawGraphs,
|
|
8263
|
+
typedLawPathsByName
|
|
7289
8264
|
);
|
|
7290
|
-
const
|
|
8265
|
+
const clientSource = generateTsClient(presentationDomainModules, {
|
|
7291
8266
|
...clientOptions,
|
|
7292
8267
|
exportHolonFactory: true,
|
|
7293
8268
|
});
|
|
7294
|
-
const operatorSource = generateTsLifecycleOperator(clientOptions.callUsda);
|
|
7295
8269
|
const frameworkOperatorSource = generateTsClient(presentationDomainModules, {
|
|
7296
8270
|
...clientOptions,
|
|
7297
8271
|
frameworkOperatorOnly: true,
|
|
7298
8272
|
});
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
8273
|
+
// Generated bundles consume the compiler's declared Protobuf runtime. Do not rely on
|
|
8274
|
+
// npm incidentally hoisting @bufbuild/protobuf into the tenant package: linked packages,
|
|
8275
|
+
// pnpm and isolated installs are all entitled to use a different node_modules layout.
|
|
8276
|
+
const compilerRequire = createRequire(import.meta.url);
|
|
8277
|
+
const protobufEntry = compilerRequire.resolve("@bufbuild/protobuf");
|
|
8278
|
+
const protobufCodegenEntry = compilerRequire.resolve(
|
|
8279
|
+
"@bufbuild/protobuf/codegenv2"
|
|
8280
|
+
);
|
|
8281
|
+
const protobufTypes = protobufEntry.replace(/\.js$/, ".d.ts");
|
|
8282
|
+
const protobufCodegenTypes = protobufCodegenEntry.replace(/\.js$/, ".d.ts");
|
|
8283
|
+
const nodeModulesMarker = `${path.sep}node_modules${path.sep}`;
|
|
8284
|
+
const nodeModulesIndex = protobufEntry.lastIndexOf(nodeModulesMarker);
|
|
8285
|
+
if (nodeModulesIndex < 0) {
|
|
8286
|
+
fail(`@bufbuild/protobuf resolved outside node_modules: ${protobufEntry}`);
|
|
8287
|
+
}
|
|
8288
|
+
const compilerNodeModules = protobufEntry.slice(
|
|
8289
|
+
0,
|
|
8290
|
+
nodeModulesIndex + nodeModulesMarker.length - 1
|
|
7303
8291
|
);
|
|
7304
|
-
|
|
8292
|
+
const protobufRuntimePlugin: import("esbuild").Plugin = {
|
|
8293
|
+
name: "nomos-compiler-protobuf-runtime",
|
|
8294
|
+
setup(build) {
|
|
8295
|
+
build.onResolve({ filter: /^@bufbuild\/protobuf(?:\/.*)?$/ }, (args) => ({
|
|
8296
|
+
path: compilerRequire.resolve(args.path),
|
|
8297
|
+
}));
|
|
8298
|
+
},
|
|
8299
|
+
};
|
|
8300
|
+
let runtimeTypesPath: string;
|
|
8301
|
+
try {
|
|
8302
|
+
runtimeTypesPath = path.join(
|
|
8303
|
+
path.dirname(
|
|
8304
|
+
createRequire(import.meta.url).resolve(
|
|
8305
|
+
"@githolon/runtime/package.json"
|
|
8306
|
+
)
|
|
8307
|
+
),
|
|
8308
|
+
"src",
|
|
8309
|
+
"application_reception.ts"
|
|
8310
|
+
);
|
|
8311
|
+
} catch {
|
|
7305
8312
|
fail(
|
|
7306
|
-
"
|
|
8313
|
+
"generated TypeScript applications require @githolon/runtime so they can link the shared Peer Office runtime"
|
|
7307
8314
|
);
|
|
7308
8315
|
}
|
|
7309
8316
|
// Validate the generated ESM clients as ESM even when the tenant project has no package.json yet.
|
|
7310
8317
|
// NodeNext classifies a plain `.ts` file from the nearest package boundary, so a freshly scaffolded
|
|
7311
|
-
// or fixture-only project would otherwise be treated as CommonJS
|
|
7312
|
-
// `import.meta.url` used to locate its adjacent law asset. The compiler options below describe the
|
|
8318
|
+
// or fixture-only project would otherwise be treated as CommonJS. The compiler options below describe the
|
|
7313
8319
|
// generated `.mjs` runtime contract directly instead of inheriting unrelated tenant metadata.
|
|
7314
8320
|
const temporaryClientPath = path.join(
|
|
7315
8321
|
outDir,
|
|
7316
|
-
`.${cfg.name}.
|
|
7317
|
-
);
|
|
7318
|
-
const temporaryHeadlessClientPath = path.join(
|
|
7319
|
-
outDir,
|
|
7320
|
-
`.${cfg.name}.headless.source.ts`
|
|
7321
|
-
);
|
|
7322
|
-
const temporaryRuntimeTypesPath = path.join(
|
|
7323
|
-
outDir,
|
|
7324
|
-
`.${cfg.name}.application-runtime.d.ts`
|
|
8322
|
+
`.${cfg.name}.reception.source.ts`
|
|
7325
8323
|
);
|
|
7326
8324
|
const temporaryDeclarationPath = temporaryClientPath.replace(
|
|
7327
8325
|
/\.ts$/,
|
|
7328
8326
|
".d.ts"
|
|
7329
8327
|
);
|
|
7330
|
-
const temporaryHeadlessDeclarationPath = temporaryHeadlessClientPath.replace(
|
|
7331
|
-
/\.ts$/,
|
|
7332
|
-
".d.ts"
|
|
7333
|
-
);
|
|
7334
|
-
const runtimeTypesSource = [
|
|
7335
|
-
`export declare function nomosInternalOpenGeneratedApplication<T extends object>(`,
|
|
7336
|
-
` options: { cloud: string; application: string; signIn: (request?: { readonly deviceChallenge?: string }) => Promise<string>; onPerformance?: (sample: { readonly operation: "open" | "offer" | "birth" | "read" | "sync" | "maintenance"; readonly name: string; readonly durationMs: number; readonly domain?: string; readonly directive?: string }) => void; approveModel?: (decision: { readonly summary: string; readonly review: { checkedActions: number; candidateActions: number; sameOutcome: boolean | null; differences: { kind: "rules" | "outcome" | "evidence"; area?: string; action?: string; summary: string }[] } }) => Promise<{ readonly because: string }>; lawHash: string; readyCommands?: readonly { readonly domain: string; readonly packageHash: string; readonly directiveId: string }[]; frameworkCalls: unknown },`,
|
|
7337
|
-
` bind: (holon: unknown, user: { readonly identityId: string; readonly identityAddress: string }) => T,`,
|
|
7338
|
-
`): Promise<T & {`,
|
|
7339
|
-
` currentStatus(): Promise<any>;`,
|
|
7340
|
-
` watchStatus(listener: (status: any) => void): () => void;`,
|
|
7341
|
-
`}>;`,
|
|
7342
|
-
`export declare function nomosInternalOpenGeneratedChildApplication<T extends object>(`,
|
|
7343
|
-
` parent: unknown,`,
|
|
7344
|
-
` workspace: string,`,
|
|
7345
|
-
` bind: (holon: unknown) => T,`,
|
|
7346
|
-
`): Promise<T>;`,
|
|
7347
|
-
`export declare function nomosInternalCreateInvitation(`,
|
|
7348
|
-
` holon: unknown,`,
|
|
7349
|
-
`): Promise<{ readonly address: string; readonly token: string }>;`,
|
|
7350
|
-
`export declare function nomosInternalRedeemInvitation(`,
|
|
7351
|
-
` holon: unknown,`,
|
|
7352
|
-
` link: string,`,
|
|
7353
|
-
` policies: readonly { readonly domain: string; readonly domainHash: string; readonly query: string; readonly redeem: string; readonly personField: string; readonly roleField: string; readonly scopeField: string }[],`,
|
|
7354
|
-
` frameworkCalls: unknown,`,
|
|
7355
|
-
`): Promise<void>;`,
|
|
7356
|
-
``,
|
|
7357
|
-
].join("\n");
|
|
7358
8328
|
// Declaration generation used to ask TypeScript to rebuild and reduce the same 800 KiB public type graph
|
|
7359
8329
|
// on every law compile, even when an implementation-only plan edit left that graph byte-identical. Cache
|
|
7360
8330
|
// the result by the exact generated source, runtime declaration boundary, TypeScript version and pruning
|
|
7361
8331
|
// algorithm. The installed law hash is normalized because it changes runtime compatibility identity but
|
|
7362
8332
|
// has type `string` in every public declaration. A typed USDA proof binds the cached declaration bytes;
|
|
7363
8333
|
// there is no JSON cache manifest and no cached file can become runtime or custody authority.
|
|
8334
|
+
stage("ts client source gen");
|
|
7364
8335
|
const declarationProofCodec = (await import(
|
|
7365
8336
|
"../compile_proof.mjs" as string
|
|
7366
8337
|
)) as {
|
|
@@ -7376,14 +8347,7 @@ async function main(): Promise<void> {
|
|
|
7376
8347
|
.update(`${declarationProofFormat}\0`)
|
|
7377
8348
|
.update(normalizedClientSource)
|
|
7378
8349
|
.update("\0")
|
|
7379
|
-
.update(
|
|
7380
|
-
headlessClientSource.replaceAll(
|
|
7381
|
-
domainHash,
|
|
7382
|
-
"<nomos-composed-law-identity>"
|
|
7383
|
-
)
|
|
7384
|
-
)
|
|
7385
|
-
.update("\0")
|
|
7386
|
-
.update(runtimeTypesSource)
|
|
8350
|
+
.update(readFileSync(runtimeTypesPath))
|
|
7387
8351
|
.update("\0")
|
|
7388
8352
|
.update(ts.version)
|
|
7389
8353
|
.update("\0")
|
|
@@ -7398,22 +8362,14 @@ async function main(): Promise<void> {
|
|
|
7398
8362
|
);
|
|
7399
8363
|
const declarationCachePath = path.join(
|
|
7400
8364
|
declarationCacheDir,
|
|
7401
|
-
`${declarationInputIdentity}.
|
|
7402
|
-
);
|
|
7403
|
-
const headlessDeclarationCachePath = path.join(
|
|
7404
|
-
declarationCacheDir,
|
|
7405
|
-
`${declarationInputIdentity}.headless.d.mts`
|
|
8365
|
+
`${declarationInputIdentity}.reception.d.mts`
|
|
7406
8366
|
);
|
|
7407
8367
|
const declarationCacheProofPath = path.join(
|
|
7408
8368
|
declarationCacheDir,
|
|
7409
|
-
`${declarationInputIdentity}.
|
|
7410
|
-
);
|
|
7411
|
-
const headlessDeclarationCacheProofPath = path.join(
|
|
7412
|
-
declarationCacheDir,
|
|
7413
|
-
`${declarationInputIdentity}.headless.proof.usda`
|
|
8369
|
+
`${declarationInputIdentity}.reception.proof.usda`
|
|
7414
8370
|
);
|
|
7415
8371
|
const declarationProofKey = (
|
|
7416
|
-
artifact: "
|
|
8372
|
+
artifact: "reception",
|
|
7417
8373
|
bytes: Buffer
|
|
7418
8374
|
): string =>
|
|
7419
8375
|
createHash("sha256")
|
|
@@ -7425,29 +8381,18 @@ async function main(): Promise<void> {
|
|
|
7425
8381
|
const cachedDeclaration = existsSync(declarationCachePath)
|
|
7426
8382
|
? readFileSync(declarationCachePath)
|
|
7427
8383
|
: undefined;
|
|
7428
|
-
const cachedHeadlessDeclaration = existsSync(headlessDeclarationCachePath)
|
|
7429
|
-
? readFileSync(headlessDeclarationCachePath)
|
|
7430
|
-
: undefined;
|
|
7431
8384
|
const declarationCacheHit =
|
|
7432
8385
|
cachedDeclaration !== undefined &&
|
|
7433
|
-
cachedHeadlessDeclaration !== undefined &&
|
|
7434
8386
|
declarationProofCodec.readKeyProofUsda(
|
|
7435
8387
|
declarationCacheProofPath,
|
|
7436
8388
|
declarationProofFormat
|
|
7437
|
-
) === declarationProofKey("
|
|
7438
|
-
declarationProofCodec.readKeyProofUsda(
|
|
7439
|
-
headlessDeclarationCacheProofPath,
|
|
7440
|
-
declarationProofFormat
|
|
7441
|
-
) === declarationProofKey("headless", cachedHeadlessDeclaration);
|
|
8389
|
+
) === declarationProofKey("reception", cachedDeclaration);
|
|
7442
8390
|
if (declarationCacheHit) {
|
|
7443
8391
|
copyFileSync(declarationCachePath, clientDeclarationPath);
|
|
7444
|
-
copyFileSync(headlessDeclarationCachePath, headlessClientDeclarationPath);
|
|
7445
8392
|
}
|
|
7446
8393
|
try {
|
|
7447
8394
|
if (!declarationCacheHit) {
|
|
7448
8395
|
writeFileSync(temporaryClientPath, clientSource, "utf8");
|
|
7449
|
-
writeFileSync(temporaryHeadlessClientPath, headlessClientSource, "utf8");
|
|
7450
|
-
writeFileSync(temporaryRuntimeTypesPath, runtimeTypesSource, "utf8");
|
|
7451
8396
|
const typescriptEntry = createRequire(import.meta.url).resolve(
|
|
7452
8397
|
"typescript/lib/typescript.js"
|
|
7453
8398
|
);
|
|
@@ -7455,11 +8400,7 @@ async function main(): Promise<void> {
|
|
|
7455
8400
|
pathToFileURL(typescriptEntry).href
|
|
7456
8401
|
)) as typeof import("typescript");
|
|
7457
8402
|
const program = ts.createProgram(
|
|
7458
|
-
[
|
|
7459
|
-
temporaryClientPath,
|
|
7460
|
-
temporaryHeadlessClientPath,
|
|
7461
|
-
temporaryRuntimeTypesPath,
|
|
7462
|
-
],
|
|
8403
|
+
[temporaryClientPath],
|
|
7463
8404
|
{
|
|
7464
8405
|
target: ts.ScriptTarget.ES2022,
|
|
7465
8406
|
module: ts.ModuleKind.ESNext,
|
|
@@ -7472,14 +8413,28 @@ async function main(): Promise<void> {
|
|
|
7472
8413
|
allowImportingTsExtensions: true,
|
|
7473
8414
|
baseUrl: outDir,
|
|
7474
8415
|
paths: {
|
|
7475
|
-
"@githolon/
|
|
7476
|
-
|
|
7477
|
-
],
|
|
8416
|
+
"@githolon/runtime/application": [runtimeTypesPath],
|
|
8417
|
+
"@bufbuild/protobuf": [protobufTypes],
|
|
8418
|
+
"@bufbuild/protobuf/codegenv2": [protobufCodegenTypes],
|
|
7478
8419
|
},
|
|
7479
8420
|
outDir,
|
|
7480
8421
|
}
|
|
7481
8422
|
);
|
|
7482
|
-
|
|
8423
|
+
// The runtime contract deliberately imports its generated framework types from the client package.
|
|
8424
|
+
// Those are dependencies, not compiler output. Capture only declarations whose source is one of the
|
|
8425
|
+
// two generated application roots; otherwise TypeScript mirrors the client package's source tree under
|
|
8426
|
+
// the tenant build directory and makes a second apparent type system.
|
|
8427
|
+
const emit = program.emit(
|
|
8428
|
+
undefined,
|
|
8429
|
+
(_fileName, data, _writeByteOrderMark, _onError, sourceFiles) => {
|
|
8430
|
+
const sourceNames = (sourceFiles ?? []).map((source) =>
|
|
8431
|
+
path.resolve(source.fileName)
|
|
8432
|
+
);
|
|
8433
|
+
if (sourceNames.includes(path.resolve(temporaryClientPath))) {
|
|
8434
|
+
writeFileSync(temporaryDeclarationPath, data, "utf8");
|
|
8435
|
+
}
|
|
8436
|
+
}
|
|
8437
|
+
);
|
|
7483
8438
|
const diagnostics = [
|
|
7484
8439
|
...ts.getPreEmitDiagnostics(program),
|
|
7485
8440
|
...emit.diagnostics,
|
|
@@ -7487,8 +8442,7 @@ async function main(): Promise<void> {
|
|
|
7487
8442
|
if (
|
|
7488
8443
|
diagnostics.length > 0 ||
|
|
7489
8444
|
emit.emitSkipped ||
|
|
7490
|
-
!existsSync(temporaryDeclarationPath)
|
|
7491
|
-
!existsSync(temporaryHeadlessDeclarationPath)
|
|
8445
|
+
!existsSync(temporaryDeclarationPath)
|
|
7492
8446
|
) {
|
|
7493
8447
|
const detail = diagnostics
|
|
7494
8448
|
.map((diagnostic) => {
|
|
@@ -7706,38 +8660,18 @@ async function main(): Promise<void> {
|
|
|
7706
8660
|
].join("\n");
|
|
7707
8661
|
writeFileSync(temporaryDeclarationPath, declarationSource, "utf8");
|
|
7708
8662
|
renameSync(temporaryDeclarationPath, clientDeclarationPath);
|
|
7709
|
-
renameSync(
|
|
7710
|
-
temporaryHeadlessDeclarationPath,
|
|
7711
|
-
headlessClientDeclarationPath
|
|
7712
|
-
);
|
|
7713
8663
|
mkdirSync(declarationCacheDir, { recursive: true });
|
|
7714
8664
|
const declarationCacheTemporaryPath = `${declarationCachePath}.tmp-${process.pid}`;
|
|
7715
|
-
const headlessDeclarationCacheTemporaryPath = `${headlessDeclarationCachePath}.tmp-${process.pid}`;
|
|
7716
8665
|
copyFileSync(clientDeclarationPath, declarationCacheTemporaryPath);
|
|
7717
|
-
copyFileSync(
|
|
7718
|
-
headlessClientDeclarationPath,
|
|
7719
|
-
headlessDeclarationCacheTemporaryPath
|
|
7720
|
-
);
|
|
7721
8666
|
renameSync(declarationCacheTemporaryPath, declarationCachePath);
|
|
7722
|
-
renameSync(
|
|
7723
|
-
headlessDeclarationCacheTemporaryPath,
|
|
7724
|
-
headlessDeclarationCachePath
|
|
7725
|
-
);
|
|
7726
8667
|
declarationProofCodec.writeKeyProofUsda(
|
|
7727
8668
|
declarationCacheProofPath,
|
|
7728
8669
|
declarationProofFormat,
|
|
7729
|
-
declarationProofKey("
|
|
7730
|
-
);
|
|
7731
|
-
declarationProofCodec.writeKeyProofUsda(
|
|
7732
|
-
headlessDeclarationCacheProofPath,
|
|
7733
|
-
declarationProofFormat,
|
|
7734
|
-
declarationProofKey(
|
|
7735
|
-
"headless",
|
|
7736
|
-
readFileSync(headlessDeclarationCachePath)
|
|
7737
|
-
)
|
|
8670
|
+
declarationProofKey("reception", readFileSync(declarationCachePath))
|
|
7738
8671
|
);
|
|
7739
8672
|
}
|
|
7740
|
-
|
|
8673
|
+
stage("ts declarations");
|
|
8674
|
+
const clientBundle = await esbuildMod.build({
|
|
7741
8675
|
stdin: {
|
|
7742
8676
|
contents: clientSource,
|
|
7743
8677
|
loader: "ts",
|
|
@@ -7745,6 +8679,8 @@ async function main(): Promise<void> {
|
|
|
7745
8679
|
sourcefile: `${cfg.name}.client.ts`,
|
|
7746
8680
|
},
|
|
7747
8681
|
outfile: clientPath,
|
|
8682
|
+
write: false,
|
|
8683
|
+
metafile: true,
|
|
7748
8684
|
bundle: true,
|
|
7749
8685
|
packages: "bundle",
|
|
7750
8686
|
platform: "node",
|
|
@@ -7752,132 +8688,42 @@ async function main(): Promise<void> {
|
|
|
7752
8688
|
target: "node20",
|
|
7753
8689
|
legalComments: "none",
|
|
7754
8690
|
logLevel: "silent",
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
() => ({ path: runtimePath })
|
|
7765
|
-
);
|
|
7766
|
-
},
|
|
7767
|
-
},
|
|
7768
|
-
],
|
|
8691
|
+
nodePaths: [compilerNodeModules],
|
|
8692
|
+
plugins: [protobufRuntimePlugin],
|
|
8693
|
+
external: [APPLICATION_CONTRACT_MODULE],
|
|
8694
|
+
});
|
|
8695
|
+
assertGeneratedClientBundleBoundary(clientBundle.metafile, {
|
|
8696
|
+
workingDirectory: process.cwd(),
|
|
8697
|
+
generatedEntry: path.join(outDir, `${cfg.name}.client.ts`),
|
|
8698
|
+
operationCodec: path.join(outDir, "operation_payloads_pb.ts"),
|
|
8699
|
+
protobufPackageRoot: protobufPackageRoot(protobufEntry),
|
|
7769
8700
|
});
|
|
7770
8701
|
const bundled = canonicalizeGeneratedClientJavascript(
|
|
7771
|
-
|
|
8702
|
+
clientBundle.outputFiles[0]!.text
|
|
7772
8703
|
);
|
|
7773
8704
|
writeFileSync(clientPath, bundled, "utf8");
|
|
7774
8705
|
if (
|
|
7775
|
-
|
|
7776
|
-
/export\s*\{[^}]*nomosInternalOpenGeneratedApplication/s.test(bundled)
|
|
8706
|
+
!/^import\s+.*["']@githolon\/runtime\/application["']/m.test(bundled)
|
|
7777
8707
|
) {
|
|
7778
8708
|
fail(
|
|
7779
|
-
"generated TypeScript application did not
|
|
8709
|
+
"generated TypeScript application did not link the shared Peer Office runtime"
|
|
7780
8710
|
);
|
|
7781
8711
|
}
|
|
7782
|
-
await esbuildMod.build({
|
|
7783
|
-
stdin: {
|
|
7784
|
-
contents: headlessClientSource,
|
|
7785
|
-
loader: "ts",
|
|
7786
|
-
resolveDir: outDir,
|
|
7787
|
-
sourcefile: `${cfg.name}.headless.ts`,
|
|
7788
|
-
},
|
|
7789
|
-
outfile: headlessClientPath,
|
|
7790
|
-
bundle: true,
|
|
7791
|
-
packages: "bundle",
|
|
7792
|
-
platform: "node",
|
|
7793
|
-
format: "esm",
|
|
7794
|
-
target: "node20",
|
|
7795
|
-
legalComments: "none",
|
|
7796
|
-
logLevel: "silent",
|
|
7797
|
-
plugins: [
|
|
7798
|
-
{
|
|
7799
|
-
name: "nomos-generated-headless-runtime",
|
|
7800
|
-
setup(build) {
|
|
7801
|
-
build.onResolve(
|
|
7802
|
-
{
|
|
7803
|
-
filter:
|
|
7804
|
-
/^@githolon\/client\/_generated_application_host_7c1f5e$/,
|
|
7805
|
-
},
|
|
7806
|
-
() => ({ path: runtimePath })
|
|
7807
|
-
);
|
|
7808
|
-
},
|
|
7809
|
-
},
|
|
7810
|
-
],
|
|
7811
|
-
});
|
|
7812
|
-
const headlessBundled = canonicalizeGeneratedClientJavascript(
|
|
7813
|
-
readFileSync(headlessClientPath, "utf8")
|
|
7814
|
-
);
|
|
7815
|
-
writeFileSync(headlessClientPath, headlessBundled, "utf8");
|
|
7816
8712
|
if (
|
|
7817
8713
|
!new RegExp(
|
|
7818
8714
|
`export\\s*\\{[^}]*${tsClientFactoryName(
|
|
7819
8715
|
resolveApplicationTopology(presentationDomainModules).homeDomain
|
|
7820
8716
|
)}`,
|
|
7821
8717
|
"s"
|
|
7822
|
-
).test(
|
|
8718
|
+
).test(bundled)
|
|
7823
8719
|
) {
|
|
7824
|
-
fail(
|
|
7825
|
-
"generated headless application omitted its structural holon factory"
|
|
7826
|
-
);
|
|
7827
|
-
}
|
|
7828
|
-
if (operatorSource !== undefined) {
|
|
7829
|
-
const operatorDeclaration = ts.transpileDeclaration(operatorSource, {
|
|
7830
|
-
fileName: `${cfg.name}.operator.ts`,
|
|
7831
|
-
compilerOptions: {
|
|
7832
|
-
target: ts.ScriptTarget.ES2022,
|
|
7833
|
-
module: ts.ModuleKind.ESNext,
|
|
7834
|
-
strict: true,
|
|
7835
|
-
},
|
|
7836
|
-
reportDiagnostics: true,
|
|
7837
|
-
});
|
|
7838
|
-
const operatorErrors = (operatorDeclaration.diagnostics ?? []).filter(
|
|
7839
|
-
(diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error
|
|
7840
|
-
);
|
|
7841
|
-
if (operatorErrors.length > 0) {
|
|
7842
|
-
fail(
|
|
7843
|
-
`generated TypeScript lifecycle operator declarations are invalid — this is a Nomos codegen bug:\n${operatorErrors
|
|
7844
|
-
.map((diagnostic) =>
|
|
7845
|
-
ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
|
|
7846
|
-
)
|
|
7847
|
-
.join("\n")}`
|
|
7848
|
-
);
|
|
7849
|
-
}
|
|
7850
|
-
writeFileSync(
|
|
7851
|
-
operatorDeclarationPath,
|
|
7852
|
-
operatorDeclaration.outputText,
|
|
7853
|
-
"utf8"
|
|
7854
|
-
);
|
|
7855
|
-
await esbuildMod.build({
|
|
7856
|
-
stdin: {
|
|
7857
|
-
contents: operatorSource,
|
|
7858
|
-
loader: "ts",
|
|
7859
|
-
resolveDir: outDir,
|
|
7860
|
-
sourcefile: `${cfg.name}.operator.ts`,
|
|
7861
|
-
},
|
|
7862
|
-
outfile: operatorPath,
|
|
7863
|
-
bundle: true,
|
|
7864
|
-
platform: "neutral",
|
|
7865
|
-
format: "esm",
|
|
7866
|
-
target: "es2022",
|
|
7867
|
-
legalComments: "none",
|
|
7868
|
-
logLevel: "silent",
|
|
7869
|
-
});
|
|
7870
|
-
writeFileSync(
|
|
7871
|
-
operatorPath,
|
|
7872
|
-
canonicalizeGeneratedClientJavascript(
|
|
7873
|
-
readFileSync(operatorPath, "utf8")
|
|
7874
|
-
),
|
|
7875
|
-
"utf8"
|
|
7876
|
-
);
|
|
7877
|
-
} else {
|
|
7878
|
-
rmSync(operatorPath, { force: true });
|
|
7879
|
-
rmSync(operatorDeclarationPath, { force: true });
|
|
8720
|
+
fail("generated Reception omitted its structural holon factory");
|
|
7880
8721
|
}
|
|
8722
|
+
stage("client bundle");
|
|
8723
|
+
// Lifecycle belongs to Peer Office. Remove outputs from older compiler
|
|
8724
|
+
// versions instead of generating a tenant-specific operator runtime.
|
|
8725
|
+
rmSync(path.join(outDir, `${cfg.name}.operator.mjs`), { force: true });
|
|
8726
|
+
rmSync(path.join(outDir, `${cfg.name}.operator.d.mts`), { force: true });
|
|
7881
8727
|
const frameworkOperatorDeclaration = ts.transpileDeclaration(
|
|
7882
8728
|
frameworkOperatorSource,
|
|
7883
8729
|
{
|
|
@@ -7910,7 +8756,7 @@ async function main(): Promise<void> {
|
|
|
7910
8756
|
"utf8"
|
|
7911
8757
|
);
|
|
7912
8758
|
writeFileSync(frameworkOperatorSourcePath, frameworkOperatorSource, "utf8");
|
|
7913
|
-
await esbuildMod.build({
|
|
8759
|
+
const frameworkBundle = await esbuildMod.build({
|
|
7914
8760
|
stdin: {
|
|
7915
8761
|
contents: frameworkOperatorSource,
|
|
7916
8762
|
loader: "ts",
|
|
@@ -7918,17 +8764,28 @@ async function main(): Promise<void> {
|
|
|
7918
8764
|
sourcefile: `${cfg.name}.framework.ts`,
|
|
7919
8765
|
},
|
|
7920
8766
|
outfile: frameworkOperatorPath,
|
|
8767
|
+
write: false,
|
|
8768
|
+
metafile: true,
|
|
7921
8769
|
bundle: true,
|
|
7922
8770
|
platform: "neutral",
|
|
7923
8771
|
format: "esm",
|
|
7924
8772
|
target: "es2022",
|
|
7925
8773
|
legalComments: "none",
|
|
7926
8774
|
logLevel: "silent",
|
|
8775
|
+
nodePaths: [compilerNodeModules],
|
|
8776
|
+
plugins: [protobufRuntimePlugin],
|
|
8777
|
+
external: [APPLICATION_CONTRACT_MODULE],
|
|
8778
|
+
});
|
|
8779
|
+
assertGeneratedClientBundleBoundary(frameworkBundle.metafile, {
|
|
8780
|
+
workingDirectory: process.cwd(),
|
|
8781
|
+
generatedEntry: path.join(outDir, `${cfg.name}.framework.ts`),
|
|
8782
|
+
operationCodec: path.join(outDir, "operation_payloads_pb.ts"),
|
|
8783
|
+
protobufPackageRoot: protobufPackageRoot(protobufEntry),
|
|
7927
8784
|
});
|
|
7928
8785
|
writeFileSync(
|
|
7929
8786
|
frameworkOperatorPath,
|
|
7930
8787
|
canonicalizeGeneratedClientJavascript(
|
|
7931
|
-
|
|
8788
|
+
frameworkBundle.outputFiles[0]!.text
|
|
7932
8789
|
),
|
|
7933
8790
|
"utf8"
|
|
7934
8791
|
);
|
|
@@ -7940,62 +8797,18 @@ async function main(): Promise<void> {
|
|
|
7940
8797
|
);
|
|
7941
8798
|
} finally {
|
|
7942
8799
|
rmSync(temporaryClientPath, { force: true });
|
|
7943
|
-
rmSync(temporaryHeadlessClientPath, { force: true });
|
|
7944
|
-
rmSync(temporaryRuntimeTypesPath, { force: true });
|
|
7945
8800
|
rmSync(temporaryDeclarationPath, { force: true });
|
|
7946
|
-
|
|
7947
|
-
|
|
8801
|
+
// Outputs of earlier compiler versions: the two-artifact split and its source sidecar.
|
|
8802
|
+
for (const stale of [".client.ts", ".client.mjs", ".client.d.mts", ".headless.mjs", ".headless.d.mts"]) {
|
|
8803
|
+
rmSync(path.join(outDir, `${cfg.name}${stale}`), { force: true });
|
|
8804
|
+
}
|
|
7948
8805
|
}
|
|
7949
8806
|
|
|
8807
|
+
stage("framework bundle");
|
|
7950
8808
|
// ── 6. the typed Dart frontend (opt-in; the Flutter sibling of step 5, emitted
|
|
7951
8809
|
// after the package hash like the TS client) ──
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
dartCfg !== false &&
|
|
7955
|
-
dartCfg !== true &&
|
|
7956
|
-
(typeof dartCfg !== "object" ||
|
|
7957
|
-
dartCfg === null ||
|
|
7958
|
-
(dartCfg.out !== undefined && typeof dartCfg.out !== "string") ||
|
|
7959
|
-
(dartCfg.support !== undefined &&
|
|
7960
|
-
dartCfg.support !== "vendored" &&
|
|
7961
|
-
dartCfg.support !== "package") ||
|
|
7962
|
-
(dartCfg.barrel !== undefined &&
|
|
7963
|
-
!/^[a-z][a-z0-9_]*$/.test(String(dartCfg.barrel))) ||
|
|
7964
|
-
(dartCfg.names !== undefined &&
|
|
7965
|
-
(typeof dartCfg.names !== "object" ||
|
|
7966
|
-
dartCfg.names === null ||
|
|
7967
|
-
Array.isArray(dartCfg.names))) ||
|
|
7968
|
-
(dartCfg.packages !== undefined &&
|
|
7969
|
-
(typeof dartCfg.packages !== "object" ||
|
|
7970
|
-
dartCfg.packages === null ||
|
|
7971
|
-
typeof dartCfg.packages.types !== "string" ||
|
|
7972
|
-
typeof dartCfg.packages.client !== "string")))
|
|
7973
|
-
) {
|
|
7974
|
-
fail(
|
|
7975
|
-
`config key 'dart' must be false, true, or { out?: "relative/dir", support?: "vendored" | "package", barrel?: "snake_case_name", packages?: { types, client, test?, compat? }, names?: { Generated: "Alias" } }; got ${JSON.stringify(
|
|
7976
|
-
dartCfg
|
|
7977
|
-
)}`
|
|
7978
|
-
);
|
|
7979
|
-
}
|
|
7980
|
-
const dartPackages =
|
|
7981
|
-
typeof dartCfg === "object" ? dartCfg.packages : undefined;
|
|
7982
|
-
const dartNames = typeof dartCfg === "object" ? dartCfg.names : undefined;
|
|
7983
|
-
if (
|
|
7984
|
-
dartPackages !== undefined &&
|
|
7985
|
-
typeof dartCfg === "object" &&
|
|
7986
|
-
dartCfg.support === "vendored"
|
|
7987
|
-
) {
|
|
7988
|
-
fail(
|
|
7989
|
-
`dart.packages requires support: "package" — layered pub packages depend on the published ` +
|
|
7990
|
-
`nomos_types/nomos_client cores; vendoring contradicts the package boundary`
|
|
7991
|
-
);
|
|
7992
|
-
}
|
|
7993
|
-
const dartSupportImport: DartSupportImport =
|
|
7994
|
-
typeof dartCfg === "object" && dartCfg.support !== undefined
|
|
7995
|
-
? dartCfg.support
|
|
7996
|
-
: dartPackages !== undefined
|
|
7997
|
-
? "package"
|
|
7998
|
-
: "vendored";
|
|
8810
|
+
await dartOperationProtoPrewarm;
|
|
8811
|
+
const dartSupportImport: DartSupportImport = "package";
|
|
7999
8812
|
let dartOut: string | undefined;
|
|
8000
8813
|
let dartFiles: string[] = [];
|
|
8001
8814
|
let dartCursedHints: string[] = [];
|
|
@@ -8018,7 +8831,6 @@ async function main(): Promise<void> {
|
|
|
8018
8831
|
packageInterface,
|
|
8019
8832
|
dartPackages,
|
|
8020
8833
|
dartNames,
|
|
8021
|
-
nomosCompat,
|
|
8022
8834
|
cfg.name,
|
|
8023
8835
|
lawUsdzPath,
|
|
8024
8836
|
cfg.application?.maxChildWorkspacesPerUser,
|
|
@@ -8047,11 +8859,11 @@ async function main(): Promise<void> {
|
|
|
8047
8859
|
packageInterface,
|
|
8048
8860
|
dartSupportImport,
|
|
8049
8861
|
dartBarrel,
|
|
8050
|
-
nomosCompat,
|
|
8051
8862
|
dartNames,
|
|
8052
8863
|
cfg.name
|
|
8053
8864
|
);
|
|
8054
8865
|
}
|
|
8866
|
+
stage("dart emit");
|
|
8055
8867
|
// COMPILE-VALID MUST MEAN COMPILABLE (co2): `githolon compile` reported success while the generated Dart
|
|
8056
8868
|
// had hard compiler errors (a duplicate id symbol, missing contract-id defs). Run `dart analyze` over the
|
|
8057
8869
|
// emitted Dart as a SYMBOL-RESOLUTION GATE — a real analysis ERROR fails the compile (with the analyzer's
|
|
@@ -8067,7 +8879,7 @@ async function main(): Promise<void> {
|
|
|
8067
8879
|
// Analyze exactly THE FILES THIS COMPILE EMITTED (`dartFiles`) — never the whole dir, which would
|
|
8068
8880
|
// sweep in the transactional `.nomos-bak` backup and any stale legacy-layout leftovers and judge the
|
|
8069
8881
|
// compile on files it did not write. First `dart pub get` each emitted PACKAGE (a dir up-chain with a
|
|
8070
|
-
// pubspec) so `package:` imports resolve
|
|
8882
|
+
// pubspec) so fixed `package:` imports resolve.
|
|
8071
8883
|
const emittedDart = dartFiles.filter((f) => f.endsWith(".dart"));
|
|
8072
8884
|
const pkgDirOf = (file: string): string | undefined => {
|
|
8073
8885
|
let dir = path.dirname(file);
|
|
@@ -8082,9 +8894,9 @@ async function main(): Promise<void> {
|
|
|
8082
8894
|
}
|
|
8083
8895
|
return undefined;
|
|
8084
8896
|
};
|
|
8085
|
-
// Only files under a PUB PACKAGE (a pubspec up-chain) are verifiable
|
|
8086
|
-
//
|
|
8087
|
-
//
|
|
8897
|
+
// Only files under a PUB PACKAGE (a pubspec up-chain) are verifiable.
|
|
8898
|
+
// A loose output directory has no package configuration against which
|
|
8899
|
+
// `nomos_client` can be resolved; note it rather than inventing runtime copies.
|
|
8088
8900
|
const inPackage = emittedDart.filter((f) => pkgDirOf(f) !== undefined);
|
|
8089
8901
|
const pkgDirs = [
|
|
8090
8902
|
...new Set(
|
|
@@ -8096,8 +8908,8 @@ async function main(): Promise<void> {
|
|
|
8096
8908
|
// necessarily LAG the local source the compiler was built alongside (tooling+client move in lockstep,
|
|
8097
8909
|
// CLAUDE.md). So the analyze gate must resolve against the LOCAL Dart source, else it judges freshly-
|
|
8098
8910
|
// emitted client code against a stale hosted runtime (e.g. a method the current codegen calls that the
|
|
8099
|
-
// last publish predates). We write a `pubspec_overrides.yaml` pointing
|
|
8100
|
-
//
|
|
8911
|
+
// last publish predates). We write a `pubspec_overrides.yaml` pointing the runtime packages at this
|
|
8912
|
+
// checkout's `dsl/dart`, `dsl/dart_types` and `nomos_peer_host`. An INSTALLED tenant (`npx githolon`, compiler under
|
|
8101
8913
|
// node_modules) gets NO override — it verifies against exactly the hosted runtime it will ship against.
|
|
8102
8914
|
const localDart = (() => {
|
|
8103
8915
|
const selfDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -8105,18 +8917,13 @@ async function main(): Promise<void> {
|
|
|
8105
8917
|
return undefined;
|
|
8106
8918
|
const client = path.resolve(selfDir, "..", "dart");
|
|
8107
8919
|
const types = path.resolve(selfDir, "..", "dart_types");
|
|
8108
|
-
const
|
|
8920
|
+
const peerHost = path.resolve(selfDir, "..", "..", "nomos_peer_host");
|
|
8109
8921
|
if (
|
|
8110
8922
|
existsSync(path.join(client, "pubspec.yaml")) &&
|
|
8111
|
-
existsSync(path.join(types, "pubspec.yaml"))
|
|
8923
|
+
existsSync(path.join(types, "pubspec.yaml")) &&
|
|
8924
|
+
existsSync(path.join(peerHost, "pubspec.yaml"))
|
|
8112
8925
|
) {
|
|
8113
|
-
return {
|
|
8114
|
-
client,
|
|
8115
|
-
types,
|
|
8116
|
-
...(existsSync(path.join(flutter, "pubspec.yaml"))
|
|
8117
|
-
? { flutter }
|
|
8118
|
-
: {}),
|
|
8119
|
-
};
|
|
8926
|
+
return { client, types, peerHost };
|
|
8120
8927
|
}
|
|
8121
8928
|
return undefined;
|
|
8122
8929
|
})();
|
|
@@ -8127,7 +8934,21 @@ async function main(): Promise<void> {
|
|
|
8127
8934
|
if (existsSync(file) && readFileSync(file, "utf8") === text) return;
|
|
8128
8935
|
writeFileSync(file, text, "utf8");
|
|
8129
8936
|
};
|
|
8130
|
-
|
|
8937
|
+
// The dependency reconciler pins nomos_client/nomos_types into the tenant package a FLAT
|
|
8938
|
+
// `dart.out` writes into, which for `dart.out: pkg/lib` sits ABOVE `dart.out` and so is
|
|
8939
|
+
// invisible to `pkgDirOf`. Pinning a dep there while withholding the override left a source
|
|
8940
|
+
// checkout emitting a package that cannot `pub get` at all (the pinned version is the one
|
|
8941
|
+
// this compiler is being released WITH, so it is not on pub yet). Override it too. It is not
|
|
8942
|
+
// added to `pkgDirs`: the analyze gate judges what the compiler emitted, never a tenant
|
|
8943
|
+
// package's own hand-written code.
|
|
8944
|
+
const overrideDirs = [
|
|
8945
|
+
...new Set(
|
|
8946
|
+
[...pkgDirs, flatDartPackageRoot(dOut)].filter(
|
|
8947
|
+
(dir): dir is string => dir !== undefined
|
|
8948
|
+
)
|
|
8949
|
+
),
|
|
8950
|
+
];
|
|
8951
|
+
for (const pkg of overrideDirs) {
|
|
8131
8952
|
// Layered Dart output is generated-owned, including the compiler's temporary source-dogfood
|
|
8132
8953
|
// override. A package may have been generated previously by a source checkout and then rebuilt
|
|
8133
8954
|
// by the published compiler. In that transition, doing nothing here leaves the old absolute
|
|
@@ -8140,7 +8961,6 @@ async function main(): Promise<void> {
|
|
|
8140
8961
|
const lines = ["dependency_overrides:"];
|
|
8141
8962
|
const usesClient = /(^|\n)\s+nomos_client\s*:/.test(spec);
|
|
8142
8963
|
const usesTypes = /(^|\n)\s+nomos_types\s*:/.test(spec);
|
|
8143
|
-
const usesFlutter = /(^|\n)\s+nomos_flutter\s*:/.test(spec);
|
|
8144
8964
|
if (usesClient)
|
|
8145
8965
|
lines.push(` nomos_client:`, ` path: ${localDart.client}`);
|
|
8146
8966
|
// A generated CLIENT or TEST package may only mention nomos_client directly, but that local
|
|
@@ -8151,12 +8971,13 @@ async function main(): Promise<void> {
|
|
|
8151
8971
|
// to absorb for tenant developers.
|
|
8152
8972
|
if (usesTypes || usesClient)
|
|
8153
8973
|
lines.push(` nomos_types:`, ` path: ${localDart.types}`);
|
|
8154
|
-
//
|
|
8155
|
-
//
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8974
|
+
// nomos_client consumes the peer-driven shell capability contract.
|
|
8975
|
+
// Overrides do not propagate into this package's solve root.
|
|
8976
|
+
if (usesClient)
|
|
8977
|
+
lines.push(
|
|
8978
|
+
` nomos_peer_host:`,
|
|
8979
|
+
` path: ${localDart.peerHost}`
|
|
8980
|
+
);
|
|
8160
8981
|
if (lines.length > 1) {
|
|
8161
8982
|
writeTextIfChanged(overrideFile, lines.join("\n") + "\n");
|
|
8162
8983
|
}
|
|
@@ -8246,9 +9067,10 @@ async function main(): Promise<void> {
|
|
|
8246
9067
|
.join(" ")}`;
|
|
8247
9068
|
}
|
|
8248
9069
|
}
|
|
9070
|
+
stage("dart pub get");
|
|
8249
9071
|
// The analyzer is a real codegen gate, but an identical generated Dart graph against identical
|
|
8250
9072
|
// resolved packages and framework sources does not become more valid by launching another analyzer
|
|
8251
|
-
// process. Content-address that verdict as USDA. Any generated type/
|
|
9073
|
+
// process. Content-address that verdict as USDA. Any generated type/peer-office change, pub resolution,
|
|
8252
9074
|
// local dogfood runtime change, or Dart SDK version change produces a different key and runs the real
|
|
8253
9075
|
// analyzer before the proof is written.
|
|
8254
9076
|
const collectDartAnalysisFiles = (root: string): string[] => {
|
|
@@ -8374,6 +9196,7 @@ async function main(): Promise<void> {
|
|
|
8374
9196
|
}
|
|
8375
9197
|
}
|
|
8376
9198
|
}
|
|
9199
|
+
stage("dart analyze");
|
|
8377
9200
|
// Only analyze when EVERY package's deps resolved: `dart analyze` against an unresolved package reports
|
|
8378
9201
|
// every framework symbol (NomosRef, …) as undefined — a meaningless flood, not a real defect. An
|
|
8379
9202
|
// unresolved dep is already surfaced as UNVERIFIED below (e.g. a fixture pinning an as-yet-unpublished
|
|
@@ -8396,7 +9219,7 @@ async function main(): Promise<void> {
|
|
|
8396
9219
|
);
|
|
8397
9220
|
} else if (emittedDart.length > 0) {
|
|
8398
9221
|
console.log(
|
|
8399
|
-
` dart
|
|
9222
|
+
` dart ⚠ UNVERIFIED — generated output has no pubspec, so fixed nomos_client imports cannot be analyzed (emit a Dart package to enable the gate)`
|
|
8400
9223
|
);
|
|
8401
9224
|
}
|
|
8402
9225
|
}
|
|
@@ -8409,6 +9232,13 @@ async function main(): Promise<void> {
|
|
|
8409
9232
|
` law ${rel(lawUsdzPath)} (${lawUsdz.byteLength} transport bytes)`
|
|
8410
9233
|
);
|
|
8411
9234
|
console.log(` domainHash ${domainHash}`);
|
|
9235
|
+
console.log(
|
|
9236
|
+
custodyPlacementDeclarations.length > 0
|
|
9237
|
+
? ` placement ${rel(custodyPlacementDeclarationsPath)} (${
|
|
9238
|
+
custodyPlacementDeclarations.length
|
|
9239
|
+
} explicit declaration(s); separate frontier)`
|
|
9240
|
+
: " placement — no custody-placement declarations"
|
|
9241
|
+
);
|
|
8412
9242
|
console.log(
|
|
8413
9243
|
` custody ${rel(
|
|
8414
9244
|
layeredRootPath
|
|
@@ -8421,6 +9251,17 @@ async function main(): Promise<void> {
|
|
|
8421
9251
|
}`
|
|
8422
9252
|
: ` usdcat ⚠ unavailable — set NOMOS_REQUIRE_USDCAT=1 in release CI to make this proof mandatory`
|
|
8423
9253
|
);
|
|
9254
|
+
console.log(" contracts ✓ generated Protobuf query results and public payloads match the kernel's law-derived contracts");
|
|
9255
|
+
console.log(
|
|
9256
|
+
compiledReadProjection
|
|
9257
|
+
? " read-law ✓ authenticated OpenUSD read projection packaged"
|
|
9258
|
+
: " read-law ⚠ compiler unavailable; runtime will compose source law on first open"
|
|
9259
|
+
);
|
|
9260
|
+
console.log(
|
|
9261
|
+
compiledCommandProjection
|
|
9262
|
+
? " commands ✓ authenticated OpenUSD command projection packaged"
|
|
9263
|
+
: " commands ⚠ compiler unavailable; runtime will compose source law before first command"
|
|
9264
|
+
);
|
|
8424
9265
|
{
|
|
8425
9266
|
const sum = (
|
|
8426
9267
|
pick: (
|
|
@@ -8490,28 +9331,14 @@ async function main(): Promise<void> {
|
|
|
8490
9331
|
}
|
|
8491
9332
|
}
|
|
8492
9333
|
console.log(
|
|
8493
|
-
`
|
|
8494
|
-
|
|
8495
|
-
)} (
|
|
8496
|
-
);
|
|
8497
|
-
console.log(
|
|
8498
|
-
` headless ${rel(headlessClientPath)} + ${rel(
|
|
8499
|
-
headlessClientDeclarationPath
|
|
8500
|
-
)} (typed business binding for code-mode/edge holons)`
|
|
9334
|
+
` reception ${rel(clientPath)} + ${rel(
|
|
9335
|
+
clientDeclarationPath
|
|
9336
|
+
)} (the application's TypeScript Reception: typed surface + holon-level factories)`
|
|
8501
9337
|
);
|
|
8502
9338
|
for (const note of recursiveNotes) console.log(` note ${note}`);
|
|
8503
9339
|
console.log(
|
|
8504
9340
|
` openapi served by the holon from installed USDA (GET /v2/workspaces/<ws>/openapi.json) — no build-time copy to drift`
|
|
8505
9341
|
);
|
|
8506
|
-
if (proofPath !== undefined) {
|
|
8507
|
-
console.log(
|
|
8508
|
-
` proof ${rel(
|
|
8509
|
-
proofPath
|
|
8510
|
-
)} (a runnable e2e GENERATED from your law — run: githolon proof)`
|
|
8511
|
-
);
|
|
8512
|
-
} else {
|
|
8513
|
-
console.log(` proof SKIPPED — ${proofSkip}`);
|
|
8514
|
-
}
|
|
8515
9342
|
for (const w of birthWarnings) {
|
|
8516
9343
|
console.log(` ⚠ births ${w}`);
|
|
8517
9344
|
}
|
|
@@ -8523,9 +9350,7 @@ async function main(): Promise<void> {
|
|
|
8523
9350
|
// compile runs it implicitly and REFUSES on any refuse-level finding (a statically-provable
|
|
8524
9351
|
// runtime refusal shipped as law). Warns/notes are printed but never block the compile.
|
|
8525
9352
|
const allCheckFindings = authorTimeFindings;
|
|
8526
|
-
const isFrameworkFinding =
|
|
8527
|
-
finding.domain === APPLICATION_HOME_DOMAIN ||
|
|
8528
|
-
finding.directiveId?.startsWith("nomos_internal_") === true;
|
|
9353
|
+
const isFrameworkFinding = isFrameworkAuthorTimeFinding;
|
|
8529
9354
|
const checkFindings = allCheckFindings.filter(
|
|
8530
9355
|
(finding) => !isFrameworkFinding(finding)
|
|
8531
9356
|
);
|
|
@@ -8594,14 +9419,10 @@ async function main(): Promise<void> {
|
|
|
8594
9419
|
);
|
|
8595
9420
|
}
|
|
8596
9421
|
} else {
|
|
8597
|
-
const supportText =
|
|
8598
|
-
dartSupportImport === "vendored"
|
|
8599
|
-
? "vendored nomos_client support"
|
|
8600
|
-
: "package nomos_client imports";
|
|
8601
9422
|
console.log(
|
|
8602
9423
|
` dart ${rel(dartOut)}${path.sep} (${
|
|
8603
9424
|
dartFiles.length
|
|
8604
|
-
} domain file(s) +
|
|
9425
|
+
} domain file(s) + package nomos_client imports — typed Dart for Flutter)`
|
|
8605
9426
|
);
|
|
8606
9427
|
}
|
|
8607
9428
|
}
|
|
@@ -8610,34 +9431,17 @@ async function main(): Promise<void> {
|
|
|
8610
9431
|
);
|
|
8611
9432
|
console.log(``);
|
|
8612
9433
|
console.log(`your business path:`);
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
}
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
console.log(
|
|
8623
|
-
` npx githolon dev # rerun that proof on every save`
|
|
8624
|
-
);
|
|
8625
|
-
console.log(
|
|
8626
|
-
` npx githolon proof --live # the same business proof against Nomos Cloud`
|
|
8627
|
-
);
|
|
8628
|
-
if (proofDomains.length > 1) {
|
|
8629
|
-
console.log(
|
|
8630
|
-
` this package proves all ${
|
|
8631
|
-
proofDomains.length
|
|
8632
|
-
} business areas together (${proofDomains.join(", ")})`
|
|
8633
|
-
);
|
|
8634
|
-
}
|
|
8635
|
-
} else {
|
|
8636
|
-
console.log(
|
|
8637
|
-
` add a business birth declaration to generate an end-to-end proof`
|
|
8638
|
-
);
|
|
8639
|
-
}
|
|
9434
|
+
console.log(
|
|
9435
|
+
` npx githolon test # generated Dart scenarios against the real local custody plane`
|
|
9436
|
+
);
|
|
9437
|
+
console.log(
|
|
9438
|
+
` vitest + @githolon/testing # drive ${cfg.name}.reception.mjs through the real kernel from TypeScript`
|
|
9439
|
+
);
|
|
9440
|
+
console.log(
|
|
9441
|
+
` npx githolon dev # recompile and re-check on every save`
|
|
9442
|
+
);
|
|
8640
9443
|
|
|
9444
|
+
stage("summary + author-time gate");
|
|
8641
9445
|
if (analyzeFlag) runDartAnalyze(dartOut);
|
|
8642
9446
|
|
|
8643
9447
|
// The unchanged lane is itself USDA: a disposable content proof under
|
|
@@ -8689,6 +9493,7 @@ async function main(): Promise<void> {
|
|
|
8689
9493
|
law: lawUsdzPath,
|
|
8690
9494
|
});
|
|
8691
9495
|
}
|
|
9496
|
+
stage("compile proof");
|
|
8692
9497
|
}
|
|
8693
9498
|
|
|
8694
9499
|
/**
|