@githolon/dsl 0.102.5 → 0.103.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compile_package.mjs +43 -2
- package/compile_proof.d.mts +47 -0
- package/compile_proof.mjs +343 -0
- package/dart/CHANGELOG.md +14 -0
- package/dart/lib/src/dev_cloud.dart +8 -8
- package/dart/lib/src/nms_generated_runtime.dart +2 -0
- package/dart/lib/src/runner/node-entry.mjs +5539 -1228
- package/dart/lib/src/runner/offline-install.mjs +4578 -528
- package/dart/lib/src/test_harness.dart +206 -152
- package/dart/lib/src/webview_bridge.dart +239 -102
- package/dart/pubspec.lock +1 -1
- package/dart/pubspec.yaml +2 -2
- package/dart/test/acquire_session_credentials_test.dart +4 -3
- package/dart/test/acquire_session_test.dart +9 -0
- package/dart/test/adv_realm_interference_test.dart +7 -6
- package/dart/test/cloud_bound_topology_test.dart +3 -3
- package/dart/test/cluster_births_test.dart +4 -3
- package/dart/test/cluster_device_keyed_acceptance_test.dart +10 -16
- package/dart/test/cluster_local_autosync_test.dart +3 -2
- package/dart/test/cluster_scale_test.dart +3 -2
- package/dart/test/dlq_session_surface_test.dart +7 -6
- package/dart/test/first_app_gauntlet_test.dart +14 -21
- package/dart/test/fixtures/wedge/fake-install.mjs +1 -1
- package/dart/test/four_layer_nested_topology_test.dart +4 -4
- package/dart/test/harness_phase_timeout_test.dart +2 -2
- package/dart/test/local_sync_inert_test.dart +1 -1
- package/dart/test/parent_signer_recovery_test.dart +2 -2
- package/dart/test/sealing_client_test.dart +4 -3
- package/dart/test/session_bound_test.dart +8 -7
- package/dart/test/test_harness_test.dart +13 -12
- package/dart/test/vo_read_typing_test.dart +4 -3
- package/dart/test/watch_after_offer_stall_test.dart +4 -3
- package/dart/test/webview_bridge_test.dart +102 -5
- package/dart_types/CHANGELOG.md +5 -0
- package/dart_types/lib/nomos_types.dart +9 -0
- package/dart_types/lib/src/nomos_ref.dart +20 -0
- package/dart_types/lib/src/optional_ref.dart +85 -0
- package/dart_types/lib/src/sha256_digest.dart +41 -0
- package/dart_types/lib/src/versioned_nomos_ref.dart +81 -0
- package/dart_types/pubspec.yaml +1 -1
- package/dart_types/test/optional_ref_test.dart +44 -0
- package/dart_types/test/sha256_digest_test.dart +18 -0
- package/package.json +9 -5
- package/runtime/generated-application-host.js +5365 -1105
- package/src/aggregate.ts +18 -0
- package/src/application_composition.ts +6 -32
- package/src/attested_core.ts +6 -3
- package/src/birth_meter.ts +1 -0
- package/src/birth_meter_ensure.ts +28 -2
- package/src/births.ts +1 -1
- package/src/build_package.ts +162 -538
- package/src/business_birth.ts +19 -3
- package/src/canonical_json.ts +21 -0
- package/src/check_package_main.ts +73 -33
- package/src/clear_directive.ts +19 -8
- package/src/codegen_dart.ts +305 -22
- package/src/codegen_dart_application.ts +862 -292
- package/src/codegen_dart_client.ts +1084 -407
- package/src/codegen_dart_scenario.ts +12 -12
- package/src/codegen_proof.ts +169 -39
- package/src/codegen_provider_dart.ts +17 -1
- package/src/codegen_ts.ts +298 -106
- package/src/compat_manifest.ts +2 -2
- package/src/compat_package_main.ts +8 -12
- package/src/compile_package_main.ts +1860 -592
- package/src/compose.ts +1 -1
- package/src/directive.ts +208 -15
- package/src/engine_entry.ts +165 -111
- package/src/evolve_preview.ts +29 -69
- package/src/extremum.ts +1 -1
- package/src/fields.ts +183 -4
- package/src/flow_gate.ts +75 -2
- package/src/framework/application_home.ts +2 -0
- package/src/framework/auth_provider_law.ts +2 -2
- package/src/framework/birth_meter.ts +7 -19
- package/src/framework/bootstrap.ts +0 -2
- package/src/framework/business_corrections.ts +9 -5
- package/src/framework/domain_lifecycle.ts +15 -29
- package/src/framework/encryption_scope.ts +2 -1
- package/src/framework/framework_aggregates.ts +2 -0
- package/src/framework/genesis.ts +16 -14
- package/src/framework/home.ts +20 -14
- package/src/framework/owned_custody.ts +9 -3
- package/src/framework/platform_admin.ts +3 -3
- package/src/framework/relations.ts +21 -13
- package/src/framework/signing_identity.ts +45 -40
- package/src/framework/workspace_invariant.ts +245 -0
- package/src/framework/workspaces.ts +23 -29
- package/src/index.ts +36 -16
- package/src/intent.ts +178 -6
- package/src/law_check.ts +32 -11
- package/src/law_graph.ts +2902 -0
- package/src/law_graph_usda.ts +1799 -0
- package/src/law_identity.ts +435 -0
- package/src/law_value_type.ts +230 -0
- package/src/lsp_server.ts +58 -29
- package/src/ops.ts +7 -0
- package/src/package_config.ts +141 -0
- package/src/package_dependencies.ts +108 -0
- package/src/package_dependency_usage.ts +150 -0
- package/src/package_source_imports.ts +71 -0
- package/src/plan_source_slice.ts +335 -0
- package/src/point_guard.ts +192 -0
- package/src/precondition.ts +44 -9
- package/src/read_projection_namespace.ts +1 -1
- package/src/runtime_zod.ts +44 -0
- package/src/secondary_ensure.ts +67 -14
- package/src/shape_compat.ts +1 -1
- package/src/stable_id_identity_bridge.ts +149 -0
- package/src/stable_id_lockfile.ts +140 -6
- package/src/stable_ids.ts +1 -1
- package/src/stable_ids_for_module.ts +348 -0
- package/src/stable_ids_types.ts +1 -1
- package/src/sum.ts +1 -1
- package/src/usd_state.ts +1 -1
- package/src/usda_relationship_integrity.ts +66 -0
- package/src/usda_text.ts +18 -0
- package/src/usdz.ts +78 -0
- package/src/value_object.ts +95 -0
- package/src/wire.ts +19 -0
- package/src/wire_encode.ts +22 -0
- package/src/workspace_sharding.ts +52 -36
- package/src/workspace_sharing.ts +28 -4
- package/src/zod_introspection.ts +1 -1
- package/dart/test/cluster_attested_test.dart +0 -201
- package/src/compile_engine.ts +0 -595
- package/src/manifest.ts +0 -1476
- package/src/read_projection_composition.ts +0 -117
- package/src/read_projection_manifest.ts +0 -115
- package/src/relation_composition.ts +0 -286
- package/src/usd.ts +0 -1967
- package/src/usd_layers.ts +0 -1776
|
@@ -27,10 +27,17 @@ import 'package:nomos_types/nomos_types.dart'
|
|
|
27
27
|
NomosTraceContext,
|
|
28
28
|
NomosErrorInfo,
|
|
29
29
|
NomosRef,
|
|
30
|
+
VersionedNomosRef,
|
|
31
|
+
ReferenceFreshness,
|
|
32
|
+
ReferenceCurrent,
|
|
33
|
+
ReferenceStale,
|
|
34
|
+
ReferenceFreshnessUnavailable,
|
|
30
35
|
CustodyBreakPolicy,
|
|
31
36
|
NomosCustodyEvent,
|
|
32
37
|
NomosCustodyStatus;
|
|
33
38
|
import 'package:nomos_types/src/nomos_ref.dart' show NmsInternalNomosRefs;
|
|
39
|
+
import 'package:nomos_types/src/versioned_nomos_ref.dart'
|
|
40
|
+
show NmsInternalVersionedNomosRefs;
|
|
34
41
|
import 'package:nomos_types/src/foreign_ref.dart'
|
|
35
42
|
show NomosForeignReader, NomosForeignWorkspaceReader;
|
|
36
43
|
|
|
@@ -43,6 +50,17 @@ import 'session_bound.dart' show DomainNotInstalledForSession;
|
|
|
43
50
|
/// bridge root so app code reads as the four-layer split (engine → session → client → values).
|
|
44
51
|
typedef NomosEngine = NomosBridge;
|
|
45
52
|
|
|
53
|
+
/// One stable bounded page from an authenticated declared-query index. [cursor] is opaque framework
|
|
54
|
+
/// state: pass it back unchanged to request the next page; [done] means this page exhausted the query.
|
|
55
|
+
final class NomosPage<T> {
|
|
56
|
+
const NomosPage(
|
|
57
|
+
{required this.rows, required this.cursor, required this.done});
|
|
58
|
+
|
|
59
|
+
final List<T> rows;
|
|
60
|
+
final String? cursor;
|
|
61
|
+
final bool done;
|
|
62
|
+
}
|
|
63
|
+
|
|
46
64
|
/// The protocol version the runner and the bridge agree on (`webview-runner.mjs` `WEBVIEW_BRIDGE_VERSION`).
|
|
47
65
|
const int kWebviewBridgeVersion = 1;
|
|
48
66
|
|
|
@@ -1021,6 +1039,11 @@ class NomosBridge {
|
|
|
1021
1039
|
// [acquireSession]'s per-workspace single-flight: workspace → the in-flight open every concurrent
|
|
1022
1040
|
// acquirer joins (removed when it completes; a live session then hits the fast path instead).
|
|
1023
1041
|
final Map<String, Future<NomosSession>> _acquiring = {};
|
|
1042
|
+
// Every workspace opened as part of one stateful application participates in continuous custody exchange.
|
|
1043
|
+
// Starting that lifecycle is itself single-flight: generated navigation may discover the same child through
|
|
1044
|
+
// several references, but the runner gets one autoSync registration for that session.
|
|
1045
|
+
final Set<String> _autoSyncSessions = {};
|
|
1046
|
+
final Map<String, Future<void>> _autoSyncStarting = {};
|
|
1024
1047
|
int _seq = 0;
|
|
1025
1048
|
final String _traceId = _newTraceId();
|
|
1026
1049
|
late final StreamSubscription<Map<String, Object?>> _sub;
|
|
@@ -1498,7 +1521,7 @@ class NomosBridge {
|
|
|
1498
1521
|
{'id': id, if (session != null) 'session': session}));
|
|
1499
1522
|
|
|
1500
1523
|
/// SALVAGE — best-effort extraction of this holon's local-only authored work (un-synced pending
|
|
1501
|
-
/// writes + the DLQ), each with its full `
|
|
1524
|
+
/// writes + the DLQ), each with its full `changeB64` bytes: `{pendingWrites, deadLetters}`. The
|
|
1502
1525
|
/// read half of the recovery lane (`connect(recover: true)` runs salvage → [absorbSalvage]).
|
|
1503
1526
|
Future<Map<String, Object?>> salvage({String? session}) async =>
|
|
1504
1527
|
_asMap(await _call('salvage', {if (session != null) 'session': session}));
|
|
@@ -1537,11 +1560,9 @@ class NomosBridge {
|
|
|
1537
1560
|
/// `encodeIntentOfferBytes`, with `security.actor` included iff [actor] is set). The generated Dart client
|
|
1538
1561
|
/// calls THIS — it never hand-builds offer bytes, so there is exactly ONE copy of the wire format. Returns
|
|
1539
1562
|
/// the new local head; durable on [sync]. ([offer] with opaque bytes stays for back-compat carriers.)
|
|
1540
|
-
///
|
|
1541
|
-
///
|
|
1542
|
-
///
|
|
1543
|
-
/// when absent, so a plain offer is byte-identical to before. [session] pins the colocated workspace in a
|
|
1544
|
-
/// cluster harness (absent ⇒ the runner's sole-session default).
|
|
1563
|
+
/// Declared foreign reads are resolved and proved inside the shared kernel plane; callers never fetch or
|
|
1564
|
+
/// inject evidence. [force] carries the deploy/evolve force flag through. [session] pins the workspace in
|
|
1565
|
+
/// a cluster harness (absent ⇒ the runner's sole-session default).
|
|
1545
1566
|
@Deprecated(
|
|
1546
1567
|
'Engine-level string-y writes are the footgun (session_bound_clients.md ask 1/7): '
|
|
1547
1568
|
'dispatch through a generated <Domain>Client bound to a NomosSession '
|
|
@@ -1553,7 +1574,6 @@ class NomosBridge {
|
|
|
1553
1574
|
required Object? payload,
|
|
1554
1575
|
String? actor,
|
|
1555
1576
|
String? domainHash,
|
|
1556
|
-
List<Map<String, Object?>>? attestations,
|
|
1557
1577
|
List<Map<String, Object?>>? nomosConflictSnapshots,
|
|
1558
1578
|
bool? force,
|
|
1559
1579
|
String? session,
|
|
@@ -1567,7 +1587,6 @@ class NomosBridge {
|
|
|
1567
1587
|
'payload': payload,
|
|
1568
1588
|
if (actor != null) 'actor': actor,
|
|
1569
1589
|
if (domainHash != null) 'domainHash': domainHash,
|
|
1570
|
-
if (attestations != null) 'attestations': attestations,
|
|
1571
1590
|
if (nomosConflictSnapshots != null)
|
|
1572
1591
|
'__nomosConflictSnapshots': nomosConflictSnapshots,
|
|
1573
1592
|
if (force != null) 'force': force,
|
|
@@ -1591,7 +1610,6 @@ class NomosBridge {
|
|
|
1591
1610
|
required Object? payload,
|
|
1592
1611
|
String? actor,
|
|
1593
1612
|
String? domainHash,
|
|
1594
|
-
List<Map<String, Object?>>? attestations,
|
|
1595
1613
|
List<Map<String, Object?>>? nomosConflictSnapshots,
|
|
1596
1614
|
bool? force,
|
|
1597
1615
|
String? session,
|
|
@@ -1603,7 +1621,6 @@ class NomosBridge {
|
|
|
1603
1621
|
payload: payload,
|
|
1604
1622
|
actor: actor,
|
|
1605
1623
|
domainHash: domainHash,
|
|
1606
|
-
attestations: attestations,
|
|
1607
1624
|
nomosConflictSnapshots: nomosConflictSnapshots,
|
|
1608
1625
|
force: force,
|
|
1609
1626
|
session: session,
|
|
@@ -1621,7 +1638,6 @@ class NomosBridge {
|
|
|
1621
1638
|
required Object? payload,
|
|
1622
1639
|
String? actor,
|
|
1623
1640
|
String? domainHash,
|
|
1624
|
-
List<Map<String, Object?>>? attestations,
|
|
1625
1641
|
List<Map<String, Object?>>? nomosConflictSnapshots,
|
|
1626
1642
|
bool? force,
|
|
1627
1643
|
String? session,
|
|
@@ -1644,7 +1660,6 @@ class NomosBridge {
|
|
|
1644
1660
|
'payload': payload,
|
|
1645
1661
|
if (actor != null) 'actor': actor,
|
|
1646
1662
|
if (domainHash != null) 'domainHash': domainHash,
|
|
1647
|
-
if (attestations != null) 'attestations': attestations,
|
|
1648
1663
|
if (nomosConflictSnapshots != null)
|
|
1649
1664
|
'__nomosConflictSnapshots': nomosConflictSnapshots,
|
|
1650
1665
|
if (force != null) 'force': force,
|
|
@@ -1786,6 +1801,22 @@ class NomosBridge {
|
|
|
1786
1801
|
return (r['offer'] as Map?)?.cast<String, Object?>();
|
|
1787
1802
|
}
|
|
1788
1803
|
|
|
1804
|
+
/// Prepare the exact authenticated execution slice a generated client is about to use. This is
|
|
1805
|
+
/// a readiness hint only: compilation, composition, cache custody and all trust decisions remain
|
|
1806
|
+
/// inside the kernel. Generated `bind` calls it so first-use cost is paid before the client becomes
|
|
1807
|
+
/// interactive, rather than on the person's first button press.
|
|
1808
|
+
Future<void> prepareDomain({
|
|
1809
|
+
required String domain,
|
|
1810
|
+
String? directiveId,
|
|
1811
|
+
required String packageHash,
|
|
1812
|
+
}) async {
|
|
1813
|
+
await _call('prepareDomain', {
|
|
1814
|
+
'domain': domain,
|
|
1815
|
+
if (directiveId != null) 'directiveId': directiveId,
|
|
1816
|
+
'packageHash': packageHash,
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1789
1820
|
/// FOLLOW A [NomosRef] — THE cross-workspace primitive (session_bound_clients.md ask 8): mount
|
|
1790
1821
|
/// the referenced workspace on this device's plane (idempotent — an already-open workspace is
|
|
1791
1822
|
/// just returned) and hand back its [NomosSession]. Typed client construction is then one line:
|
|
@@ -1849,14 +1880,34 @@ class NomosBridge {
|
|
|
1849
1880
|
plane._sessionPrincipals[workspace] = sourcePrincipal;
|
|
1850
1881
|
}
|
|
1851
1882
|
|
|
1883
|
+
Future<NomosSession> ready(NomosSession opened) async {
|
|
1884
|
+
inheritPrincipal();
|
|
1885
|
+
if (!plane._autoSyncSessions.contains(workspace)) {
|
|
1886
|
+
final start = plane._autoSyncStarting.putIfAbsent(workspace, () async {
|
|
1887
|
+
await opened.autoSync();
|
|
1888
|
+
plane._autoSyncSessions.add(workspace);
|
|
1889
|
+
});
|
|
1890
|
+
try {
|
|
1891
|
+
await start;
|
|
1892
|
+
} finally {
|
|
1893
|
+
if (identical(plane._autoSyncStarting[workspace], start)) {
|
|
1894
|
+
plane._autoSyncStarting.remove(workspace);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
return opened;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1852
1901
|
if (plane._sessionNames.contains(workspace)) {
|
|
1853
1902
|
inheritPrincipal();
|
|
1854
|
-
return
|
|
1903
|
+
return ready(session(workspace));
|
|
1855
1904
|
}
|
|
1856
1905
|
// SINGLE-FLIGHT: one in-flight open per workspace per plane; concurrent callers join it.
|
|
1857
1906
|
return plane._acquiring.putIfAbsent(workspace, () async {
|
|
1858
1907
|
try {
|
|
1859
|
-
if (plane._sessionNames.contains(workspace))
|
|
1908
|
+
if (plane._sessionNames.contains(workspace)) {
|
|
1909
|
+
return ready(session(workspace));
|
|
1910
|
+
}
|
|
1860
1911
|
// Consult the plane's TRUTHFUL registry (mounted ∪ parked ∪ resident custody — the runner's
|
|
1861
1912
|
// realm.list()): anything the device already holds opens locally, no cloud, no credentials.
|
|
1862
1913
|
var resident = false;
|
|
@@ -1872,8 +1923,7 @@ class NomosBridge {
|
|
|
1872
1923
|
session: workspace,
|
|
1873
1924
|
allowUnborn: true,
|
|
1874
1925
|
authorSecret: authorSecret);
|
|
1875
|
-
|
|
1876
|
-
return session(workspace);
|
|
1926
|
+
return ready(session(workspace));
|
|
1877
1927
|
}
|
|
1878
1928
|
if (ifAbsentCredentials == null) {
|
|
1879
1929
|
throw StateError(
|
|
@@ -1890,8 +1940,7 @@ class NomosBridge {
|
|
|
1890
1940
|
authorSecret: c.authorSecret ?? authorSecret,
|
|
1891
1941
|
allowUnborn: c.allowUnborn,
|
|
1892
1942
|
);
|
|
1893
|
-
|
|
1894
|
-
return session(workspace);
|
|
1943
|
+
return ready(session(workspace));
|
|
1895
1944
|
} finally {
|
|
1896
1945
|
// completed (either way) — the next acquire re-evaluates fresh (a live session hits the fast path).
|
|
1897
1946
|
plane._acquiring.remove(workspace);
|
|
@@ -1906,6 +1955,17 @@ class NomosBridge {
|
|
|
1906
1955
|
(_asMap(await _call('mintId', {'typeTag': typeTag}))['id'] ?? '')
|
|
1907
1956
|
.toString();
|
|
1908
1957
|
|
|
1958
|
+
/// @internal The immutable genesis of a sibling custody chain. This is observational metadata used to
|
|
1959
|
+
/// compose an ordinary source-enrolment offer; the kernel still reopens and proves every foreign read.
|
|
1960
|
+
Future<String> nomosWorkspaceGenesis(String workspace,
|
|
1961
|
+
{String? session}) async =>
|
|
1962
|
+
(_asMap(await _call('nomosWorkspaceGenesis', {
|
|
1963
|
+
'workspace': workspace,
|
|
1964
|
+
if (session != null) 'session': session,
|
|
1965
|
+
}))['genesis'] ??
|
|
1966
|
+
'')
|
|
1967
|
+
.toString();
|
|
1968
|
+
|
|
1909
1969
|
/// UNDO the most-recent action via the kernel STRIKE primitive (the fold drops the struck intent by parity).
|
|
1910
1970
|
/// [scope] `'all'` undoes any author's most-recent action; `'user'` only THIS replica's own. [actions] is the
|
|
1911
1971
|
/// app's set of undoable directive ids (so undo never strikes a framework/control intent). [domain] +
|
|
@@ -1924,7 +1984,6 @@ class NomosBridge {
|
|
|
1924
1984
|
required Object? payload,
|
|
1925
1985
|
String? actor,
|
|
1926
1986
|
String? domainHash,
|
|
1927
|
-
List<Map<String, Object?>>? attestations,
|
|
1928
1987
|
List<Map<String, Object?>>? nomosConflictSnapshots,
|
|
1929
1988
|
bool? force,
|
|
1930
1989
|
String? session,
|
|
@@ -1935,21 +1994,10 @@ class NomosBridge {
|
|
|
1935
1994
|
payload: payload,
|
|
1936
1995
|
actor: actor,
|
|
1937
1996
|
domainHash: domainHash,
|
|
1938
|
-
attestations: attestations,
|
|
1939
1997
|
nomosConflictSnapshots: nomosConflictSnapshots,
|
|
1940
1998
|
force: force,
|
|
1941
1999
|
session: session);
|
|
1942
2000
|
|
|
1943
|
-
/// ATTESTED READ — serve + SIGN a declared query over a COLOCATED source session's certified fold (the
|
|
1944
|
-
/// dynamic-source trust lane; architecture/dynamic_source_attested_reads.md). In a [NomosTestHarness.cluster]
|
|
1945
|
-
/// (one engine plane, many sessions) this fulfils the read IN-PROCESS from the [from] session and returns
|
|
1946
|
-
/// `{ entry: {queryId, result, attestation:{source_genesis, public_key, …}} }` — pass `result['entry']`
|
|
1947
|
-
/// (the whole entry map) as one [offerDirective] `attestations` element, or read
|
|
1948
|
-
/// `entry['attestation']['source_genesis']` to enrol the source in a reader's allow-list. [args] are the
|
|
1949
|
-
/// query params. [session] is the caller/reader session (defaults to this view's bound session).
|
|
1950
|
-
/// [attestorSecret] is the source's base64 read-attestor secret (from [authorKeygen]); when omitted the
|
|
1951
|
-
/// source session's connect-time `authorSecret` signs. [nowMs] stamps the freshness clock (defaults to the
|
|
1952
|
-
/// source's own clock).
|
|
1953
2001
|
/// RELEASE COHERENCE — assert the connected cloud's kernel supports every capability this law needs (the
|
|
1954
2002
|
/// generated client passes its baked `requiresKernel`, e.g. `['compoundKeys']` for a compound-key query).
|
|
1955
2003
|
/// Throws a typed [KernelCoherenceError] on a skew (a stale/mismatched cloud kernel) with the named reason,
|
|
@@ -1964,23 +2012,6 @@ class NomosBridge {
|
|
|
1964
2012
|
});
|
|
1965
2013
|
}
|
|
1966
2014
|
|
|
1967
|
-
Future<Map<String, Object?>> attestedRead({
|
|
1968
|
-
required String from,
|
|
1969
|
-
required String queryId,
|
|
1970
|
-
Map<String, Object?> args = const {},
|
|
1971
|
-
String? attestorSecret,
|
|
1972
|
-
int? nowMs,
|
|
1973
|
-
String? session,
|
|
1974
|
-
}) async =>
|
|
1975
|
-
_asMap(await _call('attestedRead', {
|
|
1976
|
-
'from': from,
|
|
1977
|
-
'queryId': queryId,
|
|
1978
|
-
'args': args,
|
|
1979
|
-
if (attestorSecret != null) 'attestorSecret': attestorSecret,
|
|
1980
|
-
if (nowMs != null) 'nowMs': nowMs,
|
|
1981
|
-
if (session != null) 'session': session,
|
|
1982
|
-
}));
|
|
1983
|
-
|
|
1984
2015
|
/// MAKE A WORKSPACE — the one-call tenant path. A child workspace under a platform/[parent] is a SIGNED
|
|
1985
2016
|
/// control-plane offer; the holon seals `createWorkspace` (or [directiveId] `birthChild`, or ANY
|
|
1986
2017
|
/// tenant-defined `.births()` directive — see [domain]) with the requester's device key ([authorSecret],
|
|
@@ -2196,8 +2227,10 @@ class NomosBridge {
|
|
|
2196
2227
|
|
|
2197
2228
|
/// Watch the RUNTIME activity feed: a stream that re-emits the newest-first activity rows on every local
|
|
2198
2229
|
/// change (doorbell/sync tick). `base` = a commit-oid cursor. Cancelling drops the watch on the runner.
|
|
2199
|
-
Stream<List<dynamic>> watchActivity(
|
|
2200
|
-
|
|
2230
|
+
Stream<List<dynamic>> watchActivity(
|
|
2231
|
+
{String? base, int? limit, String? session}) =>
|
|
2232
|
+
_watchStream(
|
|
2233
|
+
kind: 'activity', base: base, limit: limit, session: session);
|
|
2201
2234
|
|
|
2202
2235
|
/// Mint an ed25519 DEVICE KEY in-kernel → `{secret, public}` (base64). The SECRET stays on this device
|
|
2203
2236
|
/// (persist it in secure storage); pass it to [connect] as `authorSecret` (or per-offer `signWith`) so the
|
|
@@ -2388,16 +2421,46 @@ class NomosBridge {
|
|
|
2388
2421
|
/// behavior). `principal` stamps the private-read gate (defaults to the connection's bound principal).
|
|
2389
2422
|
/// Both are OMITTED from the wire when absent, so a public un-keyed read is byte-identical to before.
|
|
2390
2423
|
Future<List<Map<String, dynamic>>> query(String queryId,
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2424
|
+
[Map<String, Object?> params = const {},
|
|
2425
|
+
List<NomosScopeKey>? keys,
|
|
2426
|
+
String? principal,
|
|
2427
|
+
String? session]) async {
|
|
2428
|
+
return _asRows(await _call('query', {
|
|
2429
|
+
'queryId': queryId,
|
|
2430
|
+
'params': params,
|
|
2431
|
+
...?_readAuthArgs(keys, principal),
|
|
2432
|
+
if (session != null) 'session': session,
|
|
2433
|
+
}));
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
/// One bounded query page. Unlike [query], this never requires the kernel to materialize or serialize
|
|
2437
|
+
/// the complete result set merely to show the first screen.
|
|
2438
|
+
Future<NomosPage<Map<String, dynamic>>> queryPage(
|
|
2439
|
+
String queryId, {
|
|
2440
|
+
Map<String, Object?> params = const {},
|
|
2441
|
+
int pageSize = 100,
|
|
2442
|
+
String? cursor,
|
|
2443
|
+
List<NomosScopeKey>? keys,
|
|
2444
|
+
String? principal,
|
|
2445
|
+
String? session,
|
|
2446
|
+
}) async {
|
|
2447
|
+
if (pageSize < 1 || pageSize > 1000) {
|
|
2448
|
+
throw RangeError.range(pageSize, 1, 1000, 'pageSize');
|
|
2449
|
+
}
|
|
2450
|
+
final result = _asMap(await _call('queryPage', {
|
|
2451
|
+
'queryId': queryId,
|
|
2452
|
+
'params': params,
|
|
2453
|
+
'pageSize': pageSize,
|
|
2454
|
+
if (cursor != null) 'cursor': cursor,
|
|
2455
|
+
...?_readAuthArgs(keys, principal),
|
|
2456
|
+
if (session != null) 'session': session,
|
|
2457
|
+
}));
|
|
2458
|
+
return NomosPage<Map<String, dynamic>>(
|
|
2459
|
+
rows: _asRows(result),
|
|
2460
|
+
cursor: result['cursor'] as String?,
|
|
2461
|
+
done: result['done'] == true,
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2401
2464
|
|
|
2402
2465
|
/// A single aggregate by id (the by-id read). `keys`/`principal`: see [query].
|
|
2403
2466
|
Future<List<Map<String, dynamic>>> queryById(String id,
|
|
@@ -2410,6 +2473,42 @@ class NomosBridge {
|
|
|
2410
2473
|
if (session != null) 'session': session,
|
|
2411
2474
|
}));
|
|
2412
2475
|
|
|
2476
|
+
/// Compare an immutable publication pin with the stable series' one compiled
|
|
2477
|
+
/// current pointer. This is an O(current-state) check: it neither resolves the
|
|
2478
|
+
/// pinned publication nor reconstructs aggregate history.
|
|
2479
|
+
Future<ReferenceFreshness<TVersion>>
|
|
2480
|
+
checkReferenceFreshness<TVersion, TSeries>(
|
|
2481
|
+
VersionedNomosRef<TVersion, TSeries> reference,
|
|
2482
|
+
) async {
|
|
2483
|
+
final result = _asMap(await _call('checkRefFreshness', {
|
|
2484
|
+
'refs': [
|
|
2485
|
+
{
|
|
2486
|
+
'pinned': NmsInternalNomosRefs.toJson(reference.pinned),
|
|
2487
|
+
'series': NmsInternalNomosRefs.toJson(reference.series),
|
|
2488
|
+
'currentField': NmsInternalVersionedNomosRefs.currentField(reference),
|
|
2489
|
+
},
|
|
2490
|
+
],
|
|
2491
|
+
}));
|
|
2492
|
+
final unique = result['unique'] as List?;
|
|
2493
|
+
final first = unique == null || unique.isEmpty
|
|
2494
|
+
? const <String, Object?>{}
|
|
2495
|
+
: _asMap(unique.first);
|
|
2496
|
+
final outcome = _asMap(first['outcome']);
|
|
2497
|
+
final status = (outcome['status'] ?? 'unknown').toString();
|
|
2498
|
+
if (status == 'current') {
|
|
2499
|
+
return ReferenceCurrent<TVersion>(reference.pinned);
|
|
2500
|
+
}
|
|
2501
|
+
if (status == 'stale') {
|
|
2502
|
+
final latest = outcome['latest'];
|
|
2503
|
+
if (latest != null) {
|
|
2504
|
+
return ReferenceStale<TVersion>(
|
|
2505
|
+
NmsInternalNomosRefs.fromJson<TVersion>(latest),
|
|
2506
|
+
);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
return ReferenceFreshnessUnavailable<TVersion>(status);
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2413
2512
|
/// Verified points in this workspace's history, oldest to newest. The generated client presents these
|
|
2414
2513
|
/// as `client.past.moments()`; callers pass the opaque [NomosMoment] back to `past.at(moment)` and never
|
|
2415
2514
|
/// need to know that the framework uses commit identities underneath.
|
|
@@ -2591,12 +2690,13 @@ class NomosBridge {
|
|
|
2591
2690
|
}));
|
|
2592
2691
|
|
|
2593
2692
|
/// FRAMEWORK-INTERNAL business birth: derive the unborn child's owned key from its deterministic address.
|
|
2594
|
-
Future<NomosScopeKey> nomosOwnedBirthScopeKey(
|
|
2595
|
-
|
|
2693
|
+
Future<NomosScopeKey> nomosOwnedBirthScopeKey(String scope, String namespace,
|
|
2694
|
+
Object? key, String recordType, String recordId) async {
|
|
2596
2695
|
final out = _asMap(await _call('cryptoOwnedBirthScopeKey', {
|
|
2597
2696
|
'scope': scope,
|
|
2598
2697
|
'namespace': namespace,
|
|
2599
2698
|
'key': key,
|
|
2699
|
+
'record': {'type': recordType, 'id': recordId},
|
|
2600
2700
|
}));
|
|
2601
2701
|
return NomosScopeKey(
|
|
2602
2702
|
scope: (out['scope'] ?? scope).toString(),
|
|
@@ -2723,14 +2823,20 @@ class NomosBridge {
|
|
|
2723
2823
|
/// Each round arrives on [syncEvents] as `{ event:'sync', reason, result }`; socket state as `{event:'link'}`.
|
|
2724
2824
|
/// Call once after [connect] — then the app never polls.
|
|
2725
2825
|
Future<void> autoSync(
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2826
|
+
{int? debounceMs, int? fallbackMs, String? signalId}) async {
|
|
2827
|
+
await _call('autoSync', {
|
|
2828
|
+
if (debounceMs != null) 'debounceMs': debounceMs,
|
|
2829
|
+
if (fallbackMs != null) 'fallbackMs': fallbackMs,
|
|
2830
|
+
if (signalId != null) 'signalId': signalId
|
|
2831
|
+
});
|
|
2832
|
+
final workspace = _sessionName;
|
|
2833
|
+
if (workspace != null) _eventPlane._autoSyncSessions.add(workspace);
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2732
2836
|
Future<void> stopAutoSync() async {
|
|
2733
2837
|
await _call('stopAutoSync');
|
|
2838
|
+
final workspace = _sessionName;
|
|
2839
|
+
if (workspace != null) _eventPlane._autoSyncSessions.remove(workspace);
|
|
2734
2840
|
}
|
|
2735
2841
|
|
|
2736
2842
|
/// Background-sync telemetry: each autoSync round (`event:'sync'`) and doorbell socket state (`event:'link'`).
|
|
@@ -2876,13 +2982,15 @@ class NomosBridge {
|
|
|
2876
2982
|
Stream<List<dynamic>> watch(String queryId,
|
|
2877
2983
|
[Map<String, Object?> params = const {},
|
|
2878
2984
|
String? session,
|
|
2879
|
-
String? principal
|
|
2985
|
+
String? principal,
|
|
2986
|
+
int? intervalMs]) =>
|
|
2880
2987
|
_watchStream(
|
|
2881
2988
|
kind: 'query',
|
|
2882
2989
|
queryId: queryId,
|
|
2883
2990
|
params: params,
|
|
2884
2991
|
session: session,
|
|
2885
|
-
principal: principal
|
|
2992
|
+
principal: principal,
|
|
2993
|
+
intervalMs: intervalMs);
|
|
2886
2994
|
Stream<List<dynamic>> watchById(String id,
|
|
2887
2995
|
[String? session, String? principal]) =>
|
|
2888
2996
|
_watchStream(
|
|
@@ -3003,12 +3111,18 @@ class NomosBridge {
|
|
|
3003
3111
|
final subId = 'c${++_eventPlane._seq}';
|
|
3004
3112
|
late final StreamController<Map<String, Object?>> ctrl;
|
|
3005
3113
|
ctrl = StreamController<Map<String, Object?>>(
|
|
3006
|
-
onListen: ()
|
|
3007
|
-
'
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3114
|
+
onListen: () {
|
|
3115
|
+
unawaited(_call('watch', {
|
|
3116
|
+
'subId': subId,
|
|
3117
|
+
'kind': 'compat',
|
|
3118
|
+
'requirement': requirement,
|
|
3119
|
+
if (session != null) 'session': session,
|
|
3120
|
+
}).then<void>((_) {}, onError: (Object error, StackTrace stackTrace) {
|
|
3121
|
+
if (!ctrl.isClosed && ctrl.hasListener) {
|
|
3122
|
+
ctrl.addError(error, stackTrace);
|
|
3123
|
+
}
|
|
3124
|
+
}));
|
|
3125
|
+
},
|
|
3012
3126
|
onCancel: () {
|
|
3013
3127
|
_eventPlane._compatWatches.remove(subId);
|
|
3014
3128
|
// Cancellation is a local lifecycle boundary and must complete even if the runner is slow,
|
|
@@ -3029,25 +3143,35 @@ class NomosBridge {
|
|
|
3029
3143
|
String? spatialId,
|
|
3030
3144
|
NomosBbox? bbox,
|
|
3031
3145
|
String? base,
|
|
3146
|
+
int? limit,
|
|
3032
3147
|
String? session,
|
|
3033
|
-
String? principal
|
|
3148
|
+
String? principal,
|
|
3149
|
+
int? intervalMs}) {
|
|
3034
3150
|
// Controllers live on the EVENT PLANE (where _onMessage dispatches); the registration `_call` stays
|
|
3035
3151
|
// THIS instance's (a session's override stamps `args.session`) — see [_eventPlane].
|
|
3036
3152
|
final subId = 'w${++_eventPlane._seq}';
|
|
3037
3153
|
late final StreamController<List<dynamic>> ctrl;
|
|
3038
3154
|
ctrl = StreamController<List<dynamic>>(
|
|
3039
|
-
onListen: ()
|
|
3040
|
-
'
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3155
|
+
onListen: () {
|
|
3156
|
+
unawaited(_call('watch', {
|
|
3157
|
+
'subId': subId,
|
|
3158
|
+
'kind': kind,
|
|
3159
|
+
if (queryId != null) 'queryId': queryId,
|
|
3160
|
+
if (params != null) 'params': params,
|
|
3161
|
+
if (id != null) 'id': id,
|
|
3162
|
+
if (spatialId != null) 'spatialId': spatialId,
|
|
3163
|
+
if (bbox != null) 'bbox': bbox.toJson(),
|
|
3164
|
+
if (base != null) 'base': base,
|
|
3165
|
+
if (limit != null) 'limit': limit,
|
|
3166
|
+
if (principal != null) 'principal': principal,
|
|
3167
|
+
if (intervalMs != null) 'intervalMs': intervalMs,
|
|
3168
|
+
if (session != null) 'session': session,
|
|
3169
|
+
}).then<void>((_) {}, onError: (Object error, StackTrace stackTrace) {
|
|
3170
|
+
if (!ctrl.isClosed && ctrl.hasListener) {
|
|
3171
|
+
ctrl.addError(error, stackTrace);
|
|
3172
|
+
}
|
|
3173
|
+
}));
|
|
3174
|
+
},
|
|
3051
3175
|
onCancel: () {
|
|
3052
3176
|
_eventPlane._watches.remove(subId);
|
|
3053
3177
|
// A caller owns its local subscription; it must never need a healthy transport in order to release
|
|
@@ -3062,28 +3186,41 @@ class NomosBridge {
|
|
|
3062
3186
|
/// Tear down: stop the transport subscription + fail any in-flight calls.
|
|
3063
3187
|
Future<void> dispose() async {
|
|
3064
3188
|
await _sub.cancel();
|
|
3065
|
-
|
|
3066
|
-
|
|
3189
|
+
final pending = _pending.entries
|
|
3190
|
+
.map((entry) => (
|
|
3191
|
+
completer: entry.value,
|
|
3192
|
+
trace: _pendingTraces[entry.key] ?? const NomosTraceContext(),
|
|
3193
|
+
operation: _pendingOps[entry.key],
|
|
3194
|
+
))
|
|
3195
|
+
.toList(growable: false);
|
|
3196
|
+
_pending.clear();
|
|
3197
|
+
_pendingOps.clear();
|
|
3198
|
+
_pendingTraces.clear();
|
|
3199
|
+
for (final entry in pending) {
|
|
3200
|
+
final c = entry.completer;
|
|
3067
3201
|
if (!c.isCompleted) {
|
|
3068
|
-
final
|
|
3069
|
-
final info = NomosErrorInfo.legacy(
|
|
3202
|
+
final operation = entry.operation ?? 'unknown';
|
|
3203
|
+
final info = NomosErrorInfo.legacy(
|
|
3204
|
+
'bridge disposed while $operation was pending',
|
|
3070
3205
|
code: 'nomos.bridge.disposed',
|
|
3071
|
-
operation:
|
|
3072
|
-
trace: trace);
|
|
3206
|
+
operation: entry.operation,
|
|
3207
|
+
trace: entry.trace);
|
|
3073
3208
|
c.completeError(nomosErrorFromInfo(info));
|
|
3074
3209
|
}
|
|
3075
3210
|
}
|
|
3076
|
-
|
|
3077
|
-
_pendingOps.clear();
|
|
3078
|
-
_pendingTraces.clear();
|
|
3079
|
-
for (final w in _watches.values) {
|
|
3080
|
-
if (!w.isClosed) await w.close();
|
|
3081
|
-
}
|
|
3211
|
+
final watches = _watches.values.toList(growable: false);
|
|
3082
3212
|
_watches.clear();
|
|
3083
|
-
for (final w in
|
|
3084
|
-
|
|
3213
|
+
for (final w in watches) {
|
|
3214
|
+
// A paused consumer can keep StreamController.close() pending forever.
|
|
3215
|
+
// Disposal is the owner boundary: detach the controller immediately and
|
|
3216
|
+
// let its done event complete if/when that consumer resumes.
|
|
3217
|
+
if (!w.isClosed) unawaited(w.close());
|
|
3085
3218
|
}
|
|
3219
|
+
final compatWatches = _compatWatches.values.toList(growable: false);
|
|
3086
3220
|
_compatWatches.clear();
|
|
3221
|
+
for (final w in compatWatches) {
|
|
3222
|
+
if (!w.isClosed) unawaited(w.close());
|
|
3223
|
+
}
|
|
3087
3224
|
_stopStatusPoll();
|
|
3088
3225
|
if (!_syncEvents.isClosed) await _syncEvents.close();
|
|
3089
3226
|
if (!_signalEvents.isClosed) await _signalEvents.close();
|
package/dart/pubspec.lock
CHANGED
package/dart/pubspec.yaml
CHANGED
|
@@ -8,7 +8,7 @@ description: >-
|
|
|
8
8
|
Nomos 2 Dart frontend types — the hand-written kernel wire types plus the
|
|
9
9
|
code-generated per-domain aggregate + directive-payload types. (De)serializes
|
|
10
10
|
the EXACT serde JSON the Rust kernel + projection produce/consume.
|
|
11
|
-
version: 0.64.
|
|
11
|
+
version: 0.64.3
|
|
12
12
|
|
|
13
13
|
environment:
|
|
14
14
|
sdk: ^3.5.0
|
|
@@ -17,7 +17,7 @@ dependencies:
|
|
|
17
17
|
# The pure-data core (wire types, offer codec, NomosReads, schema validation, error
|
|
18
18
|
# info). nomos_client depends on and re-exports it, so existing imports are unchanged.
|
|
19
19
|
# For local monorepo dev, pubspec_overrides.yaml points this at ../dart_types.
|
|
20
|
-
nomos_types: ^0.8.
|
|
20
|
+
nomos_types: ^0.8.1
|
|
21
21
|
json_schema: ^5.2.2
|
|
22
22
|
crypto: ^3.0.0
|
|
23
23
|
package_config: ^2.1.0
|
|
@@ -20,7 +20,7 @@ import 'package:test/test.dart';
|
|
|
20
20
|
String get _root => '${Directory.current.path}/../..';
|
|
21
21
|
String get _repoEntry => '$_root/cloud/web-client/runner/node-entry.ts';
|
|
22
22
|
String get _docsDeploy =>
|
|
23
|
-
'$_root/bench/scale/fixtures/surfaced/build/surfaced.
|
|
23
|
+
'$_root/bench/scale/fixtures/surfaced/build/surfaced.law.usdz';
|
|
24
24
|
String get _runtimeDir =>
|
|
25
25
|
Platform.environment['NOMOS_DART_RUNTIME'] ??
|
|
26
26
|
Platform.environment['NOMOS_OFFLINE_RUNTIME'] ??
|
|
@@ -34,7 +34,8 @@ String? _missingPrereq() {
|
|
|
34
34
|
'(cd bench/scale/fixtures/surfaced && node ../../../../dsl/compile_package.mjs)';
|
|
35
35
|
}
|
|
36
36
|
if (!File('$_runtimeDir/holon-testmint.wasm').existsSync() ||
|
|
37
|
-
!File('$_runtimeDir/
|
|
37
|
+
!File('$_runtimeDir/bootstrap.law.usdz').existsSync() ||
|
|
38
|
+
!File('$_runtimeDir/nomos.law.usdz').existsSync()) {
|
|
38
39
|
return 'warm ~/.holon/runtime first (run any `githolon` command once)';
|
|
39
40
|
}
|
|
40
41
|
return null;
|
|
@@ -65,7 +66,7 @@ void main() {
|
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
final harness = await NomosTestHarness.ephemeral(
|
|
68
|
-
|
|
69
|
+
lawUsdzPaths: [_docsDeploy],
|
|
69
70
|
workspace: 'acquire-home',
|
|
70
71
|
nodeEntry: _repoEntry,
|
|
71
72
|
runtimeDir: _runtimeDir,
|
|
@@ -25,6 +25,11 @@ class FakeRunner implements BridgeTransport {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
int get connectCount => sent.where((m) => m['op'] == 'connect').length;
|
|
28
|
+
int autoSyncCount(String workspace) => sent
|
|
29
|
+
.where((m) =>
|
|
30
|
+
m['op'] == 'autoSync' &&
|
|
31
|
+
((m['args'] as Map)['session']).toString() == workspace)
|
|
32
|
+
.length;
|
|
28
33
|
Map<String, Object?> connectArgs(int i) =>
|
|
29
34
|
(sent.where((m) => m['op'] == 'connect').elementAt(i)['args'] as Map)
|
|
30
35
|
.cast<String, Object?>();
|
|
@@ -89,6 +94,8 @@ void main() {
|
|
|
89
94
|
fail('credentials must not be requested for a mounted session'));
|
|
90
95
|
expect(identical(s, bridge.session('home')), isTrue);
|
|
91
96
|
expect(runner.connectCount, 1, reason: 'no re-connect for a live session');
|
|
97
|
+
expect(runner.autoSyncCount('home'), 1,
|
|
98
|
+
reason: 'an acquired application workspace continuously syncs');
|
|
92
99
|
await bridge.dispose();
|
|
93
100
|
});
|
|
94
101
|
|
|
@@ -140,6 +147,8 @@ void main() {
|
|
|
140
147
|
reason: 'ONE connect for three concurrent acquires');
|
|
141
148
|
expect(identical(results[0], results[1]), isTrue);
|
|
142
149
|
expect(identical(results[1], results[2]), isTrue);
|
|
150
|
+
expect(runner.autoSyncCount('cloudy'), 1,
|
|
151
|
+
reason: 'the workspace lifecycle is single-flight too');
|
|
143
152
|
final args = runner.connectArgs(0);
|
|
144
153
|
expect(args['cloud'], 'https://cloud.example');
|
|
145
154
|
expect(args['authToken'], 'tok');
|