@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.
Files changed (307) hide show
  1. package/assets/operation-contract.wasm +0 -0
  2. package/check_package.mjs +12 -15
  3. package/compile_package.mjs +17 -18
  4. package/compile_proof.mjs +16 -1
  5. package/dart/CHANGELOG.md +76 -1
  6. package/dart/README.md +25 -2
  7. package/dart/lib/src/admission_decision.dart +41 -0
  8. package/dart/lib/src/application_framework_calls.dart +52 -2
  9. package/dart/lib/src/application_model_adoption.dart +187 -0
  10. package/dart/lib/src/application_status.dart +73 -0
  11. package/dart/lib/src/console_enrolment.dart +271 -0
  12. package/dart/lib/src/console_http_io.dart +26 -0
  13. package/dart/lib/src/console_http_stub.dart +7 -0
  14. package/dart/lib/src/dev_cloud.dart +69 -22
  15. package/dart/lib/src/generated_call.dart +83 -23
  16. package/dart/lib/src/kernel_primitives.dart +119 -0
  17. package/dart/lib/src/nms_conformance_runtime.dart +1 -1
  18. package/dart/lib/src/nms_generated_runtime.dart +32 -10
  19. package/dart/lib/src/nms_test_environment.dart +1 -1
  20. package/dart/lib/src/node_runner_transport.dart +41 -46
  21. package/dart/lib/src/observed_session_directory.dart +70 -0
  22. package/dart/lib/src/operation_receipts.dart +112 -0
  23. package/dart/lib/src/peer_application_streams.dart +103 -0
  24. package/dart/lib/src/reception/dart_reception.dart +4280 -0
  25. package/dart/lib/src/reception/event_desk.dart +211 -0
  26. package/dart/lib/src/reception/extremum_read.dart +46 -0
  27. package/dart/lib/src/reception/local_copies.dart +371 -0
  28. package/dart/lib/src/reception/peer_office_connection.dart +11 -0
  29. package/dart/lib/src/reception/progress_desk.dart +267 -0
  30. package/dart/lib/src/reception/reception_contract.dart +11 -0
  31. package/dart/lib/src/reception/reception_session.dart +284 -0
  32. package/dart/lib/src/reception/sync_diagnostics.dart +26 -0
  33. package/dart/lib/src/reception/workspace_preparation_report.dart +126 -0
  34. package/dart/lib/src/replica_readiness.dart +123 -0
  35. package/dart/lib/src/runner/node-entry.mjs +23905 -15774
  36. package/dart/lib/src/runtime_v2_gateway.dart +56 -3
  37. package/dart/lib/src/runtime_v2_transport.dart +544 -3
  38. package/dart/lib/src/session_bound.dart +200 -311
  39. package/dart/lib/src/signing_identity_kernel.dart +84 -0
  40. package/dart/lib/src/sync_status_projection.dart +548 -0
  41. package/dart/lib/src/sync_status_reducer.dart +170 -0
  42. package/dart/lib/src/test_harness.dart +809 -1153
  43. package/dart/lib/src/testing/test_custody.dart +66 -0
  44. package/dart/lib/src/testing/test_peer.dart +328 -0
  45. package/dart/lib/src/typed_watch_registry.dart +169 -0
  46. package/dart/lib/src/workspace_open_progress.dart +27 -0
  47. package/dart/pubspec.lock +24 -1
  48. package/dart/pubspec.yaml +7 -2
  49. package/dart/pubspec_overrides.yaml +2 -0
  50. package/dart/test/admission_decision_test.dart +48 -0
  51. package/dart/test/background_model_adoption_test.dart +228 -0
  52. package/dart/test/bundle_version_pairing_test.dart +4 -4
  53. package/dart/test/child_authentication_repair_test.dart +359 -0
  54. package/dart/test/cloud_bound_local_shell_test.dart +119 -0
  55. package/dart/test/console_enrolment_refusal_test.dart +52 -0
  56. package/dart/test/console_enrolment_test.dart +227 -0
  57. package/dart/test/errors_test.dart +5 -5
  58. package/dart/test/event_plane_seam_test.dart +50 -164
  59. package/dart/test/extremum_read_test.dart +58 -0
  60. package/dart/test/fixtures/wedge/wedge-entry.mjs +3 -21
  61. package/dart/test/generated_query_test.dart +32 -0
  62. package/dart/test/harness_phase_timeout_test.dart +18 -23
  63. package/dart/test/kernel_author_after_read_test.dart +242 -0
  64. package/dart/test/kernel_fence_test.dart +137 -0
  65. package/dart/test/local_copies_test.dart +222 -0
  66. package/dart/test/model_adoption_test.dart +198 -0
  67. package/dart/test/node_peer_local_custody_test.dart +153 -0
  68. package/dart/test/offer_authentication_proof_test.dart +352 -52
  69. package/dart/test/operation_compatibility_test.dart +151 -0
  70. package/dart/test/platform_credential_effect_test.dart +386 -0
  71. package/dart/test/receiving_group_open_test.dart +213 -0
  72. package/dart/test/reception/workspace_preparation_report_test.dart +103 -0
  73. package/dart/test/reception_span_trace_test.dart +23 -0
  74. package/dart/test/replica_readiness_test.dart +156 -0
  75. package/dart/test/runtime_projections_test.dart +92 -0
  76. package/dart/test/runtime_v2_gateway_test.dart +131 -0
  77. package/dart/test/signing_identity_kernel_test.dart +75 -0
  78. package/dart/test/sync_status_preparation_report_test.dart +29 -0
  79. package/dart/test/sync_status_reducer_test.dart +419 -0
  80. package/dart/test/test_harness_test.dart +71 -95
  81. package/dart/test/typed_query_watch_test.dart +170 -0
  82. package/dart/test/typed_watch_registry_test.dart +249 -0
  83. package/dart_lints/README.md +18 -0
  84. package/dart_lints/lib/nomos_lints.dart +70 -0
  85. package/dart_lints/pubspec.lock +580 -0
  86. package/dart_lints/pubspec.yaml +12 -0
  87. package/dart_lints/test/reference_identity_test.dart +75 -0
  88. package/dart_types/CHANGELOG.md +18 -0
  89. package/dart_types/lib/nomos_kernel_protocol.dart +21 -1
  90. package/dart_types/lib/nomos_types.dart +9 -1
  91. package/dart_types/lib/src/admission_decision.dart +37 -0
  92. package/dart_types/lib/src/aggregate_meta.dart +1 -1
  93. package/dart_types/lib/src/approved_model.dart +65 -0
  94. package/dart_types/lib/src/custody.dart +10 -18
  95. package/dart_types/lib/src/error_info.dart +4 -4
  96. package/dart_types/lib/src/errors.dart +29 -29
  97. package/dart_types/lib/src/foreign_ref.dart +62 -33
  98. package/dart_types/lib/src/local_work_rejected.dart +3 -3
  99. package/dart_types/lib/src/nomos_ref.dart +3 -1
  100. package/dart_types/lib/src/principal.dart +2 -2
  101. package/dart_types/lib/src/proto/google/protobuf/any.pb.dart +163 -0
  102. package/dart_types/lib/src/proto/google/protobuf/any.pbenum.dart +11 -0
  103. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pb.dart +137 -0
  104. package/dart_types/lib/src/proto/nomos/discovery/v1/workspace_placement.pbenum.dart +11 -0
  105. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pb.dart +20721 -10433
  106. package/dart_types/lib/src/proto/nomos/kernel/v1/kernel.pbenum.dart +555 -27
  107. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pb.dart +387 -0
  108. package/dart_types/lib/src/proto/nomos/runtime/v2/application_desk.pbenum.dart +46 -0
  109. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pb.dart +295 -0
  110. package/dart_types/lib/src/proto/nomos/runtime/v2/application_readiness_desk.pbenum.dart +139 -0
  111. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pb.dart +269 -0
  112. package/dart_types/lib/src/proto/nomos/runtime/v2/authentication_desk.pbenum.dart +103 -0
  113. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pb.dart +417 -0
  114. package/dart_types/lib/src/proto/nomos/runtime/v2/compatibility_desk.pbenum.dart +62 -0
  115. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pb.dart +9968 -0
  116. package/dart_types/lib/src/proto/nomos/runtime/v2/correspondence_desk.pbenum.dart +133 -0
  117. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pb.dart +204 -0
  118. package/dart_types/lib/src/proto/nomos/runtime/v2/credentials_desk.pbenum.dart +54 -0
  119. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pb.dart +1550 -0
  120. package/dart_types/lib/src/proto/nomos/runtime/v2/custody_desk.pbenum.dart +11 -0
  121. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pb.dart +710 -0
  122. package/dart_types/lib/src/proto/nomos/runtime/v2/framework.pbenum.dart +11 -0
  123. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pb.dart +845 -0
  124. package/dart_types/lib/src/proto/nomos/runtime/v2/identity_desk.pbenum.dart +11 -0
  125. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pb.dart +198 -0
  126. package/dart_types/lib/src/proto/nomos/runtime/v2/interaction_desk.pbenum.dart +11 -0
  127. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pb.dart +369 -0
  128. package/dart_types/lib/src/proto/nomos/runtime/v2/law_desk.pbenum.dart +11 -0
  129. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pb.dart +609 -0
  130. package/dart_types/lib/src/proto/nomos/runtime/v2/maintenance_desk.pbenum.dart +65 -0
  131. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pb.dart +358 -0
  132. package/dart_types/lib/src/proto/nomos/runtime/v2/model_adoption_desk.pbenum.dart +100 -0
  133. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pb.dart +2231 -0
  134. package/dart_types/lib/src/proto/nomos/runtime/v2/observability_desk.pbenum.dart +271 -0
  135. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pb.dart +1286 -0
  136. package/dart_types/lib/src/proto/nomos/runtime/v2/placement_desk.pbenum.dart +11 -0
  137. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pb.dart +636 -0
  138. package/dart_types/lib/src/proto/nomos/runtime/v2/receiving_group_desk.pbenum.dart +116 -0
  139. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pb.dart +1637 -0
  140. package/dart_types/lib/src/proto/nomos/runtime/v2/records_desk.pbenum.dart +175 -0
  141. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pb.dart +1269 -0
  142. package/dart_types/lib/src/proto/nomos/runtime/v2/recovery_desk.pbenum.dart +129 -0
  143. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pb.dart +2782 -0
  144. package/dart_types/lib/src/proto/nomos/runtime/v2/residency_desk.pbenum.dart +97 -0
  145. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pb.dart +2232 -8627
  146. package/dart_types/lib/src/proto/nomos/runtime/v2/runtime.pbenum.dart +0 -358
  147. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pb.dart +1639 -0
  148. package/dart_types/lib/src/proto/nomos/runtime/v2/sealing_desk.pbenum.dart +46 -0
  149. package/dart_types/lib/src/runtime_outcome.dart +133 -0
  150. package/dart_types/lib/src/subscriptions.dart +116 -23
  151. package/dart_types/pubspec.yaml +1 -1
  152. package/dart_types/test/extremum_subscriptions_test.dart +40 -0
  153. package/dart_types/test/foreign_ref_resolution_test.dart +75 -0
  154. package/dart_types/test/nomos_ref_address_test.dart +25 -0
  155. package/dart_types/test/ordered_subscriptions_test.dart +34 -0
  156. package/operation_contract_check.mjs +24 -0
  157. package/package.json +11 -6
  158. package/src/aggregate.ts +70 -10
  159. package/src/application_topology.ts +18 -11
  160. package/src/attested.ts +3 -0
  161. package/src/attested_core.ts +46 -3
  162. package/src/birth_meter_ensure.ts +19 -7
  163. package/src/births.ts +29 -4
  164. package/src/build_package.ts +140 -13
  165. package/src/business_birth.ts +181 -4
  166. package/src/codegen_call_proto.ts +1274 -0
  167. package/src/codegen_call_usda.ts +49 -6
  168. package/src/codegen_dart.ts +79 -44
  169. package/src/codegen_dart_client.ts +458 -800
  170. package/src/{codegen_dart_application.ts → codegen_dart_facade.ts} +331 -582
  171. package/src/codegen_dart_framework_calls.ts +320 -37
  172. package/src/codegen_dart_indexed_variant.ts +159 -0
  173. package/src/codegen_dart_scenario.ts +42 -13
  174. package/src/codegen_proof.ts +3 -3
  175. package/src/codegen_provider_dart.ts +5 -0
  176. package/src/codegen_query_proto.ts +158 -0
  177. package/src/codegen_refusal.ts +189 -0
  178. package/src/codegen_result_proto.ts +931 -0
  179. package/src/codegen_result_usda.ts +4 -0
  180. package/src/codegen_ts.ts +464 -616
  181. package/src/codegen_workspace_access.ts +105 -0
  182. package/src/compatibility_graph.ts +759 -0
  183. package/src/compatibility_usda.ts +264 -0
  184. package/src/compile_package_main.ts +1994 -1189
  185. package/src/custody_placement.ts +156 -0
  186. package/src/custody_placement_usda.ts +91 -0
  187. package/src/decl_shapes.ts +21 -2
  188. package/src/describe_model.ts +1 -0
  189. package/src/directive.ts +59 -2
  190. package/src/drivers.ts +51 -0
  191. package/src/engine_entry.ts +52 -8
  192. package/src/evolve_preview.ts +11 -4
  193. package/src/exists.ts +2 -0
  194. package/src/fields.ts +75 -11
  195. package/src/flow_gate.ts +2 -1
  196. package/src/framework/application_home.ts +170 -18
  197. package/src/framework/attested_receipts.ts +8 -3
  198. package/src/framework/auth_provider_law.ts +169 -9
  199. package/src/framework/birth_certificate_law.ts +4 -0
  200. package/src/framework/capability.ts +34 -3
  201. package/src/framework/child_authentication.ts +346 -0
  202. package/src/framework/console_governance.ts +295 -0
  203. package/src/framework/delegated_roles.ts +301 -0
  204. package/src/framework/domain_lifecycle.ts +26 -13
  205. package/src/framework/founding_genesis.ts +193 -0
  206. package/src/framework/framework_aggregates.ts +1 -1
  207. package/src/framework/genesis.ts +6 -1
  208. package/src/framework/governance_relations.ts +19 -0
  209. package/src/framework/home.ts +5 -734
  210. package/src/framework/legacy/child_auth_provider_epoch.ts +73 -0
  211. package/src/framework/model_authority_repair.ts +116 -0
  212. package/src/framework/model_authority_seed.ts +185 -0
  213. package/src/framework/model_transition.ts +211 -0
  214. package/src/framework/owned_custody.ts +1 -1
  215. package/src/framework/principal_enrollment.ts +302 -0
  216. package/src/framework/receiving_group.ts +857 -0
  217. package/src/framework/receiving_group_recovery.ts +314 -0
  218. package/src/framework/relations.ts +4 -8
  219. package/src/framework/signing_identity.ts +242 -95
  220. package/src/framework/source_roster.ts +11 -5
  221. package/src/framework/workspace_access.ts +149 -0
  222. package/src/framework/workspace_invariant.ts +73 -52
  223. package/src/framework/workspaces.ts +407 -40
  224. package/src/framework_call_definitions.ts +32 -3
  225. package/src/generated_client_bundle_boundary.ts +97 -0
  226. package/src/generated_client_names.ts +1 -0
  227. package/src/identity_usda.ts +27 -0
  228. package/src/index.ts +183 -12
  229. package/src/indexed_variant.ts +83 -0
  230. package/src/intent.ts +173 -45
  231. package/src/law_check.ts +8 -5
  232. package/src/law_dependency_index.ts +231 -0
  233. package/src/law_graph.ts +508 -71
  234. package/src/law_graph_usda.ts +170 -24
  235. package/src/law_identity.ts +119 -81
  236. package/src/law_source_map.ts +4 -1
  237. package/src/law_units.ts +498 -0
  238. package/src/law_value_type.ts +7 -3
  239. package/src/member_authentication.ts +302 -0
  240. package/src/null_meta.ts +1 -1
  241. package/src/openusd_compiled_layer.ts +132 -0
  242. package/src/operation_contract_check.ts +31 -0
  243. package/src/ordered_read.ts +2 -1
  244. package/src/package_config.ts +33 -12
  245. package/src/package_dependencies.ts +1 -0
  246. package/src/plan_source_slice.ts +30 -1
  247. package/src/protobuf_field_number.ts +36 -0
  248. package/src/provenance_wire.ts +6 -0
  249. package/src/query.ts +2 -1
  250. package/src/read.ts +50 -0
  251. package/src/relation.ts +4 -1
  252. package/src/relation_schema.ts +15 -2
  253. package/src/report.ts +2 -2
  254. package/src/sqlite_computation_bundle.ts +78 -0
  255. package/src/sqlite_computed.ts +102 -0
  256. package/src/sqlite_counts.ts +154 -0
  257. package/src/sqlite_layout.ts +823 -0
  258. package/src/sqlite_reads.ts +275 -0
  259. package/src/sqlite_samples.ts +185 -0
  260. package/src/stable_id_identity_bridge.ts +1 -1
  261. package/src/stable_ids_for_module.ts +16 -4
  262. package/src/test_call_usda.ts +6 -6
  263. package/src/usda_relationship_integrity.ts +20 -3
  264. package/src/usda_text.ts +21 -1
  265. package/src/value_object.ts +81 -11
  266. package/src/wire.ts +22 -0
  267. package/src/wire_encode.ts +227 -20
  268. package/src/workspace_sharding.ts +15 -6
  269. package/src/workspace_sharing.ts +51 -49
  270. package/src/zod_introspection.ts +2 -0
  271. package/compat_package.mjs +0 -46
  272. package/dart/lib/src/home.dart +0 -15
  273. package/dart/lib/src/runner/offline-install.mjs +0 -15367
  274. package/dart/lib/src/source_trust.dart +0 -51
  275. package/dart/lib/src/webview_bridge.dart +0 -6540
  276. package/dart/lib/src/workspace_name.dart +0 -30
  277. package/dart/test/acquire_session_credentials_test.dart +0 -150
  278. package/dart/test/acquire_session_test.dart +0 -534
  279. package/dart/test/adv_realm_interference_test.dart +0 -600
  280. package/dart/test/cloud_bound_topology_test.dart +0 -168
  281. package/dart/test/cluster_births_test.dart +0 -202
  282. package/dart/test/cluster_device_keyed_acceptance_test.dart +0 -105
  283. package/dart/test/cluster_local_autosync_test.dart +0 -123
  284. package/dart/test/cluster_scale_test.dart +0 -122
  285. package/dart/test/compat_test.dart +0 -61
  286. package/dart/test/dlq_session_surface_test.dart +0 -222
  287. package/dart/test/first_app_gauntlet_test.dart +0 -386
  288. package/dart/test/fixtures/wedge/fake-install.mjs +0 -11
  289. package/dart/test/four_layer_nested_topology_test.dart +0 -290
  290. package/dart/test/home_parity_test.dart +0 -13
  291. package/dart/test/local_sync_inert_test.dart +0 -107
  292. package/dart/test/node_runner_transport_test.dart +0 -78
  293. package/dart/test/parent_signer_recovery_test.dart +0 -155
  294. package/dart/test/residency_refusal_test.dart +0 -49
  295. package/dart/test/sealing_client_test.dart +0 -181
  296. package/dart/test/session_bound_test.dart +0 -285
  297. package/dart/test/stale_bundle_test.dart +0 -80
  298. package/dart/test/vo_read_typing_test.dart +0 -131
  299. package/dart/test/watch_after_offer_stall_test.dart +0 -100
  300. package/dart/test/webview_bridge_test.dart +0 -1930
  301. package/dart/test/workspace_name_test.dart +0 -24
  302. package/runtime/generated-application-host.js +0 -22860
  303. package/src/codegen_query_usda.ts +0 -214
  304. package/src/compat_manifest.ts +0 -26
  305. package/src/compat_package_main.ts +0 -224
  306. package/src/shape_compat.ts +0 -352
  307. package/src/usd_state.ts +0 -505
@@ -0,0 +1,119 @@
1
+ @internal
2
+ library;
3
+
4
+ import 'package:meta/meta.dart' show internal;
5
+ import 'package:fixnum/fixnum.dart' as fixnum;
6
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
7
+
8
+ import 'runtime_v2_transport.dart' show NomosKernelCallTransport;
9
+
10
+ /// Exact, workspace-independent kernel producers.
11
+ ///
12
+ /// These are the clearing house's stamp and key desks: each accepts one named
13
+ /// generated form and returns one named generated form. They own no custody,
14
+ /// session lifecycle, application law or generic command dispatch.
15
+ final class NmsKernelPrimitives {
16
+ NmsKernelPrimitives({
17
+ required NomosKernelCallTransport transport,
18
+ required String session,
19
+ }) : _transport = transport,
20
+ _session = session;
21
+
22
+ final NomosKernelCallTransport _transport;
23
+ final String _session;
24
+ int _sequence = 0;
25
+
26
+ Future<nomos_protocol.MintTypedIdResponse> mintTypedId({
27
+ required String typeTag,
28
+ required int capturedUnixMillis,
29
+ }) async {
30
+ final response = await _call(
31
+ nomos_protocol.CallRequest(
32
+ mintTypedId: nomos_protocol.MintTypedIdRequest(
33
+ typeTag: typeTag,
34
+ capturedUnixMillis: fixnum.Int64(capturedUnixMillis),
35
+ ),
36
+ ),
37
+ );
38
+ if (!response.hasMintTypedId()) {
39
+ throw StateError(
40
+ 'Kernel returned ${response.whichOutcome().name} for typed id mint',
41
+ );
42
+ }
43
+ return response.mintTypedId;
44
+ }
45
+
46
+ Future<nomos_protocol.EncryptionIdentityResponse>
47
+ generateEncryptionIdentity() async {
48
+ final response = await _call(
49
+ nomos_protocol.CallRequest(
50
+ encryptionIdentity: nomos_protocol.EncryptionIdentityRequest(),
51
+ ),
52
+ );
53
+ if (!response.hasEncryptionIdentity()) {
54
+ throw StateError(
55
+ 'Kernel returned ${response.whichOutcome().name} for encryption identity',
56
+ );
57
+ }
58
+ return response.encryptionIdentity;
59
+ }
60
+
61
+ Future<nomos_protocol.WrapScopeKeyResponse> wrapScopeKey({
62
+ required List<int> recipientPublicKey,
63
+ required List<int> scopeKey,
64
+ }) async {
65
+ final response = await _call(
66
+ nomos_protocol.CallRequest(
67
+ wrapScopeKey: nomos_protocol.WrapScopeKeyRequest(
68
+ recipientPublicKey: recipientPublicKey,
69
+ scopeKey: scopeKey,
70
+ ),
71
+ ),
72
+ );
73
+ if (!response.hasWrapScopeKey()) {
74
+ throw StateError(
75
+ 'Kernel returned ${response.whichOutcome().name} for scope-key wrapping',
76
+ );
77
+ }
78
+ return response.wrapScopeKey;
79
+ }
80
+
81
+ Future<nomos_protocol.UnwrapScopeKeyResponse> unwrapScopeKey({
82
+ required List<int> recipientSecretKey,
83
+ required List<int> ephemeralPublicKey,
84
+ required List<int> ciphertext,
85
+ }) async {
86
+ final response = await _call(
87
+ nomos_protocol.CallRequest(
88
+ unwrapScopeKey: nomos_protocol.UnwrapScopeKeyRequest(
89
+ recipientSecretKey: recipientSecretKey,
90
+ ephemeralPublicKey: ephemeralPublicKey,
91
+ ciphertext: ciphertext,
92
+ ),
93
+ ),
94
+ );
95
+ if (!response.hasUnwrapScopeKey()) {
96
+ throw StateError(
97
+ 'Kernel returned ${response.whichOutcome().name} for scope-key unwrapping',
98
+ );
99
+ }
100
+ return response.unwrapScopeKey;
101
+ }
102
+
103
+ Future<nomos_protocol.CallResponse> _call(
104
+ nomos_protocol.CallRequest request,
105
+ ) async {
106
+ request
107
+ ..protocolVersion = 1
108
+ ..requestId = '$_session:primitive:${++_sequence}';
109
+ final response = await _transport.callKernel(
110
+ session: _session,
111
+ request: request,
112
+ credentialUse: nomos_protocol.CredentialUse.CREDENTIAL_USE_NONE,
113
+ );
114
+ if (response.hasFailure()) {
115
+ throw StateError('${response.failure.code}: ${response.failure.message}');
116
+ }
117
+ return response;
118
+ }
119
+ }
@@ -9,4 +9,4 @@ export 'dev_cloud.dart';
9
9
  export 'node_runner_transport.dart';
10
10
  export 'session_bound.dart';
11
11
  export 'test_harness.dart';
12
- export 'webview_bridge.dart';
12
+ export 'reception/dart_reception.dart';
@@ -7,13 +7,21 @@ library;
7
7
  import 'package:meta/meta.dart' show internal;
8
8
 
9
9
  export 'package:nomos_types/nomos_types.dart';
10
+ export 'package:nomos_peer_host/nomos_peer_host.dart'
11
+ show
12
+ NomosSignIn,
13
+ NomosDeviceIdentityStore,
14
+ NomosProviderCredentialStore,
15
+ NomosLawPackageSource;
10
16
  export 'package:nomos_types/nomos_kernel_protocol.dart'
11
17
  show
12
- CustodyBreakDecision,
13
- CustodyBreakResolved,
14
- ModelDifferenceKind,
15
- ModelLifecycleResponse,
16
- ModelLifecycleStage,
18
+ ChildWorkspace,
19
+ ChildWorkspaceStatus,
20
+ CheckInterfaceCompatibilityRequest,
21
+ InterfaceCompatibilityResponse,
22
+ InterfaceCompatibilityVerdict,
23
+ InterfaceMemberVerdict,
24
+ LocalLawCurrency,
17
25
  WorkspaceSummary;
18
26
  export 'package:nomos_types/src/nomos_ref.dart' show NmsInternalNomosRefs;
19
27
  export 'package:nomos_types/src/versioned_nomos_ref.dart'
@@ -30,31 +38,45 @@ export 'package:nomos_types/src/foreign_ref.dart'
30
38
  ForeignNotFound,
31
39
  ForeignDenied,
32
40
  ForeignUnreachable;
33
- export 'home.dart';
34
- export 'workspace_name.dart';
35
- export 'webview_bridge.dart';
41
+ export 'reception/dart_reception.dart';
36
42
  export 'session_bound.dart';
43
+ export 'application_model_adoption.dart'
44
+ show
45
+ NomosApprovedModel,
46
+ NomosModelAdoption,
47
+ NomosModelAdoptionDeferred,
48
+ NomosModelAdoptionOutcome,
49
+ NomosModelAdoptionPhase,
50
+ NomosModelAdoptionProgress;
37
51
  export 'provider.dart';
38
52
  export 'record_origin.dart';
39
- export 'source_trust.dart';
40
53
  export 'generated_call.dart'
41
54
  show
42
55
  NmsGeneratedCall,
43
56
  NmsGeneratedQuery,
57
+ NmsGeneratedResult,
44
58
  NmsInternalGeneratedCalls,
45
- NmsInternalGeneratedQueries;
59
+ NmsInternalGeneratedQueries,
60
+ NmsInternalGeneratedResults;
46
61
  export 'application_framework_calls.dart' show OwnedCustody, OwnedDeviceRequest;
47
62
  export 'result_usda.dart'
48
63
  show
49
64
  NmsUsdaPrim,
65
+ NmsUsdaLayer,
50
66
  NmsUsdaResult,
51
67
  NmsUsdaConflictResult,
52
68
  NmsUsdaConflictCount,
53
69
  NmsUsdaConflictSummaryResult;
54
70
  export 'runtime_v2_transport.dart'
55
71
  show
72
+ NmsPlatformCredentialProviderHost,
73
+ NomosPlatformCredentialProviders,
74
+ NomosSessionCredentials,
56
75
  NomosKernelCallTransport,
57
76
  NomosRuntimeV2Transport,
58
77
  NomosRuntimeV2EventTransport,
59
78
  NomosRuntimeV2KernelCalls,
60
79
  NmsBufferedRuntimeEvents;
80
+ export 'reception/peer_office_connection.dart' show PeerOfficeConnection;
81
+
82
+ export 'admission_decision.dart';
@@ -5,4 +5,4 @@ library;
5
5
  import 'package:meta/meta.dart' show internal;
6
6
 
7
7
  export 'dev_cloud.dart' show DevIdentityToken, NomosTestCloud;
8
- export 'test_harness.dart' show NomosTestHarness;
8
+ export 'test_harness.dart' show NomosTestHarness, NomosTestFrontier;
@@ -1,12 +1,12 @@
1
- // THE HEADLESS (NODE) TRANSPORT — a [BridgeTransport] over the `@githolon/client` node runner
2
- // (`runner/node-entry.ts`), spawned as a plain child process speaking the ONE bridge protocol as
3
- // newline-delimited JSON on stdio. This is what lets `dart test` / `flutter test` / CI drive the REAL
4
- // githolon (the byte-identical wasm, the real ledger, the real gate) through [NomosBridge] and the
1
+ // THE NODE PEER CONNECTION — a [PeerOfficeConnection] over the `@githolon/runtime` node runner
2
+ // (`runner/node-entry.ts`), spawned as a plain child process carrying generated
3
+ // Runtime v2 protobuf frames over base64-delimited stdio. This lets `dart test` / `flutter test` / CI drive the REAL
4
+ // githolon (the byte-identical wasm, the real ledger, the real gate) through Reception and the
5
5
  // generated typed clients — no Flutter plugins, no WebView, no assets. The only environment requirement
6
- // is `node` on PATH plus a resolvable `@githolon/client` (any app that compiles domains already has both).
6
+ // is `node` on PATH plus a resolvable `@githolon/runtime` (any app that compiles domains already has both).
7
7
  //
8
- // This file is a PURE RELAY: no orchestration, no op knowledge — the protocol implementation lives in
9
- // createWebviewRunner (shared with the WebView path) and the orchestration in index.mjs, same as every peer.
8
+ // This file is a PURE RELAY: no orchestration and no operation knowledge. The
9
+ // child runs the same peer-runtime component as every other platform.
10
10
  @internal
11
11
  library;
12
12
 
@@ -23,17 +23,19 @@ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
23
23
  import 'runtime_v2_transport.dart'
24
24
  show
25
25
  NmsBufferedRuntimeEvents,
26
+ NmsPlatformCredentialProviderHost,
26
27
  NomosRuntimeV2EventTransport,
27
28
  NomosRuntimeV2KernelCalls,
28
29
  NomosRuntimeV2Transport;
29
- import 'webview_bridge.dart' show BridgeTransport;
30
+ import 'reception/peer_office_connection.dart' show PeerOfficeConnection;
30
31
 
31
32
  class NodeRunnerTransport
32
- with NomosRuntimeV2KernelCalls
33
- implements BridgeTransport, NomosRuntimeV2Transport, NomosRuntimeV2EventTransport {
33
+ with NomosRuntimeV2KernelCalls, NmsPlatformCredentialProviderHost
34
+ implements
35
+ PeerOfficeConnection,
36
+ NomosRuntimeV2Transport,
37
+ NomosRuntimeV2EventTransport {
34
38
  final Process _proc;
35
- final StreamController<Map<String, Object?>> _incoming =
36
- StreamController<Map<String, Object?>>.broadcast();
37
39
  final NmsBufferedRuntimeEvents _runtimeEvents = NmsBufferedRuntimeEvents();
38
40
  final void Function(String line)? _onDiagnostic;
39
41
  final Map<int, Completer<nomos_protocol.RuntimeResponse>> _runtimeReplies =
@@ -64,28 +66,24 @@ class NodeRunnerTransport
64
66
  }
65
67
  if (s.startsWith('runtime-v2-event:')) {
66
68
  try {
67
- _runtimeEvents.add(nomos_protocol.RuntimeEvent.fromBuffer(
69
+ final event = nomos_protocol.RuntimeEvent.fromBuffer(
68
70
  base64Decode(s.substring('runtime-v2-event:'.length)),
69
- ));
71
+ );
72
+ if (!consumeCredentialRefreshEvent(
73
+ event, this, _runtimeEvents.addError)) {
74
+ _runtimeEvents.add(event);
75
+ }
70
76
  } catch (error, stackTrace) {
71
77
  _runtimeEvents.addError(error, stackTrace);
72
78
  }
73
79
  return;
74
80
  }
75
- Object? decoded;
76
- try {
77
- decoded = jsonDecode(s);
78
- } catch (_) {
79
- _diag('[nomos-node-runner stdout] $line');
80
- return;
81
- }
82
- if (decoded is Map && !_incoming.isClosed) {
83
- _incoming.add(decoded.cast<String, Object?>());
84
- }
81
+ _diag('[nomos-node-runner unexpected stdout] $line');
85
82
  }, onDone: () {
86
- if (!_disposed && !_incoming.isClosed) {
87
- _incoming
88
- .addError(StateError('the nomos node runner exited unexpectedly'));
83
+ if (!_disposed && !_runtimeEvents.isClosed) {
84
+ _runtimeEvents.addError(
85
+ StateError('the Nomos node peer exited unexpectedly'),
86
+ );
89
87
  }
90
88
  });
91
89
  _proc.stderr
@@ -104,17 +102,18 @@ class NodeRunnerTransport
104
102
  }
105
103
 
106
104
  /// Spawn the node runner and return the connected transport. Wrap it in a
107
- /// [NomosBridge] and `await bridge.ready` (the runner posts `{event:'ready'}`
108
- /// on boot), then `connect(...)` as any other transport.
105
+ /// [DartReception] and `await reception.ready` (the runner posts `{event:'ready'}`
106
+ /// on boot), then bind an interaction as with any other transport.
109
107
  ///
110
108
  /// [entry] — path to `node-entry.ts`. When omitted it is resolved with
111
- /// `require.resolve('@githolon/client/runner/node-entry.ts')` from
109
+ /// `require.resolve('@githolon/runtime/runner/node-entry.ts')` from
112
110
  /// [resolveFrom] (default: the current directory), i.e. the consuming app's
113
111
  /// own `node_modules`.
114
112
  static Future<NodeRunnerTransport> start({
115
113
  String? entry,
116
114
  String? resolveFrom,
117
115
  String node = 'node',
116
+ List<String> arguments = const <String>[],
118
117
  Map<String, String>? environment,
119
118
  void Function(String line)? onDiagnostic,
120
119
  }) async {
@@ -122,7 +121,7 @@ class NodeRunnerTransport
122
121
  await _resolveEntry(node, resolveFrom ?? Directory.current.path);
123
122
  final proc = await Process.start(
124
123
  node,
125
- [entryPath],
124
+ [entryPath, ...arguments],
126
125
  environment: environment,
127
126
  // [resolveFrom] locates a packaged runner when [entry] is omitted; it is not a runtime dependency of
128
127
  // an explicit, self-contained entry. Reusing it as cwd made copied release tests fail with ENOENT even
@@ -162,8 +161,8 @@ class NodeRunnerTransport
162
161
  }
163
162
  }
164
163
  for (final entry in const [
165
- '@githolon/client/runner/node-entry.mjs',
166
- '@githolon/client/runner/node-entry.ts',
164
+ '@githolon/runtime/runner/node-entry.mjs',
165
+ '@githolon/runtime/runner/node-entry.ts',
167
166
  ]) {
168
167
  final r = await Process.run(
169
168
  node,
@@ -174,27 +173,24 @@ class NodeRunnerTransport
174
173
  if (r.exitCode == 0 && out.isNotEmpty) return out;
175
174
  }
176
175
  throw StateError('the Nomos release contains no bundled node runner and no '
177
- '@githolon/client runner is installed; reinstall nomos_client');
176
+ '@githolon/runtime runner is installed; reinstall nomos_client');
178
177
  }
179
178
 
180
179
  @override
181
- Stream<Map<String, Object?>> get messages => _incoming.stream;
182
-
183
- @override
184
- Stream<nomos_protocol.RuntimeEvent> get runtimeEvents => _runtimeEvents.stream;
180
+ Stream<nomos_protocol.RuntimeEvent> get runtimeEvents =>
181
+ _runtimeEvents.stream;
185
182
 
186
- @override
187
- void send(Map<String, Object?> message) {
188
- if (_disposed) throw StateError('NodeRunnerTransport is disposed');
189
- _proc.stdin.writeln(jsonEncode(message));
190
- }
191
-
192
- /// Exchange one exact generated Runtime v2 frame with the headless runner.
183
+ /// Exchange one exact generated Runtime v2 frame with the spawned node peer.
193
184
  /// The line prefix/base64 is only stdio framing; the command and result are
194
185
  /// protobuf and are never reconstructed as a JSON object or operation name.
195
186
  @override
196
187
  Future<nomos_protocol.RuntimeResponse> invokeRuntime(
197
188
  nomos_protocol.RuntimeRequest request,
189
+ ) =>
190
+ driveCredentialEffects(request, _invokeRuntimeFrame);
191
+
192
+ Future<nomos_protocol.RuntimeResponse> _invokeRuntimeFrame(
193
+ nomos_protocol.RuntimeRequest request,
198
194
  ) {
199
195
  if (_disposed) throw StateError('NodeRunnerTransport is disposed');
200
196
  final id = request.requestId.toInt();
@@ -233,7 +229,6 @@ class NodeRunnerTransport
233
229
  _proc.kill(ProcessSignal.sigkill);
234
230
  return -9;
235
231
  });
236
- await _incoming.close();
237
232
  await _runtimeEvents.close();
238
233
  }
239
234
  }
@@ -0,0 +1,70 @@
1
+ /// Disposable Dart projection of sessions owned by the peer runtime.
2
+ ///
3
+ /// This directory is not custody, placement, authentication, or lifecycle
4
+ /// authority. It remembers only which Runtime v2 bindings have succeeded and
5
+ /// provides stable typed façade handles for application code.
6
+ library;
7
+
8
+ final class _NmsObservedSession {
9
+ const _NmsObservedSession({
10
+ required this.workspace,
11
+ required this.credentialCapability,
12
+ });
13
+
14
+ final String workspace;
15
+ final String credentialCapability;
16
+ }
17
+
18
+ final class NmsObservedSessionDirectory<THandle> {
19
+ // One observation record owns the successful-bind fact. A separate Set and
20
+ // workspace/capability maps would be three mutable copies requiring fan-out.
21
+ final Map<String, _NmsObservedSession> _bindings = {};
22
+ final Map<String, THandle> _handles = {};
23
+
24
+ Iterable<String> get boundNames => _bindings.keys;
25
+ int get boundCount => _bindings.length;
26
+ bool get hasBoundSessions => _bindings.isNotEmpty;
27
+ String get soleBoundSession => _bindings.keys.single;
28
+
29
+ bool isBound(String session) => _bindings.containsKey(session);
30
+
31
+ List<String> boundSnapshot() => List.unmodifiable(_bindings.keys);
32
+
33
+ String workspaceFor(String session) =>
34
+ _bindings[session]?.workspace ?? session;
35
+
36
+ String credentialCapabilityFor(String session) {
37
+ final observed = _bindings[session];
38
+ if (observed == null) {
39
+ throw StateError("Nomos session '$session' is not bound");
40
+ }
41
+ return observed.credentialCapability;
42
+ }
43
+
44
+ void observeBound({
45
+ required String session,
46
+ required String workspace,
47
+ required String credentialCapability,
48
+ }) {
49
+ if (credentialCapability.isEmpty) {
50
+ throw StateError(
51
+ "Nomos runtime bound '$session' without a credential capability identity",
52
+ );
53
+ }
54
+ final observed = _bindings[session];
55
+ if (observed != null &&
56
+ (observed.workspace != workspace ||
57
+ observed.credentialCapability != credentialCapability)) {
58
+ throw StateError(
59
+ "Nomos session '$session' changed identity after binding",
60
+ );
61
+ }
62
+ _bindings[session] = _NmsObservedSession(
63
+ workspace: workspace,
64
+ credentialCapability: credentialCapability,
65
+ );
66
+ }
67
+
68
+ THandle handle(String session, THandle Function() create) =>
69
+ _handles.putIfAbsent(session, create);
70
+ }
@@ -0,0 +1,112 @@
1
+ import 'dart:async';
2
+
3
+ import 'package:nomos_types/nomos_kernel_protocol.dart' as nomos_protocol;
4
+
5
+ enum NomosOperationPhase {
6
+ locallyAccepted,
7
+ admittedToRemoteCustody,
8
+ converged,
9
+ }
10
+
11
+ /// Product-facing progress for one accepted write.
12
+ final class NomosOperationStatus {
13
+ const NomosOperationStatus(this.phase, {this.updatedAt});
14
+
15
+ final NomosOperationPhase phase;
16
+ final DateTime? updatedAt;
17
+
18
+ bool get isAdmitted => phase != NomosOperationPhase.locallyAccepted;
19
+ bool get isConverged => phase == NomosOperationPhase.converged;
20
+ }
21
+
22
+ /// Awaitable projection of typed peer-runtime receipts for one stable intent.
23
+ final class NomosOperationReceipt {
24
+ NomosOperationReceipt._()
25
+ : _status =
26
+ const NomosOperationStatus(NomosOperationPhase.locallyAccepted),
27
+ _changes = StreamController<NomosOperationStatus>.broadcast();
28
+
29
+ NomosOperationStatus _status;
30
+ final StreamController<NomosOperationStatus> _changes;
31
+
32
+ NomosOperationStatus get current => _status;
33
+
34
+ Stream<NomosOperationStatus> watch() async* {
35
+ yield _status;
36
+ yield* _changes.stream;
37
+ }
38
+
39
+ Future<void> get admitted => _status.isAdmitted
40
+ ? Future<void>.value()
41
+ : watch().firstWhere((status) => status.isAdmitted).then((_) {});
42
+
43
+ Future<void> get converged => _status.isConverged
44
+ ? Future<void>.value()
45
+ : watch().firstWhere((status) => status.isConverged).then((_) {});
46
+
47
+ void _advance(NomosOperationPhase phase) {
48
+ if (phase.index <= _status.phase.index) return;
49
+ _status = NomosOperationStatus(phase, updatedAt: DateTime.now());
50
+ if (!_changes.isClosed) _changes.add(_status);
51
+ if (phase == NomosOperationPhase.converged) unawaited(_changes.close());
52
+ }
53
+
54
+ void _close() {
55
+ if (!_changes.isClosed) unawaited(_changes.close());
56
+ }
57
+ }
58
+
59
+ /// One disposable application projection of kernel-authenticated receipt IDs.
60
+ /// It never manufactures an admission verdict or persists business history.
61
+ final class NmsOperationReceiptProjection {
62
+ final Map<String, Map<String, NomosOperationReceipt>> _pending = {};
63
+ final Map<String, NomosOperationReceipt> _births = {};
64
+
65
+ NomosOperationReceipt register(String workspace, String intentId) {
66
+ if (intentId.isEmpty) {
67
+ throw StateError(
68
+ 'Nomos accepted a local write without returning its stable intent identity. '
69
+ 'The runtime and kernel release are incompatible.',
70
+ );
71
+ }
72
+ return _pending
73
+ .putIfAbsent(workspace, () => {})
74
+ .putIfAbsent(intentId, NomosOperationReceipt._);
75
+ }
76
+
77
+ void rememberBirth(String workspace, NomosOperationReceipt receipt) {
78
+ _births[workspace] = receipt;
79
+ }
80
+
81
+ NomosOperationReceipt? birthFor(String workspace) => _births[workspace];
82
+
83
+ void advance(String workspace, nomos_protocol.SyncWorkspaceResponse result) {
84
+ final pending = _pending[workspace];
85
+ if (pending == null || pending.isEmpty) return;
86
+
87
+ for (final intentId in result.admittedIntentIds.where(
88
+ (value) => value.isNotEmpty,
89
+ )) {
90
+ pending[intentId]?._advance(NomosOperationPhase.admittedToRemoteCustody);
91
+ }
92
+ for (final intentId in result.convergedIntentIds.where(
93
+ (value) => value.isNotEmpty,
94
+ )) {
95
+ final receipt = pending.remove(intentId);
96
+ receipt?._advance(NomosOperationPhase.converged);
97
+ }
98
+ if (pending.isEmpty) _pending.remove(workspace);
99
+ }
100
+
101
+ void close() {
102
+ final receipts = <NomosOperationReceipt>{
103
+ for (final workspace in _pending.values) ...workspace.values,
104
+ ..._births.values,
105
+ };
106
+ _pending.clear();
107
+ _births.clear();
108
+ for (final receipt in receipts) {
109
+ receipt._close();
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,103 @@
1
+ import 'application_status.dart' show ApplicationStatus;
2
+ import 'nms_generated_runtime.dart' show NomosSession, SyncStatus;
3
+ import 'workspace_open_progress.dart' show ApplicationWorkspaceOpenProgress;
4
+
5
+ /// Dart's language-native view of Peer Office observations.
6
+ ///
7
+ /// Peer Office owns the underlying subscriptions, reconnect policy and event
8
+ /// production. This desk owns only the Dart adaptation: listening creates a
9
+ /// subscription to the already-running peer event plane and cancelling the
10
+ /// listener releases that subscription. It never starts synchronization.
11
+ abstract interface class NmsPeerApplicationStreams {
12
+ Stream<ApplicationStatus> get status;
13
+ Stream<ApplicationStatus> get applicationStatus;
14
+ Stream<ApplicationWorkspaceOpenProgress> workspaceOpenProgress(
15
+ String workspace,
16
+ );
17
+ Stream<void> get childWorkspaces;
18
+ /// Any accepted local or remote change which may invalidate a generated
19
+ /// application projection. Interpretation remains in generated law-aware code.
20
+ Stream<void> get workspaceChanges;
21
+
22
+ /// Re-read one application projection initially and after any typed change
23
+ /// signal, coalescing changes which arrive while the read is in flight.
24
+ Stream<T> refreshOn<T>({
25
+ required Future<T> Function() read,
26
+ required Iterable<Stream<void>> changes,
27
+ });
28
+ }
29
+
30
+ final class NmsPeerApplicationStreamView implements NmsPeerApplicationStreams {
31
+ const NmsPeerApplicationStreamView({
32
+ required this.session,
33
+ required this.statusProjection,
34
+ this.workspaceOpenProgressSource,
35
+ });
36
+
37
+ final NomosSession session;
38
+ final ApplicationStatus Function(SyncStatus status) statusProjection;
39
+ final Stream<ApplicationWorkspaceOpenProgress> Function(String workspace)?
40
+ workspaceOpenProgressSource;
41
+
42
+ @override
43
+ Stream<ApplicationStatus> get status =>
44
+ session.syncStatus.map(statusProjection);
45
+
46
+ @override
47
+ Stream<ApplicationStatus> get applicationStatus =>
48
+ session.parent.syncStatus.map(statusProjection);
49
+
50
+ @override
51
+ Stream<ApplicationWorkspaceOpenProgress> workspaceOpenProgress(
52
+ String workspace,
53
+ ) =>
54
+ workspaceOpenProgressSource?.call(workspace) ??
55
+ const Stream<ApplicationWorkspaceOpenProgress>.empty();
56
+
57
+ @override
58
+ Stream<void> get childWorkspaces => session.syncEvents.map<void>((_) {});
59
+
60
+ @override
61
+ Stream<void> get workspaceChanges => session.syncEvents.map<void>((_) {});
62
+
63
+ @override
64
+ Stream<T> refreshOn<T>({
65
+ required Future<T> Function() read,
66
+ required Iterable<Stream<void>> changes,
67
+ }) =>
68
+ Stream<T>.multi((events) {
69
+ var reading = false;
70
+ var reread = false;
71
+
72
+ Future<void> refresh() async {
73
+ if (reading) {
74
+ reread = true;
75
+ return;
76
+ }
77
+ reading = true;
78
+ do {
79
+ reread = false;
80
+ try {
81
+ final value = await read();
82
+ if (!events.isClosed) events.add(value);
83
+ } catch (error, stackTrace) {
84
+ if (!events.isClosed) events.addError(error, stackTrace);
85
+ }
86
+ } while (reread && !events.isClosed);
87
+ reading = false;
88
+ }
89
+
90
+ final subscriptions = changes
91
+ .map((stream) => stream.listen(
92
+ (_) => refresh(),
93
+ onError: events.addError,
94
+ ))
95
+ .toList(growable: false);
96
+ events.onCancel = () async {
97
+ for (final subscription in subscriptions) {
98
+ await subscription.cancel();
99
+ }
100
+ };
101
+ refresh();
102
+ });
103
+ }